diff options
Diffstat (limited to 'engines/agos/midi.cpp')
-rw-r--r-- | engines/agos/midi.cpp | 325 |
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, |