audio: improve cycle accuracy of SN76496+YM2612

This commit is contained in:
kub 2020-04-22 20:41:51 +02:00
parent 6432fb18ba
commit 09b96f9940
4 changed files with 32 additions and 56 deletions

View file

@ -391,7 +391,7 @@ static int get_scanline(int is_from_z80);
static void psg_write_68k(u32 d) static void psg_write_68k(u32 d)
{ {
// look for volume write and update if needed // look for volume write and update if needed
if ((d & 0x90) == 0x90 && Pico.snd.psg_line < Pico.m.scanline) if ((d & 0x90) == 0x90)
PsndDoPSG(Pico.m.scanline); PsndDoPSG(Pico.m.scanline);
SN76496Write(d); SN76496Write(d);
@ -401,8 +401,7 @@ static void psg_write_z80(u32 d)
{ {
if ((d & 0x90) == 0x90) { if ((d & 0x90) == 0x90) {
int scanline = get_scanline(1); int scanline = get_scanline(1);
if (Pico.snd.psg_line < scanline) PsndDoPSG(scanline);
PsndDoPSG(scanline);
} }
SN76496Write(d); SN76496Write(d);
@ -1061,7 +1060,7 @@ static int ym2612_write_local(u32 a, u32 d, int is_from_z80)
if (PicoIn.opt & POPT_EXT_FM) if (PicoIn.opt & POPT_EXT_FM)
return YM2612Write_940(a, d, get_scanline(is_from_z80)); return YM2612Write_940(a, d, get_scanline(is_from_z80));
#endif #endif
PsndDoFM(get_scanline(is_from_z80)); PsndDoFM(is_from_z80 ? z80_cyclesDone() : z80_cycles_from_68k());
return YM2612Write_(a, d); return YM2612Write_(a, d);
} }

View file

@ -436,12 +436,12 @@ struct PicoSound
short len_use; // adjusted short len_use; // adjusted
int len_e_add; // for non-int samples/frame int len_e_add; // for non-int samples/frame
int len_e_cnt; int len_e_cnt;
int dac_val, dac_val2; // last DAC sample unsigned int clkl_mult; // z80 clocks per line in Q20
unsigned int dac_mult; // z80 clocks per line in Q16 unsigned int smpl_mult; // samples per line in Q16
unsigned int dac_pos; // last DAC position in Q16 short dac_val, dac_val2; // last DAC sample
short psg_line; unsigned int dac_pos; // last DAC position in Q20
unsigned int fm_mult; // samples per line in Q16 unsigned int fm_pos; // last FM position in Q20
unsigned int fm_pos; // last FM position in Q16 unsigned int psg_pos; // last PSG position in Q16
}; };
// run tools/mkoffsets pico/pico_int_offs.h if you change these // run tools/mkoffsets pico/pico_int_offs.h if you change these

View file

@ -152,7 +152,7 @@ static void z80_sms_out(unsigned short a, unsigned char d)
case 0x40: case 0x40:
case 0x41: case 0x41:
if ((d & 0x90) == 0x90 && Pico.snd.psg_line < Pico.m.scanline) if ((d & 0x90) == 0x90);
PsndDoPSG(Pico.m.scanline); PsndDoPSG(Pico.m.scanline);
SN76496Write(d); SN76496Write(d);
break; break;

View file

@ -19,9 +19,6 @@ void (*PsndMix_32_to_16l)(short *dest, int *src, int count) = mix_32_to_16l_ster
// master int buffer to mix to // master int buffer to mix to
static int PsndBuffer[2*(44100+100)/50]; static int PsndBuffer[2*(44100+100)/50];
// dac, psg
static unsigned short dac_info[312+4]; // pos in sample buffer
// cdda output buffer // cdda output buffer
short cdda_out_buffer[2*1152]; short cdda_out_buffer[2*1152];
@ -29,23 +26,6 @@ short cdda_out_buffer[2*1152];
extern int *sn76496_regs; extern int *sn76496_regs;
static void dac_recalculate(void)
{
int lines = Pico.m.pal ? 313 : 262;
int i, pos;
pos = 0; // Q16
for(i = 0; i <= lines; i++)
{
dac_info[i] = ((pos+0x8000) >> 16); // round to nearest
pos += Pico.snd.fm_mult;
}
for (i = lines+1; i < sizeof(dac_info) / sizeof(dac_info[0]); i++)
dac_info[i] = dac_info[i-1];
}
PICO_INTERNAL void PsndReset(void) PICO_INTERNAL void PsndReset(void)
{ {
// PsndRerate calls YM2612Init, which also resets // PsndRerate calls YM2612Init, which also resets
@ -88,12 +68,9 @@ void PsndRerate(int preserve_state)
Pico.snd.len_e_cnt = 0; // Q16 Pico.snd.len_e_cnt = 0; // Q16
// samples per line (Q16) // samples per line (Q16)
Pico.snd.fm_mult = 65536LL * PicoIn.sndRate / (target_fps*target_lines); Pico.snd.smpl_mult = 65536LL * PicoIn.sndRate / (target_fps*target_lines);
// samples per z80 clock (Q20) // samples per z80 clock (Q20)
Pico.snd.dac_mult = 16 * Pico.snd.fm_mult * 15/7 / 488; Pico.snd.clkl_mult = 16 * Pico.snd.smpl_mult * 15/7 / 488;
// recalculate dac info
dac_recalculate();
// clear all buffers // clear all buffers
memset32(PsndBuffer, 0, sizeof(PsndBuffer)/4); memset32(PsndBuffer, 0, sizeof(PsndBuffer)/4);
@ -118,8 +95,6 @@ PICO_INTERNAL void PsndStartFrame(void)
Pico.snd.len_e_cnt -= 0x10000; Pico.snd.len_e_cnt -= 0x10000;
Pico.snd.len_use++; Pico.snd.len_use++;
} }
Pico.snd.psg_line = 0;
} }
PICO_INTERNAL void PsndDoDAC(int cyc_to) PICO_INTERNAL void PsndDoDAC(int cyc_to)
@ -128,7 +103,7 @@ PICO_INTERNAL void PsndDoDAC(int cyc_to)
int dout = ym2612.dacout; int dout = ym2612.dacout;
// number of samples to fill in buffer (Q20) // number of samples to fill in buffer (Q20)
len = (cyc_to * Pico.snd.dac_mult) - Pico.snd.dac_pos; len = (cyc_to * Pico.snd.clkl_mult) - Pico.snd.dac_pos;
// update position and calculate buffer offset and length // update position and calculate buffer offset and length
pos = (Pico.snd.dac_pos+0x80000) >> 20; pos = (Pico.snd.dac_pos+0x80000) >> 20;
@ -163,17 +138,18 @@ PICO_INTERNAL void PsndDoDAC(int cyc_to)
PICO_INTERNAL void PsndDoPSG(int line_to) PICO_INTERNAL void PsndDoPSG(int line_to)
{ {
int line_from = Pico.snd.psg_line; int pos, len;
int pos, pos1, len;
int stereo = 0; int stereo = 0;
pos = dac_info[line_from]; // Q16, number of samples since last call
pos1 = dac_info[line_to + 1]; len = ((line_to+1) * Pico.snd.smpl_mult) - Pico.snd.psg_pos;
len = pos1 - pos;
if (len <= 0) if (len <= 0)
return; return;
Pico.snd.psg_line = line_to + 1; // update position and calculate buffer offset and length
pos = (Pico.snd.psg_pos+0x8000) >> 16;
Pico.snd.psg_pos += len;
len = ((Pico.snd.psg_pos+0x8000) >> 16) - pos;
if (!PicoIn.sndOut || !(PicoIn.opt & POPT_EN_PSG)) if (!PicoIn.sndOut || !(PicoIn.opt & POPT_EN_PSG))
return; return;
@ -185,22 +161,22 @@ PICO_INTERNAL void PsndDoPSG(int line_to)
SN76496Update(PicoIn.sndOut + pos, len, stereo); SN76496Update(PicoIn.sndOut + pos, len, stereo);
} }
PICO_INTERNAL void PsndDoFM(int line_to) PICO_INTERNAL void PsndDoFM(int cyc_to)
{ {
int pos, len; int pos, len;
int stereo = 0; int stereo = 0;
// Q16, number of samples since last call // Q16, number of samples since last call
len = ((line_to-1) * Pico.snd.fm_mult) - Pico.snd.fm_pos; len = (cyc_to * Pico.snd.clkl_mult) - Pico.snd.fm_pos;
// don't do this too often (about every 4th scanline) // don't do this too often (about every 4th scanline)
if (len >> 16 <= PicoIn.sndRate >> 12) if (len >> 20 <= PicoIn.sndRate >> 12)
return; return;
// update position and calculate buffer offset and length // update position and calculate buffer offset and length
pos = (Pico.snd.fm_pos+0x8000) >> 16; pos = (Pico.snd.fm_pos+0x80000) >> 20;
Pico.snd.fm_pos += len; Pico.snd.fm_pos += len;
len = ((Pico.snd.fm_pos+0x8000) >> 16) - pos; len = ((Pico.snd.fm_pos+0x80000) >> 20) - pos;
// fill buffer // fill buffer
if (PicoIn.opt & POPT_EN_STEREO) { if (PicoIn.opt & POPT_EN_STEREO) {
@ -273,7 +249,7 @@ PICO_INTERNAL void PsndClear(void)
if (!(PicoIn.opt & POPT_EN_FM)) if (!(PicoIn.opt & POPT_EN_FM))
memset32(PsndBuffer, 0, PicoIn.opt & POPT_EN_STEREO ? len*2 : len); memset32(PsndBuffer, 0, PicoIn.opt & POPT_EN_STEREO ? len*2 : len);
// drop pos remainder to avoid rounding errors (not entirely correct though) // drop pos remainder to avoid rounding errors (not entirely correct though)
Pico.snd.dac_pos = Pico.snd.fm_pos = 0; Pico.snd.dac_pos = Pico.snd.fm_pos = Pico.snd.psg_pos = 0;
} }
@ -281,7 +257,7 @@ static int PsndRender(int offset, int length)
{ {
int *buf32; int *buf32;
int stereo = (PicoIn.opt & 8) >> 3; int stereo = (PicoIn.opt & 8) >> 3;
int fmlen = ((Pico.snd.fm_pos+0x8000) >> 16); int fmlen = ((Pico.snd.fm_pos+0x80000) >> 20);
int daclen = ((Pico.snd.dac_pos+0x80000) >> 20); int daclen = ((Pico.snd.dac_pos+0x80000) >> 20);
buf32 = PsndBuffer+(offset<<stereo); buf32 = PsndBuffer+(offset<<stereo);
@ -297,16 +273,19 @@ static int PsndRender(int offset, int length)
if (length-daclen > 0) { if (length-daclen > 0) {
short *dacbuf = PicoIn.sndOut + (daclen << stereo); short *dacbuf = PicoIn.sndOut + (daclen << stereo);
Pico.snd.dac_pos += (length-daclen) << 20; Pico.snd.dac_pos += (length-daclen) << 20;
for (; length-daclen > 0; daclen++) { *dacbuf++ += Pico.snd.dac_val2;
if (stereo) dacbuf++;
for (daclen++; length-daclen > 0; daclen++) {
*dacbuf++ += Pico.snd.dac_val; *dacbuf++ += Pico.snd.dac_val;
if (stereo) dacbuf++; if (stereo) dacbuf++;
} }
Pico.snd.dac_val2 = Pico.snd.dac_val;
} }
// Add in parts of the FM buffer not yet done // Add in parts of the FM buffer not yet done
if (length-fmlen > 0) { if (length-fmlen > 0) {
int *fmbuf = buf32 + ((fmlen-offset) << stereo); int *fmbuf = buf32 + ((fmlen-offset) << stereo);
Pico.snd.fm_pos += (length-fmlen) << 16; Pico.snd.fm_pos += (length-fmlen) << 20;
if (PicoIn.opt & POPT_EN_FM) if (PicoIn.opt & POPT_EN_FM)
YM2612UpdateOne(fmbuf, length-fmlen, stereo, 1); YM2612UpdateOne(fmbuf, length-fmlen, stereo, 1);
} }
@ -344,8 +323,6 @@ PICO_INTERNAL void PsndGetSamples(int y)
{ {
static int curr_pos = 0; static int curr_pos = 0;
if (ym2612.dacen)
PsndDoDAC(cycles_68k_to_z80(Pico.t.m68c_aim - Pico.t.m68c_frame_start));
PsndDoPSG(y - 1); PsndDoPSG(y - 1);
curr_pos = PsndRender(0, Pico.snd.len_use); curr_pos = PsndRender(0, Pico.snd.len_use);