From 0492bd6aa65fa931bb84bb994f9e9e9757acc90a Mon Sep 17 00:00:00 2001 From: Filippos Karapetis Date: Sat, 12 Jun 2010 18:20:22 +0000 Subject: SAGA: Music code cleanup. The music driver is now placed inside the MusicPlayer class, instead of the SagaEngine class. Split the functionality that the music parser should handle from the functionality that the music driver should handle. Also, fixed a bug in MusicDriver::send() (the notes off signal was sent to unallocated channels, not allocated ones) svn-id: r49607 --- engines/saga/music.cpp | 272 +++++++++++++++++++------------------------------ engines/saga/music.h | 52 ++++------ engines/saga/saga.cpp | 16 +-- engines/saga/saga.h | 1 - 4 files changed, 123 insertions(+), 218 deletions(-) (limited to 'engines/saga') 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; -- cgit v1.2.3