lavende-core 0.1.9

Core in-process Discord voice connection and playback engine
Documentation
pub mod decoder {
    use super::resample::LinearResampler;
    use std::io::ErrorKind;
    use symphonia::core::audio::SampleBuffer;
    use symphonia::core::codecs::{CODEC_TYPE_NULL, Decoder, DecoderOptions};
    use symphonia::core::errors::Error;
    use symphonia::core::formats::{FormatOptions, FormatReader};
    use symphonia::core::io::{MediaSource, MediaSourceStream};
    use symphonia::core::meta::MetadataOptions;
    use symphonia::core::probe::Hint;
    pub struct AudioDecoder {
        format: Box<dyn FormatReader>,
        decoder: Box<dyn Decoder>,
        track_id: u32,
        resampler: LinearResampler,
        sample_buf: Option<SampleBuffer<i16>>,
        raw_buffer: Vec<i16>,
        downmix_buf: Vec<i16>,
    }
    impl AudioDecoder {
        pub fn new(source: Box<dyn MediaSource>, ext_hint: Option<&str>) -> Result<Self, String> {
            let mss = MediaSourceStream::new(source, Default::default());
            let mut hint = Hint::new();
            if let Some(ext) = ext_hint {
                hint.with_extension(ext);
            }
            let probed = symphonia::default::get_probe()
                .format(
                    &hint,
                    mss,
                    &FormatOptions::default(),
                    &MetadataOptions::default(),
                )
                .map_err(|e| format!("Failed to probe format: {e}"))?;
            let format = probed.format;
            let track = format
                .tracks()
                .iter()
                .find(|t| t.codec_params.codec != CODEC_TYPE_NULL)
                .ok_or_else(|| "No audio track found in media source".to_string())?;
            let track_id = track.id;
            let sample_rate = track.codec_params.sample_rate.unwrap_or(48000);
            let decoder = symphonia::default::get_codecs()
                .make(&track.codec_params, &DecoderOptions::default())
                .map_err(|e| format!("Failed to create decoder: {e}"))?;
            let resampler = LinearResampler::new(sample_rate, 48000, 2);
            Ok(Self {
                format,
                decoder,
                track_id,
                resampler,
                sample_buf: None,
                raw_buffer: Vec::with_capacity(48000 * 2),
                downmix_buf: Vec::new(),
            })
        }
        pub fn read_frame(&mut self) -> Result<Option<Vec<i16>>, String> {
            const TARGET_FRAME_SAMPLES: usize = 960 * 2;
            while self.raw_buffer.len() < TARGET_FRAME_SAMPLES {
                let packet = match self.format.next_packet() {
                    Ok(p) => p,
                    Err(Error::IoError(ref e)) if e.kind() == ErrorKind::UnexpectedEof => {
                        break;
                    }
                    Err(e) => return Err(format!("Format read error: {e}")),
                };
                if packet.track_id() != self.track_id {
                    continue;
                }
                match self.decoder.decode(&packet) {
                    Ok(decoded) => {
                        let spec = *decoded.spec();
                        let mut buf = self.sample_buf.take().unwrap_or_else(|| {
                            SampleBuffer::<i16>::new(decoded.capacity() as u64, spec)
                        });
                        buf.copy_interleaved_ref(decoded);
                        let samples = buf.samples();
                        if !samples.is_empty() {
                            let frame_channels = spec.channels.count();
                            let stereo_pcm = if frame_channels == 2 {
                                samples
                            } else {
                                self.downmix_buf.clear();
                                let num_frames = samples.len() / frame_channels;
                                for i in 0..num_frames {
                                    let frame =
                                        &samples[i * frame_channels..(i + 1) * frame_channels];
                                    let mut l = 0i32;
                                    let mut r = 0i32;
                                    for (ch, &sample) in frame.iter().enumerate() {
                                        if ch % 2 == 0 {
                                            l += sample as i32;
                                        } else {
                                            r += sample as i32;
                                        }
                                    }
                                    let left_count = frame_channels.div_ceil(2);
                                    let right_count = frame_channels / 2;
                                    self.downmix_buf.push((l / left_count as i32) as i16);
                                    if right_count > 0 {
                                        self.downmix_buf.push((r / right_count as i32) as i16);
                                    } else {
                                        self.downmix_buf.push((l / left_count as i32) as i16);
                                    }
                                }
                                &self.downmix_buf[..]
                            };
                            if self.resampler.is_passthrough() {
                                self.raw_buffer.extend_from_slice(stereo_pcm);
                            } else {
                                self.resampler.process(stereo_pcm, &mut self.raw_buffer);
                            }
                        }
                        self.sample_buf = Some(buf);
                    }
                    Err(Error::IoError(ref e)) if e.kind() == ErrorKind::UnexpectedEof => {
                        break;
                    }
                    Err(Error::ResetRequired) => {
                        self.decoder.reset();
                        self.resampler.reset();
                        self.sample_buf = None;
                    }
                    Err(e) => return Err(format!("Decode error: {e}")),
                }
            }
            if self.raw_buffer.len() >= TARGET_FRAME_SAMPLES {
                let frame = self.raw_buffer.drain(0..TARGET_FRAME_SAMPLES).collect();
                Ok(Some(frame))
            } else if !self.raw_buffer.is_empty() {
                let mut frame = std::mem::take(&mut self.raw_buffer);
                frame.resize(TARGET_FRAME_SAMPLES, 0);
                Ok(Some(frame))
            } else {
                Ok(None)
            }
        }
        pub fn seek(&mut self, position_ms: u64) -> Result<(), String> {
            use symphonia::core::formats::{SeekMode, SeekTo};
            use symphonia::core::units::Time;
            let time = Time::from(position_ms as f64 / 1000.0);
            self.format
                .seek(
                    SeekMode::Coarse,
                    SeekTo::Time {
                        time,
                        track_id: Some(self.track_id),
                    },
                )
                .map_err(|e| format!("Symphonia seek error: {e}"))?;
            self.decoder.reset();
            self.resampler.reset();
            self.sample_buf = None;
            self.raw_buffer.clear();
            Ok(())
        }
    }
}
pub mod encoder {
    use audiopus::{Application, Bitrate, Channels, SampleRate, coder::Encoder as OpusEncoder};
    pub struct AudioEncoder {
        encoder: OpusEncoder,
    }
    impl AudioEncoder {
        pub fn new() -> Result<Self, String> {
            let mut encoder =
                OpusEncoder::new(SampleRate::Hz48000, Channels::Stereo, Application::Audio)
                    .map_err(|e| format!("Failed to create Opus encoder: {e:?}"))?;
            encoder
                .set_bitrate(Bitrate::Auto)
                .map_err(|e| format!("Failed to set Opus bitrate: {e:?}"))?;
            Ok(Self { encoder })
        }
        pub fn encode(&mut self, pcm: &[i16], out: &mut [u8]) -> Result<usize, String> {
            self.encoder
                .encode(pcm, out)
                .map_err(|e| format!("Opus encoding failed: {e:?}"))
        }
    }
}
pub mod resample {
    pub struct LinearResampler {
        ratio: f32,
        index: f32,
        last_samples: Vec<i16>,
        channels: usize,
    }
    impl LinearResampler {
        pub fn new(source_rate: u32, target_rate: u32, channels: usize) -> Self {
            Self {
                ratio: source_rate as f32 / target_rate as f32,
                index: 0.0,
                last_samples: vec![0; channels],
                channels,
            }
        }
        pub fn process(&mut self, input: &[i16], output: &mut Vec<i16>) {
            let num_frames = input.len() / self.channels;
            while self.index < num_frames as f32 {
                let idx = self.index as usize;
                let fract = self.index.fract();
                for c in 0..self.channels {
                    let s1 = if idx == 0 {
                        self.last_samples[c]
                    } else {
                        input[(idx - 1) * self.channels + c]
                    } as f32;
                    let s2 = if idx < num_frames {
                        input[idx * self.channels + c]
                    } else {
                        input[(num_frames - 1) * self.channels + c]
                    } as f32;
                    output.push((s1 * (1.0 - fract) + s2 * fract) as i16);
                }
                self.index += self.ratio;
            }
            self.index -= num_frames as f32;
            if num_frames > 0 {
                for c in 0..self.channels {
                    self.last_samples[c] = input[(num_frames - 1) * self.channels + c];
                }
            }
        }
        pub fn reset(&mut self) {
            self.index = 0.0;
            self.last_samples.fill(0);
        }
        pub fn is_passthrough(&self) -> bool {
            (self.ratio - 1.0).abs() < f32::EPSILON
        }
    }
}