symphonium 0.9.2

An unofficial easy-to-use wrapper around Symphonia for loading audio files
Documentation
use std::num::{NonZeroU32, NonZeroUsize};

use fixed_resample::rubato::audioadapter::Adapter;
use fixed_resample::{
    PacketResampler, Sequential, audioadapter_buffers::direct::SequentialSliceOfSlices,
};
use symphonia::core::codecs::Decoder;
use symphonia::core::{
    audio::{AudioBuffer, Signal},
    probe::ProbeResult,
};

use super::{decode_warning, shrink_buffer};
use crate::DecodeConfig;
use crate::{DecodedAudioF32, error::LoadError};

pub(crate) fn decode_resampled(
    probed: &mut ProbeResult,
    config: &DecodeConfig,
    sample_rate: NonZeroU32,
    original_sample_rate: NonZeroU32,
    num_channels: NonZeroUsize,
    resampler: &mut PacketResampler<f32, Sequential<f32>>,
    decoder: &mut dyn Decoder,
) -> Result<DecodedAudioF32, LoadError> {
    use fixed_resample::LastPacketInfo;

    // Get the default track in the audio stream.
    let track = probed
        .format
        .default_track()
        .ok_or_else(|| LoadError::NoTrackFound)?;

    let file_frames = track.codec_params.n_frames;
    let max_frames = config.max_bytes / (4 * num_channels.get());

    if let Some(frames) = file_frames
        && frames > max_frames as u64
    {
        return Err(LoadError::FileTooLarge(config.max_bytes));
    }

    let mut tmp_conversion_buf: Option<AudioBuffer<f32>> = None;

    let final_output_frames = file_frames.map(|f| resampler.out_alloc_frames(f) as usize);

    let alloc_frames = file_frames.unwrap_or(32768) as usize;
    let mut final_buf: Vec<Vec<f32>> = (0..num_channels.get())
        .map(|_| {
            let mut v = Vec::new();
            v.reserve_exact(alloc_frames);
            v
        })
        .collect();

    let track_id = track.id;

    let mut total_in_frames: usize = 0;

    while let Ok(packet) = probed.format.next_packet() {
        // If the packet does not belong to the selected track, skip over it.
        if packet.track_id() != track_id {
            continue;
        }

        match decoder.decode(&packet) {
            Ok(decoded) => {
                // If this is the first decoded packet, allocate the temporary conversion
                // buffer with the required capacity.
                if tmp_conversion_buf.is_none() {
                    let spec = *(decoded.spec());
                    let duration = decoded.capacity();

                    tmp_conversion_buf = Some(AudioBuffer::new(duration as u64, spec));
                }
                let tmp_conversion_buf = tmp_conversion_buf.as_mut().unwrap();
                if tmp_conversion_buf.capacity() < decoded.capacity() {
                    let spec = *(decoded.spec());
                    let duration = decoded.capacity();

                    *tmp_conversion_buf = AudioBuffer::new(duration as u64, spec);
                }

                decoded.convert(tmp_conversion_buf);

                let decoded_channels = tmp_conversion_buf.spec().channels.count();
                let decoded_frames = tmp_conversion_buf.frames();
                total_in_frames += decoded_frames;

                let last_packet = if let Some(file_frames) = file_frames {
                    if total_in_frames as u64 >= file_frames {
                        let desired_output_frames =
                            if final_buf[0].len() <= final_output_frames.unwrap() {
                                Some((final_output_frames.unwrap() - final_buf[0].len()) as u64)
                            } else {
                                None
                            };

                        Some(LastPacketInfo {
                            desired_output_frames,
                        })
                    } else {
                        None
                    }
                } else {
                    None
                };

                resampler.process(
                    &SequentialSliceOfSlices::new(
                        tmp_conversion_buf.planes().planes(),
                        decoded_channels,
                        decoded_frames,
                    )
                    .unwrap(),
                    None,
                    None,
                    |in_buf, frames| {
                        for ch in 0..in_buf.channels().min(final_buf.len()) {
                            fixed_resample::extend_from_adapter_channel(
                                &mut final_buf[ch],
                                in_buf,
                                0,
                                ch,
                                frames,
                            );
                        }
                    },
                    last_packet,
                    true, // trim_delay
                );

                if file_frames.is_none() {
                    // Protect against really large files causing out of memory errors.
                    if final_buf[0].len() > max_frames {
                        return Err(LoadError::FileTooLarge(config.max_bytes));
                    }
                }
            }
            Err(symphonia::core::errors::Error::DecodeError(err)) => decode_warning(err),
            Err(e) => return Err(LoadError::ErrorWhileDecoding(e)),
        }
    }

    // Process any leftover samples in the resampler.
    if file_frames.is_none() {
        let final_output_frames = resampler.out_alloc_frames(total_in_frames as u64) as usize;

        if final_buf[0].len() < final_output_frames * 3 {
            let desired_output_frames = final_output_frames - final_buf[0].len();

            resampler.process(
                &SequentialSliceOfSlices::new(&[&[]], 0, 0).unwrap(),
                Some(0..0),
                None,
                |in_buf, frames| {
                    for ch in 0..in_buf.channels().min(final_buf.len()) {
                        fixed_resample::extend_from_adapter_channel(
                            &mut final_buf[ch],
                            in_buf,
                            0,
                            ch,
                            frames,
                        );
                    }
                },
                Some(LastPacketInfo {
                    desired_output_frames: Some(desired_output_frames as u64),
                }),
                true, // trim_delay
            );
        } else {
            for ch in final_buf.iter_mut() {
                ch.resize(final_output_frames, 0.0);
            }
        }

        shrink_buffer(&mut final_buf);
    } else {
        // Sanity check to make sure the resampler output the correct
        // number of frames.
        assert_eq!(final_buf[0].len(), final_output_frames.unwrap());
    }

    Ok(DecodedAudioF32::new(
        final_buf,
        sample_rate,
        original_sample_rate,
    ))
}