From 2f9c5de7bedfc02c7b5a99da471dafa518a78379 Mon Sep 17 00:00:00 2001 From: athrxx Date: Sat, 30 Apr 2011 21:11:30 +0200 Subject: FM-TOWNS AUDIO: Implement some midi driver functions --- audio/softsynth/fmtowns_pc98/towns_midi.cpp | 324 ++++++++++++++++++++++++---- audio/softsynth/fmtowns_pc98/towns_midi.h | 12 +- 2 files changed, 292 insertions(+), 44 deletions(-) diff --git a/audio/softsynth/fmtowns_pc98/towns_midi.cpp b/audio/softsynth/fmtowns_pc98/towns_midi.cpp index c223d44c11..737e977545 100644 --- a/audio/softsynth/fmtowns_pc98/towns_midi.cpp +++ b/audio/softsynth/fmtowns_pc98/towns_midi.cpp @@ -25,13 +25,62 @@ #include "audio/softsynth/fmtowns_pc98/towns_midi.h" #include "common/textconsole.h" -class MidiChannel_TOWNS : public MidiChannel { +class TownsMidiOutputChannel { +friend class TownsMidiInputChannel; public: - MidiChannel_TOWNS(MidiDriver_TOWNS *driver); - ~MidiChannel_TOWNS(); + TownsMidiOutputChannel(MidiDriver_TOWNS *driver, int chanId); + ~TownsMidiOutputChannel(); + + void noteOn(uint8 msb, uint16 lsb); + void noteOnAdjust(uint8 msb, uint16 lsb); + void setupProgram(const uint8 *data, uint8 vol1, uint8 vol2); + + void connect(TownsMidiInputChannel *chan); + void disconnect(); + + enum CheckPriorityStatus { + kDisconnected = -3, + kHighPriority = -2 + }; + + int checkPriority(int pri); + +private: + void keyOn(); + void keyOff(); + void internKeyOnFrq(uint16 frq); + void out(uint8 chan, uint8 reg, uint8 val); + + TownsMidiInputChannel *_midi; + TownsMidiOutputChannel *_prev; + TownsMidiOutputChannel *_next; + uint8 _fld_f; + uint8 _note; + uint8 _tl; + uint8 _noteOffMarker; + uint8 _fld_12; + uint8 _fld_13; + uint8 _prg; + uint8 _chan; + + uint16 _freq; + int16 _freqAdjust; + + MidiDriver_TOWNS *_driver; + + 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 0; } + byte getNumber() { return _chanIndex; } + bool allocate(); void release(); void send(uint32 b); @@ -43,71 +92,247 @@ public: void controlChange(byte control, byte value); void pitchBendFactor(byte value); void priority(byte value); - void sysEx_customInstrument(uint32 type, const byte *instr); private: + TownsMidiOutputChannel *_outChan; + //TownsMidiInputChannel *_prev; + //TownsMidiInputChannel *_next; + + uint8 *_instrument; + uint8 _prg; + uint8 _chanIndex; + uint8 _effectLevel; + uint8 _priority; + uint8 _vol; + uint8 _volEff; + uint8 _pan; + uint8 _panEff; + uint8 _perc; + uint8 _percS; + uint8 _fld_22; + uint8 _pitchBendFactor; + + bool _allocated; + MidiDriver_TOWNS *_driver; }; -MidiChannel_TOWNS::MidiChannel_TOWNS(MidiDriver_TOWNS *driver) : MidiChannel(), _driver(driver) { +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) { +} +TownsMidiOutputChannel::~TownsMidiOutputChannel() { } -MidiChannel_TOWNS::~MidiChannel_TOWNS() { +void TownsMidiOutputChannel::noteOn(uint8 msb, uint16 lsb) { + _freq = (msb << 7) + lsb; + _freqAdjust = 0; + internKeyOnFrq(_freq); +} +void TownsMidiOutputChannel::noteOnAdjust(uint8 msb, uint16 lsb) { + _freq = (msb << 7) + lsb; + internKeyOnFrq(_freq + _freqAdjust); } -void MidiChannel_TOWNS::release() { +void TownsMidiOutputChannel::setupProgram(const uint8 *data, uint8 vol1, uint8 vol2) { + const uint8 *pos = data; } -void MidiChannel_TOWNS::send(uint32 b) { +void TownsMidiOutputChannel::connect(TownsMidiInputChannel *chan) { + if (!chan) + return; + _midi = chan; + _next = chan->_outChan; + _prev = 0; + chan->_outChan = 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 + _midi->_outChan = n; + _midi = 0; } -void MidiChannel_TOWNS::noteOff(byte note) { +int TownsMidiOutputChannel::checkPriority(int pri) { + if (!_midi) + return kDisconnected; + + if (!_next && pri >= _midi->_priority) + return _midi->_priority; + return kHighPriority; } -void MidiChannel_TOWNS::noteOn(byte note, byte velocity) { +void TownsMidiOutputChannel::keyOn() { + out(_chan, 0x28, 0xf0/*0x30*/ /*???*/); +} + +void TownsMidiOutputChannel::keyOff() { + out(_chan, 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*/ /*???*/); } -void MidiChannel_TOWNS::programChange(byte program) { +void TownsMidiOutputChannel::out(uint8 chan, 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(19, 0, reg, val); + else + _driver->_intf->callback(19, chan / 3, (reg & ~3) | chanRegOffs[chan], val); } -void MidiChannel_TOWNS::pitchBend(int16 bend) { +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), _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) { + _instrument = new uint8[30]; + memset(_instrument, 0, 30); } -void MidiChannel_TOWNS::controlChange(byte control, byte value) { +TownsMidiInputChannel::~TownsMidiInputChannel() { + delete _instrument; +} +bool TownsMidiInputChannel::allocate() { + if (_allocated) + return false; + _allocated = true; + return true; } -void MidiChannel_TOWNS::pitchBendFactor(byte value) { +void TownsMidiInputChannel::release() { + _allocated = false; +} +void TownsMidiInputChannel::send(uint32 b) { + _driver->send(b | _chanIndex); } -void MidiChannel_TOWNS::priority(byte value) { +void TownsMidiInputChannel::noteOff(byte note) { + if (!_outChan) + return; + if (_outChan->_note != note) + return; + + if (_fld_22) + _outChan->_noteOffMarker = 1; + else + _outChan->disconnect(); +} + +void TownsMidiInputChannel::noteOn(byte note, byte velocity) { + TownsMidiOutputChannel *oc = _driver->allocateOutputChannel(_priority); + + if (!oc) + return; + + oc->connect(this); + + + int vol1 = 0; + int vol2 = 0; + oc->setupProgram(_instrument, vol1, vol2); + //oc->noteOn(m, l); + } -void MidiChannel_TOWNS::sysEx_customInstrument(uint32 type, const byte *instr) { +void TownsMidiInputChannel::programChange(byte program) { } +void TownsMidiInputChannel::pitchBend(int16 bend) { + +} + +void TownsMidiInputChannel::controlChange(byte control, byte value) { + +} + +void TownsMidiInputChannel::pitchBendFactor(byte value) { + +} + +void TownsMidiInputChannel::priority(byte value) { + _priority = value; +} + +void TownsMidiInputChannel::sysEx_customInstrument(uint32 type, const byte *instr) { + memcpy(_instrument, instr, 30); +} + MidiDriver_TOWNS::MidiDriver_TOWNS(Audio::Mixer *mixer) : _timerBproc(0), _timerBpara(0), _open(false) { _intf = new TownsAudioInterface(mixer, this); - _channels = new MidiChannel_TOWNS*[16]; - for (int i = 0; i < 16; i++) - _channels[i] = new MidiChannel_TOWNS(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); _tickCounter = 0; _curChan = 0; - //unbuffered write: _intf->callback(17, part, reg, val); - //buffered write: _intf->callback(19, part, reg, val); } MidiDriver_TOWNS::~MidiDriver_TOWNS() { @@ -115,9 +340,12 @@ MidiDriver_TOWNS::~MidiDriver_TOWNS() { delete _intf; setTimerCallback(0, 0); - for (int i = 0; i < 16; i++) + for (int i = 0; i < 32; i++) delete _channels[i]; delete[] _channels; + for (int i = 0; i < 6; i++) + delete _out[i]; + delete[] _out; } int MidiDriver_TOWNS::open() { @@ -154,14 +382,13 @@ void MidiDriver_TOWNS::send(uint32 b) { if (chan == 9) part = &_percussion; else**/ - MidiChannel_TOWNS *c = _channels[b & 0x0F]; + TownsMidiInputChannel *c = _channels[b & 0x0F]; switch (cmd) { case 0x80: - //part->noteOff(param1); + c->noteOff(param1); break; case 0x90: - //part->noteOn(param1, param2); if (param2) c->noteOn(param1, param2); else @@ -179,7 +406,7 @@ void MidiDriver_TOWNS::send(uint32 b) { c->pitchBend((param1 | (param2 << 7)) - 0x2000); break; case 0xF0: - warning("MidiDriver_ADLIB: Receiving SysEx command on a send() call"); + warning("MidiDriver_TOWNS: Receiving SysEx command on a send() call"); break; default: @@ -193,25 +420,17 @@ void MidiDriver_TOWNS::setTimerCallback(void *timer_param, Common::TimerManager: } uint32 MidiDriver_TOWNS::getBaseTempo() { - return 0; + return 4167; } MidiChannel *MidiDriver_TOWNS::allocateChannel() { - MidiChannel *res = 0; - - for (int i = 0; i < 6; i++) { - if (++_curChan == 6) - _curChan = 0; - - //if (_channels[i]-> //// ) - // return _channels[i]; - + for (int i = 0; i < 32; ++i) { + TownsMidiInputChannel *chan = _channels[i]; + if (chan->allocate()) + return chan; } - //if (res) - // res->noteOff(); - - return res; + return 0; } MidiChannel *MidiDriver_TOWNS::getPercussionChannel() { @@ -237,3 +456,26 @@ void MidiDriver_TOWNS::timerCallback(int timerId) { break; } } + +TownsMidiOutputChannel *MidiDriver_TOWNS::allocateOutputChannel(int pri) { + TownsMidiOutputChannel *res = 0; + + for (int i = 0; i < 6; i++) { + if (++_curChan == 6) + _curChan = 0; + + int s = _out[i]->checkPriority(pri); + if (s == TownsMidiOutputChannel::kDisconnected) + return _out[i]; + + if (s != TownsMidiOutputChannel::kHighPriority) { + pri = s; + res = _out[i]; + } + } + + if (res) + 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 cc390a260b..115142911a 100644 --- a/audio/softsynth/fmtowns_pc98/towns_midi.h +++ b/audio/softsynth/fmtowns_pc98/towns_midi.h @@ -28,9 +28,12 @@ #include "audio/softsynth/fmtowns_pc98/towns_audio.h" #include "audio/mididrv.h" -class MidiChannel_TOWNS; +class TownsMidiOutputChannel; +class TownsMidiInputChannel; + class MidiDriver_TOWNS : public MidiDriver, public TownsAudioInterfacePluginDriver { -friend class MidiChannel_TOWNS; +friend class TownsMidiInputChannel; +friend class TownsMidiOutputChannel; public: MidiDriver_TOWNS(Audio::Mixer *mixer); ~MidiDriver_TOWNS(); @@ -53,7 +56,10 @@ public: TownsAudioInterface *intf() { return _intf; } private: - MidiChannel_TOWNS **_channels; + TownsMidiOutputChannel *allocateOutputChannel(int pri); + + TownsMidiInputChannel **_channels; + TownsMidiOutputChannel **_out; Common::TimerManager::TimerProc _timerBproc; void *_timerBpara; -- cgit v1.2.3