signalsmith-stretch-js
v0.2.0
Published
Type-safe WebAssembly bindings for Signalsmith Stretch pitch-shifting and time-stretching
Readme
signalsmith-stretch-js
Strict TypeScript bindings for the MIT-licensed Signalsmith Stretch C++ pitch/time library, compiled to WebAssembly. It processes planar PCM locally in modern browsers without exposing pointers or Emscripten memory.
Install
npm install signalsmith-stretch-jsThe package includes its own TypeScript declarations and precompiled .wasm. Consumers do not need Emscripten, C++, or a separate @types package.
Live Web Audio
The default export creates an AudioWorkletNode which can be connected directly into a live Web Audio graph:
import SignalsmithStretch from "signalsmith-stretch-js";
const stretch = await SignalsmithStretch(audioContext, {
numberOfInputs: 1,
numberOfOutputs: 1,
outputChannelCount: [2]
});
source.connect(stretch);
stretch.connect(dry);
stretch.connect(reverb);
await stretch.start({ active: true, semitones: 0 });
await stretch.schedule({
output: audioContext.currentTime + 0.05,
semitones: 7
});The node also provides the upstream-compatible start(), stop(), schedule(), configure(), latency(), addBuffers(), dropBuffers(), and setUpdateInterval() methods. For live input, rate, input, and loop controls are ignored; pitch and formant controls remain live. The same factory is available from signalsmith-stretch-js/worklet.
PCM processing
import { createSignalsmithStretch } from "signalsmith-stretch-js";
const stretch = await createSignalsmithStretch();
const result = await stretch.process({
sampleRate: 44_100,
channels: [left, right],
timeRatio: 1.2, // 20% longer/slower
pitchSemitones: -2,
quality: "high",
advanced: {
formantCompensation: true,
formantBaseHz: 180
}
});
stretch.dispose();timeRatio is output duration divided by input duration. pitchScale: 1 is unchanged, 2 is one octave up, and 0.5 is one octave down. Do not provide pitchScale and pitchSemitones together.
AudioBuffer helper
import { processAudioBuffer } from "signalsmith-stretch-js/browser";
const output = await processAudioBuffer(audioContext, inputBuffer, {
timeRatio: 1.15,
pitchSemitones: -2,
quality: "high"
});The browser entry point also exports audioBufferToAudioData and audioDataToAudioBuffer.
Web Worker
import { SignalsmithStretchWorkerClient } from "signalsmith-stretch-js/worker";
const worker = await SignalsmithStretchWorkerClient.create();
const result = await worker.process({ sampleRate: 48_000, channels, timeRatio: 0.9 });
worker.dispose();Worker inputs are copied before transfer, so caller-owned arrays are not detached.
Streaming
const engine = await createSignalsmithStretch();
const stream = await engine.createStreamProcessor({
sampleRate: 48_000,
channels: 2,
timeRatio: 1.1,
maxBlockSize: 4096
});
const output = stream.process([leftBlock, rightBlock]);
const tail = stream.flush();
stream.dispose();
engine.dispose();The stream API reports algorithm latency and returns the remaining tail from flush(). It is block streaming, not an AudioWorklet or a hard-realtime guarantee.
Quality and formants
| Preset | Signalsmith configuration |
| --- | --- |
| fast | presetCheaper |
| balanced | presetDefault |
| high | presetDefault (reserved for future higher-quality tuning) |
The advanced object supports tonalityLimitHz, formantScale or formantSemitones, formantCompensation, and formantBaseHz.
Signalsmith says time-stretching sounds best for moderate changes around 0.75x–1.5x. More extreme ratios are supported but can produce stronger artifacts.
WASM loading
The package resolves signalsmith-stretch-wasm.wasm relative to its emitted ES module. A CDN or custom deployment can override it:
await createSignalsmithStretch({
wasmUrl: new URL("https://cdn.example.com/signalsmith-stretch-wasm.wasm")
});Serve .wasm as application/wasm. This build is single-threaded and does not require cross-origin isolation.
Scope
This package transforms decoded PCM. It does not decode MP3/AAC, encode files, play audio, render UI, upload files, or provide a backend. Use Web Audio for decoding/playback and a separate encoder for export.
Licence
The wrapper and bundled Signalsmith sources are MIT-licensed, so they can be used in closed-source and commercial applications while retaining the required copyright and licence notices. See THIRD_PARTY_LICENSES.md.
Development
Pinned inputs:
- Signalsmith Stretch 1.3.2, commit
57b93f4e9206a089a45387eaa39bdc9f310d3308 - Signalsmith Linear 0.3.1, commit
5668673560146a9cfe38c25315071e3fd68c8317 - Emscripten 6.0.2
npm install
npm run build
npm test
npm run pack:checkConsumers receive prebuilt WASM and need no native toolchain.
