PspAudioDevice class
Implementation of IAudioDevice on top of the PSP's sceAudio hardware channel.
pspdev does ship an OpenAL, but it is an OpenAL Soft 1.6 from 2008 whose mixer filters and resamples every source in floating point under one global lock, and on this console's single 333 MHz core its thread alone measured 12-24% of the CPU in game (per-thread run clocks, prince/03_carrot1) - as much as the whole module decoder. The game does not need any of what that buys: no effects, no HRTF, no filters, and its sounds are 8- and 16-bit samples at 11-22 kHz.
So this is the SDL backend's mixer (which is the Amiga one, which is the N64 one): sources are mixed by hand into one 16-bit stereo stream with a 32-bit integer accumulator, 32.32 fixed-point cursors and Q15 gains. Where it runs is the one departure from those backends, which mix on the main thread once per rendered frame: here a thread of higher priority than the game's mixes each block right before it hands it to sceAudioOutputPannedBlocking(), double-buffered, and the hardware paces it at the sample rate. A frame that runs long on this console - a busy scene, a level load - then costs nothing audible, where a main-thread mixer with a few blocks of lead went silent the moment a frame outlasted them (measured: dropouts in the same scenes that dip below 30 fps). The price is a lock: the mixer reads source and buffer state the game writes from the main thread, so every operation on them takes a kernel semaphore, held for microseconds by the game and for the length of one block's mix (well under a millisecond) by the mixer. A kernel semaphore rather than a spin lock, because the mixer thread has the higher priority - spinning on a lock the main thread holds would never let it be released.
The hardware plays 44100 Hz and nothing else, but the sources are mixed at a lower rate - 22050 Hz by default, the user's "Sample Rate" option otherwise (see setMixingFrequency()) - and the block is upsampled to the hardware's rate afterwards with a linear interpolation. The per-source loop, which is where the mixer's time goes, then runs half or a quarter as often, for content that is 11-22 kHz samples to begin with; the interpolation is one multiply-add per output sample, whatever the number of sources. nativeFrequency() reports the mixing rate, so the module decoder renders at it too.
The mixer thread outranking the game's is also why it must never loop without waiting: a failed output call returns at once, and retried straight away it would not let the main thread run at all. That is the state a sleep of the console left it in - the channel reserved before the sleep does not play after it - and the game froze. So the thread stays out of sceAudio from the moment the console starts going to sleep until it has woken up (see Backends::PspPower), reserves its channel again after every wake, and meets any failed output with a short wait and, if it persists, a fresh channel; the main thread puts both in the log (see updatePlayers()).
Base classes
- class AudioDeviceBase
- Backend-independent part of an audio device.
Constructors, destructors, conversion operators
- PspAudioDevice()
- ~PspAudioDevice() override
Public functions
- auto isValid() const -> bool override
- Returns
trueif the device was initialized successfully. - auto name() const -> const char* override
- Returns the name of the underlying device.
- void setGain(float gain) override
- Sets the listener gain (master volume).
- void updateListener(const Vector3f& position, const Vector3f& velocity) override
- Updates the position and velocity of the listener.
-
auto nativeFrequency() -> std::
int32_t override - Returns the native sample rate of the device.
-
void setMixingFrequency(std::
int32_t frequency) override - Changes the rate the device mixes at, if the backend has one to change.
-
auto registerPlayer(IAudioPlayer* player) -> std::
uint32_t override - Registers a player so it receives state and buffer queue updates, returning its source id.
- void updatePlayers() override
- Updates the state of every registered player, including the buffer queue of stream players.
-
auto createBuffer(BufferUsage usage) -> std::
uint32_t override - Creates an empty backend buffer, returning its id or
0on failure. -
void deleteBuffer(std::
uint32_t bufferId) override - Destroys a buffer previously returned by createBuffer().
-
auto uploadBuffer(std::
uint32_t bufferId, BufferFormat format, const void* data, std:: int32_t size, std:: int32_t frequency) -> bool override - Replaces the contents of a buffer with the specified samples.
-
void setSourceBuffer(std::
uint32_t sourceId, std:: uint32_t bufferId) override - Attaches a buffer to a source for non-streamed playback,
0detaches the current one. -
void setSourceGain(std::
uint32_t sourceId, float gain) override - Sets the gain of a source.
-
void setSourcePitch(std::
uint32_t sourceId, float pitch) override - Sets the pitch of a source, as a multiplier of its natural playback rate.
-
void setSourceLooping(std::
uint32_t sourceId, bool looping) override - Sets whether a source repeats its attached buffer.
-
void setSourceRelative(std::
uint32_t sourceId, bool relative) override - Sets whether the position of a source is relative to the listener.
-
void setSourcePosition(std::
uint32_t sourceId, const Vector3f& position) override - Sets the position of a source, in physical units.
-
void setSourceLowPass(std::
uint32_t sourceId, float value) override - Sets the low-pass amount of a source,
1.0fdisables the filter. -
auto sourceSampleOffset(std::
uint32_t sourceId) -> std:: int32_t override - Returns the playback position of a source in samples.
-
void setSourceSampleOffset(std::
uint32_t sourceId, std:: int32_t offset) override - Sets the playback position of a source in samples.
-
void playSource(std::
uint32_t sourceId) override - Starts or resumes a source.
-
void pauseSource(std::
uint32_t sourceId) override - Pauses a source at its current position.
-
void stopSource(std::
uint32_t sourceId) override - Stops a source.
-
auto isSourcePlaying(std::
uint32_t sourceId) -> bool override - Returns
trueif a source is still producing sound. -
void queueBuffer(std::
uint32_t sourceId, std:: uint32_t bufferId) override - Appends a buffer to the streaming queue of a source.
-
auto numProcessedBuffers(std::
uint32_t sourceId) -> std:: int32_t override - Returns the number of queued buffers a source has finished playing.
-
void unqueueBuffers(std::
uint32_t sourceId, std:: int32_t count, std:: uint32_t* bufferIds) override - Removes the specified number of played buffers from the front of the queue.
- void suspendDevice() override
- Suspends the audio device.
- void resumeDevice() override
- Resumes the audio device.
Function documentation
void nCine:: PspAudioDevice:: setMixingFrequency(std:: int32_t frequency) override
Changes the rate the device mixes at, if the backend has one to change.
Only the software-mixing backends whose cost is linear in this rate honour it (the PSP's, where the mix is upsampled to the hardware's fixed rate, and the Amiga's, where AHI resamples the output); nativeFrequency() then reports the new rate, so the module music decoders that size themselves by it follow on the next stream they open. A rate the backend cannot run at, or 0, is ignored. Everywhere else this is a no-op.
void nCine:: PspAudioDevice:: unqueueBuffers(std:: uint32_t sourceId,
std:: int32_t count,
std:: uint32_t* bufferIds) override
Removes the specified number of played buffers from the front of the queue.
| Parameters | |
|---|---|
| sourceId | |
| count | |
| bufferIds | Receives the ids of the removed buffers, must hold count entries |