psp, revisit audio handling

sound rates 44100, 22050, 11025 Hz, internally upsampled
conversion to stereo since mono isn't supported well on psp
This commit is contained in:
kub 2024-02-22 20:51:08 +01:00
parent d12dd1b4ea
commit e28fd20f08
3 changed files with 138 additions and 79 deletions

View file

@ -1349,15 +1349,16 @@ void emu_sound_start(void)
{
int is_stereo = (PicoIn.opt & POPT_EN_STEREO) ? 1 : 0;
memset(sndBuffer, 0, sizeof(sndBuffer));
PicoIn.sndOut = sndBuffer;
PsndRerate(Pico.m.frame_count ? 1 : 0);
printf("starting audio: %i len: %i stereo: %i, pal: %i\n",
PicoIn.sndRate, Pico.snd.len, is_stereo, Pico.m.pal);
sndout_start(PicoIn.sndRate, is_stereo);
PicoIn.writeSound = snd_write_nonblocking;
plat_update_volume(0, 0);
memset(sndBuffer, 0, sizeof(sndBuffer));
PicoIn.sndOut = sndBuffer;
}
}

View 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 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)
{
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)
{
int ret = 0;
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;
}
}
// 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;
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;
}

View 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,