1use crate::error::Result;
2use crate::macros::decode_err;
3use crate::musepack::constants::{
4 FREQUENCY_TABLE, MPC_DECODER_SYNTH_DELAY, MPC_FRAME_LENGTH, MPC_OLD_GAIN_REF,
5};
6use crate::properties::FileProperties;
7
8use std::io::Read;
9use std::time::Duration;
10
11use byteorder::{LittleEndian, ReadBytesExt};
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
15pub enum Profile {
16 #[default]
18 None,
19 Unstable,
21 Unused,
23 BelowTelephone0,
25 BelowTelephone1,
27 Telephone,
29 Thumb,
31 Radio,
33 Standard,
35 Xtreme,
37 Insane,
39 BrainDead,
41 AboveBrainDead9,
43 AboveBrainDead10,
45}
46
47impl Profile {
48 #[rustfmt::skip]
52 pub fn from_u8(value: u8) -> Option<Self> {
53 match value {
54 0 => Some(Self::None),
55 1 => Some(Self::Unstable),
56 2 | 3 | 4 => Some(Self::Unused),
57 5 => Some(Self::BelowTelephone0),
58 6 => Some(Self::BelowTelephone1),
59 7 => Some(Self::Telephone),
60 8 => Some(Self::Thumb),
61 9 => Some(Self::Radio),
62 10 => Some(Self::Standard),
63 11 => Some(Self::Xtreme),
64 12 => Some(Self::Insane),
65 13 => Some(Self::BrainDead),
66 14 => Some(Self::AboveBrainDead9),
67 15 => Some(Self::AboveBrainDead10),
68 _ => None,
69 }
70 }
71}
72
73#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
75pub enum Link {
76 #[default]
78 VeryLowStartOrEnd,
79 LoudEnd,
81 LoudStart,
83 LoudStartAndEnd,
85}
86
87impl Link {
88 pub fn from_u8(value: u8) -> Option<Self> {
92 match value {
93 0 => Some(Self::VeryLowStartOrEnd),
94 1 => Some(Self::LoudEnd),
95 2 => Some(Self::LoudStart),
96 3 => Some(Self::LoudStartAndEnd),
97 _ => None,
98 }
99 }
100}
101
102#[derive(Debug, Clone, PartialEq, Default)]
106#[allow(clippy::struct_excessive_bools)]
107pub struct MpcSv7Properties {
108 pub(crate) duration: Duration,
109 pub(crate) average_bitrate: u32,
110 pub(crate) channels: u8, pub(crate) frame_count: u32,
113 pub(crate) intensity_stereo: bool,
115 pub(crate) mid_side_stereo: bool,
116 pub(crate) max_band: u8,
117 pub(crate) profile: Profile,
118 pub(crate) link: Link,
119 pub(crate) sample_freq: u32,
120 pub(crate) max_level: u16,
121 pub(crate) title_gain: i16,
123 pub(crate) title_peak: u16,
124 pub(crate) album_gain: i16,
126 pub(crate) album_peak: u16,
127 pub(crate) true_gapless: bool,
129 pub(crate) last_frame_length: u16,
130 pub(crate) fast_seeking_safe: bool,
131 pub(crate) encoder_version: u8,
133}
134
135impl From<MpcSv7Properties> for FileProperties {
136 fn from(input: MpcSv7Properties) -> Self {
137 Self {
138 duration: input.duration,
139 overall_bitrate: Some(input.average_bitrate),
140 audio_bitrate: Some(input.average_bitrate),
141 sample_rate: Some(input.sample_freq),
142 bit_depth: None,
143 channels: Some(input.channels),
144 channel_mask: None,
145 }
146 }
147}
148
149impl MpcSv7Properties {
150 pub fn duration(&self) -> Duration {
152 self.duration
153 }
154
155 pub fn average_bitrate(&self) -> u32 {
157 self.average_bitrate
158 }
159
160 pub fn sample_rate(&self) -> u32 {
162 self.sample_freq
163 }
164
165 pub fn channels(&self) -> u8 {
167 self.channels
168 }
169
170 pub fn frame_count(&self) -> u32 {
172 self.frame_count
173 }
174
175 pub fn intensity_stereo(&self) -> bool {
177 self.intensity_stereo
178 }
179
180 pub fn mid_side_stereo(&self) -> bool {
182 self.mid_side_stereo
183 }
184
185 pub fn max_band(&self) -> u8 {
187 self.max_band
188 }
189
190 pub fn profile(&self) -> Profile {
192 self.profile
193 }
194
195 pub fn link(&self) -> Link {
197 self.link
198 }
199
200 pub fn max_level(&self) -> u16 {
202 self.max_level
203 }
204
205 pub fn title_gain(&self) -> i16 {
209 self.title_gain
210 }
211
212 pub fn title_peak(&self) -> u16 {
218 self.title_peak
219 }
220
221 pub fn album_gain(&self) -> i16 {
225 self.album_gain
226 }
227
228 pub fn album_peak(&self) -> u16 {
234 self.album_peak
235 }
236
237 pub fn true_gapless(&self) -> bool {
239 self.true_gapless
240 }
241
242 pub fn last_frame_length(&self) -> u16 {
247 self.last_frame_length
248 }
249
250 pub fn fast_seeking_safe(&self) -> bool {
252 self.fast_seeking_safe
253 }
254
255 pub fn encoder_version(&self) -> u8 {
262 self.encoder_version
263 }
264
265 pub(crate) fn read<R>(reader: &mut R, stream_length: u64) -> Result<Self>
266 where
267 R: Read,
268 {
269 let version = reader.read_u8()?;
270 if version & 0x0F != 7 {
271 decode_err!(@BAIL Mpc, "Expected stream version 7");
272 }
273
274 let mut properties = MpcSv7Properties {
275 channels: 2, ..Self::default()
277 };
278
279 properties.frame_count = reader.read_u32::<LittleEndian>()?;
284
285 let chunk = reader.read_u32::<LittleEndian>()?;
287
288 let byte1 = ((chunk & 0xFF00_0000) >> 24) as u8;
289
290 properties.intensity_stereo = ((byte1 & 0x80) >> 7) == 1;
291 properties.mid_side_stereo = ((byte1 & 0x40) >> 6) == 1;
292 properties.max_band = byte1 & 0x3F;
293
294 let byte2 = ((chunk & 0xFF_0000) >> 16) as u8;
295
296 properties.profile = Profile::from_u8((byte2 & 0xF0) >> 4).unwrap(); properties.link = Link::from_u8((byte2 & 0x0C) >> 2).unwrap(); let sample_freq_index = byte2 & 0x03;
300 properties.sample_freq = FREQUENCY_TABLE[sample_freq_index as usize];
301
302 let remaining_bytes = (chunk & 0xFFFF) as u16;
303 properties.max_level = remaining_bytes;
304
305 let title_peak = reader.read_u16::<LittleEndian>()?;
307 let title_gain = reader.read_u16::<LittleEndian>()?;
308
309 let album_peak = reader.read_u16::<LittleEndian>()?;
311 let album_gain = reader.read_u16::<LittleEndian>()?;
312
313 let chunk = reader.read_u32::<LittleEndian>()?;
315
316 properties.true_gapless = (chunk >> 31) == 1;
317
318 if properties.true_gapless {
319 properties.last_frame_length = ((chunk >> 20) & 0x7FF) as u16;
320 }
321
322 properties.fast_seeking_safe = (chunk >> 19) & 1 == 1;
323
324 properties.encoder_version = reader.read_u8()?;
328
329 let set_replay_gain = |gain: u16| -> i16 {
333 if gain == 0 {
334 return 0;
335 }
336
337 let gain = ((MPC_OLD_GAIN_REF - f32::from(gain) / 100.0) * 256.0 + 0.5) as i16;
338 if !(0..i16::MAX).contains(&gain) {
339 return 0;
340 }
341 gain
342 };
343 let set_replay_peak = |peak: u16| -> u16 {
344 if peak == 0 {
345 return 0;
346 }
347
348 ((f64::from(peak).log10() * 20.0 * 256.0) + 0.5) as u16
349 };
350
351 properties.title_gain = set_replay_gain(title_gain);
352 properties.title_peak = set_replay_peak(title_peak);
353 properties.album_gain = set_replay_gain(album_gain);
354 properties.album_peak = set_replay_peak(album_peak);
355
356 if properties.last_frame_length > MPC_FRAME_LENGTH as u16 {
357 decode_err!(@BAIL Mpc, "Invalid last frame length");
358 }
359
360 if properties.sample_freq == 0 {
361 log::warn!("Sample rate is 0, unable to calculate duration and bitrate");
362 return Ok(properties);
363 }
364
365 if properties.frame_count == 0 {
366 log::warn!("Frame count is 0, unable to calculate duration and bitrate");
367 return Ok(properties);
368 }
369
370 let time_per_frame = (MPC_FRAME_LENGTH as f64) / f64::from(properties.sample_freq);
371 let length = (f64::from(properties.frame_count) * time_per_frame) * 1000.0;
372 properties.duration = Duration::from_millis(length as u64);
373
374 let total_samples;
375 if properties.true_gapless {
376 total_samples = (u64::from(properties.frame_count) * MPC_FRAME_LENGTH)
377 - (MPC_FRAME_LENGTH - u64::from(properties.last_frame_length));
378 } else {
379 total_samples =
380 (u64::from(properties.frame_count) * MPC_FRAME_LENGTH) - MPC_DECODER_SYNTH_DELAY;
381 }
382
383 properties.average_bitrate = ((stream_length * 8 * u64::from(properties.sample_freq))
384 / (total_samples * 1000)) as u32;
385
386 Ok(properties)
387 }
388}