https://github.com/analyticsinmotion/decibri
Cross-platform microphone audio capture for Node.js with pre-built binaries. No build tools required.
https://github.com/analyticsinmotion/decibri
audio-capture cross-platform microphone microphone-capture speech-recognition voice voice-ai
Last synced: 2 months ago
JSON representation
Cross-platform microphone audio capture for Node.js with pre-built binaries. No build tools required.
- Host: GitHub
- URL: https://github.com/analyticsinmotion/decibri
- Owner: analyticsinmotion
- License: apache-2.0
- Created: 2026-03-08T15:11:03.000Z (3 months ago)
- Default Branch: main
- Last Pushed: 2026-04-02T14:33:39.000Z (2 months ago)
- Last Synced: 2026-04-03T23:49:14.566Z (2 months ago)
- Topics: audio-capture, cross-platform, microphone, microphone-capture, speech-recognition, voice, voice-ai
- Language: C++
- Homepage: https://decibri.dev/
- Size: 761 KB
- Stars: 5
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
- Security: SECURITY.md
- Agents: AGENTS.md
Awesome Lists containing this project
README
# decibri
Cross-platform microphone audio capture for Node.js.
Pre-built binaries for Windows, macOS, and Linux. No build tools, no SoX, no system dependencies required.
---
## Installation
```bash
npm install decibri
```
Pre-compiled binaries for all supported platforms are bundled inside the package and loaded automatically by `node-gyp-build`. If no binary is available for your platform, it falls back to compiling from source (requires build tools and `libasound2-dev` on Linux).
---
## Usage
### Stream PCM audio from the default microphone
```javascript
const Decibri = require('decibri');
const mic = new Decibri();
mic.on('data', (chunk) => {
// chunk is a Buffer of 16-bit signed integer PCM samples (little-endian)
// default: 16kHz, mono, 1600 frames per chunk (100ms)
});
mic.on('error', (err) => {
console.error('Microphone error:', err);
});
// Stop after 10 seconds
setTimeout(() => mic.stop(), 10000);
```
### Pipe to a file
```javascript
const fs = require('fs');
const Decibri = require('decibri');
const mic = new Decibri({ sampleRate: 44100, channels: 2 });
const out = fs.createWriteStream('capture.raw');
mic.pipe(out);
setTimeout(() => mic.stop(), 5000);
```
### Pipe to a speech engine
```javascript
const Decibri = require('decibri');
const mic = new Decibri({ sampleRate: 16000, channels: 1 });
mic.on('data', (chunk) => {
speechEngine.feed(chunk); // pass raw PCM directly
});
```
### TypeScript
TypeScript definitions are bundled. No `@types/` package needed.
```typescript
import Decibri, { DeviceInfo, DecibriOptions } from 'decibri';
const options: DecibriOptions = { sampleRate: 16000, channels: 1 };
const mic = new Decibri(options);
mic.on('data', (chunk: Buffer) => {
// zero-copy Int16 view over the same memory
const samples = new Int16Array(chunk.buffer, chunk.byteOffset, chunk.length / 2);
});
mic.on('backpressure', () => console.warn('Consumer too slow'));
const devices: DeviceInfo[] = Decibri.devices();
```
---
## API
### `new Decibri(options?)`
Creates a Readable stream that captures from the system default microphone.
| Option | Type | Default | Description |
| --- | --- | --- | --- |
| `sampleRate` | number | `16000` | Samples per second (1000–384000) |
| `channels` | number | `1` | Number of input channels (1–32) |
| `framesPerBuffer` | number | `1600` | Frames per audio callback (64–65536) |
| `device` | number \| string | system default | Device index from `Decibri.devices()` or case-insensitive name substring |
| `format` | `'int16'` \| `'float32'` | `'int16'` | Sample encoding — 16-bit signed integer or 32-bit IEEE 754 float |
| `vad` | boolean | `false` | Enable voice activity detection |
| `vadThreshold` | number | `0.01` | RMS energy threshold for speech (0–1) |
| `vadHoldoff` | number | `300` | Silence holdoff in ms before `'silence'` is emitted |
Standard Node.js `Readable` stream options (e.g. `highWaterMark`) are also accepted.
### `mic.stop()`
Stops microphone capture and ends the stream. Safe to call multiple times.
### Event: `'backpressure'`
Emitted when `push()` returns `false`, meaning the stream's internal buffer is full and the consumer is reading too slowly. Because a microphone cannot be paused, audio chunks will continue to arrive. Callers should drain the stream or drop data to avoid unbounded memory growth.
### Event: `'speech'`
Emitted when the RMS energy of an audio chunk crosses `vadThreshold`. Requires `vad: true`.
### Event: `'silence'`
Emitted when audio stays below `vadThreshold` for `vadHoldoff` ms after a speech period. Requires `vad: true`.
### `mic.isOpen`
`true` if the microphone is currently capturing.
### `Decibri.devices()`
Returns an array of available input devices on the system.
```javascript
const devices = Decibri.devices();
// [
// { index: 0, name: 'Built-in Microphone', maxInputChannels: 1, defaultSampleRate: 44100, isDefault: true },
// ...
// ]
```
### `Decibri.version()`
Returns version information for decibri and the bundled PortAudio.
```javascript
Decibri.version();
// { decibri: '1.0.0', portaudio: 'PortAudio V19.7.0-devel...' }
```
---
## Examples
Runnable examples are in [`examples/`](examples/).
### Capture to WAV file
No external dependencies.
```bash
node examples/wav-capture.js
# writes capture.wav (5 seconds, 16 kHz mono)
```
### Stream to WebSocket
The WebSocket examples require the `ws` package, which is **not** bundled with decibri. Install it before running:
```bash
npm install ws
```
Then in two terminals:
```bash
# Terminal 1 — start receiver
node examples/websocket-server.js
# Terminal 2 — start streaming
node examples/websocket-stream.js
# streams raw PCM to ws://localhost:8080
```
---
## Audio format
All audio is captured as **16-bit signed integer PCM, little-endian**. This is the raw format expected by most speech and wake-word engines (Vosk, sherpa-onnx, whisper.cpp, openWakeWord).
Each `data` event emits a `Buffer` where every 2 bytes is one 16-bit sample:
```javascript
mic.on('data', (chunk) => {
const samples = new Int16Array(chunk.buffer, chunk.byteOffset, chunk.length / 2);
// samples[0], samples[1], ...
});
```
---
## Platform Support
### Pre-built binaries (zero setup)
| Platform | Architecture | Audio Backend |
| --- | --- | --- |
| Windows 11 | x64 | WASAPI |
| macOS (Apple Silicon) | arm64 | CoreAudio |
| Linux | x64 | ALSA |
| Linux | arm64 | ALSA |
### Source build fallback (requires build tools)
| Platform | Requirements |
| --- | --- |
| macOS Intel (pre-2020) | Xcode CLI tools: `xcode-select --install` |
| Windows ARM64 | Visual C++ Build Tools |
### Not supported
| Platform | Reason |
| --- | --- |
| Windows 32-bit | N-API native addons require 64-bit |
---
## Building from source
Requires Node.js >= 18, node-gyp, and platform build tools.
**Linux:**
```bash
sudo apt-get install -y build-essential libasound2-dev
```
**macOS:**
```bash
xcode-select --install
```
**Windows:**
Install [Visual C++ Build Tools](https://visualstudio.microsoft.com/visual-cpp-build-tools/).
Then:
```bash
git clone --recurse-submodules https://github.com/analyticsinmotion/decibri.git
cd decibri
npm install
npm run build
```
---
## How it works
decibri wraps [PortAudio](http://www.portaudio.com/), the standard cross-platform audio I/O library, as a Node.js native addon using [N-API](https://nodejs.org/api/n-api.html). PortAudio is compiled from source and statically linked, so there is no system PortAudio dependency.
The native addon opens the default input device, runs a PortAudio callback in an audio thread, and forwards PCM chunks to JavaScript via an N-API `ThreadSafeFunction`. The JavaScript layer wraps this in a standard Node.js `Readable` stream.
---
## License
Apache-2.0 © [Analytics in Motion](https://github.com/analyticsinmotion)