Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
208 changes: 131 additions & 77 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2918,9 +2918,11 @@ <h2 id="AudioListener">The AudioListener Interface</h2>
<h2>The StereoPannerNode Interface</h2>

<p>
This interface represents a processing node which positions an incoming audio
stream in a stereo image using a low-cost equal-power panning algorithm. This
panning effect is common in positioning audio components in a stereo stream.</p>
This interface represents a processing node which positions an incoming audio
stream in a stereo image using a low-cost <a
href="#equal-power">equal-power</a> panning algorithm. This panning effect
is common in positioning audio components in a stereo stream.
</p>

<pre>
numberOfInputs : 1
Expand All @@ -2932,17 +2934,25 @@ <h2>The StereoPannerNode Interface</h2>
</pre>

<p>
The input of this node is stereo (2 channels) and cannot be increased. Connections from nodes with fewer or more channels will be <a href="#channel-up-mixing-and-down-mixing">up-mixed or down-mixed appropriately</a>, but a NotSupportedError will be thrown if an attempt is made to set channelCount to a value great than 2 or if channelCountMode is set to "max".
The input of this node is stereo (2 channels) and cannot be increased.
Connections from nodes with fewer or more channels will be <a
href="#channel-up-mixing-and-down-mixing">up-mixed or down-mixed
appropriately</a>, but a NotSupportedError will be thrown if an attempt is made
to set channelCount to a value great than 2 or if channelCountMode is set to
<code>"max"</code>.
</p>

<p>
The output of this node is hard-coded to stereo (2 channels) and cannot be configured.
The output of this node is hard-coded to stereo (2 channels) and cannot be
configured.
</p>

<dl title="interface StereoPannerNode : AudioNode" class=idl>
<dt>readonly attribute AudioParam pan</dt>
<dd>
The position of the input in the output's stereo image. -1 represents full left, +1 represents full right. Its default value is 0, and its nominal range is from -1 to 1.
The position of the input in the output's stereo image. -1 represents full
left, +1 represents full right. Its default value is 0, and its nominal
range is from -1 to 1. This parameter is a <a>a-rate</a>.
</dd>

</dl>
Expand Down Expand Up @@ -6267,10 +6277,12 @@ <h3 id="Spatialization-background">Background</h3>
href="#Spatialization-panning-algorithm">Panning Algorithm</a> section for
details of how these values are used.
</p>

</section>
<section id="azimuth-elevation">
<h3>Azimuth and Elevation</h3>
<p>
The following algorithm must be used to calculate the <em>azimuth</em>
and <em>elevation</em>:
and <em>elevation</em>: for the <a><code>PannerNode</code></a>
</p>

<pre class="code highlight">
Expand Down Expand Up @@ -6335,87 +6347,129 @@ <h3 id="Spatialization-panning-algorithm">Panning Algorithm</h3>
<p>The following algorithms must be implemented: </p>
<ul>
<li>Equal-power (Vector-based) panning
<p>This is a simple and relatively inexpensive algorithm which provides
basic, but reasonable results. It is commonly used when panning musical sources.
</p>
The <em>elevation</em> value is ignored in this panning algorithm.

<p>
The following steps are used for processing:
This is a simple and relatively inexpensive algorithm which provides
basic, but reasonable results. It is used for the
<a><code>StereoPannerNode</code></a>, and for the
<a><code>PannerNode</code></a> when the <a><code>panningModel</code></a>
attribute is set to <code>"equalpower"</code>, in which case the the
<em>elevation</em> value is ignored.
</p>

<ol>

<li>
<p>
The <em>azimuth</em> value is first contained to be within the range -90 &lt;= <em>azimuth</em> &lt;= +90 according to:
</p>
<pre class="highlight">
// Clamp azimuth to allowed range of -180 -&gt; +180.
azimuth = max(-180, azimuth);
azimuth = min(180, azimuth);

// Now wrap to range -90 -&gt; +90.
if (azimuth &lt; -90)
azimuth = -180 - azimuth;
else if (azimuth &gt; 90)
azimuth = 180 - azimuth;
</pre>
</li>
For a <a><code>PannerNode</code></a>, the following algorithm MUST be
implemented.
<ol>
<li>
Let <code>azimuth</code> be the value computed in the <a>azimuth and
elevation</a> section.
</li>
<li>
<p>
The <em>azimuth</em> value is first contained to be within the range -90 &lt;= <em>azimuth</em> &lt;= +90 according to:
</p>
<pre class="highlight">
// Clamp azimuth to allowed range of -180 -&gt; +180.
azimuth = max(-180, azimuth);
azimuth = min(180, azimuth);

// Now wrap to range -90 -&gt; +90.
if (azimuth &lt; -90) {
azimuth = -180 - azimuth;
} else if (azimuth &gt; 90) {
azimuth = 180 - azimuth;
}
</pre>
</li>

<li>
<p>
A 0 -&gt; 1 normalized value <em>x</em> is calculated from <em>azimuth</em> for <em>mono->stereo</em> as:
</p>
<pre class="highlight">
x = (azimuth + 90) / 180
</pre>
<li>
<p>
A 0 -&gt; 1 normalized value <em>x</em> is calculated from <em>azimuth</em> for <em>mono->stereo</em> as:
</p>
<pre class="highlight">
x = (azimuth + 90) / 180
</pre>

<p>
Or for <em>stereo->stereo</em> as:
</p>
<pre class="highlight">
if (azimuth &lt;= 0) { // from -90 -&gt; 0
// inputL -&gt; outputL and "equal-power pan" inputR as in mono case
// by transforming the "azimuth" value from -90 -&gt; 0 degrees into the range -90 -&gt; +90.
x = (azimuth + 90) / 90;
} else { // from 0 -&gt; +90
// inputR -&gt; outputR and "equal-power pan" inputL as in mono case
// by transforming the "azimuth" value from 0 -&gt; +90 degrees into the range -90 -&gt; +90.
x = azimuth / 90;
}
</pre>
</li>
</ol>

<p>
Or for <em>stereo->stereo</em> as:
For a <a><code>StereoPannerNode</code></a>, the following algorithm MUST
be implemented.
<ol>
<li>
Let <code>azimuth</code> be the <a>computedValue</a> of the
<a>pan</a> <a>AudioParam</a> of this
<a><code>StereoPannerNode</code></a>.
</li>
<li>
Normalize the <code>azimuth</code> value to [0; 1]. From mono to
stereo:
<pre class="highlight">
x = (azimuth + 1) / 2;
</pre>
Or when panning stereo to stereo:
<pre class="highlight">
if (azymuth &lt;= 0) {
x = azimuth + 1;
} else {
x = azimuth;
}
</pre>
</li>
</ol>
</p>
<pre class="highlight">
if (azimuth &lt;= 0) { // from -90 -&gt; 0
// inputL -> outputL and "equal-power pan" inputR as in mono case
// by transforming the "azimuth" value from -90 -&gt; 0 degrees into the range -90 -&gt; +90.
x = (azimuth + 90) / 90;
} else { // from 0 -> +90
// inputR -> outputR and "equal-power pan" inputL as in mono case
// by transforming the "azimuth" value from 0 -&gt; +90 degrees into the range -90 -&gt; +90.
x = azimuth / 90;
}
</pre>
</li>

<li>
<p>
Left and right gain values are then calculated:
</p>
<pre class="highlight">
gainL = cos(0.5 * PI * x);
gainR = sin(0.5 * PI * x);
</pre>
</li>

<li>
<p>For <em>mono->stereo</em>, the output is calculated as:</p>
<pre class="highlight">
outputL = input * gainL
outputR = input * gainR
</pre>
<p>Else for <em>stereo->stereo</em>, the output is calculated as:</p>
<pre class="highlight">
if (azimuth &lt;= 0) { // from -90 -&gt; 0
outputL = inputL + inputR * gainL;
outputR = inputR * gainR;
} else { // from 0 -&gt; +90
outputL = inputL * gainL;
outputR = inputR + inputL * gainR;
}
</pre>
</li>
Then following steps are used for processing:

<ol>
<li>
<p>
Left and right gain values are calculated:
</p>
<pre class="highlight">
gainL = cos(0.5 * Math.PI * x);
gainR = sin(0.5 * Math.PI * x);
</pre>
</li>
<li>
<p>
For <em>mono->stereo</em>, the output is calculated as:
</p>
<pre class="highlight">
outputL = input * gainL;
outputR = input * gainR;
</pre>
<p>
Else for <em>stereo->stereo</em>, the output is calculated as:
</p>
<pre class="highlight">
if (azimuth &lt;= 0) {
outputL = inputL + inputR * gainL;
outputR = inputR * gainR;
} else {
outputL = inputL * gainL;
outputR = inputR + inputL * gainR;
}
</pre>
</li>
</ol>
</p>



Expand Down