网络音频 API 中的自定义波形

问题描述 投票:0回答:3

我正在阅读这篇很棒的文章: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 音频 API 的新手 - 任何建议的阅读将不胜感激!
  4. 其次,在我的示例中,我必须按几次“播放声音”按钮才能将正确的数据绘制到画布上 - 分析器似乎位于振荡器后面,即使在之后调用 analysisr.getFloatTimeDomainData()我启动了振荡器,你对这里发生的事情有什么想法吗?

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

javascript audio signal-processing web-audio-api
3个回答
16
投票

请注意,第一个数组定义余弦项,第二个数组定义正弦项:

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

imag
参数表示一组正弦项(传统上 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>


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 个正弦波的频率 第 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]。

© www.soinside.com 2019 - 2024. All rights reserved.