I am trying to simulate the Doppler effect in a game (a car racing game). I am not using a specific sound library that simulates the effect; I only have a callback function where I mix the data.
I already figured out how to change frequency of a sample in the mixer function.
What I don't know is how much the frequency should change, depending on player and emitter position and velocity.
Here is what I have in the game:
//player
vec3 p.pos;
vec3 p.vel;
//emitter
vec3 e.pos;
vec3 e.vel;
According to Wikipedia, the relation between emitted frequency and observed frequency is given by:
float f = (c + vr) / (c + vs) * fo
where c is a constant, the velocity of waves in the medium (typically a big number), and vs and vr are source and receiver velocities relative to medium.
So I guess:
float vr = p.vel.length; //player speed
float vs = e.vel.length; //emitter speed
However, I think this is wrong. It won't produce any change in frequency (for example) if vr = 0
(player doesn't move) and the emitter has constant speed, then vr
and vs
won't change (while they should).
Maybe should I calculate the velocity of the player relatively to the velocity of the emitter, like this?:
relative_speed = distance(p.pos + p.vel, e.pos + e.vel) -
distance(p.pos, e.pos);
Then how should vr
and vs
be found?
Wikipedia gives another formula to simulate the effect of a vehicle when that vehicle passes by the observer:
vr = vs * cos(theta); //theta is angle between observer and emitter //theta = atan2(e.pos.y-p.pos.y, e.pos.x-p.pos.x);?
However, this formula supposes that the receiver doesn't move, which is not the case here. If the player and the emitter move at same speed (or with a small difference), there should be no Doppler effect. This function is also specific to one case, I suppose the final formula should be the same no mater the situation.
EDIT: I'm trying to find a correct formula, using SkimFlux's post:
vr,r = vr.vel * cos(shortest_angle_between ( vr.vel , vs.pos - vr.pos));
vs,r = vs.vel * cos(shortest_angle_between ( vs.vel , vr.pos - vs.pos));
//is there a easier/faster way to find them out?
//note: vr.vel and vs.vel are vectors, the green and red arrows in SkimFlux's picture.
EDIT 2:
For those interested, here is final formula:
vec2 dist = vs.pos - vr.pos;
vr,r = dotproduct(vr.vel, dist) / length(dist)
vs,r = dotproduct(vs.vel, dist) / length(dist)
It uses vector projection:
Then vr,s
and vs,r
should be injected into the first Wikipedia formula:
I tested it and it works successfully, providing great results.