feat(audio): add voltex_audio crate with AudioClip type

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-25 10:59:10 +09:00
parent 75ec3b308f
commit dc12715279
4 changed files with 70 additions and 0 deletions

View File

@@ -16,6 +16,7 @@ members = [
"examples/shadow_demo",
"examples/ibl_demo",
"crates/voltex_physics",
"crates/voltex_audio",
]
[workspace.dependencies]
@@ -25,6 +26,7 @@ voltex_renderer = { path = "crates/voltex_renderer" }
voltex_ecs = { path = "crates/voltex_ecs" }
voltex_asset = { path = "crates/voltex_asset" }
voltex_physics = { path = "crates/voltex_physics" }
voltex_audio = { path = "crates/voltex_audio" }
wgpu = "28.0"
winit = "0.30"
bytemuck = { version = "1", features = ["derive"] }

View File

@@ -0,0 +1,6 @@
[package]
name = "voltex_audio"
version = "0.1.0"
edition = "2021"
[dependencies]

View File

@@ -0,0 +1,55 @@
/// A loaded audio clip stored as normalized f32 PCM samples.
#[derive(Debug)]
pub struct AudioClip {
/// Interleaved PCM samples in the range [-1.0, 1.0].
pub samples: Vec<f32>,
/// Number of samples per second (e.g. 44100).
pub sample_rate: u32,
/// Number of audio channels (1 = mono, 2 = stereo).
pub channels: u16,
}
impl AudioClip {
/// Create a new AudioClip from raw f32 samples.
pub fn new(samples: Vec<f32>, sample_rate: u32, channels: u16) -> Self {
Self {
samples,
sample_rate,
channels,
}
}
/// Total number of multi-channel frames.
/// A frame contains one sample per channel.
pub fn frame_count(&self) -> usize {
self.samples.len() / self.channels as usize
}
/// Duration in seconds.
pub fn duration(&self) -> f32 {
self.frame_count() as f32 / self.sample_rate as f32
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn mono_clip() {
// 44100 Hz, 1 channel, 1 second of silence
let samples = vec![0.0f32; 44100];
let clip = AudioClip::new(samples, 44100, 1);
assert_eq!(clip.frame_count(), 44100);
assert!((clip.duration() - 1.0).abs() < 1e-6);
}
#[test]
fn stereo_clip() {
// 44100 Hz, 2 channels, 0.5 seconds
let samples = vec![0.0f32; 44100]; // 44100 interleaved samples = 22050 frames
let clip = AudioClip::new(samples, 44100, 2);
assert_eq!(clip.frame_count(), 22050);
assert!((clip.duration() - 0.5).abs() < 1e-6);
}
}

View File

@@ -0,0 +1,7 @@
pub mod audio_clip;
pub mod wav;
pub mod mixing;
pub use audio_clip::AudioClip;
pub use wav::{parse_wav, generate_wav_bytes};
pub use mixing::{PlayingSound, mix_sounds};