Skip to content

audio: optional float pipeline end-to-end (opt-in, s16 default)#1116

Closed
bassdr wants to merge 3 commits into
Kenix3:mainfrom
bassdr:feature/float-audio-pipeline
Closed

audio: optional float pipeline end-to-end (opt-in, s16 default)#1116
bassdr wants to merge 3 commits into
Kenix3:mainfrom
bassdr:feature/float-audio-pipeline

Conversation

@bassdr

@bassdr bassdr commented May 30, 2026

Copy link
Copy Markdown

AudioPlayer gains a parallel float-precision pipeline that consumers can
opt into via AudioSettings::UseFloatPipeline. The s16 path is unchanged
and remains the default, so existing libultraship consumers keep their
byte-exact contract; SoH flips to float when FluidSynth is enabled.

Based on top of #1106
Closes #1106

New entry points

  • AudioPlayer::Play(const float*, size_t frames) alongside the legacy
    Play(const uint8_t*, size_t len). Each Play asserts it's called in
    the matching mode and drops the buffer with a warning otherwise.
  • AudioPlayer::SetUseFloatPipeline(bool) — runtime mode switch via
    DoClose → flip → DoInit. Reverts on failure.
  • AudioPlayer::SetMixSource(std::function<void(float*, int)>) — a
    secondary stereo source mixed in after the resampler so its
    contribution skips the rate-conversion step entirely. Returns false
    in s16 mode. AudioPlayer sums the source with a tanh-style soft-clip
    before any surround decode.
  • audiobridge: AudioPlayerPlayFrame (legacy uint8_t) preserved;
    AudioPlayerPlayFrameF32 added for the float path.

DSP layer

  • AudioResampler::Process gains a float overload that operates on
    interleaved float [-1, 1]. The original int16_t overload is preserved
    and wraps the float core with boundary conversions and clamping.
  • SoundMatrixDecoder::Process likewise: native float overload plus a
    legacy uint8_t (s16) overload that converts at the boundaries. Both
    reuse the float-internal filter / phase / delay state.

Backends

  • SDL / PipeWire / WASAPI / CoreAudio DoInit reads
    IsUsingFloatPipeline() and configures the device format (F32 vs S16).
    Buffered() divides by the matching sample size.
  • PipeWire's ring init, sample width in OnProcess, and underrun
    fade-out math all branch on the same flag.

AudioPlayer reorder in float mode

  • Stages: resample stereo → optional MixSource sum + soft-clip →
    surround decode (matrix-5.1) → DoPlay. Lets a secondary source
    produced at GetSampleRate() bypass the resampler.
  • Resampler channel count differs between modes: stereo (2) in float
    mode (mix + surround decode follow), GetNumOutputChannels() in s16
    mode (legacy decode-first order preserved). RebuildResampler() picks
    the right value on Init / SetUseFloatPipeline / SetAudioChannels.

@bassdr

bassdr commented May 30, 2026

Copy link
Copy Markdown
Author

Close #1106

@bassdr bassdr force-pushed the feature/float-audio-pipeline branch 2 times, most recently from 3638656 to e220260 Compare June 1, 2026 22:53
@bassdr bassdr force-pushed the feature/float-audio-pipeline branch 3 times, most recently from c5705b9 to 57defdc Compare June 12, 2026 15:14
bassdr and others added 3 commits June 17, 2026 20:45
Add AudioResampler, a polyphase windowed-sinc resampler supporting
arbitrary integer ratios. Designed for N64 audio upsampling from
32000 Hz to 48000 Hz (exact ratio 3/2, P=3 Q=2, 8 taps/phase,
Kaiser window beta=6, ~60 dB stopband attenuation).

- AudioSettings gains SourceSampleRate (default 0 = passthrough)
- AudioPlayer::Play() resamples transparently before DoPlay() when
  SourceSampleRate != SampleRate
- GetDesiredBuffered() scales from source rate to output rate so
  OTRAudio_Thread fill logic remains coherent
- Resample output uses a fixed std::array<int16_t, 16384> — no heap
  allocation on the audio hot path
- Default SampleRate changed from 44100 to 48000 Hz
- AudioPlayer destructor made virtual to fix UB in derived class dtors
AudioPlayer gains a parallel float-precision pipeline that consumers can
opt into via AudioSettings::UseFloatPipeline. The s16 path is unchanged
and remains the default, so existing libultraship consumers keep their
byte-exact contract; SoH flips to float when FluidSynth is enabled.

New entry points
- AudioPlayer::Play(const float*, size_t frames) alongside the legacy
  Play(const uint8_t*, size_t len). Each Play asserts it's called in
  the matching mode and drops the buffer with a warning otherwise.
- AudioPlayer::SetUseFloatPipeline(bool) — runtime mode switch via
  DoClose → flip → DoInit. Reverts on failure.
- AudioPlayer::SetMixSource(std::function<void(float*, int)>) — a
  secondary stereo source mixed in *after* the resampler so its
  contribution skips the rate-conversion step entirely. Returns false
  in s16 mode. AudioPlayer sums the source with a tanh-style soft-clip
  before any surround decode.
- audiobridge: AudioPlayerPlayFrame (legacy uint8_t) preserved;
  AudioPlayerPlayFrameF32 added for the float path.

DSP layer
- AudioResampler::Process gains a float overload that operates on
  interleaved float [-1, 1]. The original int16_t overload is preserved
  and wraps the float core with boundary conversions and clamping.
- SoundMatrixDecoder::Process likewise: native float overload plus a
  legacy uint8_t (s16) overload that converts at the boundaries. Both
  reuse the float-internal filter / phase / delay state.

Backends
- SDL / PipeWire / WASAPI / CoreAudio DoInit reads
  IsUsingFloatPipeline() and configures the device format (F32 vs S16).
  Buffered() divides by the matching sample size.
- PipeWire's ring init, sample width in OnProcess, and underrun
  fade-out math all branch on the same flag.

AudioPlayer reorder in float mode
- Stages: resample stereo → optional MixSource sum + soft-clip →
  surround decode (matrix-5.1) → DoPlay. Lets a secondary source
  produced at GetSampleRate() bypass the resampler.
- Resampler channel count differs between modes: stereo (2) in float
  mode (mix + surround decode follow), GetNumOutputChannels() in s16
  mode (legacy decode-first order preserved). RebuildResampler() picks
  the right value on Init / SetUseFloatPipeline / SetAudioChannels.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@bassdr bassdr force-pushed the feature/float-audio-pipeline branch from 57defdc to a913932 Compare June 18, 2026 00:46
@bassdr

bassdr commented Jun 23, 2026

Copy link
Copy Markdown
Author

Changes moved in HarbourMasters/Shipwright#6668, now self-contained, not needed in LUS anymore.

@bassdr bassdr closed this Jun 23, 2026
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.

1 participant