notation_audio 0.6.0

Fun notation - audio features
Documentation
use bevy::prelude::*;
use bevy::audio::{Source, AddAudioSource};
use ringbuffer::{AllocRingBuffer, RingBuffer};
use bevy::utils::syncunsafecell::SyncUnsafeCell;
use std::sync::Arc;

pub struct StereoStreamDecoder {
    buffer: Arc<SyncUnsafeCell<AllocRingBuffer<[f32; 2]>>>,
    data: Option<f32>,
}

impl std::fmt::Debug for StereoStreamDecoder {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let buffer = unsafe {
            self.buffer.as_ref().get().as_mut().unwrap()
        };
        write!(f, "<StereoStreamDecoder>({}/{})", buffer.len(), buffer.capacity())
    }
}

impl Iterator for StereoStreamDecoder {
    type Item = f32;

    fn next(&mut self) -> Option<Self::Item> {
        if let Some(data) = self.data {
            self.data = None;
            return Some(data);
        }
        let buffer = unsafe {
            self.buffer.as_ref().get().as_mut().unwrap()
        };
        if buffer.is_full() {
            let capacity = buffer.capacity();
            println!("<StereoStreamDecoder>[{}] buffer is full", capacity);
            buffer.clear();
        }
        let data = buffer.dequeue().unwrap_or([0.0, 0.0]);
        self.data = Some(data[1]);
        Some(data[0])
    }
}

impl Source for StereoStreamDecoder {
    fn current_frame_len(&self) -> Option<usize> {
        None
    }

    fn channels(&self) -> u16 {
        2
    }

    fn sample_rate(&self) -> u32 {
        44_100
    }

    fn total_duration(&self) -> Option<std::time::Duration> {
        None
    }
}

#[derive(Asset, TypePath)]
pub struct StereoStream {
    pub volume: f32,
    buffer: Arc<SyncUnsafeCell<AllocRingBuffer<[f32; 2]>>>,
}

impl std::fmt::Debug for StereoStream {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let buffer = unsafe {
            self.buffer.as_ref().get().as_mut().unwrap()
        };
        write!(f, "<StereoStream>({}/{}, v:{})", buffer.len(), buffer.capacity(), self.volume)
    }
}

impl Decodable for StereoStream {
    type DecoderItem = <StereoStreamDecoder as Iterator>::Item;
    type Decoder = StereoStreamDecoder;

    fn decoder(&self) -> Self::Decoder {
        StereoStreamDecoder {
            buffer: self.buffer.clone(),
            data: None,
        }
    }
}


impl StereoStream {
    pub const FRAME_STEP: f64 = 1.0 / 44_100.0;

    pub const DEFAULT_CAPACITY: usize = 4096;
    pub const DEFAULT_VOLUME: f32 = 1.0;

    pub fn remaining(&self) -> usize {
        let buffer = unsafe {
            self.buffer.as_ref().get().as_mut().unwrap()
        };
        buffer.capacity() - buffer.len()
    }

    pub fn push(&mut self, left: f32, right: f32) {
        let buffer = unsafe {
            self.buffer.as_ref().get().as_mut().unwrap()
        };
        buffer.push([left * self.volume, right * self.volume]);
    }

    pub fn push_batch(&mut self, volume_factor: f32, left: &[f32], right: &[f32]) {
        let buffer = unsafe {
            self.buffer.as_ref().get().as_mut().unwrap()
        };
        for i in 0..left.len() {
            buffer.push([left[i] * self.volume * volume_factor, right[i] * self.volume * volume_factor]);
        }
    }

    pub fn init_streaming(
        app: &mut App,
        setup_default_streaming: bool,
    ) {
        app.add_audio_source::<StereoStream>();
        if setup_default_streaming {
            app.add_systems(Startup, Self::setup_default_streaming);
        }
    }

    pub fn setup_streaming(
        commands: &mut Commands,
        assets: &mut ResMut<Assets<StereoStream>>,
        capacity: usize,
        volume: f32,
    ) {
        let buffer = AllocRingBuffer::<[f32; 2]>::new(capacity);
        let stream = StereoStream {
            volume,
            buffer: Arc::new(SyncUnsafeCell::new(buffer)),
        };
        let stream_handle = assets.add(stream);
        commands.spawn(AudioSourceBundle {
            source: stream_handle,
            ..default()
        });
    }

    pub fn setup_default_streaming(
        mut commands: Commands,
        mut assets: ResMut<Assets<StereoStream>>,
    ) {
        Self::setup_streaming(&mut commands, &mut assets, Self::DEFAULT_CAPACITY, Self::DEFAULT_VOLUME);
    }
}