replace vec.truncate(0) with vec.clear()
[nihav.git] / nihav-ms / src / codecs / msadpcm.rs
index 687e0bdb21e95eac4d382c878b32f6f36d7f8dbc..a63f4e5a4b9d5661885564d25e5d9ba3199e6f8c 100644 (file)
@@ -3,7 +3,7 @@ use nihav_core::io::byteio::*;
 use std::str::FromStr;
 
 const ADAPT_TABLE: [i32; 16] = [
-    230, 230, 230, 230, 307, 409, 512, 614, 
+    230, 230, 230, 230, 307, 409, 512, 614,
     768, 614, 512, 409, 307, 230, 230, 230
 ];
 const ADAPT_COEFFS: [[i32; 2]; 7] = [
@@ -23,12 +23,18 @@ struct Predictor {
 impl Predictor {
     fn expand_nibble(&mut self, nibble: u8) -> i16 {
         let mul = if (nibble & 8) == 0 { i32::from(nibble) } else { i32::from(nibble) - 16 };
-        let pred = ((self.sample1.wrapping_mul(self.coef1) + self.sample2.wrapping_mul(self.coef2)) >> 8) + self.delta.wrapping_mul(mul);
-        self.sample2 = self.sample1;
-        self.sample1 = pred.max(-0x8000).min(0x7FFF);
+        let pred = self.calc_pred() + self.delta.wrapping_mul(mul);
+        self.update(pred.max(-0x8000).min(0x7FFF));
         self.delta = (ADAPT_TABLE[nibble as usize].wrapping_mul(self.delta) >> 8).max(16);
         self.sample1 as i16
     }
+    fn calc_pred(&self) -> i32 {
+        self.sample1.wrapping_mul(self.coef1).wrapping_add(self.sample2.wrapping_mul(self.coef2)) >> 8
+    }
+    fn update(&mut self, new_samp: i32) {
+        self.sample2 = self.sample1;
+        self.sample1 = new_samp;
+    }
 }
 
 struct MSADPCMDecoder {
@@ -52,6 +58,7 @@ impl MSADPCMDecoder {
 }
 
 impl NADecoder for MSADPCMDecoder {
+    #[allow(clippy::int_plus_one)]
     fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> {
         if let NACodecTypeInfo::Audio(ainfo) = info.get_properties() {
             self.block_len = ainfo.get_block_len();
@@ -61,7 +68,7 @@ impl NADecoder for MSADPCMDecoder {
             self.block_samps = (self.block_len / channels - 7) * 2 + 2;
             self.ainfo = NAAudioInfo::new(ainfo.get_sample_rate(), channels as u8, SND_S16P_FORMAT, self.block_samps);
             self.chmap = NAChannelMap::from_str(if channels == 1 { "C" } else { "L,R" }).unwrap();
-            self.adapt_coeffs.truncate(0);
+            self.adapt_coeffs.clear();
             if let Some(ref buf) = info.get_extradata() {
                 validate!(buf.len() >= 6);
                 validate!((buf.len() & 3) == 0);
@@ -90,7 +97,7 @@ impl NADecoder for MSADPCMDecoder {
         if let NACodecTypeInfo::Audio(_) = info.get_properties() {
             let pktbuf = pkt.get_buffer();
             let channels = self.chmap.num_channels();
-            validate!(pktbuf.len() > 0 && (pktbuf.len() % self.block_len) == 0);
+            validate!(!pktbuf.is_empty() && (pktbuf.len() % self.block_len) == 0);
             let nblocks = pktbuf.len() / self.block_len;
             let nsamples = nblocks * self.block_samps;
             let abuf = alloc_audio_buffer(self.ainfo, nsamples, self.chmap.clone())?;
@@ -114,15 +121,16 @@ impl NADecoder for MSADPCMDecoder {
                 }
                 for ch in 0..channels {
                     let samp                    = br.read_u16le()? as i16;
-                    pred[ch].sample1            = i32::from(samp);
-                    dst[off[ch]] = samp;
-                    off[ch] += 1;
+                    pred[ch].sample2            = i32::from(samp);
                 }
                 for ch in 0..channels {
                     let samp                    = br.read_u16le()? as i16;
-                    pred[ch].sample2            = i32::from(samp);
-                    dst[off[ch]] = samp;
-                    off[ch] += 1;
+                    pred[ch].sample1            = i32::from(samp);
+                }
+                for ch in 0..channels {
+                    dst[off[ch]]     = pred[ch].sample2 as i16;
+                    dst[off[ch] + 1] = pred[ch].sample1 as i16;
+                    off[ch] += 2;
                 }
                 if channels == 1 {
                     while br.left() > 0 {
@@ -154,25 +162,309 @@ impl NADecoder for MSADPCMDecoder {
     }
 }
 
+impl NAOptionHandler for MSADPCMDecoder {
+    fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] }
+    fn set_options(&mut self, _options: &[NAOption]) { }
+    fn query_option_value(&self, _name: &str) -> Option<NAValue> { None }
+}
+
 pub fn get_decoder() -> Box<dyn NADecoder + Send> {
     Box::new(MSADPCMDecoder::new())
 }
 
+#[derive(Default)]
+struct MSADPCMEncoder {
+    stream:     Option<NAStreamRef>,
+    samples:    Vec<i16>,
+    block_len:  usize,
+    channels:   usize,
+    flush:      bool,
+    srate:      u32,
+}
+
+const DEFAULT_BLOCK_LEN: usize = 256;
+
+impl MSADPCMEncoder {
+    fn new() -> Self { Self::default() }
+    fn encode_packet(&mut self) -> EncoderResult<NAPacket> {
+        if self.samples.is_empty() {
+            return Err(EncoderError::TryAgain);
+        }
+        let len = (self.samples.len() / self.channels).min(self.block_len);
+        if len < self.block_len && !self.flush {
+            return Err(EncoderError::TryAgain);
+        }
+        if len < 2 {
+            self.flush = false;
+            return Err(EncoderError::TryAgain);
+        }
+
+        let mut dbuf = vec![0u8; Self::calc_block_size(len, self.channels)];
+        let mut mw = MemoryWriter::new_write(dbuf.as_mut_slice());
+        let mut bw = ByteWriter::new(&mut mw);
+
+        let mut best_idx = [0usize; 2];
+        for ch in 0..self.channels {
+            let mut best_dist = std::i64::MAX;
+            for i in 0..ADAPT_COEFFS.len() {
+                let dist = self.calc_dist(ch, i, len);
+                if dist < best_dist {
+                    best_dist = dist;
+                    best_idx[ch] = i;
+                }
+            }
+            bw.write_byte(best_idx[ch] as u8)?;
+        }
+        let mut dec = [Predictor::default(), Predictor::default()];
+        for ch in 0..self.channels {
+            dec[ch].sample1 = i32::from(self.samples[ch + self.channels]);
+            dec[ch].sample2 = i32::from(self.samples[ch]);
+            dec[ch].coef1   = ADAPT_COEFFS[best_idx[ch]][0];
+            dec[ch].coef2   = ADAPT_COEFFS[best_idx[ch]][1];
+            if len > 2 {
+                let pred = dec[ch].calc_pred();
+                dec[ch].delta = ((i32::from(self.samples[ch + self.channels * 2]) - pred).abs() / 4).max(16);
+            } else {
+                dec[ch].delta = 16;
+            }
+        }
+        for ch in 0..self.channels {
+            bw.write_u16le(dec[ch].delta as u16)?;
+        }
+        for ch in 0..self.channels {
+            bw.write_u16le(dec[ch].sample1 as u16)?;
+        }
+        for ch in 0..self.channels {
+            bw.write_u16le(dec[ch].sample2 as u16)?;
+        }
+        if self.channels == 1 {
+            for samps in self.samples.chunks(2).skip(1).take(len/2 - 1) {
+                let diff = i32::from(samps[0]) - dec[0].calc_pred();
+                let nib0 = Self::calculate_mul(dec[0].delta, diff);
+                dec[0].expand_nibble(nib0);
+                let diff = i32::from(samps[1]) - dec[0].calc_pred();
+                let nib1 = Self::calculate_mul(dec[0].delta, diff);
+                dec[0].expand_nibble(nib1);
+                bw.write_byte(nib0 * 16 + nib1)?;
+            }
+        } else {
+            for samps in self.samples.chunks(2).skip(2).take(len - 2) {
+                let diff = i32::from(samps[0]) - dec[0].calc_pred();
+                let nib0 = Self::calculate_mul(dec[0].delta, diff);
+                dec[0].expand_nibble(nib0);
+                let diff = i32::from(samps[1]) - dec[1].calc_pred();
+                let nib1 = Self::calculate_mul(dec[1].delta, diff);
+                dec[1].expand_nibble(nib1);
+                bw.write_byte(nib0 * 16 + nib1)?;
+            }
+        }
+        self.samples.drain(..len * self.channels);
+        drop(bw);
+        let ts = NATimeInfo::new(None, None, Some(1), 1, self.srate);
+        Ok(NAPacket::new(self.stream.clone().unwrap(), ts, true, dbuf))
+    }
+    fn calc_dist(&self, ch: usize, idx: usize, len: usize) -> i64 {
+        let mut dist = 0;
+        let mut dec = Predictor {
+                sample2: i32::from(self.samples[ch]),
+                sample1: i32::from(self.samples[ch + self.channels]),
+                coef1:   ADAPT_COEFFS[idx][0],
+                coef2:   ADAPT_COEFFS[idx][1],
+                delta:   16,
+            };
+        if self.channels == 1 {
+            for samp in self.samples.iter().skip(2).take(len - 2) {
+                let pred = dec.calc_pred();
+                dec.update(pred);
+                let diff = i64::from(*samp) - i64::from(pred);
+                dist += diff * diff;
+            }
+        } else {
+            for samp in self.samples.chunks(2).skip(2).take(len - 2) {
+                let pred = dec.calc_pred();
+                dec.update(pred);
+                let diff = i64::from(samp[ch]) - i64::from(pred);
+                dist += diff * diff;
+            }
+        }
+        dist
+    }
+    fn calculate_mul(delta: i32, diff: i32) -> u8 {
+        ((diff / delta).max(-8).min(7) & 0xF) as u8
+    }
+    fn calc_block_size(nsamps: usize, channels: usize) -> usize {
+        (nsamps - 2) * channels / 2 + 7 * channels
+    }
+}
+
+impl NAEncoder for MSADPCMEncoder {
+    fn negotiate_format(&self, encinfo: &EncodeParameters) -> EncoderResult<EncodeParameters> {
+        match encinfo.format {
+            NACodecTypeInfo::None => {
+                let mut ofmt = EncodeParameters::default();
+                ofmt.format = NACodecTypeInfo::Audio(NAAudioInfo::new(0, 1, SND_S16_FORMAT, DEFAULT_BLOCK_LEN));
+                Ok(ofmt)
+            },
+            NACodecTypeInfo::Video(_) => Err(EncoderError::FormatError),
+            NACodecTypeInfo::Audio(ainfo) => {
+                let mut outinfo = ainfo;
+                outinfo.channels = outinfo.channels.min(2);
+                if outinfo.format != SND_S16P_FORMAT && outinfo.format != SND_S16_FORMAT {
+                    outinfo.format = SND_S16_FORMAT;
+                }
+                if outinfo.block_len == 0 {
+                    outinfo.block_len = DEFAULT_BLOCK_LEN;
+                }
+                if outinfo.block_len < 2 {
+                    outinfo.block_len = 2;
+                }
+                if (outinfo.channels == 1) && ((outinfo.block_len & 1) == 1) {
+                    outinfo.block_len += 1;
+                }
+                let mut ofmt = *encinfo;
+                ofmt.format = NACodecTypeInfo::Audio(outinfo);
+                Ok(ofmt)
+            }
+        }
+    }
+    fn init(&mut self, stream_id: u32, encinfo: EncodeParameters) -> EncoderResult<NAStreamRef> {
+        match encinfo.format {
+            NACodecTypeInfo::None => Err(EncoderError::FormatError),
+            NACodecTypeInfo::Video(_) => Err(EncoderError::FormatError),
+            NACodecTypeInfo::Audio(ainfo) => {
+                if ainfo.format != SND_S16P_FORMAT && ainfo.format != SND_S16_FORMAT {
+                    return Err(EncoderError::FormatError);
+                }
+                if ainfo.channels != 1 && ainfo.channels != 2 {
+                    return Err(EncoderError::FormatError);
+                }
+                if ainfo.block_len < 2 || ((ainfo.block_len * (ainfo.channels as usize)) & 1) != 0 {
+                    return Err(EncoderError::FormatError);
+                }
+                self.channels = ainfo.channels as usize;
+                self.block_len = ainfo.block_len;
+
+                let soniton = NASoniton::new(4, 0);
+                let out_ainfo = NAAudioInfo::new(ainfo.sample_rate, ainfo.channels, soniton, Self::calc_block_size(self.block_len, self.channels));
+                let info = NACodecInfo::new("ms-adpcm", NACodecTypeInfo::Audio(out_ainfo), None);
+                let mut stream = NAStream::new(StreamType::Audio, stream_id, info, self.block_len as u32, ainfo.sample_rate, 0);
+                stream.set_num(stream_id as usize);
+                let stream = stream.into_ref();
+
+                self.stream = Some(stream.clone());
+                self.samples = Vec::with_capacity(self.block_len * self.channels);
+                self.srate = ainfo.sample_rate;
+                self.flush = false;
+
+                Ok(stream)
+            },
+        }
+    }
+    fn encode(&mut self, frm: &NAFrame) -> EncoderResult<()> {
+        let buf = frm.get_buffer();
+        if let Some(ref abuf) = buf.get_abuf_i16() {
+            let src = abuf.get_data();
+            let len = abuf.get_length();
+            let ch  = abuf.get_chmap().num_channels();
+            if abuf.get_step() > 1 || ch == 1 {
+                self.samples.extend(src.iter().take(len * ch));
+            } else {
+                let (src0, src1) = src.split_at(abuf.get_stride());
+                self.samples.reserve(len * 2);
+                for (s0, s1) in src0.iter().take(len).zip(src1.iter()) {
+                    self.samples.push(*s0);
+                    self.samples.push(*s1);
+                }
+            }
+            Ok(())
+        } else {
+            Err(EncoderError::InvalidParameters)
+        }
+    }
+    fn get_packet(&mut self) -> EncoderResult<Option<NAPacket>> {
+        if let Ok(pkt) = self.encode_packet() {
+            Ok(Some(pkt))
+        } else {
+            Ok(None)
+        }
+    }
+    fn flush(&mut self) -> EncoderResult<()> {
+        self.flush = true;
+        Ok(())
+    }
+}
+
+impl NAOptionHandler for MSADPCMEncoder {
+    fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] }
+    fn set_options(&mut self, _options: &[NAOption]) { }
+    fn query_option_value(&self, _name: &str) -> Option<NAValue> { None }
+}
+
+pub fn get_encoder() -> Box<dyn NAEncoder + Send> {
+    Box::new(MSADPCMEncoder::new())
+}
+
 #[cfg(test)]
 mod test {
-    use nihav_core::codecs::RegisteredDecoders;
-    use nihav_core::demuxers::RegisteredDemuxers;
+    use nihav_core::codecs::*;
+    use nihav_core::demuxers::*;
+    use nihav_core::muxers::*;
     use nihav_codec_support::test::dec_video::*;
-    use crate::ms_register_all_codecs;
-    use nihav_commonfmt::generic_register_all_demuxers;
+    use nihav_codec_support::test::enc_video::*;
+    use crate::*;
+    use nihav_commonfmt::*;
+    #[cfg(feature="decoder_ms_adpcm")]
     #[test]
-    fn test_ms_adpcm() {
+    fn test_ms_adpcm_decoder() {
         let mut dmx_reg = RegisteredDemuxers::new();
         generic_register_all_demuxers(&mut dmx_reg);
         let mut dec_reg = RegisteredDecoders::new();
-        ms_register_all_codecs(&mut dec_reg);
+        ms_register_all_decoders(&mut dec_reg);
 
         test_decoding("avi", "ms-adpcm", "assets/MS/dance.avi", None, &dmx_reg, &dec_reg,
-                      ExpectedTestResult::MD5([0x9d6619e1, 0x60d83560, 0xfe5c1fb7, 0xad5d130d]));
+                      ExpectedTestResult::MD5([0xf5e3fc84, 0xbcabc11c, 0x33c6874e, 0xe05ecd14]));
+    }
+    #[cfg(feature="encoder_ms_adpcm")]
+    #[test]
+    fn test_ms_adpcm_encoder() {
+        let mut dmx_reg = RegisteredDemuxers::new();
+        generic_register_all_demuxers(&mut dmx_reg);
+        let mut dec_reg = RegisteredDecoders::new();
+        generic_register_all_decoders(&mut dec_reg);
+        ms_register_all_decoders(&mut dec_reg);
+        let mut mux_reg = RegisteredMuxers::new();
+        generic_register_all_muxers(&mut mux_reg);
+        let mut enc_reg = RegisteredEncoders::new();
+        ms_register_all_encoders(&mut enc_reg);
+
+        let dec_config = DecoderTestParams {
+                demuxer:        "avi",
+                in_name:        "assets/Indeo/laser05.avi",
+                stream_type:    StreamType::Audio,
+                limit:          None,
+                dmx_reg, dec_reg,
+            };
+        let enc_config = EncoderTestParams {
+                muxer:          "wav",
+                enc_name:       "ms-adpcm",
+                out_name:       "msadpcm.wav",
+                mux_reg, enc_reg,
+            };
+        let dst_ainfo = NAAudioInfo {
+                sample_rate:    0,
+                channels:       0,
+                format:         SND_S16_FORMAT,
+                block_len:      128,
+            };
+        let enc_params = EncodeParameters {
+                format:  NACodecTypeInfo::Audio(dst_ainfo),
+                quality: 0,
+                bitrate: 0,
+                tb_num:  0,
+                tb_den:  0,
+                flags:   0,
+            };
+        test_encoding_md5(&dec_config, &enc_config, enc_params, &[],
+                          &[0x82259f45, 0xba7b984a, 0xc03c94e5, 0x00b4312b]);
     }
 }