aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
authorPaul Gilbert2007-09-16 04:06:49 +0000
committerPaul Gilbert2007-09-16 04:06:49 +0000
commitb2e97060adf93760f7e4bce97e691e34fc258922 (patch)
treec646b990c339d55578600fcf33b608802d185366 /engines
parent041bb546c652d301bc68630f309b623d89471bab (diff)
downloadscummvm-rg350-b2e97060adf93760f7e4bce97e691e34fc258922.tar.gz
scummvm-rg350-b2e97060adf93760f7e4bce97e691e34fc258922.tar.bz2
scummvm-rg350-b2e97060adf93760f7e4bce97e691e34fc258922.zip
Initial sound implementation
svn-id: r28917
Diffstat (limited to 'engines')
-rw-r--r--engines/lure/game.cpp4
-rw-r--r--engines/lure/intro.cpp58
-rw-r--r--engines/lure/lure.cpp9
-rw-r--r--engines/lure/luredefs.h6
-rw-r--r--engines/lure/scripts.cpp9
-rw-r--r--engines/lure/scripts.h2
-rw-r--r--engines/lure/sound.cpp407
-rw-r--r--engines/lure/sound.h92
-rw-r--r--engines/lure/surface.cpp2
9 files changed, 530 insertions, 59 deletions
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<SoundDescResource *>::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<SoundDescResource *>::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<SoundDescResource *>::iterator i = _activeSounds.begin();
@@ -206,7 +296,7 @@ void SoundManager::removeSounds() {
}
void SoundManager::restoreSounds() {
-
+ debugC(ERROR_BASIC, kLureDebugSounds, "SoundManager::restoreSounds");
ManagedList<SoundDescResource *>::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<MidiMusic *>::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<MidiMusic *>::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<MidiMusic *>::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<MidiMusic *>::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<MidiMusic *>::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<MidiMusic *>::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<MidiMusic *>::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<SoundManager> {
private:
+ // Outer sound interface properties
MemoryBlock *_descs;
+ MemoryBlock *_soundData;
+ uint8 _soundsTotal;
int _numDescs;
SoundDescResource *soundDescs() { return (SoundDescResource *) _descs->data(); }
+ MidiDriver *_driver;
ManagedList<SoundDescResource *> _activeSounds;
- byte _channels[NUM_CHANNELS];
+ ManagedList<MidiMusic *> _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