Skip to content

Commit

Permalink
psp, revisit audio handling
Browse files Browse the repository at this point in the history
sound rates 44100, 22050, 11025 Hz, internally upsampled
conversion to stereo since mono isn't supported well on psp
  • Loading branch information
irixxxx committed Feb 29, 2024
1 parent d2f0f7c commit 580ed3e
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 79 deletions.
5 changes: 3 additions & 2 deletions platform/common/emu.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}

Expand Down
210 changes: 134 additions & 76 deletions platform/psp/emu.c
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand All @@ -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)
Expand All @@ -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;

Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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)
{
Expand All @@ -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;
}

Expand Down
2 changes: 1 addition & 1 deletion platform/psp/plat.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down

0 comments on commit 580ed3e

Please sign in to comment.