Fix PSP audio jitter: raise feeder thread priority above main thread

Root cause of the residual jitter (correlating with framerate, per
real-hardware testing): this project never overrides
PSP_MAIN_THREAD_PRIORITY, so the main/render thread runs at PSPSDK's
default of 32. A pthread created with default attributes (as
thread_t/threadStartRequest does) runs at priority 60 - numerically
higher, meaning LOWER scheduling priority on PSP's inverted scale.
Under load the feeder thread was structurally guaranteed to lose
scheduling contention to the main thread, starving the hardware
channel's double buffer and producing audible jitter that worsens
exactly when frames get slower - confirmed via disassembling
PSPSDK's precompiled pthread glue (pte_osThreadGetDefaultPriority
returns 60) rather than guessing.

audioStreamPSPThreadFeed now self-prioritizes via
sceKernelChangeThreadPriority(sceKernelGetThreadId(), 18) as the
first thing it does, matching the elevated-priority pattern PSP SDK
samples use for their own timing-sensitive auxiliary threads.
This commit is contained in:
2026-08-31 11:14:20 -05:00
parent da275cfd52
commit 50c5621d8a
+12
View File
@@ -10,12 +10,22 @@
#include "assert/assert.h" #include "assert/assert.h"
#include "util/memory.h" #include "util/memory.h"
#include <pspaudio.h> #include <pspaudio.h>
#include <pspthreadman.h>
// Matches pspaudiolib's own PSP_NUM_AUDIO_SAMPLES convention - PSP audio // Matches pspaudiolib's own PSP_NUM_AUDIO_SAMPLES convention - PSP audio
// hardware is meant to be fed small chunks continuously, not one large // hardware is meant to be fed small chunks continuously, not one large
// buffer per call. // buffer per call.
#define AUDIO_PSP_CHUNK_FRAMES 1024 #define AUDIO_PSP_CHUNK_FRAMES 1024
// PSP priorities are inverted (lower = higher priority). This project's
// main thread runs at the PSPSDK default of 32 (PSP_MAIN_THREAD_PRIORITY
// is never overridden). A pthread created with default attributes runs at
// 60 - LOWER priority than the main/render thread - so under load the
// feeder thread starves and the hardware channel underruns, heard as
// jitter/crackle that gets worse the busier (lower-fps) a frame is. Raise
// it above main so audio feeding always wins scheduling contention.
#define AUDIO_PSP_THREAD_PRIORITY 18
errorret_t audioStreamPSPInit(audiostream_t *stream) { errorret_t audioStreamPSPInit(audiostream_t *stream) {
assertNotNull(stream, "Stream cannot be NULL."); assertNotNull(stream, "Stream cannot be NULL.");
@@ -82,6 +92,8 @@ bool_t audioStreamPSPIsFinished(audiostream_t *stream) {
void audioStreamPSPThreadFeed(thread_t *thread) { void audioStreamPSPThreadFeed(thread_t *thread) {
assertNotNull(thread, "Thread cannot be NULL."); assertNotNull(thread, "Thread cannot be NULL.");
sceKernelChangeThreadPriority(sceKernelGetThreadId(), AUDIO_PSP_THREAD_PRIORITY);
audiostream_t *stream = (audiostream_t *) thread->data; audiostream_t *stream = (audiostream_t *) thread->data;
const size_t frameSize = stream->pcm.channels * sizeof(int16_t); const size_t frameSize = stream->pcm.channels * sizeof(int16_t);
const size_t totalFrames = stream->dataSize / frameSize; const size_t totalFrames = stream->dataSize / frameSize;