diff options
-rw-r--r-- | Cargo.toml | 2 | ||||
-rw-r--r-- | Makefile | 11 | ||||
-rw-r--r-- | src/emscripten_api.rs | 26 | ||||
-rw-r--r-- | src/lib.rs | 12 | ||||
-rw-r--r-- | src/main.rs | 6 | ||||
-rw-r--r-- | web/index.html | 75 | ||||
-rw-r--r-- | web/main.js | 72 |
7 files changed, 139 insertions, 65 deletions
@@ -6,7 +6,7 @@ authors = ["Justin Worthe <justin.worthe@gmail.com>"] [dependencies] bencher = "0.1.2" -[target.'cfg(not(target_os = "emscripten"))'.dependencies] +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] portaudio = "0.7.0" gtk = "0.3.0" cairo-rs = "0.3.0" @@ -1,20 +1,17 @@ all: build build-web: - cargo build --target=wasm32-unknown-emscripten --release + cargo +nightly build --target=wasm32-unknown-unknown --release mkdir -p target/site - cp target/wasm32-unknown-emscripten/release/rusty_microphone.js target/site/ - cp target/wasm32-unknown-emscripten/release/deps/*.wasm target/site/ + cp target/wasm32-unknown-unknown/release/*.wasm target/site/ cp web/* target/site/ build-web-debug: - cargo build --target=wasm32-unknown-emscripten + cargo +nightly build --target=wasm32-unknown-unknown mkdir -p target/site - cp target/wasm32-unknown-emscripten/debug/rusty_microphone.js target/site/ - cp target/wasm32-unknown-emscripten/debug/deps/*.wasm target/site/ + cp target/wasm32-unknown-unknown/debug/*.wasm target/site/ cp web/* target/site/ - build-desktop: cargo build --release diff --git a/src/emscripten_api.rs b/src/emscripten_api.rs index e296818..c7f980f 100644 --- a/src/emscripten_api.rs +++ b/src/emscripten_api.rs @@ -3,11 +3,37 @@ use signal::Signal; use pitch::Pitch; use std::os::raw::c_char; +use std::os::raw::c_void; +use std::mem; use std::ffi::CString; use std::slice; use std::f32; #[no_mangle] +pub extern "C" fn malloc(size: usize) -> *mut c_void { + let mut buf = Vec::with_capacity(size); + let ptr = buf.as_mut_ptr(); + mem::forget(buf); + return ptr as *mut c_void; +} + +#[no_mangle] +pub extern "C" fn free(ptr: *mut c_void, cap: usize) { + unsafe { + // after it's in scope, it can go out of scope in the normal + // RAII cleanup. + let _buf = Vec::from_raw_parts(ptr, 0, cap); + } +} + +#[no_mangle] +pub extern "C" fn free_str(ptr: *mut c_char) { + unsafe { + let _ = CString::from_raw(ptr); + } +} + +#[no_mangle] pub extern "C" fn find_fundamental_frequency(signal_ptr: *const f32, signal_length: isize, sample_rate: f32) -> f32 { let signal_slice = unsafe { &slice::from_raw_parts(signal_ptr, signal_length as usize) @@ -3,18 +3,18 @@ pub mod signal; pub mod correlation; pub mod pitch; -#[cfg(not(target_os = "emscripten"))] +#[cfg(not(target_arch = "wasm32"))] extern crate gtk; -#[cfg(not(target_os = "emscripten"))] +#[cfg(not(target_arch = "wasm32"))] extern crate cairo; -#[cfg(not(target_os = "emscripten"))] +#[cfg(not(target_arch = "wasm32"))] pub mod gui; -#[cfg(not(target_os = "emscripten"))] +#[cfg(not(target_arch = "wasm32"))] extern crate portaudio; -#[cfg(not(target_os = "emscripten"))] +#[cfg(not(target_arch = "wasm32"))] pub mod audio; -#[cfg(target_os = "emscripten")] +#[cfg(target_arch = "wasm32")] pub mod emscripten_api; diff --git a/src/main.rs b/src/main.rs index cfc5629..a05c2f7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ extern crate rusty_microphone; -#[cfg(not(target_os = "emscripten"))] +#[cfg(not(target_arch = "wasm32"))] fn main() { use rusty_microphone::*; @@ -11,7 +11,7 @@ fn main() { } } -#[cfg(target_os = "emscripten")] +#[cfg(target_arch = "wasm32")] fn main() { - println!("Hello Emscripten"); + println!("Hello Wasm"); } diff --git a/web/index.html b/web/index.html index d3f6a4b..2d957cc 100644 --- a/web/index.html +++ b/web/index.html @@ -1,47 +1,48 @@ +<!DOCTYPE html> <html> <head> + <meta charset="utf-8" /> <link rel="stylesheet" href="style.css"> - </head> - <body> - <h1>Rusty Microphone</h1> - <div id="loading"> - Loading... - </div> - <div id="browser-support-error" class="error" style="display:none;"> - <p> - It looks like something has gone wrong while loading. The - most likely reason for this is that this app doesn't support - your web browser. - </p> + </head> + <body> + <h1>Rusty Microphone</h1> + <div id="loading"> + Loading... + </div> + <div id="browser-support-error" class="error" style="display:none;"> + <p> + It looks like something has gone wrong while loading. The + most likely reason for this is that this app doesn't support + your web browser. + </p> - <p> - I test with the latest version of Firefox, but it should - also work with recent versions of Chrome, Microsoft Edge, - Safari and Opera. If you're running one of these browsers, - please make sure you've updated to the latest version. - </p> + <p> + I test with the latest version of Firefox, but it should + also work with recent versions of Chrome, Microsoft Edge, + Safari and Opera. If you're running one of these browsers, + please make sure you've updated to the latest version. + </p> + </div> + <div id="unexpected-error" class="error" style="display:none;"> + <p> + An unexpected error has occurred. Please try again later. + </p> + </div> + <div id="rusty-microphone" style="display:none;"> + <p>The current note being played is <span id="pitch-label">unknown</span></p> + <div id="pitch-indicator-bar-container"> + <div id="pitch-indicator-bar"></div> </div> - <div id="unexpected-error" class="error" style="display:none;"> - <p> - An unexpected error has occurred. Please try again later. - </p> + <div id="pitch-indicator-colours"> + <div id="flat-indicator"></div> + <div id="sharp-indicator"></div> </div> - <div id="rusty-microphone" style="display:none;"> - <p>The current note being played is <span id="pitch-label">unknown</span></p> - <div id="pitch-indicator-bar-container"> - <div id="pitch-indicator-bar"></div> - </div> - <div id="pitch-indicator-colours"> - <div id="flat-indicator"></div> - <div id="sharp-indicator"></div> - </div> - <canvas id="oscilloscope" width="320" height="300"></canvas> + <canvas id="oscilloscope" width="320" height="300"></canvas> - <p>The current framerate is <span id="frame-rate">0</span>FPS</p> - </div> + <p>The current framerate is <span id="frame-rate">0</span>FPS</p> + </div> - <script src="main.js"></script> - <script src="rusty_microphone.js"></script> - </body> + <script src="main.js"></script> + </body> </html> diff --git a/web/main.js b/web/main.js index 02e94fc..8d7fa33 100644 --- a/web/main.js +++ b/web/main.js @@ -5,7 +5,30 @@ var Module = { onRuntimeInitialized: main, onAbort: onAbort }; -checkBrowserSupport(); + +var env = { + log2f: Math.log2, + roundf: Math.round +}; +checkBrowserSupport(function() { + fetch('rusty_microphone.wasm') + .then(response => response.arrayBuffer()) + .then(bytes => WebAssembly.instantiate(bytes, { env:env })) + .then(results => { + var mod = results.instance; + Module._find_fundamental_frequency = mod.exports.find_fundamental_frequency; + Module._hz_to_pitch = mod.exports.hz_to_pitch; + Module._hz_to_cents_error = mod.exports.hz_to_cents_error; + Module._correlation = mod.exports.correlation; + + Module.memory = mod.exports.memory; + Module._malloc = mod.exports.malloc; + Module._free = mod.exports.free; + Module._free_str = mod.exports.free_str; + + Module.onRuntimeInitialized(); + }); +}); function onAbort(reason) { document.getElementById('loading').setAttribute('style', 'display:none'); @@ -24,11 +47,14 @@ function supportsUserMedia() { typeof AudioContext === "function"; } -function checkBrowserSupport() { +function checkBrowserSupport(supportedCallback) { if (!supportsWasm() || !supportsUserMedia()) { document.getElementById('loading').setAttribute('style', 'display:none'); document.getElementById('browser-support-error').removeAttribute('style'); - } + } + else { + supportedCallback(); + } } /** @@ -49,7 +75,7 @@ function jsArrayToF32ArrayPtr(jsArray, callback) { var nDataBytes = data.length * data.BYTES_PER_ELEMENT; var dataPtr = Module._malloc(nDataBytes); - var dataHeap = new Uint8Array(Module.HEAPU8.buffer, dataPtr, nDataBytes); + var dataHeap = new Uint8Array(Module.memory.buffer, dataPtr, nDataBytes); dataHeap.set(new Uint8Array(data.buffer)); var result = callback(dataPtr, jsArray.length); @@ -79,12 +105,12 @@ function jsArrayToF32ArrayPtrMutateInPlace(jsArray, mutate) { var nDataBytes = data.length * data.BYTES_PER_ELEMENT; var dataPtr = Module._malloc(nDataBytes); - var dataHeap = new Uint8Array(Module.HEAPU8.buffer, dataPtr, nDataBytes); + var dataHeap = new Uint8Array(Module.memory.buffer, dataPtr, nDataBytes); dataHeap.set(new Uint8Array(data.buffer)); mutate(dataPtr, jsArray.length); - var mutatedData = new Float32Array(Module.HEAPU8.buffer, dataPtr, jsArray.length); + var mutatedData = new Float32Array(Module.memory.buffer, dataPtr, jsArray.length); var result = Array.prototype.slice.call(mutatedData); Module._free(dataPtr); @@ -112,21 +138,45 @@ function findFundamentalFrequencyNoFree(data, samplingRate) { if (!dataPtr) { nDataBytes = data.length * data.BYTES_PER_ELEMENT; dataPtr = Module._malloc(nDataBytes); - dataHeap = new Uint8Array(Module.HEAPU8.buffer, dataPtr, nDataBytes); + dataHeap = new Uint8Array(Module.memory.buffer, dataPtr, nDataBytes); } dataHeap.set(new Uint8Array(data.buffer, data.buffer.byteLength - nDataBytes)); return Module._find_fundamental_frequency(dataPtr, data.length, samplingRate); } +/** + * Takes a pointer to a C-style string (ends in a 0), and interprets it as UTF-8. + */ +function copyCStr(ptr) { + var iter = ptr; + + // ye olde 0 terminated string + function* collectCString() { + var memory = new Uint8Array(Module.memory.buffer); + while (memory[iter] !== 0) { + if (memory[iter] === undefined) { + throw new Error("Tried to read undef mem"); + } + yield memory[iter]; + iter += 1; + } + }; + + var buffer_as_u8 = new Uint8Array(collectCString()); + var utf8Decoder = new TextDecoder("UTF-8"); + var buffer_as_utf8 = utf8Decoder.decode(buffer_as_u8); + Module._free_str(ptr); + return buffer_as_utf8; +} + function hzToCentsError(hz) { return Module._hz_to_cents_error(hz); } -var hzToPitch = function(hz) { - var wrapped = Module.cwrap('hz_to_pitch', 'string', ['number']); - hzToPitch = wrapped; - return wrapped(hz); +function hzToPitch(hz) { + var strPtr = Module._hz_to_pitch(hz); + return copyCStr(strPtr); }; function correlation(data, samplingRate) { |