jaudio_NES: implement & link effect

This commit is contained in:
Cuyler36
2025-06-21 12:28:29 -04:00
parent 783bfc3cfe
commit 6fbc68f77b
7 changed files with 408 additions and 23 deletions
+2 -2
View File
@@ -773,7 +773,7 @@ config.libs = [
Object(Matching, "jaudio_NES/internal/dsp_GBAKey.c"),
Object(Equivalent, "jaudio_NES/internal/dspdriver.c"),
Object(Matching, "jaudio_NES/internal/dspinterface.c"),
Object(NonMatching, "jaudio_NES/internal/effect.c"),
Object(Matching, "jaudio_NES/internal/effect.c", extra_cflags=["-pragma \"scheduling 7400\""]),
Object(Matching, "jaudio_NES/internal/fat.c"),
Object(Matching, "jaudio_NES/internal/fxinterface.c"),
Object(Matching, "jaudio_NES/internal/heapctrl.c"),
@@ -788,7 +788,7 @@ config.libs = [
Object(NonMatching, "jaudio_NES/internal/rate.c"),
Object(NonMatching, "jaudio_NES/internal/rspsim.c"),
Object(Matching, "jaudio_NES/internal/seqsetup.c"),
Object(Equivalent, "jaudio_NES/internal/system.c"),
Object(Equivalent, "jaudio_NES/internal/system.c", extra_cflags=["-pragma \"scheduling 7400\""]),
Object(Matching, "jaudio_NES/internal/tables.c"),
Object(Matching, "jaudio_NES/internal/waveread.c"),
],
+7
View File
@@ -19,6 +19,7 @@ extern "C" {
#define AUDIO_GROUP_MAX 5
#define AUDIO_SUBTRACK_NUM 16
#define AUDIO_NOTE_MAX 128
#define AUDIO_SUBTRACK_NOTE_NUM 4
#define AUDIO_TATUMS_PER_BEAT 48
@@ -30,6 +31,12 @@ extern "C" {
#define AUDIO_MUTE_FLAG_STOP_NOTE (1 << 6)
#define AUDIO_MUTE_FLAG_STOP_SCRIPT (1 << 7)
#define ADSR_DISABLE 0
#define ADSR_HANG -1
#define ADSR_GOTO -2
#define ADSR_RESTART -3
#define ADSR_SPECIAL4 -4 // TODO: figure this out
#define NA_MAKE_COMMAND(a0, a1, a2, a3) \
(u32)((((a0) & 0xFF) << 24) | (((a1) & 0xFF) << 16) | (((a2) & 0xFF) << 8) | (((a3) & 0xFF) << 0))
+23 -18
View File
@@ -180,11 +180,16 @@ typedef struct envdat_ {
/* sizeof(envp) == 0x20 */
typedef struct envp_ {
/* 0x00 */ u8 unused : 1;
/* 0x00 */ u8 hang : 1;
/* 0x00 */ u8 decay : 1;
/* 0x00 */ u8 release : 1;
/* 0x00 */ u8 status : 4;
union {
struct {
/* 0x00 */ u8 unused : 1;
/* 0x00 */ u8 hang : 1;
/* 0x00 */ u8 decay : 1;
/* 0x00 */ u8 release : 1;
/* 0x00 */ u8 status : 4;
} flags;
u8 as_byte;
} state;
/* 0x01 */ u8 envelope_idx;
/* 0x02 */ s16 delay;
@@ -444,6 +449,15 @@ typedef struct macro_ {
/* 0x19 */ s8 value;
} macro;
typedef union subtrack_updates {
struct {
/* 0x01 */ u8 frequency_scale : 1;
/* 0x01 */ u8 volume : 1;
/* 0x01 */ u8 pan : 1;
} flags;
/* 0x01 */ u8 as_byte;
} subtrack_updates;
/* SubTrack struct */
/* sizeof(sub) == 0xE0 */
typedef struct sub_ {
@@ -455,16 +469,7 @@ typedef struct sub_ {
/* 0x00 */ u8 stereo_effects : 1;
/* 0x00 */ u8 large_notes : 1;
/* 0x00 */ u8 unused : 1;
union {
struct {
/* 0x01 */ u8 frequency_scale : 1;
/* 0x01 */ u8 volume : 1;
/* 0x01 */ u8 pan : 1;
} flags;
/* 0x01 */ u8 as_byte;
} changes;
/* 0x01 */ subtrack_updates changes;
/* 0x02 */ u8 note_alloc_policy;
/* 0x03 */ u8 mute_flags;
/* 0x04 */ u8 target_reverb_vol;
@@ -580,15 +585,15 @@ struct note_ {
union {
struct {
/* 0x0A */ u16 bit0 : 1;
/* 0x0A */ u16 bit1 : 1;
/* 0x0A */ u16 skip_volume_update : 1;
/* 0x0A */ u16 skip_freq_scale_update : 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 skip_pan_update : 1;
/* 0x0B */ u16 bit9 : 1;
/* 0x0B */ u16 bitA : 1;
/* 0x0B */ u16 bitB : 1;
+1
View File
@@ -12,5 +12,6 @@ extern u8 DEFAULT_VTABLE[];
extern u8 DEFAULT_GTABLE[];
extern u8 BDB_SEQDATA[];
extern s16* WAVEMEM_TABLE[];
#endif
+3 -1
View File
@@ -6,7 +6,9 @@
extern void Nas_ChannelModInit(channel* ch);
extern void Nas_SweepInit(channel* ch);
extern void Nas_EnvInit(envp* env, envdat* data, s16* vol_out);
extern void Nas_ChannelModulation(channel* channel);
extern void Nas_MainCtrl(group* grp);
extern f32 Nas_EnvProcess(envp* process);
#endif
+370
View File
@@ -0,0 +1,370 @@
#include "jaudio_NES/effect.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/channel.h"
#include "jaudio_NES/track.h"
#include "jaudio_NES/sub_sys.h"
#include "jaudio_NES/audioheaders.h"
#include <dolphin/os.h>
static void __Nas_CallWaveProcess_Sub(sub* subtrack, s32 recalc_vol, s32 apply_bend) {
f32 vol;
f32 freqScale;
s32 i;
if (subtrack->changes.flags.volume || recalc_vol) {
vol = subtrack->volume * subtrack->volume_scale * subtrack->group->applied_fade_volume;
if (subtrack->group->flags.muted && (subtrack->mute_flags & AUDIO_MUTE_FLAG_SOFTEN)) {
vol *= subtrack->group->mute_volume_scale;
}
subtrack->applied_volume = vol * vol;
}
if (subtrack->changes.flags.pan) {
subtrack->pan = subtrack->new_pan * subtrack->pan_channel_weight;
}
freqScale = subtrack->frequency_scale;
if (apply_bend) {
freqScale *= subtrack->group->bend;
subtrack->changes.flags.frequency_scale = TRUE;
}
for (i = 0; i < AUDIO_SUBTRACK_NOTE_NUM; i++) {
note* note = subtrack->note_layers[i];
if (note != NULL && note->enabled && note->channel != NULL) {
static subtrack_updates updates_all = { { TRUE, TRUE, TRUE } };
subtrack_updates* updates_p = &subtrack->changes;
if (note->note_properties_need_init) {
updates_p = &updates_all;
note->note_properties_need_init = FALSE;
}
if (updates_p->flags.frequency_scale) {
if (!note->_0A.flags.skip_freq_scale_update) {
note->note_frequency_scale = note->frequency_scale;
} else {
note->note_frequency_scale = note->frequency_scale * freqScale;
}
}
if (updates_p->flags.volume || recalc_vol) {
if (!note->_0A.flags.skip_volume_update) {
note->note_velocity = note->velocity_square2;
} else {
note->note_velocity = note->velocity_square2 * subtrack->applied_volume;
}
}
if (updates_p->flags.pan) {
if (!note->_0A.flags.skip_pan_update) {
note->note_velocity = note->pan; // @BUG - shouldn't this be note->note_pan??
} else {
note->note_pan = (subtrack->pan + note->pan * (128 - subtrack->pan_channel_weight)) >> 7;
}
}
}
}
subtrack->changes.as_byte = 0;
}
extern void Nas_MainCtrl(group* group) {
s32 i;
if (group->fade_timer != 0 && group->skip_ticks == 0) {
group->flags.recalculate_volume = TRUE;
group->fade_volume += group->fade_velocity;
if (group->fade_volume > 1.0f) {
group->fade_volume = 1.0f;
}
if (group->fade_volume < 0.0f) {
group->fade_volume = 0.0f;
}
group->fade_timer--;
if (group->fade_timer == 0) {
switch (group->state) {
case GROUP_STATE_FADE_OUT:
Nas_ReleaseGroup(group);
return;
}
}
}
if (group->flags.recalculate_volume) {
group->applied_fade_volume = group->fade_volume * group->fade_volume_scale;
}
for (i = 0; i < NA_GROUP_NUM; i++) {
if (group->subtracks[i]->enabled == TRUE) {
__Nas_CallWaveProcess_Sub(group->subtracks[i], group->flags.recalculate_volume, group->flags.apply_bend);
}
}
group->flags.recalculate_volume = FALSE;
}
static f32 Nas_SweepCalculator(sweep* sweep) {
u32 loResCur;
f32 sweepFreq;
sweep->current += sweep->speed;
loResCur = (sweep->current >> 8) & 0xFF;
if (loResCur >= 127) {
loResCur = 127;
sweep->mode = PORTAMENTO_MODE_OFF;
}
return 1.0f + sweep->extent * (PCENTTABLE[loResCur + 128] - 1.0f);
}
static s16 Nas_ModTableRead(tmtable* table) {
int idx;
table->time += (int)table->rate;
idx = (table->time >> 10) & 0x3F;
return table->curve[idx];
}
static f32 Nas_Modulator(tmtable* table) {
vibparam* param = table->vibrato_params;
s16 mod;
f32 depth;
f32 invDepth;
f32 result;
f32 t;
if (table->delay != 0) {
table->delay--;
return 1.0f;
}
if (param != NULL) {
if (table->depth_change_timer != 0) {
if (table->depth_change_timer == 1) {
table->depth = (int)param->depth_target;
} else {
table->depth += ((int)param->depth_target - table->depth) / (int)table->depth_change_timer;
}
table->depth_change_timer--;
} else if (param->depth_target != (int)table->depth) {
u32 delay = param->depth_change_delay;
table->depth_change_timer = delay;
if (delay == 0) {
table->depth = (s32)param->depth_target;
}
}
if (table->rate_change_timer != 0) {
if (table->rate_change_timer == 1) {
table->rate = (int)param->rate_target;
} else {
table->rate += ((int)param->rate_target - table->rate) / (int)table->rate_change_timer;
}
table->rate_change_timer--;
} else if (param->rate_target != (int)table->rate) {
u32 delay = param->rate_change_delay;
table->rate_change_timer = delay;
if (delay == 0) {
table->rate = (s32)param->rate_target;
}
}
}
if (table->depth == 0.0f) {
return 1.0f;
}
mod = Nas_ModTableRead(table);
t = table->depth;
depth = 1.0f + (t / 4096.0f);
invDepth = 1.0f / depth;
result = 1.0f / ((depth - invDepth) * (mod + 32768.0f) / 65536.0f + invDepth);
return result;
}
extern void Nas_ChannelModulation(channel* channel) {
sweep* sweep;
tmtable* table;
sweep = &channel->playback_ch.portamento_sweep;
if (sweep->mode != PORTAMENTO_MODE_OFF) {
channel->playback_ch.portamento_frequency_scale = Nas_SweepCalculator(sweep);
}
table = &channel->playback_ch.vibrato_tmtable;
if (table->active) {
channel->playback_ch.vibrato_frequency_scale = Nas_Modulator(table);
}
}
extern void Nas_ChannelModInit(channel* chan) {
tmtable* table = &chan->playback_ch.vibrato_tmtable;
vibparam* param;
table->active = TRUE;
table->curve = WAVEMEM_TABLE[2];
if (chan->playback_ch.current_parent_note->_0A.flags.use_vibrato == TRUE) {
table->vibrato_params = &chan->playback_ch.current_parent_note->sub_track->vibrato_params;
} else {
table->vibrato_params = &chan->playback_ch.current_parent_note->vibrato_params;
}
param = table->vibrato_params;
if ((table->depth_change_timer = param->depth_change_delay) == 0) {
table->depth = (int)param->depth_target;
} else {
table->depth = (int)param->depth_start;
}
if ((table->rate_change_timer = param->rate_change_delay) == 0) {
table->rate = (int)param->rate_target;
} else {
table->rate = (int)param->rate_start;
}
chan->playback_ch.vibrato_frequency_scale = 1.0f;
table->time = 0;
table->delay = param->delay;
}
extern void Nas_SweepInit(channel* chan) {
chan->playback_ch.portamento_frequency_scale = 1.0f;
chan->playback_ch.portamento_sweep = chan->playback_ch.current_parent_note->portamento_sweep;
}
extern void Nas_EnvInit(envp* env, envdat* data, s16* vol_out) {
env->state.as_byte = 0;
env->delay = 0;
env->sustain = 0.0f;
env->current = 0.0f;
env->velocity = 0.0f;
env->fadeout_velocity = 0.0f;
env->pEnvData = data;
}
extern f32 Nas_EnvProcess(envp* process) {
u8 start_state = process->state.flags.status;
switch (process->state.flags.status) {
case ADSR_STATUS_DISABLED:
return 0.0f;
case ADSR_STATUS_INITIAL:
if (process->state.flags.hang) {
process->state.flags.status = ADSR_STATUS_HANG;
break;
}
// fallthrough
case ADSR_STATUS_START_LOOP:
process->envelope_idx = 0;
process->state.flags.status = ADSR_STATUS_LOOP;
// fallthrough
retry:
case ADSR_STATUS_LOOP:
process->delay = process->pEnvData[process->envelope_idx].delay;
switch (process->delay) {
case ADSR_DISABLE:
process->state.flags.status = ADSR_STATUS_DISABLED;
break;
case ADSR_HANG:
process->state.flags.status = ADSR_STATUS_HANG;
break;
case ADSR_GOTO:
process->envelope_idx = process->pEnvData[process->envelope_idx].value;
goto retry;
case ADSR_RESTART:
process->state.flags.status = ADSR_STATUS_INITIAL;
break;
case ADSR_SPECIAL4:
process->_18 = (int)process->pEnvData[process->envelope_idx].value / 32767.0f;
process->state.flags.unused = TRUE;
process->envelope_idx++;
goto retry;
default:
process->delay = process->delay * AG.audio_params.updates_per_frame_scaled;
if (process->delay == 0) {
process->delay = 1;
}
process->target = process->pEnvData[process->envelope_idx].value / 32767.0f;
process->target *= process->target;
process->velocity = (process->target - process->current) / process->delay;
process->state.flags.status = ADSR_STATUS_FADE;
process->envelope_idx++;
break;
}
if (process->state.flags.status != ADSR_STATUS_FADE) {
break;
}
// fallthrough
case ADSR_STATUS_FADE:
process->current += process->velocity;
if (--process->delay <= 0) {
process->state.flags.status = ADSR_STATUS_LOOP;
}
// fallthrough
case ADSR_STATUS_HANG:
break;
case ADSR_STATUS_DECAY:
case ADSR_STATUS_RELEASE:
process->current -= process->fadeout_velocity;
if (process->sustain != 0.0f && start_state == ADSR_STATUS_DECAY) {
if (process->current < process->sustain) {
process->current = process->sustain;
process->delay = 128;
process->state.flags.status = ADSR_STATUS_SUSTAIN;
}
break;
}
if (process->current < 0.00001f) {
process->current = 0.0f;
process->state.flags.status = ADSR_STATUS_DISABLED;
}
break;
case ADSR_STATUS_SUSTAIN:
process->delay--;
if (process->delay == 0) {
process->state.flags.status = ADSR_STATUS_RELEASE;
}
break;
}
if (process->state.flags.decay) {
process->state.flags.status = ADSR_STATUS_DECAY;
process->state.flags.decay = FALSE;
}
if (process->state.flags.release) {
process->state.flags.status = ADSR_STATUS_RELEASE;
process->state.flags.release = FALSE;
}
if (process->current < 0.0f) {
return 0.0f;
}
if (process->current > 1.0f) {
return 1.0f;
}
return process->current;
}
+2 -2
View File
@@ -600,7 +600,7 @@ static s32 Nap_SilenceCheck_Inner(s32 flags) {
playback_chan = &channel->playback_ch;
if (channel->common_ch.enabled) {
common_chan = &channel->common_ch;
if (playback_chan->adsr_envp.status != ADSR_STATUS_DISABLED) {
if (playback_chan->adsr_envp.state.flags.status != ADSR_STATUS_DISABLED) {
if (flags >= AUDIO_NOTE_SAMPLE_NOTES) {
tuned_sample = common_chan->tuned_sample;
if ((tuned_sample == nullptr) || common_chan->is_synth_wave) {
@@ -614,7 +614,7 @@ static s32 Nap_SilenceCheck_Inner(s32 flags) {
if ((flags & AUDIO_NOTE_RELEASE) == AUDIO_NOTE_RELEASE) {
playback_chan->adsr_envp.fadeout_velocity = AG.audio_params.updates_per_frame_inverse;
playback_chan->adsr_envp.release = TRUE;
playback_chan->adsr_envp.state.flags.release = TRUE;
}
channelCount++;