Atrac: Simulate ringbuffer for writable bytes.

Ringbuffer is not actually read from PSP RAM, and there are a couple
inaccuracies, but this should be a lot better than before.
This commit is contained in:
Unknown W. Brackets 2016-01-16 18:34:44 -08:00
parent 23523a2fdc
commit e41a1f476f

View File

@ -176,7 +176,7 @@ struct Atrac {
channels_(0), outputChannels_(2), bitrate_(64), bytesPerFrame_(0), bufferMaxSize_(0), jointStereo_(0), channels_(0), outputChannels_(2), bitrate_(64), bytesPerFrame_(0), bufferMaxSize_(0), jointStereo_(0),
currentSample_(0), endSample_(0), firstSampleOffset_(0), dataOff_(0), currentSample_(0), endSample_(0), firstSampleOffset_(0), dataOff_(0),
loopStartSample_(-1), loopEndSample_(-1), loopNum_(0), loopStartSample_(-1), loopEndSample_(-1), loopNum_(0),
failedDecode_(false), resetBuffer_(false), codecType_(0), ignoreDataBuf_(false), failedDecode_(false), codecType_(0), ignoreDataBuf_(false),
bufferState_(ATRAC_STATUS_NO_DATA) { bufferState_(ATRAC_STATUS_NO_DATA) {
memset(&first_, 0, sizeof(first_)); memset(&first_, 0, sizeof(first_));
memset(&second_, 0, sizeof(second_)); memset(&second_, 0, sizeof(second_));
@ -314,8 +314,10 @@ struct Atrac {
__AtracSetContext(this); __AtracSetContext(this);
} }
if (s >= 2) if (s >= 2) {
p.Do(resetBuffer_); bool oldResetBuffer = false;
p.Do(oldResetBuffer);
}
} }
int Analyze(u32 addr, u32 size); int Analyze(u32 addr, u32 size);
@ -362,8 +364,10 @@ struct Atrac {
u8 *dataBuf_; u8 *dataBuf_;
u32 decodePos_; u32 decodePos_;
// Used only by low-level decoding. // Used by low-level decoding and to track streaming.
u32 bufferPos_; u32 bufferPos_;
u32 bufferValidBytes_;
u32 bufferHeaderSize_;
u16 channels_; u16 channels_;
u16 outputChannels_; u16 outputChannels_;
@ -385,7 +389,6 @@ struct Atrac {
int loopNum_; int loopNum_;
bool failedDecode_; bool failedDecode_;
bool resetBuffer_;
// Indicates that the dataBuf_ array should not be used. // Indicates that the dataBuf_ array should not be used.
bool ignoreDataBuf_; bool ignoreDataBuf_;
@ -536,6 +539,15 @@ struct Atrac {
#endif // USE_FFMPEG #endif // USE_FFMPEG
} }
void CalculateStreamInfo(u32 *readOffset);
u32 StreamBufferEnd() {
// 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_;
}
private: private:
void AnalyzeReset(); void AnalyzeReset();
}; };
@ -949,7 +961,6 @@ u32 _AtracAddStreamData(int atracID, u32 bufPtr, u32 bytesToAdd) {
atrac->bufferState_ = ATRAC_STATUS_ALL_DATA_LOADED; atrac->bufferState_ = ATRAC_STATUS_ALL_DATA_LOADED;
} }
atrac->first_.fileoffset += addbytes; atrac->first_.fileoffset += addbytes;
atrac->first_.writableBytes = 0;
if (atrac->context_.IsValid()) { if (atrac->context_.IsValid()) {
// refresh context_ // refresh context_
_AtracGenerateContext(atrac, atrac->context_); _AtracGenerateContext(atrac, atrac->context_);
@ -971,30 +982,49 @@ static u32 AtracValidateManaged(const Atrac *atrac) {
} }
} }
static void AtracGetStreamDataInfo(Atrac *atrac, u32 &readOffset) { void Atrac::CalculateStreamInfo(u32 *outReadOffset) {
readOffset = atrac->first_.fileoffset; u32 readOffset = first_.fileoffset;
if (atrac->bufferState_ == ATRAC_STATUS_ALL_DATA_LOADED) { if (bufferState_ == ATRAC_STATUS_ALL_DATA_LOADED) {
// Nothing to write. // Nothing to write.
readOffset = 0; readOffset = 0;
atrac->first_.offset = 0; first_.offset = 0;
atrac->first_.writableBytes = 0; first_.writableBytes = 0;
} else if (atrac->bufferState_ == ATRAC_STATUS_HALFWAY_BUFFER) { } else if (bufferState_ == ATRAC_STATUS_HALFWAY_BUFFER) {
// If we're buffering the entire file, just give the same as readOffset. // If we're buffering the entire file, just give the same as readOffset.
atrac->first_.offset = readOffset; first_.offset = readOffset;
// In this case, the bytes writable are just the remaining bytes, always. // In this case, the bytes writable are just the remaining bytes, always.
atrac->first_.writableBytes = atrac->first_.filesize - readOffset; first_.writableBytes = first_.filesize - readOffset;
} else { } else {
// TODO u32 bufferEnd = StreamBufferEnd();
atrac->first_.offset = 0; 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 >= atrac->first_.filesize) { // If you don't think this should be here, remove it. It's just a temporary safety check.
if (atrac->bufferState_ == ATRAC_STATUS_STREAMED_WITHOUT_LOOP) { 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 (readOffset >= first_.filesize) {
if (bufferState_ == ATRAC_STATUS_STREAMED_WITHOUT_LOOP) {
readOffset = 0; readOffset = 0;
} else { } else {
readOffset = atrac->dataOff_; readOffset = dataOff_;
} }
} }
} }
if (outReadOffset) {
*outReadOffset = readOffset;
}
} }
// Notifies that more data is (OR will be very soon) available in the buffer. // Notifies that more data is (OR will be very soon) available in the buffer.
@ -1016,8 +1046,7 @@ static u32 sceAtracAddStreamData(int atracID, u32 bytesToAdd) {
return hleLogWarning(ME, ATRAC_ERROR_ALL_DATA_LOADED, "stream entirely loaded"); return hleLogWarning(ME, ATRAC_ERROR_ALL_DATA_LOADED, "stream entirely loaded");
} }
u32 readOffset; atrac->CalculateStreamInfo(nullptr);
AtracGetStreamDataInfo(atrac, readOffset);
if (bytesToAdd > atrac->first_.writableBytes) if (bytesToAdd > atrac->first_.writableBytes)
return hleLogWarning(ME, ATRAC_ERROR_ADD_DATA_IS_TOO_BIG, "too many bytes"); return hleLogWarning(ME, ATRAC_ERROR_ADD_DATA_IS_TOO_BIG, "too many bytes");
@ -1038,8 +1067,9 @@ static u32 sceAtracAddStreamData(int atracID, u32 bytesToAdd) {
_AtracGenerateContext(atrac, atrac->context_); _AtracGenerateContext(atrac, atrac->context_);
} }
} }
atrac->first_.writableBytes -= bytesToAdd;
atrac->first_.offset += bytesToAdd; atrac->first_.offset += bytesToAdd;
atrac->bufferValidBytes_ += bytesToAdd;
return hleLogSuccessI(ME, 0); return hleLogSuccessI(ME, 0);
} }
@ -1094,8 +1124,6 @@ u32 _AtracDecodeData(int atracID, u8 *outbuf, u32 outbufPtr, u32 *SamplesNum, u3
if (res == ATDECODE_GOTFRAME) { if (res == ATDECODE_GOTFRAME) {
#ifdef USE_FFMPEG #ifdef USE_FFMPEG
// got a frame // got a frame
// Use a small buffer and keep overwriting it with file data constantly
atrac->first_.writableBytes += atrac->bytesPerFrame_;
int skipped = std::min(skipSamples, atrac->frame_->nb_samples); int skipped = std::min(skipSamples, atrac->frame_->nb_samples);
skipSamples -= skipped; skipSamples -= skipped;
numSamples = atrac->frame_->nb_samples - skipped; numSamples = atrac->frame_->nb_samples - skipped;
@ -1157,6 +1185,18 @@ u32 _AtracDecodeData(int atracID, u8 *outbuf, u32 outbufPtr, u32 *SamplesNum, u3
atrac->currentSample_ += numSamples; atrac->currentSample_ += numSamples;
atrac->decodePos_ = atrac->DecodePosBySample(atrac->currentSample_); atrac->decodePos_ = atrac->DecodePosBySample(atrac->currentSample_);
atrac->bufferPos_ += atrac->bytesPerFrame_;
if (atrac->bufferValidBytes_ > atrac->bytesPerFrame_) {
atrac->bufferValidBytes_ -= atrac->bytesPerFrame_;
} else {
atrac->bufferValidBytes_ = 0;
}
if (atrac->bufferPos_ >= atrac->StreamBufferEnd()) {
// Wrap around... theoretically, this should only happen at exactly StreamBufferEnd.
atrac->bufferPos_ -= atrac->StreamBufferEnd();
atrac->bufferHeaderSize_ = 0;
}
int finishFlag = 0; int finishFlag = 0;
// TODO: Verify. // TODO: Verify.
bool hitEnd = atrac->currentSample_ >= atrac->endSample_ || (numSamples == 0 && atrac->first_.size >= atrac->first_.filesize); bool hitEnd = atrac->currentSample_ >= atrac->endSample_ || (numSamples == 0 && atrac->first_.size >= atrac->first_.filesize);
@ -1472,14 +1512,10 @@ static u32 sceAtracGetRemainFrame(int atracID, u32 remainAddr) {
if (!remainingFrames.IsValid()) { if (!remainingFrames.IsValid()) {
// Would crash. // Would crash.
return hleReportError(ME, SCE_KERNEL_ERROR_ILLEGAL_ADDR, "invalid remainingFrames pointer"); return hleReportError(ME, SCE_KERNEL_ERROR_ILLEGAL_ADDR, "invalid remainingFrames pointer");
} else {
*remainingFrames = atrac->RemainingFrames();
// Let sceAtracGetStreamDataInfo() know to set the full filled buffer.
atrac->resetBuffer_ = true;
return hleLogSuccessI(ME, 0);
} }
return 0;
*remainingFrames = atrac->RemainingFrames();
return hleLogSuccessI(ME, 0);
} }
static u32 sceAtracGetSecondBufferInfo(int atracID, u32 fileOffsetAddr, u32 desiredSizeAddr) { static u32 sceAtracGetSecondBufferInfo(int atracID, u32 fileOffsetAddr, u32 desiredSizeAddr) {
@ -1546,16 +1582,8 @@ static u32 sceAtracGetStreamDataInfo(int atracID, u32 writePtrAddr, u32 writable
return err; return err;
} }
// TODO: Is this check even needed? More testing is needed on writableBytes.
if (atrac->resetBuffer_) {
// Reset temp buf for adding more stream data and set full filled buffer.
atrac->first_.writableBytes = std::min(atrac->first_.filesize - atrac->first_.size, atrac->bufferMaxSize_);
} else {
atrac->first_.writableBytes = std::min(atrac->first_.filesize - atrac->first_.size, atrac->first_.writableBytes);
}
u32 readOffset; u32 readOffset;
AtracGetStreamDataInfo(atrac, readOffset); atrac->CalculateStreamInfo(&readOffset);
if (Memory::IsValidAddress(writePtrAddr)) if (Memory::IsValidAddress(writePtrAddr))
Memory::Write_U32(atrac->first_.addr + atrac->first_.offset, writePtrAddr); Memory::Write_U32(atrac->first_.addr + atrac->first_.offset, writePtrAddr);
@ -1636,8 +1664,11 @@ static u32 sceAtracResetPlayPosition(int atracID, int sample, int bytesWrittenFi
atrac->first_.fileoffset += bytesWrittenFirstBuf; atrac->first_.fileoffset += bytesWrittenFirstBuf;
} }
atrac->first_.size = atrac->first_.fileoffset; atrac->first_.size = atrac->first_.fileoffset;
atrac->first_.writableBytes = bufferInfo.first.writableBytes - bytesWrittenFirstBuf;
atrac->first_.offset = bytesWrittenFirstBuf; atrac->first_.offset = bytesWrittenFirstBuf;
atrac->bufferHeaderSize_ = 0;
atrac->bufferPos_ = atrac->bytesPerFrame_;
atrac->bufferValidBytes_ = bytesWrittenFirstBuf;
} }
if (atrac->codecType_ == PSP_MODE_AT_3 || atrac->codecType_ == PSP_MODE_AT_3_PLUS) { if (atrac->codecType_ == PSP_MODE_AT_3 || atrac->codecType_ == PSP_MODE_AT_3_PLUS) {
@ -1766,9 +1797,8 @@ static int _AtracSetData(Atrac *atrac, u32 buffer, u32 readSize, u32 bufferSize)
atrac->first_.size = atrac->first_.filesize; atrac->first_.size = atrac->first_.filesize;
atrac->first_.fileoffset = atrac->first_.size; atrac->first_.fileoffset = atrac->first_.size;
// got the size of temp buf, and calculate writableBytes and offset // got the size of temp buf, and calculate offset
atrac->bufferMaxSize_ = bufferSize; atrac->bufferMaxSize_ = bufferSize;
atrac->first_.writableBytes = (u32)std::max((int)bufferSize - (int)atrac->first_.size, 0);
atrac->first_.offset = atrac->first_.size; atrac->first_.offset = atrac->first_.size;
// some games may reuse an atracID for playing sound // some games may reuse an atracID for playing sound
@ -1787,6 +1817,11 @@ static int _AtracSetData(Atrac *atrac, u32 buffer, u32 readSize, u32 bufferSize)
// TODO: Support this always, even for streaming. // TODO: Support this always, even for streaming.
atrac->ignoreDataBuf_ = true; atrac->ignoreDataBuf_ = true;
} }
if (atrac->bufferState_ == ATRAC_STATUS_STREAMED_WITHOUT_LOOP || atrac->bufferState_ == ATRAC_STATUS_STREAMED_LOOP_FROM_END || atrac->bufferState_ == ATRAC_STATUS_STREAMED_LOOP_WITH_TRAILER) {
atrac->bufferHeaderSize_ = atrac->dataOff_;
atrac->bufferPos_ = atrac->dataOff_ + atrac->bytesPerFrame_;
atrac->bufferValidBytes_ = atrac->first_.size - atrac->bufferPos_;
}
const char *codecName = atrac->codecType_ == PSP_MODE_AT_3 ? "atrac3" : "atrac3+"; const char *codecName = atrac->codecType_ == PSP_MODE_AT_3 ? "atrac3" : "atrac3+";
const char *channelName = atrac->channels_ == 1 ? "mono" : "stereo"; const char *channelName = atrac->channels_ == 1 ? "mono" : "stereo";