将一个音频信号除以另一个
Dividing one audio signal by another one
简短版
我需要将一个音频信号除以另一个音频信号(振幅方向)。我如何在不使用 ScriptProcessorNode 的情况下在网络音频 API 中完成此操作? (使用 ScriptProcessorNode 任务很简单,但由于固有的性能问题,它完全无法用于生产)
长版
考虑两个音频源,例如两个 OscillatorNodes,oscA
和 oscB
:
var oscA = audioCtx.createOscillator();
var oscB = audioCtx.createOscillator();
现在,考虑这些振荡器是低频振荡器,它们的频率都很低(即 <20Hz),并且它们的信号用于控制 单个目标 AudioParam, for example, the gain of a GainNode .通过各种路由设置,我们可以定义这两个信号之间的数学运算。
加法
如果oscA
和oscB
都直接连接到目标AudioParam,它们的输出添加一起:
var dest = audioCtx.createGain();
oscA.connect(dest.gain);
oscB.connect(dest.gain);
减法
如果oscB
的输出首先通过另一个增益为-1
的GainNode,然后连接到目标AudioParam,那么oscB
的输出实际上是从 oscA
的输出中减去 ,因为我们实际上是在执行一个 oscA + -oscB
操作。使用这个技巧,我们可以从一个信号中减去另一个信号:
var dest = audioCtx.createGain();
var inverter = audioCtx.createGain();
oscA.connect(dest.gain);
oscB.connect(inverter);
inverter.gain = -1;
inverter.connect(dest.gain);
乘法
类似地,如果oscA
的输出连接到另一个 GainNode,而oscB
的输出连接到 gain AudioParam of that GainNode,那么oscB
就是乘以oscA
的信号:
var dest = audioCtx.createGain();
var multiplier = audioCtx.createGain();
oscA.connect(multiplier);
oscB.connect(multiplier.gain);
multiplier.connect(dest.gain);
除法(?)
现在,我想要 oscB
的输出 除 oscA
的输出。 我该怎么做,不使用 ScriptProcessorNode?
编辑
我早些时候绝对荒谬解决这个问题的尝试是:
- 使用 PannerNode 控制
positionZ
参数,did 产生的结果随着信号 B (oscB
) 的增加而降低,但它是完全关闭(例如它产生了 12/1 = 8.5
和 12/2 = 4.2
)——现在这个值可以通过使用增益设置为 12 / 8.48528099060058593750
(近似值)的 GainNode 来补偿,但它只支持值>=1
- 使用 AnalyserNode 快速采样音频信号,然后使用该值(笑)
编辑 2
ScriptProcessorNode 对于比技术演示更复杂的应用程序基本上无用的原因是:
- 它在主线程 (!) 上执行音频处理,繁重的 UI 工作会引入音频故障
- 一个简单的 ScriptProcessorNode 在现代设备上将占用 5% CPU 电源,因为它使用 JavaScript 执行处理并需要在音频线程(或渲染线程)之间传递数据) 和主线程(或 UI 线程)
需要注意的是,ScriptProcessorNode
是deprecated。
如果你需要A/B
,那么你就需要1/B
,反转信号。您可以使用 WaveShaperNode 进行反转。该节点需要一个对应值的数组。反转意味着 -1 变成 -1,-0.5 变成 -2 等
此外,请确保您了解被零除的知识。你必须处理它。在下面的代码中,我只取零后的下一个值并将其加倍。
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;
};
工作fiddle is here. If you remove .connect(distortion)
out of the audio chain, you see a simple sine wave. Visualization code got from sonoport。
简短版
我需要将一个音频信号除以另一个音频信号(振幅方向)。我如何在不使用 ScriptProcessorNode 的情况下在网络音频 API 中完成此操作? (使用 ScriptProcessorNode 任务很简单,但由于固有的性能问题,它完全无法用于生产)
长版
考虑两个音频源,例如两个 OscillatorNodes,oscA
和 oscB
:
var oscA = audioCtx.createOscillator();
var oscB = audioCtx.createOscillator();
现在,考虑这些振荡器是低频振荡器,它们的频率都很低(即 <20Hz),并且它们的信号用于控制 单个目标 AudioParam, for example, the gain of a GainNode .通过各种路由设置,我们可以定义这两个信号之间的数学运算。
加法
如果oscA
和oscB
都直接连接到目标AudioParam,它们的输出添加一起:
var dest = audioCtx.createGain();
oscA.connect(dest.gain);
oscB.connect(dest.gain);
减法
如果oscB
的输出首先通过另一个增益为-1
的GainNode,然后连接到目标AudioParam,那么oscB
的输出实际上是从 oscA
的输出中减去 ,因为我们实际上是在执行一个 oscA + -oscB
操作。使用这个技巧,我们可以从一个信号中减去另一个信号:
var dest = audioCtx.createGain();
var inverter = audioCtx.createGain();
oscA.connect(dest.gain);
oscB.connect(inverter);
inverter.gain = -1;
inverter.connect(dest.gain);
乘法
类似地,如果oscA
的输出连接到另一个 GainNode,而oscB
的输出连接到 gain AudioParam of that GainNode,那么oscB
就是乘以oscA
的信号:
var dest = audioCtx.createGain();
var multiplier = audioCtx.createGain();
oscA.connect(multiplier);
oscB.connect(multiplier.gain);
multiplier.connect(dest.gain);
除法(?)
现在,我想要 oscB
的输出 除 oscA
的输出。 我该怎么做,不使用 ScriptProcessorNode?
编辑
我早些时候绝对荒谬解决这个问题的尝试是:
- 使用 PannerNode 控制
positionZ
参数,did 产生的结果随着信号 B (oscB
) 的增加而降低,但它是完全关闭(例如它产生了12/1 = 8.5
和12/2 = 4.2
)——现在这个值可以通过使用增益设置为12 / 8.48528099060058593750
(近似值)的 GainNode 来补偿,但它只支持值>=1
- 使用 AnalyserNode 快速采样音频信号,然后使用该值(笑)
编辑 2
ScriptProcessorNode 对于比技术演示更复杂的应用程序基本上无用的原因是:
- 它在主线程 (!) 上执行音频处理,繁重的 UI 工作会引入音频故障
- 一个简单的 ScriptProcessorNode 在现代设备上将占用 5% CPU 电源,因为它使用 JavaScript 执行处理并需要在音频线程(或渲染线程)之间传递数据) 和主线程(或 UI 线程)
需要注意的是,ScriptProcessorNode
是deprecated。
如果你需要A/B
,那么你就需要1/B
,反转信号。您可以使用 WaveShaperNode 进行反转。该节点需要一个对应值的数组。反转意味着 -1 变成 -1,-0.5 变成 -2 等
此外,请确保您了解被零除的知识。你必须处理它。在下面的代码中,我只取零后的下一个值并将其加倍。
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;
};
工作fiddle is here. If you remove .connect(distortion)
out of the audio chain, you see a simple sine wave. Visualization code got from sonoport。