aboutsummaryrefslogtreecommitdiff
path: root/engines/agos/sound.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/agos/sound.cpp')
-rw-r--r--engines/agos/sound.cpp714
1 files changed, 714 insertions, 0 deletions
diff --git a/engines/agos/sound.cpp b/engines/agos/sound.cpp
new file mode 100644
index 0000000000..42c2df1723
--- /dev/null
+++ b/engines/agos/sound.cpp
@@ -0,0 +1,714 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/stdafx.h"
+
+#include "common/file.h"
+#include "common/util.h"
+
+#include "agos/agos.h"
+#include "agos/sound.h"
+
+#include "sound/adpcm.h"
+#include "sound/audiostream.h"
+#include "sound/flac.h"
+#include "sound/mp3.h"
+#include "sound/voc.h"
+#include "sound/vorbis.h"
+#include "sound/wave.h"
+
+using Common::File;
+
+namespace Simon {
+
+#define SOUND_BIG_ENDIAN true
+
+class BaseSound {
+protected:
+ File *_file;
+ uint32 *_offsets;
+ Audio::Mixer *_mixer;
+ bool _freeOffsets;
+
+public:
+ BaseSound(Audio::Mixer *mixer, File *file, uint32 base = 0, bool bigendian = false);
+ BaseSound(Audio::Mixer *mixer, File *file, uint32 *offsets, bool bigendian = false);
+ virtual ~BaseSound();
+ virtual void playSound(uint sound, Audio::SoundHandle *handle, byte flags) = 0;
+};
+
+class WavSound : public BaseSound {
+public:
+ WavSound(Audio::Mixer *mixer, File *file, uint32 base = 0, bool bigendian = false) : BaseSound(mixer, file, base, bigendian) {};
+ WavSound(Audio::Mixer *mixer, File *file, uint32 *offsets) : BaseSound(mixer, file, offsets) {};
+ void playSound(uint sound, Audio::SoundHandle *handle, byte flags);
+};
+
+class VocSound : public BaseSound {
+public:
+ VocSound(Audio::Mixer *mixer, File *file, uint32 base = 0, bool bigendian = false) : BaseSound(mixer, file, base, bigendian) {};
+ void playSound(uint sound, Audio::SoundHandle *handle, byte flags);
+};
+class RawSound : public BaseSound {
+public:
+ RawSound(Audio::Mixer *mixer, File *file, uint32 base = 0, bool bigendian = false) : BaseSound(mixer, file, base, bigendian) {};
+ void playSound(uint sound, Audio::SoundHandle *handle, byte flags);
+};
+
+BaseSound::BaseSound(Audio::Mixer *mixer, File *file, uint32 base, bool bigendian) {
+ _mixer = mixer;
+ _file = file;
+
+ uint res = 0;
+ uint32 size;
+
+ _file->seek(base + sizeof(uint32), SEEK_SET);
+ if (bigendian)
+ size = _file->readUint32BE();
+ else
+ size = _file->readUint32LE();
+
+ // The Feeble Files uses set amount of voice offsets
+ if (size == 0)
+ size = 40000;
+
+ res = size / sizeof(uint32);
+
+ _offsets = (uint32 *)malloc(size + sizeof(uint32));
+ _freeOffsets = true;
+
+ _file->seek(base, SEEK_SET);
+
+ if (_file->read(_offsets, size) != size)
+ error("BaseSound: Can't read offsets");
+
+ for (uint i = 0; i < res; i++) {
+#if defined(SCUMM_BIG_ENDIAN)
+ if (!(bigendian))
+ _offsets[i] = FROM_LE_32(_offsets[i]);
+#endif
+ if (bigendian)
+ _offsets[i] = TO_BE_32(_offsets[i]);
+ _offsets[i] += base;
+ }
+
+ // only needed for mp3
+ _offsets[res] = _file->size();
+}
+
+BaseSound::BaseSound(Audio::Mixer *mixer, File *file, uint32 *offsets, bool bigendian) {
+ _mixer = mixer;
+ _file = file;
+ _offsets = offsets;
+ _freeOffsets = false;
+}
+
+BaseSound::~BaseSound() {
+ if (_freeOffsets)
+ free(_offsets);
+ delete _file;
+}
+
+void WavSound::playSound(uint sound, Audio::SoundHandle *handle, byte flags) {
+ if (_offsets == NULL)
+ return;
+
+ _file->seek(_offsets[sound], SEEK_SET);
+
+ byte wavFlags;
+ int size, rate;
+ if (!Audio::loadWAVFromStream(*_file, size, rate, wavFlags))
+ error("playSound: Not a valid WAV file");
+
+ flags |= wavFlags;
+
+ byte *buffer = (byte *)malloc(size);
+ // Check whether malloc was successful.
+ // TODO: Maybe we can handle this more graceful, by reverting to a smaller
+ // buffer and reading the audio data one piece at a time?
+ if (buffer) {
+ _file->read(buffer, size);
+ _mixer->playRaw(handle, buffer, size, rate, flags | Audio::Mixer::FLAG_AUTOFREE);
+ }
+}
+
+void VocSound::playSound(uint sound, Audio::SoundHandle *handle, byte flags) {
+ if (_offsets == NULL)
+ return;
+
+ _file->seek(_offsets[sound], SEEK_SET);
+
+ int size, rate;
+ byte *buffer = Audio::loadVOCFromStream(*_file, size, rate);
+ _mixer->playRaw(handle, buffer, size, rate, flags | Audio::Mixer::FLAG_AUTOFREE);
+}
+
+void RawSound::playSound(uint sound, Audio::SoundHandle *handle, byte flags) {
+ if (_offsets == NULL)
+ return;
+
+ _file->seek(_offsets[sound], SEEK_SET);
+
+ uint size = _file->readUint32BE();
+ byte *buffer = (byte *)malloc(size);
+ // Check whether malloc was successful.
+ // TODO: Maybe we can handle this more graceful, by reverting to a smaller
+ // buffer and reading the audio data one piece at a time?
+ if (buffer) {
+ _file->read(buffer, size);
+ _mixer->playRaw(handle, buffer, size, 22050, flags | Audio::Mixer::FLAG_AUTOFREE);
+ }
+}
+
+#ifdef USE_MAD
+class MP3Sound : public BaseSound {
+public:
+ MP3Sound(Audio::Mixer *mixer, File *file, uint32 base = 0) : BaseSound(mixer, file, base) {};
+ void playSound(uint sound, Audio::SoundHandle *handle, byte flags);
+};
+
+void MP3Sound::playSound(uint sound, Audio::SoundHandle *handle, byte flags)
+{
+ if (_offsets == NULL)
+ return;
+
+ _file->seek(_offsets[sound], SEEK_SET);
+
+ int i = 1;
+ while (_offsets[sound + i] == _offsets[sound])
+ i++;
+
+ uint32 size = _offsets[sound + i] - _offsets[sound];
+
+ _mixer->playInputStream(Audio::Mixer::kSFXSoundType, handle, Audio::makeMP3Stream(_file, size));
+}
+#endif
+
+#ifdef USE_VORBIS
+class VorbisSound : public BaseSound {
+public:
+ VorbisSound(Audio::Mixer *mixer, File *file, uint32 base = 0) : BaseSound(mixer, file, base) {};
+ void playSound(uint sound, Audio::SoundHandle *handle, byte flags);
+};
+
+void VorbisSound::playSound(uint sound, Audio::SoundHandle *handle, byte flags)
+{
+ if (_offsets == NULL)
+ return;
+
+ _file->seek(_offsets[sound], SEEK_SET);
+
+ int i = 1;
+ while (_offsets[sound + i] == _offsets[sound])
+ i++;
+
+ uint32 size = _offsets[sound + i] - _offsets[sound];
+
+ _mixer->playInputStream(Audio::Mixer::kSFXSoundType, handle, Audio::makeVorbisStream(_file, size));
+}
+#endif
+
+#ifdef USE_FLAC
+class FlacSound : public BaseSound {
+public:
+ FlacSound(Audio::Mixer *mixer, File *file, uint32 base = 0) : BaseSound(mixer, file, base) {};
+ void playSound(uint sound, Audio::SoundHandle *handle, byte flags);
+};
+
+void FlacSound::playSound(uint sound, Audio::SoundHandle *handle, byte flags)
+{
+ if (_offsets == NULL)
+ return;
+
+ _file->seek(_offsets[sound], SEEK_SET);
+
+ int i = 1;
+ while (_offsets[sound + i] == _offsets[sound])
+ i++;
+
+ uint32 size = _offsets[sound + i] - _offsets[sound];
+
+ _mixer->playInputStream(Audio::Mixer::kSFXSoundType, handle, Audio::makeFlacStream(_file, size));
+}
+#endif
+
+Sound::Sound(SimonEngine *vm, const GameSpecificSettings *gss, Audio::Mixer *mixer)
+ : _vm(vm), _mixer(mixer) {
+ _voice = 0;
+ _effects = 0;
+
+ _effectsPaused = false;
+ _ambientPaused = false;
+ _sfx5Paused = false;
+
+ _filenums = 0;
+ _lastVoiceFile = 0;
+ _offsets = 0;
+
+ _hasEffectsFile = false;
+ _hasVoiceFile = false;
+
+ _ambientPlaying = 0;
+
+ if (_vm->getFeatures() & GF_TALKIE) {
+ loadVoiceFile(gss);
+
+ if (_vm->getGameType() == GType_SIMON1)
+ loadSfxFile(gss);
+ }
+}
+
+Sound::~Sound() {
+ delete _voice;
+ delete _effects;
+
+ free(_filenums);
+ free(_offsets);
+}
+
+void Sound::loadVoiceFile(const GameSpecificSettings *gss) {
+ // Game versions which use separate voice files
+ if (_vm->getGameType() == GType_FF || _vm->getGameId() == GID_SIMON1CD32)
+ return;
+
+ char filename[16];
+ File *file = new File();
+
+#ifdef USE_FLAC
+ if (!_hasVoiceFile) {
+ sprintf(filename, "%s.fla", gss->speech_filename);
+ file->open(filename);
+ if (file->isOpen()) {
+ _hasVoiceFile = true;
+ _voice = new FlacSound(_mixer, file);
+ }
+ }
+#endif
+#ifdef USE_MAD
+ if (!_hasVoiceFile) {
+ sprintf(filename, "%s.mp3", gss->speech_filename);
+ file->open(filename);
+ if (file->isOpen()) {
+ _hasVoiceFile = true;
+ _voice = new MP3Sound(_mixer, file);
+ }
+ }
+#endif
+#ifdef USE_VORBIS
+ if (!_hasVoiceFile) {
+ sprintf(filename, "%s.ogg", gss->speech_filename);
+ file->open(filename);
+ if (file->isOpen()) {
+ _hasVoiceFile = true;
+ _voice = new VorbisSound(_mixer, file);
+ }
+ }
+#endif
+ if (!_hasVoiceFile && _vm->getGameType() == GType_SIMON2) {
+ // for simon2 mac/amiga, only read index file
+ file->open("voices.idx");
+ if (file->isOpen() == true) {
+ int end = file->size();
+ _filenums = (uint16 *)malloc((end / 6 + 1) * 2);
+ _offsets = (uint32 *)malloc((end / 6 + 1) * 4);
+
+ for (int i = 1; i <= end / 6; i++) {
+ _filenums[i] = file->readUint16BE();
+ _offsets[i] = file->readUint32BE();
+ }
+ _hasVoiceFile = true;
+ }
+ }
+ if (!_hasVoiceFile) {
+ sprintf(filename, "%s.wav", gss->speech_filename);
+ file->open(filename);
+ if (file->isOpen()) {
+ _hasVoiceFile = true;
+ _voice = new WavSound(_mixer, file);
+ }
+ }
+ if (!_hasVoiceFile) {
+ sprintf(filename, "%s.voc", gss->speech_filename);
+ file->open(filename);
+ if (file->isOpen()) {
+ _hasVoiceFile = true;
+ _voice = new VocSound(_mixer, file);
+ }
+ }
+ if (!_hasVoiceFile) {
+ sprintf(filename, "%s", gss->speech_filename);
+ file->open(filename);
+ if (file->isOpen()) {
+ _hasVoiceFile = true;
+ if (_vm->getGameType() == GType_PP)
+ _voice = new WavSound(_mixer, file);
+ else
+ _voice = new VocSound(_mixer, file);
+ }
+ }
+}
+
+void Sound::loadSfxFile(const GameSpecificSettings *gss) {
+ char filename[16];
+ File *file = new File();
+
+#ifdef USE_MAD
+ if (!_hasEffectsFile) {
+ sprintf(filename, "%s.mp3", gss->effects_filename);
+ file->open(filename);
+ if (file->isOpen()) {
+ _hasEffectsFile = true;
+ _effects = new MP3Sound(_mixer, file);
+ }
+ }
+#endif
+#ifdef USE_VORBIS
+ if (!_hasEffectsFile) {
+ sprintf(filename, "%s.ogg", gss->effects_filename);
+ file->open(filename);
+ if (file->isOpen()) {
+ _hasEffectsFile = true;
+ _effects = new VorbisSound(_mixer, file);
+ }
+ }
+#endif
+#ifdef USE_FLAC
+ if (!_hasEffectsFile) {
+ sprintf(filename, "%s.fla", gss->effects_filename);
+ file->open(filename);
+ if (file->isOpen()) {
+ _hasEffectsFile = true;
+ _effects = new FlacSound(_mixer, file);
+ }
+ }
+#endif
+ if (!_hasEffectsFile) {
+ sprintf(filename, "%s.voc", gss->effects_filename);
+ file->open(filename);
+ if (file->isOpen()) {
+ _hasEffectsFile = true;
+ _effects = new VocSound(_mixer, file);
+ }
+ }
+ if (!_hasEffectsFile) {
+ sprintf(filename, "%s", gss->effects_filename);
+ file->open(filename);
+ if (file->isOpen()) {
+ _hasEffectsFile = true;
+ _effects = new VocSound(_mixer, file);
+ }
+ }
+}
+
+void Sound::readSfxFile(const char *filename) {
+ if (_hasEffectsFile)
+ return;
+
+ stopAll();
+
+ File *file = new File();
+ file->open(filename);
+
+ if (file->isOpen() == false) {
+ error("readSfxFile: Can't load sfx file %s", filename);
+ }
+
+ delete _effects;
+ if (_vm->getGameId() == GID_SIMON1CD32) {
+ _effects = new VocSound(_mixer, file, 0, SOUND_BIG_ENDIAN);
+ } else
+ _effects = new WavSound(_mixer, file);
+}
+
+void Sound::loadSfxTable(File *gameFile, uint32 base) {
+ stopAll();
+
+ if (_vm->getPlatform() == Common::kPlatformWindows)
+ _effects = new WavSound(_mixer, gameFile, base);
+ else
+ _effects = new VocSound(_mixer, gameFile, base);
+}
+
+void Sound::readVoiceFile(const char *filename) {
+ stopAll();
+
+ File *file = new File();
+ file->open(filename);
+
+ if (file->isOpen() == false)
+ error("readVoiceFile: Can't load voice file %s", filename);
+
+ delete _voice;
+ _voice = new RawSound(_mixer, file, 0, SOUND_BIG_ENDIAN);
+}
+
+void Sound::playVoice(uint sound) {
+ if (_filenums) {
+ if (_lastVoiceFile != _filenums[sound]) {
+ stopAll();
+
+ char filename[16];
+ _lastVoiceFile = _filenums[sound];
+ sprintf(filename, "voices%d.dat", _filenums[sound]);
+ File *file = new File();
+ file->open(filename);
+ if (file->isOpen() == false)
+ error("playVoice: Can't load voice file %s", filename);
+
+ delete _voice;
+ _voice = new WavSound(_mixer, file, _offsets);
+ }
+ }
+
+ if (!_voice)
+ return;
+
+ _mixer->stopHandle(_voiceHandle);
+ if (_vm->getGameType() == GType_PP) {
+ _voice->playSound(sound, &_voiceHandle, Audio::Mixer::FLAG_LOOP);
+ } else if (_vm->getGameType() == GType_FF || _vm->getGameId() == GID_SIMON1CD32) {
+ _voice->playSound(sound, &_voiceHandle, 0);
+ } else {
+ _voice->playSound(sound, &_voiceHandle, Audio::Mixer::FLAG_UNSIGNED);
+ }
+}
+
+void Sound::playEffects(uint sound) {
+ if (!_effects)
+ return;
+
+ if (_effectsPaused)
+ return;
+
+ _effects->playSound(sound, &_effectsHandle, (_vm->getGameId() == GID_SIMON1CD32) ? 0 : Audio::Mixer::FLAG_UNSIGNED);
+}
+
+void Sound::playAmbient(uint sound) {
+ if (!_effects)
+ return;
+
+ if (sound == _ambientPlaying)
+ return;
+
+ _ambientPlaying = sound;
+
+ if (_ambientPaused)
+ return;
+
+ _mixer->stopHandle(_ambientHandle);
+ _effects->playSound(sound, &_ambientHandle, Audio::Mixer::FLAG_LOOP|Audio::Mixer::FLAG_UNSIGNED);
+}
+
+bool Sound::hasVoice() const {
+ return _hasVoiceFile;
+}
+
+bool Sound::isVoiceActive() const {
+ return _mixer->isSoundHandleActive(_voiceHandle);
+}
+
+void Sound::stopAllSfx() {
+ _mixer->stopHandle(_ambientHandle);
+ _mixer->stopHandle(_effectsHandle);
+ _mixer->stopHandle(_sfx5Handle);
+ _ambientPlaying = 0;
+}
+
+void Sound::stopVoice() {
+ _mixer->stopHandle(_voiceHandle);
+}
+
+void Sound::stopAll() {
+ _mixer->stopAll();
+ _ambientPlaying = 0;
+}
+
+void Sound::effectsPause(bool b) {
+ _effectsPaused = b;
+ _sfx5Paused = b;
+}
+
+void Sound::ambientPause(bool b) {
+ _ambientPaused = b;
+
+ if (_ambientPaused && _ambientPlaying) {
+ _mixer->stopHandle(_ambientHandle);
+ } else if (_ambientPlaying) {
+ uint tmp = _ambientPlaying;
+ _ambientPlaying = 0;
+ playAmbient(tmp);
+ }
+}
+
+// Feeble Files specific
+void Sound::playAmbientData(byte *soundData, uint sound, uint pan, uint vol) {
+ if (sound == _ambientPlaying)
+ return;
+
+ _ambientPlaying = sound;
+
+ if (_ambientPaused)
+ return;
+
+ _mixer->stopHandle(_ambientHandle);
+ playSoundData(&_ambientHandle, soundData, sound, pan, vol, true);
+}
+
+void Sound::playSfxData(byte *soundData, uint sound, uint pan, uint vol) {
+ if (_effectsPaused)
+ return;
+
+ playSoundData(&_effectsHandle, soundData, sound, pan, vol, false);
+}
+
+void Sound::playSfx5Data(byte *soundData, uint sound, uint pan, uint vol) {
+ if (_sfx5Paused)
+ return;
+
+ _mixer->stopHandle(_sfx5Handle);
+ playSoundData(&_sfx5Handle, soundData, sound, pan, vol, true);
+}
+
+void Sound::playVoiceData(byte *soundData, uint sound) {
+ _mixer->stopHandle(_voiceHandle);
+ playSoundData(&_voiceHandle, soundData, sound);
+}
+
+void Sound::playSoundData(Audio::SoundHandle *handle, byte *soundData, uint sound, int pan, int vol, bool loop) {
+ byte *buffer, flags;
+ uint16 compType;
+ int blockAlign, rate;
+
+ int size = READ_LE_UINT32(soundData + 4);
+ Common::MemoryReadStream stream(soundData, size);
+ if (!Audio::loadWAVFromStream(stream, size, rate, flags, &compType, &blockAlign))
+ error("playSoundData: Not a valid WAV data");
+
+ // The Feeble Files originally used DirectSound, which specifies volume
+ // and panning differently than ScummVM does, using a logarithmic scale
+ // rather than a linear one.
+ //
+ // Volume is a value between -10,000 and 0.
+ // Panning is a value between -10,000 and 10,000.
+ //
+ // In both cases, the -10,000 represents -100 dB. When panning, only
+ // one speaker's volume is affected - just like in ScummVM - with
+ // negative values affecting the left speaker, and positive values
+ // affecting the right speaker. Thus -10,000 means the left speaker is
+ // silent.
+
+ int v, p;
+
+ vol = CLIP(vol, -10000, 0);
+ pan = CLIP(pan, -10000, 10000);
+
+ if (vol) {
+ v = (int)((double)Audio::Mixer::kMaxChannelVolume * pow(10.0, (double)vol / 2000.0) + 0.5);
+ } else {
+ v = Audio::Mixer::kMaxChannelVolume;
+ }
+
+ if (pan < 0) {
+ p = (int)(255.0 * pow(10.0, (double)pan / 2000.0) + 127.5);
+ } else if (pan > 0) {
+ p = (int)(255.0 * pow(10.0, (double)pan / -2000.0) - 127.5);
+ } else {
+ p = 0;
+ }
+
+ if (loop == true)
+ flags |= Audio::Mixer::FLAG_LOOP;
+
+ if (compType == 2) {
+ Audio::AudioStream *sndStream = Audio::makeADPCMStream(&stream, size, Audio::kADPCMMS, rate, (flags & Audio::Mixer::FLAG_STEREO) ? 2 : 1, blockAlign);
+ buffer = (byte *)malloc(size * 4);
+ size = sndStream->readBuffer((int16*)buffer, size * 2);
+ size *= 2; // 16bits.
+ delete sndStream;
+ } else {
+ buffer = (byte *)malloc(size);
+ memcpy(buffer, soundData + stream.pos(), size);
+ }
+
+ _mixer->playRaw(handle, buffer, size, rate, flags | Audio::Mixer::FLAG_AUTOFREE, -1, v, p);
+}
+
+void Sound::stopSfx5() {
+ _mixer->stopHandle(_sfx5Handle);
+}
+
+void Sound::switchVoiceFile(const GameSpecificSettings *gss, uint disc) {
+ if (_lastVoiceFile == disc)
+ return;
+
+ stopAll();
+ delete _voice;
+
+ _hasVoiceFile = false;
+ _lastVoiceFile = disc;
+
+ char filename[16];
+ File *file = new File();
+
+#ifdef USE_FLAC
+ if (!_hasVoiceFile) {
+ sprintf(filename, "%s%d.fla", gss->speech_filename, disc);
+ file->open(filename);
+ if (file->isOpen()) {
+ _hasVoiceFile = true;
+ _voice = new FlacSound(_mixer, file);
+ }
+ }
+#endif
+#ifdef USE_MAD
+ if (!_hasVoiceFile) {
+ sprintf(filename, "%s%d.mp3", gss->speech_filename, disc);
+ file->open(filename);
+ if (file->isOpen()) {
+ _hasVoiceFile = true;
+ _voice = new MP3Sound(_mixer, file);
+ }
+ }
+#endif
+#ifdef USE_VORBIS
+ if (!_hasVoiceFile) {
+ sprintf(filename, "%s%d.ogg", gss->speech_filename, disc);
+ file->open(filename);
+ if (file->isOpen()) {
+ _hasVoiceFile = true;
+ _voice = new VorbisSound(_mixer, file);
+ }
+ }
+#endif
+ if (!_hasVoiceFile) {
+ sprintf(filename, "%s%d.wav", gss->speech_filename, disc);
+ file->open(filename);
+ if (file->isOpen() == false) {
+ error("switchVoiceFile: Can't load voice file %s", filename);
+ }
+ _hasVoiceFile = true;
+ _voice = new WavSound(_mixer, file);
+ }
+}
+
+} // End of namespace Simon