mirror of
https://gitlab.com/ryandwyer/perfect-dark
synced 2026-06-09 12:35:46 -04:00
382 lines
9.3 KiB
C
382 lines
9.3 KiB
C
#include <ultra64.h>
|
|
#include <n_libaudio.h>
|
|
#include "synthInternals.h"
|
|
#include "naudio/n_synthInternals.h"
|
|
#include "constants.h"
|
|
#include "game/atan2f.h"
|
|
#include "bss.h"
|
|
#include "lib/lib_3a100.h"
|
|
#include "data.h"
|
|
#include "types.h"
|
|
|
|
#define RANGE 2.0f
|
|
|
|
Acmd *_n_loadOutputBuffer(ALFx *r, ALDelay *d, s32 arg2, s32 buff, Acmd *p);
|
|
Acmd *_n_loadBuffer(ALFx *r, s32 arg1, s16 *curr_ptr, s32 buff,s32 count, Acmd *p);
|
|
Acmd *_n_saveBuffer(ALFx *r, s32 arg1, s16 *curr_ptr, s32 buff, Acmd *p);
|
|
Acmd *_n_filterBuffer(ALLowPass *lp, s32 buff, s32 count, Acmd *p);
|
|
f32 _doModFunc(ALDelay *d, s32 count);
|
|
|
|
Acmd *n_alFxPull(s32 sampleOffset, Acmd *p, s32 arg2)
|
|
{
|
|
Acmd *ptr = p;
|
|
ALFx *r = (ALFx *)n_syn->auxBus[arg2].fx;
|
|
s16 i, buff1, buff2, input, output;
|
|
s16 *in_ptr, *out_ptr, *prev_out_ptr = 0;
|
|
ALDelay *d;
|
|
s32 sp58 = 1;
|
|
u32 j;
|
|
|
|
/*
|
|
* pull channels going into this effect first
|
|
*/
|
|
ptr = n_alAuxBusPull(sampleOffset, p, arg2, &sp58);
|
|
|
|
input = N_AL_AUX_L_OUT;
|
|
output = N_AL_AUX_R_OUT;
|
|
buff1 = N_AL_TEMP_0;
|
|
buff2 = N_AL_TEMP_1;
|
|
|
|
if (var8009c344[arg2] == 0) {
|
|
aMix(ptr++, 0, 0xc000, N_AL_AUX_L_OUT, input);
|
|
aMix(ptr++, 0, 0x4000, N_AL_AUX_R_OUT, input);
|
|
}
|
|
|
|
/* and write the mixed value to the delay line at r->input */
|
|
ptr = _n_saveBuffer(r, 0, r->input[0], input, ptr);
|
|
|
|
if (var8009c344[arg2]) {
|
|
ptr = _n_saveBuffer(r, 1, r->input[1], 0x930, ptr);
|
|
}
|
|
|
|
for (j = 0; j <= var8009c344[arg2]; j++) {
|
|
aClearBuffer(ptr++, output, FIXED_SAMPLE << 1); /* clear the AL_AUX_R_OUT */
|
|
|
|
for (i = 0; i < r->section_count; i++) {
|
|
d = &r->delay[i]; /* get the ALDelay structure */
|
|
in_ptr = &r->input[j][-d->input];
|
|
out_ptr = &r->input[j][-d->output];
|
|
|
|
if (var8009c346[arg2] && var8009c344[arg2]) {
|
|
d->ffcoef = -d->ffcoef;
|
|
d->fbcoef = -d->fbcoef;
|
|
}
|
|
|
|
if (in_ptr == prev_out_ptr) {
|
|
s16 t = buff2;
|
|
buff2 = buff1;
|
|
buff1 = t;
|
|
} else { /* load data at in_ptr into buff1 */
|
|
ptr = _n_loadBuffer(r, j, in_ptr, buff1, FIXED_SAMPLE, ptr);
|
|
}
|
|
|
|
ptr = _n_loadOutputBuffer(r, d, j, buff2, ptr);
|
|
|
|
if (d->ffcoef) {
|
|
aMix(ptr++, 0, (u16)d->ffcoef, buff1, buff2);
|
|
|
|
if (!d->rs && !d->lp) {
|
|
ptr = _n_saveBuffer(r, j, out_ptr, buff2, ptr);
|
|
}
|
|
}
|
|
|
|
if (d->fbcoef) {
|
|
aMix(ptr++, 0, (u16)d->fbcoef, buff2, buff1);
|
|
ptr = _n_saveBuffer(r, j, in_ptr, buff1, ptr);
|
|
}
|
|
|
|
if (d->lp) {
|
|
ptr = _n_filterBuffer(d->lp, j, buff2, ptr);
|
|
}
|
|
|
|
if (!d->rs) {
|
|
ptr = _n_saveBuffer(r, j, out_ptr, buff2, ptr);
|
|
}
|
|
|
|
if (d->gain) {
|
|
if (var8009c344[arg2]) {
|
|
aMix(ptr++, 0, (u16)d->gain, buff2, output);
|
|
} else {
|
|
u32 sp34 = d->gain * 1.4141999483109f;
|
|
|
|
if (sp34 > 0x7fff) {
|
|
sp34 = 0x7fff;
|
|
}
|
|
|
|
aMix(ptr++, 0, (u16)sp34, buff2, output);
|
|
}
|
|
}
|
|
|
|
prev_out_ptr = &r->input[j][d->output];
|
|
}
|
|
|
|
if (var8009c344[arg2] && j == 0) {
|
|
ptr = _n_loadBuffer(r, 1, r->input[1], input, FIXED_SAMPLE, ptr);
|
|
|
|
if (var8009c346[arg2]) {
|
|
aMix(ptr++, 0, 0x5a82, output, 0x650);
|
|
} else {
|
|
aMix(ptr++, 0, 0x5a82, output, 0x4e0);
|
|
}
|
|
} else {
|
|
// empty
|
|
}
|
|
|
|
/*
|
|
* output already in AL_AUX_R_OUT
|
|
* just copy to AL_AUX_L_OUT
|
|
*/
|
|
aDMEMMove(ptr++, output, N_AL_AUX_L_OUT, FIXED_SAMPLE << 1);
|
|
|
|
/*
|
|
* bump the master delay line input pointer
|
|
* modulo the length
|
|
*/
|
|
r->input[j] += FIXED_SAMPLE;
|
|
|
|
if (r->input[j] > &r->base[j][r->length]) {
|
|
r->input[j] -= r->length;
|
|
}
|
|
}
|
|
|
|
return ptr;
|
|
}
|
|
|
|
/*
|
|
* This routine gets called by alSynSetFXParam. No checking takes place to
|
|
* verify the validity of the paramID or the param value. input and output
|
|
* values must be 8 byte aligned, so round down any param passed.
|
|
*/
|
|
s32 n_alFxParamHdl(void *filter, s32 paramID, void *param)
|
|
{
|
|
ALFx *f = (ALFx *) filter;
|
|
s32 p = paramID & 7;
|
|
s32 s = paramID >> 3;
|
|
s32 val = *(s32*)param;
|
|
f32 rsgain;
|
|
|
|
if (s >= f->section_count) {
|
|
return 0;
|
|
}
|
|
|
|
#define INPUT_PARAM 0
|
|
#define OUTPUT_PARAM 1
|
|
#define FBCOEF_PARAM 2
|
|
#define FFCOEF_PARAM 3
|
|
#define GAIN_PARAM 4
|
|
#define CHORUSRATE_PARAM 5
|
|
#define CHORUSDEPTH_PARAM 6
|
|
#define LPFILT_PARAM 7
|
|
|
|
switch (p) {
|
|
case INPUT_PARAM:
|
|
f->delay[s].input = ((s32)val * n_syn->outputRate / 1000) & 0xfffffff8;
|
|
break;
|
|
case OUTPUT_PARAM:
|
|
f->delay[s].output = ((s32)val * n_syn->outputRate / 1000) & 0xfffffff8;
|
|
break;
|
|
case FBCOEF_PARAM:
|
|
f->delay[s].fbcoef = (s16)val;
|
|
break;
|
|
case FFCOEF_PARAM:
|
|
f->delay[s].ffcoef = (s16)val;
|
|
break;
|
|
case GAIN_PARAM:
|
|
f->delay[s].gain = (s16)val;
|
|
break;
|
|
case CHORUSRATE_PARAM:
|
|
f->delay[s].rsinc = ((((f32)val)/1000) * RANGE)/n_syn->outputRate;
|
|
break;
|
|
case CHORUSDEPTH_PARAM:
|
|
rsgain = val;
|
|
break;
|
|
case LPFILT_PARAM:
|
|
if (f->delay[s].lp) {
|
|
f->delay[s].lp->fc = (s16)val;
|
|
_init_lpfilter(f->delay[s].lp);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (f->delay[s].input >= f->length - 16) {
|
|
f->delay[s].input = f->length - 16;
|
|
}
|
|
|
|
if (f->delay[s].input >= f->length - 8) {
|
|
f->delay[s].input = f->length - 8;
|
|
}
|
|
|
|
if (f->delay[s].input >= f->delay[s].output) {
|
|
f->delay[s].output = f->delay[s].input + 8;
|
|
}
|
|
|
|
/**
|
|
* the following constant is derived from:
|
|
*
|
|
* ratio = 2^(cents/1200)
|
|
*
|
|
* and therefore for hundredths of a cent
|
|
* x
|
|
* ln(ratio) = ---------------
|
|
* (120,000)/ln(2)
|
|
* where
|
|
* 120,000/ln(2) = 173123.40...
|
|
*/
|
|
#define CONVERT 173123.404906676f
|
|
#define LENGTH (f->delay[s].output - f->delay[s].input)
|
|
|
|
if (f->delay[s].rs) {
|
|
if (p != 6) {
|
|
if (LENGTH != 0) {
|
|
rsgain = (f32)f->delay[s].rsgain / (f->delay[s].output - f->delay[s].input) * CONVERT;
|
|
} else {
|
|
rsgain = 0;
|
|
}
|
|
}
|
|
|
|
f->delay[s].rsgain = (f->delay[s].output - f->delay[s].input) * (rsgain / CONVERT);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
Acmd *_n_loadOutputBuffer(ALFx *r, ALDelay *d, s32 arg2, s32 buff, Acmd *p)
|
|
{
|
|
Acmd *ptr = p;
|
|
s32 ratio, count, rbuff = N_AL_TEMP_2;
|
|
s16 *out_ptr;
|
|
f32 fincount, fratio, delta;
|
|
s32 ramalign = 0, length;
|
|
s32 incount = FIXED_SAMPLE;
|
|
s16 tmp;
|
|
|
|
if (d->rs) {
|
|
length = d->output - d->input;
|
|
delta = _doModFunc(d, incount);
|
|
delta /= length;
|
|
delta = (s32)(delta * UNITY_PITCH);
|
|
delta = delta / UNITY_PITCH;
|
|
fratio = 1.0f - delta;
|
|
fincount = d->rs->delta + (fratio * (f32)incount);
|
|
count = (s32) fincount;
|
|
d->rs->delta = fincount - (f32)count;
|
|
out_ptr = &r->input[arg2][-(d->output - d->rsdelta)];
|
|
ramalign = ((s32)out_ptr & 0x7) >> 1;
|
|
ptr = _n_loadBuffer(r, arg2, out_ptr - ramalign, rbuff, count + ramalign, ptr);
|
|
|
|
ratio = (s32)(fratio * UNITY_PITCH);
|
|
|
|
tmp = buff >> 8;
|
|
n_aResample(ptr++, osVirtualToPhysical(d->rs->state[arg2]), d->rs->first, ratio, rbuff + (ramalign << 1), tmp);
|
|
|
|
d->rs->first = 0;
|
|
d->rsdelta += count - incount;
|
|
} else {
|
|
out_ptr = &r->input[arg2][-d->output];
|
|
ptr = _n_loadBuffer(r, arg2, out_ptr, buff, FIXED_SAMPLE, ptr);
|
|
}
|
|
|
|
return ptr;
|
|
}
|
|
|
|
Acmd *_n_loadBuffer(ALFx *r, s32 arg1, s16 *curr_ptr, s32 buff,s32 count, Acmd *p)
|
|
{
|
|
Acmd *ptr = p;
|
|
s32 after_end, before_end;
|
|
s16 *updated_ptr, *delay_end;
|
|
|
|
delay_end = &r->base[arg1][r->length];
|
|
|
|
if (curr_ptr < r->base[arg1]) {
|
|
curr_ptr += r->length;
|
|
}
|
|
|
|
updated_ptr = curr_ptr + count;
|
|
|
|
if (updated_ptr > delay_end) {
|
|
after_end = updated_ptr - delay_end;
|
|
before_end = delay_end - curr_ptr;
|
|
|
|
n_aLoadBuffer(ptr++, before_end << 1, buff, osVirtualToPhysical(curr_ptr));
|
|
n_aLoadBuffer(ptr++, after_end << 1, buff + (before_end << 1), osVirtualToPhysical(r->base[arg1]));
|
|
} else {
|
|
n_aLoadBuffer(ptr++, count << 1, buff, osVirtualToPhysical(curr_ptr));
|
|
}
|
|
|
|
return ptr;
|
|
}
|
|
|
|
Acmd *_n_saveBuffer(ALFx *r, s32 arg1, s16 *curr_ptr, s32 buff, Acmd *p)
|
|
{
|
|
Acmd *ptr = p;
|
|
s32 after_end, before_end;
|
|
s16 *updated_ptr, *delay_end;
|
|
|
|
delay_end = &r->base[arg1][r->length];
|
|
|
|
if (curr_ptr < r->base[arg1]) { /* probably just security */
|
|
curr_ptr += r->length; /* shouldn't occur */
|
|
}
|
|
|
|
updated_ptr = curr_ptr + FIXED_SAMPLE;
|
|
|
|
if (updated_ptr > delay_end) { /* if the data wraps past end of r->base */
|
|
after_end = updated_ptr - delay_end;
|
|
before_end = delay_end - curr_ptr;
|
|
|
|
n_aSaveBuffer(ptr++, before_end << 1, buff, osVirtualToPhysical(curr_ptr));
|
|
n_aSaveBuffer(ptr++, after_end << 1, buff + (before_end << 1), osVirtualToPhysical(r->base[arg1]));
|
|
} else {
|
|
n_aSaveBuffer(ptr++, FIXED_SAMPLE << 1, buff, osVirtualToPhysical(curr_ptr));
|
|
}
|
|
|
|
return ptr;
|
|
}
|
|
|
|
Acmd *_n_filterBuffer(ALLowPass *lp, s32 buff, s32 count, Acmd *p)
|
|
{
|
|
Acmd *ptr = p;
|
|
s16 tmp = count >> 8;
|
|
|
|
n_aLoadADPCM(ptr++, 32, osVirtualToPhysical(lp->fcvec.fccoef));
|
|
n_aPoleFilter(ptr++, lp->first, lp->fgain, tmp, osVirtualToPhysical(lp->fstate[buff]));
|
|
lp->first = 0;
|
|
|
|
return ptr;
|
|
}
|
|
|
|
/**
|
|
* Generate a triangle wave from -1 to 1, and find the current position
|
|
* in the wave. (Rate of the wave is controlled by d->rsinc, which is chorus
|
|
* rate) Multiply the current triangle wave value by d->rsgain, (chorus depth)
|
|
* which is expressed in number of samples back from output pointer the chorus
|
|
* should go at it's full chorus. In otherwords, this function returns a number
|
|
* of samples the output pointer should modulate backwards.
|
|
*/
|
|
f32 _doModFunc(ALDelay *d, s32 count)
|
|
{
|
|
f32 val;
|
|
|
|
/*
|
|
* generate bipolar sawtooth
|
|
* from -RANGE to +RANGE
|
|
*/
|
|
d->rsval += d->rsinc * count;
|
|
d->rsval = (d->rsval > RANGE) ? d->rsval - (RANGE * 2) : d->rsval;
|
|
|
|
/*
|
|
* convert to monopolar triangle
|
|
* from 0 to RANGE
|
|
*/
|
|
val = d->rsval;
|
|
val = (val < 0) ? -val : val;
|
|
|
|
/*
|
|
* convert to bipolar triangle
|
|
* from -1 to 1
|
|
*/
|
|
val -= RANGE/2;
|
|
|
|
return d->rsgain * val;
|
|
}
|