diff --git a/engines/nancy/action/recordtypes.cpp b/engines/nancy/action/recordtypes.cpp index d2551d28a0c..bf4ede3b4da 100644 --- a/engines/nancy/action/recordtypes.cpp +++ b/engines/nancy/action/recordtypes.cpp @@ -26,6 +26,9 @@ #include "engines/nancy/logic.h" #include "engines/nancy/nancy.h" #include "engines/nancy/graphics.h" +#include "engines/nancy/audio.h" + +#include "common/str.h" namespace Nancy { @@ -71,6 +74,7 @@ void HotMultiframeSceneChange::execute(NancyEngine *engine) { switch (state) { case kBegin: // turn main rendering on + state = kRun; // fall through case kRun: hasHotspot = false; @@ -97,6 +101,7 @@ void Hot1FrSceneChange::execute(NancyEngine *engine) { switch (state) { case kBegin: hotspot = hotspotDesc.coords; + state = kRun; // fall through case kRun: if (hotspotDesc.frameID == engine->playState.currentViewFrame) { @@ -469,7 +474,34 @@ uint16 ShowInventoryItem::readData(Common::SeekableReadStream &stream) { } uint16 PlayDigiSoundAndDie::readData(Common::SeekableReadStream &stream) { - return readRaw(stream, 0x2B); // TODO + char str[10]; + stream.read(str, 10); + filename = Common::String(str); + id = stream.readSint16LE(); + stream.skip(4); + numLoops = stream.readUint16LE(); + stream.skip(4); + volume = stream.readUint16LE(); + stream.skip(6); + SceneChange::readData(stream); + return 0x2B; +} + +void PlayDigiSoundAndDie::execute(NancyEngine *engine){ + switch (state) { + case kBegin: + engine->sound->loadSound(filename, id, numLoops, volume); + state = kRun; + break; + case kRun: + if (!engine->sound->isSoundPlaying(id)) { + state = kEnd; + } + break; + case kEnd: + SceneChange::execute(engine); + break; + } } uint16 PlaySoundPanFrameAnchorAndDie::readData(Common::SeekableReadStream &stream) { diff --git a/engines/nancy/action/recordtypes.h b/engines/nancy/action/recordtypes.h index d4c3f12e84a..b5af7eb4361 100644 --- a/engines/nancy/action/recordtypes.h +++ b/engines/nancy/action/recordtypes.h @@ -337,10 +337,18 @@ public: virtual uint16 readData(Common::SeekableReadStream &stream) override; }; -class PlayDigiSoundAndDie : public ActionRecord { +class PlayDigiSoundAndDie : public SceneChange { public: virtual uint16 readData(Common::SeekableReadStream &stream) override; + virtual void execute(NancyEngine *engine) override; // TODO subclass into Play and Stop (?) + + Common::String filename; + int16 id = -1; // 0xA + uint16 numLoops = 0; // 0x10 + uint16 volume = 0; // 0x16, between 0 and 60 + // ... + // SceneChange elements at 0x1E }; class PlaySoundPanFrameAnchorAndDie : public ActionRecord { diff --git a/engines/nancy/audio.cpp b/engines/nancy/audio.cpp index 498125de269..6c36bf0881b 100644 --- a/engines/nancy/audio.cpp +++ b/engines/nancy/audio.cpp @@ -20,6 +20,10 @@ * */ +#include "engines/nancy/audio.h" +#include "engines/nancy/nancy.h" + +#include "common/system.h" #include "common/debug.h" #include "common/textconsole.h" #include "common/stream.h" @@ -172,4 +176,52 @@ Audio::SeekableAudioStream *makeHISStream(Common::SeekableReadStream *stream, Di return Audio::makeVorbisStream(subStream, DisposeAfterUse::YES); } +SoundManager::SoundManager(NancyEngine *engine) : + _engine(engine) { + _mixer = _engine->_system->getMixer(); +} + +// Combine load and play until i find a reason not to +void SoundManager::loadSound(Common::String &name, int16 id, uint16 numLoops, uint16 volume) { + if (_mixer->isSoundHandleActive(handles[id])) { + _mixer->stopHandle(handles[id]); + } + Common::SeekableReadStream *mSnd = SearchMan.createReadStreamForMember(name + ".his"); + if (mSnd) { + Audio::RewindableAudioStream *aStr = makeHISStream(mSnd, DisposeAfterUse::YES); + if (aStr) { + Audio::AudioStream *aStrLoop = Audio::makeLoopingAudioStream(aStr, numLoops); + _engine->_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &handles[id], aStrLoop, -1, volume * 255 / 60); + names[id] = name; + } + } +} + +void SoundManager::stopSound(int16 id) { + if (isSoundPlaying(id)) { + _mixer->stopHandle(handles[id]); + } + names[id] = Common::String(); +} + +bool SoundManager::isSoundPlaying(int16 id) { + if (id >= 0 && id < 20) { + return _mixer->isSoundHandleActive(handles[id]); + } + return false; +} + +// Returns whether the exception was skipped +bool SoundManager::stopAllSounds(Common::String *except) { + bool ret = false; + for (uint i = 0; i < 20; ++i) { + if (except == nullptr || names[i] != *except) { + stopSound(i); + } else { + ret = true; + } + } + return ret; +} + } // End of namespace Nancy \ No newline at end of file diff --git a/engines/nancy/audio.h b/engines/nancy/audio.h index fcbfbb71d57..0bc396b2348 100644 --- a/engines/nancy/audio.h +++ b/engines/nancy/audio.h @@ -23,6 +23,11 @@ #ifndef NANCY_AUDIO_H #define NANCY_AUDIO_H +#include "common/types.h" +#include "common/str.h" + +#include "audio/mixer.h" + namespace Common { class SeekableReadStream; } @@ -31,14 +36,31 @@ namespace Audio { class SeekableAudioStream; } -namespace DisposeAfterUse { -enum Flag; -} - namespace Nancy { +class NancyEngine; + Audio::SeekableAudioStream *makeHISStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse); +class SoundManager { +public: + enum SoundLoopType { kLoop = 0, kOneShot = 1 }; + SoundManager(NancyEngine *engine); + ~SoundManager() =default; + + void loadSound(Common::String &name, int16 id, uint16 numLoops = 0, uint16 volume = 60); + void stopSound(int16 id); + bool isSoundPlaying(int16 id); + bool stopAllSounds(Common::String *except = nullptr); + +private: + NancyEngine *_engine; + Audio::Mixer *_mixer; + + Audio::SoundHandle handles[20]; + Common::String names[20]; +}; + } // End of namespace Nancy #endif // NANCY_AUDIO_H \ No newline at end of file diff --git a/engines/nancy/datatypes.cpp b/engines/nancy/datatypes.cpp index c2bd73cc793..07021d28895 100644 --- a/engines/nancy/datatypes.cpp +++ b/engines/nancy/datatypes.cpp @@ -42,9 +42,12 @@ SceneSummary::SceneSummary(Common::SeekableReadStream *stream) { stream->seek(3, SEEK_CUR); videoFormat = stream->readUint16LE(); - stream->read(buf, 9); + stream->read(buf, 10); buf[9] = 0; audioFile = Common::String(buf); + audioID = stream->readSint16LE(); + stream->skip(0xE); + audioVolume = stream->readUint16LE(); stream->seek(0x72); verticalScrollDelta = stream->readUint16LE(); diff --git a/engines/nancy/datatypes.h b/engines/nancy/datatypes.h index f6655c7457e..e5dc8260b18 100644 --- a/engines/nancy/datatypes.h +++ b/engines/nancy/datatypes.h @@ -45,6 +45,8 @@ struct SceneSummary { // uint16 videoFormat; // 0x3E, value is 1 or 2 Common::String audioFile; // 0x40 + int16 audioID; // 0x4A + uint16 audioVolume; // 0x5A // uint16 verticalScrollDelta; // 0x72 uint16 horizontalEdgeSize; // 0x74 diff --git a/engines/nancy/logo.cpp b/engines/nancy/logo.cpp index 90f99543577..a9d18778a6e 100644 --- a/engines/nancy/logo.cpp +++ b/engines/nancy/logo.cpp @@ -63,15 +63,12 @@ void LogoSequence::init() { } void LogoSequence::startSound() { - Common::SeekableReadStream *mSnd = SearchMan.createReadStreamForMember(_engine->_menuSound.name + ".his"); - if (mSnd) { - Audio::RewindableAudioStream *aStr = makeHISStream(mSnd, DisposeAfterUse::YES); - if (aStr) { - Audio::AudioStream *aStrLoop = Audio::makeLoopingAudioStream(aStr, 0); - Audio::SoundHandle handle; - _engine->_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &handle, aStrLoop); - } - } + Common::SeekableReadStream *msnd = _engine->getBootChunkStream("MSND"); + char name[10]; + msnd->seek(0); + msnd->read(name, 10); + Common::String sname(name); + _engine->sound->loadSound(sname, 0); _startTicks = _engine->_system->getMillis(); _state = kRun; diff --git a/engines/nancy/nancy.cpp b/engines/nancy/nancy.cpp index 27a127ca783..19c9151d5bd 100644 --- a/engines/nancy/nancy.cpp +++ b/engines/nancy/nancy.cpp @@ -29,6 +29,7 @@ #include "engines/nancy/scene.h" #include "engines/nancy/graphics.h" #include "engines/nancy/input.h" +#include "engines/nancy/audio.h" #include "common/system.h" #include "common/random.h" @@ -77,6 +78,7 @@ NancyEngine::NancyEngine(OSystem *syst, const NancyGameDescription *gd) : sceneManager = new SceneManager(this); graphics = new GraphicsManager(this); input = new InputManager(this); + sound = new SoundManager(this); launchConsole = false; } @@ -90,6 +92,7 @@ NancyEngine::~NancyEngine() { delete logic; delete sceneManager; delete input; + delete sound; } GUI::Debugger *NancyEngine::getDebugger() { @@ -184,7 +187,6 @@ void NancyEngine::bootGameEngine() { if (!boot->load()) error("Failed to load boot script"); preloadCals(*boot); - readSound(*boot, "MSND", _menuSound); addBootChunk("BSUM", boot->getChunkStream("BSUM")); readBootSummary(*boot); @@ -323,15 +325,6 @@ void NancyEngine::readImageList(const IFF &boot, const Common::String &prefix, I } } -void NancyEngine::readSound(const IFF &boot, const Common::String &name, NancyEngine::Sound &sound) { - Common::SeekableReadStream *stream = boot.getChunkStream(name); - - if (!stream) - error("Failed to read BOOT %s", name.c_str()); - - sound.name = readFilename(stream); -} - class NancyEngine_v0 : public NancyEngine { public: NancyEngine_v0(OSystem *syst, const NancyGameDescription *gd) : NancyEngine(syst, gd) { } diff --git a/engines/nancy/nancy.h b/engines/nancy/nancy.h index 847758278cb..addd523e992 100644 --- a/engines/nancy/nancy.h +++ b/engines/nancy/nancy.h @@ -69,6 +69,7 @@ class SceneManager; class Logic; class GraphicsManager; class InputManager; +class SoundManager; class NancyEngine : public Engine { public: @@ -115,6 +116,7 @@ public: SceneManager *sceneManager; GraphicsManager *graphics; InputManager *input; + SoundManager *sound; // Contains all player data PlayState playState; @@ -138,10 +140,6 @@ protected: uint16 height; }; - struct Sound { - Common::String name; - }; - enum GameState { kBoot, kPartnerLogo, // v2 only @@ -173,12 +171,10 @@ protected: ImageList _objects; uint16 _firstSceneID; int32 _fontSize; - Sound _menuSound; GameFlow _gameFlow; void preloadCals(const IFF &boot); void readImageList(const IFF &boot, const Common::String &prefix, ImageList &list); - void readSound(const IFF &boot, const Common::String &name, Sound &sound); Common::String readFilename(Common::ReadStream *stream) const; virtual uint getFilenameLen() const = 0; diff --git a/engines/nancy/scene.cpp b/engines/nancy/scene.cpp index ff1387cf3b5..19b9d7f9781 100644 --- a/engines/nancy/scene.cpp +++ b/engines/nancy/scene.cpp @@ -27,6 +27,7 @@ #include "engines/nancy/logic.h" #include "engines/nancy/graphics.h" #include "engines/nancy/input.h" +#include "engines/nancy/audio.h" #include "common/memstream.h" #include "common/rect.h" @@ -49,8 +50,13 @@ void SceneManager::process() { load(); break; case kStartSound: - // TODO - break; + // Stop all sounds, unless the new scene's background music matches the last one's; + // in that case, stop everything except the background music + if (!_engine->sound->stopAllSounds(¤tScene.audioFile)) { + _engine->sound->loadSound(currentScene.audioFile, currentScene.audioID, 0, currentScene.audioVolume); + } + _state = kRun; + // fall through case kRun: run(); break; @@ -202,7 +208,7 @@ void SceneManager::load() { _engine->playState.lastDrawnViewFrame = -1; _engine->input->setPointerBitmap(0, 0, false); - _state = kRun; // TODO temp, is actually StartSound + _state = kStartSound; // TODO temp, is actually StartSound } void SceneManager::run() {