AUDIO
Source snapshot · 12 October 2026

Native DSP boundary

ravet-native-dsp is an optional workspace crate for explicitly selected native numerical behaviour. It provides two independent capabilities: scoped current-thread floating-point policy, and explicit packed values with prepared vector slice operations. The core is no_std, needs no allocator and has empty default features. Only its private native modules may use unsafe; the crate otherwise denies it, and the portable crates keep the workspace unsafe-forbid. The umbrella ravet crate does not re-export it. Decisions: 0266 and 0267.

Topics in this guide

Explicit native floating-point policy

ravet-native-dsp is a separate optional numerical boundary. Portable DSP defaults remain unchanged. Its core is no_std and needs no allocator; the optional processor feature depends on the portable Foundation contracts. Native register access is confined to a private audited module. There is no implicit per-filter policy, global thread configuration, hidden state snapping or device selection.

Choose DenormalMode::Preserve to retain the actual current controls, or FlushBoth to request f32/f64 operand and result flushing after actual capability admission. ScopedDenormalMode::enter returns a non-Copy, non-Clone, non-Send, non-Sync guard bound to its creating thread. Drop restores the exact prior register control and sticky status, including normal return, early return, unwind and nested scopes. Thus even Preserve isolates scope-created sticky flags; it does not merge them into the caller's cumulative observations. Forget, abort or foreign longjmp can prevent Drop. Other code executed on this same thread inside the scope observes that environment.

The supported register families are ordinary OS user-mode AArch64 FPCR/FPSR and x86_64 SSE2 MXCSR. Architecture selection alone does not prove privileged, bare-metal or sandbox register access. The x86 scope neither controls nor restores x87 state. It inspects the actual MXCSR_MASK using a pre-zeroed aligned 512-byte FXSAVE area; a zero hardware mask uses the architectural 0xffbf fallback, which lacks DAZ. Missing DAZ/FTZ support or any unmasked SIMD exception rejects FlushBoth before changing controls. Entry performs one capability save, or two when a changed control is read back; this is bounded work, not a deadline claim. On AArch64, FZ is admitted only with alternate handling AH=0 and all declared floating exception trap-enable bits clear. It changes neither rounding, default NaN, FZ16 nor other controls. A rejected control read-back restores the original control/status before returning its original error. Unsupported architectures return an error rather than a successful no-op called flushing.

current_float_state exposes read-only actual backend/control/status, the x86 mask and standard-profile flush observations. It is not a register setter. AArch64 AH=1 is not interpreted as this standard profile. x86 result flushing also requires masked underflow. The crate makes no universal claim that an optimized Rust expression or external math function executes precisely the source arithmetic: constants, identity rewrites, library/compiler floating assumptions and fused instructions remain separate issues. Runtime instructions and emitted code are qualified explicitly. NaN/Inf classification is tested; no cross-platform NaN payload or universal bitwise scalar parity is promised.

With processor, DenormalProcessor<P> owns an existing portable Processor and an immutable selected mode. Constructor refusal returns the exact uncalled P and original native error. Its constructor probes only the constructing thread. Borrowed prepare reports the child's actual PrepareError and retains adapter ownership on failure; a CONTROL-thread probe cannot qualify a future AUDIO thread. Each process call validates the entire configuration, block, buses and events before entering the actual thread scope or calling P. Native refusal silences/discards all active outputs/events without consuming child history, retains the first original native error, and disables retries until explicit reset/reprepare. Child faults or panics may have committed histories; no rollback is inferred. Child faults silence outputs, and an unwound process stays disabled until explicit reset/reprepare. Scope Drop still restores the registers.

Manual start_audio/stop_audio hooks must be paired on the AUDIO owner before CONTROL deactivation or transfer. Graph's explicit session guard supplies that pairing; consumers using manual hooks must do it themselves. No adapter Drop implicitly stops/deactivates P. into_inner returns the original P only when stopped/deactivated; refusal retains the complete adapter without allocating. A child lifecycle panic remains uncertain until explicit successful cleanup; stop alone cannot prove a panicked prepare left P inactive. Reset is the child's existing contract, not a guarantee that any external native backend can recover.

The headless scoped-policy example takes exactly preserve or flush-both, observes runtime operand flushing and prints prior/during/restored state. Tests qualify signed f32/f64 subnormal operands/results, normal and special classes, actual sticky/unrelated control restoration, thread-local/nested/unwind ownership, synthetic mask/AH/trap/read-back refusals, late-event preflight, failure-owned constructor/prepare/extraction and foreign child faults. Actual Foundation → Graph → scoped native policy → native32/native64 TPT IIR/FIR compositions render separate and in-place buffers, variable/zero blocks and two explicit session cycles, with independent transfer-equation/convolution checks and no callback heap activity. This does not imply physical audio, a host or deadline support.

The qualification host executes AArch64 natively and separately executes thin x86_64 Mach-O tests through Rosetta. The latter proves this translated register profile, not physical Intel/AMD hardware. Both release assembly profiles contain the declared register controls and runtime floating multiply instructions. The scope provides no automatic vectorization; the explicit packed values and prepared vector operations below are a separate API that never changes this policy.

Explicit packed values and prepared vector operations

F32x4 and F64x2 provide fixed packed lanes with immutable explicit VectorBackend::{Scalar,Aarch64Neon,X86Sse2} selection. VectorBackend::native() is a compile-time admission: aarch64 with target_feature = "neon" or x86_64 with target_feature = "sse2", the ordinary OS user-mode baseline. Any other target, including one built with those features disabled, returns UnsupportedBackend rather than silently substituting Scalar. It is not a runtime CPU probe and does not establish privileged, bare-metal or sandbox register access. The Scalar reference expresses scalar Rust arithmetic and uses the exact existing libm=0.2.16 dependency for no_std square root. Compiler auto-vectorization may optimize that reference; Scalar is not a promise to disable SIMD instructions. The native paths deliberately use audited packed intrinsics. Fixed private lane arrays support safe storage and transient register arithmetic, with no public raw pointers, alignment requirement, SIMD ABI or permanent register residency claim.

Construct from arrays, broadcast, or load/store checked complete prefixes at any slice offset. Lane reads/replacement and arbitrary permutations validate all indices. LaneMask4/LaneMask2 reject unavailable high bits, supply bit operations and checked lane tests, and support exact-bit selection. Binary values must have the same selected backend; a mismatch refuses before an output edit. Arithmetic covers add/subtract/multiply/divide, explicit unfused multiply-then-add, square root and fixed sum/dot. Absolute/negate are bit operations preserving NaN payloads without quieting them. Interleave/deinterleave provide an exact full-width pair. Arbitrary checked permutation is defined by lane copies; it does not imply a dedicated native gather instruction for every runtime index pattern.

Comparisons are ordered except not_equal, where an unordered NaN lane is true. Minimum/maximum select the left bits only for ordered less/greater; otherwise right bits are preserved, including ties, signed zero and NaN. Mask selection copies the chosen bits. An integer-only, non-inlined selection boundary prevents optimized scalar minimum/maximum instructions from rewriting selected subnormal representations under DAZ. Comparisons still observe the actual CPU input-flush policy; preserving selected bits does not disable that policy. An independent integer-encoding test oracle separates those two obligations. Arithmetic/sqrt have actual backend floating semantics, including current rounding/denormal/NaN controls and observable overflow; there is no payload parity guarantee for arithmetic NaNs, fused repair or hidden control guard. The crate does not turn every optimized Rust expression into a specified instruction sequence. Call an explicit denormal scope separately if that policy is desired. Scalar libm and native hardware are separately named implementations; numerical tests qualify the declared selected profiles.

Packed f32 sums use (lane0+lane1)+(lane2+lane3) in f32; f64 uses lane0+lane1. Dots multiply first, then use the same sum. These operations do not widen, compensate or fuse products into sums. The reduction tree is part of the API.

PreparedVectorOps32/PreparedVectorOps64 fix a selected backend and maximum 1..1,048,576 samples without allocating. Their slice family provides fill/clear/copy, elementwise add/subtract/multiply/divide/minimum/maximum, finite scalar gain/offset, absolute/negate/sqrt, ordered clip with finite endpoints, unfused add-product, sum/dot/sequential extrema, scaled i32 conversion and checked stereo interleave/deinterleave. Fill and source samples may contain nonfinite values; control gains/scales/offsets must be finite. Clip preserves input NaN bits. Its low > high range check is a floating comparison, so it observes the live input-flush policy: inside a FlushBoth scope subnormal endpoints compare as signed zeros, and for example low = +min-subnormal, high = -min-subnormal is admitted although its bit order is inverted. Selection then follows the same flushed comparisons while copying the original endpoint/input bits. All requested prefixes/control values and checked 2N lengths are admitted before any edit. Backing suffixes remain untouched; safe distinct mutable borrows define the alias contract. There is no implicit aligned allocation or callback heap.

Native chunks use the full fixed width. Remaining arithmetic elements execute scalar operations; native sqrt tails use actual scalar hardware sqrt, while Scalar tails use libm. No incomplete chunk overreads or unused-lane division/sqrt exceptions are introduced. Slice sums/dots left-fold packed pairwise chunk totals, then left-fold scalar tail values, starting at +0. Thus they have an explicit width-dependent tree and can differ from another partition/tree. Slice extrema fold in source order using the same right-preference rule and return None on an empty prefix. Raw i32 conversion is exact in f64 and rounded in f32; scaling follows afterward in sample precision without saturating or concealing overflow.

Separate/in-place gain and in-place addition are available. gain_planar and gain_planar_in_place admit 1..64 matching channels/gains and every channel prefix before editing any channel. A channel-count or gain/output-count mismatch (including 0 or 65 channels), a late NaN/±infinite gain, a 64th-channel short prefix or a frame count above the maximum leaves the whole bank unchanged. Invalid counts and gains are refused at zero frames too; a short prefix cannot exist at zero frames, so such a call is admitted without edits. Requested prefixes alone are bounded; an unused longer backing slice is not rejected. Stereo N means per-channel frames and permits an independently checked 2N destination.

Qualification covers each lane/operation/backend, mask domains, checked permutations, exact-length and offset load/store, all prefix lengths 0..17, tails, suffix preservation, heap-free callback work for every slice method and an actual 1,048,576-sample prefix. Every ordered pair of 17 special encodings (signed zeros, subnormals, qNaN with distinct payloads, sNaN, infinities and finite extrema) is placed in every packed lane and every slice chunk/tail position; comparisons, minimum/maximum, selection, extrema and bit operations are checked against an integer-encoding oracle inside both explicit Preserve and FlushBoth scopes. Rounding slice operations, conversion and sqrt are bit-exact against independently written unfused scalar sequences under the host's Preserve profile, which the tests observe to have no flush control set; Preserve itself retains, rather than clears, the ambient control. Packed arithmetic has direct result-flush and input-flush witnesses on both backends; other slice arithmetic and conversion inside FlushBoth are not separately qualified. Refusal tests truncate every slice parameter of every method, exceed the maximum, shorten each 2N buffer, and cover channel-count mismatches and nonfinite controls with all buffers unchanged. A separate Decimal120 non-negative dyadic sqrt corpus of 36 inputs (including 0 and extreme normal/subnormal values) is placed in distinct rotated register lanes and tail positions. Native hardware results must match it exactly; Scalar libm is held to a 1-ULP bound, and the recorded ARM and translated x86 runs observed 0 ULP. This finite evidence is not a universal correctly-rounded claim. Subnormal sqrt inside FlushBoth stays policy-defined and unasserted: with libm's default arch feature disabled, as the workspace configures it, Scalar sqrt of the smallest positive subnormal was observed to return the input bits on both runs, while native hardware flushes. The real Foundation → Graph consumer processes three independent packed gain/mix channels followed by existing FIR in both precisions, external separate/in-place buffers, variable/zero blocks and two lifecycle cycles. The mixer stage alone, including a variant using gain_planar_in_place and add_in_place, is compared exactly (numerical equality) with the independent rounded scalar schedule; only the FIR stage keeps a tolerance. No callback heap is used.

Native AArch64 and separately Rosetta-translated x86_64 release tests and packed instruction output are recorded separately. Translation is not physical Intel/AMD support. The headless vector-ops example explicitly selects each backend and reports numerical checks plus repeated min/median/max timings for the selected 65,536-sample gain/add-product profile. Those timings establish neither universal speedup nor a deadline; compiler optimization, machine load and instruction selection remain relevant. These reusable primitives do not claim whole FFT/FIR kernels have been vectorized, or imply host/device support, AVX/AVX512, half/SVE, integer-register families, or automatic runtime vector-width dispatch.