sys16b/upd7759: add 2000hz lp filter for less hollow sound

This commit is contained in:
dinkc64 2021-01-02 02:03:36 -05:00
parent 37f9687904
commit 1cca6fa54e
4 changed files with 145 additions and 11 deletions

View File

@ -2064,6 +2064,7 @@ INT32 System16Init()
UPD7759SetDrqCallback(0, System16UPD7759DrqCallback);
UPD7759SetSyncCallback(0, ZetTotalCycles, 5000000);
UPD7759SetRoute(0, 0.48, BURN_SND_ROUTE_BOTH);
UPD7759SetFilter(0, 2000);
BurnTimerAttachZet(5000000);
}

104
src/burn/snd/biquad.h Normal file
View File

@ -0,0 +1,104 @@
// (also appears in k054539.cpp, d_spectrum.cpp c/o dink)
// direct form II(transposed) biquadradic filter, needed for delay(echo) effect's filter taps -dink
enum { FILT_HIGHPASS = 0, FILT_LOWPASS = 1, FILT_LOWSHELF = 2, FILT_HIGHSHELF = 3 };
struct BIQ {
double a0;
double a1;
double a2;
double b1;
double b2;
double q;
double z1;
double z2;
double frequency;
double samplerate;
double output;
float filter(float input) {
output = input * a0 + z1;
z1 = input * a1 + z2 - b1 * output;
z2 = input * a2 - b2 * output;
return (float)output;
}
void reset() {
z1 = 0.0; z2 = 0.0; output = 0.0;
}
void exit() {
reset();
}
void init(INT32 type, INT32 sample_rate, INT32 freqhz, double q_, double gain) {
samplerate = sample_rate;
frequency = freqhz;
q = q_;
reset();
double k = tan(3.14159265358979323846 * frequency / samplerate);
double norm = 1 / (1 + k / q + k * k);
double v = pow(10, fabs(gain) / 20);
switch (type) {
case FILT_HIGHPASS:
{
a0 = 1 * norm;
a1 = -2 * a0;
a2 = a0;
b1 = 2 * (k * k - 1) * norm;
b2 = (1 - k / q + k * k) * norm;
}
break;
case FILT_LOWPASS:
{
a0 = k * k * norm;
a1 = 2 * a0;
a2 = a0;
b1 = 2 * (k * k - 1) * norm;
b2 = (1 - k / q + k * k) * norm;
}
break;
case FILT_LOWSHELF:
{
if (gain >= 0) {
norm = 1 / (1 + sqrt(2.0) * k + k * k);
a0 = (1 + sqrt(2*v) * k + v * k * k) * norm;
a1 = 2 * (v * k * k - 1) * norm;
a2 = (1 - sqrt(2*v) * k + v * k * k) * norm;
b1 = 2 * (k * k - 1) * norm;
b2 = (1 - sqrt(2.0) * k + k * k) * norm;
} else {
norm = 1 / (1 + sqrt(2*v) * k + v * k * k);
a0 = (1 + sqrt(2.0) * k + k * k) * norm;
a1 = 2 * (k * k - 1) * norm;
a2 = (1 - sqrt(2.0) * k + k * k) * norm;
b1 = 2 * (v * k * k - 1) * norm;
b2 = (1 - sqrt(2*v) * k + v * k * k) * norm;
}
}
break;
case FILT_HIGHSHELF:
{
if (gain >= 0) {
norm = 1 / (1 + sqrt(2.0) * k + k * k);
a0 = (v + sqrt(2*v) * k + k * k) * norm;
a1 = 2 * (k * k - v) * norm;
a2 = (v - sqrt(2*v) * k + k * k) * norm;
b1 = 2 * (k * k - 1) * norm;
b2 = (1 - sqrt(2.0) * k + k * k) * norm;
} else {
norm = 1 / (v + sqrt(2*v) * k + k * k);
a0 = (1 + sqrt(2.0) * k + k * k) * norm;
a1 = 2 * (k * k - 1) * norm;
a2 = (1 - sqrt(2.0) * k + k * k) * norm;
b1 = 2 * (k * k - v) * norm;
b2 = (v - sqrt(2*v) * k + k * k) * norm;
}
}
break;
}
}
};
// end biquad filter

View File

@ -5,6 +5,7 @@
#include "downsample.h"
#include "timer.h"
#include <math.h>
#include "biquad.h"
#define FRAC_BITS 20
#define FRAC_ONE (1 << FRAC_BITS)
@ -75,13 +76,13 @@ struct upd7759_chip
Downsampler resamp;
INT16 * out_buf;
INT16 * out_buf_linear;
INT16 * out_buf_linear_resampled;
INT32 out_buf_size;
INT32 out_pos;
// filter
INT32 filt_sa;
INT32 filt_sb;
INT32 filt_prev;
BIQ biquadL;
BIQ biquadR;
// stream sync
INT32 (*pTotalCyclesCB)();
@ -474,8 +475,16 @@ void UPD7759Render(INT32 chip, INT16 *pSoundBuf, INT32 samples)
Chip->out_buf[cpos] = 0;
}
// resample to host rate & mixdown
Chip->resamp.resample(Chip->out_buf_linear, pSoundBuf, samples, Chip->volume, Chip->output_dir);
// resample to host rate
Chip->resamp.resample(Chip->out_buf_linear, Chip->out_buf_linear_resampled, samples, Chip->volume, Chip->output_dir);
// filter and mix into pSoundBuf
for (INT32 i = 0; i < samples; i++) {
INT32 l = Chip->biquadL.filter(Chip->out_buf_linear_resampled[i*2 + 0]);
INT32 r = Chip->biquadR.filter(Chip->out_buf_linear_resampled[i*2 + 1]);
pSoundBuf[i*2 + 0] = BURN_SND_CLIP(pSoundBuf[i*2 + 0] + l);
pSoundBuf[i*2 + 1] = BURN_SND_CLIP(pSoundBuf[i*2 + 0] + r);
}
Chip->sample_counts = 0; // ready for next frame!
}
@ -536,22 +545,25 @@ void UPD7759Init(INT32 chip, INT32 clock, UINT8* pSoundData)
SlaveMode = 0;
Chip->ChipNum = chip;
Chip->resamp.init(clock / 4, nBurnSoundRate, 1);
// init resampler
Chip->resamp.init(clock / 4, nBurnSoundRate, 0);
// Init 2khz lp filter coefficient @ (clock / 4)hz
double omeg = exp(-2.0 * 3.1415926 * 2000 / (clock / 4));
Chip->filt_sa = (INT32)(omeg * (double)(1 << 12));
Chip->filt_sb = (1 << 12) - Chip->filt_sa;
// init filter (basically passthru w/these settings)
Chip->biquadL.init(FILT_LOWPASS, nBurnSoundRate, 15000, 0.554, 0.0);
Chip->biquadR.init(FILT_LOWPASS, nBurnSoundRate, 15000, 0.554, 0.0);
Chip->step = (INT32)(4 * FRAC_ONE);
Chip->state = STATE_IDLE;
Chip->clock_period = 1.0 / (double)clock;
// our 16bit (INT16) output buffer is 2 frames in length @ 640,000 kHz
// out_buf - circular buffer @ clock/4 rate
// out_buf_linear - linearized right before resampling and rendering
// out_buf_linear_resampled - resampled and ready to be filtered
Chip->out_buf_size = (clock / 4) * 100 / (nBurnFPS / 2); // word-size (byte size = *2)
Chip->out_buf = (INT16*)BurnMalloc(Chip->out_buf_size * sizeof(INT16));
Chip->out_buf_linear = (INT16*)BurnMalloc(Chip->out_buf_size * sizeof(INT16));
Chip->out_buf_linear_resampled = (INT16*)BurnMalloc(Chip->out_buf_size * sizeof(INT16));
Chip->out_pos = 0;
if (pSoundData) {
@ -572,6 +584,19 @@ void UPD7759Init(INT32 chip, INT32 clock, UINT8* pSoundData)
UPD7759Reset();
}
void UPD7759SetFilter(INT32 chip, INT32 nCutOff)
{
#if defined FBNEO_DEBUG
if (!DebugSnd_UPD7759Initted) bprintf(PRINT_ERROR, _T("UPD7759SetFilter called without init\n"));
if (chip > nNumChips) bprintf(PRINT_ERROR, _T("UPD7759SetFilter called with invalid chip %i\n"), chip);
#endif
Chip = Chips[chip];
Chip->biquadL.init(FILT_LOWPASS, nBurnSoundRate, nCutOff, 0.554, 0.0);
Chip->biquadR.init(FILT_LOWPASS, nBurnSoundRate, nCutOff, 0.554, 0.0);
}
void UPD7759SetRoute(INT32 chip, double nVolume, INT32 nRouteDir)
{
#if defined FBNEO_DEBUG
@ -730,8 +755,11 @@ void UPD7759Exit()
{
Chip = Chips[i];
if (Chip) {
Chip->biquadL.exit();
Chip->biquadR.exit();
BurnFree(Chip->out_buf);
BurnFree(Chip->out_buf_linear);
BurnFree(Chip->out_buf_linear_resampled);
BurnFree(Chips[i]);
}
}

View File

@ -7,6 +7,7 @@ void UPD7759Render(INT32 chip, INT16 *pSoundBuf, INT32 samples); // render singl
void UPD7759Reset();
void UPD7759Init(INT32 chip, INT32 clock, UINT8* pSoundData);
void UPD7759SetRoute(INT32 chip, double nVolume, INT32 nRouteDir);
void UPD7759SetFilter(INT32 chip, INT32 nCutOff);
void UPD7759SetDrqCallback(INT32 chip, drqcallback Callback);
void UPD7759SetSyncCallback(INT32 chip, INT32 (*pCPUCyclesCB)(), INT32 nCPUMhz);
INT32 UPD7759BusyRead(INT32 chip);