Skip to content

Hot Path Audio Prepared Voice Control

OpenAI Codex edited this page Nov 1, 2026 · 6 revisions

Prepared Audio Voice Controls and Filter Coefficients

Reviewed: November 1, 2026 · Source: b7140848
State: Partial delivery: callback-local CFG_FMT snapshot #191; persistent control/coefficient caches remain research.
Release availability: #191 is main only at this review; absent from v0.8.136-0-g458730bf5373 (September 30, 2026).

voice_get_samples() reads the whole CFG_FMT word once at callback entry and decodes a local const MCPXAPUVoiceFormat. Uses inside that callback share the decoded format. This is a narrow reduction in repeated physical reads and field extraction, not a persistent cache of voice controls or all state used by voice_process().

Delivery matrix

Mechanism Current state Scope / limitation
Callback-local sample format Merged #191, November 1 Immutable local value; rebuilt for the next sample-fetch call
Persistent prepared voice control Unimplemented research No general guest-word ownership/invalidation scheme delivered
Raw-word/pitch/filter-coefficient experiments #184, #194, #196 closed/unmerged Historical investigations; not available in main
Fresh voice-state mapping #332 open/HOLD Separate held mapping proposal
Attenuation lookup #193 closed/unmerged Measured mixed/negative research; current attenuate() retains powf()

Before #191

flowchart LR
    A[Sample-fetch callback] --> B[Read CFG_FMT field]
    B --> C[Read another field from same CFG_FMT word]
    C --> D[Repeat field reads in callback]
    D --> E[Fetch / convert / advance]
Loading

Current callback-local flow

flowchart TD
    A[voice_get_samples entry] --> B[Read full current CFG_FMT word once]
    B --> C[mcpx_apu_decode_voice_format]
    C --> D[Local const MCPXAPUVoiceFormat]
    D --> E[Use format fields during this callback]
    F[Read live cursor / voice state / other controls] --> E
    E --> G[Fetch / convert / commit existing state]
    G --> H[Discard local format on return]
    H --> I[Next callback reads CFG_FMT afresh]
Loading

The decoded value contains stereo/channel count, sample size, container-size index, stream/loop flags, samples per block, persist, multipass and linked flags. Dynamic pause state, offsets, endpoints and other guest voice words retain their own reads. Other functions still read their own CFG_FMT fields; #191 does not hoist all format/control reads across the complete voice-processing pipeline.

Source-shaped pseudocode:

const uint32_t cfg_fmt = voice_get_mask(d, v, NV_PAVS_VOICE_CFG_FMT,
                                       UINT32_MAX);
const MCPXAPUVoiceFormat format = mcpx_apu_decode_voice_format(cfg_fmt);
/* Consume format within this voice_get_samples() invocation only. */

The resulting observation boundary is one sample-fetch callback. Do not claim visibility of a new CFG_FMT write after each old field-read location within that callback. The next callback rebuilds the value. Tests should cover writes at the declared boundary and retain the distinction from a broader 32-sample or persistent snapshot policy.

Remaining preparation design

Routing bins, gains, attenuation inputs, pitch preparation and filter coefficients can repeat while dynamic playback continues. A broader design would group raw words, compare fresh values or prove ownership, and recompute only their derived scalars. It must keep sample position, envelopes, notifications, resampler state, HRTF history and state-variable-filter history live.

A proposed, unimplemented split is:

fresh guest words under a defined ownership window
    -> decode selected routing/format/control groups
    -> prepare scalar gains or coefficient values
    -> operate on authoritative per-voice mutable histories
    -> commit only guest-visible state that changed

A complete sv_filter must not be copied as a value object: members can point into that object's own storage. Cache scalar coefficients separately from the authoritative history (h, b, l, p, n) and its output pointer. A zero-pitch shortcut is a separate numerical/lifecycle question, not an automatic part of #191.

Guest authority and invalidation

A guest can write voice records directly. A cache invalidated only through APU MMIO methods can therefore return stale state. Persistent reuse requires fresh raw-word comparison or an established write/ownership mechanism for each group. No single global clean bit proves every voice and field unchanged.

Relevant events include voice lock/unlock or guest record writes; start/stop/reuse; format/channel/address changes; routing/volume/headroom changes; filter/HRTF targets; loops and streaming segments; reset/snapshot restore; and engine/monitor changes when they affect the group. A laboratory writer must exercise mid-block writes rather than merely replaying unchanged records.

Exact-oriented preparation preserves the established observation boundary. Assuming later visibility for a write without proof is an approximation and needs its own stated contract. Callback-local #191 must not be used as evidence that a persistent control object is safe.

Evidence, risks and next experiment

#191 is the current local-format implementation and qualification history. It does not qualify the closed or held persistent experiments, establish full audio-output equivalence for every title, or provide a new November-main game-performance campaign. The attenuation page retains that candidate's measured result separately.

Before expanding scope, count current guest-word reads and decodes by group. Measure local-format builds, prospective reuse/miss counts, invalidations and source changes, commit writes, preparation CPU and whole VP/APU cost. Add one group at a time and compare output and guest-visible state after each block.

Negative controls should change a route, gain, coefficient, format or address; modify a field during a block; reuse one voice handle; change A→B→A-like values across lifetimes; exercise partial/failed updates; and save/restore with active envelopes, filters and HRTF. Stop or narrow the cache when fresh reads/invalidation cost removes the gain or ownership cannot be established. A faster decoder is insufficient if cadence/tails or output regress.

Source and related work

Clone this wiki locally