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 #include "kyra/sound/sound.h"
25 #include "kyra/resource/resource.h"
26
27 #include "audio/mixer.h"
28 #include "audio/audiostream.h"
29
30 #include "audio/decoders/flac.h"
31 #include "audio/decoders/mp3.h"
32 #include "audio/decoders/raw.h"
33 #include "audio/decoders/voc.h"
34 #include "audio/decoders/vorbis.h"
35
36 namespace Kyra {
37
Sound(KyraEngine_v1 * vm,Audio::Mixer * mixer)38 Sound::Sound(KyraEngine_v1 *vm, Audio::Mixer *mixer)
39 : _vm(vm), _mixer(mixer), _soundChannels(), _musicEnabled(1),
40 _sfxEnabled(true) {
41 }
42
~Sound()43 Sound::~Sound() {
44 }
45
getSfxType() const46 Sound::kType Sound::getSfxType() const {
47 return getMusicType();
48 }
49
isPlaying() const50 bool Sound::isPlaying() const {
51 return false;
52 }
53
isVoicePresent(const char * file) const54 bool Sound::isVoicePresent(const char *file) const {
55 Common::String filename;
56
57 for (int i = 0; _supportedCodecs[i].fileext; ++i) {
58 filename = file;
59 filename += _supportedCodecs[i].fileext;
60
61 if (_vm->resource()->exists(filename.c_str()))
62 return true;
63 }
64
65 return false;
66 }
67
voicePlay(const char * file,Audio::SoundHandle * handle,uint8 volume,uint8 priority,bool isSfx)68 int32 Sound::voicePlay(const char *file, Audio::SoundHandle *handle, uint8 volume, uint8 priority, bool isSfx) {
69 Audio::SeekableAudioStream *audioStream = getVoiceStream(file);
70
71 if (!audioStream) {
72 return 0;
73 }
74
75 int playTime = audioStream->getLength().msecs();
76 playVoiceStream(audioStream, handle, volume, priority, isSfx);
77 return playTime;
78 }
79
getVoiceStream(const char * file) const80 Audio::SeekableAudioStream *Sound::getVoiceStream(const char *file) const {
81 Common::String filename;
82
83 Audio::SeekableAudioStream *audioStream = 0;
84 for (int i = 0; _supportedCodecs[i].fileext; ++i) {
85 filename = file;
86 filename += _supportedCodecs[i].fileext;
87
88 Common::SeekableReadStream *stream = _vm->resource()->createReadStream(filename);
89 if (!stream)
90 continue;
91
92 audioStream = _supportedCodecs[i].streamFunc(stream, DisposeAfterUse::YES);
93 break;
94 }
95
96 if (!audioStream) {
97 warning("Couldn't load sound file '%s'", file);
98 return 0;
99 } else {
100 return audioStream;
101 }
102 }
103
playVoiceStream(Audio::AudioStream * stream,Audio::SoundHandle * handle,uint8 volume,uint8 priority,bool isSfx)104 bool Sound::playVoiceStream(Audio::AudioStream *stream, Audio::SoundHandle *handle, uint8 volume, uint8 priority, bool isSfx) {
105 int h = 0;
106 while (h < kNumChannelHandles && _mixer->isSoundHandleActive(_soundChannels[h].handle))
107 ++h;
108
109 if (h >= kNumChannelHandles) {
110 h = 0;
111 while (h < kNumChannelHandles && _soundChannels[h].priority > priority)
112 ++h;
113 if (h < kNumChannelHandles)
114 voiceStop(&_soundChannels[h].handle);
115 }
116
117 if (h >= kNumChannelHandles) {
118 // When we run out of handles we need to destroy the stream object,
119 // this is to avoid memory leaks in some scenes where too many sfx
120 // are started.
121 // See bug #5886 "LOL-CD: Memory leak in caves level 3".
122 delete stream;
123 return false;
124 }
125
126 _mixer->playStream(isSfx ? Audio::Mixer::kSFXSoundType : Audio::Mixer::kSpeechSoundType, &_soundChannels[h].handle, stream, -1, volume);
127 _soundChannels[h].priority = priority;
128 if (handle)
129 *handle = _soundChannels[h].handle;
130
131 return true;
132 }
133
voiceStop(const Audio::SoundHandle * handle)134 void Sound::voiceStop(const Audio::SoundHandle *handle) {
135 if (!handle) {
136 for (int h = 0; h < kNumChannelHandles; ++h) {
137 if (_mixer->isSoundHandleActive(_soundChannels[h].handle))
138 _mixer->stopHandle(_soundChannels[h].handle);
139 }
140 } else {
141 _mixer->stopHandle(*handle);
142 }
143 }
144
voiceIsPlaying(const Audio::SoundHandle * handle) const145 bool Sound::voiceIsPlaying(const Audio::SoundHandle *handle) const {
146 if (!handle) {
147 for (int h = 0; h < kNumChannelHandles; ++h) {
148 if (_mixer->isSoundHandleActive(_soundChannels[h].handle))
149 return true;
150 }
151 } else {
152 return _mixer->isSoundHandleActive(*handle);
153 }
154
155 return false;
156 }
157
allVoiceChannelsPlaying() const158 bool Sound::allVoiceChannelsPlaying() const {
159 for (int i = 0; i < kNumChannelHandles; ++i)
160 if (!_mixer->isSoundHandleActive(_soundChannels[i].handle))
161 return false;
162 return true;
163 }
164
165 #pragma mark -
166
MixedSoundDriver(KyraEngine_v1 * vm,Audio::Mixer * mixer,Sound * music,Sound * sfx)167 MixedSoundDriver::MixedSoundDriver(KyraEngine_v1 *vm, Audio::Mixer *mixer, Sound *music, Sound *sfx)
168 : Sound(vm, mixer), _music(music), _sfx(sfx) {
169 }
170
~MixedSoundDriver()171 MixedSoundDriver::~MixedSoundDriver() {
172 delete _music;
173 delete _sfx;
174 }
175
getMusicType() const176 Sound::kType MixedSoundDriver::getMusicType() const {
177 return _music->getMusicType();
178 }
179
getSfxType() const180 Sound::kType MixedSoundDriver::getSfxType() const {
181 return _sfx->getSfxType();
182 }
183
init()184 bool MixedSoundDriver::init() {
185 return (_music->init() && _sfx->init());
186 }
187
process()188 void MixedSoundDriver::process() {
189 _music->process();
190 _sfx->process();
191 }
192
updateVolumeSettings()193 void MixedSoundDriver::updateVolumeSettings() {
194 _music->updateVolumeSettings();
195 _sfx->updateVolumeSettings();
196 }
197
initAudioResourceInfo(int set,void * info)198 void MixedSoundDriver::initAudioResourceInfo(int set, void *info) {
199 _music->initAudioResourceInfo(set, info);
200 _sfx->initAudioResourceInfo(set, info);
201 }
202
selectAudioResourceSet(int set)203 void MixedSoundDriver::selectAudioResourceSet(int set) {
204 _music->selectAudioResourceSet(set);
205 _sfx->selectAudioResourceSet(set);
206 }
207
hasSoundFile(uint file) const208 bool MixedSoundDriver::hasSoundFile(uint file) const {
209 return _music->hasSoundFile(file) && _sfx->hasSoundFile(file);
210 }
211
loadSoundFile(uint file)212 void MixedSoundDriver::loadSoundFile(uint file) {
213 _music->loadSoundFile(file);
214 _sfx->loadSoundFile(file);
215 }
216
loadSoundFile(Common::String file)217 void MixedSoundDriver::loadSoundFile(Common::String file) {
218 _music->loadSoundFile(file);
219 _sfx->loadSoundFile(file);
220 }
221
loadSfxFile(Common::String file)222 void MixedSoundDriver::loadSfxFile(Common::String file) {
223 _sfx->loadSoundFile(file);
224 }
225
playTrack(uint8 track)226 void MixedSoundDriver::playTrack(uint8 track) {
227 _music->playTrack(track);
228 }
229
haltTrack()230 void MixedSoundDriver::haltTrack() {
231 _music->haltTrack();
232 }
233
isPlaying() const234 bool MixedSoundDriver::isPlaying() const {
235 return _music->isPlaying() | _sfx->isPlaying();
236 }
237
playSoundEffect(uint16 track,uint8 volume)238 void MixedSoundDriver::playSoundEffect(uint16 track, uint8 volume) {
239 _sfx->playSoundEffect(track, volume);
240 }
241
stopAllSoundEffects()242 void MixedSoundDriver::stopAllSoundEffects() {
243 _sfx->stopAllSoundEffects();
244 }
245
beginFadeOut()246 void MixedSoundDriver::beginFadeOut() {
247 _music->beginFadeOut();
248 }
249
pause(bool paused)250 void MixedSoundDriver::pause(bool paused) {
251 _music->pause(paused);
252 _sfx->pause(paused);
253 }
254
255 #pragma mark -
256
snd_playTheme(int file,int track)257 void KyraEngine_v1::snd_playTheme(int file, int track) {
258 if (_curMusicTheme == file)
259 return;
260
261 _curSfxFile = _curMusicTheme = file;
262 _sound->loadSoundFile(_curMusicTheme);
263
264 // Kyrandia 2 uses a special file for
265 // MIDI sound effects, so we load
266 // this here
267 if (_flags.gameID == GI_KYRA2)
268 _sound->loadSfxFile("K2SFX");
269
270 if (track != -1)
271 _sound->playTrack(track);
272 }
273
snd_playSoundEffect(int track,int volume)274 void KyraEngine_v1::snd_playSoundEffect(int track, int volume) {
275 _sound->playSoundEffect(track, volume);
276 }
277
snd_playWanderScoreViaMap(int command,int restart)278 void KyraEngine_v1::snd_playWanderScoreViaMap(int command, int restart) {
279 if (restart)
280 _lastMusicCommand = -1;
281
282 // no track mapping given
283 // so don't do anything here
284 if (!_trackMap || !_trackMapSize)
285 return;
286
287 //if (!_disableSound) {
288 // XXX
289 //}
290
291 if (_flags.platform == Common::kPlatformDOS || _flags.platform == Common::kPlatformMacintosh) {
292 assert(command * 2 + 1 < _trackMapSize);
293 if (_curMusicTheme != _trackMap[command * 2]) {
294 if (_trackMap[command * 2] != -1 && _trackMap[command * 2] != -2)
295 snd_playTheme(_trackMap[command * 2], -1);
296 }
297
298 if (command != 1) {
299 if (_lastMusicCommand != command) {
300 _sound->haltTrack();
301 _sound->playTrack(_trackMap[command * 2 + 1]);
302 }
303 } else {
304 _sound->beginFadeOut();
305 }
306 } else if (_flags.platform == Common::kPlatformFMTowns || _flags.platform == Common::kPlatformPC98) {
307 if (command == -1) {
308 _sound->haltTrack();
309 } else {
310 assert(command * 2 + 1 < _trackMapSize);
311 if (_trackMap[command * 2] != -2 && command != _lastMusicCommand) {
312 _sound->haltTrack();
313 _sound->playTrack(command);
314 }
315 }
316 } else if (_flags.platform == Common::kPlatformAmiga) {
317 if (_curMusicTheme != 1)
318 snd_playTheme(1, -1);
319
320 assert(command < _trackMapSize);
321 if (_trackMap[_lastMusicCommand] != _trackMap[command])
322 _sound->playTrack(_trackMap[command]);
323 }
324
325 _lastMusicCommand = command;
326 }
327
snd_stopVoice()328 void KyraEngine_v1::snd_stopVoice() {
329 _sound->voiceStop(&_speechHandle);
330 }
331
snd_voiceIsPlaying()332 bool KyraEngine_v1::snd_voiceIsPlaying() {
333 return _sound->voiceIsPlaying(&_speechHandle);
334 }
335
336 // static res
337
338 namespace {
339
340 // A simple wrapper to create VOC streams the way like creating MP3, OGG/Vorbis and FLAC streams.
341 // Possible TODO: Think of making this complete and moving it to sound/voc.cpp ?
makeVOCStream(Common::SeekableReadStream * stream,DisposeAfterUse::Flag disposeAfterUse)342 Audio::SeekableAudioStream *makeVOCStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
343 Audio::SeekableAudioStream *as = Audio::makeVOCStream(stream, Audio::FLAG_UNSIGNED, disposeAfterUse);
344 return as;
345 }
346
347 } // end of anonymous namespace
348
349 const Sound::SpeechCodecs Sound::_supportedCodecs[] = {
350 { ".VOC", makeVOCStream },
351 { "", makeVOCStream },
352
353 #ifdef USE_MAD
354 { ".VO3", Audio::makeMP3Stream },
355 { ".MP3", Audio::makeMP3Stream },
356 #endif // USE_MAD
357
358 #ifdef USE_VORBIS
359 { ".VOG", Audio::makeVorbisStream },
360 { ".OGG", Audio::makeVorbisStream },
361 #endif // USE_VORBIS
362
363 #ifdef USE_FLAC
364 { ".VOF", Audio::makeFLACStream },
365 { ".FLA", Audio::makeFLACStream },
366 #endif // USE_FLAC
367
368 { 0, 0 }
369 };
370
371 } // End of namespace Kyra
372