Skip to content

Some work in progress on running the web-platform-tests suite for WebAudio #42

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 40 commits into from
Dec 28, 2023
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
1acdd32
Copy over some web-platform-tests
orottier Nov 25, 2023
b98fd01
Run the WPT webaudio test harness for our bindings
orottier Nov 25, 2023
66fdf55
refactor: move wpt as npm script
b-ma Nov 27, 2023
ced86f1
chore: use programmatic API to run wpt
b-ma Nov 27, 2023
7f9cd2e
chore: clean wpt files
b-ma Nov 27, 2023
b27bc9c
chore: build in debug mode when running wpt
b-ma Nov 27, 2023
e8835d6
Merge branch 'main' into feature/wpt-harness
b-ma Nov 28, 2023
0eb0b7e
test: add analyser node wpt test + disciminate errors of error types …
b-ma Nov 28, 2023
8ff8d03
test: add some options for wpt tests
b-ma Nov 28, 2023
acfbb2e
Avoid some wpt crashes by providing stub implementations
orottier Nov 28, 2023
99bfa66
Replace wpt webaudio suite with git submodule
orottier Nov 29, 2023
81e52ed
Skip crashtests and resources in web platform tests
orottier Nov 29, 2023
ce30937
Add more stubs to prevent the wpt from crashing
orottier Nov 30, 2023
edad866
Add bindings for AudioListener
orottier Nov 30, 2023
072baef
Revert unrelated changes to iir-filter example
orottier Nov 30, 2023
876c654
fix: lazily instantiate listener
b-ma Dec 2, 2023
5d24303
Revert "Add bindings for AudioListener"
orottier Dec 2, 2023
23e2cf0
Merge remote-tracking branch 'origin/feat/audio-listener' into featur…
orottier Dec 2, 2023
edc7b50
Update results of template generation
orottier Dec 2, 2023
3406ec7
Improve suspend/resume stub, use pending promise instead of resolved
orottier Dec 2, 2023
e57fd25
OfflineAudioContext now takes &mut for startRendering
orottier Dec 2, 2023
5892b7d
Revert "OfflineAudioContext now takes &mut for startRendering"
orottier Dec 3, 2023
4dddcf5
Merge remote-tracking branch 'origin/refactor/offline-audio-context' …
orottier Dec 3, 2023
e675723
Revert other changes for OfflineAudioContext being consumed
orottier Dec 3, 2023
686c466
Merge remote-tracking branch 'origin/main' into feature/wpt-harness
orottier Dec 6, 2023
babd5bd
Remove printlns
orottier Dec 6, 2023
4374dd9
Attempting to get OfflineAudioContext.suspend to work
orottier Dec 6, 2023
1ed0c50
make callback run without crashing
b-ma Dec 7, 2023
8f4d454
feat: implement async startRendering, susped and resume for OfflineAu…
b-ma Dec 21, 2023
3461cd9
Merge branch 'main' into feature/offline-context-suspend
b-ma Dec 22, 2023
17770ef
refactor: cleaning
b-ma Dec 22, 2023
56ff66e
fmt
b-ma Dec 22, 2023
df44e9f
OfflineAudioContext renamed suspend_at -> suspend
orottier Dec 27, 2023
a1d7a3e
Merge pull request #50 from ircam-ismm/feature/offline-context-suspend
orottier Dec 27, 2023
c24af51
Implement deprecated AudioListener setPosition, setOrientation
orottier Dec 27, 2023
ca9014e
Update the wpt submodule to my own fork for now
orottier Dec 27, 2023
2633aed
Upgrade to web-audio-api-rs 0.39 and reinstate lto setting
orottier Dec 27, 2023
edb3441
feat: implement all audio param attributes
b-ma Dec 28, 2023
fd4d710
fix: typo
b-ma Dec 28, 2023
1a3e918
fix: expose OfflineAudioContext::state
b-ma Dec 28, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "wpt"]
path = wpt
url = [email protected]:web-platform-tests/wpt.git
7 changes: 4 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"}
Expand All @@ -22,7 +22,8 @@ mimalloc = {version = "0.1"}
napi-build = "1"

[profile.release]
lto = true
lto = "fat" # https://nnethercote.github.io/perf-book/build-configuration.html#link-time-optimization
# debug = true

[features]
jack = ["web-audio-api/cpal-jack"]
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,26 @@ 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 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
npm run wpt:only # run all wpt test without build
npm run wpt -- --list # list all wpt test files
npm run wpt -- --filter <string> # apply <string> filter on executed/listed wpt tests
```

Avai

## License

[BSD-3-Clause](./LICENSE)
105 changes: 105 additions & 0 deletions bin/wpt-harness.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import path from 'path';
import wptRunner from 'wpt-runner';
import chalk from 'chalk';
import { program } from 'commander';

import * as nodeWebAudioAPI from '../index.mjs';

program
.option('--list', 'List the name of the test files')
.option('--with_crashtests', 'Also run crashtests')
.option('--filter <string>', 'Filter executed OR listed test files', '.*');

program.parse(process.argv);

const options = program.opts();

// -------------------------------------------------------
// 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 = 'wpt/webaudio';
const rootURL = 'webaudio';

// monkey patch `window` with our web audio API
const setup = window => {
Object.assign(window, nodeWebAudioAPI);

// seems required (weirdly...), cf. `the-audiobuffer-interface/audiobuffer.html`
window.Float32Array = Float32Array;
}

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);
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
let numPass = 0;
let numFail = 0;
let typeErrorFail = 0;

const reporter = {
startSuite: name => {
console.log(`\n ${chalk.bold.underline(path.join(testsPath, name))}\n`);
},
pass: message => {
numPass += 1;
console.log(chalk.dim(indent(chalk.green("√ ") + message, INDENT_SIZE)));
},
fail: message => {
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)))
},
};

// -------------------------------------------------------
// 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}`));
console.log(chalk.bold(` - # type error issues: ${typeErrorFail}`));

process.exit(failures);
} catch (e) {
console.error(e.stack);
process.exit(1);
}
24 changes: 24 additions & 0 deletions examples/panner.mjs
Original file line number Diff line number Diff line change
@@ -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);
7 changes: 7 additions & 0 deletions generator/templates/audio_context.tmpl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ fn constructor(ctx: CallContext) -> Result<JsUndefined> {
};

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)?;

Expand All @@ -134,6 +135,12 @@ fn constructor(ctx: CallContext) -> Result<JsUndefined> {
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()
}

Expand Down
3 changes: 3 additions & 0 deletions generator/templates/lib.tmpl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
15 changes: 12 additions & 3 deletions generator/templates/offline_audio_context.tmpl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,10 @@ fn constructor(ctx: CallContext) -> Result<JsUndefined> {
fn get_current_time(ctx: CallContext) -> Result<JsNumber> {
let js_this = ctx.this_unchecked::<JsObject>();
let napi_obj = ctx.env.unwrap::<NapiOfflineAudioContext>(&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)
Expand All @@ -90,7 +93,10 @@ fn get_current_time(ctx: CallContext) -> Result<JsNumber> {
fn get_sample_rate(ctx: CallContext) -> Result<JsNumber> {
let js_this = ctx.this_unchecked::<JsObject>();
let napi_obj = ctx.env.unwrap::<NapiOfflineAudioContext>(&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)
Expand Down Expand Up @@ -237,7 +243,10 @@ fn ${d.slug(factoryName)}(ctx: CallContext) -> Result<JsObject> {
fn get_length(ctx: CallContext) -> Result<JsNumber> {
let js_this = ctx.this_unchecked::<JsObject>();
let napi_obj = ctx.env.unwrap::<NapiOfflineAudioContext>(&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)
Expand Down
8 changes: 8 additions & 0 deletions monkey-patch.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down
10 changes: 7 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,19 @@
"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": "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",
"@ircam/sc-gettime": "^1.0.0",
"@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",
"commander": "^11.1.0",
"dotenv": "^16.0.3",
"eslint": "^8.32.0",
"mocha": "^10.2.0",
Expand All @@ -59,7 +62,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",
Expand Down
7 changes: 7 additions & 0 deletions src/audio_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ fn constructor(ctx: CallContext) -> Result<JsUndefined> {
};

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)?;

Expand All @@ -155,6 +156,12 @@ fn constructor(ctx: CallContext) -> Result<JsUndefined> {
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()
}

Expand Down
Loading