I need to divide an audio signal by another one (amplitude-wise). How could I accomplish this in the Web Audio API, without using ScriptProcessorNode? (with ScriptProcessorNode the task is trivial, but it is completely unusable for production due to the inherent performance issues)
Consider two audio sources, two OscillatorNodes for example, oscA
and oscB
:
var oscA = audioCtx.createOscillator();
var oscB = audioCtx.createOscillator();
Now, consider that these oscillators are LFOs, both with low (i.e. <20Hz) frequencies, and that their signals are used to control a single destination AudioParam, for example, the gain of a GainNode. Through various routing setups, we can define mathematical operations between these two signals.
Addition
If oscA
and oscB
are both directly connected to the destination AudioParam, their outputs are added together:
var dest = audioCtx.createGain();
oscA.connect(dest.gain);
oscB.connect(dest.gain);
Subtraction
If the output of oscB
is first routed through another GainNode with a gain of -1
, which is then connected to the destination AudioParam, then the output of oscB
is effectively subtracted from that of oscA
, because we are effectively doing an oscA + -oscB
op. Using this trick we can subtract one signal from another one:
var dest = audioCtx.createGain();
var inverter = audioCtx.createGain();
oscA.connect(dest.gain);
oscB.connect(inverter);
inverter.gain = -1;
inverter.connect(dest.gain);
Multiplication
Similarly, if the output of oscA
is connected to another GainNode, and the output of oscB
is connected to the gain AudioParam of that GainNode, then oscB
is multiplying the signal of oscA
:
var dest = audioCtx.createGain();
var multiplier = audioCtx.createGain();
oscA.connect(multiplier);
oscB.connect(multiplier.gain);
multiplier.connect(dest.gain);
Division (?)
Now, I want the output of oscB
to divide the output of oscA
. How do I do this, without using ScriptProcessorNode?
Edit
My earlier, absolutely ridiculous attempts at solving this problem were:
positionZ
param, which did yield a result that decreased as signal B (oscB
) increased, but it was completely off (e.g. it yielded 12/1 = 8.5
and 12/2 = 4.2
) -- now this value can be compensated for by using a GainNode with its gain set to 12 / 8.48528099060058593750
(approximation), but it only supports values >=1
Edit 2
The reason why the ScriptProcessorNode is essentially useless for applications more complex than a tech demo is that:
It should be noted, that ScriptProcessorNode
is deprecated.
If you need A/B
, therefore you need 1/B
, inverted signal. You can use WaveShaperNode to make the inversion. This node needs an array of corresponding values. Inversion means that -1 becomes -1, -0.5 becomes -2 etc.
In addition, make sure that you are aware of division by zero. You have to handle it. In the following code I just take the next value after zero and double it.
function makeInverseCurve( ) {
var n_samples = 44100,
curve = new Float32Array(n_samples),
x;
for (var i = 0 ; i < n_samples; i++ ) {
x = i * 2 / n_samples - 1;
// if x = 0, let reverse value be twice the previous
curve[i] = (i * 2 == n_samples) ? n_samples : 1 / x;
}
return curve;
};
Working fiddle is here. If you remove .connect(distortion)
out of the audio chain, you see a simple sine wave. Visualization code got from sonoport.