#![warn(missing_docs)]
#![allow(clippy::tabs_in_doc_comments)]
mod error;
mod streaming;
use std::{fs::File, io::Cursor, path::Path, sync::Arc};
pub use error::*;
use kira::{
dsp::Frame,
sound::static_sound::{StaticSoundData, StaticSoundSettings},
};
use streaming::decoder::symphonia::SymphoniaDecoder;
pub use streaming::*;
use symphonia::core::{
audio::{AudioBuffer, AudioBufferRef, Signal},
conv::{FromSample, IntoSample},
io::{MediaSource, MediaSourceStream},
sample::Sample,
};
fn load_from_media_source(
media_source: Box<dyn MediaSource>,
settings: StaticSoundSettings,
) -> Result<StaticSoundData, Error> {
let codecs = symphonia::default::get_codecs();
let probe = symphonia::default::get_probe();
let mss = MediaSourceStream::new(media_source, Default::default());
let mut format_reader = probe
.format(
&Default::default(),
mss,
&Default::default(),
&Default::default(),
)?
.format;
let codec_params = &format_reader
.default_track()
.ok_or(Error::NoDefaultTrack)?
.codec_params;
let sample_rate = codec_params.sample_rate.ok_or(Error::UnknownSampleRate)?;
let mut decoder = codecs.make(codec_params, &Default::default())?;
let mut frames = vec![];
loop {
match format_reader.next_packet() {
Ok(packet) => {
let buffer = decoder.decode(&packet)?;
load_frames_from_buffer_ref(&mut frames, &buffer)?;
}
Err(error) => match error {
symphonia::core::errors::Error::IoError(error) => {
if error.kind() == std::io::ErrorKind::UnexpectedEof {
break;
}
return Err(symphonia::core::errors::Error::IoError(error).into());
}
error => return Err(error.into()),
},
}
}
Ok(StaticSoundData {
sample_rate,
frames: Arc::new(frames),
settings,
})
}
pub fn load(
path: impl AsRef<Path>,
settings: StaticSoundSettings,
) -> Result<StaticSoundData, Error> {
load_from_media_source(Box::new(File::open(path)?), settings)
}
pub fn load_from_cursor<T: AsRef<[u8]> + Send + 'static>(
cursor: Cursor<T>,
settings: StaticSoundSettings,
) -> Result<StaticSoundData, Error> {
load_from_media_source(Box::new(cursor), settings)
}
pub fn stream(
path: impl AsRef<Path>,
settings: StreamingSoundSettings,
) -> Result<StreamingSoundData<Error>, Error> {
Ok(StreamingSoundData {
decoder: Box::new(SymphoniaDecoder::new(Box::new(File::open(path)?))?),
settings,
})
}
pub fn stream_from_cursor<T: AsRef<[u8]> + Send + 'static>(
cursor: Cursor<T>,
settings: StreamingSoundSettings,
) -> Result<StreamingSoundData<Error>, Error> {
Ok(StreamingSoundData {
decoder: Box::new(SymphoniaDecoder::new(Box::new(cursor))?),
settings,
})
}
fn load_frames_from_buffer_ref(
frames: &mut Vec<Frame>,
buffer: &AudioBufferRef,
) -> Result<(), Error> {
match buffer {
AudioBufferRef::U8(buffer) => load_frames_from_buffer(frames, buffer),
AudioBufferRef::U16(buffer) => load_frames_from_buffer(frames, buffer),
AudioBufferRef::U24(buffer) => load_frames_from_buffer(frames, buffer),
AudioBufferRef::U32(buffer) => load_frames_from_buffer(frames, buffer),
AudioBufferRef::S8(buffer) => load_frames_from_buffer(frames, buffer),
AudioBufferRef::S16(buffer) => load_frames_from_buffer(frames, buffer),
AudioBufferRef::S24(buffer) => load_frames_from_buffer(frames, buffer),
AudioBufferRef::S32(buffer) => load_frames_from_buffer(frames, buffer),
AudioBufferRef::F32(buffer) => load_frames_from_buffer(frames, buffer),
AudioBufferRef::F64(buffer) => load_frames_from_buffer(frames, buffer),
}
}
fn load_frames_from_buffer<S: Sample>(
frames: &mut Vec<Frame>,
buffer: &AudioBuffer<S>,
) -> Result<(), Error>
where
f32: FromSample<S>,
{
match buffer.spec().channels.count() {
1 => {
for sample in buffer.chan(0) {
frames.push(Frame::from_mono((*sample).into_sample()));
}
}
2 => {
for (left, right) in buffer.chan(0).iter().zip(buffer.chan(1).iter()) {
frames.push(Frame::new((*left).into_sample(), (*right).into_sample()));
}
}
_ => return Err(Error::UnsupportedChannelConfiguration),
}
Ok(())
}