diff --git a/CMakeLists.txt b/CMakeLists.txt
index febe3663ac..64a10be42c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2079,6 +2079,8 @@ add_library(${CoreLibName} ${CoreLinkType}
Core/HLE/__sceAudio.h
Core/HLE/sceAdler.cpp
Core/HLE/sceAdler.h
+ Core/HLE/AtracCtx.cpp
+ Core/HLE/AtracCtx.h
Core/HLE/sceAtrac.cpp
Core/HLE/sceAtrac.h
Core/HLE/sceAudio.cpp
diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj
index e88cd217ae..2cdf17d477 100644
--- a/Core/Core.vcxproj
+++ b/Core/Core.vcxproj
@@ -566,6 +566,7 @@
+
@@ -1174,6 +1175,7 @@
+
diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters
index cb3f716cc9..3c1493fc11 100644
--- a/Core/Core.vcxproj.filters
+++ b/Core/Core.vcxproj.filters
@@ -1309,6 +1309,9 @@
HW
+
+ HLE\Libraries
+
@@ -2094,6 +2097,9 @@
HW
+
+ HLE\Libraries
+
diff --git a/Core/HLE/AtracCtx.cpp b/Core/HLE/AtracCtx.cpp
new file mode 100644
index 0000000000..b78e68d23a
--- /dev/null
+++ b/Core/HLE/AtracCtx.cpp
@@ -0,0 +1,1083 @@
+// Copyright (c) 2012- PPSSPP Project.
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, version 2.0 or later versions.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License 2.0 for more details.
+
+// A copy of the GPL 2.0 should have been included with the program.
+// If not, see http://www.gnu.org/licenses/
+
+// Official git repository and contact information can be found at
+// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
+
+#include "Common/Serialize/Serializer.h"
+#include "Common/Serialize/SerializeFuncs.h"
+#include "Common/Log.h"
+#include "Core/Reporting.h"
+#include "Core/MemMapHelpers.h"
+#include "Core/System.h"
+#include "Core/HLE/HLE.h"
+#include "Core/HLE/FunctionWrappers.h"
+#include "Core/HLE/sceAtrac.h"
+#include "Core/HLE/AtracCtx.h"
+#include "Core/HW/Atrac3Standalone.h"
+#include "Core/HLE/sceKernelMemory.h"
+
+const size_t overAllocBytes = 16384;
+
+const int RIFF_CHUNK_MAGIC = 0x46464952;
+const int RIFF_WAVE_MAGIC = 0x45564157;
+const int FMT_CHUNK_MAGIC = 0x20746D66;
+const int DATA_CHUNK_MAGIC = 0x61746164;
+const int SMPL_CHUNK_MAGIC = 0x6C706D73;
+const int FACT_CHUNK_MAGIC = 0x74636166;
+
+void Atrac::DoState(PointerWrap &p) {
+ auto s = p.Section("Atrac", 1, 9);
+ if (!s)
+ return;
+
+ Do(p, channels_);
+ Do(p, outputChannels_);
+ if (s >= 5) {
+ Do(p, jointStereo_);
+ }
+
+ Do(p, atracID_);
+ Do(p, first_);
+ Do(p, bufferMaxSize_);
+ Do(p, codecType_);
+
+ Do(p, currentSample_);
+ Do(p, endSample_);
+ Do(p, firstSampleOffset_);
+ if (s >= 3) {
+ Do(p, dataOff_);
+ } else {
+ dataOff_ = firstSampleOffset_;
+ }
+
+ u32 hasDataBuf = dataBuf_ != nullptr;
+ Do(p, hasDataBuf);
+ if (hasDataBuf) {
+ if (p.mode == p.MODE_READ) {
+ if (dataBuf_)
+ delete[] dataBuf_;
+ dataBuf_ = new u8[first_.filesize + overAllocBytes];
+ memset(dataBuf_, 0, first_.filesize + overAllocBytes);
+ }
+ DoArray(p, dataBuf_, first_.filesize);
+ }
+ Do(p, second_);
+
+ Do(p, decodePos_);
+ if (s < 9) {
+ u32 oldDecodeEnd = 0;
+ Do(p, oldDecodeEnd);
+ }
+ if (s >= 4) {
+ Do(p, bufferPos_);
+ } else {
+ bufferPos_ = decodePos_;
+ }
+
+ Do(p, bitrate_);
+ Do(p, bytesPerFrame_);
+
+ Do(p, loopinfo_);
+ if (s < 9) {
+ int oldLoopInfoNum = 42;
+ Do(p, oldLoopInfoNum);
+ }
+
+ Do(p, loopStartSample_);
+ Do(p, loopEndSample_);
+ Do(p, loopNum_);
+
+ Do(p, context_);
+ if (s >= 6) {
+ Do(p, bufferState_);
+ } else {
+ if (dataBuf_ == nullptr) {
+ bufferState_ = ATRAC_STATUS_NO_DATA;
+ } else {
+ UpdateBufferState();
+ }
+ }
+
+ if (s >= 7) {
+ Do(p, ignoreDataBuf_);
+ } else {
+ ignoreDataBuf_ = false;
+ }
+
+ if (s >= 9) {
+ Do(p, bufferValidBytes_);
+ Do(p, bufferHeaderSize_);
+ } else {
+ bufferHeaderSize_ = dataOff_;
+ bufferValidBytes_ = std::min(first_.size - dataOff_, StreamBufferEnd() - dataOff_);
+ if ((bufferState_ & ATRAC_STATUS_STREAMED_MASK) == ATRAC_STATUS_STREAMED_MASK) {
+ bufferPos_ = dataOff_;
+ }
+ }
+
+ if (s < 8 && bufferState_ == ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER) {
+ // We didn't actually allow the second buffer to be set this far back.
+ // Pretend it's a regular loop, we'll just try our best.
+ bufferState_ = ATRAC_STATUS_STREAMED_LOOP_FROM_END;
+ }
+
+ // Make sure to do this late; it depends on things like bytesPerFrame_.
+ if (p.mode == p.MODE_READ && bufferState_ != ATRAC_STATUS_NO_DATA) {
+ CreateDecoder();
+ }
+
+ if (s >= 2 && s < 9) {
+ bool oldResetBuffer = false;
+ Do(p, oldResetBuffer);
+ }
+}
+
+void Atrac::ResetData() {
+ delete decoder_;
+ decoder_ = nullptr;
+
+ if (dataBuf_)
+ delete[] dataBuf_;
+ dataBuf_ = 0;
+ ignoreDataBuf_ = false;
+ bufferState_ = ATRAC_STATUS_NO_DATA;
+
+ if (context_.IsValid())
+ kernelMemory.Free(context_.ptr);
+}
+
+void Atrac::AnalyzeReset() {
+ // Reset some values.
+ codecType_ = 0;
+ currentSample_ = 0;
+ endSample_ = -1;
+ loopNum_ = 0;
+ loopinfo_.clear();
+ loopStartSample_ = -1;
+ loopEndSample_ = -1;
+ decodePos_ = 0;
+ bufferPos_ = 0;
+ channels_ = 2;
+}
+
+void Atrac::UpdateContextFromPSPMem() {
+ if (!context_.IsValid()) {
+ return;
+ }
+
+ // Read in any changes from the game to the context.
+ // TODO: Might be better to just always track in RAM.
+ bufferState_ = context_->info.state;
+ // This value is actually abused by games to store the SAS voice number.
+ loopNum_ = context_->info.loopNum;
+}
+
+void Atrac::WriteContextToPSPMem() {
+ if (!context_.IsValid()) {
+ return;
+ }
+ // context points into PSP memory.
+ SceAtracContext *context = context_;
+ context->info.buffer = first_.addr;
+ context->info.bufferByte = bufferMaxSize_;
+ context->info.secondBuffer = second_.addr;
+ context->info.secondBufferByte = second_.size;
+ context->info.codec = codecType_;
+ context->info.loopNum = loopNum_;
+ context->info.loopStart = loopStartSample_ > 0 ? loopStartSample_ : 0;
+ context->info.loopEnd = loopEndSample_ > 0 ? loopEndSample_ : 0;
+
+ // Note that we read in the state when loading the atrac object, so it's safe
+ // to update it back here all the time. Some games, like Sol Trigger, change it.
+ // TODO: Should we just keep this in PSP ram then, or something?
+ context->info.state = bufferState_;
+ if (firstSampleOffset_ != 0) {
+ context->info.samplesPerChan = firstSampleOffset_ + FirstOffsetExtra();
+ } else {
+ context->info.samplesPerChan = (codecType_ == PSP_MODE_AT_3_PLUS ? ATRAC3PLUS_MAX_SAMPLES : ATRAC3_MAX_SAMPLES);
+ }
+ context->info.sampleSize = bytesPerFrame_;
+ context->info.numChan = channels_;
+ context->info.dataOff = dataOff_;
+ context->info.endSample = endSample_ + firstSampleOffset_ + FirstOffsetExtra();
+ context->info.dataEnd = first_.filesize;
+ context->info.curOff = first_.fileoffset;
+ context->info.decodePos = DecodePosBySample(currentSample_);
+ context->info.streamDataByte = first_.size - dataOff_;
+
+ u8 *buf = (u8 *)context;
+ *(u32_le *)(buf + 0xfc) = atracID_;
+
+ NotifyMemInfo(MemBlockFlags::WRITE, context_.ptr, sizeof(SceAtracContext), "AtracContext");
+}
+
+int Atrac::Analyze(u32 addr, u32 size) {
+ struct RIFFFmtChunk {
+ u16_le fmtTag;
+ u16_le channels;
+ u32_le samplerate;
+ u32_le avgBytesPerSec;
+ u16_le blockAlign;
+ };
+
+ first_.addr = addr;
+ first_.size = size;
+
+ AnalyzeReset();
+
+ // 72 is about the size of the minimum required data to even be valid.
+ if (first_.size < 72) {
+ return hleReportError(ME, ATRAC_ERROR_SIZE_TOO_SMALL, "buffer too small");
+ }
+
+ // TODO: Check the range (addr, size) instead.
+ if (!Memory::IsValidAddress(first_.addr)) {
+ return hleReportWarning(ME, SCE_KERNEL_ERROR_ILLEGAL_ADDRESS, "invalid buffer address");
+ }
+
+ // TODO: Validate stuff.
+ if (Memory::ReadUnchecked_U32(first_.addr) != RIFF_CHUNK_MAGIC) {
+ return hleReportError(ME, ATRAC_ERROR_UNKNOWN_FORMAT, "invalid RIFF header");
+ }
+
+ u32 offset = 8;
+ firstSampleOffset_ = 0;
+
+ while (Memory::Read_U32(first_.addr + offset) != RIFF_WAVE_MAGIC) {
+ // Get the size preceding the magic.
+ int chunk = Memory::Read_U32(first_.addr + offset - 4);
+ // Round the chunk size up to the nearest 2.
+ offset += chunk + (chunk & 1);
+ if (offset + 12 > first_.size) {
+ return hleReportError(ME, ATRAC_ERROR_SIZE_TOO_SMALL, "too small for WAVE chunk at %d", offset);
+ }
+ if (Memory::Read_U32(first_.addr + offset) != RIFF_CHUNK_MAGIC) {
+ return hleReportError(ME, ATRAC_ERROR_UNKNOWN_FORMAT, "RIFF chunk did not contain WAVE");
+ }
+ offset += 8;
+ }
+ offset += 4;
+
+ if (offset != 12) {
+ WARN_LOG_REPORT(ME, "RIFF chunk at offset: %d", offset);
+ }
+
+ // RIFF size excluding chunk header.
+ first_.filesize = Memory::Read_U32(first_.addr + offset - 8) + 8;
+ // Even if the RIFF size is too low, it may simply be incorrect. This works on real firmware.
+ u32 maxSize = std::max(first_.filesize, first_.size);
+
+ bool bfoundData = false;
+ u32 dataChunkSize = 0;
+ int sampleOffsetAdjust = 0;
+ while (maxSize >= offset + 8 && !bfoundData) {
+ int chunkMagic = Memory::Read_U32(first_.addr + offset);
+ u32 chunkSize = Memory::Read_U32(first_.addr + offset + 4);
+ // Account for odd sized chunks.
+ if (chunkSize & 1) {
+ WARN_LOG_REPORT_ONCE(oddchunk, ME, "RIFF chunk had uneven size");
+ }
+ chunkSize += (chunkSize & 1);
+ offset += 8;
+ if (chunkSize > maxSize - offset)
+ break;
+ switch (chunkMagic) {
+ case FMT_CHUNK_MAGIC:
+ {
+ if (codecType_ != 0) {
+ return hleReportError(ME, ATRAC_ERROR_UNKNOWN_FORMAT, "multiple fmt definitions");
+ }
+
+ auto at3fmt = PSPPointer::Create(first_.addr + offset);
+ if (chunkSize < 32 || (at3fmt->fmtTag == AT3_PLUS_MAGIC && chunkSize < 52)) {
+ return hleReportError(ME, ATRAC_ERROR_UNKNOWN_FORMAT, "fmt definition too small (%d)", chunkSize);
+ }
+
+ if (at3fmt->fmtTag == AT3_MAGIC)
+ codecType_ = PSP_MODE_AT_3;
+ else if (at3fmt->fmtTag == AT3_PLUS_MAGIC)
+ codecType_ = PSP_MODE_AT_3_PLUS;
+ else {
+ return hleReportError(ME, ATRAC_ERROR_UNKNOWN_FORMAT, "invalid fmt magic: %04x", at3fmt->fmtTag);
+ }
+ channels_ = at3fmt->channels;
+ if (channels_ != 1 && channels_ != 2) {
+ return hleReportError(ME, ATRAC_ERROR_UNKNOWN_FORMAT, "invalid channel count: %d", channels_);
+ }
+ if (at3fmt->samplerate != 44100) {
+ return hleReportError(ME, ATRAC_ERROR_UNKNOWN_FORMAT, "unsupported sample rate: %d", at3fmt->samplerate);
+ }
+ bitrate_ = at3fmt->avgBytesPerSec * 8;
+ bytesPerFrame_ = at3fmt->blockAlign;
+ if (bytesPerFrame_ == 0) {
+ return hleReportError(ME, ATRAC_ERROR_UNKNOWN_FORMAT, "invalid bytes per frame: %d", bytesPerFrame_);
+ }
+
+ // TODO: There are some format specific bytes here which seem to have fixed values?
+ // Probably don't need them.
+
+ if (at3fmt->fmtTag == AT3_MAGIC) {
+ // This is the offset to the jointStereo_ field.
+ jointStereo_ = Memory::Read_U32(first_.addr + offset + 24);
+ }
+ }
+ break;
+ case FACT_CHUNK_MAGIC:
+ {
+ endSample_ = Memory::Read_U32(first_.addr + offset);
+ if (chunkSize >= 8) {
+ firstSampleOffset_ = Memory::Read_U32(first_.addr + offset + 4);
+ }
+ if (chunkSize >= 12) {
+ u32 largerOffset = Memory::Read_U32(first_.addr + offset + 8);
+ sampleOffsetAdjust = firstSampleOffset_ - largerOffset;
+ }
+ }
+ break;
+ case SMPL_CHUNK_MAGIC:
+ {
+ if (chunkSize < 32) {
+ return hleReportError(ME, ATRAC_ERROR_UNKNOWN_FORMAT, "smpl chunk too small (%d)", chunkSize);
+ }
+ int checkNumLoops = Memory::Read_U32(first_.addr + offset + 28);
+ if (checkNumLoops != 0 && chunkSize < 36 + 20) {
+ return hleReportError(ME, ATRAC_ERROR_UNKNOWN_FORMAT, "smpl chunk too small for loop (%d, %d)", checkNumLoops, chunkSize);
+ }
+ if (checkNumLoops < 0) {
+ return hleReportError(ME, ATRAC_ERROR_UNKNOWN_FORMAT, "bad checkNumLoops (%d)", checkNumLoops);
+ }
+
+ loopinfo_.resize(checkNumLoops);
+ u32 loopinfoAddr = first_.addr + offset + 36;
+ // The PSP only cares about the first loop start and end, it seems.
+ // Most likely can skip the rest of this data, but it's not hurting anyone.
+ for (int i = 0; i < checkNumLoops && 36 + (u32)i < chunkSize; i++, loopinfoAddr += 24) {
+ loopinfo_[i].cuePointID = Memory::Read_U32(loopinfoAddr);
+ loopinfo_[i].type = Memory::Read_U32(loopinfoAddr + 4);
+ loopinfo_[i].startSample = Memory::Read_U32(loopinfoAddr + 8);
+ loopinfo_[i].endSample = Memory::Read_U32(loopinfoAddr + 12);
+ loopinfo_[i].fraction = Memory::Read_U32(loopinfoAddr + 16);
+ loopinfo_[i].playCount = Memory::Read_U32(loopinfoAddr + 20);
+
+ if (loopinfo_[i].startSample >= loopinfo_[i].endSample) {
+ return hleReportError(ME, ATRAC_ERROR_BAD_CODEC_PARAMS, "loop starts after it ends");
+ }
+ }
+ }
+ break;
+ case DATA_CHUNK_MAGIC:
+ {
+ bfoundData = true;
+ dataOff_ = offset;
+ dataChunkSize = chunkSize;
+ if (first_.filesize < offset + chunkSize) {
+ WARN_LOG_REPORT(ME, "Atrac data chunk extends beyond riff chunk");
+ first_.filesize = offset + chunkSize;
+ }
+ }
+ break;
+ }
+ offset += chunkSize;
+ }
+
+ if (codecType_ == 0) {
+ return hleReportError(ME, ATRAC_ERROR_UNKNOWN_FORMAT, "could not detect codec");
+ }
+
+ if (!bfoundData) {
+ return hleReportError(ME, ATRAC_ERROR_SIZE_TOO_SMALL, "no data chunk");
+ }
+
+ // set the loopStartSample_ and loopEndSample_ by loopinfo_
+ if (loopinfo_.size() > 0) {
+ loopStartSample_ = loopinfo_[0].startSample + FirstOffsetExtra() + sampleOffsetAdjust;
+ loopEndSample_ = loopinfo_[0].endSample + FirstOffsetExtra() + sampleOffsetAdjust;
+ } else {
+ loopStartSample_ = -1;
+ loopEndSample_ = -1;
+ }
+
+ // if there is no correct endsample, try to guess it
+ if (endSample_ <= 0 && bytesPerFrame_ != 0) {
+ endSample_ = (dataChunkSize / bytesPerFrame_) * SamplesPerFrame();
+ endSample_ -= firstSampleOffset_ + FirstOffsetExtra();
+ }
+ endSample_ -= 1;
+
+ if (loopEndSample_ != -1 && loopEndSample_ > endSample_ + firstSampleOffset_ + (int)FirstOffsetExtra()) {
+ return hleReportError(ME, ATRAC_ERROR_BAD_CODEC_PARAMS, "loop after end of data");
+ }
+
+ return 0;
+}
+
+int Atrac::AnalyzeAA3(u32 addr, u32 size, u32 filesize) {
+ first_.addr = addr;
+ first_.size = size;
+ first_.filesize = filesize;
+
+ AnalyzeReset();
+
+ if (first_.size < 10) {
+ return hleReportError(ME, ATRAC_ERROR_AA3_SIZE_TOO_SMALL, "buffer too small");
+ }
+
+ // TODO: Make sure this validation is correct, more testing.
+
+ const u8 *buffer = Memory::GetPointer(first_.addr);
+ if (buffer[0] != 'e' || buffer[1] != 'a' || buffer[2] != '3') {
+ return hleReportError(ME, ATRAC_ERROR_AA3_INVALID_DATA, "invalid ea3 magic bytes");
+ }
+
+ // It starts with an id3 header (replaced with ea3.) This is the size.
+ u32 tagSize = buffer[9] | (buffer[8] << 7) | (buffer[7] << 14) | (buffer[6] << 21);
+ if (first_.size < tagSize + 36) {
+ return hleReportError(ME, ATRAC_ERROR_AA3_SIZE_TOO_SMALL, "truncated before id3 end");
+ }
+
+ // EA3 header starts at id3 header (10) + tagSize.
+ buffer = Memory::GetPointer(first_.addr + 10 + tagSize);
+ if (buffer[0] != 'E' || buffer[1] != 'A' || buffer[2] != '3') {
+ return hleReportError(ME, ATRAC_ERROR_AA3_INVALID_DATA, "invalid EA3 magic bytes");
+ }
+
+ // Based on FFmpeg's code.
+ u32 codecParams = buffer[35] | (buffer[34] << 8) | (buffer[35] << 16);
+ const u32 at3SampleRates[8] = { 32000, 44100, 48000, 88200, 96000, 0 };
+
+ switch (buffer[32]) {
+ case 0:
+ codecType_ = PSP_MODE_AT_3;
+ bytesPerFrame_ = (codecParams & 0x03FF) * 8;
+ bitrate_ = at3SampleRates[(codecParams >> 13) & 7] * bytesPerFrame_ * 8 / 1024;
+ channels_ = 2;
+ jointStereo_ = (codecParams >> 17) & 1;
+ break;
+ case 1:
+ codecType_ = PSP_MODE_AT_3_PLUS;
+ bytesPerFrame_ = ((codecParams & 0x03FF) * 8) + 8;
+ bitrate_ = at3SampleRates[(codecParams >> 13) & 7] * bytesPerFrame_ * 8 / 2048;
+ channels_ = (codecParams >> 10) & 7;
+ break;
+ case 3:
+ case 4:
+ case 5:
+ return hleReportError(ME, ATRAC_ERROR_AA3_INVALID_DATA, "unsupported codec type %d", buffer[32]);
+ default:
+ return hleReportError(ME, ATRAC_ERROR_AA3_INVALID_DATA, "invalid codec type %d", buffer[32]);
+ }
+
+ dataOff_ = 10 + tagSize + 96;
+ firstSampleOffset_ = 0;
+ if (endSample_ < 0 && bytesPerFrame_ != 0) {
+ endSample_ = ((first_.filesize - dataOff_) / bytesPerFrame_) * SamplesPerFrame();
+ }
+ endSample_ -= 1;
+
+ return 0;
+}
+
+void Atrac::CalculateStreamInfo(u32 *outReadOffset) {
+ u32 readOffset = first_.fileoffset;
+ if (bufferState_ == ATRAC_STATUS_ALL_DATA_LOADED) {
+ // Nothing to write.
+ readOffset = 0;
+ first_.offset = 0;
+ first_.writableBytes = 0;
+ } else if (bufferState_ == ATRAC_STATUS_HALFWAY_BUFFER) {
+ // If we're buffering the entire file, just give the same as readOffset.
+ first_.offset = readOffset;
+ // In this case, the bytes writable are just the remaining bytes, always.
+ first_.writableBytes = first_.filesize - readOffset;
+ } else {
+ u32 bufferEnd = StreamBufferEnd();
+ u32 bufferValidExtended = bufferPos_ + bufferValidBytes_;
+ if (bufferValidExtended < bufferEnd) {
+ first_.offset = bufferValidExtended;
+ first_.writableBytes = bufferEnd - bufferValidExtended;
+ } else {
+ u32 bufferStartUsed = bufferValidExtended - bufferEnd;
+ first_.offset = bufferStartUsed;
+ first_.writableBytes = bufferPos_ - bufferStartUsed;
+ }
+
+ if (readOffset >= first_.filesize) {
+ if (bufferState_ == ATRAC_STATUS_STREAMED_WITHOUT_LOOP) {
+ // We don't need anything more, so all 0s.
+ readOffset = 0;
+ first_.offset = 0;
+ first_.writableBytes = 0;
+ } else {
+ readOffset = FileOffsetBySample(loopStartSample_ - FirstOffsetExtra() - firstSampleOffset_ - SamplesPerFrame() * 2);
+ }
+ }
+
+ if (readOffset + first_.writableBytes > first_.filesize) {
+ // Never ask for past the end of file, even when the space is free.
+ first_.writableBytes = first_.filesize - readOffset;
+ }
+
+ // If you don't think this should be here, remove it. It's just a temporary safety check.
+ if (first_.offset + first_.writableBytes > bufferMaxSize_) {
+ ERROR_LOG_REPORT(ME, "Somehow calculated too many writable bytes: %d + %d > %d", first_.offset, first_.writableBytes, bufferMaxSize_);
+ first_.offset = 0;
+ first_.writableBytes = bufferMaxSize_;
+ }
+ }
+
+ if (outReadOffset) {
+ *outReadOffset = readOffset;
+ }
+}
+
+void Atrac::CreateDecoder() {
+ if (decoder_) {
+ delete decoder_;
+ }
+
+ // First, init the standalone decoder. Only used for low-level-decode initially, but simple.
+ if (codecType_ == PSP_MODE_AT_3) {
+ // We don't pull this from the RIFF so that we can support OMA also.
+ uint8_t extraData[14]{};
+ // The only thing that changes are the jointStereo_ values.
+ extraData[0] = 1;
+ extraData[3] = channels_ << 3;
+ extraData[6] = jointStereo_;
+ extraData[8] = jointStereo_;
+ extraData[10] = 1;
+ decoder_ = CreateAtrac3Audio(channels_, bytesPerFrame_, extraData, sizeof(extraData));
+ } else {
+ decoder_ = CreateAtrac3PlusAudio(channels_, bytesPerFrame_);
+ }
+ // reinit decodePos, because ffmpeg had changed it.
+ decodePos_ = 0;
+}
+
+void Atrac::GetResetBufferInfo(AtracResetBufferInfo *bufferInfo, int sample) {
+ if (bufferState_ == ATRAC_STATUS_ALL_DATA_LOADED) {
+ bufferInfo->first.writePosPtr = first_.addr;
+ // Everything is loaded, so nothing needs to be read.
+ bufferInfo->first.writableBytes = 0;
+ bufferInfo->first.minWriteBytes = 0;
+ bufferInfo->first.filePos = 0;
+ } else if (bufferState_ == ATRAC_STATUS_HALFWAY_BUFFER) {
+ // Here the message is: you need to read at least this many bytes to get to that position.
+ // This is because we're filling the buffer start to finish, not streaming.
+ bufferInfo->first.writePosPtr = first_.addr + first_.size;
+ bufferInfo->first.writableBytes = first_.filesize - first_.size;
+ int minWriteBytes = FileOffsetBySample(sample) - first_.size;
+ if (minWriteBytes > 0) {
+ bufferInfo->first.minWriteBytes = minWriteBytes;
+ } else {
+ bufferInfo->first.minWriteBytes = 0;
+ }
+ bufferInfo->first.filePos = first_.size;
+ } else {
+ // This is without the sample offset. The file offset also includes the previous batch of samples?
+ int sampleFileOffset = FileOffsetBySample(sample - firstSampleOffset_ - SamplesPerFrame());
+
+ // Update the writable bytes. When streaming, this is just the number of bytes until the end.
+ const u32 bufSizeAligned = (bufferMaxSize_ / bytesPerFrame_) * bytesPerFrame_;
+ const int needsMoreFrames = FirstOffsetExtra();
+
+ bufferInfo->first.writePosPtr = first_.addr;
+ bufferInfo->first.writableBytes = std::min(first_.filesize - sampleFileOffset, bufSizeAligned);
+ if (((sample + firstSampleOffset_) % (int)SamplesPerFrame()) >= (int)SamplesPerFrame() - needsMoreFrames) {
+ // Not clear why, but it seems it wants a bit extra in case the sample is late?
+ bufferInfo->first.minWriteBytes = bytesPerFrame_ * 3;
+ } else {
+ bufferInfo->first.minWriteBytes = bytesPerFrame_ * 2;
+ }
+ if ((u32)sample < (u32)firstSampleOffset_ && sampleFileOffset != dataOff_) {
+ sampleFileOffset -= bytesPerFrame_;
+ }
+ bufferInfo->first.filePos = sampleFileOffset;
+
+ if (second_.size != 0) {
+ // TODO: We have a second buffer. Within it, minWriteBytes should be zero.
+ // The filePos should be after the end of the second buffer (or zero.)
+ // We actually need to ensure we READ from the second buffer before implementing that.
+ }
+ }
+
+ // It seems like this is always the same as the first buffer's pos, weirdly.
+ bufferInfo->second.writePosPtr = first_.addr;
+ // Reset never needs a second buffer write, since the loop is in a fixed place.
+ bufferInfo->second.writableBytes = 0;
+ bufferInfo->second.minWriteBytes = 0;
+ bufferInfo->second.filePos = 0;
+}
+
+int Atrac::SetData(u32 buffer, u32 readSize, u32 bufferSize, int successCode) {
+ first_.addr = buffer;
+ first_.size = readSize;
+
+ if (first_.size > first_.filesize)
+ first_.size = first_.filesize;
+ first_.fileoffset = first_.size;
+
+ // got the size of temp buf, and calculate offset
+ bufferMaxSize_ = bufferSize;
+ first_.offset = first_.size;
+
+ // some games may reuse an atracID for playing sound
+ ResetData();
+ UpdateBufferState();
+
+ if (codecType_ != PSP_MODE_AT_3 && codecType_ != PSP_MODE_AT_3_PLUS) {
+ // Shouldn't have gotten here, Analyze() checks this.
+ bufferState_ = ATRAC_STATUS_NO_DATA;
+ return hleReportError(ME, ATRAC_ERROR_UNKNOWN_FORMAT, "unexpected codec type in set data");
+ }
+
+ if (bufferState_ == ATRAC_STATUS_ALL_DATA_LOADED || bufferState_ == ATRAC_STATUS_HALFWAY_BUFFER) {
+ // This says, don't use the dataBuf_ array, use the PSP RAM.
+ // This way, games can load data async into the buffer, and it still works.
+ // TODO: Support this always, even for streaming.
+ ignoreDataBuf_ = true;
+ }
+ if (bufferState_ == ATRAC_STATUS_STREAMED_WITHOUT_LOOP || bufferState_ == ATRAC_STATUS_STREAMED_LOOP_FROM_END || bufferState_ == ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER) {
+ bufferHeaderSize_ = dataOff_;
+ bufferPos_ = dataOff_ + bytesPerFrame_;
+ bufferValidBytes_ = first_.size - bufferPos_;
+ }
+
+ const char *codecName = codecType_ == PSP_MODE_AT_3 ? "atrac3" : "atrac3+";
+ const char *channelName = channels_ == 1 ? "mono" : "stereo";
+
+ // Over-allocate databuf to prevent going off the end if the bitstream is bad or if there are
+ // bugs in the decoder. This happens, see issue #15788. Arbitrary, but let's make it a whole page on the popular
+ // architecture that has the largest pages (M1).
+ dataBuf_ = new u8[first_.filesize + overAllocBytes];
+ memset(dataBuf_, 0, first_.filesize + overAllocBytes);
+ if (!ignoreDataBuf_) {
+ u32 copybytes = std::min(bufferSize, first_.filesize);
+ Memory::Memcpy(dataBuf_, buffer, copybytes, "AtracSetData");
+ }
+ CreateDecoder();
+ return hleLogSuccessInfoI(ME, successCode, "%s %s audio", codecName, channelName);
+}
+
+u32 Atrac::SetSecondBuffer(u32 secondBuffer, u32 secondBufferSize) {
+ u32 secondFileOffset = FileOffsetBySample(loopEndSample_ - firstSampleOffset_);
+ u32 desiredSize = first_.filesize - secondFileOffset;
+
+ // 3 seems to be the number of frames required to handle a loop.
+ if (secondBufferSize < desiredSize && secondBufferSize < (u32)BytesPerFrame() * 3) {
+ return hleReportError(ME, ATRAC_ERROR_SIZE_TOO_SMALL, "too small");
+ }
+ if (BufferState() != ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER) {
+ return hleReportError(ME, ATRAC_ERROR_SECOND_BUFFER_NOT_NEEDED, "not needed");
+ }
+
+ second_.addr = secondBuffer;
+ second_.size = secondBufferSize;
+ second_.fileoffset = secondFileOffset;
+ return hleLogSuccessI(ME, 0);
+}
+
+void Atrac::UpdateBitrate() {
+ bitrate_ = (bytesPerFrame_ * 352800) / 1000;
+ if (codecType_ == PSP_MODE_AT_3_PLUS)
+ bitrate_ = ((bitrate_ >> 11) + 8) & 0xFFFFFFF0;
+ else
+ bitrate_ = (bitrate_ + 511) >> 10;
+}
+
+int Atrac::AddStreamData(u32 bytesToAdd) {
+ u32 readOffset;
+ CalculateStreamInfo(&readOffset);
+ if (bytesToAdd > first_.writableBytes)
+ return hleLogWarning(ME, ATRAC_ERROR_ADD_DATA_IS_TOO_BIG, "too many bytes");
+
+ if (bytesToAdd > 0) {
+ first_.fileoffset = readOffset;
+ int addbytes = std::min(bytesToAdd, first_.filesize - first_.fileoffset);
+ if (!ignoreDataBuf_) {
+ Memory::Memcpy(dataBuf_ + first_.fileoffset, first_.addr + first_.offset, addbytes, "AtracAddStreamData");
+ }
+ first_.fileoffset += addbytes;
+ }
+ first_.size += bytesToAdd;
+ if (first_.size >= first_.filesize) {
+ first_.size = first_.filesize;
+ if (bufferState_ == ATRAC_STATUS_HALFWAY_BUFFER)
+ bufferState_ = ATRAC_STATUS_ALL_DATA_LOADED;
+ WriteContextToPSPMem();
+ }
+
+ first_.offset += bytesToAdd;
+ bufferValidBytes_ += bytesToAdd;
+
+ if (PSP_CoreParameter().compat.flags().AtracLoopHack && bufferState_ == ATRAC_STATUS_STREAMED_LOOP_FROM_END && RemainingFrames() > 2) {
+ loopNum_++;
+ SeekToSample(loopStartSample_ - FirstOffsetExtra() - firstSampleOffset_);
+ }
+
+ return 0;
+}
+
+u32 Atrac::AddStreamDataSas(u32 bufPtr, u32 bytesToAdd) {
+ int addbytes = std::min(bytesToAdd, first_.filesize - first_.fileoffset - FirstOffsetExtra());
+ Memory::Memcpy(dataBuf_ + first_.fileoffset + FirstOffsetExtra(), bufPtr, addbytes, "AtracAddStreamData");
+ first_.size += bytesToAdd;
+ if (first_.size >= first_.filesize) {
+ first_.size = first_.filesize;
+ if (bufferState_ == ATRAC_STATUS_HALFWAY_BUFFER)
+ bufferState_ = ATRAC_STATUS_ALL_DATA_LOADED;
+ }
+ first_.fileoffset += addbytes;
+ // refresh context_
+ WriteContextToPSPMem();
+ return 0;
+}
+
+u32 Atrac::GetNextSamples() {
+ // It seems like the PSP aligns the sample position to 0x800...?
+ u32 skipSamples = firstSampleOffset_ + FirstOffsetExtra();
+ u32 firstSamples = (SamplesPerFrame() - skipSamples) % SamplesPerFrame();
+ u32 numSamples = endSample_ + 1 - currentSample_;
+ if (currentSample_ == 0 && firstSamples != 0) {
+ numSamples = firstSamples;
+ }
+ u32 unalignedSamples = (skipSamples + currentSample_) % SamplesPerFrame();
+ if (unalignedSamples != 0) {
+ // We're off alignment, possibly due to a loop. Force it back on.
+ numSamples = SamplesPerFrame() - unalignedSamples;
+ }
+ if (numSamples > SamplesPerFrame())
+ numSamples = SamplesPerFrame();
+ if (bufferState_ == ATRAC_STATUS_STREAMED_LOOP_FROM_END && (int)numSamples + currentSample_ > endSample_) {
+ bufferState_ = ATRAC_STATUS_ALL_DATA_LOADED;
+ }
+ return numSamples;
+}
+
+u8 *Atrac::BufferStart() {
+ return ignoreDataBuf_ ? Memory::GetPointerWrite(first_.addr) : dataBuf_;
+}
+
+void Atrac::ForceSeekToSample(int sample) {
+ if (decoder_) {
+ decoder_->FlushBuffers();
+ }
+ currentSample_ = sample;
+}
+
+void Atrac::SeekToSample(int sample) {
+ // It seems like the PSP aligns the sample position to 0x800...?
+ const u32 offsetSamples = firstSampleOffset_ + FirstOffsetExtra();
+ const u32 unalignedSamples = (offsetSamples + sample) % SamplesPerFrame();
+ int seekFrame = sample + offsetSamples - unalignedSamples;
+
+ if ((sample != currentSample_ || sample == 0) && decoder_ != nullptr) {
+ // Prefill the decode buffer with packets before the first sample offset.
+ decoder_->FlushBuffers();
+
+ int adjust = 0;
+ if (sample == 0) {
+ int offsetSamples = firstSampleOffset_ + FirstOffsetExtra();
+ adjust = -(int)(offsetSamples % SamplesPerFrame());
+ }
+ const u32 off = FileOffsetBySample(sample + adjust);
+ const u32 backfill = bytesPerFrame_ * 2;
+ const u32 start = off - dataOff_ < backfill ? dataOff_ : off - backfill;
+
+ for (u32 pos = start; pos < off; pos += bytesPerFrame_) {
+ decoder_->Decode(BufferStart() + pos, bytesPerFrame_, nullptr, nullptr, nullptr);
+ }
+ }
+
+ currentSample_ = sample;
+}
+
+int Atrac::RemainingFrames() const {
+ if (bufferState_ == ATRAC_STATUS_ALL_DATA_LOADED) {
+ // Meaning, infinite I guess? We've got it all.
+ return PSP_ATRAC_ALLDATA_IS_ON_MEMORY;
+ }
+
+ u32 currentFileOffset = FileOffsetBySample(currentSample_ - SamplesPerFrame() + FirstOffsetExtra());
+ if (first_.fileoffset >= first_.filesize) {
+ if (bufferState_ == ATRAC_STATUS_STREAMED_WITHOUT_LOOP) {
+ return PSP_ATRAC_NONLOOP_STREAM_DATA_IS_ON_MEMORY;
+ }
+ int loopEndAdjusted = loopEndSample_ - FirstOffsetExtra() - 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;
+ }
+ if ((bufferState_ & ATRAC_STATUS_STREAMED_MASK) == ATRAC_STATUS_STREAMED_MASK && loopNum_ == 0) {
+ return PSP_ATRAC_LOOP_STREAM_DATA_IS_ON_MEMORY;
+ }
+ }
+
+ if ((bufferState_ & ATRAC_STATUS_STREAMED_MASK) == ATRAC_STATUS_STREAMED_MASK) {
+ // Since we're streaming, the remaining frames are what's valid in the buffer.
+ return bufferValidBytes_ / bytesPerFrame_;
+ }
+
+ // Since the first frame is shorter by this offset, add to round up at this offset.
+ const int remainingBytes = first_.fileoffset - currentFileOffset;
+ if (remainingBytes < 0) {
+ // Just in case. Shouldn't happen, but once did by mistake.
+ return 0;
+ }
+ return remainingBytes / bytesPerFrame_;
+}
+
+void Atrac::ConsumeFrame() {
+ bufferPos_ += bytesPerFrame_;
+ if ((bufferState_ & ATRAC_STATUS_STREAMED_MASK) == ATRAC_STATUS_STREAMED_MASK) {
+ if (bufferValidBytes_ > bytesPerFrame_) {
+ bufferValidBytes_ -= bytesPerFrame_;
+ } else {
+ bufferValidBytes_ = 0;
+ }
+ }
+ if (bufferPos_ >= StreamBufferEnd()) {
+ // Wrap around... theoretically, this should only happen at exactly StreamBufferEnd.
+ bufferPos_ -= StreamBufferEnd();
+ bufferHeaderSize_ = 0;
+ }
+}
+
+u32 Atrac::DecodeData(u8 *outbuf, u32 outbufPtr, u32 *SamplesNum, u32 *finish, int *remains) {
+ int loopNum = loopNum_;
+ if (bufferState_ == ATRAC_STATUS_FOR_SCESAS) {
+ // TODO: Might need more testing.
+ loopNum = 0;
+ }
+
+ // We already passed the end - return an error (many games check for this.)
+ if (currentSample_ >= endSample_ && loopNum == 0) {
+ *SamplesNum = 0;
+ *finish = 1;
+ // refresh context_
+ WriteContextToPSPMem();
+ return ATRAC_ERROR_ALL_DATA_DECODED;
+ }
+
+ // TODO: This isn't at all right, but at least it makes the music "last" some time.
+ u32 numSamples = 0;
+
+ // It seems like the PSP aligns the sample position to 0x800...?
+ int offsetSamples = firstSampleOffset_ + FirstOffsetExtra();
+ int skipSamples = 0;
+ u32 maxSamples = endSample_ + 1 - currentSample_;
+ u32 unalignedSamples = (offsetSamples + currentSample_) % SamplesPerFrame();
+ if (unalignedSamples != 0) {
+ // We're off alignment, possibly due to a loop. Force it back on.
+ maxSamples = SamplesPerFrame() - unalignedSamples;
+ skipSamples = unalignedSamples;
+ }
+
+ 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.
+ ConsumeFrame();
+ }
+
+ // TODO: We don't support any other codec type, check seems unnecessary?
+ if ((codecType_ == PSP_MODE_AT_3 || codecType_ == PSP_MODE_AT_3_PLUS)) {
+ SeekToSample(currentSample_);
+
+ bool gotFrame = false;
+ u32 off = FileOffsetBySample(currentSample_ - skipSamples);
+ if (off < first_.size) {
+ uint8_t *indata = BufferStart() + off;
+ int bytesConsumed = 0;
+ int outBytes = 0;
+ if (!decoder_->Decode(indata, bytesPerFrame_, &bytesConsumed,
+ outbuf, &outBytes)) {
+ // Decode failed.
+ *SamplesNum = 0;
+ *finish = 1;
+ return ATRAC_ERROR_ALL_DATA_DECODED;
+ }
+ gotFrame = true;
+
+ numSamples = outBytes / 4;
+ uint32_t packetAddr = CurBufferAddress(-skipSamples);
+ // got a frame
+ int skipped = std::min((u32)skipSamples, numSamples);
+ skipSamples -= skipped;
+ numSamples = numSamples - skipped;
+ // If we're at the end, clamp to samples we want. It always returns a full chunk.
+ numSamples = std::min(maxSamples, numSamples);
+
+ if (packetAddr != 0 && MemBlockInfoDetailed()) {
+ char tagData[128];
+ size_t tagSize = FormatMemWriteTagAt(tagData, sizeof(tagData), "AtracDecode/", packetAddr, bytesPerFrame_);
+ NotifyMemInfo(MemBlockFlags::READ, packetAddr, bytesPerFrame_, tagData, tagSize);
+ NotifyMemInfo(MemBlockFlags::WRITE, outbufPtr, outBytes, tagData, tagSize);
+ } else {
+ NotifyMemInfo(MemBlockFlags::WRITE, outbufPtr, outBytes, "AtracDecode");
+ }
+ // We only want one frame per call, let's continue the next time.
+ }
+
+ if (!gotFrame && currentSample_ < endSample_) {
+ // Never got a frame. We may have dropped a GHA frame or otherwise have a bug.
+ // For now, let's try to provide an extra "frame" if possible so games don't infinite loop.
+ if (FileOffsetBySample(currentSample_) < first_.filesize) {
+ numSamples = std::min(maxSamples, SamplesPerFrame());
+ u32 outBytes = numSamples * outputChannels_ * sizeof(s16);
+ if (outbuf != nullptr) {
+ memset(outbuf, 0, outBytes);
+ NotifyMemInfo(MemBlockFlags::WRITE, outbufPtr, outBytes, "AtracDecode");
+ }
+ }
+ }
+ }
+
+ *SamplesNum = numSamples;
+ // update current sample and decodePos
+ currentSample_ += numSamples;
+ decodePos_ = DecodePosBySample(currentSample_);
+
+ ConsumeFrame();
+
+ int finishFlag = 0;
+ // TODO: Verify.
+ bool hitEnd = currentSample_ >= endSample_ || (numSamples == 0 && first_.size >= first_.filesize);
+ int loopEndAdjusted = loopEndSample_ - FirstOffsetExtra() - firstSampleOffset_;
+ if ((hitEnd || currentSample_ > loopEndAdjusted) && loopNum != 0) {
+ SeekToSample(loopStartSample_ - FirstOffsetExtra() - firstSampleOffset_);
+ 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 = FileOffsetBySample(loopStartSample_ - FirstOffsetExtra() - firstSampleOffset_ - 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 = FileOffsetBySample(loopStartSample_ - FirstOffsetExtra() - firstSampleOffset_ - SamplesPerFrame() * 2);
+ }
+ }
+ } else if (hitEnd) {
+ finishFlag = 1;
+
+ // Still move forward, so we know that we've read everything.
+ // This seems to be reflected in the context as well.
+ currentSample_ += SamplesPerFrame() - numSamples;
+ }
+
+ *finish = finishFlag;
+ *remains = RemainingFrames();
+ // refresh context_
+ WriteContextToPSPMem();
+ return 0;
+}
+
+void Atrac::SetLoopNum(int loopNum) {
+ // Spammed in MHU
+ loopNum_ = loopNum;
+ if (loopNum != 0 && loopinfo_.size() == 0) {
+ // Just loop the whole audio
+ loopStartSample_ = firstSampleOffset_ + FirstOffsetExtra();
+ loopEndSample_ = endSample_ + firstSampleOffset_ + FirstOffsetExtra();
+ }
+ WriteContextToPSPMem();
+}
+
+u32 Atrac::ResetPlayPosition(int sample, int bytesWrittenFirstBuf, int bytesWrittenSecondBuf) {
+ // Reuse the same calculation as before.
+ AtracResetBufferInfo bufferInfo;
+ GetResetBufferInfo(&bufferInfo, sample);
+
+ if ((u32)bytesWrittenFirstBuf < bufferInfo.first.minWriteBytes || (u32)bytesWrittenFirstBuf > bufferInfo.first.writableBytes) {
+ return hleLogError(ME, ATRAC_ERROR_BAD_FIRST_RESET_SIZE, "first byte count not in valid range");
+ }
+ if ((u32)bytesWrittenSecondBuf < bufferInfo.second.minWriteBytes || (u32)bytesWrittenSecondBuf > bufferInfo.second.writableBytes) {
+ return hleLogError(ME, ATRAC_ERROR_BAD_SECOND_RESET_SIZE, "second byte count not in valid range");
+ }
+
+ if (bufferState_ == ATRAC_STATUS_ALL_DATA_LOADED) {
+ // Always adds zero bytes.
+ } else if (bufferState_ == ATRAC_STATUS_HALFWAY_BUFFER) {
+ // Okay, it's a valid number of bytes. Let's set them up.
+ if (bytesWrittenFirstBuf != 0) {
+ if (!ignoreDataBuf_) {
+ Memory::Memcpy(dataBuf_ + first_.size, first_.addr + first_.size, bytesWrittenFirstBuf, "AtracResetPlayPosition");
+ }
+ first_.fileoffset += bytesWrittenFirstBuf;
+ first_.size += bytesWrittenFirstBuf;
+ first_.offset += bytesWrittenFirstBuf;
+ }
+
+ // Did we transition to a full buffer?
+ if (first_.size >= first_.filesize) {
+ first_.size = first_.filesize;
+ bufferState_ = ATRAC_STATUS_ALL_DATA_LOADED;
+ }
+ } else {
+ if (bufferInfo.first.filePos > first_.filesize) {
+ return hleDelayResult(hleLogError(ME, ATRAC_ERROR_API_FAIL, "invalid file position"), "reset play pos", 200);
+ }
+
+ // Move the offset to the specified position.
+ first_.fileoffset = bufferInfo.first.filePos;
+
+ if (bytesWrittenFirstBuf != 0) {
+ if (!ignoreDataBuf_) {
+ Memory::Memcpy(dataBuf_ + first_.fileoffset, first_.addr, bytesWrittenFirstBuf, "AtracResetPlayPosition");
+ }
+ first_.fileoffset += bytesWrittenFirstBuf;
+ }
+ first_.size = first_.fileoffset;
+ first_.offset = bytesWrittenFirstBuf;
+
+ bufferHeaderSize_ = 0;
+ bufferPos_ = bytesPerFrame_;
+ bufferValidBytes_ = bytesWrittenFirstBuf - bufferPos_;
+ }
+
+ if (codecType_ == PSP_MODE_AT_3 || codecType_ == PSP_MODE_AT_3_PLUS) {
+ SeekToSample(sample);
+ }
+
+ WriteContextToPSPMem();
+ return 0;
+}
+
+void Atrac::InitLowLevel(u32 paramsAddr, bool jointStereo) {
+ channels_ = Memory::Read_U32(paramsAddr);
+ outputChannels_ = Memory::Read_U32(paramsAddr + 4);
+ bufferMaxSize_ = Memory::Read_U32(paramsAddr + 8);
+ bytesPerFrame_ = bufferMaxSize_;
+ first_.writableBytes = bytesPerFrame_;
+ ResetData();
+
+ if (codecType_ == PSP_MODE_AT_3) {
+ bitrate_ = (bytesPerFrame_ * 352800) / 1000;
+ bitrate_ = (bitrate_ + 511) >> 10;
+ jointStereo_ = false;
+ } else if (codecType_ == PSP_MODE_AT_3_PLUS) {
+ bitrate_ = (bytesPerFrame_ * 352800) / 1000;
+ bitrate_ = ((bitrate_ >> 11) + 8) & 0xFFFFFFF0;
+ jointStereo_ = false;
+ }
+
+ dataOff_ = 0;
+ first_.size = 0;
+ first_.filesize = bytesPerFrame_;
+ bufferState_ = ATRAC_STATUS_LOW_LEVEL;
+ currentSample_ = 0;
+ CreateDecoder();
+ WriteContextToPSPMem();
+}
diff --git a/Core/HLE/AtracCtx.h b/Core/HLE/AtracCtx.h
new file mode 100644
index 0000000000..8e0123bee4
--- /dev/null
+++ b/Core/HLE/AtracCtx.h
@@ -0,0 +1,267 @@
+// Copyright (c) 2012- PPSSPP Project.
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, version 2.0 or later versions.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License 2.0 for more details.
+
+// A copy of the GPL 2.0 should have been included with the program.
+// If not, see http://www.gnu.org/licenses/
+
+// Official git repository and contact information can be found at
+// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
+
+#pragma once
+
+#include "Common/CommonTypes.h"
+#include "Common/Swap.h"
+
+#include "Core/HLE/sceAtrac.h"
+
+struct AtracSingleResetBufferInfo {
+ u32_le writePosPtr;
+ u32_le writableBytes;
+ u32_le minWriteBytes;
+ u32_le filePos;
+};
+
+struct AtracResetBufferInfo {
+ AtracSingleResetBufferInfo first;
+ AtracSingleResetBufferInfo second;
+};
+
+#define AT3_MAGIC 0x0270
+#define AT3_PLUS_MAGIC 0xFFFE
+#define PSP_MODE_AT_3_PLUS 0x00001000
+#define PSP_MODE_AT_3 0x00001001
+
+#define ATRAC_ERROR_API_FAIL 0x80630002
+#define ATRAC_ERROR_NO_ATRACID 0x80630003
+#define ATRAC_ERROR_INVALID_CODECTYPE 0x80630004
+#define ATRAC_ERROR_BAD_ATRACID 0x80630005
+#define ATRAC_ERROR_UNKNOWN_FORMAT 0x80630006
+#define ATRAC_ERROR_WRONG_CODECTYPE 0x80630007
+#define ATRAC_ERROR_BAD_CODEC_PARAMS 0x80630008
+#define ATRAC_ERROR_ALL_DATA_LOADED 0x80630009
+#define ATRAC_ERROR_NO_DATA 0x80630010
+#define ATRAC_ERROR_SIZE_TOO_SMALL 0x80630011
+#define ATRAC_ERROR_SECOND_BUFFER_NEEDED 0x80630012
+#define ATRAC_ERROR_INCORRECT_READ_SIZE 0x80630013
+#define ATRAC_ERROR_BAD_SAMPLE 0x80630015
+#define ATRAC_ERROR_BAD_FIRST_RESET_SIZE 0x80630016
+#define ATRAC_ERROR_BAD_SECOND_RESET_SIZE 0x80630017
+#define ATRAC_ERROR_ADD_DATA_IS_TOO_BIG 0x80630018
+#define ATRAC_ERROR_NOT_MONO 0x80630019
+#define ATRAC_ERROR_NO_LOOP_INFORMATION 0x80630021
+#define ATRAC_ERROR_SECOND_BUFFER_NOT_NEEDED 0x80630022
+#define ATRAC_ERROR_BUFFER_IS_EMPTY 0x80630023
+#define ATRAC_ERROR_ALL_DATA_DECODED 0x80630024
+#define ATRAC_ERROR_IS_LOW_LEVEL 0x80630031
+#define ATRAC_ERROR_IS_FOR_SCESAS 0x80630040
+#define ATRAC_ERROR_AA3_INVALID_DATA 0x80631003
+#define ATRAC_ERROR_AA3_SIZE_TOO_SMALL 0x80631004
+
+const u32 ATRAC3_MAX_SAMPLES = 0x400;
+const u32 ATRAC3PLUS_MAX_SAMPLES = 0x800;
+
+const int PSP_ATRAC_ALLDATA_IS_ON_MEMORY = -1;
+const int PSP_ATRAC_NONLOOP_STREAM_DATA_IS_ON_MEMORY = -2;
+const int PSP_ATRAC_LOOP_STREAM_DATA_IS_ON_MEMORY = -3;
+
+struct InputBuffer {
+ // Address of the buffer.
+ u32 addr;
+ // Size of data read so far into dataBuf_ (to be removed.)
+ u32 size;
+ // Offset into addr at which new data is added.
+ u32 offset;
+ // Last writableBytes number (to be removed.)
+ u32 writableBytes;
+ // Unused, always 0.
+ u32 neededBytes;
+ // Total size of the entire file data.
+ u32 filesize;
+ // Offset into the file at which new data is read.
+ u32 fileoffset;
+};
+
+struct AtracLoopInfo {
+ int cuePointID;
+ int type;
+ int startSample;
+ int endSample;
+ int fraction;
+ int playCount;
+};
+
+class AudioDecoder;
+
+struct Atrac {
+ ~Atrac() {
+ ResetData();
+ }
+
+ void ResetData();
+ void UpdateBufferState() {
+ if (bufferMaxSize_ >= first_.filesize) {
+ if (first_.size < first_.filesize) {
+ // The buffer is big enough, but we don't have all the data yet.
+ bufferState_ = ATRAC_STATUS_HALFWAY_BUFFER;
+ } else {
+ bufferState_ = ATRAC_STATUS_ALL_DATA_LOADED;
+ }
+ } else {
+ if (loopEndSample_ <= 0) {
+ // There's no looping, but we need to stream the data in our buffer.
+ bufferState_ = ATRAC_STATUS_STREAMED_WITHOUT_LOOP;
+ } else if (loopEndSample_ == endSample_ + firstSampleOffset_ + (int)FirstOffsetExtra()) {
+ bufferState_ = ATRAC_STATUS_STREAMED_LOOP_FROM_END;
+ } else {
+ bufferState_ = ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER;
+ }
+ }
+ }
+
+ void DoState(PointerWrap &p);
+
+ // Input frame size
+ int BytesPerFrame() const {
+ return bytesPerFrame_;
+ }
+ // Output frame size
+ u32 SamplesPerFrame() const {
+ return codecType_ == PSP_MODE_AT_3_PLUS ? ATRAC3PLUS_MAX_SAMPLES : ATRAC3_MAX_SAMPLES;
+ }
+
+ u32 FirstOffsetExtra() const {
+ return codecType_ == PSP_MODE_AT_3_PLUS ? 368 : 69;
+ }
+
+ u32 DecodePosBySample(int sample) const {
+ return (u32)(firstSampleOffset_ + sample / (int)SamplesPerFrame() * bytesPerFrame_);
+ }
+
+ u32 FileOffsetBySample(int sample) const {
+ int offsetSample = sample + firstSampleOffset_;
+ int frameOffset = offsetSample / (int)SamplesPerFrame();
+ return (u32)(dataOff_ + bytesPerFrame_ + frameOffset * bytesPerFrame_);
+ }
+
+ void UpdateBitrate();
+
+ int Bitrate() const {
+ return bitrate_;
+ }
+ int Channels() const {
+ return channels_;
+ }
+
+ int RemainingFrames() const;
+
+ int atracID_ = -1;
+
+ u8 *dataBuf_ = nullptr;
+ // Indicates that the dataBuf_ array should not be used.
+ bool ignoreDataBuf_ = false;
+
+ u32 decodePos_ = 0;
+
+ u16 channels_ = 0;
+ u16 outputChannels_ = 2;
+
+ int currentSample_ = 0;
+ int endSample_ = 0;
+ int firstSampleOffset_ = 0;
+ // Offset of the first sample in the input buffer
+ int dataOff_ = 0;
+
+ std::vector loopinfo_;
+
+ int loopStartSample_ = -1;
+ int loopEndSample_ = -1;
+ int loopNum_ = 0;
+
+ InputBuffer first_{};
+ InputBuffer second_{};
+
+ PSPPointer context_{};
+
+ AtracStatus BufferState() const {
+ return bufferState_;
+ }
+
+ void ForceSeekToSample(int sample);
+ u8 *BufferStart();
+
+ void SeekToSample(int sample);
+
+ u32 CodecType() const {
+ return codecType_;
+ }
+ AudioDecoder *GetDecoder() const {
+ return decoder_;
+ }
+
+ uint32_t CurBufferAddress(int adjust = 0) {
+ u32 off = FileOffsetBySample(currentSample_ + adjust);
+ if (off < first_.size && ignoreDataBuf_) {
+ return first_.addr + off;
+ }
+ // If it's in dataBug, it's not in PSP memory.
+ return 0;
+ }
+
+ void CalculateStreamInfo(u32 *readOffset);
+
+ u32 StreamBufferEnd() const {
+ // The buffer is always aligned to a frame in size, not counting an optional header.
+ // The header will only initially exist after the data is first set.
+ u32 framesAfterHeader = (bufferMaxSize_ - bufferHeaderSize_) / bytesPerFrame_;
+ return framesAfterHeader * bytesPerFrame_ + bufferHeaderSize_;
+ }
+
+ int Analyze(u32 addr, u32 size);
+ int AnalyzeAA3(u32 addr, u32 size, u32 filesize);
+
+ void UpdateContextFromPSPMem();
+ void WriteContextToPSPMem();
+
+ int AddStreamData(u32 bytesToAdd);
+ u32 AddStreamDataSas(u32 bufPtr, u32 bytesToAdd);
+ void CreateDecoder();
+ void SetLoopNum(int loopNum);
+ u32 ResetPlayPosition(int sample, int bytesWrittenFirstBuf, int bytesWrittenSecondBuf);
+ void GetResetBufferInfo(AtracResetBufferInfo *bufferInfo, int sample);
+ int SetData(u32 buffer, u32 readSize, u32 bufferSize, int successCode = 0);
+ u32 SetSecondBuffer(u32 secondBuffer, u32 secondBufferSize);
+ u32 DecodeData(u8 *outbuf, u32 outbufPtr, u32 *SamplesNum, u32 *finish, int *remains);
+ void ConsumeFrame();
+ u32 GetNextSamples();
+
+ void InitLowLevel(u32 paramsAddr, bool jointStereo);
+
+ // To be moved into private.
+ u32 codecType_ = 0;
+
+private:
+ void AnalyzeReset();
+
+ u32 bitrate_ = 64;
+ u16 bytesPerFrame_ = 0;
+ u32 bufferMaxSize_ = 0;
+ int jointStereo_ = 0;
+
+ // Used by low-level decoding and to track streaming.
+ u32 bufferPos_ = 0;
+ u32 bufferValidBytes_ = 0;
+ u32 bufferHeaderSize_ = 0;
+
+ // TODO: Save the internal state of this, now technically possible.
+ AudioDecoder *decoder_ = nullptr;
+
+ AtracStatus bufferState_ = ATRAC_STATUS_NO_DATA;
+};
diff --git a/Core/HLE/sceAtrac.cpp b/Core/HLE/sceAtrac.cpp
index d84948cec8..1d395eba85 100644
--- a/Core/HLE/sceAtrac.cpp
+++ b/Core/HLE/sceAtrac.cpp
@@ -29,12 +29,12 @@
#include "Core/Debugger/MemBlockInfo.h"
#include "Core/HW/MediaEngine.h"
#include "Core/HW/BufferQueue.h"
-#include "Core/HW/Atrac3Standalone.h"
#include "Core/HLE/sceKernel.h"
#include "Core/HLE/sceUtility.h"
#include "Core/HLE/sceKernelMemory.h"
#include "Core/HLE/sceAtrac.h"
+#include "Core/HLE/AtracCtx.h"
#include "Core/System.h"
// Notes about sceAtrac buffer management
@@ -72,462 +72,8 @@
// any cache or separate version of the buffer - at most it manages decode state from earlier in
// the buffer.
-#define ATRAC_ERROR_API_FAIL 0x80630002
-#define ATRAC_ERROR_NO_ATRACID 0x80630003
-#define ATRAC_ERROR_INVALID_CODECTYPE 0x80630004
-#define ATRAC_ERROR_BAD_ATRACID 0x80630005
-#define ATRAC_ERROR_UNKNOWN_FORMAT 0x80630006
-#define ATRAC_ERROR_WRONG_CODECTYPE 0x80630007
-#define ATRAC_ERROR_BAD_CODEC_PARAMS 0x80630008
-#define ATRAC_ERROR_ALL_DATA_LOADED 0x80630009
-#define ATRAC_ERROR_NO_DATA 0x80630010
-#define ATRAC_ERROR_SIZE_TOO_SMALL 0x80630011
-#define ATRAC_ERROR_SECOND_BUFFER_NEEDED 0x80630012
-#define ATRAC_ERROR_INCORRECT_READ_SIZE 0x80630013
-#define ATRAC_ERROR_BAD_SAMPLE 0x80630015
-#define ATRAC_ERROR_BAD_FIRST_RESET_SIZE 0x80630016
-#define ATRAC_ERROR_BAD_SECOND_RESET_SIZE 0x80630017
-#define ATRAC_ERROR_ADD_DATA_IS_TOO_BIG 0x80630018
-#define ATRAC_ERROR_NOT_MONO 0x80630019
-#define ATRAC_ERROR_NO_LOOP_INFORMATION 0x80630021
-#define ATRAC_ERROR_SECOND_BUFFER_NOT_NEEDED 0x80630022
-#define ATRAC_ERROR_BUFFER_IS_EMPTY 0x80630023
-#define ATRAC_ERROR_ALL_DATA_DECODED 0x80630024
-#define ATRAC_ERROR_IS_LOW_LEVEL 0x80630031
-#define ATRAC_ERROR_IS_FOR_SCESAS 0x80630040
-#define ATRAC_ERROR_AA3_INVALID_DATA 0x80631003
-#define ATRAC_ERROR_AA3_SIZE_TOO_SMALL 0x80631004
-
-#define AT3_MAGIC 0x0270
-#define AT3_PLUS_MAGIC 0xFFFE
-#define PSP_MODE_AT_3_PLUS 0x00001000
-#define PSP_MODE_AT_3 0x00001001
-
-const int RIFF_CHUNK_MAGIC = 0x46464952;
-const int RIFF_WAVE_MAGIC = 0x45564157;
-const int FMT_CHUNK_MAGIC = 0x20746D66;
-const int DATA_CHUNK_MAGIC = 0x61746164;
-const int SMPL_CHUNK_MAGIC = 0x6C706D73;
-const int FACT_CHUNK_MAGIC = 0x74636166;
-
-const int PSP_ATRAC_ALLDATA_IS_ON_MEMORY = -1;
-const int PSP_ATRAC_NONLOOP_STREAM_DATA_IS_ON_MEMORY = -2;
-const int PSP_ATRAC_LOOP_STREAM_DATA_IS_ON_MEMORY = -3;
-
-const u32 ATRAC3_MAX_SAMPLES = 0x400;
-const u32 ATRAC3PLUS_MAX_SAMPLES = 0x800;
-
-const size_t overAllocBytes = 16384;
-
static const int atracDecodeDelay = 2300;
-enum AtracDecodeResult {
- ATDECODE_FAILED = -1,
- ATDECODE_FEEDME = 0,
- ATDECODE_GOTFRAME = 1,
- ATDECODE_BADFRAME = 2,
-};
-
-struct InputBuffer {
- // Address of the buffer.
- u32 addr;
- // Size of data read so far into dataBuf_ (to be removed.)
- u32 size;
- // Offset into addr at which new data is added.
- u32 offset;
- // Last writableBytes number (to be removed.)
- u32 writableBytes;
- // Unused, always 0.
- u32 neededBytes;
- // Total size of the entire file data.
- u32 filesize;
- // Offset into the file at which new data is read.
- u32 fileoffset;
-};
-
-struct Atrac;
-
-struct AtracLoopInfo {
- int cuePointID;
- int type;
- int startSample;
- int endSample;
- int fraction;
- int playCount;
-};
-
-struct AtracResetBufferInfo;
-
-struct Atrac {
- Atrac() {
- memset(&first_, 0, sizeof(first_));
- memset(&second_, 0, sizeof(second_));
- context_ = 0;
- }
-
- ~Atrac() {
- ResetData();
- }
-
- void ResetData() {
- delete decoder_;
- decoder_ = nullptr;
-
- if (dataBuf_)
- delete [] dataBuf_;
- dataBuf_ = 0;
- ignoreDataBuf_ = false;
- bufferState_ = ATRAC_STATUS_NO_DATA;
-
- if (context_.IsValid())
- kernelMemory.Free(context_.ptr);
- }
-
- void SetBufferState() {
- if (bufferMaxSize_ >= first_.filesize) {
- if (first_.size < first_.filesize) {
- // The buffer is big enough, but we don't have all the data yet.
- bufferState_ = ATRAC_STATUS_HALFWAY_BUFFER;
- } else {
- bufferState_ = ATRAC_STATUS_ALL_DATA_LOADED;
- }
- } else {
- if (loopEndSample_ <= 0) {
- // There's no looping, but we need to stream the data in our buffer.
- bufferState_ = ATRAC_STATUS_STREAMED_WITHOUT_LOOP;
- } else if (loopEndSample_ == endSample_ + firstSampleOffset_ + (int)FirstOffsetExtra()) {
- bufferState_ = ATRAC_STATUS_STREAMED_LOOP_FROM_END;
- } else {
- bufferState_ = ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER;
- }
- }
- }
-
- void DoState(PointerWrap &p) {
- auto s = p.Section("Atrac", 1, 9);
- if (!s)
- return;
-
- Do(p, channels_);
- Do(p, outputChannels_);
- if (s >= 5) {
- Do(p, jointStereo_);
- }
-
- Do(p, atracID_);
- Do(p, first_);
- Do(p, bufferMaxSize_);
- Do(p, codecType_);
-
- Do(p, currentSample_);
- Do(p, endSample_);
- Do(p, firstSampleOffset_);
- if (s >= 3) {
- Do(p, dataOff_);
- } else {
- dataOff_ = firstSampleOffset_;
- }
-
- u32 hasDataBuf = dataBuf_ != nullptr;
- Do(p, hasDataBuf);
- if (hasDataBuf) {
- if (p.mode == p.MODE_READ) {
- if (dataBuf_)
- delete [] dataBuf_;
- dataBuf_ = new u8[first_.filesize + overAllocBytes];
- memset(dataBuf_, 0, first_.filesize + overAllocBytes);
- }
- DoArray(p, dataBuf_, first_.filesize);
- }
- Do(p, second_);
-
- Do(p, decodePos_);
- if (s < 9) {
- u32 oldDecodeEnd = 0;
- Do(p, oldDecodeEnd);
- }
- if (s >= 4) {
- Do(p, bufferPos_);
- } else {
- bufferPos_ = decodePos_;
- }
-
- Do(p, bitrate_);
- Do(p, bytesPerFrame_);
-
- Do(p, loopinfo_);
- if (s < 9) {
- int oldLoopInfoNum = 42;
- Do(p, oldLoopInfoNum);
- }
-
- Do(p, loopStartSample_);
- Do(p, loopEndSample_);
- Do(p, loopNum_);
-
- Do(p, context_);
- if (s >= 6) {
- Do(p, bufferState_);
- } else {
- if (dataBuf_ == nullptr) {
- bufferState_ = ATRAC_STATUS_NO_DATA;
- } else {
- SetBufferState();
- }
- }
-
- if (s >= 7) {
- Do(p, ignoreDataBuf_);
- } else {
- ignoreDataBuf_ = false;
- }
-
- if (s >= 9) {
- Do(p, bufferValidBytes_);
- Do(p, bufferHeaderSize_);
- } else {
- bufferHeaderSize_ = dataOff_;
- bufferValidBytes_ = std::min(first_.size - dataOff_, StreamBufferEnd() - dataOff_);
- if ((bufferState_ & ATRAC_STATUS_STREAMED_MASK) == ATRAC_STATUS_STREAMED_MASK) {
- bufferPos_ = dataOff_;
- }
- }
-
- if (s < 8 && bufferState_ == ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER) {
- // We didn't actually allow the second buffer to be set this far back.
- // Pretend it's a regular loop, we'll just try our best.
- bufferState_ = ATRAC_STATUS_STREAMED_LOOP_FROM_END;
- }
-
- // Make sure to do this late; it depends on things like bytesPerFrame_.
- if (p.mode == p.MODE_READ && bufferState_ != ATRAC_STATUS_NO_DATA) {
- CreateDecoder();
- }
-
- if (s >= 2 && s < 9) {
- bool oldResetBuffer = false;
- Do(p, oldResetBuffer);
- }
- }
-
- int Analyze(u32 addr, u32 size);
- int AnalyzeAA3(u32 addr, u32 size, u32 filesize);
-
- u32 SamplesPerFrame() const {
- return codecType_ == PSP_MODE_AT_3_PLUS ? ATRAC3PLUS_MAX_SAMPLES : ATRAC3_MAX_SAMPLES;
- }
-
- u32 FirstOffsetExtra() const {
- return codecType_ == PSP_CODEC_AT3PLUS ? 368 : 69;
- }
-
- u32 DecodePosBySample(int sample) const {
- return (u32)(firstSampleOffset_ + sample / (int)SamplesPerFrame() * bytesPerFrame_);
- }
-
- u32 FileOffsetBySample(int sample) const {
- int offsetSample = sample + firstSampleOffset_;
- int frameOffset = offsetSample / (int)SamplesPerFrame();
- return (u32)(dataOff_ + bytesPerFrame_ + frameOffset * bytesPerFrame_);
- }
-
- int RemainingFrames() const {
- if (bufferState_ == ATRAC_STATUS_ALL_DATA_LOADED) {
- // Meaning, infinite I guess? We've got it all.
- return PSP_ATRAC_ALLDATA_IS_ON_MEMORY;
- }
-
- u32 currentFileOffset = FileOffsetBySample(currentSample_ - SamplesPerFrame() + FirstOffsetExtra());
- if (first_.fileoffset >= first_.filesize) {
- if (bufferState_ == ATRAC_STATUS_STREAMED_WITHOUT_LOOP) {
- return PSP_ATRAC_NONLOOP_STREAM_DATA_IS_ON_MEMORY;
- }
- int loopEndAdjusted = loopEndSample_ - FirstOffsetExtra() - 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;
- }
- if ((bufferState_ & ATRAC_STATUS_STREAMED_MASK) == ATRAC_STATUS_STREAMED_MASK && loopNum_ == 0) {
- return PSP_ATRAC_LOOP_STREAM_DATA_IS_ON_MEMORY;
- }
- }
-
- if ((bufferState_ & ATRAC_STATUS_STREAMED_MASK) == ATRAC_STATUS_STREAMED_MASK) {
- // Since we're streaming, the remaining frames are what's valid in the buffer.
- return bufferValidBytes_ / bytesPerFrame_;
- }
-
- // Since the first frame is shorter by this offset, add to round up at this offset.
- const int remainingBytes = first_.fileoffset - currentFileOffset;
- if (remainingBytes < 0) {
- // Just in case. Shouldn't happen, but once did by mistake.
- return 0;
- }
- return remainingBytes / bytesPerFrame_;
- }
-
- int atracID_ = -1;
-
- // TODO: Save the internal state of this, now technically possible.
- AudioDecoder *decoder_ = nullptr;
-
- u8 *dataBuf_ = nullptr;
-
- u32 decodePos_ = 0;
- // Used by low-level decoding and to track streaming.
- u32 bufferPos_ = 0;
- u32 bufferValidBytes_ = 0;
- u32 bufferHeaderSize_ = 0;
-
- u16 channels_ = 0;
- u16 outputChannels_ = 2;
- u32 bitrate_ = 64;
- u16 bytesPerFrame_ = 0;
- u32 bufferMaxSize_ = 0;
- int jointStereo_ = 0;
-
- int currentSample_ = 0;
- int endSample_ = 0;
- int firstSampleOffset_ = 0;
- // Offset of the first sample in the input buffer
- int dataOff_ = 0;
-
- std::vector loopinfo_;
-
- int loopStartSample_ = -1;
- int loopEndSample_ = -1;
- int loopNum_ = 0;
-
- // Indicates that the dataBuf_ array should not be used.
- bool ignoreDataBuf_ = false;
-
- u32 codecType_ = 0;
-
- InputBuffer first_;
- InputBuffer second_;
-
- PSPPointer context_;
-
- AtracStatus BufferState() const {
- return bufferState_;
- }
-
- void ForceSeekToSample(int sample) {
- if (decoder_) {
- decoder_->FlushBuffers();
- }
- currentSample_ = sample;
- }
-
- u8 *BufferStart() {
- return ignoreDataBuf_ ? Memory::GetPointerWrite(first_.addr) : dataBuf_;
- }
-
- void SeekToSample(int sample) {
- // It seems like the PSP aligns the sample position to 0x800...?
- const u32 offsetSamples = firstSampleOffset_ + FirstOffsetExtra();
- const u32 unalignedSamples = (offsetSamples + sample) % SamplesPerFrame();
- int seekFrame = sample + offsetSamples - unalignedSamples;
-
- if ((sample != currentSample_ || sample == 0) && decoder_ != nullptr) {
- // Prefill the decode buffer with packets before the first sample offset.
- decoder_->FlushBuffers();
-
- int adjust = 0;
- if (sample == 0) {
- int offsetSamples = firstSampleOffset_ + FirstOffsetExtra();
- adjust = -(int)(offsetSamples % SamplesPerFrame());
- }
- const u32 off = FileOffsetBySample(sample + adjust);
- const u32 backfill = bytesPerFrame_ * 2;
- const u32 start = off - dataOff_ < backfill ? dataOff_ : off - backfill;
-
- for (u32 pos = start; pos < off; pos += bytesPerFrame_) {
- decoder_->Decode(BufferStart() + pos, bytesPerFrame_, nullptr, nullptr, nullptr);
- }
- }
-
- currentSample_ = sample;
- }
-
- uint32_t CurBufferAddress(int adjust = 0) {
- u32 off = FileOffsetBySample(currentSample_ + adjust);
- if (off < first_.size && ignoreDataBuf_) {
- return first_.addr + off;
- }
- // If it's in dataBug, it's not in PSP memory.
- return 0;
- }
-
- void CalculateStreamInfo(u32 *readOffset);
-
- u32 StreamBufferEnd() const {
- // The buffer is always aligned to a frame in size, not counting an optional header.
- // The header will only initially exist after the data is first set.
- u32 framesAfterHeader = (bufferMaxSize_ - bufferHeaderSize_) / bytesPerFrame_;
- return framesAfterHeader * bytesPerFrame_ + bufferHeaderSize_;
- }
-
- void ConsumeFrame() {
- bufferPos_ += bytesPerFrame_;
- if ((bufferState_ & ATRAC_STATUS_STREAMED_MASK) == ATRAC_STATUS_STREAMED_MASK) {
- if (bufferValidBytes_ > bytesPerFrame_) {
- bufferValidBytes_ -= bytesPerFrame_;
- } else {
- bufferValidBytes_ = 0;
- }
- }
- if (bufferPos_ >= StreamBufferEnd()) {
- // Wrap around... theoretically, this should only happen at exactly StreamBufferEnd.
- bufferPos_ -= StreamBufferEnd();
- bufferHeaderSize_ = 0;
- }
- }
-
- void UpdateContextFromPSPMem();
-
- void WriteContextToPSPMem();
- u32 DecodeData(u8 *outbuf, u32 outbufPtr, u32 *SamplesNum, u32 *finish, int *remains);
- void GetResetBufferInfo(AtracResetBufferInfo *bufferInfo, int sample);
- void SetLoopNum(int loopNum);
- int AddStreamData(u32 bytesToAdd);
- u32 AddStreamDataSas(u32 bufPtr, u32 bytesToAdd);
- void CreateDecoder();
- u32 ResetPlayPosition(int sample, int bytesWrittenFirstBuf, int bytesWrittenSecondBuf);
- int SetData(u32 buffer, u32 readSize, u32 bufferSize, int successCode = 0);
- u32 GetNextSamples();
-
- void InitLowLevel(u32 paramsAddr, bool jointStereo);
-
-private:
- void AnalyzeReset();
-
- AtracStatus bufferState_ = ATRAC_STATUS_NO_DATA;
-};
-
-void Atrac::SetLoopNum(int loopNum) {
- // Spammed in MHU
- loopNum_ = loopNum;
- if (loopNum != 0 && loopinfo_.size() == 0) {
- // Just loop the whole audio
- loopStartSample_ = firstSampleOffset_ + FirstOffsetExtra();
- loopEndSample_ = endSample_ + firstSampleOffset_ + FirstOffsetExtra();
- }
- WriteContextToPSPMem();
-}
-
-struct AtracSingleResetBufferInfo {
- u32_le writePosPtr;
- u32_le writableBytes;
- u32_le minWriteBytes;
- u32_le filePos;
-};
-
-struct AtracResetBufferInfo {
- AtracSingleResetBufferInfo first;
- AtracSingleResetBufferInfo second;
-};
-
const int PSP_NUM_ATRAC_IDS = 6;
static bool atracInited = true;
static Atrac *atracContexts[PSP_NUM_ATRAC_IDS];
@@ -603,7 +149,7 @@ static Atrac *getAtrac(int atracID) {
static int createAtrac(Atrac *atrac) {
for (int i = 0; i < (int)ARRAY_SIZE(atracContexts); ++i) {
- if (atracContextTypes[i] == atrac->codecType_ && atracContexts[i] == 0) {
+ if (atracContextTypes[i] == atrac->CodecType() && atracContexts[i] == 0) {
atracContexts[i] = atrac;
atrac->atracID_ = i;
return i;
@@ -623,286 +169,7 @@ static int deleteAtrac(int atracID) {
return ATRAC_ERROR_BAD_ATRACID;
}
-void Atrac::AnalyzeReset() {
- // Reset some values.
- codecType_ = 0;
- currentSample_ = 0;
- endSample_ = -1;
- loopNum_ = 0;
- loopinfo_.clear();
- loopStartSample_ = -1;
- loopEndSample_ = -1;
- decodePos_ = 0;
- bufferPos_ = 0;
- channels_ = 2;
-}
-
-int Atrac::Analyze(u32 addr, u32 size) {
- struct RIFFFmtChunk {
- u16_le fmtTag;
- u16_le channels;
- u32_le samplerate;
- u32_le avgBytesPerSec;
- u16_le blockAlign;
- };
-
- first_.addr = addr;
- first_.size = size;
-
- AnalyzeReset();
-
- // 72 is about the size of the minimum required data to even be valid.
- if (first_.size < 72) {
- return hleReportError(ME, ATRAC_ERROR_SIZE_TOO_SMALL, "buffer too small");
- }
-
- // TODO: Check the range (addr, size) instead.
- if (!Memory::IsValidAddress(first_.addr)) {
- return hleReportWarning(ME, SCE_KERNEL_ERROR_ILLEGAL_ADDRESS, "invalid buffer address");
- }
-
- // TODO: Validate stuff.
- if (Memory::ReadUnchecked_U32(first_.addr) != RIFF_CHUNK_MAGIC) {
- return hleReportError(ME, ATRAC_ERROR_UNKNOWN_FORMAT, "invalid RIFF header");
- }
-
- u32 offset = 8;
- firstSampleOffset_ = 0;
-
- while (Memory::Read_U32(first_.addr + offset) != RIFF_WAVE_MAGIC) {
- // Get the size preceding the magic.
- int chunk = Memory::Read_U32(first_.addr + offset - 4);
- // Round the chunk size up to the nearest 2.
- offset += chunk + (chunk & 1);
- if (offset + 12 > first_.size) {
- return hleReportError(ME, ATRAC_ERROR_SIZE_TOO_SMALL, "too small for WAVE chunk at %d", offset);
- }
- if (Memory::Read_U32(first_.addr + offset) != RIFF_CHUNK_MAGIC) {
- return hleReportError(ME, ATRAC_ERROR_UNKNOWN_FORMAT, "RIFF chunk did not contain WAVE");
- }
- offset += 8;
- }
- offset += 4;
-
- if (offset != 12) {
- WARN_LOG_REPORT(ME, "RIFF chunk at offset: %d", offset);
- }
-
- // RIFF size excluding chunk header.
- first_.filesize = Memory::Read_U32(first_.addr + offset - 8) + 8;
- // Even if the RIFF size is too low, it may simply be incorrect. This works on real firmware.
- u32 maxSize = std::max(first_.filesize, first_.size);
-
- bool bfoundData = false;
- u32 dataChunkSize = 0;
- int sampleOffsetAdjust = 0;
- while (maxSize >= offset + 8 && !bfoundData) {
- int chunkMagic = Memory::Read_U32(first_.addr + offset);
- u32 chunkSize = Memory::Read_U32(first_.addr + offset + 4);
- // Account for odd sized chunks.
- if (chunkSize & 1) {
- WARN_LOG_REPORT_ONCE(oddchunk, ME, "RIFF chunk had uneven size");
- }
- chunkSize += (chunkSize & 1);
- offset += 8;
- if (chunkSize > maxSize - offset)
- break;
- switch (chunkMagic) {
- case FMT_CHUNK_MAGIC:
- {
- if (codecType_ != 0) {
- return hleReportError(ME, ATRAC_ERROR_UNKNOWN_FORMAT, "multiple fmt definitions");
- }
-
- auto at3fmt = PSPPointer::Create(first_.addr + offset);
- if (chunkSize < 32 || (at3fmt->fmtTag == AT3_PLUS_MAGIC && chunkSize < 52)) {
- return hleReportError(ME, ATRAC_ERROR_UNKNOWN_FORMAT, "fmt definition too small (%d)", chunkSize);
- }
-
- if (at3fmt->fmtTag == AT3_MAGIC)
- codecType_ = PSP_MODE_AT_3;
- else if (at3fmt->fmtTag == AT3_PLUS_MAGIC)
- codecType_ = PSP_MODE_AT_3_PLUS;
- else {
- return hleReportError(ME, ATRAC_ERROR_UNKNOWN_FORMAT, "invalid fmt magic: %04x", at3fmt->fmtTag);
- }
- channels_ = at3fmt->channels;
- if (channels_ != 1 && channels_ != 2) {
- return hleReportError(ME, ATRAC_ERROR_UNKNOWN_FORMAT, "invalid channel count: %d", channels_);
- }
- if (at3fmt->samplerate != 44100) {
- return hleReportError(ME, ATRAC_ERROR_UNKNOWN_FORMAT, "unsupported sample rate: %d", at3fmt->samplerate);
- }
- bitrate_ = at3fmt->avgBytesPerSec * 8;
- bytesPerFrame_ = at3fmt->blockAlign;
- if (bytesPerFrame_ == 0) {
- return hleReportError(ME, ATRAC_ERROR_UNKNOWN_FORMAT, "invalid bytes per frame: %d", bytesPerFrame_);
- }
-
- // TODO: There are some format specific bytes here which seem to have fixed values?
- // Probably don't need them.
-
- if (at3fmt->fmtTag == AT3_MAGIC) {
- // This is the offset to the jointStereo_ field.
- jointStereo_ = Memory::Read_U32(first_.addr + offset + 24);
- }
- }
- break;
- case FACT_CHUNK_MAGIC:
- {
- endSample_ = Memory::Read_U32(first_.addr + offset);
- if (chunkSize >= 8) {
- firstSampleOffset_ = Memory::Read_U32(first_.addr + offset + 4);
- }
- if (chunkSize >= 12) {
- u32 largerOffset = Memory::Read_U32(first_.addr + offset + 8);
- sampleOffsetAdjust = firstSampleOffset_ - largerOffset;
- }
- }
- break;
- case SMPL_CHUNK_MAGIC:
- {
- if (chunkSize < 32) {
- return hleReportError(ME, ATRAC_ERROR_UNKNOWN_FORMAT, "smpl chunk too small (%d)", chunkSize);
- }
- int checkNumLoops = Memory::Read_U32(first_.addr + offset + 28);
- if (checkNumLoops != 0 && chunkSize < 36 + 20) {
- return hleReportError(ME, ATRAC_ERROR_UNKNOWN_FORMAT, "smpl chunk too small for loop (%d, %d)", checkNumLoops, chunkSize);
- }
- if (checkNumLoops < 0) {
- return hleReportError(ME, ATRAC_ERROR_UNKNOWN_FORMAT, "bad checkNumLoops (%d)", checkNumLoops);
- }
-
- loopinfo_.resize(checkNumLoops);
- u32 loopinfoAddr = first_.addr + offset + 36;
- // The PSP only cares about the first loop start and end, it seems.
- // Most likely can skip the rest of this data, but it's not hurting anyone.
- for (int i = 0; i < checkNumLoops && 36 + (u32)i < chunkSize; i++, loopinfoAddr += 24) {
- loopinfo_[i].cuePointID = Memory::Read_U32(loopinfoAddr);
- loopinfo_[i].type = Memory::Read_U32(loopinfoAddr + 4);
- loopinfo_[i].startSample = Memory::Read_U32(loopinfoAddr + 8);
- loopinfo_[i].endSample = Memory::Read_U32(loopinfoAddr + 12);
- loopinfo_[i].fraction = Memory::Read_U32(loopinfoAddr + 16);
- loopinfo_[i].playCount = Memory::Read_U32(loopinfoAddr + 20);
-
- if (loopinfo_[i].startSample >= loopinfo_[i].endSample) {
- return hleReportError(ME, ATRAC_ERROR_BAD_CODEC_PARAMS, "loop starts after it ends");
- }
- }
- }
- break;
- case DATA_CHUNK_MAGIC:
- {
- bfoundData = true;
- dataOff_ = offset;
- dataChunkSize = chunkSize;
- if (first_.filesize < offset + chunkSize) {
- WARN_LOG_REPORT(ME, "Atrac data chunk extends beyond riff chunk");
- first_.filesize = offset + chunkSize;
- }
- }
- break;
- }
- offset += chunkSize;
- }
-
- if (codecType_ == 0) {
- return hleReportError(ME, ATRAC_ERROR_UNKNOWN_FORMAT, "could not detect codec");
- }
-
- if (!bfoundData) {
- return hleReportError(ME, ATRAC_ERROR_SIZE_TOO_SMALL, "no data chunk");
- }
-
- // set the loopStartSample_ and loopEndSample_ by loopinfo_
- if (loopinfo_.size() > 0) {
- loopStartSample_ = loopinfo_[0].startSample + FirstOffsetExtra() + sampleOffsetAdjust;
- loopEndSample_ = loopinfo_[0].endSample + FirstOffsetExtra() + sampleOffsetAdjust;
- } else {
- loopStartSample_ = -1;
- loopEndSample_ = -1;
- }
-
- // if there is no correct endsample, try to guess it
- if (endSample_ <= 0 && bytesPerFrame_ != 0) {
- endSample_ = (dataChunkSize / bytesPerFrame_) * SamplesPerFrame();
- endSample_ -= firstSampleOffset_ + FirstOffsetExtra();
- }
- endSample_ -= 1;
-
- if (loopEndSample_ != -1 && loopEndSample_ > endSample_ + firstSampleOffset_ + (int)FirstOffsetExtra()) {
- return hleReportError(ME, ATRAC_ERROR_BAD_CODEC_PARAMS, "loop after end of data");
- }
-
- return 0;
-}
-
-int Atrac::AnalyzeAA3(u32 addr, u32 size, u32 filesize) {
- first_.addr = addr;
- first_.size = size;
- first_.filesize = filesize;
-
- AnalyzeReset();
-
- if (first_.size < 10) {
- return hleReportError(ME, ATRAC_ERROR_AA3_SIZE_TOO_SMALL, "buffer too small");
- }
-
- // TODO: Make sure this validation is correct, more testing.
-
- const u8 *buffer = Memory::GetPointer(first_.addr);
- if (buffer[0] != 'e' || buffer[1] != 'a' || buffer[2] != '3') {
- return hleReportError(ME, ATRAC_ERROR_AA3_INVALID_DATA, "invalid ea3 magic bytes");
- }
-
- // It starts with an id3 header (replaced with ea3.) This is the size.
- u32 tagSize = buffer[9] | (buffer[8] << 7) | (buffer[7] << 14) | (buffer[6] << 21);
- if (first_.size < tagSize + 36) {
- return hleReportError(ME, ATRAC_ERROR_AA3_SIZE_TOO_SMALL, "truncated before id3 end");
- }
-
- // EA3 header starts at id3 header (10) + tagSize.
- buffer = Memory::GetPointer(first_.addr + 10 + tagSize);
- if (buffer[0] != 'E' || buffer[1] != 'A' || buffer[2] != '3') {
- return hleReportError(ME, ATRAC_ERROR_AA3_INVALID_DATA, "invalid EA3 magic bytes");
- }
-
- // Based on FFmpeg's code.
- u32 codecParams = buffer[35] | (buffer[34] << 8) | (buffer[35] << 16);
- const u32 at3SampleRates[8] = { 32000, 44100, 48000, 88200, 96000, 0 };
-
- switch (buffer[32]) {
- case 0:
- codecType_ = PSP_MODE_AT_3;
- bytesPerFrame_ = (codecParams & 0x03FF) * 8;
- bitrate_ = at3SampleRates[(codecParams >> 13) & 7] * bytesPerFrame_ * 8 / 1024;
- channels_ = 2;
- jointStereo_ = (codecParams >> 17) & 1;
- break;
- case 1:
- codecType_ = PSP_MODE_AT_3_PLUS;
- bytesPerFrame_ = ((codecParams & 0x03FF) * 8) + 8;
- bitrate_ = at3SampleRates[(codecParams >> 13) & 7] * bytesPerFrame_ * 8 / 2048;
- channels_ = (codecParams >> 10) & 7;
- break;
- case 3:
- case 4:
- case 5:
- return hleReportError(ME, ATRAC_ERROR_AA3_INVALID_DATA, "unsupported codec type %d", buffer[32]);
- default:
- return hleReportError(ME, ATRAC_ERROR_AA3_INVALID_DATA, "invalid codec type %d", buffer[32]);
- }
-
- dataOff_ = 10 + tagSize + 96;
- firstSampleOffset_ = 0;
- if (endSample_ < 0 && bytesPerFrame_ != 0) {
- endSample_ = ((first_.filesize - dataOff_) / bytesPerFrame_) * SamplesPerFrame();
- }
- endSample_ -= 1;
-
- return 0;
-}
-
+// Really, allocate a new Atrac context of a specific codec type.
static u32 sceAtracGetAtracID(int codecType) {
if (codecType != PSP_MODE_AT_3 && codecType != PSP_MODE_AT_3_PLUS) {
return hleReportError(ME, ATRAC_ERROR_INVALID_CODECTYPE, "invalid codecType");
@@ -919,21 +186,6 @@ static u32 sceAtracGetAtracID(int codecType) {
return hleLogSuccessInfoI(ME, atracID);
}
-u32 Atrac::AddStreamDataSas(u32 bufPtr, u32 bytesToAdd) {
- int addbytes = std::min(bytesToAdd, first_.filesize - first_.fileoffset - FirstOffsetExtra());
- Memory::Memcpy(dataBuf_ + first_.fileoffset + FirstOffsetExtra(), bufPtr, addbytes, "AtracAddStreamData");
- first_.size += bytesToAdd;
- if (first_.size >= first_.filesize) {
- first_.size = first_.filesize;
- if (bufferState_ == ATRAC_STATUS_HALFWAY_BUFFER)
- bufferState_ = ATRAC_STATUS_ALL_DATA_LOADED;
- }
- first_.fileoffset += addbytes;
- // refresh context_
- WriteContextToPSPMem();
- return 0;
-}
-
static u32 AtracValidateData(const Atrac *atrac) {
if (!atrac) {
return hleLogError(ME, ATRAC_ERROR_BAD_ATRACID, "bad atrac ID");
@@ -958,59 +210,6 @@ static u32 AtracValidateManaged(const Atrac *atrac) {
}
}
-void Atrac::CalculateStreamInfo(u32 *outReadOffset) {
- u32 readOffset = first_.fileoffset;
- if (bufferState_ == ATRAC_STATUS_ALL_DATA_LOADED) {
- // Nothing to write.
- readOffset = 0;
- first_.offset = 0;
- first_.writableBytes = 0;
- } else if (bufferState_ == ATRAC_STATUS_HALFWAY_BUFFER) {
- // If we're buffering the entire file, just give the same as readOffset.
- first_.offset = readOffset;
- // In this case, the bytes writable are just the remaining bytes, always.
- first_.writableBytes = first_.filesize - readOffset;
- } else {
- u32 bufferEnd = StreamBufferEnd();
- u32 bufferValidExtended = bufferPos_ + bufferValidBytes_;
- if (bufferValidExtended < bufferEnd) {
- first_.offset = bufferValidExtended;
- first_.writableBytes = bufferEnd - bufferValidExtended;
- } else {
- u32 bufferStartUsed = bufferValidExtended - bufferEnd;
- first_.offset = bufferStartUsed;
- first_.writableBytes = bufferPos_ - bufferStartUsed;
- }
-
- if (readOffset >= first_.filesize) {
- if (bufferState_ == ATRAC_STATUS_STREAMED_WITHOUT_LOOP) {
- // We don't need anything more, so all 0s.
- readOffset = 0;
- first_.offset = 0;
- first_.writableBytes = 0;
- } else {
- readOffset = FileOffsetBySample(loopStartSample_ - FirstOffsetExtra() - firstSampleOffset_ - SamplesPerFrame() * 2);
- }
- }
-
- if (readOffset + first_.writableBytes > first_.filesize) {
- // Never ask for past the end of file, even when the space is free.
- first_.writableBytes = first_.filesize - readOffset;
- }
-
- // If you don't think this should be here, remove it. It's just a temporary safety check.
- if (first_.offset + first_.writableBytes > bufferMaxSize_) {
- ERROR_LOG_REPORT(ME, "Somehow calculated too many writable bytes: %d + %d > %d", first_.offset, first_.writableBytes, bufferMaxSize_);
- first_.offset = 0;
- first_.writableBytes = bufferMaxSize_;
- }
- }
-
- if (outReadOffset) {
- *outReadOffset = readOffset;
- }
-}
-
// Notifies that more data is (OR will be very soon) available in the buffer.
// This implies it has been added to whatever position sceAtracGetStreamDataInfo would indicate.
//
@@ -1037,174 +236,6 @@ static u32 sceAtracAddStreamData(int atracID, u32 bytesToAdd) {
return hleLogSuccessI(ME, 0);
}
-int Atrac::AddStreamData(u32 bytesToAdd) {
- u32 readOffset;
- CalculateStreamInfo(&readOffset);
- if (bytesToAdd > first_.writableBytes)
- return hleLogWarning(ME, ATRAC_ERROR_ADD_DATA_IS_TOO_BIG, "too many bytes");
-
- if (bytesToAdd > 0) {
- first_.fileoffset = readOffset;
- int addbytes = std::min(bytesToAdd, first_.filesize - first_.fileoffset);
- if (!ignoreDataBuf_) {
- Memory::Memcpy(dataBuf_ + first_.fileoffset, first_.addr + first_.offset, addbytes, "AtracAddStreamData");
- }
- first_.fileoffset += addbytes;
- }
- first_.size += bytesToAdd;
- if (first_.size >= first_.filesize) {
- first_.size = first_.filesize;
- if (bufferState_ == ATRAC_STATUS_HALFWAY_BUFFER)
- bufferState_ = ATRAC_STATUS_ALL_DATA_LOADED;
- WriteContextToPSPMem();
- }
-
- first_.offset += bytesToAdd;
- bufferValidBytes_ += bytesToAdd;
-
- if (PSP_CoreParameter().compat.flags().AtracLoopHack && bufferState_ == ATRAC_STATUS_STREAMED_LOOP_FROM_END && RemainingFrames() > 2) {
- loopNum_++;
- SeekToSample(loopStartSample_ - FirstOffsetExtra() - firstSampleOffset_);
- }
-
- return 0;
-}
-
-u32 Atrac::DecodeData(u8 *outbuf, u32 outbufPtr, u32 *SamplesNum, u32 *finish, int *remains) {
- int loopNum = loopNum_;
- if (bufferState_ == ATRAC_STATUS_FOR_SCESAS) {
- // TODO: Might need more testing.
- loopNum = 0;
- }
-
- // We already passed the end - return an error (many games check for this.)
- if (currentSample_ >= endSample_ && loopNum == 0) {
- *SamplesNum = 0;
- *finish = 1;
- // refresh context_
- WriteContextToPSPMem();
- return ATRAC_ERROR_ALL_DATA_DECODED;
- }
-
- // TODO: This isn't at all right, but at least it makes the music "last" some time.
- u32 numSamples = 0;
-
- // It seems like the PSP aligns the sample position to 0x800...?
- int offsetSamples = firstSampleOffset_ + FirstOffsetExtra();
- int skipSamples = 0;
- u32 maxSamples = endSample_ + 1 - currentSample_;
- u32 unalignedSamples = (offsetSamples + currentSample_) % SamplesPerFrame();
- if (unalignedSamples != 0) {
- // We're off alignment, possibly due to a loop. Force it back on.
- maxSamples = SamplesPerFrame() - unalignedSamples;
- skipSamples = unalignedSamples;
- }
-
- 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.
- ConsumeFrame();
- }
-
- // TODO: We don't support any other codec type, check seems unnecessary?
- if ((codecType_ == PSP_MODE_AT_3 || codecType_ == PSP_MODE_AT_3_PLUS)) {
- SeekToSample(currentSample_);
-
- AtracDecodeResult res = ATDECODE_FEEDME;
- u32 off = FileOffsetBySample(currentSample_ - skipSamples);
- if (off < first_.size) {
- uint8_t *indata = BufferStart() + off;
- int bytesConsumed = 0;
- int outBytes = 0;
- if (!decoder_->Decode(indata, bytesPerFrame_, &bytesConsumed,
- outbuf, &outBytes)) {
- // Decode failed.
- *SamplesNum = 0;
- *finish = 1;
- return ATRAC_ERROR_ALL_DATA_DECODED;
- }
- res = ATDECODE_GOTFRAME;
-
- numSamples = outBytes / 4;
- uint32_t packetAddr = CurBufferAddress(-skipSamples);
- // got a frame
- int skipped = std::min((u32)skipSamples, numSamples);
- skipSamples -= skipped;
- numSamples = numSamples - skipped;
- // If we're at the end, clamp to samples we want. It always returns a full chunk.
- numSamples = std::min(maxSamples, numSamples);
-
- if (skipped > 0 && numSamples == 0) {
- // Wait for the next one.
- res = ATDECODE_FEEDME;
- }
-
- if (packetAddr != 0 && MemBlockInfoDetailed()) {
- char tagData[128];
- size_t tagSize = FormatMemWriteTagAt(tagData, sizeof(tagData), "AtracDecode/", packetAddr, bytesPerFrame_);
- NotifyMemInfo(MemBlockFlags::READ, packetAddr, bytesPerFrame_, tagData, tagSize);
- NotifyMemInfo(MemBlockFlags::WRITE, outbufPtr, outBytes, tagData, tagSize);
- } else {
- NotifyMemInfo(MemBlockFlags::WRITE, outbufPtr, outBytes, "AtracDecode");
- }
- // We only want one frame per call, let's continue the next time.
- }
-
- if (res != ATDECODE_GOTFRAME && currentSample_ < endSample_) {
- // Never got a frame. We may have dropped a GHA frame or otherwise have a bug.
- // For now, let's try to provide an extra "frame" if possible so games don't infinite loop.
- if (FileOffsetBySample(currentSample_) < first_.filesize) {
- numSamples = std::min(maxSamples, SamplesPerFrame());
- u32 outBytes = numSamples * outputChannels_ * sizeof(s16);
- if (outbuf != nullptr) {
- memset(outbuf, 0, outBytes);
- NotifyMemInfo(MemBlockFlags::WRITE, outbufPtr, outBytes, "AtracDecode");
- }
- }
- }
- }
-
- *SamplesNum = numSamples;
- // update current sample and decodePos
- currentSample_ += numSamples;
- decodePos_ = DecodePosBySample(currentSample_);
-
- ConsumeFrame();
-
- int finishFlag = 0;
- // TODO: Verify.
- bool hitEnd = currentSample_ >= endSample_ || (numSamples == 0 && first_.size >= first_.filesize);
- int loopEndAdjusted = loopEndSample_ - FirstOffsetExtra() - firstSampleOffset_;
- if ((hitEnd || currentSample_ > loopEndAdjusted) && loopNum != 0) {
- SeekToSample(loopStartSample_ - FirstOffsetExtra() - firstSampleOffset_);
- 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 = FileOffsetBySample(loopStartSample_ - FirstOffsetExtra() - firstSampleOffset_ - 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 = FileOffsetBySample(loopStartSample_ - FirstOffsetExtra() - firstSampleOffset_ - SamplesPerFrame() * 2);
- }
- }
- } else if (hitEnd) {
- finishFlag = 1;
-
- // Still move forward, so we know that we've read everything.
- // This seems to be reflected in the context as well.
- currentSample_ += SamplesPerFrame() - numSamples;
- }
-
- *finish = finishFlag;
- *remains = RemainingFrames();
- // refresh context_
- WriteContextToPSPMem();
- return 0;
-}
-
// Note that outAddr being null is completely valid here, used to skip data.
static u32 sceAtracDecodeData(int atracID, u32 outAddr, u32 numSamplesAddr, u32 finishFlagAddr, u32 remainAddr) {
Atrac *atrac = getAtrac(atracID);
@@ -1243,61 +274,6 @@ static u32 sceAtracEndEntry() {
return 0;
}
-void Atrac::GetResetBufferInfo(AtracResetBufferInfo *bufferInfo, int sample) {
- if (bufferState_ == ATRAC_STATUS_ALL_DATA_LOADED) {
- bufferInfo->first.writePosPtr = first_.addr;
- // Everything is loaded, so nothing needs to be read.
- bufferInfo->first.writableBytes = 0;
- bufferInfo->first.minWriteBytes = 0;
- bufferInfo->first.filePos = 0;
- } else if (bufferState_ == ATRAC_STATUS_HALFWAY_BUFFER) {
- // Here the message is: you need to read at least this many bytes to get to that position.
- // This is because we're filling the buffer start to finish, not streaming.
- bufferInfo->first.writePosPtr = first_.addr + first_.size;
- bufferInfo->first.writableBytes = first_.filesize - first_.size;
- int minWriteBytes = FileOffsetBySample(sample) - first_.size;
- if (minWriteBytes > 0) {
- bufferInfo->first.minWriteBytes = minWriteBytes;
- } else {
- bufferInfo->first.minWriteBytes = 0;
- }
- bufferInfo->first.filePos = first_.size;
- } else {
- // This is without the sample offset. The file offset also includes the previous batch of samples?
- int sampleFileOffset = FileOffsetBySample(sample - firstSampleOffset_ - SamplesPerFrame());
-
- // Update the writable bytes. When streaming, this is just the number of bytes until the end.
- const u32 bufSizeAligned = (bufferMaxSize_ / bytesPerFrame_) * bytesPerFrame_;
- const int needsMoreFrames = FirstOffsetExtra();
-
- bufferInfo->first.writePosPtr = first_.addr;
- bufferInfo->first.writableBytes = std::min(first_.filesize - sampleFileOffset, bufSizeAligned);
- if (((sample + firstSampleOffset_) % (int)SamplesPerFrame()) >= (int)SamplesPerFrame() - needsMoreFrames) {
- // Not clear why, but it seems it wants a bit extra in case the sample is late?
- bufferInfo->first.minWriteBytes = bytesPerFrame_ * 3;
- } else {
- bufferInfo->first.minWriteBytes = bytesPerFrame_ * 2;
- }
- if ((u32)sample < (u32)firstSampleOffset_ && sampleFileOffset != dataOff_) {
- sampleFileOffset -= bytesPerFrame_;
- }
- bufferInfo->first.filePos = sampleFileOffset;
-
- if (second_.size != 0) {
- // TODO: We have a second buffer. Within it, minWriteBytes should be zero.
- // The filePos should be after the end of the second buffer (or zero.)
- // We actually need to ensure we READ from the second buffer before implementing that.
- }
- }
-
- // It seems like this is always the same as the first buffer's pos, weirdly.
- bufferInfo->second.writePosPtr = first_.addr;
- // Reset never needs a second buffer write, since the loop is in a fixed place.
- bufferInfo->second.writableBytes = 0;
- bufferInfo->second.minWriteBytes = 0;
- bufferInfo->second.filePos = 0;
-}
-
// Obtains information about what needs to be in the buffer to seek (or "reset")
// Generally called by games right before calling sceAtracResetPlayPosition().
static u32 sceAtracGetBufferInfoForResetting(int atracID, int sample, u32 bufferInfoAddr) {
@@ -1330,13 +306,10 @@ static u32 sceAtracGetBitrate(int atracID, u32 outBitrateAddr) {
return err;
}
- atrac->bitrate_ = (atrac->bytesPerFrame_ * 352800) / 1000;
- if (atrac->codecType_ == PSP_MODE_AT_3_PLUS)
- atrac->bitrate_ = ((atrac->bitrate_ >> 11) + 8) & 0xFFFFFFF0;
- else
- atrac->bitrate_ = (atrac->bitrate_ + 511) >> 10;
+ atrac->UpdateBitrate();
+
if (Memory::IsValidAddress(outBitrateAddr)) {
- Memory::WriteUnchecked_U32(atrac->bitrate_, outBitrateAddr);
+ Memory::WriteUnchecked_U32(atrac->Bitrate(), outBitrateAddr);
return hleLogSuccessI(ME, 0);
} else {
return hleLogError(ME, 0, "invalid address");
@@ -1352,7 +325,7 @@ static u32 sceAtracGetChannel(int atracID, u32 channelAddr) {
}
if (Memory::IsValidAddress(channelAddr)){
- Memory::WriteUnchecked_U32(atrac->channels_, channelAddr);
+ Memory::WriteUnchecked_U32(atrac->Channels(), channelAddr);
return hleLogSuccessI(ME, 0);
} else {
return hleLogError(ME, 0, "invalid address");
@@ -1451,27 +424,6 @@ static u32 sceAtracGetNextSample(int atracID, u32 outNAddr) {
return hleLogSuccessI(ME, 0, "%d samples left", numSamples);
}
-u32 Atrac::GetNextSamples() {
- // It seems like the PSP aligns the sample position to 0x800...?
- u32 skipSamples = firstSampleOffset_ + FirstOffsetExtra();
- u32 firstSamples = (SamplesPerFrame() - skipSamples) % SamplesPerFrame();
- u32 numSamples = endSample_ + 1 - currentSample_;
- if (currentSample_ == 0 && firstSamples != 0) {
- numSamples = firstSamples;
- }
- u32 unalignedSamples = (skipSamples + currentSample_) % SamplesPerFrame();
- if (unalignedSamples != 0) {
- // We're off alignment, possibly due to a loop. Force it back on.
- numSamples = SamplesPerFrame() - unalignedSamples;
- }
- if (numSamples > SamplesPerFrame())
- numSamples = SamplesPerFrame();
- if (bufferState_ == ATRAC_STATUS_STREAMED_LOOP_FROM_END && (int)numSamples + currentSample_ > endSample_) {
- bufferState_ = ATRAC_STATUS_ALL_DATA_LOADED;
- }
- return numSamples;
-}
-
// Obtains the number of frames remaining in the buffer which can be decoded.
// When no more data would be needed, this returns a negative number.
static u32 sceAtracGetRemainFrame(int atracID, u32 remainAddr) {
@@ -1604,139 +556,6 @@ static u32 sceAtracResetPlayPosition(int atracID, int sample, int bytesWrittenFi
return hleDelayResult(hleLogSuccessInfoI(ME, 0), "reset play pos", 3000);
}
-u32 Atrac::ResetPlayPosition(int sample, int bytesWrittenFirstBuf, int bytesWrittenSecondBuf) {
- // Reuse the same calculation as before.
- AtracResetBufferInfo bufferInfo;
- GetResetBufferInfo(&bufferInfo, sample);
-
- if ((u32)bytesWrittenFirstBuf < bufferInfo.first.minWriteBytes || (u32)bytesWrittenFirstBuf > bufferInfo.first.writableBytes) {
- return hleLogError(ME, ATRAC_ERROR_BAD_FIRST_RESET_SIZE, "first byte count not in valid range");
- }
- if ((u32)bytesWrittenSecondBuf < bufferInfo.second.minWriteBytes || (u32)bytesWrittenSecondBuf > bufferInfo.second.writableBytes) {
- return hleLogError(ME, ATRAC_ERROR_BAD_SECOND_RESET_SIZE, "second byte count not in valid range");
- }
-
- if (bufferState_ == ATRAC_STATUS_ALL_DATA_LOADED) {
- // Always adds zero bytes.
- } else if (bufferState_ == ATRAC_STATUS_HALFWAY_BUFFER) {
- // Okay, it's a valid number of bytes. Let's set them up.
- if (bytesWrittenFirstBuf != 0) {
- if (!ignoreDataBuf_) {
- Memory::Memcpy(dataBuf_ + first_.size, first_.addr + first_.size, bytesWrittenFirstBuf, "AtracResetPlayPosition");
- }
- first_.fileoffset += bytesWrittenFirstBuf;
- first_.size += bytesWrittenFirstBuf;
- first_.offset += bytesWrittenFirstBuf;
- }
-
- // Did we transition to a full buffer?
- if (first_.size >= first_.filesize) {
- first_.size = first_.filesize;
- bufferState_ = ATRAC_STATUS_ALL_DATA_LOADED;
- }
- } else {
- if (bufferInfo.first.filePos > first_.filesize) {
- return hleDelayResult(hleLogError(ME, ATRAC_ERROR_API_FAIL, "invalid file position"), "reset play pos", 200);
- }
-
- // Move the offset to the specified position.
- first_.fileoffset = bufferInfo.first.filePos;
-
- if (bytesWrittenFirstBuf != 0) {
- if (!ignoreDataBuf_) {
- Memory::Memcpy(dataBuf_ + first_.fileoffset, first_.addr, bytesWrittenFirstBuf, "AtracResetPlayPosition");
- }
- first_.fileoffset += bytesWrittenFirstBuf;
- }
- first_.size = first_.fileoffset;
- first_.offset = bytesWrittenFirstBuf;
-
- bufferHeaderSize_ = 0;
- bufferPos_ = bytesPerFrame_;
- bufferValidBytes_ = bytesWrittenFirstBuf - bufferPos_;
- }
-
- if (codecType_ == PSP_MODE_AT_3 || codecType_ == PSP_MODE_AT_3_PLUS) {
- SeekToSample(sample);
- }
-
- WriteContextToPSPMem();
- return 0;
-}
-
-void Atrac::CreateDecoder() {
- if (decoder_) {
- delete decoder_;
- }
-
- // First, init the standalone decoder. Only used for low-level-decode initially, but simple.
- if (codecType_ == PSP_MODE_AT_3) {
- // We don't pull this from the RIFF so that we can support OMA also.
- uint8_t extraData[14]{};
- // The only thing that changes are the jointStereo_ values.
- extraData[0] = 1;
- extraData[3] = channels_ << 3;
- extraData[6] = jointStereo_;
- extraData[8] = jointStereo_;
- extraData[10] = 1;
- decoder_ = CreateAtrac3Audio(channels_, bytesPerFrame_, extraData, sizeof(extraData));
- } else {
- decoder_ = CreateAtrac3PlusAudio(channels_, bytesPerFrame_);
- }
- // reinit decodePos, because ffmpeg had changed it.
- decodePos_ = 0;
-}
-
-int Atrac::SetData(u32 buffer, u32 readSize, u32 bufferSize, int successCode) {
- first_.addr = buffer;
- first_.size = readSize;
-
- if (first_.size > first_.filesize)
- first_.size = first_.filesize;
- first_.fileoffset = first_.size;
-
- // got the size of temp buf, and calculate offset
- bufferMaxSize_ = bufferSize;
- first_.offset = first_.size;
-
- // some games may reuse an atracID for playing sound
- ResetData();
- SetBufferState();
-
- if (codecType_ != PSP_MODE_AT_3 && codecType_ != PSP_MODE_AT_3_PLUS) {
- // Shouldn't have gotten here, Analyze() checks this.
- bufferState_ = ATRAC_STATUS_NO_DATA;
- return hleReportError(ME, ATRAC_ERROR_UNKNOWN_FORMAT, "unexpected codec type in set data");
- }
-
- if (bufferState_ == ATRAC_STATUS_ALL_DATA_LOADED || bufferState_ == ATRAC_STATUS_HALFWAY_BUFFER) {
- // This says, don't use the dataBuf_ array, use the PSP RAM.
- // This way, games can load data async into the buffer, and it still works.
- // TODO: Support this always, even for streaming.
- ignoreDataBuf_ = true;
- }
- if (bufferState_ == ATRAC_STATUS_STREAMED_WITHOUT_LOOP || bufferState_ == ATRAC_STATUS_STREAMED_LOOP_FROM_END || bufferState_ == ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER) {
- bufferHeaderSize_ = dataOff_;
- bufferPos_ = dataOff_ + bytesPerFrame_;
- bufferValidBytes_ = first_.size - bufferPos_;
- }
-
- const char *codecName = codecType_ == PSP_MODE_AT_3 ? "atrac3" : "atrac3+";
- const char *channelName = channels_ == 1 ? "mono" : "stereo";
-
- // Over-allocate databuf to prevent going off the end if the bitstream is bad or if there are
- // bugs in the decoder. This happens, see issue #15788. Arbitrary, but let's make it a whole page on the popular
- // architecture that has the largest pages (M1).
- dataBuf_ = new u8[first_.filesize + overAllocBytes];
- memset(dataBuf_, 0, first_.filesize + overAllocBytes);
- if (!ignoreDataBuf_) {
- u32 copybytes = std::min(bufferSize, first_.filesize);
- Memory::Memcpy(dataBuf_, buffer, copybytes, "AtracSetData");
- }
- CreateDecoder();
- return hleLogSuccessInfoI(ME, successCode, "%s %s audio", codecName, channelName);
-}
-
static int _AtracSetData(int atracID, u32 buffer, u32 readSize, u32 bufferSize, bool needReturnAtracID = false) {
Atrac *atrac = getAtrac(atracID);
// Don't use AtracValidateManaged here.
@@ -1776,21 +595,7 @@ static u32 sceAtracSetSecondBuffer(int atracID, u32 secondBuffer, u32 secondBuff
return err;
}
- u32 secondFileOffset = atrac->FileOffsetBySample(atrac->loopEndSample_ - atrac->firstSampleOffset_);
- u32 desiredSize = atrac->first_.filesize - secondFileOffset;
-
- // 3 seems to be the number of frames required to handle a loop.
- if (secondBufferSize < desiredSize && secondBufferSize < (u32)atrac->bytesPerFrame_ * 3) {
- return hleReportError(ME, ATRAC_ERROR_SIZE_TOO_SMALL, "too small");
- }
- if (atrac->BufferState() != ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER) {
- return hleReportError(ME, ATRAC_ERROR_SECOND_BUFFER_NOT_NEEDED, "not needed");
- }
-
- atrac->second_.addr = secondBuffer;
- atrac->second_.size = secondBufferSize;
- atrac->second_.fileoffset = secondFileOffset;
- return hleLogSuccessI(ME, 0);
+ return atrac->SetSecondBuffer(secondBuffer, secondBufferSize);
}
static u32 sceAtracSetData(int atracID, u32 buffer, u32 bufferSize) {
@@ -2069,57 +874,6 @@ static int sceAtracSetAA3DataAndGetID(u32 buffer, u32 bufferSize, u32 fileSize,
return _AtracSetData(atracID, buffer, bufferSize, bufferSize, true);
}
-void Atrac::UpdateContextFromPSPMem() {
- if (!context_.IsValid()) {
- return;
- }
-
- // Read in any changes from the game to the context.
- // TODO: Might be better to just always track in RAM.
- bufferState_ = context_->info.state;
- // This value is actually abused by games to store the SAS voice number.
- loopNum_ = context_->info.loopNum;
-}
-
-void Atrac::WriteContextToPSPMem() {
- if (!context_.IsValid()) {
- return;
- }
- // context points into PSP memory.
- SceAtracContext *context = context_;
- context->info.buffer = first_.addr;
- context->info.bufferByte = bufferMaxSize_;
- context->info.secondBuffer = second_.addr;
- context->info.secondBufferByte = second_.size;
- context->info.codec = codecType_;
- context->info.loopNum = loopNum_;
- context->info.loopStart = loopStartSample_ > 0 ? loopStartSample_ : 0;
- context->info.loopEnd = loopEndSample_ > 0 ? loopEndSample_ : 0;
-
- // Note that we read in the state when loading the atrac object, so it's safe
- // to update it back here all the time. Some games, like Sol Trigger, change it.
- // TODO: Should we just keep this in PSP ram then, or something?
- context->info.state = bufferState_;
- if (firstSampleOffset_ != 0) {
- context->info.samplesPerChan = firstSampleOffset_ + FirstOffsetExtra();
- } else {
- context->info.samplesPerChan = (codecType_ == PSP_MODE_AT_3_PLUS ? ATRAC3PLUS_MAX_SAMPLES : ATRAC3_MAX_SAMPLES);
- }
- context->info.sampleSize = bytesPerFrame_;
- context->info.numChan = channels_;
- context->info.dataOff = dataOff_;
- context->info.endSample = endSample_ + firstSampleOffset_ + FirstOffsetExtra();
- context->info.dataEnd = first_.filesize;
- context->info.curOff = first_.fileoffset;
- context->info.decodePos = DecodePosBySample(currentSample_);
- context->info.streamDataByte = first_.size - dataOff_;
-
- u8 *buf = (u8 *)context;
- *(u32_le *)(buf + 0xfc) = atracID_;
-
- NotifyMemInfo(MemBlockFlags::WRITE, context_.ptr, sizeof(SceAtracContext), "AtracContext");
-}
-
static u32 _sceAtracGetContextAddress(int atracID) {
Atrac *atrac = getAtrac(atracID);
if (!atrac) {
@@ -2147,7 +901,7 @@ struct At3HeaderMap {
u8 jointStereo;
bool Matches(const Atrac *at) const {
- return bytes == at->bytesPerFrame_ && channels == at->channels_;
+ return bytes == at->BytesPerFrame() && channels == at->Channels();
}
};
@@ -2188,7 +942,7 @@ static int sceAtracLowLevelInitDecoder(int atracID, u32 paramsAddr) {
}
}
if (!found) {
- ERROR_LOG_REPORT(ME, "AT3 header map lacks entry for bpf: %i channels: %i", atrac->bytesPerFrame_, atrac->channels_);
+ ERROR_LOG_REPORT(ME, "AT3 header map lacks entry for bpf: %i channels: %i", atrac->BytesPerFrame(), atrac->Channels());
// TODO: Should we return an error code for these values?
}
}
@@ -2200,33 +954,6 @@ static int sceAtracLowLevelInitDecoder(int atracID, u32 paramsAddr) {
return hleLogSuccessInfoI(ME, 0, "%s %s audio", codecName, channelName);
}
-void Atrac::InitLowLevel(u32 paramsAddr, bool jointStereo) {
- channels_ = Memory::Read_U32(paramsAddr);
- outputChannels_ = Memory::Read_U32(paramsAddr + 4);
- bufferMaxSize_ = Memory::Read_U32(paramsAddr + 8);
- bytesPerFrame_ = bufferMaxSize_;
- first_.writableBytes = bytesPerFrame_;
- ResetData();
-
- if (codecType_ == PSP_MODE_AT_3) {
- bitrate_ = (bytesPerFrame_ * 352800) / 1000;
- bitrate_ = (bitrate_ + 511) >> 10;
- jointStereo_ = false;
- } else if (codecType_ == PSP_MODE_AT_3_PLUS) {
- bitrate_ = (bytesPerFrame_ * 352800) / 1000;
- bitrate_ = ((bitrate_ >> 11) + 8) & 0xFFFFFFF0;
- jointStereo_ = false;
- }
-
- dataOff_ = 0;
- first_.size = 0;
- first_.filesize = bytesPerFrame_;
- bufferState_ = ATRAC_STATUS_LOW_LEVEL;
- currentSample_ = 0;
- CreateDecoder();
- WriteContextToPSPMem();
-}
-
static int sceAtracLowLevelDecode(int atracID, u32 sourceAddr, u32 sourceBytesConsumedAddr, u32 samplesAddr, u32 sampleBytesAddr) {
auto srcp = PSPPointer::Create(sourceAddr);
auto srcConsumed = PSPPointer::Create(sourceBytesConsumedAddr);
@@ -2245,7 +972,7 @@ static int sceAtracLowLevelDecode(int atracID, u32 sourceAddr, u32 sourceBytesCo
int bytesConsumed = 0;
int bytesWritten = 0;
- atrac->decoder_->Decode(srcp, atrac->bytesPerFrame_, &bytesConsumed, outp, &bytesWritten);
+ atrac->GetDecoder()->Decode(srcp, atrac->BytesPerFrame(), &bytesConsumed, outp, &bytesWritten);
*srcConsumed = bytesConsumed;
*outWritten = bytesWritten;
diff --git a/UWP/CoreUWP/CoreUWP.vcxproj b/UWP/CoreUWP/CoreUWP.vcxproj
index fde72ab21e..372cf7e0ee 100644
--- a/UWP/CoreUWP/CoreUWP.vcxproj
+++ b/UWP/CoreUWP/CoreUWP.vcxproj
@@ -174,6 +174,7 @@
+
@@ -425,6 +426,7 @@
+
diff --git a/UWP/CoreUWP/CoreUWP.vcxproj.filters b/UWP/CoreUWP/CoreUWP.vcxproj.filters
index 7e6404054a..1d6c5090dc 100644
--- a/UWP/CoreUWP/CoreUWP.vcxproj.filters
+++ b/UWP/CoreUWP/CoreUWP.vcxproj.filters
@@ -1199,6 +1199,9 @@
Util
+
+ HLE
+
@@ -1882,6 +1885,9 @@
Util
+
+ HLE
+
diff --git a/android/jni/Android.mk b/android/jni/Android.mk
index 639a700c50..bd1939220d 100644
--- a/android/jni/Android.mk
+++ b/android/jni/Android.mk
@@ -614,6 +614,7 @@ EXEC_AND_LIB_FILES := \
$(SRC)/Core/HLE/Plugins.cpp \
$(SRC)/Core/HLE/sceAdler.cpp \
$(SRC)/Core/HLE/sceAtrac.cpp \
+ $(SRC)/Core/HLE/AtracCtx.cpp \
$(SRC)/Core/HLE/__sceAudio.cpp.arm \
$(SRC)/Core/HLE/sceAudio.cpp.arm \
$(SRC)/Core/HLE/sceAudiocodec.cpp.arm \
diff --git a/libretro/Makefile.common b/libretro/Makefile.common
index b07317c493..3aded79f28 100644
--- a/libretro/Makefile.common
+++ b/libretro/Makefile.common
@@ -651,6 +651,7 @@ SOURCES_CXX += \
$(COREDIR)/HLE/ReplaceTables.cpp \
$(COREDIR)/HLE/HLEHelperThread.cpp \
$(COREDIR)/HLE/HLETables.cpp \
+ $(COREDIR)/HLE/AtracCtx.cpp \
$(COREDIR)/HLE/sceAdler.cpp \
$(COREDIR)/HLE/sceAtrac.cpp \
$(COREDIR)/HLE/sceAudio.cpp \