summaryrefslogtreecommitdiff
path: root/src/Z2AudioCS
diff options
context:
space:
mode:
authorMax Roncace <me@caseif.net>2026-02-20 05:53:27 -0500
committerGitHub <noreply@github.com>2026-02-20 02:53:27 -0800
commit803bc041c7e228e2b7c88afd1121550e51db3f82 (patch)
tree5462e8061bc8a8084db8ea2fc677a0fef5bb16d1 /src/Z2AudioCS
parent0558bde1e61374f5a1b54453f07fe6cc15525bc1 (diff)
Implement Z2AudioCS (#3103)
Diffstat (limited to 'src/Z2AudioCS')
-rw-r--r--src/Z2AudioCS/SpkData.cpp37
-rw-r--r--src/Z2AudioCS/SpkMixingBuffer.cpp51
-rw-r--r--src/Z2AudioCS/SpkSound.cpp450
-rw-r--r--src/Z2AudioCS/SpkSpeakerCtrl.cpp314
-rw-r--r--src/Z2AudioCS/SpkSystem.cpp111
-rw-r--r--src/Z2AudioCS/SpkTable.cpp44
-rw-r--r--src/Z2AudioCS/SpkWave.cpp83
-rw-r--r--src/Z2AudioCS/Z2AudioCS.cpp162
8 files changed, 1252 insertions, 0 deletions
diff --git a/src/Z2AudioCS/SpkData.cpp b/src/Z2AudioCS/SpkData.cpp
new file mode 100644
index 0000000000..7f426bda78
--- /dev/null
+++ b/src/Z2AudioCS/SpkData.cpp
@@ -0,0 +1,37 @@
+#include "Z2AudioCS/SpkData.h"
+
+#include "JSystem/JUtility/JUTAssert.h"
+
+SpkData::SpkData(JKRArchive* resArc) {
+ mResArc = resArc;
+ JUT_ASSERT(21, resArc);
+}
+
+void SpkData::loadTable(u16 chan) {
+ JUT_ASSERT(32, mResArc);
+
+ void* res = mResArc->getResource(chan);
+ JUT_ASSERT(35, res);
+
+ mTableMgr.setResource(res);
+}
+
+void SpkData::loadWave(u16 chan) {
+ JUT_ASSERT(48, mResArc);
+
+ void* res = mResArc->getResource(chan);
+ JUT_ASSERT(51, res);
+
+ mWaveMgr.setResource(res);
+}
+
+BOOL SpkData::isValid(void) const {
+ return mTableMgr.isValid() && mWaveMgr.isValid();
+}
+
+static void dummy(SpkWave* wave, SpkTable* table) {
+ if (wave->isValid() && table->isValid()) {
+ table->getParams(0);
+ table->getNumOfSound();
+ }
+}
diff --git a/src/Z2AudioCS/SpkMixingBuffer.cpp b/src/Z2AudioCS/SpkMixingBuffer.cpp
new file mode 100644
index 0000000000..3118f07f94
--- /dev/null
+++ b/src/Z2AudioCS/SpkMixingBuffer.cpp
@@ -0,0 +1,51 @@
+#include "Z2AudioCS/SpkMixingBuffer.h"
+
+#include "Z2AudioCS/SpkSound.h"
+#include "JSystem/JAudio2/JASCalc.h"
+#include "JSystem/JAudio2/JASGadget.h"
+#include "JSystem/JAudio2/JASHeapCtrl.h"
+
+SpkMixingBuffer::SpkMixingBuffer(JKRHeap* heap) {
+ JUT_ASSERT(25, heap);
+ for (s32 chan = 0; chan < ARRAY_SIZE(mBuffer); chan++) {
+ mBuffer[chan] = new (heap, 0) s16[cSamplesPerAudioPacket];
+ JUT_ASSERT(29, mBuffer[chan]);
+ bzeroBuffer(chan);
+ }
+}
+
+void SpkMixingBuffer::mix(s32 chan, s16* src, s32 len, f32 weight,
+ s32 offset) {
+ JUT_ASSERT(61, chan < 4);
+ JUT_ASSERT(62, mBuffer[chan]);
+ JUT_ASSERT(63, src);
+ JUT_ASSERT(64, offset >= 0);
+ JUT_ASSERT(65, (offset+len) <= cSamplesPerAudioPacket);
+
+ for (s32 i = 0; i < len; i++) {
+ s32 sample = mBuffer[chan][i + offset];
+ s16 srcSample = src[i];
+ sample += (s32)(srcSample * weight);
+ mBuffer[chan][i + offset] = JASCalc::clamp<s16, s32>(sample);
+ }
+}
+
+s16* SpkMixingBuffer::getSamples(s32 chan) const {
+ JUT_ASSERT(97, chan < 4);
+ JUT_ASSERT(98, mBuffer[chan]);
+ return mBuffer[chan];
+}
+
+bool SpkMixingBuffer::update(s32 chan) {
+ bzeroBuffer(chan);
+ SpkSoundHolder* sound_holder = JASGlobalInstance<SpkSoundHolder>::getInstance();
+ JUT_ASSERT(121, sound_holder);
+ bool result = sound_holder->update(chan);
+ return result;
+}
+
+void SpkMixingBuffer::bzeroBuffer(s32 chan) {
+ JUT_ASSERT(143, chan < 4);
+ JUT_ASSERT(144, mBuffer[chan]);
+ JASCalc::bzero(mBuffer[chan], cSamplesPerAudioPacket * sizeof(s16));
+}
diff --git a/src/Z2AudioCS/SpkSound.cpp b/src/Z2AudioCS/SpkSound.cpp
new file mode 100644
index 0000000000..04467f1919
--- /dev/null
+++ b/src/Z2AudioCS/SpkSound.cpp
@@ -0,0 +1,450 @@
+#include "Z2AudioCS/SpkSound.h"
+
+#include "Z2AudioCS/SpkSpeakerCtrl.h"
+#include "Z2AudioCS/SpkSystem.h"
+#include "JSystem/JAudio2/JASCriticalSection.h"
+#include "JSystem/JAudio2/JASHeapCtrl.h"
+
+#define SPK_SOUND_MIX_STEP 40
+
+enum SpkSoundState {
+ SpkSoundState_STARTING_e,
+ SpkSoundState_LOCKED_e,
+ SpkSoundState_UNLOCKED_e,
+ SpkSoundState_STOPPING_e,
+ SpkSoundState_DEAD_e,
+};
+
+void SpkSoundHandle::releaseSound() {
+ if (!mSound) {
+ return;
+ }
+
+ mSound->mHandle = NULL;
+ mSound = NULL;
+}
+
+SpkSoundVolume::SpkSoundVolume() {
+ field_0x00 = 1.0f;
+ field_0x04 = 0.0f;
+ field_0x08 = 1.0f;
+ field_0x0c = 0.0f;
+ field_0x10 = 1.0f;
+ field_0x14 = 1.0f;
+ field_0x18 = 0.0f;
+ mTableVolume = 1.0f;
+}
+
+void SpkSoundVolume::setRelease(s32 release) {
+ if (release > 0) {
+ field_0x04 = 1.0f / release;
+ } else {
+ field_0x04 = 1.0f;
+ }
+
+ field_0x00 = 1.0f;
+}
+
+void SpkSoundVolume::setFadeOut(s32 fadeOut) {
+ JASCriticalSection cs;
+
+ if (fadeOut > 0) {
+ field_0x0c = 1.0f / fadeOut;
+ } else {
+ field_0x0c = 1.0f;
+ }
+
+ field_0x08 = 1.0f;
+}
+
+f32 SpkSoundVolume::calc(bool& param_0) {
+ param_0 = false;
+
+ if (field_0x04 != 0.0f) {
+ field_0x00 -= field_0x04;
+ if (field_0x00 <= 0.0f) {
+ field_0x00 = 0.0f;
+ field_0x04 = 0.0f;
+ param_0 = true;
+ }
+ }
+
+ if (field_0x0c != 0.0f) {
+ field_0x08 -= field_0x0c;
+ if (field_0x08 <= 0.0f) {
+ field_0x08 = 0.0f;
+ field_0x0c = 0.0f;
+ param_0 = true;
+ }
+ }
+
+ if (field_0x18 != 0.0f) {
+ field_0x10 += field_0x18;
+ if (field_0x18 >= 0.0f) {
+ if (field_0x10 >= field_0x14) {
+ field_0x10 = field_0x14;
+ field_0x18 = 0.0f;
+ }
+ } else if (field_0x10 <= field_0x14) {
+ field_0x10 = field_0x14;
+ field_0x18 = 0.0f;
+ }
+ }
+
+ f32 result = mTableVolume * (field_0x10 * (field_0x08 * (field_0x00 * field_0x00)));
+ return result;
+}
+
+void SpkSoundVolume::setTableVolume(f32 vol) {
+ mTableVolume = vol;
+}
+
+SpkSound::SpkSound() : JSULink<SpkSound>(this),
+ mHandle(NULL), mSoundNum(-1), mWaveData(0), mCurPos(0),
+ field_0x20(0), mPriority(100), field_0x28(0), mWaveSize(0),
+ mWaveLoopStart(-1), mWaveLoopEnd(-1), mState(SpkSoundState_STARTING_e),
+ mLifeTime(-1) {
+}
+
+SpkSound::~SpkSound() {
+ releaseHandle();
+}
+
+void SpkSound::update(f32 param_0) {
+ if (!((SpkSoundState)mState != SpkSoundState_UNLOCKED_e &&
+ (SpkSoundState)mState != SpkSoundState_STOPPING_e)) {
+ SpkSystem* system = JASGlobalInstance<SpkSystem>::getInstance();
+ JUT_ASSERT(227, system);
+
+ SpkMixingBuffer* mixing_buffer = system->getMixingBuffer();
+ JUT_ASSERT(230, mixing_buffer);
+
+ bool calc_flag = false;
+ f32 weight = mVolume.calc(calc_flag);
+ if (calc_flag) {
+ mState = SpkSoundState_DEAD_e;
+ }
+ weight *= param_0;
+
+ if (!isLoopWave()) {
+ s32 len = SPK_SOUND_MIX_STEP;
+ if (mCurPos + len >= mWaveSize) {
+ len = mWaveSize - mCurPos;
+ }
+
+ mixing_buffer->mix(field_0x20, mWaveData + mCurPos, len, weight, 0);
+
+ mCurPos += SPK_SOUND_MIX_STEP;
+
+ if (mCurPos >= mWaveSize) {
+ mState = SpkSoundState_DEAD_e;
+ }
+ return;
+ }
+ s32 len = 0;
+ s32 processedBytes = 0;
+ s32 remainingBytes = SPK_SOUND_MIX_STEP;
+ if (mWaveLoopEnd - mCurPos >= SPK_SOUND_MIX_STEP) {
+ len = SPK_SOUND_MIX_STEP;
+ mixing_buffer->mix(field_0x20, mWaveData + mCurPos, len, weight, 0);
+
+ mCurPos += SPK_SOUND_MIX_STEP;
+
+ if (mCurPos >= mWaveLoopEnd) {
+ mCurPos = mWaveLoopStart;
+ }
+ } else {
+ while (remainingBytes > 0) {
+ len = mWaveLoopEnd - mCurPos;
+ if (len >= remainingBytes) {
+ len = remainingBytes;
+ }
+ remainingBytes -= len;
+
+ mixing_buffer->mix(field_0x20, mWaveData + mCurPos, len, weight,
+ processedBytes);
+
+ processedBytes += len;
+ mCurPos += len;
+
+ if (mCurPos >= mWaveLoopEnd) {
+ mCurPos = mWaveLoopStart;
+ }
+ }
+ }
+ }
+}
+
+void SpkSound::stop(s32 msec) {
+ mState = SpkSoundState_STOPPING_e;
+ s32 fade = convertMsecToFrames(msec);
+
+ if (fade) {
+ mVolume.setFadeOut(fade);
+ } else {
+ startRelease();
+ }
+}
+
+void SpkSound::startRelease(void) {
+ mVolume.setRelease(field_0x28);
+}
+
+void SpkSound::attachHandle(SpkSoundHandle* handle) {
+ JUT_ASSERT(338, handle);
+ if (isHandleAttached()) {
+ releaseHandle();
+ }
+
+ mHandle = handle;
+ mHandle->mSound = this;
+}
+
+bool SpkSound::isHandleAttached(void) const {
+ return mHandle != NULL;
+}
+
+void SpkSound::releaseHandle() {
+ if (!mHandle) {
+ return;
+ }
+
+ mHandle->mSound = NULL;
+ mHandle = NULL;
+}
+
+bool SpkSound::start(s32 param_0, s32 soundNum) {
+ field_0x20 = param_0;
+
+ bool setResult = setWaveData(soundNum);
+ if (!setResult) {
+ return false;
+ }
+
+ mState = SpkSoundState_LOCKED_e;
+ return true;
+}
+
+void SpkSound::kill(void) {
+ mState = SpkSoundState_DEAD_e;
+}
+
+bool SpkSound::isStopping(void) const {
+ return mState == SpkSoundState_STOPPING_e;
+}
+
+bool SpkSound::isDead(void) const {
+ return mState == SpkSoundState_DEAD_e;
+}
+
+bool SpkSound::isLocked(void) const {
+ return mState == SpkSoundState_LOCKED_e;
+}
+
+void SpkSound::unlock() {
+ if (mState != SpkSoundState_LOCKED_e) {
+ return;
+ }
+
+ mState = SpkSoundState_UNLOCKED_e;
+}
+
+s32 SpkSound::getLifeTime(void) {
+ return mLifeTime;
+}
+
+void SpkSound::setLifeTime(s32 lifeTime) {
+ mLifeTime = lifeTime;
+}
+
+void SpkSound::updateLifeTime() {
+ mLifeTime--;
+}
+
+s32 SpkSound::convertMsecToFrames(s32 msec) {
+ return 6 * msec / 40;
+}
+
+inline bool SpkSound::isLoopWave(void) {
+ return mWaveLoopStart >= 0 && mWaveLoopEnd > mWaveLoopStart;
+}
+
+bool SpkSound::setWaveData(s32 soundNum) {
+ SpkSystem* system = JASGlobalInstance<SpkSystem>::getInstance();
+ JUT_ASSERT(432, system);
+
+ SpkData* data = system->getData();
+ JUT_ASSERT(435, data);
+
+ if (data->isValid() == 0) {
+ JUT_WARN(438, "%s", "Speaker data is not valid\n");
+ return false;
+ }
+
+ SpkTable& table = data->getTableMgr();
+ JUT_ASSERT(444, soundNum < table.getNumOfSound());
+
+ mSoundNum = soundNum;
+ s32 waveNum = table.getParams(soundNum)->mWaveNum;
+ mPriority = table.getParams(soundNum)->field_0x02;
+ mVolume.setTableVolume(table.getParams(soundNum)->mVolume / 127.0f);
+ field_0x28 = convertMsecToFrames(table.getParams(soundNum)->mMsec);
+
+ SpkWave& wave = data->getWaveMgr();
+ JUT_ASSERT(457, mSoundNum < wave.getNumOfWaves());
+ mWaveData = wave.getWave(waveNum);
+ mCurPos = 0;
+ mWaveSize = wave.getWaveSize(waveNum) / 2;
+ mWaveLoopStart = wave.getLoopStartPos(waveNum);
+ mWaveLoopEnd = wave.getLoopEndPos(waveNum);
+
+ JUT_ASSERT(466, mWaveLoopEnd <= mWaveSize);
+
+ return true;
+}
+
+SpkSoundHolder::SpkSoundHolder() : JASGlobalInstance(true) {
+ mMasterVolume = 1.0f;
+ mConfigVolume = 10;
+
+ for (int i = 0; i < ARRAY_SIZE(mSoundVolumes); i++) {
+ mSoundVolumes[i] = 1.0f;
+ }
+}
+
+bool SpkSoundHolder::startSound(s32 chan, s32 soundNum, SpkSoundHandle* handle) {
+ if (!SpkSpeakerCtrl::isEnable(chan)) {
+ return false;
+ }
+
+ if (soundNum < 0) {
+ return false;
+ }
+
+ if (handle != NULL) {
+ if (*handle) {
+ if ((*handle)->isLocked()) {
+ (*handle)->kill();
+ } else {
+ (*handle)->stop(0);
+ }
+ }
+ }
+
+ SpkSound* new_sound_p = new SpkSound();
+ if (new_sound_p == NULL) {
+ JUT_WARN(520, "%s", "cannot new SpkSound\n");
+ return false;
+ }
+
+ if (handle != NULL) {
+ new_sound_p->attachHandle(handle);
+ }
+
+ bool startResult = new_sound_p->start(chan, soundNum);
+ if (!startResult) {
+ JUT_WARN(532, "%s", "cannot start SpkSound\n");
+ delete new_sound_p;
+ return false;
+ }
+
+ {
+ JASCriticalSection cs;
+ appendSound(chan, new_sound_p);
+ }
+
+ return true;
+}
+
+void SpkSoundHolder::appendSound(s32 chan, SpkSound* sound) {
+ mSoundList[chan].append(sound);
+}
+
+bool SpkSoundHolder::startLevelSound(s32 chan, s32 soundNum, SpkSoundHandle* handle) {
+ if (handle == NULL) {
+ JUT_WARN(558, "%s", "Spk : No Handle for Level Sound");
+ return false;
+ }
+
+ if (*handle) {
+ if (!(*handle)->isStopping() && !(*handle)->isDead()) {
+ (*handle)->setLifeTime(2);
+ }
+ return true;
+ } else {
+ bool startResult = startSound(chan, soundNum, handle);
+ if (!startResult) {
+ return false;
+ }
+
+ if (*handle) {
+ (*handle)->setLifeTime(2);
+ return true;
+ }
+
+ return false;
+ }
+}
+
+void SpkSoundHolder::stopAll(s32 chan, s32 msec) {
+ JASCriticalSection cs;
+
+ for (JSULink<SpkSound>* it = mSoundList[chan].getFirst(); it != NULL; it = it->getNext()) {
+ if (it->getObject() != NULL) {
+ it->getObject()->stop(msec);
+ }
+ }
+}
+
+bool SpkSoundHolder::update(s32 chan) {
+ JASCriticalSection cs;
+ bool ret = updateEachSound(chan);
+ return ret;
+}
+
+bool SpkSoundHolder::updateEachSound(s32 chan) {
+ bool retval = false;
+
+ f32 volume = getMasterVolume() * getConfigVolumeF32();
+
+ for (JSULink<SpkSound>* it = mSoundList[chan].getFirst(); it != NULL; it = it->getNext()) {
+ it->getObject()->update(volume * mSoundVolumes[chan]);
+ retval = true;
+ }
+
+ return retval;
+}
+
+void SpkSoundHolder::freeDeadSound(s32 chan) {
+ JSULink<SpkSound>* it = mSoundList[chan].getFirst();
+ while (it != NULL) {
+ JSULink<SpkSound>* next = it->getNext();
+ if (it->getObject()->isDead()) {
+ mSoundList[chan].remove(it);
+ delete it->getObject();
+ }
+ it = next;
+ }
+}
+
+void SpkSoundHolder::framework(void) {
+ JASCriticalSection cs;
+
+ for (int i = 0; i < ARRAY_SIZE(mSoundList); i++) {
+ for (JSULink<SpkSound>* it = mSoundList[i].getFirst(); it != NULL; it = it->getNext()) {
+ if (it->getObject() != NULL) {
+ if (it->getObject()->isLocked()) {
+ it->getObject()->unlock();
+ } else if (it->getObject()->getLifeTime() >= 0) {
+ it->getObject()->updateLifeTime();
+ if (it->getObject()->getLifeTime() <= 0) {
+ it->getObject()->stop(0);
+ it->getObject()->setLifeTime(-1);
+ }
+ }
+ }
+ }
+
+ freeDeadSound(i);
+ }
+}
diff --git a/src/Z2AudioCS/SpkSpeakerCtrl.cpp b/src/Z2AudioCS/SpkSpeakerCtrl.cpp
new file mode 100644
index 0000000000..112dfd3a2f
--- /dev/null
+++ b/src/Z2AudioCS/SpkSpeakerCtrl.cpp
@@ -0,0 +1,314 @@
+#include "Z2AudioCS/SpkSpeakerCtrl.h"
+
+#include "Z2AudioCS/SpkMixingBuffer.h"
+#include "Z2AudioCS/SpkSound.h"
+#include "JSystem/JAudio2/JASCriticalSection.h"
+#include <revolution/os.h>
+#include <revolution/wpad.h>
+
+OSAlarm sSpeakerAlarm;
+SpeakerInfo sSpeakerInfo[WPAD_MAX_CONTROLLERS];
+SpkSoundHandle sAdjustSoundHandle[WPAD_MAX_CONTROLLERS];
+SpkMixingBuffer* sMixingBuffer;
+
+#if VERSION == VERSION_WII_USA_R0
+#define SPKSPEAKERCTRL_SET_STATE(s) (void)0
+#else
+#define SPKSPEAKERCTRL_SET_STATE(s) mState = s
+#endif
+
+void SpkSpeakerCtrl::setMixingBuffer(SpkMixingBuffer* pMixingBuffer) {
+ JASCriticalSection cs;
+
+ sMixingBuffer = pMixingBuffer;
+}
+
+void SpkSpeakerCtrl::setup(void) {
+ for (s32 i = 0; i < WPAD_MAX_CONTROLLERS; i++) {
+ sSpeakerInfo[i].mIsConnected = false;
+ sSpeakerInfo[i].mIsPlaying = false;
+ sSpeakerInfo[i].field_0x22 = true;
+ sSpeakerInfo[i].mIsMuted = false;
+#if VERSION != VERSION_WII_USA_R0
+ sSpeakerInfo[i].mState = 0;
+#endif
+ sSpeakerInfo[i].mVolume = 64;
+ sSpeakerInfo[i].mRadioSensitivityTimer = 0;
+ sSpeakerInfo[i].mExtensionTimer = 0;
+ memset(sSpeakerInfo[i].mEncInfo.data, 0, sizeof(sSpeakerInfo[i].mEncInfo));
+ }
+
+ OSCreateAlarm(&sSpeakerAlarm);
+ OSSetPeriodicAlarm(&sSpeakerAlarm, OSGetTime(), OSNanosecondsToTicks(6666667), updateSpeaker);
+}
+
+void SpkSpeakerCtrl::connect(s32 chan) {
+ JASCriticalSection cs;
+
+ sSpeakerInfo[chan].mIsConnected = true;
+ sSpeakerInfo[chan].mIsPlaying = false;
+#if VERSION != VERSION_WII_USA_R0
+ sSpeakerInfo[chan].mState = 0;
+#endif
+ setSpeakerOn(chan);
+}
+
+void SpkSpeakerCtrl::disconnect(s32 chan) {
+ JASCriticalSection cs;
+
+ sSpeakerInfo[chan].mIsConnected = false;
+ sSpeakerInfo[chan].mIsPlaying = false;
+#if VERSION != VERSION_WII_USA_R0
+ sSpeakerInfo[chan].mState = 0;
+#endif
+ setSpeakerOff(chan);
+}
+
+void SpkSpeakerCtrl::setSpeakerOn(s32 chan) {
+#if VERSION != VERSION_WII_USA_R0
+ JASCriticalSection cs;
+#endif
+
+ s32 val = WPADControlSpeaker(chan, 1, setSpeakerOnCallback);
+
+#if VERSION != VERSION_WII_USA_R0
+ if (val == -2) {
+ sSpeakerInfo[chan].mState = 1;
+ } else {
+ sSpeakerInfo[chan].mState = 0;
+ }
+#endif
+}
+
+void SpkSpeakerCtrl::setSpeakerOnCallback(s32 chan, s32 param_1) {
+#if VERSION == VERSION_WII_USA_R0
+ if (param_1 == 0) {
+ setSpeakerPlay(chan);
+ }
+#else
+ JASCriticalSection cs;
+
+ if (param_1 == 0) {
+ sSpeakerInfo[chan].mState = 0;
+ setSpeakerPlay(chan);
+ } else {
+ if (param_1 == -3) {
+ sSpeakerInfo[chan].mState = 1;
+ }
+ }
+#endif
+}
+
+void SpkSpeakerCtrl::setSpeakerPlay(s32 chan) {
+#if VERSION != VERSION_WII_USA_R0
+ JASCriticalSection cs;
+#endif
+
+ s32 val = WPADControlSpeaker(chan, 4, startPlayCallback);
+
+#if VERSION != VERSION_WII_USA_R0
+ if (val == -2) {
+ sSpeakerInfo[chan].mState = 2;
+ } else {
+ sSpeakerInfo[chan].mState = 0;
+ }
+#endif
+}
+
+void SpkSpeakerCtrl::startPlayCallback(s32 chan, s32 param_1) {
+#if VERSION != VERSION_WII_USA_R0
+ JASCriticalSection cs;
+#endif
+
+ if (param_1 == 0) {
+ sSpeakerInfo[chan].mIsPlaying = true;
+ sSpeakerInfo[chan].field_0x22 = true;
+#if VERSION != VERSION_WII_USA_R0
+ sSpeakerInfo[chan].mState = 0;
+#endif
+ memset(&sSpeakerInfo[chan].mEncInfo, 0, 0x20);
+ } else if (param_1 == -3) {
+#if VERSION != VERSION_WII_USA_R0
+ sSpeakerInfo[chan].mState = 2;
+#endif
+ }
+}
+
+void SpkSpeakerCtrl::setSpeakerOff(s32 chan) {
+ sSpeakerInfo[chan].mIsPlaying = false;
+#if VERSION != VERSION_WII_USA_R0
+ sSpeakerInfo[chan].mState = 0;
+#endif
+ WPADControlSpeaker(chan, 0, 0);
+}
+
+#if VERSION != VERSION_WII_USA_R0
+void SpkSpeakerCtrl::retryConnection(s32 chan) {
+ switch (sSpeakerInfo[chan].mState) {
+ case 0:
+ case 3:
+ break;
+ case 1:
+ setSpeakerOn(chan);
+ break;
+ case 2:
+ setSpeakerPlay(chan);
+ break;
+ }
+}
+#endif
+
+void SpkSpeakerCtrl::framework() {
+ for (s32 i = 0; i < WPAD_MAX_CONTROLLERS; i++) {
+ retryConnection(i);
+ }
+}
+
+void SpkSpeakerCtrl::updateSpeaker(OSAlarm*, OSContext*) {
+ for (s32 i = 0; i < WPAD_MAX_CONTROLLERS; i++) {
+ if (sMixingBuffer != NULL) {
+ BOOL isBusy = FALSE;
+
+ if (!checkRadioSensitivity(i)) {
+ isBusy = TRUE;
+ }
+
+ if (!updateExtensionProcess(i)) {
+ isBusy = TRUE;
+ }
+
+ if (sSpeakerInfo[i].mIsMuted) {
+ continue;
+ }
+
+ bool updateResult = sMixingBuffer->update(i);
+ if (isBusy) {
+ continue;
+ }
+
+ if (!sSpeakerInfo[i].mIsPlaying) {
+ continue;
+ }
+
+ if (updateResult) {
+ bool var_r28 = true;
+
+ if (sSpeakerInfo[i].mIsPlaying && sMixingBuffer != NULL) {
+ if (sSpeakerInfo[i].field_0x22) {
+ var_r28 = false;
+ sSpeakerInfo[i].field_0x22 = false;
+ }
+ u8 data[24];
+ WENCGetEncodeData(&sSpeakerInfo[i].mEncInfo, var_r28,
+ sMixingBuffer->getSamples(i), 0x28, data);
+ WPADSendStreamData(i, data, 0x14);
+ }
+ } else {
+ sSpeakerInfo[i].field_0x22 = true;
+ }
+ }
+ }
+}
+
+bool SpkSpeakerCtrl::isEnable(s32 chan) {
+ return WPADIsSpeakerEnabled(chan) &&
+ sSpeakerInfo[chan].mIsPlaying &&
+ !isSubmitPlayByRadioSensitivity(chan) &&
+ !isSubmitPlayByExtensionConnect(chan) &&
+ sMixingBuffer != NULL;
+}
+
+void SpkSpeakerCtrl::extensionProcess(s32 chan, s32 param_1) {
+ if (!sSpeakerInfo[chan].mIsConnected) {
+ return;
+ }
+
+ sSpeakerInfo[chan].mExtensionTimer = 49;
+ sSpeakerInfo[chan].field_0x22 = true;
+ if (param_1 == 0xFF) {
+ sSpeakerInfo[chan].mExtensionTimer = 50;
+ }
+
+ if (sSpeakerInfo[chan].mIsPlaying == 0) {
+ return;
+ }
+
+ setSpeakerOff(chan);
+}
+
+bool SpkSpeakerCtrl::updateExtensionProcess(s32 chan) {
+ if (sSpeakerInfo[chan].mIsConnected == 0) {
+ return false;
+ }
+
+ if (sSpeakerInfo[chan].mExtensionTimer < 50) {
+ if (sSpeakerInfo[chan].mExtensionTimer > 0) {
+ if (sSpeakerInfo[chan].mExtensionTimer == 1 && !sSpeakerInfo[chan].mIsPlaying) {
+ setSpeakerOn(chan);
+ }
+
+ sSpeakerInfo[chan].mExtensionTimer--;
+ } else {
+ sSpeakerInfo[chan].mExtensionTimer = 0;
+ }
+ }
+
+ bool retval;
+ if (sSpeakerInfo[chan].mExtensionTimer == 0) {
+ retval = true;
+ } else {
+ retval = false;
+ }
+ return retval;
+}
+
+bool SpkSpeakerCtrl::isSubmitPlayByExtensionConnect(s32 chan) {
+ if (sSpeakerInfo[chan].mExtensionTimer > 0) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool SpkSpeakerCtrl::checkRadioSensitivity(s32 chan) {
+ if (sSpeakerInfo[chan].mIsConnected == 0) {
+ return false;
+ }
+
+ if (WPADGetRadioSensitivity(chan) < 60) {
+ if (sSpeakerInfo[chan].mIsPlaying == 1) {
+ setSpeakerOff(chan);
+ }
+
+ sSpeakerInfo[chan].mRadioSensitivityTimer = 200;
+ sSpeakerInfo[chan].field_0x22 = true;
+ } else {
+ if (sSpeakerInfo[chan].mRadioSensitivityTimer > 0) {
+ if (sSpeakerInfo[chan].mRadioSensitivityTimer == 1) {
+ if (!sSpeakerInfo[chan].mIsPlaying) {
+ setSpeakerOn(chan);
+ }
+ }
+
+ sSpeakerInfo[chan].mRadioSensitivityTimer--;
+ } else {
+ sSpeakerInfo[chan].mRadioSensitivityTimer = 0;
+ }
+ }
+
+ bool retval;
+ if (sSpeakerInfo[chan].mRadioSensitivityTimer == 0) {
+ retval = true;
+ } else {
+ retval = false;
+ }
+ return retval;
+}
+
+bool SpkSpeakerCtrl::isSubmitPlayByRadioSensitivity(s32 chan) {
+ if (sSpeakerInfo[chan].mRadioSensitivityTimer > 0) {
+ return true;
+ } else {
+ return false;
+ }
+}
diff --git a/src/Z2AudioCS/SpkSystem.cpp b/src/Z2AudioCS/SpkSystem.cpp
new file mode 100644
index 0000000000..9e245f7503
--- /dev/null
+++ b/src/Z2AudioCS/SpkSystem.cpp
@@ -0,0 +1,111 @@
+#include "Z2AudioCS/SpkSystem.h"
+
+#include "Z2AudioCS/SpkMixingBuffer.h"
+#include "Z2AudioCS/SpkSound.h"
+#include "Z2AudioCS/SpkSpeakerCtrl.h"
+#include "JSystem/JAudio2/JASGadget.h"
+#include "JSystem/JAudio2/JASHeapCtrl.h"
+#include "JSystem/JKernel/JKRHeap.h"
+
+template<> SpkSystem* JASGlobalInstance<SpkSystem>::sInstance;
+template<> SpkSoundHolder* JASGlobalInstance<SpkSoundHolder>::sInstance;
+
+const static s32 cConfigVolumeMax = 15;
+
+SpkSystem::SpkSystem(JKRHeap* heap) : JASGlobalInstance(true) {
+ mData = NULL;
+
+ if (!heap) {
+ heap = JKRGetCurrentHeap();
+ }
+
+ mHeap = heap;
+ mMixingBuffer = new (heap, 0) SpkMixingBuffer(heap);
+ JUT_ASSERT(35, mMixingBuffer);
+ mSoundHolder = new (heap, 0) SpkSoundHolder();
+ JUT_ASSERT(38, mSoundHolder);
+ SpkSpeakerCtrl::setup();
+ SpkSpeakerCtrl::setMixingBuffer(mMixingBuffer);
+}
+
+void SpkSystem::setResource(JKRArchive* resArc, u16 param_1, u16 param_2) {
+ JUT_ASSERT(71, mHeap);
+ JUT_ASSERT(72, resArc);
+ mData = new (mHeap, 0) SpkData(resArc);
+ JUT_ASSERT(75, mData);
+ mData->loadTable(param_1);
+ mData->loadWave(param_2);
+}
+
+void SpkSystem::framework() {
+ JUT_ASSERT(89, mSoundHolder);
+#if VERSION != VERSION_WII_USA_R0
+ SpkSpeakerCtrl::framework();
+#endif
+ mSoundHolder->framework();
+}
+
+void SpkSystem::startSound(s32 chan, s32 param_1, SpkSoundHandle* handle) {
+ JUT_ASSERT(108, chan >= 0);
+ JUT_ASSERT(109, chan < 4);
+ JUT_ASSERT(110, mSoundHolder);
+ mSoundHolder->startSound(chan, param_1, handle);
+}
+
+void SpkSystem::startLevelSound(s32 chan, s32 param_1, SpkSoundHandle* handle) {
+ JUT_ASSERT(128, chan >= 0);
+ JUT_ASSERT(129, chan < 4);
+ JUT_ASSERT(130, mSoundHolder);
+ JUT_ASSERT(131, handle);
+ mSoundHolder->startLevelSound(chan, param_1, handle);
+}
+
+void SpkSystem::setMasterVolume(f32 vol) {
+ JUT_ASSERT(146, mSoundHolder);
+ JUT_ASSERT(147, vol >= 0.f);
+ JUT_ASSERT(148, vol <= 1.f);
+ mSoundHolder->setMasterVolume(vol);
+}
+
+f32 SpkSystem::getMasterVolume(void) {
+ return mSoundHolder->getMasterVolume();
+}
+
+void SpkSystem::stopAll(s32 chan, s32 msec) {
+ JUT_ASSERT(269, mSoundHolder);
+ JUT_ASSERT(270, chan < 4);
+ JUT_ASSERT(271, msec >= 0);
+
+ if (chan < 0) {
+ for (s32 i = 0; i < 4; i++) {
+ mSoundHolder->stopAll(i, msec);
+ }
+ } else {
+ mSoundHolder->stopAll(chan, msec);
+ }
+}
+
+void SpkSystem::setConfigVolume(s32 vol) {
+ JUT_ASSERT(307, mSoundHolder);
+ JUT_ASSERT(308, vol >= 0);
+ JUT_ASSERT(309, vol <= cConfigVolumeMax);
+
+ mSoundHolder->setConfigVolume(vol);
+}
+
+void SpkSystem::newSoundMemPool(s32 numOfSound) {
+ JUT_ASSERT(351, numOfSound >= 0);
+ JASPoolAllocObject<SpkSound>::newMemPool(numOfSound);
+}
+
+void SpkSystem::connect(s32 chan) {
+ SpkSpeakerCtrl::connect(chan);
+}
+
+void SpkSystem::disconnect(s32 chan) {
+ SpkSpeakerCtrl::disconnect(chan);
+}
+
+void SpkSystem::extensionProcess(s32 chan, s32 param_1) {
+ SpkSpeakerCtrl::extensionProcess(chan, param_1);
+}
diff --git a/src/Z2AudioCS/SpkTable.cpp b/src/Z2AudioCS/SpkTable.cpp
new file mode 100644
index 0000000000..51e4041394
--- /dev/null
+++ b/src/Z2AudioCS/SpkTable.cpp
@@ -0,0 +1,44 @@
+#include "Z2AudioCS/SpkTable.h"
+
+SpkTable::SpkTable(void) {
+ mIsInitialized = false;
+ mNumOfSound = 0;
+ mEntryOffset = 0;
+ mDataOffsets = 0;
+}
+
+struct SpkTableHeader {
+ s32 resourceCount;
+ s32 entryOff;
+ s32 dataOffsetsStartOff;
+ BOOL isDataOffsetsInitialized;
+};
+
+void SpkTable::setResource(void* res) {
+ mIsInitialized = false;
+
+ s32* cursor = (s32*)res;
+
+ s32 resourceCount = *cursor++;
+ s32 entryOff = *cursor++;
+ s32 dataOffsetsStartOff = *cursor++;
+ s32* pIsDataOffsetsInitialized = cursor;
+ BOOL isDataOffsetsInitialized = *cursor++;
+
+ mNumOfSound = resourceCount;
+
+ s32 entryOffset = (s32)res + entryOff;
+ mEntryOffset = entryOffset;
+ s32* dataOffsets = (s32*)((s32)res + dataOffsetsStartOff);
+ if (!isDataOffsetsInitialized) {
+ for (s32 i = 0; i < mNumOfSound; i++) {
+ dataOffsets[i] += (s32)res;
+ }
+ }
+
+ s32* dataOffsetsCopy = dataOffsets;
+ mDataOffsets = dataOffsetsCopy;
+ *pIsDataOffsetsInitialized = TRUE;
+
+ mIsInitialized = true;
+}
diff --git a/src/Z2AudioCS/SpkWave.cpp b/src/Z2AudioCS/SpkWave.cpp
new file mode 100644
index 0000000000..98c8e81a81
--- /dev/null
+++ b/src/Z2AudioCS/SpkWave.cpp
@@ -0,0 +1,83 @@
+#include "Z2AudioCS/SpkWave.h"
+
+#include "JSystem/JAudio2/JASCriticalSection.h"
+#include "JSystem/JUtility/JUTAssert.h"
+#include <revolution/os.h>
+#include <revolution/types.h>
+
+SpkWave::SpkWave(void) {
+ mWaveData = NULL;
+}
+
+void SpkWave::setResource(void* res) {
+ JUT_ASSERT(30, res);
+
+ JASCriticalSection cs;
+
+ mWaveData = res;
+}
+
+s32 SpkWave::getNumOfWaves(void) const {
+ if (mWaveData == NULL) {
+ return 0;
+ }
+
+ return ((s32*)mWaveData)[1];
+}
+
+s32 SpkWave::getWaveSize(s32 num) const {
+ JUT_ASSERT(57, num >= 0);
+ if (!mWaveData) {
+ return 0;
+ }
+ JUT_ASSERT(60, num < getNumOfWaves());
+
+ WaveData* data = getWaveData(num);
+ JUT_ASSERT(63, data);
+ return data->size;
+}
+
+u32 SpkWave::getLoopStartPos(s32 num) const {
+ JUT_ASSERT(76, num >= 0);
+ if (!mWaveData) {
+ return 0;
+ }
+ JUT_ASSERT(79, num < getNumOfWaves());
+
+ WaveData* data = getWaveData(num);
+ JUT_ASSERT(82, data);
+ return data->loopStartPos;
+}
+
+u32 SpkWave::getLoopEndPos(s32 num) const {
+ JUT_ASSERT(95, num >= 0);
+ if (!mWaveData) {
+ return 0;
+ }
+ JUT_ASSERT(98, num < getNumOfWaves());
+
+ WaveData* data = getWaveData(num);
+ JUT_ASSERT(101, data);
+ return data->loopEndPos;
+}
+
+s16* SpkWave::getWave(s32 num) const {
+ JUT_ASSERT(114, num >= 0);
+ if (!mWaveData) {
+ return 0;
+ }
+ JUT_ASSERT(117, num < getNumOfWaves());
+
+ WaveData* data = getWaveData(num);
+ JUT_ASSERT(120, data);
+ return data->wave;
+}
+
+WaveData* SpkWave::getWaveData(s32 num) const {
+ JUT_ASSERT(138, num >= 0);
+ JUT_ASSERT(139, mWaveData);
+ JUT_ASSERT(140, num < getNumOfWaves());
+
+ WaveData* data = (WaveData*)((u32)mWaveData + *(u32*)((u32)mWaveData + num * 4 + 8));
+ return data;
+}
diff --git a/src/Z2AudioCS/Z2AudioCS.cpp b/src/Z2AudioCS/Z2AudioCS.cpp
new file mode 100644
index 0000000000..e619876537
--- /dev/null
+++ b/src/Z2AudioCS/Z2AudioCS.cpp
@@ -0,0 +1,162 @@
+#include "Z2AudioCS/Z2AudioCS.h"
+
+#include "Z2AudioCS/SpkSystem.h"
+#include "JSystem/JKernel/JKRHeap.h"
+#include <revolution/types.h>
+#include <revolution/wpad.h>
+
+#define HANDLES_MAX 0x30
+
+static SpkSoundHandle* sSpkHandles;
+
+static u8 l_spkVolume;
+
+void Z2AudioCS::newSpkSoundMemPool() {
+ SpkSystem::newSoundMemPool(HANDLES_MAX);
+}
+
+int Z2AudioCS::init(JKRHeap* heap, JKRArchive* res, s32 param_2, s32 param_3) {
+ JUT_ASSERT(59, heap);
+ JUT_ASSERT(60, res);
+ SpkSystem* spkSys = new(heap, 0) SpkSystem(heap);
+ JUT_ASSERT(67, spkSys);
+
+ sSpkHandles = new (heap, 0) SpkSoundHandle[HANDLES_MAX];
+ JUT_ASSERT(71, sSpkHandles);
+
+ spkSys->setResource(res, 2, 3);
+ spkSys->setMasterVolume(1.0f);
+ spkSys->setConfigVolume(15);
+}
+
+void Z2AudioCS::update() {
+ if (JASGlobalInstance<SpkSystem>::getInstance() != NULL) {
+ JASGlobalInstance<SpkSystem>::getInstance()->framework();
+ }
+}
+
+void Z2AudioCS::connect(s32 chan) {
+ SpkSystem::connect(chan);
+ l_spkVolume = WPADGetSpeakerVolume();
+}
+
+void Z2AudioCS::disconnect(s32 chan) {
+ SpkSystem::disconnect(chan);
+}
+
+void Z2AudioCS::extensionProcess(s32 chan, s32 param_1) {
+ SpkSystem::extensionProcess(chan, param_1);
+}
+
+static SpkSoundHandle* getFreeSpkHandle(void) {
+ JUT_ASSERT(125, JASGlobalInstance<SpkSystem>::getInstance());
+ JUT_ASSERT(126, sSpkHandles);
+
+ SpkSoundHandle* highestPriorityHandle = NULL;
+ s32 highestPriority = 255;
+ for (s32 i = 0; i < HANDLES_MAX; i++) {
+ if (!sSpkHandles[i].isSoundAttached()) {
+ return &sSpkHandles[i];
+ }
+
+ if (sSpkHandles[i]->getPriority() < highestPriority) {
+ highestPriorityHandle = &sSpkHandles[i];
+ highestPriority = sSpkHandles[i]->getPriority();
+ }
+ }
+
+ return highestPriorityHandle;
+}
+
+SpkSoundHandle* Z2AudioCS::getHandleSoundID(s32 soundNum) {
+ JUT_ASSERT(145, JASGlobalInstance<SpkSystem>::getInstance());
+ JUT_ASSERT(146, sSpkHandles);
+
+ for (s32 i = 0; i < HANDLES_MAX; i++) {
+ if (!sSpkHandles[i].isSoundAttached()) {
+ continue;
+ }
+
+ if (sSpkHandles[i]->getSoundNum() == soundNum) {
+ return &sSpkHandles[i];
+ }
+ }
+
+ return NULL;
+}
+
+SpkSoundHandle* Z2AudioCS::start(s32 id, s32 chan) {
+ if (JASGlobalInstance<SpkSystem>::getInstance() == NULL) {
+ return NULL;
+ }
+ if (sSpkHandles == NULL) {
+ return NULL;
+ }
+ if (l_spkVolume == 0) {
+ return NULL;
+ }
+
+ OS_REPORT("[Z2AudioCS::start] id:%d ch:%d\n", id, chan);
+ SpkSoundHandle* handle = getFreeSpkHandle();
+ JUT_ASSERT(172, handle);
+
+ JASGlobalInstance<SpkSystem>::getInstance()->startSound(chan, id, handle);
+ return handle;
+}
+
+SpkSoundHandle* Z2AudioCS::startLevel(s32 id, s32 chan) {
+ if (JASGlobalInstance<SpkSystem>::getInstance() == NULL) {
+ return NULL;
+ }
+ if (sSpkHandles == NULL) {
+ return NULL;
+ }
+ if (l_spkVolume == 0) {
+ return NULL;
+ }
+
+ OS_REPORT("[Z2AudioCS::startLevel] id:%d ch:%d\n", id, chan);
+
+ SpkSoundHandle* handle = getHandleSoundID(id);
+ if (handle == NULL) {
+ handle = getFreeSpkHandle();
+ }
+ JUT_ASSERT(191, handle);
+
+ JASGlobalInstance<SpkSystem>::getInstance()->startLevelSound(chan, id, handle);
+ return handle;
+}
+
+s32 Z2AudioCS::getName(s32 num) {
+ if (JASGlobalInstance<SpkSystem>::getInstance() == NULL) {
+ return 0;
+ }
+ if (JASGlobalInstance<SpkSystem>::getInstance()->getData() == NULL) {
+ return 0;
+ }
+
+ return JASGlobalInstance<SpkSystem>::getInstance()->getData()->getTableMgr().getName(num);
+}
+
+s32 Z2AudioCS::getNumOfSound(void) {
+ if (JASGlobalInstance<SpkSystem>::getInstance() == NULL) {
+ return 0;
+ }
+ if (JASGlobalInstance<SpkSystem>::getInstance()->getData() == NULL) {
+ return 0;
+ }
+
+ return JASGlobalInstance<SpkSystem>::getInstance()->getData()->getTableMgr().getNumOfSound();
+}
+
+void Z2AudioCS::stopAll(s32 chan, s32 msec) {
+ if (JASGlobalInstance<SpkSystem>::getInstance() == NULL) {
+ return;
+ }
+
+ JASGlobalInstance<SpkSystem>::getInstance()->stopAll(chan, msec);
+}
+
+void Z2AudioCS::stop(s32 chan) {
+ stopAll(chan, 0);
+}