diff --git a/linker_scripts/us/symbol_addrs.txt b/linker_scripts/us/symbol_addrs.txt index 2869ad9..9cd9b2c 100644 --- a/linker_scripts/us/symbol_addrs.txt +++ b/linker_scripts/us/symbol_addrs.txt @@ -270,3 +270,4 @@ ECHO_PARAMS_N = 0x80079460; CHORUS_PARAMS_N = 0x80079488; FLANGE_PARAMS_N = 0x800794B0; NULL_PARAMS_N = 0x800794D8; +n_eqpower = 0x80078600; diff --git a/linker_scripts/us/symbol_addrs_code.txt b/linker_scripts/us/symbol_addrs_code.txt index 701ab8c..cc7996a 100644 --- a/linker_scripts/us/symbol_addrs_code.txt +++ b/linker_scripts/us/symbol_addrs_code.txt @@ -1366,9 +1366,9 @@ func_80045FF0 = 0x80045FF0; // type:func n_alSynStopVoice = 0x80046EF0; // type:func n_alEnvmixerPull = 0x80046F60; // type:func n_alEnvmixerParam = 0x80047500; // type:func -func_800475A4 = 0x800475A4; // type:func -func_800477D0 = 0x800477D0; // type:func -func_80047958 = 0x80047958; // type:func +_pullSubFrame = 0x800475A4; // type:func +_getRate = 0x800477D0; // type:func +_getVol = 0x80047958; // type:func func_800479C0 = 0x800479C0; // type:func func_80048014 = 0x80048014; // type:func func_80048060 = 0x80048060; // type:func @@ -1454,10 +1454,10 @@ alN_PVoiceNew = 0x8004F084; // type:func n_alAuxBusPull = 0x8004F1B0; // type:func n_alSynSetFXMix = 0x8004F260; // type:func n_alSynAddPlayer = 0x8004F2F0; // type:func -func_8004F350 = 0x8004F350; // type:func +n_alResamplePull = 0x8004F350; // type:func func_8004F4F8 = 0x8004F4F8; // type:func func_8004F520 = 0x8004F520; // type:func -func_8004F95C = 0x8004F95C; // type:func +n_alLoadParam = 0x8004F95C; // type:func func_8004FB1C = 0x8004FB1C; // type:func func_8004FC60 = 0x8004FC60; // type:func func_8004FCD8 = 0x8004FCD8; // type:func diff --git a/src/47B60.c b/src/47B60.c deleted file mode 100644 index 0f199ba..0000000 --- a/src/47B60.c +++ /dev/null @@ -1,11 +0,0 @@ -#include "global.h" - -#pragma GLOBAL_ASM("asm/us/nonmatchings/47B60/n_alEnvmixerPull.s") - -#pragma GLOBAL_ASM("asm/us/nonmatchings/47B60/n_alEnvmixerParam.s") - -#pragma GLOBAL_ASM("asm/us/nonmatchings/47B60/func_800475A4.s") - -#pragma GLOBAL_ASM("asm/us/nonmatchings/47B60/func_800477D0.s") - -#pragma GLOBAL_ASM("asm/us/nonmatchings/47B60/func_80047958.s") diff --git a/src/4FF50.c b/src/4FF50.c index 8d88c3b..41b0c14 100644 --- a/src/4FF50.c +++ b/src/4FF50.c @@ -1,5 +1,5 @@ #include "global.h" -#pragma GLOBAL_ASM("asm/us/nonmatchings/4FF50/func_8004F350.s") +#pragma GLOBAL_ASM("asm/us/nonmatchings/4FF50/n_alResamplePull.s") #pragma GLOBAL_ASM("asm/us/nonmatchings/4FF50/func_8004F4F8.s") diff --git a/src/50120.c b/src/50120.c index 1af632d..5168e6a 100644 --- a/src/50120.c +++ b/src/50120.c @@ -2,6 +2,6 @@ #pragma GLOBAL_ASM("asm/us/nonmatchings/50120/func_8004F520.s") -#pragma GLOBAL_ASM("asm/us/nonmatchings/50120/func_8004F95C.s") +#pragma GLOBAL_ASM("asm/us/nonmatchings/50120/n_alLoadParam.s") #pragma GLOBAL_ASM("asm/us/nonmatchings/50120/func_8004FB1C.s") diff --git a/src/libnaudio/n_env.c b/src/libnaudio/n_env.c new file mode 100644 index 0000000..9f6b21d --- /dev/null +++ b/src/libnaudio/n_env.c @@ -0,0 +1,658 @@ +#include "global.h" + +/*==================================================================== + * + * Copyright 1993, Silicon Graphics, Inc. + * All Rights Reserved. + * + * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, + * Inc.; the contents of this file may not be disclosed to third + * parties, copied or duplicated in any form, in whole or in part, + * without the prior written permission of Silicon Graphics, Inc. + * + * RESTRICTED RIGHTS LEGEND: + * Use, duplication or disclosure by the Government is subject to + * restrictions as set forth in subdivision (c)(1)(ii) of the Rights + * in Technical Data and Computer Software clause at DFARS + * 252.227-7013, and/or in similar or successor clauses in the FAR, + * DOD or NASA FAR Supplement. Unpublished - rights reserved under the + * Copyright Laws of the United States. + *====================================================================*/ + +#include "n_synthInternals.h" +#include +#include +#include + +#ifdef _DEBUG +#include +#endif + +#ifdef AUD_PROFILE +extern u32 cnt_index, env_num, env_cnt, env_max, env_min, lastCnt[]; +extern u32 rate_num, rate_cnt, rate_max, rate_min; +extern u32 vol_num, vol_cnt, vol_max, vol_min; +#endif + +#define N_EQPOWER_LENGTH 128 + +s16 n_eqpower[ N_EQPOWER_LENGTH ] = { + 32767, 32764, 32757, 32744, 32727, 32704, + 32677, 32644, 32607, 32564, 32517, 32464, + 32407, 32344, 32277, 32205, 32127, 32045, + 31958, 31866, 31770, 31668, 31561, 31450, + 31334, 31213, 31087, 30957, 30822, 30682, + 30537, 30388, 30234, 30075, 29912, 29744, + 29572, 29395, 29214, 29028, 28838, 28643, + 28444, 28241, 28033, 27821, 27605, 27385, + 27160, 26931, 26698, 26461, 26220, 25975, + 25726, 25473, 25216, 24956, 24691, 24423, + 24151, 23875, 23596, 23313, 23026, 22736, + 22442, 22145, 21845, 21541, 21234, 20924, + 20610, 20294, 19974, 19651, 19325, 18997, + 18665, 18331, 17993, 17653, 17310, 16965, + 16617, 16266, 15913, 15558, 15200, 14840, + 14477, 14113, 13746, 13377, 13006, 12633, + 12258, 11881, 11503, 11122, 10740, 10357, + 9971, 9584, 9196, 8806, 8415, 8023, + 7630, 7235, 6839, 6442, 6044, 5646, + 5246, 4845, 4444, 4042, 3640, 3237, + 2833, 2429, 2025, 1620, 1216, 810, + 405, 0 +}; + +#ifndef N_MICRO +extern f64 __pow(f64, f64); +extern f64 _frexpf(f64 value, s32 *eptr); +extern f64 _ldexpf(f64 in, s32 ex); +#endif + +/* + * prototypes for private enveloper functions + */ +static Acmd *_pullSubFrame(N_PVoice *pv, s16 *inp, s16 *outp, s32 outCount, + Acmd *p) ; + +static s16 _getRate(f64 vol, f64 tgt, s32 count, u16* ratel); + +#ifndef N_MICRO +static f32 _getVol(f32 ivol, s32 samples, s16 ratem, u16 ratel); +#else +static s16 _getVol(s16 ivol, s32 samples, s16 ratem, u16 ratel); +#endif + +/*********************************************************************** + * Enveloper filter public interfaces + ***********************************************************************/ +Acmd *n_alEnvmixerPull(N_PVoice *filter, s32 sampleOffset, Acmd *p) +{ + Acmd *ptr = p; + + + N_PVoice *e = (N_PVoice *)filter; + + s16 inp; + s32 lastOffset; + s32 thisOffset = sampleOffset; + s32 samples; + s16 loutp = 0; + s32 fVol; + ALParam *thisParam; + s32 outCount = FIXED_SAMPLE; + +#ifdef AUD_PROFILE + lastCnt[++cnt_index] = osGetCount(); +#endif + + /* + * Force the input to be the resampler output + */ +#ifndef N_MICRO + inp = AL_RESAMPLER_OUT; +#else + inp = N_AL_RESAMPLER_OUT; +#endif + + while (e->em_ctrlList != 0) { + + lastOffset = thisOffset; + thisOffset = e->em_ctrlList->delta; + +#ifdef FINAL_ROUND + samples = SAMPLE184(thisOffset - lastOffset); + if(!samples) + thisOffset = lastOffset; +#else + samples = thisOffset - lastOffset; +#endif + + if (samples > outCount) + break; + +#ifdef _DEBUG + assert(samples >= 0); +#ifndef N_MICRO + assert(samples <= AL_MAX_RSP_SAMPLES); +#else + assert(samples <= FIXED_SAMPLE); +#endif +#endif /* _DEBUG */ + + switch (e->em_ctrlList->type) { + case (AL_FILTER_START_VOICE_ALT): + { + ALStartParamAlt *param = (ALStartParamAlt *)e->em_ctrlList; + ALFilter *f = (ALFilter *) e; + s32 tmp; + + if (param->unity) { + e->rs_upitch = 1; + } + + n_alLoadParam(e, AL_FILTER_SET_WAVETABLE, param->wave); + e->em_motion = AL_PLAYING; + e->em_first = 1; + e->em_delta = 0; + +#ifdef FINAL_ROUND + e->em_segEnd = SAMPLE184(param->samples); +#else + e->em_segEnd = param->samples; +#endif + + tmp = ((s32)param->volume * (s32)param->volume) >> 15; + + + e->em_volume = (s16) tmp; + e->em_pan = param->pan; + e->em_dryamt = n_eqpower[param->fxMix]; + e->em_wetamt = n_eqpower[N_EQPOWER_LENGTH - param->fxMix - 1]; + + if (param->samples) { + e->em_cvolL = 1; + e->em_cvolR = 1; + } else { + /* + * Attack time is zero. Simply set the + * volume. We don't want an attack segment. + */ + e->em_cvolL = (e->em_volume * n_eqpower[e->em_pan]) >> 15; + e->em_cvolR = (e->em_volume * + n_eqpower[N_EQPOWER_LENGTH - e->em_pan - 1]) >> 15; + } + e->rs_ratio = param->pitch; + } + + break; + + case (AL_FILTER_SET_FXAMT): + case (AL_FILTER_SET_PAN): + case (AL_FILTER_SET_VOLUME): + ptr = _pullSubFrame(e, &inp, &loutp, samples, ptr); + + if (e->em_delta >= e->em_segEnd){ + + /* + * We should have reached our target, calculate + * target in case e->em_segEnd was 0 + */ + e->em_ltgt = (e->em_volume * n_eqpower[e->em_pan]) >> 15; + e->em_rtgt = (e->em_volume * + n_eqpower[N_EQPOWER_LENGTH - e->em_pan - 1]) >> 15; + e->em_delta = e->em_segEnd; /* To prevent overflow */ + e->em_cvolL = e->em_ltgt; + e->em_cvolR = e->em_rtgt; + } else { + /* + * Estimate the current volume + */ + e->em_cvolL = _getVol(e->em_cvolL, e->em_delta, e->em_lratm, e->em_lratl); + e->em_cvolR = _getVol(e->em_cvolR, e->em_delta, e->em_rratm, e->em_rratl); + } + + /* + * We can't have volume of zero, because the envelope + * would never go anywhere from there + */ + if( e->em_cvolL == 0 ) e->em_cvolL = 1; + if( e->em_cvolR == 0 ) e->em_cvolR = 1; + + if (e->em_ctrlList->type == AL_FILTER_SET_PAN) + + /* + * This should result in a change to the current + * segment rate and target + */ + e->em_pan = (s16) e->em_ctrlList->data.i; + + if (e->em_ctrlList->type == AL_FILTER_SET_VOLUME){ + + /* + * Switching to a new segment + */ + e->em_delta = 0; + + /* + * Map volume non-linearly to give something close to + * loudness + */ + fVol = (e->em_ctrlList->data.i); + fVol = (fVol*fVol)>>15; + e->em_volume = (s16) fVol; + +#ifdef FINAL_ROUND + e->em_segEnd = SAMPLE184(e->em_ctrlList->moredata.i); +#else + e->em_segEnd = e->em_ctrlList->moredata.i; +#endif + + } + + if (e->em_ctrlList->type == AL_FILTER_SET_FXAMT){ + e->em_dryamt = n_eqpower[e->em_ctrlList->data.i]; + e->em_wetamt = n_eqpower[N_EQPOWER_LENGTH - e->em_ctrlList->data.i - 1]; + } + + /* + * Force a volume update + */ + e->em_first = 1; + break; + + case (AL_FILTER_START_VOICE): + { + ALStartParam *p = (ALStartParam *)e->em_ctrlList; + + /* + * Changing to PLAYING (since the previous state was + * persumable STOPPED, we'll just bump the output + * pointer rather than pull a subframe of zeros). + */ + if (p->unity) { + e->rs_upitch = 1; + } + + n_alLoadParam(e, AL_FILTER_SET_WAVETABLE, p->wave); + e->em_motion = AL_PLAYING; + } + break; + case (AL_FILTER_STOP_VOICE): + { + /* + * Changing to STOPPED and reset the filter + */ + ptr = _pullSubFrame(e, &inp, &loutp, samples, ptr); + n_alEnvmixerParam(e, AL_FILTER_RESET, 0); + } + break; + + case (AL_FILTER_FREE_VOICE): + { + N_ALFreeParam *param = (N_ALFreeParam *)e->em_ctrlList; + param->pvoice->offset = 0; + _n_freePVoice((N_PVoice *)param->pvoice); + } + break; +#if 1 + case (AL_FILTER_SET_PITCH): + ptr = _pullSubFrame(e, &inp, &loutp, samples, ptr); + e->rs_ratio = e->em_ctrlList->data.f; + break; + case (AL_FILTER_SET_UNITY_PITCH): + ptr = _pullSubFrame(e, &inp, &loutp, samples, ptr); + e->rs_upitch = 1; + break; + case (AL_FILTER_SET_WAVETABLE): + ptr = _pullSubFrame(e, &inp, &loutp, samples, ptr); + n_alLoadParam(e, AL_FILTER_SET_WAVETABLE, + (void *)e->em_ctrlList->data.i); + break; +#endif + default: + /* + * Pull the reuired number of samples and then pass the message + * on down the chain + */ + ptr = _pullSubFrame(e, &inp, &loutp, samples, ptr); + n_alEnvmixerParam(e, e->em_ctrlList->type, + (void *) e->em_ctrlList->data.i); + break; + } + loutp += (samples<<1); + outCount -= samples; + + /* + * put the param record back on the free list + */ + thisParam = e->em_ctrlList; + e->em_ctrlList = e->em_ctrlList->next; + if (e->em_ctrlList == 0) + e->em_ctrlTail = 0; + + _n_freeParam(thisParam); + + } + + ptr = _pullSubFrame(e, &inp, &loutp, outCount, ptr); + + /* + * Prevent overflow in e->em_delta + */ + if (e->em_delta > e->em_segEnd) + e->em_delta = e->em_segEnd; + +#ifdef AUD_PROFILE + PROFILE_AUD(env_num, env_cnt, env_max, env_min); +#endif + return ptr; +} + +s32 + n_alEnvmixerParam(N_PVoice *filter, s32 paramID, void *param) +{ + N_PVoice *e = filter; + + switch (paramID) { + case (AL_FILTER_ADD_UPDATE): + if (e->em_ctrlTail) { + e->em_ctrlTail->next = (ALParam *)param; + } else { + e->em_ctrlList = (ALParam *)param; + } + e->em_ctrlTail = (ALParam *)param; + break; + case (AL_FILTER_RESET): + e->em_first = 1; + e->em_motion = AL_STOPPED; + e->em_volume = 1; + e->em_segEnd = 0; + e->rs_delta = 0.0; + e->rs_first = 1; + e->rs_upitch = 0; + n_alLoadParam(e, AL_FILTER_RESET, param); + break; + case (AL_FILTER_START): + e->em_motion = AL_PLAYING; + break; + default: +#if 1 + n_alLoadParam(e, paramID, param); +#else + n_alResampleParam(e, paramID, param); +#endif + break; + } + return 0; +} + +static + Acmd* _pullSubFrame(N_PVoice *filter, s16 *inp, s16 *outp, s32 outCount, + Acmd *p) +{ + Acmd *ptr = p; + N_PVoice *e = filter; +#ifndef N_MICRO + s16 *inpp = AL_RESAMPLER_OUT; +#else + s16 *inpp = N_AL_RESAMPLER_OUT; +#endif + + /* filter must be playing and request non-zero output samples to pull. */ + if (e->em_motion != AL_PLAYING || !outCount) + return ptr; + + /* + * ask all filters upstream from us to build their command + * lists. + */ + + ptr = n_alResamplePull(e,inp, p); + + /* + * construct our portion of the command list + */ +#ifndef N_MICRO + aSetBuffer(ptr++, A_MAIN, *inp, AL_MAIN_L_OUT, FIXED_SAMPLE<<1); + aSetBuffer(ptr++, A_AUX, AL_MAIN_R_OUT , AL_AUX_L_OUT , + AL_AUX_R_OUT ); +#endif + + if (e->em_first){ + e->em_first = 0; + + /* + * Calculate derived parameters + */ + e->em_ltgt = (e->em_volume * n_eqpower[e->em_pan]) >> 15; + e->em_lratm = _getRate((f64)e->em_cvolL, (f64)e->em_ltgt, + e->em_segEnd, &(e->em_lratl)); + e->em_rtgt = (e->em_volume * + n_eqpower[N_EQPOWER_LENGTH - e->em_pan - 1]) >> 15; + e->em_rratm = _getRate((f64)e->em_cvolR, (f64)e->em_rtgt, e->em_segEnd, + &(e->em_rratl)); + +#ifndef N_MICRO + aSetVolume(ptr++, A_LEFT | A_VOL, e->em_cvolL, 0, 0); + aSetVolume(ptr++, A_RIGHT | A_VOL, e->em_cvolR, 0, 0); + aSetVolume(ptr++, A_LEFT | A_RATE, e->em_ltgt, e->em_lratm, e->em_lratl); + aSetVolume(ptr++, A_RIGHT | A_RATE, e->em_rtgt, e->em_rratm, e->em_rratl); + aSetVolume(ptr++, A_AUX, e->em_dryamt, 0, e->em_wetamt); + aEnvMixer (ptr++, A_INIT | A_AUX, osVirtualToPhysical(e->em_state)); + } + else + aEnvMixer(ptr++, A_CONTINUE | A_AUX, osVirtualToPhysical(e->em_state)); +#else + // needs to be on same line. + n_aSetVolume(ptr++, A_RATE, e->em_ltgt, e->em_lratm, e->em_lratl); n_aSetVolume(ptr++, A_LEFT | A_VOL, e->em_cvolL, e->em_dryamt, e->em_wetamt); n_aSetVolume(ptr++, A_RIGHT | A_VOL, e->em_rtgt, e->em_rratm, e->em_rratl); n_aEnvMixer (ptr++, A_INIT, e->em_cvolR, osVirtualToPhysical(e->em_state)); } else n_aEnvMixer(ptr++, A_CONTINUE, 0, osVirtualToPhysical(e->em_state)); +#endif + /* + * bump the input buffer pointer + */ + + *inp += (FIXED_SAMPLE<<1); + e->em_delta += FIXED_SAMPLE; + + return ptr; +} + +#ifndef N_MICRO +#define EXP_MASK 0x7f800000 +#define MANT_MASK 0x807fffff + +static + s16 _getRate(f64 vol, f64 tgt, s32 count, u16* ratel) +{ + s16 s; + + f64 invn = 1.0/count, eps, a, fs, mant; + s32 i_invn, ex, indx; + +#ifdef AUD_PROFILE + lastCnt[++cnt_index] = osGetCount(); +#endif + + if (count == 0){ + if (tgt >= vol){ + *ratel = 0xffff; + return 0x7fff; + } + else{ + *ratel = 0; + return 0; + } + } + + if (tgt < 1.0) + tgt = 1.0; + if (vol <= 0) vol = 1; /* zero and neg values not allowed */ + +#define NBITS (3) +#define NPOS (1<>= 1; + } + } + + a *= (a *= (a *= a)); + s = (s16) a; + *ratel = (s16)(0xffff * (a - (f32) s)); + +#ifdef AUD_PROFILE + PROFILE_AUD(rate_num, rate_cnt, rate_max, rate_min); +#endif + return (s16)a; + +} +#else +static +s16 _getRate(f64 vol, f64 tgt, s32 count, u16* ratel) +{ + s16 s, tmp; + f64 invn, a, f; + +#ifdef AUD_PROFILE + lastCnt[++cnt_index] = osGetCount(); +#endif + + if (count == 0){ + if (tgt >= vol){ + *ratel = 0xffff; + return 0x7fff; + } + else{ + *ratel = 0; + return 0x8000; + } + } + + invn = 1.0 / count; + + if (tgt < 1.0) + tgt = 1.0; + if (vol <= 0.0) vol = 1.0; /* zero and neg values not allowed */ + + a = (tgt - vol) * invn * 8.0; + s = (s16)a; + f = a - (f64)s; + s -= 1; + f += 1.0; + tmp = (s16)f; + s += tmp; + f -= (f64)tmp; + +#ifdef AUD_PROFILE + PROFILE_AUD( rate_num, rate_cnt, rate_max, rate_min); +#endif + + *ratel = (u16)(0xffff * f); + return s; +} +#endif + +#ifndef N_MICRO +static + f32 _getVol(f32 ivol, s32 samples, s16 ratem, u16 ratel) +{ + f32 r, a; + s32 i; + +#ifdef AUD_PROFILE + lastCnt[++cnt_index] = osGetCount(); +#endif + + /* + * Rate values are actually rate^8 + */ + samples >>=3; + if (samples == 0){ + return ivol; + } + r = ((f32) (ratem<<16) + (f32) ratel)/65536; + + a = 1.0; + for (i=0; i<32; i++){ + if( samples & 1 ) + a *= r; + samples >>= 1; + if (samples == 0) + break; + r *= r; + } + ivol *= a; +#ifdef AUD_PROFILE + PROFILE_AUD(vol_num, vol_cnt, vol_max, vol_min); +#endif + return ivol; +} +#else +static +s16 _getVol(s16 ivol, s32 samples, s16 ratem, u16 ratel) +{ + s32 tmp1; +#ifdef AUD_PROFILE + lastCnt[++cnt_index] = osGetCount(); +#endif + + samples >>= 3; + if (samples == 0){ + return ivol; + } + + tmp1 = ratel * samples; + tmp1 >>= 16; + tmp1 += ratem * samples; + ivol += tmp1; + +#ifdef AUD_PROFILE + PROFILE_AUD(vol_num, vol_cnt, vol_max, vol_min); +#endif + return ivol; +} +#endif diff --git a/yamls/us/rom.yaml b/yamls/us/rom.yaml index 33196b1..8850f50 100644 --- a/yamls/us/rom.yaml +++ b/yamls/us/rom.yaml @@ -89,7 +89,7 @@ - [0x45720, c] # music code - [0x46680, c] # speech code - [0x47AF0, c, libnaudio/n_synstopvoice] - - [0x47B60, c] # sound code + - [0x47B60, c, libnaudio/n_env] # sound code - [0x485C0, c] # moves code - [0x48C60, c] # move effects code - [0x490A0, c] # audio functions to 0x50C00 @@ -344,6 +344,8 @@ - [0x78890, data, rom_data_78890] - [0x79180, .data, libnaudio/n_sl] - [0x79190, data, rom_data_79190] + - [0x79200, .data, libnaudio/n_env] + - [0x79300, data, rom_data_79300] - [0x79630, .data, 49790] - [0x79660, data, rom_data_79660] - [0x79AE0, .data, 4CBC0] @@ -414,7 +416,8 @@ - [0x7D230, .rodata, 373A0] - [0x7D340, rodata, rom_rodata_7D340] - [0x7D3E0, rodata, rom_rodata_7D3E0] - - [0x7D880, rodata, rom_rodata_7D880] + - [0x7D880, .rodata, libnaudio/n_env] + - [0x7D8D0, rodata, rom_rodata_7D8D0] - [0x7DF80, rodata, rom_rodata_7DF80] - [0x7DFD0, .rodata, 49790] - [0x7E0C0, .rodata, libnaudio/n_synthesizer]