diff --git a/libs/JSystem/include/JSystem/JAudio2/JASAramStream.h b/libs/JSystem/include/JSystem/JAudio2/JASAramStream.h index dc9dea9645..dcfcebd30a 100644 --- a/libs/JSystem/include/JSystem/JAudio2/JASAramStream.h +++ b/libs/JSystem/include/JSystem/JAudio2/JASAramStream.h @@ -4,6 +4,7 @@ #include "JSystem/JAudio2/JASTaskThread.h" #include "JSystem/JUtility/JUTAssert.h" #include +#include "dusk/endian.h" class JASChannel; @@ -11,9 +12,12 @@ namespace JASDsp { struct TChannel; } +#define STREAM_FORMAT_ADPCM4 0 +#define STREAM_FORMAT_PCM16 1 + /** * @ingroup jsystem-jaudio - * + * Plays streamed music from DVD .ast files. */ class JASAramStream { public: @@ -29,8 +33,8 @@ public: // Used internally for passing data to task functions struct TaskData { /* 0x0 */ JASAramStream* stream; - /* 0x4 */ u32 field_0x4; - /* 0x8 */ int field_0x8; + /* 0x4 */ u32 param0; + /* 0x8 */ int param1; }; struct Header { @@ -73,6 +77,10 @@ public: bool stop(u16); bool pause(bool); bool cancel(); + + /** + * Calculate the amount of (decoded) audio samples in a single block of streamed audio. + */ u32 getBlockSamples() const; static void headerLoadTask(void*); static void firstLoadTask(void*); @@ -136,33 +144,92 @@ public: static u32 getBlockSize() { return sBlockSize; } - /* 0x000 */ OSMessageQueue field_0x000; - /* 0x020 */ OSMessageQueue field_0x020; - /* 0x040 */ void* field_0x040[16]; - /* 0x080 */ void* field_0x080[4]; + /** + * Queue used to send specific commands that will be processed on the audio thread. + * These commands are sent from the main thread. + */ + /* 0x000 */ OSMessageQueue mMainCommandQueue; + + /** + * Queue used to send specific commands that will be processed on the audio thread. + * These commands are sent from the load (DVD) thread. + */ + /* 0x020 */ OSMessageQueue mLoadCommandQueue; + + /** + * Backing message storage for mMainCommandQueue. + */ + /* 0x040 */ void* mMainCommandQueueArray[16]; + + /** + * Backing message storage for mLoadCommandQueue. + */ + /* 0x080 */ void* mLoadCommandQueueArray[4]; /* 0x090 */ JASChannel* mChannels[CHANNEL_MAX]; - /* 0x0A8 */ JASChannel* mInitialChannel; - /* 0x0AC */ bool field_0x0ac; - /* 0x0AD */ bool field_0x0ad; - /* 0x0AE */ u8 field_0x0ae; + + /** + * The first audio channel initialized among mChannels. + * Used for the majority of bookkeeping, other channels replicate its state. + */ + /* 0x0A8 */ JASChannel* mPrimaryChannel; + + /** + * If true, stream has finished preparing (reading headers and initial blocks), + * and is ready to play. + */ + /* 0x0AC */ bool mPrepareFinished; + /* 0x0AD */ bool mLoopEndLoaded; + + /** + * Bitflag containing pause reasons/state for the stream. + */ + /* 0x0AE */ u8 mPauseFlags; /* 0x0B0 */ int field_0x0b0; - /* 0x0B4 */ int field_0x0b4; - /* 0x0B8 */ u32 field_0x0b8; + + /** + * (adjusted) value of mSamplesLeft on the primary channel last subframe. + * Used to calculate how many samples have been read and determine when the DSP looped. + */ + /* 0x0B4 */ int mLastSamplesLeft; + + /** + * How many (decoded) samples the DSP has read so far. + */ + /* 0x0B8 */ u32 mReadSample; /* 0x0BC */ int field_0x0bc; - /* 0x0C0 */ bool field_0x0c0; + + /** + * If true, the current end (of loop, or just finish) is very close. + * Loop start/end positions are modified while this is set to account for this. + */ + /* 0x0C0 */ bool mEndSetup; /* 0x0C4 */ volatile u32 field_0x0c4; /* 0x0C8 */ volatile f32 field_0x0c8; /* 0x0CC */ DVDFileInfo mDvdFileInfo; - /* 0x108 */ u32 field_0x108; - /* 0x10C */ int field_0x10c; + /* 0x108 */ u32 mRingEndIndex; + + /** + * Index into the ARAM ring buffer that is currently being loaded. + * Wrapped around when incremented. + */ + /* 0x10C */ int mBlockRingIndex; + + /** + * Block currently being loaded. + */ /* 0x110 */ u32 mBlock; - /* 0x114 */ u8 field_0x114; - /* 0x118 */ u32 field_0x118; - /* 0x11C */ int field_0x11c; - /* 0x120 */ int field_0x120; - /* 0x124 */ int field_0x124; - /* 0x128 */ u16 field_0x128; - /* 0x12C */ int field_0x12c; + /* 0x114 */ u8 mIsCancelled; + /* 0x118 */ u32 mPendingLoadTasks; + /* 0x11C */ int mUpdateSamplesLeft; + /* 0x120 */ int mUpdateLoopStartSample; + /* 0x124 */ int mUpdateEndSample; + /* 0x128 */ u16 mUpdateLoopFlag; + + /** + * Bitflags updated in the play callback to track what data needs to be synchronized + * between all channels. + */ + /* 0x12C */ int mChannelUpdateFlags; /* 0x130 */ s16 mpLasts[CHANNEL_MAX]; /* 0x13C */ s16 mpPenults[CHANNEL_MAX]; /* 0x148 */ int mAramAddress; @@ -185,9 +252,27 @@ public: /* 0x1C4 */ f32 mChannelDolby[CHANNEL_MAX]; /* 0x1DC */ u16 mMixConfig[CHANNEL_MAX]; + /** + * Thread that will be sent DVD load commands. + * This is the JASDvd thread in practice. + */ static JASTaskThread* sLoadThread; + + /** + * Buffer used to read DVD data. Can store the size of an entire streamed audio block. + */ static u8* sReadBuffer; + + /** + * Block size used by all streamed music in the game. + * This is 0x2760 for TP. + */ static u32 sBlockSize; + + /** + * Maximum amount of output channels for all streamed music in the game. + * This is 2 for TP (stereo). + */ static u32 sChannelMax; }; diff --git a/libs/JSystem/include/JSystem/JAudio2/JASDSPInterface.h b/libs/JSystem/include/JSystem/JAudio2/JASDSPInterface.h index 9bb9eecb16..8f1ca3dd72 100644 --- a/libs/JSystem/include/JSystem/JAudio2/JASDSPInterface.h +++ b/libs/JSystem/include/JSystem/JAudio2/JASDSPInterface.h @@ -94,11 +94,29 @@ namespace JASDsp { void setDistFilter(s16); void setBusConnect(u8 outputChannel, u8 param_1); + /** + * Whether this channel is currently actively playing audio. + */ /* 0x000 */ u16 mIsActive; + + /** + * Written by DSP to indicate playback has finished. + */ /* 0x002 */ u16 mIsFinished; + + /** + * Pitch shift via changing playback speed. + */ /* 0x004 */ u16 mPitch; /* 0x006 */ short _unused1; - /* 0x008 */ u16 field_0x008; + + /** + * Set to 1 when playback starts, cleared by DSP later, + * checked by JASAramStream before actually doing processing. + * Presumably to instruct DSP to clear state? + * (Corroborated by fields JASAramStream checks never being cleared explicitly by CPU.) + */ + /* 0x008 */ u16 mResetFlag; /* 0x00A */ u8 _unused2[0x00C - 0x00A]; /* 0x00C */ s16 mPauseFlag; /* 0x00E */ short _unused3; @@ -112,19 +130,33 @@ namespace JASDsp { /* 0x05A */ u8 _unused5[0x060 - 0x05A]; /* 0x060 */ short field_0x060; // Only cleared to zero, presumed used by DSP. /* 0x062 */ u8 _unused6[0x064 - 0x062]; + + /** + * Samples per ADPCM frame for ADPCM audio. Seems just set to 1 for PCM formats. + * Name could use improvement, probably? + */ /* 0x064 */ u16 mSamplesPerBlock; /* 0x066 */ short field_0x066; // Only cleared to zero, presumed used by DSP. - /* 0x068 */ int mSamplePosition; // Only ever initialized by code, name is guess. + /* 0x068 */ u32 mSamplePosition; // Only ever initialized by code, name is guess. /* 0x06C */ u8 _unused7[0x070 - 0x06C]; - /* 0x070 */ int mAramStreamPosition; // Seems written by DSP, used for audio streaming. - /* 0x074 */ int field_0x074; + + /** + * Current audio read position in ARAM. Updated by DSP. + */ + /* 0x070 */ u32 mAramStreamPosition; + + /** + * Amount of (decoded) audio samples left until the end of the buffer. + * Gets written by DSP, but also CPU. + */ + /* 0x074 */ u32 mSamplesLeft; // Never directly cleared to zero. Seems sus. Cleared by DSP? /* 0x078 */ short field_0x078[4]; // Only cleared to zero, presumed used by DSP. /* 0x080 */ short field_0x080[20]; // Only cleared to zero, presumed used by DSP. /* 0x0A8 */ short field_0x0a8[4]; // Only cleared to zero, presumed used by DSP. /* 0x0B0 */ u16 field_0x0b0[16]; // Only cleared to zero, presumed used by DSP. /* 0x0D0 */ u8 _unused8[0x100 - 0x0D0]; /* 0x100 */ u16 mBytesPerBlock; - /* 0x102 */ u16 field_0x102; + /* 0x102 */ u16 mLoopFlag; /** * Used for decoding ADPCM data around loop edges. diff --git a/libs/JSystem/src/JAudio2/JASAramStream.cpp b/libs/JSystem/src/JAudio2/JASAramStream.cpp index 77de8e17f7..30d4d8da7a 100644 --- a/libs/JSystem/src/JAudio2/JASAramStream.cpp +++ b/libs/JSystem/src/JAudio2/JASAramStream.cpp @@ -1,11 +1,12 @@ #include "JSystem/JSystem.h" // IWYU pragma: keep -#include "JSystem/JAudio2/JASAramStream.h" #include "JSystem/JAudio2/JASAiCtrl.h" +#include "JSystem/JAudio2/JASAramStream.h" +#include "JSystem/JAudio2/JASAudioThread.h" #include "JSystem/JAudio2/JASChannel.h" #include "JSystem/JAudio2/JASCriticalSection.h" -#include "JSystem/JAudio2/JASDriverIF.h" #include "JSystem/JAudio2/JASDSPInterface.h" +#include "JSystem/JAudio2/JASDriverIF.h" #include "JSystem/JAudio2/JASDvdThread.h" #include "JSystem/JKernel/JKRAram.h" #include "JSystem/JKernel/JKRSolidHeap.h" @@ -22,6 +23,21 @@ u32 JASAramStream::sChannelMax; bool dvdHasErrored; bool hasErrored; +#define PAUSE_REQUESTED 1 +#define PAUSE_DVD_ERROR 2 +#define PAUSE_UNDERFLOW 4 +#define PAUSE_OTHER_ERROR 8 + +// CMDs for mMainCommandQueue. +#define CMD_START 0 +#define CMD_STOP 1 // upper 16 bits of cmd contain oscillator direct release value. +#define CMD_PAUSE 2 +#define CMD_UNPAUSE 3 + +// CMDs for mLoadCommandQueue +#define CMD_PREPARE_FINISHED 4 +#define CMD_LOOP_END_LOADED 5 + void JASAramStream::initSystem(u32 block_size, u32 channel_max) { JUT_ASSERT(66, block_size % 32 == 0); JUT_ASSERT(67, block_size % 9 == 0); @@ -33,6 +49,10 @@ void JASAramStream::initSystem(u32 block_size, u32 channel_max) { if (sLoadThread == NULL) { sLoadThread = JASDvd::getThreadPointer(); } + + // Pretty sure this 0x20 corresponds to sizeof(BlockHeader). + // But that shouldn't be getting multiplied by the channel count. + // Bug in the original game, I guess? sReadBuffer = JKR_NEW_ARRAY_ARGS(u8, (block_size + 0x20) * channel_max, JASDram, 0x20); JUT_ASSERT(79, sReadBuffer); sBlockSize = block_size; @@ -43,23 +63,23 @@ void JASAramStream::initSystem(u32 block_size, u32 channel_max) { } JASAramStream::JASAramStream() { - mInitialChannel = NULL; - field_0x0ac = false; - field_0x0ad = false; - field_0x0ae = 0; + mPrimaryChannel = NULL; + mPrepareFinished = false; + mLoopEndLoaded = false; + mPauseFlags = 0; field_0x0b0 = 0; - field_0x0b4 = 0; - field_0x0b8 = 0; + mLastSamplesLeft = 0; + mReadSample = 0; field_0x0bc = 0; - field_0x0c0 = false; + mEndSetup = false; field_0x0c4 = 0; field_0x0c8 = 0.0f; - field_0x108 = 0; - field_0x10c = 0; + mRingEndIndex = 0; + mBlockRingIndex = 0; mBlock = 0; - field_0x114 = 0; - field_0x118 = 0; - field_0x12c = 0; + mIsCancelled = 0; + mPendingLoadTasks = 0; + mChannelUpdateFlags = 0; mAramAddress = 0; mAramSize = 0; mCallback = NULL; @@ -93,10 +113,10 @@ void JASAramStream::init(u32 aramAddress, u32 aramSize, StreamCallback i_callbac mAramAddress = aramAddress; mAramSize = aramSize; field_0x0c8 = 0.0f; - field_0x0ae = 0; - field_0x0ac = false; - field_0x0ad = false; - field_0x114 = 0; + mPauseFlags = 0; + mPrepareFinished = false; + mLoopEndLoaded = false; + mIsCancelled = 0; mChannelNum = 0; for (int i = 0; i < 6; i++) { mChannelVolume[i] = 1.0f; @@ -109,8 +129,8 @@ void JASAramStream::init(u32 aramAddress, u32 aramSize, StreamCallback i_callbac mMixConfig[0] = 0xffff; mCallback = i_callback; mCallbackData = i_callbackData; - OSInitMessageQueue(&field_0x000, field_0x040, 0x10); - OSInitMessageQueue(&field_0x020, field_0x080, 4); + OSInitMessageQueue(&mMainCommandQueue, mMainCommandQueueArray, ARRAY_SIZEU(mMainCommandQueueArray)); + OSInitMessageQueue(&mLoadCommandQueue, mLoadCommandQueueArray, ARRAY_SIZEU(mLoadCommandQueueArray)); } bool JASAramStream::prepare(s32 param_0, int param_1) { @@ -124,8 +144,8 @@ bool JASAramStream::prepare(s32 param_0, int param_1) { } TaskData data; data.stream = this; - data.field_0x4 = mAramSize; - data.field_0x8 = param_1; + data.param0 = mAramSize; + data.param1 = param_1; if (!sLoadThread->sendCmdMsg(headerLoadTask, &data, sizeof(data))) { JUT_WARN(254, "%s", "sendCmdMsg headerLoadTask Failed"); JASDriver::rejectCallback(channelProcCallback, this); @@ -135,24 +155,24 @@ bool JASAramStream::prepare(s32 param_0, int param_1) { } bool JASAramStream::start() { - if (!OSSendMessage(&field_0x000, (OSMessage)0, OS_MESSAGE_NOBLOCK)) { + if (!OSSendMessage(&mMainCommandQueue, (OSMessage)CMD_START, OS_MESSAGE_NOBLOCK)) { JUT_WARN(273, "%s", "OSSendMessage Failed") return false; } return true; } -bool JASAramStream::stop(u16 param_0) { - if (!OSSendMessage(&field_0x000, (OSMessage)(uintptr_t)(param_0 << 0x10 | 1), OS_MESSAGE_NOBLOCK)) { +bool JASAramStream::stop(u16 directRelease) { + if (!OSSendMessage(&mMainCommandQueue, (OSMessage)(uintptr_t)(directRelease << 0x10 | CMD_STOP), OS_MESSAGE_NOBLOCK)) { JUT_WARN(290, "%s", "OSSendMessage Failed"); return false; } return true; } -bool JASAramStream::pause(bool param_0) { - OSMessage msg = param_0 ? (OSMessage)2 : (OSMessage)3; - if (!OSSendMessage(&field_0x000, msg, OS_MESSAGE_NOBLOCK)) { +bool JASAramStream::pause(bool newPauseFlag) { + OSMessage msg = newPauseFlag ? (OSMessage)CMD_PAUSE : (OSMessage)CMD_UNPAUSE; + if (!OSSendMessage(&mMainCommandQueue, msg, OS_MESSAGE_NOBLOCK)) { JUT_WARN(308, "%s", "OSSendMessage Failed"); return false; } @@ -160,7 +180,7 @@ bool JASAramStream::pause(bool param_0) { } bool JASAramStream::cancel() { - field_0x114 = 1; + mIsCancelled = 1; if (!sLoadThread->sendCmdMsg(finishTask, this)) { JUT_WARN(326, "%s", "sendCmdMsg finishTask Failed"); return false; @@ -169,12 +189,12 @@ bool JASAramStream::cancel() { } u32 JASAramStream::getBlockSamples() const { - return mFormat == 0 ? (sBlockSize << 4) / 9 : sBlockSize >> 1; + return mFormat == STREAM_FORMAT_ADPCM4 ? (sBlockSize << 4) / 9 : sBlockSize >> 1; } void JASAramStream::headerLoadTask(void* i_data) { TaskData* data = (TaskData*)i_data; - data->stream->headerLoad(data->field_0x4, data->field_0x8); + data->stream->headerLoad(data->param0, data->param1); } void JASAramStream::firstLoadTask(void* i_data) { @@ -183,23 +203,23 @@ void JASAramStream::firstLoadTask(void* i_data) { if (!_this->load()) { return; } - if (data->field_0x8 > 0) { - data->field_0x8--; - if (data->field_0x8 == 0) { + if (data->param1 > 0) { + data->param1--; + if (data->param1 == 0) { if (!sLoadThread->sendCmdMsg(prepareFinishTask, _this)) { JUT_WARN(364, "%s", "sendCmdMsg prepareFinishTask Failed"); hasErrored = true; } } } - if (data->field_0x4 != 0) { - data->field_0x4--; + if (data->param0 != 0) { + data->param0--; if (!sLoadThread->sendCmdMsg(firstLoadTask, data, sizeof(*data))) { JUT_WARN(372, "%s", "sendCmdMsg firstLoadTask Failed"); hasErrored = true; } JASCriticalSection cs; - _this->field_0x118++; + _this->mPendingLoadTasks++; } } @@ -221,7 +241,7 @@ void JASAramStream::finishTask(void* i_this) { void JASAramStream::prepareFinishTask(void* i_this) { JASAramStream* _this = (JASAramStream*)i_this; - OSSendMessage(&_this->field_0x020, (OSMessage)4, OS_MESSAGE_BLOCK); + OSSendMessage(&_this->mLoadCommandQueue, (OSMessage)CMD_PREPARE_FINISHED, OS_MESSAGE_BLOCK); if (_this->mCallback != NULL) { _this->mCallback(CB_STOP, _this, _this->mCallbackData); } @@ -231,7 +251,7 @@ bool JASAramStream::headerLoad(u32 aramSize, int param_1) { if (hasErrored) { return false; } - if (field_0x114 != 0) { + if (mIsCancelled != 0) { return false; } if (DVDReadPrio(&mDvdFileInfo, sReadBuffer, sizeof(Header), 0, 1) < 0) { @@ -252,9 +272,9 @@ bool JASAramStream::headerLoad(u32 aramSize, int param_1) { mLoopStart = header->loop_start; mLoopEnd = header->loop_end; mVolume = header->mVolume / 127.0f; - field_0x118 = 0; + mPendingLoadTasks = 0; mBlock = 0; - field_0x10c = 0; + mBlockRingIndex = 0; mAramBlocksPerChannel = (aramSize / sBlockSize) / header->channels; mBufCount = mAramBlocksPerChannel; JUT_ASSERT(445, mBufCount > 0); @@ -262,28 +282,28 @@ bool JASAramStream::headerLoad(u32 aramSize, int param_1) { if (mBufCount < 3) { JUT_WARN(449, "%s", "Too few Buffer-Size"); } - field_0x108 = mBufCount; + mRingEndIndex = mBufCount; u32 local_2c = (mLoopEnd - 1) / getBlockSamples(); if (local_2c <= mBufCount && mLoop) { JUT_WARN(458, "%s", "Too few samples for Loop-buffer"); } - if (param_1 < 0 || param_1 > field_0x108) { - param_1 = field_0x108; + if (param_1 < 0 || param_1 > mRingEndIndex) { + param_1 = mRingEndIndex; } - if (field_0x114 != 0) { + if (mIsCancelled != 0) { return false; } TaskData data; data.stream = this; - data.field_0x4 = field_0x108 - 1; - data.field_0x8 = param_1; + data.param0 = mRingEndIndex - 1; + data.param1 = param_1; if (!sLoadThread->sendCmdMsg(firstLoadTask, &data, sizeof(data))) { JUT_WARN(472, "%s", "sendCmdMsg firstLoadTask Failed"); hasErrored = true; return false; } JASCriticalSection cs; - field_0x118++; + mPendingLoadTasks++; return true; } @@ -291,12 +311,12 @@ bool JASAramStream::headerLoad(u32 aramSize, int param_1) { bool JASAramStream::load() { { JASCriticalSection cs; - field_0x118--; + mPendingLoadTasks--; } if (hasErrored) { return false; } - if (field_0x114 != 0) { + if (mIsCancelled != 0) { return false; } u32 loop_end_block = (mLoopEnd - 1) / getBlockSamples(); @@ -316,26 +336,26 @@ bool JASAramStream::load() { } BlockHeader* bhead = (BlockHeader*)sReadBuffer; JUT_ASSERT(512, bhead->tag == 'BLCK'); - if (field_0x114 != 0) { + if (mIsCancelled != 0) { return false; } - u32 sp08 = mAramAddress + field_0x10c * sBlockSize; + u32 blockBaseOffset = mAramAddress + mBlockRingIndex * sBlockSize; for (int i = 0; i < mChannelNum; i++) { (void)i; // Fakematch? It seems the only way to get the bhead->field_0x4 load in the right order is // with a pointer cast on its address in one of the two places it is read, but not both. if (!JKRMainRamToAram(sReadBuffer + bhead->mSize * i + sizeof(BlockHeader), - sp08 + sBlockSize * mAramBlocksPerChannel * i, + blockBaseOffset + sBlockSize * mAramBlocksPerChannel * i, bhead->mSize, EXPAND_SWITCH_UNKNOWN0, 0, NULL, -1, NULL)) { JUT_WARN(522, "%s", "JKRMainRamToAram Failed"); hasErrored = 1; return false; } } - field_0x10c++; - if (field_0x10c >= field_0x108) { + mBlockRingIndex++; + if (mBlockRingIndex >= mRingEndIndex) { u32 r28 = mBlock; - r28 += field_0x108 - 1; + r28 += mRingEndIndex - 1; if (mLoop) { JUT_ASSERT(537, loop_start_block < loop_end_block); while (r28 > loop_end_block) { @@ -344,16 +364,16 @@ bool JASAramStream::load() { } } if (r28 == loop_end_block || r28 + 2 == loop_end_block) { - field_0x108 = mAramBlocksPerChannel; - OSSendMessage(&field_0x020, (OSMessage)5, OS_MESSAGE_BLOCK); + mRingEndIndex = mAramBlocksPerChannel; + OSSendMessage(&mLoadCommandQueue, (OSMessage)CMD_LOOP_END_LOADED, OS_MESSAGE_BLOCK); } else { - field_0x108 = mAramBlocksPerChannel - 1; + mRingEndIndex = mAramBlocksPerChannel - 1; } for (int i = 0; i < mChannelNum; i++) { mpLasts[i] = (s16)bhead->mAdpcmContinuationData[i].mpLast; mpPenults[i] = (s16)bhead->mAdpcmContinuationData[i].mpPenult; } - field_0x10c = 0; + mBlockRingIndex = 0; } mBlock++; if (mBlock > loop_end_block && mLoop) { @@ -397,80 +417,105 @@ void JASAramStream::channelCallback(u32 i_callbackType, JASChannel* i_channel, stream->updateChannel(i_callbackType, i_channel, i_dspChannel); } +#define CHANNEL_UPDATE_SAMPLES_LEFT 1 +#define CHANNEL_UPDATE_LOOP_START 2 +#define CHANNEL_UPDATE_END_SAMPLE 4 +#define CHANNEL_UPDATE_LOOP_FLAG 8 + + void JASAramStream::updateChannel(u32 i_callbackType, JASChannel* i_channel, JASDsp::TChannel* i_dspChannel) { u32 block_samples = getBlockSamples(); switch (i_callbackType) { case JASChannel::CB_START: - if (mInitialChannel == NULL) { - mInitialChannel = i_channel; - field_0x0b4 = block_samples * mBufCount; - field_0x0b8 = 0; + if (mPrimaryChannel == NULL) { + mPrimaryChannel = i_channel; + mLastSamplesLeft = block_samples * mBufCount; + mReadSample = 0; field_0x0b0 = 0; field_0x0bc = (mLoopEnd - 1) / block_samples; - field_0x0c0 = 0; + mEndSetup = 0; field_0x0c4 = 0; - field_0x12c = 0; + mChannelUpdateFlags = 0; } break; case JASChannel::CB_PLAY: - if (i_dspChannel->field_0x008 == 0) { - if (i_channel == mInitialChannel) { - field_0x12c = 0; - u32 sp28 = i_dspChannel->field_0x074 + i_dspChannel->mSamplesPerBlock; - if (sp28 <= field_0x0b4) { - field_0x0b8 += field_0x0b4 - sp28; + if (i_dspChannel->mResetFlag == 0) { + if (i_channel == mPrimaryChannel) { + if (JASAudioThread::snIntCount == 1) { + OSReportForceEnableOn(); + OSReport("mSamplesLeft: %08d, mAramStreamPosition: %08d\n", i_dspChannel->mSamplesLeft, i_dspChannel->mAramStreamPosition); + } + + mChannelUpdateFlags = 0; + u32 adjustedSamplesLeft = i_dspChannel->mSamplesLeft + i_dspChannel->mSamplesPerBlock; + if (adjustedSamplesLeft <= mLastSamplesLeft) { + mReadSample += mLastSamplesLeft - adjustedSamplesLeft; } else { - if (!field_0x0c0) { - field_0x0b8 += field_0x0b4; - field_0x0b8 += block_samples * mBufCount - sp28; + // The DSP has looped. + + if (!mEndSetup) { + // Just looping the ring buffer, data continues as normal. + mReadSample += mLastSamplesLeft; + mReadSample += block_samples * mBufCount - adjustedSamplesLeft; } else { - field_0x0b8 += field_0x0b4; - field_0x0b8 += block_samples * mBufCount - sp28 + // We hit the actual file loop position. + mReadSample += mLastSamplesLeft; + mReadSample += block_samples * mBufCount - adjustedSamplesLeft - i_dspChannel->mLoopStartSample; - field_0x0b8 -= mLoopEnd; - field_0x0b8 += mLoopStart; + mReadSample -= mLoopEnd; + mReadSample += mLoopStart; i_dspChannel->mLoopStartSample = 0; - field_0x120 = 0; - field_0x12c |= 2; + mUpdateLoopStartSample = 0; + mChannelUpdateFlags |= CHANNEL_UPDATE_LOOP_START; +#if !TARGET_PC // The variable assigned here is never used. if (field_0x0c4 < 0xffffffff) { field_0x0c4++; } - field_0x0c0 = false; +#endif + mEndSetup = false; } } - if (field_0x0b8 > mLoopEnd) { + if (mReadSample > mLoopEnd) { JUT_WARN(686, "%s", "mReadSample > mLoopEnd"); hasErrored = true; } + +#if !TARGET_PC // The variable assigned here is never used. f32 fvar1 = field_0x0c4; fvar1 *= mLoopEnd - mLoopStart; if (field_0x0c4 < 0xffffffff) { - fvar1 += field_0x0b8; + fvar1 += mReadSample; } fvar1 /= mSampleRate; field_0x0c8 = fvar1; - if (field_0x0b8 + 400 >= mLoopEnd && !field_0x0c0) { +#endif + + if (mReadSample + 400 >= mLoopEnd && !mEndSetup) { if (mLoop) { + // File needs to loop. Adjust loop start position + // (out of the normal ring buffer behavior). u32 uvar5 = field_0x0bc + 1; if (uvar5 >= mBufCount) { uvar5 = 0; } i_dspChannel->mLoopStartSample = mLoopStart % block_samples + uvar5 * block_samples; - field_0x120 = i_dspChannel->mLoopStartSample; - field_0x12c |= 2; + mUpdateLoopStartSample = i_dspChannel->mLoopStartSample; + mChannelUpdateFlags |= CHANNEL_UPDATE_LOOP_START; } else { - i_dspChannel->field_0x102 = 0; - field_0x128 = 0; - field_0x12c |= 8; + // File doesn't need to loop, just unset loop flag + // and let the DSP finish naturally. + i_dspChannel->mLoopFlag = 0; + mUpdateLoopFlag = 0; + mChannelUpdateFlags |= CHANNEL_UPDATE_LOOP_FLAG; } int sp20 = field_0x0bc * block_samples + mLoopEnd % block_samples; - i_dspChannel->field_0x074 -= block_samples * mBufCount - sp20; - field_0x11c = i_dspChannel->field_0x074; - field_0x12c |= 1; + i_dspChannel->mSamplesLeft -= block_samples * mBufCount - sp20; + mUpdateSamplesLeft = i_dspChannel->mSamplesLeft; + mChannelUpdateFlags |= CHANNEL_UPDATE_SAMPLES_LEFT; field_0x0bc += (mLoopEnd - 1) / block_samples - mLoopStart / block_samples + 1; - field_0x0c0 = true; + mEndSetup = true; } u32 uvar4 = i_dspChannel->mAramStreamPosition - i_channel->mWaveAramAddress; if (uvar4 != 0) { @@ -488,7 +533,7 @@ void JASAramStream::updateChannel(u32 i_callbackType, JASChannel* i_channel, } { JASCriticalSection cs; - field_0x118++; + mPendingLoadTasks++; } field_0x0b0++; if (field_0x0b0 >= mBufCount) { @@ -497,58 +542,58 @@ void JASAramStream::updateChannel(u32 i_callbackType, JASChannel* i_channel, } if (cmp) { field_0x0bc -= mBufCount; - if (field_0x0ad) { - if (!field_0x0c0) { - i_dspChannel->field_0x074 += block_samples; - field_0x11c = i_dspChannel->field_0x074; - field_0x12c |= 1; + if (mLoopEndLoaded) { + if (!mEndSetup) { + i_dspChannel->mSamplesLeft += block_samples; + mUpdateSamplesLeft = i_dspChannel->mSamplesLeft; + mChannelUpdateFlags |= CHANNEL_UPDATE_SAMPLES_LEFT; } i_dspChannel->mEndSample += block_samples; - field_0x124 = i_dspChannel->mEndSample; - field_0x12c |= 4; + mUpdateEndSample = i_dspChannel->mEndSample; + mChannelUpdateFlags |= CHANNEL_UPDATE_END_SAMPLE; mBufCount = mAramBlocksPerChannel; - field_0x0ad = false; + mLoopEndLoaded = false; } else { if (mBufCount != mAramBlocksPerChannel - 1) { mBufCount = mAramBlocksPerChannel - 1; i_dspChannel->mEndSample -= block_samples; - field_0x124 = i_dspChannel->mEndSample; - field_0x12c |= 4; - if (!field_0x0c0) { - i_dspChannel->field_0x074 -= block_samples; - field_0x11c = i_dspChannel->field_0x074; - field_0x12c |= 1; + mUpdateEndSample = i_dspChannel->mEndSample; + mChannelUpdateFlags |= CHANNEL_UPDATE_END_SAMPLE; + if (!mEndSetup) { + i_dspChannel->mSamplesLeft -= block_samples; + mUpdateSamplesLeft = i_dspChannel->mSamplesLeft; + mChannelUpdateFlags |= CHANNEL_UPDATE_SAMPLES_LEFT; } } } } } else { - if (field_0x118 == 0 && !dvdHasErrored) { - field_0x0ae &= ~2; - field_0x0ae &= ~4; + if (mPendingLoadTasks == 0 && !dvdHasErrored) { + mPauseFlags &= ~PAUSE_DVD_ERROR; + mPauseFlags &= ~PAUSE_UNDERFLOW; } } - field_0x0b4 = i_dspChannel->field_0x074 + i_dspChannel->mSamplesPerBlock; - if (field_0x118 >= mAramBlocksPerChannel - 2) { + mLastSamplesLeft = i_dspChannel->mSamplesLeft + i_dspChannel->mSamplesPerBlock; + if (mPendingLoadTasks >= mAramBlocksPerChannel - 2) { JUT_WARN_DEVICE(810, 1, "%s", "buffer under error"); - field_0x0ae |= (u8)4; + mPauseFlags |= (u8)PAUSE_UNDERFLOW; } } else { - if (field_0x12c & 1) { - i_dspChannel->field_0x074 = field_0x11c; + if (mChannelUpdateFlags & CHANNEL_UPDATE_SAMPLES_LEFT) { + i_dspChannel->mSamplesLeft = mUpdateSamplesLeft; } - if (field_0x12c & 2) { - i_dspChannel->mLoopStartSample = field_0x120; + if (mChannelUpdateFlags & CHANNEL_UPDATE_LOOP_START) { + i_dspChannel->mLoopStartSample = mUpdateLoopStartSample; } - if (field_0x12c & 4) { - i_dspChannel->mEndSample = field_0x124; + if (mChannelUpdateFlags & CHANNEL_UPDATE_END_SAMPLE) { + i_dspChannel->mEndSample = mUpdateEndSample; } - if (field_0x12c & 8) { - i_dspChannel->field_0x102 = field_0x128; + if (mChannelUpdateFlags & CHANNEL_UPDATE_LOOP_FLAG) { + i_dspChannel->mLoopFlag = mUpdateLoopFlag; } } int ch = 0; - for (; ch < 6; ch++) { + for (; ch < CHANNEL_MAX; ch++) { if (i_channel == mChannels[ch]) { break; } @@ -568,7 +613,7 @@ void JASAramStream::updateChannel(u32 i_callbackType, JASChannel* i_channel, } } if (!open_channel) { - field_0x114 = 1; + mIsCancelled = 1; if (!sLoadThread->sendCmdMsg(finishTask, this)) { JUT_WARN(854, "%s", "sendCmdMsg finishTask Failed"); hasErrored = true; @@ -577,48 +622,48 @@ void JASAramStream::updateChannel(u32 i_callbackType, JASChannel* i_channel, } break; } - i_channel->setPauseFlag(field_0x0ae != 0); + i_channel->setPauseFlag(mPauseFlags != 0); } s32 JASAramStream::channelProc() { OSMessage msg; - while (OSReceiveMessage(&field_0x020, &msg, OS_MESSAGE_NOBLOCK)) { + while (OSReceiveMessage(&mLoadCommandQueue, &msg, OS_MESSAGE_NOBLOCK)) { switch ((uintptr_t)msg) { - case 4: - field_0x0ac = true; + case CMD_PREPARE_FINISHED: + mPrepareFinished = true; break; - case 5: - field_0x0ad = true; + case CMD_LOOP_END_LOADED: + mLoopEndLoaded = true; break; } } - if (!field_0x0ac) { + if (!mPrepareFinished) { return 0; } - while (OSReceiveMessage(&field_0x000, &msg, OS_MESSAGE_NOBLOCK)) { + while (OSReceiveMessage(&mMainCommandQueue, &msg, OS_MESSAGE_NOBLOCK)) { switch ((uintptr_t)msg & 0xff) { - case 0: + case CMD_START: channelStart(); break; - case 1: + case CMD_STOP: channelStop(JSUHiHalf((uintptr_t)msg)); break; - case 2: - field_0x0ae |= 1; + case CMD_PAUSE: + mPauseFlags |= PAUSE_REQUESTED; break; - case 3: - field_0x0ae &= ~1; + case CMD_UNPAUSE: + mPauseFlags &= ~PAUSE_REQUESTED; break; } } if (hasErrored) { - field_0x0ae |= 8; + mPauseFlags |= PAUSE_OTHER_ERROR; } if (dvdHasErrored) { - field_0x0ae |= 2; + mPauseFlags |= PAUSE_DVD_ERROR; } for (int i = 0; i < mChannelNum; i++) { @@ -648,10 +693,10 @@ static JASOscillator::Data const OSC_ENV = {0, 1.0f, NULL, OSC_RELEASE_TABLE, 1. void JASAramStream::channelStart() { u8 format; switch (mFormat) { - case 0: + case STREAM_FORMAT_ADPCM4: format = WAVE_FORMAT_ADPCM4; break; - case 1: + case STREAM_FORMAT_PCM16: format = WAVE_FORMAT_PCM16; break; } @@ -683,7 +728,7 @@ void JASAramStream::channelStart() { JUT_ASSERT_MSG(979, mChannels[i] == NULL, "channelStart for already playing channel"); mChannels[i] = jc; } - mInitialChannel = NULL; + mPrimaryChannel = NULL; } diff --git a/libs/JSystem/src/JAudio2/JASDSPInterface.cpp b/libs/JSystem/src/JAudio2/JASDSPInterface.cpp index a4c91b103c..00c7a66539 100644 --- a/libs/JSystem/src/JAudio2/JASDSPInterface.cpp +++ b/libs/JSystem/src/JAudio2/JASDSPInterface.cpp @@ -514,7 +514,7 @@ void JASDsp::TChannel::playStart() { JUT_ASSERT(508, dspMutex); field_0x10c = 0; field_0x060 = 0; - field_0x008 = 1; + mResetFlag = 1; field_0x066 = 0; int i; for (i = 0; i < 4; i++) { @@ -564,8 +564,8 @@ void JASDsp::TChannel::setWaveInfo(JASWaveInfo const& waveInfo, u32 aramAddress, mSamplePosition = 0; if (mBytesPerBlock >= 4) { mSampleCount = waveInfo.mSampleCount; - field_0x102 = waveInfo.mLoopFlag; - if (field_0x102) { + mLoopFlag = waveInfo.mLoopFlag; + if (mLoopFlag) { if (skipSamples == 1) { skipSamples = waveInfo.mLoopStartSample; }