Files
ygopro/gframe/sound_manager.cpp
2026-02-27 16:16:08 +08:00

342 lines
8.2 KiB
C++

#include "sound_manager.h"
#include "myfilesystem.h"
#if defined(YGOPRO_USE_MINIAUDIO) && defined(YGOPRO_MINIAUDIO_SUPPORT_OPUS_VORBIS)
#include <miniaudio_libopus.h>
#include <miniaudio_libvorbis.h>
#endif
#ifdef IRRKLANG_STATIC
#include "../ikpmp3/ikpMP3.h"
#endif
namespace ygo {
SoundManager soundManager;
bool SoundManager::Init() {
#ifdef YGOPRO_USE_AUDIO
bgm_scene = -1;
RefreshBGMList();
rnd.seed(std::random_device()());
#ifdef YGOPRO_USE_MINIAUDIO
engineConfig = ma_engine_config_init();
#ifdef YGOPRO_MINIAUDIO_SUPPORT_OPUS_VORBIS
ma_decoding_backend_vtable* pCustomBackendVTables[] =
{
ma_decoding_backend_libvorbis,
ma_decoding_backend_libopus
};
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, &resourceManager) != MA_SUCCESS) {
return false;
}
engineConfig.pResourceManager = &resourceManager;
#endif
if(ma_engine_init(&engineConfig, &engineSound) != MA_SUCCESS || ma_engine_init(&engineConfig, &engineMusic) != MA_SUCCESS) {
return false;
} else {
return true;
}
#endif // YGOPRO_USE_MINIAUDIO
#ifdef YGOPRO_USE_IRRKLANG
engineSound = irrklang::createIrrKlangDevice();
engineMusic = irrklang::createIrrKlangDevice();
if(!engineSound || !engineMusic) {
return false;
} else {
#ifdef IRRKLANG_STATIC
irrklang::ikpMP3Init(engineMusic);
#endif
return true;
}
#endif // YGOPRO_USE_IRRKLANG
#endif // YGOPRO_USE_AUDIO
return false;
}
void SoundManager::RefreshBGMList() {
#ifdef YGOPRO_USE_AUDIO
RefershBGMDir(L"", BGM_DUEL);
RefershBGMDir(L"duel", BGM_DUEL);
RefershBGMDir(L"menu", BGM_MENU);
RefershBGMDir(L"deck", BGM_DECK);
RefershBGMDir(L"advantage", BGM_ADVANTAGE);
RefershBGMDir(L"disadvantage", BGM_DISADVANTAGE);
RefershBGMDir(L"win", BGM_WIN);
RefershBGMDir(L"lose", BGM_LOSE);
#endif
}
void SoundManager::RefershBGMDir(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) || defined(YGOPRO_USE_IRRKLANG)
|| 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
ma_engine_play_sound(&engineSound, soundPath.c_str(), nullptr);
#endif
#ifdef YGOPRO_USE_IRRKLANG
engineSound->play2D(soundPath.c_str());
#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(music) {
return !mywcsncasecmp(currentPlayingMusic, music, 1024) && ma_sound_is_playing(&soundBGM);
} else {
return currentPlayingMusic[0] && ma_sound_is_playing(&soundBGM);
}
#endif
#ifdef YGOPRO_USE_IRRKLANG
if(music) {
char cmusic[1024];
BufferIO::EncodeUTF8(music, cmusic);
return engineMusic->isCurrentlyPlaying(cmusic);
} else {
return soundBGM && !soundBGM->isFinished();
}
#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
BufferIO::CopyWStr(music, currentPlayingMusic, 1024);
ma_sound_init_from_file_w(&engineMusic, music, MA_SOUND_FLAG_ASYNC | MA_SOUND_FLAG_STREAM, nullptr, nullptr, &soundBGM);
ma_sound_set_looping(&soundBGM, loop);
ma_sound_start(&soundBGM);
#endif
#ifdef YGOPRO_USE_IRRKLANG
char cmusic[1024];
BufferIO::EncodeUTF8(music, cmusic);
soundBGM = engineMusic->play2D(cmusic, loop, false, true);
#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(!currentPlayingMusic[0])
return;
memset(currentPlayingMusic, 0, sizeof(currentPlayingMusic));
ma_sound_uninit(&soundBGM);
#endif
#ifdef YGOPRO_USE_IRRKLANG
engineMusic->stopAllSounds();
#endif
}
void SoundManager::SetSoundVolume(double volume) {
#ifdef YGOPRO_USE_MINIAUDIO
ma_engine_set_volume(&engineSound, volume);
#endif
#ifdef YGOPRO_USE_IRRKLANG
engineSound->setSoundVolume(volume);
#endif
}
void SoundManager::SetMusicVolume(double volume) {
#ifdef YGOPRO_USE_MINIAUDIO
ma_engine_set_volume(&engineMusic, volume);
#endif
#ifdef YGOPRO_USE_IRRKLANG
engineMusic->setSoundVolume(volume);
#endif
}
}