aboutsummaryrefslogtreecommitdiff
path: root/engines/hopkins/sound.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/hopkins/sound.cpp')
-rw-r--r--engines/hopkins/sound.cpp916
1 files changed, 916 insertions, 0 deletions
diff --git a/engines/hopkins/sound.cpp b/engines/hopkins/sound.cpp
new file mode 100644
index 0000000000..d8dfba5246
--- /dev/null
+++ b/engines/hopkins/sound.cpp
@@ -0,0 +1,916 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "hopkins/sound.h"
+
+#include "hopkins/globals.h"
+#include "hopkins/hopkins.h"
+
+#include "audio/decoders/adpcm_intern.h"
+#include "common/system.h"
+#include "common/config-manager.h"
+#include "common/file.h"
+#include "common/textconsole.h"
+#include "audio/audiostream.h"
+#include "audio/mods/module.h"
+#include "audio/mods/protracker.h"
+#include "audio/decoders/raw.h"
+
+namespace Hopkins {
+
+class APC_ADPCMStream : public Audio::DVI_ADPCMStream {
+public:
+ APC_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, int rate, int channels) : DVI_ADPCMStream(stream, disposeAfterUse, stream->size(), rate, channels, 0) {
+ stream->seek(-12, SEEK_CUR);
+ _status.ima_ch[0].last = _startValue[0] = stream->readUint32LE();
+ _status.ima_ch[1].last = _startValue[1] = stream->readUint32LE();
+ stream->seek(4, SEEK_CUR);
+ }
+
+ void reset() {
+ DVI_ADPCMStream::reset();
+ _status.ima_ch[0].last = _startValue[0];
+ _status.ima_ch[1].last = _startValue[1];
+ }
+
+private:
+ int16 _startValue[2];
+};
+
+Audio::RewindableAudioStream *makeAPCStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
+ if (stream->readUint32BE() != MKTAG('C', 'R', 'Y', 'O'))
+ return 0;
+ if (stream->readUint32BE() != MKTAG('_', 'A', 'P', 'C'))
+ return 0;
+ stream->readUint32BE(); // version
+ stream->readUint32LE(); // out size
+ uint32 rate = stream->readUint32LE();
+ stream->skip(8); // initial values, will be handled by the class
+ bool stereo = stream->readUint32LE() != 0;
+
+ return new APC_ADPCMStream(stream, disposeAfterUse, rate, stereo ? 2 : 1);
+}
+
+class TwaAudioStream : public Audio::AudioStream {
+public:
+ TwaAudioStream(Common::String name, Common::SeekableReadStream *stream) {
+ _name = name;
+ _cueSheet.clear();
+ _cueStream = NULL;
+ _cue = 0;
+ _loadedCue = -1;
+
+ for (;;) {
+ char buf[3];
+ stream->read(buf, 3);
+
+ if (buf[0] == 'x' || stream->eos())
+ break;
+
+ _cueSheet.push_back(atol(buf));
+ }
+
+ for (_cue = 0; _cue < _cueSheet.size(); _cue++) {
+ if (loadCue(_cue))
+ break;
+ }
+ }
+
+ ~TwaAudioStream() {
+ delete _cueStream;
+ _cueStream = NULL;
+ }
+
+ virtual bool isStereo() const {
+ return _cueStream ? _cueStream->isStereo() : true;
+ }
+
+ virtual int getRate() const {
+ return _cueStream ? _cueStream->getRate() : 22050;
+ }
+
+ virtual bool endOfData() const {
+ return _cueStream == NULL;
+ }
+
+ virtual int readBuffer(int16 *buffer, const int numSamples) {
+ if (!_cueStream)
+ return 0;
+
+ int16 *buf = buffer;
+ int samplesLeft = numSamples;
+
+ while (samplesLeft) {
+ if (_cueStream) {
+ int readSamples = _cueStream->readBuffer(buf, samplesLeft);
+ buf += readSamples;
+ samplesLeft -= readSamples;
+ }
+
+ if (samplesLeft > 0) {
+ if (++_cue >= _cueSheet.size()) {
+ _cue = 0;
+ }
+ loadCue(_cue);
+ }
+ }
+
+ return numSamples;
+ }
+
+protected:
+ bool loadCue(int nr) {
+ if (_loadedCue == _cueSheet[nr]) {
+ _cueStream->rewind();
+ return true;
+ }
+
+ delete _cueStream;
+ _cueStream = NULL;
+ _loadedCue = _cueSheet[nr];
+
+ Common::String filename = Common::String::format("%s_%02d", _name.c_str(), _cueSheet[nr]);
+ Common::File *file = new Common::File();
+
+ if (file->open(filename + ".APC")) {
+ _cueStream = makeAPCStream(file, DisposeAfterUse::YES);
+ return true;
+ }
+
+ if (file->open(filename + ".WAV")) {
+ _cueStream = Audio::makeWAVStream(file, DisposeAfterUse::YES);
+ return true;
+ }
+
+ if (file->open(filename + ".RAW")) {
+ _cueStream = Audio::makeRawStream(file, 22050, Audio::FLAG_UNSIGNED, DisposeAfterUse::YES);
+ return true;
+ }
+
+ warning("TwaAudioStream::loadCue: Missing cue %d (%s)", nr, filename.c_str());
+ _loadedCue = -1;
+ delete file;
+ return false;
+ }
+
+private:
+ Common::String _name;
+ Common::Array<int> _cueSheet;
+ Audio::RewindableAudioStream *_cueStream;
+ uint _cue;
+ int _loadedCue;
+};
+
+Audio::AudioStream *makeTwaStream(Common::String name, Common::SeekableReadStream *stream) {
+ return new TwaAudioStream(name, stream);
+}
+
+SoundManager::SoundManager(HopkinsEngine *vm) {
+ _vm = vm;
+
+ _specialSoundNum = 0;
+ _soundVolume = 0;
+ _voiceVolume = 0;
+ _musicVolume = 0;
+ _soundOffFl = true;
+ _musicOffFl = true;
+ _voiceOffFl = true;
+ _textOffFl = false;
+ _soundFl = false;
+ _skipRefreshFl = false;
+ _currentSoundIndex = 0;
+ _oldSoundNumber = 0;
+ _modPlayingFl = false;
+
+ for (int i = 0; i < VOICE_COUNT; ++i)
+ Common::fill((byte *)&_voice[i], (byte *)&_voice[i] + sizeof(VoiceItem), 0);
+ for (int i = 0; i < SWAV_COUNT; ++i)
+ Common::fill((byte *)&_sWav[i], (byte *)&_sWav[i] + sizeof(SwavItem), 0);
+ for (int i = 0; i < SOUND_COUNT; ++i)
+ Common::fill((byte *)&_sound[i], (byte *)&_sound[i] + sizeof(SoundItem), 0);
+ Common::fill((byte *)&_music, (byte *)&_music + sizeof(MusicItem), 0);
+}
+
+SoundManager::~SoundManager() {
+ stopMusic();
+ delMusic();
+ _vm->_mixer->stopHandle(_musicHandle);
+ _modPlayingFl = false;
+}
+
+void SoundManager::checkSoundEnd() {
+ if (!_soundOffFl && _soundFl) {
+ if (!checkVoiceStatus(1)) {
+ stopVoice(1);
+ delWav(_currentSoundIndex);
+ }
+ }
+}
+
+void SoundManager::loadAnimSound() {
+ switch (_specialSoundNum) {
+ case 2:
+ loadSample(5, "mitra1.wav");
+ loadSample(1, "tir2.wav");
+ loadSample(2, "sound6.wav");
+ loadSample(3, "sound5.WAV");
+ loadSample(4, "sound4.WAV");
+ break;
+ case 5:
+ loadWav("CRIE.WAV", 1);
+ break;
+ case 14:
+ loadWav("SOUND14.WAV", 1);
+ break;
+ case 16:
+ loadWav("SOUND16.WAV", 1);
+ break;
+ case 198:
+ loadWav("SOUND3.WAV", 1);
+ break;
+ case 199:
+ loadWav("SOUND22.WAV", 1);
+ break;
+ case 200:
+ mixVoice(682, 1);
+ break;
+ case 208:
+ loadWav("SOUND77.WAV", 1);
+ break;
+ case 210:
+ loadWav("SOUND78.WAV", 1);
+ break;
+ case 211:
+ loadWav("SOUND78.WAV", 1);
+ break;
+ case 229:
+ loadWav("SOUND80.WAV", 1);
+ loadWav("SOUND82.WAV", 2);
+ break;
+ }
+}
+
+void SoundManager::playAnimSound(int soundNumber) {
+ if (!_vm->_globals->_censorshipFl && _specialSoundNum == 2) {
+ switch (soundNumber) {
+ case 20:
+ playSample(5);
+ break;
+ case 57:
+ case 63:
+ case 69:
+ playSample(1);
+ break;
+ case 75:
+ playSample(2);
+ break;
+ case 109:
+ playSample(3);
+ break;
+ case 122:
+ playSample(4);
+ break;
+ }
+ } else if (_specialSoundNum == 1 && soundNumber == 17)
+ playSoundFile("SOUND42.WAV");
+ else if (_specialSoundNum == 5 && soundNumber == 19)
+ playWav(1);
+ else if (_specialSoundNum == 14 && soundNumber == 625)
+ playWav(1);
+ else if (_specialSoundNum == 16 && soundNumber == 25)
+ playWav(1);
+ else if (_specialSoundNum == 17) {
+ if (soundNumber == 6)
+ playSample(1);
+ else if (soundNumber == 14)
+ playSample(2);
+ else if (soundNumber == 67)
+ playSample(3);
+ } else if (_specialSoundNum == 198 && soundNumber == 15)
+ playWav(1);
+ else if (_specialSoundNum == 199 && soundNumber == 72)
+ playWav(1);
+ else if (_specialSoundNum == 208 && soundNumber == 40)
+ playWav(1);
+ else if (_specialSoundNum == 210 && soundNumber == 2)
+ playWav(1);
+ else if (_specialSoundNum == 211 && soundNumber == 22)
+ playWav(1);
+ else if (_specialSoundNum == 229) {
+ if (soundNumber == 15)
+ playWav(1);
+ else if (soundNumber == 91)
+ playWav(2);
+ }
+}
+
+static const char *const modSounds[] = {
+ "appart", "ville", "Rock", "police", "deep",
+ "purgat", "riviere", "SUSPENS", "labo", "cadavre",
+ "cabane", "purgat2", "foret", "ile", "ile2",
+ "hopkins", "peur", "URAVOLGA", "BASE", "cadavre2",
+ "usine", "chien", "coeur", "stand", "ocean",
+ "base3", "gloop", "cant", "feel", "lost",
+ "tobac"
+};
+
+void SoundManager::playSound(int soundNumber) {
+ if (_vm->getPlatform() == Common::kPlatformOS2 || _vm->getPlatform() == Common::kPlatformBeOS) {
+ if (soundNumber > 27)
+ return;
+ }
+
+ if (_oldSoundNumber != soundNumber || !_modPlayingFl) {
+ if (_modPlayingFl)
+ stopSound();
+
+ playMod(modSounds[soundNumber - 1]);
+ _oldSoundNumber = soundNumber;
+ }
+}
+
+void SoundManager::stopSound() {
+ stopVoice(0);
+ stopVoice(1);
+ stopVoice(2);
+ if (_soundFl)
+ delWav(_currentSoundIndex);
+
+ for (int i = 1; i <= 48; ++i)
+ removeWavSample(i);
+
+ if (_modPlayingFl) {
+ stopMusic();
+ delMusic();
+ _modPlayingFl = false;
+ }
+}
+
+void SoundManager::playMod(const Common::String &file) {
+ if (_musicOffFl)
+ return;
+ Common::String modFile = file;
+
+ // HACK
+ if (modFile == "URAVOLGA" && (_vm->getPlatform() == Common::kPlatformWindows || _vm->getPlatform() == Common::kPlatformLinux))
+ modFile = "peur";
+
+ // The Windows/Linux version chops off the music file names to 5 characters
+ if (modFile.size() > 5 && (_vm->getPlatform() == Common::kPlatformWindows || _vm->getPlatform() == Common::kPlatformLinux)) {
+ if (!modFile.hasSuffix("2")) {
+ while (modFile.size() > 5)
+ modFile.deleteLastChar();
+ } else {
+ while (modFile.size() > 4)
+ modFile.deleteLastChar();
+ modFile += "2";
+ }
+ }
+ if (_modPlayingFl) {
+ stopMusic();
+ delMusic();
+ _modPlayingFl = false;
+ }
+
+ loadMusic(modFile);
+ playMusic();
+ _modPlayingFl = true;
+}
+
+void SoundManager::loadMusic(const Common::String &file) {
+ if (_music._active)
+ delMusic();
+
+ Common::File f;
+ if (_vm->getPlatform() == Common::kPlatformOS2 || _vm->getPlatform() == Common::kPlatformBeOS) {
+ Common::String filename = Common::String::format("%s.MOD", file.c_str());
+
+ if (!f.open(filename))
+ error("Error opening file %s", filename.c_str());
+
+ Modules::Module *module;
+ Audio::AudioStream *modStream = Audio::makeProtrackerStream(&f, 0, 44100, true, &module);
+
+ // WORKAROUND: This song is played at the empty lot where the
+ // bank robbers have left the helicopter. The MOD file appears
+ // to be slightly broken. Almost half of it is just the same
+ // noise repeating. We fix this by only playing the working
+ // part of it. The result is pretty close to the Windows music.
+ if (file.equalsIgnoreCase("cadavre")) {
+ module->songlen = 3;
+ }
+
+ _vm->_mixer->playStream(Audio::Mixer::kMusicSoundType, &_musicHandle, modStream);
+
+ } else {
+ Common::String filename = Common::String::format("%s.TWA", file.c_str());
+
+ if (!f.open(filename))
+ error("Error opening file %s", filename.c_str());
+
+ Audio::AudioStream *twaStream = makeTwaStream(file.c_str(), &f);
+ _vm->_mixer->playStream(Audio::Mixer::kMusicSoundType, &_musicHandle, twaStream);
+ f.close();
+ }
+
+ _music._active = true;
+}
+
+void SoundManager::playMusic() {
+}
+
+void SoundManager::stopMusic() {
+ _vm->_mixer->stopHandle(_musicHandle);
+}
+
+void SoundManager::delMusic() {
+ _music._active = false;
+}
+
+void SoundManager::checkSounds() {
+ checkVoiceActivity();
+}
+
+/**
+ * Checks voices to see if they're finished
+ */
+void SoundManager::checkVoiceActivity() {
+ // Check the status of each voice.
+ bool hasActiveVoice = false;
+ for (int i = 0; i < VOICE_COUNT; ++i) {
+ checkVoiceStatus(i);
+ hasActiveVoice |= _voice[i]._status;
+ }
+
+ if (!hasActiveVoice && _soundFl) {
+ _soundFl = false;
+ _currentSoundIndex = 0;
+ }
+}
+
+bool SoundManager::mixVoice(int voiceId, int voiceMode, bool dispTxtFl) {
+ int fileNumber;
+ int oldMusicVol;
+ bool breakFlag;
+ Common::String prefix;
+ Common::String filename;
+ Common::File f;
+ size_t catPos, catLen;
+
+ fileNumber = voiceId;
+ if (_voiceOffFl)
+ return false;
+
+ if ((voiceMode == 1 || voiceMode == 2)
+ && ( voiceId == 4 || voiceId == 16 || voiceId == 121
+ || voiceId == 142 || voiceId == 182 || voiceId == 191
+ || voiceId == 212 || voiceId == 225 || voiceId == 239
+ || voiceId == 245 || voiceId == 297 || voiceId == 308
+ || voiceId == 333 || voiceId == 348 || voiceId == 352
+ || voiceId == 358 || voiceId == 364 || voiceId == 371
+ || voiceId == 394 || voiceId == 414 || voiceId == 429
+ || voiceId == 442 || voiceId == 446 || voiceId == 461
+ || voiceId == 468 || voiceId == 476 || voiceId == 484
+ || voiceId == 491 || voiceId == 497 || voiceId == 501
+ || voiceId == 511 || voiceId == 520 || voiceId == 536
+ || voiceId == 554 || voiceId == 566 || voiceId == 573
+ || voiceId == 632 || voiceId == 645))
+ fileNumber = 684;
+
+ if (voiceMode == 1 || voiceMode == 2)
+ prefix = "DF";
+ else if (voiceMode == 3)
+ prefix = "IF";
+ else if (voiceMode == 4)
+ prefix = "TF";
+ else if (voiceMode == 5)
+ prefix = "OF";
+
+ // BeOS and OS/2 versions are using a slightly different speech order during intro
+ // This map those values to the ones used by the Win95 and Linux versions
+ int mappedFileNumber = fileNumber;
+ if (voiceMode == 3 && (_vm->getPlatform() == Common::kPlatformOS2 || _vm->getPlatform() == Common::kPlatformBeOS)) {
+ if (fileNumber == 4)
+ mappedFileNumber = 0;
+ else if (fileNumber > 4)
+ mappedFileNumber = fileNumber - 1;
+ }
+
+ filename = Common::String::format("%s%d", prefix.c_str(), mappedFileNumber);
+
+ bool fileFoundFl = false;
+ _vm->_fileIO->searchCat(filename + ".WAV", RES_VOI, fileFoundFl);
+ if (fileFoundFl) {
+ if (_vm->getPlatform() == Common::kPlatformOS2 || _vm->getPlatform() == Common::kPlatformBeOS)
+ filename = "ENG_VOI.RES";
+ // Win95 and Linux versions uses another set of names
+ else if (_vm->_globals->_language == LANG_FR)
+ filename = "RES_VFR.RES";
+ else if (_vm->_globals->_language == LANG_EN)
+ filename = "RES_VAN.RES";
+ else if (_vm->_globals->_language == LANG_SP)
+ filename = "RES_VES.RES";
+
+ catPos = _vm->_fileIO->_catalogPos;
+ catLen = _vm->_fileIO->_catalogSize;
+ } else {
+ _vm->_fileIO->searchCat(filename + ".APC", RES_VOI, fileFoundFl);
+ if (fileFoundFl) {
+ if (_vm->getPlatform() == Common::kPlatformOS2 || _vm->getPlatform() == Common::kPlatformBeOS)
+ filename = "ENG_VOI.RES";
+ // Win95 and Linux versions uses another set of names
+ else if (_vm->_globals->_language == LANG_FR)
+ filename = "RES_VFR.RES";
+ else if (_vm->_globals->_language == LANG_EN)
+ filename = "RES_VAN.RES";
+ else if (_vm->_globals->_language == LANG_SP)
+ filename = "RES_VES.RES";
+
+ catPos = _vm->_fileIO->_catalogPos;
+ catLen = _vm->_fileIO->_catalogSize;
+ } else {
+ _vm->_fileIO->searchCat(filename + ".RAW", RES_VOI, fileFoundFl);
+ if (fileFoundFl) {
+ if (_vm->getPlatform() == Common::kPlatformOS2 || _vm->getPlatform() == Common::kPlatformBeOS)
+ filename = "ENG_VOI.RES";
+ // Win95 and Linux versions uses another set of names
+ else if (_vm->_globals->_language == LANG_FR)
+ filename = "RES_VFR.RES";
+ else if (_vm->_globals->_language == LANG_EN)
+ filename = "RES_VAN.RES";
+ else if (_vm->_globals->_language == LANG_SP)
+ filename = "RES_VES.RES";
+
+ catPos = _vm->_fileIO->_catalogPos;
+ catLen = _vm->_fileIO->_catalogSize;
+ } else {
+ if (!f.exists(filename + ".WAV")) {
+ if (!f.exists(filename + ".APC"))
+ return false;
+ filename = filename + ".APC";
+ } else
+ filename = filename + ".WAV";
+
+ catPos = 0;
+ catLen = 0;
+ }
+ }
+ }
+ oldMusicVol = _musicVolume;
+ if (!loadVoice(filename, catPos, catLen, _sWav[20])) {
+ // This case only concerns the English Win95 demo
+ // If it's not possible to load the voice, we force the active flag
+ // to false in order to make sure the missing buffer won't be played
+ // accidentally later
+ _sWav[20]._active = false;
+ } else {
+ _sWav[20]._active = true;
+
+ // Reduce music volume during speech
+ if (!_musicOffFl && _musicVolume > 2) {
+ _musicVolume -= _musicVolume * 9 / 20;
+ setMODMusicVolume(_musicVolume);
+ }
+ }
+ playVoice();
+
+ _vm->_events->_escKeyFl = false;
+
+ // Loop for playing voice
+ breakFlag = false;
+ do {
+ if (_specialSoundNum != 4 && !_skipRefreshFl)
+ _vm->_events->refreshScreenAndEvents();
+ if (_vm->_events->getMouseButton())
+ break;
+ _vm->_events->refreshEvents();
+ if (_vm->_events->_escKeyFl)
+ break;
+ // We only check the voice status if the file has been loaded properly
+ // This avoids skipping completely the talk animations in the Win95 UK Demo
+ if (!checkVoiceStatus(2) && _sWav[20]._active)
+ breakFlag = true;
+ // This is specific to the Win95 UK Demo again: if nothing is displayed,
+ // don't wait for a click event.
+ if (!_sWav[20]._active && !dispTxtFl)
+ break;
+ } while (!_vm->shouldQuit() && !breakFlag);
+
+
+ stopVoice(2);
+ removeWavSample(20);
+
+ // Speech is over, set the music volume back to normal
+ _musicVolume = oldMusicVol;
+ if (!_musicOffFl && _musicVolume > 2) {
+ setMODMusicVolume(_musicVolume);
+ }
+ _vm->_events->_escKeyFl = false;
+ _skipRefreshFl = false;
+ return true;
+}
+
+void SoundManager::removeSample(int soundIndex) {
+ if (checkVoiceStatus(1))
+ stopVoice(1);
+ if (checkVoiceStatus(2))
+ stopVoice(2);
+ if (checkVoiceStatus(3))
+ stopVoice(3);
+ removeWavSample(soundIndex);
+ _sound[soundIndex]._active = false;
+}
+
+void SoundManager::playSoundFile(const Common::String &file) {
+ if (_soundOffFl)
+ return;
+
+ // Fallback for the menu option.
+ // The BeOS and OS/2 versions don't play sound at this point.
+ // sound20 sounds very close to bruit2 from the linux and Win95 versions.
+ Common::File f;
+ Common::String filename;
+ if (file == "bruit2.wav" && !f.exists(file))
+ filename = "sound20.wav";
+ else
+ filename = file;
+
+ if (_soundFl)
+ delWav(_currentSoundIndex);
+ loadWav(filename, 1);
+ playWav(1);
+}
+
+void SoundManager::directPlayWav(const Common::String &file) {
+ if (_soundOffFl)
+ return;
+
+ loadWav(file, 1);
+ playWav(1);
+}
+
+void SoundManager::setMODSampleVolume() {
+ for (int idx = 0; idx < SWAV_COUNT; ++idx) {
+ if (idx != 20 && _sWav[idx]._active) {
+ int volume = _soundVolume * 255 / 16;
+ _vm->_mixer->setChannelVolume(_sWav[idx]._soundHandle, volume);
+ }
+ }
+}
+
+void SoundManager::setMODVoiceVolume() {
+ if (_sWav[20]._active) {
+ int volume = _voiceVolume * 255 / 16;
+ _vm->_mixer->setChannelVolume(_sWav[20]._soundHandle, volume);
+ }
+}
+
+void SoundManager::setMODMusicVolume(int volume) {
+ if (_vm->_mixer->isSoundHandleActive(_musicHandle))
+ _vm->_mixer->setChannelVolume(_musicHandle, volume * 255 / 16);
+}
+
+void SoundManager::loadSample(int wavIndex, const Common::String &file) {
+ loadWavSample(wavIndex, file, false);
+ _sound[wavIndex]._active = true;
+}
+
+void SoundManager::playSample(int wavIndex, int voiceMode) {
+ if (_soundOffFl || !_sound[wavIndex]._active)
+ return;
+
+ if (_soundFl)
+ delWav(_currentSoundIndex);
+
+ switch (voiceMode) {
+ case 5:
+ case 8:
+ // Case added to identify the former PLAY_SAMPLE2 calls
+ case 9:
+ if (checkVoiceStatus(1))
+ stopVoice(1);
+ playWavSample(1, wavIndex);
+ break;
+ case 6:
+ if (checkVoiceStatus(2))
+ stopVoice(1);
+ playWavSample(2, wavIndex);
+ break;
+ case 7:
+ if (checkVoiceStatus(3))
+ stopVoice(1);
+ playWavSample(3, wavIndex);
+ break;
+ default:
+ break;
+ }
+}
+
+bool SoundManager::checkVoiceStatus(int voiceIndex) {
+ if (_voice[voiceIndex]._status) {
+ int wavIndex = _voice[voiceIndex]._wavIndex;
+ if (_sWav[wavIndex]._audioStream && _sWav[wavIndex]._audioStream->endOfStream())
+ stopVoice(voiceIndex);
+ }
+
+ return _voice[voiceIndex]._status;
+}
+
+void SoundManager::stopVoice(int voiceIndex) {
+ if (_voice[voiceIndex]._status) {
+ _voice[voiceIndex]._status = false;
+ int wavIndex = _voice[voiceIndex]._wavIndex;
+ if (_sWav[wavIndex]._active && _sWav[wavIndex]._freeSampleFl)
+ removeWavSample(wavIndex);
+ }
+ _voice[voiceIndex]._status = false;
+}
+
+void SoundManager::playVoice() {
+ if (!_sWav[20]._active)
+ return;
+
+ if (!_voice[2]._status) {
+ int wavIndex = _voice[2]._wavIndex;
+ if (_sWav[wavIndex]._active && _sWav[wavIndex]._freeSampleFl)
+ removeWavSample(wavIndex);
+ }
+
+ playWavSample(2, 20);
+}
+
+bool SoundManager::removeWavSample(int wavIndex) {
+ if (!_sWav[wavIndex]._active)
+ return false;
+
+ _vm->_mixer->stopHandle(_sWav[wavIndex]._soundHandle);
+ delete _sWav[wavIndex]._audioStream;
+ _sWav[wavIndex]._audioStream = NULL;
+ _sWav[wavIndex]._active = false;
+
+ return true;
+}
+
+bool SoundManager::loadVoice(const Common::String &filename, size_t fileOffset, size_t entryLength, SwavItem &item) {
+ Common::File f;
+ if (!f.open(filename)) {
+ // Fallback to APC...
+ if (!f.open(setExtension(filename, ".APC"))) {
+ // The English demo doesn't include the speech file.
+ // This avoids it to crash when discussing with other characters
+ if (!_vm->getIsDemo())
+ error("Could not open %s for reading", filename.c_str());
+ return false;
+ }
+ }
+
+ f.seek(fileOffset);
+ item._audioStream = makeSoundStream(f.readStream((entryLength == 0) ? f.size() : entryLength));
+ f.close();
+
+ return true;
+}
+
+void SoundManager::loadWavSample(int wavIndex, const Common::String &filename, bool freeSample) {
+ if (_sWav[wavIndex]._active)
+ removeWavSample(wavIndex);
+
+ if (loadVoice(filename, 0, 0, _sWav[wavIndex])) {
+ _sWav[wavIndex]._active = true;
+ _sWav[wavIndex]._freeSampleFl = freeSample;
+ } else{
+ _sWav[wavIndex]._active = false;
+ }
+}
+
+void SoundManager::loadWav(const Common::String &file, int wavIndex) {
+ loadWavSample(wavIndex, file, true);
+}
+
+void SoundManager::playWav(int wavIndex) {
+ if (_soundFl || _soundOffFl)
+ return;
+
+ _soundFl = true;
+ _currentSoundIndex = wavIndex;
+ playWavSample(1, wavIndex);
+}
+
+void SoundManager::delWav(int wavIndex) {
+ if (!removeWavSample(wavIndex))
+ return;
+
+ if (checkVoiceStatus(1))
+ stopVoice(1);
+
+ _currentSoundIndex = 0;
+ _soundFl = false;
+}
+
+void SoundManager::playWavSample(int voiceIndex, int wavIndex) {
+ if (!_sWav[wavIndex]._active)
+ warning("Bad handle");
+
+ if (_voice[voiceIndex]._status && _sWav[wavIndex]._active && _sWav[wavIndex]._freeSampleFl)
+ removeWavSample(wavIndex);
+
+ _voice[voiceIndex]._status = true;
+ _voice[voiceIndex]._wavIndex = wavIndex;
+
+ int volume = (voiceIndex == 2) ? _voiceVolume * 255 / 16 : _soundVolume * 255 / 16;
+
+ // If the handle is still in use, stop it. Otherwise we'll lose the
+ // handle to that sound. This can currently happen (but probably
+ // shouldn't) when skipping a movie.
+ if (_vm->_mixer->isSoundHandleActive(_sWav[wavIndex]._soundHandle))
+ _vm->_mixer->stopHandle(_sWav[wavIndex]._soundHandle);
+
+ // Start the voice playing
+ _sWav[wavIndex]._audioStream->rewind();
+ _vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, &_sWav[wavIndex]._soundHandle,
+ _sWav[wavIndex]._audioStream, -1, volume, 0, DisposeAfterUse::NO);
+}
+
+void SoundManager::syncSoundSettings() {
+ bool muteAll = false;
+ if (ConfMan.hasKey("mute"))
+ muteAll = ConfMan.getBool("mute");
+
+ // Update the mute settings
+ _musicOffFl = muteAll || (ConfMan.hasKey("music_mute") && ConfMan.getBool("music_mute"));
+ _soundOffFl = muteAll || (ConfMan.hasKey("sfx_mute") && ConfMan.getBool("sfx_mute"));
+ _voiceOffFl = muteAll || (ConfMan.hasKey("speech_mute") && ConfMan.getBool("speech_mute"));
+
+ // Update the volume levels
+ _musicVolume = MIN(255, ConfMan.getInt("music_volume")) * 16 / 255;
+ _soundVolume = MIN(255, ConfMan.getInt("sfx_volume")) * 16 / 255;
+ _voiceVolume = MIN(255, ConfMan.getInt("speech_volume")) * 16 / 255;
+
+ // Update any active sounds
+ for (int idx = 0; idx < SWAV_COUNT; ++idx) {
+ if (_sWav[idx]._active) {
+ int volume = (idx == 20) ? (_voiceVolume * 255 / 16) : (_soundVolume * 255 / 16);
+ _vm->_mixer->setChannelVolume(_sWav[idx]._soundHandle, volume);
+ }
+ }
+ if (_vm->_mixer->isSoundHandleActive(_musicHandle)) {
+ _vm->_mixer->setChannelVolume(_musicHandle, _musicVolume * 255 / 16);
+ }
+}
+
+void SoundManager::updateScummVMSoundSettings() {
+ ConfMan.setBool("mute", _musicOffFl && _soundOffFl && _voiceOffFl);
+ ConfMan.setBool("music_mute", _musicOffFl);
+ ConfMan.setBool("sfx_mute", _soundOffFl);
+ ConfMan.setBool("speech_mute", _voiceOffFl);
+
+ ConfMan.setInt("music_volume", _musicVolume * 255 / 16);
+ ConfMan.setInt("sfx_volume", _soundVolume * 255 / 16);
+ ConfMan.setInt("speech_volume", _voiceVolume * 255 / 16);
+
+ ConfMan.flushToDisk();
+}
+
+/**
+ * Creates an audio stream based on a passed raw stream
+ */
+Audio::RewindableAudioStream *SoundManager::makeSoundStream(Common::SeekableReadStream *stream) {
+ if (_vm->getPlatform() == Common::kPlatformWindows)
+ return makeAPCStream(stream, DisposeAfterUse::YES);
+ else if (_vm->getPlatform() == Common::kPlatformLinux)
+ return Audio::makeWAVStream(stream, DisposeAfterUse::YES);
+ else
+ return Audio::makeRawStream(stream, 22050, Audio::FLAG_UNSIGNED, DisposeAfterUse::YES);
+}
+
+// Blatant rip from gob engine. Hi DrMcCoy!
+Common::String SoundManager::setExtension(const Common::String &str, const Common::String &ext) {
+ if (str.empty())
+ return str;
+
+ const char *dot = strrchr(str.c_str(), '.');
+ if (dot)
+ return Common::String(str.c_str(), dot - str.c_str()) + ext;
+
+ return str + ext;
+}
+} // End of namespace Hopkins