The decimator computed each output as one sequential f32 dot product over
167 taps, which is bound by the 4-cycle latency of the float addition. Use
eight independent partial sums (combined pairwise) so the compiler can
vectorise the loop.
This changes the input of the SILK encoder at rounding level, so the
bitstream differs from before; decoded quality and size are unchanged to
within noise (see the PR for SNR, segmental SNR and size at several
bitrates). A test checks the dot product against an f64 reference.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
pvq_search scored every coefficient in a serial compare-and-select chain per
pulse. Score all coefficients branch-free in 8-lane chunks instead (square
over energy, one division), take the maximum, and run the original exact
cross-multiplied test only on the entries within 1e-5 of it, which is
normally a single entry whose index comes from a lane-wise count. The chosen
pulse, and with it the bitstream, is unchanged; a test compares the result
with the old scalar search on 6000 random, sparse, tied and extreme-scale
vectors. The pre-projection also uses the truncating cast instead of a floor
libm call (the value is non-negative).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Every FFT stage was a generic DFT butterfly: p * p complex multiplications
per butterfly (16 for radix 4) and a modulo on every twiddle index. Add
kiss_fft-style kernels for radix 2, 3 and 4 (three twiddle multiplications
and eight additions for radix 4, a sqrt(3)/2 constant for radix 3, +-j done
by swapping parts). The generic butterfly stays for radix 5, which occurs
once per transform, with its roots precomputed. The modulo is dropped: the
twiddle index q * k * tw_stride is always below n.
The results differ from before at f32 rounding level, so a few packets
change on speech and hybrid input (CELT music stays byte-identical). A new
test checks the FFT against a direct DFT for every radix combination up to
the 480-point frame size.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The contour search tries 5 base lags times 34 codebook contours times 4
subframes, 680 normalised correlations, but only about 4 x 30 distinct
(subframe, lag) pairs occur. Cache them per call. The values are the same
f64 results as before, so the bitstream does not change (cmp on 24 SILK and
hybrid configurations of 90 s, mono and stereo, speech and music).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Each root was refined with 40 bisection steps, which shrinks the bracket to
about 3e-15 rad. A Q15 NLSF step is 9.6e-5 rad, so 20 steps (3e-9 rad) are
far more than the quantiser can see. The saved cos calls are the remaining
bulk of the root search after the grid table.
The coded packet streams are byte-identical to the 40-step search on 24
configurations of 90 s: speech-like and music-like input, mono and stereo,
SILK NB 8k, MB 12k, WB 16k and 32k, hybrid SWB 32k and FB 64k.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The LPC to LSF conversion scans a 1024-point grid for sign changes of two
cosine series and called libm cos for every series term, about 21,000 calls
per channel and frame (30-40 % of SILK and hybrid encode time). Compute
cos(m * w) for every grid point and multiple once, with the same expression
as before, so the values are bit-identical and the bitstream does not change.
Only the bisection steps still call cos.
A test compares the new root search against the previous one bit for bit on
random symmetric polynomials; a2nlsf rejects orders beyond the table.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
encode_pulses rebuilt the full V(n, k) table with saturating u64 adds for
every coded band and summed the prefixes through a closure. Build the table
once (n <= 176, k <= 128, saturated at u32::MAX, 91 KB) and sum the prefixes
over a contiguous row slice. Shapes outside that range, which the band
layout never produces, fall back to a local table. The unused scratch Vec
is gone from BandEnc.
The bitstream is unchanged; a test keeps the old per-call encoder and
compares the coded bytes on random codewords over the whole (n, k) range.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
opusenc: the SILK state is used through if-let / let-else instead of expect("SILK state").
vp9enc: the motion-search SAD and inter prediction return early without a reference frame
instead of expect("reference"). Denies the panic lints in both crates.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Application/Mode/Bandwidth API with OpusEncoder::with_application and
with_mode; SILK analysis-by-synthesis encoder mirroring the decoder's
fixed-point reconstruction with noise feedback, LTP, NLSF quantisation and
per-frame rate control; hybrid CELT from band 17 in the shared range coder;
pre-skip 120 in every mode. Oracle tests against filmcraft-opus and ffmpeg
(native opus + libopus).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New L0 crate effectcraft-opusenc: clean-room RFC 6716 CELT-only encoder
(fullband 48 kHz, 20 ms packets, mono/stereo as dual L/R stereo, constant
packet size from the bitrate) with the RFC 7845 OpusHead and pre-skip.
Forward low-overlap MDCT, inter/intra coarse energy, fine/final energy,
the decoder's exact allocation, recursive band splitting, PVQ search and
codeword indexing, silence frames.
crates/export depends on it; filmcraft-opus (dev) is the round-trip oracle
in tests/opus_roundtrip.rs, with ffmpeg reading an in-test Ogg Opus file
as a second oracle when installed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>