audio: Grab MP3 sample rate directly from MP3 data

MP3 data in an SWF can be of a different sample rate than
indicated in the SWF tag, so grab the sample rate from the MP3
decoder instead of passing it in from the SWF tag.

Also, a general clean up of the MP3 decoders.

Fixes #335.
This commit is contained in:
Mike Welsh 2021-10-17 18:31:09 -07:00
parent 3dfa5ade18
commit 7745c9137f
3 changed files with 62 additions and 81 deletions

View File

@ -58,11 +58,7 @@ pub fn make_decoder<R: 'static + Send + Read>(
format.sample_rate, format.sample_rate,
)?), )?),
#[cfg(any(feature = "minimp3", feature = "symphonia"))] #[cfg(any(feature = "minimp3", feature = "symphonia"))]
AudioCompression::Mp3 => Box::new(Mp3Decoder::new( AudioCompression::Mp3 => Box::new(Mp3Decoder::new(data)?),
if format.is_stereo { 2 } else { 1 },
format.sample_rate.into(),
data,
)),
AudioCompression::Nellymoser => { AudioCompression::Nellymoser => {
Box::new(NellymoserDecoder::new(data, format.sample_rate.into())) Box::new(NellymoserDecoder::new(data, format.sample_rate.into()))
} }

View File

@ -1,39 +1,42 @@
#[cfg(feature = "minimp3")] #[cfg(feature = "minimp3")]
pub mod minimp3 { pub mod minimp3 {
use crate::backend::audio::decoders::{Decoder, SeekableDecoder}; use crate::backend::audio::decoders::{Decoder, SeekableDecoder};
use std::io::{Cursor, Read}; use std::{
convert::TryInto,
io::{Cursor, Read},
};
type Error = Box<dyn std::error::Error>;
pub struct Mp3Decoder<R: Read> { pub struct Mp3Decoder<R: Read> {
decoder: minimp3::Decoder<R>, decoder: minimp3::Decoder<R>,
sample_rate: u32, frame: minimp3::Frame,
num_channels: u16, sample_rate: u16,
cur_frame: minimp3::Frame, num_channels: u8,
cur_sample: usize, cur_sample: usize,
num_samples: usize, num_samples: usize,
} }
impl<R: Read> Mp3Decoder<R> { impl<R: Read> Mp3Decoder<R> {
pub fn new(num_channels: u16, sample_rate: u32, reader: R) -> Self { pub fn new(reader: R) -> Result<Self, Error> {
Mp3Decoder { let mut decoder = minimp3::Decoder::new(reader);
decoder: minimp3::Decoder::new(reader), let frame = decoder.next_frame()?;
num_channels, let sample_rate = frame.sample_rate.try_into()?;
let num_channels = frame.channels.try_into()?;
Ok(Mp3Decoder {
decoder,
frame,
sample_rate, sample_rate,
cur_frame: minimp3::Frame { num_channels,
data: vec![],
sample_rate: sample_rate as i32,
channels: num_channels.into(),
layer: 3,
bitrate: 128,
},
cur_sample: 0, cur_sample: 0,
num_samples: 0, num_samples: 0,
} })
} }
fn next_frame(&mut self) { fn next_frame(&mut self) {
if let Ok(frame) = self.decoder.next_frame() { if let Ok(frame) = self.decoder.next_frame() {
self.num_samples = frame.data.len(); self.num_samples = frame.data.len();
self.cur_frame = frame; self.frame = frame;
} else { } else {
self.num_samples = 0; self.num_samples = 0;
} }
@ -52,13 +55,13 @@ pub mod minimp3 {
} }
if self.num_samples > 0 { if self.num_samples > 0 {
if self.num_channels == 2 { if self.num_channels() == 2 {
let left = self.cur_frame.data[self.cur_sample]; let left = self.frame.data[self.cur_sample];
let right = self.cur_frame.data[self.cur_sample + 1]; let right = self.frame.data[self.cur_sample + 1];
self.cur_sample += 2; self.cur_sample += 2;
Some([left, right]) Some([left, right])
} else { } else {
let sample = self.cur_frame.data[self.cur_sample]; let sample = self.frame.data[self.cur_sample];
self.cur_sample += 1; self.cur_sample += 1;
Some([sample, sample]) Some([sample, sample])
} }
@ -76,19 +79,21 @@ pub mod minimp3 {
// but have to work around the borrowing rules of Rust. // but have to work around the borrowing rules of Rust.
let mut cursor = std::mem::take(self.decoder.reader_mut()); let mut cursor = std::mem::take(self.decoder.reader_mut());
cursor.set_position(0); cursor.set_position(0);
*self = Mp3Decoder::new(self.num_channels, self.sample_rate, cursor); if let Ok(decoder) = Mp3Decoder::new(cursor) {
*self = decoder;
}
} }
} }
impl<R: Read> Decoder for Mp3Decoder<R> { impl<R: Read> Decoder for Mp3Decoder<R> {
#[inline] #[inline]
fn num_channels(&self) -> u8 { fn num_channels(&self) -> u8 {
self.num_channels as u8 self.num_channels
} }
#[inline] #[inline]
fn sample_rate(&self) -> u16 { fn sample_rate(&self) -> u16 {
self.sample_rate as u16 self.sample_rate
} }
} }
} }
@ -97,7 +102,10 @@ pub mod minimp3 {
#[allow(dead_code)] #[allow(dead_code)]
pub mod symphonia { pub mod symphonia {
use crate::backend::audio::decoders::{Decoder, SeekableDecoder}; use crate::backend::audio::decoders::{Decoder, SeekableDecoder};
use std::io::{Cursor, Read}; use std::{
convert::TryInto,
io::{Cursor, Read},
};
use symphonia::{ use symphonia::{
core::{ core::{
self, audio, codecs, errors, self, audio, codecs, errors,
@ -107,10 +115,11 @@ pub mod symphonia {
default::formats::Mp3Reader as SymphoniaMp3Reader, default::formats::Mp3Reader as SymphoniaMp3Reader,
}; };
type Error = Box<dyn std::error::Error>;
pub struct Mp3Decoder { pub struct Mp3Decoder {
reader: SymphoniaMp3Reader, reader: SymphoniaMp3Reader,
decoder: Box<dyn codecs::Decoder>, decoder: Box<dyn codecs::Decoder>,
codec_params: codecs::CodecParameters,
sample_buf: audio::SampleBuffer<i16>, sample_buf: audio::SampleBuffer<i16>,
cur_sample: usize, cur_sample: usize,
sample_rate: u16, sample_rate: u16,
@ -119,45 +128,35 @@ pub mod symphonia {
} }
impl Mp3Decoder { impl Mp3Decoder {
pub fn new<R: 'static + Read + Send>( const SAMPLE_BUFFER_DURATION: u64 = 4096;
num_channels: u16,
sample_rate: u32, pub fn new<R: 'static + Read + Send>(reader: R) -> Result<Self, Error> {
reader: R,
) -> Self {
let source = Box::new(io::ReadOnlySource::new(reader)) as Box<dyn io::MediaSource>; let source = Box::new(io::ReadOnlySource::new(reader)) as Box<dyn io::MediaSource>;
let source = io::MediaSourceStream::new(source, Default::default()); let source = io::MediaSourceStream::new(source, Default::default());
let reader = SymphoniaMp3Reader::try_new(source, &Default::default()).unwrap(); let reader = SymphoniaMp3Reader::try_new(source, &Default::default())?;
let track = reader.default_track().unwrap(); let track = reader.default_track().ok_or("No default track")?;
let codec_params = track.codec_params.clone(); let codec_params = track.codec_params.clone();
let decoder = symphonia::default::get_codecs() let decoder =
.make(&codec_params, &Default::default()) symphonia::default::get_codecs().make(&codec_params, &Default::default())?;
.unwrap(); let sample_rate = codec_params.sample_rate.ok_or("Invalid sample rate")?;
let sample_rate = codec_params let channels = codec_params.channels.ok_or("Invalid number of channels")?;
.sample_rate Ok(Mp3Decoder {
.map_or(sample_rate as u16, |n| n as u16);
let num_channels = codec_params
.channels
.map_or(num_channels as u8, |n| n.count() as u8);
Mp3Decoder {
reader, reader,
decoder, decoder,
codec_params,
sample_buf: audio::SampleBuffer::new( sample_buf: audio::SampleBuffer::new(
0, Self::SAMPLE_BUFFER_DURATION,
audio::SignalSpec::new(0, Default::default()), audio::SignalSpec::new(sample_rate, channels),
), ),
cur_sample: 0, cur_sample: 0,
num_channels, num_channels: channels.count().try_into()?,
sample_rate, sample_rate: sample_rate.try_into()?,
stream_ended: false, stream_ended: false,
} })
} }
pub fn new_seekable<R: 'static + AsRef<[u8]> + Send>( pub fn new_seekable<R: 'static + AsRef<[u8]> + Send>(
num_channels: u16,
sample_rate: u32,
reader: Cursor<R>, reader: Cursor<R>,
) -> Self { ) -> Result<Self, Error> {
let source = Box::new(reader) as Box<dyn io::MediaSource>; let source = Box::new(reader) as Box<dyn io::MediaSource>;
let source = io::MediaSourceStream::new(source, Default::default()); let source = io::MediaSourceStream::new(source, Default::default());
let reader = SymphoniaMp3Reader::try_new(source, &Default::default()).unwrap(); let reader = SymphoniaMp3Reader::try_new(source, &Default::default()).unwrap();
@ -166,25 +165,20 @@ pub mod symphonia {
let decoder = symphonia::default::get_codecs() let decoder = symphonia::default::get_codecs()
.make(&codec_params, &Default::default()) .make(&codec_params, &Default::default())
.unwrap(); .unwrap();
let sample_rate = codec_params let sample_rate = codec_params.sample_rate.ok_or("Invalid sample rate")?;
.sample_rate let channels = codec_params.channels.ok_or("Invalid number of channels")?;
.map_or(sample_rate as u16, |n| n as u16); Ok(Mp3Decoder {
let num_channels = codec_params
.channels
.map_or(num_channels as u8, |n| n.count() as u8);
Mp3Decoder {
reader, reader,
decoder, decoder,
codec_params,
sample_buf: audio::SampleBuffer::new( sample_buf: audio::SampleBuffer::new(
0, Self::SAMPLE_BUFFER_DURATION,
audio::SignalSpec::new(0, Default::default()), audio::SignalSpec::new(sample_rate, channels),
), ),
cur_sample: 0, cur_sample: 0,
num_channels, num_channels: channels.count() as u8,
sample_rate, sample_rate: sample_rate as u16,
stream_ended: false, stream_ended: false,
} })
} }
fn next_frame(&mut self) { fn next_frame(&mut self) {
@ -211,7 +205,6 @@ pub mod symphonia {
} }
} }
// EOF reached. // EOF reached.
self.decoder.close();
self.stream_ended = true; self.stream_ended = true;
} }
} }

View File

@ -205,17 +205,9 @@ impl AudioMixer {
format.sample_rate, format.sample_rate,
)?), )?),
#[cfg(feature = "minimp3")] #[cfg(feature = "minimp3")]
AudioCompression::Mp3 => Box::new(decoders::Mp3Decoder::new( AudioCompression::Mp3 => Box::new(decoders::Mp3Decoder::new(data)?),
if format.is_stereo { 2 } else { 1 },
format.sample_rate.into(),
data,
)),
#[cfg(all(feature = "symphonia", not(feature = "minimp3")))] #[cfg(all(feature = "symphonia", not(feature = "minimp3")))]
AudioCompression::Mp3 => Box::new(decoders::Mp3Decoder::new_seekable( AudioCompression::Mp3 => Box::new(decoders::Mp3Decoder::new_seekable(data)?),
if format.is_stereo { 2 } else { 1 },
format.sample_rate.into(),
data,
)),
AudioCompression::Nellymoser => { AudioCompression::Nellymoser => {
Box::new(NellymoserDecoder::new(data, format.sample_rate.into())) Box::new(NellymoserDecoder::new(data, format.sample_rate.into()))
} }