diff --git a/dists/msvc8/queen.vcproj b/dists/msvc8/queen.vcproj index 8bd9f122378..daba4ea0dfb 100644 --- a/dists/msvc8/queen.vcproj +++ b/dists/msvc8/queen.vcproj @@ -252,6 +252,10 @@ RelativePath="..\..\engines\queen\logic.h" > + + diff --git a/engines/queen/midiadlib.cpp b/engines/queen/midiadlib.cpp new file mode 100644 index 00000000000..af68fc2435f --- /dev/null +++ b/engines/queen/midiadlib.cpp @@ -0,0 +1,628 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "common/endian.h" + +#include "sound/fmopl.h" +#include "sound/softsynth/emumidi.h" + +namespace Queen { + +class AdlibMidiChannel; + +class AdlibMidiDriver : public MidiDriver_Emulated { +public: + + AdlibMidiDriver(Audio::Mixer *mixer) : MidiDriver_Emulated(mixer) {} + ~AdlibMidiDriver() {} + + // MidiDriver + int open(); + void close(); + void send(uint32 b); + void metaEvent(byte type, byte *data, uint16 length); + MidiChannel *allocateChannel() { return 0; } + MidiChannel *getPercussionChannel() { return 0; } + + // AudioStream + bool isStereo() const { return false; } + int getRate() const { return _mixer->getOutputRate(); } + + // MidiDriver_Emulated + void generateSamples(int16 *buf, int len); + +private: + + void handleMidiEvent0x90_NoteOn(int channel, int param1, int param2); + void handleSequencerSpecificMetaEvent1(int channel, const uint8 *data); + void handleSequencerSpecificMetaEvent2(uint8 value); + void handleSequencerSpecificMetaEvent3(uint8 value); + + void Adlib_Write(uint8 port, uint8 value); + void Adlib_SetupCard(); + void Adlib_SetupChannels(int fl); + void Adlib_ResetAmpVibratoRhythm(int am, int vib, int kso); + void Adlib_ResetChannels(); + void Adlib_SetAmpVibratoRhythm(); + void Adlib_SetCSMKeyboardSplit(); + void Adlib_SetNoteMul(int mul); + void Adlib_SetWaveformSelect(int fl); + void Adlib_SetPitchBend(int channel, int range); + void Adlib_PlayNote(int channel); + uint8 Adlib_PlayNoteHelper(int channel, int note1, int note2, int oct); + void Adlib_TurnNoteOff(int channel); + void Adlib_TurnNoteOn(int channel, int note); + void Adlib_SetupChannelFromSequence(int channel, const uint8 *src, int fl); + void Adlib_SetupChannel(int channel, const uint16 *src, int fl); + void Adlib_SetNoteVolume(int channel, int volume); + void Adlib_SetupChannelHelper(int channel); + void Adlib_SetChannel0x40(int channel); + void Adlib_SetChannel0xC0(int channel); + void Adlib_SetChannel0x60(int channel); + void Adlib_SetChannel0x80(int channel); + void Adlib_SetChannel0x20(int channel); + void Adlib_SetChannel0xE0(int channel); + + FM_OPL *_opl; + int _midiNumberOfChannels; + int _adlibNoteMul; + int _adlibWaveformSelect; + int _adlibAMDepthEq48; + int _adlibVibratoDepthEq14; + int _adlibRhythmEnabled; + int _adlibKeyboardSplitOn; + int _adlibVibratoRhythm; + uint8 _midiChannelsFreqTable[9]; + uint8 _adlibChannelsLevelKeyScalingTable[11]; + uint8 _adlibSetupChannelSequence1[14 * 18]; + uint16 _adlibSetupChannelSequence2[14]; + int16 _midiChannelsNote2Table[9]; + uint8 _midiChannelsNote1Table[9]; + uint8 _midiChannelsOctTable[9]; + uint16 _adlibChannelsVolume[11]; + uint16 _adlibMetaSequenceData[28]; + + static const uint8 _adlibChannelsMappingTable1[]; + static const uint8 _adlibChannelsNoFeedback[]; + static const uint8 _adlibChannelsMappingTable2[]; + static const uint8 _adlibChannelsMappingTable3[]; + static const uint8 _adlibChannelsKeyScalingTable1[]; + static const uint8 _adlibChannelsKeyScalingTable2[]; + static const uint8 _adlibChannelsVolumeTable[]; + static const uint8 _adlibInitSequenceData1[]; + static const uint8 _adlibInitSequenceData2[]; + static const uint8 _adlibInitSequenceData3[]; + static const uint8 _adlibInitSequenceData4[]; + static const uint8 _adlibInitSequenceData5[]; + static const uint8 _adlibInitSequenceData6[]; + static const uint8 _adlibInitSequenceData7[]; + static const uint8 _adlibInitSequenceData8[]; + static const int16 _midiChannelsNoteTable[]; + static const int16 _midiNoteFreqTable[]; +}; + +int AdlibMidiDriver::open() { + MidiDriver_Emulated::open(); + _opl = makeAdlibOPL(getRate()); + Adlib_SetupCard(); + for (int i = 0; i < 11; ++i) { + _adlibChannelsVolume[i] = 0; + Adlib_SetNoteVolume(i, 0); + Adlib_TurnNoteOff(i); + } + _mixer->playInputStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, false, true); + return 0; +} + +void AdlibMidiDriver::close() { + _mixer->stopHandle(_mixerSoundHandle); + OPLDestroy(_opl); +} + +void AdlibMidiDriver::send(uint32 b) { + int channel = b & 15; + int cmd = (b >> 4) & 7; + int param1 = (b >> 8) & 255; + int param2 = (b >> 16) & 255; + switch (cmd) { + case 0: + Adlib_TurnNoteOff(channel); + break; + case 1: + handleMidiEvent0x90_NoteOn(channel, param1, param2); + break; + case 3: + break; + case 5: + Adlib_SetNoteVolume(channel, param1); + _adlibChannelsVolume[channel] = param1; + break; + case 6: + Adlib_SetPitchBend(channel, param1 | (param2 << 7)); + break; + default: +// warning("Unhandled cmd %d channel %d (0x%X)\n", cmd, channel, b); + break; + } +} + +void AdlibMidiDriver::metaEvent(byte type, byte *data, uint16 length) { + int event = 0; + if (length > 4 && READ_BE_UINT32(data) == 0x3F00) { + event = data[4]; + switch (event) { + case 1: + if (length == 34) { + handleSequencerSpecificMetaEvent1(data[5], data + 6); + return; + } + break; + case 2: + if (length == 6) { + handleSequencerSpecificMetaEvent2(data[5]); + return; + } + break; + case 3: + if (length == 6) { + handleSequencerSpecificMetaEvent3(data[5]); + return; + } + break; + } + } + warning("Unhandled meta event %d len %d", event, length); +} + +void AdlibMidiDriver::generateSamples(int16 *data, int len) { + memset(data, 0, sizeof(int16) * len); + YM3812UpdateOne(_opl, data, len); +} + +void AdlibMidiDriver::handleSequencerSpecificMetaEvent1(int channel, const uint8 *data) { + for (int i = 0; i < 28; ++i) { + _adlibMetaSequenceData[i] = data[i]; + } + if (_midiNumberOfChannels > channel) { + const uint8 *p; + if (_adlibRhythmEnabled) { + p = &_adlibChannelsKeyScalingTable2[channel * 2]; + } else { + p = &_adlibChannelsKeyScalingTable1[channel * 2]; + } + Adlib_SetupChannel(p[0], _adlibMetaSequenceData, _adlibMetaSequenceData[26]); + if (p[1] != 255) { + Adlib_SetupChannel(p[1], _adlibMetaSequenceData + 13, _adlibMetaSequenceData[27]); + } + } +} + +void AdlibMidiDriver::handleSequencerSpecificMetaEvent2(uint8 value) { + _adlibRhythmEnabled = value; + _midiNumberOfChannels = _adlibRhythmEnabled ? 11 : 9; + Adlib_SetAmpVibratoRhythm(); +} + +void AdlibMidiDriver::handleSequencerSpecificMetaEvent3(uint8 value) { + Adlib_SetNoteMul(value); +} + +void AdlibMidiDriver::handleMidiEvent0x90_NoteOn(int channel, int param1, int param2) { // note, volume + if (param2 == 0) { + Adlib_TurnNoteOff(channel); + _adlibChannelsVolume[channel] = param2; + } else { + Adlib_SetNoteVolume(channel, param2); + _adlibChannelsVolume[channel] = param2; + Adlib_TurnNoteOff(channel); + Adlib_TurnNoteOn(channel, param1); + } +} + +void AdlibMidiDriver::Adlib_Write(uint8 port, uint8 value) { + OPLWriteReg(_opl, port, value); +} + +void AdlibMidiDriver::Adlib_SetupCard() { + for (int i = 1; i <= 0xF5; ++i) { + Adlib_Write(i, 0); + } + Adlib_Write(4, 6); + for (int i = 0; i < 9; ++i) { + _midiChannelsNote2Table[i] = 8192; + _midiChannelsOctTable[i] = 0; + _midiChannelsNote1Table[i] = 0; + _midiChannelsFreqTable[i] = 0; + } + memset(_adlibChannelsLevelKeyScalingTable, 127, 11); + Adlib_SetupChannels(0); + Adlib_ResetAmpVibratoRhythm(0, 0, 0); + Adlib_SetNoteMul(1); + Adlib_SetWaveformSelect(1); +} + +void AdlibMidiDriver::Adlib_SetupChannels(int fl) { + if (fl != 0) { + _midiChannelsNote1Table[8] = 24; + _midiChannelsNote2Table[8] = 8192; + Adlib_PlayNote(8); + _midiChannelsNote1Table[7] = 31; + _midiChannelsNote2Table[7] = 8192; + Adlib_PlayNote(7); + } + _adlibRhythmEnabled = fl; + _midiNumberOfChannels = fl ? 11 : 9; + _adlibVibratoRhythm = 0; + _adlibAMDepthEq48 = 0; + _adlibVibratoDepthEq14 = 0; + _adlibKeyboardSplitOn = 0; + Adlib_ResetChannels(); + Adlib_SetAmpVibratoRhythm(); +} + +void AdlibMidiDriver::Adlib_ResetAmpVibratoRhythm(int am, int vib, int kso) { + _adlibAMDepthEq48 = am; + _adlibVibratoDepthEq14 = vib; + _adlibKeyboardSplitOn = kso; + Adlib_SetAmpVibratoRhythm(); + Adlib_SetCSMKeyboardSplit(); +} + +void AdlibMidiDriver::Adlib_ResetChannels() { + for (int i = 0; i < 18; ++i) { + Adlib_SetupChannelFromSequence(i, _adlibChannelsNoFeedback[i] ? _adlibInitSequenceData2 : _adlibInitSequenceData1, 0); + } + if (_adlibRhythmEnabled) { + Adlib_SetupChannelFromSequence(12, _adlibInitSequenceData3, 0); + Adlib_SetupChannelFromSequence(15, _adlibInitSequenceData4, 0); + Adlib_SetupChannelFromSequence(16, _adlibInitSequenceData5, 0); + Adlib_SetupChannelFromSequence(14, _adlibInitSequenceData6, 0); + Adlib_SetupChannelFromSequence(17, _adlibInitSequenceData7, 0); + Adlib_SetupChannelFromSequence(13, _adlibInitSequenceData8, 0); + } +} + +void AdlibMidiDriver::Adlib_SetAmpVibratoRhythm() { + uint8 value = 0; + if (_adlibAMDepthEq48) { + value |= 0x80; + } + if (_adlibVibratoDepthEq14) { + value |= 0x40; + } + if (_adlibRhythmEnabled) { + value |= 0x20; + } + Adlib_Write(0xBD, value | _adlibVibratoRhythm); +} + +void AdlibMidiDriver::Adlib_SetCSMKeyboardSplit() { + uint8 value = _adlibKeyboardSplitOn ? 0x40 : 0; + Adlib_Write(8, value); +} + +void AdlibMidiDriver::Adlib_SetNoteMul(int mul) { + if (mul > 12) { + mul = 12; + } else if (mul < 1) { + mul = 1; + } + _adlibNoteMul = mul; +} + +void AdlibMidiDriver::Adlib_SetWaveformSelect(int fl) { + _adlibWaveformSelect = fl ? 0x20 : 0; + for (int i = 0; i < 18; ++i) { + Adlib_Write(0xE0 + _adlibChannelsMappingTable1[i], 0); + } + Adlib_Write(1, _adlibWaveformSelect); +} + +void AdlibMidiDriver::Adlib_SetPitchBend(int channel, int range) { + if ((_adlibRhythmEnabled && channel <= 6) || channel < 9) { + if (range > 16383) { + range = 16383; + } + _midiChannelsNote2Table[channel] = range; + Adlib_PlayNote(channel); + } +} + +void AdlibMidiDriver::Adlib_PlayNote(int channel) { + _midiChannelsFreqTable[channel] = Adlib_PlayNoteHelper(channel, _midiChannelsNote1Table[channel], _midiChannelsNote2Table[channel], _midiChannelsOctTable[channel]); +} + +uint8 AdlibMidiDriver::Adlib_PlayNoteHelper(int channel, int note1, int note2, int oct) { + int n = ((note2 * _midiChannelsNoteTable[channel]) >> 8) - 8192; + if (n != 0) { + n >>= 5; + n *= _adlibNoteMul; + } + n += (note1 << 8) + 8; + n >>= 4; + if (n < 0) { + n = 0; + } else if (n > 1535) { + n = 1535; + } + int index = (((n >> 4) % 12) << 4) | (n & 0xF); + int f = _midiNoteFreqTable[index]; + int o = (n >> 4) / 12 - 1; + if (f < 0) { + ++o; + } + if (o < 0) { + ++o; + f >>= 1; + } + Adlib_Write(0xA0 + channel, f & 0xFF); + int value = ((f >> 8) & 3) | (o << 2) | oct; + Adlib_Write(0xB0 + channel, value); + return value; +} + +void AdlibMidiDriver::Adlib_TurnNoteOff(int channel) { + if ((_adlibRhythmEnabled && channel <= 6) || channel < 9) { + _midiChannelsOctTable[channel] = 0; + _midiChannelsFreqTable[channel] &= ~0x20; + Adlib_Write(0xB0 + channel, _midiChannelsFreqTable[channel]); + } else if (_adlibRhythmEnabled && channel <= 10) { + _adlibVibratoRhythm &= ~(1 << (4 - (channel - 6))); + Adlib_SetAmpVibratoRhythm(); + } +} + +void AdlibMidiDriver::Adlib_TurnNoteOn(int channel, int note) { + note -= 12; + if (note < 0) { + note = 0; + } + if ((_adlibRhythmEnabled && channel <= 6) || channel < 9) { + _midiChannelsNote1Table[channel] = note; + _midiChannelsOctTable[channel] = 0x20; + Adlib_PlayNote(channel); + } else if (_adlibRhythmEnabled && channel <= 10) { + if (channel == 6) { + _midiChannelsNote1Table[6] = note; + Adlib_PlayNote(channel); + } else if (channel == 8 && _midiChannelsNote1Table[8] == note) { + _midiChannelsNote1Table[8] = note; + _midiChannelsNote1Table[7] = note + 7; + Adlib_PlayNote(8); + Adlib_PlayNote(7); + } + _adlibVibratoRhythm = 1 << (4 - (channel - 6)); + Adlib_SetAmpVibratoRhythm(); + } +} + +void AdlibMidiDriver::Adlib_SetupChannelFromSequence(int channel, const uint8 *src, int fl) { + for (int i = 0; i < 13; ++i) { + _adlibSetupChannelSequence2[i] = src[i]; + } + Adlib_SetupChannel(channel, _adlibSetupChannelSequence2, fl); +} + +void AdlibMidiDriver::Adlib_SetupChannel(int channel, const uint16 *src, int fl) { + for (int i = 0; i < 13; ++i) { + _adlibSetupChannelSequence1[14 * channel + i] = src[i]; + } + _adlibSetupChannelSequence1[14 * channel + 13] = fl & 3; + Adlib_SetupChannelHelper(channel); +} + +void AdlibMidiDriver::Adlib_SetNoteVolume(int channel, int volume) { + if (_midiNumberOfChannels > channel) { + if (volume > 127) { + volume = 127; + } + _adlibChannelsLevelKeyScalingTable[channel] = volume; + const uint8 *p; + if (_adlibRhythmEnabled) { + p = &_adlibChannelsKeyScalingTable2[channel * 2]; + } else { + p = &_adlibChannelsKeyScalingTable1[channel * 2]; + } + Adlib_SetChannel0x40(p[0]); + if (p[1] != 255) { + Adlib_SetChannel0x40(p[1]); + } + } +} + +void AdlibMidiDriver::Adlib_SetupChannelHelper(int channel) { + Adlib_SetAmpVibratoRhythm(); + Adlib_SetCSMKeyboardSplit(); + Adlib_SetChannel0x40(channel); + Adlib_SetChannel0xC0(channel); + Adlib_SetChannel0x60(channel); + Adlib_SetChannel0x80(channel); + Adlib_SetChannel0x20(channel); + Adlib_SetChannel0xE0(channel); +} + +void AdlibMidiDriver::Adlib_SetChannel0x40(int channel) { + int index, value, fl; + + if (_adlibRhythmEnabled) { + index = _adlibChannelsMappingTable3[channel]; + } else { + index = _adlibChannelsMappingTable2[channel]; + } + value = 63 - (_adlibSetupChannelSequence1[channel * 14 + 8] & 63); + fl = 0; + if (_adlibRhythmEnabled && index > 6) { + fl = -1; + } + if (_adlibChannelsNoFeedback[channel] || _adlibSetupChannelSequence1[channel * 14 + 12] == 0 || fl != 0) { + value = ((_adlibChannelsLevelKeyScalingTable[index] * value) + 64) >> 7; + } + value = (_adlibChannelsVolumeTable[index] * value * 2) >> 8; + if (value > 63) { + value = 63; + } + value = 63 - value; + value |= _adlibSetupChannelSequence1[channel * 14] << 6; + Adlib_Write(0x40 + _adlibChannelsMappingTable1[channel], value); +} + +void AdlibMidiDriver::Adlib_SetChannel0xC0(int channel) { + if (_adlibChannelsNoFeedback[channel] == 0) { + const uint8 *p = &_adlibSetupChannelSequence1[channel * 14]; + uint8 value = p[2] << 1; + if (p[12] == 0) { + value |= 1; + } + Adlib_Write(0xC0 + _adlibChannelsMappingTable2[channel], value); + } +} + +void AdlibMidiDriver::Adlib_SetChannel0x60(int channel) { + const uint8 *p = &_adlibSetupChannelSequence1[channel * 14]; + uint8 value = (p[3] << 4) | (p[6] & 15); + Adlib_Write(0x60 + _adlibChannelsMappingTable1[channel], value); +} + +void AdlibMidiDriver::Adlib_SetChannel0x80(int channel) { + const uint8 *p = &_adlibSetupChannelSequence1[channel * 14]; + uint8 value = (p[4] << 4) | (p[7] & 15); + Adlib_Write(0x80 + _adlibChannelsMappingTable1[channel], value); +} + +void AdlibMidiDriver::Adlib_SetChannel0x20(int channel) { + const uint8 *p = &_adlibSetupChannelSequence1[channel * 14]; + uint8 value = p[1] & 15; + if (p[9]) { + value |= 0x80; + } + if (p[10]) { + value |= 0x40; + } + if (p[5]) { + value |= 0x20; + } + if (p[11]) { + value |= 0x10; + } + Adlib_Write(0x20 + _adlibChannelsMappingTable1[channel], value); +} + +void AdlibMidiDriver::Adlib_SetChannel0xE0(int channel) { + uint8 value = 0; + if (_adlibWaveformSelect) { + const uint8 *p = &_adlibSetupChannelSequence1[channel * 14]; + value = p[13] & 3; + } + Adlib_Write(0xE0 + _adlibChannelsMappingTable1[channel], value); +} + +const uint8 AdlibMidiDriver::_adlibChannelsMappingTable1[] = { + 0, 1, 2, 3, 4, 5, 8, 9, 10, 11, 12, 13, 16, 17, 18, 19, 20, 21 +}; + +const uint8 AdlibMidiDriver::_adlibChannelsNoFeedback[] = { + 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1 +}; + +const uint8 AdlibMidiDriver::_adlibChannelsMappingTable2[] = { + 0, 1, 2, 0, 1, 2, 3, 4, 5, 3, 4, 5, 6, 7, 8, 6, 7, 8 +}; + +const uint8 AdlibMidiDriver::_adlibChannelsMappingTable3[] = { + 0, 1, 2, 0, 1, 2, 3, 4, 5, 3, 4, 5, 6, 10, 8, 6, 7, 9 +}; + +const uint8 AdlibMidiDriver::_adlibChannelsKeyScalingTable1[] = { + 0, 3, 1, 4, 2, 5, 6, 9, 7, 10, 8, 11, 12, 15, 13, 16, 14, 17 +}; + +const uint8 AdlibMidiDriver::_adlibChannelsKeyScalingTable2[] = { + 0, 3, 1, 4, 2, 5, 6, 9, 7, 10, 8, 11, 12, 15, 16, 255, 14, 255, 17, 255, 13, 255 +}; + +const uint8 AdlibMidiDriver::_adlibChannelsVolumeTable[] = { + 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 +}; + +const uint8 AdlibMidiDriver::_adlibInitSequenceData1[] = { + 1, 1, 3, 15, 5, 0, 1, 3, 15, 0, 0, 0, 1, 0 +}; + +const uint8 AdlibMidiDriver::_adlibInitSequenceData2[] = { + 0, 1, 1, 15, 7, 0, 2, 4, 0, 0, 0, 1, 0, 0 +}; + +const uint8 AdlibMidiDriver::_adlibInitSequenceData3[] = { + 0, 0, 0, 10, 4, 0, 8, 12, 11, 0, 0, 0, 1, 0 +}; + +const uint8 AdlibMidiDriver::_adlibInitSequenceData4[] = { + 0, 0, 0, 13, 4, 0, 6, 15, 0, 0, 0, 0, 1, 0 +}; + +const uint8 AdlibMidiDriver::_adlibInitSequenceData5[] = { + 0, 12, 0, 15, 11, 0, 8, 5, 0, 0, 0, 0, 0, 0 +}; + +const uint8 AdlibMidiDriver::_adlibInitSequenceData6[] = { + 0, 4, 0, 15, 11, 0, 7, 5, 0, 0, 0, 0, 0, 0 +}; + +const uint8 AdlibMidiDriver::_adlibInitSequenceData7[] = { + 0, 1, 0, 15, 11, 0, 5, 5, 0, 0, 0, 0, 0, 0 +}; + +const uint8 AdlibMidiDriver::_adlibInitSequenceData8[] = { + 0, 1, 0, 15, 11, 0, 7, 5, 0, 0, 0, 0, 0, 0 +}; + +const int16 AdlibMidiDriver::_midiChannelsNoteTable[] = { + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256 +}; + +const int16 AdlibMidiDriver::_midiNoteFreqTable[] = { + 690, 692, 695, 697, 700, 702, 705, 707, 710, 713, 715, 718, + 720, 723, 726, 728, 731, 733, 736, 739, 741, 744, 747, 749, + 752, 755, 758, 760, 763, 766, 769, 771, 774, 777, 780, 783, + 785, 788, 791, 794, 797, 800, 803, 806, 809, 811, 814, 817, + 820, 823, 826, 829, 832, 835, 838, 841, 844, 847, 850, 854, + 857, 860, 863, 866, 869, 872, 875, 879, 882, 885, 888, 891, + 895, 898, 901, 904, 908, 911, 914, 917, 921, 924, 927, 931, + 934, 937, 941, 944, 948, 951, 955, 958, 961, 965, 968, 972, + 975, 979, 983, 986, 990, 993, 997, 1000, 1004, 1008, 1011, 1015, + 1019, 1022, -511, -509, -507, -505, -504, -502, -500, -498, -496, -494, + -492, -490, -488, -486, -484, -482, -480, -479, -477, -475, -473, -471, + -469, -467, -465, -463, -460, -458, -456, -454, -452, -450, -448, -446, + -444, -442, -440, -438, -436, -433, -431, -429, -427, -425, -423, -420, + -418, -416, -414, -412, -409, -407, -405, -403, -401, -398, -396, -394, + -391, -389, -387, -385, -382, -380, -378, -375, -373, -371, -368, -366, + -363, -361, -359, -356, -354, -351, -349, -347, -344, -342, -339, -337 +}; + +MidiDriver *C_Player_CreateAdlibMidiDriver(Audio::Mixer *mixer) { + return new AdlibMidiDriver(mixer); +} + +} // End of namespace Queen diff --git a/engines/queen/module.mk b/engines/queen/module.mk index f603e0a736c..c691906558b 100644 --- a/engines/queen/module.mk +++ b/engines/queen/module.mk @@ -12,6 +12,7 @@ MODULE_OBJS := \ input.o \ journal.o \ logic.o \ + midiadlib.o \ music.o \ musicdata.o \ queen.o \ @@ -27,5 +28,5 @@ ifdef BUILD_PLUGINS PLUGIN := 1 endif -# Include common rules +# Include common rules include $(srcdir)/rules.mk diff --git a/engines/queen/music.cpp b/engines/queen/music.cpp index e5255c17782..e5c87ca53d8 100644 --- a/engines/queen/music.cpp +++ b/engines/queen/music.cpp @@ -23,6 +23,7 @@ * */ +#include "common/config-manager.h" #include "common/events.h" #include "queen/music.h" @@ -34,28 +35,64 @@ namespace Queen { -MidiMusic::MidiMusic(MidiDriver *driver, QueenEngine *vm) - : _driver(driver), _isPlaying(false), _looping(false), _randomLoop(false), _masterVolume(192), _queuePos(0), _passThrough(false), _buf(0) { +extern MidiDriver *C_Player_CreateAdlibMidiDriver(Audio::Mixer *); + +MidiMusic::MidiMusic(QueenEngine *vm) + : _isPlaying(false), _looping(false), _randomLoop(false), _masterVolume(192), _buf(0) { + memset(_channel, 0, sizeof(_channel)); + _queuePos = _lastSong = _currentSong = 0; queueClear(); - _lastSong = _currentSong = 0; + + int midiDriver = MidiDriver::detectMusicDriver(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MIDI); + _adlib = (midiDriver == MD_ADLIB); + _nativeMT32 = ((midiDriver == MD_MT32) || ConfMan.getBool("native_mt32")); + + const char *musicDataFile; + if (vm->resource()->isDemo()) { + _tune = Sound::_tuneDemo; + musicDataFile = "AQ8.RL"; + } else { + _tune = Sound::_tune; + musicDataFile = "AQ.RL"; + } + if (_adlib) { + musicDataFile = "AQBANK.MUS"; + } + _musicData = vm->resource()->loadFile(musicDataFile, 0, &_musicDataSize); + _numSongs = READ_LE_UINT16(_musicData); + + _tune = vm->resource()->isDemo() ? Sound::_tuneDemo : Sound::_tune; + + if (_adlib) { +// int infoOffset = _numSongs * 4 + 2; +// if (READ_LE_UINT16(_musicData + 2) != infoOffset) { +// defaultAdlibVolume = _musicData[infoOffset]; +// } + _driver = C_Player_CreateAdlibMidiDriver(vm->_mixer); + } else { + _driver = MidiDriver::createMidi(midiDriver); + if (_nativeMT32) { + _driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE); + } + } + + _driver->open(); + _driver->setTimerCallback(this, &onTimer); + _parser = MidiParser::createParser_SMF(); _parser->setMidiDriver(this); _parser->setTimerRate(_driver->getBaseTempo()); - const char *filename = vm->resource()->isDemo() ? "AQ8.RL" : "AQ.RL"; - _musicData = vm->resource()->loadFile(filename, 0, &_musicDataSize); - _numSongs = READ_LE_UINT16(_musicData); - this->open(); - - _tune = vm->resource()->isDemo() ? Sound::_tuneDemo : Sound::_tune; vm->_system->getEventManager()->registerRandomSource(_rnd, "queenMusic"); } MidiMusic::~MidiMusic() { + _driver->setTimerCallback(0, 0); + _driver->close(); + delete _driver; _parser->unloadMusic(); delete _parser; - this->close(); delete[] _buf; delete[] _musicData; } @@ -100,7 +137,7 @@ bool MidiMusic::queueSong(uint16 songNum) { // Work around bug in Roland music, note that these numbers are 'one-off' // from the original code - if (/*isRoland && */ songNum == 88 || songNum == 89) + if (!_adlib && (songNum == 88 || songNum == 89)) songNum = 62; _songQueue[MUSIC_QUEUE_SIZE - emptySlots] = songNum; @@ -114,27 +151,8 @@ void MidiMusic::queueClear() { memset(_songQueue, 0, sizeof(_songQueue)); } -int MidiMusic::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 MidiMusic::close() { - _driver->setTimerCallback(NULL, NULL); - if (_driver) - _driver->close(); - _driver = 0; -} - void MidiMusic::send(uint32 b) { - if (_passThrough) { + if (_adlib) { _driver->send(b); return; } @@ -146,13 +164,12 @@ void MidiMusic::send(uint32 b) { _channelVolume[channel] = volume; volume = volume * _masterVolume / 255; b = (b & 0xFF00FFFF) | (volume << 16); - } else if ((b & 0xF0) == 0xC0 && !_nativeMT32) { + } else if ((b & 0xF0) == 0xC0 && !_adlib && !_nativeMT32) { b = (b & 0xFFFF00FF) | MidiDriver::_mt32ToGm[(b >> 8) & 0xFF] << 8; - } - else if ((b & 0xFFF0) == 0x007BB0) { + } else if ((b & 0xFFF0) == 0x007BB0) { //Only respond to All Notes Off if this channel //has currently been allocated - if (_channel[b & 0x0F]) + if (_channel[channel]) return; } @@ -172,14 +189,23 @@ void MidiMusic::send(uint32 b) { } void MidiMusic::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 - stopMusic(); + switch (type) { + case 0x2F: // End of Track + if (_looping || _songQueue[1]) { + playMusic(); + } else { + stopMusic(); + } + break; + case 0x7F: // Specific + if (_adlib) { + _driver->metaEvent(type, data, length); + } + break; + default: +// warning("Unhandled meta event: %02x", type); + break; + } } void MidiMusic::onTimer(void *refCon) { @@ -248,7 +274,7 @@ void MidiMusic::playMusic() { } byte *prevSong = _musicData + songOffset(_currentSong); - if (*prevSong == 0x43 || *prevSong == 0x63) { + if (*prevSong == 'C' || *prevSong == 'c') { if (_buf) { delete[] _buf; _buf = 0; @@ -263,7 +289,7 @@ void MidiMusic::playMusic() { byte *musicPtr = _musicData + songOffset(songNum); uint32 size = songLength(songNum); - if (*musicPtr == 0x43 || *musicPtr == 0x63) { + if (*musicPtr == 'C' || *musicPtr == 'c') { uint32 packedSize = songLength(songNum) - 0x200; _buf = new uint16[packedSize]; @@ -277,7 +303,7 @@ void MidiMusic::playMusic() { _buf[i] = data[*(idx + i)]; #endif - musicPtr = ((byte *)_buf) + ((*musicPtr == 0x63) ? 1 : 0); + musicPtr = ((byte *)_buf) + ((*musicPtr == 'c') ? 1 : 0); size = packedSize * 2; } diff --git a/engines/queen/music.h b/engines/queen/music.h index 22b8490367e..9e8caeceeb7 100644 --- a/engines/queen/music.h +++ b/engines/queen/music.h @@ -33,18 +33,17 @@ class MidiParser; namespace Queen { -struct tuneData; +struct TuneData; class QueenEngine; class MidiMusic : public MidiDriver { public: - MidiMusic(MidiDriver *driver, QueenEngine *vm); + MidiMusic(QueenEngine *vm); ~MidiMusic(); void setVolume(int volume); - int getVolume() { return _masterVolume; } + int getVolume() const { return _masterVolume; } - void hasNativeMT32(bool b) { _nativeMT32 = b; } void playSong(uint16 songNum); void stopSong() { stopMusic(); } void playMusic(); @@ -53,12 +52,11 @@ public: void queueTuneList(int16 tuneList); bool queueSong(uint16 songNum); void queueClear(); - void setPassThrough(bool b) { _passThrough = b; } void toggleVChange(); //MidiDriver interface implementation - int open(); - void close(); + int open() { return 0; } + void close() {} void send(uint32 b); void metaEvent(byte type, byte *data, uint16 length); @@ -86,8 +84,8 @@ protected: MidiParser *_parser; MidiChannel *_channel[16]; byte _channelVolume[16]; + bool _adlib; bool _nativeMT32; - bool _passThrough; Common::RandomSource _rnd; @@ -105,7 +103,7 @@ protected: uint32 _musicDataSize; bool _vToggle; byte *_musicData; - const tuneData *_tune; + const TuneData *_tune; }; } // End of namespace Queen diff --git a/engines/queen/musicdata.cpp b/engines/queen/musicdata.cpp index 57de9719317..865f4cd21df 100644 --- a/engines/queen/musicdata.cpp +++ b/engines/queen/musicdata.cpp @@ -28,7 +28,7 @@ namespace Queen { -const songData Sound::_songDemo[] = { +const SongData Sound::_songDemo[] = { /* 1 - Hotel Gangsters */ { { 1, 0 }, 128, 128, 128, 1, 0 }, @@ -123,7 +123,7 @@ const songData Sound::_songDemo[] = { { { 34, 0 }, 128, 128, 128, 1, 1 }, }; -const songData Sound::_song[] = { +const SongData Sound::_song[] = { /* 1 - Hotel Gangsters */ { { 1, 0 }, 128, 180, 0, 1, 0 }, @@ -752,7 +752,7 @@ const songData Sound::_song[] = { { { 212, 0 }, 128, 128, 128, 1, 0 } }; -const tuneData Sound::_tuneDemo[] = { +const TuneData Sound::_tuneDemo[] = { /* 1 - Hotel Gangsters */ { { 32, 0 }, { 0, 0 }, 1, 0 }, @@ -859,7 +859,7 @@ const tuneData Sound::_tuneDemo[] = { { { 46, 0 }, { 0, 0 }, 1, 0 }, }; -const tuneData Sound::_tune[] = { +const TuneData Sound::_tune[] = { /* 1 - Hotel Gangsters */ { { 32, 0 }, { 0, 0 }, 1, 0 }, diff --git a/engines/queen/sound.cpp b/engines/queen/sound.cpp index fffeaf5f1d9..8b82f715ca4 100644 --- a/engines/queen/sound.cpp +++ b/engines/queen/sound.cpp @@ -157,15 +157,7 @@ void Sound::loadState(uint32 ver, byte *&ptr) { PCSound::PCSound(Audio::Mixer *mixer, QueenEngine *vm) : Sound(mixer, vm) { - int midiDriver = MidiDriver::detectMusicDriver(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MIDI); - bool native_mt32 = ((midiDriver == MD_MT32) || ConfMan.getBool("native_mt32")); - - MidiDriver *driver = MidiDriver::createMidi(midiDriver); - if (native_mt32) - driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE); - - _music = new MidiMusic(driver, vm); - _music->hasNativeMT32(native_mt32); + _music = new MidiMusic(vm); } PCSound::~PCSound() { diff --git a/engines/queen/sound.h b/engines/queen/sound.h index d87ab63a183..39061671939 100644 --- a/engines/queen/sound.h +++ b/engines/queen/sound.h @@ -37,7 +37,7 @@ namespace Common { namespace Queen { -struct songData { +struct SongData { int16 tuneList[5]; int16 volume; int16 tempo; @@ -46,7 +46,7 @@ struct songData { int16 ignore; }; -struct tuneData { +struct TuneData { int16 tuneNum[9]; int16 sfx[2]; int16 mode; @@ -103,10 +103,10 @@ public: void saveState(byte *&ptr); void loadState(uint32 ver, byte *&ptr); - static const songData _songDemo[]; - static const songData _song[]; - static const tuneData _tuneDemo[]; - static const tuneData _tune[]; + static const SongData _songDemo[]; + static const SongData _song[]; + static const TuneData _tuneDemo[]; + static const TuneData _tune[]; static const char *_sfxName[]; static const int16 _jungleList[];