2026-03-01 12:16:08 +08:00
# include " stdafx.h "
# include "SoundEngine.h"
# include "..\Consoles_App.h"
# include "..\..\MultiplayerLocalPlayer.h"
# include "..\..\..\Minecraft.World\net.minecraft.world.level.h"
# include "..\..\Minecraft.World\leveldata.h"
# include "..\..\Minecraft.World\mth.h"
# include "..\..\TexturePackRepository.h"
# include "..\..\DLCTexturePack.h"
# include "Common\DLC\DLCAudioFile.h"
# ifdef __PSVITA__
# include <audioout.h>
# endif
# ifdef _WINDOWS64
# include "..\..\Minecraft.Client\Windows64\Windows64_App.h"
# include "..\..\Minecraft.Client\Windows64\Miles\include\imssapi.h"
# endif
# ifdef __ORBIS__
# include <audioout.h>
//#define __DISABLE_MILES__ // MGH disabled for now as it crashes if we call sceNpMatching2Initialize
2026-03-02 17:37:16 +07:00
# endif
2026-03-01 12:16:08 +08:00
// take out Orbis until they are done
2026-03-02 17:37:16 +07:00
# if defined _XBOX
2026-03-01 12:16:08 +08:00
SoundEngine : : SoundEngine ( ) { }
void SoundEngine : : init ( Options * pOptions )
{
}
2026-03-02 17:37:16 +07:00
void SoundEngine : : tick ( shared_ptr < Mob > * players , float a )
2026-03-01 12:16:08 +08:00
{
}
void SoundEngine : : destroy ( ) { }
void SoundEngine : : play ( int iSound , float x , float y , float z , float volume , float pitch )
{
app . DebugPrintf ( " PlaySound - %d \n " , iSound ) ;
}
void SoundEngine : : playStreaming ( const wstring & name , float x , float y , float z , float volume , float pitch , bool bMusicDelay ) { }
void SoundEngine : : playUI ( int iSound , float volume , float pitch ) { }
void SoundEngine : : updateMusicVolume ( float fVal ) { }
void SoundEngine : : updateSoundEffectVolume ( float fVal ) { }
void SoundEngine : : add ( const wstring & name , File * file ) { }
void SoundEngine : : addMusic ( const wstring & name , File * file ) { }
void SoundEngine : : addStreaming ( const wstring & name , File * file ) { }
char * SoundEngine : : ConvertSoundPathToName ( const wstring & name , bool bConvertSpaces ) { return NULL ; }
bool SoundEngine : : isStreamingWavebankReady ( ) { return true ; }
void SoundEngine : : playMusicTick ( ) { } ;
# else
# ifdef _WINDOWS64
2026-03-01 16:52:27 -07:00
char SoundEngine : : m_szSoundPath [ ] = { " Windows64 \\ Sound \\ " } ;
2026-03-01 12:16:08 +08:00
char SoundEngine : : m_szMusicPath [ ] = { " music \\ " } ;
char SoundEngine : : m_szRedistName [ ] = { " redist64 " } ;
# elif defined _DURANGO
char SoundEngine : : m_szSoundPath [ ] = { " Sound \\ " } ;
char SoundEngine : : m_szMusicPath [ ] = { " music \\ " } ;
char SoundEngine : : m_szRedistName [ ] = { " redist64 " } ;
# elif defined __ORBIS__
# ifdef _CONTENT_PACKAGE
char SoundEngine : : m_szSoundPath [ ] = { " Sound/ " } ;
# elif defined _ART_BUILD
char SoundEngine : : m_szSoundPath [ ] = { " Sound/ " } ;
# else
// just use the host Durango folder for the sound. In the content package, we'll have moved this in the .gp4 file
char SoundEngine : : m_szSoundPath [ ] = { " Durango/Sound/ " } ;
# endif
char SoundEngine : : m_szMusicPath [ ] = { " music/ " } ;
char SoundEngine : : m_szRedistName [ ] = { " redist64 " } ;
# elif defined __PSVITA__
char SoundEngine : : m_szSoundPath [ ] = { " PSVita/Sound/ " } ;
char SoundEngine : : m_szMusicPath [ ] = { " music/ " } ;
char SoundEngine : : m_szRedistName [ ] = { " redist " } ;
# elif defined __PS3__
//extern const char* getPS3HomePath();
char SoundEngine : : m_szSoundPath [ ] = { " PS3/Sound/ " } ;
char SoundEngine : : m_szMusicPath [ ] = { " music/ " } ;
char SoundEngine : : m_szRedistName [ ] = { " redist " } ;
# define USE_SPURS
# ifdef USE_SPURS
# include <cell/spurs.h>
# else
# include <sys/spu_image.h>
# endif
# endif
2026-03-02 17:37:16 +07:00
F32 AILCALLBACK custom_falloff_function ( HSAMPLE S ,
2026-03-01 12:16:08 +08:00
F32 distance ,
F32 rolloff_factor ,
F32 min_dist ,
F32 max_dist ) ;
char * SoundEngine : : m_szStreamFileA [ eStream_Max ] =
{
" calm1 " ,
" calm2 " ,
" calm3 " ,
" hal1 " ,
" hal2 " ,
" hal3 " ,
" hal4 " ,
" nuance1 " ,
" nuance2 " ,
# ifndef _XBOX
// add the new music tracks
" creative1 " ,
" creative2 " ,
" creative3 " ,
" creative4 " ,
" creative5 " ,
" creative6 " ,
" menu1 " ,
" menu2 " ,
" menu3 " ,
" menu4 " ,
2026-03-02 17:37:16 +07:00
# endif
2026-03-01 12:16:08 +08:00
" piano1 " ,
" piano2 " ,
" piano3 " ,
// Nether
" nether1 " ,
" nether2 " ,
" nether3 " ,
" nether4 " ,
// The End
" the_end_dragon_alive " ,
" the_end_end " ,
// CDs
" 11 " ,
" 13 " ,
" blocks " ,
" cat " ,
" chirp " ,
" far " ,
" mall " ,
" mellohi " ,
" stal " ,
" strad " ,
" ward " ,
" where_are_we_now "
} ;
/////////////////////////////////////////////
//
// ErrorCallback
//
/////////////////////////////////////////////
void AILCALL ErrorCallback ( S64 i_Id , char const * i_Details )
{
char * pchLastError = AIL_last_error ( ) ;
if ( pchLastError [ 0 ] ! = 0 )
{
app . DebugPrintf ( " \r ErrorCallback Error Category: %s \n " , pchLastError ) ;
}
if ( i_Details )
{
app . DebugPrintf ( " ErrorCallback - Details: %s \n " , i_Details ) ;
}
}
# ifdef __PSVITA__
// AP - this is the callback when the driver is about to mix. At this point the mutex is locked by Miles so we can now call all Miles functions without
// the possibility of incurring a stall.
static bool SoundEngine_Change = false ; // has tick been called?
static CRITICAL_SECTION SoundEngine_MixerMutex ;
void AILCALL MilesMixerCB ( HDIGDRIVER dig )
{
// has the tick function been called since the last callback
if ( SoundEngine_Change )
{
SoundEngine_Change = false ;
EnterCriticalSection ( & SoundEngine_MixerMutex ) ;
Minecraft * pMinecraft = Minecraft : : GetInstance ( ) ;
pMinecraft - > soundEngine - > updateMiles ( ) ;
pMinecraft - > soundEngine - > playMusicUpdate ( ) ;
LeaveCriticalSection ( & SoundEngine_MixerMutex ) ;
}
}
# endif
/////////////////////////////////////////////
//
// init
//
/////////////////////////////////////////////
void SoundEngine : : init ( Options * pOptions )
{
app . DebugPrintf ( " ---SoundEngine::init \n " ) ;
# ifdef __DISABLE_MILES__
return ;
# endif
# ifdef __ORBIS__
C4JThread : : PushAffinityAllCores ( ) ;
2026-03-02 17:37:16 +07:00
# endif
2026-03-01 12:16:08 +08:00
# if defined _DURANGO || defined __ORBIS__ || defined __PS3__ || defined __PSVITA__
Register_RIB ( BinkADec ) ;
# endif
char * redistpath ;
# if (defined _WINDOWS64 || defined __PSVITA__) // || defined _DURANGO || defined __ORBIS__ )
redistpath = AIL_set_redist_directory ( m_szRedistName ) ;
# endif
app . DebugPrintf ( " ---SoundEngine::init - AIL_startup \n " ) ;
S32 ret = AIL_startup ( ) ;
int iNumberOfChannels = initAudioHardware ( 8 ) ;
// Create a driver to render our audio - 44khz, 16 bit,
2026-03-02 17:37:16 +07:00
# ifdef __PS3__
// On the Sony PS3, the driver is always opened in 48 kHz, 32-bit floating point. The only meaningful configurations are MSS_MC_STEREO, MSS_MC_51_DISCRETE, and MSS_MC_71_DISCRETE.
2026-03-01 12:16:08 +08:00
m_hDriver = AIL_open_digital_driver ( 48000 , 16 , iNumberOfChannels , AIL_OPEN_DIGITAL_USE_SPU0 ) ;
# elif defined __PSVITA__
// maximum of 16 samples
AIL_set_preference ( DIG_MIXER_CHANNELS , 16 ) ;
m_hDriver = AIL_open_digital_driver ( 48000 , 16 , MSS_MC_STEREO , 0 ) ;
2026-03-02 17:37:16 +07:00
// AP - For some reason the submit thread defaults to a priority of zero (invalid). Make sure it has the highest priority to avoid audio breakup.
2026-03-01 12:16:08 +08:00
SceUID threadID ;
AIL_platform_property ( m_hDriver , PSP2_SUBMIT_THREAD , & threadID , 0 , 0 ) ;
S32 g_DefaultCPU = sceKernelGetThreadCpuAffinityMask ( threadID ) ;
S32 Old = sceKernelChangeThreadPriority ( threadID , 64 ) ;
// AP - register a callback when the mixer starts
AILMIXERCB temp = AIL_register_mix_callback ( m_hDriver , MilesMixerCB ) ;
InitializeCriticalSection ( & SoundEngine_MixerMutex ) ;
# elif defined(__ORBIS__)
m_hDriver = AIL_open_digital_driver ( 48000 , 16 , 2 , 0 ) ;
app . DebugPrintf ( " ---SoundEngine::init - AIL_open_digital_driver \n " ) ;
# else
m_hDriver = AIL_open_digital_driver ( 44100 , 16 , MSS_MC_USE_SYSTEM_CONFIG , 0 ) ;
# endif
if ( m_hDriver = = 0 )
{
app . DebugPrintf ( " Couldn't open digital sound driver. (%s) \n " , AIL_last_error ( ) ) ;
AIL_shutdown ( ) ;
# ifdef __ORBIS__
C4JThread : : PopAffinity ( ) ;
2026-03-02 17:37:16 +07:00
# endif
2026-03-01 12:16:08 +08:00
return ;
}
app . DebugPrintf ( " ---SoundEngine::init - driver opened \n " ) ;
# ifdef __PSVITA__
// set high falloff power for maximum spatial effect in software mode
AIL_set_speaker_configuration ( m_hDriver , 0 , 0 , 4.0F ) ;
# endif
AIL_set_event_error_callback ( ErrorCallback ) ;
AIL_set_3D_rolloff_factor ( m_hDriver , 1.0 ) ;
// Create an event system tied to that driver - let Miles choose memory defaults.
//if (AIL_startup_event_system(m_hDriver, 0, 0, 0) == 0)
// 4J-PB - Durango complains that the default memory (64k)isn't enough
// Error: MilesEvent: Out of event system memory (pool passed to event system startup exhausted).
// AP - increased command buffer from the default 5K to 20K for Vita
if ( AIL_startup_event_system ( m_hDriver , 1024 * 20 , 0 , 1024 * 128 ) = = 0 )
{
app . DebugPrintf ( " Couldn't init event system (%s). \n " , AIL_last_error ( ) ) ;
AIL_close_digital_driver ( m_hDriver ) ;
AIL_shutdown ( ) ;
# ifdef __ORBIS__
C4JThread : : PopAffinity ( ) ;
2026-03-02 17:37:16 +07:00
# endif
2026-03-01 12:16:08 +08:00
app . DebugPrintf ( " ---SoundEngine::init - AIL_startup_event_system failed \n " ) ;
return ;
}
char szBankName [ 255 ] ;
2026-03-02 17:37:16 +07:00
# if defined __PS3__
2026-03-01 12:16:08 +08:00
if ( app . GetBootedFromDiscPatch ( ) )
{
char szTempSoundFilename [ 255 ] ;
sprintf ( szTempSoundFilename , " %s%s " , m_szSoundPath , " Minecraft.msscmp " ) ;
app . DebugPrintf ( " SoundEngine::playMusicUpdate - (booted from disc patch) looking for %s \n " , szTempSoundFilename ) ;
sprintf ( szBankName , " %s/%s " , app . GetBDUsrDirPath ( szTempSoundFilename ) , m_szSoundPath ) ;
app . DebugPrintf ( " SoundEngine::playMusicUpdate - (booted from disc patch) music path - %s \n " , szBankName ) ;
}
else
{
sprintf ( szBankName , " %s/%s " , getUsrDirPath ( ) , m_szSoundPath ) ;
}
2026-03-02 17:37:16 +07:00
2026-03-01 12:16:08 +08:00
# elif defined __PSVITA__
sprintf ( szBankName , " %s/%s " , getUsrDirPath ( ) , m_szSoundPath ) ;
# elif defined __ORBIS__
sprintf ( szBankName , " %s/%s " , getUsrDirPath ( ) , m_szSoundPath ) ;
# else
strcpy ( ( char * ) szBankName , m_szSoundPath ) ;
# endif
strcat ( ( char * ) szBankName , " Minecraft.msscmp " ) ;
m_hBank = AIL_add_soundbank ( szBankName , 0 ) ;
if ( m_hBank = = NULL )
{
char * Error = AIL_last_error ( ) ;
app . DebugPrintf ( " Couldn't open soundbank: %s (%s) \n " , szBankName , Error ) ;
AIL_close_digital_driver ( m_hDriver ) ;
AIL_shutdown ( ) ;
# ifdef __ORBIS__
C4JThread : : PopAffinity ( ) ;
2026-03-02 17:37:16 +07:00
# endif
2026-03-01 12:16:08 +08:00
return ;
}
//#ifdef _DEBUG
HMSSENUM token = MSS_FIRST ;
char const * Events [ 1 ] = { 0 } ;
S32 EventCount = 0 ;
while ( AIL_enumerate_events ( m_hBank , & token , 0 , & Events [ 0 ] ) )
{
app . DebugPrintf ( 4 , " %d - %s \n " , EventCount , Events [ 0 ] ) ;
EventCount + + ;
}
//#endif
U64 u64Result ;
u64Result = AIL_enqueue_event_by_name ( " Minecraft/CacheSounds " ) ;
m_MasterMusicVolume = 1.0f ;
m_MasterEffectsVolume = 1.0f ;
//AIL_set_variable_float(0,"UserEffectVol",1);
m_bSystemMusicPlaying = false ;
m_openStreamThread = NULL ;
# ifdef __ORBIS__
C4JThread : : PopAffinity ( ) ;
# endif
# ifdef __PSVITA__
2026-03-02 17:37:16 +07:00
// AP - By default the mixer won't start up and nothing will process. Kick off a blank sample to force the mixer to start up.
2026-03-01 12:16:08 +08:00
HSAMPLE Sample = AIL_allocate_sample_handle ( m_hDriver ) ;
AIL_init_sample ( Sample , DIG_F_STEREO_16 ) ;
static U64 silence = 0 ;
AIL_set_sample_address ( Sample , & silence , sizeof ( U64 ) ) ;
AIL_start_sample ( Sample ) ;
// wait for 1 mix...
AIL_release_sample_handle ( Sample ) ;
# endif
}
# ifdef __ORBIS__
// void SoundEngine::SetHandle(int32_t hAudio)
// {
// //m_hAudio=hAudio;
// }
# endif
void SoundEngine : : SetStreamingSounds ( int iOverworldMin , int iOverWorldMax , int iNetherMin , int iNetherMax , int iEndMin , int iEndMax , int iCD1 )
{
m_iStream_Overworld_Min = iOverworldMin ;
m_iStream_Overworld_Max = iOverWorldMax ;
m_iStream_Nether_Min = iNetherMin ;
m_iStream_Nether_Max = iNetherMax ;
m_iStream_End_Min = iEndMin ;
m_iStream_End_Max = iEndMax ;
m_iStream_CD_1 = iCD1 ;
// array to monitor recently played tracks
if ( m_bHeardTrackA )
{
delete [ ] m_bHeardTrackA ;
}
m_bHeardTrackA = new bool [ iEndMax + 1 ] ;
memset ( m_bHeardTrackA , 0 , sizeof ( bool ) * iEndMax + 1 ) ;
}
// AP - moved to a separate function so it can be called from the mixer callback on Vita
void SoundEngine : : updateMiles ( )
{
2026-03-02 17:37:16 +07:00
# ifdef __PSVITA__
2026-03-01 12:16:08 +08:00
//CD - We must check for Background Music [BGM] at any point
//If it's playing disable our audio, otherwise enable
int NoBGMPlaying = sceAudioOutGetAdopt ( SCE_AUDIO_OUT_PORT_TYPE_BGM ) ;
updateSystemMusicPlaying ( ! NoBGMPlaying ) ;
# elif defined __ORBIS__
// is the system playing background music?
SceAudioOutPortState outPortState ;
sceAudioOutGetPortState ( m_hBGMAudio , & outPortState ) ;
updateSystemMusicPlaying ( outPortState . output = = SCE_AUDIO_OUT_STATE_OUTPUT_UNKNOWN ) ;
# endif
if ( m_validListenerCount = = 1 )
{
for ( int i = 0 ; i < MAX_LOCAL_PLAYERS ; i + + )
{
// set the listener as the first player we find
if ( m_ListenerA [ i ] . bValid )
{
AIL_set_listener_3D_position ( m_hDriver , m_ListenerA [ i ] . vPosition . x , m_ListenerA [ i ] . vPosition . y , - m_ListenerA [ i ] . vPosition . z ) ; // Flipped sign of z as Miles is expecting left handed coord system
AIL_set_listener_3D_orientation ( m_hDriver , - m_ListenerA [ i ] . vOrientFront . x , m_ListenerA [ i ] . vOrientFront . y , m_ListenerA [ i ] . vOrientFront . z , 0 , 1 , 0 ) ; // Flipped sign of z as Miles is expecting left handed coord system
break ;
}
}
}
else
{
// 4J-PB - special case for splitscreen
// the shortest distance between any listener and a sound will be used to play a sound a set distance away down the z axis.
// The listener position will be set to 0,0,0, and the orientation will be facing down the z axis
AIL_set_listener_3D_position ( m_hDriver , 0 , 0 , 0 ) ;
AIL_set_listener_3D_orientation ( m_hDriver , 0 , 0 , 1 , 0 , 1 , 0 ) ;
}
AIL_begin_event_queue_processing ( ) ;
// Iterate over the sounds
S32 StartedCount = 0 , CompletedCount = 0 , TotalCount = 0 ;
HMSSENUM token = MSS_FIRST ;
MILESEVENTSOUNDINFO SoundInfo ;
int Playing = 0 ;
while ( AIL_enumerate_sound_instances ( 0 , & token , 0 , 0 , 0 , & SoundInfo ) )
{
AUDIO_INFO * game_data = ( AUDIO_INFO * ) ( SoundInfo . UserBuffer ) ;
if ( SoundInfo . Status = = MILESEVENT_SOUND_STATUS_PLAYING )
{
Playing + = 1 ;
}
if ( SoundInfo . Status ! = MILESEVENT_SOUND_STATUS_COMPLETE )
{
// apply the master volume
// watch for the 'special' volume levels
bool isThunder = false ;
if ( game_data - > volume = = 10000.0f )
{
isThunder = true ;
}
2026-03-02 17:37:16 +07:00
if ( game_data - > volume > 1 )
2026-03-01 12:16:08 +08:00
{
game_data - > volume = 1 ;
}
AIL_set_sample_volume_levels ( SoundInfo . Sample , game_data - > volume * m_MasterEffectsVolume , game_data - > volume * m_MasterEffectsVolume ) ;
float distanceScaler = 16.0f ;
switch ( SoundInfo . Status )
{
case MILESEVENT_SOUND_STATUS_PENDING :
// 4J-PB - causes the falloff to be calculated on the PPU instead of the SPU, and seems to resolve our distorted sound issue
AIL_register_falloff_function_callback ( SoundInfo . Sample , & custom_falloff_function ) ;
if ( game_data - > bIs3D )
2026-03-02 17:37:16 +07:00
{
2026-03-01 12:16:08 +08:00
AIL_set_sample_is_3D ( SoundInfo . Sample , 1 ) ;
int iSound = game_data - > iSound - eSFX_MAX ;
switch ( iSound )
{
// Is this the Dragon?
case eSoundType_MOB_ENDERDRAGON_GROWL :
case eSoundType_MOB_ENDERDRAGON_MOVE :
case eSoundType_MOB_ENDERDRAGON_END :
case eSoundType_MOB_ENDERDRAGON_HIT :
distanceScaler = 100.0f ;
break ;
2026-03-03 03:04:10 +08:00
case eSoundType_FIREWORKS_BLAST :
case eSoundType_FIREWORKS_BLAST_FAR :
case eSoundType_FIREWORKS_LARGE_BLAST :
case eSoundType_FIREWORKS_LARGE_BLAST_FAR :
distanceScaler = 100.0f ;
break ;
2026-03-01 12:16:08 +08:00
case eSoundType_MOB_GHAST_MOAN :
case eSoundType_MOB_GHAST_SCREAM :
case eSoundType_MOB_GHAST_DEATH :
case eSoundType_MOB_GHAST_CHARGE :
case eSoundType_MOB_GHAST_FIREBALL :
distanceScaler = 30.0f ;
break ;
}
// Set a special distance scaler for thunder, which we respond to by having no attenutation
if ( isThunder )
{
distanceScaler = 10000.0f ;
}
}
else
{
AIL_set_sample_is_3D ( SoundInfo . Sample , 0 ) ;
}
AIL_set_sample_3D_distances ( SoundInfo . Sample , distanceScaler , 1 , 0 ) ;
// set the pitch
if ( ! game_data - > bUseSoundsPitchVal )
{
AIL_set_sample_playback_rate_factor ( SoundInfo . Sample , game_data - > pitch ) ;
}
if ( game_data - > bIs3D )
2026-03-02 17:37:16 +07:00
{
2026-03-01 12:16:08 +08:00
if ( m_validListenerCount > 1 )
{
float fClosest = 10000.0f ;
int iClosestListener = 0 ;
float fClosestX = 0.0f , fClosestY = 0.0f , fClosestZ = 0.0f , fDist ;
// need to calculate the distance from the sound to the nearest listener - use Manhattan Distance as the decision
for ( int i = 0 ; i < MAX_LOCAL_PLAYERS ; i + + )
{
if ( m_ListenerA [ i ] . bValid )
{
float x , y , z ;
x = fabs ( m_ListenerA [ i ] . vPosition . x - game_data - > x ) ;
y = fabs ( m_ListenerA [ i ] . vPosition . y - game_data - > y ) ;
z = fabs ( m_ListenerA [ i ] . vPosition . z - game_data - > z ) ;
fDist = x + y + z ;
if ( fDist < fClosest )
{
fClosest = fDist ;
fClosestX = x ;
fClosestY = y ;
fClosestZ = z ;
iClosestListener = i ;
}
}
}
// our distances in the world aren't very big, so floats rather than casts to doubles should be fine
fDist = sqrtf ( ( fClosestX * fClosestX ) + ( fClosestY * fClosestY ) + ( fClosestZ * fClosestZ ) ) ;
AIL_set_sample_3D_position ( SoundInfo . Sample , 0 , 0 , fDist ) ;
//app.DebugPrintf("Playing sound %d %f from nearest listener [%d]\n",SoundInfo.EventID,fDist,iClosestListener);
}
else
{
AIL_set_sample_3D_position ( SoundInfo . Sample , game_data - > x , game_data - > y , - game_data - > z ) ; // Flipped sign of z as Miles is expecting left handed coord system
}
}
break ;
default :
if ( game_data - > bIs3D )
2026-03-02 17:37:16 +07:00
{
2026-03-01 12:16:08 +08:00
if ( m_validListenerCount > 1 )
{
float fClosest = 10000.0f ;
int iClosestListener = 0 ;
float fClosestX = 0.0f , fClosestY = 0.0f , fClosestZ = 0.0f , fDist ;
// need to calculate the distance from the sound to the nearest listener - use Manhattan Distance as the decision
for ( int i = 0 ; i < MAX_LOCAL_PLAYERS ; i + + )
{
if ( m_ListenerA [ i ] . bValid )
{
float x , y , z ;
x = fabs ( m_ListenerA [ i ] . vPosition . x - game_data - > x ) ;
y = fabs ( m_ListenerA [ i ] . vPosition . y - game_data - > y ) ;
z = fabs ( m_ListenerA [ i ] . vPosition . z - game_data - > z ) ;
fDist = x + y + z ;
if ( fDist < fClosest )
{
fClosest = fDist ;
fClosestX = x ;
fClosestY = y ;
fClosestZ = z ;
iClosestListener = i ;
}
}
}
// our distances in the world aren't very big, so floats rather than casts to doubles should be fine
fDist = sqrtf ( ( fClosestX * fClosestX ) + ( fClosestY * fClosestY ) + ( fClosestZ * fClosestZ ) ) ;
AIL_set_sample_3D_position ( SoundInfo . Sample , 0 , 0 , fDist ) ;
//app.DebugPrintf("Playing sound %d %f from nearest listener [%d]\n",SoundInfo.EventID,fDist,iClosestListener);
}
else
{
AIL_set_sample_3D_position ( SoundInfo . Sample , game_data - > x , game_data - > y , - game_data - > z ) ; // Flipped sign of z as Miles is expecting left handed coord system
}
2026-03-02 17:37:16 +07:00
}
2026-03-01 12:16:08 +08:00
break ;
2026-03-02 17:37:16 +07:00
}
2026-03-01 12:16:08 +08:00
}
}
AIL_complete_event_queue_processing ( ) ;
}
//#define DISTORTION_TEST
# ifdef DISTORTION_TEST
static float fVal = 0.0f ;
# endif
/////////////////////////////////////////////
//
// tick
//
/////////////////////////////////////////////
# ifdef __PSVITA__
static S32 running = AIL_ms_count ( ) ;
# endif
2026-03-02 17:37:16 +07:00
void SoundEngine : : tick ( shared_ptr < Mob > * players , float a )
2026-03-01 12:16:08 +08:00
{
2026-03-03 03:04:10 +08:00
ConsoleSoundEngine : : tick ( ) ;
2026-03-01 12:16:08 +08:00
# ifdef __DISABLE_MILES__
return ;
# endif
# ifdef __PSVITA__
EnterCriticalSection ( & SoundEngine_MixerMutex ) ;
# endif
// update the listener positions
int listenerCount = 0 ;
# ifdef DISTORTION_TEST
float fX , fY , fZ ;
# endif
if ( players )
{
bool bListenerPostionSet = false ;
for ( int i = 0 ; i < MAX_LOCAL_PLAYERS ; i + + )
{
if ( players [ i ] ! = NULL )
{
m_ListenerA [ i ] . bValid = true ;
F32 x , y , z ;
x = players [ i ] - > xo + ( players [ i ] - > x - players [ i ] - > xo ) * a ;
y = players [ i ] - > yo + ( players [ i ] - > y - players [ i ] - > yo ) * a ;
z = players [ i ] - > zo + ( players [ i ] - > z - players [ i ] - > zo ) * a ;
float yRot = players [ i ] - > yRotO + ( players [ i ] - > yRot - players [ i ] - > yRotO ) * a ;
float yCos = ( float ) cos ( - yRot * Mth : : RAD_TO_GRAD - PI ) ;
float ySin = ( float ) sin ( - yRot * Mth : : RAD_TO_GRAD - PI ) ;
// store the listener positions for splitscreen
m_ListenerA [ i ] . vPosition . x = x ;
m_ListenerA [ i ] . vPosition . y = y ;
2026-03-02 17:37:16 +07:00
m_ListenerA [ i ] . vPosition . z = z ;
2026-03-01 12:16:08 +08:00
m_ListenerA [ i ] . vOrientFront . x = ySin ;
m_ListenerA [ i ] . vOrientFront . y = 0 ;
m_ListenerA [ i ] . vOrientFront . z = yCos ;
listenerCount + + ;
}
else
{
m_ListenerA [ i ] . bValid = false ;
}
}
}
// If there were no valid players set, make up a default listener
if ( listenerCount = = 0 )
{
m_ListenerA [ 0 ] . vPosition . x = 0 ;
m_ListenerA [ 0 ] . vPosition . y = 0 ;
m_ListenerA [ 0 ] . vPosition . z = 0 ;
m_ListenerA [ 0 ] . vOrientFront . x = 0 ;
m_ListenerA [ 0 ] . vOrientFront . y = 0 ;
m_ListenerA [ 0 ] . vOrientFront . z = 1.0f ;
listenerCount + + ;
}
m_validListenerCount = listenerCount ;
# ifdef __PSVITA__
// AP - Show that a change has occurred so we know to update the values at the next Mixer callback
SoundEngine_Change = true ;
LeaveCriticalSection ( & SoundEngine_MixerMutex ) ;
# else
updateMiles ( ) ;
# endif
}
/////////////////////////////////////////////
//
// SoundEngine
//
/////////////////////////////////////////////
SoundEngine : : SoundEngine ( )
{
random = new Random ( ) ;
m_hStream = 0 ;
m_StreamState = eMusicStreamState_Idle ;
m_iMusicDelay = 0 ;
2026-03-02 17:37:16 +07:00
m_validListenerCount = 0 ;
2026-03-01 12:16:08 +08:00
m_bHeardTrackA = NULL ;
// Start the streaming music playing some music from the overworld
SetStreamingSounds ( eStream_Overworld_Calm1 , eStream_Overworld_piano3 ,
eStream_Nether1 , eStream_Nether4 ,
eStream_end_dragon , eStream_end_end ,
eStream_CD_1 ) ;
m_musicID = getMusicID ( LevelData : : DIMENSION_OVERWORLD ) ;
m_StreamingAudioInfo . bIs3D = false ;
m_StreamingAudioInfo . x = 0 ;
m_StreamingAudioInfo . y = 0 ;
m_StreamingAudioInfo . z = 0 ;
m_StreamingAudioInfo . volume = 1 ;
m_StreamingAudioInfo . pitch = 1 ;
memset ( CurrentSoundsPlaying , 0 , sizeof ( int ) * ( eSoundType_MAX + eSFX_MAX ) ) ;
memset ( m_ListenerA , 0 , sizeof ( AUDIO_LISTENER ) * XUSER_MAX_COUNT ) ;
# ifdef __ORBIS__
m_hBGMAudio = GetAudioBGMHandle ( ) ;
# endif
}
void SoundEngine : : destroy ( ) { }
# ifdef _DEBUG
void SoundEngine : : GetSoundName ( char * szSoundName , int iSound )
{
strcpy ( ( char * ) szSoundName , " Minecraft/ " ) ;
wstring name = wchSoundNames [ iSound ] ;
char * SoundName = ( char * ) ConvertSoundPathToName ( name ) ;
strcat ( ( char * ) szSoundName , SoundName ) ;
}
# endif
/////////////////////////////////////////////
//
// play
//
/////////////////////////////////////////////
void SoundEngine : : play ( int iSound , float x , float y , float z , float volume , float pitch )
{
U8 szSoundName [ 256 ] ;
if ( iSound = = - 1 )
{
app . DebugPrintf ( 6 , " PlaySound with sound of -1 !!!!!!!!!!!!!!! \n " ) ;
return ;
}
// AP removed old counting system. Now relying on Miles' Play Count Limit
/* // if we are already playing loads of this sounds ignore this one
2026-03-02 17:37:16 +07:00
if ( CurrentSoundsPlaying [ iSound + eSFX_MAX ] > MAX_SAME_SOUNDS_PLAYING )
2026-03-01 12:16:08 +08:00
{
// wstring name = wchSoundNames[iSound];
// char *SoundName = (char *)ConvertSoundPathToName(name);
// app.DebugPrintf("Too many %s sounds playing!\n",SoundName);
return ;
} */
//if (iSound != eSoundType_MOB_IRONGOLEM_WALK) return;
// build the name
strcpy ( ( char * ) szSoundName , " Minecraft/ " ) ;
# ifdef DISTORTION_TEST
wstring name = wchSoundNames [ eSoundType_MOB_ENDERDRAGON_GROWL ] ;
# else
wstring name = wchSoundNames [ iSound ] ;
# endif
char * SoundName = ( char * ) ConvertSoundPathToName ( name ) ;
strcat ( ( char * ) szSoundName , SoundName ) ;
// app.DebugPrintf(6,"PlaySound - %d - %s - %s (%f %f %f, vol %f, pitch %f)\n",iSound, SoundName, szSoundName,x,y,z,volume,pitch);
AUDIO_INFO AudioInfo ;
AudioInfo . x = x ;
AudioInfo . y = y ;
AudioInfo . z = z ;
AudioInfo . volume = volume ;
AudioInfo . pitch = pitch ;
AudioInfo . bIs3D = true ;
AudioInfo . bUseSoundsPitchVal = false ;
AudioInfo . iSound = iSound + eSFX_MAX ;
# ifdef _DEBUG
strncpy ( AudioInfo . chName , ( char * ) szSoundName , 64 ) ;
# endif
S32 token = AIL_enqueue_event_start ( ) ;
AIL_enqueue_event_buffer ( & token , & AudioInfo , sizeof ( AUDIO_INFO ) , 0 ) ;
AIL_enqueue_event_end_named ( token , ( char * ) szSoundName ) ;
}
/////////////////////////////////////////////
//
// playUI
//
/////////////////////////////////////////////
2026-03-02 17:37:16 +07:00
void SoundEngine : : playUI ( int iSound , float volume , float pitch )
2026-03-01 12:16:08 +08:00
{
U8 szSoundName [ 256 ] ;
wstring name ;
// we have some game sounds played as UI sounds...
// Not the best way to do this, but it seems to only be the portal sounds
if ( iSound > = eSFX_MAX )
{
// AP removed old counting system. Now relying on Miles' Play Count Limit
/* // if we are already playing loads of this sounds ignore this one
if ( CurrentSoundsPlaying [ iSound + eSFX_MAX ] > MAX_SAME_SOUNDS_PLAYING ) return ; */
// build the name
strcpy ( ( char * ) szSoundName , " Minecraft/ " ) ;
name = wchSoundNames [ iSound ] ;
}
else
{
// AP removed old counting system. Now relying on Miles' Play Count Limit
/* // if we are already playing loads of this sounds ignore this one
if ( CurrentSoundsPlaying [ iSound ] > MAX_SAME_SOUNDS_PLAYING ) return ; */
// build the name
strcpy ( ( char * ) szSoundName , " Minecraft/UI/ " ) ;
name = wchUISoundNames [ iSound ] ;
}
char * SoundName = ( char * ) ConvertSoundPathToName ( name ) ;
strcat ( ( char * ) szSoundName , SoundName ) ;
// app.DebugPrintf("UI: Playing %s, volume %f, pitch %f\n",SoundName,volume,pitch);
//app.DebugPrintf("PlaySound - %d - %s\n",iSound, SoundName);
AUDIO_INFO AudioInfo ;
memset ( & AudioInfo , 0 , sizeof ( AUDIO_INFO ) ) ;
AudioInfo . volume = volume ; // will be multiplied by the master volume
AudioInfo . pitch = pitch ;
AudioInfo . bUseSoundsPitchVal = true ;
if ( iSound > = eSFX_MAX )
{
AudioInfo . iSound = iSound + eSFX_MAX ;
}
else
{
AudioInfo . iSound = iSound ;
}
# ifdef _DEBUG
strncpy ( AudioInfo . chName , ( char * ) szSoundName , 64 ) ;
# endif
// 4J-PB - not going to stop UI events happening based on the number of currently playing sounds
S32 token = AIL_enqueue_event_start ( ) ;
AIL_enqueue_event_buffer ( & token , & AudioInfo , sizeof ( AUDIO_INFO ) , 0 ) ;
AIL_enqueue_event_end_named ( token , ( char * ) szSoundName ) ;
}
/////////////////////////////////////////////
//
// playStreaming
//
/////////////////////////////////////////////
void SoundEngine : : playStreaming ( const wstring & name , float x , float y , float z , float volume , float pitch , bool bMusicDelay )
{
// This function doesn't actually play a streaming sound, just sets states and an id for the music tick to play it
// Level audio will be played when a play with an empty name comes in
// CD audio will be played when a named stream comes in
m_StreamingAudioInfo . x = x ;
m_StreamingAudioInfo . y = y ;
m_StreamingAudioInfo . z = z ;
m_StreamingAudioInfo . volume = volume ;
m_StreamingAudioInfo . pitch = pitch ;
if ( m_StreamState = = eMusicStreamState_Playing )
{
m_StreamState = eMusicStreamState_Stop ;
}
else if ( m_StreamState = = eMusicStreamState_Opening )
{
m_StreamState = eMusicStreamState_OpeningCancel ;
}
if ( name . empty ( ) )
{
// music, or stop CD
m_StreamingAudioInfo . bIs3D = false ;
// we need a music id
// random delay of up to 3 minutes for music
m_iMusicDelay = random - > nextInt ( 20 * 60 * 3 ) ; //random->nextInt(20 * 60 * 10) + 20 * 60 * 10;
# ifdef _DEBUG
m_iMusicDelay = 0 ;
# endif
Minecraft * pMinecraft = Minecraft : : GetInstance ( ) ;
bool playerInEnd = false ;
bool playerInNether = false ;
for ( unsigned int i = 0 ; i < MAX_LOCAL_PLAYERS ; i + + )
{
if ( pMinecraft - > localplayers [ i ] ! = NULL )
{
if ( pMinecraft - > localplayers [ i ] - > dimension = = LevelData : : DIMENSION_END )
{
playerInEnd = true ;
}
else if ( pMinecraft - > localplayers [ i ] - > dimension = = LevelData : : DIMENSION_NETHER )
{
playerInNether = true ;
}
}
}
if ( playerInEnd )
{
m_musicID = getMusicID ( LevelData : : DIMENSION_END ) ;
}
else if ( playerInNether )
{
m_musicID = getMusicID ( LevelData : : DIMENSION_NETHER ) ;
}
else
{
m_musicID = getMusicID ( LevelData : : DIMENSION_OVERWORLD ) ;
}
}
else
{
// jukebox
m_StreamingAudioInfo . bIs3D = true ;
m_musicID = getMusicID ( name ) ;
m_iMusicDelay = 0 ;
}
}
int SoundEngine : : GetRandomishTrack ( int iStart , int iEnd )
{
// 4J-PB - make it more likely that we'll get a track we've not heard for a while, although repeating tracks sometimes is fine
// if all tracks have been heard, clear the flags
bool bAllTracksHeard = true ;
int iVal = iStart ;
for ( int i = iStart ; i < = iEnd ; i + + )
{
2026-03-02 17:37:16 +07:00
if ( m_bHeardTrackA [ i ] = = false )
2026-03-01 12:16:08 +08:00
{
bAllTracksHeard = false ;
app . DebugPrintf ( " Not heard all tracks yet \n " ) ;
break ;
}
}
if ( bAllTracksHeard )
{
app . DebugPrintf ( " Heard all tracks - resetting the tracking array \n " ) ;
for ( int i = iStart ; i < = iEnd ; i + + )
{
m_bHeardTrackA [ i ] = false ;
}
}
2026-03-02 17:37:16 +07:00
// trying to get a track we haven't heard, but not too hard
2026-03-01 12:16:08 +08:00
for ( int i = 0 ; i < = ( ( iEnd - iStart ) / 2 ) ; i + + )
{
// random->nextInt(1) will always return 0
iVal = random - > nextInt ( ( iEnd - iStart ) + 1 ) + iStart ;
if ( m_bHeardTrackA [ iVal ] = = false )
{
// not heard this
app . DebugPrintf ( " (%d) Not heard track %d yet, so playing it now \n " , i , iVal ) ;
m_bHeardTrackA [ iVal ] = true ;
break ;
}
else
{
app . DebugPrintf ( " (%d) Skipping track %d already heard it recently \n " , i , iVal ) ;
}
}
app . DebugPrintf ( " Select track %d \n " , iVal ) ;
return iVal ;
}
/////////////////////////////////////////////
//
// getMusicID
//
/////////////////////////////////////////////
int SoundEngine : : getMusicID ( int iDomain )
{
int iRandomVal = 0 ;
Minecraft * pMinecraft = Minecraft : : GetInstance ( ) ;
// Before the game has started?
if ( pMinecraft = = NULL )
{
// any track from the overworld
return GetRandomishTrack ( m_iStream_Overworld_Min , m_iStream_Overworld_Max ) ;
}
if ( pMinecraft - > skins - > isUsingDefaultSkin ( ) )
{
switch ( iDomain )
{
case LevelData : : DIMENSION_END :
// the end isn't random - it has different music depending on whether the dragon is alive or not, but we've not added the dead dragon music yet
return m_iStream_End_Min ;
case LevelData : : DIMENSION_NETHER :
return GetRandomishTrack ( m_iStream_Nether_Min , m_iStream_Nether_Max ) ;
//return m_iStream_Nether_Min + random->nextInt(m_iStream_Nether_Max-m_iStream_Nether_Min);
default : //overworld
//return m_iStream_Overworld_Min + random->nextInt(m_iStream_Overworld_Max-m_iStream_Overworld_Min);
return GetRandomishTrack ( m_iStream_Overworld_Min , m_iStream_Overworld_Max ) ;
}
}
else
{
// using a texture pack - may have multiple End music tracks
switch ( iDomain )
{
case LevelData : : DIMENSION_END :
return GetRandomishTrack ( m_iStream_End_Min , m_iStream_End_Max ) ;
case LevelData : : DIMENSION_NETHER :
//return m_iStream_Nether_Min + random->nextInt(m_iStream_Nether_Max-m_iStream_Nether_Min);
return GetRandomishTrack ( m_iStream_Nether_Min , m_iStream_Nether_Max ) ;
default : //overworld
//return m_iStream_Overworld_Min + random->nextInt(m_iStream_Overworld_Max-m_iStream_Overworld_Min);
return GetRandomishTrack ( m_iStream_Overworld_Min , m_iStream_Overworld_Max ) ;
}
}
}
/////////////////////////////////////////////
//
// getMusicID
//
/////////////////////////////////////////////
// check what the CD is
int SoundEngine : : getMusicID ( const wstring & name )
{
int iCD = 0 ;
char * SoundName = ( char * ) ConvertSoundPathToName ( name , true ) ;
// 4J-PB - these will always be the game cds, so use the m_szStreamFileA for this
for ( int i = 0 ; i < 12 ; i + + )
{
if ( strcmp ( SoundName , m_szStreamFileA [ i + eStream_CD_1 ] ) = = 0 )
{
iCD = i ;
break ;
}
}
// adjust for cd start position on normal or mash-up pack
return iCD + m_iStream_CD_1 ;
}
/////////////////////////////////////////////
//
// getMasterMusicVolume
//
/////////////////////////////////////////////
float SoundEngine : : getMasterMusicVolume ( )
{
if ( m_bSystemMusicPlaying )
{
return 0.0f ;
}
else
{
return m_MasterMusicVolume ;
}
}
/////////////////////////////////////////////
//
// updateMusicVolume
//
/////////////////////////////////////////////
void SoundEngine : : updateMusicVolume ( float fVal )
{
m_MasterMusicVolume = fVal ;
}
/////////////////////////////////////////////
//
// updateSystemMusicPlaying
//
/////////////////////////////////////////////
void SoundEngine : : updateSystemMusicPlaying ( bool isPlaying )
{
m_bSystemMusicPlaying = isPlaying ;
}
/////////////////////////////////////////////
//
// updateSoundEffectVolume
//
/////////////////////////////////////////////
2026-03-02 17:37:16 +07:00
void SoundEngine : : updateSoundEffectVolume ( float fVal )
2026-03-01 12:16:08 +08:00
{
m_MasterEffectsVolume = fVal ;
//AIL_set_variable_float(0,"UserEffectVol",fVal);
}
void SoundEngine : : add ( const wstring & name , File * file ) { }
void SoundEngine : : addMusic ( const wstring & name , File * file ) { }
void SoundEngine : : addStreaming ( const wstring & name , File * file ) { }
bool SoundEngine : : isStreamingWavebankReady ( ) { return true ; }
int SoundEngine : : OpenStreamThreadProc ( void * lpParameter )
{
# ifdef __DISABLE_MILES__
return 0 ;
# endif
SoundEngine * soundEngine = ( SoundEngine * ) lpParameter ;
soundEngine - > m_hStream = AIL_open_stream ( soundEngine - > m_hDriver , soundEngine - > m_szStreamName , 0 ) ;
2026-03-03 03:04:10 +08:00
if ( soundEngine - > m_hStream = = 0 )
{
app . DebugPrintf ( " SoundEngine::OpenStreamThreadProc - Could not open - %s \n " , soundEngine - > m_szStreamName ) ;
}
2026-03-01 12:16:08 +08:00
return 0 ;
}
/////////////////////////////////////////////
//
// playMusicTick
//
/////////////////////////////////////////////
2026-03-02 17:37:16 +07:00
void SoundEngine : : playMusicTick ( )
2026-03-01 12:16:08 +08:00
{
// AP - vita will update the music during the mixer callback
# ifndef __PSVITA__
playMusicUpdate ( ) ;
# endif
}
// AP - moved to a separate function so it can be called from the mixer callback on Vita
2026-03-02 17:37:16 +07:00
void SoundEngine : : playMusicUpdate ( )
2026-03-01 12:16:08 +08:00
{
//return;
static bool firstCall = true ;
static float fMusicVol = 0.0f ;
if ( firstCall )
{
fMusicVol = getMasterMusicVolume ( ) ;
firstCall = false ;
}
switch ( m_StreamState )
{
case eMusicStreamState_Idle :
// start a stream playing
if ( m_iMusicDelay > 0 )
{
m_iMusicDelay - - ;
return ;
}
if ( m_musicID ! = - 1 )
{
// start playing it
# if ( defined __PS3__ || defined __PSVITA__ || defined __ORBIS__ )
# ifdef __PS3__
// 4J-PB - Need to check if we are a patched BD build
if ( app . GetBootedFromDiscPatch ( ) )
{
2026-03-02 17:37:16 +07:00
sprintf ( m_szStreamName , " %s/%s " , app . GetBDUsrDirPath ( m_szMusicPath ) , m_szMusicPath ) ;
2026-03-01 12:16:08 +08:00
app . DebugPrintf ( " SoundEngine::playMusicUpdate - (booted from disc patch) music path - %s " , m_szStreamName ) ;
}
else
{
sprintf ( m_szStreamName , " %s/%s " , getUsrDirPath ( ) , m_szMusicPath ) ;
}
# else
sprintf ( m_szStreamName , " %s/%s " , getUsrDirPath ( ) , m_szMusicPath ) ;
# endif
# else
strcpy ( ( char * ) m_szStreamName , m_szMusicPath ) ;
# endif
// are we using a mash-up pack?
//if(pMinecraft && !pMinecraft->skins->isUsingDefaultSkin() && pMinecraft->skins->getSelected()->hasAudio())
if ( Minecraft : : GetInstance ( ) - > skins - > getSelected ( ) - > hasAudio ( ) )
{
// It's a mash-up - need to use the DLC path for the music
TexturePack * pTexPack = Minecraft : : GetInstance ( ) - > skins - > getSelected ( ) ;
DLCTexturePack * pDLCTexPack = ( DLCTexturePack * ) pTexPack ;
DLCPack * pack = pDLCTexPack - > getDLCInfoParentPack ( ) ;
DLCAudioFile * dlcAudioFile = ( DLCAudioFile * ) pack - > getFile ( DLCManager : : e_DLCType_Audio , 0 ) ;
app . DebugPrintf ( " Mashup pack \n " ) ;
// build the name
// if the music ID is beyond the end of the texture pack music files, then it's a CD
if ( m_musicID < m_iStream_CD_1 )
{
SetIsPlayingStreamingGameMusic ( true ) ;
SetIsPlayingStreamingCDMusic ( false ) ;
m_MusicType = eMusicType_Game ;
m_StreamingAudioInfo . bIs3D = false ;
2026-03-02 17:37:16 +07:00
2026-03-01 12:16:08 +08:00
# ifdef _XBOX_ONE
wstring & wstrSoundName = dlcAudioFile - > GetSoundName ( m_musicID ) ;
wstring wstrFile = L " TPACK: \\ Data \\ " + wstrSoundName + L " .binka " ;
std : : wstring mountedPath = StorageManager . GetMountedPath ( wstrFile ) ;
wcstombs ( m_szStreamName , mountedPath . c_str ( ) , 255 ) ;
# else
wstring & wstrSoundName = dlcAudioFile - > GetSoundName ( m_musicID ) ;
char szName [ 255 ] ;
wcstombs ( szName , wstrSoundName . c_str ( ) , 255 ) ;
2026-03-03 03:04:10 +08:00
# if defined __PS3__ || defined __ORBIS__ || defined __PSVITA__
string strFile = " TPACK:/Data/ " + string ( szName ) + " .binka " ;
# else
2026-03-01 12:16:08 +08:00
string strFile = " TPACK: \\ Data \\ " + string ( szName ) + " .binka " ;
2026-03-03 03:04:10 +08:00
# endif
2026-03-01 12:16:08 +08:00
std : : string mountedPath = StorageManager . GetMountedPath ( strFile ) ;
strcpy ( m_szStreamName , mountedPath . c_str ( ) ) ;
# endif
}
else
{
SetIsPlayingStreamingGameMusic ( false ) ;
SetIsPlayingStreamingCDMusic ( true ) ;
m_MusicType = eMusicType_CD ;
m_StreamingAudioInfo . bIs3D = true ;
// Need to adjust to index into the cds in the game's m_szStreamFileA
strcat ( ( char * ) m_szStreamName , " cds/ " ) ;
strcat ( ( char * ) m_szStreamName , m_szStreamFileA [ m_musicID - m_iStream_CD_1 + eStream_CD_1 ] ) ;
strcat ( ( char * ) m_szStreamName , " .binka " ) ;
}
}
else
2026-03-02 17:37:16 +07:00
{
2026-03-01 12:16:08 +08:00
// 4J-PB - if this is a PS3 disc patch, we have to check if the music file is in the patch data
# ifdef __PS3__
if ( app . GetBootedFromDiscPatch ( ) & & ( m_musicID < m_iStream_CD_1 ) )
{
// rebuild the path for the music
strcpy ( ( char * ) m_szStreamName , m_szMusicPath ) ;
strcat ( ( char * ) m_szStreamName , " music/ " ) ;
strcat ( ( char * ) m_szStreamName , m_szStreamFileA [ m_musicID ] ) ;
strcat ( ( char * ) m_szStreamName , " .binka " ) ;
// check if this is in the patch data
2026-03-02 17:37:16 +07:00
sprintf ( m_szStreamName , " %s/%s " , app . GetBDUsrDirPath ( m_szStreamName ) , m_szMusicPath ) ;
2026-03-01 12:16:08 +08:00
strcat ( ( char * ) m_szStreamName , " music/ " ) ;
strcat ( ( char * ) m_szStreamName , m_szStreamFileA [ m_musicID ] ) ;
strcat ( ( char * ) m_szStreamName , " .binka " ) ;
SetIsPlayingStreamingGameMusic ( true ) ;
SetIsPlayingStreamingCDMusic ( false ) ;
m_MusicType = eMusicType_Game ;
m_StreamingAudioInfo . bIs3D = false ;
}
else if ( m_musicID < m_iStream_CD_1 )
{
SetIsPlayingStreamingGameMusic ( true ) ;
SetIsPlayingStreamingCDMusic ( false ) ;
m_MusicType = eMusicType_Game ;
m_StreamingAudioInfo . bIs3D = false ;
// build the name
strcat ( ( char * ) m_szStreamName , " music/ " ) ;
strcat ( ( char * ) m_szStreamName , m_szStreamFileA [ m_musicID ] ) ;
strcat ( ( char * ) m_szStreamName , " .binka " ) ;
}
else
{
SetIsPlayingStreamingGameMusic ( false ) ;
SetIsPlayingStreamingCDMusic ( true ) ;
m_MusicType = eMusicType_CD ;
m_StreamingAudioInfo . bIs3D = true ;
// build the name
strcat ( ( char * ) m_szStreamName , " cds/ " ) ;
strcat ( ( char * ) m_szStreamName , m_szStreamFileA [ m_musicID ] ) ;
strcat ( ( char * ) m_szStreamName , " .binka " ) ;
}
2026-03-02 17:37:16 +07:00
# else
2026-03-01 12:16:08 +08:00
if ( m_musicID < m_iStream_CD_1 )
{
SetIsPlayingStreamingGameMusic ( true ) ;
SetIsPlayingStreamingCDMusic ( false ) ;
m_MusicType = eMusicType_Game ;
m_StreamingAudioInfo . bIs3D = false ;
// build the name
strcat ( ( char * ) m_szStreamName , " music/ " ) ;
}
else
{
SetIsPlayingStreamingGameMusic ( false ) ;
SetIsPlayingStreamingCDMusic ( true ) ;
m_MusicType = eMusicType_CD ;
m_StreamingAudioInfo . bIs3D = true ;
// build the name
strcat ( ( char * ) m_szStreamName , " cds/ " ) ;
}
strcat ( ( char * ) m_szStreamName , m_szStreamFileA [ m_musicID ] ) ;
strcat ( ( char * ) m_szStreamName , " .binka " ) ;
2026-03-02 17:37:16 +07:00
2026-03-01 12:16:08 +08:00
# endif
}
// wstring name = m_szStreamFileA[m_musicID];
// char *SoundName = (char *)ConvertSoundPathToName(name);
// strcat((char *)szStreamName,SoundName);
2026-03-02 12:27:32 -05:00
const bool isCD = ( m_musicID > = m_iStream_CD_1 ) ;
const char * folder = isCD ? " cds/ " : " music/ " ;
FILE * pFile = nullptr ;
if ( fopen_s ( & pFile , reinterpret_cast < char * > ( m_szStreamName ) , " rb " ) = = 0 & & pFile )
{
fclose ( pFile ) ;
}
else
{
const char * extensions [ ] = { " .wav " } ; // only wav works outside of binka files to my knowledge, i've only tested ogg, wav, mp3 and only wav worked out of the bunch
size_t count = sizeof ( extensions ) / sizeof ( extensions [ 0 ] ) ;
bool found = false ;
for ( size_t i = 0 ; i < count ; i + + )
{
int n = sprintf_s ( reinterpret_cast < char * > ( m_szStreamName ) , 512 , " %s%s%s%s " , m_szMusicPath , folder , m_szStreamFileA [ m_musicID ] , extensions [ i ] ) ;
if ( n < 0 ) continue ;
if ( fopen_s ( & pFile , reinterpret_cast < char * > ( m_szStreamName ) , " rb " ) = = 0 & & pFile )
{
fclose ( pFile ) ;
found = true ;
break ;
}
}
if ( ! found )
{
return ;
}
}
2026-03-01 12:16:08 +08:00
app . DebugPrintf ( " Starting streaming - %s \n " , m_szStreamName ) ;
2026-03-02 17:37:16 +07:00
// Don't actually open in this thread, as it can block for ~300ms.
2026-03-01 12:16:08 +08:00
m_openStreamThread = new C4JThread ( OpenStreamThreadProc , this , " OpenStreamThreadProc " ) ;
m_openStreamThread - > Run ( ) ;
m_StreamState = eMusicStreamState_Opening ;
}
break ;
case eMusicStreamState_Opening :
// If the open stream thread is complete, then we are ready to proceed to actually playing
if ( ! m_openStreamThread - > isRunning ( ) )
{
delete m_openStreamThread ;
m_openStreamThread = NULL ;
2026-03-02 17:37:16 +07:00
HSAMPLE hSample = AIL_stream_sample_handle ( m_hStream ) ;
2026-03-01 12:16:08 +08:00
// 4J-PB - causes the falloff to be calculated on the PPU instead of the SPU, and seems to resolve our distorted sound issue
AIL_register_falloff_function_callback ( hSample , & custom_falloff_function ) ;
if ( m_StreamingAudioInfo . bIs3D )
{
AIL_set_sample_3D_distances ( hSample , 64.0f , 1 , 0 ) ; // Larger distance scaler for music discs
if ( m_validListenerCount > 1 )
{
float fClosest = 10000.0f ;
int iClosestListener = 0 ;
float fClosestX = 0.0f , fClosestY = 0.0f , fClosestZ = 0.0f , fDist ;
// need to calculate the distance from the sound to the nearest listener - use Manhattan Distance as the decision
for ( int i = 0 ; i < MAX_LOCAL_PLAYERS ; i + + )
{
if ( m_ListenerA [ i ] . bValid )
{
float x , y , z ;
x = fabs ( m_ListenerA [ i ] . vPosition . x - m_StreamingAudioInfo . x ) ;
y = fabs ( m_ListenerA [ i ] . vPosition . y - m_StreamingAudioInfo . y ) ;
z = fabs ( m_ListenerA [ i ] . vPosition . z - m_StreamingAudioInfo . z ) ;
fDist = x + y + z ;
if ( fDist < fClosest )
{
fClosest = fDist ;
fClosestX = x ;
fClosestY = y ;
fClosestZ = z ;
iClosestListener = i ;
}
}
}
// our distances in the world aren't very big, so floats rather than casts to doubles should be fine
fDist = sqrtf ( ( fClosestX * fClosestX ) + ( fClosestY * fClosestY ) + ( fClosestZ * fClosestZ ) ) ;
AIL_set_sample_3D_position ( hSample , 0 , 0 , fDist ) ;
}
else
{
AIL_set_sample_3D_position ( hSample , m_StreamingAudioInfo . x , m_StreamingAudioInfo . y , - m_StreamingAudioInfo . z ) ; // Flipped sign of z as Miles is expecting left handed coord system
}
}
else
{
// clear the 3d flag on the stream after a jukebox finishes and streaming music starts
2026-03-02 17:37:16 +07:00
AIL_set_sample_is_3D ( hSample , 0 ) ;
2026-03-01 12:16:08 +08:00
}
// set the pitch
app . DebugPrintf ( " Sample rate:%d \n " , AIL_sample_playback_rate ( hSample ) ) ;
AIL_set_sample_playback_rate_factor ( hSample , m_StreamingAudioInfo . pitch ) ;
2026-03-02 17:37:16 +07:00
// set the volume
2026-03-01 12:16:08 +08:00
AIL_set_sample_volume_levels ( hSample , m_StreamingAudioInfo . volume * getMasterMusicVolume ( ) , m_StreamingAudioInfo . volume * getMasterMusicVolume ( ) ) ;
AIL_start_stream ( m_hStream ) ;
m_StreamState = eMusicStreamState_Playing ;
}
break ;
case eMusicStreamState_OpeningCancel :
if ( ! m_openStreamThread - > isRunning ( ) )
{
delete m_openStreamThread ;
m_openStreamThread = NULL ;
m_StreamState = eMusicStreamState_Stop ;
}
break ;
case eMusicStreamState_Stop :
// should gradually take the volume down in steps
AIL_pause_stream ( m_hStream , 1 ) ;
AIL_close_stream ( m_hStream ) ;
m_hStream = 0 ;
SetIsPlayingStreamingCDMusic ( false ) ;
SetIsPlayingStreamingGameMusic ( false ) ;
m_StreamState = eMusicStreamState_Idle ;
break ;
case eMusicStreamState_Stopping :
break ;
case eMusicStreamState_Play :
break ;
case eMusicStreamState_Playing :
if ( GetIsPlayingStreamingGameMusic ( ) )
{
//if(m_MusicInfo.pCue!=NULL)
{
bool playerInEnd = false ;
bool playerInNether = false ;
Minecraft * pMinecraft = Minecraft : : GetInstance ( ) ;
for ( unsigned int i = 0 ; i < MAX_LOCAL_PLAYERS ; + + i )
{
if ( pMinecraft - > localplayers [ i ] ! = NULL )
{
if ( pMinecraft - > localplayers [ i ] - > dimension = = LevelData : : DIMENSION_END )
{
playerInEnd = true ;
}
else if ( pMinecraft - > localplayers [ i ] - > dimension = = LevelData : : DIMENSION_NETHER )
{
playerInNether = true ;
}
}
}
if ( playerInEnd & & ! GetIsPlayingEndMusic ( ) )
{
m_StreamState = eMusicStreamState_Stop ;
// Set the end track
m_musicID = getMusicID ( LevelData : : DIMENSION_END ) ;
SetIsPlayingEndMusic ( true ) ;
2026-03-02 17:37:16 +07:00
SetIsPlayingNetherMusic ( false ) ;
2026-03-01 12:16:08 +08:00
}
else if ( ! playerInEnd & & GetIsPlayingEndMusic ( ) )
{
if ( playerInNether )
{
m_StreamState = eMusicStreamState_Stop ;
// Set the end track
m_musicID = getMusicID ( LevelData : : DIMENSION_NETHER ) ;
SetIsPlayingEndMusic ( false ) ;
2026-03-02 17:37:16 +07:00
SetIsPlayingNetherMusic ( true ) ;
2026-03-01 12:16:08 +08:00
}
else
{
m_StreamState = eMusicStreamState_Stop ;
// Set the end track
m_musicID = getMusicID ( LevelData : : DIMENSION_OVERWORLD ) ;
SetIsPlayingEndMusic ( false ) ;
2026-03-02 17:37:16 +07:00
SetIsPlayingNetherMusic ( false ) ;
2026-03-01 12:16:08 +08:00
}
}
else if ( playerInNether & & ! GetIsPlayingNetherMusic ( ) )
{
m_StreamState = eMusicStreamState_Stop ;
// set the Nether track
m_musicID = getMusicID ( LevelData : : DIMENSION_NETHER ) ;
SetIsPlayingNetherMusic ( true ) ;
SetIsPlayingEndMusic ( false ) ;
}
else if ( ! playerInNether & & GetIsPlayingNetherMusic ( ) )
{
if ( playerInEnd )
{
m_StreamState = eMusicStreamState_Stop ;
// set the Nether track
m_musicID = getMusicID ( LevelData : : DIMENSION_END ) ;
SetIsPlayingNetherMusic ( false ) ;
SetIsPlayingEndMusic ( true ) ;
}
else
{
m_StreamState = eMusicStreamState_Stop ;
// set the Nether track
m_musicID = getMusicID ( LevelData : : DIMENSION_OVERWORLD ) ;
SetIsPlayingNetherMusic ( false ) ;
SetIsPlayingEndMusic ( false ) ;
}
}
// volume change required?
if ( fMusicVol ! = getMasterMusicVolume ( ) )
{
fMusicVol = getMasterMusicVolume ( ) ;
2026-03-02 17:37:16 +07:00
HSAMPLE hSample = AIL_stream_sample_handle ( m_hStream ) ;
2026-03-01 12:16:08 +08:00
//AIL_set_sample_3D_position( hSample, m_StreamingAudioInfo.x, m_StreamingAudioInfo.y, m_StreamingAudioInfo.z );
AIL_set_sample_volume_levels ( hSample , fMusicVol , fMusicVol ) ;
}
}
}
else
{
// Music disc playing - if it's a 3D stream, then set the position - we don't have any streaming audio in the world that moves, so this isn't
// required unless we have more than one listener, and are setting the listening position to the origin and setting a fake position
// for the sound down the z axis
if ( m_StreamingAudioInfo . bIs3D )
{
if ( m_validListenerCount > 1 )
{
float fClosest = 10000.0f ;
int iClosestListener = 0 ;
float fClosestX = 0.0f , fClosestY = 0.0f , fClosestZ = 0.0f , fDist ;
// need to calculate the distance from the sound to the nearest listener - use Manhattan Distance as the decision
for ( int i = 0 ; i < MAX_LOCAL_PLAYERS ; i + + )
{
if ( m_ListenerA [ i ] . bValid )
{
float x , y , z ;
x = fabs ( m_ListenerA [ i ] . vPosition . x - m_StreamingAudioInfo . x ) ;
y = fabs ( m_ListenerA [ i ] . vPosition . y - m_StreamingAudioInfo . y ) ;
z = fabs ( m_ListenerA [ i ] . vPosition . z - m_StreamingAudioInfo . z ) ;
fDist = x + y + z ;
if ( fDist < fClosest )
{
fClosest = fDist ;
fClosestX = x ;
fClosestY = y ;
fClosestZ = z ;
iClosestListener = i ;
}
}
}
// our distances in the world aren't very big, so floats rather than casts to doubles should be fine
2026-03-02 17:37:16 +07:00
HSAMPLE hSample = AIL_stream_sample_handle ( m_hStream ) ;
2026-03-01 12:16:08 +08:00
fDist = sqrtf ( ( fClosestX * fClosestX ) + ( fClosestY * fClosestY ) + ( fClosestZ * fClosestZ ) ) ;
AIL_set_sample_3D_position ( hSample , 0 , 0 , fDist ) ;
}
}
}
break ;
case eMusicStreamState_Completed :
2026-03-02 17:37:16 +07:00
{
2026-03-01 12:16:08 +08:00
// random delay of up to 3 minutes for music
m_iMusicDelay = random - > nextInt ( 20 * 60 * 3 ) ; //random->nextInt(20 * 60 * 10) + 20 * 60 * 10;
// Check if we have a local player in The Nether or in The End, and play that music if they are
Minecraft * pMinecraft = Minecraft : : GetInstance ( ) ;
bool playerInEnd = false ;
bool playerInNether = false ;
for ( unsigned int i = 0 ; i < MAX_LOCAL_PLAYERS ; i + + )
{
if ( pMinecraft - > localplayers [ i ] ! = NULL )
{
if ( pMinecraft - > localplayers [ i ] - > dimension = = LevelData : : DIMENSION_END )
{
playerInEnd = true ;
}
else if ( pMinecraft - > localplayers [ i ] - > dimension = = LevelData : : DIMENSION_NETHER )
{
playerInNether = true ;
}
}
}
if ( playerInEnd )
{
m_musicID = getMusicID ( LevelData : : DIMENSION_END ) ;
SetIsPlayingEndMusic ( true ) ;
SetIsPlayingNetherMusic ( false ) ;
}
else if ( playerInNether )
{
m_musicID = getMusicID ( LevelData : : DIMENSION_NETHER ) ;
SetIsPlayingNetherMusic ( true ) ;
SetIsPlayingEndMusic ( false ) ;
}
else
{
m_musicID = getMusicID ( LevelData : : DIMENSION_OVERWORLD ) ;
SetIsPlayingNetherMusic ( false ) ;
SetIsPlayingEndMusic ( false ) ;
}
m_StreamState = eMusicStreamState_Idle ;
}
break ;
}
// check the status of the stream - this is for when a track completes rather than is stopped by the user action
if ( m_hStream ! = 0 )
{
if ( AIL_stream_status ( m_hStream ) = = SMP_DONE ) // SMP_DONE
{
AIL_close_stream ( m_hStream ) ;
m_hStream = 0 ;
SetIsPlayingStreamingCDMusic ( false ) ;
SetIsPlayingStreamingGameMusic ( false ) ;
m_StreamState = eMusicStreamState_Completed ;
}
}
}
/////////////////////////////////////////////
//
// ConvertSoundPathToName
//
/////////////////////////////////////////////
char * SoundEngine : : ConvertSoundPathToName ( const wstring & name , bool bConvertSpaces )
{
static char buf [ 256 ] ;
assert ( name . length ( ) < 256 ) ;
for ( unsigned int i = 0 ; i < name . length ( ) ; i + + )
{
wchar_t c = name [ i ] ;
if ( c = = ' . ' ) c = ' / ' ;
if ( bConvertSpaces )
{
if ( c = = ' ' ) c = ' _ ' ;
}
buf [ i ] = ( char ) c ;
}
buf [ name . length ( ) ] = 0 ;
return buf ;
}
# endif
2026-03-02 17:37:16 +07:00
F32 AILCALLBACK custom_falloff_function ( HSAMPLE S ,
2026-03-01 12:16:08 +08:00
F32 distance ,
F32 rolloff_factor ,
F32 min_dist ,
F32 max_dist )
{
F32 result ;
// This is now emulating the linear fall-off function that we used on the Xbox 360. The parameter which is passed as "max_dist" is the only one actually used,
// and is generally used as CurveDistanceScaler is used on XACT on the Xbox. A special value of 10000.0f is passed for thunder, which has no attenuation
if ( max_dist = = 10000.0f )
{
return 1.0f ;
}
result = 1.0f - ( distance / max_dist ) ;
if ( result < 0.0f ) result = 0.0f ;
if ( result > 1.0f ) result = 1.0f ;
return result ;
}