bevy_oddio 0.3.0

A third party Bevy plugin that integrates `oddio` into Bevy.
Documentation
use bevy::asset::{AssetLoader, BoxedFuture, Error, LoadContext, LoadedAsset};
use minimp3::Decoder;

use crate::{frames::Stereo, AudioSource};

#[derive(Default)]
pub struct Mp3Loader;

impl AssetLoader for Mp3Loader {
    fn load<'a>(
        &'a self,
        bytes: &'a [u8],
        load_context: &'a mut LoadContext,
    ) -> BoxedFuture<'a, Result<(), Error>> {
        Box::pin(async move {
            let mut decoder = Decoder::new(bytes);

            let mut samples = Vec::new();

            let mut current_sample_rate = 44100;

            loop {
                match decoder.next_frame() {
                    Ok(minimp3::Frame {
                        data, sample_rate, ..
                    }) => {
                        samples.push([data[0], data[1]]);
                        current_sample_rate = sample_rate;
                    }
                    Err(minimp3::Error::Eof) => break,
                    Err(err) => return Err(err.into()),
                }
            }

            let convert_i32_to_f32 = |int| {
                if int < 0 {
                    (f32::from(int)) / -(f32::from(i16::MIN))
                } else {
                    (f32::from(int)) / f32::from(i16::MAX)
                }
            };

            let samples: Vec<Stereo> = samples
                .into_iter()
                .map(|[l, r]| [convert_i32_to_f32(l), convert_i32_to_f32(r)])
                .map(Stereo::from)
                .collect();

            #[allow(clippy::cast_sign_loss)]
            let frames = oddio::Frames::from_iter(current_sample_rate as u32, samples);

            let audio_source = AudioSource { frames };

            load_context.set_default_asset(LoadedAsset::new(audio_source));

            Ok(())
        })
    }

    fn extensions(&self) -> &[&str] {
        &["mp3"]
    }
}