7

I am looking at the web audio API spec and the panning node uses three values to create a 3D spectrum for sound. I was wondering if in order to create a basic 2D "equal power" panner the programmer needs to do the formulaic programming to scale this ... or if I am over thinking it and there is a simpler way to do it.

EDIT

There is now a stereoPanner node being introduced.

William
  • 4,422
  • 17
  • 55
  • 108
  • Yes, the new `createStereoPanner` method works beautifully. Set left and right speaker panning using the pan node's `pan.value` property. Use any values between -1 (panned fully to the left speaker) and 1 (panned fully to the right speaker.) – d13 Apr 01 '15 at 19:26

3 Answers3

9

here's an even simpler (less formulaic?) way to achieve 2D panning:

( full code here )

<input type="range" name="pan" id="pan" min="-1" max="1" step="any" />
<script>
var panner = context.createPanner();
panner.panningModel = 'equalpower';

function pan(event) {
    var x = this.valueAsNumber,
        y = 0,
        z = 1 - Math.abs(x);
    panner.setPosition(x,y,z);
}

document.getElementById('pan').addEventListener(pan);
</script>
code_monk
  • 9,451
  • 2
  • 42
  • 41
  • Just note that the panningModel should be set to "equalpower": `panner.panningModel = "equalpower";` – d13 Feb 23 '14 at 17:40
  • some trig is technically required, but this will do the trick. My code creates a triangle around the listener as they pan. A semi-circle is what should be created, so that the source is always "1" away (1 unit of distance). In the real world, though, the difference would be very hard to hear – code_monk Feb 26 '14 at 05:54
7

I can still get a panning effect by changing only the first argument to setPosition() and keeping other arguments zero.

<!DOCTYPE html>
<html>
<head>
<script>
var c = new webkitAudioContext();
var s = c.createBufferSource();
var g = c.createGainNode();
var p = c.createPanner();
s.connect(g);
g.connect(p);   
p.connect(c.destination);

function play(e) {
  var fr = new FileReader();
  var file = document.getElementById("file").files[0];
  fr.onload = function(e) {
    c.decodeAudioData(e.target.result, 
      function (buf) {
        s.buffer = buf;
        g.gain.value = 0.5;
        s.noteOn(0)
      },
      function () {
        console.error('decodeAudioData failed.');
      }
    );
  };
  fr.readAsArrayBuffer(file);
}

function pan(range) {
  var x = Math.sin(range.value * (Math.PI / 180));
  p.setPosition(x, 0, 0);
}
</script>
</head>
<body>
  Choose your MP3 file:<br>
  <input type="file" id="file" name="file" /><br>
  <input type="submit" id="go" onclick="play()" value="Play" /><br>
  L<input type="range" min="-45" max="45" value="0" onchange="pan(this);">R
</body>
</html>

But to get a natural panning effect, you need to specify the third argument as well.

function pan(range) {
  var xDeg = parseInt(range.value);
  var zDeg = xDeg + 90;
  if (zDeg > 90) {
    zDeg = 180 - zDeg;
  }
  var x = Math.sin(xDeg * (Math.PI / 180));
  var z = Math.sin(zDeg * (Math.PI / 180));
  p.setPosition(x, 0, z);
}
kuu
  • 815
  • 1
  • 7
  • 16
  • So in short you are saying "yes" you need to add the formulaic programming I mentioned. If so I'll try your code and vote up in a few days if nobody else responds. Thanks for doing all that work. I didn't expect that. – William Jan 20 '13 at 11:23
  • If you call the above code formulaic, the answer is "yes". I don't know any other simpler way, sorry... – kuu Jan 20 '13 at 13:43
1

The panner node defaults to "HRTF" (Head Related Transfer Function) which is a stereo convolution engine and it is designed for 3D sound.

In order to have basic panning functionality and lower resource usage you need to set panningModel attribute to "equalpower".

var panner = context.createPanner();
panner.panningModel = "equalpower";
panner.setPosition(1,0,0);

Check the documentation for more details.

zya
  • 830
  • 11
  • 25
  • 1
    This will only create a hard left/right pan, unless the z value is set proportionately according to sean9999's solution: 'panner.setPosition(x, 0 , 1-Math.abs(x));' – d13 Feb 23 '14 at 17:43