aboutsummaryrefslogtreecommitdiff
path: root/engines/agos/midi.cpp
diff options
context:
space:
mode:
authorMartin Kiewitz2015-06-21 00:45:45 +0200
committerMartin Kiewitz2015-06-21 00:45:45 +0200
commitd24c68c739dadd1404d937f9a21d93e8841a09ee (patch)
treebe811b96fb4e31b732709d0741cde6be120c1688 /engines/agos/midi.cpp
parent4a88c69b5dedc4acac8bcb9609c1cf44c497c4ff (diff)
downloadscummvm-rg350-d24c68c739dadd1404d937f9a21d93e8841a09ee.tar.gz
scummvm-rg350-d24c68c739dadd1404d937f9a21d93e8841a09ee.tar.bz2
scummvm-rg350-d24c68c739dadd1404d937f9a21d93e8841a09ee.zip
AGOS: implement Accolade AdLib + MT32 music drivers
- both known variants are supported (INSTR.DAT + MUSIC.DRV) - INSTR.DAT/MUSIC.DRV holds channel mapping, instrument mapping, etc. - fixed bug inside S1D MidiParser, that ruined some instrument changes 0xFC header was seen as 2 byte header, but it's 4 bytes in Elvira 2 and 5 bytes in Waxworks / Simon 1 demo - dynamic channel allocation for the MUSIC.DRV adlib driver is not implemented atm, simply because at least the demos of Waxworks and Simon 1 do not use this feature - sound effects of Waxworks are not implemented atm - note: the game "Altered Destiny" uses Accolade INSTR.DAT variant too
Diffstat (limited to 'engines/agos/midi.cpp')
-rw-r--r--engines/agos/midi.cpp249
1 files changed, 247 insertions, 2 deletions
diff --git a/engines/agos/midi.cpp b/engines/agos/midi.cpp
index e5875a8353..d75b7b8cb7 100644
--- a/engines/agos/midi.cpp
+++ b/engines/agos/midi.cpp
@@ -27,6 +27,8 @@
#include "agos/agos.h"
#include "agos/midi.h"
+#include "agos/drivers/accolade/mididriver.h"
+
namespace AGOS {
@@ -57,6 +59,8 @@ MidiPlayer::MidiPlayer() {
_loopTrackDefault = false;
_queuedTrack = 255;
_loopQueuedTrack = 0;
+
+ _accolade_mode = false;
}
MidiPlayer::~MidiPlayer() {
@@ -73,11 +77,244 @@ MidiPlayer::~MidiPlayer() {
unloadAdlibPatches();
}
-int MidiPlayer::open(int gameType) {
+int MidiPlayer::open(int gameType, bool isDemo) {
// Don't call open() twice!
assert(!_driver);
- // Setup midi driver
+ bool accolade_useMusicDrvFile = false;
+ MusicType accolade_musicType = MT_INVALID;
+
+ switch (gameType) {
+ case GType_ELVIRA1:
+ case GType_ELVIRA2:
+ _accolade_mode = true;
+ break;
+ case GType_WW:
+ _accolade_mode = true;
+ accolade_useMusicDrvFile = true;
+ break;
+ case GType_SIMON1:
+ if (isDemo) {
+ _accolade_mode = true;
+ accolade_useMusicDrvFile = true;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (_accolade_mode) {
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MT32);
+ accolade_musicType = MidiDriver::getMusicType(dev);
+
+ switch (accolade_musicType) {
+ case MT_ADLIB:
+ case MT_MT32:
+ break;
+ case MT_GM:
+ if (ConfMan.getBool("native_mt32")) {
+ // Real MT32
+ accolade_musicType = MT_MT32;
+ } else {
+ _accolade_mode = false;
+ }
+ break;
+ default:
+ _accolade_mode = false;
+ break;
+ }
+ }
+
+ if (_accolade_mode) {
+ // Setup midi driver
+ switch (accolade_musicType) {
+ case MT_ADLIB:
+ _driver = MidiDriver_Accolade_AdLib_create();
+ break;
+ case MT_MT32:
+ _driver = MidiDriver_Accolade_MT32_create();
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ if (!_driver)
+ return 255;
+
+ byte *instrumentData = NULL;
+ uint16 instrumentDataSize = 0;
+
+ if (!accolade_useMusicDrvFile) {
+ // Elvira 1 / Elvira 2: read INSTR.DAT
+ Common::File *instrDatStream = new Common::File();
+
+ if (!instrDatStream->open("INSTR.DAT")) {
+ error("INSTR.DAT: unable to open file");
+ }
+
+ uint32 streamSize = instrDatStream->size();
+ uint32 streamLeft = streamSize;
+ uint16 skipChunks = 0; // 1 for MT32, 0 for AdLib
+ uint16 chunkSize = 0;
+
+ switch (accolade_musicType) {
+ case MT_ADLIB:
+ skipChunks = 0;
+ break;
+ case MT_MT32:
+ skipChunks = 1; // Skip one entry for MT32
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
+ do {
+ if (streamLeft < 2)
+ error("INSTR.DAT: unexpected EOF");
+
+ chunkSize = instrDatStream->readUint16LE();
+ streamLeft -= 2;
+
+ if (streamLeft < chunkSize)
+ error("INSTR.DAT: unexpected EOF");
+
+ if (skipChunks) {
+ // Skip the chunk
+ instrDatStream->skip(chunkSize);
+ streamLeft -= chunkSize;
+
+ skipChunks--;
+ }
+ } while (skipChunks);
+
+ // Seek over the ASCII string until there is a NUL terminator
+ byte curByte = 0;
+
+ do {
+ if (chunkSize == 0)
+ error("INSTR.DAT: no actual instrument data found");
+
+ curByte = instrDatStream->readByte();
+ chunkSize--;
+ } while (curByte);
+
+ instrumentDataSize = chunkSize;
+
+ // Read the requested instrument data entry
+ instrumentData = new byte[instrumentDataSize];
+ instrDatStream->read(instrumentData, instrumentDataSize);
+
+ instrDatStream->close();
+ delete instrDatStream;
+
+ } else {
+ // Waxworks / Simon 1 demo: Read MUSIC.DRV
+ Common::File *musicDrvStream = new Common::File();
+
+ if (!musicDrvStream->open("MUSIC.DRV")) {
+ error("MUSIC.DRV: unable to open file");
+ }
+
+ uint32 streamSize = musicDrvStream->size();
+ uint32 streamLeft = streamSize;
+ uint16 getChunk = 0; // 4 for MT32, 2 for AdLib
+
+ switch (accolade_musicType) {
+ case MT_ADLIB:
+ getChunk = 2;
+ break;
+ case MT_MT32:
+ getChunk = 4;
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
+ if (streamLeft < 2)
+ error("MUSIC.DRV: unexpected EOF");
+
+ uint16 chunkCount = musicDrvStream->readUint16LE();
+ streamLeft -= 2;
+
+ if (getChunk >= chunkCount)
+ error("MUSIC.DRV: required chunk not available");
+
+ uint16 headerOffset = 2 + (28 * getChunk);
+ streamLeft -= (28 * getChunk);
+
+ if (streamLeft < 28)
+ error("MUSIC.DRV: unexpected EOF");
+
+ // Seek to required chunk
+ musicDrvStream->seek(headerOffset);
+ musicDrvStream->skip(20); // skip over name
+ streamLeft -= 20;
+
+ uint16 musicDrvSignature = musicDrvStream->readUint16LE();
+ uint16 musicDrvType = musicDrvStream->readUint16LE();
+ uint16 chunkOffset = musicDrvStream->readUint16LE();
+ uint16 chunkSize = musicDrvStream->readUint16LE();
+
+ // Security checks
+ if (musicDrvSignature != 0xFEDC)
+ error("MUSIC.DRV: chunk signature mismatch");
+ if (musicDrvType != 1)
+ error("MUSIC.DRV: not a music driver");
+ if (chunkOffset >= streamSize)
+ error("MUSIC.DRV: driver chunk points outside of file");
+
+ streamLeft = streamSize - chunkOffset;
+ if (streamLeft < chunkSize)
+ error("MUSIC.DRV: driver chunk is larger than file");
+
+ instrumentDataSize = chunkSize;
+
+ // Read the requested instrument data entry
+ instrumentData = new byte[instrumentDataSize];
+
+ musicDrvStream->seek(chunkOffset);
+ musicDrvStream->read(instrumentData, instrumentDataSize);
+
+ musicDrvStream->close();
+ delete musicDrvStream;
+ }
+
+ // Pass the instrument data to the driver
+ bool instrumentSuccess = false;
+
+ switch (accolade_musicType) {
+ case MT_ADLIB:
+ instrumentSuccess = MidiDriver_Accolade_AdLib_setupInstruments(_driver, instrumentData, instrumentDataSize, accolade_useMusicDrvFile);
+ break;
+ case MT_MT32:
+ instrumentSuccess = MidiDriver_Accolade_MT32_setupInstruments(_driver, instrumentData, instrumentDataSize, accolade_useMusicDrvFile);
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ delete[] instrumentData;
+
+ if (!instrumentSuccess) {
+ // driver did not like the contents
+ if (!accolade_useMusicDrvFile)
+ error("INSTR.DAT: contents not acceptable");
+ else
+ error("MUSIC.DRV: contents not acceptable");
+ }
+
+ int ret = _driver->open();
+ if (ret == 0) {
+ // Reset is done inside our MIDI driver
+ _driver->setTimerCallback(this, &onTimer);
+ }
+
+ //setTimerRate(_driver->getBaseTempo());
+ return 0;
+ }
+
MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_ADLIB | MDT_MIDI | (gameType == GType_SIMON1 ? MDT_PREFER_MT32 : MDT_PREFER_GM));
_nativeMT32 = ((MidiDriver::getMusicType(dev) == MT_MT32) || ConfMan.getBool("native_mt32"));
@@ -113,6 +350,12 @@ void MidiPlayer::send(uint32 b) {
if (!_current)
return;
+ if (_accolade_mode) {
+ // Send directly to Accolade driver
+ _driver->send(b);
+ return;
+ }
+
byte channel = (byte)(b & 0x0F);
if ((b & 0xFFF0) == 0x07B0) {
// Adjust volume changes by master music and master sfx volume.
@@ -146,8 +389,10 @@ void MidiPlayer::send(uint32 b) {
_current->volume[channel] = 127;
}
+ // Allocate channels if needed
if (!_current->channel[channel])
_current->channel[channel] = (channel == 9) ? _driver->getPercussionChannel() : _driver->allocateChannel();
+
if (_current->channel[channel]) {
if (channel == 9) {
if (_current == &_sfx)