1use crate::config::ParsingMode;
2use crate::error::Result;
3use crate::macros::{decode_err, err, parse_mode_choice, try_vec};
4use crate::properties::{ChannelMask, FileProperties};
5
6use std::io::{Read, Seek, SeekFrom};
7use std::time::Duration;
8
9use byteorder::{LittleEndian, ReadBytesExt};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
13#[non_exhaustive]
14pub struct WavPackProperties {
15 pub(crate) version: u16,
16 pub(crate) duration: Duration,
17 pub(crate) overall_bitrate: u32,
18 pub(crate) audio_bitrate: u32,
19 pub(crate) sample_rate: u32,
20 pub(crate) channels: u16,
21 pub(crate) channel_mask: ChannelMask,
22 pub(crate) bit_depth: u8,
23 pub(crate) lossless: bool,
24}
25
26impl From<WavPackProperties> for FileProperties {
27 fn from(input: WavPackProperties) -> Self {
28 Self {
29 duration: input.duration,
30 overall_bitrate: Some(input.overall_bitrate),
31 audio_bitrate: Some(input.audio_bitrate),
32 sample_rate: Some(input.sample_rate),
33 bit_depth: Some(input.bit_depth),
34 channels: Some(input.channels as u8),
35 channel_mask: if input.channel_mask == ChannelMask(0) {
36 None
37 } else {
38 Some(input.channel_mask)
39 },
40 }
41 }
42}
43
44impl WavPackProperties {
45 pub fn duration(&self) -> Duration {
47 self.duration
48 }
49
50 pub fn overall_bitrate(&self) -> u32 {
52 self.overall_bitrate
53 }
54
55 pub fn audio_bitrate(&self) -> u32 {
57 self.audio_bitrate
58 }
59
60 pub fn sample_rate(&self) -> u32 {
62 self.sample_rate
63 }
64
65 pub fn channels(&self) -> u16 {
69 self.channels
70 }
71
72 pub fn channel_mask(&self) -> ChannelMask {
74 self.channel_mask
75 }
76
77 pub fn version(&self) -> u16 {
79 self.version
80 }
81
82 pub fn bit_depth(&self) -> u8 {
84 self.bit_depth
85 }
86
87 pub fn is_lossless(&self) -> bool {
89 self.lossless
90 }
91}
92
93const BYTES_PER_SAMPLE_MASK: u32 = 3;
98const BIT_DEPTH_SHL: u32 = 13;
99const BIT_DEPTH_SHIFT_MASK: u32 = 0x1F << BIT_DEPTH_SHL;
100const FLAG_INITIAL_BLOCK: u32 = 0x800;
101const FLAG_FINAL_BLOCK: u32 = 0x1000;
102const FLAG_MONO: u32 = 0x0004;
103const FLAG_DSD: u32 = 0x8000_0000;
104const FLAG_HYBRID_COMPRESSION: u32 = 8; const ID_FLAG_ODD_SIZE: u8 = 0x40;
109const ID_FLAG_LARGE_SIZE: u8 = 0x80;
110
111const ID_MULTICHANNEL: u8 = 0x0D;
112const ID_NON_STANDARD_SAMPLE_RATE: u8 = 0x27;
113const ID_DSD: u8 = 0xE;
114
115const MIN_STREAM_VERSION: u16 = 0x402;
116const MAX_STREAM_VERSION: u16 = 0x410;
117
118const SAMPLE_RATES: [u32; 16] = [
119 6000, 8000, 9600, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000,
120 192_000, 0,
121];
122
123#[rustfmt::skip]
124pub(super) fn read_properties<R>(reader: &mut R, stream_length: u64, parse_mode: ParsingMode) -> Result<WavPackProperties>
125where
126 R: Read + Seek,
127{
128 let mut properties = WavPackProperties::default();
129
130 let mut offset = 0;
131 let mut total_samples = 0;
132 loop {
133 reader.seek(SeekFrom::Start(offset))?;
134
135 let block_header;
136 match parse_wv_header(reader) {
137 Ok(header) => block_header = header,
138 Err(e) if parse_mode == ParsingMode::Strict => return Err(e),
139 _ => break,
140 }
141
142 let flags = block_header.flags;
143 let sample_rate_idx = ((flags >> 23) & 0xF) as usize;
144 properties.sample_rate = SAMPLE_RATES[sample_rate_idx];
145
146 if sample_rate_idx == 15 || flags & FLAG_DSD == FLAG_DSD {
149 let mut block_contents = try_vec![0; (block_header.block_size - 24) as usize];
150 if reader.read_exact(&mut block_contents).is_err() {
151 parse_mode_choice!(
152 parse_mode,
153 STRICT: decode_err!(@BAIL WavPack, "Block size mismatch"),
154 DEFAULT: break
155 );
156 }
157
158 if let Err(e) = get_extended_meta_info(parse_mode, &block_contents, &mut properties)
159 {
160 parse_mode_choice!(
161 parse_mode,
162 STRICT: return Err(e),
163 DEFAULT: break
164 );
165 }
166
167 if sample_rate_idx == 15 && properties.sample_rate == 0 {
170 parse_mode_choice!(
171 parse_mode,
172 STRICT: decode_err!(@BAIL WavPack, "Expected custom sample rate"),
173 DEFAULT: break
174 )
175 }
176 }
177
178 if flags & FLAG_INITIAL_BLOCK == FLAG_INITIAL_BLOCK {
179 if block_header.version < MIN_STREAM_VERSION
180 || block_header.version > MAX_STREAM_VERSION
181 {
182 parse_mode_choice!(
183 parse_mode,
184 STRICT: decode_err!(@BAIL WavPack, "Unsupported stream version encountered"),
185 DEFAULT: break
186 );
187 }
188
189 total_samples = block_header.total_samples;
190 properties.bit_depth = (((flags & BYTES_PER_SAMPLE_MASK) + 1) * 8).saturating_sub((flags & BIT_DEPTH_SHIFT_MASK) >> BIT_DEPTH_SHL) as u8;
191
192 properties.version = block_header.version;
193 properties.lossless = flags & FLAG_HYBRID_COMPRESSION == 0;
194
195
196 if flags & FLAG_FINAL_BLOCK > 0 {
203 let is_mono = flags & FLAG_MONO > 0;
204 properties.channels = if is_mono { 1 } else { 2 };
205 properties.channel_mask = if is_mono { ChannelMask::mono() } else { ChannelMask::stereo() };
206 }
207 }
208
209 if block_header.samples == 0 {
211 offset += u64::from(block_header.block_size + 8);
212 continue;
213 }
214
215 if flags & FLAG_FINAL_BLOCK == FLAG_FINAL_BLOCK {
216 break;
217 }
218
219 offset += u64::from(block_header.block_size + 8);
220 }
221
222 if total_samples == !0 {
224 log::warn!("Unable to calculate duration, unknown sample counts are not yet supported");
225 return Ok(properties);
226 }
227
228 if total_samples == 0 || properties.sample_rate == 0 {
229 if parse_mode == ParsingMode::Strict {
230 decode_err!(@BAIL WavPack, "Unable to calculate duration (sample count == 0 || sample rate == 0)")
231 }
232
233 return Ok(properties);
235 }
236
237 let length = f64::from(total_samples) * 1000. / f64::from(properties.sample_rate);
238 properties.duration = Duration::from_millis((length + 0.5) as u64);
239 properties.audio_bitrate = (stream_length as f64 * 8. / length + 0.5) as u32;
240
241 let file_length = reader.seek(SeekFrom::End(0))?;
242 properties.overall_bitrate = (file_length as f64 * 8. / length + 0.5) as u32;
243
244 Ok(properties)
245}
246
247const WV_BLOCK_MAX_SIZE: u32 = 1_048_576;
249
250#[derive(Debug)]
251struct WVHeader {
252 version: u16,
253 block_size: u32,
254 total_samples: u32,
255 samples: u32,
256 flags: u32,
257}
258
259fn parse_wv_header<R>(reader: &mut R) -> Result<WVHeader>
261where
262 R: Read + Seek,
263{
264 let mut wv_ident = [0; 4];
265 reader.read_exact(&mut wv_ident)?;
266
267 if &wv_ident != b"wvpk" {
268 err!(UnknownFormat);
269 }
270
271 let block_size = reader.read_u32::<LittleEndian>()?;
272 if !(24..=WV_BLOCK_MAX_SIZE).contains(&block_size) {
273 decode_err!(@BAIL WavPack, "WavPack block has an invalid size");
274 }
275
276 let version = reader.read_u16::<LittleEndian>()?;
277
278 reader.seek(SeekFrom::Current(2))?;
283
284 let total_samples = reader.read_u32::<LittleEndian>()?;
285 let _block_idx = reader.seek(SeekFrom::Current(4))?;
286 let samples = reader.read_u32::<LittleEndian>()?;
287 let flags = reader.read_u32::<LittleEndian>()?;
288
289 let _crc = reader.seek(SeekFrom::Current(4))?;
290
291 Ok(WVHeader {
292 version,
293 block_size,
294 total_samples,
295 samples,
296 flags,
297 })
298}
299
300fn get_extended_meta_info(
301 parse_mode: ParsingMode,
302 block_content: &[u8],
303 properties: &mut WavPackProperties,
304) -> Result<()> {
305 let reader = &mut &block_content[..];
306 loop {
307 if reader.len() < 2 {
308 break;
309 }
310
311 let id = reader.read_u8()?;
312 let mut size = u32::from(reader.read_u8()?) << 1;
313
314 let is_large = id & ID_FLAG_LARGE_SIZE > 0;
315 if is_large {
316 size += u32::from(reader.read_u8()?) << 9;
317 size += u32::from(reader.read_u8()?) << 17;
318 }
319
320 if size == 0 {
321 continue;
324 }
325
326 if (size as usize) > reader.len() {
327 err!(SizeMismatch);
328 }
329
330 if id & ID_FLAG_ODD_SIZE > 0 {
331 size -= 1;
332 }
333
334 match id & 0x3F {
335 ID_NON_STANDARD_SAMPLE_RATE => {
336 if size < 3 {
337 decode_err!(@BAIL WavPack, "Encountered an invalid block size for non-standard sample rate");
338 }
339
340 properties.sample_rate = reader.read_u24::<LittleEndian>()?;
341 size -= 3;
342 },
343 ID_DSD => {
344 if size <= 1 {
345 decode_err!(@BAIL WavPack, "Encountered an invalid DSD block size");
346 }
347
348 let mut rate_multiplier = u32::from(reader.read_u8()?);
349 size -= 1;
350
351 if rate_multiplier > 30 {
352 parse_mode_choice!(
353 parse_mode,
354 STRICT: decode_err!(@BAIL WavPack, "Encountered an invalid sample rate multiplier"),
355 DEFAULT: break
356 )
357 }
358
359 rate_multiplier = 1 << rate_multiplier;
360 properties.sample_rate = properties.sample_rate.wrapping_mul(rate_multiplier);
361 },
362 ID_MULTICHANNEL => {
363 if size <= 1 {
364 decode_err!(@BAIL WavPack, "Unable to extract channel information");
365 }
366
367 properties.channels = u16::from(reader.read_u8()?);
368
369 let s = size - 2;
371 match s {
372 0 => {
373 let channel_mask = reader.read_u8()?;
374 size -= 1;
375 properties.channel_mask = ChannelMask(u32::from(channel_mask));
376 },
377 1 => {
378 let channel_mask = reader.read_u16::<LittleEndian>()?;
379 size -= 2;
380 properties.channel_mask = ChannelMask(u32::from(channel_mask));
381 },
382 2 => {
383 let channel_mask = reader.read_u24::<LittleEndian>()?;
384 size -= 3;
385 properties.channel_mask = ChannelMask(channel_mask);
386 },
387 3 => {
388 let channel_mask = reader.read_u32::<LittleEndian>()?;
389 size -= 4;
390 properties.channel_mask = ChannelMask(channel_mask);
391 },
392 4 => {
393 properties.channels |= u16::from(reader.read_u8()? & 0xF) << 8;
394 properties.channels += 1;
395
396 let channel_mask = reader.read_u24::<LittleEndian>()?;
397 size -= 4;
398
399 properties.channel_mask = ChannelMask(channel_mask);
400 },
401 5 => {
402 properties.channels |= u16::from(reader.read_u8()? & 0xF) << 8;
403 properties.channels += 1;
404
405 let channel_mask = reader.read_u32::<LittleEndian>()?;
406 size -= 5;
407
408 properties.channel_mask = ChannelMask(channel_mask);
409 },
410 _ => decode_err!(@BAIL WavPack, "Encountered invalid channel info size"),
411 }
412 },
413 _ => {},
414 }
415
416 if (size as usize) > reader.len() {
418 err!(SizeMismatch);
419 }
420
421 let (_, rem) = reader.split_at(size as usize);
422 *reader = rem;
423
424 if id & ID_FLAG_ODD_SIZE > 0 {
425 let _ = reader.read_u8()?;
426 }
427 }
428
429 Ok(())
430}