Fix crackling PSP audio: feed hardware in small chunks from a thread
The original implementation reserved a channel sized to the whole buffer (~44160 samples) and sent it in one sceAudioOutputPannedBlocking call. Confirmed against the PSP SDK's own samples (pspaudiolib, the mp3 sample) that this deviates from how PSP audio hardware is actually meant to be driven: small fixed-size chunks (1024 frames, matching pspaudiolib's own convention) fed continuously, decoupled from the render loop. Reproduced as crackling on real hardware. Now reserves a 1024-frame channel and runs a dedicated thread (the project's existing thread_t abstraction, already used by asset.c and already working on PSP via DUSK_THREAD_PTHREAD) that streams the buffer chunk-by-chunk until exhausted, re-reading volume/pan every chunk so live changes take effect mid-playback. Finish detection switched from polling sceAudioGetChannelRestLength (which can't tell "between chunks" from "actually done" once chunked) to a flag the feeder thread sets on completion, same shape as the Dolphin voice callback. Verified via the dusk-psp toolchain and PPSSPP headless (channel now reserved at 1024 frames instead of 44160, onEnd still fires once). Real-hardware crackle-free confirmation pending.
This commit is contained in:
@@ -7,16 +7,22 @@
|
||||
|
||||
#pragma once
|
||||
#include "error/error.h"
|
||||
#include "thread/thread.h"
|
||||
|
||||
typedef struct audiostream_s audiostream_t;
|
||||
|
||||
typedef struct {
|
||||
// Reserved hardware output channel, from sceAudioChReserve.
|
||||
// Reserved hardware output channel, from sceAudioChReserve. Reserved with
|
||||
// a small fixed chunk size (AUDIO_PSP_CHUNK_FRAMES) - PSP audio hardware
|
||||
// expects continuous small-chunk feeding, not one large buffer per call.
|
||||
int channel;
|
||||
|
||||
// Number of samples (frames) per output call the channel was reserved
|
||||
// with. 64-aligned, between PSP_AUDIO_SAMPLE_MIN and PSP_AUDIO_SAMPLE_MAX.
|
||||
int samples;
|
||||
// Dedicated thread that feeds the channel chunk-by-chunk for the
|
||||
// duration of playback, independent of the engine's frame rate.
|
||||
thread_t thread;
|
||||
|
||||
// Set by audioStreamPSPThreadFeed() once it has fed the last chunk.
|
||||
volatile bool_t finished;
|
||||
} audiostreampsp_t;
|
||||
|
||||
/**
|
||||
@@ -29,8 +35,8 @@ typedef struct {
|
||||
errorret_t audioStreamPSPInit(audiostream_t *stream);
|
||||
|
||||
/**
|
||||
* Disposes the PSP-specific playback state of an audio stream, releasing
|
||||
* its hardware output channel.
|
||||
* Disposes the PSP-specific playback state of an audio stream, stopping its
|
||||
* feeder thread and releasing its hardware output channel.
|
||||
*
|
||||
* @param stream The audio stream to dispose.
|
||||
* @return Error state if any.
|
||||
@@ -38,8 +44,8 @@ errorret_t audioStreamPSPInit(audiostream_t *stream);
|
||||
errorret_t audioStreamPSPDispose(audiostream_t *stream);
|
||||
|
||||
/**
|
||||
* Sends the stream's currently staged PCM data (stream->data) to its
|
||||
* reserved hardware output channel.
|
||||
* Starts the stream's feeder thread, which streams stream->data to its
|
||||
* reserved hardware output channel in small chunks until exhausted.
|
||||
*
|
||||
* @param stream The audio stream to output.
|
||||
* @return Error state if any.
|
||||
@@ -47,10 +53,20 @@ errorret_t audioStreamPSPDispose(audiostream_t *stream);
|
||||
errorret_t audioStreamPSPBuffer(audiostream_t *stream);
|
||||
|
||||
/**
|
||||
* Checks whether the stream's reserved hardware channel has finished
|
||||
* playing its currently buffered data.
|
||||
* Checks whether the stream's feeder thread has finished feeding its
|
||||
* currently buffered data.
|
||||
*
|
||||
* @param stream The audio stream to check.
|
||||
* @return true if playback has finished, false otherwise.
|
||||
*/
|
||||
bool_t audioStreamPSPIsFinished(audiostream_t *stream);
|
||||
|
||||
/**
|
||||
* Feeder thread entry point. Streams stream->data (passed via thread->data)
|
||||
* to its hardware channel in fixed-size chunks, blocking naturally on each
|
||||
* sceAudioOutputPannedBlocking() call, until the whole buffer has been sent
|
||||
* or the thread is asked to stop.
|
||||
*
|
||||
* @param thread The running thread_t, with data set to the audiostream_t.
|
||||
*/
|
||||
void audioStreamPSPThreadFeed(thread_t *thread);
|
||||
|
||||
Reference in New Issue
Block a user