orottier/web-audio-api-rs

Panner node possibly ignore < ref_distance

ggadwa opened this issue · 2 comments

ggadwa commented

Looking at the specs, I think the panner node's distance model is ignoring < ref_distance. The spec says:

"
A double value representing the reference distance for reducing volume as the audio source moves further from the listener. For distances greater than this the volume will be reduced based on rolloffFactor and distanceModel.
"

(emphasis mine)

So if you run this (this can be a test, too):

`
ctx.listener().position_x().set_value(0.0);
ctx.listener().position_y().set_value(0.0);
ctx.listener().position_z().set_value(0.0);

ctx.listener().forward_x().set_value(0.0);
ctx.listener().forward_y().set_value(0.0);
ctx.listener().forward_z().set_value(1.0);

ctx.listener().up_x().set_value(0.0);
ctx.listener().up_y().set_value(1.0);
ctx.listener().up_z().set_value(0.0);

let length = ctx.sample_rate() as usize;
let sample_rate = ctx.sample_rate();
let mut src = ctx.create_buffer_source();
let flat_wave = vec![1.0; length];
let mut buffer = ctx.create_buffer(1, length, sample_rate);
buffer.copy_to_channel(&flat_wave, 0); // copied this from another example, it doesn't need to be this complex

src.set_buffer(buffer.clone());
src.set_loop(true);

let mut analyser = ctx.create_analyser();
let mut bins = vec![0.; analyser.frequency_bin_count()];

let mut panner = ctx.create_panner();
panner.set_panning_model(PanningModelType::EqualPower);
panner.set_distance_model(DistanceModelType::Linear);
panner.set_ref_distance(100.0);
panner.set_max_distance(1100.0);
panner.set_rolloff_factor(1.0);
panner.set_cone_inner_angle(360.0);
panner.set_cone_outer_angle(0.0);
panner.set_cone_outer_gain(0.0);
panner.position_x().set_value(0.0);
panner.position_y().set_value(0.0);
panner.position_z().set_value(0.0);
panner.orientation_x().set_value(1.0);
panner.orientation_y().set_value(0.0);
panner.orientation_z().set_value(0.0);

src.connect(&panner);
panner.connect(&analyser);
src.start();

for i in 0..30 {
let d = i * 50;
panner.position_z().set_value(d as f32);

std::thread::sleep(std::time::Duration::from_millis(100));
analyser.get_float_time_domain_data(&mut bins);
println!("{}>{}", d, bins[0]);

}

`

The output is:

0>0.70710677 50>0.70710677 100>0.70710677 150>0.67175144 200>0.63639605 250>0.6010408 300>0.56568545 350>0.53033006 400>0.49497473 450>0.45961937 500>0.42426407 550>0.38890874 600>0.35355338 650>0.31819803 700>0.28284273 750>0.24748737 800>0.21213204 850>0.17677669 900>0.14142136 950>0.10606602 1000>0.07071068 1050>0.03535534 1100>0 1150>0 1200>0 1250>0 1300>0 1350>0 1400>0 1450>0

If I have the specs right, from 0 to 100 it should be 1.0, and at 100 it should start at 1.0, the stuff after 1100 is 0.0 (which is right.) So basically you are missing the < ref_distance path.

Thanks for the extensive report!

However, seeing the gain for distance 0, 50 and 100 are equal, I do think we apply the ref distance correctly. It is described by the specification at https://webaudio.github.io/web-audio-api/#enumdef-distancemodeltype and implemented at https://github.com/orottier/web-audio-api-rs/blob/main/src/node/panner.rs#L850

Now why is the gain equal to 0.707 instead of 1? 0.707 = sin(pi/4) = cos(pi/4)

If I read your example right, the listener is looking along the z-axis, and you are moving the sound away along this same z-axis. This means both ears receive the same volume, because both ears are pointed 'sideways' towards to sound source.

If you change the listener to point one of the ears towards the z-direction, I believe that ear should receive the 1.0 signal and the other ear will hear nothing.

This is the equalpower panning model: https://webaudio.github.io/web-audio-api/#Spatialization-equal-power-panning

Let me know if you follow my explanation or not. I always find it very hard to reason about 3d space, directions of movement and ears. As you notice, perhaps the PannerNode is one of the lesser tested features in this library so it is perfectly possible I misunderstand some of the specification.

ggadwa commented

Nope, close this one, I went and redid the example in chrome, and chrome returns the exact same results. This doesn't mean that chrome isn't wrong, but I'm going to tend to trust that as a reference. Should have done this first! I completely forgot about that panning mode.