summaryrefslogtreecommitdiff
path: root/libs/JSystem/src/JAudio2/JASChannel.cpp
diff options
context:
space:
mode:
authorLuke Street <luke@street.dev>2026-03-01 15:35:36 -0700
committerGitHub <noreply@github.com>2026-03-01 14:35:36 -0800
commit9649319ec4926457ef85e6094f8207474c977c2d (patch)
tree1c5cd4c7cc4c1aee03f28921213da47f2775444d /libs/JSystem/src/JAudio2/JASChannel.cpp
parent6e149819e13b1f70ecbf8a4c66b030c17ffed2bb (diff)
Reorganize library code into `libs/` (#3119)
* Reorganize files into libs/{dolphin,JSystem,PowerPC_EABI_Support,revolution,TRK_MINNOW_DOLPHIN} * Update configure.py and project.py for new libs structure * Refactor `#include <dolphin/x.h>` -> `<x.h>` * Remove `__REVOLUTION_SDK__` forwards from dolphin * Fix dolphin/ references in revolution * Wrap `#include <dolphin.h>` in `!__REVOLUTION_SDK__` * Always build TRK against dolphin headers * Resolve revolution SDK header resolution issues
Diffstat (limited to 'libs/JSystem/src/JAudio2/JASChannel.cpp')
-rw-r--r--libs/JSystem/src/JAudio2/JASChannel.cpp520
1 files changed, 520 insertions, 0 deletions
diff --git a/libs/JSystem/src/JAudio2/JASChannel.cpp b/libs/JSystem/src/JAudio2/JASChannel.cpp
new file mode 100644
index 0000000000..0979b38b8d
--- /dev/null
+++ b/libs/JSystem/src/JAudio2/JASChannel.cpp
@@ -0,0 +1,520 @@
+#include "JSystem/JSystem.h" // IWYU pragma: keep
+
+#include "JSystem/JAudio2/JASChannel.h"
+#include "JSystem/JAudio2/JASAiCtrl.h"
+#include "JSystem/JAudio2/JASCalc.h"
+#include "JSystem/JAudio2/JASDriverIF.h"
+#include "JSystem/JAudio2/JASDSPChannel.h"
+#include "JSystem/JSupport/JSupport.h"
+#include "JSystem/JMath/JMATrigonometric.h"
+#include "JSystem/JGeometry.h"
+
+OSMessageQueue JASChannel::sBankDisposeMsgQ;
+
+OSMessage JASChannel::sBankDisposeMsg[16];
+
+OSMessage JASChannel::sBankDisposeList[16];
+
+int JASChannel::sBankDisposeListSize;
+
+JASChannel::JASChannel(Callback i_callback, void* i_callbackData) :
+ mStatus(STATUS_STOP),
+ mDspCh(NULL),
+ mCallback(i_callback),
+ mCallbackData(i_callbackData),
+ mUpdateTimer(0),
+ mBankDisposeID(NULL),
+ mKey(0),
+ mVelocity(0x7f),
+ mKeySweep(0.0f),
+ mKeySweepTarget(0.0f),
+ mKeySweepCount(0),
+ mSkipSamples(0)
+{
+ field_0xdc.field_0x0 = 0;
+ field_0x104 = 0;
+ mMixConfig[0].whole = 0x150;
+ mMixConfig[1].whole = 0x210;
+ mMixConfig[2].whole = 0x352;
+ mMixConfig[3].whole = 0x412;
+ mMixConfig[4].whole = 0;
+ mMixConfig[5].whole = 0;
+ mPriority = 0x13f;
+ mPauseFlag = false;
+}
+
+JASChannel::~JASChannel() {
+ if (mDspCh != NULL) {
+ JUT_WARN(62, "%s","~JASChannel:: mDspCh != NULL");
+ mDspCh->drop();
+ }
+ if (mCallback != NULL) {
+ mCallback(CB_STOP, this, NULL, mCallbackData);
+ }
+}
+
+
+int JASChannel::play() {
+ JASDSPChannel* channel = JASDSPChannel::alloc(JSULoByte(mPriority), dspUpdateCallback, this);
+ if (channel == NULL) {
+ delete this;
+ return 0;
+ }
+ mDspCh = channel;
+ channel->start();
+ mStatus = STATUS_PLAY;
+ return 1;
+}
+
+int JASChannel::playForce() {
+ JASDSPChannel* channel = JASDSPChannel::allocForce(JSULoByte(mPriority),
+ dspUpdateCallback, this);
+ if (channel == NULL) {
+ delete this;
+ return 0;
+ }
+ mDspCh = channel;
+ channel->start();
+ mStatus = STATUS_PLAY;
+ return 1;
+}
+
+void JASChannel::release(u16 i_directRelease) {
+ if (mStatus == STATUS_PLAY) {
+ if (i_directRelease != 0) {
+ setDirectRelease(i_directRelease);
+ }
+ for (u32 i = 0; i < 2; i++) {
+ if (mOscillators[i].isValid()) {
+ mOscillators[i].release();
+ }
+ }
+ JUT_ASSERT(135, mDspCh);
+ mDspCh->setPriority(JSUHiByte(mPriority));
+ mStatus = STATUS_RELEASE;
+ }
+}
+
+void JASChannel::setOscInit(u32 oscnum, JASOscillator::Data const* i_data) {
+ JUT_ASSERT(147, oscnum < OSC_NUM);
+ mOscillators[oscnum].initStart(i_data);
+}
+
+void JASChannel::setMixConfig(u32 bus, u16 i_config) {
+ JUT_ASSERT(153, bus < BUSOUT_CPUCH);
+ mMixConfig[bus].whole = i_config;
+}
+
+f32 JASChannel::calcEffect(JASChannel::PanVector const* i_vector) {
+ return i_vector->mSound + i_vector->mEffect + i_vector->mChannel;
+}
+
+f32 JASChannel::calcPan(JASChannel::PanVector const* i_vector) {
+ return 0.5f + (i_vector->mSound - 0.5f) + (i_vector->mEffect - 0.5f)
+ + (i_vector->mChannel - 0.5f);
+}
+
+void JASChannel::effectOsc(u32 oscnum, JASOscillator::EffectParams* i_params) {
+ JUT_ASSERT(176, oscnum < OSC_NUM);
+ f32 value = mOscillators[oscnum].getValue();
+ switch (mOscillators[oscnum].getTarget()) {
+ case JASOscillator::TARGET_PITCH:
+ i_params->mPitch *= value;
+ break;
+ case JASOscillator::TARGET_VOLUME:
+ i_params->mVolume *= value;
+ break;
+ case JASOscillator::TARGET_PAN:
+ value -= 0.5;
+ i_params->mPan += value;
+ break;
+ case JASOscillator::TARGET_FXMIX:
+ i_params->mFxMix += value;
+ break;
+ case JASOscillator::TARGET_DOLBY:
+ i_params->mDolby += value;
+ break;
+ case JASOscillator::TARGET_5:
+ i_params->_14 *= value;
+ break;
+ case JASOscillator::TARGET_6:
+ i_params->_18 *= value;
+ break;
+ default:
+ JUT_WARN(205, "%s", "Invalid osc target");
+ }
+}
+
+void JASChannel::setKeySweepTarget(s32 i_target, u32 i_count) {
+ if (i_count == 0) {
+ mKeySweep = i_target;
+ } else {
+ mKeySweep = 0.0f;
+ mKeySweepTarget = i_target;
+ }
+ mKeySweepCount = i_count;
+}
+
+void JASChannel::updateEffectorParam(JASDsp::TChannel* i_channel, u16* i_mixerVolume,
+ JASOscillator::EffectParams const& i_params) {
+ PanVector pan_vector, fxmix_vector, dolby_vector;
+ pan_vector.mSound = mSoundParams.mPan;
+ pan_vector.mChannel = mParams.mPan;
+ pan_vector.mEffect = i_params.mPan;
+ fxmix_vector.mSound = mSoundParams.mFxMix;
+ fxmix_vector.mChannel = mParams.mFxMix;
+ fxmix_vector.mEffect = i_params.mFxMix;
+ dolby_vector.mSound = mSoundParams.mDolby;
+ dolby_vector.mChannel = mParams.mDolby;
+ dolby_vector.mEffect = i_params.mDolby;
+
+ f32 pan = 0.5f;
+ f32 dolby = 0.0f;
+ switch (JASDriver::getOutputMode()) {
+ case 0:
+ break;
+ case 1:
+ pan = calcPan(&pan_vector);
+ break;
+ case 2:
+ pan = calcPan(&pan_vector);
+ dolby = calcEffect(&dolby_vector);
+ break;
+ }
+ f32 fxmix = calcEffect(&fxmix_vector);
+ f32 volume = mVelocity / 127.0f;
+ volume = volume * volume;
+ volume = mSoundParams.mVolume * i_params.mVolume * mParams.mVolume
+ * (i_params._18 * mTremolo.getValue() + 1.0f) * volume;
+
+ if (volume < 0.0f) {
+ volume = 0.0f;
+ }
+ pan = JASCalc::clamp01(pan);
+ fxmix = JASCalc::clamp01(fxmix);
+ dolby = JASCalc::clamp01(dolby);
+
+ if (isDolbyMode()) {
+ updateAutoMixer(i_channel, volume, pan, fxmix, dolby);
+ } else {
+ updateMixer(volume, pan, fxmix, dolby, i_mixerVolume);
+ }
+}
+
+s32 JASChannel::dspUpdateCallback(u32 i_type, JASDsp::TChannel* i_channel, void* i_this) {
+ JASChannel* _this = static_cast<JASChannel*>(i_this);
+ switch (i_type) {
+ case JASDSPChannel::CB_PLAY:
+ return _this->updateDSPChannel(i_channel);
+ case JASDSPChannel::CB_START:
+ return _this->initialUpdateDSPChannel(i_channel);
+ case JASDSPChannel::CB_STOP:
+ case JASDSPChannel::CB_DROP:
+ _this->mDspCh->free();
+ _this->mDspCh = NULL;
+ delete _this;
+ return -1;
+ default:
+ JUT_WARN(323, "Unexpected JASDSPChannel::UpdateStatus %d", i_type);
+ }
+ return 0;
+}
+
+s32 JASChannel::initialUpdateDSPChannel(JASDsp::TChannel* i_channel) {
+ if (isDolbyMode()) {
+ i_channel->initAutoMixer();
+ }
+
+ if (mCallback != NULL) {
+ mCallback(CB_START, this, i_channel, mCallbackData);
+ }
+
+ if (field_0xdc.field_0x4.field_0x20[0] == 0) {
+ JUT_WARN_DEVICE(346, 2, "%s", "Lost wave data while playing");
+ mDspCh->free();
+ mDspCh = NULL;
+ delete this;
+ return -1;
+ }
+
+ if (checkBankDispose()) {
+ JUT_WARN_DEVICE(357, 2, "%s","Lost bank data while playing");
+ mDspCh->free();
+ mDspCh = NULL;
+ delete this;
+ return -1;
+ }
+
+ switch (field_0xdc.field_0x0) {
+ case 0:
+ i_channel->setWaveInfo(field_0xdc.field_0x4, field_0x104, mSkipSamples);
+ break;
+ case 2:
+ i_channel->setOscInfo(field_0x104);
+ break;
+ }
+
+ for (u8 i = 0; i < 6; i++) {
+ MixConfig mix_config = mMixConfig[i];
+ u32 output_mode = JASDriver::getOutputMode();
+ if (output_mode == 0) {
+ switch (mix_config.parts.upper) {
+ case 8:
+ mix_config.parts.upper = 11;
+ break;
+ case 9:
+ mix_config.parts.upper = 2;
+ break;
+ }
+ } else if (output_mode == 1 && mix_config.parts.upper == 8) {
+ mix_config.parts.upper = 11;
+ }
+ i_channel->setBusConnect(i, mix_config.parts.upper);
+ }
+
+ JASOscillator::EffectParams effect_params;
+ for (u32 i = 0; i < 2; i++) {
+ if (mOscillators[i].isValid()) {
+ mOscillators[i].update();
+ effectOsc(i, &effect_params);
+ }
+ }
+ mVibrate.resetCounter();
+ mTremolo.resetCounter();
+ u16 mixer_volume[6];
+ updateEffectorParam(i_channel, mixer_volume, effect_params);
+ for (u8 i = 0; i < 6; i++) {
+ i_channel->setMixerInitVolume(i, mixer_volume[i]);
+ }
+
+ f32 pitch = JASCalc::pow2(mParams.field_0x8 + (mKey + mKeySweep) / 12.0f
+ + effect_params._14 * mVibrate.getValue());
+ pitch = mSoundParams.mPitch * effect_params.mPitch * pitch * mParams.mPitch * 4096.0f;
+ if (pitch < 0.0f) {
+ pitch = 0.0f;
+ }
+ i_channel->setPitch(pitch);
+
+ i_channel->setPauseFlag(mPauseFlag);
+ i_channel->field_0x066 = 0;
+ return 0;
+}
+
+s32 JASChannel::updateDSPChannel(JASDsp::TChannel* i_channel) {
+ JUT_ASSERT(444, mStatus == STATUS_PLAY || mStatus == STATUS_RELEASE);
+ JUT_ASSERT(445, mDspCh);
+ if (mCallback != NULL) {
+ mCallback(CB_PLAY, this, i_channel, mCallbackData);
+ }
+
+ if (field_0xdc.field_0x4.field_0x20[0] == 0) {
+ JUT_WARN_DEVICE(456, 2, "%s","Lost wave data while playing");
+ mDspCh->free();
+ mDspCh = NULL;
+ delete this;
+ return -1;
+ }
+
+ if (checkBankDispose()) {
+ JUT_WARN_DEVICE(467, 2, "%s", "Lost bank data while playing");
+ mDspCh->free();
+ mDspCh = NULL;
+ delete this;
+ return -1;
+ }
+
+ i_channel->setPauseFlag(mPauseFlag);
+ JASOscillator::EffectParams effect_params;
+ if (mPauseFlag) {
+ if (mOscillators[0].isRelease()) {
+ mDspCh->free();
+ mDspCh = NULL;
+ delete this;
+ return -1;
+ }
+ } else {
+ f32 inc = 32028.5f / JASDriver::getDacRate();
+ mVibrate.incCounter(inc);
+ mTremolo.incCounter(inc);
+ if (mUpdateTimer != 0) {
+ mUpdateTimer--;
+ if (mUpdateTimer == 0 && mCallback != NULL) {
+ mCallback(CB_TIMER, this, i_channel, mCallbackData);
+ }
+ }
+ inc = 48000.0f / JASDriver::getDacRate();
+ for (u32 i = 0; i < 2; i++) {
+ if (mOscillators[i].isValid()) {
+ mOscillators[i].incCounter(inc);
+ effectOsc(i, &effect_params);
+ if (i == 0 && mOscillators[i].isStop()) {
+ mDspCh->free();
+ mDspCh = NULL;
+ delete this;
+ return -1;
+ }
+ }
+ }
+ }
+
+ u16 mixer_volume[6];
+ updateEffectorParam(i_channel, mixer_volume, effect_params);
+ for (u8 i = 0; i < 6; i++) {
+ i_channel->setMixerVolume(i, mixer_volume[i]);
+ }
+
+ f32 pitch = JASCalc::pow2(mParams.field_0x8 + (mKey + mKeySweep) / 12.0f
+ + effect_params._14 * mVibrate.getValue());
+ pitch = mSoundParams.mPitch * effect_params.mPitch * pitch * mParams.mPitch * 4096.0f;
+ if (pitch < 0.0f) {
+ pitch = 0.0f;
+ }
+ i_channel->setPitch(pitch);
+
+ if (!mPauseFlag && mKeySweepCount != 0) {
+ mKeySweep += (mKeySweepTarget - mKeySweep) / mKeySweepCount;
+ mKeySweepCount--;
+ }
+ 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;
+ }
+ param_1 = JASCalc::clamp01(param_1);
+
+ 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);
+}
+
+void JASChannel::updateMixer(f32 i_volume, f32 i_pan, f32 i_fxmix, f32 i_dolby, u16* i_volumeOut) {
+ for (u32 i = 0; i < 6; i++) {
+ f32 volume = i_volume;
+ MixConfig config = mMixConfig[i];
+ if (config.parts.upper == 0) {
+ i_volumeOut[i] = 0;
+ } else {
+ f32 scale;
+
+ if (config.parts.lower0 != 0) {
+ switch (config.parts.lower0) {
+ case 1:
+ scale = i_pan;
+ break;
+ case 2:
+ scale = i_fxmix;
+ break;
+ case 3:
+ scale = i_dolby;
+ break;
+ case 5:
+ scale = 1.0f - i_pan;
+ break;
+ case 6:
+ scale = 1.0f - i_fxmix;
+ break;
+ case 7:
+ scale = 1.0f - i_dolby;
+ break;
+ }
+
+ switch (config.parts.lower0) {
+ case 2:
+ case 6:
+ volume *= scale;
+ break;
+ default:
+ if (JASDriver::getOutputMode() == 0) {
+ volume *= scale;
+ } else {
+ volume *= JMASinRadian(scale * JGeometry::TUtil<f32>::PI() * 0.5f);
+ }
+ break;
+ }
+ }
+
+ if (config.parts.lower1 != 0) {
+ switch (config.parts.lower1) {
+ case 1:
+ scale = i_pan;
+ break;
+ case 2:
+ scale = i_fxmix;
+ break;
+ case 3:
+ scale = i_dolby;
+ break;
+ case 5:
+ scale = 1.0f - i_pan;
+ break;
+ case 6:
+ scale = 1.0f - i_fxmix;
+ break;
+ case 7:
+ scale = 1.0f - i_dolby;
+ break;
+ }
+
+ switch (config.parts.lower1) {
+ case 3:
+ case 7:
+ volume *= JMASinRadian((scale * 0.34776f + 0.32612f)
+ * JGeometry::TUtil<f32>::PI() * 0.5f);
+ break;
+ case 2:
+ case 6:
+ volume *= scale;
+ break;
+ default:
+ if (JASDriver::getOutputMode() == 0) {
+ volume *= scale;
+ } else {
+ volume *= JMASinRadian(scale * JGeometry::TUtil<f32>::PI() * 0.5f);
+ }
+ break;
+ }
+ }
+
+ volume = JASCalc::clamp01(volume);
+ i_volumeOut[i] = volume * JASDriver::getChannelLevel_dsp();
+ }
+ }
+}
+
+void JASChannel::free() {
+ JUT_ASSERT(661, mStatus == STATUS_RELEASE || mStatus == STATUS_STOP);
+ mCallback = NULL;
+ mCallbackData = NULL;
+}
+
+void JASChannel::initBankDisposeMsgQueue() {
+ OSInitMessageQueue(&sBankDisposeMsgQ, sBankDisposeMsg, 0x10);
+ sBankDisposeListSize = 0;
+}
+
+void JASChannel::receiveBankDisposeMsg() {
+ OSMessage msg;
+ for (sBankDisposeListSize = 0;
+ OSReceiveMessage(&sBankDisposeMsgQ, &msg, OS_MESSAGE_NOBLOCK);
+ sBankDisposeListSize++)
+ {
+ sBankDisposeList[sBankDisposeListSize] = msg;
+ }
+}
+
+bool JASChannel::checkBankDispose() const {
+ if (mBankDisposeID == NULL) {
+ return false;
+ }
+ for (int i = 0; i < sBankDisposeListSize; i++) {
+ if (mBankDisposeID == sBankDisposeList[i]) {
+ return true;
+ }
+ }
+ return false;
+}