musepack: Support SV4-SV6 property reading

This commit is contained in:
Serial 2023-05-23 18:43:55 -04:00 committed by Alex
parent d89250eef7
commit 62190b8f9f
6 changed files with 147 additions and 10 deletions

View file

@ -491,7 +491,7 @@ mod tests {
use crate::ape::{ApeItem, ApeTag};
use crate::{ItemValue, Tag, TagExt, TagType};
use std::io::{Cursor, Seek, SeekFrom};
use std::io::Cursor;
#[test]
fn parse_ape() {

View file

@ -12,3 +12,6 @@ pub(super) const FREQUENCY_TABLE: [u32; 8] = [44100, 48000, 37800, 32000, 0, 0,
// Taken from mpcdec
/// This is the gain reference used in old ReplayGain
pub const MPC_OLD_GAIN_REF: f32 = 64.82;
pub(super) const MPC_DECODER_SYNTH_DELAY: u32 = 481;
pub(super) const MPC_FRAME_LENGTH: u32 = 36 * 32; // Samples per mpc frame

View file

@ -18,6 +18,7 @@ where
let mut version = MpcStreamVersion::Sv4to6;
let mut file = MpcFile::default();
#[allow(unstable_name_collisions)]
let mut stream_length = reader.stream_len()?;
// ID3v2 tags are unsupported in MPC files, but still possible
@ -33,7 +34,7 @@ where
size += 10;
}
stream_length -= size as u64;
stream_length -= u64::from(size);
}
// Save the current position, so we can go back and read the properties after the tags
@ -48,7 +49,7 @@ where
}
let ID3FindResults(_, lyrics3v2_size) = find_lyrics3v2(reader)?;
stream_length -= lyrics3v2_size as u64;
stream_length -= u64::from(lyrics3v2_size);
reader.seek(SeekFrom::Current(-32))?;
@ -59,7 +60,7 @@ where
let pos = reader.stream_position()?;
reader.seek(SeekFrom::Start(pos - u64::from(header.size)))?;
stream_length -= header.size as u64;
stream_length -= u64::from(header.size);
}
// Restore the position of the magic signature
@ -96,6 +97,7 @@ where
file.properties = MpcProperties::Sv4to6(MpcSv4to6Properties::read(
reader,
parse_options.parsing_mode,
stream_length,
)?)
},
}

View file

@ -1,26 +1,34 @@
use crate::error::Result;
use crate::macros::{decode_err, parse_mode_choice};
use crate::musepack::constants::{MPC_DECODER_SYNTH_DELAY, MPC_FRAME_LENGTH};
use crate::probe::ParsingMode;
use crate::properties::FileProperties;
use std::io::Read;
use std::time::Duration;
use byteorder::{LittleEndian, ReadBytesExt};
/// MPC stream versions 4-6 audio properties
#[derive(Debug, Clone, PartialEq, Default)]
pub struct MpcSv4to6Properties {
pub(crate) duration: Duration,
pub(crate) overall_bitrate: u32,
pub(crate) audio_bitrate: u32,
pub(crate) channels: u8, // NOTE: always 2
pub(crate) sample_rate: u32, // NOTE: always 44100
frame_count: u32,
// Fields actually contained in the header
pub(crate) audio_bitrate: u32,
pub(crate) mid_side_stereo: bool,
pub(crate) stream_version: u16,
pub(crate) max_band: u8,
pub(crate) frame_count: u32,
}
impl From<MpcSv4to6Properties> for FileProperties {
fn from(input: MpcSv4to6Properties) -> Self {
Self {
duration: input.duration,
overall_bitrate: Some(input.overall_bitrate),
overall_bitrate: Some(input.audio_bitrate),
audio_bitrate: Some(input.audio_bitrate),
sample_rate: Some(input.sample_rate),
bit_depth: None,
@ -31,10 +39,114 @@ impl From<MpcSv4to6Properties> for FileProperties {
}
impl MpcSv4to6Properties {
pub(crate) fn read<R>(_reader: &mut R, _parse_mode: ParsingMode) -> Result<Self>
/// Duration of the audio
pub fn duration(&self) -> Duration {
self.duration
}
/// Channel count
pub fn channels(&self) -> u8 {
self.channels
}
/// Sample rate (Hz)
pub fn sample_rate(&self) -> u32 {
self.sample_rate
}
/// Audio bitrate (kbps)
pub fn audio_bitrate(&self) -> u32 {
self.audio_bitrate
}
/// Whether MidSideStereo is used
pub fn mid_side_stereo(&self) -> bool {
self.mid_side_stereo
}
/// The MPC stream version (4-6)
pub fn stream_version(&self) -> u16 {
self.stream_version
}
/// Last subband used in the whole file
pub fn max_band(&self) -> u8 {
self.max_band
}
/// Total number of audio frames
pub fn frame_count(&self) -> u32 {
self.frame_count
}
pub(crate) fn read<R>(
reader: &mut R,
parse_mode: ParsingMode,
stream_length: u64,
) -> Result<Self>
where
R: Read,
{
todo!()
let mut header_data = [0u32; 8];
reader.read_u32_into::<LittleEndian>(&mut header_data)?;
let mut properties = Self::default();
properties.audio_bitrate = (header_data[0] >> 23) & 0x1FF;
let intensity_stereo = (header_data[0] >> 22) & 0x1 == 1;
properties.mid_side_stereo = (header_data[0] >> 21) & 0x1 == 1;
properties.stream_version = ((header_data[0] >> 11) & 0x03FF) as u16;
if !(4..=6).contains(&properties.stream_version) {
decode_err!(@BAIL Mpc, "Invalid stream version encountered")
}
properties.max_band = ((header_data[0] >> 6) & 0x1F) as u8;
let block_size = header_data[0] & 0x3F;
if properties.stream_version >= 5 {
properties.frame_count = header_data[1]; // 32 bit
} else {
properties.frame_count = header_data[1] >> 16; // 16 bit
}
parse_mode_choice!(
parse_mode,
STRICT: {
if properties.audio_bitrate != 0 {
decode_err!(@BAIL Mpc, "Encountered CBR stream")
}
if intensity_stereo {
decode_err!(@BAIL Mpc, "Stream uses intensity stereo coding")
}
if block_size != 1 {
decode_err!(@BAIL Mpc, "Stream has an invalid block size (must be 1)")
}
},
);
if properties.stream_version < 6 {
// Versions before 6 had an invalid last frame
properties.frame_count = properties.frame_count.saturating_sub(1);
}
properties.sample_rate = 44100;
properties.channels = 2;
if properties.frame_count > 0 {
let samples =
(properties.frame_count * MPC_FRAME_LENGTH).saturating_sub(MPC_DECODER_SYNTH_DELAY);
let length = f64::from(samples) / f64::from(properties.sample_rate);
properties.duration = Duration::from_millis(length.ceil() as u64);
let pcm_frames = 1152 * u64::from(properties.frame_count) - 576;
properties.audio_bitrate = ((stream_length as f64
* 8.0 * f64::from(properties.sample_rate))
/ pcm_frames as f64) as u32;
}
Ok(properties)
}
}

View file

@ -127,6 +127,7 @@ mod tests {
use crate::iff::wav::{WavFile, WavFormat, WavProperties};
use crate::mp4::{AudioObjectType, Mp4Codec, Mp4File, Mp4Properties};
use crate::mpeg::{ChannelMode, Emphasis, Layer, MpegFile, MpegProperties, MpegVersion};
use crate::musepack::sv4to6::MpcSv4to6Properties;
use crate::musepack::sv7::{Link, MpcSv7Properties, Profile};
use crate::musepack::sv8::{EncoderInfo, MpcSv8Properties, ReplayGain, StreamHeader};
use crate::musepack::{MpcFile, MpcProperties};
@ -275,6 +276,17 @@ mod tests {
channels: 2,
};
const MPC_SV5_PROPERTIES: MpcSv4to6Properties = MpcSv4to6Properties {
duration: Duration::from_millis(27),
audio_bitrate: 41,
channels: 2,
frame_count: 1009,
mid_side_stereo: true,
stream_version: 5,
max_band: 31,
sample_rate: 44100,
};
const MPC_SV7_PROPERTIES: MpcSv7Properties = MpcSv7Properties {
duration: Duration::from_millis(1428),
overall_bitrate: 86,
@ -484,6 +496,14 @@ mod tests {
)
}
#[test]
fn mpc_sv5_properties() {
assert_eq!(
get_properties::<MpcFile>("tests/files/assets/minimal/mpc_sv5.mpc"),
MpcProperties::Sv4to6(MPC_SV5_PROPERTIES)
)
}
#[test]
fn mpc_sv7_properties() {
assert_eq!(

Binary file not shown.