summaryrefslogtreecommitdiff
path: root/muse2/muse/sync.cpp
diff options
context:
space:
mode:
authorRobert Jonsson <spamatica@gmail.com>2010-10-13 19:34:22 +0000
committerRobert Jonsson <spamatica@gmail.com>2010-10-13 19:34:22 +0000
commit8a2c2824a59d7644e13bc52c9a0ecbd641f21f95 (patch)
tree064ad3f2bf8daab0ad27b128abd86a9bbdb1e496 /muse2/muse/sync.cpp
parenta27706d9629e8b592cca4659f865b70adef24e6d (diff)
new branch muse2, first checkin
Diffstat (limited to 'muse2/muse/sync.cpp')
-rw-r--r--muse2/muse/sync.cpp1380
1 files changed, 1380 insertions, 0 deletions
diff --git a/muse2/muse/sync.cpp b/muse2/muse/sync.cpp
new file mode 100644
index 00000000..699b5a33
--- /dev/null
+++ b/muse2/muse/sync.cpp
@@ -0,0 +1,1380 @@
+//=========================================================
+// MusE
+// Linux Music Editor
+// $Id: sync.cpp,v 1.6.2.12 2009/06/20 22:20:41 terminator356 Exp $
+//
+// (C) Copyright 2003 Werner Schweer (ws@seh.de)
+//=========================================================
+
+#include <cmath>
+#include "sync.h"
+#include "song.h"
+#include "utils.h"
+#include "midiport.h"
+#include "mididev.h"
+#include "globals.h"
+#include "midiseq.h"
+#include "audio.h"
+#include "audiodev.h"
+#include "gconfig.h"
+#include "xml.h"
+#include "midi.h"
+
+//int rxSyncPort = -1; // receive from all ports
+//int txSyncPort = 1;
+//int rxDeviceId = 0x7f; // any device
+//int txDeviceId = 0x7f; // any device
+//MidiSyncPort midiSyncPorts[MIDI_PORTS];
+int volatile curMidiSyncInPort = -1;
+
+bool debugSync = true;
+
+int mtcType = 1;
+MTC mtcOffset;
+BValue extSyncFlag(0, "extSync"); // false - MASTER, true - SLAVE
+//bool genMTCSync = false; // output MTC Sync
+//bool genMCSync = false; // output MidiClock Sync
+//bool genMMC = false; // output Midi Machine Control
+//bool acceptMTC = false;
+//bool acceptMC = true;
+//bool acceptMMC = true;
+BValue useJackTransport(0,"useJackTransport");
+bool volatile jackTransportMaster = true;
+
+static MTC mtcCurTime;
+static int mtcState; // 0-7 next expected quarter message
+static bool mtcValid;
+static int mtcLost;
+static bool mtcSync; // receive complete mtc frame?
+
+// p3.3.28
+static bool playPendingFirstClock = false;
+unsigned int syncSendFirstClockDelay = 1; // In milliseconds.
+//static int lastStoppedBeat = 0;
+static unsigned int curExtMidiSyncTick = 0;
+unsigned int volatile lastExtMidiSyncTick = 0;
+double volatile curExtMidiSyncTime = 0.0;
+double volatile lastExtMidiSyncTime = 0.0;
+
+// Not used yet.
+// static bool mcStart = false;
+// static int mcStartTick;
+
+// p3.3.25
+// From the "Introduction to the Volatile Keyword" at Embedded dot com
+/* A variable should be declared volatile whenever its value could change unexpectedly.
+ ... <such as> global variables within a multi-threaded application
+ ... So all shared global variables should be declared volatile */
+unsigned int volatile midiExtSyncTicks = 0;
+
+//---------------------------------------------------------
+// MidiSyncInfo
+//---------------------------------------------------------
+
+MidiSyncInfo::MidiSyncInfo()
+{
+ _port = -1;
+ _idOut = 127;
+ _idIn = 127;
+ _sendMC = false;
+ _sendMRT = false;
+ _sendMMC = false;
+ _sendMTC = false;
+ _recMC = false;
+ _recMRT = false;
+ _recMMC = false;
+ _recMTC = false;
+
+ _lastClkTime = 0.0;
+ _lastTickTime = 0.0;
+ _lastMRTTime = 0.0;
+ _lastMMCTime = 0.0;
+ _lastMTCTime = 0.0;
+ _clockTrig = false;
+ _tickTrig = false;
+ _MRTTrig = false;
+ _MMCTrig = false;
+ _MTCTrig = false;
+ _clockDetect = false;
+ _tickDetect = false;
+ _MRTDetect = false;
+ _MMCDetect = false;
+ _MTCDetect = false;
+ _recMTCtype = 0;
+ _recRewOnStart = true;
+ //_sendContNotStart = false;
+ _actDetectBits = 0;
+ for(int i = 0; i < MIDI_CHANNELS; ++i)
+ {
+ _lastActTime[i] = 0.0;
+ _actTrig[i] = false;
+ _actDetect[i] = false;
+ }
+}
+
+//---------------------------------------------------------
+// operator =
+//---------------------------------------------------------
+
+MidiSyncInfo& MidiSyncInfo::operator=(const MidiSyncInfo &sp)
+{
+ //_port = sp._port;
+
+ copyParams(sp);
+
+ _lastClkTime = sp._lastClkTime;
+ _lastTickTime = sp._lastTickTime;
+ _lastMRTTime = sp._lastMRTTime;
+ _lastMMCTime = sp._lastMMCTime;
+ _lastMTCTime = sp._lastMTCTime;
+ _clockTrig = sp._clockTrig;
+ _tickTrig = sp._tickTrig;
+ _MRTTrig = sp._MRTTrig;
+ _MMCTrig = sp._MMCTrig;
+ _MTCTrig = sp._MTCTrig;
+ _clockDetect = sp._clockDetect;
+ _tickDetect = sp._tickDetect;
+ _MRTDetect = sp._MRTDetect;
+ _MMCDetect = sp._MMCDetect;
+ _MTCDetect = sp._MTCDetect;
+ _recMTCtype = sp._recMTCtype;
+ for(int i = 0; i < MIDI_CHANNELS; ++i)
+ {
+ _lastActTime[i] = sp._lastActTime[i];
+ _actTrig[i] = sp._actTrig[i];
+ _actDetect[i] = sp._actDetect[i];
+ }
+ return *this;
+}
+
+//---------------------------------------------------------
+// copyParams
+//---------------------------------------------------------
+
+MidiSyncInfo& MidiSyncInfo::copyParams(const MidiSyncInfo &sp)
+{
+ //_port = sp._port;
+
+ _idOut = sp._idOut;
+ _idIn = sp._idIn;
+ _sendMC = sp._sendMC;
+ _sendMRT = sp._sendMRT;
+ _sendMMC = sp._sendMMC;
+ _sendMTC = sp._sendMTC;
+ setMCIn(sp._recMC);
+ _recMRT = sp._recMRT;
+ _recMMC = sp._recMMC;
+ _recMTC = sp._recMTC;
+ _recRewOnStart = sp._recRewOnStart;
+ //_sendContNotStart = sp._sendContNotStart;
+ return *this;
+}
+
+//---------------------------------------------------------
+// setTime
+//---------------------------------------------------------
+
+void MidiSyncInfo::setTime()
+{
+ // Note: CurTime() makes a system call to gettimeofday(),
+ // which apparently can be slow in some cases. So I avoid calling this function
+ // too frequently by calling it (at the heartbeat rate) in Song::beat(). T356
+ double t = curTime();
+
+ if(_clockTrig)
+ {
+ _clockTrig = false;
+ _lastClkTime = t;
+ }
+ else
+ 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;
+ }
+
+ if(_tickTrig)
+ {
+ _tickTrig = false;
+ _lastTickTime = t;
+ }
+ else
+ if(_tickDetect && (t - _lastTickTime) >= 1.0) // Set detect indicator timeout to about 1 second.
+ _tickDetect = false;
+
+ if(_MRTTrig)
+ {
+ _MRTTrig = false;
+ _lastMRTTime = t;
+ }
+ else
+ if(_MRTDetect && (t - _lastMRTTime) >= 1.0) // Set detect indicator timeout to about 1 second.
+ {
+ _MRTDetect = false;
+ // Give up the current midi sync in port number if we took it...
+ //if(curMidiSyncInPort == _port)
+ // curMidiSyncInPort = -1;
+ }
+
+ 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])
+ {
+ _actTrig[i] = false;
+ _lastActTime[i] = t;
+ }
+ else
+ if(_actDetect[i] && (t - _lastActTime[i]) >= 1.0) // Set detect indicator timeout to about 1 second.
+ {
+ _actDetect[i] = false;
+ _actDetectBits &= ~bitShiftLU[i];
+ }
+ }
+}
+
+//---------------------------------------------------------
+// setMCIn
+//---------------------------------------------------------
+
+void MidiSyncInfo::setMCIn(const bool v)
+{
+ _recMC = v;
+ // If sync receive was turned off, clear the current midi sync in port number so another port can grab it.
+ if(!_recMC && _port != -1 && curMidiSyncInPort == _port)
+ curMidiSyncInPort = -1;
+}
+
+//---------------------------------------------------------
+// setMRTIn
+//---------------------------------------------------------
+
+void MidiSyncInfo::setMRTIn(const bool v)
+{
+ _recMRT = v;
+ // If sync receive was turned off, clear the current midi sync in port number so another port can grab it.
+ //if(!_recMRT && _port != -1 && curMidiSyncInPort == _port)
+ // 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
+//---------------------------------------------------------
+
+void MidiSyncInfo::trigMCSyncDetect()
+{
+ _clockDetect = true;
+ _clockTrig = true;
+ // Set the current midi sync in port number if it's not taken...
+ if(_recMC && curMidiSyncInPort == -1)
+ curMidiSyncInPort = _port;
+}
+
+//---------------------------------------------------------
+// trigTickDetect
+//---------------------------------------------------------
+
+void MidiSyncInfo::trigTickDetect()
+{
+ _tickDetect = true;
+ _tickTrig = true;
+}
+
+//---------------------------------------------------------
+// trigMRTDetect
+//---------------------------------------------------------
+
+void MidiSyncInfo::trigMRTDetect()
+{
+ _MRTDetect = true;
+ _MRTTrig = true;
+ // Set the current midi sync in port number if it's not taken...
+ //if(_recMRT && curMidiSyncInPort == -1)
+ // curMidiSyncInPort = _port;
+}
+
+//---------------------------------------------------------
+// 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
+//---------------------------------------------------------
+
+bool MidiSyncInfo::actDetect(const int ch) const
+{
+ if(ch < 0 || ch >= MIDI_CHANNELS)
+ return false;
+
+ return _actDetect[ch];
+}
+
+//---------------------------------------------------------
+// trigActDetect
+//---------------------------------------------------------
+
+void MidiSyncInfo::trigActDetect(const int ch)
+{
+ if(ch < 0 || ch >= MIDI_CHANNELS)
+ return;
+
+ _actDetectBits |= bitShiftLU[ch];
+ _actDetect[ch] = true;
+ _actTrig[ch] = true;
+}
+
+//---------------------------------------------------------
+// read
+//---------------------------------------------------------
+
+void MidiSyncInfo::read(Xml& xml)
+ {
+ for (;;) {
+ Xml::Token token(xml.parse());
+ const QString& tag(xml.s1());
+ switch (token) {
+ case Xml::Error:
+ case Xml::End:
+ return;
+ case Xml::TagStart:
+ if (tag == "idOut")
+ _idOut = xml.parseInt();
+ else if (tag == "idIn")
+ _idIn = xml.parseInt();
+ else if (tag == "sendMC")
+ _sendMC = xml.parseInt();
+ else if (tag == "sendMRT")
+ _sendMRT = xml.parseInt();
+ else if (tag == "sendMMC")
+ _sendMMC = xml.parseInt();
+ else if (tag == "sendMTC")
+ _sendMTC = xml.parseInt();
+ //else if (tag == "sendContNotStart")
+ // _sendContNotStart = xml.parseInt();
+ else if (tag == "recMC")
+ _recMC = xml.parseInt();
+ else if (tag == "recMRT")
+ _recMRT = xml.parseInt();
+ else if (tag == "recMMC")
+ _recMMC = xml.parseInt();
+ else if (tag == "recMTC")
+ _recMTC = xml.parseInt();
+ else if (tag == "recRewStart")
+ _recRewOnStart = xml.parseInt();
+ else
+ xml.unknown("midiSyncInfo");
+ break;
+ case Xml::TagEnd:
+ if (tag == "midiSyncInfo")
+ return;
+ default:
+ break;
+ }
+ }
+ }
+
+//---------------------------------------------------------
+// write
+//---------------------------------------------------------
+
+//void MidiSyncInfo::write(int level, Xml& xml, MidiDevice* md)
+void MidiSyncInfo::write(int level, Xml& xml)
+{
+ //if(!md)
+ // return;
+
+ // All defaults? Nothing to write.
+ if(_idOut == 127 && _idIn == 127 && !_sendMC && !_sendMRT && !_sendMMC && !_sendMTC &&
+ /* !_sendContNotStart && */ !_recMC && !_recMRT && !_recMMC && !_recMTC && _recRewOnStart)
+ return;
+
+ xml.tag(level++, "midiSyncInfo");
+ //xml.intTag(level, "idx", idx);
+ //xml.intTag(level++, "midiSyncPort", idx);
+ //xml.tag(level++, "midiSyncInfo idx=\"%d\"", idx);
+
+ //xml.strTag(level, "device", md->name());
+
+ if(_idOut != 127)
+ xml.intTag(level, "idOut", _idOut);
+ if(_idIn != 127)
+ xml.intTag(level, "idIn", _idIn);
+
+ if(_sendMC)
+ xml.intTag(level, "sendMC", true);
+ if(_sendMRT)
+ xml.intTag(level, "sendMRT", true);
+ if(_sendMRT)
+ xml.intTag(level, "sendMMC", true);
+ if(_sendMTC)
+ xml.intTag(level, "sendMTC", true);
+ //if(_sendContNotStart)
+ // xml.intTag(level, "sendContNotStart", true);
+
+ if(_recMC)
+ xml.intTag(level, "recMC", true);
+ if(_recMRT)
+ xml.intTag(level, "recMRT", true);
+ if(_recMMC)
+ xml.intTag(level, "recMMC", true);
+ if(_recMTC)
+ xml.intTag(level, "recMTC", true);
+ if(!_recRewOnStart)
+ xml.intTag(level, "recRewStart", false);
+
+ xml.etag(level, "midiSyncInfo");
+}
+
+//---------------------------------------------------------
+// mmcInput
+// Midi Machine Control Input received
+//---------------------------------------------------------
+
+//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;
+
+ switch(p[3]) {
+ case 1:
+ if (debugSync)
+ printf(" MMC: STOP\n");
+
+ playPendingFirstClock = false;
+
+ //if ((state == PLAY || state == PRECOUNT))
+ if (audio->isPlaying())
+ audio->msgPlay(false);
+ playStateExt = false;
+ alignAllTicks();
+ //stopPlay();
+ break;
+ case 2:
+ if (debugSync)
+ printf(" MMC: PLAY\n");
+ case 3:
+ if (debugSync)
+ printf(" MMC: DEFERRED PLAY\n");
+ mtcState = 0;
+ mtcValid = false;
+ mtcLost = 0;
+ mtcSync = false;
+ //startPlay();
+ alignAllTicks();
+ audio->msgPlay(true);
+ playStateExt = true;
+ break;
+
+ case 4:
+ printf("MMC: FF not implemented\n");
+ playPendingFirstClock = false;
+ break;
+ case 5:
+ printf("MMC: REWIND not implemented\n");
+ playPendingFirstClock = false;
+ break;
+ case 6:
+ printf("MMC: REC STROBE not implemented\n");
+ playPendingFirstClock = false;
+ break;
+ case 7:
+ printf("MMC: REC EXIT not implemented\n");
+ playPendingFirstClock = false;
+ break;
+ case 0xd:
+ printf("MMC: RESET not implemented\n");
+ playPendingFirstClock = false;
+ break;
+ case 0x44:
+ if (p[5] == 0) {
+ printf("MMC: LOCATE IF not implemented\n");
+ break;
+ }
+ else if (p[5] == 1) {
+ if (!checkAudioDevice()) return;
+ MTC mtc(p[6] & 0x1f, p[7], p[8], p[9], p[10]);
+ 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);
+ 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: LOCATE mtc type:%d time:%lf frame:%d mtc: ", type, mtc.time(), mmcPos);
+ mtc.print();
+ printf("\n");
+ }
+ //write(sigFd, "G", 1);
+ break;
+ }
+ // fall through
+ default:
+ printf("MMC %x %x, unknown\n", p[3], p[4]); break;
+ }
+ }
+
+//---------------------------------------------------------
+// mtcInputQuarter
+// process Quarter Frame Message
+//---------------------------------------------------------
+
+//void MidiSeq::mtcInputQuarter(int, unsigned char c)
+void MidiSeq::mtcInputQuarter(int port, unsigned char c)
+ {
+ static int hour, min, sec, frame;
+
+ // p3.3.28
+ //printf("MidiSeq::mtcInputQuarter c:%h\n", c);
+
+ int valL = c & 0xf;
+ int valH = valL << 4;
+
+ int _state = (c & 0x70) >> 4;
+ if (mtcState != _state)
+ mtcLost += _state - mtcState;
+ mtcState = _state + 1;
+
+ switch(_state) {
+ case 7:
+ hour = (hour & 0x0f) | valH;
+ break;
+ case 6:
+ hour = (hour & 0xf0) | valL;
+ break;
+ case 5:
+ min = (min & 0x0f) | valH;
+ break;
+ case 4:
+ min = (min & 0xf0) | valL;
+ break;
+ case 3:
+ sec = (sec & 0x0f) | valH;
+ break;
+ case 2:
+ sec = (sec & 0xf0) | valL;
+ break;
+ case 1:
+ frame = (frame & 0x0f) | valH;
+ break;
+ case 0: frame = (frame & 0xf0) | valL;
+ break;
+ }
+ 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)
+ {
+ mtcValid = (mtcLost == 0);
+ mtcState = 0;
+ mtcLost = 0;
+ if(mtcValid)
+ {
+ mtcCurTime.set(hour, min, sec, frame);
+ 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("MidiSeq::mtcInputQuarter hour byte:%hx\n", tmphour);
+ mtcSyncMsg(mtcCurTime, type, !mtcSync);
+ }
+ }
+ mtcSync = true;
+ }
+ }
+ 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(int port, const unsigned char* p, int n)
+ {
+ if (debugSync)
+ printf("mtcInputFull\n");
+ //if (!extSyncFlag.value())
+ // return;
+
+ if (p[3] != 1) {
+ if (p[3] != 2) { // silently ignore user bits
+ printf("unknown mtc msg subtype 0x%02x\n", p[3]);
+ dump(p, n);
+ }
+ return;
+ }
+ int hour = p[4];
+ int min = p[5];
+ int sec = p[6];
+ int frame = p[7];
+
+ frame &= 0x1f; // 0-29
+ sec &= 0x3f; // 0-59
+ min &= 0x3f; // 0-59
+ 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(int /*port*/, const unsigned char* p, int n)
+ {
+// int chan = p[2];
+ switch(p[3]) {
+ case 4:
+ printf("NRT Setup\n");
+ break;
+ default:
+ printf("unknown NRT Msg 0x%02x\n", p[3]);
+ dump(p, n);
+ break;
+ }
+ }
+
+//---------------------------------------------------------
+// setSongPosition
+// MidiBeat is a 14 Bit value. Each MidiBeat spans
+// 6 MIDI Clocks. Inother words, each MIDI Beat is a
+// 16th note (since there are 24 MIDI Clocks in a
+// quarter note).
+//---------------------------------------------------------
+
+void MidiSeq::setSongPosition(int port, int midiBeat)
+ {
+ if (midiInputTrace)
+ printf("set song position port:%d %d\n", port, midiBeat);
+
+ //midiPorts[port].syncInfo().trigMCSyncDetect();
+ midiPorts[port].syncInfo().trigMRTDetect();
+
+ //if (!extSyncFlag.value())
+ // External sync not on? Clock in not turned on?
+ //if(!extSyncFlag.value() || !midiPorts[port].syncInfo().MCIn())
+ if(!extSyncFlag.value() || !midiPorts[port].syncInfo().MRTIn())
+ return;
+
+ // Re-transmit song position to other devices if clock out turned on.
+ for(int p = 0; p < MIDI_PORTS; ++p)
+ //if(p != port && midiPorts[p].syncInfo().MCOut())
+ if(p != port && midiPorts[p].syncInfo().MRTOut())
+ midiPorts[p].sendSongpos(midiBeat);
+
+ curExtMidiSyncTick = (config.division * midiBeat) / 4;
+ lastExtMidiSyncTick = curExtMidiSyncTick;
+
+ //Pos pos((config.division * midiBeat) / 4, true);
+ Pos pos(curExtMidiSyncTick, true);
+
+ if (!checkAudioDevice()) return;
+
+ //audioDevice->seekTransport(pos.frame());
+ audioDevice->seekTransport(pos);
+ alignAllTicks(pos.frame());
+ if (debugSync)
+ printf("setSongPosition %d\n", pos.tick());
+ }
+
+
+
+//---------------------------------------------------------
+// set all runtime variables to the "in sync" value
+//---------------------------------------------------------
+void MidiSeq::alignAllTicks(int frameOverride)
+ {
+ //printf("alignAllTicks audioDriver->framePos=%d, audio->pos().frame()=%d\n",
+ // audioDevice->framePos(), audio->pos().frame());
+ unsigned curFrame;
+ if (!frameOverride)
+ curFrame = audio->pos().frame();
+ else
+ curFrame = frameOverride;
+
+ int tempo = tempomap.tempo(0);
+
+ // use the last old values to give start values for the tripple buffering
+ int recTickSpan = recTick1 - recTick2;
+ int songTickSpan = (int)(songtick1 - songtick2); //prevent compiler warning: casting double to int
+ storedtimediffs = 0; // pretend there is no sync history
+
+ mclock2=mclock1=0.0; // set all clock values to "in sync"
+
+ recTick = (int) ((double(curFrame)/double(sampleRate)) *
+ double(config.division * 1000000.0) / double(tempo) //prevent compiler warning: casting double to int
+ );
+ songtick1 = recTick - songTickSpan;
+ if (songtick1 < 0)
+ songtick1 = 0;
+ songtick2 = songtick1 - songTickSpan;
+ if (songtick2 < 0)
+ songtick2 = 0;
+ recTick1 = recTick - recTickSpan;
+ if (recTick1 < 0)
+ recTick1 = 0;
+ recTick2 = recTick1 - recTickSpan;
+ if (recTick2 < 0)
+ recTick2 = 0;
+ if (debugSync)
+ printf("alignAllTicks curFrame=%d recTick=%d tempo=%.3f frameOverride=%d\n",curFrame,recTick,(float)((1000000.0 * 60.0)/tempo), frameOverride);
+
+ }
+
+//---------------------------------------------------------
+// realtimeSystemInput
+// real time message received
+//---------------------------------------------------------
+void MidiSeq::realtimeSystemInput(int port, int c)
+ {
+
+ if (midiInputTrace)
+ printf("realtimeSystemInput port:%d 0x%x\n", port+1, c);
+
+ //if (midiInputTrace && (rxSyncPort != port) && rxSyncPort != -1) {
+ // if (debugSync)
+ // printf("rxSyncPort configured as %d; received sync from port %d\n",
+ // rxSyncPort, port);
+ // return;
+ // }
+
+ MidiPort* mp = &midiPorts[port];
+
+ // Trigger on any tick, clock, or realtime command.
+ if(c == ME_TICK) // Tick
+ mp->syncInfo().trigTickDetect();
+ else
+ if(c == ME_CLOCK) // Clock
+ mp->syncInfo().trigMCSyncDetect();
+ else
+ mp->syncInfo().trigMRTDetect(); // Other
+
+ // External sync not on? Clock in not turned on? Otherwise realtime in not turned on?
+ if(!extSyncFlag.value())
+ return;
+ if(c == ME_CLOCK)
+ {
+ if(!mp->syncInfo().MCIn())
+ return;
+ }
+ else
+ if(!mp->syncInfo().MRTIn())
+ return;
+
+
+ switch(c) {
+ case ME_CLOCK: // midi clock (24 ticks / quarter note)
+ {
+ // Not for the current in port? Forget it.
+ if(port != curMidiSyncInPort)
+ break;
+
+ // p3.3.31
+ //printf("midi clock:%f\n", curTime());
+
+ // Re-transmit clock to other devices if clock out turned on.
+ // Must be careful not to allow more than one clock input at a time.
+ // Would re-transmit mixture of multiple clocks - confusing receivers.
+ // Solution: Added curMidiSyncInPort.
+ // Maybe in MidiSeq::processTimerTick(), call sendClock for the other devices, instead of here.
+ for(int p = 0; p < MIDI_PORTS; ++p)
+ if(p != port && midiPorts[p].syncInfo().MCOut())
+ midiPorts[p].sendClock();
+
+ // p3.3.28
+ if(playPendingFirstClock)
+ {
+ playPendingFirstClock = false;
+ // Hopefully the transport will be ready by now, the seek upon start should mean the
+ // audio prefetch has already finished or at least started...
+ // Must comfirm that play does not force a complete prefetch again, but don't think so...
+ if(!audio->isPlaying())
+ audioDevice->startTransport();
+ }
+ //else
+ // This part will be run on the second and subsequent clocks, after start.
+ // Can't check audio state, might not be playing yet, we might miss some increments.
+ //if(audio->isPlaying())
+ if(playStateExt)
+ {
+ lastExtMidiSyncTime = curExtMidiSyncTime;
+ curExtMidiSyncTime = curTime();
+ int div = config.division/24;
+ midiExtSyncTicks += div;
+ lastExtMidiSyncTick = curExtMidiSyncTick;
+ curExtMidiSyncTick += div;
+ }
+
+//BEGIN : Original code:
+ /*
+ 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
+ //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 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",
+ songtick, recTick, tickdiff, song_beat, sync_beat, scale, curFrame);
+ }
+
+ //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;
+ //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);
+ //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);
+ }
+ 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);
+ }
+ }
+ 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 ME_TICK: // midi tick (every 10 msec)
+ // FIXME: Unfinished? mcStartTick is uninitialized and Song::setPos doesn't set it either. Dangerous to allow this.
+ //if (mcStart) {
+ // song->setPos(0, mcStartTick);
+ // mcStart = false;
+ // return;
+ // }
+ break;
+ case ME_START: // start
+ // Re-transmit start to other devices if clock out turned on.
+ for(int p = 0; p < MIDI_PORTS; ++p)
+ //if(p != port && midiPorts[p].syncInfo().MCOut())
+ if(p != port && midiPorts[p].syncInfo().MRTOut())
+ {
+ // p3.3.31
+ // If we aren't rewinding on start, there's no point in re-sending start.
+ // Re-send continue instead, for consistency.
+ if(midiPorts[port].syncInfo().recRewOnStart())
+ midiPorts[p].sendStart();
+ else
+ midiPorts[p].sendContinue();
+ }
+ if (debugSync)
+ printf(" start\n");
+
+ // p3.3.31
+ //printf("midi start:%f\n", curTime());
+
+ if (1 /* !audio->isPlaying()*/ /*state == IDLE*/) {
+ if (!checkAudioDevice()) return;
+
+ // p3.3.31
+ // Rew on start option.
+ if(midiPorts[port].syncInfo().recRewOnStart())
+ {
+ curExtMidiSyncTick = 0;
+ lastExtMidiSyncTick = curExtMidiSyncTick;
+ //audioDevice->seekTransport(0);
+ audioDevice->seekTransport(Pos(0, false));
+ }
+
+ //unsigned curFrame = audio->curFrame();
+ //if (debugSync)
+ // printf(" curFrame=%d\n", curFrame);
+
+ alignAllTicks();
+ //if (debugSync)
+ // printf(" curFrame: %d curTick: %d tempo: %d\n", curFrame, recTick, tempomap.tempo(0));
+
+ storedtimediffs = 0;
+ for (int i=0; i<24; i++)
+ timediff[i] = 0.0;
+
+ // 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();
+ // p3.3.28
+ playPendingFirstClock = true;
+
+ midiExtSyncTicks = 0;
+ playStateExt = true;
+ }
+ break;
+ case ME_CONTINUE: // continue
+ // Re-transmit continue to other devices if clock out turned on.
+ for(int p = 0; p < MIDI_PORTS; ++p)
+ //if(p != port && midiPorts[p].syncInfo().MCOut())
+ if(p != port && midiPorts[p].syncInfo().MRTOut())
+ midiPorts[p].sendContinue();
+
+ if (debugSync)
+ printf("realtimeSystemInput continue\n");
+
+ // p3.3.31
+ //printf("continue:%f\n", curTime());
+
+ if (1 /* !audio->isPlaying() */ /*state == IDLE */) {
+ //unsigned curFrame = audio->curFrame();
+ //recTick = tempomap.frame2tick(curFrame); // don't think this will work... (ml)
+ //alignAllTicks();
+
+ // p3.3.28
+ //audio->msgPlay(true);
+ // p3.3.31
+ // Begin incrementing immediately upon first clock reception.
+ playPendingFirstClock = true;
+
+ playStateExt = true;
+ }
+ break;
+ case ME_STOP: // stop
+ {
+ // p3.3.35
+ // Stop the increment right away.
+ midiExtSyncTicks = 0;
+ playStateExt = false;
+ playPendingFirstClock = false;
+
+ // Re-transmit stop to other devices if clock out turned on.
+ for(int p = 0; p < MIDI_PORTS; ++p)
+ //if(p != port && midiPorts[p].syncInfo().MCOut())
+ if(p != port && midiPorts[p].syncInfo().MRTOut())
+ midiPorts[p].sendStop();
+
+ //playPendingFirstClock = false;
+
+ //lastStoppedBeat = (audio->tickPos() * 4) / config.division;
+ //curExtMidiSyncTick = (config.division * lastStoppedBeat) / 4;
+
+ // p3.3.31
+ //printf("stop:%f\n", curTime());
+
+ if (audio->isPlaying() /*state == PLAY*/) {
+ audio->msgPlay(false);
+ //playStateExt = false;
+ }
+
+ if (debugSync)
+ printf("realtimeSystemInput stop\n");
+
+ // Just in case the process still runs a cycle or two and causes the
+ // audio tick position to increment, reset the incrementer and force
+ // the transport position to what the hardware thinks is the current position.
+ //midiExtSyncTicks = 0;
+ //Pos pos((config.division * lastStoppedBeat) / 4, true);
+ //Pos pos(curExtMidiSyncTick, true);
+ //audioDevice->seekTransport(pos);
+ }
+
+ break;
+ //case 0xfd: // unknown
+ //case ME_SENSE: // active sensing
+ //case ME_META: // system reset (reset is 0xff same enumeration as file meta event)
+ default:
+ break;
+ }
+
+ }
+
+//---------------------------------------------------------
+// mtcSyncMsg
+// process received mtc Sync
+// seekFlag - first complete mtc frame received after
+// start
+//---------------------------------------------------------
+
+void MidiSeq::mtcSyncMsg(const MTC& mtc, int type, bool seekFlag)
+ {
+ double time = mtc.time();
+ double stime = mtc.time(type);
+ if (debugSync)
+ printf("MidiSeq::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("MidiSeq::mtcSyncMsg starting transport.\n");
+ audioDevice->startTransport();
+ return;
+ }
+
+ /*if (tempoSN != tempomap.tempoSN()) {
+ double cpos = tempomap.tick2time(_midiTick, 0);
+ samplePosStart = samplePos - lrint(cpos * sampleRate);
+ rtcTickStart = rtcTick - lrint(cpos * realRtcTicks);
+ tempoSN = tempomap.tempoSN();
+ }*/
+
+ //
+ // diff is the time in sec MusE is out of sync
+ //
+ /*double diff = time - (double(samplePosStart)/double(sampleRate));
+ if (debugSync)
+ printf(" state %d diff %f\n", mtcState, diff);
+ */
+ }
+
+