Skip to content

Port the intensity stereo threshold table - #200

Merged
thomas-vilte merged 1 commit into
pion:mainfrom
thomas-vilte:fix/intensity-table
Aug 12, 2026
Merged

Port the intensity stereo threshold table#200
thomas-vilte merged 1 commit into
pion:mainfrom
thomas-vilte:fix/intensity-table

Conversation

@thomas-vilte

Copy link
Copy Markdown
Member

Description

intensityStartBand was a hand-written staircase with six effectiveKbps ranges returning 8/12/16/17/19/20, plus a ±1 deadband for hysteresis.

libopus uses a table of 21 thresholds with per-entry hysteresis (celt_encoder.c):

static const opus_val16 intensity_thresholds[21]=
  {  1, 2, 3, 4, 5, 6, 7, 8,16,24,36,44,50,56,62,67,72,79,88,106,134};
static const opus_val16 intensity_histeresis[21]=
  {  1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 4, 5, 6,  8, 8};

st->intensity = hysteresis_decision((opus_val16)(equiv_rate/1000),
      intensity_thresholds, intensity_histeresis, 21, st->intensity);
st->intensity = IMIN(end,IMAX(start, st->intensity));

This PR ports that table, hysteresis_decision (celt/bands.c), and the equiv_rate calculation that feeds it (celt_encoder.c:1926). equiv_rate also accounts for the per-packet overhead that libopus subtracts:

equiv_rate = ((opus_int32)nbCompressedBytes*8*50 << (3-LM)) - (40*C+20)*((400>>LM) - 50);

The hysteresis in libopus isn't symmetric: each entry has its own margin, and the margin is only applied when moving from prev. The old ±1 deadband isn't equivalent — it can move across multiple bands within a few frames and doesn't respect the per-entry margins.

Measurement

The intensity band is encoded in the bitstream, so I read it directly by decoding libopus packets with the pion decoder.

Across 3000 music frames at each bitrate, the value was constant, so the previous implementation disagreed completely at the affected rates:

bitrate libopus pion before pion now
24000 9 8 9
32000 10 8 10
48000 12 12 12
64000 15 16 15
96000 19 19 19
128000 20 20 20

Frame-by-frame agreement goes from 0.0% → 100% at 24, 32, and 64 kbps, and stays at 100% where the old implementation already matched.

At 48 and 128 kbps the old staircase happened to land on the right value. At the other rates it was off by one or two bands. Being too low matters because pion switches to intensity stereo earlier, discarding phase information in bands that libopus would still encode normally.

Dependency

The 24 kbps result also depends on the PR that accounts for the TOC byte in the payload budget. Without that change, equiv_rate is 24000 instead of 23600 and the table selects the next entry. The other bitrates are not affected by this
dependency.

Tests

TestIntensityBandForRate covers the six values against the output from opus_demo, and TestIntensityBandForRateHysteresis covers the per-entry hysteresis margin: at 24 kbps the band is 10, but when coming from band 9 it stays at 9 until the rate exceeds 26.

Reference issue

Part of #34.

@thomas-vilte
thomas-vilte requested a review from FrantaBOT August 11, 2026 22:42
@codecov

codecov Bot commented Aug 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 85.71429% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 93.11%. Comparing base (a38c746) to head (53b0ec2).

Files with missing lines Patch % Lines
internal/celt/hysteresis.go 80.00% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main     #200   +/-   ##
=======================================
  Coverage   93.10%   93.11%           
=======================================
  Files          58       58           
  Lines       10263    10248   -15     
=======================================
- Hits         9555     9542   -13     
+ Misses        500      499    -1     
+ Partials      208      207    -1     
Flag Coverage Δ
go 93.11% <85.71%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@thomas-vilte
thomas-vilte merged commit a6430a3 into pion:main Aug 12, 2026
20 checks passed
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.

2 participants