unity_asset_binary/audio/
decoder.rs

1//! Audio decoder module
2//!
3//! This module provides audio decoding capabilities using Symphonia
4//! for various audio formats supported by Unity.
5
6use super::formats::AudioCompressionFormat;
7use super::types::{AudioClip, DecodedAudio};
8use crate::error::{BinaryError, Result};
9
10/// Main audio decoder
11///
12/// This struct provides methods for decoding various audio formats
13/// using the Symphonia audio library.
14pub struct AudioDecoder;
15
16impl AudioDecoder {
17    /// Create a new audio decoder
18    pub fn new() -> Self {
19        Self
20    }
21
22    /// Decode audio using Symphonia (supports many formats)
23    #[cfg(feature = "symphonia")]
24    pub fn decode(&self, clip: &AudioClip) -> Result<DecodedAudio> {
25        use std::io::Cursor;
26        use symphonia::core::audio::{AudioBufferRef, Signal};
27        use symphonia::core::codecs::{CODEC_TYPE_NULL, DecoderOptions};
28        use symphonia::core::errors::Error as SymphoniaError;
29        use symphonia::core::formats::FormatOptions;
30        use symphonia::core::io::MediaSourceStream;
31        use symphonia::core::meta::MetadataOptions;
32        use symphonia::core::probe::Hint;
33
34        if clip.data.is_empty() {
35            return Err(BinaryError::invalid_data("No audio data to decode"));
36        }
37
38        // Create a media source from the audio data
39        let cursor = Cursor::new(clip.data.clone());
40        let media_source = MediaSourceStream::new(Box::new(cursor), Default::default());
41
42        // Create a probe hint based on the compression format
43        let mut hint = Hint::new();
44        match clip.compression_format() {
45            AudioCompressionFormat::Vorbis => hint.with_extension("ogg"),
46            AudioCompressionFormat::MP3 => hint.with_extension("mp3"),
47            AudioCompressionFormat::AAC => hint.with_extension("aac"),
48            AudioCompressionFormat::PCM => hint.with_extension("wav"),
49            _ => &mut hint,
50        };
51
52        // Get the metadata and format readers
53        let meta_opts: MetadataOptions = Default::default();
54        let fmt_opts: FormatOptions = Default::default();
55
56        // Probe the media source
57        let probed = symphonia::default::get_probe()
58            .format(&hint, media_source, &fmt_opts, &meta_opts)
59            .map_err(|e| BinaryError::generic(format!("Failed to probe audio format: {}", e)))?;
60
61        // Get the instantiated format reader
62        let mut format = probed.format;
63
64        // Find the first audio track with a known (decodeable) codec
65        let track = format
66            .tracks()
67            .iter()
68            .find(|t| t.codec_params.codec != CODEC_TYPE_NULL)
69            .ok_or_else(|| BinaryError::generic("No supported audio tracks found"))?;
70
71        // Use the default options for the decoder
72        let dec_opts: DecoderOptions = Default::default();
73
74        // Create a decoder for the track
75        let mut decoder = symphonia::default::get_codecs()
76            .make(&track.codec_params, &dec_opts)
77            .map_err(|e| BinaryError::generic(format!("Failed to create decoder: {}", e)))?;
78
79        // Store the track identifier, it will be used to filter packets
80        let track_id = track.id;
81
82        let mut samples = Vec::new();
83        let mut sample_rate = 44100u32;
84        let mut channels = 2u32;
85
86        // The decode loop
87        loop {
88            // Get the next packet from the media format
89            let packet = match format.next_packet() {
90                Ok(packet) => packet,
91                Err(SymphoniaError::ResetRequired) => {
92                    // The track list has been changed. Re-examine it and create a new set of decoders,
93                    // then restart the decode loop. This is an advanced feature and it is not
94                    // unreasonable to consider this "the end of the stream". As of v0.5.0, the only
95                    // usage of this is for chained OGG physical streams.
96                    break;
97                }
98                Err(SymphoniaError::IoError(_)) => {
99                    // The packet reader has reached the end of the stream
100                    break;
101                }
102                Err(err) => {
103                    // A unrecoverable error occurred, halt decoding
104                    return Err(BinaryError::generic(format!("Decode error: {}", err)));
105                }
106            };
107
108            // Consume any new metadata that has been read since the last packet
109            while !format.metadata().is_latest() {
110                // Pop the latest metadata and consume it
111                format.metadata().pop();
112            }
113
114            // If the packet does not belong to the selected track, skip over it
115            if packet.track_id() != track_id {
116                continue;
117            }
118
119            // Decode the packet into an audio buffer
120            match decoder.decode(&packet) {
121                Ok(decoded) => {
122                    // Get audio buffer information
123                    let spec = *decoded.spec();
124                    sample_rate = spec.rate;
125                    channels = spec.channels.count() as u32;
126
127                    // Convert the audio buffer to f32 samples
128                    match decoded {
129                        AudioBufferRef::F32(buf) => {
130                            samples.extend_from_slice(buf.chan(0));
131                            if channels > 1 {
132                                for ch in 1..channels as usize {
133                                    if ch < buf.spec().channels.count() {
134                                        let channel_samples = buf.chan(ch);
135                                        // Interleave channels
136                                        for (i, &sample) in channel_samples.iter().enumerate() {
137                                            if i * channels as usize + ch < samples.len() {
138                                                samples.insert(i * channels as usize + ch, sample);
139                                            } else {
140                                                samples.push(sample);
141                                            }
142                                        }
143                                    }
144                                }
145                            }
146                        }
147                        AudioBufferRef::U8(buf) => {
148                            for ch in 0..channels as usize {
149                                if ch < buf.spec().channels.count() {
150                                    let channel_samples = buf.chan(ch);
151                                    for &sample in channel_samples {
152                                        let normalized = (sample as f32 - 128.0) / 128.0;
153                                        samples.push(normalized);
154                                    }
155                                }
156                            }
157                        }
158                        AudioBufferRef::U16(buf) => {
159                            for ch in 0..channels as usize {
160                                if ch < buf.spec().channels.count() {
161                                    let channel_samples = buf.chan(ch);
162                                    for &sample in channel_samples {
163                                        let normalized = (sample as f32 - 32768.0) / 32768.0;
164                                        samples.push(normalized);
165                                    }
166                                }
167                            }
168                        }
169                        AudioBufferRef::U32(buf) => {
170                            for ch in 0..channels as usize {
171                                if ch < buf.spec().channels.count() {
172                                    let channel_samples = buf.chan(ch);
173                                    for &sample in channel_samples {
174                                        let normalized =
175                                            (sample as f32 - 2147483648.0) / 2147483648.0;
176                                        samples.push(normalized);
177                                    }
178                                }
179                            }
180                        }
181                        AudioBufferRef::S8(buf) => {
182                            for ch in 0..channels as usize {
183                                if ch < buf.spec().channels.count() {
184                                    let channel_samples = buf.chan(ch);
185                                    for &sample in channel_samples {
186                                        let normalized = sample as f32 / 128.0;
187                                        samples.push(normalized);
188                                    }
189                                }
190                            }
191                        }
192                        AudioBufferRef::S16(buf) => {
193                            for ch in 0..channels as usize {
194                                if ch < buf.spec().channels.count() {
195                                    let channel_samples = buf.chan(ch);
196                                    for &sample in channel_samples {
197                                        let normalized = sample as f32 / 32768.0;
198                                        samples.push(normalized);
199                                    }
200                                }
201                            }
202                        }
203                        AudioBufferRef::S32(buf) => {
204                            for ch in 0..channels as usize {
205                                if ch < buf.spec().channels.count() {
206                                    let channel_samples = buf.chan(ch);
207                                    for &sample in channel_samples {
208                                        let normalized = sample as f32 / 2147483648.0;
209                                        samples.push(normalized);
210                                    }
211                                }
212                            }
213                        }
214                        AudioBufferRef::F64(buf) => {
215                            for ch in 0..channels as usize {
216                                if ch < buf.spec().channels.count() {
217                                    let channel_samples = buf.chan(ch);
218                                    for &sample in channel_samples {
219                                        samples.push(sample as f32);
220                                    }
221                                }
222                            }
223                        }
224                        AudioBufferRef::U24(buf) => {
225                            for ch in 0..channels as usize {
226                                if ch < buf.spec().channels.count() {
227                                    let channel_samples = buf.chan(ch);
228                                    for &sample in channel_samples {
229                                        let value = sample.inner() as i32;
230                                        let normalized = (value as f32 - 8388608.0) / 8388608.0;
231                                        samples.push(normalized);
232                                    }
233                                }
234                            }
235                        }
236                        AudioBufferRef::S24(buf) => {
237                            for ch in 0..channels as usize {
238                                if ch < buf.spec().channels.count() {
239                                    let channel_samples = buf.chan(ch);
240                                    for &sample in channel_samples {
241                                        let value = sample.inner();
242                                        let normalized = value as f32 / 8388608.0;
243                                        samples.push(normalized);
244                                    }
245                                }
246                            }
247                        }
248                    }
249                }
250                Err(SymphoniaError::IoError(_)) => {
251                    // The packet reader has reached the end of the stream
252                    break;
253                }
254                Err(SymphoniaError::DecodeError(_)) => {
255                    // Decode error, try to continue
256                    continue;
257                }
258                Err(err) => {
259                    // A unrecoverable error occurred, halt decoding
260                    return Err(BinaryError::generic(format!("Decode error: {}", err)));
261                }
262            }
263        }
264
265        if samples.is_empty() {
266            return Err(BinaryError::generic("No audio samples decoded"));
267        }
268
269        Ok(DecodedAudio::new(samples, sample_rate, channels))
270    }
271
272    /// Fallback decoder for when symphonia feature is not enabled
273    #[cfg(not(feature = "symphonia"))]
274    pub fn decode(&self, _clip: &AudioClip) -> Result<DecodedAudio> {
275        Err(BinaryError::unsupported(
276            "Audio decoding requires symphonia feature",
277        ))
278    }
279
280    /// Check if a format can be decoded
281    pub fn can_decode(&self, format: AudioCompressionFormat) -> bool {
282        #[cfg(feature = "symphonia")]
283        {
284            matches!(
285                format,
286                AudioCompressionFormat::PCM
287                    | AudioCompressionFormat::Vorbis
288                    | AudioCompressionFormat::MP3
289                    | AudioCompressionFormat::AAC
290                    | AudioCompressionFormat::ADPCM
291            )
292        }
293
294        #[cfg(not(feature = "symphonia"))]
295        {
296            false
297        }
298    }
299
300    /// Get list of supported formats
301    pub fn supported_formats(&self) -> Vec<AudioCompressionFormat> {
302        #[cfg(feature = "symphonia")]
303        {
304            vec![
305                AudioCompressionFormat::PCM,
306                AudioCompressionFormat::Vorbis,
307                AudioCompressionFormat::MP3,
308                AudioCompressionFormat::AAC,
309                AudioCompressionFormat::ADPCM,
310            ]
311        }
312
313        #[cfg(not(feature = "symphonia"))]
314        {
315            vec![]
316        }
317    }
318}
319
320impl Default for AudioDecoder {
321    fn default() -> Self {
322        Self::new()
323    }
324}