Skip to content

Keep OLR's position timeline in step with renders without positions - #52

Merged
trsonic merged 2 commits into
mainfrom
fix/olr-position-timeline
Oct 21, 2026
Merged

trsonic merged 2 commits into
mainfrom
fix/olr-position-timeline

Conversation

@trsonic

@trsonic trsonic commented Oct 13, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes the silent object after a render without position metadata, and the three related problems in the same path, described in #51.

  • Position timeline (src/renderer/olr/olr.c). _metadata_update() starts a position block at the next sample to render when that is later than the end of the object's previous block. A host that pushes a position for every rendered block, or pushes ahead, gets the same block starts as before, so its output is unchanged.
  • Refill after a block that ends inside a call (block_processor/block_processing_channel.c). The next block is checked against the finished block's last sample instead of against (uint64_t)-1.
  • 64-bit sample positions. olr_context_t::offset is a uint64_t, like the metadata counters, and _processing_block_overlap() (block_processor/processing_block.c) computes the overlap in 64-bit positions and narrows only the offsets within the call to int.
  • Log format. The underrun log casts its uint64_t values to unsigned long long for %llu.
  • Metadata after a drained queue (include/oar.h). A note on oar_update_audio_element_metadata() states that once rendering has used up an element's queued gain or position metadata, metadata pushed afterwards applies from the next rendered sample, and that a host holds a gain or a position over a span by pushing metadata that covers it (from the discussion in OLR: an object stays silent after one render without position metadata #51).

Tests:

  • tests/examples/test_object_position_gap.c renders an object element through oar.h for 4 blocks with positions and 1 to 3 blocks without, fed program input or silence, and requires the next block with a position to be audible and equal to the first block of a new oar_t with the same position and input.
  • tests/examples/test_olr_render_offsets.c calls the object audio renderer directly, because oar_render() always hands it one position block per call at the call's position: 128-sample position blocks rendered in 256-sample calls must give the output of 128-sample calls, and blocks rendered at sample positions around 2^31 and 2^32 must give the output of the same blocks at position 0. It includes object_based_audio_renderer.h, so that header's Bazel target is now also visible to //tests/examples.

Both tests fail on main: the gap cases render silence after the gap, the shorter blocks are dropped as underruns against 18446744073709551615, and the block at 2^31 - 256 renders silent.

Verified locally on macOS arm64:

  • CMake with the workflow's commands, ctest --test-dir build -L 'oar|obr' --output-on-failure: 35 of 35 pass, in Release and with -fsanitize=address,undefined, on main 54a4e89.
  • Bazel 8.5.1: bazel build //... and //tests/examples/... pass (11 pass, 1 skipped). The standalone Bazel build fails on this machine on main too, with the Xcode SDKSettings.json "absolute path inclusion" error in untouched files, so these were run with a temporary bazel_dep(name = "apple_support", version = "2.8.3") in MODULE.bazel, which is not part of this PR.

Closes #51

OLR counts the samples it has rendered (the render offset) and where
each object's next position block starts. A render without a position
advanced only the render offset. Every later position block then
started before the next sample to render, _refil_processing_queue()
dropped it as a metadata underrun and logged an error, and the object
stayed silent until the instance was rebuilt. Through oar.h this
happens whenever an object element is rendered for a block without a
position queued, for example when a host drains the renderer's tail
while paused with silent input and no metadata.

_metadata_update() now starts a position block at the next sample to
render when that is later than the end of the object's previous block.
A host that pushes a position for every rendered block, or pushes
ahead, gets the same block starts as before.

In the same path:
- After a processing block that ends inside a render call, the next
  block was checked against (uint64_t)-1 instead of the finished
  block's end, so every block starting inside a call counted as an
  underrun and was dropped. It is checked against the finished block's
  last sample.
- The render offset was an int and overflowed after 2^31 samples,
  about 12.4 hours at 48 kHz; it is a uint64_t, like the metadata
  counters. A processing block also computed its overlap with a render
  call in int; it uses 64-bit sample positions and narrows only the
  offsets within the call.
- The underrun log passed uint64_t values to %u and %lu; it casts them
  to unsigned long long for %llu.

test_object_position_gap.c renders an object element through oar.h
for 4 blocks with positions and 1 to 3 blocks without, fed program
input or silence, and requires the next block with a position to be
audible and equal to the first block of a new oar_t with the same
position and input. test_olr_render_offsets.c calls the object audio
renderer directly: 128-sample position blocks rendered in 256-sample
calls must give the output of 128-sample calls, and blocks rendered at
sample positions around 2^31 and 2^32 must give the output of the same
blocks at position 0. It includes object_based_audio_renderer.h, so
that header's Bazel target is visible to //tests/examples. Both tests
fail on main.

ctest -L 'oar|obr' (CMake, Release and ASan): 33 pass.
Once rendering has used up an element's queued gain or position
metadata, metadata_item_elapse() leaves the item's start and duration
at 0, so metadata pushed afterwards applies from the next rendered
sample. Duration-only metadata cannot describe a gap. A note on
oar_update_audio_element_metadata() now states this, and that a host
holds a gain or a position over a span by pushing metadata that covers
it. Discussed in #51.
@trsonic

trsonic commented Oct 19, 2026

Copy link
Copy Markdown
Collaborator Author

Hey @yeroro @jingbo-marquis @yilun-zhangs,

I've just re-requested your reviews following the addition of 9ae4ca8 and c952acd

Thanks!

@trsonic
trsonic merged commit 51370ac into main Oct 21, 2026
9 checks passed
@trsonic
trsonic deleted the fix/olr-position-timeline branch October 21, 2026 07:29
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.

OLR: an object stays silent after one render without position metadata

4 participants