Files
GabsPuNs-Project_Zenith_Main/Minecraft.Client/Common/Audio/SoundEngine.cpp
GabsPuNs 8ffadc0f15 Optimizations
This solves an FPS problem when 10 or more entities take damage
2026-05-28 22:22:22 -04:00

1372 lines
37 KiB
C++

#include "SoundEngine.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 "../DLC/DLCAudioFile.h"
#include "../DLC/DLCPack.h"
#include "../../BossMobGuiInfo.h"
#include "../../Windows64/Windows64_App.h"
#include "../Libs/audio/stb_vorbis.h"
#define MA_NO_DSOUND
#define MA_NO_WINMM
#define MINIAUDIO_IMPLEMENTATION
#include "../Libs/audio/miniaudio.h"
char SoundEngine::m_szMiscSoundPath[]={"Assets/Sounds/Misc/%s"};
char SoundEngine::m_szUISoundPath[]={"Assets/Sounds/UI/%s"};
char SoundEngine::m_szMusicPath[]={"Assets/Sounds/Music/"};
const char *SoundEngine::m_szStreamFileA[eStream_Max]=
{
"calm1",
"calm2",
"calm3",
"hal1",
"hal2",
"hal3",
"hal4",
"nuance1",
"nuance2",
// Creative
"creative1",
"creative2",
"creative3",
"creative4",
"creative5",
"creative6",
// Menu
"menu1",
"menu2",
"menu3",
"menu4",
// Piano
"piano1",
"piano2",
"piano3",
// Nether
"nether1",
"nether2",
"nether3",
"nether4",
// The End
"the_end_dragon_alive",
"the_end_end",
// Battle
"BattleMode1",
"BattleMode2",
"BattleMode3",
"BattleMode4",
// CDs
"11",
"13",
"blocks",
"cat",
"chirp",
"far",
"mall",
"mellohi",
"stal",
"strad",
"ward",
"where_are_we_now"
};
std::vector<MiniAudioSound*> m_activeSounds;
/////////////////////////////////////////////
//
// init
//
/////////////////////////////////////////////
void SoundEngine::init(Options* pOptions)
{
app.DebugPrintf("---SoundEngine::init\n");
m_engineConfig = ma_engine_config_init();
m_engineConfig.listenerCount = MAX_LOCAL_PLAYERS;
if (ma_engine_init(&m_engineConfig, &m_engine) != MA_SUCCESS)
{
destroy();
app.DebugPrintf("Failed to initialize miniaudio engine\n");
return;
}
ma_engine_set_volume(&m_engine, 1.0f);
m_MasterMusicVolume = 1.0f;
m_MasterEffectsVolume = 1.0f;
m_validListenerCount = 1;
m_bSystemMusicPlaying = false;
app.DebugPrintf("---miniaudio initialized\n");
return;
}
void SoundEngine::SetStreamingSounds(int iOverworldMin, int iOverWorldMax, int iNetherMin, int iNetherMax, int iEndMin, int iEndMax, int iCreativeMin, int iCreativeMax, int iMenuMin, int iMenuMax, int iBattleMin, int iBattleMax, 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_Creative_Min = iCreativeMin;
m_iStream_Creative_Max = iCreativeMax;
m_iStream_Menu_Min = iMenuMin;
m_iStream_Menu_Max = iMenuMax;
m_iStream_Battle_Min = iBattleMin;
m_iStream_Battle_Max = iBattleMax;
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));
}
void SoundEngine::updateMiniAudio()
{
if (m_validListenerCount == 1)
{
for (size_t i = 0; i < MAX_LOCAL_PLAYERS; i++)
{
if (m_ListenerA[i].bValid)
{
ma_engine_listener_set_position(
&m_engine,
0,
m_ListenerA[i].vPosition.x,
m_ListenerA[i].vPosition.y,
-m_ListenerA[i].vPosition.z);
ma_engine_listener_set_direction(
&m_engine,
0,
m_ListenerA[i].vOrientFront.x,
m_ListenerA[i].vOrientFront.y,
-m_ListenerA[i].vOrientFront.z);
ma_engine_listener_set_world_up(
&m_engine,
0,
0.0f, 1.0f, 0.0f);
break;
}
}
}
else
{
// splitscreen: listener at origin, sounds repositioned by distance down the z axis
ma_engine_listener_set_position(&m_engine, 0, 0.0f, 0.0f, 0.0f);
ma_engine_listener_set_direction(&m_engine, 0, 0.0f, 0.0f, 1.0f);
ma_engine_listener_set_world_up(&m_engine, 0, 0.0f, 1.0f, 0.0f);
}
// iterate over active sounds and clean up completed ones
for (auto it = m_activeSounds.begin(); it != m_activeSounds.end(); )
{
MiniAudioSound *s = *it;
if (!ma_sound_is_playing(&s->sound))
{
ma_sound_uninit(&s->sound);
delete s;
it = m_activeSounds.erase(it);
continue;
}
float finalVolume = s->info.volume * m_MasterEffectsVolume * SFX_VOLUME_MULTIPLIER;
if (finalVolume > SFX_MAX_GAIN)
finalVolume = SFX_MAX_GAIN;
ma_sound_set_volume(&s->sound, finalVolume);
ma_sound_set_pitch(&s->sound, s->info.pitch);
if (s->info.bIs3D)
{
if (m_validListenerCount > 1)
{
float fClosest=10000.0f;
float fClosestX=0.0f,fClosestY=0.0f,fClosestZ=0.0f,fDist;
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-s->info.x);
y=fabs(m_ListenerA[i].vPosition.y-s->info.y);
z=fabs(m_ListenerA[i].vPosition.z-s->info.z);
fDist=x+y+z;
if(fDist<fClosest)
{
fClosest=fDist;
fClosestX=x;
fClosestY=y;
fClosestZ=z;
}
}
}
float realDist = sqrtf((fClosestX*fClosestX)+(fClosestY*fClosestY)+(fClosestZ*fClosestZ));
ma_sound_set_position(&s->sound, 0, 0, realDist);
}
else
{
ma_sound_set_position(
&s->sound,
s->info.x,
s->info.y,
-s->info.z);
}
}
++it;
}
}
/////////////////////////////////////////////
//
// getGameModeMusicID
//
/////////////////////////////////////////////
inline void SoundEngine::getGameModeMusicID(Minecraft* pMinecraft, unsigned int i)
{
for(i = 0; i < MAX_LOCAL_PLAYERS; i++)
{
if(pMinecraft->localplayers[i] != nullptr && pMinecraft->localgameModes[i] != nullptr)
{
if (pMinecraft->localplayers[i]->abilities.instabuild && pMinecraft->localplayers[i]->abilities.mayfly)
{
m_musicID = getMusicID(eMusicType_Creative);
break;
}
/*
// TODO(3UR): this is a part of minigames also in the future other minigame ids will need to be handled for now TU30 only checks for BATTLE
else if (pMinecraft->GetCustomGameMode() && CustomGameModeInst::GetId() == EMiniGameId::BATTLE) // @3UR: thanks https://github.com/GRAnimated/MinecraftLCE/blob/6947670d152582457bfe02bd909ee30a7ab7eb55/src/Minecraft.World/net/minecraft/world/level/gamemode/minigames/EMiniGameId.h#L3
m_musicID = getMusicID(eMusicType_Battle);
*/
else
m_musicID = getMusicID(eMusicType_Overworld);
}
else
m_musicID = getMusicID(eMusicType_Menu);
}
}
/////////////////////////////////////////////
//
// tick
//
/////////////////////////////////////////////
void SoundEngine::tick(shared_ptr<Mob> *players, float a)
{
ConsoleSoundEngine::tick();
// update the listener positions
int listenerCount = 0;
if( players )
{
for( size_t i = 0; i < MAX_LOCAL_PLAYERS; i++ )
{
if( players[i] != nullptr )
{
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);
float ySin = (float)sin(yRot * Mth::RAD_TO_GRAD);
// store the listener positions for splitscreen
m_ListenerA[i].vPosition.x = x;
m_ListenerA[i].vPosition.y = y;
m_ListenerA[i].vPosition.z = z;
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;
updateMiniAudio();
}
/////////////////////////////////////////////
//
// SoundEngine
//
/////////////////////////////////////////////
SoundEngine::SoundEngine()
{
random = new Random();
memset(&m_engine, 0, sizeof(ma_engine));
memset(&m_engineConfig, 0, sizeof(ma_engine_config));
m_musicStreamActive = false;
m_StreamState = eMusicStreamState_Idle;
m_iMusicDelay = 0;
m_validListenerCount = 0;
m_bHeardTrackA = nullptr;
// 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_Overworld_Creative1, eStream_Overworld_Creative6,
eStream_Overworld_Menu1, eStream_Overworld_Menu4,
eStream_BattleMode1, eStream_BattleMode4,
eStream_CD_1);
m_musicID = getMusicID(eMusicType_Menu);
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);
}
SoundEngine::~SoundEngine()
{
destroy();
}
void SoundEngine::destroy()
{
for (auto s : m_activeSounds)
{
ma_sound_uninit(&s->sound);
delete s;
}
m_activeSounds.clear();
ma_engine_uninit(&m_engine);
if (random)
{
delete random;
random = nullptr;
}
if (m_bHeardTrackA)
{
delete [] m_bHeardTrackA;
m_bHeardTrackA = nullptr;
}
}
#ifdef _DEBUG
void SoundEngine::GetSoundName(char *szSoundName,int iSound)
{
strcpy_s(szSoundName, 256, "Minecraft/");
wstring name = wchSoundNames[iSound];
char *SoundName = (char *)ConvertSoundPathToName(name);
strcat_s(szSoundName, 256, SoundName);
}
#endif
/////////////////////////////////////////////
//
// play
//
/////////////////////////////////////////////
void SoundEngine::play(int iSound, float x, float y, float z, float volume, float pitch)
{
if (iSound == -1)
{
app.DebugPrintf(6, "PlaySound with sound of -1 !!!!!!!!!!!!!!!\n");
return;
}
wstring name = wchSoundNames[iSound];
char* SoundName = (char*)ConvertSoundPathToName(name);
std::vector<std::string>* validPaths = nullptr;
auto it = m_sfxPathCache.find(iSound);
if (it != m_sfxPathCache.end())
{
validPaths = &it->second;
app.DebugPrintf(6, "Cache - PlaySound - %d - Sounds/Misc/%s (%f %f %f, vol %f, pitch %f)\n",
iSound, SoundName, x, y, z, volume, pitch);
}
else
{
std::vector<std::string> foundPaths;
app.DebugPrintf(6, "PlaySound - %d - Sounds/Misc/%s (%f %f %f, vol %f, pitch %f)\n",
iSound, SoundName, x, y, z, volume, pitch);
char basePath[256];
sprintf_s(basePath, m_szMiscSoundPath, SoundName);
const char* extensions[] = { ".ogg", ".wav" };
size_t count = sizeof(extensions) / sizeof(extensions[0]);
for (size_t i = 0; i < count; i++)
{
char path[256];
sprintf_s(path, "%s%s", basePath, extensions[i]);
if (fs::exists(path))
foundPaths.push_back(path);
}
if (foundPaths.empty())
{
for (size_t i = 1; i < 32; i++)
{
for (size_t i2 = 0; i2 < count; i2++)
{
char path[256];
sprintf_s(path, "%s%d%s", basePath, i, extensions[i2]);
if (fs::exists(path))
foundPaths.push_back(path);
}
}
}
m_sfxPathCache[iSound] = foundPaths;
validPaths = &m_sfxPathCache[iSound];
if (validPaths->empty())
{
app.DebugPrintf("No sound file found: %s\n", basePath);
return;
}
}
if (validPaths->empty())
return;
int chosen = random->nextInt((int)validPaths->size());
const char* finalPath = (*validPaths)[chosen].c_str();
MiniAudioSound* s = new MiniAudioSound();
memset(&s->info, 0, sizeof(AUDIO_INFO));
s->info.x = x;
s->info.y = y;
s->info.z = z;
s->info.volume = volume;
s->info.pitch = pitch;
s->info.bIs3D = true;
s->info.bUseSoundsPitchVal = false;
s->info.iSound = iSound + eSFX_MAX;
if (ma_sound_init_from_file(
&m_engine,
finalPath,
MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_ASYNC,
nullptr,
nullptr,
&s->sound) != MA_SUCCESS)
{
app.DebugPrintf("Failed to initialize sound from file: %s\n", finalPath);
delete s;
return;
}
ma_sound_set_spatialization_enabled(&s->sound, MA_TRUE);
ma_sound_set_min_distance(&s->sound, SFX_3D_MIN_DISTANCE);
ma_sound_set_max_distance(&s->sound, SFX_3D_MAX_DISTANCE);
ma_sound_set_rolloff(&s->sound, SFX_3D_ROLLOFF);
float finalVolume = volume * m_MasterEffectsVolume * SFX_VOLUME_MULTIPLIER;
if (finalVolume > SFX_MAX_GAIN)
finalVolume = SFX_MAX_GAIN;
ma_sound_set_volume(&s->sound, finalVolume);
ma_sound_set_pitch(&s->sound, pitch);
ma_sound_set_position(&s->sound, x, y, -z);
ma_sound_start(&s->sound);
m_activeSounds.push_back(s);
}
/////////////////////////////////////////////
//
// playUI
//
/////////////////////////////////////////////
void SoundEngine::playUI(int iSound, float volume, float pitch)
{
std::string finalPathStr;
auto it = m_uiPathCache.find(iSound);
wstring name = wchUISoundNames[iSound];
char* SoundName = (char*)ConvertSoundPathToName(name);
if (it != m_uiPathCache.end())
{
finalPathStr = it->second;
app.DebugPrintf(6, "Cache - PlaySound - %d - Sounds/UI/%s (vol %f, pitch %f)\n",
iSound, SoundName, volume, pitch);
}
else
{
app.DebugPrintf(6, "PlaySound - %d - Sounds/UI/%s (vol %f, pitch %f)\n",
iSound, SoundName, volume, pitch);
char basePath[256];
sprintf_s(basePath, m_szUISoundPath, SoundName);
bool found = false;
const char* extensions[] = { ".ogg", ".wav" };
size_t count = sizeof(extensions) / sizeof(extensions[0]);
for (size_t i = 0; i < count; i++)
{
char path[256];
sprintf_s(path, "%s%s", basePath, extensions[i]);
if (fs::exists(path))
{
finalPathStr = path;
found = true;
break;
}
}
if (!found)
app.DebugPrintf("No sound file found for UI sound: %s\n", basePath);
m_uiPathCache[iSound] = finalPathStr;
}
MiniAudioSound* s = new MiniAudioSound();
memset(&s->info, 0, sizeof(AUDIO_INFO));
s->info.volume = volume;
s->info.pitch = pitch;
s->info.bIs3D = false;
s->info.bUseSoundsPitchVal = true;
if (ma_sound_init_from_file(
&m_engine,
finalPathStr.c_str(),
MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_ASYNC,
nullptr,
nullptr,
&s->sound) != MA_SUCCESS)
{
delete s;
app.DebugPrintf("Failed to initialize sound from file: %s\n", finalPathStr.c_str());
return;
}
ma_sound_set_spatialization_enabled(&s->sound, MA_FALSE);
float finalVolume = volume * m_MasterEffectsVolume;
if (finalVolume > 1.0f)
finalVolume = 1.0f;
finalVolume *= 0.75f;
#ifndef _CONTENT_PACKAGE
printf("UI Sound volume set to %f\nEffects volume: %f\n", finalVolume, m_MasterEffectsVolume);
#endif
ma_sound_set_volume(&s->sound, finalVolume);
ma_sound_set_pitch(&s->sound, pitch);
ma_sound_start(&s->sound);
m_activeSounds.push_back(s);
}
/////////////////////////////////////////////
//
// 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;
unsigned int i = 0;
for(i = 0; i < MAX_LOCAL_PLAYERS; i++)
{
if(pMinecraft->localplayers[i] != nullptr)
{
if(pMinecraft->localplayers[i]->dimension==LevelData::DIMENSION_END)
{
playerInEnd = true;
break;
}
else if(pMinecraft->localplayers[i]->dimension==LevelData::DIMENSION_NETHER)
{
playerInNether = true;
break;
}
}
}
if(playerInEnd)
{
m_musicID = getMusicID(eMusicType_End);
}
else if(playerInNether)
m_musicID = getMusicID(eMusicType_Nether);
else
getGameModeMusicID(pMinecraft, i);
}
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(size_t i=iStart;i<=iEnd;i++)
{
if(m_bHeardTrackA[i]==false)
{
bAllTracksHeard=false;
app.DebugPrintf("Not heard all tracks yet\n");
break;
}
}
if(bAllTracksHeard)
{
app.DebugPrintf("Heard all tracks - resetting the tracking array\n");
for(size_t i=iStart;i<=iEnd;i++)
m_bHeardTrackA[i]=false;
}
// trying to get a track we haven't heard, but not too hard
for(size_t 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(eMusicType iDomain)
{
Minecraft *pMinecraft=Minecraft::GetInstance();
if( pMinecraft && !pMinecraft->skins->isUsingDefaultSkin() )
{
// using a texture pack - may have multiple End music tracks
switch( iDomain )
{
case eMusicType_Nether:
return GetRandomishTrack(m_iStream_Nether_Min, m_iStream_Nether_Max);
case eMusicType_Menu:
return GetRandomishTrack(m_iStream_Menu_Min, m_iStream_Menu_Max);
case eMusicType_End:
return GetRandomishTrack(m_iStream_End_Min, m_iStream_End_Max);
case eMusicType_Creative:
return GetRandomishTrack(m_iStream_Creative_Min, m_iStream_Creative_Max);
case eMusicType_Battle:
return GetRandomishTrack(m_iStream_Battle_Min, m_iStream_Battle_Max);
default:
return GetRandomishTrack(m_iStream_Overworld_Min, m_iStream_Overworld_Max);
}
}
else
{
// using a texture pack - may have multiple End music tracks
switch( iDomain )
{
case eMusicType_Nether:
return GetRandomishTrack(m_iStream_Nether_Min, m_iStream_Nether_Max);
case eMusicType_Menu:
return GetRandomishTrack(m_iStream_Menu_Min, m_iStream_Menu_Max);
case eMusicType_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
{
int idx = BossMobGuiInfo::getIndexFromDimension(LevelData::DIMENSION_END);
if (!BossMobGuiInfo::name[idx].empty() && BossMobGuiInfo::displayTicks[idx] > 0)
return m_iStream_End_Min;
else
return m_iStream_End_Max;
}
case eMusicType_Creative:
return GetRandomishTrack(m_iStream_Creative_Min, m_iStream_Creative_Max);
case eMusicType_Battle:
return GetRandomishTrack(m_iStream_Battle_Min, m_iStream_Battle_Max);
default:
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(size_t 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
//
/////////////////////////////////////////////
void SoundEngine::updateSoundEffectVolume(float fVal)
{
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)
{
SoundEngine* soundEngine = (SoundEngine*)lpParameter;
const char* ext = strrchr(soundEngine->m_szStreamName, '.');
if (soundEngine->m_musicStreamActive)
{
ma_sound_stop(&soundEngine->m_musicStream);
ma_sound_uninit(&soundEngine->m_musicStream);
soundEngine->m_musicStreamActive = false;
}
ma_result result = ma_sound_init_from_file(
&soundEngine->m_engine,
soundEngine->m_szStreamName,
MA_SOUND_FLAG_STREAM,
nullptr,
nullptr,
&soundEngine->m_musicStream);
if (result != MA_SUCCESS)
{
app.DebugPrintf("SoundEngine::OpenStreamThreadProc - Failed to open stream: %s\n", soundEngine->m_szStreamName);
return 0;
}
ma_sound_set_spatialization_enabled(&soundEngine->m_musicStream, MA_FALSE);
ma_sound_set_looping(&soundEngine->m_musicStream, MA_FALSE);
soundEngine->m_musicStreamActive = true;
return 0;
}
/////////////////////////////////////////////
//
// playMusicTick
//
/////////////////////////////////////////////
void SoundEngine::playMusicTick()
{
playMusicUpdate();
}
// AP - moved to a separate function so it can be called from the mixer callback on Vita
void SoundEngine::playMusicUpdate()
{
static float fMusicVol = 0.0f;
fMusicVol = getMasterMusicVolume();
switch(m_StreamState)
{
case eMusicStreamState_Idle:
// start a stream playing
if (m_iMusicDelay > 0)
{
m_iMusicDelay--;
return;
}
if (m_musicStreamActive)
{
app.DebugPrintf("WARNING: m_musicStreamActive already true in Idle state, resetting to Playing\n");
m_StreamState = eMusicStreamState_Playing;
return;
}
if(m_musicID!=-1)
{
// start playing it
strcpy_s((char *)m_szStreamName, _countof(m_szStreamName), m_szMusicPath);
// 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_StreamingAudioInfo.bIs3D=false;
wstring &wstrSoundName=dlcAudioFile->GetSoundName(m_musicID);
char szName[255];
wcstombs_s(nullptr, szName, sizeof(szName), wstrSoundName.c_str(), _TRUNCATE);
string strFile="TPACK:\\Data\\" + string(szName) + ".wav";
std::string mountedPath = StorageManager.GetMountedPath(strFile);
strcpy_s(m_szStreamName,mountedPath.c_str());
}
else
{
SetIsPlayingStreamingGameMusic(false);
SetIsPlayingStreamingCDMusic(true);
m_StreamingAudioInfo.bIs3D=true;
// Need to adjust to index into the cds in the game's m_szStreamFileA
strcat_s((char *)m_szStreamName, _countof(m_szStreamName), "CDs/");
strcat_s((char *)m_szStreamName, _countof(m_szStreamName), m_szStreamFileA[m_musicID - m_iStream_CD_1 + eStream_CD_1]);
strcat_s((char *)m_szStreamName, _countof(m_szStreamName), ".wav");
}
}
else
{
// 4J-PB - if this is a PS3 disc patch, we have to check if the music file is in the patch data
if(m_musicID<m_iStream_CD_1)
{
SetIsPlayingStreamingGameMusic(true);
SetIsPlayingStreamingCDMusic(false);
m_StreamingAudioInfo.bIs3D=false;
}
else
{
SetIsPlayingStreamingGameMusic(false);
SetIsPlayingStreamingCDMusic(true);
m_StreamingAudioInfo.bIs3D=true;
// build the name
strcat_s((char *)m_szStreamName, _countof(m_szStreamName), "CDs/");
}
strcat_s((char *)m_szStreamName, _countof(m_szStreamName), m_szStreamFileA[m_musicID]);
strcat_s((char *)m_szStreamName, _countof(m_szStreamName), ".wav");
}
// wstring name = m_szStreamFileA[m_musicID];
// char *SoundName = (char *)ConvertSoundPathToName(name);
// strcat_s((char *)szStreamName,SoundName);
FILE* pFile = nullptr;
if (fopen_s(&pFile, reinterpret_cast<char*>(m_szStreamName), "rb") == 0 && pFile)
fclose(pFile);
else
{
const char* extensions[] = { ".ogg", ".wav" };
size_t extCount = sizeof(extensions) / sizeof(extensions[0]);
bool found = false;
char* dotPos = strrchr(reinterpret_cast<char*>(m_szStreamName), '.');
if (dotPos != nullptr && (dotPos - reinterpret_cast<char*>(m_szStreamName)) < 250)
{
for (size_t i = 0; i < extCount; i++)
{
strcpy_s(dotPos, 5, extensions[i]);
if (fopen_s(&pFile, reinterpret_cast<char*>(m_szStreamName), "rb") == 0 && pFile)
{
fclose(pFile);
found = true;
break;
}
}
}
if (!found)
{
if (dotPos != nullptr)
strcpy_s(dotPos, 5, ".wav");
app.DebugPrintf("WARNING: No audio file found for music ID %d (tried .ogg, .wav)\n", m_musicID);
return;
}
}
app.DebugPrintf("Starting streaming - %s\n",m_szStreamName);
m_openStreamThread = new C4JThread(OpenStreamThreadProc, this, "OpenStreamThreadProc");
m_openStreamThread->Run();
m_StreamState = eMusicStreamState_Opening;
}
break;
case eMusicStreamState_Opening:
if( !m_openStreamThread->isRunning() )
{
delete m_openStreamThread;
m_openStreamThread = nullptr;
app.DebugPrintf("OpenStreamThreadProc finished. m_musicStreamActive=%d\n", m_musicStreamActive);
if (!m_musicStreamActive)
{
const char* currentExt = strrchr(reinterpret_cast<char*>(m_szStreamName), '.');
if (currentExt && _stricmp(currentExt, ".wav") == 0)
{
const bool isCD = (m_musicID >= m_iStream_CD_1);
const char* folder = isCD ? "CDs/" : "";
int n = sprintf_s(reinterpret_cast<char*>(m_szStreamName), 512, "%s%s%s.wav", m_szMusicPath, folder, m_szStreamFileA[m_musicID]);
if (n > 0)
{
FILE* pFile = nullptr;
if (fopen_s(&pFile, reinterpret_cast<char*>(m_szStreamName), "rb") == 0 && pFile)
{
fclose(pFile);
m_openStreamThread = new C4JThread(OpenStreamThreadProc, this, "OpenStreamThreadProc");
m_openStreamThread->Run();
break;
}
}
}
m_StreamState = eMusicStreamState_Idle;
break;
}
if (m_StreamingAudioInfo.bIs3D)
{
ma_sound_set_spatialization_enabled(&m_musicStream, MA_TRUE);
ma_sound_set_position(&m_musicStream, m_StreamingAudioInfo.x, m_StreamingAudioInfo.y, -m_StreamingAudioInfo.z);
}
else
ma_sound_set_spatialization_enabled(&m_musicStream, MA_FALSE);
ma_sound_set_pitch(&m_musicStream, m_StreamingAudioInfo.pitch);
float finalVolume = m_StreamingAudioInfo.volume * getMasterMusicVolume();
ma_sound_set_volume(&m_musicStream, finalVolume);
ma_result startResult = ma_sound_start(&m_musicStream);
app.DebugPrintf("ma_sound_start result: %d\n", startResult);
m_StreamState=eMusicStreamState_Playing;
}
break;
case eMusicStreamState_OpeningCancel:
if( !m_openStreamThread->isRunning() )
{
delete m_openStreamThread;
m_openStreamThread = nullptr;
m_StreamState = eMusicStreamState_Stop;
}
break;
case eMusicStreamState_Stop:
if (m_musicStreamActive)
{
ma_sound_stop(&m_musicStream);
ma_sound_uninit(&m_musicStream);
m_musicStreamActive = false;
}
SetIsPlayingStreamingCDMusic(false);
SetIsPlayingStreamingGameMusic(false);
m_StreamState = eMusicStreamState_Idle;
break;
case eMusicStreamState_Stopping:
break;
case eMusicStreamState_Play:
break;
case eMusicStreamState_Playing:
{
static int frameCount = 0;
if (frameCount++ % 60 == 0)
{
if (m_musicStreamActive)
{
bool isPlaying = ma_sound_is_playing(&m_musicStream);
float vol = ma_sound_get_volume(&m_musicStream);
bool isAtEnd = ma_sound_at_end(&m_musicStream);
}
}
}
if(GetIsPlayingStreamingGameMusic())
{
bool playerInEnd = false;
bool playerInNether = false;
Minecraft *pMinecraft = Minecraft::GetInstance();
unsigned int i = 0;
for(i = 0; i < MAX_LOCAL_PLAYERS; ++i)
{
if(pMinecraft->localplayers[i] != nullptr)
{
if(pMinecraft->localplayers[i]->dimension == LevelData::DIMENSION_END)
{
playerInEnd = true;
break;
}
else if(pMinecraft->localplayers[i]->dimension == LevelData::DIMENSION_NETHER)
{
playerInNether = true;
break;
}
}
}
if(playerInEnd && !GetIsPlayingEndMusic())
{
m_StreamState = eMusicStreamState_Stop;
// Set the End track
m_musicID = getMusicID(eMusicType_End);
SetIsPlayingEndMusic(true);
SetIsPlayingNetherMusic(false);
}
else if(!playerInEnd && GetIsPlayingEndMusic())
{
m_StreamState = eMusicStreamState_Stop;
if(playerInNether)
{
// Set the Nether track
m_musicID = getMusicID(eMusicType_Nether);
SetIsPlayingEndMusic(false);
SetIsPlayingNetherMusic(true);
}
else
{
getGameModeMusicID(pMinecraft, i);
SetIsPlayingEndMusic(false);
SetIsPlayingNetherMusic(false);
}
}
else if (playerInNether && !GetIsPlayingNetherMusic())
{
m_StreamState = eMusicStreamState_Stop;
// set the Nether track
m_musicID = getMusicID(eMusicType_Nether);
SetIsPlayingNetherMusic(true);
SetIsPlayingEndMusic(false);
}
else if(!playerInNether && GetIsPlayingNetherMusic())
{
m_StreamState = eMusicStreamState_Stop;
if(playerInEnd)
{
// set the End track
m_musicID = getMusicID(eMusicType_End);
SetIsPlayingNetherMusic(false);
SetIsPlayingEndMusic(true);
}
else
{
getGameModeMusicID(pMinecraft, i);
SetIsPlayingNetherMusic(false);
SetIsPlayingEndMusic(false);
}
}
/* //This is setting the music in loop
else if ( !playerInEnd && !playerInNether )
getGameModeMusicID(pMinecraft, i);
*/
// volume change required?
if (m_musicStreamActive)
{
float finalVolume = m_StreamingAudioInfo.volume * fMusicVol;
ma_sound_set_volume(&m_musicStream, finalVolume);
}
}
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 && m_validListenerCount > 1)
{
int iClosestListener = 0;
float fClosestDist = 1e6f;
for (size_t i = 0; i < MAX_LOCAL_PLAYERS; i++)
{
if (m_ListenerA[i].bValid)
{
float dx = m_StreamingAudioInfo.x - m_ListenerA[i].vPosition.x;
float dy = m_StreamingAudioInfo.y - m_ListenerA[i].vPosition.y;
float dz = m_StreamingAudioInfo.z - m_ListenerA[i].vPosition.z;
float dist = sqrtf(dx*dx + dy*dy + dz*dz);
if (dist < fClosestDist)
{
fClosestDist = dist;
iClosestListener = i;
}
}
}
float relX = m_StreamingAudioInfo.x - m_ListenerA[iClosestListener].vPosition.x;
float relY = m_StreamingAudioInfo.y - m_ListenerA[iClosestListener].vPosition.y;
float relZ = m_StreamingAudioInfo.z - m_ListenerA[iClosestListener].vPosition.z;
if (m_musicStreamActive)
ma_sound_set_position(&m_musicStream, relX, relY, relZ);
}
}
break;
case eMusicStreamState_Completed:
{
// 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;
unsigned int i = 0;
for (i = 0; i < MAX_LOCAL_PLAYERS; i++)
{
if(pMinecraft->localplayers[i] != nullptr)
{
if(pMinecraft->localplayers[i]->dimension == LevelData::DIMENSION_END)
{
playerInEnd = true;
break;
}
else if(pMinecraft->localplayers[i]->dimension == LevelData::DIMENSION_NETHER)
{
playerInNether = true;
break;
}
}
}
if(playerInEnd)
{
m_musicID = getMusicID(eMusicType_End);
SetIsPlayingEndMusic(true);
SetIsPlayingNetherMusic(false);
}
else if(playerInNether)
{
m_musicID = getMusicID(eMusicType_Nether);
SetIsPlayingNetherMusic(true);
SetIsPlayingEndMusic(false);
}
else
{
getGameModeMusicID(pMinecraft, i);
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_musicStreamActive)
{
if (!ma_sound_is_playing(&m_musicStream) && ma_sound_at_end(&m_musicStream))
{
ma_sound_uninit(&m_musicStream);
m_musicStreamActive = false;
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;
}