mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-14 05:38:56 +00:00
Initial sound implementation
svn-id: r28917
This commit is contained in:
parent
041bb546c6
commit
b2e97060ad
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user