UNPKG

webaudio-node

Version:

Full-featured Web Audio API for Node.js with WASM and SIMD optimizations

556 lines (401 loc) โ€ข 15.9 kB
# Web Audio Node [![CI](https://github.com/monteslu/webaudio-node/actions/workflows/ci.yml/badge.svg)](https://github.com/monteslu/webaudio-node/actions/workflows/ci.yml) [![codecov](https://codecov.io/gh/monteslu/webaudio-node/branch/main/graph/badge.svg)](https://codecov.io/gh/monteslu/webaudio-node) [![npm version](https://badge.fury.io/js/webaudio-node.svg)](https://www.npmjs.com/package/webaudio-node) [![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](https://opensource.org/licenses/ISC) [![Node.js Version](https://img.shields.io/node/v/webaudio-node.svg)](https://nodejs.org) **Web Audio API for Node.js with WASM and SIMD optimizations** High-performance, browser-compatible Web Audio API for Node.js. Perfect for audio processing, sound generation, game audio, and music production. ## โญ Features โœจ **Web Audio API Support** - Core audio nodes (Oscillator, Gain, BiquadFilter, BufferSource) - AudioParam automation with scheduling - **OfflineAudioContext** for fast non-realtime rendering - **AudioContext** for real-time playback - **5 audio formats** via WASM decoders (MP3, WAV, FLAC, OGG, AAC) - see [WASM Audio Decoders](./docs/WASM_AUDIO_DECODERS.md) - **High-quality resampling** - Speex resampler with SIMD (same as Firefox uses) - **Automatic sample rate detection** - matches system audio device ๐Ÿš€ **High Performance** - **WASM with SIMD optimizations** - beats Rust implementation in 90% of benchmarks (55/61) - **~2,600x faster than realtime** offline rendering - **Fast resampling** - Speex quality level 1 (200-850x realtime for common sample rates) - **Sample-accurate timing** - all audio timing managed in WASM using sample counters - Zero JavaScript overhead in audio rendering - JS only handles I/O through SDL ๐ŸŽฎ **Perfect for Games** - Procedural sound effect generation - Mix multiple audio sources - Low-latency playback with SDL2 ## ๐Ÿ“ฆ Installation ```bash npm install webaudio-node ``` ### Requirements - Node.js 20+ - No build step required - uses pre-compiled WASM ## ๐ŸŽต CLI Usage Play audio files directly from the command line: ```bash # Play any supported audio file npx webaudio-node music.mp3 # Or after global install npm install -g webaudio-node webaudio-node sound.wav ``` **Supported formats:** MP3, WAV, FLAC, OGG, AAC ### ๐ŸŽค Microphone Effects Transform your voice in real-time with built-in effects: ```bash # Add reverb to your microphone webaudio-node --reverb # Auto-tune your voice (vocoder-style effect) webaudio-node --auto-tune # Show all options webaudio-node --help ``` Press Ctrl+C to stop the effect and close the microphone. ## ๐Ÿš€ Quick Start ### Simple Beep ```javascript import { AudioContext } from 'webaudio-node'; const ctx = new AudioContext(); // Create 440Hz oscillator const osc = ctx.createOscillator(); osc.frequency.value = 440; osc.connect(ctx.destination); // Play for 1 second await ctx.resume(); osc.start(); setTimeout(() => { osc.stop(); ctx.close(); }, 1000); ``` ### Play MP3 File ```javascript import { AudioContext } from 'webaudio-node'; import { readFileSync } from 'fs'; const ctx = new AudioContext({ sampleRate: 48000 }); const audioData = readFileSync('./music.mp3'); // Decode MP3 const buffer = await ctx.decodeAudioData(audioData.buffer); // Play const source = ctx.createBufferSource(); source.buffer = buffer; source.connect(ctx.destination); source.start(); await ctx.resume(); ``` ### Procedural Sound Effect (Ultra-Fast!) ```javascript import { OfflineAudioContext } from 'webaudio-node'; // Generate laser sound offline const offlineCtx = new OfflineAudioContext({ numberOfChannels: 2, length: 14400, sampleRate: 48000 }); const osc = offlineCtx.createOscillator(); osc.type = 'sawtooth'; osc.frequency.setValueAtTime(800, 0); osc.frequency.exponentialRampToValueAtTime(200, 0.3); const gain = offlineCtx.createGain(); gain.gain.setValueAtTime(0.5, 0); gain.gain.exponentialRampToValueAtTime(0.01, 0.3); osc.connect(gain); gain.connect(offlineCtx.destination); osc.start(0); // Renders in ~1ms (14,400x faster than realtime!) const laserBuffer = await offlineCtx.startRendering(); ``` ## ๐Ÿ“š API Reference ### AudioContext Real-time audio playback with chunk-ahead buffering. ```javascript const ctx = new AudioContext({ sampleRate: 48000, // Default: 44100 numberOfChannels: 2 // Default: 2 (stereo) }); await ctx.resume(); // Start audio await ctx.suspend(); // Pause audio await ctx.close(); // Stop and cleanup ``` **Note:** Real-time AudioContext uses chunk-ahead buffering (1-2 second latency), which is perfect for background music and non-interactive audio. ### OfflineAudioContext Non-realtime rendering for ultra-fast audio processing. ```javascript const offlineCtx = new OfflineAudioContext({ numberOfChannels: 2, length: 48000, // samples sampleRate: 48000 }); const buffer = await offlineCtx.startRendering(); ``` **Performance:** Renders ~50,000x faster than realtime. ### Audio Nodes **Source Nodes:** - `createOscillator()` - Sine, square, sawtooth, triangle waveforms - `createBufferSource()` - Play audio buffers - `createGain()` - Volume control **Processing:** - `createBiquadFilter()` - Lowpass, highpass, bandpass, notch filters ### Supported Node Types **16/17 applicable Web Audio API nodes implemented (94% coverage)** | Node | AudioContext | OfflineAudioContext | | ---------------------- | ------------ | ------------------- | | AudioDestinationNode | โœ… | โœ… | | AudioBufferSourceNode | โœ… | โœ… | | OscillatorNode | โœ… | โœ… | | ConstantSourceNode | โœ… | โœ… | | GainNode | โœ… | โœ… | | BiquadFilterNode | โœ… | โœ… | | IIRFilterNode | โœ… | โœ… | | DelayNode | โœ… | โœ… | | WaveShaperNode | โœ… | โœ… | | ConvolverNode | โœ… | โœ… | | DynamicsCompressorNode | โœ… | โœ… | | PannerNode | โœ… | โœ… | | StereoPannerNode | โœ… | โœ… | | ChannelSplitterNode | โœ… | โœ… | | ChannelMergerNode | โœ… | โœ… | | AnalyserNode | โœ… | โœ… | **Not applicable to Node.js:** MediaElement/MediaStream nodes (browser-only), ScriptProcessorNode (deprecated) ### AudioParam Automation All AudioParams support automation: ```javascript const osc = ctx.createOscillator(); // Instant change osc.frequency.value = 880; // Scheduled changes osc.frequency.setValueAtTime(440, 0); osc.frequency.linearRampToValueAtTime(880, 1.0); osc.frequency.exponentialRampToValueAtTime(220, 2.0); ``` ### Decoding Audio Files ```javascript import { readFileSync } from 'fs'; const ctx = new AudioContext(); const audioData = readFileSync('./audio.mp3'); // Decode MP3/WAV/OGG const buffer = await ctx.decodeAudioData(audioData.buffer); console.log(buffer.duration); // seconds console.log(buffer.numberOfChannels); // 1 or 2 console.log(buffer.sampleRate); // Hz // Get audio data const channelData = buffer.getChannelData(0); ``` **Note:** Audio decoding is currently synchronous (blocks the event loop) but very fast with WASM. Most files decode in milliseconds. For non-blocking decoding in the future, see [Threading Plan](./docs/threading.md). ## ๐Ÿ“– Examples ### Layered Music Playback ```javascript import { AudioContext } from 'webaudio-node'; import { readFileSync } from 'fs'; const ctx = new AudioContext({ sampleRate: 48000 }); const audioData = readFileSync('./music.mp3'); const buffer = await ctx.decodeAudioData(audioData.buffer); // Create master gain const masterGain = ctx.createGain(); masterGain.gain.value = 0.25; masterGain.connect(ctx.destination); // Play 5 instances with 1-second delays for (let i = 0; i < 5; i++) { const source = ctx.createBufferSource(); source.buffer = buffer; const instanceGain = ctx.createGain(); instanceGain.gain.value = 1.0; source.connect(instanceGain); instanceGain.connect(masterGain); const startTime = ctx.currentTime + i; source.start(startTime); } await ctx.resume(); ``` ### Filtered Oscillator ```javascript import { OfflineAudioContext } from 'webaudio-node'; import { writeFileSync } from 'fs'; const ctx = new OfflineAudioContext({ numberOfChannels: 2, length: 48000 * 3, // 3 seconds sampleRate: 48000 }); // Create filtered sawtooth const osc = ctx.createOscillator(); osc.type = 'sawtooth'; osc.frequency.value = 110; // A2 const filter = ctx.createBiquadFilter(); filter.type = 'lowpass'; filter.frequency.value = 2000; filter.Q.value = 5; // Sweep filter filter.frequency.setValueAtTime(200, 0); filter.frequency.exponentialRampToValueAtTime(8000, 3); const gain = ctx.createGain(); gain.gain.value = 0.3; osc.connect(filter); filter.connect(gain); gain.connect(ctx.destination); osc.start(0); const rendered = await ctx.startRendering(); // Write to file... ``` ### Game Sound Effects Generator ```javascript import { OfflineAudioContext } from 'webaudio-node'; async function generateLaserSound() { const ctx = new OfflineAudioContext({ numberOfChannels: 2, length: 14400, sampleRate: 48000 }); const osc = ctx.createOscillator(); osc.type = 'sawtooth'; osc.frequency.setValueAtTime(600 + Math.random() * 400, 0); osc.frequency.exponentialRampToValueAtTime(100 + Math.random() * 200, 0.3); const gain = ctx.createGain(); gain.gain.setValueAtTime(0.3, 0); gain.gain.exponentialRampToValueAtTime(0.01, 0.3); osc.connect(gain); gain.connect(ctx.destination); osc.start(0); return await ctx.startRendering(); } // Generate 100 variations in ~100ms const sounds = await Promise.all( Array(100) .fill(0) .map(() => generateLaserSound()) ); ``` ## โšก Performance webaudio-node delivers excellent performance for audio workloads. See [detailed benchmarks](./docs/automated_benchmarks.md) for comprehensive comparisons. **Offline Rendering Speed:** | Duration | Render Time | Speed vs Realtime | | -------- | ----------- | ----------------- | | 1s | ~0.38ms | ~2,600x | | 5s | ~1.9ms | ~2,600x | | 10s | ~3.8ms | ~2,600x | **Why so fast?** WASM with SIMD optimizations processes 4 audio samples in parallel. ## ๐Ÿ—๏ธ Architecture ### WASM Backend Audio graph rendering uses WebAssembly compiled from optimized C++: ``` โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ JavaScript API โ”‚ (Node creation, connections, I/O via SDL) โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ WASM Engine โ”‚ (Graph rendering, SIMD mixing, timing) โ”‚ โ”‚ โ€ข Sample-accurate timing with current_sample counters โ”‚ โ”‚ โ€ข All scheduling handled in WASM โ”‚ โ”‚ โ€ข Unified binary: graph + nodes + decoders โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ SDL2 Output โ”‚ (Audio device via @kmamal/sdl) โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ ``` **Key features:** - Single unified WASM binary (`dist/webaudio.wasm`) contains all audio processing - All audio timing managed in WASM using sample-accurate `current_sample` counters - JavaScript only handles I/O through SDL - no timing logic in JS - SIMD optimizations (4-8x parallel processing) - Proper gain node mixing - all inputs mixed together per Web Audio spec - Sample-accurate BufferSourceNode scheduling See [docs/architecture.md](docs/architecture.md) for architectural details. ## ๐Ÿ”ง Development ### Building from Source WASM modules are pre-compiled and included in the npm package. To rebuild from source: ```bash # Install Emscripten SDK git clone https://github.com/emscripten-core/emsdk.git cd emsdk ./emsdk install latest ./emsdk activate latest source ./emsdk_env.sh # Build WASM (includes all audio decoders) cd webaudio-node npm run build:wasm ``` ### Available Scripts ```bash # Testing npm test # Run test suite npm run test:coverage # Run tests with coverage report # Linting & Formatting npm run lint # Check code style npm run lint:fix # Auto-fix linting issues npm run format # Format code with Prettier npm run format:check # Check formatting # Benchmarks npm run benchmark # Run performance benchmarks npm run benchmark:docs # Generate benchmark documentation ``` ## ๐Ÿงช Testing Comprehensive Web Audio API test suite: ```bash npm test ``` Tests cover: - All audio nodes - AudioParam automation - Audio graph connections - Rendering accuracy - Stereo/mono support **Test results:** 61/61 tests passing โœ… ## ๐Ÿ“ Project Structure ``` webaudio-node/ โ”œโ”€โ”€ src/ โ”‚ โ”œโ”€โ”€ wasm/ # C++ source for WASM โ”‚ โ”œโ”€โ”€ wasm-integration/ # WASM wrapper classes โ”‚ โ””โ”€โ”€ javascript/ # JavaScript nodes & AudioParam โ”œโ”€โ”€ test/ โ”‚ โ”œโ”€โ”€ api-suite.js # Comprehensive API tests โ”‚ โ””โ”€โ”€ samples/ # Test audio files โ”œโ”€โ”€ docs/ โ”‚ โ””โ”€โ”€ threading.md # Architecture documentation โ””โ”€โ”€ scripts/ โ””โ”€โ”€ build-wasm.mjs # WASM build script ``` ## ๐Ÿ–ฅ๏ธ Platform Support | Platform | Status | Notes | | -------------- | ------ | ------------------ | | macOS ARM64 | โœ… | SIMD optimizations | | macOS x86-64 | โœ… | SIMD optimizations | | Linux ARM64 | โœ… | SIMD optimizations | | Linux x86-64 | โœ… | SIMD optimizations | | Windows x86-64 | โœ… | Requires SDL2 | ## ๐Ÿ”„ Comparison with Other Libraries ### vs node-web-audio-api (Rust) **node-web-audio-api** is an excellent, well-maintained Rust implementation. Both libraries offer high-performance audio processing for Node.js. **Key differences:** - **Performance**: webaudio-node is faster overall in most workloads (90% win rate - see [detailed benchmarks](./docs/automated_benchmarks.md)) - **Resampling**: Speex quality 1 (fast, good quality) vs advanced resampling - **Node Coverage**: 16/17 applicable nodes (94%) implemented - **Installation**: Pre-compiled WASM (no build toolchain required) Both libraries are production-ready - choose based on your specific needs! ### vs web-audio-engine - โœ… Much faster (WASM vs pure JS) - โœ… Better Web Audio API compliance - โœ… Real-time playback support - โœ… Comprehensive node coverage ## ๐Ÿ›ฃ๏ธ Roadmap **Current:** 94% Web Audio API compliance with WASM backend **Future:** - Worker Thread rendering for lower latency - AudioWorklet support - Additional WebCodecs integration See [docs/threading.md](docs/threading.md) for Worker Thread implementation plan. ## ๐Ÿ“œ License MIT License - see [LICENSE](LICENSE) ## ๐Ÿ™ Credits - SDL2 for audio output - [Emscripten](https://emscripten.org/) for WASM compilation - [@kmamal/sdl](https://github.com/kmamal/node-sdl) for SDL bindings - [dr_libs](https://github.com/mackron/dr_libs) for WASM audio decoding (MP3, WAV, FLAC) - [stb_vorbis](https://github.com/nothings/stb) for OGG Vorbis decoding - [fdk-aac](https://github.com/mstorsjo/fdk-aac) for AAC decoding - [node-web-audio-api](https://github.com/ircam-ismm/node-web-audio-api) for Rust implementation reference --- **Made with โค๏ธ for game developers, music producers, and audio enthusiasts** ๐ŸŽฎ ๐ŸŽต ๐Ÿ”Š