From 178147b20abb89ca8b20cb0dbe4cd1e211e6a4af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 16 Apr 2024 19:54:48 +0200 Subject: [PATCH 1/4] Make asserts work better in headless when running tests on Windows --- Common/Log.cpp | 18 ++++++++++++++---- Common/Log.h | 1 + headless/Headless.cpp | 1 + 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/Common/Log.cpp b/Common/Log.cpp index 988e51f439..31c614a9aa 100644 --- a/Common/Log.cpp +++ b/Common/Log.cpp @@ -37,9 +37,10 @@ static bool hitAnyAsserts = false; -std::mutex g_extraAssertInfoMutex; -std::string g_extraAssertInfo = "menu"; -double g_assertInfoTime = 0.0; +static std::mutex g_extraAssertInfoMutex; +static std::string g_extraAssertInfo = "menu"; +static double g_assertInfoTime = 0.0; +static bool g_exitOnAssert; void SetExtraAssertInfo(const char *info) { std::lock_guard guard(g_extraAssertInfoMutex); @@ -47,6 +48,10 @@ void SetExtraAssertInfo(const char *info) { g_assertInfoTime = time_now_d(); } +void SetCleanExitOnAssert() { + g_exitOnAssert = true; +} + bool HandleAssert(const char *function, const char *file, int line, const char *expression, const char* format, ...) { // Read message and write it to the log char text[LOG_BUF_SIZE]; @@ -78,8 +83,13 @@ bool HandleAssert(const char *function, const char *file, int line, const char * std::wstring wtext = ConvertUTF8ToWString(formatted) + L"\n\nTry to continue?"; std::wstring wcaption = ConvertUTF8ToWString(std::string(caption) + " " + GetCurrentThreadName()); OutputDebugString(wtext.c_str()); + printf("%s\n", formatted); if (IDYES != MessageBox(0, wtext.c_str(), wcaption.c_str(), msgBoxStyle)) { - return false; + if (g_exitOnAssert) { + // Hard exit. + ExitProcess(1); + return false; + } } else { return true; } diff --git a/Common/Log.h b/Common/Log.h index 91b834b7fa..eaa20ac7c0 100644 --- a/Common/Log.h +++ b/Common/Log.h @@ -121,6 +121,7 @@ __attribute__((format(printf, 5, 6))) bool HitAnyAsserts(); void ResetHitAnyAsserts(); void SetExtraAssertInfo(const char *info); +void SetCleanExitOnAssert(); #if defined(__ANDROID__) // Tricky macro to get the basename, that also works if *built* on Win32. diff --git a/headless/Headless.cpp b/headless/Headless.cpp index a396c591a0..6ce62ffa81 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -319,6 +319,7 @@ int main(int argc, const char* argv[]) { PROFILE_INIT(); #if PPSSPP_PLATFORM(WINDOWS) + SetCleanExitOnAssert(); timeBeginPeriod(1); #else // Ignore sigpipe. From f05f9a6de33fd11ee20cf6e96a43b080dcef5a66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 16 Apr 2024 15:13:08 +0200 Subject: [PATCH 2/4] Code cleanup and comments --- Core/HLE/AtracCtx.cpp | 88 +++++++++++++++++++++++------------------- Core/HLE/AtracCtx.h | 66 ++++++++++++++++++++++--------- Core/HLE/AtracCtx2.cpp | 43 +++++++++++++++++---- Core/HLE/AtracCtx2.h | 1 - Core/HLE/sceAtrac.cpp | 6 +-- 5 files changed, 133 insertions(+), 71 deletions(-) diff --git a/Core/HLE/AtracCtx.cpp b/Core/HLE/AtracCtx.cpp index d8a2725e1a..a6e84bf580 100644 --- a/Core/HLE/AtracCtx.cpp +++ b/Core/HLE/AtracCtx.cpp @@ -64,9 +64,9 @@ void Atrac::DoState(PointerWrap &p) { Do(p, track_.endSample); Do(p, track_.firstSampleOffset); if (s >= 3) { - Do(p, track_.dataOff); + Do(p, track_.dataByteOffset); } else { - track_.dataOff = track_.firstSampleOffset; + track_.dataByteOffset = track_.firstSampleOffset; } u32 hasDataBuf = dataBuf_ != nullptr; @@ -127,10 +127,10 @@ void Atrac::DoState(PointerWrap &p) { Do(p, bufferValidBytes_); Do(p, bufferHeaderSize_); } else { - bufferHeaderSize_ = track_.dataOff; - bufferValidBytes_ = std::min(first_.size - track_.dataOff, StreamBufferEnd() - track_.dataOff); + bufferHeaderSize_ = track_.dataByteOffset; + bufferValidBytes_ = std::min(first_.size - track_.dataByteOffset, StreamBufferEnd() - track_.dataByteOffset); if ((bufferState_ & ATRAC_STATUS_STREAMED_MASK) == ATRAC_STATUS_STREAMED_MASK) { - bufferPos_ = track_.dataOff; + bufferPos_ = track_.dataByteOffset; } } @@ -211,18 +211,18 @@ void Atrac::WriteContextToPSPMem() { // TODO: Should we just keep this in PSP ram then, or something? context->info.state = bufferState_; if (track_.firstSampleOffset != 0) { - context->info.samplesPerChan = track_.firstSampleOffset + FirstOffsetExtra(); + context->info.samplesPerChan = track_.FirstSampleOffsetFull(); } else { context->info.samplesPerChan = (track_.codecType == PSP_MODE_AT_3_PLUS ? ATRAC3PLUS_MAX_SAMPLES : ATRAC3_MAX_SAMPLES); } context->info.sampleSize = track_.bytesPerFrame; context->info.numChan = track_.channels; - context->info.dataOff = track_.dataOff; - context->info.endSample = track_.endSample + track_.firstSampleOffset + FirstOffsetExtra(); + context->info.dataOff = track_.dataByteOffset; + context->info.endSample = track_.endSample + track_.FirstSampleOffsetFull(); context->info.dataEnd = track_.fileSize; context->info.curOff = first_.fileoffset; context->info.decodePos = track_.DecodePosBySample(currentSample_); - context->info.streamDataByte = first_.size - track_.dataOff; + context->info.streamDataByte = first_.size - track_.dataByteOffset; u8 *buf = (u8 *)context; *(u32_le *)(buf + 0xfc) = atracID_; @@ -230,6 +230,13 @@ void Atrac::WriteContextToPSPMem() { NotifyMemInfo(MemBlockFlags::WRITE, context_.ptr, sizeof(SceAtracContext), "AtracContext"); } +void Track::DebugLog() { + DEBUG_LOG(ME, "ATRAC analyzed: %s channels: %d filesize: %d bitrate: %d kbps jointStereo: %d", + codecType == PSP_MODE_AT_3 ? "AT3" : "AT3Plus", channels, fileSize, bitrate / 1024, jointStereo); + DEBUG_LOG(ME, "dataoff: %d firstSampleOffset: %d endSample: %d", dataByteOffset, firstSampleOffset, endSample); + DEBUG_LOG(ME, "loopStartSample: %d loopEndSample: %d", loopStartSample, loopEndSample); +} + int Atrac::Analyze(u32 addr, u32 size) { track_ = {}; first_ = {}; @@ -255,6 +262,7 @@ int Atrac::Analyze(u32 addr, u32 size) { int retval = AnalyzeAtracTrack(addr, size, &track_); first_._filesize_dontuse = track_.fileSize; + track_.DebugLog(); return retval; } @@ -397,7 +405,7 @@ int AnalyzeAtracTrack(u32 addr, u32 size, Track *track) { case DATA_CHUNK_MAGIC: { bfoundData = true; - track->dataOff = offset; + track->dataByteOffset = offset; dataChunkSize = chunkSize; if (track->fileSize < offset + chunkSize) { WARN_LOG_REPORT(ME, "Atrac data chunk extends beyond riff chunk"); @@ -419,8 +427,8 @@ int AnalyzeAtracTrack(u32 addr, u32 size, Track *track) { // set the loopStartSample_ and loopEndSample_ by loopinfo_ if (track->loopinfo.size() > 0) { - track->loopStartSample = track->loopinfo[0].startSample + ::FirstOffsetExtra(track->codecType) + sampleOffsetAdjust; - track->loopEndSample = track->loopinfo[0].endSample + ::FirstOffsetExtra(track->codecType) + sampleOffsetAdjust; + track->loopStartSample = track->loopinfo[0].startSample + track->FirstOffsetExtra() + sampleOffsetAdjust; + track->loopEndSample = track->loopinfo[0].endSample + track->FirstOffsetExtra() + sampleOffsetAdjust; } else { track->loopStartSample = -1; track->loopEndSample = -1; @@ -429,11 +437,11 @@ int AnalyzeAtracTrack(u32 addr, u32 size, Track *track) { // if there is no correct endsample, try to guess it if (track->endSample <= 0 && track->bytesPerFrame != 0) { track->endSample = (dataChunkSize / track->bytesPerFrame) * track->SamplesPerFrame(); - track->endSample -= track->firstSampleOffset + ::FirstOffsetExtra(track->codecType); + track->endSample -= track->FirstSampleOffsetFull(); } track->endSample -= 1; - if (track->loopEndSample != -1 && track->loopEndSample > track->endSample + track->firstSampleOffset + (int)::FirstOffsetExtra(track->codecType)) { + if (track->loopEndSample != -1 && track->loopEndSample > track->endSample + track->FirstSampleOffsetFull()) { return hleReportError(ME, ATRAC_ERROR_BAD_CODEC_PARAMS, "loop after end of data"); } @@ -501,10 +509,10 @@ int AnalyzeAA3Track(u32 addr, u32 size, u32 fileSize, Track *track) { return hleReportError(ME, ATRAC_ERROR_AA3_INVALID_DATA, "invalid codec type %d", buffer[32]); } - track->dataOff = 10 + tagSize + 96; + track->dataByteOffset = 10 + tagSize + 96; track->firstSampleOffset = 0; if (track->endSample < 0 && track->bytesPerFrame != 0) { - track->endSample = ((track->fileSize - track->dataOff) / track->bytesPerFrame) * track->SamplesPerFrame(); + track->endSample = ((track->fileSize - track->dataByteOffset) / track->bytesPerFrame) * track->SamplesPerFrame(); } track->endSample -= 1; return 0; @@ -541,7 +549,7 @@ void Atrac::CalculateStreamInfo(u32 *outReadOffset) { first_.offset = 0; first_.writableBytes = 0; } else { - readOffset = track_.FileOffsetBySample(track_.loopStartSample - FirstOffsetExtra() - track_.firstSampleOffset - track_.SamplesPerFrame() * 2); + readOffset = track_.FileOffsetBySample(track_.loopStartSample - track_.FirstSampleOffsetFull() - track_.SamplesPerFrame() * 2); } } @@ -609,7 +617,7 @@ void Atrac::GetResetBufferInfo(AtracResetBufferInfo *bufferInfo, int sample) { // Update the writable bytes. When streaming, this is just the number of bytes until the end. const u32 bufSizeAligned = (bufferMaxSize_ / track_.bytesPerFrame) * track_.bytesPerFrame; - const int needsMoreFrames = FirstOffsetExtra(); + const int needsMoreFrames = track_.FirstOffsetExtra(); // ? bufferInfo->first.writePosPtr = first_.addr; bufferInfo->first.writableBytes = std::min(track_.fileSize - sampleFileOffset, bufSizeAligned); @@ -619,7 +627,7 @@ void Atrac::GetResetBufferInfo(AtracResetBufferInfo *bufferInfo, int sample) { } else { bufferInfo->first.minWriteBytes = track_.bytesPerFrame * 2; } - if ((u32)sample < (u32)track_.firstSampleOffset && sampleFileOffset != track_.dataOff) { + if ((u32)sample < (u32)track_.firstSampleOffset && sampleFileOffset != track_.dataByteOffset) { sampleFileOffset -= track_.bytesPerFrame; } bufferInfo->first.filePos = sampleFileOffset; @@ -670,8 +678,8 @@ int Atrac::SetData(u32 buffer, u32 readSize, u32 bufferSize, int outputChannels, ignoreDataBuf_ = true; } if (bufferState_ == ATRAC_STATUS_STREAMED_WITHOUT_LOOP || bufferState_ == ATRAC_STATUS_STREAMED_LOOP_FROM_END || bufferState_ == ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER) { - bufferHeaderSize_ = track_.dataOff; - bufferPos_ = track_.dataOff + track_.bytesPerFrame; + bufferHeaderSize_ = track_.dataByteOffset; + bufferPos_ = track_.dataByteOffset + track_.bytesPerFrame; bufferValidBytes_ = first_.size - bufferPos_; } @@ -744,7 +752,7 @@ void Atrac::UpdateBufferState() { if (track_.loopEndSample <= 0) { // There's no looping, but we need to stream the data in our buffer. bufferState_ = ATRAC_STATUS_STREAMED_WITHOUT_LOOP; - } else if (track_.loopEndSample == track_.endSample + track_.firstSampleOffset + (int)FirstOffsetExtra()) { + } else if (track_.loopEndSample == track_.endSample + track_.FirstSampleOffsetFull()) { bufferState_ = ATRAC_STATUS_STREAMED_LOOP_FROM_END; } else { bufferState_ = ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER; @@ -779,15 +787,15 @@ int Atrac::AddStreamData(u32 bytesToAdd) { if (PSP_CoreParameter().compat.flags().AtracLoopHack && bufferState_ == ATRAC_STATUS_STREAMED_LOOP_FROM_END && RemainingFrames() > 2) { loopNum_++; - SeekToSample(track_.loopStartSample - FirstOffsetExtra() - track_.firstSampleOffset); + SeekToSample(track_.loopStartSample - track_.FirstSampleOffsetFull()); } return 0; } u32 Atrac::AddStreamDataSas(u32 bufPtr, u32 bytesToAdd) { - int addbytes = std::min(bytesToAdd, track_.fileSize - first_.fileoffset - FirstOffsetExtra()); - Memory::Memcpy(dataBuf_ + first_.fileoffset + FirstOffsetExtra(), bufPtr, addbytes, "AtracAddStreamData"); + int addbytes = std::min(bytesToAdd, track_.fileSize - first_.fileoffset - track_.FirstOffsetExtra()); + Memory::Memcpy(dataBuf_ + first_.fileoffset + track_.FirstOffsetExtra(), bufPtr, addbytes, "AtracAddStreamData"); first_.size += bytesToAdd; if (first_.size >= track_.fileSize) { first_.size = track_.fileSize; @@ -802,7 +810,7 @@ u32 Atrac::AddStreamDataSas(u32 bufPtr, u32 bytesToAdd) { u32 Atrac::GetNextSamples() { // It seems like the PSP aligns the sample position to 0x800...? - u32 skipSamples = track_.firstSampleOffset + FirstOffsetExtra(); + u32 skipSamples = track_.FirstSampleOffsetFull(); u32 firstSamples = (track_.SamplesPerFrame() - skipSamples) % track_.SamplesPerFrame(); u32 numSamples = track_.endSample + 1 - currentSample_; if (currentSample_ == 0 && firstSamples != 0) { @@ -830,7 +838,7 @@ void Atrac::ForceSeekToSample(int sample) { void Atrac::SeekToSample(int sample) { // It seems like the PSP aligns the sample position to 0x800...? - const u32 offsetSamples = track_.firstSampleOffset + FirstOffsetExtra(); + const u32 offsetSamples = track_.FirstSampleOffsetFull(); const u32 unalignedSamples = (offsetSamples + sample) % track_.SamplesPerFrame(); int seekFrame = sample + offsetSamples - unalignedSamples; @@ -840,12 +848,12 @@ void Atrac::SeekToSample(int sample) { int adjust = 0; if (sample == 0) { - int offsetSamples = track_.firstSampleOffset + FirstOffsetExtra(); + int offsetSamples = track_.FirstSampleOffsetFull(); adjust = -(int)(offsetSamples % track_.SamplesPerFrame()); } const u32 off = track_.FileOffsetBySample(sample + adjust); const u32 backfill = track_.bytesPerFrame * 2; - const u32 start = off - track_.dataOff < backfill ? track_.dataOff : off - backfill; + const u32 start = off - track_.dataByteOffset < backfill ? track_.dataByteOffset : off - backfill; for (u32 pos = start; pos < off; pos += track_.bytesPerFrame) { decoder_->Decode(BufferStart() + pos, track_.bytesPerFrame, nullptr, 2, nullptr, nullptr); @@ -861,12 +869,12 @@ int Atrac::RemainingFrames() const { return PSP_ATRAC_ALLDATA_IS_ON_MEMORY; } - u32 currentFileOffset = track_.FileOffsetBySample(currentSample_ - track_.SamplesPerFrame() + FirstOffsetExtra()); + u32 currentFileOffset = track_.FileOffsetBySample(currentSample_ - track_.SamplesPerFrame() + track_.FirstOffsetExtra()); if (first_.fileoffset >= track_.fileSize) { if (bufferState_ == ATRAC_STATUS_STREAMED_WITHOUT_LOOP) { return PSP_ATRAC_NONLOOP_STREAM_DATA_IS_ON_MEMORY; } - int loopEndAdjusted = track_.loopEndSample - FirstOffsetExtra() - track_.firstSampleOffset; + int loopEndAdjusted = track_.loopEndSample - track_.FirstOffsetExtra() - track_.firstSampleOffset; if (bufferState_ == ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER && currentSample_ > loopEndAdjusted) { // No longer looping in this case, outside the loop. return PSP_ATRAC_NONLOOP_STREAM_DATA_IS_ON_MEMORY; @@ -926,7 +934,7 @@ u32 Atrac::DecodeData(u8 *outbuf, u32 outbufPtr, u32 *SamplesNum, u32 *finish, i u32 numSamples = 0; // It seems like the PSP aligns the sample position to 0x800...? - int offsetSamples = track_.firstSampleOffset + FirstOffsetExtra(); + int offsetSamples = track_.FirstSampleOffsetFull(); int skipSamples = 0; u32 maxSamples = track_.endSample + 1 - currentSample_; u32 unalignedSamples = (offsetSamples + currentSample_) % track_.SamplesPerFrame(); @@ -1001,20 +1009,20 @@ u32 Atrac::DecodeData(u8 *outbuf, u32 outbufPtr, u32 *SamplesNum, u32 *finish, i int finishFlag = 0; // TODO: Verify. bool hitEnd = currentSample_ >= track_.endSample || (numSamples == 0 && first_.size >= track_.fileSize); - int loopEndAdjusted = track_.loopEndSample - FirstOffsetExtra() - track_.firstSampleOffset; + int loopEndAdjusted = track_.loopEndSample - track_.FirstSampleOffsetFull(); if ((hitEnd || currentSample_ > loopEndAdjusted) && loopNum != 0) { - SeekToSample(track_.loopStartSample - FirstOffsetExtra() - track_.firstSampleOffset); + SeekToSample(track_.loopStartSample - track_.FirstSampleOffsetFull()); if (bufferState_ != ATRAC_STATUS_FOR_SCESAS) { if (loopNum_ > 0) loopNum_--; } if ((bufferState_ & ATRAC_STATUS_STREAMED_MASK) == ATRAC_STATUS_STREAMED_MASK) { // Whatever bytes we have left were added from the loop. - u32 loopOffset = track_.FileOffsetBySample(track_.loopStartSample - FirstOffsetExtra() - track_.firstSampleOffset - track_.SamplesPerFrame() * 2); + u32 loopOffset = track_.FileOffsetBySample(track_.loopStartSample - track_.FirstSampleOffsetFull() - track_.SamplesPerFrame() * 2); // TODO: Hmm, need to manage the buffer better. But don't move fileoffset if we already have valid data. if (loopOffset > first_.fileoffset || loopOffset + bufferValidBytes_ < first_.fileoffset) { // Skip the initial frame at the start. - first_.fileoffset = track_.FileOffsetBySample(track_.loopStartSample - FirstOffsetExtra() - track_.firstSampleOffset - track_.SamplesPerFrame() * 2); + first_.fileoffset = track_.FileOffsetBySample(track_.loopStartSample - track_.FirstSampleOffsetFull() - track_.SamplesPerFrame() * 2); } } } else if (hitEnd) { @@ -1032,15 +1040,15 @@ u32 Atrac::DecodeData(u8 *outbuf, u32 outbufPtr, u32 *SamplesNum, u32 *finish, i return 0; } -void Atrac::SetLoopNum(int loopNum) { +void AtracBase::SetLoopNum(int loopNum) { // Spammed in MHU loopNum_ = loopNum; if (loopNum != 0 && track_.loopinfo.size() == 0) { // Just loop the whole audio // This is a rare modification of track_ after the fact. // Maybe we can get away with setting these by default. - track_.loopStartSample = track_.firstSampleOffset + FirstOffsetExtra(); - track_.loopEndSample = track_.endSample + track_.firstSampleOffset + FirstOffsetExtra(); + track_.loopStartSample = track_.FirstSampleOffsetFull(); + track_.loopEndSample = track_.endSample + track_.FirstSampleOffsetFull(); } WriteContextToPSPMem(); } @@ -1123,7 +1131,7 @@ void Atrac::InitLowLevel(u32 paramsAddr, bool jointStereo) { track_.jointStereo = false; } - track_.dataOff = 0; + track_.dataByteOffset = 0; first_.size = 0; track_.fileSize = track_.bytesPerFrame; // not really meaningful bufferState_ = ATRAC_STATUS_LOW_LEVEL; diff --git a/Core/HLE/AtracCtx.h b/Core/HLE/AtracCtx.h index fdaa82b5ec..a0cda78203 100644 --- a/Core/HLE/AtracCtx.h +++ b/Core/HLE/AtracCtx.h @@ -105,24 +105,42 @@ struct AtracLoopInfo { class AudioDecoder; -inline u32 FirstOffsetExtra(int codecType) { - return codecType == PSP_MODE_AT_3_PLUS ? 368 : 69; -} - struct Track { - u32 fileSize; // This both does and doesn't belong in Track - it's fixed for an Atrac instance. Oh well. - u32 codecType; - u16 bytesPerFrame; - int firstSampleOffset; - int endSample; - // Offset of the first sample in the input buffer - int dataOff = 0; + u32 codecType = 0; + + // Size of the full track being streamed or played. Can be a lot larger than the in-memory buffer in the streaming modes. + u32 fileSize = 0; + + // Not really used for much except queries, this keeps track of the bitrate of the track. u32 bitrate = 64; + + // Signifies whether to use a more efficient coding mode with less stereo separation. For our purposes, just metadata. int jointStereo = 0; + + // Number of audio channels in the track. u16 channels = 0; + // The size of an encoded frame in bytes. + u16 bytesPerFrame = 0; + + // Byte offset of the first encoded frame in the input buffer. Note: Some samples may be skipped according to firstSampleOffset. + int dataByteOffset = 0; + + // How many samples to skip from the beginning of a track when decoding. + // Actually, the real number is this added to FirstOffsetExtra(codecType). You can call + // FirstSampleOffset2() to get that. + // Some use of these offsets around the code seem to be inconsistent, sometimes the extra is included, + // sometimes not. + int firstSampleOffset = 0; + + // Last sample number. Though, we made it so in Analyze, it's exclusive in the file. + // Does not take firstSampleOffset into account. + int endSample = 0; + + // Loop configuration. The PSP only supports one loop but we store them all. std::vector loopinfo; + // The actual used loop offsets. These appear to be raw offsets, not taking FirstSampleOffset2() into account. int loopStartSample = -1; int loopEndSample = -1; @@ -131,7 +149,16 @@ struct Track { return bytesPerFrame; } - // Output frame size + inline int FirstOffsetExtra() const { + return codecType == PSP_MODE_AT_3_PLUS ? 368 : 69; + } + + // Includes the extra offset. See firstSampleOffset comment above. + int FirstSampleOffsetFull() const { + return FirstOffsetExtra() + firstSampleOffset; + } + + // Output frame size, different between the two supported codecs. u32 SamplesPerFrame() const { return codecType == PSP_MODE_AT_3_PLUS ? ATRAC3PLUS_MAX_SAMPLES : ATRAC3_MAX_SAMPLES; } @@ -144,14 +171,17 @@ struct Track { bitrate = (bitrate + 511) >> 10; } + // This appears to be buggy, should probably include FirstOffsetExtra? + // Actually the units don't even make sense here. u32 DecodePosBySample(int sample) const { return (u32)(firstSampleOffset + sample / (int)SamplesPerFrame() * bytesPerFrame); } + // This appears to be buggy, should probably include FirstOffsetExtra? u32 FileOffsetBySample(int sample) const { int offsetSample = sample + firstSampleOffset; int frameOffset = offsetSample / (int)SamplesPerFrame(); - return (u32)(dataOff + bytesPerFrame + frameOffset * bytesPerFrame); + return (u32)(dataByteOffset + bytesPerFrame + frameOffset * bytesPerFrame); } void AnalyzeReset() { @@ -162,6 +192,8 @@ struct Track { channels = 2; // TODO: Could probably reset more. } + + void DebugLog(); }; int AnalyzeAA3Track(u32 addr, u32 size, u32 filesize, Track *track); @@ -202,9 +234,6 @@ public: AudioDecoder *Decoder() const { return decoder_; } - u32 FirstOffsetExtra() const { - return ::FirstOffsetExtra(track_.codecType); - } void CreateDecoder(); @@ -221,7 +250,7 @@ public: virtual void GetStreamDataInfo(u32 *writePtr, u32 *writableBytes, u32 *readOffset) = 0; virtual int AddStreamData(u32 bytesToAdd) = 0; virtual u32 AddStreamDataSas(u32 bufPtr, u32 bytesToAdd) = 0; - virtual void SetLoopNum(int loopNum) = 0; + virtual void SetLoopNum(int loopNum); virtual u32 ResetPlayPosition(int sample, int bytesWrittenFirstBuf, int bytesWrittenSecondBuf) = 0; virtual void GetResetBufferInfo(AtracResetBufferInfo *bufferInfo, int sample) = 0; virtual int SetData(u32 buffer, u32 readSize, u32 bufferSize, int outputChannels, int successCode) = 0; @@ -274,7 +303,6 @@ public: void GetStreamDataInfo(u32 *writePtr, u32 *writableBytes, u32 *readOffset) override; int AddStreamData(u32 bytesToAdd) override; u32 AddStreamDataSas(u32 bufPtr, u32 bytesToAdd) override; - void SetLoopNum(int loopNum) override; u32 ResetPlayPosition(int sample, int bytesWrittenFirstBuf, int bytesWrittenSecondBuf) override; void GetResetBufferInfo(AtracResetBufferInfo *bufferInfo, int sample) override; int SetData(u32 buffer, u32 readSize, u32 bufferSize, int outputChannels, int successCode) override; @@ -302,7 +330,7 @@ private: void CalculateStreamInfo(u32 *readOffset); InputBuffer first_{}; - InputBuffer second_{}; + InputBuffer second_{}; // only addr, size, fileoffset are used (incomplete) u8 *dataBuf_ = nullptr; // Indicates that the dataBuf_ array should not be used. diff --git a/Core/HLE/AtracCtx2.cpp b/Core/HLE/AtracCtx2.cpp index f0a6790eb3..7f99bef720 100644 --- a/Core/HLE/AtracCtx2.cpp +++ b/Core/HLE/AtracCtx2.cpp @@ -3,6 +3,37 @@ #include "Core/HLE/HLE.h" #include "Core/HLE/FunctionWrappers.h" #include "Core/HLE/AtracCtx2.h" +#include "Core/HW/Atrac3Standalone.h" + +// Convenient command line: +// Windows\x64\debug\PPSSPPHeadless.exe --root pspautotests/tests/../ --compare --timeout=5 --new-atrac --graphics=software pspautotests/tests/audio/atrac/decode.prx + +// See the big comment in sceAtrac.cpp for an overview of the different modes of operation. +// +// Test cases +// +// Halfway buffer +// +// * None found yet +// +// All-data-loaded +// +// * MotoGP (menu music with specified loop). Simple repeated calls to sceAtracDecodeData +// * Archer MacLean's Mercury (in-game, not menu) +// * Crisis Core +// +// Streaming +// +// - Good ones (early) +// * Everybody's Golf 2 (0x2000 buffer size, loop from end) +// * Burnout Legends (no loop, 0x1800 buffer size) +// * Suicide Barbie +// - Others +// * Bleach +// * God of War: Chains of Olympus +// * Ape Academy 2 (bufsize 8192) +// * Half Minute Hero (bufsize 65536) +// * Flatout (tricky! needs investigation) void Atrac2::DoState(PointerWrap &p) { _assert_msg_(false, "Savestates not yet support with new Atrac implementation.\n\nTurn it off in Developer settings.\n\n"); @@ -28,14 +59,14 @@ void Atrac2::WriteContextToPSPMem() { // TODO: Should we just keep this in PSP ram then, or something? context->info.state = bufferState_; if (track_.firstSampleOffset != 0) { - context->info.samplesPerChan = track_.firstSampleOffset + FirstOffsetExtra(); + context->info.samplesPerChan = track_.FirstSampleOffsetFull(); } else { context->info.samplesPerChan = (track_.codecType == PSP_MODE_AT_3_PLUS ? ATRAC3PLUS_MAX_SAMPLES : ATRAC3_MAX_SAMPLES); } context->info.sampleSize = track_.bytesPerFrame; context->info.numChan = track_.channels; - context->info.dataOff = track_.dataOff; - context->info.endSample = track_.endSample + track_.firstSampleOffset + FirstOffsetExtra(); + context->info.dataOff = track_.dataByteOffset; + context->info.endSample = track_.endSample + track_.FirstSampleOffsetFull(); context->info.dataEnd = track_.fileSize; context->info.curOff = 0; // first_.fileoffset; context->info.decodePos = track_.DecodePosBySample(currentSample_); @@ -83,10 +114,6 @@ u32 Atrac2::AddStreamDataSas(u32 bufPtr, u32 bytesToAdd) { return 0; } -void Atrac2::SetLoopNum(int loopNum) { - -} - u32 Atrac2::ResetPlayPosition(int sample, int bytesWrittenFirstBuf, int bytesWrittenSecondBuf) { return 0; } @@ -137,7 +164,7 @@ void Atrac2::InitLowLevel(u32 paramsAddr, bool jointStereo) { track_.bitrate = ((track_.bitrate >> 11) + 8) & 0xFFFFFFF0; track_.jointStereo = false; } - track_.dataOff = 0; + track_.dataByteOffset = 0; bufferState_ = ATRAC_STATUS_LOW_LEVEL; currentSample_ = 0; CreateDecoder(); diff --git a/Core/HLE/AtracCtx2.h b/Core/HLE/AtracCtx2.h index df6734d285..3c8f1fab7f 100644 --- a/Core/HLE/AtracCtx2.h +++ b/Core/HLE/AtracCtx2.h @@ -20,7 +20,6 @@ public: void GetStreamDataInfo(u32 *writePtr, u32 *writableBytes, u32 *readOffset) override; int AddStreamData(u32 bytesToAdd) override; u32 AddStreamDataSas(u32 bufPtr, u32 bytesToAdd) override; - void SetLoopNum(int loopNum) override; u32 ResetPlayPosition(int sample, int bytesWrittenFirstBuf, int bytesWrittenSecondBuf) override; void GetResetBufferInfo(AtracResetBufferInfo *bufferInfo, int sample) override; int SetData(u32 buffer, u32 readSize, u32 bufferSize, int outputChannels, int successCode) override; diff --git a/Core/HLE/sceAtrac.cpp b/Core/HLE/sceAtrac.cpp index 15513b031d..c57c826d2e 100644 --- a/Core/HLE/sceAtrac.cpp +++ b/Core/HLE/sceAtrac.cpp @@ -405,7 +405,7 @@ static u32 sceAtracGetNextDecodePosition(int atracID, u32 outposAddr) { if (Memory::IsValidAddress(outposAddr)) { if (atrac->CurrentSample() >= atrac->GetTrack().endSample) { Memory::WriteUnchecked_U32(0, outposAddr); - return hleLogSuccessI(ME, ATRAC_ERROR_ALL_DATA_DECODED, "all data decoded"); + return hleLogSuccessI(ME, ATRAC_ERROR_ALL_DATA_DECODED, "all data decoded - beyond endSample"); } else { Memory::WriteUnchecked_U32(atrac->CurrentSample(), outposAddr); return hleLogSuccessI(ME, 0); @@ -488,10 +488,10 @@ static u32 sceAtracGetSoundSample(int atracID, u32 outEndSampleAddr, u32 outLoop *outEndSample = atrac->GetTrack().endSample; auto outLoopStart = PSPPointer::Create(outLoopStartSampleAddr); if (outLoopStart.IsValid()) - *outLoopStart = atrac->GetTrack().loopStartSample == -1 ? -1 : atrac->GetTrack().loopStartSample - atrac->GetTrack().firstSampleOffset - atrac->FirstOffsetExtra(); + *outLoopStart = atrac->GetTrack().loopStartSample == -1 ? -1 : atrac->GetTrack().loopStartSample - atrac->GetTrack().FirstSampleOffsetFull(); auto outLoopEnd = PSPPointer::Create(outLoopEndSampleAddr); if (outLoopEnd.IsValid()) - *outLoopEnd = atrac->GetTrack().loopEndSample == -1 ? -1 : atrac->GetTrack().loopEndSample - atrac->GetTrack().firstSampleOffset - atrac->FirstOffsetExtra(); + *outLoopEnd = atrac->GetTrack().loopEndSample == -1 ? -1 : atrac->GetTrack().loopEndSample - atrac->GetTrack().FirstSampleOffsetFull(); if (!outEndSample.IsValid() || !outLoopStart.IsValid() || !outLoopEnd.IsValid()) { return hleReportError(ME, 0, "invalid address"); From 5b625bfd3e8c38bbc67e7861cb8280d67476a670 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 16 Apr 2024 14:53:43 +0200 Subject: [PATCH 3/4] Share GetSecondBufferInfo between implementations --- Core/HLE/AtracCtx.cpp | 4 +++- Core/HLE/AtracCtx.h | 4 ++-- Core/HLE/AtracCtx2.cpp | 6 ------ Core/HLE/AtracCtx2.h | 4 ++-- 4 files changed, 7 insertions(+), 11 deletions(-) diff --git a/Core/HLE/AtracCtx.cpp b/Core/HLE/AtracCtx.cpp index a6e84bf580..592d6b64e4 100644 --- a/Core/HLE/AtracCtx.cpp +++ b/Core/HLE/AtracCtx.cpp @@ -717,7 +717,7 @@ u32 Atrac::SetSecondBuffer(u32 secondBuffer, u32 secondBufferSize) { return hleLogSuccessI(ME, 0); } -int Atrac::GetSecondBufferInfo(u32 *fileOffset, u32 *desiredSize) { +int AtracBase::GetSecondBufferInfo(u32 *fileOffset, u32 *desiredSize) { if (BufferState() != ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER) { // Writes zeroes in this error case. *fileOffset = 0; @@ -947,6 +947,8 @@ u32 Atrac::DecodeData(u8 *outbuf, u32 outbufPtr, u32 *SamplesNum, u32 *finish, i if (skipSamples != 0 && bufferHeaderSize_ == 0) { // Skip the initial frame used to load state for the looped frame. // TODO: We will want to actually read this in. + // TODO again: This seems to happen on the first frame of playback regardless of loops. + // Can't be good. ConsumeFrame(); } diff --git a/Core/HLE/AtracCtx.h b/Core/HLE/AtracCtx.h index a0cda78203..0f5245c259 100644 --- a/Core/HLE/AtracCtx.h +++ b/Core/HLE/AtracCtx.h @@ -254,8 +254,9 @@ public: virtual u32 ResetPlayPosition(int sample, int bytesWrittenFirstBuf, int bytesWrittenSecondBuf) = 0; virtual void GetResetBufferInfo(AtracResetBufferInfo *bufferInfo, int sample) = 0; virtual int SetData(u32 buffer, u32 readSize, u32 bufferSize, int outputChannels, int successCode) = 0; + + virtual int GetSecondBufferInfo(u32 *fileOffset, u32 *desiredSize); virtual u32 SetSecondBuffer(u32 secondBuffer, u32 secondBufferSize) = 0; - virtual int GetSecondBufferInfo(u32 *fileOffset, u32 *desiredSize) = 0; virtual u32 DecodeData(u8 *outbuf, u32 outbufPtr, u32 *SamplesNum, u32 *finish, int *remains) = 0; virtual u32 GetNextSamples() = 0; virtual void InitLowLevel(u32 paramsAddr, bool jointStereo) = 0; @@ -307,7 +308,6 @@ public: void GetResetBufferInfo(AtracResetBufferInfo *bufferInfo, int sample) override; int SetData(u32 buffer, u32 readSize, u32 bufferSize, int outputChannels, int successCode) override; u32 SetSecondBuffer(u32 secondBuffer, u32 secondBufferSize) override; - int GetSecondBufferInfo(u32 *fileOffset, u32 *desiredSize) override; u32 DecodeData(u8 *outbuf, u32 outbufPtr, u32 *SamplesNum, u32 *finish, int *remains) override; u32 GetNextSamples() override; void InitLowLevel(u32 paramsAddr, bool jointStereo) override; diff --git a/Core/HLE/AtracCtx2.cpp b/Core/HLE/AtracCtx2.cpp index 7f99bef720..b30f3444f3 100644 --- a/Core/HLE/AtracCtx2.cpp +++ b/Core/HLE/AtracCtx2.cpp @@ -135,12 +135,6 @@ u32 Atrac2::SetSecondBuffer(u32 secondBuffer, u32 secondBufferSize) { return 0; } -int Atrac2::GetSecondBufferInfo(u32 *fileOffset, u32 *desiredSize) { - *fileOffset = 0; - *desiredSize = 0; - return hleLogSuccessInfoI(ME, ATRAC_ERROR_SECOND_BUFFER_NOT_NEEDED, "not needed"); -} - u32 Atrac2::DecodeData(u8 *outbuf, u32 outbufPtr, u32 *SamplesNum, u32 *finish, int *remains) { return 0; diff --git a/Core/HLE/AtracCtx2.h b/Core/HLE/AtracCtx2.h index 3c8f1fab7f..aeebec399f 100644 --- a/Core/HLE/AtracCtx2.h +++ b/Core/HLE/AtracCtx2.h @@ -15,7 +15,6 @@ public: int CurrentSample() const override { return currentSample_; } int RemainingFrames() const override; - u32 SecondBufferSize() const override; void GetStreamDataInfo(u32 *writePtr, u32 *writableBytes, u32 *readOffset) override; int AddStreamData(u32 bytesToAdd) override; @@ -24,7 +23,8 @@ public: void GetResetBufferInfo(AtracResetBufferInfo *bufferInfo, int sample) override; int SetData(u32 buffer, u32 readSize, u32 bufferSize, int outputChannels, int successCode) override; u32 SetSecondBuffer(u32 secondBuffer, u32 secondBufferSize) override; - int GetSecondBufferInfo(u32 *fileOffset, u32 *desiredSize) override; + u32 SecondBufferSize() const override; + u32 DecodeData(u8 *outbuf, u32 outbufPtr, u32 *SamplesNum, u32 *finish, int *remains) override; u32 GetNextSamples() override; void InitLowLevel(u32 paramsAddr, bool jointStereo) override; From a74a0fdbde4e64dde33fdf95e9d8ef4a0314d7ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 16 Apr 2024 12:10:40 +0200 Subject: [PATCH 4/4] Add a way to test new atrac using headless. --- headless/Headless.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/headless/Headless.cpp b/headless/Headless.cpp index 6ce62ffa81..4d84a1f265 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -339,6 +339,7 @@ int main(int argc, const char* argv[]) GPUCore gpuCore = GPUCORE_SOFTWARE; CPUCore cpuCore = CPUCore::JIT; int debuggerPort = -1; + bool newAtrac = false; std::vector testFilenames; const char *mountIso = nullptr; @@ -375,6 +376,8 @@ int main(int argc, const char* argv[]) testOptions.bench = true; else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose")) testOptions.verbose = true; + else if (!strcmp(argv[i], "--new-atrac")) + newAtrac = true; else if (!strncmp(argv[i], "--graphics=", strlen("--graphics=")) && strlen(argv[i]) > strlen("--graphics=")) { const char *gpuName = argv[i] + strlen("--graphics="); @@ -498,6 +501,7 @@ int main(int argc, const char* argv[]) g_Config.iGlobalVolume = VOLUME_FULL; g_Config.iReverbVolume = VOLUME_FULL; g_Config.internalDataDirectory.clear(); + g_Config.bUseNewAtrac = newAtrac; Path exePath = File::GetExeDirectory(); g_Config.flash0Directory = exePath / "assets/flash0";