audio: improve cycle accuracy of SN76496+YM2612
authorkub <derkub@gmail.com>
Wed, 22 Apr 2020 18:41:51 +0000 (20:41 +0200)
committerkub <derkub@gmail.com>
Wed, 22 Apr 2020 19:49:48 +0000 (21:49 +0200)
pico/memory.c
pico/pico_int.h
pico/sms.c
pico/sound/sound.c

index 0fa7b8d..e1afb4d 100644 (file)
@@ -391,7 +391,7 @@ static int get_scanline(int is_from_z80);
 static void psg_write_68k(u32 d)\r
 {\r
   // look for volume write and update if needed\r
-  if ((d & 0x90) == 0x90 && Pico.snd.psg_line < Pico.m.scanline)\r
+  if ((d & 0x90) == 0x90)\r
     PsndDoPSG(Pico.m.scanline);\r
 \r
   SN76496Write(d);\r
@@ -401,8 +401,7 @@ static void psg_write_z80(u32 d)
 {\r
   if ((d & 0x90) == 0x90) {\r
     int scanline = get_scanline(1);\r
-    if (Pico.snd.psg_line < scanline)\r
-      PsndDoPSG(scanline);\r
+    PsndDoPSG(scanline);\r
   }\r
 \r
   SN76496Write(d);\r
@@ -1061,7 +1060,7 @@ static int ym2612_write_local(u32 a, u32 d, int is_from_z80)
   if (PicoIn.opt & POPT_EXT_FM)\r
     return YM2612Write_940(a, d, get_scanline(is_from_z80));\r
 #endif\r
-  PsndDoFM(get_scanline(is_from_z80));\r
+  PsndDoFM(is_from_z80 ? z80_cyclesDone() : z80_cycles_from_68k());\r
   return YM2612Write_(a, d);\r
 }\r
 \r
index 8a4aa30..7539379 100644 (file)
@@ -436,12 +436,12 @@ struct PicoSound
   short len_use;                        // adjusted\r
   int len_e_add;                        // for non-int samples/frame\r
   int len_e_cnt;\r
-  int dac_val, dac_val2;                // last DAC sample\r
-  unsigned int dac_mult;                // z80 clocks per line in Q16\r
-  unsigned int dac_pos;                 // last DAC position in Q16\r
-  short psg_line;\r
-  unsigned int fm_mult;                 // samples per line in Q16\r
-  unsigned int fm_pos;                  // last FM position in Q16\r
+  unsigned int clkl_mult;               // z80 clocks per line in Q20\r
+  unsigned int smpl_mult;               // samples per line in Q16\r
+  short dac_val, dac_val2;              // last DAC sample\r
+  unsigned int dac_pos;                 // last DAC position in Q20\r
+  unsigned int fm_pos;                  // last FM position in Q20\r
+  unsigned int psg_pos;                 // last PSG position in Q16\r
 };\r
 \r
 // run tools/mkoffsets pico/pico_int_offs.h if you change these\r
index b016f19..901f2f5 100644 (file)
@@ -152,7 +152,7 @@ static void z80_sms_out(unsigned short a, unsigned char d)
 
     case 0x40:
     case 0x41:
-      if ((d & 0x90) == 0x90 && Pico.snd.psg_line < Pico.m.scanline)
+      if ((d & 0x90) == 0x90);
         PsndDoPSG(Pico.m.scanline);
       SN76496Write(d);
       break;
index 57d9c2e..eb10f36 100644 (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\r
 static int PsndBuffer[2*(44100+100)/50];\r
 \r
-// dac, psg\r
-static unsigned short dac_info[312+4]; // pos in sample buffer\r
-\r
 // cdda output buffer\r
 short cdda_out_buffer[2*1152];\r
 \r
@@ -29,23 +26,6 @@ short cdda_out_buffer[2*1152];
 extern int *sn76496_regs;\r
 \r
 \r
-static void dac_recalculate(void)\r
-{\r
-  int lines = Pico.m.pal ? 313 : 262;\r
-  int i, pos;\r
-\r
-  pos = 0; // Q16\r
-\r
-  for(i = 0; i <= lines; i++)\r
-  {\r
-    dac_info[i] = ((pos+0x8000) >> 16); // round to nearest\r
-    pos += Pico.snd.fm_mult;\r
-  }\r
-  for (i = lines+1; i < sizeof(dac_info) / sizeof(dac_info[0]); i++)\r
-    dac_info[i] = dac_info[i-1];\r
-}\r
-\r
-\r
 PICO_INTERNAL void PsndReset(void)\r
 {\r
   // PsndRerate calls YM2612Init, which also resets\r
@@ -88,12 +68,9 @@ void PsndRerate(int preserve_state)
   Pico.snd.len_e_cnt = 0; // Q16\r
 \r
   // samples per line (Q16)\r
-  Pico.snd.fm_mult = 65536LL * PicoIn.sndRate / (target_fps*target_lines);\r
+  Pico.snd.smpl_mult = 65536LL * PicoIn.sndRate / (target_fps*target_lines);\r
   // samples per z80 clock (Q20)\r
-  Pico.snd.dac_mult = 16 * Pico.snd.fm_mult * 15/7 / 488;\r
-\r
-  // recalculate dac info\r
-  dac_recalculate();\r
+  Pico.snd.clkl_mult = 16 * Pico.snd.smpl_mult * 15/7 / 488;\r
 \r
   // clear all buffers\r
   memset32(PsndBuffer, 0, sizeof(PsndBuffer)/4);\r
@@ -118,8 +95,6 @@ PICO_INTERNAL void PsndStartFrame(void)
     Pico.snd.len_e_cnt -= 0x10000;\r
     Pico.snd.len_use++;\r
   }\r
-\r
-  Pico.snd.psg_line = 0;\r
 }\r
 \r
 PICO_INTERNAL void PsndDoDAC(int cyc_to)\r
@@ -128,7 +103,7 @@ PICO_INTERNAL void PsndDoDAC(int cyc_to)
   int dout = ym2612.dacout;\r
 \r
   // number of samples to fill in buffer (Q20)\r
-  len = (cyc_to * Pico.snd.dac_mult) - Pico.snd.dac_pos;\r
+  len = (cyc_to * Pico.snd.clkl_mult) - Pico.snd.dac_pos;\r
 \r
   // update position and calculate buffer offset and length\r
   pos = (Pico.snd.dac_pos+0x80000) >> 20;\r
@@ -163,17 +138,18 @@ PICO_INTERNAL void PsndDoDAC(int cyc_to)
 \r
 PICO_INTERNAL void PsndDoPSG(int line_to)\r
 {\r
-  int line_from = Pico.snd.psg_line;\r
-  int pos, pos1, len;\r
+  int pos, len;\r
   int stereo = 0;\r
 \r
-  pos  = dac_info[line_from];\r
-  pos1 = dac_info[line_to + 1];\r
-  len = pos1 - pos;\r
+  // Q16, number of samples since last call\r
+  len = ((line_to+1) * Pico.snd.smpl_mult) - Pico.snd.psg_pos;\r
   if (len <= 0)\r
     return;\r
 \r
-  Pico.snd.psg_line = line_to + 1;\r
+  // update position and calculate buffer offset and length\r
+  pos = (Pico.snd.psg_pos+0x8000) >> 16;\r
+  Pico.snd.psg_pos += len;\r
+  len = ((Pico.snd.psg_pos+0x8000) >> 16) - pos;\r
 \r
   if (!PicoIn.sndOut || !(PicoIn.opt & POPT_EN_PSG))\r
     return;\r
@@ -185,22 +161,22 @@ PICO_INTERNAL void PsndDoPSG(int line_to)
   SN76496Update(PicoIn.sndOut + pos, len, stereo);\r
 }\r
 \r
-PICO_INTERNAL void PsndDoFM(int line_to)\r
+PICO_INTERNAL void PsndDoFM(int cyc_to)\r
 {\r
   int pos, len;\r
   int stereo = 0;\r
 \r
   // Q16, number of samples since last call\r
-  len = ((line_to-1) * Pico.snd.fm_mult) - Pico.snd.fm_pos;\r
+  len = (cyc_to * Pico.snd.clkl_mult) - Pico.snd.fm_pos;\r
 \r
   // don't do this too often (about every 4th scanline)\r
-  if (len >> 16 <= PicoIn.sndRate >> 12)\r
+  if (len >> 20 <= PicoIn.sndRate >> 12)\r
     return;\r
 \r
   // update position and calculate buffer offset and length\r
-  pos = (Pico.snd.fm_pos+0x8000) >> 16;\r
+  pos = (Pico.snd.fm_pos+0x80000) >> 20;\r
   Pico.snd.fm_pos += len;\r
-  len = ((Pico.snd.fm_pos+0x8000) >> 16) - pos;\r
+  len = ((Pico.snd.fm_pos+0x80000) >> 20) - pos;\r
 \r
   // fill buffer\r
   if (PicoIn.opt & POPT_EN_STEREO) {\r
@@ -273,7 +249,7 @@ PICO_INTERNAL void PsndClear(void)
   if (!(PicoIn.opt & POPT_EN_FM))\r
     memset32(PsndBuffer, 0, PicoIn.opt & POPT_EN_STEREO ? len*2 : len);\r
   // drop pos remainder to avoid rounding errors (not entirely correct though)\r
-  Pico.snd.dac_pos = Pico.snd.fm_pos = 0;\r
+  Pico.snd.dac_pos = Pico.snd.fm_pos = Pico.snd.psg_pos = 0;\r
 }\r
 \r
 \r
@@ -281,7 +257,7 @@ static int PsndRender(int offset, int length)
 {\r
   int *buf32;\r
   int stereo = (PicoIn.opt & 8) >> 3;\r
-  int fmlen = ((Pico.snd.fm_pos+0x8000) >> 16);\r
+  int fmlen = ((Pico.snd.fm_pos+0x80000) >> 20);\r
   int daclen = ((Pico.snd.dac_pos+0x80000) >> 20);\r
 \r
   buf32 = PsndBuffer+(offset<<stereo);\r
@@ -297,16 +273,19 @@ static int PsndRender(int offset, int length)
   if (length-daclen > 0) {\r
     short *dacbuf = PicoIn.sndOut + (daclen << stereo);\r
     Pico.snd.dac_pos += (length-daclen) << 20;\r
-    for (; length-daclen > 0; daclen++) {\r
+    *dacbuf++ += Pico.snd.dac_val2;\r
+    if (stereo) dacbuf++;\r
+    for (daclen++; length-daclen > 0; daclen++) {\r
       *dacbuf++ += Pico.snd.dac_val;\r
       if (stereo) dacbuf++;\r
     }\r
+    Pico.snd.dac_val2 = Pico.snd.dac_val;\r
   }\r
 \r
   // Add in parts of the FM buffer not yet done\r
   if (length-fmlen > 0) {\r
     int *fmbuf = buf32 + ((fmlen-offset) << stereo);\r
-    Pico.snd.fm_pos += (length-fmlen) << 16;\r
+    Pico.snd.fm_pos += (length-fmlen) << 20;\r
     if (PicoIn.opt & POPT_EN_FM)\r
       YM2612UpdateOne(fmbuf, length-fmlen, stereo, 1);\r
   }\r
@@ -344,8 +323,6 @@ PICO_INTERNAL void PsndGetSamples(int y)
 {\r
   static int curr_pos = 0;\r
 \r
-  if (ym2612.dacen)\r
-    PsndDoDAC(cycles_68k_to_z80(Pico.t.m68c_aim - Pico.t.m68c_frame_start));\r
   PsndDoPSG(y - 1);\r
 \r
   curr_pos  = PsndRender(0, Pico.snd.len_use);\r