use super::formats::AudioCompressionFormat;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct StreamingInfo {
pub offset: u64,
pub size: u32,
pub path: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum AudioClipMeta {
Legacy {
format: i32,
type_: i32,
is_3d: bool,
use_hardware: bool,
},
Modern {
load_type: i32,
channels: i32,
frequency: i32,
bits_per_sample: i32,
length: f32,
is_tracker_format: bool,
subsound_index: i32,
preload_audio_data: bool,
load_in_background: bool,
legacy_3d: bool,
compression_format: AudioCompressionFormat,
},
}
impl Default for AudioClipMeta {
fn default() -> Self {
AudioClipMeta::Modern {
load_type: 0,
channels: 2,
frequency: 44100,
bits_per_sample: 16,
length: 0.0,
is_tracker_format: false,
subsound_index: 0,
preload_audio_data: true,
load_in_background: false,
legacy_3d: false,
compression_format: AudioCompressionFormat::PCM,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct AudioClip {
pub name: String,
pub meta: AudioClipMeta,
pub source: Option<String>,
pub offset: u64,
pub size: u64,
pub stream_info: StreamingInfo,
pub data: Vec<u8>,
pub ambisonic: Option<bool>,
}
impl AudioClip {
pub fn new(name: String, format: AudioCompressionFormat) -> Self {
Self {
name,
meta: AudioClipMeta::Modern {
compression_format: format,
load_type: 0,
channels: 2,
frequency: 44100,
bits_per_sample: 16,
length: 0.0,
is_tracker_format: false,
subsound_index: 0,
preload_audio_data: true,
load_in_background: false,
legacy_3d: false,
},
..Default::default()
}
}
pub fn compression_format(&self) -> AudioCompressionFormat {
match &self.meta {
AudioClipMeta::Legacy { .. } => AudioCompressionFormat::Unknown,
AudioClipMeta::Modern {
compression_format, ..
} => *compression_format,
}
}
pub fn properties(&self) -> AudioProperties {
match &self.meta {
AudioClipMeta::Legacy { .. } => AudioProperties {
channels: 2,
sample_rate: 44100,
bits_per_sample: 16,
length: 0.0,
},
AudioClipMeta::Modern {
channels,
frequency,
bits_per_sample,
length,
..
} => AudioProperties {
channels: *channels,
sample_rate: *frequency,
bits_per_sample: *bits_per_sample,
length: *length,
},
}
}
pub fn has_data(&self) -> bool {
!self.data.is_empty()
}
pub fn is_streamed(&self) -> bool {
!self.stream_info.path.is_empty() && self.stream_info.size > 0
}
pub fn info(&self) -> AudioInfo {
let format = self.compression_format();
let properties = self.properties();
AudioInfo {
name: self.name.clone(),
format,
format_info: format.info(),
properties,
has_data: self.has_data(),
is_streamed: self.is_streamed(),
data_size: self.data.len(),
}
}
pub fn validate(&self) -> Result<(), String> {
if self.name.is_empty() {
return Err("AudioClip name cannot be empty".to_string());
}
let properties = self.properties();
if properties.channels <= 0 {
return Err("Invalid channel count".to_string());
}
if properties.sample_rate <= 0 {
return Err("Invalid sample rate".to_string());
}
if !self.has_data() && !self.is_streamed() {
return Err("No audio data available and not streamed".to_string());
}
Ok(())
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AudioProperties {
pub channels: i32,
pub sample_rate: i32,
pub bits_per_sample: i32,
pub length: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AudioInfo {
pub name: String,
pub format: AudioCompressionFormat,
pub format_info: super::formats::AudioFormatInfo,
pub properties: AudioProperties,
pub has_data: bool,
pub is_streamed: bool,
pub data_size: usize,
}
#[derive(Debug, Clone)]
pub struct DecodedAudio {
pub samples: Vec<f32>,
pub sample_rate: u32,
pub channels: u32,
pub duration: f32,
}
impl DecodedAudio {
pub fn new(samples: Vec<f32>, sample_rate: u32, channels: u32) -> Self {
let duration = samples.len() as f32 / (sample_rate * channels) as f32;
Self {
samples,
sample_rate,
channels,
duration,
}
}
pub fn sample_count(&self) -> usize {
self.samples.len()
}
pub fn frame_count(&self) -> usize {
self.samples.len() / self.channels as usize
}
pub fn to_i16_samples(&self) -> Vec<i16> {
self.samples
.iter()
.map(|&sample| (sample.clamp(-1.0, 1.0) * i16::MAX as f32) as i16)
.collect()
}
pub fn to_i32_samples(&self) -> Vec<i32> {
self.samples
.iter()
.map(|&sample| (sample.clamp(-1.0, 1.0) * i32::MAX as f32) as i32)
.collect()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AudioAnalysis {
pub duration: f32,
pub sample_rate: u32,
pub channels: u32,
pub bit_depth: u32,
pub format: AudioCompressionFormat,
pub file_size: usize,
pub peak_amplitude: f32,
pub rms_amplitude: f32,
}
impl AudioAnalysis {
pub fn from_decoded(
decoded: &DecodedAudio,
format: AudioCompressionFormat,
file_size: usize,
) -> Self {
let peak_amplitude = decoded
.samples
.iter()
.map(|&s| s.abs())
.fold(0.0f32, f32::max);
let rms_amplitude = if !decoded.samples.is_empty() {
let sum_squares: f32 = decoded.samples.iter().map(|&s| s * s).sum();
(sum_squares / decoded.samples.len() as f32).sqrt()
} else {
0.0
};
Self {
duration: decoded.duration,
sample_rate: decoded.sample_rate,
channels: decoded.channels,
bit_depth: 32, format,
file_size,
peak_amplitude,
rms_amplitude,
}
}
}