mirror of
https://github.com/thug1src/thug.git
synced 2024-12-02 21:07:12 +00:00
962 lines
29 KiB
C++
962 lines
29 KiB
C++
/*****************************************************************************
|
|
** **
|
|
** Neversoft Entertainment **
|
|
** **
|
|
** Copyright (C) 2001 - All Rights Reserved **
|
|
** **
|
|
******************************************************************************
|
|
** **
|
|
** Project: GEL Library **
|
|
** **
|
|
** Module: Sound effects (Sfx) **
|
|
** **
|
|
** File name: ngc/p_sfx.cpp **
|
|
** **
|
|
** Created by: 08/21/01 - dc **
|
|
** **
|
|
** Description: NGC Sound effects **
|
|
** **
|
|
*****************************************************************************/
|
|
|
|
|
|
/*****************************************************************************
|
|
** Includes **
|
|
*****************************************************************************/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <core/macros.h>
|
|
#include <core/defines.h>
|
|
#include <sys/file/filesys.h>
|
|
#include <sys/ngc/p_dma.h>
|
|
#include <gel/soundfx/ngc/p_sfx.h>
|
|
#include <gel/music/ngc/pcm/pcm.h>
|
|
#include <gel/music/ngc/p_music.h>
|
|
#include <gel/music/music.h>
|
|
#include <dolphin/mix.h>
|
|
#include <dolphin\dtk.h>
|
|
#include "gfx\ngc\nx\nx_init.h"
|
|
|
|
|
|
/*****************************************************************************
|
|
** DBG Information **
|
|
*****************************************************************************/
|
|
|
|
namespace Sfx
|
|
{
|
|
|
|
/*****************************************************************************
|
|
** Externals **
|
|
*****************************************************************************/
|
|
|
|
/*****************************************************************************
|
|
** Defines **
|
|
*****************************************************************************/
|
|
|
|
#define GAMECUBE_START_DSP_DATA_ADDR ( NxNgc::EngineGlobals.aram_dsp )
|
|
#define GAMECUBE_END_DSP_DATA_ADDR ( NxNgc::EngineGlobals.aram_dsp + aram_dsp_size )
|
|
|
|
#define MAX_VOLUME 0x7FFF // Max Gamecube volume.
|
|
#define MAX_CHANNEL_VOLUME 0x3FFF // Half of the maximum, so sounds played at 100% will be half max volume.
|
|
#define MAX_REVERB_DEPTH 0x3FFF
|
|
#define MAX_PITCH 0xFFFFFL
|
|
#define UNALTERED_PITCH 0x10000L
|
|
|
|
/*****************************************************************************
|
|
** Private Types **
|
|
*****************************************************************************/
|
|
|
|
struct sDSPADPCM
|
|
{
|
|
// for WAV header generation during decode
|
|
u32 num_samples; // total number of RAW samples
|
|
u32 num_adpcm_nibbles; // number of ADPCM nibbles (including frame headers)
|
|
u32 sample_rate; // Sample rate, in Hz
|
|
|
|
// DSP addressing and decode context
|
|
u16 loop_flag; // 1=LOOPED, 0=NOT LOOPED
|
|
u16 format; // Always 0x0000, for ADPCM
|
|
u32 sa; // Start offset address for looped samples (zero for non-looped)
|
|
u32 ea; // End offset address for looped samples
|
|
u32 ca; // always zero
|
|
u16 coef[16]; // decode coefficients (eight pairs of 16-bit words)
|
|
|
|
// DSP decoder initial state
|
|
u16 gain; // always zero for ADPCM
|
|
u16 ps; // predictor/scale
|
|
u16 yn1; // sample history
|
|
u16 yn2; // sample history
|
|
|
|
// DSP decoder loop context
|
|
u16 lps; // predictor/scale for loop context
|
|
u16 lyn1; // sample history (n-1) for loop context
|
|
u16 lyn2; // sample history (n-2) for loop context
|
|
u16 pad[10]; // reserved
|
|
};
|
|
|
|
|
|
|
|
struct VoiceEntry
|
|
{
|
|
AXVPB* p_axvpb; // Pointer returned by AXAcquireVoice().
|
|
struct sDSPADPCM* p_dspadpcm; // Pointer to sample header data in memory.
|
|
};
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
** Private Data **
|
|
*****************************************************************************/
|
|
|
|
// Because we don't use voices in the same way as on PS2, this array simulates
|
|
// the system. An entry with a NULL pointer means no sound is playing on that
|
|
// 'voice', a valid pointer indicates a sound is playing on that 'voice', and
|
|
// provides details of the playback.
|
|
static VoiceEntry VoiceSimulator[NUM_VOICES];
|
|
|
|
// Global sfx multiplier, percentage.
|
|
static float gSfxVolume = 100.0f;
|
|
|
|
// allocated SPU RAM
|
|
int SpuRAMUsedTemp = 0;
|
|
int SpuRAMUsedPerm = 0;
|
|
|
|
// For maintaining pointers to the temporary sound headers, so they can be wiped
|
|
// when cleanup between levels is performed.
|
|
static void* tempSoundHeaders[200];
|
|
static int numTempSoundHeaders = 0;
|
|
|
|
/*****************************************************************************
|
|
** Public Data **
|
|
*****************************************************************************/
|
|
|
|
bool isStereo = true;
|
|
|
|
/*****************************************************************************
|
|
** Private Prototypes **
|
|
*****************************************************************************/
|
|
|
|
/*****************************************************************************
|
|
** Private Functions **
|
|
*****************************************************************************/
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
void AXUserCBack( void )
|
|
{
|
|
for( int i = 0; i < NUM_VOICES; ++i )
|
|
{
|
|
if( VoiceSimulator[i].p_axvpb != NULL )
|
|
{
|
|
if( VoiceSimulator[i].p_axvpb->pb.state == AX_PB_STATE_STOP )
|
|
{
|
|
// Playback on this voice has ended, so free up the voice.
|
|
AXFreeVoice( VoiceSimulator[i].p_axvpb );
|
|
VoiceSimulator[i].p_axvpb = NULL;
|
|
// OSReport( "voice %d stopped %d\n", i, OSGetTick() );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
static void ax_voice_reacquisition_callback( void* p )
|
|
{
|
|
OSReport( "Voice was reacquired\n" );
|
|
|
|
for( int i = 0; i < NUM_VOICES; ++i )
|
|
{
|
|
if( VoiceSimulator[i].p_axvpb != NULL )
|
|
{
|
|
if( (void*)( VoiceSimulator[i].p_axvpb ) == p )
|
|
{
|
|
OSReport( "Voice was voice %d\n", i );
|
|
VoiceSimulator[i].p_axvpb = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
static int getFreeVoice( void )
|
|
{
|
|
for( int i = 0; i < NUM_VOICES; ++i )
|
|
{
|
|
if( VoiceSimulator[i].p_axvpb == NULL )
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
static bool loadGamecubeDSP( PlatformWaveInfo *pInfo, const char *filename, bool loadPerm, float *samplePitch )
|
|
{
|
|
int vagAddr = 0; // VAG address in SPU2 RAM, returned to calling function
|
|
bool rv = false;
|
|
|
|
void* p_file = File::Open( filename, "rb" );
|
|
if( p_file )
|
|
{
|
|
int size = File::GetFileSize( p_file );
|
|
if ( size )
|
|
{
|
|
int align_size = ( size + 31 ) & ~31;
|
|
int data_size = align_size - 96;
|
|
|
|
// Grab enough memory to ensure a multiple of 32 bytes data length. To avoid fragmentation, we grab
|
|
// memory for the header copy first.
|
|
char* p_header_copy = (char*)Mem::Malloc( 96 );
|
|
// File::Read( p_header_copy, 1, 96, p_file );
|
|
Mem::Manager::sHandle().BottomUpHeap()->PushAlign( 32 );
|
|
char* p_data = (char*)Mem::Malloc( align_size );
|
|
Mem::Manager::sHandle().BottomUpHeap()->PopAlign();
|
|
// #ifdef __NOPT_ASSERT__
|
|
int bytes_read = File::Read( p_data, 1, size, p_file );
|
|
// #else
|
|
// File::Read( p_data, 1, size, p_file );
|
|
DCFlushRange ( p_data, size );
|
|
// #endif // __NOPT_ASSERT__
|
|
// Dbg_Assert( bytes_read == size )
|
|
if ( bytes_read != size )
|
|
{
|
|
File::Close( p_file );
|
|
delete p_data;
|
|
return false;
|
|
}
|
|
// file.close();
|
|
|
|
// Copy the header to the reserved area.
|
|
memcpy( p_header_copy, p_data, 96 );
|
|
|
|
// Obtain information from the file header.
|
|
sDSPADPCM* p_header = (sDSPADPCM*)p_header_copy;
|
|
// OSReport( "num samples: %d\n", p_header->num_samples );
|
|
// OSReport( "sample rate: %d\n", p_header->sample_rate );
|
|
// OSReport( "loop flag: %d\n", p_header->loop_flag );
|
|
|
|
if( loadPerm )
|
|
{
|
|
// Stack the permanent sounds up from the bottom.
|
|
SpuRAMUsedPerm += data_size;
|
|
if(( GAMECUBE_END_DSP_DATA_ADDR - SpuRAMUsedPerm ) & 0x0F )
|
|
{
|
|
#ifdef __NOPT_ASSERT__
|
|
int oldSpuRAM = SpuRAMUsedPerm;
|
|
#endif // __NOPT_ASSERT__
|
|
SpuRAMUsedPerm += (( END_WAVE_DATA_ADDR - SpuRAMUsedPerm ) & 0x0F );
|
|
Dbg_MsgAssert( SpuRAMUsedPerm > oldSpuRAM, ( "Duh asshole." ));
|
|
}
|
|
vagAddr = GAMECUBE_END_DSP_DATA_ADDR - SpuRAMUsedPerm;
|
|
Dbg_MsgAssert( !( vagAddr & 0x0F ), ( "Fire Matt please." ));
|
|
}
|
|
else
|
|
{
|
|
// If a temporary sound, add the details of the header to the cleanup array.
|
|
tempSoundHeaders[numTempSoundHeaders++] = p_header_copy;
|
|
|
|
if(( SpuRAMUsedTemp + GAMECUBE_START_DSP_DATA_ADDR ) & 0x0F )
|
|
{
|
|
SpuRAMUsedTemp += 0x10 - ( ( SpuRAMUsedTemp + BASE_WAVE_DATA_ADDR ) & 0x0F );
|
|
}
|
|
vagAddr = GAMECUBE_START_DSP_DATA_ADDR + SpuRAMUsedTemp;
|
|
SpuRAMUsedTemp += data_size;
|
|
}
|
|
|
|
// Start DMA transfer buffer to sound RAM...
|
|
Dbg_Assert(((unsigned int)p_data & 31 ) == 0 );
|
|
Dbg_Assert(((unsigned int)vagAddr & 31 ) == 0 );
|
|
|
|
// Read data in chunks & DMA.
|
|
// int toread = size - 96;
|
|
// int offset = 0;
|
|
// while ( toread )
|
|
// {
|
|
//#define BUFFER_SIZE 16384
|
|
// char readBuffer[BUFFER_SIZE];
|
|
// if ( toread > BUFFER_SIZE )
|
|
// {
|
|
// // Read a full buffer.
|
|
// File::Read( readBuffer, 1, BUFFER_SIZE, p_file );
|
|
// toread -= BUFFER_SIZE;
|
|
// offset += BUFFER_SIZE;
|
|
// NsDMA::toARAM( vagAddr + offset, readBuffer, BUFFER_SIZE );
|
|
// }
|
|
// else
|
|
// {
|
|
// // Last chunk
|
|
// File::Read( readBuffer, 1, toread, p_file );
|
|
// NsDMA::toARAM( vagAddr + offset, readBuffer, ( toread + 31 ) & ~31 );
|
|
// toread = 0;
|
|
// }
|
|
// }
|
|
//
|
|
|
|
NsDMA::toARAM( vagAddr, p_data + 96, data_size );
|
|
|
|
pInfo->headerAddress = p_header_copy;
|
|
pInfo->vagAddress = vagAddr;
|
|
pInfo->looping = ( p_header->loop_flag > 0 );
|
|
|
|
// Figure pitch adjustment coefficient.
|
|
if( samplePitch )
|
|
{
|
|
*samplePitch = ( (float)p_header->sample_rate * 100.0f ) / 32000.0f;
|
|
}
|
|
|
|
// Release main RAM version of data.
|
|
Mem::Free( p_data );
|
|
|
|
rv = true;
|
|
}
|
|
File::Close( p_file );
|
|
}
|
|
else
|
|
{
|
|
// OSReport( 0, "************ failed to load sound: %s\n", filename );
|
|
// while ( 1== 1 );
|
|
//Dbg_MsgAssert( 0, ( "failed to load sound: %s\n", filename ));
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
** Public Functions **
|
|
*****************************************************************************/
|
|
|
|
void GetStreamBufferInfo( int* stream0_base, int* stream1_base, int* stream2_base,
|
|
int* stream0_size, int* stream1_size, int* stream2_size )
|
|
{
|
|
*stream0_base = NxNgc::EngineGlobals.aram_stream0;
|
|
*stream0_size = aram_stream0_size;
|
|
*stream1_base = NxNgc::EngineGlobals.aram_stream1;
|
|
*stream1_size = aram_stream1_size;
|
|
*stream2_base = NxNgc::EngineGlobals.aram_stream2;
|
|
*stream2_size = aram_stream2_size;
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
void PerFrameUpdate( void )
|
|
{
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
void InitSoundFX( CSfxManager *p_manager )
|
|
{
|
|
// Initialize the 'voice' array.
|
|
for( int i = 0; i < NUM_VOICES; ++i )
|
|
{
|
|
VoiceSimulator[i].p_axvpb = NULL;
|
|
}
|
|
|
|
// See whether the unit is in mono or stereo mode.
|
|
isStereo = !( OSGetSoundMode() == OS_SOUND_MODE_MONO );
|
|
|
|
// Initialize Audio Interface API.
|
|
// AIInit( NULL );
|
|
|
|
// Initialize the AX library, and boot the DSP.
|
|
// AXInit();
|
|
|
|
// Set the callback responsible for freeing up voices.
|
|
AXRegisterCallback( AXUserCBack );
|
|
|
|
// Zero first 1024 bytes of ARAM - this is where one-shot sounds will end up when they finish.
|
|
# if aram_zero_size != 1024
|
|
# error aram zero buffer size has changed
|
|
# endif
|
|
|
|
char reset_buffer[1024];
|
|
memset( reset_buffer, 0, 1024 );
|
|
DCFlushRange( reset_buffer, 1024 );
|
|
NsDMA::toARAM( NxNgc::EngineGlobals.aram_zero, reset_buffer, 1024 );
|
|
|
|
CleanUpSoundFX();
|
|
|
|
// Moved this from sioman.cpp since the pcm stuff for ngc needs the sfx stuff initialised first.
|
|
Pcm::Init();
|
|
}
|
|
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
void ReInitSoundFX( void )
|
|
{
|
|
// Initialize the 'voice' array.
|
|
for( int i = 0; i < NUM_VOICES; ++i )
|
|
{
|
|
VoiceSimulator[i].p_axvpb = NULL;
|
|
}
|
|
|
|
// Flush pending ARQ transfers and wait for DMA to finish.
|
|
ARQFlushQueue();
|
|
while( ARGetDMAStatus() != 0 );
|
|
|
|
AXQuit();
|
|
AIReset();
|
|
ARQReset();
|
|
ARReset();
|
|
|
|
// Initialize Audio Interface API.
|
|
AIInit( NULL );
|
|
|
|
// Initialize the AX library, and boot the DSP.
|
|
AXInit();
|
|
|
|
// Set the callback responsible for freeing up voices.
|
|
AXRegisterCallback( AXUserCBack );
|
|
|
|
#ifndef DVDETH
|
|
DTKInit();
|
|
DTKSetRepeatMode( DTK_MODE_NOREPEAT );
|
|
#endif // DVDETH
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
bool VoiceIsOn( int whichVoice )
|
|
{
|
|
if( VoiceSimulator[whichVoice].p_axvpb != NULL )
|
|
{
|
|
// Check it hasn't already been stopped since then it will be dealt with by the 5ms callback.
|
|
// if( VoiceSimulator[whichVoice].p_axvpb->pb.state != AX_PB_STATE_STOP )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
//void SetVoiceParameters( int voiceNumber, float volL, float volR, float fPitch )
|
|
void SetVoiceParameters( int whichVoice, sVolume *p_vol, float pitch )
|
|
{
|
|
unsigned int rpitch = UNALTERED_PITCH;
|
|
if( pitch != 0.0f )
|
|
{
|
|
rpitch = (unsigned int)PERCENT( UNALTERED_PITCH, pitch );
|
|
if ( rpitch > MAX_PITCH )
|
|
rpitch = MAX_PITCH;
|
|
if ( rpitch <= 0 )
|
|
rpitch = 1;
|
|
}
|
|
|
|
int old = OSDisableInterrupts();
|
|
if( VoiceSimulator[whichVoice].p_axvpb )
|
|
{
|
|
AXPB* p_pb = &( VoiceSimulator[whichVoice].p_axvpb->pb );
|
|
// Pcm::setDolby( p_pb, VoiceSimulator[voiceNumber].p_axvpb, volL, volR, pitch, true, gSfxVolume );
|
|
//Pcm::setDolby( p_pb, VoiceSimulator[whichVoice].p_axvpb, p_vol->GetChannelVolume( 0 ), p_vol->GetChannelVolume( 1 ), rpitch, true, gSfxVolume );
|
|
if ( p_vol )
|
|
{
|
|
Pcm::setDolby( p_pb, VoiceSimulator[whichVoice].p_axvpb, p_vol->GetChannelVolume( 0 ), p_vol->GetChannelVolume( 1 ), rpitch, true, gSfxVolume );
|
|
}
|
|
else
|
|
{
|
|
Pcm::setDolby( p_pb, VoiceSimulator[whichVoice].p_axvpb, 0.0f, 0.0f, rpitch, true, gSfxVolume );
|
|
}
|
|
}
|
|
OSRestoreInterrupts( old );
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
bool LoadSoundPlease( const char *sfxName, uint32 checksum, PlatformWaveInfo *pInfo, bool loadPerm )
|
|
{
|
|
Dbg_Assert( pInfo );
|
|
|
|
// Need to append file type. Copy string into a buffer.
|
|
static char name_buffer[256] = "sounds\\dsp\\";
|
|
const int SOUND_PREPEND_START_POS = 11;
|
|
|
|
// Check there is room to add the extension.
|
|
int length = strlen( sfxName );
|
|
Dbg_Assert( strlen( sfxName ) <= ( 256 - ( SOUND_PREPEND_START_POS + 4 )));
|
|
strcpy( name_buffer + SOUND_PREPEND_START_POS, sfxName );
|
|
strcpy( name_buffer + SOUND_PREPEND_START_POS + length, ".dsp" );
|
|
|
|
// OSReport( "Loading sound: %s\n", name_buffer );
|
|
|
|
loadGamecubeDSP( pInfo, name_buffer, loadPerm, &pInfo->pitchAdjustmentForSamplingRate );
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
//int PlaySoundPlease( PlatformWaveInfo *pInfo, float volL, float volR, float pitch )
|
|
int PlaySoundPlease( PlatformWaveInfo *pInfo, sVolume *p_vol, float pitch )
|
|
{
|
|
//Dbg_Assert( pInfo->headerAddress != NULL );
|
|
|
|
// Spt::SingletonPtr< Sfx::CSfxManager > sfx_manager;
|
|
// if ( sfx_manager->GetMainVolume() < 1.0f ) return -1;
|
|
|
|
int voice = getFreeVoice();
|
|
if( voice == -1 )
|
|
{
|
|
// No voice in our array available.
|
|
return -1;
|
|
}
|
|
|
|
// Attempt to grab a voice. Set the priority high if it is a looping sound.
|
|
sDSPADPCM* p_header = (sDSPADPCM*)pInfo->headerAddress;
|
|
// HACK!!!
|
|
if( !p_header )
|
|
{
|
|
// No voice available.
|
|
return -1;
|
|
}
|
|
AXVPB* p_axvpb = AXAcquireVoice( ( p_header->loop_flag ) ? AX_PRIORITY_NODROP : AX_PRIORITY_LOWEST, ax_voice_reacquisition_callback, 0 );
|
|
if( p_axvpb == NULL )
|
|
{
|
|
// No voice available.
|
|
return -1;
|
|
}
|
|
|
|
AXPBADDR addr;
|
|
AXPBSRC src;
|
|
AXPBADPCM adpcm;
|
|
AXPBADPCMLOOP loop;
|
|
|
|
addr.loopFlag = p_header->loop_flag ? AXPBADDR_LOOP_ON : AXPBADDR_LOOP_OFF;
|
|
addr.format = AX_PB_FORMAT_ADPCM;
|
|
|
|
// Convert from byte to nibble address, and split into low and high words.
|
|
u32 start_address = ( pInfo->vagAddress * 2 ) + 2;
|
|
u32 end_address = ( pInfo->vagAddress * 2 ) + ( p_header->num_adpcm_nibbles - 1 );
|
|
|
|
addr.currentAddressHi = (u16)( start_address >> 16 );
|
|
addr.currentAddressLo = (u16)( start_address & 0xFFFFU );
|
|
addr.endAddressHi = (u16)( end_address >> 16 );
|
|
addr.endAddressLo = (u16)( end_address & 0xFFFFU );
|
|
|
|
if( p_header->loop_flag )
|
|
{
|
|
addr.loopAddressHi = (u16)( start_address >> 16 );
|
|
addr.loopAddressLo = (u16)( start_address & 0xFFFFU );
|
|
}
|
|
else
|
|
{
|
|
u32 zero_buffer_address = NxNgc::EngineGlobals.aram_zero * 2;
|
|
addr.loopAddressHi = (u16)( zero_buffer_address >> 16 );
|
|
addr.loopAddressLo = (u16)( zero_buffer_address & 0xFFFFU );
|
|
}
|
|
|
|
loop.loop_pred_scale = p_header->lps;
|
|
loop.loop_yn1 = p_header->lyn1;
|
|
loop.loop_yn2 = p_header->lyn2;
|
|
|
|
// uint32 srcBits = (uint32)(0x00010000 * ((f32)p_header->sample_rate / AX_IN_SAMPLES_PER_SEC));
|
|
// src.ratioHi = (u16)(srcBits >> 16);
|
|
// src.ratioLo = (u16)(srcBits & 0xFFFF);
|
|
src.ratioHi = 1;
|
|
src.ratioLo = 0;
|
|
src.currentAddressFrac = 0;
|
|
src.last_samples[0] = 0;
|
|
src.last_samples[1] = 0;
|
|
src.last_samples[2] = 0;
|
|
src.last_samples[3] = 0;
|
|
|
|
memcpy( &adpcm.a[0][0], p_header->coef, sizeof( u16 ) * 16 );
|
|
adpcm.gain = p_header->gain;
|
|
adpcm.pred_scale = p_header->ps;
|
|
adpcm.yn1 = p_header->yn1;
|
|
adpcm.yn2 = p_header->yn2;
|
|
|
|
AXPBVE ve;
|
|
ve.currentVolume = 0x7FFF; // This is max volume.
|
|
ve.currentDelta = 0x0000;
|
|
|
|
AXSetVoiceType( p_axvpb, AX_PB_TYPE_NORMAL );
|
|
|
|
AXSetVoiceAddr( p_axvpb, &addr ); // input addressing
|
|
AXSetVoiceAdpcm( p_axvpb, &adpcm ); // ADPCM coefficients
|
|
AXSetVoiceAdpcmLoop( p_axvpb, &loop ); // ADPCM loop context
|
|
|
|
AXSetVoiceVe( p_axvpb, &ve );
|
|
|
|
AXSetVoiceSrcType( p_axvpb, AX_SRC_TYPE_LINEAR ); // SRC type
|
|
AXSetVoiceSrc( p_axvpb, &src ); // initial SRC settings
|
|
|
|
// Set the voice to run.
|
|
AXSetVoiceState( p_axvpb, AX_PB_STATE_RUN );
|
|
|
|
// Fill in voice details.
|
|
VoiceSimulator[voice].p_axvpb = p_axvpb;
|
|
VoiceSimulator[voice].p_dspadpcm = p_header;
|
|
// OSReport( "voice %d started %d\n", voice, OSGetTick() );
|
|
|
|
// Adjust pitch to account for lower sampling rates:
|
|
pitch = PERCENT( pitch, pInfo->pitchAdjustmentForSamplingRate );
|
|
|
|
// This takes the place of AxSetVoiceMix().
|
|
SetVoiceParameters( voice, p_vol, pitch );
|
|
|
|
return voice;
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
void StopSoundPlease( int whichVoice )
|
|
{
|
|
if( VoiceIsOn( whichVoice ))
|
|
{
|
|
AXSetVoiceState( VoiceSimulator[whichVoice].p_axvpb, AX_PB_STATE_STOP );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
void StopAllSoundFX( void )
|
|
{
|
|
// Pcm::StopMusic();
|
|
// Pcm::StopStreams();
|
|
|
|
for( int i = 0; i < NUM_VOICES; i++ )
|
|
{
|
|
StopSoundPlease( i );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
void SetVolumePlease( float volumeLevel )
|
|
{
|
|
// Sets master volume level.
|
|
gSfxVolume = volumeLevel;
|
|
|
|
// Pcm::PCMAudio_SetStreamGlobalVolume( ( unsigned int ) volumeLevel );
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
void PauseSoundsPlease( void )
|
|
{
|
|
// sVolume silent_vol;
|
|
// silent_vol.SetSilent();
|
|
|
|
// Just turn the volume down on all playing voices.
|
|
for( int i = 0; i < NUM_VOICES; i++ )
|
|
{
|
|
if( VoiceIsOn( i ))
|
|
{
|
|
// SetVoiceParameters( i, 0.0f, 0.0f );
|
|
// SetVoiceParameters( i, &silent_vol );
|
|
SetVoiceParameters( i, NULL );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
void CleanUpSoundFX( void )
|
|
{
|
|
StopAllSoundFX();
|
|
SpuRAMUsedTemp = 0;
|
|
SetReverbPlease( 0 );
|
|
|
|
for( int i = 0; i < numTempSoundHeaders; ++i )
|
|
{
|
|
Dbg_Assert( tempSoundHeaders[i] );
|
|
Mem::Free( tempSoundHeaders[i] );
|
|
tempSoundHeaders[i] = NULL;
|
|
}
|
|
numTempSoundHeaders = 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Macros and constants:
|
|
|
|
int ReverbModes[ ] =
|
|
{
|
|
// put the default first in this list, since the default param to SetReverb() is 0
|
|
0,
|
|
// SD_REV_MODE_HALL,
|
|
// SD_REV_MODE_ROOM,
|
|
// SD_REV_MODE_STUDIO_A,
|
|
// SD_REV_MODE_STUDIO_B,
|
|
// SD_REV_MODE_STUDIO_C,
|
|
// SD_REV_MODE_SPACE,
|
|
// SD_REV_MODE_ECHO,
|
|
// SD_REV_MODE_DELAY,
|
|
// SD_REV_MODE_PIPE,
|
|
};
|
|
|
|
#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
|
|
|
|
// convert sample rate to pitch setting
|
|
#define FREQ2PITCH( x ) ( ( float )( ( ( x ) * 100 ) / 48000 ) )
|
|
|
|
#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 LoopingSounds[ NUM_CORES ];
|
|
// gotta keep track of voices used, to be able
|
|
// to tell whether a voice is free or not...
|
|
//int VoicesUsed[ NUM_CORES ];
|
|
|
|
// avoid checking voice status more than once per frame:
|
|
//uint64 gLastVoiceAvailableUpdate[ NUM_CORES ];
|
|
//int gAvailableVoices[ NUM_CORES ];
|
|
|
|
|
|
#define BLOCKHEADER_LOOP_BIT ( 1 << 9 )
|
|
|
|
// Internal function prototypes:
|
|
|
|
static bool loadGamecubeDSP( PlatformWaveInfo *pInfo, const char *filename, bool loadPerm, float *samplePitch = NULL );
|
|
|
|
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: void SetReverb(void)
|
|
// Description: Sets reverb level and type
|
|
// Parameters: none
|
|
//
|
|
// Returns:
|
|
//-------------------------------------------------------------------------------------------
|
|
void SetReverbPlease( float reverbLevel, int reverbMode, bool instant = false )
|
|
{
|
|
// int i;
|
|
// sceSdEffectAttr r_attr;
|
|
//
|
|
// Dbg_MsgAssert( reverbType >= 0,( "Bad reverb mode." ));
|
|
// Dbg_MsgAssert( reverbType < NUM_REVERB_MODES,( "Bad reverb mode..." ));
|
|
//
|
|
// if ( !reverbLevel )
|
|
// {
|
|
// // --- 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
|
|
// {
|
|
// // just turning off reverb on all voices...
|
|
// if(sceSdRemote( 1, rSdSetSwitch, i | SD_S_VMIXEL , 0 ) == -1 )
|
|
// Dbg_Message("Error setting reverb4\n");
|
|
// if(sceSdRemote( 1, rSdSetSwitch, i | SD_S_VMIXER , 0 ) == -1 )
|
|
// Dbg_Message("Error setting reverb5\n");
|
|
// if( sceSdRemote( 1, rSdSetEffectAttr, i, &r_attr ) == -1 )
|
|
// Dbg_Message( "Error setting reverb\n" );
|
|
// }
|
|
// }
|
|
// else
|
|
// {
|
|
// int reverbDepth = ( 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( sceSdRemote( 1, rSdSetEffectAttr, i, &r_attr ) == -1 )
|
|
// Dbg_Message( "Error setting reverb\n" );
|
|
//
|
|
// if(sceSdRemote( 1, rSdSetCoreAttr, i | SD_C_EFFECT_ENABLE, 1 ) == -1 )
|
|
// Dbg_Message("Error setting reverb\n");
|
|
// if(sceSdRemote( 1, rSdSetParam, i | SD_P_EVOLL , reverbDepth ) == -1 )
|
|
// Dbg_Message("Error setting reverb2\n");
|
|
// if(sceSdRemote( 1, rSdSetParam, i | SD_P_EVOLR , reverbDepth ) == -1 )
|
|
// Dbg_Message("Error setting reverb3\n");;
|
|
// if(sceSdRemote( 1, rSdSetSwitch, i | SD_S_VMIXEL , VOICES_MINUS_STREAMS ) == -1 )
|
|
// Dbg_Message("Error setting reverb4\n");
|
|
// if(sceSdRemote( 1, rSdSetSwitch, i | SD_S_VMIXER , VOICES_MINUS_STREAMS ) == -1 )
|
|
// Dbg_Message("Error setting reverb5\n");
|
|
//
|
|
// }
|
|
// }
|
|
} // end of SetReverb( )
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace Sfx
|