selene-core 0.5.0

selene-core is the backend for Selene, a local-first music player
Documentation
use std::fs;

use symphonia::{
    core::{
        audio::{GenericAudioBuffer, GenericAudioBufferRef},
        codecs::audio::{AudioDecoder, AudioDecoderOptions},
        errors::Error as SymphoniaError,
        formats::{FormatOptions, FormatReader, TrackType},
        io::{MediaSourceStream, MediaSourceStreamOptions},
    },
    default::formats::{FlacReader, MpaReader, OggReader, WavReader},
};

use thiserror::Error;

use crate::{
    errors::ContainerError,
    media_container::{ContainerFormat, MediaContainer, Stream},
};

#[derive(Debug, Error)]
pub enum DecodingError {
    #[error("{0}")]
    Io(#[from] std::io::Error),

    #[error("{0}")]
    Symphonia(#[from] SymphoniaError),

    #[error("{0}")]
    Container(#[from] ContainerError),

    #[error("Player attempted to play an unsupported container: '{0:?}'")]
    UnsupportedContainer(ContainerFormat),
}

pub struct RawDecoder {
    pub stream: Stream,
    pub format_reader: Box<dyn FormatReader>,
    pub decoder: Box<dyn AudioDecoder>,
}

impl RawDecoder {
    pub fn from_container(
        container: &MediaContainer,
        buffer_len: usize,
    ) -> Result<Self, DecodingError> {
        let file = fs::File::open(container.path())?;

        let options = MediaSourceStreamOptions { buffer_len };
        let mss = MediaSourceStream::new(Box::new(file), options);

        let fmt_opts = FormatOptions::default();
        let format_reader: Box<dyn FormatReader> = match container.format() {
            ContainerFormat::Flac => Box::new(FlacReader::try_new(mss, fmt_opts)?),
            ContainerFormat::Mpa => Box::new(MpaReader::try_new(mss, fmt_opts)?),
            ContainerFormat::Ogg => Box::new(OggReader::try_new(mss, fmt_opts)?),
            ContainerFormat::Wav => Box::new(WavReader::try_new(mss, fmt_opts)?),
            other => return Err(DecodingError::UnsupportedContainer(*other)),
        };

        let track = format_reader
            .default_track(TrackType::Audio)
            .expect("Playable file does not have any supported codecs");

        let dec_opts: AudioDecoderOptions = Default::default();

        let params = track
            .codec_params
            .as_ref()
            .expect("Track is not playable")
            .audio()
            .unwrap();
        let decoder = symphonia::default::get_codecs().make_audio_decoder(params, &dec_opts)?;

        Ok(Self {
            stream: Stream::try_from(track)?,
            format_reader,
            decoder,
        })
    }

    pub fn decode_next_packet(&mut self) -> Result<Option<GenericAudioBuffer>, DecodingError> {
        loop {
            let packet = match self.format_reader.next_packet() {
                Ok(Some(packet)) => packet,
                Err(SymphoniaError::ResetRequired) => {
                    self.decoder.reset();
                    continue;
                }
                Ok(None) => return Ok(None),
                Err(err) => return Err(DecodingError::Symphonia(err)),
            };

            while !self.format_reader.metadata().is_latest() {
                self.format_reader.metadata().pop();
            }

            if packet.track_id() != self.stream.id {
                continue;
            }

            let decoded = match self.decoder.decode(&packet) {
                Ok(decoded) => decoded,
                Err(SymphoniaError::DecodeError(_)) => continue,
                Err(SymphoniaError::IoError(_)) => continue,
                Err(err) => return Err(DecodingError::Symphonia(err)),
            };

            let audio_buf = match decoded {
                GenericAudioBufferRef::U8(audio_buffer) => {
                    GenericAudioBuffer::U8(audio_buffer.clone())
                }
                GenericAudioBufferRef::U16(audio_buffer) => {
                    GenericAudioBuffer::U16(audio_buffer.clone())
                }
                GenericAudioBufferRef::U24(audio_buffer) => {
                    GenericAudioBuffer::U24(audio_buffer.clone())
                }
                GenericAudioBufferRef::U32(audio_buffer) => {
                    GenericAudioBuffer::U32(audio_buffer.clone())
                }
                GenericAudioBufferRef::S8(audio_buffer) => {
                    GenericAudioBuffer::S8(audio_buffer.clone())
                }
                GenericAudioBufferRef::S16(audio_buffer) => {
                    GenericAudioBuffer::S16(audio_buffer.clone())
                }
                GenericAudioBufferRef::S24(audio_buffer) => {
                    GenericAudioBuffer::S24(audio_buffer.clone())
                }
                GenericAudioBufferRef::S32(audio_buffer) => {
                    GenericAudioBuffer::S32(audio_buffer.clone())
                }
                GenericAudioBufferRef::F32(audio_buffer) => {
                    GenericAudioBuffer::F32(audio_buffer.clone())
                }
                GenericAudioBufferRef::F64(audio_buffer) => {
                    GenericAudioBuffer::F64(audio_buffer.clone())
                }
            };

            return Ok(Some(audio_buf));
        }
    }
}