From f43c5d468784b72c92e557c83b22c9c34f8ce497 Mon Sep 17 00:00:00 2001 From: Matthew Stewart Date: Mon, 7 May 2018 16:40:13 -0400 Subject: STARTREK: Implement CD Audio handling. --- engines/startrek/graphics.cpp | 1 + engines/startrek/graphics.h | 1 - engines/startrek/sound.cpp | 90 ++++++++++++++++++++++++++++++++++++++++--- engines/startrek/sound.h | 17 ++++++-- engines/startrek/startrek.cpp | 50 ++++++++++++++---------- engines/startrek/startrek.h | 9 +++-- engines/startrek/text.cpp | 38 +++++++++++++----- 7 files changed, 164 insertions(+), 42 deletions(-) diff --git a/engines/startrek/graphics.cpp b/engines/startrek/graphics.cpp index 029928d021..ed4113db40 100755 --- a/engines/startrek/graphics.cpp +++ b/engines/startrek/graphics.cpp @@ -49,6 +49,7 @@ Graphics::Graphics(StarTrekEngine *vm) : _vm(vm), _egaMode(false) { _backgroundImage = new Bitmap(_vm->openFile("DEMON0.BMP").get()); _numSprites = 0; + _textboxVar1 = 2; CursorMan.showMouse(true); } diff --git a/engines/startrek/graphics.h b/engines/startrek/graphics.h index 578abdf705..47a0fbfe78 100755 --- a/engines/startrek/graphics.h +++ b/engines/startrek/graphics.h @@ -153,7 +153,6 @@ private: SharedPtr _activeMenu; uint16 _textboxButtonVar4; - uint16 _word_4B422; }; } diff --git a/engines/startrek/sound.cpp b/engines/startrek/sound.cpp index 436316233b..32b31901b8 100755 --- a/engines/startrek/sound.cpp +++ b/engines/startrek/sound.cpp @@ -24,8 +24,10 @@ #include "common/file.h" #include "common/macresman.h" -#include "audio/mods/protracker.h" +#include "audio/audiostream.h" #include "audio/decoders/raw.h" +#include "audio/decoders/voc.h" +#include "audio/mods/protracker.h" namespace StarTrek { @@ -59,6 +61,12 @@ Sound::Sound(StarTrekEngine *vm) : _vm(vm) { for (int i=1; i<8; i++) { _sfxSlotList.push_back(&_midiSlots[i]); } + + if (!SearchMan.hasFile("voc/speech.mrk")) { + error("Couldn't find 'voc/speech.mrk'. The 'trekcd/voc/' directory must be dumped from the CD"); + } + + _playingSpeech = false; } Sound::~Sound() { @@ -71,7 +79,7 @@ Sound::~Sound() { void Sound::playMidiTrack(int track) { - if (!_vm->_midiAudioEnabled) + if (!_vm->_musicEnabled) return; /* if (!_vm->_word_467a8) @@ -94,7 +102,7 @@ void Sound::playMidiTrack(int track) { } // Take the least recently used slot and use that for the sound effect - MidiSlot *slot = _sfxSlotList.front(); + MidiPlaybackSlot *slot = _sfxSlotList.front(); _sfxSlotList.pop_front(); _sfxSlotList.push_back(slot); slot->track = track; @@ -124,10 +132,75 @@ void Sound::playSoundEffect(const char *baseSoundName) { playMacSoundEffect(baseSoundName); else */ - error("PC Sound Effects Not Supported"); + if (scumm_stricmp(baseSoundName+4, "loop") == 0) + _loopingAudioName = Common::String(baseSoundName); + + if (!_vm->_sfxEnabled || !_vm->_audioEnabled) + return; + + /* + if (word_5113a == 0) + sub_2aaa3(); + */ + + for (int i=0; i_system->getMixer()->isSoundHandleActive(_sfxHandles[i])) + continue; + + Common::String soundName = Common::String("voc/sfx/") + baseSoundName + ".voc"; + Common::SeekableReadStream *readStream = SearchMan.createReadStreamForMember(soundName); + if (readStream == nullptr) + error("Couldn't open '%s'", soundName.c_str()); + + Audio::AudioStream *audioStream = Audio::makeVOCStream(readStream, Audio::FLAG_UNSIGNED, DisposeAfterUse::YES); + _vm->_system->getMixer()->playStream(Audio::Mixer::kSFXSoundType, &_sfxHandles[i], audioStream); + return; + } } -// PC Functions +void Sound::playSpeech(const Common::String &basename) { + stopPlayingSpeech(); + + Audio::QueuingAudioStream *audioQueue = nullptr; + Common::String name = basename; + + // Play a list of comma-separated audio files in sequence (usually there's only one) + while (!name.empty()) { + uint i = 0; + while (i < name.size() && name[i] != ',') { + if (name[i] == '\\') + name.setChar('/', i); + i++; + } + + Common::String filename = "voc/" + Common::String(name.c_str(), name.c_str()+i) + ".voc"; + debug("Playing speech '%s'", filename.c_str()); + Common::SeekableReadStream *readStream = SearchMan.createReadStreamForMember(filename); + if (readStream == nullptr) + error("Couldn't open '%s'", filename.c_str()); + + Audio::AudioStream *audioStream = Audio::makeVOCStream(readStream, Audio::FLAG_UNSIGNED, DisposeAfterUse::YES); + if (audioQueue == nullptr) + audioQueue = Audio::makeQueuingAudioStream(audioStream->getRate(), audioStream->isStereo()); + audioQueue->queueAudioStream(audioStream, DisposeAfterUse::YES); + + name.erase(0,i+1); + } + + if (audioQueue != nullptr) { + audioQueue->finish(); + _vm->_system->getMixer()->playStream(Audio::Mixer::kSpeechSoundType, &_speechHandle, audioQueue); + } + + _playingSpeech = true; +} + +void Sound::stopPlayingSpeech() { + if (_playingSpeech) { + _playingSpeech = false; + _vm->_system->getMixer()->stopHandle(_speechHandle); + } +} // XMIDI or SM sound @@ -179,10 +252,17 @@ void Sound::clearAllMidiSlots() { } } +// Static callback method void Sound::midiDriverCallback(void *data) { Sound *s = (Sound*)data; for (int i=0; i<8; i++) s->_midiSlots[i].midiParser->onTimer(); + + // TODO: put this somewhere other than the midi callback... + if (s->_playingSpeech && !s->_vm->_system->getMixer()->isSoundHandleActive(s->_speechHandle)) { + s->stopPlayingSpeech(); + s->_vm->_finishedPlayingSpeech = true; + } } diff --git a/engines/startrek/sound.h b/engines/startrek/sound.h index f314f2c688..a9116627d5 100755 --- a/engines/startrek/sound.h +++ b/engines/startrek/sound.h @@ -41,12 +41,16 @@ namespace StarTrek { class StarTrekEngine; -struct MidiSlot { +const int MAX_LOADED_SFX_FILES = 8; +const int MAX_SFX_PLAYING = 4; + +struct MidiPlaybackSlot { int slot; int track; MidiParser *midiParser; }; + class Sound { public: Sound(StarTrekEngine *vm); @@ -56,6 +60,8 @@ public: void loadMusicFile(const char *baseSoundName); void playSoundEffect(const char *baseSoundName); + void playSpeech(const Common::String &basename); + void stopPlayingSpeech(); private: StarTrekEngine *_vm; @@ -67,12 +73,17 @@ private: // MIDI-Related Variables MidiDriver *_midiDriver; - MidiSlot _midiSlots[8]; // 0 is for music; 1-7 are for sfx - Common::List _sfxSlotList; // Sorts midi slots by most recently used + MidiPlaybackSlot _midiSlots[8]; // 0 is for music; 1-7 are for sfx + Common::List _sfxSlotList; // Sorts midi slots by most recently used byte *loadedSoundData; uint32 _midiDevice; + // VOC-related variables + Common::String _loopingAudioName; + Audio::SoundHandle _sfxHandles[MAX_SFX_PLAYING]; + Audio::SoundHandle _speechHandle; + bool _playingSpeech; // Driver callback static void midiDriverCallback(void *data); diff --git a/engines/startrek/startrek.cpp b/engines/startrek/startrek.cpp index dba8a48756..d888d1f710 100755 --- a/engines/startrek/startrek.cpp +++ b/engines/startrek/startrek.cpp @@ -42,8 +42,11 @@ StarTrekEngine::StarTrekEngine(OSystem *syst, const StarTrekGameDescription *gam _gfx = nullptr; _sound = nullptr; _macResFork = nullptr; - _room = nullptr; + + _audioEnabled = true; + _musicEnabled = true; + _sfxEnabled = true; } StarTrekEngine::~StarTrekEngine() { @@ -55,9 +58,6 @@ StarTrekEngine::~StarTrekEngine() { } Common::Error StarTrekEngine::run() { - _cdAudioEnabled = true; - _midiAudioEnabled = true; - _gfx = new Graphics(this); _sound = new Sound(this); @@ -101,7 +101,7 @@ Common::Error StarTrekEngine::run() { _gfx->loadPri("DEMON0.PRI"); _gfx->redrawScreen(); - _sound->loadMusicFile("BRIDGEW"); + _sound->loadMusicFile("GROUND"); } else { _gfx->drawBackgroundImage("BRIDGE.BGD"); } @@ -191,50 +191,50 @@ void StarTrekEngine::pollSystemEvents() { } void StarTrekEngine::playSoundEffectIndex(int index) { - switch(index-4) { - case 0: + switch(index) { + case 0x04: _sound->playSoundEffect("tricorde"); break; - case 1: + case 0x05: _sound->playSoundEffect("STDOOR1"); break; - case 2: + case 0x06: _sound->playSoundEffect("PHASSHOT"); break; - case 3: + case 0x07: _sound->playMidiTrack(index); break; - case 4: + case 0x08: _sound->playSoundEffect("TRANSDEM"); break; - case 5: + case 0x09: _sound->playSoundEffect("TRANSMAT"); break; - case 6: + case 0x0a: _sound->playSoundEffect("TRANSENE"); break; - case 0x0c: // Menu selection sound + case 0x10: // Menu selection sound _sound->playMidiTrack(index); break; - case 0x1e: + case 0x22: _sound->playSoundEffect("HAILING"); break; - case 0x20: + case 0x24: _sound->playSoundEffect("PHASSHOT"); break; - case 0x21: + case 0x25: _sound->playSoundEffect("PHOTSHOT"); break; - case 0x22: + case 0x26: _sound->playSoundEffect("HITSHIEL"); break; - case 0x23: + case 0x27: _sound->playMidiTrack(index); break; - case 0x24: + case 0x28: _sound->playSoundEffect("REDALERT"); break; - case 0x25: + case 0x29: _sound->playSoundEffect("WARP"); break; default: @@ -242,6 +242,14 @@ void StarTrekEngine::playSoundEffectIndex(int index) { } } +void StarTrekEngine::playSpeech(const Common::String &filename) { + _sound->playSpeech(filename.c_str()); +} + +void StarTrekEngine::stopPlayingSpeech() { + _sound->stopPlayingSpeech(); +} + void StarTrekEngine::updateClockTicks() { // TODO (based on DOS interrupt 1A, AH=0; read system clock counter) diff --git a/engines/startrek/startrek.h b/engines/startrek/startrek.h index c190659d6a..232482a767 100755 --- a/engines/startrek/startrek.h +++ b/engines/startrek/startrek.h @@ -92,6 +92,8 @@ public: void pollSystemEvents(); void playSoundEffectIndex(int index); + void playSpeech(const Common::String &filename); + void stopPlayingSpeech(); // Events public: @@ -135,11 +137,12 @@ public: uint32 _clockTicks; - bool _midiAudioEnabled; - bool _cdAudioEnabled; + bool _musicEnabled; + bool _sfxEnabled; uint16 _word_467a6; uint16 _word_467a8; - bool _textboxVar4; + bool _audioEnabled; + bool _finishedPlayingSpeech; private: diff --git a/engines/startrek/text.cpp b/engines/startrek/text.cpp index fe2f0eeb6c..2d875da397 100644 --- a/engines/startrek/text.cpp +++ b/engines/startrek/text.cpp @@ -88,12 +88,12 @@ int Graphics::showText(TextGetterFunc textGetter, int var, int xoffset, int yoff int choiceIndex = 0; int scrollOffset = 0; if (tmpTextboxVar1 != 0 && tmpTextboxVar1 != 1 && numChoices == 1 - && _vm->_cdAudioEnabled && !_vm->_textboxVar4) + && _vm->_sfxEnabled && !_vm->_audioEnabled) _textboxHasMultipleChoices = false; else _textboxHasMultipleChoices = true; - if (tmpTextboxVar1 >= 0 && tmpTextboxVar1 <= 2 && _vm->_cdAudioEnabled && !_vm->_textboxVar4) + if (tmpTextboxVar1 >= 0 && tmpTextboxVar1 <= 2 && _vm->_sfxEnabled && !_vm->_audioEnabled) _textboxVar6 = true; else _textboxVar6 = false; @@ -268,7 +268,7 @@ reloadText: } _textboxVar2 = _textboxVar3; - // sub_29EE3(); + _vm->stopPlayingSpeech(); return choiceIndex; } @@ -312,8 +312,8 @@ int Graphics::handleTextboxEvents(uint32 ticksUntilClickingEnabled, bool arg4) { // sub_10BE7(); // sub_2A4B1(); - if (_word_4B422 != 0) { - _word_4B422 = 0; + if (_vm->_finishedPlayingSpeech != 0) { + _vm->_finishedPlayingSpeech = 0; if (_textboxVar1 != 0) { return TEXTEVENT_SPEECH_DONE; } @@ -539,13 +539,13 @@ String Graphics::readLineFormattedText(TextGetterFunc textGetter, int var, int c String headerText; String text = (this->*textGetter)(choiceIndex, &var, &headerText); - if (_textboxVar1 == 2 && _vm->_cdAudioEnabled && _vm->_textboxVar4) { + if (_textboxVar1 == 2 && _vm->_sfxEnabled && _vm->_audioEnabled) { uint32 oldSize = text.size(); text = playTextAudio(text); if (oldSize != text.size()) _textboxHasMultipleChoices = true; } - else if ((_textboxVar1 == 0 || _textboxVar1 == 1) && _vm->_cdAudioEnabled && _vm->_textboxVar4) { + else if ((_textboxVar1 == 0 || _textboxVar1 == 1) && _vm->_sfxEnabled && _vm->_audioEnabled) { text = playTextAudio(text); } else { @@ -682,9 +682,29 @@ String Graphics::skipTextAudioPrompt(const String &str) { return String(text+1); } +/** + * Plays an audio prompt, if it exists, and returns the string starting at the end of the + * prompt. + */ String Graphics::playTextAudio(const String &str) { - // TODO - return skipTextAudioPrompt(str); + const char *text = str.c_str(); + char soundFile[0x100]; + + if (*text != '#') + return str; + + int len = 0; + text++; + while (*text != '#') { + if (*text == '\0' || len > 0xfa) + return str; + soundFile[len++] = *text++; + } + + soundFile[len] = '\0'; + _vm->playSpeech(soundFile); + + return String(text+1); } /** -- cgit v1.2.3