function dp = dichoticPitch(sbr, peak, width, tsSig, tsBack, sampSec, duration) % dp = dichoticPitch(sbr, peak, width, tsSig, tsBack, [sampSec], [duration]) % returns an n x 2 stereo sound (no low-pass filter & not normalized) % % * sbr: signal-to-background ratio (<=1 is true dichotic pitch) % * peak and width are in Hertz (pass equal-length vectors to get % multiple simultaneous pitches) % * signal and background time shifts (tsSig and tsBack) are in % milliseconds (.6 to .8 ms is about optimal) % * sampSec is the sample rate (i.e., samples per second)- defaults to 8192 % * duration is in seconds- defaults to .5 % % Note: This algorithm starts with 2 independant noise sources, applies % filters and time shifts, and then sums the processed noises. However, % the same result can be achieved more efficiently by starting with just one % noise and tweaking some of its phases in just the right way. I think % the algorithm presented here most clearly illustrates the basic idea, % but it would be nice to have the more eficient version. If anyone % writes it, ket me know! % % copyright 1999 % Robert F. Dougherty (bobd@stanford.edu) % http://www.stanford.edu/~bobd % Notes: % 99.11.05 RFD: fixed a bug in the renormalization section. % fftshift _does not_ work like fft when applied % to matrices. fft does 1-d ffts on each column % (2d ffts require fft2). However, fftshift, when % applied to a matrix, swaps quadrants (ie. assumes % a 2d fft). Anyway, this introduced a bug that % added some noise to DPs requiring renorm- sbrs<1. if nargin<5 | nargin>7 error('usage: dp = dichoticPitch(sbr, peak, fwhm, tsSig, tsBack, [sampSec], [duration])') end if nargin<6 sampSec = 8192; end if nargin<7 duration = 0.5; end if sbr<0 error('SBR must be >= 0!'); end numPeaks = length(peak); fNyquist = sampSec/2; % Take n up to the next power of 2 (to speed DFT) %n = 2^ceil(log2(duration*sampSec)); % n must at least be even: n = round(duration*sampSec); n = n+mod(n,2); % % Create pseudo-noise % (all components have equal amplitude and random phases) % amp = sqrt(n); phase = rand(n/2-1,1)*2*pi-pi; % Remember: % z = amp * exp(i*phase) % amp = abs(z) % phase = angle(z); % phase of negative freqs = -phase of positive freqs sigPhase = [0; phase; 0; -flipud(phase)]; % the background is independant- so we need another set of random phases phase = rand(n/2-1,1)*2*pi-pi; backPhase = [0; phase; 0; -flipud(phase)]; % Combine amplitude and phase into the imaginary representation sig = amp .* exp(i*sigPhase); back = amp .* exp(i*backPhase); clear phase sigPhase backPhase amp; % % Create signal and background filters % % give 'em all the same rectangular bandpass filter (we'll % appropriately scale each one later) sigFilter = multiBandpass(n, (peak-width./2)./fNyquist, (peak+width./2)./fNyquist)'; backFilter = sigFilter; renormFilter = sigFilter; % sigFilter gets scaled by the sbr sigFilter = sigFilter .* sbr; % backFilter is 1-sigFilter if sig <1, 0 otherwise backFilter = backFilter .* max((1-sbr-1),-1) + 1; % We need to renormalize the signal region by up to a factor of root 2 % to account for the decrease in amplitude caused by adding independant % noise sources. (but only if the sbr is <1) % for independant noise sources N1 and N2: % a1*N1 + a2*N2 = N3 % the average amplitude of N3 will be: % a3 = (sqrt((a1^2 + a2^2) / (a1+a2)^2)) % to renormalize, we mutiply by 1/a3 if sbr<1 renorm = 1/sqrt(sbr.^2+(1-sbr).^2); renormFilter = renormFilter .* (renorm - 1) + 1; else renormFilter = []; end % % Take signal and background into time domain % sig = real(ifft(ifftshift(sigFilter .* fftshift(sig)))); back = real(ifft(ifftshift(backFilter .* fftshift(back)))); clear sigFilter backFilter; % % Create the two channels for dichotic pitch % dp = zeros(n,2); % The first stereo channel is just the sum of sig and back dp(:,1) = (sig+back); % Apply time shifts- the other channel is the sum of the time-shifted versions sig = shift(sig', round(tsSig/1000*sampSec))'; back = shift(back', round(tsBack/1000*sampSec))'; dp(:,2) = sig+back; clear sig back; % % Apply renormalization filter (if necessary) % if ~isempty(renormFilter) ft = fftshift(fft(dp(:,1))); dp(:,1) = real(ifft(ifftshift(ft .* renormFilter))); ft = fftshift(fft(dp(:,2))); dp(:,2) = real(ifft(ifftshift(ft .* renormFilter))); end