Skip to content

feat: report playback position in AudioEvent - #488

Open
LautaroPetaccio wants to merge 2 commits into
mainfrom
feat/audio-event-playback-position
Open

LautaroPetaccio wants to merge 2 commits into
mainfrom
feat/audio-event-playback-position

Conversation

@LautaroPetaccio

@LautaroPetaccio LautaroPetaccio commented Sep 15, 2026 •

Copy link
Copy Markdown

Summary

Adds three optional fields to PBAudioEvent so renderers can report where a clip's playhead actually is, whenever the playhead moves while a clip plays:

  • tick_number: scene tick of the report, equals EngineInfo.tick_number
  • current_offset: playback position of the clip in seconds at that tick
  • clip_length: total clip length in seconds, when known

And one optional field to PBAudioSource, by which a scene asks for them:

  • report_playback_position: default false

Why the reports are opt-in

A position report is written whenever the playhead moves, far more often than a media state changes, and a scene can hold many more audio sources than video players: video playback is capped by a prioritisation mechanism in the renderer, audio is not. Reporting unconditionally would make every fire-and-forget sound effect pay for a signal only rhythm and synchronisation scenes read.

The cost is not only traffic. AudioEvent is a grow-only value set bounded per entity, so a single source reporting on every playhead movement fills its own window within seconds, evicting state-change history a scene reading the component directly might want.

Media state changes are reported whatever the flag says, so a scene that never sets it observes exactly what it observes today.

Why

PBAudioSource.current_time is a write-only seek and PBAudioEvent carries only a media state and a counter, so a scene cannot tell when the audio it hears started or where it is. Renderers start a clip some milliseconds after being asked to (100 to 250 ms measured on the Unity explorer, varying per start), which rhythm and video-synchronized scenes cannot correct without this signal. PBVideoEvent already reports current_offset and tick_number for video; this brings audio to the same shape.

Why optional

The fields are absent rather than zero when a renderer has no position to give, which is the case for streams whose player does not expose one, and on any renderer that has not implemented the reports. A zero offset is a valid playback position, so presence has to be distinguishable from it. PBPointerEventsResult.analog sets the precedent in a renderer-written grow-only event set, and the rest of the audio family is optional throughout. PBVideoEvent is the outlier here, not this message.

What it does not carry

current_offset is the decoder's read position, not the moment a sample leaves the speaker. The output path adds tens of milliseconds more, consistently signed and roughly constant per device, and no field here reports it. A renderer able to estimate its own output latency should expose it, and a later revision may add a field; ADR-318 records the limit meanwhile.

Compatibility

All fields are optional. Existing renderers and scenes are unaffected. The component id does not change. audio_event.proto compiles with protoc, and the compatibility check against main passes.

Related

@github-actions

github-actions Bot commented Sep 15, 2026 •

Copy link
Copy Markdown

Test this pull request

  • The @dcl/protocol package can be tested in scenes by running
    npm install "https://sdk-team-cdn.decentraland.org/@dcl/protocol/branch//dcl-protocol-1.0.0-35636892899.commit-fed09fd.tgz"

Renderers start an AudioSource some milliseconds after being asked to and
PBAudioSource.current_time is a write-only seek, so a scene has no way to
tell where the audio it hears actually is. Add optional fields to
PBAudioEvent so renderers can report the clip position on every state
change and periodically while playing:

- tick_number: the scene tick of the report (EngineInfo.tick_number)
- current_offset: playback position in seconds at that tick
- clip_length: total clip length in seconds, when known

All fields are optional, so existing renderers and scenes are unaffected.
@LautaroPetaccio
LautaroPetaccio force-pushed the feat/audio-event-playback-position branch from b29c1cb to 7a449b7 Compare September 16, 2026 13:01
LautaroPetaccio added a commit to decentraland/js-sdk-toolchain that referenced this pull request Sep 16, 2026
CI regenerates the ecs components from the installed @dcl/protocol, which
did not yet carry tick_number, current_offset and clip_length, so the
build failed on audioEvents.ts. Pin both protocol dependencies to the
package published from decentraland/protocol#488 until it is released,
and regenerate the API report.

The serialization specs deserialize into objects that carry every proto3
optional field explicitly, so AudioEvent.spec.ts now lists them and also
covers a full position report and a position without a clip length.
ExplorerUiEventsResult gained a required request_id upstream in the same
protocol window; its spec expects the zero value.
LautaroPetaccio added a commit to decentraland/js-sdk-toolchain that referenced this pull request Sep 16, 2026
CI regenerates the ecs components from the installed @dcl/protocol, which
did not yet carry tick_number, current_offset and clip_length, so the
build failed on audioEvents.ts. Pin both protocol dependencies to the
package published from decentraland/protocol#488 until it is released,
and regenerate the API report.

The serialization specs deserialize into objects that carry every proto3
optional field explicitly, so AudioEvent.spec.ts now lists them and also
covers a full position report and a position without a clip length.
ExplorerUiEventsResult gained a required request_id upstream in the same
protocol window; its spec expects the zero value.
LautaroPetaccio added a commit to decentraland/js-sdk-toolchain that referenced this pull request Sep 16, 2026
CI regenerates the ecs components from the installed @dcl/protocol, which
did not yet carry tick_number, current_offset and clip_length, so the
build failed on audioEvents.ts. Pin both protocol dependencies to the
package published from decentraland/protocol#488 until it is released,
and regenerate the API report.

The serialization specs deserialize into objects that carry every proto3
optional field explicitly, so AudioEvent.spec.ts now lists them and also
covers a full position report and a position without a clip length.
ExplorerUiEventsResult gained a required request_id upstream in the same
protocol window; its spec expects the zero value.
pravusjif
pravusjif previously approved these changes Sep 17, 2026

@pravusjif pravusjif left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch the current_offset is a real need, like the one we have in video_event 👍

@LautaroPetaccio
LautaroPetaccio marked this pull request as ready for review September 17, 2026 14:16
LautaroPetaccio added a commit to decentraland/unity-explorer that referenced this pull request Sep 17, 2026
…ition

Adds TickNumber, CurrentOffset and ClipLength to PBAudioEvent, with the
Has/Clear accessors proto3 optional fields generate. Without them the system
in this branch does not compile.

Pins @dcl/protocol to the build of decentraland/protocol#488. The pin goes back
to a released version once that merges.
A position report is written whenever the playhead moves, which is far more
often than a media state changes, and a scene can hold many more audio sources
than videos: video playback is capped by a prioritisation system, audio is not.
Reporting unconditionally would make every fire-and-forget sound effect pay for
a signal only rhythm and sync scenes read.

report_playback_position gates only the position fields. Media state changes are
reported either way, so nothing an existing scene observes today changes.

Also correct the AudioEvent comment, which still described the reports as
periodic. There is no interval; a report goes out when the playhead moves.
LautaroPetaccio added a commit to decentraland/js-sdk-toolchain that referenced this pull request Sep 21, 2026
Repins @dcl/protocol to the build carrying PBAudioSource.report_playback_position
(decentraland/protocol#488) and regenerates. The flag is a plain generated field,
so a scene sets it like any other property; the SDK does not set it on the
scene's behalf. Registration is allowed before the AudioSource exists, and a
later scene-written createOrReplace would clear an SDK-written flag silently,
so it stays explicit and owned by the scene.

Documents on registerAudioPlaybackEntity that the source has to opt in, since
the failure mode otherwise is a callback that simply never runs.

Covers the field in the AudioSource serialization spec.
LautaroPetaccio added a commit to decentraland/js-sdk-toolchain that referenced this pull request Sep 21, 2026
CI regenerates the ecs components from the installed @dcl/protocol, which
did not yet carry tick_number, current_offset and clip_length, so the
build failed on audioEvents.ts. Pin both protocol dependencies to the
package published from decentraland/protocol#488 until it is released,
and regenerate the API report.

The serialization specs deserialize into objects that carry every proto3
optional field explicitly, so AudioEvent.spec.ts now lists them and also
covers a full position report and a position without a clip length.
ExplorerUiEventsResult gained a required request_id upstream in the same
protocol window; its spec expects the zero value.
LautaroPetaccio added a commit to decentraland/js-sdk-toolchain that referenced this pull request Sep 21, 2026
Repins @dcl/protocol to the build carrying PBAudioSource.report_playback_position
(decentraland/protocol#488) and regenerates. The flag is a plain generated field,
so a scene sets it like any other property; the SDK does not set it on the
scene's behalf. Registration is allowed before the AudioSource exists, and a
later scene-written createOrReplace would clear an SDK-written flag silently,
so it stays explicit and owned by the scene.

Documents on registerAudioPlaybackEntity that the source has to opt in, since
the failure mode otherwise is a callback that simply never runs.

Covers the field in the AudioSource serialization spec.
LautaroPetaccio added a commit to decentraland/unity-explorer that referenced this pull request Sep 21, 2026
Adds PBAudioSource.report_playback_position (field 8, optional bool), the opt-in
that gates the playback position reports, and picks up the PBAudioEvent doc
change that describes when a position is written.

Moves the @dcl/protocol pin to the newer build of decentraland/protocol#488. The
pin goes back to a released version once that merges.
LautaroPetaccio added a commit to decentraland/js-sdk-toolchain that referenced this pull request Sep 28, 2026
CI regenerates the ecs components from the installed @dcl/protocol, which
did not yet carry tick_number, current_offset and clip_length, so the
build failed on audioEvents.ts. Pin both protocol dependencies to the
package published from decentraland/protocol#488 until it is released,
and regenerate the API report.

The serialization specs deserialize into objects that carry every proto3
optional field explicitly, so AudioEvent.spec.ts now lists them and also
covers a full position report and a position without a clip length.
ExplorerUiEventsResult gained a required request_id upstream in the same
protocol window; its spec expects the zero value.
LautaroPetaccio added a commit to decentraland/js-sdk-toolchain that referenced this pull request Sep 28, 2026
Repins @dcl/protocol to the build carrying PBAudioSource.report_playback_position
(decentraland/protocol#488) and regenerates. The flag is a plain generated field,
so a scene sets it like any other property; the SDK does not set it on the
scene's behalf. Registration is allowed before the AudioSource exists, and a
later scene-written createOrReplace would clear an SDK-written flag silently,
so it stays explicit and owned by the scene.

Documents on registerAudioPlaybackEntity that the source has to opt in, since
the failure mode otherwise is a callback that simply never runs.

Covers the field in the AudioSource serialization spec.
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.

2 participants