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  * Based on the original sources
23  *   Faery Tale II -- The Halls of the Dead
24  *   (c) 1993-1996 The Wyrmkeep Entertainment Co.
25  */
26 
27 #include "common/config-manager.h"
28 #include "audio/audiostream.h"
29 #include "audio/mididrv.h"
30 #include "audio/decoders/raw.h"
31 
32 #include "saga2/saga2.h"
33 #include "saga2/audio.h"
34 #include "saga2/fta.h"
35 #include "saga2/shorten.h"
36 #include "saga2/hresmgr.h"
37 #include "saga2/music.h"
38 #include "saga2/annoy.h"
39 #include "saga2/player.h"
40 
41 namespace Saga2 {
42 
43 static const StaticPoint32 VeryFarAway = {32767, 32766};
44 
45 const uint32 fullVolumeDist = 75;
46 const uint32 offVolumeDist = 200;
47 
48 const uint32        baseMusicID     = MKTAG('M', 'I', 'L', 'O'),
49                     goodMusicID     = MKTAG('M', 'I', 'H', 'I'),
50                     soundID         = MKTAG('L', 'O', 'U', 'D'),
51                     loopedID        = MKTAG('L', 'O', 'O', 'P'),
52                     voiceID         = MKTAG('T', 'A', 'L', 'K');
53 
54 extern hResource *soundResFile;          // script resources
55 extern hResource *voiceResFile;          // script resources
56 
57 hResContext *voiceRes, *musicRes, *soundRes, *loopRes, *longRes;
58 
59 bool haveKillerSoundCard(void);
60 void writeConfig(void);
61 void disableBGLoop(bool s = true);
62 void enableBGLoop(void);
63 void audioStressTest(void);
64 extern GameObject *getViewCenterObject(void);
65 void playSoundAt(uint32 s, Location playAt);
66 void playSoundAt(uint32 s, Point32 playAt);
67 bool sayVoiceAt(uint32 s[], Location l);
68 bool sayVoiceAt(uint32 s[], Point32 l);
69 void playLoopAt(uint32 s, Location l);
70 void playLoopAt(uint32 s, Point32 l);
71 
72 bool bufCheckResID(hResContext *hrc, uint32 s);
73 bool hResCheckResID(hResContext *hrc, uint32 s);
74 bool hResCheckResID(hResContext *hrc, uint32 s[]);
75 
76 /* ===================================================================== *
77    ATTENUATOR routines
78  * ===================================================================== */
79 
80 //-----------------------------------------------------------------------
81 //	our distance based volume attenuator
82 
volumeFromDist(Point32 loc,byte maxVol)83 static byte volumeFromDist(Point32 loc, byte maxVol) {
84 	TilePoint tp(loc.x, loc.y, 0);
85 	uint32 dist = tp.quickHDistance();
86 	if (dist < fullVolumeDist) {
87 		return ABS(maxVol);
88 	} else if (dist < offVolumeDist) {
89 		return ABS((int)(maxVol * ((int)((offVolumeDist - fullVolumeDist) - (dist - fullVolumeDist))) / (offVolumeDist - fullVolumeDist)));
90 	}
91 	return 0;
92 }
93 
94 /* ===================================================================== *
95    Module code
96  * ===================================================================== */
97 
98 //-----------------------------------------------------------------------
99 //	after system initialization - startup code
100 
startAudio(void)101 void startAudio(void) {
102 	uint32 musicID = haveKillerSoundCard() ? goodMusicID : baseMusicID;
103 
104 	musicRes = soundResFile->newContext(musicID, "music resource");
105 	if (musicRes == NULL)
106 		error("Musicians on Strike (No music resource context)!\n");
107 
108 	soundRes = soundResFile->newContext(soundID, "sound resource");
109 	if (soundRes == NULL)
110 		error("No sound effect resource context!\n");
111 
112 	longRes = soundResFile->newContext(soundID, "long sound resource");
113 	if (longRes == NULL)
114 		error("No sound effect resource context!\n");
115 
116 	loopRes = soundResFile->newContext(loopedID, "loops resource");
117 	if (loopRes == NULL)
118 		error("No loop effect resource context!\n");
119 
120 	voiceRes = voiceResFile->newContext(voiceID, "voice resource");
121 	if (voiceRes == NULL)
122 		error("Laryngitis Error (No voice resource context)!\n");
123 
124 	g_vm->_audio->initAudioInterface(musicRes);
125 
126 	// kludgy in memory click sounds
127 	g_vm->_audio->_clickSizes[0] = 0;
128 	g_vm->_audio->_clickSizes[1] = soundRes->size(MKTAG('C', 'L', 'K', 1));
129 	g_vm->_audio->_clickSizes[2] = soundRes->size(MKTAG('C', 'L', 'K', 2));
130 	g_vm->_audio->_clickData[0] = NULL;
131 	g_vm->_audio->_clickData[1] = (uint8 *)LoadResource(soundRes, MKTAG('C', 'L', 'K', 1), "Click 1");
132 	g_vm->_audio->_clickData[2] = (uint8 *)LoadResource(soundRes, MKTAG('C', 'L', 'K', 2), "Click 2");
133 }
134 
cleanupAudio()135 void cleanupAudio() {
136 	if (g_vm->_audio) {
137 		delete g_vm->_audio;
138 		g_vm->_audio = nullptr;
139 
140 		delete musicRes;
141 		musicRes = nullptr;
142 
143 		delete soundRes;
144 		soundRes = nullptr;
145 
146 		delete longRes;
147 		longRes = nullptr;
148 
149 		delete loopRes;
150 		loopRes = nullptr;
151 
152 		delete voiceRes;
153 		voiceRes = nullptr;
154 	}
155 }
156 
157 //-----------------------------------------------------------------------
158 // audio event loop
159 
160 
audioEventLoop(void)161 void audioEventLoop(void) {
162 	if (g_vm->_audio->playFlag())
163 		g_vm->_audio->playMe();
164 
165 	audioEnvironmentCheck();
166 }
167 
168 /* ===================================================================== *
169    Assorted code
170  * ===================================================================== */
171 
makeCombatSound(uint8 cs,Location l)172 void makeCombatSound(uint8 cs, Location l) {
173 	playSoundAt(MKTAG('C', 'B', 'T', cs), l);
174 }
175 
makeGruntSound(uint8 cs,Location l)176 void makeGruntSound(uint8 cs, Location l) {
177 	playSoundAt(MKTAG('G', 'N', 'T', cs), l);
178 }
179 
180 
181 //-----------------------------------------------------------------------
182 //	check for higher quality MIDI card
183 
haveKillerSoundCard(void)184 bool haveKillerSoundCard(void) {
185 	MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
186 	MusicType driverType = MidiDriver::getMusicType(dev);
187 
188 	switch (driverType) {
189 	case MT_ADLIB:
190 	case MT_MT32:
191 		return true;
192 
193 	default:
194 		return false;
195 	}
196 }
197 
198 //-----------------------------------------------------------------------
199 // unwritten music toggler
200 
toggleMusic(void)201 void toggleMusic(void) {
202 }
203 
204 /* ===================================================================== *
205    Audio hooks for videos
206  * ===================================================================== */
207 
208 //-----------------------------------------------------------------------
209 //  suspend & resume calls
210 
211 
suspendLoops(void)212 void suspendLoops(void) {
213 	disableBGLoop(false);
214 }
215 
resumeLoops(void)216 void resumeLoops(void) {
217 	if (loopRes)
218 		enableBGLoop();
219 }
220 
suspendMusic(void)221 void suspendMusic(void) {
222 	audioEnvironmentSuspend(true);
223 }
224 
resumeMusic(void)225 void resumeMusic(void) {
226 	if (musicRes)
227 		audioEnvironmentSuspend(false);
228 }
229 
suspendAudio(void)230 void suspendAudio(void) {
231 	if (g_vm->_audio) {
232 		suspendMusic();
233 		suspendLoops();
234 		g_vm->_audio->suspend();
235 	}
236 }
237 
resumeAudio(void)238 void resumeAudio(void) {
239 	if (g_vm->_audio) {
240 		if (soundRes != NULL || voiceRes != NULL) {
241 			g_vm->_audio->resume();
242 			resumeLoops();
243 			resumeMusic();
244 		}
245 	}
246 }
247 
248 //-----------------------------------------------------------------------
249 //  UI volume change hook
250 
volumeChanged(void)251 void volumeChanged(void) {
252 	if (g_vm->_audio->getVolume(kVolSfx))
253 		resumeLoops();
254 	else
255 		suspendLoops();
256 
257 	if (g_vm->_audio->getVolume(kVolMusic)) {
258 		resumeMusic();
259 
260 		g_vm->_audio->_music->syncSoundSettings();
261 	} else
262 		suspendMusic();
263 }
264 
265 /* ===================================================================== *
266    main audio playback routines
267  * ===================================================================== */
268 
translateLocation(Location playAt)269 Point32 translateLocation(Location playAt) {
270 	GameObject *go = getViewCenterObject();
271 	Location cal = Location(go->getWorldLocation(), go->IDParent());
272 
273 	if (playAt.context == cal.context) {
274 		Point32 p = Point32(playAt.u - cal.u, playAt.v - cal.v);
275 		return p;
276 	}
277 	return VeryFarAway;
278 }
279 
280 //-----------------------------------------------------------------------
281 //	MIDI playback
282 
playMusic(uint32 s)283 void playMusic(uint32 s) {
284 	debugC(1, kDebugSound, "playMusic(%s)", tag2strP(s));
285 
286 	if (hResCheckResID(musicRes, s)) {
287 		g_vm->_audio->playMusic(s, 1);
288 	} else
289 		g_vm->_audio->stopMusic();
290 }
291 
292 //-----------------------------------------------------------------------
293 // in memory sfx
294 
playMemSound(uint32 s)295 void playMemSound(uint32 s) {
296 	debugC(1, kDebugSound, "playMemSound(%s)", tag2strP(s));
297 
298 	Audio::AudioStream *aud = Audio::makeRawStream(g_vm->_audio->_clickData[s], g_vm->_audio->_clickSizes[s], 22050, Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN, DisposeAfterUse::NO);
299 
300 	g_system->getMixer()->playStream(Audio::Mixer::kSFXSoundType, &g_vm->_audio->_clickSoundHandle, aud);
301 }
302 
303 //-----------------------------------------------------------------------
304 // on disk sfx
305 
playSound(uint32 s)306 void playSound(uint32 s) {
307 	debugC(1, kDebugSound, "playSound(%s)", tag2strP(s));
308 
309 	if (hResCheckResID(soundRes, s))
310 		g_vm->_audio->queueSound(s, 1, Here);
311 }
312 
313 //-----------------------------------------------------------------------
314 // on disk sfx (x2 buffered)
315 
playLongSound(uint32 s)316 void playLongSound(uint32 s) {
317 	debugC(1, kDebugSound, "playLongSound(%s)", tag2strP(s));
318 
319 	if (hResCheckResID(longRes, s))
320 		g_vm->_audio->queueVoice(s);
321 	else
322 		g_vm->_audio->stopVoice();
323 }
324 
325 //-----------------------------------------------------------------------
326 // on disk voice (x2 buffered)
327 
playVoice(uint32 s)328 void playVoice(uint32 s) {
329 	debugC(1, kDebugSound, "playVoice(%s)", tag2strP(s));
330 
331 	if (hResCheckResID(voiceRes, s)) {
332 		if (s)
333 			g_vm->_audio->queueVoice(s, Here);
334 		else
335 			g_vm->_audio->stopVoice();
336 	}
337 }
338 
339 //-----------------------------------------------------------------------
340 // supplemental interface for speech
341 
sayVoice(uint32 s[])342 bool sayVoice(uint32 s[]) {
343 	debugCN(1, kDebugSound, "sayVoice([%s", tag2strP(s[0]));
344 
345 	for (uint32 i = 1; s[i]; i++)
346 		debugCN(1, kDebugSound, ", %s", tag2strP(s[i]));
347 
348 	debugC(1, kDebugSound, "])");
349 
350 	bool worked = false;
351 
352 	if (hResCheckResID(voiceRes, s)) {
353 		g_vm->_audio->queueVoice(s, Here);
354 		if (g_vm->_audio->talking())
355 			worked = true;
356 	}
357 
358 	return worked;
359 }
360 
361 //-----------------------------------------------------------------------
362 // main loop playback
363 
_playLoop(uint32 s)364 void _playLoop(uint32 s) {
365 	if (s == g_vm->_audio->currentLoop())
366 		return;
367 
368 	g_vm->_audio->stopLoop();
369 
370 	if (!s)
371 		return;
372 
373 	g_vm->_audio->playLoop(s, 0, Here);
374 }
375 
376 //-----------------------------------------------------------------------
377 // loop playback that disables background loops
378 
playLoop(uint32 s)379 void playLoop(uint32 s) {
380 	if (s) {
381 	} else {
382 		_playLoop(s);
383 	}
384 }
385 
386 
387 //-----------------------------------------------------------------------
388 // attenuated sound players
389 
playSoundAt(uint32 s,Point32 p)390 void playSoundAt(uint32 s, Point32 p) {
391 	debugC(1, kDebugSound, "playSoundAt(%s, %d,%d)", tag2strP(s), p.x, p.y);
392 
393 	if (hResCheckResID(soundRes, s))
394 		g_vm->_audio->queueSound(s, 1, p);
395 }
396 
playSoundAt(uint32 s,Location playAt)397 void playSoundAt(uint32 s, Location playAt) {
398 	Point32 p = translateLocation(playAt);
399 	if (p != VeryFarAway)
400 		playSoundAt(s, p);
401 }
402 
403 //-----------------------------------------------------------------------
404 // voice playback w/ attenuation
405 
sayVoiceAt(uint32 s[],Point32 p)406 bool sayVoiceAt(uint32 s[], Point32 p) {
407 	debugCN(1, kDebugSound, "sayVoiceAt([%s", tag2strP(s[0]));
408 
409 	for (uint32 i = 1; s[i]; i++)
410 		debugCN(1, kDebugSound, ", %s", tag2strP(s[i]));
411 
412 	debugC(1, kDebugSound, "], %d,%d)", p.x, p.y);
413 
414 	g_vm->_audio->queueVoice(s, p);
415 
416 	return true;
417 }
418 
sayVoiceAt(uint32 s[],Location playAt)419 bool sayVoiceAt(uint32 s[], Location playAt) {
420 	Point32 p = translateLocation(playAt);
421 	if (p != VeryFarAway)
422 		return sayVoiceAt(s, p);
423 	return false;
424 }
425 
426 //-----------------------------------------------------------------------
427 // loop playback w/ attenuation
428 
playLoopAt(uint32 s,Point32 loc)429 void playLoopAt(uint32 s, Point32 loc) {
430 	debugC(1, kDebugSound, "playLoopAt(%s, %d,%d)", tag2strP(s), loc.x, loc.y);
431 
432 	if (hResCheckResID(loopRes, s))
433 		g_vm->_audio->playLoop(s, 0, loc);
434 	else
435 		g_vm->_audio->stopLoop();
436 }
437 
438 void addAuxTheme(Location loc, uint32 lid);
439 void killAuxTheme(uint32 lid);
440 void killAllAuxThemes(void);
441 
playLoopAt(uint32 s,Location playAt)442 void playLoopAt(uint32 s, Location playAt) {
443 	debugC(1, kDebugSound, "playLoopAt(%s, %d,%d,%d)", tag2strP(s), playAt.u, playAt.v, playAt.z);
444 
445 	if (s) {
446 		addAuxTheme(playAt, s);
447 	} else {
448 		killAllAuxThemes();
449 	}
450 }
451 
452 //-----------------------------------------------------------------------
453 // loop attenuation
454 
moveLoop(Point32 loc)455 void moveLoop(Point32 loc) {
456 	g_vm->_audio->setLoopPosition(loc);
457 }
458 
moveLoop(Location loc)459 void moveLoop(Location loc) {
460 	Point32 p = translateLocation(loc);
461 	if (p != VeryFarAway) {
462 		moveLoop(p);
463 	}
464 }
465 
466 //-----------------------------------------------------------------------
467 // supplemental interface check for speech
468 
stillDoingVoice(uint32 sampno)469 bool stillDoingVoice(uint32 sampno) {
470 	bool result = g_vm->_audio->saying(sampno);
471 
472 	debugC(1, kDebugSound, "stillDoingVoice(%s) -> %d", tag2strP(sampno), result);
473 
474 	return result;
475 }
476 
stillDoingVoice(uint32 s[])477 bool stillDoingVoice(uint32 s[]) {
478 	uint32 *p = s;
479 
480 	while (*p) {
481 		if (g_vm->_audio->saying(*p++))
482 			return true;
483 	}
484 
485 	return false;
486 }
487 
488 /* ===================================================================== *
489    SAGA compatible audio playback routines
490  * ===================================================================== */
491 
492 //-----------------------------------------------------------------------
493 // derive an ID from SAGA string
494 
parse_res_id(char IDstr[])495 uint32 parse_res_id(char IDstr[]) {
496 	uint32 a[5] = {0, 0, 0, 0, 0};
497 	uint32 a2;
498 	uint32 i, j;
499 	assert(IDstr != NULL);
500 	if (strlen(IDstr)) {
501 		for (i = 0, j = 0; i < strlen(IDstr); i++) {
502 			if (IDstr[i] == ':') {
503 				a2 = atoi(IDstr + i + 1);
504 				return MKTAG(a[0], a[1], a[2], a2);
505 			} else {
506 				a[j++] = IDstr[i];
507 			}
508 		}
509 	}
510 	return MKTAG(a[0], a[1], a[2], a[3]);
511 }
512 
513 //-----------------------------------------------------------------------
514 // playback aliases
515 
PlaySound(char IDstr[])516 void PlaySound(char IDstr[]) {
517 	if (IDstr == NULL)
518 		playSound(0);
519 	else
520 		playSound(parse_res_id(IDstr));
521 }
522 
PlayLongSound(char IDstr[])523 void PlayLongSound(char IDstr[]) {
524 	if (IDstr == NULL)
525 		playLongSound(0);
526 	else
527 		playLongSound(parse_res_id(IDstr));
528 }
529 
PlayVoice(char IDstr[])530 void PlayVoice(char IDstr[]) {
531 	if (IDstr == NULL)
532 		playVoice(0);
533 	else
534 		playVoice(parse_res_id(IDstr));
535 }
536 
PlayLoop(char IDstr[])537 void PlayLoop(char IDstr[]) {
538 	if (IDstr == NULL)
539 		playLoop(0);
540 	else
541 		playLoop(parse_res_id(IDstr));
542 }
543 
PlayLoopAt(char IDstr[],Location l)544 void PlayLoopAt(char IDstr[], Location l) {
545 	if (IDstr == NULL)
546 		playLoop(0);
547 	else
548 		playLoopAt(parse_res_id(IDstr), l);
549 }
550 
PlayMusic(char IDstr[])551 void PlayMusic(char IDstr[]) {
552 	if (IDstr == NULL)
553 		playMusic(0);
554 	else
555 		playMusic(parse_res_id(IDstr));
556 }
557 
558 ////////////////////////////////////////////////////////////////
559 
initAudio()560 bool initAudio() {
561 	g_vm->_audio = new AudioInterface();
562 	return true;
563 }
564 
AudioInterface()565 AudioInterface::AudioInterface() {
566 	_music = nullptr;
567 	_mixer = g_system->getMixer();
568 
569 	memset(_clickSizes, 0, sizeof(_clickSizes));
570 	memset(_clickData, 0, sizeof(_clickData));
571 }
572 
~AudioInterface()573 AudioInterface::~AudioInterface() {
574 	delete _music;
575 	free(_clickData[1]);
576 	free(_clickData[2]);
577 }
578 
initAudioInterface(hResContext * musicContext)579 void AudioInterface::initAudioInterface(hResContext *musicContext) {
580 	_music = new Music(musicContext);
581 }
582 
playFlag(void)583 bool AudioInterface::playFlag(void) {
584 	debugC(5, kDebugSound, "AudioInterface::playFlag()");
585 	if (_speechQueue.size() == 0 && !_mixer->isSoundHandleActive(_speechSoundHandle))
586 		_currentSpeech.seg = 0;
587 
588 	return _speechQueue.size() > 0 || _sfxQueue.size() > 0;
589 }
590 
playMe(void)591 void AudioInterface::playMe(void) {
592 	if (_speechQueue.size() > 0 && !_mixer->isSoundHandleActive(_speechSoundHandle)) {
593 		SoundInstance si = _speechQueue.front();
594 		_speechQueue.pop_front();
595 
596 		_currentSpeech = si;
597 
598 		Common::SeekableReadStream *stream = loadResourceToStream(voiceRes, si.seg, "voice data");
599 		Audio::AudioStream *aud = makeShortenStream(*stream);
600 		byte vol = g_vm->_speechVoice ? volumeFromDist(si.loc, getVolume(kVolVoice)) : 0;
601 
602 		_mixer->playStream(Audio::Mixer::kSpeechSoundType, &_speechSoundHandle, aud, -1, vol);
603 
604 		delete stream;
605 	}
606 
607 	if (_sfxQueue.size() > 0) {
608 		SoundInstance si = _sfxQueue.pop();
609 
610 		Common::SeekableReadStream *stream = loadResourceToStream(soundRes, si.seg, "sound data");
611 		Audio::AudioStream *aud = Audio::makeRawStream(stream, 22050, Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN);
612 		byte vol = volumeFromDist(si.loc, getVolume(kVolSfx));
613 
614 		_mixer->playStream(Audio::Mixer::kSFXSoundType, &_sfxSoundHandle, aud, -1, vol);
615 	}
616 }
617 
playMusic(uint32 s,int16 loopFactor,Point32 where)618 void AudioInterface::playMusic(uint32 s, int16 loopFactor, Point32 where) {
619 	_music->play(s, loopFactor ? MUSIC_LOOP : MUSIC_NORMAL);
620 
621 	_currentMusic.seg = s;
622 	_currentMusic.loop = loopFactor;
623 	_currentMusic.loc = where;
624 }
625 
stopMusic(void)626 void AudioInterface::stopMusic(void) {
627 	_music->stop();
628 }
629 
queueSound(uint32 s,int16 loopFactor,Point32 where)630 void AudioInterface::queueSound(uint32 s, int16 loopFactor, Point32 where) {
631 	SoundInstance si;
632 
633 	si.seg = s;
634 	si.loop = loopFactor;
635 	si.loc = where;
636 
637 	_sfxQueue.push(si);
638 }
639 
playLoop(uint32 s,int16 loopFactor,Point32 where)640 void AudioInterface::playLoop(uint32 s, int16 loopFactor, Point32 where) {
641 	_currentLoop.seg = s;
642 	_currentLoop.loop = loopFactor;
643 	_currentLoop.loc = where;
644 
645 	Common::SeekableReadStream *stream = loadResourceToStream(loopRes, s, "loop data");
646 	Audio::SeekableAudioStream *aud = Audio::makeRawStream(stream, 22050, Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN);
647 	Audio::AudioStream *laud = Audio::makeLoopingAudioStream(aud, loopFactor);
648 	byte vol = volumeFromDist(where, getVolume(kVolSfx));
649 
650 	_mixer->playStream(Audio::Mixer::kSFXSoundType, &g_vm->_audio->_loopSoundHandle, laud, -1, vol);
651 }
652 
stopLoop(void)653 void AudioInterface::stopLoop(void) {
654 	_mixer->stopHandle(_loopSoundHandle);
655 }
656 
setLoopPosition(Point32 newLoc)657 void AudioInterface::setLoopPosition(Point32 newLoc) {
658 	if (_currentLoop.loc == newLoc)
659 		return;
660 
661 	_currentLoop.loc = newLoc;
662 	byte vol = volumeFromDist(newLoc, getVolume(kVolSfx));
663 
664 	_mixer->setChannelVolume(_loopSoundHandle, vol);
665 }
666 
queueVoice(uint32 s,Point32 where)667 void AudioInterface::queueVoice(uint32 s, Point32 where) {
668 	SoundInstance si;
669 
670 	si.seg = s;
671 	si.loop = false;
672 	si.loc = where;
673 
674 	_speechQueue.push_back(si);
675 }
676 
queueVoice(uint32 s[],Point32 where)677 void AudioInterface::queueVoice(uint32 s[], Point32 where) {
678 	SoundInstance si;
679 
680 	uint32 *p = s;
681 	while (*p) {
682 		si.seg = *p;
683 		si.loop = false;
684 		si.loc = where;
685 
686 		_speechQueue.push_back(si);
687 		p++;
688 	}
689 }
690 
stopVoice(void)691 void AudioInterface::stopVoice(void) {
692 	_mixer->stopHandle(_speechSoundHandle);
693 }
694 
talking(void)695 bool AudioInterface::talking(void) {
696 	return _mixer->isSoundHandleActive(_speechSoundHandle);
697 }
698 
saying(uint32 s)699 bool AudioInterface::saying(uint32 s) {
700 	if (_currentSpeech.seg == s)
701 		return true;
702 
703 	for (Common::List<SoundInstance>::iterator it = _speechQueue.begin(); it != _speechQueue.end(); ++it)
704 		if ((*it).seg == s)
705 			return true;
706 
707 	return false;
708 }
709 
getVolume(VolumeTarget src)710 byte AudioInterface::getVolume(VolumeTarget src) {
711 	switch (src) {
712 	case kVolMusic:
713 		return ConfMan.getInt("music_volume");
714 
715 	case kVolSfx:
716 		return ConfMan.getInt("sfx_volume");
717 
718 	case kVolVoice:
719 		return ConfMan.getInt("speech_volume");
720 	}
721 
722 	return 0;
723 }
724 
suspend(void)725 void AudioInterface::suspend(void) {
726 	_mixer->pauseAll(true);
727 }
728 
resume(void)729 void AudioInterface::resume(void) {
730 	_mixer->pauseAll(false);
731 }
732 
bufCheckResID(hResContext * hrc,uint32 s)733 bool bufCheckResID(hResContext *hrc, uint32 s) {
734 	return s != 0;
735 }
736 
hResCheckResID(hResContext * hrc,uint32 s)737 bool hResCheckResID(hResContext *hrc, uint32 s) {
738 	if (hrc != NULL)
739 		return hrc->seek(s);
740 	return false;
741 }
742 
hResCheckResID(hResContext * hrc,uint32 s[])743 bool hResCheckResID(hResContext *hrc, uint32 s[]) {
744 	if (s != NULL) {
745 		if (s[0] == 0)
746 			return false;
747 
748 		for (int i = 0; s[i]; i++) {
749 			if (!hResCheckResID(hrc, s[i]))
750 				return false;
751 		}
752 	}
753 	return true;
754 }
755 
756 } // end of namespace Saga2
757