sample-resource 0.1.0

Traits for working with arbitrary resources of audio samples
Documentation
//! Helper methods to create a [`SampleResource`](crate::SampleResource)

use audioadapter_sample::sample::{BytesSample, RawSample};
use core::ops::Range;

/// A helper for
/// [`SampleResource::fill_buffers_f32()`](crate::SampleResource::fill_buffers_f32)
/// to fill buffers from a resource of interleaved samples.
///
/// Returns the number of frames (samples in a single channel of audio) that
/// were successfully written to the buffer. This may be less than the requested
/// number of frames if `output_buffer_range` falls outside the range of the sample
/// resource.
///
/// # Panics
/// Panics if:
/// * `output_buffer_range` is out-of-bounds for any channel slice in `output_buffer`
/// * `resource_samples.len() < resource_channels * resource_frames`
pub fn fill_buffers_interleaved<T: RawSample>(
    output_buffer: &mut [&mut [f32]],
    output_buffer_range: Range<usize>,
    start_frame_in_resource: u64,
    resource_channels: usize,
    resource_frames: usize,
    resource_samples: &[T],
) -> usize {
    if output_buffer.is_empty() {
        return 0;
    }

    let Some(frames) = valid_frames(
        output_buffer_range.clone(),
        start_frame_in_resource,
        resource_frames,
    ) else {
        return 0;
    };

    let start_frame_in_resource = start_frame_in_resource as usize;

    // Provide an optimized loop for mono.
    if resource_channels == 1 {
        // Mono, no need to deinterleave.
        for (buf_s, src_s) in output_buffer[0][output_buffer_range.clone()]
            .iter_mut()
            .zip(&resource_samples[start_frame_in_resource..start_frame_in_resource + frames])
        {
            *buf_s = src_s.to_scaled_float();
        }
        return frames;
    }

    // Provide an optimized loop for stereo.
    if resource_channels == 2 && output_buffer.len() >= 2 {
        let (buf0, buf1) = output_buffer.split_first_mut().unwrap();
        let buf0 = &mut buf0[output_buffer_range.clone()];
        let buf1 = &mut buf1[0][output_buffer_range.clone()];

        let src_slice =
            &resource_samples[start_frame_in_resource * 2..(start_frame_in_resource + 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 = src_chunk[0].to_scaled_float();
            *buf1_s = src_chunk[1].to_scaled_float();
        }

        return frames;
    }

    let src_slice = &resource_samples[start_frame_in_resource * resource_channels
        ..(start_frame_in_resource + frames) * resource_channels];
    for (ch_i, buf_ch) in (0..resource_channels).zip(output_buffer.iter_mut()) {
        for (src_chunk, buf_s) in src_slice
            .chunks_exact(resource_channels)
            .zip(buf_ch[output_buffer_range.clone()].iter_mut())
        {
            *buf_s = src_chunk[ch_i].to_scaled_float();
        }
    }

    frames
}

/// A helper for
/// [`SampleResource::fill_buffers_f32()`](crate::SampleResource::fill_buffers_f32)
/// to fill buffers from a resource of interleaved samples stored as raw bytes.
///
/// Returns the number of frames (samples in a single channel of audio) that
/// were successfully written to the buffer. This may be less than the requested
/// number of frames if `output_buffer_range` falls outside the range of the sample
/// resource.
///
/// # Panics
/// Panics if:
/// * `output_buffer_range` is out-of-bounds for any channel slice in `output_buffer`
/// * `resource_samples.len() < resource_channels * resource_frames`
pub fn fill_buffers_interleaved_bytes<T: BytesSample>(
    output_buffer: &mut [&mut [f32]],
    output_buffer_range: Range<usize>,
    start_frame_in_resource: u64,
    resource_channels: usize,
    resource_frames: usize,
    resource_data: &[u8],
) -> usize
where
    T::NumericType: RawSample,
{
    if output_buffer.is_empty() {
        return 0;
    }

    let Some(frames) = valid_frames(
        output_buffer_range.clone(),
        start_frame_in_resource,
        resource_frames,
    ) else {
        return 0;
    };

    let start_frame_in_resource = start_frame_in_resource as usize;

    // Provide an optimized loop for mono.
    if resource_channels == 1 {
        for (buf_s, src_s) in output_buffer[0][output_buffer_range.clone()]
            .iter_mut()
            .zip(
                resource_data[start_frame_in_resource * T::BYTES_PER_SAMPLE
                    ..(start_frame_in_resource + frames) * T::BYTES_PER_SAMPLE]
                    .chunks_exact(T::BYTES_PER_SAMPLE),
            )
        {
            *buf_s = T::from_slice(src_s).to_number().to_scaled_float();
        }
        return frames;
    }

    // Provide an optimized loop for stereo.
    if resource_channels == 2 && output_buffer.len() >= 2 {
        let (buf0, buf1) = output_buffer.split_first_mut().unwrap();
        let buf0 = &mut buf0[output_buffer_range.clone()];
        let buf1 = &mut buf1[0][output_buffer_range.clone()];

        let src_slice = &resource_data[start_frame_in_resource * 2 * T::BYTES_PER_SAMPLE
            ..(start_frame_in_resource + frames) * 2 * T::BYTES_PER_SAMPLE];

        for (src_chunk, (buf0_s, buf1_s)) in src_slice
            .chunks_exact(2 * T::BYTES_PER_SAMPLE)
            .zip(buf0.iter_mut().zip(buf1.iter_mut()))
        {
            *buf0_s = T::from_slice(&src_chunk[0..T::BYTES_PER_SAMPLE])
                .to_number()
                .to_scaled_float();
            *buf1_s = T::from_slice(&src_chunk[T::BYTES_PER_SAMPLE..2 * T::BYTES_PER_SAMPLE])
                .to_number()
                .to_scaled_float();
        }

        return frames;
    }

    let src_slice = &resource_data[start_frame_in_resource * resource_channels * T::BYTES_PER_SAMPLE
        ..(start_frame_in_resource + frames) * resource_channels * T::BYTES_PER_SAMPLE];
    for (ch_i, buf_ch) in (0..resource_channels).zip(output_buffer.iter_mut()) {
        for (src_chunk, buf_s) in src_slice
            .chunks_exact(resource_channels * T::BYTES_PER_SAMPLE)
            .zip(buf_ch[output_buffer_range.clone()].iter_mut())
        {
            *buf_s = T::from_slice(
                &src_chunk[ch_i * T::BYTES_PER_SAMPLE..(ch_i + 1) * T::BYTES_PER_SAMPLE],
            )
            .to_number()
            .to_scaled_float();
        }
    }

    frames
}

/// A helper for
/// [`SampleResource::fill_buffers_f32()`](crate::SampleResource::fill_buffers_f32)
/// to fill buffers from a resource of de-interleaved samples.
///
/// Returns the number of frames (samples in a single channel of audio) that
/// were successfully written to the buffer. This may be less than the requested
/// number of frames if `output_buffer_range` falls outside the range of the sample
/// resource.
///
/// # Panics
/// Panics if:
/// * `output_buffer_range` is out-of-bounds for any channel slice in `output_buffer`
/// * The length of a channel slice in `resource_channels` is less than `resource_frames`
pub fn fill_buffers_deinterleaved<T: RawSample, V: AsRef<[T]>>(
    output_buffer: &mut [&mut [f32]],
    output_buffer_range: Range<usize>,
    start_frame_in_resource: u64,
    resource_frames: usize,
    resource_channels: &[V],
) -> usize {
    if output_buffer.is_empty() {
        return 0;
    }

    let Some(frames) = valid_frames(
        output_buffer_range.clone(),
        start_frame_in_resource,
        resource_frames,
    ) else {
        return 0;
    };

    let start_frame_in_resource = start_frame_in_resource as usize;

    for (buf, ch) in output_buffer.iter_mut().zip(resource_channels.iter()) {
        for (buf_s, ch_s) in buf[output_buffer_range.clone()]
            .iter_mut()
            .zip(ch.as_ref()[start_frame_in_resource..start_frame_in_resource + frames].iter())
        {
            *buf_s = ch_s.to_scaled_float();
        }
    }

    frames
}

/// A helper for
/// [`SampleResource::fill_buffers_f32()`](crate::SampleResource::fill_buffers_f32)
/// to fill buffers from a resource of de-interleaved samples stored as raw bytes.
///
/// Returns the number of frames (samples in a single channel of audio) that
/// were successfully written to the buffer. This may be less than the requested
/// number of frames if `output_buffer_range` falls outside the range of the sample
/// resource.
///
/// # Panics
/// Panics if:
/// * `output_buffer_range` is out-of-bounds for any channel slice in `output_buffer`
/// * The length of a channel slice in `resource_channels` is less than `resource_frames`
pub fn fill_buffers_deinterleaved_bytes<T: BytesSample, V: AsRef<[u8]>>(
    output_buffer: &mut [&mut [f32]],
    output_buffer_range: Range<usize>,
    start_frame_in_resource: u64,
    resource_frames: usize,
    resource_channels: &[V],
) -> usize
where
    T::NumericType: RawSample,
{
    if output_buffer.is_empty() {
        return 0;
    }

    let Some(frames) = valid_frames(
        output_buffer_range.clone(),
        start_frame_in_resource,
        resource_frames,
    ) else {
        return 0;
    };

    let start_frame_in_resource = start_frame_in_resource as usize;

    for (buf, ch) in output_buffer.iter_mut().zip(resource_channels.iter()) {
        for (buf_s, ch_s) in buf[output_buffer_range.clone()].iter_mut().zip(
            ch.as_ref()[start_frame_in_resource * T::BYTES_PER_SAMPLE
                ..(start_frame_in_resource + frames) * T::BYTES_PER_SAMPLE]
                .chunks_exact(T::BYTES_PER_SAMPLE),
        ) {
            *buf_s = T::from_slice(ch_s).to_number().to_scaled_float();
        }
    }

    frames
}

/// A helper for
/// [`SampleResource::fill_buffers_f32()`](crate::SampleResource::fill_buffers_f32)
/// to fill buffers from a resource of de-interleaved samples in `f32`
/// format.
///
/// Returns the number of frames (samples in a single channel of audio) that
/// were successfully written to the buffer. This may be less than the requested
/// number of frames if `output_buffer_range` falls outside the range of the sample
/// resource.
///
/// # Panics
/// Panics if:
/// * `output_buffer_range` is out-of-bounds for any channel slice in `output_buffer`
/// * The length of a channel slice in `resource_channels` is less than `resource_frames`
pub fn fill_buffers_deinterleaved_f32<V: AsRef<[f32]>>(
    output_buffer: &mut [&mut [f32]],
    output_buffer_range: Range<usize>,
    start_frame_in_resource: u64,
    resource_frames: usize,
    resource_channels: &[V],
) -> usize {
    if output_buffer.is_empty() {
        return 0;
    }

    let Some(frames) = valid_frames(
        output_buffer_range.clone(),
        start_frame_in_resource,
        resource_frames,
    ) else {
        return 0;
    };

    let start_frame_in_resource = start_frame_in_resource as usize;

    for (buf, ch) in output_buffer.iter_mut().zip(resource_channels.iter()) {
        buf[output_buffer_range.clone()].copy_from_slice(
            &ch.as_ref()[start_frame_in_resource..start_frame_in_resource + frames],
        );
    }

    frames
}

fn valid_frames(
    output_buffer_range: Range<usize>,
    start_frame_in_resource: u64,
    resource_frames: usize,
) -> Option<usize> {
    let frames = ((output_buffer_range.end - output_buffer_range.start) as u64)
        .min((resource_frames as u64).saturating_sub(start_frame_in_resource))
        as usize;

    if frames == 0 { None } else { Some(frames) }
}