diff --git a/Core/HLE/AtracBase.h b/Core/HLE/AtracBase.h index 2c29ccb8e8..73b373f7b9 100644 --- a/Core/HLE/AtracBase.h +++ b/Core/HLE/AtracBase.h @@ -222,6 +222,8 @@ public: virtual int ResetPlayPosition(int sample, int bytesWrittenFirstBuf, int bytesWrittenSecondBuf, bool *delay) = 0; virtual int GetBufferInfoForResetting(AtracResetBufferInfo *bufferInfo, int sample, bool *delay) = 0; // NOTE: Not const! This can cause SkipFrames! virtual int SetData(const Track &track, u32 buffer, u32 readSize, u32 bufferSize, u32 fileSize, int outputChannels, bool isAA3) = 0; + // How many frames the last SetData decoded and threw away, to get to the first sample. + int SkippedFramesOnSetData() const { return setDataSkippedFrames_; } virtual int GetSecondBufferInfo(u32 *fileOffset, u32 *desiredSize) const = 0; virtual int SetSecondBuffer(u32 secondBuffer, u32 secondBufferSize) = 0; @@ -242,6 +244,7 @@ public: protected: u16 outputChannels_ = 2; + int setDataSkippedFrames_ = 0; // TODO: Save the internal state of this, now technically possible. AudioDecoder *decoder_ = nullptr; diff --git a/Core/HLE/AtracCtx2.cpp b/Core/HLE/AtracCtx2.cpp index 2a403d84af..5e7d4f5858 100644 --- a/Core/HLE/AtracCtx2.cpp +++ b/Core/HLE/AtracCtx2.cpp @@ -1019,8 +1019,9 @@ int Atrac2::SetData(const Track &track, u32 bufferAddr, u32 readSize, u32 buffer info.fileDataEnd, info.decodePos, info.numSkipFrames, info.numChan ); - int skipCount = 0; // TODO: use for delay + int skipCount = 0; retval = SkipFrames(&skipCount); + setDataSkippedFrames_ = skipCount; // Seen in Mui Mui house. Things go very wrong after this.. if (retval == SCE_ERROR_ATRAC_API_FAIL) { diff --git a/Core/HLE/sceAtrac.cpp b/Core/HLE/sceAtrac.cpp index 58ebe7c143..24a48ab841 100644 --- a/Core/HLE/sceAtrac.cpp +++ b/Core/HLE/sceAtrac.cpp @@ -32,8 +32,11 @@ #include "Core/HLE/sceKernel.h" #include "Core/HLE/sceUtility.h" +#include "Core/HLE/sceVideocodec.h" #include "Core/HLE/sceKernelMemory.h" +#include "Core/HLE/scePower.h" #include "Core/HLE/sceAtrac.h" +#include "Core/HLE/sceAudiocodec.h" #include "Core/HLE/AtracCtx.h" #include "Core/HLE/AtracCtx2.h" #include "Core/System.h" @@ -81,7 +84,21 @@ // TODO: We should add checks that the utility module is loaded. -static const int atracDecodeDelay = 2300; +// The Media Engine does the decoding while the caller waits. Setting data decodes the frames before +// the first sample (thrown away), which on hardware makes it cost a decoder setup plus a frame +// decode: ~900us for mono Atrac3, ~3.5ms for stereo Atrac3+ (pspautotests threads/scheduling/callcosts). +static int AtracFrameUs(const AtracBase *atrac) { + return AudioCodecDecodeUs(atrac->CodecType(), atrac->Channels(), atrac->BytesPerFrame()); +} + +static int AtracSetDataDelay(const AtracBase *atrac) { + const int us = AudioCodecInitUs(atrac->CodecType(), atrac->Channels() == 1) + atrac->SkippedFramesOnSetData() * AtracFrameUs(atrac); + return MEScheduleJob(PowerScaleFromDefaultClock(us)); +} + +static int AtracDecodeDelay(const AtracBase *atrac) { + return MEScheduleJob(PowerScaleFromDefaultClock(AtracFrameUs(atrac))); +} static bool atracInited = true; static AtracBase *atracContexts[PSP_MAX_ATRAC_IDS]; @@ -94,7 +111,7 @@ static int g_atracBSS = 0; static bool g_muteFlag[PSP_MAX_ATRAC_IDS]{}; // Not saved, just for debugging. // On a PSP, the Media Engine does the decoding, and the samples land in the output buffer when -// sceAtracDecodeData returns, about atracDecodeDelay later. A game can still be playing out of that +// sceAtracDecodeData returns, a frame decode later. A game can still be playing out of that // memory in the meantime: Fired Up decodes into a buffer that overlaps the first 16 samples of the // one it has just handed to sceAudio, and relies on the mixer having read them first. So the // samples are written just before the thread wakes rather than when the call is made. @@ -423,6 +440,7 @@ static u32 sceAtracDecodeData(int atracID, u32 outAddr, u32 numSamplesAddr, u32 } if (ret == 0 || ret == SCE_ERROR_ATRAC_API_FAIL) { + const int delay = AtracDecodeDelay(atrac); const u32 written = std::min((u32)(atrac->GetOutputChannels() * 2 * numSamplesWritten), (u32)previous.size()); if (outPtr && written != 0) { AtracPendingOutput pending{ ++g_pendingOutputId, outAddr }; @@ -430,10 +448,10 @@ static u32 sceAtracDecodeData(int atracID, u32 outAddr, u32 numSamplesAddr, u32 memcpy(outPtr, previous.data(), previous.size()); g_pendingOutput.push_back(std::move(pending)); // Just ahead of the thread waking up. - CoreTiming::ScheduleEvent(usToCycles(atracDecodeDelay) - 1, g_atracOutputEvent, g_pendingOutputId); + CoreTiming::ScheduleEvent(usToCycles(delay) - 1, g_atracOutputEvent, g_pendingOutputId); } // Decoded or at least attempted to decode data, delay thread - return hleDelayResult(hleNoLog(ret), "atrac decode data", atracDecodeDelay); + return hleDelayResult(hleNoLog(ret), "atrac decode data", delay); } return hleNoLog(ret); @@ -753,7 +771,7 @@ static u32 sceAtracSetHalfwayBuffer(int atracID, u32 buffer, u32 readSize, u32 b } // not sure the real delay time - return hleDelayResult(hleLogDebug(Log::Atrac, ret), "atrac set data", 100); + return hleDelayResult(hleLogDebug(Log::Atrac, ret), "atrac set data", AtracSetDataDelay(atrac)); } static u32 sceAtracSetSecondBuffer(int atracID, u32 secondBuffer, u32 secondBufferSize) { @@ -788,7 +806,7 @@ static u32 sceAtracSetData(int atracID, u32 buffer, u32 bufferSize) { return hleLogError(Log::Atrac, ret); } - return hleDelayResult(hleLogDebug(Log::Atrac, ret), "atrac set data", 100); + return hleDelayResult(hleLogDebug(Log::Atrac, ret), "atrac set data", AtracSetDataDelay(atrac)); } static int sceAtracSetDataAndGetID(u32 buffer, int bufferSize) { @@ -819,7 +837,7 @@ static int sceAtracSetDataAndGetID(u32 buffer, int bufferSize) { return hleLogError(Log::Atrac, ret); } - return hleDelayResult(hleLogDebug(Log::Atrac, atracID), "atrac set data", 100); + return hleDelayResult(hleLogDebug(Log::Atrac, atracID), "atrac set data", AtracSetDataDelay(atracContexts[atracID])); } static int sceAtracSetHalfwayBufferAndGetID(u32 buffer, u32 readSize, u32 bufferSize) { @@ -845,7 +863,7 @@ static int sceAtracSetHalfwayBufferAndGetID(u32 buffer, u32 readSize, u32 buffer return hleLogError(Log::Atrac, ret); } - return hleDelayResult(hleLogDebug(Log::Atrac, atracID), "atrac set data", 100); + return hleDelayResult(hleLogDebug(Log::Atrac, atracID), "atrac set data", AtracSetDataDelay(atracContexts[atracID])); } static u32 sceAtracStartEntry() { @@ -959,7 +977,7 @@ static int sceAtracSetMOutHalfwayBuffer(int atracID, u32 buffer, u32 readSize, u // Must not delay. return hleLogError(Log::Atrac, ret); } - return hleDelayResult(hleLogDebugOrError(Log::Atrac, ret), "atrac set data mono", 100); + return hleDelayResult(hleLogDebugOrError(Log::Atrac, ret), "atrac set data mono", AtracSetDataDelay(atrac)); } // Note: This doesn't seem to be part of any available libatrac3plus library. @@ -985,7 +1003,7 @@ static u32 sceAtracSetMOutData(int atracID, u32 buffer, u32 bufferSize) { return hleLogError(Log::Atrac, ret); } // It's OK if this fails, at least with NO_MONO... - return hleDelayResult(hleLogDebugOrError(Log::Atrac, ret), "atrac set data mono", 100); + return hleDelayResult(hleLogDebugOrError(Log::Atrac, ret), "atrac set data mono", AtracSetDataDelay(atrac)); } // Note: This doesn't seem to be part of any available libatrac3plus library. @@ -1012,7 +1030,7 @@ static int sceAtracSetMOutDataAndGetID(u32 buffer, u32 bufferSize) { UnregisterAndDeleteAtrac(atracID); return hleLogError(Log::Atrac, ret); } - return hleDelayResult(hleLogDebugOrError(Log::Atrac, atracID), "atrac set data", 100); + return hleDelayResult(hleLogDebugOrError(Log::Atrac, atracID), "atrac set data", AtracSetDataDelay(atracContexts[atracID])); } static int sceAtracSetMOutHalfwayBufferAndGetID(u32 buffer, u32 readSize, u32 bufferSize) { @@ -1041,7 +1059,7 @@ static int sceAtracSetMOutHalfwayBufferAndGetID(u32 buffer, u32 readSize, u32 bu UnregisterAndDeleteAtrac(atracID); return hleLogError(Log::Atrac, ret); } - return hleDelayResult(hleLogDebug(Log::Atrac, atracID), "atrac set data", 100); + return hleDelayResult(hleLogDebug(Log::Atrac, atracID), "atrac set data", AtracSetDataDelay(atracContexts[atracID])); } static int sceAtracSetAA3DataAndGetID(u32 buffer, u32 bufferSize, u32 fileSize, u32 metadataSizeAddr) { @@ -1063,7 +1081,7 @@ static int sceAtracSetAA3DataAndGetID(u32 buffer, u32 bufferSize, u32 fileSize, return hleLogError(Log::Atrac, ret); } - return hleDelayResult(hleLogDebug(Log::Atrac, atracID), "atrac set aa3 data", 100); + return hleDelayResult(hleLogDebug(Log::Atrac, atracID), "atrac set aa3 data", AtracSetDataDelay(atracContexts[atracID])); } static int sceAtracSetAA3HalfwayBufferAndGetID(u32 buffer, u32 readSize, u32 bufferSize, u32 fileSize) { @@ -1089,7 +1107,7 @@ static int sceAtracSetAA3HalfwayBufferAndGetID(u32 buffer, u32 readSize, u32 buf return hleLogError(Log::Atrac, ret); } - return hleDelayResult(hleLogDebug(Log::Atrac, atracID), "atrac set data", 100); + return hleDelayResult(hleLogDebug(Log::Atrac, atracID), "atrac set data", AtracSetDataDelay(atracContexts[atracID])); } // TODO: Should see if these are stored contiguously in memory somewhere, or if there really are @@ -1155,7 +1173,7 @@ static int sceAtracLowLevelDecode(int atracID, u32 sourceAddr, u32 sourceBytesCo } NotifyMemInfo(MemBlockFlags::WRITE, samplesAddr, bytesWritten, "AtracLowLevelDecode"); - return hleDelayResult(hleLogDebug(Log::Atrac, retval), "low level atrac decode data", atracDecodeDelay); + return hleDelayResult(hleLogDebug(Log::Atrac, retval), "low level atrac decode data", AtracDecodeDelay(atrac)); } // These three are the external interface used by sceSas' AT3 integration. diff --git a/Core/HLE/sceAudiocodec.cpp b/Core/HLE/sceAudiocodec.cpp index d13a64e6b0..c6947622c8 100644 --- a/Core/HLE/sceAudiocodec.cpp +++ b/Core/HLE/sceAudiocodec.cpp @@ -239,15 +239,19 @@ static int MECall(int result, int us) { return hleDelayResult(result, "audiocodec", MEScheduleJob(PowerScaleFromDefaultClock(us))); } -static int InitUs(int codec, const SceAudiocodecCodec *ctx) { +int AudioCodecInitUs(int codec, bool monoAt3Plus) { switch (codec) { - case PSP_CODEC_AT3PLUS: return ((ctx->fmt.at3.formatByte1 >> 2) & 7) == 1 ? 524 : 646; + case PSP_CODEC_AT3PLUS: return monoAt3Plus ? 524 : 646; case PSP_CODEC_AT3: return 210; case PSP_CODEC_MP3: return 517; default: return 230; } } +static int InitUs(int codec, const SceAudiocodecCodec *ctx) { + return AudioCodecInitUs(codec, codec == PSP_CODEC_AT3PLUS && ((ctx->fmt.at3.formatByte1 >> 2) & 7) == 1); +} + // libmp4.prx puts the sample rate here. On hardware 22050 and 44100 are accepted, 0 and 12345 // aren't; that the rest of the standard AAC rates are accepted is an assumption. 0 if not valid. static int AacSampleRateFromContext(const SceAudiocodecCodec *ctx) { @@ -395,6 +399,11 @@ static int EstimateDecodeUs(int codec, int channels, int frameBytes, const SceAu } } +int AudioCodecDecodeUs(int codec, int channels, int frameBytes) { + _dbg_assert_(codec == PSP_CODEC_AT3PLUS || codec == PSP_CODEC_AT3); + return EstimateDecodeUs(codec, channels, frameBytes, nullptr); +} + static int sceAudiocodecInit(u32 ctxPtr, int codec) { return __AudioCodecInitCommon(ctxPtr, codec, false); } diff --git a/Core/HLE/sceAudiocodec.h b/Core/HLE/sceAudiocodec.h index ce48f7598b..138005cd3a 100644 --- a/Core/HLE/sceAudiocodec.h +++ b/Core/HLE/sceAudiocodec.h @@ -128,3 +128,8 @@ class AudioDecoder; extern std::map g_audioDecoderContexts; bool IsAtrac3StreamJointStereo(int codecType, int bytesPerFrame, int channels); + +// ME time at the default clock, for sceAtrac too (it drives the same decoder): setting up a decoder, +// and decoding one frame. AudioCodecDecodeUs takes Atrac3 and Atrac3+ only. +int AudioCodecInitUs(int codec, bool monoAt3Plus); +int AudioCodecDecodeUs(int codec, int channels, int frameBytes);