From 303a21a4a377e0c719e6dc14c6c04e857e81673c Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Wed, 11 Mar 2026 21:50:46 +0100 Subject: [PATCH 01/94] JAudio field name/comment pass HUGE thanks to XAYRGA for their work documenting JAudio file formats --- .../include/JSystem/JAudio2/JASAramStream.h | 42 +++--- .../include/JSystem/JAudio2/JASBNKParser.h | 20 +-- .../include/JSystem/JAudio2/JASChannel.h | 15 +- .../include/JSystem/JAudio2/JASDSPChannel.h | 28 +++- .../include/JSystem/JAudio2/JASDSPInterface.h | 114 +++++++++----- .../include/JSystem/JAudio2/JASDriverIF.h | 4 + .../include/JSystem/JAudio2/JASOscillator.h | 27 +++- .../include/JSystem/JAudio2/JASWSParser.h | 35 +++-- .../include/JSystem/JAudio2/JASWaveInfo.h | 29 ++-- .../include/JSystem/JAudio2/JAUInitializer.h | 10 +- .../JSystem/include/JSystem/JAudio2/dspproc.h | 2 +- .../JSystem/include/JSystem/JAudio2/dsptask.h | 2 +- libs/JSystem/src/JAudio2/JASAramStream.cpp | 142 +++++++++--------- libs/JSystem/src/JAudio2/JASAudioReseter.cpp | 2 +- libs/JSystem/src/JAudio2/JASBNKParser.cpp | 16 +- libs/JSystem/src/JAudio2/JASBank.cpp | 16 +- libs/JSystem/src/JAudio2/JASBasicWaveBank.cpp | 10 +- libs/JSystem/src/JAudio2/JASChannel.cpp | 52 +++---- libs/JSystem/src/JAudio2/JASDSPChannel.cpp | 42 +++--- libs/JSystem/src/JAudio2/JASDSPInterface.cpp | 120 ++++++++------- libs/JSystem/src/JAudio2/JASDriverIF.cpp | 6 +- libs/JSystem/src/JAudio2/JASDrumSet.cpp | 4 +- libs/JSystem/src/JAudio2/JASOscillator.cpp | 52 +++---- .../JSystem/src/JAudio2/JASSimpleWaveBank.cpp | 2 +- libs/JSystem/src/JAudio2/JASTrack.cpp | 10 +- libs/JSystem/src/JAudio2/JASWSParser.cpp | 44 +++--- libs/JSystem/src/JAudio2/JAUInitializer.cpp | 30 ++-- libs/JSystem/src/JAudio2/JAUSectionHeap.cpp | 6 +- libs/JSystem/src/JAudio2/dspproc.cpp | 6 +- libs/JSystem/src/JAudio2/dsptask.cpp | 4 +- libs/JSystem/src/JAudio2/osdsp_task.cpp | 18 +-- src/Z2AudioLib/Z2AudioMgr.cpp | 10 +- src/d/actor/d_a_movie_player.cpp | 2 +- src/d/d_menu_option.cpp | 14 +- 34 files changed, 518 insertions(+), 418 deletions(-) diff --git a/libs/JSystem/include/JSystem/JAudio2/JASAramStream.h b/libs/JSystem/include/JSystem/JAudio2/JASAramStream.h index 17d4c6f521..119145de9a 100644 --- a/libs/JSystem/include/JSystem/JAudio2/JASAramStream.h +++ b/libs/JSystem/include/JSystem/JAudio2/JASAramStream.h @@ -17,6 +17,8 @@ namespace JASDsp { */ class JASAramStream { public: + static const int CHANNEL_MAX = 6; + typedef void (*StreamCallback)(u32, JASAramStream*, void*); enum CallbackType { @@ -32,29 +34,29 @@ public: }; struct Header { - /* 0x00 */ u32 tag; + /* 0x00 */ u32 tag; // 'STRM' /* 0x04 */ u8 field_0x4[5]; /* 0x09 */ u8 format; /* 0x0A */ u8 bits; /* 0x0C */ u16 channels; /* 0x0E */ u16 loop; - /* 0x10 */ int field_0x10; - /* 0x14 */ u8 field_0x14[4]; + /* 0x10 */ int mSampleRate; + /* 0x14 */ u32 mSampleCount; // unused /* 0x18 */ int loop_start; /* 0x1C */ int loop_end; /* 0x20 */ u32 block_size; - /* 0x24 */ u8 field_0x24[4]; - /* 0x28 */ u8 field_0x28; - /* 0x29 */ u8 field_0x29[0x17]; + /* 0x24 */ u8 _unused2[4]; + /* 0x28 */ u8 mVolume; + /* 0x29 */ u8 _unused3[0x17]; }; // Size: 0x40 struct BlockHeader { - /* 0x00 */ u32 tag; - /* 0x04 */ u32 field_0x4; + /* 0x00 */ u32 tag; // 'BLCK' + /* 0x04 */ u32 mSize; /* 0x08 */ struct { - s16 field_0x0; - s16 field_0x2; - } field_0x8[6]; + s16 mpLast; + s16 mpPenult; + } mAdpcmContinuationData[CHANNEL_MAX]; }; // Size: 0x20 static void initSystem(u32, u32); @@ -128,14 +130,12 @@ public: static u32 getBlockSize() { return sBlockSize; } - static const int CHANNEL_MAX = 6; - /* 0x000 */ OSMessageQueue field_0x000; /* 0x020 */ OSMessageQueue field_0x020; /* 0x040 */ void* field_0x040[16]; /* 0x080 */ void* field_0x080[4]; /* 0x090 */ JASChannel* mChannels[CHANNEL_MAX]; - /* 0x0A8 */ JASChannel* field_0x0a8; + /* 0x0A8 */ JASChannel* mInitialChannel; /* 0x0AC */ bool field_0x0ac; /* 0x0AD */ bool field_0x0ad; /* 0x0AE */ u8 field_0x0ae; @@ -157,17 +157,17 @@ public: /* 0x124 */ int field_0x124; /* 0x128 */ u16 field_0x128; /* 0x12C */ int field_0x12c; - /* 0x130 */ s16 field_0x130[CHANNEL_MAX]; - /* 0x13C */ s16 field_0x13c[CHANNEL_MAX]; - /* 0x148 */ int field_0x148; + /* 0x130 */ s16 mpLasts[CHANNEL_MAX]; + /* 0x13C */ s16 mpPenults[CHANNEL_MAX]; + /* 0x148 */ int mAramAddress; /* 0x14C */ u32 field_0x14c; /* 0x150 */ StreamCallback mCallback; /* 0x154 */ void* mCallbackData; - /* 0x158 */ u16 field_0x158; + /* 0x158 */ u16 mFormat; /* 0x15A */ u16 mChannelNum; /* 0x15C */ u32 mBufCount; - /* 0x160 */ u32 field_0x160; - /* 0x164 */ u32 field_0x164; + /* 0x160 */ u32 mBlocksPerChannel; + /* 0x164 */ u32 mSampleRate; /* 0x168 */ bool mLoop; /* 0x16C */ u32 mLoopStart; /* 0x170 */ u32 mLoopEnd; @@ -177,7 +177,7 @@ public: /* 0x194 */ f32 mChannelPan[CHANNEL_MAX]; /* 0x1AC */ f32 mChannelFxMix[CHANNEL_MAX]; /* 0x1C4 */ f32 mChannelDolby[CHANNEL_MAX]; - /* 0x1DC */ u16 field_0x1dc[CHANNEL_MAX]; + /* 0x1DC */ u16 mMixConfig[CHANNEL_MAX]; static JASTaskThread* sLoadThread; static u8* sReadBuffer; diff --git a/libs/JSystem/include/JSystem/JAudio2/JASBNKParser.h b/libs/JSystem/include/JSystem/JAudio2/JASBNKParser.h index f7a4d0585b..7d466734c0 100644 --- a/libs/JSystem/include/JSystem/JAudio2/JASBNKParser.h +++ b/libs/JSystem/include/JSystem/JAudio2/JASBNKParser.h @@ -10,21 +10,21 @@ class JKRHeap; namespace JASBNKParser { struct TFileHeader { - /* 0x0 */ BE(int) id; + /* 0x0 */ BE(int) mMagic; /* 0x4 */ BE(u32) mSize; - /* 0x8 */ u8 _08[4]; + /* 0x8 */ BE(u32) mGlobalId; /* 0xC */ BE(u32) mVersion; }; namespace Ver1 { struct TOsc { - /* 0x00 */ BE(u32) id; + /* 0x00 */ BE(u32) id; // "Osci" /* 0x04 */ u8 mTarget; - /* 0x08 */ BE(f32) _08; - /* 0x0C */ BE(u32) mTableOffset; - /* 0x10 */ BE(u32) _10; - /* 0x14 */ BE(f32) mScale; - /* 0x18 */ BE(f32) _18; + /* 0x08 */ BE(f32) mRate; + /* 0x0C */ BE(u32) mAttackEnvelopeOffset; + /* 0x10 */ BE(u32) mReleaseEnvelopeOffset; + /* 0x14 */ BE(f32) mScale; // width + /* 0x18 */ BE(f32) mVertex; }; struct TPercData { @@ -36,7 +36,7 @@ namespace JASBNKParser { }; struct TChunk { - /* 0x0 */ BE(u32) mID; + /* 0x0 */ BE(u32) mID; // Magic /* 0x4 */ BE(u32) mSize; }; @@ -113,7 +113,7 @@ namespace JASBNKParser { }; struct TOffsetData { - /* 0x000 */ u8 field_0x20[4]; + /* 0x000 */ BE(u32) mMagic; // 'BANK' /* 0x004 */ TOffset mInstOffset[0x80]; /* 0x204 */ u8 field_0x204[0x190]; /* 0x394 */ TOffset mPercOffset[12]; diff --git a/libs/JSystem/include/JSystem/JAudio2/JASChannel.h b/libs/JSystem/include/JSystem/JAudio2/JASChannel.h index 042e3343cd..53bf064d16 100644 --- a/libs/JSystem/include/JSystem/JAudio2/JASChannel.h +++ b/libs/JSystem/include/JSystem/JAudio2/JASChannel.h @@ -45,6 +45,9 @@ public: /* 0x14 */ f32 mDolby; }; +#define CHANNEL_WAVE 0 +#define CHANNEL_OSCILLATOR 2 + /** * @ingroup jsystem-jaudio * @@ -52,7 +55,7 @@ public: class JASChannel : public JASPoolAllocObject_MultiThreaded { public: typedef void (*Callback)(u32, JASChannel*, JASDsp::TChannel*, void*); - static const int BUSOUT_CPUCH = 6; + static const int BUSOUT_CPUCH = DSP_OUTPUT_CHANNELS; static const int OSC_NUM = 2; enum CallbackType { @@ -153,10 +156,14 @@ public: /* 0xD4 */ u32 mKeySweepCount; /* 0xD8 */ u32 mSkipSamples; struct { - u32 field_0x0; - JASWaveInfo field_0x4; + u32 mChannelType; // CHANNEL_WAVE or CHANNEL_OSCILLATOR + JASWaveInfo mWaveInfo; } field_0xdc; - intptr_t field_0x104; + union { + u32 mWaveAramAddress; + u32 mOscillatorSomething; + u32 field_0x104; + }; static OSMessageQueue sBankDisposeMsgQ; static OSMessage sBankDisposeMsg[16]; diff --git a/libs/JSystem/include/JSystem/JAudio2/JASDSPChannel.h b/libs/JSystem/include/JSystem/JAudio2/JASDSPChannel.h index f843562f37..8204ab0838 100644 --- a/libs/JSystem/include/JSystem/JAudio2/JASDSPChannel.h +++ b/libs/JSystem/include/JSystem/JAudio2/JASDSPChannel.h @@ -5,9 +5,14 @@ /** * @ingroup jsystem-jaudio - * + * Responsible for allocating and prioritizing the available hardware DSP channels. */ struct JASDSPChannel { + /** + * Callback used to update DSP channel information on a regular basis. + * Parameters are a CallbackType, hardware channel, and specified user data. + * Return -1 to immediately abort playback. + */ typedef s32 (*Callback)(u32, JASDsp::TChannel*, void*); enum Status { @@ -17,9 +22,24 @@ struct JASDSPChannel { }; enum CallbackType { + /** + * Fired on a regular basis during play to update parameters. + */ /* 0 */ CB_PLAY, + + /** + * Fired once, when the channel starts playing. + */ /* 1 */ CB_START, + + /** + * Fired once, when the channel naturally finishes playing. + */ /* 2 */ CB_STOP, + + /** + * Fired once, if the channel is abruptly stopped due to an error or prioritization. + */ /* 3 */ CB_DROP, }; @@ -46,9 +66,13 @@ struct JASDSPChannel { static JASDSPChannel* sDspChannels; /* 0x00 */ s32 mStatus; + + /** + * Priority of this DSP channel. Used to + */ /* 0x04 */ s16 mPriority; /* 0x08 */ u32 mFlags; - /* 0x0C */ u32 field_0xc; + /* 0x0C */ u32 mUpdateCounter; /* 0x10 */ Callback mCallback; /* 0x14 */ void* mCallbackData; /* 0x18 */ JASDsp::TChannel* mChannel; diff --git a/libs/JSystem/include/JSystem/JAudio2/JASDSPInterface.h b/libs/JSystem/include/JSystem/JAudio2/JASDSPInterface.h index a653431bee..ec2d9f6c89 100644 --- a/libs/JSystem/include/JSystem/JAudio2/JASDSPInterface.h +++ b/libs/JSystem/include/JSystem/JAudio2/JASDSPInterface.h @@ -3,6 +3,16 @@ #include +/** + * Amount of separate audio channels (i.e. individual playbacks, voices) the DSP can mix at once. + */ +#define DSP_CHANNELS 64 + +/** + * Amount of audio channels the DSP can calculate outputs for. + */ +#define DSP_OUTPUT_CHANNELS 6 // Presumed 5.1 surround + struct JASWaveInfo; namespace JASDsp { @@ -29,6 +39,24 @@ namespace JASDsp { u16 field_0x10[8]; } FxBuf; + struct OutputChannelConfig { + u16 mBusConnect; + u16 mTargetVolume; + u16 mCurrentVolume; + + /** + * Gets upper 8 bits cleared when audio volume is changed mid-playback. + * Presumed to be some kind of progress used by the DSP to calculate position between + * mTargetVolume and mCurrentVolume. + */ + u16 mVolumeProgress; + }; + + /** + * DSP memory for each playback channel ("voice"). + * The DSP can read and write this memory. It is used as configuration, feedback, + * and working memory. + */ struct TChannel { void init(); void playStart(); @@ -36,67 +64,83 @@ namespace JASDsp { void replyFinishRequest(); void forceStop(); bool isActive() const; + + /** + * Check whether the DSP has finished playing this channel. + */ bool isFinish() const; void setWaveInfo(JASWaveInfo const&, u32, u32); void setOscInfo(u32); void initAutoMixer(); void setAutoMixer(u16, u8, u8, u8, u8); void setPitch(u16); - void setMixerInitVolume(u8, s16); - void setMixerVolume(u8, s16); + void setMixerInitVolume(u8 outputChannel, s16 volume); + void setMixerVolume(u8 outputChannel, s16 volume); void setPauseFlag(u8); + + /** + * Flushes backing memory of channel out of the data cache. + */ void flush(); void initFilter(); void setFilterMode(u16); void setIIRFilterParam(s16*); void setFIR8FilterParam(s16*); void setDistFilter(s16); - void setBusConnect(u8, u8); + void setBusConnect(u8 outputChannel, u8 param_1); /* 0x000 */ u16 mIsActive; /* 0x002 */ u16 mIsFinished; /* 0x004 */ u16 mPitch; - /* 0x006 */ short field_0x006; + /* 0x006 */ short _unused1; /* 0x008 */ u16 field_0x008; - /* 0x00A */ u8 field_0x00A[0x00C - 0x00A]; + /* 0x00A */ u8 _unused2[0x00C - 0x00A]; /* 0x00C */ s16 mPauseFlag; - /* 0x00E */ short field_0x00E; - /* 0x010 */ u16 field_0x010[1][4]; // array size unknown - /* 0x018 */ u8 field_0x018[0x050 - 0x018]; - /* 0x050 */ u16 field_0x050; - /* 0x052 */ u16 field_0x052; - /* 0x054 */ u16 field_0x054; - /* 0x056 */ u16 field_0x056; - /* 0x058 */ u16 field_0x058; - /* 0x05A */ u8 field_0x05A[0x060 - 0x05A]; - /* 0x060 */ short field_0x060; - /* 0x062 */ u8 field_0x062[0x064 - 0x062]; - /* 0x064 */ u16 field_0x064; - /* 0x066 */ short field_0x066; - /* 0x068 */ int field_0x068; - /* 0x06C */ u8 field_0x06C[0x070 - 0x06C]; - /* 0x070 */ int field_0x070; + /* 0x00E */ short _unused3; + /* 0x010 */ OutputChannelConfig mOutputChannels[DSP_OUTPUT_CHANNELS]; + /* 0x040 */ u8 _unused4[0x050 - 0x040]; + /* 0x050 */ u16 mAutoMixerPanDolby; // pan is upper 8 bits, dolby lower 8. + /* 0x052 */ u16 mAutoMixerFxMix; + /* 0x054 */ u16 mAutoMixerInitVolume; + /* 0x056 */ u16 mAutoMixerVolume; + /* 0x058 */ u16 mAutoMixerBeenSet; + /* 0x05A */ u8 _unused5[0x060 - 0x05A]; + /* 0x060 */ short field_0x060; // Only cleared to zero, presumed used by DSP. + /* 0x062 */ u8 _unused6[0x064 - 0x062]; + /* 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. + /* 0x06C */ u8 _unused7[0x070 - 0x06C]; + /* 0x070 */ int mAramStreamPosition; // Seems written by DSP, used for audio streaming. /* 0x074 */ int field_0x074; - /* 0x078 */ short field_0x078[4]; - /* 0x080 */ short field_0x080[20]; - /* 0x0A8 */ short field_0x0a8[4]; - /* 0x0B0 */ u16 field_0x0b0[16]; - /* 0x0D0 */ u8 field_0x0D0[0x100 - 0x0D0]; - /* 0x100 */ u16 field_0x100; + /* 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; - /* 0x104 */ s16 field_0x104; - /* 0x106 */ s16 field_0x106; + + /** + * Used for decoding ADPCM data around loop edges. + */ + /* 0x104 */ s16 mpLast; + + /** + * Used for decoding ADPCM data around loop edges. + */ + /* 0x106 */ s16 mpPenult; /* 0x108 */ u16 mFilterMode; /* 0x10A */ u16 mForcedStop; /* 0x10C */ int field_0x10c; - /* 0x110 */ u32 field_0x110; - /* 0x114 */ u32 field_0x114; - /* 0x118 */ u32 field_0x118; - /* 0x11C */ int field_0x11c; + /* 0x110 */ u32 mLoopStartSample; + /* 0x114 */ u32 mEndSample; + /* 0x118 */ u32 mWaveAramAddress; + /* 0x11C */ int mSampleCount; /* 0x120 */ s16 fir_filter_params[8]; - /* 0x130 */ u8 field_0x130[0x148 - 0x130]; + /* 0x130 */ u8 _unused9[0x148 - 0x130]; /* 0x148 */ s16 iir_filter_params[8]; - /* 0x158 */ u8 field_0x158[0x180 - 0x158]; + /* 0x158 */ u8 _unused10[0x180 - 0x158]; }; void boot(void (*)(void*)); diff --git a/libs/JSystem/include/JSystem/JAudio2/JASDriverIF.h b/libs/JSystem/include/JSystem/JAudio2/JASDriverIF.h index 319c826345..5cddc38596 100644 --- a/libs/JSystem/include/JSystem/JAudio2/JASDriverIF.h +++ b/libs/JSystem/include/JSystem/JAudio2/JASDriverIF.h @@ -3,6 +3,10 @@ #include "JSystem/JAudio2/JASCallback.h" +#define JAS_OUTPUT_MONO OS_SOUND_MODE_MONO +#define JAS_OUTPUT_STEREO OS_SOUND_MODE_STEREO +#define JAS_OUTPUT_SURROUND 2 + typedef s32 (*DriverCallback)(void*); namespace JASDriver { diff --git a/libs/JSystem/include/JSystem/JAudio2/JASOscillator.h b/libs/JSystem/include/JSystem/JAudio2/JASOscillator.h index 746d462e12..e811e890ab 100644 --- a/libs/JSystem/include/JSystem/JAudio2/JASOscillator.h +++ b/libs/JSystem/include/JSystem/JAudio2/JASOscillator.h @@ -9,10 +9,21 @@ * */ struct JASOscillator { + enum EnvelopeMode { + /* 0x0 */ ENVELOPE_LINEAR, + /* 0x1 */ ENVELOPE_SQUARE, + /* 0x2 */ ENVELOPE_SQUARE_ROOT, + /* 0x3 */ ENVELOPE_SAMPLE_CELL, + + ENVELOPE_LOOP = 0xD, + ENVELOPE_HOLD = 0xE, + ENVELOPE_STOP = 0xF, + }; + struct Point { - /* 0x0 */ BE(s16) _0; - /* 0x2 */ BE(s16) _2; - /* 0x4 */ BE(s16) _4; + /* 0x0 */ BE(s16) mEnvelopeMode; // EnvelopeMode + /* 0x2 */ BE(s16) mTime; + /* 0x4 */ BE(s16) mValue; }; struct EffectParams { @@ -37,11 +48,11 @@ struct JASOscillator { struct Data { /* 0x00 */ u32 mTarget; - /* 0x04 */ f32 _04; + /* 0x04 */ f32 mRate; /* 0x08 */ const Point* mTable; /* 0x0C */ const Point* rel_table; - /* 0x10 */ f32 mScale; - /* 0x14 */ f32 _14; + /* 0x10 */ f32 mScale; // aka width + /* 0x14 */ f32 mVertex; }; enum Target { @@ -75,9 +86,9 @@ struct JASOscillator { /* 0x08 */ f32 _08; /* 0x0C */ f32 _0C; /* 0x10 */ f32 _10; - /* 0x14 */ u16 _14; + /* 0x14 */ u16 mCurPoint; /* 0x16 */ u16 mDirectRelease; - /* 0x18 */ u8 _18; + /* 0x18 */ u8 mEnvelopeMode; // EnvelopeMode /* 0x1A */ u16 _1A; /* 0x1C */ int _1C; diff --git a/libs/JSystem/include/JSystem/JAudio2/JASWSParser.h b/libs/JSystem/include/JSystem/JAudio2/JASWSParser.h index 020158c096..68d91006ba 100644 --- a/libs/JSystem/include/JSystem/JAudio2/JASWSParser.h +++ b/libs/JSystem/include/JSystem/JAudio2/JASWSParser.h @@ -31,26 +31,28 @@ public: struct TWave { /* 0x00 */ u8 _00; - /* 0x01 */ u8 _01; - /* 0x02 */ u8 _02; - /* 0x04 */ BE(f32) _04; - /* 0x08 */ BE(u32) mOffset; - /* 0x0C */ BE(u32) _0C; - /* 0x10 */ BE(u32) _10; - /* 0x14 */ BE(u32) _14; - /* 0x18 */ BE(u32) _18; - /* 0x1C */ BE(u32) _1C; - /* 0x20 */ BE(s16) _20; - /* 0x22 */ BE(s16) _22; + /* 0x01 */ u8 mWaveFormat; + /* 0x02 */ u8 mBaseKey; + /* 0x04 */ BE(f32) mSampleRate; + /* 0x08 */ BE(u32) mAWOffsetStart; + /* 0x0C */ BE(u32) mAWOffsetEnd; + /* 0x10 */ BE(u32) mLoopFlags; + /* 0x14 */ BE(u32) mLoopStartSample; + /* 0x18 */ BE(u32) mLoopEndSample; + /* 0x1C */ BE(u32) mSampleCount; + /* 0x20 */ BE(s16) mpLast; + /* 0x22 */ BE(s16) mpPenult; }; struct TWaveArchive { - /* 0x00 */ char mFileName[0x74]; // unknown length + /* 0x00 */ char mFileName[0x70]; + /* 0x70 */ BE(u32) mWaveCount; /* 0x74 */ TOffset mWaveOffsets[0]; }; struct TWaveArchiveBank { - /* 0x0 */ u8 _00[8]; + /* 0x0 */ BE(u32) mMagic; // 'WINF' + /* 0x0 */ BE(u32) mArchiveCounts; /* 0x8 */ TOffset mArchiveOffsets[0]; }; @@ -66,14 +68,17 @@ public: }; struct TCtrlGroup { - /* 0x0 */ u8 _00[8]; + /* 0x0 */ BE(u32) mMagic; // 'WBCT' + /* 0x4 */ u32 mUnknown; /* 0x8 */ BE(u32) mGroupCount; /* 0xC */ TOffset mCtrlSceneOffsets[0]; }; /** @fabricated */ struct THeader { - /* 0x00 */ u8 _00[0xC]; + /* 0x00 */ BE(u32) mMagic; // 'WSYS' + /* 0x04 */ BE(u32) mSize; + /* 0x08 */ BE(u32) mId; /* 0x0C */ BE(u32) mWaveTableSize; /* 0x10 */ TOffset mArchiveBankOffset; /* 0x14 */ TOffset mCtrlGroupOffset; diff --git a/libs/JSystem/include/JSystem/JAudio2/JASWaveInfo.h b/libs/JSystem/include/JSystem/JAudio2/JASWaveInfo.h index cbff6d3ac2..62110754d7 100644 --- a/libs/JSystem/include/JSystem/JAudio2/JASWaveInfo.h +++ b/libs/JSystem/include/JSystem/JAudio2/JASWaveInfo.h @@ -5,27 +5,32 @@ class JASWaveArc; +#define WAVE_FORMAT_ADPCM4 0 +#define WAVE_FORMAT_ADPCM2 1 +#define WAVE_FORMAT_PCM8 2 +#define WAVE_FORMAT_PCM16 3 + /** * @ingroup jsystem-jaudio * */ struct JASWaveInfo { JASWaveInfo() { - field_0x01 = 0x3c; + mBaseKey = 0x3c; field_0x20 = &one; } - /* 0x00 */ u8 field_0x00; - /* 0x01 */ u8 field_0x01; - /* 0x02 */ u8 field_0x02; - /* 0x04 */ f32 field_0x04; - /* 0x08 */ int field_0x08; - /* 0x0C */ int field_0x0c; - /* 0x10 */ u32 field_0x10; - /* 0x14 */ int field_0x14; - /* 0x18 */ int field_0x18; - /* 0x1C */ s16 field_0x1c; - /* 0x1E */ s16 field_0x1e; + /* 0x00 */ u8 mWaveFormat; + /* 0x01 */ u8 mBaseKey; + /* 0x02 */ u8 mLoopFlag; + /* 0x04 */ f32 mSampleRate; + /* 0x08 */ int mOffsetStart; + /* 0x0C */ int mOffsetLength; + /* 0x10 */ u32 mLoopStartSample; + /* 0x14 */ int mLoopEndSample; + /* 0x18 */ int mSampleCount; + /* 0x1C */ s16 mpLast; + /* 0x1E */ s16 mpPenult; /* 0x20 */ const u32* field_0x20; static u32 one; diff --git a/libs/JSystem/include/JSystem/JAudio2/JAUInitializer.h b/libs/JSystem/include/JSystem/JAudio2/JAUInitializer.h index 8b5090c580..9f9e5bc6fd 100644 --- a/libs/JSystem/include/JSystem/JAudio2/JAUInitializer.h +++ b/libs/JSystem/include/JSystem/JAudio2/JAUInitializer.h @@ -21,7 +21,7 @@ public: /* 0x10 */ int audioThreadPriority_; /* 0x14 */ int dvdThreadId_; /* 0x18 */ int audioThreadId_; - /* 0x1C */ int field_0x1c; + /* 0x1C */ int mJasTrackPoolSize; /* 0x20 */ int field_0x20; /* 0x24 */ int aramBlockSize_; /* 0x28 */ int aramChannelNum_; @@ -38,10 +38,10 @@ public: JAU_JAIInitializer(); void initJAInterface(); - int field_0x0; - int field_0x4; - int field_0x8; - int field_0xc; + int mJaiSePoolSize; + int mJaiSeqPoolSize; + int mJaiStreamPoolSize; + int mJaiSoundChildPoolSize; }; #endif /* JAUINITIALIZER_H */ diff --git a/libs/JSystem/include/JSystem/JAudio2/dspproc.h b/libs/JSystem/include/JSystem/JAudio2/dspproc.h index 868080031a..c49c143fe4 100644 --- a/libs/JSystem/include/JSystem/JAudio2/dspproc.h +++ b/libs/JSystem/include/JSystem/JAudio2/dspproc.h @@ -4,7 +4,7 @@ #include void DSPReleaseHalt2(u32 msg); -void DsetupTable(u32 param_0, u32 param_1, u32 param_2, u32 param_3, u32 param_4); +void DsetupTable(u32 channelCount, u32 channelBufferAddress, u32 param_2, u32 param_3, u32 param_4); void DsetMixerLevel(f32 level); void DsyncFrame2ch(u32 param_0, u32 param_1, u32 param_2); void DsyncFrame4ch(u32 param_0, u32 param_1, u32 param_2, u32 param_3, u32 param_4); diff --git a/libs/JSystem/include/JSystem/JAudio2/dsptask.h b/libs/JSystem/include/JSystem/JAudio2/dsptask.h index 8042d1884b..7dbcff2dec 100644 --- a/libs/JSystem/include/JSystem/JAudio2/dsptask.h +++ b/libs/JSystem/include/JSystem/JAudio2/dsptask.h @@ -3,7 +3,7 @@ #include -void DspBoot(void (*)(void*)); +void DspBoot(void (*requestCallback)(void*)); void DspFinishWork(u16 param_0); int DSPSendCommands2(u32* msgs, u32 param_1, void (*param_2)(u16)); diff --git a/libs/JSystem/src/JAudio2/JASAramStream.cpp b/libs/JSystem/src/JAudio2/JASAramStream.cpp index 2a188b87e3..159df92739 100644 --- a/libs/JSystem/src/JAudio2/JASAramStream.cpp +++ b/libs/JSystem/src/JAudio2/JASAramStream.cpp @@ -43,7 +43,7 @@ void JASAramStream::initSystem(u32 block_size, u32 channel_max) { } JASAramStream::JASAramStream() { - field_0x0a8 = NULL; + mInitialChannel = NULL; field_0x0ac = false; field_0x0ad = false; field_0x0ae = 0; @@ -60,15 +60,15 @@ JASAramStream::JASAramStream() { field_0x114 = 0; field_0x118 = 0; field_0x12c = 0; - field_0x148 = 0; + mAramAddress = 0; field_0x14c = 0; mCallback = NULL; mCallbackData = NULL; - field_0x158 = 0; + mFormat = 0; mChannelNum = 0; mBufCount = 0; - field_0x160 = 0; - field_0x164 = 0; + mBlocksPerChannel = 0; + mSampleRate = 0; mLoop = false; mLoopStart = 0; mLoopEnd = 0; @@ -76,21 +76,21 @@ JASAramStream::JASAramStream() { mPitch = 1.0f; for (int i = 0; i < 6; i++) { mChannels[i] = NULL; - field_0x130[i] = 0; - field_0x13c[i] = 0; + mpLasts[i] = 0; + mpPenults[i] = 0; mChannelVolume[i] = 1.0f; mChannelPan[i] = 0.5f; mChannelFxMix[i] = 0.0f; mChannelDolby[i] = 0.0f; } for (int i = 0; i < 6; i++) { - field_0x1dc[i] = 0; + mMixConfig[i] = 0; } } -void JASAramStream::init(u32 param_0, u32 param_1, StreamCallback i_callback, void* i_callbackData) { +void JASAramStream::init(u32 aramAddress, u32 param_1, StreamCallback i_callback, void* i_callbackData) { JUT_ASSERT(153, sReadBuffer != NULL); - field_0x148 = param_0; + mAramAddress = aramAddress; field_0x14c = param_1; field_0x0c8 = 0.0f; field_0x0ae = 0; @@ -106,7 +106,7 @@ void JASAramStream::init(u32 param_0, u32 param_1, StreamCallback i_callback, vo } mVolume = 1.0f; mPitch = 1.0f; - field_0x1dc[0] = 0xffff; + mMixConfig[0] = 0xffff; mCallback = i_callback; mCallbackData = i_callbackData; OSInitMessageQueue(&field_0x000, field_0x040, 0x10); @@ -169,7 +169,7 @@ bool JASAramStream::cancel() { } u32 JASAramStream::getBlockSamples() const { - return field_0x158 == 0 ? (sBlockSize << 4) / 9 : sBlockSize >> 1; + return mFormat == 0 ? (sBlockSize << 4) / 9 : sBlockSize >> 1; } void JASAramStream::headerLoadTask(void* i_data) { @@ -245,18 +245,18 @@ bool JASAramStream::headerLoad(u32 param_0, int param_1) { JUT_ASSERT(428, header->bits == 16); JUT_ASSERT(429, header->channels <= sChannelMax); JUT_ASSERT(430, header->block_size == sBlockSize); - field_0x158 = header->format; + mFormat = header->format; mChannelNum = header->channels; - field_0x164 = header->field_0x10; + mSampleRate = header->mSampleRate; mLoop = header->loop != 0; mLoopStart = header->loop_start; mLoopEnd = header->loop_end; - mVolume = header->field_0x28 / 127.0f; + mVolume = header->mVolume / 127.0f; field_0x118 = 0; mBlock = 0; field_0x10c = 0; - field_0x160 = (param_0 / sBlockSize) / header->channels; - mBufCount = field_0x160; + mBlocksPerChannel = (param_0 / sBlockSize) / header->channels; + mBufCount = mBlocksPerChannel; JUT_ASSERT(445, mBufCount > 0); mBufCount--; if (mBufCount < 3) { @@ -319,14 +319,14 @@ bool JASAramStream::load() { if (field_0x114 != 0) { return false; } - u32 sp08 = field_0x148 + field_0x10c * sBlockSize; + u32 sp08 = mAramAddress + field_0x10c * 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 + *(u32*)&bhead->field_0x4 * i + sizeof(BlockHeader), - sp08 + sBlockSize * field_0x160 * i, - bhead->field_0x4, EXPAND_SWITCH_UNKNOWN0, 0, NULL, -1, NULL)) { + if (!JKRMainRamToAram(sReadBuffer + *(u32*)&bhead->mSize * i + sizeof(BlockHeader), + sp08 + sBlockSize * mBlocksPerChannel * i, + bhead->mSize, EXPAND_SWITCH_UNKNOWN0, 0, NULL, -1, NULL)) { JUT_WARN(522, "%s", "JKRMainRamToAram Failed"); struct_80451261 = 1; return false; @@ -344,14 +344,14 @@ bool JASAramStream::load() { } } if (r28 == loop_end_block || r28 + 2 == loop_end_block) { - field_0x108 = field_0x160; + field_0x108 = mBlocksPerChannel; OSSendMessage(&field_0x020, (OSMessage)5, OS_MESSAGE_BLOCK); } else { - field_0x108 = field_0x160 - 1; + field_0x108 = mBlocksPerChannel - 1; } for (int i = 0; i < mChannelNum; i++) { - field_0x130[i] = (s16)bhead->field_0x8[i].field_0x0; - field_0x13c[i] = (s16)bhead->field_0x8[i].field_0x2; + mpLasts[i] = (s16)bhead->mAdpcmContinuationData[i].mpLast; + mpPenults[i] = (s16)bhead->mAdpcmContinuationData[i].mpPenult; } field_0x10c = 0; } @@ -402,8 +402,8 @@ void JASAramStream::updateChannel(u32 i_callbackType, JASChannel* i_channel, u32 block_samples = getBlockSamples(); switch (i_callbackType) { case JASChannel::CB_START: - if (field_0x0a8 == NULL) { - field_0x0a8 = i_channel; + if (mInitialChannel == NULL) { + mInitialChannel = i_channel; field_0x0b4 = block_samples * mBufCount; field_0x0b8 = 0; field_0x0b0 = 0; @@ -415,9 +415,9 @@ void JASAramStream::updateChannel(u32 i_callbackType, JASChannel* i_channel, break; case JASChannel::CB_PLAY: if (i_dspChannel->field_0x008 == 0) { - if (i_channel == field_0x0a8) { + if (i_channel == mInitialChannel) { field_0x12c = 0; - u32 sp28 = i_dspChannel->field_0x074 + i_dspChannel->field_0x064; + u32 sp28 = i_dspChannel->field_0x074 + i_dspChannel->mSamplesPerBlock; if (sp28 <= field_0x0b4) { field_0x0b8 += field_0x0b4 - sp28; } else { @@ -427,10 +427,10 @@ void JASAramStream::updateChannel(u32 i_callbackType, JASChannel* i_channel, } else { field_0x0b8 += field_0x0b4; field_0x0b8 += block_samples * mBufCount - sp28 - - i_dspChannel->field_0x110; + - i_dspChannel->mLoopStartSample; field_0x0b8 -= mLoopEnd; field_0x0b8 += mLoopStart; - i_dspChannel->field_0x110 = 0; + i_dspChannel->mLoopStartSample = 0; field_0x120 = 0; field_0x12c |= 2; if (field_0x0c4 < 0xffffffff) { @@ -448,7 +448,7 @@ void JASAramStream::updateChannel(u32 i_callbackType, JASChannel* i_channel, if (field_0x0c4 < 0xffffffff) { fvar1 += field_0x0b8; } - fvar1 /= field_0x164; + fvar1 /= mSampleRate; field_0x0c8 = fvar1; if (field_0x0b8 + 400 >= mLoopEnd && !field_0x0c0) { if (mLoop) { @@ -456,9 +456,9 @@ void JASAramStream::updateChannel(u32 i_callbackType, JASChannel* i_channel, if (uvar5 >= mBufCount) { uvar5 = 0; } - i_dspChannel->field_0x110 = mLoopStart % block_samples + i_dspChannel->mLoopStartSample = mLoopStart % block_samples + uvar5 * block_samples; - field_0x120 = i_dspChannel->field_0x110; + field_0x120 = i_dspChannel->mLoopStartSample; field_0x12c |= 2; } else { i_dspChannel->field_0x102 = 0; @@ -472,17 +472,17 @@ void JASAramStream::updateChannel(u32 i_callbackType, JASChannel* i_channel, field_0x0bc += (mLoopEnd - 1) / block_samples - mLoopStart / block_samples + 1; field_0x0c0 = true; } - u32 uvar4 = i_dspChannel->field_0x070 - i_channel->field_0x104; + u32 uvar4 = i_dspChannel->mAramStreamPosition - i_channel->mWaveAramAddress; if (uvar4 != 0) { uvar4--; } - u32 sp18 = uvar4 / sBlockSize; + u32 blockCount = uvar4 / sBlockSize; u32 sp14 = (mLoopEnd - 1) / getBlockSamples(); - if (sp18 != field_0x0b0) { - bool cmp = sp18 < field_0x0b0; - while (sp18 != field_0x0b0) { + if (blockCount != field_0x0b0) { + bool cmp = blockCount < field_0x0b0; + while (blockCount != field_0x0b0) { if (!sLoadThread->sendCmdMsg(loadToAramTask, this)) { - JUT_WARN(741, "sendCmdMsg Failed %d %d (%d %d)", i_dspChannel->field_0x070, i_channel->field_0x104, sp18, field_0x0b0); + JUT_WARN(741, "sendCmdMsg Failed %d %d (%d %d)", i_dspChannel->mAramStreamPosition, i_channel->mWaveAramAddress, blockCount, field_0x0b0); struct_80451261 = true; break; } @@ -503,16 +503,16 @@ void JASAramStream::updateChannel(u32 i_callbackType, JASChannel* i_channel, field_0x11c = i_dspChannel->field_0x074; field_0x12c |= 1; } - i_dspChannel->field_0x114 += block_samples; - field_0x124 = i_dspChannel->field_0x114; + i_dspChannel->mEndSample += block_samples; + field_0x124 = i_dspChannel->mEndSample; field_0x12c |= 4; - mBufCount = field_0x160; + mBufCount = mBlocksPerChannel; field_0x0ad = false; } else { - if (mBufCount != field_0x160 - 1) { - mBufCount = field_0x160 - 1; - i_dspChannel->field_0x114 -= block_samples; - field_0x124 = i_dspChannel->field_0x114; + if (mBufCount != mBlocksPerChannel - 1) { + mBufCount = mBlocksPerChannel - 1; + i_dspChannel->mEndSample -= block_samples; + field_0x124 = i_dspChannel->mEndSample; field_0x12c |= 4; if (!field_0x0c0) { i_dspChannel->field_0x074 -= block_samples; @@ -528,8 +528,8 @@ void JASAramStream::updateChannel(u32 i_callbackType, JASChannel* i_channel, field_0x0ae &= ~4; } } - field_0x0b4 = i_dspChannel->field_0x074 + i_dspChannel->field_0x064; - if (field_0x118 >= field_0x160 - 2) { + field_0x0b4 = i_dspChannel->field_0x074 + i_dspChannel->mSamplesPerBlock; + if (field_0x118 >= mBlocksPerChannel - 2) { JUT_WARN_DEVICE(810, 1, "%s", "buffer under error"); field_0x0ae |= (u8)4; } @@ -538,10 +538,10 @@ void JASAramStream::updateChannel(u32 i_callbackType, JASChannel* i_channel, i_dspChannel->field_0x074 = field_0x11c; } if (field_0x12c & 2) { - i_dspChannel->field_0x110 = field_0x120; + i_dspChannel->mLoopStartSample = field_0x120; } if (field_0x12c & 4) { - i_dspChannel->field_0x114 = field_0x124; + i_dspChannel->mEndSample = field_0x124; } if (field_0x12c & 8) { i_dspChannel->field_0x102 = field_0x128; @@ -554,8 +554,8 @@ void JASAramStream::updateChannel(u32 i_callbackType, JASChannel* i_channel, } } JUT_ASSERT(834, ch < CHANNEL_MAX); - i_dspChannel->field_0x104 = (s16)field_0x130[ch]; - i_dspChannel->field_0x106 = (s16)field_0x13c[ch]; + i_dspChannel->mpLast = (s16)mpLasts[ch]; + i_dspChannel->mpPenult = (s16)mpPenults[ch]; } break; case JASChannel::CB_STOP: @@ -646,44 +646,44 @@ static JASOscillator::Point const OSC_RELEASE_TABLE[2] = { static JASOscillator::Data const OSC_ENV = {0, 1.0f, NULL, OSC_RELEASE_TABLE, 1.0f, 0.0f}; void JASAramStream::channelStart() { - u8 r31; - switch (field_0x158) { + u8 format; + switch (mFormat) { case 0: - r31 = 0; + format = WAVE_FORMAT_ADPCM4; break; case 1: - r31 = 3; + format = WAVE_FORMAT_PCM16; break; } for (int i = 0; i < mChannelNum; i++) { JASWaveInfo wave_info; - wave_info.field_0x00 = r31; - wave_info.field_0x02 = 0xff; - wave_info.field_0x10 = 0; - wave_info.field_0x14 = mBufCount * getBlockSamples(); - wave_info.field_0x18 = wave_info.field_0x14; - wave_info.field_0x1c = 0; - wave_info.field_0x1e = 0; + wave_info.mWaveFormat = format; + wave_info.mLoopFlag = 0xff; + wave_info.mLoopStartSample = 0; + wave_info.mLoopEndSample = mBufCount * getBlockSamples(); + wave_info.mSampleCount = wave_info.mLoopEndSample; + wave_info.mpLast = 0; + wave_info.mpPenult = 0; // probably a fake match, this should be set in the JASWaveInfo constructor static u32 const one = 1; wave_info.field_0x20 = &one; JASChannel* jc = JKR_NEW JASChannel(channelCallback, this); JUT_ASSERT(963, jc); jc->setPriority(0x7f7f); - for (u32 j = 0; j < 6; j++) { - jc->setMixConfig(j, field_0x1dc[j]); + for (u32 j = 0; j < DSP_OUTPUT_CHANNELS; j++) { + jc->setMixConfig(j, mMixConfig[j]); } - jc->setInitPitch(field_0x164 / JASDriver::getDacRate()); + jc->setInitPitch(mSampleRate / JASDriver::getDacRate()); jc->setOscInit(0, &OSC_ENV); - jc->field_0xdc.field_0x4 = wave_info; - jc->field_0x104 = field_0x148 + sBlockSize * field_0x160 * i; - jc->field_0xdc.field_0x0 = 0; + jc->field_0xdc.mWaveInfo = wave_info; + jc->mWaveAramAddress = mAramAddress + sBlockSize * mBlocksPerChannel * i; + jc->field_0xdc.mChannelType = 0; int ret = jc->playForce(); JUT_ASSERT(977, ret); JUT_ASSERT_MSG(979, mChannels[i] == NULL, "channelStart for already playing channel"); mChannels[i] = jc; } - field_0x0a8 = NULL; + mInitialChannel = NULL; } diff --git a/libs/JSystem/src/JAudio2/JASAudioReseter.cpp b/libs/JSystem/src/JAudio2/JASAudioReseter.cpp index 4d4f3517c1..46853d03e7 100644 --- a/libs/JSystem/src/JAudio2/JASAudioReseter.cpp +++ b/libs/JSystem/src/JAudio2/JASAudioReseter.cpp @@ -47,7 +47,7 @@ s32 JASAudioReseter::checkDone() const { s32 JASAudioReseter::calc() { if (field_0x0==0) { - for (size_t i = 0; i<64; i++) { + for (size_t i = 0; igetStatus() == 0) { handle->drop(); diff --git a/libs/JSystem/src/JAudio2/JASBNKParser.cpp b/libs/JSystem/src/JAudio2/JASBNKParser.cpp index b9f010ad60..5b46009381 100644 --- a/libs/JSystem/src/JAudio2/JASBNKParser.cpp +++ b/libs/JSystem/src/JAudio2/JASBNKParser.cpp @@ -23,7 +23,7 @@ JASBasicBank* JASBNKParser::createBasicBank(void const* stream, JKRHeap* heap) { u32 free_size = heap->getFreeSize(); TFileHeader* filep = (TFileHeader*)stream; - JUT_ASSERT(59, filep->id == 'IBNK'); + JUT_ASSERT(59, filep->mMagic == 'IBNK'); JASBasicBank* bank = NULL; switch (filep->mVersion) { case 0: @@ -79,11 +79,11 @@ JASBasicBank* JASBNKParser::Ver1::createBasicBank(void const* stream, JKRHeap* h JUT_ASSERT(155, op->id == 'Osci'); JASOscillator::Data* data = &osc_data[i]; data->mTarget = op->mTarget; - data->_04 = op->_08; + data->mRate = op->mRate; data->mScale = op->mScale; - data->_14 = op->_18; - data->mTable = (JASOscillator::Point*)(envt + op->mTableOffset); - data->rel_table = (JASOscillator::Point*)(envt + op->_10); + data->mVertex = op->mVertex; + data->mTable = (JASOscillator::Point*)(envt + op->mAttackEnvelopeOffset); + data->rel_table = (JASOscillator::Point*)(envt + op->mReleaseEnvelopeOffset); } TListChunk* list = list_chunk; JUT_ASSERT(172, list->count <= JASBank::PRG_OSC); @@ -203,7 +203,7 @@ JASBasicBank* JASBNKParser::Ver0::createBasicBank(void const* stream, JKRHeap* h osc = JKR_NEW_ARGS (heap, 0) JASOscillator::Data; JUT_ASSERT(386, osc != NULL); osc->mTarget = tosc->mTarget; - osc->_04 = tosc->field_0x4; + osc->mRate = tosc->field_0x4; JASOscillator::Point* points = tosc->mPointOffset.ptr(header); if (points != NULL) { @@ -230,7 +230,7 @@ JASBasicBank* JASBNKParser::Ver0::createBasicBank(void const* stream, JKRHeap* h } osc->mScale = tosc->mScale; - osc->_14 = tosc->field_0x14; + osc->mVertex = tosc->field_0x14; instp->setOsc(osc_idx, osc); } @@ -312,7 +312,7 @@ JASOscillator::Data* JASBNKParser::Ver0::findOscPtr(JASBasicBank* bank, THeader JASOscillator::Point const* JASBNKParser::Ver0::getOscTableEndPtr(JASOscillator::Point const* points) { const JASOscillator::Point* ptr = points; while(true) { - s16 tmp = ptr->_0; + s16 tmp = ptr->mEnvelopeMode; ptr++; if (tmp > 10) { break; diff --git a/libs/JSystem/src/JAudio2/JASBank.cpp b/libs/JSystem/src/JAudio2/JASBank.cpp index 7d4fb86514..29ebe11cb5 100644 --- a/libs/JSystem/src/JAudio2/JASBank.cpp +++ b/libs/JSystem/src/JAudio2/JASBank.cpp @@ -42,13 +42,13 @@ JASChannel* JASBank::noteOn(JASBank const* param_0, int param_1, u8 param_2, u8 return NULL; } channel->setPriority(param_4); - channel->field_0xdc.field_0x4 = *waveInfo; - channel->field_0x104 = wavePtr; - channel->field_0xdc.field_0x0 = stack_60.field_0x1c; + channel->field_0xdc.mWaveInfo = *waveInfo; + channel->mWaveAramAddress = wavePtr; + channel->field_0xdc.mChannelType = stack_60.field_0x1c; channel->setBankDisposeID(param_0); - channel->setInitPitch(stack_60.mPitch * (waveInfo->field_0x04 / JASDriver::getDacRate())); + channel->setInitPitch(stack_60.mPitch * (waveInfo->mSampleRate / JASDriver::getDacRate())); if (stack_60.field_0x1e == 0) { - channel->setKey(param_2 - waveInfo->field_0x01); + channel->setKey(param_2 - waveInfo->mBaseKey); } channel->setInitVolume(stack_60.mVolume); channel->setVelocity(param_3); @@ -79,10 +79,10 @@ JASChannel* JASBank::noteOnOsc(int param_0, u8 param_1, u8 param_2, u16 param_3, return NULL; } channel->setPriority(param_3); - channel->field_0x104 = param_0; - channel->field_0xdc.field_0x0 = 2; + channel->mOscillatorSomething = param_0; + channel->field_0xdc.mChannelType = 2; channel->setInitPitch(16736.016f / JASDriver::getDacRate()); - channel->setKey(param_1 - channel->field_0xdc.field_0x4.field_0x01); + channel->setKey(param_1 - channel->field_0xdc.mWaveInfo.mBaseKey); channel->setVelocity(param_2); channel->setOscInit(0, &OSC_ENV); if (!channel->play()) { diff --git a/libs/JSystem/src/JAudio2/JASBasicWaveBank.cpp b/libs/JSystem/src/JAudio2/JASBasicWaveBank.cpp index 5387238a56..9170e59ed1 100644 --- a/libs/JSystem/src/JAudio2/JASBasicWaveBank.cpp +++ b/libs/JSystem/src/JAudio2/JASBasicWaveBank.cpp @@ -50,7 +50,7 @@ void JASBasicWaveBank::incWaveTable(JASBasicWaveBank::TWaveGroup const* param_0) if (!handle->mHeap) { handle->mHeap = ¶m_0->mHeap; handle->field_0x4.field_0x20 = ¶m_0->_48; - handle->field_0x4.field_0x08 = param_0->mCtrlWaveArray[i].field_0x4; + handle->field_0x4.mOffsetStart = param_0->mCtrlWaveArray[i].field_0x4; } } } @@ -64,7 +64,7 @@ void JASBasicWaveBank::decWaveTable(JASBasicWaveBank::TWaveGroup const* param_0) if (handle->mHeap == ¶m_0->mHeap) { handle->mHeap = NULL; handle->field_0x4.field_0x20 = &mNoLoad; - handle->field_0x4.field_0x08 = -1; + handle->field_0x4.mOffsetStart = -1; } } } @@ -86,9 +86,9 @@ void JASBasicWaveBank::setWaveInfo(JASBasicWaveBank::TWaveGroup* wgrp, int index JUT_ASSERT(206, index >= 0); mWaveTable[param_2].field_0x4 = param_3; mWaveTable[param_2].field_0x4.field_0x20 = &mNoLoad; - mWaveTable[param_2].field_0x4.field_0x08 = -1; + mWaveTable[param_2].field_0x4.mOffsetStart = -1; wgrp->mCtrlWaveArray[index].field_0x0 = param_2; - wgrp->mCtrlWaveArray[index].field_0x4 = param_3.field_0x08; + wgrp->mCtrlWaveArray[index].field_0x4 = param_3.mOffsetStart; } JASBasicWaveBank::TWaveGroup::TWaveGroup() { @@ -131,5 +131,5 @@ intptr_t JASBasicWaveBank::TWaveHandle::getWavePtr() const { if (base == 0) { return 0; } - return (intptr_t)base + field_0x4.field_0x08; + return (intptr_t)base + field_0x4.mOffsetStart; } diff --git a/libs/JSystem/src/JAudio2/JASChannel.cpp b/libs/JSystem/src/JAudio2/JASChannel.cpp index 8e81ef3e86..ffd5056e1d 100644 --- a/libs/JSystem/src/JAudio2/JASChannel.cpp +++ b/libs/JSystem/src/JAudio2/JASChannel.cpp @@ -31,7 +31,7 @@ JASChannel::JASChannel(Callback i_callback, void* i_callbackData) : mKeySweepCount(0), mSkipSamples(0) { - field_0xdc.field_0x0 = 0; + field_0xdc.mChannelType = 0; field_0x104 = 0; mMixConfig[0].whole = 0x150; mMixConfig[1].whole = 0x210; @@ -171,12 +171,12 @@ void JASChannel::updateEffectorParam(JASDsp::TChannel* i_channel, u16* i_mixerVo f32 pan = 0.5f; f32 dolby = 0.0f; switch (JASDriver::getOutputMode()) { - case 0: + case JAS_OUTPUT_MONO: break; - case 1: + case JAS_OUTPUT_STEREO: pan = calcPan(&pan_vector); break; - case 2: + case JAS_OUTPUT_SURROUND: pan = calcPan(&pan_vector); dolby = calcEffect(&dolby_vector); break; @@ -229,7 +229,7 @@ s32 JASChannel::initialUpdateDSPChannel(JASDsp::TChannel* i_channel) { mCallback(CB_START, this, i_channel, mCallbackData); } - if (field_0xdc.field_0x4.field_0x20[0] == 0) { + if (field_0xdc.mWaveInfo.field_0x20[0] == 0) { JUT_WARN_DEVICE(346, 2, "%s", "Lost wave data while playing"); mDspCh->free(); mDspCh = NULL; @@ -245,19 +245,19 @@ s32 JASChannel::initialUpdateDSPChannel(JASDsp::TChannel* i_channel) { return -1; } - switch (field_0xdc.field_0x0) { + switch (field_0xdc.mChannelType) { case 0: - i_channel->setWaveInfo(field_0xdc.field_0x4, field_0x104, mSkipSamples); + i_channel->setWaveInfo(field_0xdc.mWaveInfo, mWaveAramAddress, mSkipSamples); break; case 2: - i_channel->setOscInfo(field_0x104); + i_channel->setOscInfo(mOscillatorSomething); break; } - for (u8 i = 0; i < 6; i++) { + for (u8 i = 0; i < DSP_OUTPUT_CHANNELS; i++) { MixConfig mix_config = mMixConfig[i]; u32 output_mode = JASDriver::getOutputMode(); - if (output_mode == 0) { + if (output_mode == JAS_OUTPUT_MONO) { switch (mix_config.parts.upper) { case 8: mix_config.parts.upper = 11; @@ -266,7 +266,7 @@ s32 JASChannel::initialUpdateDSPChannel(JASDsp::TChannel* i_channel) { mix_config.parts.upper = 2; break; } - } else if (output_mode == 1 && mix_config.parts.upper == 8) { + } else if (output_mode == JAS_OUTPUT_STEREO && mix_config.parts.upper == 8) { mix_config.parts.upper = 11; } i_channel->setBusConnect(i, mix_config.parts.upper); @@ -281,9 +281,9 @@ s32 JASChannel::initialUpdateDSPChannel(JASDsp::TChannel* i_channel) { } mVibrate.resetCounter(); mTremolo.resetCounter(); - u16 mixer_volume[6]; + u16 mixer_volume[DSP_OUTPUT_CHANNELS]; updateEffectorParam(i_channel, mixer_volume, effect_params); - for (u8 i = 0; i < 6; i++) { + for (u8 i = 0; i < DSP_OUTPUT_CHANNELS; i++) { i_channel->setMixerInitVolume(i, mixer_volume[i]); } @@ -307,7 +307,7 @@ s32 JASChannel::updateDSPChannel(JASDsp::TChannel* i_channel) { mCallback(CB_PLAY, this, i_channel, mCallbackData); } - if (field_0xdc.field_0x4.field_0x20[0] == 0) { + if (field_0xdc.mWaveInfo.field_0x20[0] == 0) { JUT_WARN_DEVICE(456, 2, "%s","Lost wave data while playing"); mDspCh->free(); mDspCh = NULL; @@ -378,18 +378,18 @@ s32 JASChannel::updateDSPChannel(JASDsp::TChannel* i_channel) { return 0; } -void JASChannel::updateAutoMixer(JASDsp::TChannel* i_channel, f32 param_1, f32 param_2, - f32 param_3, f32 param_4) { - if (JASDriver::getOutputMode() == 0) { - param_1 *= 0.707f; +void JASChannel::updateAutoMixer(JASDsp::TChannel* i_channel, f32 volume, f32 pan, + f32 fxmix, f32 dolby) { + if (JASDriver::getOutputMode() == JAS_OUTPUT_MONO) { + volume *= 0.707f; } - param_1 = JASCalc::clamp01(param_1); + volume = JASCalc::clamp01(volume); - u16 r31 = param_1 * JASDriver::getChannelLevel_dsp(); - u8 r30 = param_2 * 127.5f; - u8 r29 = param_4 * 127.5f; - u8 r28 = param_3 * 127.5f; - i_channel->setAutoMixer(r31, r30, r29, r28, 0); + u16 dspVolume = volume * JASDriver::getChannelLevel_dsp(); + u8 dspPan = pan * 127.5f; + u8 dspDolby = dolby * 127.5f; + u8 dspFxMix = fxmix * 127.5f; + i_channel->setAutoMixer(dspVolume, dspPan, dspDolby, dspFxMix, 0); } void JASChannel::updateMixer(f32 i_volume, f32 i_pan, f32 i_fxmix, f32 i_dolby, u16* i_volumeOut) { @@ -429,7 +429,7 @@ void JASChannel::updateMixer(f32 i_volume, f32 i_pan, f32 i_fxmix, f32 i_dolby, volume *= scale; break; default: - if (JASDriver::getOutputMode() == 0) { + if (JASDriver::getOutputMode() == JAS_OUTPUT_MONO) { volume *= scale; } else { volume *= JMASinRadian(scale * JGeometry::TUtil::PI() * 0.5f); @@ -471,7 +471,7 @@ void JASChannel::updateMixer(f32 i_volume, f32 i_pan, f32 i_fxmix, f32 i_dolby, volume *= scale; break; default: - if (JASDriver::getOutputMode() == 0) { + if (JASDriver::getOutputMode() == JAS_OUTPUT_MONO) { volume *= scale; } else { volume *= JMASinRadian(scale * JGeometry::TUtil::PI() * 0.5f); diff --git a/libs/JSystem/src/JAudio2/JASDSPChannel.cpp b/libs/JSystem/src/JAudio2/JASDSPChannel.cpp index 1c5171082e..62c983248c 100644 --- a/libs/JSystem/src/JAudio2/JASDSPChannel.cpp +++ b/libs/JSystem/src/JAudio2/JASDSPChannel.cpp @@ -10,7 +10,7 @@ JASDSPChannel::JASDSPChannel() : mStatus(STATUS_INACTIVE), mPriority(-1), mFlags(0), - field_0xc(0), + mUpdateCounter(0), mCallback(NULL), mCallbackData(NULL), mChannel(NULL) @@ -42,9 +42,9 @@ void JASDSPChannel::drop() { } void JASDSPChannel::initAll() { - sDspChannels = JKR_NEW_ARRAY_ARGS(JASDSPChannel, 0x40, JASDram, 0x20); + sDspChannels = JKR_NEW_ARRAY_ARGS(JASDSPChannel, DSP_CHANNELS, JASDram, 0x20); JUT_ASSERT(102, sDspChannels); - for (int i = 0; i < 0x40; i++) { + for (int i = 0; i < DSP_CHANNELS; i++) { sDspChannels[i].mChannel = JASDsp::getDSPHandle(i); } } @@ -56,7 +56,7 @@ JASDSPChannel* JASDSPChannel::alloc(u8 i_priority, Callback i_callback, void* i_ } channel->drop(); channel->mPriority = i_priority; - channel->field_0xc = 0; + channel->mUpdateCounter = 0; channel->mCallback = i_callback; channel->mCallbackData = i_callbackData; return channel; @@ -70,7 +70,7 @@ JASDSPChannel* JASDSPChannel::allocForce(u8 i_priority, Callback i_callback, voi channel->mStatus = STATUS_INACTIVE; channel->drop(); channel->mPriority = i_priority; - channel->field_0xc = 0; + channel->mUpdateCounter = 0; channel->mCallback = i_callback; channel->mCallbackData = i_callbackData; return channel; @@ -83,16 +83,16 @@ void JASDSPChannel::setPriority(u8 i_priority) { JASDSPChannel* JASDSPChannel::getLowestChannel(int i_priority) { s16 best_priority = 0xff; int best_index = -1; - int best_unknown = 0; - for (int i = 0; i < 0x40; i++) { + int best_updateCounter = 0; + for (int i = 0; i < DSP_CHANNELS; i++) { JASDSPChannel* channel = &sDspChannels[i]; s16 priority = channel->mPriority; if (priority < 0) { return &sDspChannels[i]; } if (priority <= i_priority && priority <= best_priority) { - if (priority != best_priority || channel->field_0xc > best_unknown) { - best_unknown = channel->field_0xc; + if (priority != best_priority || channel->mUpdateCounter > best_updateCounter) { + best_updateCounter = channel->mUpdateCounter; best_index = i; best_priority = priority; } @@ -107,14 +107,14 @@ JASDSPChannel* JASDSPChannel::getLowestChannel(int i_priority) { JASDSPChannel* JASDSPChannel::getLowestActiveChannel() { s16 best_priority = 0xff; int best_index = -1; - int best_unknown = 0; - for (int i = 0; i < 0x40; i++) { + int best_updateCounter = 0; + for (int i = 0; i < DSP_CHANNELS; i++) { JASDSPChannel* channel = &sDspChannels[i]; if (channel->mStatus == STATUS_ACTIVE) { s16 priority = channel->mPriority; if (priority < 0x7f && priority <= best_priority) { - if (priority != best_priority || channel->field_0xc > best_unknown) { - best_unknown = channel->field_0xc; + if (priority != best_priority || channel->mUpdateCounter > best_updateCounter) { + best_updateCounter = channel->mUpdateCounter; best_index = i; best_priority = priority; } @@ -195,7 +195,7 @@ void JASDSPChannel::updateProc() { mChannel->playStop(); mChannel->flush(); } else { - field_0xc++; + mUpdateCounter++; if (flush) { mChannel->flush(); } @@ -205,7 +205,7 @@ void JASDSPChannel::updateProc() { } void JASDSPChannel::updateAll() { - for (u32 i = 0; i < 0x40; i++) { + for (u32 i = 0; i < DSP_CHANNELS; i++) { if ((i & 0xf) == 0 && i != 0) { JASDsp::releaseHalt((i - 1) >> 4); } @@ -230,8 +230,8 @@ JASDSPChannel* JASDSPChannel::getHandle(u32 i_index) { u32 JASDSPChannel::getNumUse() { u32 count = 0; - for (int i = 0; i < 0x40; i++) { - if (sDspChannels[i].mStatus == 0) { + for (int i = 0; i < DSP_CHANNELS; i++) { + if (sDspChannels[i].mStatus == STATUS_ACTIVE) { count++; } } @@ -240,8 +240,8 @@ u32 JASDSPChannel::getNumUse() { u32 JASDSPChannel::getNumFree() { u32 count = 0; - for (int i = 0; i < 0x40; i++) { - if (sDspChannels[i].mStatus == 1) { + for (int i = 0; i < DSP_CHANNELS; i++) { + if (sDspChannels[i].mStatus == STATUS_INACTIVE) { count++; } } @@ -250,8 +250,8 @@ u32 JASDSPChannel::getNumFree() { u32 JASDSPChannel::getNumBreak() { u32 count = 0; - for (int i = 0; i < 0x40; i++) { - if (sDspChannels[i].mStatus == 2) { + for (int i = 0; i < DSP_CHANNELS; i++) { + if (sDspChannels[i].mStatus == STATUS_DROP) { count++; } } diff --git a/libs/JSystem/src/JAudio2/JASDSPInterface.cpp b/libs/JSystem/src/JAudio2/JASDSPInterface.cpp index ba2d110436..e8d7fe050c 100644 --- a/libs/JSystem/src/JAudio2/JASDSPInterface.cpp +++ b/libs/JSystem/src/JAudio2/JASDSPInterface.cpp @@ -72,8 +72,8 @@ f32 JASDsp::getDSPMixerLevel() { return sDSPVolume; } -JASDsp::TChannel* JASDsp::getDSPHandle(int param_0) { - return CH_BUF + param_0; +JASDsp::TChannel* JASDsp::getDSPHandle(int index) { + return CH_BUF + index; } JASDsp::TChannel* JASDsp::getDSPHandleNc(int param_0) { @@ -87,12 +87,12 @@ void JASDsp::setFilterTable(s16* param_0, s16* param_1, u32 param_2) { } void JASDsp::flushBuffer() { - DCFlushRange(CH_BUF, sizeof(TChannel) * 64); + DCFlushRange(CH_BUF, sizeof(TChannel) * DSP_CHANNELS); DCFlushRange(FX_BUF, sizeof(FxBuf) * 4); } void JASDsp::invalChannelAll() { - DCInvalidateRange(CH_BUF, sizeof(TChannel) * 64); + DCInvalidateRange(CH_BUF, sizeof(TChannel) * DSP_CHANNELS); } u8 const ATTRIBUTE_ALIGN(32) JASDsp::DSPADPCM_FILTER[64] = { @@ -426,16 +426,16 @@ u32 const ATTRIBUTE_ALIGN(32) JASDsp::DSPRES_FILTER[320] = { }; void JASDsp::initBuffer() { - CH_BUF = JKR_NEW_ARRAY_ARGS(TChannel, 64, JASDram, 0x20); + CH_BUF = JKR_NEW_ARRAY_ARGS(TChannel, DSP_CHANNELS, JASDram, 0x20); JUT_ASSERT(354, CH_BUF); FX_BUF = JKR_NEW_ARRAY_ARGS(FxBuf, 4, JASDram, 0x20); JUT_ASSERT(356, FX_BUF); - JASCalc::bzero(CH_BUF, 0x6000); + JASCalc::bzero(CH_BUF, sizeof(TChannel) * DSP_CHANNELS); JASCalc::bzero(FX_BUF, sizeof(FxBuf) * 4); for (u8 i = 0; i < 4; i++) { setFXLine(i, NULL, NULL); } - DsetupTable(0x40, uintptr_t(CH_BUF), uintptr_t(&DSPRES_FILTER), uintptr_t(&DSPADPCM_FILTER), uintptr_t(FX_BUF)); + DsetupTable(DSP_CHANNELS, uintptr_t(CH_BUF), uintptr_t(&DSPRES_FILTER), uintptr_t(&DSPADPCM_FILTER), uintptr_t(FX_BUF)); flushBuffer(); } @@ -501,8 +501,8 @@ void JASDsp::TChannel::init() { mIsFinished = 0; mForcedStop = 0; mIsActive = 0; - field_0x058 = 0; - field_0x068 = 0; + mAutoMixerBeenSet = 0; + mSamplePosition = 0; initFilter(); } @@ -549,41 +549,41 @@ bool JASDsp::TChannel::isFinish() const { return mIsFinished != 0; } -void JASDsp::TChannel::setWaveInfo(JASWaveInfo const& param_0, u32 param_1, u32 param_2) { +void JASDsp::TChannel::setWaveInfo(JASWaveInfo const& waveInfo, u32 aramAddress, u32 skipSamples) { int i; JUT_ASSERT(610, dspMutex); - field_0x118 = param_1; + mWaveAramAddress = aramAddress; static const u8 COMP_BLOCKSAMPLES[8] = {0x10, 0x10, 0x01, 0x01, 0x01, 0x10, 0x10, 0x01}; - field_0x064 = COMP_BLOCKSAMPLES[param_0.field_0x00]; + mSamplesPerBlock = COMP_BLOCKSAMPLES[waveInfo.mWaveFormat]; static const u8 COMP_BLOCKBYTES[8] = {0x09, 0x05, 0x08, 0x10, 0x01, 0x01, 0x01, 0x01}; - field_0x100 = COMP_BLOCKBYTES[param_0.field_0x00]; - field_0x068 = 0; - if (field_0x100 >= 4) { - field_0x11c = param_0.field_0x18; - field_0x102 = param_0.field_0x02; + mBytesPerBlock = COMP_BLOCKBYTES[waveInfo.mWaveFormat]; + mSamplePosition = 0; + if (mBytesPerBlock >= 4) { + mSampleCount = waveInfo.mSampleCount; + field_0x102 = waveInfo.mLoopFlag; if (field_0x102) { - if (param_2 == 1) { - param_2 = param_0.field_0x10; + if (skipSamples == 1) { + skipSamples = waveInfo.mLoopStartSample; } - field_0x110 = param_0.field_0x10; - field_0x114 = param_0.field_0x14; - field_0x104 = param_0.field_0x1c; - field_0x106 = param_0.field_0x1e; + mLoopStartSample = waveInfo.mLoopStartSample; + mEndSample = waveInfo.mLoopEndSample; + mpLast = waveInfo.mpLast; + mpPenult = waveInfo.mpPenult; } else { - field_0x114 = field_0x11c; + mEndSample = mSampleCount; } - if (param_2 && field_0x114 > param_2) { - switch (param_0.field_0x00) { - case 0: - case 1: - field_0x068 = param_2; - field_0x118 += param_2 * field_0x100 >> 4; - field_0x110 -= param_2; - field_0x114 -= param_2; + if (skipSamples && mEndSample > skipSamples) { + switch (waveInfo.mWaveFormat) { + case WAVE_FORMAT_ADPCM4: + case WAVE_FORMAT_ADPCM2: + mSamplePosition = skipSamples; + mWaveAramAddress += skipSamples * mBytesPerBlock >> 4; + mLoopStartSample -= skipSamples; + mEndSample -= skipSamples; break; - case 2: - case 3: - field_0x068 = param_2; + case WAVE_FORMAT_PCM8: + case WAVE_FORMAT_PCM16: + mSamplePosition = skipSamples; break; } } @@ -595,28 +595,27 @@ void JASDsp::TChannel::setWaveInfo(JASWaveInfo const& param_0, u32 param_1, u32 void JASDsp::TChannel::setOscInfo(u32 param_0) { JUT_ASSERT(671, dspMutex); - field_0x118 = 0; - field_0x064 = 16; - field_0x100 = param_0; + mWaveAramAddress = 0; + mSamplesPerBlock = 16; + mBytesPerBlock = param_0; } void JASDsp::TChannel::initAutoMixer() { JUT_ASSERT(688, dspMutex); - if (field_0x058) { - field_0x054 = field_0x056; + if (mAutoMixerBeenSet) { + mAutoMixerInitVolume = mAutoMixerVolume; } else { - field_0x054 = 0; - field_0x058 = 1; + mAutoMixerInitVolume = 0; + mAutoMixerBeenSet = 1; } } -void JASDsp::TChannel::setAutoMixer(u16 param_0, u8 param_1, u8 param_2, u8 param_3, - u8 param_4) { +void JASDsp::TChannel::setAutoMixer(u16 volume, u8 pan, u8 dolby, u8 fxMix, u8) { JUT_ASSERT(709, dspMutex); - field_0x050 = (param_1 << 8) | param_2; - field_0x052 = param_3 << 8 | param_3 << 1; - field_0x056 = param_0; - field_0x058 = 1; + mAutoMixerPanDolby = (pan << 8) | dolby; + mAutoMixerFxMix = fxMix << 8 | fxMix << 1; + mAutoMixerVolume = volume; + mAutoMixerBeenSet = 1; } void JASDsp::TChannel::setPitch(u16 param_0) { @@ -627,21 +626,20 @@ void JASDsp::TChannel::setPitch(u16 param_0) { mPitch = param_0; } -void JASDsp::TChannel::setMixerInitVolume(u8 param_0, s16 param_1) { +void JASDsp::TChannel::setMixerInitVolume(u8 outputChannel, s16 volume) { JUT_ASSERT(798, dspMutex); - u16* tmp = field_0x010[param_0]; - tmp[2] = param_1; - tmp[1] = param_1; - tmp[3] = 0; + OutputChannelConfig& cfg = mOutputChannels[outputChannel]; + cfg.mCurrentVolume = volume; + cfg.mTargetVolume = volume; + cfg.mVolumeProgress = 0; } -void JASDsp::TChannel::setMixerVolume(u8 param_0, s16 param_1) { - u16* tmp; +void JASDsp::TChannel::setMixerVolume(u8 outputChannel, s16 volume) { JUT_ASSERT(841, dspMutex); if (mForcedStop == 0) { - tmp = field_0x010[param_0]; - tmp[1] = param_1; - tmp[3] &= 0xff; + OutputChannelConfig& cfg = mOutputChannels[outputChannel]; + cfg.mTargetVolume = volume; + cfg.mVolumeProgress &= 0xff; } } @@ -699,16 +697,16 @@ void JASDsp::TChannel::setDistFilter(s16 param_0) { iir_filter_params[4] = param_0; } -void JASDsp::TChannel::setBusConnect(u8 param_0, u8 param_1) { +void JASDsp::TChannel::setBusConnect(u8 outputChannel, u8 param_1) { JUT_ASSERT(973, dspMutex); - u16* tmp = field_0x010[param_0]; + OutputChannelConfig& tmp = mOutputChannels[outputChannel]; static u16 const connect_table[12] = { 0x0000, 0x0D00, 0x0D60, 0x0DC0, 0x0E20, 0x0E80, 0x0EE0, 0x0CA0, 0x0F40, 0x0FA0, 0x0B00, 0x09A0, }; u16 r30 = 0; r30 = connect_table[param_1]; - tmp[0] = r30; + tmp.mBusConnect = r30; } u16 DSP_CreateMap2(u32 param_0) { diff --git a/libs/JSystem/src/JAudio2/JASDriverIF.cpp b/libs/JSystem/src/JAudio2/JASDriverIF.cpp index 2c9f305b61..7d602a8856 100644 --- a/libs/JSystem/src/JAudio2/JASDriverIF.cpp +++ b/libs/JSystem/src/JAudio2/JASDriverIF.cpp @@ -23,10 +23,10 @@ f32 JASDriver::getDSPLevel() { return JASDsp::getDSPMixerLevel(); } -u32 JASDriver::JAS_SYSTEM_OUTPUT_MODE = 0x00000001; +u32 JASDriver::JAS_SYSTEM_OUTPUT_MODE = JAS_OUTPUT_STEREO; -void JASDriver::setOutputMode(u32 param_0) { - JAS_SYSTEM_OUTPUT_MODE = param_0; +void JASDriver::setOutputMode(u32 mode) { + JAS_SYSTEM_OUTPUT_MODE = mode; } u32 JASDriver::getOutputMode() { diff --git a/libs/JSystem/src/JAudio2/JASDrumSet.cpp b/libs/JSystem/src/JAudio2/JASDrumSet.cpp index a7a483fe29..8c8736451d 100644 --- a/libs/JSystem/src/JAudio2/JASDrumSet.cpp +++ b/libs/JSystem/src/JAudio2/JASDrumSet.cpp @@ -49,11 +49,11 @@ bool JASDrumSet::getParam(int key, int param_1, JASInstParam* param_2) const { static JASOscillator::Data osc; osc.mTarget = 0; - osc._04 = 1.0f; + osc.mRate = 1.0f; osc.mTable = NULL; osc.rel_table = NULL; osc.mScale = 1.0f; - osc._14 = 0.0f; + osc.mVertex = 0.0f; static JASOscillator::Data* oscp = &osc; diff --git a/libs/JSystem/src/JAudio2/JASOscillator.cpp b/libs/JSystem/src/JAudio2/JASOscillator.cpp index 4073af7424..f4bf2ade6f 100644 --- a/libs/JSystem/src/JAudio2/JASOscillator.cpp +++ b/libs/JSystem/src/JAudio2/JASOscillator.cpp @@ -4,9 +4,9 @@ JASOscillator::JASOscillator() { mData = NULL; - _14 = 0; + mCurPoint = 0; mDirectRelease = 0; - _18 = 0; + mEnvelopeMode = 0; _1C = 0; _04 = _08 = _10 = _0C = 0.0f; } @@ -17,7 +17,7 @@ void JASOscillator::initStart(JASOscillator::Data const* data) { _04 = 0.0f; _08 = 0.0f; _0C = 0.0f; - _14 = 0; + mCurPoint = 0; mDirectRelease = 0; if (!data) { _1C = 0; @@ -30,8 +30,8 @@ void JASOscillator::initStart(JASOscillator::Data const* data) { return; } - _10 = mData->mTable[0]._4 / 32768.0f; - _18 = mData->mTable[0]._0; + _10 = mData->mTable[0].mValue / 32768.0f; + mEnvelopeMode = mData->mTable[0].mEnvelopeMode; _1C = 1; } @@ -44,13 +44,13 @@ void JASOscillator::incCounter(f32 param_0) { case 1: break; } - _04 += param_0 * mData->_04; + _04 += param_0 * mData->mRate; update(); } f32 JASOscillator::getValue() const { JUT_ASSERT(120, mData); - return _08 * mData->mScale + mData->_14; + return _08 * mData->mScale + mData->mVertex; } void JASOscillator::release() { @@ -63,8 +63,8 @@ void JASOscillator::release() { _04 = 0.0f; _0C = _08; _10 = 0.0f; - _14 = 0; - _18 = (mDirectRelease >> 14) & 3; + mCurPoint = 0; + mEnvelopeMode = (mDirectRelease >> 14) & 3; _1C = 4; update(); return; @@ -74,9 +74,9 @@ void JASOscillator::release() { JUT_ASSERT(157, mData->rel_table != NULL); _04 = 0.0f; _0C = _08; - _10 = mData->rel_table[0]._4 / 32768.0f; - _14 = 0; - _18 = mData->rel_table[0]._0; + _10 = mData->rel_table[0].mValue / 32768.0f; + mCurPoint = 0; + mEnvelopeMode = mData->rel_table[0].mEnvelopeMode; } _1C = 3; @@ -104,31 +104,31 @@ void JASOscillator::update() { return; } - while (_04 >= psVar1[_14]._2) { - _04 -= psVar1[_14]._2; + while (_04 >= psVar1[mCurPoint].mTime) { + _04 -= psVar1[mCurPoint].mTime; _08 = _10; - _14++; + mCurPoint++; _0C = _08; - const BE(s16)* ps = &psVar1[_14]._0; - s16 r26 = ps[0]; - switch(r26) { - case 0xf: + const Point* ps = &psVar1[mCurPoint]; + s16 mode = ps->mEnvelopeMode; + switch(mode) { + case ENVELOPE_STOP: _1C = 0; return; - case 0xe: + case ENVELOPE_HOLD: _1C = 2; return; - case 0xd: - _14 = ps[2]; + case ENVELOPE_LOOP: + mCurPoint = ps->mValue; break; default: - _18 = r26; - _10 = ps[2] / 32768.0f; + mEnvelopeMode = mode; + _10 = ps->mValue / 32768.0f; break; } } - updateCurrentValue(psVar1[_14]._2); + updateCurrentValue(psVar1[mCurPoint].mTime); } f32 const JASOscillator::sCurveTableLinear[17] = { @@ -163,7 +163,7 @@ static f32* table_list[4] = { }; void JASOscillator::updateCurrentValue(f32 param_0) { - f32* table = table_list[_18]; + f32* table = table_list[mEnvelopeMode]; f32 fVar1 = 16.0f * (_04 / param_0); u32 index = (u32) fVar1; f32 fVar3 = (fVar1 - index); diff --git a/libs/JSystem/src/JAudio2/JASSimpleWaveBank.cpp b/libs/JSystem/src/JAudio2/JASSimpleWaveBank.cpp index 8bfcc2c460..52935324d8 100644 --- a/libs/JSystem/src/JAudio2/JASSimpleWaveBank.cpp +++ b/libs/JSystem/src/JAudio2/JASSimpleWaveBank.cpp @@ -44,7 +44,7 @@ intptr_t JASSimpleWaveBank::TWaveHandle::getWavePtr() const { if (base == NULL) { return 0; } - return (intptr_t)base + mWaveInfo.field_0x08; + return (intptr_t)base + mWaveInfo.mOffsetStart; } JASSimpleWaveBank::TWaveHandle::TWaveHandle() { diff --git a/libs/JSystem/src/JAudio2/JASTrack.cpp b/libs/JSystem/src/JAudio2/JASTrack.cpp index de070adde3..a60577ca38 100644 --- a/libs/JSystem/src/JAudio2/JASTrack.cpp +++ b/libs/JSystem/src/JAudio2/JASTrack.cpp @@ -356,7 +356,7 @@ int JASTrack::gateOn(u32 param_0, u32 i_velocity, f32 i_time, u32 i_flags) { } else { JASChannel* channel = channel_mgr->mChannels[0]; if (channel != NULL) { - channel->setKey(uvar7 - channel->field_0xdc.field_0x4.field_0x01); + channel->setKey(uvar7 - channel->field_0xdc.mWaveInfo.mBaseKey); channel->setVelocity(i_velocity); channel->setUpdateTimer(update_timer); } @@ -545,10 +545,10 @@ void JASTrack::setOscTable(u32 osc_no, JASOscillator::Point const* i_table) { void JASTrack::setOscAdsr(s16 param_0, s16 param_1, s16 param_2, s16 param_3, u16 i_directRelease) { mOscParam[0] = sEnvOsc; mOscParam[0].mTable = mOscPoint; - mOscPoint[0]._2 = param_0; - mOscPoint[1]._2 = param_1; - mOscPoint[2]._2 = param_2; - mOscPoint[2]._4 = param_3; + mOscPoint[0].mTime = param_0; + mOscPoint[1].mTime = param_1; + mOscPoint[2].mTime = param_2; + mOscPoint[2].mValue = param_3; mDirectRelease = i_directRelease; } diff --git a/libs/JSystem/src/JAudio2/JASWSParser.cpp b/libs/JSystem/src/JAudio2/JASWSParser.cpp index 35ec9526ed..4288377548 100644 --- a/libs/JSystem/src/JAudio2/JASWSParser.cpp +++ b/libs/JSystem/src/JAudio2/JASWSParser.cpp @@ -48,17 +48,17 @@ JASBasicWaveBank* JASWSParser::createBasicWaveBank(void const* stream, JKRHeap* for (int j = 0; j < ctrl->mWaveCount; j++) { TWave* wave = archive->mWaveOffsets[j].ptr(header); JASWaveInfo wave_info; - wave_info.field_0x00 = wave->_01; - wave_info.field_0x01 = wave->_02; - wave_info.field_0x04 = wave->_04; - wave_info.field_0x08 = wave->mOffset; - wave_info.field_0x0c = wave->_0C; - wave_info.field_0x02 = wave->_10 == 0 ? 0 : 0xff; - wave_info.field_0x10 = wave->_14; - wave_info.field_0x14 = wave->_18; - wave_info.field_0x18 = wave->_1C; - wave_info.field_0x1c = wave->_20; - wave_info.field_0x1e = wave->_22; + wave_info.mWaveFormat = wave->mWaveFormat; + wave_info.mBaseKey = wave->mBaseKey; + wave_info.mSampleRate = wave->mSampleRate; + wave_info.mOffsetStart = wave->mAWOffsetStart; + wave_info.mOffsetLength = wave->mAWOffsetEnd; + wave_info.mLoopFlag = wave->mLoopFlags == 0 ? 0 : 0xff; + wave_info.mLoopStartSample = wave->mLoopStartSample; + wave_info.mLoopEndSample = wave->mLoopEndSample; + wave_info.mSampleCount = wave->mSampleCount; + wave_info.mpLast = wave->mpLast; + wave_info.mpPenult = wave->mpPenult; TCtrlWave* ctrl_wave = ctrl->mCtrlWaveOffsets[j].ptr(header); u16 local_74 = JSULoHalf(ctrl_wave->_00); wave_bank->setWaveInfo(wave_group, j, local_74, wave_info); @@ -104,17 +104,17 @@ JASSimpleWaveBank* JASWSParser::createSimpleWaveBank(void const* stream, JKRHeap for (int i = 0; i < ctrl->mWaveCount; i++) { TWave* wave = archive->mWaveOffsets[i].ptr(header); JASWaveInfo wave_info; - wave_info.field_0x00 = wave->_01; - wave_info.field_0x01 = wave->_02; - wave_info.field_0x04 = wave->_04; - wave_info.field_0x08 = wave->mOffset; - wave_info.field_0x0c = wave->_0C; - wave_info.field_0x02 = wave->_10 == 0 ? 0 : 0xff; - wave_info.field_0x10 = wave->_14; - wave_info.field_0x14 = wave->_18; - wave_info.field_0x18 = wave->_1C; - wave_info.field_0x1c = wave->_20; - wave_info.field_0x1e = wave->_22; + wave_info.mWaveFormat = wave->mWaveFormat; + wave_info.mBaseKey = wave->mBaseKey; + wave_info.mSampleRate = wave->mSampleRate; + wave_info.mOffsetStart = wave->mAWOffsetStart; + wave_info.mOffsetLength = wave->mAWOffsetEnd; + wave_info.mLoopFlag = wave->mLoopFlags == 0 ? 0 : 0xff; + wave_info.mLoopStartSample = wave->mLoopStartSample; + wave_info.mLoopEndSample = wave->mLoopEndSample; + wave_info.mSampleCount = wave->mSampleCount; + wave_info.mpLast = wave->mpLast; + wave_info.mpPenult = wave->mpPenult; TCtrlWave* ctrl_wave = ctrl->mCtrlWaveOffsets[i].ptr(header); u32 tmp = JSULoHalf(ctrl_wave->_00); wave_bank->setWaveInfo(tmp, wave_info); diff --git a/libs/JSystem/src/JAudio2/JAUInitializer.cpp b/libs/JSystem/src/JAudio2/JAUInitializer.cpp index 3800170f77..9a9a3eeabd 100644 --- a/libs/JSystem/src/JAudio2/JAUInitializer.cpp +++ b/libs/JSystem/src/JAudio2/JAUInitializer.cpp @@ -27,7 +27,7 @@ JAU_JASInitializer::JAU_JASInitializer() { #endif dvdThreadPriority_ = 3; audioThreadPriority_ = 2; - field_0x1c = 0x80; + mJasTrackPoolSize = 0x80; dspLevel_ = 1.0f; aramBlockSize_ = 0x2760; aramChannelNum_ = 2; @@ -54,7 +54,7 @@ void JAU_JASInitializer::initJASystem(JKRSolidHeap* heap) { JASKernel::setupAramHeap(audioMemory_, audioMemSize_); - JASTrack::newMemPool(field_0x1c); + JASTrack::newMemPool(mJasTrackPoolSize); if (field_0x20 > 0) { JASTrack::TChannelMgr::newMemPool(field_0x20); } @@ -80,33 +80,33 @@ void JAU_JASInitializer::initJASystem(JKRSolidHeap* heap) { } #if PLATFORM_SHIELD - JASDriver::setOutputMode(1); + JASDriver::setOutputMode(JAS_OUTPUT_STEREO); #else switch (OSGetSoundMode()) { - case 0: - JASDriver::setOutputMode(0); + case OS_SOUND_MODE_MONO: + JASDriver::setOutputMode(JAS_OUTPUT_MONO); break; - case 1: - JASDriver::setOutputMode(1); + case OS_SOUND_MODE_STEREO: + JASDriver::setOutputMode(JAS_OUTPUT_STEREO); break; } #endif } JAU_JAIInitializer::JAU_JAIInitializer() { - field_0x0 = 100; - field_0x4 = 4; - field_0x8 = 2; - field_0xc = 16; + mJaiSePoolSize = 100; + mJaiSeqPoolSize = 4; + mJaiStreamPoolSize = 2; + mJaiSoundChildPoolSize = 16; } // NONMATCHING JASPoolAllocObject<_> locations void JAU_JAIInitializer::initJAInterface() { s32 r30 = JASDram->getFreeSize(); - JAIStream::newMemPool(field_0x8); - JAISeq::newMemPool(field_0x4); - JAISe::newMemPool(field_0x0); - JAISoundChild::newMemPool(field_0xc); + JAIStream::newMemPool(mJaiStreamPoolSize); + JAISeq::newMemPool(mJaiSeqPoolSize); + JAISe::newMemPool(mJaiSePoolSize); + JAISoundChild::newMemPool(mJaiSoundChildPoolSize); s32 r29 = JASDram->getFreeSize(); OS_REPORT("JAU_JAIInitializer uses %d bytes\n", r30 - r29); } diff --git a/libs/JSystem/src/JAudio2/JAUSectionHeap.cpp b/libs/JSystem/src/JAudio2/JAUSectionHeap.cpp index 8207c21f2f..e1f45de1ae 100644 --- a/libs/JSystem/src/JAudio2/JAUSectionHeap.cpp +++ b/libs/JSystem/src/JAudio2/JAUSectionHeap.cpp @@ -245,11 +245,11 @@ u8* JAUSection::newStaticSeqDataBlock_(JAISoundID param_0, u32 size) { return NULL; } -bool JAUSection::newStaticSeqData(JAISoundID param_0, void const* param_1, u32 param_2) { +bool JAUSection::newStaticSeqData(JAISoundID param_0, void const* param_1, u32 size) { { - u8* r30 = newStaticSeqDataBlock_(param_0, param_2); + u8* r30 = newStaticSeqDataBlock_(param_0, size); if (r30) { - memcpy(r30, param_1, param_2); + memcpy(r30, param_1, size); return true; } } diff --git a/libs/JSystem/src/JAudio2/dspproc.cpp b/libs/JSystem/src/JAudio2/dspproc.cpp index a9c08e37ff..6a40210fd0 100644 --- a/libs/JSystem/src/JAudio2/dspproc.cpp +++ b/libs/JSystem/src/JAudio2/dspproc.cpp @@ -20,10 +20,10 @@ static void setup_callback(u16 param_0) { flag = FALSE; } -void DsetupTable(u32 param_0, u32 param_1, u32 param_2, u32 param_3, u32 param_4) { +void DsetupTable(u32 channelCount, u32 channelBufferAddress, u32 param_2, u32 param_3, u32 param_4) { u32 table[5]; - table[0] = (param_0 & 0xFFFF) | 0x81000000; - table[1] = param_1; + table[0] = (channelCount & 0xFFFF) | 0x81000000; + table[1] = channelBufferAddress; table[2] = param_2; table[3] = param_3; table[4] = param_4; diff --git a/libs/JSystem/src/JAudio2/dsptask.cpp b/libs/JSystem/src/JAudio2/dsptask.cpp index 86d6323333..881c75252a 100644 --- a/libs/JSystem/src/JAudio2/dsptask.cpp +++ b/libs/JSystem/src/JAudio2/dsptask.cpp @@ -527,7 +527,7 @@ static DSPTaskInfo audio_task ATTRIBUTE_ALIGN(32); static u8 AUDIO_YIELD_BUFFER[8192] ATTRIBUTE_ALIGN(32); -void DspBoot(void (*param_0)(void*)) { +void DspBoot(void (*requestCallback)(void*)) { DspInitWork(); OS_REPORT("Dsp をブートします\n"); audio_task.priority = 0xf0; @@ -542,7 +542,7 @@ void DspBoot(void (*param_0)(void*)) { audio_task.init_cb = DspHandShake; audio_task.res_cb = NULL; audio_task.done_cb = NULL; - audio_task.req_cb = param_0; + audio_task.req_cb = requestCallback; DSPInit(); DSPAddPriorTask(&audio_task); OS_REPORT("Dspブートしました\n"); diff --git a/libs/JSystem/src/JAudio2/osdsp_task.cpp b/libs/JSystem/src/JAudio2/osdsp_task.cpp index d9f4973880..7ec8f3f855 100644 --- a/libs/JSystem/src/JAudio2/osdsp_task.cpp +++ b/libs/JSystem/src/JAudio2/osdsp_task.cpp @@ -12,7 +12,7 @@ extern "C" void __DSP_remove_task(DSPTaskInfo* task); static void Dsp_Update_Request(); -static vu8 struct_80451308; +static vu8 DspRunningStatus; static u8 struct_80451309; DSPTaskInfo* DSP_prior_task; @@ -23,7 +23,7 @@ extern "C" void __DSPHandler(__OSInterrupt interrupt, OSContext* context) { OSClearContext(&funcContext); OSSetCurrentContext(&funcContext); - if (struct_80451308 == 1 || struct_80451308 == 0) { + if (DspRunningStatus == 1 || DspRunningStatus == 0) { __DSP_curr_task = DSP_prior_task; } @@ -38,7 +38,7 @@ extern "C" void __DSPHandler(__OSInterrupt interrupt, OSContext* context) { case 0xDCD10000: __DSP_curr_task->state = 1; if (__DSP_curr_task == DSP_prior_task) { - struct_80451308 = 1; + DspRunningStatus = 1; } if (__DSP_curr_task->init_cb != NULL) { __DSP_curr_task->init_cb(__DSP_curr_task); @@ -47,7 +47,7 @@ extern "C" void __DSPHandler(__OSInterrupt interrupt, OSContext* context) { case 0xDCD10001: __DSP_curr_task->state = 1; if (__DSP_curr_task == DSP_prior_task) { - struct_80451308 = 1; + DspRunningStatus = 1; Dsp_Update_Request(); } if (__DSP_curr_task->res_cb != NULL) { @@ -97,7 +97,7 @@ extern "C" void __DSPHandler(__OSInterrupt interrupt, OSContext* context) { __DSP_curr_task = DSP_prior_task; Dsp_Update_Request(); } else { - struct_80451308 = 3; + DspRunningStatus = 3; DSPSendMailToDSP(0xCDD10001); while (DSPCheckMailToDSP() != 0); __DSP_exec_task(DSP_prior_task, __DSP_first_task); @@ -113,7 +113,7 @@ extern "C" void __DSPHandler(__OSInterrupt interrupt, OSContext* context) { static u32 sync_stack[5]; void DsyncFrame2(u32 param_0, u32 param_1, u32 param_2) { - if (struct_80451308 != 1) { + if (DspRunningStatus != 1) { sync_stack[0] = param_0; struct_80451309 = 1; sync_stack[1] = param_1; @@ -125,7 +125,7 @@ void DsyncFrame2(u32 param_0, u32 param_1, u32 param_2) { } static void DsyncFrame3(u32 param_0, u32 param_1, u32 param_2, u32 param_3, u32 param_4) { - if (struct_80451308 != 1) { + if (DspRunningStatus != 1) { sync_stack[0] = param_0; struct_80451309 = 2; sync_stack[1] = param_1; @@ -152,9 +152,9 @@ static void Dsp_Update_Request() { } int Dsp_Running_Check() { - return struct_80451308 == 1 ? TRUE : FALSE; + return DspRunningStatus == 1 ? TRUE : FALSE; } void Dsp_Running_Start() { - struct_80451308 = 1; + DspRunningStatus = 1; } diff --git a/src/Z2AudioLib/Z2AudioMgr.cpp b/src/Z2AudioLib/Z2AudioMgr.cpp index 32fa4de849..218881fbc9 100644 --- a/src/Z2AudioLib/Z2AudioMgr.cpp +++ b/src/Z2AudioLib/Z2AudioMgr.cpp @@ -30,15 +30,15 @@ Z2AudioMgr::Z2AudioMgr() : mSoundStarter(true) { void Z2AudioMgr::init(JKRSolidHeap* heap, u32 memSize, void* baaData, JKRArchive* seqArc) { JAU_JASInitializer JASInitializer; JASInitializer.audioMemSize_ = memSize; - JASInitializer.field_0x1c = 140; + JASInitializer.mJasTrackPoolSize = 140; JASInitializer.dspLevel_ = 1.3f; JASInitializer.waveArcDir_ = "Audiores/Waves/"; JASInitializer.initJASystem(heap); JAU_JAIInitializer JAIInitializer; - JAIInitializer.field_0x0 = 78; - JAIInitializer.field_0x4 = 4; - JAIInitializer.field_0xc = 48; + JAIInitializer.mJaiSePoolSize = 78; + JAIInitializer.mJaiSeqPoolSize = 4; + JAIInitializer.mJaiSoundChildPoolSize = 48; JAIInitializer.initJAInterface(); JAISeMgr* seMgr = mSoundMgr.getSeMgr(); @@ -157,7 +157,7 @@ void Z2AudioMgr::init(JKRSolidHeap* heap, u32 memSize, void* baaData, JKRArchive } void Z2AudioMgr::setOutputMode(u32 mode) { - if (mode <= 2) { + if (mode <= JAS_OUTPUT_SURROUND) { JAISetOutputMode(mode); } } diff --git a/src/d/actor/d_a_movie_player.cpp b/src/d/actor/d_a_movie_player.cpp index c2d1c394c0..0f9f68fb50 100644 --- a/src/d/actor/d_a_movie_player.cpp +++ b/src/d/actor/d_a_movie_player.cpp @@ -3268,7 +3268,7 @@ static void daMP_MixAudio(s16* destination, s16*, u32 sample) { if (r_mix > 32767) r_mix = 32767; - if (JASDriver::getOutputMode() == 0) { + if (JASDriver::getOutputMode() == JAS_OUTPUT_MONO) { l_mix = r_mix = ((r_mix >> 1) + (l_mix >> 1)); r_mix = (s16)r_mix; l_mix = (s16)l_mix; diff --git a/src/d/d_menu_option.cpp b/src/d/d_menu_option.cpp index 5e00afe208..abe9f864ce 100644 --- a/src/d/d_menu_option.cpp +++ b/src/d/d_menu_option.cpp @@ -24,6 +24,8 @@ #include "m_Do/m_Do_graphic.h" #include +#include "JSystem/JAudio2/JASDriverIF.h" + typedef void (dMenu_Option_c::*initFunc)(); static initFunc init[] = { &dMenu_Option_c::atten_init, @@ -89,7 +91,7 @@ dMenu_Option_c::dMenu_Option_c(JKRArchive* i_archive, STControl* i_stick) { dMenu_Option_c::~dMenu_Option_c() {} -static const u32 dMo_soundMode[3] = {0, 1, 2}; +static const u32 dMo_soundMode[3] = {JAS_OUTPUT_MONO, JAS_OUTPUT_STEREO, JAS_OUTPUT_SURROUND}; void dMenu_Option_c::_create() { static const u64 text_a_tag[5] = {MULTI_CHAR('atext1_1'), MULTI_CHAR('atext1_2'), MULTI_CHAR('atext1_3'), MULTI_CHAR('atext1_4'), MULTI_CHAR('atext1_5')}; @@ -1755,13 +1757,13 @@ void dMenu_Option_c::screenSet() { } } -void dMenu_Option_c::setSoundMode(u32 param_0) { - switch (param_0) { - case 0: +void dMenu_Option_c::setSoundMode(u32 soundMode) { + switch (soundMode) { + case JAS_OUTPUT_MONO: OSSetSoundMode(OS_SOUND_MODE_MONO); break; - case 1: - case 2: + case JAS_OUTPUT_STEREO: + case JAS_OUTPUT_SURROUND: // Via dolby pro logic 2, so it's over 2 output channels. OSSetSoundMode(OS_SOUND_MODE_STEREO); break; } From 11d90302f12eca1439c852d639fb3d6183d3e954 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Thu, 12 Mar 2026 07:04:51 +0100 Subject: [PATCH 02/94] Fix build Heck --- libs/JSystem/include/JSystem/JAudio2/JASChannel.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/JSystem/include/JSystem/JAudio2/JASChannel.h b/libs/JSystem/include/JSystem/JAudio2/JASChannel.h index 53bf064d16..89bc381575 100644 --- a/libs/JSystem/include/JSystem/JAudio2/JASChannel.h +++ b/libs/JSystem/include/JSystem/JAudio2/JASChannel.h @@ -6,6 +6,7 @@ #include "JSystem/JAudio2/JASOscillator.h" #include "JSystem/JAudio2/JASSoundParams.h" #include "JSystem/JAudio2/JASWaveInfo.h" +#include "JSystem/JAudio2/JASDSPInterface.h" #include struct JASDSPChannel; From 29dc57f91bb42475f8b4f6b76013fa985ca7f9bb Mon Sep 17 00:00:00 2001 From: madeline Date: Tue, 10 Mar 2026 16:12:10 -0700 Subject: [PATCH 03/94] stub dsp send commands for now --- libs/JSystem/src/JAudio2/dsptask.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libs/JSystem/src/JAudio2/dsptask.cpp b/libs/JSystem/src/JAudio2/dsptask.cpp index 881c75252a..de54bcc7f5 100644 --- a/libs/JSystem/src/JAudio2/dsptask.cpp +++ b/libs/JSystem/src/JAudio2/dsptask.cpp @@ -4,6 +4,7 @@ #include "JSystem/JAudio2/osdsp.h" #include "global.h" #include "os_report.h" +#include "dusk/logging.h" static void DspInitWork(); static void DspHandShake(void* param_0); @@ -549,6 +550,9 @@ void DspBoot(void (*requestCallback)(void*)) { } int DSPSendCommands2(u32* param_1, u32 param_2, void (*callBack)(u16)) { + callBack(param_1[0]); + STUB_RET(1); + s32 i; BOOL interruptFlag; s32 startWorkStatus; From 94aca25337406c3076446951c79918dc98d3328a Mon Sep 17 00:00:00 2001 From: madeline Date: Tue, 10 Mar 2026 16:12:20 -0700 Subject: [PATCH 04/94] enable audio --- include/dusk/audio.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/dusk/audio.h b/include/dusk/audio.h index 46a2dd8859..e3dfda9f52 100644 --- a/include/dusk/audio.h +++ b/include/dusk/audio.h @@ -2,13 +2,13 @@ #define DUSK_AUDIO_H #if TARGET_PC -#define DUSK_AUDIO_DISABLED 1 +#define DUSK_AUDIO_DISABLED 0 #else #define DUSK_AUDIO_DISABLED 0 #endif #if TARGET_PC -#define DUSK_AUDIO_SKIP(...) return __VA_ARGS__; +#define DUSK_AUDIO_SKIP(...) #else #define DUSK_AUDIO_SKIP(...) #endif From 24e650d7baa7639c4d83a738071726bbfc9a868f Mon Sep 17 00:00:00 2001 From: madeline Date: Tue, 10 Mar 2026 16:12:38 -0700 Subject: [PATCH 05/94] fix truncated pointer --- src/m_Do/m_Do_dvd_thread.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/m_Do/m_Do_dvd_thread.cpp b/src/m_Do/m_Do_dvd_thread.cpp index 526b5a255e..6ea1cf99f7 100644 --- a/src/m_Do/m_Do_dvd_thread.cpp +++ b/src/m_Do/m_Do_dvd_thread.cpp @@ -159,7 +159,11 @@ void mDoDvdThd_param_c::mainLoop() { while (command = this->getFirstCommand()) { this->cut(command); if (mDoDvdThd::SyncWidthSound) { + #if TARGET_PC + JASDvd::getThreadPointer()->sendCmdMsg(cb, &command, sizeof(void*)); + #else JASDvd::getThreadPointer()->sendCmdMsg(cb, &command, 4); + #endif } else { cb(&command); } From 213d193b9d023285078b1c177c6c397b55fe866f Mon Sep 17 00:00:00 2001 From: madeline Date: Tue, 10 Mar 2026 20:10:45 -0700 Subject: [PATCH 06/94] fix createBasicBank (for now) --- libs/JSystem/src/JAudio2/JASBNKParser.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libs/JSystem/src/JAudio2/JASBNKParser.cpp b/libs/JSystem/src/JAudio2/JASBNKParser.cpp index 5b46009381..ddab6d2e38 100644 --- a/libs/JSystem/src/JAudio2/JASBNKParser.cpp +++ b/libs/JSystem/src/JAudio2/JASBNKParser.cpp @@ -128,8 +128,10 @@ JASBasicBank* JASBNKParser::Ver1::createBasicBank(void const* stream, JKRHeap* h case 'Perc': { JASDrumSet* drump = JKR_NEW_ARGS (heap, 0) JASDrumSet; JUT_ASSERT(264, drump != NULL); + #if PLATFORM_SHIELD u32 pmap_count = data[1]; JUT_ASSERT(268, pmap_count <= 128); + #endif u32 count = *data++; drump->newPercArray(count, heap); for (int j = 0; j < count; j++) { @@ -137,8 +139,10 @@ JASBasicBank* JASBNKParser::Ver1::createBasicBank(void const* stream, JKRHeap* h if (offset != 0) { JASDrumSet::TPerc* percp = JKR_NEW_ARGS (heap, 0) JASDrumSet::TPerc; JUT_ASSERT(277, percp); + #if PLATFORM_SHIELD u32 type = data[0]; JUT_ASSERT(282, type == 'Pmap'); + #endif BE(u32)* ptr = (BE(u32)*)((intptr_t)stream + offset); TPercData* perc_data = (TPercData*)(ptr + 1); percp->setVolume(perc_data->mVolume); From a0cf0a3c8cc3f17578d9b998ee1b528bc3df4f35 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Thu, 12 Mar 2026 08:26:35 +0100 Subject: [PATCH 07/94] Fix JASTaskThread::allocCallStack on 64-bit --- libs/JSystem/src/JAudio2/JASTaskThread.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/JSystem/src/JAudio2/JASTaskThread.cpp b/libs/JSystem/src/JAudio2/JASTaskThread.cpp index 30a6d07a98..1d031671b4 100644 --- a/libs/JSystem/src/JAudio2/JASTaskThread.cpp +++ b/libs/JSystem/src/JAudio2/JASTaskThread.cpp @@ -26,7 +26,7 @@ JASTaskThread::~JASTaskThread() { void* JASTaskThread::allocCallStack(JASThreadCallback callback, const void* msg, u32 msgSize) { ThreadMemPool* heap; - u32 size = msgSize + 8; + u32 size = msgSize + offsetof(JASThreadCallStack, msg); JASThreadCallStack *callStack = (JASThreadCallStack*) JASKernel::getCommandHeap()->alloc(size); if (callStack == NULL) { return NULL; From fa139ecf5c5f37b7c2bc6c546bcb349bbf4e2e58 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Thu, 12 Mar 2026 11:10:21 +0100 Subject: [PATCH 08/94] Implement JASCriticalSection with a regular mutex --- .../include/JSystem/JAudio2/JASCriticalSection.h | 6 ++++++ src/dusk/audio/JASCriticalSection.cpp | 13 +++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 src/dusk/audio/JASCriticalSection.cpp diff --git a/libs/JSystem/include/JSystem/JAudio2/JASCriticalSection.h b/libs/JSystem/include/JSystem/JAudio2/JASCriticalSection.h index 53faae80fe..ed4cb023ed 100644 --- a/libs/JSystem/include/JSystem/JAudio2/JASCriticalSection.h +++ b/libs/JSystem/include/JSystem/JAudio2/JASCriticalSection.h @@ -8,12 +8,18 @@ * */ class JASCriticalSection { +public: +#if TARGET_PC + JASCriticalSection(); + ~JASCriticalSection(); +#else public: JASCriticalSection() { mInterruptState = OSDisableInterrupts(); }; ~JASCriticalSection() { OSRestoreInterrupts(mInterruptState); }; private: u32 mInterruptState; +#endif }; #endif /* JASCRITICALSECTION_H */ diff --git a/src/dusk/audio/JASCriticalSection.cpp b/src/dusk/audio/JASCriticalSection.cpp new file mode 100644 index 0000000000..9b315b9d0b --- /dev/null +++ b/src/dusk/audio/JASCriticalSection.cpp @@ -0,0 +1,13 @@ +#include "JSystem/JAudio2/JASCriticalSection.h" + +#include + +static std::recursive_mutex gAudioThreadMutex; + +JASCriticalSection::JASCriticalSection() { + gAudioThreadMutex.lock(); +} + +JASCriticalSection::~JASCriticalSection() { + gAudioThreadMutex.unlock(); +} From 5f2f9d01c5d9ffa6bc8076bd839fadb40f3d8848 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Thu, 12 Mar 2026 11:11:09 +0100 Subject: [PATCH 09/94] Make stub log thread safe --- src/dusk/imgui/ImGuiStubLog.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/dusk/imgui/ImGuiStubLog.cpp b/src/dusk/imgui/ImGuiStubLog.cpp index fc9780800a..92bdf7fb9a 100644 --- a/src/dusk/imgui/ImGuiStubLog.cpp +++ b/src/dusk/imgui/ImGuiStubLog.cpp @@ -1,4 +1,5 @@ #include +#include #include "dusk/logging.h" #include "imgui.h" @@ -9,6 +10,7 @@ namespace dusk { static ImGuiTextBuffer StubLogBuffer; static std::vector LineOffsets; static bool StubLogPaused; + static std::mutex StubLogMutex; const char* LogLevelName(const AuroraLogLevel level) { switch (level) { @@ -32,6 +34,8 @@ namespace dusk { return; } + std::lock_guard lock(StubLogMutex); + LineOffsets.push_back(StubLogBuffer.size()); const auto levelName = LogLevelName(level); StubLogBuffer.appendf("[%s | %s] %s\n", levelName, module, message); @@ -40,6 +44,8 @@ namespace dusk { static void ClearPastFrame(); void ImGuiMenuTools::ShowStubLog() { + std::lock_guard lock(StubLogMutex); + if (!ImGuiConsole::CheckMenuViewToggle(ImGuiKey_F5, m_showStubLog)) { ClearPastFrame(); return; From a19e8eb0c27ea41b8c82810198b28b4fec53e43d Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Thu, 12 Mar 2026 11:12:46 +0100 Subject: [PATCH 10/94] Fix warning in stub_log.cpp --- src/dusk/imgui/ImGuiStubLog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dusk/imgui/ImGuiStubLog.cpp b/src/dusk/imgui/ImGuiStubLog.cpp index 92bdf7fb9a..595ae655f6 100644 --- a/src/dusk/imgui/ImGuiStubLog.cpp +++ b/src/dusk/imgui/ImGuiStubLog.cpp @@ -62,7 +62,7 @@ namespace dusk { if (ImGui::BeginChild("scrolling")) { ImGuiListClipper clipper; - clipper.Begin(LineOffsets.size()); + clipper.Begin(static_cast(LineOffsets.size())); while (clipper.Step()) { for (int idx = clipper.DisplayStart; idx < clipper.DisplayEnd; idx++) { const char* lineStart = StubLogBuffer.begin() + LineOffsets[idx]; From 5235bb03fe40f5a033d8599d8ecedc64e1b78139 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Thu, 12 Mar 2026 11:46:09 +0100 Subject: [PATCH 11/94] Fix JAISoundID endianness issues --- libs/JSystem/include/JSystem/JAudio2/JAISound.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libs/JSystem/include/JSystem/JAudio2/JAISound.h b/libs/JSystem/include/JSystem/JAudio2/JAISound.h index 5e1cd40b4b..155eca1e65 100644 --- a/libs/JSystem/include/JSystem/JAudio2/JAISound.h +++ b/libs/JSystem/include/JSystem/JAudio2/JAISound.h @@ -5,6 +5,7 @@ #include "JSystem/JAudio2/JAIAudible.h" #include "JSystem/JUtility/JUTAssert.h" #include "global.h" +#include "dusk/endian.h" #include class JAISound; @@ -55,16 +56,16 @@ public: void setAnonymous() { id_.composite_ = -1; } union { - u32 composite_; + BE(u32) composite_; struct { union { - u16 value; + BE(u16) value; struct { u8 sectionID; u8 groupID; } parts; } type; - u16 waveID; + BE(u16) waveID; } info; } id_; }; From 4382f86c4fafe18e2ce15f7cbadabb5b8149886d Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Thu, 12 Mar 2026 11:46:31 +0100 Subject: [PATCH 12/94] Add SDL-mixer dependency --- CMakeLists.txt | 4 +++- cmake/SDL-mixer.cmake | 28 ++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 cmake/SDL-mixer.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 9395f83759..f4bfc954f7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,7 +62,9 @@ FetchContent_Declare( FetchContent_MakeAvailable(cxxopts) +message(STATUS "Dusk: Fetching SDL3-mixer") +include(cmake/SDL-mixer.cmake) include(files.cmake) # TODO: version handling for res includes @@ -94,7 +96,7 @@ target_include_directories(game_debug PUBLIC extern ${CMAKE_SOURCE_DIR}/build/${DUSK_TP_VERSION}/include build/${DUSK_TP_VERSION}/include) -target_link_libraries(game_debug PUBLIC aurora::core aurora::gx aurora::gd aurora::si aurora::vi aurora::pad aurora::mtx aurora::os aurora::dvd aurora::card) +target_link_libraries(game_debug PUBLIC aurora::core aurora::gx aurora::gd aurora::si aurora::vi aurora::pad aurora::mtx aurora::os aurora::dvd aurora::card SDL3_mixer::SDL3_mixer) set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) add_library(game SHARED ${DOLZEL_FILES} ${Z2AUDIOLIB_FILES} ${SSYSTEM_FILES} ${JSYSTEM_FILES} ${REL_FILES} ${DUSK_FILES} ${DOLPHIN_FILES} diff --git a/cmake/SDL-mixer.cmake b/cmake/SDL-mixer.cmake new file mode 100644 index 0000000000..e2106c4eb6 --- /dev/null +++ b/cmake/SDL-mixer.cmake @@ -0,0 +1,28 @@ +set(SDLMIXER_VENDORED ON) +set(SDLMIXER_STRICT ON) +set(BUILD_SHARED_LIBS OFF) + +set(SDLMIXER_AIFF OFF) +set(SDLMIXER_WAVE OFF) +set(SDLMIXER_VOC OFF) +set(SDLMIXER_AU OFF) +set(SDLMIXER_FLAC OFF) +set(SDLMIXER_GME OFF) +set(SDLMIXER_MOD OFF) +set(SDLMIXER_MP3 OFF) +set(SDLMIXER_MIDI OFF) +set(SDLMIXER_OPUS OFF) +set(SDLMIXER_WAVPACK OFF) +set(SDLMIXER_VORBIS_STB OFF) +set(SDLMIXER_VORBIS_VORBISFILE OFF) + +FetchContent_Declare(SDL-mixer + URL https://github.com/libsdl-org/SDL_mixer/archive/refs/tags/release-3.2.0.tar.gz + URL_HASH SHA256=1077fef4a6e9513027da0a08e6f1b8645f8c88c8bef001c17befeb52b129b5dd + DOWNLOAD_EXTRACT_TIMESTAMP TRUE +) + +FetchContent_MakeAvailable(SDL-mixer) +if (NOT TARGET SDL3_mixer::SDL3_mixer) + message(FATAL_ERROR "Failed to make SDL-mixer available") +endif () From 4f09fc65acc6ff72925790100bd24a4ab48b607e Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Thu, 12 Mar 2026 11:47:04 +0100 Subject: [PATCH 13/94] Remove JASAudioThread race condition fixing code I'm not planning on using JASAudioThread anymore --- .../include/JSystem/JAudio2/JASAudioThread.h | 5 ----- libs/JSystem/src/JAudio2/JASAudioThread.cpp | 18 +----------------- src/Z2AudioLib/Z2AudioMgr.cpp | 13 ------------- 3 files changed, 1 insertion(+), 35 deletions(-) diff --git a/libs/JSystem/include/JSystem/JAudio2/JASAudioThread.h b/libs/JSystem/include/JSystem/JAudio2/JASAudioThread.h index 6be7d07000..dd9fed2183 100644 --- a/libs/JSystem/include/JSystem/JAudio2/JASAudioThread.h +++ b/libs/JSystem/include/JSystem/JAudio2/JASAudioThread.h @@ -32,11 +32,6 @@ struct JASAudioThread : public JKRThread, public JASGlobalInstance::newMemPool(0x48); JASDriver::startDMA(); -#if TARGET_PC - OSLockMutex(&sThreadInitCompleteMutex); - sThreadInitComplete = true; - OSUnlockMutex(&sThreadInitCompleteMutex); - OSSignalCond(&sThreadInitCompleteCond); -#endif - OSMessage msg; while (true) { msg = waitMessageBlock(); diff --git a/src/Z2AudioLib/Z2AudioMgr.cpp b/src/Z2AudioLib/Z2AudioMgr.cpp index 218881fbc9..1cfc4b28b1 100644 --- a/src/Z2AudioLib/Z2AudioMgr.cpp +++ b/src/Z2AudioLib/Z2AudioMgr.cpp @@ -106,19 +106,6 @@ void Z2AudioMgr::init(JKRSolidHeap* heap, u32 memSize, void* baaData, JKRArchive JASPoolAllocObject::newMemPool(0x4e); OS_REPORT("[Z2AudioMgr::init]before Create Section: %d\n", heap->getFreeSize()); -#if TARGET_PC - // Fix a race condition with OS threading where JAUNewSectionHeap will use all the remaining - // space in the JASDram heap before JASAudioThread has finished initializing. - - OSLockMutex(&JASAudioThread::sThreadInitCompleteMutex); - while (!JASAudioThread::sThreadInitComplete) { - OSWaitCond( - &JASAudioThread::sThreadInitCompleteCond, - &JASAudioThread::sThreadInitCompleteMutex); - } - OSUnlockMutex(&JASAudioThread::sThreadInitCompleteMutex); -#endif - JAUSectionHeap* sectionHeap = JAUNewSectionHeap(true); sectionHeap->setSeqDataArchive(seqArc); size_t resMaxSize = JASResArcLoader::getResMaxSize(seqArc); From 45e55f1384f4dfc476034d4c2472d6e5cee858ba Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Thu, 12 Mar 2026 11:47:22 +0100 Subject: [PATCH 14/94] Constant for DSP_SUBFRAME_SIZE --- libs/JSystem/include/JSystem/JAudio2/JASDSPInterface.h | 5 +++++ libs/JSystem/src/JAudio2/JASAiCtrl.cpp | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/libs/JSystem/include/JSystem/JAudio2/JASDSPInterface.h b/libs/JSystem/include/JSystem/JAudio2/JASDSPInterface.h index ec2d9f6c89..9bb9eecb16 100644 --- a/libs/JSystem/include/JSystem/JAudio2/JASDSPInterface.h +++ b/libs/JSystem/include/JSystem/JAudio2/JASDSPInterface.h @@ -13,6 +13,11 @@ */ #define DSP_OUTPUT_CHANNELS 6 // Presumed 5.1 surround +/** + * Amount of audio samples rendered by the DSP in a single sub frame. + */ +#define DSP_SUBFRAME_SIZE 0x50 + struct JASWaveInfo; namespace JASDsp { diff --git a/libs/JSystem/src/JAudio2/JASAiCtrl.cpp b/libs/JSystem/src/JAudio2/JASAiCtrl.cpp index 4e1c296388..ec60bd9d89 100644 --- a/libs/JSystem/src/JAudio2/JASAiCtrl.cpp +++ b/libs/JSystem/src/JAudio2/JASAiCtrl.cpp @@ -271,11 +271,11 @@ u32 JASDriver::getSubFrames() { } u32 JASDriver::getDacSize() { - return sSubFrames * 0x50 * 2; + return sSubFrames * DSP_SUBFRAME_SIZE * 2; } u32 JASDriver::getFrameSamples() { - return sSubFrames * 0x50; + return sSubFrames * DSP_SUBFRAME_SIZE; } void JASDriver::mixMonoTrack(s16* buffer, u32 param_1, MixCallback param_2) { From e07fbef8186dd1924e04e7bc119427a02bfc925c Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Thu, 12 Mar 2026 11:48:02 +0100 Subject: [PATCH 15/94] Block DSP commands, no DSP emulated --- libs/JSystem/src/JAudio2/dspproc.cpp | 4 ++++ libs/JSystem/src/JAudio2/dsptask.cpp | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/libs/JSystem/src/JAudio2/dspproc.cpp b/libs/JSystem/src/JAudio2/dspproc.cpp index 6a40210fd0..891722b7f4 100644 --- a/libs/JSystem/src/JAudio2/dspproc.cpp +++ b/libs/JSystem/src/JAudio2/dspproc.cpp @@ -21,6 +21,10 @@ static void setup_callback(u16 param_0) { } void DsetupTable(u32 channelCount, u32 channelBufferAddress, u32 param_2, u32 param_3, u32 param_4) { +#if TARGET_PC + return; +#endif + u32 table[5]; table[0] = (channelCount & 0xFFFF) | 0x81000000; table[1] = channelBufferAddress; diff --git a/libs/JSystem/src/JAudio2/dsptask.cpp b/libs/JSystem/src/JAudio2/dsptask.cpp index de54bcc7f5..7d2e8dd2ad 100644 --- a/libs/JSystem/src/JAudio2/dsptask.cpp +++ b/libs/JSystem/src/JAudio2/dsptask.cpp @@ -550,9 +550,9 @@ void DspBoot(void (*requestCallback)(void*)) { } int DSPSendCommands2(u32* param_1, u32 param_2, void (*callBack)(u16)) { - callBack(param_1[0]); - STUB_RET(1); - +#if TARGET_PC + OSPanic(__FILE__, __LINE__, "We do not have a DSP"); +#endif s32 i; BOOL interruptFlag; s32 startWorkStatus; From d77bceb821c04463cfa2f59b233f03c0eeb1ebfc Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Thu, 12 Mar 2026 11:48:55 +0100 Subject: [PATCH 16/94] nop JASDsp::releaseHalt --- libs/JSystem/src/JAudio2/JASDSPInterface.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libs/JSystem/src/JAudio2/JASDSPInterface.cpp b/libs/JSystem/src/JAudio2/JASDSPInterface.cpp index e8d7fe050c..a4c91b103c 100644 --- a/libs/JSystem/src/JAudio2/JASDSPInterface.cpp +++ b/libs/JSystem/src/JAudio2/JASDSPInterface.cpp @@ -50,6 +50,10 @@ void JASDsp::boot(void (*param_0)(void*)) { } void JASDsp::releaseHalt(u32 param_0) { +#if TARGET_PC + // Dusk DSP does not work incrementally. + return; +#endif DSPReleaseHalt2(param_0); } From 9dbade65d44c1008814c5e9d19293a5171257d2d Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Thu, 12 Mar 2026 11:49:03 +0100 Subject: [PATCH 17/94] Basic audio system start --- CMakeLists.txt | 6 +- include/dusk/audio/DuskAudioSystem.h | 3 + libs/JSystem/src/JAudio2/JAUInitializer.cpp | 6 ++ src/dusk/audio/DuskAudioSystem.cpp | 82 +++++++++++++++++++++ src/dusk/audio/DuskDsp.cpp | 16 ++++ src/dusk/audio/DuskDsp.hpp | 9 +++ 6 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 include/dusk/audio/DuskAudioSystem.h create mode 100644 src/dusk/audio/DuskAudioSystem.cpp create mode 100644 src/dusk/audio/DuskDsp.cpp create mode 100644 src/dusk/audio/DuskDsp.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index f4bfc954f7..ceebbc1818 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -83,7 +83,11 @@ source_group("dusk" FILES ${DUSK_FILES}) # game_debug is for game code files that we know work when compiled with DEBUG=1 # Of course, if building a release build, this distinction is irrelevant -add_library(game_debug STATIC ${JSYSTEM_DEBUG_FILES} ${SSYSTEM_FILES}) +add_library(game_debug STATIC ${JSYSTEM_DEBUG_FILES} ${SSYSTEM_FILES} + src/dusk/audio/DuskAudioSystem.cpp + src/dusk/audio/JASCriticalSection.cpp + src/dusk/audio/DuskDsp.hpp + src/dusk/audio/DuskDsp.cpp) target_compile_definitions(game_debug PRIVATE TARGET_PC AVOID_UB=1 VERSION=0 $<$:DEBUG=1>) # Make these properties PUBLIC so that the regular game target also sees them. diff --git a/include/dusk/audio/DuskAudioSystem.h b/include/dusk/audio/DuskAudioSystem.h new file mode 100644 index 0000000000..0ea13125ad --- /dev/null +++ b/include/dusk/audio/DuskAudioSystem.h @@ -0,0 +1,3 @@ +#pragma once + +void DuskAudioInitialize(); diff --git a/libs/JSystem/src/JAudio2/JAUInitializer.cpp b/libs/JSystem/src/JAudio2/JAUInitializer.cpp index 9a9a3eeabd..7d56282b45 100644 --- a/libs/JSystem/src/JAudio2/JAUInitializer.cpp +++ b/libs/JSystem/src/JAudio2/JAUInitializer.cpp @@ -15,6 +15,8 @@ #include "JSystem/JKernel/JKRSolidHeap.h" #include "JSystem/JKernel/JKRThread.h" +#include "dusk/audio/DuskAudioSystem.h" + JAU_JASInitializer::JAU_JASInitializer() { audioMemory_ = 0; audioMemSize_ = 0; @@ -60,7 +62,11 @@ void JAU_JASInitializer::initJASystem(JKRSolidHeap* heap) { } JASDvd::createThread(dvdThreadPriority_, 0x80, 0x1000); +#if TARGET_PC + DuskAudioInitialize(); +#else JASAudioThread::create(audioThreadPriority_); +#endif JKRThreadSwitch* threadSwitch = JKRThreadSwitch::getManager(); if (threadSwitch) { if (dvdThreadId_ >= 0) { diff --git a/src/dusk/audio/DuskAudioSystem.cpp b/src/dusk/audio/DuskAudioSystem.cpp new file mode 100644 index 0000000000..566d2ab400 --- /dev/null +++ b/src/dusk/audio/DuskAudioSystem.cpp @@ -0,0 +1,82 @@ +#include "dusk/audio/DuskAudioSystem.h" + +#include +#include + +#include "JSystem/JAudio2/JASAiCtrl.h" +#include "JSystem/JAudio2/JASChannel.h" +#include "JSystem/JAudio2/JASCriticalSection.h" +#include "JSystem/JAudio2/JASDSPChannel.h" +#include "JSystem/JAudio2/JASHeapCtrl.h" + +#include "DuskDsp.hpp" + +static DspSubframe AllSubframeBuffers[DSP_OUTPUT_CHANNELS]; + +static SDL_AudioStream* PlaybackStream; + +static void SDLCALL GetNewAudio( + void *userdata, + SDL_AudioStream *stream, + int additional_amount, + int total_amount); + +static int RenderNewAudioFrame(); +static void RenderAudioSubframe(); + +static void InitSDL3Output() { + SDL_Init(SDL_INIT_AUDIO); + + constexpr SDL_AudioSpec spec = { + SDL_AUDIO_S16, + 1, + 32000, + }; + PlaybackStream = SDL_OpenAudioDeviceStream( + SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, + &spec, + &GetNewAudio, + nullptr); +} + +void DuskAudioInitialize() { + InitSDL3Output(); + + JASDsp::initBuffer(); + JASDSPChannel::initAll(); + + JASPoolAllocObject_MultiThreaded::newMemPool(0x48); + + SDL_ResumeAudioStreamDevice(PlaybackStream); +} + +void SDLCALL GetNewAudio( + void*, + SDL_AudioStream*, + int, + int total_amount) { + while (total_amount > 0) { + const int rendered = RenderNewAudioFrame(); + total_amount -= rendered; + } +} + +int RenderNewAudioFrame() { + JASCriticalSection section; + const u32 countSubframes = JASDriver::getSubFrames(); + + for (u32 i = 0; i < countSubframes; i++) { + RenderAudioSubframe(); + } + + return static_cast(countSubframes) * DSP_SUBFRAME_SIZE; +} + +void RenderAudioSubframe() { + DspSubframe& subFrame = AllSubframeBuffers[0]; + + JASDriver::updateDSP(); + DuskDspRender(subFrame); + + SDL_PutAudioStreamData(PlaybackStream, &subFrame, sizeof(subFrame)); +} diff --git a/src/dusk/audio/DuskDsp.cpp b/src/dusk/audio/DuskDsp.cpp new file mode 100644 index 0000000000..63cc5707e3 --- /dev/null +++ b/src/dusk/audio/DuskDsp.cpp @@ -0,0 +1,16 @@ +#include "DuskDsp.hpp" + +#include + +static float SinePos; + +void DuskDspRender(DspSubframe& subframe) { + subframe.fill(0); + + for (auto& elem : subframe) { + elem = static_cast(sinf(SinePos) * std::numeric_limits::max() * 0.2); + SinePos += 0.05f; + } + + auto& channels = *reinterpret_cast*>(JASDsp::CH_BUF); +} diff --git a/src/dusk/audio/DuskDsp.hpp b/src/dusk/audio/DuskDsp.hpp new file mode 100644 index 0000000000..418c285686 --- /dev/null +++ b/src/dusk/audio/DuskDsp.hpp @@ -0,0 +1,9 @@ +#pragma once + +#include "JSystem/JAudio2/JASDSPInterface.h" + +#include + +using DspSubframe = std::array; + +void DuskDspRender(DspSubframe& subframe); From ffd2c474206aac2bf781e47b79e49800eeaddcde Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Thu, 12 Mar 2026 11:56:15 +0100 Subject: [PATCH 18/94] BE JAUSoundTableItem --- libs/JSystem/include/JSystem/JAudio2/JAUSoundTable.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libs/JSystem/include/JSystem/JAudio2/JAUSoundTable.h b/libs/JSystem/include/JSystem/JAudio2/JAUSoundTable.h index 0226a917f7..4bb9985d28 100644 --- a/libs/JSystem/include/JSystem/JAudio2/JAUSoundTable.h +++ b/libs/JSystem/include/JSystem/JAudio2/JAUSoundTable.h @@ -12,9 +12,9 @@ struct JAUSoundTableItem { u8 mPriority; u8 field_0x1; - u16 mResourceId; - u32 field_0x4; - f32 field_0x8; + BE(u16) mResourceId; + BE(u32) field_0x4; + BE(f32) field_0x8; }; /** From 4db5684d3ef131f90431aec70a6725d8202aa041 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Thu, 12 Mar 2026 12:13:14 +0100 Subject: [PATCH 19/94] BE JASAramStream --- .../include/JSystem/JAudio2/JASAramStream.h | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/libs/JSystem/include/JSystem/JAudio2/JASAramStream.h b/libs/JSystem/include/JSystem/JAudio2/JASAramStream.h index 119145de9a..5c718d9263 100644 --- a/libs/JSystem/include/JSystem/JAudio2/JASAramStream.h +++ b/libs/JSystem/include/JSystem/JAudio2/JASAramStream.h @@ -34,28 +34,28 @@ public: }; struct Header { - /* 0x00 */ u32 tag; // 'STRM' + /* 0x00 */ BE(u32) tag; // 'STRM' /* 0x04 */ u8 field_0x4[5]; /* 0x09 */ u8 format; /* 0x0A */ u8 bits; - /* 0x0C */ u16 channels; - /* 0x0E */ u16 loop; - /* 0x10 */ int mSampleRate; - /* 0x14 */ u32 mSampleCount; // unused - /* 0x18 */ int loop_start; - /* 0x1C */ int loop_end; - /* 0x20 */ u32 block_size; + /* 0x0C */ BE(u16) channels; + /* 0x0E */ BE(u16) loop; + /* 0x10 */ BE(int) mSampleRate; + /* 0x14 */ BE(u32) mSampleCount; // unused + /* 0x18 */ BE(int) loop_start; + /* 0x1C */ BE(int) loop_end; + /* 0x20 */ BE(u32) block_size; /* 0x24 */ u8 _unused2[4]; /* 0x28 */ u8 mVolume; /* 0x29 */ u8 _unused3[0x17]; }; // Size: 0x40 struct BlockHeader { - /* 0x00 */ u32 tag; // 'BLCK' - /* 0x04 */ u32 mSize; + /* 0x00 */ BE(u32) tag; // 'BLCK' + /* 0x04 */ BE(u32) mSize; /* 0x08 */ struct { - s16 mpLast; - s16 mpPenult; + BE(s16) mpLast; + BE(s16) mpPenult; } mAdpcmContinuationData[CHANNEL_MAX]; }; // Size: 0x20 From 977cbf1b6325a6bdd9c4b0ff92e828655533bc48 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Thu, 12 Mar 2026 12:24:28 +0100 Subject: [PATCH 20/94] BE JASSeqReader.h --- libs/JSystem/include/JSystem/JAudio2/JASSeqReader.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/JSystem/include/JSystem/JAudio2/JASSeqReader.h b/libs/JSystem/include/JSystem/JAudio2/JASSeqReader.h index 9dce7f56f7..42a34b9dff 100644 --- a/libs/JSystem/include/JSystem/JAudio2/JASSeqReader.h +++ b/libs/JSystem/include/JSystem/JAudio2/JASSeqReader.h @@ -41,7 +41,7 @@ public: #ifdef __MWERKS__ return *((u16*)field_0x04)++; #else - u16* value = (u16*)field_0x04; + BE(u16)* value = (BE(u16)*)field_0x04; field_0x04 += 2; return *value; #endif @@ -51,7 +51,7 @@ public: #ifdef __MWERKS__ return (*((u32*)field_0x04)++) & 0x00ffffff; #else - u32* value = (u32*)field_0x04; + BE(u32)* value = (BE(u32)*)field_0x04; field_0x04 += 4; return (*value) & 0x00ffffff; #endif From 2228241c2bc0a7e4aea7175db14c439fdd045f7c Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Thu, 12 Mar 2026 12:38:30 +0100 Subject: [PATCH 21/94] Fix JASAramStream layout --- libs/JSystem/include/JSystem/JAudio2/JASAramStream.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libs/JSystem/include/JSystem/JAudio2/JASAramStream.h b/libs/JSystem/include/JSystem/JAudio2/JASAramStream.h index 5c718d9263..f095b1b489 100644 --- a/libs/JSystem/include/JSystem/JAudio2/JASAramStream.h +++ b/libs/JSystem/include/JSystem/JAudio2/JASAramStream.h @@ -35,9 +35,15 @@ public: struct Header { /* 0x00 */ BE(u32) tag; // 'STRM' +#if TARGET_PC + /* 0x04 */ BE(u32) soundBlockSize; + /* 0x08 */ BE(u16) format; + /* 0x0A */ BE(u16) bits; +#else /* 0x04 */ u8 field_0x4[5]; /* 0x09 */ u8 format; /* 0x0A */ u8 bits; +#endif /* 0x0C */ BE(u16) channels; /* 0x0E */ BE(u16) loop; /* 0x10 */ BE(int) mSampleRate; From 3c83c988870eeea95bbc329ddebe322de98644f9 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Thu, 12 Mar 2026 12:46:40 +0100 Subject: [PATCH 22/94] Fix BE in JASAramStream::load --- libs/JSystem/src/JAudio2/JASAramStream.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/JSystem/src/JAudio2/JASAramStream.cpp b/libs/JSystem/src/JAudio2/JASAramStream.cpp index 159df92739..6cbc523c60 100644 --- a/libs/JSystem/src/JAudio2/JASAramStream.cpp +++ b/libs/JSystem/src/JAudio2/JASAramStream.cpp @@ -324,7 +324,7 @@ bool JASAramStream::load() { (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 + *(u32*)&bhead->mSize * i + sizeof(BlockHeader), + if (!JKRMainRamToAram(sReadBuffer + bhead->mSize * i + sizeof(BlockHeader), sp08 + sBlockSize * mBlocksPerChannel * i, bhead->mSize, EXPAND_SWITCH_UNKNOWN0, 0, NULL, -1, NULL)) { JUT_WARN(522, "%s", "JKRMainRamToAram Failed"); From d48b8196ebc54c2eb5305f61f0b78f6ec66a7aaf Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Thu, 12 Mar 2026 12:46:54 +0100 Subject: [PATCH 23/94] Make JKRAramPiece trap on invalid pointers --- libs/JSystem/src/JKernel/JKRAramPiece.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/libs/JSystem/src/JKernel/JKRAramPiece.cpp b/libs/JSystem/src/JKernel/JKRAramPiece.cpp index f01b530cfe..0946275b83 100644 --- a/libs/JSystem/src/JKernel/JKRAramPiece.cpp +++ b/libs/JSystem/src/JKernel/JKRAramPiece.cpp @@ -27,6 +27,10 @@ JSUList JKRAramPiece::sAramPieceCommandList; OSMutex JKRAramPiece::mMutex; +#if DEBUG && TARGET_PC +volatile u8 forceRead; +#endif + JKRAMCommand* JKRAramPiece::orderAsync(int direction, uintptr_t source, uintptr_t destination, u32 length, JKRAramBlock* block, JKRAMCommand::AsyncCallback callback) { lock(); @@ -45,6 +49,12 @@ JKRAMCommand* JKRAramPiece::orderAsync(int direction, uintptr_t source, uintptr_ JKRAramPiece::prepareCommand(direction, source, destination, length, block, callback); message->setting(1, command); +#if DEBUG && TARGET_PC + if (direction == ARAM_DIR_MRAM_TO_ARAM) { + forceRead = *reinterpret_cast(source); + } +#endif + OSSendMessage(&JKRAram::sMessageQueue, message, OS_MESSAGE_BLOCK); if (command->mCallback != NULL) { sAramPieceCommandList.append(&command->mPieceLink); From 29b316f8aed852d9bce2ea6ea2a1b7032375b026 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Thu, 12 Mar 2026 13:17:20 +0100 Subject: [PATCH 24/94] Fix JASTrackNodeOffset missing offset --- libs/JSystem/include/JSystem/JAudio2/JASTrack.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/JSystem/include/JSystem/JAudio2/JASTrack.h b/libs/JSystem/include/JSystem/JAudio2/JASTrack.h index 796bb6afd7..b54a98aa77 100644 --- a/libs/JSystem/include/JSystem/JAudio2/JASTrack.h +++ b/libs/JSystem/include/JSystem/JAudio2/JASTrack.h @@ -54,7 +54,7 @@ struct JASTrack : public JASPoolAllocObject_MultiThreaded { /* 0x4c */ JASTrack* mTrack; }; - struct TList : JGadget::TLinkList { + struct TList : JGadget::TLinkList { TList() : mCallbackRegistered(false) {} void append(JASTrack*); void seqMain(); From 18a63d06bd6290635a084641bf71bb16dca67019 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Thu, 12 Mar 2026 14:08:58 +0100 Subject: [PATCH 25/94] Fix DVD command sizes --- libs/JSystem/src/JAudio2/JASAramStream.cpp | 6 +++--- libs/JSystem/src/JAudio2/JASWaveArcLoader.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libs/JSystem/src/JAudio2/JASAramStream.cpp b/libs/JSystem/src/JAudio2/JASAramStream.cpp index 6cbc523c60..544a26ac99 100644 --- a/libs/JSystem/src/JAudio2/JASAramStream.cpp +++ b/libs/JSystem/src/JAudio2/JASAramStream.cpp @@ -126,7 +126,7 @@ bool JASAramStream::prepare(s32 param_0, int param_1) { data.stream = this; data.field_0x4 = field_0x14c; data.field_0x8 = param_1; - if (!sLoadThread->sendCmdMsg(headerLoadTask, &data, 0xc)) { + if (!sLoadThread->sendCmdMsg(headerLoadTask, &data, sizeof(data))) { JUT_WARN(254, "%s", "sendCmdMsg headerLoadTask Failed"); JASDriver::rejectCallback(channelProcCallback, this); return false; @@ -194,7 +194,7 @@ void JASAramStream::firstLoadTask(void* i_data) { } if (data->field_0x4 != 0) { data->field_0x4--; - if (!sLoadThread->sendCmdMsg(firstLoadTask, data, 0xc)) { + if (!sLoadThread->sendCmdMsg(firstLoadTask, data, sizeof(*data))) { JUT_WARN(372, "%s", "sendCmdMsg firstLoadTask Failed"); struct_80451261 = true; } @@ -277,7 +277,7 @@ bool JASAramStream::headerLoad(u32 param_0, int param_1) { data.stream = this; data.field_0x4 = field_0x108 - 1; data.field_0x8 = param_1; - if (!sLoadThread->sendCmdMsg(firstLoadTask, &data, 0xc)) { + if (!sLoadThread->sendCmdMsg(firstLoadTask, &data, sizeof(data))) { JUT_WARN(472, "%s", "sendCmdMsg firstLoadTask Failed"); struct_80451261 = true; return false; diff --git a/libs/JSystem/src/JAudio2/JASWaveArcLoader.cpp b/libs/JSystem/src/JAudio2/JASWaveArcLoader.cpp index 54018a9e25..d14ff70298 100644 --- a/libs/JSystem/src/JAudio2/JASWaveArcLoader.cpp +++ b/libs/JSystem/src/JAudio2/JASWaveArcLoader.cpp @@ -99,7 +99,7 @@ bool JASWaveArc::sendLoadCmd() { _5a++; - if (JASDvd::getThreadPointer()->sendCmdMsg(loadToAramCallback, &commandInfo, 0x10) == 0) { + if (JASDvd::getThreadPointer()->sendCmdMsg(loadToAramCallback, &commandInfo, sizeof(commandInfo)) == 0) { JUT_WARN(193, "%s", "sendCmdMsg loadToAramCallback Failed"); mHeap.free(); return false; From 66e270929030859e9e783e0a4eb35f59677cc845 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Thu, 12 Mar 2026 14:09:39 +0100 Subject: [PATCH 26/94] Some more names --- .../include/JSystem/JAudio2/JASAramStream.h | 4 +- .../JSystem/JAudio2/JAUStreamAramMgr.h | 4 +- libs/JSystem/src/JAudio2/JAIStream.cpp | 6 +- libs/JSystem/src/JAudio2/JASAramStream.cpp | 72 +++++++++---------- 4 files changed, 43 insertions(+), 43 deletions(-) diff --git a/libs/JSystem/include/JSystem/JAudio2/JASAramStream.h b/libs/JSystem/include/JSystem/JAudio2/JASAramStream.h index f095b1b489..dc9dea9645 100644 --- a/libs/JSystem/include/JSystem/JAudio2/JASAramStream.h +++ b/libs/JSystem/include/JSystem/JAudio2/JASAramStream.h @@ -166,13 +166,13 @@ public: /* 0x130 */ s16 mpLasts[CHANNEL_MAX]; /* 0x13C */ s16 mpPenults[CHANNEL_MAX]; /* 0x148 */ int mAramAddress; - /* 0x14C */ u32 field_0x14c; + /* 0x14C */ u32 mAramSize; /* 0x150 */ StreamCallback mCallback; /* 0x154 */ void* mCallbackData; /* 0x158 */ u16 mFormat; /* 0x15A */ u16 mChannelNum; /* 0x15C */ u32 mBufCount; - /* 0x160 */ u32 mBlocksPerChannel; + /* 0x160 */ u32 mAramBlocksPerChannel; /* 0x164 */ u32 mSampleRate; /* 0x168 */ bool mLoop; /* 0x16C */ u32 mLoopStart; diff --git a/libs/JSystem/include/JSystem/JAudio2/JAUStreamAramMgr.h b/libs/JSystem/include/JSystem/JAudio2/JAUStreamAramMgr.h index bf861b495d..f1cfe8f7f6 100644 --- a/libs/JSystem/include/JSystem/JAudio2/JAUStreamAramMgr.h +++ b/libs/JSystem/include/JSystem/JAudio2/JAUStreamAramMgr.h @@ -48,13 +48,13 @@ template class JAUStreamStaticAramMgr_ : public JAUStreamAramMgrBase_ { public: JAUStreamStaticAramMgr_() { field_0x4c = 0; } - virtual void* newStreamAram(u32* param_0) { + virtual void* newStreamAram(u32* size) { for (u32 i = 0; i < field_0x4c; i++) { if (this->field_0x4.test(i)) { continue; } this->field_0x4.set(i, true); - *param_0 = this->mHeaps[i].getSize(); + *size = this->mHeaps[i].getSize(); return this->mHeaps[i].getBase(); } return NULL; diff --git a/libs/JSystem/src/JAudio2/JAIStream.cpp b/libs/JSystem/src/JAudio2/JAIStream.cpp index 72f37ec248..5799080b12 100644 --- a/libs/JSystem/src/JAudio2/JAIStream.cpp +++ b/libs/JSystem/src/JAudio2/JAIStream.cpp @@ -48,7 +48,7 @@ void JAIStream::JAIStreamMgr_startID_(JAISoundID id, s32 streamFileEntry, } bool JAIStream::prepare_prepareStream_() { - u32 local_28; + u32 size; JAIStreamAramMgr* streamAramMgr; switch (field_0x290) { @@ -56,9 +56,9 @@ bool JAIStream::prepare_prepareStream_() { streamAramMgr = streamMgr_->getStreamAramMgr(); JUT_ASSERT(100, streamAramMgr); - streamAramAddr_ = streamAramMgr->newStreamAram(&local_28); + streamAramAddr_ = streamAramMgr->newStreamAram(&size); if (streamAramAddr_ != NULL) { - inner_.aramStream_.init((uintptr_t)streamAramAddr_, local_28, &JAIStream_JASAramStreamCallback_, this); + inner_.aramStream_.init((uintptr_t)streamAramAddr_, size, &JAIStream_JASAramStreamCallback_, this); field_0x290 = 1; prepareCount_ = 0; } else { diff --git a/libs/JSystem/src/JAudio2/JASAramStream.cpp b/libs/JSystem/src/JAudio2/JASAramStream.cpp index 544a26ac99..77de8e17f7 100644 --- a/libs/JSystem/src/JAudio2/JASAramStream.cpp +++ b/libs/JSystem/src/JAudio2/JASAramStream.cpp @@ -19,8 +19,8 @@ u32 JASAramStream::sBlockSize; u32 JASAramStream::sChannelMax; -bool struct_80451260; -bool struct_80451261; +bool dvdHasErrored; +bool hasErrored; void JASAramStream::initSystem(u32 block_size, u32 channel_max) { JUT_ASSERT(66, block_size % 32 == 0); @@ -37,8 +37,8 @@ void JASAramStream::initSystem(u32 block_size, u32 channel_max) { JUT_ASSERT(79, sReadBuffer); sBlockSize = block_size; sChannelMax = channel_max; - struct_80451260 = false; - struct_80451261 = false; + dvdHasErrored = false; + hasErrored = false; } } @@ -61,13 +61,13 @@ JASAramStream::JASAramStream() { field_0x118 = 0; field_0x12c = 0; mAramAddress = 0; - field_0x14c = 0; + mAramSize = 0; mCallback = NULL; mCallbackData = NULL; mFormat = 0; mChannelNum = 0; mBufCount = 0; - mBlocksPerChannel = 0; + mAramBlocksPerChannel = 0; mSampleRate = 0; mLoop = false; mLoopStart = 0; @@ -88,10 +88,10 @@ JASAramStream::JASAramStream() { } } -void JASAramStream::init(u32 aramAddress, u32 param_1, StreamCallback i_callback, void* i_callbackData) { +void JASAramStream::init(u32 aramAddress, u32 aramSize, StreamCallback i_callback, void* i_callbackData) { JUT_ASSERT(153, sReadBuffer != NULL); mAramAddress = aramAddress; - field_0x14c = param_1; + mAramSize = aramSize; field_0x0c8 = 0.0f; field_0x0ae = 0; field_0x0ac = false; @@ -124,7 +124,7 @@ bool JASAramStream::prepare(s32 param_0, int param_1) { } TaskData data; data.stream = this; - data.field_0x4 = field_0x14c; + data.field_0x4 = mAramSize; data.field_0x8 = param_1; if (!sLoadThread->sendCmdMsg(headerLoadTask, &data, sizeof(data))) { JUT_WARN(254, "%s", "sendCmdMsg headerLoadTask Failed"); @@ -188,7 +188,7 @@ void JASAramStream::firstLoadTask(void* i_data) { if (data->field_0x8 == 0) { if (!sLoadThread->sendCmdMsg(prepareFinishTask, _this)) { JUT_WARN(364, "%s", "sendCmdMsg prepareFinishTask Failed"); - struct_80451261 = true; + hasErrored = true; } } } @@ -196,7 +196,7 @@ void JASAramStream::firstLoadTask(void* i_data) { data->field_0x4--; if (!sLoadThread->sendCmdMsg(firstLoadTask, data, sizeof(*data))) { JUT_WARN(372, "%s", "sendCmdMsg firstLoadTask Failed"); - struct_80451261 = true; + hasErrored = true; } JASCriticalSection cs; _this->field_0x118++; @@ -227,8 +227,8 @@ void JASAramStream::prepareFinishTask(void* i_this) { } } -bool JASAramStream::headerLoad(u32 param_0, int param_1) { - if (struct_80451261) { +bool JASAramStream::headerLoad(u32 aramSize, int param_1) { + if (hasErrored) { return false; } if (field_0x114 != 0) { @@ -236,7 +236,7 @@ bool JASAramStream::headerLoad(u32 param_0, int param_1) { } if (DVDReadPrio(&mDvdFileInfo, sReadBuffer, sizeof(Header), 0, 1) < 0) { JUT_WARN(420, "%s", "DVDReadPrio Failed"); - struct_80451261 = true; + hasErrored = true; return false; } Header* header = (Header*)sReadBuffer; @@ -255,8 +255,8 @@ bool JASAramStream::headerLoad(u32 param_0, int param_1) { field_0x118 = 0; mBlock = 0; field_0x10c = 0; - mBlocksPerChannel = (param_0 / sBlockSize) / header->channels; - mBufCount = mBlocksPerChannel; + mAramBlocksPerChannel = (aramSize / sBlockSize) / header->channels; + mBufCount = mAramBlocksPerChannel; JUT_ASSERT(445, mBufCount > 0); mBufCount--; if (mBufCount < 3) { @@ -279,7 +279,7 @@ bool JASAramStream::headerLoad(u32 param_0, int param_1) { data.field_0x8 = param_1; if (!sLoadThread->sendCmdMsg(firstLoadTask, &data, sizeof(data))) { JUT_WARN(472, "%s", "sendCmdMsg firstLoadTask Failed"); - struct_80451261 = true; + hasErrored = true; return false; } JASCriticalSection cs; @@ -293,7 +293,7 @@ bool JASAramStream::load() { JASCriticalSection cs; field_0x118--; } - if (struct_80451261) { + if (hasErrored) { return false; } if (field_0x114 != 0) { @@ -311,7 +311,7 @@ bool JASAramStream::load() { } if (DVDReadPrio(&mDvdFileInfo, sReadBuffer, size, offset, 1) < 0) { JUT_WARN(507, "%s", "DVDReadPrio Failed"); - struct_80451261 = true; + hasErrored = true; return false; } BlockHeader* bhead = (BlockHeader*)sReadBuffer; @@ -325,10 +325,10 @@ bool JASAramStream::load() { // 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 * mBlocksPerChannel * i, + sp08 + sBlockSize * mAramBlocksPerChannel * i, bhead->mSize, EXPAND_SWITCH_UNKNOWN0, 0, NULL, -1, NULL)) { JUT_WARN(522, "%s", "JKRMainRamToAram Failed"); - struct_80451261 = 1; + hasErrored = 1; return false; } } @@ -344,10 +344,10 @@ bool JASAramStream::load() { } } if (r28 == loop_end_block || r28 + 2 == loop_end_block) { - field_0x108 = mBlocksPerChannel; + field_0x108 = mAramBlocksPerChannel; OSSendMessage(&field_0x020, (OSMessage)5, OS_MESSAGE_BLOCK); } else { - field_0x108 = mBlocksPerChannel - 1; + field_0x108 = mAramBlocksPerChannel - 1; } for (int i = 0; i < mChannelNum; i++) { mpLasts[i] = (s16)bhead->mAdpcmContinuationData[i].mpLast; @@ -371,7 +371,7 @@ s32 JASAramStream::dvdErrorCheck(void* param_0) { s32 status = DVDGetDriveStatus(); switch (status) { case DVD_STATE_END: - struct_80451260 = false; + dvdHasErrored = false; break; case DVD_STATE_BUSY: break; @@ -385,7 +385,7 @@ s32 JASAramStream::dvdErrorCheck(void* param_0) { case DVD_STATE_RETRY: case DVD_STATE_FATAL_ERROR: default: - struct_80451260 = true; + dvdHasErrored = true; break; } return 0; @@ -441,7 +441,7 @@ void JASAramStream::updateChannel(u32 i_callbackType, JASChannel* i_channel, } if (field_0x0b8 > mLoopEnd) { JUT_WARN(686, "%s", "mReadSample > mLoopEnd"); - struct_80451261 = true; + hasErrored = true; } f32 fvar1 = field_0x0c4; fvar1 *= mLoopEnd - mLoopStart; @@ -483,7 +483,7 @@ void JASAramStream::updateChannel(u32 i_callbackType, JASChannel* i_channel, while (blockCount != field_0x0b0) { if (!sLoadThread->sendCmdMsg(loadToAramTask, this)) { JUT_WARN(741, "sendCmdMsg Failed %d %d (%d %d)", i_dspChannel->mAramStreamPosition, i_channel->mWaveAramAddress, blockCount, field_0x0b0); - struct_80451261 = true; + hasErrored = true; break; } { @@ -506,11 +506,11 @@ void JASAramStream::updateChannel(u32 i_callbackType, JASChannel* i_channel, i_dspChannel->mEndSample += block_samples; field_0x124 = i_dspChannel->mEndSample; field_0x12c |= 4; - mBufCount = mBlocksPerChannel; + mBufCount = mAramBlocksPerChannel; field_0x0ad = false; } else { - if (mBufCount != mBlocksPerChannel - 1) { - mBufCount = mBlocksPerChannel - 1; + if (mBufCount != mAramBlocksPerChannel - 1) { + mBufCount = mAramBlocksPerChannel - 1; i_dspChannel->mEndSample -= block_samples; field_0x124 = i_dspChannel->mEndSample; field_0x12c |= 4; @@ -523,13 +523,13 @@ void JASAramStream::updateChannel(u32 i_callbackType, JASChannel* i_channel, } } } else { - if (field_0x118 == 0 && !struct_80451260) { + if (field_0x118 == 0 && !dvdHasErrored) { field_0x0ae &= ~2; field_0x0ae &= ~4; } } field_0x0b4 = i_dspChannel->field_0x074 + i_dspChannel->mSamplesPerBlock; - if (field_0x118 >= mBlocksPerChannel - 2) { + if (field_0x118 >= mAramBlocksPerChannel - 2) { JUT_WARN_DEVICE(810, 1, "%s", "buffer under error"); field_0x0ae |= (u8)4; } @@ -571,7 +571,7 @@ void JASAramStream::updateChannel(u32 i_callbackType, JASChannel* i_channel, field_0x114 = 1; if (!sLoadThread->sendCmdMsg(finishTask, this)) { JUT_WARN(854, "%s", "sendCmdMsg finishTask Failed"); - struct_80451261 = true; + hasErrored = true; return; } } @@ -614,10 +614,10 @@ s32 JASAramStream::channelProc() { } } - if (struct_80451261) { + if (hasErrored) { field_0x0ae |= 8; } - if (struct_80451260) { + if (dvdHasErrored) { field_0x0ae |= 2; } @@ -676,7 +676,7 @@ void JASAramStream::channelStart() { jc->setInitPitch(mSampleRate / JASDriver::getDacRate()); jc->setOscInit(0, &OSC_ENV); jc->field_0xdc.mWaveInfo = wave_info; - jc->mWaveAramAddress = mAramAddress + sBlockSize * mBlocksPerChannel * i; + jc->mWaveAramAddress = mAramAddress + sBlockSize * mAramBlocksPerChannel * i; jc->field_0xdc.mChannelType = 0; int ret = jc->playForce(); JUT_ASSERT(977, ret); From 91069ae3b28b3ba908b6c1135c530fc905223bcd Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Thu, 12 Mar 2026 14:46:18 +0100 Subject: [PATCH 27/94] Fix incorrect JASChannel size for JASPoolAllocObject_MultiThreaded Despair --- libs/JSystem/src/JAudio2/JASAudioThread.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libs/JSystem/src/JAudio2/JASAudioThread.cpp b/libs/JSystem/src/JAudio2/JASAudioThread.cpp index c978e62aeb..943a3e5cfe 100644 --- a/libs/JSystem/src/JAudio2/JASAudioThread.cpp +++ b/libs/JSystem/src/JAudio2/JASAudioThread.cpp @@ -55,9 +55,14 @@ private: BOOL mInterrupts; }; +#if !TARGET_PC class JASChannel { u8 filler[0x108]; }; +#else +// You don't want to know how long I spent debugging this. +#include "JSystem/JAudio2/JASChannel.h" +#endif // NONMATCHING location of JASPoolAllocObject_MultiThreaded void* JASAudioThread::run() { From dd9155dc3041b7be116be26023c960bbe73fb601 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Thu, 12 Mar 2026 14:46:44 +0100 Subject: [PATCH 28/94] Make JASMemPool_MultiThreaded use ObjectLevelLockable Shrug I wrote this in despair debugging the previous issue. Can't hurt I guess. --- .../include/JSystem/JAudio2/JASHeapCtrl.h | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/libs/JSystem/include/JSystem/JAudio2/JASHeapCtrl.h b/libs/JSystem/include/JSystem/JAudio2/JASHeapCtrl.h index 70f63bc889..8e1381cda3 100644 --- a/libs/JSystem/include/JSystem/JAudio2/JASHeapCtrl.h +++ b/libs/JSystem/include/JSystem/JAudio2/JASHeapCtrl.h @@ -352,6 +352,28 @@ template JASMemPool JASPoolAllocObject::memPool_; template class JASMemPool_MultiThreaded : public JASGenericMemPool { public: +#if TARGET_PC + OSMutex mutex; + + JASMemPool_MultiThreaded() { + OSInitMutex(&mutex); + } + + void newMemPool(int param_0) { + JASThreadingModel::ObjectLevelLockable::Lock lock(mutex); + JASGenericMemPool::newMemPool(sizeof(T), param_0); + } + + void* alloc(size_t count) { + JASThreadingModel::ObjectLevelLockable::Lock lock(mutex); + return JASGenericMemPool::alloc(count); + } + + void free(void* ptr, u32 param_1) { + JASThreadingModel::ObjectLevelLockable::Lock lock(mutex); + JASGenericMemPool::free(ptr, param_1); + } +#else void newMemPool(int param_0) { typename JASThreadingModel::InterruptsDisable >::Lock lock(*this); JASGenericMemPool::newMemPool(sizeof(T), param_0); @@ -366,6 +388,7 @@ public: typename JASThreadingModel::InterruptsDisable >::Lock lock(*this); JASGenericMemPool::free(ptr, param_1); } +#endif }; /** From 9d1deb2a370f80e261f5e5a04d6a3407dc6d6382 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Sat, 14 Mar 2026 11:21:16 +0100 Subject: [PATCH 29/94] Fix another JASTaskThread::allocCallStack size --- libs/JSystem/src/JAudio2/JASTaskThread.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/JSystem/src/JAudio2/JASTaskThread.cpp b/libs/JSystem/src/JAudio2/JASTaskThread.cpp index 1d031671b4..365424deb4 100644 --- a/libs/JSystem/src/JAudio2/JASTaskThread.cpp +++ b/libs/JSystem/src/JAudio2/JASTaskThread.cpp @@ -40,7 +40,7 @@ void* JASTaskThread::allocCallStack(JASThreadCallback callback, const void* msg, void* JASTaskThread::allocCallStack(JASThreadCallback callback, void* msg) { JASThreadCallStack *callStack; - callStack = (JASThreadCallStack*)JASKernel::getCommandHeap()->alloc(12); + callStack = (JASThreadCallStack*)JASKernel::getCommandHeap()->alloc(offsetof(JASThreadCallStack, msg) + sizeof(void*)); if (callStack == NULL) { return NULL; } From ca769802b00cddbfcb830d9ea269012e4656e407 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Sat, 14 Mar 2026 11:21:23 +0100 Subject: [PATCH 30/94] Add CRASH() macro --- include/global.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/global.h b/include/global.h index 375b92de1a..94a2c9dc6e 100644 --- a/include/global.h +++ b/include/global.h @@ -200,4 +200,6 @@ static const float INF = 2000000000.0f; using std::isnan; #endif +#define CRASH(msg, ...) OSPanic(__FILE__, __LINE__, msg, __VA_ARGS__) + #endif From 9e5202787bd79ba093640351a1dea2403198f483 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Sat, 14 Mar 2026 12:19:40 +0100 Subject: [PATCH 31/94] ADPCM4 decode impl --- CMakeLists.txt | 4 ++- src/dusk/audio/Adpcm.cpp | 60 ++++++++++++++++++++++++++++++++++++++++ src/dusk/audio/Adpcm.hpp | 14 ++++++++++ 3 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 src/dusk/audio/Adpcm.cpp create mode 100644 src/dusk/audio/Adpcm.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ceebbc1818..25b78d60b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -87,7 +87,9 @@ add_library(game_debug STATIC ${JSYSTEM_DEBUG_FILES} ${SSYSTEM_FILES} src/dusk/audio/DuskAudioSystem.cpp src/dusk/audio/JASCriticalSection.cpp src/dusk/audio/DuskDsp.hpp - src/dusk/audio/DuskDsp.cpp) + src/dusk/audio/DuskDsp.cpp + src/dusk/audio/Adpcm.cpp + src/dusk/audio/Adpcm.hpp) target_compile_definitions(game_debug PRIVATE TARGET_PC AVOID_UB=1 VERSION=0 $<$:DEBUG=1>) # Make these properties PUBLIC so that the regular game target also sees them. diff --git a/src/dusk/audio/Adpcm.cpp b/src/dusk/audio/Adpcm.cpp new file mode 100644 index 0000000000..f4318d5d59 --- /dev/null +++ b/src/dusk/audio/Adpcm.cpp @@ -0,0 +1,60 @@ +#include "Adpcm.hpp" + +#include +#include + +#include "JSystem/JAudio2/JASAramStream.h" + +// https://github.com/magcius/vgmtrans/blob/8e34ddc2fb43948dc1e1a8759c739a0c1c7b62d7/src/main/formats/JaiSeqScanner.cpp#L489-L531 +// https://github.com/XAYRGA/JaiSeqX/blob/f29c024ec3663503f506aa02bcd503ada6e7d8aa/JaiSeqXLJA/DSP/JAIDSPADPCM4.cs#L86-L87 + +static constexpr u16 Coefficient0[] = { + 0,0x0800,0,0x0400, + 0x1000,0x0e00,0x0c00,0x1200, + 0x1068,0x12c0,0x1400,0x0800, + 0x0400,0xfc00,0xfc00,0xf800 +}; + +static constexpr u16 Coefficient1[] = { + 0,0,0x0800,0x0400,0xf800, + 0xfa00,0xfc00,0xf600,0xf738, + 0xf704,0xf400,0xf800,0xfc00, + 0x0400,0,0, +}; + +constexpr int AdpcmFrameSize = 9; + +static s16 Clamp16(s32 value) { + if (value > 0x7FFF) return 0x7FFF; + if (value < -0x8000) return -0x8000; + return value; +} + +void dusk::audio::Adpcm4ToPcm16(const u8* adpcm, size_t adpcmLength, s16* pcm, size_t pcmLength, s16& hist2, s16& hist1) { + assert (adpcmLength % AdpcmFrameSize == 0 && "ADPCM must be divisible by frame size"); + + const auto expectedPcmLength = (adpcmLength / AdpcmFrameSize) * 16; + assert (expectedPcmLength <= pcmLength && "PCM output buffer is too small!"); + + for (int i = 0; i < adpcmLength; i += AdpcmFrameSize) { + u8 header = adpcm[i]; + + s32 scale = 1 << (header >> 4); + u8 coefIndex = header & 0xF; + + s16 coef0 = (s16)Coefficient0[coefIndex]; + s16 coef1 = (s16)Coefficient1[coefIndex]; + + for (int sampleIdx = 0; sampleIdx < 16; sampleIdx++) { + u8 adpcmValue = adpcm[i + 1 + sampleIdx / 2]; + u8 unsignedNibble = sampleIdx % 2 == 0 ? adpcmValue >> 4 : adpcmValue & 0xF; + s8 signedNibble = ((s8)(unsignedNibble << 4)) >> 4; + s16 sample = Clamp16((((signedNibble * scale) << 11) + (coef0 * hist1 + coef1 * hist2)) >> 11); + + hist2 = hist1; + hist1 = sample; + + *pcm++ = sample; + } + } +} diff --git a/src/dusk/audio/Adpcm.hpp b/src/dusk/audio/Adpcm.hpp new file mode 100644 index 0000000000..e893d54791 --- /dev/null +++ b/src/dusk/audio/Adpcm.hpp @@ -0,0 +1,14 @@ +#ifndef DUSK_ADPCM_HPP +#define DUSK_ADPCM_HPP + +#include + +namespace dusk::audio { + constexpr u32 Adpcm4FrameSize = 9; + constexpr u32 AdpcmSampleCount = 16; + constexpr u32 Adpcm2FrameSize = 5; + + void Adpcm4ToPcm16(const u8* adpcm, size_t adpcmLength, s16* pcm, size_t pcmLength, s16& hist1, s16& hist0); +} + +#endif // DUSK_ADPCM_HPP From ca467e62ab041b52c089c32fc3382bf26b62ecdd Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Sat, 14 Mar 2026 12:20:02 +0100 Subject: [PATCH 32/94] More documenting for JASAramStream and JASDSPInterface --- .../include/JSystem/JAudio2/JASAramStream.h | 131 +++++-- .../include/JSystem/JAudio2/JASDSPInterface.h | 42 ++- libs/JSystem/src/JAudio2/JASAramStream.cpp | 333 ++++++++++-------- libs/JSystem/src/JAudio2/JASDSPInterface.cpp | 6 +- 4 files changed, 337 insertions(+), 175 deletions(-) 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; } From 7eaabe6ecccda6948fecb3bcac9e452d65ae151b Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Sat, 14 Mar 2026 12:21:26 +0100 Subject: [PATCH 33/94] We have audio!! --- include/dusk/audio/DuskAudioSystem.h | 4 +- libs/JSystem/src/JAudio2/JAUInitializer.cpp | 2 +- src/dusk/audio/DuskAudioSystem.cpp | 27 +++- src/dusk/audio/DuskDsp.cpp | 150 ++++++++++++++++++-- src/dusk/audio/DuskDsp.hpp | 13 +- 5 files changed, 178 insertions(+), 18 deletions(-) diff --git a/include/dusk/audio/DuskAudioSystem.h b/include/dusk/audio/DuskAudioSystem.h index 0ea13125ad..b06bdbe84e 100644 --- a/include/dusk/audio/DuskAudioSystem.h +++ b/include/dusk/audio/DuskAudioSystem.h @@ -1,3 +1,5 @@ #pragma once -void DuskAudioInitialize(); +namespace dusk::audio { + void Initialize(); +} diff --git a/libs/JSystem/src/JAudio2/JAUInitializer.cpp b/libs/JSystem/src/JAudio2/JAUInitializer.cpp index 7d56282b45..0cc3fcbb62 100644 --- a/libs/JSystem/src/JAudio2/JAUInitializer.cpp +++ b/libs/JSystem/src/JAudio2/JAUInitializer.cpp @@ -63,7 +63,7 @@ void JAU_JASInitializer::initJASystem(JKRSolidHeap* heap) { JASDvd::createThread(dvdThreadPriority_, 0x80, 0x1000); #if TARGET_PC - DuskAudioInitialize(); + dusk::audio::Initialize(); #else JASAudioThread::create(audioThreadPriority_); #endif diff --git a/src/dusk/audio/DuskAudioSystem.cpp b/src/dusk/audio/DuskAudioSystem.cpp index 566d2ab400..191df90686 100644 --- a/src/dusk/audio/DuskAudioSystem.cpp +++ b/src/dusk/audio/DuskAudioSystem.cpp @@ -2,6 +2,8 @@ #include #include +#include +#include #include "JSystem/JAudio2/JASAiCtrl.h" #include "JSystem/JAudio2/JASChannel.h" @@ -10,6 +12,9 @@ #include "JSystem/JAudio2/JASHeapCtrl.h" #include "DuskDsp.hpp" +#include "JSystem/JAudio2/JASAudioThread.h" + +using namespace dusk::audio; static DspSubframe AllSubframeBuffers[DSP_OUTPUT_CHANNELS]; @@ -39,7 +44,7 @@ static void InitSDL3Output() { nullptr); } -void DuskAudioInitialize() { +void dusk::audio::Initialize() { InitSDL3Output(); JASDsp::initBuffer(); @@ -53,22 +58,30 @@ void DuskAudioInitialize() { void SDLCALL GetNewAudio( void*, SDL_AudioStream*, - int, - int total_amount) { - while (total_amount > 0) { + int needed, + int) { + while (needed > 0) { const int rendered = RenderNewAudioFrame(); - total_amount -= rendered; + needed -= rendered; } } +static std::ofstream outRaw("guh.raw", std::ios_base::out | std::ios_base::binary); + int RenderNewAudioFrame() { JASCriticalSection section; const u32 countSubframes = JASDriver::getSubFrames(); + JASAudioThread::setDSPSyncCount(countSubframes); + for (u32 i = 0; i < countSubframes; i++) { RenderAudioSubframe(); + + JASAudioThread::snIntCount -= 1; } + outRaw.flush(); + return static_cast(countSubframes) * DSP_SUBFRAME_SIZE; } @@ -76,7 +89,9 @@ void RenderAudioSubframe() { DspSubframe& subFrame = AllSubframeBuffers[0]; JASDriver::updateDSP(); - DuskDspRender(subFrame); + DspRender(subFrame); + + outRaw.write((const char*)subFrame.data(), subFrame.size() * sizeof(s16)); SDL_PutAudioStreamData(PlaybackStream, &subFrame, sizeof(subFrame)); } diff --git a/src/dusk/audio/DuskDsp.cpp b/src/dusk/audio/DuskDsp.cpp index 63cc5707e3..cb2c01d1cb 100644 --- a/src/dusk/audio/DuskDsp.cpp +++ b/src/dusk/audio/DuskDsp.cpp @@ -1,16 +1,150 @@ +#include +#include + #include "DuskDsp.hpp" -#include +#include +#include +#include -static float SinePos; +#include "Adpcm.hpp" +#include "global.h" -void DuskDspRender(DspSubframe& subframe) { - subframe.fill(0); +using namespace dusk::audio; - for (auto& elem : subframe) { - elem = static_cast(sinf(SinePos) * std::numeric_limits::max() * 0.2); - SinePos += 0.05f; +ChannelAuxData dusk::audio::ChannelAux[DSP_CHANNELS] = {}; + +static bool ValidateChannelWaveFormat(const JASDsp::TChannel& channel) { + if (channel.mSamplesPerBlock == AdpcmSampleCount && channel.mBytesPerBlock == Adpcm4FrameSize) + return true; + /* + if (channel.mSamplesPerBlock == AdpcmSampleCount && channel.mBytesPerBlock == Adpcm2FrameSize) + return true; + if (channel.mSamplesPerBlock == 1 && channel.mBytesPerBlock == 8) + return true; + if (channel.mSamplesPerBlock == 1 && channel.mBytesPerBlock == 16) + return true; + */ + return false; +} + +static void ValidateChannel(const JASDsp::TChannel& channel) { + if (!ValidateChannelWaveFormat(channel)) { + CRASH( + "Unable to handle channel format: %02x, %02x\n", + channel.mSamplesPerBlock, + channel.mBytesPerBlock); + } +} + +static u32 ConvertDataLengthToSamples(const JASDsp::TChannel& channel, u32 dataLen) { + if (dataLen % channel.mBytesPerBlock != 0) { + CRASH("Indivisible data length: %d\n", dataLen); } - auto& channels = *reinterpret_cast*>(JASDsp::CH_BUF); + return (dataLen / channel.mBytesPerBlock) * channel.mSamplesPerBlock; +} + +static u32 ConvertSamplesToDataLength(const JASDsp::TChannel& channel, u32 samples) { + if (samples % channel.mSamplesPerBlock != 0) { + CRASH("Indivisible sample count: %d\n", samples); + } + + return (samples / channel.mSamplesPerBlock) * channel.mBytesPerBlock; +} + +static void RenderChannel( + JASDsp::TChannel& channel, + ChannelAuxData& channelAux, + DspSubframe& subframe); + +static void ResetChannel(JASDsp::TChannel& channel) { + channel.mSamplesLeft = channel.mEndSample - channel.mSamplePosition; + + channel.mResetFlag = false; +} + +static void MixSubframe(DspSubframe& dst, const DspSubframe& src) { + for (int i = 0; i < dst.size(); i++) { + dst[i] = static_cast(dst[i] + src[i]); + } +} + +void dusk::audio::DspRender(DspSubframe& subframe) { + subframe.fill(0); + + // This cast half exists because my debugger sucks and this is an easy way to look at the data. + auto& channels = *reinterpret_cast*>(JASDsp::CH_BUF); + + for (int i = 0; i < channels.size(); i++) { + auto& channel = channels[i]; + auto& channelAux = ChannelAux[i]; + + if (!channel.mIsActive) { + continue; + } + + ValidateChannel(channel); + + DspSubframe channelSubframe = {}; + RenderChannel(channel, channelAux, channelSubframe); + MixSubframe(subframe, channelSubframe); + } +} + +static void RenderChannel( + JASDsp::TChannel& channel, + ChannelAuxData& channelAux, + DspSubframe& subframe) { + if (channel.mResetFlag) { + ResetChannel(channel); + } + + auto aramBase = static_cast(ARGetStorageAddress()) + channel.mWaveAramAddress; + + // Streaming logic directly modifies mSamplesLeft. + // So we use that as our tracking of where we are. + auto curSamplePosition = channel.mEndSample - channel.mSamplesLeft; + auto dataPosition = ConvertSamplesToDataLength(channel, curSamplePosition); + + u32 renderSamples = std::min(channel.mSamplesLeft, static_cast(DSP_SUBFRAME_SIZE)); + + Adpcm4ToPcm16( + aramBase + dataPosition, + ConvertSamplesToDataLength(channel, renderSamples), + subframe.data(), + renderSamples, + channelAux.hist1, + channelAux.hist0); + + channel.mSamplesLeft -= renderSamples; + channel.mSamplePosition += renderSamples; + + if (channel.mSamplesLeft == 0) { + // Reached end of buffer. + if (!channel.mLoopFlag) { + // Finish. + channel.mIsFinished = true; + return; + } + + channel.mSamplesLeft = channel.mEndSample - channel.mLoopStartSample; + channel.mSamplePosition = channel.mLoopStartSample; + curSamplePosition = channel.mEndSample - channel.mSamplesLeft; + dataPosition = ConvertSamplesToDataLength(channel, curSamplePosition); + + Adpcm4ToPcm16( + aramBase + dataPosition, + ConvertSamplesToDataLength(channel, DSP_SUBFRAME_SIZE - renderSamples), + subframe.data() + renderSamples, + subframe.size() - renderSamples, + channelAux.hist1, + channelAux.hist0); + + channel.mSamplesLeft -= (DSP_SUBFRAME_SIZE - renderSamples); + channel.mSamplePosition += (DSP_SUBFRAME_SIZE - renderSamples); + } + + channel.mAramStreamPosition = channel.mWaveAramAddress + + ConvertSamplesToDataLength(channel, channel.mSamplePosition); } diff --git a/src/dusk/audio/DuskDsp.hpp b/src/dusk/audio/DuskDsp.hpp index 418c285686..a8013d6669 100644 --- a/src/dusk/audio/DuskDsp.hpp +++ b/src/dusk/audio/DuskDsp.hpp @@ -4,6 +4,15 @@ #include -using DspSubframe = std::array; +namespace dusk::audio { + struct ChannelAuxData { + s16 hist1; + s16 hist0; + }; -void DuskDspRender(DspSubframe& subframe); + extern ChannelAuxData ChannelAux[DSP_CHANNELS]; + + using DspSubframe = std::array; + + void DspRender(DspSubframe& subframe); +} From 82b4f6327c0a80fe745ec9b7c161259c10a77020 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Sat, 14 Mar 2026 14:16:49 +0100 Subject: [PATCH 34/94] Assign names to threads Visible in debuggers etc --- include/dusk/os.h | 14 +++++++++ .../include/JSystem/JKernel/JKRThread.h | 4 +++ libs/JSystem/src/JKernel/JKRThread.cpp | 12 ++++++++ src/dusk/OSThread.cpp | 30 +++++++++++++++++++ src/m_Do/m_Do_DVDError.cpp | 7 +++++ src/m_Do/m_Do_MemCard.cpp | 5 ++++ src/m_Do/m_Do_dvd_thread.cpp | 4 +++ 7 files changed, 76 insertions(+) create mode 100644 include/dusk/os.h diff --git a/include/dusk/os.h b/include/dusk/os.h new file mode 100644 index 0000000000..180bdb9b1a --- /dev/null +++ b/include/dusk/os.h @@ -0,0 +1,14 @@ +#ifndef DUSK_OS_H +#define DUSK_OS_H + +#ifdef __cplusplus +extern "C" { +#endif + +void OSSetCurrentThreadName(const char* name); + +#ifdef __cplusplus +} +#endif + +#endif // DUSK_OS_H \ No newline at end of file diff --git a/libs/JSystem/include/JSystem/JKernel/JKRThread.h b/libs/JSystem/include/JSystem/JKernel/JKRThread.h index b58dc6232d..2830252281 100644 --- a/libs/JSystem/include/JSystem/JKernel/JKRThread.h +++ b/libs/JSystem/include/JSystem/JKernel/JKRThread.h @@ -132,6 +132,10 @@ public: static JSUList sThreadList; // static u8 sThreadList[12]; + +#if TARGET_PC + const char* mThreadName; +#endif }; class JKRIdleThread : public JKRThread { diff --git a/libs/JSystem/src/JKernel/JKRThread.cpp b/libs/JSystem/src/JKernel/JKRThread.cpp index 96dfd53499..6ba97e3d4a 100644 --- a/libs/JSystem/src/JKernel/JKRThread.cpp +++ b/libs/JSystem/src/JKernel/JKRThread.cpp @@ -7,6 +7,10 @@ #include "global.h" #include +#if TARGET_PC +#include "dusk/os.h" +#endif + JSUList JKRThread::sThreadList(0); void* JKRIdleThread::sThread; @@ -88,6 +92,14 @@ void JKRThread::setCommon_heapSpecified(JKRHeap* heap, u32 stack_size, int param } void* JKRThread::start(void* thread) { +#if TARGET_PC + auto& thd = *static_cast(thread); + if (thd.mThreadName == nullptr) { + thd.mThreadName = typeid(thd).name(); + } + OSSetCurrentThreadName(thd.mThreadName); +#endif + return ((JKRThread*)thread)->run(); } diff --git a/src/dusk/OSThread.cpp b/src/dusk/OSThread.cpp index 2d19139272..5301614c5b 100644 --- a/src/dusk/OSThread.cpp +++ b/src/dusk/OSThread.cpp @@ -17,6 +17,12 @@ #include #include "JSystem/JKernel/JKRHeap.h" +#include "dusk/os.h" + +#if _WIN32 +#define WIN32_LEAN_AND_MEAN 1 +#include +#endif // ============================================================================ // Malloc-based allocator to bypass JKRHeap operator new/delete @@ -744,6 +750,30 @@ OSInterruptMask __OSUnmaskInterrupts(OSInterruptMask mask) { return 0; } +void OSSetCurrentThreadName(const char* name) { + // "Why is this current thread only?", you might ask? + // Because macOS requires that. For some reason. + +#if _WIN32 + wchar_t buffer[256]; + const auto converted = MultiByteToWideChar( + CP_UTF8, + 0, + name, + -1, + buffer, + sizeof(buffer)/sizeof(wchar_t)); + if (converted == 0) { + CRASH("OSSetThreadName: MultiByteToWideChar failed"); + } + + const auto result = SetThreadDescription(GetCurrentThread(), buffer); + if (!SUCCEEDED(result)) { + CRASH("OSSetThreadName: SetThreadDescription failed"); + } +#endif +} + #ifdef __cplusplus } #endif diff --git a/src/m_Do/m_Do_DVDError.cpp b/src/m_Do/m_Do_DVDError.cpp index 4984ea22c7..62fd71ed21 100644 --- a/src/m_Do/m_Do_DVDError.cpp +++ b/src/m_Do/m_Do_DVDError.cpp @@ -14,6 +14,8 @@ #include #include +#include "dusk/os.h" + #if PLATFORM_GCN const int stack_size = 3072; #else @@ -65,6 +67,11 @@ static void mDoDvdErr_Watch(void*) { OSDisableInterrupts(); #endif #endif + +#if TARGET_PC + OSSetCurrentThreadName("DVD error thread"); +#endif + JKRThread(OSGetCurrentThread(), 0); JKRSetCurrentHeap(mDoExt_getAssertHeap()); diff --git a/src/m_Do/m_Do_MemCard.cpp b/src/m_Do/m_Do_MemCard.cpp index 919774d2e0..bbb94f2bcd 100644 --- a/src/m_Do/m_Do_MemCard.cpp +++ b/src/m_Do/m_Do_MemCard.cpp @@ -10,6 +10,7 @@ #include "m_Do/m_Do_MemCardRWmng.h" #include "m_Do/m_Do_Reset.h" #include "os_report.h" +#include "dusk/os.h" #if PLATFORM_WII || PLATFORM_SHIELD #include @@ -866,6 +867,10 @@ mDoMemCd_Ctrl_c g_mDoMemCd_control; static int mDoMemCd_main(void*) { JKRThread(OSGetCurrentThread(), 0); +#if TARGET_PC + OSSetCurrentThreadName("MemCardThread"); +#endif + JKRSetCurrentHeap(mDoExt_getAssertHeap()); g_mDoMemCd_control.main(); diff --git a/src/m_Do/m_Do_dvd_thread.cpp b/src/m_Do/m_Do_dvd_thread.cpp index 6ea1cf99f7..c5f982bc9d 100644 --- a/src/m_Do/m_Do_dvd_thread.cpp +++ b/src/m_Do/m_Do_dvd_thread.cpp @@ -10,6 +10,7 @@ #include "JSystem/JKernel/JKRDvdRipper.h" #include "JSystem/JKernel/JKRExpHeap.h" #include "JSystem/JKernel/JKRMemArchive.h" +#include "dusk/os.h" #include "m_Do/m_Do_Reset.h" #include "m_Do/m_Do_controller_pad.h" #include "m_Do/m_Do_ext.h" @@ -17,6 +18,9 @@ s32 mDoDvdThd::main(void* param_0) { JKRThread(OSGetCurrentThread(), 0); +#if TARGET_PC + OSSetCurrentThreadName("DVD thread"); +#endif JKRSetCurrentHeap(mDoExt_getAssertHeap()); mDoDvdThd_param_c* param = static_cast(param_0); param->mainLoop(); From 602f69673b3124f56494df4ff1ec78fb6f1e1801 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Sat, 14 Mar 2026 14:22:33 +0100 Subject: [PATCH 35/94] Don't make the DAC rate weird --- libs/JSystem/src/JAudio2/JASAiCtrl.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libs/JSystem/src/JAudio2/JASAiCtrl.cpp b/libs/JSystem/src/JAudio2/JASAiCtrl.cpp index ec60bd9d89..fd9446932d 100644 --- a/libs/JSystem/src/JAudio2/JASAiCtrl.cpp +++ b/libs/JSystem/src/JAudio2/JASAiCtrl.cpp @@ -98,7 +98,10 @@ void JASDriver::setOutputRate(JASOutputRate param_0) { sSubFrames = 10; sDacRate = 48000.0f; } + +#if !TARGET_PC sDacRate *= 1.0008897f; +#endif } const JASDriver::MixFunc JASDriver::sMixFuncs[4] = { From 5eaec7360b85d2b3f053c2a78f142a9958f489c0 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Sat, 14 Mar 2026 15:50:39 +0100 Subject: [PATCH 36/94] Resample audio with SDL3 --- src/dusk/audio/DuskAudioSystem.cpp | 3 +- src/dusk/audio/DuskDsp.cpp | 90 +++++++++++++++++++++++------- src/dusk/audio/DuskDsp.hpp | 6 ++ 3 files changed, 78 insertions(+), 21 deletions(-) diff --git a/src/dusk/audio/DuskAudioSystem.cpp b/src/dusk/audio/DuskAudioSystem.cpp index 191df90686..9b089eb807 100644 --- a/src/dusk/audio/DuskAudioSystem.cpp +++ b/src/dusk/audio/DuskAudioSystem.cpp @@ -35,7 +35,7 @@ static void InitSDL3Output() { constexpr SDL_AudioSpec spec = { SDL_AUDIO_S16, 1, - 32000, + SampleRate, }; PlaybackStream = SDL_OpenAudioDeviceStream( SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, @@ -46,6 +46,7 @@ static void InitSDL3Output() { void dusk::audio::Initialize() { InitSDL3Output(); + DspInit(); JASDsp::initBuffer(); JASDSPChannel::initAll(); diff --git a/src/dusk/audio/DuskDsp.cpp b/src/dusk/audio/DuskDsp.cpp index cb2c01d1cb..5b4c5d0730 100644 --- a/src/dusk/audio/DuskDsp.cpp +++ b/src/dusk/audio/DuskDsp.cpp @@ -5,7 +5,6 @@ #include #include -#include #include "Adpcm.hpp" #include "global.h" @@ -58,9 +57,18 @@ static void RenderChannel( ChannelAuxData& channelAux, DspSubframe& subframe); -static void ResetChannel(JASDsp::TChannel& channel) { +static void ResetChannel(JASDsp::TChannel& channel, const ChannelAuxData& aux) { channel.mSamplesLeft = channel.mEndSample - channel.mSamplePosition; + const SDL_AudioSpec spec = { + SDL_AUDIO_S16, + 1, + static_cast(static_cast(SampleRate) * channel.mPitch / 4096) + }; + + SDL_ClearAudioStream(aux.resampleStream); + SDL_SetAudioStreamFormat(aux.resampleStream, &spec, nullptr); + channel.mResetFlag = false; } @@ -92,13 +100,21 @@ void dusk::audio::DspRender(DspSubframe& subframe) { } } -static void RenderChannel( - JASDsp::TChannel& channel, - ChannelAuxData& channelAux, - DspSubframe& subframe) { - if (channel.mResetFlag) { - ResetChannel(channel); - } +static void SDLCALL ReadChannelSamples( + void *userdata, + SDL_AudioStream *stream, + int additional_amount, + int) { + + const auto index = static_cast(reinterpret_cast(userdata)); + auto& channel = JASDsp::CH_BUF[index]; + auto& aux = ChannelAux[index]; + + additional_amount = ALIGN_NEXT(additional_amount, channel.mSamplesPerBlock); + + int requestedSize = static_cast(sizeof(s16) * additional_amount); + auto requested = static_cast(alloca(requestedSize)); + memset(requested, 0, requestedSize); auto aramBase = static_cast(ARGetStorageAddress()) + channel.mWaveAramAddress; @@ -107,15 +123,15 @@ static void RenderChannel( auto curSamplePosition = channel.mEndSample - channel.mSamplesLeft; auto dataPosition = ConvertSamplesToDataLength(channel, curSamplePosition); - u32 renderSamples = std::min(channel.mSamplesLeft, static_cast(DSP_SUBFRAME_SIZE)); + u32 renderSamples = std::min(channel.mSamplesLeft, static_cast(additional_amount)); Adpcm4ToPcm16( aramBase + dataPosition, ConvertSamplesToDataLength(channel, renderSamples), - subframe.data(), + requested, renderSamples, - channelAux.hist1, - channelAux.hist0); + aux.hist1, + aux.hist0); channel.mSamplesLeft -= renderSamples; channel.mSamplePosition += renderSamples; @@ -135,16 +151,50 @@ static void RenderChannel( Adpcm4ToPcm16( aramBase + dataPosition, - ConvertSamplesToDataLength(channel, DSP_SUBFRAME_SIZE - renderSamples), - subframe.data() + renderSamples, - subframe.size() - renderSamples, - channelAux.hist1, - channelAux.hist0); + ConvertSamplesToDataLength(channel, additional_amount - renderSamples), + requested + renderSamples, + additional_amount - renderSamples, + aux.hist1, + aux.hist0); - channel.mSamplesLeft -= (DSP_SUBFRAME_SIZE - renderSamples); - channel.mSamplePosition += (DSP_SUBFRAME_SIZE - renderSamples); + channel.mSamplesLeft -= (additional_amount - renderSamples); + channel.mSamplePosition += (additional_amount - renderSamples); } channel.mAramStreamPosition = channel.mWaveAramAddress + ConvertSamplesToDataLength(channel, channel.mSamplePosition); + + SDL_PutAudioStreamData(stream, requested, requestedSize); +} + +static void RenderChannel( + JASDsp::TChannel& channel, + ChannelAuxData& channelAux, + DspSubframe& subframe) { + if (channel.mResetFlag) { + ResetChannel(channel, channelAux); + } + + SDL_GetAudioStreamData( + channelAux.resampleStream, + subframe.data(), + static_cast(subframe.size() * sizeof(s16))); +} + +void dusk::audio::DspInit() { + constexpr SDL_AudioSpec spec = { + SDL_AUDIO_S16, + 1, + SampleRate + }; + + for (int i = 0; i < DSP_CHANNELS; i++) { + auto& aux = ChannelAux[i]; + aux.resampleStream = SDL_CreateAudioStream(&spec, &spec); + + SDL_SetAudioStreamGetCallback( + aux.resampleStream, + ReadChannelSamples, + reinterpret_cast(i)); + } } diff --git a/src/dusk/audio/DuskDsp.hpp b/src/dusk/audio/DuskDsp.hpp index a8013d6669..186f7de677 100644 --- a/src/dusk/audio/DuskDsp.hpp +++ b/src/dusk/audio/DuskDsp.hpp @@ -4,15 +4,21 @@ #include +#include "SDL3/SDL_audio.h" + namespace dusk::audio { + constexpr int SampleRate = 32000; + struct ChannelAuxData { s16 hist1; s16 hist0; + SDL_AudioStream* resampleStream; }; extern ChannelAuxData ChannelAux[DSP_CHANNELS]; using DspSubframe = std::array; + void DspInit(); void DspRender(DspSubframe& subframe); } From 6f68ce35315e0e2af73056f68932dcfb26338411 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Sat, 14 Mar 2026 16:42:41 +0100 Subject: [PATCH 37/94] Fix OFFSET_PTR name conflicts --- .../JSystem/J3DGraphAnimator/J3DAnimation.h | 162 +++++++++--------- .../JSystem/J3DGraphAnimator/J3DCluster.h | 22 +-- .../JSystem/J3DGraphLoader/J3DClusterLoader.h | 20 +-- 3 files changed, 102 insertions(+), 102 deletions(-) diff --git a/libs/JSystem/include/JSystem/J3DGraphAnimator/J3DAnimation.h b/libs/JSystem/include/JSystem/J3DGraphAnimator/J3DAnimation.h index 81c363988f..84b27ea969 100644 --- a/libs/JSystem/include/JSystem/J3DGraphAnimator/J3DAnimation.h +++ b/libs/JSystem/include/JSystem/J3DGraphAnimator/J3DAnimation.h @@ -9,9 +9,9 @@ #include "dusk/endian.h" #if TARGET_PC -#define OFFSET_PTR BE(u32) +#define OFFSET_PTR_V0 BE(u32) #else -#define OFFSET_PTR void* +#define OFFSET_PTR_V0 void* #endif struct J3DTransformInfo; @@ -80,7 +80,7 @@ struct J3DAnmColorKeyTable { */ struct J3DAnmVtxColorIndexData { /* 0x00 */ BE(u16) mNum; - /* 0x04 */ OFFSET_PTR mpData; + /* 0x04 */ OFFSET_PTR_V0 mpData; }; /** @@ -200,13 +200,13 @@ struct J3DAnmVtxColorFullData { /* 0x0A */ BE(s16) mFrameMax; /* 0x0C */ BE(u16) mAnmTableNum[2]; /* 0x10 */ u8 field_0x10[0x18 - 0x10]; - /* 0x18 */ OFFSET_PTR mTableOffsets[2]; - /* 0x20 */ OFFSET_PTR mVtxColorIndexDataOffsets[2]; - /* 0x28 */ OFFSET_PTR mVtxColorIndexPointerOffsets[2]; - /* 0x30 */ OFFSET_PTR mRValuesOffset; - /* 0x34 */ OFFSET_PTR mGValuesOffset; - /* 0x38 */ OFFSET_PTR mBValuesOffset; - /* 0x3C */ OFFSET_PTR mAValuesOffset; + /* 0x18 */ OFFSET_PTR_V0 mTableOffsets[2]; + /* 0x20 */ OFFSET_PTR_V0 mVtxColorIndexDataOffsets[2]; + /* 0x28 */ OFFSET_PTR_V0 mVtxColorIndexPointerOffsets[2]; + /* 0x30 */ OFFSET_PTR_V0 mRValuesOffset; + /* 0x34 */ OFFSET_PTR_V0 mGValuesOffset; + /* 0x38 */ OFFSET_PTR_V0 mBValuesOffset; + /* 0x3C */ OFFSET_PTR_V0 mAValuesOffset; }; // Size = 0x40 STATIC_ASSERT(sizeof(J3DAnmVtxColorFullData) == 0x40); @@ -222,8 +222,8 @@ struct J3DAnmVisibilityFullData { /* 0x0A */ BE(s16) mFrameMax; /* 0x0C */ BE(u16) field_0xc; /* 0x0E */ BE(u16) field_0xe; - /* 0x10 */ OFFSET_PTR mTableOffset; - /* 0x14 */ OFFSET_PTR mValuesOffset; + /* 0x10 */ OFFSET_PTR_V0 mTableOffset; + /* 0x14 */ OFFSET_PTR_V0 mValuesOffset; }; // Size = 0x18 STATIC_ASSERT(sizeof(J3DAnmVisibilityFullData) == 0x18); @@ -239,10 +239,10 @@ struct J3DAnmTransformFullData { /* 0x0A */ BE(s16) mFrameMax; /* 0x0C */ BE(u16) field_0xc; /* 0x0E */ u8 field_0xe[0x14 - 0xe]; - /* 0x14 */ OFFSET_PTR mTableOffset; - /* 0x18 */ OFFSET_PTR mScaleValOffset; - /* 0x1C */ OFFSET_PTR mRotValOffset; - /* 0x20 */ OFFSET_PTR mTransValOffset; + /* 0x14 */ OFFSET_PTR_V0 mTableOffset; + /* 0x18 */ OFFSET_PTR_V0 mScaleValOffset; + /* 0x1C */ OFFSET_PTR_V0 mRotValOffset; + /* 0x20 */ OFFSET_PTR_V0 mTransValOffset; }; // Size = 0x24 STATIC_ASSERT(sizeof(J3DAnmTransformFullData) == 0x24); @@ -261,13 +261,13 @@ struct J3DAnmColorKeyData { /* 0x12 */ BE(u16) field_0x12; /* 0x14 */ BE(u16) field_0x14; /* 0x16 */ BE(u16) field_0x16; - /* 0x18 */ OFFSET_PTR mTableOffset; - /* 0x1C */ OFFSET_PTR mUpdateMaterialIDOffset; - /* 0x20 */ OFFSET_PTR mNameTabOffset; - /* 0x24 */ OFFSET_PTR mRValOffset; - /* 0x28 */ OFFSET_PTR mGValOffset; - /* 0x2C */ OFFSET_PTR mBValOffset; - /* 0x30 */ OFFSET_PTR mAValOffset; + /* 0x18 */ OFFSET_PTR_V0 mTableOffset; + /* 0x1C */ OFFSET_PTR_V0 mUpdateMaterialIDOffset; + /* 0x20 */ OFFSET_PTR_V0 mNameTabOffset; + /* 0x24 */ OFFSET_PTR_V0 mRValOffset; + /* 0x28 */ OFFSET_PTR_V0 mGValOffset; + /* 0x2C */ OFFSET_PTR_V0 mBValOffset; + /* 0x30 */ OFFSET_PTR_V0 mAValOffset; }; // Size = 0x34 STATIC_ASSERT(sizeof(J3DAnmColorKeyData) == 0x34); @@ -285,26 +285,26 @@ struct J3DAnmTextureSRTKeyData { /* 0x0E */ BE(u16) field_0xe; /* 0x10 */ BE(u16) field_0x10; /* 0x12 */ BE(u16) field_0x12; - /* 0x14 */ OFFSET_PTR mTableOffset; - /* 0x18 */ OFFSET_PTR mUpdateMatIDOffset; - /* 0x1C */ OFFSET_PTR mNameTab1Offset; - /* 0x20 */ OFFSET_PTR mUpdateTexMtxIDOffset; - /* 0x24 */ OFFSET_PTR unkOffset; - /* 0x28 */ OFFSET_PTR mScaleValOffset; - /* 0x2C */ OFFSET_PTR mRotValOffset; - /* 0x30 */ OFFSET_PTR mTransValOffset; + /* 0x14 */ OFFSET_PTR_V0 mTableOffset; + /* 0x18 */ OFFSET_PTR_V0 mUpdateMatIDOffset; + /* 0x1C */ OFFSET_PTR_V0 mNameTab1Offset; + /* 0x20 */ OFFSET_PTR_V0 mUpdateTexMtxIDOffset; + /* 0x24 */ OFFSET_PTR_V0 unkOffset; + /* 0x28 */ OFFSET_PTR_V0 mScaleValOffset; + /* 0x2C */ OFFSET_PTR_V0 mRotValOffset; + /* 0x30 */ OFFSET_PTR_V0 mTransValOffset; /* 0x34 */ BE(u16) field_0x34; /* 0x36 */ BE(u16) field_0x36; /* 0x38 */ BE(u16) field_0x38; /* 0x3A */ BE(u16) field_0x3a; - /* 0x3C */ OFFSET_PTR mInfoTable2Offset; - /* 0x40 */ OFFSET_PTR field_0x40; - /* 0x44 */ OFFSET_PTR mNameTab2Offset; - /* 0x48 */ OFFSET_PTR field_0x48; - /* 0x4C */ OFFSET_PTR field_0x4c; - /* 0x50 */ OFFSET_PTR field_0x50; - /* 0x54 */ OFFSET_PTR field_0x54; - /* 0x58 */ OFFSET_PTR field_0x58; + /* 0x3C */ OFFSET_PTR_V0 mInfoTable2Offset; + /* 0x40 */ OFFSET_PTR_V0 field_0x40; + /* 0x44 */ OFFSET_PTR_V0 mNameTab2Offset; + /* 0x48 */ OFFSET_PTR_V0 field_0x48; + /* 0x4C */ OFFSET_PTR_V0 field_0x4c; + /* 0x50 */ OFFSET_PTR_V0 field_0x50; + /* 0x54 */ OFFSET_PTR_V0 field_0x54; + /* 0x58 */ OFFSET_PTR_V0 field_0x58; /* 0x5C */ BE(s32) field_0x5c; }; // Size = 0x60 @@ -321,13 +321,13 @@ struct J3DAnmVtxColorKeyData { /* 0x0A */ BE(s16) mFrameMax; /* 0x0C */ BE(u16) mAnmTableNum[2]; /* 0x10 */ u8 field_0x10[0x18 - 0x10]; - /* 0x18 */ OFFSET_PTR mTableOffsets[2]; - /* 0x20 */ OFFSET_PTR mVtxColoIndexDataOffset[2]; - /* 0x28 */ OFFSET_PTR mVtxColoIndexPointerOffset[2]; - /* 0x30 */ OFFSET_PTR mRValOffset; - /* 0x34 */ OFFSET_PTR mGValOffset; - /* 0x38 */ OFFSET_PTR mBValOffset; - /* 0x3C */ OFFSET_PTR mAValOffset; + /* 0x18 */ OFFSET_PTR_V0 mTableOffsets[2]; + /* 0x20 */ OFFSET_PTR_V0 mVtxColoIndexDataOffset[2]; + /* 0x28 */ OFFSET_PTR_V0 mVtxColoIndexPointerOffset[2]; + /* 0x30 */ OFFSET_PTR_V0 mRValOffset; + /* 0x34 */ OFFSET_PTR_V0 mGValOffset; + /* 0x38 */ OFFSET_PTR_V0 mBValOffset; + /* 0x3C */ OFFSET_PTR_V0 mAValOffset; }; // Size = 0x40 STATIC_ASSERT(sizeof(J3DAnmVtxColorKeyData) == 0x40); @@ -343,10 +343,10 @@ struct J3DAnmTexPatternFullData { /* 0x0A */ BE(s16) mFrameMax; /* 0x0C */ BE(u16) field_0xc; /* 0x0E */ BE(u16) field_0xe; - /* 0x10 */ OFFSET_PTR mTableOffset; - /* 0x14 */ OFFSET_PTR mValuesOffset; - /* 0x18 */ OFFSET_PTR mUpdateMaterialIDOffset; - /* 0x1C */ OFFSET_PTR mNameTabOffset; + /* 0x10 */ OFFSET_PTR_V0 mTableOffset; + /* 0x14 */ OFFSET_PTR_V0 mValuesOffset; + /* 0x18 */ OFFSET_PTR_V0 mUpdateMaterialIDOffset; + /* 0x1C */ OFFSET_PTR_V0 mNameTabOffset; }; // Size = 0x20 STATIC_ASSERT(sizeof(J3DAnmTexPatternFullData) == 0x20); @@ -370,20 +370,20 @@ struct J3DAnmTevRegKeyData { /* 0x1A */ BE(u16) field_0x1a; /* 0x1C */ BE(u16) field_0x1c; /* 0x1E */ BE(u16) field_0x1e; - /* 0x20 */ OFFSET_PTR mCRegTableOffset; - /* 0x24 */ OFFSET_PTR mKRegTableOffset; - /* 0x28 */ OFFSET_PTR mCRegUpdateMaterialIDOffset; - /* 0x2C */ OFFSET_PTR mKRegUpdateMaterialIDOffset; - /* 0x30 */ OFFSET_PTR mCRegNameTabOffset; - /* 0x34 */ OFFSET_PTR mKRegNameTabOffset; - /* 0x38 */ OFFSET_PTR mCRValuesOffset; - /* 0x3C */ OFFSET_PTR mCGValuesOffset; - /* 0x40 */ OFFSET_PTR mCBValuesOffset; - /* 0x44 */ OFFSET_PTR mCAValuesOffset; - /* 0x48 */ OFFSET_PTR mKRValuesOffset; - /* 0x4C */ OFFSET_PTR mKGValuesOffset; - /* 0x50 */ OFFSET_PTR mKBValuesOffset; - /* 0x54 */ OFFSET_PTR mKAValuesOffset; + /* 0x20 */ OFFSET_PTR_V0 mCRegTableOffset; + /* 0x24 */ OFFSET_PTR_V0 mKRegTableOffset; + /* 0x28 */ OFFSET_PTR_V0 mCRegUpdateMaterialIDOffset; + /* 0x2C */ OFFSET_PTR_V0 mKRegUpdateMaterialIDOffset; + /* 0x30 */ OFFSET_PTR_V0 mCRegNameTabOffset; + /* 0x34 */ OFFSET_PTR_V0 mKRegNameTabOffset; + /* 0x38 */ OFFSET_PTR_V0 mCRValuesOffset; + /* 0x3C */ OFFSET_PTR_V0 mCGValuesOffset; + /* 0x40 */ OFFSET_PTR_V0 mCBValuesOffset; + /* 0x44 */ OFFSET_PTR_V0 mCAValuesOffset; + /* 0x48 */ OFFSET_PTR_V0 mKRValuesOffset; + /* 0x4C */ OFFSET_PTR_V0 mKGValuesOffset; + /* 0x50 */ OFFSET_PTR_V0 mKBValuesOffset; + /* 0x54 */ OFFSET_PTR_V0 mKAValuesOffset; }; // Size = 0x58 STATIC_ASSERT(sizeof(J3DAnmTevRegKeyData) == 0x58); @@ -399,13 +399,13 @@ struct J3DAnmColorFullData { /* PlaceHolder Structure */ /* 0x0C */ BE(s16) mFrameMax; /* 0x0E */ BE(u16) mUpdateMaterialNum; /* 0x10 */ u8 field_0x10[0x18 - 0x10]; - /* 0x18 */ OFFSET_PTR mTableOffset; - /* 0x1C */ OFFSET_PTR mUpdateMaterialIDOffset; - /* 0x20 */ OFFSET_PTR mNameTabOffset; - /* 0x24 */ OFFSET_PTR mRValuesOffset; - /* 0x28 */ OFFSET_PTR mGValuesOffset; - /* 0x2C */ OFFSET_PTR mBValuesOffset; - /* 0x30 */ OFFSET_PTR mAValuesOffset; + /* 0x18 */ OFFSET_PTR_V0 mTableOffset; + /* 0x1C */ OFFSET_PTR_V0 mUpdateMaterialIDOffset; + /* 0x20 */ OFFSET_PTR_V0 mNameTabOffset; + /* 0x24 */ OFFSET_PTR_V0 mRValuesOffset; + /* 0x28 */ OFFSET_PTR_V0 mGValuesOffset; + /* 0x2C */ OFFSET_PTR_V0 mBValuesOffset; + /* 0x30 */ OFFSET_PTR_V0 mAValuesOffset; }; // Size = 0x34 STATIC_ASSERT(sizeof(J3DAnmColorFullData) == 0x34); @@ -431,10 +431,10 @@ struct J3DAnmTransformKeyData { /* 0x0E */ BE(u16) mSCount; /* 0x10 */ BE(u16) mRCount; /* 0x12 */ BE(u16) mTCount; - /* 0x14 */ OFFSET_PTR mJointAnimationTableOffs; - /* 0x18 */ OFFSET_PTR mSTableOffs; - /* 0x1c */ OFFSET_PTR mRTableOffs; - /* 0x20 */ OFFSET_PTR mTTableOffs; + /* 0x14 */ OFFSET_PTR_V0 mJointAnimationTableOffs; + /* 0x18 */ OFFSET_PTR_V0 mSTableOffs; + /* 0x1c */ OFFSET_PTR_V0 mRTableOffs; + /* 0x20 */ OFFSET_PTR_V0 mTTableOffs; }; /** @@ -446,8 +446,8 @@ struct J3DAnmClusterKeyData { /* 0x08 */ u8 field_0x8; /* 0x0A */ BE(s16) mFrameMax; /* 0x0C */ BE(s32) field_0xc; - /* 0x10 */ OFFSET_PTR mTableOffset; - /* 0x14 */ OFFSET_PTR mWeightOffset; + /* 0x10 */ OFFSET_PTR_V0 mTableOffset; + /* 0x14 */ OFFSET_PTR_V0 mWeightOffset; }; /** @@ -459,8 +459,8 @@ struct J3DAnmClusterFullData { /* 0x08 */ u8 field_0x8; /* 0x0A */ BE(s16) mFrameMax; /* 0x0C */ BE(s32) field_0xc; - /* 0x10 */ OFFSET_PTR mTableOffset; - /* 0x14 */ OFFSET_PTR mWeightOffset; + /* 0x10 */ OFFSET_PTR_V0 mTableOffset; + /* 0x14 */ OFFSET_PTR_V0 mWeightOffset; }; /** @@ -718,7 +718,7 @@ public: // Address to which getAnmVtxColorIndexData pointers are relative. u16* colorAddressBase[2]; - u16* offsetColorIndexAddress(u8 index, OFFSET_PTR ptr) const { + u16* offsetColorIndexAddress(u8 index, OFFSET_PTR_V0 ptr) const { return colorAddressBase[index] + ptr; } #endif @@ -994,6 +994,6 @@ public: /* 0x10 */ f32 mFrame; }; // Size: 0x14 -#undef OFFSET_PTR +#undef OFFSET_PTR_V0 #endif /* J3DANIMATION_H */ diff --git a/libs/JSystem/include/JSystem/J3DGraphAnimator/J3DCluster.h b/libs/JSystem/include/JSystem/J3DGraphAnimator/J3DCluster.h index df7ca2e5c4..4680cd1fff 100644 --- a/libs/JSystem/include/JSystem/J3DGraphAnimator/J3DCluster.h +++ b/libs/JSystem/include/JSystem/J3DGraphAnimator/J3DCluster.h @@ -14,9 +14,9 @@ class J3DAnmCluster; class JUTNameTab; #if TARGET_PC -#define OFFSET_PTR BE(u32) +#define OFFSET_PTR_V0 BE(u32) #else -#define OFFSET_PTR void* +#define OFFSET_PTR_V0 void* #endif /** @@ -49,7 +49,7 @@ public: /* 0x00 */ BE(f32) mMaxAngle; /* 0x04 */ BE(f32) mMinAngle; - /* 0x08 */ OFFSET_PTR mClusterKey; + /* 0x08 */ OFFSET_PTR_V0 mClusterKey; /* 0x0C */ u8 mFlags; /* 0x0E */ u8 field_0xe[0x10 - 0xD]; /* 0x10 */ BE(u16) mKeyNum; @@ -57,9 +57,9 @@ public: /* 0x14 */ BE(u16) field_0x14; /* 0x16 */ BE(u16) field_0x16; #if TARGET_PC - OFFSET_PTR field_0x18; - OFFSET_PTR mClusterVertex; - OFFSET_PTR mDeformer; + OFFSET_PTR_V0 field_0x18; + OFFSET_PTR_V0 mClusterVertex; + OFFSET_PTR_V0 mDeformer; #else /* 0x18 */ u16* field_0x18; /* 0x1C */ J3DClusterVertex* mClusterVertex; @@ -82,8 +82,8 @@ public: /* 0x00 */ BE(u16) mPosNum; /* 0x02 */ BE(u16) mNrmNum; - /* 0x04 */ OFFSET_PTR field_0x4; - /* 0x08 */ OFFSET_PTR field_0x8; + /* 0x04 */ OFFSET_PTR_V0 field_0x4; + /* 0x08 */ OFFSET_PTR_V0 field_0x8; }; // Size: 0x0C /** @@ -144,14 +144,14 @@ public: /* 0x00 */ BE(u16) mNum; #if TARGET_PC - /* 0x04 */ OFFSET_PTR field_0x4; - /* 0x08 */ OFFSET_PTR field_0x8; + /* 0x04 */ OFFSET_PTR_V0 field_0x4; + /* 0x08 */ OFFSET_PTR_V0 field_0x8; #else /* 0x04 */ u16* field_0x4; /* 0x08 */ u16* field_0x8; #endif }; // Size: 0x0C -#undef OFFSET_PTR +#undef OFFSET_PTR_V0 #endif /* J3DCLUSTER_H */ diff --git a/libs/JSystem/include/JSystem/J3DGraphLoader/J3DClusterLoader.h b/libs/JSystem/include/JSystem/J3DGraphLoader/J3DClusterLoader.h index 8fa2681f35..2899065150 100644 --- a/libs/JSystem/include/JSystem/J3DGraphLoader/J3DClusterLoader.h +++ b/libs/JSystem/include/JSystem/J3DGraphLoader/J3DClusterLoader.h @@ -7,9 +7,9 @@ #include "dusk/endian.h" #if TARGET_PC -#define OFFSET_PTR BE(u32) +#define OFFSET_PTR_V0 BE(u32) #else -#define OFFSET_PTR void* +#define OFFSET_PTR_V0 void* #endif /** @@ -31,13 +31,13 @@ public: /* 0x0C */ BE(u16) mClusterVertexNum; /* 0x0E */ BE(u16) mVtxPosNum; /* 0x10 */ BE(u16) mVtxNrmNum; - /* 0x14 */ OFFSET_PTR mClusterPointer; - /* 0x18 */ OFFSET_PTR mClusterKeyPointer; - /* 0x1C */ OFFSET_PTR mClusterVertex; - /* 0x20 */ OFFSET_PTR mVtxPos; - /* 0x24 */ OFFSET_PTR mVtxNrm; - /* 0x28 */ OFFSET_PTR mClusterName; - /* 0x2C */ OFFSET_PTR mClusterKeyName; + /* 0x14 */ OFFSET_PTR_V0 mClusterPointer; + /* 0x18 */ OFFSET_PTR_V0 mClusterKeyPointer; + /* 0x1C */ OFFSET_PTR_V0 mClusterVertex; + /* 0x20 */ OFFSET_PTR_V0 mVtxPos; + /* 0x24 */ OFFSET_PTR_V0 mVtxNrm; + /* 0x28 */ OFFSET_PTR_V0 mClusterName; + /* 0x2C */ OFFSET_PTR_V0 mClusterKeyName; }; /** @@ -67,6 +67,6 @@ public: /* 0x04 */ J3DDeformData* mpDeformData; }; -#undef OFFSET_PTR +#undef OFFSET_PTR_V0 #endif /* J3DCLUSTERLOADER_H */ From 73d1d20d8933197c04222b2f80d3b6303bbfb0f5 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Sat, 14 Mar 2026 16:43:20 +0100 Subject: [PATCH 38/94] Fix JAUSoundAnimation --- .../JSystem/JAudio2/JAUSoundAnimator.h | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/libs/JSystem/include/JSystem/JAudio2/JAUSoundAnimator.h b/libs/JSystem/include/JSystem/JAudio2/JAUSoundAnimator.h index b9e1d433c9..e81cd70ee4 100644 --- a/libs/JSystem/include/JSystem/JAudio2/JAUSoundAnimator.h +++ b/libs/JSystem/include/JSystem/JAudio2/JAUSoundAnimator.h @@ -2,6 +2,7 @@ #define JAUSOUNDANIMATOR_H #include "JSystem/JAudio2/JAISound.h" +#include "dusk/offset_ptr.h" class JAUSoundAnimation; @@ -62,10 +63,10 @@ public: } /* 0x00 */ JAISoundID mSoundId; - /* 0x04 */ f32 field_0x04; - /* 0x08 */ f32 field_0x08; - /* 0x0C */ f32 field_0x0c; - /* 0x10 */ u32 mFlags; + /* 0x04 */ BE(f32) field_0x04; + /* 0x08 */ BE(f32) field_0x08; + /* 0x0C */ BE(f32) field_0x0c; + /* 0x10 */ BE(u32) mFlags; /* 0x14 */ u8 field_0x14; /* 0x15 */ u8 field_0x15; /* 0x16 */ u8 field_0x16; @@ -97,23 +98,31 @@ public: int getEndSoundIndex(f32) const; u16 getNumSounds() const { +#if TARGET_PC + return mNumSounds; +#else if (mControl != NULL) { return mControl->getNumSounds(this); } else { return mNumSounds; } +#endif } const JAUSoundAnimationSound* getSound(int i_index) const { +#if TARGET_PC + return &mSounds + i_index; +#else if (mControl != NULL) { return mControl->getSound(this, i_index); } else { return &mSounds + i_index; } +#endif } - /* 0x0 */ u16 mNumSounds; - /* 0x4 */ JAUSoundAnimationControl* mControl; + /* 0x0 */ BE(u16) mNumSounds; + /* 0x4 */ OFFSET_PTR(JAUSoundAnimationControl) mControl; /* 0x8 */ JAUSoundAnimationSound mSounds; // actually an array }; From c962c167b7f1e877b1fe4fe883a0c228271b2e75 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Sat, 14 Mar 2026 16:43:29 +0100 Subject: [PATCH 39/94] Fix sound userdata pointers being truncated --- include/Z2AudioLib/Z2SoundHandles.h | 2 +- src/Z2AudioLib/Z2SoundHandles.cpp | 2 +- src/Z2AudioLib/Z2SoundObject.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/Z2AudioLib/Z2SoundHandles.h b/include/Z2AudioLib/Z2SoundHandles.h index 2d9d490f5e..5915e5967b 100644 --- a/include/Z2AudioLib/Z2SoundHandles.h +++ b/include/Z2AudioLib/Z2SoundHandles.h @@ -22,7 +22,7 @@ public: bool isActive() const; Z2SoundHandlePool* getHandleSoundID(JAISoundID soundID); - Z2SoundHandlePool* getHandleUserData(u32 userData); + Z2SoundHandlePool* getHandleUserData(uintptr_t userData); void stopAllSounds(u32 fadeTime); diff --git a/src/Z2AudioLib/Z2SoundHandles.cpp b/src/Z2AudioLib/Z2SoundHandles.cpp index 9b6aa5a9ba..de7e265ae7 100644 --- a/src/Z2AudioLib/Z2SoundHandles.cpp +++ b/src/Z2AudioLib/Z2SoundHandles.cpp @@ -40,7 +40,7 @@ Z2SoundHandlePool* Z2SoundHandles::getHandleSoundID(JAISoundID soundID) { return NULL; } -Z2SoundHandlePool* Z2SoundHandles::getHandleUserData(u32 userData) { +Z2SoundHandlePool* Z2SoundHandles::getHandleUserData(uintptr_t userData) { JSULink* i; for (i = getFirst(); i != NULL; i = i->getNext()) { Z2SoundHandlePool* handle = i->getObject(); diff --git a/src/Z2AudioLib/Z2SoundObject.cpp b/src/Z2AudioLib/Z2SoundObject.cpp index 0978842251..4581ca389e 100644 --- a/src/Z2AudioLib/Z2SoundObject.cpp +++ b/src/Z2AudioLib/Z2SoundObject.cpp @@ -500,7 +500,7 @@ void Z2SoundObjAnime::startSoundInner(const JGeometry::TVec3& pos, f32 para JUT_ASSERT(747, curSoundIndex_ < animation_->getNumSounds()); const JAUSoundAnimationSound* animationSound = animation_->getSound(curSoundIndex_); - u32 user_data = (uintptr_t)animationSound; + uintptr_t user_data = (uintptr_t)animationSound; if (reverse_) { curSoundIndex_--; } else { From e86aebfcd86814f340d06d4b254d9dabbcbe7062 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Sat, 14 Mar 2026 16:44:04 +0100 Subject: [PATCH 40/94] Disable debug code in JASAramStream --- libs/JSystem/src/JAudio2/JASAramStream.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libs/JSystem/src/JAudio2/JASAramStream.cpp b/libs/JSystem/src/JAudio2/JASAramStream.cpp index 30d4d8da7a..56e3e6effd 100644 --- a/libs/JSystem/src/JAudio2/JASAramStream.cpp +++ b/libs/JSystem/src/JAudio2/JASAramStream.cpp @@ -442,10 +442,12 @@ void JASAramStream::updateChannel(u32 i_callbackType, JASChannel* i_channel, case JASChannel::CB_PLAY: 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; From 0bd5ec4da58a8d386a8acf16699df1bbdf074343 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Sat, 14 Mar 2026 17:35:09 +0100 Subject: [PATCH 41/94] Audio debug imgui --- CMakeLists.txt | 3 ++- src/dusk/imgui/ImGuiMenuTools.cpp | 2 ++ src/dusk/imgui/ImGuiMenuTools.hpp | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 25b78d60b3..12c4154994 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -106,7 +106,8 @@ target_link_libraries(game_debug PUBLIC aurora::core aurora::gx aurora::gd auror set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) add_library(game SHARED ${DOLZEL_FILES} ${Z2AUDIOLIB_FILES} ${SSYSTEM_FILES} ${JSYSTEM_FILES} ${REL_FILES} ${DUSK_FILES} ${DOLPHIN_FILES} - src/dusk/imgui/ImGuiStubLog.cpp) + src/dusk/imgui/ImGuiStubLog.cpp + src/dusk/imgui/ImGuiAudio.cpp) target_link_libraries(game PRIVATE game_debug cxxopts::cxxopts) target_compile_definitions(game PRIVATE TARGET_PC AVOID_UB=1 VERSION=0 NDEBUG=1 NDEBUG_DEFINED=1 DEBUG_DEFINED=0 diff --git a/src/dusk/imgui/ImGuiMenuTools.cpp b/src/dusk/imgui/ImGuiMenuTools.cpp index 64afcc2ddf..fe21a9088a 100644 --- a/src/dusk/imgui/ImGuiMenuTools.cpp +++ b/src/dusk/imgui/ImGuiMenuTools.cpp @@ -16,6 +16,7 @@ namespace dusk { ImGui::MenuItem("Stub Log", "F5", &m_showStubLog); ImGui::MenuItem("Camera", "F6", &m_showCameraOverlay); ImGui::MenuItem("Map Loader", nullptr, &m_showMapLoader); + ImGui::MenuItem("Audio Debug", "F7", &m_showAudioDebug); ImGui::EndMenu(); } @@ -25,6 +26,7 @@ namespace dusk { ShowHeapOverlay(); ShowStubLog(); ShowMapLoader(); + ShowAudioDebug(); DuskDebugPad(); // temporary, remove later } diff --git a/src/dusk/imgui/ImGuiMenuTools.hpp b/src/dusk/imgui/ImGuiMenuTools.hpp index 1a2e5c705f..d5905cd2f9 100644 --- a/src/dusk/imgui/ImGuiMenuTools.hpp +++ b/src/dusk/imgui/ImGuiMenuTools.hpp @@ -18,6 +18,7 @@ namespace dusk { void ShowHeapOverlay(); void ShowStubLog(); void ShowMapLoader(); + void ShowAudioDebug(); private: bool m_showDebugOverlay = false; @@ -33,6 +34,8 @@ namespace dusk { bool m_showStubLog = false; bool m_showMapLoader = false; + + bool m_showAudioDebug = false; struct { int mapIdx = -1; int regionIdx = -1; From 197e59896e5d77b9e6f4b2b28049651560fb539c Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Sat, 14 Mar 2026 17:38:26 +0100 Subject: [PATCH 42/94] Foiled again --- src/dusk/imgui/ImGuiAudio.cpp | 38 +++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 src/dusk/imgui/ImGuiAudio.cpp diff --git a/src/dusk/imgui/ImGuiAudio.cpp b/src/dusk/imgui/ImGuiAudio.cpp new file mode 100644 index 0000000000..4dfd64fe9c --- /dev/null +++ b/src/dusk/imgui/ImGuiAudio.cpp @@ -0,0 +1,38 @@ +#include "ImGuiConsole.hpp" +#include "ImGuiMenuTools.hpp" +#include "JSystem/JAudio2/JASCriticalSection.h" +#include "JSystem/JAudio2/JASDSPInterface.h" + +void dusk::ImGuiMenuTools::ShowAudioDebug() { + if (!ImGuiConsole::CheckMenuViewToggle(ImGuiKey_F7, m_showAudioDebug)) { + return; + } + + if (!ImGui::Begin("Audio Debug", &m_showAudioDebug)) { + ImGui::End(); + return; + } + + { + JASCriticalSection cs; + for (int i = 0; i < DSP_CHANNELS; i++) { + auto& channel = JASDsp::CH_BUF[i]; + if (!channel.mIsActive) { + continue; + } + + char buf[64]; + snprintf(buf, sizeof(buf), "%d", i); + + if (ImGui::BeginChild(buf, ImVec2(), ImGuiChildFlags_Border | ImGuiChildFlags_AutoResizeY)) { + ImGui::Text("[%02X]", i); + ImGui::TextUnformatted(channel.mLoopFlag ? "Loop: true" : "Loop: false"); + ImGui::Text("Format: %02X/%02X", channel.mSamplesPerBlock, channel.mBytesPerBlock); + } + + ImGui::EndChild(); + } + } + + ImGui::End(); +} \ No newline at end of file From 2f38d70db80093337752e2d987c73aa0629a59ba Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Sat, 14 Mar 2026 17:55:19 +0100 Subject: [PATCH 43/94] Use type-level operator delete overload when appropriate in jkrDelete Found by Lars --- libs/JSystem/include/JSystem/JKernel/JKRHeap.h | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/libs/JSystem/include/JSystem/JKernel/JKRHeap.h b/libs/JSystem/include/JSystem/JKernel/JKRHeap.h index db4d40b0a3..99f438b0e2 100644 --- a/libs/JSystem/include/JSystem/JKernel/JKRHeap.h +++ b/libs/JSystem/include/JSystem/JKernel/JKRHeap.h @@ -276,7 +276,18 @@ void jkrDelete(T* ptr) { return; } ptr->~T(); - operator delete(ptr, JKRHeapToken::Dummy); + + if constexpr (requires { T::operator delete(ptr, JKRHeapToken::Dummy); }) { + T::operator delete(ptr, JKRHeapToken::Dummy); + } else if constexpr (requires { T::operator delete(ptr, sizeof(T), JKRHeapToken::Dummy); }) { + T::operator delete(ptr, sizeof(T), JKRHeapToken::Dummy); + } else if constexpr (requires { T::operator delete(ptr); }) { + T::operator delete(ptr); + } else if constexpr (requires { T::operator delete(ptr, sizeof(T)); }) { + T::operator delete(ptr, sizeof(T)); + } else { + operator delete(ptr, JKRHeapToken::Dummy); + } } template<> From d759bcffbd2f1c6db86863d4e7a06c9e4a5ae9b7 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Sun, 15 Mar 2026 01:23:11 +0100 Subject: [PATCH 44/94] Names mixed in with some BE fixes --- include/Z2AudioLib/Z2DebugSys.h | 2 +- .../include/JSystem/JAudio2/JAISeqDataMgr.h | 16 +++--- .../include/JSystem/JAudio2/JASSeqParser.h | 6 +-- .../include/JSystem/JAudio2/JASSeqReader.h | 50 +++++++++--------- .../include/JSystem/JAudio2/JASTrackPort.h | 6 +-- .../include/JSystem/JAudio2/JAUAudibleParam.h | 7 +-- .../JSystem/JAudio2/JAUSeqCollection.h | 22 ++++---- libs/JSystem/src/JAudio2/JAISe.cpp | 2 +- libs/JSystem/src/JAudio2/JAISeq.cpp | 2 +- libs/JSystem/src/JAudio2/JASSeqCtrl.cpp | 6 +-- libs/JSystem/src/JAudio2/JASSeqParser.cpp | 8 +-- libs/JSystem/src/JAudio2/JASSeqReader.cpp | 52 +++++++++---------- libs/JSystem/src/JAudio2/JASTrack.cpp | 4 +- libs/JSystem/src/JAudio2/JASTrackPort.cpp | 12 ++--- libs/JSystem/src/JAudio2/JAUSeqCollection.cpp | 26 +++++----- .../src/JAudio2/JAUSeqDataBlockMgr.cpp | 4 +- 16 files changed, 114 insertions(+), 111 deletions(-) diff --git a/include/Z2AudioLib/Z2DebugSys.h b/include/Z2AudioLib/Z2DebugSys.h index a98292e8c1..d7dd19fcf2 100644 --- a/include/Z2AudioLib/Z2DebugSys.h +++ b/include/Z2AudioLib/Z2DebugSys.h @@ -27,7 +27,7 @@ public: virtual ~Z2HioSeSeqDataMgr() {} virtual SeqDataReturnValue getSeqData(JAISoundID param_1, JAISeqData* param_2) { if (field_0x18->getSeqList()->getSeqData(param_1, param_2)) { - param_2->field_0x4 = 4; + param_2->mOffset = 4; return SeqDataReturnValue_2; } else { return JAUSeqDataMgr_SeqCollection::getSeqData(param_1, param_2); diff --git a/libs/JSystem/include/JSystem/JAudio2/JAISeqDataMgr.h b/libs/JSystem/include/JSystem/JAudio2/JAISeqDataMgr.h index 510f1f4905..a517a58c16 100644 --- a/libs/JSystem/include/JSystem/JAudio2/JAISeqDataMgr.h +++ b/libs/JSystem/include/JSystem/JAudio2/JAISeqDataMgr.h @@ -10,17 +10,17 @@ */ struct JAISeqData { JAISeqData(const void* param_0, u32 param_1) { - field_0x0 = (void*)param_0; - field_0x4 = param_1; + mBase = (void*)param_0; + mOffset = param_1; } void set(const void* param_0, u32 param_1) { - field_0x0 = (void*)param_0; - field_0x4 = param_1; + mBase = (void*)param_0; + mOffset = param_1; } - /* 0x00 */ void* field_0x0; - /* 0x04 */ u32 field_0x4; + /* 0x00 */ void* mBase; + /* 0x04 */ u32 mOffset; }; /** @@ -29,10 +29,10 @@ struct JAISeqData { */ struct JAISeqDataRegion { bool intersects(const JAISeqData& seqData) const { - if ((uintptr_t)addr + size < (uintptr_t)seqData.field_0x0) { + if ((uintptr_t)addr + size < (uintptr_t)seqData.mBase) { return false; } - if ((uintptr_t)seqData.field_0x0 + seqData.field_0x4 < (uintptr_t)addr) { + if ((uintptr_t)seqData.mBase + seqData.mOffset < (uintptr_t)addr) { return false; } return true; diff --git a/libs/JSystem/include/JSystem/JAudio2/JASSeqParser.h b/libs/JSystem/include/JSystem/JAudio2/JASSeqParser.h index 096cbadd98..51db04ba26 100644 --- a/libs/JSystem/include/JSystem/JAudio2/JASSeqParser.h +++ b/libs/JSystem/include/JSystem/JAudio2/JASSeqParser.h @@ -14,9 +14,9 @@ public: enum BranchCondition {}; struct CmdInfo { - s32 (JASSeqParser::*field_0x0)(JASTrack*, u32*); - u16 field_0xc; - u16 field_0xe; + s32 (JASSeqParser::*mHandler)(JASTrack*, u32*); + u16 mParameterCount; + u16 mParameterTypes; }; virtual ~JASSeqParser() {} diff --git a/libs/JSystem/include/JSystem/JAudio2/JASSeqReader.h b/libs/JSystem/include/JSystem/JAudio2/JASSeqReader.h index 42a34b9dff..878713602a 100644 --- a/libs/JSystem/include/JSystem/JAudio2/JASSeqReader.h +++ b/libs/JSystem/include/JSystem/JAudio2/JASSeqReader.h @@ -3,6 +3,8 @@ #include +#define JAS_SEQ_STACK_SIZE 8 + /** * @ingroup jsystem-jaudio * @@ -19,55 +21,55 @@ public: int readMidiValue(); void jump(u32 param_1) { - field_0x04 = field_0x00 + param_1; + mCurPos = mBase + param_1; } void jump(void* param_1) { - field_0x04 = (u8*)param_1; + mCurPos = (u8*)param_1; } u32 get24(u32 param_0) const { - return (*(u32*)(field_0x00 + param_0 - 1)) & 0xffffff; + return (*(BE(u32)*)(mBase + param_0 - 1)) & 0xffffff; } - u32* getBase() { return (u32*)field_0x00; } - void* getAddr(u32 param_0) { return field_0x00 + param_0; } - u8 getByte(u32 param_0) const { return *(field_0x00 + param_0); } - u16 get16(u32 param_0) const { return *(u16*)(field_0x00 + param_0); } - u32 get32(u32 param_0) const { return *(u32*)(field_0x00 + param_0); } - u8* getCur() { return field_0x04; } - u32 readByte() { return *field_0x04++; } + u32* getBase() { return (u32*)mBase; } + void* getAddr(u32 param_0) { return mBase + param_0; } + u8 getByte(u32 param_0) const { return *(mBase + param_0); } + u16 get16(u32 param_0) const { return *(BE(u16)*)(mBase + param_0); } + u32 get32(u32 param_0) const { return *(BE(u32)*)(mBase + param_0); } + u8* getCur() { return mCurPos; } + u32 readByte() { return *mCurPos++; } u32 read16() { #ifdef __MWERKS__ - return *((u16*)field_0x04)++; + return *((u16*)mCurPos)++; #else - BE(u16)* value = (BE(u16)*)field_0x04; - field_0x04 += 2; + BE(u16)* value = (BE(u16)*)mCurPos; + mCurPos += 2; return *value; #endif } u32 read24() { - field_0x04--; + mCurPos--; #ifdef __MWERKS__ - return (*((u32*)field_0x04)++) & 0x00ffffff; + return (*((u32*)mCurPos)++) & 0x00ffffff; #else - BE(u32)* value = (BE(u32)*)field_0x04; - field_0x04 += 4; + BE(u32)* value = (BE(u32)*)mCurPos; + mCurPos += 4; return (*value) & 0x00ffffff; #endif } u16 getLoopCount() const { - if (field_0x08 == 0) { + if (mCurStackDepth == 0) { return 0; } - return field_0x2c[field_0x08 - 1]; + return mLoopCount[mCurStackDepth - 1]; } - /* 0x00 */ u8* field_0x00; - /* 0x04 */ u8* field_0x04; - /* 0x08 */ u32 field_0x08; - /* 0x0C */ u16* field_0x0c[8]; - /* 0x2C */ u16 field_0x2c[8]; + /* 0x00 */ u8* mBase; + /* 0x04 */ u8* mCurPos; + /* 0x08 */ u32 mCurStackDepth; + /* 0x0C */ u16* mReturnAddr[JAS_SEQ_STACK_SIZE]; + /* 0x2C */ u16 mLoopCount[JAS_SEQ_STACK_SIZE]; }; #endif /* JASSEQREADER_H */ diff --git a/libs/JSystem/include/JSystem/JAudio2/JASTrackPort.h b/libs/JSystem/include/JSystem/JAudio2/JASTrackPort.h index 393a701c27..781da78049 100644 --- a/libs/JSystem/include/JSystem/JAudio2/JASTrackPort.h +++ b/libs/JSystem/include/JSystem/JAudio2/JASTrackPort.h @@ -19,12 +19,12 @@ public: u32 checkImport(u32) const; u32 checkExport(u32) const; - u16 get(u32 param_0) const { return field_0x4[param_0]; } - void set(u32 param_0, u16 param_1) { field_0x4[param_0] = param_1; } + u16 get(u32 param_0) const { return mPortValues[param_0]; } + void set(u32 param_0, u16 param_1) { mPortValues[param_0] = param_1; } u16 field_0x0; u16 field_0x2; - u16 field_0x4[MAX_PORTS]; + u16 mPortValues[MAX_PORTS]; }; #endif /* JASTRACKPORT_H */ diff --git a/libs/JSystem/include/JSystem/JAudio2/JAUAudibleParam.h b/libs/JSystem/include/JSystem/JAudio2/JAUAudibleParam.h index 89b016cb0a..50f5b45f09 100644 --- a/libs/JSystem/include/JSystem/JAudio2/JAUAudibleParam.h +++ b/libs/JSystem/include/JSystem/JAudio2/JAUAudibleParam.h @@ -2,6 +2,7 @@ #define JAUAUDIBLEPARAM_H #include +#include "dusk/endian.h" /** * @ingroup jsystem-jaudio @@ -14,8 +15,8 @@ struct JAUAudibleParam { union { struct { - u16 f0; - u16 f1; + BE(u16) f0; + BE(u16) f1; } half; struct { u8 b0_0 : 4; @@ -29,7 +30,7 @@ struct JAUAudibleParam { u8 b2; u8 b3; } bytes; - u32 raw; + BE(u32) raw; } field_0x0; }; diff --git a/libs/JSystem/include/JSystem/JAudio2/JAUSeqCollection.h b/libs/JSystem/include/JSystem/JAudio2/JAUSeqCollection.h index 1812976e2f..bfc88ef3ad 100644 --- a/libs/JSystem/include/JSystem/JAudio2/JAUSeqCollection.h +++ b/libs/JSystem/include/JSystem/JAudio2/JAUSeqCollection.h @@ -11,11 +11,11 @@ struct JAISeqDataRegion; * */ struct JAUSeqCollectionData { - s8 field_0x0; - s8 field_0x1; - BE(u16) field_0x2; - BE(u32) field_0x4; - BE(u32) field_0x8; + s8 mMagic1; // 'S' + s8 mMagic2; // 'C' + BE(u16) mNumSoundCategories; + BE(u32) mSectionSize; + BE(u32) mTableOffsets[0]; // VLA }; /** @@ -29,12 +29,12 @@ public: bool getSeqData(int, int, JAISeqData*); bool getSeqDataRegion(JAISeqDataRegion*); - bool isValid() const { return field_0x8; } + bool isValid() const { return mHeader; } - /* 0x00 */ u16 field_0x0; - /* 0x04 */ const BE(u32)* field_0x4; - /* 0x08 */ const JAUSeqCollectionData* field_0x8; - /* 0x0C */ int field_0xc; + /* 0x00 */ u16 mNumSoundCategories; + /* 0x04 */ const BE(u32)* mTableOffsets; + /* 0x08 */ const JAUSeqCollectionData* mHeader; + /* 0x0C */ u32 mSectionSize; }; /** @@ -49,7 +49,7 @@ public: SeqDataReturnValue getSeqData(JAISoundID, JAISeqData*); ~JAUSeqDataMgr_SeqCollection(); - const void* getResource() const { return field_0x4; } + const void* getResource() const { return mTableOffsets; } void init(const void* param_1) { JAUSeqCollection::init(param_1); } /* 0x14 */ JAISeqDataUser* user_; diff --git a/libs/JSystem/src/JAudio2/JAISe.cpp b/libs/JSystem/src/JAudio2/JAISe.cpp index 4e453811a9..504c875bad 100644 --- a/libs/JSystem/src/JAudio2/JAISe.cpp +++ b/libs/JSystem/src/JAudio2/JAISe.cpp @@ -57,7 +57,7 @@ void JAISe::startTrack_(const JASSoundParams& params) { } inner_.field_0x26c = 1; - inner_.track.setSeqData(inner_.mSeqData.field_0x0, inner_.mSeqData.field_0x4); + inner_.track.setSeqData(inner_.mSeqData.mBase, inner_.mSeqData.mOffset); inner_.track.startSeq(); } diff --git a/libs/JSystem/src/JAudio2/JAISeq.cpp b/libs/JSystem/src/JAudio2/JAISeq.cpp index e3d07d5418..c05494b82e 100644 --- a/libs/JSystem/src/JAudio2/JAISeq.cpp +++ b/libs/JSystem/src/JAudio2/JAISeq.cpp @@ -40,7 +40,7 @@ void JAISeq::JAISeqMgr_startID_(JAISoundID id, const JGeometry::TVec3* posP void JAISeq::playSeqData_(const JASSoundParams& params, JAISoundActivity activity) { JUT_ASSERT(72, JASTrack_isFreeOrStopped( & inner_.outputTrack )); - inner_.outputTrack.setSeqData(inner_.mSeqData.field_0x0, inner_.mSeqData.field_0x4); + inner_.outputTrack.setSeqData(inner_.mSeqData.mBase, inner_.mSeqData.mOffset); if (audible_) { initTrack_JAISound_(&inner_.outputTrack); } else { diff --git a/libs/JSystem/src/JAudio2/JASSeqCtrl.cpp b/libs/JSystem/src/JAudio2/JASSeqCtrl.cpp index 044f3873f2..a4e4a4b441 100644 --- a/libs/JSystem/src/JAudio2/JASSeqCtrl.cpp +++ b/libs/JSystem/src/JAudio2/JASSeqCtrl.cpp @@ -34,9 +34,9 @@ void JASSeqCtrl::init() { field_0x51 = 0; } -void JASSeqCtrl::start(void* param_0, u32 param_1) { - mReader.init(param_0); - mReader.jump(param_1); +void JASSeqCtrl::start(void* base, u32 offset) { + mReader.init(base); + mReader.jump(offset); } int JASSeqCtrl::tickProc(JASTrack* param_0) { diff --git a/libs/JSystem/src/JAudio2/JASSeqParser.cpp b/libs/JSystem/src/JAudio2/JASSeqParser.cpp index ea91cc06ae..21cb551cb0 100644 --- a/libs/JSystem/src/JAudio2/JASSeqParser.cpp +++ b/libs/JSystem/src/JAudio2/JASSeqParser.cpp @@ -959,10 +959,10 @@ s32 JASSeqParser::parseCommand(JASTrack* param_0, u8 cmd, u16 param_2) { } else { cmdInfo = &sExtCmdInfo[seqCtrl->readByte() & 0xff]; } - u16 r28 = (u16)cmdInfo->field_0xe; + u16 r28 = (u16)cmdInfo->mParameterTypes; r28 |= param_2; u32 stack_28[8]; - for (int i = 0; i < cmdInfo->field_0xc; i++, r28 >>= 2) { + for (int i = 0; i < cmdInfo->mParameterCount; i++, r28 >>= 2) { int r27 = 0; switch (r28 & 3) { case 0: @@ -980,11 +980,11 @@ s32 JASSeqParser::parseCommand(JASTrack* param_0, u8 cmd, u16 param_2) { } stack_28[i] = r27; } - s32 (JASSeqParser::*ptr)(JASTrack*, u32*) = cmdInfo->field_0x0; + s32 (JASSeqParser::*ptr)(JASTrack*, u32*) = cmdInfo->mHandler; if (!ptr) { return 0; } - return execCommand(param_0, ptr, cmdInfo->field_0xc, stack_28); + return execCommand(param_0, ptr, cmdInfo->mParameterCount, stack_28); } s32 JASSeqParser::parseRegCommand(JASTrack* param_0, int param_1) { diff --git a/libs/JSystem/src/JAudio2/JASSeqReader.cpp b/libs/JSystem/src/JAudio2/JASSeqReader.cpp index c259c57d40..459c2edeee 100644 --- a/libs/JSystem/src/JAudio2/JASSeqReader.cpp +++ b/libs/JSystem/src/JAudio2/JASSeqReader.cpp @@ -8,81 +8,81 @@ #include "JSystem/JAudio2/JASSeqReader.h" void JASSeqReader::init() { - field_0x00 = 0; - field_0x04 = 0; - field_0x08 = 0; + mBase = 0; + mCurPos = 0; + mCurStackDepth = 0; - for (int i = 0; i < 8; i++) { - field_0x0c[i] = NULL; - field_0x2c[i] = 0; + for (int i = 0; i < JAS_SEQ_STACK_SIZE; i++) { + mReturnAddr[i] = NULL; + mLoopCount[i] = 0; } } -void JASSeqReader::init(void* param_0) { - field_0x00 = (u8*)param_0; - field_0x04 = field_0x00; - field_0x08 = 0; +void JASSeqReader::init(void* base) { + mBase = (u8*)base; + mCurPos = mBase; + mCurStackDepth = 0; - for (int i = 0; i < 8; i++) { - field_0x0c[i] = NULL; - field_0x2c[i] = 0; + for (int i = 0; i < JAS_SEQ_STACK_SIZE; i++) { + mReturnAddr[i] = NULL; + mLoopCount[i] = 0; } } bool JASSeqReader::call(u32 param_0) { - if (field_0x08 >= 8) { + if (mCurStackDepth >= JAS_SEQ_STACK_SIZE) { JUT_WARN(42, "%s", "Cannot exec call command"); return false; } - field_0x0c[field_0x08++] = (u16*)field_0x04; - field_0x04 = field_0x00 + param_0; + mReturnAddr[mCurStackDepth++] = (u16*)mCurPos; + mCurPos = mBase + param_0; return true; } bool JASSeqReader::loopStart(u32 param_0) { - if (8 <= field_0x08) { + if (JAS_SEQ_STACK_SIZE <= mCurStackDepth) { JUT_WARN(53, "%s", "Cannot exec loopStart command"); return false; } - field_0x0c[field_0x08] = (u16*)field_0x04; - field_0x2c[field_0x08++] = param_0; + mReturnAddr[mCurStackDepth] = (u16*)mCurPos; + mLoopCount[mCurStackDepth++] = param_0; return true; } bool JASSeqReader::loopEnd() { - if (field_0x08 == 0) { + if (mCurStackDepth == 0) { JUT_WARN(65, "%s", "cannot loopE for call-stack is NULL"); return false; } - u16 tmp = field_0x2c[field_0x08 - 1]; + u16 tmp = mLoopCount[mCurStackDepth - 1]; if (tmp != 0) { tmp--; } if (!tmp) { - field_0x08--; + mCurStackDepth--; return true; } - field_0x2c[field_0x08 - 1] = tmp; - field_0x04 = (u8*)field_0x0c[field_0x08 - 1]; + mLoopCount[mCurStackDepth - 1] = tmp; + mCurPos = (u8*)mReturnAddr[mCurStackDepth - 1]; return true; } bool JASSeqReader::ret() { - if (field_0x08 == 0) { + if (mCurStackDepth == 0) { return false; } - field_0x04 = (u8*)field_0x0c[--field_0x08]; + mCurPos = (u8*)mReturnAddr[--mCurStackDepth]; return true; } diff --git a/libs/JSystem/src/JAudio2/JASTrack.cpp b/libs/JSystem/src/JAudio2/JASTrack.cpp index a60577ca38..68fbee63e5 100644 --- a/libs/JSystem/src/JAudio2/JASTrack.cpp +++ b/libs/JSystem/src/JAudio2/JASTrack.cpp @@ -171,9 +171,9 @@ void JASTrack::assignExtBuffer(u32 index, JASSoundParams* i_soundParams) { mChannelMgrs[index]->mSoundParams = i_soundParams; } -void JASTrack::setSeqData(void* param_0, u32 param_1) { +void JASTrack::setSeqData(void* base, u32 offset) { JUT_ASSERT(257, mStatus == STATUS_FREE); - mSeqCtrl.start(param_0, param_1); + mSeqCtrl.start(base, offset); } void JASTrack::startSeq() { diff --git a/libs/JSystem/src/JAudio2/JASTrackPort.cpp b/libs/JSystem/src/JAudio2/JASTrackPort.cpp index e822212ec9..bde86b37f9 100644 --- a/libs/JSystem/src/JAudio2/JASTrackPort.cpp +++ b/libs/JSystem/src/JAudio2/JASTrackPort.cpp @@ -3,8 +3,8 @@ #include "JSystem/JAudio2/JASTrackPort.h" void JASTrackPort::init() { - for (int i = 0; i < 16; i++) { - field_0x4[i] = 0; + for (int i = 0; i < MAX_PORTS; i++) { + mPortValues[i] = 0; } field_0x0 = 0; field_0x2 = 0; @@ -13,25 +13,25 @@ void JASTrackPort::init() { u16 JASTrackPort::readImport(u32 port_num) { JUT_ASSERT(27, port_num < MAX_PORTS); field_0x0 = field_0x0 & ~(1 << port_num); - return field_0x4[port_num]; + return mPortValues[port_num]; } u16 JASTrackPort::readExport(u32 port_num) { JUT_ASSERT(34, port_num < MAX_PORTS); field_0x2 = field_0x2 & ~(1 << port_num); - return field_0x4[port_num]; + return mPortValues[port_num]; } void JASTrackPort::writeImport(u32 port_num, u16 param_1) { JUT_ASSERT(41, port_num < MAX_PORTS); field_0x0 = field_0x0 | (1 << port_num); - field_0x4[port_num] = param_1; + mPortValues[port_num] = param_1; } void JASTrackPort::writeExport(u32 port_num, u16 param_1) { JUT_ASSERT(47, port_num < MAX_PORTS); field_0x2 = field_0x2 | (1 << port_num); - field_0x4[port_num] = param_1; + mPortValues[port_num] = param_1; } u32 JASTrackPort::checkImport(u32 param_0) const { diff --git a/libs/JSystem/src/JAudio2/JAUSeqCollection.cpp b/libs/JSystem/src/JAudio2/JAUSeqCollection.cpp index 1301cbc4fd..a6071a75e6 100644 --- a/libs/JSystem/src/JAudio2/JAUSeqCollection.cpp +++ b/libs/JSystem/src/JAudio2/JAUSeqCollection.cpp @@ -4,39 +4,39 @@ #include "JSystem/JUtility/JUTAssert.h" JAUSeqCollection::JAUSeqCollection() { - field_0x8 = NULL; + mHeader = NULL; } void JAUSeqCollection::init(void const* param_0) { - field_0x8 = (const JAUSeqCollectionData*)param_0; - if (field_0x8->field_0x0 != 0x53 || field_0x8->field_0x1 != 0x43) { - field_0x8 = NULL; + mHeader = (const JAUSeqCollectionData*)param_0; + if (mHeader->mMagic1 != 0x53 || mHeader->mMagic2 != 0x43) { + mHeader = NULL; return; } - field_0x0 = field_0x8->field_0x2; - field_0xc = field_0x8->field_0x4; - field_0x4 = &field_0x8->field_0x8; + mNumSoundCategories = mHeader->mNumSoundCategories; + mSectionSize = mHeader->mSectionSize; + mTableOffsets = mHeader->mTableOffsets; } bool JAUSeqCollection::getSeqData(int param_0, int param_1, JAISeqData* param_2) { - if (param_0 >= field_0x0) { + if (param_0 >= mNumSoundCategories) { return false; } - u32 r29 = field_0x4[param_0]; - BE(u32)* puVar2 = (BE(u32)*)((u8*)field_0x8 + r29); + u32 r29 = mTableOffsets[param_0]; + BE(u32)* puVar2 = (BE(u32)*)((u8*)mHeader + r29); if (param_1 >= puVar2[0]) { return false; } - param_2->set((void*)field_0x8, puVar2[param_1 + 1]); + param_2->set((void*)mHeader, puVar2[param_1 + 1]); return true; } bool JAUSeqCollection::getSeqDataRegion(JAISeqDataRegion* param_0) { if (isValid()) { - param_0->addr = (u8*)field_0x8; - param_0->size = field_0xc; + param_0->addr = (u8*)mHeader; + param_0->size = mSectionSize; return true; } return false; diff --git a/libs/JSystem/src/JAudio2/JAUSeqDataBlockMgr.cpp b/libs/JSystem/src/JAudio2/JAUSeqDataBlockMgr.cpp index 2fafe9a4bc..698c4aff65 100644 --- a/libs/JSystem/src/JAudio2/JAUSeqDataBlockMgr.cpp +++ b/libs/JSystem/src/JAudio2/JAUSeqDataBlockMgr.cpp @@ -87,8 +87,8 @@ s32 JAUDynamicSeqDataBlocks::getSeqData(JAISoundID param_0, JAISeqDataUser* para u8* seqData = mLoadedBlocks.getSeqData(param_0); if (seqData != NULL) { - param_2->field_0x0 = seqData; - param_2->field_0x4 = 0; + param_2->mBase = seqData; + param_2->mOffset = 0; return 2; } From 2c84387ec9c9041ad1732d361eb99d2834ced518 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Sun, 15 Mar 2026 01:32:13 +0100 Subject: [PATCH 45/94] Implement DSP pause somewhat --- src/dusk/audio/DuskDsp.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/dusk/audio/DuskDsp.cpp b/src/dusk/audio/DuskDsp.cpp index 5b4c5d0730..80e7ffe028 100644 --- a/src/dusk/audio/DuskDsp.cpp +++ b/src/dusk/audio/DuskDsp.cpp @@ -92,6 +92,12 @@ void dusk::audio::DspRender(DspSubframe& subframe) { continue; } + if (channel.mPauseFlag) { + // Not really sure what the practical difference between pause and + // deactivation is. Either avoids clearing state or allows the DSP to avoid popping? + continue; + } + ValidateChannel(channel); DspSubframe channelSubframe = {}; From df7d08a7f732f1f0a3199aac8b5afece3ae03d47 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Sun, 15 Mar 2026 17:56:26 +0100 Subject: [PATCH 46/94] Un-stub audio wave loading code guh --- src/Z2AudioLib/Z2SceneMgr.cpp | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/Z2AudioLib/Z2SceneMgr.cpp b/src/Z2AudioLib/Z2SceneMgr.cpp index 776a0c2d69..9e3bf2c91c 100644 --- a/src/Z2AudioLib/Z2SceneMgr.cpp +++ b/src/Z2AudioLib/Z2SceneMgr.cpp @@ -2096,10 +2096,6 @@ bool Z2SceneMgr::loadSceneWave(u32 wave, u32 bank) { #endif bool Z2SceneMgr::loadSeWave(u32 wave) { - return 0; -} - -/* //lw stub lw JAUSectionHeap* sectionHeap = JASGlobalInstance::getInstance(); JUT_ASSERT(3030, sectionHeap); @@ -2114,13 +2110,8 @@ bool Z2SceneMgr::loadSeWave(u32 wave) { JUT_WARN_DEVICE(3038, 1, "Z2SceneMgr::cannot load SE wave:%d\n", wave); return false; } -*/ + bool Z2SceneMgr::loadBgmWave(u32 wave) { - return true; -} - - /* //lw stub lw - JAUSectionHeap* sectionHeap = JASGlobalInstance::getInstance(); JUT_ASSERT(3047, sectionHeap); @@ -2134,4 +2125,4 @@ bool Z2SceneMgr::loadBgmWave(u32 wave) { JUT_WARN_DEVICE(3055, 1, "Z2SceneMgr::cannot load BGM wave:%d\n", wave); return false; -} */ +} From 4307f9149f0cae26b705f5260d52ecbd095e3d28 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Sun, 15 Mar 2026 17:57:44 +0100 Subject: [PATCH 47/94] More audio debug imgui that idk how useful it is --- src/dusk/imgui/ImGuiAudio.cpp | 130 ++++++++++++++++++++++++++++++---- 1 file changed, 118 insertions(+), 12 deletions(-) diff --git a/src/dusk/imgui/ImGuiAudio.cpp b/src/dusk/imgui/ImGuiAudio.cpp index 4dfd64fe9c..56976bce55 100644 --- a/src/dusk/imgui/ImGuiAudio.cpp +++ b/src/dusk/imgui/ImGuiAudio.cpp @@ -1,7 +1,106 @@ #include "ImGuiConsole.hpp" #include "ImGuiMenuTools.hpp" +#include "JSystem/JAudio2/JAISeMgr.h" +#include "JSystem/JAudio2/JAIStreamMgr.h" #include "JSystem/JAudio2/JASCriticalSection.h" #include "JSystem/JAudio2/JASDSPInterface.h" +#include "JSystem/JAudio2/JASTrack.h" + +static void ShowAllDspChannels() { + for (int i = 0; i < DSP_CHANNELS; i++) { + auto& channel = JASDsp::CH_BUF[i]; + if (!channel.mIsActive) { + continue; + } + + char buf[64]; + snprintf(buf, sizeof(buf), "%d", i); + + if (ImGui::BeginChild(buf, ImVec2(), ImGuiChildFlags_Border | ImGuiChildFlags_AutoResizeY)) { + ImGui::Text("[%02X]", i); + ImGui::TextUnformatted(channel.mLoopFlag ? "Loop: true" : "Loop: false"); + ImGui::Text("Format: %02X/%02X", channel.mSamplesPerBlock, channel.mBytesPerBlock); + ImGui::Text("Position: %08X/%08X", channel.mSamplePosition, channel.mEndSample); + ImGui::Text("Memory: %08X/%08X", channel.mWaveAramAddress, channel.mAramStreamPosition); + } + + ImGui::EndChild(); + } +} + +static void ShowAllTracks() { + if (ImGui::Button("Pause all")) { + for (auto& track : JASTrack::sTrackList) { + track.pause(true); + } + } + + for (auto& track : JASTrack::sTrackList) { + char buf[32]; + snprintf(buf, sizeof(buf), "%p", &track); + + if (ImGui::BeginChild(buf, ImVec2(), ImGuiChildFlags_Border | ImGuiChildFlags_AutoResizeY)) { + ImGui::Text("[%p]", &track); + bool paused = track.mFlags.pause; + ImGui::Checkbox("Paused", &paused); + track.mFlags.pause = paused; + + for (int i = 0; i < JASTrack::MAX_CHILDREN; i++) { + const auto child = track.getChild(i); + if (child != nullptr) { + ImGui::Text("child: [%p]", child); + } + } + } + + ImGui::EndChild(); + } +} + +static void ShowAllJAIStreams() { + auto& mgr = *JAIStreamMgr::getInstance(); + + for (auto streamLink = mgr.getStreamList()->getFirst(); streamLink != nullptr; streamLink = streamLink->getNext()) { + auto& stream = *streamLink->getObject(); + char buf[32]; + snprintf(buf, sizeof(buf), "%p", &stream); + + if (ImGui::BeginChild(buf, ImVec2(), ImGuiChildFlags_Border | ImGuiChildFlags_AutoResizeY)) { + ImGui::Text("[%p]", &stream); + bool paused = stream.status_.field_0x0.flags.paused; + ImGui::Checkbox("Paused", &paused); + stream.status_.field_0x0.flags.paused = paused; + } + + ImGui::EndChild(); + } +} + +static void ShowAllJAISes() { + auto& mgr = *JAISeMgr::getInstance(); + + for (int i = 0; i < JAISeMgr::NUM_CATEGORIES; i++) { + const auto category = mgr.getCategory(i); + + char buf[32]; + snprintf(buf, sizeof(buf), "%i", i); + + if (ImGui::BeginChild(buf, ImVec2(), ImGuiChildFlags_Border | ImGuiChildFlags_AutoResizeY)) { + ImGui::Text("Category: %i", i); + if (ImGui::Button("Pause All")) { + category->pause(true); + } + + for (auto seLink = category->getSeList()->getFirst(); seLink != nullptr; seLink = seLink->getNext()) { + const auto se = seLink->getObject(); + ImGui::Text("[%p]", se); + ImGui::Text(se->status_.field_0x0.flags.paused ? "Paused" : "Not paused"); + } + } + + ImGui::EndChild(); + } +} void dusk::ImGuiMenuTools::ShowAudioDebug() { if (!ImGuiConsole::CheckMenuViewToggle(ImGuiKey_F7, m_showAudioDebug)) { @@ -15,22 +114,29 @@ void dusk::ImGuiMenuTools::ShowAudioDebug() { { JASCriticalSection cs; - for (int i = 0; i < DSP_CHANNELS; i++) { - auto& channel = JASDsp::CH_BUF[i]; - if (!channel.mIsActive) { - continue; + + if (ImGui::BeginTabBar("Tabs")) { + if (ImGui::BeginTabItem("DSP channels")) { + ShowAllDspChannels(); + ImGui::EndTabItem(); } - char buf[64]; - snprintf(buf, sizeof(buf), "%d", i); - - if (ImGui::BeginChild(buf, ImVec2(), ImGuiChildFlags_Border | ImGuiChildFlags_AutoResizeY)) { - ImGui::Text("[%02X]", i); - ImGui::TextUnformatted(channel.mLoopFlag ? "Loop: true" : "Loop: false"); - ImGui::Text("Format: %02X/%02X", channel.mSamplesPerBlock, channel.mBytesPerBlock); + if (ImGui::BeginTabItem("JAITrack")) { + ShowAllTracks(); + ImGui::EndTabItem(); } - ImGui::EndChild(); + if (ImGui::BeginTabItem("JAIStream")) { + ShowAllJAIStreams(); + ImGui::EndTabItem(); + } + + if (ImGui::BeginTabItem("JAISe")) { + ShowAllJAISes(); + ImGui::EndTabItem(); + } + + ImGui::EndTabBar(); } } From 96dc827011007d0b3ea8709bd14eb12add114b04 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Sun, 15 Mar 2026 17:59:11 +0100 Subject: [PATCH 48/94] More minor naming --- libs/JSystem/src/JAudio2/JASSeqParser.cpp | 39 +++++++++++++++-------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/libs/JSystem/src/JAudio2/JASSeqParser.cpp b/libs/JSystem/src/JAudio2/JASSeqParser.cpp index 21cb551cb0..1c80ea52c3 100644 --- a/libs/JSystem/src/JAudio2/JASSeqParser.cpp +++ b/libs/JSystem/src/JAudio2/JASSeqParser.cpp @@ -937,6 +937,9 @@ s32 JASSeqParser::parseNoteOff(JASTrack* param_0, u8 param_1) { s32 JASSeqParser::parseNoteOn(JASTrack* param_0, u8 param_1) { JASSeqCtrl* seqCtrl = param_0->getSeqCtrl(); + if (seqCtrl->trace) { + printf("Guh\n"); + } u32 r28 = seqCtrl->readByte(); u32 r30 = r28; r30 &= 7; @@ -950,7 +953,7 @@ s32 JASSeqParser::parseNoteOn(JASTrack* param_0, u8 param_1) { return 0; } -s32 JASSeqParser::parseCommand(JASTrack* param_0, u8 cmd, u16 param_2) { +s32 JASSeqParser::parseCommand(JASTrack* param_0, u8 cmd, u16 parameterTypesOverride) { JASSeqCtrl* seqCtrl = param_0->getSeqCtrl(); CmdInfo* cmdInfo = NULL; if (cmd != 0xb0) { @@ -959,32 +962,32 @@ s32 JASSeqParser::parseCommand(JASTrack* param_0, u8 cmd, u16 param_2) { } else { cmdInfo = &sExtCmdInfo[seqCtrl->readByte() & 0xff]; } - u16 r28 = (u16)cmdInfo->mParameterTypes; - r28 |= param_2; - u32 stack_28[8]; - for (int i = 0; i < cmdInfo->mParameterCount; i++, r28 >>= 2) { - int r27 = 0; - switch (r28 & 3) { + u16 parameterTypes = (u16)cmdInfo->mParameterTypes; + parameterTypes |= parameterTypesOverride; + u32 args[8]; + for (int i = 0; i < cmdInfo->mParameterCount; i++, parameterTypes >>= 2) { + int value = 0; + switch (parameterTypes & 3) { case 0: - r27 = (u8)seqCtrl->readByte(); + value = (u8)seqCtrl->readByte(); break; case 1: - r27 = (u16)seqCtrl->read16(); + value = (u16)seqCtrl->read16(); break; case 2: - r27 = seqCtrl->read24(); + value = seqCtrl->read24(); break; case 3: - r27 = readReg(param_0, (u8)seqCtrl->readByte()); + value = readReg(param_0, (u8)seqCtrl->readByte()); break; } - stack_28[i] = r27; + args[i] = value; } s32 (JASSeqParser::*ptr)(JASTrack*, u32*) = cmdInfo->mHandler; if (!ptr) { return 0; } - return execCommand(param_0, ptr, cmdInfo->mParameterCount, stack_28); + return execCommand(param_0, ptr, cmdInfo->mParameterCount, args); } s32 JASSeqParser::parseRegCommand(JASTrack* param_0, int param_1) { @@ -1004,7 +1007,15 @@ s32 JASSeqParser::parseRegCommand(JASTrack* param_0, int param_1) { } s32 JASSeqParser::parse(JASTrack* param_0) { - u32 r31 = param_0->getSeqCtrl()->readByte(); + JASSeqCtrl* ctrl = param_0->getSeqCtrl(); + u32 base = ctrl->mReader.mCurPos - ctrl->mReader.mBase; + u32 r31 = ctrl->readByte(); + + if (ctrl->trace) { + OSReportForceEnableOn(); + OSReport("%06X: %02X\n", base, r31); + fflush(stdout); + } s32 r30 = 0; if ((r31 & 0x80) == 0) { r30 = parseNoteOn(param_0, r31); From 8b63858d9dfc381d23ccbb98937b7eb00d88b067 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Sun, 15 Mar 2026 18:41:48 +0100 Subject: [PATCH 49/94] Oops --- libs/JSystem/src/JAudio2/JASSeqParser.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/libs/JSystem/src/JAudio2/JASSeqParser.cpp b/libs/JSystem/src/JAudio2/JASSeqParser.cpp index 1c80ea52c3..05e63acc05 100644 --- a/libs/JSystem/src/JAudio2/JASSeqParser.cpp +++ b/libs/JSystem/src/JAudio2/JASSeqParser.cpp @@ -937,9 +937,6 @@ s32 JASSeqParser::parseNoteOff(JASTrack* param_0, u8 param_1) { s32 JASSeqParser::parseNoteOn(JASTrack* param_0, u8 param_1) { JASSeqCtrl* seqCtrl = param_0->getSeqCtrl(); - if (seqCtrl->trace) { - printf("Guh\n"); - } u32 r28 = seqCtrl->readByte(); u32 r30 = r28; r30 &= 7; @@ -1010,12 +1007,6 @@ s32 JASSeqParser::parse(JASTrack* param_0) { JASSeqCtrl* ctrl = param_0->getSeqCtrl(); u32 base = ctrl->mReader.mCurPos - ctrl->mReader.mBase; u32 r31 = ctrl->readByte(); - - if (ctrl->trace) { - OSReportForceEnableOn(); - OSReport("%06X: %02X\n", base, r31); - fflush(stdout); - } s32 r30 = 0; if ((r31 & 0x80) == 0) { r30 = parseNoteOn(param_0, r31); From 07892dbe28071fa836b540a9ae55420c69dc6ca6 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Sun, 15 Mar 2026 20:35:06 +0100 Subject: [PATCH 50/94] Allow decoding less PCM samples than a full frame --- src/dusk/audio/Adpcm.cpp | 5 +++-- src/dusk/audio/DuskDsp.cpp | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/dusk/audio/Adpcm.cpp b/src/dusk/audio/Adpcm.cpp index f4318d5d59..67b9ac9fd3 100644 --- a/src/dusk/audio/Adpcm.cpp +++ b/src/dusk/audio/Adpcm.cpp @@ -33,8 +33,7 @@ static s16 Clamp16(s32 value) { void dusk::audio::Adpcm4ToPcm16(const u8* adpcm, size_t adpcmLength, s16* pcm, size_t pcmLength, s16& hist2, s16& hist1) { assert (adpcmLength % AdpcmFrameSize == 0 && "ADPCM must be divisible by frame size"); - const auto expectedPcmLength = (adpcmLength / AdpcmFrameSize) * 16; - assert (expectedPcmLength <= pcmLength && "PCM output buffer is too small!"); + auto endPtr = pcm + pcmLength; for (int i = 0; i < adpcmLength; i += AdpcmFrameSize) { u8 header = adpcm[i]; @@ -55,6 +54,8 @@ void dusk::audio::Adpcm4ToPcm16(const u8* adpcm, size_t adpcmLength, s16* pcm, s hist1 = sample; *pcm++ = sample; + if (endPtr == pcm) + return; } } } diff --git a/src/dusk/audio/DuskDsp.cpp b/src/dusk/audio/DuskDsp.cpp index 80e7ffe028..a1fe8545dd 100644 --- a/src/dusk/audio/DuskDsp.cpp +++ b/src/dusk/audio/DuskDsp.cpp @@ -46,7 +46,9 @@ static u32 ConvertDataLengthToSamples(const JASDsp::TChannel& channel, u32 dataL static u32 ConvertSamplesToDataLength(const JASDsp::TChannel& channel, u32 samples) { if (samples % channel.mSamplesPerBlock != 0) { - CRASH("Indivisible sample count: %d\n", samples); + // Ensure we round up. + samples += channel.mSamplesPerBlock; + //CRASH("Indivisible sample count: %d\n", samples); } return (samples / channel.mSamplesPerBlock) * channel.mBytesPerBlock; From baf10b6b4ed9fed3c8751e51823d8bacd787515c Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Sun, 15 Mar 2026 20:35:30 +0100 Subject: [PATCH 51/94] Basic volume control, no panning yet --- src/dusk/audio/DuskDsp.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/dusk/audio/DuskDsp.cpp b/src/dusk/audio/DuskDsp.cpp index a1fe8545dd..3692834b13 100644 --- a/src/dusk/audio/DuskDsp.cpp +++ b/src/dusk/audio/DuskDsp.cpp @@ -7,6 +7,7 @@ #include #include "Adpcm.hpp" +#include "JSystem/JAudio2/JASDriverIF.h" #include "global.h" using namespace dusk::audio; @@ -187,6 +188,16 @@ static void RenderChannel( channelAux.resampleStream, subframe.data(), static_cast(subframe.size() * sizeof(s16))); + + for (auto& sample : subframe) { + u16 volume; + if (channel.mAutoMixerBeenSet) { + volume = channel.mAutoMixerVolume; + } else { + volume = channel.mOutputChannels[0].mTargetVolume; + } + sample = (s16)((s64)sample * volume / JASDriver::getChannelLevel_dsp()); + } } void dusk::audio::DspInit() { From 3d908545793202b9314cdeaeb977c900c2d86de0 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Sun, 15 Mar 2026 20:36:04 +0100 Subject: [PATCH 52/94] Ignore oscillator channels Idk if these show up yet but they did when the BMS stuff was broken --- src/dusk/audio/DuskDsp.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/dusk/audio/DuskDsp.cpp b/src/dusk/audio/DuskDsp.cpp index 3692834b13..25bc822e9f 100644 --- a/src/dusk/audio/DuskDsp.cpp +++ b/src/dusk/audio/DuskDsp.cpp @@ -101,6 +101,12 @@ void dusk::audio::DspRender(DspSubframe& subframe) { continue; } + if (channel.mBytesPerBlock == 0) { + // I think these are oscillator channels? Not backed by audio. + channel.mIsFinished = true; + continue; + } + ValidateChannel(channel); DspSubframe channelSubframe = {}; From 247b04379adc366df8a184da2b33a37d110a764a Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Mon, 16 Mar 2026 13:57:04 +0100 Subject: [PATCH 53/94] Output as F32 This completely fucks up audio. Pending debugging to figure out why --- src/dusk/audio/DuskAudioSystem.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/dusk/audio/DuskAudioSystem.cpp b/src/dusk/audio/DuskAudioSystem.cpp index 9b089eb807..ae23b995f3 100644 --- a/src/dusk/audio/DuskAudioSystem.cpp +++ b/src/dusk/audio/DuskAudioSystem.cpp @@ -33,7 +33,7 @@ static void InitSDL3Output() { SDL_Init(SDL_INIT_AUDIO); constexpr SDL_AudioSpec spec = { - SDL_AUDIO_S16, + SDL_AUDIO_F32, 1, SampleRate, }; @@ -68,6 +68,7 @@ void SDLCALL GetNewAudio( } static std::ofstream outRaw("guh.raw", std::ios_base::out | std::ios_base::binary); +static std::ofstream outRawF32("guh.f32.raw", std::ios_base::out | std::ios_base::binary); int RenderNewAudioFrame() { JASCriticalSection section; @@ -92,7 +93,14 @@ void RenderAudioSubframe() { JASDriver::updateDSP(); DspRender(subFrame); - outRaw.write((const char*)subFrame.data(), subFrame.size() * sizeof(s16)); + std::array guh = {}; + for (int i = 0; i < DSP_SUBFRAME_SIZE; i++) { + guh[i] = (f32)subFrame[i] / (f32)0x7FFF; + } - SDL_PutAudioStreamData(PlaybackStream, &subFrame, sizeof(subFrame)); + outRaw.write((const char*)subFrame.data(), sizeof(subFrame)); + outRawF32.write((const char*)guh.data(), sizeof(guh)); + + SDL_PutAudioStreamData(PlaybackStream, guh.data(), sizeof(guh)); + // SDL_PutAudioStreamData(PlaybackStream, subFrame.data(), sizeof(subFrame)); } From 0b9bcc7e08335cf072d70291724ea8788c738121 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Mon, 16 Mar 2026 14:27:27 +0100 Subject: [PATCH 54/94] Only mark channel as finished when resample buffer is empty Does not fix the audio corruption but necessary either way --- src/dusk/audio/DuskDsp.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/dusk/audio/DuskDsp.cpp b/src/dusk/audio/DuskDsp.cpp index 25bc822e9f..947b414bc3 100644 --- a/src/dusk/audio/DuskDsp.cpp +++ b/src/dusk/audio/DuskDsp.cpp @@ -125,6 +125,12 @@ static void SDLCALL ReadChannelSamples( auto& channel = JASDsp::CH_BUF[index]; auto& aux = ChannelAux[index]; + if (channel.mSamplesLeft == 0 && !channel.mLoopFlag) { + // May get called when we're out of data to read. + // This is expected, as we need to drain the resampler channel before we mark the channel as finished. + return; + } + additional_amount = ALIGN_NEXT(additional_amount, channel.mSamplesPerBlock); int requestedSize = static_cast(sizeof(s16) * additional_amount); @@ -154,8 +160,6 @@ static void SDLCALL ReadChannelSamples( if (channel.mSamplesLeft == 0) { // Reached end of buffer. if (!channel.mLoopFlag) { - // Finish. - channel.mIsFinished = true; return; } @@ -190,10 +194,15 @@ static void RenderChannel( ResetChannel(channel, channelAux); } - SDL_GetAudioStreamData( + int wantRead = static_cast(subframe.size() * sizeof(s16)); + auto read = SDL_GetAudioStreamData( channelAux.resampleStream, subframe.data(), - static_cast(subframe.size() * sizeof(s16))); + wantRead); + + if (read < wantRead) { + channel.mIsFinished = true; + } for (auto& sample : subframe) { u16 volume; From 0db942f82a21ba6b0732b63ecdf44deeebf73529 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Mon, 16 Mar 2026 15:44:02 +0100 Subject: [PATCH 55/94] Clang Windows compile fixes --- include/f_pc/f_pc_manager.h | 4 ++++ include/global.h | 7 +++++-- src/d/actor/d_a_alink.cpp | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/include/f_pc/f_pc_manager.h b/include/f_pc/f_pc_manager.h index f772949d85..d8d9cbe1c9 100644 --- a/include/f_pc/f_pc_manager.h +++ b/include/f_pc/f_pc_manager.h @@ -9,7 +9,11 @@ #include "f_pc/f_pc_stdcreate_req.h" #include "f_pc/f_pc_searcher.h" +#if TARGET_PC +enum : u32 { +#else enum { +#endif fpcM_UNK_PROCESS_ID_e = 0xFFFFFFFE, fpcM_ERROR_PROCESS_ID_e = 0xFFFFFFFF, }; diff --git a/include/global.h b/include/global.h index 94a2c9dc6e..36fb40dfb1 100644 --- a/include/global.h +++ b/include/global.h @@ -83,7 +83,11 @@ extern int __abs(int); void* __memcpy(void*, const void*, int); #endif -#ifdef _MSVC_LANG +#ifndef M_PI +#define M_PI 3.14159265358979323846f +#endif + +#if defined(_MSVC_LANG) && !defined(__clang__) inline int __builtin_clz(unsigned int v) { int count = 32; while (v != 0) { @@ -94,7 +98,6 @@ inline int __builtin_clz(unsigned int v) { } #define COMPOUND_LITERAL(x) -#define M_PI 3.14159265358979323846f #else #define COMPOUND_LITERAL(x) (x) diff --git a/src/d/actor/d_a_alink.cpp b/src/d/actor/d_a_alink.cpp index 93bfd80296..493d788a52 100644 --- a/src/d/actor/d_a_alink.cpp +++ b/src/d/actor/d_a_alink.cpp @@ -1467,7 +1467,7 @@ static f32 l_ladderAnmBaseTransY = 102.00054168701172f; static dCcD_SrcCyl l_cylSrc = { { - {0, {{AT_TYPE_WOLF_ATTACK, 3, 0x1A}, {0xD8FFFDFF, 5}, 0x73}}, + {0, {{(u32)AT_TYPE_WOLF_ATTACK, 3, 0x1A}, {0xD8FFFDFF, 5}, 0x73}}, {dCcD_SE_WOLF_BITE, 3, 1, 0, {1}}, {dCcD_SE_NONE, 6, 0, 0, {0}}, {0}, From a724d1b9ad9503a063a9ee04d49006ef8616b5ab Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Mon, 16 Mar 2026 15:44:26 +0100 Subject: [PATCH 56/94] Fix sprintf buffer too small in dMsgObject_c::readMessageGroupLocal --- src/d/d_msg_object.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/d/d_msg_object.cpp b/src/d/d_msg_object.cpp index 39c44b1470..37ca9cff28 100644 --- a/src/d/d_msg_object.cpp +++ b/src/d/d_msg_object.cpp @@ -1602,7 +1602,10 @@ u8 dMsgObject_c::isSend() { } void dMsgObject_c::readMessageGroupLocal(mDoDvdThd_mountXArchive_c** p_arcMount) { - static char arcName[22]; +#if TARGET_PC + static char arcName[32]; +#else +#endif int msgGroup = dStage_stagInfo_GetMsgGroup(dComIfGp_getStage()->getStagInfo()); #if REGION_PAL @@ -1625,7 +1628,12 @@ void dMsgObject_c::readMessageGroupLocal(mDoDvdThd_mountXArchive_c** p_arcMount) #elif REGION_JPN sprintf(arcName, "/res/Msgjp/bmgres%d.arc", msgGroup); #else +#if TARGET_PC + // Original game UB + snprintf(arcName, sizeof(arcName), "/res/Msgus/bmgres%d.arc", msgGroup); +#else sprintf(arcName, "/res/Msgus/bmgres%d.arc", msgGroup); +#endif #endif *p_arcMount = mDoDvdThd_mountXArchive_c::create(arcName, 0, JKRArchive::MOUNT_MEM, NULL); From 743e5b11d876f4ab66030677a13ad235afa4c397 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Mon, 16 Mar 2026 15:44:36 +0100 Subject: [PATCH 57/94] Fix NullTexData too small --- libs/JSystem/src/J3DGraphBase/J3DSys.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libs/JSystem/src/J3DGraphBase/J3DSys.cpp b/libs/JSystem/src/J3DGraphBase/J3DSys.cpp index c34ba546f0..2f9cf51a89 100644 --- a/libs/JSystem/src/J3DGraphBase/J3DSys.cpp +++ b/libs/JSystem/src/J3DGraphBase/J3DSys.cpp @@ -17,7 +17,11 @@ Vec J3DSys::mParentS; J3DTexCoordScaleInfo J3DSys::sTexCoordScaleTable[8]; +#if TARGET_PC // Original game bug, array is too small. +static u8 NullTexData[0x20] ATTRIBUTE_ALIGN(32) = {0}; +#else static u8 NullTexData[0x10] ATTRIBUTE_ALIGN(32) = {0}; +#endif static Mtx j3dIdentityMtx = { 1.0f, 0.0f, 0.0f, 0.0f, From 1e1dc8d88e7f2a0d4f3e984c8cd22a215621947e Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Mon, 16 Mar 2026 15:48:09 +0100 Subject: [PATCH 58/94] dStage_startStage_c::set fix strcpy to same pointer And just switch to strncpy_s while we're at it. --- src/d/d_stage.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/d/d_stage.cpp b/src/d/d_stage.cpp index 1f6186112b..87fbd9e40b 100644 --- a/src/d/d_stage.cpp +++ b/src/d/d_stage.cpp @@ -147,7 +147,14 @@ static int dStage_RoomKeepDoorInit(dStage_dt_c* i_stage, void* i_data, int entry } void dStage_startStage_c::set(const char* i_Name, s8 i_RoomNo, s16 i_Point, s8 i_Layer) { +#if TARGET_PC + // UB fix. + if (mName != i_Name) { + strncpy_s(mName, sizeof(mName), i_Name, sizeof(mName)); + } +#else strcpy(mName, i_Name); +#endif mRoomNo = i_RoomNo; mPoint = i_Point; mLayer = i_Layer; From b383fad1addf93b1e077b2f9f3bac2017c09a296 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Mon, 16 Mar 2026 15:53:15 +0100 Subject: [PATCH 59/94] Fix fopAcM_cullingCheck use after scope --- src/f_op/f_op_actor_mng.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/f_op/f_op_actor_mng.cpp b/src/f_op/f_op_actor_mng.cpp index fa4da69e68..0f5c47df37 100644 --- a/src/f_op/f_op_actor_mng.cpp +++ b/src/f_op/f_op_actor_mng.cpp @@ -1020,10 +1020,15 @@ cull_sphere l_cullSizeSphere[fopAc_CULLSPHERE_MAX_e] = { s32 fopAcM_cullingCheck(fopAc_ac_c const* i_actor) { MtxP mtx_p; +#if AVOID_UB + Mtx concat_mtx; +#endif if (fopAcM_GetMtx(i_actor) == NULL) { mtx_p = j3dSys.getViewMtx(); } else { +#if !AVOID_UB Mtx concat_mtx; +#endif cMtx_concat(j3dSys.getViewMtx(), fopAcM_GetMtx(i_actor), concat_mtx); mtx_p = concat_mtx; } From fae09942a4ab9271c7b6ea188f89e8218d8cf3af Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Mon, 16 Mar 2026 16:48:11 +0100 Subject: [PATCH 60/94] Fix setBusConnect OOB for "dolby mode" channels --- libs/JSystem/src/JAudio2/JASDSPInterface.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/libs/JSystem/src/JAudio2/JASDSPInterface.cpp b/libs/JSystem/src/JAudio2/JASDSPInterface.cpp index 00c7a66539..49ca691011 100644 --- a/libs/JSystem/src/JAudio2/JASDSPInterface.cpp +++ b/libs/JSystem/src/JAudio2/JASDSPInterface.cpp @@ -704,6 +704,15 @@ void JASDsp::TChannel::setDistFilter(s16 param_0) { void JASDsp::TChannel::setBusConnect(u8 outputChannel, u8 param_1) { JUT_ASSERT(973, dspMutex); OutputChannelConfig& tmp = mOutputChannels[outputChannel]; +#if AVOID_UB + if (param_1 == 255) { + // Seems to happen for "dolby mode" where the mix config is 0xFFFF. + // Probably UB without side effect in the base game as afaict the DSP + // doesn't look at mix config when "dolby mode" is active. + return; + } + JUT_ASSERT(0, param_1 < 12); +#endif static u16 const connect_table[12] = { 0x0000, 0x0D00, 0x0D60, 0x0DC0, 0x0E20, 0x0E80, 0x0EE0, 0x0CA0, 0x0F40, 0x0FA0, 0x0B00, 0x09A0, From 6f2b7301433987e683d2dec3a6816279f3636fde Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Mon, 16 Mar 2026 16:48:38 +0100 Subject: [PATCH 61/94] BE fix MixConfig --- libs/JSystem/include/JSystem/JAudio2/JASChannel.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/JSystem/include/JSystem/JAudio2/JASChannel.h b/libs/JSystem/include/JSystem/JAudio2/JASChannel.h index 89bc381575..ce9b0b20de 100644 --- a/libs/JSystem/include/JSystem/JAudio2/JASChannel.h +++ b/libs/JSystem/include/JSystem/JAudio2/JASChannel.h @@ -79,7 +79,7 @@ public: }; union MixConfig { - u16 whole; + BE(u16) whole; struct { u8 upper; u8 lower0 : 4; From 3bab77b59d9a2474bc0268597d0d582e360eb533 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Mon, 16 Mar 2026 16:48:45 +0100 Subject: [PATCH 62/94] Fix CUTOFF_TO_IIR_TABLE OOB --- libs/JSystem/include/JSystem/JAudio2/JASCalc.h | 4 ++++ libs/JSystem/src/JAudio2/JASCalc.cpp | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/libs/JSystem/include/JSystem/JAudio2/JASCalc.h b/libs/JSystem/include/JSystem/JAudio2/JASCalc.h index bb8054675f..35b8fd5516 100644 --- a/libs/JSystem/include/JSystem/JAudio2/JASCalc.h +++ b/libs/JSystem/include/JSystem/JAudio2/JASCalc.h @@ -33,7 +33,11 @@ struct JASCalc { f32 fake2(s32 x); f32 fake3(); +#if AVOID_UB + static const s16 CUTOFF_TO_IIR_TABLE[129][4]; +#else static const s16 CUTOFF_TO_IIR_TABLE[128][4]; +#endif }; template diff --git a/libs/JSystem/src/JAudio2/JASCalc.cpp b/libs/JSystem/src/JAudio2/JASCalc.cpp index be87a8aa4e..9ac209c96b 100644 --- a/libs/JSystem/src/JAudio2/JASCalc.cpp +++ b/libs/JSystem/src/JAudio2/JASCalc.cpp @@ -132,7 +132,11 @@ void JASCalc::bzero(void* dest, u32 size) { } } +#if AVOID_UB +s16 const JASCalc::CUTOFF_TO_IIR_TABLE[129][4] = { +#else s16 const JASCalc::CUTOFF_TO_IIR_TABLE[128][4] = { +#endif 0x0F5C, 0x0A3D, 0x4665, 0x1E73, 0x0F5E, 0x0A3D, 0x4664, 0x1E73, 0x0F63, 0x0A3C, 0x4661, 0x1E71, @@ -261,6 +265,10 @@ s16 const JASCalc::CUTOFF_TO_IIR_TABLE[128][4] = { 0x7C7A, 0x0052, 0x0233, 0x00F4, 0x7E3B, 0x0029, 0x011B, 0x007A, 0x7FFF, 0x0000, 0x0000, 0x0000, +#if AVOID_UB + // Game OOB reads this in some cases. + 0,0,0,0 +#endif }; // currently required because of missing functions From c2879e735c98dbaa2d7f5534b5961aae79d6940d Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Mon, 16 Mar 2026 23:35:57 +0100 Subject: [PATCH 63/94] Disable JASAiCtrl safety kill code Doesn't work properly and causes weird behavior --- libs/JSystem/src/JAudio2/JASAiCtrl.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libs/JSystem/src/JAudio2/JASAiCtrl.cpp b/libs/JSystem/src/JAudio2/JASAiCtrl.cpp index fd9446932d..1285d68359 100644 --- a/libs/JSystem/src/JAudio2/JASAiCtrl.cpp +++ b/libs/JSystem/src/JAudio2/JASAiCtrl.cpp @@ -152,6 +152,8 @@ void JASDriver::updateDSP() { JASPortCmd::execAllCommand(); DSPSyncCallback(); +#if !TARGET_PC + // Safety kill code? Our audio engine isn't consistent enough and hits this incorrectly. static u32 old_time = 0; static u32 history[10] = {0x000F4240}; u32 r28 = OSGetTick(); @@ -172,6 +174,7 @@ void JASDriver::updateDSP() { JASDSPChannel::killActiveChannel(); #endif } +#endif JASChannel::receiveBankDisposeMsg(); JASDSPChannel::updateAll(); From b6fc8019ce4441c2b30547034b2fb9719f949ffd Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Mon, 16 Mar 2026 23:37:57 +0100 Subject: [PATCH 64/94] More ImGuiAudio stuff --- src/dusk/imgui/ImGuiAudio.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/dusk/imgui/ImGuiAudio.cpp b/src/dusk/imgui/ImGuiAudio.cpp index 56976bce55..2c1f6d96ad 100644 --- a/src/dusk/imgui/ImGuiAudio.cpp +++ b/src/dusk/imgui/ImGuiAudio.cpp @@ -3,12 +3,23 @@ #include "JSystem/JAudio2/JAISeMgr.h" #include "JSystem/JAudio2/JAIStreamMgr.h" #include "JSystem/JAudio2/JASCriticalSection.h" +#include "JSystem/JAudio2/JASDSPChannel.h" #include "JSystem/JAudio2/JASDSPInterface.h" #include "JSystem/JAudio2/JASTrack.h" static void ShowAllDspChannels() { + int activeChannels = 0; + for (int i = 0; i < DSP_CHANNELS; i++) { + if (JASDsp::CH_BUF[i].mIsActive) { + activeChannels++; + } + } + + ImGui::Text("Active channels: %d", activeChannels); + for (int i = 0; i < DSP_CHANNELS; i++) { auto& channel = JASDsp::CH_BUF[i]; + auto& jasChannel = JASDSPChannel::sDspChannels[i]; if (!channel.mIsActive) { continue; } @@ -19,8 +30,11 @@ static void ShowAllDspChannels() { if (ImGui::BeginChild(buf, ImVec2(), ImGuiChildFlags_Border | ImGuiChildFlags_AutoResizeY)) { ImGui::Text("[%02X]", i); ImGui::TextUnformatted(channel.mLoopFlag ? "Loop: true" : "Loop: false"); + ImGui::SameLine(); + ImGui::Text("Priority: %hd", jasChannel.mPriority); ImGui::Text("Format: %02X/%02X", channel.mSamplesPerBlock, channel.mBytesPerBlock); ImGui::Text("Position: %08X/%08X", channel.mSamplePosition, channel.mEndSample); + ImGui::SameLine(); ImGui::Text("Memory: %08X/%08X", channel.mWaveAramAddress, channel.mAramStreamPosition); } From db47a6b9bdc07e212d34d4be4d248c97173e185a Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Tue, 17 Mar 2026 00:03:15 +0100 Subject: [PATCH 65/94] Clear history samples when resetting channel --- src/dusk/audio/DuskDsp.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/dusk/audio/DuskDsp.cpp b/src/dusk/audio/DuskDsp.cpp index 947b414bc3..3c5679e9df 100644 --- a/src/dusk/audio/DuskDsp.cpp +++ b/src/dusk/audio/DuskDsp.cpp @@ -60,7 +60,7 @@ static void RenderChannel( ChannelAuxData& channelAux, DspSubframe& subframe); -static void ResetChannel(JASDsp::TChannel& channel, const ChannelAuxData& aux) { +static void ResetChannel(JASDsp::TChannel& channel, ChannelAuxData& aux) { channel.mSamplesLeft = channel.mEndSample - channel.mSamplePosition; const SDL_AudioSpec spec = { @@ -69,6 +69,9 @@ static void ResetChannel(JASDsp::TChannel& channel, const ChannelAuxData& aux) { static_cast(static_cast(SampleRate) * channel.mPitch / 4096) }; + aux.hist0 = 0; + aux.hist1 = 0; + SDL_ClearAudioStream(aux.resampleStream); SDL_SetAudioStreamFormat(aux.resampleStream, &spec, nullptr); From 2688ed93f09732dfe3a718f56a4e34b4c8b55216 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Tue, 17 Mar 2026 00:03:35 +0100 Subject: [PATCH 66/94] Audio processing in f32 --- src/dusk/audio/DuskAudioSystem.cpp | 16 +++++++--------- src/dusk/audio/DuskDsp.cpp | 28 +++++++++++++++++----------- src/dusk/audio/DuskDsp.hpp | 2 +- 3 files changed, 25 insertions(+), 21 deletions(-) diff --git a/src/dusk/audio/DuskAudioSystem.cpp b/src/dusk/audio/DuskAudioSystem.cpp index ae23b995f3..1d625a8101 100644 --- a/src/dusk/audio/DuskAudioSystem.cpp +++ b/src/dusk/audio/DuskAudioSystem.cpp @@ -67,8 +67,9 @@ void SDLCALL GetNewAudio( } } +#if 0 static std::ofstream outRaw("guh.raw", std::ios_base::out | std::ios_base::binary); -static std::ofstream outRawF32("guh.f32.raw", std::ios_base::out | std::ios_base::binary); +#endif int RenderNewAudioFrame() { JASCriticalSection section; @@ -82,7 +83,9 @@ int RenderNewAudioFrame() { JASAudioThread::snIntCount -= 1; } +#if 0 outRaw.flush(); +#endif return static_cast(countSubframes) * DSP_SUBFRAME_SIZE; } @@ -93,14 +96,9 @@ void RenderAudioSubframe() { JASDriver::updateDSP(); DspRender(subFrame); - std::array guh = {}; - for (int i = 0; i < DSP_SUBFRAME_SIZE; i++) { - guh[i] = (f32)subFrame[i] / (f32)0x7FFF; - } - +#if 0 outRaw.write((const char*)subFrame.data(), sizeof(subFrame)); - outRawF32.write((const char*)guh.data(), sizeof(guh)); +#endif - SDL_PutAudioStreamData(PlaybackStream, guh.data(), sizeof(guh)); - // SDL_PutAudioStreamData(PlaybackStream, subFrame.data(), sizeof(subFrame)); + SDL_PutAudioStreamData(PlaybackStream, subFrame.data(), sizeof(subFrame)); } diff --git a/src/dusk/audio/DuskDsp.cpp b/src/dusk/audio/DuskDsp.cpp index 3c5679e9df..a901cc0cdc 100644 --- a/src/dusk/audio/DuskDsp.cpp +++ b/src/dusk/audio/DuskDsp.cpp @@ -80,7 +80,7 @@ static void ResetChannel(JASDsp::TChannel& channel, ChannelAuxData& aux) { static void MixSubframe(DspSubframe& dst, const DspSubframe& src) { for (int i = 0; i < dst.size(); i++) { - dst[i] = static_cast(dst[i] + src[i]); + dst[i] += src[i]; } } @@ -197,7 +197,7 @@ static void RenderChannel( ResetChannel(channel, channelAux); } - int wantRead = static_cast(subframe.size() * sizeof(s16)); + int wantRead = static_cast(subframe.size() * sizeof(subframe[0])); auto read = SDL_GetAudioStreamData( channelAux.resampleStream, subframe.data(), @@ -207,27 +207,33 @@ static void RenderChannel( channel.mIsFinished = true; } + u16 volume; + if (channel.mAutoMixerBeenSet) { + volume = channel.mAutoMixerVolume; + } else { + volume = channel.mOutputChannels[0].mTargetVolume; + } + f32 ratio = static_cast(volume) / static_cast(JASDriver::getChannelLevel_dsp()); for (auto& sample : subframe) { - u16 volume; - if (channel.mAutoMixerBeenSet) { - volume = channel.mAutoMixerVolume; - } else { - volume = channel.mOutputChannels[0].mTargetVolume; - } - sample = (s16)((s64)sample * volume / JASDriver::getChannelLevel_dsp()); + sample *= ratio; } } void dusk::audio::DspInit() { - constexpr SDL_AudioSpec spec = { + constexpr SDL_AudioSpec srcSpec = { SDL_AUDIO_S16, 1, SampleRate }; + constexpr SDL_AudioSpec dstSpec = { + SDL_AUDIO_F32, + 1, + SampleRate + }; for (int i = 0; i < DSP_CHANNELS; i++) { auto& aux = ChannelAux[i]; - aux.resampleStream = SDL_CreateAudioStream(&spec, &spec); + aux.resampleStream = SDL_CreateAudioStream(&srcSpec, &dstSpec); SDL_SetAudioStreamGetCallback( aux.resampleStream, diff --git a/src/dusk/audio/DuskDsp.hpp b/src/dusk/audio/DuskDsp.hpp index 186f7de677..76a4ef2e14 100644 --- a/src/dusk/audio/DuskDsp.hpp +++ b/src/dusk/audio/DuskDsp.hpp @@ -17,7 +17,7 @@ namespace dusk::audio { extern ChannelAuxData ChannelAux[DSP_CHANNELS]; - using DspSubframe = std::array; + using DspSubframe = std::array; void DspInit(); void DspRender(DspSubframe& subframe); From a2d00da978c37f6003a9e9d9cda2910ce70c9425 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Thu, 19 Mar 2026 16:04:18 +0100 Subject: [PATCH 67/94] More imgui stuff!!! --- libs/JSystem/src/JAudio2/JASChannel.cpp | 2 +- src/dusk/imgui/ImGuiAudio.cpp | 118 ++++++++++++++++++++---- 2 files changed, 99 insertions(+), 21 deletions(-) diff --git a/libs/JSystem/src/JAudio2/JASChannel.cpp b/libs/JSystem/src/JAudio2/JASChannel.cpp index ffd5056e1d..61fe80a098 100644 --- a/libs/JSystem/src/JAudio2/JASChannel.cpp +++ b/libs/JSystem/src/JAudio2/JASChannel.cpp @@ -470,7 +470,7 @@ void JASChannel::updateMixer(f32 i_volume, f32 i_pan, f32 i_fxmix, f32 i_dolby, case 6: volume *= scale; break; - default: + default: // 0, 1, 4, 5, 8-15 if (JASDriver::getOutputMode() == JAS_OUTPUT_MONO) { volume *= scale; } else { diff --git a/src/dusk/imgui/ImGuiAudio.cpp b/src/dusk/imgui/ImGuiAudio.cpp index 2c1f6d96ad..07caf9f0c0 100644 --- a/src/dusk/imgui/ImGuiAudio.cpp +++ b/src/dusk/imgui/ImGuiAudio.cpp @@ -1,12 +1,81 @@ #include "ImGuiConsole.hpp" #include "ImGuiMenuTools.hpp" #include "JSystem/JAudio2/JAISeMgr.h" +#include "JSystem/JAudio2/JAISeqMgr.h" #include "JSystem/JAudio2/JAIStreamMgr.h" #include "JSystem/JAudio2/JASCriticalSection.h" #include "JSystem/JAudio2/JASDSPChannel.h" #include "JSystem/JAudio2/JASDSPInterface.h" +#include "JSystem/JAudio2/JASDriverIF.h" #include "JSystem/JAudio2/JASTrack.h" +static std::array channelSortIndices = {}; + +static bool sortUpdateCount = true; + +static void DisplayDspChannel(int i) { + auto& channel = JASDsp::CH_BUF[i]; + auto& jasChannel = JASDSPChannel::sDspChannels[i]; + if (!channel.mIsActive) { + return; + } + + char buf[64]; + snprintf(buf, sizeof(buf), "%d", i); + + if (ImGui::BeginChild(buf, ImVec2(), ImGuiChildFlags_Border | ImGuiChildFlags_AutoResizeY)) { + ImGui::Text("[%02X]", i); + ImGui::SameLine(); + ImGui::Text("Update count: %d", jasChannel.mUpdateCounter); + ImGui::TextUnformatted(channel.mLoopFlag ? "Loop: true" : "Loop: false"); + ImGui::SameLine(); + ImGui::Text("Priority: %hd", jasChannel.mPriority); + ImGui::Text("Format: %02X/%02X", channel.mSamplesPerBlock, channel.mBytesPerBlock); + ImGui::Text("Position: %08X/%08X", channel.mSamplePosition, channel.mEndSample); + ImGui::SameLine(); + ImGui::Text("Memory: %08X/%08X", channel.mWaveAramAddress, channel.mAramStreamPosition); + + if (channel.mAutoMixerBeenSet) { + auto pan = (channel.mAutoMixerPanDolby >> 8) / 127.5f; + auto dolby = (channel.mAutoMixerPanDolby & 0xFF) / 127.5f; + auto fxMix = (channel.mAutoMixerFxMix >> 8) / 127.5f; + auto volume = (channel.mAutoMixerVolume) / (f32) JASDriver::getChannelLevel_dsp(); + ImGui::Text( + "Auto mixer active (pan: %f, dolby: %f, fx: %f, volume: %f)", + pan, dolby, fxMix, volume); + } else { + ImGui::Text( + "Bus connect: %04X,%04X,%04X,%04X,%04X,%04X", + channel.mOutputChannels[0].mBusConnect, + channel.mOutputChannels[1].mBusConnect, + channel.mOutputChannels[2].mBusConnect, + channel.mOutputChannels[3].mBusConnect, + channel.mOutputChannels[4].mBusConnect, + channel.mOutputChannels[5].mBusConnect); + } + } + + ImGui::EndChild(); +} + +static void InitChannelSortIndices() { + for (int i = 0; i < channelSortIndices.size(); i++) { + channelSortIndices[i] = i; + } +} + +static void SortChannelsByUpdateCount() { + InitChannelSortIndices(); + std::ranges::sort( + channelSortIndices, + [](u8 a, u8 b) { + auto& jasChannelA = JASDSPChannel::sDspChannels[a]; + auto& jasChannelB = JASDSPChannel::sDspChannels[b]; + + return jasChannelA.mUpdateCounter > jasChannelB.mUpdateCounter; + }); +} + static void ShowAllDspChannels() { int activeChannels = 0; for (int i = 0; i < DSP_CHANNELS; i++) { @@ -16,29 +85,17 @@ static void ShowAllDspChannels() { } ImGui::Text("Active channels: %d", activeChannels); + ImGui::Checkbox("Sort by update count", &sortUpdateCount); - for (int i = 0; i < DSP_CHANNELS; i++) { - auto& channel = JASDsp::CH_BUF[i]; - auto& jasChannel = JASDSPChannel::sDspChannels[i]; - if (!channel.mIsActive) { - continue; + if (sortUpdateCount) { + SortChannelsByUpdateCount(); + for (u8 index : channelSortIndices) { + DisplayDspChannel(index); } - - char buf[64]; - snprintf(buf, sizeof(buf), "%d", i); - - if (ImGui::BeginChild(buf, ImVec2(), ImGuiChildFlags_Border | ImGuiChildFlags_AutoResizeY)) { - ImGui::Text("[%02X]", i); - ImGui::TextUnformatted(channel.mLoopFlag ? "Loop: true" : "Loop: false"); - ImGui::SameLine(); - ImGui::Text("Priority: %hd", jasChannel.mPriority); - ImGui::Text("Format: %02X/%02X", channel.mSamplesPerBlock, channel.mBytesPerBlock); - ImGui::Text("Position: %08X/%08X", channel.mSamplePosition, channel.mEndSample); - ImGui::SameLine(); - ImGui::Text("Memory: %08X/%08X", channel.mWaveAramAddress, channel.mAramStreamPosition); + } else { + for (int i = 0; i < DSP_CHANNELS; i++) { + DisplayDspChannel(i); } - - ImGui::EndChild(); } } @@ -58,6 +115,9 @@ static void ShowAllTracks() { bool paused = track.mFlags.pause; ImGui::Checkbox("Paused", &paused); track.mFlags.pause = paused; + bool muted = track.mFlags.mute; + ImGui::Checkbox("Muted", &muted); + track.mFlags.mute = muted; for (int i = 0; i < JASTrack::MAX_CHILDREN; i++) { const auto child = track.getChild(i); @@ -116,6 +176,19 @@ static void ShowAllJAISes() { } } + +static void ShowAllJAISeqs() { + auto& mgr = *JAISeqMgr::getInstance(); + + if (ImGui::Button("Pause")) { + mgr.pause(true); + } + ImGui::SameLine(); + if (ImGui::Button("Unpause")) { + mgr.pause(false); + } +} + void dusk::ImGuiMenuTools::ShowAudioDebug() { if (!ImGuiConsole::CheckMenuViewToggle(ImGuiKey_F7, m_showAudioDebug)) { return; @@ -150,6 +223,11 @@ void dusk::ImGuiMenuTools::ShowAudioDebug() { ImGui::EndTabItem(); } + if (ImGui::BeginTabItem("JAISeq")) { + ShowAllJAISeqs(); + ImGui::EndTabItem(); + } + ImGui::EndTabBar(); } } From c8866854c3d5ce28bdd116588e6fc2c7a206a978 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Thu, 19 Mar 2026 17:47:11 +0100 Subject: [PATCH 68/94] Stereo audio! --- src/dusk/audio/DuskAudioSystem.cpp | 26 ++++-- src/dusk/audio/DuskDsp.cpp | 126 ++++++++++++++++++++++++----- src/dusk/audio/DuskDsp.hpp | 22 ++++- 3 files changed, 147 insertions(+), 27 deletions(-) diff --git a/src/dusk/audio/DuskAudioSystem.cpp b/src/dusk/audio/DuskAudioSystem.cpp index 1d625a8101..3becb76cfa 100644 --- a/src/dusk/audio/DuskAudioSystem.cpp +++ b/src/dusk/audio/DuskAudioSystem.cpp @@ -2,8 +2,10 @@ #include #include +#include #include #include +#include #include "JSystem/JAudio2/JASAiCtrl.h" #include "JSystem/JAudio2/JASChannel.h" @@ -16,7 +18,8 @@ using namespace dusk::audio; -static DspSubframe AllSubframeBuffers[DSP_OUTPUT_CHANNELS]; +static OutputSubframe OutBuffer; +static std::array OutInterleaveBuffer; static SDL_AudioStream* PlaybackStream; @@ -34,7 +37,7 @@ static void InitSDL3Output() { constexpr SDL_AudioSpec spec = { SDL_AUDIO_F32, - 1, + 2, SampleRate, }; PlaybackStream = SDL_OpenAudioDeviceStream( @@ -90,15 +93,28 @@ int RenderNewAudioFrame() { return static_cast(countSubframes) * DSP_SUBFRAME_SIZE; } +static void InterleaveOutputData(const OutputSubframe& data, std::span target) { + assert(target.size() >= data.channels[0].size() * OutputSubframe::NUM_CHANNELS); + + size_t outPos = 0; + for (size_t inPos = 0; inPos < data.channels[0].size(); inPos++) { + for (size_t channelIdx = 0; channelIdx < OutputSubframe::NUM_CHANNELS; channelIdx++) { + target[outPos++] = data.channels[channelIdx][inPos]; + } + } +} + void RenderAudioSubframe() { - DspSubframe& subFrame = AllSubframeBuffers[0]; + OutBuffer = {}; JASDriver::updateDSP(); - DspRender(subFrame); + DspRender(OutBuffer); #if 0 outRaw.write((const char*)subFrame.data(), sizeof(subFrame)); #endif - SDL_PutAudioStreamData(PlaybackStream, subFrame.data(), sizeof(subFrame)); + InterleaveOutputData(OutBuffer, OutInterleaveBuffer); + + SDL_PutAudioStreamData(PlaybackStream, &OutInterleaveBuffer, sizeof(OutInterleaveBuffer)); } diff --git a/src/dusk/audio/DuskDsp.cpp b/src/dusk/audio/DuskDsp.cpp index a901cc0cdc..a042b0aa88 100644 --- a/src/dusk/audio/DuskDsp.cpp +++ b/src/dusk/audio/DuskDsp.cpp @@ -5,6 +5,7 @@ #include #include +#include #include "Adpcm.hpp" #include "JSystem/JAudio2/JASDriverIF.h" @@ -58,7 +59,7 @@ static u32 ConvertSamplesToDataLength(const JASDsp::TChannel& channel, u32 sampl static void RenderChannel( JASDsp::TChannel& channel, ChannelAuxData& channelAux, - DspSubframe& subframe); + OutputSubframe& subframe); static void ResetChannel(JASDsp::TChannel& channel, ChannelAuxData& aux) { channel.mSamplesLeft = channel.mEndSample - channel.mSamplePosition; @@ -84,9 +85,7 @@ static void MixSubframe(DspSubframe& dst, const DspSubframe& src) { } } -void dusk::audio::DspRender(DspSubframe& subframe) { - subframe.fill(0); - +void dusk::audio::DspRender(OutputSubframe& subframe) { // This cast half exists because my debugger sucks and this is an easy way to look at the data. auto& channels = *reinterpret_cast*>(JASDsp::CH_BUF); @@ -112,9 +111,12 @@ void dusk::audio::DspRender(DspSubframe& subframe) { ValidateChannel(channel); - DspSubframe channelSubframe = {}; + OutputSubframe channelSubframe = {}; RenderChannel(channel, channelAux, channelSubframe); - MixSubframe(subframe, channelSubframe); + + for (int o = 0; o < subframe.channels.size(); o++) { + MixSubframe(subframe.channels[o], channelSubframe.channels[o]); + } } } @@ -189,34 +191,116 @@ static void SDLCALL ReadChannelSamples( SDL_PutAudioStreamData(stream, requested, requestedSize); } +constexpr u16 GetBusConnect(const OutputChannel channel) { + switch (channel) { + // TODO: This is a guess for now. + case OutputChannel::LEFT: + return 0x0D00; + case OutputChannel::RIGHT: + return 0x0D60; + default: + CRASH("Invalid output channel!"); + } +} + +static const JASDsp::OutputChannelConfig* GetOutputConfig( + const JASDsp::TChannel& sourceChannel, + OutputChannel channel) { + + auto busConnect = GetBusConnect(channel); + for (const auto& mOutputChannel : sourceChannel.mOutputChannels) { + auto config = &mOutputChannel; + if (config->mBusConnect == busConnect) { + return config; + } + } + + return nullptr; +} + +static f32 GetVolumeForOutputChannel( + const JASDsp::TChannel& sourceChannel, + OutputChannel outputChannel) { + + u16 volume; + f32 panValue = 1; + if (sourceChannel.mAutoMixerBeenSet) { + volume = sourceChannel.mAutoMixerVolume; + + auto autoMixerPan = static_cast(sourceChannel.mAutoMixerPanDolby >> 8) / 127; + + switch (outputChannel) { + case OutputChannel::LEFT: + panValue = 1 - autoMixerPan; + break; + case OutputChannel::RIGHT: + panValue = autoMixerPan; + break; + default: + CRASH("Unhandled output channel: OutputChannel"); + } + + } else { + auto config = GetOutputConfig(sourceChannel, outputChannel); + if (config == nullptr) { + return 0; + } + + volume = config->mTargetVolume; + } + + // TODO: interpolate to avoid popping. + f32 ratio = static_cast(volume) / static_cast(JASDriver::getChannelLevel_dsp()); + ratio *= panValue; + + return ratio; +} + +static void RenderOutputChannel( + const JASDsp::TChannel& sourceChannel, + OutputChannel outputChannel, + const std::span inputSamples, + OutputSubframe& fullOutputSubframe) { + + auto& outputSubframe = fullOutputSubframe[outputChannel]; + assert(inputSamples.size() <= outputSubframe.size()); + + auto volume = GetVolumeForOutputChannel(sourceChannel, outputChannel); + if (volume == 0) { + return; + } + + for (int i = 0; i < inputSamples.size(); i++) { + outputSubframe[i] = inputSamples[i] * volume; + } +} + static void RenderChannel( JASDsp::TChannel& channel, ChannelAuxData& channelAux, - DspSubframe& subframe) { + OutputSubframe& subframe) { if (channel.mResetFlag) { ResetChannel(channel, channelAux); } - int wantRead = static_cast(subframe.size() * sizeof(subframe[0])); + DspSubframe audioLoadBuffer = {}; + + int wantRead = sizeof(audioLoadBuffer); auto read = SDL_GetAudioStreamData( channelAux.resampleStream, - subframe.data(), + &audioLoadBuffer, wantRead); if (read < wantRead) { channel.mIsFinished = true; } - u16 volume; - if (channel.mAutoMixerBeenSet) { - volume = channel.mAutoMixerVolume; - } else { - volume = channel.mOutputChannels[0].mTargetVolume; - } - f32 ratio = static_cast(volume) / static_cast(JASDriver::getChannelLevel_dsp()); - for (auto& sample : subframe) { - sample *= ratio; - } + auto hasReadSamples = std::span(audioLoadBuffer).subspan(0, wantRead / sizeof(f32)); + + static_assert(OutputSubframe::NUM_CHANNELS == 2, "Keep RenderChannel in sync!"); + + RenderOutputChannel(channel, OutputChannel::LEFT, hasReadSamples, subframe); + RenderOutputChannel(channel, OutputChannel::RIGHT, hasReadSamples, subframe); } void dusk::audio::DspInit() { @@ -231,13 +315,13 @@ void dusk::audio::DspInit() { SampleRate }; - for (int i = 0; i < DSP_CHANNELS; i++) { + for (u32 i = 0; i < DSP_CHANNELS; i++) { auto& aux = ChannelAux[i]; aux.resampleStream = SDL_CreateAudioStream(&srcSpec, &dstSpec); SDL_SetAudioStreamGetCallback( aux.resampleStream, ReadChannelSamples, - reinterpret_cast(i)); + reinterpret_cast(static_cast(i))); } } diff --git a/src/dusk/audio/DuskDsp.hpp b/src/dusk/audio/DuskDsp.hpp index 76a4ef2e14..0b47cf2570 100644 --- a/src/dusk/audio/DuskDsp.hpp +++ b/src/dusk/audio/DuskDsp.hpp @@ -3,12 +3,21 @@ #include "JSystem/JAudio2/JASDSPInterface.h" #include +#include #include "SDL3/SDL_audio.h" namespace dusk::audio { constexpr int SampleRate = 32000; + enum class OutputChannel : u8 { + LEFT, + RIGHT, + OutputChannel_MAX + }; + + constexpr + struct ChannelAuxData { s16 hist1; s16 hist0; @@ -19,6 +28,17 @@ namespace dusk::audio { using DspSubframe = std::array; + struct OutputSubframe { + static constexpr int NUM_CHANNELS = static_cast(OutputChannel::OutputChannel_MAX); + + std::array channels; + + DspSubframe& operator[](OutputChannel channel) { + assert(channel < OutputChannel::OutputChannel_MAX); + return channels[static_cast(channel)]; + } + }; + void DspInit(); - void DspRender(DspSubframe& subframe); + void DspRender(OutputSubframe& subframe); } From bc1e2cb31f27d8cc92581efd205226893d06ce3d Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Thu, 19 Mar 2026 20:05:47 +0100 Subject: [PATCH 69/94] PCM16 support Wanted this to test something, not sure if it's used in-game --- src/dusk/audio/DuskDsp.cpp | 51 +++++++++++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/src/dusk/audio/DuskDsp.cpp b/src/dusk/audio/DuskDsp.cpp index a042b0aa88..730a5fb856 100644 --- a/src/dusk/audio/DuskDsp.cpp +++ b/src/dusk/audio/DuskDsp.cpp @@ -18,13 +18,13 @@ ChannelAuxData dusk::audio::ChannelAux[DSP_CHANNELS] = {}; static bool ValidateChannelWaveFormat(const JASDsp::TChannel& channel) { if (channel.mSamplesPerBlock == AdpcmSampleCount && channel.mBytesPerBlock == Adpcm4FrameSize) return true; + if (channel.mSamplesPerBlock == 1 && channel.mBytesPerBlock == 16) + return true; /* if (channel.mSamplesPerBlock == AdpcmSampleCount && channel.mBytesPerBlock == Adpcm2FrameSize) return true; if (channel.mSamplesPerBlock == 1 && channel.mBytesPerBlock == 8) return true; - if (channel.mSamplesPerBlock == 1 && channel.mBytesPerBlock == 16) - return true; */ return false; } @@ -47,6 +47,16 @@ static u32 ConvertDataLengthToSamples(const JASDsp::TChannel& channel, u32 dataL } static u32 ConvertSamplesToDataLength(const JASDsp::TChannel& channel, u32 samples) { + if (channel.mSamplesPerBlock == 1) { + if (channel.mBytesPerBlock == 16) { + return samples * 2; + } + if (channel.mBytesPerBlock == 8) { + return samples; + } + CRASH("Unknown format"); + } + if (samples % channel.mSamplesPerBlock != 0) { // Ensure we round up. samples += channel.mSamplesPerBlock; @@ -120,6 +130,37 @@ void dusk::audio::DspRender(OutputSubframe& subframe) { } } +static void ReadSamplesCore( + const JASDsp::TChannel& channel, + const u8* data, + size_t dataLength, + s16* pcm, + size_t pcmLength, + s16& hist1, + s16& hist0) { + if (channel.mSamplesPerBlock == 1) { + if (channel.mBytesPerBlock == 0x10) { + // PCM16 + assert(reinterpret_cast(data) % 2 == 0 && "PCM data must be aligned"); + assert(dataLength % 2 == 0 && "Data length must be multiple of 2"); + assert(dataLength * 2 >= pcmLength && "Input too small!"); + + auto srcPcm = reinterpret_cast(data); + for (size_t i = 0; i < pcmLength; i++) { + pcm[i] = srcPcm[i]; + } + } else { + CRASH("Unsupported format: PCM8"); + } + } else { + if (channel.mBytesPerBlock == 9) { + Adpcm4ToPcm16(data, dataLength, pcm, pcmLength, hist1, hist0); + } else { + CRASH("Unsupported format: ADPCM2"); + } + } +} + static void SDLCALL ReadChannelSamples( void *userdata, SDL_AudioStream *stream, @@ -151,7 +192,8 @@ static void SDLCALL ReadChannelSamples( u32 renderSamples = std::min(channel.mSamplesLeft, static_cast(additional_amount)); - Adpcm4ToPcm16( + ReadSamplesCore( + channel, aramBase + dataPosition, ConvertSamplesToDataLength(channel, renderSamples), requested, @@ -173,7 +215,8 @@ static void SDLCALL ReadChannelSamples( curSamplePosition = channel.mEndSample - channel.mSamplesLeft; dataPosition = ConvertSamplesToDataLength(channel, curSamplePosition); - Adpcm4ToPcm16( + ReadSamplesCore( + channel, aramBase + dataPosition, ConvertSamplesToDataLength(channel, additional_amount - renderSamples), requested + renderSamples, From 34e0da4a00f733d3ed06d36e4311d11624fbee94 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Thu, 19 Mar 2026 20:06:02 +0100 Subject: [PATCH 70/94] Skip work in ReadChannelSamples if no new samples needed --- src/dusk/audio/DuskDsp.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/dusk/audio/DuskDsp.cpp b/src/dusk/audio/DuskDsp.cpp index 730a5fb856..61d9abc92f 100644 --- a/src/dusk/audio/DuskDsp.cpp +++ b/src/dusk/audio/DuskDsp.cpp @@ -167,6 +167,10 @@ static void SDLCALL ReadChannelSamples( int additional_amount, int) { + if (additional_amount == 0) { + return; + } + const auto index = static_cast(reinterpret_cast(userdata)); auto& channel = JASDsp::CH_BUF[index]; auto& aux = ChannelAux[index]; From 355c4e95562e304654730412bf8a87435f2931e3 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Thu, 19 Mar 2026 20:06:28 +0100 Subject: [PATCH 71/94] Move the audio dumping code to a define --- src/dusk/audio/DuskAudioSystem.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/dusk/audio/DuskAudioSystem.cpp b/src/dusk/audio/DuskAudioSystem.cpp index 3becb76cfa..e01d427901 100644 --- a/src/dusk/audio/DuskAudioSystem.cpp +++ b/src/dusk/audio/DuskAudioSystem.cpp @@ -16,6 +16,8 @@ #include "DuskDsp.hpp" #include "JSystem/JAudio2/JASAudioThread.h" +#define DUSK_DUMP_AUDIO + using namespace dusk::audio; static OutputSubframe OutBuffer; @@ -70,7 +72,7 @@ void SDLCALL GetNewAudio( } } -#if 0 +#if defined(DUSK_DUMP_AUDIO) static std::ofstream outRaw("guh.raw", std::ios_base::out | std::ios_base::binary); #endif @@ -86,7 +88,7 @@ int RenderNewAudioFrame() { JASAudioThread::snIntCount -= 1; } -#if 0 +#if defined(DUSK_DUMP_AUDIO) outRaw.flush(); #endif @@ -110,11 +112,11 @@ void RenderAudioSubframe() { JASDriver::updateDSP(); DspRender(OutBuffer); -#if 0 - outRaw.write((const char*)subFrame.data(), sizeof(subFrame)); -#endif - InterleaveOutputData(OutBuffer, OutInterleaveBuffer); +#if defined(DUSK_DUMP_AUDIO) + outRaw.write((const char*)OutInterleaveBuffer.data(), sizeof(OutInterleaveBuffer)); +#endif + SDL_PutAudioStreamData(PlaybackStream, &OutInterleaveBuffer, sizeof(OutInterleaveBuffer)); } From 44e8bf521b7dc8b0b1fc756a02f6ec18f0bbbe3b Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Thu, 19 Mar 2026 20:07:39 +0100 Subject: [PATCH 72/94] Load bearing include --- src/dusk/audio/DuskDsp.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/dusk/audio/DuskDsp.cpp b/src/dusk/audio/DuskDsp.cpp index 61d9abc92f..9d6f3d10d0 100644 --- a/src/dusk/audio/DuskDsp.cpp +++ b/src/dusk/audio/DuskDsp.cpp @@ -9,6 +9,7 @@ #include "Adpcm.hpp" #include "JSystem/JAudio2/JASDriverIF.h" +#include "dusk/endian.h" #include "global.h" using namespace dusk::audio; From 36beea1fa5354f4a95881977b5acec209a053871 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Thu, 19 Mar 2026 20:27:46 +0100 Subject: [PATCH 73/94] How did this compile ??? --- src/dusk/audio/DuskDsp.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/dusk/audio/DuskDsp.hpp b/src/dusk/audio/DuskDsp.hpp index 0b47cf2570..e2dec52e1a 100644 --- a/src/dusk/audio/DuskDsp.hpp +++ b/src/dusk/audio/DuskDsp.hpp @@ -16,8 +16,6 @@ namespace dusk::audio { OutputChannel_MAX }; - constexpr - struct ChannelAuxData { s16 hist1; s16 hist0; From 62df53299ad9be6a2faadbdd6fff16baaee98720 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Thu, 19 Mar 2026 20:28:15 +0100 Subject: [PATCH 74/94] Attempt to allow sample rates to be changed dynamically Doesn't fix the walking bomb things :( --- src/dusk/audio/DuskDsp.cpp | 21 +++++++++++++++++---- src/dusk/audio/DuskDsp.hpp | 1 + 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/dusk/audio/DuskDsp.cpp b/src/dusk/audio/DuskDsp.cpp index 9d6f3d10d0..f52d191d09 100644 --- a/src/dusk/audio/DuskDsp.cpp +++ b/src/dusk/audio/DuskDsp.cpp @@ -72,20 +72,31 @@ static void RenderChannel( ChannelAuxData& channelAux, OutputSubframe& subframe); -static void ResetChannel(JASDsp::TChannel& channel, ChannelAuxData& aux) { - channel.mSamplesLeft = channel.mEndSample - channel.mSamplePosition; +constexpr static int PitchToSampleRate(u16 value) { + return static_cast(static_cast(SampleRate) * value / 4096); +} + +static void UpdateSampleRate(const JASDsp::TChannel& channel, ChannelAuxData& aux) { + auto sampleRate = PitchToSampleRate(channel.mPitch); const SDL_AudioSpec spec = { SDL_AUDIO_S16, 1, - static_cast(static_cast(SampleRate) * channel.mPitch / 4096) + sampleRate }; + SDL_SetAudioStreamFormat(aux.resampleStream, &spec, nullptr); + aux.prevPitch = channel.mPitch; +} + +static void ResetChannel(JASDsp::TChannel& channel, ChannelAuxData& aux) { + channel.mSamplesLeft = channel.mEndSample - channel.mSamplePosition; + aux.hist0 = 0; aux.hist1 = 0; SDL_ClearAudioStream(aux.resampleStream); - SDL_SetAudioStreamFormat(aux.resampleStream, &spec, nullptr); + UpdateSampleRate(channel, aux); channel.mResetFlag = false; } @@ -329,6 +340,8 @@ static void RenderChannel( OutputSubframe& subframe) { if (channel.mResetFlag) { ResetChannel(channel, channelAux); + } else if (channelAux.prevPitch != channel.mPitch) { + UpdateSampleRate(channel, channelAux); } DspSubframe audioLoadBuffer = {}; diff --git a/src/dusk/audio/DuskDsp.hpp b/src/dusk/audio/DuskDsp.hpp index e2dec52e1a..23e05b60e6 100644 --- a/src/dusk/audio/DuskDsp.hpp +++ b/src/dusk/audio/DuskDsp.hpp @@ -20,6 +20,7 @@ namespace dusk::audio { s16 hist1; s16 hist0; SDL_AudioStream* resampleStream; + u16 prevPitch; }; extern ChannelAuxData ChannelAux[DSP_CHANNELS]; From c940e04b5651b76f758dc85a5ddbc0aa229fdc19 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Fri, 20 Mar 2026 17:51:09 +0100 Subject: [PATCH 75/94] struct JASWaveArc forward declaration fix --- libs/JSystem/include/JSystem/JAudio2/JASWaveInfo.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/JSystem/include/JSystem/JAudio2/JASWaveInfo.h b/libs/JSystem/include/JSystem/JAudio2/JASWaveInfo.h index 62110754d7..cac518836e 100644 --- a/libs/JSystem/include/JSystem/JAudio2/JASWaveInfo.h +++ b/libs/JSystem/include/JSystem/JAudio2/JASWaveInfo.h @@ -3,7 +3,7 @@ #include -class JASWaveArc; +struct JASWaveArc; #define WAVE_FORMAT_ADPCM4 0 #define WAVE_FORMAT_ADPCM2 1 From 6443b9fded47bc6881506a2be9c1bca3425e5237 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Fri, 20 Mar 2026 17:51:42 +0100 Subject: [PATCH 76/94] Audio system cleanup and comments --- include/dusk/audio/DuskAudioSystem.h | 3 + src/dusk/audio/DuskAudioSystem.cpp | 22 +++- src/dusk/audio/DuskDsp.cpp | 155 ++++++++++++++++----------- src/dusk/audio/DuskDsp.hpp | 41 +++++++ 4 files changed, 152 insertions(+), 69 deletions(-) diff --git a/include/dusk/audio/DuskAudioSystem.h b/include/dusk/audio/DuskAudioSystem.h index b06bdbe84e..c41762e146 100644 --- a/include/dusk/audio/DuskAudioSystem.h +++ b/include/dusk/audio/DuskAudioSystem.h @@ -1,5 +1,8 @@ #pragma once namespace dusk::audio { + /** + * Initialize the audio system and start playing audio. + */ void Initialize(); } diff --git a/src/dusk/audio/DuskAudioSystem.cpp b/src/dusk/audio/DuskAudioSystem.cpp index e01d427901..7c2572dd53 100644 --- a/src/dusk/audio/DuskAudioSystem.cpp +++ b/src/dusk/audio/DuskAudioSystem.cpp @@ -16,7 +16,7 @@ #include "DuskDsp.hpp" #include "JSystem/JAudio2/JASAudioThread.h" -#define DUSK_DUMP_AUDIO +// #define DUSK_DUMP_AUDIO using namespace dusk::audio; @@ -25,13 +25,25 @@ static std::array OutInte static SDL_AudioStream* PlaybackStream; +/** + * SDL audiostream callback to trigger rendering of new audio data. + */ static void SDLCALL GetNewAudio( - void *userdata, - SDL_AudioStream *stream, - int additional_amount, - int total_amount); + void*, + SDL_AudioStream*, + int needed, + int); +/** + * Render an entire new frame of audio and output it to SDL3. + * Note: "audio frames" are unrelated to video frames. + * @return Amount of audio samples rendered. + */ static int RenderNewAudioFrame(); + +/** + * Render an audio subframe and output it to SDL3. + */ static void RenderAudioSubframe(); static void InitSDL3Output() { diff --git a/src/dusk/audio/DuskDsp.cpp b/src/dusk/audio/DuskDsp.cpp index f52d191d09..97c6f1c6d1 100644 --- a/src/dusk/audio/DuskDsp.cpp +++ b/src/dusk/audio/DuskDsp.cpp @@ -16,6 +16,9 @@ using namespace dusk::audio; ChannelAuxData dusk::audio::ChannelAux[DSP_CHANNELS] = {}; +/** + * Validate that a DSP channel's format is actually something we know how to play. + */ static bool ValidateChannelWaveFormat(const JASDsp::TChannel& channel) { if (channel.mSamplesPerBlock == AdpcmSampleCount && channel.mBytesPerBlock == Adpcm4FrameSize) return true; @@ -30,6 +33,9 @@ static bool ValidateChannelWaveFormat(const JASDsp::TChannel& channel) { return false; } +/** + * Validate that a DSP channel is actually something we know how to play. + */ static void ValidateChannel(const JASDsp::TChannel& channel) { if (!ValidateChannelWaveFormat(channel)) { CRASH( @@ -39,39 +45,27 @@ static void ValidateChannel(const JASDsp::TChannel& channel) { } } -static u32 ConvertDataLengthToSamples(const JASDsp::TChannel& channel, u32 dataLen) { - if (dataLen % channel.mBytesPerBlock != 0) { - CRASH("Indivisible data length: %d\n", dataLen); - } - - return (dataLen / channel.mBytesPerBlock) * channel.mSamplesPerBlock; -} - static u32 ConvertSamplesToDataLength(const JASDsp::TChannel& channel, u32 samples) { - if (channel.mSamplesPerBlock == 1) { - if (channel.mBytesPerBlock == 16) { - return samples * 2; - } - if (channel.mBytesPerBlock == 8) { - return samples; - } - CRASH("Unknown format"); - } - if (samples % channel.mSamplesPerBlock != 0) { // Ensure we round up. samples += channel.mSamplesPerBlock; //CRASH("Indivisible sample count: %d\n", samples); } - return (samples / channel.mSamplesPerBlock) * channel.mBytesPerBlock; + return (samples / channel.mSamplesPerBlock) * BlockBytes(channel); } +/** + * Render the audio data contributed by a single DSP channel. Reads & decodes new input samples. + */ static void RenderChannel( JASDsp::TChannel& channel, ChannelAuxData& channelAux, OutputSubframe& subframe); +/** + * Converts a pitch value on a DSP channel to a sample rate. + */ constexpr static int PitchToSampleRate(u16 value) { return static_cast(static_cast(SampleRate) * value / 4096); } @@ -89,6 +83,9 @@ static void UpdateSampleRate(const JASDsp::TChannel& channel, ChannelAuxData& au aux.prevPitch = channel.mPitch; } +/** + * Reset state for a DSP channel between independent playbacks. + */ static void ResetChannel(JASDsp::TChannel& channel, ChannelAuxData& aux) { channel.mSamplesLeft = channel.mEndSample - channel.mSamplePosition; @@ -101,6 +98,9 @@ static void ResetChannel(JASDsp::TChannel& channel, ChannelAuxData& aux) { channel.mResetFlag = false; } +/** + * Mix subframe data from src into dst. + */ static void MixSubframe(DspSubframe& dst, const DspSubframe& src) { for (int i = 0; i < dst.size(); i++) { dst[i] += src[i]; @@ -142,14 +142,16 @@ void dusk::audio::DspRender(OutputSubframe& subframe) { } } -static void ReadSamplesCore( +/** + * Actually decode samples from memory for the given audio channel. + */ +static void ReadSampleData( const JASDsp::TChannel& channel, + ChannelAuxData& aux, const u8* data, size_t dataLength, s16* pcm, - size_t pcmLength, - s16& hist1, - s16& hist0) { + size_t pcmLength) { if (channel.mSamplesPerBlock == 1) { if (channel.mBytesPerBlock == 0x10) { // PCM16 @@ -166,16 +168,62 @@ static void ReadSamplesCore( } } else { if (channel.mBytesPerBlock == 9) { - Adpcm4ToPcm16(data, dataLength, pcm, pcmLength, hist1, hist0); + Adpcm4ToPcm16(data, dataLength, pcm, pcmLength, aux.hist1, aux.hist0); } else { CRASH("Unsupported format: ADPCM2"); } } } +/** + * Read a single *contiguous* chunk of sample data from a channel, + * writes the samples to the channel's resampler stream. + * + * @returns Amount of samples actually read. + */ +static int ReadChannelSamplesChunk( + JASDsp::TChannel& channel, + ChannelAuxData& aux, + int desiredSamples) { + + assert(desiredSamples >= 0); + + auto aramBase = static_cast(ARGetStorageAddress()) + channel.mWaveAramAddress; + + // Streaming logic directly modifies mSamplesLeft. + // So we use that as our tracking of where we are. + auto curSamplePosition = channel.mEndSample - channel.mSamplesLeft; + assert(curSamplePosition % channel.mSamplesPerBlock == 0); + auto dataPosition = ConvertSamplesToDataLength(channel, curSamplePosition); + + u32 renderSamples = std::min(channel.mSamplesLeft, static_cast(desiredSamples)); + + int renderSize = static_cast(sizeof(s16) * renderSamples); + auto renderData = static_cast(alloca(renderSize)); + memset(renderData, 0, renderSize); + + ReadSampleData( + channel, + aux, + aramBase + dataPosition, + ConvertSamplesToDataLength(channel, renderSamples), + renderData, + renderSamples); + + channel.mSamplesLeft -= renderSamples; + channel.mSamplePosition += renderSamples; + + SDL_PutAudioStreamData(aux.resampleStream, renderData, renderSize); + + return static_cast(renderSamples); +} + +/** + * Reads new audio channels from a DSP channel and writes them to the resampler stream. + */ static void SDLCALL ReadChannelSamples( void *userdata, - SDL_AudioStream *stream, + SDL_AudioStream*, int additional_amount, int) { @@ -195,30 +243,8 @@ static void SDLCALL ReadChannelSamples( additional_amount = ALIGN_NEXT(additional_amount, channel.mSamplesPerBlock); - int requestedSize = static_cast(sizeof(s16) * additional_amount); - auto requested = static_cast(alloca(requestedSize)); - memset(requested, 0, requestedSize); - - auto aramBase = static_cast(ARGetStorageAddress()) + channel.mWaveAramAddress; - - // Streaming logic directly modifies mSamplesLeft. - // So we use that as our tracking of where we are. - auto curSamplePosition = channel.mEndSample - channel.mSamplesLeft; - auto dataPosition = ConvertSamplesToDataLength(channel, curSamplePosition); - - u32 renderSamples = std::min(channel.mSamplesLeft, static_cast(additional_amount)); - - ReadSamplesCore( - channel, - aramBase + dataPosition, - ConvertSamplesToDataLength(channel, renderSamples), - requested, - renderSamples, - aux.hist1, - aux.hist0); - - channel.mSamplesLeft -= renderSamples; - channel.mSamplePosition += renderSamples; + auto samplesRead = ReadChannelSamplesChunk(channel, aux, additional_amount); + additional_amount -= samplesRead; if (channel.mSamplesLeft == 0) { // Reached end of buffer. @@ -228,28 +254,19 @@ static void SDLCALL ReadChannelSamples( channel.mSamplesLeft = channel.mEndSample - channel.mLoopStartSample; channel.mSamplePosition = channel.mLoopStartSample; - curSamplePosition = channel.mEndSample - channel.mSamplesLeft; - dataPosition = ConvertSamplesToDataLength(channel, curSamplePosition); + } - ReadSamplesCore( - channel, - aramBase + dataPosition, - ConvertSamplesToDataLength(channel, additional_amount - renderSamples), - requested + renderSamples, - additional_amount - renderSamples, - aux.hist1, - aux.hist0); - - channel.mSamplesLeft -= (additional_amount - renderSamples); - channel.mSamplePosition += (additional_amount - renderSamples); + if (additional_amount >= 0) { + ReadChannelSamplesChunk(channel, aux, additional_amount); } channel.mAramStreamPosition = channel.mWaveAramAddress + ConvertSamplesToDataLength(channel, channel.mSamplePosition); - - SDL_PutAudioStreamData(stream, requested, requestedSize); } +/** + * Get the expected BusConnect value needed to define the given output channel in a DSP channel. + */ constexpr u16 GetBusConnect(const OutputChannel channel) { switch (channel) { // TODO: This is a guess for now. @@ -262,6 +279,10 @@ constexpr u16 GetBusConnect(const OutputChannel channel) { } } +/** + * For a DSP channel the JASDsp::OutputChannelConfig value targeting the given output channel. + * Returns null if the DSP channel does not output to this output channel. + */ static const JASDsp::OutputChannelConfig* GetOutputConfig( const JASDsp::TChannel& sourceChannel, OutputChannel channel) { @@ -277,6 +298,9 @@ static const JASDsp::OutputChannelConfig* GetOutputConfig( return nullptr; } +/** + * Get the volume that the given DSP channel should render to the given output channel at. + */ static f32 GetVolumeForOutputChannel( const JASDsp::TChannel& sourceChannel, OutputChannel outputChannel) { @@ -315,6 +339,9 @@ static f32 GetVolumeForOutputChannel( return ratio; } +/** + * Given decoded & resampled input samples, render a DSP channel to a given output channel. + */ static void RenderOutputChannel( const JASDsp::TChannel& sourceChannel, OutputChannel outputChannel, diff --git a/src/dusk/audio/DuskDsp.hpp b/src/dusk/audio/DuskDsp.hpp index 23e05b60e6..9dbffbfec4 100644 --- a/src/dusk/audio/DuskDsp.hpp +++ b/src/dusk/audio/DuskDsp.hpp @@ -16,6 +16,9 @@ namespace dusk::audio { OutputChannel_MAX }; + /** + * Data stored by DSP implementation for each DSP channel. + */ struct ChannelAuxData { s16 hist1; s16 hist0; @@ -25,8 +28,14 @@ namespace dusk::audio { extern ChannelAuxData ChannelAux[DSP_CHANNELS]; + /** + * Data storage for a single subframe and output channel's worth of samples. + */ using DspSubframe = std::array; + /** + * Data storage for a single subframe's worth of samples, across all output channels. + */ struct OutputSubframe { static constexpr int NUM_CHANNELS = static_cast(OutputChannel::OutputChannel_MAX); @@ -38,6 +47,38 @@ namespace dusk::audio { } }; + /** + * Initialize the DSP system, creating data storage needed for channels and such. + */ void DspInit(); + + /** + * Render a subframe of audio with the current DSP state. + */ void DspRender(OutputSubframe& subframe); + + /** + * Get the amount of samples a single "block" of this channel's data has. + */ + constexpr u32 BlockSamples(const JASDsp::TChannel& channel) { + return channel.mSamplesPerBlock; + } + + /** + * Get the amount of bytes a single "block" of this channel's data has. + */ + constexpr u32 BlockBytes(const JASDsp::TChannel& channel) { + if (channel.mSamplesPerBlock == 1) { + if (channel.mBytesPerBlock == 16) { + return 2; + } + if (channel.mBytesPerBlock == 8) { + return 1; + } + + CRASH("Unknown format"); + } + + return channel.mBytesPerBlock; + } } From 3f671b028d8473e7e1e435701bc39902f491e503 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Fri, 20 Mar 2026 17:54:37 +0100 Subject: [PATCH 77/94] CLion? More like CLiedToMe --- src/dusk/audio/DuskDsp.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/dusk/audio/DuskDsp.hpp b/src/dusk/audio/DuskDsp.hpp index 9dbffbfec4..9168320e0f 100644 --- a/src/dusk/audio/DuskDsp.hpp +++ b/src/dusk/audio/DuskDsp.hpp @@ -6,6 +6,8 @@ #include #include "SDL3/SDL_audio.h" +// ReSharper disable once CppUnusedIncludeDirective +#include "global.h" namespace dusk::audio { constexpr int SampleRate = 32000; From 0fbb62d7dc0b16dc9bd8a79e77a215a8c5f03b7a Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Fri, 20 Mar 2026 18:05:17 +0100 Subject: [PATCH 78/94] Stop compiling some DSP-related files. Replace it all with stubs. --- CMakeLists.txt | 3 ++- files.cmake | 4 ---- src/dusk/audio/DspStub.cpp | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 5 deletions(-) create mode 100644 src/dusk/audio/DspStub.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 12c4154994..1ee357e1a7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -89,7 +89,8 @@ add_library(game_debug STATIC ${JSYSTEM_DEBUG_FILES} ${SSYSTEM_FILES} src/dusk/audio/DuskDsp.hpp src/dusk/audio/DuskDsp.cpp src/dusk/audio/Adpcm.cpp - src/dusk/audio/Adpcm.hpp) + src/dusk/audio/Adpcm.hpp + src/dusk/audio/DspStub.cpp) target_compile_definitions(game_debug PRIVATE TARGET_PC AVOID_UB=1 VERSION=0 $<$:DEBUG=1>) # Make these properties PUBLIC so that the regular game target also sees them. diff --git a/files.cmake b/files.cmake index cae7f66aed..2290f68b53 100644 --- a/files.cmake +++ b/files.cmake @@ -507,10 +507,6 @@ set(JSYSTEM_DEBUG_FILES libs/JSystem/src/JAudio2/JASDSPInterface.cpp libs/JSystem/src/JAudio2/JASDriverIF.cpp libs/JSystem/src/JAudio2/JASSoundParams.cpp - libs/JSystem/src/JAudio2/dspproc.cpp - libs/JSystem/src/JAudio2/dsptask.cpp - libs/JSystem/src/JAudio2/osdsp.cpp - libs/JSystem/src/JAudio2/osdsp_task.cpp libs/JSystem/src/JAudio2/JAIAudible.cpp libs/JSystem/src/JAudio2/JAIAudience.cpp libs/JSystem/src/JAudio2/JAISe.cpp diff --git a/src/dusk/audio/DspStub.cpp b/src/dusk/audio/DspStub.cpp new file mode 100644 index 0000000000..c2dec809f2 --- /dev/null +++ b/src/dusk/audio/DspStub.cpp @@ -0,0 +1,35 @@ +#include "JSystem/JAudio2/dspproc.h" +#include "JSystem/JAudio2/osdsp_task.h" +#include "JSystem/JAudio2/dsptask.h" +#include "global.h" +#include "os.h" + +void DSPReleaseHalt2(u32) { + CRASH("We do not directly emulate the DSP"); +} +void DsetupTable(u32, u32, u32, u32, u32) { + // Nada. +} +void DsetMixerLevel(f32) { + // Nada for now, but maybe we should care about this? +} +void DsyncFrame2ch(u32, u32, u32) { + CRASH("We do not directly emulate the DSP"); +} +void DsyncFrame4ch(u32, u32, u32, u32, u32) { + CRASH("We do not directly emulate the DSP"); +} + +void DspBoot(void (*)(void*)) { + CRASH("We do not directly emulate the DSP"); +} +void DspFinishWork(u16) { + CRASH("We do not directly emulate the DSP"); +} +int DSPSendCommands2(u32*, u32, void (*)(u16)) { + CRASH("We do not directly emulate the DSP"); +} + +void DsyncFrame2(u32, u32, u32) { + CRASH("We do not directly emulate the DSP"); +} From 36e90df60c6b307ef55aefebe27467f2f427752a Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Fri, 20 Mar 2026 18:50:47 +0100 Subject: [PATCH 79/94] Stop compiling more DSP stubs --- libs/JSystem/src/JAudio2/JASAudioThread.cpp | 6 ++++ src/dusk/stubs.cpp | 40 --------------------- 2 files changed, 6 insertions(+), 40 deletions(-) diff --git a/libs/JSystem/src/JAudio2/JASAudioThread.cpp b/libs/JSystem/src/JAudio2/JASAudioThread.cpp index baa3d8b42e..fa476d1cb4 100644 --- a/libs/JSystem/src/JAudio2/JASAudioThread.cpp +++ b/libs/JSystem/src/JAudio2/JASAudioThread.cpp @@ -66,6 +66,7 @@ class JASChannel { // NONMATCHING location of JASPoolAllocObject_MultiThreaded void* JASAudioThread::run() { +#if !TARGET_PC OSInitFastCast(); JASDriver::initAI(DMACallback); JASDsp::boot(DSPCallback); @@ -115,6 +116,9 @@ void* JASAudioThread::run() { JUT_PANIC(152, "AUDIO THREAD INVALID MESSAGE\n"); } } +#else + return 0; +#endif } void JASAudioThread::DMACallback() { @@ -128,6 +132,7 @@ void JASAudioThread::DMACallback() { } void JASAudioThread::DSPCallback(void*) { +#if !TARGET_PC JASAudioThread* pAudioThread = getInstance(); JUT_ASSERT(184, pAudioThread); while (DSPCheckMailFromDSP() == 0) { } @@ -144,4 +149,5 @@ void JASAudioThread::DSPCallback(void*) { JASDsp::finishWork(mail); } } +#endif } diff --git a/src/dusk/stubs.cpp b/src/dusk/stubs.cpp index 73f4e93d80..3e44240d24 100644 --- a/src/dusk/stubs.cpp +++ b/src/dusk/stubs.cpp @@ -402,46 +402,6 @@ VIRetraceCallback VISetPreRetraceCallback(VIRetraceCallback cb) { } // extern "C" -#pragma mark DSP -#include -extern "C" void __DSP_insert_task(DSPTaskInfo* task) { - STUB_LOG(); -} - -extern "C" void __DSP_boot_task(DSPTaskInfo*) { - STUB_LOG(); -} - -extern "C" void __DSP_exec_task(DSPTaskInfo*, DSPTaskInfo*) { - STUB_LOG(); -} - -extern "C" void __DSP_remove_task(DSPTaskInfo* task) { - STUB_LOG(); -} - -void DSPAssertInt(void) { - STUB_LOG(); -} -u32 DSPCheckMailFromDSP(void) { - STUB_LOG(); - return 0; -} -u32 DSPCheckMailToDSP(void) { - STUB_LOG(); - return 0; -} -void DSPInit(void) { - STUB_LOG(); -} -u32 DSPReadMailFromDSP(void) { - STUB_LOG(); - return 0; -} -void DSPSendMailToDSP(u32 mail) { - STUB_LOG(); -} - #pragma mark Z2Audio class Z2AudioCS { public: From 324f65168370d3103d99be9bd194592184b18ee9 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Fri, 20 Mar 2026 19:38:49 +0100 Subject: [PATCH 80/94] Fix block-unaligned loop decoding --- src/dusk/audio/DuskDsp.cpp | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/src/dusk/audio/DuskDsp.cpp b/src/dusk/audio/DuskDsp.cpp index 97c6f1c6d1..81cceddb75 100644 --- a/src/dusk/audio/DuskDsp.cpp +++ b/src/dusk/audio/DuskDsp.cpp @@ -179,7 +179,7 @@ static void ReadSampleData( * Read a single *contiguous* chunk of sample data from a channel, * writes the samples to the channel's resampler stream. * - * @returns Amount of samples actually read. + * @returns Amount of samples actually read. Can be greater than the amount requested. */ static int ReadChannelSamplesChunk( JASDsp::TChannel& channel, @@ -193,6 +193,22 @@ static int ReadChannelSamplesChunk( // Streaming logic directly modifies mSamplesLeft. // So we use that as our tracking of where we are. auto curSamplePosition = channel.mEndSample - channel.mSamplesLeft; + + u32 skipSamples = curSamplePosition % channel.mSamplesPerBlock; + if (skipSamples != 0) { + // We need to start reading in the middle of a block. This can happen thanks to loops. + // So we move back to the start of the block and keep track that those samples should + // *not* be emitted. + desiredSamples += static_cast(skipSamples); + curSamplePosition -= skipSamples; + + channel.mSamplesLeft += skipSamples; + channel.mSamplePosition -= skipSamples; + } + + // Pad desiredSamples so that we always leave the channel block-aligned. + desiredSamples = ALIGN_NEXT(desiredSamples, channel.mSamplesPerBlock); + assert(curSamplePosition % channel.mSamplesPerBlock == 0); auto dataPosition = ConvertSamplesToDataLength(channel, curSamplePosition); @@ -213,9 +229,14 @@ static int ReadChannelSamplesChunk( channel.mSamplesLeft -= renderSamples; channel.mSamplePosition += renderSamples; - SDL_PutAudioStreamData(aux.resampleStream, renderData, renderSize); + SDL_PutAudioStreamData( + aux.resampleStream, + renderData + skipSamples, + static_cast(renderSize - skipSamples * sizeof(u16))); - return static_cast(renderSamples); + assert(channel.mSamplePosition % channel.mSamplesPerBlock == 0 || channel.mSamplesLeft == 0); + + return static_cast(renderSamples - skipSamples); } /** @@ -241,8 +262,6 @@ static void SDLCALL ReadChannelSamples( return; } - additional_amount = ALIGN_NEXT(additional_amount, channel.mSamplesPerBlock); - auto samplesRead = ReadChannelSamplesChunk(channel, aux, additional_amount); additional_amount -= samplesRead; @@ -254,6 +273,9 @@ static void SDLCALL ReadChannelSamples( channel.mSamplesLeft = channel.mEndSample - channel.mLoopStartSample; channel.mSamplePosition = channel.mLoopStartSample; + + aux.hist1 = channel.mpPenult; + aux.hist0 = channel.mpLast; } if (additional_amount >= 0) { From 5fbf8e4a7ff249b1715495a78c53ab9b54ce09a1 Mon Sep 17 00:00:00 2001 From: Lurs <2795933+Lurs@users.noreply.github.com> Date: Wed, 25 Mar 2026 09:35:16 +0100 Subject: [PATCH 81/94] removing Z2Creature::startCreatureVoice stub --- src/Z2AudioLib/Z2Creature.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Z2AudioLib/Z2Creature.cpp b/src/Z2AudioLib/Z2Creature.cpp index a17e3d516c..7a27e9e9c4 100644 --- a/src/Z2AudioLib/Z2Creature.cpp +++ b/src/Z2AudioLib/Z2Creature.cpp @@ -211,11 +211,6 @@ Z2SoundHandlePool* Z2Creature::startCreatureSoundLevel(JAISoundID soundID, u32 m } Z2SoundHandlePool* Z2Creature::startCreatureVoice(JAISoundID soundID, s8 reverb) { - #if TARGET_PC - // stubbed - return nullptr; - #endif - switch (soundID) { case Z2SE_MDN_V_FLY_OUT: case Z2SE_MDN_V_MGN_TAME: From 4a90460ba0a659be980ee463c9ac03613c21f049 Mon Sep 17 00:00:00 2001 From: Lurs <2795933+Lurs@users.noreply.github.com> Date: Thu, 26 Mar 2026 11:02:24 +0100 Subject: [PATCH 82/94] Fix audio memory leak --- src/Z2AudioLib/Z2Audience.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Z2AudioLib/Z2Audience.cpp b/src/Z2AudioLib/Z2Audience.cpp index be7bd4b126..9adab4084f 100644 --- a/src/Z2AudioLib/Z2Audience.cpp +++ b/src/Z2AudioLib/Z2Audience.cpp @@ -552,7 +552,11 @@ JAIAudible* Z2Audience::newAudible(const JGeometry::TVec3& pos, JAISoundID } void Z2Audience::deleteAudible(JAIAudible* audible) { +#if TARGET_PC + JKR_DELETE(static_cast(audible)); +#else JKR_DELETE(audible); +#endif } Z2Audible::~Z2Audible() {} From 2240c04cec04ea3db618b826df70e82c5e92b28f Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Thu, 26 Mar 2026 15:03:48 +0100 Subject: [PATCH 83/94] Fix shutdown crash --- include/dusk/main.h | 8 ++++++++ libs/JSystem/src/JAudio2/JAIAudience.cpp | 9 +++++++++ src/m_Do/m_Do_main.cpp | 7 +++++++ 3 files changed, 24 insertions(+) create mode 100644 include/dusk/main.h diff --git a/include/dusk/main.h b/include/dusk/main.h new file mode 100644 index 0000000000..b357de8ea5 --- /dev/null +++ b/include/dusk/main.h @@ -0,0 +1,8 @@ +#ifndef DUSK_MAIN_H +#define DUSK_MAIN_H + +namespace dusk { + extern bool IsShuttingDown; +} + +#endif // DUSK_MAIN_H diff --git a/libs/JSystem/src/JAudio2/JAIAudience.cpp b/libs/JSystem/src/JAudio2/JAIAudience.cpp index 3e1205e2fc..a14422a943 100644 --- a/libs/JSystem/src/JAudio2/JAIAudience.cpp +++ b/libs/JSystem/src/JAudio2/JAIAudience.cpp @@ -1,11 +1,20 @@ #include "JSystem/JSystem.h" // IWYU pragma: keep #include "JSystem/JAudio2/JAIAudience.h" + #include "JSystem/JAudio2/JAISeMgr.h" #include "JSystem/JAudio2/JAISeqMgr.h" #include "JSystem/JAudio2/JAIStreamMgr.h" +#include "dusk/main.h" JAIAudience::~JAIAudience() { +#if TARGET_PC + if (dusk::IsShuttingDown) { + // Those asserts down there crash on shutdown from dtors. + return; + } +#endif + JUT_ASSERT(14, ! JAISeMgr::getInstance() || ( JAISeMgr::getInstance() ->getAudience() != this )); JUT_ASSERT(15, ! JAISeqMgr::getInstance() || ( JAISeqMgr::getInstance() ->getAudience() != this )); JUT_ASSERT(16, ! JAIStreamMgr::getInstance() || ( JAIStreamMgr::getInstance() ->getAudience() != this )); diff --git a/src/m_Do/m_Do_main.cpp b/src/m_Do/m_Do_main.cpp index 20fc2a8aee..d03b352151 100644 --- a/src/m_Do/m_Do_main.cpp +++ b/src/m_Do/m_Do_main.cpp @@ -47,6 +47,7 @@ #include "dusk/dusk.h" #include "dusk/logging.h" #include "dusk/time.h" +#include "dusk/main.h" #include #include @@ -73,6 +74,10 @@ const int audioHeapSize = 0x14D800; // ========================================================================= #define COPYDATE_PATH "/str/Final/Release/COPYDATE" +#if TARGET_PC +bool dusk::IsShuttingDown = false; +#endif + s32 LOAD_COPYDATE(void*) { char buffer[32]; memset(buffer, 0, sizeof(buffer)); @@ -306,6 +311,8 @@ int game_main(int argc, char* argv[]) { fflush(stdout); fflush(stderr); + dusk::IsShuttingDown = true; + aurora_shutdown(); return 0; From 258d42b49ee5319dabf2d2ae857090450e48244f Mon Sep 17 00:00:00 2001 From: Lurs <2795933+Lurs@users.noreply.github.com> Date: Fri, 27 Mar 2026 12:23:41 +0100 Subject: [PATCH 84/94] fix stuck note e.g. in Hyrule field --- libs/JSystem/src/JKernel/JKRHeap.cpp | 8 +++++++- src/dusk/audio/DuskDsp.cpp | 5 +++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/libs/JSystem/src/JKernel/JKRHeap.cpp b/libs/JSystem/src/JKernel/JKRHeap.cpp index bc02d877fb..c2b32999fd 100644 --- a/libs/JSystem/src/JKernel/JKRHeap.cpp +++ b/libs/JSystem/src/JKernel/JKRHeap.cpp @@ -585,7 +585,13 @@ void* operator new(size_t size JKR_HEAP_TOKEN_PARAM, int alignment) { #endif void* operator new(size_t size JKR_HEAP_TOKEN_PARAM, JKRHeap* heap, int alignment) { - return JKRHeap::alloc(size, alignment, heap); + void* mem = JKRHeap::alloc(size, alignment, heap); +#if TARGET_PC + if (mem == nullptr) { + return fallback_alloc(size, abs(alignment), true); + } +#endif + return mem; } #if !TARGET_PC diff --git a/src/dusk/audio/DuskDsp.cpp b/src/dusk/audio/DuskDsp.cpp index 81cceddb75..9c58d8de59 100644 --- a/src/dusk/audio/DuskDsp.cpp +++ b/src/dusk/audio/DuskDsp.cpp @@ -125,6 +125,11 @@ void dusk::audio::DspRender(OutputSubframe& subframe) { continue; } + if (channel.mForcedStop) { + channel.mIsFinished = true; + continue; + } + if (channel.mBytesPerBlock == 0) { // I think these are oscillator channels? Not backed by audio. channel.mIsFinished = true; From 962193d73ca0ed7f08fc6491b74d3d146a38aff7 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Fri, 27 Mar 2026 12:42:37 +0100 Subject: [PATCH 85/94] Skip oscillator channels properly I think --- src/dusk/audio/DuskDsp.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dusk/audio/DuskDsp.cpp b/src/dusk/audio/DuskDsp.cpp index 9c58d8de59..bbcf4e94fd 100644 --- a/src/dusk/audio/DuskDsp.cpp +++ b/src/dusk/audio/DuskDsp.cpp @@ -130,8 +130,9 @@ void dusk::audio::DspRender(OutputSubframe& subframe) { continue; } - if (channel.mBytesPerBlock == 0) { + if (channel.mWaveAramAddress == 0) { // I think these are oscillator channels? Not backed by audio. + // No idea how to implement these yet, so skip them. channel.mIsFinished = true; continue; } From 21ce0d35b04e04a012a1bcc1d5dc9e8eeb226a65 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Fri, 27 Mar 2026 13:35:33 +0100 Subject: [PATCH 86/94] Replace ugly cast in dusk::audio::DspRender --- src/dusk/audio/DuskDsp.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/dusk/audio/DuskDsp.cpp b/src/dusk/audio/DuskDsp.cpp index bbcf4e94fd..76e6154f80 100644 --- a/src/dusk/audio/DuskDsp.cpp +++ b/src/dusk/audio/DuskDsp.cpp @@ -108,8 +108,7 @@ static void MixSubframe(DspSubframe& dst, const DspSubframe& src) { } void dusk::audio::DspRender(OutputSubframe& subframe) { - // This cast half exists because my debugger sucks and this is an easy way to look at the data. - auto& channels = *reinterpret_cast*>(JASDsp::CH_BUF); + std::span channels(JASDsp::CH_BUF, DSP_CHANNELS); for (int i = 0; i < channels.size(); i++) { auto& channel = channels[i]; From 50303bba1bf3beab6a08595d41b20025066cb205 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Fri, 27 Mar 2026 14:54:03 +0100 Subject: [PATCH 87/94] Implement master volume & volume interpolation Does not fix clicking :( Fixes https://github.com/TakaRikka/dusk/issues/132 Fixes https://github.com/TakaRikka/dusk/issues/128 --- include/dusk/audio/DuskAudioSystem.h | 8 +++ src/dusk/audio/DuskAudioSystem.cpp | 15 ++++++ src/dusk/audio/DuskDsp.cpp | 77 ++++++++++++++++++++++++---- src/dusk/audio/DuskDsp.hpp | 26 ++++++++++ src/dusk/imgui/ImGuiAudio.cpp | 16 ++++-- src/dusk/imgui/ImGuiMenuGame.cpp | 9 +++- 6 files changed, 134 insertions(+), 17 deletions(-) diff --git a/include/dusk/audio/DuskAudioSystem.h b/include/dusk/audio/DuskAudioSystem.h index c41762e146..1b31db6e7c 100644 --- a/include/dusk/audio/DuskAudioSystem.h +++ b/include/dusk/audio/DuskAudioSystem.h @@ -1,8 +1,16 @@ #pragma once +#include + namespace dusk::audio { /** * Initialize the audio system and start playing audio. */ void Initialize(); + + void SetMasterVolume(f32 value); + + u32 GetResetCount(int channelIdx); + + f32 VolumeFromU16(u16 value); } diff --git a/src/dusk/audio/DuskAudioSystem.cpp b/src/dusk/audio/DuskAudioSystem.cpp index 7c2572dd53..0ed2101dcf 100644 --- a/src/dusk/audio/DuskAudioSystem.cpp +++ b/src/dusk/audio/DuskAudioSystem.cpp @@ -15,6 +15,7 @@ #include "DuskDsp.hpp" #include "JSystem/JAudio2/JASAudioThread.h" +#include "JSystem/JAudio2/JASDriverIF.h" // #define DUSK_DUMP_AUDIO @@ -73,6 +74,12 @@ void dusk::audio::Initialize() { SDL_ResumeAudioStreamDevice(PlaybackStream); } +void dusk::audio::SetMasterVolume(const f32 value) { + JASCriticalSection section; + + MasterVolume = value; +} + void SDLCALL GetNewAudio( void*, SDL_AudioStream*, @@ -132,3 +139,11 @@ void RenderAudioSubframe() { SDL_PutAudioStreamData(PlaybackStream, &OutInterleaveBuffer, sizeof(OutInterleaveBuffer)); } + +u32 dusk::audio::GetResetCount(int channelIdx) { + return ChannelAux[channelIdx].resetCount; +} + +f32 dusk::audio::VolumeFromU16(u16 value) { + return static_cast(value) / static_cast(JASDriver::getChannelLevel_dsp()); +} diff --git a/src/dusk/audio/DuskDsp.cpp b/src/dusk/audio/DuskDsp.cpp index 76e6154f80..2b26ea478c 100644 --- a/src/dusk/audio/DuskDsp.cpp +++ b/src/dusk/audio/DuskDsp.cpp @@ -9,6 +9,7 @@ #include "Adpcm.hpp" #include "JSystem/JAudio2/JASDriverIF.h" +#include "dusk/audio/DuskAudioSystem.h" #include "dusk/endian.h" #include "global.h" @@ -16,6 +17,9 @@ using namespace dusk::audio; ChannelAuxData dusk::audio::ChannelAux[DSP_CHANNELS] = {}; +f32 dusk::audio::MasterVolume = 1.0f; +f32 dusk::audio::PrevMasterVolume = 1.0f; + /** * Validate that a DSP channel's format is actually something we know how to play. */ @@ -87,6 +91,8 @@ static void UpdateSampleRate(const JASDsp::TChannel& channel, ChannelAuxData& au * Reset state for a DSP channel between independent playbacks. */ static void ResetChannel(JASDsp::TChannel& channel, ChannelAuxData& aux) { + aux.resetCount += 1; + channel.mSamplesLeft = channel.mEndSample - channel.mSamplePosition; aux.hist0 = 0; @@ -95,6 +101,10 @@ static void ResetChannel(JASDsp::TChannel& channel, ChannelAuxData& aux) { SDL_ClearAudioStream(aux.resampleStream); UpdateSampleRate(channel, aux); + for (auto& volume : aux.prevVolume) { + volume = NAN; + } + channel.mResetFlag = false; } @@ -145,6 +155,11 @@ void dusk::audio::DspRender(OutputSubframe& subframe) { MixSubframe(subframe.channels[o], channelSubframe.channels[o]); } } + + for (auto& channel : subframe.channels) { + ApplyVolume(channel, channel, PrevMasterVolume, MasterVolume); + } + PrevMasterVolume = MasterVolume; } /** @@ -325,17 +340,24 @@ static const JASDsp::OutputChannelConfig* GetOutputConfig( return nullptr; } +struct VolumeValue { + f32 Target; + f32 Init; +}; + /** * Get the volume that the given DSP channel should render to the given output channel at. */ -static f32 GetVolumeForOutputChannel( +static VolumeValue GetVolumeForOutputChannel( const JASDsp::TChannel& sourceChannel, OutputChannel outputChannel) { u16 volume; + u16 initVolume; f32 panValue = 1; if (sourceChannel.mAutoMixerBeenSet) { volume = sourceChannel.mAutoMixerVolume; + initVolume = sourceChannel.mAutoMixerInitVolume; auto autoMixerPan = static_cast(sourceChannel.mAutoMixerPanDolby >> 8) / 127; @@ -353,17 +375,21 @@ static f32 GetVolumeForOutputChannel( } else { auto config = GetOutputConfig(sourceChannel, outputChannel); if (config == nullptr) { - return 0; + return {0, 0}; } volume = config->mTargetVolume; + initVolume = config->mCurrentVolume; } // TODO: interpolate to avoid popping. - f32 ratio = static_cast(volume) / static_cast(JASDriver::getChannelLevel_dsp()); - ratio *= panValue; + f32 targetRatio = VolumeFromU16(volume); + targetRatio *= panValue; - return ratio; + f32 initRatio = VolumeFromU16(initVolume); + initRatio *= panValue; + + return {targetRatio, initRatio}; } /** @@ -371,6 +397,7 @@ static f32 GetVolumeForOutputChannel( */ static void RenderOutputChannel( const JASDsp::TChannel& sourceChannel, + ChannelAuxData& aux, OutputChannel outputChannel, const std::span inputSamples, OutputSubframe& fullOutputSubframe) { @@ -379,13 +406,20 @@ static void RenderOutputChannel( assert(inputSamples.size() <= outputSubframe.size()); auto volume = GetVolumeForOutputChannel(sourceChannel, outputChannel); - if (volume == 0) { + + f32 targetVolume = volume.Target; + auto& prevVolume = aux.PrevVolume(outputChannel); + if (std::isnan(prevVolume)) { + // Initialize previous volume to new volume on first render. + prevVolume = volume.Init; + } + + if (prevVolume == 0 && targetVolume == 0) { return; } - for (int i = 0; i < inputSamples.size(); i++) { - outputSubframe[i] = inputSamples[i] * volume; - } + ApplyVolume(outputSubframe, inputSamples, prevVolume, targetVolume); + prevVolume = targetVolume; } static void RenderChannel( @@ -414,8 +448,8 @@ static void RenderChannel( static_assert(OutputSubframe::NUM_CHANNELS == 2, "Keep RenderChannel in sync!"); - RenderOutputChannel(channel, OutputChannel::LEFT, hasReadSamples, subframe); - RenderOutputChannel(channel, OutputChannel::RIGHT, hasReadSamples, subframe); + RenderOutputChannel(channel, channelAux, OutputChannel::LEFT, hasReadSamples, subframe); + RenderOutputChannel(channel, channelAux,OutputChannel::RIGHT, hasReadSamples, subframe); } void dusk::audio::DspInit() { @@ -440,3 +474,24 @@ void dusk::audio::DspInit() { reinterpret_cast(static_cast(i))); } } + +void dusk::audio::ApplyVolume( + std::span dst, + const std::span src, + const f32 startVolume, + const f32 endVolume) { + assert(dst.size() >= src.size()); + + if (startVolume == endVolume) { + for (int i = 0; i < src.size(); i++) { + dst[i] = src[i] * startVolume; + } + } else { + const f32 step = (endVolume - startVolume) / static_cast(src.size()); + auto curVolume = startVolume; + for (int i = 0; i < src.size(); i++) { + dst[i] = src[i] * curVolume; + curVolume += step; + } + } +} diff --git a/src/dusk/audio/DuskDsp.hpp b/src/dusk/audio/DuskDsp.hpp index 9168320e0f..c84bb338db 100644 --- a/src/dusk/audio/DuskDsp.hpp +++ b/src/dusk/audio/DuskDsp.hpp @@ -6,6 +6,8 @@ #include #include "SDL3/SDL_audio.h" +#include + // ReSharper disable once CppUnusedIncludeDirective #include "global.h" @@ -26,6 +28,21 @@ namespace dusk::audio { s16 hist0; SDL_AudioStream* resampleStream; u16 prevPitch; + + // Used for debugging tools. + u32 resetCount; + + /** + * Previous volume values, per output channel. + * Used to avoid clicking when volumes change. + * Set to NaN after channel reset, indicating that initial volume value is previous. + */ + f32 prevVolume[static_cast(OutputChannel::OutputChannel_MAX)]; + + f32& PrevVolume(OutputChannel channel) { + assert(channel < OutputChannel::OutputChannel_MAX); + return prevVolume[static_cast(channel)]; + } }; extern ChannelAuxData ChannelAux[DSP_CHANNELS]; @@ -83,4 +100,13 @@ namespace dusk::audio { return channel.mBytesPerBlock; } + + /** + * Apply a volume level to audio data. + * Interpolates across the two provided volume levels to avoid clicking. + */ + void ApplyVolume(std::span dst, std::span src, f32 startVolume, f32 endVolume); + + extern f32 MasterVolume; + extern f32 PrevMasterVolume; } diff --git a/src/dusk/imgui/ImGuiAudio.cpp b/src/dusk/imgui/ImGuiAudio.cpp index 07caf9f0c0..b348a5c3d9 100644 --- a/src/dusk/imgui/ImGuiAudio.cpp +++ b/src/dusk/imgui/ImGuiAudio.cpp @@ -6,14 +6,16 @@ #include "JSystem/JAudio2/JASCriticalSection.h" #include "JSystem/JAudio2/JASDSPChannel.h" #include "JSystem/JAudio2/JASDSPInterface.h" -#include "JSystem/JAudio2/JASDriverIF.h" #include "JSystem/JAudio2/JASTrack.h" +#include "dusk/audio/DuskAudioSystem.h" static std::array channelSortIndices = {}; static bool sortUpdateCount = true; static void DisplayDspChannel(int i) { + using namespace dusk::audio; + auto& channel = JASDsp::CH_BUF[i]; auto& jasChannel = JASDSPChannel::sDspChannels[i]; if (!channel.mIsActive) { @@ -39,19 +41,25 @@ static void DisplayDspChannel(int i) { auto pan = (channel.mAutoMixerPanDolby >> 8) / 127.5f; auto dolby = (channel.mAutoMixerPanDolby & 0xFF) / 127.5f; auto fxMix = (channel.mAutoMixerFxMix >> 8) / 127.5f; - auto volume = (channel.mAutoMixerVolume) / (f32) JASDriver::getChannelLevel_dsp(); + auto volume = VolumeFromU16(channel.mAutoMixerVolume); ImGui::Text( "Auto mixer active (pan: %f, dolby: %f, fx: %f, volume: %f)", pan, dolby, fxMix, volume); } else { ImGui::Text( - "Bus connect: %04X,%04X,%04X,%04X,%04X,%04X", + "Bus connect: %04X(%.2f),%04X(%.2f),%04X(%.2f),%04X(%.2f),%04X(%.2f),%04X(%.2f)", channel.mOutputChannels[0].mBusConnect, + VolumeFromU16(channel.mOutputChannels[0].mTargetVolume), channel.mOutputChannels[1].mBusConnect, + VolumeFromU16(channel.mOutputChannels[1].mTargetVolume), channel.mOutputChannels[2].mBusConnect, + VolumeFromU16(channel.mOutputChannels[2].mTargetVolume), channel.mOutputChannels[3].mBusConnect, + VolumeFromU16(channel.mOutputChannels[3].mTargetVolume), channel.mOutputChannels[4].mBusConnect, - channel.mOutputChannels[5].mBusConnect); + VolumeFromU16(channel.mOutputChannels[4].mTargetVolume), + channel.mOutputChannels[5].mBusConnect, + VolumeFromU16(channel.mOutputChannels[5].mTargetVolume)); } } diff --git a/src/dusk/imgui/ImGuiMenuGame.cpp b/src/dusk/imgui/ImGuiMenuGame.cpp index a0b79579eb..416b7d062c 100644 --- a/src/dusk/imgui/ImGuiMenuGame.cpp +++ b/src/dusk/imgui/ImGuiMenuGame.cpp @@ -7,8 +7,9 @@ #include #include "JSystem/JUtility/JUTGamePad.h" -#include "m_Do/m_Do_controller_pad.h" +#include "dusk/audio/DuskAudioSystem.h" #include "m_Do/m_Do_audio.h" +#include "m_Do/m_Do_controller_pad.h" namespace dusk { ImGuiMenuGame::ImGuiMenuGame() {} @@ -30,6 +31,8 @@ namespace dusk { ImGui::Text("Master Volume"); ImGui::SliderFloat("##m_masterVolume", &m_audioSettings.m_masterVolume, 0.0f, 1.0f, ""); + /* + // TODO: implement additional settings ImGui::Text("Main Music Volume"); ImGui::SliderFloat("##m_mainMusicVolume", &m_audioSettings.m_mainMusicVolume, 0.0f, 1.0f, ""); @@ -44,8 +47,10 @@ namespace dusk { Z2AudioMgr* audioMgr = Z2AudioMgr::getInterface(); if (audioMgr != nullptr) { - // TODO: actually apply volume settings } + */ + + audio::SetMasterVolume(m_audioSettings.m_masterVolume); ImGui::EndMenu(); } From 0ef39ae045fbff182fb6ac1c2a3b189958509cf1 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Fri, 27 Mar 2026 14:54:17 +0100 Subject: [PATCH 88/94] Show new audio channels as green on the debug tool --- src/dusk/imgui/ImGuiAudio.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/dusk/imgui/ImGuiAudio.cpp b/src/dusk/imgui/ImGuiAudio.cpp index b348a5c3d9..96bd854fbd 100644 --- a/src/dusk/imgui/ImGuiAudio.cpp +++ b/src/dusk/imgui/ImGuiAudio.cpp @@ -10,6 +10,7 @@ #include "dusk/audio/DuskAudioSystem.h" static std::array channelSortIndices = {}; +static std::array lastResetCounts = {}; static bool sortUpdateCount = true; @@ -28,7 +29,14 @@ static void DisplayDspChannel(int i) { if (ImGui::BeginChild(buf, ImVec2(), ImGuiChildFlags_Border | ImGuiChildFlags_AutoResizeY)) { ImGui::Text("[%02X]", i); ImGui::SameLine(); - ImGui::Text("Update count: %d", jasChannel.mUpdateCounter); + + auto resetCount = GetResetCount(i); + ImColor color = IM_COL32_WHITE; + if (lastResetCounts[i] != resetCount) { + lastResetCounts[i] = resetCount; + color = IM_COL32(0, 0xFF, 0, 0xFF); + } + ImGui::TextColored(color, "Update count: %d, reset count: %ud", jasChannel.mUpdateCounter, resetCount); ImGui::TextUnformatted(channel.mLoopFlag ? "Loop: true" : "Loop: false"); ImGui::SameLine(); ImGui::Text("Priority: %hd", jasChannel.mPriority); From c9d81c924c183d1a44b9316306f12aa26bb04d7e Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Fri, 27 Mar 2026 14:54:27 +0100 Subject: [PATCH 89/94] Audio channels are sorted with a stable sort --- src/dusk/imgui/ImGuiAudio.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dusk/imgui/ImGuiAudio.cpp b/src/dusk/imgui/ImGuiAudio.cpp index 96bd854fbd..707091b158 100644 --- a/src/dusk/imgui/ImGuiAudio.cpp +++ b/src/dusk/imgui/ImGuiAudio.cpp @@ -82,7 +82,7 @@ static void InitChannelSortIndices() { static void SortChannelsByUpdateCount() { InitChannelSortIndices(); - std::ranges::sort( + std::ranges::stable_sort( channelSortIndices, [](u8 a, u8 b) { auto& jasChannelA = JASDSPChannel::sDspChannels[a]; From f6f2f14a6036e178dd62b969c4297ecfd8af2e3a Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Fri, 27 Mar 2026 15:10:31 +0100 Subject: [PATCH 90/94] Remove SDL mixer Didn't end up using it. --- CMakeLists.txt | 5 +---- cmake/SDL-mixer.cmake | 28 ---------------------------- 2 files changed, 1 insertion(+), 32 deletions(-) delete mode 100644 cmake/SDL-mixer.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 1ee357e1a7..583b5efa94 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,9 +62,6 @@ FetchContent_Declare( FetchContent_MakeAvailable(cxxopts) -message(STATUS "Dusk: Fetching SDL3-mixer") - -include(cmake/SDL-mixer.cmake) include(files.cmake) # TODO: version handling for res includes @@ -103,7 +100,7 @@ target_include_directories(game_debug PUBLIC extern ${CMAKE_SOURCE_DIR}/build/${DUSK_TP_VERSION}/include build/${DUSK_TP_VERSION}/include) -target_link_libraries(game_debug PUBLIC aurora::core aurora::gx aurora::gd aurora::si aurora::vi aurora::pad aurora::mtx aurora::os aurora::dvd aurora::card SDL3_mixer::SDL3_mixer) +target_link_libraries(game_debug PUBLIC aurora::core aurora::gx aurora::gd aurora::si aurora::vi aurora::pad aurora::mtx aurora::os aurora::dvd aurora::card) set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) add_library(game SHARED ${DOLZEL_FILES} ${Z2AUDIOLIB_FILES} ${SSYSTEM_FILES} ${JSYSTEM_FILES} ${REL_FILES} ${DUSK_FILES} ${DOLPHIN_FILES} diff --git a/cmake/SDL-mixer.cmake b/cmake/SDL-mixer.cmake deleted file mode 100644 index e2106c4eb6..0000000000 --- a/cmake/SDL-mixer.cmake +++ /dev/null @@ -1,28 +0,0 @@ -set(SDLMIXER_VENDORED ON) -set(SDLMIXER_STRICT ON) -set(BUILD_SHARED_LIBS OFF) - -set(SDLMIXER_AIFF OFF) -set(SDLMIXER_WAVE OFF) -set(SDLMIXER_VOC OFF) -set(SDLMIXER_AU OFF) -set(SDLMIXER_FLAC OFF) -set(SDLMIXER_GME OFF) -set(SDLMIXER_MOD OFF) -set(SDLMIXER_MP3 OFF) -set(SDLMIXER_MIDI OFF) -set(SDLMIXER_OPUS OFF) -set(SDLMIXER_WAVPACK OFF) -set(SDLMIXER_VORBIS_STB OFF) -set(SDLMIXER_VORBIS_VORBISFILE OFF) - -FetchContent_Declare(SDL-mixer - URL https://github.com/libsdl-org/SDL_mixer/archive/refs/tags/release-3.2.0.tar.gz - URL_HASH SHA256=1077fef4a6e9513027da0a08e6f1b8645f8c88c8bef001c17befeb52b129b5dd - DOWNLOAD_EXTRACT_TIMESTAMP TRUE -) - -FetchContent_MakeAvailable(SDL-mixer) -if (NOT TARGET SDL3_mixer::SDL3_mixer) - message(FATAL_ERROR "Failed to make SDL-mixer available") -endif () From abfe917008315a7f4d5b5131697376cdf3595759 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Fri, 27 Mar 2026 16:54:51 +0100 Subject: [PATCH 91/94] Add replacement helpers for strncpy[_s] Portable without vulnerabilities. --- include/dusk/string.hpp | 52 ++++++++++++++++++++++++++++ libs/JSystem/src/JKernel/JKRHeap.cpp | 5 ++- src/d/d_stage.cpp | 4 ++- 3 files changed, 57 insertions(+), 4 deletions(-) create mode 100644 include/dusk/string.hpp diff --git a/include/dusk/string.hpp b/include/dusk/string.hpp new file mode 100644 index 0000000000..f1a365ebbb --- /dev/null +++ b/include/dusk/string.hpp @@ -0,0 +1,52 @@ +#ifndef DUSK_STRING_HPP +#define DUSK_STRING_HPP + +#include "global.h" +#include +#include + +namespace dusk { + +/** + * Copy a string to a fixed-size array. + * Truncates if the destination is not large enough, always inserts a null terminator (padding the remainder of the buffer with zeroes.) + */ +template +void SafeStringCopyTruncate(char (&buffer)[BufSize], const char* src) { + static_assert(BufSize > 0, "Target buffer cannot be size zero"); + + // Can't just use strncpy as I can't figure out how to selectively mute the warnings on MSVC. + // And can't use strncpy_s on MSVC as it doesn't fill the entire buffer. + constexpr size_t copyBufSize = BufSize - 1; + const auto srcLen = strlen(src); + const auto copyLen = srcLen > copyBufSize ? copyBufSize : srcLen; + memcpy(buffer, src, copyLen); + memset(buffer + copyLen, 0, BufSize - copyLen); +} + +/** + * Copy a string to a fixed-size array. + * Aborts if the destination is not large enough, always inserts a null terminator (padding the remainder of the buffer with zeroes.) + */ +template +void SafeStringCopy(char (&buffer)[BufSize], const char* src) { + static_assert(BufSize > 0, "Target buffer cannot be size zero"); + if (buffer == src) { + CRASH("Cannot copy string to same buffer"); + } + + // Can't just use strncpy as I can't figure out how to selectively mute the warnings on MSVC. + // And can't use strncpy_s on MSVC as it doesn't fill the entire buffer. + constexpr size_t copyBufSize = BufSize - 1; + const auto srcLen = strlen(src); + if (srcLen > copyBufSize) { + CRASH("Destination buffer too small!"); + } + + memcpy(buffer, src, srcLen); + memset(buffer + srcLen, 0, BufSize - srcLen); +} + +} + +#endif // DUSK_STRING_HPP diff --git a/libs/JSystem/src/JKernel/JKRHeap.cpp b/libs/JSystem/src/JKernel/JKRHeap.cpp index c2b32999fd..ce1899a4ba 100644 --- a/libs/JSystem/src/JKernel/JKRHeap.cpp +++ b/libs/JSystem/src/JKernel/JKRHeap.cpp @@ -15,6 +15,7 @@ #include "JSystem/JUtility/JUTAssert.h" #include "JSystem/JUtility/JUTException.h" +#include "dusk/string.hpp" #ifdef __MWERKS__ #include #else @@ -701,9 +702,7 @@ JKRHeap* JKRHeap::getCurrentHeap() { } void JKRHeap::setName(const char* name) { - size_t len = strlen(name); - strncpy(mName, name, sizeof(mName) - 1); - mName[sizeof(mName) - 1] = '\0'; + dusk::SafeStringCopyTruncate(mName, name); } void JKRHeap::setNamef(const char* fmt, ...) { diff --git a/src/d/d_stage.cpp b/src/d/d_stage.cpp index 9ada8b57b6..4d8c531b3c 100644 --- a/src/d/d_stage.cpp +++ b/src/d/d_stage.cpp @@ -21,6 +21,8 @@ #include "m_Do/m_Do_Reset.h" #include #include + +#include "dusk/string.hpp" #if TARGET_PC #include #include @@ -154,7 +156,7 @@ void dStage_startStage_c::set(const char* i_Name, s8 i_RoomNo, s16 i_Point, s8 i #if TARGET_PC // UB fix. if (mName != i_Name) { - strncpy_s(mName, sizeof(mName), i_Name, sizeof(mName)); + dusk::SafeStringCopy(mName, i_Name); } #else strcpy(mName, i_Name); From 54f4dddbde3d10e35c3db203961f8e478a6574e8 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Fri, 27 Mar 2026 17:07:54 +0100 Subject: [PATCH 92/94] Fix CRASH() macro outside MSVC Just add a CRASHF() overload that... isn't necessary anymore. Oh well. --- include/global.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/global.h b/include/global.h index df3723bd57..56662eb5be 100644 --- a/include/global.h +++ b/include/global.h @@ -214,6 +214,7 @@ using std::isnan; #define IS_REF_NONNULL(r) (1) #endif -#define CRASH(msg, ...) OSPanic(__FILE__, __LINE__, msg, __VA_ARGS__) +#define CRASH(msg) OSPanic(__FILE__, __LINE__, "%s", msg) +#define CRASHF(msg, ...) OSPanic(__FILE__, __LINE__, msg, __VA_ARGS__) #endif From a2b32c27c0a5523f9fbc26850936e0ee814d393c Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Fri, 27 Mar 2026 17:16:41 +0100 Subject: [PATCH 93/94] Do use strncpy in SafeStringCopy I figured out how to mute the warnings. --- include/dusk/string.hpp | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/include/dusk/string.hpp b/include/dusk/string.hpp index f1a365ebbb..598ae83730 100644 --- a/include/dusk/string.hpp +++ b/include/dusk/string.hpp @@ -7,6 +7,17 @@ namespace dusk { +inline void strncpyProxy(char* dst, const char* src, size_t count) { +#if _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4996) +#endif + strncpy(dst, src, count); +#if _MSC_VER +#pragma warning(pop) +#endif +} + /** * Copy a string to a fixed-size array. * Truncates if the destination is not large enough, always inserts a null terminator (padding the remainder of the buffer with zeroes.) @@ -15,13 +26,8 @@ template void SafeStringCopyTruncate(char (&buffer)[BufSize], const char* src) { static_assert(BufSize > 0, "Target buffer cannot be size zero"); - // Can't just use strncpy as I can't figure out how to selectively mute the warnings on MSVC. - // And can't use strncpy_s on MSVC as it doesn't fill the entire buffer. - constexpr size_t copyBufSize = BufSize - 1; - const auto srcLen = strlen(src); - const auto copyLen = srcLen > copyBufSize ? copyBufSize : srcLen; - memcpy(buffer, src, copyLen); - memset(buffer + copyLen, 0, BufSize - copyLen); + strncpyProxy(buffer, src, BufSize); + buffer[BufSize - 1] = 0; } /** @@ -35,16 +41,12 @@ void SafeStringCopy(char (&buffer)[BufSize], const char* src) { CRASH("Cannot copy string to same buffer"); } - // Can't just use strncpy as I can't figure out how to selectively mute the warnings on MSVC. - // And can't use strncpy_s on MSVC as it doesn't fill the entire buffer. - constexpr size_t copyBufSize = BufSize - 1; - const auto srcLen = strlen(src); - if (srcLen > copyBufSize) { + if (strlen(src) > BufSize - 1) { CRASH("Destination buffer too small!"); } - memcpy(buffer, src, srcLen); - memset(buffer + srcLen, 0, BufSize - srcLen); + strncpyProxy(buffer, src, BufSize); + buffer[BufSize - 1] = 0; } } From 5d24e1125f323079f99fa4c9e4058711c730dc21 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Fri, 27 Mar 2026 17:17:45 +0100 Subject: [PATCH 94/94] Re-add src == dst check in SafeStringCopyTruncate Accidentally dropped this while iterating on the exact implementation. --- include/dusk/string.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/dusk/string.hpp b/include/dusk/string.hpp index 598ae83730..7de1b1ad75 100644 --- a/include/dusk/string.hpp +++ b/include/dusk/string.hpp @@ -26,6 +26,10 @@ template void SafeStringCopyTruncate(char (&buffer)[BufSize], const char* src) { static_assert(BufSize > 0, "Target buffer cannot be size zero"); + if (buffer == src) { + CRASH("Cannot copy string to same buffer"); + } + strncpyProxy(buffer, src, BufSize); buffer[BufSize - 1] = 0; }