aboutsummaryrefslogtreecommitdiff
path: root/engines/sherlock/sound.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/sherlock/sound.cpp')
-rw-r--r--engines/sherlock/sound.cpp327
1 files changed, 327 insertions, 0 deletions
diff --git a/engines/sherlock/sound.cpp b/engines/sherlock/sound.cpp
new file mode 100644
index 0000000000..66b5b5bf5c
--- /dev/null
+++ b/engines/sherlock/sound.cpp
@@ -0,0 +1,327 @@
+/* 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 "sherlock/sherlock.h"
+#include "sherlock/sound.h"
+#include "common/config-manager.h"
+#include "audio/audiostream.h"
+#include "common/algorithm.h"
+#include "audio/mixer.h"
+#include "audio/decoders/raw.h"
+#include "audio/decoders/aiff.h"
+#include "audio/decoders/wave.h"
+
+namespace Sherlock {
+
+static const int8 creativeADPCM_ScaleMap[64] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 0, -1, -2, -3, -4, -5, -6, -7,
+ 1, 3, 5, 7, 9, 11, 13, 15, -1, -3, -5, -7, -9, -11, -13, -15,
+ 2, 6, 10, 14, 18, 22, 26, 30, -2, -6, -10, -14, -18, -22, -26, -30,
+ 4, 12, 20, 28, 36, 44, 52, 60, -4, -12, -20, -28, -36, -44, -52, -60
+};
+
+static const uint8 creativeADPCM_AdjustMap[64] = {
+ 0, 0, 0, 0, 0, 16, 16, 16,
+ 0, 0, 0, 0, 0, 16, 16, 16,
+ 240, 0, 0, 0, 0, 16, 16, 16,
+ 240, 0, 0, 0, 0, 16, 16, 16,
+ 240, 0, 0, 0, 0, 16, 16, 16,
+ 240, 0, 0, 0, 0, 16, 16, 16,
+ 240, 0, 0, 0, 0, 0, 0, 0,
+ 240, 0, 0, 0, 0, 0, 0, 0
+};
+
+/*----------------------------------------------------------------*/
+
+Sound::Sound(SherlockEngine *vm, Audio::Mixer *mixer) : _vm(vm), _mixer(mixer) {
+ _digitized = false;
+ _voices = 0;
+ _soundPlaying = false;
+ _speechPlaying = false;
+ _curPriority = 0;
+ _soundVolume = 255;
+
+ _soundOn = true;
+ _speechOn = true;
+
+ if (IS_3DO) {
+ // 3DO: we don't need to prepare anything for sound
+ return;
+ }
+
+ _vm->_res->addToCache("MUSIC.LIB");
+ if (!_vm->_interactiveFl)
+ _vm->_res->addToCache("TITLE.SND");
+ else {
+ _vm->_res->addToCache("MUSIC.LIB");
+
+ if (IS_ROSE_TATTOO) {
+ _vm->_res->addToCache("SOUND.LIB");
+ } else {
+ _vm->_res->addToCache("SND.SND");
+
+ if (!_vm->isDemo()) {
+ _vm->_res->addToCache("TITLE.SND");
+ _vm->_res->addToCache("EPILOGUE.SND");
+ }
+ }
+ }
+}
+
+void Sound::syncSoundSettings() {
+ _digitized = !ConfMan.getBool("mute");
+ _speechOn = !ConfMan.getBool("mute") && !ConfMan.getBool("speech_mute");
+ _voices = _speechOn ? 1 : 0;
+}
+
+void Sound::loadSound(const Common::String &name, int priority) {
+ // No implementation required in ScummVM
+ //warning("loadSound");
+}
+
+byte Sound::decodeSample(byte sample, byte &reference, int16 &scale) {
+ int16 samp = sample + scale;
+ int16 ref = 0;
+
+ // clip bad ADPCM-4 sample
+ samp = CLIP<int16>(samp, 0, 63);
+
+ ref = reference + creativeADPCM_ScaleMap[samp];
+ if (ref > 0xff) {
+ reference = 0xff;
+ } else {
+ if (ref < 0x00) {
+ reference = 0;
+ } else {
+ reference = (uint8)(ref & 0xff);
+ }
+ }
+
+ scale = (scale + creativeADPCM_AdjustMap[samp]) & 0xff;
+ return reference;
+}
+
+bool Sound::playSound(const Common::String &name, WaitType waitType, int priority, const char *libraryFilename) {
+ stopSound();
+
+ Common::String filename = name;
+ if (!filename.contains('.')) {
+ if (!IS_3DO) {
+ if (IS_SERRATED_SCALPEL) {
+ filename += ".SND";
+ } else {
+ filename += ".WAV";
+ }
+ } else {
+ // 3DO uses .aiff extension
+ filename += ".AIFF";
+ if (!filename.contains('/')) {
+ // if no directory was given, use the room sounds directory
+ filename = "rooms/sounds/" + filename;
+ }
+ }
+ }
+
+ Audio::SoundHandle soundHandle = (IS_SERRATED_SCALPEL) ? _scalpelEffectsHandle : getFreeSoundHandle();
+ if (!playSoundResource(filename, libraryFilename, Audio::Mixer::kSFXSoundType, soundHandle))
+ error("Could not find sound resource - %s", filename.c_str());
+
+ _soundPlaying = true;
+ _curPriority = priority;
+
+ if (waitType == WAIT_RETURN_IMMEDIATELY) {
+ return true;
+ }
+
+ bool retval = true;
+ do {
+ _vm->_events->pollEvents();
+ g_system->delayMillis(10);
+ if ((waitType == WAIT_KBD_OR_FINISH) && _vm->_events->kbHit()) {
+ retval = false;
+ break;
+ }
+ } while (!_vm->shouldQuit() && _mixer->isSoundHandleActive(soundHandle));
+
+ _soundPlaying = false;
+ _mixer->stopHandle(soundHandle);
+
+ return retval;
+}
+
+void Sound::playAiff(const Common::String &name, int volume, bool loop) {
+ Common::File *file = new Common::File();
+ if (!file->open(name)) {
+ delete file;
+ return;
+ }
+ Audio::AudioStream *stream;
+ Audio::RewindableAudioStream *audioStream = Audio::makeAIFFStream(file, DisposeAfterUse::YES);
+ if (loop) {
+ Audio::AudioStream *loopingStream = Audio::makeLoopingAudioStream(audioStream, 0);
+ stream = loopingStream;
+ } else {
+ stream = audioStream;
+ }
+ stopAiff();
+ _mixer->playStream(Audio::Mixer::kSFXSoundType, &_aiffHandle, stream, -1, volume);
+}
+
+void Sound::stopAiff() {
+ if (_mixer->isSoundHandleActive(_aiffHandle)) {
+ _mixer->stopHandle(_aiffHandle);
+ }
+}
+
+void Sound::playLoadedSound(int bufNum, WaitType waitType) {
+ if (IS_SERRATED_SCALPEL) {
+ if (_mixer->isSoundHandleActive(_scalpelEffectsHandle) && (_curPriority > _vm->_scene->_sounds[bufNum]._priority))
+ return;
+
+ stopSound();
+ }
+
+ playSound(_vm->_scene->_sounds[bufNum]._name, waitType, _vm->_scene->_sounds[bufNum]._priority);
+}
+
+void Sound::freeLoadedSounds() {
+ // As sounds are played with DisposeAfterUse::YES, stopping the sounds also
+ // frees them
+ stopSound();
+}
+
+void Sound::stopSound() {
+ if (IS_SERRATED_SCALPEL) {
+ _mixer->stopHandle(_scalpelEffectsHandle);
+ } else {
+ for (int i = 0; i < MAX_MIXER_CHANNELS; i++)
+ _mixer->stopHandle(_tattooEffectsHandle[i]);
+ }
+}
+
+void Sound::freeDigiSound() {
+ _soundPlaying = false;
+}
+
+Audio::SoundHandle Sound::getFreeSoundHandle() {
+ for (int i = 0; i < MAX_MIXER_CHANNELS; i++) {
+ if (!_mixer->isSoundHandleActive(_tattooEffectsHandle[i]))
+ return _tattooEffectsHandle[i];
+ }
+
+ error("getFreeSoundHandle: No sound handle found");
+}
+
+void Sound::setVolume(int volume) {
+ warning("TODO: setVolume - %d", volume);
+}
+
+void Sound::playSpeech(const Common::String &name) {
+ Resources &res = *_vm->_res;
+ Scene &scene = *_vm->_scene;
+ stopSpeech();
+
+ // TODO: Technically Scalpel has an sfx command which I've set to call this method because it sets the
+ // _voice variable as if it were speech. Need to do a play-through of Scalpel and see if it's ever called.
+ // If so, will need to enhance this method to handle the Serrated Scalpel voice resources
+ assert(IS_ROSE_TATTOO);
+
+ // Figure out which speech library to use
+ Common::String libraryName = Common::String::format("speech%02d.lib", scene._currentScene);
+ if ((!scumm_strnicmp(name.c_str(), "SLVE12S", 7)) || (!scumm_strnicmp(name.c_str(), "WATS12X", 7))
+ || (!scumm_strnicmp(name.c_str(), "HOLM12X", 7)))
+ libraryName = "SPEECH12.LIB";
+
+ // If the speech library file doesn't even exist, then we can't play anything
+ Common::File f;
+ if (!f.exists(libraryName))
+ return;
+
+ // Ensure the given library is in the cache
+ res.addToCache(libraryName);
+
+ if (playSoundResource(name, libraryName, Audio::Mixer::kSpeechSoundType, _speechHandle))
+ _speechPlaying = true;
+}
+
+void Sound::stopSpeech() {
+ _mixer->stopHandle(_speechHandle);
+ _speechPlaying = false;
+}
+
+bool Sound::isSpeechPlaying() {
+ _speechPlaying = _mixer->isSoundHandleActive(_speechHandle);
+ return _speechPlaying;
+}
+
+bool Sound::playSoundResource(const Common::String &name, const Common::String &libFilename,
+ Audio::Mixer::SoundType soundType, Audio::SoundHandle &handle) {
+ Resources &res = *_vm->_res;
+ Common::SeekableReadStream *stream = libFilename.empty() ? res.load(name) : res.load(name, libFilename, true);
+ if (!stream)
+ return false;
+
+ Audio::AudioStream *audioStream;
+ if (IS_ROSE_TATTOO && soundType == Audio::Mixer::kSpeechSoundType) {
+ audioStream = Audio::makeRawStream(stream, 11025, Audio::FLAG_UNSIGNED);
+ } else if (IS_3DO) {
+ // 3DO: AIFF file
+ audioStream = Audio::makeAIFFStream(stream, DisposeAfterUse::YES);
+ } else if (IS_SERRATED_SCALPEL) {
+ stream->skip(2);
+ int size = stream->readUint32BE();
+ int rate = stream->readUint16BE();
+ byte *data = (byte *)malloc(size);
+ byte *ptr = data;
+ stream->read(ptr, size);
+ delete stream;
+
+ assert(size > 2);
+
+ byte *decoded = (byte *)malloc((size - 1) * 2);
+
+ // Holmes uses Creative ADPCM 4-bit data
+ int counter = 0;
+ byte reference = ptr[0];
+ int16 scale = 0;
+
+ for (int i = 1; i < size; i++) {
+ decoded[counter++] = decodeSample((ptr[i] >> 4) & 0x0f, reference, scale);
+ decoded[counter++] = decodeSample((ptr[i] >> 0) & 0x0f, reference, scale);
+ }
+
+ free(data);
+
+ audioStream = Audio::makeRawStream(decoded, (size - 2) * 2, rate, Audio::FLAG_UNSIGNED, DisposeAfterUse::YES);
+ } else {
+ audioStream = Audio::makeWAVStream(stream, DisposeAfterUse::YES);
+ }
+
+ if (!audioStream)
+ return false;
+
+ _mixer->playStream(soundType, &handle, audioStream, -1, Audio::Mixer::kMaxChannelVolume);
+ return true;
+}
+
+} // End of namespace Sherlock
+