Skip to content

feat(audio): Compressed voice recordings with @braintrust/audio - #2658

Merged
David Elner (delner) merged 4 commits into
mainfrom
delner/audio-codecs
Oct 27, 2026
Merged

David Elner (delner) merged 4 commits into
mainfrom
delner/audio-codecs

Conversation

@delner

@delner David Elner (delner) commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Based on #2657

To provide more efficient, out-of-the-box encoding of audio, this adds opus and ogg plugins as middleware.

What it does

By default, voice recordings upload as standard Ogg Opus files instead of raw PCM.

Install the package to activate:

npm install @braintrust/audio

Encoder settings are optional and declarative:

import { configureInstrumentation } from "braintrust/instrumentation";

configureInstrumentation({
  integrations: {
    livekit: {
      audio: {
        codec: { name: "opus", options: { bitrate: 32_000 } },
        container: { name: "ogg" },
      },
    },
  },
});
  • Optional. braintrust doesn't depend on this package. Without it, recordings fall back to raw PCM, with a warning.
  • Per integration. Each integration owns its defaults: LiveKit records Opus in Ogg.
  • Opt out in code. Set codec: { name: "pcm" } to always record raw PCM.

How it works

livekit.audio ─► encoding config ─► loads @braintrust/audio ─► createStack(config) ─► AudioRecorder
                  (+ defaults)       (Node only, once)           opus → ogg
  • Opus encodes speech with libopus (WebAssembly, via @audio/encode-opus) 20 ms at a time, pausing for the event loop every second of audio, so the agent stays responsive.
  • Ogg wraps the packets in a standard file with no extra dependencies. The file plays back at exactly the recorded length.
  • Declarative config, middleware underneath. The SDK turns codec and container into the internal middleware pipeline; users never write middleware.

Performance

Measured on one 44-second recording segment: about 0.4 s of CPU in total, with the longest pause to the event loop around 10 ms, and 4 MB of PCM uploaded as 344 KB.

David Elner (delner) added a commit that referenced this pull request Oct 27, 2026
To facilitate transforms on recorded audio, for the purposes of
compression, encoding, or filtering, this adds a configurable
hook+middleware stack to the audio recording pipeline.

## What it does


Moves recording out of the LiveKit plugin into a reusable
**`AudioRecorder`** with an internal **middleware pipeline** that can
transform each segment before upload.

No outward changes to data or UX.

[Opus compression and Ogg plugins leverage this pipeline in a separate,
optional
package.](#2658)

## Design

Internal pipeline:

<img width="492" height="642" alt="Screenshot 2026-10-20 at 3 20 41 PM"
src="https://github.com/user-attachments/assets/77f6064c-fe74-48f5-a639-a5f2331a8bb7"
/>

- **`AudioRecorder`** places PCM frames from each source on a shared
timeline and cuts bounded segments. It runs each segment through the
middleware and hands the result to the integration.
- **Middleware** works like Rack or Koa: `(segment, meta, next)`,
transform on the way in, observe on the way out. Each recording builds
its own stack, so state is never shared between calls. Middleware can
change the format or channel layout and record facts in
`meta.attributes`, published with the file.
- **The integration** (LiveKit) owns capture, playback, spans and
uploads. It publishes what each file contains: format, channels and
sources.
- **Bounded and safe.** One segment is in flight at a time, and buffered
audio has a memory limit. A failing middleware omits its segment without
affecting the call.

```
js/src/
  pcm-buffer.ts        PCM timeline, resampling, segment cuts (renamed from pcm-audio.ts)
  audio-recorder.ts    buffering, segment rotation, finish
  audio-middleware.ts  the middleware contract and runner
```
Base automatically changed from delner/audio-recorder-pipeline to main October 27, 2026 19:16
@delner
David Elner (delner) merged commit 4401a24 into main Oct 27, 2026
117 of 119 checks passed
@delner
David Elner (delner) deleted the delner/audio-codecs branch October 27, 2026 21:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants