Fixed PSP trying to load entirely into memory
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
#include "audio/audiostream.h"
|
||||
#include "assert/assert.h"
|
||||
#include "util/memory.h"
|
||||
#include "util/math.h"
|
||||
#include <ansndlib.h>
|
||||
#include <ogc/cache.h>
|
||||
#include <ogc/system.h>
|
||||
@@ -25,6 +26,7 @@ errorret_t audioStreamDolphinInit(audiostream_t *stream) {
|
||||
|
||||
stream->platform.voiceId = voiceId;
|
||||
stream->platform.finished = false;
|
||||
stream->platform.buffer = NULL;
|
||||
|
||||
errorOk();
|
||||
}
|
||||
@@ -34,6 +36,11 @@ errorret_t audioStreamDolphinDispose(audiostream_t *stream) {
|
||||
|
||||
ansnd_deallocate_voice((u32) stream->platform.voiceId);
|
||||
|
||||
if(stream->platform.buffer != NULL) {
|
||||
memoryFree(stream->platform.buffer);
|
||||
stream->platform.buffer = NULL;
|
||||
}
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
@@ -41,6 +48,28 @@ errorret_t audioStreamDolphinBuffer(audiostream_t *stream) {
|
||||
assertNotNull(stream, "Stream cannot be NULL.");
|
||||
|
||||
const size_t frameSize = stream->pcm.channels * sizeof(int16_t);
|
||||
const size_t totalFrames = audioStreamPcmGetTotalFrames(stream);
|
||||
|
||||
// loopEndFrame/loopToFrame define the loop segment [loopToFrame,
|
||||
// loopEndFrame) the DSP wraps within, once looping is enabled -
|
||||
// defaulting to the whole clip (loopStart == -1, loopTo == 0) so
|
||||
// behaviour is unchanged when no explicit loop points are configured.
|
||||
// Same math as PSP/Linux - see their own comments.
|
||||
const size_t loopEndFrame = stream->loopStart >= 0
|
||||
? mathMin((size_t) (stream->loopStart * stream->pcm.sampleRate), totalFrames)
|
||||
: totalFrames;
|
||||
const size_t loopToFrame = mathMin(
|
||||
(size_t) (stream->loopTo * stream->pcm.sampleRate), loopEndFrame
|
||||
);
|
||||
|
||||
const size_t startFrame = mathMin(stream->startFrame, totalFrames);
|
||||
stream->startFrame = 0;
|
||||
stream->seeking = false;
|
||||
|
||||
// A seek can legitimately land past the loop segment's end (e.g. into an
|
||||
// outro) - buffer out to the true end of the clip in that case, same as
|
||||
// PSP/Linux, instead of only ever buffering the loop segment.
|
||||
const size_t bufferFrames = startFrame < loopEndFrame ? loopEndFrame : totalFrames;
|
||||
|
||||
float_t leftFactor, rightFactor;
|
||||
audioStreamGetPanFactors(stream, &leftFactor, &rightFactor);
|
||||
@@ -49,6 +78,37 @@ errorret_t audioStreamDolphinBuffer(audiostream_t *stream) {
|
||||
|
||||
stream->platform.finished = false;
|
||||
|
||||
// ansnd's hardware loop needs one contiguous buffer spanning everything
|
||||
// the voice might play - both start_offset and the loop segment address
|
||||
// the same frame_data_ptr - so (unlike PSP/Linux, which stream bounded
|
||||
// windows on demand) this reads the whole segment up front. Still
|
||||
// bounded to the current loop segment's length rather than the whole
|
||||
// clip if the clip is longer than one loop - see this struct field's
|
||||
// own comment. Re-read fresh every Buffer() call.
|
||||
if(stream->platform.buffer != NULL) {
|
||||
memoryFree(stream->platform.buffer);
|
||||
stream->platform.buffer = NULL;
|
||||
}
|
||||
|
||||
errorChain(audioStreamPcmSeek(stream, 0));
|
||||
uint8_t *buffer = (uint8_t *) memoryAllocate(bufferFrames * frameSize);
|
||||
size_t framesRead = 0;
|
||||
errorret_t readRet = audioStreamPcmRead(
|
||||
stream, (int16_t *) buffer, bufferFrames, &framesRead
|
||||
);
|
||||
if(errorIsNotOk(readRet)) {
|
||||
memoryFree(buffer);
|
||||
errorChain(readRet);
|
||||
}
|
||||
if(framesRead < bufferFrames) {
|
||||
// The asset is shorter than its declared header size (corrupt or
|
||||
// truncated) - pad what's missing with silence.
|
||||
memoryZero(
|
||||
buffer + (framesRead * frameSize), (bufferFrames - framesRead) * frameSize
|
||||
);
|
||||
}
|
||||
stream->platform.buffer = buffer;
|
||||
|
||||
ansnd_pcm_voice_config_t config;
|
||||
memoryZero(&config, sizeof(ansnd_pcm_voice_config_t));
|
||||
config.samplerate = stream->pcm.sampleRate;
|
||||
@@ -63,12 +123,10 @@ errorret_t audioStreamDolphinBuffer(audiostream_t *stream) {
|
||||
// ANSND_ERROR_INVALID_MEMORY) - it needs the physical address instead.
|
||||
// Flush first so the DMA sees what the CPU actually wrote, not stale
|
||||
// memory contents.
|
||||
DCFlushRange(stream->data, stream->dataSize);
|
||||
config.frame_data_ptr = MEM_VIRTUAL_TO_PHYSICAL(stream->data);
|
||||
config.frame_count = (u32) (stream->dataSize / frameSize);
|
||||
config.start_offset = (u32) stream->startFrame;
|
||||
stream->startFrame = 0;
|
||||
stream->seeking = false;
|
||||
DCFlushRange(buffer, bufferFrames * frameSize);
|
||||
config.frame_data_ptr = MEM_VIRTUAL_TO_PHYSICAL(buffer);
|
||||
config.frame_count = (u32) bufferFrames;
|
||||
config.start_offset = (u32) startFrame;
|
||||
config.voice_callback = audioStreamDolphinVoiceCallback;
|
||||
config.stream_callback = NULL; // single-buffer playback
|
||||
config.user_pointer = stream;
|
||||
@@ -87,10 +145,8 @@ errorret_t audioStreamDolphinBuffer(audiostream_t *stream) {
|
||||
// loopTo's (true today only because the shared 441Hz test tone was
|
||||
// deliberately chosen to divide evenly into the sample rate).
|
||||
if(stream->state & AUDIO_STREAM_STATE_LOOPING) {
|
||||
config.loop_start_offset = (u32) (stream->loopTo * stream->pcm.sampleRate);
|
||||
config.loop_end_offset = stream->loopStart >= 0
|
||||
? (u32) (stream->loopStart * stream->pcm.sampleRate) - 1
|
||||
: config.frame_count - 1;
|
||||
config.loop_start_offset = (u32) loopToFrame;
|
||||
config.loop_end_offset = (u32) loopEndFrame - 1;
|
||||
}
|
||||
|
||||
s32 result = ansnd_configure_pcm_voice((u32) stream->platform.voiceId, &config);
|
||||
|
||||
@@ -19,6 +19,19 @@ typedef struct {
|
||||
// itself (unlike PSP/Linux), so this flag is how audioStreamDolphinIsFinished()
|
||||
// answers without a callback of its own.
|
||||
volatile bool_t finished;
|
||||
|
||||
// ansnd's DSP hardware loop (loop_start_offset/loop_end_offset) needs one
|
||||
// contiguous physical buffer spanning the whole segment it may play -
|
||||
// both the pre-loop lead-in and the loop segment itself, since the DSP
|
||||
// addresses both as offsets into the same frame_data_ptr. Unlike
|
||||
// PSP/Linux (which stream bounded windows on demand), a Dolphin voice
|
||||
// can't be handed data incrementally mid-playback, so this buffer is
|
||||
// read from the asset and owned here for the lifetime of one pass -
|
||||
// bounded to the current loop segment's length (not the whole clip, if
|
||||
// the clip is longer than one loop), freed/reallocated on each
|
||||
// audioStreamDolphinBuffer() call and released for good in
|
||||
// audioStreamDolphinDispose().
|
||||
uint8_t *buffer;
|
||||
} audiostreamdolphin_t;
|
||||
|
||||
/**
|
||||
@@ -40,7 +53,8 @@ errorret_t audioStreamDolphinInit(audiostream_t *stream);
|
||||
errorret_t audioStreamDolphinDispose(audiostream_t *stream);
|
||||
|
||||
/**
|
||||
* Sends the stream's currently staged PCM data (stream->data) to its
|
||||
* Reads the current loop segment's PCM data from the stream's asset (see
|
||||
* audiostreamdolphin_t.buffer's own comment) and sends it to its
|
||||
* allocated voice.
|
||||
*
|
||||
* @param stream The audio stream to output.
|
||||
|
||||
Reference in New Issue
Block a user