diff options
-rw-r--r-- | muse/ChangeLog | 4 | ||||
-rw-r--r-- | muse/muse/driver/jackmidi.cpp | 205 | ||||
-rw-r--r-- | muse/muse/driver/jackmidi.h | 13 | ||||
-rw-r--r-- | muse/muse/midi.cpp | 80 | ||||
-rw-r--r-- | muse/muse/midi.h | 34 | ||||
-rw-r--r-- | muse/muse/mididev.cpp | 66 | ||||
-rw-r--r-- | muse/muse/mididev.h | 28 | ||||
-rw-r--r-- | muse/muse/mpevent.h | 2 |
8 files changed, 368 insertions, 64 deletions
diff --git a/muse/ChangeLog b/muse/ChangeLog index a44ae038..d77a3d83 100644 --- a/muse/ChangeLog +++ b/muse/ChangeLog @@ -1,3 +1,7 @@ +14.02.2010 + * Added: Jack midi input should be working now. Controllers, too. (T356) + - Tested with large 1024 frame buffer. Events are recorded with good sub-tick (frame) resolution, + as viewed in pianoroll with 64T snap grid. 13.02.2010 * Fixed: Jack midi output should now be frame-accurate, support variable length events (like sysex), and most controllers should work. (T356) diff --git a/muse/muse/driver/jackmidi.cpp b/muse/muse/driver/jackmidi.cpp index cc400fd9..c603d1be 100644 --- a/muse/muse/driver/jackmidi.cpp +++ b/muse/muse/driver/jackmidi.cpp @@ -9,9 +9,10 @@ #include <stdio.h> #include <jack/jack.h> -#include <jack/midiport.h> +//#include <jack/midiport.h> #include "jackmidi.h" +#include "song.h" #include "globals.h" #include "midi.h" #include "mididev.h" @@ -22,12 +23,14 @@ #include "mpevent.h" //#include "sync.h" #include "audiodev.h" +#include "../mplugins/midiitransform.h" +#include "../mplugins/mitplugin.h" // Turn on debug messages. //#define JACK_MIDI_DEBUG -int jackmidi_pi[2]; -int jackmidi_po[2]; +///int jackmidi_pi[2]; +///int jackmidi_po[2]; //extern muse_jack_midi_buffer jack_midi_out_data[JACK_MIDI_CHANNELS]; //extern muse_jack_midi_buffer jack_midi_in_data[JACK_MIDI_CHANNELS]; @@ -340,6 +343,202 @@ bool MidiJackDevice::putEvent(int* event) */ //--------------------------------------------------------- +// recordEvent +//--------------------------------------------------------- + +void MidiJackDevice::recordEvent(MidiRecordEvent& event) + { + // By T356. Set the loop number which the event came in at. + //if(audio->isRecording()) + if(audio->isPlaying()) + event.setLoopNum(audio->loopCount()); + + if (midiInputTrace) { + printf("Jack MidiInput: "); + event.dump(); + } + + if(_port != -1) + { + int idin = midiPorts[_port].syncInfo().idIn(); + +// p3.3.26 1/23/10 Section was disabled, enabled by Tim. +//#if 0 + int typ = event.type(); + + //--------------------------------------------------- + // filter some SYSEX events + //--------------------------------------------------- + + if (typ == ME_SYSEX) { + const unsigned char* p = event.data(); + int n = event.len(); + if (n >= 4) { + if ((p[0] == 0x7f) + //&& ((p[1] == 0x7f) || (p[1] == rxDeviceId))) { + && ((p[1] == 0x7f) || (idin == 0x7f) || (p[1] == idin))) { + if (p[2] == 0x06) { + //mmcInput(p, n); + midiSeq->mmcInput(_port, p, n); + return; + } + if (p[2] == 0x01) { + //mtcInputFull(p, n); + midiSeq->mtcInputFull(_port, p, n); + return; + } + } + else if (p[0] == 0x7e) { + //nonRealtimeSystemSysex(p, n); + midiSeq->nonRealtimeSystemSysex(_port, p, n); + return; + } + } + } + else + // p3.3.26 1/23/10 Moved here from alsaProcessMidiInput(). Anticipating Jack midi support, so don't make it ALSA specific. Tim. + // Trigger general activity indicator detector. Sysex has no channel, don't trigger. + midiPorts[_port].syncInfo().trigActDetect(event.channel()); + +//#endif + + } + + // + // process midi event input filtering and + // transformation + // + + processMidiInputTransformPlugins(event); + + if (filterEvent(event, midiRecordType, false)) + return; + + if (!applyMidiInputTransformation(event)) { + if (midiInputTrace) + printf(" midi input transformation: event filtered\n"); + return; + } + + // + // transfer noteOn events to gui for step recording and keyboard + // remote control + // + if (event.type() == ME_NOTEON) { + int pv = ((event.dataA() & 0xff)<<8) + (event.dataB() & 0xff); + song->putEvent(pv); + } + + if(_recordFifo.put(MidiPlayEvent(event))) + printf("MidiJackDevice::recordEvent: fifo overflow\n"); + } + +//--------------------------------------------------------- +// midiReceived +//--------------------------------------------------------- + +void MidiJackDevice::eventReceived(jack_midi_event_t* ev) + { + MidiRecordEvent event; + event.setB(0); + + // + // move all events 2*segmentSize into the future to get + // jitterfree playback + // + // cycle n-1 n n+1 + // -+----------+----------+----------+- + // ^ ^ ^ + // catch process play + // +// const SeqTime* st = audio->seqTime(); + + //unsigned curFrame = st->startFrame() + segmentSize; +// unsigned curFrame = st->lastFrameTime; + //int frameOffset = audio->getFrameOffset(); + unsigned pos = audio->pos().frame(); + event.setTime(pos + ev->time); + + event.setChannel(*(ev->buffer) & 0xf); + int type = *(ev->buffer) & 0xf0; + int a = *(ev->buffer + 1) & 0x7f; + int b = *(ev->buffer + 2) & 0x7f; + event.setType(type); + switch(type) { + case ME_NOTEON: + case ME_NOTEOFF: + case ME_CONTROLLER: + event.setA(*(ev->buffer + 1)); + event.setB(*(ev->buffer + 2)); + break; + case ME_PROGRAM: + case ME_AFTERTOUCH: + event.setA(*(ev->buffer + 1)); + break; + + case ME_PITCHBEND: + event.setA(((b << 7) + a) - 8192); + break; + + case ME_SYSEX: + { + int type = *(ev->buffer) & 0xff; + switch(type) { + case ME_SYSEX: + event.setTime(0); // mark as used + event.setType(ME_SYSEX); + event.setData((unsigned char*)(ev->buffer + 1), + ev->size - 2); + break; + case ME_CLOCK: + case ME_SENSE: + break; + default: + printf("MidiJackDevice::eventReceived unknown event 0x%02x\n", type); + return; + } + } + return; + } + + if (midiInputTrace) { + printf("MidiInput<%s>: ", name().latin1()); + event.dump(); + } + + #ifdef JACK_MIDI_DEBUG + printf("MidiJackDevice::eventReceived time:%d type:%d ch:%d A:%d B:%d\n", event.time(), event.type(), event.channel(), event.dataA(), event.dataB()); + #endif + + // Let recordEvent handle it from here, with timestamps, filtering, gui triggering etc. + recordEvent(event); + } + +//--------------------------------------------------------- +// collectMidiEvents +//--------------------------------------------------------- + +//void MidiJackDevice::collectMidiEvents(MidiInPort* track, Port port) +void MidiJackDevice::collectMidiEvents(int port) + { + //void* port_buf = jack_port_get_buffer(port.jackPort(), segmentSize); + void* port_buf = jack_port_get_buffer(midi_port_in[port], segmentSize); + jack_midi_event_t event; + jack_nframes_t eventCount = jack_midi_get_event_count(port_buf); + for (jack_nframes_t i = 0; i < eventCount; ++i) { + jack_midi_event_get(&event, port_buf, i); + + #ifdef JACK_MIDI_DEBUG + printf("MidiJackDevice::collectMidiEvents number:%d time:%d\n", i, event.time); + #endif + + //track->eventReceived(&event); + eventReceived(&event); + } + } + + +//--------------------------------------------------------- // queueEvent // return true if event cannot be delivered //--------------------------------------------------------- diff --git a/muse/muse/driver/jackmidi.h b/muse/muse/driver/jackmidi.h index 2758ae89..1c65a423 100644 --- a/muse/muse/driver/jackmidi.h +++ b/muse/muse/driver/jackmidi.h @@ -8,10 +8,16 @@ #ifndef __JACKMIDI_H__ #define __JACKMIDI_H__ -#include <config.h> +//#include <config.h> + +#include <jack/midiport.h> #include "mididev.h" + +class QString; class MidiFifo; +class MidiRecordEvent; +class MidiPlayEvent; // Turn on to show multiple devices, work in progress, // not working fully yet, can't seem to connect... @@ -59,6 +65,8 @@ class MidiJackDevice : public MidiDevice { virtual bool putMidiEvent(const MidiPlayEvent&); //bool sendEvent(const MidiPlayEvent&); + + void eventReceived(jack_midi_event_t*); public: MidiJackDevice() {} @@ -69,7 +77,10 @@ class MidiJackDevice : public MidiDevice { //virtual int selectWfd(); //virtual void processInput(); + virtual void recordEvent(MidiRecordEvent&); + virtual bool putEvent(const MidiPlayEvent&); + virtual void collectMidiEvents(int port); }; extern bool initMidiJack(); diff --git a/muse/muse/midi.cpp b/muse/muse/midi.cpp index dc14a3bf..61504a97 100644 --- a/muse/muse/midi.cpp +++ b/muse/muse/midi.cpp @@ -945,13 +945,25 @@ void Audio::processMidi() playEvents->erase(playEvents->begin(), nextPlayEvent); // klumsy hack for synti devices: - if (!md->isSynti()) - continue; - SynthI* s = (SynthI*)md; - while (s->eventsPending()) { - MidiRecordEvent ev = s->receiveEvent(); - md->recordEvent(ev); - } + if(md->isSynti()) + { + SynthI* s = (SynthI*)md; + while (s->eventsPending()) + { + MidiRecordEvent ev = s->receiveEvent(); + md->recordEvent(ev); + } + } + + // Is it a Jack midi device? + MidiJackDevice* mjd = dynamic_cast<MidiJackDevice*>(md); + if(mjd) + // TODO: Just use MusE port 0 for now. Support for multiple MusE ports maybe will come later. + mjd->collectMidiEvents(0); + + // Take snapshots of the current sizes of the recording fifos, + // because they may change while here in process, asynchronously. + md->beforeProcess(); } MPEventList* playEvents = metronome->playEvents(); @@ -1008,17 +1020,25 @@ void Audio::processMidi() if (devport == -1 || !(portMask & (1 << devport))) continue; - MREventList* el = dev->recordEvents(); - for (iMREvent ie = el->begin(); ie != el->end(); ++ie) + //MREventList* el = dev->recordEvents(); + MidiFifo& rf = dev->recordEvents(); + // Get the frozen snapshot of the size. + int count = dev->tmpRecordCount(); + + //for (iMREvent ie = el->begin(); ie != el->end(); ++ie) + for(int i = 0; i < count; ++i) { - int channel = ie->channel(); + MidiPlayEvent event(rf.peek(i)); + + //int channel = ie->channel(); + int channel = event.channel(); int defaultPort = devport; if (!(channelMask & (1 << channel))) { continue; } - MidiPlayEvent event(*ie); + //MidiPlayEvent event(*ie); int drumRecPitch=0; //prevent compiler warning: variable used without initialization MidiController *mc = 0; int ctl = 0; @@ -1121,23 +1141,18 @@ void Audio::processMidi() if (!dev->isSynti()) { - // Added by Tim. p3.3.8 - if(track->recEcho()) - { - - //Check if we're outputting to another port than default: - if (devport == defaultPort) { - event.setPort(port); - if(md) - playEvents->add(event); - } - else { - // Hmm, this appears to work, but... Will this induce trouble with md->setNextPlayEvent?? - MidiDevice* mdAlt = midiPorts[devport].device(); - if(mdAlt) - mdAlt->playEvents()->add(event); - } - } + //Check if we're outputting to another port than default: + if (devport == defaultPort) { + event.setPort(port); + if(md && track->recEcho()) + playEvents->add(event); + } + else { + // Hmm, this appears to work, but... Will this induce trouble with md->setNextPlayEvent?? + MidiDevice* mdAlt = midiPorts[devport].device(); + if(mdAlt && track->recEcho()) + mdAlt->playEvents()->add(event); + } // Shall we activate meters even while rec echo is off? Sure, why not... if(event.isNote() && event.dataB() > track->activity()) track->setActivity(event.dataB()); @@ -1220,11 +1235,12 @@ void Audio::processMidi() // for (iMidiDevice id = midiDevices.begin(); id != midiDevices.end(); ++id) { MidiDevice* md = *id; - md->recordEvents()->clear(); - - // By T356. Done processing this rec buffer, now flip to the other one. - md->flipRecBuffer(); + ///md->recordEvents()->clear(); + // By T356. Done processing this rec buffer, now flip to the other one. + ///md->flipRecBuffer(); + // We are done with the 'frozen' recording fifos, remove the events. + md->afterProcess(); MPEventList* stuckNotes = md->stuckNotes(); MPEventList* playEvents = md->playEvents(); diff --git a/muse/muse/midi.h b/muse/muse/midi.h index ee4c6291..ee13fb36 100644 --- a/muse/muse/midi.h +++ b/muse/muse/midi.h @@ -15,20 +15,26 @@ #include <qstring.h> enum { - ME_NOTEOFF = 0x80, - ME_NOTEON = 0x90, - ME_POLYAFTER = 0xa0, - ME_CONTROLLER = 0xb0, - ME_PROGRAM = 0xc0, - ME_AFTERTOUCH = 0xd0, - ME_PITCHBEND = 0xe0, - ME_SYSEX = 0xf0, - ME_META = 0xff, - ME_SONGPOS = 0xf2, - ME_CLOCK = 0xf8, - ME_START = 0xfa, - ME_CONTINUE = 0xfb, - ME_STOP = 0xfc, + ME_NOTEOFF = 0x80, + ME_NOTEON = 0x90, + ME_POLYAFTER = 0xa0, + ME_CONTROLLER = 0xb0, + ME_PROGRAM = 0xc0, + ME_AFTERTOUCH = 0xd0, + ME_PITCHBEND = 0xe0, + ME_SYSEX = 0xf0, + ME_META = 0xff, + ME_MTC_QUARTER = 0xf1, + ME_SONGPOS = 0xf2, + ME_SONGSEL = 0xf3, + ME_TUNE_REQ = 0xf6, + ME_SYSEX_END = 0xf7, + ME_CLOCK = 0xf8, + ME_TICK = 0xf9, + ME_START = 0xfa, + ME_CONTINUE = 0xfb, + ME_STOP = 0xfc, + ME_SENSE = 0xfe }; #define ME_TIMESIG 0x58 diff --git a/muse/muse/mididev.cpp b/muse/muse/mididev.cpp index 4d9f425c..01eb429e 100644 --- a/muse/muse/mididev.cpp +++ b/muse/muse/mididev.cpp @@ -82,14 +82,16 @@ void MidiDevice::init() MidiDevice::MidiDevice() { - _recBufFlipped = false; + ///_recBufFlipped = false; + _tmpRecordCount = 0; init(); } MidiDevice::MidiDevice(const QString& n) : _name(n) { - _recBufFlipped = false; + ///_recBufFlipped = false; + _tmpRecordCount = 0; init(); } @@ -98,7 +100,8 @@ MidiDevice::MidiDevice(const QString& n) // return true if event filtered //--------------------------------------------------------- -static bool filterEvent(const MEvent& event, int type, bool thru) +//static bool filterEvent(const MEvent& event, int type, bool thru) +bool filterEvent(const MEvent& event, int type, bool thru) { switch(event.type()) { case ME_NOTEON: @@ -143,6 +146,52 @@ static bool filterEvent(const MEvent& event, int type, bool thru) } //--------------------------------------------------------- +// afterProcess +// clear all recorded events after a process cycle +//--------------------------------------------------------- + +void MidiDevice::afterProcess() + { + while (_tmpRecordCount--) + _recordFifo.remove(); + } + +//--------------------------------------------------------- +// beforeProcess +// "freeze" fifo for this process cycle +//--------------------------------------------------------- + +void MidiDevice::beforeProcess() + { + //if (!jackPort(0).isZero()) + // audioDriver->collectMidiEvents(this, jackPort(0)); + _tmpRecordCount = _recordFifo.getSize(); + } + +/* +//--------------------------------------------------------- +// getEvents +//--------------------------------------------------------- + +void MidiDevice::getEvents(unsigned , unsigned , int ch, MPEventList* dst) //from //to +{ + for (int i = 0; i < _tmpRecordCount; ++i) { + const MidiPlayEvent& ev = _recordFifo.peek(i); + if (ch == -1 || (ev.channel() == ch)) + dst->insert(ev); + } + + //while(!recordFifo.isEmpty()) + //{ + // MidiPlayEvent e(recordFifo.get()); + // if (ch == -1 || (e.channel() == ch)) + // dst->insert(e); + //} +} +*/ + +/* +//--------------------------------------------------------- // recordEvent //--------------------------------------------------------- @@ -154,6 +203,7 @@ MREventList* MidiDevice::recordEvents() else return &_recordEvents2; } +*/ //--------------------------------------------------------- // recordEvent @@ -251,10 +301,12 @@ void MidiDevice::recordEvent(MidiRecordEvent& event) song->putEvent(pv); } - if(_recBufFlipped) - _recordEvents2.add(event); // add event to secondary list of recorded events - else - _recordEvents.add(event); // add event to primary list of recorded events + ///if(_recBufFlipped) + /// _recordEvents2.add(event); // add event to secondary list of recorded events + ///else + /// _recordEvents.add(event); // add event to primary list of recorded events + if(_recordFifo.put(MidiPlayEvent(event))) + printf("MidiDevice::recordEvent: fifo overflow\n"); } //--------------------------------------------------------- diff --git a/muse/muse/mididev.h b/muse/muse/mididev.h index 8117b4da..a0744923 100644 --- a/muse/muse/mididev.h +++ b/muse/muse/mididev.h @@ -23,9 +23,13 @@ class MidiDevice { MPEventList _stuckNotes; MPEventList _playEvents; iMPEvent _nextPlayEvent; - MREventList _recordEvents; - MREventList _recordEvents2; - bool _recBufFlipped; + ///MREventList _recordEvents; + ///MREventList _recordEvents2; + + // Used for multiple reads of fifo during process. + int _tmpRecordCount; + + ///bool _recBufFlipped; // Holds sync settings and detection monitors. //MidiSyncInfo _syncInfo; @@ -36,6 +40,8 @@ class MidiDevice { int _openFlags; // configured open flags bool _readEnable; // set when opened/closed. bool _writeEnable; // + // Recording fifo. + MidiFifo _recordFifo; void init(); virtual bool putMidiEvent(const MidiPlayEvent&) = 0; @@ -67,15 +73,22 @@ class MidiDevice { virtual void processInput() {} virtual void discardInput() {} - void recordEvent(MidiRecordEvent&); + virtual void recordEvent(MidiRecordEvent&); virtual bool putEvent(const MidiPlayEvent&); MPEventList* stuckNotes() { return &_stuckNotes; } MPEventList* playEvents() { return &_playEvents; } - MREventList* recordEvents(); - void flipRecBuffer() { _recBufFlipped = _recBufFlipped ? false : true; } - bool recBufFlipped() { return _recBufFlipped; } + + ///MREventList* recordEvents(); + ///void flipRecBuffer() { _recBufFlipped = _recBufFlipped ? false : true; } + ///bool recBufFlipped() { return _recBufFlipped; } + void beforeProcess(); + void afterProcess(); + int tmpRecordCount() { return _tmpRecordCount; } + MidiFifo& recordEvents() { return _recordFifo; } + //virtual void getEvents(unsigned /*from*/, unsigned /*to*/, int /*channel*/, MPEventList* /*dst*/); + iMPEvent nextPlayEvent() { return _nextPlayEvent; } void setNextPlayEvent(iMPEvent i) { _nextPlayEvent = i; } bool sendNullRPNParams(int, bool); @@ -97,6 +110,7 @@ class MidiDeviceList : public std::list<MidiDevice*> { extern MidiDeviceList midiDevices; extern void initMidiDevices(); +extern bool filterEvent(const MEvent& event, int type, bool thru); #endif diff --git a/muse/muse/mpevent.h b/muse/muse/mpevent.h index 8ec11ddf..6df7b0c0 100644 --- a/muse/muse/mpevent.h +++ b/muse/muse/mpevent.h @@ -138,6 +138,7 @@ struct MPEventList : public MPEL { typedef MPEventList::iterator iMPEvent; typedef MPEventList::const_iterator ciMPEvent; +/* //--------------------------------------------------------- // MREventList // memory allocation in midi thread domain @@ -155,6 +156,7 @@ struct MREventList : public MREL { typedef MREventList::iterator iMREvent; typedef MREventList::const_iterator ciMREvent; +*/ //--------------------------------------------------------- // MidiFifo |