Skip to content

forge3d.audio — 3D Spatial Audio

forge3d's audio system provides 3D positional sound with automatic fallback to a no-op driver in headless / CI environments.

Dependency

Real audio output requires OpenAL (pip install openal-soft or system package). In environments without audio hardware, AudioSystem automatically uses NullDriver — all API calls succeed silently.


Classes

AudioClip dataclass

PCM 오디오 데이터를 담는 불변 데이터 클래스.

load classmethod

load(path: str | Path) -> AudioClip

WAV/OGG/FLAC 파일을 float32 PCM 배열로 로드한다.

soundfile이 없으면 순수 Python WAV 파서(stdlib)로 폴백.

from_sine classmethod

from_sine(freq: float = 440.0, duration: float = 0.5, sample_rate: int = 44100, name: str = 'sine') -> AudioClip

테스트용: 사인파 AudioClip 생성.

to_pcm16

to_pcm16() -> bytes

16-bit PCM bytes 변환 (OpenAL AL_FORMAT_MONO16 등).

AudioSource dataclass

Bases: Component

3D 공간에서 소리를 발생시키는 ECS 컴포넌트.

AudioListener dataclass

Bases: Component

월드에서 소리를 듣는 청취 위치 ECS 컴포넌트.

보통 카메라 엔티티에 부착한다.

AudioSystem

Bases: System

오디오 ECS 시스템.

  • 매 프레임 AudioSource 엔티티를 순회해 auto_play 클립을 재생
  • AudioListener 위치를 드라이버에 업데이트
  • play_at() 으로 임의 위치에서 1회성 사운드 재생 지원

play

play(clip: AudioClip, volume: float = 1.0, loop: bool = False) -> None

위치 없이 2D 사운드 재생 (BGM 등).

play_at

play_at(clip: AudioClip, position: ndarray, volume: float = 1.0, pitch: float = 1.0) -> None

3D 공간 위치에서 1회성 사운드 재생.

make_collision_handler

make_collision_handler(clip: AudioClip, max_volume: float = 1.0, impulse_scale: float = 10.0) -> Any

v1 World.on_collision_begin() 에 등록할 콜백 팩토리.


Usage examples

Load and play a sound

from forge3d import AudioSystem, AudioClip, AudioSource, AudioListener

audio = AudioSystem()

# Load audio file (WAV or OGG)
clip = AudioClip.load("assets/sounds/thud.wav")

# Create a 3D source at a world position
source = AudioSource(position=(3, 0, 1), volume=0.8, loop=False)
audio.play(source, clip)

Attach to physics body

import forge3d as f3d

world = f3d.World()
box = world.add_box(size=(1, 1, 1), position=(0, 0, 5), mass=1.0)

audio = f3d.AudioSystem()
impact_clip = f3d.AudioClip.load("impact.wav")

@world.on_collision_begin
def on_hit(event: f3d.CollisionEvent) -> None:
    if event.relative_speed > 2.0:
        source = f3d.AudioSource(
            position=tuple(event.body_a.position),
            volume=min(1.0, event.relative_speed / 10.0),
        )
        audio.play(source, impact_clip)

3D listener (camera-relative)

# Move the listener to follow the camera each frame
listener = f3d.AudioListener()
cam_pos = np.array([4.0, -7.0, 3.0])
listener.position = cam_pos
listener.forward = np.array([0.0, 1.0, 0.0])   # direction camera looks
audio.set_listener(listener)

Background music

music = AudioClip.load("assets/music/ambient.ogg")
bgm_source = AudioSource(
    position=(0, 0, 0),   # world-space but audible everywhere
    volume=0.4,
    loop=True,
)
audio.play(bgm_source, music)

AudioSystem integration with App

import forge3d as f3d

app = f3d.App("Sound Demo")
audio = f3d.AudioSystem()

@app.on_start
def setup(world: f3d.World) -> None:
    bgm = f3d.AudioClip.load("music.ogg")
    audio.play(f3d.AudioSource(loop=True, volume=0.5), bgm)

@app.on_update
def update(world: f3d.World, dt: float, inp: f3d.Input) -> None:
    audio.update(dt)   # advance source positions

app.run()