Web音频API中的自定义波形

19

我正在阅读这篇很棒的文章:https://jackschaedler.github.io/circles-sines-signals/dft_introduction.html

我想使用Web Audio API的PeriodicWave对象来实现该演示:enter image description here

然而,当我使用以下设置来设置周期性波形时:

 var real = new Float32Array([0,0,1,0,1]);
 var imag = new Float32Array(real.length);
 var customWave = context.createPeriodicWave(real,imag);
 osc.setPeriodicWave(customWave);

我输出的波形看起来像这样:

enter image description here 完整代码在此处:http://jsbin.com/zaqojavixo/4/edit 要查看波形,请播放声音几次。

我认为它们应该匹配,所以我有以下问题:

  1. 我是否对理论有什么基本误解或者我的实现方式有误?PeriodicWave对象是否应该与文章中所示的执行相同的操作 ?
  2. 如果我的方法不正确,如何在Web Audio API中实现此图示?我已经能够通过将两个不同频率的正弦波连接到同一个增益节点上来匹配下面的图 - 这与使用PeriodicWave对象有何不同?
  3. 我是DSP和Web Audio API的新手 - 欢迎推荐阅读材料!
  4. 其次,在我的例子中,我必须按下“播放声音”按钮几次才能将正确的数据绘制到画布上 - 即使在我启动振荡器之后调用了analyser.getFloatTimeDomainData()函数,分析仪似乎也比振荡器慢,对此有何看法?

编辑:如评论中所述,我的图形是上下颠倒的(在画布上,0,0是左上角)。

3个回答

16
注意,第一个数组定义了cosine项,第二个数组定义了sine项:

参数 real 表示cosine项的数组(传统上是A项)。 在音频术语中,第一个元素(索引0)是周期波形的直流偏移量。 第二个元素(索引1)表示基频。第三个元素代表第一个谐波,以此类推。 第一个元素将被忽略,实现必须在内部将其设置为零。

参数 imag 表示sine项的数组(传统上是B项)。 第一个元素(索引0)应该设置为零(并将被忽略), 因为这一项在傅里叶级数中不存在。第二个元素(索引1)表示基频。 第三个元素代表第一个谐波,以此类推。

来源

您会发现您得到了预期的波形,但是“反向”绘制(感谢 @Julian在他的回答中指出了这一点 - 已经修复):

snap

(我在此处使用了您的代码,并交换了数组:
更新 修复原始代码中的绘制问题)

//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>


感谢分享这个漂亮的波浪渲染! - Sergio Abreu

2
演示和你的输出之间唯一的区别是两个音调之间的相位关系。演示使用的是 y=sin(x) + sin(2x),而你的则是 y=sin(x) + sin(2x + pi/2)。我不清楚这个相位偏移来自哪里,但我认为这不是你所做的任何事情。
以下是wolfram alpha给出的一些图表:
- y=sin(x) + sin(2x) - y=sin(x) + sin(2x + pi/2)

1
createPeriodicWave中,real数组是余弦部分(或a项),而imag数组(正弦部分)是正弦部分(或b项)。
参见:http://en.wikipedia.org/wiki/Fourier_series 我认为你刚刚颠倒了两个。 在文章中,它说:
“其中Ai是第i个正弦波的振幅,fi是第i个正弦波的频率。”
因此,您的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/

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接