sound: psp sound output rewritten

This commit is contained in:
Crow-bar
2022-04-16 12:39:02 +03:00
parent 7be87a913d
commit 2eef2cec49
3 changed files with 277 additions and 248 deletions

View File

@@ -1,247 +0,0 @@
/*
s_psp.c - sound hardware output
Copyright (C) 2020 Sergey Galushko
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#include "platform/platform.h"
#if XASH_SOUND == SOUND_PSP
#include <pspaudiolib.h>
#include <pspkernel.h>
#include <psputility.h>
#include "sound.h"
#define SAMPLE_16BIT_SHIFT 1
#define SECONDARY_BUFFER_SIZE 0x10000
#define SND_CHANELS 2
#define SND_FREQ 44100
#define SND_BUF_SIZE 16384
typedef struct
{
short left;
short right;
}sample_t;
static sample_t snd_buffer[SND_BUF_SIZE] __attribute__( ( aligned( 64 ) ) );
static volatile unsigned int snd_sampleread;
/*
=======================================================================
Global variables. Must be visible to window-procedure function
so it can unlock and free the data block after it has been played.
=======================================================================
*/
void S_Activate( qboolean active )
{
}
/* From PSP Quake */
static void PSP_SoundCallback( void* buffer, unsigned int samplesToWrite, void* userData )
{
// Where are we writing to?
sample_t* const dest = ( sample_t* )buffer;
// Where are we reading from?
const sample_t* const src = &snd_buffer[snd_sampleread];
// How many samples to read?
const unsigned int samplesToRead = samplesToWrite; // 44100
// Going to wrap past the end of the input buffer?
const unsigned int samplesBeforeEndOfInput = SND_BUF_SIZE - snd_sampleread;
if ( samplesToRead > samplesBeforeEndOfInput )
{
// Yes, so write the first chunk from the end of the input buffer.
memcpy( dest, src, samplesBeforeEndOfInput * sizeof( sample_t ) );
// Write the second chunk from the start of the input buffer.
const unsigned int samplesToReadFromBeginning = samplesToRead - samplesBeforeEndOfInput;
memcpy( &dest[samplesBeforeEndOfInput], src, samplesToReadFromBeginning * sizeof( sample_t ) );
}
else
{
// No wrapping, just copy.
memcpy( &dest[0], src, samplesToRead * sizeof( sample_t ) );
}
// Update the read offset.
snd_sampleread = ( snd_sampleread + samplesToRead ) % SND_BUF_SIZE;
}
/*
==================
SNDDMA_Init
Try to find a sound device to mix for.
Returns false if nothing is found.
==================
*/
qboolean SNDDMA_Init( void )
{
dma.format.speed = SND_FREQ;
dma.format.channels = SND_CHANELS;
dma.format.width = 2;
dma.samples = SND_BUF_SIZE * SND_CHANELS;
dma.buffer = ( unsigned char * )snd_buffer;
dma.samplepos = 0;
// Initialise the audio system. This initialises it for the CD audio module
// too.
pspAudioInit();
// Set the channel callback.
// Sound effects use channel 0, CD audio uses channel 1.
pspAudioSetChannelCallback( 0, PSP_SoundCallback, 0 );
Con_Printf( "Using PSP audio driver: %d Hz\n", dma.format.speed );
//SNDDMA_Activate( true );
dma.initialized = true;
#if 0 /* libmp3 */
// load modules
int status = sceUtilityLoadModule( PSP_MODULE_AV_AVCODEC );
if ( status < 0 )
{
Con_Printf( "Error - PSP_MODULE_AV_AVCODEC returned 0x%08X\n", status );
}else printf("PSP_MODULE_AV_AVCODEC - OK\n");
status = sceUtilityLoadModule( PSP_MODULE_AV_MP3 );
if ( status < 0 )
{
Con_Printf( "Error - PSP_MODULE_AV_MP3 returned 0x%08X\n", status );
}else printf("PSP_MODULE_AV_MP3 - OK\n");
// init mp3 resources
status = sceMp3InitResource();
if ( status < 0 )
{
Con_Printf("Error - sceMp3InitResource returned 0x%08X\n", status);
}else printf("sceMp3InitResource - OK\n");
#endif
return true;
}
/*
==============
SNDDMA_GetDMAPos
return the current sample position (in mono samples read)
inside the recirculating dma buffer, so the mixing code will know
how many sample are required to fill it up.
===============
*/
int SNDDMA_GetDMAPos( void )
{
dma.samplepos = snd_sampleread * SND_CHANELS;
return dma.samplepos;
}
/*
==============
SNDDMA_GetSoundtime
update global soundtime
===============
*/
int SNDDMA_GetSoundtime( void )
{
static int buffers, oldsamplepos;
int samplepos, fullsamples;
fullsamples = dma.samples / 2;
// it is possible to miscount buffers
// if it has wrapped twice between
// calls to S_Update. Oh well.
samplepos = SNDDMA_GetDMAPos();
if( samplepos < oldsamplepos )
{
buffers++; // buffer wrapped
if( paintedtime > 0x40000000 )
{
// time to chop things off to avoid 32 bit limits
buffers = 0;
paintedtime = fullsamples;
S_StopAllSounds( true );
}
}
oldsamplepos = samplepos;
return (buffers * fullsamples + samplepos / 2);
}
/*
==============
SNDDMA_BeginPainting
Makes sure dma.buffer is valid
===============
*/
void SNDDMA_BeginPainting( void )
{
}
/*
==============
SNDDMA_Submit
Send sound to device if buffer isn't really the dma buffer
Also unlocks the dsound buffer
===============
*/
void SNDDMA_Submit( void )
{
}
/*
==============
SNDDMA_Shutdown
Reset the sound device for exiting
===============
*/
void SNDDMA_Shutdown( void )
{
Con_Printf("Shutting down audio.\n");
// Clear the mixing buffer so we don't get any noise during cleanup.
memset(snd_buffer, 0, sizeof(snd_buffer));
// Clear the channel callback.
pspAudioSetChannelCallback(0, 0, 0);
// Stop the audio system?
pspAudioEndPre();
// Insert a false delay so the thread can be cleaned up.
sceKernelDelayThread(50 * 1000);
// Shut down the audio system.
pspAudioEnd();
dma.initialized = false;
}
#endif // XASH_SOUND == SOUND_PSP

View File

@@ -0,0 +1,276 @@
/*
snd_psp.c - psp sound hardware output
Copyright (C) 2022 Sergey Galushko
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#include "platform/platform.h"
#if XASH_SOUND == SOUND_PSP
#include <malloc.h>
#include <pspaudio.h>
#include <pspkernel.h>
#include <pspdmac.h>
#include "sound.h"
#define PSP_NUM_AUDIO_SAMPLES 1024 // must be multiple of 64
#define PSP_OUTPUT_CHANNELS 2
#define PSP_OUTPUT_BUFFER_SIZE ( ( PSP_NUM_AUDIO_SAMPLES ) * ( PSP_OUTPUT_CHANNELS ) )
static struct
{
SceUID threadUID;
SceUID semaUID;
int channel;
volatile int volL;
volatile int volR;
volatile int running;
} snd_psp = { -1, -1, -1, PSP_AUDIO_VOLUME_MAX, PSP_AUDIO_VOLUME_MAX, 1 };
static short snd_psp_buff[2][PSP_OUTPUT_BUFFER_SIZE] __attribute__( ( aligned( 64 ) ) );
/*
==================
SNDDMA_MainThread
Copy samples
==================
*/
static int SNDDMA_MainThread( SceSize args, void *argp )
{
int index = 0;
while( snd_psp.running )
{
sceKernelWaitSema( snd_psp.semaUID, 1, NULL );
int len = PSP_OUTPUT_BUFFER_SIZE;
int size = dma.samples;
int pos = dma.samplepos;
int wrapped = pos + len - size;
if( wrapped < 0 )
{
sceDmacMemcpy( snd_psp_buff[index], dma.buffer + ( pos * 2 ), len * 2 );
dma.samplepos += len;
}
else
{
int remaining = size - pos;
sceDmacMemcpy( snd_psp_buff[index], dma.buffer + ( pos * 2 ), remaining * 2 );
if( wrapped > 0 )
sceDmacMemcpy( snd_psp_buff[index] + ( remaining * 2 ), dma.buffer, wrapped * 2 );
dma.samplepos = wrapped;
}
sceKernelSignalSema( snd_psp.semaUID, 1 );
sceAudioOutputPannedBlocking( snd_psp.channel, snd_psp.volL, snd_psp.volR, snd_psp_buff[index] );
index = !index;
}
sceKernelExitThread( 0 );
return 0;
}
/*
==================
SNDDMA_Init
Try to find a sound device to mix for.
Returns false if nothing is found.
==================
*/
qboolean SNDDMA_Init( void )
{
int samplecount;
dma.format.speed = SOUND_DMA_SPEED;
dma.format.channels = PSP_OUTPUT_CHANNELS;
dma.format.width = 2;
// must be multiple of 64
samplecount = s_samplecount->value;
if( !samplecount )
samplecount = 0x4000;
dma.samples = samplecount * PSP_OUTPUT_CHANNELS;
dma.samplepos = 0;
dma.buffer = memalign( 64, dma.samples * 2 ); // 16 bit
if( !dma.buffer )
return false;
// clearing buffers
memset( dma.buffer, 0, dma.samples * 2 );
memset( snd_psp_buff, 0, sizeof( snd_psp_buff ) );
// allocate and initialize a hardware output channel
snd_psp.channel = sceAudioChReserve( PSP_AUDIO_NEXT_CHANNEL,
PSP_NUM_AUDIO_SAMPLES,
PSP_AUDIO_FORMAT_STEREO );
if( snd_psp.channel < 0 )
{
SNDDMA_Shutdown();
return false;
}
// create semaphore
snd_psp.semaUID = sceKernelCreateSema( "sound_sema", 0, 1, 255, NULL );
if( snd_psp.semaUID <= 0 )
{
SNDDMA_Shutdown();
return false;
}
// create audio thread
snd_psp.threadUID = sceKernelCreateThread( "sound_thread", SNDDMA_MainThread, 0x11, 0x8000, 0, 0 );
if( snd_psp.threadUID < 0 )
{
SNDDMA_Shutdown();
return false;
}
// start audio thread
if( sceKernelStartThread( snd_psp.threadUID, 0, 0 ) < 0 )
{
SNDDMA_Shutdown();
return false;
}
Con_Printf( "Using PSP audio driver: %d Hz\n", dma.format.speed );
dma.initialized = true;
return true;
}
/*
==============
SNDDMA_GetDMAPos
return the current sample position (in mono samples read)
inside the recirculating dma buffer, so the mixing code will know
how many sample are required to fill it up.
===============
*/
int SNDDMA_GetDMAPos( void )
{
return dma.samplepos;
}
/*
==============
SNDDMA_GetSoundtime
update global soundtime
===============
*/
int SNDDMA_GetSoundtime( void )
{
static int buffers, oldsamplepos;
int samplepos, fullsamples;
fullsamples = dma.samples / 2;
// it is possible to miscount buffers
// if it has wrapped twice between
// calls to S_Update. Oh well.
samplepos = SNDDMA_GetDMAPos();
if( samplepos < oldsamplepos )
{
buffers++; // buffer wrapped
if( paintedtime > 0x40000000 )
{
// time to chop things off to avoid 32 bit limits
buffers = 0;
paintedtime = fullsamples;
S_StopAllSounds( true );
}
}
oldsamplepos = samplepos;
return (buffers * fullsamples + samplepos / 2);
}
/*
==============
SNDDMA_BeginPainting
Makes sure dma.buffer is valid
===============
*/
void SNDDMA_BeginPainting( void )
{
if( snd_psp.semaUID > 0 )
sceKernelWaitSema( snd_psp.semaUID, 1, NULL );
}
/*
==============
SNDDMA_Submit
Send sound to device if buffer isn't really the dma buffer
Also unlocks the dsound buffer
===============
*/
void SNDDMA_Submit( void )
{
if( snd_psp.semaUID > 0 )
sceKernelSignalSema( snd_psp.semaUID, 1 );
}
/*
==============
SNDDMA_Shutdown
Reset the sound device for exiting
===============
*/
void SNDDMA_Shutdown( void )
{
Con_Printf("Shutting down audio.\n");
snd_psp.running = 0;
if( snd_psp.threadUID >= 0 )
{
sceKernelWaitThreadEnd( snd_psp.threadUID, NULL );
sceKernelDeleteThread( snd_psp.threadUID );
snd_psp.threadUID = -1;
}
if( snd_psp.semaUID > 0 )
{
sceKernelDeleteSema( snd_psp.semaUID );
snd_psp.semaUID = -1;
}
if( snd_psp.channel >= 0 )
{
sceAudioChRelease( snd_psp.channel );
snd_psp.channel = -1;
}
if( dma.buffer )
{
free( dma.buffer );
dma.buffer = NULL;
}
dma.initialized = false;
}
#endif // XASH_SOUND == SOUND_PSP

View File

@@ -379,7 +379,7 @@ class PSP:
def stdlibs(self):
stdlibs = []
stdlibs += ['-lpspdisplay', '-lpspgum_vfpu', '-lpspgu','-lpspge', '-lpspvfpu']
stdlibs += ['-lpspaudiolib', '-lpspaudio']
stdlibs += ['-lpspaudio', '-lpspdmac']
stdlibs += ['-lstdc++', '-lc', '-lm']
stdlibs += ['-lpspctrl', '-lpspdebug', '-lpsppower', '-lpsputility', '-lpspsdk', '-lpsprtc']
stdlibs += ['-lpspuser', '-lpspkernel']