core/soundcvt: support conversion from packed audio
[nihav.git] / nihav-core / src / soundcvt / mod.rs
index 891a58a3933442d6d91a2e91192e74181acf62d2..bb307b91e581e3c21f51c83609cf55c9d0f68af3 100644 (file)
@@ -7,6 +7,7 @@ pub use crate::frame::{NAAudioBuffer,NAAudioInfo,NABufferType};
 use crate::formats::NAChannelType;
 use crate::frame::alloc_audio_buffer;
 use crate::io::byteio::*;
+use crate::io::bitreader::*;
 use std::f32::consts::SQRT_2;
 
 /// A list specifying general sound conversion errors.
@@ -29,15 +30,11 @@ enum ChannelOp {
 
 impl ChannelOp {
     fn is_remix(&self) -> bool {
-        match *self {
-            ChannelOp::Remix(_) => true,
-            ChannelOp::DupMono(_) => true,
-            _ => false,
-        }
+        matches! (*self, ChannelOp::Remix(_) | ChannelOp::DupMono(_))
     }
 }
 
-fn apply_channel_op<T:Copy>(ch_op: &ChannelOp, src: &[T], dst: &mut Vec<T>) {
+fn apply_channel_op<T:Copy>(ch_op: &ChannelOp, src: &[T], dst: &mut [T]) {
     match *ch_op {
         ChannelOp::Passthrough => {
             dst.copy_from_slice(src);
@@ -51,7 +48,7 @@ fn apply_channel_op<T:Copy>(ch_op: &ChannelOp, src: &[T], dst: &mut Vec<T>) {
     };
 }
 
-fn remix_i32(ch_op: &ChannelOp, src: &[i32], dst: &mut Vec<i32>) {
+fn remix_i32(ch_op: &ChannelOp, src: &[i32], dst: &mut [i32]) {
     if let ChannelOp::Remix(ref remix_mat) = ch_op {
         let sch = src.len();
         for (out, coeffs) in dst.iter_mut().zip(remix_mat.chunks(sch)) {
@@ -70,7 +67,7 @@ fn remix_i32(ch_op: &ChannelOp, src: &[i32], dst: &mut Vec<i32>) {
     }
 }
 
-fn remix_f32(ch_op: &ChannelOp, src: &[f32], dst: &mut Vec<f32>) {
+fn remix_f32(ch_op: &ChannelOp, src: &[f32], dst: &mut [f32]) {
     if let ChannelOp::Remix(ref remix_mat) = ch_op {
         let sch = src.len();
         for (out, coeffs) in dst.iter_mut().zip(remix_mat.chunks(sch)) {
@@ -164,8 +161,8 @@ impl<T:Copy, U:Copy> IntoFmt<U> for T where U: FromFmt<T> {
 
 
 trait SampleReader {
-    fn get_samples_i32(&self, pos: usize, dst: &mut Vec<i32>);
-    fn get_samples_f32(&self, pos: usize, dst: &mut Vec<f32>);
+    fn get_samples_i32(&self, pos: usize, dst: &mut [i32]);
+    fn get_samples_f32(&self, pos: usize, dst: &mut [f32]);
 }
 
 struct GenericSampleReader<'a, T:Copy> {
@@ -174,14 +171,14 @@ struct GenericSampleReader<'a, T:Copy> {
 }
 
 impl<'a, T:Copy+IntoFmt<i32>+IntoFmt<f32>> SampleReader for GenericSampleReader<'a, T> {
-    fn get_samples_i32(&self, pos: usize, dst: &mut Vec<i32>) {
+    fn get_samples_i32(&self, pos: usize, dst: &mut [i32]) {
         let mut off = pos;
         for el in dst.iter_mut() {
             *el = self.data[off].cvt_into();
             off += self.stride;
         }
     }
-    fn get_samples_f32(&self, pos: usize, dst: &mut Vec<f32>) {
+    fn get_samples_f32(&self, pos: usize, dst: &mut [f32]) {
         let mut off = pos;
         for el in dst.iter_mut() {
             *el = self.data[off].cvt_into();
@@ -196,14 +193,14 @@ struct S8SampleReader<'a> {
 }
 
 impl<'a> SampleReader for S8SampleReader<'a> {
-    fn get_samples_i32(&self, pos: usize, dst: &mut Vec<i32>) {
+    fn get_samples_i32(&self, pos: usize, dst: &mut [i32]) {
         let mut off = pos;
         for el in dst.iter_mut() {
             *el = (self.data[off] ^ 0x80).cvt_into();
             off += self.stride;
         }
     }
-    fn get_samples_f32(&self, pos: usize, dst: &mut Vec<f32>) {
+    fn get_samples_f32(&self, pos: usize, dst: &mut [f32]) {
         let mut off = pos;
         for el in dst.iter_mut() {
             *el = (self.data[off] ^ 0x80).cvt_into();
@@ -220,11 +217,32 @@ struct PackedSampleReader<'a> {
 
 impl<'a> PackedSampleReader<'a> {
     fn new(data: &'a [u8], fmt: NASoniton) -> Self {
-        if (fmt.bits & 7) != 0 { unimplemented!(); }
         let bpp = (fmt.bits >> 3) as usize;
         Self { data, fmt, bpp }
     }
-    fn get_samples<T:Copy>(&self, pos: usize, dst: &mut Vec<T>) where u8: IntoFmt<T>, i16: IntoFmt<T>, i32: IntoFmt<T>, f32: IntoFmt<T> {
+    fn get_samples<T:Copy>(&self, pos: usize, dst: &mut [T]) where u8: IntoFmt<T>, i16: IntoFmt<T>, i32: IntoFmt<T>, f32: IntoFmt<T> {
+        if (self.fmt.bits & 7) != 0 {
+            let mode = if self.fmt.be { BitReaderMode::BE } else { BitReaderMode::LE };
+            let mut br = BitReader::new(self.data, mode);
+            let offset = pos * (self.fmt.bits as usize) * dst.len();
+
+            if br.skip(offset as u32).is_ok() {
+                for el in dst.iter_mut() {
+                    if self.fmt.bits < 16 {
+                        *el = ((br.read(self.fmt.bits).unwrap_or(0) as i16) << (16 - self.fmt.bits)).cvt_into();
+                    } else {
+                        *el = ((br.read(self.fmt.bits).unwrap_or(0) as i32) << (32 - self.fmt.bits)).cvt_into();
+                    }
+                }
+            } else {
+                for el in dst.iter_mut() {
+                    *el = 0i16.cvt_into();
+                }
+            }
+
+            return;
+        }
+
         let mut offset = pos * self.bpp * dst.len();
 
         for el in dst.iter_mut() {
@@ -255,10 +273,10 @@ impl<'a> PackedSampleReader<'a> {
 }
 
 impl SampleReader for PackedSampleReader<'_> {
-    fn get_samples_i32(&self, pos: usize, dst: &mut Vec<i32>) {
+    fn get_samples_i32(&self, pos: usize, dst: &mut [i32]) {
         self.get_samples(pos, dst);
     }
-    fn get_samples_f32(&self, pos: usize, dst: &mut Vec<f32>) {
+    fn get_samples_f32(&self, pos: usize, dst: &mut [f32]) {
         self.get_samples(pos, dst);
     }
 }
@@ -349,7 +367,7 @@ impl SampleWriter for PackedSampleWriter<'_> {
 /// Converts input audio buffer into desired format and returns a newly allocated buffer.
 pub fn convert_audio_frame(src: &NABufferType, dst_info: &NAAudioInfo, dst_chmap: &NAChannelMap) ->
 Result<NABufferType, SoundConvertError> {
-    let mut nsamples = src.get_audio_length();
+    let nsamples = src.get_audio_length();
     if nsamples == 0 {
         return Err(SoundConvertError::InvalidInput);
     }
@@ -359,10 +377,6 @@ Result<NABufferType, SoundConvertError> {
         return Err(SoundConvertError::InvalidInput);
     }
 
-    if let NABufferType::AudioPacked(_) = src {
-        nsamples = nsamples * 8 / (src_info.get_format().get_bits() as usize) / src_chmap.num_channels();
-    }
-
     let needs_remix = src_chmap.num_channels() != dst_chmap.num_channels();
     let no_channel_needs = !needs_remix && channel_maps_equal(src_chmap, dst_chmap);
     let needs_reorder = !needs_remix && !no_channel_needs && channel_maps_reordered(src_chmap, dst_chmap);
@@ -653,5 +667,40 @@ mod test {
         } else {
             panic!("wrong buffer type");
         }
+
+        const PCM12: &[u8] = &[ 0x02, 0x50, 0x00, 0x07, 0x70, 0x00, 0x0D, 0x00 ];
+        const PCM12_SAMPLES: usize = 5;
+        let src_ainfo = NAAudioInfo {
+                            sample_rate:    44100,
+                            channels:       1,
+                            format:         NASoniton::new(12, SONITON_FLAG_PACKED | SONITON_FLAG_SIGNED),
+                            block_len:      3,
+                        };
+        let dst_ainfo = NAAudioInfo {
+                            sample_rate:    44100,
+                            channels:       1,
+                            format:         SND_S16P_FORMAT,
+                            block_len:      512,
+                        };
+        let mono = NAChannelMap::from_str("C").unwrap();
+        let mut src_frm = alloc_audio_buffer(src_ainfo, PCM12_SAMPLES, mono.clone()).unwrap();
+        if let NABufferType::AudioPacked(ref mut abuf) = src_frm {
+            let data = abuf.get_data_mut().unwrap();
+            data.copy_from_slice(PCM12);
+        } else {
+            panic!("wrong buffer type");
+        }
+        let out_frm = convert_audio_frame(&src_frm, &dst_ainfo, &mono).unwrap();
+        if let NABufferType::AudioI16(ref abuf) = out_frm {
+            let data = abuf.get_data();
+            assert_eq!(data.len(), PCM12_SAMPLES);
+            assert_eq!(data[0], 0x0020);
+            assert_eq!(data[1], 0x0050);
+            assert_eq!(data[2], 0x0070);
+            assert_eq!(data[3], 0x0070);
+            assert_eq!(data[4], 0x00D0);
+        } else {
+            panic!("wrong buffer type");
+        }
     }
 }