summaryrefslogtreecommitdiff
path: root/arm7/lib/src
diff options
context:
space:
mode:
authorPikalaxALT <pikalaxalt@gmail.com>2021-08-31 08:54:33 -0400
committerPikalaxALT <pikalaxalt@gmail.com>2021-08-31 08:54:33 -0400
commitba5f5449a4fb7c36a20ebccb0aa560d88d6a49c0 (patch)
tree63e5cab56041641b85b231f09296d1cecf9e0a1c /arm7/lib/src
parentcc891865fa1eb5a823f6a3eeb1746f05addb0d90 (diff)
parent1499e9f8be9458bbf94cbc4dc6d1034f53606cd0 (diff)
Merge branch 'pikalax_work' of github.com:PikalaxALT/pokediamond into pikalax_work
Diffstat (limited to 'arm7/lib/src')
-rw-r--r--arm7/lib/src/OS_reset.c3
-rw-r--r--arm7/lib/src/SND_alarm.c83
-rw-r--r--arm7/lib/src/SND_bank.c194
-rw-r--r--arm7/lib/src/SND_capture.c25
-rw-r--r--arm7/lib/src/SND_channel.c192
-rw-r--r--arm7/lib/src/SND_command.c329
-rw-r--r--arm7/lib/src/SND_exChannel.c685
-rw-r--r--arm7/lib/src/SND_global.c63
-rw-r--r--arm7/lib/src/SND_main.c133
-rw-r--r--arm7/lib/src/SND_seq.c1324
-rw-r--r--arm7/lib/src/SND_util.c109
-rw-r--r--arm7/lib/src/SND_work.c41
12 files changed, 3179 insertions, 2 deletions
diff --git a/arm7/lib/src/OS_reset.c b/arm7/lib/src/OS_reset.c
index e0f380f8..13d59bfa 100644
--- a/arm7/lib/src/OS_reset.c
+++ b/arm7/lib/src/OS_reset.c
@@ -2,6 +2,7 @@
#include "OS_reset.h"
#include "OS_interrupt.h"
#include "OS_terminate_proc.h"
+#include "PXI_fifo.h"
static u16 OSi_IsInitReset = 0;
vu16 OSi_IsResetOccurred = 0;
@@ -10,8 +11,6 @@ extern void MI_StopDma(u32 dma);
extern OSIrqMask OS_SetIrqMask(OSIrqMask mask);
extern OSIrqMask OS_ResetRequestIrqMask(OSIrqMask mask);
extern void SND_Shutdown(void);
-extern void PXI_SetFifoRecvCallback(u32 param1, void* callback);
-extern u32 PXI_SendWordByFifo(u32 param1, u32 data, u32 param2);
extern void FUN_038073EC(void); //OSi_DoResetSystem, in wram
ARM_FUNC void OS_InitReset(void)
diff --git a/arm7/lib/src/SND_alarm.c b/arm7/lib/src/SND_alarm.c
new file mode 100644
index 00000000..9b71d8f3
--- /dev/null
+++ b/arm7/lib/src/SND_alarm.c
@@ -0,0 +1,83 @@
+#include "SND_alarm.h"
+
+#include "OS_alarm.h"
+#include "OS_tick.h"
+#include "PXI_fifo.h"
+#include "SND_work.h"
+
+static void AlarmHandler(void *msg);
+
+void SND_AlarmInit(void)
+{
+ for (s32 i = 0; i < SND_ALARM_COUNT; i++)
+ {
+ SNDi_Work.alarms[i].enable = 0;
+ SNDi_Work.alarms[i].id = 0;
+ }
+}
+
+void SND_SetupAlarm(s32 idx, OSTick tick, OSTick period, u32 id)
+{
+ struct SNDAlarm *alarm = &SNDi_Work.alarms[idx];
+
+ if (alarm->enable)
+ {
+ OS_CancelAlarm(&alarm->alarm);
+ alarm->enable = 0;
+ }
+
+ alarm->tick = tick;
+ alarm->period = period;
+ alarm->id = (u8)id;
+}
+
+void SND_StartAlarm(s32 idx)
+{
+ OSTick tick;
+ OSTick period;
+
+ struct SNDAlarm *alarm = &SNDi_Work.alarms[idx];
+
+ if (alarm->enable != 0)
+ {
+ OS_CancelAlarm(&alarm->alarm);
+ alarm->enable = 0;
+ }
+
+ tick = alarm->tick;
+ period = alarm->period;
+ s32 arg = idx | (alarm->id << 8);
+
+ OS_CreateAlarm(&alarm->alarm);
+
+ if (period == 0)
+ {
+ OS_SetAlarm(&alarm->alarm, tick, AlarmHandler, (void *)arg);
+ }
+ else
+ {
+ OS_SetPeriodicAlarm(&alarm->alarm, tick + OS_GetTick(), period, AlarmHandler, (void *)arg);
+ }
+
+ alarm->enable = 1;
+}
+
+void SND_StopAlarm(s32 idx)
+{
+ struct SNDAlarm *alarm = &SNDi_Work.alarms[idx];
+
+ if (alarm->enable != 0)
+ {
+ OS_CancelAlarm(&alarm->alarm);
+ alarm->id++;
+ alarm->enable = 0;
+ }
+}
+
+static void AlarmHandler(void *msg)
+{
+ while (PXI_SendWordByFifo(7, (u32)msg, 0) < 0)
+ {
+ // nothing
+ }
+}
diff --git a/arm7/lib/src/SND_bank.c b/arm7/lib/src/SND_bank.c
new file mode 100644
index 00000000..3437e021
--- /dev/null
+++ b/arm7/lib/src/SND_bank.c
@@ -0,0 +1,194 @@
+#include "SND_bank.h"
+
+#include "SND_exChannel.h"
+#include "SND_main.h"
+
+#include "mmap.h"
+
+static const struct SNDWaveData *GetWaveData(
+ const struct SNDBankData *bankData, s32 waveArc, s32 wave);
+
+BOOL SND_ReadInstData(
+ const struct SNDBankData *bankData, s32 program, s32 midiKey, struct SNDInstData *instData)
+{
+ s32 i;
+ struct SNDDrumSet *drumSet;
+ struct SNDKeySplit *keySplit;
+ u32 off;
+ u8 minKey, maxKey;
+
+ if (program < 0)
+ return FALSE;
+
+ SNDi_LockMutex();
+
+ if (program >= bankData->instCount)
+ {
+ SNDi_UnlockMutex();
+ return FALSE;
+ }
+
+ off = bankData->instOffsets[program];
+ instData->type = SND_INST_OFFSET_TYPE(off);
+
+ switch (instData->type)
+ {
+ case SND_INST_PCM:
+ case SND_INST_PSG:
+ case SND_INST_NOISE:
+ case SND_INST_DIRECTPCM:
+ case SND_INST_DUMMY:
+ instData->param = *SND_INST_OFFSET_NORMAL(bankData, off);
+ break;
+ case SND_INST_DRUM_TABLE:
+ drumSet = SND_INST_OFFSET_DRUMS(bankData, off);
+
+ // seperate variables needed for matching
+ maxKey = drumSet->maxKey;
+ minKey = drumSet->minKey;
+
+ if (midiKey < minKey || midiKey > maxKey)
+ {
+ SNDi_UnlockMutex();
+ return FALSE;
+ }
+
+ *instData = drumSet->instruments[midiKey - drumSet->minKey];
+ break;
+ case SND_INST_KEY_SPLIT:
+ i = 0;
+ keySplit = SND_INST_OFFSET_KEYSPL(bankData, off);
+
+ while (midiKey > keySplit->key[i])
+ {
+ i++;
+ if (i >= SND_INST_MAX_KEYSPLIT)
+ {
+ SNDi_UnlockMutex();
+ return FALSE;
+ }
+ }
+ *instData = keySplit->instruments[i];
+ break;
+ case SND_INST_ILLEGAL:
+ default:
+ SNDi_UnlockMutex();
+ return FALSE;
+ }
+
+ SNDi_UnlockMutex();
+ return TRUE;
+}
+
+const struct SNDWaveData *SND_GetWaveDataAddress(const struct SNDWaveArc *waveArc, s32 wave)
+{
+ SNDi_LockMutex();
+
+ const struct SNDWaveData *retval = (const struct SNDWaveData *)waveArc->waveOffsets[wave];
+ if (retval != NULL)
+ {
+ if ((u32)retval < HW_MAIN_MEM)
+ {
+ retval = (const struct SNDWaveData *)((u32)waveArc + (u32)retval);
+ }
+ }
+ else
+ {
+ retval = NULL;
+ }
+
+ SNDi_UnlockMutex();
+
+ return retval;
+}
+
+BOOL SND_NoteOn(struct SNDExChannel *chn,
+ s32 midiKey,
+ s32 velocity,
+ s32 length,
+ const struct SNDBankData *bankData,
+ const struct SNDInstData *instData)
+{
+ const struct SNDWaveData *waveData;
+ u8 release;
+ BOOL success;
+
+ release = instData->param.envRelease;
+
+ if (release == 0xFF)
+ {
+ length = -1;
+ release = 0;
+ }
+
+ switch (instData->type)
+ {
+ case SND_INST_PCM:
+ case SND_INST_DIRECTPCM:
+ if (instData->type == SND_INST_PCM)
+ {
+ waveData = GetWaveData(bankData, instData->param.wave[1], instData->param.wave[0]);
+ }
+ else
+ {
+ waveData = (const struct SNDWaveData *)((instData->param.wave[1] << 16) |
+ instData->param.wave[0]);
+ }
+
+ if (waveData == NULL)
+ {
+ success = FALSE;
+ }
+ else
+ {
+ success =
+ SND_StartExChannelPcm(chn, &waveData->param, waveData->sampleData, length);
+ }
+ break;
+ case SND_INST_PSG:
+ success = SND_StartExChannelPsg(chn, instData->param.wave[0], length);
+ break;
+ case SND_INST_NOISE:
+ success = SND_StartExChannelNoise(chn, length);
+ break;
+ default:
+ success = FALSE;
+ break;
+ }
+
+ if (success == FALSE)
+ {
+ return FALSE;
+ }
+ else
+ {
+ chn->midiKey = (u8)midiKey;
+ chn->rootMidiKey = instData->param.rootKey;
+ chn->velocity = (u8)velocity;
+ SND_SetExChannelAttack(chn, instData->param.envAttack);
+ SND_SetExChannelDecay(chn, instData->param.envDecay);
+ SND_SetExChannelSustain(chn, instData->param.envSustain);
+ SND_SetExChannelRelease(chn, release);
+ chn->initPan = (s8)(instData->param.pan - 0x40);
+ return TRUE;
+ }
+}
+
+static const struct SNDWaveData *GetWaveData(
+ const struct SNDBankData *bankData, s32 waveArc, s32 wave)
+{
+ const struct SNDWaveArc *arcPtr = bankData->waveArcLinks[waveArc].waveArc;
+
+ if (arcPtr == NULL)
+ {
+ return NULL;
+ }
+ else if (wave < arcPtr->waveCount)
+ {
+ return SND_GetWaveDataAddress(arcPtr, wave);
+ }
+ else
+ {
+ return NULL;
+ }
+}
diff --git a/arm7/lib/src/SND_capture.c b/arm7/lib/src/SND_capture.c
new file mode 100644
index 00000000..57494b72
--- /dev/null
+++ b/arm7/lib/src/SND_capture.c
@@ -0,0 +1,25 @@
+#include "SND_capture.h"
+
+#include "registers.h"
+
+enum SNDLoop
+{
+ SND_CAP_LOOP = 0,
+ SND_CAP_ONESHOT = 1
+};
+
+void SND_SetupCapture(
+ int idx, int format, void *captureData, int size, BOOL loop, int capCtrlSrc, int capCtrlDst)
+{
+ int off = idx * 8;
+
+ reg_SNDCAPxCNT(idx) = (u8)((format << 3) | ((loop ? SND_CAP_LOOP : SND_CAP_ONESHOT) << 2) |
+ (capCtrlSrc << 1) | capCtrlDst);
+ *(vu32 *)(0x4000510 + off) = (u32)captureData;
+ *(vu16 *)(0x4000514 + off) = (u16)size;
+}
+
+BOOL SND_IsCaptureActive(int idx)
+{
+ return (reg_SNDCAPxCNT(idx) & 0x80) != 0;
+}
diff --git a/arm7/lib/src/SND_channel.c b/arm7/lib/src/SND_channel.c
new file mode 100644
index 00000000..be0bba3a
--- /dev/null
+++ b/arm7/lib/src/SND_channel.c
@@ -0,0 +1,192 @@
+#include "SND_channel.h"
+
+#include "SND_work.h"
+#include "registers.h"
+
+static int sMasterPan = -1;
+
+static u8 sOrgVolume[SND_CHANNEL_COUNT];
+static u8 sOrgPan[SND_CHANNEL_COUNT];
+static int sSurroundDecay;
+
+static int CalcSurroundDecay(int vol, int pan);
+
+void SND_SetupChannelPcm(int chnIdx,
+ const void *data,
+ int format,
+ int loop,
+ int loopStart,
+ int loopLength,
+ int volume,
+ int volumeDiv,
+ int timer,
+ int pan)
+{
+ int off = chnIdx * 0x10;
+
+ sOrgPan[chnIdx] = (u8)pan;
+ if (sMasterPan >= 0)
+ pan = sMasterPan;
+
+ sOrgVolume[chnIdx] = (u8)volume;
+ if (sSurroundDecay > 0 && (1 << chnIdx) & 0xFFF5)
+ {
+ volume = CalcSurroundDecay(volume, pan);
+ }
+
+ reg_SOUNDoffCNT(off) =
+ (u32)((format << 29) | (loop << 27) | (pan << 16) | (volumeDiv << 8) | (volume));
+ reg_SOUNDoffTMR(off) = (u16)(0x10000 - timer);
+ reg_SOUNDoffPNT(off) = (u16)loopStart;
+ reg_SOUNDoffLEN(off) = (u32)loopLength;
+ reg_SOUNDoffSAD(off) = (u32)data;
+}
+
+void SND_SetupChannelPsg(int chnIdx, int duty, int volume, int volumeDiv, int timer, int pan)
+{
+ int off = chnIdx * 0x10;
+
+ sOrgPan[chnIdx] = (u8)pan;
+ if (sMasterPan >= 0)
+ pan = sMasterPan;
+
+ sOrgVolume[chnIdx] = (u8)volume;
+ if (sSurroundDecay > 0 && (1 << chnIdx) & 0xFFF5)
+ {
+ volume = CalcSurroundDecay(volume, pan);
+ }
+
+ reg_SOUNDoffCNT(off) =
+ (u32)(0x60000000 | (duty << 24) | (pan << 16) | (volumeDiv << 8) | volume);
+ reg_SOUNDoffTMR(off) = (u16)(0x10000 - timer);
+}
+
+void SND_SetupChannelNoise(int chnIdx, int volume, int volumeDiv, int timer, int pan)
+{
+ int off = chnIdx * 0x10;
+
+ sOrgPan[chnIdx] = (u8)pan;
+ if (sMasterPan >= 0)
+ pan = sMasterPan;
+
+ sOrgVolume[chnIdx] = (u8)volume;
+ if (sSurroundDecay > 0 && (1 << chnIdx) & 0xFFF5)
+ {
+ volume = CalcSurroundDecay(volume, pan);
+ }
+
+ reg_SOUNDoffCNT(off) = (u32)(0x60000000 | (pan << 16) | (volumeDiv << 8) | volume);
+ reg_SOUNDoffTMR(off) = (u16)(0x10000 - timer);
+}
+
+void SND_StopChannel(int idx, int hold)
+{
+ vu32 *reg = &reg_SOUNDxCNT(idx);
+
+ u32 v = *reg;
+
+ // disable channel
+ v &= ~0x80000000;
+
+ // set hold flag
+ if (hold & 1)
+ v |= 0x8000;
+
+ *reg = v;
+}
+
+void SND_SetChannelVolume(int chnIdx, int vol, int volDiv)
+{
+ sOrgVolume[chnIdx] = (u8)vol;
+
+ if (sSurroundDecay > 0 && (1 << chnIdx) & 0xFFF5)
+ {
+ int pan = reg_SOUNDxCNT_PAN(chnIdx);
+ vol = CalcSurroundDecay(vol, pan);
+ }
+
+ reg_SOUNDxCNT_VOLS(chnIdx) = (u16)((volDiv << 8) | vol);
+}
+
+void SND_SetChannelTimer(int chnIdx, int timer)
+{
+ reg_SOUNDxTMR(chnIdx) = (u16)(0x10000 - timer);
+}
+
+void SND_SetChannelPan(int chnIdx, int pan)
+{
+ sOrgPan[chnIdx] = (u8)pan;
+
+ if (sMasterPan >= 0)
+ {
+ pan = sMasterPan;
+ }
+
+ reg_SOUNDxCNT_PAN(chnIdx) = (u8)pan;
+
+ if (sSurroundDecay > 0 && (1 << chnIdx) & 0xFFF5)
+ {
+ reg_SOUNDxCNT_VOL(chnIdx) = (u8)CalcSurroundDecay(sOrgVolume[chnIdx], pan);
+ }
+}
+
+BOOL SND_IsChannelActive(int chnIdx)
+{
+ return (reg_SOUNDxCNT_STAT(chnIdx) & 0x80) != 0;
+}
+
+void SND_SetMasterPan(int pan)
+{
+ sMasterPan = pan;
+
+ if (pan >= 0)
+ {
+ for (int i = 0; i < SND_CHANNEL_COUNT; i++)
+ {
+ reg_SOUNDxCNT_PAN(i) = (u8)pan;
+ }
+ }
+ else
+ {
+ for (int i = 0; i < SND_CHANNEL_COUNT; i++)
+ {
+ reg_SOUNDxCNT_PAN(i) = sOrgPan[i];
+ }
+ }
+}
+
+u32 SND_GetChannelControl(int chnIdx)
+{
+ return reg_SOUNDxCNT(chnIdx);
+}
+
+void SNDi_SetSurroundDecay(int decay)
+{
+ sSurroundDecay = decay;
+
+ for (int i = 0; i < SND_CHANNEL_COUNT; i++)
+ {
+ // do not process channel 1+3 (capture playback channels)
+ if ((1 << i) & 0xFFF5)
+ {
+ int pan = reg_SOUNDxCNT_PAN(i);
+ reg_SOUNDxCNT_VOL(i) = (u8)CalcSurroundDecay(sOrgVolume[i], pan);
+ }
+ }
+}
+
+static int CalcSurroundDecay(int vol, int pan)
+{
+ if (pan < 24)
+ {
+ return vol * (sSurroundDecay * (pan + 40) + ((0x7FFF - sSurroundDecay) << 6)) >> 21;
+ }
+ else if (pan <= 104)
+ {
+ return vol;
+ }
+ else
+ {
+ return vol * (-sSurroundDecay * (pan - 40) + ((sSurroundDecay + 0x7FFF) << 6)) >> 21;
+ }
+}
diff --git a/arm7/lib/src/SND_command.c b/arm7/lib/src/SND_command.c
new file mode 100644
index 00000000..f561138a
--- /dev/null
+++ b/arm7/lib/src/SND_command.c
@@ -0,0 +1,329 @@
+#include "SND_command.h"
+
+#include "mmap.h"
+#include "registers.h"
+
+#include "MI_memory.h"
+#include "OS_message.h"
+#include "OS_system.h"
+#include "PXI_fifo.h"
+#include "SND_alarm.h"
+#include "SND_capture.h"
+#include "SND_channel.h"
+#include "SND_exChannel.h"
+#include "SND_global.h"
+#include "SND_main.h"
+#include "SND_seq.h"
+#include "SND_work.h"
+
+#define SND_MSG_ARRAY_SIZE 8
+
+static OSMessage sMsgArray[SND_MSG_ARRAY_SIZE];
+static struct OSMessageQueue sMsgQueue;
+
+static void InitPXI(void);
+static void StartTimer(u32 channelMask, u32 captureMask, u32 alarmMask, s32 unused);
+static void StopTimer(u32 channelMask, u32 captureMask, u32 alarmMask, s32 hold);
+static void SetChannelTimer(u32 channelMask, int timer);
+static void SetChannelVolume(u32 channelMask, int vol, int shift);
+static void SetChannelPan(u32 channelMask, int pan);
+static void ReadDriverInfo(struct SNDDriverInfo *driverInfo);
+
+void SND_CommandInit(void)
+{
+ OS_InitMessageQueue(&sMsgQueue, sMsgArray, SND_MSG_ARRAY_SIZE);
+ InitPXI();
+ SNDi_SharedWork = NULL;
+}
+
+void SND_CommandProc(void)
+{
+ struct SNDCommand cmd;
+ struct SNDCommand *cmd_ptr;
+ OSMessage msg;
+
+ while (OS_ReceiveMessage(&sMsgQueue, &msg, 0))
+ {
+ // casting it directly below doesn't appear to match
+ cmd_ptr = (struct SNDCommand *)msg;
+
+ while (cmd_ptr)
+ {
+ cmd = *cmd_ptr;
+
+ switch (cmd.id)
+ {
+ case SND_CMD_START_SEQ:
+ SND_StartSeq((int)cmd.arg[0],
+ (const void *)cmd.arg[1],
+ cmd.arg[2],
+ (struct SNDBankData *)cmd.arg[3]);
+ break;
+ case SND_CMD_STOP_SEQ:
+ SND_StopSeq((int)cmd.arg[0]);
+ break;
+ case SND_CMD_PREPARE_SEQ:
+ SND_PrepareSeq((int)cmd.arg[0],
+ (const void *)cmd.arg[1],
+ cmd.arg[2],
+ (struct SNDBankData *)cmd.arg[3]);
+ break;
+ case SND_CMD_START_PREPARED_SEQ:
+ SND_StartPreparedSeq((int)cmd.arg[0]);
+ break;
+ case SND_CMD_PAUSE_SEQ:
+ SND_PauseSeq((int)cmd.arg[0], (BOOL)cmd.arg[1]);
+ break;
+ case SND_CMD_SKIP_SEQ:
+ SND_SkipSeq((int)cmd.arg[0], cmd.arg[1]);
+ break;
+ case SND_CMD_PLAYER_PARAM:
+ SNDi_SetPlayerParam((int)cmd.arg[0], cmd.arg[1], cmd.arg[2], (int)cmd.arg[3]);
+ break;
+ case SND_CMD_TRACK_PARAM:
+ SNDi_SetTrackParam((int)cmd.arg[0] & 0xFFFFFF,
+ cmd.arg[1],
+ cmd.arg[2],
+ cmd.arg[3],
+ (u8)(cmd.arg[0] >> 24));
+ break;
+ case SND_CMD_MUTE_TRACK:
+ SND_SetTrackMute((int)cmd.arg[0], cmd.arg[1], (BOOL)cmd.arg[2]);
+ break;
+ case SND_CMD_ALLOCATABLE_CHANNEL:
+ SND_SetTrackAllocatableChannel((int)cmd.arg[0], cmd.arg[1], cmd.arg[2]);
+ break;
+ case SND_CMD_PLAYER_LOCAL_VAR:
+ SND_SetPlayerLocalVariable((int)cmd.arg[0], (int)cmd.arg[1], (s16)cmd.arg[2]);
+ break;
+ case SND_CMD_PLAYER_GLOBAL_VAR:
+ SND_SetPlayerGlobalVariable((int)cmd.arg[0], (s16)cmd.arg[1]);
+ break;
+ case SND_CMD_START_TIMER:
+ StartTimer(cmd.arg[0], cmd.arg[1], cmd.arg[2], (int)cmd.arg[3]);
+ break;
+ case SND_CMD_STOP_TIMER:
+ StopTimer(cmd.arg[0], cmd.arg[1], cmd.arg[2], (int)cmd.arg[3]);
+ break;
+ case SND_CMD_SETUP_CAPTURE:
+ SND_SetupCapture((int)(cmd.arg[2] >> 31u) & 1,
+ (int)(cmd.arg[2] >> 30u) & 1,
+ (void *)cmd.arg[0],
+ (int)cmd.arg[1],
+ (BOOL)(cmd.arg[2] >> 29u) & 1,
+ (int)(cmd.arg[2] >> 28u) & 1,
+ (int)(cmd.arg[2] >> 27u) & 1);
+ break;
+ case SND_CMD_SETUP_ALARM:
+ SND_SetupAlarm((int)cmd.arg[0], cmd.arg[1], cmd.arg[2], cmd.arg[3]);
+ break;
+ case SND_CMD_CHANNEL_TIMER:
+ SetChannelTimer(cmd.arg[0], (int)cmd.arg[1]);
+ break;
+ case SND_CMD_CHANNEL_VOLUME:
+ SetChannelVolume(cmd.arg[0], (int)cmd.arg[1], (int)cmd.arg[2]);
+ break;
+ case SND_CMD_CHANNEL_PAN:
+ SetChannelPan(cmd.arg[0], (int)cmd.arg[1]);
+ break;
+ case SND_CMD_SETUP_CHANNEL_PCM:
+ SND_SetupChannelPcm((int)cmd.arg[0] & 0xFFFF,
+ (void *)(cmd.arg[1] & 0x7FFFFFFu),
+ (int)(cmd.arg[3] >> 24u) & 0x3,
+ (int)(cmd.arg[3] >> 26u) & 0x3,
+ (int)cmd.arg[3] & 0xFFFF,
+ (int)cmd.arg[2] & 0x3FFFFF,
+ (int)(cmd.arg[2] >> 24u) & 0x7F,
+ (int)(cmd.arg[2] >> 22u) & 0x3,
+ (int)(cmd.arg[0] >> 16u) & 0xFFFF,
+ (int)(cmd.arg[3] >> 16u) & 0x7F);
+ break;
+ case SND_CMD_SETUP_CHANNEL_PSG:
+ SND_SetupChannelPsg((int)cmd.arg[0],
+ (int)cmd.arg[3],
+ (int)cmd.arg[1] & 0x7F,
+ (int)(cmd.arg[1] >> 8u) & 0x3,
+ (int)(cmd.arg[2] >> 8u) & 0xFFFF,
+ (int)cmd.arg[2] & 0x7F);
+ break;
+ case SND_CMD_SETUP_CHANNEL_NOISE:
+ SND_SetupChannelNoise((int)cmd.arg[0],
+ (int)cmd.arg[1] & 0x7F,
+ (int)(cmd.arg[1] >> 8u) & 0x3,
+ (int)(cmd.arg[2] >> 8u) & 0xFFFF,
+ (int)cmd.arg[2] & 0x7F);
+ break;
+ case SND_CMD_SURROUND_DECAY:
+ SNDi_SetSurroundDecay((int)cmd.arg[0]);
+ break;
+ case SND_CMD_MASTER_VOLUME:
+ SND_SetMasterVolume((int)cmd.arg[0]);
+ break;
+ case SND_CMD_MASTER_PAN:
+ SND_SetMasterPan((int)cmd.arg[0]);
+ break;
+ case SND_CMD_OUTPUT_SELECTOR:
+ SND_SetOutputSelector(
+ (int)cmd.arg[0], (int)cmd.arg[1], (int)cmd.arg[2], (int)cmd.arg[3]);
+ break;
+ case SND_CMD_LOCK_CHANNEL:
+ SND_LockChannel(cmd.arg[0], cmd.arg[1]);
+ break;
+ case SND_CMD_UNLOCK_CHANNEL:
+ SND_UnlockChannel(cmd.arg[0], cmd.arg[1]);
+ break;
+ case SND_CMD_STOP_UNLOCKED_CHANNEL:
+ SND_StopUnlockedChannel(cmd.arg[0], cmd.arg[1]);
+ break;
+ case SND_CMD_INVALIDATE_SEQ:
+ SND_InvalidateSeq((void *)cmd.arg[0], (void *)cmd.arg[1]);
+ break;
+ case SND_CMD_INVALIDATE_BANK:
+ SND_InvalidateBank((void *)cmd.arg[0], (void *)cmd.arg[1]);
+ break;
+ case SND_CMD_INVALIDATE_WAVE:
+ SND_InvalidateWave((void *)cmd.arg[0], (void *)cmd.arg[1]);
+ break;
+ case SND_CMD_SET_SHARED_WORK:
+ SNDi_SharedWork = (struct SNDSharedWork *)cmd.arg[0];
+ break;
+ case SND_CMD_READ_DRIVER_INFO:
+ ReadDriverInfo((struct SNDDriverInfo *)cmd.arg[0]);
+ break;
+ } // end switch
+
+ cmd_ptr = cmd.llNext;
+ } // end loop over command linked list
+
+ SNDi_SharedWork->finishedCommandTag++;
+ } // end loop over receive message
+}
+
+static void PxiFifoCallback(PXIFifoTag tag, u32 data, BOOL error)
+{
+ (void)tag;
+ (void)error;
+
+ OSIntrMode intrMode = OS_DisableInterrupts();
+
+ if (data >= HW_MAIN_MEM)
+ {
+ (void)OS_SendMessage(&sMsgQueue, (OSMessage)data, 0);
+ }
+ else
+ {
+ if (data == 0)
+ SND_SendWakeupMessage();
+ }
+
+ (void)OS_RestoreInterrupts(intrMode);
+}
+
+static void InitPXI(void)
+{
+ PXI_SetFifoRecvCallback(PXI_FIFO_TAG_SOUND, PxiFifoCallback);
+}
+
+static void SetChannelTimer(u32 channelMask, int timer)
+{
+ for (int i = 0; i < SND_CHANNEL_COUNT && channelMask != 0; i++, channelMask >>= 1)
+ {
+ if (channelMask & 1)
+ SND_SetChannelTimer(i, timer);
+ }
+}
+
+static void SetChannelVolume(u32 channelMask, int vol, int shift)
+{
+ for (int i = 0; i < SND_CHANNEL_COUNT && channelMask != 0; i++, channelMask >>= 1)
+ {
+ if (channelMask & 1)
+ SND_SetChannelVolume(i, vol, shift);
+ }
+}
+
+static void SetChannelPan(u32 channelMask, int pan)
+{
+ for (int i = 0; i < SND_CHANNEL_COUNT && channelMask != 0; i++, channelMask >>= 1)
+ {
+ if (channelMask & 1)
+ SND_SetChannelPan(i, pan);
+ }
+}
+
+static void StartTimer(u32 channelMask, u32 captureMask, u32 alarmMask, s32 unused)
+{
+ (void)unused;
+
+ OSIntrMode intrMode = OS_DisableInterrupts();
+
+ for (int i = 0; i < SND_CHANNEL_COUNT && channelMask != 0; i++, channelMask >>= 1)
+ {
+ if (channelMask & 1)
+ reg_SOUNDxCNT_STAT(i) |= 0x80;
+ }
+
+ if (captureMask & 1)
+ {
+ if (captureMask & 2)
+ {
+ *(vu16 *)&reg_SNDCAPxCNT(0) |= 0x8080;
+ }
+ else
+ {
+ reg_SNDCAPxCNT(0) |= 0x80;
+ }
+ }
+ else if (captureMask & 2)
+ {
+ reg_SNDCAPxCNT(1) |= 0x80;
+ }
+
+ for (int i = 0; i < SND_ALARM_COUNT && alarmMask != 0; i++, alarmMask >>= 1)
+ {
+ if (alarmMask & 1)
+ SND_StartAlarm(i);
+ }
+
+ (void)OS_RestoreInterrupts(intrMode);
+ SND_UpdateSharedWork();
+}
+
+static void StopTimer(u32 channelMask, u32 captureMask, u32 alarmMask, s32 hold)
+{
+ OSIntrMode intrMode = OS_DisableInterrupts();
+
+ for (int i = 0; i < SND_ALARM_COUNT && alarmMask != 0; i++, alarmMask >>= 1)
+ {
+ if (alarmMask & 1)
+ SND_StopAlarm(i);
+ }
+
+ for (int i = 0; i < SND_CHANNEL_COUNT && channelMask != 0; i++, channelMask >>= 1)
+ {
+ if (channelMask & 1)
+ SND_StopChannel(i, hold);
+ }
+
+ if (captureMask & 1)
+ reg_SNDCAPxCNT(0) = 0;
+ if (captureMask & 2)
+ reg_SNDCAPxCNT(1) = 0;
+
+ (void)OS_RestoreInterrupts(intrMode);
+ SND_UpdateSharedWork();
+}
+
+static void ReadDriverInfo(struct SNDDriverInfo *driverInfo)
+{
+ MI_CpuCopy32(&SNDi_Work, driverInfo, sizeof(SNDi_Work));
+
+ driverInfo->workPtr = &SNDi_Work;
+
+ for (int i = 0; i < SND_CHANNEL_COUNT; i++)
+ {
+ driverInfo->channelControls[i] = SND_GetChannelControl(i);
+ }
+
+ driverInfo->lockedChannels = SND_GetLockedChannel(0);
+}
diff --git a/arm7/lib/src/SND_exChannel.c b/arm7/lib/src/SND_exChannel.c
new file mode 100644
index 00000000..dfaabeda
--- /dev/null
+++ b/arm7/lib/src/SND_exChannel.c
@@ -0,0 +1,685 @@
+#include "SND_exChannel.h"
+
+#include "SND_channel.h"
+#include "SND_main.h"
+#include "SND_util.h"
+#include "SND_work.h"
+
+#include "registers.h"
+
+// TODO import these tables into here if we have a working .rodata section
+extern const u8 sChannelAllocationOrder[SND_CHANNEL_COUNT];
+extern const u8 sAttackCoeffTable[19];
+extern const u8 sSampleDataShiftTable[4];
+
+static u32 sLockedChannelMask;
+static u32 sWeakLockedChannelMask;
+
+static u16 CalcDecayCoeff(int vol);
+
+static int ExChannelSweepUpdate(struct SNDExChannel *chn, BOOL step);
+static int ExChannelLfoUpdate(struct SNDExChannel *chn, BOOL step);
+static void ExChannelStart(struct SNDExChannel *chn, int length);
+static int ExChannelVolumeCmp(struct SNDExChannel *chn_a, struct SNDExChannel *chn_b);
+static void ExChannelSetup(
+ struct SNDExChannel *, SNDExChannelCallback callback, void *callbackUserData, int priority);
+
+void SND_ExChannelInit(void)
+{
+ struct SNDExChannel *chn;
+ s32 i;
+
+ for (i = 0; i < SND_CHANNEL_COUNT; i++)
+ {
+ chn = &SNDi_Work.channels[i];
+
+ chn->id = (u8)i;
+ chn->flags.syncFlag = 0;
+ chn->flags.active = FALSE;
+ }
+
+ sLockedChannelMask = 0;
+ sWeakLockedChannelMask = 0;
+}
+
+void SND_UpdateExChannel(void)
+{
+ struct SNDExChannel *chn;
+ s32 i;
+
+ for (i = 0; i < SND_CHANNEL_COUNT; i++)
+ {
+ chn = &SNDi_Work.channels[i];
+
+ if (chn->flags.syncFlag == 0)
+ continue;
+
+ if (chn->flags.syncFlag & SND_CHN_SYNC_STOP)
+ SND_StopChannel(i, 0);
+
+ if (chn->flags.syncFlag & SND_CHN_SYNC_START)
+ {
+ switch (chn->type)
+ {
+ case SND_CHN_TYPE_PCM:
+ SND_SetupChannelPcm(i,
+ chn->waveDataPtr,
+ chn->waveParam.format,
+ chn->waveParam.loopEnabled ? 1 : 2,
+ (s32)chn->waveParam.loopStart,
+ (s32)chn->waveParam.loopLength,
+ chn->volume & 0xFF,
+ chn->volume >> 8,
+ chn->timer,
+ chn->pan);
+ break;
+ case SND_CHN_TYPE_PSG:
+ SND_SetupChannelPsg(i,
+ chn->dutyCycle,
+ chn->volume & 0xFF,
+ chn->volume >> 8,
+ chn->timer,
+ chn->pan);
+ break;
+ case SND_CHN_TYPE_NOISE:
+ SND_SetupChannelNoise(
+ i, chn->volume & 0xFF, chn->volume >> 8, chn->timer, chn->pan);
+ break;
+ }
+ }
+ else
+ {
+ if (chn->flags.syncFlag & SND_CHN_SYNC_TIMER)
+ {
+ SND_SetChannelTimer(i, chn->timer);
+ }
+ if (chn->flags.syncFlag & SND_CHN_SYNC_VOLUME)
+ {
+ SND_SetChannelVolume(i, chn->volume & 0xFF, chn->volume >> 8);
+ }
+ if (chn->flags.syncFlag & SND_CHN_SYNC_PAN)
+ {
+ SND_SetChannelPan(i, chn->pan);
+ }
+ }
+ }
+
+ for (i = 0; i < SND_CHANNEL_COUNT; i++)
+ {
+ chn = &SNDi_Work.channels[i];
+
+ if (!chn->flags.syncFlag)
+ continue;
+
+ if (chn->flags.syncFlag & SND_CHN_SYNC_START)
+ reg_SOUNDxCNT_STAT(i) |= 0x80;
+ chn->flags.syncFlag = 0;
+ }
+}
+
+void SND_ExChannelMain(BOOL step)
+{
+ struct SNDExChannel *chn;
+ s32 i;
+ s32 vol;
+ s32 pitch;
+ s32 pan;
+ s32 lfo;
+ u16 newTimer;
+
+ for (i = 0; i < SND_CHANNEL_COUNT; i++)
+ {
+ vol = 0;
+ pitch = 0;
+ pan = 0;
+ chn = &SNDi_Work.channels[i];
+
+ if (!chn->flags.active)
+ continue;
+
+ if (chn->flags.start)
+ {
+ chn->flags.syncFlag |= SND_CHN_SYNC_START;
+ chn->flags.start = FALSE;
+ }
+ else if (!SND_IsChannelActive(i))
+ {
+ if (chn->callback)
+ chn->callback(chn, 1, chn->callbackUserData);
+ else
+ chn->priority = 0;
+ chn->volume = 0;
+ chn->flags.active = FALSE;
+ continue;
+ }
+
+ vol += SNDi_DecibelSquareTable[chn->velocity];
+ pitch += (chn->midiKey - chn->rootMidiKey) * 0x40;
+
+ vol += SND_UpdateExChannelEnvelope(chn, step);
+ pitch += ExChannelSweepUpdate(chn, step);
+
+ vol += chn->userDecay;
+ vol += chn->userDecay2;
+ pitch += chn->userPitch;
+
+ lfo = ExChannelLfoUpdate(chn, step);
+
+ switch (chn->lfo.param.target)
+ {
+ case SND_LFO_VOLUME:
+ if (vol > -0x8000)
+ vol += lfo;
+ break;
+ case SND_LFO_PAN:
+ pan += lfo;
+ break;
+ case SND_LFO_PITCH:
+ pitch += lfo;
+ break;
+ }
+
+ pan += chn->initPan;
+ if (chn->panRange != 127)
+ {
+ pan = (pan * chn->panRange + 0x40) >> 7;
+ }
+ pan += chn->userPan;
+
+ if (chn->envStatus == SND_ENV_RELEASE && vol <= -723)
+ {
+ chn->flags.syncFlag = SND_CHN_SYNC_STOP;
+ if (chn->callback)
+ chn->callback(chn, 1, chn->callbackUserData);
+ else
+ chn->priority = 0;
+ chn->volume = 0;
+ chn->flags.active = 0;
+ }
+ else
+ {
+ vol = SND_CalcChannelVolume(vol);
+ newTimer = SND_CalcTimer(chn->waveParam.timer, pitch);
+
+ if (chn->type == SND_CHN_TYPE_PSG)
+ newTimer &= 0xFFFC;
+
+ pan += 0x40;
+ if (pan < 0)
+ pan = 0;
+ else if (pan > 127)
+ pan = 127;
+
+ if (vol != chn->volume)
+ {
+ chn->volume = (u16)vol;
+ chn->flags.syncFlag |= SND_CHN_SYNC_VOLUME;
+ }
+ if (newTimer != chn->timer)
+ {
+ chn->timer = (u16)newTimer;
+ chn->flags.syncFlag |= SND_CHN_SYNC_TIMER;
+ }
+ if (pan != chn->pan)
+ {
+ chn->pan = (u8)pan;
+ chn->flags.syncFlag |= SND_CHN_SYNC_PAN;
+ }
+ }
+ }
+}
+
+BOOL SND_StartExChannelPcm(
+ struct SNDExChannel *chn, const struct SNDWaveParam *wave, const void *data, s32 length)
+{
+ chn->type = SND_CHN_TYPE_PCM;
+ chn->waveParam = *wave;
+ chn->waveDataPtr = data;
+ ExChannelStart(chn, length);
+ return TRUE;
+}
+
+BOOL SND_StartExChannelPsg(struct SNDExChannel *chn, s32 duty, s32 length)
+{
+ if (chn->id < 8)
+ {
+ return FALSE;
+ }
+ else if (chn->id > 13)
+ {
+ return FALSE;
+ }
+ else
+ {
+ chn->type = SND_CHN_TYPE_PSG;
+ chn->dutyCycle = duty;
+ chn->waveParam.timer = 8006;
+ ExChannelStart(chn, length);
+ return TRUE;
+ }
+}
+
+BOOL SND_StartExChannelNoise(struct SNDExChannel *chn, s32 length)
+{
+ if (chn->id < 14)
+ {
+ return FALSE;
+ }
+ else if (chn->id > 15)
+ {
+ return FALSE;
+ }
+ else
+ {
+ chn->type = SND_CHN_TYPE_NOISE;
+ chn->waveParam.timer = 8006;
+ ExChannelStart(chn, length);
+ return TRUE;
+ }
+}
+
+s32 SND_UpdateExChannelEnvelope(struct SNDExChannel *chn, BOOL step)
+{
+ s32 sustain;
+
+ if (step)
+ {
+ switch (chn->envStatus)
+ {
+ case SND_ENV_ATTACK:
+ chn->envAttenuation = -((-chn->envAttenuation * chn->envAttack) >> 8);
+ if (chn->envAttenuation == 0)
+ chn->envStatus = SND_ENV_DECAY;
+ break;
+ case SND_ENV_DECAY:
+ sustain = SNDi_DecibelSquareTable[chn->envSustain] << 7;
+ chn->envAttenuation -= chn->envDecay;
+ if (chn->envAttenuation <= sustain)
+ {
+ chn->envAttenuation = sustain;
+ chn->envStatus = SND_ENV_SUSTAIN;
+ }
+ break;
+ case SND_ENV_SUSTAIN:
+ break;
+ case SND_ENV_RELEASE:
+ chn->envAttenuation -= chn->envRelease;
+ break;
+ }
+ }
+
+ return chn->envAttenuation >> 7;
+}
+
+void SND_SetExChannelAttack(struct SNDExChannel *chn, s32 attack)
+{
+ if (attack < 109)
+ chn->envAttack = (u8)(255 - attack);
+ else
+ chn->envAttack = sAttackCoeffTable[127 - attack];
+}
+
+void SND_SetExChannelDecay(struct SNDExChannel *chn, s32 decay)
+{
+ chn->envDecay = CalcDecayCoeff(decay);
+}
+
+void SND_SetExChannelSustain(struct SNDExChannel *chn, s32 sustain)
+{
+ chn->envSustain = (u8)sustain;
+}
+
+void SND_SetExChannelRelease(struct SNDExChannel *chn, s32 release)
+{
+ chn->envRelease = CalcDecayCoeff(release);
+}
+
+void SND_ReleaseExChannel(struct SNDExChannel *chn)
+{
+ chn->envStatus = SND_ENV_RELEASE;
+}
+
+BOOL SND_IsExChannelActive(struct SNDExChannel *chn)
+{
+ return chn->flags.active;
+}
+
+struct SNDExChannel *SND_AllocExChannel(
+ u32 channelMask, int priority, u32 flags, SNDExChannelCallback callback, void *callbackUserData)
+{
+ struct SNDExChannel *chnPrev;
+ int i;
+ struct SNDExChannel *chn;
+ u8 channelCandidate;
+
+ channelMask &= ~sLockedChannelMask;
+ if (flags == 0)
+ channelMask &= ~sWeakLockedChannelMask;
+
+ chnPrev = NULL;
+
+ for (i = 0; i < SND_CHANNEL_COUNT; i++)
+ {
+ channelCandidate = sChannelAllocationOrder[i];
+
+ if (channelMask & (1 << channelCandidate))
+ {
+ chn = &SNDi_Work.channels[channelCandidate];
+
+ if (chnPrev == NULL)
+ {
+ chnPrev = chn;
+ continue;
+ }
+
+ if (chn->priority > chnPrev->priority)
+ continue;
+
+ if (chn->priority != chnPrev->priority || ExChannelVolumeCmp(chnPrev, chn) < 0)
+ chnPrev = chn;
+ }
+ }
+
+ if (chnPrev == NULL)
+ return NULL;
+
+ if (priority < chnPrev->priority)
+ return NULL;
+
+ if (chnPrev->callback)
+ chnPrev->callback(chnPrev, 0, chnPrev->callbackUserData);
+
+ chnPrev->flags.syncFlag = 2;
+ chnPrev->flags.active = 0;
+ ExChannelSetup(chnPrev, callback, callbackUserData, priority);
+ return chnPrev;
+}
+
+void SND_FreeExChannel(struct SNDExChannel *chn)
+{
+ if (chn)
+ {
+ chn->callback = NULL;
+ chn->callbackUserData = NULL;
+ }
+}
+
+void SND_StopUnlockedChannel(u32 channelMask, u32 weak)
+{
+ (void)weak;
+
+ struct SNDExChannel *chn;
+
+ for (int i = 0; i < SND_CHANNEL_COUNT && channelMask != 0; i++, channelMask >>= 1)
+ {
+ if ((channelMask & 1) == 0)
+ continue;
+
+ chn = &SNDi_Work.channels[i];
+
+ if (sLockedChannelMask & (1 << i))
+ continue;
+
+ if (chn->callback)
+ chn->callback(chn, 0, chn->callbackUserData);
+
+ SND_StopChannel(i, 0);
+ chn->priority = 0;
+ SND_FreeExChannel(chn);
+ chn->flags.syncFlag = 0;
+ chn->flags.active = 0;
+ }
+}
+
+void SND_LockChannel(u32 channelMask, u32 weak)
+{
+ struct SNDExChannel *chn;
+ u32 j = channelMask;
+ int i = 0;
+
+ for (; i < SND_CHANNEL_COUNT && j != 0; i++, j >>= 1)
+ {
+ if ((j & 1) == 0)
+ continue;
+
+ chn = &SNDi_Work.channels[i];
+
+ if (sLockedChannelMask & (1 << i))
+ continue;
+
+ if (chn->callback)
+ chn->callback(chn, 0, chn->callbackUserData);
+
+ SND_StopChannel(i, 0);
+ chn->priority = 0;
+ SND_FreeExChannel(chn);
+ chn->flags.syncFlag = 0;
+ chn->flags.active = 0;
+ }
+
+ if (weak & 1)
+ {
+ sWeakLockedChannelMask |= channelMask;
+ }
+ else
+ {
+ sLockedChannelMask |= channelMask;
+ }
+}
+
+void SND_UnlockChannel(u32 channelMask, u32 weak)
+{
+ if (weak & 1)
+ {
+ sWeakLockedChannelMask &= ~channelMask;
+ }
+ else
+ {
+ sLockedChannelMask &= ~channelMask;
+ }
+}
+
+u32 SND_GetLockedChannel(u32 weak)
+{
+ if (weak & 1)
+ {
+ return sWeakLockedChannelMask;
+ }
+ else
+ {
+ return sLockedChannelMask;
+ }
+}
+
+void SND_InvalidateWave(const void *start, const void *end)
+{
+ for (u8 i = 0; i < SND_CHANNEL_COUNT; i++)
+ {
+ struct SNDExChannel *chn = &SNDi_Work.channels[i];
+
+ if (chn->flags.active && chn->type == 0 && start <= chn->waveDataPtr &&
+ chn->waveDataPtr <= end)
+ {
+ chn->flags.start = FALSE;
+ SND_StopChannel(i, 0);
+ }
+ }
+}
+
+void SND_InitLfoParam(struct SNDLfoParam *lfoParam)
+{
+ lfoParam->target = SND_LFO_PITCH;
+ lfoParam->depth = 0;
+ lfoParam->range = 1;
+ lfoParam->speed = 16;
+ lfoParam->delay = 0;
+}
+
+void SND_StartLfo(struct SNDLfo *lfo)
+{
+ lfo->counter = 0;
+ lfo->delayCounter = 0;
+}
+
+void SND_UpdateLfo(struct SNDLfo *lfo)
+{
+ if (lfo->delayCounter < lfo->param.delay)
+ {
+ lfo->delayCounter++;
+ }
+ else
+ {
+ u32 tmp = lfo->counter;
+ tmp += lfo->param.speed << 6;
+ tmp >>= 8;
+ while (tmp >= 0x80)
+ {
+ tmp -= 0x80;
+ }
+ lfo->counter += lfo->param.speed << 6;
+ lfo->counter &= 0xFF;
+ lfo->counter |= tmp << 8;
+ }
+}
+
+int SND_GetLfoValue(struct SNDLfo *lfo)
+{
+ if (lfo->param.depth == 0)
+ {
+ return 0;
+ }
+ else if (lfo->delayCounter < lfo->param.delay)
+ {
+ return 0;
+ }
+ else
+ {
+ return SND_SinIdx((s32)((u32)lfo->counter >> 8)) * lfo->param.depth * lfo->param.range;
+ }
+}
+
+static u16 CalcDecayCoeff(int vol)
+{
+ if (vol == 127)
+ return 0xFFFF;
+ else if (vol == 126)
+ return 0x3C00;
+ else if (vol < 50)
+ return (u16)(vol * 2 + 1);
+ else
+ return (u16)(0x1E00 / (126 - vol));
+}
+
+static void ExChannelSetup(
+ struct SNDExChannel *chn, SNDExChannelCallback callback, void *callbackUserData, int priority)
+{
+ chn->channelLLNext = NULL;
+ chn->callback = callback;
+ chn->callbackUserData = callbackUserData;
+ chn->length = 0;
+ chn->priority = (u8)priority;
+ chn->volume = 127;
+ chn->flags.start = FALSE;
+ chn->flags.autoSweep = TRUE;
+ chn->midiKey = 60;
+ chn->rootMidiKey = 60;
+ chn->velocity = 127;
+ chn->initPan = 0;
+ chn->userDecay = 0;
+ chn->userDecay2 = 0;
+ chn->userPitch = 0;
+ chn->userPan = 0;
+ chn->panRange = 127;
+ chn->sweepPitch = 0;
+ chn->sweepLength = 0;
+ chn->sweepCounter = 0;
+
+ SND_SetExChannelAttack(chn, 127);
+ SND_SetExChannelDecay(chn, 127);
+ SND_SetExChannelSustain(chn, 127);
+ SND_SetExChannelRelease(chn, 127);
+ SND_InitLfoParam(&chn->lfo.param);
+}
+
+static void ExChannelStart(struct SNDExChannel *chn, int length)
+{
+ chn->envAttenuation = -92544;
+ chn->envStatus = 0;
+ chn->length = length;
+ SND_StartLfo(&chn->lfo);
+ chn->flags.start = TRUE;
+ chn->flags.active = TRUE;
+}
+
+static int ExChannelVolumeCmp(struct SNDExChannel *chn_a, struct SNDExChannel *chn_b)
+{
+ int vol_a = chn_a->volume & 0xFF;
+ int vol_b = chn_b->volume & 0xFF;
+
+ vol_a <<= 4;
+ vol_b <<= 4;
+
+ vol_a >>= sSampleDataShiftTable[chn_a->volume >> 8];
+ vol_b >>= sSampleDataShiftTable[chn_b->volume >> 8];
+
+ if (vol_a != vol_b)
+ {
+ if (vol_a < vol_b)
+ return 1;
+ else
+ return -1;
+ }
+ return 0;
+}
+
+static int ExChannelSweepUpdate(struct SNDExChannel *chn, BOOL step)
+{
+ s64 result;
+
+ if (chn->sweepPitch == 0)
+ {
+ result = 0;
+ }
+ else if (chn->sweepCounter >= chn->sweepLength)
+ {
+ result = 0;
+ }
+ else
+ {
+ result = (s64)chn->sweepPitch * (chn->sweepLength - chn->sweepCounter) / chn->sweepLength;
+
+ if (step && chn->flags.autoSweep)
+ chn->sweepCounter++;
+ }
+
+ return (int)result;
+}
+
+static int ExChannelLfoUpdate(struct SNDExChannel *chn, BOOL step)
+{
+ s64 result = SND_GetLfoValue(&chn->lfo);
+
+ if (result != 0)
+ {
+ switch (chn->lfo.param.target)
+ {
+ case SND_LFO_VOLUME:
+ result *= 60;
+ break;
+ case SND_LFO_PITCH:
+ result <<= 6;
+ break;
+ case SND_LFO_PAN:
+ result <<= 6;
+ break;
+ }
+ result >>= 14;
+ }
+
+ if (step)
+ {
+ SND_UpdateLfo(&chn->lfo);
+ }
+
+ return (int)result;
+}
diff --git a/arm7/lib/src/SND_global.c b/arm7/lib/src/SND_global.c
new file mode 100644
index 00000000..c78fcd5f
--- /dev/null
+++ b/arm7/lib/src/SND_global.c
@@ -0,0 +1,63 @@
+#include "SND_global.h"
+
+#include "SND_channel.h"
+#include "SND_work.h"
+
+#include "OS_system.h"
+#include "PM.h"
+#include "registers.h"
+#include "syscall.h"
+
+void SND_Enable(void)
+{
+ reg_SOUNDCNT_MIX |= 0x80;
+}
+
+void SND_Disable(void)
+{
+ reg_SOUNDCNT_MIX &= ~0x80;
+}
+
+void SND_Shutdown(void)
+{
+ SND_Disable();
+
+ for (int i = 0; i < SND_CHANNEL_COUNT; i++)
+ {
+ SND_StopChannel(i, 1);
+ }
+
+ reg_SNDCAPxCNT(0) = 0;
+ reg_SNDCAPxCNT(1) = 0;
+}
+
+void SND_BeginSleep(void)
+{
+ SND_Disable();
+ SVC_SoundBiasReset(0x80);
+ OS_SpinWait(0x40000);
+ PMi_ResetControl(1);
+ reg_POWCNT2 &= ~1;
+}
+
+void SND_EndSleep(void)
+{
+ reg_POWCNT2 |= 1; // enable speakers
+ PMi_SetControl(1);
+ SVC_SoundBiasSet(0x100);
+ OS_SpinWait(0x7AB80); // what does this wait for and how long does it wait?
+ SND_Enable();
+}
+
+void SND_SetMasterVolume(int vol)
+{
+ reg_SOUNDCNT_VOL = (u8)vol;
+}
+
+void SND_SetOutputSelector(
+ int leftOutputFrom, int rightOutputFrom, int outputCh1ToMixer, int outputCh3ToMixer)
+{
+ int masterEnable = (reg_SOUNDCNT_MIX & 0x80) ? 1 : 0;
+ reg_SOUNDCNT_MIX = (u8)((masterEnable << 7) | (outputCh3ToMixer << 5) |
+ (outputCh1ToMixer << 4) | (rightOutputFrom << 2) | (leftOutputFrom));
+}
diff --git a/arm7/lib/src/SND_main.c b/arm7/lib/src/SND_main.c
new file mode 100644
index 00000000..fbf84573
--- /dev/null
+++ b/arm7/lib/src/SND_main.c
@@ -0,0 +1,133 @@
+#include "SND_main.h"
+
+#include "global.h"
+
+#include "SND_alarm.h"
+#include "SND_capture.h"
+#include "SND_command.h"
+#include "SND_exChannel.h"
+#include "SND_global.h"
+#include "SND_seq.h"
+#include "SND_util.h"
+#include "SND_work.h"
+
+#include "OS_alarm.h"
+#include "OS_message.h"
+#include "OS_thread.h"
+#include "OS_tick.h"
+
+static void FUN_037fc330(void *);
+static void SndThread(void *);
+
+u8 sThreadStack[0x400];
+OSThread sThread;
+OSAlarm sAlarm;
+OSMessageQueue sMessageQueue;
+OSMessage sMessageArray[8];
+u32 sInitialized = 0;
+
+void SND_Init(u32 priority)
+{
+ if (sInitialized == 0)
+ {
+ sInitialized = 1;
+ SND_CommandInit();
+ SND_CreateThread(priority);
+ }
+}
+
+void SND_CreateThread(u32 priority)
+{
+ OS_CreateThread(&sThread,
+ SndThread,
+ NULL,
+ &sThreadStack[sizeof(sThreadStack)],
+ sizeof(sThreadStack),
+ priority);
+ OS_WakeupThreadDirect(&sThread);
+}
+
+void SND_InitIntervalTimer(void)
+{
+ OS_InitMessageQueue(&sMessageQueue, sMessageArray, 8);
+ OS_CreateAlarm(&sAlarm);
+}
+
+void SND_StartIntervalTimer(void)
+{
+ OSTick tick = OS_GetTick();
+ OS_SetPeriodicAlarm(&sAlarm, tick + 0x10000, 2728, FUN_037fc330, NULL);
+}
+
+void SND_StopIntervalTimer(void)
+{
+ OS_CancelAlarm(&sAlarm);
+}
+
+u32 SND_WaitForIntervalTimer(void)
+{
+ OSMessage result;
+ (void)OS_ReceiveMessage(&sMessageQueue, &result, 1);
+ return (u32)result;
+}
+
+void SND_SendWakeupMessage(void)
+{
+ (void)OS_SendMessage(&sMessageQueue, (OSMessage)2, 0);
+}
+
+void SNDi_LockMutex(void)
+{
+ // nothing
+}
+
+void SNDi_UnlockMutex(void)
+{
+ // nothing
+}
+
+static void FUN_037fc330(void *arg)
+{
+ (void)arg;
+ (void)OS_SendMessage(&sMessageQueue, (OSMessage)1, 0);
+}
+
+static void SndThread(void *arg)
+{
+ (void)arg;
+
+ SND_InitIntervalTimer();
+ SND_ExChannelInit();
+ SND_SeqInit();
+ SND_AlarmInit();
+ SND_Enable();
+ SND_SetOutputSelector(0, 0, 0, 0);
+ SND_SetMasterVolume(0x7F);
+ SND_StartIntervalTimer();
+
+ while (1)
+ {
+ BOOL update = FALSE;
+
+ u32 result = SND_WaitForIntervalTimer();
+
+ switch (result)
+ {
+ case 2:
+ update = FALSE;
+ break;
+ case 1:
+ update = TRUE;
+ break;
+ }
+
+ SND_UpdateExChannel();
+ SND_CommandProc();
+
+ SND_SeqMain(update);
+ SND_ExChannelMain(update);
+
+ SND_UpdateSharedWork();
+ (void)SND_CalcRandom();
+ }
+}
diff --git a/arm7/lib/src/SND_seq.c b/arm7/lib/src/SND_seq.c
new file mode 100644
index 00000000..15b60596
--- /dev/null
+++ b/arm7/lib/src/SND_seq.c
@@ -0,0 +1,1324 @@
+#include "SND_seq.h"
+
+#include "SND_bank.h"
+#include "SND_exChannel.h"
+#include "SND_lfo.h"
+#include "SND_main.h"
+#include "SND_util.h"
+#include "SND_work.h"
+
+#define SND_TIMER_RATE 240
+
+#define SND_TRACK_MUTE_MODE_UNMUTE 0
+#define SND_TRACK_MUTE_MODE_MUTE 1
+#define SND_TRACK_MUTE_MODE_MUTE_RELEASE 2
+#define SND_TRACK_MUTE_MODE_MUTE_STOP 3
+
+enum SNDSeqProc
+{
+ SND_PROC_SKIP_NOTES = 0,
+ SND_PROC_PLAY_NOTES = 1,
+};
+
+enum SNDSeqVal
+{
+ SND_SEQ_VAL_U8 = 0,
+ SND_SEQ_VAL_U16 = 1,
+ SND_SEQ_VAL_VLV = 2,
+ SND_SEQ_VAL_RAN = 3,
+ SND_SEQ_VAL_VAR = 4,
+};
+
+static struct
+{
+ const u8 *begin;
+ const u8 *end;
+ union
+ {
+ u8 buf[16];
+ u32 buf32[4];
+ };
+} sSeqCache;
+static int sPrintEnabled;
+
+static void PlayerSeqMain(struct SNDPlayer *player);
+static void PlayerUpdateChannel(struct SNDPlayer *player);
+static void PlayerStop(struct SNDPlayer *player);
+static void PlayerInit(struct SNDPlayer *player, struct SNDBankData *bank);
+static int AllocateTrack(void);
+static void TrackInit(struct SNDTrack *track);
+static void TrackStart(struct SNDTrack *track, const void *seq, u32 offset);
+static void SeqCacheFetch(const u8 *addr);
+static u8 SeqReadByte(const u8 *addr);
+static u16 TrackReadU16(struct SNDTrack *track);
+static struct SNDTrack *PlayerGetTrack(struct SNDPlayer *player, int track);
+static void TrackReleaseChannels(struct SNDTrack *track, struct SNDPlayer *player, int release);
+static void TrackFreeChannels(struct SNDTrack *track);
+static BOOL PlayerStepTicks(struct SNDPlayer *player, u32 ticks);
+static void TrackMute(struct SNDTrack *track, struct SNDPlayer *player, int muteMode);
+static s16 *PlayerGetVariablePointer(const struct SNDPlayer *player, int var);
+static void TrackUpdateChannel(struct SNDTrack *track, struct SNDPlayer *player, int release);
+
+void SND_SeqInit(void)
+{
+ for (int i = 0; i < SND_PLAYER_COUNT; i++)
+ {
+ struct SNDPlayer *ply = &SNDi_Work.players[i];
+
+ ply->flags.active = FALSE;
+ ply->playerId = (u8)i;
+ }
+
+ for (int i = 0; i < SND_TRACK_COUNT; i++)
+ {
+ struct SNDTrack *trk = &SNDi_Work.tracks[i];
+
+ trk->flags.active = FALSE;
+ }
+}
+
+void SND_SeqMain(BOOL step)
+{
+ struct SNDPlayer *ply;
+ int i;
+ u32 playerStatus = 0;
+
+ for (i = 0; i < SND_PLAYER_COUNT; i++)
+ {
+ ply = &SNDi_Work.players[i];
+
+ if (!ply->flags.active)
+ continue;
+
+ if (ply->flags.prepared)
+ {
+ if (step && !ply->flags.paused)
+ PlayerSeqMain(ply);
+ PlayerUpdateChannel(ply);
+ }
+
+ if (ply->flags.active)
+ playerStatus |= 1 << i;
+ }
+
+ if (SNDi_SharedWork)
+ SNDi_SharedWork->playerStatus = playerStatus;
+}
+
+void SND_PrepareSeq(int player, const void *seq, u32 offset, struct SNDBankData *bankData)
+{
+ struct SNDPlayer *ply = &SNDi_Work.players[player];
+
+ if (ply->flags.active)
+ PlayerStop(ply);
+
+ PlayerInit(ply, bankData);
+
+ int allocTrkIdx = AllocateTrack();
+
+ if (allocTrkIdx < 0)
+ return;
+
+ struct SNDTrack *trk = &SNDi_Work.tracks[allocTrkIdx];
+ TrackInit(trk);
+ TrackStart(trk, seq, offset);
+ ply->tracks[0] = (u8)allocTrkIdx;
+ SeqCacheFetch(trk->cur);
+
+ u8 cmd = SeqReadByte(trk->cur);
+
+ trk->cur++;
+
+ if (cmd != 0xFE)
+ {
+ trk->cur--;
+ }
+ else
+ {
+ int track;
+ u16 trackMask;
+
+ for (trackMask = (u16)(TrackReadU16(trk) >> 1), track = 1; trackMask != 0;
+ track++, trackMask >>= 1)
+ {
+ if (trackMask & 1)
+ {
+ allocTrkIdx = AllocateTrack();
+ if (allocTrkIdx < 0)
+ break;
+ TrackInit(&SNDi_Work.tracks[allocTrkIdx]);
+ ply->tracks[track] = (u8)allocTrkIdx;
+ }
+ }
+ }
+
+ ply->flags.active = TRUE;
+ ply->flags.prepared = FALSE;
+
+ if (SNDi_SharedWork)
+ {
+ SNDi_SharedWork->playerStatus |= 1 << player;
+ }
+}
+
+void SND_StartPreparedSeq(int player)
+{
+ SNDi_Work.players[player].flags.prepared = TRUE;
+}
+
+void SND_StartSeq(int player, const void *seq, u32 offset, struct SNDBankData *bankData)
+{
+ SND_PrepareSeq(player, seq, offset, bankData);
+ SND_StartPreparedSeq(player);
+}
+
+void SND_StopSeq(int player)
+{
+ struct SNDPlayer *ply = &SNDi_Work.players[player];
+
+ if (ply->flags.active)
+ {
+ PlayerStop(ply);
+
+ if (SNDi_SharedWork)
+ {
+ SNDi_SharedWork->playerStatus &= ~(1 << player);
+ }
+ }
+}
+
+void SND_PauseSeq(int player, BOOL flag)
+{
+ struct SNDPlayer *ply = &SNDi_Work.players[player];
+
+ ply->flags.paused = flag;
+
+ if (flag)
+ {
+ for (int i = 0; i < SND_TRACK_COUNT_PER_PLAYER; i++)
+ {
+ struct SNDTrack *trk = PlayerGetTrack(ply, i);
+
+ if (trk)
+ {
+ TrackReleaseChannels(trk, ply, 127);
+ TrackFreeChannels(trk);
+ }
+ }
+ }
+}
+
+void SND_SkipSeq(int player, u32 ticks)
+{
+ struct SNDPlayer *ply = &SNDi_Work.players[player];
+
+ for (int i = 0; i < SND_TRACK_COUNT_PER_PLAYER; i++)
+ {
+ struct SNDTrack *trk = PlayerGetTrack(ply, i);
+
+ if (trk)
+ {
+ TrackReleaseChannels(trk, ply, 127);
+ TrackFreeChannels(trk);
+ }
+ }
+
+ SND_StopIntervalTimer();
+
+ u32 i;
+ for (i = 0; i < ticks; i++)
+ {
+ if (PlayerStepTicks(ply, 0))
+ {
+ PlayerStop(ply);
+ break;
+ }
+ }
+
+ SND_StartIntervalTimer();
+
+ if (SNDi_SharedWork)
+ {
+ SNDi_SharedWork->players[ply->playerId].tickCounter += i;
+ }
+}
+
+void SND_SetTrackMute(int player, u32 trackMask, int muteMode)
+{
+ struct SNDPlayer *ply = &SNDi_Work.players[player];
+
+ for (int i = 0; i < SND_TRACK_COUNT_PER_PLAYER && trackMask != 0; i++, trackMask >>= 1)
+ {
+ if (trackMask & 1)
+ {
+ struct SNDTrack *trk = PlayerGetTrack(ply, i);
+
+ if (trk)
+ {
+ TrackMute(trk, ply, muteMode);
+ }
+ }
+ }
+}
+
+void SND_SetTrackAllocatableChannel(int player, u32 trackMask, u32 channelMask)
+{
+ struct SNDPlayer *ply = &SNDi_Work.players[player];
+
+ for (int i = 0; i < SND_TRACK_COUNT_PER_PLAYER && trackMask != 0; i++, trackMask >>= 1)
+ {
+ if (trackMask & 1)
+ {
+ struct SNDTrack *trk = PlayerGetTrack(ply, i);
+
+ if (trk)
+ {
+ trk->channelMask = (u16)channelMask;
+ trk->flags.channelMask = TRUE;
+ }
+ }
+ }
+}
+
+void SND_InvalidateSeq(const void *start, const void *end)
+{
+ struct SNDPlayer *ply;
+ struct SNDTrack *trk;
+ int i;
+ int j;
+
+ for (i = 0; i < SND_PLAYER_COUNT; i++)
+ {
+ ply = &SNDi_Work.players[i];
+
+ if (!ply->flags.active)
+ continue;
+
+ for (j = 0; j < SND_TRACK_COUNT_PER_PLAYER; j++)
+ {
+ trk = PlayerGetTrack(ply, j);
+
+ if (!trk)
+ continue;
+
+ if (start <= trk->cur && trk->cur <= end)
+ {
+ PlayerStop(ply);
+ break;
+ }
+ }
+ }
+}
+
+void SND_InvalidateBank(const void *start, const void *end)
+{
+ for (int i = 0; i < SND_PLAYER_COUNT; i++)
+ {
+ struct SNDPlayer *ply = &SNDi_Work.players[i];
+
+ if (ply->flags.active && start <= ply->bank && ply->bank <= end)
+ PlayerStop(ply);
+ }
+}
+
+void SNDi_SetPlayerParam(int player, u32 offset, u32 data, int size)
+{
+ struct SNDPlayer *ply = &SNDi_Work.players[player];
+
+ switch (size)
+ {
+ case 1:
+ *(u8 *)((u8 *)ply + offset) = (u8)data;
+ break;
+ case 2:
+ *(u16 *)((u8 *)ply + offset) = (u16)data;
+ break;
+ case 4:
+ *(u32 *)((u8 *)ply + offset) = (u32)data;
+ break;
+ }
+}
+
+void SNDi_SetTrackParam(int player, u32 trackMask, u32 offset, u32 data, int size)
+{
+ struct SNDPlayer *ply = &SNDi_Work.players[player];
+
+ for (int i = 0; i < SND_TRACK_COUNT_PER_PLAYER && trackMask != 0; i++, trackMask >>= 1)
+ {
+ if (!(trackMask & 1))
+ continue;
+
+ struct SNDTrack *trk = PlayerGetTrack(ply, i);
+
+ if (!trk)
+ continue;
+
+ switch (size)
+ {
+ case 1:
+ *(u8 *)((u8 *)trk + offset) = (u8)data;
+ break;
+ case 2:
+ *(u16 *)((u8 *)trk + offset) = (u16)data;
+ break;
+ case 4:
+ *(u32 *)((u8 *)trk + offset) = (u32)data;
+ break;
+ }
+ }
+}
+
+static void SeqCacheFetch(const u8 *addr)
+{
+ addr = (const u8 *)((u32)addr & ~3);
+ sSeqCache.begin = addr;
+ sSeqCache.end = sSeqCache.begin + 16;
+
+ const u32 *src = (const u32 *)addr;
+
+ sSeqCache.buf32[0] = src[0];
+ sSeqCache.buf32[1] = src[1];
+ sSeqCache.buf32[2] = src[2];
+ sSeqCache.buf32[3] = src[3];
+}
+
+static u8 SeqReadByte(const u8 *addr)
+{
+ if (addr < sSeqCache.begin || addr >= sSeqCache.end)
+ SeqCacheFetch(addr);
+
+ return sSeqCache.buf[(u32)addr - (u32)sSeqCache.begin];
+}
+
+static inline u8 TrackReadU8(struct SNDTrack *track)
+{
+ u8 retval = SeqReadByte(track->cur);
+ track->cur++;
+ return retval;
+}
+
+static u16 TrackReadU16(struct SNDTrack *track)
+{
+ int retval;
+
+ retval = TrackReadU8(track);
+ retval |= TrackReadU8(track) << 8;
+
+ return (u16)retval;
+}
+
+static u32 TrackReadU24(struct SNDTrack *track)
+{
+ u32 retval;
+
+ retval = TrackReadU8(track);
+ retval |= TrackReadU8(track) << 8;
+ retval |= TrackReadU8(track) << 16;
+
+ return retval;
+}
+
+static int TrackReadVLV(struct SNDTrack *track)
+{
+ int retval = 0;
+ int b;
+
+ do
+ {
+ b = TrackReadU8(track);
+ retval = (retval << 7) | (b & 0x7F);
+ } while (b & 0x80);
+
+ return retval;
+}
+
+static int TrackParseValue(struct SNDTrack *track, struct SNDPlayer *player, int valueType)
+{
+ int retval;
+ int hi;
+ int lo;
+ int ran;
+ s16 *var;
+
+ // BUG: undefined behavior if invalid valueType is passed (uninitialized return value)
+
+ switch (valueType)
+ {
+ case SND_SEQ_VAL_U8:
+ retval = TrackReadU8(track);
+ break;
+ case SND_SEQ_VAL_U16:
+ retval = TrackReadU16(track);
+ break;
+ case SND_SEQ_VAL_VLV:
+ retval = TrackReadVLV(track);
+ break;
+ case SND_SEQ_VAL_VAR:
+ var = PlayerGetVariablePointer(player, TrackReadU8(track));
+ if (var)
+ retval = *var;
+ break;
+ case SND_SEQ_VAL_RAN:
+ lo = TrackReadU16(track) << 16;
+ hi = (s16)TrackReadU16(track);
+ ran = SND_CalcRandom();
+ retval = hi - (lo >> 16);
+ retval += 1;
+ retval = (ran * retval) >> 16;
+ retval += lo >> 16;
+ break;
+ }
+
+ return retval;
+}
+
+static void TrackInit(struct SNDTrack *track)
+{
+ track->base = NULL;
+ track->cur = NULL;
+
+ track->flags.noteWait = TRUE;
+ track->flags.muted = FALSE;
+ track->flags.tie = FALSE;
+ track->flags.noteFinishWait = FALSE;
+ track->flags.portamento = FALSE;
+ track->flags.cmp = TRUE;
+ track->flags.channelMask = FALSE;
+
+ track->callStackDepth = 0;
+ track->program = 0;
+ track->priority = 64;
+ track->volume = 127;
+ track->expression = 127;
+ track->extFader = 0;
+ track->pan = 0;
+ track->extPan = 0;
+ track->pitchBend = 0;
+ track->extPitch = 0;
+ track->envAttack = 255;
+ track->envDecay = 255;
+ track->envSustain = 255;
+ track->envRelease = 255;
+ track->panRange = 127;
+ track->bendRange = 2;
+ track->portamentoKey = 60;
+ track->portamentoTime = 0;
+ track->sweepPitch = 0;
+ track->transpose = 0;
+ track->channelMask = 0xFFFF;
+ SND_InitLfoParam(&track->mod);
+ track->wait = 0;
+ track->channelLLHead = NULL;
+}
+
+static void TrackStart(struct SNDTrack *track, const void *seq, u32 offset)
+{
+ track->base = (const u8 *)seq;
+ track->cur = &track->base[offset];
+}
+
+static void PlayerInit(struct SNDPlayer *player, struct SNDBankData *bank)
+{
+ player->flags.paused = FALSE;
+ player->bank = bank;
+ player->tempo = 120;
+ player->tempoRatio = 256;
+ player->tempoCounter = SND_TIMER_RATE;
+ player->volume = 0x7F;
+ player->extFader = 0;
+ player->prio = 64;
+
+ for (int i = 0; i < SND_TRACK_COUNT_PER_PLAYER; i++)
+ {
+ player->tracks[i] = 0xFF;
+ }
+
+ if (SNDi_SharedWork)
+ {
+ SNDi_SharedWork->players[player->playerId].tickCounter = 0;
+
+ for (int i = 0; i < SND_TRACK_COUNT_PER_PLAYER; i++)
+ {
+ SNDi_SharedWork->players[player->playerId].localVars[i] = -1;
+ }
+ }
+}
+
+static void TrackReleaseChannels(struct SNDTrack *track, struct SNDPlayer *player, int release)
+{
+ TrackUpdateChannel(track, player, 0);
+
+ for (struct SNDExChannel *chn = track->channelLLHead; chn; chn = chn->channelLLNext)
+ {
+ if (SND_IsExChannelActive(chn))
+ {
+ if (release >= 0)
+ SND_SetExChannelRelease(chn, release & 0xFF);
+ chn->priority = 1;
+ SND_ReleaseExChannel(chn);
+ }
+ }
+}
+
+static void TrackFreeChannels(struct SNDTrack *track)
+{
+ for (struct SNDExChannel *chn = track->channelLLHead; chn; chn = chn->channelLLNext)
+ {
+ SND_FreeExChannel(chn);
+ }
+
+ track->channelLLHead = NULL;
+}
+
+static void PlayerSeqMain(struct SNDPlayer *player)
+{
+ int ticks = 0;
+ int tempoInc;
+ int i;
+
+ while (player->tempoCounter >= SND_TIMER_RATE)
+ {
+ player->tempoCounter -= SND_TIMER_RATE;
+ ticks++;
+ }
+
+ for (i = 0; i < ticks; i++)
+ {
+ if (PlayerStepTicks(player, SND_PROC_PLAY_NOTES))
+ {
+ PlayerStop(player);
+ break;
+ }
+ }
+
+ if (SNDi_SharedWork)
+ {
+ SNDi_SharedWork->players[player->playerId].tickCounter += i;
+ }
+
+ tempoInc = player->tempo;
+ tempoInc *= player->tempoRatio;
+ tempoInc >>= 8;
+
+ player->tempoCounter += tempoInc;
+}
+
+static struct SNDTrack *PlayerGetTrack(struct SNDPlayer *player, int track)
+{
+ if (track > (SND_TRACK_COUNT_PER_PLAYER - 1))
+ return NULL;
+
+ if (player->tracks[track] == 0xFF)
+ return NULL;
+
+ return &SNDi_Work.tracks[player->tracks[track]];
+}
+
+static void TrackStop(struct SNDTrack *track, struct SNDPlayer *player)
+{
+ TrackReleaseChannels(track, player, -1);
+ TrackFreeChannels(track);
+}
+
+static void PlayerStopTrack(struct SNDPlayer *player, int trackIdx)
+{
+ struct SNDTrack *track = PlayerGetTrack(player, trackIdx);
+
+ if (track == NULL)
+ return;
+
+ TrackStop(track, player);
+ SNDi_Work.tracks[player->tracks[trackIdx]].flags.active = FALSE;
+ player->tracks[trackIdx] = 0xFF;
+}
+
+static void PlayerStop(struct SNDPlayer *player)
+{
+ for (int i = 0; i < SND_TRACK_COUNT_PER_PLAYER; i++)
+ {
+ PlayerStopTrack(player, i);
+ }
+ player->flags.active = FALSE;
+}
+
+static void ChannelCallback(struct SNDExChannel *chn, int status, void *track_)
+{
+ struct SNDExChannel *cur;
+ struct SNDTrack *track;
+
+ track = (struct SNDTrack *)track_;
+
+ if (status == 1)
+ {
+ chn->priority = 0;
+ SND_FreeExChannel(chn);
+ }
+
+ if (track->channelLLHead == chn)
+ {
+ track->channelLLHead = chn->channelLLNext;
+ }
+ else
+ {
+ cur = track->channelLLHead;
+
+ while (cur->channelLLNext)
+ {
+ if (cur->channelLLNext == chn)
+ {
+ cur->channelLLNext = chn->channelLLNext;
+ return;
+ }
+ cur = cur->channelLLNext;
+ }
+ }
+}
+
+static void TrackUpdateChannel(struct SNDTrack *track, struct SNDPlayer *player, int release)
+{
+ int vol;
+ int fader;
+ int pan;
+ int pitch;
+
+ vol = SNDi_DecibelSquareTable[track->volume] + SNDi_DecibelSquareTable[track->expression] +
+ SNDi_DecibelSquareTable[player->volume];
+
+ fader = track->extFader + player->extFader;
+
+ pitch = track->pitchBend;
+ pitch *= track->bendRange << 6;
+ pitch >>= 7;
+ pitch += track->extPitch;
+
+ pan = track->pan;
+
+ if (track->panRange != 127)
+ pan = (pan * track->panRange + 0x40) >> 7;
+
+ pan += track->extPan;
+
+ if (vol < -0x8000)
+ vol = -0x8000;
+
+ if (fader < -0x8000)
+ fader = -0x8000;
+
+ if (pan < -128)
+ pan = -128;
+ else if (pan > 127)
+ pan = 127;
+
+ for (struct SNDExChannel *chn = track->channelLLHead; chn != NULL; chn = chn->channelLLNext)
+ {
+ chn->userDecay2 = (s16)fader;
+
+ if (chn->envStatus == 3)
+ continue;
+
+ chn->userDecay = (s16)vol;
+ chn->userPitch = (s16)pitch;
+ chn->userPan = (s8)pan;
+ chn->panRange = track->panRange;
+ chn->lfo.param = track->mod;
+
+ if (chn->length == 0 && release != 0)
+ {
+ chn->priority = 1;
+ SND_ReleaseExChannel(chn);
+ }
+ }
+}
+
+static void PlayerUpdateChannel(struct SNDPlayer *player)
+{
+ for (int i = 0; i < SND_TRACK_COUNT_PER_PLAYER; i++)
+ {
+ struct SNDTrack *trk = PlayerGetTrack(player, i);
+ if (trk)
+ {
+ TrackUpdateChannel(trk, player, 1);
+ }
+ }
+}
+
+static void TrackPlayNote(
+ struct SNDTrack *track, struct SNDPlayer *player, int midiKey, int velocity, int length)
+{
+ struct SNDExChannel *chn = NULL;
+
+ if (track->flags.tie)
+ {
+ chn = track->channelLLHead;
+ if (chn)
+ {
+ chn->midiKey = (u8)midiKey;
+ chn->velocity = (u8)velocity;
+ }
+ }
+
+ if (chn == NULL)
+ {
+ struct SNDInstData inst;
+ if (!SND_ReadInstData(player->bank, track->program, midiKey, &inst))
+ return;
+
+ u32 allowedChannels;
+
+ // get bitmask with allocatable channels based on channel type
+ switch (inst.type)
+ {
+ case SND_INST_PCM:
+ case SND_INST_DIRECTPCM:
+ // all channels support PCM
+ allowedChannels = 0xFFFF;
+ break;
+ case SND_INST_PSG:
+ // only channels 8, 9, 10, 11, 12, 13 support PSG
+ allowedChannels = 0x3F00;
+ break;
+ case SND_INST_NOISE:
+ // only channels 14 and 15 support noise
+ allowedChannels = 0xC000;
+ break;
+ default:
+ return;
+ }
+
+ allowedChannels &= track->channelMask;
+
+ chn = SND_AllocExChannel(allowedChannels,
+ player->prio + track->priority,
+ track->flags.channelMask,
+ ChannelCallback,
+ track);
+ if (chn == NULL)
+ return;
+
+ if (!SND_NoteOn(
+ chn, midiKey, velocity, track->flags.tie ? -1 : length, player->bank, &inst))
+ {
+ chn->priority = 0;
+ SND_FreeExChannel(chn);
+ return;
+ }
+
+ chn->channelLLNext = track->channelLLHead;
+ track->channelLLHead = chn;
+ }
+
+ if (track->envAttack != 0xFF)
+ SND_SetExChannelAttack(chn, track->envAttack);
+
+ if (track->envDecay != 0xFF)
+ SND_SetExChannelDecay(chn, track->envDecay);
+
+ if (track->envSustain != 0xFF)
+ SND_SetExChannelSustain(chn, track->envSustain);
+
+ if (track->envRelease != 0xFF)
+ SND_SetExChannelRelease(chn, track->envRelease);
+
+ chn->sweepPitch = track->sweepPitch;
+ if (track->flags.portamento)
+ chn->sweepPitch += (s16)((track->portamentoKey - midiKey) << 6);
+
+ if (track->portamentoTime == 0)
+ {
+ chn->sweepLength = length;
+ chn->flags.autoSweep = FALSE;
+ }
+ else
+ {
+ int swp = track->portamentoTime * track->portamentoTime;
+ swp *= chn->sweepPitch < 0 ? -chn->sweepPitch : chn->sweepPitch;
+ swp >>= 11;
+ chn->sweepLength = swp;
+ }
+
+ chn->sweepCounter = 0;
+}
+
+static int TrackStepTicks(
+ struct SNDTrack *track, struct SNDPlayer *player, int trackIdx, u32 playNotes)
+{
+ (void)trackIdx;
+
+ struct SNDExChannel *chn;
+ u8 cmd;
+ enum SNDSeqVal valueType;
+ BOOL specialValueType;
+ BOOL runCmd;
+ s32 length;
+ int midiKey;
+
+ s32 par;
+
+ for (chn = track->channelLLHead; chn; chn = chn->channelLLNext)
+ {
+ if (chn->length > 0)
+ chn->length--;
+
+ if (!chn->flags.autoSweep && chn->sweepCounter < chn->sweepLength)
+ chn->sweepCounter++;
+ }
+
+ if (track->flags.noteFinishWait)
+ {
+ if (track->channelLLHead)
+ return 0;
+ track->flags.noteFinishWait = FALSE;
+ }
+
+ if (track->wait > 0)
+ {
+ track->wait--;
+ if (track->wait > 0)
+ return 0;
+ }
+
+ SeqCacheFetch(track->cur);
+
+ while (track->wait == 0 && !track->flags.noteFinishWait)
+ {
+ specialValueType = FALSE;
+ runCmd = TRUE;
+
+ cmd = TrackReadU8(track);
+
+ if (cmd == 0xA2)
+ {
+ cmd = TrackReadU8(track);
+ runCmd = track->flags.cmp;
+ }
+
+ if (cmd == 0xA0)
+ {
+ cmd = TrackReadU8(track);
+ valueType = SND_SEQ_VAL_RAN;
+ specialValueType = TRUE;
+ }
+
+ if (cmd == 0xA1)
+ {
+ cmd = TrackReadU8(track);
+ valueType = SND_SEQ_VAL_VAR;
+ specialValueType = TRUE;
+ }
+
+ if ((cmd & 0x80) == 0)
+ {
+ par = TrackReadU8(track);
+
+ length = TrackParseValue(track, player, specialValueType ? valueType : SND_SEQ_VAL_VLV);
+ midiKey = cmd + track->transpose;
+
+ if (!runCmd)
+ continue;
+
+ if (midiKey < 0)
+ midiKey = 0;
+ else if (midiKey > 127)
+ {
+ midiKey = 127;
+ }
+
+ if (!track->flags.muted && playNotes != 0)
+ {
+ TrackPlayNote(track, player, midiKey, par, (length > 0) ? length : -1);
+ }
+
+ track->portamentoKey = (u8)midiKey;
+
+ if (track->flags.noteWait)
+ {
+ track->wait = length;
+ if (length == 0)
+ {
+ track->flags.noteFinishWait = TRUE;
+ }
+ }
+
+ continue;
+ }
+
+ switch (cmd & 0xF0)
+ {
+ case 0x80:
+ par =
+ TrackParseValue(track, player, specialValueType ? valueType : SND_SEQ_VAL_VLV);
+ if (!runCmd)
+ break;
+
+ switch (cmd)
+ {
+ case 0x80:
+ track->wait = par;
+ break;
+ case 0x81:
+ if (par < 0x10000)
+ track->program = (u16)par;
+ break;
+ }
+ break;
+ case 0x90:
+ switch (cmd)
+ {
+ case 0x93:
+ {
+ u32 off;
+ struct SNDTrack *newTrack;
+
+ par = TrackReadU8(track);
+ off = TrackReadU24(track);
+ if (!runCmd)
+ break;
+
+ newTrack = PlayerGetTrack(player, par);
+ if (newTrack && newTrack != track)
+ {
+ TrackStop(newTrack, player);
+ TrackStart(newTrack, track->base, off);
+ }
+ }
+ break;
+ case 0x94:
+ {
+ u32 off = TrackReadU24(track);
+ if (!runCmd)
+ break;
+ track->cur = &track->base[off];
+ }
+ break;
+ case 0x95:
+ {
+ u32 off = TrackReadU24(track);
+ if (!runCmd)
+ break;
+
+ if (track->callStackDepth < SND_TRACK_MAX_CALL)
+ {
+ track->posCallStack[track->callStackDepth] = track->cur;
+ track->callStackDepth++;
+ track->cur = &track->base[off];
+ }
+ break;
+ }
+ }
+ break;
+ case 0xC0:
+ case 0xD0:
+ {
+ union
+ {
+ u8 _u8;
+ s8 _s8;
+ } par;
+ par._u8 = (u8)TrackParseValue(
+ track, player, specialValueType ? valueType : SND_SEQ_VAL_U8);
+ if (!runCmd)
+ break;
+
+ switch (cmd)
+ {
+ case 0xC1:
+ track->volume = par._u8;
+ break;
+ case 0xD5:
+ track->expression = par._u8;
+ break;
+ case 0xC2:
+ player->volume = par._u8;
+ break;
+ case 0xC5:
+ track->bendRange = par._u8;
+ break;
+ case 0xC6:
+ track->priority = par._u8;
+ break;
+ case 0xC7:
+ track->flags.noteWait = par._u8;
+ break;
+ case 0xCF:
+ track->portamentoTime = par._u8;
+ break;
+ case 0xCA:
+ track->mod.depth = par._u8;
+ break;
+ case 0xCB:
+ track->mod.speed = par._u8;
+ break;
+ case 0xCC:
+ track->mod.target = par._u8;
+ break;
+ case 0xCD:
+ track->mod.range = par._u8;
+ break;
+ case 0xD0:
+ track->envAttack = par._u8;
+ break;
+ case 0xD1:
+ track->envDecay = par._u8;
+ break;
+ case 0xD2:
+ track->envSustain = par._u8;
+ break;
+ case 0xD3:
+ track->envRelease = par._u8;
+ break;
+ case 0xD4:
+ if (track->callStackDepth < SND_TRACK_MAX_CALL)
+ {
+ track->posCallStack[track->callStackDepth] = track->cur;
+ track->loopCount[track->callStackDepth] = par._u8;
+ track->callStackDepth++;
+ }
+ break;
+ case 0xC8:
+ track->flags.tie = par._u8;
+ TrackReleaseChannels(track, player, -1);
+ TrackFreeChannels(track);
+ break;
+ case 0xD7:
+ TrackMute(track, player, par._u8);
+ break;
+ case 0xC9:
+ track->portamentoKey = (u8)(par._u8 + track->transpose);
+ track->flags.portamento = TRUE;
+ break;
+ case 0xCE:
+ track->flags.portamento = par._u8;
+ break;
+ case 0xC3:
+ track->transpose = par._s8;
+ break;
+ case 0xC4:
+ track->pitchBend = par._s8;
+ break;
+ case 0xC0:
+ track->pan = (s8)(par._u8 - 0x40);
+ break;
+ case 0xD6:
+ {
+ s16 *varPtr;
+
+ if (sPrintEnabled)
+ {
+ varPtr = PlayerGetVariablePointer(player, par._u8);
+ }
+ }
+ break;
+ }
+ }
+ break;
+ case 0xE0:
+ {
+ s16 par = (s16)TrackParseValue(
+ track, player, specialValueType ? valueType : SND_SEQ_VAL_U16);
+ if (!runCmd)
+ break;
+
+ switch (cmd)
+ {
+ case 0xE3:
+ track->sweepPitch = par;
+ break;
+ case 0xE1:
+ player->tempo = (u16)par;
+ break;
+ case 0xE0:
+ track->mod.delay = (u16)par;
+ break;
+ }
+ }
+ break;
+ case 0xB0:
+ {
+ int varNum = TrackReadU8(track);
+
+ s16 par = (s16)TrackParseValue(
+ track, player, specialValueType ? valueType : SND_SEQ_VAL_U16);
+ s16 *varPtr = PlayerGetVariablePointer(player, varNum);
+
+ if (!runCmd)
+ break;
+
+ if (varPtr == NULL)
+ break;
+
+ switch (cmd)
+ {
+ case 0xB0:
+ *varPtr = par;
+ break;
+ case 0xB1:
+ *varPtr += par;
+ break;
+ case 0xB2:
+ *varPtr -= par;
+ break;
+ case 0xB3:
+ *varPtr *= par;
+ break;
+ case 0xB4:
+ if (par != 0)
+ *varPtr /= par;
+ break;
+ case 0xB5:
+ if (par >= 0)
+ *varPtr <<= par;
+ else
+ *varPtr >>= -par;
+ break;
+ case 0xB6:
+ {
+ BOOL neg = FALSE;
+ if (par < 0)
+ {
+ neg = TRUE;
+ par = (s16)(-par);
+ }
+ s32 random = SND_CalcRandom();
+ random = (random * (par + 1)) >> 16;
+ if (neg)
+ random = -random;
+ *varPtr = (s16)random;
+ }
+ break;
+ case 0xB7:
+ break;
+ case 0xB8:
+ track->flags.cmp = *varPtr == par;
+ break;
+ case 0xB9:
+ track->flags.cmp = *varPtr >= par;
+ break;
+ case 0xBA:
+ track->flags.cmp = *varPtr > par;
+ break;
+ case 0xBB:
+ track->flags.cmp = *varPtr <= par;
+ break;
+ case 0xBC:
+ track->flags.cmp = *varPtr < par;
+ break;
+ case 0xBD:
+ track->flags.cmp = *varPtr != par;
+ break;
+ }
+ }
+ break;
+ case 0xF0:
+ if (!runCmd)
+ break;
+
+ switch (cmd)
+ {
+ case 0xFD:
+ if (track->callStackDepth != 0)
+ {
+ track->callStackDepth--;
+ track->cur = track->posCallStack[track->callStackDepth];
+ }
+ break;
+ case 0xFC:
+ {
+ if (track->callStackDepth == 0)
+ break;
+
+ // gosh, this was nasty to figure out
+ u8 loopCount = track->loopCount[track->callStackDepth - 1];
+ if (loopCount != 0)
+ {
+ loopCount--;
+ if (loopCount == 0)
+ {
+ track->callStackDepth--;
+ break;
+ }
+ }
+ track->loopCount[track->callStackDepth - 1] = loopCount;
+ track->cur = track->posCallStack[track->callStackDepth - 1];
+ }
+ break;
+ case 0xFE:
+ break;
+ case 0xFF:
+ return -1;
+ }
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static BOOL PlayerStepTicks(struct SNDPlayer *player, u32 ticks)
+{
+ BOOL isPlaying = FALSE;
+
+ for (int i = 0; i < SND_TRACK_COUNT_PER_PLAYER; i++)
+ {
+ struct SNDTrack *trk = PlayerGetTrack(player, i);
+
+ if (trk && trk->cur)
+ {
+ if (TrackStepTicks(trk, player, i, ticks))
+ PlayerStopTrack(player, i);
+ else
+ isPlaying = TRUE;
+ }
+ }
+
+ return !isPlaying;
+}
+
+static s16 *PlayerGetVariablePointer(const struct SNDPlayer *player, int var)
+{
+ if (SNDi_SharedWork == NULL)
+ return NULL;
+ else if (var < 16)
+ {
+ return &SNDi_SharedWork->players[player->playerId].localVars[var];
+ }
+ else
+ {
+ return &SNDi_SharedWork->globalVars[var - 16];
+ }
+}
+
+static int AllocateTrack(void)
+{
+ for (int i = 0; i < SND_TRACK_COUNT; i++)
+ {
+ if (!SNDi_Work.tracks[i].flags.active)
+ {
+ SNDi_Work.tracks[i].flags.active = TRUE;
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+static void TrackMute(struct SNDTrack *track, struct SNDPlayer *player, int muteMode)
+{
+ switch (muteMode)
+ {
+ case SND_TRACK_MUTE_MODE_UNMUTE:
+ track->flags.muted = FALSE;
+ break;
+ case SND_TRACK_MUTE_MODE_MUTE:
+ track->flags.muted = TRUE;
+ break;
+ case SND_TRACK_MUTE_MODE_MUTE_RELEASE:
+ track->flags.muted = TRUE;
+ TrackReleaseChannels(track, player, -1);
+ break;
+ case SND_TRACK_MUTE_MODE_MUTE_STOP:
+ track->flags.muted = TRUE;
+ TrackReleaseChannels(track, player, 127);
+ TrackFreeChannels(track);
+ break;
+ }
+}
diff --git a/arm7/lib/src/SND_util.c b/arm7/lib/src/SND_util.c
new file mode 100644
index 00000000..5c0b220b
--- /dev/null
+++ b/arm7/lib/src/SND_util.c
@@ -0,0 +1,109 @@
+#include "SND_util.h"
+
+#include "syscall.h"
+
+// TODO remove this extern once the static const definition of it is here
+extern s8 sLfoSinTable[0x21];
+
+u16 SND_CalcTimer(int timer, int pitch)
+{
+ int octave = 0;
+ int pitch_normalized = -pitch;
+
+ while (pitch_normalized < 0)
+ {
+ octave--;
+ pitch_normalized += 768;
+ }
+
+ while (pitch_normalized >= 768)
+ {
+ octave++;
+ pitch_normalized -= 768;
+ }
+
+ u64 result = SVC_GetPitchTable(pitch_normalized);
+
+ result += 0x10000;
+ result *= timer;
+
+ int shift = octave - 16;
+
+ if (shift <= 0)
+ {
+ shift = -shift;
+ result >>= shift;
+ }
+ else if (shift < 32)
+ {
+ // clamp in case timer value overflows
+ u64 tmp = result & ~0uLL << (32 - shift);
+ if (tmp != 0)
+ return 0xFFFF;
+ result <<= shift;
+ }
+ else
+ {
+ return 0xFFFF;
+ }
+
+ if (result < 0x10)
+ result = 0x10;
+ else if (result > 0xFFFF)
+ result = 0xFFFF;
+
+ return (u16)result;
+}
+
+u16 SND_CalcChannelVolume(int value)
+{
+ if (value < SND_VOL_DB_MIN)
+ value = SND_VOL_DB_MIN;
+ else if (value > 0)
+ value = 0;
+
+ int result = SVC_GetVolumeTable(value + (-SND_VOL_DB_MIN));
+ int div;
+
+ if (value < -240)
+ div = 3;
+ else if (value < -120)
+ div = 2;
+ else if (value < -60)
+ div = 1;
+ else
+ div = 0;
+
+ return (u16)(result | (div << 8));
+}
+
+s8 SND_SinIdx(int x)
+{
+ // BUG: UB for out of range values
+
+ if (x < 0x20)
+ {
+ return sLfoSinTable[x];
+ }
+ else if (x < 0x40)
+ {
+ return sLfoSinTable[0x40 - x];
+ }
+ else if (x < 0x60)
+ {
+ return (s8)(-sLfoSinTable[x - 0x40]);
+ }
+ else
+ {
+ return (s8)(-sLfoSinTable[0x20 - (x - 0x60)]);
+ }
+}
+
+u16 SND_CalcRandom(void)
+{
+ static u32 state = 0x12345678;
+
+ // values from "Numerical Recipes"
+ state = state * 1664525u + 1013904223u;
+ return (u16)(state >> 16u);
+}
diff --git a/arm7/lib/src/SND_work.c b/arm7/lib/src/SND_work.c
new file mode 100644
index 00000000..2256ee3f
--- /dev/null
+++ b/arm7/lib/src/SND_work.c
@@ -0,0 +1,41 @@
+#include "SND_work.h"
+
+#include "SND_capture.h"
+#include "SND_channel.h"
+#include "SND_exChannel.h"
+
+struct SNDWork SNDi_Work;
+struct SNDSharedWork *SNDi_SharedWork;
+
+void SND_SetPlayerLocalVariable(int player, int var, s16 value)
+{
+ SNDi_SharedWork->players[player].localVars[var] = value;
+}
+
+void SND_SetPlayerGlobalVariable(int var, s16 value)
+{
+ SNDi_SharedWork->globalVars[var] = value;
+}
+
+void SND_UpdateSharedWork(void)
+{
+ u16 channelActiveMask = 0;
+ u16 channelCaptureMask = 0;
+
+ if (SNDi_SharedWork == NULL)
+ return;
+
+ for (s32 i = 0; i < SND_CHANNEL_COUNT; i++)
+ {
+ if (SND_IsChannelActive(i))
+ channelActiveMask |= 1 << i;
+ }
+
+ if (SND_IsCaptureActive(0))
+ channelCaptureMask |= 0x1;
+ if (SND_IsCaptureActive(1))
+ channelCaptureMask |= 0x2;
+
+ SNDi_SharedWork->channelStatus = channelActiveMask;
+ SNDi_SharedWork->captureStatus = channelCaptureMask;
+}