2 Copyright (C) 2000 Paul Barton-Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 #include <pbd/error.h>
24 #include <midi++/mmc.h>
25 #include <midi++/port.h>
26 #include <midi++/parser.h>
31 static std::map<int,string> mmc_cmd_map;
32 static void build_mmc_cmd_map ()
34 pair<int,string> newpair;
37 newpair.second = "Stop";
38 mmc_cmd_map.insert (newpair);
41 newpair.second = "Play";
42 mmc_cmd_map.insert (newpair);
45 newpair.second = "DeferredPlay";
46 mmc_cmd_map.insert (newpair);
49 newpair.second = "FastForward";
50 mmc_cmd_map.insert (newpair);
53 newpair.second = "Rewind";
54 mmc_cmd_map.insert (newpair);
57 newpair.second = "RecordStrobe";
58 mmc_cmd_map.insert (newpair);
61 newpair.second = "RecordExit";
62 mmc_cmd_map.insert (newpair);
65 newpair.second = "RecordPause";
66 mmc_cmd_map.insert (newpair);
69 newpair.second = "Pause";
70 mmc_cmd_map.insert (newpair);
73 newpair.second = "Eject";
74 mmc_cmd_map.insert (newpair);
77 newpair.second = "Chase";
78 mmc_cmd_map.insert (newpair);
81 newpair.second = "CommandErrorReset";
82 mmc_cmd_map.insert (newpair);
85 newpair.second = "MmcReset";
86 mmc_cmd_map.insert (newpair);
89 newpair.second = "Illegal Mackie Jog Start";
90 mmc_cmd_map.insert (newpair);
93 newpair.second = "Illegal Mackie Jog Stop";
94 mmc_cmd_map.insert (newpair);
97 newpair.second = "Write";
98 mmc_cmd_map.insert (newpair);
100 newpair.first = 0x41;
101 newpair.second = "MaskedWrite";
102 mmc_cmd_map.insert (newpair);
104 newpair.first = 0x42;
105 newpair.second = "Read";
106 mmc_cmd_map.insert (newpair);
108 newpair.first = 0x43;
109 newpair.second = "Update";
110 mmc_cmd_map.insert (newpair);
112 newpair.first = 0x44;
113 newpair.second = "Locate";
114 mmc_cmd_map.insert (newpair);
116 newpair.first = 0x45;
117 newpair.second = "VariablePlay";
118 mmc_cmd_map.insert (newpair);
120 newpair.first = 0x46;
121 newpair.second = "Search";
122 mmc_cmd_map.insert (newpair);
124 newpair.first = 0x47;
125 newpair.second = "Shuttle";
126 mmc_cmd_map.insert (newpair);
128 newpair.first = 0x48;
129 newpair.second = "Step";
130 mmc_cmd_map.insert (newpair);
132 newpair.first = 0x49;
133 newpair.second = "AssignSystemMaster";
134 mmc_cmd_map.insert (newpair);
136 newpair.first = 0x4A;
137 newpair.second = "GeneratorCommand";
138 mmc_cmd_map.insert (newpair);
140 newpair.first = 0x4B;
141 newpair.second = "MtcCommand";
142 mmc_cmd_map.insert (newpair);
144 newpair.first = 0x4C;
145 newpair.second = "Move";
146 mmc_cmd_map.insert (newpair);
148 newpair.first = 0x4D;
149 newpair.second = "Add";
150 mmc_cmd_map.insert (newpair);
152 newpair.first = 0x4E;
153 newpair.second = "Subtract";
154 mmc_cmd_map.insert (newpair);
156 newpair.first = 0x4F;
157 newpair.second = "DropFrameAdjust";
158 mmc_cmd_map.insert (newpair);
160 newpair.first = 0x50;
161 newpair.second = "Procedure";
162 mmc_cmd_map.insert (newpair);
164 newpair.first = 0x51;
165 newpair.second = "Event";
166 mmc_cmd_map.insert (newpair);
168 newpair.first = 0x52;
169 newpair.second = "Group";
170 mmc_cmd_map.insert (newpair);
172 newpair.first = 0x53;
173 newpair.second = "CommandSegment";
174 mmc_cmd_map.insert (newpair);
176 newpair.first = 0x54;
177 newpair.second = "DeferredVariablePlay";
178 mmc_cmd_map.insert (newpair);
180 newpair.first = 0x55;
181 newpair.second = "RecordStrobeVariable";
182 mmc_cmd_map.insert (newpair);
184 newpair.first = 0x7C;
185 newpair.second = "Wait";
186 mmc_cmd_map.insert (newpair);
188 newpair.first = 0x7F;
189 newpair.second = "Resume";
190 mmc_cmd_map.insert (newpair);
194 MachineControl::MachineControl (Port &p, float version,
195 CommandSignature &csig,
196 ResponseSignature &rsig)
202 build_mmc_cmd_map ();
206 if ((parser = _port.input()) != 0) {
208 (mem_fun (*this, &MachineControl::process_mmc_message));
210 warning << "MMC connected to a non-input port: useless!"
216 MachineControl::set_device_id (byte id)
219 _device_id = id & 0x7f;
223 MachineControl::is_mmc (byte *sysex_buf, size_t len)
226 if (len < 4 || len > 48) {
230 if (sysex_buf[1] != 0x7f) {
234 if (sysex_buf[3] != 0x6 && /* MMC Command */
235 sysex_buf[3] != 0x7) { /* MMC Response */
243 MachineControl::process_mmc_message (Parser &p, byte *msg, size_t len)
250 /* Reject if its not for us. 0x7f is the "all-call" device ID */
252 /* msg[0] = 0x7f (MMC sysex ID(
254 msg[2] = 0x6 (MMC command) or 0x7 (MMC response)
255 msg[3] = MMC command code
256 msg[4] = (typically) byte count for following part of command
260 cerr << "*** MMC message: len = " << len << "\n\t";
261 for (size_t i = 0; i < len; i++) {
262 cerr << hex << (int) msg[i] << dec << ' ';
267 if (msg[1] != 0x7f && msg[1] != _device_id) {
278 /* this works for all non-single-byte "counted"
279 commands. we set it to 1 for the exceptions.
282 std::map<int,string>::iterator x = mmc_cmd_map.find ((int)mmc_msg[0]);
283 string cmdname = "unknown";
285 if (x != mmc_cmd_map.end()) {
286 cmdname = (*x).second;
290 cerr << "+++ MMC type "
294 << " \"" << cmdname << "\" "
301 /* SINGLE-BYTE, UNCOUNTED COMMANDS */
313 case cmdDeferredPlay:
314 DeferredPlay (*this);
328 case cmdRecordStrobe:
329 RecordStrobe (*this);
358 case cmdCommandErrorReset:
359 CommandErrorReset (*this);
368 case cmdIllegalMackieJogStart:
373 case cmdIllegalMackieJogStop:
378 /* END OF SINGLE-BYTE, UNCOUNTED COMMANDS */
381 do_masked_write (mmc_msg, len);
385 do_locate (mmc_msg, len);
389 do_shuttle (mmc_msg, len);
393 do_step (mmc_msg, len);
399 case cmdVariablePlay:
401 case cmdAssignSystemMaster:
402 case cmdGeneratorCommand:
407 case cmdDropFrameAdjust:
411 case cmdCommandSegment:
412 case cmdDeferredVariablePlay:
413 case cmdRecordStrobeVariable:
416 error << "MIDI::MachineControl: unimplemented MMC command "
417 << hex << (int) *mmc_msg << dec
423 error << "MIDI::MachineControl: unknown MMC command "
424 << hex << (int) *mmc_msg << dec
430 /* increase skiplen to cover the command byte and
431 count byte (if it existed).
435 skiplen = mmc_msg[1] + 2;
440 if (len <= skiplen) {
447 } while (len > 1); /* skip terminating EOX byte */
451 MachineControl::do_masked_write (byte *msg, size_t len)
454 /* return the number of bytes "consumed" */
456 int retval = msg[1] + 2; /* bytes following + 2 */
459 case 0x4f: /* Track Record Ready Status */
460 write_track_record_ready (&msg[3], len - 3);
464 warning << "MIDI::MachineControl: masked write to "
465 << hex << (int) msg[2] << dec
466 << " not implemented"
474 MachineControl::write_track_record_ready (byte *msg, size_t len)
480 /* Bits 0-4 of the first byte are for special tracks:
490 /* XXX check needed to make sure we don't go outside the
491 support number of tracks.
494 base_track = (msg[0] * 7) - 5;
496 for (n = 0; n < 7; n++) {
497 if (msg[1] & (1<<n)) {
499 /* Only touch tracks that have the "mask"
503 if (msg[2] & (1<<n)) {
504 trackRecordStatus[base_track+n] = true;
505 TrackRecordStatusChange (*this, base_track+n,
508 trackRecordStatus[base_track+n] = false;
509 TrackRecordStatusChange (*this, base_track+n,
518 MachineControl::do_locate (byte *msg, size_t msglen)
522 warning << "MIDI::MMC: locate [I/F] command not supported"
527 /* regular "target" locate command */
529 Locate (*this, &msg[3]);
534 MachineControl::do_step (byte *msg, size_t msglen)
536 int steps = msg[2] & 0x3f;
547 MachineControl::do_shuttle (byte *msg, size_t msglen)
565 left_shift = (sh & 0x38);
567 integral = ((sh & 0x7) << left_shift) | (sm >> (7 - left_shift));
568 fractional = ((sm << left_shift) << 7) | sl;
570 shuttle_speed = integral +
571 ((float)fractional / (1 << (14 - left_shift)));
573 Shuttle (*this, shuttle_speed, forward);