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