aboutsummaryrefslogtreecommitdiff
path: root/engines/scumm/imuse
diff options
context:
space:
mode:
authorathrxx2019-07-05 18:32:01 +0200
committerathrxx2019-07-14 21:45:43 +0200
commit01f99f1a0a39a5e09f9eafc9e03ece4a8470bf1c (patch)
treed3f0ff8e08e7e0f4078881d8a0b6f09537e08407 /engines/scumm/imuse
parentd1b64aab0cd1477f6d56c9ced8b84886d66de4d8 (diff)
downloadscummvm-rg350-01f99f1a0a39a5e09f9eafc9e03ece4a8470bf1c.tar.gz
scummvm-rg350-01f99f1a0a39a5e09f9eafc9e03ece4a8470bf1c.tar.bz2
scummvm-rg350-01f99f1a0a39a5e09f9eafc9e03ece4a8470bf1c.zip
SCUMM: imuse driver directory cleanup
- move mac, pc speaker and fm-towns ims sound drivers into separate directory (AdLib and MT32/GM drivers are still too entangled with common code to be moved so easily, especially MT32/GM. It would require lots of changes to the common code and possibly to all engines using the MidiDriver class. So I leave that for now.)
Diffstat (limited to 'engines/scumm/imuse')
-rw-r--r--engines/scumm/imuse/drivers/fmtowns.cpp1029
-rw-r--r--engines/scumm/imuse/drivers/fmtowns.h83
-rw-r--r--engines/scumm/imuse/drivers/mac_m68k.cpp (renamed from engines/scumm/imuse/mac_m68k.cpp)2
-rw-r--r--engines/scumm/imuse/drivers/mac_m68k.h (renamed from engines/scumm/imuse/mac_m68k.h)0
-rw-r--r--engines/scumm/imuse/drivers/pcspk.cpp (renamed from engines/scumm/imuse/pcspk.cpp)2
-rw-r--r--engines/scumm/imuse/drivers/pcspk.h (renamed from engines/scumm/imuse/pcspk.h)0
6 files changed, 1114 insertions, 2 deletions
diff --git a/engines/scumm/imuse/drivers/fmtowns.cpp b/engines/scumm/imuse/drivers/fmtowns.cpp
new file mode 100644
index 0000000000..37765e07e7
--- /dev/null
+++ b/engines/scumm/imuse/drivers/fmtowns.cpp
@@ -0,0 +1,1029 @@
+/* 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.
+ *
+ */
+
+#include "engines/scumm/imuse/drivers/fmtowns.h"
+#include "audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h"
+#include "common/textconsole.h"
+#include "common/system.h"
+
+class TownsMidiOutputChannel {
+friend class TownsMidiInputChannel;
+public:
+ TownsMidiOutputChannel(MidiDriver_TOWNS *driver, int chanId);
+ ~TownsMidiOutputChannel();
+
+ void noteOn(uint8 msb, uint16 lsb);
+ void noteOnPitchBend(uint8 msb, uint16 lsb);
+ void setupProgram(const uint8 *data, uint8 mLevelPara, uint8 tLevelPara);
+ void setupEffects(int index, uint8 flags, const uint8 *effectData);
+ void setModWheel(uint8 value);
+
+ void connect(TownsMidiInputChannel *chan);
+ void disconnect();
+
+ bool update();
+
+ enum CheckPriorityStatus {
+ kDisconnected = -2,
+ kHighPriority = -1
+ };
+
+ int checkPriority(int pri);
+
+private:
+ struct EffectEnvelope {
+ uint8 state;
+ int32 currentLevel;
+ int32 duration;
+ int32 maxLevel;
+ int32 startLevel;
+ uint8 loop;
+ uint8 stateTargetLevels[4];
+ uint8 stateModWheelLevels[4];
+ int8 modWheelSensitivity;
+ int8 modWheelState;
+ int8 modWheelLast;
+ uint16 numSteps;
+ uint32 stepCounter;
+ int32 incrPerStep;
+ int8 dir;
+ uint32 incrPerStepRem;
+ uint32 incrCountRem;
+ } *_effectEnvelopes;
+
+ struct EffectDef {
+ int32 phase;
+ uint8 type;
+ uint8 useModWheel;
+ uint8 loopRefresh;
+ EffectEnvelope *s;
+ } *_effectDefs;
+
+ void startEffect(EffectEnvelope *s, const uint8 *effectData);
+ void updateEffectGenerator(EffectEnvelope *s, EffectDef *d);
+ int advanceEffectEnvelope(EffectEnvelope *s, EffectDef *d);
+ void initNextEnvelopeState(EffectEnvelope *s);
+ int16 getEffectStartLevel(uint8 type);
+ int getEffectModLevel(int lvl, int mod);
+
+ void keyOn();
+ void keyOff();
+ void keyOnSetFreq(uint16 frq);
+ void out(uint8 reg, uint8 val);
+
+ TownsMidiInputChannel *_in;
+ TownsMidiOutputChannel *_prev;
+ TownsMidiOutputChannel *_next;
+ uint8 _adjustModTl;
+ uint8 _chan;
+ uint8 _note;
+ uint8 _operator2Tl;
+ uint8 _operator1Tl;
+ uint8 _sustainNoteOff;
+ int16 _duration;
+
+ uint16 _freq;
+ int16 _freqAdjust;
+
+ MidiDriver_TOWNS *_driver;
+
+ static const uint8 _chanMap[];
+ static const uint8 _chanMap2[];
+ static const uint8 _effectDefaults[];
+ static const uint16 _effectEnvStepTable[];
+ static const uint8 _freqMSB[];
+ static const uint16 _freqLSB[];
+};
+
+class TownsMidiInputChannel : public MidiChannel {
+friend class TownsMidiOutputChannel;
+public:
+ TownsMidiInputChannel(MidiDriver_TOWNS *driver, int chanIndex);
+ ~TownsMidiInputChannel();
+
+ MidiDriver *device() { return _driver; }
+ byte getNumber() { return _chanIndex; }
+ bool allocate();
+ void release();
+
+ void send(uint32 b);
+
+ void noteOff(byte note);
+ void noteOn(byte note, byte velocity);
+ void programChange(byte program);
+ void pitchBend(int16 bend);
+ void controlChange(byte control, byte value);
+ void pitchBendFactor(byte value);
+ void priority(byte value);
+ void sysEx_customInstrument(uint32 type, const byte *instr);
+
+private:
+ void controlModulationWheel(byte value);
+ void controlVolume(byte value);
+ void controlPanPos(byte value);
+ void controlSustain(byte value);
+
+ void releasePedal();
+
+ TownsMidiOutputChannel *_out;
+
+ uint8 *_instrument;
+ uint8 _chanIndex;
+ uint8 _priority;
+ uint8 _tl;
+ int8 _transpose;
+ int8 _detune;
+ int8 _modWheel;
+ uint8 _sustain;
+ uint8 _pitchBendFactor;
+ int16 _pitchBend;
+ uint16 _freqLSB;
+
+ bool _allocated;
+
+ MidiDriver_TOWNS *_driver;
+
+ static const uint8 _programAdjustLevel[];
+};
+
+class TownsMidiChanState {
+public:
+ TownsMidiChanState();
+ ~TownsMidiChanState() {}
+ uint8 get(uint8 type);
+
+ uint8 unk1;
+ uint8 mulAmsFms;
+ uint8 tl;
+ uint8 attDec;
+ uint8 sus;
+ uint8 fgAlg;
+ uint8 unk2;
+};
+
+TownsMidiChanState::TownsMidiChanState() {
+ unk1 = mulAmsFms = tl = attDec = sus = fgAlg = unk2 = 0;
+}
+
+uint8 TownsMidiChanState::get(uint8 type) {
+ switch (type) {
+ case 0:
+ return unk1;
+ case 1:
+ return mulAmsFms;
+ case 2:
+ return tl;
+ case 3:
+ return attDec;
+ case 4:
+ return sus;
+ case 5:
+ return fgAlg;
+ case 6:
+ return unk2;
+ default:
+ break;
+ }
+ return 0;
+}
+
+TownsMidiOutputChannel::TownsMidiOutputChannel(MidiDriver_TOWNS *driver, int chanIndex) : _driver(driver), _chan(chanIndex),
+ _in(0), _prev(0), _next(0), _adjustModTl(0), _operator2Tl(0), _note(0), _operator1Tl(0), _sustainNoteOff(0), _duration(0), _freq(0), _freqAdjust(0) {
+ _effectEnvelopes = new EffectEnvelope[2];
+ _effectDefs = new EffectDef[2];
+
+ memset(_effectEnvelopes, 0, 2 * sizeof(EffectEnvelope));
+ memset(_effectDefs, 0, 2 * sizeof(EffectDef));
+ _effectDefs[0].s = &_effectEnvelopes[1];
+ _effectDefs[1].s = &_effectEnvelopes[0];
+}
+
+TownsMidiOutputChannel::~TownsMidiOutputChannel() {
+ delete[] _effectEnvelopes;
+ delete[] _effectDefs;
+}
+
+void TownsMidiOutputChannel::noteOn(uint8 msb, uint16 lsb) {
+ _freq = (msb << 7) + lsb;
+ _freqAdjust = 0;
+ keyOnSetFreq(_freq);
+}
+
+void TownsMidiOutputChannel::noteOnPitchBend(uint8 msb, uint16 lsb) {
+ _freq = (msb << 7) + lsb;
+ keyOnSetFreq(_freq + _freqAdjust);
+}
+
+void TownsMidiOutputChannel::setupProgram(const uint8 *data, uint8 mLevelPara, uint8 tLevelPara) {
+ // This driver uses only 2 operators and 2 algorithms (algorithm 5 and 7),
+ // since it is just a modified AdLib driver. It also uses AdLib programs.
+ // There are no FM-TOWNS specific programs. This is the reason for the low quality of the FM-TOWNS
+ // music (unsuitable data is just forced into the wrong audio device).
+
+ static const uint8 mul[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 12, 12, 15, 15 };
+ uint8 chan = _chanMap[_chan];
+
+ uint8 mulAmsFms1 = _driver->_chanState[chan].mulAmsFms = data[0];
+ uint8 tl1 = _driver->_chanState[chan].tl = (data[1] | 0x3f) - mLevelPara;
+ uint8 attDec1 = _driver->_chanState[chan].attDec = ~data[2];
+ uint8 sus1 = _driver->_chanState[chan].sus = ~data[3];
+ _driver->_chanState[chan].unk2 = data[4];
+ chan += 3;
+
+ out(0x30, mul[mulAmsFms1 & 0x0f]);
+ out(0x40, (tl1 & 0x3f) + 15);
+ out(0x50, ((attDec1 >> 4) << 1) | ((attDec1 >> 4) & 1));
+ out(0x60, ((attDec1 << 1) | (attDec1 & 1)) & 0x1f);
+ out(0x70, (mulAmsFms1 & 0x20) ^ 0x20 ? (((sus1 & 0x0f) << 1) | 1) : 0);
+ out(0x80, sus1);
+
+ uint8 mulAmsFms2 = _driver->_chanState[chan].mulAmsFms = data[5];
+ uint8 tl2 = _driver->_chanState[chan].tl = (data[6] | 0x3f) - tLevelPara;
+ uint8 attDec2 = _driver->_chanState[chan].attDec = ~data[7];
+ uint8 sus2 = _driver->_chanState[chan].sus = ~data[8];
+ _driver->_chanState[chan].unk2 = data[9];
+
+ uint8 mul2 = mul[mulAmsFms2 & 0x0f];
+ tl2 = (tl2 & 0x3f) + 15;
+ uint8 ar2 = ((attDec2 >> 4) << 1) | ((attDec2 >> 4) & 1);
+ uint8 dec2 = ((attDec2 << 1) | (attDec2 & 1)) & 0x1f;
+ uint8 sus2r = (mulAmsFms2 & 0x20) ^ 0x20 ? (((sus2 & 0x0f) << 1) | 1) : 0;
+
+ for (int i = 4; i < 16; i += 4) {
+ out(0x30 + i, mul2);
+ out(0x40 + i, tl2);
+ out(0x50 + i, ar2);
+ out(0x60 + i, dec2);
+ out(0x70 + i, sus2r);
+ out(0x80 + i, sus2);
+ }
+
+ _driver->_chanState[chan].fgAlg = data[10];
+
+ uint8 alg = 5 + 2 * (data[10] & 1);
+ uint8 fb = 4 * (data[10] & 0x0e);
+ out(0xb0, fb | alg);
+ uint8 t = mulAmsFms1 | mulAmsFms2;
+ out(0xb4, (0xc0 | ((t & 0x80) >> 3) | ((t & 0x40) >> 5)));
+}
+
+void TownsMidiOutputChannel::setupEffects(int index, uint8 flags, const uint8 *effectData) {
+ uint16 effectMaxLevel[] = { 0x2FF, 0x1F, 0x07, 0x3F, 0x0F, 0x0F, 0x0F, 0x03, 0x3F, 0x0F, 0x0F, 0x0F, 0x03, 0x3E, 0x1F };
+ uint8 effectType[] = { 0x1D, 0x1C, 0x1B, 0x00, 0x03, 0x04, 0x07, 0x08, 0x0D, 0x10, 0x11, 0x14, 0x15, 0x1e, 0x1f, 0x00 };
+
+ EffectEnvelope *s = &_effectEnvelopes[index];
+ EffectDef *d = &_effectDefs[index];
+
+ d->phase = 0;
+ d->useModWheel = flags & 0x40;
+ s->loop = flags & 0x20;
+ d->loopRefresh = flags & 0x10;
+ d->type = effectType[flags & 0x0f];
+ s->maxLevel = effectMaxLevel[flags & 0x0f];
+ s->modWheelSensitivity = 31;
+ s->modWheelState = d->useModWheel ? _in->_modWheel >> 2 : 31;
+
+ switch (d->type) {
+ case 0:
+ s->startLevel = _operator2Tl;
+ break;
+ case 13:
+ s->startLevel = _operator1Tl;
+ break;
+ case 30:
+ s->startLevel = 31;
+ d->s->modWheelState = 0;
+ break;
+ case 31:
+ s->startLevel = 0;
+ d->s->modWheelSensitivity = 0;
+ break;
+ default:
+ s->startLevel = getEffectStartLevel(d->type);
+ break;
+ }
+
+ startEffect(s, effectData);
+}
+
+void TownsMidiOutputChannel::setModWheel(uint8 value) {
+ if (_effectEnvelopes[0].state != kEnvReady && _effectDefs[0].type)
+ _effectEnvelopes[0].modWheelState = value >> 2;
+
+ if (_effectEnvelopes[1].state != kEnvReady && _effectDefs[1].type)
+ _effectEnvelopes[1].modWheelState = value >> 2;
+}
+
+void TownsMidiOutputChannel::connect(TownsMidiInputChannel *chan) {
+ if (!chan)
+ return;
+
+ _in = chan;
+ _next = chan->_out;
+ _prev = 0;
+ chan->_out = this;
+ if (_next)
+ _next->_prev = this;
+}
+
+void TownsMidiOutputChannel::disconnect() {
+ keyOff();
+
+ TownsMidiOutputChannel *p = _prev;
+ TownsMidiOutputChannel *n = _next;
+
+ if (n)
+ n->_prev = p;
+ if (p)
+ p->_next = n;
+ else
+ _in->_out = n;
+ _in = 0;
+}
+
+bool TownsMidiOutputChannel::update() {
+ if (!_in)
+ return false;
+
+ if (_duration) {
+ _duration -= 17;
+ if (_duration <= 0) {
+ disconnect();
+ return true;
+ }
+ }
+
+ for (int i = 0; i < 2; i++) {
+ if (_effectEnvelopes[i].state != kEnvReady)
+ updateEffectGenerator(&_effectEnvelopes[i], &_effectDefs[i]);
+ }
+
+ return false;
+}
+
+int TownsMidiOutputChannel::checkPriority(int pri) {
+ if (!_in)
+ return kDisconnected;
+
+ if (!_next && pri >= _in->_priority)
+ return _in->_priority;
+
+ return kHighPriority;
+}
+
+void TownsMidiOutputChannel::startEffect(EffectEnvelope *s, const uint8 *effectData) {
+ s->state = kEnvAttacking;
+ s->currentLevel = 0;
+ s->modWheelLast = 31;
+ s->duration = effectData[0] * 63;
+ s->stateTargetLevels[0] = effectData[1];
+ s->stateTargetLevels[1] = effectData[3];
+ s->stateTargetLevels[2] = effectData[5];
+ s->stateTargetLevels[3] = effectData[6];
+ s->stateModWheelLevels[0] = effectData[2];
+ s->stateModWheelLevels[1] = effectData[4];
+ s->stateModWheelLevels[2] = 0;
+ s->stateModWheelLevels[3] = effectData[7];
+ initNextEnvelopeState(s);
+}
+
+void TownsMidiOutputChannel::updateEffectGenerator(EffectEnvelope *s, EffectDef *d) {
+ uint8 f = advanceEffectEnvelope(s, d);
+
+ if (f & 1) {
+ switch (d->type) {
+ case 0:
+ _operator2Tl = s->startLevel + d->phase;
+ break;
+ case 13:
+ _operator1Tl = s->startLevel + d->phase;
+ break;
+ case 30:
+ d->s->modWheelState = d->phase;
+ break;
+ case 31:
+ d->s->modWheelSensitivity = d->phase;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (f & 2) {
+ if (d->loopRefresh)
+ keyOn();
+ }
+}
+
+int TownsMidiOutputChannel::advanceEffectEnvelope(EffectEnvelope *s, EffectDef *d) {
+ if (s->duration) {
+ s->duration -= 17;
+ if (s->duration <= 0) {
+ s->state = kEnvReady;
+ return 0;
+ }
+ }
+
+ int32 t = s->currentLevel + s->incrPerStep;
+
+ s->incrCountRem += s->incrPerStepRem;
+ if (s->incrCountRem >= s->numSteps) {
+ s->incrCountRem -= s->numSteps;
+ t += s->dir;
+ }
+
+ int retFlags = 0;
+
+ if (t != s->currentLevel || (s->modWheelState != s->modWheelLast)) {
+ s->currentLevel = t;
+ s->modWheelLast = s->modWheelState;
+ t = getEffectModLevel(t, s->modWheelState);
+ if (t != d->phase) {
+ d->phase = t;
+ retFlags |= 1;
+ }
+ }
+
+ if (--s->stepCounter)
+ return retFlags;
+
+ if (++s->state > kEnvReleasing) {
+ if (!s->loop) {
+ s->state = kEnvReady;
+ return retFlags;
+ }
+ s->state = kEnvAttacking;
+ retFlags |= 2;
+ }
+
+ initNextEnvelopeState(s);
+
+ return retFlags;
+}
+
+void TownsMidiOutputChannel::initNextEnvelopeState(EffectEnvelope *s) {
+ uint8 v = s->stateTargetLevels[s->state - 1];
+ int32 e = _effectEnvStepTable[_driver->_operatorLevelTable[((v & 0x7f) << 5) + s->modWheelSensitivity]];
+
+ if (v & 0x80)
+ e = _driver->randomValue(e);
+
+ if (!e)
+ e = 1;
+
+ s->numSteps = s->stepCounter = e;
+ int32 d = 0;
+
+ if (s->state != kEnvSustaining) {
+ v = s->stateModWheelLevels[s->state - 1];
+ e = getEffectModLevel(s->maxLevel, (v & 0x7f) - 31);
+
+ if (v & 0x80)
+ e = _driver->randomValue(e);
+
+ if (e + s->startLevel > s->maxLevel) {
+ e = s->maxLevel - s->startLevel;
+ } else {
+ if (e + s->startLevel < 0)
+ e = -s->startLevel;
+ }
+
+ d = e - s->currentLevel;
+ }
+
+ s->incrPerStep = d / s->numSteps;
+ s->dir = (d < 0) ? -1 : 1;
+ d *= s->dir;
+ s->incrPerStepRem = d % s->numSteps;
+ s->incrCountRem = 0;
+}
+
+int16 TownsMidiOutputChannel::getEffectStartLevel(uint8 type) {
+ uint8 chan = (type < 13) ? _chanMap2[_chan] : ((type < 26) ? _chanMap[_chan] : _chan);
+
+ if (type == 28)
+ return 15;
+ else if (type == 29)
+ return 383;
+ else if (type > 29)
+ return 0;
+ else if (type > 12)
+ type -= 13;
+
+ const uint8 *def = &_effectDefaults[type << 2];
+ uint8 res = (_driver->_chanState[chan].get(def[0] >> 5) & def[2]) >> def[1];
+ if (def[3])
+ res = def[3] - res;
+
+ return res;
+}
+
+int TownsMidiOutputChannel::getEffectModLevel(int lvl, int mod) {
+ if (mod == 0)
+ return 0;
+
+ if (mod == 31)
+ return lvl;
+
+ if (lvl > 63 || lvl < -63)
+ return ((lvl + 1) * mod) >> 5;
+
+ if (mod < 0) {
+ if (lvl < 0)
+ return _driver->_operatorLevelTable[((-lvl) << 5) - mod];
+ else
+ return -_driver->_operatorLevelTable[(lvl << 5) - mod];
+ } else {
+ if (lvl < 0)
+ return -_driver->_operatorLevelTable[((-lvl) << 5) + mod];
+ else
+ return _driver->_operatorLevelTable[(lvl << 5) + mod];
+ }
+
+ return 0;
+}
+
+void TownsMidiOutputChannel::keyOn() {
+ out(0x28, 0x30);
+}
+
+void TownsMidiOutputChannel::keyOff() {
+ out(0x28, 0);
+}
+
+void TownsMidiOutputChannel::keyOnSetFreq(uint16 frq) {
+ uint16 note = (frq << 1) >> 8;
+ frq = (_freqMSB[note] << 11) | _freqLSB[note];
+ out(0xa4, frq >> 8);
+ out(0xa0, frq & 0xff);
+ //out(0x28, 0x00);
+ out(0x28, 0x30);
+}
+
+void TownsMidiOutputChannel::out(uint8 reg, uint8 val) {
+ static const uint8 chanRegOffs[] = { 0, 1, 2, 0, 1, 2 };
+ static const uint8 keyValOffs[] = { 0, 1, 2, 4, 5, 6 };
+
+ if (reg == 0x28)
+ val = (val & 0xf0) | keyValOffs[_chan];
+ if (reg < 0x30)
+ _driver->_intf->callback(17, 0, reg, val);
+ else
+ _driver->_intf->callback(17, _chan / 3, (reg & ~3) | chanRegOffs[_chan], val);
+}
+
+const uint8 TownsMidiOutputChannel::_chanMap[] = {
+ 0, 1, 2, 8, 9, 10
+};
+
+const uint8 TownsMidiOutputChannel::_chanMap2[] = {
+ 3, 4, 5, 11, 12, 13
+};
+
+const uint8 TownsMidiOutputChannel::_effectDefaults[] = {
+ 0x40, 0x00, 0x3F, 0x3F, 0xE0, 0x02, 0x00, 0x00, 0x40, 0x06, 0xC0, 0x00,
+ 0x20, 0x00, 0x0F, 0x00, 0x60, 0x04, 0xF0, 0x0F, 0x60, 0x00, 0x0F, 0x0F,
+ 0x80, 0x04, 0xF0, 0x0F, 0x80, 0x00, 0x0F, 0x0F, 0xE0, 0x00, 0x03, 0x00,
+ 0x20, 0x07, 0x80, 0x00, 0x20, 0x06, 0x40, 0x00, 0x20, 0x05, 0x20, 0x00,
+ 0x20, 0x04, 0x10, 0x00, 0xC0, 0x00, 0x01, 0x00, 0xC0, 0x01, 0x0E, 0x00
+};
+
+const uint16 TownsMidiOutputChannel::_effectEnvStepTable[] = {
+ 0x0001, 0x0002, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009,
+ 0x000A, 0x000C, 0x000E, 0x0010, 0x0012, 0x0015, 0x0018, 0x001E,
+ 0x0024, 0x0032, 0x0040, 0x0052, 0x0064, 0x0088, 0x00A0, 0x00C0,
+ 0x00F0, 0x0114, 0x0154, 0x01CC, 0x0258, 0x035C, 0x04B0, 0x0640
+};
+
+const uint8 TownsMidiOutputChannel::_freqMSB[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x80, 0x81, 0x83, 0x85,
+ 0x87, 0x88, 0x8A, 0x8C, 0x8E, 0x8F, 0x91, 0x93, 0x95, 0x96, 0x98, 0x9A,
+ 0x9C, 0x9E, 0x9F, 0xA1, 0xA3, 0xA5, 0xA6, 0xA8, 0xAA, 0xAC, 0xAD, 0xAF,
+ 0xB1, 0xB3, 0xB4, 0xB6, 0xB8, 0xBA, 0xBC, 0xBD, 0xBF, 0xC1, 0xC3, 0xC4,
+ 0xC6, 0xC8, 0xCA, 0xCB, 0xCD, 0xCF, 0xD1, 0xD2, 0xD4, 0xD6, 0xD8, 0xDA,
+ 0xDB, 0xDD, 0xDF, 0xE1, 0xE2, 0xE4, 0xE6, 0xE8, 0xE9, 0xEB, 0xED, 0xEF
+};
+
+const uint16 TownsMidiOutputChannel::_freqLSB[] = {
+ 0x02D6, 0x02D6, 0x02D6, 0x02D6, 0x02D6, 0x02D6, 0x02D6, 0x02D6,
+ 0x02D6, 0x02D6, 0x02D6, 0x02D6, 0x02D6, 0x02D6, 0x0301, 0x032F,
+ 0x0360, 0x0393, 0x03C9, 0x0403, 0x0440, 0x0481, 0x04C6, 0x050E,
+ 0x055B, 0x02D6, 0x0301, 0x032F, 0x0360, 0x0393, 0x03C9, 0x0403,
+ 0x0440, 0x0481, 0x04C6, 0x050E, 0x055B, 0x02D6, 0x0301, 0x032F,
+ 0x0360, 0x0393, 0x03C9, 0x0403, 0x0440, 0x0481, 0x04C6, 0x050E,
+ 0x055B, 0x02D6, 0x0301, 0x032F, 0x0360, 0x0393, 0x03C9, 0x0403,
+ 0x0440, 0x0481, 0x04C6, 0x050E, 0x055B, 0x02D6, 0x0301, 0x032F,
+ 0x0360, 0x0393, 0x03C9, 0x0403, 0x0440, 0x0481, 0x04C6, 0x050E,
+ 0x055B, 0x02D6, 0x0301, 0x032F, 0x0360, 0x0393, 0x03C9, 0x0403,
+ 0x0440, 0x0481, 0x04C6, 0x050E, 0x055B, 0x02D6, 0x0301, 0x032F,
+ 0x0360, 0x0393, 0x03C9, 0x0403, 0x0440, 0x0481, 0x04C6, 0x050E,
+ 0x055B, 0x055B, 0x055B, 0x055B, 0x055B, 0x055B, 0x055B, 0x055B,
+ 0x055B, 0x055B, 0x055B, 0x055B, 0x055B, 0x055B, 0x055B, 0x055B,
+ 0x055B, 0x055B, 0x055B, 0x055B, 0x055B, 0x055B, 0x055B, 0x055B,
+ 0x055B, 0x055B, 0x055B, 0x055B, 0x055B, 0x055B, 0x055B, 0x055B
+};
+
+TownsMidiInputChannel::TownsMidiInputChannel(MidiDriver_TOWNS *driver, int chanIndex) : MidiChannel(), _driver(driver), _out(0), _chanIndex(chanIndex),
+ _priority(0), _tl(0), _transpose(0), _pitchBendFactor(0), _pitchBend(0), _sustain(0), _freqLSB(0), _detune(0), _modWheel(0), _allocated(false) {
+ _instrument = new uint8[30];
+ memset(_instrument, 0, 30);
+}
+
+TownsMidiInputChannel::~TownsMidiInputChannel() {
+ delete[] _instrument;
+}
+
+bool TownsMidiInputChannel::allocate() {
+ if (_allocated)
+ return false;
+ _allocated = true;
+ return true;
+}
+
+void TownsMidiInputChannel::release() {
+ _allocated = false;
+}
+
+void TownsMidiInputChannel::send(uint32 b) {
+ _driver->send(b | _chanIndex);
+}
+
+void TownsMidiInputChannel::noteOff(byte note) {
+ if (!_out)
+ return;
+
+ for (TownsMidiOutputChannel *oc = _out; oc; oc = oc->_next) {
+ if (oc->_note != note)
+ continue;
+
+ if (_sustain)
+ oc->_sustainNoteOff = 1;
+ else
+ oc->disconnect();
+ }
+}
+
+void TownsMidiInputChannel::noteOn(byte note, byte velocity) {
+ TownsMidiOutputChannel *oc = _driver->allocateOutputChannel(_priority);
+
+ if (!oc)
+ return;
+
+ oc->connect(this);
+
+ oc->_adjustModTl = _instrument[10] & 1;
+ oc->_note = note;
+ oc->_sustainNoteOff = 0;
+ oc->_duration = _instrument[29] * 63;
+
+ oc->_operator1Tl = (_instrument[1] & 0x3f) + _driver->_operatorLevelTable[((velocity >> 1) << 5) + (_instrument[4] >> 2)];
+ if (oc->_operator1Tl > 63)
+ oc->_operator1Tl = 63;
+
+ oc->_operator2Tl = (_instrument[6] & 0x3f) + _driver->_operatorLevelTable[((velocity >> 1) << 5) + (_instrument[9] >> 2)];
+ if (oc->_operator2Tl > 63)
+ oc->_operator2Tl = 63;
+
+ oc->setupProgram(_instrument, oc->_adjustModTl == 1 ? _programAdjustLevel[_driver->_operatorLevelTable[(_tl >> 2) + (oc->_operator1Tl << 5)]] : oc->_operator1Tl, _programAdjustLevel[_driver->_operatorLevelTable[(_tl >> 2) + (oc->_operator2Tl << 5)]]);
+ oc->noteOn(note + _transpose, _freqLSB);
+
+ if (_instrument[11] & 0x80)
+ oc->setupEffects(0, _instrument[11], &_instrument[12]);
+ else
+ oc->_effectEnvelopes[0].state = kEnvReady;
+
+ if (_instrument[20] & 0x80)
+ oc->setupEffects(1, _instrument[20], &_instrument[21]);
+ else
+ oc->_effectEnvelopes[1].state = kEnvReady;
+}
+
+void TownsMidiInputChannel::programChange(byte program) {
+ // Not implemented (The loading and assignment of programs
+ // is handled externally by the SCUMM engine. The programs
+ // get sent via sysEx_customInstrument.)
+}
+
+void TownsMidiInputChannel::pitchBend(int16 bend) {
+ _pitchBend = bend;
+ _freqLSB = ((_pitchBend * _pitchBendFactor) >> 6) + _detune;
+ for (TownsMidiOutputChannel *oc = _out; oc; oc = oc->_next)
+ oc->noteOnPitchBend(oc->_note + oc->_in->_transpose, _freqLSB);
+}
+
+void TownsMidiInputChannel::controlChange(byte control, byte value) {
+ switch (control) {
+ case 1:
+ controlModulationWheel(value);
+ break;
+ case 7:
+ controlVolume(value);
+ break;
+ case 10:
+ controlPanPos(value);
+ break;
+ case 64:
+ controlSustain(value);
+ break;
+ case 123:
+ while (_out)
+ _out->disconnect();
+ break;
+ default:
+ break;
+ }
+}
+
+void TownsMidiInputChannel::pitchBendFactor(byte value) {
+ _pitchBendFactor = value;
+ _freqLSB = ((_pitchBend * _pitchBendFactor) >> 6) + _detune;
+ for (TownsMidiOutputChannel *oc = _out; oc; oc = oc->_next)
+ oc->noteOnPitchBend(oc->_note + oc->_in->_transpose, _freqLSB);
+}
+
+void TownsMidiInputChannel::priority(byte value) {
+ _priority = value;
+}
+
+void TownsMidiInputChannel::sysEx_customInstrument(uint32 type, const byte *instr) {
+ memcpy(_instrument, instr, 30);
+}
+
+void TownsMidiInputChannel::controlModulationWheel(byte value) {
+ _modWheel = value;
+ for (TownsMidiOutputChannel *oc = _out; oc; oc = oc->_next)
+ oc->setModWheel(value);
+}
+
+void TownsMidiInputChannel::controlVolume(byte value) {
+ /* This is all done inside the imuse code
+ uint16 v1 = _ctrlVolume + 1;
+ uint16 v2 = value;
+ if (_chanIndex != 16) {
+ _ctrlVolume = value;
+ v2 = _player->getEffectiveVolume();
+ }
+ _tl = (v1 * v2) >> 7;*/
+
+ _tl = value;
+}
+
+void TownsMidiInputChannel::controlPanPos(byte value) {
+ // not implemented
+}
+
+void TownsMidiInputChannel::controlSustain(byte value) {
+ _sustain = value;
+ if (!value)
+ releasePedal();
+}
+
+void TownsMidiInputChannel::releasePedal() {
+ for (TownsMidiOutputChannel *oc = _out; oc; oc = oc->_next) {
+ if (oc->_sustainNoteOff)
+ oc->disconnect();
+ }
+}
+
+const uint8 TownsMidiInputChannel::_programAdjustLevel[] = {
+ 0x00, 0x04, 0x07, 0x0B, 0x0D, 0x10, 0x12, 0x14,
+ 0x16, 0x18, 0x1A, 0x1B, 0x1D, 0x1E, 0x1F, 0x21,
+ 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29,
+ 0x2A, 0x2B, 0x2C, 0x2C, 0x2D, 0x2E, 0x2F, 0x2F,
+ 0x30, 0x31, 0x31, 0x32, 0x33, 0x33, 0x34, 0x35,
+ 0x35, 0x36, 0x36, 0x37, 0x37, 0x38, 0x38, 0x39,
+ 0x39, 0x3A, 0x3A, 0x3B, 0x3B, 0x3C, 0x3C, 0x3C,
+ 0x3D, 0x3D, 0x3E, 0x3E, 0x3E, 0x3F, 0x3F, 0x3F
+};
+
+MidiDriver_TOWNS::MidiDriver_TOWNS(Audio::Mixer *mixer) : _timerProc(0), _timerProcPara(0), _channels(0), _out(0),
+ _baseTempo(10080), _chanState(0), _operatorLevelTable(0), _tickCounter(0), _rand(1), _allocCurPos(0), _isOpen(false) {
+ // We set exteral mutex handling to true to avoid lockups in SCUMM which has its own mutex.
+ _intf = new TownsAudioInterface(mixer, this, true);
+
+ _channels = new TownsMidiInputChannel*[32];
+ for (int i = 0; i < 32; i++)
+ _channels[i] = new TownsMidiInputChannel(this, i > 8 ? (i + 1) : i);
+
+ _out = new TownsMidiOutputChannel*[6];
+ for (int i = 0; i < 6; i++)
+ _out[i] = new TownsMidiOutputChannel(this, i);
+
+ _chanState = new TownsMidiChanState[32];
+
+ _operatorLevelTable = new uint8[2048];
+ for (int i = 0; i < 64; i++) {
+ for (int ii = 0; ii < 32; ii++)
+ _operatorLevelTable[(i << 5) + ii] = ((i * (ii + 1)) >> 5) & 0xff;
+ }
+ for (int i = 0; i < 64; i++)
+ _operatorLevelTable[i << 5] = 0;
+}
+
+MidiDriver_TOWNS::~MidiDriver_TOWNS() {
+ close();
+ delete _intf;
+
+ if (_channels) {
+ for (int i = 0; i < 32; i++)
+ delete _channels[i];
+ delete[] _channels;
+ }
+ _channels = 0;
+
+ if (_out) {
+ for (int i = 0; i < 6; i++)
+ delete _out[i];
+ delete[] _out;
+ }
+ _out = 0;
+
+ delete[] _chanState;
+ _chanState = 0;
+ delete[] _operatorLevelTable;
+ _operatorLevelTable = 0;
+}
+
+int MidiDriver_TOWNS::open() {
+ if (_isOpen)
+ return MERR_ALREADY_OPEN;
+
+ if (!_intf->init())
+ return MERR_CANNOT_CONNECT;
+
+ _intf->callback(0);
+
+ _intf->callback(21, 255, 1);
+ _intf->callback(21, 0, 1);
+ _intf->callback(22, 255, 221);
+
+ _intf->callback(33, 8);
+ _intf->setSoundEffectChanMask(~0x3f);
+
+ _allocCurPos = 0;
+
+ _isOpen = true;
+
+ return 0;
+}
+
+void MidiDriver_TOWNS::close() {
+ if (!_isOpen)
+ return;
+
+ _isOpen = false;
+
+ setTimerCallback(0, 0);
+ g_system->delayMillis(20);
+}
+
+void MidiDriver_TOWNS::send(uint32 b) {
+ if (!_isOpen)
+ return;
+
+ byte param2 = (b >> 16) & 0xFF;
+ byte param1 = (b >> 8) & 0xFF;
+ byte cmd = b & 0xF0;
+
+ TownsMidiInputChannel *c = _channels[b & 0x0F];
+
+ switch (cmd) {
+ case 0x80:
+ c->noteOff(param1);
+ break;
+ case 0x90:
+ if (param2)
+ c->noteOn(param1, param2);
+ else
+ c->noteOff(param1);
+ break;
+ case 0xB0:
+ c->controlChange(param1, param2);
+ break;
+ case 0xC0:
+ c->programChange(param1);
+ break;
+ case 0xE0:
+ c->pitchBend((param1 | (param2 << 7)) - 0x2000);
+ break;
+ case 0xF0:
+ warning("MidiDriver_TOWNS: Receiving SysEx command on a send() call");
+ break;
+
+ default:
+ break;
+ }
+}
+
+void MidiDriver_TOWNS::setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc) {
+ _timerProc = timer_proc;
+ _timerProcPara = timer_param;
+}
+
+uint32 MidiDriver_TOWNS::getBaseTempo() {
+ return _baseTempo;
+}
+
+MidiChannel *MidiDriver_TOWNS::allocateChannel() {
+ if (!_isOpen)
+ return 0;
+
+ for (int i = 0; i < 32; ++i) {
+ TownsMidiInputChannel *chan = _channels[i];
+ if (chan->allocate())
+ return chan;
+ }
+
+ return 0;
+}
+
+MidiChannel *MidiDriver_TOWNS::getPercussionChannel() {
+ return 0;
+}
+
+void MidiDriver_TOWNS::timerCallback(int timerId) {
+ if (!_isOpen)
+ return;
+
+ switch (timerId) {
+ case 1:
+ updateParser();
+ updateOutputChannels();
+ break;
+ default:
+ break;
+ }
+}
+
+void MidiDriver_TOWNS::updateParser() {
+ if (_timerProc)
+ _timerProc(_timerProcPara);
+}
+
+void MidiDriver_TOWNS::updateOutputChannels() {
+ _tickCounter += _baseTempo;
+ while (_tickCounter >= 16667) {
+ _tickCounter -= 16667;
+ for (int i = 0; i < 6; i++) {
+ if (_out[i]->update())
+ return;
+ }
+ }
+}
+
+TownsMidiOutputChannel *MidiDriver_TOWNS::allocateOutputChannel(uint8 pri) {
+ TownsMidiOutputChannel *res = 0;
+
+ for (int i = 0; i < 6; i++) {
+ if (++_allocCurPos == 6)
+ _allocCurPos = 0;
+
+ int s = _out[_allocCurPos]->checkPriority(pri);
+ if (s == TownsMidiOutputChannel::kDisconnected)
+ return _out[_allocCurPos];
+
+ if (s != TownsMidiOutputChannel::kHighPriority) {
+ pri = s;
+ res = _out[_allocCurPos];
+ }
+ }
+
+ if (res)
+ res->disconnect();
+
+ return res;
+}
+
+int MidiDriver_TOWNS::randomValue(int para) {
+ _rand = (_rand & 1) ? (_rand >> 1) ^ 0xb8 : (_rand >> 1);
+ return (_rand * para) >> 8;
+}
diff --git a/engines/scumm/imuse/drivers/fmtowns.h b/engines/scumm/imuse/drivers/fmtowns.h
new file mode 100644
index 0000000000..5413e3797f
--- /dev/null
+++ b/engines/scumm/imuse/drivers/fmtowns.h
@@ -0,0 +1,83 @@
+/* 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.
+ *
+ */
+
+#ifndef IMUSE_DRV_FMTOWNS_H
+#define IMUSE_DRV_FMTOWNS_H
+
+#include "audio/softsynth/fmtowns_pc98/towns_audio.h"
+#include "audio/mididrv.h"
+
+
+class TownsMidiOutputChannel;
+class TownsMidiInputChannel;
+class TownsMidiChanState;
+
+class MidiDriver_TOWNS : public MidiDriver, public TownsAudioInterfacePluginDriver {
+friend class TownsMidiInputChannel;
+friend class TownsMidiOutputChannel;
+public:
+ MidiDriver_TOWNS(Audio::Mixer *mixer);
+ ~MidiDriver_TOWNS();
+
+ int open();
+ bool isOpen() const { return _isOpen; }
+ void close();
+
+ void send(uint32 b);
+
+ void setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc);
+
+ uint32 getBaseTempo();
+ MidiChannel *allocateChannel();
+ MidiChannel *getPercussionChannel();
+
+ void timerCallback(int timerId);
+
+private:
+ void updateParser();
+ void updateOutputChannels();
+
+ TownsMidiOutputChannel *allocateOutputChannel(uint8 pri);
+
+ int randomValue(int para);
+
+ TownsMidiInputChannel **_channels;
+ TownsMidiOutputChannel **_out;
+ TownsMidiChanState *_chanState;
+
+ Common::TimerManager::TimerProc _timerProc;
+ void *_timerProcPara;
+
+ TownsAudioInterface *_intf;
+
+ uint32 _tickCounter;
+ uint8 _allocCurPos;
+ uint8 _rand;
+
+ bool _isOpen;
+
+ uint8 *_operatorLevelTable;
+
+ const uint16 _baseTempo;
+};
+
+#endif
diff --git a/engines/scumm/imuse/mac_m68k.cpp b/engines/scumm/imuse/drivers/mac_m68k.cpp
index 8ebd8e4cca..1bcef44222 100644
--- a/engines/scumm/imuse/mac_m68k.cpp
+++ b/engines/scumm/imuse/drivers/mac_m68k.cpp
@@ -20,7 +20,7 @@
*
*/
-#include "scumm/imuse/mac_m68k.h"
+#include "scumm/imuse/drivers/mac_m68k.h"
#include "common/util.h"
#include "common/macresman.h"
diff --git a/engines/scumm/imuse/mac_m68k.h b/engines/scumm/imuse/drivers/mac_m68k.h
index 31beaf4e66..31beaf4e66 100644
--- a/engines/scumm/imuse/mac_m68k.h
+++ b/engines/scumm/imuse/drivers/mac_m68k.h
diff --git a/engines/scumm/imuse/pcspk.cpp b/engines/scumm/imuse/drivers/pcspk.cpp
index 856b771990..0e516c27aa 100644
--- a/engines/scumm/imuse/pcspk.cpp
+++ b/engines/scumm/imuse/drivers/pcspk.cpp
@@ -20,7 +20,7 @@
*
*/
-#include "scumm/imuse/pcspk.h"
+#include "scumm/imuse/drivers/pcspk.h"
#include "common/util.h"
diff --git a/engines/scumm/imuse/pcspk.h b/engines/scumm/imuse/drivers/pcspk.h
index 6a107e169a..6a107e169a 100644
--- a/engines/scumm/imuse/pcspk.h
+++ b/engines/scumm/imuse/drivers/pcspk.h