//-------------------------------------------------------------------------------------------- // Playstation 2 sound effect functionality... // Taken from samples on Sony website, modified by mjd. //-------------------------------------------------------------------------------------------- #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 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