jaudio_NES: implement & link channel

This commit is contained in:
Cuyler36
2025-06-22 10:40:37 -04:00
parent 6fbc68f77b
commit a10e202517
11 changed files with 1074 additions and 72 deletions
+1 -1
View File
@@ -764,7 +764,7 @@ config.libs = [
Object(Matching, "jaudio_NES/internal/bankdrv.c", extra_cflags=["-pragma \"scheduling 7400\""]),
Object(Matching, "jaudio_NES/internal/bankread.c"),
Object(Matching, "jaudio_NES/internal/centcalc.c"),
Object(NonMatching, "jaudio_NES/internal/channel.c"),
Object(Matching, "jaudio_NES/internal/channel.c"),
Object(Matching, "jaudio_NES/internal/cmdstack.c"),
Object(Matching, "jaudio_NES/internal/connect.c"),
Object(NonMatching, "jaudio_NES/internal/driver.c"),
+9
View File
@@ -138,6 +138,15 @@ typedef enum LpsCacheState {
/* 3 */ LPS_CACHE_STATE_DONE
} LpsCacheState;
typedef enum PhaseType {
PHASE_TYPE_0,
PHASE_TYPE_1,
PHASE_TYPE_2,
PHASE_TYPE_3,
PHASE_TYPE_NUM
} PhaseType;
#define VOICE_TYPE_PERCUSSION 0
#define VOICE_TYPE_SOUND_EFF 1
#define VOICE_TYPE_INSTRUMENT_START 2
+37 -36
View File
@@ -16,8 +16,8 @@ typedef struct link_ link;
/* sizeof(struct link_) == 0x10 */
struct link_ {
/* 0x00 */ link* next;
/* 0x04 */ link* prev;
/* 0x00 */ link* prev;
/* 0x04 */ link* next;
union {
/* 0x08 */ s32 numAfter; /* when link is head */
/* 0x08 */ void* pData; /* when link is node */
@@ -127,17 +127,24 @@ typedef struct wtstr_ {
/* sizeof(phase) == 0x01 */
typedef struct phase_ {
union {
struct {
/* 0x00 */ u8 _unused : 2;
/* 0x00 */ u8 type : 2;
/* 0x00 */ u8 strong_right : 1;
/* 0x00 */ u8 strong_left : 1;
/* 0x00 */ u8 strong_reverb_right : 1;
/* 0x00 */ u8 strong_reverb_left : 1;
};
/* 0x00 */ u8 asU8;
};
// union {
// struct {
// /* 0x00 */ u8 _unused : 2;
// /* 0x00 */ u8 type : 2;
// /* 0x00 */ u8 strong_right : 1;
// /* 0x00 */ u8 strong_left : 1;
// /* 0x00 */ u8 strong_reverb_right : 1;
// /* 0x00 */ u8 strong_reverb_left : 1;
// } flags;
// /* 0x00 */ u8 asU8;
// };
/* 0x00 */ u8 _unused : 2;
/* 0x00 */ u8 type : 2;
/* 0x00 */ u8 strong_right : 1;
/* 0x00 */ u8 strong_left : 1;
/* 0x00 */ u8 strong_reverb_right : 1;
/* 0x00 */ u8 strong_reverb_left : 1;
} phase;
/* sizeof(sweep) == 0x0C */
@@ -218,10 +225,10 @@ typedef struct commonch_ {
/* 0x00 */ u8 strong_right : 1;
/* 0x00 */ u8 strong_left : 1;
/* 0x00 */ u8 strong_reverb_right : 1;
/* 0x00 */ u8 storng_reverb_left : 1;
/* 0x00 */ u8 strong_reverb_left : 1;
/* 0x01 */ u8 reverb_idx : 3;
/* 0x01 */ u8 book_ofs : 2;
/* 0x01 */ u8 __book_ofs : 2;
/* 0x01 */ u8 is_synth_wave : 1;
/* 0x01 */ u8 has_two_parts : 1;
/* 0x01 */ u8 use_haas_effect : 1;
@@ -243,7 +250,7 @@ typedef struct commonch_ {
/* 0x14 */ s16* filter;
/* 0x18 */ u8 _18;
/* 0x19 */ u8 surround_effect_idx;
/* 0x1A */ u8 _1A;
/* 0x1A */ u8 book_ofs;
/* 0x1B */ u8 _1B[4];
} commonch;
@@ -289,7 +296,7 @@ typedef struct playbackparams_ {
/* 0x04 */ phase stereo_phase;
/* 0x05 */ u8 comb_filter_size;
/* 0x06 */ u16 comb_filter_gain;
/* 0x08 */ f32 frequency_scale;
/* 0x08 */ f32 pitch;
/* 0x0C */ f32 velocity;
/* 0x10 */ s16* filter;
/* 0x14 */ s16* filter_buf;
@@ -331,18 +338,10 @@ struct channel_ {
/* sizeof(drvparam) == 0x1C */
typedef struct drvparam_ {
/* 0x00 */ u8 _00;
/* 0x01 */ u8 _01;
/* 0x02 */ u8 _02;
/* 0x03 */ u8 _03;
/* 0x04 */ phase phase;
/* 0x08 */ f32 pitch;
/* 0x0C */ f32 volume;
/* 0x10 */ int _10;
/* 0x14 */ int _14;
/* 0x18 */ u8 _18;
/* 0x00 */ playbackparams playback;
/* 0x18 */ u8 comb_filter_size;
/* 0x19 */ u8 _19;
/* 0x1A */ u16 _1A;
/* 0x1A */ u16 comb_filter_gain;
} drvparam;
/* sizeof(voicetable) == 0x20 */
@@ -357,19 +356,19 @@ typedef struct voicetable_ {
/* 0x18 */ wtstr high_pitch_tuned_sample;
} voicetable;
/* sizeof(percvoicetable) == 0x10 */
typedef struct percvoicetable_ {
/* sizeof(perctable) == 0x10 */
typedef struct perctable_ {
/* 0x00 */ u8 adsr_decay_idx;
/* 0x01 */ u8 pan;
/* 0x02 */ u8 is_relocated;
/* 0x04 */ wtstr tuned_sample;
/* 0x0C */ envdat* envelope;
} percvoicetable;
} perctable;
/* sizeof(veffvoicetable) == 0x08 */
typedef struct veffvoicetable_ {
/* sizeof(percvoicetable) == 0x08 */
typedef struct percvoicetable_ {
/* 0x00 */ wtstr tuned_sample;
} veffvoicetable;
} percvoicetable;
/* sizeof(voiceinfo) == 0x14 */
typedef struct voiceinfo_ {
@@ -379,8 +378,8 @@ typedef struct voiceinfo_ {
/* 0x03 */ u8 wave_bank_id1;
/* 0x04 */ u16 num_sfx;
/* 0x08 */ voicetable** instruments;
/* 0x0C */ percvoicetable** percussion;
/* 0x10 */ veffvoicetable* effects;
/* 0x0C */ perctable** percussion;
/* 0x10 */ percvoicetable* effects;
} voiceinfo;
/* sizeof(delayparam) == 0x1C */
@@ -928,6 +927,8 @@ typedef struct AudioGlobals {
/* 0x92AC */ s32 _92AC;
} AudioGlobals;
#define NA_NO_NOTE ((note*)-1)
// typedef union SOUNDID_ {
// struct {
// u8 wave_id;
+8
View File
@@ -14,4 +14,12 @@ extern u8 DEFAULT_GTABLE[];
extern u8 BDB_SEQDATA[];
extern s16* WAVEMEM_TABLE[];
extern commonch NA_SVCINIT_TABLE[];
extern commonch NA_CHINIT_TABLE[];
extern u16 CDELAYTABLE[];
extern f32 PhoneLeft[];
extern f32 WideLeft[];
extern f32 StereoLeft[];
#endif
+5 -2
View File
@@ -9,8 +9,8 @@ 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 perctable* PercToPp(s32 perc, s32 note);
extern percvoicetable* VpercToVep(s32 vperc, s32 note);
extern void Nas_UpdateChannel(void);
@@ -19,5 +19,8 @@ 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);
extern void Nas_EntryTrack(channel* chan, note* n);
extern void Nas_CutList(link* l);
#endif
+1
View File
@@ -7,6 +7,7 @@
extern void Nas_HeapInit(ALHeap* heap, u8* base, s32 len);
extern void* Nas_HeapAlloc(ALHeap* heap, s32 size);
extern void* Nas_HeapAlloc_CL(ALHeap* heap, s32 size);
extern void* Nas_NcHeapAlloc(ALHeap* heap, s32 size);
extern void* Nas_2ndHeapAlloc(ALHeap* heap, s32 size);
extern void* Nas_Alloc_Single(s32 size, s32 bank_id, u8* wave_addr, s8 medium, s32 cache);
+980
View File
@@ -0,0 +1,980 @@
#include "jaudio_NES/channel.h"
#include "jaudio_NES/system.h"
#include "jaudio_NES/audiostruct.h"
#include "jaudio_NES/audiowork.h"
#include "jaudio_NES/audioconst.h"
#include "jaudio_NES/memory.h"
#include "jaudio_NES/os.h"
#include "jaudio_NES/effect.h"
#include "jaudio_NES/track.h"
#include "jaudio_NES/sub_sys.h"
#include "jaudio_NES/audioheaders.h"
#include <dolphin/os.h>
static void Nas_smzSetPitch(channel* chan, f32 pitch);
static void Nas_AddListHead(link* list, link* l);
static void Nas_smzSetParam(channel* chan, drvparam* param) {
f32 volL;
f32 volR;
s32 strongL;
s32 strongR;
s32 halfPanIdx;
f32 velocity;
u8 pan;
u8 panScale;
u8 reverbVol;
phase stereoPhase;
s32 phaseEffects;
phaseEffects = chan->playback_ch.stereo_headset_effects;
Nas_smzSetPitch(chan, param->playback.pitch);
velocity = param->playback.velocity;
pan = param->playback.pan;
reverbVol = param->playback.target_reverb_volume;
stereoPhase = param->playback.stereo_phase;
panScale = pan & 0x7F;
chan->common_ch.strong_right = FALSE;
chan->common_ch.strong_left = FALSE;
chan->common_ch.strong_reverb_right = stereoPhase.strong_reverb_right;
chan->common_ch.strong_reverb_left = stereoPhase.strong_reverb_left;
if (phaseEffects && AG.sound_mode == SOUND_OUTPUT_HEADSET) {
halfPanIdx = panScale >> 1;
if (halfPanIdx > 0x3F) {
halfPanIdx = 0x3F;
}
chan->common_ch.haas_effect_right_delay_size = CDELAYTABLE[halfPanIdx];
chan->common_ch.haas_effect_left_delay_size = CDELAYTABLE[0x3F - halfPanIdx];
chan->common_ch.use_haas_effect = TRUE;
volL = PhoneLeft[panScale];
volR = PhoneLeft[0x7F - panScale];
} else if (phaseEffects && AG.sound_mode == SOUND_OUTPUT_STEREO) {
strongL = strongR = FALSE;
chan->common_ch.haas_effect_left_delay_size = 0;
chan->common_ch.haas_effect_right_delay_size = 0;
chan->common_ch.use_haas_effect = FALSE;
volL = WideLeft[panScale];
volR = WideLeft[0x7F - panScale];
if (panScale < 0x20) {
strongL = TRUE;
} else if (panScale > 0x60) {
strongR = TRUE;
}
chan->common_ch.strong_right = strongR;
chan->common_ch.strong_left = strongL;
switch (stereoPhase.type) {
case PHASE_TYPE_0:
break;
case PHASE_TYPE_1:
chan->common_ch.strong_right = stereoPhase.strong_right;
chan->common_ch.strong_left = stereoPhase.strong_left;
break;
case PHASE_TYPE_2:
chan->common_ch.strong_right = strongR | stereoPhase.strong_right;
chan->common_ch.strong_left = strongL | stereoPhase.strong_left;
break;
case PHASE_TYPE_3:
chan->common_ch.strong_right = strongR ^ stereoPhase.strong_right;
chan->common_ch.strong_left = strongL ^ stereoPhase.strong_left;
break;
}
} else {
switch (AG.sound_mode) {
case SOUND_OUTPUT_MONO:
chan->common_ch.strong_reverb_right = FALSE;
chan->common_ch.strong_reverb_left = FALSE;
volL = 0.707f;
volR = 0.707f;
break;
default:
chan->common_ch.strong_right = stereoPhase.strong_right;
chan->common_ch.strong_left = stereoPhase.strong_left;
volL = StereoLeft[panScale];
volR = StereoLeft[0x7F - panScale];
break;
}
}
if (velocity < 0.0f) {
velocity = 0.0f;
}
if (velocity > 1.0f) {
velocity = 1.0f;
}
chan->common_ch.target_volume_left = (s32)((velocity * volL) * (0x1000 - 0.001f));
chan->common_ch.target_volume_right = (s32)((velocity * volR) * (0x1000 - 0.001f));
chan->common_ch.gain = param->playback.gain;
chan->common_ch.filter = param->playback.filter_buf;
chan->common_ch.comb_filter_size = param->comb_filter_size;
chan->common_ch.comb_filter_gain = param->comb_filter_gain;
chan->common_ch.target_reverb_volume = reverbVol;
chan->common_ch.surround_effect_idx = param->playback.surround_effect_idx;
}
static void Nas_smzSetPitch(channel* chan, f32 pitch) {
f32 rate = 0.0f;
if (pitch < 2.0f) {
chan->common_ch.has_two_parts = FALSE;
if (pitch > 1.99998f) {
rate = 1.99998f;
} else {
rate = pitch;
}
} else {
chan->common_ch.has_two_parts = TRUE;
if (pitch > 3.99996f) {
rate = 1.99998f;
} else {
rate = pitch / 2.0f;
}
}
chan->common_ch.frequency_fixed_point = (s32)(rate * 32768.0f);
}
extern void Nas_StartVoice(channel* chan) {
if (chan->playback_ch.current_parent_note->adsr_env.decay_idx == 0) {
Nas_EnvInit(&chan->playback_ch.adsr_envp, chan->playback_ch.current_parent_note->sub_track->adsr_env.envelope, &chan->playback_ch.adsr_volume_scale_unused);
} else {
Nas_EnvInit(&chan->playback_ch.adsr_envp, chan->playback_ch.current_parent_note->adsr_env.envelope, &chan->playback_ch.adsr_volume_scale_unused);
}
chan->playback_ch.status = 0;
chan->playback_ch.adsr_envp.state.flags.status = ADSR_STATUS_INITIAL;
chan->common_ch = NA_SVCINIT_TABLE[0];
}
extern void Nas_StopVoice(channel* chan) {
if (chan->common_ch.needs_init == TRUE) {
chan->common_ch.needs_init = FALSE;
}
chan->common_ch.enabled = FALSE;
chan->common_ch.finished = FALSE;
chan->playback_ch.priority = 0;
chan->playback_ch.status = 0;
chan->playback_ch.current_parent_note = NA_NO_NOTE;
chan->playback_ch.previous_parent_note = NA_NO_NOTE;
chan->playback_ch.adsr_envp.state.flags.status = ADSR_STATUS_DISABLED;
chan->playback_ch.adsr_envp.current = 0;
}
// TODO: this needs to be checked to see if we can cleanup the control flow,
// the excessive gotos may be unnecessary
extern void Nas_UpdateChannel(void) {
playbackparams* main_param_p;
channel* chan;
commonch* common_chan;
playbackch* playback_chan;
drvparam param;
u8 bookOfs;
f32 scale;
s32 i;
for (i = 0; i < AG.num_channels; i++) {
chan = &AG.channels[i];
playback_chan = &chan->playback_ch;
if (playback_chan->current_parent_note != NA_NO_NOTE) {
if ((u32)playback_chan->current_parent_note < 0x7FFFFFFF) {
continue;
}
if (chan != playback_chan->current_parent_note->channel && playback_chan->status == 0) {
playback_chan->adsr_envp.state.flags.release = TRUE;
playback_chan->adsr_envp.fadeout_velocity = AG.audio_params.updates_per_frame_inverse;
playback_chan->priority = 1;
playback_chan->status = 2;
goto skip;
} else if (!playback_chan->current_parent_note->enabled && playback_chan->status == 0 && playback_chan->priority != 0) {
// nothing
} else if (playback_chan->current_parent_note->sub_track->group == NULL) {
Nas_ReleaseSubTrack(playback_chan->current_parent_note->sub_track);
playback_chan->priority = 1;
playback_chan->status = 1;
continue;
} else if ((!playback_chan->current_parent_note->sub_track->muted && (!playback_chan->current_parent_note->sub_track->group->flags.muted || (playback_chan->current_parent_note->sub_track->mute_flags & AUDIO_MUTE_FLAG_STOP_NOTE) == 0))) {
goto skip;
}
Nas_Release_Channel_Force(playback_chan->current_parent_note);
Nas_CutList(&chan->link);
Nas_AddListHead(&chan->link.pNode->releaseList, &chan->link);
playback_chan->priority = 1;
playback_chan->status = 2;
goto skip;
} else if (playback_chan->status == 0) {
if (playback_chan->priority != 0) {
continue;
} else {
goto skip;
}
} else {
goto skip;
}
step2:
{
common_chan = &chan->common_ch;
if (playback_chan->status >= 1 || common_chan->finished) {
if (playback_chan->adsr_envp.state.flags.status == ADSR_STATUS_DISABLED || common_chan->finished) {
if (playback_chan->wanted_parent_note != NA_NO_NOTE) {
Nas_StopVoice(chan);
if (playback_chan->wanted_parent_note->sub_track != NULL) {
Nas_EntryTrack(chan, playback_chan->wanted_parent_note);
Nas_ChannelModInit(chan);
Nas_SweepInit(chan);
Nas_CutList(&chan->link);
Nas_AddList(&chan->link.pNode->useList, &chan->link);
playback_chan->wanted_parent_note = NA_NO_NOTE;
} else {
Nas_StopVoice(chan);
Nas_CutList(&chan->link);
Nas_AddList(&chan->link.pNode->freeList, &chan->link);
playback_chan->wanted_parent_note = NA_NO_NOTE;
continue;
}
} else {
if (playback_chan->current_parent_note != NA_NO_NOTE) {
playback_chan->current_parent_note->_00bit1 = TRUE;
}
Nas_StopVoice(chan);
Nas_CutList(&chan->link);
Nas_AddList(&chan->link.pNode->freeList, &chan->link);
continue;
}
}
} else if (playback_chan->adsr_envp.state.flags.status == ADSR_STATUS_DISABLED) {
if (playback_chan->current_parent_note != NA_NO_NOTE) {
playback_chan->current_parent_note->_00bit1 = TRUE;
}
Nas_StopVoice(chan);
Nas_CutList(&chan->link);
Nas_AddList(&chan->link.pNode->freeList, &chan->link);
continue;
}
scale = Nas_EnvProcess(&playback_chan->adsr_envp);
Nas_ChannelModulation(chan);
main_param_p = &playback_chan->params;
switch (playback_chan->status) {
case 1:
case 2:
param.playback.pitch = main_param_p->pitch;
param.playback.velocity = main_param_p->velocity;
param.playback.pan = main_param_p->pan;
param.playback.target_reverb_volume = main_param_p->target_reverb_volume;
param.playback.stereo_phase = main_param_p->stereo_phase;
param.playback.gain = main_param_p->gain;
param.playback.filter_buf = main_param_p->filter;
param.comb_filter_size = main_param_p->comb_filter_size;
param.comb_filter_gain = main_param_p->comb_filter_gain;
param.playback.surround_effect_idx = main_param_p->surround_effect_idx;
bookOfs = common_chan->book_ofs;
break;
default: {
note* n = playback_chan->current_parent_note;
sub* subtrack = n->sub_track;
param.playback.pitch = n->note_frequency_scale;
param.playback.velocity = n->note_velocity;
param.playback.pan = n->note_pan;
if (n->surround_effect_idx == 128) {
param.playback.surround_effect_idx = subtrack->surround_effect_idx;
} else {
param.playback.surround_effect_idx = n->surround_effect_idx;
}
if (n->stereo_phase.type == 0) {
param.playback.stereo_phase = subtrack->stereo_phase;
} else {
param.playback.stereo_phase = n->stereo_phase;
}
if (n->_0A.flags.bit2 == TRUE) {
param.playback.target_reverb_volume = subtrack->target_reverb_vol;
} else {
param.playback.target_reverb_volume = n->target_reverb_volume;
}
if (n->_0A.flags.bit9 == TRUE) {
param.playback.gain = subtrack->gain;
} else {
param.playback.gain = 0;
}
param.playback.filter_buf = subtrack->filter;
param.comb_filter_size = subtrack->comb_filter_size;
param.comb_filter_gain = subtrack->comb_filter_gain;
bookOfs = subtrack->book_ofs;
if (subtrack->group->flags.muted && (subtrack->mute_flags & AUDIO_MUTE_FLAG_STOP_SAMPLES)) {
param.playback.pitch = 0.0f;
param.playback.velocity = 0.0f;
}
break;
}
}
param.playback.pitch *= playback_chan->vibrato_frequency_scale * playback_chan->portamento_frequency_scale;
param.playback.pitch *= AG.audio_params.resample_rate;
param.playback.velocity *= scale;
Nas_smzSetParam(chan, &param);
common_chan->book_ofs = bookOfs;
continue;
}
skip:
if (playback_chan->priority != 0) {
goto step2;
}
}
}
extern wtstr* NoteToVoice(voicetable* vtbl, s32 note) {
wtstr* voice_p;
if (note < vtbl->normal_range_low) {
voice_p = &vtbl->low_pitch_tuned_sample;
} else if (note <= vtbl->normal_range_high) {
voice_p = &vtbl->normal_pitch_tuned_sample;
} else {
voice_p = &vtbl->high_pitch_tuned_sample;
}
return voice_p;
}
extern voicetable* ProgToVp(s32 prog, s32 inst) {
voicetable* vtbl;
if (prog == 0xFF) {
return NULL;
}
if (!Nas_CheckIDbank(prog)) {
AG.audio_error_flags = 0x10000000 + prog;
return NULL;
}
if (inst >= AG.voice_info[prog].num_instruments) {
AG.audio_error_flags = 0x03000000 + (prog << 8) + inst;
return NULL;
}
vtbl = AG.voice_info[prog].instruments[inst];
if (vtbl == NULL) {
AG.audio_error_flags = 0x01000000 + (prog << 8) + inst;
return vtbl;
}
return vtbl;
}
extern perctable* PercToPp(s32 prog, s32 drum) {
perctable* vtbl;
if (prog == 0xFF) {
return NULL;
}
if (!Nas_CheckIDbank(prog)) {
AG.audio_error_flags = 0x10000000 + prog;
return NULL;
}
if (drum >= AG.voice_info[prog].num_drums) {
AG.audio_error_flags = 0x04000000 + (prog << 8) + drum;
return NULL;
}
if ((u32)AG.voice_info[prog].percussion < OS_BASE_CACHED) {
return NULL;
}
vtbl = AG.voice_info[prog].percussion[drum];
if (vtbl == NULL) {
AG.audio_error_flags = 0x05000000 + (prog << 8) + drum;
return vtbl;
}
return vtbl;
}
extern percvoicetable* VpercToVep(s32 prog, s32 sfx) {
percvoicetable* vtbl;
if (prog == 0xFF) {
return NULL;
}
if (!Nas_CheckIDbank(prog)) {
AG.audio_error_flags = 0x10000000 + prog;
return NULL;
}
if (sfx >= AG.voice_info[prog].num_sfx) {
AG.audio_error_flags = 0x04000000 + (prog << 8) + sfx;
return NULL;
}
if ((u32)AG.voice_info[prog].effects < OS_BASE_CACHED) {
return NULL;
}
vtbl = &AG.voice_info[prog].effects[sfx];
if (vtbl == NULL) {
// this should be impossible to trigger
AG.audio_error_flags = 0x05000000 + (prog << 8) + sfx;
}
if (vtbl->tuned_sample.wavetable == NULL) {
return NULL;
}
return vtbl;
}
extern s32 OverwriteBank(s32 type, s32 bankId, s32 idx, s32 table) {
if (bankId == 0xFF) {
return -1;
}
if (!Nas_CheckIDbank(bankId)) {
return -2;
}
switch (type) {
case VOICE_TYPE_PERCUSSION:
if (idx >= AG.voice_info[bankId].num_drums) {
return -3;
}
AG.voice_info[bankId].percussion[idx] = (perctable*)table;
break;
case VOICE_TYPE_SOUND_EFF:
if (idx >= AG.voice_info[bankId].num_sfx) {
return -3;
}
AG.voice_info[bankId].effects[idx] = *(percvoicetable*)table;
break;
default:
if (idx >= AG.voice_info[bankId].num_instruments) {
return -3;
}
AG.voice_info[bankId].instruments[idx] = (voicetable*)table;
break;
}
return 0;
}
static void __Nas_Release_Channel_Main(note* n, int target) {
channel* chan;
playbackparams* param;
sub* subtrack;
s32 i;
if (n == NA_NO_NOTE) {
return;
}
n->_00bit3 = FALSE;
if (n->channel == NULL) {
return;
}
chan = n->channel;
param = &chan->playback_ch.params;
if (chan->playback_ch.wanted_parent_note == n) {
chan->playback_ch.wanted_parent_note = NA_NO_NOTE;
}
if (chan->playback_ch.current_parent_note != n) {
if (chan->playback_ch.current_parent_note != NA_NO_NOTE || chan->playback_ch.wanted_parent_note != NA_NO_NOTE ||
chan->playback_ch.previous_parent_note != n || target == ADSR_STATUS_DECAY) {
return;
}
chan->playback_ch.adsr_envp.fadeout_velocity = AG.audio_params.updates_per_frame_inverse;
chan->playback_ch.adsr_envp.state.flags.release = TRUE;
return;
}
if (chan->playback_ch.adsr_envp.state.flags.status != ADSR_STATUS_DECAY) {
param->pitch = n->note_frequency_scale;
param->velocity = n->note_velocity;
param->pan = n->note_pan;
if (n->sub_track != NULL) {
subtrack = n->sub_track;
if (n->_0A.flags.bit2 == TRUE) {
param->target_reverb_volume = subtrack->target_reverb_vol;
} else {
param->target_reverb_volume = n->target_reverb_volume;
}
if (n->surround_effect_idx == 128) {
param->surround_effect_idx = subtrack->surround_effect_idx;
} else {
param->surround_effect_idx = n->surround_effect_idx;
}
if (n->_0A.flags.bit9 == TRUE) {
param->gain = subtrack->gain;
} else {
param->gain = 0;
}
param->filter = subtrack->filter;
if (param->filter != NULL) {
for (i = 0; i < 8; i++) {
param->filter_buf[i] = param->filter[i];
}
param->filter = param->filter_buf;
}
param->comb_filter_gain = subtrack->comb_filter_gain;
param->comb_filter_size = subtrack->comb_filter_size;
if (subtrack->group->flags.muted && (subtrack->mute_flags & AUDIO_MUTE_FLAG_STOP_SAMPLES)) {
chan->common_ch.finished = TRUE;
}
if (*(u8*)&n->stereo_phase == 0) {
param->stereo_phase = subtrack->stereo_phase;
} else {
param->stereo_phase = n->stereo_phase;
}
chan->playback_ch.priority = subtrack->priority2;
} else {
param->stereo_phase = n->stereo_phase;
chan->playback_ch.priority = 1;
}
chan->playback_ch.previous_parent_note = chan->playback_ch.current_parent_note;
chan->playback_ch.current_parent_note = NA_NO_NOTE;
if (target == ADSR_STATUS_RELEASE) {
chan->playback_ch.adsr_envp.fadeout_velocity = AG.audio_params.updates_per_frame_inverse;
chan->playback_ch.adsr_envp.state.flags.release = TRUE;
chan->playback_ch.status = 2;
} else {
chan->playback_ch.status = 1;
chan->playback_ch.adsr_envp.state.flags.decay = TRUE;
if (n->adsr_env.decay_idx == 0) {
chan->playback_ch.adsr_envp.fadeout_velocity = AG.adsr_decay_table[n->sub_track->adsr_env.decay_idx];
} else {
chan->playback_ch.adsr_envp.fadeout_velocity = AG.adsr_decay_table[n->adsr_env.decay_idx];
}
chan->playback_ch.adsr_envp.sustain = (f32)(s32)n->sub_track->adsr_env.sustain * chan->playback_ch.adsr_envp.current / 256.0f;
}
}
if (target == ADSR_STATUS_DECAY) {
Nas_CutList(&chan->link);
Nas_AddListHead(&chan->link.pNode->releaseList, &chan->link);
}
}
extern void Nas_Release_Channel(note* n) {
__Nas_Release_Channel_Main(n, ADSR_STATUS_DECAY);
}
extern void Nas_Release_Channel_Force(note* n) {
__Nas_Release_Channel_Main(n, ADSR_STATUS_RELEASE);
}
static void __Nas_InitList(link* l) {
l->prev = l;
l->next = l;
l->numAfter = 0;
}
extern void Nas_InitChNode(chnode* node) {
__Nas_InitList(&node->freeList);
__Nas_InitList(&node->releaseList);
__Nas_InitList(&node->relwaitList);
__Nas_InitList(&node->useList);
node->freeList.pNode = node;
node->releaseList.pNode = node;
node->relwaitList.pNode = node;
node->useList.pNode = node;
}
extern void Nas_InitChannelList(void) {
s32 i;
Nas_InitChNode(&AG.channel_node);
for (i = 0; i < AG.num_channels; i++) {
AG.channels[i].link.pData = &AG.channels[i];
AG.channels[i].link.prev = NULL;
Nas_AddList(&AG.channel_node.freeList, &AG.channels[i].link);
}
}
extern void Nas_DeAllocAllVoices(chnode* node) {
s32 i;
link* src;
link* cur;
link* dst;
for (i = 0; i < 4; i++) {
switch (i) {
case 0:
src = &node->freeList;
dst = &AG.channel_node.freeList;
break;
case 1:
src = &node->releaseList;
dst = &AG.channel_node.releaseList;
break;
case 2:
src = &node->relwaitList;
dst = &AG.channel_node.relwaitList;
break;
case 3:
src = &node->useList;
dst = &AG.channel_node.useList;
break;
}
while (TRUE) {
cur = src->next;
if ((s32)cur == (s32)src || cur == NULL) {
break;
}
Nas_CutList(cur);
Nas_AddList(dst, cur);
}
}
}
extern void Nas_AllocVoices(chnode* node, s32 count) {
s32 i;
s32 j;
channel* chan;
link* src;
link* dst;
Nas_DeAllocAllVoices(node);
for (i = 0, j = 0; j < count; i++) {
if (i == 4) {
return;
}
switch (i) {
case 0:
src = &AG.channel_node.freeList;
dst = &node->freeList;
break;
case 1:
src = &AG.channel_node.releaseList;
dst = &node->releaseList;
break;
case 2:
src = &AG.channel_node.relwaitList;
dst = &node->relwaitList;
break;
case 3:
src = &AG.channel_node.useList;
dst = &node->useList;
break;
}
while (j < count) {
chan = (channel*)Nas_GetList(src);
if (chan == NULL) {
break;
}
Nas_AddList(dst, &chan->link);
j++;
}
}
}
static void Nas_AddListHead(link* list, link* l) {
if (l->prev == NULL) {
l->prev = list;
l->next = list->next;
list->next->prev = l;
list->next = l;
list->numAfter++;
l->pNode = list->pNode;
}
}
extern void Nas_CutList(link* l) {
if (l->prev != NULL) {
l->prev->next = l->next;
l->next->prev = l->prev;
l->prev = NULL;
}
}
static channel* __Nas_GetLowerPrio(link* l, s32 prio) {
link* cur = l->next;
link* best;
if (cur == l) {
return NULL;
}
for (best = cur; cur != l; cur = cur->next) {
if (((channel*)cur->pData)->playback_ch.priority <= ((channel*)best->pData)->playback_ch.priority) {
best = cur;
}
}
if (best == NULL) {
return NULL;
}
if (((channel*)best->pData)->playback_ch.priority >= prio) {
return NULL;
}
return (channel*)best->pData;
}
extern void Nas_EntryTrack(channel* chan, note* n) {
s32 instId;
sub* subtrack = n->sub_track;
playbackch* playback = &chan->playback_ch;
commonch* common = &chan->common_ch;
playback->previous_parent_note = NA_NO_NOTE;
playback->current_parent_note = n;
playback->priority = subtrack->note_priority;
n->note_properties_need_init = TRUE;
n->_00bit3 = TRUE;
n->channel = chan;
subtrack->channel = chan;
subtrack->note = n;
n->note_velocity = 0.0f;
Nas_StartVoice(chan);
instId = n->inst_or_wave;
if (instId == 0xFF) {
instId = subtrack->inst_or_wave;
}
common->tuned_sample = n->tuned_sample;
if (instId >= 0x80 && instId < 0xC0) {
common->is_synth_wave = TRUE;
} else {
common->is_synth_wave = FALSE;
}
if (subtrack->sample_start_pos == 1) {
playback->start_sample_pos = common->tuned_sample->wavetable->loop->loop_start;
} else {
playback->start_sample_pos = subtrack->sample_start_pos;
if (playback->start_sample_pos >= common->tuned_sample->wavetable->loop->loop_end) {
playback->start_sample_pos = 0;
}
}
playback->_80 = (int)(n->velocity_square2 * 11.5f);
if (playback->_80 > 15) {
playback->_80 = 15;
}
playback->bank_id = subtrack->bank_id;
playback->stereo_headset_effects = subtrack->stereo_effects;
common->reverb_idx = subtrack->reverb_idx & 3;
}
static void __Nas_InterTrack(channel* chan, note* n) {
playbackch* playback = &chan->playback_ch;
Nas_Release_Channel_Force(playback->current_parent_note);
playback->wanted_parent_note = n;
}
static void __Nas_InterReleaseTrack(channel* chan, note* n) {
playbackch* playback = &chan->playback_ch;
playback->wanted_parent_note = n;
playback->priority = n->sub_track->note_priority;
playback->adsr_envp.fadeout_velocity = AG.audio_params.updates_per_frame_inverse;
playback->adsr_envp.state.flags.release = TRUE;
}
static channel* __Nas_ChLookFree(chnode* node, note* n) {
channel* chan = (channel*)Nas_GetList(&node->freeList);
if (chan != NULL) {
Nas_EntryTrack(chan, n);
Nas_AddListHead(&node->useList, &chan->link);
}
return chan;
}
static channel* __Nas_ChLookRelease(chnode* node, note* n) {
channel* chan = __Nas_GetLowerPrio(&node->releaseList, n->sub_track->note_priority);
if (chan != NULL) {
__Nas_InterReleaseTrack(chan, n);
Nas_CutList(&chan->link);
Nas_AddList(&node->relwaitList, &chan->link);
}
return chan;
}
static channel* __Nas_ChLookRelWait(chnode* node, note* n) {
channel* relWaitChan;
channel* useChan;
s32 relWaitPrio;
s32 usePrio;
relWaitPrio = usePrio = 16;
relWaitChan = __Nas_GetLowerPrio(&node->relwaitList, n->sub_track->note_priority);
if (relWaitChan != NULL) {
relWaitPrio = relWaitChan->playback_ch.priority;
}
useChan = __Nas_GetLowerPrio(&node->useList, n->sub_track->note_priority);
if (useChan != NULL) {
usePrio = useChan->playback_ch.priority;
}
if (relWaitChan == NULL && useChan == NULL) {
return NULL;
}
if (usePrio < relWaitPrio) {
Nas_CutList(&useChan->link);
__Nas_InterTrack(useChan, n);
Nas_AddList(&node->relwaitList, &useChan->link);
useChan->playback_ch.priority = n->sub_track->note_priority;
return useChan;
} else {
relWaitChan->playback_ch.wanted_parent_note = n;
relWaitChan->playback_ch.priority = n->sub_track->note_priority;
return relWaitChan;
}
}
extern channel* Nas_AllocationOnRequest(note* n) {
channel* chan;
u32 policy = n->sub_track->note_alloc_policy;
if (policy & 1) {
chan = n->channel;
if (chan != NULL && chan->playback_ch.previous_parent_note == n &&
chan->playback_ch.wanted_parent_note == NA_NO_NOTE) {
__Nas_InterReleaseTrack(chan, n);
Nas_CutList(&chan->link);
Nas_AddList(&chan->link.pNode->relwaitList, &chan->link);
return chan;
}
}
if (policy & 2) {
if (!(chan = __Nas_ChLookFree(&n->sub_track->channel_node, n)) &&
!(chan = __Nas_ChLookRelease(&n->sub_track->channel_node, n)) &&
!(chan = __Nas_ChLookRelWait(&n->sub_track->channel_node, n))) {
goto null_return;
}
return chan;
}
if (policy & 4) {
if (!(chan = __Nas_ChLookFree(&n->sub_track->channel_node, n)) &&
!(chan = __Nas_ChLookFree(&n->sub_track->group->channel_node, n)) &&
!(chan = __Nas_ChLookRelease(&n->sub_track->channel_node, n)) &&
!(chan = __Nas_ChLookRelease(&n->sub_track->group->channel_node, n)) &&
!(chan = __Nas_ChLookRelWait(&n->sub_track->channel_node, n)) &&
!(chan = __Nas_ChLookRelWait(&n->sub_track->group->channel_node, n))) {
goto null_return;
}
return chan;
}
if (policy & 8) {
if (!(chan = __Nas_ChLookFree(&AG.channel_node, n)) &&
!(chan = __Nas_ChLookRelease(&AG.channel_node, n)) &&
!(chan = __Nas_ChLookRelWait(&AG.channel_node, n))) {
goto null_return;
}
return chan;
}
if (!(chan = __Nas_ChLookFree(&n->sub_track->channel_node, n)) &&
!(chan = __Nas_ChLookFree(&n->sub_track->group->channel_node, n)) &&
!(chan = __Nas_ChLookFree(&AG.channel_node, n)) &&
!(chan = __Nas_ChLookRelease(&n->sub_track->channel_node, n)) &&
!(chan = __Nas_ChLookRelease(&n->sub_track->group->channel_node, n)) &&
!(chan = __Nas_ChLookRelease(&AG.channel_node, n)) &&
!(chan = __Nas_ChLookRelWait(&n->sub_track->channel_node, n)) &&
!(chan = __Nas_ChLookRelWait(&n->sub_track->group->channel_node, n)) &&
!(chan = __Nas_ChLookRelWait(&AG.channel_node, n))) {
goto null_return;
}
return chan;
null_return:
n->_00bit3 = TRUE;
return NULL;
}
extern void Nas_ChannelInit(void) {
s32 i;
channel* chan;
for (i = 0; i < AG.num_channels; i++) {
chan = &AG.channels[i];
chan->common_ch = NA_CHINIT_TABLE[0];
chan->playback_ch.priority = 0;
chan->playback_ch.status = 0;
chan->playback_ch.current_parent_note = NA_NO_NOTE;
chan->playback_ch.wanted_parent_note = NA_NO_NOTE;
chan->playback_ch.previous_parent_note = NA_NO_NOTE;
chan->playback_ch.wave_id = 0;
chan->playback_ch.params.velocity = 0.0f;
chan->playback_ch.adsr_volume_scale_unused = 0;
chan->playback_ch.adsr_envp.state.as_byte = 0;
chan->playback_ch.vibrato_tmtable.active = FALSE;
chan->playback_ch.portamento_sweep.current = 0;
chan->playback_ch.portamento_sweep.speed = 0;
chan->playback_ch.stereo_headset_effects = FALSE;
chan->playback_ch.start_sample_pos = 0;
chan->driver_ch.synth_params = (synthparams*)Nas_NcHeapAlloc(&AG.misc_heap, sizeof(synthparams));
chan->playback_ch.params.filter_buf = (s16*)Nas_NcHeapAlloc(&AG.misc_heap, 8 * sizeof(s16));
}
}
+1 -1
View File
@@ -95,7 +95,7 @@ void* Nas_2ndHeapAlloc(ALHeap*, s32)
* Address: ........
* Size: 00003C
*/
void Nas_NcHeapAlloc(ALHeap*, s32)
void* Nas_NcHeapAlloc(ALHeap*, s32)
{
// UNUSED FUNCTION
}
+1 -1
View File
@@ -552,7 +552,7 @@ static void __SetSubParam(sub* subtrack, AudioPort* port) {
subtrack->comb_filter_gain = port->param.asU16;
break;
case AUDIOCMD_OP_SUB_SET_STEREO:
subtrack->stereo_phase.asU8 = port->param.asU8;
*(u8*)&subtrack->stereo_phase = port->param.asU8;
break;
case AUDIOCMD_OP_SUB_SET_SET_START_POS:
subtrack->sample_start_pos = port->param.asS32;
+14 -14
View File
@@ -406,7 +406,7 @@ s32 Nas_LoadVoice(s32 bank_id, s32 instId, s32 drumId) {
__Nas_LoadVoice_Inner(voice->high_pitch_tuned_sample.wavetable, bank_id);
}
} else if (instId == 0x7F) {
percvoicetable* perc = PercToPp(bank_id, drumId);
perctable* perc = PercToPp(bank_id, drumId);
if (perc == NULL) {
return -1;
@@ -809,8 +809,8 @@ static void Nas_BankOfsToAddr_Inner(s32 bank_id, u8* ctrl_p, WaveMedia* wave_med
u32 ofs;
u32 inst_ofs;
voicetable* inst;
percvoicetable* percvt;
veffvoicetable* sfx;
perctable* percvt;
percvoicetable* sfx;
s32 i;
s32 n_perc_inst, n_voice_inst, n_sfx_inst;
n_voice_inst = AG.voice_info[bank_id].num_instruments;
@@ -823,14 +823,14 @@ static void Nas_BankOfsToAddr_Inner(s32 bank_id, u8* ctrl_p, WaveMedia* wave_med
*BANK_ENTRY(ctrl_p, 0) = OFS2RAM(ctrl_p, ofs);
for (i = 0; i < n_perc_inst; i++) {
inst_ofs = (u32)((percvoicetable**)*BANK_ENTRY(ctrl_p, 0))[i];
inst_ofs = (u32)((perctable**)*BANK_ENTRY(ctrl_p, 0))[i];
if (inst_ofs == 0) {
continue; // empty percussion/drum entry
}
inst_ofs += (u32)ctrl_p;//OFS2RAM(ctrl_p, ofs);
percvt = (percvoicetable*)inst_ofs;
((percvoicetable**)*BANK_ENTRY(ctrl_p, 0))[i] = percvt;
percvt = (perctable*)inst_ofs;
((perctable**)*BANK_ENTRY(ctrl_p, 0))[i] = percvt;
// Percussion may already have been relocated since percussion
// can appear in list multiple times
@@ -851,8 +851,8 @@ static void Nas_BankOfsToAddr_Inner(s32 bank_id, u8* ctrl_p, WaveMedia* wave_med
*BANK_ENTRY(ctrl_p, 1) = OFS2RAM(ctrl_p, ofs);
for (i = 0; i < n_sfx_inst; i++) {
inst_ofs = (u32)(((veffvoicetable*)*BANK_ENTRY(ctrl_p, 1)) + i);
sfx = (veffvoicetable*)inst_ofs;
inst_ofs = (u32)(((percvoicetable*)*BANK_ENTRY(ctrl_p, 1)) + i);
sfx = (percvoicetable*)inst_ofs;
// check for null sfx or null sample wave table pointer
if (sfx == NULL || sfx->tuned_sample.wavetable == NULL) {
@@ -897,8 +897,8 @@ static void Nas_BankOfsToAddr_Inner(s32 bank_id, u8* ctrl_p, WaveMedia* wave_med
}
}
AG.voice_info[bank_id].percussion = (percvoicetable**)*BANK_ENTRY(ctrl_p, 0);
AG.voice_info[bank_id].effects = (veffvoicetable*)*BANK_ENTRY(ctrl_p, 1);
AG.voice_info[bank_id].percussion = (perctable**)*BANK_ENTRY(ctrl_p, 0);
AG.voice_info[bank_id].effects = (percvoicetable*)*BANK_ENTRY(ctrl_p, 1);
AG.voice_info[bank_id].instruments = (voicetable**)BANK_ENTRY(ctrl_p, 2);
}
@@ -1330,7 +1330,7 @@ static smzwavetable* __GetWaveTable(s32 bank_id, s32 inst_id) {
return vt->normal_pitch_tuned_sample.wavetable;
} else if (inst_id <= 255) {
percvoicetable* pvt = PercToPp(bank_id, inst_id - 128);
perctable* pvt = PercToPp(bank_id, inst_id - 128);
if (pvt == NULL) {
return NULL;
@@ -1338,7 +1338,7 @@ static smzwavetable* __GetWaveTable(s32 bank_id, s32 inst_id) {
return pvt->tuned_sample.wavetable;
} else {
veffvoicetable* evt = VpercToVep(bank_id, inst_id - 256);
percvoicetable* evt = VpercToVep(bank_id, inst_id - 256);
if (evt == NULL) {
return NULL;
@@ -1885,9 +1885,9 @@ void WaveReload(s32 bank_id, s32 async, WaveMedia* wavemedia) {
s32 n_drums;
s32 n_instruments;
s32 n_sfx;
percvoicetable* percussion;
perctable* percussion;
voicetable* instrument;
veffvoicetable* sfx;
percvoicetable* sfx;
Bgloadreq* preload;
Bgloadreq* top_preload;
smzwavetable* wavetable;
+17 -17
View File
@@ -250,7 +250,7 @@ static void Nas_InitSubTrack(sub* subtrack) {
subtrack->stereo_effects = FALSE;
subtrack->large_notes = FALSE;
subtrack->book_ofs = 0;
subtrack->stereo_phase.asU8 = 0;
*(u8*)&subtrack->stereo_phase = 0;
subtrack->changes.as_byte = 0xFF;
subtrack->macro_player.depth = 0;
subtrack->volume = 1.0f;
@@ -324,7 +324,7 @@ static s32 Nas_EntryNoteTrack(sub* subtrack, int note_idx) {
entry_note->ignore_drum_pan = FALSE;
entry_note->_00bit1 = FALSE;
entry_note->note_properties_need_init = FALSE;
entry_note->stereo_phase.asU8 = 0;
*(u8*)&entry_note->stereo_phase = 0;
entry_note->portamento_sweep.mode = 0;
entry_note->macro_player.depth = 0;
entry_note->gate_time = 128;
@@ -464,7 +464,7 @@ extern void Nas_ReleaseGroup(group* grp) {
}
extern void Nas_AddList(link* root, link* list) {
if (list->next != nullptr) {
if (list->prev != nullptr) {
static BOOL first = TRUE;
if (first) {
@@ -478,25 +478,25 @@ extern void Nas_AddList(link* root, link* list) {
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->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->next;
link* list = root->prev;
if (list == root) {
return nullptr;
}
list->next->prev = root;
root->next = list->next;
list->next = nullptr;
list->prev->next = root;
root->prev = list->prev;
list->prev = nullptr;
root->numAfter--;
return list->pData;
}
@@ -504,14 +504,14 @@ extern void* Nas_GetList(link* root) {
static void Nas_InitNoteList(void) {
s32 i;
AG.note_link.next = &AG.note_link;
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.next = nullptr;
AG.notes[i].link.prev = nullptr;
Nas_AddList(&AG.note_link, &AG.notes[i].link);
}
}
@@ -749,7 +749,7 @@ static s32 __Command_Seq(note* n) {
n->ignore_drum_pan = TRUE;
break;
case 0xCD:
n->stereo_phase.asU8 = Nas_ReadByteData(m);
*(u8*)&n->stereo_phase = Nas_ReadByteData(m);
break;
case 0xCE:
cmdArgU8 = 128 + Nas_ReadByteData(m);
@@ -785,8 +785,8 @@ static s32 __SetVoice(note* n, s32 arg) {
f32 freq_scale2;
wtstr* tuned_sample;
voicetable* instrument;
percvoicetable* percussion;
veffvoicetable* effect;
perctable* percussion;
percvoicetable* effect;
sub* subtrack;
group* grp;
u16 effect_id;
@@ -1450,7 +1450,7 @@ static void Nas_SubSeq(sub* subtrack) {
subtrack->stereo_effects = FALSE;
}
subtrack->stereo_phase.asU8 = cmdArgU8 & 0x7F;
*(u8*)&subtrack->stereo_phase = cmdArgU8 & 0x7F;
break;
case 0xD1: // set note allocation policy
cmdArgU8 = (u8)cmdArgs[0];