Skip to content

feat: add a real WMAv2 encoder - #7

Merged
cloud-hai-vo merged 2 commits into
mainfrom
feat/wma-encoder
Aug 6, 2026
Merged

feat: add a real WMAv2 encoder#7
cloud-hai-vo merged 2 commits into
mainfrom
feat/wma-encoder

Conversation

@cloud-hai-vo

Copy link
Copy Markdown
Contributor

Summary

  • Implements WmaEncoder/WmaEncoderSession/WmaFrameEncoder/AsfContainerWriter, mirroring WmaDecoder's bitstream format exactly (gain, per-band exponents via the shared AAC scalefactor Huffman table, run-length+Huffman coefficient coding) so this library's own encode/decode round-trips correctly for mono and independently-coded stereo.
  • Shares exponent band/window/run-level table construction between encoder and decoder via WmaTables (moved out of WmaDecoder's local functions).
  • Wires WmaEncoderSession into AudioCutter as a .wma Convert/Cut destination.
  • Flips WMA Encode to supported (and corrects a stale "mono-only" decode claim) across README, llms.txt, llms-full.txt, and the docs site.

Two real bugs were found and fixed via the round-trip tests before landing:

  1. SolveTotalGain was solving the frame's gain from the exponent value clamped to PowTable's representable range (~866000 max) instead of the true unclamped peak MDCT coefficient magnitude (Mdct.Forward is unnormalized, so raw coefficients routinely reach into the millions for full-scale PCM). This produced grossly mis-scaled quantization.
  2. WriteCoefficients unconditionally wrote a trailing STOP code, but WmaDecoder's coefficient loop terminates implicitly once its cumulative offset reaches coefsEnd — it never reads a STOP code in that case. Whenever a block's last coefficient before coefsEnd was itself nonzero, the extra STOP code was an unconsumed stray symbol that desynced everything decoded afterward (the next channel's exponents, or the next frame). This only surfaced under real broadband audio, not sparse single-tone test signals.

Test plan

  • dotnet build --configuration Release — 0 warnings, 0 errors
  • dotnet test --configuration Release (net8.0/net9.0/net10.0) — 115 passed, 1 pre-existing skip, 0 failed
  • New WmaEncoderTest: mono round-trip SNR, stereo round-trip SNR, whole-buffer API smoke test, invalid-channel-count validation
  • New AudioCutterTest.Convert_WavToWma/Cut_WavToWma using real (non-tone) fixture audio, exercising the coefficient path far more than a pure tone does

🤖 Generated with Claude Code

cloud-hai-vo and others added 2 commits August 6, 2026 13:02
Implements WmaEncoder/WmaEncoderSession/WmaFrameEncoder/AsfContainerWriter,
mirroring WmaDecoder's format exactly so this library's own encode/decode
round-trips correctly (mono and independently-coded stereo). Shares exponent
band/window/run-level table construction between encoder and decoder via
WmaTables instead of decoder-local functions.

Wires WmaEncoderSession into AudioCutter as a .wma Convert/Cut destination
and flips WMA Encode to supported across README/llms.txt/llms-full.txt/docs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…bles

WmaFrameEncoder had its own private copies of GetFrameLengthBits,
BuildExponentBands, BuildSineWindow, and TotalGainToBits instead of the
WmaTables versions WmaDecoder already uses. They were byte-for-byte
identical so nothing was broken, but these four functions define the exact
encoder/decoder bitstream contract -- an independent edit to one copy
without the other would silently desync encode/decode with no compiler
error, the same class of bug as the coefficient stop-code fix earlier in
this branch. TotalGainToBits was also duplicated as a WmaDecoder-local
function pre-dating this branch; moved it into WmaTables too so both sides
share a single implementation.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@cloud-hai-vo
cloud-hai-vo merged commit dc085fa into main Aug 6, 2026
3 checks passed
@cloud-hai-vo
cloud-hai-vo deleted the feat/wma-encoder branch August 6, 2026 08:09
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.

1 participant