use std::{num::NonZeroUsize, ops::Range};
pub trait SampleResource: Send + Sync + 'static {
fn num_channels(&self) -> NonZeroUsize;
fn len_frames(&self) -> u64;
fn fill_buffers(
&self,
buffers: &mut [&mut [f32]],
buffer_range: Range<usize>,
start_frame: u64,
);
}
pub struct InterleavedResourceI16 {
pub data: Vec<i16>,
pub channels: NonZeroUsize,
}
impl SampleResource for InterleavedResourceI16 {
fn num_channels(&self) -> NonZeroUsize {
self.channels
}
fn len_frames(&self) -> u64 {
(self.data.len() / self.channels.get()) as u64
}
fn fill_buffers(
&self,
buffers: &mut [&mut [f32]],
buffer_range: Range<usize>,
start_frame: u64,
) {
fill_buffers_interleaved(
buffers,
buffer_range,
start_frame as usize,
self.channels,
&self.data,
pcm_i16_to_f32,
);
}
}
pub struct InterleavedResourceU16 {
pub data: Vec<u16>,
pub channels: NonZeroUsize,
}
impl SampleResource for InterleavedResourceU16 {
fn num_channels(&self) -> NonZeroUsize {
self.channels
}
fn len_frames(&self) -> u64 {
(self.data.len() / self.channels.get()) as u64
}
fn fill_buffers(
&self,
buffers: &mut [&mut [f32]],
buffer_range: Range<usize>,
start_frame: u64,
) {
fill_buffers_interleaved(
buffers,
buffer_range,
start_frame as usize,
self.channels,
&self.data,
pcm_u16_to_f32,
);
}
}
pub struct InterleavedResourceF32 {
pub data: Vec<f32>,
pub channels: NonZeroUsize,
}
impl SampleResource for InterleavedResourceF32 {
fn num_channels(&self) -> NonZeroUsize {
self.channels
}
fn len_frames(&self) -> u64 {
(self.data.len() / self.channels.get()) as u64
}
fn fill_buffers(
&self,
buffers: &mut [&mut [f32]],
buffer_range: Range<usize>,
start_frame: u64,
) {
fill_buffers_interleaved(
buffers,
buffer_range,
start_frame as usize,
self.channels,
&self.data,
|s| s,
);
}
}
impl SampleResource for Vec<Vec<i16>> {
fn num_channels(&self) -> NonZeroUsize {
NonZeroUsize::new(self.len()).unwrap()
}
fn len_frames(&self) -> u64 {
self[0].len() as u64
}
fn fill_buffers(
&self,
buffers: &mut [&mut [f32]],
buffer_range: Range<usize>,
start_frame: u64,
) {
fill_buffers_deinterleaved(
buffers,
buffer_range,
start_frame as usize,
self.as_slice(),
pcm_i16_to_f32,
);
}
}
impl SampleResource for Vec<Vec<u16>> {
fn num_channels(&self) -> NonZeroUsize {
NonZeroUsize::new(self.len()).unwrap()
}
fn len_frames(&self) -> u64 {
self[0].len() as u64
}
fn fill_buffers(
&self,
buffers: &mut [&mut [f32]],
buffer_range: Range<usize>,
start_frame: u64,
) {
fill_buffers_deinterleaved(
buffers,
buffer_range,
start_frame as usize,
self.as_slice(),
pcm_u16_to_f32,
);
}
}
impl SampleResource for Vec<Vec<f32>> {
fn num_channels(&self) -> NonZeroUsize {
NonZeroUsize::new(self.len()).unwrap()
}
fn len_frames(&self) -> u64 {
self[0].len() as u64
}
fn fill_buffers(
&self,
buffers: &mut [&mut [f32]],
buffer_range: Range<usize>,
start_frame: u64,
) {
fill_buffers_deinterleaved_f32(buffers, buffer_range, start_frame as usize, self);
}
}
#[inline]
pub fn pcm_i16_to_f32(s: i16) -> f32 {
f32::from(s) * (1.0 / std::i16::MAX as f32)
}
#[inline]
pub fn pcm_u16_to_f32(s: u16) -> f32 {
((f32::from(s)) * (2.0 / std::u16::MAX as f32)) - 1.0
}
pub fn fill_buffers_interleaved<T: Clone + Copy>(
buffers: &mut [&mut [f32]],
buffer_range: Range<usize>,
start_frame: usize,
channels: NonZeroUsize,
data: &[T],
convert: impl Fn(T) -> f32,
) {
let start_frame = start_frame as usize;
let channels = channels.get();
let frames = buffer_range.end - buffer_range.start;
if channels == 1 {
for (buf_s, &src_s) in buffers[0][buffer_range.clone()]
.iter_mut()
.zip(&data[start_frame..start_frame + frames])
{
*buf_s = convert(src_s);
}
return;
}
if channels == 2 && buffers.len() >= 2 {
let (buf0, buf1) = buffers.split_first_mut().unwrap();
let buf0 = &mut buf0[buffer_range.clone()];
let buf1 = &mut buf1[0][buffer_range.clone()];
let src_slice = &data[start_frame * 2..(start_frame + frames) * 2];
for (src_chunk, (buf0_s, buf1_s)) in src_slice
.chunks_exact(2)
.zip(buf0.iter_mut().zip(buf1.iter_mut()))
{
*buf0_s = convert(src_chunk[0]);
*buf1_s = convert(src_chunk[1]);
}
return;
}
let src_slice = &data[start_frame * channels..(start_frame + frames) * channels];
for (ch_i, buf_ch) in (0..channels).zip(buffers.iter_mut()) {
for (src_chunk, buf_s) in src_slice
.chunks_exact(channels)
.zip(buf_ch[buffer_range.clone()].iter_mut())
{
*buf_s = convert(src_chunk[ch_i]);
}
}
}
pub fn fill_buffers_deinterleaved<T: Clone + Copy, V: AsRef<[T]>>(
buffers: &mut [&mut [f32]],
buffer_range: Range<usize>,
start_frame: usize,
data: &[V],
convert: impl Fn(T) -> f32,
) {
let start_frame = start_frame as usize;
let frames = buffer_range.end - buffer_range.start;
if data.len() == 2 && buffers.len() >= 2 {
let (buf0, buf1) = buffers.split_first_mut().unwrap();
let buf0 = &mut buf0[buffer_range.clone()];
let buf1 = &mut buf1[0][buffer_range.clone()];
let s0 = &data[0].as_ref()[start_frame..start_frame + frames];
let s1 = &data[1].as_ref()[start_frame..start_frame + frames];
for i in 0..frames {
buf0[i] = convert(s0[i]);
buf1[i] = convert(s1[i]);
}
return;
}
for (buf, ch) in buffers.iter_mut().zip(data.iter()) {
for (buf_s, &ch_s) in buf[buffer_range.clone()]
.iter_mut()
.zip(ch.as_ref()[start_frame..start_frame + frames].iter())
{
*buf_s = convert(ch_s);
}
}
}
pub fn fill_buffers_deinterleaved_f32<V: AsRef<[f32]>>(
buffers: &mut [&mut [f32]],
buffer_range: Range<usize>,
start_frame: usize,
data: &[V],
) {
let start_frame = start_frame as usize;
for (buf, ch) in buffers.iter_mut().zip(data.iter()) {
buf[buffer_range.clone()].copy_from_slice(
&ch.as_ref()[start_frame..start_frame + buffer_range.end - buffer_range.start],
);
}
}
#[cfg(feature = "symphonium")]
pub struct DecodedAudio(pub symphonium::DecodedAudio);
#[cfg(feature = "symphonium")]
impl DecodedAudio {
pub fn duration_seconds(&self) -> f64 {
self.0.frames() as f64 / self.0.sample_rate() as f64
}
pub fn into_dyn_resource(self) -> crate::collector::ArcGc<dyn SampleResource> {
crate::collector::ArcGc::new_unsized(|| {
std::sync::Arc::new(self) as std::sync::Arc<dyn SampleResource>
})
}
}
#[cfg(feature = "symphonium")]
impl Into<crate::collector::ArcGc<dyn SampleResource>> for DecodedAudio {
fn into(self) -> crate::collector::ArcGc<dyn SampleResource> {
self.into_dyn_resource()
}
}
#[cfg(feature = "symphonium")]
impl SampleResource for DecodedAudio {
fn num_channels(&self) -> NonZeroUsize {
NonZeroUsize::new(self.0.channels()).unwrap()
}
fn len_frames(&self) -> u64 {
self.0.frames() as u64
}
fn fill_buffers(
&self,
buffers: &mut [&mut [f32]],
buffer_range: Range<usize>,
start_frame: u64,
) {
let channels = self.0.channels().min(buffers.len());
if channels == 2 {
let (b1, b2) = buffers.split_first_mut().unwrap();
self.0.fill_stereo(
start_frame as usize,
&mut b1[buffer_range.clone()],
&mut b2[0][buffer_range.clone()],
);
} else {
for (ch_i, b) in buffers[0..channels].iter_mut().enumerate() {
self.0
.fill_channel(ch_i, start_frame as usize, &mut b[buffer_range.clone()])
.unwrap();
}
}
}
}
#[cfg(feature = "symphonium")]
impl From<symphonium::DecodedAudio> for DecodedAudio {
fn from(data: symphonium::DecodedAudio) -> Self {
Self(data)
}
}
#[cfg(feature = "symphonium")]
pub struct DecodedAudioF32(pub symphonium::DecodedAudioF32);
#[cfg(feature = "symphonium")]
impl DecodedAudioF32 {
pub fn duration_seconds(&self, sample_rate: u32) -> f64 {
self.0.frames() as f64 / sample_rate as f64
}
}
#[cfg(feature = "symphonium")]
impl SampleResource for DecodedAudioF32 {
fn num_channels(&self) -> NonZeroUsize {
NonZeroUsize::new(self.0.channels()).unwrap()
}
fn len_frames(&self) -> u64 {
self.0.frames() as u64
}
fn fill_buffers(
&self,
buffers: &mut [&mut [f32]],
buffer_range: Range<usize>,
start_frame: u64,
) {
fill_buffers_deinterleaved_f32(buffers, buffer_range, start_frame as usize, &self.0.data);
}
}
#[cfg(feature = "symphonium")]
impl From<symphonium::DecodedAudioF32> for DecodedAudioF32 {
fn from(data: symphonium::DecodedAudioF32) -> Self {
Self(data)
}
}
#[cfg(feature = "symphonium")]
pub fn load_audio_file<P: AsRef<std::path::Path>>(
loader: &mut symphonium::SymphoniumLoader,
path: P,
#[cfg(feature = "resampler")] sample_rate: std::num::NonZeroU32,
#[cfg(feature = "resampler")] resample_quality: symphonium::ResampleQuality,
) -> Result<DecodedAudio, symphonium::error::LoadError> {
loader
.load(
path,
#[cfg(feature = "resampler")]
Some(sample_rate.get()),
#[cfg(feature = "resampler")]
resample_quality,
None,
)
.map(|d| DecodedAudio(d))
}
#[cfg(feature = "symphonium")]
pub fn load_audio_file_from_source(
loader: &mut symphonium::SymphoniumLoader,
source: Box<dyn symphonium::symphonia::core::io::MediaSource>,
hint: Option<symphonium::symphonia::core::probe::Hint>,
#[cfg(feature = "resampler")] sample_rate: std::num::NonZeroU32,
#[cfg(feature = "resampler")] resample_quality: symphonium::ResampleQuality,
) -> Result<DecodedAudio, symphonium::error::LoadError> {
loader
.load_from_source(
source,
hint,
#[cfg(feature = "resampler")]
Some(sample_rate.get()),
#[cfg(feature = "resampler")]
resample_quality,
None,
)
.map(|d| DecodedAudio(d))
}
#[cfg(feature = "symphonium")]
pub fn decoded_to_resource(data: symphonium::DecodedAudio) -> std::sync::Arc<dyn SampleResource> {
std::sync::Arc::new(DecodedAudio(data))
}
#[cfg(feature = "symphonium")]
pub fn decoded_f32_to_resource(
data: symphonium::DecodedAudioF32,
) -> std::sync::Arc<dyn SampleResource> {
std::sync::Arc::new(DecodedAudioF32(data))
}