sound, add FM filtering

This commit is contained in:
kub 2022-04-14 17:32:40 +00:00
parent 21e0cd52e6
commit 68a950875c
13 changed files with 73 additions and 854 deletions

View file

@ -13,18 +13,13 @@
#include "../pico_int.h"
#include "mix.h"
#include "emu2413/emu2413.h"
#ifdef USE_BLIPPER
#include "blipper.h"
#else
#include "resampler.h"
#endif
void (*PsndMix_32_to_16l)(s16 *dest, s32 *src, int count) = mix_32_to_16l_stereo;
// master int buffer to mix to
// +1 for a fill triggered by an instruction overhanging into the next scanline
static s32 PsndBuffer[2*(53267+100)/50+2];
static s32 PsndBuffer[2*(54000+100)/50+2];
// cdda output buffer
s16 cdda_out_buffer[2*1152];
@ -38,11 +33,7 @@ OPLL old_opll;
static OPLL *opll = NULL;
unsigned YM2413_reg;
#ifdef USE_BLIPPER
static blipper_t *fmlblip, *fmrblip;
#else
static resampler_t *fmresampler;
#endif
PICO_INTERNAL void PsndInit(void)
{
@ -56,12 +47,7 @@ PICO_INTERNAL void PsndExit(void)
OPLL_delete(opll);
opll = NULL;
#ifdef USE_BLIPPER
blipper_free(fmlblip); fmlblip = NULL;
blipper_free(fmrblip); fmrblip = NULL;
#else
resampler_free(fmresampler); fmresampler = NULL;
#endif
}
PICO_INTERNAL void PsndReset(void)
@ -74,70 +60,7 @@ PICO_INTERNAL void PsndReset(void)
int (*PsndFMUpdate)(s32 *buffer, int length, int stereo, int is_buf_empty);
// FM polyphase FIR resampling
#ifdef USE_BLIPPER
#define FMFIR_TAPS 11
// resample FM from its native 53267Hz/52781Hz with the blipper library
static u32 ymmulinv;
int YM2612UpdateFIR(s32 *buffer, int length, int stereo, int is_buf_empty)
{
int mul = Pico.snd.fm_fir_mul, div = Pico.snd.fm_fir_div;
s32 *p = buffer, *q = buffer;
int ymlen;
int ret = 0;
if (length <= 0) return ret;
// FM samples needed: (length*div + div-blipper_read_phase(fmlblip)) / mul
ymlen = ((length*div + div-blipper_read_phase(fmlblip)) * ymmulinv) >> 32;
if (ymlen > 0)
ret = YM2612UpdateOne(p, ymlen, stereo, is_buf_empty);
if (stereo) {
blipper_push_long_samples(fmlblip, p , ymlen, 2, mul);
blipper_push_long_samples(fmrblip, p+1, ymlen, 2, mul);
blipper_read_long(fmlblip, q , blipper_read_avail(fmlblip), 2);
blipper_read_long(fmrblip, q+1, blipper_read_avail(fmrblip), 2);
} else {
blipper_push_long_samples(fmlblip, p , ymlen, 1, mul);
blipper_read_long(fmlblip, q , blipper_read_avail(fmlblip), 1);
}
return ret;
}
static void YM2612_setup_FIR(int inrate, int outrate, int stereo)
{
int mindiff = 999;
int diff, mul, div;
int maxdecim = 1500/FMFIR_TAPS;
// compute filter ratio with smallest error for a decent number of taps
for (div = maxdecim/2; div <= maxdecim; div++) {
mul = (outrate*div + inrate/2) / inrate;
diff = outrate*div/mul - inrate;
if (abs(diff) < abs(mindiff)) {
mindiff = diff;
Pico.snd.fm_fir_mul = mul;
Pico.snd.fm_fir_div = div;
}
}
ymmulinv = 0x100000000ULL / mul; /* 1/mul in Q32 */
printf("FM polyphase FIR ratio=%d/%d error=%.3f%%\n",
Pico.snd.fm_fir_mul, Pico.snd.fm_fir_div, 100.0*mindiff/inrate);
// create blipper (modified for polyphase resampling). Not really perfect for
// FM, but has SINC generator, a good window, and computes the filter in Q16.
blipper_free(fmlblip);
blipper_free(fmrblip);
fmlblip = blipper_new(FMFIR_TAPS, 0.85, 8.5, Pico.snd.fm_fir_div, 1000, NULL);
if (!stereo) return;
fmrblip = blipper_new(FMFIR_TAPS, 0.85, 8.5, Pico.snd.fm_fir_div, 1000, NULL);
}
#else
#define FMFIR_TAPS 8
#define FMFIR_TAPS 9
// resample FM from its native 53267Hz/52781Hz with polyphase FIR filter
static int ymchans;
@ -156,16 +79,17 @@ static void YM2612_setup_FIR(int inrate, int outrate, int stereo)
{
int mindiff = 999;
int diff, mul, div;
int maxmult = 30; // max interpolation factor
int minmult = 22, maxmult = 55; // min,max interpolation factor
// compute filter ratio with largest multiplier for smallest error
for (mul = maxmult/2; mul <= maxmult; mul++) {
for (mul = minmult; mul <= maxmult; mul++) {
div = (inrate*mul + outrate/2) / outrate;
diff = outrate*div/mul - inrate;
if (abs(diff) <= abs(mindiff)) {
if (abs(diff) < abs(mindiff)) {
mindiff = diff;
Pico.snd.fm_fir_mul = mul;
Pico.snd.fm_fir_div = div;
if (abs(mindiff) <= inrate/1000) break; // below error limit
}
}
printf("FM polyphase FIR ratio=%d/%d error=%.3f%%\n",
@ -173,9 +97,8 @@ static void YM2612_setup_FIR(int inrate, int outrate, int stereo)
resampler_free(fmresampler);
fmresampler = resampler_new(FMFIR_TAPS, Pico.snd.fm_fir_mul, Pico.snd.fm_fir_div,
0.85, 2.35, 2*inrate/50, stereo);
0.85, 2, 2*inrate/50, stereo);
}
#endif
// to be called after changing sound rate or chips
void PsndRerate(int preserve_state)
@ -184,6 +107,7 @@ void PsndRerate(int preserve_state)
int target_fps = Pico.m.pal ? 50 : 60;
int target_lines = Pico.m.pal ? 313 : 262;
int ym2612_clock = Pico.m.pal ? OSC_PAL/7 : OSC_NTSC/7;
int ym2612_rate = YM2612_NATIVE_RATE();
if (preserve_state) {
state = malloc(0x204);
@ -191,8 +115,8 @@ void PsndRerate(int preserve_state)
ym2612_pack_state();
memcpy(state, YM2612GetRegs(), 0x204);
}
if (PicoIn.opt & POPT_EN_FM_FILTER) {
int ym2612_rate = (ym2612_clock+(6*24)/2) / (6*24);
if ((PicoIn.opt & POPT_EN_FM_FILTER) && ym2612_rate != PicoIn.sndRate) {
// polyphase FIR resampler, resampling directly from native to output rate
YM2612Init(ym2612_clock, ym2612_rate,
((PicoIn.opt&POPT_DIS_FM_SSGEG) ? 0 : ST_SSG) |
((PicoIn.opt&POPT_EN_FM_DAC) ? ST_DAC : 0));