ardour/libs/midi++2/mtc.cc
David Robillard 1324c25282 WIP: Fix namespace issues and build with GCC8
The build was broken for me with both GCC 8 and clang 11 due to the lookup of
operator<<().  However, since the previous pattern of using a namespace then
defining things in that namespace in... the global namespace... sort of... is
very strange, and likely to cause further problems with ADL especially as we
move to newer language versions and libraries, I opted to go all-out here and
define things inside the appropriate namespace.

This will probably resolve some earlier issues with clang and MSVC as well,
since they each use different lookup rules that all have their own quirks
around this stuff.
2021-05-25 16:41:19 -04:00

357 lines
8.1 KiB
C++

/*
* Copyright (C) 2004-2017 Paul Davis <paul@linuxaudiosystems.com>
* Copyright (C) 2008-2012 David Robillard <d@drobilla.net>
* Copyright (C) 2014-2019 Robin Gareus <robin@gareus.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <cstdlib>
#include <unistd.h>
#include <cstring>
#include <iostream>
#include "midi++/types.h"
#include "midi++/parser.h"
#include "midi++/port.h"
#include "midi++/mmc.h"
#include "pbd/transmitter.h"
using namespace std;
using namespace sigc;
#undef DEBUG_MTC
namespace MIDI {
bool
Parser::possible_mtc (MIDI::byte *sysex_buf, size_t msglen)
{
byte fake_mtc_time[5];
if (msglen != 10 || sysex_buf[0] != 0xf0 || sysex_buf[1] != 0x7f || sysex_buf[3] != 0x01 || sysex_buf[4] != 0x01) {
return false;
}
/* full MTC */
fake_mtc_time[0] = sysex_buf[8]; // frames
fake_mtc_time[1] = sysex_buf[7]; // minutes
fake_mtc_time[2] = sysex_buf[6]; // seconds
fake_mtc_time[3] = (sysex_buf[5] & 0x1f); // hours
_mtc_fps = MTC_FPS ((sysex_buf[5] & 0x60) >> 5); // fps
fake_mtc_time[4] = (byte) _mtc_fps;
/* wait for first quarter frame, which could indicate forwards
or backwards ...
*/
reset_mtc_state ();
/* emit signals */
mtc (*this, &sysex_buf[1], msglen - 1);
mtc_time (fake_mtc_time, true, _timestamp);
#ifdef DEBUG_MTC
cerr << "New full-MTC message marks state stopped" << endl;
#endif
mtc_status (MTC_Stopped);
return true;
}
void
Parser::reset_mtc_state ()
{
#ifdef DEBUG_MTC
cerr << "MTC state reset" << endl;
#endif
/* MUST REMAIN RT-SAFE */
_mtc_forward = false;
_mtc_running = MTC_Stopped;
_mtc_locked = false;
expected_mtc_quarter_frame_code = 0;
memset (_mtc_time, 0, sizeof (_mtc_time));
memset (_qtr_mtc_time, 0, sizeof (_mtc_time));
consecutive_qtr_frame_cnt = 0;
last_qtr_frame = 0;
}
void
Parser::process_mtc_quarter_frame (MIDI::byte *msg)
{
int which_quarter_frame = (msg[1] & 0xf0) >> 4;
/* Is it an expected frame?
Remember, the first can be frame 7 or frame 0,
depending on the direction of the MTC generator ...
*/
#ifdef DEBUG_MTC
cerr << "MTC: (state = " << _mtc_running << ") "
<< which_quarter_frame << " vs. " << expected_mtc_quarter_frame_code
<< " consecutive ? " << consecutive_qtr_frame_cnt
<< endl;
#endif
if (_mtc_running == MTC_Stopped) {
/* we are stopped but are seeing qtr frame messages */
if (consecutive_qtr_frame_cnt == 0) {
/* first quarter frame */
if (which_quarter_frame != 0 && which_quarter_frame != 7) {
last_qtr_frame = which_quarter_frame;
consecutive_qtr_frame_cnt++;
}
// cerr << "first seen qframe = " << (int) last_qtr_frame << endl;
return;
} else if (consecutive_qtr_frame_cnt == 1) {
/* third quarter frame */
#ifdef DEBUG_MTC
cerr << "second seen qframe = " << (int) which_quarter_frame << endl;
#endif
if (last_qtr_frame < which_quarter_frame) {
_mtc_running = MTC_Forward;
} else if (last_qtr_frame > which_quarter_frame) {
_mtc_running = MTC_Backward;
}
#ifdef DEBUG_MTC
cerr << "Send MTC status as " << _mtc_running << endl;
#endif
mtc_status (_mtc_running);
}
switch (_mtc_running) {
case MTC_Forward:
if (which_quarter_frame == 7) {
expected_mtc_quarter_frame_code = 0;
} else {
expected_mtc_quarter_frame_code = which_quarter_frame + 1;
}
break;
case MTC_Backward:
if (which_quarter_frame == 0) {
expected_mtc_quarter_frame_code = 7;
} else {
expected_mtc_quarter_frame_code = which_quarter_frame - 1;
}
break;
case MTC_Stopped:
break;
}
} else {
/* already running */
// for testing bad MIDI connections etc.
// if ((random() % 500) < 10) {
if (which_quarter_frame != expected_mtc_quarter_frame_code) {
consecutive_qtr_frame_cnt = 0;
#ifdef DEBUG_MTC
cerr << "MTC: (state = " << _mtc_running << ") "
<< which_quarter_frame << " vs. " << expected_mtc_quarter_frame_code << endl;
#endif
/* tell listener(s) that we skipped. if they return
true, just ignore this in terms of it being an error.
*/
boost::optional<bool> res = mtc_skipped ();
if (res.value_or (false)) {
/* no error, reset next expected frame */
switch (_mtc_running) {
case MTC_Forward:
if (which_quarter_frame == 7) {
expected_mtc_quarter_frame_code = 0;
} else {
expected_mtc_quarter_frame_code = which_quarter_frame + 1;
}
break;
case MTC_Backward:
if (which_quarter_frame == 0) {
expected_mtc_quarter_frame_code = 7;
} else {
expected_mtc_quarter_frame_code = which_quarter_frame - 1;
}
break;
case MTC_Stopped:
break;
}
#ifdef DEBUG_MTC
cerr << "SKIPPED, next expected = " << expected_mtc_quarter_frame_code << endl;
#endif
return;
}
/* skip counts as an error ... go back to waiting for the first frame */
#ifdef DEBUG_MTC
cerr << "Skipped MTC qtr frame, return to stopped state" << endl;
#endif
reset_mtc_state ();
mtc_status (MTC_Stopped);
return;
} else {
/* received qtr frame matched expected */
consecutive_qtr_frame_cnt++;
}
}
/* time code is looking good */
#ifdef DEBUG_MTC
cerr << "for quarter frame " << which_quarter_frame << " byte = " << hex << (int) msg[1] << dec << endl;
#endif
switch (which_quarter_frame) {
case 0: // frames LS nibble
_qtr_mtc_time[0] |= msg[1] & 0xf;
break;
case 1: // frames MS nibble
_qtr_mtc_time[0] |= (msg[1] & 0xf)<<4;
break;
case 2: // seconds LS nibble
_qtr_mtc_time[1] |= msg[1] & 0xf;
break;
case 3: // seconds MS nibble
_qtr_mtc_time[1] |= (msg[1] & 0xf)<<4;
break;
case 4: // minutes LS nibble
_qtr_mtc_time[2] |= msg[1] & 0xf;
break;
case 5: // minutes MS nibble
_qtr_mtc_time[2] |= (msg[1] & 0xf)<<4;
break;
case 6: // hours LS nibble
_qtr_mtc_time[3] |= msg[1] & 0xf;
break;
case 7:
/* last quarter frame msg has the MS bit of
the hour in bit 0, and the SMPTE FPS type
in bits 5 and 6
*/
_qtr_mtc_time[3] |= ((msg[1] & 0x1) << 4);
_mtc_fps = MTC_FPS ((msg[1] & 0x6) >> 1);
_qtr_mtc_time[4] = _mtc_fps;
break;
default:
abort(); /*NOTREACHED*/
break;
}
#ifdef DEBUG_MTC
cerr << "Emit MTC Qtr\n";
#endif
mtc_qtr (*this, which_quarter_frame, _timestamp); /* EMIT_SIGNAL */
// mtc (*this, &msg[1], msglen - 1);
switch (_mtc_running) {
case MTC_Forward:
if (which_quarter_frame == 7) {
/* we've reached the final of 8 quarter frame messages.
store the time, reset the pending time holder,
and signal anyone who wants to know the time.
*/
if (consecutive_qtr_frame_cnt >= 8) {
memcpy (_mtc_time, _qtr_mtc_time, sizeof (_mtc_time));
memset (_qtr_mtc_time, 0, sizeof (_qtr_mtc_time));
if (!_mtc_locked) {
_mtc_locked = true;
}
mtc_time (_mtc_time, false, _timestamp);
}
expected_mtc_quarter_frame_code = 0;
} else {
expected_mtc_quarter_frame_code = which_quarter_frame + 1;
}
break;
case MTC_Backward:
if (which_quarter_frame == 0) {
/* we've reached the final of 8 quarter frame messages.
store the time, reset the pending time holder,
and signal anyone who wants to know the time.
*/
if (consecutive_qtr_frame_cnt >= 8) {
memcpy (_mtc_time, _qtr_mtc_time, sizeof (_mtc_time));
memset (_qtr_mtc_time, 0, sizeof (_qtr_mtc_time));
if (!_mtc_locked) {
_mtc_locked = true;
}
mtc_time (_mtc_time, false, _timestamp);
}
expected_mtc_quarter_frame_code = 7;
} else {
expected_mtc_quarter_frame_code = which_quarter_frame - 1;
}
break;
default:
break;
}
}
} // namespace MIDI