mirror of
https://github.com/Patoke/LCERenewed.git
synced 2026-07-09 16:17:03 +00:00
2513 lines
84 KiB
C++
2513 lines
84 KiB
C++
#include "stdafx.h"
|
|
|
|
#include "..\..\..\Minecraft.World\net.minecraft.world.level.h"
|
|
#include "..\..\DLCTexturePack.h"
|
|
#include "..\..\Minecraft.World\leveldata.h"
|
|
#include "..\..\Minecraft.World\mth.h"
|
|
#include "..\..\MultiplayerLocalPlayer.h"
|
|
#include "..\..\TexturePackRepository.h"
|
|
#include "..\Consoles_App.h"
|
|
#include "Common\DLC\DLCAudioFile.h"
|
|
#include "SoundEngine.h"
|
|
|
|
#ifdef __PSVITA__
|
|
#include <audioout.h>
|
|
#endif
|
|
|
|
#ifdef _WINDOWS64
|
|
#include "..\..\Minecraft.Client\Windows64\Windows64_App.h"
|
|
#include "third_party\openAL\include\AL\al.h"
|
|
#include "third_party\openAL\include\AL\alc.h"
|
|
#include "third_party\openAL\include\AL\alext.h"
|
|
#include "third_party/stb_vorbis.c"
|
|
#include <io.h>
|
|
#include <map>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <windows.h>
|
|
|
|
#endif
|
|
|
|
#ifdef __ORBIS__
|
|
#include <audioout.h>
|
|
// #define __DISABLE_MILES__ // MGH disabled for now as it
|
|
// crashes if we call sceNpMatching2Initialize
|
|
#endif
|
|
|
|
// take out Orbis until they are done
|
|
#if defined _XBOX
|
|
|
|
SoundEngine::SoundEngine() {}
|
|
void SoundEngine::init(Options *pOptions) {}
|
|
|
|
void SoundEngine::tick(shared_ptr<Mob> *players, float a) {}
|
|
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
|
|
char SoundEngine::m_szSoundPath[] = {"sources\\sounds\\Durango\\Sound\\"};
|
|
char SoundEngine::m_szMusicPath[] = {"sources\\sounds\\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
|
|
|
|
#ifndef _WINDOWS64
|
|
F32 AILCALLBACK custom_falloff_function(HSAMPLE S, F32 distance,
|
|
F32 rolloff_factor, F32 min_dist,
|
|
F32 max_dist);
|
|
#endif
|
|
|
|
#ifdef _WINDOWS64
|
|
struct OpenALApi {
|
|
LPALGENBUFFERS alGenBuffers;
|
|
LPALDELETEBUFFERS alDeleteBuffers;
|
|
LPALBUFFERDATA alBufferData;
|
|
LPALGENSOURCES alGenSources;
|
|
LPALDELETESOURCES alDeleteSources;
|
|
LPALSOURCEI alSourcei;
|
|
LPALSOURCEF alSourcef;
|
|
LPALSOURCE3F alSource3f;
|
|
LPALSOURCEPLAY alSourcePlay;
|
|
LPALSOURCEPAUSE alSourcePause;
|
|
LPALSOURCESTOP alSourceStop;
|
|
LPALGETSOURCEI alGetSourcei;
|
|
LPALLISTENER3F alListener3f;
|
|
LPALLISTENERFV alListenerfv;
|
|
LPALDISTANCEMODEL alDistanceModel;
|
|
LPALGETERROR alGetError;
|
|
LPALCOPENDEVICE alcOpenDevice;
|
|
LPALCCLOSEDEVICE alcCloseDevice;
|
|
LPALCCREATECONTEXT alcCreateContext;
|
|
LPALCDESTROYCONTEXT alcDestroyContext;
|
|
LPALCMAKECONTEXTCURRENT alcMakeContextCurrent;
|
|
LPALCGETERROR alcGetError;
|
|
LPALCGETSTRING alcGetString;
|
|
};
|
|
|
|
struct OpenALState {
|
|
bool loaded;
|
|
bool ready;
|
|
bool moduleOwned;
|
|
HMODULE module;
|
|
ALCdevice *device;
|
|
ALCcontext *context;
|
|
ALuint source;
|
|
ALuint buffer;
|
|
bool streamLoaded;
|
|
int streamChannels;
|
|
int streamSampleRate;
|
|
std::vector<short> streamPCM;
|
|
std::vector<ALuint> activeSources;
|
|
std::map<std::string, ALuint> cachedBuffers;
|
|
};
|
|
|
|
static OpenALApi g_OpenALApi;
|
|
static OpenALState g_OpenALState;
|
|
|
|
template <typename T>
|
|
static bool resolveProc(HMODULE module, const char *name, T &proc) {
|
|
proc = reinterpret_cast<T>(GetProcAddress(module, name));
|
|
return proc != NULL;
|
|
}
|
|
|
|
static void resetStreamData() {
|
|
g_OpenALState.streamLoaded = false;
|
|
g_OpenALState.streamChannels = 0;
|
|
g_OpenALState.streamSampleRate = 0;
|
|
g_OpenALState.streamPCM.clear();
|
|
}
|
|
|
|
static void cleanupSources() {
|
|
for (size_t i = 0; i < g_OpenALState.activeSources.size();) {
|
|
ALuint source = g_OpenALState.activeSources[i];
|
|
ALint state = AL_STOPPED;
|
|
g_OpenALApi.alGetSourcei(source, AL_SOURCE_STATE, &state);
|
|
if (state == AL_STOPPED || state == AL_INITIAL) {
|
|
g_OpenALApi.alDeleteSources(1, &source);
|
|
g_OpenALState.activeSources.erase(g_OpenALState.activeSources.begin() +
|
|
i);
|
|
} else {
|
|
++i;
|
|
}
|
|
}
|
|
}
|
|
|
|
// terrible
|
|
static bool loadApi() {
|
|
if (g_OpenALState.loaded) {
|
|
return g_OpenALState.module != NULL;
|
|
}
|
|
|
|
memset(&g_OpenALApi, 0, sizeof(g_OpenALApi));
|
|
g_OpenALState.loaded = true;
|
|
g_OpenALState.ready = false;
|
|
g_OpenALState.moduleOwned = false;
|
|
g_OpenALState.module = NULL;
|
|
g_OpenALState.device = NULL;
|
|
g_OpenALState.context = NULL;
|
|
g_OpenALState.source = 0;
|
|
g_OpenALState.buffer = 0;
|
|
resetStreamData();
|
|
g_OpenALState.activeSources.clear();
|
|
g_OpenALState.cachedBuffers.clear();
|
|
|
|
HMODULE module = GetModuleHandleA("soft_oal.dll");
|
|
if (module == NULL) {
|
|
module = LoadLibraryA("soft_oal.dll");
|
|
if (module) {
|
|
g_OpenALState.moduleOwned = true;
|
|
}
|
|
}
|
|
|
|
if (module == NULL) {
|
|
return false;
|
|
}
|
|
|
|
g_OpenALState.module = module;
|
|
|
|
bool resolved =
|
|
resolveProc(module, "alGenBuffers", g_OpenALApi.alGenBuffers) &&
|
|
resolveProc(module, "alDeleteBuffers", g_OpenALApi.alDeleteBuffers) &&
|
|
resolveProc(module, "alBufferData", g_OpenALApi.alBufferData) &&
|
|
resolveProc(module, "alGenSources", g_OpenALApi.alGenSources) &&
|
|
resolveProc(module, "alDeleteSources", g_OpenALApi.alDeleteSources) &&
|
|
resolveProc(module, "alSourcei", g_OpenALApi.alSourcei) &&
|
|
resolveProc(module, "alSourcef", g_OpenALApi.alSourcef) &&
|
|
resolveProc(module, "alSource3f", g_OpenALApi.alSource3f) &&
|
|
resolveProc(module, "alSourcePlay", g_OpenALApi.alSourcePlay) &&
|
|
resolveProc(module, "alSourcePause", g_OpenALApi.alSourcePause) &&
|
|
resolveProc(module, "alSourceStop", g_OpenALApi.alSourceStop) &&
|
|
resolveProc(module, "alGetSourcei", g_OpenALApi.alGetSourcei) &&
|
|
resolveProc(module, "alListener3f", g_OpenALApi.alListener3f) &&
|
|
resolveProc(module, "alListenerfv", g_OpenALApi.alListenerfv) &&
|
|
resolveProc(module, "alDistanceModel", g_OpenALApi.alDistanceModel) &&
|
|
resolveProc(module, "alGetError", g_OpenALApi.alGetError) &&
|
|
resolveProc(module, "alcOpenDevice", g_OpenALApi.alcOpenDevice) &&
|
|
resolveProc(module, "alcCloseDevice", g_OpenALApi.alcCloseDevice) &&
|
|
resolveProc(module, "alcCreateContext", g_OpenALApi.alcCreateContext) &&
|
|
resolveProc(module, "alcDestroyContext", g_OpenALApi.alcDestroyContext) &&
|
|
resolveProc(module, "alcMakeContextCurrent",
|
|
g_OpenALApi.alcMakeContextCurrent) &&
|
|
resolveProc(module, "alcGetError", g_OpenALApi.alcGetError) &&
|
|
resolveProc(module, "alcGetString", g_OpenALApi.alcGetString);
|
|
|
|
if (!resolved) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static ALCdevice *tryDevice(const ALCchar *deviceName) {
|
|
ALCdevice *device = g_OpenALApi.alcOpenDevice(deviceName);
|
|
return device;
|
|
}
|
|
|
|
static bool initAudio() {
|
|
if (g_OpenALState.ready) {
|
|
return true;
|
|
}
|
|
if (!loadApi()) {
|
|
return false;
|
|
}
|
|
|
|
g_OpenALState.device = tryDevice(NULL);
|
|
if (g_OpenALState.device == NULL) {
|
|
const char *fallbackDevices[] = {"OpenAL Soft", "Generic Software",
|
|
"Generic Hardware"};
|
|
for (unsigned int i = 0;
|
|
i < sizeof(fallbackDevices) / sizeof(fallbackDevices[0]); ++i) {
|
|
g_OpenALState.device = tryDevice(fallbackDevices[i]);
|
|
if (g_OpenALState.device) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (g_OpenALState.device == NULL) {
|
|
return false;
|
|
}
|
|
|
|
g_OpenALState.context =
|
|
g_OpenALApi.alcCreateContext(g_OpenALState.device, NULL);
|
|
if (g_OpenALState.context == NULL) {
|
|
g_OpenALApi.alcCloseDevice(g_OpenALState.device);
|
|
g_OpenALState.device = NULL;
|
|
return false;
|
|
}
|
|
|
|
if (!g_OpenALApi.alcMakeContextCurrent(g_OpenALState.context)) {
|
|
g_OpenALApi.alcDestroyContext(g_OpenALState.context);
|
|
g_OpenALState.context = NULL;
|
|
g_OpenALApi.alcCloseDevice(g_OpenALState.device);
|
|
g_OpenALState.device = NULL;
|
|
return false;
|
|
}
|
|
|
|
g_OpenALApi.alGenSources(1, &g_OpenALState.source);
|
|
if (g_OpenALApi.alGetError() != AL_NO_ERROR || g_OpenALState.source == 0) {
|
|
g_OpenALApi.alcMakeContextCurrent(NULL);
|
|
g_OpenALApi.alcDestroyContext(g_OpenALState.context);
|
|
g_OpenALState.context = NULL;
|
|
g_OpenALApi.alcCloseDevice(g_OpenALState.device);
|
|
g_OpenALState.device = NULL;
|
|
return false;
|
|
}
|
|
|
|
g_OpenALApi.alGenBuffers(1, &g_OpenALState.buffer);
|
|
if (g_OpenALApi.alGetError() != AL_NO_ERROR || g_OpenALState.buffer == 0) {
|
|
g_OpenALApi.alDeleteSources(1, &g_OpenALState.source);
|
|
g_OpenALState.source = 0;
|
|
g_OpenALApi.alcMakeContextCurrent(NULL);
|
|
g_OpenALApi.alcDestroyContext(g_OpenALState.context);
|
|
g_OpenALState.context = NULL;
|
|
g_OpenALApi.alcCloseDevice(g_OpenALState.device);
|
|
g_OpenALState.device = NULL;
|
|
return false;
|
|
}
|
|
|
|
g_OpenALApi.alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);
|
|
g_OpenALState.ready = true;
|
|
return true;
|
|
}
|
|
|
|
static void shutdownAudio() {
|
|
if (!g_OpenALState.loaded) {
|
|
return;
|
|
}
|
|
|
|
if (g_OpenALState.ready) {
|
|
for (size_t i = 0; i < g_OpenALState.activeSources.size(); ++i) {
|
|
ALuint source = g_OpenALState.activeSources[i];
|
|
g_OpenALApi.alSourceStop(source);
|
|
g_OpenALApi.alDeleteSources(1, &source);
|
|
}
|
|
g_OpenALState.activeSources.clear();
|
|
|
|
for (std::map<std::string, ALuint>::iterator it =
|
|
g_OpenALState.cachedBuffers.begin();
|
|
it != g_OpenALState.cachedBuffers.end(); ++it) {
|
|
ALuint buffer = it->second;
|
|
g_OpenALApi.alDeleteBuffers(1, &buffer);
|
|
}
|
|
g_OpenALState.cachedBuffers.clear();
|
|
|
|
if (g_OpenALState.source) {
|
|
g_OpenALApi.alSourceStop(g_OpenALState.source);
|
|
g_OpenALApi.alSourcei(g_OpenALState.source, AL_BUFFER, 0);
|
|
}
|
|
if (g_OpenALState.buffer) {
|
|
g_OpenALApi.alDeleteBuffers(1, &g_OpenALState.buffer);
|
|
g_OpenALState.buffer = 0;
|
|
}
|
|
if (g_OpenALState.source) {
|
|
g_OpenALApi.alDeleteSources(1, &g_OpenALState.source);
|
|
g_OpenALState.source = 0;
|
|
}
|
|
if (g_OpenALState.context) {
|
|
g_OpenALApi.alcMakeContextCurrent(NULL);
|
|
g_OpenALApi.alcDestroyContext(g_OpenALState.context);
|
|
g_OpenALState.context = NULL;
|
|
}
|
|
if (g_OpenALState.device) {
|
|
g_OpenALApi.alcCloseDevice(g_OpenALState.device);
|
|
g_OpenALState.device = NULL;
|
|
}
|
|
}
|
|
|
|
if (g_OpenALState.moduleOwned && g_OpenALState.module) {
|
|
FreeLibrary(g_OpenALState.module);
|
|
}
|
|
g_OpenALState.loaded = false;
|
|
g_OpenALState.ready = false;
|
|
g_OpenALState.moduleOwned = false;
|
|
g_OpenALState.module = NULL;
|
|
resetStreamData();
|
|
memset(&g_OpenALApi, 0, sizeof(g_OpenALApi));
|
|
}
|
|
|
|
static void normalizeSlashes(char *path) {
|
|
for (char *p = path; *p; ++p) {
|
|
if (*p == '\\') {
|
|
*p = '/';
|
|
}
|
|
}
|
|
}
|
|
|
|
static void addUniquePathCandidate(std::vector<std::string> &candidates,
|
|
const std::string &path) {
|
|
if (path.empty()) {
|
|
return;
|
|
}
|
|
for (size_t i = 0; i < candidates.size(); ++i) {
|
|
if (candidates[i] == path) {
|
|
return;
|
|
}
|
|
}
|
|
candidates.push_back(path);
|
|
}
|
|
|
|
static void addSourcesSoundPathCandidates(std::vector<std::string> &candidates,
|
|
const std::string &relativePath) {
|
|
addUniquePathCandidate(candidates, "sources/sounds/" + relativePath);
|
|
addUniquePathCandidate(candidates, "../sources/sounds/" + relativePath);
|
|
addUniquePathCandidate(candidates, "../../sources/sounds/" + relativePath);
|
|
addUniquePathCandidate(candidates, "../../../sources/sounds/" + relativePath);
|
|
}
|
|
|
|
static void addSourcesSoundRootCandidates(std::vector<std::string> &roots,
|
|
const std::string &basePath) {
|
|
addUniquePathCandidate(roots, basePath + "sources/sounds/");
|
|
addUniquePathCandidate(roots, basePath + "../sources/sounds/");
|
|
addUniquePathCandidate(roots, basePath + "../../sources/sounds/");
|
|
addUniquePathCandidate(roots, basePath + "../../../sources/sounds/");
|
|
}
|
|
|
|
static float g_lateralScale = 0.10f;
|
|
|
|
static void transformWorldPos(float x, float y, float z, float &xOut,
|
|
float &yOut, float &zOut) {
|
|
xOut = x * g_lateralScale;
|
|
yOut = y;
|
|
zOut = z;
|
|
}
|
|
|
|
static void buildOrientation(float frontX, float frontY, float frontZ,
|
|
float bOrientation[6]) {
|
|
float ox = -frontX * g_lateralScale;
|
|
float oy = frontY;
|
|
float oz = frontZ;
|
|
float lenSq = ox * ox + oy * oy + oz * oz;
|
|
if (lenSq > 0.000001f) {
|
|
float invLen = 1.0f / sqrtf(lenSq);
|
|
ox *= invLen;
|
|
oy *= invLen;
|
|
oz *= invLen;
|
|
} else {
|
|
ox = 0.0f;
|
|
oy = 0.0f;
|
|
oz = 1.0f;
|
|
}
|
|
bOrientation[0] = ox;
|
|
bOrientation[1] = oy;
|
|
bOrientation[2] = oz;
|
|
bOrientation[3] = 0.0f;
|
|
bOrientation[4] = 1.0f;
|
|
bOrientation[5] = 0.0f;
|
|
}
|
|
|
|
static bool tryDecodeVorbis(const char *fileName, std::vector<short> &pcm,
|
|
int &channels, int &sampleRate) {
|
|
if (_access(fileName, 0) != 0) {
|
|
return false;
|
|
}
|
|
|
|
short *output = NULL;
|
|
int sampleCount =
|
|
stb_vorbis_decode_filename(fileName, &channels, &sampleRate, &output);
|
|
if (sampleCount <= 0 || output == NULL) {
|
|
return false;
|
|
}
|
|
|
|
size_t totalSamples =
|
|
static_cast<size_t>(sampleCount) * static_cast<size_t>(channels);
|
|
pcm.assign(output, output + totalSamples);
|
|
free(output);
|
|
return true;
|
|
}
|
|
|
|
static bool decodeStreamVorbis(const char *requestedPath) {
|
|
std::vector<std::string> candidates;
|
|
|
|
char normalized[512];
|
|
strncpy(normalized, requestedPath, sizeof(normalized) - 1);
|
|
normalized[sizeof(normalized) - 1] = 0;
|
|
normalizeSlashes(normalized);
|
|
|
|
std::string normalizedPath = normalized;
|
|
|
|
std::string maybeOgg = normalizedPath;
|
|
size_t extPos = maybeOgg.rfind('.');
|
|
if (extPos != std::string::npos) {
|
|
maybeOgg = maybeOgg.substr(0, extPos) + ".ogg";
|
|
}
|
|
|
|
addSourcesSoundPathCandidates(candidates, normalizedPath);
|
|
if (maybeOgg != normalizedPath) {
|
|
addSourcesSoundPathCandidates(candidates, maybeOgg);
|
|
}
|
|
|
|
addUniquePathCandidate(candidates, requestedPath);
|
|
addUniquePathCandidate(candidates, normalizedPath);
|
|
addUniquePathCandidate(candidates, maybeOgg);
|
|
|
|
char cwd[MAX_PATH];
|
|
cwd[0] = 0;
|
|
if (GetCurrentDirectoryA(sizeof(cwd), cwd) > 0) {
|
|
std::string cwdPath = cwd;
|
|
for (size_t i = 0; i < cwdPath.size(); ++i) {
|
|
if (cwdPath[i] == '\\') {
|
|
cwdPath[i] = '/';
|
|
}
|
|
}
|
|
if (!cwdPath.empty() && cwdPath[cwdPath.size() - 1] != '/') {
|
|
cwdPath += "/";
|
|
}
|
|
addSourcesSoundPathCandidates(candidates, cwdPath + normalizedPath);
|
|
if (maybeOgg != normalizedPath) {
|
|
addSourcesSoundPathCandidates(candidates, cwdPath + maybeOgg);
|
|
}
|
|
addUniquePathCandidate(candidates, cwdPath + normalizedPath);
|
|
addUniquePathCandidate(candidates, cwdPath + maybeOgg);
|
|
addUniquePathCandidate(candidates,
|
|
cwdPath + "Minecraft.Client/" + normalizedPath);
|
|
addUniquePathCandidate(candidates,
|
|
cwdPath + "x64/Release/" + normalizedPath);
|
|
addUniquePathCandidate(candidates,
|
|
cwdPath + "../x64/Release/" + normalizedPath);
|
|
addUniquePathCandidate(candidates,
|
|
cwdPath + "../../x64/Release/" + normalizedPath);
|
|
}
|
|
|
|
char modulePath[MAX_PATH];
|
|
modulePath[0] = 0;
|
|
if (GetModuleFileNameA(NULL, modulePath, sizeof(modulePath)) > 0) {
|
|
std::string moduleDir = modulePath;
|
|
for (size_t i = 0; i < moduleDir.size(); ++i) {
|
|
if (moduleDir[i] == '\\') {
|
|
moduleDir[i] = '/';
|
|
}
|
|
}
|
|
size_t slashPos = moduleDir.find_last_of('/');
|
|
if (slashPos != std::string::npos) {
|
|
moduleDir = moduleDir.substr(0, slashPos + 1);
|
|
addSourcesSoundPathCandidates(candidates, moduleDir + normalizedPath);
|
|
if (maybeOgg != normalizedPath) {
|
|
addSourcesSoundPathCandidates(candidates, moduleDir + maybeOgg);
|
|
}
|
|
addUniquePathCandidate(candidates, moduleDir + normalizedPath);
|
|
addUniquePathCandidate(candidates, moduleDir + maybeOgg);
|
|
addUniquePathCandidate(candidates,
|
|
moduleDir + "Minecraft.Client/" + normalizedPath);
|
|
addUniquePathCandidate(candidates,
|
|
moduleDir + "x64/Release/" + normalizedPath);
|
|
addUniquePathCandidate(candidates,
|
|
moduleDir + "../x64/Release/" + normalizedPath);
|
|
addUniquePathCandidate(candidates,
|
|
moduleDir + "../../x64/Release/" + normalizedPath);
|
|
}
|
|
}
|
|
|
|
for (unsigned int i = 0; i < candidates.size(); ++i) {
|
|
int channels = 0;
|
|
int sampleRate = 0;
|
|
std::vector<short> pcm;
|
|
if (tryDecodeVorbis(candidates[i].c_str(), pcm, channels, sampleRate)) {
|
|
g_OpenALState.streamPCM.swap(pcm);
|
|
g_OpenALState.streamChannels = channels;
|
|
g_OpenALState.streamSampleRate = sampleRate;
|
|
g_OpenALState.streamLoaded = true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool resolveFilePath(const char *relativeNoExt,
|
|
std::string &resolvedPath) {
|
|
std::vector<std::string> candidates;
|
|
std::vector<std::string> roots;
|
|
|
|
std::string base = relativeNoExt ? relativeNoExt : "";
|
|
if (base.empty()) {
|
|
return false;
|
|
}
|
|
for (size_t i = 0; i < base.size(); ++i) {
|
|
if (base[i] == '\\') {
|
|
base[i] = '/';
|
|
}
|
|
}
|
|
candidates.push_back(base + ".ogg");
|
|
|
|
if (base.find("Minecraft/UI/") == 0) {
|
|
std::string uiName = base.substr(13);
|
|
if (uiName == "back") {
|
|
candidates.push_back("Minecraft/note/btn_Back.ogg");
|
|
} else if (uiName == "craft") {
|
|
candidates.push_back("Minecraft/random/click.ogg");
|
|
} else if (uiName == "craftfail") {
|
|
candidates.push_back("Minecraft/random/drr.ogg");
|
|
} else if (uiName == "focus" || uiName == "press" || uiName == "scroll") {
|
|
candidates.push_back("Minecraft/random/click.ogg");
|
|
}
|
|
}
|
|
|
|
char cwd[MAX_PATH];
|
|
cwd[0] = 0;
|
|
bool hasCwd = GetCurrentDirectoryA(sizeof(cwd), cwd) > 0;
|
|
std::string cwdPath = hasCwd ? std::string(cwd) : std::string();
|
|
for (size_t i = 0; i < cwdPath.size(); ++i) {
|
|
if (cwdPath[i] == '\\') {
|
|
cwdPath[i] = '/';
|
|
}
|
|
}
|
|
if (!cwdPath.empty() && cwdPath[cwdPath.size() - 1] != '/') {
|
|
cwdPath += "/";
|
|
}
|
|
|
|
addSourcesSoundRootCandidates(roots, "");
|
|
addUniquePathCandidate(roots, "");
|
|
|
|
if (hasCwd) {
|
|
addSourcesSoundRootCandidates(roots, cwdPath);
|
|
addUniquePathCandidate(roots, cwdPath);
|
|
addUniquePathCandidate(roots, cwdPath + "x64/Release/");
|
|
addUniquePathCandidate(roots, cwdPath + "../x64/Release/");
|
|
addUniquePathCandidate(roots, cwdPath + "../../x64/Release/");
|
|
addUniquePathCandidate(roots, cwdPath + "Minecraft.Client/");
|
|
}
|
|
|
|
char modulePath[MAX_PATH];
|
|
modulePath[0] = 0;
|
|
if (GetModuleFileNameA(NULL, modulePath, sizeof(modulePath)) > 0) {
|
|
std::string moduleDir = modulePath;
|
|
for (size_t i = 0; i < moduleDir.size(); ++i) {
|
|
if (moduleDir[i] == '\\') {
|
|
moduleDir[i] = '/';
|
|
}
|
|
}
|
|
size_t slashPos = moduleDir.find_last_of('/');
|
|
if (slashPos != std::string::npos) {
|
|
moduleDir = moduleDir.substr(0, slashPos + 1);
|
|
addSourcesSoundRootCandidates(roots, moduleDir);
|
|
addUniquePathCandidate(roots, moduleDir);
|
|
addUniquePathCandidate(roots, moduleDir + "x64/Release/");
|
|
addUniquePathCandidate(roots, moduleDir + "../x64/Release/");
|
|
addUniquePathCandidate(roots, moduleDir + "../../x64/Release/");
|
|
addUniquePathCandidate(roots, moduleDir + "Minecraft.Client/");
|
|
}
|
|
}
|
|
|
|
for (size_t i = 0; i < candidates.size(); ++i) {
|
|
for (size_t j = 0; j < roots.size(); ++j) {
|
|
std::string exactPath = roots[j] + candidates[i];
|
|
if (_access(exactPath.c_str(), 0) == 0) {
|
|
resolvedPath = exactPath;
|
|
return true;
|
|
}
|
|
|
|
std::string wildcard = exactPath;
|
|
if (wildcard.size() >= 4 &&
|
|
wildcard.substr(wildcard.size() - 4) == ".ogg") {
|
|
wildcard.erase(wildcard.size() - 4);
|
|
wildcard += "*.ogg";
|
|
|
|
_finddata_t findData;
|
|
intptr_t findHandle = _findfirst(wildcard.c_str(), &findData);
|
|
if (findHandle != -1) {
|
|
std::vector<std::string> variantFiles;
|
|
size_t slashPos = exactPath.find_last_of("/\\");
|
|
std::string baseFileName = slashPos == std::string::npos
|
|
? exactPath
|
|
: exactPath.substr(slashPos + 1);
|
|
if (baseFileName.size() >= 4 &&
|
|
baseFileName.substr(baseFileName.size() - 4) == ".ogg") {
|
|
baseFileName.erase(baseFileName.size() - 4);
|
|
}
|
|
do {
|
|
if (!(findData.attrib & _A_SUBDIR)) {
|
|
std::string fileName = findData.name;
|
|
if (fileName.size() >= 4 &&
|
|
fileName.substr(fileName.size() - 4) == ".ogg") {
|
|
std::string stem = fileName.substr(0, fileName.size() - 4);
|
|
if (stem == baseFileName) {
|
|
variantFiles.push_back(fileName);
|
|
} else if (stem.size() > baseFileName.size() &&
|
|
stem.compare(0, baseFileName.size(), baseFileName) ==
|
|
0) {
|
|
char suffixLead = stem[baseFileName.size()];
|
|
if ((suffixLead >= '0' && suffixLead <= '9') ||
|
|
suffixLead == '_') {
|
|
variantFiles.push_back(fileName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} while (_findnext(findHandle, &findData) == 0);
|
|
_findclose(findHandle);
|
|
|
|
if (!variantFiles.empty()) {
|
|
size_t slashPos = wildcard.find_last_of("/\\");
|
|
std::string dirPrefix = slashPos == std::string::npos
|
|
? ""
|
|
: wildcard.substr(0, slashPos + 1);
|
|
DWORD tick = GetTickCount();
|
|
size_t selected = (size_t)(tick % (DWORD)variantFiles.size());
|
|
resolvedPath = dirPrefix + variantFiles[selected];
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// also we use ogg now
|
|
static bool loadBuffer(const std::string &filePath, ALuint &bufferOut) {
|
|
std::map<std::string, ALuint>::iterator existing =
|
|
g_OpenALState.cachedBuffers.find(filePath);
|
|
if (existing != g_OpenALState.cachedBuffers.end()) {
|
|
bufferOut = existing->second;
|
|
return true;
|
|
}
|
|
|
|
int channels = 0;
|
|
int sampleRate = 0;
|
|
std::vector<short> pcm;
|
|
if (!tryDecodeVorbis(filePath.c_str(), pcm, channels, sampleRate)) {
|
|
return false;
|
|
}
|
|
|
|
ALenum format = 0;
|
|
if (channels == 1) {
|
|
format = AL_FORMAT_MONO16;
|
|
} else if (channels == 2) {
|
|
format = AL_FORMAT_STEREO16;
|
|
}
|
|
if (format == 0 || pcm.empty()) {
|
|
return false;
|
|
}
|
|
|
|
ALuint buffer = 0;
|
|
g_OpenALApi.alGenBuffers(1, &buffer);
|
|
if (g_OpenALApi.alGetError() != AL_NO_ERROR || buffer == 0) {
|
|
return false;
|
|
}
|
|
|
|
g_OpenALApi.alBufferData(buffer, format, &pcm[0],
|
|
(ALsizei)(pcm.size() * sizeof(short)), sampleRate);
|
|
if (g_OpenALApi.alGetError() != AL_NO_ERROR) {
|
|
g_OpenALApi.alDeleteBuffers(1, &buffer);
|
|
return false;
|
|
}
|
|
|
|
g_OpenALState.cachedBuffers[filePath] = buffer;
|
|
bufferOut = buffer;
|
|
return true;
|
|
}
|
|
|
|
static bool playOneShot(const char *relativeNoExt, bool is3D, float x, float y,
|
|
float z, float volume, float pitch) {
|
|
if (!initAudio()) {
|
|
return false;
|
|
}
|
|
|
|
cleanupSources();
|
|
|
|
std::string filePath;
|
|
if (!resolveFilePath(relativeNoExt, filePath)) {
|
|
return false;
|
|
}
|
|
|
|
ALuint buffer = 0;
|
|
if (!loadBuffer(filePath, buffer)) {
|
|
return false;
|
|
}
|
|
|
|
ALuint source = 0;
|
|
g_OpenALApi.alGenSources(1, &source);
|
|
if (g_OpenALApi.alGetError() != AL_NO_ERROR || source == 0) {
|
|
return false;
|
|
}
|
|
|
|
g_OpenALApi.alSourcei(source, AL_BUFFER, buffer);
|
|
g_OpenALApi.alSourcef(source, AL_PITCH, pitch);
|
|
g_OpenALApi.alSourcef(source, AL_GAIN, volume);
|
|
if (is3D) {
|
|
float sourceX, sourceY, sourceZ;
|
|
transformWorldPos(x, y, z, sourceX, sourceY, sourceZ);
|
|
g_OpenALApi.alSource3f(source, AL_POSITION, sourceX, sourceY, sourceZ);
|
|
g_OpenALApi.alSourcef(source, AL_REFERENCE_DISTANCE, 16.0f);
|
|
} else {
|
|
g_OpenALApi.alSource3f(source, AL_POSITION, 0.0f, 0.0f, 0.0f);
|
|
g_OpenALApi.alSourcef(source, AL_REFERENCE_DISTANCE, 1.0f);
|
|
}
|
|
g_OpenALApi.alSourcePlay(source);
|
|
|
|
if (g_OpenALApi.alGetError() != AL_NO_ERROR) {
|
|
g_OpenALApi.alDeleteSources(1, &source);
|
|
return false;
|
|
}
|
|
|
|
g_OpenALState.activeSources.push_back(source);
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
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",
|
|
#endif
|
|
"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
|
|
//
|
|
/////////////////////////////////////////////
|
|
#ifndef _WINDOWS64
|
|
void AILCALL ErrorCallback(S64 i_Id, char const *i_Details) {
|
|
char *pchLastError = AIL_last_error();
|
|
|
|
if (pchLastError[0] != 0) {
|
|
app.DebugPrintf("\rErrorCallback Error Category: %s\n", pchLastError);
|
|
}
|
|
|
|
if (i_Details) {
|
|
app.DebugPrintf("ErrorCallback - Details: %s\n", i_Details);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#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 _WINDOWS64
|
|
initAudio();
|
|
return;
|
|
#else
|
|
#ifdef __DISABLE_MILES__
|
|
return;
|
|
#endif
|
|
#ifdef __ORBIS__
|
|
C4JThread::PushAffinityAllCores();
|
|
#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,
|
|
#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.
|
|
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);
|
|
|
|
// 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.
|
|
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();
|
|
#endif
|
|
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();
|
|
#endif
|
|
app.DebugPrintf("---SoundEngine::init - AIL_startup_event_system failed\n");
|
|
return;
|
|
}
|
|
char szBankName[255];
|
|
#if defined __PS3__
|
|
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);
|
|
}
|
|
|
|
#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();
|
|
#endif
|
|
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__
|
|
// 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.
|
|
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
|
|
#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() {
|
|
#ifdef __PSVITA__
|
|
// 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
|
|
|
|
#ifdef _WINDOWS64
|
|
if (initAudio()) {
|
|
cleanupSources();
|
|
if (m_validListenerCount == 1) {
|
|
for (int i = 0; i < MAX_LOCAL_PLAYERS; i++) {
|
|
if (m_ListenerA[i].bValid) {
|
|
float listenerX, listenerY, listenerZ;
|
|
transformWorldPos(
|
|
m_ListenerA[i].vPosition.x, m_ListenerA[i].vPosition.y,
|
|
m_ListenerA[i].vPosition.z, listenerX, listenerY, listenerZ);
|
|
g_OpenALApi.alListener3f(AL_POSITION, listenerX, listenerY,
|
|
listenerZ);
|
|
float bOrientation[6];
|
|
buildOrientation(m_ListenerA[i].vOrientFront.x,
|
|
m_ListenerA[i].vOrientFront.y,
|
|
m_ListenerA[i].vOrientFront.z, bOrientation);
|
|
g_OpenALApi.alListenerfv(AL_ORIENTATION, bOrientation);
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
g_OpenALApi.alListener3f(AL_POSITION, 0.0f, 0.0f, 0.0f);
|
|
float bOrientation[6] = {0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f};
|
|
g_OpenALApi.alListenerfv(AL_ORIENTATION, bOrientation);
|
|
}
|
|
}
|
|
#else
|
|
if (m_hDriver == 0 || m_hBank == NULL) {
|
|
return;
|
|
}
|
|
|
|
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;
|
|
}
|
|
if (game_data->volume > 1) {
|
|
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) {
|
|
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;
|
|
case eSoundType_FIREWORKS_BLAST:
|
|
case eSoundType_FIREWORKS_BLAST_FAR:
|
|
case eSoundType_FIREWORKS_LARGE_BLAST:
|
|
case eSoundType_FIREWORKS_LARGE_BLAST_FAR:
|
|
distanceScaler = 100.0f;
|
|
break;
|
|
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) {
|
|
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) {
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
AIL_complete_event_queue_processing();
|
|
#endif
|
|
}
|
|
|
|
// #define DISTORTION_TEST
|
|
#ifdef DISTORTION_TEST
|
|
static float fVal = 0.0f;
|
|
#endif
|
|
/////////////////////////////////////////////
|
|
//
|
|
// tick
|
|
//
|
|
/////////////////////////////////////////////
|
|
|
|
#ifdef __PSVITA__
|
|
static S32 running = AIL_ms_count();
|
|
#endif
|
|
|
|
void SoundEngine::tick(shared_ptr<Mob> *players, float a) {
|
|
ConsoleSoundEngine::tick();
|
|
#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;
|
|
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;
|
|
|
|
#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_hBank = NULL;
|
|
m_hDriver = 0;
|
|
m_hStream = 0;
|
|
m_openStreamThread = NULL;
|
|
m_StreamState = eMusicStreamState_Idle;
|
|
m_iMusicDelay = 0;
|
|
m_validListenerCount = 0;
|
|
m_MasterMusicVolume = 1.0f;
|
|
m_MasterEffectsVolume = 1.0f;
|
|
m_bSystemMusicPlaying = false;
|
|
|
|
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 _WINDOWS64
|
|
shutdownAudio();
|
|
#endif
|
|
}
|
|
|
|
#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
|
|
if(CurrentSoundsPlaying[iSound+eSFX_MAX]>MAX_SAME_SOUNDS_PLAYING)
|
|
{
|
|
// 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);
|
|
|
|
#ifdef _WINDOWS64
|
|
char szOpenALName[256];
|
|
strcpy(szOpenALName, "Minecraft/");
|
|
strcat(szOpenALName, SoundName);
|
|
if (playOneShot(szOpenALName, true, x, y, z, volume * m_MasterEffectsVolume,
|
|
pitch)) {
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
#ifndef _WINDOWS64
|
|
if (m_hDriver == 0 || m_hBank == NULL) {
|
|
return;
|
|
}
|
|
|
|
// 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);
|
|
#endif
|
|
}
|
|
|
|
/////////////////////////////////////////////
|
|
//
|
|
// playUI
|
|
//
|
|
/////////////////////////////////////////////
|
|
void SoundEngine::playUI(int iSound, float volume, float pitch) {
|
|
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);
|
|
|
|
#ifdef _WINDOWS64
|
|
char szOpenALName[256];
|
|
if (iSound >= eSFX_MAX) {
|
|
strcpy(szOpenALName, "Minecraft/");
|
|
strcat(szOpenALName, SoundName);
|
|
} else {
|
|
strcpy(szOpenALName, "Minecraft/UI/");
|
|
strcat(szOpenALName, SoundName);
|
|
}
|
|
if (playOneShot(szOpenALName, false, 0.0f, 0.0f, 0.0f,
|
|
volume * m_MasterEffectsVolume, pitch)) {
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
#ifndef _WINDOWS64
|
|
if (m_hDriver == 0 || m_hBank == NULL) {
|
|
return;
|
|
}
|
|
// 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);
|
|
#endif
|
|
}
|
|
|
|
/////////////////////////////////////////////
|
|
//
|
|
// 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++) {
|
|
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 (int i = iStart; i <= iEnd; i++) {
|
|
m_bHeardTrackA[i] = false;
|
|
}
|
|
}
|
|
|
|
// trying to get a track we haven't heard, but not too hard
|
|
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
|
|
//
|
|
/////////////////////////////////////////////
|
|
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) {
|
|
#ifdef __DISABLE_MILES__
|
|
return 0;
|
|
#endif
|
|
SoundEngine *soundEngine = (SoundEngine *)lpParameter;
|
|
|
|
#ifdef _WINDOWS64
|
|
resetStreamData();
|
|
|
|
if (!initAudio()) {
|
|
return 0;
|
|
}
|
|
|
|
decodeStreamVorbis(soundEngine->m_szStreamName);
|
|
return 0;
|
|
#endif
|
|
|
|
#ifndef _WINDOWS64
|
|
soundEngine->m_hStream =
|
|
AIL_open_stream(soundEngine->m_hDriver, soundEngine->m_szStreamName, 0);
|
|
|
|
if (soundEngine->m_hStream == 0) {
|
|
app.DebugPrintf("SoundEngine::OpenStreamThreadProc - Could not open - %s\n",
|
|
soundEngine->m_szStreamName);
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/////////////////////////////////////////////
|
|
//
|
|
// playMusicTick
|
|
//
|
|
/////////////////////////////////////////////
|
|
void SoundEngine::playMusicTick() {
|
|
// 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
|
|
void SoundEngine::playMusicUpdate() {
|
|
// 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()) {
|
|
sprintf(m_szStreamName, "%s/%s", app.GetBDUsrDirPath(m_szMusicPath),
|
|
m_szMusicPath);
|
|
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;
|
|
|
|
#ifdef _XBOX_ONE
|
|
wstring &wstrSoundName = dlcAudioFile->GetSoundName(m_musicID);
|
|
wstring wstrFile = L"TPACK:\\Data\\" + wstrSoundName + L".ogg";
|
|
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);
|
|
|
|
#if defined __PS3__ || defined __ORBIS__ || defined __PSVITA__
|
|
string strFile = "TPACK:/Data/" + string(szName) + ".ogg";
|
|
#else
|
|
string strFile = "TPACK:\\Data\\" + string(szName) + ".ogg";
|
|
#endif
|
|
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, ".ogg");
|
|
}
|
|
} else {
|
|
// 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, ".ogg");
|
|
|
|
// check if this is in the patch data
|
|
sprintf(m_szStreamName, "%s/%s", app.GetBDUsrDirPath(m_szStreamName),
|
|
m_szMusicPath);
|
|
strcat((char *)m_szStreamName, "music/");
|
|
strcat((char *)m_szStreamName, m_szStreamFileA[m_musicID]);
|
|
strcat((char *)m_szStreamName, ".ogg");
|
|
|
|
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, ".ogg");
|
|
}
|
|
|
|
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, ".ogg");
|
|
}
|
|
#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/");
|
|
} 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, ".ogg");
|
|
|
|
#endif
|
|
}
|
|
|
|
// wstring name = m_szStreamFileA[m_musicID];
|
|
// char *SoundName = (char
|
|
// *)ConvertSoundPathToName(name); strcat((char
|
|
// *)szStreamName,SoundName);
|
|
|
|
#ifdef _WINDOWS64
|
|
normalizeSlashes(m_szStreamName);
|
|
#endif
|
|
|
|
app.DebugPrintf("Starting streaming - %s\n", m_szStreamName);
|
|
|
|
// Don't actually open in this thread, as it can block for ~300ms.
|
|
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;
|
|
|
|
#ifdef _WINDOWS64
|
|
bool started = false;
|
|
if (initAudio() && g_OpenALState.streamLoaded && g_OpenALState.source &&
|
|
g_OpenALState.buffer && !g_OpenALState.streamPCM.empty()) {
|
|
ALenum format = 0;
|
|
if (g_OpenALState.streamChannels == 1) {
|
|
format = AL_FORMAT_MONO16;
|
|
} else if (g_OpenALState.streamChannels == 2) {
|
|
format = AL_FORMAT_STEREO16;
|
|
}
|
|
|
|
if (format != 0) {
|
|
g_OpenALApi.alSourceStop(g_OpenALState.source);
|
|
g_OpenALApi.alSourcei(g_OpenALState.source, AL_BUFFER, 0);
|
|
g_OpenALApi.alBufferData(
|
|
g_OpenALState.buffer, format, &g_OpenALState.streamPCM[0],
|
|
(ALsizei)(g_OpenALState.streamPCM.size() * sizeof(short)),
|
|
g_OpenALState.streamSampleRate);
|
|
|
|
if (g_OpenALApi.alGetError() == AL_NO_ERROR) {
|
|
g_OpenALApi.alSourcei(g_OpenALState.source, AL_BUFFER,
|
|
g_OpenALState.buffer);
|
|
g_OpenALApi.alSourcef(g_OpenALState.source, AL_PITCH,
|
|
m_StreamingAudioInfo.pitch);
|
|
g_OpenALApi.alSourcef(g_OpenALState.source, AL_GAIN,
|
|
m_StreamingAudioInfo.volume *
|
|
getMasterMusicVolume());
|
|
if (m_StreamingAudioInfo.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 -
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
fDist =
|
|
sqrtf((fClosestX * fClosestX) + (fClosestY * fClosestY) +
|
|
(fClosestZ * fClosestZ));
|
|
g_OpenALApi.alSource3f(g_OpenALState.source, AL_POSITION, 0.0f,
|
|
0.0f, fDist);
|
|
} else {
|
|
float sourceX, sourceY, sourceZ;
|
|
transformWorldPos(
|
|
m_StreamingAudioInfo.x, m_StreamingAudioInfo.y,
|
|
m_StreamingAudioInfo.z, sourceX, sourceY, sourceZ);
|
|
g_OpenALApi.alSource3f(g_OpenALState.source, AL_POSITION,
|
|
sourceX, sourceY, sourceZ);
|
|
}
|
|
g_OpenALApi.alSourcef(g_OpenALState.source, AL_REFERENCE_DISTANCE,
|
|
64.0f);
|
|
} else {
|
|
g_OpenALApi.alSource3f(g_OpenALState.source, AL_POSITION, 0.0f,
|
|
0.0f, 0.0f);
|
|
g_OpenALApi.alSourcef(g_OpenALState.source, AL_REFERENCE_DISTANCE,
|
|
1.0f);
|
|
}
|
|
g_OpenALApi.alSourcePlay(g_OpenALState.source);
|
|
started = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (started) {
|
|
m_StreamState = eMusicStreamState_Playing;
|
|
} else {
|
|
SetIsPlayingStreamingCDMusic(false);
|
|
SetIsPlayingStreamingGameMusic(false);
|
|
m_StreamState = eMusicStreamState_Idle;
|
|
}
|
|
resetStreamData();
|
|
#else
|
|
HSAMPLE hSample = AIL_stream_sample_handle(m_hStream);
|
|
|
|
// 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
|
|
AIL_set_sample_is_3D(hSample, 0);
|
|
}
|
|
// set the pitch
|
|
app.DebugPrintf("Sample rate:%d\n", AIL_sample_playback_rate(hSample));
|
|
AIL_set_sample_playback_rate_factor(hSample, m_StreamingAudioInfo.pitch);
|
|
// set the volume
|
|
AIL_set_sample_volume_levels(
|
|
hSample, m_StreamingAudioInfo.volume * getMasterMusicVolume(),
|
|
m_StreamingAudioInfo.volume * getMasterMusicVolume());
|
|
|
|
AIL_start_stream(m_hStream);
|
|
|
|
m_StreamState = eMusicStreamState_Playing;
|
|
#endif
|
|
}
|
|
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
|
|
#ifdef _WINDOWS64
|
|
if (initAudio() && g_OpenALState.source) {
|
|
g_OpenALApi.alSourceStop(g_OpenALState.source);
|
|
g_OpenALApi.alSourcei(g_OpenALState.source, AL_BUFFER, 0);
|
|
}
|
|
resetStreamData();
|
|
#else
|
|
AIL_pause_stream(m_hStream, 1);
|
|
AIL_close_stream(m_hStream);
|
|
m_hStream = 0;
|
|
#endif
|
|
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);
|
|
SetIsPlayingNetherMusic(false);
|
|
} else if (!playerInEnd && GetIsPlayingEndMusic()) {
|
|
if (playerInNether) {
|
|
m_StreamState = eMusicStreamState_Stop;
|
|
|
|
// Set the end track
|
|
m_musicID = getMusicID(LevelData::DIMENSION_NETHER);
|
|
SetIsPlayingEndMusic(false);
|
|
SetIsPlayingNetherMusic(true);
|
|
} else {
|
|
m_StreamState = eMusicStreamState_Stop;
|
|
|
|
// Set the end track
|
|
m_musicID = getMusicID(LevelData::DIMENSION_OVERWORLD);
|
|
SetIsPlayingEndMusic(false);
|
|
SetIsPlayingNetherMusic(false);
|
|
}
|
|
} 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();
|
|
#ifdef _WINDOWS64
|
|
if (initAudio() && g_OpenALState.source) {
|
|
g_OpenALApi.alSourcef(g_OpenALState.source, AL_GAIN, fMusicVol);
|
|
}
|
|
#else
|
|
HSAMPLE hSample = AIL_stream_sample_handle(m_hStream);
|
|
// AIL_set_sample_3D_position( hSample, m_StreamingAudioInfo.x,
|
|
// m_StreamingAudioInfo.y, m_StreamingAudioInfo.z );
|
|
AIL_set_sample_volume_levels(hSample, fMusicVol, fMusicVol);
|
|
#endif
|
|
}
|
|
}
|
|
} 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
|
|
#ifdef _WINDOWS64
|
|
if (initAudio() && g_OpenALState.source) {
|
|
fDist = sqrtf((fClosestX * fClosestX) + (fClosestY * fClosestY) +
|
|
(fClosestZ * fClosestZ));
|
|
g_OpenALApi.alSource3f(g_OpenALState.source, AL_POSITION, 0.0f,
|
|
0.0f, fDist);
|
|
}
|
|
#else
|
|
HSAMPLE hSample = AIL_stream_sample_handle(m_hStream);
|
|
fDist = sqrtf((fClosestX * fClosestX) + (fClosestY * fClosestY) +
|
|
(fClosestZ * fClosestZ));
|
|
AIL_set_sample_3D_position(hSample, 0, 0, fDist);
|
|
#endif
|
|
}
|
|
#ifdef _WINDOWS64
|
|
else {
|
|
if (initAudio() && g_OpenALState.source) {
|
|
float sourceX, sourceY, sourceZ;
|
|
transformWorldPos(m_StreamingAudioInfo.x, m_StreamingAudioInfo.y,
|
|
m_StreamingAudioInfo.z, sourceX, sourceY,
|
|
sourceZ);
|
|
g_OpenALApi.alSource3f(g_OpenALState.source, AL_POSITION, sourceX,
|
|
sourceY, sourceZ);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
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
|
|
|
|
#ifdef _WINDOWS64
|
|
if (m_StreamState == eMusicStreamState_Playing && initAudio() &&
|
|
g_OpenALState.source) {
|
|
ALint sourceState = AL_STOPPED;
|
|
g_OpenALApi.alGetSourcei(g_OpenALState.source, AL_SOURCE_STATE,
|
|
&sourceState);
|
|
if (sourceState == AL_STOPPED) {
|
|
g_OpenALApi.alSourcei(g_OpenALState.source, AL_BUFFER, 0);
|
|
SetIsPlayingStreamingCDMusic(false);
|
|
SetIsPlayingStreamingGameMusic(false);
|
|
m_StreamState = eMusicStreamState_Completed;
|
|
}
|
|
}
|
|
#else
|
|
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;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/////////////////////////////////////////////
|
|
//
|
|
// 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
|
|
|
|
#ifndef _WINDOWS64
|
|
F32 AILCALLBACK custom_falloff_function(HSAMPLE S, 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;
|
|
}
|
|
#endif
|