/* 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 _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); if (!_vm->_fileManager->searchCat(filename + ".WAV", RES_VOI)) { 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->_globals->_catalogPos; catLen = _vm->_globals->_catalogSize; } else if (!_vm->_fileManager->searchCat(filename + ".APC", RES_VOI)) { 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->_globals->_catalogPos; catLen = _vm->_globals->_catalogSize; } else if (!_vm->_fileManager->searchCat(filename + ".RAW", RES_VOI)) { 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->_globals->_catalogPos; catLen = _vm->_globals->_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->_eventsManager->_escKeyFl = false; // Loop for playing voice breakFlag = false; do { if (_specialSoundNum != 4 && !_skipRefreshFl) _vm->_eventsManager->refreshScreenAndEvents(); if (_vm->_eventsManager->getMouseButton()) break; _vm->_eventsManager->refreshEvents(); if (_vm->_eventsManager->_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->_eventsManager->_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 != NULL && _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