committed RWlock fixes to libardour. added hw monitoring fixes from nick_m. minor...
[ardour.git] / libs / midi++2 / mmc.cc
1 /*
2     Copyright (C) 2000 Paul Barton-Davis 
3
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.
8
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.
13
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.
17
18     $Id$
19 */
20
21 #include <map>
22
23 #include <pbd/error.h>
24 #include <midi++/mmc.h>
25 #include <midi++/port.h>
26 #include <midi++/parser.h>
27
28 using namespace std;
29 using namespace MIDI;
30
31 static std::map<int,string> mmc_cmd_map;
32 static void build_mmc_cmd_map ()
33 {
34         pair<int,string> newpair;
35
36         newpair.first = 0x1;
37         newpair.second = "Stop";
38         mmc_cmd_map.insert (newpair);
39
40         newpair.first = 0x2;
41         newpair.second = "Play";
42         mmc_cmd_map.insert (newpair);
43
44         newpair.first = 0x3;
45         newpair.second = "DeferredPlay";
46         mmc_cmd_map.insert (newpair);
47
48         newpair.first = 0x4;
49         newpair.second = "FastForward";
50         mmc_cmd_map.insert (newpair);
51
52         newpair.first = 0x5;
53         newpair.second = "Rewind";
54         mmc_cmd_map.insert (newpair);
55
56         newpair.first = 0x6;
57         newpair.second = "RecordStrobe";
58         mmc_cmd_map.insert (newpair);
59
60         newpair.first = 0x7;
61         newpair.second = "RecordExit";
62         mmc_cmd_map.insert (newpair);
63
64         newpair.first = 0x8;
65         newpair.second = "RecordPause";
66         mmc_cmd_map.insert (newpair);
67
68         newpair.first = 0x9;
69         newpair.second = "Pause";
70         mmc_cmd_map.insert (newpair);
71
72         newpair.first = 0xA;
73         newpair.second = "Eject";
74         mmc_cmd_map.insert (newpair);
75
76         newpair.first = 0xB;
77         newpair.second = "Chase";
78         mmc_cmd_map.insert (newpair);
79
80         newpair.first = 0xC;
81         newpair.second = "CommandErrorReset";
82         mmc_cmd_map.insert (newpair);
83
84         newpair.first = 0xD;
85         newpair.second = "MmcReset";
86         mmc_cmd_map.insert (newpair);
87
88         newpair.first = 0x20;
89         newpair.second = "Illegal Mackie Jog Start";
90         mmc_cmd_map.insert (newpair);
91
92         newpair.first = 0x21;
93         newpair.second = "Illegal Mackie Jog Stop";
94         mmc_cmd_map.insert (newpair);
95
96         newpair.first = 0x40;
97         newpair.second = "Write";
98         mmc_cmd_map.insert (newpair);
99
100         newpair.first = 0x41;
101         newpair.second = "MaskedWrite";
102         mmc_cmd_map.insert (newpair);
103
104         newpair.first = 0x42;
105         newpair.second = "Read";
106         mmc_cmd_map.insert (newpair);
107
108         newpair.first = 0x43;
109         newpair.second = "Update";
110         mmc_cmd_map.insert (newpair);
111
112         newpair.first = 0x44;
113         newpair.second = "Locate";
114         mmc_cmd_map.insert (newpair);
115
116         newpair.first = 0x45;
117         newpair.second = "VariablePlay";
118         mmc_cmd_map.insert (newpair);
119
120         newpair.first = 0x46;
121         newpair.second = "Search";
122         mmc_cmd_map.insert (newpair);
123
124         newpair.first = 0x47;
125         newpair.second = "Shuttle";
126         mmc_cmd_map.insert (newpair);
127
128         newpair.first = 0x48;
129         newpair.second = "Step";
130         mmc_cmd_map.insert (newpair);
131
132         newpair.first = 0x49;
133         newpair.second = "AssignSystemMaster";
134         mmc_cmd_map.insert (newpair);
135
136         newpair.first = 0x4A;
137         newpair.second = "GeneratorCommand";
138         mmc_cmd_map.insert (newpair);
139
140         newpair.first = 0x4B;
141         newpair.second = "MtcCommand";
142         mmc_cmd_map.insert (newpair);
143
144         newpair.first = 0x4C;
145         newpair.second = "Move";
146         mmc_cmd_map.insert (newpair);
147
148         newpair.first = 0x4D;
149         newpair.second = "Add";
150         mmc_cmd_map.insert (newpair);
151
152         newpair.first = 0x4E;
153         newpair.second = "Subtract";
154         mmc_cmd_map.insert (newpair);
155
156         newpair.first = 0x4F;
157         newpair.second = "DropFrameAdjust";
158         mmc_cmd_map.insert (newpair);
159
160         newpair.first = 0x50;
161         newpair.second = "Procedure";
162         mmc_cmd_map.insert (newpair);
163
164         newpair.first = 0x51;
165         newpair.second = "Event";
166         mmc_cmd_map.insert (newpair);
167
168         newpair.first = 0x52;
169         newpair.second = "Group";
170         mmc_cmd_map.insert (newpair);
171
172         newpair.first = 0x53;
173         newpair.second = "CommandSegment";
174         mmc_cmd_map.insert (newpair);
175
176         newpair.first = 0x54;
177         newpair.second = "DeferredVariablePlay";
178         mmc_cmd_map.insert (newpair);
179
180         newpair.first = 0x55;
181         newpair.second = "RecordStrobeVariable";
182         mmc_cmd_map.insert (newpair);
183
184         newpair.first = 0x7C;
185         newpair.second = "Wait";
186         mmc_cmd_map.insert (newpair);
187
188         newpair.first = 0x7F;
189         newpair.second = "Resume";
190         mmc_cmd_map.insert (newpair);
191 }
192
193
194 MachineControl::MachineControl (Port &p, float version,
195                                 CommandSignature &csig,
196                                 ResponseSignature &rsig)
197
198         : _port (p)
199 {
200         Parser *parser;
201         
202         build_mmc_cmd_map ();
203
204         _device_id = 1;
205         
206         if ((parser = _port.input()) != 0) {
207                 parser->mmc.connect 
208                         (mem_fun (*this, &MachineControl::process_mmc_message));
209         } else {
210                 warning << "MMC connected to a non-input port: useless!"
211                         << endmsg;
212         }
213 }
214
215 void
216 MachineControl::set_device_id (byte id)
217
218 {
219         _device_id = id & 0x7f;
220 }
221
222 bool
223 MachineControl::is_mmc (byte *sysex_buf, size_t len)
224
225 {
226         if (len < 4 || len > 48) {
227                 return false;
228         }
229
230         if (sysex_buf[1] != 0x7f) {
231                 return false;
232         }
233
234         if (sysex_buf[3] != 0x6 && /* MMC Command */
235             sysex_buf[3] != 0x7) { /* MMC Response */
236                 return false;
237         }
238         
239         return true;
240 }
241
242 void
243 MachineControl::process_mmc_message (Parser &p, byte *msg, size_t len)
244
245 {
246         size_t skiplen;
247         byte *mmc_msg;
248         bool single_byte;
249
250         /* Reject if its not for us. 0x7f is the "all-call" device ID */
251
252         /* msg[0] = 0x7f (MMC sysex ID(
253            msg[1] = device 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
257         */
258
259 #if 0
260         cerr << "*** MMC message: len = " << len << "\n\t";
261         for (size_t i = 0; i < len; i++) {
262                 cerr << hex << (int) msg[i] << dec << ' ';
263         }
264         cerr << endl;
265 #endif
266
267         if (msg[1] != 0x7f && msg[1] != _device_id) {
268                 return;
269         }
270
271         mmc_msg = &msg[3];
272         len -= 3;
273
274         do {
275
276                 single_byte = false;
277
278                 /* this works for all non-single-byte "counted"
279                    commands. we set it to 1 for the exceptions.
280                 */
281
282                 std::map<int,string>::iterator x = mmc_cmd_map.find ((int)mmc_msg[0]);
283                 string cmdname = "unknown";
284
285                 if (x != mmc_cmd_map.end()) {
286                         cmdname = (*x).second;
287                 }
288
289 #if 0
290                 cerr << "+++ MMC type " 
291                      << hex
292                      << ((int) *mmc_msg)
293                      << dec
294                      << " \"" << cmdname << "\" "
295                      << " len = " << len
296                      << endl;
297 #endif
298
299                 switch (*mmc_msg) {
300
301                 /* SINGLE-BYTE, UNCOUNTED COMMANDS */
302
303                 case cmdStop:
304                         Stop (*this);
305                         single_byte = true;
306                         break;
307
308                 case cmdPlay:
309                         Play (*this);
310                         single_byte = true;
311                         break;
312
313                 case cmdDeferredPlay:
314                         DeferredPlay (*this);
315                         single_byte = true;
316                         break;
317
318                 case cmdFastForward:
319                         FastForward (*this);
320                         single_byte = true;
321                         break;
322
323                 case cmdRewind:
324                         Rewind (*this);
325                         single_byte = true;
326                         break;
327
328                 case cmdRecordStrobe:
329                         RecordStrobe (*this);
330                         single_byte = true;
331                         break;
332
333                 case cmdRecordExit:
334                         RecordExit (*this);
335                         single_byte = true;
336                         break;
337
338                 case cmdRecordPause:
339                         RecordPause (*this);
340                         single_byte = true;
341                         break;
342
343                 case cmdPause:
344                         Pause (*this);
345                         single_byte = true;
346                         break;
347
348                 case cmdEject:
349                         Eject (*this);
350                         single_byte = true;
351                         break;
352
353                 case cmdChase:
354                         Chase (*this);
355                         single_byte = true;
356                         break;
357
358                 case cmdCommandErrorReset:
359                         CommandErrorReset (*this);
360                         single_byte = true;
361                         break;
362
363                 case cmdMmcReset:
364                         MmcReset (*this);
365                         single_byte = true;
366                         break;
367
368                 case cmdIllegalMackieJogStart:
369                         JogStart (*this);
370                         single_byte = true;
371                         break;
372
373                 case cmdIllegalMackieJogStop:
374                         JogStop (*this);
375                         single_byte = true;
376                         break;
377
378                 /* END OF SINGLE-BYTE, UNCOUNTED COMMANDS */
379
380                 case cmdMaskedWrite:
381                         do_masked_write (mmc_msg, len);
382                         break;
383
384                 case cmdLocate:
385                         do_locate (mmc_msg, len);
386                         break;
387
388                 case cmdShuttle:
389                         do_shuttle (mmc_msg, len);
390                         break;
391
392                 case cmdStep:
393                         do_step (mmc_msg, len);
394                         break;
395
396                 case cmdWrite:
397                 case cmdRead:
398                 case cmdUpdate:
399                 case cmdVariablePlay:
400                 case cmdSearch:
401                 case cmdAssignSystemMaster:
402                 case cmdGeneratorCommand:
403                 case cmdMtcCommand:
404                 case cmdMove:
405                 case cmdAdd:
406                 case cmdSubtract:
407                 case cmdDropFrameAdjust:
408                 case cmdProcedure:
409                 case cmdEvent:
410                 case cmdGroup:
411                 case cmdCommandSegment:
412                 case cmdDeferredVariablePlay:
413                 case cmdRecordStrobeVariable:
414                 case cmdWait:
415                 case cmdResume:
416                         error << "MIDI::MachineControl: unimplemented MMC command "
417                               << hex << (int) *mmc_msg << dec
418                               << endmsg;
419
420                         break;
421
422                 default:
423                         error << "MIDI::MachineControl: unknown MMC command "
424                               << hex << (int) *mmc_msg << dec
425                               << endmsg;
426
427                         break;
428                 }
429
430                 /* increase skiplen to cover the command byte and 
431                    count byte (if it existed).
432                 */
433
434                 if (!single_byte) {
435                         skiplen = mmc_msg[1] + 2;
436                 } else {
437                         skiplen = 1;
438                 }
439
440                 if (len <= skiplen) {
441                         break;
442                 }
443
444                 mmc_msg += skiplen;
445                 len -= skiplen;
446
447         } while (len > 1); /* skip terminating EOX byte */
448 }               
449
450 int
451 MachineControl::do_masked_write (byte *msg, size_t len)
452
453 {
454         /* return the number of bytes "consumed" */
455
456         int retval = msg[1] + 2; /* bytes following + 2 */
457         
458         switch (msg[2]) {
459         case 0x4f:  /* Track Record Ready Status */
460                 write_track_record_ready (&msg[3], len - 3);
461                 break;
462
463         default:
464                 warning << "MIDI::MachineControl: masked write to "
465                         << hex << (int) msg[2] << dec
466                         << " not implemented"
467                         << endmsg;
468         }
469
470         return retval;
471 }
472
473 void
474 MachineControl::write_track_record_ready (byte *msg, size_t len)
475
476 {
477         size_t n;
478         size_t base_track;
479
480         /* Bits 0-4 of the first byte are for special tracks:
481
482            bit 0: video
483            bit 1: reserved
484            bit 2: time code
485            bit 3: aux track a
486            bit 4: aux track b
487
488         */
489
490         /* XXX check needed to make sure we don't go outside the
491            support number of tracks.
492         */
493
494         base_track = (msg[0] * 7) - 5;
495
496         for (n = 0; n < 7; n++) {
497                 if (msg[1] & (1<<n)) {
498
499                         /* Only touch tracks that have the "mask"
500                            bit set.
501                         */
502
503                         if (msg[2] & (1<<n)) {
504                                 trackRecordStatus[base_track+n] = true;
505                                 TrackRecordStatusChange (*this, base_track+n,
506                                                          true);
507                         } else {
508                                 trackRecordStatus[base_track+n] = false;
509                                 TrackRecordStatusChange (*this, base_track+n,
510                                                          false);
511                         }
512                 } 
513
514         }
515 }
516
517 int
518 MachineControl::do_locate (byte *msg, size_t msglen)
519
520 {
521         if (msg[2] == 0) {
522                 warning << "MIDI::MMC: locate [I/F] command not supported"
523                         << endmsg;
524                 return 0;
525         }
526
527         /* regular "target" locate command */
528
529         Locate (*this, &msg[3]);
530         return 0;
531 }
532
533 int
534 MachineControl::do_step (byte *msg, size_t msglen)
535 {
536         int steps = msg[2] & 0x3f;
537
538         if (msg[2] & 0x40) {
539                 steps = -steps;
540         }
541
542         Step (*this, steps);
543         return 0;
544 }
545
546 int
547 MachineControl::do_shuttle (byte *msg, size_t msglen)
548
549 {
550         size_t forward;
551         byte sh = msg[2];
552         byte sm = msg[3];
553         byte sl = msg[4];
554         size_t left_shift;
555         size_t integral;
556         size_t fractional;
557         float shuttle_speed;
558
559         if (sh & (1<<6)) {
560                 forward = false;
561         } else {
562                 forward = true;
563         }
564         
565         left_shift = (sh & 0x38);
566
567         integral = ((sh & 0x7) << left_shift) | (sm >> (7 - left_shift));
568         fractional = ((sm << left_shift) << 7) | sl;
569
570         shuttle_speed = integral + 
571                 ((float)fractional / (1 << (14 - left_shift)));
572
573         Shuttle (*this, shuttle_speed, forward);
574
575         return 0;
576 }
577