1use std::{fs, sync::LazyLock};
2
3use symphonia::{
4 core::{
5 audio::{SampleBuffer, SignalSpec},
6 codecs::{CODEC_TYPE_NULL, CodecRegistry, Decoder, DecoderOptions},
7 errors::Error as SymphoniaError,
8 formats::{FormatOptions, FormatReader},
9 io::{MediaSourceStream, MediaSourceStreamOptions},
10 probe::Probe,
11 },
12 default::{
13 codecs::{FlacDecoder, MpaDecoder, PcmDecoder, VorbisDecoder},
14 formats::{FlacReader, MpaReader, OggReader, WavReader},
15 },
16};
17use thiserror::Error;
18
19use crate::media_container::{ContainerFormat, MediaContainer};
20
21pub static SYMPHONIA_CODEC_REGISTRY: LazyLock<CodecRegistry> = LazyLock::new(|| {
23 let mut codecs = CodecRegistry::new();
24 codecs.register_all::<FlacDecoder>();
25 codecs.register_all::<MpaDecoder>();
26 codecs.register_all::<VorbisDecoder>();
27 codecs.register_all::<PcmDecoder>();
28 codecs
29});
30
31pub static SYMPHONIA_PROBE: LazyLock<Probe> = LazyLock::new(|| {
32 let mut probe = Probe::default();
33 probe.register_all::<FlacReader>();
34 probe.register_all::<OggReader>();
35 probe.register_all::<MpaReader>();
36 probe.register_all::<WavReader>();
37 probe
38});
39
40#[derive(Debug, Error)]
41pub enum DecodingError {
42 #[error("{0}")]
43 Io(#[from] std::io::Error),
44
45 #[error("{0}")]
46 Symphonia(#[from] SymphoniaError),
47
48 #[error("Player attempted to play an unsupported container: '{0:?}'")]
49 UnsupportedContainer(ContainerFormat),
50}
51
52pub struct RawDecoder {
53 pub stream: symphonia::core::formats::Track,
54 pub format_reader: Box<dyn FormatReader>,
55 pub decoder: Box<dyn Decoder>,
56}
57
58pub struct DecodedPacket {
59 pub sample_buf: SampleBuffer<f32>,
60 pub frame_count: usize,
61 pub spec: SignalSpec,
62}
63
64impl RawDecoder {
65 pub fn from_container(
66 container: &MediaContainer,
67 buffer_len: usize,
68 ) -> Result<Self, DecodingError> {
69 let file = fs::File::open(container.path())?;
70
71 let options = MediaSourceStreamOptions { buffer_len };
72 let mss = MediaSourceStream::new(Box::new(file), options);
73
74 let fmt_opts = FormatOptions {
75 enable_gapless: true,
76 ..Default::default()
77 };
78
79 let format_reader: Box<dyn FormatReader> = match container.container() {
80 ContainerFormat::Flac => Box::new(FlacReader::try_new(mss, &fmt_opts)?),
81 ContainerFormat::Mpa => Box::new(MpaReader::try_new(mss, &fmt_opts)?),
82 ContainerFormat::Ogg => Box::new(OggReader::try_new(mss, &fmt_opts)?),
83 ContainerFormat::Wav => Box::new(WavReader::try_new(mss, &fmt_opts)?),
84 other => return Err(DecodingError::UnsupportedContainer(*other)),
85 };
86
87 let track = format_reader
88 .tracks()
89 .iter()
90 .find(|t| t.codec_params.codec != CODEC_TYPE_NULL)
91 .expect("Playable file does not have any supported codecs");
92 let dec_opts: DecoderOptions = Default::default();
93
94 let decoder = SYMPHONIA_CODEC_REGISTRY.make(&track.codec_params, &dec_opts)?;
95
96 Ok(Self {
97 stream: track.clone(),
98 format_reader,
99 decoder,
100 })
101 }
102
103 pub fn decode_next_packet(&mut self) -> Result<Option<DecodedPacket>, DecodingError> {
104 loop {
105 let packet = match self.format_reader.next_packet() {
106 Ok(packet) => packet,
107 Err(SymphoniaError::ResetRequired) => {
108 self.decoder.reset();
109 continue;
110 }
111 Err(SymphoniaError::IoError(err))
112 if matches!(err.kind(), std::io::ErrorKind::UnexpectedEof) =>
113 {
114 return Ok(None);
115 }
116 Err(err) => return Err(DecodingError::Symphonia(err)),
117 };
118
119 while !self.format_reader.metadata().is_latest() {
120 self.format_reader.metadata().pop();
121 }
122
123 if packet.track_id() != self.stream.id {
124 continue;
125 }
126
127 let decoded = match self.decoder.decode(&packet) {
128 Ok(decoded) => decoded,
129 Err(SymphoniaError::DecodeError(_)) => continue,
130 Err(SymphoniaError::IoError(_)) => continue,
131 Err(err) => return Err(DecodingError::Symphonia(err)),
132 };
133
134 let frame_count = decoded.frames();
135 let spec = *decoded.spec();
136
137 let mut sample_buf = SampleBuffer::<f32>::new(frame_count as u64, spec);
138 sample_buf.copy_interleaved_ref(decoded);
139
140 return Ok(Some(DecodedPacket {
141 sample_buf,
142 frame_count,
143 spec,
144 }));
145 }
146 }
147}