aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--queen/music.cpp178
-rw-r--r--queen/music.h66
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