Initial sound implementation

svn-id: r28917
This commit is contained in:
Paul Gilbert 2007-09-16 04:06:49 +00:00
parent 041bb546c6
commit b2e97060ad
9 changed files with 532 additions and 61 deletions

View File

@ -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) {

View File

@ -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;
}

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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 {

View File

@ -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;
for (int channelNum = 0; channelNum < NUM_CHANNELS; ++channelNum)
_channels[channelNum] = 0;
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;
} 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) {
SoundDescResource *entry = findSound(soundNumber);
if (entry == NULL) return;
debugC(ERROR_BASIC, kLureDebugSounds, "SoundManager::setVolume soundNumber=%d, volume=%d",
soundNumber, volume);
musicInterface_TidySounds();
// Special check is done for Adlib in original game, to ignore any volume changes
SoundDescResource *entry = findSound(soundNumber);
if (entry)
musicInterface_SetVolume(entry->channel, volume);
}
void SoundManager::setVolume(uint8 volume) {
debugC(ERROR_BASIC, kLureDebugSounds, "SoundManager::setVolume volume=%d", volume);
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();
}
// musicInterface_ContinuePlaying
// The original player used this method for any sound managers needing continual calls
void SoundManager::musicInterface_ContinuePlaying() {
// No implementation needed
}
void SoundManager::musicInterface_TrashReverb() {
// 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

View File

@ -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);

View File

@ -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