lavende-core 0.1.1

Core in-process Discord voice connection and playback engine
Documentation
pub mod controller {
    use crate::audio::{
        AudioFrame,
        buffer::{PooledBuffer, acquire_buffer},
        constants::FRAME_SIZE_SAMPLES,
        effects::{
            crossfade::CrossfadeController, fade::FadeEffect, tape::TapeEffect,
            volume::VolumeEffect,
        },
        error::AudioError,
    };
    use flume::{Receiver, Sender};
    pub struct FlowController {
        pub tape: TapeEffect,
        pub volume: VolumeEffect,
        pub fade: FadeEffect,
        pub crossfade: CrossfadeController,
        pending_pcm: Vec<i16>,
        decoder_done: bool,
        frame_rx: Receiver<AudioFrame>,
        frame_tx: Option<Sender<AudioFrame>>,
        latest_opus: Option<Vec<u8>>,
    }
    impl FlowController {
        pub fn new(
            frame_rx: Receiver<AudioFrame>,
            frame_tx: Sender<AudioFrame>,
            sample_rate: u32,
            channels: usize,
            volume: f32,
        ) -> Self {
            Self::build(frame_rx, Some(frame_tx), sample_rate, channels, volume)
        }
        pub fn for_mixer(
            frame_rx: Receiver<AudioFrame>,
            sample_rate: u32,
            channels: usize,
            volume: f32,
        ) -> Self {
            Self::build(frame_rx, None, sample_rate, channels, volume)
        }
        fn build(
            frame_rx: Receiver<AudioFrame>,
            frame_tx: Option<Sender<AudioFrame>>,
            sample_rate: u32,
            channels: usize,
            volume: f32,
        ) -> Self {
            Self {
                tape: TapeEffect::new(sample_rate, channels),
                volume: VolumeEffect::new(volume, sample_rate, channels),
                fade: FadeEffect::new(1.0, channels),
                crossfade: CrossfadeController::new(sample_rate, channels),
                pending_pcm: Vec::with_capacity(FRAME_SIZE_SAMPLES * 2),
                decoder_done: false,
                frame_rx,
                frame_tx,
                latest_opus: None,
            }
        }
        pub fn run(&mut self) {
            while let Ok(frame_data) = self.frame_rx.recv() {
                match frame_data {
                    AudioFrame::Pcm(pooled) => {
                        if pooled.is_empty() {
                            self.pending_pcm.clear();
                            continue;
                        }
                        self.pending_pcm.extend_from_slice(&pooled);
                        while self.pending_pcm.len() >= FRAME_SIZE_SAMPLES {
                            let mut frame = acquire_buffer(FRAME_SIZE_SAMPLES);
                            frame.extend(self.pending_pcm.drain(..FRAME_SIZE_SAMPLES));
                            self.process_frame(&mut frame);
                            if self
                                .frame_tx
                                .as_ref()
                                .is_some_and(|tx| tx.send(AudioFrame::Pcm(frame)).is_err())
                            {
                                return;
                            }
                        }
                    }
                    AudioFrame::Opus(packet) => {
                        if let Some(tx) = &self.frame_tx
                            && tx.send(AudioFrame::Opus(packet)).is_err()
                        {
                            return;
                        }
                    }
                }
            }
        }
        pub fn try_pop_frame(&mut self) -> Result<Option<PooledBuffer>, AudioError> {
            if !self.decoder_done {
                while self.pending_pcm.len() < FRAME_SIZE_SAMPLES {
                    match self.frame_rx.try_recv() {
                        Ok(AudioFrame::Pcm(chunk)) if chunk.is_empty() => {
                            self.pending_pcm.clear();
                            self.decoder_done = false;
                        }
                        Ok(AudioFrame::Pcm(chunk)) => {
                            self.pending_pcm.extend_from_slice(&chunk);
                            crate::audio::buffer::release_buffer(chunk);
                        }
                        Ok(AudioFrame::Opus(packet)) => {
                            self.latest_opus = Some(packet);
                        }
                        Err(flume::TryRecvError::Empty) => break,
                        Err(flume::TryRecvError::Disconnected) => {
                            self.decoder_done = true;
                            break;
                        }
                    }
                }
            }
            if self.pending_pcm.len() >= FRAME_SIZE_SAMPLES {
                let mut frame = acquire_buffer(FRAME_SIZE_SAMPLES);
                frame.extend(self.pending_pcm.drain(..FRAME_SIZE_SAMPLES));
                self.process_frame(&mut frame);
                Ok(Some(frame))
            } else if self.decoder_done {
                Err(AudioError::DecoderFinished)
            } else {
                Ok(None)
            }
        }
        pub fn process_frame(&mut self, frame: &mut [i16]) {
            self.tape.process(frame);
            self.volume.process(frame);
            self.fade.process(frame);
            self.crossfade.fill_buffer();
            if self.crossfade.is_active() {
                self.crossfade.process(frame);
            }
        }
        pub fn take_opus(&mut self) -> Option<Vec<u8>> {
            self.latest_opus.take()
        }
    }
}
pub use controller::FlowController;