diff --git a/audio/adlib.cpp b/audio/adlib.cpp index b4099128a8f..196e7feed89 100644 --- a/audio/adlib.cpp +++ b/audio/adlib.cpp @@ -942,7 +942,6 @@ public: uint32 getBaseTempo() override { return 1000000 / OPL::OPL::kDefaultCallbackFrequency; } void setPitchBendRange(byte channel, uint range) override; - void sysEx_customInstrument(byte channel, uint32 type, const byte *instr) override; MidiChannel *allocateChannel() override; MidiChannel *getPercussionChannel() override { return &_percussion; } // Percussion partially supported @@ -1585,10 +1584,6 @@ void MidiDriver_ADLIB::setPitchBendRange(byte channel, uint range) { } } -void MidiDriver_ADLIB::sysEx_customInstrument(byte channel, uint32 type, const byte *instr) { - _parts[channel].sysEx_customInstrument(type, instr); -} - MidiChannel *MidiDriver_ADLIB::allocateChannel() { AdLibPart *part; uint i; diff --git a/audio/mididrv.h b/audio/mididrv.h index adc85b6e905..49a279f6246 100644 --- a/audio/mididrv.h +++ b/audio/mididrv.h @@ -499,8 +499,6 @@ public: */ void sendGMReset(); - virtual void sysEx_customInstrument(byte channel, uint32 type, const byte *instr) { } - // Timing functions - MidiDriver now operates timers virtual void setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc) = 0; diff --git a/audio/mpu401.cpp b/audio/mpu401.cpp index 76ae76fd8f0..06ab6ebd355 100644 --- a/audio/mpu401.cpp +++ b/audio/mpu401.cpp @@ -71,9 +71,6 @@ void MidiChannel_MPU401::pitchBendFactor(byte value) { _owner->setPitchBendRange(_channel, value); } -void MidiChannel_MPU401::sysEx_customInstrument(uint32 type, const byte *instr) { - _owner->sysEx_customInstrument(_channel, type, instr); -} const char *MidiDriver::getErrorName(int error_code) { static const char *const midi_errors[] = { diff --git a/audio/mpu401.h b/audio/mpu401.h index 9d91a144bd4..13857d852ab 100644 --- a/audio/mpu401.h +++ b/audio/mpu401.h @@ -55,7 +55,7 @@ public: virtual void pitchBendFactor(byte value); // SysEx messages - virtual void sysEx_customInstrument(uint32 type, const byte *instr); + virtual void sysEx_customInstrument(uint32 type, const byte *instr) {} // Only to be called by the owner void init(MidiDriver *owner, byte channel); diff --git a/engines/scumm/imuse/drivers/fmtowns.cpp b/engines/scumm/imuse/drivers/fmtowns.cpp index 787e787a5cf..6161bc8c82a 100644 --- a/engines/scumm/imuse/drivers/fmtowns.cpp +++ b/engines/scumm/imuse/drivers/fmtowns.cpp @@ -19,15 +19,17 @@ * */ -#include "engines/scumm/imuse/drivers/fmtowns.h" +#include "scumm/imuse/drivers/fmtowns.h" #include "audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h" #include "common/textconsole.h" #include "common/system.h" +namespace Scumm { + class TownsMidiOutputChannel { friend class TownsMidiInputChannel; public: - TownsMidiOutputChannel(MidiDriver_TOWNS *driver, int chanId); + TownsMidiOutputChannel(IMuseDriver_FMTowns *driver, int chanId); ~TownsMidiOutputChannel(); void noteOn(uint8 msb, uint16 lsb); @@ -103,7 +105,7 @@ private: uint16 _freq; int16 _freqAdjust; - MidiDriver_TOWNS *_driver; + IMuseDriver_FMTowns *_driver; static const uint8 _chanMap[]; static const uint8 _chanMap2[]; @@ -116,11 +118,12 @@ private: class TownsMidiInputChannel : public MidiChannel { friend class TownsMidiOutputChannel; public: - TownsMidiInputChannel(MidiDriver_TOWNS *driver, int chanIndex); + TownsMidiInputChannel(IMuseDriver_FMTowns *driver, int16 number); ~TownsMidiInputChannel() override; MidiDriver *device() override { return _driver; } - byte getNumber() override { return _chanIndex; } + byte getNumber() override { return _number; } + bool allocate(); void release() override; @@ -147,8 +150,8 @@ private: TownsMidiOutputChannel *_out; + const byte _number; uint8 *_instrument; - uint8 _chanIndex; uint8 _priority; uint8 _tl; int8 _transpose; @@ -161,7 +164,7 @@ private: bool _allocated; - MidiDriver_TOWNS *_driver; + IMuseDriver_FMTowns *_driver; static const uint8 _programAdjustLevel[]; }; @@ -207,7 +210,7 @@ uint8 TownsMidiChanState::get(uint8 type) { return 0; } -TownsMidiOutputChannel::TownsMidiOutputChannel(MidiDriver_TOWNS *driver, int chanIndex) : _driver(driver), _chan(chanIndex), +TownsMidiOutputChannel::TownsMidiOutputChannel(IMuseDriver_FMTowns *driver, int number) : _driver(driver), _chan(number), _in(nullptr), _prev(nullptr), _next(nullptr), _adjustModTl(0), _operator2Tl(0), _note(0), _operator1Tl(0), _sustainNoteOff(0), _duration(0), _freq(0), _freqAdjust(0) { _effectEnvelopes = new EffectEnvelope[2](); _effectDefs = new EffectDef[2](); @@ -651,7 +654,7 @@ const uint16 TownsMidiOutputChannel::_freqLSB[] = { 0x055B, 0x055B, 0x055B, 0x055B, 0x055B, 0x055B, 0x055B, 0x055B }; -TownsMidiInputChannel::TownsMidiInputChannel(MidiDriver_TOWNS *driver, int chanIndex) : MidiChannel(), _driver(driver), _out(nullptr), _chanIndex(chanIndex), +TownsMidiInputChannel::TownsMidiInputChannel(IMuseDriver_FMTowns *driver, int16 number) : MidiChannel(), _driver(driver), _number(number), _out(nullptr), _priority(0), _tl(0), _transpose(0), _pitchBendFactor(0), _pitchBend(0), _sustain(0), _freqLSB(0), _detune(0), _modWheel(0), _allocated(false) { _instrument = new uint8[30](); } @@ -672,7 +675,7 @@ void TownsMidiInputChannel::release() { } void TownsMidiInputChannel::send(uint32 b) { - _driver->send(b | _chanIndex); + device()->send(b | _number); } void TownsMidiInputChannel::noteOff(byte note) { @@ -836,7 +839,7 @@ const uint8 TownsMidiInputChannel::_programAdjustLevel[] = { 0x3D, 0x3D, 0x3E, 0x3E, 0x3E, 0x3F, 0x3F, 0x3F }; -MidiDriver_TOWNS::MidiDriver_TOWNS(Audio::Mixer *mixer) : _timerProc(nullptr), _timerProcPara(nullptr), _channels(nullptr), _out(nullptr), +IMuseDriver_FMTowns::IMuseDriver_FMTowns(Audio::Mixer *mixer) : _timerProc(nullptr), _timerProcPara(nullptr), _channels(nullptr), _out(nullptr), _baseTempo(10080), _chanState(nullptr), _operatorLevelTable(nullptr), _tickCounter(0), _rand(1), _allocCurPos(0), _isOpen(false) { _intf = new TownsAudioInterface(mixer, this, true); @@ -859,7 +862,7 @@ MidiDriver_TOWNS::MidiDriver_TOWNS(Audio::Mixer *mixer) : _timerProc(nullptr), _ _operatorLevelTable[i << 5] = 0; } -MidiDriver_TOWNS::~MidiDriver_TOWNS() { +IMuseDriver_FMTowns::~IMuseDriver_FMTowns() { close(); delete _intf; @@ -883,7 +886,7 @@ MidiDriver_TOWNS::~MidiDriver_TOWNS() { _operatorLevelTable = nullptr; } -int MidiDriver_TOWNS::open() { +int IMuseDriver_FMTowns::open() { if (_isOpen) return MERR_ALREADY_OPEN; @@ -906,7 +909,7 @@ int MidiDriver_TOWNS::open() { return 0; } -void MidiDriver_TOWNS::close() { +void IMuseDriver_FMTowns::close() { if (!_isOpen) return; @@ -916,7 +919,7 @@ void MidiDriver_TOWNS::close() { g_system->delayMillis(20); } -void MidiDriver_TOWNS::send(uint32 b) { +void IMuseDriver_FMTowns::send(uint32 b) { if (!_isOpen) return; @@ -954,16 +957,16 @@ void MidiDriver_TOWNS::send(uint32 b) { } } -void MidiDriver_TOWNS::setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc) { +void IMuseDriver_FMTowns::setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc) { _timerProc = timer_proc; _timerProcPara = timer_param; } -uint32 MidiDriver_TOWNS::getBaseTempo() { +uint32 IMuseDriver_FMTowns::getBaseTempo() { return _baseTempo; } -MidiChannel *MidiDriver_TOWNS::allocateChannel() { +MidiChannel *IMuseDriver_FMTowns::allocateChannel() { if (!_isOpen) return nullptr; @@ -976,11 +979,11 @@ MidiChannel *MidiDriver_TOWNS::allocateChannel() { return nullptr; } -MidiChannel *MidiDriver_TOWNS::getPercussionChannel() { +MidiChannel *IMuseDriver_FMTowns::getPercussionChannel() { return nullptr; } -void MidiDriver_TOWNS::timerCallback(int timerId) { +void IMuseDriver_FMTowns::timerCallback(int timerId) { if (!_isOpen) return; @@ -994,12 +997,12 @@ void MidiDriver_TOWNS::timerCallback(int timerId) { } } -void MidiDriver_TOWNS::updateParser() { +void IMuseDriver_FMTowns::updateParser() { if (_timerProc) _timerProc(_timerProcPara); } -void MidiDriver_TOWNS::updateOutputChannels() { +void IMuseDriver_FMTowns::updateOutputChannels() { _tickCounter += _baseTempo; while (_tickCounter >= 16667) { _tickCounter -= 16667; @@ -1010,7 +1013,7 @@ void MidiDriver_TOWNS::updateOutputChannels() { } } -TownsMidiOutputChannel *MidiDriver_TOWNS::allocateOutputChannel(uint8 pri) { +TownsMidiOutputChannel *IMuseDriver_FMTowns::allocateOutputChannel(uint8 pri) { TownsMidiOutputChannel *res = nullptr; for (int i = 0; i < 6; i++) { @@ -1033,7 +1036,9 @@ TownsMidiOutputChannel *MidiDriver_TOWNS::allocateOutputChannel(uint8 pri) { return res; } -int MidiDriver_TOWNS::randomValue(int para) { +int IMuseDriver_FMTowns::randomValue(int para) { _rand = (_rand & 1) ? (_rand >> 1) ^ 0xb8 : (_rand >> 1); return (_rand * para) >> 8; } + +} // end of namespace Scumm diff --git a/engines/scumm/imuse/drivers/fmtowns.h b/engines/scumm/imuse/drivers/fmtowns.h index 5ece0182c91..aa8a8cedd02 100644 --- a/engines/scumm/imuse/drivers/fmtowns.h +++ b/engines/scumm/imuse/drivers/fmtowns.h @@ -25,17 +25,18 @@ #include "audio/softsynth/fmtowns_pc98/towns_audio.h" #include "audio/mididrv.h" +namespace Scumm { class TownsMidiOutputChannel; class TownsMidiInputChannel; class TownsMidiChanState; -class MidiDriver_TOWNS : public MidiDriver, public TownsAudioInterfacePluginDriver { +class IMuseDriver_FMTowns : public MidiDriver, public TownsAudioInterfacePluginDriver { friend class TownsMidiInputChannel; friend class TownsMidiOutputChannel; public: - MidiDriver_TOWNS(Audio::Mixer *mixer); - ~MidiDriver_TOWNS() override; + IMuseDriver_FMTowns(Audio::Mixer *mixer); + ~IMuseDriver_FMTowns() override; int open() override; bool isOpen() const override { return _isOpen; } @@ -79,4 +80,6 @@ private: const uint16 _baseTempo; }; +} // end of namespace Scumm + #endif diff --git a/engines/scumm/imuse/drivers/gmidi.cpp b/engines/scumm/imuse/drivers/gmidi.cpp new file mode 100644 index 00000000000..eaa322cb981 --- /dev/null +++ b/engines/scumm/imuse/drivers/gmidi.cpp @@ -0,0 +1,195 @@ +/* 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 3 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, see . + * + */ + +#include "common/debug.h" +#include "common/system.h" +#include "scumm/imuse/drivers/gmidi.h" + +namespace Scumm { + +IMuseDriver_GMidi::IMuseDriver_GMidi(MidiDriver::DeviceHandle dev, bool rolandGSMode, bool newSystem) : MidiDriver(), _drv(nullptr), _gsMode(rolandGSMode), _newSystem(newSystem) { + _drv = MidiDriver::createMidi(dev); + assert(_drv); +} + +IMuseDriver_GMidi::~IMuseDriver_GMidi() { + delete _drv; +} + +int IMuseDriver_GMidi::open() { + if (!_drv) + return MERR_CANNOT_CONNECT; + + int res = _drv->open(); + + if (_gsMode) + initDeviceAsRolandGS(); + else + initDevice(); + + return res; +} + +MidiChannel *IMuseDriver_GMidi::allocateChannel() { + if (!_newSystem) + return _drv->allocateChannel(); + + return nullptr; +} + +MidiChannel *IMuseDriver_GMidi::getPercussionChannel() { + if (!_newSystem) + return _drv->getPercussionChannel(); + + return nullptr; +} + +void IMuseDriver_GMidi::initDevice() { + // These are the init messages from the DOTT General Midi + // driver. This is the major part of the bug fix for bug + // no. 13460 ("DOTT: Incorrect MIDI pitch bending"). + // SAMNMAX has some less of the default settings (since + // the driver works a bit different), but it uses the same + // values for the pitch bend range. + for (int i = 0; i < 16; ++i) { + send(0x0064B0 | i); + send(0x0065B0 | i); + send(0x1006B0 | i); + send(0x7F07B0 | i); + send(0x3F0AB0 | i); + send(0x0000C0 | i); + send(0x4000E0 | i); + send(0x0001B0 | i); + send(0x0040B0 | i); + send(0x405BB0 | i); + send(0x005DB0 | i); + send(0x0000B0 | i); + send(0x007BB0 | i); + } +} + +void IMuseDriver_GMidi::initDeviceAsRolandGS() { + byte buffer[12]; + int i; + + // General MIDI System On message + // Resets all GM devices to default settings + memcpy(&buffer[0], "\x7E\x7F\x09\x01", 4); + _drv->sysEx(buffer, 4); + debug(2, "GM SysEx: GM System On"); + g_system->delayMillis(200); + + // All GS devices recognize the GS Reset command, + // even using Roland's ID. It is impractical to + // support other manufacturers' devices for + // further GS settings, as there are limitless + // numbers of them out there that would each + // require individual SysEx commands with unique IDs. + + // Roland GS SysEx ID + memcpy(&buffer[0], "\x41\x10\x42\x12", 4); + + // GS Reset + memcpy(&buffer[4], "\x40\x00\x7F\x00\x41", 5); + _drv->sysEx(buffer, 9); + debug(2, "GS SysEx: GS Reset"); + g_system->delayMillis(200); + + // Set global Master Tune to 442.0kHz, as on the MT-32 + memcpy(&buffer[4], "\x40\x00\x00\x00\x04\x04\x0F\x29", 8); + _drv->sysEx(buffer, 12); + debug(2, "GS SysEx: Master Tune set to 442.0kHz"); + + // Note: All Roland GS devices support CM-64/32L maps + + // Set Channels 1-16 to SC-55 Map, then CM-64/32L Variation + for (i = 0; i < 16; ++i) { + _drv->send((127 << 16) | (0 << 8) | (0xB0 | i)); + _drv->send((1 << 16) | (32 << 8) | (0xB0 | i)); + _drv->send((0 << 16) | (0 << 8) | (0xC0 | i)); + } + debug(2, "GS Program Change: CM-64/32L Map Selected"); + + // Set Percussion Channel to SC-55 Map (CC#32, 01H), then + // Switch Drum Map to CM-64/32L (MT-32 Compatible Drums) + getPercussionChannel()->controlChange(0, 0); + getPercussionChannel()->controlChange(32, 1); + _drv->send(127 << 8 | 0xC0 | 9); + debug(2, "GS Program Change: Drum Map is CM-64/32L"); + + // Set Master Chorus to 0. The MT-32 has no chorus capability. + memcpy(&buffer[4], "\x40\x01\x3A\x00\x05", 5); + _drv->sysEx(buffer, 9); + debug(2, "GS SysEx: Master Chorus Level is 0"); + + // Set Channels 1-16 Reverb to 64, which is the + // equivalent of MT-32 default Reverb Level 5 + for (i = 0; i < 16; ++i) + _drv->send((64 << 16) | (91 << 8) | (0xB0 | i)); + debug(2, "GM Controller 91 Change: Channels 1-16 Reverb Level is 64"); + + // Set Channels 1-16 Pitch Bend Sensitivity to + // 12 semitones; then lock the RPN by setting null. + for (i = 0; i < 16; ++i) + _drv->setPitchBendRange(i, 12); + debug(2, "GM Controller 6 Change: Channels 1-16 Pitch Bend Sensitivity is 12 semitones"); + + // Set channels 1-16 Mod. LFO1 Pitch Depth to 4 + memcpy(&buffer[4], "\x40\x20\x04\x04\x18", 5); + for (i = 0; i < 16; ++i) { + buffer[5] = 0x20 + i; + buffer[8] = 0x18 - i; + _drv->sysEx(buffer, 9); + } + + debug(2, "GS SysEx: Channels 1-16 Mod. LFO1 Pitch Depth Level is 4"); + + // Set Percussion Channel Expression to 80 + getPercussionChannel()->controlChange(11, 80); + debug(2, "GM Controller 11 Change: Percussion Channel Expression Level is 80"); + + // Turn off Percussion Channel Rx. Expression so that + // Expression cannot be modified. I don't know why, but + // Roland does it this way. + memcpy(&buffer[4], "\x40\x10\x0E\x00\x22", 5); + _drv->sysEx(buffer, 9); + debug(2, "GS SysEx: Percussion Channel Rx. Expression is OFF"); + + // Change Reverb Character to 0. I don't think this + // sounds most like MT-32, but apparently Roland does. + memcpy(&buffer[4], "\x40\x01\x31\x00\x0E", 5); + _drv->sysEx(buffer, 9); + debug(2, "GS SysEx: Reverb Character is 0"); + + // Change Reverb Pre-LF to 4, which is similar to + // what MT-32 reverb does. + memcpy(&buffer[4], "\x40\x01\x32\x04\x09", 5); + _drv->sysEx(buffer, 9); + debug(2, "GS SysEx: Reverb Pre-LF is 4"); + + // Change Reverb Time to 106; the decay on Hall 2 + // Reverb is too fast compared to the MT-32's + memcpy(&buffer[4], "\x40\x01\x34\x6A\x21", 5); + _drv->sysEx(buffer, 9); + debug(2, "GS SysEx: Reverb Time is 106"); +} + +} // End of namespace Scumm diff --git a/engines/scumm/imuse/drivers/gmidi.h b/engines/scumm/imuse/drivers/gmidi.h new file mode 100644 index 00000000000..311c8d9a45e --- /dev/null +++ b/engines/scumm/imuse/drivers/gmidi.h @@ -0,0 +1,60 @@ +/* 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 3 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, see . + * + */ + +#ifndef SCUMM_IMUSE_DRV_GMIDI_H +#define SCUMM_IMUSE_DRV_GMIDI_H + +#include "audio/mididrv.h" + +namespace Scumm { + +class IMuseDriver_GMidi : public MidiDriver { +public: + IMuseDriver_GMidi(MidiDriver::DeviceHandle dev, bool rolandGSMode, bool newSystem); + ~IMuseDriver_GMidi() override; + + int open() override; + + // Just pass these through... + bool isOpen() const override { return _drv ? _drv->isOpen() : false; } + void close() override { if (_drv) _drv->close(); } + uint32 property(int prop, uint32 param) override { return _drv ? _drv->property(prop, param) : 0; } + void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) override { if (_drv) _drv->setTimerCallback(timerParam, timerProc); } + uint32 getBaseTempo() override { return _drv ? _drv->getBaseTempo() : 0; } + void send(uint32 b) override { if (_drv) _drv->send(b); }; + void sysEx(const byte *msg, uint16 length) override { if (_drv) _drv->sysEx(msg, length); } + + // Channel allocation functions + MidiChannel *allocateChannel() override; + MidiChannel *getPercussionChannel() override; + +private: + void initDevice(); + void initDeviceAsRolandGS(); + + MidiDriver *_drv; + const bool _gsMode; + const bool _newSystem; +}; + +} // End of namespace Scumm + +#endif diff --git a/engines/scumm/imuse/drivers/mac_m68k.cpp b/engines/scumm/imuse/drivers/mac_m68k.cpp index 33e5993ff56..600c07032ce 100644 --- a/engines/scumm/imuse/drivers/mac_m68k.cpp +++ b/engines/scumm/imuse/drivers/mac_m68k.cpp @@ -27,14 +27,19 @@ namespace Scumm { -MacM68kDriver::MacM68kDriver(Audio::Mixer *mixer) - : MidiDriver_Emulated(mixer) { +IMuseDriver_MacM68k::IMuseDriver_MacM68k(Audio::Mixer *mixer) + : MidiDriver_Emulated(mixer), _mixBuffer(nullptr), _mixBufferLength(0), _volumeTable(nullptr), _lastUsedVoiceChannel(0) { + memset(_channels, 0, sizeof(_channels)); + memset(_pitchTable, 0, sizeof(_pitchTable)); + memset(_voiceChannels, 0, sizeof(_voiceChannels)); } -MacM68kDriver::~MacM68kDriver() { +IMuseDriver_MacM68k::~IMuseDriver_MacM68k() { + for (uint i = 0; i < ARRAYSIZE(_channels); ++i) + delete _channels[i]; } -int MacM68kDriver::open() { +int IMuseDriver_MacM68k::open() { if (_isOpen) { return MERR_ALREADY_OPEN; } @@ -45,7 +50,8 @@ int MacM68kDriver::open() { } for (uint i = 0; i < ARRAYSIZE(_channels); ++i) { - _channels[i].init(this, i); + delete _channels[i]; + _channels[i] = new MidiChannel_MacM68k(this, i); } memset(_voiceChannels, 0, sizeof(_voiceChannels)); @@ -88,7 +94,7 @@ int MacM68kDriver::open() { return 0; } -void MacM68kDriver::close() { +void IMuseDriver_MacM68k::close() { if (!_isOpen) { return; } @@ -106,25 +112,21 @@ void MacM68kDriver::close() { _mixBufferLength = 0; } -void MacM68kDriver::send(uint32 d) { +void IMuseDriver_MacM68k::send(uint32 d) { assert(false); } -void MacM68kDriver::sysEx_customInstrument(byte channel, uint32 type, const byte *instr) { - assert(false); -} - -MidiChannel *MacM68kDriver::allocateChannel() { +MidiChannel *IMuseDriver_MacM68k::allocateChannel() { for (uint i = 0; i < ARRAYSIZE(_channels); ++i) { - if (_channels[i].allocate()) { - return &_channels[i]; + if (_channels[i]->allocate()) { + return _channels[i]; } } return nullptr; } -MacM68kDriver::Instrument MacM68kDriver::getInstrument(int idx) const { +IMuseDriver_MacM68k::Instrument IMuseDriver_MacM68k::getInstrument(int idx) const { InstrumentMap::const_iterator i = _instruments.find(idx); if (i != _instruments.end()) { return i->_value; @@ -133,7 +135,7 @@ MacM68kDriver::Instrument MacM68kDriver::getInstrument(int idx) const { } } -void MacM68kDriver::generateSamples(int16 *buf, int len) { +void IMuseDriver_MacM68k::generateSamples(int16 *buf, int len) { int silentChannels = 0; if (_mixBufferLength < len) { @@ -191,7 +193,7 @@ void MacM68kDriver::generateSamples(int16 *buf, int len) { } } -void MacM68kDriver::loadAllInstruments() { +void IMuseDriver_MacM68k::loadAllInstruments() { Common::MacResManager resource; if (resource.open("iMUSE Setups")) { if (!resource.hasResFork()) { @@ -225,7 +227,7 @@ void MacM68kDriver::loadAllInstruments() { } } -void MacM68kDriver::addInstrument(int idx, Common::SeekableReadStream *data) { +void IMuseDriver_MacM68k::addInstrument(int idx, Common::SeekableReadStream *data) { // We parse the "SND" files manually here, since we need special data // from their header and need to work on them raw while mixing. data->skip(2); @@ -251,7 +253,7 @@ void MacM68kDriver::addInstrument(int idx, Common::SeekableReadStream *data) { _instruments[idx] = inst; } -void MacM68kDriver::setPitch(OutputChannel *out, int frequency) { +void IMuseDriver_MacM68k::setPitch(OutputChannel *out, int frequency) { out->frequency = frequency; out->isFinished = false; @@ -266,7 +268,7 @@ void MacM68kDriver::setPitch(OutputChannel *out, int frequency) { } } -void MacM68kDriver::VoiceChannel::off() { +void IMuseDriver_MacM68k::VoiceChannel::off() { if (out.start) { out.isFinished = true; } @@ -275,14 +277,14 @@ void MacM68kDriver::VoiceChannel::off() { part = nullptr; } -void MacM68kDriver::MidiChannel_MacM68k::release() { +void IMuseDriver_MacM68k::MidiChannel_MacM68k::release() { _allocated = false; while (_voice) { _voice->off(); } } -void MacM68kDriver::MidiChannel_MacM68k::send(uint32 b) { +void IMuseDriver_MacM68k::MidiChannel_MacM68k::send(uint32 b) { uint8 type = b & 0xF0; uint8 p1 = (b >> 8) & 0xFF; uint8 p2 = (b >> 16) & 0xFF; @@ -313,7 +315,7 @@ void MacM68kDriver::MidiChannel_MacM68k::send(uint32 b) { } } -void MacM68kDriver::MidiChannel_MacM68k::noteOff(byte note) { +void IMuseDriver_MacM68k::MidiChannel_MacM68k::noteOff(byte note) { for (VoiceChannel *i = _voice; i; i = i->next) { if (i->note == note) { if (_sustain) { @@ -325,7 +327,7 @@ void MacM68kDriver::MidiChannel_MacM68k::noteOff(byte note) { } } -void MacM68kDriver::MidiChannel_MacM68k::noteOn(byte note, byte velocity) { +void IMuseDriver_MacM68k::MidiChannel_MacM68k::noteOn(byte note, byte velocity) { // Do not start a not unless there is an instrument set up if (!_instrument.data) { return; @@ -366,18 +368,18 @@ void MacM68kDriver::MidiChannel_MacM68k::noteOn(byte note, byte velocity) { voice->out.subPos = 0; } -void MacM68kDriver::MidiChannel_MacM68k::programChange(byte program) { +void IMuseDriver_MacM68k::MidiChannel_MacM68k::programChange(byte program) { _instrument = _owner->getInstrument(program + kProgramChangeBase); } -void MacM68kDriver::MidiChannel_MacM68k::pitchBend(int16 bend) { +void IMuseDriver_MacM68k::MidiChannel_MacM68k::pitchBend(int16 bend) { _pitchBend = (bend * _pitchBendFactor) >> 6; for (VoiceChannel *i = _voice; i; i = i->next) { _owner->setPitch(&i->out, (i->note << 7) + _pitchBend); } } -void MacM68kDriver::MidiChannel_MacM68k::controlChange(byte control, byte value) { +void IMuseDriver_MacM68k::MidiChannel_MacM68k::controlChange(byte control, byte value) { switch (control) { // volume change case 7: @@ -412,28 +414,22 @@ void MacM68kDriver::MidiChannel_MacM68k::controlChange(byte control, byte value) } } -void MacM68kDriver::MidiChannel_MacM68k::pitchBendFactor(byte value) { +void IMuseDriver_MacM68k::MidiChannel_MacM68k::pitchBendFactor(byte value) { _pitchBendFactor = value; } -void MacM68kDriver::MidiChannel_MacM68k::priority(byte value) { +void IMuseDriver_MacM68k::MidiChannel_MacM68k::priority(byte value) { _priority = value; } -void MacM68kDriver::MidiChannel_MacM68k::sysEx_customInstrument(uint32 type, const byte *instr) { +void IMuseDriver_MacM68k::MidiChannel_MacM68k::sysEx_customInstrument(uint32 type, const byte *instr) { assert(instr); if (type == 'MAC ') { _instrument = _owner->getInstrument(*instr + kSysExBase); } } -void MacM68kDriver::MidiChannel_MacM68k::init(MacM68kDriver *owner, byte channel) { - _owner = owner; - _number = channel; - _allocated = false; -} - -bool MacM68kDriver::MidiChannel_MacM68k::allocate() { +bool IMuseDriver_MacM68k::MidiChannel_MacM68k::allocate() { if (_allocated) { return false; } @@ -448,7 +444,7 @@ bool MacM68kDriver::MidiChannel_MacM68k::allocate() { return true; } -void MacM68kDriver::MidiChannel_MacM68k::addVoice(VoiceChannel *voice) { +void IMuseDriver_MacM68k::MidiChannel_MacM68k::addVoice(VoiceChannel *voice) { voice->next = _voice; voice->prev = nullptr; voice->part = this; @@ -458,7 +454,7 @@ void MacM68kDriver::MidiChannel_MacM68k::addVoice(VoiceChannel *voice) { _voice = voice; } -void MacM68kDriver::MidiChannel_MacM68k::removeVoice(VoiceChannel *voice) { +void IMuseDriver_MacM68k::MidiChannel_MacM68k::removeVoice(VoiceChannel *voice) { VoiceChannel *i = _voice; while (i && i != voice) { i = i->next; @@ -477,7 +473,7 @@ void MacM68kDriver::MidiChannel_MacM68k::removeVoice(VoiceChannel *voice) { } } -MacM68kDriver::VoiceChannel *MacM68kDriver::allocateVoice(int priority) { +IMuseDriver_MacM68k::VoiceChannel *IMuseDriver_MacM68k::allocateVoice(int priority) { VoiceChannel *channel = nullptr; for (int i = 0; i < kChannelCount; ++i) { if (++_lastUsedVoiceChannel == kChannelCount) { @@ -504,7 +500,7 @@ MacM68kDriver::VoiceChannel *MacM68kDriver::allocateVoice(int priority) { return channel; } -const int MacM68kDriver::_volumeBaseTable[32] = { +const int IMuseDriver_MacM68k::_volumeBaseTable[32] = { 0, 0, 1, 1, 2, 3, 5, 6, 8, 11, 13, 16, 19, 22, 26, 30, 34, 38, 43, 48, 53, 58, 64, 70, diff --git a/engines/scumm/imuse/drivers/mac_m68k.h b/engines/scumm/imuse/drivers/mac_m68k.h index d04e769da5f..cf51c5d90d6 100644 --- a/engines/scumm/imuse/drivers/mac_m68k.h +++ b/engines/scumm/imuse/drivers/mac_m68k.h @@ -32,20 +32,19 @@ class SeekableReadStream; namespace Scumm { -class MacM68kDriver : public MidiDriver_Emulated { +class IMuseDriver_MacM68k : public MidiDriver_Emulated { friend class MidiChannel_MacM68k; public: - MacM68kDriver(Audio::Mixer *mixer); - ~MacM68kDriver() override; + IMuseDriver_MacM68k(Audio::Mixer *mixer); + ~IMuseDriver_MacM68k() override; int open() override; void close() override; void send(uint32 d) override; - void sysEx_customInstrument(byte channel, uint32 type, const byte *instr) override; MidiChannel *allocateChannel() override; - MidiChannel *getPercussionChannel() override { return 0; } + MidiChannel *getPercussionChannel() override { return nullptr; } bool isStereo() const override { return false; } int getRate() const override { @@ -127,8 +126,9 @@ private: }; class MidiChannel_MacM68k : public MidiChannel { - friend class MacM68kDriver; + friend class IMuseDriver_MacM68k; public: + MidiChannel_MacM68k(IMuseDriver_MacM68k *driver, byte number) : MidiChannel(), _owner(driver), _number(number), _allocated(false) {} MidiDriver *device() override { return _owner; } byte getNumber() override { return _number; } void release() override; @@ -143,15 +143,14 @@ private: void priority(byte value) override; void sysEx_customInstrument(uint32 type, const byte *instr) override; - void init(MacM68kDriver *owner, byte channel); bool allocate(); void addVoice(VoiceChannel *voice); void removeVoice(VoiceChannel *voice); private: - MacM68kDriver *_owner; + IMuseDriver_MacM68k *_owner; + const byte _number; bool _allocated; - int _number; VoiceChannel *_voice; int _priority; @@ -162,7 +161,7 @@ private: int _volume; }; - MidiChannel_MacM68k _channels[32]; + MidiChannel_MacM68k *_channels[32]; enum { kChannelCount = 8 diff --git a/engines/scumm/imuse/drivers/mt32.cpp b/engines/scumm/imuse/drivers/mt32.cpp new file mode 100644 index 00000000000..e65001834ac --- /dev/null +++ b/engines/scumm/imuse/drivers/mt32.cpp @@ -0,0 +1,103 @@ +/* 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 3 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, see . + * + */ + +#include "common/str.h" +#include "common/system.h" +#include "base/version.h" +#include "scumm/imuse/drivers/mt32.h" + +namespace Scumm { + +IMuseDriver_MT32::IMuseDriver_MT32(MidiDriver::DeviceHandle dev, bool newSystem) : MidiDriver(), _newSystem(newSystem), _drv(nullptr) { + _drv = MidiDriver::createMidi(dev); + assert(_drv); + + if (!_newSystem) + _drv->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE); +} + +IMuseDriver_MT32::~IMuseDriver_MT32() { + delete _drv; +} + +int IMuseDriver_MT32::open() { + if (!_drv) + return MERR_CANNOT_CONNECT; + + int res = _drv->open(); + + initDevice(); + + return res; +} + +MidiChannel *IMuseDriver_MT32::allocateChannel() { + if (!_newSystem) + return _drv->allocateChannel(); + + return nullptr; +} + +MidiChannel *IMuseDriver_MT32::getPercussionChannel() { + if (!_newSystem) + return _drv->getPercussionChannel(); + + return nullptr; +} + +void IMuseDriver_MT32::initDevice() { + byte buffer[52]; + + // Reset the MT-32 + _drv->sysEx((const byte *) "\x41\x10\x16\x12\x7f\x00\x00\x01\x00", 9); + g_system->delayMillis(250); + + // Setup master tune, reverb mode, reverb time, reverb level, + // channel mapping, partial reserve and master volume + _drv->sysEx((const byte *) "\x41\x10\x16\x12\x10\x00\x00\x40\x00\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x64\x77", 31); + g_system->delayMillis(250); + + // Map percussion to notes 24 - 34 without reverb + if (!_newSystem) { + _drv->sysEx((const byte *) "\x41\x10\x16\x12\x03\x01\x10\x40\x64\x07\x00\x4a\x64\x06\x00\x41\x64\x07\x00\x4b\x64\x08\x00\x45\x64\x06\x00\x44\x64\x0b\x00\x51\x64\x05\x00\x43\x64\x08\x00\x50\x64\x07\x00\x42\x64\x03\x00\x4c\x64\x07\x00\x44", 52); + g_system->delayMillis(250); + } + + // Compute version string (truncated to 20 chars max.) + Common::String infoStr = "ScummVM "; + infoStr += gScummVMVersion; + int len = infoStr.size(); + if (len > 20) + len = 20; + + // Display a welcome message on MT-32 displays. + memcpy(&buffer[0], "\x41\x10\x16\x12\x20\x00\x00", 7); + memcpy(&buffer[7], " ", 20); + memcpy(buffer + 7 + (20 - len) / 2, infoStr.c_str(), len); + byte checksum = 0; + for (int i = 4; i < 27; ++i) + checksum -= buffer[i]; + buffer[27] = checksum & 0x7F; + _drv->sysEx(buffer, 28); + g_system->delayMillis(1000); +} + +} // End of namespace Scumm diff --git a/engines/scumm/imuse/drivers/mt32.h b/engines/scumm/imuse/drivers/mt32.h new file mode 100644 index 00000000000..9f6cf7b2687 --- /dev/null +++ b/engines/scumm/imuse/drivers/mt32.h @@ -0,0 +1,59 @@ +/* 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 3 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, see . + * + */ + +#ifndef SCUMM_IMUSE_DRV_MT32_H +#define SCUMM_IMUSE_DRV_MT32_H + +#include "audio/mididrv.h" + +namespace Scumm { + +class IMuseDriver_MT32 : public MidiDriver { +public: + IMuseDriver_MT32(MidiDriver::DeviceHandle dev, bool newSystem); + ~IMuseDriver_MT32() override; + + int open() override; + + // Just pass these through... + bool isOpen() const override { return _drv ? _drv->isOpen() : false; } + void close() override { if (_drv) _drv->close(); } + uint32 property(int prop, uint32 param) override { return _drv ? _drv->property(prop, param) : 0; } + void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) override { if (_drv) _drv->setTimerCallback(timerParam, timerProc); } + uint32 getBaseTempo() override { return _drv ? _drv->getBaseTempo() : 0; } + void send(uint32 b) override { if (_drv) _drv->send(b); }; + void sysEx(const byte *msg, uint16 length) override { if (_drv) _drv->sysEx(msg, length); } + void setPitchBendRange(byte channel, uint range) override { if (_drv) _drv->setPitchBendRange(channel, range); } + + // Channel allocation functions + MidiChannel *allocateChannel() override; + MidiChannel *getPercussionChannel() override; + +private: + void initDevice(); + + MidiDriver *_drv; + const bool _newSystem; +}; + +} // End of namespace Scumm + +#endif diff --git a/engines/scumm/imuse/drivers/pcspk.cpp b/engines/scumm/imuse/drivers/pcspk.cpp index 4ccfa8e0c3f..546d674321a 100644 --- a/engines/scumm/imuse/drivers/pcspk.cpp +++ b/engines/scumm/imuse/drivers/pcspk.cpp @@ -25,22 +25,26 @@ namespace Scumm { -PcSpkDriver::PcSpkDriver(Audio::Mixer *mixer) - : MidiDriver_Emulated(mixer), _pcSpk(mixer->getOutputRate()) { +IMuseDriver_PCSpk::IMuseDriver_PCSpk(Audio::Mixer *mixer) + : MidiDriver_Emulated(mixer), _pcSpk(mixer->getOutputRate()) , _activeChannel(nullptr), _lastActiveChannel(nullptr), _lastActiveOut(0), _effectTimer(0), _randBase(1) { + memset(_channels, 0, sizeof(_channels)); } -PcSpkDriver::~PcSpkDriver() { +IMuseDriver_PCSpk::~IMuseDriver_PCSpk() { close(); } -int PcSpkDriver::open() { +int IMuseDriver_PCSpk::open() { if (_isOpen) return MERR_ALREADY_OPEN; MidiDriver_Emulated::open(); - for (uint i = 0; i < 6; ++i) - _channels[i].init(this, i); + for (uint i = 0; i < 6; ++i) { + delete _channels[i]; + _channels[i] = new MidiChannel_PcSpk(this, i); + } + _activeChannel = nullptr; _effectTimer = 0; _randBase = 1; @@ -59,44 +63,41 @@ int PcSpkDriver::open() { return 0; } -void PcSpkDriver::close() { +void IMuseDriver_PCSpk::close() { if (!_isOpen) return; _isOpen = false; _mixer->stopHandle(_mixerSoundHandle); + + for (uint i = 0; i < 6; ++i) + delete _channels[i]; } -void PcSpkDriver::send(uint32 d) { +void IMuseDriver_PCSpk::send(uint32 d) { assert((d & 0x0F) < 6); - _channels[(d & 0x0F)].send(d); + _channels[(d & 0x0F)]->send(d); } -void PcSpkDriver::sysEx_customInstrument(byte channel, uint32 type, const byte *instr) { - assert(channel < 6); - if (type == 'SPK ') - _channels[channel].sysEx_customInstrument(type, instr); -} - -MidiChannel *PcSpkDriver::allocateChannel() { +MidiChannel *IMuseDriver_PCSpk::allocateChannel() { for (uint i = 0; i < 6; ++i) { - if (_channels[i].allocate()) - return &_channels[i]; + if (_channels[i]->allocate()) + return _channels[i]; } return nullptr; } -void PcSpkDriver::generateSamples(int16 *buf, int len) { +void IMuseDriver_PCSpk::generateSamples(int16 *buf, int len) { _pcSpk.readBuffer(buf, len); } -void PcSpkDriver::onTimer() { +void IMuseDriver_PCSpk::onTimer() { if (!_activeChannel) return; for (uint i = 0; i < 6; ++i) { - OutputChannel &out = _channels[i]._out; + OutputChannel &out = _channels[i]->_out; if (!out.active) continue; @@ -113,9 +114,9 @@ void PcSpkDriver::onTimer() { _effectTimer = 0; if (out.effectEnvelopeA.state) - updateEffectGenerator(_channels[i], out.effectEnvelopeA, out.effectDefA); + updateEffectGenerator(*_channels[i], out.effectEnvelopeA, out.effectDefA); if (out.effectEnvelopeB.state) - updateEffectGenerator(_channels[i], out.effectEnvelopeB, out.effectDefB); + updateEffectGenerator(*_channels[i], out.effectEnvelopeB, out.effectDefB); } } else { out.active = 0; @@ -133,13 +134,13 @@ void PcSpkDriver::onTimer() { } } -void PcSpkDriver::updateNote() { +void IMuseDriver_PCSpk::updateNote() { uint8 priority = 0; _activeChannel = nullptr; for (uint i = 0; i < 6; ++i) { - if (_channels[i]._allocated && _channels[i]._out.active && _channels[i]._priority >= priority) { - priority = _channels[i]._priority; - _activeChannel = &_channels[i]; + if (_channels[i]->_allocated && _channels[i]->_out.active && _channels[i]->_priority >= priority) { + priority = _channels[i]->_priority; + _activeChannel = _channels[i]; } } @@ -152,7 +153,7 @@ void PcSpkDriver::updateNote() { } } -void PcSpkDriver::output(uint16 out) { +void IMuseDriver_PCSpk::output(uint16 out) { byte v1 = (out >> 7) & 0xFF; byte v2 = (out >> 2) & 0x1E; @@ -170,14 +171,11 @@ void PcSpkDriver::output(uint16 out) { } } -void PcSpkDriver::MidiChannel_PcSpk::init(PcSpkDriver *owner, byte channel) { - _owner = owner; - _channel = channel; - _allocated = false; +IMuseDriver_PCSpk::MidiChannel_PcSpk::MidiChannel_PcSpk(IMuseDriver_PCSpk *owner, byte number) : MidiChannel(), _owner(owner), _number(number), _allocated(false) { memset(&_out, 0, sizeof(_out)); } -bool PcSpkDriver::MidiChannel_PcSpk::allocate() { +bool IMuseDriver_PCSpk::MidiChannel_PcSpk::allocate() { if (_allocated) return false; @@ -190,21 +188,13 @@ bool PcSpkDriver::MidiChannel_PcSpk::allocate() { return true; } -MidiDriver *PcSpkDriver::MidiChannel_PcSpk::device() { - return _owner; -} - -byte PcSpkDriver::MidiChannel_PcSpk::getNumber() { - return _channel; -} - -void PcSpkDriver::MidiChannel_PcSpk::release() { +void IMuseDriver_PCSpk::MidiChannel_PcSpk::release() { _out.active = 0; _allocated = false; _owner->updateNote(); } -void PcSpkDriver::MidiChannel_PcSpk::send(uint32 b) { +void IMuseDriver_PCSpk::MidiChannel_PcSpk::send(uint32 b) { uint8 type = b & 0xF0; uint8 p1 = (b >> 8) & 0xFF; uint8 p2 = (b >> 16) & 0xFF; @@ -234,7 +224,7 @@ void PcSpkDriver::MidiChannel_PcSpk::send(uint32 b) { } } -void PcSpkDriver::MidiChannel_PcSpk::noteOff(byte note) { +void IMuseDriver_PCSpk::MidiChannel_PcSpk::noteOff(byte note) { if (!_allocated) return; @@ -249,7 +239,7 @@ void PcSpkDriver::MidiChannel_PcSpk::noteOff(byte note) { } } -void PcSpkDriver::MidiChannel_PcSpk::noteOn(byte note, byte velocity) { +void IMuseDriver_PCSpk::MidiChannel_PcSpk::noteOn(byte note, byte velocity) { if (!_allocated) return; @@ -257,7 +247,7 @@ void PcSpkDriver::MidiChannel_PcSpk::noteOn(byte note, byte velocity) { _out.sustainNoteOff = 0; _out.length = _instrument[0]; - if (_instrument[4] * 256 < ARRAYSIZE(PcSpkDriver::_outInstrumentData)) + if (_instrument[4] * 256 < ARRAYSIZE(IMuseDriver_PCSpk::_outInstrumentData)) _out.instrument = _owner->_outInstrumentData + _instrument[4] * 256; else _out.instrument = nullptr; @@ -278,7 +268,7 @@ void PcSpkDriver::MidiChannel_PcSpk::noteOn(byte note, byte velocity) { } _owner->updateNote(); - _out.unkC += PcSpkDriver::getEffectModifier(_instrument[3] + ((velocity & 0xFE) << 4)); + _out.unkC += IMuseDriver_PCSpk::getEffectModifier(_instrument[3] + ((velocity & 0xFE) << 4)); if (_out.unkC > 63) _out.unkC = 63; @@ -289,16 +279,16 @@ void PcSpkDriver::MidiChannel_PcSpk::noteOn(byte note, byte velocity) { _owner->setupEffects(*this, _out.effectEnvelopeB, _out.effectDefB, _instrument[14], _instrument + 15); } -void PcSpkDriver::MidiChannel_PcSpk::programChange(byte program) { +void IMuseDriver_PCSpk::MidiChannel_PcSpk::programChange(byte program) { // Nothing to implement here, the iMuse code takes care of passing us the // instrument data. } -void PcSpkDriver::MidiChannel_PcSpk::pitchBend(int16 bend) { +void IMuseDriver_PCSpk::MidiChannel_PcSpk::pitchBend(int16 bend) { _pitchBend = (bend * _pitchBendFactor) >> 6; } -void PcSpkDriver::MidiChannel_PcSpk::controlChange(byte control, byte value) { +void IMuseDriver_PCSpk::MidiChannel_PcSpk::controlChange(byte control, byte value) { switch (control) { case 1: if (_out.effectEnvelopeA.state && _out.effectDefA.useModWheel) @@ -338,19 +328,19 @@ void PcSpkDriver::MidiChannel_PcSpk::controlChange(byte control, byte value) { } } -void PcSpkDriver::MidiChannel_PcSpk::pitchBendFactor(byte value) { +void IMuseDriver_PCSpk::MidiChannel_PcSpk::pitchBendFactor(byte value) { _pitchBendFactor = value; } -void PcSpkDriver::MidiChannel_PcSpk::priority(byte value) { +void IMuseDriver_PCSpk::MidiChannel_PcSpk::priority(byte value) { _priority = value; } -void PcSpkDriver::MidiChannel_PcSpk::sysEx_customInstrument(uint32 type, const byte *instr) { +void IMuseDriver_PCSpk::MidiChannel_PcSpk::sysEx_customInstrument(uint32 type, const byte *instr) { memcpy(_instrument, instr, sizeof(_instrument)); } -uint8 PcSpkDriver::getEffectModifier(uint16 level) { +uint8 IMuseDriver_PCSpk::getEffectModifier(uint16 level) { uint8 base = level / 32; uint8 index = level % 32; @@ -360,7 +350,7 @@ uint8 PcSpkDriver::getEffectModifier(uint16 level) { return (base * (index + 1)) >> 5; } -int16 PcSpkDriver::getEffectModLevel(int16 level, int8 mod) { +int16 IMuseDriver_PCSpk::getEffectModLevel(int16 level, int8 mod) { if (!mod) { return 0; } else if (mod == 31) { @@ -380,7 +370,7 @@ int16 PcSpkDriver::getEffectModLevel(int16 level, int8 mod) { } } -int16 PcSpkDriver::getRandScale(int16 input) { +int16 IMuseDriver_PCSpk::getRandScale(int16 input) { if (_randBase & 1) _randBase = (_randBase >> 1) ^ 0xB8; else @@ -389,7 +379,7 @@ int16 PcSpkDriver::getRandScale(int16 input) { return (_randBase * input) >> 8; } -void PcSpkDriver::setupEffects(MidiChannel_PcSpk &chan, EffectEnvelope &env, EffectDefinition &def, byte flags, const byte *data) { +void IMuseDriver_PCSpk::setupEffects(MidiChannel_PcSpk &chan, EffectEnvelope &env, EffectDefinition &def, byte flags, const byte *data) { def.phase = 0; def.useModWheel = flags & 0x40; env.loop = flags & 0x20; @@ -446,7 +436,7 @@ void PcSpkDriver::setupEffects(MidiChannel_PcSpk &chan, EffectEnvelope &env, Eff startEffect(env, data); } -void PcSpkDriver::startEffect(EffectEnvelope &env, const byte *data) { +void IMuseDriver_PCSpk::startEffect(EffectEnvelope &env, const byte *data) { env.state = 1; env.currentLevel = 0; env.modWheelLast = 31; @@ -465,7 +455,7 @@ void PcSpkDriver::startEffect(EffectEnvelope &env, const byte *data) { initNextEnvelopeState(env); } -void PcSpkDriver::initNextEnvelopeState(EffectEnvelope &env) { +void IMuseDriver_PCSpk::initNextEnvelopeState(EffectEnvelope &env) { uint8 lastState = env.state - 1; uint16 stepCount = _effectEnvStepTable[getEffectModifier(((env.stateTargetLevels[lastState] & 0x7F) << 5) + env.modWheelSensitivity)]; @@ -501,7 +491,7 @@ void PcSpkDriver::initNextEnvelopeState(EffectEnvelope &env) { env.changeCountRem = 0; } -void PcSpkDriver::updateEffectGenerator(MidiChannel_PcSpk &chan, EffectEnvelope &env, EffectDefinition &def) { +void IMuseDriver_PCSpk::updateEffectGenerator(MidiChannel_PcSpk &chan, EffectEnvelope &env, EffectDefinition &def) { if (advanceEffectEnvelope(env, def) & 1) { switch (def.type) { case 0: case 1: @@ -537,7 +527,7 @@ void PcSpkDriver::updateEffectGenerator(MidiChannel_PcSpk &chan, EffectEnvelope } } -uint8 PcSpkDriver::advanceEffectEnvelope(EffectEnvelope &env, EffectDefinition &def) { +uint8 IMuseDriver_PCSpk::advanceEffectEnvelope(EffectEnvelope &env, EffectDefinition &def) { if (env.duration != 0) { env.duration -= 17; if (env.duration <= 0) { @@ -584,7 +574,7 @@ uint8 PcSpkDriver::advanceEffectEnvelope(EffectEnvelope &env, EffectDefinition & return changedFlags; } -const byte PcSpkDriver::_outInstrumentData[1024] = { +const byte IMuseDriver_PCSpk::_outInstrumentData[1024] = { 0x00, 0x03, 0x06, 0x09, 0x0C, 0x0F, 0x12, 0x15, 0x18, 0x1B, 0x1E, 0x21, 0x24, 0x27, 0x2A, 0x2D, 0x30, 0x33, 0x36, 0x39, 0x3B, 0x3E, 0x41, 0x43, @@ -715,7 +705,7 @@ const byte PcSpkDriver::_outInstrumentData[1024] = { 0xD4, 0xF7, 0x4A, 0x4A, 0xD0, 0x57, 0x68, 0x76 }; -const byte PcSpkDriver::_outputTable1[] = { +const byte IMuseDriver_PCSpk::_outputTable1[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -734,7 +724,7 @@ const byte PcSpkDriver::_outputTable1[] = { 7, 7, 7, 7, 7, 7, 7, 7 }; -const byte PcSpkDriver::_outputTable2[] = { +const byte IMuseDriver_PCSpk::_outputTable2[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, @@ -769,7 +759,7 @@ const byte PcSpkDriver::_outputTable2[] = { 4, 5, 6, 7 }; -const uint16 PcSpkDriver::_effectEnvStepTable[] = { +const uint16 IMuseDriver_PCSpk::_effectEnvStepTable[] = { 1, 2, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, @@ -780,7 +770,7 @@ const uint16 PcSpkDriver::_effectEnvStepTable[] = { 600, 860, 1200, 1600 }; -const uint16 PcSpkDriver::_frequencyTable[] = { +const uint16 IMuseDriver_PCSpk::_frequencyTable[] = { 0x8E84, 0x8E00, 0x8D7D, 0x8CFA, 0x8C78, 0x8BF7, 0x8B76, 0x8AF5, 0x8A75, 0x89F5, 0x8976, 0x88F7, diff --git a/engines/scumm/imuse/drivers/pcspk.h b/engines/scumm/imuse/drivers/pcspk.h index 8d4c23521d6..bad9f1269ff 100644 --- a/engines/scumm/imuse/drivers/pcspk.h +++ b/engines/scumm/imuse/drivers/pcspk.h @@ -27,19 +27,18 @@ namespace Scumm { -class PcSpkDriver : public MidiDriver_Emulated { +class IMuseDriver_PCSpk : public MidiDriver_Emulated { public: - PcSpkDriver(Audio::Mixer *mixer); - ~PcSpkDriver() override; + IMuseDriver_PCSpk(Audio::Mixer *mixer); + ~IMuseDriver_PCSpk() override; int open() override; void close() override; void send(uint32 d) override; - void sysEx_customInstrument(byte channel, uint32 type, const byte *instr) override; MidiChannel *allocateChannel() override; - MidiChannel *getPercussionChannel() override { return 0; } + MidiChannel *getPercussionChannel() override { return nullptr; } bool isStereo() const override { return _pcSpk.isStereo(); } int getRate() const override { return _pcSpk.getRate(); } @@ -103,9 +102,11 @@ private: int16 unk60; }; - struct MidiChannel_PcSpk : public MidiChannel { - MidiDriver *device() override; - byte getNumber() override; + class MidiChannel_PcSpk: public MidiChannel { + public: + MidiChannel_PcSpk(IMuseDriver_PCSpk *owner, byte number); + MidiDriver *device() override { return _owner; } + byte getNumber() override { return _number; } void release() override; void send(uint32 b) override; @@ -118,22 +119,23 @@ private: void priority(byte value) override; void sysEx_customInstrument(uint32 type, const byte *instr) override; - void init(PcSpkDriver *owner, byte channel); bool allocate(); - PcSpkDriver *_owner; bool _allocated; - byte _channel; - OutputChannel _out; uint8 _instrument[23]; - uint8 _programNr; uint8 _priority; uint8 _tl; uint8 _modWheel; + int16 _pitchBend; + + private: + IMuseDriver_PCSpk *_owner; + const byte _number; + uint8 _programNr; uint8 _sustain; uint8 _pitchBendFactor; - int16 _pitchBend; + }; void setupEffects(MidiChannel_PcSpk &chan, EffectEnvelope &env, EffectDefinition &def, byte flags, const byte *data); @@ -142,7 +144,7 @@ private: void updateEffectGenerator(MidiChannel_PcSpk &chan, EffectEnvelope &env, EffectDefinition &def); uint8 advanceEffectEnvelope(EffectEnvelope &env, EffectDefinition &def); - MidiChannel_PcSpk _channels[6]; + MidiChannel_PcSpk *_channels[6]; MidiChannel_PcSpk *_activeChannel; MidiChannel_PcSpk *_lastActiveChannel; diff --git a/engines/scumm/imuse/imuse.cpp b/engines/scumm/imuse/imuse.cpp index 93699c49311..8b071e2f838 100644 --- a/engines/scumm/imuse/imuse.cpp +++ b/engines/scumm/imuse/imuse.cpp @@ -1437,17 +1437,6 @@ int IMuseInternal::initialize(OSystem *syst, MidiDriver *native_midi, MidiDriver init_queue(); init_parts(); - if (_midi_native && _soundType != MDT_MACINTOSH && _soundType != MDT_AMIGA) { - if (_native_mt32 && !_enable_gs) { - initMT32(_midi_native); - } else if (_enable_gs) { - initGS(_midi_native); - } else if (!_native_mt32) { - // If GS is disabled we do the "normal" init from the original GM drivers. - initGM(); - } - } - _initialized = true; return 0; @@ -1463,174 +1452,6 @@ void IMuseInternal::initMidiDriver(TimerCallbackInfo *info) { info->driver->setTimerCallback(info, &IMuseInternal::midiTimerCallback); } -void IMuseInternal::initMT32(MidiDriver *midi) { - byte buffer[52]; - - // Reset the MT-32 - midi->sysEx((const byte *) "\x41\x10\x16\x12\x7f\x00\x00\x01\x00", 9); - _system->delayMillis(250); - - // Setup master tune, reverb mode, reverb time, reverb level, - // channel mapping, partial reserve and master volume - midi->sysEx((const byte *) "\x41\x10\x16\x12\x10\x00\x00\x40\x00\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x64\x77", 31); - _system->delayMillis(250); - - // Map percussion to notes 24 - 34 without reverb - midi->sysEx((const byte *) "\x41\x10\x16\x12\x03\x01\x10\x40\x64\x07\x00\x4a\x64\x06\x00\x41\x64\x07\x00\x4b\x64\x08\x00\x45\x64\x06\x00\x44\x64\x0b\x00\x51\x64\x05\x00\x43\x64\x08\x00\x50\x64\x07\x00\x42\x64\x03\x00\x4c\x64\x07\x00\x44", 52); - _system->delayMillis(250); - - // Compute version string (truncated to 20 chars max.) - Common::String infoStr = "ScummVM "; - infoStr += gScummVMVersion; - int len = infoStr.size(); - if (len > 20) - len = 20; - - // Display a welcome message on MT-32 displays. - memcpy(&buffer[0], "\x41\x10\x16\x12\x20\x00\x00", 7); - memcpy(&buffer[7], " ", 20); - memcpy(buffer + 7 + (20 - len) / 2, infoStr.c_str(), len); - byte checksum = 0; - for (int i = 4; i < 27; ++i) - checksum -= buffer[i]; - buffer[27] = checksum & 0x7F; - midi->sysEx(buffer, 28); - _system->delayMillis(1000); -} - -void IMuseInternal::initGS(MidiDriver *midi) { - byte buffer[12]; - int i; - - // General MIDI System On message - // Resets all GM devices to default settings - memcpy(&buffer[0], "\x7E\x7F\x09\x01", 4); - midi->sysEx(buffer, 4); - debug(2, "GM SysEx: GM System On"); - _system->delayMillis(200); - - if (_enable_gs) { - // All GS devices recognize the GS Reset command, - // even using Roland's ID. It is impractical to - // support other manufacturers' devices for - // further GS settings, as there are limitless - // numbers of them out there that would each - // require individual SysEx commands with unique IDs. - - // Roland GS SysEx ID - memcpy(&buffer[0], "\x41\x10\x42\x12", 4); - - // GS Reset - memcpy(&buffer[4], "\x40\x00\x7F\x00\x41", 5); - midi->sysEx(buffer, 9); - debug(2, "GS SysEx: GS Reset"); - _system->delayMillis(200); - - // Set global Master Tune to 442.0kHz, as on the MT-32 - memcpy(&buffer[4], "\x40\x00\x00\x00\x04\x04\x0F\x29", 8); - midi->sysEx(buffer, 12); - debug(2, "GS SysEx: Master Tune set to 442.0kHz"); - - // Note: All Roland GS devices support CM-64/32L maps - - // Set Channels 1-16 to SC-55 Map, then CM-64/32L Variation - for (i = 0; i < 16; ++i) { - midi->send((127 << 16) | (0 << 8) | (0xB0 | i)); - midi->send((1 << 16) | (32 << 8) | (0xB0 | i)); - midi->send((0 << 16) | (0 << 8) | (0xC0 | i)); - } - debug(2, "GS Program Change: CM-64/32L Map Selected"); - - // Set Percussion Channel to SC-55 Map (CC#32, 01H), then - // Switch Drum Map to CM-64/32L (MT-32 Compatible Drums) - midi->getPercussionChannel()->controlChange(0, 0); - midi->getPercussionChannel()->controlChange(32, 1); - midi->send(127 << 8 | 0xC0 | 9); - debug(2, "GS Program Change: Drum Map is CM-64/32L"); - - // Set Master Chorus to 0. The MT-32 has no chorus capability. - memcpy(&buffer[4], "\x40\x01\x3A\x00\x05", 5); - midi->sysEx(buffer, 9); - debug(2, "GS SysEx: Master Chorus Level is 0"); - - // Set Channels 1-16 Reverb to 64, which is the - // equivalent of MT-32 default Reverb Level 5 - for (i = 0; i < 16; ++i) - midi->send((64 << 16) | (91 << 8) | (0xB0 | i)); - debug(2, "GM Controller 91 Change: Channels 1-16 Reverb Level is 64"); - - // Set Channels 1-16 Pitch Bend Sensitivity to - // 12 semitones; then lock the RPN by setting null. - for (i = 0; i < 16; ++i) { - midi->setPitchBendRange(i, 12); - } - debug(2, "GM Controller 6 Change: Channels 1-16 Pitch Bend Sensitivity is 12 semitones"); - - // Set channels 1-16 Mod. LFO1 Pitch Depth to 4 - memcpy(&buffer[4], "\x40\x20\x04\x04\x18", 5); - for (i = 0; i < 16; ++i) { - buffer[5] = 0x20 + i; - buffer[8] = 0x18 - i; - midi->sysEx(buffer, 9); - } - debug(2, "GS SysEx: Channels 1-16 Mod. LFO1 Pitch Depth Level is 4"); - - // Set Percussion Channel Expression to 80 - midi->getPercussionChannel()->controlChange(11, 80); - debug(2, "GM Controller 11 Change: Percussion Channel Expression Level is 80"); - - // Turn off Percussion Channel Rx. Expression so that - // Expression cannot be modified. I don't know why, but - // Roland does it this way. - memcpy(&buffer[4], "\x40\x10\x0E\x00\x22", 5); - midi->sysEx(buffer, 9); - debug(2, "GS SysEx: Percussion Channel Rx. Expression is OFF"); - - // Change Reverb Character to 0. I don't think this - // sounds most like MT-32, but apparently Roland does. - memcpy(&buffer[4], "\x40\x01\x31\x00\x0E", 5); - midi->sysEx(buffer, 9); - debug(2, "GS SysEx: Reverb Character is 0"); - - // Change Reverb Pre-LF to 4, which is similar to - // what MT-32 reverb does. - memcpy(&buffer[4], "\x40\x01\x32\x04\x09", 5); - midi->sysEx(buffer, 9); - debug(2, "GS SysEx: Reverb Pre-LF is 4"); - - // Change Reverb Time to 106; the decay on Hall 2 - // Reverb is too fast compared to the MT-32's - memcpy(&buffer[4], "\x40\x01\x34\x6A\x21", 5); - midi->sysEx(buffer, 9); - debug(2, "GS SysEx: Reverb Time is 106"); - } -} - -void IMuseInternal::initGM() { - // These are the init messages from the DOTT General Midi - // driver. This is the major part of the bug fix for bug - // no. 13460 ("DOTT: Incorrect MIDI pitch bending"). - // SAMNMAX has some less of the default settings (since - // the driver works a bit different), but it uses the same - // value for the pitch bend range. - MidiDriver *m = _midi_native; - for (int i = 0; i < 16; ++i) { - m->send(0x0064B0 | i); - m->send(0x0065B0 | i); - m->send(0x1006B0 | i); - m->send(0x7F07B0 | i); - m->send(0x3F0AB0 | i); - m->send(0x0000C0 | i); - m->send(0x4000E0 | i); - m->send(0x0001B0 | i); - m->send(0x0040B0 | i); - m->send(0x405BB0 | i); - m->send(0x005DB0 | i); - m->send(0x0000B0 | i); - m->send(0x007BB0 | i); - } -} - void IMuseInternal::init_queue() { _queue_adding = false; _queue_pos = 0; diff --git a/engines/scumm/imuse/imuse_internal.h b/engines/scumm/imuse/imuse_internal.h index c7d883b83c3..7539eb55423 100644 --- a/engines/scumm/imuse/imuse_internal.h +++ b/engines/scumm/imuse/imuse_internal.h @@ -367,6 +367,7 @@ struct Part : public Common::Serializable { void set_pri(int8 pri); void set_pan(int8 pan); + void set_sm17(int8 val); void set_onoff(bool on); void fix_after_load(); @@ -481,9 +482,6 @@ protected: void handle_marker(uint id, byte data); int get_channel_volume(uint a); void initMidiDriver(TimerCallbackInfo *info); - void initGS(MidiDriver *midi); - void initMT32(MidiDriver *midi); - void initGM(); void init_players(); void init_parts(); void init_queue(); diff --git a/engines/scumm/imuse/imuse_part.cpp b/engines/scumm/imuse/imuse_part.cpp index 46e69f1c492..28f3e345e80 100644 --- a/engines/scumm/imuse/imuse_part.cpp +++ b/engines/scumm/imuse/imuse_part.cpp @@ -24,8 +24,8 @@ #include "common/debug.h" #include "common/textconsole.h" #include "common/util.h" -#include "scumm/imuse/imuse_internal.h" #include "scumm/scumm.h" +#include "scumm/imuse/imuse_internal.h" namespace Scumm { @@ -146,6 +146,10 @@ void Part::set_pan(int8 pan) { sendPanPosition(_pan_eff + 0x40); } +void Part::set_sm17(int8 val) { + +} + void Part::set_transpose(int8 transpose, int8 clipRangeLow, int8 clipRangeHi) { if (_se->_game_id == GID_TENTACLE && (transpose > 24 || transpose < -24)) return; diff --git a/engines/scumm/imuse/imuse_player.cpp b/engines/scumm/imuse/imuse_player.cpp index 73a4c75dbbf..4837f960c76 100644 --- a/engines/scumm/imuse/imuse_player.cpp +++ b/engines/scumm/imuse/imuse_player.cpp @@ -223,7 +223,7 @@ int Player::start_seq_sound(int sound, bool reset_vars) { } void Player::loadStartParameters(int sound) { - _priority = 0x80; + _priority = (_se->_game_id != GID_SAMNMAX) ? 0x80 : 0; _volume = 0x7F; _vol_chan = 0xFFFF; _vol_eff = (_se->get_channel_volume(0xFFFF) << 7) >> 7; @@ -319,10 +319,15 @@ void Player::send(uint32 b) { part->pitchBendFactor(param2); break; case 17: // GP Slider 2 - part->set_detune(param2 - 0x40); + if (_se->_game_id == GID_SAMNMAX) + part->set_sm17(param2); + else + part->set_detune(param2 - 0x40); break; case 18: // GP Slider 3 - part->set_pri(param2 - 0x40); + if (_se->_game_id != GID_SAMNMAX) + param2 -= 0x40; + part->set_pri(param2); _se->reallocateMidiChannels(_midi); break; case 64: // Sustain Pedal @@ -401,9 +406,6 @@ void Player::sysEx(const byte *p, uint16 len) { part->_instrument.send(part->_mc); } } - } else if (a == YM2612_SYSEX_ID) { - // FM-TOWNS custom instrument definition - _midi->sysEx_customInstrument(p[0], 'EUP ', p + 1); } else { // SysEx manufacturer 0x97 has been spotted in the // Monkey Island 2 AdLib music, so don't make this a diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk index d8686d2c672..49f12e3e241 100644 --- a/engines/scumm/module.mk +++ b/engines/scumm/module.mk @@ -33,7 +33,9 @@ MODULE_OBJS := \ imuse/sysex_scumm.o \ imuse/drivers/amiga.o \ imuse/drivers/fmtowns.o \ + imuse/drivers/gmidi.o \ imuse/drivers/mac_m68k.o \ + imuse/drivers/mt32.o \ imuse/drivers/pcspk.o \ input.o \ ks_check.o \ diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp index 2da35837065..3ddf5b5a295 100644 --- a/engines/scumm/scumm.cpp +++ b/engines/scumm/scumm.cpp @@ -81,6 +81,8 @@ #include "scumm/imuse/drivers/mac_m68k.h" #include "scumm/imuse/drivers/amiga.h" #include "scumm/imuse/drivers/fmtowns.h" +#include "scumm/imuse/drivers/gmidi.h" +#include "scumm/imuse/drivers/mt32.h" #include "scumm/detection_steam.h" #include "backends/audiocd/audiocd.h" @@ -2044,10 +2046,16 @@ void ScummEngine::setupMusic(int midi, const Common::String &macInstrumentFile) bool multi_midi = ConfMan.getBool("multi_midi") && _sound->_musicType != MDT_NONE && _sound->_musicType != MDT_PCSPK && (midi & MDT_ADLIB); bool useOnlyNative = false; + uint32 imsFlags = 0; + if (_native_mt32) + imsFlags |= IMuse::kFlagNativeMT32; + if (enable_gs && MidiDriver::getMusicType(dev) != MT_MT32) + imsFlags |= IMuse::kFlagRolandGS; + if (isMacM68kIMuse()) { // We setup this driver as native MIDI driver to avoid playback // of the Mac music via a selected MIDI device. - nativeMidiDriver = new MacM68kDriver(_mixer); + nativeMidiDriver = new IMuseDriver_MacM68k(_mixer); // The Mac driver is never MT-32. _native_mt32 = false; // Ignore non-native drivers. This also ignores the multi MIDI setting. @@ -2057,31 +2065,25 @@ void ScummEngine::setupMusic(int midi, const Common::String &macInstrumentFile) _native_mt32 = enable_gs = false; useOnlyNative = true; } else if (_sound->_musicType != MDT_ADLIB && _sound->_musicType != MDT_TOWNS && _sound->_musicType != MDT_PCSPK) { - nativeMidiDriver = MidiDriver::createMidi(dev); + if (_native_mt32) + nativeMidiDriver = new IMuseDriver_MT32(dev, _game.id == GID_SAMNMAX); + else + nativeMidiDriver = new IMuseDriver_GMidi(dev, imsFlags & IMuse::kFlagRolandGS, _game.id == GID_SAMNMAX); } - if (nativeMidiDriver != nullptr && _native_mt32) - nativeMidiDriver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE); - if (!useOnlyNative) { - if (_sound->_musicType == MDT_TOWNS) { - adlibMidiDriver = new MidiDriver_TOWNS(_mixer); - } else if (_sound->_musicType == MDT_ADLIB || multi_midi) { - adlibMidiDriver = MidiDriver::createMidi(MidiDriver::detectDevice(_sound->_musicType == MDT_TOWNS ? MDT_TOWNS : MDT_ADLIB)); + if (_sound->_musicType == MDT_ADLIB || multi_midi) { + adlibMidiDriver = MidiDriver::createMidi(MidiDriver::detectDevice(MDT_ADLIB)); adlibMidiDriver->property(MidiDriver::PROP_OLD_ADLIB, (_game.features & GF_SMALL_HEADER) ? 1 : 0); // Try to use OPL3 mode for Sam&Max when possible. adlibMidiDriver->property(MidiDriver::PROP_SCUMM_OPL3, (_game.id == GID_SAMNMAX) ? 1 : 0); + } else if (_sound->_musicType == MDT_TOWNS) { + adlibMidiDriver = new IMuseDriver_FMTowns(_mixer); } else if (_sound->_musicType == MDT_PCSPK) { - adlibMidiDriver = new PcSpkDriver(_mixer); + adlibMidiDriver = new IMuseDriver_PCSpk(_mixer); } } - uint32 imsFlags = 0; - if (_native_mt32) - imsFlags |= IMuse::kFlagNativeMT32; - if (enable_gs && MidiDriver::getMusicType(dev) != MT_MT32) - imsFlags |= IMuse::kFlagRolandGS; - _imuse = IMuse::create(this, nativeMidiDriver, adlibMidiDriver, isMacM68kIMuse() ? MDT_MACINTOSH : _sound->_musicType, imsFlags); if (_game.platform == Common::kPlatformFMTowns) {