I'm working through this awesome article: https://jackschaedler.github.io/circles-sines-signals/dft_introduction.html
I want to use the Web Audio API's PeriodicWave object to implement this demo:
However, when I set a periodic wave with these settings:
var real = new Float32Array([0,0,1,0,1]);
var imag = new Float32Array(real.length);
var customWave = context.createPeriodicWave(real,imag);
osc.setPeriodicWave(customWave);
I output a wave that looks like this:
Here is full code: http://jsbin.com/zaqojavixo/4/edit To see the waveform, please press play the sound a few times.
I believe these should match up, so here are my questions:
Edit: As noted in comments, my graph is upside down (on the canvas 0,0 is the upper left corner).
Note that the first array defines the cosine terms, the second the sine terms:
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.
You will see you get the expected waveform but "reversed" (drawn upside down thanks to @Julian for pointing that out in his answer - fixed below):
(I inlined your code here with the arrays swapped around:)
updated fixed drawing issues in original code
//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>