2 * Copyright (C) 2000-2017 Paul Davis <paul@linuxaudiosystems.com>
3 * Copyright (C) 2009-2010 David Robillard <d@drobilla.net>
4 * Copyright (C) 2009-2012 Carl Hetherington <carl@carlh.net>
5 * Copyright (C) 2014-2015 Robin Gareus <robin@gareus.org>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 #include "temporal/time.h"
26 #include "temporal/bbt_time.h"
28 #include "pbd/error.h"
30 #include "midi++/mmc.h"
31 #include "midi++/port.h"
32 #include "midi++/parser.h"
34 #ifndef __INT_MAX__ // 'ssize_t' won't be defined yet
43 * As libtimecode is linked statically to libmidi++ this
44 * is necessary to pull in all the symbols from libtimecode
45 * so they are exported for other users of libtimecode.
47 double tmp = Timecode::BBT_Time::ticks_per_beat;
49 static std::map<int,string> mmc_cmd_map;
50 static void build_mmc_cmd_map ()
52 pair<int,string> newpair;
55 newpair.second = "Stop";
56 mmc_cmd_map.insert (newpair);
59 newpair.second = "Play";
60 mmc_cmd_map.insert (newpair);
63 newpair.second = "DeferredPlay";
64 mmc_cmd_map.insert (newpair);
67 newpair.second = "FastForward";
68 mmc_cmd_map.insert (newpair);
71 newpair.second = "Rewind";
72 mmc_cmd_map.insert (newpair);
75 newpair.second = "RecordStrobe";
76 mmc_cmd_map.insert (newpair);
79 newpair.second = "RecordExit";
80 mmc_cmd_map.insert (newpair);
83 newpair.second = "RecordPause";
84 mmc_cmd_map.insert (newpair);
87 newpair.second = "Pause";
88 mmc_cmd_map.insert (newpair);
91 newpair.second = "Eject";
92 mmc_cmd_map.insert (newpair);
95 newpair.second = "Chase";
96 mmc_cmd_map.insert (newpair);
99 newpair.second = "CommandErrorReset";
100 mmc_cmd_map.insert (newpair);
103 newpair.second = "MmcReset";
104 mmc_cmd_map.insert (newpair);
106 newpair.first = 0x20;
107 newpair.second = "Illegal Mackie Jog Start";
108 mmc_cmd_map.insert (newpair);
110 newpair.first = 0x21;
111 newpair.second = "Illegal Mackie Jog Stop";
112 mmc_cmd_map.insert (newpair);
114 newpair.first = 0x40;
115 newpair.second = "Write";
116 mmc_cmd_map.insert (newpair);
118 newpair.first = 0x41;
119 newpair.second = "MaskedWrite";
120 mmc_cmd_map.insert (newpair);
122 newpair.first = 0x42;
123 newpair.second = "Read";
124 mmc_cmd_map.insert (newpair);
126 newpair.first = 0x43;
127 newpair.second = "Update";
128 mmc_cmd_map.insert (newpair);
130 newpair.first = 0x44;
131 newpair.second = "Locate";
132 mmc_cmd_map.insert (newpair);
134 newpair.first = 0x45;
135 newpair.second = "VariablePlay";
136 mmc_cmd_map.insert (newpair);
138 newpair.first = 0x46;
139 newpair.second = "Search";
140 mmc_cmd_map.insert (newpair);
142 newpair.first = 0x47;
143 newpair.second = "Shuttle";
144 mmc_cmd_map.insert (newpair);
146 newpair.first = 0x48;
147 newpair.second = "Step";
148 mmc_cmd_map.insert (newpair);
150 newpair.first = 0x49;
151 newpair.second = "AssignSystemMaster";
152 mmc_cmd_map.insert (newpair);
154 newpair.first = 0x4A;
155 newpair.second = "GeneratorCommand";
156 mmc_cmd_map.insert (newpair);
158 newpair.first = 0x4B;
159 newpair.second = "MtcCommand";
160 mmc_cmd_map.insert (newpair);
162 newpair.first = 0x4C;
163 newpair.second = "Move";
164 mmc_cmd_map.insert (newpair);
166 newpair.first = 0x4D;
167 newpair.second = "Add";
168 mmc_cmd_map.insert (newpair);
170 newpair.first = 0x4E;
171 newpair.second = "Subtract";
172 mmc_cmd_map.insert (newpair);
174 newpair.first = 0x4F;
175 newpair.second = "DropFrameAdjust";
176 mmc_cmd_map.insert (newpair);
178 newpair.first = 0x50;
179 newpair.second = "Procedure";
180 mmc_cmd_map.insert (newpair);
182 newpair.first = 0x51;
183 newpair.second = "Event";
184 mmc_cmd_map.insert (newpair);
186 newpair.first = 0x52;
187 newpair.second = "Group";
188 mmc_cmd_map.insert (newpair);
190 newpair.first = 0x53;
191 newpair.second = "CommandSegment";
192 mmc_cmd_map.insert (newpair);
194 newpair.first = 0x54;
195 newpair.second = "DeferredVariablePlay";
196 mmc_cmd_map.insert (newpair);
198 newpair.first = 0x55;
199 newpair.second = "RecordStrobeVariable";
200 mmc_cmd_map.insert (newpair);
202 newpair.first = 0x7C;
203 newpair.second = "Wait";
204 mmc_cmd_map.insert (newpair);
206 newpair.first = 0x7F;
207 newpair.second = "Resume";
208 mmc_cmd_map.insert (newpair);
211 MachineControl::MachineControl ()
213 build_mmc_cmd_map ();
215 _receive_device_id = 0x7f;
216 _send_device_id = 0x7f;
220 MachineControl::set_ports (MIDI::Port* ip, MIDI::Port* op)
222 port_connections.drop_connections ();
227 _input_port->parser()->mmc.connect_same_thread (port_connections, boost::bind (&MachineControl::process_mmc_message, this, _1, _2, _3));
228 _input_port->parser()->start.connect_same_thread (port_connections, boost::bind (&MachineControl::spp_start, this));
229 _input_port->parser()->contineu.connect_same_thread (port_connections, boost::bind (&MachineControl::spp_continue, this));
230 _input_port->parser()->stop.connect_same_thread (port_connections, boost::bind (&MachineControl::spp_stop, this));
234 MachineControl::set_receive_device_id (MIDI::byte id)
236 _receive_device_id = id & 0x7f;
240 MachineControl::set_send_device_id (MIDI::byte id)
242 _send_device_id = id & 0x7f;
246 MachineControl::is_mmc (MIDI::byte *sysex_buf, size_t len)
248 if (len < 4 || len > 48) {
252 if (sysex_buf[1] != 0x7f) {
256 if (sysex_buf[3] != 0x6 && /* MMC Command */
257 sysex_buf[3] != 0x7) { /* MMC Response */
265 MachineControl::process_mmc_message (Parser &, MIDI::byte *msg, size_t len)
271 /* Reject if its not for us. 0x7f is the "all-call" device ID */
273 /* msg[0] = 0x7f (MMC sysex ID(
275 msg[2] = 0x6 (MMC command) or 0x7 (MMC response)
276 msg[3] = MMC command code
277 msg[4] = (typically) byte count for following part of command
281 cerr << "*** me = " << (int) _receive_device_id << " MMC message: len = " << len << "\n\t";
282 for (size_t i = 0; i < len; i++) {
283 cerr << hex << (int) msg[i] << dec << ' ';
288 if (_receive_device_id != 0x7f && msg[1] != 0x7f && msg[1] != _receive_device_id) {
299 /* this works for all non-single-byte "counted"
300 commands. we set it to 1 for the exceptions.
303 std::map<int,string>::iterator x = mmc_cmd_map.find ((int)mmc_msg[0]);
304 string cmdname = "unknown";
306 if (x != mmc_cmd_map.end()) {
307 cmdname = (*x).second;
311 cerr << "+++ MMC type "
315 << " \"" << cmdname << "\" "
322 /* SINGLE-BYTE, UNCOUNTED COMMANDS */
334 case cmdDeferredPlay:
335 DeferredPlay (*this);
349 case cmdRecordStrobe:
350 RecordStrobe (*this);
379 case cmdCommandErrorReset:
380 CommandErrorReset (*this);
389 case cmdIllegalMackieJogStart:
394 case cmdIllegalMackieJogStop:
399 /* END OF SINGLE-BYTE, UNCOUNTED COMMANDS */
402 do_masked_write (mmc_msg, len);
406 do_locate (mmc_msg, len);
410 do_shuttle (mmc_msg, len);
414 do_step (mmc_msg, len);
420 case cmdVariablePlay:
422 case cmdAssignSystemMaster:
423 case cmdGeneratorCommand:
428 case cmdDropFrameAdjust:
432 case cmdCommandSegment:
433 case cmdDeferredVariablePlay:
434 case cmdRecordStrobeVariable:
437 error << "MIDI::MachineControl: unimplemented MMC command "
438 << hex << (int) *mmc_msg << dec
444 error << "MIDI::MachineControl: unknown MMC command "
445 << hex << (int) *mmc_msg << dec
451 /* increase skiplen to cover the command byte and
452 count byte (if it existed).
456 skiplen = mmc_msg[1] + 2;
461 if (len <= skiplen) {
468 } while (len > 1); /* skip terminating EOX byte */
472 MachineControl::do_masked_write (MIDI::byte *msg, size_t len)
474 /* return the number of bytes "consumed" */
476 int retval = msg[1] + 2; /* bytes following + 2 */
479 case 0x4f: /* Track Record Ready Status */
480 write_track_status (&msg[3], len - 3, msg[2]);
483 case 0x62: /* track mute */
484 write_track_status (&msg[3], len - 3, msg[2]);
488 warning << "MIDI::MachineControl: masked write to "
489 << hex << (int) msg[2] << dec
490 << " not implemented"
498 MachineControl::write_track_status (MIDI::byte *msg, size_t /*len*/, MIDI::byte reg)
503 /* Bits 0-4 of the first byte are for special tracks:
511 the format of the message (its an MMC Masked Write) is:
514 <count> byte count of following data
515 <name> byte value of the field being written
516 <byte #> byte number of target byte in the
517 bitmap being written to
518 <mask> ones in the mask indicate which bits will be changed
519 <data> new data for the byte being written
521 by the time this code is executing, msg[0] is the
522 byte number of the target byte. if its zero, we
523 are writing to a special byte in the standard
524 track bitmap, in which the first 5 bits are
525 special. hence the bits for tracks 1 + 2 are bits
526 5 and 6 of the first byte of the track
529 change track 1: msg[0] = 0; << first byte of track bitmap
530 msg[1] = 0100000; << binary: bit 5 set
532 change track 2: msg[0] = 0; << first byte of track bitmap
533 msg[1] = 1000000; << binary: bit 6 set
535 change track 3: msg[0] = 1; << second byte of track bitmap
536 msg[1] = 0000001; << binary: bit 0 set
538 the (msg[0] * 8) - 6 computation is an attempt to
539 extract the value of the first track: ie. the one
540 that would be indicated by bit 0 being set.
542 so, if msg[0] = 0, msg[1] = 0100000 (binary),
543 what happens is that base_track = -5, but by the
544 time we check the correct bit, n = 5, and so the
545 computed track for the status change is 0 (first
548 if msg[0] = 1, then the base track for any change is 2 (the third track), and so on.
551 /* XXX check needed to make sure we don't go outside the
552 supported number of tracks.
558 base_track = (msg[0] * 8) - 6;
561 for (n = 0; n < 7; n++) {
562 if (msg[1] & (1<<n)) {
564 /* Only touch tracks that have the "mask"
568 bool val = (msg[2] & (1<<n));
572 trackRecordStatus[base_track+n] = val;
573 TrackRecordStatusChange (*this, base_track+n, val);
577 trackMute[base_track+n] = val;
578 TrackMuteChange (*this, base_track+n, val);
587 MachineControl::do_locate (MIDI::byte *msg, size_t /*msglen*/)
590 warning << "MIDI::MMC: locate [I/F] command not supported"
595 /* regular "target" locate command */
597 Locate (*this, &msg[3]);
602 MachineControl::do_step (MIDI::byte *msg, size_t /*msglen*/)
604 int steps = msg[2] & 0x3f;
615 MachineControl::do_shuttle (MIDI::byte *msg, size_t /*msglen*/)
632 left_shift = (sh & 0x38);
634 integral = ((sh & 0x7) << left_shift) | (sm >> (7 - left_shift));
635 fractional = ((sm << left_shift) << 7) | sl;
637 shuttle_speed = integral +
638 ((float)fractional / (1 << (14 - left_shift)));
640 Shuttle (*this, shuttle_speed, forward);
646 MachineControl::enable_send (bool yn)
651 /** Send a MMC command to a the MMC port.
655 MachineControl::send (MachineControlCommand const & c, timestamp_t when)
657 if (_output_port == 0 || !_enable_send) {
658 // cerr << "Not delivering MMC " << _mmc->port() << " - " << session_send_mmc << endl;
662 MIDI::byte buffer[32];
663 MIDI::byte* b = c.fill_buffer (this, buffer);
665 if (_output_port->midimsg (buffer, b - buffer, when)) {
666 error << "MMC: cannot send command" << endmsg;
671 MachineControl::spp_start ()
673 SPPStart (); /* EMIT SIGNAL */
677 MachineControl::spp_continue ()
679 SPPContinue (); /* EMIT SIGNAL */
683 MachineControl::spp_stop ()
685 SPPStop (); /* EMIT SIGNAL */
688 MachineControlCommand::MachineControlCommand (MachineControl::Command c)
694 MachineControlCommand::MachineControlCommand (Timecode::Time t)
695 : _command (MachineControl::cmdLocate)
702 MachineControlCommand::fill_buffer (MachineControl* mmc, MIDI::byte* b) const
704 *b++ = 0xf0; // SysEx
705 *b++ = 0x7f; // Real-time SysEx ID for MMC
706 *b++ = mmc->send_device_id();
707 *b++ = 0x6; // MMC command
711 if (_command == MachineControl::cmdLocate) {
712 *b++ = 0x6; // byte count
713 *b++ = 0x1; // "TARGET" subcommand
714 *b++ = _time.hours % 24;
715 *b++ = _time.minutes;
716 *b++ = _time.seconds;
718 *b++ = _time.subframes;