1use crate::bitmath;
13use crate::bitwriter::BitWriter;
14use crate::format::{
15 ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER,
16 ENTROPY_CODING_METHOD_PARTITIONED_RICE2_ESCAPE_PARAMETER, MAX_FIXED_ORDER, MAX_LPC_ORDER,
17 MAX_QLP_COEFF_PRECISION, MIN_QLP_COEFF_PRECISION,
18};
19use crate::frame::{ChannelAssignment, FrameHeader, write_frame_footer, write_frame_header};
20use crate::{fixed, lpc, metadata, ogg, rice, subframe, window};
21
22const MIN_RESIDUAL_PARTITION_ORDER: u32 = 0;
24
25fn rice_parameter_limit(bits_per_sample: u32) -> u32 {
28 if bits_per_sample > 16 {
29 ENTROPY_CODING_METHOD_PARTITIONED_RICE2_ESCAPE_PARAMETER
30 } else {
31 ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER
32 }
33}
34
35#[derive(Clone, Copy)]
39pub enum Apodization {
40 Tukey(f32),
42 SubdivideTukey(i32),
45}
46
47impl Apodization {
48 fn window_p(self) -> f32 {
50 match self {
51 Apodization::Tukey(p) => p,
52 Apodization::SubdivideTukey(parts) => 0.5 / parts as f32,
53 }
54 }
55 fn subdivide_parts(self) -> i32 {
56 match self {
57 Apodization::Tukey(_) => 1,
58 Apodization::SubdivideTukey(parts) => parts,
59 }
60 }
61 fn is_subdivide(self) -> bool {
62 matches!(self, Apodization::SubdivideTukey(_))
63 }
64}
65
66#[derive(Clone, Copy)]
72pub struct Config {
73 pub do_mid_side: bool,
74 pub loose_mid_side: bool,
75 pub max_lpc_order: u32,
76 pub max_residual_partition_order: u32,
77 pub apodization: Apodization,
78}
79
80pub fn preset(level: u32) -> Config {
83 use Apodization::{SubdivideTukey, Tukey};
84 let (do_mid_side, loose_mid_side, max_lpc_order, max_residual_partition_order, apodization) =
85 match level {
86 0 => (false, false, 0, 3, Tukey(0.5)),
87 1 => (true, true, 0, 3, Tukey(0.5)),
88 2 => (true, false, 0, 3, Tukey(0.5)),
89 3 => (false, false, 6, 4, Tukey(0.5)),
90 4 => (true, true, 8, 4, Tukey(0.5)),
91 5 => (true, false, 8, 5, Tukey(0.5)),
92 6 => (true, false, 8, 6, SubdivideTukey(2)),
93 7 => (true, false, 12, 6, SubdivideTukey(2)),
94 _ => (true, false, 12, 6, SubdivideTukey(3)),
95 };
96 Config {
97 do_mid_side,
98 loose_mid_side,
99 max_lpc_order,
100 max_residual_partition_order,
101 apodization,
102 }
103}
104
105pub(crate) fn get_wasted_bits(signal: &mut [i64], wide: bool) -> u32 {
112 let mut x = 0i64;
113 let mut i = 0;
114 while i < signal.len() && x & 1 == 0 {
115 x |= signal[i];
116 i += 1;
117 }
118 let shift = if x == 0 {
119 u32::from(wide)
120 } else {
121 x.trailing_zeros()
122 };
123 if shift > 0 {
124 for s in signal.iter_mut() {
125 *s >>= shift;
126 }
127 }
128 shift
129}
130
131fn auto_qlp_coeff_precision(bits_per_sample: u32, blocksize: u32) -> u32 {
135 if bits_per_sample < 16 {
136 (2 + bits_per_sample / 2).max(MIN_QLP_COEFF_PRECISION)
137 } else if bits_per_sample == 16 {
138 match blocksize {
139 0..=192 => 7,
140 193..=384 => 8,
141 385..=576 => 9,
142 577..=1152 => 10,
143 1153..=2304 => 11,
144 2305..=4608 => 12,
145 _ => 13,
146 }
147 } else {
148 match blocksize {
149 0..=384 => MAX_QLP_COEFF_PRECISION - 2,
150 385..=1152 => MAX_QLP_COEFF_PRECISION - 1,
151 _ => MAX_QLP_COEFF_PRECISION,
152 }
153 }
154}
155
156struct LpcCtx<'a> {
159 window: &'a [f32],
160 max_lpc_order: u32,
161 qlp_coeff_precision: u32,
162 apodization: Apodization,
163}
164
165struct LpcChoice {
167 order: u32,
168 qlp_coeff: Vec<i32>,
169 precision: u32,
170 shift: i32,
171 residual: Vec<i32>,
172 rice: rice::RicePartition,
173}
174
175enum Choice {
176 Constant,
177 Verbatim,
178 Fixed {
179 order: u32,
180 residual: Vec<i32>,
181 rice: rice::RicePartition,
182 },
183 Lpc(LpcChoice),
184}
185
186struct ChosenChannel {
189 signal: Vec<i64>,
190 subframe_bps: u32,
191 wasted_bits: u32,
192 choice: Choice,
193 bits: u32,
194}
195
196fn set_next_subdivide_tukey(parts: i32, a: &mut u32, depth: &mut u32, part: &mut u32) {
201 if *depth == 2 {
202 if *part == 0 {
204 *part = 2;
205 } else {
206 *part = 0;
207 *depth += 1;
208 }
209 } else if *part < 2 * *depth - 1 {
210 *part += 1;
211 } else {
212 *part = 0;
213 *depth += 1;
214 }
215 if *depth > parts as u32 {
216 *a += 1;
217 *depth = 1;
218 *part = 0;
219 }
220}
221
222#[allow(clippy::too_many_arguments)]
227fn apply_apodization(
228 signal: &[i64],
229 window: &[f32],
230 windowed: &mut [f32],
231 autoc: &mut [f64],
232 autoc_root: &mut [f64],
233 blocksize: usize,
234 max_order: usize,
235 subframe_bps: u32,
236 ctx: &LpcCtx,
237 a: &mut u32,
238 b: &mut u32,
239 c: &mut u32,
240) -> Option<(lpc::LpCoefficients, usize)> {
241 let parts = ctx.apodization.subdivide_parts();
242 let lag = max_order + 1;
243 if *b == 1 {
244 lpc::window_data(signal, window, windowed, blocksize);
246 lpc::compute_autocorrelation(&windowed[..blocksize], lag, autoc);
247 if ctx.apodization.is_subdivide() {
248 autoc_root[..max_order].copy_from_slice(&autoc[..max_order]);
249 *b += 1;
250 } else {
251 *a += 1;
253 }
254 } else {
255 let bb = *b as usize;
256 if blocksize / bb <= MAX_LPC_ORDER as usize {
257 set_next_subdivide_tukey(parts, a, b, c);
259 return None;
260 }
261 if *c % 2 == 0 {
262 let part_size = blocksize / bb / 2;
264 let data_shift = (*c as usize / 2 * blocksize) / bb;
265 lpc::window_data_partial(signal, window, windowed, blocksize, part_size, data_shift);
266 lpc::compute_autocorrelation(&windowed[..blocksize / bb], lag, autoc);
267 } else {
268 for i in 0..max_order {
272 autoc[i] = autoc_root[i] - autoc[i];
273 }
274 }
275 set_next_subdivide_tukey(parts, a, b, c);
276 }
277
278 if autoc[0] == 0.0 {
279 return None;
281 }
282 let lp = lpc::compute_lp_coefficients(autoc, max_order);
283 let overhead = subframe_bps + ctx.qlp_coeff_precision;
286 let guess_order = lpc::compute_best_order(&lp.error, lp.max_order, blocksize as u32, overhead);
287 Some((lp, guess_order))
288}
289
290#[allow(clippy::too_many_arguments)]
295fn evaluate_lpc_subframe(
296 signal: &[i64],
297 subframe_bps: u32,
298 wasted_bits: u32,
299 lp_coeff_row: &[f32],
300 order: u32,
301 qlp_coeff_precision: u32,
302 rice_parameter_limit: u32,
303 min_partition_order: u32,
304 max_partition_order: u32,
305) -> Option<(LpcChoice, u32)> {
306 let order_us = order as usize;
307
308 let mut precision = qlp_coeff_precision;
311 if subframe_bps <= 17 {
312 precision = precision.min(32 - subframe_bps - bitmath::ilog2(order));
313 }
314
315 let q = lpc::quantize_coefficients(lp_coeff_row, order_us, precision).ok()?;
316
317 let residual = if lpc::max_residual_bps(subframe_bps, &q.qlp_coeff, order_us, q.shift) > 32 {
318 lpc::compute_residual_limit(signal, order_us, &q.qlp_coeff, q.shift)?
319 } else {
320 lpc::compute_residual(signal, order_us, &q.qlp_coeff, q.shift)
321 };
322
323 let (rice, residual_bits) = rice::find_best_partition_order(
324 &residual,
325 order,
326 rice_parameter_limit,
327 min_partition_order,
328 max_partition_order,
329 );
330 let bits = subframe::lpc_bits(order, precision, subframe_bps, wasted_bits, residual_bits);
331
332 Some((
333 LpcChoice {
334 order,
335 qlp_coeff: q.qlp_coeff,
336 precision,
337 shift: q.shift,
338 residual,
339 rice,
340 },
341 bits,
342 ))
343}
344
345#[allow(clippy::too_many_arguments)]
350fn best_lpc_subframe(
351 signal: &[i64],
352 subframe_bps: u32,
353 wasted_bits: u32,
354 blocksize: u32,
355 ctx: &LpcCtx,
356 rice_parameter_limit: u32,
357 min_partition_order: u32,
358 max_partition_order: u32,
359) -> Option<(LpcChoice, u32)> {
360 let max_lpc_order = if ctx.max_lpc_order >= blocksize {
361 blocksize - 1
362 } else {
363 ctx.max_lpc_order
364 };
365 if max_lpc_order == 0 {
366 return None;
367 }
368
369 let bs = blocksize as usize;
370 let mut windowed = vec![0f32; bs];
371 let mut autoc = [0f64; MAX_LPC_ORDER as usize + 1];
372 let mut autoc_root = [0f64; MAX_LPC_ORDER as usize + 1];
373
374 let mut best: Option<(LpcChoice, u32)> = None;
375 let (mut a, mut b, mut c) = (0u32, 1u32, 0u32);
378 while a < 1 {
379 let max_order_this = max_lpc_order as usize;
380 if let Some((lp, guess_order)) = apply_apodization(
381 signal,
382 ctx.window,
383 &mut windowed,
384 &mut autoc,
385 &mut autoc_root,
386 bs,
387 max_order_this,
388 subframe_bps,
389 ctx,
390 &mut a,
391 &mut b,
392 &mut c,
393 ) {
394 let lpc_residual_bps =
396 lpc::expected_bits(lp.error[guess_order - 1], blocksize - guess_order as u32);
397 if lpc_residual_bps < subframe_bps as f64 {
398 if let Some((choice, bits)) = evaluate_lpc_subframe(
399 signal,
400 subframe_bps,
401 wasted_bits,
402 lp.row(guess_order),
403 guess_order as u32,
404 ctx.qlp_coeff_precision,
405 rice_parameter_limit,
406 min_partition_order,
407 max_partition_order,
408 ) {
409 if best.as_ref().is_none_or(|(_, bb)| bits < *bb) {
410 best = Some((choice, bits));
411 }
412 }
413 }
414 }
415 }
416 best
417}
418
419#[allow(clippy::too_many_arguments)]
426fn choose_subframe(
427 signal: &[i64],
428 subframe_bps: u32,
429 wasted_bits: u32,
430 blocksize: u32,
431 rice_parameter_limit: u32,
432 min_partition_order: u32,
433 max_partition_order: u32,
434 lpc_ctx: Option<&LpcCtx>,
435) -> (Choice, u32) {
436 let bs = signal.len() as u32;
437 let mut best_bits = subframe::verbatim_bits(bs, subframe_bps, wasted_bits);
438 let mut best = Choice::Verbatim;
439
440 if bs > MAX_FIXED_ORDER {
441 if subframe_bps < 28 && signal.iter().all(|&s| s == signal[0]) {
446 let cb = subframe::constant_bits(subframe_bps, wasted_bits);
447 if cb < best_bits {
448 best_bits = cb;
449 best = Choice::Constant;
450 }
451 } else {
452 let (order, fixed_rbps) = fixed::compute_best_predictor_order(signal, subframe_bps);
453 if (fixed_rbps) < subframe_bps as f32 {
457 let residual = fixed::compute_residual(signal, order);
458 let (rice_part, residual_bits) = rice::find_best_partition_order(
459 &residual,
460 order,
461 rice_parameter_limit,
462 min_partition_order,
463 max_partition_order,
464 );
465 let fb = subframe::fixed_bits(order, subframe_bps, wasted_bits, residual_bits);
466 if fb < best_bits {
467 best_bits = fb;
468 best = Choice::Fixed {
469 order,
470 residual,
471 rice: rice_part,
472 };
473 }
474 }
475
476 if let Some(ctx) = lpc_ctx {
477 if let Some((lpc_choice, lpc_bits)) = best_lpc_subframe(
478 signal,
479 subframe_bps,
480 wasted_bits,
481 blocksize,
482 ctx,
483 rice_parameter_limit,
484 min_partition_order,
485 max_partition_order,
486 ) {
487 if lpc_bits < best_bits {
488 best_bits = lpc_bits;
489 best = Choice::Lpc(lpc_choice);
490 }
491 }
492 }
493 }
494 }
495
496 (best, best_bits)
497}
498
499fn write_choice(
502 bw: &mut BitWriter,
503 choice: &Choice,
504 signal: &[i64],
505 subframe_bps: u32,
506 wasted_bits: u32,
507) {
508 match choice {
509 Choice::Constant => subframe::write_constant(bw, signal[0], subframe_bps, wasted_bits),
510 Choice::Verbatim => subframe::write_verbatim(bw, signal, subframe_bps, wasted_bits),
511 Choice::Fixed {
512 order,
513 residual,
514 rice,
515 } => subframe::write_fixed(
516 bw,
517 *order,
518 &signal[..*order as usize],
519 subframe_bps,
520 wasted_bits,
521 residual,
522 rice,
523 ),
524 Choice::Lpc(c) => subframe::write_lpc(
525 bw,
526 c.order,
527 &signal[..c.order as usize],
528 &c.qlp_coeff,
529 c.precision,
530 c.shift,
531 subframe_bps,
532 wasted_bits,
533 &c.residual,
534 &c.rice,
535 ),
536 }
537}
538
539fn fill_seekpoints(
551 points: &mut [metadata::SeekPoint],
552 first: &mut usize,
553 frame_first_sample: u64,
554 blocksize: u64,
555 stream_offset: u64,
556) {
557 let frame_last_sample = frame_first_sample + blocksize - 1;
558 while *first < points.len() {
559 let test = points[*first].sample_number;
560 if test > frame_last_sample {
561 break; }
563 if test >= frame_first_sample {
564 points[*first] = metadata::SeekPoint {
565 sample_number: frame_first_sample,
566 stream_offset,
567 frame_samples: blocksize as u32,
568 };
569 }
570 *first += 1;
573 }
574}
575
576pub fn encode_frames(
582 interleaved: &[i32],
583 channels: u32,
584 bits_per_sample: u32,
585 sample_rate: u32,
586 blocksize: u32,
587 config: &Config,
588) -> Vec<u8> {
589 encode_frames_inner(
590 interleaved,
591 channels,
592 bits_per_sample,
593 sample_rate,
594 blocksize,
595 config,
596 None,
597 None,
598 )
599 .0
600}
601
602#[allow(clippy::too_many_arguments)]
610fn encode_frames_inner(
611 interleaved: &[i32],
612 channels: u32,
613 bits_per_sample: u32,
614 sample_rate: u32,
615 blocksize: u32,
616 config: &Config,
617 mut seektable: Option<&mut [metadata::SeekPoint]>,
618 mut frame_lengths: Option<&mut Vec<usize>>,
619) -> (Vec<u8>, u32, u32) {
620 let ch = channels as usize;
621 assert!(ch > 0 && interleaved.len() % ch == 0, "ragged interleave");
622 let total = interleaved.len() / ch;
623 let stereo_ms = config.do_mid_side && ch == 2;
624
625 let qlp_coeff_precision = auto_qlp_coeff_precision(bits_per_sample, blocksize);
627 let window_p = config.apodization.window_p();
630 let mut window_bs = 0usize;
631 let mut window_buf: Vec<f32> = Vec::new();
632
633 let loose_frames = if config.loose_mid_side {
636 ((sample_rate as f64 * 0.4 / blocksize as f64 + 0.5) as u32).max(1)
637 } else {
638 0
639 };
640 let mut loose_count = 0u32;
641 let mut last_assignment = ChannelAssignment::Independent;
642
643 let mut out = Vec::new();
644 let mut min_framesize = u32::MAX;
645 let mut max_framesize = 0u32;
646 let mut frame_number = 0u32;
647 let mut first_seekpoint_to_check = 0usize;
650 let mut start = 0usize;
651 while start < total {
652 let bs = (total - start).min(blocksize as usize);
653 let stream_offset = out.len() as u64;
656 if let Some(points) = seektable.as_deref_mut() {
657 fill_seekpoints(
658 points,
659 &mut first_seekpoint_to_check,
660 start as u64,
661 bs as u64,
662 stream_offset,
663 );
664 }
665
666 let max_partition_order = rice::max_partition_order_from_blocksize(bs as u32)
670 .min(config.max_residual_partition_order);
671 let min_partition_order = MIN_RESIDUAL_PARTITION_ORDER;
672
673 if config.max_lpc_order > 0 && bs > 1 && bs != window_bs {
675 window_buf = vec![0f32; bs];
676 window::tukey(&mut window_buf, window_p);
677 window_bs = bs;
678 }
679 let lpc_ctx = (config.max_lpc_order > 0 && bs > 1).then(|| LpcCtx {
680 window: &window_buf,
681 max_lpc_order: config.max_lpc_order,
682 qlp_coeff_precision,
683 apodization: config.apodization,
684 });
685
686 let choose = |signal: Vec<i64>, extra_bps: u32| -> ChosenChannel {
689 let mut sig = signal;
690 let wide = extra_bps == 1 && bits_per_sample == 32;
693 let mut wasted = get_wasted_bits(&mut sig, wide);
694 if wasted > bits_per_sample {
695 wasted = bits_per_sample;
696 }
697 let subframe_bps = bits_per_sample - wasted + extra_bps;
698 let (choice, bits) = choose_subframe(
699 &sig,
700 subframe_bps,
701 wasted,
702 bs as u32,
703 rice_parameter_limit(bits_per_sample),
704 min_partition_order,
705 max_partition_order,
706 lpc_ctx.as_ref(),
707 );
708 ChosenChannel {
709 signal: sig,
710 subframe_bps,
711 wasted_bits: wasted,
712 choice,
713 bits,
714 }
715 };
716
717 let mut frame = BitWriter::new();
718
719 if stereo_ms {
720 let left: Vec<i64> = (0..bs)
721 .map(|i| interleaved[(start + i) * ch] as i64)
722 .collect();
723 let right: Vec<i64> = (0..bs)
724 .map(|i| interleaved[(start + i) * ch + 1] as i64)
725 .collect();
726 let mid_side = || {
731 let mid: Vec<i64> = (0..bs).map(|i| (left[i] + right[i]) >> 1).collect();
732 let side: Vec<i64> = (0..bs).map(|i| left[i] - right[i]).collect();
733 (mid, side)
734 };
735
736 let (assignment, pair): (ChannelAssignment, [ChosenChannel; 2]) =
739 if config.loose_mid_side && loose_count != 0 {
740 if last_assignment == ChannelAssignment::Independent {
741 (
742 ChannelAssignment::Independent,
743 [choose(left, 0), choose(right, 0)],
744 )
745 } else {
746 let (mid, side) = mid_side();
747 (
748 ChannelAssignment::MidSide,
749 [choose(mid, 0), choose(side, 1)],
750 )
751 }
752 } else {
753 let (mid, side) = mid_side();
754 let cl = choose(left, 0);
755 let cr = choose(right, 0);
756 let cm = choose(mid, 0);
757 let cs = choose(side, 1);
758 let mut assignment = ChannelAssignment::Independent;
761 let mut min_bits = cl.bits + cr.bits;
762 let all = !config.loose_mid_side;
763 for (enabled, bits, ca) in [
764 (all, cl.bits + cs.bits, ChannelAssignment::LeftSide),
765 (all, cr.bits + cs.bits, ChannelAssignment::RightSide),
766 (true, cm.bits + cs.bits, ChannelAssignment::MidSide),
767 ] {
768 if enabled && bits < min_bits {
769 min_bits = bits;
770 assignment = ca;
771 }
772 }
773 let pair = match assignment {
774 ChannelAssignment::Independent => [cl, cr],
775 ChannelAssignment::LeftSide => [cl, cs],
776 ChannelAssignment::RightSide => [cs, cr],
777 ChannelAssignment::MidSide => [cm, cs],
778 };
779 (assignment, pair)
780 };
781
782 write_frame_header(
783 &mut frame,
784 &FrameHeader {
785 blocksize: bs as u32,
786 sample_rate,
787 channels,
788 channel_assignment: assignment,
789 bits_per_sample,
790 frame_number,
791 },
792 );
793 for cc in &pair {
794 write_choice(
795 &mut frame,
796 &cc.choice,
797 &cc.signal,
798 cc.subframe_bps,
799 cc.wasted_bits,
800 );
801 }
802
803 last_assignment = assignment;
804 if config.loose_mid_side {
805 loose_count += 1;
806 if loose_count >= loose_frames {
807 loose_count = 0;
808 }
809 }
810 } else {
811 write_frame_header(
812 &mut frame,
813 &FrameHeader {
814 blocksize: bs as u32,
815 sample_rate,
816 channels,
817 channel_assignment: ChannelAssignment::Independent,
818 bits_per_sample,
819 frame_number,
820 },
821 );
822 for c in 0..ch {
823 let signal: Vec<i64> = (0..bs)
824 .map(|i| interleaved[(start + i) * ch + c] as i64)
825 .collect();
826 let cc = choose(signal, 0);
827 write_choice(
828 &mut frame,
829 &cc.choice,
830 &cc.signal,
831 cc.subframe_bps,
832 cc.wasted_bits,
833 );
834 }
835 }
836
837 write_frame_footer(&mut frame);
838 let fsize = frame.as_bytes().len() as u32;
839 min_framesize = min_framesize.min(fsize);
840 max_framesize = max_framesize.max(fsize);
841 if let Some(fl) = frame_lengths.as_deref_mut() {
842 fl.push(fsize as usize);
843 }
844 out.extend_from_slice(frame.as_bytes());
845
846 start += bs;
847 frame_number += 1;
848 }
849 if min_framesize == u32::MAX {
850 min_framesize = 0;
851 }
852 if let Some(points) = seektable {
855 metadata::seektable_sort(points);
856 }
857 (out, min_framesize, max_framesize)
858}
859
860#[allow(clippy::too_many_arguments)]
868pub fn encode(
869 interleaved: &[i32],
870 channels: u32,
871 bits_per_sample: u32,
872 sample_rate: u32,
873 blocksize: u32,
874 config: &Config,
875 do_md5: bool,
876 blocks: &[metadata::MetadataBlock],
877) -> Vec<u8> {
878 let ch = channels as usize;
879 assert!(ch > 0 && interleaved.len() % ch == 0, "ragged interleave");
880 let total_samples = (interleaved.len() / ch) as u64;
881
882 let mut filled_seektable: Option<Vec<metadata::SeekPoint>> = blocks.iter().find_map(|b| {
886 if let metadata::MetadataBlock::Seektable(pts) = b {
887 Some(pts.to_vec())
888 } else {
889 None
890 }
891 });
892
893 let (frames, min_framesize, max_framesize) = encode_frames_inner(
894 interleaved,
895 channels,
896 bits_per_sample,
897 sample_rate,
898 blocksize,
899 config,
900 filled_seektable.as_deref_mut(),
901 None,
902 );
903
904 let md5 = if do_md5 {
905 crate::md5::audio_md5(interleaved, bits_per_sample.div_ceil(8) as usize)
906 } else {
907 [0u8; 16]
908 };
909
910 let si = metadata::StreamInfo {
911 min_blocksize: blocksize,
912 max_blocksize: blocksize,
913 min_framesize,
914 max_framesize,
915 sample_rate,
916 channels,
917 bits_per_sample,
918 total_samples,
919 md5,
920 };
921
922 let mut bw = BitWriter::new();
923 bw.write_byte_block(b"fLaC");
924 metadata::write_streaminfo(&mut bw, &si, blocks.is_empty());
925 for (i, block) in blocks.iter().enumerate() {
926 let is_last = i + 1 == blocks.len();
927 match block {
928 metadata::MetadataBlock::Seektable(_) => metadata::write_seektable(
930 &mut bw,
931 filled_seektable.as_deref().unwrap_or(&[]),
932 is_last,
933 ),
934 _ => metadata::write_block(&mut bw, block, is_last),
935 }
936 }
937 let mut out = bw.as_bytes().to_vec();
938 out.extend_from_slice(&frames);
939 out
940}
941
942#[allow(clippy::too_many_arguments)]
957pub fn encode_ogg(
958 interleaved: &[i32],
959 channels: u32,
960 bits_per_sample: u32,
961 sample_rate: u32,
962 blocksize: u32,
963 config: &Config,
964 do_md5: bool,
965 blocks: &[metadata::MetadataBlock],
966 serial: i32,
967) -> Vec<u8> {
968 let ch = channels as usize;
969 assert!(ch > 0 && interleaved.len() % ch == 0, "ragged interleave");
970 let total_samples = (interleaved.len() / ch) as u64;
971
972 let mut frame_lengths: Vec<usize> = Vec::new();
975 let (frames, min_framesize, max_framesize) = encode_frames_inner(
976 interleaved,
977 channels,
978 bits_per_sample,
979 sample_rate,
980 blocksize,
981 config,
982 None,
983 Some(&mut frame_lengths),
984 );
985
986 let md5 = if do_md5 {
987 crate::md5::audio_md5(interleaved, bits_per_sample.div_ceil(8) as usize)
988 } else {
989 [0u8; 16]
990 };
991 let si = metadata::StreamInfo {
992 min_blocksize: blocksize,
993 max_blocksize: blocksize,
994 min_framesize,
995 max_framesize,
996 sample_rate,
997 channels,
998 bits_per_sample,
999 total_samples,
1000 md5,
1001 };
1002
1003 let meta: Vec<&metadata::MetadataBlock> = blocks
1005 .iter()
1006 .filter(|b| !matches!(b, metadata::MetadataBlock::Seektable(_)))
1007 .collect();
1008
1009 let mut si_bw = BitWriter::new();
1012 metadata::write_streaminfo(&mut si_bw, &si, meta.is_empty());
1013 let mut bos = Vec::with_capacity(13 + 38);
1014 bos.push(0x7F);
1015 bos.extend_from_slice(b"FLAC");
1016 bos.push(1); bos.push(0); bos.extend_from_slice(&[0, 0]); bos.extend_from_slice(b"fLaC");
1020 bos.extend_from_slice(si_bw.as_bytes());
1021
1022 let mut og = ogg::OggStream::new(serial);
1023 og.packetin(&bos, false, 0);
1024 og.flush();
1025
1026 for (i, block) in meta.iter().enumerate() {
1027 let is_last = i + 1 == meta.len();
1028 let mut bw = BitWriter::new();
1029 metadata::write_block(&mut bw, block, is_last);
1030 og.packetin(bw.as_bytes(), false, 0);
1031 og.flush();
1032 }
1033
1034 let nframes = frame_lengths.len();
1035 let mut byte_off = 0usize;
1036 let mut sample_off = 0u64;
1037 for (i, &flen) in frame_lengths.iter().enumerate() {
1038 let fsamples = (blocksize as u64).min(total_samples - sample_off);
1039 sample_off += fsamples;
1040 let is_last = i + 1 == nframes;
1041 og.packetin(
1042 &frames[byte_off..byte_off + flen],
1043 is_last,
1044 sample_off as i64,
1045 );
1046 byte_off += flen;
1047 og.pageout();
1048 }
1049
1050 og.into_bytes()
1051}
1052
1053#[derive(Clone, Copy, Debug)]
1059pub struct EncoderConfig {
1060 pub channels: u32,
1062 pub bits_per_sample: u32,
1064 pub sample_rate: u32,
1066 pub block_size: u32,
1069 pub compression_level: u32,
1071 pub md5: bool,
1074}
1075
1076impl EncoderConfig {
1077 pub fn new(channels: u32, bits_per_sample: u32, sample_rate: u32) -> Self {
1080 Self {
1081 channels,
1082 bits_per_sample,
1083 sample_rate,
1084 block_size: 4096,
1085 compression_level: 8,
1086 md5: true,
1087 }
1088 }
1089
1090 pub fn chd(block_size: u32) -> Self {
1095 Self {
1096 channels: 2,
1097 bits_per_sample: 16,
1098 sample_rate: 44_100,
1099 block_size,
1100 compression_level: 8,
1101 md5: false,
1102 }
1103 }
1104
1105 pub fn with_compression_level(mut self, level: u32) -> Self {
1107 self.compression_level = level;
1108 self
1109 }
1110 pub fn with_block_size(mut self, block_size: u32) -> Self {
1112 self.block_size = block_size;
1113 self
1114 }
1115 pub fn with_md5(mut self, md5: bool) -> Self {
1117 self.md5 = md5;
1118 self
1119 }
1120}
1121
1122pub struct Encoder {
1137 config: EncoderConfig,
1138 inner: Config,
1139}
1140
1141impl Encoder {
1142 pub fn new(config: EncoderConfig) -> Self {
1144 let inner = preset(config.compression_level);
1145 Self { config, inner }
1146 }
1147
1148 pub fn config(&self) -> &EncoderConfig {
1150 &self.config
1151 }
1152
1153 pub fn encode_frames(&self, interleaved: &[i32]) -> Vec<u8> {
1157 encode_frames(
1158 interleaved,
1159 self.config.channels,
1160 self.config.bits_per_sample,
1161 self.config.sample_rate,
1162 self.config.block_size,
1163 &self.inner,
1164 )
1165 }
1166
1167 pub fn encode(&self, interleaved: &[i32]) -> Vec<u8> {
1171 self.encode_with_metadata(
1172 interleaved,
1173 &[metadata::MetadataBlock::VorbisComment(
1174 metadata::LIBFLAC_VENDOR_STRING,
1175 )],
1176 )
1177 }
1178
1179 pub fn encode_with_metadata(
1187 &self,
1188 interleaved: &[i32],
1189 blocks: &[metadata::MetadataBlock],
1190 ) -> Vec<u8> {
1191 encode(
1192 interleaved,
1193 self.config.channels,
1194 self.config.bits_per_sample,
1195 self.config.sample_rate,
1196 self.config.block_size,
1197 &self.inner,
1198 self.config.md5,
1199 blocks,
1200 )
1201 }
1202
1203 pub fn encode_ogg(&self, interleaved: &[i32], serial: i32) -> Vec<u8> {
1207 encode_ogg(
1208 interleaved,
1209 self.config.channels,
1210 self.config.bits_per_sample,
1211 self.config.sample_rate,
1212 self.config.block_size,
1213 &self.inner,
1214 self.config.md5,
1215 &[metadata::MetadataBlock::VorbisComment(
1216 metadata::LIBFLAC_VENDOR_STRING,
1217 )],
1218 serial,
1219 )
1220 }
1221}