Contents

Lesson 16: Auditory Motion

In this lesson, I'll introduce a simple method that converts a sound wave and a vector of x and y spatial position over time into a souund wave that simulates what an observer would hear while standing at the origin.

The method introduces the cues of interaural time delay (ITD) a doppler shift, and inverse square law. It also has a bit of an interaural level difference (ILD) because of the effect of the inverse square law and the fact that one ear will be closer to the sound than the other.

The trick is to calculate what the time-course of a sound wave should be at the location of the ear given the sound wave at the source, s(t), and the vectors of spatial position x(t) and y(t).

We'll start with a stationary band-pass sound. We'll then simulate what it should sound like when translating on a horizontal pass from left to right in front of your head. This first part should look familiar.

p.Fs = 44100;     % Sampling rate (Hz)
p.dur = 4;        % Duration (seconds)
centerFreq =440;  % Center of band-pass filter (Hz)
width = 200;      % Width of band-pass filter (Hz)

% Time vector
t= linspace(0,p.dur,p.dur*p.Fs)';

% White noise
y = randn(size(t));

% Fourier Transform:
F = complex2real(fft(y),t);

% Band-pass filter in the frequency domain:
Gaussian = exp(-(F.freq-centerFreq).^2/width^2)';
plot(F.freq,Gaussian);
F.amp = F.amp.*Gaussian;

% Inverse Fourier Transform:
s = real(ifft(real2complex(F)))';

% Play the sound
sound(s,p.Fs);

Auditory Horizontal Motion

The next step is to set up vectors x and y that determine the location of the sound over time. The units will be in meters. This will be a sound traveling over the four seconds from 50 meters to the left to 50 meters to the right, 10 meters in front of you. (The speed of the sound source will therefore be a steady 25 meters/sec or about 56 mph). The graphics show a simple diagram of the position relative to your (yellow) head.

x = 50*linspace(-1,1,length(s))';
y = 10*ones(size(s));

figure(1)
clf
plot(x(1:200:end),y(1:200:end),'r:');
hold on
plot(0,0,'ko','MarkerSize',20,'MarkerFaceColor','y');
axis equal
xlabel('X (meters)');
ylabel('Y (meters)');

To calculate the sound at each of the two ears, we need to define the speed of sound and the width of the head:

p.c = 345;  %speed of sound (meters/second)
p.a = .0875;  %with of head (meters)

Next we'll calculate the distance (squared) from the source to each ear for each point in time. For illustration, we can plot these distances as a function of time.

dLsq = (x-p.a).^2+y.^2;
dRsq = (x+p.a).^2+y.^2;

figure(1)
clf
plot(t,sqrt(dLsq),'r-');
hold on
plot(t,sqrt(dRsq),'g-');
set(gca,'YLim',[0,sqrt(max(dLsq))*1.1]);
xlabel('Time (s)');
ylabel('Distance to each ear (m)');
legend({'left','right'},'Location','SouthEast');

Time delay to each ear

The sound received at each ear depends on how long it takes for the sound to travel from the source to each ear, as a function of time. The delay (tauL and tauR) is the distance to the ears divided by the speed of sound:

tauL = sqrt(dLsq)/p.c;
tauR = sqrt(dRsq)/p.c;

clf
hold on
plot(t,tauL,'r-');
plot(t,tauR,'g-');

xlabel('Time (s)');
ylabel('Time delay to each ear (m)');
legend({'left','right'},'Location','SouthEast');

Interaural Time Difference (ITD)

The Interaural Time Difference (ITD) is caused by the difference in distance of the object to the two ears. The time difference between the two ears will be the difference in the left and right distances divided by the speed of sound. Here's a plot of the ITD for our sound as a function of time:

ITD = 1000*(tauR-tauL);

clf
plot(t,ITD);
xlabel('Time (s)');
ylabel('ITD (msec)');

The ITD asymptotes at +/- 5 msec because it takes 5 msec for a sound to pass the width of the head. At the beginning and end of the sound, the position is pretty much on one side.

Inverse square law

The intensity of a sound drops of inversely with the square of the distance. We can simulate this by dividing our sound wave by our distance vectors. The two vectors are then combined to produce a two-channel (stereo) sound. There will be a very slight interaural level difference because one ear will always be closer to the sound than the other. But this is negligable for the distances here (as can seen by the overlap in the curves in the figure).

sL = s./dLsq;
sR = s./dRsq;

yy = [sL,sR];
yy = yy/max(yy(:));

sound(yy,p.Fs);

The Doppler effect

The Doppler effect is the change in pitch experienced by an observer as an object changes its speed relative to you. For illustration, we can plot the speed based on the rate of changes of the distances:

dt = 1/(t(2)-t(1));
speedL = diff(sqrt(dLsq))*dt;
speedR = diff(sqrt(dRsq))*dt;
clf
hold on
plot(t(1:end-1),speedL,'r-');
plot(t(1:end-1),speedR,'g-');

xlabel('Time (s)');
ylabel('Speed relative to each ear (m/s)');
legend({'left','right'},'Location','SouthEast');

For the first half, the object is coming toward you at 25 m/sec(so the speed is negative). This will increase the pitch of the sound because the sound waves will effectively bunch up in front of the source. In the second half, as the object retreats, the pitch will be lower than the actual sound because the sound waves will be relatively spaced out behind the object.

Interpolating to simulate the sound at your head

Putting this all together might seem like a calculus nightmare. Fortunately it's really easy. All we need to do is re-sample the sound using linear interpolation based on our calculations of the time delay, after attenuating based on the inverse square law. The interpolation step uses 'interp1' which basically re-evaluates our sound wave with a different vector of time.

zero out the auditory stimulus matrix

yy = zeros(length(t),2);

Here's the interesting part: Interpolate back in time to determine sound for each ear and attenuate the sound based on inverse-square law

yy(:,1) = interp1(t,sL,t-tauR);
yy(:,2) = interp1(t,sR,t-tauL);

%clear out the NaN's (at the beginning of time where sound hasn't come to
%the head yet)
yy(isnan(yy))= 0;

yy = yy/max(yy(:));
sound(yy,p.Fs);

'makeAuditoryMotion.m'

I've provided a function 'makeAuditoryMotion' that performs the above calculations.

yy = makeAuditoryMotion(p,s,x,y);
yy = yy/max(yy(:));
sound(yy,p.Fs);

A cheesy animation

For fun, we can make a movie in the figure that moves our sound source over time while the sound is playing.

n= 1;  % number of times to loop.
figure(1)
clf

hold on

% Draw the 'Head'
plot(0,0,'ko','MarkerSize',10,'MarkerFaceColor','y');
axis equal

% Draw the path
plot(x(1:200:end),y(1:200:end),'r:');
h = plot(x(1),y(1),'ko','MarkerFaceColor','r','MarkerSize',5);
xlabel('X (meters)');
ylabel('Y (meters)');

%loop n times

for i=1:n
    %play the sound
    sound(yy,p.Fs);
    %animate the object
    tic
    while toc<p.dur
        %Find index that matches closest to the current time
        id = ceil(toc*p.Fs);
        set(h,'XData',x(id));
        set(h,'Ydata',y(id));
        drawnow
    end
end

animateAuditoryMotion

I've also provided a function 'animateAuditoryMotion' that runs 'makeAuditoryMotion' and plays it along with our cheesy animation. It's essentially the same code as above.

animateAuditoryMotion(p,s,x,y,2);

Recording a sound

Matlab's function 'audiorecorder' allows you to record your own sounds with a built-in microphone or an external one. It's easy to use. Here we'll sample a little over 4 seconds of sound and clip it so it's exactly four seconds so that it's compatible with our existing x and y vectors.

r = audiorecorder(p.Fs, 16, 1);

record(r);     % speak into microphone...
pause(4.2)
stop(r);

s =  getaudiodata(r);
s = s(1:length(t));

sound(s,p.Fs);   % listen to complete recording

Now we'll make it move!

animateAuditoryMotion(p,s,x,y,2);

Other sounds, other paths

Since this is all done with simulation and not with calculus, we can simulate any sound with any path. Here are some examples:

load handel
s = y;
p.Fs = Fs;
p.dur = length(s)/p.Fs;  %duration of sound (second)

t= linspace(0,p.dur,length(s))';

%position of sound over time (in meters, with observer at [0,0])
x = 20*sin(2*pi*t/p.dur);
y = 5*cos(2*pi*t/p.dur)+10;

%translate sound wave and position into stereo sound wave
[yy,p] = animateAuditoryMotion(p,s,x,y,1);
p.dur = 4;
p.Fs = 44100;

t= linspace(0,p.dur,p.dur*p.Fs)';
s = floor(rand(size(t))+.001);

x = linspace(-.5,.5,length(t))';
y = .05*ones(size(t));

[yy,p] = animateAuditoryMotion(p,s,x,y,1);

Exercises

1) Combining sounds is easy. Just add two sound waves together - but be sure that the sum stays between -1 and 1. Generate the sound of low frequency moving from left to right and high frequency moving from right to left. Can you attentively separate the two sounds? What if they are the same frequency?

2) A sonic boom occurs when a noisy object moves at the speed of sound, where the sound waves all pile up in front of the object. Can you simulate a sonic boom?

3) What happens to a sound that is receeding from you faster than the speed of sound?