diff --git a/configure.py b/configure.py index 1167fa86c..3f91c4e8a 100644 --- a/configure.py +++ b/configure.py @@ -745,7 +745,7 @@ def JSystemLib(lib_name, objects): Object(Matching, "JSystem/JAudio/JASChAllocQueue.cpp"), Object(NonMatching, "JSystem/JAudio/JASChannel.cpp"), Object(NonMatching, "JSystem/JAudio/JASChannelMgr.cpp"), - Object(NonMatching, "JSystem/JAudio/JASOscillator.cpp"), + Object(Matching, "JSystem/JAudio/JASOscillator.cpp"), Object(Matching, "JSystem/JAudio/JASDriverTables.cpp"), Object(Matching, "JSystem/JAudio/dspproc.c", extra_cflags="-lang c++ -O4 -func_align 32"), Object(Matching, "JSystem/JAudio/dsptask.c", extra_cflags="-lang c++ -O4 -func_align 32"), diff --git a/include/JSystem/JAudio/JASOscillator.h b/include/JSystem/JAudio/JASOscillator.h index d3aaaab94..bf7ee9de4 100644 --- a/include/JSystem/JAudio/JASOscillator.h +++ b/include/JSystem/JAudio/JASOscillator.h @@ -9,8 +9,8 @@ namespace JASystem { struct Osc_ { /* 0x00 */ u8 field_0x0; /* 0x04 */ f32 field_0x4; - /* 0x08 */ void* table; - /* 0x0C */ void* rel_table; + /* 0x08 */ s16* table; + /* 0x0C */ s16* rel_table; /* 0x10 */ f32 field_0x10; /* 0x14 */ f32 field_0x14; }; @@ -22,18 +22,31 @@ namespace JASystem { f32 getOffset(); bool forceStop(); bool release(); - void calc(s16*); + f32 calc(s16*); - /* 0x00 */ const Osc_* field_0x0; - /* 0x04 */ u8 field_0x4; + const Osc_* getOsc() const { return mOsc; } + void setOsc(const Osc_* osc) { mOsc = osc; } + BOOL isOsc() { return mOsc != NULL ? TRUE : FALSE; } + void releaseDirect(u16 param_0) { mDirectRelease = param_0; } + void bankOscToOfs() {} + void getEffectorKind() const {} + void getOscMode() const {} + + static s16 oscTableForceStop[6]; + static const f32 relTableSampleCell[17]; + static const f32 relTableSqRoot[17]; + static const f32 relTableSquare[17]; + + /* 0x00 */ const Osc_* mOsc; + /* 0x04 */ u8 mState; /* 0x05 */ u8 field_0x5; - /* 0x06 */ short field_0x6; - /* 0x08 */ f32 field_0x8; - /* 0x0C */ f32 field_0xc; - /* 0x10 */ f32 field_0x10; - /* 0x14 */ f32 field_0x14; - /* 0x18 */ u16 field_0x18; - /* 0x1C */ f32 field_0x1c; + /* 0x06 */ u16 field_0x6; + /* 0x08 */ f32 mReleaseRate; + /* 0x0C */ f32 mPhase; + /* 0x10 */ f32 mTargetPhase; + /* 0x14 */ f32 mPhaseChangeRate; + /* 0x18 */ u16 mDirectRelease; + /* 0x1C */ f32 mInitialReleaseRate; }; } diff --git a/src/JSystem/JAudio/JASChannel.cpp b/src/JSystem/JAudio/JASChannel.cpp index 18a9ae3b9..09190eb2f 100644 --- a/src/JSystem/JAudio/JASChannel.cpp +++ b/src/JSystem/JAudio/JASChannel.cpp @@ -47,7 +47,7 @@ void JASystem::TChannel::init() { } for (u32 i = 0; i < 4; i++) { JUT_ASSERT(155, osc[i]); - osc[i]->field_0x0 = NULL; + osc[i]->setOsc(NULL); osc[i]->init(); } mPauseFlag = 0; @@ -67,7 +67,7 @@ void JASystem::TChannel::setOscillator(u32 oscnum, TOscillator* param_2) { /* 8028B620-8028B6A8 .text setOscInit__Q28JASystem8TChannelFUlPCQ38JASystem11TOscillator4Osc_ */ void JASystem::TChannel::setOscInit(u32 oscnum, const TOscillator::Osc_* param_2) { JUT_ASSERT(183, oscnum < (4)); - osc[oscnum]->field_0x0 = param_2; + osc[oscnum]->setOsc(param_2); osc[oscnum]->initStart(); } @@ -75,27 +75,28 @@ void JASystem::TChannel::setOscInit(u32 oscnum, const TOscillator::Osc_* param_2 bool JASystem::TChannel::forceStopOsc(u32 numosc) { /* Nonmatching */ JUT_ASSERT(195, numosc < (4)); - return osc[numosc]->field_0x0 != NULL ? osc[numosc]->forceStop() : false; + return osc[numosc]->isOsc() ? osc[numosc]->forceStop() : false; } /* 8028B73C-8028B7D0 .text releaseOsc__Q28JASystem8TChannelFUl */ bool JASystem::TChannel::releaseOsc(u32 numosc) { /* Nonmatching */ JUT_ASSERT(209, numosc < (4)); - return osc[numosc]->field_0x0 ? osc[numosc]->release() : false; + return osc[numosc]->isOsc() ? osc[numosc]->release() : false; } /* 8028B7D0-8028B850 .text directReleaseOsc__Q28JASystem8TChannelFUlUs */ void JASystem::TChannel::directReleaseOsc(u32 oscnum, u16 param_2) { JUT_ASSERT(224, oscnum < (4)); - osc[oscnum]->field_0x18 = param_2; + osc[oscnum]->releaseDirect(param_2); } /* 8028B850-8028B8E4 .text bankOscToOfs__Q28JASystem8TChannelFUl */ f32 JASystem::TChannel::bankOscToOfs(u32 oscnum) { /* Nonmatching */ JUT_ASSERT(234, oscnum < (4)); - return osc[oscnum]->field_0x0 ? osc[oscnum]->getOffset() : 1.0f; + // Probably uses inline JASystem::TOscillator::bankOscToOfs + return osc[oscnum]->isOsc() ? osc[oscnum]->getOffset() : 1.0f; } /* 8028B8E4-8028BA98 .text effectOsc__Q28JASystem8TChannelFUlf */ @@ -107,14 +108,14 @@ void JASystem::TChannel::effectOsc(u32 oscnum, f32 param_2) { int JASystem::TChannel::getOscState(u32 oscnum) const { /* Nonmatching */ JUT_ASSERT(274, oscnum < (4)); - return osc[oscnum]->field_0x4; + return osc[oscnum]->mState; } /* 8028BB14-8028BB98 .text isOsc__Q28JASystem8TChannelFUl */ BOOL JASystem::TChannel::isOsc(u32 oscnum) { /* Nonmatching */ JUT_ASSERT(284, oscnum < (4)); - return osc[oscnum]->field_0x0 != 0; + return osc[oscnum]->isOsc(); } /* 8028BB98-8028BC78 .text copyOsc__Q28JASystem8TChannelFUlPQ38JASystem11TOscillator4Osc_ */ @@ -122,7 +123,7 @@ void JASystem::TChannel::copyOsc(u32 oscnum, TOscillator::Osc_* param_2) { /* Nonmatching */ JUT_ASSERT(295, oscnum < (4)); if (isOsc(oscnum)) { - *param_2 = *osc[oscnum]->field_0x0; + *param_2 = *osc[oscnum]->getOsc(); } else { OSReport("osc[%d] is NULL\n", oscnum); } diff --git a/src/JSystem/JAudio/JASOscillator.cpp b/src/JSystem/JAudio/JASOscillator.cpp index 73455b03e..2c8ec371c 100644 --- a/src/JSystem/JAudio/JASOscillator.cpp +++ b/src/JSystem/JAudio/JASOscillator.cpp @@ -4,59 +4,275 @@ // #include "JSystem/JAudio/JASOscillator.h" +#include "JSystem/JAudio/JASDriverIF.h" +#include "JSystem/JAudio/JASRate.h" +#include "dolphin/os/OS.h" +#include "dolphin/types.h" +#include "math.h" + +s16 JASystem::TOscillator::oscTableForceStop[] = { + 0, 15, 0, + 15, 0, 0 +}; + +const f32 JASystem::TOscillator::relTableSampleCell[] = { + 1.0f, 0.970489f, 0.781274f, 0.546281f, 0.399792f, 0.289315f, + 0.212104f, 0.157476f, 0.112613f, 0.0817896f, 0.0579852f, 0.0436415f, + 0.0308237f, 0.0237129f, 0.0152593f, 0.00915555f, +}; + +// relTableSqRoot[i] = pow(1 - i/16, 2) +const f32 JASystem::TOscillator::relTableSqRoot[] = { + 1.0f, 0.878906f, 0.765625f, 0.660156f, + 0.5625f, 0.472656f, 0.390625f, 0.316406f, + 0.25f, 0.191406f, 0.140625f, 0.0976562f, + 0.0625f, 0.0351562f, 0.015625f, 0.00390625, +}; + +// relTableSquare[i] = sqrt(1 - i/16) +const f32 JASystem::TOscillator::relTableSquare[] = { + 1.0f, 0.968246f, 0.935414f, 0.901388f, + 0.866025f, 0.829156f, 0.790569f, 0.75f, + 0.707107f, 0.661438f, 0.612372f, 0.559017f, + 0.5f, 0.433013f, 0.353553f, 0.25f, +}; /* 8028DE94-8028DECC .text init__Q28JASystem11TOscillatorFv */ void JASystem::TOscillator::init() { - field_0x0 = NULL; - field_0x4 = 1; + mOsc = NULL; + mState = 1; field_0x5 = 0; field_0x6 = 0; - field_0x8 = 0.0f; - field_0xc = 0.0f; - field_0x10 = 0.0f; - field_0x14 = 0.0f; - field_0x18 = 0; - field_0x1c = 0.0f; + mReleaseRate = 0.0f; + mPhase = 0.0f; + mTargetPhase = 0.0f; + mPhaseChangeRate = 0.0f; + mDirectRelease = 0; + mInitialReleaseRate = 0.0f; } /* 8028DECC-8028DF2C .text initStart__Q28JASystem11TOscillatorFv */ void JASystem::TOscillator::initStart() { - field_0x4 = 2; - field_0x18 = 0; - if (!field_0x0 || !field_0x0->table) { - field_0xc = 0.0f; + mState = 2; + mDirectRelease = 0; + if (mOsc == NULL || !mOsc->table) { + mPhase = 0.0f; return; } field_0x6 = 0; - field_0x8 = 0.0f; - field_0x10 = 0.0f; - field_0x18 = 0; - field_0x8 -= field_0x0->field_0x4; + mReleaseRate = 0.0f; + mTargetPhase = 0.0f; + mDirectRelease = 0; + mReleaseRate -= mOsc->field_0x4; } /* 8028DF2C-8028E070 .text getOffset__Q28JASystem11TOscillatorFv */ f32 JASystem::TOscillator::getOffset() { - /* Nonmatching */ + if (mOsc == NULL) { + OSReport("----- Oscillator is NULL\n"); + mPhase = 1.0f; + return 1.0f; + } + + switch (mState) { + case 0: + return 1.0f; + case 3: + return mOsc->field_0x14 + (mPhase * mOsc->field_0x10); + + case 1: + mState = 2; + OSReport("----- Error Initialize\n"); + /* Fallthrough */ + default: + s16* var_r4; + if (mState == 4) { + var_r4 = mOsc->rel_table; + } else if (mState == 5) { + var_r4 = oscTableForceStop; + } else { + var_r4 = mOsc->table; + } + + if (var_r4 == NULL && mState != 6) { + mPhase = 1.0f; + return 1.0f; + } + + if (mState == 5) { + mReleaseRate -= 1.0f; + } else { + mReleaseRate -= mOsc->field_0x4; + } + + return calc(var_r4); + + } } /* 8028E070-8028E0AC .text forceStop__Q28JASystem11TOscillatorFv */ bool JASystem::TOscillator::forceStop() { - if (field_0x4 == 5) { + if (mState == 5) { return false; } field_0x6 = 0; - field_0x8 = 0.0f; - field_0x10 = field_0xc; - field_0x4 = 5; + mReleaseRate = 0.0f; + mTargetPhase = mPhase; + mState = 5; return true; } /* 8028E0AC-8028E238 .text release__Q28JASystem11TOscillatorFv */ bool JASystem::TOscillator::release() { - /* Nonmatching */ + if (mState == 5) { + return false; + } + + if (mOsc->table != mOsc->rel_table) { + field_0x6 = 0; + mReleaseRate = 0.0f; + mTargetPhase = mPhase; + } + + if (mOsc->rel_table == NULL && mDirectRelease == 0) { + mDirectRelease = 0x10; + } + + if (mDirectRelease != 0) { + mState = 6; + field_0x5 = (mDirectRelease >> 14) & 3; + + f32 temp_f31 = mDirectRelease & 0x3FFF; + temp_f31 *= ((Kernel::getDacRate() / 80.0f) / 600.0f); + temp_f31 /= Driver::getUpdateInterval(); + mReleaseRate = temp_f31; + + if (mReleaseRate < 1.0f) { + mReleaseRate = 1.0f; + } + + mInitialReleaseRate = mReleaseRate; + mTargetPhase = 0.0f; + if (field_0x5 == 0) { + mPhaseChangeRate = (mTargetPhase - mPhase) / mReleaseRate; + } else { + mPhaseChangeRate = mTargetPhase - mPhase; + } + } else { + mState = 4; + } + + return true; } /* 8028E238-8028E5EC .text calc__Q28JASystem11TOscillatorFPs */ -void JASystem::TOscillator::calc(s16*) { - /* Nonmatching */ +f32 JASystem::TOscillator::calc(s16* i_table) { + while (mReleaseRate <= 0.0f) { + int idx = field_0x6 * 3; + mPhase = mTargetPhase; + if (mState == 6) { + mState = 0; + break; + } + + s16 envMode = i_table[idx]; + s16 envTime = i_table[idx + 1]; + s16 envValue = i_table[idx + 2]; + + if (envMode == 13) { + field_0x6 = envValue; + continue; + } + + if (envMode == 15) { + mState = 0; + break; + } + + if (envMode == 14) { + mState = 3; + return mOsc->field_0x14 + mPhase * mOsc->field_0x10; + } + + field_0x5 = envMode; + + if (envTime == 0) { + mTargetPhase = envValue / 32768.0f; + field_0x6 += 1; + continue; + } + + mReleaseRate = envTime * ((Kernel::getDacRate() / 80.0f) / 600.0f) / Driver::getUpdateInterval(); + mInitialReleaseRate = mReleaseRate; + mTargetPhase = envValue / 32768.0f; + + if (field_0x5 == 0) { + mPhaseChangeRate = (mTargetPhase - mPhase) / mReleaseRate; + } else { + mPhaseChangeRate = mTargetPhase - mPhase; + } + + field_0x6 += 1; + } + + if (mOsc->field_0x10 == 0.0f) { + return mOsc->field_0x14; + } + + + f32 temp_f31 = 0.0f; + f32 newPhase; + if (mInitialReleaseRate == 0.0) { // Developer probably forgot the f suffix + newPhase = mTargetPhase; + mPhase = mTargetPhase; + } else { + if (field_0x5 == 0 || (temp_f31 = mPhaseChangeRate) == 0.0f) { + newPhase = mTargetPhase - (mPhaseChangeRate * mReleaseRate); + mPhase = newPhase; + } else if (field_0x5 == 3 || field_0x5 == 1 || field_0x5 == 2) { + const f32* table = NULL; + + switch (field_0x5) { + case 3: + table = relTableSampleCell; + break; + case 1: + table = relTableSquare; + break; + case 2: + table = relTableSqRoot; + break; + } + + f32 fIdx; + + if (temp_f31 < 0.0f) { + fIdx = 16.0f * (1.0f - (mReleaseRate / mInitialReleaseRate)); + } else { + fIdx = 16.0f * (mReleaseRate / mInitialReleaseRate); + } + + u32 idx = fIdx; + f32 prop = fIdx - (f32)idx; + if (idx >= 16) { + idx = 15; + prop = 1.0f; + } + + f32 valAbs = std::fabsf(temp_f31 * (prop * (table[idx + 1] - table[idx]) + table[idx])); + + if (mPhaseChangeRate < 0.0f) { + newPhase = mTargetPhase + valAbs; + } else { + newPhase = mTargetPhase - (mPhaseChangeRate - valAbs); + } + + mPhase = newPhase; + } else { + newPhase = mTargetPhase - (temp_f31 * mReleaseRate); + mPhase = newPhase; + } + } + + return newPhase * mOsc->field_0x10 + mOsc->field_0x14; } diff --git a/src/JSystem/JAudio/JASPlayer_impl.cpp b/src/JSystem/JAudio/JASPlayer_impl.cpp index 6252b85bc..152412cfa 100644 --- a/src/JSystem/JAudio/JASPlayer_impl.cpp +++ b/src/JSystem/JAudio/JASPlayer_impl.cpp @@ -65,15 +65,15 @@ const JASystem::TOscillator::Osc_ JASystem::Player::sAdsrDef = { }; const JASystem::TOscillator::Osc_ JASystem::Player::sEnvelopeDef = { - 0, 1.0f, NULL, &sRelTable, 1.0f, 0.0f + 0, 1.0f, NULL, sRelTable, 1.0f, 0.0f }; const JASystem::TOscillator::Osc_ JASystem::Player::sVibratoDef = { - 1, 0.5f, &sVibTable, &sVibTable, 0.0f, 1.0f + 1, 0.5f, sVibTable, sVibTable, 0.0f, 1.0f }; const JASystem::TOscillator::Osc_ JASystem::Player::sTremoroDef = { - 0, 0.5f, &sTreTable, &sTreTable, 0.0f, 1.0f + 0, 0.5f, sTreTable, sTreTable, 0.0f, 1.0f }; s16 JASystem::Player::CUTOFF_TO_IIR_TABLE[] = { diff --git a/src/JSystem/JAudio/JASTrack.cpp b/src/JSystem/JAudio/JASTrack.cpp index 69809c617..78b323b8f 100644 --- a/src/JSystem/JAudio/JASTrack.cpp +++ b/src/JSystem/JAudio/JASTrack.cpp @@ -325,7 +325,7 @@ void JASystem::TTrack::overwriteOsc(TChannel* param_1) { } param_1->copyOsc(r28, &field_0x2cc[i]); } else if (var1 & 4) { - void* var4 = field_0x2cc[i].rel_table; + s16* var4 = field_0x2cc[i].rel_table; if (!param_1->isOsc(r28)) { JUT_WARN(603, "%s", "cannot copy osc"); continue; @@ -390,7 +390,7 @@ void JASystem::TTrack::oscSetupFull(u8 param_1, u32 param_2, u32 param_3) { if (param_2 == 0) { field_0x2cc[var1].table = NULL; } - field_0x2cc[var1].table = mSeqCtrl.mRawFilePtr + param_2; + field_0x2cc[var1].table = (s16*)(mSeqCtrl.mRawFilePtr + param_2); } if (!var5) { return; @@ -398,7 +398,7 @@ void JASystem::TTrack::oscSetupFull(u8 param_1, u32 param_2, u32 param_3) { if (param_3 == 0) { field_0x2cc[var1].rel_table = Player::sRelTable; } - field_0x2cc[var1].rel_table = mSeqCtrl.mRawFilePtr + param_2; + field_0x2cc[var1].rel_table = (s16*)(mSeqCtrl.mRawFilePtr + param_2); } /* 802817E4-80281850 .text oscSetupSimpleEnv__Q28JASystem6TTrackFUcUl */ @@ -406,10 +406,10 @@ void JASystem::TTrack::oscSetupSimpleEnv(u8 param_1, u32 param_2) { switch (param_1) { case 0: field_0x2cc[0] = Player::sEnvelopeDef; - field_0x2cc[0].table = mSeqCtrl.mRawFilePtr + param_2; + field_0x2cc[0].table = (s16*)(mSeqCtrl.mRawFilePtr + param_2); break; case 1: - field_0x2cc[0].rel_table = mSeqCtrl.mRawFilePtr + param_2; + field_0x2cc[0].rel_table = (s16*)(mSeqCtrl.mRawFilePtr + param_2); break; } }