summaryrefslogtreecommitdiff
path: root/attic/muse_qt4_evolution/muse/sync.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'attic/muse_qt4_evolution/muse/sync.cpp')
-rw-r--r--attic/muse_qt4_evolution/muse/sync.cpp499
1 files changed, 499 insertions, 0 deletions
diff --git a/attic/muse_qt4_evolution/muse/sync.cpp b/attic/muse_qt4_evolution/muse/sync.cpp
new file mode 100644
index 00000000..c4cb30fc
--- /dev/null
+++ b/attic/muse_qt4_evolution/muse/sync.cpp
@@ -0,0 +1,499 @@
+//=============================================================================
+// MusE
+// Linux Music Editor
+// $Id:$
+//
+// Copyright (C) 2002-2006 by Werner Schweer and others
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//=============================================================================
+
+#include "sync.h"
+#include "song.h"
+#include "widgets/utils.h"
+#include "globals.h"
+#include "audio.h"
+#include "audiodev.h"
+#include "gconfig.h"
+#include "al/tempo.h"
+#include "al/al.h"
+
+int rxSyncPort = -1; // receive from all ports
+int txSyncPort = 1;
+
+bool debugSync = false;
+MTC mtcOffset;
+bool extSyncFlag = false;
+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;
+
+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?
+
+//static bool mcStart = false;
+//static int mcStartTick;
+
+enum {
+ MMC_STOP = 1,
+ MMC_PLAY = 2,
+ MMC_DEFERRED_PLAY = 3,
+ MMC_FAST_FORWARD = 4,
+ MMC_REWIND = 5,
+ MMC_RECORD_STROBE = 6, // Punch In
+ MMC_RECORD_EXIT = 7, // Punch Out
+ MMC_PAUSE = 9,
+ MMC_RESET = 13,
+ MMC_GOTO = 0x44
+ };
+
+#if 0
+//---------------------------------------------------------
+// mmcInput
+// Midi Machine Control Input received
+//---------------------------------------------------------
+
+void MidiSeq::mmcInput(int id, int cmd, const Pos& pos)
+ {
+#if 0
+ int rxDeviceId = 127;
+
+ if (!extSyncFlag || !acceptMMC || (id != 127 && id != rxDeviceId))
+ return;
+
+ if (debugMsg)
+ printf("mmcInput: id %d cmd %02x %02x\n", id, cmd, cmd);
+
+ switch (cmd) {
+ case MMC_STOP:
+ if (debugSync)
+ printf(" MMC: STOP\n");
+ if (audio->isPlaying())
+ audioDriver->stopTransport();
+ else
+ // reset REC
+ audio->sendMsgToGui(MSG_STOP);
+ break;
+ case MMC_PLAY:
+ case MMC_DEFERRED_PLAY:
+ audioDriver->startTransport();
+ break;
+ case MMC_FAST_FORWARD:
+ printf("MMC: FF not implemented\n");
+ break;
+ case MMC_REWIND:
+ printf("MMC: REWIND not implemented\n");
+ break;
+ case MMC_RECORD_STROBE:
+ printf("MMC: REC STROBE not implemented\n");
+ break;
+ case MMC_RECORD_EXIT:
+ printf("MMC: REC EXIT not implemented\n");
+ break;
+ case MMC_PAUSE:
+ audio->sendMsgToGui(MSG_RECORD);
+ break;
+ case MMC_RESET:
+ printf("MMC: RESET not implemented\n");
+ break;
+
+ case MMC_GOTO:
+ audioDriver->seekTransport(pos.frame());
+ break;
+
+ default:
+ printf("MMC id %x cmd %x, unknown\n", id, cmd);
+ break;
+ }
+#endif
+ }
+
+//---------------------------------------------------------
+// mtcInputQuarter
+// process Quarter Frame Message
+//---------------------------------------------------------
+
+void MidiSeq::mtcInputQuarter(int, unsigned char c)
+ {
+#if 0
+ static int hour, min, sec, frame;
+
+ if (!extSyncFlag)
+ return;
+
+ 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
+ hour &= 0x1f;
+
+ if (mtcState == 8) {
+ mtcValid = (mtcLost == 0);
+ mtcState = 0;
+ mtcLost = 0;
+ if (mtcValid) {
+ mtcCurTime.set(hour, min, sec, frame);
+ mtcSyncMsg(mtcCurTime, !mtcSync);
+ mtcSync = true;
+ }
+ }
+ else if (mtcValid && (mtcLost == 0)) {
+ mtcCurTime.incQuarter();
+ mtcSyncMsg(mtcCurTime, false);
+ }
+#endif
+ }
+
+//---------------------------------------------------------
+// mtcInputFull
+// process Frame Message
+//---------------------------------------------------------
+
+void MidiSeq::mtcInputFull(const unsigned char* p, int n)
+ {
+#if 0
+ if (debugSync)
+ printf("mtcInputFull\n");
+ if (!extSyncFlag)
+ 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;
+#endif
+ }
+
+//---------------------------------------------------------
+// nonRealtimeSystemSysex
+//---------------------------------------------------------
+
+void MidiSeq::nonRealtimeSystemSysex(const unsigned char* p, int n)
+ {
+#if 0
+// 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;
+ }
+#endif
+ }
+
+//---------------------------------------------------------
+// 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 0
+ if (midiInputTrace)
+ printf("set song position port:%d %d\n", port, midiBeat);
+ if (!extSyncFlag)
+ return;
+ Pos pos((config.division * midiBeat) / 4, AL::TICKS);
+ audioDriver->seekTransport(pos.frame());
+ if (debugSync)
+ printf("setSongPosition %d\n", pos.tick());
+#endif
+ }
+
+//---------------------------------------------------------
+// realtimeSystemInput
+// real time message received
+//---------------------------------------------------------
+
+void MidiSeq::realtimeSystemInput(int port, int c)
+ {
+#if 0
+ 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;
+ }
+ if (!extSyncFlag)
+ return;
+ switch(c) {
+ case 0xf8: // midi clock (24 ticks / quarter note)
+ {
+ 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;
+ }
+ }
+ processMidiClock();
+
+ // Compare w audio if playing:
+ if (audio->isPlaying() /*state == PLAY*/) {
+ //BEGIN standard setup:
+ recTick += config.division / 24; // The one we're syncing to
+ int tempo = AL::tempomap.tempo(0);
+ unsigned curFrame = audio->pos().frame();
+ double songtick = (double(curFrame)/double(AL::sampleRate)) * config.division * 1000000.0 / double(tempo);
+ double scale = tdiff0/averagetimediff;
+ double tickdiff = songtick - ((double) recTick - 24 + scale*24.0);
+
+ //END standard setup
+ if (debugSync) {
+ //
+ // Create debug values for printing out which beat we're at, etc etc... yaddayadda...
+ //
+ 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", songtick, recTick, tickdiff, song_beat, sync_beat, scale);
+ }
+ //if ((mclock2 !=0.0) && (tdiff1 > 0.0) && tickdiff != 0.0 && lastTempo != 0) {
+ if ((mclock2 !=0.0) && (tdiff1 > 0.0) && fabs(tickdiff) > 2.0 && lastTempo != 0) {
+ // Interpolate:
+ double tickdiff1 = songtick1 - recTick1;
+ double tickdiff2 = songtick2 - recTick2;
+ //double newtickdiff = tickdiff/3.0 + tickdiff1/5.0 + tickdiff2/7.0; //2 min 15 sec, 120BPM, -p 512 jackd
+ //double newtickdiff = tickdiff/4.0 + tickdiff1/4.0 + tickdiff2/4.0; // Not long... :-P
+ //double newtickdiff = tickdiff/5.0 + tickdiff1/8.0 + tickdiff2/12.0; //3,5 mins on 120BPM, -p 512 jackd
+ //double newtickdiff = tickdiff/7.0 + tickdiff1/8.0 + tickdiff2/9.0; //2 min 15 sec, 120BPM, -p 512 jackd
+ //double newtickdiff = tickdiff/5.0 + tickdiff1/8.0 + tickdiff2/16.0; //3,5 mins on 120BPM, -p 512 jackd
+ double newtickdiff = tickdiff/5.0 + tickdiff1/16.0 + tickdiff2/24.0; //5 mins 30 secs on 116BPM, -p 512 jackd
+ //double newtickdiff = tickdiff/5.0 + tickdiff1/23.0 + tickdiff2/31.0; //5 mins on 116BPM, -p 512 jackd
+ //double newtickdiff = tickdiff + tickdiff1/8.0 + tickdiff2/16.0; // Not long...
+
+ if (newtickdiff != 0.0) {
+ int newTempo = AL::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));
+ AL::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) {
+//TODO3 midiPorts[port].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 = AL::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));
+ AL::tempomap.setTempo(0, newTempo);
+ }
+ }
+ mclock2 = mclock1;
+ mclock1 = mclock0;
+ }
+ break;
+ case 0xf9: // midi tick (every 10 msec)
+ if (mcStart) {
+ song->setPos(0, mcStartTick);
+ mcStart = false;
+ return;
+ }
+ break;
+ case 0xfa: // start
+ if (debugSync)
+ printf(" start\n");
+ if (!audio->isPlaying() /*state == IDLE*/) {
+ //seek(0);
+ audioDriver->seekTransport(0);
+ unsigned curFrame = audioDriver->framePos();
+ recTick = recTick1 = recTick2 = 0;
+ mclock1 = 0.0; mclock2 = 0.0;
+ songtick1 = songtick2 = 0;
+ if (debugSync)
+ printf(" curFrame: %d curTick: %d tempo: %d\n", curFrame, recTick, AL::tempomap.tempo(0));
+
+ //startPlay();
+ storedtimediffs = 0;
+ for (int i=0; i<24; i++)
+ timediff[i] = 0.0;
+ audio->msgPlay(true);
+ }
+ break;
+ case 0xfb: // continue
+ if (debugSync)
+ printf(" continue\n");
+ if (!audio->isPlaying() /*state == IDLE */) {
+ unsigned curFrame = audioDriver->framePos();
+ recTick = AL::tempomap.frame2tick(curFrame); // don't think this will work... (ml)
+ audio->msgPlay(true);
+ }
+ break;
+ case 0xfc: // stop
+ if (debugSync)
+ printf(" stop\n");
+ if (audio->isPlaying() /*state == PLAY*/)
+ audio->msgPlay(false);
+ break;
+ case 0xfd: // unknown
+ case 0xfe: // active sensing
+ case 0xff: // system reset
+ break;
+ }
+#endif
+
+ }
+
+//---------------------------------------------------------
+// mtcSyncMsg
+// process received mtc Sync
+// seekFlag - first complete mtc frame received after
+// start
+//---------------------------------------------------------
+
+void MidiSeq::mtcSyncMsg(const MTC& /*mtc*/, bool /*seekFlag*/)
+ {
+#if 0
+ double time = mtc.time();
+ if (debugSync)
+ printf("mtcSyncMsg: time %f\n", time);
+
+ if (seekFlag && state == START_PLAY) {
+// int tick = tempomap.time2tick(time);
+ state = PLAY;
+ sendMsgToGui(MSG_PLAY);
+ return;
+ }
+ // double curT = curTime();
+
+ 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);
+#endif
+ }
+#endif
+