Skip to content

Row-sum-preserving quantization (RBJ round 2) + Appendix D, on Tap House Rules#33

Open
tap wants to merge 2 commits into
mainfrom
claude/rbj-round2
Open

Row-sum-preserving quantization (RBJ round 2) + Appendix D, on Tap House Rules#33
tap wants to merge 2 commits into
mainfrom
claude/rbj-round2

Conversation

@tap

@tap tap commented Jul 8, 2026

Copy link
Copy Markdown
Owner

Context

RBJ's MATLAB script arrived (validated at R=512/N=32 — exactly economy()'s shape) with one more claim: "for DC, this should get you infinite S/N ratio... for every phase or fractional delay, the FIR coefficients must add to 1" — plus a warning that 16-bit coefficient quantization needs "tricky things."

Rebased-by-port onto the Tap House Rules migration: the branch is now a single commit re-applying everything in the new dialect (snake_case, .h, mandatory braces, new clang-format).

Verified, then fixed

  • In double, the property was already ours for free: the image_zeros designs' branch-DC sums are uniform to a spread of 1.8×10⁻¹⁵ (machine epsilon) — zeros at k·fs and branch-DC uniformity are the same fact stated in two domains. The plain fast() design spreads 4.7×10⁻⁶ (its stopband leakage at fs). New Kaiser.CompensatedBranchSumsAreUniform pins this.
  • His 16-bit warning pointed at a live defect: independent per-tap rounding re-broke the property by several LSB — the exact bias measured in k·fs transmission zeros, the economy preset, the program-weighted metric, and the book's epilogue #31 and papered over with a widened DcGain tolerance.

The fix

Largest-remainder row-sum-preserving quantization in polyphase_filter_bank's constructor (integer coefficient types only): each row's total rounding residual is distributed to the taps rounded furthest from it, so every row sums to exactly llround(exact × k_coeff_scale). The extra row L is copied from quantized row 0, keeping the μ-wrap continuity invariant bit-exact through the correction.

Measured

  • DC passes the Q15 and Q31 converters bit-exactly at every fractional delay — 0 LSB deviation over a 256-point μ sweep (was −6…−8 LSB)
  • DcGain tolerances tightened 12 → 1; new RowSumsAreExactQ15/Q31 pin every row to exactly k_coeff_scale
  • Host suite 73/73 under -Werror (examples enabled); constructor cost is ~L·T doubles of bookkeeping — noise even on the M33 icount ledger

Riders

  • Completes the style migration in examples/alsa_bridge.cpp, which the pass left half-renamed and uncompilable when examples are enabled: converter_state members, Config/servo/status field spellings, and six struct members declared camelCase but used snake_case. (Separately: the migration minted k_k_fs/k_k_eps in the tests by double-prefixing the old kFs/kEps — left untouched here, flagging for the style owner.)
  • Book: the epilogue gains its postscript ("infinite S/N at DC, delivered in 16 bits", with the pw_row_sum live excerpt), and Appendix D: The two budgets — a first-person essay on real-time instinct meeting the million-point FFT, with a measured timing table and runnable one-liners.

🤖 Generated with Claude Code

https://claude.ai/code/session_01HuAFfoeD5a5Xe5aGNA16M9

PR #33's content re-applied on top of the snake_case migration: the
largest-remainder row-sum correction in polyphase_filter_bank (with the
quantized-row-0 copy for the extra row), k_coeff_scale on all three
sample_traits, the tightened DcGain gates (12 -> 1; measured 0 LSB over
a 256-point mu sweep), RowSumsAreExact and CompensatedBranchSumsAreUniform
tests, the epilogue postscript, and Appendix D (The two budgets).

Also completes the migration in examples/alsa_bridge.cpp, which the
style pass left half-renamed and uncompilable when examples are enabled
(converter_state members, Config/servo/status field spellings, and six
struct members declared camelCase but used snake_case).

https://claude.ai/code/session_01HuAFfoeD5a5Xe5aGNA16M9
@tap tap force-pushed the claude/rbj-round2 branch from a53b5c1 to 1cf9762 Compare July 9, 2026 14:32
@tap tap changed the title Row-sum-preserving coefficient quantization (RBJ round 2): bit-exact DC in fixed point Row-sum-preserving quantization (RBJ round 2) + Appendix D, on Tap House Rules Jul 9, 2026
(1) examples/alsa_bridge.cpp fully migrated: the remaining consistently-
camelCase internals (g_stop, on_sigint, state_name, parse_args,
open_device, dump_failed, buf_frames), the cli_args and alsa_device
types, and the target_latency_frames mention in the help text -- the
file now reads one dialect throughout instead of the half-and-half the
style pass left it in.

(2) k_k_ -> k_ repo-wide (14 files: tests, three headers,
drifting_clocks): the migrator double-prefixed constants that already
carried the k (kFs -> k_k_fs). Thirteen distinct names, no collisions
with existing k_ identifiers. Bonus alignment: the book's prose was
already migrated to the intended single-prefix spellings
(k_channel_parallel in the decision log and resampler chapter), so code
and book agree again; the live excerpts pick the fix up automatically.

73/73 under -Werror with examples enabled; book builds clean.

https://claude.ai/code/session_01HuAFfoeD5a5Xe5aGNA16M9
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