mirror of
https://github.com/FWGS/xash3d-fwgs.git
synced 2026-08-05 03:24:56 +08:00
sound: psp sound output rewritten
This commit is contained in:
@@ -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
|
||||
276
engine/platform/psp/snd_psp.c
Normal file
276
engine/platform/psp/snd_psp.c
Normal 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
|
||||
@@ -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']
|
||||
|
||||
Reference in New Issue
Block a user