Repository navigation
Conversation
trsonic
requested review from
felicialim,
jingbo-marquis,
trevorknight,
yeroro and
yilun-zhangs
October 13, 2026 14:21
yeroro
approved these changes
Oct 14, 2026
yilun-zhangs
approved these changes
Oct 15, 2026
jingbo-marquis
approved these changes
Oct 15, 2026
ProcessingGroup::Process() encodes the loudspeaker layout and object channels of a group in two passes, the world-locked elements' and the head-locked elements', and copies each pass's channels to the encoder's inputs 0 to k-1. One encoder held all the group's channels as sources, in element order. In a group with both kinds of element a pass's channels were therefore encoded with other channels' positions, the encoder inputs past k still held the other pass's audio, and both passes shared one encoder's interpolation state. Through oar.h, with element 1 head-locked and silent at +90 degrees and element 2 world-locked noise at -90 degrees, element 2 is rendered at +90 degrees: left and right energy 226.695 and 6.799, against 6.577 and 227.456 with element 1 world-locked. Each pass now has its own encoder and input buffer (EncoderPass), with the pass's channels as sources in element order, so input i of a pass is encoded with the position of the pass's i-th channel. UpdateAmbisonicEncoder() sizes and sets both, and SetElementHeadLocked() calls it through UpdateAllAmbisonicEncoders(), because a change moves the element's channels to the other pass; its documentation says that it can allocate. The unfiltered GetAmbisonicEncoderSourceChannelIndices() overload had no other caller and is removed. A group whose elements are all world-locked or all head-locked uses one encoder with the same sources as before. test_binaural_mixed_lock.c renders a world-locked object beside a silent head-locked one and requires the output of the world-locked object alone, and the reverse with the head-locked object alone. ObrImplTest.TestSetElementHeadLockedUpdatesEncoders sets a configured group's element head-locked, and back to world-locked so that the head-locked pass is empty, and requires the output of the world-locked object alone. Both fail on main. ctest -L 'oar|obr' (CMake, Release and ASan): 32 pass.
trsonic
force-pushed
the
fix/obr-mixed-head-locked
branch
from
October 19, 2026 16:43
d9c1195 to
e647b0b
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #53: in a binaural processing group with both head-locked and world-locked loudspeaker or object elements, channels were encoded with other channels' positions.
renderer/processing_group.{h,cc}).ProcessingGroupkeeps anEncoderPass(encoder and input buffer) for the world-locked and for the head-locked channels. Each holds only its pass's channels as sources, in element order, so input i of a pass is encoded with the position of the pass's i-th channel, and the passes no longer share inputs or interpolation state.UpdateAmbisonicEncoder()sizes and sets both. The unfilteredGetAmbisonicEncoderSourceChannelIndices()overload had no other caller and is removed.SetElementHeadLocked()(renderer/obr_impl.{h,cc}) callsUpdateAllAmbisonicEncoders(), because a change moves the element's channels to the other pass. Its documentation says that it can allocate when an encoder's source count changes.A group whose elements are all world-locked or all head-locked uses one encoder with the same sources as before, so its output does not change. With the reproduction from #53, both runs now render element 2 on the right: L 6.577, R 227.456.
Tests:
tests/examples/test_binaural_mixed_lock.crenders, throughoar.h, a world-locked object beside a silent head-locked one and requires the output of the world-locked object alone, and the reverse with the head-locked object alone.ObrImplTest.TestSetElementHeadLockedUpdatesEncoderssets a configured group's element head-locked, and back to world-locked so that the head-locked pass is empty, and requires the output of the world-locked object alone.Both fail on
main.Verified locally on macOS arm64:
ctest --test-dir build -L 'oar|obr' --output-on-failure: 32 of 32 pass, in Release and with-fsanitize=address.//tests/examples:alland@obr//obr/renderer/tests:allpass (13 pass, 1 skipped). The standalone Bazel build fails on this machine onmaintoo, with the XcodeSDKSettings.json"absolute path inclusion" error in untouched files, so these targets were built as modules from a workspace that registersapple_support's toolchain.Closes #53