Files
ygopro/gframe/sound_manager.cpp

363 lines
9.0 KiB
C++

#include "sound_manager.h"
#include "game.h"
#include "file_system.h"
#ifdef YGOPRO_USE_MINIAUDIO
#include <miniaudio.h>
#ifdef YGOPRO_MINIAUDIO_SUPPORT_OPUS_VORBIS
#include <miniaudio_libopus.h>
#include <miniaudio_libvorbis.h>
#endif
#endif
namespace ygo {
#ifdef YGOPRO_USE_MINIAUDIO
struct SoundManager::MiniAudioImpl {
ma_engine engineSound;
bool engineSoundInitialized{};
ma_engine engineMusic;
bool engineMusicInitialized{};
ma_sound soundBGM;
bool soundBGMInitialized{};
#ifdef YGOPRO_MINIAUDIO_SUPPORT_OPUS_VORBIS
ma_resource_manager resourceManager;
bool resourceManagerInitialized{};
#endif
wchar_t currentPlayingMusic[1024]{};
~MiniAudioImpl() {
if(soundBGMInitialized)
ma_sound_uninit(&soundBGM);
if(engineMusicInitialized)
ma_engine_uninit(&engineMusic);
if(engineSoundInitialized)
ma_engine_uninit(&engineSound);
#ifdef YGOPRO_MINIAUDIO_SUPPORT_OPUS_VORBIS
if(resourceManagerInitialized)
ma_resource_manager_uninit(&resourceManager);
#endif
}
};
#endif
SoundManager::~SoundManager() {
#ifdef YGOPRO_USE_MINIAUDIO
delete miniAudio;
#endif
}
SoundManager soundManager;
bool SoundManager::Init() {
#ifdef YGOPRO_USE_AUDIO
bgm_scene = -1;
RefreshBGMList();
rnd.seed(std::random_device()());
#ifdef YGOPRO_USE_MINIAUDIO
if(miniAudio)
return true;
miniAudio = new MiniAudioImpl{};
auto engineConfig = ma_engine_config_init();
#ifdef YGOPRO_MINIAUDIO_SUPPORT_OPUS_VORBIS
ma_decoding_backend_vtable* pCustomBackendVTables[] =
{
ma_decoding_backend_libvorbis,
ma_decoding_backend_libopus
};
auto resourceManagerConfig = ma_resource_manager_config_init();
resourceManagerConfig.ppCustomDecodingBackendVTables = pCustomBackendVTables;
resourceManagerConfig.customDecodingBackendCount = sizeof(pCustomBackendVTables) / sizeof(pCustomBackendVTables[0]);
resourceManagerConfig.pCustomDecodingBackendUserData = NULL;
if(ma_resource_manager_init(&resourceManagerConfig, &miniAudio->resourceManager) != MA_SUCCESS) {
delete miniAudio;
miniAudio = nullptr;
return false;
}
miniAudio->resourceManagerInitialized = true;
engineConfig.pResourceManager = &miniAudio->resourceManager;
#endif
if(ma_engine_init(&engineConfig, &miniAudio->engineSound) != MA_SUCCESS) {
delete miniAudio;
miniAudio = nullptr;
return false;
}
miniAudio->engineSoundInitialized = true;
if(ma_engine_init(&engineConfig, &miniAudio->engineMusic) != MA_SUCCESS) {
delete miniAudio;
miniAudio = nullptr;
return false;
}
miniAudio->engineMusicInitialized = true;
return true;
#endif // YGOPRO_USE_MINIAUDIO
#endif // YGOPRO_USE_AUDIO
return false;
}
void SoundManager::RefreshBGMList() {
#ifdef YGOPRO_USE_AUDIO
for(size_t i = 0; i < sizeof(BGMList) / sizeof(BGMList[0]); ++i)
BGMList[i].clear();
RefreshBGMDir(L"", BGM_DUEL);
RefreshBGMDir(L"duel", BGM_DUEL);
RefreshBGMDir(L"menu", BGM_MENU);
RefreshBGMDir(L"deck", BGM_DECK);
RefreshBGMDir(L"advantage", BGM_ADVANTAGE);
RefreshBGMDir(L"disadvantage", BGM_DISADVANTAGE);
RefreshBGMDir(L"win", BGM_WIN);
RefreshBGMDir(L"lose", BGM_LOSE);
#endif
}
void SoundManager::RefreshBGMDir(std::wstring path, int scene) {
#ifdef YGOPRO_USE_AUDIO
std::wstring search = L"./sound/BGM/" + path;
FileSystem::TraversalDir(search.c_str(), [this, &path, scene](const wchar_t* name, bool isdir) {
if(!isdir && (
IsExtension(name, L".mp3")
#if defined(YGOPRO_MINIAUDIO_SUPPORT_OPUS_VORBIS)
|| IsExtension(name, L".ogg")
#endif
)) {
std::wstring filename = path + L"/" + name;
BGMList[BGM_ALL].push_back(filename);
BGMList[scene].push_back(filename);
}
});
#endif // YGOPRO_USE_AUDIO
}
void SoundManager::PlaySoundEffect(int sound) {
#ifdef YGOPRO_USE_AUDIO
if(!mainGame->chkEnableSound->isChecked())
return;
std::string soundName;
switch(sound) {
case SOUND_SUMMON: {
soundName = "summon";
break;
}
case SOUND_SPECIAL_SUMMON: {
soundName = "specialsummon";
break;
}
case SOUND_ACTIVATE: {
soundName = "activate";
break;
}
case SOUND_SET: {
soundName = "set";
break;
}
case SOUND_FLIP: {
soundName = "flip";
break;
}
case SOUND_REVEAL: {
soundName = "reveal";
break;
}
case SOUND_EQUIP: {
soundName = "equip";
break;
}
case SOUND_DESTROYED: {
soundName = "destroyed";
break;
}
case SOUND_BANISHED: {
soundName = "banished";
break;
}
case SOUND_TOKEN: {
soundName = "token";
break;
}
case SOUND_NEGATE: {
soundName = "negate";
break;
}
case SOUND_ATTACK: {
soundName = "attack";
break;
}
case SOUND_DIRECT_ATTACK: {
soundName = "directattack";
break;
}
case SOUND_DRAW: {
soundName = "draw";
break;
}
case SOUND_SHUFFLE: {
soundName = "shuffle";
break;
}
case SOUND_DAMAGE: {
soundName = "damage";
break;
}
case SOUND_RECOVER: {
soundName = "gainlp";
break;
}
case SOUND_COUNTER_ADD: {
soundName = "addcounter";
break;
}
case SOUND_COUNTER_REMOVE: {
soundName = "removecounter";
break;
}
case SOUND_COIN: {
soundName = "coinflip";
break;
}
case SOUND_DICE: {
soundName = "diceroll";
break;
}
case SOUND_NEXT_TURN: {
soundName = "nextturn";
break;
}
case SOUND_PHASE: {
soundName = "phase";
break;
}
case SOUND_MENU: {
soundName = "menu";
break;
}
case SOUND_BUTTON: {
soundName = "button";
break;
}
case SOUND_INFO: {
soundName = "info";
break;
}
case SOUND_QUESTION: {
soundName = "question";
break;
}
case SOUND_CARD_PICK: {
soundName = "cardpick";
break;
}
case SOUND_CARD_DROP: {
soundName = "carddrop";
break;
}
case SOUND_PLAYER_ENTER: {
soundName = "playerenter";
break;
}
case SOUND_CHAT: {
soundName = "chatmessage";
break;
}
default:
return;
}
std::string soundPath = "./sound/" + soundName + ".wav";
SetSoundVolume(mainGame->gameConf.sound_volume);
#ifdef YGOPRO_USE_MINIAUDIO
if(miniAudio && miniAudio->engineSoundInitialized)
ma_engine_play_sound(&miniAudio->engineSound, soundPath.c_str(), nullptr);
#endif
#endif // YGOPRO_USE_AUDIO
}
void SoundManager::PlayDialogSound(irr::gui::IGUIElement * element) {
#ifdef YGOPRO_USE_AUDIO
if(element == mainGame->wMessage) {
PlaySoundEffect(SOUND_INFO);
} else if(element == mainGame->wQuery) {
PlaySoundEffect(SOUND_QUESTION);
} else if(element == mainGame->wSurrender) {
PlaySoundEffect(SOUND_QUESTION);
} else if(element == mainGame->wOptions) {
PlaySoundEffect(SOUND_QUESTION);
} else if(element == mainGame->wANAttribute) {
PlaySoundEffect(SOUND_QUESTION);
} else if(element == mainGame->wANCard) {
PlaySoundEffect(SOUND_QUESTION);
} else if(element == mainGame->wANNumber) {
PlaySoundEffect(SOUND_QUESTION);
} else if(element == mainGame->wANRace) {
PlaySoundEffect(SOUND_QUESTION);
} else if(element == mainGame->wReplaySave) {
PlaySoundEffect(SOUND_QUESTION);
} else if(element == mainGame->wFTSelect) {
PlaySoundEffect(SOUND_QUESTION);
}
#endif // YGOPRO_USE_AUDIO
}
bool SoundManager::IsPlayingMusic(wchar_t* music) {
#ifdef YGOPRO_USE_MINIAUDIO
if(!miniAudio || !miniAudio->soundBGMInitialized)
return false;
if(music) {
return !mywcsncasecmp(miniAudio->currentPlayingMusic, music, 1024) && ma_sound_is_playing(&miniAudio->soundBGM);
} else {
return miniAudio->currentPlayingMusic[0] && ma_sound_is_playing(&miniAudio->soundBGM);
}
#endif
return false;
}
void SoundManager::PlayMusic(wchar_t* music, bool loop) {
#ifdef YGOPRO_USE_AUDIO
if(!mainGame->chkEnableMusic->isChecked())
return;
if(!IsPlayingMusic(music)) {
StopBGM();
SetMusicVolume(mainGame->gameConf.music_volume);
#ifdef YGOPRO_USE_MINIAUDIO
if(!miniAudio || !miniAudio->engineMusicInitialized || ma_sound_init_from_file_w(&miniAudio->engineMusic, music, MA_SOUND_FLAG_ASYNC | MA_SOUND_FLAG_STREAM, nullptr, nullptr, &miniAudio->soundBGM) != MA_SUCCESS)
return;
miniAudio->soundBGMInitialized = true;
BufferIO::CopyWStr(music, miniAudio->currentPlayingMusic, 1024);
ma_sound_set_looping(&miniAudio->soundBGM, loop);
ma_sound_start(&miniAudio->soundBGM);
#endif
}
#endif
}
void SoundManager::PlayBGM(int scene) {
#ifdef YGOPRO_USE_AUDIO
if(!mainGame->chkEnableMusic->isChecked())
return;
if(!mainGame->chkMusicMode->isChecked())
scene = BGM_ALL;
if(scene != bgm_scene || !IsPlayingMusic()) {
int count = BGMList[scene].size();
if(count <= 0)
return;
bgm_scene = scene;
int bgm = (count > 1) ? std::uniform_int_distribution<>(0, count - 1)(rnd) : 0;
auto name = BGMList[scene][bgm].c_str();
wchar_t BGMName[1024];
myswprintf(BGMName, L"./sound/BGM/%ls", name);
PlayMusic(BGMName, false);
}
#endif
}
void SoundManager::StopBGM() {
#ifdef YGOPRO_USE_MINIAUDIO
if(!miniAudio || !miniAudio->soundBGMInitialized)
return;
memset(miniAudio->currentPlayingMusic, 0, sizeof(miniAudio->currentPlayingMusic));
ma_sound_uninit(&miniAudio->soundBGM);
miniAudio->soundBGMInitialized = false;
#endif
}
void SoundManager::SetSoundVolume(int volume) {
#ifdef YGOPRO_USE_MINIAUDIO
if(miniAudio && miniAudio->engineSoundInitialized)
ma_engine_set_volume(&miniAudio->engineSound, volume / 100.0f);
#endif
}
void SoundManager::SetMusicVolume(int volume) {
#ifdef YGOPRO_USE_MINIAUDIO
if(miniAudio && miniAudio->engineMusicInitialized)
ma_engine_set_volume(&miniAudio->engineMusic, volume / 100.0f);
#endif
}
}