aboutsummaryrefslogtreecommitdiff
path: root/engines/tony/sound.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/tony/sound.cpp')
-rw-r--r--engines/tony/sound.cpp688
1 files changed, 688 insertions, 0 deletions
diff --git a/engines/tony/sound.cpp b/engines/tony/sound.cpp
new file mode 100644
index 0000000000..2c2c280eb2
--- /dev/null
+++ b/engines/tony/sound.cpp
@@ -0,0 +1,688 @@
+/* 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.
+ *
+ */
+
+/*
+ * This code is based on original Tony Tough source code
+ *
+ * Copyright (c) 1997-2003 Nayma Software
+ */
+
+#include "audio/audiostream.h"
+#include "audio/decoders/adpcm.h"
+#include "audio/decoders/raw.h"
+#include "audio/decoders/wave.h"
+#include "common/textconsole.h"
+#include "tony/game.h"
+#include "tony/tony.h"
+
+namespace Tony {
+
+/****************************************************************************\
+* FPSOUND Methods
+\****************************************************************************/
+
+/**
+ * Default constructor. Initializes the attributes.
+ *
+ */
+FPSound::FPSound() {
+ _bSoundSupported = false;
+}
+
+/**
+ * Initializes the object, and prepare everything you need to create streams and sound effects.
+ *
+ * @returns True is everything is OK, False otherwise
+ */
+bool FPSound::init() {
+ _bSoundSupported = g_system->getMixer()->isReady();
+ return _bSoundSupported;
+}
+
+/**
+ * Destroy the object and free the memory
+ *
+ */
+
+FPSound::~FPSound() {
+}
+
+/**
+ * Allocates an object of type FPStream, and return its pointer
+ *
+ * @param lplpStream Will contain a pointer to the object you just created.
+ *
+ * @returns True is everything is OK, False otherwise
+ */
+
+bool FPSound::createStream(FPStream **lplpStream) {
+ (*lplpStream) = new FPStream(_bSoundSupported);
+
+ return (*lplpStream != NULL);
+}
+
+/**
+ * Allocates an object of type FpSfx, and return its pointer
+ *
+ * @param lplpSfx Will contain a pointer to the object you just created.
+ *
+ * @returns True is everything is OK, False otherwise
+ */
+
+bool FPSound::createSfx(FPSfx **lplpSfx) {
+ (*lplpSfx) = new FPSfx(_bSoundSupported);
+
+ return (*lplpSfx != NULL);
+}
+
+/**
+ * Set the general volume
+ *
+ * @param dwVolume Volume to set (0-63)
+ */
+
+void FPSound::setMasterVolume(int dwVolume) {
+ if (!_bSoundSupported)
+ return;
+
+ g_system->getMixer()->setVolumeForSoundType(Audio::Mixer::kPlainSoundType, CLIP<int>(dwVolume, 0, 63) * Audio::Mixer::kMaxChannelVolume / 63);
+}
+
+/**
+ * Get the general volume
+ *
+ * @param lpdwVolume Variable that will contain the volume (0-63)
+ */
+
+void FPSound::getMasterVolume(int *lpdwVolume) {
+ if (!_bSoundSupported)
+ return;
+
+ *lpdwVolume = g_system->getMixer()->getVolumeForSoundType(Audio::Mixer::kPlainSoundType) * 63 / Audio::Mixer::kMaxChannelVolume;
+}
+
+/**
+ * Default constructor.
+ *
+ * @remarks Do *NOT* declare an object directly, but rather
+ * create it using FPSound::CreateSfx()
+ *
+ */
+
+FPSfx::FPSfx(bool bSoundOn) {
+ _bSoundSupported = bSoundOn;
+ _bFileLoaded = false;
+ _lastVolume = 63;
+ _hEndOfBuffer = CoroScheduler.createEvent(true, false);
+ _bIsVoice = false;
+ _loopStream = 0;
+ _rewindableStream = 0;
+ _bPaused = false;
+
+ g_vm->_activeSfx.push_back(this);
+}
+
+/**
+ * Default Destructor.
+ *
+ * @remarks It is also stops the sound effect that may be
+ * currently played, and free the memory it uses.
+ *
+ */
+
+FPSfx::~FPSfx() {
+ if (!_bSoundSupported)
+ return;
+
+ g_system->getMixer()->stopHandle(_handle);
+ g_vm->_activeSfx.remove(this);
+
+ if (_loopStream)
+ delete _loopStream; // _rewindableStream is deleted by deleting _loopStream
+ else
+ delete _rewindableStream;
+
+ // Free the buffer end event
+ CoroScheduler.closeEvent(_hEndOfBuffer);
+}
+
+/**
+ * Releases the memory used by the object.
+ *
+ * @remarks Must be called when the object is no longer used and
+ * **ONLY** if the object was created by
+ * FPSound::CreateStream().
+ * Object pointers are no longer valid after this call.
+ */
+
+void FPSfx::release() {
+ delete this;
+}
+
+bool FPSfx::loadWave(Common::SeekableReadStream *stream) {
+ if (!stream)
+ return false;
+
+ _rewindableStream = Audio::makeWAVStream(stream, DisposeAfterUse::YES);
+
+ if (!_rewindableStream)
+ return false;
+
+ _bFileLoaded = true;
+ setVolume(_lastVolume);
+ return true;
+}
+
+bool FPSfx::loadVoiceFromVDB(Common::File &vdbFP) {
+ if (!_bSoundSupported)
+ return true;
+
+ uint32 size = vdbFP.readUint32LE();
+ uint32 rate = vdbFP.readUint32LE();
+ _bIsVoice = true;
+
+ _rewindableStream = Audio::makeADPCMStream(vdbFP.readStream(size), DisposeAfterUse::YES, 0, Audio::kADPCMDVI, rate, 1);
+
+ _bFileLoaded = true;
+ setVolume(62);
+ return true;
+}
+
+/**
+ * Opens a file and loads a sound effect.
+ *
+ * @param lpszFileName Sfx filename
+ * @param dwCodec CODEC used to uncompress the samples
+ *
+ * @returns True is everything is OK, False otherwise
+ */
+
+bool FPSfx::loadFile(const char *lpszFileName, uint32 dwCodec) {
+ if (!_bSoundSupported)
+ return true;
+
+ Common::File file;
+ if (!file.open(lpszFileName)) {
+ warning("FPSfx::LoadFile(): Cannot open sfx file!");
+ return false;
+ }
+
+ if (file.readUint32BE() != MKTAG('A', 'D', 'P', 0x10)) {
+ warning("FPSfx::LoadFile(): Invalid ADP header!");
+ return false;
+ }
+
+ uint32 rate = file.readUint32LE();
+ uint32 channels = file.readUint32LE();
+
+ Common::SeekableReadStream *buffer = file.readStream(file.size() - file.pos());
+
+ if (dwCodec == FPCODEC_ADPCM) {
+ _rewindableStream = Audio::makeADPCMStream(buffer, DisposeAfterUse::YES, 0, Audio::kADPCMDVI, rate, channels);
+ } else {
+ byte flags = Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN;
+
+ if (channels == 2)
+ flags |= Audio::FLAG_STEREO;
+
+ _rewindableStream = Audio::makeRawStream(buffer, rate, flags, DisposeAfterUse::YES);
+ }
+
+ _bFileLoaded = true;
+ return true;
+}
+
+/**
+ * Play the Sfx in memory.
+ *
+ * @returns True is everything is OK, False otherwise
+ */
+
+bool FPSfx::play() {
+ stop(); // sanity check
+
+ if (_bFileLoaded) {
+ CoroScheduler.resetEvent(_hEndOfBuffer);
+
+ _rewindableStream->rewind();
+
+ Audio::AudioStream *stream = _rewindableStream;
+
+ if (_bLoop) {
+ if (!_loopStream)
+ _loopStream = Audio::makeLoopingAudioStream(_rewindableStream, 0);
+
+ stream = _loopStream;
+ }
+
+ g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &_handle, stream, -1,
+ Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO);
+
+ setVolume(_lastVolume);
+
+ if (_bPaused)
+ g_system->getMixer()->pauseHandle(_handle, true);
+ }
+
+ return true;
+}
+
+/**
+ * Stops a Sfx.
+ *
+ * @returns True is everything is OK, False otherwise
+ */
+
+bool FPSfx::stop() {
+ if (_bFileLoaded) {
+ g_system->getMixer()->stopHandle(_handle);
+ _bPaused = false;
+ }
+
+ return true;
+}
+
+/**
+ * Enables or disables the Sfx loop.
+ *
+ * @param _bLoop True to enable the loop, False to disable
+ *
+ * @remarks The loop must be activated BEFORE the sfx starts
+ * playing. Any changes made during the play will have
+ * no effect until the sfx is stopped then played again.
+ */
+
+void FPSfx::setLoop(bool bLop) {
+ _bLoop = bLop;
+}
+
+/**
+ * Pauses a Sfx.
+ *
+ */
+
+void FPSfx::pause(bool bPause) {
+ if (_bFileLoaded) {
+ if (g_system->getMixer()->isSoundHandleActive(_handle) && (bPause ^ _bPaused))
+ g_system->getMixer()->pauseHandle(_handle, bPause);
+
+ _bPaused = bPause;
+ }
+}
+
+/**
+ * Change the volume of Sfx
+ *
+ * @param dwVolume Volume to be set (0-63)
+ *
+ */
+
+void FPSfx::setVolume(int dwVolume) {
+ if (dwVolume > 63)
+ dwVolume = 63;
+
+ if (dwVolume < 0)
+ dwVolume = 0;
+
+ _lastVolume = dwVolume;
+
+ if (_bIsVoice) {
+ if (!GLOBALS._bCfgDubbing)
+ dwVolume = 0;
+ else {
+ dwVolume -= (10 - GLOBALS._nCfgDubbingVolume) * 2;
+ if (dwVolume < 0)
+ dwVolume = 0;
+ }
+ } else {
+ if (!GLOBALS._bCfgSFX)
+ dwVolume = 0;
+ else {
+ dwVolume -= (10 - GLOBALS._nCfgSFXVolume) * 2;
+ if (dwVolume < 0)
+ dwVolume = 0;
+ }
+ }
+
+ if (g_system->getMixer()->isSoundHandleActive(_handle))
+ g_system->getMixer()->setChannelVolume(_handle, dwVolume * Audio::Mixer::kMaxChannelVolume / 63);
+}
+
+/**
+ * Gets the Sfx volume
+ *
+ * @param lpdwVolume Will contain the current Sfx volume
+ *
+ */
+
+void FPSfx::getVolume(int *lpdwVolume) {
+ if (g_system->getMixer()->isSoundHandleActive(_handle))
+ *lpdwVolume = g_system->getMixer()->getChannelVolume(_handle) * 63 / Audio::Mixer::kMaxChannelVolume;
+ else
+ *lpdwVolume = 0;
+}
+
+/**
+ * Returns true if the underlying sound has ended
+ */
+
+bool FPSfx::endOfBuffer() const {
+ return !g_system->getMixer()->isSoundHandleActive(_handle) && (!_rewindableStream || _rewindableStream->endOfData());
+}
+
+/**
+ * Continually checks to see if active sounds have finished playing
+ * Sets the event signalling the sound has ended
+ */
+void FPSfx::soundCheckProcess(CORO_PARAM, const void *param) {
+ CORO_BEGIN_CONTEXT;
+ Common::List<FPSfx *>::iterator i;
+ CORO_END_CONTEXT(_ctx);
+
+ CORO_BEGIN_CODE(_ctx);
+
+ for (;;) {
+ // Check each active sound
+ for (_ctx->i = g_vm->_activeSfx.begin(); _ctx->i != g_vm->_activeSfx.end(); ++_ctx->i) {
+ FPSfx *sfx = *_ctx->i;
+ if (sfx->endOfBuffer())
+ CoroScheduler.setEvent(sfx->_hEndOfBuffer);
+ }
+
+ // Delay until the next check is done
+ CORO_INVOKE_1(CoroScheduler.sleep, 50);
+ }
+
+ CORO_END_CODE;
+}
+
+/**
+ * Default constructor.
+ *
+ * @remarks Do *NOT* declare an object directly, but rather
+ * create it using FPSound::CreateStream()
+ */
+FPStream::FPStream(bool bSoundOn) {
+ _bSoundSupported = bSoundOn;
+ _bFileLoaded = false;
+ _bPaused = false;
+ _bLoop = false;
+ _bDoFadeOut = false;
+ _bSyncExit = false;
+ _dwBufferSize = _dwSize = 0;
+ _lastVolume = 0;
+ _syncToPlay = NULL;
+ _loopStream = NULL;
+ _rewindableStream = NULL;
+}
+
+/**
+ * Default destructor.
+ *
+ * @remarks It calls CloseFile() if needed.
+ */
+
+FPStream::~FPStream() {
+ if (!_bSoundSupported)
+ return;
+
+ if (g_system->getMixer()->isSoundHandleActive(_handle))
+ stop();
+
+ if (_bFileLoaded)
+ unloadFile();
+
+ _syncToPlay = NULL;
+}
+
+/**
+ * Releases the memory object.
+ *
+ * @remarks Must be called when the object is no longer used
+ * and **ONLY** if the object was created by
+ * FPSound::CreateStream().
+ * Object pointers are no longer valid after this call.
+ */
+void FPStream::release() {
+ delete this;
+}
+
+/**
+ * Opens a file stream
+ *
+ * @param fileName Filename to be opened
+ * @param dwCodec CODEC to be used to uncompress samples
+ *
+ * @returns True is everything is OK, False otherwise
+ */
+bool FPStream::loadFile(const Common::String &fileName, uint32 dwCodType, int nBufSize) {
+ if (!_bSoundSupported)
+ return true;
+
+ if (_bFileLoaded)
+ unloadFile();
+
+ // Save the codec type
+ _dwCodec = dwCodType;
+
+ // Open the file stream for reading
+ if (!_file.open(fileName)) {
+ // Fallback: try with an extra '0' prefix
+ if (!_file.open("0" + fileName))
+ return false;
+ }
+
+ // Save the size of the stream
+ _dwSize = _file.size();
+
+ switch (_dwCodec) {
+ case FPCODEC_RAW:
+ _rewindableStream = Audio::makeRawStream(&_file, 44100, Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN | Audio::FLAG_STEREO, DisposeAfterUse::NO);
+ break;
+
+ case FPCODEC_ADPCM:
+ _rewindableStream = Audio::makeADPCMStream(&_file, DisposeAfterUse::NO, 0, Audio::kADPCMDVI, 44100, 2);
+ break;
+
+ default:
+ _file.close();
+ return false;
+ }
+
+ // All done
+ _bFileLoaded = true;
+ _bPaused = false;
+
+ setVolume(63);
+
+ return true;
+}
+
+/**
+ * Closes a file stream (opened or not).
+ *
+ * @returns For safety, the destructor calls unloadFile() if it has not
+ * been mentioned explicitly.
+ *
+ * @remarks It is necessary to call this function to free the
+ * memory used by the stream.
+ */
+bool FPStream::unloadFile() {
+ if (!_bSoundSupported || !_bFileLoaded)
+ return true;
+
+ assert(!g_system->getMixer()->isSoundHandleActive(_handle));
+
+ // Closes the file handle stream
+ delete _loopStream;
+ delete _rewindableStream;
+ _loopStream = NULL;
+ _rewindableStream = NULL;
+ _file.close();
+
+ // Flag that the file is no longer in memory
+ _bFileLoaded = false;
+
+ return true;
+}
+
+/**
+ * Play the stream.
+ *
+ * @returns True is everything is OK, False otherwise
+ */
+
+bool FPStream::play() {
+ if (!_bSoundSupported || !_bFileLoaded)
+ return false;
+
+ stop();
+
+ _rewindableStream->rewind();
+
+ Audio::AudioStream *stream = _rewindableStream;
+
+ if (_bLoop) {
+ if (!_loopStream)
+ _loopStream = new Audio::LoopingAudioStream(_rewindableStream, 0, DisposeAfterUse::NO);
+
+ stream = _loopStream;
+ }
+
+ // FIXME: Should this be kMusicSoundType or KPlainSoundType?
+ g_system->getMixer()->playStream(Audio::Mixer::kMusicSoundType, &_handle, stream, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO);
+ setVolume(_lastVolume);
+ _bPaused = false;
+
+ return true;
+}
+
+/**
+ * Closes the stream.
+ *
+ * @returns True is everything is OK, False otherwise
+ *
+ */
+
+bool FPStream::stop() {
+ if (!_bSoundSupported)
+ return true;
+
+ if (!_bFileLoaded)
+ return false;
+
+ if (!g_system->getMixer()->isSoundHandleActive(_handle))
+ return false;
+
+ g_system->getMixer()->stopHandle(_handle);
+
+ _bPaused = false;
+
+ return true;
+}
+
+void FPStream::waitForSync(FPStream *toplay) {
+ // FIXME: The idea here is that you wait for this stream to reach
+ // a buffer which is a multiple of nBufSize/nSync, and then the
+ // thread stops it and immediately starts the 'toplay' stream.
+
+ stop();
+ toplay->play();
+}
+
+/**
+ * Unables or disables stream loop.
+ *
+ * @param _bLoop True enable loop, False disables it
+ *
+ * @remarks The loop must be activated BEFORE the stream starts
+ * playing. Any changes made during the play will have no
+ * effect until the stream is stopped then played again.
+ */
+void FPStream::setLoop(bool loop) {
+ _bLoop = loop;
+}
+
+/**
+ * Pause sound effect
+ *
+ * @param bPause True enables pause, False disables it
+ */
+void FPStream::pause(bool bPause) {
+ if (!_bFileLoaded)
+ return;
+
+ if (bPause == _bPaused)
+ return;
+
+ if (g_system->getMixer()->isSoundHandleActive(_handle))
+ g_system->getMixer()->pauseHandle(_handle, bPause);
+
+ _bPaused = bPause;
+
+ // Trick to reset the volume after a possible new sound configuration
+ setVolume(_lastVolume);
+}
+
+/**
+ * Change the volume of the stream
+ *
+ * @param dwVolume Volume to be set (0-63)
+ *
+ */
+
+void FPStream::setVolume(int dwVolume) {
+ if (dwVolume > 63)
+ dwVolume = 63;
+
+ if (dwVolume < 0)
+ dwVolume = 0;
+
+ _lastVolume = dwVolume;
+
+ if (!GLOBALS._bCfgMusic)
+ dwVolume = 0;
+ else {
+ dwVolume -= (10 - GLOBALS._nCfgMusicVolume) * 2;
+ if (dwVolume < 0)
+ dwVolume = 0;
+ }
+
+ if (g_system->getMixer()->isSoundHandleActive(_handle))
+ g_system->getMixer()->setChannelVolume(_handle, dwVolume * Audio::Mixer::kMaxChannelVolume / 63);
+}
+
+/**
+ * Gets the volume of the stream
+ *
+ * @param lpdwVolume Variable that will contain the current volume
+ *
+ */
+
+void FPStream::getVolume(int *lpdwVolume) {
+ if (g_system->getMixer()->isSoundHandleActive(_handle))
+ *lpdwVolume = g_system->getMixer()->getChannelVolume(_handle) * 63 / Audio::Mixer::kMaxChannelVolume;
+ else
+ *lpdwVolume = 0;
+}
+
+} // End of namespace Tony