diff options
-rw-r--r-- | queen/music.cpp | 178 | ||||
-rw-r--r-- | queen/music.h | 66 |
2 files changed, 193 insertions, 51 deletions
diff --git a/queen/music.cpp b/queen/music.cpp index 0ac9ef14ef..82a108cb24 100644 --- a/queen/music.cpp +++ b/queen/music.cpp @@ -24,70 +24,168 @@ #include "queen/queen.h" #include "queen/resource.h" -#include "sound/mididrv.h" #include "sound/midiparser.h" namespace Queen { - Music::Music(MidiDriver *driver, QueenEngine *vm) : _isPlaying(false), _loop(false), _driver(driver) { - _midi = MidiParser::createParser_SMF(); - _midi->setMidiDriver(_driver); - int ret = _driver->open(); - if (ret) - warning("MIDI Player init failed: \"%s\"", _driver->getErrorName(ret)); - _midi->setTimerRate(_driver->getBaseTempo()); - _driver->setTimerCallback(this, myTimerProc); + MusicPlayer::MusicPlayer(MidiDriver *driver, byte *data, uint32 size) : _driver(driver), _isPlaying(false), _looping(false), _volume(255), _queuePos(0), _musicData(data), _musicDataSize(size) { + queueClear(); + _parser = MidiParser::createParser_SMF(); + _parser->setMidiDriver(this); + _parser->setTimerRate(_driver->getBaseTempo()); - if (vm->resource()->isDemo()) { - _musicData = vm->resource()->loadFile("AQ8.RL", 0, NULL); - _musicDataSize = vm->resource()->fileSize("AQ8.RL"); - } else { - _musicData = vm->resource()->loadFile("AQ.RL", 0, NULL); - _musicDataSize = vm->resource()->fileSize("AQ.RL"); - } _numSongs = READ_LE_UINT16(_musicData); + this->open(); } - - Music::~Music() { + + MusicPlayer::~MusicPlayer() { _driver->setTimerCallback(NULL, NULL); - _midi->unloadMusic(); - _driver->close(); - delete _midi; - delete[] _musicData; + _parser->unloadMusic(); + this->close(); + delete _parser; } - - void Music::playSong(uint16 songNum) { - if (_loop) - _midi->property(MidiParser::mpAutoLoop, 1); + + bool MusicPlayer::queueSong(uint16 songNum) { + uint8 emptySlots = 0; + for (int i = 0; i < MUSIC_QUEUE_SIZE; i++) + if (!_songQueue[i]) + emptySlots++; + + if (!emptySlots) + return false; + + _songQueue[MUSIC_QUEUE_SIZE - emptySlots] = songNum; + return true; + } + + void MusicPlayer::queueClear() { + _queuePos = 0; + memset(_songQueue, 0, sizeof(_songQueue)); + } + + 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) { + byte channel = (byte)(b & 0x0F); + if ((b & 0xFFF0) == 0x07B0) { + // Adjust volume changes by master volume + byte volume = (byte)((b >> 16) & 0x7F); + _channelVolume[channel] = volume; + //volume = volume * _masterVolume / 255; + b = (b & 0xFF00FFFF) | (volume << 16); + } + else if ((b & 0xFFF0) == 0x007BB0) { + //Only respond to All Notes Off if this channel + //has currently been allocated + if (_channel[b & 0x0F]) + return; + } + + + if (!_channel[channel]) + _channel[channel] = (channel == 9) ? _driver->getPercussionChannel() : _driver->allocateChannel(); + + if (_channel[channel]) + _channel[channel]->send(b); + } + + void MusicPlayer::metaEvent(byte type, byte *data, uint16 length) { + //Only thing we care about is End of Track. + if (type != 0x2F) + return; + + if (_looping || _songQueue[1]) + playMusic(); else - _midi->property(MidiParser::mpAutoLoop, 0); + stopMusic(); + } + + void MusicPlayer::onTimer(void *refCon) { + MusicPlayer *music = (MusicPlayer *)refCon; + if (music->_isPlaying) + music->_parser->onTimer(); + } + + void MusicPlayer::playMusic() { + if (!_queuePos && !_songQueue[_queuePos]) { + debug(5, "MusicPlayer::playMusic - Music queue is empty!"); + return; + } + uint16 songNum = _songQueue[_queuePos]; + _parser->loadMusic(_musicData + songOffset(songNum), songLength(songNum)); + _parser->setTrack(0); + //debug(0, "Playing song %d [queue position: %d]", songNum, _queuePos); _isPlaying = true; - _midi->loadMusic(_musicData + songOffset(songNum), songLength(songNum)); - _midi->setTrack(0); + queueUpdatePos(); } - - void Music::stopSong() { + + void MusicPlayer::queueUpdatePos() { + if (_queuePos < (MUSIC_QUEUE_SIZE - 1) && _songQueue[_queuePos + 1]) + _queuePos++; + else + _queuePos = 0; + } + + void MusicPlayer::stopMusic() { _isPlaying = false; - _midi->unloadMusic(); + _parser->unloadMusic(); } - void Music::myTimerProc(void *refCon) { - Music *music = (Music *)refCon; - if (music->_isPlaying) - music->_midi->onTimer(); - } - - uint32 Music::songOffset(uint16 songNum) { + uint32 MusicPlayer::songOffset(uint16 songNum) { uint16 offsLo = READ_LE_UINT16(_musicData + (songNum * 4) + 2); uint16 offsHi = READ_LE_UINT16(_musicData + (songNum * 4) + 4); return (offsHi << 4) | offsLo; } - uint32 Music::songLength(uint16 songNum) { + uint32 MusicPlayer::songLength(uint16 songNum) { if (songNum < _numSongs) return (songOffset(songNum + 1) - songOffset(songNum)); return (_musicDataSize - songOffset(songNum)); } + + Music::Music(MidiDriver *driver, QueenEngine *vm) { + if (vm->resource()->isDemo()) { + _musicData = vm->resource()->loadFile("AQ8.RL", 0, NULL); + _musicDataSize = vm->resource()->fileSize("AQ8.RL"); + } else { + _musicData = vm->resource()->loadFile("AQ.RL", 0, NULL); + _musicDataSize = vm->resource()->fileSize("AQ.RL"); + } + + _player = new MusicPlayer(driver, _musicData, _musicDataSize); + } + + Music::~Music() { + delete _player; + delete[] _musicData; + } + + void Music::playSong(uint16 songNum) { + _player->queueClear(); + _player->queueSong(songNum); + _player->playMusic(); + } + + void Music::stopSong() { + return _player->stopMusic(); + } } // End of namespace Queen diff --git a/queen/music.h b/queen/music.h index c18b708156..b8d3851ce9 100644 --- a/queen/music.h +++ b/queen/music.h @@ -23,13 +23,65 @@ #define QUEENMUSIC_H #include "common/util.h" +#include "sound/mididrv.h" -class MidiDriver; class MidiParser; namespace Queen { class QueenEngine; + +class MusicPlayer : public MidiDriver { +public: + MusicPlayer(MidiDriver *driver, byte *data, uint32 size); + ~MusicPlayer(); + + void playMusic(); + void stopMusic(); + void setLoop(bool loop) { _looping = loop; } + bool queueSong(uint16 songNum); + void queueClear(); + + //MidiDriver interface implementation + int open(); + void close(); + void send(uint32 b); + + 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; } + +protected: + + enum { + MUSIC_QUEUE_SIZE = 8 + }; + + void queueUpdatePos(); + static void onTimer(void *data); + uint32 songOffset(uint16 songNum); + uint32 songLength(uint16 songNum); + + MidiDriver *_driver; + MidiParser *_parser; + MidiChannel *_channel[16]; + byte _channelVolume[16]; + + bool _isPlaying; + bool _looping; + byte _volume; + uint8 _queuePos; + int16 _songQueue[MUSIC_QUEUE_SIZE]; + + uint16 _numSongs; + byte *_musicData; + uint32 _musicDataSize; +}; class Music { public: @@ -37,20 +89,12 @@ public: ~Music(); void playSong(uint16 songNum); void stopSong(); - void loop(bool val) { _loop = val; } + void loop(bool val) { return _player->setLoop(val); } protected: - bool _isPlaying; - bool _loop; byte *_musicData; - uint16 _numSongs; uint32 _musicDataSize; - MidiDriver *_driver; - MidiParser *_midi; - - static void myTimerProc(void *refCon); - uint32 songOffset(uint16 songNum); - uint32 songLength(uint16 songNum); + MusicPlayer *_player; }; } // End of namespace Queen |