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