diff options
Diffstat (limited to 'engines/saga')
-rw-r--r-- | engines/saga/music.cpp | 272 | ||||
-rw-r--r-- | engines/saga/music.h | 52 | ||||
-rw-r--r-- | engines/saga/saga.cpp | 16 | ||||
-rw-r--r-- | engines/saga/saga.h | 1 |
4 files changed, 123 insertions, 218 deletions
diff --git a/engines/saga/music.cpp b/engines/saga/music.cpp index 75c5cdffd7..f38e47dc48 100644 --- a/engines/saga/music.cpp +++ b/engines/saga/music.cpp @@ -42,19 +42,25 @@ namespace Saga { #define BUFFER_SIZE 4096 #define MUSIC_SUNSPOT 26 -MusicPlayer::MusicPlayer(MidiDriver *driver) : _parser(0), _driver(driver), _looping(false), _isPlaying(false), _passThrough(false), _isGM(false) { +MusicDriver::MusicDriver() : _isGM(false) { memset(_channel, 0, sizeof(_channel)); _masterVolume = 0; + _nativeMT32 = ConfMan.getBool("native_mt32"); + + _driverType = MidiDriver::detectMusicDriver(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MIDI); + _driver = MidiDriver::createMidi(_driverType); + if (isMT32()) + _driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE); + this->open(); } -MusicPlayer::~MusicPlayer() { - _driver->setTimerCallback(NULL, NULL); - stopMusic(); +MusicDriver::~MusicDriver() { this->close(); + delete _driver; } -void MusicPlayer::setVolume(int volume) { +void MusicDriver::setVolume(int volume) { volume = CLIP(volume, 0, 255); if (_masterVolume == volume) @@ -71,32 +77,7 @@ void MusicPlayer::setVolume(int volume) { } } -int MusicPlayer::open() { - // Don't ever call open without first setting the output driver! - if (!_driver) - return 255; - - int ret = _driver->open(); - if (ret) - return ret; - - _driver->setTimerCallback(this, &onTimer); - return 0; -} - -void MusicPlayer::close() { - stopMusic(); - if (_driver) - _driver->close(); - _driver = 0; -} - -void MusicPlayer::send(uint32 b) { - if (_passThrough) { - _driver->send(b); - return; - } - +void MusicDriver::send(uint32 b) { byte channel = (byte)(b & 0x0F); if ((b & 0xFFF0) == 0x07B0) { // Adjust volume changes by master volume @@ -104,71 +85,86 @@ void MusicPlayer::send(uint32 b) { _channelVolume[channel] = volume; volume = volume * _masterVolume / 255; b = (b & 0xFF00FFFF) | (volume << 16); - } else if ((b & 0xF0) == 0xC0 && !_isGM && !_nativeMT32) { + } else if ((b & 0xF0) == 0xC0 && !_isGM && !isMT32()) { + // Remap MT32 instruments to General Midi b = (b & 0xFFFF00FF) | MidiDriver::_mt32ToGm[(b >> 8) & 0xFF] << 8; - } - else if ((b & 0xFFF0) == 0x007BB0) { - //Only respond to All Notes Off if this channel - //has currently been allocated - if (_channel[b & 0x0F]) + } else if ((b & 0xFFF0) == 0x007BB0) { + // Only respond to All Notes Off if this channel + // has currently been allocated + if (!_channel[channel]) return; } if (!_channel[channel]) _channel[channel] = (channel == 9) ? _driver->getPercussionChannel() : _driver->allocateChannel(); - - if (_channel[channel]) + else _channel[channel]->send(b); } -void MusicPlayer::metaEvent(byte type, byte *data, uint16 length) { - // FIXME: The "elkfanfare" is played much too quickly. There are some - // meta events that we don't handle. Perhaps there is a - // connection...? - - switch (type) { - case 0x2F: // End of Track - if (_looping) - _parser->jumpToTick(0); - else - stopMusic(); - break; - default: - //warning("Unhandled meta event: %02x", type); - break; - } -} - -void MusicPlayer::onTimer(void *refCon) { - MusicPlayer *music = (MusicPlayer *)refCon; - Common::StackLock lock(music->_mutex); - - if (music->_isPlaying) - music->_parser->onTimer(); -} - -void MusicPlayer::playMusic() { - _isPlaying = true; -} +Music::Music(SagaEngine *vm, Audio::Mixer *mixer) : _vm(vm), _mixer(mixer) { + _currentVolume = 0; + _driver = new MusicDriver(); -void MusicPlayer::stopMusic() { - Common::StackLock lock(_mutex); + _digitalMusicContext = _vm->_resource->getContext(GAME_DIGITALMUSICFILE); + if (!_driver->isAdlib()) + _musicContext = _vm->_resource->getContext(GAME_MUSICFILE_GM); + else + _musicContext = _vm->_resource->getContext(GAME_MUSICFILE_FM); - _isPlaying = false; - if (_parser) { - _parser->unloadMusic(); - _parser = NULL; + if (!_musicContext) { + if (_vm->getGameId() == GID_ITE) { + _musicContext = _vm->_resource->getContext(GAME_RESOURCEFILE); + } else { + // I've listened to music from both the FM and the GM + // file, and I've tentatively reached the conclusion + // that they are both General MIDI. My guess is that + // the FM file has been reorchestrated to sound better + // on AdLib and other FM synths. + // + // Sev says the AdLib music does not sound like in the + // original, but I still think assuming General MIDI is + // the right thing to do. Some music, like the End + // Title (song 0) sound absolutely atrocious when piped + // through our MT-32 to GM mapping. + // + // It is, however, quite possible that the original + // used a different GM to FM mapping. If the original + // sounded markedly better, perhaps we should add some + // way of replacing our stock mapping in adlib.cpp? + // + // For the composer's own recording of the End Title, + // see http://www.johnottman.com/ + + // Oddly enough, the intro music (song 1) is very + // different in the two files. I have no idea why. + // Note that the IHNM demo has only got one music file + // (music.rsc). It is assumed that it contains FM music + _musicContext = _vm->_resource->getContext(GAME_MUSICFILE_FM); + } } -} -Music::Music(SagaEngine *vm, Audio::Mixer *mixer, MidiDriver *driver) : _vm(vm), _mixer(mixer), _adlib(false) { - _player = new MusicPlayer(driver); - _currentVolume = 0; - - xmidiParser = MidiParser::createParser_XMIDI(); - smfParser = MidiParser::createParser_SMF(); + // Check if the game is using XMIDI or SMF music + if (_vm->getGameId() == GID_IHNM && _vm->isMacResources()) { + // Just set an XMIDI parser for Mac IHNM for now + _parser = MidiParser::createParser_XMIDI(); + } else { + byte *resourceData; + size_t resourceSize; + int resourceId = (_vm->getGameId() == GID_ITE ? 9 : 0); + _vm->_resource->loadResource(_musicContext, resourceId, resourceData, resourceSize); + if (!memcmp(resourceData, "FORM", 4)) { + _parser = MidiParser::createParser_XMIDI(); + // ITE had MT32 mapped instruments + _driver->setGM(_vm->getGameId() != GID_ITE); + } else { + _parser = MidiParser::createParser_SMF(); + } + free(resourceData); + } - _digitalMusicContext = _vm->_resource->getContext(GAME_DIGITALMUSICFILE); + _parser->setMidiDriver(_driver); + _parser->setTimerRate(_driver->getBaseTempo()); + _parser->property(MidiParser::mpCenterPitchWheelOnUnload, 1); _songTableLen = 0; _songTable = 0; @@ -180,11 +176,11 @@ Music::Music(SagaEngine *vm, Audio::Mixer *mixer, MidiDriver *driver) : _vm(vm), Music::~Music() { _vm->getTimerManager()->removeTimerProc(&musicVolumeGaugeCallback); _mixer->stopHandle(_musicHandle); - delete _player; - xmidiParser->setMidiDriver(NULL); - smfParser->setMidiDriver(NULL); - delete xmidiParser; - delete smfParser; + _driver->setTimerCallback(NULL, NULL); + _driver->close(); + delete _driver; + _parser->setMidiDriver(NULL); + delete _parser; free(_songTable); free(_midiMusicData); @@ -194,6 +190,12 @@ void Music::musicVolumeGaugeCallback(void *refCon) { ((Music *)refCon)->musicVolumeGauge(); } +void Music::onTimer(void *refCon) { + Music *music = (Music *)refCon; + Common::StackLock lock(music->_driver->_mutex); + music->_parser->onTimer(); +} + void Music::musicVolumeGauge() { int volume; @@ -209,7 +211,7 @@ void Music::musicVolumeGauge() { volume = 1; _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, volume); - _player->setVolume(volume); + _driver->setVolume(volume); if (_currentVolumePercent == 100) { _vm->getTimerManager()->removeTimerProc(&musicVolumeGaugeCallback); @@ -226,7 +228,7 @@ void Music::setVolume(int volume, int time) { if (time == 1) { _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, volume); - _player->setVolume(volume); + _driver->setVolume(volume); _vm->getTimerManager()->removeTimerProc(&musicVolumeGaugeCallback); _currentVolume = volume; return; @@ -236,13 +238,11 @@ void Music::setVolume(int volume, int time) { } bool Music::isPlaying() { - return _mixer->isSoundHandleActive(_musicHandle) || _player->isPlaying(); + return _mixer->isSoundHandleActive(_musicHandle) || _parser->isPlaying(); } void Music::play(uint32 resourceId, MusicFlags flags) { Audio::SeekableAudioStream *audioStream = NULL; - MidiParser *parser; - ResourceContext *context = NULL; byte *resourceData; size_t resourceSize; uint32 loopStart; @@ -254,8 +254,8 @@ void Music::play(uint32 resourceId, MusicFlags flags) { } _trackNumber = resourceId; - _player->stopMusic(); _mixer->stopHandle(_musicHandle); + _parser->unloadMusic(); int realTrackNumber; @@ -356,55 +356,10 @@ void Music::play(uint32 resourceId, MusicFlags flags) { return; } - if (flags == MUSIC_DEFAULT) { + if (flags == MUSIC_DEFAULT) flags = MUSIC_NORMAL; - } // Load MIDI/XMI resource data - - if (_vm->getGameId() == GID_ITE) { - context = _vm->_resource->getContext(GAME_MUSICFILE_GM); - if (context == NULL) { - context = _vm->_resource->getContext(GAME_RESOURCEFILE); - } - } else if (_vm->getGameId() == GID_IHNM && _vm->isMacResources()) { - // The music of the Mac version of IHNM is loaded from its - // associated external file later on - } else { - // I've listened to music from both the FM and the GM - // file, and I've tentatively reached the conclusion - // that they are both General MIDI. My guess is that - // the FM file has been reorchestrated to sound better - // on AdLib and other FM synths. - // - // Sev says the AdLib music does not sound like in the - // original, but I still think assuming General MIDI is - // the right thing to do. Some music, like the End - // Title (song 0) sound absolutely atrocious when piped - // through our MT-32 to GM mapping. - // - // It is, however, quite possible that the original - // used a different GM to FM mapping. If the original - // sounded markedly better, perhaps we should add some - // way of replacing our stock mapping in adlib.cpp? - // - // For the composer's own recording of the End Title, - // see http://www.johnottman.com/ - - // Oddly enough, the intro music (song 1) is very - // different in the two files. I have no idea why. - // Note that the IHNM demo has only got one music file - // (music.rsc). It is assumed that it contains FM music - - if (hasAdLib() || _vm->isIHNMDemo()) { - context = _vm->_resource->getContext(GAME_MUSICFILE_FM); - } else { - context = _vm->_resource->getContext(GAME_MUSICFILE_GM); - } - } - - _player->setGM(true); - if (_vm->getGameId() == GID_IHNM && _vm->isMacResources()) { // Load the external music file for Mac IHNM #if 0 @@ -422,56 +377,39 @@ void Music::play(uint32 resourceId, MusicFlags flags) { #endif return; } else { - _vm->_resource->loadResource(context, resourceId, resourceData, resourceSize); + _vm->_resource->loadResource(_musicContext, resourceId, resourceData, resourceSize); } if (resourceSize < 4) { error("Music::play() wrong music resource size"); } - if (xmidiParser->loadMusic(resourceData, resourceSize)) { - if (_vm->getGameId() == GID_ITE) - _player->setGM(false); + if (!_parser->loadMusic(resourceData, resourceSize)) + error("Music::play() wrong music resource"); - parser = xmidiParser; - } else { - if (smfParser->loadMusic(resourceData, resourceSize)) { - parser = smfParser; - } else { - error("Music::play() wrong music resource"); - } - } - - parser->setTrack(0); - parser->setMidiDriver(_player); - parser->setTimerRate(_player->getBaseTempo()); - parser->property(MidiParser::mpCenterPitchWheelOnUnload, 1); + _parser->setTrack(0); + _driver->setTimerCallback(this, &onTimer); - _player->_parser = parser; setVolume(_vm->_musicVolume); - if (flags & MUSIC_LOOP) - _player->setLoop(true); - else - _player->setLoop(false); + // Handle music looping + _parser->property(MidiParser::mpAutoLoop, (flags & MUSIC_LOOP) ? 1 : 0); - _player->playMusic(); free(_midiMusicData); _midiMusicData = resourceData; } void Music::pause() { - _player->setVolume(-1); - _player->setPlaying(false); + _driver->setTimerCallback(NULL, NULL); } void Music::resume() { - _player->setVolume(_vm->_musicVolume); - _player->setPlaying(true); + _driver->setTimerCallback(this, &onTimer); } void Music::stop() { - _player->stopMusic(); + _driver->setTimerCallback(NULL, NULL); + _parser->unloadMusic(); } } // End of namespace Saga diff --git a/engines/saga/music.h b/engines/saga/music.h index 22711ba167..cb068cd835 100644 --- a/engines/saga/music.h +++ b/engines/saga/music.h @@ -44,41 +44,32 @@ enum MusicFlags { MUSIC_DEFAULT = 0xffff }; -class MusicPlayer : public MidiDriver { +class MusicDriver : public MidiDriver { public: - MusicPlayer(MidiDriver *driver); - ~MusicPlayer(); - - bool isPlaying() { return _isPlaying; } - void setPlaying(bool playing) { _isPlaying = playing; } + MusicDriver(); + ~MusicDriver(); void setVolume(int volume); int getVolume() { return _masterVolume; } - void setNativeMT32(bool b) { _nativeMT32 = b; } - bool hasNativeMT32() { return _nativeMT32; } - void playMusic(); - void stopMusic(); - void setLoop(bool loop) { _looping = loop; } - void setPassThrough(bool b) { _passThrough = b; } - + bool isAdlib() { return _driverType == MD_ADLIB; } + bool isMT32() { return _driverType == MD_MT32 || _nativeMT32; } void setGM(bool isGM) { _isGM = isGM; } //MidiDriver interface implementation - int open(); - void close(); + int open() { return _driver->open(); } + void close() { _driver->close(); } void send(uint32 b); - void metaEvent(byte type, byte *data, uint16 length); + void metaEvent(byte type, byte *data, uint16 length) {} - void setTimerCallback(void *timerParam, void (*timerProc)(void *)) { } - uint32 getBaseTempo() { return _driver ? _driver->getBaseTempo() : 0; } + void setTimerCallback(void *timerParam, void (*timerProc)(void *)) { _driver->setTimerCallback(timerParam, timerProc); } + uint32 getBaseTempo() { return _driver->getBaseTempo(); } //Channel allocation functions MidiChannel *allocateChannel() { return 0; } MidiChannel *getPercussionChannel() { return 0; } - MidiParser *_parser; Common::Mutex _mutex; protected: @@ -87,14 +78,11 @@ protected: MidiChannel *_channel[16]; MidiDriver *_driver; + MidiDriverType _driverType; byte _channelVolume[16]; - bool _nativeMT32; bool _isGM; - bool _passThrough; + bool _nativeMT32; - bool _isPlaying; - bool _looping; - bool _randomLoop; byte _masterVolume; byte *_musicData; @@ -105,13 +93,8 @@ protected: class Music { public: - Music(SagaEngine *vm, Audio::Mixer *mixer, MidiDriver *driver); + Music(SagaEngine *vm, Audio::Mixer *mixer); ~Music(); - void setNativeMT32(bool b) { _player->setNativeMT32(b); } - bool hasNativeMT32() { return _player->hasNativeMT32(); } - void setAdLib(bool b) { _adlib = b; } - bool hasAdLib() { return _adlib; } - void setPassThrough(bool b) { _player->setPassThrough(b); } bool isPlaying(); bool hasDigitalMusic() { return _digitalMusic; } @@ -130,24 +113,23 @@ private: SagaEngine *_vm; Audio::Mixer *_mixer; - MusicPlayer *_player; + MusicDriver *_driver; Audio::SoundHandle _musicHandle; uint32 _trackNumber; - bool _adlib; - int _targetVolume; int _currentVolume; int _currentVolumePercent; bool _digitalMusic; + ResourceContext *_musicContext; ResourceContext *_digitalMusicContext; - MidiParser *xmidiParser; - MidiParser *smfParser; + MidiParser *_parser; byte *_midiMusicData; static void musicVolumeGaugeCallback(void *refCon); + static void onTimer(void *refCon); void musicVolumeGauge(); }; diff --git a/engines/saga/saga.cpp b/engines/saga/saga.cpp index ed8a9055ba..446ab86228 100644 --- a/engines/saga/saga.cpp +++ b/engines/saga/saga.cpp @@ -83,7 +83,6 @@ SagaEngine::SagaEngine(OSystem *syst, const SAGAGameDescription *gameDesc) _sndRes = NULL; _sound = NULL; _music = NULL; - _driver = NULL; _anim = NULL; _render = NULL; _isoMap = NULL; @@ -198,9 +197,6 @@ SagaEngine::~SagaEngine() { delete _sound; _sound = NULL; - delete _driver; - _driver = NULL; - delete _gfx; _gfx = NULL; @@ -285,17 +281,7 @@ Common::Error SagaEngine::run() { _console = new Console(this); // Graphics should be initialized before music - MidiDriverType midiDriver = MidiDriver::detectMusicDriver(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MIDI); - bool native_mt32 = ((midiDriver == MD_MT32) || ConfMan.getBool("native_mt32")); - bool adlib = (midiDriver == MD_ADLIB); - - _driver = MidiDriver::createMidi(midiDriver); - if (native_mt32) - _driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE); - - _music = new Music(this, _mixer, _driver); - _music->setNativeMT32(native_mt32); - _music->setAdLib(adlib); + _music = new Music(this, _mixer); _render = new Render(this, _system); if (!_render->initialized()) { return Common::kUnknownError; diff --git a/engines/saga/saga.h b/engines/saga/saga.h index 2bef489e05..102d1e5c82 100644 --- a/engines/saga/saga.h +++ b/engines/saga/saga.h @@ -525,7 +525,6 @@ public: SndRes *_sndRes; Sound *_sound; Music *_music; - MidiDriver *_driver; Anim *_anim; Render *_render; IsoMap *_isoMap; |