mirror of
https://github.com/ACreTeam/ac-decomp
synced 2026-05-23 06:34:18 -04:00
jaudio_NES: implement & link effect
This commit is contained in:
+2
-2
@@ -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"),
|
||||
],
|
||||
|
||||
@@ -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))
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -12,5 +12,6 @@ extern u8 DEFAULT_VTABLE[];
|
||||
extern u8 DEFAULT_GTABLE[];
|
||||
|
||||
extern u8 BDB_SEQDATA[];
|
||||
extern s16* WAVEMEM_TABLE[];
|
||||
|
||||
#endif
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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++;
|
||||
|
||||
Reference in New Issue
Block a user