From 1acdd32f9fdba10ba4fa32f60803101d41daedbd Mon Sep 17 00:00:00 2001 From: Otto Date: Sat, 25 Nov 2023 19:40:31 +0100 Subject: [PATCH 01/34] Copy over some web-platform-tests From https://github.com/web-platform-tests/wpt/tree/master/webaudio Only leave 'the-audiobuffer-interface' for now. Drop the crashtests/ dir as they hang the runner --- webaudio/META.yml | 4 + webaudio/README.md | 5 + webaudio/historical.html | 29 + webaudio/idlharness.https.window.js | 72 + webaudio/js/buffer-loader.js | 44 + webaudio/js/helpers.js | 250 +++ webaudio/js/worklet-recorder.js | 55 + webaudio/resources/4ch-440.wav | Bin 0 -> 353022 bytes webaudio/resources/audio-param.js | 44 + .../resources/audiobuffersource-testing.js | 102 ++ webaudio/resources/audionodeoptions.js | 292 ++++ webaudio/resources/audioparam-testing.js | 554 +++++++ webaudio/resources/audit-util.js | 195 +++ webaudio/resources/audit.js | 1445 +++++++++++++++++ webaudio/resources/biquad-filters.js | 376 +++++ webaudio/resources/biquad-testing.js | 172 ++ webaudio/resources/convolution-testing.js | 168 ++ webaudio/resources/delay-testing.js | 66 + webaudio/resources/distance-model-testing.js | 196 +++ webaudio/resources/merger-testing.js | 24 + webaudio/resources/mix-testing.js | 23 + webaudio/resources/mixing-rules.js | 350 ++++ webaudio/resources/note-grain-on-testing.js | 165 ++ webaudio/resources/panner-formulas.js | 190 +++ webaudio/resources/panner-model-testing.js | 184 +++ webaudio/resources/sin_440Hz_-6dBFS_1s.wav | Bin 0 -> 88246 bytes webaudio/resources/start-stop-exceptions.js | 45 + webaudio/resources/stereopanner-testing.js | 205 +++ .../the-audiobuffer-interface/.gitkeep | 0 .../acquire-the-content.html | 85 + .../audiobuffer-copy-channel.html | 330 ++++ .../audiobuffer-getChannelData.html | 66 + .../audiobuffer-reuse.html | 36 + .../audiobuffer.html | 71 + .../ctor-audiobuffer.html | 236 +++ 35 files changed, 6079 insertions(+) create mode 100644 webaudio/META.yml create mode 100644 webaudio/README.md create mode 100644 webaudio/historical.html create mode 100644 webaudio/idlharness.https.window.js create mode 100644 webaudio/js/buffer-loader.js create mode 100644 webaudio/js/helpers.js create mode 100644 webaudio/js/worklet-recorder.js create mode 100644 webaudio/resources/4ch-440.wav create mode 100644 webaudio/resources/audio-param.js create mode 100644 webaudio/resources/audiobuffersource-testing.js create mode 100644 webaudio/resources/audionodeoptions.js create mode 100644 webaudio/resources/audioparam-testing.js create mode 100644 webaudio/resources/audit-util.js create mode 100644 webaudio/resources/audit.js create mode 100644 webaudio/resources/biquad-filters.js create mode 100644 webaudio/resources/biquad-testing.js create mode 100644 webaudio/resources/convolution-testing.js create mode 100644 webaudio/resources/delay-testing.js create mode 100644 webaudio/resources/distance-model-testing.js create mode 100644 webaudio/resources/merger-testing.js create mode 100644 webaudio/resources/mix-testing.js create mode 100644 webaudio/resources/mixing-rules.js create mode 100644 webaudio/resources/note-grain-on-testing.js create mode 100644 webaudio/resources/panner-formulas.js create mode 100644 webaudio/resources/panner-model-testing.js create mode 100644 webaudio/resources/sin_440Hz_-6dBFS_1s.wav create mode 100644 webaudio/resources/start-stop-exceptions.js create mode 100644 webaudio/resources/stereopanner-testing.js create mode 100644 webaudio/the-audio-api/the-audiobuffer-interface/.gitkeep create mode 100644 webaudio/the-audio-api/the-audiobuffer-interface/acquire-the-content.html create mode 100644 webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer-copy-channel.html create mode 100644 webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer-getChannelData.html create mode 100644 webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer-reuse.html create mode 100644 webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer.html create mode 100644 webaudio/the-audio-api/the-audiobuffer-interface/ctor-audiobuffer.html diff --git a/webaudio/META.yml b/webaudio/META.yml new file mode 100644 index 00000000..3bcd1cb8 --- /dev/null +++ b/webaudio/META.yml @@ -0,0 +1,4 @@ +spec: https://webaudio.github.io/web-audio-api/ +suggested_reviewers: + - hoch + - padenot diff --git a/webaudio/README.md b/webaudio/README.md new file mode 100644 index 00000000..bcfe291f --- /dev/null +++ b/webaudio/README.md @@ -0,0 +1,5 @@ +Our test suite is currently tracking the [editor's draft](https://webaudio.github.io/web-audio-api/) of the Web Audio API. + +The tests are arranged in subdirectories, corresponding to different +sections of the spec. So, for example, tests for the `DelayNode` are +in `the-audio-api/the-delaynode-interface`. diff --git a/webaudio/historical.html b/webaudio/historical.html new file mode 100644 index 00000000..1f3146c3 --- /dev/null +++ b/webaudio/historical.html @@ -0,0 +1,29 @@ + +Historical Web Audio API features + + + diff --git a/webaudio/idlharness.https.window.js b/webaudio/idlharness.https.window.js new file mode 100644 index 00000000..e941a75c --- /dev/null +++ b/webaudio/idlharness.https.window.js @@ -0,0 +1,72 @@ +// META: script=/resources/WebIDLParser.js +// META: script=/resources/idlharness.js +// META: timeout=long + +// https://webaudio.github.io/web-audio-api/ + +'use strict'; + +idl_test( + ['webaudio'], + ['cssom', 'uievents', 'mediacapture-streams', 'html', 'dom'], + async idl_array => { + idl_array.add_untested_idls('interface SVGElement {};'); + + idl_array.add_objects({ + BaseAudioContext: [], + AudioContext: ['context'], + OfflineAudioContext: ['new OfflineAudioContext(1, 1, sample_rate)'], + OfflineAudioCompletionEvent: [ + 'new OfflineAudioCompletionEvent("", {renderedBuffer: buffer})' + ], + AudioBuffer: ['buffer'], + AudioNode: [], + AudioParam: ['new AudioBufferSourceNode(context).playbackRate'], + AudioScheduledSourceNode: [], + AnalyserNode: ['new AnalyserNode(context)'], + AudioBufferSourceNode: ['new AudioBufferSourceNode(context)'], + AudioDestinationNode: ['context.destination'], + AudioListener: ['context.listener'], + AudioProcessingEvent: [`new AudioProcessingEvent('', { + playbackTime: 0, inputBuffer: buffer, outputBuffer: buffer + })`], + BiquadFilterNode: ['new BiquadFilterNode(context)'], + ChannelMergerNode: ['new ChannelMergerNode(context)'], + ChannelSplitterNode: ['new ChannelSplitterNode(context)'], + ConstantSourceNode: ['new ConstantSourceNode(context)'], + ConvolverNode: ['new ConvolverNode(context)'], + DelayNode: ['new DelayNode(context)'], + DynamicsCompressorNode: ['new DynamicsCompressorNode(context)'], + GainNode: ['new GainNode(context)'], + IIRFilterNode: [ + 'new IIRFilterNode(context, {feedforward: [1], feedback: [1]})' + ], + MediaElementAudioSourceNode: [ + 'new MediaElementAudioSourceNode(context, {mediaElement: new Audio})' + ], + MediaStreamAudioDestinationNode: [ + 'new MediaStreamAudioDestinationNode(context)' + ], + MediaStreamAudioSourceNode: [], + MediaStreamTrackAudioSourceNode: [], + OscillatorNode: ['new OscillatorNode(context)'], + PannerNode: ['new PannerNode(context)'], + PeriodicWave: ['new PeriodicWave(context)'], + ScriptProcessorNode: ['context.createScriptProcessor()'], + StereoPannerNode: ['new StereoPannerNode(context)'], + WaveShaperNode: ['new WaveShaperNode(context)'], + AudioWorklet: ['context.audioWorklet'], + AudioWorkletGlobalScope: [], + AudioParamMap: ['worklet_node.parameters'], + AudioWorkletNode: ['worklet_node'], + AudioWorkletProcessor: [], + }); + + self.sample_rate = 44100; + self.context = new AudioContext; + self.buffer = new AudioBuffer({length: 1, sampleRate: sample_rate}); + await context.audioWorklet.addModule( + 'the-audio-api/the-audioworklet-interface/processors/dummy-processor.js'); + self.worklet_node = new AudioWorkletNode(context, 'dummy'); + } +); diff --git a/webaudio/js/buffer-loader.js b/webaudio/js/buffer-loader.js new file mode 100644 index 00000000..453dc4a5 --- /dev/null +++ b/webaudio/js/buffer-loader.js @@ -0,0 +1,44 @@ +/* Taken from + https://raw.github.com/WebKit/webkit/master/LayoutTests/webaudio/resources/buffer-loader.js */ + +function BufferLoader(context, urlList, callback) { + this.context = context; + this.urlList = urlList; + this.onload = callback; + this.bufferList = new Array(); + this.loadCount = 0; +} + +BufferLoader.prototype.loadBuffer = function(url, index) { + // Load buffer asynchronously + var request = new XMLHttpRequest(); + request.open("GET", url, true); + request.responseType = "arraybuffer"; + + var loader = this; + + request.onload = function() { + loader.context.decodeAudioData(request.response, decodeSuccessCallback, decodeErrorCallback); + }; + + request.onerror = function() { + alert('BufferLoader: XHR error'); + }; + + var decodeSuccessCallback = function(buffer) { + loader.bufferList[index] = buffer; + if (++loader.loadCount == loader.urlList.length) + loader.onload(loader.bufferList); + }; + + var decodeErrorCallback = function() { + alert('decodeErrorCallback: decode error'); + }; + + request.send(); +} + +BufferLoader.prototype.load = function() { + for (var i = 0; i < this.urlList.length; ++i) + this.loadBuffer(this.urlList[i], i); +} diff --git a/webaudio/js/helpers.js b/webaudio/js/helpers.js new file mode 100644 index 00000000..413c7205 --- /dev/null +++ b/webaudio/js/helpers.js @@ -0,0 +1,250 @@ +/* + Returns an array (typed or not), of the passed array with removed trailing and ending + zero-valued elements + */ +function trimEmptyElements(array) { + var start = 0; + var end = array.length; + + while (start < array.length) { + if (array[start] !== 0) { + break; + } + start++; + } + + while (end > 0) { + end--; + if (array[end] !== 0) { + break; + } + } + return array.subarray(start, end); +} + + +function fuzzyCompare(a, b) { + return Math.abs(a - b) < 9e-3; +} + +function compareChannels(buf1, buf2, + /*optional*/ length, + /*optional*/ sourceOffset, + /*optional*/ destOffset, + /*optional*/ skipLengthCheck) { + if (!skipLengthCheck) { + assert_equals(buf1.length, buf2.length, "Channels must have the same length"); + } + sourceOffset = sourceOffset || 0; + destOffset = destOffset || 0; + if (length == undefined) { + length = buf1.length - sourceOffset; + } + var difference = 0; + var maxDifference = 0; + var firstBadIndex = -1; + for (var i = 0; i < length; ++i) { + if (!fuzzyCompare(buf1[i + sourceOffset], buf2[i + destOffset])) { + difference++; + maxDifference = Math.max(maxDifference, Math.abs(buf1[i + sourceOffset] - buf2[i + destOffset])); + if (firstBadIndex == -1) { + firstBadIndex = i; + } + } + }; + + assert_equals(difference, 0, "maxDifference: " + maxDifference + + ", first bad index: " + firstBadIndex + " with test-data offset " + + sourceOffset + " and expected-data offset " + destOffset + + "; corresponding values " + buf1[firstBadIndex + sourceOffset] + " and " + + buf2[firstBadIndex + destOffset] + " --- differences"); +} + +function compareBuffers(got, expected) { + if (got.numberOfChannels != expected.numberOfChannels) { + assert_equals(got.numberOfChannels, expected.numberOfChannels, + "Correct number of buffer channels"); + return; + } + if (got.length != expected.length) { + assert_equals(got.length, expected.length, + "Correct buffer length"); + return; + } + if (got.sampleRate != expected.sampleRate) { + assert_equals(got.sampleRate, expected.sampleRate, + "Correct sample rate"); + return; + } + + for (var i = 0; i < got.numberOfChannels; ++i) { + compareChannels(got.getChannelData(i), expected.getChannelData(i), + got.length, 0, 0, true); + } +} + +/** + * This function assumes that the test is a "single page test" [0], and defines a + * single gTest variable with the following properties and methods: + * + * + numberOfChannels: optional property which specifies the number of channels + * in the output. The default value is 2. + * + createGraph: mandatory method which takes a context object and does + * everything needed in order to set up the Web Audio graph. + * This function returns the node to be inspected. + * + createGraphAsync: async version of createGraph. This function takes + * a callback which should be called with an argument + * set to the node to be inspected when the callee is + * ready to proceed with the test. Either this function + * or createGraph must be provided. + * + createExpectedBuffers: optional method which takes a context object and + * returns either one expected buffer or an array of + * them, designating what is expected to be observed + * in the output. If omitted, the output is expected + * to be silence. All buffers must have the same + * length, which must be a bufferSize supported by + * ScriptProcessorNode. This function is guaranteed + * to be called before createGraph. + * + length: property equal to the total number of frames which we are waiting + * to see in the output, mandatory if createExpectedBuffers is not + * provided, in which case it must be a bufferSize supported by + * ScriptProcessorNode (256, 512, 1024, 2048, 4096, 8192, or 16384). + * If createExpectedBuffers is provided then this must be equal to + * the number of expected buffers * the expected buffer length. + * + * + skipOfflineContextTests: optional. when true, skips running tests on an offline + * context by circumventing testOnOfflineContext. + * + * [0]: https://web-platform-tests.org/writing-tests/testharness-api.html#single-page-tests + */ +function runTest(name) +{ + function runTestFunction () { + if (!gTest.numberOfChannels) { + gTest.numberOfChannels = 2; // default + } + + var testLength; + + function runTestOnContext(context, callback, testOutput) { + if (!gTest.createExpectedBuffers) { + // Assume that the output is silence + var expectedBuffers = getEmptyBuffer(context, gTest.length); + } else { + var expectedBuffers = gTest.createExpectedBuffers(context); + } + if (!(expectedBuffers instanceof Array)) { + expectedBuffers = [expectedBuffers]; + } + var expectedFrames = 0; + for (var i = 0; i < expectedBuffers.length; ++i) { + assert_equals(expectedBuffers[i].numberOfChannels, gTest.numberOfChannels, + "Correct number of channels for expected buffer " + i); + expectedFrames += expectedBuffers[i].length; + } + if (gTest.length && gTest.createExpectedBuffers) { + assert_equals(expectedFrames, + gTest.length, "Correct number of expected frames"); + } + + if (gTest.createGraphAsync) { + gTest.createGraphAsync(context, function(nodeToInspect) { + testOutput(nodeToInspect, expectedBuffers, callback); + }); + } else { + testOutput(gTest.createGraph(context), expectedBuffers, callback); + } + } + + function testOnNormalContext(callback) { + function testOutput(nodeToInspect, expectedBuffers, callback) { + testLength = 0; + var sp = context.createScriptProcessor(expectedBuffers[0].length, gTest.numberOfChannels, 1); + nodeToInspect.connect(sp).connect(context.destination); + sp.onaudioprocess = function(e) { + var expectedBuffer = expectedBuffers.shift(); + testLength += expectedBuffer.length; + compareBuffers(e.inputBuffer, expectedBuffer); + if (expectedBuffers.length == 0) { + sp.onaudioprocess = null; + callback(); + } + }; + } + var context = new AudioContext(); + runTestOnContext(context, callback, testOutput); + } + + function testOnOfflineContext(callback, sampleRate) { + function testOutput(nodeToInspect, expectedBuffers, callback) { + nodeToInspect.connect(context.destination); + context.oncomplete = function(e) { + var samplesSeen = 0; + while (expectedBuffers.length) { + var expectedBuffer = expectedBuffers.shift(); + assert_equals(e.renderedBuffer.numberOfChannels, expectedBuffer.numberOfChannels, + "Correct number of input buffer channels"); + for (var i = 0; i < e.renderedBuffer.numberOfChannels; ++i) { + compareChannels(e.renderedBuffer.getChannelData(i), + expectedBuffer.getChannelData(i), + expectedBuffer.length, + samplesSeen, + undefined, + true); + } + samplesSeen += expectedBuffer.length; + } + callback(); + }; + context.startRendering(); + } + + var context = new OfflineAudioContext(gTest.numberOfChannels, testLength, sampleRate); + runTestOnContext(context, callback, testOutput); + } + + testOnNormalContext(function() { + if (!gTest.skipOfflineContextTests) { + testOnOfflineContext(function() { + testOnOfflineContext(done, 44100); + }, 48000); + } else { + done(); + } + }); + }; + + runTestFunction(); +} + +// Simpler than audit.js, but still logs the message. Requires +// `setup("explicit_done": true)` if testing code that runs after the "load" +// event. +function equals(a, b, msg) { + test(function() { + assert_equals(a, b); + }, msg); +} +function is_true(a, msg) { + test(function() { + assert_true(a); + }, msg); +} + +// This allows writing AudioWorkletProcessor code in the same file as the rest +// of the test, for quick one off AudioWorkletProcessor testing. +function URLFromScriptsElements(ids) +{ + var scriptTexts = []; + for (let id of ids) { + + const e = document.querySelector("script#"+id) + if (!e) { + throw id+" is not the id of a + + diff --git a/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer-copy-channel.html b/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer-copy-channel.html new file mode 100644 index 00000000..c0cd49d3 --- /dev/null +++ b/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer-copy-channel.html @@ -0,0 +1,330 @@ + + + + + Test Basic Functionality of AudioBuffer.copyFromChannel and + AudioBuffer.copyToChannel + + + + + + + + + + diff --git a/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer-getChannelData.html b/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer-getChannelData.html new file mode 100644 index 00000000..612a91cf --- /dev/null +++ b/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer-getChannelData.html @@ -0,0 +1,66 @@ + + + + + Test AudioBuffer.getChannelData() Returns the Same Object + + + + + + + + + + + diff --git a/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer-reuse.html b/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer-reuse.html new file mode 100644 index 00000000..dabe323c --- /dev/null +++ b/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer-reuse.html @@ -0,0 +1,36 @@ + + +AudioBuffer can be reused between AudioBufferSourceNodes + + + diff --git a/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer.html b/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer.html new file mode 100644 index 00000000..a2c4581c --- /dev/null +++ b/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer.html @@ -0,0 +1,71 @@ + + + + + audiobuffer.html + + + + + + + + + + diff --git a/webaudio/the-audio-api/the-audiobuffer-interface/ctor-audiobuffer.html b/webaudio/the-audio-api/the-audiobuffer-interface/ctor-audiobuffer.html new file mode 100644 index 00000000..fbe6e42e --- /dev/null +++ b/webaudio/the-audio-api/the-audiobuffer-interface/ctor-audiobuffer.html @@ -0,0 +1,236 @@ + + + + + Test Constructor: AudioBuffer + + + + + + + + + + + From b98fd01d22533f3e4b2fe85a6588e59a60181462 Mon Sep 17 00:00:00 2001 From: Otto Date: Sat, 25 Nov 2023 19:44:20 +0100 Subject: [PATCH 02/34] Run the WPT webaudio test harness for our bindings --- Cargo.toml | 7 ++++--- README.md | 4 ++++ import-for-wpt.js | 12 ++++++++++++ run-harness.sh | 1 + 4 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 import-for-wpt.js create mode 100755 run-harness.sh diff --git a/Cargo.toml b/Cargo.toml index 76285240..d1ccd73f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,8 +12,8 @@ crate-type = ["cdylib"] [dependencies] napi = {version="2.13", features=["napi6"]} napi-derive = "2.13" -web-audio-api = "0.36.1" -# web-audio-api = { path = "../web-audio-api-rs" } +# web-audio-api = "0.36.1" +web-audio-api = { path = "../web-audio-api-rs" } [target.'cfg(all(any(windows, unix), target_arch = "x86_64", not(target_env = "musl")))'.dependencies] mimalloc = {version = "0.1"} @@ -22,7 +22,8 @@ mimalloc = {version = "0.1"} napi-build = "1" [profile.release] -lto = true +#lto = true +debug = true [features] jack = ["web-audio-api/cpal-jack"] diff --git a/README.md b/README.md index ea1aadd4..24c384ee 100644 --- a/README.md +++ b/README.md @@ -129,6 +129,10 @@ The npm `postversion` script rely on [`cargo-bump`](https://crates.io/crates/car cargo install cargo-bump ``` +## Running the web-platform-test suite + +Follow the steps for 'Manual Build' first. Then run `./run-harness.sh`. + ## License [BSD-3-Clause](./LICENSE) diff --git a/import-for-wpt.js b/import-for-wpt.js new file mode 100644 index 00000000..6573a160 --- /dev/null +++ b/import-for-wpt.js @@ -0,0 +1,12 @@ +let webAudioItems; +import('./index.mjs').then((m) => (webAudioItems = m)); + +function setup(window) { + if (!webAudioItems) { + throw new ReferenceError("setup() called before loading webAudioItems"); + } + + Object.assign(window, webAudioItems); +} + +module.exports = exports = setup; diff --git a/run-harness.sh b/run-harness.sh new file mode 100755 index 00000000..d12b2a90 --- /dev/null +++ b/run-harness.sh @@ -0,0 +1 @@ +npx wpt-runner webaudio -u webaudio -s import-for-wpt.js From 66fdf55ecb0daf3b85ccd5e82e35f4d2c9aaa808 Mon Sep 17 00:00:00 2001 From: b-ma Date: Mon, 27 Nov 2023 08:57:55 +0100 Subject: [PATCH 03/34] refactor: move wpt as npm script --- README.md | 6 +++++- deploy/README.md | 3 +++ package.json | 6 ++++-- run-harness.sh | 1 - 4 files changed, 12 insertions(+), 4 deletions(-) delete mode 100755 run-harness.sh diff --git a/README.md b/README.md index 24c384ee..6006f5b6 100644 --- a/README.md +++ b/README.md @@ -131,7 +131,11 @@ cargo install cargo-bump ## Running the web-platform-test suite -Follow the steps for 'Manual Build' first. Then run `./run-harness.sh`. +Follow the steps for 'Manual Build' first. Then run: + +``` +npm run wpt +``` ## License diff --git a/deploy/README.md b/deploy/README.md index 26d8fd32..38016002 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -24,3 +24,6 @@ Profile (which tools and data to install)? (minimal/default/complete) [default] - install dev libs + `sudo apt-get install -y libjack-jackd2-dev` + `sudo apt-get install -y libasound2-dev` + +- [ ] try to remove +node-web-audio-api.linux-arm-gnueabihf.node: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, BuildID[sha1]=5e4e7e318cc2f92e8d15364d5f8350187b250ae1, with debug_info, not stripped diff --git a/package.json b/package.json index 4a73da15..d3325ca9 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,8 @@ "lint": "eslint monkey-patch.js index.cjs index.mjs && eslint examples/*.mjs", "preversion": "yarn install && npm run generate", "postversion": "cargo bump $npm_package_version && git commit -am \"v$npm_package_version\" && node bin/check-changelog.mjs", - "test": "mocha" + "test": "mocha", + "wpt": "wpt-runner webaudio -u webaudio -s import-for-wpt.js" }, "devDependencies": { "@ircam/eslint-config": "^1.3.0", @@ -59,7 +60,8 @@ "ping": "^0.4.2", "template-literal": "^1.0.4", "waves-masters": "^2.3.1", - "webidl2": "^24.2.0" + "webidl2": "^24.2.0", + "wpt-runner": "^5.0.0" }, "dependencies": { "@napi-rs/cli": "^2.14.3", diff --git a/run-harness.sh b/run-harness.sh deleted file mode 100755 index d12b2a90..00000000 --- a/run-harness.sh +++ /dev/null @@ -1 +0,0 @@ -npx wpt-runner webaudio -u webaudio -s import-for-wpt.js From ced86f1b584d32c271c09e8d3cdf3798982d216e Mon Sep 17 00:00:00 2001 From: b-ma Date: Mon, 27 Nov 2023 09:23:02 +0100 Subject: [PATCH 04/34] chore: use programmatic API to run wpt --- import-for-wpt.js | 12 --------- import-for-wpt.mjs | 66 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 4 +-- 3 files changed, 68 insertions(+), 14 deletions(-) delete mode 100644 import-for-wpt.js create mode 100644 import-for-wpt.mjs diff --git a/import-for-wpt.js b/import-for-wpt.js deleted file mode 100644 index 6573a160..00000000 --- a/import-for-wpt.js +++ /dev/null @@ -1,12 +0,0 @@ -let webAudioItems; -import('./index.mjs').then((m) => (webAudioItems = m)); - -function setup(window) { - if (!webAudioItems) { - throw new ReferenceError("setup() called before loading webAudioItems"); - } - - Object.assign(window, webAudioItems); -} - -module.exports = exports = setup; diff --git a/import-for-wpt.mjs b/import-for-wpt.mjs new file mode 100644 index 00000000..a38cf8ce --- /dev/null +++ b/import-for-wpt.mjs @@ -0,0 +1,66 @@ +import wptRunner from 'wpt-runner'; +import * as webAudioItems from './index.mjs'; +import chalk from 'chalk'; + + +// ------------------------------------------------------- +// Some helpers +// ------------------------------------------------------- +const INDENT_SIZE = 2; + +function indent(string, times) { + const prefix = " ".repeat(times); + return string.split("\n").map(l => prefix + l).join("\n"); +} + +// ------------------------------------------------------- +// WPT Runner configuration options +// ------------------------------------------------------- +const testsPath = 'webaudio'; +const rootURL = 'webaudio'; + +// monkey patch `window` with our web audio API +const setup = window => { + Object.assign(window, webAudioItems); +} + +// run all tests for now, @todo - filter from npm command line +const filter = () => true; + +// reporter, adapted from default console reporter +// https://github.com/domenic/wpt-runner/blob/master/lib/console-reporter.js +let numPass = 0; +let numFail = 0; + +const reporter = { + startSuite: name => { + console.log(`\n ${chalk.bold.underline(name)}\n`); + }, + pass: message => { + numPass += 1; + console.log(chalk.dim(indent(chalk.green("√ ") + message, INDENT_SIZE))); + }, + fail: message => { + numFail += 1; + console.log(chalk.bold.red(indent(`\u00D7 ${message}`, INDENT_SIZE))); + }, + reportStack: stack => { + // console.log(colors.dim(indent(stack, INDENT_SIZE * 2))) + }, +}; + +// ------------------------------------------------------- +// Run test suite +// ------------------------------------------------------- +try { + const failures = await wptRunner(testsPath, { rootURL, setup, filter, reporter }); + + console.log(`\n ${chalk.bold.underline('RESULTS:')}`); + console.log(chalk.bold(` - # pass: ${numPass}`)); + console.log(chalk.bold(` - # fail: ${numFail}`)); + + process.exit(failures); +} catch (e) { + console.error(e.stack); + process.exit(1); +} diff --git a/package.json b/package.json index d3325ca9..cc12093d 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "preversion": "yarn install && npm run generate", "postversion": "cargo bump $npm_package_version && git commit -am \"v$npm_package_version\" && node bin/check-changelog.mjs", "test": "mocha", - "wpt": "wpt-runner webaudio -u webaudio -s import-for-wpt.js" + "wpt": "node ./import-for-wpt.mjs" }, "devDependencies": { "@ircam/eslint-config": "^1.3.0", @@ -50,7 +50,7 @@ "@sindresorhus/slugify": "^2.1.1", "camelcase": "^7.0.1", "chai": "^4.3.7", - "chalk": "^5.2.0", + "chalk": "^5.3.0", "cli-table": "^0.3.11", "dotenv": "^16.0.3", "eslint": "^8.32.0", From 7f9cd2e26de082c03c6569b51f259d60d81306cc Mon Sep 17 00:00:00 2001 From: b-ma Date: Mon, 27 Nov 2023 09:24:06 +0100 Subject: [PATCH 05/34] chore: clean wpt files --- import-for-wpt.mjs => bin/wpt-harness.mjs | 3 ++- package.json | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) rename import-for-wpt.mjs => bin/wpt-harness.mjs (97%) diff --git a/import-for-wpt.mjs b/bin/wpt-harness.mjs similarity index 97% rename from import-for-wpt.mjs rename to bin/wpt-harness.mjs index a38cf8ce..e9779cec 100644 --- a/import-for-wpt.mjs +++ b/bin/wpt-harness.mjs @@ -1,7 +1,8 @@ import wptRunner from 'wpt-runner'; -import * as webAudioItems from './index.mjs'; import chalk from 'chalk'; +import * as webAudioItems from '../index.mjs'; + // ------------------------------------------------------- // Some helpers diff --git a/package.json b/package.json index cc12093d..dc7430e9 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "preversion": "yarn install && npm run generate", "postversion": "cargo bump $npm_package_version && git commit -am \"v$npm_package_version\" && node bin/check-changelog.mjs", "test": "mocha", - "wpt": "node ./import-for-wpt.mjs" + "wpt": "node ./bin/wpt-harness.mjs" }, "devDependencies": { "@ircam/eslint-config": "^1.3.0", From b27bc9cd1c86fe44b4f0ebb776b653fd949710bc Mon Sep 17 00:00:00 2001 From: b-ma Date: Mon, 27 Nov 2023 09:51:52 +0100 Subject: [PATCH 06/34] chore: build in debug mode when running wpt --- Cargo.toml | 4 ++-- bin/wpt-harness.mjs | 2 +- package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d1ccd73f..33698286 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,8 +22,8 @@ mimalloc = {version = "0.1"} napi-build = "1" [profile.release] -#lto = true -debug = true +lto = "fat" # https://nnethercote.github.io/perf-book/build-configuration.html#link-time-optimization +# debug = true [features] jack = ["web-audio-api/cpal-jack"] diff --git a/bin/wpt-harness.mjs b/bin/wpt-harness.mjs index e9779cec..68a9c354 100644 --- a/bin/wpt-harness.mjs +++ b/bin/wpt-harness.mjs @@ -46,7 +46,7 @@ const reporter = { console.log(chalk.bold.red(indent(`\u00D7 ${message}`, INDENT_SIZE))); }, reportStack: stack => { - // console.log(colors.dim(indent(stack, INDENT_SIZE * 2))) + // console.log(chalk.dim(indent(stack, INDENT_SIZE * 2))) }, }; diff --git a/package.json b/package.json index dc7430e9..a8125592 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "preversion": "yarn install && npm run generate", "postversion": "cargo bump $npm_package_version && git commit -am \"v$npm_package_version\" && node bin/check-changelog.mjs", "test": "mocha", - "wpt": "node ./bin/wpt-harness.mjs" + "wpt": "npm run generate && napi build --platform && node ./bin/wpt-harness.mjs" }, "devDependencies": { "@ircam/eslint-config": "^1.3.0", From 0eb0b7e325648a71cacae29815cec91c1397a909 Mon Sep 17 00:00:00 2001 From: b-ma Date: Tue, 28 Nov 2023 15:40:09 +0100 Subject: [PATCH 07/34] test: add analyser node wpt test + disciminate errors of error types from other errors --- bin/wpt-harness.mjs | 11 +- deploy/README.md | 3 - package.json | 3 +- .../the-analysernode-interface/.gitkeep | 0 .../ctor-analyser.html | 183 ++++++++++++++ .../realtimeanalyser-basic.html | 57 +++++ .../realtimeanalyser-fft-scaling.html | 111 ++++++++ .../realtimeanalyser-fft-sizing.html | 54 ++++ .../test-analyser-gain.html | 50 ++++ .../test-analyser-minimum.html | 43 ++++ .../test-analyser-output.html | 44 ++++ .../test-analyser-scale.html | 51 ++++ .../test-analysernode.html | 237 ++++++++++++++++++ 13 files changed, 841 insertions(+), 6 deletions(-) create mode 100644 webaudio/the-audio-api/the-analysernode-interface/.gitkeep create mode 100644 webaudio/the-audio-api/the-analysernode-interface/ctor-analyser.html create mode 100644 webaudio/the-audio-api/the-analysernode-interface/realtimeanalyser-basic.html create mode 100644 webaudio/the-audio-api/the-analysernode-interface/realtimeanalyser-fft-scaling.html create mode 100644 webaudio/the-audio-api/the-analysernode-interface/realtimeanalyser-fft-sizing.html create mode 100644 webaudio/the-audio-api/the-analysernode-interface/test-analyser-gain.html create mode 100644 webaudio/the-audio-api/the-analysernode-interface/test-analyser-minimum.html create mode 100644 webaudio/the-audio-api/the-analysernode-interface/test-analyser-output.html create mode 100644 webaudio/the-audio-api/the-analysernode-interface/test-analyser-scale.html create mode 100644 webaudio/the-audio-api/the-analysernode-interface/test-analysernode.html diff --git a/bin/wpt-harness.mjs b/bin/wpt-harness.mjs index 68a9c354..12065a05 100644 --- a/bin/wpt-harness.mjs +++ b/bin/wpt-harness.mjs @@ -32,6 +32,7 @@ const filter = () => true; // https://github.com/domenic/wpt-runner/blob/master/lib/console-reporter.js let numPass = 0; let numFail = 0; +let typeErrorFail = 0; const reporter = { startSuite: name => { @@ -42,8 +43,13 @@ const reporter = { console.log(chalk.dim(indent(chalk.green("√ ") + message, INDENT_SIZE))); }, fail: message => { - numFail += 1; - console.log(chalk.bold.red(indent(`\u00D7 ${message}`, INDENT_SIZE))); + if (/threw "Error" instead of/.test(message)) { + typeErrorFail += 1; + console.log(chalk.bold.yellow(indent(`| ${message}`, INDENT_SIZE))); + } else { + numFail += 1; + console.log(chalk.bold.red(indent(`\u00D7 ${message}`, INDENT_SIZE))); + } }, reportStack: stack => { // console.log(chalk.dim(indent(stack, INDENT_SIZE * 2))) @@ -59,6 +65,7 @@ try { console.log(`\n ${chalk.bold.underline('RESULTS:')}`); console.log(chalk.bold(` - # pass: ${numPass}`)); console.log(chalk.bold(` - # fail: ${numFail}`)); + console.log(chalk.bold(` - # type error issues: ${typeErrorFail}`)); process.exit(failures); } catch (e) { diff --git a/deploy/README.md b/deploy/README.md index 38016002..26d8fd32 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -24,6 +24,3 @@ Profile (which tools and data to install)? (minimal/default/complete) [default] - install dev libs + `sudo apt-get install -y libjack-jackd2-dev` + `sudo apt-get install -y libasound2-dev` - -- [ ] try to remove -node-web-audio-api.linux-arm-gnueabihf.node: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, BuildID[sha1]=5e4e7e318cc2f92e8d15364d5f8350187b250ae1, with debug_info, not stripped diff --git a/package.json b/package.json index a8125592..aead6693 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,8 @@ "preversion": "yarn install && npm run generate", "postversion": "cargo bump $npm_package_version && git commit -am \"v$npm_package_version\" && node bin/check-changelog.mjs", "test": "mocha", - "wpt": "npm run generate && napi build --platform && node ./bin/wpt-harness.mjs" + "wpt": "npm run generate && napi build --platform && node ./bin/wpt-harness.mjs", + "wpt:only": "node ./bin/wpt-harness.mjs" }, "devDependencies": { "@ircam/eslint-config": "^1.3.0", diff --git a/webaudio/the-audio-api/the-analysernode-interface/.gitkeep b/webaudio/the-audio-api/the-analysernode-interface/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/webaudio/the-audio-api/the-analysernode-interface/ctor-analyser.html b/webaudio/the-audio-api/the-analysernode-interface/ctor-analyser.html new file mode 100644 index 00000000..a9aa4831 --- /dev/null +++ b/webaudio/the-audio-api/the-analysernode-interface/ctor-analyser.html @@ -0,0 +1,183 @@ + + + + + Test Constructor: AnalyserNode + + + + + + + + + + + diff --git a/webaudio/the-audio-api/the-analysernode-interface/realtimeanalyser-basic.html b/webaudio/the-audio-api/the-analysernode-interface/realtimeanalyser-basic.html new file mode 100644 index 00000000..e176d611 --- /dev/null +++ b/webaudio/the-audio-api/the-analysernode-interface/realtimeanalyser-basic.html @@ -0,0 +1,57 @@ + + + + + realtimeanalyser-basic.html + + + + + + + + + + diff --git a/webaudio/the-audio-api/the-analysernode-interface/realtimeanalyser-fft-scaling.html b/webaudio/the-audio-api/the-analysernode-interface/realtimeanalyser-fft-scaling.html new file mode 100644 index 00000000..043bd589 --- /dev/null +++ b/webaudio/the-audio-api/the-analysernode-interface/realtimeanalyser-fft-scaling.html @@ -0,0 +1,111 @@ + + + + + realtimeanalyser-fft-scaling.html + + + + + + + +
+
+ + + diff --git a/webaudio/the-audio-api/the-analysernode-interface/realtimeanalyser-fft-sizing.html b/webaudio/the-audio-api/the-analysernode-interface/realtimeanalyser-fft-sizing.html new file mode 100644 index 00000000..7ee6a223 --- /dev/null +++ b/webaudio/the-audio-api/the-analysernode-interface/realtimeanalyser-fft-sizing.html @@ -0,0 +1,54 @@ + + + + + realtimeanalyser-fft-sizing.html + + + + + + + + + + diff --git a/webaudio/the-audio-api/the-analysernode-interface/test-analyser-gain.html b/webaudio/the-audio-api/the-analysernode-interface/test-analyser-gain.html new file mode 100644 index 00000000..dff51a74 --- /dev/null +++ b/webaudio/the-audio-api/the-analysernode-interface/test-analyser-gain.html @@ -0,0 +1,50 @@ + + + + + + + + + + diff --git a/webaudio/the-audio-api/the-analysernode-interface/test-analyser-minimum.html b/webaudio/the-audio-api/the-analysernode-interface/test-analyser-minimum.html new file mode 100644 index 00000000..ab0fe6b2 --- /dev/null +++ b/webaudio/the-audio-api/the-analysernode-interface/test-analyser-minimum.html @@ -0,0 +1,43 @@ + + + + + Test AnalyserNode when the input is silent + + + + + + + diff --git a/webaudio/the-audio-api/the-analysernode-interface/test-analyser-output.html b/webaudio/the-audio-api/the-analysernode-interface/test-analyser-output.html new file mode 100644 index 00000000..43d56b89 --- /dev/null +++ b/webaudio/the-audio-api/the-analysernode-interface/test-analyser-output.html @@ -0,0 +1,44 @@ + + + + + AnalyserNode output + + + + + + + + diff --git a/webaudio/the-audio-api/the-analysernode-interface/test-analyser-scale.html b/webaudio/the-audio-api/the-analysernode-interface/test-analyser-scale.html new file mode 100644 index 00000000..904b14be --- /dev/null +++ b/webaudio/the-audio-api/the-analysernode-interface/test-analyser-scale.html @@ -0,0 +1,51 @@ + + + + + Test AnalyserNode when the input is scaled + + + + + + diff --git a/webaudio/the-audio-api/the-analysernode-interface/test-analysernode.html b/webaudio/the-audio-api/the-analysernode-interface/test-analysernode.html new file mode 100644 index 00000000..e8325388 --- /dev/null +++ b/webaudio/the-audio-api/the-analysernode-interface/test-analysernode.html @@ -0,0 +1,237 @@ + + + + + + + + + + From 8ff8d0306cf5914518363a7fa6203d2993fee430 Mon Sep 17 00:00:00 2001 From: b-ma Date: Tue, 28 Nov 2023 16:43:20 +0100 Subject: [PATCH 08/34] test: add some options for wpt tests --- README.md | 7 ++++++- bin/wpt-harness.mjs | 34 +++++++++++++++++++++++++++++----- package.json | 1 + 3 files changed, 36 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 6006f5b6..a96ab185 100644 --- a/README.md +++ b/README.md @@ -134,9 +134,14 @@ cargo install cargo-bump Follow the steps for 'Manual Build' first. Then run: ``` -npm run wpt +npm run wpt # build in debug mode and run all wpt test +npm run wpt:only # run all wpt test without build +npm run wpt -- --list # list all wpt test files +npm run wpt -- --filter # apply filter on executed/listed wpt tests ``` +Avai + ## License [BSD-3-Clause](./LICENSE) diff --git a/bin/wpt-harness.mjs b/bin/wpt-harness.mjs index 12065a05..3be5e3c2 100644 --- a/bin/wpt-harness.mjs +++ b/bin/wpt-harness.mjs @@ -1,8 +1,17 @@ +import path from 'path'; import wptRunner from 'wpt-runner'; import chalk from 'chalk'; +import { program } from 'commander'; -import * as webAudioItems from '../index.mjs'; +import * as nodeWebAudioAPI from '../index.mjs'; +program + .option('--list', 'List the name of the test files') + .option('--filter ', 'Filter executed OR listed test files', '.*'); + +program.parse(process.argv); + +const options = program.opts(); // ------------------------------------------------------- // Some helpers @@ -22,11 +31,26 @@ const rootURL = 'webaudio'; // monkey patch `window` with our web audio API const setup = window => { - Object.assign(window, webAudioItems); + Object.assign(window, nodeWebAudioAPI); + + // seems required (weirdly...), cf. `the-audiobuffer-interface/audiobuffer.html` + window.Float32Array = Float32Array; } -// run all tests for now, @todo - filter from npm command line -const filter = () => true; +const filterRe = new RegExp(`${options.filter}`); + +const filter = (name) => { + if (filterRe.test(name)) { + if (options.list) { + console.log(name); + return false; + } else { + return true; + } + } else { + return false; + } +}; // reporter, adapted from default console reporter // https://github.com/domenic/wpt-runner/blob/master/lib/console-reporter.js @@ -36,7 +60,7 @@ let typeErrorFail = 0; const reporter = { startSuite: name => { - console.log(`\n ${chalk.bold.underline(name)}\n`); + console.log(`\n ${chalk.bold.underline(path.join(testsPath, name))}\n`); }, pass: message => { numPass += 1; diff --git a/package.json b/package.json index aead6693..45965d32 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ "chai": "^4.3.7", "chalk": "^5.3.0", "cli-table": "^0.3.11", + "commander": "^11.1.0", "dotenv": "^16.0.3", "eslint": "^8.32.0", "mocha": "^10.2.0", From acfbb2e260aa32bc0127991a00f45c2151050a99 Mon Sep 17 00:00:00 2001 From: Otto Date: Tue, 28 Nov 2023 17:58:44 +0100 Subject: [PATCH 09/34] Avoid some wpt crashes by providing stub implementations --- generator/templates/offline_audio_context.tmpl.rs | 5 ++++- monkey-patch.js | 8 ++++++++ src/offline_audio_context.rs | 5 ++++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/generator/templates/offline_audio_context.tmpl.rs b/generator/templates/offline_audio_context.tmpl.rs index 07ccebca..0c82ba70 100644 --- a/generator/templates/offline_audio_context.tmpl.rs +++ b/generator/templates/offline_audio_context.tmpl.rs @@ -237,7 +237,10 @@ fn ${d.slug(factoryName)}(ctx: CallContext) -> Result { fn get_length(ctx: CallContext) -> Result { let js_this = ctx.this_unchecked::(); let napi_obj = ctx.env.unwrap::(&js_this)?; - let obj = napi_obj.unwrap(); + let obj = match napi_obj.0.as_ref() { + Some(v) => v, + None => return ctx.env.create_double(0.), + }; let length = obj.length() as f64; ctx.env.create_double(length) diff --git a/monkey-patch.js b/monkey-patch.js index 90672e72..c2f37d29 100644 --- a/monkey-patch.js +++ b/monkey-patch.js @@ -142,6 +142,14 @@ function patchOfflineAudioContext(nativeBinding) { } } + suspend() { + return Promise.resolve(null); + } + + resume() { + return Promise.resolve(null); + } + decodeAudioData(audioData) { if (!audioData instanceof ArrayBuffer) { throw new Error('Invalid argument, please provide an ArrayBuffer'); diff --git a/src/offline_audio_context.rs b/src/offline_audio_context.rs index b935e504..291e528b 100644 --- a/src/offline_audio_context.rs +++ b/src/offline_audio_context.rs @@ -413,7 +413,10 @@ fn create_wave_shaper(ctx: CallContext) -> Result { fn get_length(ctx: CallContext) -> Result { let js_this = ctx.this_unchecked::(); let napi_obj = ctx.env.unwrap::(&js_this)?; - let obj = napi_obj.unwrap(); + let obj = match napi_obj.0.as_ref() { + Some(v) => v, + None => return ctx.env.create_double(0.), + }; let length = obj.length() as f64; ctx.env.create_double(length) From 99bfa66412c0feb57debfa7b8e866a023f632ffa Mon Sep 17 00:00:00 2001 From: Otto Date: Wed, 29 Nov 2023 08:27:41 +0100 Subject: [PATCH 10/34] Replace wpt webaudio suite with git submodule --- .gitmodules | 3 + README.md | 9 +- bin/wpt-harness.mjs | 2 +- webaudio/META.yml | 4 - webaudio/README.md | 5 - webaudio/historical.html | 29 - webaudio/idlharness.https.window.js | 72 - webaudio/js/buffer-loader.js | 44 - webaudio/js/helpers.js | 250 --- webaudio/js/worklet-recorder.js | 55 - webaudio/resources/4ch-440.wav | Bin 353022 -> 0 bytes webaudio/resources/audio-param.js | 44 - .../resources/audiobuffersource-testing.js | 102 -- webaudio/resources/audionodeoptions.js | 292 ---- webaudio/resources/audioparam-testing.js | 554 ------- webaudio/resources/audit-util.js | 195 --- webaudio/resources/audit.js | 1445 ----------------- webaudio/resources/biquad-filters.js | 376 ----- webaudio/resources/biquad-testing.js | 172 -- webaudio/resources/convolution-testing.js | 168 -- webaudio/resources/delay-testing.js | 66 - webaudio/resources/distance-model-testing.js | 196 --- webaudio/resources/merger-testing.js | 24 - webaudio/resources/mix-testing.js | 23 - webaudio/resources/mixing-rules.js | 350 ---- webaudio/resources/note-grain-on-testing.js | 165 -- webaudio/resources/panner-formulas.js | 190 --- webaudio/resources/panner-model-testing.js | 184 --- webaudio/resources/sin_440Hz_-6dBFS_1s.wav | Bin 88246 -> 0 bytes webaudio/resources/start-stop-exceptions.js | 45 - webaudio/resources/stereopanner-testing.js | 205 --- .../the-analysernode-interface/.gitkeep | 0 .../ctor-analyser.html | 183 --- .../realtimeanalyser-basic.html | 57 - .../realtimeanalyser-fft-scaling.html | 111 -- .../realtimeanalyser-fft-sizing.html | 54 - .../test-analyser-gain.html | 50 - .../test-analyser-minimum.html | 43 - .../test-analyser-output.html | 44 - .../test-analyser-scale.html | 51 - .../test-analysernode.html | 237 --- .../the-audiobuffer-interface/.gitkeep | 0 .../acquire-the-content.html | 85 - .../audiobuffer-copy-channel.html | 330 ---- .../audiobuffer-getChannelData.html | 66 - .../audiobuffer-reuse.html | 36 - .../audiobuffer.html | 71 - .../ctor-audiobuffer.html | 236 --- wpt | 1 + 49 files changed, 13 insertions(+), 6911 deletions(-) create mode 100644 .gitmodules delete mode 100644 webaudio/META.yml delete mode 100644 webaudio/README.md delete mode 100644 webaudio/historical.html delete mode 100644 webaudio/idlharness.https.window.js delete mode 100644 webaudio/js/buffer-loader.js delete mode 100644 webaudio/js/helpers.js delete mode 100644 webaudio/js/worklet-recorder.js delete mode 100644 webaudio/resources/4ch-440.wav delete mode 100644 webaudio/resources/audio-param.js delete mode 100644 webaudio/resources/audiobuffersource-testing.js delete mode 100644 webaudio/resources/audionodeoptions.js delete mode 100644 webaudio/resources/audioparam-testing.js delete mode 100644 webaudio/resources/audit-util.js delete mode 100644 webaudio/resources/audit.js delete mode 100644 webaudio/resources/biquad-filters.js delete mode 100644 webaudio/resources/biquad-testing.js delete mode 100644 webaudio/resources/convolution-testing.js delete mode 100644 webaudio/resources/delay-testing.js delete mode 100644 webaudio/resources/distance-model-testing.js delete mode 100644 webaudio/resources/merger-testing.js delete mode 100644 webaudio/resources/mix-testing.js delete mode 100644 webaudio/resources/mixing-rules.js delete mode 100644 webaudio/resources/note-grain-on-testing.js delete mode 100644 webaudio/resources/panner-formulas.js delete mode 100644 webaudio/resources/panner-model-testing.js delete mode 100644 webaudio/resources/sin_440Hz_-6dBFS_1s.wav delete mode 100644 webaudio/resources/start-stop-exceptions.js delete mode 100644 webaudio/resources/stereopanner-testing.js delete mode 100644 webaudio/the-audio-api/the-analysernode-interface/.gitkeep delete mode 100644 webaudio/the-audio-api/the-analysernode-interface/ctor-analyser.html delete mode 100644 webaudio/the-audio-api/the-analysernode-interface/realtimeanalyser-basic.html delete mode 100644 webaudio/the-audio-api/the-analysernode-interface/realtimeanalyser-fft-scaling.html delete mode 100644 webaudio/the-audio-api/the-analysernode-interface/realtimeanalyser-fft-sizing.html delete mode 100644 webaudio/the-audio-api/the-analysernode-interface/test-analyser-gain.html delete mode 100644 webaudio/the-audio-api/the-analysernode-interface/test-analyser-minimum.html delete mode 100644 webaudio/the-audio-api/the-analysernode-interface/test-analyser-output.html delete mode 100644 webaudio/the-audio-api/the-analysernode-interface/test-analyser-scale.html delete mode 100644 webaudio/the-audio-api/the-analysernode-interface/test-analysernode.html delete mode 100644 webaudio/the-audio-api/the-audiobuffer-interface/.gitkeep delete mode 100644 webaudio/the-audio-api/the-audiobuffer-interface/acquire-the-content.html delete mode 100644 webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer-copy-channel.html delete mode 100644 webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer-getChannelData.html delete mode 100644 webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer-reuse.html delete mode 100644 webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer.html delete mode 100644 webaudio/the-audio-api/the-audiobuffer-interface/ctor-audiobuffer.html create mode 160000 wpt diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..e67814bd --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "wpt"] + path = wpt + url = git@github.com:web-platform-tests/wpt.git diff --git a/README.md b/README.md index a96ab185..7e9f327c 100644 --- a/README.md +++ b/README.md @@ -131,7 +131,14 @@ cargo install cargo-bump ## Running the web-platform-test suite -Follow the steps for 'Manual Build' first. Then run: +Follow the steps for 'Manual Build' first. Then checkout the web-platform-tests submodule with: + +``` +git submodule init +git submodule update +``` + +Then run: ``` npm run wpt # build in debug mode and run all wpt test diff --git a/bin/wpt-harness.mjs b/bin/wpt-harness.mjs index 3be5e3c2..14b02ade 100644 --- a/bin/wpt-harness.mjs +++ b/bin/wpt-harness.mjs @@ -26,7 +26,7 @@ function indent(string, times) { // ------------------------------------------------------- // WPT Runner configuration options // ------------------------------------------------------- -const testsPath = 'webaudio'; +const testsPath = 'wpt/webaudio'; const rootURL = 'webaudio'; // monkey patch `window` with our web audio API diff --git a/webaudio/META.yml b/webaudio/META.yml deleted file mode 100644 index 3bcd1cb8..00000000 --- a/webaudio/META.yml +++ /dev/null @@ -1,4 +0,0 @@ -spec: https://webaudio.github.io/web-audio-api/ -suggested_reviewers: - - hoch - - padenot diff --git a/webaudio/README.md b/webaudio/README.md deleted file mode 100644 index bcfe291f..00000000 --- a/webaudio/README.md +++ /dev/null @@ -1,5 +0,0 @@ -Our test suite is currently tracking the [editor's draft](https://webaudio.github.io/web-audio-api/) of the Web Audio API. - -The tests are arranged in subdirectories, corresponding to different -sections of the spec. So, for example, tests for the `DelayNode` are -in `the-audio-api/the-delaynode-interface`. diff --git a/webaudio/historical.html b/webaudio/historical.html deleted file mode 100644 index 1f3146c3..00000000 --- a/webaudio/historical.html +++ /dev/null @@ -1,29 +0,0 @@ - -Historical Web Audio API features - - - diff --git a/webaudio/idlharness.https.window.js b/webaudio/idlharness.https.window.js deleted file mode 100644 index e941a75c..00000000 --- a/webaudio/idlharness.https.window.js +++ /dev/null @@ -1,72 +0,0 @@ -// META: script=/resources/WebIDLParser.js -// META: script=/resources/idlharness.js -// META: timeout=long - -// https://webaudio.github.io/web-audio-api/ - -'use strict'; - -idl_test( - ['webaudio'], - ['cssom', 'uievents', 'mediacapture-streams', 'html', 'dom'], - async idl_array => { - idl_array.add_untested_idls('interface SVGElement {};'); - - idl_array.add_objects({ - BaseAudioContext: [], - AudioContext: ['context'], - OfflineAudioContext: ['new OfflineAudioContext(1, 1, sample_rate)'], - OfflineAudioCompletionEvent: [ - 'new OfflineAudioCompletionEvent("", {renderedBuffer: buffer})' - ], - AudioBuffer: ['buffer'], - AudioNode: [], - AudioParam: ['new AudioBufferSourceNode(context).playbackRate'], - AudioScheduledSourceNode: [], - AnalyserNode: ['new AnalyserNode(context)'], - AudioBufferSourceNode: ['new AudioBufferSourceNode(context)'], - AudioDestinationNode: ['context.destination'], - AudioListener: ['context.listener'], - AudioProcessingEvent: [`new AudioProcessingEvent('', { - playbackTime: 0, inputBuffer: buffer, outputBuffer: buffer - })`], - BiquadFilterNode: ['new BiquadFilterNode(context)'], - ChannelMergerNode: ['new ChannelMergerNode(context)'], - ChannelSplitterNode: ['new ChannelSplitterNode(context)'], - ConstantSourceNode: ['new ConstantSourceNode(context)'], - ConvolverNode: ['new ConvolverNode(context)'], - DelayNode: ['new DelayNode(context)'], - DynamicsCompressorNode: ['new DynamicsCompressorNode(context)'], - GainNode: ['new GainNode(context)'], - IIRFilterNode: [ - 'new IIRFilterNode(context, {feedforward: [1], feedback: [1]})' - ], - MediaElementAudioSourceNode: [ - 'new MediaElementAudioSourceNode(context, {mediaElement: new Audio})' - ], - MediaStreamAudioDestinationNode: [ - 'new MediaStreamAudioDestinationNode(context)' - ], - MediaStreamAudioSourceNode: [], - MediaStreamTrackAudioSourceNode: [], - OscillatorNode: ['new OscillatorNode(context)'], - PannerNode: ['new PannerNode(context)'], - PeriodicWave: ['new PeriodicWave(context)'], - ScriptProcessorNode: ['context.createScriptProcessor()'], - StereoPannerNode: ['new StereoPannerNode(context)'], - WaveShaperNode: ['new WaveShaperNode(context)'], - AudioWorklet: ['context.audioWorklet'], - AudioWorkletGlobalScope: [], - AudioParamMap: ['worklet_node.parameters'], - AudioWorkletNode: ['worklet_node'], - AudioWorkletProcessor: [], - }); - - self.sample_rate = 44100; - self.context = new AudioContext; - self.buffer = new AudioBuffer({length: 1, sampleRate: sample_rate}); - await context.audioWorklet.addModule( - 'the-audio-api/the-audioworklet-interface/processors/dummy-processor.js'); - self.worklet_node = new AudioWorkletNode(context, 'dummy'); - } -); diff --git a/webaudio/js/buffer-loader.js b/webaudio/js/buffer-loader.js deleted file mode 100644 index 453dc4a5..00000000 --- a/webaudio/js/buffer-loader.js +++ /dev/null @@ -1,44 +0,0 @@ -/* Taken from - https://raw.github.com/WebKit/webkit/master/LayoutTests/webaudio/resources/buffer-loader.js */ - -function BufferLoader(context, urlList, callback) { - this.context = context; - this.urlList = urlList; - this.onload = callback; - this.bufferList = new Array(); - this.loadCount = 0; -} - -BufferLoader.prototype.loadBuffer = function(url, index) { - // Load buffer asynchronously - var request = new XMLHttpRequest(); - request.open("GET", url, true); - request.responseType = "arraybuffer"; - - var loader = this; - - request.onload = function() { - loader.context.decodeAudioData(request.response, decodeSuccessCallback, decodeErrorCallback); - }; - - request.onerror = function() { - alert('BufferLoader: XHR error'); - }; - - var decodeSuccessCallback = function(buffer) { - loader.bufferList[index] = buffer; - if (++loader.loadCount == loader.urlList.length) - loader.onload(loader.bufferList); - }; - - var decodeErrorCallback = function() { - alert('decodeErrorCallback: decode error'); - }; - - request.send(); -} - -BufferLoader.prototype.load = function() { - for (var i = 0; i < this.urlList.length; ++i) - this.loadBuffer(this.urlList[i], i); -} diff --git a/webaudio/js/helpers.js b/webaudio/js/helpers.js deleted file mode 100644 index 413c7205..00000000 --- a/webaudio/js/helpers.js +++ /dev/null @@ -1,250 +0,0 @@ -/* - Returns an array (typed or not), of the passed array with removed trailing and ending - zero-valued elements - */ -function trimEmptyElements(array) { - var start = 0; - var end = array.length; - - while (start < array.length) { - if (array[start] !== 0) { - break; - } - start++; - } - - while (end > 0) { - end--; - if (array[end] !== 0) { - break; - } - } - return array.subarray(start, end); -} - - -function fuzzyCompare(a, b) { - return Math.abs(a - b) < 9e-3; -} - -function compareChannels(buf1, buf2, - /*optional*/ length, - /*optional*/ sourceOffset, - /*optional*/ destOffset, - /*optional*/ skipLengthCheck) { - if (!skipLengthCheck) { - assert_equals(buf1.length, buf2.length, "Channels must have the same length"); - } - sourceOffset = sourceOffset || 0; - destOffset = destOffset || 0; - if (length == undefined) { - length = buf1.length - sourceOffset; - } - var difference = 0; - var maxDifference = 0; - var firstBadIndex = -1; - for (var i = 0; i < length; ++i) { - if (!fuzzyCompare(buf1[i + sourceOffset], buf2[i + destOffset])) { - difference++; - maxDifference = Math.max(maxDifference, Math.abs(buf1[i + sourceOffset] - buf2[i + destOffset])); - if (firstBadIndex == -1) { - firstBadIndex = i; - } - } - }; - - assert_equals(difference, 0, "maxDifference: " + maxDifference + - ", first bad index: " + firstBadIndex + " with test-data offset " + - sourceOffset + " and expected-data offset " + destOffset + - "; corresponding values " + buf1[firstBadIndex + sourceOffset] + " and " + - buf2[firstBadIndex + destOffset] + " --- differences"); -} - -function compareBuffers(got, expected) { - if (got.numberOfChannels != expected.numberOfChannels) { - assert_equals(got.numberOfChannels, expected.numberOfChannels, - "Correct number of buffer channels"); - return; - } - if (got.length != expected.length) { - assert_equals(got.length, expected.length, - "Correct buffer length"); - return; - } - if (got.sampleRate != expected.sampleRate) { - assert_equals(got.sampleRate, expected.sampleRate, - "Correct sample rate"); - return; - } - - for (var i = 0; i < got.numberOfChannels; ++i) { - compareChannels(got.getChannelData(i), expected.getChannelData(i), - got.length, 0, 0, true); - } -} - -/** - * This function assumes that the test is a "single page test" [0], and defines a - * single gTest variable with the following properties and methods: - * - * + numberOfChannels: optional property which specifies the number of channels - * in the output. The default value is 2. - * + createGraph: mandatory method which takes a context object and does - * everything needed in order to set up the Web Audio graph. - * This function returns the node to be inspected. - * + createGraphAsync: async version of createGraph. This function takes - * a callback which should be called with an argument - * set to the node to be inspected when the callee is - * ready to proceed with the test. Either this function - * or createGraph must be provided. - * + createExpectedBuffers: optional method which takes a context object and - * returns either one expected buffer or an array of - * them, designating what is expected to be observed - * in the output. If omitted, the output is expected - * to be silence. All buffers must have the same - * length, which must be a bufferSize supported by - * ScriptProcessorNode. This function is guaranteed - * to be called before createGraph. - * + length: property equal to the total number of frames which we are waiting - * to see in the output, mandatory if createExpectedBuffers is not - * provided, in which case it must be a bufferSize supported by - * ScriptProcessorNode (256, 512, 1024, 2048, 4096, 8192, or 16384). - * If createExpectedBuffers is provided then this must be equal to - * the number of expected buffers * the expected buffer length. - * - * + skipOfflineContextTests: optional. when true, skips running tests on an offline - * context by circumventing testOnOfflineContext. - * - * [0]: https://web-platform-tests.org/writing-tests/testharness-api.html#single-page-tests - */ -function runTest(name) -{ - function runTestFunction () { - if (!gTest.numberOfChannels) { - gTest.numberOfChannels = 2; // default - } - - var testLength; - - function runTestOnContext(context, callback, testOutput) { - if (!gTest.createExpectedBuffers) { - // Assume that the output is silence - var expectedBuffers = getEmptyBuffer(context, gTest.length); - } else { - var expectedBuffers = gTest.createExpectedBuffers(context); - } - if (!(expectedBuffers instanceof Array)) { - expectedBuffers = [expectedBuffers]; - } - var expectedFrames = 0; - for (var i = 0; i < expectedBuffers.length; ++i) { - assert_equals(expectedBuffers[i].numberOfChannels, gTest.numberOfChannels, - "Correct number of channels for expected buffer " + i); - expectedFrames += expectedBuffers[i].length; - } - if (gTest.length && gTest.createExpectedBuffers) { - assert_equals(expectedFrames, - gTest.length, "Correct number of expected frames"); - } - - if (gTest.createGraphAsync) { - gTest.createGraphAsync(context, function(nodeToInspect) { - testOutput(nodeToInspect, expectedBuffers, callback); - }); - } else { - testOutput(gTest.createGraph(context), expectedBuffers, callback); - } - } - - function testOnNormalContext(callback) { - function testOutput(nodeToInspect, expectedBuffers, callback) { - testLength = 0; - var sp = context.createScriptProcessor(expectedBuffers[0].length, gTest.numberOfChannels, 1); - nodeToInspect.connect(sp).connect(context.destination); - sp.onaudioprocess = function(e) { - var expectedBuffer = expectedBuffers.shift(); - testLength += expectedBuffer.length; - compareBuffers(e.inputBuffer, expectedBuffer); - if (expectedBuffers.length == 0) { - sp.onaudioprocess = null; - callback(); - } - }; - } - var context = new AudioContext(); - runTestOnContext(context, callback, testOutput); - } - - function testOnOfflineContext(callback, sampleRate) { - function testOutput(nodeToInspect, expectedBuffers, callback) { - nodeToInspect.connect(context.destination); - context.oncomplete = function(e) { - var samplesSeen = 0; - while (expectedBuffers.length) { - var expectedBuffer = expectedBuffers.shift(); - assert_equals(e.renderedBuffer.numberOfChannels, expectedBuffer.numberOfChannels, - "Correct number of input buffer channels"); - for (var i = 0; i < e.renderedBuffer.numberOfChannels; ++i) { - compareChannels(e.renderedBuffer.getChannelData(i), - expectedBuffer.getChannelData(i), - expectedBuffer.length, - samplesSeen, - undefined, - true); - } - samplesSeen += expectedBuffer.length; - } - callback(); - }; - context.startRendering(); - } - - var context = new OfflineAudioContext(gTest.numberOfChannels, testLength, sampleRate); - runTestOnContext(context, callback, testOutput); - } - - testOnNormalContext(function() { - if (!gTest.skipOfflineContextTests) { - testOnOfflineContext(function() { - testOnOfflineContext(done, 44100); - }, 48000); - } else { - done(); - } - }); - }; - - runTestFunction(); -} - -// Simpler than audit.js, but still logs the message. Requires -// `setup("explicit_done": true)` if testing code that runs after the "load" -// event. -function equals(a, b, msg) { - test(function() { - assert_equals(a, b); - }, msg); -} -function is_true(a, msg) { - test(function() { - assert_true(a); - }, msg); -} - -// This allows writing AudioWorkletProcessor code in the same file as the rest -// of the test, for quick one off AudioWorkletProcessor testing. -function URLFromScriptsElements(ids) -{ - var scriptTexts = []; - for (let id of ids) { - - const e = document.querySelector("script#"+id) - if (!e) { - throw id+" is not the id of a - - - - - - - - - diff --git a/webaudio/the-audio-api/the-analysernode-interface/realtimeanalyser-basic.html b/webaudio/the-audio-api/the-analysernode-interface/realtimeanalyser-basic.html deleted file mode 100644 index e176d611..00000000 --- a/webaudio/the-audio-api/the-analysernode-interface/realtimeanalyser-basic.html +++ /dev/null @@ -1,57 +0,0 @@ - - - - - realtimeanalyser-basic.html - - - - - - - - - - diff --git a/webaudio/the-audio-api/the-analysernode-interface/realtimeanalyser-fft-scaling.html b/webaudio/the-audio-api/the-analysernode-interface/realtimeanalyser-fft-scaling.html deleted file mode 100644 index 043bd589..00000000 --- a/webaudio/the-audio-api/the-analysernode-interface/realtimeanalyser-fft-scaling.html +++ /dev/null @@ -1,111 +0,0 @@ - - - - - realtimeanalyser-fft-scaling.html - - - - - - - -
-
- - - diff --git a/webaudio/the-audio-api/the-analysernode-interface/realtimeanalyser-fft-sizing.html b/webaudio/the-audio-api/the-analysernode-interface/realtimeanalyser-fft-sizing.html deleted file mode 100644 index 7ee6a223..00000000 --- a/webaudio/the-audio-api/the-analysernode-interface/realtimeanalyser-fft-sizing.html +++ /dev/null @@ -1,54 +0,0 @@ - - - - - realtimeanalyser-fft-sizing.html - - - - - - - - - - diff --git a/webaudio/the-audio-api/the-analysernode-interface/test-analyser-gain.html b/webaudio/the-audio-api/the-analysernode-interface/test-analyser-gain.html deleted file mode 100644 index dff51a74..00000000 --- a/webaudio/the-audio-api/the-analysernode-interface/test-analyser-gain.html +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - - diff --git a/webaudio/the-audio-api/the-analysernode-interface/test-analyser-minimum.html b/webaudio/the-audio-api/the-analysernode-interface/test-analyser-minimum.html deleted file mode 100644 index ab0fe6b2..00000000 --- a/webaudio/the-audio-api/the-analysernode-interface/test-analyser-minimum.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - Test AnalyserNode when the input is silent - - - - - - - diff --git a/webaudio/the-audio-api/the-analysernode-interface/test-analyser-output.html b/webaudio/the-audio-api/the-analysernode-interface/test-analyser-output.html deleted file mode 100644 index 43d56b89..00000000 --- a/webaudio/the-audio-api/the-analysernode-interface/test-analyser-output.html +++ /dev/null @@ -1,44 +0,0 @@ - - - - - AnalyserNode output - - - - - - - - diff --git a/webaudio/the-audio-api/the-analysernode-interface/test-analyser-scale.html b/webaudio/the-audio-api/the-analysernode-interface/test-analyser-scale.html deleted file mode 100644 index 904b14be..00000000 --- a/webaudio/the-audio-api/the-analysernode-interface/test-analyser-scale.html +++ /dev/null @@ -1,51 +0,0 @@ - - - - - Test AnalyserNode when the input is scaled - - - - - - diff --git a/webaudio/the-audio-api/the-analysernode-interface/test-analysernode.html b/webaudio/the-audio-api/the-analysernode-interface/test-analysernode.html deleted file mode 100644 index e8325388..00000000 --- a/webaudio/the-audio-api/the-analysernode-interface/test-analysernode.html +++ /dev/null @@ -1,237 +0,0 @@ - - - - - - - - - - diff --git a/webaudio/the-audio-api/the-audiobuffer-interface/.gitkeep b/webaudio/the-audio-api/the-audiobuffer-interface/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/webaudio/the-audio-api/the-audiobuffer-interface/acquire-the-content.html b/webaudio/the-audio-api/the-audiobuffer-interface/acquire-the-content.html deleted file mode 100644 index 70f5d8e3..00000000 --- a/webaudio/the-audio-api/the-audiobuffer-interface/acquire-the-content.html +++ /dev/null @@ -1,85 +0,0 @@ - - -Test for AudioBuffer's "acquire the content" operation - - - diff --git a/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer-copy-channel.html b/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer-copy-channel.html deleted file mode 100644 index c0cd49d3..00000000 --- a/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer-copy-channel.html +++ /dev/null @@ -1,330 +0,0 @@ - - - - - Test Basic Functionality of AudioBuffer.copyFromChannel and - AudioBuffer.copyToChannel - - - - - - - - - - diff --git a/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer-getChannelData.html b/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer-getChannelData.html deleted file mode 100644 index 612a91cf..00000000 --- a/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer-getChannelData.html +++ /dev/null @@ -1,66 +0,0 @@ - - - - - Test AudioBuffer.getChannelData() Returns the Same Object - - - - - - - - - - - diff --git a/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer-reuse.html b/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer-reuse.html deleted file mode 100644 index dabe323c..00000000 --- a/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer-reuse.html +++ /dev/null @@ -1,36 +0,0 @@ - - -AudioBuffer can be reused between AudioBufferSourceNodes - - - diff --git a/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer.html b/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer.html deleted file mode 100644 index a2c4581c..00000000 --- a/webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer.html +++ /dev/null @@ -1,71 +0,0 @@ - - - - - audiobuffer.html - - - - - - - - - - diff --git a/webaudio/the-audio-api/the-audiobuffer-interface/ctor-audiobuffer.html b/webaudio/the-audio-api/the-audiobuffer-interface/ctor-audiobuffer.html deleted file mode 100644 index fbe6e42e..00000000 --- a/webaudio/the-audio-api/the-audiobuffer-interface/ctor-audiobuffer.html +++ /dev/null @@ -1,236 +0,0 @@ - - - - - Test Constructor: AudioBuffer - - - - - - - - - - - diff --git a/wpt b/wpt new file mode 160000 index 00000000..57c9cc80 --- /dev/null +++ b/wpt @@ -0,0 +1 @@ +Subproject commit 57c9cc80fbfac825da2f4e2bdedea55828f7ce76 From 81e52ed7452c414261b9fc474d2bfaec88544bc8 Mon Sep 17 00:00:00 2001 From: Otto Date: Wed, 29 Nov 2023 08:59:24 +0100 Subject: [PATCH 11/34] Skip crashtests and resources in web platform tests --- bin/wpt-harness.mjs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/bin/wpt-harness.mjs b/bin/wpt-harness.mjs index 14b02ade..bc958f5c 100644 --- a/bin/wpt-harness.mjs +++ b/bin/wpt-harness.mjs @@ -7,6 +7,7 @@ import * as nodeWebAudioAPI from '../index.mjs'; program .option('--list', 'List the name of the test files') + .option('--with_crashtests', 'Also run crashtests') .option('--filter ', 'Filter executed OR listed test files', '.*'); program.parse(process.argv); @@ -40,6 +41,12 @@ const setup = window => { const filterRe = new RegExp(`${options.filter}`); const filter = (name) => { + if (!options.with_crashtests && name.includes('/crashtests/')) { + return false; + } + if (name.includes('/resources/')) { + return false; + } if (filterRe.test(name)) { if (options.list) { console.log(name); From ce309377cd36cf913184e1e61eb2d37d3bff0e4d Mon Sep 17 00:00:00 2001 From: Otto Date: Thu, 30 Nov 2023 09:18:17 +0100 Subject: [PATCH 12/34] Add more stubs to prevent the wpt from crashing --- generator/templates/offline_audio_context.tmpl.rs | 10 ++++++++-- src/offline_audio_context.rs | 10 ++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/generator/templates/offline_audio_context.tmpl.rs b/generator/templates/offline_audio_context.tmpl.rs index 0c82ba70..66787d9d 100644 --- a/generator/templates/offline_audio_context.tmpl.rs +++ b/generator/templates/offline_audio_context.tmpl.rs @@ -80,7 +80,10 @@ fn constructor(ctx: CallContext) -> Result { fn get_current_time(ctx: CallContext) -> Result { let js_this = ctx.this_unchecked::(); let napi_obj = ctx.env.unwrap::(&js_this)?; - let obj = napi_obj.unwrap(); + let obj = match napi_obj.0.as_ref() { + Some(v) => v, + None => return ctx.env.create_double(0.), + }; let current_time = obj.current_time() as f64; ctx.env.create_double(current_time) @@ -90,7 +93,10 @@ fn get_current_time(ctx: CallContext) -> Result { fn get_sample_rate(ctx: CallContext) -> Result { let js_this = ctx.this_unchecked::(); let napi_obj = ctx.env.unwrap::(&js_this)?; - let obj = napi_obj.unwrap(); + let obj = match napi_obj.0.as_ref() { + Some(v) => v, + None => return ctx.env.create_double(0.), + }; let sample_rate = obj.sample_rate() as f64; ctx.env.create_double(sample_rate) diff --git a/src/offline_audio_context.rs b/src/offline_audio_context.rs index 291e528b..589a8fd0 100644 --- a/src/offline_audio_context.rs +++ b/src/offline_audio_context.rs @@ -106,7 +106,10 @@ fn constructor(ctx: CallContext) -> Result { fn get_current_time(ctx: CallContext) -> Result { let js_this = ctx.this_unchecked::(); let napi_obj = ctx.env.unwrap::(&js_this)?; - let obj = napi_obj.unwrap(); + let obj = match napi_obj.0.as_ref() { + Some(v) => v, + None => return ctx.env.create_double(0.), + }; let current_time = obj.current_time() as f64; ctx.env.create_double(current_time) @@ -116,7 +119,10 @@ fn get_current_time(ctx: CallContext) -> Result { fn get_sample_rate(ctx: CallContext) -> Result { let js_this = ctx.this_unchecked::(); let napi_obj = ctx.env.unwrap::(&js_this)?; - let obj = napi_obj.unwrap(); + let obj = match napi_obj.0.as_ref() { + Some(v) => v, + None => return ctx.env.create_double(0.), + }; let sample_rate = obj.sample_rate() as f64; ctx.env.create_double(sample_rate) From edad8660cc3f9b71cdd115070239dd1f8eb01f11 Mon Sep 17 00:00:00 2001 From: Otto Date: Thu, 30 Nov 2023 21:02:15 +0100 Subject: [PATCH 13/34] Add bindings for AudioListener --- examples/iir-filter.mjs | 4 + examples/panner.mjs | 24 ++++ generator/templates/audio_context.tmpl.rs | 7 + generator/templates/lib.tmpl.rs | 3 + src/audio_context.rs | 7 + src/audio_listener.rs | 156 ++++++++++++++++++++++ src/lib.rs | 3 + 7 files changed, 204 insertions(+) create mode 100644 examples/panner.mjs create mode 100644 src/audio_listener.rs diff --git a/examples/iir-filter.mjs b/examples/iir-filter.mjs index 2bf9a160..64b95349 100644 --- a/examples/iir-filter.mjs +++ b/examples/iir-filter.mjs @@ -5,6 +5,10 @@ import { AudioContext } from '../index.mjs'; const latencyHint = process.env.WEB_AUDIO_LATENCY === 'playback' ? 'playback' : 'interactive'; const audioContext = new AudioContext({ latencyHint }); +console.log(audioContext.listener.position_x.value); +audioContext.listener.position_x.set_value(1.); +console.log(audioContext.listener.position_x.value); + const pathname = path.join(process.cwd(), 'samples', 'think-stereo-48000.wav'); const arrayBuffer = fs.readFileSync(pathname).buffer; const buffer = await audioContext.decodeAudioData(arrayBuffer); diff --git a/examples/panner.mjs b/examples/panner.mjs new file mode 100644 index 00000000..928e1e1d --- /dev/null +++ b/examples/panner.mjs @@ -0,0 +1,24 @@ +import { AudioContext } from '../index.mjs'; + +const latencyHint = process.env.WEB_AUDIO_LATENCY === 'playback' ? 'playback' : 'interactive'; +const audioContext = new AudioContext({ latencyHint }); + +audioContext.listener.positionZ.value = 1; +audioContext.listener.positionX.value = -10; +audioContext.listener.positionX.linearRampToValueAtTime(10, 4); + +const osc = audioContext.createOscillator(); +const panner = audioContext.createPanner(); +osc.connect(panner); +panner.connect(audioContext.destination); +osc.start(); + +let direction = 1; +setInterval(function loop() { + console.log(audioContext.listener.positionX.value); + if (Math.abs(audioContext.listener.positionX.value) >= 10.) { + direction *= -1; + const now = audioContext.currentTime; + audioContext.listener.positionX.linearRampToValueAtTime(10 * direction, now + 4); + } +}, 500); diff --git a/generator/templates/audio_context.tmpl.rs b/generator/templates/audio_context.tmpl.rs index 0583348d..1599c1fd 100644 --- a/generator/templates/audio_context.tmpl.rs +++ b/generator/templates/audio_context.tmpl.rs @@ -116,6 +116,7 @@ fn constructor(ctx: CallContext) -> Result { }; let audio_context = AudioContext::new(audio_context_options); + let native_listener = audio_context.listener(); let napi_audio_context = NapiAudioContext(audio_context); ctx.env.wrap(&mut js_this, napi_audio_context)?; @@ -134,6 +135,12 @@ fn constructor(ctx: CallContext) -> Result { let js_obj = ctor.new_instance(&[&js_this])?; js_this.set_named_property("destination", &js_obj)?; + // Audio Listener + let napi_listener = NapiAudioListener::new(native_listener); + let mut js_obj = NapiAudioListener::create_js_object(ctx.env)?; + ctx.env.wrap(&mut js_obj, napi_listener)?; + js_this.set_named_property("listener", &js_obj)?; + ctx.env.get_undefined() } diff --git a/generator/templates/lib.tmpl.rs b/generator/templates/lib.tmpl.rs index 477d9806..8fdce03c 100644 --- a/generator/templates/lib.tmpl.rs +++ b/generator/templates/lib.tmpl.rs @@ -7,6 +7,9 @@ use napi_derive::module_exports; #[macro_use] mod audio_node; +mod audio_listener; +use audio_listener::NapiAudioListener; + mod audio_param; use crate::audio_param::{NapiAudioParam}; // public diff --git a/src/audio_context.rs b/src/audio_context.rs index 314fb581..a010f358 100644 --- a/src/audio_context.rs +++ b/src/audio_context.rs @@ -138,6 +138,7 @@ fn constructor(ctx: CallContext) -> Result { }; let audio_context = AudioContext::new(audio_context_options); + let native_listener = audio_context.listener(); let napi_audio_context = NapiAudioContext(audio_context); ctx.env.wrap(&mut js_this, napi_audio_context)?; @@ -155,6 +156,12 @@ fn constructor(ctx: CallContext) -> Result { let js_obj = ctor.new_instance(&[&js_this])?; js_this.set_named_property("destination", &js_obj)?; + // Audio Listener + let napi_listener = NapiAudioListener::new(native_listener); + let mut js_obj = NapiAudioListener::create_js_object(ctx.env)?; + ctx.env.wrap(&mut js_obj, napi_listener)?; + js_this.set_named_property("listener", &js_obj)?; + ctx.env.get_undefined() } diff --git a/src/audio_listener.rs b/src/audio_listener.rs new file mode 100644 index 00000000..303e4f38 --- /dev/null +++ b/src/audio_listener.rs @@ -0,0 +1,156 @@ +// @TODO +// This should be generated as any other AudioNode + +use crate::*; +use napi::*; +use napi_derive::js_function; +use web_audio_api::AudioListener; + +pub struct NapiAudioListener(AudioListener); + +impl NapiAudioListener { + pub fn new(audio_listener: AudioListener) -> Self { + Self(audio_listener) + } + + pub fn create_js_object(env: &Env) -> Result { + let mut obj = env.create_object()?; + obj.define_properties(&[ + Property::new("Symbol.toStringTag")? + .with_value(&env.create_string("AudioListener")?) + .with_property_attributes(PropertyAttributes::Static), + Property::new("positionX")?.with_getter(get_position_x), + Property::new("positionY")?.with_getter(get_position_y), + Property::new("positionZ")?.with_getter(get_position_z), + Property::new("forwardX")?.with_getter(get_forward_x), + Property::new("forwardY")?.with_getter(get_forward_y), + Property::new("forwardZ")?.with_getter(get_forward_z), + Property::new("upX")?.with_getter(get_up_x), + Property::new("upY")?.with_getter(get_up_y), + Property::new("upZ")?.with_getter(get_up_z), + ])?; + + Ok(obj) + } + + pub fn unwrap(&self) -> &AudioListener { + &self.0 + } +} + +#[js_function] +fn get_position_x(ctx: CallContext) -> Result { + let js_this = ctx.this_unchecked::(); + let napi_node = ctx.env.unwrap::(&js_this)?; + let node = napi_node.unwrap(); + + let native_param = node.position_x().clone(); + let napi_param = NapiAudioParam::new(native_param); + let mut js_obj = NapiAudioParam::create_js_object(ctx.env)?; + ctx.env.wrap(&mut js_obj, napi_param)?; + Ok(js_obj) +} + +#[js_function] +fn get_position_y(ctx: CallContext) -> Result { + let js_this = ctx.this_unchecked::(); + let napi_node = ctx.env.unwrap::(&js_this)?; + let node = napi_node.unwrap(); + + let native_param = node.position_y().clone(); + let napi_param = NapiAudioParam::new(native_param); + let mut js_obj = NapiAudioParam::create_js_object(ctx.env)?; + ctx.env.wrap(&mut js_obj, napi_param)?; + Ok(js_obj) +} + +#[js_function] +fn get_position_z(ctx: CallContext) -> Result { + let js_this = ctx.this_unchecked::(); + let napi_node = ctx.env.unwrap::(&js_this)?; + let node = napi_node.unwrap(); + + let native_param = node.position_z().clone(); + let napi_param = NapiAudioParam::new(native_param); + let mut js_obj = NapiAudioParam::create_js_object(ctx.env)?; + ctx.env.wrap(&mut js_obj, napi_param)?; + Ok(js_obj) +} + +#[js_function] +fn get_forward_x(ctx: CallContext) -> Result { + let js_this = ctx.this_unchecked::(); + let napi_node = ctx.env.unwrap::(&js_this)?; + let node = napi_node.unwrap(); + + let native_param = node.forward_x().clone(); + let napi_param = NapiAudioParam::new(native_param); + let mut js_obj = NapiAudioParam::create_js_object(ctx.env)?; + ctx.env.wrap(&mut js_obj, napi_param)?; + Ok(js_obj) +} + +#[js_function] +fn get_forward_y(ctx: CallContext) -> Result { + let js_this = ctx.this_unchecked::(); + let napi_node = ctx.env.unwrap::(&js_this)?; + let node = napi_node.unwrap(); + + let native_param = node.forward_y().clone(); + let napi_param = NapiAudioParam::new(native_param); + let mut js_obj = NapiAudioParam::create_js_object(ctx.env)?; + ctx.env.wrap(&mut js_obj, napi_param)?; + Ok(js_obj) +} + +#[js_function] +fn get_forward_z(ctx: CallContext) -> Result { + let js_this = ctx.this_unchecked::(); + let napi_node = ctx.env.unwrap::(&js_this)?; + let node = napi_node.unwrap(); + + let native_param = node.forward_z().clone(); + let napi_param = NapiAudioParam::new(native_param); + let mut js_obj = NapiAudioParam::create_js_object(ctx.env)?; + ctx.env.wrap(&mut js_obj, napi_param)?; + Ok(js_obj) +} + +#[js_function] +fn get_up_x(ctx: CallContext) -> Result { + let js_this = ctx.this_unchecked::(); + let napi_node = ctx.env.unwrap::(&js_this)?; + let node = napi_node.unwrap(); + + let native_param = node.up_x().clone(); + let napi_param = NapiAudioParam::new(native_param); + let mut js_obj = NapiAudioParam::create_js_object(ctx.env)?; + ctx.env.wrap(&mut js_obj, napi_param)?; + Ok(js_obj) +} + +#[js_function] +fn get_up_y(ctx: CallContext) -> Result { + let js_this = ctx.this_unchecked::(); + let napi_node = ctx.env.unwrap::(&js_this)?; + let node = napi_node.unwrap(); + + let native_param = node.up_y().clone(); + let napi_param = NapiAudioParam::new(native_param); + let mut js_obj = NapiAudioParam::create_js_object(ctx.env)?; + ctx.env.wrap(&mut js_obj, napi_param)?; + Ok(js_obj) +} + +#[js_function] +fn get_up_z(ctx: CallContext) -> Result { + let js_this = ctx.this_unchecked::(); + let napi_node = ctx.env.unwrap::(&js_this)?; + let node = napi_node.unwrap(); + + let native_param = node.up_z().clone(); + let napi_param = NapiAudioParam::new(native_param); + let mut js_obj = NapiAudioParam::create_js_object(ctx.env)?; + ctx.env.wrap(&mut js_obj, napi_param)?; + Ok(js_obj) +} diff --git a/src/lib.rs b/src/lib.rs index c0a7e3ee..ea61b765 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,6 +26,9 @@ use napi_derive::module_exports; #[macro_use] mod audio_node; +mod audio_listener; +use audio_listener::NapiAudioListener; + mod audio_param; use crate::audio_param::NapiAudioParam; // public From 072baefb5222db88f59eddb1296a4465f6644b0f Mon Sep 17 00:00:00 2001 From: Otto Date: Thu, 30 Nov 2023 21:04:40 +0100 Subject: [PATCH 14/34] Revert unrelated changes to iir-filter example --- examples/iir-filter.mjs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/examples/iir-filter.mjs b/examples/iir-filter.mjs index 64b95349..2bf9a160 100644 --- a/examples/iir-filter.mjs +++ b/examples/iir-filter.mjs @@ -5,10 +5,6 @@ import { AudioContext } from '../index.mjs'; const latencyHint = process.env.WEB_AUDIO_LATENCY === 'playback' ? 'playback' : 'interactive'; const audioContext = new AudioContext({ latencyHint }); -console.log(audioContext.listener.position_x.value); -audioContext.listener.position_x.set_value(1.); -console.log(audioContext.listener.position_x.value); - const pathname = path.join(process.cwd(), 'samples', 'think-stereo-48000.wav'); const arrayBuffer = fs.readFileSync(pathname).buffer; const buffer = await audioContext.decodeAudioData(arrayBuffer); From 876c654ebb5262a85cfc511fa037c13675aa4b34 Mon Sep 17 00:00:00 2001 From: b-ma Date: Sat, 2 Dec 2023 09:54:00 +0100 Subject: [PATCH 15/34] fix: lazily instantiate listener --- generator/templates/audio_context.tmpl.rs | 33 ++++++++++++++++++----- src/audio_context.rs | 33 ++++++++++++++++++----- wpt | 2 +- 3 files changed, 53 insertions(+), 15 deletions(-) diff --git a/generator/templates/audio_context.tmpl.rs b/generator/templates/audio_context.tmpl.rs index 1599c1fd..78c48d10 100644 --- a/generator/templates/audio_context.tmpl.rs +++ b/generator/templates/audio_context.tmpl.rs @@ -16,6 +16,7 @@ impl NapiAudioContext { &[ Property::new("currentTime")?.with_getter(get_current_time), Property::new("sampleRate")?.with_getter(get_sample_rate), + Property::new("listener")?.with_getter(get_listener), Property::new("decodeAudioData")?.with_method(decode_audio_data), Property::new("createPeriodicWave")?.with_method(create_periodic_wave), @@ -116,7 +117,7 @@ fn constructor(ctx: CallContext) -> Result { }; let audio_context = AudioContext::new(audio_context_options); - let native_listener = audio_context.listener(); + let napi_audio_context = NapiAudioContext(audio_context); ctx.env.wrap(&mut js_this, napi_audio_context)?; @@ -135,12 +136,6 @@ fn constructor(ctx: CallContext) -> Result { let js_obj = ctor.new_instance(&[&js_this])?; js_this.set_named_property("destination", &js_obj)?; - // Audio Listener - let napi_listener = NapiAudioListener::new(native_listener); - let mut js_obj = NapiAudioListener::create_js_object(ctx.env)?; - ctx.env.wrap(&mut js_obj, napi_listener)?; - js_this.set_named_property("listener", &js_obj)?; - ctx.env.get_undefined() } @@ -164,6 +159,30 @@ fn get_sample_rate(ctx: CallContext) -> Result { ctx.env.create_double(sample_rate) } +#[js_function] +fn get_listener(ctx: CallContext) -> Result { + let mut js_this = ctx.this_unchecked::(); + + // reproduce lazy instanciation strategy from rust crate + let ok_obj = if js_this.has_named_property("__listener__").ok().unwrap() == true { + // println!("ok"); + js_this.get_named_property("__listener__") + } else { + // println!("> ONCE"); + let napi_obj = ctx.env.unwrap::(&js_this)?; + let obj = napi_obj.unwrap(); + let native_listener = obj.listener(); + // Audio Listener + let napi_listener = NapiAudioListener::new(native_listener); + let mut js_obj = NapiAudioListener::create_js_object(ctx.env)?; + ctx.env.wrap(&mut js_obj, napi_listener)?; + js_this.set_named_property("__listener__", &js_obj)?; + Ok(js_obj) + }; + + ok_obj +} + // ---------------------------------------------------- // METHODS // ---------------------------------------------------- diff --git a/src/audio_context.rs b/src/audio_context.rs index a010f358..a86f2249 100644 --- a/src/audio_context.rs +++ b/src/audio_context.rs @@ -35,6 +35,7 @@ impl NapiAudioContext { &[ Property::new("currentTime")?.with_getter(get_current_time), Property::new("sampleRate")?.with_getter(get_sample_rate), + Property::new("listener")?.with_getter(get_listener), Property::new("decodeAudioData")?.with_method(decode_audio_data), Property::new("createPeriodicWave")?.with_method(create_periodic_wave), Property::new("createBuffer")?.with_method(create_buffer), @@ -138,7 +139,7 @@ fn constructor(ctx: CallContext) -> Result { }; let audio_context = AudioContext::new(audio_context_options); - let native_listener = audio_context.listener(); + let napi_audio_context = NapiAudioContext(audio_context); ctx.env.wrap(&mut js_this, napi_audio_context)?; @@ -156,12 +157,6 @@ fn constructor(ctx: CallContext) -> Result { let js_obj = ctor.new_instance(&[&js_this])?; js_this.set_named_property("destination", &js_obj)?; - // Audio Listener - let napi_listener = NapiAudioListener::new(native_listener); - let mut js_obj = NapiAudioListener::create_js_object(ctx.env)?; - ctx.env.wrap(&mut js_obj, napi_listener)?; - js_this.set_named_property("listener", &js_obj)?; - ctx.env.get_undefined() } @@ -185,6 +180,30 @@ fn get_sample_rate(ctx: CallContext) -> Result { ctx.env.create_double(sample_rate) } +#[js_function] +fn get_listener(ctx: CallContext) -> Result { + let mut js_this = ctx.this_unchecked::(); + + // reproduce lazy instanciation strategy from rust crate + let ok_obj = if js_this.has_named_property("__listener__").ok().unwrap() == true { + println!("ok"); + js_this.get_named_property("__listener__") + } else { + println!("> ONCE"); + let napi_obj = ctx.env.unwrap::(&js_this)?; + let obj = napi_obj.unwrap(); + let native_listener = obj.listener(); + // Audio Listener + let napi_listener = NapiAudioListener::new(native_listener); + let mut js_obj = NapiAudioListener::create_js_object(ctx.env)?; + ctx.env.wrap(&mut js_obj, napi_listener)?; + js_this.set_named_property("__listener__", &js_obj)?; + Ok(js_obj) + }; + + ok_obj +} + // ---------------------------------------------------- // METHODS // ---------------------------------------------------- diff --git a/wpt b/wpt index 57c9cc80..83f318a1 160000 --- a/wpt +++ b/wpt @@ -1 +1 @@ -Subproject commit 57c9cc80fbfac825da2f4e2bdedea55828f7ce76 +Subproject commit 83f318a105a1a9c58a0c7bff65607d753d3ad9ad From 5d24303556fdf0b2df3a7b0cc3e5449ce0ba0643 Mon Sep 17 00:00:00 2001 From: Otto Date: Sat, 2 Dec 2023 20:03:07 +0100 Subject: [PATCH 16/34] Revert "Add bindings for AudioListener" --- examples/panner.mjs | 24 ---- generator/templates/audio_context.tmpl.rs | 26 ---- generator/templates/lib.tmpl.rs | 3 - src/audio_context.rs | 26 ---- src/audio_listener.rs | 156 ---------------------- src/lib.rs | 3 - wpt | 2 +- 7 files changed, 1 insertion(+), 239 deletions(-) delete mode 100644 examples/panner.mjs delete mode 100644 src/audio_listener.rs diff --git a/examples/panner.mjs b/examples/panner.mjs deleted file mode 100644 index 928e1e1d..00000000 --- a/examples/panner.mjs +++ /dev/null @@ -1,24 +0,0 @@ -import { AudioContext } from '../index.mjs'; - -const latencyHint = process.env.WEB_AUDIO_LATENCY === 'playback' ? 'playback' : 'interactive'; -const audioContext = new AudioContext({ latencyHint }); - -audioContext.listener.positionZ.value = 1; -audioContext.listener.positionX.value = -10; -audioContext.listener.positionX.linearRampToValueAtTime(10, 4); - -const osc = audioContext.createOscillator(); -const panner = audioContext.createPanner(); -osc.connect(panner); -panner.connect(audioContext.destination); -osc.start(); - -let direction = 1; -setInterval(function loop() { - console.log(audioContext.listener.positionX.value); - if (Math.abs(audioContext.listener.positionX.value) >= 10.) { - direction *= -1; - const now = audioContext.currentTime; - audioContext.listener.positionX.linearRampToValueAtTime(10 * direction, now + 4); - } -}, 500); diff --git a/generator/templates/audio_context.tmpl.rs b/generator/templates/audio_context.tmpl.rs index 78c48d10..0583348d 100644 --- a/generator/templates/audio_context.tmpl.rs +++ b/generator/templates/audio_context.tmpl.rs @@ -16,7 +16,6 @@ impl NapiAudioContext { &[ Property::new("currentTime")?.with_getter(get_current_time), Property::new("sampleRate")?.with_getter(get_sample_rate), - Property::new("listener")?.with_getter(get_listener), Property::new("decodeAudioData")?.with_method(decode_audio_data), Property::new("createPeriodicWave")?.with_method(create_periodic_wave), @@ -117,7 +116,6 @@ fn constructor(ctx: CallContext) -> Result { }; let audio_context = AudioContext::new(audio_context_options); - let napi_audio_context = NapiAudioContext(audio_context); ctx.env.wrap(&mut js_this, napi_audio_context)?; @@ -159,30 +157,6 @@ fn get_sample_rate(ctx: CallContext) -> Result { ctx.env.create_double(sample_rate) } -#[js_function] -fn get_listener(ctx: CallContext) -> Result { - let mut js_this = ctx.this_unchecked::(); - - // reproduce lazy instanciation strategy from rust crate - let ok_obj = if js_this.has_named_property("__listener__").ok().unwrap() == true { - // println!("ok"); - js_this.get_named_property("__listener__") - } else { - // println!("> ONCE"); - let napi_obj = ctx.env.unwrap::(&js_this)?; - let obj = napi_obj.unwrap(); - let native_listener = obj.listener(); - // Audio Listener - let napi_listener = NapiAudioListener::new(native_listener); - let mut js_obj = NapiAudioListener::create_js_object(ctx.env)?; - ctx.env.wrap(&mut js_obj, napi_listener)?; - js_this.set_named_property("__listener__", &js_obj)?; - Ok(js_obj) - }; - - ok_obj -} - // ---------------------------------------------------- // METHODS // ---------------------------------------------------- diff --git a/generator/templates/lib.tmpl.rs b/generator/templates/lib.tmpl.rs index 8fdce03c..477d9806 100644 --- a/generator/templates/lib.tmpl.rs +++ b/generator/templates/lib.tmpl.rs @@ -7,9 +7,6 @@ use napi_derive::module_exports; #[macro_use] mod audio_node; -mod audio_listener; -use audio_listener::NapiAudioListener; - mod audio_param; use crate::audio_param::{NapiAudioParam}; // public diff --git a/src/audio_context.rs b/src/audio_context.rs index a86f2249..314fb581 100644 --- a/src/audio_context.rs +++ b/src/audio_context.rs @@ -35,7 +35,6 @@ impl NapiAudioContext { &[ Property::new("currentTime")?.with_getter(get_current_time), Property::new("sampleRate")?.with_getter(get_sample_rate), - Property::new("listener")?.with_getter(get_listener), Property::new("decodeAudioData")?.with_method(decode_audio_data), Property::new("createPeriodicWave")?.with_method(create_periodic_wave), Property::new("createBuffer")?.with_method(create_buffer), @@ -139,7 +138,6 @@ fn constructor(ctx: CallContext) -> Result { }; let audio_context = AudioContext::new(audio_context_options); - let napi_audio_context = NapiAudioContext(audio_context); ctx.env.wrap(&mut js_this, napi_audio_context)?; @@ -180,30 +178,6 @@ fn get_sample_rate(ctx: CallContext) -> Result { ctx.env.create_double(sample_rate) } -#[js_function] -fn get_listener(ctx: CallContext) -> Result { - let mut js_this = ctx.this_unchecked::(); - - // reproduce lazy instanciation strategy from rust crate - let ok_obj = if js_this.has_named_property("__listener__").ok().unwrap() == true { - println!("ok"); - js_this.get_named_property("__listener__") - } else { - println!("> ONCE"); - let napi_obj = ctx.env.unwrap::(&js_this)?; - let obj = napi_obj.unwrap(); - let native_listener = obj.listener(); - // Audio Listener - let napi_listener = NapiAudioListener::new(native_listener); - let mut js_obj = NapiAudioListener::create_js_object(ctx.env)?; - ctx.env.wrap(&mut js_obj, napi_listener)?; - js_this.set_named_property("__listener__", &js_obj)?; - Ok(js_obj) - }; - - ok_obj -} - // ---------------------------------------------------- // METHODS // ---------------------------------------------------- diff --git a/src/audio_listener.rs b/src/audio_listener.rs deleted file mode 100644 index 303e4f38..00000000 --- a/src/audio_listener.rs +++ /dev/null @@ -1,156 +0,0 @@ -// @TODO -// This should be generated as any other AudioNode - -use crate::*; -use napi::*; -use napi_derive::js_function; -use web_audio_api::AudioListener; - -pub struct NapiAudioListener(AudioListener); - -impl NapiAudioListener { - pub fn new(audio_listener: AudioListener) -> Self { - Self(audio_listener) - } - - pub fn create_js_object(env: &Env) -> Result { - let mut obj = env.create_object()?; - obj.define_properties(&[ - Property::new("Symbol.toStringTag")? - .with_value(&env.create_string("AudioListener")?) - .with_property_attributes(PropertyAttributes::Static), - Property::new("positionX")?.with_getter(get_position_x), - Property::new("positionY")?.with_getter(get_position_y), - Property::new("positionZ")?.with_getter(get_position_z), - Property::new("forwardX")?.with_getter(get_forward_x), - Property::new("forwardY")?.with_getter(get_forward_y), - Property::new("forwardZ")?.with_getter(get_forward_z), - Property::new("upX")?.with_getter(get_up_x), - Property::new("upY")?.with_getter(get_up_y), - Property::new("upZ")?.with_getter(get_up_z), - ])?; - - Ok(obj) - } - - pub fn unwrap(&self) -> &AudioListener { - &self.0 - } -} - -#[js_function] -fn get_position_x(ctx: CallContext) -> Result { - let js_this = ctx.this_unchecked::(); - let napi_node = ctx.env.unwrap::(&js_this)?; - let node = napi_node.unwrap(); - - let native_param = node.position_x().clone(); - let napi_param = NapiAudioParam::new(native_param); - let mut js_obj = NapiAudioParam::create_js_object(ctx.env)?; - ctx.env.wrap(&mut js_obj, napi_param)?; - Ok(js_obj) -} - -#[js_function] -fn get_position_y(ctx: CallContext) -> Result { - let js_this = ctx.this_unchecked::(); - let napi_node = ctx.env.unwrap::(&js_this)?; - let node = napi_node.unwrap(); - - let native_param = node.position_y().clone(); - let napi_param = NapiAudioParam::new(native_param); - let mut js_obj = NapiAudioParam::create_js_object(ctx.env)?; - ctx.env.wrap(&mut js_obj, napi_param)?; - Ok(js_obj) -} - -#[js_function] -fn get_position_z(ctx: CallContext) -> Result { - let js_this = ctx.this_unchecked::(); - let napi_node = ctx.env.unwrap::(&js_this)?; - let node = napi_node.unwrap(); - - let native_param = node.position_z().clone(); - let napi_param = NapiAudioParam::new(native_param); - let mut js_obj = NapiAudioParam::create_js_object(ctx.env)?; - ctx.env.wrap(&mut js_obj, napi_param)?; - Ok(js_obj) -} - -#[js_function] -fn get_forward_x(ctx: CallContext) -> Result { - let js_this = ctx.this_unchecked::(); - let napi_node = ctx.env.unwrap::(&js_this)?; - let node = napi_node.unwrap(); - - let native_param = node.forward_x().clone(); - let napi_param = NapiAudioParam::new(native_param); - let mut js_obj = NapiAudioParam::create_js_object(ctx.env)?; - ctx.env.wrap(&mut js_obj, napi_param)?; - Ok(js_obj) -} - -#[js_function] -fn get_forward_y(ctx: CallContext) -> Result { - let js_this = ctx.this_unchecked::(); - let napi_node = ctx.env.unwrap::(&js_this)?; - let node = napi_node.unwrap(); - - let native_param = node.forward_y().clone(); - let napi_param = NapiAudioParam::new(native_param); - let mut js_obj = NapiAudioParam::create_js_object(ctx.env)?; - ctx.env.wrap(&mut js_obj, napi_param)?; - Ok(js_obj) -} - -#[js_function] -fn get_forward_z(ctx: CallContext) -> Result { - let js_this = ctx.this_unchecked::(); - let napi_node = ctx.env.unwrap::(&js_this)?; - let node = napi_node.unwrap(); - - let native_param = node.forward_z().clone(); - let napi_param = NapiAudioParam::new(native_param); - let mut js_obj = NapiAudioParam::create_js_object(ctx.env)?; - ctx.env.wrap(&mut js_obj, napi_param)?; - Ok(js_obj) -} - -#[js_function] -fn get_up_x(ctx: CallContext) -> Result { - let js_this = ctx.this_unchecked::(); - let napi_node = ctx.env.unwrap::(&js_this)?; - let node = napi_node.unwrap(); - - let native_param = node.up_x().clone(); - let napi_param = NapiAudioParam::new(native_param); - let mut js_obj = NapiAudioParam::create_js_object(ctx.env)?; - ctx.env.wrap(&mut js_obj, napi_param)?; - Ok(js_obj) -} - -#[js_function] -fn get_up_y(ctx: CallContext) -> Result { - let js_this = ctx.this_unchecked::(); - let napi_node = ctx.env.unwrap::(&js_this)?; - let node = napi_node.unwrap(); - - let native_param = node.up_y().clone(); - let napi_param = NapiAudioParam::new(native_param); - let mut js_obj = NapiAudioParam::create_js_object(ctx.env)?; - ctx.env.wrap(&mut js_obj, napi_param)?; - Ok(js_obj) -} - -#[js_function] -fn get_up_z(ctx: CallContext) -> Result { - let js_this = ctx.this_unchecked::(); - let napi_node = ctx.env.unwrap::(&js_this)?; - let node = napi_node.unwrap(); - - let native_param = node.up_z().clone(); - let napi_param = NapiAudioParam::new(native_param); - let mut js_obj = NapiAudioParam::create_js_object(ctx.env)?; - ctx.env.wrap(&mut js_obj, napi_param)?; - Ok(js_obj) -} diff --git a/src/lib.rs b/src/lib.rs index ea61b765..c0a7e3ee 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,9 +26,6 @@ use napi_derive::module_exports; #[macro_use] mod audio_node; -mod audio_listener; -use audio_listener::NapiAudioListener; - mod audio_param; use crate::audio_param::NapiAudioParam; // public diff --git a/wpt b/wpt index 83f318a1..57c9cc80 160000 --- a/wpt +++ b/wpt @@ -1 +1 @@ -Subproject commit 83f318a105a1a9c58a0c7bff65607d753d3ad9ad +Subproject commit 57c9cc80fbfac825da2f4e2bdedea55828f7ce76 From edc7b5029fb997d8a9ad7bc06e2a3106e0eb971c Mon Sep 17 00:00:00 2001 From: Otto Date: Sat, 2 Dec 2023 20:05:32 +0100 Subject: [PATCH 17/34] Update results of template generation --- src/audio_context.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/audio_context.rs b/src/audio_context.rs index f3341b8c..8a233a3a 100644 --- a/src/audio_context.rs +++ b/src/audio_context.rs @@ -186,10 +186,10 @@ fn get_listener(ctx: CallContext) -> Result { // reproduce lazy instanciation strategy from rust crate let ok_obj = if js_this.has_named_property("__listener__").ok().unwrap() == true { - println!("ok"); + // println!("ok"); js_this.get_named_property("__listener__") } else { - println!("> ONCE"); + // println!("> ONCE"); let napi_obj = ctx.env.unwrap::(&js_this)?; let obj = napi_obj.unwrap(); let native_listener = obj.listener(); From 3406ec75a82337d37ea97e69ca63dfd357583643 Mon Sep 17 00:00:00 2001 From: Otto Date: Sat, 2 Dec 2023 20:06:14 +0100 Subject: [PATCH 18/34] Improve suspend/resume stub, use pending promise instead of resolved --- monkey-patch.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/monkey-patch.js b/monkey-patch.js index c2f37d29..11ee20a5 100644 --- a/monkey-patch.js +++ b/monkey-patch.js @@ -143,11 +143,11 @@ function patchOfflineAudioContext(nativeBinding) { } suspend() { - return Promise.resolve(null); + return new Promise((resolve, reject) => null); } resume() { - return Promise.resolve(null); + return new Promise((resolve, reject) => null); } decodeAudioData(audioData) { From e57fd25a21aee511e92d307e97eb44ac13c9d9d2 Mon Sep 17 00:00:00 2001 From: Otto Date: Sat, 2 Dec 2023 21:05:02 +0100 Subject: [PATCH 19/34] OfflineAudioContext now takes &mut for startRendering --- .../templates/offline_audio_context.tmpl.rs | 56 ++++++------------- src/offline_audio_context.rs | 56 ++++++------------- 2 files changed, 36 insertions(+), 76 deletions(-) diff --git a/generator/templates/offline_audio_context.tmpl.rs b/generator/templates/offline_audio_context.tmpl.rs index 6c8989e9..a726385b 100644 --- a/generator/templates/offline_audio_context.tmpl.rs +++ b/generator/templates/offline_audio_context.tmpl.rs @@ -6,9 +6,7 @@ use web_audio_api::context::*; use crate::*; -// @todo - once Option has been removed, share template with AudioContext - -pub(crate) struct NapiOfflineAudioContext(Option); +pub(crate) struct NapiOfflineAudioContext(OfflineAudioContext); impl NapiOfflineAudioContext { pub fn create_js_class(env: &Env) -> Result { @@ -45,7 +43,7 @@ impl NapiOfflineAudioContext { } pub fn unwrap(&self) -> &OfflineAudioContext { - &self.0.as_ref().unwrap() + &self.0 } } @@ -58,7 +56,7 @@ fn constructor(ctx: CallContext) -> Result { let sample_rate = ctx.get::(2)?.get_double()? as f32; let audio_context = OfflineAudioContext::new(number_of_channels, length, sample_rate); - let napi_audio_context = NapiOfflineAudioContext(Some(audio_context)); + let napi_audio_context = NapiOfflineAudioContext(audio_context); ctx.env.wrap(&mut js_this, napi_audio_context)?; js_this.define_properties(&[ @@ -82,10 +80,7 @@ fn constructor(ctx: CallContext) -> Result { fn get_current_time(ctx: CallContext) -> Result { let js_this = ctx.this_unchecked::(); let napi_obj = ctx.env.unwrap::(&js_this)?; - let obj = match napi_obj.0.as_ref() { - Some(v) => v, - None => return ctx.env.create_double(0.), - }; + let obj = napi_obj.unwrap(); let current_time = obj.current_time() as f64; ctx.env.create_double(current_time) @@ -95,10 +90,7 @@ fn get_current_time(ctx: CallContext) -> Result { fn get_sample_rate(ctx: CallContext) -> Result { let js_this = ctx.this_unchecked::(); let napi_obj = ctx.env.unwrap::(&js_this)?; - let obj = match napi_obj.0.as_ref() { - Some(v) => v, - None => return ctx.env.create_double(0.), - }; + let obj = napi_obj.unwrap(); let sample_rate = obj.sample_rate() as f64; ctx.env.create_double(sample_rate) @@ -270,10 +262,7 @@ fn ${d.slug(factoryName)}(ctx: CallContext) -> Result { fn get_length(ctx: CallContext) -> Result { let js_this = ctx.this_unchecked::(); let napi_obj = ctx.env.unwrap::(&js_this)?; - let obj = match napi_obj.0.as_ref() { - Some(v) => v, - None => return ctx.env.create_double(0.), - }; + let obj = napi_obj.unwrap(); let length = obj.length() as f64; ctx.env.create_double(length) @@ -284,28 +273,19 @@ fn get_length(ctx: CallContext) -> Result { fn start_rendering(ctx: CallContext) -> Result { let js_this = ctx.this_unchecked::(); let napi_obj = ctx.env.unwrap::(&js_this)?; - let some_audio_context = napi_obj.0.take(); - - match some_audio_context { - Some(audio_context) => { - let audio_buffer = audio_context.start_rendering_sync(); + let audio_buffer = napi_obj.0.start_rendering_sync(); - // create js audio buffer instance - let store_ref: &mut napi::Ref<()> = ctx.env.get_instance_data()?.unwrap(); - let store: JsObject = ctx.env.get_reference_value(store_ref)?; - let ctor: JsFunction = store.get_named_property("AudioBuffer")?; - let mut options = ctx.env.create_object()?; - options.set("__internal_caller__", ctx.env.get_null())?; + // create js audio buffer instance + let store_ref: &mut napi::Ref<()> = ctx.env.get_instance_data()?.unwrap(); + let store: JsObject = ctx.env.get_reference_value(store_ref)?; + let ctor: JsFunction = store.get_named_property("AudioBuffer")?; + let mut options = ctx.env.create_object()?; + options.set("__internal_caller__", ctx.env.get_null())?; - // populate with audio buffer - let js_audio_buffer = ctor.new_instance(&[options])?; - let napi_audio_buffer = ctx.env.unwrap::(&js_audio_buffer)?; - napi_audio_buffer.populate(audio_buffer); + // populate with audio buffer + let js_audio_buffer = ctor.new_instance(&[options])?; + let napi_audio_buffer = ctx.env.unwrap::(&js_audio_buffer)?; + napi_audio_buffer.populate(audio_buffer); - Ok(js_audio_buffer) - } - None => { - Err(napi::Error::from_reason("startRendering already called".to_string())) - }, - } + Ok(js_audio_buffer) } diff --git a/src/offline_audio_context.rs b/src/offline_audio_context.rs index abc5d04d..027f4875 100644 --- a/src/offline_audio_context.rs +++ b/src/offline_audio_context.rs @@ -25,9 +25,7 @@ use web_audio_api::context::*; use crate::*; -// @todo - once Option has been removed, share template with AudioContext - -pub(crate) struct NapiOfflineAudioContext(Option); +pub(crate) struct NapiOfflineAudioContext(OfflineAudioContext); impl NapiOfflineAudioContext { pub fn create_js_class(env: &Env) -> Result { @@ -71,7 +69,7 @@ impl NapiOfflineAudioContext { } pub fn unwrap(&self) -> &OfflineAudioContext { - &self.0.as_ref().unwrap() + &self.0 } } @@ -84,7 +82,7 @@ fn constructor(ctx: CallContext) -> Result { let sample_rate = ctx.get::(2)?.get_double()? as f32; let audio_context = OfflineAudioContext::new(number_of_channels, length, sample_rate); - let napi_audio_context = NapiOfflineAudioContext(Some(audio_context)); + let napi_audio_context = NapiOfflineAudioContext(audio_context); ctx.env.wrap(&mut js_this, napi_audio_context)?; js_this.define_properties(&[ @@ -108,10 +106,7 @@ fn constructor(ctx: CallContext) -> Result { fn get_current_time(ctx: CallContext) -> Result { let js_this = ctx.this_unchecked::(); let napi_obj = ctx.env.unwrap::(&js_this)?; - let obj = match napi_obj.0.as_ref() { - Some(v) => v, - None => return ctx.env.create_double(0.), - }; + let obj = napi_obj.unwrap(); let current_time = obj.current_time() as f64; ctx.env.create_double(current_time) @@ -121,10 +116,7 @@ fn get_current_time(ctx: CallContext) -> Result { fn get_sample_rate(ctx: CallContext) -> Result { let js_this = ctx.this_unchecked::(); let napi_obj = ctx.env.unwrap::(&js_this)?; - let obj = match napi_obj.0.as_ref() { - Some(v) => v, - None => return ctx.env.create_double(0.), - }; + let obj = napi_obj.unwrap(); let sample_rate = obj.sample_rate() as f64; ctx.env.create_double(sample_rate) @@ -446,10 +438,7 @@ fn create_wave_shaper(ctx: CallContext) -> Result { fn get_length(ctx: CallContext) -> Result { let js_this = ctx.this_unchecked::(); let napi_obj = ctx.env.unwrap::(&js_this)?; - let obj = match napi_obj.0.as_ref() { - Some(v) => v, - None => return ctx.env.create_double(0.), - }; + let obj = napi_obj.unwrap(); let length = obj.length() as f64; ctx.env.create_double(length) @@ -460,28 +449,19 @@ fn get_length(ctx: CallContext) -> Result { fn start_rendering(ctx: CallContext) -> Result { let js_this = ctx.this_unchecked::(); let napi_obj = ctx.env.unwrap::(&js_this)?; - let some_audio_context = napi_obj.0.take(); - - match some_audio_context { - Some(audio_context) => { - let audio_buffer = audio_context.start_rendering_sync(); + let audio_buffer = napi_obj.0.start_rendering_sync(); - // create js audio buffer instance - let store_ref: &mut napi::Ref<()> = ctx.env.get_instance_data()?.unwrap(); - let store: JsObject = ctx.env.get_reference_value(store_ref)?; - let ctor: JsFunction = store.get_named_property("AudioBuffer")?; - let mut options = ctx.env.create_object()?; - options.set("__internal_caller__", ctx.env.get_null())?; + // create js audio buffer instance + let store_ref: &mut napi::Ref<()> = ctx.env.get_instance_data()?.unwrap(); + let store: JsObject = ctx.env.get_reference_value(store_ref)?; + let ctor: JsFunction = store.get_named_property("AudioBuffer")?; + let mut options = ctx.env.create_object()?; + options.set("__internal_caller__", ctx.env.get_null())?; - // populate with audio buffer - let js_audio_buffer = ctor.new_instance(&[options])?; - let napi_audio_buffer = ctx.env.unwrap::(&js_audio_buffer)?; - napi_audio_buffer.populate(audio_buffer); + // populate with audio buffer + let js_audio_buffer = ctor.new_instance(&[options])?; + let napi_audio_buffer = ctx.env.unwrap::(&js_audio_buffer)?; + napi_audio_buffer.populate(audio_buffer); - Ok(js_audio_buffer) - } - None => Err(napi::Error::from_reason( - "startRendering already called".to_string(), - )), - } + Ok(js_audio_buffer) } From 5892b7db02fee03d49d1c85e76bd750909af25b8 Mon Sep 17 00:00:00 2001 From: Otto Date: Sun, 3 Dec 2023 11:47:22 +0100 Subject: [PATCH 20/34] Revert "OfflineAudioContext now takes &mut for startRendering" This reverts commit e57fd25a21aee511e92d307e97eb44ac13c9d9d2. --- .../templates/offline_audio_context.tmpl.rs | 56 +++++++++++++------ src/offline_audio_context.rs | 56 +++++++++++++------ 2 files changed, 76 insertions(+), 36 deletions(-) diff --git a/generator/templates/offline_audio_context.tmpl.rs b/generator/templates/offline_audio_context.tmpl.rs index a726385b..6c8989e9 100644 --- a/generator/templates/offline_audio_context.tmpl.rs +++ b/generator/templates/offline_audio_context.tmpl.rs @@ -6,7 +6,9 @@ use web_audio_api::context::*; use crate::*; -pub(crate) struct NapiOfflineAudioContext(OfflineAudioContext); +// @todo - once Option has been removed, share template with AudioContext + +pub(crate) struct NapiOfflineAudioContext(Option); impl NapiOfflineAudioContext { pub fn create_js_class(env: &Env) -> Result { @@ -43,7 +45,7 @@ impl NapiOfflineAudioContext { } pub fn unwrap(&self) -> &OfflineAudioContext { - &self.0 + &self.0.as_ref().unwrap() } } @@ -56,7 +58,7 @@ fn constructor(ctx: CallContext) -> Result { let sample_rate = ctx.get::(2)?.get_double()? as f32; let audio_context = OfflineAudioContext::new(number_of_channels, length, sample_rate); - let napi_audio_context = NapiOfflineAudioContext(audio_context); + let napi_audio_context = NapiOfflineAudioContext(Some(audio_context)); ctx.env.wrap(&mut js_this, napi_audio_context)?; js_this.define_properties(&[ @@ -80,7 +82,10 @@ fn constructor(ctx: CallContext) -> Result { fn get_current_time(ctx: CallContext) -> Result { let js_this = ctx.this_unchecked::(); let napi_obj = ctx.env.unwrap::(&js_this)?; - let obj = napi_obj.unwrap(); + let obj = match napi_obj.0.as_ref() { + Some(v) => v, + None => return ctx.env.create_double(0.), + }; let current_time = obj.current_time() as f64; ctx.env.create_double(current_time) @@ -90,7 +95,10 @@ fn get_current_time(ctx: CallContext) -> Result { fn get_sample_rate(ctx: CallContext) -> Result { let js_this = ctx.this_unchecked::(); let napi_obj = ctx.env.unwrap::(&js_this)?; - let obj = napi_obj.unwrap(); + let obj = match napi_obj.0.as_ref() { + Some(v) => v, + None => return ctx.env.create_double(0.), + }; let sample_rate = obj.sample_rate() as f64; ctx.env.create_double(sample_rate) @@ -262,7 +270,10 @@ fn ${d.slug(factoryName)}(ctx: CallContext) -> Result { fn get_length(ctx: CallContext) -> Result { let js_this = ctx.this_unchecked::(); let napi_obj = ctx.env.unwrap::(&js_this)?; - let obj = napi_obj.unwrap(); + let obj = match napi_obj.0.as_ref() { + Some(v) => v, + None => return ctx.env.create_double(0.), + }; let length = obj.length() as f64; ctx.env.create_double(length) @@ -273,19 +284,28 @@ fn get_length(ctx: CallContext) -> Result { fn start_rendering(ctx: CallContext) -> Result { let js_this = ctx.this_unchecked::(); let napi_obj = ctx.env.unwrap::(&js_this)?; - let audio_buffer = napi_obj.0.start_rendering_sync(); + let some_audio_context = napi_obj.0.take(); - // create js audio buffer instance - let store_ref: &mut napi::Ref<()> = ctx.env.get_instance_data()?.unwrap(); - let store: JsObject = ctx.env.get_reference_value(store_ref)?; - let ctor: JsFunction = store.get_named_property("AudioBuffer")?; - let mut options = ctx.env.create_object()?; - options.set("__internal_caller__", ctx.env.get_null())?; + match some_audio_context { + Some(audio_context) => { + let audio_buffer = audio_context.start_rendering_sync(); + + // create js audio buffer instance + let store_ref: &mut napi::Ref<()> = ctx.env.get_instance_data()?.unwrap(); + let store: JsObject = ctx.env.get_reference_value(store_ref)?; + let ctor: JsFunction = store.get_named_property("AudioBuffer")?; + let mut options = ctx.env.create_object()?; + options.set("__internal_caller__", ctx.env.get_null())?; - // populate with audio buffer - let js_audio_buffer = ctor.new_instance(&[options])?; - let napi_audio_buffer = ctx.env.unwrap::(&js_audio_buffer)?; - napi_audio_buffer.populate(audio_buffer); + // populate with audio buffer + let js_audio_buffer = ctor.new_instance(&[options])?; + let napi_audio_buffer = ctx.env.unwrap::(&js_audio_buffer)?; + napi_audio_buffer.populate(audio_buffer); - Ok(js_audio_buffer) + Ok(js_audio_buffer) + } + None => { + Err(napi::Error::from_reason("startRendering already called".to_string())) + }, + } } diff --git a/src/offline_audio_context.rs b/src/offline_audio_context.rs index 027f4875..abc5d04d 100644 --- a/src/offline_audio_context.rs +++ b/src/offline_audio_context.rs @@ -25,7 +25,9 @@ use web_audio_api::context::*; use crate::*; -pub(crate) struct NapiOfflineAudioContext(OfflineAudioContext); +// @todo - once Option has been removed, share template with AudioContext + +pub(crate) struct NapiOfflineAudioContext(Option); impl NapiOfflineAudioContext { pub fn create_js_class(env: &Env) -> Result { @@ -69,7 +71,7 @@ impl NapiOfflineAudioContext { } pub fn unwrap(&self) -> &OfflineAudioContext { - &self.0 + &self.0.as_ref().unwrap() } } @@ -82,7 +84,7 @@ fn constructor(ctx: CallContext) -> Result { let sample_rate = ctx.get::(2)?.get_double()? as f32; let audio_context = OfflineAudioContext::new(number_of_channels, length, sample_rate); - let napi_audio_context = NapiOfflineAudioContext(audio_context); + let napi_audio_context = NapiOfflineAudioContext(Some(audio_context)); ctx.env.wrap(&mut js_this, napi_audio_context)?; js_this.define_properties(&[ @@ -106,7 +108,10 @@ fn constructor(ctx: CallContext) -> Result { fn get_current_time(ctx: CallContext) -> Result { let js_this = ctx.this_unchecked::(); let napi_obj = ctx.env.unwrap::(&js_this)?; - let obj = napi_obj.unwrap(); + let obj = match napi_obj.0.as_ref() { + Some(v) => v, + None => return ctx.env.create_double(0.), + }; let current_time = obj.current_time() as f64; ctx.env.create_double(current_time) @@ -116,7 +121,10 @@ fn get_current_time(ctx: CallContext) -> Result { fn get_sample_rate(ctx: CallContext) -> Result { let js_this = ctx.this_unchecked::(); let napi_obj = ctx.env.unwrap::(&js_this)?; - let obj = napi_obj.unwrap(); + let obj = match napi_obj.0.as_ref() { + Some(v) => v, + None => return ctx.env.create_double(0.), + }; let sample_rate = obj.sample_rate() as f64; ctx.env.create_double(sample_rate) @@ -438,7 +446,10 @@ fn create_wave_shaper(ctx: CallContext) -> Result { fn get_length(ctx: CallContext) -> Result { let js_this = ctx.this_unchecked::(); let napi_obj = ctx.env.unwrap::(&js_this)?; - let obj = napi_obj.unwrap(); + let obj = match napi_obj.0.as_ref() { + Some(v) => v, + None => return ctx.env.create_double(0.), + }; let length = obj.length() as f64; ctx.env.create_double(length) @@ -449,19 +460,28 @@ fn get_length(ctx: CallContext) -> Result { fn start_rendering(ctx: CallContext) -> Result { let js_this = ctx.this_unchecked::(); let napi_obj = ctx.env.unwrap::(&js_this)?; - let audio_buffer = napi_obj.0.start_rendering_sync(); + let some_audio_context = napi_obj.0.take(); - // create js audio buffer instance - let store_ref: &mut napi::Ref<()> = ctx.env.get_instance_data()?.unwrap(); - let store: JsObject = ctx.env.get_reference_value(store_ref)?; - let ctor: JsFunction = store.get_named_property("AudioBuffer")?; - let mut options = ctx.env.create_object()?; - options.set("__internal_caller__", ctx.env.get_null())?; + match some_audio_context { + Some(audio_context) => { + let audio_buffer = audio_context.start_rendering_sync(); + + // create js audio buffer instance + let store_ref: &mut napi::Ref<()> = ctx.env.get_instance_data()?.unwrap(); + let store: JsObject = ctx.env.get_reference_value(store_ref)?; + let ctor: JsFunction = store.get_named_property("AudioBuffer")?; + let mut options = ctx.env.create_object()?; + options.set("__internal_caller__", ctx.env.get_null())?; - // populate with audio buffer - let js_audio_buffer = ctor.new_instance(&[options])?; - let napi_audio_buffer = ctx.env.unwrap::(&js_audio_buffer)?; - napi_audio_buffer.populate(audio_buffer); + // populate with audio buffer + let js_audio_buffer = ctor.new_instance(&[options])?; + let napi_audio_buffer = ctx.env.unwrap::(&js_audio_buffer)?; + napi_audio_buffer.populate(audio_buffer); - Ok(js_audio_buffer) + Ok(js_audio_buffer) + } + None => Err(napi::Error::from_reason( + "startRendering already called".to_string(), + )), + } } From e675723f6a3871c7514618f0b96a694f2cadb378 Mon Sep 17 00:00:00 2001 From: Otto Date: Sun, 3 Dec 2023 11:51:37 +0100 Subject: [PATCH 21/34] Revert other changes for OfflineAudioContext being consumed --- generator/templates/offline_audio_context.tmpl.rs | 15 +++------------ src/offline_audio_context.rs | 15 +++------------ 2 files changed, 6 insertions(+), 24 deletions(-) diff --git a/generator/templates/offline_audio_context.tmpl.rs b/generator/templates/offline_audio_context.tmpl.rs index 21cde8bf..d1bf4ea6 100644 --- a/generator/templates/offline_audio_context.tmpl.rs +++ b/generator/templates/offline_audio_context.tmpl.rs @@ -83,10 +83,7 @@ fn constructor(ctx: CallContext) -> Result { fn get_current_time(ctx: CallContext) -> Result { let js_this = ctx.this_unchecked::(); let napi_obj = ctx.env.unwrap::(&js_this)?; - let obj = match napi_obj.0.as_ref() { - Some(v) => v, - None => return ctx.env.create_double(0.), - }; + let obj = napi_obj.unwrap(); let current_time = obj.current_time() as f64; ctx.env.create_double(current_time) @@ -96,10 +93,7 @@ fn get_current_time(ctx: CallContext) -> Result { fn get_sample_rate(ctx: CallContext) -> Result { let js_this = ctx.this_unchecked::(); let napi_obj = ctx.env.unwrap::(&js_this)?; - let obj = match napi_obj.0.as_ref() { - Some(v) => v, - None => return ctx.env.create_double(0.), - }; + let obj = napi_obj.unwrap(); let sample_rate = obj.sample_rate() as f64; ctx.env.create_double(sample_rate) @@ -267,10 +261,7 @@ fn ${d.slug(factoryName)}(ctx: CallContext) -> Result { fn get_length(ctx: CallContext) -> Result { let js_this = ctx.this_unchecked::(); let napi_obj = ctx.env.unwrap::(&js_this)?; - let obj = match napi_obj.0.as_ref() { - Some(v) => v, - None => return ctx.env.create_double(0.), - }; + let obj = napi_obj.unwrap(); let length = obj.length() as f64; ctx.env.create_double(length) diff --git a/src/offline_audio_context.rs b/src/offline_audio_context.rs index 42ccd346..8ff7e9ef 100644 --- a/src/offline_audio_context.rs +++ b/src/offline_audio_context.rs @@ -108,10 +108,7 @@ fn constructor(ctx: CallContext) -> Result { fn get_current_time(ctx: CallContext) -> Result { let js_this = ctx.this_unchecked::(); let napi_obj = ctx.env.unwrap::(&js_this)?; - let obj = match napi_obj.0.as_ref() { - Some(v) => v, - None => return ctx.env.create_double(0.), - }; + let obj = napi_obj.unwrap(); let current_time = obj.current_time() as f64; ctx.env.create_double(current_time) @@ -121,10 +118,7 @@ fn get_current_time(ctx: CallContext) -> Result { fn get_sample_rate(ctx: CallContext) -> Result { let js_this = ctx.this_unchecked::(); let napi_obj = ctx.env.unwrap::(&js_this)?; - let obj = match napi_obj.0.as_ref() { - Some(v) => v, - None => return ctx.env.create_double(0.), - }; + let obj = napi_obj.unwrap(); let sample_rate = obj.sample_rate() as f64; ctx.env.create_double(sample_rate) @@ -442,10 +436,7 @@ fn create_wave_shaper(ctx: CallContext) -> Result { fn get_length(ctx: CallContext) -> Result { let js_this = ctx.this_unchecked::(); let napi_obj = ctx.env.unwrap::(&js_this)?; - let obj = match napi_obj.0.as_ref() { - Some(v) => v, - None => return ctx.env.create_double(0.), - }; + let obj = napi_obj.unwrap(); let length = obj.length() as f64; ctx.env.create_double(length) From babd5bd9b914394f38f4708318378ac9a69b03bc Mon Sep 17 00:00:00 2001 From: Otto Date: Wed, 6 Dec 2023 08:36:20 +0100 Subject: [PATCH 22/34] Remove printlns --- src/audio_context.rs | 2 -- src/offline_audio_context.rs | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/audio_context.rs b/src/audio_context.rs index b0e1fedf..a87ed201 100644 --- a/src/audio_context.rs +++ b/src/audio_context.rs @@ -192,10 +192,8 @@ fn get_listener(ctx: CallContext) -> Result { // reproduce lazy instanciation strategy from rust crate if js_this.has_named_property("__listener__").ok().unwrap() { - println!("reuse"); js_this.get_named_property("__listener__") } else { - println!("wrap"); let store_ref: &mut napi::Ref<()> = ctx.env.get_instance_data()?.unwrap(); let store: JsObject = ctx.env.get_reference_value(store_ref)?; let ctor: JsFunction = store.get_named_property("AudioListener")?; diff --git a/src/offline_audio_context.rs b/src/offline_audio_context.rs index bf306ccf..5dd14078 100644 --- a/src/offline_audio_context.rs +++ b/src/offline_audio_context.rs @@ -136,10 +136,8 @@ fn get_listener(ctx: CallContext) -> Result { // reproduce lazy instanciation strategy from rust crate if js_this.has_named_property("__listener__").ok().unwrap() { - println!("reuse"); js_this.get_named_property("__listener__") } else { - println!("wrap"); let store_ref: &mut napi::Ref<()> = ctx.env.get_instance_data()?.unwrap(); let store: JsObject = ctx.env.get_reference_value(store_ref)?; let ctor: JsFunction = store.get_named_property("AudioListener")?; From 4374dd990b5cd63cd4813fdc8ac322131cab4f5a Mon Sep 17 00:00:00 2001 From: Otto Date: Wed, 6 Dec 2023 09:04:06 +0100 Subject: [PATCH 23/34] Attempting to get OfflineAudioContext.suspend to work --- Cargo.toml | 8 ++--- examples/offline.mjs | 16 ++++++---- generator/templates/audio_context.tmpl.rs | 39 +++++++++++++++++++++++ src/offline_audio_context.rs | 39 +++++++++++++++++++++++ 4 files changed, 92 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e4e098a0..05f22c44 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,8 +12,8 @@ crate-type = ["cdylib"] [dependencies] napi = {version="2.13", features=["napi6"]} napi-derive = "2.13" -web-audio-api = "0.38" -# web-audio-api = { path = "../web-audio-api-rs" } +# web-audio-api = "0.38" +web-audio-api = { path = "../web-audio-api-rs" } [target.'cfg(all(any(windows, unix), target_arch = "x86_64", not(target_env = "musl")))'.dependencies] mimalloc = {version = "0.1"} @@ -22,8 +22,8 @@ mimalloc = {version = "0.1"} napi-build = "1" [profile.release] -lto = "fat" # https://nnethercote.github.io/perf-book/build-configuration.html#link-time-optimization -# debug = true +lto = false +debug = true [features] jack = ["web-audio-api/cpal-jack"] diff --git a/examples/offline.mjs b/examples/offline.mjs index 600566ee..723782af 100644 --- a/examples/offline.mjs +++ b/examples/offline.mjs @@ -2,22 +2,26 @@ import { AudioContext, OfflineAudioContext } from '../index.mjs'; const offline = new OfflineAudioContext(1, 44100, 44100); -const osc = offline.createOscillator(); -osc.connect(offline.destination); -osc.frequency.value = 220; -osc.start(0); -osc.stop(1); +offline.suspend_and(0.5, function() { + console.log("hello", offline.length); + const osc = offline.createOscillator(); + osc.connect(offline.destination); + osc.frequency.value = 220; + osc.start(0); +}); const buffer = await offline.startRendering(); +console.log("buffer duration s:", buffer.duration); const latencyHint = process.env.WEB_AUDIO_LATENCY === 'playback' ? 'playback' : 'interactive'; const online = new AudioContext({ latencyHint }); const src = online.createBufferSource(); +src.loop = true; src.buffer = buffer; src.connect(online.destination); src.start(); -await new Promise(resolve => setTimeout(resolve, 1000)); +await new Promise(resolve => setTimeout(resolve, 3000)); await online.close(); diff --git a/generator/templates/audio_context.tmpl.rs b/generator/templates/audio_context.tmpl.rs index 5cbc7d83..e4f6850d 100644 --- a/generator/templates/audio_context.tmpl.rs +++ b/generator/templates/audio_context.tmpl.rs @@ -53,6 +53,7 @@ impl ${d.napiName(d.node)} { // ---------------------------------------------------- Property::new("length")?.with_getter(get_length), Property::new("startRendering")?.with_method(start_rendering), + Property::new("suspend_and")?.with_method(suspend_and), ` } ], @@ -470,5 +471,43 @@ fn start_rendering(ctx: CallContext) -> Result { Ok(js_audio_buffer) } + +#[js_function(2)] +fn suspend_and(ctx: CallContext) -> Result { + use napi::threadsafe_function::ThreadSafeCallContext; + let js_this = ctx.this_unchecked::(); + let napi_obj = ctx.env.unwrap::(&js_this)?; + + let when = match ctx.try_get::(0)? { + Either::A(value) => value.get_double()?, + Either::B(_) => 0. + }; + + let js_func = match ctx.try_get::(1)? { + Either::A(value) => value, + Either::B(_) => todo!(), + }; + + println!("suspend_and {}", when); + let js_func_ref = ctx.env.create_reference(js_func)?; + let js_func: JsFunction = ctx.env.get_reference_value(&js_func_ref).unwrap(); + + /* + let tsfn = + ctx + .env + .create_threadsafe_function(&js_func, 0, |ctx: ThreadSafeCallContext<()>| { + Ok(vec![()]) + })?; + */ + + napi_obj.0.suspend_at(when, move |_| { + println!("callback runs now"); + js_func.call_without_args(Some(&js_this)).unwrap(); + //tsfn.call(Ok(()), napi::threadsafe_function::ThreadsafeFunctionCallMode::Blocking); + }); + + ctx.env.get_undefined() +} ` } diff --git a/src/offline_audio_context.rs b/src/offline_audio_context.rs index 5dd14078..99f83576 100644 --- a/src/offline_audio_context.rs +++ b/src/offline_audio_context.rs @@ -62,6 +62,7 @@ impl NapiOfflineAudioContext { // ---------------------------------------------------- Property::new("length")?.with_getter(get_length), Property::new("startRendering")?.with_method(start_rendering), + Property::new("suspend_and")?.with_method(suspend_and), ], ) } @@ -470,3 +471,41 @@ fn start_rendering(ctx: CallContext) -> Result { Ok(js_audio_buffer) } + +#[js_function(2)] +fn suspend_and(ctx: CallContext) -> Result { + use napi::threadsafe_function::ThreadSafeCallContext; + let js_this = ctx.this_unchecked::(); + let napi_obj = ctx.env.unwrap::(&js_this)?; + + let when = match ctx.try_get::(0)? { + Either::A(value) => value.get_double()?, + Either::B(_) => 0., + }; + + let js_func = match ctx.try_get::(1)? { + Either::A(value) => value, + Either::B(_) => todo!(), + }; + + println!("suspend_and {}", when); + let js_func_ref = ctx.env.create_reference(js_func)?; + let js_func: JsFunction = ctx.env.get_reference_value(&js_func_ref).unwrap(); + + /* + let tsfn = + ctx + .env + .create_threadsafe_function(&js_func, 0, |ctx: ThreadSafeCallContext<()>| { + Ok(vec![()]) + })?; + */ + + napi_obj.0.suspend_at(when, move |_| { + println!("callback runs now"); + js_func.call_without_args(Some(&js_this)).unwrap(); + //tsfn.call(Ok(()), napi::threadsafe_function::ThreadsafeFunctionCallMode::Blocking); + }); + + ctx.env.get_undefined() +} From 1ed0c50895f132c8763be4b60c5aad419cfb9cef Mon Sep 17 00:00:00 2001 From: b-ma Date: Thu, 7 Dec 2023 14:07:03 +0100 Subject: [PATCH 24/34] make callback run without crashing --- examples/offline.mjs | 14 +++++++------ generator/templates/audio_context.tmpl.rs | 19 ++++++----------- src/offline_audio_context.rs | 25 +++++++++++++---------- wpt | 2 +- 4 files changed, 29 insertions(+), 31 deletions(-) diff --git a/examples/offline.mjs b/examples/offline.mjs index 723782af..165838ec 100644 --- a/examples/offline.mjs +++ b/examples/offline.mjs @@ -2,16 +2,18 @@ import { AudioContext, OfflineAudioContext } from '../index.mjs'; const offline = new OfflineAudioContext(1, 44100, 44100); -offline.suspend_and(0.5, function() { - console.log("hello", offline.length); - const osc = offline.createOscillator(); - osc.connect(offline.destination); - osc.frequency.value = 220; - osc.start(0); +offline.suspend_and(0.1, function() { + console.log("hello there", offline.length); + const osc = offline.createOscillator(); + osc.connect(offline.destination); + osc.frequency.value = 220; + osc.start(0); }); const buffer = await offline.startRendering(); console.log("buffer duration s:", buffer.duration); +const channelData = buffer.getChannelData(0) +console.log("buffer", channelData[40000]); const latencyHint = process.env.WEB_AUDIO_LATENCY === 'playback' ? 'playback' : 'interactive'; const online = new AudioContext({ latencyHint }); diff --git a/generator/templates/audio_context.tmpl.rs b/generator/templates/audio_context.tmpl.rs index e4f6850d..50cc49a3 100644 --- a/generator/templates/audio_context.tmpl.rs +++ b/generator/templates/audio_context.tmpl.rs @@ -489,22 +489,15 @@ fn suspend_and(ctx: CallContext) -> Result { }; println!("suspend_and {}", when); - let js_func_ref = ctx.env.create_reference(js_func)?; - let js_func: JsFunction = ctx.env.get_reference_value(&js_func_ref).unwrap(); - - /* - let tsfn = - ctx - .env - .create_threadsafe_function(&js_func, 0, |ctx: ThreadSafeCallContext<()>| { - Ok(vec![()]) - })?; - */ + + let tsfn = ctx.env.create_threadsafe_function(&js_func, 0, |ctx: ThreadSafeCallContext<()>| { + Ok(vec![()]) + })?; napi_obj.0.suspend_at(when, move |_| { println!("callback runs now"); - js_func.call_without_args(Some(&js_this)).unwrap(); - //tsfn.call(Ok(()), napi::threadsafe_function::ThreadsafeFunctionCallMode::Blocking); + // js_func.call_without_args(Some(&js_this)).unwrap(); + tsfn.call(Ok(()), napi::threadsafe_function::ThreadsafeFunctionCallMode::Blocking); }); ctx.env.get_undefined() diff --git a/src/offline_audio_context.rs b/src/offline_audio_context.rs index 99f83576..494c97d7 100644 --- a/src/offline_audio_context.rs +++ b/src/offline_audio_context.rs @@ -489,23 +489,26 @@ fn suspend_and(ctx: CallContext) -> Result { }; println!("suspend_and {}", when); - let js_func_ref = ctx.env.create_reference(js_func)?; - let js_func: JsFunction = ctx.env.get_reference_value(&js_func_ref).unwrap(); - /* let tsfn = - ctx - .env - .create_threadsafe_function(&js_func, 0, |ctx: ThreadSafeCallContext<()>| { - Ok(vec![()]) - })?; - */ + ctx.env + .create_threadsafe_function(&js_func, 0, |ctx: ThreadSafeCallContext<()>| { + Ok(vec![()]) + })?; napi_obj.0.suspend_at(when, move |_| { println!("callback runs now"); - js_func.call_without_args(Some(&js_this)).unwrap(); - //tsfn.call(Ok(()), napi::threadsafe_function::ThreadsafeFunctionCallMode::Blocking); + // js_func.call_without_args(Some(&js_this)).unwrap(); + tsfn.call( + Ok(()), + napi::threadsafe_function::ThreadsafeFunctionCallMode::Blocking, + ); }); + // let tsfn_cloned = tsfn.clone(); + + // let js_func_ref = ctx.env.create_reference(js_func)?; + // let js_func: JsFunction = ctx.env.get_reference_value(&js_func_ref).unwrap(); + ctx.env.get_undefined() } diff --git a/wpt b/wpt index 57c9cc80..83f318a1 160000 --- a/wpt +++ b/wpt @@ -1 +1 @@ -Subproject commit 57c9cc80fbfac825da2f4e2bdedea55828f7ce76 +Subproject commit 83f318a105a1a9c58a0c7bff65607d753d3ad9ad From 8f4d4540d2d11b5335b15c9085d003527cbe6d0d Mon Sep 17 00:00:00 2001 From: b-ma Date: Thu, 21 Dec 2023 16:23:52 +0100 Subject: [PATCH 25/34] feat: implement async startRendering, susped and resume for OfflineAudioContext --- Cargo.toml | 2 +- examples/offline.mjs | 43 ++++++++-- generator/templates/audio_context.tmpl.rs | 89 +++++++++++++-------- monkey-patch.js | 12 +-- src/audio_context.rs | 5 +- src/offline_audio_context.rs | 97 ++++++++++++----------- 6 files changed, 146 insertions(+), 102 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 05f22c44..0277e291 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ version = "0.13.0" crate-type = ["cdylib"] [dependencies] -napi = {version="2.13", features=["napi6"]} +napi = {version="2.13", features=["napi6", "tokio_rt"]} napi-derive = "2.13" # web-audio-api = "0.38" web-audio-api = { path = "../web-audio-api-rs" } diff --git a/examples/offline.mjs b/examples/offline.mjs index 165838ec..25e8c4b9 100644 --- a/examples/offline.mjs +++ b/examples/offline.mjs @@ -1,29 +1,56 @@ import { AudioContext, OfflineAudioContext } from '../index.mjs'; -const offline = new OfflineAudioContext(1, 44100, 44100); +const offline = new OfflineAudioContext(1, 48000, 48000); + +offline.suspend(128 / 48000).then(() => { + console.log("suspend"); -offline.suspend_and(0.1, function() { - console.log("hello there", offline.length); const osc = offline.createOscillator(); osc.connect(offline.destination); osc.frequency.value = 220; osc.start(0); + + console.log("resume"); + offline.resume(); }); const buffer = await offline.startRendering(); -console.log("buffer duration s:", buffer.duration); -const channelData = buffer.getChannelData(0) -console.log("buffer", channelData[40000]); +console.log("buffer duration:", buffer.duration); + +// dirty check the audio buffer +const channelData = buffer.getChannelData(0); + +for (let i = 0; i < 48000; i++) { + // we check + if (i < 128) { + // before suspend the graph is empty + if (channelData[i] !== 0) { + throw new Error('should be zero') + } + // first sine sample is zero + } else if (i === 128) { + if (channelData[i] !== 0) { + throw new Error('should be zero') + } + } else { + // should ha ve a sine wave, hopefully without zero values :) + if (channelData[i] === 0) { + throw new Error(`should not be zero ${i}`); + console.log(channelData[i]) + } + } +} const latencyHint = process.env.WEB_AUDIO_LATENCY === 'playback' ? 'playback' : 'interactive'; const online = new AudioContext({ latencyHint }); const src = online.createBufferSource(); -src.loop = true; +// src.loop = true; src.buffer = buffer; +src.loop = true; src.connect(online.destination); src.start(); -await new Promise(resolve => setTimeout(resolve, 3000)); +await new Promise(resolve => setTimeout(resolve, 2000)); await online.close(); diff --git a/generator/templates/audio_context.tmpl.rs b/generator/templates/audio_context.tmpl.rs index 50cc49a3..ecbe8fbd 100644 --- a/generator/templates/audio_context.tmpl.rs +++ b/generator/templates/audio_context.tmpl.rs @@ -1,4 +1,5 @@ use std::io::Cursor; +use std::sync::Arc; use napi::*; use napi_derive::js_function; @@ -6,7 +7,7 @@ use web_audio_api::context::*; use crate::*; -pub(crate) struct ${d.napiName(d.node)}(${d.name(d.node)}); +pub(crate) struct ${d.napiName(d.node)}(Arc<${d.name(d.node)}>); impl ${d.napiName(d.node)} { pub fn create_js_class(env: &Env) -> Result { @@ -53,7 +54,8 @@ impl ${d.napiName(d.node)} { // ---------------------------------------------------- Property::new("length")?.with_getter(get_length), Property::new("startRendering")?.with_method(start_rendering), - Property::new("suspend_and")?.with_method(suspend_and), + Property::new("suspend")?.with_method(suspend_offline), + Property::new("resume")?.with_method(resume_offline), ` } ], @@ -142,7 +144,7 @@ fn constructor(ctx: CallContext) -> Result { // ------------------------------------------------- // Wrap context // ------------------------------------------------- - let napi_audio_context = ${d.napiName(d.node)}(audio_context); + let napi_audio_context = ${d.napiName(d.node)}(Arc::new(audio_context)); ctx.env.wrap(&mut js_this, napi_audio_context)?; js_this.define_properties(&[ @@ -455,52 +457,69 @@ fn start_rendering(ctx: CallContext) -> Result { let js_this = ctx.this_unchecked::(); let napi_obj = ctx.env.unwrap::<${d.napiName(d.node)}>(&js_this)?; - let audio_buffer = napi_obj.0.start_rendering_sync(); + let clone = napi_obj.0.clone(); - // create js audio buffer instance - let store_ref: &mut napi::Ref<()> = ctx.env.get_instance_data()?.unwrap(); - let store: JsObject = ctx.env.get_reference_value(store_ref)?; - let ctor: JsFunction = store.get_named_property("AudioBuffer")?; - let mut options = ctx.env.create_object()?; - options.set("__internal_caller__", ctx.env.get_null())?; - - // populate with audio buffer - let js_audio_buffer = ctor.new_instance(&[options])?; - let napi_audio_buffer = ctx.env.unwrap::(&js_audio_buffer)?; - napi_audio_buffer.populate(audio_buffer); + ctx.env.execute_tokio_future( + async move { + let audio_buffer = clone.start_rendering().await; + Ok(audio_buffer) + }, + |&mut env, audio_buffer| { + // create js audio buffer instance + let store_ref: &mut napi::Ref<()> = env.get_instance_data()?.unwrap(); + let store: JsObject = env.get_reference_value(store_ref)?; + let ctor: JsFunction = store.get_named_property("AudioBuffer")?; + // this should be cleaned + let mut options = env.create_object()?; + options.set("__internal_caller__", env.get_null())?; + // populate with audio buffer + let js_audio_buffer = ctor.new_instance(&[options])?; + let napi_audio_buffer = env.unwrap::(&js_audio_buffer)?; + napi_audio_buffer.populate(audio_buffer); - Ok(js_audio_buffer) + Ok(js_audio_buffer) + }, + ) } -#[js_function(2)] -fn suspend_and(ctx: CallContext) -> Result { - use napi::threadsafe_function::ThreadSafeCallContext; +#[js_function(1)] +fn suspend_offline(ctx: CallContext) -> Result { let js_this = ctx.this_unchecked::(); let napi_obj = ctx.env.unwrap::(&js_this)?; + let clone = napi_obj.0.clone(); let when = match ctx.try_get::(0)? { Either::A(value) => value.get_double()?, Either::B(_) => 0. }; - let js_func = match ctx.try_get::(1)? { - Either::A(value) => value, - Either::B(_) => todo!(), - }; - - println!("suspend_and {}", when); - - let tsfn = ctx.env.create_threadsafe_function(&js_func, 0, |ctx: ThreadSafeCallContext<()>| { - Ok(vec![()]) - })?; + ctx.env.execute_tokio_future( + async move { + clone.suspend_at(when).await; + Ok(()) + }, + |&mut env, _val| { + env.get_undefined() + }, + ) +} - napi_obj.0.suspend_at(when, move |_| { - println!("callback runs now"); - // js_func.call_without_args(Some(&js_this)).unwrap(); - tsfn.call(Ok(()), napi::threadsafe_function::ThreadsafeFunctionCallMode::Blocking); - }); +#[js_function] +fn resume_offline(ctx: CallContext) -> Result { + // use napi::threadsafe_function::ThreadSafeCallContext; + let js_this = ctx.this_unchecked::(); + let napi_obj = ctx.env.unwrap::(&js_this)?; + let clone = napi_obj.0.clone(); - ctx.env.get_undefined() + ctx.env.execute_tokio_future( + async move { + clone.resume().await; + Ok(()) + }, + |&mut env, _val| { + env.get_undefined() + }, + ) } ` } diff --git a/monkey-patch.js b/monkey-patch.js index 11ee20a5..c700595a 100644 --- a/monkey-patch.js +++ b/monkey-patch.js @@ -131,9 +131,9 @@ function patchOfflineAudioContext(nativeBinding) { } // promisify sync APIs - startRendering() { + async startRendering() { try { - const audioBuffer = super.startRendering(); + const audioBuffer = await super.startRendering(); clearTimeout(this.__keepAwakeId); return Promise.resolve(audioBuffer); @@ -142,14 +142,6 @@ function patchOfflineAudioContext(nativeBinding) { } } - suspend() { - return new Promise((resolve, reject) => null); - } - - resume() { - return new Promise((resolve, reject) => null); - } - decodeAudioData(audioData) { if (!audioData instanceof ArrayBuffer) { throw new Error('Invalid argument, please provide an ArrayBuffer'); diff --git a/src/audio_context.rs b/src/audio_context.rs index a87ed201..3853cf79 100644 --- a/src/audio_context.rs +++ b/src/audio_context.rs @@ -18,6 +18,7 @@ // -------------------------------------------------------------------------- // use std::io::Cursor; +use std::sync::Arc; use napi::*; use napi_derive::js_function; @@ -25,7 +26,7 @@ use web_audio_api::context::*; use crate::*; -pub(crate) struct NapiAudioContext(AudioContext); +pub(crate) struct NapiAudioContext(Arc); impl NapiAudioContext { pub fn create_js_class(env: &Env) -> Result { @@ -143,7 +144,7 @@ fn constructor(ctx: CallContext) -> Result { // ------------------------------------------------- // Wrap context // ------------------------------------------------- - let napi_audio_context = NapiAudioContext(audio_context); + let napi_audio_context = NapiAudioContext(Arc::new(audio_context)); ctx.env.wrap(&mut js_this, napi_audio_context)?; js_this.define_properties(&[ diff --git a/src/offline_audio_context.rs b/src/offline_audio_context.rs index 494c97d7..9e4d9a83 100644 --- a/src/offline_audio_context.rs +++ b/src/offline_audio_context.rs @@ -18,6 +18,7 @@ // -------------------------------------------------------------------------- // use std::io::Cursor; +use std::sync::Arc; use napi::*; use napi_derive::js_function; @@ -25,7 +26,7 @@ use web_audio_api::context::*; use crate::*; -pub(crate) struct NapiOfflineAudioContext(OfflineAudioContext); +pub(crate) struct NapiOfflineAudioContext(Arc); impl NapiOfflineAudioContext { pub fn create_js_class(env: &Env) -> Result { @@ -62,7 +63,8 @@ impl NapiOfflineAudioContext { // ---------------------------------------------------- Property::new("length")?.with_getter(get_length), Property::new("startRendering")?.with_method(start_rendering), - Property::new("suspend_and")?.with_method(suspend_and), + Property::new("suspend")?.with_method(suspend_offline), + Property::new("resume")?.with_method(resume_offline), ], ) } @@ -88,7 +90,7 @@ fn constructor(ctx: CallContext) -> Result { // ------------------------------------------------- // Wrap context // ------------------------------------------------- - let napi_audio_context = NapiOfflineAudioContext(audio_context); + let napi_audio_context = NapiOfflineAudioContext(Arc::new(audio_context)); ctx.env.wrap(&mut js_this, napi_audio_context)?; js_this.define_properties(&[ @@ -455,60 +457,63 @@ fn start_rendering(ctx: CallContext) -> Result { let js_this = ctx.this_unchecked::(); let napi_obj = ctx.env.unwrap::(&js_this)?; - let audio_buffer = napi_obj.0.start_rendering_sync(); + let clone = napi_obj.0.clone(); - // create js audio buffer instance - let store_ref: &mut napi::Ref<()> = ctx.env.get_instance_data()?.unwrap(); - let store: JsObject = ctx.env.get_reference_value(store_ref)?; - let ctor: JsFunction = store.get_named_property("AudioBuffer")?; - let mut options = ctx.env.create_object()?; - options.set("__internal_caller__", ctx.env.get_null())?; - - // populate with audio buffer - let js_audio_buffer = ctor.new_instance(&[options])?; - let napi_audio_buffer = ctx.env.unwrap::(&js_audio_buffer)?; - napi_audio_buffer.populate(audio_buffer); + ctx.env.execute_tokio_future( + async move { + let audio_buffer = clone.start_rendering().await; + Ok(audio_buffer) + }, + |&mut env, audio_buffer| { + // create js audio buffer instance + let store_ref: &mut napi::Ref<()> = env.get_instance_data()?.unwrap(); + let store: JsObject = env.get_reference_value(store_ref)?; + let ctor: JsFunction = store.get_named_property("AudioBuffer")?; + // this should be cleaned + let mut options = env.create_object()?; + options.set("__internal_caller__", env.get_null())?; + // populate with audio buffer + let js_audio_buffer = ctor.new_instance(&[options])?; + let napi_audio_buffer = env.unwrap::(&js_audio_buffer)?; + napi_audio_buffer.populate(audio_buffer); - Ok(js_audio_buffer) + Ok(js_audio_buffer) + }, + ) } -#[js_function(2)] -fn suspend_and(ctx: CallContext) -> Result { - use napi::threadsafe_function::ThreadSafeCallContext; +#[js_function(1)] +fn suspend_offline(ctx: CallContext) -> Result { let js_this = ctx.this_unchecked::(); let napi_obj = ctx.env.unwrap::(&js_this)?; + let clone = napi_obj.0.clone(); let when = match ctx.try_get::(0)? { Either::A(value) => value.get_double()?, Either::B(_) => 0., }; - let js_func = match ctx.try_get::(1)? { - Either::A(value) => value, - Either::B(_) => todo!(), - }; - - println!("suspend_and {}", when); - - let tsfn = - ctx.env - .create_threadsafe_function(&js_func, 0, |ctx: ThreadSafeCallContext<()>| { - Ok(vec![()]) - })?; - - napi_obj.0.suspend_at(when, move |_| { - println!("callback runs now"); - // js_func.call_without_args(Some(&js_this)).unwrap(); - tsfn.call( - Ok(()), - napi::threadsafe_function::ThreadsafeFunctionCallMode::Blocking, - ); - }); - - // let tsfn_cloned = tsfn.clone(); - - // let js_func_ref = ctx.env.create_reference(js_func)?; - // let js_func: JsFunction = ctx.env.get_reference_value(&js_func_ref).unwrap(); + ctx.env.execute_tokio_future( + async move { + clone.suspend_at(when).await; + Ok(()) + }, + |&mut env, _val| env.get_undefined(), + ) +} - ctx.env.get_undefined() +#[js_function] +fn resume_offline(ctx: CallContext) -> Result { + // use napi::threadsafe_function::ThreadSafeCallContext; + let js_this = ctx.this_unchecked::(); + let napi_obj = ctx.env.unwrap::(&js_this)?; + let clone = napi_obj.0.clone(); + + ctx.env.execute_tokio_future( + async move { + clone.resume().await; + Ok(()) + }, + |&mut env, _val| env.get_undefined(), + ) } From 17770ef220be1d4d2777ef03d6c07795c7b92e86 Mon Sep 17 00:00:00 2001 From: b-ma Date: Fri, 22 Dec 2023 07:59:50 +0100 Subject: [PATCH 26/34] refactor: cleaning --- examples/offline.mjs | 3 +-- generator/templates/audio_context.tmpl.rs | 2 -- src/offline_audio_context.rs | 2 -- 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/examples/offline.mjs b/examples/offline.mjs index 25e8c4b9..ab5a1cb5 100644 --- a/examples/offline.mjs +++ b/examples/offline.mjs @@ -21,9 +21,8 @@ console.log("buffer duration:", buffer.duration); const channelData = buffer.getChannelData(0); for (let i = 0; i < 48000; i++) { - // we check + // before suspend the graph is empty if (i < 128) { - // before suspend the graph is empty if (channelData[i] !== 0) { throw new Error('should be zero') } diff --git a/generator/templates/audio_context.tmpl.rs b/generator/templates/audio_context.tmpl.rs index 487a8565..d2f81055 100644 --- a/generator/templates/audio_context.tmpl.rs +++ b/generator/templates/audio_context.tmpl.rs @@ -454,7 +454,6 @@ fn get_length(ctx: CallContext) -> Result { fn start_rendering(ctx: CallContext) -> Result { let js_this = ctx.this_unchecked::(); let napi_obj = ctx.env.unwrap::<${d.napiName(d.node)}>(&js_this)?; - let clone = napi_obj.0.clone(); ctx.env.execute_tokio_future( @@ -504,7 +503,6 @@ fn suspend_offline(ctx: CallContext) -> Result { #[js_function] fn resume_offline(ctx: CallContext) -> Result { - // use napi::threadsafe_function::ThreadSafeCallContext; let js_this = ctx.this_unchecked::(); let napi_obj = ctx.env.unwrap::(&js_this)?; let clone = napi_obj.0.clone(); diff --git a/src/offline_audio_context.rs b/src/offline_audio_context.rs index aff829fe..4624787b 100644 --- a/src/offline_audio_context.rs +++ b/src/offline_audio_context.rs @@ -456,7 +456,6 @@ fn get_length(ctx: CallContext) -> Result { fn start_rendering(ctx: CallContext) -> Result { let js_this = ctx.this_unchecked::(); let napi_obj = ctx.env.unwrap::(&js_this)?; - let clone = napi_obj.0.clone(); ctx.env.execute_tokio_future( @@ -504,7 +503,6 @@ fn suspend_offline(ctx: CallContext) -> Result { #[js_function] fn resume_offline(ctx: CallContext) -> Result { - // use napi::threadsafe_function::ThreadSafeCallContext; let js_this = ctx.this_unchecked::(); let napi_obj = ctx.env.unwrap::(&js_this)?; let clone = napi_obj.0.clone(); From 56ff66e758d9de07cbe439638307322e44ba3901 Mon Sep 17 00:00:00 2001 From: b-ma Date: Fri, 22 Dec 2023 08:12:21 +0100 Subject: [PATCH 27/34] fmt --- generator/templates/audio_context.tmpl.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/generator/templates/audio_context.tmpl.rs b/generator/templates/audio_context.tmpl.rs index d2f81055..ab86a07d 100644 --- a/generator/templates/audio_context.tmpl.rs +++ b/generator/templates/audio_context.tmpl.rs @@ -495,9 +495,7 @@ fn suspend_offline(ctx: CallContext) -> Result { clone.suspend_at(when).await; Ok(()) }, - |&mut env, _val| { - env.get_undefined() - }, + |&mut env, _val| env.get_undefined(), ) } @@ -512,9 +510,7 @@ fn resume_offline(ctx: CallContext) -> Result { clone.resume().await; Ok(()) }, - |&mut env, _val| { - env.get_undefined() - }, + |&mut env, _val| env.get_undefined(), ) } ` From df44e9fa56af37e577b457f597c767f6f865da79 Mon Sep 17 00:00:00 2001 From: Otto Date: Wed, 27 Dec 2023 09:25:18 +0100 Subject: [PATCH 28/34] OfflineAudioContext renamed suspend_at -> suspend --- generator/templates/audio_context.tmpl.rs | 2 +- src/offline_audio_context.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/generator/templates/audio_context.tmpl.rs b/generator/templates/audio_context.tmpl.rs index ab86a07d..391ad0b7 100644 --- a/generator/templates/audio_context.tmpl.rs +++ b/generator/templates/audio_context.tmpl.rs @@ -492,7 +492,7 @@ fn suspend_offline(ctx: CallContext) -> Result { ctx.env.execute_tokio_future( async move { - clone.suspend_at(when).await; + clone.suspend(when).await; Ok(()) }, |&mut env, _val| env.get_undefined(), diff --git a/src/offline_audio_context.rs b/src/offline_audio_context.rs index 4624787b..334f11a4 100644 --- a/src/offline_audio_context.rs +++ b/src/offline_audio_context.rs @@ -494,7 +494,7 @@ fn suspend_offline(ctx: CallContext) -> Result { ctx.env.execute_tokio_future( async move { - clone.suspend_at(when).await; + clone.suspend(when).await; Ok(()) }, |&mut env, _val| env.get_undefined(), From c24af51c913f3b444dab3ee774ee790c029072db Mon Sep 17 00:00:00 2001 From: Otto Date: Wed, 27 Dec 2023 09:38:29 +0100 Subject: [PATCH 29/34] Implement deprecated AudioListener setPosition, setOrientation to prevent a wpt crash --- src/audio_listener.rs | 65 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 2 deletions(-) diff --git a/src/audio_listener.rs b/src/audio_listener.rs index aaea696d..1e109583 100644 --- a/src/audio_listener.rs +++ b/src/audio_listener.rs @@ -8,14 +8,24 @@ pub(crate) struct NapiAudioListener(AudioListener); impl NapiAudioListener { pub fn create_js_class(env: &Env) -> Result { - env.define_class("AudioListener", constructor, &[]) + env.define_class( + "AudioListener", + constructor, + &[ + Property::new("setPosition")?.with_method(set_position), + Property::new("setOrientation")?.with_method(set_orientation), + ], + ) + } + + pub fn unwrap(&mut self) -> &mut AudioListener { + &mut self.0 } } // https://webaudio.github.io/web-audio-api/#AudioListener // // @note: should be a private constructor -// #todo: implement deprecateds methods: `setOrientation` and `setPosition` #[js_function(1)] fn constructor(ctx: CallContext) -> Result { let mut js_this = ctx.this_unchecked::(); @@ -112,3 +122,54 @@ fn constructor(ctx: CallContext) -> Result { ctx.env.get_undefined() } + +#[js_function(3)] +fn set_position(ctx: CallContext) -> Result { + // TODO https://webaudio.github.io/web-audio-api/#dom-audiolistener-setposition + // + // When any of the positionX, positionY, and positionZ AudioParams for this AudioListener have + // an automation curve set using setValueCurveAtTime() at the time this method is called, a + // NotSupportedError MUST be thrown. + let js_this = ctx.this_unchecked::(); + let napi_node = ctx.env.unwrap::(&js_this)?; + let node = napi_node.unwrap(); + + let x = ctx.get::(0)?.get_double()? as f32; + let y = ctx.get::(1)?.get_double()? as f32; + let z = ctx.get::(2)?.get_double()? as f32; + + node.position_x().set_value(x); + node.position_y().set_value(y); + node.position_z().set_value(z); + + ctx.env.get_undefined() +} + +#[js_function(6)] +fn set_orientation(ctx: CallContext) -> Result { + // TODO https://webaudio.github.io/web-audio-api/#dom-audiolistener-setorientation + // + // If any of the forwardX, forwardY, forwardZ, upX, upY and upZ AudioParams have an automation + // curve set using setValueCurveAtTime() at the time this method is called, a NotSupportedError + // MUST be thrown. + let js_this = ctx.this_unchecked::(); + let napi_node = ctx.env.unwrap::(&js_this)?; + let node = napi_node.unwrap(); + + let x_forward = ctx.get::(0)?.get_double()? as f32; + let y_forward = ctx.get::(1)?.get_double()? as f32; + let z_forward = ctx.get::(2)?.get_double()? as f32; + let x_up = ctx.get::(3)?.get_double()? as f32; + let y_up = ctx.get::(4)?.get_double()? as f32; + let z_up = ctx.get::(5)?.get_double()? as f32; + + node.forward_x().set_value(x_forward); + node.forward_y().set_value(y_forward); + node.forward_z().set_value(z_forward); + + node.up_x().set_value(x_up); + node.up_y().set_value(y_up); + node.up_z().set_value(z_up); + + ctx.env.get_undefined() +} From ca9014e2e32534311f51fc7728ea2dbddd820c32 Mon Sep 17 00:00:00 2001 From: Otto Date: Wed, 27 Dec 2023 09:54:00 +0100 Subject: [PATCH 30/34] Update the wpt submodule to my own fork for now You may need to run `git submodule sync --recursive` --- .gitmodules | 2 +- wpt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index e67814bd..89a825b1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "wpt"] path = wpt - url = git@github.com:web-platform-tests/wpt.git + url = git@github.com:orottier/wpt.git diff --git a/wpt b/wpt index 83f318a1..d99b032d 160000 --- a/wpt +++ b/wpt @@ -1 +1 @@ -Subproject commit 83f318a105a1a9c58a0c7bff65607d753d3ad9ad +Subproject commit d99b032d98eb39b2222e9b824a5f6cdff28c883f From 2633aede3eadbc9a1ecf2a3fb3adfe213dde4858 Mon Sep 17 00:00:00 2001 From: Otto Date: Wed, 27 Dec 2023 10:11:46 +0100 Subject: [PATCH 31/34] Upgrade to web-audio-api-rs 0.39 and reinstate lto setting --- Cargo.toml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4b78fb57..26dc0f34 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,8 +12,8 @@ crate-type = ["cdylib"] [dependencies] napi = {version="2.13", features=["napi6", "tokio_rt"]} napi-derive = "2.13" -# web-audio-api = "0.38" -web-audio-api = { path = "../web-audio-api-rs" } +web-audio-api = "0.39" +# web-audio-api = { path = "../web-audio-api-rs" } [target.'cfg(all(any(windows, unix), target_arch = "x86_64", not(target_env = "musl")))'.dependencies] mimalloc = {version = "0.1"} @@ -22,8 +22,7 @@ mimalloc = {version = "0.1"} napi-build = "1" [profile.release] -lto = false -debug = true +lto = true [features] jack = ["web-audio-api/cpal-jack"] From edb34411a3f370d705813f61ba652c37837e2ef8 Mon Sep 17 00:00:00 2001 From: b-ma Date: Thu, 28 Dec 2023 09:11:17 +0100 Subject: [PATCH 32/34] feat: implement all audio param attributes --- generator/templates/audio_param.tmpl.rs | 79 ++++++++++++++++++++++++- src/audio_param.rs | 78 +++++++++++++++++++++++- tests/AudioParam.spec.mjs | 28 +++++++++ 3 files changed, 182 insertions(+), 3 deletions(-) create mode 100644 tests/AudioParam.spec.mjs diff --git a/generator/templates/audio_param.tmpl.rs b/generator/templates/audio_param.tmpl.rs index af14cbe5..9db1b621 100644 --- a/generator/templates/audio_param.tmpl.rs +++ b/generator/templates/audio_param.tmpl.rs @@ -1,6 +1,6 @@ use napi::*; use napi_derive::js_function; -use web_audio_api::AudioParam; +use web_audio_api::{AudioParam, AutomationRate}; pub(crate) struct NapiAudioParam(AudioParam); @@ -16,10 +16,20 @@ impl NapiAudioParam { Property::new("Symbol.toStringTag")? .with_value(&env.create_string("AudioParam")?) .with_property_attributes(PropertyAttributes::Static), - + // Attributes + Property::new("automationRate")? + .with_getter(get_automation_rate) + .with_setter(set_automation_rate), + Property::new("defaultValue")? + .with_getter(get_default_value), + Property::new("maxValue")? + .with_getter(get_max_value), + Property::new("minValue")? + .with_getter(get_min_value), Property::new("value")? .with_getter(get_value) .with_setter(set_value), + // Methods Property::new("setValueAtTime")?.with_method(set_value_at_time), Property::new("linearRampToValueAtTime")?.with_method(linear_ramp_to_value_at_time), Property::new("exponentialRampToValueAtTime")? @@ -38,6 +48,70 @@ impl NapiAudioParam { } } +// Attributes +#[js_function] +fn get_automation_rate(ctx: CallContext) -> Result { + let js_this = ctx.this_unchecked::(); + let napi_obj = ctx.env.unwrap::(&js_this)?; + let obj = napi_obj.unwrap(); + + let value = obj.automation_rate(); + let value_str = match value { + AutomationRate::A => "a-rate", + AutomationRate::K => "k-rate", + }; + + ctx.env.create_string(value_str) +} + +#[js_function(1)] +fn set_automation_rate(ctx: CallContext) -> Result { + let js_this = ctx.this_unchecked::(); + let napi_obj = ctx.env.unwrap::(&js_this)?; + let obj = napi_obj.unwrap(); + + let js_str = ctx.get::(0)?; + let utf8_str = js_str.into_utf8()?.into_owned()?; + let value = match utf8_str.as_str() { + "a-rate" => AutomationRate::A, + "k-rate" => AutomationRate::K, + _ => panic!("The provided value '{:?}' is not a valid enum value of type AutomationRate.", utf8_str), + }; + obj.set_automation_rate(value); + + ctx.env.get_undefined() +} + +#[js_function] +fn get_default_value(ctx: CallContext) -> Result { + let js_this = ctx.this_unchecked::(); + let napi_obj = ctx.env.unwrap::(&js_this)?; + let obj = napi_obj.unwrap(); + + let value = obj.default_value(); + ctx.env.create_double(value as f64) +} + +#[js_function] +fn get_max_value(ctx: CallContext) -> Result { + let js_this = ctx.this_unchecked::(); + let napi_obj = ctx.env.unwrap::(&js_this)?; + let obj = napi_obj.unwrap(); + + let value = obj.max_value(); + ctx.env.create_double(value as f64) +} + +#[js_function] +fn get_min_value(ctx: CallContext) -> Result { + let js_this = ctx.this_unchecked::(); + let napi_obj = ctx.env.unwrap::(&js_this)?; + let obj = napi_obj.unwrap(); + + let value = obj.min_value(); + ctx.env.create_double(value as f64) +} + #[js_function] fn get_value(ctx: CallContext) -> Result { let js_this = ctx.this_unchecked::(); @@ -60,6 +134,7 @@ fn set_value(ctx: CallContext) -> Result { ctx.env.get_undefined() } +// Methods #[js_function(2)] fn set_value_at_time(ctx: CallContext) -> Result { let js_this = ctx.this_unchecked::(); diff --git a/src/audio_param.rs b/src/audio_param.rs index 163476ec..58a7dbe0 100644 --- a/src/audio_param.rs +++ b/src/audio_param.rs @@ -19,7 +19,7 @@ use napi::*; use napi_derive::js_function; -use web_audio_api::AudioParam; +use web_audio_api::{AudioParam, AutomationRate}; pub(crate) struct NapiAudioParam(AudioParam); @@ -35,9 +35,17 @@ impl NapiAudioParam { Property::new("Symbol.toStringTag")? .with_value(&env.create_string("AudioParam")?) .with_property_attributes(PropertyAttributes::Static), + // Attributes + Property::new("automationRate")? + .with_getter(get_automation_rate) + .with_setter(set_automation_rate), + Property::new("defaultValue")?.with_getter(get_default_value), + Property::new("maxValue")?.with_getter(get_max_value), + Property::new("minValue")?.with_getter(get_min_value), Property::new("value")? .with_getter(get_value) .with_setter(set_value), + // Methods Property::new("setValueAtTime")?.with_method(set_value_at_time), Property::new("linearRampToValueAtTime")?.with_method(linear_ramp_to_value_at_time), Property::new("exponentialRampToValueAtTime")? @@ -56,6 +64,73 @@ impl NapiAudioParam { } } +// Attributes +#[js_function] +fn get_automation_rate(ctx: CallContext) -> Result { + let js_this = ctx.this_unchecked::(); + let napi_obj = ctx.env.unwrap::(&js_this)?; + let obj = napi_obj.unwrap(); + + let value = obj.automation_rate(); + let value_str = match value { + AutomationRate::A => "a-rate", + AutomationRate::K => "k-rate", + }; + + ctx.env.create_string(value_str) +} + +#[js_function(1)] +fn set_automation_rate(ctx: CallContext) -> Result { + let js_this = ctx.this_unchecked::(); + let napi_obj = ctx.env.unwrap::(&js_this)?; + let obj = napi_obj.unwrap(); + + let js_str = ctx.get::(0)?; + let utf8_str = js_str.into_utf8()?.into_owned()?; + let value = match utf8_str.as_str() { + "a-rate" => AutomationRate::A, + "k-rate" => AutomationRate::K, + _ => panic!( + "The provided value '{:?}' is not a valid enum value of type AutomationRate.", + utf8_str + ), + }; + obj.set_automation_rate(value); + + ctx.env.get_undefined() +} + +#[js_function] +fn get_default_value(ctx: CallContext) -> Result { + let js_this = ctx.this_unchecked::(); + let napi_obj = ctx.env.unwrap::(&js_this)?; + let obj = napi_obj.unwrap(); + + let value = obj.default_value(); + ctx.env.create_double(value as f64) +} + +#[js_function] +fn get_max_value(ctx: CallContext) -> Result { + let js_this = ctx.this_unchecked::(); + let napi_obj = ctx.env.unwrap::(&js_this)?; + let obj = napi_obj.unwrap(); + + let value = obj.max_value(); + ctx.env.create_double(value as f64) +} + +#[js_function] +fn get_min_value(ctx: CallContext) -> Result { + let js_this = ctx.this_unchecked::(); + let napi_obj = ctx.env.unwrap::(&js_this)?; + let obj = napi_obj.unwrap(); + + let value = obj.min_value(); + ctx.env.create_double(value as f64) +} + #[js_function] fn get_value(ctx: CallContext) -> Result { let js_this = ctx.this_unchecked::(); @@ -78,6 +153,7 @@ fn set_value(ctx: CallContext) -> Result { ctx.env.get_undefined() } +// Methods #[js_function(2)] fn set_value_at_time(ctx: CallContext) -> Result { let js_this = ctx.this_unchecked::(); diff --git a/tests/AudioParam.spec.mjs b/tests/AudioParam.spec.mjs new file mode 100644 index 00000000..f764ad4a --- /dev/null +++ b/tests/AudioParam.spec.mjs @@ -0,0 +1,28 @@ +import { assert } from 'chai'; +import { AudioContext } from '../index.mjs'; + +describe('# AudioBuffer', () => { + let audioContext; + + beforeEach(() => { + audioContext = new AudioContext(); + }); + + afterEach(() => { + audioContext.close(); + }); + + describe('attributes', () => { + it(`should implement all attributes`, () => { + const gain = audioContext.createGain(); + + assert.equal(gain.gain.automationRate, 'a-rate'); + assert.equal(gain.gain.defaultValue, 1); + // should accept some delta + assert.equal(gain.gain.maxValue, 3.4028234663852886e+38); + assert.equal(gain.gain.minValue, -3.4028234663852886e+38); + + assert.equal(gain.gain.value, 1); + }); + }); +}); From fd4d71085714700946e1f0a5e8ecddfe42d134a2 Mon Sep 17 00:00:00 2001 From: b-ma Date: Thu, 28 Dec 2023 09:11:29 +0100 Subject: [PATCH 33/34] fix: typo --- generator/templates/audio_nodes.tmpl.rs | 12 ++++++------ src/analyser_node.rs | 8 ++++---- src/audio_buffer_source_node.rs | 8 ++++---- src/biquad_filter_node.rs | 12 ++++++------ src/channel_merger_node.rs | 8 ++++---- src/channel_splitter_node.rs | 8 ++++---- src/constant_source_node.rs | 8 ++++---- src/convolver_node.rs | 8 ++++---- src/delay_node.rs | 8 ++++---- src/dynamics_compressor_node.rs | 8 ++++---- src/gain_node.rs | 8 ++++---- src/iir_filter_node.rs | 8 ++++---- src/oscillator_node.rs | 12 ++++++------ src/panner_node.rs | 16 ++++++++-------- src/stereo_panner_node.rs | 8 ++++---- src/wave_shaper_node.rs | 12 ++++++------ wpt | 2 +- 17 files changed, 77 insertions(+), 77 deletions(-) diff --git a/generator/templates/audio_nodes.tmpl.rs b/generator/templates/audio_nodes.tmpl.rs index 75b4b6bd..20d01ec6 100644 --- a/generator/templates/audio_nodes.tmpl.rs +++ b/generator/templates/audio_nodes.tmpl.rs @@ -384,8 +384,8 @@ fn set_channel_count_mode(ctx: CallContext) -> Result { let node = napi_node.unwrap(); let js_str = ctx.get::(0)?; - let uf8_str = js_str.into_utf8()?.into_owned()?; - let value = match uf8_str.as_str() { + let utf8_str = js_str.into_utf8()?.into_owned()?; + let value = match utf8_str.as_str() { "max" => ChannelCountMode::Max, "clamped-max" => ChannelCountMode::ClampedMax, "explicit" => ChannelCountMode::Explicit, @@ -418,8 +418,8 @@ fn set_channel_interpretation(ctx: CallContext) -> Result { let node = napi_node.unwrap(); let js_str = ctx.get::(0)?; - let uf8_str = js_str.into_utf8()?.into_owned()?; - let value = match uf8_str.as_str() { + let utf8_str = js_str.into_utf8()?.into_owned()?; + let value = match utf8_str.as_str() { "speakers" => ChannelInterpretation::Speakers, "discrete" => ChannelInterpretation::Discrete, _ => panic!("undefined value for ChannelInterpretation"), @@ -755,8 +755,8 @@ fn set_${d.slug(attr)}(ctx: CallContext) -> Result { let node = napi_node.unwrap(); let js_str = ctx.get::(0)?; - let uf8_str = js_str.into_utf8()?.into_owned()?; - let value = match uf8_str.as_str() {${idl.values.map(v => ` + let utf8_str = js_str.into_utf8()?.into_owned()?; + let value = match utf8_str.as_str() {${idl.values.map(v => ` "${v.value}" => ${idl.name}::${d.camelcase(v.value)},`).join('')} _ => panic!("undefined value for ${idl.name}"), }; diff --git a/src/analyser_node.rs b/src/analyser_node.rs index 53fb77de..ec5d70aa 100644 --- a/src/analyser_node.rs +++ b/src/analyser_node.rs @@ -282,8 +282,8 @@ fn set_channel_count_mode(ctx: CallContext) -> Result { let node = napi_node.unwrap(); let js_str = ctx.get::(0)?; - let uf8_str = js_str.into_utf8()?.into_owned()?; - let value = match uf8_str.as_str() { + let utf8_str = js_str.into_utf8()?.into_owned()?; + let value = match utf8_str.as_str() { "max" => ChannelCountMode::Max, "clamped-max" => ChannelCountMode::ClampedMax, "explicit" => ChannelCountMode::Explicit, @@ -316,8 +316,8 @@ fn set_channel_interpretation(ctx: CallContext) -> Result { let node = napi_node.unwrap(); let js_str = ctx.get::(0)?; - let uf8_str = js_str.into_utf8()?.into_owned()?; - let value = match uf8_str.as_str() { + let utf8_str = js_str.into_utf8()?.into_owned()?; + let value = match utf8_str.as_str() { "speakers" => ChannelInterpretation::Speakers, "discrete" => ChannelInterpretation::Discrete, _ => panic!("undefined value for ChannelInterpretation"), diff --git a/src/audio_buffer_source_node.rs b/src/audio_buffer_source_node.rs index 086004a6..8b53b01e 100644 --- a/src/audio_buffer_source_node.rs +++ b/src/audio_buffer_source_node.rs @@ -257,8 +257,8 @@ fn set_channel_count_mode(ctx: CallContext) -> Result { let node = napi_node.unwrap(); let js_str = ctx.get::(0)?; - let uf8_str = js_str.into_utf8()?.into_owned()?; - let value = match uf8_str.as_str() { + let utf8_str = js_str.into_utf8()?.into_owned()?; + let value = match utf8_str.as_str() { "max" => ChannelCountMode::Max, "clamped-max" => ChannelCountMode::ClampedMax, "explicit" => ChannelCountMode::Explicit, @@ -291,8 +291,8 @@ fn set_channel_interpretation(ctx: CallContext) -> Result { let node = napi_node.unwrap(); let js_str = ctx.get::(0)?; - let uf8_str = js_str.into_utf8()?.into_owned()?; - let value = match uf8_str.as_str() { + let utf8_str = js_str.into_utf8()?.into_owned()?; + let value = match utf8_str.as_str() { "speakers" => ChannelInterpretation::Speakers, "discrete" => ChannelInterpretation::Discrete, _ => panic!("undefined value for ChannelInterpretation"), diff --git a/src/biquad_filter_node.rs b/src/biquad_filter_node.rs index f099bd67..cb9bab05 100644 --- a/src/biquad_filter_node.rs +++ b/src/biquad_filter_node.rs @@ -304,8 +304,8 @@ fn set_channel_count_mode(ctx: CallContext) -> Result { let node = napi_node.unwrap(); let js_str = ctx.get::(0)?; - let uf8_str = js_str.into_utf8()?.into_owned()?; - let value = match uf8_str.as_str() { + let utf8_str = js_str.into_utf8()?.into_owned()?; + let value = match utf8_str.as_str() { "max" => ChannelCountMode::Max, "clamped-max" => ChannelCountMode::ClampedMax, "explicit" => ChannelCountMode::Explicit, @@ -338,8 +338,8 @@ fn set_channel_interpretation(ctx: CallContext) -> Result { let node = napi_node.unwrap(); let js_str = ctx.get::(0)?; - let uf8_str = js_str.into_utf8()?.into_owned()?; - let value = match uf8_str.as_str() { + let utf8_str = js_str.into_utf8()?.into_owned()?; + let value = match utf8_str.as_str() { "speakers" => ChannelInterpretation::Speakers, "discrete" => ChannelInterpretation::Discrete, _ => panic!("undefined value for ChannelInterpretation"), @@ -395,8 +395,8 @@ fn set_type(ctx: CallContext) -> Result { let node = napi_node.unwrap(); let js_str = ctx.get::(0)?; - let uf8_str = js_str.into_utf8()?.into_owned()?; - let value = match uf8_str.as_str() { + let utf8_str = js_str.into_utf8()?.into_owned()?; + let value = match utf8_str.as_str() { "lowpass" => BiquadFilterType::Lowpass, "highpass" => BiquadFilterType::Highpass, "bandpass" => BiquadFilterType::Bandpass, diff --git a/src/channel_merger_node.rs b/src/channel_merger_node.rs index d691a0ef..5cac4e3e 100644 --- a/src/channel_merger_node.rs +++ b/src/channel_merger_node.rs @@ -227,8 +227,8 @@ fn set_channel_count_mode(ctx: CallContext) -> Result { let node = napi_node.unwrap(); let js_str = ctx.get::(0)?; - let uf8_str = js_str.into_utf8()?.into_owned()?; - let value = match uf8_str.as_str() { + let utf8_str = js_str.into_utf8()?.into_owned()?; + let value = match utf8_str.as_str() { "max" => ChannelCountMode::Max, "clamped-max" => ChannelCountMode::ClampedMax, "explicit" => ChannelCountMode::Explicit, @@ -261,8 +261,8 @@ fn set_channel_interpretation(ctx: CallContext) -> Result { let node = napi_node.unwrap(); let js_str = ctx.get::(0)?; - let uf8_str = js_str.into_utf8()?.into_owned()?; - let value = match uf8_str.as_str() { + let utf8_str = js_str.into_utf8()?.into_owned()?; + let value = match utf8_str.as_str() { "speakers" => ChannelInterpretation::Speakers, "discrete" => ChannelInterpretation::Discrete, _ => panic!("undefined value for ChannelInterpretation"), diff --git a/src/channel_splitter_node.rs b/src/channel_splitter_node.rs index b65a46dd..06e5c0e5 100644 --- a/src/channel_splitter_node.rs +++ b/src/channel_splitter_node.rs @@ -227,8 +227,8 @@ fn set_channel_count_mode(ctx: CallContext) -> Result { let node = napi_node.unwrap(); let js_str = ctx.get::(0)?; - let uf8_str = js_str.into_utf8()?.into_owned()?; - let value = match uf8_str.as_str() { + let utf8_str = js_str.into_utf8()?.into_owned()?; + let value = match utf8_str.as_str() { "max" => ChannelCountMode::Max, "clamped-max" => ChannelCountMode::ClampedMax, "explicit" => ChannelCountMode::Explicit, @@ -261,8 +261,8 @@ fn set_channel_interpretation(ctx: CallContext) -> Result { let node = napi_node.unwrap(); let js_str = ctx.get::(0)?; - let uf8_str = js_str.into_utf8()?.into_owned()?; - let value = match uf8_str.as_str() { + let utf8_str = js_str.into_utf8()?.into_owned()?; + let value = match utf8_str.as_str() { "speakers" => ChannelInterpretation::Speakers, "discrete" => ChannelInterpretation::Discrete, _ => panic!("undefined value for ChannelInterpretation"), diff --git a/src/constant_source_node.rs b/src/constant_source_node.rs index b9552fdc..d2ad91ac 100644 --- a/src/constant_source_node.rs +++ b/src/constant_source_node.rs @@ -192,8 +192,8 @@ fn set_channel_count_mode(ctx: CallContext) -> Result { let node = napi_node.unwrap(); let js_str = ctx.get::(0)?; - let uf8_str = js_str.into_utf8()?.into_owned()?; - let value = match uf8_str.as_str() { + let utf8_str = js_str.into_utf8()?.into_owned()?; + let value = match utf8_str.as_str() { "max" => ChannelCountMode::Max, "clamped-max" => ChannelCountMode::ClampedMax, "explicit" => ChannelCountMode::Explicit, @@ -226,8 +226,8 @@ fn set_channel_interpretation(ctx: CallContext) -> Result { let node = napi_node.unwrap(); let js_str = ctx.get::(0)?; - let uf8_str = js_str.into_utf8()?.into_owned()?; - let value = match uf8_str.as_str() { + let utf8_str = js_str.into_utf8()?.into_owned()?; + let value = match utf8_str.as_str() { "speakers" => ChannelInterpretation::Speakers, "discrete" => ChannelInterpretation::Discrete, _ => panic!("undefined value for ChannelInterpretation"), diff --git a/src/convolver_node.rs b/src/convolver_node.rs index 60ead53b..f7102fa3 100644 --- a/src/convolver_node.rs +++ b/src/convolver_node.rs @@ -245,8 +245,8 @@ fn set_channel_count_mode(ctx: CallContext) -> Result { let node = napi_node.unwrap(); let js_str = ctx.get::(0)?; - let uf8_str = js_str.into_utf8()?.into_owned()?; - let value = match uf8_str.as_str() { + let utf8_str = js_str.into_utf8()?.into_owned()?; + let value = match utf8_str.as_str() { "max" => ChannelCountMode::Max, "clamped-max" => ChannelCountMode::ClampedMax, "explicit" => ChannelCountMode::Explicit, @@ -279,8 +279,8 @@ fn set_channel_interpretation(ctx: CallContext) -> Result { let node = napi_node.unwrap(); let js_str = ctx.get::(0)?; - let uf8_str = js_str.into_utf8()?.into_owned()?; - let value = match uf8_str.as_str() { + let utf8_str = js_str.into_utf8()?.into_owned()?; + let value = match utf8_str.as_str() { "speakers" => ChannelInterpretation::Speakers, "discrete" => ChannelInterpretation::Discrete, _ => panic!("undefined value for ChannelInterpretation"), diff --git a/src/delay_node.rs b/src/delay_node.rs index 40f628ef..45d51e3d 100644 --- a/src/delay_node.rs +++ b/src/delay_node.rs @@ -241,8 +241,8 @@ fn set_channel_count_mode(ctx: CallContext) -> Result { let node = napi_node.unwrap(); let js_str = ctx.get::(0)?; - let uf8_str = js_str.into_utf8()?.into_owned()?; - let value = match uf8_str.as_str() { + let utf8_str = js_str.into_utf8()?.into_owned()?; + let value = match utf8_str.as_str() { "max" => ChannelCountMode::Max, "clamped-max" => ChannelCountMode::ClampedMax, "explicit" => ChannelCountMode::Explicit, @@ -275,8 +275,8 @@ fn set_channel_interpretation(ctx: CallContext) -> Result { let node = napi_node.unwrap(); let js_str = ctx.get::(0)?; - let uf8_str = js_str.into_utf8()?.into_owned()?; - let value = match uf8_str.as_str() { + let utf8_str = js_str.into_utf8()?.into_owned()?; + let value = match utf8_str.as_str() { "speakers" => ChannelInterpretation::Speakers, "discrete" => ChannelInterpretation::Discrete, _ => panic!("undefined value for ChannelInterpretation"), diff --git a/src/dynamics_compressor_node.rs b/src/dynamics_compressor_node.rs index 60ccd547..7e233cd9 100644 --- a/src/dynamics_compressor_node.rs +++ b/src/dynamics_compressor_node.rs @@ -295,8 +295,8 @@ fn set_channel_count_mode(ctx: CallContext) -> Result { let node = napi_node.unwrap(); let js_str = ctx.get::(0)?; - let uf8_str = js_str.into_utf8()?.into_owned()?; - let value = match uf8_str.as_str() { + let utf8_str = js_str.into_utf8()?.into_owned()?; + let value = match utf8_str.as_str() { "max" => ChannelCountMode::Max, "clamped-max" => ChannelCountMode::ClampedMax, "explicit" => ChannelCountMode::Explicit, @@ -329,8 +329,8 @@ fn set_channel_interpretation(ctx: CallContext) -> Result { let node = napi_node.unwrap(); let js_str = ctx.get::(0)?; - let uf8_str = js_str.into_utf8()?.into_owned()?; - let value = match uf8_str.as_str() { + let utf8_str = js_str.into_utf8()?.into_owned()?; + let value = match utf8_str.as_str() { "speakers" => ChannelInterpretation::Speakers, "discrete" => ChannelInterpretation::Discrete, _ => panic!("undefined value for ChannelInterpretation"), diff --git a/src/gain_node.rs b/src/gain_node.rs index 75698524..cd893d85 100644 --- a/src/gain_node.rs +++ b/src/gain_node.rs @@ -233,8 +233,8 @@ fn set_channel_count_mode(ctx: CallContext) -> Result { let node = napi_node.unwrap(); let js_str = ctx.get::(0)?; - let uf8_str = js_str.into_utf8()?.into_owned()?; - let value = match uf8_str.as_str() { + let utf8_str = js_str.into_utf8()?.into_owned()?; + let value = match utf8_str.as_str() { "max" => ChannelCountMode::Max, "clamped-max" => ChannelCountMode::ClampedMax, "explicit" => ChannelCountMode::Explicit, @@ -267,8 +267,8 @@ fn set_channel_interpretation(ctx: CallContext) -> Result { let node = napi_node.unwrap(); let js_str = ctx.get::(0)?; - let uf8_str = js_str.into_utf8()?.into_owned()?; - let value = match uf8_str.as_str() { + let utf8_str = js_str.into_utf8()?.into_owned()?; + let value = match utf8_str.as_str() { "speakers" => ChannelInterpretation::Speakers, "discrete" => ChannelInterpretation::Discrete, _ => panic!("undefined value for ChannelInterpretation"), diff --git a/src/iir_filter_node.rs b/src/iir_filter_node.rs index 3ebb722b..06ded9f4 100644 --- a/src/iir_filter_node.rs +++ b/src/iir_filter_node.rs @@ -252,8 +252,8 @@ fn set_channel_count_mode(ctx: CallContext) -> Result { let node = napi_node.unwrap(); let js_str = ctx.get::(0)?; - let uf8_str = js_str.into_utf8()?.into_owned()?; - let value = match uf8_str.as_str() { + let utf8_str = js_str.into_utf8()?.into_owned()?; + let value = match utf8_str.as_str() { "max" => ChannelCountMode::Max, "clamped-max" => ChannelCountMode::ClampedMax, "explicit" => ChannelCountMode::Explicit, @@ -286,8 +286,8 @@ fn set_channel_interpretation(ctx: CallContext) -> Result { let node = napi_node.unwrap(); let js_str = ctx.get::(0)?; - let uf8_str = js_str.into_utf8()?.into_owned()?; - let value = match uf8_str.as_str() { + let utf8_str = js_str.into_utf8()?.into_owned()?; + let value = match utf8_str.as_str() { "speakers" => ChannelInterpretation::Speakers, "discrete" => ChannelInterpretation::Discrete, _ => panic!("undefined value for ChannelInterpretation"), diff --git a/src/oscillator_node.rs b/src/oscillator_node.rs index 252112c9..992811d5 100644 --- a/src/oscillator_node.rs +++ b/src/oscillator_node.rs @@ -287,8 +287,8 @@ fn set_channel_count_mode(ctx: CallContext) -> Result { let node = napi_node.unwrap(); let js_str = ctx.get::(0)?; - let uf8_str = js_str.into_utf8()?.into_owned()?; - let value = match uf8_str.as_str() { + let utf8_str = js_str.into_utf8()?.into_owned()?; + let value = match utf8_str.as_str() { "max" => ChannelCountMode::Max, "clamped-max" => ChannelCountMode::ClampedMax, "explicit" => ChannelCountMode::Explicit, @@ -321,8 +321,8 @@ fn set_channel_interpretation(ctx: CallContext) -> Result { let node = napi_node.unwrap(); let js_str = ctx.get::(0)?; - let uf8_str = js_str.into_utf8()?.into_owned()?; - let value = match uf8_str.as_str() { + let utf8_str = js_str.into_utf8()?.into_owned()?; + let value = match utf8_str.as_str() { "speakers" => ChannelInterpretation::Speakers, "discrete" => ChannelInterpretation::Discrete, _ => panic!("undefined value for ChannelInterpretation"), @@ -411,8 +411,8 @@ fn set_type(ctx: CallContext) -> Result { let node = napi_node.unwrap(); let js_str = ctx.get::(0)?; - let uf8_str = js_str.into_utf8()?.into_owned()?; - let value = match uf8_str.as_str() { + let utf8_str = js_str.into_utf8()?.into_owned()?; + let value = match utf8_str.as_str() { "sine" => OscillatorType::Sine, "square" => OscillatorType::Square, "sawtooth" => OscillatorType::Sawtooth, diff --git a/src/panner_node.rs b/src/panner_node.rs index ac1c3697..a5a13bce 100644 --- a/src/panner_node.rs +++ b/src/panner_node.rs @@ -422,8 +422,8 @@ fn set_channel_count_mode(ctx: CallContext) -> Result { let node = napi_node.unwrap(); let js_str = ctx.get::(0)?; - let uf8_str = js_str.into_utf8()?.into_owned()?; - let value = match uf8_str.as_str() { + let utf8_str = js_str.into_utf8()?.into_owned()?; + let value = match utf8_str.as_str() { "max" => ChannelCountMode::Max, "clamped-max" => ChannelCountMode::ClampedMax, "explicit" => ChannelCountMode::Explicit, @@ -456,8 +456,8 @@ fn set_channel_interpretation(ctx: CallContext) -> Result { let node = napi_node.unwrap(); let js_str = ctx.get::(0)?; - let uf8_str = js_str.into_utf8()?.into_owned()?; - let value = match uf8_str.as_str() { + let utf8_str = js_str.into_utf8()?.into_owned()?; + let value = match utf8_str.as_str() { "speakers" => ChannelInterpretation::Speakers, "discrete" => ChannelInterpretation::Discrete, _ => panic!("undefined value for ChannelInterpretation"), @@ -583,8 +583,8 @@ fn set_panning_model(ctx: CallContext) -> Result { let node = napi_node.unwrap(); let js_str = ctx.get::(0)?; - let uf8_str = js_str.into_utf8()?.into_owned()?; - let value = match uf8_str.as_str() { + let utf8_str = js_str.into_utf8()?.into_owned()?; + let value = match utf8_str.as_str() { "equalpower" => PanningModelType::EqualPower, "HRTF" => PanningModelType::HRTF, _ => panic!("undefined value for PanningModelType"), @@ -602,8 +602,8 @@ fn set_distance_model(ctx: CallContext) -> Result { let node = napi_node.unwrap(); let js_str = ctx.get::(0)?; - let uf8_str = js_str.into_utf8()?.into_owned()?; - let value = match uf8_str.as_str() { + let utf8_str = js_str.into_utf8()?.into_owned()?; + let value = match utf8_str.as_str() { "linear" => DistanceModelType::Linear, "inverse" => DistanceModelType::Inverse, "exponential" => DistanceModelType::Exponential, diff --git a/src/stereo_panner_node.rs b/src/stereo_panner_node.rs index fbd260b6..5e1ef5d2 100644 --- a/src/stereo_panner_node.rs +++ b/src/stereo_panner_node.rs @@ -234,8 +234,8 @@ fn set_channel_count_mode(ctx: CallContext) -> Result { let node = napi_node.unwrap(); let js_str = ctx.get::(0)?; - let uf8_str = js_str.into_utf8()?.into_owned()?; - let value = match uf8_str.as_str() { + let utf8_str = js_str.into_utf8()?.into_owned()?; + let value = match utf8_str.as_str() { "max" => ChannelCountMode::Max, "clamped-max" => ChannelCountMode::ClampedMax, "explicit" => ChannelCountMode::Explicit, @@ -268,8 +268,8 @@ fn set_channel_interpretation(ctx: CallContext) -> Result { let node = napi_node.unwrap(); let js_str = ctx.get::(0)?; - let uf8_str = js_str.into_utf8()?.into_owned()?; - let value = match uf8_str.as_str() { + let utf8_str = js_str.into_utf8()?.into_owned()?; + let value = match utf8_str.as_str() { "speakers" => ChannelInterpretation::Speakers, "discrete" => ChannelInterpretation::Discrete, _ => panic!("undefined value for ChannelInterpretation"), diff --git a/src/wave_shaper_node.rs b/src/wave_shaper_node.rs index 8b56d43c..f400a6d3 100644 --- a/src/wave_shaper_node.rs +++ b/src/wave_shaper_node.rs @@ -251,8 +251,8 @@ fn set_channel_count_mode(ctx: CallContext) -> Result { let node = napi_node.unwrap(); let js_str = ctx.get::(0)?; - let uf8_str = js_str.into_utf8()?.into_owned()?; - let value = match uf8_str.as_str() { + let utf8_str = js_str.into_utf8()?.into_owned()?; + let value = match utf8_str.as_str() { "max" => ChannelCountMode::Max, "clamped-max" => ChannelCountMode::ClampedMax, "explicit" => ChannelCountMode::Explicit, @@ -285,8 +285,8 @@ fn set_channel_interpretation(ctx: CallContext) -> Result { let node = napi_node.unwrap(); let js_str = ctx.get::(0)?; - let uf8_str = js_str.into_utf8()?.into_owned()?; - let value = match uf8_str.as_str() { + let utf8_str = js_str.into_utf8()?.into_owned()?; + let value = match utf8_str.as_str() { "speakers" => ChannelInterpretation::Speakers, "discrete" => ChannelInterpretation::Discrete, _ => panic!("undefined value for ChannelInterpretation"), @@ -369,8 +369,8 @@ fn set_oversample(ctx: CallContext) -> Result { let node = napi_node.unwrap(); let js_str = ctx.get::(0)?; - let uf8_str = js_str.into_utf8()?.into_owned()?; - let value = match uf8_str.as_str() { + let utf8_str = js_str.into_utf8()?.into_owned()?; + let value = match utf8_str.as_str() { "none" => OverSampleType::None, "2x" => OverSampleType::X2, "4x" => OverSampleType::X4, diff --git a/wpt b/wpt index d99b032d..83f318a1 160000 --- a/wpt +++ b/wpt @@ -1 +1 @@ -Subproject commit d99b032d98eb39b2222e9b824a5f6cdff28c883f +Subproject commit 83f318a105a1a9c58a0c7bff65607d753d3ad9ad From 1a3e918685dec63568304e054960d8afa58805b8 Mon Sep 17 00:00:00 2001 From: b-ma Date: Thu, 28 Dec 2023 09:52:10 +0100 Subject: [PATCH 34/34] fix: expose OfflineAudioContext::state --- generator/templates/audio_context.tmpl.rs | 49 +++++++++++------------ src/audio_context.rs | 44 ++++++++++---------- src/offline_audio_context.rs | 18 +++++++++ 3 files changed, 62 insertions(+), 49 deletions(-) diff --git a/generator/templates/audio_context.tmpl.rs b/generator/templates/audio_context.tmpl.rs index 391ad0b7..9610f915 100644 --- a/generator/templates/audio_context.tmpl.rs +++ b/generator/templates/audio_context.tmpl.rs @@ -23,6 +23,8 @@ impl ${d.napiName(d.node)} { Property::new("createPeriodicWave")?.with_method(create_periodic_wave), Property::new("createBuffer")?.with_method(create_buffer), + Property::new("state")?.with_getter(get_state), + // ---------------------------------------------------- // Factory methods // ---------------------------------------------------- @@ -35,12 +37,6 @@ impl ${d.napiName(d.node)} { ${d.name(d.node) === 'AudioContext' ? ` - // @todo - expose in OfflineAudioContext as well - Property::new("state")?.with_getter(get_state), - Property::new("resume")?.with_method(resume), - Property::new("suspend")?.with_method(suspend), - Property::new("close")?.with_method(close), - // ---------------------------------------------------- // Methods and attributes specific to AudioContext // ---------------------------------------------------- @@ -48,12 +44,17 @@ impl ${d.napiName(d.node)} { Property::new("outputLatency")?.with_getter(get_output_latency), Property::new("setSinkId")?.with_method(set_sink_id), Property::new("createMediaStreamSource")?.with_method(create_media_stream_source), + // implementation specific to online audio context + Property::new("resume")?.with_method(resume), + Property::new("suspend")?.with_method(suspend), + Property::new("close")?.with_method(close), ` : ` // ---------------------------------------------------- // Methods and attributes specifc to OfflineAudioContext // ---------------------------------------------------- Property::new("length")?.with_getter(get_length), Property::new("startRendering")?.with_method(start_rendering), + // implementation specific to offline audio context Property::new("suspend")?.with_method(suspend_offline), Property::new("resume")?.with_method(resume_offline), ` @@ -206,6 +207,22 @@ fn get_listener(ctx: CallContext) -> Result { } } +#[js_function] +fn get_state(ctx: CallContext) -> Result { + let js_this = ctx.this_unchecked::(); + let napi_obj = ctx.env.unwrap::<${d.napiName(d.node)}>(&js_this)?; + let obj = napi_obj.unwrap(); + + let state = obj.state(); + let state_str = match state { + AudioContextState::Suspended => "suspended", + AudioContextState::Running => "running", + AudioContextState::Closed => "closed", + }; + + ctx.env.create_string(state_str) +} + // ---------------------------------------------------- // METHODS // ---------------------------------------------------- @@ -342,31 +359,11 @@ fn ${d.slug(factoryName)}(ctx: CallContext) -> Result { `; }).join('')} - ${d.name(d.node) === 'AudioContext' ? ` // ---------------------------------------------------- // Methods and attributes specific to AudioContext // ---------------------------------------------------- - -// @todo - expose in OfflineAudioContext -// see https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-state -#[js_function] -fn get_state(ctx: CallContext) -> Result { - let js_this = ctx.this_unchecked::(); - let napi_obj = ctx.env.unwrap::<${d.napiName(d.node)}>(&js_this)?; - let obj = napi_obj.unwrap(); - - let state = obj.state(); - let state_str = match state { - AudioContextState::Suspended => "suspended", - AudioContextState::Running => "running", - AudioContextState::Closed => "closed", - }; - - ctx.env.create_string(state_str) -} - ${['resume', 'suspend', 'close'].map(method => ` // @todo - async version #[js_function] diff --git a/src/audio_context.rs b/src/audio_context.rs index 3f02d3b6..983e6c78 100644 --- a/src/audio_context.rs +++ b/src/audio_context.rs @@ -40,6 +40,7 @@ impl NapiAudioContext { Property::new("decodeAudioData")?.with_method(decode_audio_data), Property::new("createPeriodicWave")?.with_method(create_periodic_wave), Property::new("createBuffer")?.with_method(create_buffer), + Property::new("state")?.with_getter(get_state), // ---------------------------------------------------- // Factory methods // ---------------------------------------------------- @@ -58,11 +59,6 @@ impl NapiAudioContext { Property::new("createPanner")?.with_method(create_panner), Property::new("createStereoPanner")?.with_method(create_stereo_panner), Property::new("createWaveShaper")?.with_method(create_wave_shaper), - // @todo - expose in OfflineAudioContext as well - Property::new("state")?.with_getter(get_state), - Property::new("resume")?.with_method(resume), - Property::new("suspend")?.with_method(suspend), - Property::new("close")?.with_method(close), // ---------------------------------------------------- // Methods and attributes specific to AudioContext // ---------------------------------------------------- @@ -70,6 +66,10 @@ impl NapiAudioContext { Property::new("outputLatency")?.with_getter(get_output_latency), Property::new("setSinkId")?.with_method(set_sink_id), Property::new("createMediaStreamSource")?.with_method(create_media_stream_source), + // implementation specific to online audio context + Property::new("resume")?.with_method(resume), + Property::new("suspend")?.with_method(suspend), + Property::new("close")?.with_method(close), ], ) } @@ -205,6 +205,22 @@ fn get_listener(ctx: CallContext) -> Result { } } +#[js_function] +fn get_state(ctx: CallContext) -> Result { + let js_this = ctx.this_unchecked::(); + let napi_obj = ctx.env.unwrap::(&js_this)?; + let obj = napi_obj.unwrap(); + + let state = obj.state(); + let state_str = match state { + AudioContextState::Suspended => "suspended", + AudioContextState::Running => "running", + AudioContextState::Closed => "closed", + }; + + ctx.env.create_string(state_str) +} + // ---------------------------------------------------- // METHODS // ---------------------------------------------------- @@ -496,24 +512,6 @@ fn create_wave_shaper(ctx: CallContext) -> Result { // Methods and attributes specific to AudioContext // ---------------------------------------------------- -// @todo - expose in OfflineAudioContext -// see https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-state -#[js_function] -fn get_state(ctx: CallContext) -> Result { - let js_this = ctx.this_unchecked::(); - let napi_obj = ctx.env.unwrap::(&js_this)?; - let obj = napi_obj.unwrap(); - - let state = obj.state(); - let state_str = match state { - AudioContextState::Suspended => "suspended", - AudioContextState::Running => "running", - AudioContextState::Closed => "closed", - }; - - ctx.env.create_string(state_str) -} - // @todo - async version #[js_function] fn resume(ctx: CallContext) -> Result { diff --git a/src/offline_audio_context.rs b/src/offline_audio_context.rs index 334f11a4..b95cc702 100644 --- a/src/offline_audio_context.rs +++ b/src/offline_audio_context.rs @@ -40,6 +40,7 @@ impl NapiOfflineAudioContext { Property::new("decodeAudioData")?.with_method(decode_audio_data), Property::new("createPeriodicWave")?.with_method(create_periodic_wave), Property::new("createBuffer")?.with_method(create_buffer), + Property::new("state")?.with_getter(get_state), // ---------------------------------------------------- // Factory methods // ---------------------------------------------------- @@ -63,6 +64,7 @@ impl NapiOfflineAudioContext { // ---------------------------------------------------- Property::new("length")?.with_getter(get_length), Property::new("startRendering")?.with_method(start_rendering), + // implementation specific to offline audio context Property::new("suspend")?.with_method(suspend_offline), Property::new("resume")?.with_method(resume_offline), ], @@ -151,6 +153,22 @@ fn get_listener(ctx: CallContext) -> Result { } } +#[js_function] +fn get_state(ctx: CallContext) -> Result { + let js_this = ctx.this_unchecked::(); + let napi_obj = ctx.env.unwrap::(&js_this)?; + let obj = napi_obj.unwrap(); + + let state = obj.state(); + let state_str = match state { + AudioContextState::Suspended => "suspended", + AudioContextState::Running => "running", + AudioContextState::Closed => "closed", + }; + + ctx.env.create_string(state_str) +} + // ---------------------------------------------------- // METHODS // ----------------------------------------------------