mirror of
https://github.com/libretro/ppsspp.git
synced 2024-12-02 22:26:25 +00:00
Block audio output more correctly.
Still with the same thresholds, so hopefully it won't hurt sync.
This commit is contained in:
parent
996bfdc1dd
commit
2bab76a940
@ -49,7 +49,7 @@ const int hostAttemptBlockSize = 512;
|
||||
const int audioIntervalUs = (int)(1000000ULL * hwBlockSize / hwSampleRate);
|
||||
const int audioHostIntervalUs = (int)(1000000ULL * hostAttemptBlockSize / hwSampleRate);
|
||||
|
||||
// High and low watermarks, basically.
|
||||
// High and low watermarks, basically. For perfect emulation, the correct values are 0 and 1, respectively.
|
||||
// TODO: Tweak
|
||||
#ifdef ANDROID
|
||||
const int chanQueueMaxSizeFactor = 4;
|
||||
@ -122,24 +122,38 @@ void __AudioShutdown()
|
||||
|
||||
u32 __AudioEnqueue(AudioChannel &chan, int chanNum, bool blocking)
|
||||
{
|
||||
u32 ret = 0;
|
||||
if (chan.sampleAddress == 0)
|
||||
return SCE_ERROR_AUDIO_NOT_OUTPUT;
|
||||
if (chan.sampleQueue.size() > chan.sampleCount*2*chanQueueMaxSizeFactor) {
|
||||
// Block!
|
||||
if (blocking) {
|
||||
chan.waitingThread = __KernelGetCurThread();
|
||||
// WARNING: This changes currentThread so must grab waitingThread before (line above).
|
||||
__KernelWaitCurThread(WAITTYPE_AUDIOCHANNEL, (SceUID)chanNum, 0, 0, false, "blocking audio waited");
|
||||
// Fall through to the sample queueing, don't want to lose the samples even though
|
||||
// we're getting full.
|
||||
u32 ret = chan.sampleCount;
|
||||
|
||||
if (chan.sampleAddress == 0) {
|
||||
// For some reason, multichannel audio lies and returns the sample count here.
|
||||
if (chanNum == PSP_AUDIO_CHANNEL_SRC || chanNum == PSP_AUDIO_CHANNEL_OUTPUT2) {
|
||||
ret = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
chan.waitingThread = 0;
|
||||
}
|
||||
|
||||
// If there's anything on the queue at all, it should be busy, but we try to be a bit lax.
|
||||
if (chan.sampleQueue.size() > chan.sampleCount * 2 * chanQueueMaxSizeFactor || chan.sampleAddress == 0) {
|
||||
if (blocking) {
|
||||
// TODO: Regular multichannel audio seems to block for 64 samples less? Or enqueue the first 64 sync?
|
||||
int blockSamples = chan.sampleQueue.size() / 2 / chanQueueMinSizeFactor;
|
||||
|
||||
AudioChannelWaitInfo waitInfo = {__KernelGetCurThread(), blockSamples};
|
||||
chan.waitingThreads.push_back(waitInfo);
|
||||
// Also remember the value to return in the waitValue.
|
||||
__KernelWaitCurThread(WAITTYPE_AUDIOCHANNEL, (SceUID)chanNum + 1, ret, 0, false, "blocking audio waited");
|
||||
|
||||
// Fall through to the sample queueing, don't want to lose the samples even though
|
||||
// we're getting full. The PSP would enqueue after blocking.
|
||||
} else {
|
||||
// Non-blocking doesn't even enqueue, but it's not commonly used.
|
||||
return SCE_ERROR_AUDIO_CHANNEL_BUSY;
|
||||
}
|
||||
}
|
||||
|
||||
if (chan.sampleAddress == 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (chan.format == PSP_AUDIO_FORMAT_STEREO)
|
||||
{
|
||||
const u32 totalSamples = chan.sampleCount * 2;
|
||||
@ -160,8 +174,6 @@ u32 __AudioEnqueue(AudioChannel &chan, int chanNum, bool blocking)
|
||||
for (u32 i = 0; i < totalSamples; i++)
|
||||
chan.sampleQueue.push((s16)Memory::Read_U16(chan.sampleAddress + sizeof(s16) * i));
|
||||
}
|
||||
|
||||
ret = chan.sampleCount;
|
||||
}
|
||||
else if (chan.format == PSP_AUDIO_FORMAT_MONO)
|
||||
{
|
||||
@ -172,8 +184,6 @@ u32 __AudioEnqueue(AudioChannel &chan, int chanNum, bool blocking)
|
||||
chan.sampleQueue.push(sample);
|
||||
chan.sampleQueue.push(sample);
|
||||
}
|
||||
|
||||
ret = chan.sampleCount;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@ -186,6 +196,31 @@ static inline s16 clamp_s16(int i) {
|
||||
return i;
|
||||
}
|
||||
|
||||
inline void __AudioWakeThreads(AudioChannel &chan, int step)
|
||||
{
|
||||
u32 error;
|
||||
for (size_t w = 0; w < chan.waitingThreads.size(); ++w)
|
||||
{
|
||||
AudioChannelWaitInfo &waitInfo = chan.waitingThreads[w];
|
||||
waitInfo.numSamples -= hwBlockSize;
|
||||
|
||||
// If it's done (there will still be samples on queue) and actually still waiting, wake it up.
|
||||
if (waitInfo.numSamples <= 0 && __KernelGetWaitID(waitInfo.threadID, WAITTYPE_AUDIOCHANNEL, error) != 0)
|
||||
{
|
||||
// DEBUG_LOG(HLE, "Woke thread %i for some buffer filling", waitingThread);
|
||||
u32 ret = __KernelGetWaitValue(waitInfo.threadID, error);
|
||||
__KernelResumeThreadFromWait(waitInfo.threadID, ret);
|
||||
|
||||
chan.waitingThreads.erase(chan.waitingThreads.begin() + w--);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void __AudioWakeThreads(AudioChannel &chan)
|
||||
{
|
||||
__AudioWakeThreads(chan, 0x7FFFFFFF);
|
||||
}
|
||||
|
||||
// Mix samples from the various audio channels into a single sample queue.
|
||||
// This single sample queue is where __AudioMix should read from. If the sample queue is full, we should
|
||||
// just sleep the main emulator thread a little.
|
||||
@ -202,6 +237,8 @@ void __AudioUpdate()
|
||||
{
|
||||
if (!chans[i].reserved)
|
||||
continue;
|
||||
__AudioWakeThreads(chans[i], hwBlockSize);
|
||||
|
||||
if (!chans[i].sampleQueue.size()) {
|
||||
// ERROR_LOG(HLE, "No queued samples, skipping channel %i", i);
|
||||
continue;
|
||||
@ -223,17 +260,6 @@ void __AudioUpdate()
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (chans[i].sampleQueue.size() < chans[i].sampleCount * 2 * chanQueueMinSizeFactor)
|
||||
{
|
||||
// Ask the thread to send more samples until next time, queue is being drained.
|
||||
if (chans[i].waitingThread) {
|
||||
SceUID waitingThread = chans[i].waitingThread;
|
||||
chans[i].waitingThread = 0;
|
||||
// DEBUG_LOG(HLE, "Woke thread %i for some buffer filling", waitingThread);
|
||||
__KernelResumeThreadFromWait(waitingThread, chans[i].sampleCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (g_Config.bEnableSound) {
|
||||
|
@ -29,5 +29,7 @@ void __AudioSetOutputFrequency(int freq);
|
||||
|
||||
// May return SCE_ERROR_AUDIO_CHANNEL_BUSY if buffer too large
|
||||
u32 __AudioEnqueue(AudioChannel &chan, int chanNum, bool blocking);
|
||||
void __AudioWakeThreads(AudioChannel &chan, int step);
|
||||
void __AudioWakeThreads(AudioChannel &chan);
|
||||
|
||||
int __AudioMix(short *outstereo, int numSamples);
|
||||
|
@ -15,14 +15,14 @@
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include "../MIPS/MIPS.h"
|
||||
#include "../Host.h"
|
||||
#include "Common/ChunkFile.h"
|
||||
#include "Core/MIPS/MIPS.h"
|
||||
#include "Core/Host.h"
|
||||
#include "Core/CoreTiming.h"
|
||||
#include "ChunkFile.h"
|
||||
|
||||
#include "sceAudio.h"
|
||||
#include "__sceAudio.h"
|
||||
#include "HLE.h"
|
||||
#include "Core/HLE/HLE.h"
|
||||
#include "Core/HLE/sceKernelThread.h"
|
||||
#include "Core/HLE/sceAudio.h"
|
||||
#include "Core/HLE/__sceAudio.h"
|
||||
|
||||
const int PSP_AUDIO_SAMPLE_MAX = 65536 - 64;
|
||||
const int PSP_AUDIO_ERROR_SRC_FORMAT_4 = 0x80000003;
|
||||
@ -35,11 +35,24 @@ void AudioChannel::DoState(PointerWrap &p)
|
||||
p.Do(leftVolume);
|
||||
p.Do(rightVolume);
|
||||
p.Do(format);
|
||||
p.Do(waitingThread);
|
||||
p.Do(waitingThreads);
|
||||
sampleQueue.DoState(p);
|
||||
p.DoMarker("AudioChannel");
|
||||
}
|
||||
|
||||
void AudioChannel::clear()
|
||||
{
|
||||
reserved = false;
|
||||
leftVolume = 0;
|
||||
rightVolume = 0;
|
||||
format = 0;
|
||||
sampleAddress = 0;
|
||||
sampleCount = 0;
|
||||
sampleQueue.clear();
|
||||
|
||||
__AudioWakeThreads(*this);
|
||||
}
|
||||
|
||||
// There's a second Audio api called Audio2 that only has one channel, I guess the 8 channel api was overkill.
|
||||
// We simply map it to an extra channel after the 8 channels, since they can be used concurrently.
|
||||
|
||||
@ -56,9 +69,6 @@ u32 sceAudioOutputBlocking(u32 chan, int vol, u32 samplePtr) {
|
||||
if (vol > 0xFFFF) {
|
||||
ERROR_LOG(HLE, "sceAudioOutputBlocking() - invalid volume");
|
||||
return SCE_ERROR_AUDIO_INVALID_VOLUME;
|
||||
} else if (samplePtr == 0) {
|
||||
ERROR_LOG(HLE, "sceAudioOutputBlocking() - Sample pointer null");
|
||||
return 0;
|
||||
} else if (chan >= PSP_AUDIO_CHANNEL_MAX) {
|
||||
ERROR_LOG(HLE,"sceAudioOutputBlocking() - bad channel");
|
||||
return SCE_ERROR_AUDIO_INVALID_CHANNEL;
|
||||
@ -80,9 +90,6 @@ u32 sceAudioOutputPannedBlocking(u32 chan, int leftvol, int rightvol, u32 sample
|
||||
if (leftvol > 0xFFFF || rightvol > 0xFFFF) {
|
||||
ERROR_LOG(HLE, "sceAudioOutputPannedBlocking() - invalid volume");
|
||||
return SCE_ERROR_AUDIO_INVALID_VOLUME;
|
||||
} else if (samplePtr == 0) {
|
||||
ERROR_LOG(HLE, "sceAudioOutputPannedBlocking() - Sample pointer null");
|
||||
return 0;
|
||||
} else if (chan >= PSP_AUDIO_CHANNEL_MAX) {
|
||||
ERROR_LOG(HLE,"sceAudioOutputPannedBlocking() - bad channel");
|
||||
return SCE_ERROR_AUDIO_INVALID_CHANNEL;
|
||||
@ -106,9 +113,6 @@ u32 sceAudioOutput(u32 chan, int vol, u32 samplePtr) {
|
||||
if (vol > 0xFFFF) {
|
||||
ERROR_LOG(HLE, "sceAudioOutput() - invalid volume");
|
||||
return SCE_ERROR_AUDIO_INVALID_VOLUME;
|
||||
} else if (samplePtr == 0) {
|
||||
ERROR_LOG(HLE, "sceAudioOutput() - Sample pointer null");
|
||||
return 0;
|
||||
} else if (chan >= PSP_AUDIO_CHANNEL_MAX) {
|
||||
ERROR_LOG(HLE,"sceAudioOutput() - bad channel");
|
||||
return SCE_ERROR_AUDIO_INVALID_CHANNEL;
|
||||
@ -130,9 +134,6 @@ u32 sceAudioOutputPanned(u32 chan, int leftvol, int rightvol, u32 samplePtr) {
|
||||
if (leftvol > 0xFFFF || rightvol > 0xFFFF) {
|
||||
ERROR_LOG(HLE, "sceAudioOutputPannedBlocking() - invalid volume");
|
||||
return SCE_ERROR_AUDIO_INVALID_VOLUME;
|
||||
} else if (samplePtr == 0) {
|
||||
ERROR_LOG(HLE, "sceAudioOutputPannedBlocking() - Sample pointer null");
|
||||
return 0;
|
||||
} else if (chan >= PSP_AUDIO_CHANNEL_MAX) {
|
||||
ERROR_LOG(HLE,"sceAudioOutputPanned() - bad channel");
|
||||
return SCE_ERROR_AUDIO_INVALID_CHANNEL;
|
||||
|
@ -47,6 +47,12 @@ const int PSP_AUDIO_CHANNEL_MAX = 8;
|
||||
const int PSP_AUDIO_CHANNEL_SRC = 8;
|
||||
const int PSP_AUDIO_CHANNEL_OUTPUT2 = 8;
|
||||
|
||||
struct AudioChannelWaitInfo
|
||||
{
|
||||
SceUID threadID;
|
||||
int numSamples;
|
||||
};
|
||||
|
||||
struct AudioChannel
|
||||
{
|
||||
AudioChannel() {
|
||||
@ -64,7 +70,7 @@ struct AudioChannel
|
||||
u32 rightVolume;
|
||||
u32 format;
|
||||
|
||||
SceUID waitingThread;
|
||||
std::vector<AudioChannelWaitInfo> waitingThreads;
|
||||
|
||||
// PC side - should probably split out
|
||||
|
||||
@ -74,16 +80,7 @@ struct AudioChannel
|
||||
|
||||
void DoState(PointerWrap &p);
|
||||
|
||||
void clear() {
|
||||
reserved = false;
|
||||
waitingThread = 0;
|
||||
leftVolume = 0;
|
||||
rightVolume = 0;
|
||||
format = 0;
|
||||
sampleAddress = 0;
|
||||
sampleCount = 0;
|
||||
sampleQueue.clear();
|
||||
}
|
||||
void clear();
|
||||
};
|
||||
|
||||
// The extra channel is for SRC/Output2.
|
||||
|
Loading…
Reference in New Issue
Block a user