sound code rewrite for lower sample rates (except mp3s)
authornotaz <notasas@gmail.com>
Tue, 6 Feb 2007 22:25:53 +0000 (22:25 +0000)
committernotaz <notasas@gmail.com>
Tue, 6 Feb 2007 22:25:53 +0000 (22:25 +0000)
git-svn-id: file:///home/notaz/opt/svn/PicoDrive@30 be3aeb3a-fb24-0410-a615-afba39da0efa

Pico/Pico.c
Pico/Pico.h
Pico/PicoInt.h
Pico/cd/Pico.c
Pico/sound/mix.s
Pico/sound/sound.c
platform/gp2x/940ctl_ym2612.c
platform/gp2x/code940/940.c
platform/gp2x/emu.c

index 004a247..3cee0c2 100644 (file)
@@ -18,7 +18,7 @@ int PicoSkipFrame=0; // skip rendering frame?
 int PicoRegionOverride = 0; // override the region detection 0: Auto, 1: Japan NTSC, 2: Japan PAL, 4: US, 8: Europe\r
 int PicoAutoRgnOrder = 0;\r
 int emustatus = 0;\r
-void (*PicoWriteSound)(void) = 0; // called once per frame at the best time to send sound buffer (PsndOut) to hardware\r
+void (*PicoWriteSound)(int len) = 0; // called once per frame at the best time to send sound buffer (PsndOut) to hardware\r
 \r
 struct PicoSRAM SRam;\r
 int z80startCycle = 0, z80stopCycle = 0; // in 68k cycles\r
@@ -315,21 +315,22 @@ static int CheckIdle(void)
 // to be called on 224 or line_sample scanlines only\r
 static __inline void getSamples(int y)\r
 {\r
+  static int curr_pos = 0;\r
+\r
   if(y == 224) {\r
     //dprintf("sta%i: %i [%i]", (emustatus & 2), emustatus, y);\r
     if(emustatus & 2)\r
-        sound_render(PsndLen/2, PsndLen-PsndLen/2);\r
-    else sound_render(0, PsndLen);\r
+         curr_pos += sound_render(curr_pos, PsndLen-PsndLen/2);\r
+    else curr_pos  = sound_render(0, PsndLen);\r
     if (emustatus&1) emustatus|=2; else emustatus&=~2;\r
-    if (PicoWriteSound) PicoWriteSound();\r
+    if (PicoWriteSound) PicoWriteSound(curr_pos);\r
     // clear sound buffer\r
     sound_clear();\r
-    //memset(PsndOut, 0, (PicoOpt & 8) ? (PsndLen<<2) : (PsndLen<<1));\r
   }\r
   else if(emustatus & 3) {\r
     emustatus|= 2;\r
     emustatus&=~1;\r
-    sound_render(0, PsndLen/2);\r
+    curr_pos = sound_render(0, PsndLen/2);\r
   }\r
 }\r
 \r
@@ -538,10 +539,10 @@ static int PicoFrameSimple(void)
 \r
   // here we render sound if ym2612 is disabled\r
   if(!(PicoOpt&1) && PsndOut) {\r
-    sound_render(0, PsndLen);\r
-    if(PicoWriteSound) PicoWriteSound();\r
+    int len = sound_render(0, PsndLen);\r
+    if(PicoWriteSound) PicoWriteSound(len);\r
     // clear sound buffer\r
-    memset(PsndOut, 0, (PicoOpt & 8) ? (PsndLen<<2) : (PsndLen<<1));\r
+    sound_clear();\r
   }\r
 \r
   // render screen\r
index ef2db10..7e7633c 100644 (file)
@@ -42,7 +42,7 @@ int PicoReset(int hard);
 int PicoFrame(void);\r
 extern int PicoPad[2]; // Joypads, format is MXYZ SACB RLDU\r
 extern int (*PicoCram)(int cram); // Callback to convert colour ram 0000bbb0 ggg0rrr0\r
-extern void (*PicoWriteSound)(void); // called once per frame at the best time to send sound buffer (PsndOut) to hardware\r
+extern void (*PicoWriteSound)(int len); // called once per frame at the best time to send sound buffer (PsndOut) to hardware\r
 \r
 int PicoFrameMCD(void);\r
 \r
@@ -89,7 +89,7 @@ extern void (*PicoPrepareCram)();    // prepares PicoCramHigh for renderer to us
 extern int PsndRate,PsndLen;\r
 extern short *PsndOut;\r
 void sound_reset();\r
-void sound_rerate();\r
+void sound_rerate(int preserve_state);\r
 void z80_pack(unsigned char *data);\r
 void z80_unpack(unsigned char *data);\r
 void z80_reset();\r
index c7b72a2..b17277f 100644 (file)
@@ -277,6 +277,10 @@ int SekInitS68k(void);
 int SekResetS68k(void);\r
 int SekInterruptS68k(int irq);\r
 \r
+// sound/sound.c\r
+extern int PsndLen_exc_cnt;\r
+extern int PsndLen_exc_add;\r
+\r
 // VideoPort.c\r
 void PicoVideoWrite(unsigned int a,unsigned short d);\r
 unsigned int PicoVideoRead(unsigned int a);\r
index d643cb7..dfdbf5e 100644 (file)
@@ -131,27 +131,6 @@ static __inline void update_chips(void)
                gfx_cd_update();
 }
 
-// to be called on 224 or line_sample scanlines only
-static __inline void getSamples(int y)
-{
-  if(y == 224) {
-    //dprintf("sta%i: %i [%i]", (emustatus & 2), emustatus, y);
-    if(emustatus & 2)
-        sound_render(PsndLen/2, PsndLen-PsndLen/2);
-    else sound_render(0, PsndLen);
-    if (emustatus&1) emustatus|=2; else emustatus&=~2;
-    if (PicoWriteSound) PicoWriteSound();
-    // clear sound buffer
-    sound_clear();
-    //memset(PsndOut, 0, (PicoOpt & 8) ? (PsndLen<<2) : (PsndLen<<1));
-  }
-  else if(emustatus & 3) {
-    emustatus|= 2;
-    emustatus&=~1;
-    sound_render(0, PsndLen/2);
-  }
-}
-
 
 static int PicoFrameHintsMCD(void)
 {
@@ -240,10 +219,12 @@ static int PicoFrameHintsMCD(void)
       sound_timers_and_dac(y);
 
     // get samples from sound chips
-    if(y == 32 && PsndOut)
-      emustatus &= ~1;
-    else if((y == 224 || y == line_sample) && PsndOut)
-      getSamples(y);
+    if (y == 224 && PsndOut) {
+      int len = sound_render(0, PsndLen);
+      if (PicoWriteSound) PicoWriteSound(len);
+      // clear sound buffer
+      sound_clear();
+    }
 
     // Run scanline:
       //dprintf("m68k starting exec @ %06x", SekPc);
index ab0853e..d34a273 100644 (file)
@@ -206,10 +206,10 @@ m16_32_s2_no_unal2:
 @ limit
 @ reg=int_sample, lr=1, r3=tmp, kills flags
 .macro Limit reg
-    add     r3, lr, \reg, asr #16
+    add     r3, lr, \reg, asr #15
     bics    r3, r3, #1                 @ in non-overflow conditions r3 is 0 or 1
     movne   \reg, #0x8000
-    submi   \reg, \reg, #1
+    subpl   \reg, \reg, #1
 .endm
 
 
@@ -223,11 +223,11 @@ m16_32_s2_no_unal2:
 @    mov     r3, #0x8000
 @    subpl   r3, r3, #1
 
-    add     r3, lr, \reg, asr #16
+    add     r3, lr, \reg, asr #15
     bics    r3, r3, #1                 @ in non-overflow conditions r3 is 0 or 1
     moveq   \reg, \reg, lsl #16
     movne   \reg, #0x80000000
-    submi   \reg, \reg, #0x00010000
+    subpl   \reg, \reg, #0x00010000
 .endm
 
 
@@ -292,7 +292,7 @@ mix_32_to_16_mono:
     @ check if dest is word aligned
     tst     r0, #2
     beq     m32_16_mo_no_unalw
-    ldrsh   r5, [r0], #2
+    ldrsh   r5, [r0]
     ldr     r4, [r1], #4
     sub     r2, r2, #1
     add     r4, r4, r5
@@ -339,7 +339,7 @@ m32_16_mo_end:
 m32_16_mo_no_unal2:
     tst     r2, #1
     ldmeqfd sp!, {r4-r8,pc}
-    ldrsh   r5, [r0], #2
+    ldrsh   r5, [r0]
     ldr     r4, [r1], #4
     add     r4, r4, r5
     Limit   r4
index 6ea966b..18cc46e 100644 (file)
@@ -38,6 +38,8 @@ unsigned short dac_info[312]; // pppppppp ppppllll, p - pos in buff, l - length
 // for Pico\r
 int PsndRate=0;\r
 int PsndLen=0; // number of mono samples, multiply by 2 for stereo\r
+int PsndLen_exc_add=0; // this is for non-integer sample counts per line, eg. 22050/60\r
+int PsndLen_exc_cnt=0;\r
 short *PsndOut=NULL; // PCM data buffer\r
 \r
 // from ym2612.c\r
@@ -55,7 +57,7 @@ static void dac_recalculate()
 \r
   if(PsndLen <= lines) {\r
     // shrinking algo\r
-    dac_cnt = 0;//lines - PsndLen;\r
+    dac_cnt = -PsndLen;\r
     len=1; pos=0;\r
     dac_info[225] = 1;\r
 \r
@@ -72,7 +74,7 @@ static void dac_recalculate()
     }\r
   } else {\r
     // stretching\r
-    dac_cnt = PsndLen/2;\r
+    dac_cnt = PsndLen;\r
     pos=0;\r
     for(i = 225; i != 224; i++) {\r
       if (i >= lines) i = 0;\r
@@ -92,12 +94,16 @@ static void dac_recalculate()
     }\r
     // last sample\r
     for(len = 0, i = pos; i < PsndLen; i++) len++;\r
+    if (PsndLen_exc_add) len++;\r
     dac_info[224] = (pos<<4)|len;\r
   }\r
-//  dprintf("rate is %i, len %i", PsndRate, PsndLen);\r
-//  for(i=0; i < lines; i++)\r
-//    dprintf("%03i : %03i : %i", i, dac_info[i]>>4, dac_info[i]&0xf);\r
-//exit(8);\r
+  //for(i=len=0; i < lines; i++) {\r
+  //  printf("%03i : %03i : %i\n", i, dac_info[i]>>4, dac_info[i]&0xf);\r
+  //  len+=dac_info[i]&0xf;\r
+  //}\r
+  //printf("rate is %i, len %f\n", PsndRate, (double)PsndRate/(Pico.m.pal ? 50.0 : 60.0));\r
+  //printf("len total: %i, last pos: %i\n", len, pos);\r
+  //exit(8);\r
 }\r
 \r
 \r
@@ -106,53 +112,58 @@ void sound_reset()
   extern int z80stopCycle;\r
   void *ym2612_regs;\r
 \r
-  // init even if we are not going to use them, just in case we ever enable it\r
-  YM2612Init(Pico.m.pal ? OSC_PAL/7 : OSC_NTSC/7, PsndRate);\r
   // also clear the internal registers+addr line\r
   ym2612_regs = YM2612GetRegs();\r
   memset(ym2612_regs, 0, 0x200+4);\r
-\r
-  SN76496_init(Pico.m.pal ? OSC_PAL/15 : OSC_NTSC/15, PsndRate);\r
-\r
-  // calculate PsndLen\r
-  PsndLen=PsndRate/(Pico.m.pal ? 50 : 60);\r
-\r
-  // recalculate dac info\r
-  dac_recalculate();\r
   z80stopCycle = 0;\r
+\r
+  sound_rerate(0);\r
 }\r
 \r
 \r
 // to be called after changing sound rate or chips\r
-void sound_rerate()\r
+void sound_rerate(int preserve_state)\r
 {\r
   unsigned int state[28];\r
+  int target_fps = Pico.m.pal ? 50 : 60;\r
 \r
-  // not all rates are supported in MCD mode due to mp3 decoder\r
+  // not all rates are supported in MCD mode due to mp3 decoder limitations\r
   if (PicoMCD & 1) {\r
     if (PsndRate != 11025 && PsndRate != 22050 && PsndRate != 44100) PsndRate = 22050;\r
-    if (!(PicoOpt & 8)) PicoOpt |= 8;\r
+    PicoOpt |= 8; // force stereo\r
   }\r
 \r
-  if ((PicoMCD & 1) && Pico_mcd->m.audio_track) Pico_mcd->m.audio_offset = mp3_get_offset();\r
+  if (preserve_state) {\r
+    if ((PicoMCD & 1) && Pico_mcd->m.audio_track)\r
+      Pico_mcd->m.audio_offset = mp3_get_offset();\r
+  }\r
   YM2612Init(Pico.m.pal ? OSC_PAL/7 : OSC_NTSC/7, PsndRate);\r
-  // feed it back it's own registers, just like after loading state\r
-  YM2612PicoStateLoad();\r
-  if ((PicoMCD & 1) && Pico_mcd->m.audio_track)\r
-    mp3_start_play(Pico_mcd->TOC.Tracks[Pico_mcd->m.audio_track].F, Pico_mcd->m.audio_offset);\r
+  if (preserve_state) {\r
+    // feed it back it's own registers, just like after loading state\r
+    YM2612PicoStateLoad();\r
+    if ((PicoMCD & 1) && Pico_mcd->m.audio_track)\r
+      mp3_start_play(Pico_mcd->TOC.Tracks[Pico_mcd->m.audio_track].F, Pico_mcd->m.audio_offset);\r
+  }\r
 \r
-  memcpy(state, sn76496_regs, 28*4); // remember old state\r
+  if (preserve_state) memcpy(state, sn76496_regs, 28*4); // remember old state\r
   SN76496_init(Pico.m.pal ? OSC_PAL/15 : OSC_NTSC/15, PsndRate);\r
-  memcpy(sn76496_regs, state, 28*4); // restore old state\r
+  if (preserve_state) memcpy(sn76496_regs, state, 28*4); // restore old state\r
 \r
   // calculate PsndLen\r
-  PsndLen=PsndRate/(Pico.m.pal ? 50 : 60);\r
+  PsndLen=PsndRate / target_fps;\r
+  PsndLen_exc_add=((PsndRate - PsndLen*target_fps)<<16) / target_fps;\r
+  PsndLen_exc_cnt=0;\r
 \r
   // recalculate dac info\r
   dac_recalculate();\r
 \r
   if (PicoMCD & 1)\r
     pcm_set_rate(PsndRate);\r
+\r
+  // clear all buffers\r
+  memset32(PsndBuffer, 0, sizeof(PsndBuffer)/4);\r
+  if (PsndOut)\r
+    sound_clear();\r
 }\r
 \r
 \r
@@ -181,22 +192,16 @@ void sound_timers_and_dac(int raster)
       d[0] = dout;\r
       if (len > 1) {\r
         d[2] = dout;\r
-        if (len > 2) {\r
+        if (len > 2)\r
           d[4] = dout;\r
-          if (len > 3)\r
-            d[6] = dout;\r
-        }\r
       }\r
     } else {\r
       short *d = PsndOut + pos;\r
       d[0] = dout;\r
       if (len > 1) {\r
         d[1] = dout;\r
-        if (len > 2) {\r
+        if (len > 2)\r
           d[2] = dout;\r
-          if (len > 3)\r
-            d[3] = dout;\r
-        }\r
       }\r
     }\r
   }\r
@@ -213,9 +218,10 @@ void sound_timers_and_dac(int raster)
 \r
 void sound_clear(void)\r
 {\r
-  if (PicoOpt & 8) memset32((int *) PsndOut, 0, PsndLen);\r
-  else memset(PsndOut, 0, PsndLen<<1);\r
-//  memset(PsndBuffer, 0, (PicoOpt & 8) ? (PsndLen<<3) : (PsndLen<<2));\r
+  int len = PsndLen;\r
+  if (PsndLen_exc_add) len++;\r
+  if (PicoOpt & 8) memset32((int *) PsndOut, 0, len); // clear both channels at once\r
+  else memset(PsndOut, 0, len<<1);\r
 }\r
 \r
 \r
@@ -227,6 +233,15 @@ int sound_render(int offset, int length)
   int do_pcm = (PicoMCD&1) && (PicoOpt&0x400) && (Pico_mcd->pcm.control & 0x80) && Pico_mcd->pcm.enabled;\r
   offset <<= stereo;\r
 \r
+  if (offset == 0) { // should happen once per frame\r
+    // compensate for float part of PsndLen\r
+    PsndLen_exc_cnt += PsndLen_exc_add;\r
+    if (PsndLen_exc_cnt >= 0x10000) {\r
+      PsndLen_exc_cnt -= 0x10000;\r
+      length++;\r
+    }\r
+  }\r
+\r
   // PSG\r
   if (PicoOpt & 2)\r
     SN76496Update(PsndOut+offset, length, stereo);\r
@@ -235,10 +250,11 @@ int sound_render(int offset, int length)
   if (PicoOpt & 1)\r
     YM2612UpdateOne(buf32, length, stereo, 1);\r
 \r
+  // CD: PCM sound\r
   if (do_pcm)\r
     pcm_update(buf32, length, stereo);\r
 \r
-  // CDDA audio\r
+  // CD: CDDA audio\r
 //  if ((PicoMCD & 1) && (PicoOpt & 0x800))\r
 //    mp3_update(PsndBuffer+offset, length, stereo);\r
 \r
@@ -247,7 +263,7 @@ int sound_render(int offset, int length)
        mix_32_to_16l_stereo(PsndOut+offset, buf32, length);\r
   else mix_32_to_16_mono   (PsndOut+offset, buf32, length);\r
 \r
-  return 0;\r
+  return length;\r
 }\r
 \r
 \r
index 1a8160d..29fd8bf 100644 (file)
@@ -537,6 +537,12 @@ int YM2612UpdateOne_940(int *buffer, int length, int stereo, int is_buf_empty)
        }\r
        writebuff_ptr = 0;\r
 \r
+       /* predict sample counter for next frame */\r
+       if (PsndLen_exc_add) {\r
+               if (PsndLen_exc_cnt + PsndLen_exc_add >= 0x10000) length = PsndLen + 1;\r
+               else length = PsndLen;\r
+       }\r
+\r
        /* give 940 ym job */\r
        shared_ctl->writebuffsel ^= 1;\r
        shared_ctl->length = length;\r
index 3f7130a..18d282c 100644 (file)
@@ -4,7 +4,6 @@ static _940_data_t *shared_data = (_940_data_t *)   0x00100000;
 static _940_ctl_t  *shared_ctl  = (_940_ctl_t *)    0x00200000;\r
 static unsigned char *mp3_data  = (unsigned char *) 0x01000000;\r
 YM2612 *ym2612_940;\r
-int *mix_buffer;\r
 \r
 // from init.s\r
 void wait_irq(void);\r
@@ -59,8 +58,8 @@ static void mp3_decode(void)
 \r
 void Main940(int startvector, int pc_at_irq)\r
 {\r
+       int mix_buffer = shared_data->mix_buffer;\r
        ym2612_940 = &shared_data->ym2612;\r
-       mix_buffer = shared_data->mix_buffer;\r
 \r
        // debug\r
        shared_ctl->vstarts[startvector]++;\r
index bbb78b2..871d05f 100644 (file)
@@ -48,6 +48,7 @@ unsigned char *rom_data = NULL;
 \r
 extern int crashed_940;\r
 \r
+static short sndBuffer[2*44100/50];\r
 static char noticeMsg[64];                                     // notice msg to draw\r
 static struct timeval noticeMsgTime = { 0, 0 };        // when started showing\r
 static int reset_timing, osd_fps_x;\r
@@ -927,24 +928,10 @@ static void updateKeys(void)
        prevEvents = (allActions[0] | allActions[1]) >> 16;\r
 }\r
 \r
-static int snd_excess_add = 0, snd_excess_cnt = 0; // hack\r
 \r
-static void updateSound(void)\r
+static void updateSound(int len)\r
 {\r
-       int len = (PicoOpt&8)?PsndLen*2:PsndLen;\r
-\r
-       snd_excess_cnt += snd_excess_add;\r
-       if (snd_excess_cnt >= 0x10000) {\r
-               snd_excess_cnt -= 0x10000;\r
-               if (PicoOpt&8) {\r
-                       PsndOut[len]   = PsndOut[len-2];\r
-                       PsndOut[len+1] = PsndOut[len-1];\r
-                       len+=2;\r
-               } else {\r
-                       PsndOut[len]   = PsndOut[len-1];\r
-                       len++;\r
-               }\r
-       }\r
+       if (PicoOpt&8) len<<=1;\r
 \r
        gp2x_sound_write(PsndOut, len<<1);\r
 }\r
@@ -1024,22 +1011,23 @@ void emu_Loop(void)
 \r
        // prepare sound stuff\r
        if(currentConfig.EmuOpt & 4) {\r
+               int snd_excess_add;\r
                if(PsndRate != PsndRate_old || (PicoOpt&0x20b) != (PicoOpt_old&0x20b) || Pico.m.pal != pal_old || crashed_940) {\r
                        /* if 940 is turned off, we need it to be put back to sleep */\r
                        if (!(PicoOpt&0x200) && ((PicoOpt^PicoOpt_old)&0x200)) {\r
                                Reset940(1, 2);\r
                                Pause940(1);\r
                        }\r
-                       sound_rerate();\r
+                       sound_rerate(1);\r
                }\r
                //excess_samples = PsndRate - PsndLen*target_fps;\r
-               snd_excess_cnt = 0;\r
                snd_excess_add = ((PsndRate - PsndLen*target_fps)<<16) / target_fps;\r
                printf("starting audio: %i len: %i (ex: %04x) stereo: %i, pal: %i\n", PsndRate, PsndLen, snd_excess_add, (PicoOpt&8)>>3, Pico.m.pal);\r
                gp2x_start_sound(PsndRate, 16, (PicoOpt&8)>>3);\r
                gp2x_sound_volume(currentConfig.volume, currentConfig.volume);\r
                PicoWriteSound = updateSound;\r
-               PsndOut = calloc((PicoOpt&8) ? (PsndLen*4+4) : (PsndLen*2+2), 1);\r
+               memset(sndBuffer, 0, sizeof(sndBuffer));\r
+               PsndOut = sndBuffer;\r
                PsndRate_old = PsndRate;\r
                PsndLen_real = PsndLen;\r
                PicoOpt_old  = PicoOpt;\r
@@ -1239,11 +1227,6 @@ if (Pico.m.frame_count == 31563) {
                emu_SaveLoadGame(0, 1);\r
                SRam.changed = 0;\r
        }\r
-\r
-       if (PsndOut != 0) {\r
-               free(PsndOut);\r
-               PsndOut = 0;\r
-       }\r
 }\r
 \r
 \r