synapto-plugin-linux-host-audio 0.1.0-dev.1

Linux host audio capture and playback plugin for the Synapto framework
use ogg::PacketReader;
use opus::{Channels, Decoder};
use std::error::Error;
use std::io::Cursor;
use tracing::instrument;

#[derive(Debug)]
#[allow(dead_code)]
pub(crate) struct DecodedAudio {
    pub samples: Vec<f32>,
    pub sample_rate: u32,
    pub channels: u32,
}

#[instrument(name = "Audio Decoding", level = "debug", skip_all)]
pub(crate) fn decode_opus_from_memory(
    input_data: &[u8],
) -> Result<DecodedAudio, Box<dyn Error + Send + Sync>> {
    let cursor = Cursor::new(input_data);
    let mut packet_reader = PacketReader::new(cursor);

    let sample_rate = 48000;

    let mut decoder: Option<Decoder> = None;
    let mut all_samples = Vec::new();
    let mut channel_count: usize = 0;

    while let Some(packet) = packet_reader.read_packet()? {
        let data = packet.data;

        if decoder.is_none() {
            if data.len() > 8 && &data[0..8] == b"OpusHead" {
                let c = data[9];
                channel_count = c as usize;

                let channels = match c {
                    1 => Channels::Mono,
                    2 => Channels::Stereo,
                    _ => {
                        return Err("Unsupported channel count (only Mono/Stereo supported)".into());
                    }
                };

                decoder = Some(Decoder::new(sample_rate, channels)?);
            }
            continue;
        }

        if data.len() > 8 && &data[0..8] == b"OpusTags" {
            continue;
        }

        if let Some(dec) = decoder.as_mut() {
            let mut output_buffer = [0.0f32; 5760 * 2];

            match dec.decode_float(&data, &mut output_buffer, false) {
                Ok(samples_per_channel) => {
                    let total_samples = samples_per_channel * channel_count;
                    all_samples.extend_from_slice(&output_buffer[..total_samples]);
                }
                Err(e) => {
                    tracing::debug!("Skipping corrupt audio packet: {}", e);
                }
            }
        }
    }

    if all_samples.is_empty() {
        return Err("No audio data could be decoded".into());
    }

    Ok(DecodedAudio {
        samples: all_samples,
        sample_rate,
        channels: channel_count as u32,
    })
}