メインコンテンツまでスキップ

STCoreV2

モジュール式の音響経路探索とlock-freeデータ転送を採用した次世代Sound Tracingコア。

STCoreV2は、メッシュ、材質、リスナー、音源からインパルス応答(IR)と空間オーディオをリアルタイムに合成するC++ライブラリです。

概要

項目
基準ブランチfeat/lock-free-hybrid-v2-reverb (lock-free reverb)
主言語C++ (C++17ビルド、C API公開)
ビルドシステムCMake 3.22+
プラットフォームmacOS · Windows · Linux
WebビルドEmscripten対応 (EMSCRIPTEN_KEEPALIVE export)
アクセラレータ内蔵BVH または External callback(ゲームエンジンBVHなど)
Max Path Depth16 (EXA_MAX_DEPTH)
テストGoogle Test (unit + benchmark)
状態アクティブ開発

アーキテクチャ

Scene


SceneSnapshot ── lock-free per-tick snapshot
│ (TripleBuffer · POD · immutable)

┌─────────────────────────────────────────────┐
│ Propagation │
│ ├ IAccelerator │
│ │ ├ Internal BVH │
│ │ └ ExternalAccelerator (callback) │
│ └ Path Modules (IPathModule) │
│ ├ SpecularReflectionModule │
│ ├ UTDDiffractionModule │
│ ├ DiffuseOffsetModule │
│ └ StaticReverbModule │
│ (+ ScatterHandoff, ComparisonReport) │
└─────────────────────────────────────────────┘


Auralizator (filter / frequence / HRTF / states)


Audio Output

エンジンは大きく4層で構成されます。

  • Scene → Snapshot — 毎tickのシーン状態をPODベースのSceneSnapshotとして取得し、TripleBufferを通してpropagation/rendererスレッドへlock-freeで渡します。
  • Propagation (モジュール式)IPathModule共通インターフェース上で4種類のモジュールが動作します。モジュールごとにアルゴリズムの差し替えや数値比較が可能です。アクセラレータは内蔵BVHに加え、ホスト側BVHへコールバック委譲できます。
  • Auralizator — 追跡済み経路をIRへ合成し、filter、frequency decomposition、HRTFを適用して空間オーディオを生成します。
  • Audio Output — チャンネルマッピング後に呼び出し側へ返します。

モジュール構成

exaSound/
├── src/
│ ├── core/ # engine core
│ │ ├── EngineConfig # engine settings
│ │ ├── SceneSnapshot # immutable per-tick snapshot
│ │ ├── SnapshotBuilder # snapshot builder
│ │ ├── ExternalAccelerator # external BVH callback
│ │ ├── IAccelerator # accelerator abstraction
│ │ ├── Handle / Ref / FixedPool / PoolAllocator
│ │ ├── ThreadAffinity / Telemetry
│ │ └── PropagationResult
│ ├── propagation/
│ │ ├── Propagator # top-level dispatch
│ │ ├── RGC # Ray Generation Cluster
│ │ ├── module/ # Path Module abstraction
│ │ │ ├── IPathModule # common interface
│ │ │ ├── PathModuleRegistry
│ │ │ ├── reflection/ # SpecularReflectionModule
│ │ │ ├── diffraction/ # UTDDiffractionModule
│ │ │ ├── diffuse/ # DiffuseOffsetModule
│ │ │ └── reverb/ # StaticReverbModule
│ │ ├── UTDDiffraction.hpp # UTD formula
│ │ └── Ray/ # ray and plane utilities
│ ├── auralizator/ # auralization
│ │ ├── core / filter / frequence / HRTF / states
│ ├── scene/
│ │ ├── SoundObject # Object / Mesh / Preprocessing
│ │ └── BVH # Native BVH/TLAS
│ ├── math/, utils/, objects/, config/
│ ├── exasound.h # C++ main header
│ └── exasoundC.h # **public C API**
├── tests/ # Google Test
└── demo/ # demos

Public C API

C linkage API (exasoundC.h, 約120 export) として公開されます。

カテゴリ代表関数
ライフサイクルexaInit, exaReset, exaGetVersion, exaGetPathTypeCount
SceneexaNewScene, exaTickScene, exaSceneAddObject/Source/Listener
ObjectexaNewObject, exaObjectSetPosition/Rotation/Scale/Mesh, exaObjectSetUpdateType
MeshexaNewMesh, exaMeshSetData, exaMeshUpdateVertices, exaMeshRefit, exaMeshSetMaterial
MaterialexaAddSoundMaterial, exaSetSoundMaterial
SoundSourceexaNewSoundSource, exaSoundSourceSetPosition/Direction/Velocity/Intensity
Listener (basic)exaNewListener, exaListenerSetPosition/Orientation/Velocity, exaListenerSetRayCount/RayDepth
Listener (HRTF)exaInitでdefault HRTFを一度loadし、listener rendererが共有
RendererexaCreateRenderer, exaRenderSound, exaRemoveRenderer
結果取得exaGetValidPathCount, exaGetValidPaths, exaGetSortedIRDatas
診断・可視化exaPropagatorGetGuidePlanes/MirrorPositions, exaPropagatorGetProfile, exaGetStatistics, exaGetMemoryTraceSnapshot, exaGetLastError

はじめに

要件

  • CMake 3.22以上
  • C++17互換コンパイラ
  • macOS · Windows · Linux

ビルド

cd exaSound
cmake -S . -B build
cmake --build build

テスト込み:

cmake -S . -B build -DBUILD_TESTS=ON
cmake --build build --target unit_tests
./build/tests/unit/unit_tests

最小利用シナリオ(疑似コード)

#include "exasoundC.h"

// 1. Initialize the engine
exaInit();

// 2. Create a scene
int sceneID = exaNewScene();

// 3. Register a mesh and assign material
int meshID = exaNewMesh();
exaMeshSetData(meshID, vertices, vertexCount, indices, indexCount);
exaMeshSetMaterial(meshID, materialIndex);

// 4. Attach the mesh to an object and add it to the scene
int objID = exaNewObject();
exaObjectSetMesh(objID, meshID);
exaObjectSetPosition(objID, 0.f, 0.f, 0.f);
exaSceneAddObject(sceneID, objID);

// 5. Configure source and listener
int srcID = exaNewSoundSource();
exaSoundSourceSetPosition(srcID, 1.f, 1.f, 0.f);
exaSoundSourceSetIntensity(srcID, 1.0f);
exaSceneAddSource(sceneID, srcID);

int listenerID = exaNewListener();
exaListenerSetPosition(listenerID, -1.f, 1.f, 0.f);
exaListenerSetRayCount(listenerID, 4096);
exaListenerSetRayDepth(listenerID, 16); // up to EXA_MAX_DEPTH = 16
exaSceneAddListener(sceneID, listenerID);

// 6. Simulate and render audio each frame
for (;;) {
exaTickScene(sceneID, deltaTime);
exaRenderSound(/* render args */);
// Query results through exaGetValidPaths / exaGetSortedIRDatas
}

// 7. Clean up
exaReset();

このコードはAPIの流れを示す疑似例です。実際のシグネチャはexasoundC.hで確認してください。

主要概念

Path Module構造

伝搬アルゴリズムは4種類のモジュールに分離され、内部パイプラインで動作します。

モジュール経路タイプ位置
SpecularReflectionModule鏡面反射propagation/module/reflection/
UTDDiffractionModule回折(UTD)propagation/module/diffraction/
DiffuseOffsetModule散乱propagation/module/diffuse/
StaticReverbModule静的残響propagation/module/reverb/

各モジュールはIPathModuleを実装し、2段階で動作します。

  1. Phase 1 — buildSetupPlanes: Guide ray結果からSetupPlaneを構成
  2. Phase 2 — validatePaths: 経路を追跡・検証し、有効経路を出力バッファへ記録

SpecularからDiffuseへはScatterHandoffEntryでpath状態が渡されます。

Lock-free snapshot (SceneSnapshot)

シーン状態は毎tick immutable POD snapshotとして取得されます。

  • すべての構造はフラット配列、non-virtual、heap pointerなしで、TripleBufferスロットへ値コピー可能
  • propagation/audioスレッドは別スロットを読み、lock-freeで動作
  • geometryは別BVH double bufferで管理(Phase 3)

この構造により、mutexなしでマルチスレッドのシミュレーションとレンダリングを安全に進められます。

Materialモデル

SoundTriangle単位にabsorptiontransmissionを直接保存します。別Material ID/pointerは持たず、反射は次の規則で計算します。

reflection = 1 - (absorption + transmission)

ExaRayHitにはmaterialIdフィールドがあり、ray cast時に衝突材質を直接識別できます。

Ray Count・Ray Depth

リスナーごとにray数と最大反射深度を設定します。

関数効果
exaListenerSetRayCount(id, n)ray数を設定
exaListenerSetRayDepth(id, d)最大深度 (1 ≤ d ≤ EXA_MAX_DEPTH = 16)

現在のbuildではpath depth上限はEXA_MAX_DEPTH = 16です。

Diffuse Scatteringオプション

ExaSTOptionには散乱関連パラメータがあり、シミュレーションを微調整できます。

フィールド意味
diffuseEnabled散乱処理のon/off
diffuseStartDepth散乱を開始する反射深度(default 5)
diffuseMaxOffsetRadius散乱offset半径
diffuseCurveA/B/C距離・角度に対する散乱曲線係数
guideDiffuseEnabledGuide diffuse rayを使用
guideDiffuseListenerHeadRadiusリスナー頭部半径(default 0.0875m)

HRTF

exaInit(); // embedded default HRTFを一度load
exaNewListener(); // rendererはengine default HRTFで開始
exaReset(); // renderer stateとdefault HRTFを解放

結果取得

形式関数
有効経路(可視化・デバッグ)exaGetValidPathCount, exaGetValidPaths
ソート済みIR(コンボリューション入力)exaGetSortedIRDatas
Guide Plane / Mirror Position(診断)exaPropagatorGetGuidePlanes, exaPropagatorGetMirrorPositions

ExaPathDatapos[0]=source, pos[1..N]=hit points, pos[N+1]=listenerの順に保存されます。

Object Update Type

exaObjectSetUpdateType(objID, updateType);
// 0 = EXA_OBJECT_UPDATE_STATIC : 実行時の TLAS/BLAS 更新なし
// 1 = EXA_OBJECT_UPDATE_REFIT : deformation - BLAS refit + TLAS bounds 更新
// 2 = EXA_OBJECT_UPDATE_REBUILD : topology 変更 - 再ビルド
// 3 = EXA_OBJECT_UPDATE_DYNAMIC : transform のみ変更 - TLAS instance 更新

静的オブジェクトは毎フレームのBVH refitコストを避けられます。

Refit は skinned animation のように topology を保ったまま vertex だけが動く geometry の ためのポリシーです。vertex 自体は mesh 側の 2-call protocol でアップロードします。

exaMeshUpdateVertices(meshID, vertices, numVertices); // CPU skinning パス
exaMeshRefit(meshID); // mesh BVH の refit

numVerticesexaMeshSetData 時の vertex 数と完全に一致する必要があり、異なる場合は EXA_ERR_INVALID_ARG として拒否されます。topology(triangle index)が変わる場合は refit では なく、exaMeshSetData で再ビルドして object update type を Rebuild にしてください。

診断・統計

関数用途
exaGetStatistics()ray・path・時間統計
exaPropagatorGetProfile(sceneID)伝搬段階別プロファイル
exaPropagatorGetGuidePlanes/MirrorPositionsアルゴリズム内部状態
exaGetMemoryTraceSnapshot()メモリ使用snapshot
exaGetLastError()最後のエラーメッセージ

参考