1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 /*
24 * This file is based on WME Lite.
25 * http://dead-code.org/redir.php?target=wmelite
26 * Copyright (c) 2011 Jan Nedoma
27 */
28
29 #include "engines/wintermute/base/sound/base_sound_manager.h"
30 #include "engines/wintermute/base/base_engine.h"
31 #include "engines/wintermute/utils/path_util.h"
32 #include "engines/wintermute/utils/string_util.h"
33 #include "engines/wintermute/base/base_file_manager.h"
34 #include "engines/wintermute/base/gfx/base_renderer.h"
35 #include "engines/wintermute/base/sound/base_sound_buffer.h"
36 #include "engines/wintermute/wintermute.h"
37 #include "common/config-manager.h"
38 #include "audio/mixer.h"
39
40 namespace Wintermute {
41
42 //////////////////////////////////////////////////////////////////////
43 // Construction/Destruction
44 //////////////////////////////////////////////////////////////////////
45
46 //IMPLEMENT_PERSISTENT(BaseSoundMgr, true);
47
48 //////////////////////////////////////////////////////////////////////////
BaseSoundMgr(BaseGame * inGame)49 BaseSoundMgr::BaseSoundMgr(BaseGame *inGame) : BaseClass(inGame) {
50 _soundAvailable = false;
51 _volumeMaster = 255;
52 _volumeMasterPercent = 100;
53 }
54
55
56 //////////////////////////////////////////////////////////////////////////
~BaseSoundMgr()57 BaseSoundMgr::~BaseSoundMgr() {
58 saveSettings();
59 cleanup();
60 }
61
62
63 //////////////////////////////////////////////////////////////////////////
cleanup()64 bool BaseSoundMgr::cleanup() {
65 for (uint32 i = 0; i < _sounds.size(); i++) {
66 delete _sounds[i];
67 }
68 _sounds.clear();
69 return STATUS_OK;
70 }
71
72 //////////////////////////////////////////////////////////////////////////
saveSettings()73 void BaseSoundMgr::saveSettings() {
74 if (_soundAvailable) {
75 ConfMan.setInt("master_volume_percent", _volumeMasterPercent);
76 }
77 }
78
79 //////////////////////////////////////////////////////////////////////////
initialize()80 bool BaseSoundMgr::initialize() {
81 _soundAvailable = false;
82
83 if (!g_system->getMixer()->isReady()) {
84 return STATUS_FAILED;
85 }
86 byte volumeMasterPercent = (ConfMan.hasKey("master_volume_percent") ? ConfMan.getInt("master_volume_percent") : 100);
87 setMasterVolumePercent(volumeMasterPercent);
88 _soundAvailable = true;
89
90 g_engine->syncSoundSettings();
91 return STATUS_OK;
92 }
93
94 //////////////////////////////////////////////////////////////////////////
addSound(const Common::String & filename,Audio::Mixer::SoundType type,bool streamed)95 BaseSoundBuffer *BaseSoundMgr::addSound(const Common::String &filename, Audio::Mixer::SoundType type, bool streamed) {
96 if (!_soundAvailable) {
97 return nullptr;
98 }
99
100 if (filename.empty()) {
101 // At least one game, Bickadoodle, calls playSound with an empty filename, see #6594
102 BaseEngine::LOG(0, "addSound called with empty filename");
103 }
104
105 BaseSoundBuffer *sound;
106
107 Common::String useFilename = filename;
108 useFilename.toLowercase();
109 // try to switch WAV to OGG file (if available)
110 if (useFilename.hasSuffix(".wav")) {
111 Common::String oggFilename = useFilename;
112 oggFilename.erase(oggFilename.size() - 4);
113 oggFilename = oggFilename + ".ogg";
114 if (BaseFileManager::getEngineInstance()->hasFile(oggFilename)) {
115 useFilename = oggFilename;
116 }
117 }
118
119 sound = new BaseSoundBuffer(_gameRef);
120 if (!sound) {
121 return nullptr;
122 }
123
124 sound->setStreaming(streamed);
125 sound->setType(type);
126
127
128 bool res = sound->loadFromFile(useFilename);
129 if (DID_FAIL(res)) {
130 BaseEngine::LOG(res, "Error loading sound '%s'", useFilename.c_str());
131 delete sound;
132 return nullptr;
133 }
134
135 // Make sure the master-volume is applied to the sound.
136 sound->updateVolume();
137
138 // register sound
139 _sounds.push_back(sound);
140
141 return sound;
142
143 return nullptr;
144 }
145
146 //////////////////////////////////////////////////////////////////////////
addSound(BaseSoundBuffer * sound,Audio::Mixer::SoundType type)147 bool BaseSoundMgr::addSound(BaseSoundBuffer *sound, Audio::Mixer::SoundType type) {
148 if (!sound) {
149 return STATUS_FAILED;
150 }
151
152 // Make sure the master-volume is applied to the sound.
153 sound->updateVolume();
154
155 // register sound
156 _sounds.push_back(sound);
157
158 return STATUS_OK;
159 }
160
161 //////////////////////////////////////////////////////////////////////////
removeSound(BaseSoundBuffer * sound)162 bool BaseSoundMgr::removeSound(BaseSoundBuffer *sound) {
163 for (uint32 i = 0; i < _sounds.size(); i++) {
164 if (_sounds[i] == sound) {
165 delete _sounds[i];
166 _sounds.remove_at(i);
167 return STATUS_OK;
168 }
169 }
170
171 return STATUS_FAILED;
172 }
173
174
175 //////////////////////////////////////////////////////////////////////////
setVolume(Audio::Mixer::SoundType type,int volume)176 bool BaseSoundMgr::setVolume(Audio::Mixer::SoundType type, int volume) {
177 if (!_soundAvailable) {
178 return STATUS_OK;
179 }
180
181 switch (type) {
182 case Audio::Mixer::kSFXSoundType:
183 ConfMan.setInt("sfx_volume", volume);
184 break;
185 case Audio::Mixer::kSpeechSoundType:
186 ConfMan.setInt("speech_volume", volume);
187 break;
188 case Audio::Mixer::kMusicSoundType:
189 ConfMan.setInt("music_volume", volume);
190 break;
191 case Audio::Mixer::kPlainSoundType:
192 error("Plain sound type shouldn't be used in WME");
193 break;
194 default:
195 break;
196 }
197 g_engine->syncSoundSettings();
198
199 return STATUS_OK;
200 }
201
202 //////////////////////////////////////////////////////////////////////////
setVolumePercent(Audio::Mixer::SoundType type,byte percent)203 bool BaseSoundMgr::setVolumePercent(Audio::Mixer::SoundType type, byte percent) {
204 return setVolume(type, percent * 255 / 100);
205 }
206
207
208 //////////////////////////////////////////////////////////////////////////
getVolumePercent(Audio::Mixer::SoundType type)209 byte BaseSoundMgr::getVolumePercent(Audio::Mixer::SoundType type) {
210 int volume = 0;
211
212 switch (type) {
213 case Audio::Mixer::kSFXSoundType:
214 case Audio::Mixer::kSpeechSoundType:
215 case Audio::Mixer::kMusicSoundType:
216 volume = g_system->getMixer()->getVolumeForSoundType(type);
217 break;
218 default:
219 error("Sound-type not set");
220 break;
221 }
222
223 return (byte)(volume * 100 / 255);
224 }
225
226
227 //////////////////////////////////////////////////////////////////////////
setMasterVolume(byte value)228 bool BaseSoundMgr::setMasterVolume(byte value) {
229 // This function intentionally doesn't touch _volumeMasterPercent,
230 // as that variable keeps track of what the game actually wanted,
231 // and this gives a close approximation, while letting the game
232 // be none the wiser about round-off-errors. This function should thus
233 // ONLY be called by setMasterVolumePercent.
234 _volumeMaster = value;
235 for (uint32 i = 0; i < _sounds.size(); i++) {
236 _sounds[i]->updateVolume();
237 }
238 return STATUS_OK;
239 }
240
241 //////////////////////////////////////////////////////////////////////////
setMasterVolumePercent(byte percent)242 bool BaseSoundMgr::setMasterVolumePercent(byte percent) {
243 _volumeMasterPercent = percent;
244 setMasterVolume((int)ceil(percent * 255.0 / 100.0));
245 return STATUS_OK;
246 }
247
248
249 //////////////////////////////////////////////////////////////////////////
getMasterVolumePercent()250 byte BaseSoundMgr::getMasterVolumePercent() {
251 return _volumeMasterPercent;
252 }
253
254 //////////////////////////////////////////////////////////////////////////
getMasterVolume()255 byte BaseSoundMgr::getMasterVolume() {
256 return (byte)_volumeMaster;
257 }
258
259
260 //////////////////////////////////////////////////////////////////////////
pauseAll(bool includingMusic)261 bool BaseSoundMgr::pauseAll(bool includingMusic) {
262
263 for (uint32 i = 0; i < _sounds.size(); i++) {
264 if (_sounds[i]->isPlaying() && (_sounds[i]->getType() != Audio::Mixer::kMusicSoundType || includingMusic)) {
265 _sounds[i]->pause();
266 _sounds[i]->setFreezePaused(true);
267 }
268 }
269
270 return STATUS_OK;
271 }
272
273
274 //////////////////////////////////////////////////////////////////////////
resumeAll()275 bool BaseSoundMgr::resumeAll() {
276
277 for (uint32 i = 0; i < _sounds.size(); i++) {
278 if (_sounds[i]->isFreezePaused()) {
279 _sounds[i]->resume();
280 _sounds[i]->setFreezePaused(false);
281 }
282 }
283
284 return STATUS_OK;
285 }
286
287
288 //////////////////////////////////////////////////////////////////////////
posToPan(int x,int y)289 float BaseSoundMgr::posToPan(int x, int y) {
290 float relPos = (float)x / ((float)BaseEngine::getRenderer()->getWidth());
291
292 float minPan = -0.7f;
293 float maxPan = 0.7f;
294
295 return minPan + relPos * (maxPan - minPan);
296 }
297
298 } // End of namespace Wintermute
299