psp, revisit audio handling
authorkub <derkub@gmail.com>
Thu, 22 Feb 2024 19:51:08 +0000 (20:51 +0100)
committerkub <derkub@gmail.com>
Sat, 2 Mar 2024 09:19:57 +0000 (10:19 +0100)
sound rates 44100, 22050, 11025 Hz, internally upsampled
conversion to stereo since mono isn't supported well on psp

platform/common/emu.c
platform/psp/emu.c
platform/psp/plat.c

index 5e51d93..6895498 100644 (file)
@@ -1349,15 +1349,16 @@ void emu_sound_start(void)
        {\r
                int is_stereo = (PicoIn.opt & POPT_EN_STEREO) ? 1 : 0;\r
 \r
+               memset(sndBuffer, 0, sizeof(sndBuffer));\r
+               PicoIn.sndOut = sndBuffer;\r
                PsndRerate(Pico.m.frame_count ? 1 : 0);\r
 \r
                printf("starting audio: %i len: %i stereo: %i, pal: %i\n",\r
                        PicoIn.sndRate, Pico.snd.len, is_stereo, Pico.m.pal);\r
+\r
                sndout_start(PicoIn.sndRate, is_stereo);\r
                PicoIn.writeSound = snd_write_nonblocking;\r
                plat_update_volume(0, 0);\r
-               memset(sndBuffer, 0, sizeof(sndBuffer));\r
-               PicoIn.sndOut = sndBuffer;\r
        }\r
 }\r
 \r
index 8b5a498..4a60311 100644 (file)
@@ -415,54 +415,139 @@ static void vidResetMode(void)
 }
 
 /* sound stuff */
-#define SOUND_BLOCK_SIZE_NTSC (1470*2) // 1024 // 1152
-#define SOUND_BLOCK_SIZE_PAL  (1764*2)
-#define SOUND_BLOCK_COUNT    8
+#define SOUND_BLOCK_COUNT    7
+#define SOUND_BUFFER_CHUNK   (2*44100/50) // max.rate/min.frames in stereo
+
+static short sndBuffer_emu[SOUND_BUFFER_CHUNK+4]; // 4 for sample rounding overhang
+static short __attribute__((aligned(4))) sndBuffer[SOUND_BUFFER_CHUNK*SOUND_BLOCK_COUNT];
+static short *sndBuffer_endptr;
+static int samples_block;
+
+static short *snd_playptr, *sndBuffer_ptr;
+static int samples_made, samples_done;
 
-static short __attribute__((aligned(4))) sndBuffer[SOUND_BLOCK_SIZE_PAL*SOUND_BLOCK_COUNT + 54000/50*2];
-static short *snd_playptr = NULL, *sndBuffer_endptr = NULL;
-static int samples_made = 0, samples_done = 0, samples_block = 0;
 static int sound_thread_exit = 0;
 static SceUID sound_sem = -1;
 
-static void writeSound(int len);
+// There are problems if the sample rate used with the PSP isn't 44100 Hz stereo.
+// Hence, use only 11025,22050,44100 here and handle duplication and upsampling.
+// Upsample by nearest neighbour, which is the fastest but may create artifacts.
 
-static int sound_thread(SceSize args, void *argp)
+static void writeSound(int len)
+{
+       // make sure there is (samples_block+2) free space in the buffer after
+       // this frame, else the next frame may overwrite old stored samples.
+       if (samples_made - samples_done < samples_block * (SOUND_BLOCK_COUNT-2) - 4) {
+               sndBuffer_ptr += len / 2;
+               if (sndBuffer_ptr - sndBuffer > sizeof(sndBuffer)/2)
+                       lprintf("snd ovrn %d %d\n", len, PicoIn.sndOut - sndBuffer);
+               if (sndBuffer_ptr >= sndBuffer_endptr) {
+                       int wrap = sndBuffer_ptr - sndBuffer_endptr;
+                       if (wrap > 0)
+                               memcpy(sndBuffer, sndBuffer_endptr, 2*wrap);
+                       sndBuffer_ptr -= sndBuffer_endptr - sndBuffer;
+               }
+
+               samples_made += len / 2;
+       } else
+               lprintf("snd oflow %i!\n", samples_made - samples_done);
+
+       // signal the snd thread
+       sceKernelSignalSema(sound_sem, 1);
+}
+
+static void writeSound_44100_stereo(int len)
+{
+       writeSound(len);
+       PicoIn.sndOut = sndBuffer_ptr;
+}
+
+static void writeSound_44100_mono(int len)
+{
+       short *p = sndBuffer_ptr;
+       int i;
+
+       for (i = 0; i < len / 2; i++, p+=2)
+               p[0] = p[1] = PicoIn.sndOut[i];
+       writeSound(2*len);
+}
+
+static void writeSound_22050_stereo(int len)
+{
+       short *p = sndBuffer_ptr;
+       int i;
+
+       for (i = 0; i < len / 2; i+=2, p+=4) {
+               p[0] = p[2] = PicoIn.sndOut[i];
+               p[1] = p[3] = PicoIn.sndOut[i+1];
+       }
+       writeSound(2*len);
+}
+
+static void writeSound_22050_mono(int len)
+{
+       short *p = sndBuffer_ptr;
+       int i;
+
+       for (i = 0; i < len / 2; i++, p+=4) {
+               p[0] = p[2] = PicoIn.sndOut[i];
+               p[1] = p[3] = PicoIn.sndOut[i];
+       }
+       writeSound(4*len);
+}
+
+static void writeSound_11025_stereo(int len)
+{
+       short *p = sndBuffer_ptr;
+       int i;
+
+       for (i = 0; i < len / 2; i+=2, p+=8) {
+               p[0] = p[2] = p[4] = p[6] = PicoIn.sndOut[i];
+               p[1] = p[3] = p[5] = p[7] = PicoIn.sndOut[i+1];
+       }
+       writeSound(4*len);
+}
+
+static void writeSound_11025_mono(int len)
 {
-       int ret = 0;
+       short *p = sndBuffer_ptr;
+       int i;
 
+       for (i = 0; i < len / 2; i++, p+=8) {
+               p[0] = p[2] = p[4] = p[6] = PicoIn.sndOut[i];
+               p[1] = p[3] = p[5] = p[7] = PicoIn.sndOut[i];
+       }
+       writeSound(8*len);
+}
+
+static int sound_thread(SceSize args, void *argp)
+{
        lprintf("sthr: started, priority %i\n", sceKernelGetThreadCurrentPriority());
 
        while (!sound_thread_exit)
        {
+               int ret;
+
                if (samples_made - samples_done < samples_block) {
                        // wait for data (use at least 2 blocks)
                        //lprintf("sthr: wait... (%i)\n", samples_made - samples_done);
-                       while (samples_made - samples_done <= samples_block*2 && !sound_thread_exit)
+                       while (samples_made - samples_done < samples_block*2 && !sound_thread_exit) {
                                ret = sceKernelWaitSema(sound_sem, 1, 0);
-                       if (ret < 0) lprintf("sthr: sceKernelWaitSema: %i\n", ret);
-                       continue;
+                               if (ret < 0) lprintf("sthr: sceKernelWaitSema: %i\n", ret);
+                       }
                }
 
-               // lprintf("sthr: got data: %i\n", samples_made - samples_done);
-
+               // if the sample buffer runs low, push some extra
+               if (sceAudioOutput2GetRestSample()*2 < samples_block/4)
+                       ret = sceAudioSRCOutputBlocking(PSP_AUDIO_VOLUME_MAX, snd_playptr);
                ret = sceAudioSRCOutputBlocking(PSP_AUDIO_VOLUME_MAX, snd_playptr);
+               // 1.5 kernel returns 0, newer ones return # of samples queued
+               if (ret < 0) lprintf("sthr: play: ret %08x; pos %i/%i\n", ret, samples_done, samples_made);
 
                samples_done += samples_block;
                snd_playptr  += samples_block;
                if (snd_playptr >= sndBuffer_endptr)
-                       snd_playptr = sndBuffer;
-               // 1.5 kernel returns 0, newer ones return # of samples queued
-               if (ret < 0)
-                       lprintf("sthr: sceAudioSRCOutputBlocking: %08x; pos %i/%i\n", ret, samples_done, samples_made);
-
-               // shouln't happen, but just in case
-               if (samples_made - samples_done >= samples_block*3) {
-                       //lprintf("sthr: block skip (%i)\n", samples_made - samples_done);
-                       samples_done += samples_block; // skip
-                       snd_playptr  += samples_block;
-               }
-
+                       snd_playptr -= sndBuffer_endptr - sndBuffer;
        }
 
        lprintf("sthr: exit\n");
@@ -479,7 +564,7 @@ static void sound_init(void)
        if (sound_sem < 0) lprintf("sceKernelCreateSema() failed: %i\n", sound_sem);
 
        samples_made = samples_done = 0;
-       samples_block = SOUND_BLOCK_SIZE_NTSC; // make sure it goes to sema
+       samples_block = 2*22050/60; // make sure it goes to sema
        sound_thread_exit = 0;
        thid = sceKernelCreateThread("sndthread", sound_thread, 0x12, 0x10000, 0, NULL);
        if (thid >= 0)
@@ -491,11 +576,13 @@ static void sound_init(void)
                lprintf("sceKernelCreateThread failed: %i\n", thid);
 }
 
+#define PSP_RATE 44100 // PicoIn.sndRate
+
 void pemu_sound_start(void)
 {
        static int PsndRate_old = 0, PicoOpt_old = 0, pal_old = 0;
        static int mp3_init_done;
-       int ret, stereo;
+       int ret, stereo, factor;
 
        samples_made = samples_done = 0;
 
@@ -511,34 +598,38 @@ void pemu_sound_start(void)
                }
        }
 
-       if (PicoIn.sndRate > 52000 && PicoIn.sndRate < 54000)
-               PicoIn.sndRate = YM2612_NATIVE_RATE();
        ret = POPT_EN_FM|POPT_EN_PSG|POPT_EN_STEREO;
        if (PicoIn.sndRate != PsndRate_old || (PicoIn.opt&ret) != (PicoOpt_old&ret) || Pico.m.pal != pal_old) {
                PsndRerate(Pico.m.frame_count ? 1 : 0);
        }
-       stereo=(PicoIn.opt&8)>>3;
+       stereo = (PicoIn.opt&8)>>3;
 
-       samples_block = Pico.m.pal ? SOUND_BLOCK_SIZE_PAL : SOUND_BLOCK_SIZE_NTSC;
-       if (PicoIn.sndRate <= 22050) samples_block /= 2;
-       sndBuffer_endptr = &sndBuffer[samples_block*SOUND_BLOCK_COUNT];
+       // PSP doesn't support mono in SRC, always use stereo and convert
+       factor = PSP_RATE / PicoIn.sndRate;
+       samples_block = (PSP_RATE / (Pico.m.pal ? 50 : 60)) * 2;
 
        lprintf("starting audio: %i, len: %i, stereo: %i, pal: %i, block samples: %i\n",
                        PicoIn.sndRate, Pico.snd.len, stereo, Pico.m.pal, samples_block);
 
-       // while (sceAudioOutput2GetRestSample() > 0) psp_msleep(100);
-       // sceAudioSRCChRelease();
-       ret = sceAudioSRCChReserve(samples_block/2, PicoIn.sndRate, 2); // seems to not need that stupid 64byte alignment
+       ret = sceAudioSRCChReserve(samples_block/2, PSP_RATE, 2); // seems to not need that stupid 64byte alignment
        if (ret < 0) {
                lprintf("sceAudioSRCChReserve() failed: %i\n", ret);
                emu_status_msg("sound init failed (%i), snd disabled", ret);
                currentConfig.EmuOpt &= ~EOPT_EN_SOUND;
        } else {
-               PicoIn.writeSound = writeSound;
-               memset32((int *)(void *)sndBuffer, 0, sizeof(sndBuffer)/4);
-               snd_playptr = sndBuffer_endptr - samples_block;
-               samples_made = samples_block; // send 1 empty block first..
-               PicoIn.sndOut = sndBuffer;
+               switch (factor) {
+               case 1: PicoIn.writeSound = stereo ? writeSound_44100_stereo:writeSound_44100_mono; break;
+               case 2: PicoIn.writeSound = stereo ? writeSound_22050_stereo:writeSound_22050_mono; break;
+               case 4: PicoIn.writeSound = stereo ? writeSound_11025_stereo:writeSound_11025_mono; break;
+               }
+               sndBuffer_endptr = sndBuffer + (SOUND_BLOCK_COUNT-1)*samples_block;
+               snd_playptr = sndBuffer_ptr = sndBuffer;
+               PicoIn.sndOut = (factor == 1 && stereo ? sndBuffer_ptr : sndBuffer_emu);
+
+               // push one audio block to cover time to first frame audio
+//             memset32(PicoIn.sndOut, 0, samples_block/2);
+//             writeSound(samples_block*2);
+
                PsndRate_old = PicoIn.sndRate;
                PicoOpt_old  = PicoIn.opt;
                pal_old = Pico.m.pal;
@@ -564,14 +655,6 @@ void pemu_sound_stop(void)
        sceAudioSRCChRelease();
 }
 
-/* wait until we can write more sound */
-void pemu_sound_wait(void)
-{
-       // TODO: test this
-       while (!sound_thread_exit && samples_made - samples_done > samples_block * 4)
-               psp_msleep(10);
-}
-
 static void sound_deinit(void)
 {
        sound_thread_exit = 1;
@@ -580,31 +663,6 @@ static void sound_deinit(void)
        sound_sem = -1;
 }
 
-static void writeSound(int len)
-{
-       int ret;
-
-       PicoIn.sndOut += len / 2;
-       /*if (PicoIn.sndOut > sndBuffer_endptr) {
-               memcpy((int *)(void *)sndBuffer, (int *)endptr, (PicoIn.sndOut - endptr + 1) * 2);
-               PicoIn.sndOut = &sndBuffer[PicoIn.sndOut - endptr];
-               lprintf("mov\n");
-       }
-       else*/
-       if (PicoIn.sndOut > sndBuffer_endptr) lprintf("snd oflow %i!\n", PicoIn.sndOut - sndBuffer_endptr);
-       if (PicoIn.sndOut >= sndBuffer_endptr)
-               PicoIn.sndOut = sndBuffer;
-
-       // signal the snd thread
-       samples_made += len / 2;
-       if (samples_made - samples_done > samples_block*2) {
-               // lprintf("signal, %i/%i\n", samples_done, samples_made);
-               ret = sceKernelSignalSema(sound_sem, 1);
-               //if (ret < 0) lprintf("snd signal ret %08x\n", ret);
-       }
-}
-
-
 /* set default configuration values */
 void pemu_prep_defconfig(void)
 {
@@ -615,7 +673,7 @@ void pemu_prep_defconfig(void)
        defaultConfig.scaling = EOPT_SCALE_43;
        defaultConfig.vscaling = EOPT_VSCALE_FULL;
        defaultConfig.renderer = RT_8BIT_ACC;
-       defaultConfig.renderer32x = RT_8BIT_ACC;
+       defaultConfig.renderer32x = RT_8BIT_FAST;
        defaultConfig.EmuOpt |= EOPT_SHOW_RTC;
 }
 
index 86b15e7..b8a29e5 100644 (file)
@@ -275,7 +275,7 @@ static int plat_bat_capacity_get(void)
        return scePowerGetBatteryLifePercent();
 }
 
-static int sound_rates[] = { 8000, 11025, 16000, 22050, 32000, 44100, -1 };
+static int sound_rates[] = { 11025, 22050, 44100, -1 };
 struct plat_target plat_target = {
        .cpu_clock_get = plat_cpu_clock_get,
        .cpu_clock_set = plat_cpu_clock_set,