mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
Some new findings, loop work
This commit is contained in:
parent
4d7a8b8233
commit
b99348383c
8 changed files with 92 additions and 15 deletions
|
@ -128,7 +128,8 @@ struct Track {
|
|||
}
|
||||
|
||||
inline int FirstOffsetExtra() const {
|
||||
return codecType == PSP_MODE_AT_3_PLUS ? 368 : 69;
|
||||
// These first samples are skipped, after first possibly skipping 0-2 full frames, it seems.
|
||||
return codecType == PSP_MODE_AT_3_PLUS ? 0x170 : 0x45;
|
||||
}
|
||||
|
||||
// Includes the extra offset. See firstSampleOffset comment above.
|
||||
|
|
|
@ -91,6 +91,7 @@ int Atrac2::RemainingFrames() const {
|
|||
}
|
||||
|
||||
// Since we're streaming, the remaining frames are what's valid in the buffer.
|
||||
// NOTE: If read wraps around the buffer, we need to decrement by one, it seems.
|
||||
return info.streamDataByte / info.sampleSize;
|
||||
}
|
||||
|
||||
|
@ -329,14 +330,69 @@ void Atrac2::GetStreamDataInfo(u32 *writePtr, u32 *bytesToRead, u32 *readFileOff
|
|||
// from just logs of variables wasn't all that easy... Initially I had it more complicated, but boiled it
|
||||
// all down to fairly simple logic. And then boiled it down further and fixed bugs.
|
||||
//
|
||||
// TODO: Take care of loop points.
|
||||
// And then had to make it complicated again to support looping... Sigh.
|
||||
|
||||
const int fileOffset = (int)info.curFileOff + (int)info.streamDataByte;
|
||||
const int bytesLeftInFile = (int)info.fileDataEnd - fileOffset;
|
||||
auto LoopPointToFileOffset = [&](int loop, bool roundUp) -> int {
|
||||
if (roundUp) {
|
||||
loop += info.sampleSize - 1;
|
||||
}
|
||||
return info.dataOff + (loop / track_.SamplesPerFrame()) * info.sampleSize;
|
||||
};
|
||||
|
||||
|
||||
_dbg_assert_(bytesLeftInFile >= 0);
|
||||
// These are mutable so the looping code can make another decision.
|
||||
int fileOffset = -1;
|
||||
int bytesLeftToRead = -1;
|
||||
|
||||
if (bytesLeftInFile == 0) {
|
||||
bool looped = info.loopNum != 0;
|
||||
if (looped) {
|
||||
// We rewind back to one frame before the loopstart.
|
||||
// Not sure about this math but it works for the test so can fix it later!
|
||||
const int loopStartFileOffset = LoopPointToFileOffset(info.loopStart, false) - info.sampleSize;
|
||||
const int loopEndFileOffset = LoopPointToFileOffset(info.firstValidSample + info.loopEnd, true);
|
||||
|
||||
// We also need to loop the streaming.
|
||||
switch (info.state) {
|
||||
case ATRAC_STATUS_STREAMED_LOOP_FROM_END:
|
||||
case ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER:
|
||||
{
|
||||
// Let's ignore the tail for now, and just get some logic to work.
|
||||
// The playback logic has to match this.
|
||||
fileOffset = (int)info.curFileOff + (int)info.streamDataByte;
|
||||
int streamFirstPart = 0;
|
||||
if (fileOffset == loopEndFileOffset) {
|
||||
INFO_LOG(Log::ME, "Atrac: Hit the stream buffer wrap point (buffering)");
|
||||
}
|
||||
if (fileOffset >= loopEndFileOffset) {
|
||||
// We have the data up to the loop already, so wrap the read around.
|
||||
int secondPart = fileOffset - loopEndFileOffset;
|
||||
int firstPart = info.streamDataByte - secondPart;
|
||||
_dbg_assert_(firstPart >= 0);
|
||||
_dbg_assert_(secondPart >= 0);
|
||||
bytesLeftToRead = loopEndFileOffset - secondPart;
|
||||
fileOffset = loopStartFileOffset + secondPart;
|
||||
} else {
|
||||
bytesLeftToRead = loopEndFileOffset - fileOffset;
|
||||
}
|
||||
_dbg_assert_(bytesLeftToRead >= 0);
|
||||
break;
|
||||
}
|
||||
case ATRAC_STATUS_ALL_DATA_LOADED:
|
||||
case ATRAC_STATUS_HALFWAY_BUFFER:
|
||||
default:
|
||||
// TODO
|
||||
_dbg_assert_(false);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Non-looped. Basic math.
|
||||
fileOffset = (int)info.curFileOff + (int)info.streamDataByte;
|
||||
bytesLeftToRead = (int)info.fileDataEnd - fileOffset;
|
||||
_dbg_assert_(bytesLeftToRead >= 0);
|
||||
}
|
||||
|
||||
_dbg_assert_(bytesLeftToRead >= 0 && fileOffset >= 0);
|
||||
if (bytesLeftToRead == 0) {
|
||||
// We've got all the data up to the end buffered, no more streaming is needed.
|
||||
// Signalling this by setting everything to default.
|
||||
*writePtr = info.buffer;
|
||||
|
@ -352,7 +408,7 @@ void Atrac2::GetStreamDataInfo(u32 *writePtr, u32 *bytesToRead, u32 *readFileOff
|
|||
// There's space left without wrapping.
|
||||
const int writeOffset = info.streamOff + info.streamDataByte;
|
||||
*writePtr = info.buffer + writeOffset;
|
||||
*bytesToRead = std::min(distanceToEnd - (int)info.streamDataByte, bytesLeftInFile);
|
||||
*bytesToRead = std::min(distanceToEnd - (int)info.streamDataByte, bytesLeftToRead);
|
||||
*readFileOffset = *bytesToRead == 0 ? 0 : fileOffset; // Seems this behavior (which isn't important) only happens on this path?
|
||||
} else {
|
||||
// Wraps around.
|
||||
|
@ -360,7 +416,7 @@ void Atrac2::GetStreamDataInfo(u32 *writePtr, u32 *bytesToRead, u32 *readFileOff
|
|||
const int secondPart = info.streamDataByte - firstPart;
|
||||
const int spaceLeft = info.streamOff - secondPart;
|
||||
*writePtr = info.buffer + secondPart;
|
||||
*bytesToRead = std::min(spaceLeft, bytesLeftInFile);
|
||||
*bytesToRead = std::min(spaceLeft, bytesLeftToRead);
|
||||
*readFileOffset = fileOffset;
|
||||
}
|
||||
break;
|
||||
|
@ -470,18 +526,29 @@ u32 Atrac2::DecodeData(u8 *outbuf, u32 outbufPtr, u32 *SamplesNum, u32 *finish,
|
|||
info.curFileOff += info.sampleSize;
|
||||
info.decodePos += decodePosAdvance;
|
||||
} else {
|
||||
// Handle looping differently depending on state.
|
||||
if (info.state == ATRAC_STATUS_ALL_DATA_LOADED) {
|
||||
// Handle looping differently depending on state (actually seems pretty similar...)
|
||||
switch (info.state) {
|
||||
case ATRAC_STATUS_ALL_DATA_LOADED:
|
||||
case ATRAC_STATUS_STREAMED_LOOP_FROM_END:
|
||||
// Curiously this is exactly the same.
|
||||
info.decodePos = info.loopStart;
|
||||
info.curFileOff = info.dataOff + (info.loopStart / track_.SamplesPerFrame()) * info.sampleSize; // for some reason??? Not the same as the first frame after SetData.
|
||||
info.numFrame = 1; // not sure what this is about, but in testing it's there.
|
||||
if (info.loopNum > 0) {
|
||||
info.loopNum--;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
case ATRAC_STATUS_LOW_LEVEL:
|
||||
case ATRAC_STATUS_FOR_SCESAS:
|
||||
case ATRAC_STATUS_STREAMED_WITHOUT_LOOP:
|
||||
_dbg_assert_msg_or_log_(false, Log::ME, "Hit a loop end, but is in state %s. This shouldn't happen.", AtracStatusToString(info.state));
|
||||
break;
|
||||
default:
|
||||
ERROR_LOG(Log::ME, "Looping in state %s not yet supported", AtracStatusToString(info.state));
|
||||
// Fallback to just ending.
|
||||
info.curFileOff += info.sampleSize;
|
||||
info.decodePos += decodePosAdvance;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -393,6 +393,7 @@ enum PSPErrorCode : u32 {
|
|||
SCE_ERROR_ATRAC_SIZE_TOO_SMALL = 0x80630011,
|
||||
SCE_ERROR_ATRAC_SECOND_BUFFER_NEEDED = 0x80630012,
|
||||
SCE_ERROR_ATRAC_INCORRECT_READ_SIZE = 0x80630013,
|
||||
SCE_ERROR_ATRAC_BAD_ALIGNMENT = 0x80630014,
|
||||
SCE_ERROR_ATRAC_BAD_SAMPLE = 0x80630015,
|
||||
SCE_ERROR_ATRAC_BAD_FIRST_RESET_SIZE = 0x80630016,
|
||||
SCE_ERROR_ATRAC_BAD_SECOND_RESET_SIZE = 0x80630017,
|
||||
|
|
|
@ -281,6 +281,10 @@ static u32 sceAtracDecodeData(int atracID, u32 outAddr, u32 numSamplesAddr, u32
|
|||
return hleLogError(Log::ME, err);
|
||||
}
|
||||
|
||||
if (outAddr & 1) {
|
||||
return hleLogError(Log::ME, SCE_ERROR_ATRAC_BAD_ALIGNMENT);
|
||||
}
|
||||
|
||||
u32 numSamples = 0;
|
||||
u32 finish = 0;
|
||||
int remains = 0;
|
||||
|
@ -914,6 +918,8 @@ static int sceAtracSetAA3DataAndGetID(u32 buffer, u32 bufferSize, u32 fileSize,
|
|||
return hleDelayResult(hleLogDebug(Log::ME, atracID), "atrac set aa3 data", 100);
|
||||
}
|
||||
|
||||
// TODO: Should see if these are stored contiguously in memory somewhere, or if there really are
|
||||
// individual allocations being used.
|
||||
static u32 _sceAtracGetContextAddress(int atracID) {
|
||||
AtracBase *atrac = getAtrac(atracID);
|
||||
if (!atrac) {
|
||||
|
|
|
@ -70,9 +70,9 @@ 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 numFrame; // This is 1 for a single frame when a loop is triggered, otherwise seems to stay at 0. Likely mis-named.
|
||||
u8 framesToSkip; // 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 unk22; // UNKNOWN: Always zero?
|
||||
u8 curBuffer; // Current buffer (1 == second, 2 == done?) Previously unk
|
||||
u8 numChan; // Number of audio channels, usually 2 but 1 is possible.
|
||||
u16 sampleSize; // Size in bytes of an encoded audio frame.
|
||||
u16 codec; // Codec. 0x1000 is Atrac3+, 0x1001 is Atrac3. See the PSP_CODEC_ enum (only these two are supported).
|
||||
|
@ -82,7 +82,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 unk52; // UNKNOWN: Always zero?
|
||||
u32 secondStreamOff; // Secondary streamoff? previously unk.
|
||||
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.
|
||||
|
|
|
@ -32,7 +32,7 @@ struct SceAudiocodecCodec {
|
|||
s32 srcFrameSize; // 1c
|
||||
u32 outBuf; // 20 // This is where the decoded data is written.
|
||||
s32 dstBytesWritten; // 24
|
||||
s8 unk40; // 28 format or looping related
|
||||
s8 unk40; // 28 format or looping related // Probably, from here on out is a union with different fields for different codecs.
|
||||
s8 unk41; // 29 format or looping related
|
||||
s16 unk42; // 2a
|
||||
s8 unk44;
|
||||
|
|
|
@ -1425,6 +1425,7 @@ const char *KernelErrorToString(u32 err) {
|
|||
case SCE_ERROR_ATRAC_IS_FOR_SCESAS: return "SCE_ERROR_ATRAC_IS_FOR_SCESAS";
|
||||
case SCE_ERROR_ATRAC_AA3_INVALID_DATA: return "SCE_ERROR_ATRAC_AA3_INVALID_DATA";
|
||||
case SCE_ERROR_ATRAC_AA3_SIZE_TOO_SMALL: return "SCE_ERROR_ATRAC_AA3_SIZE_TOO_SMALL";
|
||||
case SCE_ERROR_ATRAC_BAD_ALIGNMENT: return "SCE_ERROR_ATRAC_BAD_ALIGNMENT";
|
||||
|
||||
default:
|
||||
return nullptr;
|
||||
|
|
1
test.py
1
test.py
|
@ -382,6 +382,7 @@ tests_next = [
|
|||
"cpu/vfpu/vector",
|
||||
"cpu/vfpu/vregs",
|
||||
"audio/atrac/replay",
|
||||
"audio/atrac/stream",
|
||||
"audio/atrac/second/resetting",
|
||||
"audio/sceaudio/datalen",
|
||||
"audio/sceaudio/output",
|
||||
|
|
Loading…
Add table
Reference in a new issue