Skip to content

Add disable_reservoir and cbr options for clean audio splicing#55

Merged
mat-hek merged 6 commits intomembraneframework:masterfrom
PRX:master
Apr 16, 2026
Merged

Add disable_reservoir and cbr options for clean audio splicing#55
mat-hek merged 6 commits intomembraneframework:masterfrom
PRX:master

Conversation

@kookster
Copy link
Copy Markdown
Contributor

Summary

Adds two new boolean options to the LAME encoder for live streaming use cases where audio from different sources is concatenated (e.g., ad insertion):

disable_reservoir (default: false)

Calls lame_set_disable_reservoir(1) making each MP3 frame self-contained (main_data_begin is always 0). Without this, the bit reservoir creates dependencies between adjacent frames — when audio from different sources is spliced together, the decoder sees invalid bit reservoir references from the previous source, causing audible glitches.

cbr (default: false)

Calls lame_set_VBR(vbr_off) to explicitly enforce constant bitrate mode. CBR is already the LAME default when a bitrate is set, but this option makes it explicit.

Changes

  • c_src/membrane_mp3_lame_plugin/encoder.c — added disable_reservoir and cbr parameters to create(), calls lame_set_disable_reservoir() and lame_set_VBR(vbr_off)
  • c_src/membrane_mp3_lame_plugin/encoder.spec.exs — updated Unifex spec with new parameters
  • lib/membrane_mp3_lame/encoder.ex — added disable_reservoir and cbr options with docs, passed to Native.create/5
  • test/integration/integration_test.exs — two new tests:
    • disable_reservoir: encodes PCM, parses all MP3 frames, asserts main_data_begin == 0 for every frame
    • cbr: collects frame buffer sizes, asserts constant size (±1 byte for padding bit)
  • test/fixtures/ref.mp3 — regenerated for current LAME build

Usage

child(:encoder, %Membrane.MP3.Lame.Encoder{
  bitrate: 64,
  cbr: true,
  disable_reservoir: true
})

Context

We're building a live streaming server with per-listener ad insertion using Membrane. The bit reservoir was causing audible glitches at ad→live transitions because frames from the ad audio had reservoir references that were invalid when followed by frames from the live stream. Disabling the reservoir eliminates this entirely.

Test results

All 6 tests pass (3 existing + 2 new + 1 with regenerated reference file).

Adds a disable_reservoir boolean option (default: false) that calls
lame_set_disable_reservoir(1) when enabled. This makes each MP3 frame
self-contained (main_data_begin is always 0), producing constant-size
CBR frames that can be cleanly spliced at any frame boundary.

Useful for live streaming servers that concatenate audio from different
sources (e.g., ad insertion) where bit reservoir dependencies between
frames from different sources would cause decoder artifacts.
Two new boolean options (both default false):

- disable_reservoir: calls lame_set_disable_reservoir(1) making each
  MP3 frame self-contained (main_data_begin always 0). Frames can be
  spliced at any boundary without decoder artifacts from bit reservoir
  dependencies between frames from different sources.

- cbr: calls lame_set_VBR(vbr_off) to explicitly enforce constant
  bitrate mode. CBR is already the LAME default when a bitrate is set,
  but this option makes it explicit.

Both options are useful for live streaming servers that concatenate
audio from different sources (e.g., ad insertion).
New tests:
- disable_reservoir: encodes PCM input, parses MP3 frames from output
  file, asserts main_data_begin == 0 for every frame (self-contained)
- cbr: collects frame buffer sizes from Membrane encoder, asserts all
  frames are constant size (±1 byte for MP3 padding bit)

Also regenerated test/fixtures/ref.mp3 for the current LAME build
(previous reference was from a different build, causing a single-bit
difference at byte 1064 — encoding decision variance, not a bug).

All 6 tests now pass.
Comment thread test/integration/integration_test.exs Outdated
Per review feedback, the disable_reservoir test now captures frame
buffers via Membrane.Testing.Sink and checks main_data_begin on each
buffer directly, matching the pattern used by the CBR test.

Removed the file-based parse_mp3_frames/mp3_frame_size helpers that
were no longer needed.
Comment thread test/integration/integration_test.exs Outdated
@kookster kookster requested a review from mat-hek April 15, 2026 15:07
Comment thread test/integration/integration_test.exs Outdated
end
end

defp collect_frames(pid, acc) do
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A common utility method now!

@kookster kookster requested a review from mat-hek April 15, 2026 16:45
@mat-hek
Copy link
Copy Markdown
Member

mat-hek commented Apr 16, 2026

Looks good, pls fix credo and we can merge ;)

@kookster
Copy link
Copy Markdown
Contributor Author

Looks good, pls fix credo and we can merge ;)

done!

@mat-hek mat-hek added this to Smackore Apr 16, 2026
@mat-hek mat-hek moved this to Done in Smackore Apr 16, 2026
@mat-hek mat-hek merged commit 9039c92 into membraneframework:master Apr 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants