Skip to content

[SP] Audio: fix crackle in cinematics, better resampling and limiting, lower latency - #1355

Open
Rugaliz wants to merge 1 commit into
JACoders:masterfrom
Rugaliz:sp-audio-quality-and-cinematic-fix
Open

Rugaliz wants to merge 1 commit into
JACoders:masterfrom
Rugaliz:sp-audio-quality-and-cinematic-fix

Conversation

@Rugaliz

@Rugaliz Rugaliz commented Oct 1, 2026

Copy link
Copy Markdown

Summary

Fixes crackling audio during cinematics (heard in the opening logos) and improves SP sound quality and latency.

Changes

  • Cinematic crackle: cinematic audio and streamed music both append to the same raw sample ring. The main menu music is started by a console command while the opening logos video is still playing, so the two interleaved, and the first audio packet of a roq video (~0.5 s, 23520 samples at 44.1 kHz) was larger than the whole 16384-sample ring. The ring is now 65536 samples, music buffering keeps its original 16384 look-ahead (MUSIC_RAW_LOOKAHEAD), and music is not streamed while a cinematic with a soundtrack is playing (CIN_IsPlayingAudio). Frame rate was ruled out as a cause (tested caps of 30/125/250 and vsync).
  • Resampling: ResampleSfx used nearest-neighbour conversion, which mirrors high frequencies into the audible range (harsh, gritty 11/22 kHz sounds). New s_quality cvar: 0 linear/averaging, 1 cubic, 2 windowed-sinc (default). Costs ~0.35 s extra per map load (233 sounds: 373 ms vs 20 ms at s_quality 0), nothing during play. Cinematic audio is linearly interpolated instead of repeating samples. s_quality 0 is the cheap option for slow machines.
  • Limiter: hard clipping of the mix is replaced by a soft limiter that only acts above 0.75 of full scale (quieter sound is untouched). With developer 1 the log reports when the mix exceeds full scale.
  • Latency: the SDL callback buffer defaults to ~11 ms instead of ~23 ms (the mix ring keeps its previous size, s_sdlDevSamps still overrides), and the mix pre-step adapts to the device buffer and recent frame times (s_mixPreStepAuto, default 1; s_mixPreStep stays the upper bound, default 0.05). The pre-step settled at ~30 ms at 125 fps and ~28 ms at 144 fps. s_mixPreStepAuto 0 restores the old fixed behaviour.

Not included: native 48 kHz output. Voice lines under sound/chars/ stay MP3-compressed and are decoded on the fly by mp3code, which only outputs 44.1/22.05/11.025 kHz, so that would need a separate change.

Testing

  • JO SP on Linux (PipeWire): the intro logos played in full with zero ring overflow messages (previously dozens per second of video), new game cinematics and briefing, several map loads, 125/144 fps caps; no audible artifacts reported by the tester.
  • Offline check on a real 22.05 kHz game sound: energy above the source Nyquist drops by ~11 dB with the new resampler (Python re-implementation of the algorithm).
  • JA SP, the MP engine (shares sdl_sound.cpp) and JO SP build cleanly. JA SP and MP were not run, and Windows/macOS were not tested. The 32-bit MSVC OpenAL code path was not touched or tested.

Notes

Original code, contributed under the project's GPLv2 license (the spline, sinc and window formulas are standard math, no third-party code was copied). Written with AI assistance (Claude Code) and reviewed/tested by the submitter.

🤖 Generated with Claude Code

…, lower latency

* Cinematic audio and streamed music both append to the same raw sample ring. The
  main menu music is started while the opening logos video is still playing, so the
  two interleaved (heard as crackling), and the first audio packet of a roq video
  (~0.5s, 23520 samples at 44.1kHz) was larger than the whole 16384 sample ring.
  Enlarge the ring to 65536 samples, keep music buffering at its original 16384
  look-ahead (MUSIC_RAW_LOOKAHEAD) and don't stream music while a cinematic with a
  soundtrack is playing (CIN_IsPlayingAudio).
* ResampleSfx used nearest-neighbour conversion, which mirrors high frequencies back
  into the audible range (harsh, gritty 11/22kHz sounds). New s_quality cvar:
  0 linear/averaging, 1 cubic, 2 windowed-sinc (default). Costs roughly 0.35s extra
  per map load, nothing during play. Cinematic audio is linearly interpolated
  instead of repeating samples.
* Replace hard clipping of the mix with a soft limiter that only acts above 0.75 of
  full scale (quiet sound is untouched), and report clipping in the developer log.
* Latency: the SDL callback buffer defaults to ~11ms instead of ~23ms (the mix ring
  is kept at its previous size), and the mix pre-step adapts to the device buffer
  and recent frame times (s_mixPreStepAuto, default on; s_mixPreStep stays the upper
  bound, 0.05). Roughly halves the delay between an event and its sound.

Tested on JO SP (intro, new game cinematics and briefing, map loads, 144Hz/125fps
caps). JA SP and the MP engine (shares sdl_sound.cpp) were build-tested only.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@Rugaliz
Rugaliz requested a review from a team as a code owner October 1, 2026 21:30
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