Files
ac-decomp/src/static/jaudio_NES/internal/track.c
T
2026-07-14 11:49:34 -04:00

2258 lines
94 KiB
C

#include "jaudio_NES/track.h"
#include "jaudio_NES/audiotable.h"
#include "jaudio_NES/channel.h"
#include "jaudio_NES/effect.h"
#include "jaudio_NES/memory.h"
#include "jaudio_NES/sub_sys.h"
#include "jaudio_NES/system.h"
#include "dolphin/os.h"
#define COMMON_SCRIPT_END -1
/**
* SCOM_TABLE is a table for each sequence instruction
* that contains both how many arguments an instruction takes, as well
* as the type of each argument
*
* SCOM_TABLE is bitpacked as follows:
* abcUUUnn
*
* n - number of arguments that the sequence instruction takes
*
* a - bitFlag for the type of arg0 if it exists
* b - bitFlag for the type of arg1 if it exists
* c - bitFlag for the type of arg2 if it exists
*
* bitFlag on - argument is s16
* bitFlag off - argument is u8
*
* U - Unused
*/
// CMD_ARGS_(NUMBER_OF_ARGS)
#define CMD_ARGS_0() 0
#define CMD_ARGS_1(arg0Type) (((sizeof(arg0Type) - 1) << 7) | 1)
#define CMD_ARGS_2(arg0Type, arg1Type) (((sizeof(arg0Type) - 1) << 7) | ((sizeof(arg1Type) - 1) << 6) | 2)
#define CMD_ARGS_3(arg0Type, arg1Type, arg2Type) \
(((sizeof(arg0Type) - 1) << 7) | ((sizeof(arg1Type) - 1) << 6) | ((sizeof(arg2Type) - 1) << 5) | 3)
#define SCOM_TYPE_U8 0
#define SCOM_TYPE_S16 1
static u8 SCOM_TABLE[] = {
CMD_ARGS_1(s16), // 0xA0 (channel:)
CMD_ARGS_0(), // 0xA1 (channel:)
CMD_ARGS_1(s16), // 0xA2 (channel:)
CMD_ARGS_0(), // 0xA3 (channel:)
CMD_ARGS_1(u8), // 0xA4 (channel:)
CMD_ARGS_0(), // 0xA5 (channel:)
CMD_ARGS_2(u8, s16), // 0xA6 (channel:)
CMD_ARGS_1(u8), // 0xA7 (channel:)
CMD_ARGS_0(), // 0xA8 (channel: random range large) THIS ONE
CMD_ARGS_0(), // 0xA9 ()
CMD_ARGS_0(), // 0xAA ()
CMD_ARGS_0(), // 0xAB ()
CMD_ARGS_0(), // 0xAC ()
CMD_ARGS_0(), // 0xAD ()
CMD_ARGS_0(), // 0xAE ()
CMD_ARGS_0(), // 0xAF ()
CMD_ARGS_1(s16), // 0xB0 (channel: set filter)
CMD_ARGS_0(), // 0xB1 (channel: clear filter)
CMD_ARGS_1(s16), // 0xB2 (channel: dynread sequence large)
CMD_ARGS_1(u8), // 0xB3 (channel: load filter)
CMD_ARGS_0(), // 0xB4 (channel: set dyntable large)
CMD_ARGS_0(), // 0xB5 (channel: read dyntable large)
CMD_ARGS_0(), // 0xB6 (channel: read dyntable)
CMD_ARGS_1(s16), // 0xB7 (channel: random large)
CMD_ARGS_1(u8), // 0xB8 (channel: random)
CMD_ARGS_1(u8), // 0xB9 (channel: set velocity random variance)
CMD_ARGS_1(u8), // 0xBA (channel: set gatetime random variance)
CMD_ARGS_2(u8, s16), // 0xBB (channel:)
CMD_ARGS_1(s16), // 0xBC (channel: add large)
CMD_ARGS_1(s16), // 0xBD (channel:)
CMD_ARGS_1(u8), // 0xBE (channel:)
CMD_ARGS_0(), // 0xBF ()
CMD_ARGS_0(), // 0xC0 ()
CMD_ARGS_1(u8), // 0xC1 (channel: set instrument)
CMD_ARGS_1(s16), // 0xC2 (channel: set dyntable)
CMD_ARGS_0(), // 0xC3 (channel: large notes off)
CMD_ARGS_0(), // 0xC4 (channel: large notes on)
CMD_ARGS_0(), // 0xC5 (channel: dyn set dyntable)
CMD_ARGS_1(u8), // 0xC6 (channel: set soundFont)
CMD_ARGS_2(u8, s16), // 0xC7 (channel: write into sequence script)
CMD_ARGS_1(u8), // 0xC8 (channel: subtract -> set value)
CMD_ARGS_1(u8), // 0xC9 (channel: `bit and` -> set value)
CMD_ARGS_1(u8), // 0xCA (channel: set mute behavior)
CMD_ARGS_1(s16), // 0xCB (channel: read sequence -> set value)
CMD_ARGS_1(u8), // 0xCC (channel: set value)
CMD_ARGS_1(u8), // 0xCD (channel: disable channel)
CMD_ARGS_1(s16), // 0xCE (channel:)
CMD_ARGS_1(s16), // 0xCF (channel: write large into sequence script)
CMD_ARGS_1(u8), // 0xD0 (channel: stereo headset effects)
CMD_ARGS_1(u8), // 0xD1 (channel: set note allocation policy)
CMD_ARGS_1(u8), // 0xD2 (channel: set sustain)
CMD_ARGS_1(u8), // 0xD3 (channel: large bend pitch)
CMD_ARGS_1(u8), // 0xD4 (channel: set reverb)
CMD_ARGS_1(u8), // 0xD5 ()
CMD_ARGS_1(u8), // 0xD6 ()
CMD_ARGS_1(u8), // 0xD7 (channel: set vibrato rate)
CMD_ARGS_1(u8), // 0xD8 (channel: set vibrato depth)
CMD_ARGS_1(u8), // 0xD9 (channel: set decay index)
CMD_ARGS_1(s16), // 0xDA (channel: set envelope)
CMD_ARGS_1(u8), // 0xDB (channel: transpose)
CMD_ARGS_1(u8), // 0xDC (channel: set pan mix)
CMD_ARGS_1(u8), // 0xDD (channel: set pan)
CMD_ARGS_1(s16), // 0xDE (channel: set freqscale)
CMD_ARGS_1(u8), // 0xDF (channel: set volume)
CMD_ARGS_1(u8), // 0xE0 (channel: set volume scale)
CMD_ARGS_3(u8, u8, u8), // 0xE1 (channel: set vibratorate linear)
CMD_ARGS_3(u8, u8, u8), // 0xE2 (channel: set vibrato depth linear)
CMD_ARGS_1(u8), // 0xE3 (channel: set vibrato delay)
CMD_ARGS_0(), // 0xE4 (channel: dyncall)
CMD_ARGS_1(u8), // 0xE5 (channel: set reverb index)
CMD_ARGS_1(u8), // 0xE6 (channel: set book offset)
CMD_ARGS_1(s16), // 0xE7 (channel:)
CMD_ARGS_3(u8, u8, u8), // 0xE8 (channel:)
CMD_ARGS_1(u8), // 0xE9 (channel: set note priority)
CMD_ARGS_0(), // 0xEA (channel: stop script)
CMD_ARGS_2(u8, u8), // 0xEB (channel: set soundFont and instrument)
CMD_ARGS_0(), // 0xEC (channel: reset vibrato)
CMD_ARGS_1(u8), // 0xED (channel: set hilo gain)
CMD_ARGS_1(u8), // 0xEE (channel: small bend pitch)
CMD_ARGS_2(s16, u8), // 0xEF ()
CMD_ARGS_0(), // 0xF0 (channel: unreserve notes)
CMD_ARGS_1(u8), // 0xF1 (channel: reserve notes)
// Control flow instructions (>= 0xF2) can only have 0 or 1 args
CMD_ARGS_1(u8), // 0xF2 (branch relative if less than zero)
CMD_ARGS_1(u8), // 0xF3 (branch relative if equal to zero)
CMD_ARGS_1(u8), // 0xF4 (jump relative)
CMD_ARGS_1(s16), // 0xF5 (branch if greater than or equal to zero)
CMD_ARGS_0(), // 0xF6 (break)
CMD_ARGS_0(), // 0xF7 (loop end)
CMD_ARGS_1(u8), // 0xF8 (loop)
CMD_ARGS_1(s16), // 0xF9 (branch if less than zero)
CMD_ARGS_1(s16), // 0xFA (branch if equal to zero)
CMD_ARGS_1(s16), // 0xFB (jump)
CMD_ARGS_1(s16), // 0xFC (call and jump to a function)
CMD_ARGS_0(), // 0xFD (delay n frames)
CMD_ARGS_0(), // 0xFE (delay 1 frame)
CMD_ARGS_0(), // 0xFF (end script)
};
#define SCOM_GET_ARG_NUM(cmd) (SCOM_TABLE[(cmd)] & 3)
#define SCOM_GET_TYPE(cmd, argno) (SCOM_TABLE[(cmd)] & (1 << (7 - (argno & 3))))
static u8 Nas_ReadByteData(macro* macro);
static s16 Nas_ReadWordData(macro* macro);
static u16 Nas_ReadLengthData(macro* macro);
static void __Stop_Note(note* n);
static s32 __SetNote(note* n, s32 cmd);
static s32 __Command_Seq(note* n);
static s32 __SetVoice(note* n, s32 cmd);
static s32 __SetChannel(note* n, s32 cmd);
static u8 Nas_ProgramChanger(sub* subtrack, u8 prog_id, voicetable** inst_pp, env* adsr_env);
static u16 Convert_Com(macro* macro, u8 cmd) {
u16 val = 0;
if ((u8)SCOM_GET_ARG_NUM(cmd - 0xA0) == 1) {
if (SCOM_GET_TYPE(cmd - 0xA0, 0) == SCOM_TYPE_U8) {
val = Nas_ReadByteData(macro);
} else {
val = Nas_ReadWordData(macro);
}
}
return val;
}
static s32 Common_Com(group* group, macro* macro, s32 cmd, s32 arg) {
u8 iters;
u16 abs;
s8 rel;
switch (cmd) {
case COMMON_CMD_STOP_SCRIPT:
if (macro->depth == 0) {
return COMMON_SCRIPT_END;
}
macro->pc = macro->stack[--macro->depth];
break;
case COMMON_CMD_DELAY_N_FRAMES:
return Nas_ReadLengthData(macro);
case COMMON_CMD_DELAY_1_FRAME:
return 1;
case COMMON_CMD_CALL:
macro->stack[macro->depth++] = macro->pc;
macro->pc = group->seq_data + (arg & 0xFFFF);
break;
case COMMON_CMD_LOOP:
macro->remaining_loop_iters[macro->depth] = arg & 0xFF;
macro->stack[macro->depth++] = macro->pc;
break;
case COMMON_CMD_LOOP_END:
iters = macro->remaining_loop_iters[macro->depth - 1];
iters--;
macro->remaining_loop_iters[macro->depth - 1] = iters;
if (iters != 0) {
macro->pc = macro->stack[macro->depth - 1];
} else {
macro->depth--;
}
break;
case COMMON_CMD_BREAK:
macro->depth--;
break;
case COMMON_CMD_BRANCH_ABS_GREQ_ZERO:
case COMMON_CMD_BRANCH_ABS_NOT_EQ_ZERO:
case COMMON_CMD_BRANCH_ABS_EQ_ZERO:
case COMMON_CMD_BRANCH_ABS:
abs = arg & 0xFFFF;
if (cmd == COMMON_CMD_BRANCH_ABS_EQ_ZERO && macro->value != 0) {
break;
} else if (cmd == COMMON_CMD_BRANCH_ABS_NOT_EQ_ZERO && macro->value >= 0) {
break;
} else if (cmd == COMMON_CMD_BRANCH_ABS_GREQ_ZERO && macro->value < 0) {
break;
} else {
macro->pc = group->seq_data + abs;
}
break;
case COMMON_CMD_BRANCH_REL_NOT_EQ_ZERO:
case COMMON_CMD_BRANCH_REL_EQ_ZERO:
case COMMON_CMD_BRANCH_REL:
rel = (s8)(arg & 0xFF);
if (cmd == COMMON_CMD_BRANCH_REL_EQ_ZERO && macro->value != 0) {
break;
} else if (cmd == COMMON_CMD_BRANCH_REL_NOT_EQ_ZERO && macro->value >= 0) {
break;
} else {
macro->pc += rel;
}
break;
}
return 0;
}
static void Nas_InitSubTrack(sub* subtrack) {
s32 i;
if (subtrack != &AG.null_sub_track) {
subtrack->transposition = 0;
subtrack->enabled = FALSE;
subtrack->finished = FALSE;
subtrack->stop_script = FALSE;
// subtrack->muted = FALSE;
subtrack->has_instrument = FALSE;
subtrack->stereo_effects = FALSE;
subtrack->large_notes = FALSE;
subtrack->book_ofs = 0;
*(u8*)&subtrack->stereo_phase = 0;
subtrack->changes.as_byte = 0xFF;
subtrack->macro_player.depth = 0;
subtrack->volume = 1.0f;
subtrack->volume_scale = 1.0f;
subtrack->new_pan = 64;
subtrack->frequency_scale = 1.0f;
subtrack->pan_channel_weight = 128;
subtrack->surround_effect_idx = 0xFF;
subtrack->velocity_random_variance = 0;
subtrack->gate_time_random_variance = 0;
subtrack->channel = nullptr;
subtrack->reverb_idx = 0;
subtrack->target_reverb_vol = 0;
subtrack->gain = 0;
subtrack->note_priority = 3;
subtrack->priority2 = 1;
subtrack->delay = 0;
subtrack->adsr_env.envelope = DEFAULT_ENV;
subtrack->adsr_env.decay_idx = 240;
subtrack->adsr_env.sustain = 0;
subtrack->vibrato_params.rate_target = 2048;
subtrack->vibrato_params.rate_start = 2048;
subtrack->vibrato_params.depth_target = 0;
subtrack->vibrato_params.depth_start = 0;
subtrack->vibrato_params.rate_change_delay = 0;
subtrack->vibrato_params.depth_change_delay = 0;
subtrack->vibrato_params.delay = 0;
subtrack->filter = nullptr;
subtrack->comb_filter_gain = 0;
subtrack->comb_filter_size = 0;
for (i = 0; i < ARRAY_COUNT(subtrack->port); i++) {
subtrack->port[i] = -1;
}
subtrack->unused = FALSE;
Nas_InitChNode(&subtrack->channel_node);
subtrack->sample_start_pos = 0;
subtrack->_E0 = 0;
subtrack->sfx_state = nullptr;
}
}
static s32 Nas_EntryNoteTrack(sub* subtrack, int note_idx) {
note* entry_note;
if (subtrack->note_layers[note_idx] == nullptr) {
entry_note = (note*)Nas_GetList(&AG.note_link);
subtrack->note_layers[note_idx] = entry_note;
if (entry_note == nullptr) {
subtrack->note_layers[note_idx] = nullptr;
return -1;
}
} else {
Nas_Release_Channel(subtrack->note_layers[note_idx]);
}
entry_note = subtrack->note_layers[note_idx];
entry_note->sub_track = subtrack;
entry_note->adsr_env = subtrack->adsr_env;
entry_note->adsr_env.decay_idx = 0;
entry_note->target_reverb_volume = subtrack->target_reverb_vol;
entry_note->enabled = TRUE;
entry_note->finished = FALSE;
entry_note->muted = FALSE;
entry_note->continuous = FALSE;
entry_note->channel_attached = FALSE;
entry_note->ignore_drum_pan = FALSE;
entry_note->continuous_channel_released = FALSE;
entry_note->note_properties_need_init = FALSE;
*(u8*)&entry_note->stereo_phase = 0;
entry_note->portamento_sweep.mode = 0;
entry_note->macro_player.depth = 0;
entry_note->gate_time = 128;
entry_note->frequency_scale = 1.0f;
entry_note->bend = 1.0f;
entry_note->velocity_square2 = 0.0f;
entry_note->pan = 64;
entry_note->surround_effect_idx = 128;
entry_note->transposition = 0;
entry_note->delay = 0;
entry_note->gate_delay = 0;
entry_note->delay2 = 0;
entry_note->channel = nullptr;
entry_note->instrument = nullptr;
entry_note->inst_or_wave = -1;
entry_note->_0A.asU16 = 0xFFFF;
entry_note->vibrato_params.rate_target = 2048;
entry_note->vibrato_params.rate_start = 2048;
entry_note->vibrato_params.depth_target = 0;
entry_note->vibrato_params.depth_start = 0;
entry_note->vibrato_params.rate_change_delay = 0;
entry_note->vibrato_params.depth_change_delay = 0;
entry_note->vibrato_params.delay = 0;
return 0;
}
extern void Nas_ReleaseNoteTrack(note* note) {
if (note != nullptr) {
if (note->sub_track != &AG.null_sub_track && note->sub_track->group->flags.finished == TRUE) {
Nas_Release_Channel_Force(note);
} else {
Nas_Release_Channel(note);
}
note->enabled = FALSE;
note->finished = TRUE;
}
}
static void Nas_CloseNoteTrack(sub* subtrack, s32 note_idx) {
note* note = subtrack->note_layers[note_idx];
if (note != nullptr) {
Nas_AddList(&AG.note_link, &note->link);
Nas_ReleaseNoteTrack(note);
subtrack->note_layers[note_idx] = nullptr;
}
}
extern void Nas_ReleaseSubTrack(sub* subtrack) {
s32 i;
subtrack->finished = TRUE;
for (i = 0; i < ARRAY_COUNT(subtrack->note_layers); i++) {
Nas_CloseNoteTrack(subtrack, i);
}
Nas_DeAllocAllVoices(&subtrack->channel_node);
subtrack->enabled = FALSE;
}
static void Nas_AllocSub(group* grp, u16 subtrack_enable_flags) {
s32 i;
for (i = 0; i < AUDIO_SUBTRACK_NUM; i++) {
if ((subtrack_enable_flags & 1) != 0) {
sub* subtrack = grp->subtracks[i];
subtrack->bank_id = grp->bank_id;
subtrack->mute_flags = grp->mute_flags;
subtrack->note_alloc_policy = grp->note_alloc_policy;
}
subtrack_enable_flags = subtrack_enable_flags >> 1;
}
}
static void Nas_DeAllocSub(group* grp, u16 subtrack_dealloc_flags) {
s32 i;
for (i = 0; i < AUDIO_SUBTRACK_NUM; i++) {
sub* subtrack = grp->subtracks[i];
if (subtrack != &AG.null_sub_track) {
Nas_ReleaseSubTrack(subtrack);
}
}
}
static void Nas_OpenSub(group* grp, u8 subtrack_idx, u8* script) {
sub* subtrack = grp->subtracks[subtrack_idx];
s32 i;
subtrack->enabled = TRUE;
subtrack->finished = FALSE;
subtrack->macro_player.depth = 0;
subtrack->macro_player.pc = script;
subtrack->delay = 0;
for (i = 0; i < ARRAY_COUNT(subtrack->note_layers); i++) {
if (subtrack->note_layers[i] != nullptr) {
Nas_CloseNoteTrack(subtrack, i);
}
}
}
extern void Nas_ReleaseGroup_Force(group* grp) {
grp->flags.finished = TRUE;
Nas_ReleaseGroup(grp);
}
extern void Nas_ReleaseGroup(group* grp) {
Nas_DeAllocSub(grp, AUDIO_GROUP_ALL_SUBTRACKS);
Nas_DeAllocAllVoices(&grp->channel_node);
if (grp->flags.enabled) {
grp->flags.enabled = FALSE;
grp->flags.finished = TRUE;
if (Nas_CheckIDseq(grp->seq_id)) {
Nas_WriteIDseq(grp->seq_id, LOAD_STATUS_DISCARDABLE);
}
if (Nas_CheckIDbank(grp->bank_id)) {
Nas_WriteIDbank(grp->bank_id, LOAD_STATUS_MAYBE_DISCARDABLE);
}
if (AG.bank_heap.auto_heap.entries[0].id == grp->bank_id) {
AG.bank_heap.auto_heap.use_entry_idx = 1;
} else if (AG.bank_heap.auto_heap.entries[1].id == grp->bank_id) {
AG.bank_heap.auto_heap.use_entry_idx = 0;
}
}
}
extern void Nas_AddList(link* root, link* list) {
if (list->prev != nullptr) {
static BOOL first = TRUE;
if (first) {
OSReport("GLOBAL USE %x\n", &AG.channel_node.useList);
OSReport("GLOBAL FREE %x\n", &AG.channel_node.freeList);
OSReport("GLOBAL RELEASE %x\n", &AG.channel_node.releaseList);
OSReport("GLOBAL RELWAIT %x\n", &AG.channel_node.relwaitList);
first = FALSE;
}
OSReport("List %x\n", list);
OSReport("Root %x\n", root);
} else {
root->prev->next = list;
list->prev = root->prev;
list->next = root;
root->prev = list;
root->numAfter++;
list->pNode = root->pNode;
}
}
extern void* Nas_GetList(link* root) {
link* list = root->prev;
if (list == root) {
return nullptr;
}
list->prev->next = root;
root->prev = list->prev;
list->prev = nullptr;
root->numAfter--;
return list->pData;
}
static void Nas_InitNoteList(void) {
s32 i;
AG.note_link.prev = &AG.note_link;
AG.note_link.next = &AG.note_link;
AG.note_link.numAfter = 0;
AG.note_link.pNode = nullptr;
for (i = 0; i < AUDIO_NOTE_MAX; i++) {
AG.notes[i].link.pData = &AG.notes[i];
AG.notes[i].link.prev = nullptr;
Nas_AddList(&AG.note_link, &AG.notes[i].link);
}
}
static u8 Nas_ReadByteData(macro* m) {
return *m->pc++;
}
static s16 Nas_ReadWordData(macro* m) {
s16 data = (*m->pc++) << 8;
data = data | (*m->pc++);
return data;
}
#define Nas_LENGTH_IS_U16(d) ((d) & 0x80)
static u16 Nas_ReadLengthData(macro* m) {
u16 data = (*m->pc++);
if (Nas_LENGTH_IS_U16(data)) {
data = (data << 8) & 0x7F00;
data = (*m->pc++) | data;
}
return data;
}
#undef Nas_LENGTH_IS_U16
static void Nas_NoteSeq(note* n) {
if (n->enabled) {
if (n->delay > 1) {
n->delay--;
if (n->muted == FALSE && n->delay <= n->gate_delay) {
Nas_Release_Channel(n);
n->muted = TRUE;
}
} else {
s32 cmd;
__Stop_Note(n);
do {
cmd = __Command_Seq(n);
if (cmd == COMMON_SCRIPT_END) {
return;
}
cmd = __SetNote(n, cmd);
} while (cmd == COMMON_SCRIPT_END && n->delay == 0);
if (cmd != COMMON_SCRIPT_END) {
cmd = __SetVoice(n, cmd);
}
if (cmd != COMMON_SCRIPT_END) {
__SetChannel(n, cmd);
}
if (n->muted == TRUE) {
if (n->channel != nullptr || n->continuous) {
Nas_Release_Channel(n);
}
}
}
}
}
static void __Stop_Note(note* n) {
s32 mode;
if (n->continuous == FALSE) {
Nas_Release_Channel(n);
} else if (n->channel != nullptr && n->channel->playback_ch.wanted_parent_note == n) {
Nas_Release_Channel(n);
}
switch (AUDIO_SWEEP_MODE(n->portamento_sweep)) {
case PORTAMENTO_MODE_1:
case PORTAMENTO_MODE_2:
case PORTAMENTO_MODE_6:
n->portamento_sweep.mode = 0;
break;
}
n->note_properties_need_init = TRUE;
}
static s32 __SetChannel(note* n, s32 same_sample) {
if (n->continuous == TRUE && n->continuous_channel_released == TRUE) {
return 0;
}
if (n->continuous != TRUE || n->channel == nullptr || n->channel_attached == FALSE || same_sample != TRUE ||
n->channel->playback_ch.current_parent_note != n) {
if (same_sample == FALSE) {
Nas_Release_Channel(n);
}
n->channel = Nas_AllocationOnRequest(n);
if (n->channel != nullptr) {
if (n->channel->playback_ch.current_parent_note == n) {
Nas_ChannelModInit(n->channel);
}
}
}
if (n->channel != nullptr && n->channel->playback_ch.current_parent_note == n) {
Nas_SweepInit(n->channel);
}
return 0;
}
static s32 __Command_Seq(note* n) {
macro* m = &n->macro_player;
sub* subtrack = n->sub_track;
group* grp = subtrack->group;
sweep* port_sweep;
u8 cmd;
u8 cmdArgU8;
u32 tmp;
u16 cmdArgU16;
u16 velocity;
while (TRUE) {
cmd = Nas_ReadByteData(m);
if (cmd <= NOTE_CMD_MUTE) {
return cmd;
}
if (cmd >= COMMON_CMD_BRANCH_REL_NOT_EQ_ZERO) {
cmdArgU16 = Convert_Com(m, cmd);
if (Common_Com(grp, m, cmd, cmdArgU16) == 0) {
continue; // keep processing
}
/* stop processing script */
Nas_ReleaseNoteTrack(n);
return COMMON_SCRIPT_END;
}
switch (cmd) {
case NOTE_CMD_SET_VELOCITY_SQ:
case NOTE_CMD_SET_PAN:
cmdArgU8 = *m->pc++;
if (cmd == NOTE_CMD_SET_VELOCITY_SQ) {
n->velocity_square = (f32)(cmdArgU8 * cmdArgU8) / (f32)(127 * 127);
} else {
n->pan = cmdArgU8;
}
break;
case NOTE_CMD_SET_TRANSPOSITION:
case NOTE_CMD_SET_GATE_TIME:
cmdArgU8 = *m->pc++;
if (cmd == NOTE_CMD_SET_GATE_TIME) {
n->gate_time = cmdArgU8;
} else {
n->transposition = cmdArgU8;
}
break;
case NOTE_CMD_CONTINUOUS_ON:
case NOTE_CMD_CONTINUOUS_OFF:
n->continuous = cmd == NOTE_CMD_CONTINUOUS_ON;
n->continuous_channel_released = FALSE;
Nas_Release_Channel(n);
break;
case NOTE_CMD_SET_SHORT_NOTE_DEFAULT_DELAY:
n->short_note_default_delay = Nas_ReadLengthData(m);
break;
case NOTE_CMD_SET_INSTRUMENT:
cmdArgU8 = Nas_ReadByteData(m);
if (cmdArgU8 >= 0x7E) {
if (cmdArgU8 == 0x7E) {
// effect/sfx
n->inst_or_wave = VOICE_TYPE_SOUND_EFF;
} else if (cmdArgU8 == 0x7F) {
// percussion/drum
n->inst_or_wave = VOICE_TYPE_PERCUSSION;
} else {
// synth wave
n->inst_or_wave = cmdArgU8;
n->instrument = nullptr;
}
if (cmdArgU8 == 0xFF) {
n->adsr_env.decay_idx = 0;
}
} else {
// voice/instrument
cmd = Nas_ProgramChanger(subtrack, cmdArgU8, &n->instrument, &n->adsr_env);
n->inst_or_wave = cmd;
if (cmd == 0) {
n->inst_or_wave = 0xFF;
}
}
break;
case NOTE_CMD_ENABLE_SWEEP:
n->portamento_sweep.mode = Nas_ReadByteData(m);
cmdArgU8 = Nas_ReadByteData(m);
cmdArgU8 = cmdArgU8 + subtrack->transposition;
cmdArgU8 = cmdArgU8 + n->transposition;
cmdArgU8 = cmdArgU8 + grp->transposition;
switch (AUDIO_SWEEP_MODE(n->portamento_sweep)) {
case PORTAMENTO_MODE_6:
case PORTAMENTO_MODE_7:
break;
default:
if ((u8)cmdArgU8 > 127) {
cmdArgU8 = (u8)0;
}
break;
}
n->portamento_target_note = (u8)cmdArgU8;
if (AUDIO_SWEEP_IS_SPECIAL(n->portamento_sweep)) {
n->portamento_time = *m->pc++;
} else {
n->portamento_time = Nas_ReadLengthData(m);
}
break;
case NOTE_CMD_DISABLE_SWEEP:
n->portamento_sweep.mode = PORTAMENTO_MODE_OFF;
break;
case NOTE_CMD_SET_ADSR_ENVELOPE_DECAY_IDX:
n->adsr_env.envelope = (envdat*)(grp->seq_data + (Nas_ReadWordData(m) & 0xFFFF));
// fallthrough 0xCB -> 0xCF
case NOTE_CMD_SET_ADSR_DECAY_IDX:
n->adsr_env.decay_idx = Nas_ReadByteData(m);
break;
case NOTE_CMD_IGNORE_DRUM_PAN:
n->ignore_drum_pan = TRUE;
break;
case NOTE_CMD_SET_STEREO_PHASE:
*(u8*)&n->stereo_phase = Nas_ReadByteData(m);
break;
case NOTE_CMD_SET_BEND:
cmdArgU8 = 128 + Nas_ReadByteData(m);
n->bend = PCENTTABLE2[cmdArgU8];
break;
case NOTE_CMD_DISABLE_FLAGS:
cmdArgU16 = Nas_ReadWordData(m);
n->_0A.asU16 &= cmdArgU16 ^ 0xFFFF;
break;
case NOTE_CMD_SET_SURROUND_EFFECT_IDX:
n->surround_effect_idx = Nas_ReadByteData(m);
break;
default:
switch (cmd & 0xF0) {
case NOTE_CMD_SHORT_NOTE_VELOCITY_SQ_MASK:
velocity = grp->short_note_velocity_tbl[cmd & 0xF];
n->velocity_square = (f32)(velocity * velocity) / (f32)(127 * 127);
break;
case NOTE_CMD_SET_SHORT_NOTE_GATE_TIME_MASK:
n->gate_time = grp->short_note_gate_time_tbl[cmd & 0xF];
break;
}
}
}
return cmd;
}
static s32 __SetVoice(note* n, s32 arg) {
u8 semitone;
s32 same_sample;
s32 inst_or_wave;
sweep* port_sweep;
f32 freq_scale;
f32 freq_scale2;
wtstr* tuned_sample;
voicetable* instrument;
perctable* percussion;
percvoicetable* effect;
sub* subtrack;
group* grp;
u16 effect_id;
s32 velocity;
s32 semitone2;
s32 speed;
s32 speed2;
f32 tuning;
f32 t0;
f32 t1;
f32 freq0;
f32 freq1;
f32 time;
semitone = arg;
same_sample = TRUE;
inst_or_wave = n->inst_or_wave;
subtrack = n->sub_track;
grp = subtrack->group;
if (inst_or_wave == 0xFF) {
if (subtrack->has_instrument == FALSE) {
return COMMON_SCRIPT_END;
}
inst_or_wave = subtrack->inst_or_wave;
}
switch (inst_or_wave) {
case VOICE_TYPE_PERCUSSION:
semitone = semitone + n->transposition;
if (n->_0A.flags.add_subtrack_transposition) {
semitone = semitone + subtrack->transposition;
}
n->semitone = semitone;
percussion = PercToPp(subtrack->bank_id, semitone);
/* Mute note and exit if percussion doesn't exist or the subtrack is muted */
if (percussion == nullptr || subtrack->muted) {
n->muted = TRUE;
n->delay2 = n->delay;
return COMMON_SCRIPT_END;
}
n->adsr_env.envelope = percussion->envelope;
n->adsr_env.decay_idx = percussion->adsr_decay_idx;
if (n->ignore_drum_pan == FALSE) {
n->pan = percussion->pan;
}
n->tuned_sample = &percussion->tuned_sample;
n->frequency_scale = n->tuned_sample->tuning;
break;
case VOICE_TYPE_SOUND_EFF:
effect_id = (semitone + (n->transposition << 6));
n->semitone = semitone;
effect = VpercToVep(subtrack->bank_id, effect_id);
/* Mute note and exit if sfx doesn't exist or the subtrack is muted */
if (effect == nullptr || subtrack->muted) {
n->muted = TRUE;
n->delay2 = n->delay + 1;
return COMMON_SCRIPT_END;
}
n->tuned_sample = &effect->tuned_sample;
n->frequency_scale = n->tuned_sample->tuning;
break;
default: // voices/instruments
semitone = semitone + grp->transposition + n->transposition;
if (n->_0A.flags.add_subtrack_transposition) {
semitone = semitone + subtrack->transposition;
}
n->semitone = semitone;
if (semitone > 127 || subtrack->muted) {
n->muted = TRUE;
if (subtrack->muted) {
subtrack->note_semitone = n->semitone;
}
return COMMON_SCRIPT_END;
}
if (n->inst_or_wave == 0xFF) {
instrument = subtrack->voicetable;
} else {
instrument = n->instrument;
}
if (n->portamento_sweep.mode != PORTAMENTO_MODE_OFF) {
port_sweep = &n->portamento_sweep;
switch (AUDIO_SWEEP_MODE(*port_sweep)) {
case PORTAMENTO_MODE_6:
case PORTAMENTO_MODE_7:
velocity = semitone;
break;
default:
velocity = semitone > n->portamento_target_note ? semitone : n->portamento_target_note;
break;
}
// velocity =
if (instrument != nullptr) {
tuned_sample = NoteToVoice(instrument, velocity);
same_sample = n->tuned_sample == tuned_sample;
n->tuned_sample = tuned_sample;
tuning = n->tuned_sample->tuning;
} else {
n->tuned_sample = nullptr;
tuning = 1.0f;
if (inst_or_wave >= 0xC0) {
n->tuned_sample = &AG.synth_delay[inst_or_wave - 0xC0].tuned_sample;
}
}
t0 = tuning * PITCHTABLE[semitone];
semitone2 = (s8)n->portamento_target_note;
switch (AUDIO_SWEEP_MODE(*port_sweep)) {
case PORTAMENTO_MODE_6:
case PORTAMENTO_MODE_7:
semitone2 += semitone;
break;
}
if (semitone2 < 0) {
semitone2 = 0;
}
if (semitone2 > 127) {
semitone2 = 127;
}
t1 = tuning * PITCHTABLE[semitone2];
switch (AUDIO_SWEEP_MODE(*port_sweep)) {
case PORTAMENTO_MODE_1:
case PORTAMENTO_MODE_3:
case PORTAMENTO_MODE_5:
case PORTAMENTO_MODE_6:
case PORTAMENTO_MODE_7:
freq_scale2 = t0;
freq_scale = t1;
break;
case PORTAMENTO_MODE_2:
case PORTAMENTO_MODE_4:
freq_scale = t0;
freq_scale2 = t1;
break;
default:
freq_scale = t0;
freq_scale2 = t0;
break;
}
port_sweep->extent = (freq_scale2 / freq_scale) - 1.0f;
if (AUDIO_SWEEP_IS_SPECIAL(*port_sweep)) {
speed = (grp->tempo * 0x8000) / AG.max_tempo;
if (n->delay != 0) {
speed = (speed * 0x100) / (n->delay * n->portamento_time);
}
} else {
speed = (2 * 0x10000) / (n->portamento_time * AG.audio_params.updates_per_frame);
}
if (speed >= 0x7FFF) {
speed = 0x7FFF;
} else if (speed <= 0) {
speed = 1;
}
port_sweep->speed = speed;
port_sweep->current = 0;
n->frequency_scale = freq_scale;
if (AUDIO_SWEEP_MODE(*port_sweep) == PORTAMENTO_MODE_5) {
n->portamento_target_note = semitone;
}
} else {
if (instrument != nullptr) {
tuned_sample = NoteToVoice(instrument, semitone);
same_sample = n->tuned_sample == tuned_sample;
n->tuned_sample = tuned_sample;
n->frequency_scale = PITCHTABLE[semitone] * n->tuned_sample->tuning;
} else {
n->tuned_sample = nullptr;
n->frequency_scale = PITCHTABLE[semitone];
if (inst_or_wave >= 0xC0) {
n->tuned_sample = &AG.synth_delay[inst_or_wave - 0xC0].tuned_sample;
}
}
}
break;
}
n->delay2 = n->delay;
n->frequency_scale *= n->bend;
if (n->delay == 0) {
if (n->tuned_sample != nullptr) {
time = n->tuned_sample->wavetable->loop->loop_end;
} else {
time = 0.0f;
}
time *= grp->tempo;
time *= AG.sample_to_update_delay_scale;
time /= n->frequency_scale;
if (time > (f32)0x7FFE) {
time = (f32)0x7FFE;
}
n->delay = (u16)(s32)time + 1;
n->gate_delay = 0;
if (n->portamento_sweep.mode != PORTAMENTO_MODE_OFF) {
if (AUDIO_SWEEP_IS_SPECIAL(*port_sweep)) {
speed2 = (grp->tempo * 0x8000) / AG.max_tempo;
speed2 = (speed2 * 0x100) / (n->delay * n->portamento_time);
if (speed2 >= 0x7FFF) {
speed2 = 0x7FFF;
} else if (speed2 <= 0) {
speed2 = 1;
}
port_sweep->speed = speed2;
}
}
}
subtrack->note_semitone = n->semitone;
return same_sample;
}
static s32 __SetNote(note* n, s32 cmd) {
macro* m = &n->macro_player;
u16 delay;
s32 velocity;
sub* subtrack = n->sub_track;
group* grp = subtrack->group;
s32 int_delta;
f32 float_delta;
if (cmd == 0xC0) {
n->delay = Nas_ReadLengthData(m);
n->muted = TRUE;
n->continuous_channel_released = FALSE;
return COMMON_SCRIPT_END;
}
n->muted = FALSE;
if (subtrack->large_notes == TRUE) {
switch (cmd & 0xC0) {
case 0x00:
delay = Nas_ReadLengthData(m);
velocity = *m->pc++;
n->gate_time = *m->pc++;
n->last_delay = delay;
break;
case 0x40:
delay = Nas_ReadLengthData(m);
velocity = *m->pc++;
n->gate_time = 0;
n->last_delay = delay;
break;
case 0x80:
delay = n->last_delay;
velocity = *m->pc++;
n->gate_time = *m->pc++;
break;
}
if (velocity > 127 || velocity < 0) {
velocity = 127;
}
n->velocity_square = ((f32)velocity * (f32)velocity) / ((f32)127 * (f32)127);
cmd -= (cmd & 0xC0);
} else {
switch (cmd & 0xC0) {
case 0x00:
delay = Nas_ReadLengthData(m);
n->last_delay = delay;
break;
case 0x40:
delay = n->short_note_default_delay;
break;
case 0x80:
delay = n->last_delay;
break;
}
cmd -= (cmd & 0xC0);
}
if (subtrack->velocity_random_variance != 0) {
float_delta = n->velocity_square * (AG.audio_random_seed % subtrack->velocity_random_variance) / 100.0f;
if (AG.audio_random_seed & 0x8000) {
float_delta = -float_delta;
}
n->velocity_square2 = n->velocity_square + float_delta;
if (n->velocity_square2 < 0.0f) {
n->velocity_square2 = 0.0f;
} else if (n->velocity_square2 > 1.0f) {
n->velocity_square2 = 1.0f;
}
} else {
n->velocity_square2 = n->velocity_square;
}
n->delay = delay;
n->gate_delay = (n->gate_time * delay) >> 8;
if (subtrack->gate_time_random_variance != 0) {
/* @BUG - should be gate_time_random_variance (longstanding bug) */
int_delta = (n->gate_delay * (AG.audio_random_seed % subtrack->velocity_random_variance)) / 100;
if (AG.audio_random_seed & 0x4000) {
int_delta = -int_delta;
}
n->gate_delay += int_delta;
if (n->gate_delay < 0) {
n->gate_delay = 0;
} else if (n->gate_delay > n->delay) {
n->gate_delay = n->delay;
}
}
if (grp->flags.muted && (subtrack->mute_flags & (AUDIO_MUTE_FLAG_STOP_SUBTRACK | AUDIO_MUTE_FLAG_STOP_NOTE))) {
n->muted = TRUE;
return COMMON_SCRIPT_END;
}
if (grp->skip_ticks != 0) {
n->muted = TRUE;
return COMMON_SCRIPT_END;
}
return cmd;
}
static void Nas_PriorityChanger(sub* subtrack, u8 prio) {
u8 prio0 = prio & 0xF;
u8 prio1;
if (prio0) {
subtrack->note_priority = prio0;
}
prio1 = (u8)(prio >> 4);
if (prio1) {
subtrack->priority2 = prio1;
}
if (subtrack->note_priority < 2) {
subtrack->note_priority = 2;
}
if (subtrack->priority2 < 1) {
// @BUG - should be subtrack->priority2 = 1;
subtrack->note_priority = 1;
}
}
static u8 Nas_ProgramChanger(sub* subtrack, u8 prog_id, voicetable** inst_pp, env* adsr_env) {
voicetable* inst_p = ProgToVp(subtrack->bank_id, prog_id);
if (inst_p == nullptr) {
*inst_pp = nullptr;
return 0;
} else {
adsr_env->envelope = inst_p->envelope;
adsr_env->decay_idx = inst_p->adsr_decay_idx;
*inst_pp = inst_p;
prog_id += VOICE_TYPE_INSTRUMENT_START;
return prog_id;
}
}
static void Nas_SubVoiceSet(sub* subtrack, u8 inst_id) {
if (inst_id >= 128) {
/* synth waves */
subtrack->inst_or_wave = inst_id;
subtrack->voicetable = nullptr;
} else if (inst_id == 0x7F) {
/* percussion */
subtrack->inst_or_wave = VOICE_TYPE_PERCUSSION;
subtrack->voicetable = (voicetable*)1;
} else if (inst_id == 0x7E) {
/* sound effect */
subtrack->inst_or_wave = VOICE_TYPE_SOUND_EFF;
subtrack->voicetable = (voicetable*)2;
} else {
u8 inst_or_wave;
/* instrument */
inst_or_wave = Nas_ProgramChanger(subtrack, inst_id, &subtrack->voicetable, &subtrack->adsr_env);
subtrack->inst_or_wave = inst_or_wave;
if (inst_or_wave == 0) {
/* failed to load the instrument */
subtrack->has_instrument = FALSE;
return;
}
}
subtrack->has_instrument = TRUE;
}
static void Nas_SubVolumeSet(sub* subtrack, u8 volume) {
subtrack->volume = (f32)(s32)volume / 127.0f;
}
static void Nas_SubSeq(sub* subtrack) {
u8 cmd;
u8 cmdArgU8;
s32 i;
u8* data;
group* grp;
macro* m;
s16 temp1;
u16 cmdArgU16;
u32 cmdArgs[3];
s8 cmdArgS8;
u8 lo_bits;
u8 hi_bits;
u8* seq_data;
u32 new_var2;
s32 new_var3;
if (subtrack->stop_script == FALSE) {
grp = subtrack->group;
if (grp->flags.muted && (subtrack->mute_flags & AUDIO_MUTE_FLAG_STOP_SCRIPT)) {
return;
}
if (subtrack->delay > 1) {
subtrack->delay--;
} else {
/* Sub-Track sequence command process loop */
while (TRUE) {
m = &subtrack->macro_player;
cmd = Nas_ReadByteData(m);
if (cmd >= 0xA0) {
/* Load command arguments */
hi_bits = SCOM_TABLE[(u8)cmd - 0xA0];
lo_bits = hi_bits & 3;
for (i = 0; i < lo_bits; i++) {
if ((hi_bits & 0x80) == SCOM_TYPE_U8) {
cmdArgs[i] = Nas_ReadByteData(m);
} else {
cmdArgs[i] = Nas_ReadWordData(m);
}
hi_bits = hi_bits << 1;
}
if (cmd >= COMMON_CMD_BRANCH_REL_NOT_EQ_ZERO) {
s32 delay = Common_Com(grp, m, cmd, cmdArgs[0]);
if (delay != 0) {
if (delay == COMMON_SCRIPT_END) {
Nas_ReleaseSubTrack(subtrack);
} else {
subtrack->delay = delay;
}
break;
}
} else {
switch ((u8)cmd) {
case SUBTRACK_CMD_STOP: // stop subtrack seq script
subtrack->stop_script = TRUE;
goto note_seq;
case SUBTRACK_CMD_ALLOC_VOICES: // alloc voices
Nas_DeAllocAllVoices(&subtrack->channel_node);
Nas_AllocVoices(&subtrack->channel_node, cmdArgs[0] & 0xFF);
break;
case SUBTRACK_CMD_DEALLOC_VOICES: // dealloc voices
Nas_DeAllocAllVoices(&subtrack->channel_node);
break;
case SUBTRACK_CMD_SET_DYNTBL: // set dynamic table
subtrack->dyn_tbl = (unsigned char(*)[][2])&grp->seq_data[(u16)cmdArgs[0]];
break;
case SUBTRACK_CMD_JMP_DYNTBL: // jump to entry in dynamic table
if (m->value != -1) {
data = (*subtrack->dyn_tbl)[m->value];
cmdArgU16 = (u16)((data[0] << 8) + data[1]);
m->pc = (u8*)&grp->seq_data[cmdArgU16];
}
break;
case SUBTRACK_CMD_INIT_INSTRUMENTS: // Load subtrack bank and set instruments
cmdArgU8 = cmdArgs[0];
if (grp->bank_id != 0xFF) {
cmdArgU16 = ((u16*)AG.map_header)[grp->seq_id];
lo_bits = ((u8*)AG.map_header)[cmdArgU16]; // load number of banks
// inverse lookup, 1 = last bank, 2 = second to last, ...
cmdArgU8 = ((u8*)AG.map_header)[cmdArgU16 + lo_bits - cmdArgU8];
}
if (Nas_SzCacheCheck(BANK_TABLE, CACHE_EITHER, cmdArgU8)) {
subtrack->bank_id = cmdArgU8;
}
cmdArgs[0] = cmdArgs[1];
// fallthrough 0xEB -> 0xC1
case SUBTRACK_CMD_VOICE_SET: // Set subtrack voice
Nas_SubVoiceSet(subtrack, cmdArgs[0]);
break;
case SUBTRACK_CMD_LARGE_NOTE_OFF: // turn off large notes
subtrack->large_notes = FALSE;
break;
case SUBTRACK_CMD_LARGE_NOTE_ON: // turn on large notes
subtrack->large_notes = TRUE;
break;
case SUBTRACK_CMD_SET_VOL: // set volume
cmdArgU8 = (u8)cmdArgs[0];
Nas_SubVolumeSet(subtrack, cmdArgU8);
subtrack->changes.flags.volume = TRUE;
break;
case SUBTRACK_CMD_SET_VOL_SCALE: // set volume scale
cmdArgU8 = (u8)cmdArgs[0];
subtrack->volume_scale = (f32)(s32)cmdArgU8 / 128.0f;
subtrack->changes.flags.volume = TRUE;
break;
case SUBTRACK_CMD_SET_FREQ_SCALE: // set frequency scale
cmdArgU16 = (u16)cmdArgs[0];
subtrack->frequency_scale = (f32)(s32)cmdArgU16 / (f32)0x8000;
subtrack->changes.flags.frequency_scale = TRUE;
break;
case SUBTRACK_CMD_LARGE_BEND_PITCH: // large bend pitch
cmdArgU8 = (u8)cmdArgs[0];
cmdArgU8 += 128;
subtrack->frequency_scale = PCENTTABLE[cmdArgU8];
subtrack->changes.flags.frequency_scale = TRUE;
break;
case SUBTRACK_CMD_SMALL_BEND_PITCH: // small bend pitch
cmdArgU8 = 128 + (u8)cmdArgs[0];
subtrack->frequency_scale = PCENTTABLE2[cmdArgU8];
subtrack->changes.flags.frequency_scale = TRUE;
break;
case SUBTRACK_CMD_SET_PAN: // set pan
cmdArgU8 = (u8)cmdArgs[0];
subtrack->new_pan = cmdArgU8;
subtrack->changes.flags.pan = TRUE;
break;
case SUBTRACK_CMD_SET_PAN_WEIGHT: // set pan mix/channel weight
cmdArgU8 = (u8)cmdArgs[0];
subtrack->pan_channel_weight = cmdArgU8;
subtrack->changes.flags.pan = TRUE;
break;
case SUBTRACK_CMD_SET_TRANSPOSITION: // set transposition
cmdArgS8 = (s8)cmdArgs[0];
subtrack->transposition = cmdArgS8;
break;
case SUBTRACK_CMD_SET_ENVELOPE: // set envelope
cmdArgU16 = (u16)cmdArgs[0];
subtrack->adsr_env.envelope = (envdat*)&grp->seq_data[cmdArgU16];
break;
case SUBTRACK_CMD_SET_DECAY_IDX: // set decay index
cmdArgU8 = (u8)cmdArgs[0];
subtrack->adsr_env.decay_idx = cmdArgU8;
break;
case SUBTRACK_CMD_SET_VIBRATO_DEPTH: // set vibrato depth
cmdArgU8 = (u8)cmdArgs[0];
subtrack->vibrato_params.depth_target = cmdArgU8 * 8;
subtrack->vibrato_params.depth_start = 0;
subtrack->vibrato_params.depth_change_delay = 0;
break;
case SUBTRACK_CMD_SET_VIBRATO_RATE: // set vibrato rate
cmdArgU8 = (u8)cmdArgs[0];
subtrack->vibrato_params.rate_target = cmdArgU8 * 32;
subtrack->vibrato_params.rate_start = cmdArgU8 * 32;
subtrack->vibrato_params.rate_change_delay = 0;
break;
case SUBTRACK_CMD_SET_VIBRATO_DEPTH_LINEAR: // set vibrato depth linear
cmdArgU8 = (u8)cmdArgs[0];
subtrack->vibrato_params.depth_start = cmdArgU8 * 8;
cmdArgU8 = (u8)cmdArgs[1];
subtrack->vibrato_params.depth_target = cmdArgU8 * 8;
cmdArgU8 = (u8)cmdArgs[2];
subtrack->vibrato_params.depth_change_delay = cmdArgU8 * 16;
break;
case SUBTRACK_CMD_SET_VIBRATO_RATE_LINEAR: // set vibrato rate linear
cmdArgU8 = (u8)cmdArgs[0];
subtrack->vibrato_params.rate_start = cmdArgU8 * 32;
cmdArgU8 = (u8)cmdArgs[1];
subtrack->vibrato_params.rate_target = cmdArgU8 * 32;
cmdArgU8 = (u8)cmdArgs[2];
subtrack->vibrato_params.rate_change_delay = cmdArgU8 * 16;
break;
case SUBTRACK_CMD_SET_VIBRATO_DELAY: // set vibrato delay
cmdArgU8 = (u8)cmdArgs[0];
subtrack->vibrato_params.delay = cmdArgU8 * 16;
break;
case SUBTRACK_CMD_SET_REVERB_VOL: // set reverb volume
cmdArgU8 = (u8)cmdArgs[0];
subtrack->target_reverb_vol = cmdArgU8;
break;
case SUBTRACK_CMD_SET_INSTRUMENT_BANK: // set bank
cmdArgU8 = cmdArgs[0];
if (grp->bank_id != 0xFF) {
cmdArgU16 = ((u16*)AG.map_header)[grp->seq_id]; // get offset for bank info for seq
lo_bits = ((u8*)AG.map_header)[cmdArgU16]; // read number of banks
// inverse lookup, 1 = last bank, 2 = second to last, ...
cmdArgU8 = ((u8*)AG.map_header)[cmdArgU16 + lo_bits - cmdArgU8];
}
if (Nas_SzCacheCheck(BANK_TABLE, CACHE_EITHER, cmdArgU8)) {
subtrack->bank_id = cmdArgU8;
}
break;
case SUBTRACK_CMD_WRITE_GROUP_SEQ: // write to sequence script
cmdArgU8 = (u8)cmdArgs[0];
cmdArgU16 = (u16)cmdArgs[1];
seq_data = &grp->seq_data[cmdArgU16];
seq_data[0] = (u8)m->value + cmdArgU8;
break;
case SUBTRACK_CMD_MACRO_SUBTRACT: // subtract macro value
case SUBTRACK_CMD_MACRO_SET: // set macro value
case SUBTRACK_CMD_MACRO_AND: // bit& macro value
cmdArgS8 = (s8)cmdArgs[0];
if (cmd == SUBTRACK_CMD_MACRO_SUBTRACT) {
m->value -= cmdArgS8;
} else if (cmd == SUBTRACK_CMD_MACRO_SET) {
m->value = cmdArgS8;
} else {
m->value &= cmdArgS8;
}
break;
case SUBTRACK_CMD_DISABLE_SUBTRACK: // disable subtrack
cmdArgU8 = (u8)cmdArgs[0];
Nas_ReleaseSubTrack(grp->subtracks[cmdArgU8]);
break;
case SUBTRACK_CMD_SET_MUTE_FLAGS: // set mute flags
cmdArgU8 = (u8)cmdArgs[0];
subtrack->mute_flags = cmdArgU8;
subtrack->changes.flags.volume = TRUE;
break;
case SUBTRACK_CMD_MACRO_LOAD_FROM_GROUP_SEQ: // load macro register value from table
cmdArgU16 = (u16)cmdArgs[0];
m->value = grp->seq_data[(u32)(cmdArgU16 + m->value)];
break;
case SUBTRACK_CMD_SET_DYNVAL: // set stored sequence data index
cmdArgU16 = (u16)cmdArgs[0];
subtrack->dynamic_value = cmdArgU16;
break;
case SUBTRACK_CMD_WRITE_DYNVAL_TO_GROUP_SEQ: // write u16 into sequence script
cmdArgU16 = (u16)cmdArgs[0];
seq_data = &grp->seq_data[cmdArgU16];
seq_data[0] = (subtrack->dynamic_value >> 8) & 0xFF;
seq_data[1] = subtrack->dynamic_value & 0xFF;
break;
case SUBTRACK_CMD_STEREO_PHASE_SET: // stereo phase effects
cmdArgU8 = (u8)cmdArgs[0];
if (cmdArgU8 & 0x80) {
subtrack->stereo_effects = TRUE;
} else {
subtrack->stereo_effects = FALSE;
}
*(u8*)&subtrack->stereo_phase = cmdArgU8 & 0x7F;
break;
case SUBTRACK_CMD_SET_NOTE_ALLOC_POLICY: // set note allocation policy
cmdArgU8 = (u8)cmdArgs[0];
subtrack->note_alloc_policy = cmdArgU8;
break;
case SUBTRACK_CMD_SET_SUSTAIN: // set sustain
cmdArgU8 = (u8)cmdArgs[0];
subtrack->adsr_env.sustain = cmdArgU8;
break;
case SUBTRACK_CMD_SET_REVERB_IDX: // set reverb index
cmdArgU8 = (u8)cmdArgs[0];
subtrack->reverb_idx = cmdArgU8;
break;
case SUBTRACK_CMD_DYNTBL_CALL: // dynamic call
if (m->value != -1) {
data = (*subtrack->dyn_tbl)[m->value];
/* @BUG - missing stack depth bounds check */
m->stack[m->depth++] = m->pc;
cmdArgU16 = (u16)((data[0] << 8) + data[1]);
m->pc = &grp->seq_data[cmdArgU16];
}
break;
case SUBTRACK_CMD_SET_BOOK_OFS: // set book ofs
cmdArgU8 = (u8)cmdArgs[0];
subtrack->book_ofs = cmdArgU8;
break;
case SUBTRACK_CMD_SET_ENV_PARAMS_FROM_GROUP_SEQ: //
cmdArgU16 = (u16)cmdArgs[0];
data = &grp->seq_data[cmdArgU16];
subtrack->mute_flags = data[0];
subtrack->note_alloc_policy = data[1];
Nas_PriorityChanger(subtrack, data[2]);
subtrack->transposition = (s8)data[3];
subtrack->new_pan = data[4];
subtrack->pan_channel_weight = data[5];
subtrack->target_reverb_vol = data[6];
subtrack->reverb_idx = data[7];
/* @BUG - didn't update reverb state as changed */
subtrack->changes.flags.pan = TRUE;
break;
case SUBTRACK_CMD_SET_ENV_PARAMS: //
subtrack->mute_flags = cmdArgs[0];
subtrack->note_alloc_policy = cmdArgs[1];
cmdArgU8 = (u8)cmdArgs[2];
Nas_PriorityChanger(subtrack, cmdArgU8);
subtrack->transposition = (s8)Nas_ReadByteData(m);
subtrack->new_pan = Nas_ReadByteData(m);
subtrack->pan_channel_weight = Nas_ReadByteData(m);
subtrack->target_reverb_vol = Nas_ReadByteData(m);
subtrack->reverb_idx = Nas_ReadByteData(m);
/* @BUG - didn't update reverb state as changed */
subtrack->changes.flags.pan = TRUE;
break;
case SUBTRACK_CMD_RESET_VIBRATO: // reset vibrato
subtrack->frequency_scale = 1.0f;
subtrack->vibrato_params.depth_target = 0;
subtrack->vibrato_params.depth_start = 0;
subtrack->vibrato_params.depth_change_delay = 0;
subtrack->vibrato_params.rate_target = 0;
subtrack->vibrato_params.rate_start = 0;
subtrack->vibrato_params.rate_change_delay = 0;
subtrack->filter = nullptr;
subtrack->gain = 0;
subtrack->adsr_env.sustain = 0;
subtrack->velocity_random_variance = 0;
subtrack->gate_time_random_variance = 0;
subtrack->comb_filter_size = 0;
subtrack->comb_filter_gain = 0;
subtrack->book_ofs = 0;
subtrack->sample_start_pos = 0;
subtrack->_E0 = 0;
break;
case SUBTRACK_CMD_SET_PRIORITY: // set priority
cmdArgU8 = (u8)cmdArgs[0];
Nas_PriorityChanger(subtrack, cmdArgU8);
break;
case SUBTRACK_CMD_SET_GAIN: // set gain
cmdArgU8 = (u8)cmdArgs[0];
subtrack->gain = cmdArgU8;
break;
case SUBTRACK_CMD_SET_FILTER: // set filter
cmdArgU16 = (u16)cmdArgs[0];
data = &grp->seq_data[cmdArgU16];
subtrack->filter = (s16*)data;
break;
case SUBTRACK_CMD_CLEAR_FILTER: // clear filter
subtrack->filter = nullptr;
break;
case SUBTRACK_CMD_LOAD_FILTER: // load filter
cmdArgU8 = (u8)cmdArgs[0];
if (subtrack->filter != nullptr) {
lo_bits = (cmdArgU8 >> 4) & 0xF;
cmdArgU8 &= 0xF;
Nas_SetBPFilter(subtrack->filter, lo_bits, cmdArgU8);
}
break;
case SUBTRACK_CMD_LOAD_DYNVAL_FROM_GROUP_SEQ: // load dynamic idx
cmdArgU16 = (u16)cmdArgs[0];
subtrack->dynamic_value = *(u16*)&grp->seq_data[cmdArgU16 + m->value * 2];
break;
case SUBTRACK_CMD_SET_DYNTBL_FROM_GROUP_SEQ: // set dynamic table
subtrack->dyn_tbl = (unsigned char(*)[][2]) & grp->seq_data[subtrack->dynamic_value];
break;
case SUBTRACK_CMD_LOAD_DYNVAL_FROM_DYNTBL: // read from dynamic table
subtrack->dynamic_value = ((u16*)subtrack->dyn_tbl)[m->value];
break;
case SUBTRACK_CMD_MACRO_LOAD_FROM_DYNTBL: // read to macro register from dynamic table
m->value = (*subtrack->dyn_tbl)[0][m->value];
break;
case SUBTRACK_CMD_RANDOM_DYNVAL: // random dynamic value
if (cmdArgs[0] == 0) {
subtrack->dynamic_value = Nap_GetRandom();
} else {
subtrack->dynamic_value = Nap_GetRandom() % cmdArgs[0];
}
break;
case SUBTRACK_CMD_MACRO_RANDOM_VAL: // macro register random value
if (cmdArgs[0] == 0) {
m->value = Nap_GetRandom();
} else {
m->value = Nap_GetRandom() % cmdArgs[0];
}
break;
case SUBTRACK_CMD_SET_VEL_RANDOM_VARIANCE: // set velocity random variance
cmdArgU8 = (u8)cmdArgs[0];
subtrack->velocity_random_variance = cmdArgU8;
break;
case SUBTRACK_CMD_SET_GATE_TIME_RANDOM_VARIANCE: // set gate time random variance
cmdArgU8 = (u8)cmdArgs[0];
subtrack->gate_time_random_variance = cmdArgU8;
break;
case SUBTRACK_CMD_SET_COMB_FILTER_SIZE_GAIN: // set comb filter size & gain
cmdArgU8 = (u8)cmdArgs[0];
subtrack->comb_filter_size = cmdArgU8;
cmdArgU16 = (u16)cmdArgs[1];
subtrack->comb_filter_gain = cmdArgU16;
break;
case SUBTRACK_CMD_ADD_DYNVAL: // Add to dynamic value
subtrack->dynamic_value += cmdArgs[0];
break;
case SUBTRACK_CMD_DYNVAL_SPECIAL: // Dynamic value bit shift & negate (when s16), differs from OoT and MM
new_var2 = (subtrack->dynamic_value & 0x7F);
new_var2 |= 0x8000 | ((subtrack->dynamic_value & 0x7FC0) << 1);
subtrack->dynamic_value = new_var2;
break;
case SUBTRACK_CMD_SET_SAMPLE_START_POS: // set sample start pos
subtrack->sample_start_pos = cmdArgs[0];
break;
case SUBTRACK_CMD_MACRO_SET_FROM_CALLBACK: // call custom sequence callback and update macro register value
if (cmdArgs[0] <= 4) {
if (AG.seq_callbacks[cmdArgs[0]] != nullptr) {
NA_CALLBACK = AG.seq_callbacks[cmdArgs[0]];
m->value = (*NA_CALLBACK)(m->value, subtrack);
}
}
break;
case SUBTRACK_CMD_MACRO_LOAD_FROM_SFX_STATE: // read from sfx state using macro register value as index
case SUBTRACK_CMD_MACRO_LOAD_FROM_SFX_STATE_DYNVAL: // read from sfx state using dynamic value as index
case SUBTRACK_CMD_SFX_STATE_SET: // write to sfx state using macro register value as index
case SUBTRACK_CMD_SFX_STATE_SET_DYNVAL: // write to sfx state using dynamic value as index
if (cmd == SUBTRACK_CMD_MACRO_LOAD_FROM_SFX_STATE || cmd == SUBTRACK_CMD_SFX_STATE_SET) {
cmdArgU16 = cmdArgs[0];
} else {
cmdArgU16 = subtrack->dynamic_value;
}
if (subtrack->sfx_state != nullptr) {
if (cmd == SUBTRACK_CMD_MACRO_LOAD_FROM_SFX_STATE || cmd == SUBTRACK_CMD_MACRO_LOAD_FROM_SFX_STATE_DYNVAL) {
m->value = subtrack->sfx_state[cmdArgU16];
} else {
subtrack->sfx_state[cmdArgU16] = m->value;
}
}
break;
case SUBTRACK_CMD_SURROUND_EFFECT_IDX_SET: // set surround effect index
subtrack->surround_effect_idx = cmdArgs[0];
break;
case SUBTRACK_CMD_MACRO_ADD_SUBTRACK_IDX: // add subtrack index to macro register value
m->value += subtrack->subtrack_idx;
break;
case SUBTRACK_CMD_WRITE_GOUP_SEQ_OFFSET_BY_SUBTRACK: // Set value in sequence data to macro register value plus arg value
cmdArgU8 = (u8)cmdArgs[0];
cmdArgU16 = (u16)cmdArgs[1];
seq_data = &grp->seq_data[cmdArgU16 + subtrack->subtrack_idx];
*seq_data = (u8)m->value + cmdArgU8;
break;
case SUBTRACK_CMD_MACRO_BIT_MOD: // manipulate macro register value
u32 shift = (cmdArgs[0] & 0xF);
lo_bits = (m->value & 0x80);
new_var2 = (cmdArgs[0] & 0x80);
new_var3 = (cmdArgs[0] & 0x40);
if (new_var2 == 0) {
cmdArgU8 = m->value << shift;
} else {
cmdArgU8 = m->value >> shift;
}
if (new_var3) {
cmdArgU8 &= (u8)~0x80;
cmdArgU8 |= lo_bits;
}
m->value = cmdArgU8;
break;
}
}
// continue;
} else if (cmd >= SUBTRACK_CMD_PORT_WRITE_MACRO_REG_MASK) {
s32 hi_bits = (cmd & (u8)~7);
lo_bits = cmd;
lo_bits &= ~0xF8;
/* if command is not in range [0x70, 0x77] and the lower 4 bits are >= 4, set them to be 0xY0*/
if (hi_bits != SUBTRACK_CMD_PORT_WRITE_MACRO_REG_MASK && lo_bits >= ARRAY_COUNT(subtrack->note_layers)) {
lo_bits = 0;
}
switch (hi_bits) {
// [0x80 - 0x83]
case SUBTRACK_CMD_READ_NOTE_FINISHED_MASK: // macro register value = subtrack->note_layers[idx]->finished
if (subtrack->note_layers[lo_bits] != nullptr) {
m->value = subtrack->note_layers[lo_bits]->finished;
} else {
m->value = -1;
}
break;
// [0x88 - 0x8B]
case SUBTRACK_CMD_NOTE_SET_PC_MASK: // start note layer and initialize note macro pc
cmdArgU16 = Nas_ReadWordData(m);
if (Nas_EntryNoteTrack(subtrack, lo_bits) == 0) {
subtrack->note_layers[lo_bits]->macro_player.pc = &grp->seq_data[cmdArgU16];
}
break;
// [0x90 - 0x97] NOTE: 0x94-0x97 are invalid and will be changed to 0x90.
case SUBTRACK_CMD_NOTE_STOP_MASK: // stop note layer
Nas_CloseNoteTrack(subtrack, lo_bits);
break;
// [0x98 - 0x9B]
case SUBTRACK_CMD_NOTE_START_DYNTBL_MASK: // start note layer and initialize note macro pc from data at macro register offset
if (m->value != -1) {
if (Nas_EntryNoteTrack(subtrack, lo_bits) != -1) {
data = (*subtrack->dyn_tbl)[m->value];
cmdArgU16 = (u16)((data[0] << 8) + data[1]);
subtrack->note_layers[lo_bits]->macro_player.pc = &grp->seq_data[cmdArgU16];
}
}
break;
// [0x70 - 0x77]
case SUBTRACK_CMD_PORT_WRITE_MACRO_REG_MASK: // write macro register value into subtrack port
subtrack->port[lo_bits] = m->value;
break;
// [0x78 - 0x7B]
case SUBTRACK_CMD_NOTE_START_MASK: // start note layer and initialize note macro pc from subtrack pc and rel offset
temp1 = Nas_ReadWordData(m);
if (Nas_EntryNoteTrack(subtrack, lo_bits) == 0) {
subtrack->note_layers[lo_bits]->macro_player.pc = m->pc + temp1;
}
break;
}
} else {
/* Process commands 0x00 - 0x6F */
lo_bits = cmd & 0xF; // each command has 16 indicies which come from the lower 4 bits
switch (cmd & 0xF0) {
// [0x00, 0x0F]
case SUBTRACK_CMD_DELAY_MASK: // set delay
subtrack->delay = lo_bits;
if (lo_bits == 0) {
break; /* clear delay, continue processing */
} else {
goto note_seq; /* delay > 0 so stop processing subtrack commands for now */
}
// [0x10, 0x17] & [0x18, 0x1F]
case SUBTRACK_CMD_VOICELOAD_MASK: // load new wave samples
if (lo_bits < 8) {
subtrack->port[lo_bits] = -1; // clear port
/* Load samples from bank with inst id being macro's register value, load result in
* port[lo_bits] */
VoiceLoad(subtrack->bank_id, m->value, &subtrack->port[lo_bits]);
} else {
lo_bits -= 8;
subtrack->port[lo_bits] = -1;
/* Load synth? samples based on dynamic value */
VoiceLoad(subtrack->bank_id, 0x100 + subtrack->dynamic_value, &subtrack->port[lo_bits]);
}
break;
// [0x60, 0x6F] (only 0x60, 0x67 are valid)
case SUBTRACK_CMD_MACRO_READ_PORT0_MASK: // read port value into macro register value
m->value = subtrack->port[lo_bits];
if (lo_bits < 2) {
subtrack->port[lo_bits] = -1;
}
break;
// [0x50, 0x5F] (only 0x50, 0x57 are valid)
case SUBTRACK_CMD_MACRO_SUBTRACT_MASK: // subtrack port value from macro register value
m->value -= subtrack->port[lo_bits];
break;
// [0x20, 0x2F]
case SUBTRACK_CMD_START_SUBTRACK_MASK: // start subtrack
cmdArgU16 = (u16)Nas_ReadWordData(m);
Nas_OpenSub(grp, lo_bits, &grp->seq_data[cmdArgU16]);
break;
// [0x30, 0x3F]
case SUBTRACK_CMD_WRITE_SUBTRACK_PORT_MASK: // set subtrack port to this subtrack's macro register value
cmdArgU8 = Nas_ReadByteData(m);
grp->subtracks[lo_bits]->port[cmdArgU8] = m->value;
break;
// [0x40, 0x4F]
case SUBTRACK_CMD_READ_SUBTRACK_PORT_MASK: // set this subtrack's macro register value to other subtrack's port
cmdArgU8 = Nas_ReadByteData(m);
m->value = grp->subtracks[lo_bits]->port[cmdArgU8];
break;
}
}
}
}
}
note_seq:
for (i = 0; i < ARRAY_COUNT(subtrack->note_layers); i++) {
if (subtrack->note_layers[i] != nullptr) {
Nas_NoteSeq(subtrack->note_layers[i]);
}
}
}
static void Nas_GroupSeq(group* grp) {
u8 cmd;
u8 cmd_lo_bits;
macro* m;
s16 tempS;
u16 temp;
s32 i;
s32 value;
u8* data1;
u8* data2;
u8* data3;
u8* data4;
s32 tempo_change;
sub* subtrack;
u16* new_var;
s32 delay;
f32 tempf32;
s8 argS8;
s16 argS16;
u8 argU8;
if (grp->flags.enabled) {
if (Nas_CheckIDseq(grp->seq_id) == FALSE || Nas_CheckIDbank(grp->bank_id) == FALSE) {
Nas_CheckIDseq(grp->seq_id);
Nas_CheckIDseq(grp->bank_id);
Nas_ReleaseGroup(grp);
} else {
Nas_WriteIDseq(grp->seq_id, LOAD_STATUS_COMPLETE);
Nas_WriteIDbank(grp->bank_id, LOAD_STATUS_COMPLETE);
if (grp->flags.muted && (grp->mute_flags & AUDIO_MUTE_FLAG_STOP_SCRIPT)) {
return;
}
grp->script_counter++;
tempo_change = grp->tempo + grp->tempo_change;
if (tempo_change > AG.max_tempo) {
tempo_change = AG.max_tempo;
}
grp->tempo_acceleration += tempo_change;
if (grp->tempo_acceleration < AG.max_tempo) {
return;
}
grp->tempo_acceleration -= (u16)AG.max_tempo;
grp->counter++;
if (grp->flags.stop_seq_script == TRUE) {
return;
}
if (grp->delay > 1) {
grp->delay--;
} else {
m = &grp->macro_player;
grp->flags.recalculate_volume = TRUE;
/* Sequence group commands processing loop */
while (TRUE) {
cmd = Nas_ReadByteData(m);
if (cmd >= COMMON_CMD_BRANCH_REL_NOT_EQ_ZERO) {
u16 arg = Convert_Com(m, cmd);
delay = Common_Com(grp, m, cmd, arg);
if (delay != 0) {
/* Group is on delay or script has exited */
if (delay == COMMON_SCRIPT_END) {
Nas_ReleaseGroup(grp);
} else {
grp->delay = delay;
}
break;
}
} else if (cmd >= 0xC0) {
switch (cmd) {
case GRP_CMD_RESERVE_VOICES: // reserve voices
Nas_DeAllocAllVoices(&grp->channel_node);
cmd = Nas_ReadByteData(m);
Nas_AllocVoices(&grp->channel_node, (u8)cmd);
break;
case GRP_CMD_RELEASE_VOICES: // release voices
Nas_DeAllocAllVoices(&grp->channel_node);
break;
case GRP_CMD_TRANSPOSITION_ABSOLUTE: // absolute transposition
grp->transposition = 0;
// fallthrough 0xDF -> 0xDE
case GRP_CMD_TRANSPOSITION_RELATIVE: // relative transposition
argU8 = Nas_ReadByteData(m);
grp->transposition = grp->transposition + (s8)argU8;
break;
case GRP_CMD_SET_TEMPO: // set tempo
grp->tempo = Nas_ReadByteData(m) * AUDIO_TATUMS_PER_BEAT;
if (grp->tempo > AG.max_tempo) {
grp->tempo = AG.max_tempo;
}
if ((s16)grp->tempo < 1) {
grp->tempo = 1;
}
break;
case GRP_CMD_SET_TEMPO_CHANGE: // adjust tempo relative
grp->tempo_change = (s8)Nas_ReadByteData(m) * AUDIO_TATUMS_PER_BEAT;
break;
case GRP_CMD_CHANGE_VOLUME: // change volume
cmd = Nas_ReadByteData(m);
temp = Nas_ReadWordData(m);
switch (cmd) {
case GROUP_STATE_0:
case GROUP_STATE_FADE_IN:
if (grp->state != GROUP_STATE_FADE_OUT) {
grp->stored_fade_timer = temp;
grp->state = cmd;
}
break;
case GROUP_STATE_FADE_OUT:
grp->fade_timer = temp;
grp->state = cmd;
grp->fade_velocity = -grp->fade_volume / (f32)(s32)grp->fade_timer;
break;
}
break;
case GRP_CMD_SET_VOLUME: // set volume
tempf32 = (s32)Nas_ReadByteData(m) / 127.0f;
switch (grp->state) {
case GROUP_STATE_FADE_IN:
grp->fade_volume = 0.0f;
grp->state = GROUP_STATE_0;
// fallthrough GROUP_STATE_FADE_IN
case GROUP_STATE_0:
temp = grp->stored_fade_timer;
grp->fade_timer = temp;
if (temp != 0) {
grp->fade_velocity =
(tempf32 - grp->fade_volume) / (f32)(s32)grp->fade_timer;
} else {
grp->fade_volume = tempf32;
}
break;
case GROUP_STATE_FADE_OUT:
break;
}
break;
case GRP_CMD_SET_VOLUME_SCALE: // set volume scale
grp->fade_volume_scale = (s8)Nas_ReadByteData(m) / 127.0f;
break;
case GRP_CMD_ALLOC_SUBTRACKS: // initialize subtracks
temp = Nas_ReadWordData(m);
Nas_AllocSub(grp, temp);
break;
case GRP_CMD_DISABLE_SUBTRACKS: // [ineffective] - disable subtracks
Nas_ReadWordData(m);
break;
case GRP_CMD_SET_MUTE_SCALE: // set mute scale
grp->mute_volume_scale = (s8)Nas_ReadByteData(m) / 127.0f;
break;
case GRP_CMD_MUTE: // mute
grp->flags.muted = TRUE;
break;
case GRP_CMD_SET_MUTE_BEHAVIOR: // set mute behaivor
grp->mute_flags = Nas_ReadByteData(m);
break;
case GRP_CMD_SET_SHORT_NOTE_GATE_TIME_TBL: // set short note gate-time table
case GRP_CMD_SET_SHORT_NOTE_VELOCITY_TBL: // set short note velocity table
temp = Nas_ReadWordData(m);
data3 = &grp->seq_data[temp];
if (cmd == GRP_CMD_SET_SHORT_NOTE_VELOCITY_TBL) {
grp->short_note_velocity_tbl = data3;
} else {
grp->short_note_gate_time_tbl = data3;
}
break;
case GRP_CMD_SET_NOTE_ALLOC_POLICY: // set note allocation policy
grp->note_alloc_policy = Nas_ReadByteData(m);
break;
case GRP_CMD_MACRO_VALUE_RANDOM: // set macro register value to random value
cmd = Nas_ReadByteData(m);
if (cmd == 0) {
/* 0-255 */
m->value = (AG.audio_random_seed >> 2) & 0xFF;
} else {
/* 0-N */
m->value = (AG.audio_random_seed >> 2) % cmd;
}
break;
case GRP_CMD_DYN_TBL_CALL: // dynamic look-up table call
temp = Nas_ReadWordData(m);
if (m->value != -1 && m->depth != 3) {
data1 = &grp->seq_data[(u32)(temp + (m->value << 1))];
m->stack[m->depth++] = m->pc;
temp = (data1[0] << 8) + data1[1];
m->pc = &grp->seq_data[temp];
}
break;
case GRP_CMD_MACRO_VALUE_LOAD: // set macro register value
m->value = Nas_ReadByteData(m);
break;
case GRP_CMD_MACRO_VALUE_BIT_AND: // macro register bit mask
m->value &= Nas_ReadByteData(m);
break;
case GRP_CMD_MACRO_VALUE_SUBTRACT: // macro register subtraction
m->value -= Nas_ReadByteData(m);
break;
case GRP_CMD_MACRO_VALUE_STORE: // write macro register value to sequence data
cmd = Nas_ReadByteData(m);
temp = Nas_ReadWordData(m);
data2 = &grp->seq_data[temp];
data2[0] = (u8)m->value + cmd;
break;
case GRP_CMD_DYNAMIC_BRANCH: // dynamic lookup branch (no return)
temp = Nas_ReadWordData(m);
if (m->value != -1) {
data4 = &grp->seq_data[(u32)(temp + (m->value << 1))];
temp = (data4[0] << 8) + data4[1];
m->pc = &grp->seq_data[temp];
}
break;
case GRP_CMD_STOP_SEQ: // stop script
grp->flags.stop_seq_script = TRUE;
return;
case GRP_CMD_UPDATE_COUNTER: // update counter
grp->counter = Nas_ReadWordData(m);
break;
case GRP_CMD_EF: //
Nas_ReadWordData(m);
Nas_ReadByteData(m);
break;
case GRP_CMD_START_SEQ: // start sequence script
/* Get sequence group index */
cmd = Nas_ReadByteData(m);
if (cmd == 0xFF) {
cmd = grp->group_idx;
}
Nas_StartMySeq(cmd, Nas_ReadByteData(m), 0);
if (cmd == (u8)grp->group_idx) {
return; // stop processing since we just used this group for a new sequence
}
break;
case GRP_CMD_MUTE_SUBTRACKS: // mute subtracks from u16 bitfield
temp = Nas_ReadWordData(m);
if (m->value != -1) {
new_var = (u16*)(grp->seq_data + (u32)(temp + m->value * 2));
temp = *new_var;
for (i = 0; i < ARRAY_COUNT(grp->subtracks); i++) {
grp->subtracks[i]->muted = temp & 1;
temp = temp >> 1;
}
}
break;
}
} else if (cmd == GRP_CMD_CALLBACK) {
cmd = Nas_ReadByteData(m);
if (cmd <= 4) {
if (AG.seq_callbacks[cmd] != nullptr) {
NA_GRP_CALLBACK = (GRP_CALLBACK)AG.seq_callbacks[cmd];
m->value = (*(GRP_CALLBACK)AG.seq_callbacks[cmd])(m->value, grp);
}
} else {
break;
}
} else {
/* Commands 0x00 - 0xBD & 0xBF */
cmd_lo_bits = cmd & 0x0F;
switch (cmd & 0xF0) {
// [0x00, 0x0F]
case GRP_CMD_MACRO_VALUE_LOAD_SUBTRACK_DISABLED_MASK: // macro register value = !subtrack->enabled
m->value = grp->subtracks[cmd_lo_bits]->enabled ^ TRUE;
break;
// [0x50, 0x57] @BUG - ([0x58, 0x5F] end here but would result in undefined behavior)
case GRP_CMD_MACRO_VALUE_SUBTRACT_FROM_PORT_MASK: // macro register subtract port value
m->value -= grp->port[cmd_lo_bits];
break;
// [0x70, 0x77] @BUG - ([0x78, 0x7F] end here but would result in undefined behavior)
case GRP_CMD_MACRO_VALUE_WRITE_PORT_MASK: // write macro register value into port
grp->port[cmd_lo_bits] = m->value;
break;
// [0x80, 0x87] @BUG - ([0x88, 0x8F] end here but would result in undefined behavior)
case GRP_CMD_MACRO_VALUE_READ_PORT_MASK: // read macro register value from port
m->value = grp->port[cmd_lo_bits];
if (cmd_lo_bits < 2) {
grp->port[cmd_lo_bits] = -1;
}
break;
// [0x40, 0x4F]
case GRP_CMD_DISABLE_SUBTRACK_MASK: // disable subtrack
Nas_ReleaseSubTrack(grp->subtracks[cmd_lo_bits]);
break;
// [0x90, 0x9F]
case GRP_CMD_START_SUBTRACK_MASK: // start subtrack w/ absolute script address
temp = Nas_ReadWordData(m);
Nas_OpenSub(grp, cmd_lo_bits, &grp->seq_data[temp]);
break;
// [0xA0, 0xAF]
case GRP_CMD_START_RELATIVE_SUBTRACK_MASK: // start subtrack w/ relative script address
tempS = Nas_ReadWordData(m);
Nas_OpenSub(grp, cmd_lo_bits, &m->pc[tempS]);
break;
// [0xB0, 0xB7] @BUG - ([0xB8, 0xBF] end here but would result in undefined behavior)
case GRP_CMD_SEQ_LOAD_MASK: // load sequence
cmd = Nas_ReadByteData(m);
temp = Nas_ReadWordData(m);
data2 = &grp->seq_data[temp];
SeqLoad(cmd, data2, &grp->port[cmd_lo_bits]);
break;
// [0x60, 0x67] @BUG - ([0x68, 0x6F] end here but would result in undefined behavior)
case GRP_CMD_ASYNC_LOAD_MASK: // async load
cmd = (u8)Nas_ReadByteData(m);
argU8 = Nas_ReadByteData(m);
MK_load(cmd, argU8, (u8*)&grp->port[cmd_lo_bits]);
break;
}
}
}
}
for (i = 0; i < AUDIO_SUBTRACK_NUM; i++) {
subtrack = grp->subtracks[i];
if (subtrack->enabled) {
Nas_SubSeq(subtrack);
}
}
}
}
}
extern void Nas_MySeqMain(u32 frames_left) {
group* grp;
group* main_grp;
u32 i;
AG.sample_state_offset = (AG.audio_params.updates_per_frame - (frames_left + 1)) * AG.num_channels;
for (i = 0; i < AG.audio_params.num_groups; i++) {
grp = &AG.groups[i];
if (grp->flags.enabled == TRUE) {
Nas_GroupSeq(grp);
Nas_MainCtrl(grp);
}
}
main_grp = &AG.main_group;
Nas_GroupSeq(main_grp);
Nas_MainCtrl(main_grp);
Nas_UpdateChannel();
}
extern void Nas_SeqSkip(group* grp) {
while (grp->skip_ticks > 0) {
Nas_GroupSeq(grp);
Nas_MainCtrl(grp);
grp->skip_ticks--;
}
}
extern void Nas_InitMySeq(group* grp) {
s32 i;
Nas_ReleaseGroup(grp);
grp->flags.stop_seq_script = FALSE;
grp->delay = 0;
grp->state = GROUP_STATE_FADE_IN;
grp->fade_timer = 0;
grp->stored_fade_timer = 0;
grp->tempo_acceleration = 0;
grp->tempo = 120 * AUDIO_TATUMS_PER_BEAT;
grp->tempo_change = 0;
grp->fade_volume = 1.0f;
grp->fade_velocity = 0.0f;
grp->volume = 0.0f;
grp->mute_volume_scale = 0.5f;
grp->transposition = 0;
grp->note_alloc_policy = 0;
grp->short_note_velocity_tbl = DEFAULT_VTABLE;
grp->short_note_gate_time_tbl = DEFAULT_GTABLE;
grp->script_counter = 0;
grp->counter = 0;
for (i = 0; i < AUDIO_SUBTRACK_NUM; i++) {
Nas_InitSubTrack(grp->subtracks[i]);
}
}
extern void Nas_AssignSubTrack(s32 group_idx) {
sub* subtrack;
group* grp = &AG.groups[group_idx];
s32 i;
s32 j;
for (i = 0; i < AUDIO_SUBTRACK_NUM; i++) {
grp->subtracks[i] = (sub*)Nas_HeapAlloc_CL(&AG.misc_heap, sizeof(sub));
if (grp->subtracks[i] == nullptr) {
grp->subtracks[i] = &AG.null_sub_track;
} else {
subtrack = grp->subtracks[i];
subtrack->group = grp;
subtrack->subtrack_idx = i;
subtrack->enabled = FALSE;
for (j = 0; j < ARRAY_COUNT(subtrack->note_layers); j++) {
subtrack->note_layers[j] = nullptr;
}
}
Nas_InitSubTrack(grp->subtracks[i]);
}
}
static void __InitGroup(group* grp) {
s32 i;
s32 j;
for (i = 0; i < AUDIO_SUBTRACK_NUM; i++) {
grp->subtracks[i] = &AG.null_sub_track;
}
grp->flags.enabled = FALSE;
grp->flags.muted = FALSE;
grp->flags.bank_dma_in_progress = FALSE;
grp->flags.sequence_dma_in_progress = FALSE;
grp->flags.apply_bend = FALSE;
for (j = 0; j < ARRAY_COUNT(grp->port); j++) {
grp->port[j] = -1;
}
grp->mute_flags = AUDIO_MUTE_FLAG_SOFTEN | AUDIO_MUTE_FLAG_STOP_NOTE;
grp->fade_volume_scale = 1.0f;
grp->bend = 1.0f;
Nas_InitChNode(&grp->channel_node);
Nas_InitMySeq(grp);
}
extern void Nas_InitPlayer(void) {
group* grp;
sub* subtrack;
s32 i;
Nas_InitNoteList();
for (i = 0; i < AUDIO_NOTE_MAX; i++) {
AG.notes[i].sub_track = nullptr;
AG.notes[i].enabled = FALSE;
}
for (i = 0; i < AUDIO_GROUP_MAX; i++) {
AG.groups_p[i] = &AG.groups[i];
__InitGroup(&AG.groups[i]);
}
grp = &AG.main_group;
__InitGroup(grp);
subtrack = &AG.main_sub;
subtrack->group = grp;
subtrack->enabled = FALSE;
for (i = 0; i < ARRAY_COUNT(subtrack->note_layers); i++) {
subtrack->note_layers[i] = nullptr;
}
Nas_InitSubTrack(&AG.main_sub);
grp->subtracks[0] = &AG.main_sub;
grp->seq_id = 0xFF;
grp->bank_id = 0xFF;
grp->seq_data = BDB_SEQDATA;
grp->macro_player.pc = BDB_SEQDATA;
grp->macro_player.depth = 0;
grp->delay = 0;
grp->flags.enabled = TRUE;
grp->flags.finished = FALSE;
grp->skip_ticks = 0;
}