diff --git a/Core/HLE/__sceAudio.cpp b/Core/HLE/__sceAudio.cpp index 99f40e4551e6..b6e7179ea1dd 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. @@ -337,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) @@ -348,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++) 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 6fd437bad69e..f3c355b6555a 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) { @@ -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); } @@ -339,11 +340,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 +356,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"); + } + 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; } - DEBUG_LOG(SCEAUDIO, "sceAudioOutput2GetRestSample()"); - return (u32) chans[PSP_AUDIO_CHANNEL_OUTPUT2].sampleQueue.size() / 2; + return hleLogSuccessI(SCEAUDIO, size); } static u32 sceAudioOutput2Release() { @@ -382,6 +390,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); @@ -424,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); } @@ -449,6 +456,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 +522,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/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(); } 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();