diff options
author | Martin Kiewitz | 2015-06-21 00:45:45 +0200 |
---|---|---|
committer | Martin Kiewitz | 2015-06-21 00:45:45 +0200 |
commit | d24c68c739dadd1404d937f9a21d93e8841a09ee (patch) | |
tree | be811b96fb4e31b732709d0741cde6be120c1688 /engines/agos/midi.cpp | |
parent | 4a88c69b5dedc4acac8bcb9609c1cf44c497c4ff (diff) | |
download | scummvm-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.cpp | 249 |
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) |