Reimplement a lot of logic, implement loop streaming properly

This commit is contained in:
Henrik Rydgård
2025-03-14 14:28:55 +01:00
parent b99348383c
commit bb436cda8f
9 changed files with 643 additions and 419 deletions
+5 -2
View File
@@ -342,7 +342,10 @@ int AnalyzeAtracTrack(u32 addr, u32 size, Track *track) {
if (at3fmt->fmtTag == AT3_MAGIC) {
// This is the offset to the jointStereo_ field.
track->jointStereo = Memory::Read_U32(addr + offset + 24);
track->jointStereo = Memory::Read_U16(addr + offset + 24);
// Then there are more fields here.
u16 unknown1_2 = Memory::Read_U16(addr + offset + 30);
}
if (chunkSize > 16) {
// Read and format extra bytes as hexadecimal
@@ -393,7 +396,7 @@ int AnalyzeAtracTrack(u32 addr, u32 size, Track *track) {
track->loopinfo[i].fraction = Memory::Read_U32(loopinfoAddr + 16);
track->loopinfo[i].playCount = Memory::Read_U32(loopinfoAddr + 20);
if (track->loopinfo[i].startSample >= track->loopinfo[i].endSample) {
if (i == 0 && track->loopinfo[i].startSample >= track->loopinfo[i].endSample) {
ERROR_LOG(Log::ME, "AnalyzeTrack: loop starts after it ends");
return SCE_ERROR_ATRAC_BAD_CODEC_PARAMS;
}
-3
View File
@@ -42,9 +42,6 @@ struct AtracResetBufferInfo {
#define PSP_MODE_AT_3_PLUS 0x00001000
#define PSP_MODE_AT_3 0x00001001
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;
+585 -398
View File
File diff suppressed because it is too large Load Diff
+7 -2
View File
@@ -56,8 +56,13 @@ public:
u32 GetInternalCodecError() const override;
private:
void InitContext(int offset, u32 bufferAddr, u32 readSize, u32 bufferSize, int sampleOffset);
void FastForwardDummyFrames(int discardedSamples);
u32 DecodeInternal(u32 outbufAddr, u32 *SamplesNum, u32 *finish);
void GetResetBufferInfoInternal(AtracResetBufferInfo *bufferInfo, int sample);
u32 ResetPlayPositionInternal(int seekPos, int bytesWrittenFirstBuf, int bytesWrittenSecondBuf);
u32 InitContext(u32 bufferAddr, u32 readSize, u32 bufferSize);
u32 SkipFrames();
void WrapLastPacket();
void SeekToSample(int sample);
+14 -7
View File
@@ -38,9 +38,10 @@
#include "Core/HLE/AtracCtx2.h"
#include "Core/System.h"
// (Old) notes about sceAtrac buffer management
// Notes about sceAtrac buffer management
//
// sceAtrac decodes from a buffer the game fills, where this buffer has a status, one of:
//
// * Not yet initialized (state NO_DATA = 1)
// * The entire size of the audio data, and filled with audio data (state ALL_DATA_LOADED = 2)
// * The entire size, but only partially filled so far (state HALFWAY_BUFFER = 3)
@@ -50,9 +51,11 @@
// * Not managed, decoding using "low level" manual looping etc. (LOW_LEVEL = 8)
// * Not managed, reserved externally - possibly by sceSas - through low level (RESERVED = 16)
//
// A game will call sceAtracAddStreamData after filling data, and where it filled it was given by
// either sceAtracGetStreamDataInfo when continuing to move forwards in the stream of audio data,
// or sceAtracGetBufferInfoForResetting when seeking to a specific location in the audio stream.
// When streaming (modes 3-6), a game will call sceAtracGetStreamDataInfo to figure out what data
// to read and where to place it, and after doing that it'll call sceAtracAddStreamData with the amount
// of data it actually read. This will move the various pointers forward.
// Similarly, for a game to seek, it'll call sceAtracGetBufferInfoForResetting with a sample offset,
// and read that data into the buffer.
//
// State 6 indicates a second buffer is needed. This buffer is used to manage looping correctly.
// To determine how to fill it, the game will call sceAtracGetSecondBufferInfo, then after filling
@@ -60,10 +63,11 @@
//
// The second buffer will just contain the data for the end of loop. The "first" buffer may manage
// only the looped portion, or some of the part after the loop (depending on second buf size.)
//
// TODO: What games use this?
//
// Most files will be in RIFF format. It's also possible to load in an OMA/AA3 format file, but
// ultimately this will share the same buffer - it's just offset a bit more.
// ultimately this works the same, just the loading process is a little different.
//
// Low level decoding doesn't use the buffer, and decodes only a single packet at a time.
//
@@ -302,7 +306,7 @@ static u32 sceAtracDecodeData(int atracID, u32 outAddr, u32 numSamplesAddr, u32
numSamplesAddr, numSamples,
finishFlagAddr, finish,
remainAddr, remains);
if (ret == 0) {
if (ret == 0 || ret == SCE_ERROR_ATRAC_API_FAIL) {
// decode data successfully, delay thread
return hleDelayResult(hleNoLog(ret), "atrac decode data", atracDecodeDelay);
}
@@ -337,6 +341,8 @@ static u32 sceAtracGetBufferInfoForResetting(int atracID, int sample, u32 buffer
}
*/
// Note: If we error here, it's because of the internal SkipFrames.
// Note again: We should probably delayresult if we skipframes..
int ret = atrac->GetResetBufferInfo(bufferInfo, sample);
return hleLogDebugOrError(Log::ME, ret);
}
@@ -586,7 +592,8 @@ static u32 sceAtracResetPlayPosition(int atracID, int sample, int bytesWrittenFi
return hleLogError(Log::ME, res);
}
}
return hleDelayResult(hleLogDebug(Log::ME, res), "reset play pos", 3000);
return hleDelayResult(res, "reset play pos", 3000);
}
static u32 sceAtracSetHalfwayBuffer(int atracID, u32 buffer, u32 readSize, u32 bufferSize) {
+19 -2
View File
@@ -29,6 +29,9 @@ void __AtracShutdown();
void __AtracNotifyLoadModule(int version, u32 crc, u32 bssAddr, int bssSize);
void __AtracNotifyUnloadModule();
constexpr u32 ATRAC3_MAX_SAMPLES = 0x400; // 1024
constexpr u32 ATRAC3PLUS_MAX_SAMPLES = 0x800; // 2048
// The "state" member of SceAtracIdInfo.
enum AtracStatus : u8 {
ATRAC_STATUS_NO_DATA = 1,
@@ -70,7 +73,7 @@ struct SceAtracIdInfo {
s32 loopStart; // Start of the loop (sample index)
s32 loopEnd; // End of the loop (sample index)
s32 firstValidSample; // Seems to be the number of skipped samples at the start. After SetID, decodePos will match this. Was previously misnamed 'samplesPerChan'.
u8 framesToSkip; // This is 1 for a single frame when a loop is triggered, otherwise seems to stay at 0. Likely mis-named.
u8 numSkipFrames; // This is 1 for a single frame when a loop is triggered, otherwise seems to stay at 0. Likely mis-named.
AtracStatus state; // State enum, see AtracStatus.
u8 curBuffer; // Current buffer (1 == second, 2 == done?) Previously unk
u8 numChan; // Number of audio channels, usually 2 but 1 is possible.
@@ -82,7 +85,7 @@ struct SceAtracIdInfo {
s32 loopNum; // Current loop counter. If 0, will not loop. -1 loops for ever, positive numbers get decremented on the loop end. So to play a song 3 times and then end, set this to 2.
s32 streamDataByte; // Number of bytes of queued/buffered/uploaded data. In full and half-way modes, this isn't decremented as you decode.
s32 streamOff; // Streaming modes only: The byte offset inside the RAM buffer where sceAtracDecodeData will read from next. ONLY points to even packet boundaries.
u32 secondStreamOff; // Secondary streamoff? previously unk.
s32 secondStreamOff; // A kind of stream position in the secondary buffer.
u32 buffer; // Address in RAM of the main buffer.
u32 secondBuffer; // Address in RAM of the second buffer, or 0 if not used.
u32 bufferByte; // Size in bytes of the main buffer.
@@ -91,6 +94,19 @@ struct SceAtracIdInfo {
// make sure the size is 128
u32 unk[13];
u32 atracID; // The atrac context number is stored here, for some reason.
// Simple helpers. Similar ones are on track_, but we shouldn't need track_ anymore when playing back.
int SamplesPerFrame() const {
return codec == 0x1000 ? ATRAC3PLUS_MAX_SAMPLES : ATRAC3_MAX_SAMPLES;
}
int SamplesFrameMask() const {
return SamplesPerFrame() - 1;
}
int SkipSamples() const {
// These first samples are skipped, after first possibly skipping 0-2 full frames, it seems.
return codec == 0x1000 ? 0x170 : 0x45;
}
};
// One of these structs is allocated for each Atrac context.
@@ -108,6 +124,7 @@ constexpr int PSP_MAX_ATRAC_IDS = 6;
class AtracBase;
// For debugger use ONLY.
const AtracBase *__AtracGetCtx(int i, u32 *type);
// External interface used by sceSas, see ATRAC_STATUS_FOR_SCESAS.
+8 -1
View File
@@ -3450,7 +3450,14 @@ bool __KernelCheckCallbacks() {
}
if (processed) {
return __KernelExecutePendingMipsCalls(__GetCurrentThread(), true);
PSPThread *thread = __GetCurrentThread();
if (thread) {
return __KernelExecutePendingMipsCalls(thread, true);
} else {
ERROR_LOG(Log::sceKernel, "No current thread in __KernelCheckCallbacks!");
// stumble along?
return true;
}
}
return false;
}
+4 -3
View File
@@ -84,7 +84,7 @@ static void NotifyLoadStatusAtrac(int state, u32 loadAddr, u32 totalSize) {
if (state == 1) {
// We try to imitate a recent version of the prx.
// Let's just give it a piece of the space.
constexpr int version = 0x103;
constexpr int version = 0x105; // latest.
constexpr int bssSize = 0x67C;
_dbg_assert_(bssSize <= totalSize);
__AtracNotifyLoadModule(version, 0, loadAddr, bssSize);
@@ -115,9 +115,10 @@ static const ModuleLoadInfo moduleLoadInfo[] = {
ModuleLoadInfo(0x202, 0x00000000, "usb_gps"),
ModuleLoadInfo(0x203, 0x00000000, "usb_unk_0x203"),
ModuleLoadInfo(0x2ff, 0x00000000, "unk_0x2ff"),
ModuleLoadInfo(0x300, 0x00000000, "av_avcodec", &NotifyLoadStatusAvcodec),
ModuleLoadInfo(0x300, 0x00000000, "av_avcodec", &NotifyLoadStatusAvcodec), // AudioCodec
ModuleLoadInfo(0x301, 0x00000000, "av_sascore"),
ModuleLoadInfo(0x302, 0x00004000, "av_atrac3plus", atrac3PlusModuleDeps, &NotifyLoadStatusAtrac), // TODO: 0x8000 is likely too large.
// TODO: We should put the Atrac contexts inside the allocated bss space. Also, current actual full size (including text, on latest fw) seems to be 0x45C0.
ModuleLoadInfo(0x302, 0x00004000, "av_atrac3plus", atrac3PlusModuleDeps, &NotifyLoadStatusAtrac),
ModuleLoadInfo(0x303, 0x0000c000, "av_mpegbase", mpegBaseModuleDeps),
ModuleLoadInfo(0x304, 0x00004000, "av_mp3"),
ModuleLoadInfo(0x305, 0x0000a300, "av_vaudio"),
+1 -1
View File
@@ -1050,7 +1050,7 @@ void DrawAudioDecodersView(ImConfig &cfg, ImControl &control) {
ImGui::Text("Stream: offset %d, streamDataBytes: %d", info.streamOff, info.streamDataByte);
}
// Display unknown vars.
ImGui::Text("numFrame: %d unk22: %d unk52: %d", info.numFrame, info.unk22, info.unk52);
ImGui::Text("numFrame: %d curBuffer: %d streamOff2: %d", info.numSkipFrames, info.curBuffer, info.secondStreamOff);
} else {
ImGui::Text("loop: %d", ctx->LoopNum());
}