Try it in the browser: Tuner, Audio recorder, Room and speaker measurement. Runs on this package, nothing is uploaded.
Capture audio from microphone in node or browser.
import mic from '@audio/mic'
let read = mic({
sampleRate: 44100,
channels: 1,
bitDepth: 16,
// bufferSize: 50, // the most one block holds, ms (default 50)
// backlog: 10000, // the most held unread, ms (default 10000)
// backend: 'miniaudio', // force backend
})
read((err, pcmBuffer) => {
// process chunk
})
read(null) // stop captureimport mic from '@audio/mic'
let read = mic({ sampleRate: 44100, channels: 1 })
for await (let chunk of read) {
// process PCM chunk
}A break out of the loop stops capture. An overflow (see read(cb)) is thrown.
import mic, { devices } from '@audio/mic'
await devices()
// [{ id: 'BuiltInMicrophoneDevice', name: 'MacBook Pro Microphone', default: true }, …]
let read = mic({ device: 'USB' }) // by id, by name, or by part of its name when only one holds itIds are the platform's own: CoreAudio's UID, WASAPI's endpoint id, the ALSA or PulseAudio name. A device that isn't there is an error (code: 'ENODEVICE'), never another device. In the browser, devices() reads enumerateDevices() (names show once the page may use the microphone) and device becomes the deviceId getUserMedia asks for.
import MicReadable from '@audio/mic/stream'
MicReadable({ sampleRate: 44100, channels: 1 }).pipe(dest)Tried in order; first successful one wins.
| Backend | How | Latency | Install |
|---|---|---|---|
miniaudio |
N-API addon wrapping miniaudio.h | Low | Prebuilt via @audio/mic-* packages |
process |
Pipes from ffmpeg/sox/arecord | High | System tool must be installed |
null |
Silent, maintains timing contract | — | Built-in (CI/headless fallback) |
mediastream |
getUserMedia + AudioWorklet (browser) | Low | Built-in |
Returns a source function. Options:
sampleRate— default44100channels— default1bitDepth—8,16(default),24,32bufferSize— the most one block from the device holds, in ms, default50backlog– the most audio held unread, in ms, default10000(seeread(cb))device— an input by id or name (devices()), default the system'sbackend— force a specific backend
Read PCM data. Callback fires with each captured chunk: (err, buffer) => {}.
Capture doesn't wait for reads: what comes between them is held, and the next read takes all of it in one buffer, so a reader that takes its time loses nothing. Past backlog held unread, capture stops: what was held still reads, then an error with code: 'EOVERFLOW', then the end (null). In node the device's own ring holds what comes while the event loop is busy, backlog's worth up to 2 s, and overflows the same way. A device that stops of itself (unplugged) ends it the same way, with code: 'EDEVICELOST'.
Stop capture. Closes the audio device. What was captured before it still reads, then null.
Immediately close the audio device.
Name of the active backend ('miniaudio', 'process', 'null', 'mediastream').
npm run build # compile native addon locally
npm test # run tests# JS-only change (no native code changed):
npm version patch && git push && git push --tags
npm publish
# Native code changed — rebuild platform packages:
npm version patch && git push && git push --tags
gh run watch # wait for CI
rm -rf artifacts
gh run download --dir artifacts \
-n mic-darwin-arm64 -n mic-darwin-x64 \
-n mic-linux-x64 -n mic-linux-arm64 -n mic-win32-x64
# (fallback) If darwin-x64 CI is unavailable, cross-compile locally:
npx node-gyp@latest rebuild --arch=x64
mkdir -p artifacts/mic-darwin-x64
cp build/Release/mic.node artifacts/mic-darwin-x64/
for pkg in packages/mic-*/; do
cp artifacts/$(basename $pkg)/mic.node $pkg/
(cd $pkg && npm publish)
done
npm publishMIT