ITADN
Far-Beyond-Pulsar/Quasar · 文件
文件最后提交记录最后更新时间
README.md

Gemini_Generated_Image_zaovfjzaovfjzaov

Quasar — WIP Spatial Audio Engine

A high-performance, ultra-modular spatial audio engine designed for deep integration into custom game engine ecosystems. Quasar bridges offline acoustic baking with a flexible real-time spatial computation pipeline, enabling everything from static baked environments to fully dynamic real-time acoustics.

image

https://github.com/user-attachments/assets/b06229b1-589d-4fb2-ba1e-74ac4bad6793

Applications: game engine spatial audio, virtual production sound design, interactive installation acoustics, architectural acoustic simulation, VR/AR audio pipelines, real-time reverberation for music performance software, multi-speaker exhibit audio routing, and any system needing lock-free channel-level routing between arbitrary sources and physical speaker layouts.


Core Concept

Quasar decouples three concerns through a channel-pulling architecture:

  Source (multi-channel audio)
     │
     │ ChannelPull (source_id, channel, gain_db)
     ▼
  SceneOutput (positioned world emitter)
     │
     │ SpatialQuery (source_position → listener_position)
     ▼
  Listener (world position + heading + physical device layout)
     │
     ▼
  VBAP decode onto physical speakers / HRTF
  • Sources are raw multi-channel audio files or streams (mono, stereo, 7.1.4, etc.)
  • SceneOutputs are positioned world-space emitters whose audible content is the sum of explicit ChannelPull taps onto loaded sources
  • Listeners have a world position, heading, and a physical output layout (Stereo, 5.1, 7.1.4, Quad, Custom, HRTF)

Audio flows from sources through the patch bay into scene outputs, then through occlusion / early reflections / late reverb, and finally VBAP-decoded onto each listener's physical speaker layout — all zero-allocation on the audio thread.


Crate Layout

Quasar/
├── Cargo.toml                    # Workspace root
├── crates/
│   ├── quasar/                   # Facade crate (re-exports everything + SpatialAudioEngine)
│   ├── quasar-core/              # Core traits, types, triple-buffer, probe grid, scene output model
│   ├── quasar-materials/         # Dynamic acoustic material system
│   ├── quasar-dsp/               # Zero-alloc audio DSP graph
│   └── quasar-backends/          # Acoustic compute backends (CPU SIMD / WGPU / HW stub)

Quick Start

use quasar::prelude::*;
use quasar::SpatialAudioEngine;
use quasar_backends::cpu_simd::{CpuSimdComputeBackend, CpuSimdConfig};
use quasar_materials::tabular::{Tabular8BandEvaluator, TABULAR_MODEL_ID};
use quasar_materials::instance::AcousticMaterialInstance;
use quasar_core::scene::AcousticScene;
use quasar_core::scene_output::*;
use quasar_core::bands::Band8;
use quasar_dsp::audio_buffer::AudioBuffer;

let sr = 48000.0;
let mut engine = SpatialAudioEngine::new(0, sr, 15.0);

// 1. Set up acoustic materials
engine.materials().register_evaluator(Box::new(Tabular8BandEvaluator::new()));
let mat = engine.materials().add_instance(AcousticMaterialInstance::new(
    TABULAR_MODEL_ID,
    Tabular8BandEvaluator::create_params(Band8::splat(0.35), Band8::zeros(), Band8::zeros()),
));

// 2. Set up real-time acoustic compute backend
let mut scene = AcousticScene::new();
// add meshes with material handles...
let backend = CpuSimdComputeBackend::new(scene, CpuSimdConfig::default());
engine.set_backend(Box::new(backend));

// 3. Load an audio source
let src = engine.load_source(SourceConfig {
    path: "audio.wav".into(),
    channels: 2,
})?;

// 4. Create a positioned scene output (world-space speaker)
let speaker = engine.add_scene_output(SceneOutputConfig::new(
    [5.0, 1.5, 0.0],
    Movability::Static,
));

// 5. Pull a channel from the source into the speaker
engine.connect_pull(speaker, ChannelPull::new(src, 0, 0.0)); // L
engine.connect_pull(speaker, ChannelPull::new(src, 1, 0.0)); // R

// 6. Add a listener
let listener = engine.add_listener(ListenerConfig {
    position: [0.0, 1.6, 0.0],
    heading: [0.0, 0.0, -1.0],
    physical_layout: PhysicalOutputLayout::Stereo,
});

// Compute thread (15-30 Hz):
engine.update_scene_spatial();

// Audio thread (48 kHz):
let src_buf = AudioBuffer::new(2, 256);
let mut out = AudioBuffer::new(2, 256);
engine.process_audio_scene(&[&src_buf], &mut [&mut out]);

Architecture

Content Model (P1)

Three registries define the audio scene:

TypeIDPurpose
SourceConfigSourceIdMulti-channel audio content
SceneOutputConfigSceneOutputIdPositioned world emitter with pull list
ListenerConfigListenerIdWorld position + heading + physical layout

Patches (ChannelPull) define the content of each scene output — a (source_id, channel, gain_db) tuple. The sum of all pulls on an output is its audible content.

Scene Pipeline (P2)

process_audio_scene() runs in one zero-alloc pass:

  1. Publish latest triple-buffer coefficients from the compute thread
  2. Smooth per-pair (listener × output) coefficients through equal-power crossfaders
  3. Patch bay sums configured pulls into one mono buffer per scene output
  4. Spatial render per output: occlusion/air absorption → early reflections → late reverb
  5. Listener decode: VBAP-decode each output's mono onto the listener's physical speaker layout

Threading Model

┌──────────────────────┐
│ GAME / MAIN THREAD   │
│ load_source()        │
│ add_scene_output()   │
│ add_listener()       │
│ connect_pull()       │
│ update_listener()    │
└──────────┬───────────┘
           │
           ▼
┌──────────────────────┐
│ COMPUTE THREAD       │ (15–30 Hz)
│ update_scene_spatial()│
│ → hybrid probe / ray │
│ → triple-buffer write│
└──────────┬───────────┘
           │ lock-free atomic
           ▼
┌──────────────────────┐
│ AUDIO THREAD         │ (48 kHz)
│ process_audio_scene()│
│ → patch bay          │
│ → occlusion / early  │
│   / late reverb      │
│ → VBAP decode        │
│ NEVER allocates      │
└──────────────────────┘

Lock-Free Parameter Exchange

ParameterTripleBuffer uses three atomic-indexed slots so the compute thread and audio thread never access the same slot simultaneously. No mutexes, no blocking, no allocation.

Compute: begin_write() → modify → end_write()  [atomic swap write↔staging]
Audio:   update() → read()                      [atomic swap staging↔read]

Scene Output Config

pub struct SceneOutputConfig {
    pub position: [f32; 3],
    pub orientation: Option<[f32; 3]>,
    pub directivity: f32,           // 0 = omni, 1 = max cone
    pub pulls: Vec<ChannelPull>,    // patch-bay taps
    pub movability: Movability,
}

Listener Config

pub struct ListenerConfig {
    pub position: [f32; 3],
    pub heading: [f32; 3],          // normalized forward vector
    pub physical_layout: PhysicalOutputLayout,
}

pub enum PhysicalOutputLayout {
    Stereo,
    Surround51,
    Surround714,
    Quad,
    Custom { positions: Vec<[f32; 3]> },
    Hrtf,
}

Live Patch-Bay Remapping

Pulls can be added, removed, or re-gained at runtime — no audio glitch, no DSP rebuild:

engine.connect_pull(output_id, ChannelPull::new(src_id, 3, -6.0));
engine.disconnect_pull(output_id, src_id, 3);
engine.set_pull_gain(output_id, src_id, 3, -3.0);

Crate Details

quasar-core — Foundation Layer

ModuleKey TypesPurpose
bandsBand88-octave band model (62.5 Hz–8 kHz)
raysRay, RayHit, RayInteractionContextRay tracing primitives
sceneAcousticScene, AcousticMeshRuntime proxy geometry
backendIAcousticComputeBackend, MaterialProvider, SpatialQuery, SpatialQueryResultCompute backend abstraction
scene_outputSourceId, SourceConfig, SceneOutputId, SceneOutputConfig, ListenerId, ListenerConfig, ChannelPull, PhysicalOutputLayoutContent model
param_exchangeSpatialCoefficients, ParameterTripleBufferLock-free compute ↔ audio handoff
probe_gridAcousticProbeGrid, AcousticProbe, AcousticProbeSampleBaked probe data
hybridHybridProbeSampler, HybridSamplingStrategyBaked vs real-time strategy
errorSpatialAudioErrorUnified error type

quasar-materials — Dynamic Material System

ModuleKey TypesPurpose
evaluatorIAcousticMaterialEvaluator, AcousticResponse8BandMaterial evaluation trait
instanceMaterialModelId, MaterialParameterBuffer, AcousticMaterialInstanceMaterial data
registryAcousticMaterialRegistryThread-safe registry
tabularTabular8BandEvaluatorStatic 8-band lookup
delany_bazleyPorousDelanyBazleyEvaluatorContinuous porous absorber
resonant_panelResonantPanelEvaluatorLow-frequency membrane absorber
gpu_pipelineGpuMaterialLayoutGPU bindless pipeline helpers

quasar-dsp — Zero-Allocation Audio Graph

ModuleKey TypesPurpose
audio_bufferAudioBufferFixed-capacity deinterleaved buffer
crossfaderEqualPowerCrossfaderCosine/sine equal-power crossfade
fractional_delayHermiteInterpolatingDelayLine4-point Hermite interpolation
patch_bayPatchBayNode, PatchEntryMulti-input/multi-output mixer
occlusionAirAbsorptionOcclusionNode8-band occlusion + distance delay
early_reflectionsEarlyReflectionDelayNodeMulti-tap fractional delay
late_reverbFdnReverbNode16-line FDN with Hadamard matrix
master_decoderMasterSpatialDecoderNodeVBAP speaker decoding

quasar-backends — Compute Backends

BackendFeatureDescription
CpuSimdComputeBackendcpu-simd (default)BVH + rayon-parallel ray tracing
WgpuComputeBackendwgpu-computeWGSL compute shaders on GPU
HardwareAcceleratorStubalwaysFuture hardware placeholder

Feature Flags

CrateFeatureDescription
quasar-corenebula-importNebula serialization bridge
quasar-backendscpu-simdCPU SIMD backend (default)
quasar-backendswgpu-computeWGPU GPU compute backend

Integration with Nebula

Nebula is the offline baking toolchain. Quasar consumes baked probe grids at runtime:

engine.set_probe_grid(nebula_bytes_to_probe_grid(&baked_bytes)?);
engine.set_strategy(HybridSamplingStrategy::HybridBlend);

Hybrid strategies:

StrategyDirect PathEarly ReflectionsLate Reverb
BakedOnlyProbe gridProbe gridProbe grid
RealTimeOnlyRay traceRay traceStatistical
HybridBlendRay traceRay traceProbe grid

Development

cargo build
cargo test --no-run
cargo test -p quasar-core
cargo test -p quasar-materials
cargo test -p quasar-dsp
cargo test -p quasar-backends --no-default-features --features cpu-simd
cargo check --workspace

Crate Dependencies

quasar-audio
  ├── quasar-core        (glam, bytemuck, serde)
  ├── quasar-materials   (quasar-core)
  ├── quasar-dsp         (quasar-core, quasar-materials)
  └── quasar-backends    (quasar-core, quasar-materials, rayon, wgpu)

License

MIT OR Apache-2.0