Commit | Line | Data |
---|---|---|
4abcd842 KS |
1 | use nihav_core::codecs::*; |
2 | use nihav_core::io::byteio::*; | |
3 | use std::str::FromStr; | |
4 | ||
5 | const ADAPT_TABLE: [i32; 16] = [ | |
6 | 230, 230, 230, 230, 307, 409, 512, 614, | |
7 | 768, 614, 512, 409, 307, 230, 230, 230 | |
8 | ]; | |
9 | const ADAPT_COEFFS: [[i32; 2]; 7] = [ | |
10 | [ 256, 0 ], [ 512, -256 ], [ 0, 0 ], [ 192, 64 ], | |
11 | [ 240, 0 ], [ 460, -208 ], [ 392, -232 ] | |
12 | ]; | |
13 | ||
14 | #[derive(Default)] | |
15 | struct Predictor { | |
16 | sample1: i32, | |
17 | sample2: i32, | |
18 | delta: i32, | |
19 | coef1: i32, | |
20 | coef2: i32, | |
21 | } | |
22 | ||
23 | impl Predictor { | |
24 | fn expand_nibble(&mut self, nibble: u8) -> i16 { | |
25 | let mul = if (nibble & 8) == 0 { i32::from(nibble) } else { i32::from(nibble) - 16 }; | |
26 | let pred = ((self.sample1.wrapping_mul(self.coef1) + self.sample2.wrapping_mul(self.coef2)) >> 8) + self.delta.wrapping_mul(mul); | |
27 | self.sample2 = self.sample1; | |
28 | self.sample1 = pred.max(-0x8000).min(0x7FFF); | |
29 | self.delta = (ADAPT_TABLE[nibble as usize].wrapping_mul(self.delta) >> 8).max(16); | |
30 | self.sample1 as i16 | |
31 | } | |
32 | } | |
33 | ||
34 | struct MSADPCMDecoder { | |
35 | ainfo: NAAudioInfo, | |
36 | chmap: NAChannelMap, | |
37 | adapt_coeffs: Vec<[i32; 2]>, | |
38 | block_len: usize, | |
39 | block_samps: usize, | |
40 | } | |
41 | ||
42 | impl MSADPCMDecoder { | |
43 | fn new() -> Self { | |
44 | Self { | |
45 | ainfo: NAAudioInfo::new(0, 1, SND_S16P_FORMAT, 0), | |
46 | chmap: NAChannelMap::new(), | |
47 | adapt_coeffs: Vec::with_capacity(7), | |
48 | block_len: 0, | |
49 | block_samps: 0, | |
50 | } | |
51 | } | |
52 | } | |
53 | ||
54 | impl NADecoder for MSADPCMDecoder { | |
55 | fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> { | |
56 | if let NACodecTypeInfo::Audio(ainfo) = info.get_properties() { | |
57 | self.block_len = ainfo.get_block_len(); | |
58 | let channels = ainfo.get_channels() as usize; | |
59 | validate!(channels == 2 || channels == 1); | |
60 | validate!(self.block_len >= 7 * channels + 1); | |
61 | self.block_samps = (self.block_len / channels - 7) * 2 + 2; | |
62 | self.ainfo = NAAudioInfo::new(ainfo.get_sample_rate(), channels as u8, SND_S16P_FORMAT, self.block_samps); | |
63 | self.chmap = NAChannelMap::from_str(if channels == 1 { "C" } else { "L,R" }).unwrap(); | |
64 | self.adapt_coeffs.truncate(0); | |
65 | if let Some(ref buf) = info.get_extradata() { | |
66 | validate!(buf.len() >= 6); | |
67 | validate!((buf.len() & 3) == 0); | |
68 | let mut mr = MemoryReader::new_read(buf.as_slice()); | |
69 | let mut br = ByteReader::new(&mut mr); | |
70 | let _smth = br.read_u16le()?; | |
71 | let ncoeffs = br.read_u16le()? as usize; | |
72 | validate!(buf.len() == ncoeffs * 4 + 4); | |
73 | ||
74 | for _ in 0..ncoeffs { | |
75 | let pair = [ | |
76 | i32::from(br.read_u16le()? as i16), | |
77 | i32::from(br.read_u16le()? as i16)]; | |
78 | self.adapt_coeffs.push(pair); | |
79 | } | |
80 | } else { | |
81 | self.adapt_coeffs.extend_from_slice(&ADAPT_COEFFS); | |
82 | } | |
83 | Ok(()) | |
84 | } else { | |
85 | Err(DecoderError::InvalidData) | |
86 | } | |
87 | } | |
88 | fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> { | |
89 | let info = pkt.get_stream().get_info(); | |
90 | if let NACodecTypeInfo::Audio(_) = info.get_properties() { | |
91 | let pktbuf = pkt.get_buffer(); | |
92 | let channels = self.chmap.num_channels(); | |
93 | validate!(pktbuf.len() > 0 && (pktbuf.len() % self.block_len) == 0); | |
94 | let nblocks = pktbuf.len() / self.block_len; | |
95 | let nsamples = nblocks * self.block_samps; | |
96 | let abuf = alloc_audio_buffer(self.ainfo, nsamples, self.chmap.clone())?; | |
97 | let mut adata = abuf.get_abuf_i16().unwrap(); | |
98 | let mut off = [adata.get_offset(0), adata.get_offset(1)]; | |
99 | let dst = adata.get_data_mut().unwrap(); | |
100 | ||
101 | let mut pred = [Predictor::default(), Predictor::default()]; | |
102 | ||
103 | for blk in pktbuf.chunks(self.block_len) { | |
104 | let mut mr = MemoryReader::new_read(blk); | |
105 | let mut br = ByteReader::new(&mut mr); | |
106 | for ch in 0..channels { | |
107 | let coef_idx = br.read_byte()? as usize; | |
108 | validate!(coef_idx < self.adapt_coeffs.len()); | |
109 | pred[ch].coef1 = self.adapt_coeffs[coef_idx][0]; | |
110 | pred[ch].coef2 = self.adapt_coeffs[coef_idx][1]; | |
111 | } | |
112 | for ch in 0..channels { | |
113 | pred[ch].delta = i32::from(br.read_u16le()?); | |
114 | } | |
115 | for ch in 0..channels { | |
116 | let samp = br.read_u16le()? as i16; | |
117 | pred[ch].sample1 = i32::from(samp); | |
118 | dst[off[ch]] = samp; | |
119 | off[ch] += 1; | |
120 | } | |
121 | for ch in 0..channels { | |
122 | let samp = br.read_u16le()? as i16; | |
123 | pred[ch].sample2 = i32::from(samp); | |
124 | dst[off[ch]] = samp; | |
125 | off[ch] += 1; | |
126 | } | |
127 | if channels == 1 { | |
128 | while br.left() > 0 { | |
129 | let idx = br.read_byte()?; | |
130 | dst[off[0]] = pred[0].expand_nibble(idx >> 4); | |
131 | off[0] += 1; | |
132 | dst[off[0]] = pred[0].expand_nibble(idx & 0xF); | |
133 | off[0] += 1; | |
134 | } | |
135 | } else { | |
136 | while br.left() > 0 { | |
137 | let idx = br.read_byte()?; | |
138 | dst[off[0]] = pred[0].expand_nibble(idx >> 4); | |
139 | off[0] += 1; | |
140 | dst[off[1]] = pred[1].expand_nibble(idx & 0xF); | |
141 | off[1] += 1; | |
142 | } | |
143 | } | |
144 | } | |
145 | let mut frm = NAFrame::new_from_pkt(pkt, info.replace_info(NACodecTypeInfo::Audio(self.ainfo)), abuf); | |
146 | frm.set_duration(Some(nsamples as u64)); | |
147 | frm.set_keyframe(false); | |
148 | Ok(frm.into_ref()) | |
149 | } else { | |
150 | Err(DecoderError::InvalidData) | |
151 | } | |
152 | } | |
153 | fn flush(&mut self) { | |
154 | } | |
155 | } | |
156 | ||
157 | pub fn get_decoder() -> Box<dyn NADecoder + Send> { | |
158 | Box::new(MSADPCMDecoder::new()) | |
159 | } | |
160 | ||
161 | #[cfg(test)] | |
162 | mod test { | |
163 | use nihav_core::codecs::RegisteredDecoders; | |
164 | use nihav_core::demuxers::RegisteredDemuxers; | |
165 | use nihav_codec_support::test::dec_video::*; | |
166 | use crate::ms_register_all_codecs; | |
167 | use nihav_commonfmt::generic_register_all_demuxers; | |
168 | #[test] | |
169 | fn test_ms_adpcm() { | |
170 | let mut dmx_reg = RegisteredDemuxers::new(); | |
171 | generic_register_all_demuxers(&mut dmx_reg); | |
172 | let mut dec_reg = RegisteredDecoders::new(); | |
173 | ms_register_all_codecs(&mut dec_reg); | |
174 | ||
175 | test_decoding("avi", "ms-adpcm", "assets/MS/dance.avi", None, &dmx_reg, &dec_reg, | |
176 | ExpectedTestResult::MD5([0x9d6619e1, 0x60d83560, 0xfe5c1fb7, 0xad5d130d])); | |
177 | } | |
178 | } |