From 7d2b332b2da4ccdc2eb1c3ac34aeb0c368783e61 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 30 Jun 2019 16:39:14 -0700 Subject: [PATCH 1/4] Audio: Improve error messages for sample counts. --- Core/HLE/sceAudio.cpp | 38 +++++++++++++++++++++++++------------- headless/Headless.cpp | 1 + 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/Core/HLE/sceAudio.cpp b/Core/HLE/sceAudio.cpp index 6fd437bad69e..905c5a60e351 100644 --- a/Core/HLE/sceAudio.cpp +++ b/Core/HLE/sceAudio.cpp @@ -339,11 +339,15 @@ static u32 sceAudioOutput2OutputBlocking(u32 vol, u32 dataPtr) { } auto &chan = chans[PSP_AUDIO_CHANNEL_OUTPUT2]; + if (!chan.reserved) { + return hleLogError(SCEAUDIO, SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED, "channel not reserved"); + } + chan.leftVolume = vol; chan.rightVolume = vol; chan.sampleAddress = dataPtr; - hleEatCycles(10000); + hleEatCycles(10000); int result = __AudioEnqueue(chan, PSP_AUDIO_CHANNEL_OUTPUT2, true); if (result < 0) return hleLogError(SCEAUDIO, result); @@ -351,22 +355,25 @@ static u32 sceAudioOutput2OutputBlocking(u32 vol, u32 dataPtr) { } static u32 sceAudioOutput2ChangeLength(u32 sampleCount) { - if (!chans[PSP_AUDIO_CHANNEL_OUTPUT2].reserved) { - ERROR_LOG(SCEAUDIO, "sceAudioOutput2ChangeLength(%08x) - channel not reserved ", sampleCount); - return SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED; + auto &chan = chans[PSP_AUDIO_CHANNEL_OUTPUT2]; + if (!chan.reserved) { + return hleLogError(SCEAUDIO, SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED, "channel not reserved"); } - DEBUG_LOG(SCEAUDIO, "sceAudioOutput2ChangeLength(%08x)", sampleCount); - chans[PSP_AUDIO_CHANNEL_OUTPUT2].sampleCount = sampleCount; - return 0; + chan.sampleCount = sampleCount; + return hleLogSuccessI(SCEAUDIO, 0); } static u32 sceAudioOutput2GetRestSample() { - if (!chans[PSP_AUDIO_CHANNEL_OUTPUT2].reserved) { - ERROR_LOG(SCEAUDIO, "sceAudioOutput2GetRestSample() - channel not reserved "); - return SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED; + auto &chan = chans[PSP_AUDIO_CHANNEL_OUTPUT2]; + if (!chan.reserved) { + return hleLogError(SCEAUDIO, SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED, "channel not reserved"); } - DEBUG_LOG(SCEAUDIO, "sceAudioOutput2GetRestSample()"); - return (u32) chans[PSP_AUDIO_CHANNEL_OUTPUT2].sampleQueue.size() / 2; + u32 size = (u32)chan.sampleQueue.size() / 2; + if (size > chan.sampleCount) { + // If ChangeLength reduces the size, it still gets output but this return is clamped. + size = chan.sampleCount; + } + return hleLogSuccessI(SCEAUDIO, size); } static u32 sceAudioOutput2Release() { @@ -382,6 +389,7 @@ static u32 sceAudioOutput2Release() { } static u32 sceAudioSetFrequency(u32 freq) { + // TODO: Not available from user code. if (freq == 44100 || freq == 48000) { INFO_LOG(SCEAUDIO, "sceAudioSetFrequency(%08x)", freq); __AudioSetOutputFrequency(freq); @@ -449,6 +457,10 @@ static u32 sceAudioSRCOutputBlocking(u32 vol, u32 buf) { } auto &chan = chans[PSP_AUDIO_CHANNEL_SRC]; + if (!chan.reserved) { + return hleLogError(SCEAUDIO, SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED, "channel not reserved"); + } + chan.leftVolume = vol; chan.rightVolume = vol; chan.sampleAddress = buf; @@ -511,7 +523,7 @@ const HLEFunction sceAudio[] = {0X95FD0C2D, &WrapU_UU, "sceAudioChangeChannelConfig", 'x', "ii" }, {0XB7E1D8E7, &WrapU_UUU, "sceAudioChangeChannelVolume", 'x', "ixx" }, - // Not sure about the point of these, maybe like traditional but with ability to do sample rate conversion? + // Like Output2, but with ability to do sample rate conversion. {0X38553111, &WrapU_UUU, "sceAudioSRCChReserve", 'x', "iii" }, {0X5C37C0AE, &WrapU_V, "sceAudioSRCChRelease", 'x', "" }, {0XE0727056, &WrapU_UU, "sceAudioSRCOutputBlocking", 'x', "xx" }, diff --git a/headless/Headless.cpp b/headless/Headless.cpp index 11a9e8843f9e..77e136baf509 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -377,6 +377,7 @@ int main(int argc, const char* argv[]) g_Config.bHighQualityDepth = true; g_Config.bMemStickInserted = true; g_Config.bFragmentTestCache = true; + g_Config.iAudioLatency = 1; #ifdef _WIN32 InitSysDirectories(); From 9ef3973b4910266f1eb88859c4fe65911b2fd446 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 30 Jun 2019 23:09:07 -0700 Subject: [PATCH 2/4] Audio: Correct sceAudioChRelease return value. --- Core/HLE/sceAudio.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/HLE/sceAudio.cpp b/Core/HLE/sceAudio.cpp index 905c5a60e351..2a5465f37157 100644 --- a/Core/HLE/sceAudio.cpp +++ b/Core/HLE/sceAudio.cpp @@ -248,10 +248,10 @@ static u32 sceAudioChRelease(u32 chan) { ERROR_LOG(SCEAUDIO, "sceAudioChRelease(%i) - channel not reserved", chan); return SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED; } - DEBUG_LOG(SCEAUDIO, "sceAudioChRelease(%i)", chan); + // TODO: Does this error if busy? chans[chan].reset(); chans[chan].reserved = false; - return 1; + return hleLogSuccessI(SCEAUDIO, 0); } static u32 sceAudioSetChannelDataLen(u32 chan, u32 len) { From 20ad7f291430de279f80621ca1529744f76ca3b1 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 30 Jun 2019 23:09:46 -0700 Subject: [PATCH 3/4] Audio: Track SRC frequency. sceAudioSetFrequency is kernel mode only, and it sounds like it changes the audio controller clock (didn't test.) SRC only affects the SRC channel. Currently, this doesn't actually use the sample rate yet, but at least it's tracked appropriately. --- Core/HLE/__sceAudio.cpp | 16 +++++++++++++++- Core/HLE/__sceAudio.h | 1 + Core/HLE/sceAudio.cpp | 7 +++---- Core/HLE/sceVaudio.cpp | 2 +- Core/HW/StereoResampler.cpp | 2 ++ 5 files changed, 22 insertions(+), 6 deletions(-) diff --git a/Core/HLE/__sceAudio.cpp b/Core/HLE/__sceAudio.cpp index 99f40e4551e6..bf3b86c1ba64 100644 --- a/Core/HLE/__sceAudio.cpp +++ b/Core/HLE/__sceAudio.cpp @@ -62,6 +62,7 @@ enum latency { int eventAudioUpdate = -1; int eventHostAudioUpdate = -1; int mixFrequency = 44100; +int srcFrequency = 0; const int hwSampleRate = 44100; @@ -107,6 +108,7 @@ static void __AudioCPUMHzChange() { void __AudioInit() { memset(&g_AudioDebugStats, 0, sizeof(g_AudioDebugStats)); mixFrequency = 44100; + srcFrequency = 0; switch (g_Config.iAudioLatency) { case LOW_LATENCY: @@ -149,7 +151,7 @@ void __AudioInit() { } void __AudioDoState(PointerWrap &p) { - auto s = p.Section("sceAudio", 1); + auto s = p.Section("sceAudio", 1, 2); if (!s) return; @@ -159,6 +161,13 @@ void __AudioDoState(PointerWrap &p) { CoreTiming::RestoreRegisterEvent(eventHostAudioUpdate, "AudioUpdateHost", &hleHostAudioUpdate); p.Do(mixFrequency); + if (s >= 2) { + p.Do(srcFrequency); + } else { + // Assume that it was actually the SRC channel frequency. + srcFrequency = mixFrequency; + mixFrequency = 44100; + } // TODO: This never happens because maxVer=1. if (s >= 2) { @@ -176,6 +185,7 @@ void __AudioDoState(PointerWrap &p) { if (chanCount != ARRAY_SIZE(chans)) { ERROR_LOG(SCEAUDIO, "Savestate failure: different number of audio channels."); + p.SetError(p.ERROR_FAILURE); return; } for (int i = 0; i < chanCount; ++i) @@ -329,6 +339,10 @@ void __AudioSetOutputFrequency(int freq) { mixFrequency = freq; } +void __AudioSetSRCFrequency(int freq) { + srcFrequency = freq; +} + // 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. diff --git a/Core/HLE/__sceAudio.h b/Core/HLE/__sceAudio.h index 39f83d840141..c836bd3f19cc 100644 --- a/Core/HLE/__sceAudio.h +++ b/Core/HLE/__sceAudio.h @@ -36,6 +36,7 @@ void __AudioDoState(PointerWrap &p); void __AudioUpdate(bool resetRecording = false); void __AudioShutdown(); void __AudioSetOutputFrequency(int freq); +void __AudioSetSRCFrequency(int freq); // May return SCE_ERROR_AUDIO_CHANNEL_BUSY if buffer too large u32 __AudioEnqueue(AudioChannel &chan, int chanNum, bool blocking); diff --git a/Core/HLE/sceAudio.cpp b/Core/HLE/sceAudio.cpp index 2a5465f37157..f3c355b6555a 100644 --- a/Core/HLE/sceAudio.cpp +++ b/Core/HLE/sceAudio.cpp @@ -329,6 +329,7 @@ static u32 sceAudioOutput2Reserve(u32 sampleCount) { chan.sampleCount = sampleCount; chan.format = PSP_AUDIO_FORMAT_STEREO; chan.reserved = true; + __AudioSetSRCFrequency(0); return hleLogSuccessI(SCEAUDIO, 0); } @@ -432,10 +433,8 @@ static u32 sceAudioSRCChReserve(u32 sampleCount, u32 freq, u32 format) { chan.reserved = true; chan.sampleCount = sampleCount; chan.format = format == 2 ? PSP_AUDIO_FORMAT_STEREO : PSP_AUDIO_FORMAT_MONO; - // TODO: Zero probably means don't change? Or it means default? - if (freq != 0) { - __AudioSetOutputFrequency(freq); - } + // Zero means default to 44.1kHz. + __AudioSetSRCFrequency(freq); return hleLogSuccessI(SCEAUDIO, 0); } diff --git a/Core/HLE/sceVaudio.cpp b/Core/HLE/sceVaudio.cpp index ca344110ca2c..e3749d068094 100644 --- a/Core/HLE/sceVaudio.cpp +++ b/Core/HLE/sceVaudio.cpp @@ -56,7 +56,7 @@ static u32 sceVaudioChReserve(int sampleCount, int freq, int format) { chans[PSP_AUDIO_CHANNEL_VAUDIO].leftVolume = 0; chans[PSP_AUDIO_CHANNEL_VAUDIO].rightVolume = 0; vaudioReserved = true; - __AudioSetOutputFrequency(freq); + __AudioSetSRCFrequency(freq); return 0; } diff --git a/Core/HW/StereoResampler.cpp b/Core/HW/StereoResampler.cpp index f17de4fe61f9..8bf58c3f4ce0 100644 --- a/Core/HW/StereoResampler.cpp +++ b/Core/HW/StereoResampler.cpp @@ -293,4 +293,6 @@ void StereoResampler::DoState(PointerWrap &p) { auto s = p.Section("resampler", 1); if (!s) return; + if (p.mode == p.MODE_READ) + Clear(); } From 4546db5635e3351078b83be0879d3d8ff81eaaed Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 30 Jun 2019 23:44:18 -0700 Subject: [PATCH 4/4] Audio: Quick SRC resampling experiment. --- Core/HLE/__sceAudio.cpp | 47 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/Core/HLE/__sceAudio.cpp b/Core/HLE/__sceAudio.cpp index bf3b86c1ba64..b6e7179ea1dd 100644 --- a/Core/HLE/__sceAudio.cpp +++ b/Core/HLE/__sceAudio.cpp @@ -351,6 +351,7 @@ void __AudioUpdate(bool resetRecording) { // to the CPU. Much better to throttle the frame rate on frame display and just throw away audio // if the buffer somehow gets full. bool firstChannel = true; + std::vector srcBuffer; for (u32 i = 0; i < PSP_AUDIO_CHANNEL_MAX + 1; i++) { if (!chans[i].reserved) @@ -362,14 +363,54 @@ void __AudioUpdate(bool resetRecording) { continue; } - if (hwBlockSize * 2 > (int)chans[i].sampleQueue.size()) { - ERROR_LOG(SCEAUDIO, "Channel %i buffer underrun at %i of %i", i, (int)chans[i].sampleQueue.size() / 2, hwBlockSize); + bool needsResample = i == PSP_AUDIO_CHANNEL_SRC && srcFrequency != 0 && srcFrequency != mixFrequency; + size_t sz = needsResample ? (hwBlockSize * 2 * srcFrequency) / mixFrequency : hwBlockSize * 2; + if (sz > chans[i].sampleQueue.size()) { + ERROR_LOG(SCEAUDIO, "Channel %i buffer underrun at %i of %i", i, (int)chans[i].sampleQueue.size() / 2, (int)sz / 2); } const s16 *buf1 = 0, *buf2 = 0; size_t sz1, sz2; - chans[i].sampleQueue.popPointers(hwBlockSize * 2, &buf1, &sz1, &buf2, &sz2); + chans[i].sampleQueue.popPointers(sz, &buf1, &sz1, &buf2, &sz2); + + if (needsResample) { + auto read = [&](size_t i) { + if (i < sz1) + return buf1[i]; + if (i < sz1 + sz2) + return buf2[i - sz1]; + if (buf2) + return buf2[sz2 - 1]; + return buf1[sz1 - 1]; + }; + + srcBuffer.resize(hwBlockSize * 2); + + // TODO: This is terrible, since it's doing it by small chunk and discarding frac. + const uint32_t ratio = (uint32_t)(65536.0 * srcFrequency / (double)mixFrequency); + uint32_t frac = 0; + size_t readIndex = 0; + for (size_t outIndex = 0; readIndex < sz && outIndex < srcBuffer.size(); outIndex += 2) { + size_t readIndex2 = readIndex + 2; + int16_t l1 = read(readIndex); + int16_t r1 = read(readIndex + 1); + int16_t l2 = read(readIndex2); + int16_t r2 = read(readIndex2 + 1); + int sampleL = ((l1 << 16) + (l2 - l1) * (uint16_t)frac) >> 16; + int sampleR = ((r1 << 16) + (r2 - r1) * (uint16_t)frac) >> 16; + srcBuffer[outIndex] = sampleL; + srcBuffer[outIndex + 1] = sampleR; + frac += ratio; + readIndex += 2 * (uint16_t)(frac >> 16); + frac &= 0xffff; + } + + buf1 = srcBuffer.data(); + sz1 = srcBuffer.size(); + buf2 = nullptr; + sz2 = 0; + } if (firstChannel) { for (size_t s = 0; s < sz1; s++)