aboutsummaryrefslogtreecommitdiff
path: root/engines/agos/midi.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/agos/midi.cpp')
-rw-r--r--engines/agos/midi.cpp325
1 files changed, 320 insertions, 5 deletions
diff --git a/engines/agos/midi.cpp b/engines/agos/midi.cpp
index 045fd9dac5..604824777e 100644
--- a/engines/agos/midi.cpp
+++ b/engines/agos/midi.cpp
@@ -27,6 +27,10 @@
#include "agos/agos.h"
#include "agos/midi.h"
+#include "agos/drivers/accolade/mididriver.h"
+
+#include "gui/message.h"
+
namespace AGOS {
@@ -42,6 +46,8 @@ MidiPlayer::MidiPlayer() {
_driver = 0;
_map_mt32_to_gm = false;
+ _adlibPatches = NULL;
+
_enable_sfx = true;
_current = 0;
@@ -55,6 +61,8 @@ MidiPlayer::MidiPlayer() {
_loopTrackDefault = false;
_queuedTrack = 255;
_loopQueuedTrack = 0;
+
+ _accoladeMode = false;
}
MidiPlayer::~MidiPlayer() {
@@ -68,13 +76,254 @@ MidiPlayer::~MidiPlayer() {
}
_driver = NULL;
clearConstructs();
+ unloadAdlibPatches();
}
-int MidiPlayer::open(int gameType) {
+int MidiPlayer::open(int gameType, bool isDemo) {
// Don't call open() twice!
assert(!_driver);
- // Setup midi driver
+ bool accoladeUseMusicDrvFile = false;
+ MusicType accoladeMusicType = MT_INVALID;
+
+ switch (gameType) {
+ case GType_ELVIRA1:
+ _accoladeMode = true;
+ break;
+ case GType_ELVIRA2:
+ case GType_WW:
+ // Attention: Elvira 2 shipped with INSTR.DAT and MUSIC.DRV
+ // MUSIC.DRV is the correct one. INSTR.DAT seems to be a left-over
+ _accoladeMode = true;
+ accoladeUseMusicDrvFile = true;
+ break;
+ case GType_SIMON1:
+ if (isDemo) {
+ _accoladeMode = true;
+ accoladeUseMusicDrvFile = true;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (_accoladeMode) {
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MT32);
+ accoladeMusicType = MidiDriver::getMusicType(dev);
+
+ switch (accoladeMusicType) {
+ case MT_ADLIB:
+ case MT_MT32:
+ break;
+ case MT_GM:
+ if (!ConfMan.getBool("native_mt32")) {
+ // Not a real MT32 / no MUNT
+ ::GUI::MessageDialog dialog(("You appear to be using a General MIDI device,\n"
+ "but your game only supports Roland MT32 MIDI.\n"
+ "We try to map the Roland MT32 instruments to\n"
+ "General MIDI ones. It is still possible that\n"
+ "some tracks sound incorrect."));
+ dialog.runModal();
+ }
+ // Switch to MT32 driver in any case
+ accoladeMusicType = MT_MT32;
+ break;
+ default:
+ _accoladeMode = false;
+ break;
+ }
+ }
+
+ if (_accoladeMode) {
+ // Setup midi driver
+ switch (accoladeMusicType) {
+ 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 (!accoladeUseMusicDrvFile) {
+ // 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 (accoladeMusicType) {
+ 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 (accoladeMusicType) {
+ 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 (accoladeMusicType) {
+ case MT_ADLIB:
+ instrumentSuccess = MidiDriver_Accolade_AdLib_setupInstruments(_driver, instrumentData, instrumentDataSize, accoladeUseMusicDrvFile);
+ break;
+ case MT_MT32:
+ instrumentSuccess = MidiDriver_Accolade_MT32_setupInstruments(_driver, instrumentData, instrumentDataSize, accoladeUseMusicDrvFile);
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ delete[] instrumentData;
+
+ if (!instrumentSuccess) {
+ // driver did not like the contents
+ if (!accoladeUseMusicDrvFile)
+ 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"));
@@ -85,6 +334,12 @@ int MidiPlayer::open(int gameType) {
if (_nativeMT32)
_driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
+ /* Disabled due to not sounding right, and low volume level
+ if (gameType == GType_SIMON1 && MidiDriver::getMusicType(dev) == MT_ADLIB) {
+ loadAdlibPatches();
+ }
+ */
+
_map_mt32_to_gm = (gameType != GType_SIMON2 && !_nativeMT32);
int ret = _driver->open();
@@ -104,6 +359,12 @@ void MidiPlayer::send(uint32 b) {
if (!_current)
return;
+ if (_accoladeMode) {
+ // 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.
@@ -114,8 +375,10 @@ void MidiPlayer::send(uint32 b) {
else if (_current == &_music)
volume = volume * _musicVolume / 255;
b = (b & 0xFF00FFFF) | (volume << 16);
- } else if ((b & 0xF0) == 0xC0 && _map_mt32_to_gm) {
- b = (b & 0xFFFF00FF) | (MidiDriver::_mt32ToGm[(b >> 8) & 0xFF] << 8);
+ } else if ((b & 0xF0) == 0xC0) {
+ if (_map_mt32_to_gm && !_adlibPatches) {
+ b = (b & 0xFFFF00FF) | (MidiDriver::_mt32ToGm[(b >> 8) & 0xFF] << 8);
+ }
} else if ((b & 0xFFF0) == 0x007BB0) {
// Only respond to an All Notes Off if this channel
// has already been allocated.
@@ -135,8 +398,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)
@@ -144,7 +409,16 @@ void MidiPlayer::send(uint32 b) {
else if (_current == &_music)
_current->channel[9]->volume(_current->volume[9] * _musicVolume / 255);
}
- _current->channel[channel]->send(b);
+
+ if ((b & 0xF0) == 0xC0 && _adlibPatches) {
+ // NOTE: In the percussion channel, this function is a
+ // no-op. Any percussion instruments you hear may
+ // be the stock ones from adlib.cpp.
+ _driver->sysEx_customInstrument(_current->channel[channel]->getNumber(), 'ADL ', _adlibPatches + 30 * ((b >> 8) & 0xFF));
+ } else {
+ _current->channel[channel]->send(b);
+ }
+
if ((b & 0xFFF0) == 0x79B0) {
// We have received a "Reset All Controllers" message
// and passed it on to the MIDI driver. This may or may
@@ -355,6 +629,47 @@ void MidiPlayer::resetVolumeTable() {
}
}
+void MidiPlayer::loadAdlibPatches() {
+ Common::File ibk;
+
+ if (!ibk.open("mt_fm.ibk"))
+ return;
+
+ if (ibk.readUint32BE() == 0x49424b1a) {
+ _adlibPatches = new byte[128 * 30];
+ byte *ptr = _adlibPatches;
+
+ memset(_adlibPatches, 0, 128 * 30);
+
+ for (int i = 0; i < 128; i++) {
+ byte instr[16];
+
+ ibk.read(instr, 16);
+
+ ptr[0] = instr[0]; // Modulator Sound Characteristics
+ ptr[1] = instr[2]; // Modulator Scaling/Output Level
+ ptr[2] = ~instr[4]; // Modulator Attack/Decay
+ ptr[3] = ~instr[6]; // Modulator Sustain/Release
+ ptr[4] = instr[8]; // Modulator Wave Select
+ ptr[5] = instr[1]; // Carrier Sound Characteristics
+ ptr[6] = instr[3]; // Carrier Scaling/Output Level
+ ptr[7] = ~instr[5]; // Carrier Attack/Delay
+ ptr[8] = ~instr[7]; // Carrier Sustain/Release
+ ptr[9] = instr[9]; // Carrier Wave Select
+ ptr[10] = instr[10]; // Feedback/Connection
+
+ // The remaining six bytes are reserved for future use
+
+ ptr += 30;
+ }
+ }
+}
+
+void MidiPlayer::unloadAdlibPatches() {
+ delete[] _adlibPatches;
+ _adlibPatches = NULL;
+}
+
static const int simon1_gmf_size[] = {
8900, 12166, 2848, 3442, 4034, 4508, 7064, 9730, 6014, 4742, 3138,
6570, 5384, 8909, 6457, 16321, 2742, 8968, 4804, 8442, 7717,