From b2e97060adf93760f7e4bce97e691e34fc258922 Mon Sep 17 00:00:00 2001 From: Paul Gilbert Date: Sun, 16 Sep 2007 04:06:49 +0000 Subject: Initial sound implementation svn-id: r28917 --- engines/lure/game.cpp | 4 +- engines/lure/intro.cpp | 58 ++++--- engines/lure/lure.cpp | 9 +- engines/lure/luredefs.h | 6 +- engines/lure/scripts.cpp | 9 +- engines/lure/scripts.h | 2 +- engines/lure/sound.cpp | 407 ++++++++++++++++++++++++++++++++++++++++++++--- engines/lure/sound.h | 92 ++++++++++- engines/lure/surface.cpp | 2 +- 9 files changed, 530 insertions(+), 59 deletions(-) (limited to 'engines') diff --git a/engines/lure/game.cpp b/engines/lure/game.cpp index f3576bb3bd..e4aeef2253 100644 --- a/engines/lure/game.cpp +++ b/engines/lure/game.cpp @@ -33,6 +33,7 @@ #include "lure/strings.h" #include "common/config-manager.h" +#include "common/system.h" namespace Lure { @@ -300,7 +301,7 @@ void Game::handleMenuResponse(uint8 selection) { break; case MENUITEM_RESTART_GAME: - doQuit(); + doRestart(); break; case MENUITEM_SAVE_GAME: @@ -883,6 +884,7 @@ void Game::doSound() { _soundFlag = !_soundFlag; menu.getMenu(2).entries()[2] = sl.getString(_soundFlag ? S_SOUND_ON : S_SOUND_OFF); + Sound.setVolume(_soundFlag ? DEFAULT_VOLUME : 0); } void Game::handleBootParam(int value) { diff --git a/engines/lure/intro.cpp b/engines/lure/intro.cpp index 41146742b7..ae4199051e 100644 --- a/engines/lure/intro.cpp +++ b/engines/lure/intro.cpp @@ -26,20 +26,26 @@ #include "lure/intro.h" #include "lure/animseq.h" #include "lure/events.h" +#include "lure/sound.h" namespace Lure { struct AnimRecord { uint16 resourceId; uint8 paletteIndex; - bool initialPause; - bool endingPause; + uint16 initialPause; + uint16 endingPause; + uint8 soundNumber; }; static const uint16 start_screens[] = {0x18, 0x1A, 0x1E, 0x1C, 0}; -static const AnimRecord anim_screens[] = {{0x40, 0, true, true}, {0x42, 1, false, true}, - {0x44, 2, false, false}, {0x24, 3, false, true}, {0x46, 3, false, false}, - {0, 0, false, false}}; +static const AnimRecord anim_screens[] = { + {0x40, 0, 0x35A, 0xC8, 0}, // The kingdom was at peace + {0x42, 1, 0, 0x5FA, 1}, // Cliff overhang + {0x44, 2, 0, 0, 2}, // Siluette in moonlight + {0x24, 3, 0, 0x328 + 0x24, 0xff}, // Exposition of reaching town + {0x46, 3, 0, 0, 3}, // Skorl approaches + {0, 0, 0, 0, 0xff}}; // showScreen // Shows a screen by loading it from the given resource, and then fading it in @@ -73,25 +79,38 @@ bool Introduction::show() { if (showScreen(start_screens[ctr], start_screens[ctr] + 1, 5000)) return true; - AnimationSequence *anim; - bool result; - // Animated screens + AnimationSequence *anim; + bool result; + uint8 currentSound = 0xff; PaletteCollection coll(0x32); const AnimRecord *curr_anim = anim_screens; for (; curr_anim->resourceId; ++curr_anim) { + // Handle sound selection + if (curr_anim->soundNumber != 0xff) { + if (currentSound != 0xff) + // Fade out the previous sound + Sound.fadeOut(); + + currentSound = curr_anim->soundNumber; + Sound.musicInterface_Play(currentSound, 0); + // DEBUG TEST +// g_system->delayMillis(1000); +// Sound.musicInterface_Play(1, 1); + } + bool fadeIn = curr_anim == anim_screens; anim = new AnimationSequence(_screen, _system, curr_anim->resourceId, coll.getPalette(curr_anim->paletteIndex), fadeIn); - if (curr_anim->initialPause) - if (events.interruptableDelay(12000)) return true; + if (curr_anim->initialPause != 0) + if (events.interruptableDelay(curr_anim->initialPause * 1000 / 50)) return true; result = false; switch (anim->show()) { case ABORT_NONE: - if (curr_anim->endingPause) { - result = events.interruptableDelay(12000); + if (curr_anim->endingPause != 0) { + result = events.interruptableDelay(curr_anim->endingPause * 1000 / 50); } break; @@ -104,7 +123,10 @@ bool Introduction::show() { } delete anim; - if (result) return true; + if (result) { + Sound.musicInterface_KillAll(); + return true; + } } // Show battle pictures one frame at a time @@ -118,12 +140,12 @@ bool Introduction::show() { if (result) break; } while (anim->step()); delete anim; - if (result) return true; - - // Show final introduction screen - - showScreen(0x22, 0x21, 10000); + + if (!result) + // Show final introduction screen + showScreen(0x22, 0x21, 10000); + Sound.musicInterface_KillAll(); return false; } diff --git a/engines/lure/lure.cpp b/engines/lure/lure.cpp index 7359ce9fb8..ada8f5081b 100644 --- a/engines/lure/lure.cpp +++ b/engines/lure/lure.cpp @@ -34,6 +34,7 @@ #include "lure/lure.h" #include "lure/intro.h" #include "lure/game.h" +#include "lure/sound.h" namespace Lure { @@ -45,17 +46,16 @@ LureEngine::LureEngine(OSystem *system): Engine(system) { Common::addSpecialDebugLevel(kLureDebugAnimations, "animations", "Animations debugging"); Common::addSpecialDebugLevel(kLureDebugHotspots, "hotspots", "Hotspots debugging"); Common::addSpecialDebugLevel(kLureDebugFights, "fights", "Fights debugging"); + Common::addSpecialDebugLevel(kLureDebugSounds, "sounds", "Sounds debugging"); // Setup mixer -/* + if (!_mixer->isReady()) { warning("Sound initialization failed."); } _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume")); _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume")); - _mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, ConfMan.getInt("speech_volume")); -*/ _features = 0; _game = 0; @@ -106,7 +106,9 @@ LureEngine &LureEngine::getReference() { int LureEngine::go() { if (ConfMan.getInt("boot_param") == 0) { // Show the introduction + Sound.loadSection(INTRO_SOUND_RESOURCE_ID); Introduction *intro = new Introduction(*_screen, *_system); + intro->show(); delete intro; } @@ -114,6 +116,7 @@ int LureEngine::go() { // Play the game if (!_events->quitFlag) { // Play the game + Sound.loadSection(MAIN_SOUND_RESOURCE_ID); Game *gameInstance = new Game(); gameInstance->execute(); delete gameInstance; diff --git a/engines/lure/luredefs.h b/engines/lure/luredefs.h index cd7f483f2f..ef48f6f452 100644 --- a/engines/lure/luredefs.h +++ b/engines/lure/luredefs.h @@ -45,7 +45,8 @@ enum { kLureDebugScripts = 1 << 0, kLureDebugAnimations = 1 << 1, kLureDebugHotspots = 1 << 2, - kLureDebugFights = 1 << 3 + kLureDebugFights = 1 << 3, + kLureDebugSounds = 1 << 4 }; #define ERROR_BASIC 1 @@ -229,6 +230,8 @@ enum CursorType {CURSOR_ARROW = 0, CURSOR_DISK = 1, CURSOR_TIME_START = 2, // Miscellaneous resources #define NAMES_RESOURCE_ID 9 +#define MAIN_SOUND_RESOURCE_ID 0xC +#define INTRO_SOUND_RESOURCE_ID 0x31 #define NOONE_ID 0x3E7 #define PLAYER_ID 0x3E8 #define RATPOUCH_ID 0x3E9 @@ -291,6 +294,7 @@ enum CursorType {CURSOR_ARROW = 0, CURSOR_DISK = 1, CURSOR_TIME_START = 2, #define EWAN_ALT_ANIM_ID 0x59ED #define PLAYER_ANIM_ID 0x5C80 #define SELENA_ANIM_ID 0x5CAA +#define DEFAULT_VOLUME 192 #define CONVERSE_COUNTDOWN_SIZE 40 #define IDLE_COUNTDOWN_SIZE 15 diff --git a/engines/lure/scripts.cpp b/engines/lure/scripts.cpp index f24e5c301f..bba364bcf2 100644 --- a/engines/lure/scripts.cpp +++ b/engines/lure/scripts.cpp @@ -720,14 +720,11 @@ void Script::checkCellDoor(uint16 v1, uint16 v2, uint16 v3) { // Checks if a sound is running -void Script::checkSound(uint16 hotspotId, uint16 v2, uint16 v3) { +void Script::checkSound(uint16 soundNumber, uint16 v2, uint16 v3) { Sound.tidySounds(); - // For now, simply set the general value field so that the Skorl schedule - // will work properly - Resources::getReference().fieldList().setField(GENERAL, 0); - - // TODO: Check whether active sound can be found or not + SoundDescResource *rec = Sound.findSound(soundNumber); + Resources::getReference().fieldList().setField(GENERAL, (rec != NULL) ? 1 : 0); } typedef void(*SequenceMethodPtr)(uint16, uint16, uint16); diff --git a/engines/lure/scripts.h b/engines/lure/scripts.h index bcc9147896..4ad5226e70 100644 --- a/engines/lure/scripts.h +++ b/engines/lure/scripts.h @@ -142,7 +142,7 @@ public: static void addActions(uint16 hotspotId, uint16 actions, uint16 v3); static void randomToGeneral(uint16 maxVal, uint16 minVal, uint16 v3); static void checkCellDoor(uint16 v1, uint16 v2, uint16 v3); - static void checkSound(uint16 v1, uint16 v2, uint16 v3); + static void checkSound(uint16 soundNumber, uint16 v2, uint16 v3); }; class HotspotScript { diff --git a/engines/lure/sound.cpp b/engines/lure/sound.cpp index b0fb3a54f6..bf9a2032ab 100644 --- a/engines/lure/sound.cpp +++ b/engines/lure/sound.cpp @@ -22,9 +22,14 @@ #include "lure/sound.h" #include "lure/game.h" +#include "lure/memory.h" #include "lure/res.h" #include "lure/room.h" +#include "common/config-manager.h" +#include "common/endian.h" +#include "sound/midiparser.h" + DECLARE_SINGLETON(Lure::SoundManager); namespace Lure { @@ -32,12 +37,65 @@ namespace Lure { SoundManager::SoundManager() { _descs = Disk::getReference().getEntry(SOUND_DESC_RESOURCE_ID); _numDescs = _descs->size() / sizeof(SoundDescResource); + _soundData = NULL; + + int midiDriver = MidiDriver::detectMusicDriver(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MIDI); + _nativeMT32 = ((midiDriver == MD_MT32) || ConfMan.getBool("native_mt32")); + + memset(_channelsInUse, false, NUM_CHANNELS_OUTER); + + _driver = MidiDriver::createMidi(midiDriver); + int statusCode = _driver->open(); + if (statusCode) { + warning("Sound driver returned error code %d", statusCode); + _driver = NULL; - for (int channelNum = 0; channelNum < NUM_CHANNELS; ++channelNum) - _channels[channelNum] = 0; + } else { + if (_nativeMT32) + _driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE); + + for (int index = 0; index < NUM_CHANNELS_INNER; ++index) { + _channelsInner[index].midiChannel = _driver->allocateChannel(); + _channelsInner[index].volume = DEFAULT_VOLUME; + } + } +} + +SoundManager::~SoundManager() { + if (_driver) + _driver->setTimerCallback(this, NULL); + + removeSounds(); + _activeSounds.clear(); + _playingSounds.clear(); + + + delete _descs; + if (_soundData) + delete _soundData; + + if (_driver) + _driver->close(); + _driver = NULL; +} + +void SoundManager::loadSection(uint16 sectionId) { + debugC(ERROR_BASIC, kLureDebugSounds, "SoundManager::loadSection = %xh", sectionId); + killSounds(); + + if (_soundData) { + delete _soundData; + _driver->setTimerCallback(this, NULL); + } + + _soundData = Disk::getReference().getEntry(sectionId); + _soundsTotal = *_soundData->data(); + + _driver->setTimerCallback(this, &onTimer); } void SoundManager::bellsBodge() { + debugC(ERROR_BASIC, kLureDebugSounds, "SoundManager::bellsBodge"); Resources &res = Resources::getReference(); Room &room = Room::getReference(); @@ -64,16 +122,19 @@ void SoundManager::bellsBodge() { } void SoundManager::killSounds() { + debugC(ERROR_BASIC, kLureDebugSounds, "SoundManager::killSounds"); + // Stop the player playing all sounds musicInterface_KillAll(); // Clear the active sounds _activeSounds.clear(); - for (int channelNum = 0; channelNum < NUM_CHANNELS; ++channelNum) - _channels[channelNum] = 0; + for (int channelNum = 0; channelNum < NUM_CHANNELS_INNER; ++channelNum) + _channelsInUse[channelNum] = false; } void SoundManager::addSound(uint8 soundIndex, bool tidyFlag) { + debugC(ERROR_BASIC, kLureDebugSounds, "SoundManager::addSound index=%d", soundIndex); Game &game = Game::getReference(); if (tidyFlag) @@ -87,13 +148,13 @@ void SoundManager::addSound(uint8 soundIndex, bool tidyFlag) { int numChannels = (rec.numChannels >> 2) & 3; int channelCtr = 0; - while (channelCtr < NUM_CHANNELS) { - if (_channels[channelCtr] == 0) { + while (channelCtr <= (NUM_CHANNELS_OUTER - numChannels)) { + if (!_channelsInUse[channelCtr]) { bool foundSpace = true; - int channelCtr2 = 0; + int channelCtr2 = 1; while (channelCtr2 < numChannels) { - foundSpace = _channels[channelCtr2] == 0; + foundSpace = !_channelsInUse[channelCtr + channelCtr2]; if (!foundSpace) break; ++channelCtr2; } @@ -105,13 +166,15 @@ void SoundManager::addSound(uint8 soundIndex, bool tidyFlag) { ++channelCtr; } - if (channelCtr == 8) + if (channelCtr > NUM_CHANNELS_OUTER - numChannels) { // No channels free + debugC(ERROR_BASIC, kLureDebugSounds, "SoundManager::addSound - no channels free"); return; + } // Mark the found channels as in use for (int channelCtr2 = 0; channelCtr2 < numChannels; ++channelCtr2) - _channels[channelCtr + channelCtr2] = 1; + _channelsInUse[channelCtr + channelCtr2] = true; SoundDescResource *newEntry = new SoundDescResource(); newEntry->soundNumber = rec.soundNumber; @@ -121,11 +184,18 @@ void SoundManager::addSound(uint8 soundIndex, bool tidyFlag) { newEntry->volume = rec.volume; _activeSounds.push_back(newEntry); - musicInterface_Play(rec.soundNumber, false, channelCtr); + // TODO: Figure a better way of sharing channels between multiple parsers - currently + // each parser seems to use 8 channels of a maximum 16 available, but here I'm + // overlapping channels 4 - 7 (3rd & 4th parser) across the other two + byte innerChannel = (channelCtr < 4) ? ((channelCtr / 2) * 8) : + (4 + (channelCtr / 2) * 8); + + musicInterface_Play(rec.soundNumber, innerChannel); setVolume(rec.soundNumber, rec.volume); } void SoundManager::addSound2(uint8 soundIndex) { + debugC(ERROR_BASIC, kLureDebugSounds, "SoundManager::addSound2 index=%d", soundIndex); tidySounds(); if (soundIndex == 6) @@ -142,36 +212,55 @@ void SoundManager::addSound2(uint8 soundIndex) { void SoundManager::stopSound(uint8 soundIndex) { + debugC(ERROR_BASIC, kLureDebugSounds, "SoundManager::stopSound index=%d", soundIndex); SoundDescResource &rec = soundDescs()[soundIndex]; musicInterface_Stop(rec.soundNumber & 0x7f); } void SoundManager::killSound(uint8 soundNumber) { + debugC(ERROR_BASIC, kLureDebugSounds, "SoundManager::stopSound soundNumber=%d", soundNumber); musicInterface_Stop(soundNumber & 0x7f); } void SoundManager::setVolume(uint8 soundNumber, uint8 volume) { + debugC(ERROR_BASIC, kLureDebugSounds, "SoundManager::setVolume soundNumber=%d, volume=%d", + soundNumber, volume); + musicInterface_TidySounds(); + SoundDescResource *entry = findSound(soundNumber); - if (entry == NULL) return; + if (entry) + musicInterface_SetVolume(entry->channel, volume); +} + +void SoundManager::setVolume(uint8 volume) { + debugC(ERROR_BASIC, kLureDebugSounds, "SoundManager::setVolume volume=%d", volume); - // Special check is done for Adlib in original game, to ignore any volume changes + for (int index = 0; index < NUM_CHANNELS_INNER; ++index) { + _channelsInner[index].midiChannel->volume(volume); + _channelsInner[index].volume = volume; + } } SoundDescResource *SoundManager::findSound(uint8 soundNumber) { + debugC(ERROR_BASIC, kLureDebugSounds, "SoundManager::findSound soundNumber=%d", soundNumber); ManagedList::iterator i; for (i = _activeSounds.begin(); i != _activeSounds.end(); ++i) { SoundDescResource *rec = *i; - if (rec->soundNumber == soundNumber) + if (rec->soundNumber == soundNumber) { + debugC(ERROR_INTERMEDIATE, kLureDebugSounds, "SoundManager::findSound - sound found"); return rec; + } } // Signal that sound wasn't found + debugC(ERROR_INTERMEDIATE, kLureDebugSounds, "SoundManager::findSound - sound not found"); return NULL; } void SoundManager::tidySounds() { + debugC(ERROR_INTERMEDIATE, kLureDebugSounds, "SoundManager::tidySounds"); ManagedList::iterator i = _activeSounds.begin(); while (i != _activeSounds.end()) { @@ -183,7 +272,7 @@ void SoundManager::tidySounds() { else { // Mark the channels that it used as now being free for (int channelCtr = 0; channelCtr < rec->numChannels; ++channelCtr) - _channels[rec->channel + channelCtr] = 0; + _channelsInUse[rec->channel + channelCtr] = false; i = _activeSounds.erase(i); } @@ -191,6 +280,7 @@ void SoundManager::tidySounds() { } void SoundManager::removeSounds() { + debugC(ERROR_BASIC, kLureDebugSounds, "SoundManager::removeSounds"); bellsBodge(); ManagedList::iterator i = _activeSounds.begin(); @@ -206,7 +296,7 @@ void SoundManager::removeSounds() { } void SoundManager::restoreSounds() { - + debugC(ERROR_BASIC, kLureDebugSounds, "SoundManager::restoreSounds"); ManagedList::iterator i = _activeSounds.begin(); while (i != _activeSounds.end()) { @@ -214,9 +304,9 @@ void SoundManager::restoreSounds() { if ((rec->numChannels != 0) && ((rec->flags & SF_RESTORE) != 0)) { for (int channelCtr = 0; channelCtr < rec->numChannels; ++channelCtr) - _channels[rec->channel + channelCtr] = 1; + _channelsInUse[rec->channel + channelCtr] = true; - musicInterface_Play(rec->soundNumber, false, rec->channel); + musicInterface_Play(rec->soundNumber, rec->channel); musicInterface_SetVolume(rec->soundNumber, rec->volume); } @@ -224,47 +314,314 @@ void SoundManager::restoreSounds() { } } +void SoundManager::fadeOut() { + debugC(ERROR_BASIC, kLureDebugSounds, "SoundManager::fadeOut"); + + // Fade out all the active sounds + musicInterface_TidySounds(); + + bool inProgress = true; + while (inProgress) + { + inProgress = false; + + ManagedList::iterator i; + for (i = _playingSounds.begin(); i != _playingSounds.end(); ++i) { + MidiMusic *music = *i; + if (music->getVolume() > 0) { + inProgress = true; + music->setVolume(music->getVolume() > 4 ? (music->getVolume() - 10) : 0); + } + } + + g_system->delayMillis(10); + } + + // Kill all the sounds + musicInterface_KillAll(); +} /*------------------------------------------------------------------------*/ -// musicInterface_CheckPlaying +// musicInterface_Play // Play the specified sound -void SoundManager::musicInterface_Play(uint8 soundNumber, bool isEffect, uint8 channelNumber) { +void SoundManager::musicInterface_Play(uint8 soundNumber, uint8 channelNumber) { + debugC(ERROR_INTERMEDIATE, kLureDebugSounds, "musicInterface_Play soundNumber=%d, channel=%d", + soundNumber, channelNumber); + + if (!_soundData) + error("Sound section has not been specified"); + + uint8 soundNum = soundNumber & 0x7f; + if (soundNum > _soundsTotal) + error("Invalid sound index %d requested", soundNum); + + if (_driver == NULL) + // Only play sounds if a sound driver is active + return; + uint32 dataOfs = READ_LE_UINT32(_soundData->data() + soundNum * 4 + 2); + uint8 *soundStart = _soundData->data() + dataOfs; + uint32 dataSize; + + if (soundNumber == _soundsTotal - 1) + dataSize = _soundData->size() - dataOfs; + else { + uint32 nextDataOfs = READ_LE_UINT32(_soundData->data() + (soundNum + 1) * 4 + 2); + dataSize = nextDataOfs - dataOfs; + } + + MidiMusic *sound = new MidiMusic(_driver, _channelsInner, channelNumber, soundNumber, + soundStart, dataSize); + _playingSounds.push_back(sound); } // musicInterface_Stop // Stops the specified sound from playing void SoundManager::musicInterface_Stop(uint8 soundNumber) { - + debugC(ERROR_INTERMEDIATE, kLureDebugSounds, "musicInterface_Stop soundNumber=%d", soundNumber); + musicInterface_TidySounds(); + uint8 soundNum = soundNumber & 0x7f; + + ManagedList::iterator i; + for (i = _playingSounds.begin(); i != _playingSounds.end(); ++i) { + MidiMusic *music = *i; + if (music->soundNumber() == soundNum) { + _playingSounds.erase(i); + return; + } + } } // musicInterface_CheckPlaying -// Returns true if a sound is still player +// Returns true if a sound is still playing bool SoundManager::musicInterface_CheckPlaying(uint8 soundNumber) { - return true; + debugC(ERROR_DETAILED, kLureDebugSounds, "musicInterface_CheckPlaying soundNumber=%d", soundNumber); + musicInterface_TidySounds(); + uint8 soundNum = soundNumber & 0x7f; + + ManagedList::iterator i; + for (i = _playingSounds.begin(); i != _playingSounds.end(); ++i) { + MidiMusic *music = *i; + if (music->soundNumber() == soundNum) + return true; + } + + return false; } // musicInterface_SetVolume // Sets the volume of the specified channel void SoundManager::musicInterface_SetVolume(uint8 channelNum, uint8 volume) { - + debugC(ERROR_INTERMEDIATE, kLureDebugSounds, "musicInterface_SetVolume channel=%d, volume=%d", + channelNum, volume); + musicInterface_TidySounds(); + + ManagedList::iterator i; + for (i = _playingSounds.begin(); i != _playingSounds.end(); ++i) { + MidiMusic *music = *i; + if (music->channelNumber() == channelNum) + music->setVolume(volume); + } } +// musicInterface_KillAll +// Stops all currently active sounds playing + void SoundManager::musicInterface_KillAll() { + debugC(ERROR_INTERMEDIATE, kLureDebugSounds, "musicInterface_KillAll"); + musicInterface_TidySounds(); + + ManagedList::iterator i; + for (i = _playingSounds.begin(); i != _playingSounds.end(); ++i) { + MidiMusic *music = *i; + music->stopMusic(); + } + _playingSounds.clear(); + _activeSounds.clear(); } -void SoundManager::musicInterface_ContinuePlaying() { +// musicInterface_ContinuePlaying +// The original player used this method for any sound managers needing continual calls +void SoundManager::musicInterface_ContinuePlaying() { + // No implementation needed } +// musicInterface_TrashReverb +// Trashes reverb on actively playing sounds + void SoundManager::musicInterface_TrashReverb() { + // TODO: Handle support for trashing reverb + debugC(ERROR_INTERMEDIATE, kLureDebugSounds, "musicInterface_TrashReverb"); +} + +// musicInterface_KillAll +// Scans all the active sounds and deallocates any objects that have finished playing + +void SoundManager::musicInterface_TidySounds() { + debugC(ERROR_DETAILED, kLureDebugSounds, "musicInterface_TidySounds"); + ManagedList::iterator i = _playingSounds.begin(); + while (i != _playingSounds.end()) { + MidiMusic *music = *i; + if (!music->isPlaying()) + i = _playingSounds.erase(i); + else + ++i; + } +} + +void SoundManager::onTimer(void *data) { + SoundManager *snd = (SoundManager *) data; + + ManagedList::iterator i; + for (i = snd->_playingSounds.begin(); i != snd->_playingSounds.end(); ++i) { + MidiMusic *music = *i; + if (music->isPlaying()) + music->onTimer(); + } +} + +/*------------------------------------------------------------------------*/ + +MidiMusic::MidiMusic(MidiDriver *driver, ChannelEntry channels[NUM_CHANNELS_INNER], + uint8 channelNum, uint8 soundNum, void *soundData, uint32 size) { + + _driver = driver; + _channels = channels; + _soundNumber = soundNum; + _channelNumber = channelNum; + _numChannels = 8; + _volume = _channels[channelNum].volume; + + _passThrough = false; + + _parser = MidiParser::createParser_SMF(); + _parser->setMidiDriver(this); + _parser->setTimerRate(_driver->getBaseTempo()); + + this->open(); + + _soundData = (uint8 *) soundData; + _soundSize = size; + + // Check whether the music data is compressed - if so, decompress it for the duration + // of playing the sound + + _decompressedSound = NULL; + if ((*_soundData == 'C') || (*_soundData == 'c')) { + uint32 packedSize = size - 0x201; + _decompressedSound = Memory::allocate(packedSize * 2); + + uint16 *data = (uint16 *)(_soundData + 1); + uint16 *dataDest = (uint16 *) _decompressedSound->data(); + byte *idx = ((byte *)data) + 0x200; + + for (uint i = 0; i < packedSize; i++) +#if defined(SCUMM_NEED_ALIGNMENT) + memcpy(dataDest++, (byte*)((byte*)data + *(idx + i) * sizeof(uint16)), sizeof(uint16)); +#else + *dataDest++ = data[*(idx + i)]; +#endif + + _soundData = _decompressedSound->data() + ((*_soundData == 'c') ? 1 : 0); + _soundSize = _decompressedSound->size(); + } + + playMusic(); +} + +MidiMusic::~MidiMusic() { + _parser->unloadMusic(); + delete _parser; + this->close(); + if (_decompressedSound != NULL) + delete _decompressedSound; +} + +void MidiMusic::setVolume(int volume) { + if (volume < 0) + volume = 0; + else if (volume > 255) + volume = 255; + + if (_volume == volume) + return; + + _volume = volume; + + for (int i = 0; i < _numChannels; ++i) + _channels[_channelNumber + i].midiChannel->volume( + _channels[_channelNumber + i].volume * _volume / 255); +} + +void MidiMusic::playMusic() { + debugC(ERROR_DETAILED, kLureDebugSounds, "MidiMusic::PlayMusic playing sound %d", _soundNumber); + _parser->loadMusic(_soundData, _soundSize); + _parser->setTrack(0); + _isPlaying = true; +} + +int MidiMusic::open() { + // Don't ever call open without first setting the output driver! + if (!_driver) + return 255; + + return 0; +} + +void MidiMusic::close() { +} + +void MidiMusic::send(uint32 b) { + if (_passThrough) { + _driver->send(b); + return; + } + + byte channel = _channelNumber + (byte)(b & 0x0F); + if ((channel >= NUM_CHANNELS_INNER) || (_channels[channel].midiChannel == NULL)) + return; + + if ((b & 0xFFF0) == 0x07B0) { + // Adjust volume changes by master volume + byte volume = (byte)((b >> 16) & 0x7F); + _channels[channel].volume = volume; + volume = volume * _volume / 255; + b = (b & 0xFF00FFFF) | (volume << 16); + } else if ((b & 0xF0) == 0xC0 && !_nativeMT32) { + b = (b & 0xFFFF00FF) | MidiDriver::_mt32ToGm[(b >> 8) & 0xFF] << 8; + } + else if ((b & 0xFFF0) == 0x007BB0) { + // No implementation + } + + _channels[channel].midiChannel->send(b); +} + +void MidiMusic::metaEvent(byte type, byte *data, uint16 length) { + //Only thing we care about is End of Track. + if (type != 0x2F) + return; + + stopMusic(); +} + +void MidiMusic::onTimer() { + if (_isPlaying) + _parser->onTimer(); +} +void MidiMusic::stopMusic() { + debugC(ERROR_DETAILED, kLureDebugSounds, "MidiMusic::stopMusic sound %d", _soundNumber); + _isPlaying = false; + _parser->unloadMusic(); + close(); } } // end of namespace Lure diff --git a/engines/lure/sound.h b/engines/lure/sound.h index 95cb5f6f88..cfe0739298 100644 --- a/engines/lure/sound.h +++ b/engines/lure/sound.h @@ -26,37 +26,123 @@ #include "lure/luredefs.h" #include "lure/disk.h" #include "lure/memory.h" + #include "common/singleton.h" +#include "sound/mididrv.h" +#include "sound/mixer.h" + +class MidiParser; namespace Lure { -#define NUM_CHANNELS 8 +#define NUM_CHANNELS_OUTER 8 +#define NUM_CHANNELS_INNER 16 + +struct ChannelEntry { + MidiChannel *midiChannel; + byte volume; +}; + +class MidiMusic: public MidiDriver { +private: + uint8 _soundNumber; + uint8 _channelNumber; + uint8 _numChannels; + byte _volume; + MemoryBlock *_decompressedSound; + uint8 *_soundData; + uint8 _soundSize; + MidiDriver *_driver; + MidiParser *_parser; + ChannelEntry *_channels; + bool _isPlaying; + bool _nativeMT32; + + void queueUpdatePos(); + uint8 randomQueuePos(); + uint32 songOffset(uint16 songNum) const; + uint32 songLength(uint16 songNum) const; + + bool _passThrough; + +public: + MidiMusic(MidiDriver *driver, ChannelEntry channels[NUM_CHANNELS_INNER], + uint8 channelNum, uint8 soundNum, void *soundData, uint32 size); + ~MidiMusic(); + void setVolume(int volume); + int getVolume() { return _volume; } + + void hasNativeMT32(bool b) { _nativeMT32 = b; } + void playSong(uint16 songNum); + void stopSong() { stopMusic(); } + void playMusic(); + void stopMusic(); + void queueTuneList(int16 tuneList); + bool queueSong(uint16 songNum); + void setPassThrough(bool b) { _passThrough = b; } + void toggleVChange(); + + //MidiDriver interface implementation + int open(); + void close(); + void send(uint32 b); + void onTimer(); + + void metaEvent(byte type, byte *data, uint16 length); + + void setTimerCallback(void *timerParam, void (*timerProc)(void *)) { } + uint32 getBaseTempo(void) { return _driver ? _driver->getBaseTempo() : 0; } + + //Channel allocation functions + MidiChannel *allocateChannel() { return 0; } + MidiChannel *getPercussionChannel() { return 0; } + + uint8 channelNumber() { return _channelNumber; } + uint8 soundNumber() { return _soundNumber; } + bool isPlaying() { return _isPlaying; } +}; class SoundManager: public Common::Singleton { private: + // Outer sound interface properties MemoryBlock *_descs; + MemoryBlock *_soundData; + uint8 _soundsTotal; int _numDescs; SoundDescResource *soundDescs() { return (SoundDescResource *) _descs->data(); } + MidiDriver *_driver; ManagedList _activeSounds; - byte _channels[NUM_CHANNELS]; + ManagedList _playingSounds; + ChannelEntry _channelsInner[NUM_CHANNELS_INNER]; + bool _channelsInUse[NUM_CHANNELS_OUTER]; + bool _isPlaying; + bool _nativeMT32; + // Internal support methods void bellsBodge(); + void musicInterface_TidySounds(); + static void onTimer(void *data); public: SoundManager(); + ~SoundManager(); + void loadSection(uint16 sectionId); void killSounds(); void addSound(uint8 soundIndex, bool tidyFlag = true); void addSound2(uint8 soundIndex); void stopSound(uint8 soundIndex); void killSound(uint8 soundNumber); void setVolume(uint8 soundNumber, uint8 volume); + void setVolume(uint8 volume); void tidySounds(); SoundDescResource *findSound(uint8 soundNumber); void removeSounds(); void restoreSounds(); + void fadeOut(); // The following methods implement the external sound player module - void musicInterface_Play(uint8 soundNumber, bool isEffect, uint8 channelNumber); + void musicInterface_Initialise(); + void musicInterface_Play(uint8 soundNumber, uint8 channelNumber); void musicInterface_Stop(uint8 soundNumber); bool musicInterface_CheckPlaying(uint8 soundNumber); void musicInterface_SetVolume(uint8 channelNum, uint8 volume); diff --git a/engines/lure/surface.cpp b/engines/lure/surface.cpp index 005b1b08c4..d5b9db4955 100644 --- a/engines/lure/surface.cpp +++ b/engines/lure/surface.cpp @@ -802,7 +802,7 @@ bool RestartRestoreDialog::show() { LureEngine &engine = LureEngine::getReference(); Sound.killSounds(); - Sound.musicInterface_Play(60, true, 0); + Sound.musicInterface_Play(60, 0); mouse.setCursorNum(CURSOR_ARROW); // See if there are any savegames that can be restored -- cgit v1.2.3