unity_asset_decode/audio/
types.rs1use super::formats::AudioCompressionFormat;
6use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, Default, Serialize, Deserialize)]
10pub struct StreamingInfo {
11 pub offset: u64,
12 pub size: u32,
13 pub path: String,
14}
15
16#[derive(Debug, Clone, Serialize, Deserialize)]
18pub enum AudioClipMeta {
19 Legacy {
21 format: i32,
22 type_: i32,
23 is_3d: bool,
24 use_hardware: bool,
25 },
26 Modern {
28 load_type: i32,
29 channels: i32,
30 frequency: i32,
31 bits_per_sample: i32,
32 length: f32,
33 is_tracker_format: bool,
34 subsound_index: i32,
35 preload_audio_data: bool,
36 load_in_background: bool,
37 legacy_3d: bool,
38 compression_format: AudioCompressionFormat,
39 },
40}
41
42impl Default for AudioClipMeta {
43 fn default() -> Self {
44 AudioClipMeta::Modern {
45 load_type: 0,
46 channels: 2,
47 frequency: 44100,
48 bits_per_sample: 16,
49 length: 0.0,
50 is_tracker_format: false,
51 subsound_index: 0,
52 preload_audio_data: true,
53 load_in_background: false,
54 legacy_3d: false,
55 compression_format: AudioCompressionFormat::PCM,
56 }
57 }
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize, Default)]
65pub struct AudioClip {
66 pub name: String,
67 pub meta: AudioClipMeta,
68 pub source: Option<String>,
69 pub offset: u64,
70 pub size: u64,
71 pub stream_info: StreamingInfo,
72 pub data: Vec<u8>,
73
74 pub ambisonic: Option<bool>,
76}
77
78impl AudioClip {
79 pub fn new(name: String, format: AudioCompressionFormat) -> Self {
81 Self {
82 name,
83 meta: AudioClipMeta::Modern {
84 compression_format: format,
85 load_type: 0,
86 channels: 2,
87 frequency: 44100,
88 bits_per_sample: 16,
89 length: 0.0,
90 is_tracker_format: false,
91 subsound_index: 0,
92 preload_audio_data: true,
93 load_in_background: false,
94 legacy_3d: false,
95 },
96 ..Default::default()
97 }
98 }
99
100 pub fn compression_format(&self) -> AudioCompressionFormat {
102 match &self.meta {
103 AudioClipMeta::Legacy { .. } => AudioCompressionFormat::Unknown,
104 AudioClipMeta::Modern {
105 compression_format, ..
106 } => *compression_format,
107 }
108 }
109
110 pub fn properties(&self) -> AudioProperties {
112 match &self.meta {
113 AudioClipMeta::Legacy { .. } => AudioProperties {
114 channels: 2,
115 sample_rate: 44100,
116 bits_per_sample: 16,
117 length: 0.0,
118 },
119 AudioClipMeta::Modern {
120 channels,
121 frequency,
122 bits_per_sample,
123 length,
124 ..
125 } => AudioProperties {
126 channels: *channels,
127 sample_rate: *frequency,
128 bits_per_sample: *bits_per_sample,
129 length: *length,
130 },
131 }
132 }
133
134 pub fn has_data(&self) -> bool {
136 !self.data.is_empty()
137 }
138
139 pub fn is_streamed(&self) -> bool {
141 !self.stream_info.path.is_empty() && self.stream_info.size > 0
142 }
143
144 pub fn info(&self) -> AudioInfo {
146 let format = self.compression_format();
147 let properties = self.properties();
148
149 AudioInfo {
150 name: self.name.clone(),
151 format,
152 format_info: format.info(),
153 properties,
154 has_data: self.has_data(),
155 is_streamed: self.is_streamed(),
156 data_size: self.data.len(),
157 }
158 }
159
160 pub fn validate(&self) -> Result<(), String> {
162 if self.name.is_empty() {
163 return Err("AudioClip name cannot be empty".to_string());
164 }
165
166 let properties = self.properties();
167 if properties.channels <= 0 {
168 return Err("Invalid channel count".to_string());
169 }
170
171 if properties.sample_rate <= 0 {
172 return Err("Invalid sample rate".to_string());
173 }
174
175 if !self.has_data() && !self.is_streamed() {
176 return Err("No audio data available and not streamed".to_string());
177 }
178
179 Ok(())
180 }
181}
182
183#[derive(Debug, Clone, Serialize, Deserialize)]
185pub struct AudioProperties {
186 pub channels: i32,
187 pub sample_rate: i32,
188 pub bits_per_sample: i32,
189 pub length: f32,
190}
191
192#[derive(Debug, Clone, Serialize, Deserialize)]
194pub struct AudioInfo {
195 pub name: String,
196 pub format: AudioCompressionFormat,
197 pub format_info: super::formats::AudioFormatInfo,
198 pub properties: AudioProperties,
199 pub has_data: bool,
200 pub is_streamed: bool,
201 pub data_size: usize,
202}
203
204#[derive(Debug, Clone)]
206pub struct DecodedAudio {
207 pub samples: Vec<f32>,
208 pub sample_rate: u32,
209 pub channels: u32,
210 pub duration: f32,
211}
212
213impl DecodedAudio {
214 pub fn new(samples: Vec<f32>, sample_rate: u32, channels: u32) -> Self {
216 let duration = samples.len() as f32 / (sample_rate * channels) as f32;
217 Self {
218 samples,
219 sample_rate,
220 channels,
221 duration,
222 }
223 }
224
225 pub fn sample_count(&self) -> usize {
227 self.samples.len()
228 }
229
230 pub fn frame_count(&self) -> usize {
232 self.samples.len() / self.channels as usize
233 }
234
235 pub fn to_i16_samples(&self) -> Vec<i16> {
237 self.samples
238 .iter()
239 .map(|&sample| (sample.clamp(-1.0, 1.0) * i16::MAX as f32) as i16)
240 .collect()
241 }
242
243 pub fn to_i32_samples(&self) -> Vec<i32> {
245 self.samples
246 .iter()
247 .map(|&sample| (sample.clamp(-1.0, 1.0) * i32::MAX as f32) as i32)
248 .collect()
249 }
250}
251
252#[derive(Debug, Clone, Serialize, Deserialize)]
254pub struct AudioAnalysis {
255 pub duration: f32,
256 pub sample_rate: u32,
257 pub channels: u32,
258 pub bit_depth: u32,
259 pub format: AudioCompressionFormat,
260 pub file_size: usize,
261 pub peak_amplitude: f32,
262 pub rms_amplitude: f32,
263}
264
265impl AudioAnalysis {
266 pub fn from_decoded(
268 decoded: &DecodedAudio,
269 format: AudioCompressionFormat,
270 file_size: usize,
271 ) -> Self {
272 let peak_amplitude = decoded
273 .samples
274 .iter()
275 .map(|&s| s.abs())
276 .fold(0.0f32, f32::max);
277
278 let rms_amplitude = if !decoded.samples.is_empty() {
279 let sum_squares: f32 = decoded.samples.iter().map(|&s| s * s).sum();
280 (sum_squares / decoded.samples.len() as f32).sqrt()
281 } else {
282 0.0
283 };
284
285 Self {
286 duration: decoded.duration,
287 sample_rate: decoded.sample_rate,
288 channels: decoded.channels,
289 bit_depth: 32, format,
291 file_size,
292 peak_amplitude,
293 rms_amplitude,
294 }
295 }
296}