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