4

I'm trying to use a simple HTML range input to control the panning of my Web Audio API audio but I can only get 3 "positions" for my audio output:
-Center
-100% to the left
-100% to the right.

I would like to have something in between does positions, like 20% left and 80% right and so on...

The code that I'm using is:

//Creating the node
var pannerNode = context.createPanner();
//Getting the value from the HTML input and using it on the position X value 
document.getElementById('panInput').addEventListener('change', function () {
    pannerNode.setPosition(this.value, 0, 0);
});

And it refers to this input on my HTML file:

<input id="panInput" type="range" min="-1" max="1" step="0.001" value="0"/>

Does anyone knows what am I doing wrong?

Artjom B.
  • 61,146
  • 24
  • 125
  • 222
Oliver Drummond
  • 680
  • 1
  • 6
  • 19

3 Answers3

4

You shouldn't need to use two panners - Panner is stereo. This old answer is a great one to this question:

How to create very basic left/right equal power panning with createPanner();

Community
  • 1
  • 1
cwilso
  • 13,610
  • 1
  • 30
  • 35
  • Wow. Thank you for posting this. Panning has been the one part of the Web Audio spec that's been kind of tough for me to wrap my head around. @oliverdrummond, you really should accept this answer instead of mine. It's way, way cleaner. – Kevin Ennis Sep 07 '13 at 05:34
  • Nice! I'll try this one too! Thanks @cwilso! – Oliver Drummond Sep 07 '13 at 20:28
  • Just tried it! It's great! Just changed the input values, from -45 and 45 to -90 and 90, this created a "bigger" stereo image. Thanks a lot! – Oliver Drummond Sep 07 '13 at 21:12
2

I've actually found simple left/right panning to be kind of difficult with the Web Audio API. It's really set up for surround / spatial stuff, and I honestly don't understand it very well.

The way that I usually do panning is like this:

var panLeft = context.createGain();
var panRight = context.createGain();
var merger = context.createMerger(2);

source.connect(panLeft);
source.connect(panRight);
panLeft.connect(merger, 0, 0);
panRight.connect(merger, 0, 1);
merger.connect(context.destination);

document.getElementById('panInput').addEventListener('change', function () {
  var val = this.value;
  panLeft.gain.value = ( val * -0.5 ) + 0.5;
  panRight.gain.value = ( val * 0.5 ) + 0.5;
});

Basically, you send the signal to two gain nodes that you're going to use as your left and right channel. Then you take the value from your range element and use it to set the gain on each of the nodes.

This is sort of the lazy version though. In serious audio apps, there's usually a bit more math involved with the panning to make sure there aren't changes in overall level -- but hopefully this is enough to get you started.

Kevin Ennis
  • 14,226
  • 2
  • 43
  • 44
0

I'm quite sure there is a better and easier way to do that but, for now, it definitely works for me.
If anyone else have a better/cleaner way of doing it, please share it here!
Thanks to Kevin Ennis for giving me this hint!

JavaScript File

//Create a splitter to "separete" the stereo audio data to two channels.
var splitter = context.createChannelSplitter(2);

//Connect your source to the splitter (usually, you will do it with the last audio node before context destination)
audioSource.connect(splitter);

//Create two gain nodes (one for each side of the stereo image)
var panLeft = context.createGain();
var panRight = context.createGain();

//Connect the splitter channels to the Gain Nodes that we've just created
splitter.connect(panRight,0);
splitter.connect(panLeft,1);

//Getting the input data from a "range" input from HTML (the code used on this range will be shown right on the end of this code)
var panPosition = document.getElementById("dispPanPositionLiveInput");
document.getElementById('panControl').addEventListener('change', function () {
  var val = this.value;
  panPosition.value = val;
  panLeft.gain.value = ( val * -0.5 ) + 0.5;
  panRight.gain.value = ( val * 0.5 ) + 0.5;
});

//Create a merger node, to get both signals back together
var merger = context.createChannelMerger(2);

//Connect both channels to the Merger
panLeft.connect(merger, 0, 0);
panRight.connect(merger, 0, 1);

//Connect the Merger Node to the final audio destination (your speakers)
merger.connect(context.destination);

HTML File

< input id="panControl" type="range" min="-1" max="1" step="0.001" value="0"/>

Oliver Drummond
  • 680
  • 1
  • 6
  • 19