Skip to content

Comparison: add SpeexDSP (both arithmetic builds), re-pin r8brain at 7.6 - #75

Merged
tap merged 2 commits into
mainfrom
claude/sample-rate-solutions-comparison-mqc190
Oct 9, 2026
Merged

tap merged 2 commits into
mainfrom
claude/sample-rate-solutions-comparison-mqc190

Conversation

@tap

@tap tap commented Oct 9, 2026 •

Copy link
Copy Markdown
Owner

What this changes

PR 1b of the comparison plan (after #73; bridge and rational follow). SpeexDSP's resampler joins the async comparison in both of its arithmetic builds, and r8brain moves from 7.5 to the upstream tip (7.6).

  • SpeexDSP, pinned at the SpeexDSP-1.2.1 commit, compiled from resample.c twice (cmake/speexdsp.cmake: FIXED_POINT and FLOATING_POINT under separate RANDOM_PREFIXes) for the host bench, the embedded counts (four new comparison binaries: quality 10 and quality 4 per build, in compare.yml) and the notebook (a two-function shim over both builds, tools/compare_shim/).
  • Notebook: two SpeexDSP quality rows, a SampleRateTap Q15 row through the new Q15 C ABI, and a sweep over Speex's quality knob (latency and 20 kHz gain per quality, both builds).
  • update_compare_docs.py derives the SpeexDSP rows when the record carries them, the fixed-point rows ratioed against balanced Q15; COMPARISON.md gets the quality rows, a Speex latency section, a per-target reading paragraph, the landscape row, and a refreshed host table; README.md names the new dependency.
  • The record (bench/compare_counts.json) is re-harvested from compare.yml run 37970395757 on this branch's first commit, the first run with the Speex binaries; the second commit carries it and the regenerated block.
  • r8brain 7.6: upstream's one change is faster filter calculation. Measured as construction only: every steady-state count is identical to 7.5's; construction moves by −0.1 M of 38 M on the M33.

Why

SpeexDSP is the one resampler library with a fixed-point build, so it is the first competitor the Q15 rows can be measured against on their own terms: until now the Q15 datapath's cost tables had no row to be read against, and the quality table had no 16-bit competitor beside it.

Verification

  • Quality (notebooks/asrc_comparison.ipynb, committed executed): every existing row reproduces to the displayed precision. SpeexDSP float at quality 10 measures −133.7 dB THD+N / 149.1 dB DR, where balanced sits, not at the format ceiling the other three libraries reach; its fixed-point build −80.2 dB / 97.4 dB beside SampleRateTap Q15 at −76.6 dB / 97.3 dB (the README's "77 dB, format-limited"). The sweep's assertions pin the knee: no quality is flat to 20 kHz (−0.42 dB from quality 4 up), so quality 4 (32 frames) is the matched row beside quality 10 (128 frames).
  • Embedded counts: CI-measured (run 22, above). The record reproduces the local M55/M33/Hexagon derivation cell for cell (pinned toolchains, the tree's plugin). SpeexDSP fixed-point at quality 4 costs 1.9× / 2.0× / 1.3× the Q15 datapath (M55 / M33 / Hexagon), quality 10 6.9× / 7.3× / 4.1×. Its float build is single-precision throughout, so on the two soft-double targets it undercuts SampleRateTap's float row; the doc says which row to read there. Every pre-existing steady-state cell is unchanged from Comparison freshness gate, construct-only ratchet legs, async comparison refresh #73's record.
  • Host table: re-measured in one session on this host (all 25 subjects, median of 5); the pre-existing ratios held within ~15 % of the 2026-09-25 session, and the reading guide carries the new session's numbers. SpeexDSP is the slowest subject on x86 at every setting (4× to 14× behind the matching SampleRateTap row).
  • Gates run locally: retired-words clean, clang-format and clang-tidy clean on the three changed C++ units, update_compare_docs.py idempotent on the committed record, the workflow YAML parses, the book builds, native runs of all four Speex comparison binaries report ok.
  • First on CI: the compare.yml harvest with the Speex binaries ran end to end on this branch (green, record above); the Comparison build smoke job is the first CI build of the Speex shim on the hosted compilers.

Notes for the reviewer

  • Notebooks re-executed, because the subject set grew. Committed executed.
  • New third-party fetch (BSD-3) under the same rule as r8brain: opt-in comparison builds only, never linked into the library or its tests. The two libraries' compile definitions are PRIVATE by necessity, because a translation unit that reads the header for both builds has to define them itself; speexdsp.cmake says so.
  • Speex's speex_resampler_* names are macros over RANDOM_PREFIX, expanded at the use site, so the shim and the bench call the prefixed symbols directly and undefine the include guard between the two reads of the header. It looks odd; the comment at each site explains why.
  • The "0.1×" cells for Speex's float build on the M33 and Hexagon are honest: SampleRateTap's float datapath accumulates in double and is soft-float there, which is why the README directs those targets to Q15. The derived table keeps the one rule (float rows vs. balanced float, fixed rows vs. Q15) and the prose carries the reading.
  • Provenance toolchain string now comes from the runner's package versions (15:13.2.rel1-2 is arm-none-eabi-gcc 13.2.1); the prose above the block says the same thing in the human form.

🤖 Generated with Claude Code

https://claude.ai/code/session_01G3HxzEGiZp7jStoMuYNuBK

claude added 2 commits October 9, 2026 18:01
PR 1b of the comparison plan. SpeexDSP's resampler is the one competitor
with a fixed-point build, so it is the first that the Q15 rows can be
measured against on their own terms. It is fetched at the SpeexDSP-1.2.1
commit and compiled from resample.c twice (cmake/speexdsp.cmake, FIXED_POINT
and FLOATING_POINT under separate RANDOM_PREFIXes), for the host bench, the
embedded counts (four new comparison binaries, quality 10 and quality 4 per
build, in compare.yml) and the notebook (a two-function shim, both builds).

The notebook adds the two SpeexDSP quality rows and a SampleRateTap Q15 row
through the new Q15 C ABI, and a sweep over the quality knob: no setting is
flat to 20 kHz (-0.42 dB from quality 4 up), so quality 4 is the row that
matches balanced's latency class (32 frames) beside quality 10's ceiling.
update_compare_docs.py derives the SpeexDSP rows when the record carries
them, the fixed-point ones ratioed against balanced Q15.

r8brain moves from 7.5 to the upstream tip (7.6): faster filter calculation,
construction only.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3HxzEGiZp7jStoMuYNuBK
The record from the first compare.yml run with the four SpeexDSP binaries,
on this branch's head, and the block it derives. Every pre-existing
steady-state cell is unchanged; r8brain 7.6 moves its M33 construction by
0.1 M. The SpeexDSP rows reproduce the local M33/M55/Hexagon derivation
cell for cell.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3HxzEGiZp7jStoMuYNuBK
@tap
tap merged commit 99a9fd7 into main Oct 9, 2026
30 checks passed
@tap
tap deleted the claude/sample-rate-solutions-comparison-mqc190 branch October 9, 2026 19:22
tap pushed a commit that referenced this pull request Oct 10, 2026
The comparison plan (#73, #75, #76, #77) and its follow-up (#78) closed;
this is the plan for what the family's plans and ledgers still carry
open, sequenced into twelve weeks in three blocks, with "production-ready"
defined as a thirteen-row checklist per engine so the verdict can be
checked rather than believed.

Drafted and then audited by two independent adversarial reviews (one
against the trees' facts and settled decisions, one against engineering
judgement). The audit changed the draft materially: the book's bridge
chapters are done (#68), so they are off the list; rational has no
cross-engine validation by design (scipy and decimate.h are its legs);
TSan runs async only; four host compilers and three QEMU targets, not the
draft's counts; the +1.9 % M55 ratio23_q15_eco drift is ab8fea3's stated
move, not a DspTap bump, and the chain rows sit +926/+1,102; the hardware
block never ran bridge or rational on silicon; nothing builds with
-fno-exceptions; TSan has never seen the converter with two threads; no
footprint, stack or worst-single-call figure exists; Setup 1's "< 1 ppm"
criterion tests the counting, not the servo, and cheap USB dongles may not
own a crystal; the timestamp servo is a four-to-five-week extension, not an
overload, and must not be frozen in the release that adds it; "C ABIs
frozen" needs a reconciled NULL model, an extension rule, SOVERSION and a
compat test. Section 7 holds the six decisions the maintainer takes before
week 1, each with a recommendation.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3HxzEGiZp7jStoMuYNuBK
tap pushed a commit that referenced this pull request Oct 10, 2026
The comparison plan (#73, #75, #76, #77) and its follow-up (#78) closed;
this is the plan for what the family's plans and ledgers still carry
open, sequenced into twelve weeks in three blocks, with "production-ready"
defined as a thirteen-row checklist per engine so the verdict can be
checked rather than believed.

Drafted and then audited by two independent adversarial reviews (one
against the trees' facts and settled decisions, one against engineering
judgement). The audit changed the draft materially: the book's bridge
chapters are done (#68), so they are off the list; rational has no
cross-engine validation by design (scipy and decimate.h are its legs);
TSan runs async only; four host compilers and three QEMU targets, not the
draft's counts; the +1.9 % M55 ratio23_q15_eco drift is ab8fea3's stated
move, not a DspTap bump, and the chain rows sit +926/+1,102; the hardware
block never ran bridge or rational on silicon; nothing builds with
-fno-exceptions; TSan has never seen the converter with two threads; no
footprint, stack or worst-single-call figure exists; Setup 1's "< 1 ppm"
criterion tests the counting, not the servo, and cheap USB dongles may not
own a crystal; the timestamp servo is a four-to-five-week extension, not an
overload, and must not be frozen in the release that adds it; "C ABIs
frozen" needs a reconciled NULL model, an extension rule, SOVERSION and a
compat test. Section 7 holds the six decisions the maintainer takes before
week 1, each with a recommendation.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3HxzEGiZp7jStoMuYNuBK
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