aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorathrxx2011-05-01 03:57:31 +0200
committerWillem Jan Palenstijn2011-05-17 20:32:53 +0200
commitd4325a0411f2f6c86a117543a4dcbf588ac84fe7 (patch)
tree54298fda04811461e0faa86b03ac15e2944cdd4b
parent2f9c5de7bedfc02c7b5a99da471dafa518a78379 (diff)
downloadscummvm-rg350-d4325a0411f2f6c86a117543a4dcbf588ac84fe7.tar.gz
scummvm-rg350-d4325a0411f2f6c86a117543a4dcbf588ac84fe7.tar.bz2
scummvm-rg350-d4325a0411f2f6c86a117543a4dcbf588ac84fe7.zip
FM-TOWNS AUDIO: Implement some more midi driver code
-rw-r--r--audio/softsynth/fmtowns_pc98/towns_midi.cpp178
-rw-r--r--audio/softsynth/fmtowns_pc98/towns_midi.h11
2 files changed, 163 insertions, 26 deletions
diff --git a/audio/softsynth/fmtowns_pc98/towns_midi.cpp b/audio/softsynth/fmtowns_pc98/towns_midi.cpp
index 737e977545..86ff7e20af 100644
--- a/audio/softsynth/fmtowns_pc98/towns_midi.cpp
+++ b/audio/softsynth/fmtowns_pc98/towns_midi.cpp
@@ -34,6 +34,7 @@ public:
void noteOn(uint8 msb, uint16 lsb);
void noteOnAdjust(uint8 msb, uint16 lsb);
void setupProgram(const uint8 *data, uint8 vol1, uint8 vol2);
+ void noteOnSubSubSub_s1(int index, uint8 c, const uint8 *instr);
void connect(TownsMidiInputChannel *chan);
void disconnect();
@@ -49,25 +50,45 @@ private:
void keyOn();
void keyOff();
void internKeyOnFrq(uint16 frq);
- void out(uint8 chan, uint8 reg, uint8 val);
+ void out(uint8 reg, uint8 val);
TownsMidiInputChannel *_midi;
TownsMidiOutputChannel *_prev;
TownsMidiOutputChannel *_next;
- uint8 _fld_f;
+ uint8 _fld_c;
+ uint8 _chan;
uint8 _note;
- uint8 _tl;
+ uint8 _tl2;
+ uint8 _tl1;
uint8 _noteOffMarker;
- uint8 _fld_12;
+ uint32 _duration;
uint8 _fld_13;
- uint8 _prg;
- uint8 _chan;
+ uint8 _prg;
uint16 _freq;
int16 _freqAdjust;
+ struct StateA {
+ uint8 a[50];
+ } *_stateA;
+
+ struct StateB {
+ uint8 b1;
+ uint8 b2;
+ uint8 b3;
+ uint8 b4;
+ uint8 b5;
+ uint8 b6;
+ uint8 b7;
+ uint8 b8;
+ uint8 b9;
+ uint8 b10;
+ uint8 b11;
+ } *_stateB;
+
MidiDriver_TOWNS *_driver;
+ static const uint8 _chanMap[];
static const uint8 _freqMSB[];
static const uint16 _freqLSB[];
};
@@ -105,24 +126,33 @@ private:
uint8 _effectLevel;
uint8 _priority;
uint8 _vol;
- uint8 _volEff;
+ uint8 _tl;
uint8 _pan;
uint8 _panEff;
- uint8 _perc;
+ int8 _transpose;
uint8 _percS;
uint8 _fld_22;
uint8 _pitchBendFactor;
+ uint16 _freqLSB;
bool _allocated;
MidiDriver_TOWNS *_driver;
+
+ static const uint8 _programAdjustLevel[];
};
TownsMidiOutputChannel::TownsMidiOutputChannel(MidiDriver_TOWNS *driver, int chanIndex) : _driver(driver), _chan(chanIndex),
- _midi(0), _prev(0), _next(0), _fld_f(0), _note(0), _tl(0), _noteOffMarker(0), _fld_12(0), _fld_13(0), _prg(0), _freq(0), _freqAdjust(0) {
+ _midi(0), _prev(0), _next(0), _fld_c(0), _tl2(0), _note(0), _tl1(0), _noteOffMarker(0), _duration(0), _fld_13(0), _prg(0), _freq(0), _freqAdjust(0) {
+ _stateA = new StateA[2];
+ memset(_stateA, 0, 2 * sizeof(StateA));
+ _stateB = new StateB[2];
+ memset(_stateB, 0, 2 * sizeof(StateB));
}
TownsMidiOutputChannel::~TownsMidiOutputChannel() {
+ delete[] _stateA;
+ delete[] _stateB;
}
void TownsMidiOutputChannel::noteOn(uint8 msb, uint16 lsb) {
@@ -137,8 +167,54 @@ void TownsMidiOutputChannel::noteOnAdjust(uint8 msb, uint16 lsb) {
}
void TownsMidiOutputChannel::setupProgram(const uint8 *data, uint8 vol1, uint8 vol2) {
+ static const uint8 mul[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 12, 12, 15, 15 };
const uint8 *pos = data;
+ uint8 chan = _chanMap[_chan];
+
+ uint8 mulAmsFms1 = _driver->_chanState[chan].mulAmsFms = *pos++;
+ uint8 tl1 = _driver->_chanState[chan].tl = (*pos++ | 0x3f) - vol1;
+ uint8 attDec1 = _driver->_chanState[chan].attDec = !(*pos++);
+ uint8 sus1 = _driver->_chanState[chan].sus = !(*pos++);
+ uint8 unk1 = _driver->_chanState[chan].unk = *pos++;
+ 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 = *pos++;
+ uint8 tl2 = _driver->_chanState[chan].tl = (*pos++ | 0x3f) - vol2;
+ uint8 attDec2 = _driver->_chanState[chan].attDec = !(*pos++);
+ uint8 sus2 = _driver->_chanState[chan].sus = !(*pos++);
+ uint8 unk2 = _driver->_chanState[chan].unk = *pos++;
+
+ 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);
+ }
+
+ uint8 t = _driver->_chanState[chan /*_chan*/ /*???*/].fgAlg = *pos;
+ out(0xb0, ((t & 0x0e) << 2) | (((t & 1) << 1) + 5));
+ t = mulAmsFms1 | mulAmsFms2;
+ out(0xb4, 0xc0 | ((t & 0x80) >> 3) | ((t & 0x40) >> 5));
+}
+void TownsMidiOutputChannel::noteOnSubSubSub_s1(int index, uint8 c, const uint8 *instr) {
+ StateA *a = &_stateA[index];
+ StateB *b = &_stateB[index];
}
void TownsMidiOutputChannel::connect(TownsMidiInputChannel *chan) {
@@ -177,34 +253,38 @@ int TownsMidiOutputChannel::checkPriority(int pri) {
}
void TownsMidiOutputChannel::keyOn() {
- out(_chan, 0x28, 0xf0/*0x30*/ /*???*/);
+ out(0x28, 0xf0/*0x30*/ /*???*/);
}
void TownsMidiOutputChannel::keyOff() {
- out(_chan, 0x28, 0);
+ out(0x28, 0);
}
void TownsMidiOutputChannel::internKeyOnFrq(uint16 frq) {
uint8 t = (frq << 1) >> 8;
frq = (_freqMSB[t] << 3) | _freqLSB[t] ;
- out(_chan, 0xa4, frq >> 8);
- out(_chan, 0xa0, frq & 0xff);
- out(_chan, 0x28, 0);
- out(_chan, 0x28, 0xf0/*0x30*/ /*???*/);
+ out(0xa4, frq >> 8);
+ out(0xa0, frq & 0xff);
+ out(0x28, 0);
+ out(0x28, 0xf0/*0x30*/ /*???*/);
}
-void TownsMidiOutputChannel::out(uint8 chan, uint8 reg, uint8 val) {
+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];
+ val = (val & 0xf0) | keyValOffs[_chan];
if (reg < 0x30)
- _driver->_intf->callback(19, 0, reg, val);
+ _driver->_intf->callback(17, 0, reg, val);
else
- _driver->_intf->callback(19, chan / 3, (reg & ~3) | chanRegOffs[chan], val);
+ _driver->_intf->callback(17, _chan / 3, (reg & ~3) | chanRegOffs[_chan], val);
}
+const uint8 TownsMidiOutputChannel::_chanMap[] = {
+ 0, 1, 2, 8, 9, 10
+};
+
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,
@@ -244,7 +324,7 @@ const uint16 TownsMidiOutputChannel::_freqLSB[] = {
};
TownsMidiInputChannel::TownsMidiInputChannel(MidiDriver_TOWNS *driver, int chanIndex) : MidiChannel(), _driver(driver), _outChan(0), _prg(0), _chanIndex(chanIndex),
- _effectLevel(0), _priority(0), _vol(0), _volEff(0), _pan(0), _panEff(0), _perc(0), _percS(0), _pitchBendFactor(0), _fld_22(0), _allocated(false) {
+ _effectLevel(0), _priority(0), _vol(0), _tl(0), _pan(0), _panEff(0), _transpose(0), _percS(0), _pitchBendFactor(0), _fld_22(0), _freqLSB(0), _allocated(false) {
_instrument = new uint8[30];
memset(_instrument, 0, 30);
}
@@ -289,12 +369,31 @@ void TownsMidiInputChannel::noteOn(byte note, byte velocity) {
oc->connect(this);
+ oc->_fld_c = _instrument[10] & 1;
+ oc->_note = note;
+ oc->_noteOffMarker = 0;
+ oc->_duration = _instrument[29] * 72;
- int vol1 = 0;
- int vol2 = 0;
- oc->setupProgram(_instrument, vol1, vol2);
- //oc->noteOn(m, l);
-
+ oc->_tl1 = (_instrument[1] & 0x3f) + _driver->_chanOutputLevel[((velocity >> 1) << 5) + (_instrument[4] >> 2)];
+ if (oc->_tl1 > 63)
+ oc->_tl1 = 63;
+
+ oc->_tl2 = (_instrument[6] & 0x3f) + _driver->_chanOutputLevel[((velocity >> 1) << 5) + (_instrument[9] >> 2)];
+ if (oc->_tl2 > 63)
+ oc->_tl2 = 63;
+
+ oc->setupProgram(_instrument, oc->_fld_c == 1 ? _programAdjustLevel[_driver->_chanOutputLevel[(_tl >> 2) + (oc->_tl1 << 5)]] : oc->_tl1, _programAdjustLevel[_driver->_chanOutputLevel[(_tl >> 2) + (oc->_tl2 << 5)]]);
+ oc->noteOn(note + _transpose, _freqLSB);
+
+ if (_instrument[11] & 0x80)
+ oc->noteOnSubSubSub_s1(0, _instrument[11], &_instrument[12]);
+ else
+ oc->_stateA[0].a[0] = 0;
+
+ if (_instrument[20] & 0x80)
+ oc->noteOnSubSubSub_s1(1, _instrument[20], &_instrument[21]);
+ else
+ oc->_stateA[1].a[0] = 0;
}
void TownsMidiInputChannel::programChange(byte program) {
@@ -321,16 +420,39 @@ void TownsMidiInputChannel::sysEx_customInstrument(uint32 type, const byte *inst
memcpy(_instrument, instr, 30);
}
+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) : _timerBproc(0), _timerBpara(0), _open(false) {
_intf = new TownsAudioInterface(mixer, this);
_channels = new TownsMidiInputChannel*[32];
for (int i = 0; i < 32; i++)
_channels[i] = new TownsMidiInputChannel(this, i);
+
_out = new TownsMidiOutputChannel*[6];
for (int i = 0; i < 6; i++)
_out[i] = new TownsMidiOutputChannel(this, i);
+ _chanState = new ChanState[32];
+ memset(_chanState, 0, 32 * sizeof(ChanState));
+
+ _chanOutputLevel = new uint8[2048];
+ for (int i = 0; i < 64; i++) {
+ for (int ii = 0; ii < 32; ii++)
+ _chanOutputLevel[(i << 5) + ii] = ((i * (ii + 1)) >> 5) & 0xff;
+ }
+ for (int i = 0; i < 64; i++)
+ _chanOutputLevel[i << 5] = 0;
+
_tickCounter = 0;
_curChan = 0;
}
@@ -343,9 +465,13 @@ MidiDriver_TOWNS::~MidiDriver_TOWNS() {
for (int i = 0; i < 32; i++)
delete _channels[i];
delete[] _channels;
+
for (int i = 0; i < 6; i++)
delete _out[i];
delete[] _out;
+
+ delete[] _chanState;
+ delete[] _chanOutputLevel;
}
int MidiDriver_TOWNS::open() {
@@ -478,4 +604,4 @@ TownsMidiOutputChannel *MidiDriver_TOWNS::allocateOutputChannel(int pri) {
res->disconnect();
return res;
-} \ No newline at end of file
+}
diff --git a/audio/softsynth/fmtowns_pc98/towns_midi.h b/audio/softsynth/fmtowns_pc98/towns_midi.h
index 115142911a..6ff8a99cda 100644
--- a/audio/softsynth/fmtowns_pc98/towns_midi.h
+++ b/audio/softsynth/fmtowns_pc98/towns_midi.h
@@ -61,6 +61,15 @@ private:
TownsMidiInputChannel **_channels;
TownsMidiOutputChannel **_out;
+ struct ChanState {
+ uint8 mulAmsFms;
+ uint8 tl;
+ uint8 attDec;
+ uint8 sus;
+ uint8 fgAlg;
+ uint8 unk;
+ } *_chanState;
+
Common::TimerManager::TimerProc _timerBproc;
void *_timerBpara;
@@ -70,6 +79,8 @@ private:
uint8 _curChan;
bool _open;
+
+ uint8 *_chanOutputLevel;
};
#endif