From 88790f7fcf74d0a3e2473eb824544d247cc74b64 Mon Sep 17 00:00:00 2001 From: "Tim E. Real" Date: Sun, 24 Jan 2010 02:38:39 +0000 Subject: See ChangeLog --- muse/ChangeLog | 7 + muse/muse/audio.cpp | 44 +++-- muse/muse/driver/alsamidi.cpp | 5 +- muse/muse/midi.cpp | 24 ++- muse/muse/midi.h | 11 ++ muse/muse/mididev.cpp | 75 +++++--- muse/muse/midiport.cpp | 61 +++++- muse/muse/midiport.h | 5 + muse/muse/midiseq.cpp | 1 - muse/muse/midiseq.h | 11 +- muse/muse/mtc.cpp | 21 ++- muse/muse/mtc.h | 6 +- muse/muse/sync.cpp | 369 +++++++++++++++++++++++++++++++++---- muse/muse/sync.h | 20 +- muse/muse/widgets/midisyncimpl.cpp | 313 ++++++++++++++++++++++++++++--- muse/muse/widgets/midisyncimpl.h | 4 + 16 files changed, 842 insertions(+), 135 deletions(-) diff --git a/muse/ChangeLog b/muse/ChangeLog index 4b5376ca..b555dc38 100644 --- a/muse/ChangeLog +++ b/muse/ChangeLog @@ -1,3 +1,10 @@ +23.01.2010 + * Fixed: External midi sync in: MusE transport not rewinding upon reception of start. (T356) + * Added: Midi sync: MusE now transmits and receives some MMC commands, and displays some MTC and SMTPE info. (T356) + - No transmit MTC or receive MTC syncing yet, just transport control stuff. + - Tested OK with Rosegarden and Ardour, but they do not seem to have an option to sync to the input midi clock, + only MTC or internally. Must test when MTC output added to MusE. + * Improved: Midi sync editor window: Shows MMC and MTC activity and works with them now. (T356) 21.01.2010 * Fixed: First tests: External midi sync in works now! Should be rock solid no matter what is thrown at it. (T356) - All changes are labeled "p3.3.25" diff --git a/muse/muse/audio.cpp b/muse/muse/audio.cpp index aa5cebdd..e8b38904 100644 --- a/muse/muse/audio.cpp +++ b/muse/muse/audio.cpp @@ -36,11 +36,11 @@ Audio* audio; AudioDevice* audioDevice; // current audio device in use // p3.3.25 -extern unsigned int midiExtSyncTicks; +extern unsigned int volatile midiExtSyncTicks; -static const unsigned char mmcDeferredPlayMsg[] = { 0x7f, 0x7f, 0x06, 0x03 }; -static const unsigned char mmcStopMsg[] = { 0x7f, 0x7f, 0x06, 0x01 }; +//static const unsigned char mmcDeferredPlayMsg[] = { 0x7f, 0x7f, 0x06, 0x03 }; +//static const unsigned char mmcStopMsg[] = { 0x7f, 0x7f, 0x06, 0x01 }; const char* seqMsgList[] = { "SEQM_ADD_TRACK", "SEQM_REMOVE_TRACK", "SEQM_CHANGE_TRACK", "SEQM_MOVE_TRACK", @@ -234,10 +234,6 @@ bool Audio::sync(int jackState, unsigned frame) // Changed by Tim. p3.3.24 /* - // Added by Tim. p3.3.20 - if(debugMsg) - printf("Audio::sync state %s jackState %s frame %d\n", audioStates[state], audioStates[jackState], frame); - bool done = true; if (state == LOOP1) state = LOOP2; @@ -252,10 +248,6 @@ bool Audio::sync(int jackState, unsigned frame) done = audioPrefetch->seekDone(); } - // Added by Tim. p3.3.20 - //if(debugMsg) - // printf("Audio::sync done:%d state %s\n", done, audioStates[state]); - return done; */ bool done = true; @@ -974,7 +966,8 @@ void Audio::startRolling() //if(genMMC && si.MMCOut()) if(si.MMCOut()) - mp->sendSysex(mmcDeferredPlayMsg, sizeof(mmcDeferredPlayMsg)); + //mp->sendSysex(mmcDeferredPlayMsg, sizeof(mmcDeferredPlayMsg)); + mp->sendMMCDeferredPlay(); //if(genMCSync && si.MCOut()) if(si.MCOut()) @@ -1093,6 +1086,8 @@ void Audio::startRolling() } } } + + //tempomap.clearExtTempoList(); } //--------------------------------------------------------- @@ -1170,20 +1165,23 @@ void Audio::stopRolling() //if(genMMC && si.MMCOut()) if(si.MMCOut()) { - unsigned char mmcPos[] = { - 0x7f, 0x7f, 0x06, 0x44, 0x06, 0x01, - 0, 0, 0, 0, 0 - }; + //unsigned char mmcPos[] = { + // 0x7f, 0x7f, 0x06, 0x44, 0x06, 0x01, + // 0, 0, 0, 0, 0 + // }; int frame = tempomap.tick2frame(curTickPos); MTC mtc(double(frame) / double(sampleRate)); - mmcPos[6] = mtc.h() | (mtcType << 5); - mmcPos[7] = mtc.m(); - mmcPos[8] = mtc.s(); - mmcPos[9] = mtc.f(); - mmcPos[10] = mtc.sf(); + //mmcPos[6] = mtc.h() | (mtcType << 5); + //mmcPos[7] = mtc.m(); + //mmcPos[8] = mtc.s(); + //mmcPos[9] = mtc.f(); + //mmcPos[10] = mtc.sf(); - mp->sendSysex(mmcStopMsg, sizeof(mmcStopMsg)); - mp->sendSysex(mmcPos, sizeof(mmcPos)); + //mp->sendSysex(mmcStopMsg, sizeof(mmcStopMsg)); + mp->sendMMCStop(); + //mp->sendSysex(mmcPos, sizeof(mmcPos)); + mp->sendMMCLocate(mtc.h() | (mtcType << 5), + mtc.m(), mtc.s(), mtc.f(), mtc.sf()); } //if(genMCSync && si.MCOut()) // Midi Clock diff --git a/muse/muse/driver/alsamidi.cpp b/muse/muse/driver/alsamidi.cpp index b3aeddd3..f6483a6b 100644 --- a/muse/muse/driver/alsamidi.cpp +++ b/muse/muse/driver/alsamidi.cpp @@ -775,9 +775,10 @@ void alsaProcessMidiInput() if(event.type()) { mdev->recordEvent(event); - if(ev->type != SND_SEQ_EVENT_SYSEX) + // p3.3.26 1/23/10 Moved to MidiDevice now. Anticipating Jack midi support, so don't make it ALSA specific. Tim. + //if(ev->type != SND_SEQ_EVENT_SYSEX) // Trigger general activity indicator detector. Sysex has no channel, don't trigger. - midiPorts[curPort].syncInfo().trigActDetect(event.channel()); + // midiPorts[curPort].syncInfo().trigActDetect(event.channel()); } snd_seq_free_event(ev); diff --git a/muse/muse/midi.cpp b/muse/muse/midi.cpp index 60b63179..d330f51e 100644 --- a/muse/muse/midi.cpp +++ b/muse/muse/midi.cpp @@ -32,12 +32,24 @@ extern void dump(const unsigned char* p, int n); -const unsigned char gmOnMsg[] = { 0x7e, 0x7f, 0x09, 0x01 }; -const unsigned char gsOnMsg[] = { 0x41, 0x10, 0x42, 0x12, 0x40, 0x00, 0x7f, 0x00, 0x41 }; -const unsigned char xgOnMsg[] = { 0x43, 0x10, 0x4c, 0x00, 0x00, 0x7e, 0x00 }; -const unsigned int gmOnMsgLen = sizeof(gmOnMsg); -const unsigned int gsOnMsgLen = sizeof(gsOnMsg); -const unsigned int xgOnMsgLen = sizeof(xgOnMsg); +const unsigned char gmOnMsg[] = { 0x7e, 0x7f, 0x09, 0x01 }; +const unsigned char gsOnMsg[] = { 0x41, 0x10, 0x42, 0x12, 0x40, 0x00, 0x7f, 0x00, 0x41 }; +const unsigned char gsOnMsg2[] = { 0x41, 0x10, 0x42, 0x12, 0x40, 0x01, 0x33, 0x50, 0x3c }; +const unsigned char gsOnMsg3[] = { 0x41, 0x10, 0x42, 0x12, 0x40, 0x01, 0x34, 0x50, 0x3b }; +const unsigned char xgOnMsg[] = { 0x43, 0x10, 0x4c, 0x00, 0x00, 0x7e, 0x00 }; +const unsigned int gmOnMsgLen = sizeof(gmOnMsg); +const unsigned int gsOnMsgLen = sizeof(gsOnMsg); +const unsigned int gsOnMsg2Len = sizeof(gsOnMsg2); +const unsigned int gsOnMsg3Len = sizeof(gsOnMsg3); +const unsigned int xgOnMsgLen = sizeof(xgOnMsg); + +const unsigned char mmcDeferredPlayMsg[] = { 0x7f, 0x7f, 0x06, 0x03 }; +const unsigned char mmcStopMsg[] = { 0x7f, 0x7f, 0x06, 0x01 }; +const unsigned char mmcLocateMsg[] = { 0x7f, 0x7f, 0x06, 0x44, 0x06, 0x01, 0, 0, 0, 0, 0 }; + +const unsigned int mmcDeferredPlayMsgLen = sizeof(mmcDeferredPlayMsg); +const unsigned int mmcStopMsgLen = sizeof(mmcStopMsg); +const unsigned int mmcLocateMsgLen = sizeof(mmcLocateMsg); #define CALC_TICK(the_tick) lrintf((float(the_tick) * float(config.division) + float(div/2)) / float(div)); /*--------------------------------------------------------- diff --git a/muse/muse/midi.h b/muse/muse/midi.h index d9bc1fc5..ee4c6291 100644 --- a/muse/muse/midi.h +++ b/muse/muse/midi.h @@ -34,12 +34,23 @@ enum { #define ME_TIMESIG 0x58 extern const unsigned char gmOnMsg[]; + extern const unsigned char gsOnMsg[]; +extern const unsigned char gsOnMsg2[]; +extern const unsigned char gsOnMsg3[]; extern const unsigned char xgOnMsg[]; +extern const unsigned char mmcDeferredPlayMsg[]; +extern const unsigned char mmcStopMsg[]; +extern const unsigned char mmcLocateMsg[]; extern const unsigned int gmOnMsgLen; extern const unsigned int gsOnMsgLen; +extern const unsigned int gsOnMsg2Len; +extern const unsigned int gsOnMsg3Len; extern const unsigned int xgOnMsgLen; +extern const unsigned int mmcDeferredPlayMsgLen; +extern const unsigned int mmcStopMsgLen; +extern const unsigned int mmcLocateMsgLen; QString nameSysex(unsigned int len, const unsigned char* buf); QString midiMetaName(int); diff --git a/muse/muse/mididev.cpp b/muse/muse/mididev.cpp index 6a649798..aec9a51d 100644 --- a/muse/muse/mididev.cpp +++ b/muse/muse/mididev.cpp @@ -162,36 +162,53 @@ void MidiDevice::recordEvent(MidiRecordEvent& event) printf("MidiInput: "); event.dump(); } -#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))) { - if (p[2] == 0x06) { - mmcInput(p, n); - return; - } - if (p[2] == 0x01) { - mtcInputFull(p, n); - return; - } - } - else if (p[0] == 0x7e) { - nonRealtimeSystemSysex(p, n); - return; - } - } - } -#endif + 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 diff --git a/muse/muse/midiport.cpp b/muse/muse/midiport.cpp index 568fa68d..177e957c 100644 --- a/muse/muse/midiport.cpp +++ b/muse/muse/midiport.cpp @@ -433,11 +433,12 @@ void MidiPort::sendGmOn() void MidiPort::sendGsOn() { - static unsigned char data2[] = { 0x41, 0x10, 0x42, 0x12, 0x40, 0x01, 0x33, 0x50, 0x3c }; - static unsigned char data3[] = { 0x41, 0x10, 0x42, 0x12, 0x40, 0x01, 0x34, 0x50, 0x3b }; - - sendSysex(data2, sizeof(data2)); - sendSysex(data3, sizeof(data3)); + //static unsigned char data2[] = { 0x41, 0x10, 0x42, 0x12, 0x40, 0x01, 0x33, 0x50, 0x3c }; + //static unsigned char data3[] = { 0x41, 0x10, 0x42, 0x12, 0x40, 0x01, 0x34, 0x50, 0x3b }; + //sendSysex(data2, sizeof(data2)); + //sendSysex(data3, sizeof(data3)); + sendSysex(gsOnMsg2, gsOnMsg2Len); + sendSysex(gsOnMsg3, gsOnMsg3Len); } //--------------------------------------------------------- @@ -464,6 +465,56 @@ void MidiPort::sendSysex(const unsigned char* p, int n) } } +//--------------------------------------------------------- +// sendMMCLocate +//--------------------------------------------------------- + +void MidiPort::sendMMCLocate(unsigned char ht, unsigned char m, unsigned char s, unsigned char f, unsigned char sf, int devid) +{ + unsigned char msg[mmcLocateMsgLen]; + memcpy(msg, mmcLocateMsg, mmcLocateMsgLen); + if(devid != -1) + msg[1] = devid; + else + msg[1] = _syncInfo.idOut(); + msg[6] = ht; + msg[7] = m; + msg[8] = s; + msg[9] = f; + msg[10] = sf; + sendSysex(msg, mmcLocateMsgLen); +} + +//--------------------------------------------------------- +// sendMMCStop +//--------------------------------------------------------- + +void MidiPort::sendMMCStop(int devid) +{ + unsigned char msg[mmcStopMsgLen]; + memcpy(msg, mmcStopMsg, mmcStopMsgLen); + if(devid != -1) + msg[1] = devid; + else + msg[1] = _syncInfo.idOut(); + sendSysex(msg, mmcStopMsgLen); +} + +//--------------------------------------------------------- +// sendMMCDeferredPlay +//--------------------------------------------------------- + +void MidiPort::sendMMCDeferredPlay(int devid) +{ + unsigned char msg[mmcDeferredPlayMsgLen]; + memcpy(msg, mmcDeferredPlayMsg, mmcDeferredPlayMsgLen); + if(devid != -1) + msg[1] = devid; + else + msg[1] = _syncInfo.idOut(); + sendSysex(msg, mmcDeferredPlayMsgLen); +} + //--------------------------------------------------------- // sendStart //--------------------------------------------------------- diff --git a/muse/muse/midiport.h b/muse/muse/midiport.h index 33276845..9da238fb 100644 --- a/muse/muse/midiport.h +++ b/muse/muse/midiport.h @@ -91,6 +91,11 @@ class MidiPort { void sendSongpos(int); void sendClock(); void sendSysex(const unsigned char* p, int n); + void sendMMCLocate(unsigned char ht, unsigned char m, + unsigned char s, unsigned char f, unsigned char sf, int devid = -1); + void sendMMCStop(int devid = -1); + void sendMMCDeferredPlay(int devid = -1); + bool sendEvent(const MidiPlayEvent&); AutomationType automationType(int channel) { return _automationType[channel]; } void setAutomationType(int channel, AutomationType t) { diff --git a/muse/muse/midiseq.cpp b/muse/muse/midiseq.cpp index f3dfe870..2307203f 100644 --- a/muse/muse/midiseq.cpp +++ b/muse/muse/midiseq.cpp @@ -389,7 +389,6 @@ void MidiSeq::updatePollFd() // (one fd for all devices) // this allows for processing of some alsa events // even if no alsa driver is active (assigned to a port) - addPollFd(alsaSelectRfd(), POLLIN, ::alsaMidiRead, this, 0); } diff --git a/muse/muse/midiseq.h b/muse/muse/midiseq.h index 8cce855a..a11820fe 100644 --- a/muse/muse/midiseq.h +++ b/muse/muse/midiseq.h @@ -60,9 +60,9 @@ class MidiSeq : public Thread { virtual void processMsg(const ThreadMsg*); void updatePollFd(); - void mtcSyncMsg(const MTC& mtc, bool seekFlag); - void mtcInputFull(const unsigned char* p, int n); - void nonRealtimeSystemSysex(const unsigned char* p, int n); + void mtcSyncMsg(const MTC&, int, bool); + //void mtcInputFull(const unsigned char* p, int n); + //void nonRealtimeSystemSysex(const unsigned char* p, int n); public: //MidiSeq(int prio, const char* name); @@ -80,7 +80,10 @@ class MidiSeq : public Thread { void mtcInputQuarter(int, unsigned char); void setSongPosition(int, int); // void eventReceived(MidiRecordEvent& event); - void mmcInput(const unsigned char* p, int n); + //void mmcInput(const unsigned char* p, int n); + void mmcInput(int, const unsigned char*, int); + void mtcInputFull(int, const unsigned char*, int); + void nonRealtimeSystemSysex(int, const unsigned char*, int); void msgMsg(int id); void msgProcess(); diff --git a/muse/muse/mtc.cpp b/muse/muse/mtc.cpp index 2921933e..647d6359 100644 --- a/muse/muse/mtc.cpp +++ b/muse/muse/mtc.cpp @@ -17,11 +17,13 @@ extern int mtcType; // global mtcType //--------------------------------------------------------- -double MTC::time() const +double MTC::time(int type) const { double time = _h * 3600 + _m * 60 + _s; double ft = 0.0; - switch (mtcType) { + if(type == -1) + type = mtcType; + switch (type) { case 0: // 24 frames sec ft = 1.0/24.0; break; @@ -30,6 +32,7 @@ double MTC::time() const break; case 2: // 30 drop frame TODO case 3: // 30 non drop frame + default: ft = 1.0/30.0; break; } @@ -40,7 +43,7 @@ double MTC::time() const // MTC //--------------------------------------------------------- -MTC::MTC(double t) +MTC::MTC(double t, int type) { _h = int(t/3600); t -= _h * 3600; @@ -49,7 +52,9 @@ MTC::MTC(double t) _s = int(t); t -= _s; double ft = 1.0/24.0; - switch (mtcType) { + if(type == -1) + type = mtcType; + switch (type) { case 0: // 24 frames sec ft = 1.0/24.0; break; @@ -58,6 +63,7 @@ MTC::MTC(double t) break; case 2: // 30 drop frame case 3: // 30 non drop frame + default: ft = 1.0/30.0; break; } @@ -72,10 +78,12 @@ MTC::MTC(double t) // increment MTC time one quarter frame time //--------------------------------------------------------- -void MTC::incQuarter() +void MTC::incQuarter(int type) { int frames = 24; - switch (mtcType) { + if(type == -1) + type = mtcType; + switch (type) { case 0: frames = 24; break; @@ -84,6 +92,7 @@ void MTC::incQuarter() break; case 2: case 3: + default: frames = 30; break; } diff --git a/muse/muse/mtc.h b/muse/muse/mtc.h index 658cd581..ae2bb01e 100644 --- a/muse/muse/mtc.h +++ b/muse/muse/mtc.h @@ -27,7 +27,7 @@ class MTC { MTC() { _h = _m = _s = _f = _sf = 0; } - MTC(double); + MTC(double, int type = -1); void set(int h, int m, int s, int f, int sf=0) { _h = h; _m = m; @@ -35,7 +35,7 @@ class MTC { _f = f; _sf = sf; } - void incQuarter(); + void incQuarter(int type = -1); void setH(int val) { _h = val; } void setM(int val) { _m = val; } void setS(int val) { _s = val; } @@ -47,7 +47,7 @@ class MTC { int s() const { return _s; } int f() const { return _f; } int sf() const { return _sf; } - double time() const; + double time(int type = -1) const; void print() const; }; diff --git a/muse/muse/sync.cpp b/muse/muse/sync.cpp index 1d032cdd..141674dc 100644 --- a/muse/muse/sync.cpp +++ b/muse/muse/sync.cpp @@ -26,7 +26,9 @@ //MidiSyncPort midiSyncPorts[MIDI_PORTS]; int curMidiSyncInPort = -1; +// P3.3.26 bool debugSync = false; + int mtcType = 1; MTC mtcOffset; BValue extSyncFlag(0, "extSync"); // false - MASTER, true - SLAVE @@ -49,7 +51,11 @@ static bool mtcSync; // receive complete mtc frame? // static int mcStartTick; // p3.3.25 -unsigned int midiExtSyncTicks = 0; +// From the "Introduction to the Volatile Keyword" at Embedded.com +/* A variable should be declared volatile whenever its value could change unexpectedly. + ... global variables within a multi-threaded application + ... So all shared global variables should be declared volatile */ +unsigned int volatile midiExtSyncTicks = 0; //--------------------------------------------------------- // MidiSyncInfo @@ -69,10 +75,17 @@ MidiSyncInfo::MidiSyncInfo() _lastClkTime = 0.0; _lastTickTime = 0.0; + _lastMMCTime = 0.0; + _lastMTCTime = 0.0; _clockTrig = false; _tickTrig = false; + _MMCTrig = false; + _MTCTrig = false; _clockDetect = false; _tickDetect = false; + _MMCDetect = false; + _MTCDetect = false; + _recMTCtype = 0; _actDetectBits = 0; for(int i = 0; i < MIDI_CHANNELS; ++i) { @@ -94,10 +107,17 @@ MidiSyncInfo& MidiSyncInfo::operator=(const MidiSyncInfo &sp) _lastClkTime = sp._lastClkTime; _lastTickTime = sp._lastTickTime; + _lastMMCTime = sp._lastMMCTime; + _lastMTCTime = sp._lastMTCTime; _clockTrig = sp._clockTrig; _tickTrig = sp._tickTrig; + _MMCTrig = sp._MMCTrig; + _MTCTrig = sp._MTCTrig; _clockDetect = sp._clockDetect; _tickDetect = sp._tickDetect; + _MMCDetect = sp._MMCDetect; + _MTCDetect = sp._MTCDetect; + _recMTCtype = sp._recMTCtype; _actDetectBits = sp._actDetectBits; for(int i = 0; i < MIDI_CHANNELS; ++i) { @@ -147,6 +167,7 @@ void MidiSyncInfo::setTime() if(_clockDetect && (t - _lastClkTime >= 1.0)) // Set detect indicator timeout to about 1 second. { _clockDetect = false; + // Give up the current midi sync in port number if we took it... if(curMidiSyncInPort == _port) curMidiSyncInPort = -1; } @@ -160,6 +181,34 @@ void MidiSyncInfo::setTime() if(_tickDetect && (t - _lastTickTime) >= 1.0) // Set detect indicator timeout to about 1 second. _tickDetect = false; + if(_MMCTrig) + { + _MMCTrig = false; + _lastMMCTime = t; + } + else + if(_MMCDetect && (t - _lastMMCTime) >= 1.0) // Set detect indicator timeout to about 1 second. + { + _MMCDetect = false; + // Give up the current midi sync in port number if we took it... + //if(curMidiSyncInPort == _port) + // curMidiSyncInPort = -1; + } + + if(_MTCTrig) + { + _MTCTrig = false; + _lastMTCTime = t; + } + else + if(_MTCDetect && (t - _lastMTCTime) >= 1.0) // Set detect indicator timeout to about 1 second. + { + _MTCDetect = false; + // Give up the current midi sync in port number if we took it... + if(curMidiSyncInPort == _port) + curMidiSyncInPort = -1; + } + for(int i = 0; i < MIDI_CHANNELS; i++) { if(_actTrig[i]) @@ -188,6 +237,30 @@ void MidiSyncInfo::setMCIn(const bool v) curMidiSyncInPort = -1; } +//--------------------------------------------------------- +// setMMCIn +//--------------------------------------------------------- + +void MidiSyncInfo::setMMCIn(const bool v) +{ + _recMMC = v; + // If sync receive was turned off, clear the current midi sync in port number so another port can grab it. + //if(!_recMMC && _port != -1 && curMidiSyncInPort == _port) + // curMidiSyncInPort = -1; +} + +//--------------------------------------------------------- +// setMTCIn +//--------------------------------------------------------- + +void MidiSyncInfo::setMTCIn(const bool v) +{ + _recMTC = v; + // If sync receive was turned off, clear the current midi sync in port number so another port can grab it. + if(!_recMTC && _port != -1 && curMidiSyncInPort == _port) + curMidiSyncInPort = -1; +} + //--------------------------------------------------------- // trigMCSyncDetect //--------------------------------------------------------- @@ -211,6 +284,32 @@ void MidiSyncInfo::trigTickDetect() _tickTrig = true; } +//--------------------------------------------------------- +// trigMMCDetect +//--------------------------------------------------------- + +void MidiSyncInfo::trigMMCDetect() +{ + _MMCDetect = true; + _MMCTrig = true; + // Set the current midi sync in port number if it's not taken... + //if(_recMMC && curMidiSyncInPort == -1) + // curMidiSyncInPort = _port; +} + +//--------------------------------------------------------- +// trigMTCDetect +//--------------------------------------------------------- + +void MidiSyncInfo::trigMTCDetect() +{ + _MTCDetect = true; + _MTCTrig = true; + // Set the current midi sync in port number if it's not taken... + if(_recMTC && curMidiSyncInPort == -1) + curMidiSyncInPort = _port; +} + //--------------------------------------------------------- // actDetect //--------------------------------------------------------- @@ -327,15 +426,29 @@ void MidiSyncInfo::write(int level, Xml& xml) // Midi Machine Control Input received //--------------------------------------------------------- -void MidiSeq::mmcInput(const unsigned char* p, int n) +//void MidiSeq::mmcInput(const unsigned char* p, int n) +void MidiSeq::mmcInput(int port, const unsigned char* p, int n) { if (debugSync) printf("mmcInput: n:%d %02x %02x %02x %02x\n", n, p[2], p[3], p[4], p[5]); + + MidiPort* mp = &midiPorts[port]; + MidiSyncInfo& msync = mp->syncInfo(); + // Trigger MMC detect in. + msync.trigMMCDetect(); + // MMC locate SMPTE time code may contain format type bits. Grab them. + if(p[3] == 0x44 && p[4] == 6 && p[5] == 1) + msync.setRecMTCtype((p[6] >> 5) & 3); + + // MMC in not turned on? Forget it. + if(!msync.MMCIn()) + return; + //if (!(extSyncFlag.value() && acceptMMC)) - if(!extSyncFlag.value()) - return; - + //if(!extSyncFlag.value()) + // return; + switch(p[3]) { case 1: if (debugSync) @@ -384,18 +497,22 @@ void MidiSeq::mmcInput(const unsigned char* p, int n) break; } else if (p[5] == 1) { + if (!checkAudioDevice()) return; MTC mtc(p[6] & 0x1f, p[7], p[8], p[9], p[10]); - int mmcPos = tempomap.frame2tick(lrint(mtc.time()*sampleRate)); + int type = (p[6] >> 5) & 3; + //int mmcPos = tempomap.frame2tick(lrint(mtc.time()*sampleRate)); + //int mmcPos = lrint(mtc.time()*sampleRate); + int mmcPos = lrint(mtc.time(type) * sampleRate); - Pos tp(mmcPos, true); - if (!checkAudioDevice()) return; + //Pos tp(mmcPos, true); + Pos tp(mmcPos, false); //audioDevice->seekTransport(tp.frame()); audioDevice->seekTransport(tp); alignAllTicks(); //seek(tp); if (debugSync) { - printf("MMC: %f %d seek ", - mtc.time(), mmcPos); + //printf("MMC: %f %d seek ", mtc.time(), mmcPos); + printf("MMC: LOCATE mtc type:%d time:%lf frame:%d mtc: ", type, mtc.time(), mmcPos); mtc.print(); printf("\n"); } @@ -413,13 +530,11 @@ void MidiSeq::mmcInput(const unsigned char* p, int n) // process Quarter Frame Message //--------------------------------------------------------- -void MidiSeq::mtcInputQuarter(int, unsigned char c) +//void MidiSeq::mtcInputQuarter(int, unsigned char c) +void MidiSeq::mtcInputQuarter(int port, unsigned char c) { static int hour, min, sec, frame; - if (!extSyncFlag.value()) - return; - int valL = c & 0xf; int valH = valL << 4; @@ -456,35 +571,55 @@ void MidiSeq::mtcInputQuarter(int, unsigned char c) frame &= 0x1f; // 0-29 sec &= 0x3f; // 0-59 min &= 0x3f; // 0-59 + int tmphour = hour; + int type = (hour >> 5) & 3; hour &= 0x1f; - if (mtcState == 8) { + if(mtcState == 8) + { mtcValid = (mtcLost == 0); mtcState = 0; mtcLost = 0; - if (mtcValid) { + if(mtcValid) + { mtcCurTime.set(hour, min, sec, frame); - mtcSyncMsg(mtcCurTime, !mtcSync); - mtcSync = true; + if(port != -1) + { + MidiPort* mp = &midiPorts[port]; + MidiSyncInfo& msync = mp->syncInfo(); + msync.setRecMTCtype(type); + msync.trigMTCDetect(); + // Not for the current in port? External sync not turned on? MTC in not turned on? Forget it. + if(port == curMidiSyncInPort && extSyncFlag.value() && msync.MTCIn()) + { + if(debugSync) + printf("mtcInputQuarter: hour byte:%hx\n", tmphour); + mtcSyncMsg(mtcCurTime, type, !mtcSync); + } } + mtcSync = true; } - else if (mtcValid && (mtcLost == 0)) { - mtcCurTime.incQuarter(); - mtcSyncMsg(mtcCurTime, false); - } + } + else if (mtcValid && (mtcLost == 0)) + { + //mtcCurTime.incQuarter(); + mtcCurTime.incQuarter(type); + //mtcSyncMsg(mtcCurTime, type, false); } + } //--------------------------------------------------------- // mtcInputFull // process Frame Message //--------------------------------------------------------- -void MidiSeq::mtcInputFull(const unsigned char* p, int n) +//void MidiSeq::mtcInputFull(const unsigned char* p, int n) +void MidiSeq::mtcInputFull(int port, const unsigned char* p, int n) { if (debugSync) printf("mtcInputFull\n"); - if (!extSyncFlag.value()) - return; + //if (!extSyncFlag.value()) + // return; if (p[3] != 1) { if (p[3] != 2) { // silently ignore user bits @@ -501,20 +636,41 @@ void MidiSeq::mtcInputFull(const unsigned char* p, int n) frame &= 0x1f; // 0-29 sec &= 0x3f; // 0-59 min &= 0x3f; // 0-59 -// int type = (hour >> 5) & 3; + int type = (hour >> 5) & 3; hour &= 0x1f; mtcCurTime.set(hour, min, sec, frame); mtcState = 0; mtcValid = true; mtcLost = 0; - } + + // Added by Tim. + if(debugSync) + printf("mtcInputFull: time:%lf stime:%lf hour byte (all bits):%hx\n", mtcCurTime.time(), mtcCurTime.time(type), p[4]); + if(port != -1) + { + MidiPort* mp = &midiPorts[port]; + MidiSyncInfo& msync = mp->syncInfo(); + msync.setRecMTCtype(type); + msync.trigMTCDetect(); + // MTC in not turned on? Forget it. + //if(extSyncFlag.value() && msync.MTCIn()) + if(msync.MTCIn()) + { + //Pos tp(lrint(mtcCurTime.time() * sampleRate), false); + Pos tp(lrint(mtcCurTime.time(type) * sampleRate), false); + audioDevice->seekTransport(tp); + alignAllTicks(); + } + } + } //--------------------------------------------------------- // nonRealtimeSystemSysex //--------------------------------------------------------- -void MidiSeq::nonRealtimeSystemSysex(const unsigned char* p, int n) +//void MidiSeq::nonRealtimeSystemSysex(const unsigned char* p, int n) +void MidiSeq::nonRealtimeSystemSysex(int /*port*/, const unsigned char* p, int n) { // int chan = p[2]; switch(p[3]) { @@ -660,6 +816,7 @@ void MidiSeq::realtimeSystemInput(int port, int c) midiExtSyncTicks += div; } +//BEGIN : Original code: /* double mclock0 = curTime(); // Difference in time last 2 rounds: @@ -771,6 +928,142 @@ void MidiSeq::realtimeSystemInput(int port, int c) mclock2 = mclock1; mclock1 = mclock0; */ +//END : Original Code + +//BEGIN : Using external tempo map: + /* + double mclock0 = curTime(); + // Difference in time last 2 rounds: + double tdiff0 = mclock0 - mclock1; + double tdiff1 = mclock1 - mclock2; + double averagetimediff = 0.0; + + if (mclock1 != 0.0) { + if (storedtimediffs < 24) + { + timediff[storedtimediffs] = mclock0 - mclock1; + storedtimediffs++; + } + else { + for (int i=0; i<23; i++) { + timediff[i] = timediff[i+1]; + } + timediff[23] = mclock0 - mclock1; + } + // Calculate average timediff: + for (int i=0; i < storedtimediffs; i++) { + averagetimediff += timediff[i]/storedtimediffs; + } + } + + // Compare w audio if playing: + //if (playStateExt == true ) { //audio->isPlaying() state == PLAY + if (0) { + //BEGIN standard setup: + recTick += config.division / 24; // The one we're syncing to + int tempo = tempomap.tempo(0); + //unsigned curFrame = audio->pos().frame(); + //double songtick = (double(curFrame)/double(sampleRate)) * + // double(config.division * 1000000.0) / double(tempo); + double songtick = tempomap.curTickExt(mclock0); + + double scale = double(tdiff0/averagetimediff); + double tickdiff = songtick - ((double) recTick - 24 + scale*24.0); + + //END standard setup + if (debugSync) { + int m, b, t; + audio->pos().mbt(&m, &b, &t); + + int song_beat = b + m*4; // if the time-signature is different than 4/4, this will be wrong. + int sync_beat = recTick/config.division; + printf("pT=%.3f rT=%d diff=%.3f songB=%d syncB=%d scale=%.3f, curFrame=%d averagetimediff:%.3lf", + songtick, recTick, tickdiff, song_beat, sync_beat, scale, audio->pos().frame(), averagetimediff); + } + + //if ((mclock2 !=0.0) && (tdiff1 > 0.0) && fabs(tickdiff) > 0.5 && lastTempo != 0) { + if ((mclock2 !=0.0) && (tdiff1 > 0.0) && lastTempo != 0) { + // Interpolate: + double tickdiff1 = songtick1 - recTick1; + double tickdiff2 = songtick2 - recTick2; + double newtickdiff = (tickdiff1+tickdiff2)/250; + ////double newtickdiff = (tickdiff1+tickdiff2) / 10.0; + //double newtickdiff = tickdiff/5.0 + + // tickdiff1/16.0 + + // tickdiff2/24.0; //5 mins 30 secs on 116BPM, -p 512 jackd + + if (newtickdiff != 0.0) { + //int newTempo = tempomap.tempo(0); + int newTempo = tempo; + //newTempo += int(24.0 * newtickdiff * scale); + newTempo += int(24.0 * newtickdiff); + if (debugSync) + printf(" tdiff=%f ntd=%f lt=%d tmpo=%.3f", + tdiff0, newtickdiff, lastTempo, (float)((1000000.0 * 60.0)/newTempo)); + //syncTempo = newTempo; + //tempomap.setTempo(0,newTempo); + // Don't set the last stable tempo. + //tempomap.setTempo(0, newTempo, false); + tempomap.setExtTempo(newTempo); + } + if (debugSync) + printf("\n"); + } + else if (debugSync) + printf("\n"); + + //BEGIN post calc + lastTempo = tempo; + recTick2 = recTick1; + recTick1 = recTick; + mclock2 = mclock1; + mclock1 = mclock0; + songtick2 = songtick1; + songtick1 = songtick; + //END post calc + break; + } // END state play + // + // Pre-sync (when audio is not running) + // Calculate tempo depending on time per pulse + // + if (mclock1 == 0.0) { + mp->device()->discardInput(); + if (debugSync) + printf("Discarding input from port %d\n", port); + } + if ((mclock2 != 0.0) && (tdiff0 > 0.0)) { + + //int tempo0 = int(24000000.0 * tdiff0 + .5); + //int tempo1 = int(24000000.0 * tdiff1 + .5); + //int tempo = tempomap.tempo(0); + //int diff0 = tempo0 - tempo; + //int diff1 = tempo1 - tempo0; + + //if (diff0) { + // int newTempo = tempo + diff0/8 + diff1/16; + // if (debugSync) + // printf("setting new tempo %d = %f\n", newTempo, (float)((1000000.0 * 60.0)/newTempo)); + //tempomap.setTempo(0, newTempo); + // Don't set the last stable tempo. + //tempomap.setTempo(0, newTempo, false); + // tempomap.setExtTempo(newTempo); + // } + + //double tempo0 = 24000000.0 * tdiff0; + //double tempo1 = 24000000.0 * tdiff1; + //int newTempo = int((tempo0 + tempo1) / 2.0); + int newTempo = int(averagetimediff * 24000000.0); + if(debugSync) + printf("setting new tempo %d = %f\n", newTempo, (float)((1000000.0 * 60.0)/newTempo)); + tempomap.setExtTempo(newTempo); + } + + mclock2 = mclock1; + mclock1 = mclock0; + */ +//END : Using external tempo map + } break; case 0xf9: // midi tick (every 10 msec) @@ -793,6 +1086,7 @@ void MidiSeq::realtimeSystemInput(int port, int c) if (!checkAudioDevice()) return; //audioDevice->seekTransport(0); audioDevice->seekTransport(Pos(0, false)); + unsigned curFrame = audio->curFrame(); if (debugSync) printf(" curFrame=%d\n", curFrame); @@ -804,7 +1098,13 @@ void MidiSeq::realtimeSystemInput(int port, int c) storedtimediffs = 0; for (int i=0; i<24; i++) timediff[i] = 0.0; - audio->msgPlay(true); + + // p3.3.26 1/23/10 + // Changed because msgPlay calls audioDevice->seekTransport(song->cPos()) + // and song->cPos() may not be changed to 0 yet, causing tranport not to go to 0. + //audio->msgPlay(true); + audioDevice->startTransport(); + playStateExt = true; } break; @@ -815,7 +1115,7 @@ void MidiSeq::realtimeSystemInput(int port, int c) midiPorts[p].sendContinue(); if (debugSync) - printf(" continue\n"); + printf("realtimeSystemInput continue\n"); if (1 /* !audio->isPlaying() */ /*state == IDLE */) { //unsigned curFrame = audio->curFrame(); //recTick = tempomap.frame2tick(curFrame); // don't think this will work... (ml) @@ -831,7 +1131,7 @@ void MidiSeq::realtimeSystemInput(int port, int c) midiPorts[p].sendStop(); if (debugSync) - printf(" stop\n"); + printf("realtimeSystemInput stop\n"); if (audio->isPlaying() /*state == PLAY*/) { audio->msgPlay(false); playStateExt = false; @@ -852,17 +1152,20 @@ void MidiSeq::realtimeSystemInput(int port, int c) // start //--------------------------------------------------------- -void MidiSeq::mtcSyncMsg(const MTC& mtc, bool seekFlag) +void MidiSeq::mtcSyncMsg(const MTC& mtc, int type, bool seekFlag) { double time = mtc.time(); + double stime = mtc.time(type); if (debugSync) - printf("mtcSyncMsg: time %f\n", time); + printf("mtcSyncMsg: time:%lf stime:%lf seekFlag:%d\n", time, stime, seekFlag); if (seekFlag && audio->isRunning() /*state == START_PLAY*/) { // int tick = tempomap.time2tick(time); //state = PLAY; //write(sigFd, "1", 1); // say PLAY to gui if (!checkAudioDevice()) return; + if (debugSync) + printf("mtcSyncMsg: starting transport.\n"); audioDevice->startTransport(); return; } diff --git a/muse/muse/sync.h b/muse/muse/sync.h index f09d410c..b5a75fb8 100644 --- a/muse/muse/sync.h +++ b/muse/muse/sync.h @@ -32,14 +32,22 @@ class MidiSyncInfo bool _recMMC; bool _recMTC; + int _recMTCtype; + double _lastClkTime; double _lastTickTime; + double _lastMMCTime; + double _lastMTCTime; double _lastActTime[MIDI_CHANNELS]; bool _clockTrig; bool _tickTrig; + bool _MMCTrig; + bool _MTCTrig; bool _actTrig[MIDI_CHANNELS]; bool _clockDetect; bool _tickDetect; + bool _MMCDetect; + bool _MTCDetect; bool _actDetect[MIDI_CHANNELS]; int _actDetectBits; @@ -69,8 +77,8 @@ class MidiSyncInfo void setMTCOut(const bool v) { _sendMTC = v; } void setMCIn(const bool v); - void setMMCIn(const bool v) { _recMMC = v; } - void setMTCIn(const bool v) { _recMTC = v; } + void setMMCIn(const bool v); + void setMTCIn(const bool v); void setTime(); @@ -80,6 +88,14 @@ class MidiSyncInfo bool tickDetect() const { return _tickDetect; } void trigTickDetect(); + bool MTCDetect() const { return _MTCDetect; } + void trigMTCDetect(); + int recMTCtype() const { return _recMTCtype; } + void setRecMTCtype(int t) { _recMTCtype = t; } + + bool MMCDetect() const { return _MMCDetect; } + void trigMMCDetect(); + int actDetectBits() const { return _actDetectBits; } bool actDetect(const int ch) const; void trigActDetect(const int ch); diff --git a/muse/muse/widgets/midisyncimpl.cpp b/muse/muse/widgets/midisyncimpl.cpp index d38068fc..cbae9cb2 100644 --- a/muse/muse/widgets/midisyncimpl.cpp +++ b/muse/muse/widgets/midisyncimpl.cpp @@ -31,8 +31,11 @@ #include "sync.h" #include "globals.h" #include "midisyncimpl.h" +#include "driver/audiodev.h" +#include "audio.h" -enum { DEVCOL_NO = 0, DEVCOL_NAME, DEVCOL_IN, DEVCOL_TICKIN, DEVCOL_RID, DEVCOL_RCLK, DEVCOL_RMMC, DEVCOL_RMTC, +//enum { DEVCOL_NO = 0, DEVCOL_NAME, DEVCOL_IN, DEVCOL_TICKIN, DEVCOL_RID, DEVCOL_RCLK, DEVCOL_RMMC, DEVCOL_RMTC, +enum { DEVCOL_NO = 0, DEVCOL_NAME, DEVCOL_IN, DEVCOL_TICKIN, DEVCOL_MMCIN, DEVCOL_MTCIN, DEVCOL_MTCTYPE, DEVCOL_RID, DEVCOL_RCLK, DEVCOL_RMMC, DEVCOL_RMTC, //enum { DEVCOL_NAME = 0, DEVCOL_IN, DEVCOL_RID, DEVCOL_RCLK, DEVCOL_RMMC, DEVCOL_RMTC, DEVCOL_TID, DEVCOL_TCLK, DEVCOL_TMMC, DEVCOL_TMTC }; @@ -57,11 +60,14 @@ void MSyncHeaderTip::maybeTip(const QPoint &pos) " this port number"); break; case DEVCOL_IN: p = QHeader::tr("Midi realtime input detected"); break; case DEVCOL_TICKIN: p = QHeader::tr("Midi tick input detected"); break; - case DEVCOL_RID: p = QHeader::tr("Receive id number. Double click to edit."); break; + case DEVCOL_MMCIN: p = QHeader::tr("MMC input detected"); break; + case DEVCOL_MTCIN: p = QHeader::tr("MTC input detected"); break; + case DEVCOL_MTCTYPE: p = QHeader::tr("Detected SMPTE format"); break; + case DEVCOL_RID: p = QHeader::tr("Receive id number. 127 = Global. Double click to edit."); break; case DEVCOL_RCLK: p = QHeader::tr("Accept midi realtime input"); break; case DEVCOL_RMMC: p = QHeader::tr("Accept MMC input"); break; case DEVCOL_RMTC: p = QHeader::tr("Accept MTC input"); break; - case DEVCOL_TID: p = QHeader::tr("Transmit id number. Double click to edit."); break; + case DEVCOL_TID: p = QHeader::tr("Transmit id number. 127 = Global. Double click to edit."); break; case DEVCOL_TCLK: p = QHeader::tr("Send midi realtime output"); break; case DEVCOL_TMMC: p = QHeader::tr("Send MMC output"); break; case DEVCOL_TMTC: p = QHeader::tr("Send MTC output"); break; @@ -85,26 +91,36 @@ QString MSyncWhatsThis::text(const QPoint& pos) case DEVCOL_NAME: return QHeader::tr("Name of the midi device associated with this port number"); case DEVCOL_IN: - return QHeader::tr("Midi realtime input detected, including clock/start/stop/continue, and song position. " + return QHeader::tr("Midi realtime input detected, including clock/start/stop/continue, and song position.\n" "Current port actually used is red. Click to force a port to be current."); case DEVCOL_TICKIN: return QHeader::tr("Midi tick input detected"); + case DEVCOL_MMCIN: + return QHeader::tr("MMC input detected, including stop/play/deferred play, and locate."); + //"Current port actually used is red. Click to force a port to be current."); + case DEVCOL_MTCIN: + return QHeader::tr("MTC input detected, including forward quarter-frame sync and full-frame locate.\n" + "Current port actually used is red. Click to force a port to be current."); + case DEVCOL_MTCTYPE: + return QHeader::tr("Detected SMPTE format: 24fps, 25fps, 30fps drop frame, or 30fps non-drop\n" + "Detects format of MTC quarter and full frame, and MMC locate."); case DEVCOL_RID: - return QHeader::tr("Receive id number"); + return QHeader::tr("Receive id number. 127 = global receive all, even if not global."); case DEVCOL_RCLK: - return QHeader::tr("Accept midi realtime input, including clock/start/stop/continue, and song position. " - "Only one input is used for clock. Auto-acquire: If two or more port realtime inputs " - "are enabled, the first clock detected is used, until clock is lost, then another " - "can take over. Non-clock events (start,stop etc) are accepted by ALL enabled ports. " - "This means you may have several master devices connected, and muse will accept input " - "from any, including one clock (best if each turns off its clock at stop, so muse can " - "re-acquire the clock from another port. Click on detect indicator to force another.) "); + return QHeader::tr("Accept midi realtime input, including clock/start/stop/continue, and song position.\n" + "Only one input is used for clock. Auto-acquire: If two or more port realtime inputs\n" + "are enabled, the first clock detected is used, until clock is lost, then another\n" + "can take over. Non-clock events (start,stop etc) are accepted by ALL enabled ports.\n" + "This means you may have several master devices connected, and muse will accept input\n" + "from any, including one clock (best if each turns off its clock at stop, so muse can\n" + "re-acquire the clock from another port. Click on detect indicator to force another.)"); case DEVCOL_RMMC: - return QHeader::tr("Accept MMC input"); + return QHeader::tr("Accept MMC input, including stop/play/deferred play, and locate."); case DEVCOL_RMTC: - return QHeader::tr("Accept MTC input"); + return QHeader::tr("Accept MTC input, including forward quarter-frame sync and full-frame locate.\n" + "See rmc column for more help."); case DEVCOL_TID: - return QHeader::tr("Transmit id number"); + return QHeader::tr("Transmit id number. 127 = global transmit to all."); case DEVCOL_TCLK: return QHeader::tr("Send midi realtime output, including clock/start/stop/continue, and song position. " "If 'Slave to External Sync' is chosen, muse can re-transmit midi realtime input " @@ -237,6 +253,9 @@ MidiSyncConfig::MidiSyncConfig(QWidget* parent, const char* name) devicesListView->addColumn(tr("Device Name"), 120); devicesListView->addColumn(tr("i")); devicesListView->addColumn(tr("t")); + devicesListView->addColumn(tr("m")); + devicesListView->addColumn(tr("c")); + devicesListView->addColumn(tr("type")); devicesListView->addColumn(tr("rid")); // Receive devicesListView->addColumn(tr("rmc")); // Receive devicesListView->addColumn(tr("rmmc")); // Receive @@ -250,6 +269,9 @@ MidiSyncConfig::MidiSyncConfig(QWidget* parent, const char* name) devicesListView->setColumnAlignment(DEVCOL_NO, AlignHCenter); devicesListView->setColumnAlignment(DEVCOL_IN, AlignCenter); devicesListView->setColumnAlignment(DEVCOL_TICKIN, AlignCenter); + devicesListView->setColumnAlignment(DEVCOL_MMCIN, AlignCenter); + devicesListView->setColumnAlignment(DEVCOL_MTCIN, AlignCenter); + //devicesListView->setColumnAlignment(DEVCOL_MTCTYPE, AlignCenter); //devicesListView->setColumnAlignment(DEVCOL_RID, AlignCenter); devicesListView->setColumnAlignment(DEVCOL_RCLK, AlignCenter); devicesListView->setColumnAlignment(DEVCOL_RMMC, AlignCenter); @@ -261,6 +283,7 @@ MidiSyncConfig::MidiSyncConfig(QWidget* parent, const char* name) devicesListView->header()->setResizeEnabled(false, DEVCOL_NO); devicesListView->header()->setResizeEnabled(false, DEVCOL_IN); devicesListView->header()->setResizeEnabled(false, DEVCOL_TICKIN); + devicesListView->header()->setResizeEnabled(false, DEVCOL_MTCIN); devicesListView->header()->setResizeEnabled(false, DEVCOL_RCLK); devicesListView->header()->setResizeEnabled(false, DEVCOL_RMMC); devicesListView->header()->setResizeEnabled(false, DEVCOL_RMTC); @@ -270,6 +293,7 @@ MidiSyncConfig::MidiSyncConfig(QWidget* parent, const char* name) //devicesListView->setResizeMode(QListView::LastColumn); devicesListView->setResizeMode(QListView::NoColumn); + new MSyncWhatsThis(devicesListView, devicesListView->header()); _synctooltip = new MSyncHeaderTip(devicesListView->header()); //MSyncHeaderTip::add(devicesListView->header(), QString("Midi sync ports")); @@ -295,6 +319,7 @@ MidiSyncConfig::MidiSyncConfig(QWidget* parent, const char* name) connect(extSyncCheckbox, SIGNAL(clicked()), SLOT(syncChanged())); connect(mtcSyncType, SIGNAL(activated(int)), SLOT(syncChanged())); connect(useJackTransportCheckbox, SIGNAL(clicked()), SLOT(syncChanged())); + //connect(jackTransportMasterCheckbox, SIGNAL(clicked()), SLOT(syncChanged())); connect(&extSyncFlag, SIGNAL(valueChanged(bool)), SLOT(extSyncChanged(bool))); @@ -333,18 +358,32 @@ void MidiSyncConfig::songChanged(int flags) extSyncCheckbox->blockSignals(true); useJackTransportCheckbox->blockSignals(true); + //jackTransportMasterCheckbox->blockSignals(true); extSyncCheckbox->setChecked(extSyncFlag.value()); useJackTransportCheckbox->setChecked(useJackTransport); + //jackTransportMasterCheckbox->setChecked(jackTransportMaster); + //jackTransportMasterCheckbox->setEnabled(useJackTransport); + //jackTransportMasterCheckbox->blockSignals(false); useJackTransportCheckbox->blockSignals(false); extSyncCheckbox->blockSignals(false); mtcSyncType->setCurrentItem(mtcType); + mtcOffH->blockSignals(true); + mtcOffM->blockSignals(true); + mtcOffS->blockSignals(true); + mtcOffF->blockSignals(true); + mtcOffSf->blockSignals(true); mtcOffH->setValue(mtcOffset.h()); mtcOffM->setValue(mtcOffset.m()); mtcOffS->setValue(mtcOffset.s()); mtcOffF->setValue(mtcOffset.f()); mtcOffSf->setValue(mtcOffset.sf()); + mtcOffH->blockSignals(false); + mtcOffM->blockSignals(false); + mtcOffS->blockSignals(false); + mtcOffF->blockSignals(false); + mtcOffSf->blockSignals(false); updateSyncInfoLV(); @@ -427,7 +466,118 @@ void MidiSyncConfig::heartBeat() lvi->setPixmap(DEVCOL_TICKIN, *dothIcon); } } + + int type = midiPorts[port].syncInfo().recMTCtype(); + sdet = midiPorts[port].syncInfo().MMCDetect(); + bool mtcdet = midiPorts[port].syncInfo().MTCDetect(); + if(sdet) + { + if(!lvi->_MMCDet) + { + // Added by Tim. p3.3.6 + //printf("MidiSyncConfig::heartBeat setting MMC on icon\n"); + + lvi->_MMCDet = true; + lvi->setPixmap(DEVCOL_MMCIN, *dotIcon); + } + // MMC locate command can contain SMPTE format type. Update now. + if(!mtcdet && lvi->_recMTCtype != type) + { + lvi->_recMTCtype = type; + switch(type) + { + case 0: + lvi->setText(DEVCOL_MTCTYPE, "24"); + break; + case 1: + lvi->setText(DEVCOL_MTCTYPE, "25"); + break; + case 2: + lvi->setText(DEVCOL_MTCTYPE, "30D"); + break; + case 3: + lvi->setText(DEVCOL_MTCTYPE, "30N"); + break; + default: + lvi->setText(DEVCOL_MTCTYPE, "??"); + break; + } + } + } + else + { + if(lvi->_MMCDet) + { + // Added by Tim. p3.3.6 + //printf("MidiSyncConfig::heartBeat setting MMC off icon\n"); + + lvi->_MMCDet = false; + lvi->setPixmap(DEVCOL_MMCIN, *dothIcon); + } + } + + if(mtcdet) + { + if(port == curMidiSyncInPort) + { + if(!lvi->_curMTCDet) + { + // Added by Tim. p3.3.6 + //printf("MidiSyncConfig::heartBeat setting current red icon\n"); + + lvi->_curMTCDet = true; + lvi->_MTCDet = false; + lvi->setPixmap(DEVCOL_MTCIN, *record1_Icon); + } + } + else + if(!lvi->_MTCDet) + { + // Added by Tim. p3.3.6 + //printf("MidiSyncConfig::heartBeat setting MTC on icon\n"); + + lvi->_MTCDet = true; + lvi->_curMTCDet = false; + lvi->setPixmap(DEVCOL_MTCIN, *dotIcon); + } + + if(lvi->_recMTCtype != type) + { + lvi->_recMTCtype = type; + switch(type) + { + case 0: + lvi->setText(DEVCOL_MTCTYPE, "24"); + break; + case 1: + lvi->setText(DEVCOL_MTCTYPE, "25"); + break; + case 2: + lvi->setText(DEVCOL_MTCTYPE, "30D"); + break; + case 3: + lvi->setText(DEVCOL_MTCTYPE, "30N"); + break; + default: + lvi->setText(DEVCOL_MTCTYPE, "??"); + break; + } + } + } + else + { + if(lvi->_curMTCDet || lvi->_MTCDet) + { + // Added by Tim. p3.3.6 + //printf("MidiSyncConfig::heartBeat setting MTC off icon\n"); + + lvi->_MTCDet = false; + lvi->_curMTCDet = false; + lvi->setPixmap(DEVCOL_MTCIN, *dothIcon); + } + } } + //MidiDevice* dev = lvi->device(); //bool sdet = dev->syncInfo().MCSyncDetect(); //if(lvi->pixmap(DEVCOL_IN) != (sdet ? *dotIcon : *dothIcon)) @@ -449,6 +599,8 @@ void MidiSyncConfig::syncChanged() { setDirty(); + //jackTransportMasterCheckbox->setEnabled(useJackTransport); + //acceptMTCCheckbox->setEnabled(val); // acceptMTCCheckbox->setEnabled(false); // acceptMCCheckbox->setEnabled(val); @@ -557,6 +709,13 @@ void MidiSyncConfig::apply() // song->setMasterFlag(false); //extSyncFlag.blockSignals(false); useJackTransport = useJackTransportCheckbox->isChecked(); +// if(useJackTransport) +// jackTransportMaster = jackTransportMasterCheckbox->isChecked(); +// else +// jackTransportMaster = false; +// jackTransportMasterCheckbox->setEnabled(useJackTransport); +// if(audioDevice) +// audioDevice->setMaster(jackTransportMaster); mtcOffset.setH(mtcOffH->value()); mtcOffset.setM(mtcOffM->value()); @@ -589,7 +748,8 @@ void MidiSyncConfig::apply() if(applyButton->isEnabled()) applyButton->setEnabled(false); - midiSeq->msgUpdatePollFd(); + // Do not call this. Causes freeze sometimes. Only will be needed if extra pollfds are used by midi seq thread. + //midiSeq->msgUpdatePollFd(); } //--------------------------------------------------------- @@ -652,6 +812,84 @@ void MidiSyncConfig::updateSyncInfoLV() lvi->setPixmap(DEVCOL_TICKIN, *dothIcon); } + if(portsi.MMCDetect()) + { + lvi->_MMCDet = true; + lvi->setPixmap(DEVCOL_MMCIN, *dotIcon); + // MMC locate command can have SMPTE format bits... + if(lvi->_recMTCtype != portsi.recMTCtype()) + { + switch(portsi.recMTCtype()) + { + case 0: + lvi->setText(DEVCOL_MTCTYPE, "24"); + break; + case 1: + lvi->setText(DEVCOL_MTCTYPE, "25"); + break; + case 2: + lvi->setText(DEVCOL_MTCTYPE, "30D"); + break; + case 3: + lvi->setText(DEVCOL_MTCTYPE, "30N"); + break; + default: + lvi->setText(DEVCOL_MTCTYPE, "??"); + break; + } + } + } + else + { + lvi->_MMCDet = false; + lvi->setPixmap(DEVCOL_MMCIN, *dothIcon); + } + + if(portsi.MTCDetect()) + { + if(i == curMidiSyncInPort) + { + lvi->_curMTCDet = true; + lvi->_MTCDet = false; + lvi->setPixmap(DEVCOL_MTCIN, *record1_Icon); + } + else + { + lvi->_curMTCDet = false; + lvi->_MTCDet = true; + lvi->setPixmap(DEVCOL_MTCIN, *dotIcon); + } + + if(lvi->_recMTCtype != portsi.recMTCtype()) + { + switch(portsi.recMTCtype()) + { + case 0: + lvi->setText(DEVCOL_MTCTYPE, "24"); + break; + case 1: + lvi->setText(DEVCOL_MTCTYPE, "25"); + break; + case 2: + lvi->setText(DEVCOL_MTCTYPE, "30D"); + break; + case 3: + lvi->setText(DEVCOL_MTCTYPE, "30N"); + break; + default: + lvi->setText(DEVCOL_MTCTYPE, "??"); + break; + } + } + } + else + { + lvi->_curMTCDet = false; + lvi->_MTCDet = false; + lvi->setPixmap(DEVCOL_MTCIN, *dothIcon); + //lvi->setText(DEVCOL_MTCTYPE, "--"); + } + //lvi->setText(DEVCOL_RID, QString().setNum(si.idIn()) ); //lvi->setRenameEnabled(DEVCOL_RID, true); //lvi->setPixmap(DEVCOL_RCLK, si.MCIn() ? *dotIcon : *dothIcon); @@ -748,14 +986,47 @@ void MidiSyncConfig::dlvClicked(int /*button*/, QListViewItem* item, const QPoin // and sync is in fact detected on this port, allow the user to force this port to now be the // current sync in port. //if(no != curMidiSyncInPort && si.MCIn() && midiPorts[no].syncInfo().MCSyncDetect()) - if(no != curMidiSyncInPort && lvi->_recMC && midiPorts[no].syncInfo().MCSyncDetect()) + //if(no != curMidiSyncInPort && lvi->_recMC && midiPorts[no].syncInfo().MCSyncDetect()) + if(no != curMidiSyncInPort) { - curMidiSyncInPort = no; - lvi->setPixmap(DEVCOL_IN, *record1_Icon); + if(lvi->_recMC && midiPorts[no].syncInfo().MCSyncDetect()) + { + curMidiSyncInPort = no; + lvi->setPixmap(DEVCOL_IN, *record1_Icon); + } + if(lvi->_recMTC && midiPorts[no].syncInfo().MTCDetect()) + { + curMidiSyncInPort = no; + lvi->setPixmap(DEVCOL_MTCIN, *record1_Icon); + } } break; case DEVCOL_TICKIN: break; + case DEVCOL_MMCIN: + break; + case DEVCOL_MTCIN: + // If this is not the current midi sync in port, and sync in from this port is enabled, + // and sync is in fact detected on this port, allow the user to force this port to now be the + // current sync in port. + //if(no != curMidiSyncInPort && si.MTCIn() && midiPorts[no].syncInfo().MTCDetect()) + //if(no != curMidiSyncInPort && lvi->_recMTC && midiPorts[no].syncInfo().MTCDetect()) + if(no != curMidiSyncInPort) + { + if(lvi->_recMTC && midiPorts[no].syncInfo().MTCDetect()) + { + curMidiSyncInPort = no; + lvi->setPixmap(DEVCOL_MTCIN, *record1_Icon); + } + if(lvi->_recMC && midiPorts[no].syncInfo().MCSyncDetect()) + { + curMidiSyncInPort = no; + lvi->setPixmap(DEVCOL_IN, *record1_Icon); + } + } + break; + case DEVCOL_MTCTYPE: + break; case DEVCOL_RID: break; case DEVCOL_RCLK: @@ -828,7 +1099,7 @@ void MidiSyncConfig::dlvDoubleClicked(QListViewItem* item, const QPoint&, int co { //int id = lvi->syncInfo().idIn(); int id = lvi->_idIn; - int newid = QInputDialog::getInteger("Muse: Sync info" , "Enter new id number:", id, 0, 127, 1, &ok, this); + int newid = QInputDialog::getInteger("Muse: Sync info" , "Enter new id number (127 = all):", id, 0, 127, 1, &ok, this); if(ok) { //lvi->syncInfo().setIdIn(newid); @@ -841,7 +1112,7 @@ void MidiSyncConfig::dlvDoubleClicked(QListViewItem* item, const QPoint&, int co { //int id = lvi->syncInfo().idOut(); int id = lvi->_idOut; - int newid = QInputDialog::getInteger("Muse: Sync info" , "Enter new id number:", id, 0, 127, 1, &ok, this); + int newid = QInputDialog::getInteger("Muse: Sync info" , "Enter new id number (127 = global):", id, 0, 127, 1, &ok, this); if(ok) { //lvi->syncInfo().setIdOut(newid); diff --git a/muse/muse/widgets/midisyncimpl.h b/muse/muse/widgets/midisyncimpl.h index cbca11d4..afb640f9 100644 --- a/muse/muse/widgets/midisyncimpl.h +++ b/muse/muse/widgets/midisyncimpl.h @@ -75,7 +75,11 @@ class MidiSyncLViewItem : public QListViewItem //int _port; bool _inDet; bool _curDet; + bool _curMTCDet; bool _tickDet; + bool _MMCDet; + bool _MTCDet; + int _recMTCtype; int _idOut; int _idIn; -- cgit v1.2.3