kira 0.12.2

Expressive audio library for games
Documentation
use std::convert::TryInto;

use crate::{
	frame::Frame,
	sound::{FromFileError, symphonia::load_frames_from_buffer_ref},
};
use symphonia::core::{
	codecs::{CodecParameters, audio::AudioDecoder},
	formats::{FormatReader, SeekMode, SeekTo, TrackType, probe::Hint},
	io::{MediaSource, MediaSourceStream},
	units::Timestamp,
};

pub(crate) struct SymphoniaDecoder {
	format_reader: Box<dyn FormatReader>,
	decoder: Box<dyn AudioDecoder>,
	sample_rate: u32,
	num_frames: usize,
	track_id: u32,
}

impl SymphoniaDecoder {
	pub(crate) fn new(media_source: Box<dyn MediaSource>) -> Result<Self, FromFileError> {
		let codecs = symphonia::default::get_codecs();
		let probe = symphonia::default::get_probe();
		let mss = MediaSourceStream::new(media_source, Default::default());
		let format_reader = probe.probe(
			&Hint::default(),
			mss,
			Default::default(),
			Default::default(),
		)?;
		let default_track = format_reader
			.default_track(TrackType::Audio)
			.ok_or(FromFileError::NoDefaultTrack)?;
		let audio_params = match default_track.codec_params.as_ref() {
			Some(CodecParameters::Audio(p)) => p,
			_ => return Err(FromFileError::NoDefaultTrack),
		};
		let sample_rate = audio_params
			.sample_rate
			.ok_or(FromFileError::UnknownSampleRate)?;
		let num_frames = default_track
			.num_frames
			.ok_or(FromFileError::UnknownSampleRate)?
			.try_into()
			.expect("could not convert u64 into usize");
		let decoder = codecs.make_audio_decoder(audio_params, &Default::default())?;
		let track_id = default_track.id;
		Ok(Self {
			format_reader,
			decoder,
			sample_rate,
			num_frames,
			track_id,
		})
	}
}

impl super::Decoder for SymphoniaDecoder {
	type Error = FromFileError;

	fn sample_rate(&self) -> u32 {
		self.sample_rate
	}

	fn num_frames(&self) -> usize {
		self.num_frames
	}

	fn decode(&mut self) -> Result<Vec<Frame>, Self::Error> {
		let packet = loop {
			match self.format_reader.next_packet()? {
				Some(packet) if packet.track_id == self.track_id => break packet,
				Some(_) => continue,
				None => return Ok(vec![]),
			}
		};
		let buffer = self.decoder.decode(&packet)?;
		load_frames_from_buffer_ref(&buffer)
	}

	fn seek(&mut self, index: usize) -> Result<usize, Self::Error> {
		let seeked_to = self.format_reader.seek(
			SeekMode::Accurate,
			SeekTo::Timestamp {
				ts: Timestamp::new(index as i64),
				track_id: self.track_id,
			},
		)?;
		let actual = seeked_to.actual_ts.get();
		Ok(if actual < 0 {
			0
		} else {
			(actual as usize).min(self.num_frames)
		})
	}
}