mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-14 15:00:30 +02:00
From pspautotests intr/waits: - sceAudioOutputBlocking sets the channel's waiting flag before its event flag wait, and when that wait fails at once (interrupts or dispatch disabled, or inside an interrupt) it returns the error without clearing the flag. The channel stays busy from then on, and can't be released. - The SRC blocking output fails the same way even when a completion is already there, leaving the buffer armed. - After a block that had samples in it, the mixer DMA is still playing it out, so a buffer arriving then isn't read early or restarts it. - sceKernelVolatileMemLock only writes the fake address and size through pointers that are there, instead of faulting on NULL. intr/waits now runs to the end; one scheduling marker still differs, from async IO timing. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
124 lines
4.2 KiB
C++
124 lines
4.2 KiB
C++
// Copyright (c) 2012- PPSSPP Project.
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, version 2.0 or later versions.
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License 2.0 for more details.
|
|
|
|
// A copy of the GPL 2.0 should have been included with the program.
|
|
// If not, see http://www.gnu.org/licenses/
|
|
|
|
// Official git repository and contact information can be found at
|
|
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
|
|
|
#pragma once
|
|
|
|
#include <vector>
|
|
|
|
#include "CommonTypes.h"
|
|
#include "sceKernel.h"
|
|
|
|
class PointerWrap;
|
|
|
|
enum PspAudioFormats { PSP_AUDIO_FORMAT_STEREO = 0, PSP_AUDIO_FORMAT_MONO = 0x10 };
|
|
enum PspAudioFrequencies { PSP_AUDIO_FREQ_44K = 44100, PSP_AUDIO_FREQ_48K = 48000 };
|
|
|
|
// Channels 0-7, the ones the software mixer walks.
|
|
const u32 PSP_AUDIO_CHANNEL_MAX = 8;
|
|
|
|
// Mixer channels wait on their own index plus one, so the SRC channel takes the id after them.
|
|
const int PSP_AUDIO_SRC_WAIT_ID = PSP_AUDIO_CHANNEL_MAX + 1;
|
|
// In AudioChannel::waitingThread: the flag was set by a wait that failed, and nobody will clear it.
|
|
const SceUID AUDIO_WAITING_ABANDONED = -1;
|
|
|
|
// One buffer handed over and not yet fully played.
|
|
struct AudioPendingBuffer {
|
|
u32 address;
|
|
u32 samples;
|
|
};
|
|
|
|
// A channel the software mixer walks. It holds exactly one buffer, played straight out of the
|
|
// game's memory 64 samples at a time, and has room for exactly one parked thread. See
|
|
// docs/sceAudio.md.
|
|
struct AudioChannel {
|
|
int index = 0;
|
|
bool reserved = false;
|
|
|
|
u32 sampleCount = 0; // Buffer size agreed at reserve time.
|
|
u32 leftVolume = 0;
|
|
u32 rightVolume = 0;
|
|
u32 format = 0;
|
|
|
|
// For the debugger only. Not saved.
|
|
bool mute = false;
|
|
|
|
// sampleAddress walks forward as the mixer consumes the buffer and drops back to zero when
|
|
// remainingSamples runs out. An output with a null pointer sets remainingSamples but leaves
|
|
// sampleAddress at zero, which is the one case where the two rest-length calls disagree.
|
|
u32 sampleAddress = 0;
|
|
u32 remainingSamples = 0;
|
|
|
|
// A second thread arriving while one is parked here is told the channel is busy rather than
|
|
// queueing up behind it. These remember what the parked one wanted to hand over, so the
|
|
// enqueue can be retried once the buffer finishes.
|
|
// AUDIO_WAITING_ABANDONED: see __AudioEnqueueBlocking().
|
|
SceUID waitingThread = 0;
|
|
u32 waitingAddress = 0;
|
|
int waitingLeftVolume = 0;
|
|
int waitingRightVolume = 0;
|
|
|
|
void DoState(PointerWrap &p);
|
|
|
|
void clear();
|
|
};
|
|
|
|
// Channel 8, and there is only one of it: sceAudioOutput2, sceAudioSRC and sceVaudio are three
|
|
// names for the same channel. It skips the mixer entirely - its two DMA descriptors point
|
|
// straight at the game's buffers and the codec resamples them - so it shares nothing with the
|
|
// mixer channels beyond what reserve agrees on. Two buffers fit; a third caller is turned away.
|
|
struct AudioSRCChannel {
|
|
bool reserved = false;
|
|
|
|
u32 sampleCount = 0; // Buffer size agreed at reserve time.
|
|
u32 leftVolume = 0;
|
|
u32 rightVolume = 0;
|
|
u32 format = 0;
|
|
|
|
// For the debugger only. Not saved.
|
|
bool mute = false;
|
|
|
|
AudioPendingBuffer buffers[2]{};
|
|
int bufferCount = 0;
|
|
u32 playedSamples = 0; // Consumed from buffers[0].
|
|
u32 frac = 0; // 16.16 position between two input samples, for resampling.
|
|
// The driver signals a finished buffer with an event flag bit, so one completion can sit
|
|
// there unclaimed - which is why the first output after an idle period doesn't block.
|
|
bool completion = false;
|
|
std::vector<SceUID> waitingThreads;
|
|
|
|
void DoState(PointerWrap &p);
|
|
|
|
void reset();
|
|
void clear();
|
|
|
|
bool Full() const {
|
|
return bufferCount >= (int)ARRAY_SIZE(buffers);
|
|
}
|
|
};
|
|
|
|
extern AudioChannel g_audioChans[PSP_AUDIO_CHANNEL_MAX];
|
|
extern AudioSRCChannel g_audioSRC;
|
|
|
|
// The sample rates the SRC and VAUDIO channels will accept.
|
|
bool SRCFrequencyAllowed(int freq);
|
|
|
|
// The two routing modes are globals rather than per-channel; they get their own little block.
|
|
void __AudioRoutingDoState(PointerWrap &p);
|
|
|
|
void Register_sceAudio();
|
|
|