Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ TODOS.md
NOTES.md
run-wpt.*
all-checks.sh
tests/junk.mjs
tests/junk-*
yarn.lock
issues
Expand Down
3 changes: 1 addition & 2 deletions .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@
.env
.gitmodules

eslint.config.mjs
eslint.config.js
build.rs
Cargo.*
Cross.toml
NOTES.md
dummy.mjs
all-checks.sh
run-wpt.sh

Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion .scripts/move-artifact.mjs → .scripts/move-artifact.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
destReleaseFile,
destDebugFile,
deleteDevArtifacts,
} from './utils/dev-artifacts-helpers.mjs';
} from './utils/dev-artifacts-helpers.js';

const { platform } = process;
const profile = process.argv.includes('--release') ? 'release' : 'debug';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Octokit } from 'octokit';
import fs from 'node:fs';
import { execSync } from 'node:child_process';

import { deleteDevArtifacts } from './utils/dev-artifacts-helpers.mjs';
import { deleteDevArtifacts } from './utils/dev-artifacts-helpers.js';

// --------------------------------------------------------------
console.log('');
Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion .scripts/wpt-harness.mjs → .scripts/wpt-harness.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import wptRunner from 'wpt-runner';
import chalk from 'chalk';
import { program } from 'commander';

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

// mocks
import createXMLHttpRequest from './wpt-mock/XMLHttpRequest.js';
Expand Down
6 changes: 3 additions & 3 deletions .scripts/wpt-mock/XMLHttpRequest.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const fs = require('node:fs');
const path = require('node:path');
import fs from 'node:fs';
import path from 'node:path';

// @note - once all of them are listed, make a pull request to wpt to harmonize all file loading calls
const relativePathPatches = {
Expand All @@ -8,7 +8,7 @@ const relativePathPatches = {

// to be passed to wtp-runner step
// window.XMLHttpRequest = XMLHttpRequest;
module.exports = function createXMLHttpRequest(basepath) {
export default function createXMLHttpRequest(basepath) {
return class XMLHttpRequest {
constructor() {
this._pathname;
Expand Down
6 changes: 3 additions & 3 deletions .scripts/wpt-mock/fetch.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// required in node_modules/wpt_runner/testharness/idlharness.js, cf `fetch_spec`
const fs = require('node:fs');
const path = require('node:path');
import fs from 'node:fs';
import path from 'node:path';

module.exports = function createFetch(basePath) {
export default function createFetch(basePath) {
return function fetch(pathname) {
pathname = path.join(basePath, pathname);

Expand Down
4 changes: 2 additions & 2 deletions .scripts/wpt-mock/requestAnimationFrame.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const animFrame = () => {
setTimeout(animFrame, time - t);
};

module.exports.requestAnimationFrame = requestAnimationFrame = func => {
export const requestAnimationFrame = func => {
// lazily start timer
if (!launched) {
launched = true;
Expand All @@ -32,6 +32,6 @@ module.exports.requestAnimationFrame = requestAnimationFrame = func => {
return funcs.length - 1;
};

module.exports.cancelAnimationFrame = cancelAnimationFrame = id => {
export const cancelAnimationFrame = id => {
funcs[id] = skip;
};
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const path = require('node:path');
import path from 'node:path';

const createXMLHttpRequest = require('./XMLHttpRequest.js');
const { OfflineAudioContext } = require('../../index.cjs');
import createXMLHttpRequest from './XMLHttpRequest.js';
import { OfflineAudioContext } from '../../index.js';

// create a XMLHttpRequest to be passed to the runner
// can be configured to handle the difference between process.cwd() and given path
Expand Down
54 changes: 37 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ npm install [--save] node-web-audio-api

```js
import { AudioContext, OscillatorNode, GainNode } from 'node-web-audio-api';
// or using old fashioned commonjs syntax:
// const { AudioContext, OscillatorNode, GainNode } = require('node-web-audio-api');
// audioContext is resumed by default
const audioContext = new AudioContext();

setInterval(() => {
Expand All @@ -43,33 +42,38 @@ setInterval(() => {
}, 80);
```

### Running the Examples
## Running the Examples

To run all examples locally on your machine you will need to:

1. Install Rust toolchain
1. Clone the repo and build the binary on your machine
```sh
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
git clone https://github.com/ircam-ismm/node-web-audio-api.git
```

2. Clone the repo and build the binary on your machine
2. Install dependencies in the examples directory
```sh
git clone https://github.com/ircam-ismm/node-web-audio-api.git
cd node-web-audio-api
npm install
npm run build
npm run examples:install
# Basically a shortcut for `cd examples && npm install`
```

3. Run the examples from the project's root directory
```sh
node examples/granular-scrub.js
```

_Note that in the examples, the library is loaded through a proxy which loads either from the
`examples/node_modules` directory, or from the root of the project if you want to test
against a local build (see [Build](#build)).

In this last case, make sure to run `npm run examples:clean` to delete any previously
installed `examples/node_modules`._

## Caveats

- `AudioBuffer#getChannelData` is implemented but not reliable in some situations. Your should prefer `AudioBuffer#copyToChannel` and `AudioBuffer#copyFromChannel` when you want to access or manipulate the underlying samples in a safe way.
- `Streams`: only a minimal audio input stream and the `MediaStreamSourceNode` are provided. All other `MediaStream` features are left on the side for now as they principally concern a different API specification, which is not a trivial problem.
- `new AudioContext({sinkId:{type:'none'}})`: if your system has no audio sinks (e.g. docker image) use `{sinkId:{type:'none'}}` when initializing `AudioContext`, else it will crash with `DeviceNotAvailable` [see MDN](https://developer.mozilla.org/en-US/docs/Web/API/AudioContext/AudioContext#sinkid)
- `new AudioContext({ sinkId: { type:'none' } })`: if your system has no audio sinks (e.g. docker image, CI) use `{ sinkId: { type:'none' } }` when initializing `AudioContext`, else it will crash with `DeviceNotAvailable` [see MDN](https://developer.mozilla.org/en-US/docs/Web/API/AudioContext/AudioContext#sinkid)

## Supported Platforms - Prebuilt Binaries

Expand All @@ -93,8 +97,7 @@ We provide prebuilt binaries for the following platforms:

- All provided Linux binaries are built with the `jack` flag, which should work either with properly configured [Jack](https://jackaudio.org/) or [pipewire-jack](https://pipewire.org/) backends. If this is a limitation for you, please fill an [issue](https://github.com/ircam-ismm/node-web-audio-api/issues) and we will see what we can do.


## Manual Build
### Manually install and build the library

If prebuilt binaries are not shippped for your platform, you will need to:

Expand All @@ -119,7 +122,7 @@ Be aware that the package won't be listed on your `package.json` file, and that

## Notes for Linux users

### Build
### Dependencies

To build the library, you will need to manually install the `libasound2-dev` package:

Expand All @@ -133,7 +136,7 @@ Optionally, if you use the Jack Audio Backend, the `libjack-jackd2-dev` package:
sudo apt install libjack-jackd2-dev
```

In such case, you can use the `npm run build:jack` script to enable the Jack feature.
In that case, you can use the `npm run build:jack` script to enable the Jack feature.

### Audio backend and latency

Expand All @@ -148,11 +151,28 @@ For real-time and interactive applications where low latency is crucial, you sho
If you don't have JACK installed, you can still pass the `WEB_AUDIO_LATENCY=playback` environment variable to all examples to create the audio context with the playback latency hint, e.g.:

```sh
WEB_AUDIO_LATENCY=playback node examples/amplitude-modulation.mjs
WEB_AUDIO_LATENCY=playback node examples/amplitude-modulation.js
```

## Development notes

### Build

1. Install Rust toolchain

```sh
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
````

2. Clone the repo and build the binary on your machine

```sh
git clone https://github.com/ircam-ismm/node-web-audio-api.git
cd node-web-audio-api
npm install
npm run build
```

### Synchronize versioning

The npm `postversion` script rely on [`cargo-bump`](https://crates.io/crates/cargo-bump) to maintain versions synced between the `package.json` and the `Cargo.toml` files. Therefore, you will need to install `cargo-bump` on your machine
Expand All @@ -163,7 +183,7 @@ 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:
Follow the steps for [Build](#build) first. Then checkout the web-platform-tests submodule with:

```
git submodule init
Expand Down
File renamed without changes.
1 change: 1 addition & 0 deletions examples/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package-lock=false
2 changes: 1 addition & 1 deletion examples/all-nodes.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
OfflineAudioContext,
AudioBuffer,
mediaDevices,
} from '../index.mjs';
} from '#node-web-audio-api';

const contexts = [new AudioContext(), new OfflineAudioContext(1, 1, 48000)];

Expand Down
2 changes: 1 addition & 1 deletion examples/amplitude-modulation.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AudioContext } from '../index.mjs';
import { AudioContext } from '#node-web-audio-api';

const latencyHint = process.env.WEB_AUDIO_LATENCY === 'playback' ? 'playback' : 'interactive';
const audioContext = new AudioContext({ latencyHint });
Expand Down
2 changes: 1 addition & 1 deletion examples/analyser.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AudioContext } from '../index.mjs';
import { AudioContext } from '#node-web-audio-api';

const latencyHint = process.env.WEB_AUDIO_LATENCY === 'playback' ? 'playback' : 'interactive';
const audioContext = new AudioContext({ latencyHint });
Expand Down
2 changes: 1 addition & 1 deletion examples/audio-buffer.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import assert from 'node:assert';
import { AudioContext } from '../index.mjs';
import { AudioContext } from '#node-web-audio-api';

const latencyHint = process.env.WEB_AUDIO_LATENCY === 'playback' ? 'playback' : 'interactive';
const audioContext = new AudioContext({ latencyHint });
Expand Down
2 changes: 1 addition & 1 deletion examples/audio-worklet-online.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AudioContext, OscillatorNode, GainNode, AudioWorkletNode } from '../index.mjs';
import { AudioContext, OscillatorNode, GainNode, AudioWorkletNode } from '#node-web-audio-api';

// load audio worklet from online source

Expand Down
2 changes: 1 addition & 1 deletion examples/audio-worklet-shared-array-buffer.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import path from 'node:path';

import { AudioContext, AudioWorkletNode } from '../index.mjs';
import { AudioContext, AudioWorkletNode } from '#node-web-audio-api';
import { sleep } from '@ircam/sc-utils';

const latencyHint = process.env.WEB_AUDIO_LATENCY === 'playback' ? 'playback' : 'interactive';
Expand Down
4 changes: 2 additions & 2 deletions examples/audio-worklet-webassembly.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import { AudioContext, OscillatorNode, AudioWorkletNode } from '../index.mjs';
import { AudioContext, OscillatorNode, AudioWorkletNode } from '#node-web-audio-api';

const audioContext = new AudioContext();

await audioContext.audioWorklet.addModule('./worklets/wasm-worklet-processor.mjs');
await audioContext.audioWorklet.addModule('./worklets/wasm-worklet-processor.js');
const oscillator = new OscillatorNode(audioContext);
const bypasser = new AudioWorkletNode(audioContext, 'wasm-worklet-processor');
oscillator.connect(bypasser).connect(audioContext.destination);
Expand Down
2 changes: 1 addition & 1 deletion examples/audio-worklet-white-woise.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import path from 'node:path';

import { AudioContext, OfflineAudioContext, AudioWorkletNode } from '../index.mjs';
import { AudioContext, OfflineAudioContext, AudioWorkletNode } from '#node-web-audio-api';
import { sleep } from '@ircam/sc-utils';

const latencyHint = process.env.WEB_AUDIO_LATENCY === 'playback' ? 'playback' : 'interactive';
Expand Down
2 changes: 1 addition & 1 deletion examples/audio-worklet-with-params.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import path from 'node:path';

import { AudioContext, OfflineAudioContext, OscillatorNode, AudioWorkletNode } from '../index.mjs';
import { AudioContext, OfflineAudioContext, OscillatorNode, AudioWorkletNode } from '#node-web-audio-api';
import { sleep } from '@ircam/sc-utils';

const latencyHint = process.env.WEB_AUDIO_LATENCY === 'playback' ? 'playback' : 'interactive';
Expand Down
2 changes: 1 addition & 1 deletion examples/benchmarks.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import readline from 'node:readline';
import fs from 'node:fs';
import path from 'node:path';

import { OfflineAudioContext, AudioContext } from '../index.mjs';
import { OfflineAudioContext, AudioContext } from '#node-web-audio-api';
import { getTime } from '@ircam/sc-utils';
import Table from 'cli-table';

Expand Down
2 changes: 1 addition & 1 deletion examples/biquad.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import fs from 'node:fs';
import path from 'node:path';
import { AudioContext } from '../index.mjs';
import { AudioContext } from '#node-web-audio-api';

const latencyHint = process.env.WEB_AUDIO_LATENCY === 'playback' ? 'playback' : 'interactive';
const audioContext = new AudioContext({ latencyHint });
Expand Down
2 changes: 1 addition & 1 deletion examples/change-state.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AudioContext } from '../index.mjs';
import { AudioContext } from '#node-web-audio-api';

console.warn('[incomplete example]');

Expand Down
22 changes: 22 additions & 0 deletions examples/commonjs.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
const { AudioContext, OscillatorNode, GainNode } = require('../index.js');

const latencyHint = process.env.WEB_AUDIO_LATENCY === 'playback' ? 'playback' : 'interactive';
const audioContext = new AudioContext({ latencyHint });

setInterval(() => {
const now = audioContext.currentTime;
const frequency = 200 + Math.random() * 2800;

const env = new GainNode(audioContext, { gain: 0 });
env.connect(audioContext.destination);
env.gain
.setValueAtTime(0, now)
.linearRampToValueAtTime(0.2, now + 0.02)
.exponentialRampToValueAtTime(0.0001, now + 1);

const osc = new OscillatorNode(audioContext);
osc.frequency.value = frequency;
osc.connect(env);
osc.start(now);
osc.stop(now + 1);
}, 80);
2 changes: 1 addition & 1 deletion examples/composite-audio-node.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { sleep } from '@ircam/sc-utils';
import { AudioContext, AudioNode } from '../index.mjs';
import { AudioContext, AudioNode } from '#node-web-audio-api';

// Monkeypatching AudioNode.connect to allow for composite nodes.
// https://github.com/GoogleChromeLabs/web-audio-samples/wiki/CompositeAudioNode
Expand Down
2 changes: 1 addition & 1 deletion examples/compressor.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import fs from 'node:fs';
import path from 'node:path';
import { AudioContext } from '../index.mjs';
import { AudioContext } from '#node-web-audio-api';

const latencyHint = process.env.WEB_AUDIO_LATENCY === 'playback' ? 'playback' : 'interactive';
const audioContext = new AudioContext({ latencyHint });
Expand Down
2 changes: 1 addition & 1 deletion examples/constant-source.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AudioContext } from '../index.mjs';
import { AudioContext } from '#node-web-audio-api';

const latencyHint = process.env.WEB_AUDIO_LATENCY === 'playback' ? 'playback' : 'interactive';
const audioContext = new AudioContext({ latencyHint });
Expand Down
2 changes: 1 addition & 1 deletion examples/convolution.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import fs from 'node:fs';
import path from 'node:path';
import { AudioContext, ConvolverNode } from '../index.mjs';
import { AudioContext, ConvolverNode } from '#node-web-audio-api';

// create an `AudioContext` and load a sound file
const latencyHint = process.env.WEB_AUDIO_LATENCY === 'playback' ? 'playback' : 'interactive';
Expand Down
2 changes: 1 addition & 1 deletion examples/decoding-legacy.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import fs from 'node:fs';
import path from 'node:path';
import { AudioContext, OfflineAudioContext } from '../index.mjs';
import { AudioContext, OfflineAudioContext } from '#node-web-audio-api';

const latencyHint = process.env.WEB_AUDIO_LATENCY === 'playback' ? 'playback' : 'interactive';
const audioContext = new AudioContext({ latencyHint });
Expand Down
2 changes: 1 addition & 1 deletion examples/decoding.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import fs from 'node:fs';
import path from 'node:path';
import { AudioContext } from '../index.mjs';
import { AudioContext } from '#node-web-audio-api';

const files = [
path.join('examples', 'samples', 'sample-faulty.wav'),
Expand Down
Loading
Loading