From ad80933ccffe3877d9eaaea161acf7ca065720cb Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Sat, 25 Jul 2026 15:49:13 +0200 Subject: [PATCH] Allow audio samples to be played back from anywhere in memory rather than ARAM. This is accomplished through a new mAramBaseAddress field on the DSP channels, which effectively relocates ARAM for that channel. --- .../include/JSystem/JAudio2/JASBasicWaveBank.h | 6 ++++++ libs/JSystem/include/JSystem/JAudio2/JASChannel.h | 12 ++++++++++++ .../include/JSystem/JAudio2/JASDSPInterface.h | 14 ++++++++++++++ .../include/JSystem/JAudio2/JASSimpleWaveBank.h | 6 ++++++ libs/JSystem/include/JSystem/JAudio2/JASWaveInfo.h | 6 ++++++ libs/JSystem/src/JAudio2/JASBank.cpp | 5 ++++- libs/JSystem/src/JAudio2/JASChannel.cpp | 6 ++++++ src/dusk/audio/DuskDsp.cpp | 5 +++-- 8 files changed, 57 insertions(+), 3 deletions(-) diff --git a/libs/JSystem/include/JSystem/JAudio2/JASBasicWaveBank.h b/libs/JSystem/include/JSystem/JAudio2/JASBasicWaveBank.h index 5a84750e3b..cf739b18a2 100644 --- a/libs/JSystem/include/JSystem/JAudio2/JASBasicWaveBank.h +++ b/libs/JSystem/include/JSystem/JAudio2/JASBasicWaveBank.h @@ -13,6 +13,12 @@ struct JASBasicWaveBank : public JASWaveBank { TWaveHandle() { mHeap = NULL; } virtual intptr_t getWavePtr() const; virtual const JASWaveInfo* getWaveInfo() const { return &mWaveInfo; } +#if TARGET_PC + /** + * @see JASChannel::mAramBaseAddress + */ + [[nodiscard]] void const* getAramBaseAddress() const override { return nullptr; } +#endif bool compareHeap(JASHeap* heap) const { return mHeap == heap;} /* 0x04 */ JASWaveInfo mWaveInfo; diff --git a/libs/JSystem/include/JSystem/JAudio2/JASChannel.h b/libs/JSystem/include/JSystem/JAudio2/JASChannel.h index af922638da..59512fbadd 100644 --- a/libs/JSystem/include/JSystem/JAudio2/JASChannel.h +++ b/libs/JSystem/include/JSystem/JAudio2/JASChannel.h @@ -166,6 +166,18 @@ public: u32 field_0x104; }; +#if TARGET_PC + /** + * Memory address at which this sound effect should consider ARAM to start. + * + * By changing this, sound effects can effectively be played back from anywhere in memory, + * rather than just the emulated ARAM space. + * + * If nullptr, the regular emulated ARAM is used. + */ + void const* mAramBaseAddress; +#endif + static DUSK_GAME_DATA OSMessageQueue sBankDisposeMsgQ; static DUSK_GAME_DATA OSMessage sBankDisposeMsg[16]; static DUSK_GAME_DATA OSMessage sBankDisposeList[16]; diff --git a/libs/JSystem/include/JSystem/JAudio2/JASDSPInterface.h b/libs/JSystem/include/JSystem/JAudio2/JASDSPInterface.h index b413052da7..7f830b6469 100644 --- a/libs/JSystem/include/JSystem/JAudio2/JASDSPInterface.h +++ b/libs/JSystem/include/JSystem/JAudio2/JASDSPInterface.h @@ -179,6 +179,20 @@ namespace JASDsp { /* 0x130 */ u8 _unused9[0x148 - 0x130]; /* 0x148 */ s16 iir_filter_params[8]; /* 0x158 */ u8 _unused10[0x180 - 0x158]; +#endif + +#if TARGET_PC + /** + * Memory address at which this sound effect should consider ARAM to start. + * This means fields like mWaveAramAddress will be relative to this pointer instead. + * + * By changing this, sound effects can effectively be played back from anywhere in memory, + * rather than just the emulated ARAM space. + * + * If nullptr, the regular emulated ARAM is used. + */ + void const* mAramBaseAddress; +#endif }; void boot(void (*)(void*)); diff --git a/libs/JSystem/include/JSystem/JAudio2/JASSimpleWaveBank.h b/libs/JSystem/include/JSystem/JAudio2/JASSimpleWaveBank.h index 4fbeee32b9..1dce0968a9 100644 --- a/libs/JSystem/include/JSystem/JAudio2/JASSimpleWaveBank.h +++ b/libs/JSystem/include/JSystem/JAudio2/JASSimpleWaveBank.h @@ -10,6 +10,12 @@ struct JASSimpleWaveBank : JASWaveBank, JASWaveArc { intptr_t getWavePtr() const; TWaveHandle(); const JASWaveInfo* getWaveInfo() const; +#if TARGET_PC + /** + * @see JASChannel::mAramBaseAddress + */ + [[nodiscard]] void const* getAramBaseAddress() const override { return nullptr; } +#endif /* 0x04 */ JASWaveInfo mWaveInfo; /* 0x28 */ JASHeap* mHeap; diff --git a/libs/JSystem/include/JSystem/JAudio2/JASWaveInfo.h b/libs/JSystem/include/JSystem/JAudio2/JASWaveInfo.h index 9d1c16d775..20b4be89fd 100644 --- a/libs/JSystem/include/JSystem/JAudio2/JASWaveInfo.h +++ b/libs/JSystem/include/JSystem/JAudio2/JASWaveInfo.h @@ -45,6 +45,12 @@ public: virtual ~JASWaveHandle() {} virtual const JASWaveInfo* getWaveInfo() const = 0; virtual intptr_t getWavePtr() const = 0; +#if TARGET_PC + /** + * @see JASChannel::mAramBaseAddress + */ + [[nodiscard]] virtual void const* getAramBaseAddress() const = 0; +#endif }; /** diff --git a/libs/JSystem/src/JAudio2/JASBank.cpp b/libs/JSystem/src/JAudio2/JASBank.cpp index 29ebe11cb5..69dd05795f 100644 --- a/libs/JSystem/src/JAudio2/JASBank.cpp +++ b/libs/JSystem/src/JAudio2/JASBank.cpp @@ -33,7 +33,7 @@ JASChannel* JASBank::noteOn(JASBank const* param_0, int param_1, u8 param_2, u8 return NULL; } intptr_t wavePtr = waveHandle->getWavePtr(); - if (!wavePtr) { + if (!wavePtr IF_DUSK(&& !waveHandle->getAramBaseAddress())) { return NULL; } @@ -44,6 +44,9 @@ JASChannel* JASBank::noteOn(JASBank const* param_0, int param_1, u8 param_2, u8 channel->setPriority(param_4); channel->field_0xdc.mWaveInfo = *waveInfo; channel->mWaveAramAddress = wavePtr; +#if TARGET_PC + channel->mAramBaseAddress = waveHandle->getAramBaseAddress(); +#endif channel->field_0xdc.mChannelType = stack_60.field_0x1c; channel->setBankDisposeID(param_0); channel->setInitPitch(stack_60.mPitch * (waveInfo->mSampleRate / JASDriver::getDacRate())); diff --git a/libs/JSystem/src/JAudio2/JASChannel.cpp b/libs/JSystem/src/JAudio2/JASChannel.cpp index d2d31d1e6a..d227ad3fc1 100644 --- a/libs/JSystem/src/JAudio2/JASChannel.cpp +++ b/libs/JSystem/src/JAudio2/JASChannel.cpp @@ -26,6 +26,9 @@ JASChannel::JASChannel(Callback i_callback, void* i_callbackData) : mCallback(i_callback), mCallbackData(i_callbackData), mUpdateTimer(0), +#if TARGET_PC + mAramBaseAddress(nullptr), +#endif mBankDisposeID(NULL), mKey(0), mVelocity(0x7f), @@ -256,6 +259,9 @@ s32 JASChannel::initialUpdateDSPChannel(JASDsp::TChannel* i_channel) { switch (field_0xdc.mChannelType) { case 0: i_channel->setWaveInfo(field_0xdc.mWaveInfo, mWaveAramAddress, mSkipSamples); +#if TARGET_PC + i_channel->mAramBaseAddress = mAramBaseAddress; +#endif break; case 2: i_channel->setOscInfo(mOscillatorSomething); diff --git a/src/dusk/audio/DuskDsp.cpp b/src/dusk/audio/DuskDsp.cpp index 4009b37e39..3240c0f810 100644 --- a/src/dusk/audio/DuskDsp.cpp +++ b/src/dusk/audio/DuskDsp.cpp @@ -317,7 +317,7 @@ void dusk::audio::DspRender(OutputSubframe& subframe) { } OutputSubframe channelSubframe = {}; - if (channel.mWaveAramAddress == 0) { + if (channel.mWaveAramAddress == 0 && !channel.mAramBaseAddress) { RenderOscChannel(channel, channelAux, channelSubframe); } else { ValidateChannel(channel); @@ -455,7 +455,8 @@ static int ReadChannelSamplesChunk( assert(desiredSamples >= 0); - auto aramBase = static_cast(ARGetStorageAddress()) + channel.mWaveAramAddress; + auto aramBase = static_cast(channel.mAramBaseAddress ? channel.mAramBaseAddress : ARGetStorageAddress()); + aramBase += channel.mWaveAramAddress; auto curSamplePosition = channel.mSamplePosition; u32 skipSamples = curSamplePosition % channel.mSamplesPerBlock;