Audio

Purpose

Audio plays independent voices from immutable clips or streamed files. Playback has its own output clock and application-owned lifetime rather than depending on render-frame publication or a runtime context adapter.

Interface

Types below are in CE::Audio unless another namespace is shown.

Types and operations

API and purpose

Clip, Format
Own immutable interleaved float PCM and source rate/channel metadata through from_samples(). Clips and voices.

System
Play clips/streams, set master volume, maintain retired voices and close output. Lifetime and scheduling; adapter catalogue.

Voice, VoiceSnapshot
Control independent playback and obtain a copied state/volume/loop/duration observation. Playback controls.

Miniaudio::System
Select device or offline output; use module decoding for file clips. miniaudio setup.

PlaybackEnded, PlaybackNotifications
Observe logical generation outcomes through an explicit owner pump and reconcile retained current state after bounded loss. Playback outcomes; runtime pump.

Playback values and choices

Type or choice

Purpose and details

PlaybackOptions

Initial normalized volume, looping and paused state per voice. Clip/stream submission.

PlaybackState

Playing, Paused, Finished, Stopped, Closed; stopped/finished voices require explicit restart. State transitions and closed handles.

Device / offline output

Device output advances independently; offline output advances only through explicit render() calls. Backend selection; miniaudio preparation.

Clip / streamed file

Retained decoded samples or file-backed playback whose source must remain available. Sources and limits.

Use a decoded clip when several effects should share retained samples without reopening a file. Stream longer tracks when retaining the whole decoded source is undesirable; keep that file available during playback. The miniaudio preparation example shows both entry points.

Usage

Own one selected audio system alongside the game, prepare clips at loading boundaries and retain voice handles for playback needing later control. Audio progresses on its output clock; its optional event pump is a separate owner task.

        ---
config:
  theme: base
  themeVariables:
    fontFamily: system-ui, sans-serif
    fontSize: 14px
  flowchart:
    htmlLabels: true
    curve: linear
    nodeSpacing: 22
    rankSpacing: 38
    padding: 12
---
flowchart TB
    accTitle: Audio · application-owned playback and outcomes
    accDescr: Local names omit Audio::; ambiguous nested namespaces remain qualified. Arrows point toward the API or type used, not call order or data flow. Methods beneath type names are selected application entry points; Implement hooks marks engine-invoked overrides. Dashed arrows mark optional choices.
    k0(["Application<br/>composition"]):::app
    k1["<b>Miniaudio::<br/>System</b><br/><br/>open_device(Format, PlaybackNotificationOptions):<br/>Miniaudio::System<br/><br/>publish_events_to(Events::EventBus::Publisher)<br/><br/>pump_with(Runtime::RuntimeServices):<br/>Runtime::ServiceAttachment<br/><br/>publish_notifications()"]:::focal
    k2(["Application<br/>loading work"]):::app
    k3("Miniaudio::decode_file(file, Miniaudio::DecodeOptions):<br/>Clip"):::focal
    k4["<b>Clip</b><br/><br/>from_samples(Format, samples): Clip"]:::focal
    k5(["Application<br/>sound controller"]):::app
    k6["<b>System</b><br/><br/>play(Clip, PlaybackOptions): Voice<br/><br/>stream(file, PlaybackOptions): Voice<br/><br/>maintain()<br/><br/>close()"]:::focal
    k7["PlaybackOptions"]:::focal
    k8["<b>Voice</b><br/><br/>snapshot(): VoiceSnapshot<br/><br/>pause()<br/><br/>resume()<br/><br/>stop()"]:::focal
    k9["VoiceSnapshot"]:::focal
    k10(["Application<br/>listener setup"]):::app
    k11["<b>Events::<br/>EventBus</b><br/><br/>register_listener(<br/>Events::EventChannel&lt;PlaybackEnded&gt;,<br/>Events::EventChannel&lt;PlaybackEnded&gt;::Callback,<br/>Events::EventBus::Delivery,<br/>Events::EventBus::ErrorHandler):<br/>Events::EventBus::Registration<br/><br/>publisher(): Events::EventBus::Publisher"]:::neighbor
    k12["PlaybackEnded"]:::focal
    k13(["Application<br/>init_platform()"]):::app
    k14["Runtime::<br/>RuntimeServices"]:::neighbor
    k15["<b>Runtime::<br/>ServiceAttachment</b><br/><br/>reset()"]:::neighbor
    k16(["Application<br/>cleanup / quiesce()"]):::app
    k17["Events::EventBus::<br/>Publisher"]:::neighbor
    k0 --> k1
    k2 -.-> k4
    k5 --> k6
    k5 --> k7
    k6 --> k4
    k6 --> k8
    k5 --> k8
    k8 --> k9
    k10 --> k11
    k10 --> k12
    k13 --> k1
    k1 --> k14
    k1 --> k15
    k13 --> k15
    k16 --> k6
    k2 --> k3
    k3 --> k4
    k6 --> k7
    k11 --> k17
    k1 --> k17
    classDef focal fill:#e7f3ef,stroke:#387d6b,color:#153d33,stroke-width:1.2px
    classDef neighbor fill:#f0f3f8,stroke:#8c9bb1,color:#35445b,stroke-width:1px
    classDef app fill:#fff2df,stroke:#c09452,color:#674515,stroke-dasharray:5 3
    classDef system fill:#eaf0fc,stroke:#617fab,color:#263f69,stroke-width:1.2px
    linkStyle default stroke:#7e8fa7,stroke-width:1.2px

    

Local names omit Audio:: where unambiguous. Explore this diagram and its other views →

The runtime maintenance attachment in this map is optional. Audio-only programs need no Context, display or renderer. The engine dependency map shows the neutral contracts and miniaudio implementation boundary.

Uses

Consumer

Dependency

Integration boundary

Audio::System playback

Audio::Clip, Audio::Format and Audio::PlaybackOptions, or a streamed file

Retain immutable PCM for independent voices; keep stream files available through playback and restart. The selected module owns decoding/mixing/output.

Outcome publication

Events Events::EventBus::Publisher

The designated owner explicitly pumps publication outside audio locks. Immediate listeners run there; queued listeners use their selected delivery adapter.

Miniaudio pump_with()

Runtime Runtime::RuntimeServices and Runtime::ServiceAttachment

Optional platform-maintenance notification pump. It retains weak audio access and does not own output or voice reclamation.

Workers can run application-owned CPU file decoding. Audio::System does not depend on an Engine worker pool, and playback does not wait for game updates or render-frame publication.

Used by

Consumer

Audio value/operation

Responsibility

Application sound/music controller

System::play() / stream() and retained Audio::Voice handles

Trigger prepared sources, control individual voices and explicitly close output after producers stop. Dropping a handle does not stop active playback.

Application state/UI observation

Audio::VoiceSnapshot and optional Audio::PlaybackEnded

Observe copied state or logical generation outcomes; select simulation delivery when a platform pump must reach simulation-owned state.

Offline processors and tests

Audio::Miniaudio::System::open_offline() and render()

Explicitly advance the mixer clock with complete output frames; no native device opens. Output selection.

Playback, maintenance and notifications

Operation

What it advances or releases

What the application must arrange

Device output / offline render()

Audio playback time.

Choose real device or explicit offline output; opening and initial source preparation can block.

maintain()

Reclaims unreferenced inactive voices.

Call regularly while open. It neither advances audio time nor publishes callbacks.

publish_notifications()

Observes terminal facts and attempts pending event publication.

Call on the designated owner, or retain the optional pump_with() attachment. The attachment does not call maintain().

close()

Stops output and retires voices; surviving handles report Closed.

Settle producers first. Pump final outcomes explicitly before removing subscriptions and releasing the system.

Observe playback outcomes

The optional Audio::playback_ended_event reports one logical terminal fact per admitted generation: Completed, Stopped, Replaced, OutputClosed or Reclaimed. Restart admits another generation while retaining voice identity; pause/resume and loop iterations do not create generations. Miniaudio supports this extension; notification_snapshot().system == 0 means unsupported.

Notifications use bounded storage and one publication attempt per retained record. Use Audio::PlaybackNotifications to detect loss and a retained voice’s current snapshot().terminal to reconcile that generation. This does not recover superseded playback history. The playback outcome contract explains identity, reason selection and pump ownership; the miniaudio example supplies subscription setup and removal.

Contracts

Clips require complete nonempty finite mono/stereo PCM at supported source rates. Volume is finite normalized linear gain in [0, 1]; mixing loud sources can still exceed nominal amplitude. A stream file must remain available through playback. Device opening, decode and initial stream opening can block.

Dropping a voice handle does not stop active playback. Stop and natural finish require explicit restart; toggling looping alone does not restart them. Snapshots observe playback rather than freezing its native cursor.

Lifecycle

Create output before submitting voices. Retain handles for voices needing later control and call maintain() regularly to reclaim eligible unreferenced voices. Maintenance does not advance audio time. Settle producers before system destruction; idempotent close() stops output and retires voices before native state. Surviving handles report Closed and reject controls. Construct a new system for another session.

Execution

Controls, submissions, snapshots and maintenance synchronize application producers. Playback advances independently on the backend clock, without calling game callbacks from mixing. Runtime concurrency does not change the audio ownership contract.

Integration

With an application-owned audio system and previously prepared clip:

auto voice = audio.play(clip, {.volume = 0.5f});
// Keep voice if the game needs to pause, stop or change this playback.
audio.maintain();

Create the system and decode clips at loading boundaries. Trigger prepared clips from game state, then close output only after all producers stop.

Limits and further reading

Offline processing is explicit; an unavailable device must not silently succeed inaudibly. Spatial/world/collision behavior belongs to game code or another selected capability. Read audio ownership, miniaudio setup and coverage and the System/Voice interface.