diff options
author | Justin Worthe <justin@worthe-it.co.za> | 2017-11-03 20:35:43 +0200 |
---|---|---|
committer | Justin Worthe <justin@worthe-it.co.za> | 2017-11-03 20:35:43 +0200 |
commit | 604f56e2e433d9b941e3054612b5c286120f558a (patch) | |
tree | 47f90a0b1f8680905ecaa3129ff8b46f02a06ade /web | |
parent | 669987f0ebd01963d0eb52849fd4f16640350232 (diff) |
Added a connection to the microphone through the web, calling into Rust
Diffstat (limited to 'web')
-rw-r--r-- | web/index.html | 7 | ||||
-rw-r--r-- | web/main.js | 116 |
2 files changed, 110 insertions, 13 deletions
diff --git a/web/index.html b/web/index.html index f70f24b..faf637b 100644 --- a/web/index.html +++ b/web/index.html @@ -3,5 +3,10 @@ <script src="main.js"></script> <script src="rusty_microphone.js"></script> </head> - <body></body> + <body> + <p>The current note being played is <span id="pitch-label"></span></p> + <p>It is <span id="pitch-error-direction"></span> by <span id="pitch-error"></span> cents</p> + + <canvas id="oscilloscope" width="300" height="300" /> + </body> </html> diff --git a/web/main.js b/web/main.js index ace739f..c7ba24b 100644 --- a/web/main.js +++ b/web/main.js @@ -6,7 +6,7 @@ var Module = { }; function jsArrayToF32ArrayPtr(jsArray, callback) { - var data = new Float32Array(jsArray); + var data = (jsArray instanceof Float32Array) ? jsArray : new Float32Array(jsArray); var nDataBytes = data.length * data.BYTES_PER_ELEMENT; var dataPtr = Module._malloc(nDataBytes); @@ -44,14 +44,29 @@ function findFundamentalFrequency(data, samplingRate) { }); } +var dataPtr = null; +var dataHeap = null; +function findFundamentalFrequencyNoFree(data, samplingRate) { + //assume data is already a Float32Array and its length won't change from call to call + if (!dataPtr) { + var nDataBytes = data.length * data.BYTES_PER_ELEMENT; + dataPtr = Module._malloc(nDataBytes); + dataHeap = new Uint8Array(Module.HEAPU8.buffer, dataPtr, nDataBytes); + } + dataHeap.set(new Uint8Array(data.buffer)); + return Module._find_fundamental_frequency(dataPtr, data.length, samplingRate); +} + + function hzToCentsError(hz) { return Module._hz_to_cents_error(hz); } -function hzToPitch(hz) { +var hzToPitch = function(hz) { var wrapped = Module.cwrap('hz_to_pitch', 'string', ['number']); + hzToPitch = wrapped; return wrapped(hz); -} +}; function correlation(data) { return jsArrayToF32ArrayPtrMutateInPlace(data, function(dataPtr, dataLength) { @@ -59,15 +74,92 @@ function correlation(data) { }); } -function main() { - var data = [1, 0, -1, 0, 1, 0, -1, 0, 1, 0, -1, 0, 1, 0, -1, 0]; - var fundamental = findFundamentalFrequency(data, 44100.0); - var correlated = correlation(data); +function update(signal, sampleRate) { + var fundamental = findFundamentalFrequencyNoFree(signal, sampleRate); + + var pitch = hzToPitch(fundamental); + var error = hzToCentsError(fundamental); + + document.getElementById('pitch-label').innerHTML = pitch; + if (error > 0) { + document.getElementById('pitch-error-direction').innerHTML = 'sharp'; + document.getElementById('pitch-error').innerHTML = error; + } else { + document.getElementById('pitch-error-direction').innerHTML = 'flat'; + document.getElementById('pitch-error').innerHTML = -error; + } +} + +function draw(dataArray, canvas, canvasCtx) { + // This draw example is currently heavily based on an example + // from MDN: + // https://developer.mozilla.org/en-US/docs/Web/API/AnalyserNode + var bufferLength = dataArray.length; + + canvasCtx.fillStyle = 'rgb(200, 200, 200)'; + canvasCtx.fillRect(0, 0, canvas.width, canvas.height); - var error = hzToCentsError(450.0); - var pitch = hzToPitch(450.0); + canvasCtx.lineWidth = 2; + canvasCtx.strokeStyle = 'rgb(0, 0, 0)'; + + canvasCtx.beginPath(); + + var sliceWidth = canvas.width * 1.0 / bufferLength; + var x = 0; + + for (var i = 0; i < bufferLength; i++) { + + var v = dataArray[i] / 128.0; + var y = v * canvas.height / 2; + + if (i === 0) { + canvasCtx.moveTo(x, y); + } else { + canvasCtx.lineTo(x, y); + } + + x += sliceWidth; + } + + canvasCtx.lineTo(canvas.width, canvas.height / 2); + canvasCtx.stroke(); +}; + +function main() { + var canvas = document.getElementById("oscilloscope"); + var canvasCtx = canvas.getContext("2d"); - console.log("Javascript here. Our fundamental frequency according to Rust is " + fundamental + "Hz"); - console.log("The other math shows a pitch of " + pitch + ", and an error of " + error); - console.log("Correlation of the array is " + correlated); + navigator.mediaDevices.getUserMedia({ audio: true }) + .then(function(stream) { + var context = new window.AudioContext(); + var input = context.createMediaStreamSource(stream); + var analyser = context.createAnalyser(); + input.connect(analyser); + + var lastTimestamp = 0; + var timestampMod = 0; + + function analyserNodeCallback(timestamp) { + timestampMod += 1; + if (timestampMod === 100) { + timestampMod = 0; + var dt = timestamp - lastTimestamp; + lastTimestamp = timestamp; + var framerate = 100000/dt; + console.log("Framerate is", framerate); + } + + var dataArray = new Float32Array(analyser.fftSize); + analyser.getFloatTimeDomainData(dataArray); + update(dataArray, context.sampleRate); + draw(dataArray, canvas, canvasCtx); + window.requestAnimationFrame(analyserNodeCallback); + } + + window.requestAnimationFrame(analyserNodeCallback); + }) + .catch(function(err) { + console.err('Could not get the microphone'); + console.err(err); + }); } |