limnus_audio_sample/
lib.rs1use limnus_app::prelude::{App, Plugin};
6use limnus_asset_registry::AssetRegistry;
7use limnus_assets::Assets;
8use limnus_assets::prelude::{AssetName, RawWeakId};
9use limnus_assets_loader::{
10    AssetLoader, ConversionError, ResourceStorage, WrappedAssetLoaderRegistry,
11};
12use limnus_audio_mixer::StereoSample;
13use limnus_local_resource::LocalResourceStorage;
14use tracing::debug;
15
16#[must_use]
17pub fn load_wav(payload: &[u8]) -> StereoSample {
18    let mut reader = hound::WavReader::new(payload).expect("could not convert wav file");
19    let hound::WavSpec {
20        sample_rate: source_sample_rate,
21        sample_format,
22        bits_per_sample,
23        channels,
24        ..
25    } = reader.spec();
26    assert_eq!(channels, 2);
30
31    let samples_result: Result<Vec<f32>, _> = match sample_format {
33        hound::SampleFormat::Int => {
34            let max_value = 2_u32.pow(u32::from(bits_per_sample) - 1) - 1;
35            reader
36                .samples::<i32>()
37                .map(|sample| sample.map(|sample| sample as f32 / max_value as f32))
38                .collect()
39        }
40        hound::SampleFormat::Float => reader.samples::<f32>().collect(),
41    };
42    let mut samples = samples_result.unwrap();
43
44    let samples_stereo = oddio::frame_stereo(&mut samples);
45    let sound_frames = oddio::Frames::from_slice(source_sample_rate, samples_stereo);
46
47    StereoSample {
48        stereo_frames: sound_frames,
49    }
50}
51
52pub struct AudioSamplePlugin;
53
54impl Plugin for AudioSamplePlugin {
55    fn build(&self, app: &mut App) {
56        {
57            let registry = app.resource_mut::<WrappedAssetLoaderRegistry>();
58            let loader = StereoSampleConverter::new();
59
60            registry.value.lock().unwrap().register_loader(loader);
61        }
62        app.insert_resource(Assets::<StereoSample>::default());
63    }
64}
65
66#[derive(Default)]
67pub struct StereoSampleConverter;
68
69impl StereoSampleConverter {
70    #[must_use]
71    pub const fn new() -> Self {
72        Self {}
73    }
74}
75
76impl AssetLoader for StereoSampleConverter {
77    type AssetType = StereoSample;
78
79    fn convert_and_insert(
80        &self,
81        id: RawWeakId,
82        octets: &[u8],
83        resources: &mut ResourceStorage,
84        _local_resources: &mut LocalResourceStorage,
85    ) -> Result<(), ConversionError> {
86        let name: AssetName;
87        {
88            let asset_container = resources.fetch::<AssetRegistry>();
89            name = asset_container
90                .name_raw(id)
91                .expect("should know about this Id");
92        }
93
94        debug!("convert from wav {name}");
95        let stereo_sample = load_wav(octets);
96
97        debug!("converted wav {name}");
98        let stereo_sample_assets = resources.fetch_mut::<Assets<StereoSample>>();
99
100        stereo_sample_assets.set_raw(id, stereo_sample);
101
102        Ok(())
103    }
104}