Block audio output more correctly.

Still with the same thresholds, so hopefully it won't hurt sync.
This commit is contained in:
Unknown W. Brackets 2013-05-19 12:31:47 -07:00
parent 996bfdc1dd
commit 2bab76a940
4 changed files with 87 additions and 61 deletions

View File

@ -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) {

View File

@ -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);

View File

@ -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;

View File

@ -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.