#include "sound_manager.h" #include "game.h" #include "file_system.h" #ifdef YGOPRO_USE_MINIAUDIO #include #ifdef YGOPRO_MINIAUDIO_SUPPORT_OPUS_VORBIS #include #include #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 } }