diff options
author | athrxx | 2011-05-01 03:57:31 +0200 |
---|---|---|
committer | Willem Jan Palenstijn | 2011-05-17 20:32:53 +0200 |
commit | d4325a0411f2f6c86a117543a4dcbf588ac84fe7 (patch) | |
tree | 54298fda04811461e0faa86b03ac15e2944cdd4b | |
parent | 2f9c5de7bedfc02c7b5a99da471dafa518a78379 (diff) | |
download | scummvm-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.cpp | 178 | ||||
-rw-r--r-- | audio/softsynth/fmtowns_pc98/towns_midi.h | 11 |
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 |