From 01f99f1a0a39a5e09f9eafc9e03ece4a8470bf1c Mon Sep 17 00:00:00 2001 From: athrxx Date: Fri, 5 Jul 2019 18:32:01 +0200 Subject: 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.) --- audio/module.mk | 1 - audio/softsynth/fmtowns_pc98/towns_midi.cpp | 1029 -------------------- audio/softsynth/fmtowns_pc98/towns_midi.h | 83 -- .../softsynth/fmtowns_pc98/towns_pc98_plugins.cpp | 9 +- engines/scumm/imuse/drivers/fmtowns.cpp | 1029 ++++++++++++++++++++ engines/scumm/imuse/drivers/fmtowns.h | 83 ++ engines/scumm/imuse/drivers/mac_m68k.cpp | 515 ++++++++++ engines/scumm/imuse/drivers/mac_m68k.h | 178 ++++ engines/scumm/imuse/drivers/pcspk.cpp | 835 ++++++++++++++++ engines/scumm/imuse/drivers/pcspk.h | 161 +++ engines/scumm/imuse/mac_m68k.cpp | 515 ---------- engines/scumm/imuse/mac_m68k.h | 178 ---- engines/scumm/imuse/pcspk.cpp | 835 ---------------- engines/scumm/imuse/pcspk.h | 161 --- engines/scumm/module.mk | 5 +- engines/scumm/players/player_towns.h | 2 +- engines/scumm/scumm.cpp | 9 +- 17 files changed, 2815 insertions(+), 2813 deletions(-) delete mode 100644 audio/softsynth/fmtowns_pc98/towns_midi.cpp delete mode 100644 audio/softsynth/fmtowns_pc98/towns_midi.h create mode 100644 engines/scumm/imuse/drivers/fmtowns.cpp create mode 100644 engines/scumm/imuse/drivers/fmtowns.h create mode 100644 engines/scumm/imuse/drivers/mac_m68k.cpp create mode 100644 engines/scumm/imuse/drivers/mac_m68k.h create mode 100644 engines/scumm/imuse/drivers/pcspk.cpp create mode 100644 engines/scumm/imuse/drivers/pcspk.h delete mode 100644 engines/scumm/imuse/mac_m68k.cpp delete mode 100644 engines/scumm/imuse/mac_m68k.h delete mode 100644 engines/scumm/imuse/pcspk.cpp delete mode 100644 engines/scumm/imuse/pcspk.h diff --git a/audio/module.mk b/audio/module.mk index 686c70be63..c2d764ace4 100644 --- a/audio/module.mk +++ b/audio/module.mk @@ -49,7 +49,6 @@ MODULE_OBJS := \ softsynth/fmtowns_pc98/pc98_audio.o \ softsynth/fmtowns_pc98/towns_audio.o \ softsynth/fmtowns_pc98/towns_euphony.o \ - softsynth/fmtowns_pc98/towns_midi.o \ softsynth/fmtowns_pc98/towns_pc98_driver.o \ softsynth/fmtowns_pc98/towns_pc98_fmsynth.o \ softsynth/fmtowns_pc98/towns_pc98_plugins.o \ diff --git a/audio/softsynth/fmtowns_pc98/towns_midi.cpp b/audio/softsynth/fmtowns_pc98/towns_midi.cpp deleted file mode 100644 index c02b047153..0000000000 --- a/audio/softsynth/fmtowns_pc98/towns_midi.cpp +++ /dev/null @@ -1,1029 +0,0 @@ -/* 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 "audio/softsynth/fmtowns_pc98/towns_midi.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/audio/softsynth/fmtowns_pc98/towns_midi.h b/audio/softsynth/fmtowns_pc98/towns_midi.h deleted file mode 100644 index 1143dbaa02..0000000000 --- a/audio/softsynth/fmtowns_pc98/towns_midi.h +++ /dev/null @@ -1,83 +0,0 @@ -/* 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 TOWNS_MIDI_H -#define TOWNS_MIDI_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/audio/softsynth/fmtowns_pc98/towns_pc98_plugins.cpp b/audio/softsynth/fmtowns_pc98/towns_pc98_plugins.cpp index 194bfc41f9..41034846eb 100644 --- a/audio/softsynth/fmtowns_pc98/towns_pc98_plugins.cpp +++ b/audio/softsynth/fmtowns_pc98/towns_pc98_plugins.cpp @@ -20,7 +20,6 @@ * */ -#include "audio/softsynth/fmtowns_pc98/towns_midi.h" #include "audio/musicplugin.h" #include "common/translation.h" #include "common/error.h" @@ -48,8 +47,8 @@ MusicDevices TownsEmuMusicPlugin::getDevices() const { } Common::Error TownsEmuMusicPlugin::createInstance(MidiDriver **mididriver, MidiDriver::DeviceHandle) const { - *mididriver = new MidiDriver_TOWNS(g_system->getMixer()); - return Common::kNoError; + *mididriver = 0; + return Common::kUnknownError; } class PC98EmuMusicPlugin : public MusicPluginObject { @@ -73,8 +72,8 @@ MusicDevices PC98EmuMusicPlugin::getDevices() const { } Common::Error PC98EmuMusicPlugin::createInstance(MidiDriver **mididriver, MidiDriver::DeviceHandle) const { - //*mididriver = /**/ - return Common::kNoError; + *mididriver = 0; + return Common::kUnknownError; } //#if PLUGIN_ENABLED_DYNAMIC(TOWNS) 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/drivers/mac_m68k.cpp b/engines/scumm/imuse/drivers/mac_m68k.cpp new file mode 100644 index 0000000000..1bcef44222 --- /dev/null +++ b/engines/scumm/imuse/drivers/mac_m68k.cpp @@ -0,0 +1,515 @@ +/* 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 "scumm/imuse/drivers/mac_m68k.h" + +#include "common/util.h" +#include "common/macresman.h" +#include "common/stream.h" + +namespace Scumm { + +MacM68kDriver::MacM68kDriver(Audio::Mixer *mixer) + : MidiDriver_Emulated(mixer) { +} + +MacM68kDriver::~MacM68kDriver() { +} + +int MacM68kDriver::open() { + if (_isOpen) { + return MERR_ALREADY_OPEN; + } + + const int error = MidiDriver_Emulated::open(); + if (error) { + return error; + } + + for (uint i = 0; i < ARRAYSIZE(_channels); ++i) { + _channels[i].init(this, i); + } + + memset(_voiceChannels, 0, sizeof(_voiceChannels)); + _lastUsedVoiceChannel = 0; + + loadAllInstruments(); + + _pitchTable[116] = 1664510; + _pitchTable[117] = 1763487; + _pitchTable[118] = 1868350; + _pitchTable[119] = 1979447; + _pitchTable[120] = 2097152; + _pitchTable[121] = 2221855; + _pitchTable[122] = 2353973; + _pitchTable[123] = 2493948; + _pitchTable[124] = 2642246; + _pitchTable[125] = 2799362; + _pitchTable[126] = 2965820; + _pitchTable[127] = 3142177; + for (int i = 115; i >= 0; --i) { + _pitchTable[i] = _pitchTable[i + 12] / 2; + } + + _volumeTable = new byte[8192]; + for (int i = 0; i < 32; ++i) { + for (int j = 0; j < 256; ++j) { + _volumeTable[i * 256 + j] = ((-128 + j) * _volumeBaseTable[i]) / 127 - 128; + } + } + + _mixBuffer = 0; + _mixBufferLength = 0; + + // We set the output sound type to music here to allow sound volume + // adjustment. The drawback here is that we can not control the music and + // sfx separately here. But the AdLib output has the same issue so it + // should not be that bad. + _mixer->playStream(Audio::Mixer::kMusicSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); + + return 0; +} + +void MacM68kDriver::close() { + if (!_isOpen) { + return; + } + + _mixer->stopHandle(_mixerSoundHandle); + _isOpen = false; + for (InstrumentMap::iterator i = _instruments.begin(); i != _instruments.end(); ++i) { + delete[] i->_value.data; + } + _instruments.clear(); + delete[] _volumeTable; + _volumeTable = 0; + delete[] _mixBuffer; + _mixBuffer = 0; + _mixBufferLength = 0; +} + +void MacM68kDriver::send(uint32 d) { + assert(false); +} + +void MacM68kDriver::sysEx_customInstrument(byte channel, uint32 type, const byte *instr) { + assert(false); +} + +MidiChannel *MacM68kDriver::allocateChannel() { + for (uint i = 0; i < ARRAYSIZE(_channels); ++i) { + if (_channels[i].allocate()) { + return &_channels[i]; + } + } + + return 0; +} + +MacM68kDriver::Instrument MacM68kDriver::getInstrument(int idx) const { + InstrumentMap::const_iterator i = _instruments.find(idx); + if (i != _instruments.end()) { + return i->_value; + } else { + return _defaultInstrument; + } +} + +void MacM68kDriver::generateSamples(int16 *buf, int len) { + int silentChannels = 0; + + if (_mixBufferLength < len) { + delete[] _mixBuffer; + + _mixBufferLength = len; + _mixBuffer = new int[_mixBufferLength]; + assert(_mixBuffer); + } + memset(_mixBuffer, 0, sizeof(int) * _mixBufferLength); + + for (int i = 0; i < kChannelCount; ++i) { + OutputChannel &out = _voiceChannels[i].out; + if (out.isFinished) { + ++silentChannels; + continue; + } + + byte *volumeTable = &_volumeTable[(out.volume / 4) * 256]; + int *buffer = _mixBuffer; + + int samplesLeft = len; + while (samplesLeft) { + out.subPos += out.pitchModifier; + while (out.subPos >= 0x10000) { + out.subPos -= 0x10000; + out.instrument++; + } + + if (out.instrument >= out.end) { + if (!out.start) { + break; + } + + out.instrument = out.start; + out.subPos = 0; + } + + *buffer++ += volumeTable[*out.instrument]; + --samplesLeft; + } + + if (samplesLeft) { + out.isFinished = true; + while (samplesLeft--) { + *buffer++ += 0x80; + } + } + } + + const int *buffer = _mixBuffer; + const int silenceAdd = silentChannels << 7; + while (len--) { + *buf++ = (((*buffer++ + silenceAdd) >> 3) << 8) ^ 0x8000; + } +} + +void MacM68kDriver::loadAllInstruments() { + Common::MacResManager resource; + if (resource.open("iMUSE Setups")) { + if (!resource.hasResFork()) { + error("MacM68kDriver::loadAllInstruments: \"iMUSE Setups\" loaded, but no resource fork present"); + } + + for (int i = 0x3E7; i < 0x468; ++i) { + Common::SeekableReadStream *stream = resource.getResource(MKTAG('s', 'n', 'd', ' '), i); + if (stream) { + addInstrument(i, stream); + delete stream; + } + } + + for (int i = 0x7D0; i < 0x8D0; ++i) { + Common::SeekableReadStream *stream = resource.getResource(MKTAG('s', 'n', 'd', ' '), i); + if (stream) { + addInstrument(i, stream); + delete stream; + } + } + + InstrumentMap::iterator inst = _instruments.find(kDefaultInstrument); + if (inst != _instruments.end()) { + _defaultInstrument = inst->_value; + } else { + error("MacM68kDriver::loadAllInstruments: Could not load default instrument"); + } + } else { + error("MacM68kDriver::loadAllInstruments: Could not load \"iMUSE Setups\""); + } +} + +void MacM68kDriver::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); + int count = data->readUint16BE(); + data->skip(2 * (3 * count)); + count = data->readUint16BE(); + data->skip(2 * (4 * count)); + + Instrument inst; + // Skip (optional) pointer to data + data->skip(4); + inst.length = data->readUint32BE(); + inst.sampleRate = data->readUint32BE(); + inst.loopStart = data->readUint32BE(); + inst.loopEnd = data->readUint32BE(); + // Skip encoding + data->skip(1); + inst.baseFrequency = data->readByte(); + + inst.data = new byte[inst.length]; + assert(inst.data); + data->read(inst.data, inst.length); + _instruments[idx] = inst; +} + +void MacM68kDriver::setPitch(OutputChannel *out, int frequency) { + out->frequency = frequency; + out->isFinished = false; + + const int pitchIdx = (frequency >> 7) + 60 - out->baseFrequency; + assert(pitchIdx >= 0); + + const int low7Bits = frequency & 0x7F; + if (low7Bits) { + out->pitchModifier = _pitchTable[pitchIdx] + (((_pitchTable[pitchIdx + 1] - _pitchTable[pitchIdx]) * low7Bits) >> 7); + } else { + out->pitchModifier = _pitchTable[pitchIdx]; + } +} + +void MacM68kDriver::VoiceChannel::off() { + if (out.start) { + out.isFinished = true; + } + + part->removeVoice(this); + part = 0; +} + +void MacM68kDriver::MidiChannel_MacM68k::release() { + _allocated = false; + while (_voice) { + _voice->off(); + } +} + +void MacM68kDriver::MidiChannel_MacM68k::send(uint32 b) { + uint8 type = b & 0xF0; + uint8 p1 = (b >> 8) & 0xFF; + uint8 p2 = (b >> 16) & 0xFF; + + switch (type) { + case 0x80: + noteOff(p1); + break; + + case 0x90: + if (p2) { + noteOn(p1, p2); + } else { + noteOff(p1); + } + break; + + case 0xB0: + controlChange(p1, p2); + break; + + case 0xE0: + pitchBend((p1 | (p2 << 7)) - 0x2000); + break; + + default: + break; + } +} + +void MacM68kDriver::MidiChannel_MacM68k::noteOff(byte note) { + for (VoiceChannel *i = _voice; i; i = i->next) { + if (i->note == note) { + if (_sustain) { + i->sustainNoteOff = true; + } else { + i->off(); + } + } + } +} + +void MacM68kDriver::MidiChannel_MacM68k::noteOn(byte note, byte velocity) { + // Do not start a not unless there is an instrument set up + if (!_instrument.data) { + return; + } + + // Allocate a voice channel + VoiceChannel *voice = _owner->allocateVoice(_priority); + if (!voice) { + return; + } + addVoice(voice); + + voice->note = note; + // This completly ignores the note's volume, but is in accordance + // to the original. + voice->out.volume = _volume; + + // Set up the instrument data + voice->out.baseFrequency = _instrument.baseFrequency; + voice->out.soundStart = _instrument.data; + voice->out.soundEnd = _instrument.data + _instrument.length; + if (_instrument.loopEnd && _instrument.loopEnd - 12 > _instrument.loopStart) { + voice->out.loopStart = _instrument.data + _instrument.loopStart; + voice->out.loopEnd = _instrument.data + _instrument.loopEnd; + } else { + voice->out.loopStart = 0; + voice->out.loopEnd = voice->out.soundEnd; + } + + voice->out.start = voice->out.loopStart; + voice->out.end = voice->out.loopEnd; + + // Set up the pitch + _owner->setPitch(&voice->out, (note << 7) + _pitchBend); + + // Set up the sample position + voice->out.instrument = voice->out.soundStart; + voice->out.subPos = 0; +} + +void MacM68kDriver::MidiChannel_MacM68k::programChange(byte program) { + _instrument = _owner->getInstrument(program + kProgramChangeBase); +} + +void MacM68kDriver::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) { + switch (control) { + // volume change + case 7: + _volume = value; + for (VoiceChannel *i = _voice; i; i = i->next) { + i->out.volume = value; + i->out.isFinished = false; + } + break; + + // sustain + case 64: + _sustain = value; + if (!_sustain) { + for (VoiceChannel *i = _voice; i; i = i->next) { + if (i->sustainNoteOff) { + i->off(); + } + } + } + break; + + // all notes off + case 123: + for (VoiceChannel *i = _voice; i; i = i->next) { + i->off(); + } + break; + + default: + break; + } +} + +void MacM68kDriver::MidiChannel_MacM68k::pitchBendFactor(byte value) { + _pitchBendFactor = value; +} + +void MacM68kDriver::MidiChannel_MacM68k::priority(byte value) { + _priority = value; +} + +void MacM68kDriver::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() { + if (_allocated) { + return false; + } + + _allocated = true; + _voice = 0; + _priority = 0; + memset(&_instrument, 0, sizeof(_instrument)); + _pitchBend = 0; + _pitchBendFactor = 0; + _volume = 0; + return true; +} + +void MacM68kDriver::MidiChannel_MacM68k::addVoice(VoiceChannel *voice) { + voice->next = _voice; + voice->prev = 0; + voice->part = this; + if (_voice) { + _voice->prev = voice; + } + _voice = voice; +} + +void MacM68kDriver::MidiChannel_MacM68k::removeVoice(VoiceChannel *voice) { + VoiceChannel *i = _voice; + while (i && i != voice) { + i = i->next; + } + + if (i) { + if (i->next) { + i->next->prev = i->prev; + } + + if (i->prev) { + i->prev->next = i->next; + } else { + _voice = i->next; + } + } +} + +MacM68kDriver::VoiceChannel *MacM68kDriver::allocateVoice(int priority) { + VoiceChannel *channel = 0; + for (int i = 0; i < kChannelCount; ++i) { + if (++_lastUsedVoiceChannel == kChannelCount) { + _lastUsedVoiceChannel = 0; + } + + VoiceChannel *cur = &_voiceChannels[_lastUsedVoiceChannel]; + if (!cur->part) { + memset(cur, 0, sizeof(*cur)); + return cur; + } else if (!cur->next) { + if (cur->part->_priority <= priority) { + priority = cur->part->_priority; + channel = cur; + } + } + } + + if (channel) { + channel->off(); + memset(channel, 0, sizeof(*channel)); + } + + return channel; +} + +const int MacM68kDriver::_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, + 76, 83, 89, 96, 104, 111, 119, 127 +}; + +} // End of namespace Scumm diff --git a/engines/scumm/imuse/drivers/mac_m68k.h b/engines/scumm/imuse/drivers/mac_m68k.h new file mode 100644 index 0000000000..31beaf4e66 --- /dev/null +++ b/engines/scumm/imuse/drivers/mac_m68k.h @@ -0,0 +1,178 @@ +/* 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 SCUMM_IMUSE_MAC_M68K_H +#define SCUMM_IMUSE_MAC_M68K_H + +#include "audio/softsynth/emumidi.h" + +#include "common/hashmap.h" + +namespace Common { +class SeekableReadStream; +} + +namespace Scumm { + +class MacM68kDriver : public MidiDriver_Emulated { + friend class MidiChannel_MacM68k; +public: + MacM68kDriver(Audio::Mixer *mixer); + ~MacM68kDriver(); + + virtual int open(); + virtual void close(); + + virtual void send(uint32 d); + virtual void sysEx_customInstrument(byte channel, uint32 type, const byte *instr); + + virtual MidiChannel *allocateChannel(); + virtual MidiChannel *getPercussionChannel() { return 0; } + + virtual bool isStereo() const { return false; } + virtual int getRate() const { + // The original is using a frequency of approx. 22254.54546 here. + // To be precise it uses the 16.16 fixed point value 0x56EE8BA3. + return 22254; + } + +protected: + virtual void generateSamples(int16 *buf, int len); + virtual void onTimer() {} + +private: + int *_mixBuffer; + int _mixBufferLength; + + struct Instrument { + uint length; + uint sampleRate; + uint loopStart; + uint loopEnd; + int baseFrequency; + + byte *data; + }; + + enum { + kDefaultInstrument = 0x3E7, + kProgramChangeBase = 0x3E8, + kSysExBase = 0x7D0 + }; + + Instrument getInstrument(int idx) const; + typedef Common::HashMap InstrumentMap; + InstrumentMap _instruments; + Instrument _defaultInstrument; + void loadAllInstruments(); + void addInstrument(int idx, Common::SeekableReadStream *data); + + struct OutputChannel { + int pitchModifier; + + const byte *instrument; + uint subPos; + + const byte *start; + const byte *end; + + const byte *soundStart; + const byte *soundEnd; + const byte *loopStart; + const byte *loopEnd; + + int frequency; + int volume; + + bool isFinished; + + int baseFrequency; + }; + + void setPitch(OutputChannel *out, int frequency); + int _pitchTable[128]; + + byte *_volumeTable; + static const int _volumeBaseTable[32]; + + class MidiChannel_MacM68k; + + struct VoiceChannel { + MidiChannel_MacM68k *part; + VoiceChannel *prev, *next; + int channel; + int note; + bool sustainNoteOff; + OutputChannel out; + + void off(); + }; + + class MidiChannel_MacM68k : public MidiChannel { + friend class MacM68kDriver; + public: + virtual MidiDriver *device() { return _owner; } + virtual byte getNumber() { return _number; } + virtual void release(); + + virtual void send(uint32 b); + virtual void noteOff(byte note); + virtual void noteOn(byte note, byte velocity); + virtual void programChange(byte program); + virtual void pitchBend(int16 bend); + virtual void controlChange(byte control, byte value); + virtual void pitchBendFactor(byte value); + virtual void priority(byte value); + virtual void sysEx_customInstrument(uint32 type, const byte *instr); + + void init(MacM68kDriver *owner, byte channel); + bool allocate(); + + void addVoice(VoiceChannel *voice); + void removeVoice(VoiceChannel *voice); + private: + MacM68kDriver *_owner; + bool _allocated; + int _number; + + VoiceChannel *_voice; + int _priority; + int _sustain; + Instrument _instrument; + int _pitchBend; + int _pitchBendFactor; + int _volume; + }; + + MidiChannel_MacM68k _channels[32]; + + enum { + kChannelCount = 8 + }; + VoiceChannel _voiceChannels[kChannelCount]; + int _lastUsedVoiceChannel; + VoiceChannel *allocateVoice(int priority); +}; + +} // End of namespace Scumm + +#endif diff --git a/engines/scumm/imuse/drivers/pcspk.cpp b/engines/scumm/imuse/drivers/pcspk.cpp new file mode 100644 index 0000000000..0e516c27aa --- /dev/null +++ b/engines/scumm/imuse/drivers/pcspk.cpp @@ -0,0 +1,835 @@ +/* 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 "scumm/imuse/drivers/pcspk.h" + +#include "common/util.h" + +namespace Scumm { + +PcSpkDriver::PcSpkDriver(Audio::Mixer *mixer) + : MidiDriver_Emulated(mixer), _pcSpk(mixer->getOutputRate()) { +} + +PcSpkDriver::~PcSpkDriver() { + close(); +} + +int PcSpkDriver::open() { + if (_isOpen) + return MERR_ALREADY_OPEN; + + MidiDriver_Emulated::open(); + + for (uint i = 0; i < 6; ++i) + _channels[i].init(this, i); + _activeChannel = 0; + _effectTimer = 0; + _randBase = 1; + + // We need to take care we only send note frequencies, when the internal + // settings actually changed, thus we need some extra state to keep track + // of that. + _lastActiveChannel = 0; + _lastActiveOut = 0; + + // We set the output sound type to music here to allow sound volume + // adjustment. The drawback here is that we can not control the music and + // sfx separately here. But the AdLib output has the same issue so it + // should not be that bad. + _mixer->playStream(Audio::Mixer::kMusicSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); + return 0; +} + +void PcSpkDriver::close() { + if (!_isOpen) + return; + _isOpen = false; + + _mixer->stopHandle(_mixerSoundHandle); +} + +void PcSpkDriver::send(uint32 d) { + assert((d & 0x0F) < 6); + _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() { + for (uint i = 0; i < 6; ++i) { + if (_channels[i].allocate()) + return &_channels[i]; + } + + return 0; +} + +void PcSpkDriver::generateSamples(int16 *buf, int len) { + _pcSpk.readBuffer(buf, len); +} + +void PcSpkDriver::onTimer() { + if (!_activeChannel) + return; + + for (uint i = 0; i < 6; ++i) { + OutputChannel &out = _channels[i]._out; + + if (!out.active) + continue; + + if (out.length == 0 || --out.length != 0) { + if (out.unkB && out.unkC) { + out.unkA += out.unkB; + if (out.instrument) + out.unkE = ((int8)out.instrument[out.unkA] * out.unkC) >> 4; + } + + ++_effectTimer; + if (_effectTimer > 3) { + _effectTimer = 0; + + if (out.effectEnvelopeA.state) + updateEffectGenerator(_channels[i], out.effectEnvelopeA, out.effectDefA); + if (out.effectEnvelopeB.state) + updateEffectGenerator(_channels[i], out.effectEnvelopeB, out.effectDefB); + } + } else { + out.active = 0; + updateNote(); + return; + } + } + + if (_activeChannel->_tl) { + output((_activeChannel->_out.note << 7) + _activeChannel->_pitchBend + _activeChannel->_out.unk60 + _activeChannel->_out.unkE); + } else { + _pcSpk.stop(); + _lastActiveChannel = 0; + _lastActiveOut = 0; + } +} + +void PcSpkDriver::updateNote() { + uint8 priority = 0; + _activeChannel = 0; + 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 (_activeChannel == 0 || _activeChannel->_tl == 0) { + _pcSpk.stop(); + _lastActiveChannel = 0; + _lastActiveOut = 0; + } else { + output(_activeChannel->_pitchBend + (_activeChannel->_out.note << 7)); + } +} + +void PcSpkDriver::output(uint16 out) { + byte v1 = (out >> 7) & 0xFF; + byte v2 = (out >> 2) & 0x1E; + + byte shift = _outputTable1[v1]; + uint16 indexBase = _outputTable2[v1] << 5; + uint16 frequency = _frequencyTable[(indexBase + v2) / 2] >> shift; + + // Only output in case the active channel changed or the frequency changed. + // This is not faithful to the original. Since our timings differ we would + // get distorted sound otherwise though. + if (_lastActiveChannel != _activeChannel || _lastActiveOut != out) { + _pcSpk.play(Audio::PCSpeaker::kWaveFormSquare, 1193180 / frequency, -1); + _lastActiveChannel = _activeChannel; + _lastActiveOut = out; + } +} + +void PcSpkDriver::MidiChannel_PcSpk::init(PcSpkDriver *owner, byte channel) { + _owner = owner; + _channel = channel; + _allocated = false; + memset(&_out, 0, sizeof(_out)); +} + +bool PcSpkDriver::MidiChannel_PcSpk::allocate() { + if (_allocated) + return false; + + memset(&_out, 0, sizeof(_out)); + memset(_instrument, 0, sizeof(_instrument)); + _out.effectDefA.envelope = &_out.effectEnvelopeA; + _out.effectDefB.envelope = &_out.effectEnvelopeB; + + _allocated = true; + return true; +} + +MidiDriver *PcSpkDriver::MidiChannel_PcSpk::device() { + return _owner; +} + +byte PcSpkDriver::MidiChannel_PcSpk::getNumber() { + return _channel; +} + +void PcSpkDriver::MidiChannel_PcSpk::release() { + _out.active = 0; + _allocated = false; + _owner->updateNote(); +} + +void PcSpkDriver::MidiChannel_PcSpk::send(uint32 b) { + uint8 type = b & 0xF0; + uint8 p1 = (b >> 8) & 0xFF; + uint8 p2 = (b >> 16) & 0xFF; + + switch (type) { + case 0x80: + noteOff(p1); + break; + + case 0x90: + if (p2) + noteOn(p1, p2); + else + noteOff(p1); + break; + + case 0xB0: + controlChange(p1, p2); + break; + + case 0xE0: + pitchBend((p1 | (p2 << 7)) - 0x2000); + break; + + default: + break; + } +} + +void PcSpkDriver::MidiChannel_PcSpk::noteOff(byte note) { + if (!_allocated) + return; + + if (_sustain) { + if (_out.note == note) + _out.sustainNoteOff = 1; + } else { + if (_out.note == note) { + _out.active = 0; + _owner->updateNote(); + } + } +} + +void PcSpkDriver::MidiChannel_PcSpk::noteOn(byte note, byte velocity) { + if (!_allocated) + return; + + _out.note = note; + _out.sustainNoteOff = 0; + _out.length = _instrument[0]; + + if (_instrument[4] * 256 < ARRAYSIZE(PcSpkDriver::_outInstrumentData)) + _out.instrument = _owner->_outInstrumentData + _instrument[4] * 256; + else + _out.instrument = 0; + + _out.unkA = 0; + _out.unkB = _instrument[1]; + _out.unkC = _instrument[2]; + _out.unkE = 0; + _out.unk60 = 0; + _out.active = 1; + + // In case we get a note on event on the last active channel, we reset the + // last active channel, thus we assure the frequency is correctly set, even + // when the same note was sent. + if (_owner->_lastActiveChannel == this) { + _owner->_lastActiveChannel = 0; + _owner->_lastActiveOut = 0; + } + _owner->updateNote(); + + _out.unkC += PcSpkDriver::getEffectModifier(_instrument[3] + ((velocity & 0xFE) << 4)); + if (_out.unkC > 63) + _out.unkC = 63; + + if ((_instrument[5] & 0x80) != 0) + _owner->setupEffects(*this, _out.effectEnvelopeA, _out.effectDefA, _instrument[5], _instrument + 6); + + if ((_instrument[14] & 0x80) != 0) + _owner->setupEffects(*this, _out.effectEnvelopeB, _out.effectDefB, _instrument[14], _instrument + 15); +} + +void PcSpkDriver::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) { + _pitchBend = (bend * _pitchBendFactor) >> 6; +} + +void PcSpkDriver::MidiChannel_PcSpk::controlChange(byte control, byte value) { + switch (control) { + case 1: + if (_out.effectEnvelopeA.state && _out.effectDefA.useModWheel) + _out.effectEnvelopeA.modWheelState = (value >> 2); + if (_out.effectEnvelopeB.state && _out.effectDefB.useModWheel) + _out.effectEnvelopeB.modWheelState = (value >> 2); + break; + + case 7: + _tl = value; + if (_owner->_activeChannel == this) { + if (_tl == 0) { + _owner->_lastActiveChannel = 0; + _owner->_lastActiveOut = 0; + _owner->_pcSpk.stop(); + } else { + _owner->output((_out.note << 7) + _pitchBend + _out.unk60 + _out.unkE); + } + } + break; + + case 64: + _sustain = value; + if (!value && _out.sustainNoteOff) { + _out.active = 0; + _owner->updateNote(); + } + break; + + case 123: + _out.active = 0; + _owner->updateNote(); + break; + + default: + break; + } +} + +void PcSpkDriver::MidiChannel_PcSpk::pitchBendFactor(byte value) { + _pitchBendFactor = value; +} + +void PcSpkDriver::MidiChannel_PcSpk::priority(byte value) { + _priority = value; +} + +void PcSpkDriver::MidiChannel_PcSpk::sysEx_customInstrument(uint32 type, const byte *instr) { + memcpy(_instrument, instr, sizeof(_instrument)); +} + +uint8 PcSpkDriver::getEffectModifier(uint16 level) { + uint8 base = level / 32; + uint8 index = level % 32; + + if (index == 0) + return 0; + + return (base * (index + 1)) >> 5; +} + +int16 PcSpkDriver::getEffectModLevel(int16 level, int8 mod) { + if (!mod) { + return 0; + } else if (mod == 31) { + return level; + } else if (level < -63 || level > 63) { + return (mod * (level + 1)) >> 6; + } else if (mod < 0) { + if (level < 0) + return getEffectModifier(((-level) << 5) - mod); + else + return -getEffectModifier((level << 5) - mod); + } else { + if (level < 0) + return -getEffectModifier(((-level) << 5) + mod); + else + return getEffectModifier(((-level) << 5) + mod); + } +} + +int16 PcSpkDriver::getRandScale(int16 input) { + if (_randBase & 1) + _randBase = (_randBase >> 1) ^ 0xB8; + else + _randBase >>= 1; + + return (_randBase * input) >> 8; +} + +void PcSpkDriver::setupEffects(MidiChannel_PcSpk &chan, EffectEnvelope &env, EffectDefinition &def, byte flags, const byte *data) { + def.phase = 0; + def.useModWheel = flags & 0x40; + env.loop = flags & 0x20; + def.type = flags & 0x1F; + + env.modWheelSensitivity = 31; + if (def.useModWheel) + env.modWheelState = chan._modWheel >> 2; + else + env.modWheelState = 31; + + switch (def.type) { + case 0: + env.maxLevel = 767; + env.startLevel = 383; + break; + + case 1: + env.maxLevel = 31; + env.startLevel = 15; + break; + + case 2: + env.maxLevel = 63; + env.startLevel = chan._out.unkB; + break; + + case 3: + env.maxLevel = 63; + env.startLevel = chan._out.unkC; + break; + + case 4: + env.maxLevel = 3; + env.startLevel = chan._instrument[4]; + break; + + case 5: + env.maxLevel = 62; + env.startLevel = 31; + env.modWheelState = 0; + break; + + case 6: + env.maxLevel = 31; + env.startLevel = 0; + env.modWheelSensitivity = 0; + break; + + default: + break; + } + + startEffect(env, data); +} + +void PcSpkDriver::startEffect(EffectEnvelope &env, const byte *data) { + env.state = 1; + env.currentLevel = 0; + env.modWheelLast = 31; + env.duration = data[0] * 63; + + env.stateTargetLevels[0] = data[1]; + env.stateTargetLevels[1] = data[3]; + env.stateTargetLevels[2] = data[5]; + env.stateTargetLevels[3] = data[6]; + + env.stateModWheelLevels[0] = data[2]; + env.stateModWheelLevels[1] = data[4]; + env.stateModWheelLevels[2] = 0; + env.stateModWheelLevels[3] = data[7]; + + initNextEnvelopeState(env); +} + +void PcSpkDriver::initNextEnvelopeState(EffectEnvelope &env) { + uint8 lastState = env.state - 1; + + uint16 stepCount = _effectEnvStepTable[getEffectModifier(((env.stateTargetLevels[lastState] & 0x7F) << 5) + env.modWheelSensitivity)]; + if (env.stateTargetLevels[lastState] & 0x80) + stepCount = getRandScale(stepCount); + if (!stepCount) + stepCount = 1; + + env.stateNumSteps = env.stateStepCounter = stepCount; + + int16 totalChange = 0; + if (lastState != 2) { + totalChange = getEffectModLevel(env.maxLevel, (env.stateModWheelLevels[lastState] & 0x7F) - 31); + if (env.stateModWheelLevels[lastState] & 0x80) + totalChange = getRandScale(totalChange); + + if (totalChange + env.startLevel > env.maxLevel) + totalChange = env.maxLevel - env.startLevel; + else if (totalChange + env.startLevel < 0) + totalChange = -env.startLevel; + + totalChange -= env.currentLevel; + } + + env.changePerStep = totalChange / stepCount; + if (totalChange < 0) { + totalChange = -totalChange; + env.dir = -1; + } else { + env.dir = 1; + } + env.changePerStepRem = totalChange % stepCount; + env.changeCountRem = 0; +} + +void PcSpkDriver::updateEffectGenerator(MidiChannel_PcSpk &chan, EffectEnvelope &env, EffectDefinition &def) { + if (advanceEffectEnvelope(env, def) & 1) { + switch (def.type) { + case 0: case 1: + chan._out.unk60 = def.phase << 4; + break; + + case 2: + chan._out.unkB = (def.phase & 0xFF) + chan._instrument[1]; + break; + + case 3: + chan._out.unkC = (def.phase & 0xFF) + chan._instrument[2]; + break; + + case 4: + if ((chan._instrument[4] + (def.phase & 0xFF)) * 256 < ARRAYSIZE(_outInstrumentData)) + chan._out.instrument = _outInstrumentData + (chan._instrument[4] + (def.phase & 0xFF)) * 256; + else + chan._out.instrument = 0; + break; + + case 5: + env.modWheelState = (def.phase & 0xFF); + break; + + case 6: + env.modWheelSensitivity = (def.phase & 0xFF); + break; + + default: + break; + } + } +} + +uint8 PcSpkDriver::advanceEffectEnvelope(EffectEnvelope &env, EffectDefinition &def) { + if (env.duration != 0) { + env.duration -= 17; + if (env.duration <= 0) { + env.state = 0; + return 0; + } + } + + uint8 changedFlags = 0; + int16 newLevel = env.currentLevel + env.changePerStep; + env.changeCountRem += env.changePerStepRem; + if (env.changeCountRem >= env.stateNumSteps) { + env.changeCountRem -= env.stateNumSteps; + newLevel += env.dir; + } + + if (env.currentLevel != newLevel || env.modWheelLast != env.modWheelState) { + env.currentLevel = newLevel; + env.modWheelLast = env.modWheelState; + + int16 newPhase = getEffectModLevel(newLevel, env.modWheelState); + if (def.phase != newPhase) { + changedFlags |= 1; + def.phase = newPhase; + } + } + + --env.stateStepCounter; + if (!env.stateStepCounter) { + ++env.state; + if (env.state > 4) { + if (env.loop) { + env.state = 1; + changedFlags |= 2; + } else { + env.state = 0; + return changedFlags; + } + } + + initNextEnvelopeState(env); + } + + return changedFlags; +} + +const byte PcSpkDriver::_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, + 0x46, 0x49, 0x4B, 0x4E, 0x50, 0x52, 0x55, 0x57, + 0x59, 0x5B, 0x5E, 0x60, 0x62, 0x64, 0x66, 0x67, + 0x69, 0x6B, 0x6C, 0x6E, 0x70, 0x71, 0x72, 0x74, + 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7B, + 0x7C, 0x7D, 0x7D, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, + 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7D, 0x7D, + 0x7C, 0x7B, 0x7B, 0x7A, 0x79, 0x78, 0x77, 0x76, + 0x75, 0x74, 0x72, 0x71, 0x70, 0x6E, 0x6C, 0x6B, + 0x69, 0x67, 0x66, 0x64, 0x62, 0x60, 0x5E, 0x5B, + 0x59, 0x57, 0x55, 0x52, 0x50, 0x4E, 0x4B, 0x49, + 0x46, 0x43, 0x41, 0x3E, 0x3B, 0x39, 0x36, 0x33, + 0x30, 0x2D, 0x2A, 0x27, 0x24, 0x21, 0x1E, 0x1B, + 0x18, 0x15, 0x12, 0x0F, 0x0C, 0x09, 0x06, 0x03, + 0x00, 0xFD, 0xFA, 0xF7, 0xF4, 0xF1, 0xEE, 0xEB, + 0xE8, 0xE5, 0xE2, 0xDF, 0xDC, 0xD9, 0xD6, 0xD3, + 0xD0, 0xCD, 0xCA, 0xC7, 0xC5, 0xC2, 0xBF, 0xBD, + 0xBA, 0xB7, 0xB5, 0xB2, 0xB0, 0xAE, 0xAB, 0xA9, + 0xA7, 0xA5, 0xA2, 0xA0, 0x9E, 0x9C, 0x9A, 0x99, + 0x97, 0x95, 0x94, 0x92, 0x90, 0x8F, 0x8E, 0x8C, + 0x8B, 0x8A, 0x89, 0x88, 0x87, 0x86, 0x85, 0x85, + 0x84, 0x83, 0x83, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x83, 0x83, + 0x84, 0x85, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, + 0x8B, 0x8C, 0x8E, 0x8F, 0x90, 0x92, 0x94, 0x95, + 0x97, 0x99, 0x9A, 0x9C, 0x9E, 0xA0, 0xA2, 0xA5, + 0xA7, 0xA9, 0xAB, 0xAE, 0xB0, 0xB2, 0xB5, 0xB7, + 0xBA, 0xBD, 0xBF, 0xC2, 0xC5, 0xC7, 0xCA, 0xCD, + 0xD0, 0xD3, 0xD6, 0xD9, 0xDC, 0xDF, 0xE2, 0xE5, + 0xE8, 0xEB, 0xEE, 0xF1, 0xF4, 0xF7, 0xFA, 0xFD, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, + 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, + 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, + 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, + 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, + 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, + 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, + 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, + 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, + 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, + 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, + 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, + 0x29, 0x23, 0xBE, 0x84, 0xE1, 0x6C, 0xD6, 0xAE, + 0x52, 0x90, 0x49, 0xF1, 0xF1, 0xBB, 0xE9, 0xEB, + 0xB3, 0xA6, 0xDB, 0x3C, 0x87, 0x0C, 0x3E, 0x99, + 0x24, 0x5E, 0x0D, 0x1C, 0x06, 0xB7, 0x47, 0xDE, + 0xB3, 0x12, 0x4D, 0xC8, 0x43, 0xBB, 0x8B, 0xA6, + 0x1F, 0x03, 0x5A, 0x7D, 0x09, 0x38, 0x25, 0x1F, + 0x5D, 0xD4, 0xCB, 0xFC, 0x96, 0xF5, 0x45, 0x3B, + 0x13, 0x0D, 0x89, 0x0A, 0x1C, 0xDB, 0xAE, 0x32, + 0x20, 0x9A, 0x50, 0xEE, 0x40, 0x78, 0x36, 0xFD, + 0x12, 0x49, 0x32, 0xF6, 0x9E, 0x7D, 0x49, 0xDC, + 0xAD, 0x4F, 0x14, 0xF2, 0x44, 0x40, 0x66, 0xD0, + 0x6B, 0xC4, 0x30, 0xB7, 0x32, 0x3B, 0xA1, 0x22, + 0xF6, 0x22, 0x91, 0x9D, 0xE1, 0x8B, 0x1F, 0xDA, + 0xB0, 0xCA, 0x99, 0x02, 0xB9, 0x72, 0x9D, 0x49, + 0x2C, 0x80, 0x7E, 0xC5, 0x99, 0xD5, 0xE9, 0x80, + 0xB2, 0xEA, 0xC9, 0xCC, 0x53, 0xBF, 0x67, 0xD6, + 0xBF, 0x14, 0xD6, 0x7E, 0x2D, 0xDC, 0x8E, 0x66, + 0x83, 0xEF, 0x57, 0x49, 0x61, 0xFF, 0x69, 0x8F, + 0x61, 0xCD, 0xD1, 0x1E, 0x9D, 0x9C, 0x16, 0x72, + 0x72, 0xE6, 0x1D, 0xF0, 0x84, 0x4F, 0x4A, 0x77, + 0x02, 0xD7, 0xE8, 0x39, 0x2C, 0x53, 0xCB, 0xC9, + 0x12, 0x1E, 0x33, 0x74, 0x9E, 0x0C, 0xF4, 0xD5, + 0xD4, 0x9F, 0xD4, 0xA4, 0x59, 0x7E, 0x35, 0xCF, + 0x32, 0x22, 0xF4, 0xCC, 0xCF, 0xD3, 0x90, 0x2D, + 0x48, 0xD3, 0x8F, 0x75, 0xE6, 0xD9, 0x1D, 0x2A, + 0xE5, 0xC0, 0xF7, 0x2B, 0x78, 0x81, 0x87, 0x44, + 0x0E, 0x5F, 0x50, 0x00, 0xD4, 0x61, 0x8D, 0xBE, + 0x7B, 0x05, 0x15, 0x07, 0x3B, 0x33, 0x82, 0x1F, + 0x18, 0x70, 0x92, 0xDA, 0x64, 0x54, 0xCE, 0xB1, + 0x85, 0x3E, 0x69, 0x15, 0xF8, 0x46, 0x6A, 0x04, + 0x96, 0x73, 0x0E, 0xD9, 0x16, 0x2F, 0x67, 0x68, + 0xD4, 0xF7, 0x4A, 0x4A, 0xD0, 0x57, 0x68, 0x76 +}; + +const byte PcSpkDriver::_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, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7 +}; + +const byte PcSpkDriver::_outputTable2[] = { + 0, 1, 2, 3, + 4, 5, 6, 7, + 8, 9, 10, 11, + 0, 1, 2, 3, + 4, 5, 6, 7, + 8, 9, 10, 11, + 0, 1, 2, 3, + 4, 5, 6, 7, + 8, 9, 10, 11, + 0, 1, 2, 3, + 4, 5, 6, 7, + 8, 9, 10, 11, + 0, 1, 2, 3, + 4, 5, 6, 7, + 8, 9, 10, 11, + 0, 1, 2, 3, + 4, 5, 6, 7, + 8, 9, 10, 11, + 0, 1, 2, 3, + 4, 5, 6, 7, + 8, 9, 10, 11, + 0, 1, 2, 3, + 4, 5, 6, 7, + 8, 9, 10, 11, + 0, 1, 2, 3, + 4, 5, 6, 7, + 8, 9, 10, 11, + 0, 1, 2, 3, + 4, 5, 6, 7, + 8, 9, 10, 11, + 0, 1, 2, 3, + 4, 5, 6, 7 +}; + +const uint16 PcSpkDriver::_effectEnvStepTable[] = { + 1, 2, 4, 5, + 6, 7, 8, 9, + 10, 12, 14, 16, + 18, 21, 24, 30, + 36, 50, 64, 82, + 100, 136, 160, 192, + 240, 276, 340, 460, + 600, 860, 1200, 1600 +}; + +const uint16 PcSpkDriver::_frequencyTable[] = { + 0x8E84, 0x8E00, 0x8D7D, 0x8CFA, + 0x8C78, 0x8BF7, 0x8B76, 0x8AF5, + 0x8A75, 0x89F5, 0x8976, 0x88F7, + 0x8879, 0x87FB, 0x877D, 0x8700, + 0x8684, 0x8608, 0x858C, 0x8511, + 0x8496, 0x841C, 0x83A2, 0x8328, + 0x82AF, 0x8237, 0x81BF, 0x8147, + 0x80D0, 0x8059, 0x7FE3, 0x7F6D, + 0x7EF7, 0x7E82, 0x7E0D, 0x7D99, + 0x7D25, 0x7CB2, 0x7C3F, 0x7BCC, + 0x7B5A, 0x7AE8, 0x7A77, 0x7A06, + 0x7995, 0x7925, 0x78B5, 0x7846, + 0x77D7, 0x7768, 0x76FA, 0x768C, + 0x761F, 0x75B2, 0x7545, 0x74D9, + 0x746D, 0x7402, 0x7397, 0x732C, + 0x72C2, 0x7258, 0x71EF, 0x7186, + 0x711D, 0x70B5, 0x704D, 0x6FE5, + 0x6F7E, 0x6F17, 0x6EB0, 0x6E4A, + 0x6DE5, 0x6D7F, 0x6D1A, 0x6CB5, + 0x6C51, 0x6BED, 0x6B8A, 0x6B26, + 0x6AC4, 0x6A61, 0x69FF, 0x699D, + 0x693C, 0x68DB, 0x687A, 0x681A, + 0x67BA, 0x675A, 0x66FA, 0x669B, + 0x663D, 0x65DF, 0x6581, 0x6523, + 0x64C6, 0x6469, 0x640C, 0x63B0, + 0x6354, 0x62F8, 0x629D, 0x6242, + 0x61E7, 0x618D, 0x6133, 0x60D9, + 0x6080, 0x6027, 0x5FCE, 0x5F76, + 0x5F1E, 0x5EC6, 0x5E6E, 0x5E17, + 0x5DC1, 0x5D6A, 0x5D14, 0x5CBE, + 0x5C68, 0x5C13, 0x5BBE, 0x5B6A, + 0x5B15, 0x5AC1, 0x5A6E, 0x5A1A, + 0x59C7, 0x5974, 0x5922, 0x58CF, + 0x587D, 0x582C, 0x57DA, 0x5789, + 0x5739, 0x56E8, 0x5698, 0x5648, + 0x55F9, 0x55A9, 0x555A, 0x550B, + 0x54BD, 0x546F, 0x5421, 0x53D3, + 0x5386, 0x5339, 0x52EC, 0x52A0, + 0x5253, 0x5207, 0x51BC, 0x5170, + 0x5125, 0x50DA, 0x5090, 0x5046, + 0x4FFB, 0x4FB2, 0x4F68, 0x4F1F, + 0x4ED6, 0x4E8D, 0x4E45, 0x4DFC, + 0x4DB5, 0x4D6D, 0x4D25, 0x4CDE, + 0x4C97, 0x4C51, 0x4C0A, 0x4BC4, + 0x4B7E, 0x4B39, 0x4AF3, 0x4AAE, + 0x4A69, 0x4A24, 0x49E0, 0x499C, + 0x4958, 0x4914, 0x48D1, 0x488E, + 0x484B, 0x4808, 0x47C6, 0x4783 +}; + +} // End of namespace Scumm diff --git a/engines/scumm/imuse/drivers/pcspk.h b/engines/scumm/imuse/drivers/pcspk.h new file mode 100644 index 0000000000..6a107e169a --- /dev/null +++ b/engines/scumm/imuse/drivers/pcspk.h @@ -0,0 +1,161 @@ +/* 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 SCUMM_IMUSE_PCSPK_H +#define SCUMM_IMUSE_PCSPK_H + +#include "audio/softsynth/emumidi.h" +#include "audio/softsynth/pcspk.h" + +namespace Scumm { + +class PcSpkDriver : public MidiDriver_Emulated { +public: + PcSpkDriver(Audio::Mixer *mixer); + ~PcSpkDriver(); + + virtual int open(); + virtual void close(); + + virtual void send(uint32 d); + virtual void sysEx_customInstrument(byte channel, uint32 type, const byte *instr); + + virtual MidiChannel *allocateChannel(); + virtual MidiChannel *getPercussionChannel() { return 0; } + + bool isStereo() const { return _pcSpk.isStereo(); } + int getRate() const { return _pcSpk.getRate(); } +protected: + void generateSamples(int16 *buf, int len); + void onTimer(); + +private: + Audio::PCSpeaker _pcSpk; + int _effectTimer; + uint8 _randBase; + + void updateNote(); + void output(uint16 out); + + static uint8 getEffectModifier(uint16 level); + int16 getEffectModLevel(int16 level, int8 mod); + int16 getRandScale(int16 input); + + struct EffectEnvelope { + uint8 state; + int16 currentLevel; + int16 duration; + int16 maxLevel; + int16 startLevel; + uint8 loop; + uint8 stateTargetLevels[4]; + uint8 stateModWheelLevels[4]; + uint8 modWheelSensitivity; + uint8 modWheelState; + uint8 modWheelLast; + int16 stateNumSteps; + int16 stateStepCounter; + int16 changePerStep; + int8 dir; + int16 changePerStepRem; + int16 changeCountRem; + }; + + struct EffectDefinition { + int16 phase; + uint8 type; + uint8 useModWheel; + EffectEnvelope *envelope; + }; + + struct OutputChannel { + uint8 active; + uint8 note; + uint8 sustainNoteOff; + uint8 length; + const uint8 *instrument; + uint8 unkA; + uint8 unkB; + uint8 unkC; + int16 unkE; + EffectEnvelope effectEnvelopeA; + EffectDefinition effectDefA; + EffectEnvelope effectEnvelopeB; + EffectDefinition effectDefB; + int16 unk60; + }; + + struct MidiChannel_PcSpk : public MidiChannel { + virtual MidiDriver *device(); + virtual byte getNumber(); + virtual void release(); + + virtual void send(uint32 b); + virtual void noteOff(byte note); + virtual void noteOn(byte note, byte velocity); + virtual void programChange(byte program); + virtual void pitchBend(int16 bend); + virtual void controlChange(byte control, byte value); + virtual void pitchBendFactor(byte value); + virtual void priority(byte value); + virtual void sysEx_customInstrument(uint32 type, const byte *instr); + + 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; + uint8 _sustain; + uint8 _pitchBendFactor; + int16 _pitchBend; + }; + + void setupEffects(MidiChannel_PcSpk &chan, EffectEnvelope &env, EffectDefinition &def, byte flags, const byte *data); + void startEffect(EffectEnvelope &env, const byte *data); + void initNextEnvelopeState(EffectEnvelope &env); + void updateEffectGenerator(MidiChannel_PcSpk &chan, EffectEnvelope &env, EffectDefinition &def); + uint8 advanceEffectEnvelope(EffectEnvelope &env, EffectDefinition &def); + + MidiChannel_PcSpk _channels[6]; + MidiChannel_PcSpk *_activeChannel; + + MidiChannel_PcSpk *_lastActiveChannel; + uint16 _lastActiveOut; + + static const byte _outInstrumentData[1024]; + static const byte _outputTable1[]; + static const byte _outputTable2[]; + static const uint16 _effectEnvStepTable[]; + static const uint16 _frequencyTable[]; +}; + +} // End of namespace Scumm + +#endif diff --git a/engines/scumm/imuse/mac_m68k.cpp b/engines/scumm/imuse/mac_m68k.cpp deleted file mode 100644 index 8ebd8e4cca..0000000000 --- a/engines/scumm/imuse/mac_m68k.cpp +++ /dev/null @@ -1,515 +0,0 @@ -/* 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 "scumm/imuse/mac_m68k.h" - -#include "common/util.h" -#include "common/macresman.h" -#include "common/stream.h" - -namespace Scumm { - -MacM68kDriver::MacM68kDriver(Audio::Mixer *mixer) - : MidiDriver_Emulated(mixer) { -} - -MacM68kDriver::~MacM68kDriver() { -} - -int MacM68kDriver::open() { - if (_isOpen) { - return MERR_ALREADY_OPEN; - } - - const int error = MidiDriver_Emulated::open(); - if (error) { - return error; - } - - for (uint i = 0; i < ARRAYSIZE(_channels); ++i) { - _channels[i].init(this, i); - } - - memset(_voiceChannels, 0, sizeof(_voiceChannels)); - _lastUsedVoiceChannel = 0; - - loadAllInstruments(); - - _pitchTable[116] = 1664510; - _pitchTable[117] = 1763487; - _pitchTable[118] = 1868350; - _pitchTable[119] = 1979447; - _pitchTable[120] = 2097152; - _pitchTable[121] = 2221855; - _pitchTable[122] = 2353973; - _pitchTable[123] = 2493948; - _pitchTable[124] = 2642246; - _pitchTable[125] = 2799362; - _pitchTable[126] = 2965820; - _pitchTable[127] = 3142177; - for (int i = 115; i >= 0; --i) { - _pitchTable[i] = _pitchTable[i + 12] / 2; - } - - _volumeTable = new byte[8192]; - for (int i = 0; i < 32; ++i) { - for (int j = 0; j < 256; ++j) { - _volumeTable[i * 256 + j] = ((-128 + j) * _volumeBaseTable[i]) / 127 - 128; - } - } - - _mixBuffer = 0; - _mixBufferLength = 0; - - // We set the output sound type to music here to allow sound volume - // adjustment. The drawback here is that we can not control the music and - // sfx separately here. But the AdLib output has the same issue so it - // should not be that bad. - _mixer->playStream(Audio::Mixer::kMusicSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); - - return 0; -} - -void MacM68kDriver::close() { - if (!_isOpen) { - return; - } - - _mixer->stopHandle(_mixerSoundHandle); - _isOpen = false; - for (InstrumentMap::iterator i = _instruments.begin(); i != _instruments.end(); ++i) { - delete[] i->_value.data; - } - _instruments.clear(); - delete[] _volumeTable; - _volumeTable = 0; - delete[] _mixBuffer; - _mixBuffer = 0; - _mixBufferLength = 0; -} - -void MacM68kDriver::send(uint32 d) { - assert(false); -} - -void MacM68kDriver::sysEx_customInstrument(byte channel, uint32 type, const byte *instr) { - assert(false); -} - -MidiChannel *MacM68kDriver::allocateChannel() { - for (uint i = 0; i < ARRAYSIZE(_channels); ++i) { - if (_channels[i].allocate()) { - return &_channels[i]; - } - } - - return 0; -} - -MacM68kDriver::Instrument MacM68kDriver::getInstrument(int idx) const { - InstrumentMap::const_iterator i = _instruments.find(idx); - if (i != _instruments.end()) { - return i->_value; - } else { - return _defaultInstrument; - } -} - -void MacM68kDriver::generateSamples(int16 *buf, int len) { - int silentChannels = 0; - - if (_mixBufferLength < len) { - delete[] _mixBuffer; - - _mixBufferLength = len; - _mixBuffer = new int[_mixBufferLength]; - assert(_mixBuffer); - } - memset(_mixBuffer, 0, sizeof(int) * _mixBufferLength); - - for (int i = 0; i < kChannelCount; ++i) { - OutputChannel &out = _voiceChannels[i].out; - if (out.isFinished) { - ++silentChannels; - continue; - } - - byte *volumeTable = &_volumeTable[(out.volume / 4) * 256]; - int *buffer = _mixBuffer; - - int samplesLeft = len; - while (samplesLeft) { - out.subPos += out.pitchModifier; - while (out.subPos >= 0x10000) { - out.subPos -= 0x10000; - out.instrument++; - } - - if (out.instrument >= out.end) { - if (!out.start) { - break; - } - - out.instrument = out.start; - out.subPos = 0; - } - - *buffer++ += volumeTable[*out.instrument]; - --samplesLeft; - } - - if (samplesLeft) { - out.isFinished = true; - while (samplesLeft--) { - *buffer++ += 0x80; - } - } - } - - const int *buffer = _mixBuffer; - const int silenceAdd = silentChannels << 7; - while (len--) { - *buf++ = (((*buffer++ + silenceAdd) >> 3) << 8) ^ 0x8000; - } -} - -void MacM68kDriver::loadAllInstruments() { - Common::MacResManager resource; - if (resource.open("iMUSE Setups")) { - if (!resource.hasResFork()) { - error("MacM68kDriver::loadAllInstruments: \"iMUSE Setups\" loaded, but no resource fork present"); - } - - for (int i = 0x3E7; i < 0x468; ++i) { - Common::SeekableReadStream *stream = resource.getResource(MKTAG('s', 'n', 'd', ' '), i); - if (stream) { - addInstrument(i, stream); - delete stream; - } - } - - for (int i = 0x7D0; i < 0x8D0; ++i) { - Common::SeekableReadStream *stream = resource.getResource(MKTAG('s', 'n', 'd', ' '), i); - if (stream) { - addInstrument(i, stream); - delete stream; - } - } - - InstrumentMap::iterator inst = _instruments.find(kDefaultInstrument); - if (inst != _instruments.end()) { - _defaultInstrument = inst->_value; - } else { - error("MacM68kDriver::loadAllInstruments: Could not load default instrument"); - } - } else { - error("MacM68kDriver::loadAllInstruments: Could not load \"iMUSE Setups\""); - } -} - -void MacM68kDriver::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); - int count = data->readUint16BE(); - data->skip(2 * (3 * count)); - count = data->readUint16BE(); - data->skip(2 * (4 * count)); - - Instrument inst; - // Skip (optional) pointer to data - data->skip(4); - inst.length = data->readUint32BE(); - inst.sampleRate = data->readUint32BE(); - inst.loopStart = data->readUint32BE(); - inst.loopEnd = data->readUint32BE(); - // Skip encoding - data->skip(1); - inst.baseFrequency = data->readByte(); - - inst.data = new byte[inst.length]; - assert(inst.data); - data->read(inst.data, inst.length); - _instruments[idx] = inst; -} - -void MacM68kDriver::setPitch(OutputChannel *out, int frequency) { - out->frequency = frequency; - out->isFinished = false; - - const int pitchIdx = (frequency >> 7) + 60 - out->baseFrequency; - assert(pitchIdx >= 0); - - const int low7Bits = frequency & 0x7F; - if (low7Bits) { - out->pitchModifier = _pitchTable[pitchIdx] + (((_pitchTable[pitchIdx + 1] - _pitchTable[pitchIdx]) * low7Bits) >> 7); - } else { - out->pitchModifier = _pitchTable[pitchIdx]; - } -} - -void MacM68kDriver::VoiceChannel::off() { - if (out.start) { - out.isFinished = true; - } - - part->removeVoice(this); - part = 0; -} - -void MacM68kDriver::MidiChannel_MacM68k::release() { - _allocated = false; - while (_voice) { - _voice->off(); - } -} - -void MacM68kDriver::MidiChannel_MacM68k::send(uint32 b) { - uint8 type = b & 0xF0; - uint8 p1 = (b >> 8) & 0xFF; - uint8 p2 = (b >> 16) & 0xFF; - - switch (type) { - case 0x80: - noteOff(p1); - break; - - case 0x90: - if (p2) { - noteOn(p1, p2); - } else { - noteOff(p1); - } - break; - - case 0xB0: - controlChange(p1, p2); - break; - - case 0xE0: - pitchBend((p1 | (p2 << 7)) - 0x2000); - break; - - default: - break; - } -} - -void MacM68kDriver::MidiChannel_MacM68k::noteOff(byte note) { - for (VoiceChannel *i = _voice; i; i = i->next) { - if (i->note == note) { - if (_sustain) { - i->sustainNoteOff = true; - } else { - i->off(); - } - } - } -} - -void MacM68kDriver::MidiChannel_MacM68k::noteOn(byte note, byte velocity) { - // Do not start a not unless there is an instrument set up - if (!_instrument.data) { - return; - } - - // Allocate a voice channel - VoiceChannel *voice = _owner->allocateVoice(_priority); - if (!voice) { - return; - } - addVoice(voice); - - voice->note = note; - // This completly ignores the note's volume, but is in accordance - // to the original. - voice->out.volume = _volume; - - // Set up the instrument data - voice->out.baseFrequency = _instrument.baseFrequency; - voice->out.soundStart = _instrument.data; - voice->out.soundEnd = _instrument.data + _instrument.length; - if (_instrument.loopEnd && _instrument.loopEnd - 12 > _instrument.loopStart) { - voice->out.loopStart = _instrument.data + _instrument.loopStart; - voice->out.loopEnd = _instrument.data + _instrument.loopEnd; - } else { - voice->out.loopStart = 0; - voice->out.loopEnd = voice->out.soundEnd; - } - - voice->out.start = voice->out.loopStart; - voice->out.end = voice->out.loopEnd; - - // Set up the pitch - _owner->setPitch(&voice->out, (note << 7) + _pitchBend); - - // Set up the sample position - voice->out.instrument = voice->out.soundStart; - voice->out.subPos = 0; -} - -void MacM68kDriver::MidiChannel_MacM68k::programChange(byte program) { - _instrument = _owner->getInstrument(program + kProgramChangeBase); -} - -void MacM68kDriver::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) { - switch (control) { - // volume change - case 7: - _volume = value; - for (VoiceChannel *i = _voice; i; i = i->next) { - i->out.volume = value; - i->out.isFinished = false; - } - break; - - // sustain - case 64: - _sustain = value; - if (!_sustain) { - for (VoiceChannel *i = _voice; i; i = i->next) { - if (i->sustainNoteOff) { - i->off(); - } - } - } - break; - - // all notes off - case 123: - for (VoiceChannel *i = _voice; i; i = i->next) { - i->off(); - } - break; - - default: - break; - } -} - -void MacM68kDriver::MidiChannel_MacM68k::pitchBendFactor(byte value) { - _pitchBendFactor = value; -} - -void MacM68kDriver::MidiChannel_MacM68k::priority(byte value) { - _priority = value; -} - -void MacM68kDriver::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() { - if (_allocated) { - return false; - } - - _allocated = true; - _voice = 0; - _priority = 0; - memset(&_instrument, 0, sizeof(_instrument)); - _pitchBend = 0; - _pitchBendFactor = 0; - _volume = 0; - return true; -} - -void MacM68kDriver::MidiChannel_MacM68k::addVoice(VoiceChannel *voice) { - voice->next = _voice; - voice->prev = 0; - voice->part = this; - if (_voice) { - _voice->prev = voice; - } - _voice = voice; -} - -void MacM68kDriver::MidiChannel_MacM68k::removeVoice(VoiceChannel *voice) { - VoiceChannel *i = _voice; - while (i && i != voice) { - i = i->next; - } - - if (i) { - if (i->next) { - i->next->prev = i->prev; - } - - if (i->prev) { - i->prev->next = i->next; - } else { - _voice = i->next; - } - } -} - -MacM68kDriver::VoiceChannel *MacM68kDriver::allocateVoice(int priority) { - VoiceChannel *channel = 0; - for (int i = 0; i < kChannelCount; ++i) { - if (++_lastUsedVoiceChannel == kChannelCount) { - _lastUsedVoiceChannel = 0; - } - - VoiceChannel *cur = &_voiceChannels[_lastUsedVoiceChannel]; - if (!cur->part) { - memset(cur, 0, sizeof(*cur)); - return cur; - } else if (!cur->next) { - if (cur->part->_priority <= priority) { - priority = cur->part->_priority; - channel = cur; - } - } - } - - if (channel) { - channel->off(); - memset(channel, 0, sizeof(*channel)); - } - - return channel; -} - -const int MacM68kDriver::_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, - 76, 83, 89, 96, 104, 111, 119, 127 -}; - -} // End of namespace Scumm diff --git a/engines/scumm/imuse/mac_m68k.h b/engines/scumm/imuse/mac_m68k.h deleted file mode 100644 index 31beaf4e66..0000000000 --- a/engines/scumm/imuse/mac_m68k.h +++ /dev/null @@ -1,178 +0,0 @@ -/* 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 SCUMM_IMUSE_MAC_M68K_H -#define SCUMM_IMUSE_MAC_M68K_H - -#include "audio/softsynth/emumidi.h" - -#include "common/hashmap.h" - -namespace Common { -class SeekableReadStream; -} - -namespace Scumm { - -class MacM68kDriver : public MidiDriver_Emulated { - friend class MidiChannel_MacM68k; -public: - MacM68kDriver(Audio::Mixer *mixer); - ~MacM68kDriver(); - - virtual int open(); - virtual void close(); - - virtual void send(uint32 d); - virtual void sysEx_customInstrument(byte channel, uint32 type, const byte *instr); - - virtual MidiChannel *allocateChannel(); - virtual MidiChannel *getPercussionChannel() { return 0; } - - virtual bool isStereo() const { return false; } - virtual int getRate() const { - // The original is using a frequency of approx. 22254.54546 here. - // To be precise it uses the 16.16 fixed point value 0x56EE8BA3. - return 22254; - } - -protected: - virtual void generateSamples(int16 *buf, int len); - virtual void onTimer() {} - -private: - int *_mixBuffer; - int _mixBufferLength; - - struct Instrument { - uint length; - uint sampleRate; - uint loopStart; - uint loopEnd; - int baseFrequency; - - byte *data; - }; - - enum { - kDefaultInstrument = 0x3E7, - kProgramChangeBase = 0x3E8, - kSysExBase = 0x7D0 - }; - - Instrument getInstrument(int idx) const; - typedef Common::HashMap InstrumentMap; - InstrumentMap _instruments; - Instrument _defaultInstrument; - void loadAllInstruments(); - void addInstrument(int idx, Common::SeekableReadStream *data); - - struct OutputChannel { - int pitchModifier; - - const byte *instrument; - uint subPos; - - const byte *start; - const byte *end; - - const byte *soundStart; - const byte *soundEnd; - const byte *loopStart; - const byte *loopEnd; - - int frequency; - int volume; - - bool isFinished; - - int baseFrequency; - }; - - void setPitch(OutputChannel *out, int frequency); - int _pitchTable[128]; - - byte *_volumeTable; - static const int _volumeBaseTable[32]; - - class MidiChannel_MacM68k; - - struct VoiceChannel { - MidiChannel_MacM68k *part; - VoiceChannel *prev, *next; - int channel; - int note; - bool sustainNoteOff; - OutputChannel out; - - void off(); - }; - - class MidiChannel_MacM68k : public MidiChannel { - friend class MacM68kDriver; - public: - virtual MidiDriver *device() { return _owner; } - virtual byte getNumber() { return _number; } - virtual void release(); - - virtual void send(uint32 b); - virtual void noteOff(byte note); - virtual void noteOn(byte note, byte velocity); - virtual void programChange(byte program); - virtual void pitchBend(int16 bend); - virtual void controlChange(byte control, byte value); - virtual void pitchBendFactor(byte value); - virtual void priority(byte value); - virtual void sysEx_customInstrument(uint32 type, const byte *instr); - - void init(MacM68kDriver *owner, byte channel); - bool allocate(); - - void addVoice(VoiceChannel *voice); - void removeVoice(VoiceChannel *voice); - private: - MacM68kDriver *_owner; - bool _allocated; - int _number; - - VoiceChannel *_voice; - int _priority; - int _sustain; - Instrument _instrument; - int _pitchBend; - int _pitchBendFactor; - int _volume; - }; - - MidiChannel_MacM68k _channels[32]; - - enum { - kChannelCount = 8 - }; - VoiceChannel _voiceChannels[kChannelCount]; - int _lastUsedVoiceChannel; - VoiceChannel *allocateVoice(int priority); -}; - -} // End of namespace Scumm - -#endif diff --git a/engines/scumm/imuse/pcspk.cpp b/engines/scumm/imuse/pcspk.cpp deleted file mode 100644 index 856b771990..0000000000 --- a/engines/scumm/imuse/pcspk.cpp +++ /dev/null @@ -1,835 +0,0 @@ -/* 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 "scumm/imuse/pcspk.h" - -#include "common/util.h" - -namespace Scumm { - -PcSpkDriver::PcSpkDriver(Audio::Mixer *mixer) - : MidiDriver_Emulated(mixer), _pcSpk(mixer->getOutputRate()) { -} - -PcSpkDriver::~PcSpkDriver() { - close(); -} - -int PcSpkDriver::open() { - if (_isOpen) - return MERR_ALREADY_OPEN; - - MidiDriver_Emulated::open(); - - for (uint i = 0; i < 6; ++i) - _channels[i].init(this, i); - _activeChannel = 0; - _effectTimer = 0; - _randBase = 1; - - // We need to take care we only send note frequencies, when the internal - // settings actually changed, thus we need some extra state to keep track - // of that. - _lastActiveChannel = 0; - _lastActiveOut = 0; - - // We set the output sound type to music here to allow sound volume - // adjustment. The drawback here is that we can not control the music and - // sfx separately here. But the AdLib output has the same issue so it - // should not be that bad. - _mixer->playStream(Audio::Mixer::kMusicSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); - return 0; -} - -void PcSpkDriver::close() { - if (!_isOpen) - return; - _isOpen = false; - - _mixer->stopHandle(_mixerSoundHandle); -} - -void PcSpkDriver::send(uint32 d) { - assert((d & 0x0F) < 6); - _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() { - for (uint i = 0; i < 6; ++i) { - if (_channels[i].allocate()) - return &_channels[i]; - } - - return 0; -} - -void PcSpkDriver::generateSamples(int16 *buf, int len) { - _pcSpk.readBuffer(buf, len); -} - -void PcSpkDriver::onTimer() { - if (!_activeChannel) - return; - - for (uint i = 0; i < 6; ++i) { - OutputChannel &out = _channels[i]._out; - - if (!out.active) - continue; - - if (out.length == 0 || --out.length != 0) { - if (out.unkB && out.unkC) { - out.unkA += out.unkB; - if (out.instrument) - out.unkE = ((int8)out.instrument[out.unkA] * out.unkC) >> 4; - } - - ++_effectTimer; - if (_effectTimer > 3) { - _effectTimer = 0; - - if (out.effectEnvelopeA.state) - updateEffectGenerator(_channels[i], out.effectEnvelopeA, out.effectDefA); - if (out.effectEnvelopeB.state) - updateEffectGenerator(_channels[i], out.effectEnvelopeB, out.effectDefB); - } - } else { - out.active = 0; - updateNote(); - return; - } - } - - if (_activeChannel->_tl) { - output((_activeChannel->_out.note << 7) + _activeChannel->_pitchBend + _activeChannel->_out.unk60 + _activeChannel->_out.unkE); - } else { - _pcSpk.stop(); - _lastActiveChannel = 0; - _lastActiveOut = 0; - } -} - -void PcSpkDriver::updateNote() { - uint8 priority = 0; - _activeChannel = 0; - 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 (_activeChannel == 0 || _activeChannel->_tl == 0) { - _pcSpk.stop(); - _lastActiveChannel = 0; - _lastActiveOut = 0; - } else { - output(_activeChannel->_pitchBend + (_activeChannel->_out.note << 7)); - } -} - -void PcSpkDriver::output(uint16 out) { - byte v1 = (out >> 7) & 0xFF; - byte v2 = (out >> 2) & 0x1E; - - byte shift = _outputTable1[v1]; - uint16 indexBase = _outputTable2[v1] << 5; - uint16 frequency = _frequencyTable[(indexBase + v2) / 2] >> shift; - - // Only output in case the active channel changed or the frequency changed. - // This is not faithful to the original. Since our timings differ we would - // get distorted sound otherwise though. - if (_lastActiveChannel != _activeChannel || _lastActiveOut != out) { - _pcSpk.play(Audio::PCSpeaker::kWaveFormSquare, 1193180 / frequency, -1); - _lastActiveChannel = _activeChannel; - _lastActiveOut = out; - } -} - -void PcSpkDriver::MidiChannel_PcSpk::init(PcSpkDriver *owner, byte channel) { - _owner = owner; - _channel = channel; - _allocated = false; - memset(&_out, 0, sizeof(_out)); -} - -bool PcSpkDriver::MidiChannel_PcSpk::allocate() { - if (_allocated) - return false; - - memset(&_out, 0, sizeof(_out)); - memset(_instrument, 0, sizeof(_instrument)); - _out.effectDefA.envelope = &_out.effectEnvelopeA; - _out.effectDefB.envelope = &_out.effectEnvelopeB; - - _allocated = true; - return true; -} - -MidiDriver *PcSpkDriver::MidiChannel_PcSpk::device() { - return _owner; -} - -byte PcSpkDriver::MidiChannel_PcSpk::getNumber() { - return _channel; -} - -void PcSpkDriver::MidiChannel_PcSpk::release() { - _out.active = 0; - _allocated = false; - _owner->updateNote(); -} - -void PcSpkDriver::MidiChannel_PcSpk::send(uint32 b) { - uint8 type = b & 0xF0; - uint8 p1 = (b >> 8) & 0xFF; - uint8 p2 = (b >> 16) & 0xFF; - - switch (type) { - case 0x80: - noteOff(p1); - break; - - case 0x90: - if (p2) - noteOn(p1, p2); - else - noteOff(p1); - break; - - case 0xB0: - controlChange(p1, p2); - break; - - case 0xE0: - pitchBend((p1 | (p2 << 7)) - 0x2000); - break; - - default: - break; - } -} - -void PcSpkDriver::MidiChannel_PcSpk::noteOff(byte note) { - if (!_allocated) - return; - - if (_sustain) { - if (_out.note == note) - _out.sustainNoteOff = 1; - } else { - if (_out.note == note) { - _out.active = 0; - _owner->updateNote(); - } - } -} - -void PcSpkDriver::MidiChannel_PcSpk::noteOn(byte note, byte velocity) { - if (!_allocated) - return; - - _out.note = note; - _out.sustainNoteOff = 0; - _out.length = _instrument[0]; - - if (_instrument[4] * 256 < ARRAYSIZE(PcSpkDriver::_outInstrumentData)) - _out.instrument = _owner->_outInstrumentData + _instrument[4] * 256; - else - _out.instrument = 0; - - _out.unkA = 0; - _out.unkB = _instrument[1]; - _out.unkC = _instrument[2]; - _out.unkE = 0; - _out.unk60 = 0; - _out.active = 1; - - // In case we get a note on event on the last active channel, we reset the - // last active channel, thus we assure the frequency is correctly set, even - // when the same note was sent. - if (_owner->_lastActiveChannel == this) { - _owner->_lastActiveChannel = 0; - _owner->_lastActiveOut = 0; - } - _owner->updateNote(); - - _out.unkC += PcSpkDriver::getEffectModifier(_instrument[3] + ((velocity & 0xFE) << 4)); - if (_out.unkC > 63) - _out.unkC = 63; - - if ((_instrument[5] & 0x80) != 0) - _owner->setupEffects(*this, _out.effectEnvelopeA, _out.effectDefA, _instrument[5], _instrument + 6); - - if ((_instrument[14] & 0x80) != 0) - _owner->setupEffects(*this, _out.effectEnvelopeB, _out.effectDefB, _instrument[14], _instrument + 15); -} - -void PcSpkDriver::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) { - _pitchBend = (bend * _pitchBendFactor) >> 6; -} - -void PcSpkDriver::MidiChannel_PcSpk::controlChange(byte control, byte value) { - switch (control) { - case 1: - if (_out.effectEnvelopeA.state && _out.effectDefA.useModWheel) - _out.effectEnvelopeA.modWheelState = (value >> 2); - if (_out.effectEnvelopeB.state && _out.effectDefB.useModWheel) - _out.effectEnvelopeB.modWheelState = (value >> 2); - break; - - case 7: - _tl = value; - if (_owner->_activeChannel == this) { - if (_tl == 0) { - _owner->_lastActiveChannel = 0; - _owner->_lastActiveOut = 0; - _owner->_pcSpk.stop(); - } else { - _owner->output((_out.note << 7) + _pitchBend + _out.unk60 + _out.unkE); - } - } - break; - - case 64: - _sustain = value; - if (!value && _out.sustainNoteOff) { - _out.active = 0; - _owner->updateNote(); - } - break; - - case 123: - _out.active = 0; - _owner->updateNote(); - break; - - default: - break; - } -} - -void PcSpkDriver::MidiChannel_PcSpk::pitchBendFactor(byte value) { - _pitchBendFactor = value; -} - -void PcSpkDriver::MidiChannel_PcSpk::priority(byte value) { - _priority = value; -} - -void PcSpkDriver::MidiChannel_PcSpk::sysEx_customInstrument(uint32 type, const byte *instr) { - memcpy(_instrument, instr, sizeof(_instrument)); -} - -uint8 PcSpkDriver::getEffectModifier(uint16 level) { - uint8 base = level / 32; - uint8 index = level % 32; - - if (index == 0) - return 0; - - return (base * (index + 1)) >> 5; -} - -int16 PcSpkDriver::getEffectModLevel(int16 level, int8 mod) { - if (!mod) { - return 0; - } else if (mod == 31) { - return level; - } else if (level < -63 || level > 63) { - return (mod * (level + 1)) >> 6; - } else if (mod < 0) { - if (level < 0) - return getEffectModifier(((-level) << 5) - mod); - else - return -getEffectModifier((level << 5) - mod); - } else { - if (level < 0) - return -getEffectModifier(((-level) << 5) + mod); - else - return getEffectModifier(((-level) << 5) + mod); - } -} - -int16 PcSpkDriver::getRandScale(int16 input) { - if (_randBase & 1) - _randBase = (_randBase >> 1) ^ 0xB8; - else - _randBase >>= 1; - - return (_randBase * input) >> 8; -} - -void PcSpkDriver::setupEffects(MidiChannel_PcSpk &chan, EffectEnvelope &env, EffectDefinition &def, byte flags, const byte *data) { - def.phase = 0; - def.useModWheel = flags & 0x40; - env.loop = flags & 0x20; - def.type = flags & 0x1F; - - env.modWheelSensitivity = 31; - if (def.useModWheel) - env.modWheelState = chan._modWheel >> 2; - else - env.modWheelState = 31; - - switch (def.type) { - case 0: - env.maxLevel = 767; - env.startLevel = 383; - break; - - case 1: - env.maxLevel = 31; - env.startLevel = 15; - break; - - case 2: - env.maxLevel = 63; - env.startLevel = chan._out.unkB; - break; - - case 3: - env.maxLevel = 63; - env.startLevel = chan._out.unkC; - break; - - case 4: - env.maxLevel = 3; - env.startLevel = chan._instrument[4]; - break; - - case 5: - env.maxLevel = 62; - env.startLevel = 31; - env.modWheelState = 0; - break; - - case 6: - env.maxLevel = 31; - env.startLevel = 0; - env.modWheelSensitivity = 0; - break; - - default: - break; - } - - startEffect(env, data); -} - -void PcSpkDriver::startEffect(EffectEnvelope &env, const byte *data) { - env.state = 1; - env.currentLevel = 0; - env.modWheelLast = 31; - env.duration = data[0] * 63; - - env.stateTargetLevels[0] = data[1]; - env.stateTargetLevels[1] = data[3]; - env.stateTargetLevels[2] = data[5]; - env.stateTargetLevels[3] = data[6]; - - env.stateModWheelLevels[0] = data[2]; - env.stateModWheelLevels[1] = data[4]; - env.stateModWheelLevels[2] = 0; - env.stateModWheelLevels[3] = data[7]; - - initNextEnvelopeState(env); -} - -void PcSpkDriver::initNextEnvelopeState(EffectEnvelope &env) { - uint8 lastState = env.state - 1; - - uint16 stepCount = _effectEnvStepTable[getEffectModifier(((env.stateTargetLevels[lastState] & 0x7F) << 5) + env.modWheelSensitivity)]; - if (env.stateTargetLevels[lastState] & 0x80) - stepCount = getRandScale(stepCount); - if (!stepCount) - stepCount = 1; - - env.stateNumSteps = env.stateStepCounter = stepCount; - - int16 totalChange = 0; - if (lastState != 2) { - totalChange = getEffectModLevel(env.maxLevel, (env.stateModWheelLevels[lastState] & 0x7F) - 31); - if (env.stateModWheelLevels[lastState] & 0x80) - totalChange = getRandScale(totalChange); - - if (totalChange + env.startLevel > env.maxLevel) - totalChange = env.maxLevel - env.startLevel; - else if (totalChange + env.startLevel < 0) - totalChange = -env.startLevel; - - totalChange -= env.currentLevel; - } - - env.changePerStep = totalChange / stepCount; - if (totalChange < 0) { - totalChange = -totalChange; - env.dir = -1; - } else { - env.dir = 1; - } - env.changePerStepRem = totalChange % stepCount; - env.changeCountRem = 0; -} - -void PcSpkDriver::updateEffectGenerator(MidiChannel_PcSpk &chan, EffectEnvelope &env, EffectDefinition &def) { - if (advanceEffectEnvelope(env, def) & 1) { - switch (def.type) { - case 0: case 1: - chan._out.unk60 = def.phase << 4; - break; - - case 2: - chan._out.unkB = (def.phase & 0xFF) + chan._instrument[1]; - break; - - case 3: - chan._out.unkC = (def.phase & 0xFF) + chan._instrument[2]; - break; - - case 4: - if ((chan._instrument[4] + (def.phase & 0xFF)) * 256 < ARRAYSIZE(_outInstrumentData)) - chan._out.instrument = _outInstrumentData + (chan._instrument[4] + (def.phase & 0xFF)) * 256; - else - chan._out.instrument = 0; - break; - - case 5: - env.modWheelState = (def.phase & 0xFF); - break; - - case 6: - env.modWheelSensitivity = (def.phase & 0xFF); - break; - - default: - break; - } - } -} - -uint8 PcSpkDriver::advanceEffectEnvelope(EffectEnvelope &env, EffectDefinition &def) { - if (env.duration != 0) { - env.duration -= 17; - if (env.duration <= 0) { - env.state = 0; - return 0; - } - } - - uint8 changedFlags = 0; - int16 newLevel = env.currentLevel + env.changePerStep; - env.changeCountRem += env.changePerStepRem; - if (env.changeCountRem >= env.stateNumSteps) { - env.changeCountRem -= env.stateNumSteps; - newLevel += env.dir; - } - - if (env.currentLevel != newLevel || env.modWheelLast != env.modWheelState) { - env.currentLevel = newLevel; - env.modWheelLast = env.modWheelState; - - int16 newPhase = getEffectModLevel(newLevel, env.modWheelState); - if (def.phase != newPhase) { - changedFlags |= 1; - def.phase = newPhase; - } - } - - --env.stateStepCounter; - if (!env.stateStepCounter) { - ++env.state; - if (env.state > 4) { - if (env.loop) { - env.state = 1; - changedFlags |= 2; - } else { - env.state = 0; - return changedFlags; - } - } - - initNextEnvelopeState(env); - } - - return changedFlags; -} - -const byte PcSpkDriver::_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, - 0x46, 0x49, 0x4B, 0x4E, 0x50, 0x52, 0x55, 0x57, - 0x59, 0x5B, 0x5E, 0x60, 0x62, 0x64, 0x66, 0x67, - 0x69, 0x6B, 0x6C, 0x6E, 0x70, 0x71, 0x72, 0x74, - 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7B, - 0x7C, 0x7D, 0x7D, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, - 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7D, 0x7D, - 0x7C, 0x7B, 0x7B, 0x7A, 0x79, 0x78, 0x77, 0x76, - 0x75, 0x74, 0x72, 0x71, 0x70, 0x6E, 0x6C, 0x6B, - 0x69, 0x67, 0x66, 0x64, 0x62, 0x60, 0x5E, 0x5B, - 0x59, 0x57, 0x55, 0x52, 0x50, 0x4E, 0x4B, 0x49, - 0x46, 0x43, 0x41, 0x3E, 0x3B, 0x39, 0x36, 0x33, - 0x30, 0x2D, 0x2A, 0x27, 0x24, 0x21, 0x1E, 0x1B, - 0x18, 0x15, 0x12, 0x0F, 0x0C, 0x09, 0x06, 0x03, - 0x00, 0xFD, 0xFA, 0xF7, 0xF4, 0xF1, 0xEE, 0xEB, - 0xE8, 0xE5, 0xE2, 0xDF, 0xDC, 0xD9, 0xD6, 0xD3, - 0xD0, 0xCD, 0xCA, 0xC7, 0xC5, 0xC2, 0xBF, 0xBD, - 0xBA, 0xB7, 0xB5, 0xB2, 0xB0, 0xAE, 0xAB, 0xA9, - 0xA7, 0xA5, 0xA2, 0xA0, 0x9E, 0x9C, 0x9A, 0x99, - 0x97, 0x95, 0x94, 0x92, 0x90, 0x8F, 0x8E, 0x8C, - 0x8B, 0x8A, 0x89, 0x88, 0x87, 0x86, 0x85, 0x85, - 0x84, 0x83, 0x83, 0x82, 0x82, 0x82, 0x82, 0x82, - 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x83, 0x83, - 0x84, 0x85, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, - 0x8B, 0x8C, 0x8E, 0x8F, 0x90, 0x92, 0x94, 0x95, - 0x97, 0x99, 0x9A, 0x9C, 0x9E, 0xA0, 0xA2, 0xA5, - 0xA7, 0xA9, 0xAB, 0xAE, 0xB0, 0xB2, 0xB5, 0xB7, - 0xBA, 0xBD, 0xBF, 0xC2, 0xC5, 0xC7, 0xCA, 0xCD, - 0xD0, 0xD3, 0xD6, 0xD9, 0xDC, 0xDF, 0xE2, 0xE5, - 0xE8, 0xEB, 0xEE, 0xF1, 0xF4, 0xF7, 0xFA, 0xFD, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, - 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, - 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, - 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, - 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, - 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, - 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, - 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, - 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, - 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, - 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, - 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, - 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, - 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, - 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, - 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, - 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, - 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, - 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, - 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, - 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, - 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, - 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, - 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, - 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, - 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, - 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, - 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, - 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, - 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, - 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, - 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, - 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, - 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, - 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, - 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, - 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, - 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, - 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, - 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, - 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, - 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, - 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, - 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, - 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, - 0x29, 0x23, 0xBE, 0x84, 0xE1, 0x6C, 0xD6, 0xAE, - 0x52, 0x90, 0x49, 0xF1, 0xF1, 0xBB, 0xE9, 0xEB, - 0xB3, 0xA6, 0xDB, 0x3C, 0x87, 0x0C, 0x3E, 0x99, - 0x24, 0x5E, 0x0D, 0x1C, 0x06, 0xB7, 0x47, 0xDE, - 0xB3, 0x12, 0x4D, 0xC8, 0x43, 0xBB, 0x8B, 0xA6, - 0x1F, 0x03, 0x5A, 0x7D, 0x09, 0x38, 0x25, 0x1F, - 0x5D, 0xD4, 0xCB, 0xFC, 0x96, 0xF5, 0x45, 0x3B, - 0x13, 0x0D, 0x89, 0x0A, 0x1C, 0xDB, 0xAE, 0x32, - 0x20, 0x9A, 0x50, 0xEE, 0x40, 0x78, 0x36, 0xFD, - 0x12, 0x49, 0x32, 0xF6, 0x9E, 0x7D, 0x49, 0xDC, - 0xAD, 0x4F, 0x14, 0xF2, 0x44, 0x40, 0x66, 0xD0, - 0x6B, 0xC4, 0x30, 0xB7, 0x32, 0x3B, 0xA1, 0x22, - 0xF6, 0x22, 0x91, 0x9D, 0xE1, 0x8B, 0x1F, 0xDA, - 0xB0, 0xCA, 0x99, 0x02, 0xB9, 0x72, 0x9D, 0x49, - 0x2C, 0x80, 0x7E, 0xC5, 0x99, 0xD5, 0xE9, 0x80, - 0xB2, 0xEA, 0xC9, 0xCC, 0x53, 0xBF, 0x67, 0xD6, - 0xBF, 0x14, 0xD6, 0x7E, 0x2D, 0xDC, 0x8E, 0x66, - 0x83, 0xEF, 0x57, 0x49, 0x61, 0xFF, 0x69, 0x8F, - 0x61, 0xCD, 0xD1, 0x1E, 0x9D, 0x9C, 0x16, 0x72, - 0x72, 0xE6, 0x1D, 0xF0, 0x84, 0x4F, 0x4A, 0x77, - 0x02, 0xD7, 0xE8, 0x39, 0x2C, 0x53, 0xCB, 0xC9, - 0x12, 0x1E, 0x33, 0x74, 0x9E, 0x0C, 0xF4, 0xD5, - 0xD4, 0x9F, 0xD4, 0xA4, 0x59, 0x7E, 0x35, 0xCF, - 0x32, 0x22, 0xF4, 0xCC, 0xCF, 0xD3, 0x90, 0x2D, - 0x48, 0xD3, 0x8F, 0x75, 0xE6, 0xD9, 0x1D, 0x2A, - 0xE5, 0xC0, 0xF7, 0x2B, 0x78, 0x81, 0x87, 0x44, - 0x0E, 0x5F, 0x50, 0x00, 0xD4, 0x61, 0x8D, 0xBE, - 0x7B, 0x05, 0x15, 0x07, 0x3B, 0x33, 0x82, 0x1F, - 0x18, 0x70, 0x92, 0xDA, 0x64, 0x54, 0xCE, 0xB1, - 0x85, 0x3E, 0x69, 0x15, 0xF8, 0x46, 0x6A, 0x04, - 0x96, 0x73, 0x0E, 0xD9, 0x16, 0x2F, 0x67, 0x68, - 0xD4, 0xF7, 0x4A, 0x4A, 0xD0, 0x57, 0x68, 0x76 -}; - -const byte PcSpkDriver::_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, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, - 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, - 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, - 6, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 6, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7 -}; - -const byte PcSpkDriver::_outputTable2[] = { - 0, 1, 2, 3, - 4, 5, 6, 7, - 8, 9, 10, 11, - 0, 1, 2, 3, - 4, 5, 6, 7, - 8, 9, 10, 11, - 0, 1, 2, 3, - 4, 5, 6, 7, - 8, 9, 10, 11, - 0, 1, 2, 3, - 4, 5, 6, 7, - 8, 9, 10, 11, - 0, 1, 2, 3, - 4, 5, 6, 7, - 8, 9, 10, 11, - 0, 1, 2, 3, - 4, 5, 6, 7, - 8, 9, 10, 11, - 0, 1, 2, 3, - 4, 5, 6, 7, - 8, 9, 10, 11, - 0, 1, 2, 3, - 4, 5, 6, 7, - 8, 9, 10, 11, - 0, 1, 2, 3, - 4, 5, 6, 7, - 8, 9, 10, 11, - 0, 1, 2, 3, - 4, 5, 6, 7, - 8, 9, 10, 11, - 0, 1, 2, 3, - 4, 5, 6, 7 -}; - -const uint16 PcSpkDriver::_effectEnvStepTable[] = { - 1, 2, 4, 5, - 6, 7, 8, 9, - 10, 12, 14, 16, - 18, 21, 24, 30, - 36, 50, 64, 82, - 100, 136, 160, 192, - 240, 276, 340, 460, - 600, 860, 1200, 1600 -}; - -const uint16 PcSpkDriver::_frequencyTable[] = { - 0x8E84, 0x8E00, 0x8D7D, 0x8CFA, - 0x8C78, 0x8BF7, 0x8B76, 0x8AF5, - 0x8A75, 0x89F5, 0x8976, 0x88F7, - 0x8879, 0x87FB, 0x877D, 0x8700, - 0x8684, 0x8608, 0x858C, 0x8511, - 0x8496, 0x841C, 0x83A2, 0x8328, - 0x82AF, 0x8237, 0x81BF, 0x8147, - 0x80D0, 0x8059, 0x7FE3, 0x7F6D, - 0x7EF7, 0x7E82, 0x7E0D, 0x7D99, - 0x7D25, 0x7CB2, 0x7C3F, 0x7BCC, - 0x7B5A, 0x7AE8, 0x7A77, 0x7A06, - 0x7995, 0x7925, 0x78B5, 0x7846, - 0x77D7, 0x7768, 0x76FA, 0x768C, - 0x761F, 0x75B2, 0x7545, 0x74D9, - 0x746D, 0x7402, 0x7397, 0x732C, - 0x72C2, 0x7258, 0x71EF, 0x7186, - 0x711D, 0x70B5, 0x704D, 0x6FE5, - 0x6F7E, 0x6F17, 0x6EB0, 0x6E4A, - 0x6DE5, 0x6D7F, 0x6D1A, 0x6CB5, - 0x6C51, 0x6BED, 0x6B8A, 0x6B26, - 0x6AC4, 0x6A61, 0x69FF, 0x699D, - 0x693C, 0x68DB, 0x687A, 0x681A, - 0x67BA, 0x675A, 0x66FA, 0x669B, - 0x663D, 0x65DF, 0x6581, 0x6523, - 0x64C6, 0x6469, 0x640C, 0x63B0, - 0x6354, 0x62F8, 0x629D, 0x6242, - 0x61E7, 0x618D, 0x6133, 0x60D9, - 0x6080, 0x6027, 0x5FCE, 0x5F76, - 0x5F1E, 0x5EC6, 0x5E6E, 0x5E17, - 0x5DC1, 0x5D6A, 0x5D14, 0x5CBE, - 0x5C68, 0x5C13, 0x5BBE, 0x5B6A, - 0x5B15, 0x5AC1, 0x5A6E, 0x5A1A, - 0x59C7, 0x5974, 0x5922, 0x58CF, - 0x587D, 0x582C, 0x57DA, 0x5789, - 0x5739, 0x56E8, 0x5698, 0x5648, - 0x55F9, 0x55A9, 0x555A, 0x550B, - 0x54BD, 0x546F, 0x5421, 0x53D3, - 0x5386, 0x5339, 0x52EC, 0x52A0, - 0x5253, 0x5207, 0x51BC, 0x5170, - 0x5125, 0x50DA, 0x5090, 0x5046, - 0x4FFB, 0x4FB2, 0x4F68, 0x4F1F, - 0x4ED6, 0x4E8D, 0x4E45, 0x4DFC, - 0x4DB5, 0x4D6D, 0x4D25, 0x4CDE, - 0x4C97, 0x4C51, 0x4C0A, 0x4BC4, - 0x4B7E, 0x4B39, 0x4AF3, 0x4AAE, - 0x4A69, 0x4A24, 0x49E0, 0x499C, - 0x4958, 0x4914, 0x48D1, 0x488E, - 0x484B, 0x4808, 0x47C6, 0x4783 -}; - -} // End of namespace Scumm diff --git a/engines/scumm/imuse/pcspk.h b/engines/scumm/imuse/pcspk.h deleted file mode 100644 index 6a107e169a..0000000000 --- a/engines/scumm/imuse/pcspk.h +++ /dev/null @@ -1,161 +0,0 @@ -/* 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 SCUMM_IMUSE_PCSPK_H -#define SCUMM_IMUSE_PCSPK_H - -#include "audio/softsynth/emumidi.h" -#include "audio/softsynth/pcspk.h" - -namespace Scumm { - -class PcSpkDriver : public MidiDriver_Emulated { -public: - PcSpkDriver(Audio::Mixer *mixer); - ~PcSpkDriver(); - - virtual int open(); - virtual void close(); - - virtual void send(uint32 d); - virtual void sysEx_customInstrument(byte channel, uint32 type, const byte *instr); - - virtual MidiChannel *allocateChannel(); - virtual MidiChannel *getPercussionChannel() { return 0; } - - bool isStereo() const { return _pcSpk.isStereo(); } - int getRate() const { return _pcSpk.getRate(); } -protected: - void generateSamples(int16 *buf, int len); - void onTimer(); - -private: - Audio::PCSpeaker _pcSpk; - int _effectTimer; - uint8 _randBase; - - void updateNote(); - void output(uint16 out); - - static uint8 getEffectModifier(uint16 level); - int16 getEffectModLevel(int16 level, int8 mod); - int16 getRandScale(int16 input); - - struct EffectEnvelope { - uint8 state; - int16 currentLevel; - int16 duration; - int16 maxLevel; - int16 startLevel; - uint8 loop; - uint8 stateTargetLevels[4]; - uint8 stateModWheelLevels[4]; - uint8 modWheelSensitivity; - uint8 modWheelState; - uint8 modWheelLast; - int16 stateNumSteps; - int16 stateStepCounter; - int16 changePerStep; - int8 dir; - int16 changePerStepRem; - int16 changeCountRem; - }; - - struct EffectDefinition { - int16 phase; - uint8 type; - uint8 useModWheel; - EffectEnvelope *envelope; - }; - - struct OutputChannel { - uint8 active; - uint8 note; - uint8 sustainNoteOff; - uint8 length; - const uint8 *instrument; - uint8 unkA; - uint8 unkB; - uint8 unkC; - int16 unkE; - EffectEnvelope effectEnvelopeA; - EffectDefinition effectDefA; - EffectEnvelope effectEnvelopeB; - EffectDefinition effectDefB; - int16 unk60; - }; - - struct MidiChannel_PcSpk : public MidiChannel { - virtual MidiDriver *device(); - virtual byte getNumber(); - virtual void release(); - - virtual void send(uint32 b); - virtual void noteOff(byte note); - virtual void noteOn(byte note, byte velocity); - virtual void programChange(byte program); - virtual void pitchBend(int16 bend); - virtual void controlChange(byte control, byte value); - virtual void pitchBendFactor(byte value); - virtual void priority(byte value); - virtual void sysEx_customInstrument(uint32 type, const byte *instr); - - 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; - uint8 _sustain; - uint8 _pitchBendFactor; - int16 _pitchBend; - }; - - void setupEffects(MidiChannel_PcSpk &chan, EffectEnvelope &env, EffectDefinition &def, byte flags, const byte *data); - void startEffect(EffectEnvelope &env, const byte *data); - void initNextEnvelopeState(EffectEnvelope &env); - void updateEffectGenerator(MidiChannel_PcSpk &chan, EffectEnvelope &env, EffectDefinition &def); - uint8 advanceEffectEnvelope(EffectEnvelope &env, EffectDefinition &def); - - MidiChannel_PcSpk _channels[6]; - MidiChannel_PcSpk *_activeChannel; - - MidiChannel_PcSpk *_lastActiveChannel; - uint16 _lastActiveOut; - - static const byte _outInstrumentData[1024]; - static const byte _outputTable1[]; - static const byte _outputTable2[]; - static const uint16 _effectEnvStepTable[]; - static const uint16 _frequencyTable[]; -}; - -} // End of namespace Scumm - -#endif diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk index bcc1eba704..799176f869 100644 --- a/engines/scumm/module.mk +++ b/engines/scumm/module.mk @@ -28,11 +28,12 @@ MODULE_OBJS := \ imuse/imuse_part.o \ imuse/imuse_player.o \ imuse/instrument.o \ - imuse/mac_m68k.o \ - imuse/pcspk.o \ imuse/sysex_samnmax.o \ imuse/sysex_scumm.o \ imuse/drivers/amiga.o \ + imuse/drivers/fmtowns.o \ + imuse/drivers/mac_m68k.o \ + imuse/drivers/pcspk.o \ input.o \ midiparser_ro.o \ object.o \ diff --git a/engines/scumm/players/player_towns.h b/engines/scumm/players/player_towns.h index ad51c3e561..62835471ac 100644 --- a/engines/scumm/players/player_towns.h +++ b/engines/scumm/players/player_towns.h @@ -25,8 +25,8 @@ #include "scumm/scumm.h" #include "scumm/imuse/imuse.h" +#include "scumm/imuse/drivers/fmtowns.h" #include "audio/softsynth/fmtowns_pc98/towns_euphony.h" -#include "audio/softsynth/fmtowns_pc98/towns_midi.h" namespace Scumm { diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp index 64f45c1b98..e3919eee05 100644 --- a/engines/scumm/scumm.cpp +++ b/engines/scumm/scumm.cpp @@ -76,9 +76,10 @@ #include "scumm/he/cup_player_he.h" #include "scumm/util.h" #include "scumm/verbs.h" -#include "scumm/imuse/pcspk.h" -#include "scumm/imuse/mac_m68k.h" +#include "scumm/imuse/drivers/pcspk.h" +#include "scumm/imuse/drivers/mac_m68k.h" #include "scumm/imuse/drivers/amiga.h" +#include "scumm/imuse/drivers/fmtowns.h" #include "backends/audiocd/audiocd.h" @@ -2001,7 +2002,9 @@ void ScummEngine::setupMusic(int midi) { nativeMidiDriver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE); if (!useOnlyNative) { - if (_sound->_musicType == MDT_ADLIB || _sound->_musicType == MDT_TOWNS || multi_midi) { + 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)); adlibMidiDriver->property(MidiDriver::PROP_OLD_ADLIB, (_game.features & GF_SMALL_HEADER) ? 1 : 0); // Try to use OPL3 mode for Sam&Max when possible. -- cgit v1.2.3