网络音频中的自定义波形 API
custom wave forms in web audio API
我正在阅读这篇很棒的文章:https://jackschaedler.github.io/circles-sines-signals/dft_introduction.html
我想使用 Web Audio API 的 PeriodicWave 对象来实现这个演示:
但是,当我使用这些设置设置周期波时:
var real = new Float32Array([0,0,1,0,1]);
var imag = new Float32Array(real.length);
var customWave = context.createPeriodicWave(real,imag);
osc.setPeriodicWave(customWave);
我输出了一个看起来像这样的波:
这是完整的代码:http://jsbin.com/zaqojavixo/4/edit
要查看波形,请按几次播放声音。
我认为这些应该匹配,所以这是我的问题:
- 我是不是遗漏了一些关于理论的基本知识,还是我只是错误地实施了它? PeriodicWave 对象是否应该做与文章中说明的相同的事情?
- 如果我采用了错误的方法,我将如何在 Web Audio 中实现此图 API?通过将两个不同频率的不同正弦波连接到同一增益节点,我已经能够在下面进行匹配 - 这与使用 PeriodicWave 对象有何不同?
- 我是 DSP 和网络音频的新手 API - 任何建议的阅读都将不胜感激!
- 其次,在我的示例中,我必须按几次 'play the sound' 按钮才能将正确的数据绘制到 canvas - 分析仪似乎在振荡器后面,即使analyser.getFloatTimeDomainData() 在我启动振荡器后被调用,对这里发生的事情有什么想法吗?
编辑:如评论中所述,我的图表是颠倒的(canvas 0,0 是左上角)。
演示和您的输出之间的唯一区别是两个音调之间的相位关系。演示是 y=sin(x) + sin(2x)
,你的是 y=sin(x) + sin(2x + pi/2)
。我不清楚这种相移是从哪里来的,但我认为这不是你所做的任何事情。
以下是 wolfram alpha 中的一些绘图:
请注意,第一个数组定义余弦项,第二个数组定义正弦项:
The real
parameter represents an array of cosine terms (traditionally
the A terms). In audio terminology, the first element (index 0) is the
DC-offset of the periodic waveform. The second element (index 1)
represents the fundamental frequency. The third element represents the
first overtone, and so on. The first element is ignored and
implementations must set it to zero internally.
The imag
parameter represents an array of sine terms (traditionally
the B terms). The first element (index 0) should be set to zero (and
will be ignored) since this term does not exist in the Fourier series.
The second element (index 1) represents the fundamental frequency. The
third element represents the first overtone, and so on.
你会看到你得到了预期的波形,但是 "reversed"(由于@Julian 在他的回答中指出了这一点而被颠倒了 - 在下面修复):
(我在这里内联了你的代码,交换了数组:)
已更新 修复了原始代码中的绘图问题
//setup audio context
window.AudioContext = window.AudioContext || window.webkitAudioContext;
var context = new window.AudioContext();
//create nodes
var osc; //create in event listener so we can press the button more than once
var masterGain = context.createGain();
var analyser = context.createAnalyser();
//routing
masterGain.connect(analyser);
analyser.connect(context.destination);
var isPlaying = false;
//draw function for canvas
function drawWave(analyser, ctx) {
var buffer = new Float32Array(1024),
w = ctx.canvas.width;
ctx.strokeStyle = "#777";
ctx.setTransform(1,0,0,-1,0,100.5); // flip y-axis and translate to center
ctx.lineWidth = 2;
(function loop() {
analyser.getFloatTimeDomainData(buffer);
ctx.clearRect(0, -100, w, ctx.canvas.height);
ctx.beginPath();
ctx.moveTo(0, buffer[0] * 90);
for (var x = 2; x < w; x += 2) ctx.lineTo(x, buffer[x] * 90);
ctx.stroke();
if (isPlaying) requestAnimationFrame(loop)
})();
}
//button trigger
$(function() {
var c = document.getElementById('scope'),
ctx = c.getContext("2d");
c.height = 200;
c.width = 600;
// make 0-line permanent as background
ctx.moveTo(0, 100.5);
ctx.lineTo(c.width, 100.5);
ctx.stroke();
c.style.backgroundImage = "url(" + c.toDataURL() + ")";
$('button').on('mousedown', function() {
osc = context.createOscillator();
//osc settings
osc.frequency.value = 220;
var imag= new Float32Array([0,0,1,0,1]); // sine
var real = new Float32Array(imag.length); // cos
var customWave = context.createPeriodicWave(real, imag); // cos,sine
osc.setPeriodicWave(customWave);
osc.connect(masterGain);
osc.start();
isPlaying = true;
drawWave(analyser, ctx);
});
$('button').on('mouseup', function() {
isPlaying = false;
osc.stop();
});
});
button {position:fixed;left:10px;top:10px}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button>Play the sound</button>
<canvas id='scope'></canvas>
在createPeriodicWave
中,real
数组是余弦部分(或a项),而imag
数组(b term) 是正弦部分。
参见:http://en.wikipedia.org/wiki/Fourier_series
我想你刚刚把两者颠倒了。文章中说:
Where Ai is the Amplitude of the ith sine and fi is the frequency of
the ith sine.
所以你的 imag
数组(正弦部分)应该是 [0,0,1,0,1] 而你的真实数组应该是 [0,0,0,0,0]。
像这样:
var imag = new Float32Array([0,0,1,0,1]);
var real = new Float32Array(real.length);
在您的 jsbin 中尝试它会起作用,唯一的事情是它会反转,因为您绘制的是负数(意味着 0 坐标是顶部,而不是底部)。要获得文章中的内容,请反向绘制或使用这样的 imag 数组:[0,0,-1,0,-1].
如果你想用不同的 imag
和 real
配置玩一下并查看结果,你可以在这里看到:http://themusictoolbox.net/waves/
我正在阅读这篇很棒的文章:https://jackschaedler.github.io/circles-sines-signals/dft_introduction.html
我想使用 Web Audio API 的 PeriodicWave 对象来实现这个演示:
但是,当我使用这些设置设置周期波时:
var real = new Float32Array([0,0,1,0,1]);
var imag = new Float32Array(real.length);
var customWave = context.createPeriodicWave(real,imag);
osc.setPeriodicWave(customWave);
我输出了一个看起来像这样的波:
我认为这些应该匹配,所以这是我的问题:
- 我是不是遗漏了一些关于理论的基本知识,还是我只是错误地实施了它? PeriodicWave 对象是否应该做与文章中说明的相同的事情?
- 如果我采用了错误的方法,我将如何在 Web Audio 中实现此图 API?通过将两个不同频率的不同正弦波连接到同一增益节点,我已经能够在下面进行匹配 - 这与使用 PeriodicWave 对象有何不同?
- 我是 DSP 和网络音频的新手 API - 任何建议的阅读都将不胜感激!
- 其次,在我的示例中,我必须按几次 'play the sound' 按钮才能将正确的数据绘制到 canvas - 分析仪似乎在振荡器后面,即使analyser.getFloatTimeDomainData() 在我启动振荡器后被调用,对这里发生的事情有什么想法吗?
编辑:如评论中所述,我的图表是颠倒的(canvas 0,0 是左上角)。
演示和您的输出之间的唯一区别是两个音调之间的相位关系。演示是 y=sin(x) + sin(2x)
,你的是 y=sin(x) + sin(2x + pi/2)
。我不清楚这种相移是从哪里来的,但我认为这不是你所做的任何事情。
以下是 wolfram alpha 中的一些绘图:
请注意,第一个数组定义余弦项,第二个数组定义正弦项:
The
real
parameter represents an array of cosine terms (traditionally the A terms). In audio terminology, the first element (index 0) is the DC-offset of the periodic waveform. The second element (index 1) represents the fundamental frequency. The third element represents the first overtone, and so on. The first element is ignored and implementations must set it to zero internally.The
imag
parameter represents an array of sine terms (traditionally the B terms). The first element (index 0) should be set to zero (and will be ignored) since this term does not exist in the Fourier series. The second element (index 1) represents the fundamental frequency. The third element represents the first overtone, and so on.
你会看到你得到了预期的波形,但是 "reversed"(由于@Julian 在他的回答中指出了这一点而被颠倒了 - 在下面修复):
(我在这里内联了你的代码,交换了数组:)
已更新 修复了原始代码中的绘图问题
//setup audio context
window.AudioContext = window.AudioContext || window.webkitAudioContext;
var context = new window.AudioContext();
//create nodes
var osc; //create in event listener so we can press the button more than once
var masterGain = context.createGain();
var analyser = context.createAnalyser();
//routing
masterGain.connect(analyser);
analyser.connect(context.destination);
var isPlaying = false;
//draw function for canvas
function drawWave(analyser, ctx) {
var buffer = new Float32Array(1024),
w = ctx.canvas.width;
ctx.strokeStyle = "#777";
ctx.setTransform(1,0,0,-1,0,100.5); // flip y-axis and translate to center
ctx.lineWidth = 2;
(function loop() {
analyser.getFloatTimeDomainData(buffer);
ctx.clearRect(0, -100, w, ctx.canvas.height);
ctx.beginPath();
ctx.moveTo(0, buffer[0] * 90);
for (var x = 2; x < w; x += 2) ctx.lineTo(x, buffer[x] * 90);
ctx.stroke();
if (isPlaying) requestAnimationFrame(loop)
})();
}
//button trigger
$(function() {
var c = document.getElementById('scope'),
ctx = c.getContext("2d");
c.height = 200;
c.width = 600;
// make 0-line permanent as background
ctx.moveTo(0, 100.5);
ctx.lineTo(c.width, 100.5);
ctx.stroke();
c.style.backgroundImage = "url(" + c.toDataURL() + ")";
$('button').on('mousedown', function() {
osc = context.createOscillator();
//osc settings
osc.frequency.value = 220;
var imag= new Float32Array([0,0,1,0,1]); // sine
var real = new Float32Array(imag.length); // cos
var customWave = context.createPeriodicWave(real, imag); // cos,sine
osc.setPeriodicWave(customWave);
osc.connect(masterGain);
osc.start();
isPlaying = true;
drawWave(analyser, ctx);
});
$('button').on('mouseup', function() {
isPlaying = false;
osc.stop();
});
});
button {position:fixed;left:10px;top:10px}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button>Play the sound</button>
<canvas id='scope'></canvas>
在createPeriodicWave
中,real
数组是余弦部分(或a项),而imag
数组(b term) 是正弦部分。
参见:http://en.wikipedia.org/wiki/Fourier_series
我想你刚刚把两者颠倒了。文章中说:
Where Ai is the Amplitude of the ith sine and fi is the frequency of the ith sine.
所以你的 imag
数组(正弦部分)应该是 [0,0,1,0,1] 而你的真实数组应该是 [0,0,0,0,0]。
像这样:
var imag = new Float32Array([0,0,1,0,1]);
var real = new Float32Array(real.length);
在您的 jsbin 中尝试它会起作用,唯一的事情是它会反转,因为您绘制的是负数(意味着 0 坐标是顶部,而不是底部)。要获得文章中的内容,请反向绘制或使用这样的 imag 数组:[0,0,-1,0,-1].
如果你想用不同的 imag
和 real
配置玩一下并查看结果,你可以在这里看到:http://themusictoolbox.net/waves/