diff --git a/config/disasm_overrides.yml b/config/disasm_overrides.yml index 525ded38..a2b85821 100644 --- a/config/disasm_overrides.yml +++ b/config/disasm_overrides.yml @@ -9,6 +9,7 @@ symbol_aligns: 0x80019760: 32 0x8001a0c0: 32 0x80026400: 32 + 0x8002c920: 32 # __DSPCheckMXICBoot2__FP16STRUCT_DSP_TASK2 0x80031d80: 32 0x80207458: 8 # align RunQueue to 0x001251d8 0x800b9140: 32 # align gam_win_moji1_tex to 32 bytes @@ -16,4 +17,5 @@ symbol_aligns: 0x800daaa0: 32 # align texture_cache_data_func to 32 bytes 0x80206f30: 16 # malloc.c align 16 bytes 0x800ab260: 32 - 0x801864b0: 8 # dspbuf align 8 (not sure why this doesn't happen) + 0x800d3b00: 32 # dspboot data section has alignment to 32 + 0x801864b0: 8 # dspbuf align 8 (not sure why this doesn't happen) \ No newline at end of file diff --git a/config/dol_slices.yml b/config/dol_slices.yml index b19a5dd6..e7a78d46 100644 --- a/config/dol_slices.yml +++ b/config/dol_slices.yml @@ -111,6 +111,12 @@ jaudio_NES/internal/os.c: .text: [0x80026120, 0x80026300] jaudio_NES/internal/astest.c: .text: [0x80026300, 0x80026400] +jaudio_NES/internal/track.c: + .text: [0x80028e60, 0x8002c920] + .rodata: [0x800aa720, 0x800aa780] + .data: [0x800d36f0, 0x800d3b00] + .sbss: [0x80218540, 0x80218548] + .sdata2: [0x80218ec8, 0x80218f08] jaudio_NES/internal/random.c: .text: [0x80031ce0, 0x80031d80] .sdata: [0x80217c38, 0x80217c40] diff --git a/include/jaudio_NES/audiocommon.h b/include/jaudio_NES/audiocommon.h index d1e688da..4012025c 100644 --- a/include/jaudio_NES/audiocommon.h +++ b/include/jaudio_NES/audiocommon.h @@ -15,13 +15,16 @@ extern "C" { #define AUDIO_GROUP_MAX 5 #define AUDIO_SUBTRACK_NUM 16 +#define AUDIO_NOTE_MAX 128 #define AUDIO_TATUMS_PER_BEAT 48 +#define AUDIO_GROUP_ALL_SUBTRACKS (0xFFFF) + #define AUDIO_MUTE_FLAG_STOP_SAMPLES (1 << 3) -#define AUDIO_MUTE_FLAG_STOP_NOTE (1 << 4) +#define AUDIO_MUTE_FLAG_STOP_SUBTRACK (1 << 4) #define AUDIO_MUTE_FLAG_SOFTEN (1 << 5) -#define AUDIO_MUTE_FLAG_6 (1 << 6) +#define AUDIO_MUTE_FLAG_STOP_NOTE (1 << 6) #define AUDIO_MUTE_FLAG_STOP_SCRIPT (1 << 7) #define NA_MAKE_COMMAND(a0, a1, a2, a3) \ @@ -41,6 +44,13 @@ typedef enum SampleMedium { /* 5 */ MEDIUM_RAM_UNLOADED = 5 } SampleMedium; +typedef enum AudioCacheType { + /* 0 */ CACHE_TEMPORARY, + /* 1 */ CACHE_PERSISTENT, + /* 2 */ CACHE_EITHER, + /* 3 */ CACHE_PERMANENT +} AudioCacheType; + typedef enum AudioCacheLoadType { /* 0 */ CACHE_LOAD_PERMANENT, /* 1 */ CACHE_LOAD_PERSISTENT, @@ -49,6 +59,15 @@ typedef enum AudioCacheLoadType { /* 4 */ CACHE_LOAD_EITHER_NOSYNC } AudioCacheLoadType; +typedef enum AudioLoadStatus { + /* 0 */ LOAD_STATUS_NOT_LOADED, + /* 1 */ LOAD_STATUS_IN_PROGRESS, + /* 2 */ LOAD_STATUS_COMPLETE, + /* 3 */ LOAD_STATUS_DISCARDABLE, + /* 4 */ LOAD_STATUS_MAYBE_DISCARDABLE, + /* 5 */ LOAD_STATUS_PERMANENT +} AudioLoadStatus; + typedef enum AdsrStatus { /* 0 */ ADSR_STATUS_DISABLED, /* 1 */ ADSR_STATUS_INITIAL, @@ -61,6 +80,31 @@ typedef enum AdsrStatus { /* 8 */ ADSR_STATUS_SUSTAIN } AdsrStatus; +typedef enum PortamentoMode { + /* 0 */ PORTAMENTO_MODE_OFF, + /* 1 */ PORTAMENTO_MODE_1, + /* 2 */ PORTAMENTO_MODE_2, + /* 3 */ PORTAMENTO_MODE_3, + /* 4 */ PORTAMENTO_MODE_4, + /* 5 */ PORTAMENTO_MODE_5, + /* 6 */ PORTAMENTO_MODE_6, + /* 7 */ PORTAMENTO_MODE_7, + /* 8 */ PORTAMENTO_MODE_8, // might be PORTAMENTO_MODE_NUM +} PortamentoMode; + +typedef enum SampleBankTableType { + /* 0 */ SEQUENCE_TABLE, + /* 1 */ BANK_TABLE, + /* 2 */ WAVE_TABLE +} SampleBankTableType; + +#define VOICE_TYPE_PERCUSSION 0 +#define VOICE_TYPE_SOUND_EFF 1 +#define VOICE_TYPE_INSTRUMENT_START 2 + +#define AUDIO_SWEEP_IS_SPECIAL(s) ((s).mode & 0x80) +#define AUDIO_SWEEP_MODE(s) ((s).mode & ~0x80) + #define AUDIO_NOTE_RELEASE (1 << 0) #define AUDIO_NOTE_SAMPLE_NOTES (1 << 1) @@ -144,6 +188,23 @@ typedef enum AudioCmd { /* 0xFF */ AUDIOCMD_MAIN_GROUP_SET_VOL_SCALE } AudioCmd; +typedef enum SeqCmd { + SEQCMD_BRANCH_REL_NOT_EQ_ZERO = 0xF2, + SEQCMD_BRANCH_REL_EQ_ZERO = 0xF3, + SEQCMD_BRANCH_REL = 0xF4, + SEQCMD_BRANCH_ABS_GREQ_ZERO = 0xF5, + SEQCMD_BREAK = 0xF6, + SEQCMD_LOOP_END = 0xF7, + SEQCMD_LOOP = 0xF8, + SEQCMD_BRANCH_ABS_NOT_EQ_ZERO = 0xF9, + SEQCMD_BRANCH_ABS_EQ_ZERO = 0xFA, + SEQCMD_BRANCH_ABS = 0xFB, + SEQCMD_CALL = 0xFC, + SEQCMD_DELAY_N_FRAMES = 0xFD, + SEQCMD_DELAY_1_FRAME = 0xFE, + SEQCMD_STOP_SCRIPT = 0xFF, +} SeqCmd; + typedef enum AUDIO_CALLBACKS { /* 0x00 */ AUDIO_CALLBACK_SEQ_0, /* 0x01 */ AUDIO_CALLBACK_SEQ_1, diff --git a/include/jaudio_NES/audiostruct.h b/include/jaudio_NES/audiostruct.h index cc545b4e..96000601 100644 --- a/include/jaudio_NES/audiostruct.h +++ b/include/jaudio_NES/audiostruct.h @@ -26,10 +26,10 @@ struct link_ { /* sizeof(struct chnode_) == 0x40 */ struct chnode_ { - /* 0x00 */ link globalUse; - /* 0x10 */ link globalFree; - /* 0x20 */ link globalRelease; - /* 0x30 */ link globalReleaseWait; + /* 0x00 */ link freeList; + /* 0x10 */ link releaseList; + /* 0x20 */ link relwaitList; + /* 0x30 */ link useList; }; /* forward declared */ @@ -301,9 +301,9 @@ typedef struct playbackch_ { /* 0x06 */ s16 adsr_volume_scale_unused; /* 0x08 */ f32 portamento_frequency_scale; /* 0x0C */ f32 vibrato_frequency_scale; - /* 0x10 */ note* wanted_parent_note; + /* 0x10 */ note* previous_parent_note; /* 0x14 */ note* current_parent_note; - /* 0x18 */ note* previous_parent_note; + /* 0x18 */ note* wanted_parent_note; /* 0x1C */ playbackparams params; /* 0x34 */ envp adsr_envp; /* 0x54 */ sweep portamento_sweep; @@ -434,14 +434,14 @@ typedef struct delay_ { /* 0x2A0 */ adpcmloop adpcm_loop; } delay; -/* sizeof(seqplayer) == 0x1C */ -typedef struct seqplayer_ { +/* sizeof(macro) == 0x1C */ +typedef struct macro_ { /* 0x00 */ u8* pc; /* 0x04 */ u8* stack[4]; /* 0x14 */ u8 remaining_loop_iters[4]; /* 0x18 */ u8 depth; /* 0x19 */ s8 value; -} seqplayer; +} macro; /* SubTrack struct */ /* sizeof(sub) == 0xE0 */ @@ -469,7 +469,7 @@ typedef struct sub_ { /* 0x04 */ u8 target_reverb_vol; /* 0x05 */ u8 note_priority; /* 0x06 */ u8 priority2; - /* 0x07 */ u8 inst_id; + /* 0x07 */ u8 bank_id; /* 0x08 */ u8 reverb_idx; /* 0x09 */ u8 book_ofs; /* 0x0A */ u8 new_pan; @@ -479,12 +479,12 @@ typedef struct sub_ { /* 0x0E */ u8 gate_time_random_variance; /* 0x0F */ u8 comb_filter_size; /* 0x10 */ u8 surround_effect_idx; - /* 0x11 */ u8 channel_idx; - /* 0x12 */ u16 _12; + /* 0x11 */ u8 subtrack_idx; + /* 0x12 */ u8 note_semitone; /* 0x14 */ vibparam vibrato_params; /* 0x22 */ u16 delay; /* 0x24 */ u16 comb_filter_gain; - /* 0x26 */ u16 _26; + /* 0x26 */ u16 dynamic_value; /* 0x28 */ s16 inst_or_wave; /* 0x2A */ s16 transposition; /* 0x2C */ f32 volume_scale; @@ -498,7 +498,7 @@ typedef struct sub_ { /* 0x4C */ voicetable* voicetable; /* 0x50 */ group* group; /* 0x54 */ struct note_* note_layers[4]; - /* 0x64 */ seqplayer seq_player; + /* 0x64 */ macro macro_player; /* 0x80 */ env adsr_env; /* 0x88 */ chnode channel_node; /* 0xC8 */ s8 port[8]; @@ -506,7 +506,8 @@ typedef struct sub_ { /* 0xD4 */ s16* filter; /* 0xD8 */ phase stereo_phase; /* 0xDC */ s32 sample_start_pos; - /* 0xE0 */ u8 _unk[0x100 - 0x0E0]; + /* 0xE0 */ s32 _E0; + /* 0xE4 */ u8 _unk[0x100 - 0x0E4]; } sub; /* sizeof(group) == 0x160 */ @@ -526,7 +527,7 @@ struct group_ { /* 0x002 */ u8 note_alloc_policy; /* 0x003 */ u8 mute_flags; /* 0x004 */ u8 seq_id; - /* 0x005 */ u8 default_bank; + /* 0x005 */ u8 bank_id; /* 0x006 */ u8 unk006; /* 0x007 */ s8 group_idx; /* 0x008 */ u16 tempo; @@ -546,7 +547,7 @@ struct group_ { /* 0x030 */ f32 applied_fade_volume; /* 0x034 */ f32 bend; /* 0x038 */ sub* subtracks[AUDIO_SUBTRACK_NUM]; - /* 0x078 */ seqplayer seq_player; + /* 0x078 */ macro macro_player; /* 0x094 */ u8* short_note_velocity_tbl; /* 0x098 */ u8* short_note_gate_time_tbl; /* 0x09C */ chnode channel_node; @@ -576,24 +577,27 @@ struct note_ { /* 0x08 */ u8 surround_effect_idx; /* 0x09 */ u8 target_reverb_volume; - struct { - /* 0x0A */ u16 bit0 : 1; - /* 0x0A */ u16 bit1 : 1; - /* 0x0A */ u16 bit2 : 1; - /* 0x0A */ u16 use_vibrato : 1; - /* 0x0A */ u16 bit4 : 1; - /* 0x0A */ u16 bit5 : 1; - /* 0x0A */ u16 bit6 : 1; - /* 0x0A */ u16 bit7 : 1; - /* 0x0B */ u16 bit8 : 1; - /* 0x0B */ u16 bit9 : 1; - /* 0x0B */ u16 bitA : 1; - /* 0x0B */ u16 bitB : 1; - /* 0x0B */ u16 bitC : 1; - /* 0x0B */ u16 bitD : 1; - /* 0x0B */ u16 bitE : 1; - /* 0x0B */ u16 bitF : 1; - }; + union { + struct { + /* 0x0A */ u16 bit0 : 1; + /* 0x0A */ u16 bit1 : 1; + /* 0x0A */ u16 bit2 : 1; + /* 0x0A */ u16 use_vibrato : 1; + /* 0x0A */ u16 add_subtrack_transposition : 1; + /* 0x0A */ u16 bit5 : 1; + /* 0x0A */ u16 bit6 : 1; + /* 0x0A */ u16 bit7 : 1; + /* 0x0B */ u16 bit8 : 1; + /* 0x0B */ u16 bit9 : 1; + /* 0x0B */ u16 bitA : 1; + /* 0x0B */ u16 bitB : 1; + /* 0x0B */ u16 bitC : 1; + /* 0x0B */ u16 bitD : 1; + /* 0x0B */ u16 bitE : 1; + /* 0x0B */ u16 bitF : 1; + } flags; + /* 0x0A */ u16 asU16; + } _0A; /* 0x0C */ vibparam vibrato_params; /* 0x1A */ s16 delay; @@ -615,7 +619,7 @@ struct note_ { /* 0x58 */ voicetable* instrument; /* 0x5C */ wtstr* tuned_sample; /* 0x60 */ sub* sub_track; - /* 0x64 */ seqplayer seq_player; + /* 0x64 */ macro macro_player; /* 0x80 */ link link; }; @@ -883,10 +887,10 @@ typedef struct AudioGlobals { /* 0x3780 */ size_t audio_heap_size; /* 0x3784 */ channel* channels; /* 0x3788 */ struct group_ groups[AUDIO_GROUP_MAX]; - /* 0x3E68 */ note notes[128]; + /* 0x3E68 */ note notes[AUDIO_NOTE_MAX]; /* 0x8668 */ sub null_sub_track; /* used for 'null' sub tracks */ /* 0x8748 */ group* groups_p[AUDIO_GROUP_MAX]; - /* 0x877C */ s32 _877C; + /* 0x877C */ s32 sample_state_offset; /* 0x8780 */ link note_link; /* 0x8790 */ chnode channel_node; /* main chnode */ /* 0x87D0 */ struct group_ main_group; diff --git a/include/jaudio_NES/audiotable.h b/include/jaudio_NES/audiotable.h index 6928fc0a..855d8f54 100644 --- a/include/jaudio_NES/audiotable.h +++ b/include/jaudio_NES/audiotable.h @@ -2,8 +2,15 @@ #define AUDIOTABLE_H #include "types.h" +#include "jaudio_NES/audiostruct.h" extern f32 PCENTTABLE[]; extern f32 PCENTTABLE2[]; +extern envdat DEFAULT_ENV[]; +extern f32 PITCHTABLE[]; +extern u8 DEFAULT_VTABLE[]; +extern u8 DEFAULT_GTABLE[]; + +extern u8 BDB_SEQDATA[]; #endif diff --git a/include/jaudio_NES/audiowork.h b/include/jaudio_NES/audiowork.h index 0b8adff3..4dc14f8f 100644 --- a/include/jaudio_NES/audiowork.h +++ b/include/jaudio_NES/audiowork.h @@ -11,13 +11,13 @@ extern "C" { #endif typedef void (*VFRAME_CALLBACK)(void); -typedef u32 (*GRP_CALLBACK)(s8, sub*); +typedef u32 (*GRP_CALLBACK)(s8, group*); typedef void* (*SOUND_CALLBACK)(smzwavetable*, s32, s8, s32); typedef Acmd* (*DACOUT_CALLBACK)(Acmd*, s32, s32); extern AudioGlobals AG; extern VFRAME_CALLBACK NA_VFRAME_CALLBACK; -// extern ??? NA_CALLBACK; +extern SequenceCallback NA_CALLBACK; extern GRP_CALLBACK NA_GRP_CALLBACK; extern SOUND_CALLBACK NA_SOUND_CALLBACK; extern DACOUT_CALLBACK NA_DACOUT_CALLBACK; diff --git a/include/jaudio_NES/channel.h b/include/jaudio_NES/channel.h index a808e496..74610bfb 100644 --- a/include/jaudio_NES/channel.h +++ b/include/jaudio_NES/channel.h @@ -2,7 +2,22 @@ #define CHANNEL_H #include "types.h" +#include "jaudio_NES/audiowork.h" extern s32 OverwriteBank(s32 type, s32 bankId, s32 idx, s32 voicetable); +extern channel* Nas_AllocationOnRequest(note* n); + +extern wtstr* NoteToVoice(voicetable* voicetbl, s32 note); +extern voicetable* ProgToVp(s32 prog, s32 note); +extern percvoicetable* PercToPp(s32 perc, s32 note); +extern veffvoicetable* VpercToVep(s32 vperc, s32 note); + +extern void Nas_UpdateChannel(void); + +extern void Nas_DeAllocAllVoices(chnode* node); +extern void Nas_AllocVoices(chnode* node, s32 num); +extern void Nas_InitChNode(chnode* node); +extern void Nas_Release_Channel(note* n); +extern void Nas_Release_Channel_Force(note* n); #endif diff --git a/include/jaudio_NES/effect.h b/include/jaudio_NES/effect.h new file mode 100644 index 00000000..df419d8d --- /dev/null +++ b/include/jaudio_NES/effect.h @@ -0,0 +1,12 @@ +#ifndef EFFECT_H +#define EFFECT_H + +#include "types.h" +#include "jaudio_NES/audiowork.h" + +extern void Nas_ChannelModInit(channel* ch); +extern void Nas_SweepInit(channel* ch); + +extern void Nas_MainCtrl(group* grp); + +#endif diff --git a/include/jaudio_NES/memory.h b/include/jaudio_NES/memory.h index c853ba66..10312c7b 100644 --- a/include/jaudio_NES/memory.h +++ b/include/jaudio_NES/memory.h @@ -6,6 +6,9 @@ extern void Nas_HeapInit(ALHeap* heap, u8* base, s32 len); extern void* Nas_HeapAlloc(ALHeap* heap, s32 size); +extern void* Nas_HeapAlloc_CL(ALHeap*, s32 size); + +extern u32 Nas_SzCacheCheck(s32 type, s32 cache_type, s32 id); extern void Nas_SzStayDelete(s32 type); extern void Nas_SetDelayLineParam(s32 delayIdx, s32 param_type, s32 param_value, s32 init); diff --git a/include/jaudio_NES/system.h b/include/jaudio_NES/system.h index 37508029..552ff26f 100644 --- a/include/jaudio_NES/system.h +++ b/include/jaudio_NES/system.h @@ -32,6 +32,16 @@ extern void Nas_WaveDmaFrameWork(void); extern void Nas_BgDmaFrameWork(s32 reset_status); extern void MK_FrameWork(void); +extern BOOL Nas_CheckIDseq(s32 id); +extern BOOL Nas_CheckIDbank(s32 id); + +extern void Nas_WriteIDseq(s32 id, s32 type); +extern void Nas_WriteIDbank(s32 id, s32 type); + +extern s32 VoiceLoad(s32 bank_id, u32 inst_id, s8* done_p); +extern s32 SeqLoad(s32 seq_id, u8* ram_addr, s8* done_p); +extern void MK_load(s32 type, s32 id, u8* done_p); + extern BOOL AUDIO_SYSTEM_READY; #endif diff --git a/include/jaudio_NES/track.h b/include/jaudio_NES/track.h index 88efcd3e..94811dd5 100644 --- a/include/jaudio_NES/track.h +++ b/include/jaudio_NES/track.h @@ -4,7 +4,16 @@ #include "types.h" #include "jaudio_NES/audiowork.h" -extern void Nas_SeqSkip(group* group); -extern void Nas_ReleaseGroup_Force(group* group); +extern void Nas_ReleaseNoteTrack(note* note); +extern void Nas_ReleaseSubTrack(sub* subtrack); +extern void Nas_ReleaseGroup_Force(group* grp); +extern void Nas_ReleaseGroup(group* grp); +extern void Nas_AddList(link* root, link* list); +extern void* Nas_GetList(link* root); +extern void Nas_MySeqMain(u32 frames_left); +extern void Nas_SeqSkip(group* grp); +extern void Nas_InitMySeq(group* grp); +extern void Nas_AssignSubTrack(s32 group_idx); +extern void Nas_InitPlayer(void); #endif diff --git a/src/static/jaudio_NES/internal/track.c b/src/static/jaudio_NES/internal/track.c new file mode 100644 index 00000000..ab3afac0 --- /dev/null +++ b/src/static/jaudio_NES/internal/track.c @@ -0,0 +1,2252 @@ +#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 SEQCMD_STOP_SCRIPT: + if (macro->depth == 0) { + return COMMON_SCRIPT_END; + } + + macro->pc = macro->stack[--macro->depth]; + break; + case SEQCMD_DELAY_N_FRAMES: + return Nas_ReadLengthData(macro); + case SEQCMD_DELAY_1_FRAME: + return 1; + case SEQCMD_CALL: + macro->stack[macro->depth++] = macro->pc; + macro->pc = group->seq_data + (arg & 0xFFFF); + break; + case SEQCMD_LOOP: + macro->remaining_loop_iters[macro->depth] = arg & 0xFF; + macro->stack[macro->depth++] = macro->pc; + break; + case SEQCMD_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 SEQCMD_BREAK: + macro->depth--; + break; + case SEQCMD_BRANCH_ABS_GREQ_ZERO: + case SEQCMD_BRANCH_ABS_NOT_EQ_ZERO: + case SEQCMD_BRANCH_ABS_EQ_ZERO: + case SEQCMD_BRANCH_ABS: + abs = arg & 0xFFFF; + if (cmd == SEQCMD_BRANCH_ABS_EQ_ZERO && macro->value != 0) { + break; + } else if (cmd == SEQCMD_BRANCH_ABS_NOT_EQ_ZERO && macro->value >= 0) { + break; + } else if (cmd == SEQCMD_BRANCH_ABS_GREQ_ZERO && macro->value < 0) { + break; + } else { + macro->pc = group->seq_data + abs; + } + break; + case SEQCMD_BRANCH_REL_NOT_EQ_ZERO: + case SEQCMD_BRANCH_REL_EQ_ZERO: + case SEQCMD_BRANCH_REL: + rel = (s8)(arg & 0xFF); + if (cmd == SEQCMD_BRANCH_REL_EQ_ZERO && macro->value != 0) { + break; + } else if (cmd == SEQCMD_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; + subtrack->stereo_phase.asU8 = 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->_00bit3 = FALSE; + entry_note->ignore_drum_pan = FALSE; + entry_note->_00bit1 = FALSE; + entry_note->note_properties_need_init = FALSE; + entry_note->stereo_phase.asU8 = 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->next != 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->next->prev = list; + list->next = root->next; + list->prev = root; + root->next = list; + root->numAfter++; + list->pNode = root->pNode; + } +} + +extern void* Nas_GetList(link* root) { + link* list = root->next; + + if (list == root) { + return nullptr; + } + + list->next->prev = root; + root->next = list->next; + list->next = nullptr; + root->numAfter--; + return list->pData; +} + +static void Nas_InitNoteList(void) { + s32 i; + + AG.note_link.next = &AG.note_link; + AG.note_link.prev = &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.next = 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 == -1 && n->delay == 0); + + if (cmd != -1) { + cmd = __SetVoice(n, cmd); + } + + if (cmd != -1) { + __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->_00bit1 == TRUE) { + return 0; + } + + if (n->continuous != TRUE || n->channel == nullptr || n->_00bit3 == 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 <= 0xC0) { + return cmd; + } + + if (cmd >= SEQCMD_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 0xC1: + case 0xCA: + cmdArgU8 = *m->pc++; + if (cmd == 0xC1) { + n->velocity_square = (f32)(cmdArgU8 * cmdArgU8) / (f32)(127 * 127); + } else { + n->pan = cmdArgU8; + } + break; + case 0xC2: + case 0xC9: + cmdArgU8 = *m->pc++; + if (cmd == 0xC9) { + n->gate_time = cmdArgU8; + } else { + n->transposition = cmdArgU8; + } + break; + case 0xC4: + case 0xC5: + n->continuous = cmd == 0xC4; + n->_00bit1 = FALSE; + Nas_Release_Channel(n); + break; + case 0xC3: + n->short_note_default_delay = Nas_ReadLengthData(m); + break; + case 0xC6: + cmdArgU8 = Nas_ReadByteData(m); + if (cmdArgU8 >= 0x7E) { + if (cmdArgU8 == 0x7E) { + // effect/sfx + n->inst_or_wave = 1; + } else if (cmdArgU8 == 0x7F) { + // percussion/drum + n->inst_or_wave = 0; + } 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 0xC7: + 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 0xC8: + n->portamento_sweep.mode = PORTAMENTO_MODE_OFF; + break; + case 0xCB: + n->adsr_env.envelope = (envdat*)(grp->seq_data + (Nas_ReadWordData(m) & 0xFFFF)); + // fallthrough 0xCB -> 0xCF + case 0xCF: + n->adsr_env.decay_idx = Nas_ReadByteData(m); + break; + case 0xCC: + n->ignore_drum_pan = TRUE; + break; + case 0xCD: + n->stereo_phase.asU8 = Nas_ReadByteData(m); + break; + case 0xCE: + cmdArgU8 = 128 + Nas_ReadByteData(m); + n->bend = PCENTTABLE2[cmdArgU8]; + break; + case 0xF0: + cmdArgU16 = Nas_ReadWordData(m); + n->_0A.asU16 &= cmdArgU16 ^ 0xFFFF; + break; + case 0xF1: + n->surround_effect_idx = Nas_ReadByteData(m); + break; + default: + switch (cmd & 0xF0) { + case 0xD0: + velocity = grp->short_note_velocity_tbl[cmd & 0xF]; + n->velocity_square = (f32)(velocity * velocity) / (f32)(127 * 127); + break; + case 0xE0: + n->gate_time = grp->short_note_gate_time_tbl[cmd & 0xF]; + break; + } + } + } +} + +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; + percvoicetable* percussion; + veffvoicetable* 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._2894; + 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->_00bit1 = 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) { + 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 >= 0xF2) { + 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 0xEA: // stop subtrack seq script + subtrack->stop_script = TRUE; + goto note_seq; + case 0xF1: // alloc voices + Nas_DeAllocAllVoices(&subtrack->channel_node); + Nas_AllocVoices(&subtrack->channel_node, cmdArgs[0] & 0xFF); + break; + case 0xF0: // dealloc voices + Nas_DeAllocAllVoices(&subtrack->channel_node); + break; + case 0xC2: // set dynamic table + subtrack->dyn_tbl = (unsigned char(*)[][2]) & grp->seq_data[(u16)cmdArgs[0]]; + break; + case 0xC5: // 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 0xEB: // Load subtrack bank and set instruments + cmdArgU8 = cmdArgs[0]; + if (grp->bank_id != 0xFF) { + cmdArgU16 = AG.map_header[grp->seq_id]; + lo_bits = ((u8*)AG.map_header)[cmdArgU16]; + 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 0xC1: // Set subtrack voice + Nas_SubVoiceSet(subtrack, cmdArgs[0]); + break; + case 0xC3: // turn off large notes + subtrack->large_notes = FALSE; + break; + case 0xC4: // turn on large notes + subtrack->large_notes = TRUE; + break; + case 0xDF: // set volume + cmdArgU8 = (u8)cmdArgs[0]; + Nas_SubVolumeSet(subtrack, cmdArgU8); + subtrack->changes.flags.volume = TRUE; + break; + case 0xE0: // set volume scale + cmdArgU8 = (u8)cmdArgs[0]; + subtrack->volume_scale = (f32)(s32)cmdArgU8 / 128.0f; + subtrack->changes.flags.volume = TRUE; + break; + case 0xDE: // set frequency scale + cmdArgU16 = (u16)cmdArgs[0]; + subtrack->frequency_scale = (f32)(s32)cmdArgU16 / (f32)0x8000; + subtrack->changes.flags.frequency_scale = TRUE; + break; + case 0xD3: // large bend pitch + cmdArgU8 = (u8)cmdArgs[0]; + cmdArgU8 += 128; + subtrack->frequency_scale = PCENTTABLE[cmdArgU8]; + subtrack->changes.flags.frequency_scale = TRUE; + break; + case 0xEE: // small bend pitch + cmdArgU8 = 128 + (u8)cmdArgs[0]; + subtrack->frequency_scale = PCENTTABLE2[cmdArgU8]; + subtrack->changes.flags.frequency_scale = TRUE; + break; + case 0xDD: // set pan + cmdArgU8 = (u8)cmdArgs[0]; + subtrack->new_pan = cmdArgU8; + subtrack->changes.flags.pan = TRUE; + break; + case 0xDC: // set pan mix/channel weight + cmdArgU8 = (u8)cmdArgs[0]; + subtrack->pan_channel_weight = cmdArgU8; + subtrack->changes.flags.pan = TRUE; + break; + case 0xDB: // set transposition + cmdArgS8 = (s8)cmdArgs[0]; + subtrack->transposition = cmdArgS8; + break; + case 0xDA: // set envelope + cmdArgU16 = (u16)cmdArgs[0]; + subtrack->adsr_env.envelope = (envdat*)&grp->seq_data[cmdArgU16]; + break; + case 0xD9: // set decay index + cmdArgU8 = (u8)cmdArgs[0]; + subtrack->adsr_env.decay_idx = cmdArgU8; + break; + case 0xD8: // 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 0xD7: // 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 0xE2: // 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 0xE1: // 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 0xE3: // set vibrato delay + cmdArgU8 = (u8)cmdArgs[0]; + subtrack->vibrato_params.delay = cmdArgU8 * 16; + break; + case 0xD4: // set reverb volume + cmdArgU8 = (u8)cmdArgs[0]; + subtrack->target_reverb_vol = cmdArgU8; + break; + case 0xC6: // set bank + cmdArgU8 = cmdArgs[0]; + if (grp->bank_id != 0xFF) { + cmdArgU16 = AG.map_header[grp->seq_id]; + lo_bits = ((u8*)AG.map_header)[cmdArgU16]; + cmdArgU8 = ((u8*)AG.map_header)[cmdArgU16 + lo_bits - cmdArgU8]; + } + + if (Nas_SzCacheCheck(BANK_TABLE, CACHE_EITHER, cmdArgU8)) { + subtrack->bank_id = cmdArgU8; + } + break; + case 0xC7: // 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 0xC8: // subtract macro value + case 0xCC: // set macro value + case 0xC9: // bit& macro value + cmdArgS8 = (s8)cmdArgs[0]; + if (cmd == 0xC8) { + m->value -= cmdArgS8; + } else if (cmd == 0xCC) { + m->value = cmdArgS8; + } else { + m->value &= cmdArgS8; + } + break; + case 0xCD: // disable subtrack + cmdArgU8 = (u8)cmdArgs[0]; + Nas_ReleaseSubTrack(grp->subtracks[cmdArgU8]); + break; + case 0xCA: // set mute flags + cmdArgU8 = (u8)cmdArgs[0]; + subtrack->mute_flags = cmdArgU8; + subtrack->changes.flags.volume = TRUE; + break; + case 0xCB: // load macro register value from table + cmdArgU16 = (u16)cmdArgs[0]; + m->value = grp->seq_data[(u32)(cmdArgU16 + m->value)]; + break; + case 0xCE: // set stored sequence data index + cmdArgU16 = (u16)cmdArgs[0]; + subtrack->dynamic_value = cmdArgU16; + break; + case 0xCF: // 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 0xD0: // stereo phase effects + cmdArgU8 = (u8)cmdArgs[0]; + if (cmdArgU8 & 0x80) { + subtrack->stereo_effects = TRUE; + } else { + subtrack->stereo_effects = FALSE; + } + + subtrack->stereo_phase.asU8 = cmdArgU8 & 0x7F; + break; + case 0xD1: // set note allocation policy + cmdArgU8 = (u8)cmdArgs[0]; + subtrack->note_alloc_policy = cmdArgU8; + break; + case 0xD2: // set sustain + cmdArgU8 = (u8)cmdArgs[0]; + subtrack->adsr_env.sustain = cmdArgU8; + break; + case 0xE5: // set reverb index + cmdArgU8 = (u8)cmdArgs[0]; + subtrack->reverb_idx = cmdArgU8; + break; + case 0xE4: // 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 0xE6: // set book ofs + cmdArgU8 = (u8)cmdArgs[0]; + subtrack->book_ofs = cmdArgU8; + break; + case 0xE7: // + 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 0xE8: // + 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 0xEC: // 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 0xE9: // set priority + cmdArgU8 = (u8)cmdArgs[0]; + Nas_PriorityChanger(subtrack, cmdArgU8); + break; + case 0xED: // set gain + cmdArgU8 = (u8)cmdArgs[0]; + subtrack->gain = cmdArgU8; + break; + case 0xB0: // set filter + cmdArgU16 = (u16)cmdArgs[0]; + data = &grp->seq_data[cmdArgU16]; + subtrack->filter = (s16*)data; + break; + case 0xB1: // clear filter + subtrack->filter = nullptr; + break; + case 0xB3: // 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 0xB2: // load dynamic idx + cmdArgU16 = (u16)cmdArgs[0]; + subtrack->dynamic_value = *(u16*)&grp->seq_data[cmdArgU16 + m->value * 2]; + break; + case 0xB4: // set dynamic table + subtrack->dyn_tbl = (unsigned char(*)[][2]) & grp->seq_data[subtrack->dynamic_value]; + break; + case 0xB5: // read from dynamic table + subtrack->dynamic_value = ((u16*)subtrack->dyn_tbl)[m->value]; + break; + case 0xB6: // read to macro register from dynamic table + m->value = (*subtrack->dyn_tbl)[0][m->value]; + break; + case 0xB7: // random dynamic value + if (cmdArgs[0] == 0) { + subtrack->dynamic_value = Nap_GetRandom(); + } else { + subtrack->dynamic_value = Nap_GetRandom() % cmdArgs[0]; + } + break; + case 0xB8: // macro register random value + if (cmdArgs[0] == 0) { + m->value = Nap_GetRandom(); + } else { + m->value = Nap_GetRandom() % cmdArgs[0]; + } + break; + case 0xB9: // set velocity random variance + cmdArgU8 = (u8)cmdArgs[0]; + subtrack->velocity_random_variance = cmdArgU8; + break; + case 0xBA: // set gate time random variance + cmdArgU8 = (u8)cmdArgs[0]; + subtrack->gate_time_random_variance = cmdArgU8; + break; + case 0xBB: // 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 0xBC: // Add to dynamic value + subtrack->dynamic_value += cmdArgs[0]; + break; + case 0xA8: // 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 0xBD: // set sample start pos + subtrack->sample_start_pos = cmdArgs[0]; + break; + case 0xBE: // 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 0xA0: // read from sfx state using macro register value as index + case 0xA1: // read from sfx state using dynamic value as index + case 0xA2: // write to sfx state using macro register value as index + case 0xA3: // write to sfx state using dynamic value as index + if (cmd == 0xA0 || cmd == 0xA2) { + cmdArgU16 = cmdArgs[0]; + } else { + cmdArgU16 = subtrack->dynamic_value; + } + + if (subtrack->sfx_state != nullptr) { + if (cmd == 0xA0 || cmd == 0xA1) { + m->value = subtrack->sfx_state[cmdArgU16]; + } else { + subtrack->sfx_state[cmdArgU16] = m->value; + } + } + break; + case 0xA4: // set surround effect index + subtrack->surround_effect_idx = cmdArgs[0]; + break; + case 0xA5: // add subtrack index to macro register value + m->value += subtrack->subtrack_idx; + break; + case 0xA6: // 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 0xA7: // 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 >= 0x70) { + 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 != 0x70 && lo_bits >= ARRAY_COUNT(subtrack->note_layers)) { + lo_bits = 0; + } + + switch (hi_bits) { + // [0x80 - 0x83] + case 0x80: // 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 0x88: // 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 - 0x93] + case 0x90: // stop note layer + Nas_CloseNoteTrack(subtrack, lo_bits); + break; + // [0x98 - 0x9B] + case 0x98: // 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 0x70: // write macro register value into subtrack port + subtrack->port[lo_bits] = m->value; + break; + // [0x78 - 0x7B] + case 0x78: // 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 0x00: // 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 0x10: // 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 0x60: // 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 0x50: // subtrack port value from macro register value + m->value -= subtrack->port[lo_bits]; + break; + // [0x20, 0x2F] + case 0x20: // start subtrack + cmdArgU16 = (u16)Nas_ReadWordData(m); + Nas_OpenSub(grp, lo_bits, &grp->seq_data[cmdArgU16]); + break; + // [0x30, 0x3F] + case 0x30: // 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 0x40: // 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->unk016++; + + 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 >= 0xF2) { + 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 0xF1: // reserve voices + Nas_DeAllocAllVoices(&grp->channel_node); + cmd = Nas_ReadByteData(m); + Nas_AllocVoices(&grp->channel_node, (u8)cmd); + break; + case 0xF0: // release voices + Nas_DeAllocAllVoices(&grp->channel_node); + break; + case 0xDF: // absolute transposition + grp->transposition = 0; + // fallthrough 0xDF -> 0xDE + case 0xDE: // relative transposition + argU8 = Nas_ReadByteData(m); + grp->transposition = grp->transposition + (s8)argU8; + break; + case 0xDD: // 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 0xDC: // adjust tempo relative + grp->tempo_change = (s8)Nas_ReadByteData(m) * AUDIO_TATUMS_PER_BEAT; + break; + case 0xDA: // 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 0xDB: // 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 0xD9: // set volume scale + grp->fade_volume_scale = (s8)Nas_ReadByteData(m) / 127.0f; + break; + case 0xD7: // initialize subtracks + temp = Nas_ReadWordData(m); + Nas_AllocSub(grp, temp); + break; + case 0xD6: // [ineffective] - disable subtracks + Nas_ReadWordData(m); + break; + case 0xD5: // set mute scale + grp->mute_volume_scale = (s8)Nas_ReadByteData(m) / 127.0f; + break; + case 0xD4: // mute + grp->flags.muted = TRUE; + break; + case 0xD3: // set mute behaivor + grp->mute_flags = Nas_ReadByteData(m); + break; + case 0xD1: // set short note gate-time table + case 0xD2: // set short note velocity table + temp = Nas_ReadWordData(m); + data3 = &grp->seq_data[temp]; + if (cmd == 0xD2) { + grp->short_note_velocity_tbl = data3; + } else { + grp->short_note_gate_time_tbl = data3; + } + break; + case 0xD0: // set note allocation policy + grp->note_alloc_policy = Nas_ReadByteData(m); + break; + case 0xCE: // 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 0xCD: // 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 0xCC: // set macro register value + m->value = Nas_ReadByteData(m); + break; + case 0xC9: // macro register bit mask + m->value &= Nas_ReadByteData(m); + break; + case 0xC8: // macro register subtraction + m->value -= Nas_ReadByteData(m); + break; + case 0xC7: // 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 0xC2: // 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 0xC6: // stop script + grp->flags.stop_seq_script = TRUE; + return; + case 0xC5: // + grp->unk016 = Nas_ReadWordData(m); + break; + case 0xEF: // + Nas_ReadWordData(m); + Nas_ReadByteData(m); + break; + case 0xC4: // 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 0xC3: // 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 == 0xBE) { + 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 0x00: // 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 0x50: // 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 0x70: // 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 0x80: // 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 0x40: // disable subtrack + Nas_ReleaseSubTrack(grp->subtracks[cmd_lo_bits]); + break; + // [0x90, 0x9F] + case 0x90: // start subtrack w/ absolute script address + temp = Nas_ReadWordData(m); + Nas_OpenSub(grp, cmd_lo_bits, &grp->seq_data[temp]); + break; + // [0xA0, 0xAF] + case 0xA0: // 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 0xB0: // 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 0x60: // 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->unk016 = 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; +}