summaryrefslogtreecommitdiff
path: root/arm7/lib/src/SND_seq.c
diff options
context:
space:
mode:
Diffstat (limited to 'arm7/lib/src/SND_seq.c')
-rw-r--r--arm7/lib/src/SND_seq.c1324
1 files changed, 1324 insertions, 0 deletions
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;
+ }
+}