mirror of
https://github.com/ACreTeam/ac-decomp
synced 2026-08-01 15:59:48 -04:00
2258 lines
94 KiB
C
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, ¬e->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;
|
|
}
|