Skip to content

Commit cf7cdb2

Browse files
committed
Support more than one audio backend at the same time
Add infrastucture to be able to pick a specific audio backend via text config
1 parent 19c825c commit cf7cdb2

8 files changed

+180
-66
lines changed

gframe/data_handler.cpp

+12-1
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,18 @@ DataHandler::DataHandler() {
134134
LoadZipArchives();
135135
deckManager = std::make_unique<DeckManager>();
136136
gitManager = std::make_unique<RepoManager>();
137-
sounds = std::make_unique<SoundManager>(configs->soundVolume / 100.0, configs->musicVolume / 100.0, configs->enablesound, configs->enablemusic);
137+
auto sound_backend_valid = [wanted_backend=configs->sound_backend]() {
138+
for(const auto& backend : SoundManager::GetSupportedBackends()) {
139+
if(backend == wanted_backend)
140+
return true;
141+
}
142+
return false;
143+
}();
144+
if(!sound_backend_valid) {
145+
auto old = std::exchange(configs->sound_backend, SoundManager::GetDefaultBackend());
146+
epro::print("Wanted {} audio backend but not supported, using {} instead\n", old, configs->sound_backend);
147+
}
148+
sounds = std::make_unique<SoundManager>(configs->soundVolume / 100.0, configs->musicVolume / 100.0, configs->enablesound, configs->enablemusic, configs->sound_backend);
138149
gitManager->ToggleReadOnly(cli_args[REPOS_READ_ONLY].enabled);
139150
gitManager->LoadRepositoriesFromJson(configs->user_configs);
140151
gitManager->LoadRepositoriesFromJson(configs->configs);

gframe/game_config.cpp

+22
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,23 @@ int parseOption<int, ygo::GameConfig::MusicConfig>(std::string& value) {
159159
return std::min(std::max(std::stoi(value), 0), 100);
160160
}
161161

162+
template<>
163+
ygo::SoundManager::BACKEND parseOption<ygo::SoundManager::BACKEND>(std::string& value) {
164+
Utils::ToUpperNoAccentsSelf(value);
165+
if(value == "IRRKLANG")
166+
return SoundManager::IRRKLANG;
167+
else if(value == "SDL")
168+
return SoundManager::SDL;
169+
else if(value == "SFML")
170+
return SoundManager::SFML;
171+
else if(value == "MINIAUDIO")
172+
return SoundManager::MINIAUDIO;
173+
else if(value == "NONE")
174+
return SoundManager::NONE;
175+
else
176+
return SoundManager::GetDefaultBackend();
177+
}
178+
162179
template<>
163180
uint8_t parseOption<uint8_t, ygo::GameConfig::BoolMaybeUndefined>(std::string& value) {
164181
return std::min<uint8_t>(static_cast<uint8_t>(std::stoul(value)), 2);
@@ -233,6 +250,11 @@ std::string serializeOption(const irr::video::E_DRIVER_TYPE& driver) {
233250
}
234251
}
235252

253+
template<>
254+
std::string serializeOption(const ygo::SoundManager::BACKEND& backend) {
255+
return std::string{ ygo::SoundManager::GetBackendName(backend) };
256+
}
257+
236258
bool GameConfig::Load(const epro::path_stringview filename) {
237259
FileStream conf_file{ filename.data(), FileStream::in };
238260
if(conf_file.fail())

gframe/game_config.h

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <nlohmann/json.hpp>
66
#include <EDriverTypes.h>
77
#include "config.h"
8+
#include "sound_manager.h"
89
#include "text_types.h"
910

1011
namespace ygo {

gframe/game_config.inl

+1
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ OPTION_ALIASED(bool, enablesound, enable_sound, true)
9999
OPTION_ALIASED_TAGGED(int, ygo::GameConfig::MusicConfig, musicVolume, music_volume, 20)
100100
OPTION_ALIASED_TAGGED(int, ygo::GameConfig::MusicConfig, soundVolume, sound_volume, 20)
101101
OPTION(bool, loopMusic, true)
102+
OPTION(ygo::SoundManager::BACKEND, sound_backend, ygo::SoundManager::GetDefaultBackend())
102103
OPTION(bool, saveHandTest, true)
103104
OPTION(bool, discordIntegration, true)
104105
OPTION(bool, noClientUpdates, false)

gframe/premake5.lua

+7-9
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,14 @@ local ygopro_config=function(static_core)
7272
links { "CoreAudio.framework", "AudioToolbox.framework", "CoreVideo.framework", "ForceFeedback.framework", "Carbon.framework" }
7373
filter {}
7474
end
75-
if _OPTIONS["sound"] then
76-
if _OPTIONS["sound"]=="irrklang" then
75+
if next(sounds) ~= nil then
76+
if sounds.irrklang then
7777
defines "YGOPRO_USE_IRRKLANG"
7878
_includedirs "../irrKlang/include"
7979
files "SoundBackends/irrklang/**"
8080
filter {}
8181
end
82-
if _OPTIONS["sound"]=="sdl-mixer" then
82+
if sounds["sdl-mixer"] then
8383
defines "YGOPRO_USE_SDL_MIXER"
8484
files "SoundBackends/sdlmixer/**"
8585
filter "system:windows"
@@ -94,7 +94,7 @@ local ygopro_config=function(static_core)
9494
links { "CoreAudio.framework", "AudioToolbox.framework", "CoreVideo.framework", "ForceFeedback.framework", "Carbon.framework" }
9595
filter {}
9696
end
97-
if _OPTIONS["sound"]=="sfml" then
97+
if sounds.sfml then
9898
defines "YGOPRO_USE_SFML"
9999
files "SoundBackends/sfml/**"
100100
_includedirs "../sfAudio/include"
@@ -115,7 +115,7 @@ local ygopro_config=function(static_core)
115115
end
116116
filter {}
117117
end
118-
if _OPTIONS["sound"]=="miniaudio" then
118+
if sounds.miniaudio then
119119
defines "YGOPRO_USE_MINIAUDIO"
120120
files "SoundBackends/miniaudio/**"
121121
filter { "system:ios", "files:**sound_miniaudio.cpp" }
@@ -129,9 +129,7 @@ local ygopro_config=function(static_core)
129129
links { "AVFoundation.framework" }
130130
filter {}
131131
end
132-
if _OPTIONS["sound"] then
133-
files "SoundBackends/sound_threaded_backend.*"
134-
end
132+
files "SoundBackends/sound_threaded_backend.*"
135133
end
136134

137135
filter "system:windows"
@@ -237,7 +235,7 @@ local ygopro_config=function(static_core)
237235
end
238236

239237
include "lzma/."
240-
if _OPTIONS["sound"]=="sfml" then
238+
if sounds.sfml then
241239
include "../sfAudio"
242240
end
243241

gframe/sound_manager.cpp

+46-48
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,62 @@
55
#include "fmt.h"
66
#if defined(YGOPRO_USE_IRRKLANG)
77
#include "SoundBackends/irrklang/sound_irrklang.h"
8-
#define BACKEND SoundIrrklang
9-
#elif defined(YGOPRO_USE_SDL_MIXER)
8+
#endif
9+
#if defined(YGOPRO_USE_SDL_MIXER)
1010
#include "SoundBackends/sdlmixer/sound_sdlmixer.h"
11-
#define BACKEND SoundMixer
12-
#elif defined(YGOPRO_USE_SFML)
11+
#endif
12+
#if defined(YGOPRO_USE_SFML)
1313
#include "SoundBackends/sfml/sound_sfml.h"
14-
#define BACKEND SoundSFML
15-
#elif defined(YGOPRO_USE_MINIAUDIO)
14+
#endif
15+
#if defined(YGOPRO_USE_MINIAUDIO)
1616
#include "SoundBackends/miniaudio/sound_miniaudio.h"
17-
#define BACKEND SoundMiniaudio
1817
#endif
1918

2019
namespace ygo {
21-
SoundManager::SoundManager(double sounds_volume, double music_volume, bool sounds_enabled, bool music_enabled) {
22-
#ifdef BACKEND
23-
epro::print("Using: " STR(BACKEND)" for audio playback.\n");
20+
namespace {
21+
std::unique_ptr<SoundBackend> make_backend(SoundManager::BACKEND backend) {
22+
switch(backend) {
23+
#ifdef YGOPRO_USE_IRRKLANG
24+
case SoundManager::IRRKLANG:
25+
return std::make_unique<SoundIrrklang>();
26+
#endif
27+
#ifdef YGOPRO_USE_SDL_MIXER
28+
case SoundManager::SDL:
29+
return std::make_unique<SoundMixer>();
30+
#endif
31+
#ifdef YGOPRO_USE_SFML
32+
case SoundManager::SFML:
33+
return std::make_unique<SoundSFML>();
34+
#endif
35+
#ifdef YGOPRO_USE_MINIAUDIO
36+
case SoundManager::MINIAUDIO:
37+
return std::make_unique<SoundMiniaudio>();
38+
#endif
39+
default:
40+
epro::print("Backend not compiled in.\n");
41+
[[fallthrough]];
42+
case SoundManager::NONE:
43+
return nullptr;
44+
}
45+
}
46+
}
47+
48+
SoundManager::SoundManager(double sounds_volume, double music_volume, bool sounds_enabled, bool music_enabled, BACKEND backend) {
49+
epro::print("Using: {} for audio playback.\n", backend);
50+
if(backend == NONE) {
51+
soundsEnabled = musicEnabled = false;
52+
return;
53+
}
2454
working_dir = Utils::ToUTF8IfNeeded(Utils::GetWorkingDirectory());
2555
soundsEnabled = sounds_enabled;
2656
musicEnabled = music_enabled;
2757
try {
28-
auto tmp_mixer = std::make_unique<BACKEND>();
58+
auto tmp_mixer = make_backend(backend);
59+
if(!tmp_mixer) {
60+
epro::print("Failed to initialize audio backend:\n");
61+
soundsEnabled = musicEnabled = false;
62+
return;
63+
}
2964
tmp_mixer->SetMusicVolume(music_volume);
3065
tmp_mixer->SetSoundVolume(sounds_volume);
3166
mixer = std::move(tmp_mixer);
@@ -46,19 +81,13 @@ SoundManager::SoundManager(double sounds_volume, double music_volume, bool sound
4681
RefreshBGMList();
4782
RefreshSoundsList();
4883
RefreshChantsList();
49-
#else
50-
epro::print("No audio backend available.\nAudio will be disabled.\n");
51-
soundsEnabled = musicEnabled = false;
52-
return;
53-
#endif // BACKEND
5484
}
5585
bool SoundManager::IsUsable() {
5686
return mixer != nullptr;
5787
}
5888
void SoundManager::RefreshBGMList() {
5989
if(!IsUsable())
6090
return;
61-
#ifdef BACKEND
6291
Utils::MakeDirectory(EPRO_TEXT("./sound/BGM/"));
6392
Utils::MakeDirectory(EPRO_TEXT("./sound/BGM/duel"));
6493
Utils::MakeDirectory(EPRO_TEXT("./sound/BGM/menu"));
@@ -77,12 +106,10 @@ void SoundManager::RefreshBGMList() {
77106
RefreshBGMDir(EPRO_TEXT("disadvantage"), BGM::DISADVANTAGE);
78107
RefreshBGMDir(EPRO_TEXT("win"), BGM::WIN);
79108
RefreshBGMDir(EPRO_TEXT("lose"), BGM::LOSE);
80-
#endif
81109
}
82110
void SoundManager::RefreshSoundsList() {
83111
if(!IsUsable())
84112
return;
85-
#ifdef BACKEND
86113
static constexpr std::pair<SFX, epro::path_stringview> fx[]{
87114
{SUMMON, EPRO_TEXT("./sound/summon.{}"sv)},
88115
{SPECIAL_SUMMON, EPRO_TEXT("./sound/specialsummon.{}"sv)},
@@ -119,23 +146,19 @@ void SoundManager::RefreshSoundsList() {
119146
}
120147
}
121148
}
122-
#endif
123149
}
124150
void SoundManager::RefreshBGMDir(epro::path_stringview path, BGM scene) {
125151
if(!IsUsable())
126152
return;
127-
#ifdef BACKEND
128153
for(auto& file : Utils::FindFiles(epro::format(EPRO_TEXT("./sound/BGM/{}"), path), mixer->GetSupportedMusicExtensions())) {
129154
auto conv = Utils::ToUTF8IfNeeded(epro::format(EPRO_TEXT("{}/{}"), path, file));
130155
BGMList[BGM::ALL].push_back(conv);
131156
BGMList[scene].push_back(std::move(conv));
132157
}
133-
#endif
134158
}
135159
void SoundManager::RefreshChantsList() {
136160
if(!IsUsable())
137161
return;
138-
#ifdef BACKEND
139162
static constexpr std::pair<CHANT, epro::path_stringview> types[]{
140163
{CHANT::SUMMON, EPRO_TEXT("summon"sv)},
141164
{CHANT::ATTACK, EPRO_TEXT("attack"sv)},
@@ -158,23 +181,19 @@ void SoundManager::RefreshChantsList() {
158181
}
159182
}
160183
}
161-
#endif
162184
}
163185
void SoundManager::PlaySoundEffect(SFX sound) {
164186
if(!IsUsable())
165187
return;
166-
#ifdef BACKEND
167188
if(!soundsEnabled) return;
168189
if(sound >= SFX::SFX_TOTAL_SIZE) return;
169190
const auto& soundfile = SFXList[sound];
170191
if(soundfile.empty()) return;
171192
mixer->PlaySound(soundfile);
172-
#endif
173193
}
174194
void SoundManager::PlayBGM(BGM scene, bool loop) {
175195
if(!IsUsable())
176196
return;
177-
#ifdef BACKEND
178197
if(!musicEnabled)
179198
return;
180199
auto& list = BGMList[scene];
@@ -194,80 +213,59 @@ void SoundManager::PlayBGM(BGM scene, bool loop) {
194213
currentlyLooping = loop;
195214
mixer->LoopMusic(loop);
196215
}
197-
#endif
198216
}
199217
bool SoundManager::PlayChant(CHANT chant, uint32_t code) {
200218
if(!IsUsable())
201219
return false;
202-
#ifdef BACKEND
203220
if(!soundsEnabled) return false;
204221
auto key = std::make_pair(chant, code);
205222
auto chant_it = ChantsList.find(key);
206223
if(chant_it == ChantsList.end())
207224
return false;
208225
return mixer->PlaySound(chant_it->second);
209-
#else
210-
return false;
211-
#endif
212226
}
213227
void SoundManager::SetSoundVolume(double volume) {
214228
if(!IsUsable())
215229
return;
216-
#ifdef BACKEND
217230
mixer->SetSoundVolume(volume);
218-
#endif
219231
}
220232
void SoundManager::SetMusicVolume(double volume) {
221233
if(!IsUsable())
222234
return;
223-
#ifdef BACKEND
224235
mixer->SetMusicVolume(volume);
225-
#endif
226236
}
227237
void SoundManager::EnableSounds(bool enable) {
228238
if(!IsUsable())
229239
return;
230-
#ifdef BACKEND
231240
if(!(soundsEnabled = enable))
232241
mixer->StopSounds();
233-
#endif
234242
}
235243
void SoundManager::EnableMusic(bool enable) {
236244
if(!IsUsable())
237245
return;
238-
#ifdef BACKEND
239246
if(!(musicEnabled = enable))
240247
mixer->StopMusic();
241-
#endif
242248
}
243249
void SoundManager::StopSounds() {
244250
if(!IsUsable())
245251
return;
246-
#ifdef BACKEND
247252
mixer->StopSounds();
248-
#endif
249253
}
250254
void SoundManager::StopMusic() {
251255
if(!IsUsable())
252256
return;
253-
#ifdef BACKEND
254257
mixer->StopMusic();
255-
#endif
256258
}
257259
void SoundManager::PauseMusic(bool pause) {
258260
if(!IsUsable())
259261
return;
260-
#ifdef BACKEND
261262
mixer->PauseMusic(pause);
262-
#endif
263263
}
264264

265265
void SoundManager::Tick() {
266266
if(!IsUsable())
267267
return;
268-
#ifdef BACKEND
269268
mixer->Tick();
270-
#endif
271269
}
272270

273271
} // namespace ygo

0 commit comments

Comments
 (0)