# Phase 6-2: 3D Audio — Design Spec ## Overview `voltex_audio`에 3D 공간 오디오를 추가한다. 거리 기반 감쇠와 스테레오 패닝으로 소리의 공간감을 표현한다. ## Scope - Listener (위치, 방향) - SpatialParams (이미터 위치, min/max 거리) - 거리 감쇠 (inverse distance) - 스테레오 패닝 (equal-power) - play_3d, set_listener API ## Out of Scope - 도플러 효과 - HRTF - Reverb/Echo - 다중 리스너 ## Module Structure ### voltex_audio (수정/추가) - `crates/voltex_audio/src/spatial.rs` — Listener, SpatialParams, 감쇠/패닝 순수 함수 (Create) - `crates/voltex_audio/src/mixing.rs` — PlayingSound에 spatial 추가, mix에 spatial 적용 (Modify) - `crates/voltex_audio/src/audio_system.rs` — Play3d, SetListener 명령 추가 (Modify) - `crates/voltex_audio/src/lib.rs` — spatial 모듈 등록 (Modify) ## Dependencies - `voltex_math` — Vec3 (위치, 방향 계산) - `crates/voltex_audio/Cargo.toml`에 voltex_math 의존 추가 ## Types ### Listener ```rust #[derive(Debug, Clone, Copy)] pub struct Listener { pub position: Vec3, pub forward: Vec3, pub right: Vec3, } ``` - `default()` → position=ZERO, forward=-Z, right=X ### SpatialParams ```rust #[derive(Debug, Clone, Copy)] pub struct SpatialParams { pub position: Vec3, pub min_distance: f32, pub max_distance: f32, } ``` - `new(position, min_distance, max_distance)` — 생성 - `at(position)` — min=1.0, max=50.0 기본값으로 간편 생성 ## Functions (spatial.rs) ### distance_attenuation ```rust pub fn distance_attenuation(distance: f32, min_dist: f32, max_dist: f32) -> f32 ``` - distance <= min_dist → 1.0 - distance >= max_dist → 0.0 - 사이: `min_dist / distance` (inverse distance, clamped) ### stereo_pan ```rust pub fn stereo_pan(listener: &Listener, emitter_pos: Vec3) -> (f32, f32) ``` 1. direction = (emitter_pos - listener.position).normalize() 2. pan = direction.dot(listener.right) — -1.0(왼쪽) ~ 1.0(오른쪽) 3. Equal-power panning: - left_gain = cos(pan * PI/4 + PI/4) — pan=-1일 때 1.0, pan=1일 때 0.0 - right_gain = sin(pan * PI/4 + PI/4) — pan=-1일 때 0.0, pan=1일 때 1.0 4. 이미터가 리스너 위치와 동일하면 (1.0, 1.0) 반환 ### compute_spatial_gains ```rust pub fn compute_spatial_gains(listener: &Listener, spatial: &SpatialParams) -> (f32, f32, f32) ``` Returns (attenuation, left_gain, right_gain). 편의 함수: distance_attenuation + stereo_pan 결합. ## Mixing Integration ### PlayingSound 변경 ```rust pub struct PlayingSound { pub clip_index: usize, pub position: usize, pub volume: f32, pub looping: bool, pub spatial: Option, // NEW } ``` ### mix_sounds 변경 시그니처에 `listener: &Listener` 파라미터 추가: ```rust pub fn mix_sounds( output: &mut [f32], playing: &mut Vec, clips: &[AudioClip], device_sample_rate: u32, device_channels: u16, frames: usize, listener: &Listener, // NEW ) ``` spatial이 Some인 사운드: 1. compute_spatial_gains로 (attenuation, left, right) 계산 2. 볼륨에 attenuation 곱하기 3. 스테레오 출력에 left/right gain 적용 spatial이 None인 사운드 (2D): - 기존 동작 유지 (전체 볼륨 균등) ## AudioSystem API 변경 ### 새 명령 ```rust AudioCommand::Play3d { clip_index, volume, looping, spatial: SpatialParams }, AudioCommand::SetListener { position: Vec3, forward: Vec3, right: Vec3 }, ``` ### 새 메서드 ```rust pub fn play_3d(&self, clip_index: usize, volume: f32, looping: bool, spatial: SpatialParams) pub fn set_listener(&self, position: Vec3, forward: Vec3, right: Vec3) ``` ## Test Plan ### spatial.rs - distance_attenuation: at min_dist (1.0), at max_dist (0.0), between, zero distance - stereo_pan: right side → right gain > left, left side → left gain > right, front → roughly equal, same position → (1.0, 1.0) - compute_spatial_gains: combines both correctly ### mixing.rs (spatial integration) - 2D sound unchanged (spatial=None, listener 무관) - 3D sound at max_distance → near-silent output - 3D sound at min_distance → full volume - 3D sound on right → right channel louder