From 87b4c8a9d9654eec78e8f3e58995d61e6ad98b04 Mon Sep 17 00:00:00 2001 From: Paul Gilbert Date: Sat, 6 Aug 2016 21:55:19 -0400 Subject: TITANIC: Added more sound manager functionality --- engines/titanic/core/game_object.cpp | 20 ++-- engines/titanic/core/game_object.h | 13 ++- engines/titanic/game/computer_screen.cpp | 6 +- engines/titanic/sound/proximity.cpp | 2 +- engines/titanic/sound/proximity.h | 2 +- engines/titanic/sound/qmixer.cpp | 9 ++ engines/titanic/sound/qmixer.h | 10 ++ engines/titanic/sound/sound.cpp | 21 ++-- engines/titanic/sound/sound.h | 27 ++++- engines/titanic/sound/sound_manager.cpp | 132 +++++++++++++++++------- engines/titanic/sound/sound_manager.h | 64 ++++++++---- engines/titanic/sound/wave_file.cpp | 16 +++ engines/titanic/sound/wave_file.h | 16 ++- engines/titanic/true_talk/true_talk_manager.cpp | 14 +-- 14 files changed, 264 insertions(+), 88 deletions(-) diff --git a/engines/titanic/core/game_object.cpp b/engines/titanic/core/game_object.cpp index a366f7f336..6cd9db7056 100644 --- a/engines/titanic/core/game_object.cpp +++ b/engines/titanic/core/game_object.cpp @@ -38,9 +38,11 @@ namespace Titanic { EMPTY_MESSAGE_MAP(CGameObject, CNamedItem); CCreditText *CGameObject::_credits; +int CGameObject::_soundHandles[3]; void CGameObject::init() { _credits = nullptr; + _soundHandles[0] = _soundHandles[1] = _soundHandles[2] = -1; } void CGameObject::deinit() { @@ -439,11 +441,11 @@ void CGameObject::soundFn2(const CString &resName, int v1, int v2, int v3, int h warning("TODO: CGameObject::soundFn2"); } -void CGameObject::soundFn3(int handle, int val2, int val3) { +void CGameObject::setSoundVolume(uint handle, uint percent, uint seconds) { if (handle != 0 && handle != -1) { CGameManager *gameManager = getGameManager(); if (gameManager) - return gameManager->_sound.fn3(handle, val2, val3); + return gameManager->_sound.setVolume(handle, percent, seconds); } } @@ -456,7 +458,7 @@ void CGameObject::soundFn5(int v1, int v2, int v3) { } void CGameObject::sound8(bool flag) const { - getGameManager()->_sound.managerProc8(flag ? 3 : 0); + getGameManager()->_sound.stopChannel(flag ? 3 : 0); } void CGameObject::setVisible(bool val) { @@ -660,14 +662,16 @@ int CGameObject::playSound(const CString &name, CProximity &prox) { return 0; } -void CGameObject::stopSound(int handle, int val2) { +void CGameObject::stopSound(int handle, uint seconds) { if (handle != 0 && handle != -1) { CGameManager *gameManager = getGameManager(); if (gameManager) { - if (val2) - gameManager->_sound.fn3(handle, 0, val2); - else - gameManager->_sound.fn2(handle); + if (seconds) { + gameManager->_sound.setVolume(handle, 0, seconds); + gameManager->_sound.setCanFree(handle); + } else { + gameManager->_sound.stopSound(handle); + } } } } diff --git a/engines/titanic/core/game_object.h b/engines/titanic/core/game_object.h index e5ee22a8d5..7bc5d156fc 100644 --- a/engines/titanic/core/game_object.h +++ b/engines/titanic/core/game_object.h @@ -53,6 +53,7 @@ class CGameObject : public CNamedItem { DECLARE_MESSAGE_MAP; private: static CCreditText *_credits; + static int _soundHandles[3]; private: /** * Load a visual resource for the object @@ -192,8 +193,10 @@ protected: /** * Stop a sound + * @param handle Sound handle + * @param seconds Optional number of seconds to transition sound off */ - void stopSound(int handle, int val2 = 0); + void stopSound(int handle, uint seconds = 0); /** * Returns true if a sound with the specified handle is active @@ -202,7 +205,13 @@ protected: void soundFn2(const CString &resName, int v1, int v2, int v3, int handleIndex); - void soundFn3(int handle, int val2, int val3); + /** + * Sets the volume for a sound + * @param handle Sound handle + * @param volume Volume percentage (0 to 100) + * @param seconds Number of seconds to transition to the new volume + */ + void setSoundVolume(uint handle, uint percent, uint seconds); void soundFn4(int v1, int v2, int v3); diff --git a/engines/titanic/game/computer_screen.cpp b/engines/titanic/game/computer_screen.cpp index fd56737b01..3e5172219d 100644 --- a/engines/titanic/game/computer_screen.cpp +++ b/engines/titanic/game/computer_screen.cpp @@ -112,7 +112,7 @@ bool CComputerScreen::TimerMsg(CTimerMsg *msg) { stopSound(handle); playSound("y#662.wav"); - soundFn3(handle, 10, 2); + setSoundVolume(handle, 10, 2); playClip(392, 450); startTalking("Doorbot", 0x3611A); sleep(8000); @@ -120,10 +120,10 @@ bool CComputerScreen::TimerMsg(CTimerMsg *msg) { playClip(450, 492); startTalking("Doorbot", 0x36121); playClip(492, 522); - soundFn3(handle, 30, 2); + setSoundVolume(handle, 30, 2); playClip(523, 540); - soundFn3(handle, 0, 1); + setSoundVolume(handle, 0, 1); playClip(541, 551); stopSound(handle); diff --git a/engines/titanic/sound/proximity.cpp b/engines/titanic/sound/proximity.cpp index dd1f9a4b7a..ce91a0770f 100644 --- a/engines/titanic/sound/proximity.cpp +++ b/engines/titanic/sound/proximity.cpp @@ -27,7 +27,7 @@ namespace Titanic { CProximity::CProximity() : _field4(0), _channelVolume(100), _fieldC(0), _soundHandle((uint)-1), _field14(0), _frequencyMultiplier(0.0), _field1C(1.875), - _repeated(false), _field24(10), _field28(0), _azimuth(0.0), + _repeated(false), _channel(10), _field28(0), _azimuth(0.0), _range(0.5), _elevation(0), _posX(0.0), _posY(0.0), _posZ(0.0), _hasVelocity(false), _velocityX(0), _velocityY(0), _velocityZ(0), _field54(0), _field58(0), _field5C(0), _field60(0), _endTalkerFn(nullptr), diff --git a/engines/titanic/sound/proximity.h b/engines/titanic/sound/proximity.h index afec3a7069..d8eee4d9e5 100644 --- a/engines/titanic/sound/proximity.h +++ b/engines/titanic/sound/proximity.h @@ -41,7 +41,7 @@ public: double _frequencyMultiplier; double _field1C; bool _repeated; - int _field24; + int _channel; int _field28; double _azimuth; double _range; diff --git a/engines/titanic/sound/qmixer.cpp b/engines/titanic/sound/qmixer.cpp index 5ece37a579..da707c9527 100644 --- a/engines/titanic/sound/qmixer.cpp +++ b/engines/titanic/sound/qmixer.cpp @@ -99,4 +99,13 @@ int QMixer::qsWaveMixPlayEx(int iChannel, uint flags, CWaveFile *mixWave, int lo return 0; } +bool QMixer::qsWaveMixIsChannelDone(int iChannel) const { + // Not currently implemented in ScummVM + return true; +} + +void QMixer::qsWaveMixPump() { + // TODO: Handle checking for done sounds, and calling their end functions +} + } // End of namespace Titanic z diff --git a/engines/titanic/sound/qmixer.h b/engines/titanic/sound/qmixer.h index 948e91979b..fa4604cd2e 100644 --- a/engines/titanic/sound/qmixer.h +++ b/engines/titanic/sound/qmixer.h @@ -275,6 +275,16 @@ public: * @param params Playback parameter data */ int qsWaveMixPlayEx(int iChannel, uint flags, CWaveFile *waveFile, int loops, const QMIXPLAYPARAMS ¶ms); + + /** + * Returns true if there are no more buffers playing or queued on the channel + */ + bool qsWaveMixIsChannelDone(int iChannel) const; + + /** + * Handles regularly updating the mixer + */ + void qsWaveMixPump(); }; } // End of namespace Titanic diff --git a/engines/titanic/sound/sound.cpp b/engines/titanic/sound/sound.cpp index b796ba5595..e499bd5b78 100644 --- a/engines/titanic/sound/sound.cpp +++ b/engines/titanic/sound/sound.cpp @@ -62,18 +62,18 @@ bool CSound::isActive(int handle) const { return false; } -void CSound::fn2(int handle) { - warning("TODO: CSound::fn3"); -} - -void CSound::fn3(int handle, int val2, int val3) { - warning("TODO: CSound::fn3"); +void CSound::setVolume(uint handle, uint volume, uint seconds) { + _soundManager.setVolume(handle, volume, seconds); } void CSound::fn4(CWaveFile *waveFile, int val) { // TODO } +void CSound::stopChannel(int channel) { + _soundManager.stopChannel(channel); +} + void CSound::checkSounds() { for (CSoundItemList::iterator i = _sounds.begin(); i != _sounds.end(); ++i) { CSoundItem *soundItem = *i; @@ -195,4 +195,13 @@ int CSound::playSpeech(CDialogueFile *dialogueFile, int speechId, CProximity &pr return _soundManager.playSound(*waveFile, prox); } +void CSound::stopSound(uint handle) { + _soundManager.stopSound(handle); +} + +void CSound::setCanFree(int handle) { + if (handle != 0 && handle != -1) + _soundManager.setCanFree(handle); +} + } // End of namespace Titanic diff --git a/engines/titanic/sound/sound.h b/engines/titanic/sound/sound.h index bb2525a7c3..34ef234f2a 100644 --- a/engines/titanic/sound/sound.h +++ b/engines/titanic/sound/sound.h @@ -117,11 +117,20 @@ public: */ bool isActive(int handle) const; - void fn2(int handle); - void fn3(int handle, int val2, int val3); + /** + * Sets the volume for a sound + * @param handle Sound handle + * @param volume Volume percentage (0 to 100) + * @param seconds Number of seconds to transition to the new volume + */ + void setVolume(uint handle, uint volume, uint seconds); + void fn4(CWaveFile *waveFile, int val); - - void managerProc8(int v) { _soundManager.proc8(v); } + + /** + * Stops any sounds attached to a given channel + */ + void stopChannel(int channel); /** * Loads a TrueTalk dialogue @@ -158,6 +167,16 @@ public: * Play a sound */ int playSound(const CString &name, CProximity &prox); + + /** + * Stop a sound + */ + void stopSound(uint handle); + + /** + * Flags that a sound can be freed if a timeout is set + */ + void setCanFree(int handle); }; } // End of namespace Titanic diff --git a/engines/titanic/sound/sound_manager.cpp b/engines/titanic/sound/sound_manager.cpp index cb65a71631..2b5e137a5a 100644 --- a/engines/titanic/sound/sound_manager.cpp +++ b/engines/titanic/sound/sound_manager.cpp @@ -76,6 +76,16 @@ bool QSoundManagerSounds::contains(const CWaveFile *waveFile) const { return false; } +/*------------------------------------------------------------------------*/ + +void QSoundManager::Slot::clear() { + _waveFile = nullptr; + _isTimed = false; + _ticks = 0; + _channel = -1; + _handle = 0; + _val3 = 0; +} /*------------------------------------------------------------------------*/ @@ -108,12 +118,19 @@ CWaveFile *QSoundManager::loadSound(const CString &name) { } CWaveFile *QSoundManager::loadSpeech(CDialogueFile *dialogueFile, int speechId) { - warning("TODO"); - return nullptr; + CWaveFile *waveFile = new CWaveFile(); + + // Try to load the specified sound + if (!waveFile->loadSpeech(dialogueFile, speechId)) { + delete waveFile; + return nullptr; + } + + return waveFile; } int QSoundManager::proc5() const { - warning("TODO"); + error("TODO"); return 0; } @@ -129,7 +146,7 @@ int QSoundManager::playSound(CWaveFile &waveFile, CProximity &prox) { } } - if (channel >= 0 || (channel = flushChannels(prox._field24)) != -1) { + if (channel >= 0 || (channel = resetChannel(prox._channel)) != -1) { return playWave(&waveFile, channel, flags, prox); } @@ -137,17 +154,45 @@ int QSoundManager::playSound(CWaveFile &waveFile, CProximity &prox) { } void QSoundManager::stopSound(uint handle) { - warning("TODO"); + resetChannel(10); + + for (uint idx = 0; idx < _slots.size(); ++idx) { + Slot &slot = _slots[idx]; + if (slot._handle == handle) { + qsWaveMixFlushChannel(slot._channel); + _sounds.flushChannel(slot._channel); + resetChannel(10); + } + } } -void QSoundManager::proc8(int v) { - warning("TODO"); +void QSoundManager::stopChannel(int channel) { + int endChannel; + switch (channel) { + case 0: + case 3: + endChannel = channel + 3; + break; + case 6: + endChannel = 10; + break; + case 10: + endChannel = 48; + break; + default: + return; + } + + for (; channel < endChannel; ++channel) { + qsWaveMixFlushChannel(channel); + _sounds.flushChannel(channel); + } } -void QSoundManager::proc9(uint handle) { +void QSoundManager::setCanFree(uint handle) { for (uint idx = 0; idx < _slots.size(); ++idx) { if (_slots[idx]._handle == handle) - _slots[idx]._val2 = 1; + _slots[idx]._isTimed = true; } } @@ -156,12 +201,46 @@ void QSoundManager::stopAllChannels() { for (int idx = 0; idx < 16; ++idx) _sounds.flushChannel(idx); - flushChannels(10); + resetChannel(10); } -int QSoundManager::flushChannels(int iChannel) { - // TODO - return -1; +int QSoundManager::resetChannel(int iChannel) { + int newChannel = -1; + int channelStart = 10; + int channelEnd = 16; + + if (iChannel != 10) { + qsWaveMixFlushChannel(iChannel); + _sounds.flushChannel(iChannel); + channelStart = iChannel; + channelEnd = iChannel + 1; + } else { + uint ticks = g_vm->_events->getTicksCount(); + + for (uint idx = 0; idx < _slots.size(); ++idx) { + Slot &slot = _slots[idx]; + if (slot._isTimed && slot._ticks && ticks > slot._ticks) { + qsWaveMixFlushChannel(slot._channel); + _sounds.flushChannel(slot._channel); + } + } + } + + for (iChannel = channelStart; iChannel < channelEnd; ++iChannel) { + if (qsWaveMixIsChannelDone(iChannel)) { + // Scan through the slots, and reset any slot using the channel + for (uint idx = 0; idx < _slots.size(); ++idx) { + Slot &slot = _slots[idx]; + if (slot._channel == iChannel) + slot.clear(); + } + + // Use the empty channel + newChannel = iChannel; + } + } + + return newChannel; } void QSoundManager::setVolume(uint handle, uint volume, uint seconds) { @@ -219,6 +298,10 @@ bool QSoundManager::isActive(const CWaveFile *waveFile) const { return _sounds.contains(waveFile); } +void QSoundManager::waveMixPump() { + +} + uint QSoundManager::getLatency() const { return LATENCY; } @@ -317,29 +400,6 @@ void QSoundManager::soundFreed(Audio::SoundHandle &handle) { qsWaveMixFreeWave(handle); } -void QSoundManager::stopChannels(int channel) { - int endChannel; - switch (channel) { - case 0: - case 3: - endChannel = channel + 3; - break; - case 6: - endChannel = 10; - break; - case 10: - endChannel = 48; - break; - default: - return; - } - - for (; channel < endChannel; ++channel) { - qsWaveMixFlushChannel(channel); - _sounds.flushChannel(channel); - } -} - void QSoundManager::updateVolume(int channel, uint panRate) { uint volume = _channelsVolume[channel] * 327; diff --git a/engines/titanic/sound/sound_manager.h b/engines/titanic/sound/sound_manager.h index 7ad5c8f51a..91e1167195 100644 --- a/engines/titanic/sound/sound_manager.h +++ b/engines/titanic/sound/sound_manager.h @@ -72,7 +72,11 @@ public: */ virtual void stopSound(uint handle) = 0; - virtual void proc8(int v) = 0; + /** + * Stops a designated range of channels + */ + virtual void stopChannel(int channel) = 0; + virtual void proc9(uint handle) {} /** @@ -118,8 +122,11 @@ public: */ virtual bool isActive(const CWaveFile *waveFile) const { return false; } - virtual int proc16() const { return 0; } - + /** + * Handles regularly updating the mixer + */ + virtual void waveMixPump() = 0; + /** * Returns the movie latency */ @@ -175,7 +182,21 @@ public: */ virtual void postSave() {} - virtual void proc29() {} + /** + * Sets the position and orientation for the listener (player) + */ + virtual void setListenerPosition(double posX, double posY, double posZ, + double directionX, double directionY, double directionZ, bool stopSounds) {} + + /** + * Returns the music volume percent + */ + int getMusicVolume() const { return _musicPercent; } + + /** + * Returns the speech volume percent + */ + int getSpeechVolume() const { return _speechPercent; } /** * Returns the parrot volume percent @@ -226,12 +247,14 @@ public: class QSoundManager : public CSoundManager, public QMixer { struct Slot { CWaveFile *_waveFile; - uint _val2; + bool _isTimed; uint _ticks; int _channel; uint _handle; uint _val3; - Slot() : _waveFile(0), _val2(0), _ticks(0), _channel(0), _handle(0), _val3(0) {} + + Slot() : _waveFile(0), _isTimed(0), _ticks(0), _channel(-1), _handle(0), _val3(0) {} + void clear(); }; private: QSoundManagerSounds _sounds; @@ -239,11 +262,6 @@ private: uint _channelsVolume[16]; int _channelsMode[16]; private: - /** - * Stops a designated range of channels - */ - void stopChannels(int channel); - /** * Updates the volume for a channel * @param channel Channel to be update @@ -272,9 +290,9 @@ private: void setChannelVolume(int iChannel, uint volume, uint mode); /** - * Flushes channels + * Resets the specified channel and returns a new free one */ - int flushChannels(int iChannel); + int resetChannel(int iChannel); public: int _field18; int _field1C; @@ -303,14 +321,21 @@ public: * Start playing a previously loaded sound resource */ virtual int playSound(CWaveFile &waveFile, CProximity &prox); - + /** * Stop playing the specified sound */ virtual void stopSound(uint handle); - virtual void proc8(int v); - virtual void proc9(uint handle); + /** + * Stops a designated range of channels + */ + virtual void stopChannel(int channel); + + /** + * Flags that a sound can be freed if a timeout is set + */ + virtual void setCanFree(uint handle); /** * Stops sounds on all playing channels @@ -355,8 +380,11 @@ public: */ virtual bool isActive(const CWaveFile *waveFile) const; - virtual int proc16() const { return 1; } - + /** + * Handles regularly updating the mixer + */ + virtual void waveMixPump(); + /** * Returns the movie latency */ diff --git a/engines/titanic/sound/wave_file.cpp b/engines/titanic/sound/wave_file.cpp index adbc41455d..827e2f5586 100644 --- a/engines/titanic/sound/wave_file.cpp +++ b/engines/titanic/sound/wave_file.cpp @@ -21,6 +21,7 @@ */ #include "audio/decoders/wave.h" +#include "common/memstream.h" #include "titanic/sound/wave_file.h" #include "titanic/sound/sound_manager.h" #include "titanic/support/simple_file.h" @@ -48,6 +49,21 @@ bool CWaveFile::loadSound(const CString &name) { Common::SeekableReadStream *stream = file.readStream(); _stream = Audio::makeWAVStream(stream->readStream(stream->size()), DisposeAfterUse::YES); + _soundType = SOUND_SFX; + return true; +} + +bool CWaveFile::loadSpeech(CDialogueFile *dialogueFile, int speechIndex) { + DialogueResource *res = dialogueFile->openWaveEntry(speechIndex); + if (!res) + return false; + + byte *data = (byte *)malloc(res->_size); + dialogueFile->read(res, data, res->_size); + + _stream = Audio::makeWAVStream(new Common::MemoryReadStream(data, res->_size, DisposeAfterUse::YES), + DisposeAfterUse::YES); + _soundType = SOUND_SPEECH; return true; } diff --git a/engines/titanic/sound/wave_file.h b/engines/titanic/sound/wave_file.h index 7818c1386d..2644b6807a 100644 --- a/engines/titanic/sound/wave_file.h +++ b/engines/titanic/sound/wave_file.h @@ -26,19 +26,26 @@ #include "audio/audiostream.h" #include "audio/mixer.h" #include "titanic/support/string.h" +#include "titanic/true_talk/dialogue_file.h" namespace Titanic { class QSoundManager; +enum SoundType { + SOUND_SFX = 0, + SOUND_SPEECH = 1 +}; + class CWaveFile { public: QSoundManager *_owner; Audio::AudioStream *_stream; Audio::SoundHandle _soundHandle; + SoundType _soundType; public: - CWaveFile() : _owner(nullptr), _stream(nullptr) {} - CWaveFile(QSoundManager *owner) : _owner(owner), _stream(nullptr) {} + CWaveFile() : _owner(nullptr), _stream(nullptr), _soundType(SOUND_SFX) {} + CWaveFile(QSoundManager *owner) : _owner(owner), _stream(nullptr), _soundType(SOUND_SFX) {} ~CWaveFile(); int fn1(); @@ -48,6 +55,11 @@ public: */ bool loadSound(const CString &name); + /** + * Tries to load speech from a specified dialogue file + */ + bool loadSpeech(CDialogueFile *dialogueFile, int speechIndex); + /** * Returns true if the wave file has data loaded */ diff --git a/engines/titanic/true_talk/true_talk_manager.cpp b/engines/titanic/true_talk/true_talk_manager.cpp index 32fb06c6d8..82d443fcda 100644 --- a/engines/titanic/true_talk/true_talk_manager.cpp +++ b/engines/titanic/true_talk/true_talk_manager.cpp @@ -494,13 +494,13 @@ void CTrueTalkManager::playSpeech(TTtalker *talker, TTroomScript *roomScript, CV // Setup proximities CProximity p1, p2, p3; if (isParrot) { - p1._field24 = 3; - p2._field24 = 5; - p3._field24 = 4; + p1._channel = 3; + p2._channel = 5; + p3._channel = 4; } else { - p1._field24 = 0; - p2._field24 = 1; - p3._field24 = 2; + p1._channel = 0; + p2._channel = 1; + p3._channel = 2; } if (milli > 0) { @@ -517,7 +517,7 @@ void CTrueTalkManager::playSpeech(TTtalker *talker, TTroomScript *roomScript, CV p2._elevation = 0; } - _gameManager->_sound.managerProc8(p1._field24); + _gameManager->_sound.stopChannel(p1._channel); if (view) { p1._field28 = 2; view->getPosition(p1._posX, p1._posY, p1._posZ); -- cgit v1.2.3