core/soundcvt: fix u8->i16 test
[nihav.git] / nihav-core / src / soundcvt / mod.rs
index 8d1ebb8a1487fca180afffeecc585902225f311a..891a58a3933442d6d91a2e91192e74181acf62d2 100644 (file)
@@ -1,3 +1,7 @@
+//! Sound format conversion.
+//!
+//! This module implements the functionality for conversion between different sound formats: packed or planar audio, 8-/16-/24-/32-bit, integer or floating point, different number of channels.
+//! Eventually this might support resampling as well.
 pub use crate::formats::{NASoniton,NAChannelMap};
 pub use crate::frame::{NAAudioBuffer,NAAudioInfo,NABufferType};
 use crate::formats::NAChannelType;
@@ -5,10 +9,14 @@ use crate::frame::alloc_audio_buffer;
 use crate::io::byteio::*;
 use std::f32::consts::SQRT_2;
 
+/// A list specifying general sound conversion errors.
 #[derive(Clone,Copy,Debug,PartialEq)]
 pub enum SoundConvertError {
+    /// Invalid input arguments.
     InvalidInput,
+    /// Allocation failed.
     AllocError,
+    /// Requested feature is not supported.
     Unsupported,
 }
 
@@ -29,10 +37,10 @@ impl ChannelOp {
     }
 }
 
-fn apply_channel_op<T:Copy>(ch_op: &ChannelOp, src: &Vec<T>, dst: &mut Vec<T>) {
+fn apply_channel_op<T:Copy>(ch_op: &ChannelOp, src: &[T], dst: &mut Vec<T>) {
     match *ch_op {
         ChannelOp::Passthrough => {
-            dst.copy_from_slice(src.as_slice());
+            dst.copy_from_slice(src);
         },
         ChannelOp::Reorder(ref reorder) => {
             for (out, idx) in dst.iter_mut().zip(reorder.iter()) {
@@ -43,7 +51,7 @@ fn apply_channel_op<T:Copy>(ch_op: &ChannelOp, src: &Vec<T>, dst: &mut Vec<T>) {
     };
 }
 
-fn remix_i32(ch_op: &ChannelOp, src: &Vec<i32>, dst: &mut Vec<i32>) {
+fn remix_i32(ch_op: &ChannelOp, src: &[i32], dst: &mut Vec<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)) {
@@ -62,7 +70,7 @@ fn remix_i32(ch_op: &ChannelOp, src: &Vec<i32>, dst: &mut Vec<i32>) {
     }
 }
 
-fn remix_f32(ch_op: &ChannelOp, src: &Vec<f32>, dst: &mut Vec<f32>) {
+fn remix_f32(ch_op: &ChannelOp, src: &[f32], dst: &mut Vec<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)) {
@@ -89,13 +97,13 @@ impl FromFmt<u8> for u8 {
     fn cvt_from(val: u8) -> u8 { val }
 }
 impl FromFmt<u8> for i16 {
-    fn cvt_from(val: u8) -> i16 { ((val as i16) - 128).wrapping_mul(0x101) }
+    fn cvt_from(val: u8) -> i16 { u16::from(val ^ 0x80).wrapping_mul(0x101) as i16}
 }
 impl FromFmt<u8> for i32 {
-    fn cvt_from(val: u8) -> i32 { ((val as i32) - 128).wrapping_mul(0x01010101) }
+    fn cvt_from(val: u8) -> i32 { u32::from(val ^ 0x80).wrapping_mul(0x01010101) as i32 }
 }
 impl FromFmt<u8> for f32 {
-    fn cvt_from(val: u8) -> f32 { ((val as f32) - 128.0) / 128.0 }
+    fn cvt_from(val: u8) -> f32 { (f32::from(val) - 128.0) / 128.0 }
 }
 
 impl FromFmt<i16> for u8 {
@@ -105,10 +113,10 @@ impl FromFmt<i16> for i16 {
     fn cvt_from(val: i16) -> i16 { val }
 }
 impl FromFmt<i16> for i32 {
-    fn cvt_from(val: i16) -> i32 { (val as i32).wrapping_mul(0x10001) }
+    fn cvt_from(val: i16) -> i32 { (i32::from(val) & 0xFFFF) | (i32::from(val) << 16) }
 }
 impl FromFmt<i16> for f32 {
-    fn cvt_from(val: i16) -> f32 { (val as f32) / 32768.0 }
+    fn cvt_from(val: i16) -> f32 { f32::from(val) / 32768.0 }
 }
 
 impl FromFmt<i32> for u8 {
@@ -128,10 +136,19 @@ impl FromFmt<f32> for u8 {
     fn cvt_from(val: f32) -> u8 { ((val * 128.0) + 128.0).min(255.0).max(0.0) as u8 }
 }
 impl FromFmt<f32> for i16 {
-    fn cvt_from(val: f32) -> i16 { (val * 32768.0).min(16383.0).max(-16384.0) as i16 }
+    fn cvt_from(val: f32) -> i16 { (val * 32768.0).min(32767.0).max(-32768.0) as i16 }
 }
 impl FromFmt<f32> for i32 {
-    fn cvt_from(val: f32) -> i32 { (val * 31.0f32.exp2()) as i32 }
+    fn cvt_from(val: f32) -> i32 {
+        if val >= 1.0 {
+            std::i32::MAX
+        } else if val <= -1.0 {
+            std::i32::MIN
+        } else {
+            let scale = (1u32 << 31) as f32;
+            (val * scale) as i32
+        }
+    }
 }
 impl FromFmt<f32> for f32 {
     fn cvt_from(val: f32) -> f32 { val }
@@ -173,6 +190,28 @@ impl<'a, T:Copy+IntoFmt<i32>+IntoFmt<f32>> SampleReader for GenericSampleReader<
     }
 }
 
+struct S8SampleReader<'a> {
+    data:   &'a [u8],
+    stride: usize,
+}
+
+impl<'a> SampleReader for S8SampleReader<'a> {
+    fn get_samples_i32(&self, pos: usize, dst: &mut Vec<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>) {
+        let mut off = pos;
+        for el in dst.iter_mut() {
+            *el = (self.data[off] ^ 0x80).cvt_into();
+            off += self.stride;
+        }
+    }
+}
+
 struct PackedSampleReader<'a> {
     data:   &'a [u8],
     fmt:    NASoniton,
@@ -192,7 +231,7 @@ impl<'a> PackedSampleReader<'a> {
             let src = &self.data[offset..];
             *el = if !self.fmt.float {
                     match (self.bpp, self.fmt.be) {
-                        (1, _)     => src[0].cvt_into(),
+                        (1, _)     => if !self.fmt.signed { src[0].cvt_into() } else { (src[0] ^ 0x80).cvt_into() },
                         (2, true)  => (read_u16be(src).unwrap() as i16).cvt_into(),
                         (2, false) => (read_u16le(src).unwrap() as i16).cvt_into(),
                         (3, true)  => ((read_u24be(src).unwrap() << 8) as i32).cvt_into(),
@@ -225,8 +264,8 @@ impl SampleReader for PackedSampleReader<'_> {
 }
 
 trait SampleWriter {
-    fn store_samples_i32(&mut self, pos: usize, src: &Vec<i32>);
-    fn store_samples_f32(&mut self, pos: usize, src: &Vec<f32>);
+    fn store_samples_i32(&mut self, pos: usize, src: &[i32]);
+    fn store_samples_f32(&mut self, pos: usize, src: &[f32]);
 }
 
 struct GenericSampleWriter<'a, T:Copy> {
@@ -235,14 +274,14 @@ struct GenericSampleWriter<'a, T:Copy> {
 }
 
 impl<'a, T:Copy+FromFmt<i32>+FromFmt<f32>> SampleWriter for GenericSampleWriter<'a, T> {
-    fn store_samples_i32(&mut self, pos: usize, src: &Vec<i32>) {
+    fn store_samples_i32(&mut self, pos: usize, src: &[i32]) {
         let mut off = pos;
         for el in src.iter() {
             self.data[off] = (*el).cvt_into();
             off += self.stride;
         }
     }
-    fn store_samples_f32(&mut self, pos: usize, src: &Vec<f32>) {
+    fn store_samples_f32(&mut self, pos: usize, src: &[f32]) {
         let mut off = pos;
         for el in src.iter() {
             self.data[off] = (*el).cvt_into();
@@ -264,7 +303,7 @@ impl<'a> PackedSampleWriter<'a> {
         Self { data, fmt, bpp }
     }
 
-    fn store_samples<T:Copy>(&mut self, pos: usize, src: &Vec<T>) where u8: FromFmt<T>, i16: FromFmt<T>, i32: FromFmt<T>, f32: FromFmt<T> {
+    fn store_samples<T:Copy>(&mut self, pos: usize, src: &[T]) where u8: FromFmt<T>, i16: FromFmt<T>, i32: FromFmt<T>, f32: FromFmt<T> {
         let mut offset = pos * self.bpp * src.len();
         for el in src.iter() {
             let dst = &mut self.data[offset..];
@@ -272,6 +311,9 @@ impl<'a> PackedSampleWriter<'a> {
                 match (self.bpp, self.fmt.be) {
                     (1, _) => {
                         dst[0] = u8::cvt_from(*el);
+                        if self.fmt.signed {
+                            dst[0] ^= 0x80;
+                        }
                     },
                     (2, true)  => write_u16be(dst, i16::cvt_from(*el) as u16).unwrap(),
                     (2, false) => write_u16le(dst, i16::cvt_from(*el) as u16).unwrap(),
@@ -285,8 +327,8 @@ impl<'a> PackedSampleWriter<'a> {
                 match (self.bpp, self.fmt.be) {
                     (4, true)  => write_f32be(dst, f32::cvt_from(*el)).unwrap(),
                     (4, false) => write_f32le(dst, f32::cvt_from(*el)).unwrap(),
-                    (8, true)  => write_f64be(dst, f32::cvt_from(*el) as f64).unwrap(),
-                    (8, false) => write_f64le(dst, f32::cvt_from(*el) as f64).unwrap(),
+                    (8, true)  => write_f64be(dst, f64::from(f32::cvt_from(*el))).unwrap(),
+                    (8, false) => write_f64le(dst, f64::from(f32::cvt_from(*el))).unwrap(),
                     (_, _) => unreachable!(),
                 };
             }
@@ -296,14 +338,15 @@ impl<'a> PackedSampleWriter<'a> {
 }
 
 impl SampleWriter for PackedSampleWriter<'_> {
-    fn store_samples_i32(&mut self, pos: usize, src: &Vec<i32>) {
+    fn store_samples_i32(&mut self, pos: usize, src: &[i32]) {
         self.store_samples(pos, src);
     }
-    fn store_samples_f32(&mut self, pos: usize, src: &Vec<f32>) {
+    fn store_samples_f32(&mut self, pos: usize, src: &[f32]) {
         self.store_samples(pos, src);
     }
 }
 
+/// 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();
@@ -353,17 +396,23 @@ Result<NABufferType, SoundConvertError> {
         return Ok(src.clone());
     }
 
-    let ret = alloc_audio_buffer(dst_info.clone(), nsamples, dst_chmap.clone());
+    let ret = alloc_audio_buffer(*dst_info, nsamples, dst_chmap.clone());
     if ret.is_err() {
         return Err(SoundConvertError::AllocError);
     }
     let mut dst_buf = ret.unwrap();
 
+    let sstep = src.get_audio_step().max(1);
+    let dstep = dst_buf.get_audio_step().max(1);
     let sr: Box<dyn SampleReader> = match src {
             NABufferType::AudioU8(ref ab) => {
                 let stride = ab.get_stride();
                 let data = ab.get_data();
-                Box::new(GenericSampleReader { data, stride })
+                if !src_fmt.signed {
+                    Box::new(GenericSampleReader { data, stride })
+                } else {
+                    Box::new(S8SampleReader { data, stride })
+                }
             },
             NABufferType::AudioI16(ref ab) => {
                 let data = ab.get_data();
@@ -418,26 +467,34 @@ Result<NABufferType, SoundConvertError> {
     if !into_float {
         let mut svec = vec![0; src_chmap.num_channels()];
         let mut dvec = vec![0; dst_chmap.num_channels()];
-        for i in 0..nsamples {
-            sr.get_samples_i32(i, &mut svec);
+        let mut spos = 0;
+        let mut dpos = 0;
+        for _ in 0..nsamples {
+            sr.get_samples_i32(spos, &mut svec);
             if !channel_op.is_remix() {
                 apply_channel_op(&channel_op, &svec, &mut dvec);
             } else {
                 remix_i32(&channel_op, &svec, &mut dvec);
             }
-            sw.store_samples_i32(i, &dvec);
+            sw.store_samples_i32(dpos, &dvec);
+            spos += sstep;
+            dpos += dstep;
         }
     } else {
         let mut svec = vec![0.0; src_chmap.num_channels()];
         let mut dvec = vec![0.0; dst_chmap.num_channels()];
-        for i in 0..nsamples {
-            sr.get_samples_f32(i, &mut svec);
+        let mut spos = 0;
+        let mut dpos = 0;
+        for _ in 0..nsamples {
+            sr.get_samples_f32(spos, &mut svec);
             if !channel_op.is_remix() {
                 apply_channel_op(&channel_op, &svec, &mut dvec);
             } else {
                 remix_f32(&channel_op, &svec, &mut dvec);
             }
-            sw.store_samples_f32(i, &dvec);
+            sw.store_samples_f32(dpos, &dvec);
+            spos += sstep;
+            dpos += dstep;
         }
     }
     drop(sw);
@@ -445,6 +502,7 @@ Result<NABufferType, SoundConvertError> {
     Ok(dst_buf)
 }
 
+/// Checks whether two channel maps are identical.
 pub fn channel_maps_equal(a: &NAChannelMap, b: &NAChannelMap) -> bool {
     if a.num_channels() != b.num_channels() { return false; }
     for i in 0..a.num_channels() {
@@ -455,6 +513,7 @@ pub fn channel_maps_equal(a: &NAChannelMap, b: &NAChannelMap) -> bool {
     true
 }
 
+/// Checks whether two channel maps have identical channels (but maybe in different order).
 pub fn channel_maps_reordered(a: &NAChannelMap, b: &NAChannelMap) -> bool {
     if a.num_channels() != b.num_channels() { return false; }
     let mut count_a = [0u8; 32];
@@ -471,6 +530,7 @@ pub fn channel_maps_reordered(a: &NAChannelMap, b: &NAChannelMap) -> bool {
     true
 }
 
+/// Calculates permutation matrix for reordering channels from source channel map into destination one.
 pub fn calculate_reorder_matrix(src: &NAChannelMap, dst: &NAChannelMap) -> Vec<usize> {
     if src.num_channels() != dst.num_channels() { return Vec::new(); }
     let num_channels = src.num_channels();
@@ -494,6 +554,7 @@ fn is_stereo(chmap: &NAChannelMap) -> bool {
     (chmap.get_channel(1) == NAChannelType::R)
 }
 
+/// Calculates matrix of remixing coefficients for converting input channel layout into destination one.
 pub fn calculate_remix_matrix(src: &NAChannelMap, dst: &NAChannelMap) -> Vec<f32> {
     if is_stereo(src) && dst.num_channels() == 1 &&
         (dst.get_channel(0) == NAChannelType::L || dst.get_channel(0) == NAChannelType::C) {
@@ -553,7 +614,7 @@ mod test {
                             block_len:      512,
                         };
         let mut src_frm = alloc_audio_buffer(src_ainfo, 42, chcfg51.clone()).unwrap();
-        if let NABufferType::AudioPacked(ref mut abuf) = src_frm {
+        if let NABufferType::AudioU8(ref mut abuf) = src_frm {
             let data = abuf.get_data_mut().unwrap();
             let mut idx = 0;
             for _ in 0..42 {
@@ -574,7 +635,7 @@ mod test {
             let l = data[off0];
             let r = data[off1];
             assert_eq!(l, 7445);
-            assert_eq!(r, -19943);
+            assert_eq!(r, -19505);
         } else {
             panic!("wrong buffer type");
         }