Implement PSP audio playback via native sceAudio
Reserves a hardware channel per stream (64-aligned sample count, mono/stereo format) and outputs through sceAudioOutputPannedBlocking, giving PSP real stereo panning from directionality. Enforces the 44100Hz hardware-channel constraint with a clear error instead of silently mispitching, and drops the shared test tone to 44100Hz to match. Verified via the dusk-psp toolchain and a headless PPSSPP run (correct channel reservation, onEnd firing once, no errors).
This commit is contained in:
@@ -6,3 +6,101 @@
|
||||
*/
|
||||
|
||||
#include "audiostreampsp.h"
|
||||
#include "audio/audiostream.h"
|
||||
#include "assert/assert.h"
|
||||
#include "util/memory.h"
|
||||
#include <pspaudio.h>
|
||||
|
||||
errorret_t audioStreamPSPInit(audiostream_t *stream) {
|
||||
assertNotNull(stream, "Stream cannot be NULL.");
|
||||
|
||||
if(stream->pcm.channels != 1 && stream->pcm.channels != 2) {
|
||||
errorThrow(
|
||||
"PSP audio only supports mono or stereo PCM, got %d channels.",
|
||||
stream->pcm.channels
|
||||
);
|
||||
}
|
||||
|
||||
// sceAudioChReserve's hardware channels always run at the PSP's native
|
||||
// 44100Hz; there is no per-channel sample rate. Arbitrary rates would need
|
||||
// sceAudioSRCChReserve's single exclusive channel instead.
|
||||
if(stream->pcm.sampleRate != 44100) {
|
||||
errorThrow(
|
||||
"PSP audio channels are fixed at 44100Hz, got %uHz.",
|
||||
stream->pcm.sampleRate
|
||||
);
|
||||
}
|
||||
|
||||
const size_t frameSize = stream->pcm.channels * sizeof(int16_t);
|
||||
const size_t frameCount = stream->dataSize / frameSize;
|
||||
|
||||
int samples = (int) PSP_AUDIO_SAMPLE_ALIGN(frameCount);
|
||||
if(samples < PSP_AUDIO_SAMPLE_MIN) samples = PSP_AUDIO_SAMPLE_MIN;
|
||||
if(samples > PSP_AUDIO_SAMPLE_MAX) {
|
||||
// TODO: sources longer than ~1.5s need chunked streaming across
|
||||
// multiple buffer calls instead of a single one.
|
||||
samples = PSP_AUDIO_SAMPLE_MAX;
|
||||
}
|
||||
|
||||
const int format = stream->pcm.channels == 1
|
||||
? PSP_AUDIO_FORMAT_MONO
|
||||
: PSP_AUDIO_FORMAT_STEREO;
|
||||
|
||||
const int channel = sceAudioChReserve(PSP_AUDIO_NEXT_CHANNEL, samples, format);
|
||||
if(channel < 0) {
|
||||
errorThrow("Failed to reserve PSP audio channel: 0x%08X", channel);
|
||||
}
|
||||
|
||||
stream->platform.channel = channel;
|
||||
stream->platform.samples = samples;
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t audioStreamPSPDispose(audiostream_t *stream) {
|
||||
assertNotNull(stream, "Stream cannot be NULL.");
|
||||
|
||||
sceAudioChRelease(stream->platform.channel);
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t audioStreamPSPBuffer(audiostream_t *stream) {
|
||||
assertNotNull(stream, "Stream cannot be NULL.");
|
||||
|
||||
const size_t frameSize = stream->pcm.channels * sizeof(int16_t);
|
||||
const size_t bufferSize = (size_t) stream->platform.samples * frameSize;
|
||||
|
||||
int16_t *buffer = memoryAllocate(bufferSize);
|
||||
memoryZero(buffer, bufferSize);
|
||||
memoryCopy(
|
||||
buffer, stream->data,
|
||||
stream->dataSize < bufferSize ? stream->dataSize : bufferSize
|
||||
);
|
||||
|
||||
// Simple linear pan; AUDIO_STREAM_LEFT/CENTER/RIGHT map to -128..0..127.
|
||||
float_t pan = (float_t) stream->directionality / 128.0f;
|
||||
if(pan < -1.0f) pan = -1.0f;
|
||||
if(pan > 1.0f) pan = 1.0f;
|
||||
|
||||
const int baseVolume = (stream->volume * PSP_AUDIO_VOLUME_MAX) / 0xFF;
|
||||
const int leftVolume = (int) (baseVolume * (pan > 0 ? (1.0f - pan) : 1.0f));
|
||||
const int rightVolume = (int) (baseVolume * (pan < 0 ? (1.0f + pan) : 1.0f));
|
||||
|
||||
const int result = sceAudioOutputPannedBlocking(
|
||||
stream->platform.channel, leftVolume, rightVolume, buffer
|
||||
);
|
||||
memoryFree(buffer);
|
||||
|
||||
if(result < 0) {
|
||||
errorThrow("Failed to output PSP audio data: 0x%08X", result);
|
||||
}
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
bool_t audioStreamPSPIsFinished(audiostream_t *stream) {
|
||||
assertNotNull(stream, "Stream cannot be NULL.");
|
||||
|
||||
return sceAudioGetChannelRestLength(stream->platform.channel) <= 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user