diff options
Diffstat (limited to 'arm7/lib/src')
-rw-r--r-- | arm7/lib/src/OS_reset.c | 3 | ||||
-rw-r--r-- | arm7/lib/src/SND_alarm.c | 83 | ||||
-rw-r--r-- | arm7/lib/src/SND_bank.c | 194 | ||||
-rw-r--r-- | arm7/lib/src/SND_capture.c | 25 | ||||
-rw-r--r-- | arm7/lib/src/SND_channel.c | 192 | ||||
-rw-r--r-- | arm7/lib/src/SND_command.c | 329 | ||||
-rw-r--r-- | arm7/lib/src/SND_exChannel.c | 685 | ||||
-rw-r--r-- | arm7/lib/src/SND_global.c | 63 | ||||
-rw-r--r-- | arm7/lib/src/SND_main.c | 133 | ||||
-rw-r--r-- | arm7/lib/src/SND_seq.c | 1324 | ||||
-rw-r--r-- | arm7/lib/src/SND_util.c | 109 | ||||
-rw-r--r-- | arm7/lib/src/SND_work.c | 41 |
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 = ®_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 *)®_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; +} |