flacenc/
coding.rs

1// Copyright 2022-2024 Google LLC
2// Copyright 2025- flacenc-rs developers
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//      http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16//! Controller connecting coding algorithms.
17
18use super::arrayutils::find_sum_abs_f32;
19use super::arrayutils::is_constant;
20use super::arrayutils::SimdVec;
21use super::component::BitRepr;
22use super::component::BlockSizeSpec;
23use super::component::ChannelAssignment;
24use super::component::Constant;
25use super::component::FixedLpc;
26use super::component::Frame;
27use super::component::FrameHeader;
28use super::component::FrameOffset;
29use super::component::Lpc;
30use super::component::Residual;
31use super::component::SampleRateSpec;
32use super::component::SampleSizeSpec;
33use super::component::Stream;
34use super::component::StreamInfo;
35use super::component::SubFrame;
36use super::component::Verbatim;
37use super::config;
38use super::constant::fixed::MAX_LPC_ORDER as MAX_FIXED_LPC_ORDER;
39use super::constant::panic_msg;
40use super::constant::qlpc::MAX_ORDER as MAX_LPC_ORDER;
41use super::constant::MIN_BLOCK_SIZE_FOR_PREDICTION;
42use super::error::verify_range;
43use super::error::verify_true;
44use super::error::EncodeError;
45use super::error::Verified;
46use super::lpc;
47#[cfg(feature = "par")]
48use super::par;
49use super::rice;
50use super::source::Context;
51use super::source::FrameBuf;
52use super::source::Source;
53
54import_simd!(as simd);
55
56/// Computes rice encoding of a scalar (used in `encode_residual`.)
57#[inline]
58const fn quotients_and_remainders(err: i32, rice_p: u8) -> (u32, u32) {
59    let remainder_mask = (1u32 << rice_p) - 1;
60    let err = rice::encode_signbit(err);
61    (err >> rice_p, err & remainder_mask)
62}
63
64/// Computes rice encoding of a SIMD vector (used in `encode_residual`.)
65#[inline]
66#[cfg(feature = "simd-nightly")]
67fn quotients_and_remainders_simd<const N: usize>(
68    err_v: simd::Simd<i32, N>,
69    rice_p: u8,
70    quotients: &mut [u32],
71    remainders: &mut [u32],
72) where
73    simd::LaneCount<N>: simd::SupportedLaneCount,
74{
75    let rice_p_v = simd::Simd::splat(u32::from(rice_p));
76    let remainder_mask_v = simd::Simd::splat((1u32 << rice_p) - 1);
77    let err_v = rice::encode_signbit_simd(err_v);
78    quotients.copy_from_slice((err_v >> rice_p_v).as_ref());
79    remainders.copy_from_slice((err_v & remainder_mask_v).as_ref());
80}
81
82/// Computes encoding of each residual partition.
83///
84/// This function is moved out from the main loop for avoiding messy conditoinal
85/// compilation due to fakesimd. We had to resort conditional compilation
86/// because `as_simd` operation that is provided as an extension to standard
87/// types (slice/ Vec) is still there even if we use stable version, so the
88/// approach we used in "fakesimd" is not suitable for mimicking this.
89///
90/// TODO: Probably, it's better to introduce another abstraction for `as_simd`
91/// e.g. SIMD-version of `map` so we can do conditional compilation there.
92#[cfg(feature = "simd-nightly")]
93#[inline]
94fn encode_residual_partition(
95    start: usize,
96    end: usize,
97    rice_p: u8,
98    errors: &[i32],
99    quotients: &mut [u32],
100    remainders: &mut [u32],
101) {
102    const SIMD_N: usize = 8;
103    // note that t >= warmup_length because start >= warmup_length.
104    let mut t = start;
105    let (head, body, tail) = errors[start..end].as_simd::<SIMD_N>();
106    for err in head {
107        (quotients[t], remainders[t]) = quotients_and_remainders(*err, rice_p);
108        t += 1;
109    }
110    for err_v in body {
111        quotients_and_remainders_simd::<SIMD_N>(
112            *err_v,
113            rice_p,
114            &mut quotients[t..t + SIMD_N],
115            &mut remainders[t..t + SIMD_N],
116        );
117        t += SIMD_N;
118    }
119    for err in tail {
120        (quotients[t], remainders[t]) = quotients_and_remainders(*err, rice_p);
121        t += 1;
122    }
123}
124
125/// Computes encoding of each residual partition. (without SIMD)
126#[cfg(not(feature = "simd-nightly"))]
127#[inline]
128fn encode_residual_partition(
129    start: usize,
130    end: usize,
131    rice_p: u8,
132    errors: &[i32],
133    quotients: &mut [u32],
134    remainders: &mut [u32],
135) {
136    let mut t = start;
137    for err in &errors[start..end] {
138        (quotients[t], remainders[t]) = quotients_and_remainders(*err, rice_p);
139        t += 1;
140    }
141}
142
143/// Computes `Residual` from the given error signal and PRC parameters.
144fn encode_residual_with_prc_parameter(
145    _config: &config::Prc,
146    errors: &[i32],
147    warmup_length: usize,
148    prc_p: rice::PrcParameter,
149) -> Residual {
150    let block_size = errors.len();
151    let nparts = 1 << prc_p.order;
152    let part_size = errors.len() >> prc_p.order;
153    debug_assert!(part_size >= warmup_length);
154
155    let mut quotients = vec![0u32; block_size];
156    let mut remainders = vec![0u32; block_size];
157
158    let mut offset = 0;
159    for rice_p in &prc_p.ps[0..nparts] {
160        let start = std::cmp::max(offset, warmup_length);
161        offset += part_size;
162        let end = offset;
163        // ^ this is okay because partitions are larger than warmup_length
164        encode_residual_partition(start, end, *rice_p, errors, &mut quotients, &mut remainders);
165    }
166    Residual::from_parts(
167        prc_p.order as u8,
168        block_size,
169        warmup_length,
170        prc_p.ps,
171        quotients,
172        remainders,
173    )
174}
175
176/// Constructs `Residual` component given the error signal.
177pub fn encode_residual(config: &config::Prc, errors: &[i32], warmup_length: usize) -> Residual {
178    let prc_p = rice::find_partitioned_rice_parameter(errors, warmup_length, config.max_parameter);
179    encode_residual_with_prc_parameter(config, errors, warmup_length, prc_p)
180}
181
182type FixedLpcErrors = [SimdVec<i32, 16>; MAX_FIXED_LPC_ORDER + 1];
183reusable!(FIXED_LPC_ERRORS: FixedLpcErrors);
184
185/// Resets `FixedLpcErrors` from the given signal.
186fn reset_fixed_lpc_errors(errors: &mut FixedLpcErrors, signal: &[i32]) {
187    errors[0].reset_from_slice(signal);
188
189    for order in 0..MAX_FIXED_LPC_ORDER {
190        let next_order = order + 1;
191
192        let mut carry = 0i32;
193        errors[next_order].resize(signal.len(), simd::Simd::default());
194        for t in 0..errors[order].simd_len() {
195            let x = errors[order].as_ref_simd()[t];
196            let mut shifted = x.rotate_elements_right::<1>();
197            (shifted[0], carry) = (carry, shifted[0]);
198            errors[next_order].as_mut_simd()[t] = x - shifted;
199        }
200    }
201}
202
203/// Estimate bit count from the error.
204fn estimate_entropy(errors: &[i32], warmup_len: usize, partitions: usize) -> usize {
205    // this function computes partition average of:
206    //   (1 + e) log (1 + e) - e * log e
207    // where log-base is 2 and e is the average error.
208    // This can further be approximated (by Stirling's formula) as:
209    //   log(1 + e) + constant
210    // given e >> 1, it can further be approximated as log(e); however we don't
211    // use this formula as it is anyway cheap to compute.
212    let block_size = errors.len();
213    let partition_size = (block_size + partitions - 1) / partitions;
214
215    let mut offset = 0;
216    let mut acc = 0;
217    for _p in 0..partitions {
218        let end = std::cmp::min(block_size, offset + partition_size);
219        let partition_len = end - offset;
220        if end >= warmup_len {
221            let sample_count = std::cmp::min(end - warmup_len, partition_len);
222            let sum_errors = find_sum_abs_f32::<16>(&errors[offset..end]);
223            let avg_errors = sum_errors * 2.0 / (sample_count as f32 + 0.00001);
224            let geom_p = 1.0 / (avg_errors + 1.0);
225            let xent = avg_errors.mul_add(-(1.0 - geom_p).log2(), -geom_p.log2());
226            acc += (xent * sample_count as f32) as usize;
227        }
228        offset = end;
229    }
230    acc
231}
232
233/// Selects the best LPC order from error signals and encode `Residual`.
234fn select_order_and_encode_residual<'a, I>(
235    order_sel: &config::OrderSel,
236    prc_config: &config::Prc,
237    errors: I,
238    bits_per_sample: usize,
239    baseline_bits: usize,
240) -> Option<(usize, Residual)>
241where
242    I: Iterator<Item = (usize, &'a [i32])>,
243{
244    let max_rice_p = prc_config.max_parameter;
245    match *order_sel {
246        config::OrderSel::BitCount => errors
247            .map(
248                #[inline]
249                |(order, err)| {
250                    let prc_p = rice::find_partitioned_rice_parameter(err, order, max_rice_p);
251                    let bits = bits_per_sample * order + prc_p.code_bits;
252                    (order, err, prc_p, bits)
253                },
254            )
255            .min_by_key(|(_order, _err, _prc_p, bits)| *bits)
256            .and_then(
257                #[inline]
258                |(order, err, prc_p, bits)| {
259                    (bits < baseline_bits).then(
260                        #[inline]
261                        || {
262                            (
263                                order,
264                                encode_residual_with_prc_parameter(prc_config, err, order, prc_p),
265                            )
266                        },
267                    )
268                },
269            ),
270        config::OrderSel::ApproxEnt { partitions } => errors
271            .map(
272                #[inline]
273                |(order, err)| {
274                    (
275                        order,
276                        err,
277                        estimate_entropy(err, order, partitions) + bits_per_sample * order,
278                    )
279                },
280            )
281            .min_by_key(
282                #[inline]
283                |(_order, _err, bits)| *bits,
284            )
285            .and_then(
286                #[inline]
287                |(order, err, bits)| {
288                    (bits < baseline_bits).then(|| (order, encode_residual(prc_config, err, order)))
289                },
290            ),
291    }
292}
293
294/// Tries `0..=4`-th order fixed LPC and returns the smallest `SubFrame`.
295///
296/// # Panics
297///
298/// The current implementation may cause overflow error if `bits_per_sample` is
299/// larger than 29. Therefore, it panics when `bits_per_sample` is larger than
300/// this.
301#[inline]
302fn fixed_lpc(
303    config: &config::SubFrameCoding,
304    signal: &[i32],
305    bits_per_sample: u8,
306    baseline_bits: usize,
307) -> Option<SubFrame> {
308    assert!(bits_per_sample < 30);
309    let max_order = config.fixed.max_order;
310
311    reuse!(FIXED_LPC_ERRORS, |errors: &mut FixedLpcErrors| {
312        reset_fixed_lpc_errors(errors, signal);
313        let errors = errors
314            .iter()
315            .map(SimdVec::as_ref)
316            .take(max_order + 1)
317            .enumerate();
318        select_order_and_encode_residual(
319            &config.fixed.order_sel,
320            &config.prc,
321            errors,
322            bits_per_sample as usize,
323            baseline_bits,
324        )
325        .map(|(order, residual)| {
326            FixedLpc::from_parts(
327                heapless::Vec::from_slice(&signal[..order])
328                    .expect("Exceeded maximum order for FixedLpc component."),
329                residual,
330                bits_per_sample,
331            )
332            .into()
333        })
334    })
335}
336
337fn perform_qlpc(
338    config: &config::SubFrameCoding,
339    signal: &[i32],
340) -> heapless::Vec<f64, MAX_LPC_ORDER> {
341    if config.qlpc.use_direct_mse {
342        if config.qlpc.mae_optimization_steps > 0 {
343            lpc::lpc_with_irls_mae(
344                signal,
345                &config.qlpc.window,
346                config.qlpc.lpc_order,
347                config.qlpc.mae_optimization_steps,
348            )
349        } else {
350            lpc::lpc_with_direct_mse(signal, &config.qlpc.window, config.qlpc.lpc_order)
351        }
352    } else {
353        lpc::lpc_from_autocorr(signal, &config.qlpc.window, config.qlpc.lpc_order)
354    }
355}
356
357reusable!(QLPC_ERROR_BUFFER: Vec<i32>);
358
359/// Estimates the optimal LPC coefficients and returns `SubFrame`s with these.
360///
361/// # Panics
362///
363/// It panics if `signal` is shorter than `MAX_LPC_ORDER_PLUS_1`.
364fn estimated_qlpc(
365    config: &config::SubFrameCoding,
366    signal: &[i32],
367    bits_per_sample: u8,
368) -> SubFrame {
369    let lpc_order = config.qlpc.lpc_order;
370    let lpc_coefs = perform_qlpc(config, signal);
371    let qlpc = lpc::quantize_parameters(&lpc_coefs[0..lpc_order], config.qlpc.quant_precision);
372    let residual = reuse!(QLPC_ERROR_BUFFER, |errors: &mut Vec<i32>| {
373        errors.resize(signal.len(), 0i32);
374        lpc::compute_error(&qlpc, signal, errors);
375        encode_residual(&config.prc, errors, qlpc.order())
376    });
377    Lpc::from_parts(
378        heapless::Vec::from_slice(&signal[0..qlpc.order()])
379            .expect("LPC order exceeded the maximum"),
380        qlpc,
381        residual,
382        bits_per_sample,
383    )
384    .into()
385}
386
387/// Finds the best method to encode the given samples, and returns `SubFrame`.
388fn encode_subframe(
389    config: &config::SubFrameCoding,
390    samples: &[i32],
391    bits_per_sample: u8,
392) -> SubFrame {
393    if config.use_constant && is_constant(samples) {
394        // Assuming constant is always best if it's applicable.
395        Constant::from_parts(samples.len(), samples[0], bits_per_sample).into()
396    } else {
397        let baseline_bits =
398            Verbatim::count_bits_from_metadata(samples.len(), bits_per_sample as usize);
399
400        let too_short = samples.len() < MIN_BLOCK_SIZE_FOR_PREDICTION;
401        let fixed = if !too_short && config.use_fixed {
402            fixed_lpc(config, samples, bits_per_sample, baseline_bits)
403        } else {
404            None
405        };
406
407        let baseline_bits = fixed.as_ref().map_or(baseline_bits, |x| {
408            std::cmp::min(baseline_bits, x.count_bits())
409        });
410        let est_lpc = if !too_short && config.use_lpc {
411            let candidate = estimated_qlpc(config, samples, bits_per_sample);
412            (candidate.count_bits() < baseline_bits).then_some(candidate)
413        } else {
414            None
415        };
416
417        est_lpc
418            .or(fixed)
419            .unwrap_or_else(|| Verbatim::from_samples(samples, bits_per_sample).into())
420    }
421}
422
423/// Encode frame with the given channel assignment.
424fn encode_frame_impl(
425    config: &config::Encoder,
426    framebuf: &FrameBuf,
427    offset: u64,
428    stream_info: &StreamInfo,
429    ch_info: &ChannelAssignment,
430) -> Frame {
431    let nchannels = stream_info.channels();
432    let bits_per_sample = stream_info.bits_per_sample();
433    let mut frame = Frame::new_empty(
434        BlockSizeSpec::from_size(framebuf.filled_size() as u16),
435        ch_info.clone(),
436        SampleSizeSpec::from_bits(bits_per_sample as u8).unwrap_or(SampleSizeSpec::Unspecified),
437        SampleRateSpec::from_freq(stream_info.sample_rate() as u32)
438            .unwrap_or(SampleRateSpec::Unspecified),
439    );
440    frame
441        .header_mut()
442        .set_frame_offset(FrameOffset::StartSample(offset));
443    for ch in 0..nchannels {
444        frame.add_subframe(encode_subframe(
445            &config.subframe_coding,
446            framebuf.channel_slice(ch),
447            (bits_per_sample + ch_info.bits_per_sample_offset(ch)) as u8,
448        ));
449    }
450
451    frame
452}
453
454// Recombines stereo frame.
455#[allow(clippy::tuple_array_conversions)] // recommended conversion methods are not supported in MSRV
456#[inline]
457fn recombine_stereo_frame(header: FrameHeader, indep: Frame, ms: Frame) -> Frame {
458    let (_header, l, r) = indep
459        .into_stereo_channels()
460        .expect(panic_msg::DATA_INCONSISTENT);
461    let (_header, m, s) = ms
462        .into_stereo_channels()
463        .expect(panic_msg::DATA_INCONSISTENT);
464
465    let chans = header.channel_assignment().select_channels(l, r, m, s);
466    Frame::from_parts(header, vec![chans.0, chans.1])
467}
468
469reusable!(MSFRAMEBUF: FrameBuf = FrameBuf::new_stereo_buffer());
470
471/// Tries several stereo channel recombinations and returns the best.
472fn try_stereo_coding(
473    config: &config::Encoder,
474    framebuf: &FrameBuf,
475    indep: Frame,
476    offset: u64,
477    stream_info: &StreamInfo,
478) -> Frame {
479    reuse!(MSFRAMEBUF, |ms_framebuf: &mut FrameBuf| {
480        ms_framebuf.resize(framebuf.size());
481        ms_framebuf.fill_stereo_with_iter(
482            framebuf
483                .channel_slice(0)
484                .iter()
485                .zip(framebuf.channel_slice(1).iter())
486                .map(|(l, r)| ((l + r) >> 1, l - r)),
487        );
488        let ms_frame = encode_frame_impl(
489            config,
490            ms_framebuf,
491            offset,
492            stream_info,
493            &ChannelAssignment::MidSide,
494        );
495
496        let (bits_l, bits_r, bits_m, bits_s) = (
497            indep.subframe(0).unwrap().count_bits(),
498            indep.subframe(1).unwrap().count_bits(),
499            ms_frame.subframe(0).unwrap().count_bits(),
500            ms_frame.subframe(1).unwrap().count_bits(),
501        );
502
503        let combinations = [
504            config
505                .stereo_coding
506                .use_leftside
507                .then_some((ChannelAssignment::LeftSide, bits_l + bits_s)),
508            config
509                .stereo_coding
510                .use_rightside
511                .then_some((ChannelAssignment::RightSide, bits_r + bits_s)),
512            config
513                .stereo_coding
514                .use_midside
515                .then_some((ChannelAssignment::MidSide, bits_m + bits_s)),
516        ];
517
518        let mut min_bits = bits_l + bits_r;
519        let mut min_ch_info = ChannelAssignment::Independent(2);
520        for (ch_info, bits) in combinations.iter().flatten() {
521            if *bits < min_bits {
522                min_bits = *bits;
523                min_ch_info = ch_info.clone();
524            }
525        }
526        let mut header = ms_frame.header().clone();
527        header.reset_channel_assignment(min_ch_info);
528        recombine_stereo_frame(header, indep, ms_frame)
529    })
530}
531
532/// Finds the best configuration for encoding samples and returns a `Frame`.
533fn encode_frame(
534    config: &config::Encoder,
535    framebuf: &FrameBuf,
536    offset: u64,
537    stream_info: &StreamInfo,
538) -> Frame {
539    let nchannels = stream_info.channels();
540    let ch_info = ChannelAssignment::Independent(nchannels as u8);
541    let mut ret = encode_frame_impl(config, framebuf, offset, stream_info, &ch_info);
542
543    if nchannels == 2 {
544        ret = try_stereo_coding(config, framebuf, ret, offset, stream_info);
545    }
546    ret
547}
548
549/// Encodes [`FrameBuf`] to [`Frame`].
550///
551/// The block size is taken from `FrameBuf::size`.
552///
553/// # Errors
554///
555/// Returns an error when an argument is invalid, e.g. when `frame_number` is
556/// out of 31-bit range, or `framebuf` contains a sample that is out of range.
557///
558/// # Examples
559///
560/// ```
561/// # use flacenc::*;
562/// use flacenc::config;
563/// use flacenc::component::StreamInfo;
564/// use flacenc::error::Verify;
565/// use flacenc::source::{Context, FrameBuf, MemSource, Source};
566///
567/// let (signal_len, block_size, channels, sample_rate) = (32000, 160, 2, 16000);
568/// let signal = vec![0i32; signal_len * channels];
569/// let bits_per_sample = 16;
570///
571/// let mut source = MemSource::from_samples(&signal, channels, bits_per_sample, sample_rate);
572/// let mut fb = FrameBuf::with_size(channels, block_size).unwrap();
573/// let stream_info = StreamInfo::new(sample_rate, channels, bits_per_sample).unwrap();
574/// assert!(source.read_samples(block_size, &mut fb).is_ok());
575///
576/// // NOTE: block-size in config will be overridden.
577/// let frame = encode_fixed_size_frame(
578///     &config::Encoder::default().into_verified().unwrap(),
579///     &fb,
580///     0,
581///     &stream_info
582/// );
583/// ```
584pub fn encode_fixed_size_frame(
585    config: &Verified<config::Encoder>,
586    framebuf: &FrameBuf,
587    frame_number: usize,
588    stream_info: &StreamInfo,
589) -> Result<Frame, EncodeError> {
590    verify_range!(
591        "encode_fixed_size_frame (frame_number)",
592        frame_number,
593        ..(1usize << 31)
594    )?;
595
596    framebuf.verify_samples(stream_info.bits_per_sample())?;
597    // NOTE: From expected use cases, wrapping `stream_info` is not practical
598    // since it is mutable everywhere. On the other hand, verifying it here is
599    // a bit redundant. Because broken `stream_info` actually harms nothing,
600    // as long as it is consistent with `framebuf` (that is checked in the
601    // previous line), we just leave as it is here.
602
603    // A bit awkward, but this function is implemented by overwriting relevant
604    // fields of `Frame` generated by `encode_frame`.
605    let mut ret = encode_frame(config, framebuf, 0, stream_info);
606    ret.header_mut()
607        .set_frame_offset(FrameOffset::Frame(frame_number as u32));
608    Ok(ret)
609}
610
611/// Encodes [`Source`] to [`Stream`].
612///
613/// This is the main entry point of this library crate.
614///
615/// # Errors
616///
617/// This function returns [`EncodeError`] that contains a [`SourceError`] when
618/// it failed to read samples from `src`.
619///
620/// [`SourceError`]: crate::error::SourceError
621///
622/// # Panics
623///
624/// This function panics only by an internal error.
625///
626/// # Examples
627///
628/// ```
629/// # use flacenc::*;
630/// # #[path = "doctest_helper.rs"]
631/// # mod doctest_helper;
632/// # use doctest_helper::*;
633/// use flacenc::config;
634/// use flacenc::error::Verify;
635/// use flacenc::source::MemSource;
636///
637/// let (signal_len, block_size, channels, sample_rate) = (32000, 160, 2, 16000);
638/// let signal = vec![0i32; signal_len * channels];
639/// let bits_per_sample = 16;
640/// let source = MemSource::from_samples(&signal, channels, bits_per_sample, sample_rate);
641/// let result = encode_with_fixed_block_size(
642///     &config::Encoder::default().into_verified().unwrap(),
643///     source,
644///     block_size
645/// );
646/// assert!(result.is_ok());
647/// ```
648pub fn encode_with_fixed_block_size<T: Source>(
649    config: &Verified<config::Encoder>,
650    mut src: T,
651    block_size: usize,
652) -> Result<Stream, EncodeError> {
653    #[cfg(feature = "par")]
654    {
655        if config.multithread {
656            return par::encode_with_fixed_block_size(config, src, block_size);
657        }
658    }
659    let mut stream = Stream::new(src.sample_rate(), src.channels(), src.bits_per_sample())?;
660    let mut framebuf_and_context = (
661        FrameBuf::with_size(src.channels(), block_size)?,
662        Context::new(src.bits_per_sample(), src.channels()),
663    );
664
665    // Probably not very important, but it follows the FLAC reference encoder's behavior
666    // that copies `block_size` to `max_block_size` field of `StreamInfo` when there's
667    // only one frame that is shorter than `block_size`.
668    stream
669        .stream_info_mut()
670        .set_block_sizes(block_size, block_size)
671        .unwrap();
672
673    loop {
674        let read_samples = src.read_samples(block_size, &mut framebuf_and_context)?;
675        if read_samples == 0 {
676            break;
677        }
678        let frame = encode_fixed_size_frame(
679            config,
680            &framebuf_and_context.0,
681            framebuf_and_context.1.current_frame_number().unwrap(),
682            stream.stream_info(),
683        )?;
684        stream.add_frame(frame);
685    }
686
687    let (_, context) = framebuf_and_context;
688    stream
689        .stream_info_mut()
690        .set_md5_digest(&context.md5_digest());
691    stream
692        .stream_info_mut()
693        .set_total_samples(src.len_hint().unwrap_or_else(|| context.total_samples()));
694    Ok(stream)
695}
696
697#[cfg(test)]
698mod tests {
699    use super::*;
700    use crate::component::Decode;
701    use crate::error::Verify;
702    use crate::sigen;
703    use crate::sigen::Signal;
704    use crate::source;
705    use crate::source::Fill;
706
707    #[test]
708    fn fixed_lpc_error_computation() {
709        let mut errors = FixedLpcErrors::default();
710        let signal = sigen::Sine::new(32, 0.3)
711            .noise(0.1)
712            .to_vec_quantized(16, 64);
713        reset_fixed_lpc_errors(&mut errors, &signal);
714        let unpacked = errors[1].as_ref();
715        for t in 1..signal.len() {
716            assert_eq!(unpacked[t], signal[t] - signal[t - 1]);
717        }
718        let unpacked = errors[2].as_ref();
719        for t in 2..signal.len() {
720            assert_eq!(unpacked[t], signal[t] - 2 * signal[t - 1] + signal[t - 2]);
721        }
722    }
723
724    #[test]
725    fn fixed_lpc_of_sine() {
726        let signal = sigen::Sine::new(100, 0.6).to_vec_quantized(8, 1024);
727        let mut config = config::SubFrameCoding::default();
728        for order in 0..=MAX_FIXED_LPC_ORDER {
729            config.fixed.max_order = order;
730            let subframe = fixed_lpc(&config, &signal, 8, usize::MAX)
731                .expect("Should return Some because `baseline_bits` is usize::MAX.");
732            subframe.verify().expect("Should return valid subframe.");
733            assert_eq!(subframe.decode(), signal);
734        }
735    }
736
737    #[test]
738    fn md5_invariance() {
739        let channels = 2;
740        let bits_per_sample = 24;
741        let sample_rate = 16000;
742        let block_size = 128;
743        let constant: f32 = (23f64 / f64::from(1 << 23)) as f32;
744        let signal_len = 1024;
745        let signal =
746            sigen::Dc::new(constant).to_vec_quantized(bits_per_sample, signal_len * channels);
747        assert_eq!(signal[0], 23);
748        let source =
749            source::MemSource::from_samples(&signal, channels, bits_per_sample, sample_rate);
750        let stream = encode_with_fixed_block_size(
751            &config::Encoder::default().into_verified().unwrap(),
752            source,
753            block_size,
754        )
755        .expect("Source read error");
756        eprintln!("MD5 of DC signal ({constant}) with len={signal_len} and ch={channels} was",);
757        eprint!("[");
758        for &b in stream.stream_info().md5_digest() {
759            eprint!("0x{b:02X}, ");
760        }
761        eprintln!("]");
762        assert_eq!(
763            stream.stream_info().md5_digest(),
764            &[
765                0xEE, 0x78, 0x7A, 0x6E, 0x99, 0x01, 0x36, 0x79, 0xA5, 0xBB, 0x6D, 0x5C, 0x10, 0xAF,
766                0x0B, 0x87
767            ]
768        );
769    }
770
771    #[test]
772    fn losslessness_residual_coding() {
773        let signal = sigen::Noise::new(0.4).to_vec_quantized(8, 64);
774        let residual = encode_residual(&config::Prc::default(), &signal, 0);
775        let decoded = residual.decode();
776        assert_eq!(decoded, signal);
777
778        let signal = sigen::Noise::new(0.9)
779            .concat(2048, sigen::Sine::new(40, 0.1))
780            .to_vec_quantized(8, 4096);
781        let residual = encode_residual(&config::Prc::default(), &signal, 0);
782        let decoded = residual.decode();
783        assert_eq!(decoded, signal);
784    }
785
786    #[test]
787    fn losslessness_subframe_coding() {
788        let bits_per_sample = 8;
789        let config = config::SubFrameCoding::default();
790        let signal = sigen::Noise::new(0.4).to_vec_quantized(bits_per_sample, 64);
791        let subframe = encode_subframe(&config, &signal, bits_per_sample as u8);
792        let decoded = subframe.decode();
793        assert_eq!(decoded, signal);
794
795        let signal = sigen::Sine::new(40, 0.9).to_vec_quantized(bits_per_sample, 64);
796        let subframe = encode_subframe(&config, &signal, bits_per_sample as u8);
797        let decoded = subframe.decode();
798        assert_eq!(decoded, signal);
799    }
800
801    #[test]
802    fn encoding_zeros() {
803        let channel_count = 1;
804        let block_size = 64;
805        let bits_per_sample = 8;
806        let sample_rate = 88200;
807        let stream_info = StreamInfo::new(sample_rate, channel_count, bits_per_sample).unwrap();
808        let mut fb = FrameBuf::with_size(channel_count, block_size).unwrap();
809        fb.fill_interleaved(&vec![0; block_size]).unwrap();
810        let frame = encode_fixed_size_frame(
811            &config::Encoder::default().into_verified().unwrap(),
812            &fb,
813            0,
814            &stream_info,
815        )
816        .unwrap();
817        frame.verify().unwrap();
818
819        assert_eq!(frame.decode(), vec![0; block_size]);
820    }
821
822    #[test]
823    fn order_selector_bitcount() {
824        let block_size = 256;
825        let bits_per_sample = 16;
826        let prc_config = config::Prc::default();
827        let errors = [
828            vec![255i32; block_size],
829            vec![256i32; block_size],
830            vec![128i32; block_size],
831        ];
832        let select_result = select_order_and_encode_residual(
833            &config::OrderSel::BitCount,
834            &prc_config,
835            errors.iter().map(AsRef::as_ref).enumerate(),
836            bits_per_sample,
837            usize::MAX,
838        );
839        let (selected_order, residual) =
840            select_result.expect("should be `Some` because baseline_bits == usize::MAX.");
841        residual.verify().expect("should return a valid residual.");
842
843        assert_eq!(selected_order, 0);
844        let selected_count = residual.count_bits() + selected_order * bits_per_sample;
845
846        for (order, err) in errors.iter().enumerate() {
847            let ref_residual = encode_residual(&prc_config, err, order);
848            let ref_count = ref_residual.count_bits() + bits_per_sample * order;
849            assert!(
850                ref_count >= selected_count,
851                "should select the error sequence that minimizes the bit count."
852            );
853            if order == selected_order {
854                assert!(ref_residual.decode() == residual.decode());
855            }
856        }
857    }
858
859    #[test]
860    fn order_selector_approxent() {
861        let block_size = 256;
862        let bits_per_sample = 16;
863        let prc_config = config::Prc::default();
864        let errors = [
865            vec![255i32; block_size],
866            vec![256i32; block_size],
867            vec![128i32; block_size],
868            vec![127i32; block_size],
869        ];
870        let select_result = select_order_and_encode_residual(
871            &config::OrderSel::ApproxEnt { partitions: 32 },
872            &prc_config,
873            errors.iter().map(AsRef::as_ref).enumerate(),
874            bits_per_sample,
875            usize::MAX,
876        );
877        let (selected_order, residual) =
878            select_result.expect("should be `Some` because baseline_bits == usize::MAX.");
879        residual.verify().expect("should return a valid residual.");
880
881        assert_eq!(selected_order, 2);
882    }
883
884    // The block comment below is left intentionally for future when we found
885    // another fuzz test failure.
886    /*
887    #[test]
888    fn encode_failed_fuzz_pattern_1() {
889        let signals = vec![
890            sigen::Sine::with_initial_phase(21893, 0.92941177, 5.8396664),
891            sigen::Sine::with_initial_phase(21892, 0.92941177, 5.8396664),
892        ];
893        let block_size = 23556;
894        let channel_count = 2;
895        let bits_per_sample = 8;
896        let mut config = config::Encoder::default();
897        let stream_info = crate::component::StreamInfo::new(8000, channel_count, bits_per_sample);
898        config.subframe_coding.qlpc.window = config::Window::Tukey { alpha: 0.8 };
899
900        let sample_count = channel_count * block_size;
901        let mut buffer = vec![0i32; sample_count];
902        for (ch, sig) in signals.iter().enumerate() {
903            for (t, x) in sig
904                .to_vec_quantized(bits_per_sample, block_size)
905                .into_iter()
906                .enumerate()
907            {
908                buffer[t * channel_count + ch] = x;
909            }
910        }
911
912        let mut fb = FrameBuf::with_size(channel_count, block_size);
913        fb.fill_interleaved(&buffer).unwrap();
914        encode_fixed_size_frame(&config, &fb, 0, &stream_info).expect("should be ok");
915    }
916    */
917}
918
919#[cfg(all(test, feature = "simd-nightly"))]
920mod bench {
921    use super::*;
922    use crate::source::Fill;
923
924    extern crate test;
925
926    use test::bench::Bencher;
927    use test::black_box;
928
929    use crate::error::Verify;
930    use crate::sigen;
931    use crate::sigen::Signal;
932
933    #[bench]
934    fn residual_encoder_zero(b: &mut Bencher) {
935        let errors = [0i32; 4096];
936        let cfg = &config::Prc::default();
937        b.iter(|| encode_residual(black_box(cfg), black_box(&errors), black_box(3)));
938    }
939
940    #[bench]
941    fn residual_partition_encoder_zero(b: &mut Bencher) {
942        let errors = [0i32; 4096];
943        let mut quotients = [0u32; 4096];
944        let mut remainders = [0u32; 4096];
945        b.iter(|| {
946            encode_residual_partition(
947                black_box(1024),
948                black_box(2048),
949                black_box(10u8),
950                black_box(&errors),
951                black_box(&mut quotients),
952                black_box(&mut remainders),
953            );
954            (quotients, remainders)
955        });
956    }
957
958    #[bench]
959    fn fixed_lpc_encoding_zero(b: &mut Bencher) {
960        let signal = [0i32; 4096];
961        let cfg = &config::SubFrameCoding::default();
962        b.iter(|| {
963            fixed_lpc(
964                black_box(cfg),
965                black_box(&signal),
966                black_box(16u8),
967                black_box(0usize),
968            )
969        });
970    }
971
972    #[bench]
973    fn fixed_size_frame_encoder_zero(b: &mut Bencher) {
974        let cfg = &config::Encoder::default().into_verified().unwrap();
975        let stream_info = StreamInfo::new(44100, 2, 16).unwrap();
976        let mut fb = FrameBuf::with_size(2, 4096).unwrap();
977        // input is always zero, so it should use Constant and fast.
978        #[allow(clippy::large_stack_arrays)] // okay as it is just for benchmark preparation.
979        fb.fill_interleaved(&[0i32; 4096 * 2]).unwrap();
980        b.iter(|| {
981            encode_fixed_size_frame(
982                black_box(cfg),
983                black_box(&fb),
984                black_box(123usize),
985                &stream_info,
986            )
987        });
988    }
989
990    fn bench_stereo_frame_encoder_impl<S: Signal>(
991        b: &mut Bencher,
992        signal: &S,
993        use_constant: bool,
994        use_fixed: bool,
995        use_lpc: bool,
996    ) {
997        let mut cfg = config::Encoder::default();
998        cfg.subframe_coding.use_constant = use_constant;
999        cfg.subframe_coding.use_fixed = use_fixed;
1000        cfg.subframe_coding.use_lpc = use_lpc;
1001        let cfg = cfg.into_verified().unwrap();
1002        let stream_info = StreamInfo::new(48000, 2, 16).unwrap();
1003        let mut fb = FrameBuf::with_size(2, 4096).unwrap();
1004        let signal = signal.to_vec_quantized(16, 4096 * 2);
1005        fb.fill_interleaved(&signal).unwrap();
1006        b.iter(|| {
1007            encode_fixed_size_frame(
1008                black_box(&cfg),
1009                black_box(&fb),
1010                black_box(123usize),
1011                &stream_info,
1012            )
1013        });
1014    }
1015
1016    #[bench]
1017    fn stereo_frame_encoder_pure_sine_fixed(b: &mut Bencher) {
1018        bench_stereo_frame_encoder_impl(b, &sigen::Sine::new(200, 0.4), false, true, false);
1019    }
1020
1021    #[bench]
1022    fn stereo_frame_encoder_pure_sine_lpc(b: &mut Bencher) {
1023        bench_stereo_frame_encoder_impl(b, &sigen::Sine::new(200, 0.4), false, false, true);
1024    }
1025
1026    #[bench]
1027    fn stereo_frame_encoder_noisy_sine_fixed(b: &mut Bencher) {
1028        bench_stereo_frame_encoder_impl(
1029            b,
1030            &sigen::Sine::new(200, 0.4).noise(0.4),
1031            false,
1032            true,
1033            false,
1034        );
1035    }
1036
1037    #[bench]
1038    fn stereo_frame_encoder_noisy_sine_lpc(b: &mut Bencher) {
1039        bench_stereo_frame_encoder_impl(
1040            b,
1041            &sigen::Sine::new(200, 0.4).noise(0.4),
1042            false,
1043            false,
1044            true,
1045        );
1046    }
1047
1048    #[bench]
1049    fn normal_qlpc_noise(b: &mut Bencher) {
1050        let cfg = &config::SubFrameCoding::default();
1051        let bits_per_sample = 16u8;
1052        let signal = sigen::Noise::new(0.6).to_vec_quantized(bits_per_sample as usize, 4096);
1053
1054        b.iter(|| {
1055            estimated_qlpc(
1056                black_box(cfg),
1057                black_box(&signal),
1058                black_box(bits_per_sample),
1059            )
1060        });
1061    }
1062}