diff options
author | Willem Jan Palenstijn | 2013-04-18 23:55:01 +0200 |
---|---|---|
committer | Willem Jan Palenstijn | 2013-05-08 20:47:44 +0200 |
commit | 102299630901d08a44ef3aec367fcbcae065b9fe (patch) | |
tree | 98db4bbe0c54176c0a43e2f5076f8b3d63b8065c /engines/hopkins/sound.cpp | |
parent | 583f9abaf98f64895546b75573e9442ca47426e3 (diff) | |
parent | 78ba3210a57094086d44b25d5a8507c33ce9bef3 (diff) | |
download | scummvm-rg350-102299630901d08a44ef3aec367fcbcae065b9fe.tar.gz scummvm-rg350-102299630901d08a44ef3aec367fcbcae065b9fe.tar.bz2 scummvm-rg350-102299630901d08a44ef3aec367fcbcae065b9fe.zip |
Merge branch 'master'
Diffstat (limited to 'engines/hopkins/sound.cpp')
-rw-r--r-- | engines/hopkins/sound.cpp | 916 |
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 |