mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-05-29 16:14:54 -04:00
More documenting for JASAramStream and JASDSPInterface
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
#include "JSystem/JAudio2/JASTaskThread.h"
|
||||
#include "JSystem/JUtility/JUTAssert.h"
|
||||
#include <dvd.h>
|
||||
#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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user