Skip to content

README: update for stages 5.1 and 6.1 - #582

Closed
razedproduction wants to merge 82 commits into
ValveSoftware:masterfrom
razedproduction:readme-stage-6.1
Closed

razedproduction wants to merge 82 commits into
ValveSoftware:masterfrom
razedproduction:readme-stage-6.1

Conversation

@razedproduction

Copy link
Copy Markdown

Updates the status block, badge, stage table, next stage and roadmap to reflect the direct simulator (5.1) and energy field/IR/reconstructor (6.1).

razedproduction and others added 30 commits October 8, 2026 08:09
Keep core only.
- Replace the placeholder workspace/ crate (and its committed target/
  directory) with a root Cargo workspace; CI now actually runs.
- crates/steam-audio: library crate skeleton for the port.
- crates/parity: .npy golden-data reader/writer, Tolerance presets and
  assert_close for numeric parity with the reference C++ library.
- docs: porting plan, deviations log, ADR 0001 (pure-Rust SOFA loader).
- README: describe core/ as the reference implementation, new layout.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Set up Cargo workspace and parity test infrastructure
- tools/golden/fetch_deps.py: builds the minimal dependency set (zlib,
  libmysofa, PFFFT, FlatBuffers, Catch) at the commits pinned in
  core/build/dependencies.json, with Ninja and a static CRT; works with
  MSVC newer than VS 2022, which upstream's script does not support.
- tools/golden/hook.cmake: adds the `golden` target to the unmodified
  upstream build via CMAKE_PROJECT_Phonon_INCLUDE + deferred include,
  so it shares upstream's flags and links the internal core library.
- First generator: FFT (real and complex, forward and inverse, five
  sizes). Golden files committed under testdata/golden/fft.
- parity::Lcg mirrors the C++ input generator; an end-to-end test checks
  that Rust reads the C++ output and regenerates identical inputs.
- Plan: stage 1b done; Bevy integration added as stage 9.

All 155 upstream Catch tests pass on this reference build.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Add reference build and golden data generator (stage 1b)
…apes

- steam_audio::math: Vector2/3/4, Matrix<R, C> (column-major, const
  generics), Quaternion, CoordinateSpace3, spherical/interaural/
  cylindrical polar vectors, upstream's truncated PI/HALF_PI.
- steam_audio::geometry: Sphere, Aabb (upstream Box), Ray with
  triangle, slab-box and sphere intersection.
- Parity details kept on purpose: division via reciprocal multiply,
  quaternion normalisation in double, std::min/max and min_element
  tie/NaN semantics, NaN-tolerant triangle bounds check.
- tools/golden: geometry generator (6 modules, 256 cases each).
- tests/geometry.rs replays the generator's LCG draws; all outputs match
  the reference bit for bit on Windows (F32 tolerance for other libms).
- Plan: 2.1 done, upstream-to-Rust name mapping; deviations recorded.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Port stage 2.1: vectors, matrices, quaternions, coordinate spaces, shapes
- math: DEGREES_TO_RADIANS (upstream's truncated 0.017452), next_pow2,
  factorial/double_factorial (wrapping like uint64_t), never-panicking
  clamp with std::min/max semantics.
- math::DynamicMatrix: column-major, non-allocating add/subtract/scale/
  multiply/multiply_vector into caller-provided outputs.
- sampling: sphere, hemisphere and Halton sphere-volume samples plus
  transforms (time-seeded RandomNumberGenerator deferred to stage 6).
- array_math: element-wise real/complex ops as plain loops; threshold
  reproduces upstream's SIMD-body vs scalar-tail NaN/-0.0 difference.
- steam_audio::Complex = num_complex::Complex<f32> (shared with rustfft).
- tools/golden: sampling, array_math, dynamic_matrix generators.
- All parity outputs are bit-exact on Windows.
- Plan: R2 refined per RNG source, R6 (IPP in shipped binaries), names.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Port stages 2.2-2.3: math functions, sampling, array math, DynamicMatrix
Captures what new sessions need without re-reading the history: repo
layout, required checks, reference build and golden regeneration, the
per-slice porting procedure, parity pitfalls found in stages 2.1-2.3,
Rust conventions and the git/PR workflow.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Add CLAUDE.md with project working rules
- audio_buffer: planar AudioBuffer generic over storage (owned Vec or
  borrowed views), interleave/deinterleave, scale, mix, downmix,
  convert_ambisonics (N3D/SN3D/FuMa), AmbisonicsType, AudioSettings,
  AudioEffectState.
- tools/golden: audio_buffer and ambisonics_conversion generators.
- Parity tests: all goldens bit-exact on Windows.
- Plan and DEVIATIONS updated.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Port stage 2.4: AudioBuffer and Ambisonics conversion
RealFft and ComplexFft replace upstream FFT (PFFFT backend) on top of
realfft/rustfft. Sizes round up to a power of two, forward transforms are
unscaled and inverses scale by 1/n; DC/Nyquist imaginary parts are ignored
on the real inverse, as upstream. Transforms own their scratch, so applying
them never allocates.

Parity against the existing fft goldens passes at peak_relative(1e-5);
measured worst error is 5.4e-7 x peak (not bit-exact, risk R1). The
upstream FourierTransform tests are ported too.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Plan: AudioBuffer consequences for stages 4 and 8 (R7)
razedproduction and others added 27 commits October 12, 2026 02:22
…fects

Port AmbisonicsEncodeEffect, AmbisonicsRotateEffect, AmbisonicsPanningEffect,
AmbisonicsBinauralEffect and AmbisonicsDecodeEffect, with golden generators
(tools/golden/src/ambisonics.cpp) and parity tests. Encode, rotate, panning
and panned decoding are bit-exact on Windows; binaural is within 2.5e-7 x
peak (FFT, R1). The virtual speaker table moves from hrtf to
AmbisonicsPanningEffect::VIRTUAL_SPEAKERS. Upstream quirks kept: R15.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Stage 3.4: Ambisonics effects (encode, rotate, panning, binaural, decode)
README: show only the current stage (3.4)
Port libmysofa 1.3.3's loader (mysofa_load_data, the HDF5 subset in src/hdf)
and mysofa_check into a new pure-Rust crate, `sofa`, as decided in ADR 0001.
Stage 3.5 is split: 3.5b adds resampling, lookup and neighbourhood, 3.5c the
SOFA HRTF map and HRTFMapFactory.

- Safe parser for untrusted input: errors through `sofa::Error` with
  libmysofa's status codes; limits where libmysofa crashes or hangs.
- Inflate with miniz_oxide's core inflater, matching zlib's single-call
  semantics (it stops once the chunk-sized output is full).
- Golden generator `sofa` with libmysofa as the oracle: the three shipped
  files, libmysofa's own test corpus (local only) and 256 corruptions of
  cipic_124.sofa. All bit-exact (2000 corruptions checked once locally).
- cargo-fuzz target in crates/sofa/fuzz; `|u1` arrays in parity and npy.h.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Stage 3.5a: sofa crate (HDF5 subset, SOFA loader and check)
README: stage 3.5a status and notable deviations
Port the rest of libmysofa's mysofa_open_no_norm / mysofa_open_data_no_norm,
which Steam Audio uses for custom HRTFs:

- Hrtf::resample (mysofa_resample) with the Speex resampler at quality 10,
  both the direct and the interpolated sinc table, mixed float/double as
  compiled by MSVC; Speex error codes as Error::Resampler.
- Hrtf::to_cartesian, c2s and s2c; radius calls the C library's powf like
  upstream (MSVC's powf(x, 2) is not always x * x and LLVM folds it).
- Lookup (k-d tree, mysofa_lookup_init/mysofa_lookup) with an explicit
  search stack and reusable scratch; Neighborhood; Easy bringing them
  together.
- Limits where libmysofa overflows or never terminates (DEVIATIONS.md).
- Golden generator sofa_easy: coordinates, resampling of the three shipped
  files to 8-96 kHz, corrupt sampling rates, Easy with lookups and
  neighbourhoods, and libmysofa's 43 test files (local only). Bit-exact on
  Windows, debug and release.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
…hood indices

Linux and macOS pick the other of two exactly equidistant measurements in
sadie_h12 (one ulp of sinf/cosf). Indices stay exact on Windows; elsewhere a
different index must be as far from the point as the expected one.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
…lookup

Stage 3.5b: Speex resampling, lookup and neighbourhood (sofa crate)
Port SOFAHRTFMap and HRTFMapFactory to steam-audio behind the `sofa` cargo
feature (on by default):

- `SofaHrtfMap`: loads SOFA files through the `sofa` crate, rejects non-zero
  Data.Delay, patches missing azimuth neighbours, and answers nearest and
  trilinear interpolated queries with upstream's arithmetic.
- `AnyHrtfMap` (IHRTFMap + HRTFMapFactory) and `HrtfSource` in `HrtfSettings`;
  `HrtfDatabase` builds its map through the factory, so binaural rendering,
  virtual surround and Ambisonics work with SOFA HRTFs.
- `HrtfDatabase::nearest_index` takes `&mut self`: the SOFA lookup searches
  with scratch space and must not allocate.
- Golden generators `sofa_hrtf_map` and `sofa_hrtf_database`; the map is
  bit-exact on Windows (HRIRs, patched neighbourhoods, indices, weights);
  database and binaural outputs within the 3.1/3.3 tolerances.
- CI and CLAUDE.md also run clippy without the `sofa` feature.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
… off Windows

Interpolation weights divide angles in degrees by grid steps of a few degrees,
so libm ulp differences reach 1.25e-5 on Linux: use abs 2e-5 as for the
built-in map's bilinear weights (3.1). Projected Ambisonics HRTFs blend
interpolated HRTFs with those weights: use the synthesized tolerance (R10),
measured 1.24e-5 x peak on Linux. Windows stays bit-exact for the weights.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
sadie_d1's 1-degree azimuth grid amplifies libm ulps in the interpolation
weights to 4.4e-5 on Linux: use abs 1e-4. A query exactly on the nearest
measurement's azimuth (straight behind) picks its interpolation side from the
sign of a difference of about an ulp, which flips on macOS: accept such
queries off MSVC. Windows stays bit-exact.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Stage 3.5c: SOFA HRTF map and HRTF map factory
…ity, gain, EQ, direct effect)

Ports distance_attenuation, air_absorption, directivity, propagation_medium, gain_effect, eq_effect and
direct_effect. Models are enums with Arc callbacks; DirectEffect, GainEffect and EqEffect gain apply_in_place
(R7). New golden generator tools/golden/src/direct.cpp and parity tests in tests/direct.rs, each effect run
checked out of place and in place. Bit-exact on Windows in debug and release. Upstream quirks recorded as R19.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Stage 4: direct sound (distance attenuation, air absorption, directivity, gain, EQ, direct effect)
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…scene)

Ports mesh, bvh, static_mesh, instanced_mesh, scene, custom_scene, material, hit and triangle as the scene
module. A Scene owns its meshes and refers to them by id, with upstream's staged add/remove/commit and
versioning; instanced meshes share an immutable Arc<Scene>; CustomScene becomes the RayTracer trait. The BVH
builder sorts with a port of MSVC's std::sort, so trees match the reference node for node (R21).

New generator tools/golden/src/scene.cpp (modules bvh and scene) and parity tests in tests/scene.rs, with a
port of RayTracerCompare's OBJ loader: the six meshes in core/data/meshes, Sponza included, and a scene with
instances traced through seven stages of changes. Bit-exact on Windows in debug and release. Serialization
moves to stage 6. Upstream quirks recorded as R20.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Stage 5: scene and geometry (mesh, BVH, static and instanced meshes, scene)
Port DirectSimulator into a new `simulation` module: distance attenuation, air absorption, delay, directivity,
raycast and volumetric occlusion, and transmission through the scene. DirectSoundPath moves there (still
re-exported from `effects`). The OBJ loader and the scene's Lcg draws are now shared between the stage 5
generators (scene_inputs.h) and tests (tests/common); the scene and BVH goldens are unchanged.

Goldens: boxroom and simplescene with Lcg materials and an instanced box, 256 source/listener pairs each, with
raycast and volumetric occlusion, 1-16 transmission rays (0-9 surfaces crossed), all 64 flag combinations and
no scene. Bit-exact on Windows in debug and release. Upstream quirks recorded as R22.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Stage 5.1: direct simulator (occlusion, transmission)
Split stage 6 into 6.1-6.4 (reconstruction, reverb, reflection simulator, probes/baking/serialization) and
port its leaves into `simulation`: EnergyField and ImpulseResponse (sizes, add, scale, scale-accumulate,
copy) and the Reconstructor (Gaussian and linear reconstruction, air absorption, distance attenuation
correction). Its white noise uses a port of MSVC's std::mt19937 and uniform_real_distribution<float>
(crate-private `random`). The overlap-save partitioner now takes an ImpulseResponse.

Goldens: sizes, operations between objects of different sizes, and 10 reconstructed IRs per rate at 48, 44.1
and 22.05 kHz in 6 calls on one reconstructor (silent and below-threshold bins, mismatched channels and
lengths, both air models, correction curves). Bit-exact on Windows in debug and release. Upstream quirks
recorded as R23.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
…constructor

Stage 6.1: energy field, impulse response, reconstructor
@razedproduction

Copy link
Copy Markdown
Author

Opened by mistake against upstream; closing.

@razedproduction
razedproduction deleted the readme-stage-6.1 branch October 13, 2026 00:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant