]>
Commit | Line | Data |
---|---|---|
c17769db KS |
1 | use nihav_core::codecs::*; |
2 | use nihav_core::io::bitreader::*; | |
3 | use nihav_codec_support::codecs::imaadpcm::*; | |
4 | use std::str::FromStr; | |
5 | ||
6 | const VIMA_STEPS: [&[i8]; 4] = [ | |
7 | /*&[ -1, 4, -1, 4 ], | |
8 | &[ -1, -1, 2, 6, -1, -1, 2, 6 ],*/ | |
9 | &[ -1, -1, -1, -1, 1, 2, 4, 6, -1, -1, -1, -1, 1, 2, 4, 6 ], | |
10 | &[ -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 2, 2, 4, 5, 6, | |
11 | -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 2, 2, 4, 5, 6 ], | |
12 | &[ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | |
13 | 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 5, 5, 6, 6, | |
14 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | |
15 | 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 5, 5, 6, 6 ], | |
16 | &[ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | |
17 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | |
18 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, | |
19 | 2, 2, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, | |
20 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | |
21 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | |
22 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, | |
23 | 2, 2, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6 ] | |
24 | ]; | |
25 | ||
26 | const STEP_TO_BITS: [u8; 89] = [ | |
27 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, | |
28 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, | |
29 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, | |
30 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, | |
31 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, | |
32 | 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 | |
33 | ]; | |
34 | ||
35 | struct VIMADecoder { | |
36 | ainfo: NAAudioInfo, | |
37 | chmap: NAChannelMap, | |
38 | } | |
39 | ||
40 | impl VIMADecoder { | |
41 | fn new() -> Self { | |
42 | Self { | |
43 | ainfo: NAAudioInfo::new(0, 1, SND_S16P_FORMAT, 0), | |
44 | chmap: NAChannelMap::new(), | |
45 | } | |
46 | } | |
47 | } | |
48 | ||
49 | impl NADecoder for VIMADecoder { | |
50 | fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> { | |
51 | if let NACodecTypeInfo::Audio(ainfo) = info.get_properties() { | |
52 | let channels = ainfo.get_channels(); | |
53 | validate!(channels == 1 || channels == 2); | |
54 | self.ainfo = NAAudioInfo::new(ainfo.get_sample_rate(), channels, SND_S16P_FORMAT, 1); | |
55 | self.chmap = NAChannelMap::from_str(if channels == 1 { "C" } else { "L,R" }).unwrap(); | |
56 | Ok(()) | |
57 | } else { | |
58 | Err(DecoderError::InvalidData) | |
59 | } | |
60 | } | |
61 | fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> { | |
62 | let info = pkt.get_stream().get_info(); | |
63 | if let NACodecTypeInfo::Audio(_) = info.get_properties() { | |
64 | let src = pkt.get_buffer(); | |
65 | validate!(src.len() > 4); | |
66 | ||
67 | let mut br = BitReader::new(&src, BitReaderMode::BE); | |
68 | let mut samples = br.read(32)? as usize; | |
69 | if samples == 0xFFFFFFFF { | |
70 | br.skip(32)?; | |
71 | samples = br.read(32)? as usize; | |
72 | } | |
73 | ||
74 | let mut steps = [0; 2]; | |
75 | steps[0] = br.read(8)? as usize; | |
76 | let stereo = (steps[0] & 0x80) != 0; | |
77 | validate!(!stereo || (self.chmap.num_channels() == 2)); | |
78 | if stereo { | |
79 | steps[0] ^= 0xFF; | |
80 | } | |
81 | validate!(steps[0] <= (IMA_MAX_STEP as usize)); | |
82 | ||
83 | let mut predictor = [0; 2]; | |
84 | predictor[0] = br.read_s(16)?; | |
85 | if stereo { | |
86 | steps[1] = br.read(8)? as usize; | |
87 | validate!(steps[1] <= (IMA_MAX_STEP as usize)); | |
88 | predictor[1] = br.read_s(16)?; | |
89 | } | |
90 | ||
91 | let abuf = alloc_audio_buffer(self.ainfo, samples, self.chmap.clone())?; | |
92 | let mut adata = abuf.get_abuf_i16().unwrap(); | |
93 | let offset = adata.get_offset(1); | |
94 | let adata = adata.get_data_mut().unwrap(); | |
95 | let (l, r) = adata.split_at_mut(offset); | |
96 | ||
97 | for (ch_no, ch) in [l, r].iter_mut().take(if stereo { 2 } else { 1 }).enumerate() { | |
98 | let mut step = steps[ch_no]; | |
99 | let mut sample = predictor[ch_no]; | |
100 | ||
101 | for dst in ch.iter_mut().take(samples) { | |
102 | let bits = STEP_TO_BITS[step]; | |
103 | let mask = 1 << (bits - 1); | |
104 | ||
105 | let idx = br.read(bits)? as u8; | |
106 | ||
107 | sample = if (idx & !mask) != (mask - 1) { | |
108 | let sign = (idx & mask) != 0; | |
109 | let aidx = idx & !mask; | |
110 | ||
111 | let mut diff = (i32::from(2 * aidx + 1) * IMA_STEP_TABLE[step]) >> (bits - 1); | |
112 | if sign { | |
113 | diff = -diff; | |
114 | } | |
115 | ||
116 | (sample + diff).max(-32768).min(32767) | |
117 | } else { | |
118 | br.read_s(16)? | |
119 | }; | |
120 | step = ((step as i8) + VIMA_STEPS[(bits - 4) as usize][idx as usize]).max(0).min(IMA_MAX_STEP as i8) as usize; | |
121 | *dst = sample as i16; | |
122 | } | |
123 | } | |
124 | if !stereo && self.chmap.num_channels() == 2 { | |
125 | let (l, r) = adata.split_at_mut(offset); | |
126 | r[..samples].copy_from_slice(l); | |
127 | } | |
128 | ||
129 | let mut frm = NAFrame::new_from_pkt(pkt, info, abuf); | |
130 | frm.set_duration(Some(samples as u64)); | |
131 | frm.set_keyframe(true); | |
132 | Ok(frm.into_ref()) | |
133 | } else { | |
134 | Err(DecoderError::InvalidData) | |
135 | } | |
136 | } | |
137 | fn flush(&mut self) { | |
138 | } | |
139 | } | |
140 | ||
141 | impl NAOptionHandler for VIMADecoder { | |
142 | fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] } | |
143 | fn set_options(&mut self, _options: &[NAOption]) { } | |
144 | fn query_option_value(&self, _name: &str) -> Option<NAValue> { None } | |
145 | } | |
146 | ||
147 | pub fn get_decoder_vima() -> Box<dyn NADecoder + Send> { | |
148 | Box::new(VIMADecoder::new()) | |
149 | } | |
150 | ||
151 | #[cfg(test)] | |
152 | mod test { | |
153 | use nihav_core::codecs::RegisteredDecoders; | |
154 | use nihav_core::demuxers::RegisteredDemuxers; | |
155 | use nihav_codec_support::test::dec_video::*; | |
156 | use crate::game_register_all_decoders; | |
157 | use crate::game_register_all_demuxers; | |
158 | #[test] | |
159 | fn test_smush_vima() { | |
160 | let mut dmx_reg = RegisteredDemuxers::new(); | |
161 | game_register_all_demuxers(&mut dmx_reg); | |
162 | let mut dec_reg = RegisteredDecoders::new(); | |
163 | game_register_all_decoders(&mut dec_reg); | |
164 | ||
165 | // samples from Grim Fandango | |
166 | test_decoding("smush", "smush-vima", "assets/Game/smush/lol.snm", Some(75000), &dmx_reg, &dec_reg, | |
167 | ExpectedTestResult::MD5([0xddd5dce1, 0xd5dc353c, 0xba176be8, 0x5afade63])); | |
168 | test_decoding("smush", "smush-vima", "assets/Game/smush/ac_bu.snm", None, &dmx_reg, &dec_reg, | |
169 | ExpectedTestResult::MD5([0x97a548e7, 0xb22d082b, 0x14c4110b, 0x9723891f])); | |
9e08bfdd KS |
170 | test_decoding("smush-mcmp", "smush-vima", "assets/Game/smush/1104 - Lupe.IMC", None, &dmx_reg, &dec_reg, |
171 | ExpectedTestResult::MD5([0x78389e65, 0xd99458a9, 0x6c62904e, 0xcaf732ba])); | |
c17769db KS |
172 | } |
173 | } | |
174 |