mirror of
https://github.com/thug1src/thug.git
synced 2024-12-02 12:56:45 +00:00
1513 lines
48 KiB
C++
1513 lines
48 KiB
C++
//--------------------------------------------------------------------------------------------
|
|
// Playstation 2 sound effect functionality...
|
|
// Taken from samples on Sony website, modified by mjd.
|
|
//--------------------------------------------------------------------------------------------
|
|
|
|
#include <eetypes.h>
|
|
#include <eekernel.h>
|
|
#include <stdio.h>
|
|
#include <sif.h>
|
|
#include <sifdev.h>
|
|
#include <sdrcmd.h>
|
|
#include <libsdr.h>
|
|
#include <sdmacro.h>
|
|
#include <string.h>
|
|
|
|
#include <core/macros.h>
|
|
#include <core/defines.h>
|
|
#include <gel/soundfx/soundfx.h>
|
|
#include <gel/soundfx/ngps/p_sfx.h>
|
|
#include <gel/music/ngps/pcm/pcm.h>
|
|
#include <gel/music/ngps/p_music.h>
|
|
#include <gel/music/music.h>
|
|
#include <sys/file/filesys.h>
|
|
|
|
#include <gel/music/ngps/pcm/pcm.h>
|
|
|
|
#define NO_SOUND 0
|
|
|
|
namespace Pcm
|
|
{
|
|
extern int gNonAllignedIopBuffer;
|
|
};
|
|
|
|
namespace Sfx
|
|
{
|
|
|
|
|
|
|
|
// Macros and constants:
|
|
|
|
// This array maps the platform-independent reverb modes specified in reverb.q to the available Ps2 reverb modes.
|
|
int ReverbModes[] =
|
|
{
|
|
// put the default first in this list, since the default param to SetReverb() is 0
|
|
SD_REV_MODE_HALL, // REV_MODE_DEFAULT
|
|
SD_REV_MODE_HALL, // REV_MODE_GENERIC
|
|
SD_REV_MODE_ROOM, // REV_MODE_PADDEDCELL
|
|
SD_REV_MODE_ROOM, // REV_MODE_ROOM
|
|
SD_REV_MODE_STUDIO_A, // REV_MODE_BATHROOM
|
|
SD_REV_MODE_ROOM, // REV_MODE_LIVINGROOM
|
|
SD_REV_MODE_STUDIO_C, // REV_MODE_STONEROOM
|
|
SD_REV_MODE_HALL, // REV_MODE_AUDITORIUM
|
|
SD_REV_MODE_HALL, // REV_MODE_CONCERTHALL
|
|
SD_REV_MODE_ROOM, // REV_MODE_CAVE
|
|
SD_REV_MODE_HALL, // REV_MODE_ARENA
|
|
SD_REV_MODE_HALL, // REV_MODE_HANGAR
|
|
SD_REV_MODE_HALL, // REV_MODE_CARPETEDHALLWAY
|
|
SD_REV_MODE_HALL, // REV_MODE_HALLWAY
|
|
SD_REV_MODE_HALL, // REV_MODE_STONECORRIDOR
|
|
SD_REV_MODE_HALL, // REV_MODE_ALLEY
|
|
SD_REV_MODE_HALL, // REV_MODE_FOREST
|
|
SD_REV_MODE_HALL, // REV_MODE_CITY
|
|
SD_REV_MODE_HALL, // REV_MODE_MOUNTAINS
|
|
SD_REV_MODE_HALL, // REV_MODE_QUARRY
|
|
SD_REV_MODE_HALL, // REV_MODE_PLAIN
|
|
SD_REV_MODE_HALL, // REV_MODE_PARKINGLOT
|
|
SD_REV_MODE_PIPE, // REV_MODE_SEWERPIPE
|
|
SD_REV_MODE_PIPE, // REV_MODE_UNDERWATER
|
|
};
|
|
|
|
#define NUM_REVERB_MODES ( ( int )( sizeof( ReverbModes ) / sizeof( int ) ) )
|
|
|
|
#if NUM_STREAMS > 3
|
|
#error tell matt to handle more than 3 streams in sfx.cpp
|
|
#endif
|
|
|
|
#if NUM_STREAMS == 3
|
|
#define VOICES_MINUS_STREAMS ( 0xFFFFFF & ~( ( 1 << STREAM_VOICE( 0 ) ) | ( 1 << STREAM_VOICE( 1 ) ) | ( 1 << STREAM_VOICE( 2 ) ) ) )
|
|
#else
|
|
#define VOICES_MINUS_STREAMS ( 0xFFFFFF & ~( ( 1 << MUSIC_L_VOICE ) | ( 1 << MUSIC_R_VOICE ) ) )
|
|
#endif
|
|
#define VOICES_MINUS_NON_REVERB ( ( ( 1 << NUM_NO_REVERB_VOICES ) - 1 ) << NUM_NO_REVERB_START_VOICE )
|
|
|
|
// convert sample rate to pitch setting
|
|
#define FREQ2PITCH( x ) ( ( ( float ) ( ( x ) * 100 ) / 48000.0f ) )
|
|
|
|
#define DMA_CH 0
|
|
|
|
#if ( ( CORE_0_EFFECTS_END_ADDRESS - CORE_0_EFFECTS_START_ADDRESS ) < ( RAM_NEEDED_FOR_EFFECTS ) )
|
|
#error "not enough room for effects"
|
|
#endif
|
|
|
|
int sTargetReverbDepth = 0;
|
|
int sCurrentReverbDepth = 0;
|
|
|
|
// allocated SPU RAM
|
|
int SpuRAMUsedTemp = 0;
|
|
int SpuRAMUsedPerm = 0;
|
|
|
|
int LoopingSounds[ NUM_CORES ];
|
|
// gotta keep track of voices used, to be able
|
|
// to tell whether a voice is free or not...
|
|
volatile int VoicesUsed[ NUM_CORES ];
|
|
volatile int VoicesCleared[ NUM_CORES ];
|
|
|
|
// avoid checking voice status more than once per frame:
|
|
//uint64 gLastVoiceAvailableUpdate[ NUM_CORES ];
|
|
//int gAvailableVoices[ NUM_CORES ];
|
|
float gSfxVolume = 100.0f;
|
|
|
|
#define MAX_VOLUME 0x3FFF // max playstation 2 volume...
|
|
#define MAX_REVERB_DEPTH 0x3FFF
|
|
#define MAX_PITCH 0x3FFF
|
|
#define UNALTERED_PITCH 4096
|
|
|
|
#define BLOCKHEADER_LOOP_BIT ( 1 << 9 )
|
|
|
|
// Internal function prototypes:
|
|
|
|
float GetPitchValue( VagHeader *VagHdr );
|
|
int convertEnd( int inp );
|
|
bool PS2Sfx_LoadVAG( PlatformWaveInfo *pInfo, const char *filename, bool loadPerm, float *samplePitch = NULL );
|
|
int GetCore( int whichVoice );
|
|
|
|
int PS2Sfx_PlaySound( PlatformWaveInfo *pInfo, int voiceNumber, float volL = 100.0f, float volR = 100.0f, float pitch = 100.0f );
|
|
void PS2Sfx_StopSound( int voiceNumber );
|
|
int PS2Sfx_GetVoiceStatus( int core );
|
|
|
|
#define ALLIGN_REQUIREMENT 64 // EE to IOP
|
|
#define SIZE_OF_LEADIN 16
|
|
#define USE_CLEVER_TRICK_FOR_CLOBBERED_DATA 1
|
|
|
|
// IOP_MIN_TRANSFER must be a power of 2!!!
|
|
#define IOP_MIN_TRANSFER 64 // iop will clobber memory if you're writing above a sound. we'll restore the
|
|
// potentially clobbered memory by keeping a buffer containing the beginning of
|
|
// the previously loaded perm sound, and copying that to the end of our new
|
|
// data...
|
|
|
|
#if USE_CLEVER_TRICK_FOR_CLOBBERED_DATA
|
|
#define PAD_TO_PREVENT_CLOBBERING 0
|
|
#else
|
|
#define PAD_TO_PREVENT_CLOBBERING 64
|
|
#endif
|
|
|
|
#define FULL_NAME_SIZE 256
|
|
|
|
int GetMemAvailable( void )
|
|
{
|
|
|
|
return ( MAX_SPU_RAM_AVAILABLE - ( SpuRAMUsedTemp + SpuRAMUsedPerm ) );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------
|
|
// Function: int EELoadVAG(char *filename ); //, int *samplePitch)
|
|
// Description: Transfers specified filename into SPU2 RAM and returns an address value for
|
|
// for use in the SetVoice() function
|
|
// Parameters: filename to be transfered
|
|
//
|
|
// Returns: value referencing the vag address in SPU2 RAM
|
|
// Notes: This transfers all data in a blocking fashion, but quickly.
|
|
//-------------------------------------------------------------------------------------------
|
|
bool PS2Sfx_LoadVAG( PlatformWaveInfo *pInfo, const char *filename, bool loadPerm, float *samplePitch)
|
|
{
|
|
|
|
|
|
uint32 size = 0; // file size
|
|
int vagAddr = 0; // VAG address in SPU2 RAM, returned to calling function
|
|
VagHeader vagHeader;
|
|
char fullname[ FULL_NAME_SIZE ]; // sound names can be HUGE...
|
|
unsigned int IOPbuffer = 0; // IOP memory buffer
|
|
|
|
Dbg_MsgAssert( strlen( filename ) < FULL_NAME_SIZE - 30,( "Increase buffer for full filename..." ));
|
|
sprintf(fullname, "sounds\\vag\\%s.vag", filename);
|
|
//Matt("Loading sound %s\n", fullname);
|
|
|
|
void *fp = File::Open( fullname, "rb" );
|
|
if ( !fp )
|
|
{
|
|
Dbg_MsgAssert( 0,( "LoadSound: Couldn't find file %s", filename ));
|
|
printf( "load sound failed to find %s\n", filename );
|
|
return ( false );
|
|
}
|
|
File::Seek( fp, 0, SEEK_SET );
|
|
if ( SIZE_OF_VAGHEADER != File::Read( &vagHeader, 1, SIZE_OF_VAGHEADER, fp ) )
|
|
{
|
|
// error reading header...
|
|
Dbg_MsgAssert( 0,( "Sorry, young lady, but your VAG is too small" ));
|
|
// Dbg_MsgAssert( 0,( "Not enough bytes to satisfy our VAG" ));
|
|
}
|
|
/* this fucked things up...
|
|
int endType = 0;
|
|
if ( strncmp( vagHeader.ID, "VAGp", 4 ) == 0 )
|
|
endType = 0;
|
|
else
|
|
endType = 1;
|
|
|
|
size = ( ( endType == 0 ) ? convertEnd( vagHeader.dataSize ) : vagHeader.dataSize );
|
|
*/
|
|
size = convertEnd( vagHeader.dataSize );
|
|
|
|
Dbg_MsgAssert( size > 0,( "There's nothing worse than an empty VAG." ));
|
|
|
|
//Dbg_Message( "vag header size %d data size %d", SIZE_OF_VAGHEADER, size );
|
|
|
|
#if USE_CLEVER_TRICK_FOR_CLOBBERED_DATA
|
|
uint8 *buffer = new uint8[ size + ALLIGN_REQUIREMENT + IOP_MIN_TRANSFER ];
|
|
#else
|
|
uint8 *buffer = new uint8[ size + ALLIGN_REQUIREMENT ];
|
|
#endif
|
|
File::Seek( fp, SIZE_OF_VAGHEADER, SEEK_SET );
|
|
|
|
uint8 *allignedBuffer = ( uint8 * )( ( ( int ) buffer + ( ALLIGN_REQUIREMENT - 1 ) ) & ~( ALLIGN_REQUIREMENT - 1 ) );
|
|
|
|
uint32 readsize = File::Read( allignedBuffer, 1, size, fp );
|
|
if ( size != readsize)
|
|
{
|
|
Dbg_Message( "if non-converted size %d is the correct size of file %s, inconsistent VAG format", vagHeader.dataSize + SIZE_OF_VAGHEADER, filename );
|
|
Dbg_MsgAssert( 0,( "VAG %s is %d bytes, not as big as she said it would be (%d bytes)", filename, readsize, size ));
|
|
}
|
|
File::Close( fp );
|
|
|
|
if ( samplePitch )
|
|
{
|
|
*samplePitch = GetPitchValue( &vagHeader );
|
|
}
|
|
// Dbg_Message( "Pitch value %f", GetPitchValue( &vagHeader ) );
|
|
|
|
// make sure we've got room for this sound in RAM:
|
|
// sound data must be 16byte aligned in the SPU, so check for that
|
|
// also, the IOP seems to transfer only in 128 byte chunks, so let's
|
|
// pad the buffer if we're writing a permanent sound (which stacks from
|
|
// the bottom if RAM up) and write the potentially clobbered bytes from
|
|
// the previously loaded permanent sound into the end of the buffer to
|
|
// be transfered (clever huh?) NOTE: This didn't seem to work without
|
|
// flushing the cache... Gives everything in memory time to settle I
|
|
// guess?
|
|
|
|
if ( loadPerm )
|
|
{
|
|
#if USE_CLEVER_TRICK_FOR_CLOBBERED_DATA
|
|
int i;
|
|
int overshoot = 0; // need to initialize to zero!
|
|
static uint8 prevPermBuffer[ IOP_MIN_TRANSFER ];
|
|
static int prevVagAddr;
|
|
if ( !SpuRAMUsedPerm )
|
|
{
|
|
// don't store nuthin' first time through...
|
|
prevVagAddr = 0;
|
|
}
|
|
#endif
|
|
// stack the permanent sounds up from the bottom:
|
|
SpuRAMUsedPerm += ( size + PAD_TO_PREVENT_CLOBBERING );
|
|
if ( ( END_WAVE_DATA_ADDR - SpuRAMUsedPerm ) & 0x0F )
|
|
{
|
|
#ifdef __NOPT_ASSERT__
|
|
int oldSpuRAM = SpuRAMUsedPerm;
|
|
#endif
|
|
SpuRAMUsedPerm += ( ( END_WAVE_DATA_ADDR - SpuRAMUsedPerm ) & 0x0F );
|
|
Dbg_MsgAssert( SpuRAMUsedPerm > oldSpuRAM,( "Duh asshole." ));
|
|
}
|
|
vagAddr = END_WAVE_DATA_ADDR - SpuRAMUsedPerm;
|
|
Dbg_MsgAssert( !( vagAddr & 0x0F ),( "Fire Matt please." ));
|
|
|
|
#if USE_CLEVER_TRICK_FOR_CLOBBERED_DATA
|
|
if ( prevVagAddr )
|
|
{
|
|
// don't have to do this if this is the first loaded perm sound...
|
|
// hence condition above...
|
|
|
|
// find out how much of the previous perm data we'd be writing
|
|
// over, add that to the end of our buffer...
|
|
if ( size & ( IOP_MIN_TRANSFER - 1 ) )
|
|
{
|
|
// Dbg_Message( "size %d over 64-byte boundary", size & ( IOP_MIN_TRANSFER - 1 ) );
|
|
overshoot = ( IOP_MIN_TRANSFER ) - ( size & ( IOP_MIN_TRANSFER - 1 ) );
|
|
// Dbg_Message( "overshoot %d", overshoot );
|
|
int spaceBetween = prevVagAddr - ( vagAddr + size );
|
|
// Dbg_Message( "space between %d", spaceBetween );
|
|
int numBytesClobbered = overshoot - spaceBetween;
|
|
Dbg_MsgAssert( numBytesClobbered <= IOP_MIN_TRANSFER,( "how in the world?" ));
|
|
// Dbg_Message( "numBytesClobbered = %d", numBytesClobbered );
|
|
for ( i = 0; i < numBytesClobbered; i++ )
|
|
{
|
|
// store the previous buffer at the end of our buffer,
|
|
allignedBuffer[ size + i + spaceBetween ] = prevPermBuffer[ i ];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
overshoot = 0;
|
|
}
|
|
size += overshoot;
|
|
// add on the size of the overshoot, which in the case of permanent sounds
|
|
// has data from a previous sound (which will be clobbered), written in.
|
|
// size += overshoot;
|
|
Dbg_MsgAssert( !( size & ( IOP_MIN_TRANSFER - 1 ) ),( "Fire Matt." ));
|
|
|
|
}
|
|
for ( i = 0; i < IOP_MIN_TRANSFER; i++ )
|
|
{
|
|
// store the beginning of our new buffer into the static buffer
|
|
// for when the next perm sound loads...
|
|
prevPermBuffer[ i ] = allignedBuffer[ i ];
|
|
}
|
|
prevVagAddr = vagAddr;
|
|
#endif
|
|
} // if ( loadPerm )
|
|
else
|
|
{
|
|
if ( ( SpuRAMUsedTemp + BASE_WAVE_DATA_ADDR ) & 0x0F )
|
|
{
|
|
SpuRAMUsedTemp += 0x10 - ( ( SpuRAMUsedTemp + BASE_WAVE_DATA_ADDR ) & 0x0F );
|
|
}
|
|
vagAddr = BASE_WAVE_DATA_ADDR + SpuRAMUsedTemp;
|
|
SpuRAMUsedTemp += ( size );
|
|
}
|
|
|
|
// transfer buffer to IOP, it's a DMA operation so FlushCache() to ensure cache is flushed
|
|
FlushCache( 0 );
|
|
|
|
// Ken: Instead of borrowing a new chunk of memory from the IOP, (which causes it to often
|
|
// run out cos we're so tight on memory) use the already existing
|
|
// gNonAllignedIopBuffer, used by streams and music etc. They have no need of it at this point.
|
|
//IOPbuffer = ( unsigned int )sceSifAllocIopHeap( size );
|
|
Dbg_MsgAssert(size<TOTAL_IOP_BUFFER_SIZE_NEEDED + ALLIGN_REQUIREMENT,("Oops! Vag '%s' too big for iop buffer",filename));
|
|
Dbg_MsgAssert(Pcm::gNonAllignedIopBuffer,("NULL gNonAllignedIopBuffer"));
|
|
IOPbuffer=Pcm::gNonAllignedIopBuffer;
|
|
|
|
Dbg_MsgAssert( !( IOPbuffer & 3 ),( "This address should be four byte alligned." ));
|
|
if ( !IOPbuffer )
|
|
{
|
|
Dbg_MsgAssert( 0,( "Failed to allocate IOP memory\n" ));
|
|
return false;
|
|
}
|
|
|
|
if ( sceSdTransToIOP( allignedBuffer, IOPbuffer, size, 1 ) == -1 )
|
|
{
|
|
Dbg_MsgAssert( 0,( "Failed to transfer to IOP\n" ));
|
|
return false;
|
|
}
|
|
// now transfer from IOP memory into SPU2 RAM
|
|
|
|
if ( ( SpuRAMUsedTemp + SpuRAMUsedPerm + IOP_MIN_TRANSFER ) > MAX_SPU_RAM_AVAILABLE )
|
|
{
|
|
Dbg_MsgAssert( 0,( "Our VAG space has been RAMed to the hilt" ));
|
|
}
|
|
|
|
if (!CSpuManager::sVoiceTrans(DMA_CH, SD_TRANS_MODE_WRITE | SD_TRANS_BY_DMA, (void *) IOPbuffer, (uint32 *) vagAddr, size))
|
|
{
|
|
Dbg_MsgAssert( 0,( "Failed to Transfer to SPU2 size %d vagAddr %x IOPBuffer addr %x\n", size, vagAddr, IOPbuffer ));
|
|
}
|
|
|
|
// Ken: Commented this out because now we're using Pcm::gNonAllignedIopBuffer instead.
|
|
// Now free up IOP and EE memory, check this return value is zero, as documentation (iopservice.txt v1.5) seems to be wrong
|
|
//if( sceSifFreeIopHeap( (void * ) IOPbuffer ) != 0 )
|
|
//{
|
|
// Dbg_MsgAssert( 0,( "Failed to free IOP memory\n" ));
|
|
//}
|
|
|
|
pInfo->vagAddress = vagAddr;
|
|
uint16 blockHeader = *( ( uint16 * ) allignedBuffer + SIZE_OF_LEADIN );
|
|
pInfo->looping = ( blockHeader & BLOCKHEADER_LOOP_BIT ) ? true : false;
|
|
|
|
delete buffer;
|
|
return ( true );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------
|
|
// Function : float GetPitchValue(char *buffer)
|
|
// Description : gets pitch value from VAG header
|
|
//
|
|
// Parameters : pointer to VAG header
|
|
//
|
|
// Returns : pitch value
|
|
// Notes :
|
|
//-------------------------------------------------------------------------------------------
|
|
float GetPitchValue( VagHeader *VagHdr )
|
|
{
|
|
|
|
/* int endType = 0;
|
|
|
|
if ( strncmp( VagHdr->ID, "VAGp", 4 ) == 0 )
|
|
endType = 0;
|
|
else
|
|
endType = 1;
|
|
|
|
if(endType == 0)
|
|
{*/
|
|
return ( ( FREQ2PITCH( convertEnd( VagHdr->sampleFreq ) ) ) );
|
|
/* }
|
|
else
|
|
{
|
|
return ( FREQ2PITCH( VagHdr->sampleFreq ) );
|
|
}
|
|
return 0;*/
|
|
}
|
|
|
|
#ifdef __USER_MATT__
|
|
//--------------------------------------------------------------------------------------------
|
|
// Function : void PrintVAGDetails(char *buffer)
|
|
// Description : debug function to print out VAG details
|
|
//
|
|
// Parameters : pointer to VAG header
|
|
//
|
|
// Returns :
|
|
// Notes :
|
|
//-------------------------------------------------------------------------------------------
|
|
void PrintVAGDetails(char *buffer)
|
|
{
|
|
|
|
VagHeader *VagHdr;
|
|
int i;
|
|
int endType = 0; // 0 for PC, 1 for MAC
|
|
|
|
VagHdr = (VagHeader *)buffer;
|
|
|
|
Dbg_Message("Checking for VAGp string ..'");
|
|
for(i=0;i<4;i++)
|
|
{
|
|
Dbg_Message("%c",VagHdr->ID[i]);
|
|
}
|
|
Dbg_Message("' found.\n");
|
|
|
|
Dbg_Message("VAG Details\n");
|
|
Dbg_Message("===========\n");
|
|
|
|
// check for either little or big endian format (PC or MAC)
|
|
if(strncmp(VagHdr->ID,"VAGp",4) == 0)
|
|
endType = 0;
|
|
else
|
|
endType = 1;
|
|
|
|
Dbg_Message("ID : ");
|
|
for(i=0;i<4;i++)
|
|
{
|
|
Dbg_Message("%c",VagHdr->ID[i]);
|
|
}
|
|
Dbg_Message("\n");
|
|
|
|
Dbg_Message("VERSION : ");
|
|
Dbg_Message("0x%x", (endType == 0) ? convertEnd(VagHdr->version) : VagHdr->version);
|
|
Dbg_Message("\n");
|
|
|
|
Dbg_Message("RESERVED : ");
|
|
for(i=0;i<4;i++)
|
|
{
|
|
Dbg_Message("%x",VagHdr->reserved1[i]);
|
|
}
|
|
Dbg_Message("\n");
|
|
|
|
Dbg_Message("DATASIZE : ");
|
|
Dbg_Message("0x%x", (endType == 0) ? convertEnd(VagHdr->dataSize) : VagHdr->dataSize);
|
|
Dbg_Message("\n");
|
|
|
|
Dbg_Message("SAMPLEFREQ : ");
|
|
Dbg_Message("0x%x (%d)", (endType == 0) ? convertEnd(VagHdr->sampleFreq) : VagHdr->sampleFreq, (endType == 0) ? convertEnd(VagHdr->sampleFreq) : VagHdr->sampleFreq);
|
|
Dbg_Message("\n");
|
|
|
|
Dbg_Message("RESERVED : ");
|
|
for(i=0;i<12;i++)
|
|
{
|
|
Dbg_Message("%x",VagHdr->reserved2[i]);
|
|
}
|
|
Dbg_Message("\n");
|
|
|
|
Dbg_Message("NAME : ");
|
|
for(i=0;i<16;i++)
|
|
{
|
|
if(VagHdr->name[i] == '\0')
|
|
break;
|
|
Dbg_Message("%c",VagHdr->name[i]);
|
|
}
|
|
Dbg_Message("\n");
|
|
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
//--------------------------------------------------------------------------------------------
|
|
// Function : int convertEnd(int inp)
|
|
// Description : needed for swopping endian values if VAG has been created by PC
|
|
//
|
|
// Parameters :
|
|
//
|
|
// Returns :
|
|
// Notes :
|
|
//-------------------------------------------------------------------------------------------
|
|
int convertEnd(int inp)
|
|
{
|
|
|
|
// swap bytes 0 and 4, 1 and 2
|
|
return((inp & 0xFF) << 24) | ((inp & 0xFF00) << 8) | ((inp & 0xFF0000) >> 8) | ((inp & 0xFF000000) >> 24);
|
|
}
|
|
|
|
// Master volumeLevel from 0 to 100 percent...
|
|
void SetVolumePlease( float volumeLevel )
|
|
{
|
|
|
|
gSfxVolume = volumeLevel;
|
|
|
|
Pcm::PCMAudio_SetStreamGlobalVolume( ( unsigned int ) volumeLevel );
|
|
/*
|
|
int vol = ( int )PERCENT( volumeLevel, MAX_VOLUME );
|
|
if ( vol > MAX_VOLUME )
|
|
vol = MAX_VOLUME;
|
|
|
|
Dbg_MsgAssert( vol >= 0.0f,( "Can't have negative main volume." ));
|
|
|
|
int i;
|
|
// set master volume for both cores, both blocking
|
|
for ( i = 0; i < 2; i++ )
|
|
{
|
|
|
|
if ( !CSpuManager::sSendSdRemote( rSdSetParam, i | SD_P_MVOLL, vol ) )
|
|
{
|
|
Dbg_MsgAssert( 0,( "Error Setting Left Vol on core %d\n",i));
|
|
}
|
|
if ( !CSpuManager::sSendSdRemote( rSdSetParam, i | SD_P_MVOLR, vol ) )
|
|
{
|
|
Dbg_MsgAssert( 0,( "Error Setting Right Vol on core %d\n",i));
|
|
}
|
|
}*/
|
|
} // end of SetVolumePlease( )
|
|
|
|
void ReverbOff( void )
|
|
{
|
|
|
|
int i;
|
|
sceSdEffectAttr r_attr;
|
|
|
|
sTargetReverbDepth = sCurrentReverbDepth = 0;
|
|
// --- set reverb attribute
|
|
r_attr.depth_L = 0;
|
|
r_attr.depth_R = 0;
|
|
r_attr.delay = 0;
|
|
r_attr.feedback = 0;
|
|
r_attr.mode = SD_REV_MODE_OFF;
|
|
#if REVERB_ONLY_ON_CORE_0
|
|
i = 0; // just set reverb on core 0 (not the music core or any sfx on core 1)
|
|
#else
|
|
for ( i = 0; i < 2; i++ )
|
|
#endif
|
|
{
|
|
// Shut volume first
|
|
if(!CSpuManager::sSendSdRemote( rSdSetParam, i | SD_P_EVOLL , sTargetReverbDepth ) )
|
|
Dbg_Message("Error setting reverb2\n");
|
|
if(!CSpuManager::sSendSdRemote( rSdSetParam, i | SD_P_EVOLR , sTargetReverbDepth ) )
|
|
Dbg_Message("Error setting reverb3\n");
|
|
|
|
// just turning off reverb on all voices...
|
|
if(!CSpuManager::sSendSdRemote( rSdSetSwitch, i | SD_S_VMIXEL , 0 ) )
|
|
Dbg_Message("Error setting reverb4\n");
|
|
if(!CSpuManager::sSendSdRemote( rSdSetSwitch, i | SD_S_VMIXER , 0 ) )
|
|
Dbg_Message("Error setting reverb5\n");
|
|
if( !CSpuManager::sSendSdRemote( rSdSetEffectAttr, i, (uint32) &r_attr ) )
|
|
Dbg_Message( "Error setting reverb\n" );
|
|
}
|
|
}
|
|
|
|
#define REVERB_STEP ( ( MAX_REVERB_DEPTH / 20 ) + 1 )
|
|
|
|
void DoReverbFade( void )
|
|
{
|
|
int i;
|
|
if ( sTargetReverbDepth != sCurrentReverbDepth )
|
|
{
|
|
if ( sCurrentReverbDepth < sTargetReverbDepth )
|
|
{
|
|
sCurrentReverbDepth += REVERB_STEP;
|
|
if ( sCurrentReverbDepth > sTargetReverbDepth )
|
|
{
|
|
sCurrentReverbDepth = sTargetReverbDepth;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sCurrentReverbDepth -= REVERB_STEP;
|
|
if ( sCurrentReverbDepth < sTargetReverbDepth )
|
|
{
|
|
sCurrentReverbDepth = sTargetReverbDepth;
|
|
if ( !sTargetReverbDepth )
|
|
{
|
|
ReverbOff( );
|
|
}
|
|
}
|
|
}
|
|
|
|
#if REVERB_ONLY_ON_CORE_0
|
|
i = 0; // just set reverb on core 0 (not the music core or any sfx on core 1)
|
|
#else
|
|
for ( i = 0; i < 2; i++ )
|
|
#endif
|
|
{
|
|
if(!CSpuManager::sSendSdRemote( rSdSetParam, i | SD_P_EVOLL , sCurrentReverbDepth ) )
|
|
Dbg_Message("Error setting reverb2\n");
|
|
|
|
if(!CSpuManager::sSendSdRemote( rSdSetParam, i | SD_P_EVOLR , sCurrentReverbDepth ) )
|
|
Dbg_Message("Error setting reverb3\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
void InitReverbAddr( void )
|
|
{
|
|
#if REVERB_ONLY_ON_CORE_0
|
|
if ( !CSpuManager::sSendSdRemote( rSdSetAddr, 0 | SD_A_ESA, CORE_0_EFFECTS_START_ADDRESS ) )
|
|
Dbg_Message( "Error setting reverb\n" );
|
|
if ( !CSpuManager::sSendSdRemote( rSdSetAddr, 0 | SD_A_EEA, CORE_0_EFFECTS_END_ADDRESS ) )
|
|
Dbg_Message( "Error setting reverb\n" );
|
|
#else
|
|
if ( !CSpuManager::sSendSdRemote( rSdSetAddr, 0 | SD_A_ESA, CORE_0_EFFECTS_START_ADDRESS ) )
|
|
Dbg_Message( "Error setting reverb\n" );
|
|
if ( !CSpuManager::sSendSdRemote( rSdSetAddr, 0 | SD_A_EEA, CORE_0_EFFECTS_END_ADDRESS ) )
|
|
Dbg_Message( "Error setting reverb\n" );
|
|
|
|
if ( !CSpuManager::sSendSdRemote( rSdSetAddr, 1 | SD_A_ESA, CORE_1_EFFECTS_START_ADDRESS ) )
|
|
Dbg_Message( "Error setting reverb\n" );
|
|
if ( !CSpuManager::sSendSdRemote( rSdSetAddr, 1 | SD_A_EEA, CORE_1_EFFECTS_END_ADDRESS ) )
|
|
Dbg_Message( "Error setting reverb\n" );
|
|
#endif
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------
|
|
// Function: void SetReverb(void)
|
|
// Description: Sets reverb level and type
|
|
// Parameters: none
|
|
//
|
|
// Returns:
|
|
//-------------------------------------------------------------------------------------------
|
|
void SetReverbPlease( float reverbLevel, int reverbType, bool instant )
|
|
{
|
|
|
|
int i;
|
|
sceSdEffectAttr r_attr;
|
|
|
|
Dbg_MsgAssert( reverbType >= 0,( "Bad reverb mode." ));
|
|
Dbg_MsgAssert( reverbType < NUM_REVERB_MODES,( "Bad reverb mode..." ));
|
|
|
|
if ( !reverbLevel )
|
|
{
|
|
sTargetReverbDepth = 0;
|
|
if ( instant )
|
|
{
|
|
ReverbOff( );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Calling this in case it wasn't set before. There is a bug with the SPU reg set code where some
|
|
// messages are lost
|
|
InitReverbAddr();
|
|
|
|
sTargetReverbDepth = ( int )PERCENT( MAX_REVERB_DEPTH, reverbLevel );
|
|
|
|
// --- set reverb attribute
|
|
r_attr.depth_L = 0x3fff;
|
|
r_attr.depth_R = 0x3fff;
|
|
r_attr.delay = 30;
|
|
r_attr.feedback = 200;
|
|
r_attr.mode = ReverbModes[ reverbType ];
|
|
#if REVERB_ONLY_ON_CORE_0
|
|
i = 0;
|
|
#else
|
|
for( i = 0; i < 2; i++ )
|
|
#endif
|
|
{
|
|
if( !CSpuManager::sSendSdRemote( rSdSetEffectAttr, i, (uint32) &r_attr ) )
|
|
Dbg_Message( "Error setting reverb\n" );
|
|
|
|
if(!CSpuManager::sSendSdRemote( rSdSetCoreAttr, i | SD_C_EFFECT_ENABLE, 1 ) )
|
|
Dbg_Message("Error setting reverb\n");
|
|
if ( instant )
|
|
{
|
|
if(!CSpuManager::sSendSdRemote( rSdSetParam, i | SD_P_EVOLL , sTargetReverbDepth ) )
|
|
Dbg_Message("Error setting reverb2\n");
|
|
if(!CSpuManager::sSendSdRemote( rSdSetParam, i | SD_P_EVOLR , sTargetReverbDepth ) )
|
|
Dbg_Message("Error setting reverb3\n");
|
|
sCurrentReverbDepth = sTargetReverbDepth;
|
|
}
|
|
|
|
uint32 reverb_voices = (i == NUM_NO_REVERB_CORE) ? VOICES_MINUS_NON_REVERB : VOICES_MINUS_STREAMS;
|
|
if(!CSpuManager::sSendSdRemote( rSdSetSwitch, i | SD_S_VMIXEL , reverb_voices ) )
|
|
Dbg_Message("Error setting reverb4\n");
|
|
if(!CSpuManager::sSendSdRemote( rSdSetSwitch, i | SD_S_VMIXER , reverb_voices ) )
|
|
Dbg_Message("Error setting reverb5\n");
|
|
|
|
}
|
|
}
|
|
} // end of SetReverb( )
|
|
|
|
//--------------------------------------------------------------------------------------------
|
|
// Function : void StopSound(int voiceNumber)
|
|
// Description : turns off voice
|
|
// Parameters : voiceNumber - voice to stop
|
|
//
|
|
// Returns:
|
|
// Notes:
|
|
//-------------------------------------------------------------------------------------------
|
|
void PS2Sfx_StopSound(int voiceNumber)
|
|
{
|
|
//Dbg_Message("Turning off voice number %d", voiceNumber);
|
|
|
|
int coreUsed;
|
|
|
|
if ( voiceNumber >= NUM_VOICES_PER_CORE )
|
|
{
|
|
voiceNumber -= NUM_VOICES_PER_CORE;
|
|
coreUsed = SD_CORE_1;
|
|
}
|
|
else
|
|
{
|
|
coreUsed = SD_CORE_0;
|
|
}
|
|
|
|
if ( !CSpuManager::sSendSdRemote( rSdSetSwitch, coreUsed | SD_S_KOFF , 1 << voiceNumber ) )
|
|
Dbg_MsgAssert( 0,( "Failed to turn off\n" ));
|
|
|
|
// turn off looping sound flag (if it's even on...)
|
|
// Garrett: Took out this line so that we don't possibly turn off and on a voice in the same SPU tick.
|
|
//LoopingSounds[ coreUsed ] &= ~( 1 << voiceNumber );
|
|
// SoundsThatWereSwitchedOff[ coreUsed ] |= ( 1 << voiceNumber );
|
|
// might not want this... can't write too often to a voice...
|
|
// Garrett: Took out this line so that we don't possibly turn off and on a voice in the same SPU tick.
|
|
// The only bad thing is this won't be cleared until the next frame.
|
|
//VoicesUsed[ coreUsed ] &= ~( 1 << voiceNumber );
|
|
VoicesCleared[ coreUsed ] |= 1 << voiceNumber;
|
|
return;
|
|
}
|
|
|
|
bool LoadSoundPlease( const char *sfxName, uint32 checksum, PlatformWaveInfo *pInfo, bool loadPerm )
|
|
{
|
|
|
|
Dbg_MsgAssert( pInfo,( "Null pointer to PlatformWaveInfo." ));
|
|
return PS2Sfx_LoadVAG( pInfo, sfxName, loadPerm, &pInfo->pitchAdjustmentForSamplingRate );
|
|
} // end of LoadSoundPlease( )
|
|
|
|
int GetCore( int voiceNumber )
|
|
{
|
|
|
|
if ( voiceNumber >= NUM_VOICES_PER_CORE )
|
|
{
|
|
voiceNumber -= NUM_VOICES_PER_CORE;
|
|
Dbg_MsgAssert( voiceNumber < NUM_VOICES_PER_CORE,( "Bad voice number." ));
|
|
return ( SD_CORE_1 );
|
|
}
|
|
Dbg_MsgAssert( voiceNumber >= 0,( "Bad voice number." ));
|
|
return ( SD_CORE_0 );
|
|
|
|
} // end of GetCore( )
|
|
|
|
//void SetVoiceParameters( int voiceNumber, float volL, float volR, float fPitch )
|
|
void SetVoiceParameters( int voiceNumber, sVolume *p_vol, float fPitch )
|
|
{
|
|
int coreUsed = GetCore( voiceNumber );
|
|
|
|
if ( coreUsed == SD_CORE_1 )
|
|
{
|
|
voiceNumber -= NUM_VOICES_PER_CORE;
|
|
}
|
|
Dbg_MsgAssert( voiceNumber < NUM_VOICES_PER_CORE,( "What in tarnation? Voice out of range." ));
|
|
|
|
// adjust for SFX Volume level:
|
|
p_vol->PercentageAdjustment( gSfxVolume );
|
|
|
|
int lVol = (int)PERCENT( MAX_VOLUME, p_vol->GetChannelVolume( 0 ));
|
|
int rVol = (int)PERCENT( MAX_VOLUME, p_vol->GetChannelVolume( 1 ));
|
|
|
|
if ( lVol > MAX_VOLUME )
|
|
lVol = MAX_VOLUME;
|
|
else if ( lVol < -MAX_VOLUME )
|
|
lVol = -MAX_VOLUME;
|
|
|
|
if ( rVol > MAX_VOLUME )
|
|
rVol = MAX_VOLUME;
|
|
else if ( rVol < -MAX_VOLUME )
|
|
rVol = -MAX_VOLUME;
|
|
|
|
if ( fPitch )
|
|
{
|
|
int pitch = ( int )PERCENT( UNALTERED_PITCH, fPitch );
|
|
if ( pitch > MAX_PITCH )
|
|
pitch = MAX_PITCH;
|
|
if ( pitch <= 0 )
|
|
pitch = 1;
|
|
if ( !CSpuManager::sSendSdRemote( rSdSetParam, coreUsed | ( voiceNumber << 1 ) | SD_VP_PITCH, pitch ) )
|
|
Dbg_Message("Failed pitch setting\n");
|
|
}
|
|
|
|
// for phasing effects (of sounds behind you), set the volume
|
|
// to 0x7fff (left channel will be negative if sound is behind
|
|
// us)
|
|
if ( lVol < 0 )
|
|
{
|
|
lVol = 0x7fff + lVol;
|
|
}
|
|
if ( rVol < 0 ) // Just in case we start phase shifting the right side instead
|
|
{
|
|
rVol = 0x7fff + rVol;
|
|
}
|
|
if ( !CSpuManager::sSendSdRemote( rSdSetParam, coreUsed | ( voiceNumber << 1 ) | SD_VP_VOLL, lVol ) )
|
|
Dbg_Message( "Error1\n" );
|
|
if ( !CSpuManager::sSendSdRemote( rSdSetParam, coreUsed | ( voiceNumber << 1 ) | SD_VP_VOLR, rVol ) )
|
|
Dbg_Message("Error2\n");
|
|
} // end of SetVoiceParameters( )
|
|
|
|
int PlaySoundPlease( PlatformWaveInfo *pInfo, sVolume *p_vol, float pitch, bool no_reverb )
|
|
{
|
|
#if NO_SOUND
|
|
return ( -1 );
|
|
#endif
|
|
|
|
int voiceAvailableFlags;
|
|
int i;
|
|
|
|
if ( no_reverb )
|
|
{
|
|
voiceAvailableFlags = PS2Sfx_GetVoiceStatus( NUM_NO_REVERB_CORE );
|
|
|
|
for ( i = NUM_NO_REVERB_START_VOICE; i < ( NUM_NO_REVERB_VOICES + NUM_NO_REVERB_START_VOICE ); i++ )
|
|
{
|
|
if ( voiceAvailableFlags & ( 1 << i ) )
|
|
{
|
|
return ( PS2Sfx_PlaySound( pInfo, i + (NUM_NO_REVERB_CORE * NUM_VOICES_PER_CORE), p_vol->GetChannelVolume( 0 ), p_vol->GetChannelVolume( 1 ), pitch ));
|
|
}
|
|
}
|
|
|
|
// Dbg_MsgAssert(0, ("Ran out of NO_REVERB voices"));
|
|
printf("WARNING: Ran out of NO_REVERB voices - using reverb voices\n");
|
|
}
|
|
|
|
voiceAvailableFlags = PS2Sfx_GetVoiceStatus( 0 );
|
|
for ( i = 0; i < NUM_VOICES_PER_CORE; i++ )
|
|
{
|
|
if ( voiceAvailableFlags & ( 1 << i ) )
|
|
{
|
|
//gAvailableVoices[ 0 ] &= ~( 1 << i );
|
|
return ( PS2Sfx_PlaySound( pInfo, i, p_vol->GetChannelVolume( 0 ), p_vol->GetChannelVolume( 1 ), pitch ));
|
|
}
|
|
}
|
|
|
|
// 2nd core ( 1st core busy, all voices used )
|
|
// For now, allow reverb sounds to use non-reverb voices. They are at the end, anyway
|
|
voiceAvailableFlags = PS2Sfx_GetVoiceStatus( 1 );
|
|
for ( i = 0; i < NUM_VOICES_PER_CORE; i++ )
|
|
{
|
|
if ( voiceAvailableFlags & ( 1 << i ) )
|
|
{
|
|
//gAvailableVoices[ 1 ] &= ~( 1 << i );
|
|
return ( PS2Sfx_PlaySound( pInfo, i + NUM_VOICES_PER_CORE, p_vol->GetChannelVolume( 0 ), p_vol->GetChannelVolume( 1 ), pitch ));
|
|
}
|
|
}
|
|
|
|
#ifdef __NOPT_ASSERT__
|
|
CSpuManager::sPrintStatus();
|
|
#endif
|
|
Dbg_MsgAssert(0, ("Can't play sound. Out of voices"));
|
|
return ( -1 );
|
|
} // end of PlaySoundPlease( )
|
|
|
|
void StopSoundPlease( int whichVoice )
|
|
{
|
|
|
|
|
|
if ( VoiceIsOn( whichVoice ) )
|
|
{
|
|
PS2Sfx_StopSound( whichVoice );
|
|
}
|
|
} // end of StopSoundPlease( )
|
|
|
|
void PauseSoundsPlease( void )
|
|
{
|
|
sVolume silent_volume;
|
|
silent_volume.SetSilent();
|
|
|
|
// just turn the volume down on all playing voices...
|
|
for( int i = 0; i < NUM_VOICES; i++ )
|
|
{
|
|
if( VoiceIsOn( i ))
|
|
{
|
|
SetVoiceParameters( i, &silent_volume );
|
|
}
|
|
}
|
|
}// end of PauseSounds( )
|
|
|
|
//--------------------------------------------------------------------------------------------
|
|
// Function : void StopAllSoundFX(void)
|
|
// Description : turns off all voices
|
|
// Parameters :
|
|
//
|
|
// Returns:
|
|
// Notes:
|
|
//-------------------------------------------------------------------------------------------
|
|
void StopAllSoundFX( void )
|
|
{
|
|
|
|
int core;
|
|
Pcm::StopMusic( );
|
|
Pcm::StopStreams( );
|
|
for ( core = 0; core < NUM_CORES; core++ )
|
|
{
|
|
// SoundsThatWereSwitchedOff[ core ] = ( ( 1 << NUM_VOICES_PER_CORE ) - 1 );
|
|
VoicesUsed[ core ] = 1; // So that the KOFF check thinks there are sounds to kill
|
|
VoicesCleared[ core ] = 0;
|
|
LoopingSounds[ core ] = 0;
|
|
// Dbg_Message( "Stopping all sfx -- remind Matt to fix what this is going to do to streams!!!" );
|
|
CSpuManager::sClean();
|
|
CSpuManager::sSendSdRemote( rSdSetSwitch, core | SD_S_KOFF, VOICES_MINUS_STREAMS );
|
|
CSpuManager::sSendSdRemote( rSdSetSwitch, core | SD_S_ENDX, 0 );
|
|
VoicesUsed[ core ] = 0;
|
|
}
|
|
return;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------
|
|
// Function : void PlaySound(int voiceNumber,int pitch,int volume)
|
|
// Description : Plays voice
|
|
// Parameters : voiceNumber - voice to play
|
|
// pitch - pitch to play at
|
|
// volume - volume to play at
|
|
//
|
|
// Returns: voice number
|
|
//-------------------------------------------------------------------------------------------
|
|
int PS2Sfx_PlaySound( PlatformWaveInfo *pInfo, int voiceNumber, float volL, float volR, float pitch )
|
|
{
|
|
|
|
int coreUsed = GetCore( voiceNumber );
|
|
int voiceNumOnCore;
|
|
|
|
if ( coreUsed == SD_CORE_1 )
|
|
{
|
|
voiceNumOnCore = voiceNumber - NUM_VOICES_PER_CORE;
|
|
}
|
|
else
|
|
voiceNumOnCore = voiceNumber;
|
|
|
|
if ( !CSpuManager::sSendSdRemote( rSdSetAddr, coreUsed | ( voiceNumOnCore << 1 ) | SD_VA_SSA, pInfo->vagAddress ) )
|
|
Dbg_Message( "Error4\n" );
|
|
|
|
// adjust pitch to account for lower sampling rates:
|
|
pitch = PERCENT( pitch, pInfo->pitchAdjustmentForSamplingRate );
|
|
|
|
// should be voiceNumber, not voiceNumOnCore:
|
|
sVolume vol;
|
|
vol.SetChannelVolume( 0, volL );
|
|
vol.SetChannelVolume( 1, volR );
|
|
SetVoiceParameters( voiceNumber, &vol, pitch );
|
|
|
|
if ( !CSpuManager::sSendSdRemote( rSdSetSwitch, coreUsed | SD_S_KON, 0x1 << voiceNumOnCore ) )
|
|
Dbg_Message("Failed setswitch\n");
|
|
|
|
if ( pInfo->looping )
|
|
{
|
|
LoopingSounds[ coreUsed ] |= ( 1 << voiceNumOnCore );
|
|
//Dbg_Message("Setting looping sound on voice %d core %d", voiceNumOnCore, coreUsed);
|
|
//Dbg_Assert(0);
|
|
}
|
|
|
|
// Set the VoicesUsed flag.
|
|
VoicesUsed[ coreUsed ] |= 1 << voiceNumOnCore;
|
|
// printf( "voice %d\n", voiceNumber );
|
|
//Dbg_Message("Voices Used (%x %x) Cleared (%x %x) Looped (%x %x) Voice Status (%x %x)", VoicesUsed[0], VoicesUsed[1], VoicesCleared[0], VoicesCleared[1],
|
|
// LoopingSounds[0], LoopingSounds[1], CSpuManager::sGetVoiceStatus(0), CSpuManager::sGetVoiceStatus(1));
|
|
|
|
return ( voiceNumber );
|
|
} // end of PS2Sfx_PlaySound( )
|
|
|
|
void CleanUpSoundFX( void )
|
|
{
|
|
|
|
StopAllSoundFX( );
|
|
SpuRAMUsedTemp = 0;
|
|
SetReverbPlease( 0 );
|
|
}// end of CleanUpSoundFX( )
|
|
|
|
#define NUM_VOICES_INCLUDING_STREAMS_PER_CORE 24
|
|
|
|
//--------------------------------------------------------------------------------------------
|
|
// Function : void InitSound( void )
|
|
// Description : initialise sound system
|
|
// Parameters :
|
|
//
|
|
// Returns:
|
|
// Notes:
|
|
//--------------------------------------------------------------------------------------------
|
|
void InitSoundFX( CSfxManager *p_sfx_manager )
|
|
{
|
|
// Set default volume type.
|
|
if( p_sfx_manager )
|
|
{
|
|
p_sfx_manager->SetDefaultVolumeType( VOLUME_TYPE_2_CHANNEL_DOLBYII );
|
|
}
|
|
|
|
int i, j;
|
|
// set master volume for both cores, both blocking
|
|
for ( i = 0; i < 2; i++ )
|
|
{
|
|
|
|
if ( !CSpuManager::sSendSdRemote( rSdSetParam, i | SD_P_MVOLL, MAX_VOLUME ) )
|
|
{
|
|
Dbg_MsgAssert( 0,( "Error Setting Left Vol on core %d\n",i));
|
|
}
|
|
if ( !CSpuManager::sSendSdRemote( rSdSetParam, i | SD_P_MVOLR, MAX_VOLUME ) )
|
|
{
|
|
Dbg_MsgAssert( 0,( "Error Setting Right Vol on core %d\n",i));
|
|
}
|
|
}
|
|
|
|
// SetVolumePlease( 100 );
|
|
|
|
// Sets the reverb address registers
|
|
InitReverbAddr();
|
|
|
|
CleanUpSoundFX( );
|
|
|
|
// Set the envelope info at the end because we sometimes lose messages
|
|
for ( i = 0; i < 2; i++ )
|
|
{
|
|
for ( j = 0; j < NUM_VOICES_INCLUDING_STREAMS_PER_CORE; j++ )
|
|
{
|
|
CSpuManager::sSendSdRemote ( rSdSetParam, i | ( j << 1 ) | SD_VP_ADSR1, 0x00ff );
|
|
CSpuManager::sSendSdRemote ( rSdSetParam, i | ( j << 1 ) | SD_VP_ADSR2, 0x1fc0 );
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void PS2Sfx_InitCold( void )
|
|
{
|
|
|
|
|
|
CSpuManager::sInit();
|
|
|
|
// int i;
|
|
// for ( i = 0; i < NUM_CORES; i++ )
|
|
// {
|
|
// gLastVoiceAvailableUpdate[ i ] = 0;
|
|
// gAvailableVoices[ i ] = 0;
|
|
// }
|
|
|
|
} // end of PS2Sfx_Init( )
|
|
|
|
//--------------------------------------------------------------------------------------------
|
|
// Function : int GetVoiceStatus( int core )
|
|
// Description : returns bitmask showing which voices are available
|
|
// Parameters : core - SPU core
|
|
//
|
|
// Returns : integer value which is the bitmask for voices
|
|
//--------------------------------------------------------------------------------------------
|
|
int PS2Sfx_GetVoiceStatus( int core )
|
|
{
|
|
|
|
#if 1
|
|
int availableVoices = CSpuManager::sGetVoiceStatus(core);
|
|
#else
|
|
int availableVoices;
|
|
|
|
if ( gLastVoiceAvailableUpdate[ core ] != Tmr::GetRenderFrame( ) )
|
|
{
|
|
gLastVoiceAvailableUpdate[ core ] = Tmr::GetRenderFrame( );
|
|
CSpuManager::sWaitForIO();
|
|
gAvailableVoices[ core ] = sceSdRemote( 1, rSdGetSwitch, core | SD_S_ENDX );
|
|
}
|
|
// available voices at this point will show all voices that have reached the end...
|
|
availableVoices = gAvailableVoices[ core ];
|
|
#endif
|
|
|
|
// except we've got to remove looping sounds (they set the ENDX flag first time they reach LOOPEND)
|
|
availableVoices &= ~LoopingSounds[ core ];
|
|
// include the voices that have not ever been used yet (if never used,
|
|
// a voice never has had a chance to set the ENDX register...)
|
|
availableVoices |= ~VoicesUsed[ core ];
|
|
|
|
return ( availableVoices );
|
|
}
|
|
|
|
bool VoiceIsOn( int whichVoice )
|
|
{
|
|
|
|
int whichCore;
|
|
int voiceAvailableFlags;
|
|
|
|
if ( whichVoice < NUM_VOICES_PER_CORE )
|
|
{
|
|
whichCore = 0;
|
|
}
|
|
else
|
|
{
|
|
whichCore = 1;
|
|
whichVoice -= NUM_VOICES_PER_CORE;
|
|
}
|
|
|
|
voiceAvailableFlags = PS2Sfx_GetVoiceStatus( whichCore );
|
|
return ( ( voiceAvailableFlags & ( 1 << whichVoice ) ) ? false : true );
|
|
} // end of VoiceIsOn( )
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
void PerFrameUpdate( void )
|
|
{
|
|
|
|
DoReverbFade( );
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
/// CSpuManager
|
|
|
|
volatile bool CSpuManager::s_sd_remote_busy;
|
|
volatile bool CSpuManager::s_sd_transferring_voice;
|
|
volatile uint32 CSpuManager::s_sd_voice_status[NUM_CORES];
|
|
volatile uint32 CSpuManager::s_sd_voice_status_update_frame[NUM_CORES] = { 0, 0 };
|
|
|
|
uint32 CSpuManager::s_spu_reg_kon[NUM_CORES] = { 0, 0 };
|
|
uint32 CSpuManager::s_spu_reg_koff[NUM_CORES] = { 0, 0 };
|
|
|
|
SSifCmdSoundRequestPacket CSpuManager::s_sd_remote_commands[CSpuManager::MAX_QUEUED_COMMANDS] __attribute__ ((aligned(16)));
|
|
volatile int CSpuManager::s_sd_remote_command_free_index;
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CSpuManager::sInit()
|
|
{
|
|
// Init variables
|
|
s_sd_remote_busy = false;
|
|
s_sd_remote_command_free_index = 0;
|
|
|
|
if ( sceSdRemoteInit( ) == -1 )
|
|
{
|
|
Dbg_MsgAssert( 0,( "Error Initialising sound\n"));
|
|
return;
|
|
}
|
|
|
|
// Initialise IOP side, blocking call
|
|
if ( sceSdRemote( 1, rSdInit, SD_INIT_COLD ) == -1 )
|
|
{
|
|
Dbg_MsgAssert( 0,( "Error Initialising IOP\n"));
|
|
return;
|
|
}
|
|
|
|
// Make sure the DMA ID's are 0 so we don't wait for random ID's
|
|
for (int i = 0; i < MAX_QUEUED_COMMANDS; i++)
|
|
{
|
|
s_sd_remote_commands[i].m_request.m_ee_dma_id = 0;
|
|
}
|
|
|
|
sceSifAddCmdHandler(SOUND_RESULT_COMMAND, s_result_callback, NULL );
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CSpuManager::sClean()
|
|
{
|
|
for (int core = 0; core < NUM_CORES; core++)
|
|
{
|
|
s_spu_reg_kon[core] = 0;
|
|
s_spu_reg_koff[core] = 0;
|
|
s_sd_voice_status[core] = 0;
|
|
}
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
#ifdef __NOPT_ASSERT__
|
|
void CSpuManager::sPrintStatus()
|
|
{
|
|
for (int core = 0; core < NUM_CORES; core++)
|
|
{
|
|
Dbg_Message("Core %d: Voices Used (%x) Cleared (%x) Looped (%x) Voice Status (%x)", core, VoicesUsed[core], VoicesCleared[core],
|
|
LoopingSounds[core], sGetVoiceStatus(core));
|
|
Dbg_Message(" Saved KON value (%x) Saved KOFF value (%x)", s_spu_reg_kon[core], s_spu_reg_koff[core]);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CSpuManager::sWaitForIO()
|
|
{
|
|
while (s_sd_remote_busy)
|
|
;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CSpuManager::sUpdateStatus()
|
|
{
|
|
//scePrintf("******** Doing CSpuManager::sUpdateStatus() on frame %d\n", Tmr::GetRenderFrame( ));
|
|
for (int core = 0; core < NUM_CORES; core++)
|
|
{
|
|
sSendSdRemote( rSdGetSwitch, core | SD_S_ENDX, 0 );
|
|
|
|
// Clear out switches
|
|
//s_spu_reg_kon[core] = 0;
|
|
//s_spu_reg_koff[core] = 0;
|
|
}
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
uint32 CSpuManager::sGetVoiceStatus(int core)
|
|
{
|
|
// For now, just go into a wait loop. In the future, we can probably use the old value
|
|
while (s_sd_voice_status_update_frame[core] != Tmr::GetRenderFrame( ))
|
|
;
|
|
|
|
if (s_sd_voice_status_update_frame[core] != Tmr::GetRenderFrame( ))
|
|
{
|
|
Dbg_MsgAssert(0, ("Sound voice status not current: need frame %d but have frame %d", Tmr::GetRenderFrame( ), s_sd_voice_status_update_frame[core]));
|
|
}
|
|
|
|
return s_sd_voice_status[core];
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
bool CSpuManager::sSendSdRemote(uint16 function, uint16 entry, uint32 value)
|
|
{
|
|
return s_queue_new_command(function, entry, value);
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
bool CSpuManager::sVoiceTrans(uint16 channel, uint16 mode, void *p_iop_addr, uint32 *p_spu_addr, uint32 size)
|
|
{
|
|
Dbg_Assert(!s_sd_transferring_voice);
|
|
|
|
#if 0
|
|
sceSdRemote(1, rSdVoiceTrans, channel, mode, p_iop_addr, p_spu_addr, size);
|
|
sceSdRemote(1, rSdVoiceTransStatus, channel, SD_TRANS_STATUS_WAIT);
|
|
#else
|
|
s_sd_transferring_voice = true;
|
|
|
|
// Send transfer command first
|
|
if (!s_queue_new_command(rSdVoiceTrans, channel, mode, (uint32) p_iop_addr, (uint32) p_spu_addr, size))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!s_queue_new_command(rSdVoiceTransStatus, channel, SD_TRANS_STATUS_WAIT))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
s_wait_for_voice_transfer();
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
bool CSpuManager::s_queue_new_command(uint16 function, uint16 entry, uint32 value, uint32 arg0, uint32 arg1, uint32 arg2)
|
|
{
|
|
static int s_num_sound_requests_sent = 0;
|
|
|
|
if (function == rSdSetSwitch)
|
|
{
|
|
#if 0
|
|
// If we are sending a switch, combine with old settings to make sure we don't overwrite them.
|
|
// Switches are only updated every 1/48000 a second on the SPU.
|
|
value = s_get_switch_value(entry, value);
|
|
#endif
|
|
s_update_local_status(entry, value);
|
|
}
|
|
|
|
int nextFreeIndex = (s_sd_remote_command_free_index + 1) % MAX_QUEUED_COMMANDS;
|
|
SSifCmdSoundRequestPacket *p_command = &s_sd_remote_commands[s_sd_remote_command_free_index];
|
|
|
|
// Wait for old send to complete
|
|
if (p_command->m_request.m_ee_dma_id)
|
|
{
|
|
//Dbg_Assert(0);
|
|
int wait_num = 0;
|
|
while(sceSifDmaStat(p_command->m_request.m_ee_dma_id) >= 0)
|
|
{
|
|
if ((wait_num++ % 1000) == 0)
|
|
scePrintf("Waiting for sound request buffer\n");
|
|
}
|
|
}
|
|
|
|
if (function == rSdSetEffectAttr)
|
|
{
|
|
sceSdEffectAttr *p_attr = (sceSdEffectAttr *) value;
|
|
|
|
p_command->m_request.m_function = function;
|
|
p_command->m_request.m_entry = entry;
|
|
p_command->m_request.m_value = p_attr->mode;
|
|
p_command->m_request.m_arg_0 = ((uint32) p_attr->depth_L << 16) | (uint32) p_attr->depth_R;
|
|
p_command->m_request.m_arg_1 = p_attr->delay;
|
|
p_command->m_request.m_arg_2 = p_attr->feedback;
|
|
}
|
|
else
|
|
{
|
|
p_command->m_request.m_function = function;
|
|
p_command->m_request.m_entry = entry;
|
|
p_command->m_request.m_value = value;
|
|
p_command->m_request.m_arg_0 = arg0;
|
|
p_command->m_request.m_arg_1 = arg1;
|
|
p_command->m_request.m_arg_2 = arg2;
|
|
}
|
|
p_command->m_request.m_pad[0] = ++s_num_sound_requests_sent;
|
|
|
|
// Send the actual command
|
|
//scePrintf("CSpuManager: sending command %x with entry %x and value %x\n", function, entry, value);
|
|
p_command->m_request.m_ee_dma_id = sceSifSendCmd(SOUND_REQUEST_COMMAND, p_command, sizeof(*p_command), 0, 0, 0);
|
|
|
|
s_sd_remote_command_free_index = nextFreeIndex;
|
|
|
|
return true;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
uint32 CSpuManager::s_get_switch_value(uint16 entry, uint32 value)
|
|
{
|
|
int core = entry & 0x1;
|
|
int switch_reg = entry & 0xFF00;
|
|
|
|
uint32 new_value = value;
|
|
|
|
switch (switch_reg)
|
|
{
|
|
case SD_S_KON:
|
|
if(s_spu_reg_koff[core] & value)
|
|
{
|
|
//Dbg_MsgAssert(0, ("Turning on a voice that was turned off this frame."));
|
|
Dbg_Message("Turning on a voice that was turned off this frame.");
|
|
s_spu_reg_koff[core] &= ~(s_spu_reg_koff[core] & value); // turn off those bits
|
|
}
|
|
|
|
s_spu_reg_kon[core] |= value;
|
|
new_value = s_spu_reg_kon[core];
|
|
|
|
break;
|
|
|
|
case SD_S_KOFF:
|
|
if(s_spu_reg_kon[core] & value)
|
|
{
|
|
//Dbg_MsgAssert(0, ("Turning off a voice that was turned on this frame."));
|
|
Dbg_Message("Turning off a voice that was turned on this frame.");
|
|
s_spu_reg_kon[core] &= ~(s_spu_reg_kon[core] & value); // turn off those bits
|
|
}
|
|
|
|
s_spu_reg_koff[core] |= value;
|
|
new_value = s_spu_reg_koff[core];
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return new_value;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CSpuManager::s_update_local_status(uint16 entry, uint32 value)
|
|
{
|
|
int core = entry & 0x1;
|
|
int switch_reg = entry & 0xFF00;
|
|
|
|
switch (switch_reg)
|
|
{
|
|
case SD_S_KON:
|
|
//Dbg_Message("************ ORing KON%d with %x", core, value);
|
|
if(s_spu_reg_koff[core] & value)
|
|
{
|
|
#ifdef __NOPT_ASSERT__
|
|
sPrintStatus();
|
|
#endif
|
|
Dbg_Message("Core %d: Set KON value (%x)", core, value);
|
|
Dbg_MsgAssert(0, ("Turning on a voice that was turned off this frame."));
|
|
Dbg_Message("Turning on a voice that was turned off this frame.");
|
|
s_spu_reg_koff[core] &= ~(s_spu_reg_koff[core] & value); // turn off those bits
|
|
}
|
|
|
|
s_spu_reg_kon[core] |= value;
|
|
|
|
// Make sure the voice is not used again before SPU update is done
|
|
s_sd_voice_status[core] &= ~value;
|
|
break;
|
|
|
|
case SD_S_KOFF:
|
|
//Dbg_Message("************ ORing KOFF%d with %x", core, value);
|
|
if(!(value & ~PS2Sfx_GetVoiceStatus(core)))
|
|
{
|
|
#ifdef __NOPT_ASSERT__
|
|
sPrintStatus();
|
|
#endif
|
|
Dbg_MsgAssert(0, ("Can't set KOFF with %x: status %x raw status %x", value, sGetVoiceStatus(core), s_sd_voice_status[core]));
|
|
Dbg_Message("Can't set KOFF with %x: status %x raw status %x", value, sGetVoiceStatus(core), s_sd_voice_status[core]);
|
|
}
|
|
if(s_spu_reg_kon[core] & value)
|
|
{
|
|
#ifdef __NOPT_ASSERT__
|
|
//sPrintStatus();
|
|
#endif
|
|
//Dbg_Message("Core %d: Set KOFF value (%x)", core, value);
|
|
//Dbg_MsgAssert(0, ("Turning off a voice that was turned on this frame."));
|
|
Dbg_Message("Turning off a voice that was turned on this frame.");
|
|
s_spu_reg_kon[core] &= ~(s_spu_reg_kon[core] & value); // turn off those bits
|
|
}
|
|
|
|
s_spu_reg_koff[core] |= value;
|
|
|
|
// Lets not clear the voice now. Wait until we get the SPU status break;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CSpuManager::s_result_callback(void *p, void *q)
|
|
{
|
|
SSifCmdSoundResultPacket *p_result = (SSifCmdSoundResultPacket *) p;
|
|
|
|
if (p_result->m_function == rSdVoiceTransStatus)
|
|
{
|
|
s_sd_transferring_voice = false;
|
|
}
|
|
else if (p_result->m_function == rSdGetSwitch)
|
|
{
|
|
int core = p_result->m_entry & 0x1;
|
|
|
|
//scePrintf("************ CSpuManager: received result from command %x with entry %x and result %x for core %d\n", p_result->m_function, p_result->m_entry, p_result->m_result, core);
|
|
|
|
if ((p_result->m_entry & 0xFF00) == SD_S_ENDX)
|
|
{
|
|
s_sd_voice_status[core] = p_result->m_result;
|
|
s_sd_voice_status_update_frame[core] = Tmr::GetRenderFrame( );
|
|
|
|
// We can clear this out now since the status is up-to-date
|
|
VoicesUsed[ core ] &= ~VoicesCleared[ core ];
|
|
//if (LoopingSounds[ core ] & VoicesCleared[ core ])
|
|
// {
|
|
// scePrintf("Clearing looping sound voices %x on core %d\n", LoopingSounds[ core ] & VoicesCleared[ core ], core);
|
|
// }
|
|
LoopingSounds[ core ] &= ~VoicesCleared[ core ];
|
|
VoicesCleared[ core ] = 0;
|
|
|
|
// Keep bits that haven't been updated yet
|
|
s_spu_reg_kon[core] &= (s_sd_voice_status[core] | ~VoicesUsed[core]);
|
|
s_spu_reg_koff[core] &= ~(s_sd_voice_status[core] | ~VoicesUsed[core]);
|
|
|
|
// And remove any leftover KON voices
|
|
s_sd_voice_status[core] &= ~s_spu_reg_kon[core];
|
|
}
|
|
}
|
|
|
|
if (p_result->m_panic)
|
|
{
|
|
Dbg_MsgAssert(0, ("Error on IOP module while processing SPU request %x", p_result->m_function));
|
|
}
|
|
|
|
ExitHandler();
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CSpuManager::s_wait_for_voice_transfer()
|
|
{
|
|
while (s_sd_transferring_voice)
|
|
;
|
|
}
|
|
|
|
} // namespace Sfx
|