fix clippy warnings
[nihav.git] / nihav-ms / src / codecs / imaadpcmenc.rs
CommitLineData
c7cd83f3
KS
1use nihav_core::codecs::*;
2use nihav_core::io::byteio::*;
3use nihav_codec_support::codecs::imaadpcm::*;
4
5struct NibbleWriter<'a> {
6 bw: ByteWriter<'a>,
7 val: u8,
8 first: bool,
9}
10
11impl<'a> NibbleWriter<'a> {
12 fn new(bw: ByteWriter<'a>) -> Self {
13 Self { bw, val: 0, first: true }
14 }
15 fn write(&mut self, nib: u8) -> EncoderResult<()> {
16 if self.first {
17 self.val = nib;
18 self.first = false;
19 } else {
20 self.val |= nib << 4;
21 self.bw.write_byte(self.val)?;
22 self.first = true;
23 }
24 Ok(())
25 }
26}
27
28#[derive(Clone,Copy)]
29struct TrellisNode {
30 state: IMAState,
31 nib: u8,
32 error: i64,
33}
34
35impl Default for TrellisNode {
36 fn default() -> Self {
37 TrellisNode {
38 state: IMAState::new(),
39 nib: 0,
40 error: 0,
41 }
42 }
43}
44
45#[derive(Default)]
46struct IMAADPCMEncoder {
47 stream: Option<NAStreamRef>,
48 samples: Vec<i16>,
49 block_len: usize,
50 block_size: usize,
51 channels: usize,
52 flush: bool,
53 srate: u32,
54 trellis: bool,
55 nodes: Vec<[TrellisNode; 16]>,
56 first: Vec<IMAState>,
57 nibs: Vec<Vec<u8>>,
58}
59
60const DEFAULT_BLOCK_LEN: usize = 256;
61
62impl IMAADPCMEncoder {
63 fn new() -> Self { Self::default() }
64 fn encode_packet(&mut self) -> EncoderResult<NAPacket> {
65 if self.samples.is_empty() {
66 return Err(EncoderError::TryAgain);
67 }
68 let cur_len = (self.samples.len() / self.channels).min(self.block_len);
69 if cur_len < self.block_len && !self.flush {
70 return Err(EncoderError::TryAgain);
71 }
72 if cur_len < self.block_len {
73 self.samples.resize(self.block_len * self.channels, 0);
74 }
75
76 let mut dbuf = vec![0u8; self.block_size];
77 let mut mw = MemoryWriter::new_write(dbuf.as_mut_slice());
78 let mut bw = ByteWriter::new(&mut mw);
79
80 for ch in 0..self.channels {
81 self.first[ch].predictor = i32::from(self.samples[ch]);
82 self.first[ch].step = Self::calc_step(self.samples[ch], self.samples[ch + self.channels]) as usize;
83 }
84
85 if !self.trellis {
86 for ch in 0..self.channels {
87 bw.write_u16le(self.first[ch].predictor as u16)?;
88 bw.write_byte(self.first[ch].step as u8)?;
89 bw.write_byte(0)?;
90 }
91 let mut nw = NibbleWriter::new(bw);
92 for samples in self.samples.chunks(self.channels).take(self.block_len).skip(1) {
93 for (state, &samp) in self.first.iter_mut().zip(samples.iter()) {
94 let nib = state.compress_sample(samp);
95 state.expand_sample(nib);
96 nw.write(nib)?;
97 }
98 }
c7cd83f3
KS
99 } else {
100 self.nodes.reserve(self.block_len);
101 self.nibs.resize(self.channels, Vec::new());
102 for nibs in self.nibs.iter_mut() {
103 nibs.resize(self.block_len, 0);
104 }
105 let step = self.channels;
106 let mut state = [TrellisNode::default(); 16];
107 for ch in 0..self.channels {
108 self.nodes.clear();
109 for i in 0..16 {
110 let step = (((self.first[ch].step + i) as isize) - 8).max(0).min(IMA_MAX_STEP as isize) as usize;
111 state[i].state.predictor = self.first[ch].predictor;
112 state[i].state.step = step;
113 state[i].error = 0;
114 state[i].nib = step as u8;
115 }
116 self.nodes.push(state);
117 let mut sidx = ch + step;
118 for _i in 1..self.block_len {
119 let sample = self.samples[sidx];
120
121 for (nnib, cstate) in state.iter_mut().enumerate() {
122 for nib in 0..16 {
123 let pnode = &self.nodes[self.nodes.len() - 1][nib];
124 let mut ima = pnode.state;
125 let nsamp = ima.expand_sample(nnib as u8);
126 let diff = i64::from(i32::from(sample) - i32::from(nsamp));
127 let error = pnode.error + diff * diff;
128 if (nib == 0) || error < cstate.error {
129 cstate.state = ima;
130 cstate.nib = nib as u8;
131 cstate.error = error;
132 }
133 }
134 }
135 self.nodes.push(state);
136
137 sidx += step;
138 }
139
140 let mut idx = 0;
141 let mut best_err = self.nodes[self.nodes.len() - 1][0].error;
142 for (i, node) in self.nodes[self.nodes.len() - 1].iter().enumerate() {
143 if node.error < best_err {
144 best_err = node.error;
145 idx = i;
146 }
147 }
148 let mut dst = self.nibs[ch].iter_mut().rev();
149 while let Some(nodes) = self.nodes.pop() {
150 *dst.next().unwrap() = idx as u8;
151 idx = nodes[idx].nib as usize;
152 }
153 self.nibs[ch][0] = idx as u8;
154 }
155 for ch in 0..self.channels {
156 bw.write_u16le(self.first[ch].predictor as u16)?;
157 bw.write_byte(self.nibs[ch][0])?;
158 bw.write_byte(0)?;
159 }
160 let mut nw = NibbleWriter::new(bw);
161 for i in 1..self.block_len {
162 for ch in 0..self.channels {
163 nw.write(self.nibs[ch][i])?;
164 }
165 }
c7cd83f3
KS
166 }
167
168 self.samples.drain(..self.block_len * self.channels);
169 let ts = NATimeInfo::new(None, None, Some(1), 1, self.srate);
170 Ok(NAPacket::new(self.stream.clone().unwrap(), ts, true, dbuf))
171 }
172 fn calc_block_size(nsamps: usize, channels: usize) -> usize {
173 ((nsamps - 1) * channels + 1) / 2 + 4 * channels
174 }
175 fn calc_step(samp1: i16, samp2: i16) -> u8 {
176 let diff = (i32::from(samp1) - i32::from(samp2)).abs();
177 for (i, &step) in IMA_STEP_TABLE.iter().enumerate() {
178 if step >= diff {
179 return i as u8;
180 }
181 }
182 IMA_MAX_STEP
183 }
184}
185
186impl NAEncoder for IMAADPCMEncoder {
187 fn negotiate_format(&self, encinfo: &EncodeParameters) -> EncoderResult<EncodeParameters> {
188 match encinfo.format {
189 NACodecTypeInfo::None => {
6f263099
KS
190 Ok(EncodeParameters {
191 format: NACodecTypeInfo::Audio(NAAudioInfo::new(0, 1, SND_S16_FORMAT, DEFAULT_BLOCK_LEN)),
192 ..Default::default() })
c7cd83f3
KS
193 },
194 NACodecTypeInfo::Video(_) => Err(EncoderError::FormatError),
195 NACodecTypeInfo::Audio(ainfo) => {
196 let mut outinfo = ainfo;
197 outinfo.channels = outinfo.channels.max(1);
198 if outinfo.format != SND_S16P_FORMAT && outinfo.format != SND_S16_FORMAT {
199 outinfo.format = SND_S16_FORMAT;
200 }
201 if outinfo.block_len == 0 {
202 outinfo.block_len = DEFAULT_BLOCK_LEN;
203 }
204 if outinfo.block_len < 2 {
205 outinfo.block_len = 2;
206 }
207 if (outinfo.block_len & 7) != 0 {
208 outinfo.block_len = (outinfo.block_len & 7) + 7;
209 }
210 let mut ofmt = *encinfo;
211 ofmt.format = NACodecTypeInfo::Audio(outinfo);
212 Ok(ofmt)
213 }
214 }
215 }
2757a028 216 fn get_capabilities(&self) -> u64 { ENC_CAPS_CBR }
c7cd83f3
KS
217 fn init(&mut self, stream_id: u32, encinfo: EncodeParameters) -> EncoderResult<NAStreamRef> {
218 match encinfo.format {
219 NACodecTypeInfo::None => Err(EncoderError::FormatError),
220 NACodecTypeInfo::Video(_) => Err(EncoderError::FormatError),
221 NACodecTypeInfo::Audio(ainfo) => {
222 if ainfo.format != SND_S16P_FORMAT && ainfo.format != SND_S16_FORMAT {
223 return Err(EncoderError::FormatError);
224 }
225 if ainfo.block_len < 2 || ((ainfo.block_len * (ainfo.channels as usize)) & 7) != 0 {
226 return Err(EncoderError::FormatError);
227 }
228 self.channels = ainfo.channels as usize;
229 self.block_len = ainfo.block_len;
230 self.block_size = Self::calc_block_size(self.block_len, ainfo.channels as usize);
231 self.first = Vec::with_capacity(self.channels);
232 for _ in 0..self.channels {
233 self.first.push(IMAState::new());
234 }
235
236 let soniton = NASoniton::new(4, 0);
237 let out_ainfo = NAAudioInfo::new(ainfo.sample_rate, ainfo.channels, soniton, Self::calc_block_size(self.block_len, self.channels));
238 let info = NACodecInfo::new("ima-adpcm-ms", NACodecTypeInfo::Audio(out_ainfo), None);
239 let mut stream = NAStream::new(StreamType::Audio, stream_id, info, self.block_len as u32, ainfo.sample_rate, 0);
240 stream.set_num(stream_id as usize);
241 let stream = stream.into_ref();
242
243 self.stream = Some(stream.clone());
244 self.samples = Vec::with_capacity(self.block_len * self.channels);
245 self.srate = ainfo.sample_rate;
246 self.flush = false;
247
248 Ok(stream)
249 },
250 }
251 }
252 fn encode(&mut self, frm: &NAFrame) -> EncoderResult<()> {
253 let buf = frm.get_buffer();
254 if let Some(ref abuf) = buf.get_abuf_i16() {
255 let src = abuf.get_data();
256 let len = abuf.get_length();
257 let ch = abuf.get_chmap().num_channels();
258 if abuf.get_step() > 1 || ch == 1 {
259 self.samples.extend_from_slice(&src[..len * ch]);
260 } else {
261 let astride = abuf.get_stride();
262 self.samples.reserve(len * ch);
263 for i in 0..len {
264 for ch in 0..self.channels {
265 self.samples.push(src[ch * astride + i]);
266 }
267 }
268 }
269 Ok(())
270 } else {
271 Err(EncoderError::InvalidParameters)
272 }
273 }
274 fn get_packet(&mut self) -> EncoderResult<Option<NAPacket>> {
275 if let Ok(pkt) = self.encode_packet() {
276 Ok(Some(pkt))
277 } else {
278 Ok(None)
279 }
280 }
281 fn flush(&mut self) -> EncoderResult<()> {
282 self.flush = true;
283 Ok(())
284 }
285}
286
287const ENCODER_OPTS: &[NAOptionDefinition] = &[
288 NAOptionDefinition {
289 name: "trellis", description: "Use trellis search",
290 opt_type: NAOptionDefinitionType::Bool },
291];
292
293impl NAOptionHandler for IMAADPCMEncoder {
294 fn get_supported_options(&self) -> &[NAOptionDefinition] { ENCODER_OPTS }
295 fn set_options(&mut self, options: &[NAOption]) {
296 for option in options.iter() {
297 for opt_def in ENCODER_OPTS.iter() {
298 if opt_def.check(option).is_ok() {
299 match option.name {
300 "trellis" => {
301 if let NAValue::Bool(val) = option.value {
302 self.trellis = val;
303 }
304 },
305 _ => {},
306 };
307 }
308 }
309 }
310 }
311 fn query_option_value(&self, name: &str) -> Option<NAValue> {
312 match name {
313 "trellis" => Some(NAValue::Bool(self.trellis)),
314 _ => None,
315 }
316 }
317}
318
319pub fn get_encoder() -> Box<dyn NAEncoder + Send> {
320 Box::new(IMAADPCMEncoder::new())
321}
322
323#[cfg(test)]
324mod test {
325 use nihav_core::codecs::*;
326 use nihav_core::demuxers::*;
327 use nihav_core::muxers::*;
328 use nihav_codec_support::test::enc_video::*;
329 use crate::*;
330 use nihav_commonfmt::*;
331
332 fn test_ima_adpcm_ms_encoder(name: &'static str, trellis: bool, hash: &[u32; 4]) {
333 let mut dmx_reg = RegisteredDemuxers::new();
334 generic_register_all_demuxers(&mut dmx_reg);
335 let mut dec_reg = RegisteredDecoders::new();
336 generic_register_all_decoders(&mut dec_reg);
337 ms_register_all_decoders(&mut dec_reg);
338 let mut mux_reg = RegisteredMuxers::new();
339 generic_register_all_muxers(&mut mux_reg);
340 let mut enc_reg = RegisteredEncoders::new();
341 ms_register_all_encoders(&mut enc_reg);
342
886cde48 343 // sample: https://samples.mplayerhq.hu/V-codecs/VP4/ot171_vp40.avi
c7cd83f3
KS
344 let dec_config = DecoderTestParams {
345 demuxer: "avi",
346 in_name: "assets/Duck/ot171_vp40.avi",
347 stream_type: StreamType::Audio,
348 limit: None,
349 dmx_reg, dec_reg,
350 };
351 let enc_config = EncoderTestParams {
352 muxer: "wav",
353 enc_name: "ima-adpcm-ms",
354 out_name: name,
355 mux_reg, enc_reg,
356 };
357 let dst_ainfo = NAAudioInfo {
358 sample_rate: 0,
359 channels: 0,
360 format: SND_S16_FORMAT,
361 block_len: 128,
362 };
363 let enc_params = EncodeParameters {
364 format: NACodecTypeInfo::Audio(dst_ainfo),
365 quality: 0,
366 bitrate: 0,
367 tb_num: 0,
368 tb_den: 0,
369 flags: 0,
370 };
371 let enc_options = &[
372 NAOption{name: "trellis", value: NAValue::Bool(trellis)},
373 ];
85d4be52 374// test_encoding_to_file(&dec_config, &enc_config, enc_params, enc_options);
c7cd83f3
KS
375
376 test_encoding_md5(&dec_config, &enc_config, enc_params, enc_options,
377 hash);
378 }
379 #[test]
380 fn test_ima_adpcm_ms_encoder_notrellis() {
381 test_ima_adpcm_ms_encoder("msimaadpcm-notr.wav", false,
382 &[0x59909f10, 0xf0420dd2, 0xcfee7ef5, 0x7623caa3]);
383 }
384 #[test]
385 fn test_ima_adpcm_ms_encoder_trellis() {
386 test_ima_adpcm_ms_encoder("msimaadpcm-tr.wav", true,
387 &[0x99e373e2, 0x80439b4b, 0xcb4f0b78, 0xeb1e9a51]);
388 }
389}