scummvm/audio/softsynth/fmtowns_pc98/towns_midi.cpp
Max Horn 88913c0139 ALL: Remove trailing whitespaces
This tries to make our code a bit more compliant with our code formatting
conventions. For future use, this is the command I used:
  git ls-files "*.cpp" "*.h" | xargs sed -i -e 's/[ \t]*$//'
2011-06-20 00:59:48 +02:00

1036 lines
26 KiB
C++

/* 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 _prg;
uint8 _chanIndex;
uint8 _effectLevel;
uint8 _priority;
uint8 _ctrlVolume;
uint8 _tl;
uint8 _pan;
uint8 _panEff;
uint8 _percS;
int8 _transpose;
uint8 _fld_1f;
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), _prg(0), _chanIndex(chanIndex),
_effectLevel(0), _priority(0), _ctrlVolume(0), _tl(0), _pan(0), _panEff(0), _transpose(0), _percS(0), _pitchBendFactor(0), _pitchBend(0), _sustain(0), _freqLSB(0),
_fld_1f(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;
}