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 * Additional copyright for this file:
8 * Copyright (C) 1994-1998 Revolution Software Ltd.
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 */
24
25 // One feature still missing is the original's DipMusic() function which, as
26 // far as I can understand, softened the music volume when someone was
27 // speaking, but only (?) if the music was playing loudly at the time.
28 //
29 // All things considered, I think this is more bother than it's worth.
30
31
32 #include "common/file.h"
33 #include "common/memstream.h"
34 #include "common/substream.h"
35 #include "common/system.h"
36 #include "common/textconsole.h"
37
38 #include "audio/audiostream.h"
39 #include "audio/mixer.h"
40 #include "audio/decoders/mp3.h"
41 #include "audio/decoders/vorbis.h"
42 #include "audio/decoders/flac.h"
43 #include "audio/decoders/xa.h"
44 #include "audio/rate.h"
45
46 #include "sword2/sword2.h"
47 #include "sword2/defs.h"
48 #include "sword2/header.h"
49 #include "sword2/resman.h"
50 #include "sword2/sound.h"
51
52 namespace Sword2 {
53
54 static Audio::AudioStream *makeCLUStream(Common::File *fp, int size);
55 static Audio::AudioStream *makePSXCLUStream(Common::File *fp, int size);
56
getAudioStream(SoundFileHandle * fh,const char * base,int cd,uint32 id,uint32 * numSamples)57 static Audio::AudioStream *getAudioStream(SoundFileHandle *fh, const char *base, int cd, uint32 id, uint32 *numSamples) {
58 bool alreadyOpen;
59
60 if (!fh->file.isOpen()) {
61 alreadyOpen = false;
62
63 struct {
64 const char *ext;
65 int mode;
66 } file_types[] = {
67 #ifdef USE_FLAC
68 { "clf", kFLACMode },
69 #endif
70 #ifdef USE_VORBIS
71 { "clg", kVorbisMode },
72 #endif
73 #ifdef USE_MAD
74 { "cl3", kMP3Mode },
75 #endif
76 { "clu", kCLUMode }
77 };
78
79 int soundMode = 0;
80 char filename[20];
81
82 for (int i = 0; i < ARRAYSIZE(file_types); i++) {
83 sprintf(filename, "%s%d.%s", base, cd, file_types[i].ext);
84 if (Common::File::exists(filename)) {
85 soundMode = file_types[i].mode;
86 break;
87 }
88
89 sprintf(filename, "%s.%s", base, file_types[i].ext);
90 if (Common::File::exists(filename)) {
91 soundMode = file_types[i].mode;
92 break;
93 }
94 }
95
96 if (soundMode == 0)
97 return NULL;
98
99 fh->file.open(filename);
100 fh->fileType = soundMode;
101 if (!fh->file.isOpen()) {
102 warning("BS2 getAudioStream: Failed opening file '%s'", filename);
103 return NULL;
104 }
105 if (fh->fileSize != fh->file.size()) {
106 free(fh->idxTab);
107 fh->idxTab = NULL;
108 }
109 } else
110 alreadyOpen = true;
111
112 uint32 entrySize = (fh->fileType == kCLUMode) ? 2 : 3;
113
114 if (!fh->idxTab) {
115 fh->file.seek(0);
116 fh->idxLen = fh->file.readUint32LE();
117 fh->file.seek(entrySize * 4);
118
119 fh->idxTab = (uint32 *)malloc(fh->idxLen * 3 * sizeof(uint32));
120 for (uint32 cnt = 0; cnt < fh->idxLen; cnt++) {
121 fh->idxTab[cnt * 3 + 0] = fh->file.readUint32LE();
122 fh->idxTab[cnt * 3 + 1] = fh->file.readUint32LE();
123 if (fh->fileType == kCLUMode) {
124 fh->idxTab[cnt * 3 + 2] = fh->idxTab[cnt * 3 + 1];
125 fh->idxTab[cnt * 3 + 1]--;
126 } else
127 fh->idxTab[cnt * 3 + 2] = fh->file.readUint32LE();
128 }
129 }
130
131 // FIXME: In the forest maze on Zombie Island, the scripts will often
132 // try to play song 451, which doesn't exist. We could easily substitute
133 // another for it here, but which one? There are roughly 250 musical
134 // cues to choose from.
135
136 uint32 pos = fh->idxTab[id * 3 + 0];
137 uint32 len = fh->idxTab[id * 3 + 1];
138 uint32 enc_len = fh->idxTab[id * 3 + 2];
139
140 if (numSamples)
141 *numSamples = len;
142
143 if (!pos || !len) {
144 // We couldn't find the sound. Possibly as a result of a bad
145 // installation (e.g. using the music file from CD 2 as the
146 // first music file). Don't close the file if it was already
147 // open though, because something is playing from it.
148 warning("getAudioStream: Could not find %s ID %d! Possibly the wrong file", base, id);
149 if (!alreadyOpen)
150 fh->file.close();
151 return NULL;
152 }
153
154 fh->file.seek(pos, SEEK_SET);
155
156 switch (fh->fileType) {
157 case kCLUMode:
158 if (Sword2Engine::isPsx())
159 return makePSXCLUStream(&fh->file, enc_len);
160 else
161 return makeCLUStream(&fh->file, enc_len);
162 #ifdef USE_MAD
163 case kMP3Mode: {
164 Common::SafeSeekableSubReadStream *tmp = new Common::SafeSeekableSubReadStream(&fh->file, pos, pos + enc_len);
165 return Audio::makeMP3Stream(tmp, DisposeAfterUse::YES);
166 }
167 #endif
168 #ifdef USE_VORBIS
169 case kVorbisMode: {
170 Common::SafeSeekableSubReadStream *tmp = new Common::SafeSeekableSubReadStream(&fh->file, pos, pos + enc_len);
171 return Audio::makeVorbisStream(tmp, DisposeAfterUse::YES);
172 }
173 #endif
174 #ifdef USE_FLAC
175 case kFLACMode: {
176 Common::SafeSeekableSubReadStream *tmp = new Common::SafeSeekableSubReadStream(&fh->file, pos, pos + enc_len);
177 return Audio::makeFLACStream(tmp, DisposeAfterUse::YES);
178 }
179 #endif
180 default:
181 return NULL;
182 }
183 }
184
185 // ----------------------------------------------------------------------------
186 // Custom AudioStream class to handle Broken Sword 2's audio compression.
187 // ----------------------------------------------------------------------------
188
189 #define GetCompressedShift(n) (((n) >> 4) & 0x0F)
190 #define GetCompressedSign(n) ((n) & 0x08)
191 #define GetCompressedAmplitude(n) ((n) & 0x07)
192
CLUInputStream(Common::File * file,int size)193 CLUInputStream::CLUInputStream(Common::File *file, int size)
194 : _file(file), _firstTime(true), _bufferEnd(_outbuf + BUFFER_SIZE) {
195
196 // Determine the end position.
197 _file_pos = _file->pos();
198 _end_pos = _file_pos + size;
199
200 // Read in initial data
201 refill();
202 }
203
~CLUInputStream()204 CLUInputStream::~CLUInputStream() {
205 }
206
readBuffer(int16 * buffer,const int numSamples)207 int CLUInputStream::readBuffer(int16 *buffer, const int numSamples) {
208 int samples = 0;
209 while (samples < numSamples && !eosIntern()) {
210 const int len = MIN(numSamples - samples, (int)(_bufferEnd - _pos));
211 memcpy(buffer, _pos, len * 2);
212 buffer += len;
213 _pos += len;
214 samples += len;
215 if (_pos >= _bufferEnd) {
216 refill();
217 }
218 }
219 return samples;
220 }
221
refill()222 void CLUInputStream::refill() {
223 byte *in = _inbuf;
224 int16 *out = _outbuf;
225
226 _file->seek(_file_pos, SEEK_SET);
227
228 uint len_left = _file->read(in, MIN((uint32)BUFFER_SIZE, _end_pos - _file->pos()));
229
230 _file_pos = _file->pos();
231
232 while (len_left > 0) {
233 uint16 sample;
234
235 if (_firstTime) {
236 _firstTime = false;
237 _prev = READ_LE_UINT16(in);
238 sample = _prev;
239 len_left -= 2;
240 in += 2;
241 } else {
242 uint16 delta = GetCompressedAmplitude(*in) << GetCompressedShift(*in);
243 if (GetCompressedSign(*in))
244 sample = _prev - delta;
245 else
246 sample = _prev + delta;
247
248 _prev = sample;
249 len_left--;
250 in++;
251 }
252
253 *out++ = (int16)sample;
254 }
255
256 _pos = _outbuf;
257 _bufferEnd = out;
258 }
259
makeCLUStream(Common::File * file,int size)260 Audio::AudioStream *makeCLUStream(Common::File *file, int size) {
261 return new CLUInputStream(file, size);
262 }
263
makePSXCLUStream(Common::File * file,int size)264 Audio::AudioStream *makePSXCLUStream(Common::File *file, int size) {
265
266 // Buffer audio file data, and ask MemoryReadStream to dispose of it
267 // when not needed anymore.
268
269 byte *buffer = (byte *)malloc(size);
270 file->read(buffer, size);
271 return Audio::makeXAStream(new Common::MemoryReadStream(buffer, size, DisposeAfterUse::YES), 11025);
272 }
273
274 // ----------------------------------------------------------------------------
275 // Another custom AudioStream class, to wrap around the various AudioStream
276 // classes used for music decompression, and to add looping, fading, etc.
277 // ----------------------------------------------------------------------------
278
279 // The length of a fade-in/out, in milliseconds.
280 #define FADE_LENGTH 3000
281
MusicInputStream(int cd,SoundFileHandle * fh,uint32 musicId,bool looping)282 MusicInputStream::MusicInputStream(int cd, SoundFileHandle *fh, uint32 musicId, bool looping) {
283 _cd = cd;
284 _fh = fh;
285 _musicId = musicId;
286 _looping = looping;
287
288 _bufferEnd = _buffer + BUFFER_SIZE;
289 _remove = false;
290 _fading = 0;
291
292 _decoder = getAudioStream(_fh, "music", _cd, _musicId, &_numSamples);
293 if (_decoder) {
294 _samplesLeft = _numSamples;
295 _fadeSamples = (getRate() * FADE_LENGTH) / 1000;
296 fadeUp();
297
298 // Read in initial data
299 refill();
300 }
301 }
302
~MusicInputStream()303 MusicInputStream::~MusicInputStream() {
304 delete _decoder;
305 _decoder = NULL;
306 }
307
readBuffer(int16 * buffer,const int numSamples)308 int MusicInputStream::readBuffer(int16 *buffer, const int numSamples) {
309 if (!_decoder)
310 return 0;
311
312 int samples = 0;
313 while (samples < numSamples && !eosIntern()) {
314 const int len = MIN(numSamples - samples, (int)(_bufferEnd - _pos));
315 memcpy(buffer, _pos, len * 2);
316 buffer += len;
317 _pos += len;
318 samples += len;
319 if (_pos >= _bufferEnd) {
320 refill();
321 }
322 }
323 return samples;
324 }
325
refill()326 void MusicInputStream::refill() {
327 int16 *buf = _buffer;
328 uint32 numSamples = 0;
329 uint32 len_left;
330 bool endFade = false;
331
332 len_left = BUFFER_SIZE;
333
334 if (_fading > 0 && (uint32)_fading < len_left)
335 len_left = _fading;
336
337 if (_samplesLeft < len_left)
338 len_left = _samplesLeft;
339
340 if (!_looping) {
341 // Non-looping music is faded out at the end. If this fade
342 // out would have started somewhere within the len_left samples
343 // to read, we only read up to that point. This way, we can
344 // treat this fade as any other.
345
346 if (!_fading) {
347 uint32 currentlyAt = _numSamples - _samplesLeft;
348 uint32 fadeOutAt = _numSamples - _fadeSamples;
349 uint32 readTo = currentlyAt + len_left;
350
351 if (fadeOutAt == currentlyAt)
352 fadeDown();
353 else if (fadeOutAt > currentlyAt && fadeOutAt <= readTo) {
354 len_left = fadeOutAt - currentlyAt;
355 endFade = true;
356 }
357 }
358 }
359
360 int desired = len_left - numSamples;
361 int len = _decoder->readBuffer(buf, desired);
362
363 // Shouldn't happen, but if it does it could cause an infinite loop.
364 // Of course there were bugs that caused it to happen several times
365 // during development. :-)
366
367 if (len < desired) {
368 warning("Expected %d samples, but got %d", desired, len);
369 _samplesLeft = len;
370 }
371
372 buf += len;
373 numSamples += len;
374 len_left -= len;
375 _samplesLeft -= len;
376
377 int16 *ptr;
378
379 if (_fading > 0) {
380 // Fade down
381 for (ptr = _buffer; ptr < buf; ptr++) {
382 if (_fading > 0) {
383 _fading--;
384 *ptr = (*ptr * _fading) / _fadeSamples;
385 }
386 if (_fading == 0) {
387 _looping = false;
388 _remove = true;
389 *ptr = 0;
390 }
391 }
392 } else if (_fading < 0) {
393 // Fade up
394 for (ptr = _buffer; ptr < buf; ptr++) {
395 _fading--;
396 *ptr = -(*ptr * _fading) / _fadeSamples;
397 if (_fading <= -_fadeSamples) {
398 _fading = 0;
399 break;
400 }
401 }
402 }
403
404 if (endFade)
405 fadeDown();
406
407 if (!_samplesLeft) {
408 if (_looping) {
409 delete _decoder;
410 _decoder = getAudioStream(_fh, "music", _cd, _musicId, &_numSamples);
411 _samplesLeft = _numSamples;
412 } else
413 _remove = true;
414 }
415
416 _pos = _buffer;
417 _bufferEnd = buf;
418 }
419
fadeUp()420 void MusicInputStream::fadeUp() {
421 if (_fading > 0)
422 _fading = -_fading;
423 else if (_fading == 0)
424 _fading = -1;
425 }
426
fadeDown()427 void MusicInputStream::fadeDown() {
428 if (_fading < 0)
429 _fading = -_fading;
430 else if (_fading == 0)
431 _fading = _fadeSamples;
432 }
433
readyToRemove()434 bool MusicInputStream::readyToRemove() {
435 return _remove;
436 }
437
getTimeRemaining()438 int32 MusicInputStream::getTimeRemaining() {
439 // This is far from exact, but it doesn't have to be.
440 return (_samplesLeft + BUFFER_SIZE) / getRate();
441 }
442
443 // ----------------------------------------------------------------------------
444 // Main sound class
445 // ----------------------------------------------------------------------------
446
447 // AudioStream API
448
readBuffer(int16 * buffer,const int numSamples)449 int Sound::readBuffer(int16 *buffer, const int numSamples) {
450 Common::StackLock lock(_mutex);
451 int i;
452
453 if (_musicPaused)
454 return 0;
455
456 for (i = 0; i < MAXMUS; i++) {
457 if (_music[i] && _music[i]->readyToRemove()) {
458 delete _music[i];
459 _music[i] = NULL;
460 }
461 }
462
463 memset(buffer, 0, 2 * numSamples);
464
465 if (!_mixBuffer || numSamples > _mixBufferLen) {
466 if (_mixBuffer) {
467 int16 *newBuffer = (int16 *)realloc(_mixBuffer, 2 * numSamples);
468 if (newBuffer) {
469 _mixBuffer = newBuffer;
470 } else {
471 // We can't use the old buffer any more. It's too small.
472 free(_mixBuffer);
473 _mixBuffer = 0;
474 }
475 } else
476 _mixBuffer = (int16 *)malloc(2 * numSamples);
477
478 _mixBufferLen = numSamples;
479 }
480
481 if (!_mixBuffer)
482 return 0;
483
484 for (i = 0; i < MAXMUS; i++) {
485 if (!_music[i])
486 continue;
487
488 int len = _music[i]->readBuffer(_mixBuffer, numSamples);
489
490 if (!_musicMuted) {
491 for (int j = 0; j < len; j++) {
492 Audio::clampedAdd(buffer[j], _mixBuffer[j]);
493 }
494 }
495 }
496
497 bool inUse[MAXMUS];
498
499 for (i = 0; i < MAXMUS; i++)
500 inUse[i] = false;
501
502 for (i = 0; i < MAXMUS; i++) {
503 if (_music[i]) {
504 if (_music[i]->getCD() == 1)
505 inUse[0] = true;
506 else
507 inUse[1] = true;
508 }
509 }
510
511 for (i = 0; i < MAXMUS; i++) {
512 if (!inUse[i] && !_musicFile[i].inUse && _musicFile[i].file.isOpen())
513 _musicFile[i].file.close();
514 }
515
516 return numSamples;
517 }
518
endOfData() const519 bool Sound::endOfData() const {
520 // The music never stops. It just goes quiet.
521 return false;
522 }
523
524 // ----------------------------------------------------------------------------
525 // MUSIC
526 // ----------------------------------------------------------------------------
527
528 /**
529 * Stops the music dead in its tracks. Any music that is currently being
530 * streamed is paused.
531 */
532
pauseMusic()533 void Sound::pauseMusic() {
534 Common::StackLock lock(_mutex);
535
536 _musicPaused = true;
537 }
538
539 /**
540 * Restarts the music from where it was stopped.
541 */
542
unpauseMusic()543 void Sound::unpauseMusic() {
544 Common::StackLock lock(_mutex);
545
546 _musicPaused = false;
547 }
548
549 /**
550 * Fades out and stops the music.
551 */
552
stopMusic(bool immediately)553 void Sound::stopMusic(bool immediately) {
554 Common::StackLock lock(_mutex);
555
556 _loopingMusicId = 0;
557
558 for (int i = 0; i < MAXMUS; i++) {
559 if (_music[i]) {
560 if (immediately) {
561 delete _music[i];
562 _music[i] = NULL;
563 } else
564 _music[i]->fadeDown();
565 }
566 }
567 }
568
569 /**
570 * Streams music from a cluster file.
571 * @param musicId the id of the music to stream
572 * @param loop true if the music is to loop back to the start
573 * @return RD_OK or an error code
574 */
streamCompMusic(uint32 musicId,bool loop)575 int32 Sound::streamCompMusic(uint32 musicId, bool loop) {
576 Common::StackLock lock(_mutex);
577
578 int cd = _vm->_resman->getCD();
579
580 if (loop)
581 _loopingMusicId = musicId;
582 else
583 _loopingMusicId = 0;
584
585 int primary = -1;
586 int secondary = -1;
587
588 // If both music streams are active, one of them will have to go.
589
590 if (_music[0] && _music[1]) {
591 int32 fade0 = _music[0]->isFading();
592 int32 fade1 = _music[1]->isFading();
593
594 if (!fade0 && !fade1) {
595 // Neither is fading. This shouldn't happen, so just
596 // pick one and be done with it.
597 primary = 0;
598 } else if (fade0 && !fade1) {
599 // Stream 0 is fading, so pick that one.
600 primary = 0;
601 } else if (!fade0 && fade1) {
602 // Stream 1 is fading, so pick that one.
603 primary = 1;
604 } else {
605 // Both streams are fading. Pick the one that is
606 // closest to silent.
607 if (ABS(fade0) < ABS(fade1))
608 primary = 0;
609 else
610 primary = 1;
611 }
612
613 delete _music[primary];
614 _music[primary] = NULL;
615 }
616
617 // Pick the available music stream. If no music is playing it doesn't
618 // matter which we use.
619
620 if (_music[0] || _music[1]) {
621 if (_music[0]) {
622 primary = 1;
623 secondary = 0;
624 } else {
625 primary = 0;
626 secondary = 1;
627 }
628 } else
629 primary = 0;
630
631 // Don't start streaming if the volume is off.
632 if (isMusicMute()) {
633 return RD_OK;
634 }
635
636 if (secondary != -1)
637 _music[secondary]->fadeDown();
638 SoundFileHandle *fh = (cd == 1) ? &_musicFile[0] : &_musicFile[1];
639 fh->inUse = true;
640
641 MusicInputStream *tmp = new MusicInputStream(cd, fh, musicId, loop);
642
643 if (tmp->isReady()) {
644 _music[primary] = tmp;
645 fh->inUse = false;
646 return RD_OK;
647 } else {
648 fh->inUse = false;
649 delete tmp;
650 return RDERR_INVALIDFILENAME;
651 }
652 }
653
654 /**
655 * @return the time left for the current music, in seconds.
656 */
657
musicTimeRemaining()658 int32 Sound::musicTimeRemaining() {
659 Common::StackLock lock(_mutex);
660
661 for (int i = 0; i < MAXMUS; i++) {
662 if (_music[i] && _music[i]->isFading() <= 0)
663 return _music[i]->getTimeRemaining();
664 }
665
666 return 0;
667 }
668
669 // ----------------------------------------------------------------------------
670 // SPEECH
671 // ----------------------------------------------------------------------------
672
673 /**
674 * Mutes/Unmutes the speech.
675 * @param mute If mute is false, restore the volume to the last set master
676 * level. Otherwise the speech is muted (volume 0).
677 */
678
muteSpeech(bool mute)679 void Sound::muteSpeech(bool mute) {
680 _speechMuted = mute;
681
682 if (_vm->_mixer->isSoundHandleActive(_soundHandleSpeech)) {
683 uint volume = mute ? 0 : Audio::Mixer::kMaxChannelVolume;
684
685 _vm->_mixer->setChannelVolume(_soundHandleSpeech, volume);
686 }
687 }
688
689 /**
690 * Stops the speech dead in its tracks.
691 */
692
pauseSpeech()693 void Sound::pauseSpeech() {
694 if (!_speechPaused) {
695 _speechPaused = true;
696 _vm->_mixer->pauseHandle(_soundHandleSpeech, true);
697 }
698 }
699
700 /**
701 * Restarts the speech from where it was stopped.
702 */
703
unpauseSpeech()704 void Sound::unpauseSpeech() {
705 if (_speechPaused) {
706 _speechPaused = false;
707 _vm->_mixer->pauseHandle(_soundHandleSpeech, false);
708 }
709 }
710
711 /**
712 * Stops the speech from playing.
713 */
714
stopSpeech()715 int32 Sound::stopSpeech() {
716 if (_vm->_mixer->isSoundHandleActive(_soundHandleSpeech)) {
717 _vm->_mixer->stopHandle(_soundHandleSpeech);
718 return RD_OK;
719 }
720
721 return RDERR_SPEECHNOTPLAYING;
722 }
723
724 /**
725 * @return Either RDSE_SAMPLEPLAYING or RDSE_SAMPLEFINISHED
726 */
727
getSpeechStatus()728 int32 Sound::getSpeechStatus() {
729 return _vm->_mixer->isSoundHandleActive(_soundHandleSpeech) ? RDSE_SAMPLEPLAYING : RDSE_SAMPLEFINISHED;
730 }
731
732 /**
733 * Returns either RDSE_QUIET or RDSE_SPEAKING
734 */
735
amISpeaking()736 int32 Sound::amISpeaking() {
737 if (!_speechMuted && !_speechPaused && _vm->_mixer->isSoundHandleActive(_soundHandleSpeech))
738 return RDSE_SPEAKING;
739
740 return RDSE_QUIET;
741 }
742
743 /**
744 * This function loads, decompresses and plays a line of speech. An error
745 * occurs if speech is already playing.
746 * @param speechId the text line id used to reference the speech
747 * @param vol volume, 0 (minimum) to 16 (maximum)
748 * @param pan panning, -16 (full left) to 16 (full right)
749 */
750
playCompSpeech(uint32 speechId,uint8 vol,int8 pan)751 int32 Sound::playCompSpeech(uint32 speechId, uint8 vol, int8 pan) {
752 if (_speechMuted)
753 return RD_OK;
754
755 if (getSpeechStatus() == RDERR_SPEECHPLAYING)
756 return RDERR_SPEECHPLAYING;
757
758 int cd = _vm->_resman->getCD();
759 SoundFileHandle *fh = (cd == 1) ? &_speechFile[0] : &_speechFile[1];
760
761 Audio::AudioStream *input = getAudioStream(fh, "speech", cd, speechId, NULL);
762
763 if (!input)
764 return RDERR_INVALIDID;
765
766 // Modify the volume according to the master volume
767
768 byte volume = _speechMuted ? 0 : vol * Audio::Mixer::kMaxChannelVolume / 16;
769 int8 p = (pan * 127) / 16;
770
771 if (isReverseStereo())
772 p = -p;
773
774 // Start the speech playing
775 _vm->_mixer->playStream(Audio::Mixer::kSpeechSoundType, &_soundHandleSpeech, input, -1, volume, p);
776 return RD_OK;
777 }
778
779 // ----------------------------------------------------------------------------
780 // SOUND EFFECTS
781 // ----------------------------------------------------------------------------
782
783 /**
784 * Mutes/Unmutes the sound effects.
785 * @param mute If mute is false, restore the volume to the last set master
786 * level. Otherwise the sound effects are muted (volume 0).
787 */
788
muteFx(bool mute)789 void Sound::muteFx(bool mute) {
790 _fxMuted = mute;
791
792 // Now update the volume of any fxs playing
793 for (int i = 0; i < FXQ_LENGTH; i++) {
794 if (_fxQueue[i].resource) {
795 _vm->_mixer->setChannelVolume(_fxQueue[i].handle, mute ? 0 : _fxQueue[i].volume);
796 }
797 }
798 }
799
800 /**
801 * Sets the volume and pan of the sample which is currently playing
802 * @param id the id of the sample
803 * @param vol volume
804 * @param pan panning
805 */
806
setFxIdVolumePan(int32 id,int vol,int pan)807 int32 Sound::setFxIdVolumePan(int32 id, int vol, int pan) {
808 if (!_fxQueue[id].resource)
809 return RDERR_FXNOTOPEN;
810
811 if (vol > 16)
812 vol = 16;
813
814 _fxQueue[id].volume = (vol * Audio::Mixer::kMaxChannelVolume) / 16;
815
816 if (pan != 255) {
817 if (isReverseStereo())
818 pan = -pan;
819 _fxQueue[id].pan = (pan * 127) / 16;
820 }
821
822 if (!_fxMuted && _vm->_mixer->isSoundHandleActive(_fxQueue[id].handle)) {
823 _vm->_mixer->setChannelVolume(_fxQueue[id].handle, _fxQueue[id].volume);
824 if (pan != -1)
825 _vm->_mixer->setChannelBalance(_fxQueue[id].handle, _fxQueue[id].pan);
826 }
827
828 return RD_OK;
829 }
830
pauseFx()831 void Sound::pauseFx() {
832 if (!_fxPaused) {
833 for (int i = 0; i < FXQ_LENGTH; i++) {
834 if (_fxQueue[i].resource)
835 _vm->_mixer->pauseHandle(_fxQueue[i].handle, true);
836 }
837 _fxPaused = true;
838 }
839 }
840
unpauseFx()841 void Sound::unpauseFx() {
842 if (_fxPaused) {
843 for (int i = 0; i < FXQ_LENGTH; i++)
844 if (_fxQueue[i].resource)
845 _vm->_mixer->pauseHandle(_fxQueue[i].handle, false);
846 _fxPaused = false;
847 }
848 }
849
850 } // End of namespace Sword2
851