]> git.nihav.org Git - nihav.git/blob - nihav-llaudio/src/demuxers/wavpack.rs
switch some TableCodebookDescReader use cases to closures
[nihav.git] / nihav-llaudio / src / demuxers / wavpack.rs
1 use nihav_core::frame::*;
2 use nihav_core::demuxers::*;
3
4 const SAMPLE_RATES: [u32; 15] = [
5 6000, 8000, 9600, 11025, 12000, 16000, 22050, 24000,
6 32000, 44100, 48000, 64000, 88200, 96000, 192000
7 ];
8 const WV_FLAG_MONO: u32 = 1 << 2;
9 //const WV_FLAG_HYBRID: u32 = 1 << 3;
10 //const WV_FLAG_JSTEREO: u32 = 1 << 4;
11 //const WV_FLAG_CH_DECORR: u32 = 1 << 5;
12 //const WV_FLAG_HYB_NOISE_SHAPING: u32 = 1 << 6;
13 const WV_FLAG_FLOATS: u32 = 1 << 7;
14 //const WV_FLAG_EXT_INTEGERS: u32 = 1 << 8;
15 //const WV_FLAG_HYB_BITRATE: u32 = 1 << 9;
16 //const WV_FLAG_HYB_BALANCED_NOISE: u32 = 1 << 10;
17 const WV_FLAG_START_BLOCK: u32 = 1 << 11;
18 const WV_FLAG_END_BLOCK: u32 = 1 << 12;
19 //const WV_FLAG_HAS_CRC: u32 = 1 << 28;
20 const WV_FLAG_FALSE_STEREO: u32 = 1 << 30;
21 //const WV_FLAG_DSD_AUDIO: u32 = 1 << 31;
22
23 const WV_STREAM_FLAGS: u32 = 0x8000008B;
24
25 #[derive(Clone,Copy,Default)]
26 struct WVHeader {
27 size: usize,
28 ver: u16,
29 tot_samples: u64,
30 block_index: u64,
31 block_samples: u32,
32 flags: u32,
33 crc: u32,
34 }
35
36 const WV_HEADER_SIZE: usize = 32;
37
38 impl WVHeader {
39 #[allow(clippy::field_reassign_with_default)]
40 fn parse(src: &[u8]) -> DemuxerResult<Self> {
41 let mut mr = MemoryReader::new_read(src);
42 let mut br = ByteReader::new(&mut mr);
43 let tag = br.read_tag()?;
44 validate!(&tag == b"wvpk");
45 let mut hdr = Self::default();
46 hdr.size = br.read_u32le()? as usize;
47 validate!(hdr.size >= 24);
48 hdr.ver = br.read_u16le()?;
49 validate!(hdr.ver >= 0x402 || hdr.ver <= 0x410);
50 let top_idx = br.read_byte()?;
51 let top_samps = br.read_byte()?;
52 hdr.tot_samples = u64::from(br.read_u32le()?) | (u64::from(top_samps) << 32);
53 hdr.block_index = u64::from(br.read_u32le()?) | (u64::from(top_idx) << 32);
54 hdr.block_samples = br.read_u32le()?;
55 hdr.flags = br.read_u32le()?;
56 hdr.crc = br.read_u32le()?;
57 Ok(hdr)
58 }
59 fn stream_eq(&self, rval: &Self) -> bool {
60 self.ver == rval.ver &&
61 (self.flags & WV_STREAM_FLAGS) == (rval.flags & WV_STREAM_FLAGS)
62 }
63 fn block_eq(&self, rval: &Self) -> bool {
64 self.stream_eq(rval) && self.tot_samples == rval.tot_samples &&
65 self.block_index == rval.block_index &&
66 self.block_samples == rval.block_samples
67 }
68 fn is_start_block(&self) -> bool {
69 (self.flags & WV_FLAG_START_BLOCK) != 0
70 }
71 fn is_end_block(&self) -> bool {
72 (self.flags & WV_FLAG_END_BLOCK) != 0
73 }
74 fn get_num_channels(&self) -> u8 {
75 if (self.flags & WV_FLAG_MONO) != 0 && (self.flags & WV_FLAG_FALSE_STEREO) == 0 { 1 } else { 2 }
76 }
77 fn get_sample_rate(&self) -> u32 {
78 let idx = ((self.flags >> 23) & 0xF) as usize;
79 if idx != 15 {
80 SAMPLE_RATES[idx]
81 } else {
82 0
83 }
84 }
85 fn get_size(&self) -> usize {
86 self.size - (WV_HEADER_SIZE - 8)
87 }
88 }
89
90 #[derive(Clone,Copy,Default)]
91 struct FrameSeekInfo {
92 off: u64,
93 samplepos: u64,
94 }
95
96 struct WavPackDemuxer<'a> {
97 src: &'a mut ByteReader<'a>,
98 samplepos: u64,
99 nsamples: u64,
100 first_blocks: Option<(WVHeader, Vec<u8>)>,
101 srate: u32,
102 known_frames: Vec<FrameSeekInfo>,
103 }
104
105 impl<'a> WavPackDemuxer<'a> {
106 fn new(io: &'a mut ByteReader<'a>) -> Self {
107 Self {
108 src: io,
109 samplepos: 0,
110 nsamples: 0,
111 first_blocks: None,
112 srate: 0,
113 known_frames: Vec::new(),
114 }
115 }
116 fn read_blocks(&mut self) -> DemuxerResult<(WVHeader, Vec<u8>)> {
117 let mut hdrbuf = [0u8; WV_HEADER_SIZE];
118 let mut buf: Vec<u8> = Vec::new();
119 let mut first = true;
120 let mut refhdr = WVHeader::default();
121 loop {
122 self.src.read_buf(&mut hdrbuf)?;
123 let hdr = WVHeader::parse(&hdrbuf)?;
124 if first {
125 validate!(hdr.is_start_block());
126 refhdr = hdr;
127 first = false;
128 } else {
129 validate!(refhdr.block_eq(&hdr));
130 }
131 buf.extend_from_slice(&hdrbuf);
132 let pos = buf.len();
133 buf.resize(pos + hdr.get_size(), 0);
134 self.src.read_buf(&mut buf[pos..])?;
135
136 if hdr.is_end_block() {
137 break;
138 }
139 }
140 Ok((refhdr, buf))
141 }
142 }
143
144 impl<'a> DemuxCore<'a> for WavPackDemuxer<'a> {
145 fn open(&mut self, strmgr: &mut StreamManager, seek_index: &mut SeekIndex) -> DemuxerResult<()> {
146
147 let (hdr, buf) = self.read_blocks()?;
148
149 let srate = hdr.get_sample_rate();
150 validate!(srate != 0);
151 let channels = if !hdr.is_end_block() {
152 let mut ch_count = 0;
153 let mut off = 0;
154 loop {
155 let hdr = WVHeader::parse(&buf[off..]).unwrap();
156 off += WV_HEADER_SIZE + hdr.get_size();
157 ch_count += hdr.get_num_channels();
158 if hdr.is_end_block() {
159 break;
160 }
161 }
162 ch_count
163 } else {
164 hdr.get_num_channels()
165 };
166
167 self.nsamples = hdr.tot_samples;
168
169 let mut fmt = SND_S16P_FORMAT;
170 if (hdr.flags & WV_FLAG_FLOATS) != 0 {
171 fmt.float = true;
172 } else {
173 fmt.bits = (((hdr.flags & 3) + 1) * 8) as u8;
174 }
175
176 let ahdr = NAAudioInfo::new(srate, channels, SND_S16P_FORMAT, 1);
177 let ainfo = NACodecInfo::new("wavpack", NACodecTypeInfo::Audio(ahdr), Some(buf.clone()));
178 strmgr.add_stream(NAStream::new(StreamType::Audio, 0, ainfo, 1, srate, hdr.tot_samples)).unwrap();
179 seek_index.mode = SeekIndexMode::Automatic;
180 self.srate = srate;
181 self.known_frames = Vec::with_capacity(((self.nsamples + u64::from(srate) - 1) / u64::from(srate)) as usize);
182 self.known_frames.push(FrameSeekInfo { off: 0, samplepos: hdr.block_index });
183
184 self.first_blocks = Some((hdr, buf));
185 Ok(())
186 }
187 fn get_frame(&mut self, strmgr: &mut StreamManager) -> DemuxerResult<NAPacket> {
188 if self.first_blocks.is_some() {
189 let mut fb = None;
190 std::mem::swap(&mut fb, &mut self.first_blocks);
191 let (refhdr, buf) = fb.unwrap();
192 let stream = strmgr.get_stream(0).unwrap();
193 let ts = stream.make_ts(Some(self.samplepos), None, None);
194 let pkt = NAPacket::new(stream, ts, true, buf);
195
196 self.samplepos += u64::from(refhdr.block_samples);
197
198 return Ok(pkt);
199 }
200 if self.samplepos == self.nsamples {
201 return Err(DemuxerError::EOF);
202 }
203 let cur_off = self.src.tell();
204 let cur_spos = self.samplepos;
205 let (refhdr, buf) = self.read_blocks()?;
206
207 let stream = strmgr.get_stream(0).unwrap();
208 let ts = stream.make_ts(Some(self.samplepos), None, None);
209 let pkt = NAPacket::new(stream, ts, true, buf);
210
211 self.samplepos += u64::from(refhdr.block_samples);
212 if self.known_frames.last().unwrap_or(&FrameSeekInfo::default()).samplepos < cur_spos {
213 self.known_frames.push(FrameSeekInfo{off: cur_off, samplepos: cur_spos });
214 }
215
216 Ok(pkt)
217 }
218 fn seek(&mut self, time: NATimePoint, _seek_index: &SeekIndex) -> DemuxerResult<()> {
219 self.first_blocks = None;
220 if let NATimePoint::Milliseconds(ms) = time {
221 let samppos = ms * u64::from(self.srate) / 1000;
222 if self.known_frames.last().unwrap_or(&FrameSeekInfo::default()).samplepos >= samppos {
223 for point in self.known_frames.iter().rev() {
224 if point.samplepos <= samppos {
225 self.src.seek(SeekFrom::Start(point.off))?;
226 self.samplepos = point.samplepos;
227 return Ok(());
228 }
229 }
230 } else {
231 let mut hdrbuf = [0u8; WV_HEADER_SIZE];
232 let lastoff = self.known_frames.last().unwrap_or(&FrameSeekInfo::default()).off;
233 self.src.seek(SeekFrom::Start(lastoff))?;
234 loop {
235 self.src.peek_buf(&mut hdrbuf)?;
236 let hdr = WVHeader::parse(&hdrbuf)?;
237 if hdr.is_start_block() {
238 self.known_frames.push(FrameSeekInfo{off: self.src.tell(), samplepos: hdr.block_index });
239 if hdr.block_index <= samppos && hdr.block_index + u64::from(hdr.block_samples) > samppos {
240 self.samplepos = hdr.block_index;
241 return Ok(());
242 }
243 if hdr.block_index > samppos {
244 break;
245 }
246 }
247 self.src.read_skip(WV_HEADER_SIZE + hdr.get_size())?
248 }
249 }
250 Err(DemuxerError::SeekError)
251 } else {
252 Err(DemuxerError::NotPossible)
253 }
254 }
255 fn get_duration(&self) -> u64 { 0 }
256 }
257
258 impl<'a> NAOptionHandler for WavPackDemuxer<'a> {
259 fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] }
260 fn set_options(&mut self, _options: &[NAOption]) { }
261 fn query_option_value(&self, _name: &str) -> Option<NAValue> { None }
262 }
263
264
265 pub struct WavPackDemuxerCreator { }
266
267 impl DemuxerCreator for WavPackDemuxerCreator {
268 fn new_demuxer<'a>(&self, br: &'a mut ByteReader<'a>) -> Box<dyn DemuxCore<'a> + 'a> {
269 Box::new(WavPackDemuxer::new(br))
270 }
271 fn get_name(&self) -> &'static str { "wavpack" }
272 }
273
274 #[cfg(test)]
275 mod test {
276 use super::*;
277 use std::fs::File;
278
279 #[test]
280 fn test_wavpack_demux() {
281 // sample from the official WavPack test samples set
282 let mut file = File::open("assets/LLaudio/wv/false_stereo.wv").unwrap();
283 let mut fr = FileReader::new_read(&mut file);
284 let mut br = ByteReader::new(&mut fr);
285 let mut dmx = WavPackDemuxer::new(&mut br);
286 let mut sm = StreamManager::new();
287 let mut si = SeekIndex::new();
288 dmx.open(&mut sm, &mut si).unwrap();
289 loop {
290 let pktres = dmx.get_frame(&mut sm);
291 if let Err(e) = pktres {
292 if (e as i32) == (DemuxerError::EOF as i32) { break; }
293 panic!("error");
294 }
295 let pkt = pktres.unwrap();
296 println!("Got {}", pkt);
297 }
298 }
299 }