diff --git a/src/duskpsp/audio/audiostreampsp.c b/src/duskpsp/audio/audiostreampsp.c index 940dad6a..46cdc00c 100644 --- a/src/duskpsp/audio/audiostreampsp.c +++ b/src/duskpsp/audio/audiostreampsp.c @@ -32,6 +32,12 @@ // just stopping outright on an exact-multiple-length one). #define AUDIO_PSP_FADE_FRAMES 32 +// A couple of extra all-silence chunks fed after the real audio (and +// after the fade above), so the channel keeps being actively driven at +// zero for a moment rather than stopping outright - mitigates a possible +// hardware/DAC settling pop on top of the digital-domain fade. +#define AUDIO_PSP_TRAILING_SILENT_CHUNKS 2 + errorret_t audioStreamPSPInit(audiostream_t *stream) { assertNotNull(stream, "Stream cannot be NULL."); @@ -106,13 +112,13 @@ void audioStreamPSPThreadFeed(thread_t *thread) { const size_t totalFrames = stream->dataSize / frameSize; const size_t chunkSize = AUDIO_PSP_CHUNK_FRAMES * frameSize; - // A previous playback pass may have left this shortened for its own - // final partial chunk (see below) - restore the full chunk length. - sceAudioSetChannelDataLen(stream->platform.channel, AUDIO_PSP_CHUNK_FRAMES); - int16_t *chunk = memoryAllocate(chunkSize); size_t position = 0; + // Every call always sends a full, constant-size AUDIO_PSP_CHUNK_FRAMES + // buffer - the channel is never re-declared to a different length, to + // avoid relying on sceAudioSetChannelDataLen's undocumented behavior + // mid-stream. The final chunk is zero-padded out to that full size. while(!threadShouldStop(thread) && position < totalFrames) { const size_t framesRemaining = totalFrames - position; const size_t framesThisChunk = framesRemaining < AUDIO_PSP_CHUNK_FRAMES @@ -127,7 +133,7 @@ void audioStreamPSPThreadFeed(thread_t *thread) { if(isLastChunk) { // Fade the tail down to zero so the waveform never stops (or meets - // the zero-padding below) at a non-zero amplitude - that abrupt + // the zero-padding above) at a non-zero amplitude - that abrupt // jump is what was heard as a click at the end of playback. const size_t fadeFrames = framesThisChunk < AUDIO_PSP_FADE_FRAMES ? framesThisChunk @@ -140,14 +146,6 @@ void audioStreamPSPThreadFeed(thread_t *thread) { *sample = (int16_t) ((float_t) *sample * factor); } } - - // Only declare the real sample count for output instead of the full - // reserved chunk size, 64-aligned (sceAudioChReserve's own - // constraint - not documented for this call, but not worth risking). - const int alignedFrames = (int) PSP_AUDIO_SAMPLE_ALIGN(framesThisChunk); - if((size_t) alignedFrames != AUDIO_PSP_CHUNK_FRAMES) { - sceAudioSetChannelDataLen(stream->platform.channel, alignedFrames); - } } // Re-read every chunk (~23ms at 44100Hz) so SetVolume/SetDirectionality @@ -167,6 +165,17 @@ void audioStreamPSPThreadFeed(thread_t *thread) { position += framesThisChunk; } + // A couple of extra all-silence chunks so the channel keeps being + // actively driven at zero for a moment rather than stopping outright. + memoryZero(chunk, chunkSize); + for( + uint8_t i = 0; + !threadShouldStop(thread) && i < AUDIO_PSP_TRAILING_SILENT_CHUNKS; + i++ + ) { + sceAudioOutputBlocking(stream->platform.channel, 0, chunk); + } + memoryFree(chunk); stream->platform.finished = true; }