From e77928440307f0d8a765e32100e031989ad25ff5 Mon Sep 17 00:00:00 2001 From: Robert Špalek Date: Mon, 12 Oct 2009 22:27:23 +0000 Subject: Sound effects are now correctly played. Dubbing is not yet played. svn-id: r45000 --- engines/draci/animation.cpp | 15 ++++++ engines/draci/animation.h | 3 +- engines/draci/draci.cpp | 12 +++-- engines/draci/draci.h | 4 +- engines/draci/game.cpp | 7 ++- engines/draci/sound.cpp | 122 ++++++++++++++++++++++++++++++++++++++++++-- engines/draci/sound.h | 57 +++++++++++++++++++-- 7 files changed, 205 insertions(+), 15 deletions(-) diff --git a/engines/draci/animation.cpp b/engines/draci/animation.cpp index c3ae4a8f12..37ef609ea4 100644 --- a/engines/draci/animation.cpp +++ b/engines/draci/animation.cpp @@ -39,6 +39,7 @@ Animation::Animation(DraciEngine *vm, int index) : _vm(vm) { _paused = false; _tick = _vm->_system->getMillis(); _currentFrame = 0; + _hasChangedFrame = true; _callback = &Animation::doNothing; } @@ -97,6 +98,8 @@ void Animation::nextFrame(bool force) { // Fetch new frame and mark it dirty markDirtyRect(surface); + + _hasChangedFrame = true; } } @@ -129,6 +132,15 @@ void Animation::drawFrame(Surface *surface) { // Draw frame frame->drawReScaled(surface, false, _displacement); } + + const SoundSample *sample = _samples[_currentFrame]; + if (_hasChangedFrame && sample) { + debugC(3, kDraciSoundDebugLevel, + "Playing sample on animation %d, frame %d: %d+%d at %dHz", + _id, _currentFrame, sample->_offset, sample->_length, sample->_frequency); + _vm->_sound->playSound(sample, Audio::Mixer::kMaxChannelVolume, false); + } + _hasChangedFrame = false; } void Animation::setID(int id) { @@ -162,6 +174,9 @@ bool Animation::isPlaying() const { void Animation::setPlaying(bool playing) { _tick = _vm->_system->getMillis(); _playing = playing; + + // When restarting an animation, allow playing sounds. + _hasChangedFrame |= playing; } bool Animation::isPaused() const { diff --git a/engines/draci/animation.h b/engines/draci/animation.h index 8dc596406d..ceebf04261 100644 --- a/engines/draci/animation.h +++ b/engines/draci/animation.h @@ -130,6 +130,7 @@ private: uint _currentFrame; uint _z; + bool _hasChangedFrame; Displacement _displacement; @@ -145,7 +146,7 @@ private: * object doesn't own these pointers, but they are stored in the * cache. */ - Common::List _samples; + Common::Array _samples; AnimationCallback _callback; diff --git a/engines/draci/draci.cpp b/engines/draci/draci.cpp index 1868460fcf..8fc7046d86 100644 --- a/engines/draci/draci.cpp +++ b/engines/draci/draci.cpp @@ -62,6 +62,9 @@ const char *stringsPath = "RETEZCE.DFW"; const char *soundsPath = "CD2.SAM"; const char *dubbingPath = "CD.SAM"; +const uint kSoundsFrequency = 13000; +const uint kDubbingFrequency = 22000; + DraciEngine::DraciEngine(OSystem *syst, const ADGameDescription *gameDesc) : Engine(syst) { // Put your engine in a sane state, but do nothing big yet; @@ -79,6 +82,7 @@ DraciEngine::DraciEngine(OSystem *syst, const ADGameDescription *gameDesc) Common::addDebugChannel(kDraciArchiverDebugLevel, "archiver", "BAR archiver debug info"); Common::addDebugChannel(kDraciLogicDebugLevel, "logic", "Game logic debug info"); Common::addDebugChannel(kDraciAnimationDebugLevel, "animation", "Animation debug info"); + Common::addDebugChannel(kDraciSoundDebugLevel, "sound", "Sound debug info"); // Don't forget to register your random source g_eventRec.registerRandomSource(_rnd, "draci"); @@ -109,8 +113,9 @@ int DraciEngine::init() { _itemImagesArchive = new BArchive(itemImagesPath); _stringsArchive = new BArchive(stringsPath); - _soundsArchive = new SoundArchive(soundsPath); - _dubbingArchive = new SoundArchive(dubbingPath); + _soundsArchive = new SoundArchive(soundsPath, kSoundsFrequency); + _dubbingArchive = new SoundArchive(dubbingPath, kDubbingFrequency); + _sound = new Sound(_mixer); // Load the game's fonts _smallFont = new Font(kFontSmall); @@ -298,6 +303,7 @@ DraciEngine::~DraciEngine() { delete _soundsArchive; delete _dubbingArchive; + delete _sound; // Remove all of our debug levels here Common::clearAllDebugChannels(); @@ -337,7 +343,7 @@ void DraciEngine::pauseEngineIntern(bool pause) { void DraciEngine::syncSoundSettings() { Engine::syncSoundSettings(); - // TODO: update our volumes + _sound->setVolume(); } const char *DraciEngine::getSavegameFile(int saveGameIdx) { diff --git a/engines/draci/draci.h b/engines/draci/draci.h index d5683d5516..6ef339de33 100644 --- a/engines/draci/draci.h +++ b/engines/draci/draci.h @@ -66,6 +66,7 @@ public: Game *_game; Script *_script; AnimationManager *_anims; + Sound *_sound; Font *_smallFont; Font *_bigFont; @@ -99,7 +100,8 @@ enum { kDraciBytecodeDebugLevel = 1 << 1, kDraciArchiverDebugLevel = 1 << 2, kDraciLogicDebugLevel = 1 << 3, - kDraciAnimationDebugLevel = 1 << 4 + kDraciAnimationDebugLevel = 1 << 4, + kDraciSoundDebugLevel = 1 << 5 }; // Macro to simulate lround() for non-C99 compilers diff --git a/engines/draci/game.cpp b/engines/draci/game.cpp index d5701c3eae..ff15483cc7 100644 --- a/engines/draci/game.cpp +++ b/engines/draci/game.cpp @@ -1157,7 +1157,7 @@ int Game::loadAnimation(uint animNum, uint z) { uint scaledWidth = animationReader.readUint16LE(); uint scaledHeight = animationReader.readUint16LE(); byte mirror = animationReader.readByte(); - uint sample = animationReader.readUint16LE(); + int sample = animationReader.readUint16LE() - 1; uint freq = animationReader.readUint16LE(); uint delay = animationReader.readUint16LE(); @@ -1301,6 +1301,11 @@ void Game::enterNewRoom(bool force_reload) { } debugC(1, kDraciLogicDebugLevel, "Entering room %d using gate %d", _newRoom, _newGate); + // TODO: maybe wait till all sounds end instead of stopping them. + // In any case, make sure all sounds are stopped before we deallocate + // their memory by clearing the cache. + _vm->_sound->stopAll(); + // Clear archives _vm->_roomsArchive->clearCache(); _vm->_spritesArchive->clearCache(); diff --git a/engines/draci/sound.cpp b/engines/draci/sound.cpp index 8f734d1afb..2d2cbcf74d 100644 --- a/engines/draci/sound.cpp +++ b/engines/draci/sound.cpp @@ -23,6 +23,7 @@ * */ +#include "common/config-manager.h" #include "common/debug.h" #include "common/file.h" #include "common/str.h" @@ -31,6 +32,9 @@ #include "draci/sound.h" #include "draci/draci.h" +#include "sound/audiostream.h" +#include "sound/mixer.h" + namespace Draci { void SoundArchive::openArchive(const Common::String &path) { @@ -129,9 +133,9 @@ void SoundArchive::clearCache() { * * Loads individual samples from an archive to memory on demand. */ -const SoundSample *SoundArchive::getSample(uint i, uint freq) { +const SoundSample *SoundArchive::getSample(int i, uint freq) { // Check whether requested file exists - if (i >= _sampleCount) { + if (i < 0 || i >= (int) _sampleCount) { return NULL; } @@ -150,12 +154,122 @@ const SoundSample *SoundArchive::getSample(uint i, uint freq) { debugC(3, kDraciArchiverDebugLevel, "Cached sample %d from archive %s", i, _path.c_str()); } - _samples[i]._frequency = freq; + _samples[i]._frequency = freq ? freq : _defaultFreq; return _samples + i; } -} // End of namespace Draci +Sound::Sound(Audio::Mixer *mixer) : _mixer(mixer) { + + for (int i = 0; i < SOUND_HANDLES; i++) + _handles[i].type = kFreeHandle; + + setVolume(); +} + +SndHandle *Sound::getHandle() { + for (int i = 0; i < SOUND_HANDLES; i++) { + if (_handles[i].type != kFreeHandle && !_mixer->isSoundHandleActive(_handles[i].handle)) { + debugC(5, kDraciSoundDebugLevel, "Handle %d has finished playing", i); + _handles[i].type = kFreeHandle; + } + } + + for (int i = 0; i < SOUND_HANDLES; i++) { + if (_handles[i].type == kFreeHandle) + return &_handles[i]; + } + + error("Sound::getHandle(): Too many sound handles"); + + return NULL; // for compilers that don't support NORETURN +} + +void Sound::playSoundBuffer(Audio::SoundHandle *handle, const SoundSample &buffer, int volume, + sndHandleType handleType, bool loop) { + + // Don't use FLAG_AUTOFREE, because our caching system deletes samples by itself. + byte flags = Audio::Mixer::FLAG_UNSIGNED; + + if (loop) + flags |= Audio::Mixer::FLAG_LOOP; + + const Audio::Mixer::SoundType soundType = (handleType == kVoiceHandle) ? + Audio::Mixer::kSpeechSoundType : Audio::Mixer::kSFXSoundType; + + _mixer->playRaw(soundType, handle, buffer._data, + buffer._length, buffer._frequency, flags, -1, volume); +} + +void Sound::playSound(const SoundSample *buffer, int volume, bool loop) { + if (!buffer) + return; + SndHandle *handle = getHandle(); + + handle->type = kEffectHandle; + playSoundBuffer(&handle->handle, *buffer, 2 * volume, handle->type, loop); +} + +void Sound::pauseSound() { + for (int i = 0; i < SOUND_HANDLES; i++) + if (_handles[i].type == kEffectHandle) + _mixer->pauseHandle(_handles[i].handle, true); +} + +void Sound::resumeSound() { + for (int i = 0; i < SOUND_HANDLES; i++) + if (_handles[i].type == kEffectHandle) + _mixer->pauseHandle(_handles[i].handle, false); +} + +void Sound::stopSound() { + for (int i = 0; i < SOUND_HANDLES; i++) + if (_handles[i].type == kEffectHandle) { + _mixer->stopHandle(_handles[i].handle); + _handles[i].type = kFreeHandle; + } +} + +void Sound::playVoice(const SoundSample *buffer) { + if (!buffer) + return; + SndHandle *handle = getHandle(); + + handle->type = kVoiceHandle; + playSoundBuffer(&handle->handle, *buffer, Audio::Mixer::kMaxChannelVolume, handle->type, false); +} + +void Sound::pauseVoice() { + for (int i = 0; i < SOUND_HANDLES; i++) + if (_handles[i].type == kVoiceHandle) + _mixer->pauseHandle(_handles[i].handle, true); +} +void Sound::resumeVoice() { + for (int i = 0; i < SOUND_HANDLES; i++) + if (_handles[i].type == kVoiceHandle) + _mixer->pauseHandle(_handles[i].handle, false); +} +void Sound::stopVoice() { + for (int i = 0; i < SOUND_HANDLES; i++) + if (_handles[i].type == kVoiceHandle) { + _mixer->stopHandle(_handles[i].handle); + _handles[i].type = kFreeHandle; + } +} +void Sound::stopAll() { + stopVoice(); + stopSound(); +} + +void Sound::setVolume() { + const int soundVolume = ConfMan.getInt("sfx_volume"); + const int speechVolume = ConfMan.getInt("speech_volume"); + _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, soundVolume); + _mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, speechVolume); + // TODO: make sure this sound settings works +} + +} // End of namespace Draci diff --git a/engines/draci/sound.h b/engines/draci/sound.h index b9429a70a7..86d92f1bf7 100644 --- a/engines/draci/sound.h +++ b/engines/draci/sound.h @@ -28,6 +28,7 @@ #include "common/str.h" #include "common/file.h" +#include "sound/mixer.h" namespace Draci { @@ -48,10 +49,8 @@ struct SoundSample { class SoundArchive { public: - SoundArchive() : _path(), _samples(NULL), _sampleCount(0), _opened(false), _f(NULL) {} - - SoundArchive(const Common::String &path) : - _path(), _samples(NULL), _sampleCount(0), _opened(false), _f(NULL) { + SoundArchive(const Common::String &path, uint defaultFreq) : + _path(), _samples(NULL), _sampleCount(0), _defaultFreq(defaultFreq), _opened(false), _f(NULL) { openArchive(path); } @@ -69,16 +68,64 @@ public: void clearCache(); - const SoundSample *getSample(uint i, uint freq); + const SoundSample *getSample(int i, uint freq); private: Common::String _path; ///< Path to file SoundSample *_samples; ///< Internal array of files uint _sampleCount; ///< Number of files in archive + uint _defaultFreq; ///< The default sampling frequency of the archived samples bool _opened; ///< True if the archive is opened, false otherwise Common::File *_f; ///< Opened file }; +#define SOUND_HANDLES 10 + +enum sndHandleType { + kFreeHandle, + kEffectHandle, + kVoiceHandle +}; + +struct SndHandle { + Audio::SoundHandle handle; + sndHandleType type; +}; + +// Taken from engines/saga/sound.h and simplified (in particular, removed +// decompression until we support compressed files too). +class Sound { +public: + + Sound(Audio::Mixer *mixer); + ~Sound() {} + + void playSound(const SoundSample *buffer, int volume, bool loop); + void pauseSound(); + void resumeSound(); + void stopSound(); + + void playVoice(const SoundSample *buffer); + void pauseVoice(); + void resumeVoice(); + void stopVoice(); + + void stopAll(); + + void setVolume(); + + private: + + void playSoundBuffer(Audio::SoundHandle *handle, const SoundSample &buffer, int volume, + sndHandleType handleType, bool loop); + + SndHandle *getHandle(); + + Audio::Mixer *_mixer; + + SndHandle _handles[SOUND_HANDLES]; +}; + } // End of namespace Draci #endif // DRACI_SOUND_H -- cgit v1.2.3