Merge pull request #20156 from hrydgard/reimplement-sas-atrac

Reimplement Atrac-through-SAS
This commit is contained in:
Henrik Rydgård 2025-03-26 00:34:07 +01:00 committed by GitHub
commit 16f9851bbc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 394 additions and 207 deletions

View file

@ -571,21 +571,6 @@ int Atrac::AddStreamData(u32 bytesToAdd) {
return 0;
}
u32 Atrac::AddStreamDataSas(u32 bufPtr, u32 bytesToAdd) {
int addbytes = std::min(bytesToAdd, track_.fileSize - first_.fileoffset - track_.FirstOffsetExtra());
Memory::Memcpy(dataBuf_ + first_.fileoffset + track_.FirstOffsetExtra(), bufPtr, addbytes, "AtracAddStreamData");
first_.size += bytesToAdd;
if (first_.size >= track_.fileSize) {
first_.size = track_.fileSize;
if (bufferState_ == ATRAC_STATUS_HALFWAY_BUFFER)
bufferState_ = ATRAC_STATUS_ALL_DATA_LOADED;
}
first_.fileoffset += addbytes;
// refresh context_
WriteContextToPSPMem();
return 0;
}
u32 Atrac::GetNextSamples() {
if (currentSample_ >= track_.endSample) {
return 0;
@ -961,6 +946,25 @@ int Atrac::DecodeLowLevel(const u8 *srcData, int *bytesConsumed, s16 *dstData, i
return 0;
}
void Atrac::CheckForSas() {
SetOutputChannels(1);
}
int Atrac::EnqueueForSas(u32 bufPtr, u32 bytesToAdd) {
int addbytes = std::min(bytesToAdd, track_.fileSize - first_.fileoffset - track_.FirstOffsetExtra());
Memory::Memcpy(dataBuf_ + first_.fileoffset + track_.FirstOffsetExtra(), bufPtr, addbytes, "AtracAddStreamData");
first_.size += bytesToAdd;
if (first_.size >= track_.fileSize) {
first_.size = track_.fileSize;
if (bufferState_ == ATRAC_STATUS_HALFWAY_BUFFER)
bufferState_ = ATRAC_STATUS_ALL_DATA_LOADED;
}
first_.fileoffset += addbytes;
// refresh context_
WriteContextToPSPMem();
return 0;
}
void Atrac::DecodeForSas(s16 *dstData, int *bytesWritten, int *finish) {
// Hack, but works.
int samplesNum;

View file

@ -37,6 +37,20 @@ struct AtracResetBufferInfo {
AtracSingleResetBufferInfo second;
};
struct AtracSasStreamState {
u32 bufPtr[2]{};
u32 bufSize[2]{};
int streamOffset = 0;
int fileOffset = 0;
int curBuffer = 0;
bool isStreaming = false;
int CurPos() const {
int retval = fileOffset - bufSize[curBuffer] + streamOffset;
_dbg_assert_(retval >= 0);
return retval;
}
};
const int PSP_ATRAC_ALLDATA_IS_ON_MEMORY = -1;
const int PSP_ATRAC_NONLOOP_STREAM_DATA_IS_ON_MEMORY = -2;
@ -112,7 +126,6 @@ public:
virtual void GetStreamDataInfo(u32 *writePtr, u32 *writableBytes, u32 *readOffset) = 0;
virtual int AddStreamData(u32 bytesToAdd) = 0;
virtual u32 AddStreamDataSas(u32 bufPtr, u32 bytesToAdd) = 0;
virtual int ResetPlayPosition(int sample, int bytesWrittenFirstBuf, int bytesWrittenSecondBuf, bool *delay) = 0;
virtual int GetResetBufferInfo(AtracResetBufferInfo *bufferInfo, int sample, bool *delay) = 0;
virtual int SetData(const Track &track, u32 buffer, u32 readSize, u32 bufferSize, int outputChannels) = 0;
@ -121,10 +134,15 @@ public:
virtual int SetSecondBuffer(u32 secondBuffer, u32 secondBufferSize) = 0;
virtual u32 DecodeData(u8 *outbuf, u32 outbufPtr, int *SamplesNum, int *finish, int *remains) = 0;
virtual int DecodeLowLevel(const u8 *srcData, int *bytesConsumed, s16 *dstData, int *bytesWritten) = 0;
virtual void DecodeForSas(s16 *dstData, int *bytesWritten, int *finish) = 0;
virtual u32 GetNextSamples() = 0;
virtual void InitLowLevel(const Atrac3LowLevelParams &params, int codecType) = 0;
virtual void CheckForSas() = 0;
virtual int EnqueueForSas(u32 address, u32 ptr) = 0;
virtual void DecodeForSas(s16 *dstData, int *bytesWritten, int *finish) = 0;
virtual const AtracSasStreamState *StreamStateForSas() const { return nullptr; }
virtual int GetSoundSample(int *endSample, int *loopStartSample, int *loopEndSample) const = 0;
virtual int GetContextVersion() const = 0;
@ -205,7 +223,6 @@ public:
void GetStreamDataInfo(u32 *writePtr, u32 *writableBytes, u32 *readOffset) override;
// Notify the player that the user has written some new data.
int AddStreamData(u32 bytesToAdd) override;
u32 AddStreamDataSas(u32 bufPtr, u32 bytesToAdd) override;
int ResetPlayPosition(int sample, int bytesWrittenFirstBuf, int bytesWrittenSecondBuf, bool *delay) override;
int GetResetBufferInfo(AtracResetBufferInfo *bufferInfo, int sample, bool *delay) override;
int SetData(const Track &track, u32 buffer, u32 readSize, u32 bufferSize, int outputChannels) override;
@ -213,6 +230,9 @@ public:
int SetSecondBuffer(u32 secondBuffer, u32 secondBufferSize) override;
u32 DecodeData(u8 *outbuf, u32 outbufPtr, int *SamplesNum, int *finish, int *remains) override;
int DecodeLowLevel(const u8 *srcData, int *bytesConsumed, s16 *dstData, int *bytesWritten) override;
void CheckForSas() override;
int EnqueueForSas(u32 address, u32 ptr) override;
void DecodeForSas(s16 *dstData, int *bytesWritten, int *finish) override;
// Returns how many samples the next DecodeData will write.

View file

@ -175,15 +175,16 @@ Atrac2::Atrac2(u32 contextAddr, int codecType) {
info.state = ATRAC_STATUS_NO_DATA;
info.curBuffer = 0;
sasReadOffset_ = 0;
sasBasePtr_ = 0;
sas_.streamOffset = 0;
sas_.bufPtr[0] = 0;
sas_.bufPtr[1] = 0;
} else {
// We're loading state, we'll restore the context in DoState.
}
}
void Atrac2::DoState(PointerWrap &p) {
auto s = p.Section("Atrac2", 1, 2);
auto s = p.Section("Atrac2", 1, 3);
if (!s)
return;
@ -191,10 +192,20 @@ void Atrac2::DoState(PointerWrap &p) {
// The only thing we need to save now is the outputChannels_ and the context pointer. And technically, not even that since
// it can be computed. Still, for future proofing, let's save it.
Do(p, context_);
// Actually, now we also need to save sas state. I guess this could also be saved on the Sas side, but this is easier.
if (s >= 2) {
Do(p, sasReadOffset_);
Do(p, sasBasePtr_);
Do(p, sas_.streamOffset);
Do(p, sas_.bufPtr[0]);
}
// Added support for streaming sas audio, need some more context state.
if (s >= 3) {
Do(p, sas_.bufPtr[1]);
Do(p, sas_.bufSize[0]);
Do(p, sas_.bufSize[1]);
Do(p, sas_.isStreaming);
Do(p, sas_.curBuffer);
Do(p, sas_.fileOffset);
}
const SceAtracIdInfo &info = context_->info;
@ -497,20 +508,6 @@ int Atrac2::AddStreamData(u32 bytesToAdd) {
return 0;
}
u32 Atrac2::AddStreamDataSas(u32 bufPtr, u32 bytesToAdd) {
SceAtracIdInfo &info = context_->info;
// Internal API, seems like a combination of GetStreamDataInfo and AddStreamData, for use when
// an Atrac context is bound to an sceSas channel.
// Sol Trigger is the only game I know that uses this.
_dbg_assert_(false);
u8 *dest = Memory::GetPointerWrite(sasBasePtr_ + sasReadOffset_);
memcpy(dest, Memory::GetPointer(bufPtr), bytesToAdd);
info.buffer += bytesToAdd;
info.streamDataByte += bytesToAdd;
return 0;
}
static int ComputeLoopedStreamWritableBytes(const SceAtracIdInfo &info, const int loopStartFileOffset, const u32 loopEndFileOffset) {
const u32 writeOffset = info.curFileOff + info.streamDataByte;
if (writeOffset >= loopEndFileOffset) {
@ -681,6 +678,10 @@ u32 Atrac2::DecodeInternal(u32 outbufAddr, int *SamplesNum, int *finish) {
return SCE_ERROR_ATRAC_BUFFER_IS_EMPTY;
}
if (info.state == ATRAC_STATUS_FOR_SCESAS) {
_dbg_assert_(false);
}
u32 streamOff;
u32 bufferPtr;
if (!AtracStatusIsStreaming(info.state)) {
@ -1039,27 +1040,132 @@ int Atrac2::DecodeLowLevel(const u8 *srcData, int *bytesConsumed, s16 *dstData,
return 0;
}
void Atrac2::DecodeForSas(s16 *dstData, int *bytesWritten, int *finish) {
void Atrac2::CheckForSas() {
SceAtracIdInfo &info = context_->info;
if (info.buffer) {
// Adopt it then zero it.
sasBasePtr_ = info.buffer;
sasReadOffset_ = 0;
info.buffer = 0;
if (info.numChan != 1) {
WARN_LOG(Log::ME, "Caller forgot to set channels to 1");
}
const u8 *srcData = Memory::GetPointer(sasBasePtr_ + sasReadOffset_);
int outSamples = 0;
int bytesConsumed = 0;
decoder_->Decode(srcData, info.sampleSize, &bytesConsumed, 1, dstData, bytesWritten);
sasReadOffset_ += bytesConsumed;
if (sasReadOffset_ + info.dataOff >= info.fileDataEnd) {
*finish = 1;
if (info.state != 0x10) {
WARN_LOG(Log::ME, "Caller forgot to set state to 0x10");
}
sas_.isStreaming = info.fileDataEnd > info.bufferByte;
if (sas_.isStreaming) {
INFO_LOG(Log::ME, "SasAtrac stream mode");
} else {
*finish = 0;
INFO_LOG(Log::ME, "SasAtrac non-streaming mode");
}
}
int Atrac2::EnqueueForSas(u32 address, u32 ptr) {
SceAtracIdInfo &info = context_->info;
// Set the new buffer up to be adopted by the next call to Decode that needs more data.
// Note: Can't call this if the decoder isn't asking for another buffer to be queued.
if (info.secondBuffer != 0xFFFFFFFF) {
return SCE_SAS_ERROR_ATRAC3_ALREADY_QUEUED;
}
if (address == 0 && ptr == 0) {
WARN_LOG(Log::ME, "Caller tries to send us a zero buffer. Something went wrong.");
}
DEBUG_LOG(Log::ME, "EnqueueForSas: Second buffer updated to %08x, sz: %08x", address, ptr);
info.secondBuffer = address;
info.secondBufferByte = ptr;
return 0;
}
// Completely different streaming setup!
void Atrac2::DecodeForSas(s16 *dstData, int *bytesWritten, int *finish) {
SceAtracIdInfo &info = context_->info;
*bytesWritten = 0;
// First frame handling. Not sure if accurate. Set up the initial buffer as the current streaming buffer.
// Also works for the non-streaming case.
if (info.buffer) {
sas_.curBuffer = 0;
sas_.bufPtr[0] = info.buffer;
sas_.bufSize[0] = info.bufferByte - info.streamOff; // also equals info.streamDataByte
sas_.streamOffset = 0;
sas_.fileOffset = info.bufferByte; // Possibly should just set it to info.curFileOff
info.buffer = 0; // yes, this happens.
}
u8 assembly[1000];
// Keep decoding from the current buffer until it runs out.
if (sas_.streamOffset + info.sampleSize <= sas_.bufSize[sas_.curBuffer]) {
// Just decode.
const u8 *srcData = Memory::GetPointer(sas_.bufPtr[sas_.curBuffer] + sas_.streamOffset);
int bytesConsumed = 0;
bool decodeResult = decoder_->Decode(srcData, info.sampleSize, &bytesConsumed, 1, dstData, bytesWritten);
if (!decodeResult) {
ERROR_LOG(Log::ME, "SAS failed to decode regular packet");
}
sas_.streamOffset += bytesConsumed;
} else if (sas_.isStreaming) {
// TODO: Do we need special handling for the first buffer, since SetData will wrap around that packet? I think yes!
DEBUG_LOG(Log::ME, "Streaming atrac through sas, and hit the end of buffer %d", sas_.curBuffer);
// Compute the part sizes using the current size.
int part1Size = sas_.bufSize[sas_.curBuffer] - sas_.streamOffset;
int part2Size = info.sampleSize - part1Size;
_dbg_assert_(part1Size >= 0);
if (part1Size >= 0) {
// Grab the partial packet, before we switch over to the other buffer.
Memory::Memcpy(assembly, sas_.bufPtr[sas_.curBuffer] + sas_.streamOffset, part1Size);
}
// Check if we hit the end.
if (sas_.fileOffset >= info.fileDataEnd) {
DEBUG_LOG(Log::ME, "Streaming and hit the file end.");
*bytesWritten = 0;
*finish = 1;
return;
}
// Check that a new buffer actually exists
if (info.secondBuffer == sas_.bufPtr[sas_.curBuffer]) {
ERROR_LOG(Log::ME, "Can't enqueue the same buffer twice in a row!");
*bytesWritten = 0;
*finish = 1;
return;
}
if ((int)info.secondBuffer < 0) {
ERROR_LOG(Log::ME, "AtracSas streaming ran out of data, no secondbuffer pending");
*bytesWritten = 0;
*finish = 1;
return;
}
// Switch to the other buffer.
sas_.curBuffer ^= 1;
sas_.bufPtr[sas_.curBuffer] = info.secondBuffer;
sas_.bufSize[sas_.curBuffer] = info.secondBufferByte;
sas_.fileOffset += info.secondBufferByte;
sas_.streamOffset = part2Size;
// If we'll reach the end during this buffer, set second buffer to 0, signaling that we don't need more data.
if (sas_.fileOffset >= info.fileDataEnd) {
// We've reached the end.
info.secondBuffer = 0;
DEBUG_LOG(Log::ME, "%08x >= %08x: Reached the end.", sas_.fileOffset, info.fileDataEnd);
} else {
// Signal to the caller that we accept a new next buffer.
info.secondBuffer = 0xFFFFFFFF;
}
DEBUG_LOG(Log::ME, "Switching over to buffer %d, updating buffer to %08x, sz: %08x. %s", sas_.curBuffer, info.secondBuffer, info.secondBufferByte, info.secondBuffer == 0xFFFFFFFF ? "Signalling for more data." : "");
// Copy the second half (or if part1Size == 0, the whole packet) to the assembly buffer.
Memory::Memcpy(assembly + part1Size, sas_.bufPtr[sas_.curBuffer], part2Size);
// Decode the packet from the assembly, whether it's was assembled from two or one.
const u8 *srcData = assembly;
int bytesConsumed = 0;
bool decodeResult = decoder_->Decode(srcData, info.sampleSize, &bytesConsumed, 1, dstData, bytesWritten);
if (!decodeResult) {
ERROR_LOG(Log::ME, "SAS failed to decode assembled packet");
}
}
}

View file

@ -33,7 +33,6 @@ public:
void GetStreamDataInfo(u32 *writePtr, u32 *writableBytes, u32 *readOffset) override;
int GetSecondBufferInfo(u32 *fileOffset, u32 *desiredSize) override;
int AddStreamData(u32 bytesToAdd) override;
u32 AddStreamDataSas(u32 bufPtr, u32 bytesToAdd) override;
int ResetPlayPosition(int sample, int bytesWrittenFirstBuf, int bytesWrittenSecondBuf, bool *delay) override;
int GetResetBufferInfo(AtracResetBufferInfo *bufferInfo, int sample, bool *delay) override;
int SetData(const Track &track, u32 buffer, u32 readSize, u32 bufferSize, int outputChannels) override;
@ -42,7 +41,11 @@ public:
u32 DecodeData(u8 *outbuf, u32 outbufPtr, int *SamplesNum, int *finish, int *remains) override;
int DecodeLowLevel(const u8 *srcData, int *bytesConsumed, s16 *dstData, int *bytesWritten) override;
void CheckForSas() override;
int EnqueueForSas(u32 address, u32 ptr) override;
void DecodeForSas(s16 *dstData, int *bytesWritten, int *finish) override;
const AtracSasStreamState *StreamStateForSas() const { return context_->info.state == 0x10 ? &sas_ : nullptr; }
u32 GetNextSamples() override;
@ -55,7 +58,6 @@ public:
void NotifyGetContextAddress() override {}
int GetContextVersion() const override { return 2; }
u32 GetInternalCodecError() const override;
private:
@ -73,6 +75,5 @@ private:
// This is hidden state inside sceSas, really. Not visible in the context.
// But it doesn't really matter whether it's here or there.
u32 sasBasePtr_ = 0;
int sasReadOffset_ = 0;
AtracSasStreamState sas_;
};

View file

@ -467,4 +467,29 @@ enum PSPErrorCode : u32 {
SCE_SSL_ERROR_ALREADY_INIT = 0x80435020,
SCE_SSL_ERROR_OUT_OF_MEMORY = 0x80435022,
SCE_SSL_ERROR_INVALID_PARAMETER = 0x804351FE,
SCE_SAS_ERROR_INVALID_GRAIN = 0x80420001,
SCE_SAS_ERROR_INVALID_MAX_VOICES = 0x80420002,
SCE_SAS_ERROR_INVALID_OUTPUT_MODE = 0x80420003,
SCE_SAS_ERROR_INVALID_SAMPLE_RATE = 0x80420004,
SCE_SAS_ERROR_BAD_ADDRESS = 0x80420005,
SCE_SAS_ERROR_INVALID_VOICE = 0x80420010,
SCE_SAS_ERROR_INVALID_NOISE_FREQ = 0x80420011,
SCE_SAS_ERROR_INVALID_PITCH = 0x80420012,
SCE_SAS_ERROR_INVALID_ADSR_CURVE_MODE = 0x80420013,
SCE_SAS_ERROR_INVALID_PARAMETER = 0x80420014,
SCE_SAS_ERROR_INVALID_LOOP_POS = 0x80420015,
SCE_SAS_ERROR_VOICE_PAUSED = 0x80420016,
SCE_SAS_ERROR_INVALID_VOLUME = 0x80420018,
SCE_SAS_ERROR_INVALID_ADSR_RATE = 0x80420019,
SCE_SAS_ERROR_INVALID_PCM_SIZE = 0x8042001A,
SCE_SAS_ERROR_REV_INVALID_TYPE = 0x80420020,
SCE_SAS_ERROR_REV_INVALID_FEEDBACK = 0x80420021,
SCE_SAS_ERROR_REV_INVALID_DELAY = 0x80420022,
SCE_SAS_ERROR_REV_INVALID_VOLUME = 0x80420023,
SCE_SAS_ERROR_BUSY = 0x80420030,
SCE_SAS_ERROR_ATRAC3_ALREADY_SET = 0x80420040,
SCE_SAS_ERROR_ATRAC3_NOT_SET = 0x80420041,
SCE_SAS_ERROR_ATRAC3_ALREADY_QUEUED = 0x80420042,
SCE_SAS_ERROR_NOT_INIT = 0x80420100,
};

View file

@ -1096,13 +1096,14 @@ static int sceAtracLowLevelDecode(int atracID, u32 sourceAddr, u32 sourceBytesCo
// This is __sceSasConcatenateATRAC3.
// The context's fileOff is incremented by the caller, but we need to bump the other pointers.
// The context's fileOff is incremented by the caller. No other pointers are bumped, except
// internally in sceSas on the real hardware.
u32 AtracSasAddStreamData(int atracID, u32 bufPtr, u32 bytesToAdd) {
AtracBase *atrac = getAtrac(atracID);
if (!atrac) {
WARN_LOG(Log::ME, "bad atrac ID");
}
return atrac->AddStreamDataSas(bufPtr, bytesToAdd);
return atrac->EnqueueForSas(bufPtr, bytesToAdd);
}
void AtracSasDecodeData(int atracID, u8* outbuf, int *SamplesNum, int *finish) {
@ -1110,16 +1111,20 @@ void AtracSasDecodeData(int atracID, u8* outbuf, int *SamplesNum, int *finish) {
if (!atrac) {
WARN_LOG(Log::ME, "bad atrac ID");
}
// NOTE: If streaming, this will write 0xFFFFFFFF to secondBuffer in the context when close to running
// out of data. It'll then expect __sceSasConcatenateATRAC3 to concatenate a new buffer, which cannot
// be at the same address as the old one. I think you need to double buffer, and we in fact continouously
// read out of these.
atrac->DecodeForSas((s16 *)outbuf, SamplesNum, finish);
}
// Ugly hack, but needed to support both old and new contexts.
int AtracSasBindContextAndGetID(u32 contextAddr) {
// Ugly hack, but needed to support both old and new contexts.
int atracID = (int)Memory::Read_U32(contextAddr + 0xfc);
if (atracID < PSP_MAX_ATRAC_IDS && atracContexts[atracID] && atracContexts[atracID]->GetContextVersion() == 1) {
// We can assume the old atracID hack was used, and atracID is valid.
} else {
// Let's just loop around the contexts and find it.
// Let's just loop around the contexts and find it by address.
atracID = -1;
for (int i = 0; i < PSP_MAX_ATRAC_IDS; i++) {
if (!atracContexts[i]) {
@ -1133,9 +1138,8 @@ int AtracSasBindContextAndGetID(u32 contextAddr) {
_dbg_assert_(atracID != -1);
}
// Not actually a hack, this happens.
AtracBase *atrac = getAtrac(atracID);
atrac->SetOutputChannels(1);
atrac->CheckForSas();
return atracID;
}

View file

@ -1467,6 +1467,30 @@ const char *KernelErrorToString(u32 err) {
case SCE_SSL_ERROR_OUT_OF_MEMORY: return "SCE_SSL_ERROR_OUT_OF_MEMORY";
case SCE_SSL_ERROR_INVALID_PARAMETER: return "SCE_SSL_ERROR_INVALID_PARAMETER";
case SCE_SAS_ERROR_INVALID_GRAIN: return "SCE_SAS_ERROR_INVALID_GRAIN";
case SCE_SAS_ERROR_INVALID_MAX_VOICES: return "SCE_SAS_ERROR_INVALID_MAX_VOICES";
case SCE_SAS_ERROR_INVALID_OUTPUT_MODE: return "SCE_SAS_ERROR_INVALID_OUTPUT_MODE";
case SCE_SAS_ERROR_INVALID_SAMPLE_RATE: return "SCE_SAS_ERROR_INVALID_SAMPLE_RATE";
case SCE_SAS_ERROR_BAD_ADDRESS: return "SCE_SAS_ERROR_BAD_ADDRESS";
case SCE_SAS_ERROR_INVALID_VOICE: return "SCE_SAS_ERROR_INVALID_VOICE";
case SCE_SAS_ERROR_INVALID_NOISE_FREQ: return "SCE_SAS_ERROR_INVALID_NOISE_FREQ";
case SCE_SAS_ERROR_INVALID_PITCH: return "SCE_SAS_ERROR_INVALID_PITCH";
case SCE_SAS_ERROR_INVALID_ADSR_CURVE_MODE: return "SCE_SAS_ERROR_INVALID_ADSR_CURVE_MODE";
case SCE_SAS_ERROR_INVALID_PARAMETER: return "SCE_SAS_ERROR_INVALID_PARAMETER";
case SCE_SAS_ERROR_INVALID_LOOP_POS: return "SCE_SAS_ERROR_INVALID_LOOP_POS";
case SCE_SAS_ERROR_VOICE_PAUSED: return "SCE_SAS_ERROR_VOICE_PAUSED";
case SCE_SAS_ERROR_INVALID_VOLUME: return "SCE_SAS_ERROR_INVALID_VOLUME";
case SCE_SAS_ERROR_INVALID_ADSR_RATE: return "SCE_SAS_ERROR_INVALID_ADSR_RATE";
case SCE_SAS_ERROR_INVALID_PCM_SIZE: return "SCE_SAS_ERROR_INVALID_PCM_SIZE";
case SCE_SAS_ERROR_REV_INVALID_TYPE: return "SCE_SAS_ERROR_REV_INVALID_TYPE";
case SCE_SAS_ERROR_REV_INVALID_FEEDBACK: return "SCE_SAS_ERROR_REV_INVALID_FEEDBACK";
case SCE_SAS_ERROR_REV_INVALID_DELAY: return "SCE_SAS_ERROR_REV_INVALID_DELAY";
case SCE_SAS_ERROR_REV_INVALID_VOLUME: return "SCE_SAS_ERROR_REV_INVALID_VOLUME";
case SCE_SAS_ERROR_BUSY: return "SCE_SAS_ERROR_BUSY";
case SCE_SAS_ERROR_ATRAC3_ALREADY_SET: return "SCE_SAS_ERROR_ATRAC3_ALREADY_SET";
case SCE_SAS_ERROR_ATRAC3_NOT_SET: return "SCE_SAS_ERROR_ATRAC3_NOT_SET";
case SCE_SAS_ERROR_NOT_INIT: return "SCE_SAS_ERROR_NOT_INIT";
default:
return nullptr;
}

View file

@ -50,32 +50,6 @@
#include "Core/HLE/sceKernel.h"
#include "Core/HLE/sceKernelThread.h"
enum {
ERROR_SAS_INVALID_GRAIN = 0x80420001,
ERROR_SAS_INVALID_MAX_VOICES = 0x80420002,
ERROR_SAS_INVALID_OUTPUT_MODE = 0x80420003,
ERROR_SAS_INVALID_SAMPLE_RATE = 0x80420004,
ERROR_SAS_BAD_ADDRESS = 0x80420005,
ERROR_SAS_INVALID_VOICE = 0x80420010,
ERROR_SAS_INVALID_NOISE_FREQ = 0x80420011,
ERROR_SAS_INVALID_PITCH = 0x80420012,
ERROR_SAS_INVALID_ADSR_CURVE_MODE = 0x80420013,
ERROR_SAS_INVALID_PARAMETER = 0x80420014,
ERROR_SAS_INVALID_LOOP_POS = 0x80420015,
ERROR_SAS_VOICE_PAUSED = 0x80420016,
ERROR_SAS_INVALID_VOLUME = 0x80420018,
ERROR_SAS_INVALID_ADSR_RATE = 0x80420019,
ERROR_SAS_INVALID_PCM_SIZE = 0x8042001A,
ERROR_SAS_REV_INVALID_TYPE = 0x80420020,
ERROR_SAS_REV_INVALID_FEEDBACK = 0x80420021,
ERROR_SAS_REV_INVALID_DELAY = 0x80420022,
ERROR_SAS_REV_INVALID_VOLUME = 0x80420023,
ERROR_SAS_BUSY = 0x80420030,
ERROR_SAS_ATRAC3_ALREADY_SET = 0x80420040,
ERROR_SAS_ATRAC3_NOT_SET = 0x80420041,
ERROR_SAS_NOT_INIT = 0x80420100,
};
// TODO - allow more than one, associating each with one Core pointer (passed in to all the functions)
// No known games use more than one instance of Sas though.
static SasInstance *sas;
@ -230,23 +204,23 @@ void __SasShutdown() {
static u32 sceSasInit(u32 core, u32 grainSize, u32 maxVoices, u32 outputMode, u32 sampleRate) {
if (!Memory::IsValidAddress(core) || (core & 0x3F) != 0) {
ERROR_LOG_REPORT(Log::sceSas, "sceSasInit(%08x, %i, %i, %i, %i): bad core address", core, grainSize, maxVoices, outputMode, sampleRate);
return hleNoLog(ERROR_SAS_BAD_ADDRESS);
return hleNoLog(SCE_SAS_ERROR_BAD_ADDRESS);
}
if (maxVoices == 0 || maxVoices > PSP_SAS_VOICES_MAX) {
ERROR_LOG_REPORT(Log::sceSas, "sceSasInit(%08x, %i, %i, %i, %i): bad max voices", core, grainSize, maxVoices, outputMode, sampleRate);
return hleNoLog(ERROR_SAS_INVALID_MAX_VOICES);
return hleNoLog(SCE_SAS_ERROR_INVALID_MAX_VOICES);
}
if (grainSize < 0x40 || grainSize > 0x800 || (grainSize & 0x1F) != 0) {
ERROR_LOG_REPORT(Log::sceSas, "sceSasInit(%08x, %i, %i, %i, %i): bad grain size", core, grainSize, maxVoices, outputMode, sampleRate);
return hleNoLog(ERROR_SAS_INVALID_GRAIN);
return hleNoLog(SCE_SAS_ERROR_INVALID_GRAIN);
}
if (outputMode != 0 && outputMode != 1) {
ERROR_LOG_REPORT(Log::sceSas, "sceSasInit(%08x, %i, %i, %i, %i): bad output mode", core, grainSize, maxVoices, outputMode, sampleRate);
return hleNoLog(ERROR_SAS_INVALID_OUTPUT_MODE);
return hleNoLog(SCE_SAS_ERROR_INVALID_OUTPUT_MODE);
}
if (sampleRate != 44100) {
ERROR_LOG_REPORT(Log::sceSas, "sceSasInit(%08x, %i, %i, %i, %i): bad sample rate", core, grainSize, maxVoices, outputMode, sampleRate);
return hleNoLog(ERROR_SAS_INVALID_SAMPLE_RATE);
return hleNoLog(SCE_SAS_ERROR_INVALID_SAMPLE_RATE);
}
sas->SetGrainSize(grainSize);
@ -290,7 +264,7 @@ static u32 _sceSasCore(u32 core, u32 outAddr) {
PROFILE_THIS_SCOPE("mixer");
if (!Memory::IsValidAddress(outAddr)) {
return hleReportError(Log::sceSas, ERROR_SAS_INVALID_PARAMETER, "invalid address");
return hleReportError(Log::sceSas, SCE_SAS_ERROR_INVALID_PARAMETER, "invalid address");
}
if (!__KernelIsDispatchEnabled()) {
return hleLogError(Log::sceSas, SCE_KERNEL_ERROR_CAN_NOT_WAIT, "dispatch disabled");
@ -306,7 +280,7 @@ static u32 _sceSasCoreWithMix(u32 core, u32 inoutAddr, int leftVolume, int right
PROFILE_THIS_SCOPE("mixer");
if (!Memory::IsValidAddress(inoutAddr)) {
return hleReportError(Log::sceSas, ERROR_SAS_INVALID_PARAMETER, "invalid address");
return hleReportError(Log::sceSas, SCE_SAS_ERROR_INVALID_PARAMETER, "invalid address");
}
if (sas->outputMode == PSP_SAS_OUTPUTMODE_RAW) {
return hleReportError(Log::sceSas, 0x80000004, "unsupported outputMode");
@ -322,19 +296,19 @@ static u32 _sceSasCoreWithMix(u32 core, u32 inoutAddr, int leftVolume, int right
static u32 sceSasSetVoice(u32 core, int voiceNum, u32 vagAddr, int size, int loop) {
if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0) {
return hleLogVerbose(Log::sceSas, ERROR_SAS_INVALID_VOICE, "invalid voicenum");
return hleLogVerbose(Log::sceSas, SCE_SAS_ERROR_INVALID_VOICE, "invalid voicenum");
}
if (size == 0 || ((u32)size & 0xF) != 0) {
if (size == 0) {
return hleLogDebug(Log::sceSas, ERROR_SAS_INVALID_PARAMETER, "invalid size %d", size);
return hleLogDebug(Log::sceSas, SCE_SAS_ERROR_INVALID_PARAMETER, "invalid size %d", size);
} else {
return hleLogWarning(Log::sceSas, ERROR_SAS_INVALID_PARAMETER, "invalid size %d", size);
return hleLogWarning(Log::sceSas, SCE_SAS_ERROR_INVALID_PARAMETER, "invalid size %d", size);
}
}
if (loop != 0 && loop != 1) {
WARN_LOG_REPORT(Log::sceSas, "%s: invalid loop mode %d", __FUNCTION__, loop);
return hleNoLog(ERROR_SAS_INVALID_LOOP_POS);
return hleNoLog(SCE_SAS_ERROR_INVALID_LOOP_POS);
}
if (!Memory::IsValidAddress(vagAddr)) {
@ -344,7 +318,7 @@ static u32 sceSasSetVoice(u32 core, int voiceNum, u32 vagAddr, int size, int loo
__SasDrain();
SasVoice &v = sas->voices[voiceNum];
if (v.type == VOICETYPE_ATRAC3) {
return hleLogError(Log::sceSas, ERROR_SAS_ATRAC3_ALREADY_SET, "voice is already ATRAC3");
return hleLogError(Log::sceSas, SCE_SAS_ERROR_ATRAC3_ALREADY_SET, "voice is already ATRAC3");
}
if (size < 0) {
@ -373,14 +347,14 @@ static u32 sceSasSetVoice(u32 core, int voiceNum, u32 vagAddr, int size, int loo
static u32 sceSasSetVoicePCM(u32 core, int voiceNum, u32 pcmAddr, int size, int loopPos) {
if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0) {
return hleLogWarning(Log::sceSas, ERROR_SAS_INVALID_VOICE, "invalid voicenum");
return hleLogWarning(Log::sceSas, SCE_SAS_ERROR_INVALID_VOICE, "invalid voicenum");
}
if (size <= 0 || size > 0x10000) {
return hleLogWarning(Log::sceSas, ERROR_SAS_INVALID_PCM_SIZE, "invalid size %d", size);
return hleLogWarning(Log::sceSas, SCE_SAS_ERROR_INVALID_PCM_SIZE, "invalid size %d", size);
}
if (loopPos >= size) {
ERROR_LOG_REPORT(Log::sceSas, "sceSasSetVoicePCM(%08x, %i, %08x, %i, %i): bad loop pos", core, voiceNum, pcmAddr, size, loopPos);
return hleNoLog(ERROR_SAS_INVALID_LOOP_POS);
return hleNoLog(SCE_SAS_ERROR_INVALID_LOOP_POS);
}
if (!Memory::IsValidAddress(pcmAddr)) {
return hleLogError(Log::sceSas, 0, "Ignoring invalid PCM audio address %08x", pcmAddr);
@ -389,7 +363,7 @@ static u32 sceSasSetVoicePCM(u32 core, int voiceNum, u32 pcmAddr, int size, int
__SasDrain();
SasVoice &v = sas->voices[voiceNum];
if (v.type == VOICETYPE_ATRAC3) {
return hleLogError(Log::sceSas, ERROR_SAS_ATRAC3_ALREADY_SET, "voice is already ATRAC3");
return hleLogError(Log::sceSas, SCE_SAS_ERROR_ATRAC3_ALREADY_SET, "voice is already ATRAC3");
}
u32 prevPcmAddr = v.pcmAddr;
@ -428,12 +402,12 @@ static u32 sceSasSetPause(u32 core, u32 voicebit, int pause) {
static u32 sceSasSetVolume(u32 core, int voiceNum, int leftVol, int rightVol, int effectLeftVol, int effectRightVol) {
if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0) {
return hleLogWarning(Log::sceSas, ERROR_SAS_INVALID_VOICE, "invalid voicenum");
return hleLogWarning(Log::sceSas, SCE_SAS_ERROR_INVALID_VOICE, "invalid voicenum");
}
bool overVolume = abs(leftVol) > PSP_SAS_VOL_MAX || abs(rightVol) > PSP_SAS_VOL_MAX;
overVolume = overVolume || abs(effectLeftVol) > PSP_SAS_VOL_MAX || abs(effectRightVol) > PSP_SAS_VOL_MAX;
if (overVolume) {
return hleLogError(Log::sceSas, ERROR_SAS_INVALID_VOLUME);
return hleLogError(Log::sceSas, SCE_SAS_ERROR_INVALID_VOLUME);
}
__SasDrain();
@ -442,15 +416,15 @@ static u32 sceSasSetVolume(u32 core, int voiceNum, int leftVol, int rightVol, in
v.volumeRight = rightVol;
v.effectLeft = effectLeftVol;
v.effectRight = effectRightVol;
return hleLogDebug(Log::sceSas, 0);
return hleLogVerbose(Log::sceSas, 0);
}
static u32 sceSasSetPitch(u32 core, int voiceNum, int pitch) {
if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0) {
return hleLogWarning(Log::sceSas, ERROR_SAS_INVALID_VOICE, "invalid voiceNum");
return hleLogWarning(Log::sceSas, SCE_SAS_ERROR_INVALID_VOICE, "invalid voiceNum");
}
if (pitch < PSP_SAS_PITCH_MIN || pitch > PSP_SAS_PITCH_MAX) {
return hleLogWarning(Log::sceSas, ERROR_SAS_INVALID_PITCH, "bad pitch");
return hleLogWarning(Log::sceSas, SCE_SAS_ERROR_INVALID_PITCH, "bad pitch");
}
__SasDrain();
@ -461,12 +435,12 @@ static u32 sceSasSetPitch(u32 core, int voiceNum, int pitch) {
static u32 sceSasSetKeyOn(u32 core, int voiceNum) {
if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0) {
return hleLogWarning(Log::sceSas, ERROR_SAS_INVALID_VOICE, "invalid voiceNum");
return hleLogWarning(Log::sceSas, SCE_SAS_ERROR_INVALID_VOICE, "invalid voiceNum");
}
__SasDrain();
if (sas->voices[voiceNum].paused || sas->voices[voiceNum].on) {
return hleLogError(Log::sceSas, ERROR_SAS_VOICE_PAUSED);
return hleLogError(Log::sceSas, SCE_SAS_ERROR_VOICE_PAUSED);
}
SasVoice &v = sas->voices[voiceNum];
@ -477,11 +451,11 @@ static u32 sceSasSetKeyOn(u32 core, int voiceNum) {
// sceSasSetKeyOff can be used to start sounds, that just sound during the Release phase!
static u32 sceSasSetKeyOff(u32 core, int voiceNum) {
if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0) {
return hleLogWarning(Log::sceSas, ERROR_SAS_INVALID_VOICE, "invalid voiceNum");
return hleLogWarning(Log::sceSas, SCE_SAS_ERROR_INVALID_VOICE, "invalid voiceNum");
} else {
__SasDrain();
if (sas->voices[voiceNum].paused || !sas->voices[voiceNum].on) {
return hleLogDebug(Log::sceSas, ERROR_SAS_VOICE_PAUSED); // this is ok
return hleLogDebug(Log::sceSas, SCE_SAS_ERROR_VOICE_PAUSED); // this is ok
}
SasVoice &v = sas->voices[voiceNum];
@ -492,10 +466,10 @@ static u32 sceSasSetKeyOff(u32 core, int voiceNum) {
static u32 sceSasSetNoise(u32 core, int voiceNum, int freq) {
if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0) {
return hleLogWarning(Log::sceSas, ERROR_SAS_INVALID_VOICE, "invalid voiceNum");
return hleLogWarning(Log::sceSas, SCE_SAS_ERROR_INVALID_VOICE, "invalid voiceNum");
}
if (freq < 0 || freq >= 64) {
return hleLogWarning(Log::sceSas, ERROR_SAS_INVALID_NOISE_FREQ);
return hleLogWarning(Log::sceSas, SCE_SAS_ERROR_INVALID_NOISE_FREQ);
}
__SasDrain();
@ -507,7 +481,7 @@ static u32 sceSasSetNoise(u32 core, int voiceNum, int freq) {
static u32 sceSasSetSL(u32 core, int voiceNum, int level) {
if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0) {
return hleLogWarning(Log::sceSas, ERROR_SAS_INVALID_VOICE, "invalid voiceNum");
return hleLogWarning(Log::sceSas, SCE_SAS_ERROR_INVALID_VOICE, "invalid voiceNum");
}
__SasDrain();
@ -518,13 +492,13 @@ static u32 sceSasSetSL(u32 core, int voiceNum, int level) {
static u32 sceSasSetADSR(u32 core, int voiceNum, int flag, int a, int d, int s, int r) {
if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0) {
return hleLogWarning(Log::sceSas, ERROR_SAS_INVALID_VOICE, "invalid voiceNum");
return hleLogWarning(Log::sceSas, SCE_SAS_ERROR_INVALID_VOICE, "invalid voiceNum");
}
// Create a mask like flag for the invalid values.
int invalid = (a < 0 ? 0x1 : 0) | (d < 0 ? 0x2 : 0) | (s < 0 ? 0x4 : 0) | (r < 0 ? 0x8 : 0);
if (invalid & flag) {
WARN_LOG_REPORT(Log::sceSas, "sceSasSetADSR(%08x, %i, %i, %08x, %08x, %08x, %08x): invalid value", core, voiceNum, flag, a, d, s, r);
return hleNoLog(ERROR_SAS_INVALID_ADSR_RATE);
return hleNoLog(SCE_SAS_ERROR_INVALID_ADSR_RATE);
}
__SasDrain();
@ -535,7 +509,7 @@ static u32 sceSasSetADSR(u32 core, int voiceNum, int flag, int a, int d, int s,
static u32 sceSasSetADSRMode(u32 core, int voiceNum, int flag, int a, int d, int s, int r) {
if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0) {
return hleLogWarning(Log::sceSas, ERROR_SAS_INVALID_VOICE, "invalid voiceNum");
return hleLogWarning(Log::sceSas, SCE_SAS_ERROR_INVALID_VOICE, "invalid voiceNum");
}
// Probably by accident (?), the PSP ignores the top bit of these values.
@ -561,10 +535,10 @@ static u32 sceSasSetADSRMode(u32 core, int voiceNum, int flag, int a, int d, int
if (invalid & flag) {
if (a == 5 && d == 5 && s == 5 && r == 5) {
// Some games do this right at init. It seems to fail even on a PSP, but let's not report it.
return hleLogDebug(Log::sceSas, ERROR_SAS_INVALID_ADSR_CURVE_MODE, "sceSasSetADSRMode(%08x, %i, %i, %08x, %08x, %08x, %08x): invalid modes", core, voiceNum, flag, a, d, s, r);
return hleLogDebug(Log::sceSas, SCE_SAS_ERROR_INVALID_ADSR_CURVE_MODE, "sceSasSetADSRMode(%08x, %i, %i, %08x, %08x, %08x, %08x): invalid modes", core, voiceNum, flag, a, d, s, r);
} else {
WARN_LOG_REPORT(Log::sceSas, "sceSasSetADSRMode(%08x, %i, %i, %08x, %08x, %08x, %08x): invalid modes", core, voiceNum, flag, a, d, s, r);
return hleNoLog(ERROR_SAS_INVALID_ADSR_CURVE_MODE);
return hleNoLog(SCE_SAS_ERROR_INVALID_ADSR_CURVE_MODE);
}
}
@ -577,11 +551,11 @@ static u32 sceSasSetADSRMode(u32 core, int voiceNum, int flag, int a, int d, int
static u32 sceSasSetSimpleADSR(u32 core, int voiceNum, u32 ADSREnv1, u32 ADSREnv2) {
if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0) {
return hleLogWarning(Log::sceSas, ERROR_SAS_INVALID_VOICE, "invalid voiceNum");
return hleLogWarning(Log::sceSas, SCE_SAS_ERROR_INVALID_VOICE, "invalid voiceNum");
}
// This bit could be related to decay type or systain type, but gives an error if you try to set it.
if ((ADSREnv2 >> 13) & 1) {
return hleLogWarning(Log::sceSas, ERROR_SAS_INVALID_ADSR_CURVE_MODE, "Invalid ADSREnv2");
return hleLogWarning(Log::sceSas, SCE_SAS_ERROR_INVALID_ADSR_CURVE_MODE, "Invalid ADSREnv2");
}
__SasDrain();
@ -592,7 +566,7 @@ static u32 sceSasSetSimpleADSR(u32 core, int voiceNum, u32 ADSREnv1, u32 ADSREnv
static u32 sceSasGetEnvelopeHeight(u32 core, int voiceNum) {
if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0) {
return hleLogWarning(Log::sceSas, ERROR_SAS_INVALID_VOICE, "invalid voiceNum");
return hleLogWarning(Log::sceSas, SCE_SAS_ERROR_INVALID_VOICE, "invalid voiceNum");
}
__SasDrain();
@ -603,7 +577,7 @@ static u32 sceSasGetEnvelopeHeight(u32 core, int voiceNum) {
static u32 sceSasRevType(u32 core, int type) {
if (type < PSP_SAS_EFFECT_TYPE_OFF || type > PSP_SAS_EFFECT_TYPE_MAX) {
return hleLogError(Log::sceSas, ERROR_SAS_REV_INVALID_TYPE, "invalid type");
return hleLogError(Log::sceSas, SCE_SAS_ERROR_REV_INVALID_TYPE, "invalid type");
}
__SasDrain();
@ -613,10 +587,10 @@ static u32 sceSasRevType(u32 core, int type) {
static u32 sceSasRevParam(u32 core, int delay, int feedback) {
if (delay < 0 || delay >= 128) {
return hleLogError(Log::sceSas, ERROR_SAS_REV_INVALID_DELAY, "invalid delay value");
return hleLogError(Log::sceSas, SCE_SAS_ERROR_REV_INVALID_DELAY, "invalid delay value");
}
if (feedback < 0 || feedback >= 128) {
return hleLogError(Log::sceSas, ERROR_SAS_REV_INVALID_FEEDBACK, "invalid feedback value");
return hleLogError(Log::sceSas, SCE_SAS_ERROR_REV_INVALID_FEEDBACK, "invalid feedback value");
}
__SasDrain();
@ -627,7 +601,7 @@ static u32 sceSasRevParam(u32 core, int delay, int feedback) {
static u32 sceSasRevEVOL(u32 core, u32 lv, u32 rv) {
if (lv > 0x1000 || rv > 0x1000) {
return hleLogWarning(Log::sceSas, ERROR_SAS_REV_INVALID_VOLUME, "invalid volume");
return hleLogWarning(Log::sceSas, SCE_SAS_ERROR_REV_INVALID_VOLUME, "invalid volume");
}
__SasDrain();
@ -660,7 +634,7 @@ static u32 sceSasGetOutputMode(u32 core) {
static u32 sceSasSetOutputMode(u32 core, u32 outputMode) {
if (outputMode != 0 && outputMode != 1) {
ERROR_LOG_REPORT(Log::sceSas, "sceSasSetOutputMode(%08x, %i): bad output mode", core, outputMode);
return hleNoLog(ERROR_SAS_INVALID_OUTPUT_MODE);
return hleNoLog(SCE_SAS_ERROR_INVALID_OUTPUT_MODE);
}
__SasDrain();
@ -670,7 +644,7 @@ static u32 sceSasSetOutputMode(u32 core, u32 outputMode) {
static u32 sceSasGetAllEnvelopeHeights(u32 core, u32 heightsAddr) {
if (!Memory::IsValidAddress(heightsAddr)) {
return hleLogError(Log::sceSas, ERROR_SAS_INVALID_PARAMETER);
return hleLogError(Log::sceSas, SCE_SAS_ERROR_INVALID_PARAMETER);
}
__SasDrain();
@ -694,45 +668,46 @@ static u32 sceSasSetSteepWave(u32 sasCore, int voice, int unknown) {
static u32 __sceSasSetVoiceATRAC3(u32 core, int voiceNum, u32 atrac3Context) {
if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0) {
return hleLogWarning(Log::sceSas, ERROR_SAS_INVALID_VOICE, "invalid voicenum");
return hleLogWarning(Log::sceSas, SCE_SAS_ERROR_INVALID_VOICE, "invalid voicenum");
}
__SasDrain();
SasVoice &v = sas->voices[voiceNum];
if (v.type == VOICETYPE_ATRAC3) {
return hleLogError(Log::sceSas, ERROR_SAS_ATRAC3_ALREADY_SET, "voice is already ATRAC3");
return hleLogError(Log::sceSas, SCE_SAS_ERROR_ATRAC3_ALREADY_SET, "voice is already ATRAC3");
}
v.type = VOICETYPE_ATRAC3;
v.loop = false;
v.playing = true;
v.atrac3.setContext(atrac3Context);
v.atrac3.SetContext(atrac3Context);
Memory::Write_U32(atrac3Context, core + 56 * voiceNum + 20);
return hleLogDebug(Log::sceSas, 0);
}
static u32 __sceSasConcatenateATRAC3(u32 core, int voiceNum, u32 atrac3DataAddr, int atrac3DataLength) {
if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0) {
return hleLogWarning(Log::sceSas, ERROR_SAS_INVALID_VOICE, "invalid voicenum");
return hleLogWarning(Log::sceSas, SCE_SAS_ERROR_INVALID_VOICE, "invalid voicenum");
}
DEBUG_LOG_REPORT_ONCE(concatAtrac3, Log::sceSas, "__sceSasConcatenateATRAC3(%08x, %i, %08x, %i)", core, voiceNum, atrac3DataAddr, atrac3DataLength);
__SasDrain();
DEBUG_LOG_REPORT_ONCE(concatAtrac3, Log::sceSas, "__sceSasConcatenateATRAC3(%08x, %i, %08x, %i)", core, voiceNum, atrac3DataAddr, atrac3DataLength);
SasVoice &v = sas->voices[voiceNum];
if (Memory::IsValidAddress(atrac3DataAddr))
v.atrac3.addStreamData(atrac3DataAddr, atrac3DataLength);
return hleNoLog(0);
v.atrac3.Concatenate(atrac3DataAddr, atrac3DataLength);
return hleLogDebug(Log::sceSas, 0);
}
static u32 __sceSasUnsetATRAC3(u32 core, int voiceNum) {
if (voiceNum >= PSP_SAS_VOICES_MAX || voiceNum < 0) {
return hleLogWarning(Log::sceSas, ERROR_SAS_INVALID_VOICE, "invalid voicenum");
return hleLogWarning(Log::sceSas, SCE_SAS_ERROR_INVALID_VOICE, "invalid voicenum");
}
__SasDrain();
SasVoice &v = sas->voices[voiceNum];
if (v.type != VOICETYPE_ATRAC3) {
return hleLogError(Log::sceSas, ERROR_SAS_ATRAC3_NOT_SET, "voice is not ATRAC3");
return hleLogError(Log::sceSas, SCE_SAS_ERROR_ATRAC3_NOT_SET, "voice is not ATRAC3");
}
v.type = VOICETYPE_OFF;
v.playing = false;
@ -752,8 +727,7 @@ void __SasGetDebugStats(char *stats, size_t bufsize) {
}
}
const HLEFunction sceSasCore[] =
{
const HLEFunction sceSasCore[] = {
{0X42778A9F, &WrapU_UUUUU<sceSasInit>, "__sceSasInit", 'x', "xxxxx" },
{0XA3589D81, &WrapU_UU<_sceSasCore>, "__sceSasCore", 'x', "xx" },
{0X50A14DFC, &WrapU_UUII<_sceSasCoreWithMix>, "__sceSasCoreWithMix", 'x', "xxii" },

View file

@ -188,9 +188,10 @@ void VagDecoder::DoState(PointerWrap &p) {
Do(p, end_);
}
int SasAtrac3::setContext(u32 contextAddr) {
int SasAtrac3::SetContext(u32 contextAddr) {
contextAddr_ = contextAddr;
// Note: This atracID_ is also stored in the loopNum member of the context.
// Note: On hardware, atracID_ is also stored in the loopNum member of the context.
// But we don't actually mirror our struct to memory, so it doesn't really matter.
atracID_ = AtracSasBindContextAndGetID(contextAddr);
if (!sampleQueue_)
sampleQueue_ = new BufferQueue();
@ -199,19 +200,23 @@ int SasAtrac3::setContext(u32 contextAddr) {
return 0;
}
void SasAtrac3::getNextSamples(s16 *outbuf, int wantedSamples) {
void SasAtrac3::GetNextSamples(s16 *outbuf, int wantedSamples) {
if (atracID_ < 0) {
end_ = true;
return;
}
if (!buf_) {
buf_ = new s16[0x800];
}
int finish = 0;
int wantedbytes = wantedSamples * sizeof(s16);
while (!finish && sampleQueue_->getQueueSize() < wantedbytes) {
int numSamples = 0;
static s16 buf[0x800];
AtracSasDecodeData(atracID_, (u8*)buf, &numSamples, &finish);
AtracSasDecodeData(atracID_, (u8*)buf_, &numSamples, &finish);
if (numSamples > 0)
sampleQueue_->push((u8*)buf, numSamples * sizeof(s16));
sampleQueue_->push((u8*)buf_, numSamples * sizeof(s16));
else
finish = 1;
}
@ -219,8 +224,8 @@ void SasAtrac3::getNextSamples(s16 *outbuf, int wantedSamples) {
end_ = finish == 1;
}
int SasAtrac3::addStreamData(u32 bufPtr, u32 addbytes) {
if (atracID_ > 0) {
int SasAtrac3::Concatenate(u32 bufPtr, u32 addbytes) {
if (atracID_ >= 0) {
AtracSasAddStreamData(atracID_, bufPtr, addbytes);
}
return 0;
@ -496,7 +501,7 @@ void SasVoice::ReadSamples(s16 *output, int numSamples) {
}
break;
case VOICETYPE_ATRAC3:
atrac3.getNextSamples(output, numSamples);
atrac3.GetNextSamples(output, numSamples);
break;
default:
memset(output, 0, numSamples * sizeof(s16));

View file

@ -137,22 +137,22 @@ private:
class SasAtrac3 {
public:
SasAtrac3() : contextAddr_(0), atracID_(-1), sampleQueue_(0), end_(false) {}
~SasAtrac3() { delete sampleQueue_; }
int setContext(u32 context);
int id() const { return atracID_; }
void getNextSamples(s16 *outbuf, int wantedSamples);
int addStreamData(u32 bufPtr, u32 addbytes);
~SasAtrac3() { delete sampleQueue_; delete[] buf_; }
int AtracID() const { return atracID_; } // for the debugger
int SetContext(u32 context);
void GetNextSamples(s16 *outbuf, int wantedSamples);
int Concatenate(u32 bufPtr, u32 addbytes);
void DoState(PointerWrap &p);
bool End() const {
return end_;
}
private:
u32 contextAddr_;
int atracID_;
BufferQueue *sampleQueue_;
bool end_;
u32 contextAddr_ = 0;
int atracID_ = -1;
BufferQueue *sampleQueue_ = nullptr;
bool end_ = false;
s16 *buf_ = nullptr;
};
class ADSREnvelope {

View file

@ -212,7 +212,7 @@ bool DiscIDFromGEDumpPath(const Path &path, FileLoader *fileLoader, std::string
}
}
bool CPU_Init(std::string *errorString, FileLoader *loadedFile) {
bool CPU_Init(std::string *errorString, FileLoader *loadedFile, IdentifiedFileType type) {
coreState = CORE_POWERUP;
currentMIPS = &mipsr4k;
@ -228,8 +228,6 @@ bool CPU_Init(std::string *errorString, FileLoader *loadedFile) {
Path filename = g_CoreParameter.fileToStart;
IdentifiedFileType type = Identify_File(loadedFile, errorString);
// TODO: Put this somewhere better?
if (!g_CoreParameter.mountIso.empty()) {
g_CoreParameter.mountIsoLoader = ConstructFileLoader(g_CoreParameter.mountIso);
@ -414,9 +412,12 @@ bool PSP_InitStart(const CoreParameter &coreParam, std::string *error_string) {
Path filename = g_CoreParameter.fileToStart;
FileLoader *loadedFile = ResolveFileLoaderTarget(ConstructFileLoader(filename));
#if PPSSPP_ARCH(AMD64)
IdentifiedFileType type = Identify_File(loadedFile, &g_CoreParameter.errorString);
g_CoreParameter.fileType = type;
if (g_Config.bCacheFullIsoInRam) {
switch (coreParam.fileType) {
switch (g_CoreParameter.fileType) {
case IdentifiedFileType::PSP_ISO:
case IdentifiedFileType::PSP_ISO_NP:
loadedFile = new RamCachingFileLoader(loadedFile);
@ -426,7 +427,6 @@ bool PSP_InitStart(const CoreParameter &coreParam, std::string *error_string) {
break;
}
}
#endif
if (g_Config.bAchievementsEnable) {
// Need to re-identify after ResolveFileLoaderTarget - although in practice probably not,
@ -436,7 +436,7 @@ bool PSP_InitStart(const CoreParameter &coreParam, std::string *error_string) {
Achievements::SetGame(filename, type, loadedFile);
}
if (!CPU_Init(&g_CoreParameter.errorString, loadedFile)) {
if (!CPU_Init(&g_CoreParameter.errorString, loadedFile, type)) {
*error_string = g_CoreParameter.errorString;
if (error_string->empty()) {
*error_string = "Failed initializing CPU/Memory";

View file

@ -426,8 +426,8 @@ static void ExecuteWebServer() {
RegisterServer(http->Port());
double lastRegister = time_now_d();
while (RetrieveStatus() == ServerStatus::RUNNING) {
http->RunSlice(1.0);
constexpr double webServerSliceSeconds = 0.2f;
http->RunSlice(webServerSliceSeconds);
double now = time_now_d();
if (now > lastRegister + 540.0) {
RegisterServer(http->Port());

View file

@ -17,12 +17,16 @@
#pragma once
#include "Common/Common.h"
enum class WebServerFlags {
NONE = 0,
DISCS = 1,
DEBUGGER = 2,
ALL = 1 | 2,
};
ENUM_CLASS_BITOPS(WebServerFlags);
bool StartWebServer(WebServerFlags flags);
bool StopWebServer(WebServerFlags flags);

View file

@ -1028,14 +1028,14 @@ void DrawAudioDecodersView(ImConfig &cfg, ImControl &control) {
char header[32];
snprintf(header, sizeof(header), "Atrac context %d", cfg.selectedAtracCtx);
if (ctx && ImGui::CollapsingHeader(header, ImGuiTreeNodeFlags_DefaultOpen)) {
int pos;
ctx->GetNextDecodePosition(&pos);
int endSample, loopStart, loopEnd;
ctx->GetSoundSample(&endSample, &loopStart, &loopEnd);
ImGui::ProgressBar((float)pos / (float)endSample, ImVec2(200.0f, 0.0f));
ImGui::Text("Status: %s", AtracStatusToString(ctx->BufferState()));
if (ctx->BufferState() <= ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER) {
ImGui::Text("cur/end sample: %d/%d/%d", pos, endSample);
bool isNormal = AtracStatusIsNormal(ctx->BufferState());
if (isNormal) {
int pos;
ctx->GetNextDecodePosition(&pos);
int endSample, loopStart, loopEnd;
ctx->GetSoundSample(&endSample, &loopStart, &loopEnd);
ImGui::ProgressBar((float)pos / (float)endSample, ImVec2(200.0f, 0.0f));
ImGui::Text("Status: %s", AtracStatusToString(ctx->BufferState())); ImGui::Text("cur/end sample: %d/%d/%d", pos, endSample);
}
if (ctx->context_.IsValid()) {
ImGui::Text("ctx addr: ");
@ -1044,25 +1044,41 @@ void DrawAudioDecodersView(ImConfig &cfg, ImControl &control) {
}
if (ctx->context_.IsValid() && ctx->GetContextVersion() >= 2) {
const auto &info = ctx->context_->info;
ImGui::Text("Buffer: (size: %d / %08x) Frame: %d", info.bufferByte, info.bufferByte, info.sampleSize);
ImGui::SameLine();
ImClickableValue("buffer", info.buffer, control, ImCmd::SHOW_IN_MEMORY_VIEWER);
if (info.secondBuffer || info.secondBufferByte) {
ImGui::Text("Second: (size: %d / %08x)", info.secondBufferByte, info.secondBufferByte);
if (isNormal) {
ImGui::Text("Buffer: (size: %d / %08x) Frame: %d", info.bufferByte, info.bufferByte, info.sampleSize);
ImGui::SameLine();
ImClickableValue("second", info.secondBuffer, control, ImCmd::SHOW_IN_MEMORY_VIEWER);
ImClickableValue("buffer", info.buffer, control, ImCmd::SHOW_IN_MEMORY_VIEWER);
if (info.secondBuffer || info.secondBufferByte) {
ImGui::Text("Second: (size: %d / %08x)", info.secondBufferByte, info.secondBufferByte);
ImGui::SameLine();
ImClickableValue("second", info.secondBuffer, control, ImCmd::SHOW_IN_MEMORY_VIEWER);
}
ImGui::Text("Data: %d/%d", info.dataOff, info.fileDataEnd);
if (info.state != ATRAC_STATUS_STREAMED_WITHOUT_LOOP) {
ImGui::Text("LoopNum: %d (%d-%d)", info.loopNum, info.loopStart, info.loopEnd);
}
ImGui::Text("DecodePos: %d EndSample: %d", info.decodePos, info.fileDataEnd);
if (AtracStatusIsStreaming(info.state)) {
ImGui::Text("Stream: offset %d, streamDataBytes: %d", info.streamOff, info.streamDataByte);
}
ImGui::Text("numFrame: %d curBuffer: %d streamOff2: %d", info.numSkipFrames, info.curBuffer, info.secondStreamOff);
} else if (ctx->BufferState() == ATRAC_STATUS_FOR_SCESAS) {
// A different set of state!
const AtracSasStreamState *sas = ctx->StreamStateForSas();
if (sas) {
ImGui::ProgressBar((float)sas->CurPos() / (float)info.fileDataEnd, ImVec2(200.0f, 0.0f));
ImGui::ProgressBar((float)sas->streamOffset / (float)sas->bufSize[sas->curBuffer], ImVec2(200.0f, 0.0f));
ImGui::Text("Cur pos: %08x File offset: %08x File end: %08x%s", sas->CurPos(), sas->fileOffset, info.fileDataEnd, sas->fileOffset >= info.fileDataEnd ? " (END)" : "");
ImGui::Text("Second (next buffer): %08x (sz: %08x)", info.secondBuffer, info.secondBufferByte);
ImGui::Text("Cur buffer: %d (%08x, sz: %08x)", sas->curBuffer, sas->bufPtr[sas->curBuffer], sas->bufSize[sas->curBuffer]);
ImGui::Text("2nd buffer: %d (%08x, sz: %08x)", sas->curBuffer ^ 1, sas->bufPtr[sas->curBuffer ^ 1], sas->bufSize[sas->curBuffer ^ 1]);
ImGui::Text("Loop points: %08x, %08x", info.loopStart, info.loopEnd);
ImGui::TextUnformatted(sas->isStreaming ? "Streaming mode!" : "Non-streaming mode");
} else {
ImGui::Text("can't access sas state");
}
}
ImGui::Text("Data: %d/%d", info.dataOff, info.fileDataEnd);
if (info.state != ATRAC_STATUS_STREAMED_WITHOUT_LOOP) {
ImGui::Text("LoopNum: %d (%d-%d)", info.loopNum, info.loopStart, info.loopEnd);
}
ImGui::Text("DecodePos: %d EndSample: %d", info.decodePos, info.fileDataEnd);
if (AtracStatusIsStreaming(info.state)) {
ImGui::Text("Stream: offset %d, streamDataBytes: %d", info.streamOff, info.streamDataByte);
}
// Display unknown vars.
ImGui::Text("numFrame: %d curBuffer: %d streamOff2: %d", info.numSkipFrames, info.curBuffer, info.secondStreamOff);
} else {
} else {
ImGui::Text("loop: %d", ctx->LoopNum());
}
}
@ -1306,7 +1322,7 @@ void DrawSasAudio(ImConfig &cfg) {
case VOICETYPE_OFF: ImGui::TextUnformatted("(off)"); break;
case VOICETYPE_VAG: ImGui::Text("%08x", voice.vagAddr); break;
case VOICETYPE_PCM: ImGui::Text("%08x", voice.pcmAddr); break;
case VOICETYPE_ATRAC3: ImGui::Text("atrac: %d", voice.atrac3.id()); break;
case VOICETYPE_ATRAC3: ImGui::Text("atrac: %d", voice.atrac3.AtracID()); break;
default:
ImGui::TextUnformatted("N/A");
break;

View file

@ -749,12 +749,16 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch
// Easy testing
// screenManager->push(new GPUDriverTestScreen());
if (g_Config.bRemoteShareOnStartup && g_Config.bRemoteDebuggerOnStartup)
WebServerFlags flags = (WebServerFlags)0;
if (g_Config.bRemoteShareOnStartup) {
flags |= WebServerFlags::DISCS;
}
if (g_Config.bRemoteDebuggerOnStartup) {
flags |= WebServerFlags::DEBUGGER;
}
if (flags != WebServerFlags::NONE) {
StartWebServer(WebServerFlags::ALL);
else if (g_Config.bRemoteShareOnStartup)
StartWebServer(WebServerFlags::DISCS);
else if (g_Config.bRemoteDebuggerOnStartup)
StartWebServer(WebServerFlags::DEBUGGER);
}
std::string sysName = System_GetProperty(SYSPROP_NAME);

View file

@ -92,7 +92,7 @@ void TabbedUIDialogScreenWithGameBackground::CreateViews() {
return UI::EVENT_DONE;
});
noSearchResults_ = searchSettings->Add(new TextView(se->T("No settings matched '%1'"), new LinearLayoutParams(Margins(20, 5))));
noSearchResults_ = searchSettings->Add(new TextView("", new LinearLayoutParams(Margins(20, 5))));
}, true);
}