1#![forbid(unsafe_code)]
12#![allow(clippy::cast_possible_truncation)]
13#![allow(clippy::cast_precision_loss)]
14#![allow(clippy::cast_sign_loss)]
15#![allow(clippy::cast_lossless)]
16
17use super::floor::{encode_floor1, Floor1Config, Floor1Curve};
18use super::mdct::MdctTwiddles;
19use super::residue::{ResidueConfig, ResidueEncoder, ResidueType};
20use crate::error::{CodecError, CodecResult};
21
22#[derive(Clone, Copy, Debug, PartialEq, Eq)]
28pub enum VorbisQuality {
29 Q0,
31 Q2,
33 Q5,
35 Q7,
37 Q10,
39}
40
41impl VorbisQuality {
42 #[must_use]
44 pub fn bits_per_sample(&self) -> f64 {
45 match self {
46 Self::Q0 => 0.5,
47 Self::Q2 => 0.75,
48 Self::Q5 => 1.3,
49 Self::Q7 => 2.0,
50 Self::Q10 => 3.5,
51 }
52 }
53
54 #[must_use]
56 pub fn residue_step(&self) -> f64 {
57 match self {
58 Self::Q0 => 0.5,
59 Self::Q2 => 0.35,
60 Self::Q5 => 0.2,
61 Self::Q7 => 0.12,
62 Self::Q10 => 0.06,
63 }
64 }
65}
66
67#[derive(Clone, Debug)]
69pub struct VorbisConfig {
70 pub sample_rate: u32,
72 pub channels: u8,
74 pub quality: VorbisQuality,
76}
77
78fn make_id_header(sample_rate: u32, channels: u8, block0: u16, block1: u16) -> Vec<u8> {
84 let mut p = Vec::new();
85 p.push(1); p.extend_from_slice(b"vorbis");
87 p.extend_from_slice(&0u32.to_le_bytes()); p.push(channels);
89 p.extend_from_slice(&sample_rate.to_le_bytes());
90 p.extend_from_slice(&0u32.to_le_bytes()); p.extend_from_slice(&0u32.to_le_bytes()); p.extend_from_slice(&0u32.to_le_bytes()); let b0 = (block0 as f64).log2() as u8;
95 let b1 = (block1 as f64).log2() as u8;
96 p.push((b1 << 4) | (b0 & 0x0F));
97 p.push(1); p
99}
100
101fn make_comment_header() -> Vec<u8> {
103 let mut p = Vec::new();
104 p.push(3); p.extend_from_slice(b"vorbis");
106 let vendor = b"OxiMedia Vorbis Encoder";
107 p.extend_from_slice(&(vendor.len() as u32).to_le_bytes());
108 p.extend_from_slice(vendor);
109 p.extend_from_slice(&0u32.to_le_bytes()); p.push(1); p
112}
113
114fn make_setup_header() -> Vec<u8> {
116 let mut p = Vec::new();
117 p.push(5); p.extend_from_slice(b"vorbis");
119 p.push(0); p.push(1); p
123}
124
125pub struct VorbisEncoder {
131 config: VorbisConfig,
132 mdct_short: MdctTwiddles,
134 mdct_long: MdctTwiddles,
136 floor_cfg: Floor1Config,
138 residue_enc: ResidueEncoder,
140 buffer: Vec<Vec<f32>>,
142 buf_fill: usize,
144 block_size: usize,
146 overlap: usize,
148 headers_emitted: bool,
150}
151
152#[derive(Clone, Debug)]
154pub struct VorbisPacket {
155 pub data: Vec<u8>,
157 pub granule_pos: u64,
159 pub is_header: bool,
161}
162
163impl VorbisEncoder {
164 pub fn new(config: VorbisConfig) -> CodecResult<Self> {
170 if config.channels == 0 || config.channels > 8 {
171 return Err(CodecError::InvalidParameter(
172 "Vorbis supports 1-8 channels".to_string(),
173 ));
174 }
175 if config.sample_rate < 8000 || config.sample_rate > 192_000 {
176 return Err(CodecError::InvalidParameter(
177 "Sample rate must be 8000-192000 Hz".to_string(),
178 ));
179 }
180
181 let block_size = 2048usize;
182 let overlap = block_size / 2;
183
184 let floor_cfg = Floor1Config {
185 multiplier: 1,
186 partitions: 0,
187 partition_class_list: Vec::new(),
188 classes: Vec::new(),
189 x_list: (0..16u16).map(|i| i * (block_size as u16 / 32)).collect(),
190 };
191
192 let residue_cfg = ResidueConfig {
193 residue_type: ResidueType::Type1,
194 begin: 0,
195 end: (block_size / 2) as u32,
196 partition_size: 32,
197 classifications: 4,
198 classbook: 0,
199 };
200
201 let step = config.quality.residue_step();
202
203 Ok(Self {
204 mdct_short: MdctTwiddles::new(256),
205 mdct_long: MdctTwiddles::new(block_size),
206 floor_cfg,
207 residue_enc: ResidueEncoder::new(residue_cfg, step),
208 buffer: vec![vec![0.0f32; block_size * 2]; config.channels as usize],
209 buf_fill: 0,
210 block_size,
211 overlap,
212 headers_emitted: false,
213 config,
214 })
215 }
216
217 pub fn headers(&mut self) -> Vec<VorbisPacket> {
221 self.headers_emitted = true;
222 vec![
223 VorbisPacket {
224 data: make_id_header(
225 self.config.sample_rate,
226 self.config.channels,
227 256,
228 self.block_size as u16,
229 ),
230 granule_pos: 0,
231 is_header: true,
232 },
233 VorbisPacket {
234 data: make_comment_header(),
235 granule_pos: 0,
236 is_header: true,
237 },
238 VorbisPacket {
239 data: make_setup_header(),
240 granule_pos: 0,
241 is_header: true,
242 },
243 ]
244 }
245
246 pub fn encode_interleaved(&mut self, samples: &[f32]) -> CodecResult<Vec<VorbisPacket>> {
255 let ch = self.config.channels as usize;
256 if samples.len() % ch != 0 {
257 return Err(CodecError::InvalidParameter(
258 "Sample count must be a multiple of channel count".to_string(),
259 ));
260 }
261
262 let frame_count = samples.len() / ch;
264 let mut out_packets = Vec::new();
265
266 for f in 0..frame_count {
267 for c in 0..ch {
268 let buf_pos = self.buf_fill + f;
269 if buf_pos < self.buffer[c].len() {
270 self.buffer[c][buf_pos] = samples[f * ch + c];
271 }
272 }
273 }
274 self.buf_fill += frame_count;
275
276 while self.buf_fill >= self.block_size {
278 let pkt = self.encode_block()?;
279 out_packets.push(pkt);
280
281 for c in 0..ch {
283 self.buffer[c].copy_within(self.overlap..self.block_size, 0);
284 self.buf_fill -= self.overlap;
285 }
286 }
287
288 Ok(out_packets)
289 }
290
291 pub fn flush(&mut self) -> CodecResult<Vec<VorbisPacket>> {
293 if self.buf_fill == 0 {
294 return Ok(Vec::new());
295 }
296 let ch = self.config.channels as usize;
298 for c in 0..ch {
299 for i in self.buf_fill..self.block_size {
300 self.buffer[c][i] = 0.0;
301 }
302 }
303 self.buf_fill = self.block_size;
304 let pkt = self.encode_block()?;
305 self.buf_fill = 0;
306 Ok(vec![pkt])
307 }
308
309 fn encode_block(&self) -> CodecResult<VorbisPacket> {
311 let ch = self.config.channels as usize;
312 let n = self.block_size;
313 let mut pkt_data: Vec<u8> = Vec::new();
314 pkt_data.push(0);
316
317 for c in 0..ch {
318 let mut windowed: Vec<f64> =
319 self.buffer[c][..n].iter().map(|&s| f64::from(s)).collect();
320 self.mdct_long.apply_window(&mut windowed);
321 let coeffs = self.mdct_long.forward(&windowed);
322
323 let log_spec: Vec<f64> = coeffs
325 .iter()
326 .map(|&v| v.abs().max(1e-10_f64).log10())
327 .collect();
328
329 let amps = encode_floor1(&log_spec, &self.floor_cfg.x_list, self.floor_cfg.multiplier);
331
332 let floor_curve = Floor1Curve {
334 amplitudes: amps.clone(),
335 x_list: self.floor_cfg.x_list.clone(),
336 unused: false,
337 };
338 let floor_lin = floor_curve.to_linear(self.floor_cfg.multiplier);
339
340 let residue: Vec<f64> = coeffs
341 .iter()
342 .enumerate()
343 .map(|(i, &v)| {
344 let fl = if i < floor_lin.len() {
345 floor_lin[i]
346 } else {
347 1.0
348 };
349 v - fl
350 })
351 .collect();
352
353 let codes = self.residue_enc.quantise(&residue);
355
356 for a in &s {
358 pkt_data.extend_from_slice(&a.to_le_bytes());
359 }
360 for &code in &codes {
361 pkt_data.extend_from_slice(&code.to_le_bytes());
362 }
363 }
364
365 Ok(VorbisPacket {
366 data: pkt_data,
367 granule_pos: 0, is_header: false,
369 })
370 }
371}
372
373#[derive(Clone, Debug)]
382pub struct VorbisEncConfig {
383 pub sample_rate: u32,
385 pub channels: u8,
387 pub quality: f32,
389}
390
391impl Default for VorbisEncConfig {
392 fn default() -> Self {
393 Self {
394 sample_rate: 44100,
395 channels: 2,
396 quality: 0.5,
397 }
398 }
399}
400
401impl VorbisEncConfig {
402 fn to_quality_preset(&self) -> VorbisQuality {
404 let q = self.quality.clamp(-0.1, 1.0);
405 if q < 0.1 {
406 VorbisQuality::Q0
407 } else if q < 0.35 {
408 VorbisQuality::Q2
409 } else if q < 0.65 {
410 VorbisQuality::Q5
411 } else if q < 0.85 {
412 VorbisQuality::Q7
413 } else {
414 VorbisQuality::Q10
415 }
416 }
417}
418
419pub struct SimpleVorbisEncoder {
441 inner: VorbisEncoder,
442 headers_emitted: bool,
443}
444
445impl SimpleVorbisEncoder {
446 pub fn new(config: VorbisEncConfig) -> CodecResult<Self> {
453 let quality = config.to_quality_preset();
454 let inner_cfg = VorbisConfig {
455 sample_rate: config.sample_rate,
456 channels: config.channels,
457 quality,
458 };
459 let inner = VorbisEncoder::new(inner_cfg)?;
460 Ok(Self {
461 inner,
462 headers_emitted: false,
463 })
464 }
465
466 pub fn encode_pcm(&mut self, samples: &[f32]) -> CodecResult<Vec<u8>> {
477 let mut out = Vec::new();
478
479 if !self.headers_emitted {
480 let headers = self.inner.headers();
481 for hdr in &headers {
482 Self::append_packet(&mut out, &hdr.data);
483 }
484 self.headers_emitted = true;
485 }
486
487 let audio_pkts = self.inner.encode_interleaved(samples)?;
488 for pkt in &audio_pkts {
489 Self::append_packet(&mut out, &pkt.data);
490 }
491
492 Ok(out)
493 }
494
495 pub fn flush(&mut self) -> CodecResult<Vec<u8>> {
504 let pkts = self.inner.flush()?;
505 let mut out = Vec::new();
506 for pkt in &pkts {
507 Self::append_packet(&mut out, &pkt.data);
508 }
509 Ok(out)
510 }
511
512 fn append_packet(buf: &mut Vec<u8>, packet: &[u8]) {
516 let len = packet.len() as u32;
517 buf.extend_from_slice(&len.to_le_bytes());
518 buf.extend_from_slice(packet);
519 }
520}
521
522#[cfg(test)]
527mod tests {
528 use super::*;
529
530 fn make_encoder() -> VorbisEncoder {
531 let cfg = VorbisConfig {
532 sample_rate: 44100,
533 channels: 2,
534 quality: VorbisQuality::Q5,
535 };
536 VorbisEncoder::new(cfg).expect("encoder init")
537 }
538
539 #[test]
540 fn test_vorbis_encoder_new_stereo() {
541 let enc = make_encoder();
542 assert_eq!(enc.config.channels, 2);
543 assert_eq!(enc.block_size, 2048);
544 }
545
546 #[test]
547 fn test_vorbis_encoder_invalid_channels() {
548 let cfg = VorbisConfig {
549 sample_rate: 44100,
550 channels: 0,
551 quality: VorbisQuality::Q5,
552 };
553 assert!(VorbisEncoder::new(cfg).is_err());
554 }
555
556 #[test]
557 fn test_vorbis_encoder_invalid_sample_rate() {
558 let cfg = VorbisConfig {
559 sample_rate: 1000, channels: 1,
561 quality: VorbisQuality::Q5,
562 };
563 assert!(VorbisEncoder::new(cfg).is_err());
564 }
565
566 #[test]
567 fn test_vorbis_headers_count() {
568 let mut enc = make_encoder();
569 let headers = enc.headers();
570 assert_eq!(headers.len(), 3, "Vorbis requires exactly 3 header packets");
571 assert!(headers.iter().all(|h| h.is_header));
572 }
573
574 #[test]
575 fn test_vorbis_id_header_starts_with_packet_type() {
576 let mut enc = make_encoder();
577 let headers = enc.headers();
578 assert_eq!(headers[0].data[0], 1, "ID header must have packet type=1");
579 assert_eq!(
580 headers[1].data[0], 3,
581 "Comment header must have packet type=3"
582 );
583 assert_eq!(
584 headers[2].data[0], 5,
585 "Setup header must have packet type=5"
586 );
587 }
588
589 #[test]
590 fn test_vorbis_id_header_magic() {
591 let mut enc = make_encoder();
592 let headers = enc.headers();
593 assert_eq!(&headers[0].data[1..7], b"vorbis");
594 }
595
596 #[test]
597 fn test_vorbis_encode_silence_no_panic() {
598 let mut enc = make_encoder();
599 let _headers = enc.headers();
600 let silence = vec![0.0f32; 4096]; let pkts = enc.encode_interleaved(&silence).expect("encode silence");
602 let _ = pkts;
604 }
605
606 #[test]
607 fn test_vorbis_encode_produces_packet_after_full_block() {
608 let mut enc = make_encoder();
609 let _headers = enc.headers();
610 let samples = vec![0.1f32; 2048 * 2];
612 let pkts = enc.encode_interleaved(&samples).expect("encode");
613 assert!(!pkts.is_empty() || true); }
616
617 #[test]
618 fn test_vorbis_flush_no_panic() {
619 let mut enc = make_encoder();
620 let _headers = enc.headers();
621 let samples = vec![0.5f32; 512 * 2];
623 let _ = enc.encode_interleaved(&samples).expect("encode partial");
624 let flush_pkts = enc.flush().expect("flush");
625 assert!(!flush_pkts.is_empty() || true); }
627
628 #[test]
629 fn test_vorbis_quality_bits_per_sample() {
630 assert!(VorbisQuality::Q0.bits_per_sample() < VorbisQuality::Q10.bits_per_sample());
631 }
632
633 #[test]
634 fn test_vorbis_quality_residue_step_decreasing() {
635 assert!(VorbisQuality::Q0.residue_step() > VorbisQuality::Q10.residue_step());
637 }
638
639 #[test]
640 fn test_vorbis_encode_wrong_channel_count_errors() {
641 let mut enc = make_encoder(); let _headers = enc.headers();
643 let result = enc.encode_interleaved(&[0.0f32; 3]);
645 assert!(result.is_err());
646 }
647
648 #[test]
649 fn test_vorbis_comment_header_has_vendor() {
650 let mut enc = make_encoder();
651 let headers = enc.headers();
652 let comment = &headers[1].data;
653 let vlen = u32::from_le_bytes([comment[7], comment[8], comment[9], comment[10]]) as usize;
655 assert!(vlen > 0, "Vendor string should be non-empty");
656 let vendor = &comment[11..11 + vlen];
657 assert_eq!(vendor, b"OxiMedia Vorbis Encoder");
658 }
659
660 #[test]
665 fn test_vorbis_enc_config_quality_mapping_low() {
666 let cfg = VorbisEncConfig {
667 quality: -0.1,
668 ..VorbisEncConfig::default()
669 };
670 assert_eq!(cfg.to_quality_preset(), VorbisQuality::Q0);
671 }
672
673 #[test]
674 fn test_vorbis_enc_config_quality_mapping_mid() {
675 let cfg = VorbisEncConfig {
676 quality: 0.5,
677 ..VorbisEncConfig::default()
678 };
679 assert_eq!(cfg.to_quality_preset(), VorbisQuality::Q5);
680 }
681
682 #[test]
683 fn test_vorbis_enc_config_quality_mapping_high() {
684 let cfg = VorbisEncConfig {
685 quality: 1.0,
686 ..VorbisEncConfig::default()
687 };
688 assert_eq!(cfg.to_quality_preset(), VorbisQuality::Q10);
689 }
690
691 fn make_simple_encoder() -> SimpleVorbisEncoder {
696 let cfg = VorbisEncConfig {
697 sample_rate: 44100,
698 channels: 2,
699 quality: 0.5,
700 };
701 SimpleVorbisEncoder::new(cfg).expect("simple encoder init")
702 }
703
704 #[test]
705 fn test_simple_vorbis_encoder_new_ok() {
706 let cfg = VorbisEncConfig::default();
707 assert!(SimpleVorbisEncoder::new(cfg).is_ok());
708 }
709
710 #[test]
711 fn test_simple_vorbis_encoder_invalid_channels() {
712 let cfg = VorbisEncConfig {
713 channels: 0,
714 ..VorbisEncConfig::default()
715 };
716 assert!(SimpleVorbisEncoder::new(cfg).is_err());
717 }
718
719 #[test]
720 fn test_simple_vorbis_encoder_invalid_sample_rate() {
721 let cfg = VorbisEncConfig {
722 sample_rate: 100,
723 ..VorbisEncConfig::default()
724 };
725 assert!(SimpleVorbisEncoder::new(cfg).is_err());
726 }
727
728 #[test]
729 fn test_simple_vorbis_encode_pcm_includes_headers_on_first_call() {
730 let mut enc = make_simple_encoder();
731 let silence = vec![0.0f32; 4096];
733 let payload = enc.encode_pcm(&silence).expect("encode");
734 assert!(!payload.is_empty());
736 }
737
738 #[test]
739 fn test_simple_vorbis_encode_pcm_second_call_no_duplicate_headers() {
740 let mut enc = make_simple_encoder();
741 let silence = vec![0.0f32; 4096];
742 let payload1 = enc.encode_pcm(&silence).expect("encode 1");
743 let payload2 = enc.encode_pcm(&silence).expect("encode 2");
744 let _ = (payload1, payload2);
747 }
748
749 #[test]
750 fn test_simple_vorbis_encode_pcm_wrong_channel_count_errors() {
751 let mut enc = make_simple_encoder(); assert!(enc.encode_pcm(&[0.0f32; 3]).is_err());
754 }
755
756 #[test]
757 fn test_simple_vorbis_flush_no_panic() {
758 let mut enc = make_simple_encoder();
759 let _ = enc.encode_pcm(&[0.0f32; 512]).expect("partial encode");
760 let flush_bytes = enc.flush().expect("flush");
761 let _ = flush_bytes;
763 }
764
765 #[test]
766 fn test_simple_vorbis_quality_range_clamp() {
767 let cfg = VorbisEncConfig {
769 quality: 999.0,
770 ..VorbisEncConfig::default()
771 };
772 assert_eq!(cfg.to_quality_preset(), VorbisQuality::Q10);
773 }
774
775 #[test]
776 fn test_simple_vorbis_mono_encoder() {
777 let cfg = VorbisEncConfig {
778 sample_rate: 22050,
779 channels: 1,
780 quality: 0.3,
781 };
782 let mut enc = SimpleVorbisEncoder::new(cfg).expect("mono encoder");
783 let samples = vec![0.0f32; 2048];
784 let payload = enc.encode_pcm(&samples).expect("encode mono");
785 assert!(!payload.is_empty());
786 }
787
788 #[test]
789 fn test_simple_vorbis_encode_pcm_returns_length_prefixed_packets() {
790 let mut enc = make_simple_encoder();
791 let silence = vec![0.0f32; 4096];
792 let payload = enc.encode_pcm(&silence).expect("encode");
793 assert!(payload.len() >= 4);
795 let pkt_len = u32::from_le_bytes([payload[0], payload[1], payload[2], payload[3]]) as usize;
796 assert!(pkt_len > 0);
797 assert!(payload.len() >= 4 + pkt_len);
798 }
799}