docs: add Phase 5-1 through 6-3 specs, plans, and Cargo.lock
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
163
docs/superpowers/specs/2026-03-25-phase6-2-3d-audio.md
Normal file
163
docs/superpowers/specs/2026-03-25-phase6-2-3d-audio.md
Normal file
@@ -0,0 +1,163 @@
|
||||
# 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<SpatialParams>, // NEW
|
||||
}
|
||||
```
|
||||
|
||||
### mix_sounds 변경
|
||||
|
||||
시그니처에 `listener: &Listener` 파라미터 추가:
|
||||
|
||||
```rust
|
||||
pub fn mix_sounds(
|
||||
output: &mut [f32],
|
||||
playing: &mut Vec<PlayingSound>,
|
||||
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
|
||||
Reference in New Issue
Block a user