summaryrefslogtreecommitdiff
path: root/libs/midi++2/mmc.cc
diff options
context:
space:
mode:
authorTaybin Rutkin <taybin@taybin.com>2005-05-13 20:47:18 +0000
committerTaybin Rutkin <taybin@taybin.com>2005-05-13 20:47:18 +0000
commitd09f6b3016bacbc2871a8946cbb24ad705076509 (patch)
treef27312839c2a772cb2ce068a4f28b2449ad869df /libs/midi++2/mmc.cc
Initial revision
git-svn-id: svn://localhost/trunk/ardour2@4 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs/midi++2/mmc.cc')
-rw-r--r--libs/midi++2/mmc.cc577
1 files changed, 577 insertions, 0 deletions
diff --git a/libs/midi++2/mmc.cc b/libs/midi++2/mmc.cc
new file mode 100644
index 0000000000..a0de774329
--- /dev/null
+++ b/libs/midi++2/mmc.cc
@@ -0,0 +1,577 @@
+/*
+ Copyright (C) 2000 Paul Barton-Davis
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#include <map>
+
+#include <pbd/error.h>
+#include <midi++/mmc.h>
+#include <midi++/port.h>
+#include <midi++/parser.h>
+
+using namespace std;
+using namespace MIDI;
+
+static std::map<int,string> mmc_cmd_map;
+static void build_mmc_cmd_map ()
+{
+ pair<int,string> newpair;
+
+ newpair.first = 0x1;
+ newpair.second = "Stop";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x2;
+ newpair.second = "Play";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x3;
+ newpair.second = "DeferredPlay";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x4;
+ newpair.second = "FastForward";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x5;
+ newpair.second = "Rewind";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x6;
+ newpair.second = "RecordStrobe";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x7;
+ newpair.second = "RecordExit";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x8;
+ newpair.second = "RecordPause";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x9;
+ newpair.second = "Pause";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0xA;
+ newpair.second = "Eject";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0xB;
+ newpair.second = "Chase";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0xC;
+ newpair.second = "CommandErrorReset";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0xD;
+ newpair.second = "MmcReset";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x20;
+ newpair.second = "Illegal Mackie Jog Start";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x21;
+ newpair.second = "Illegal Mackie Jog Stop";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x40;
+ newpair.second = "Write";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x41;
+ newpair.second = "MaskedWrite";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x42;
+ newpair.second = "Read";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x43;
+ newpair.second = "Update";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x44;
+ newpair.second = "Locate";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x45;
+ newpair.second = "VariablePlay";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x46;
+ newpair.second = "Search";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x47;
+ newpair.second = "Shuttle";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x48;
+ newpair.second = "Step";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x49;
+ newpair.second = "AssignSystemMaster";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x4A;
+ newpair.second = "GeneratorCommand";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x4B;
+ newpair.second = "MtcCommand";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x4C;
+ newpair.second = "Move";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x4D;
+ newpair.second = "Add";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x4E;
+ newpair.second = "Subtract";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x4F;
+ newpair.second = "DropFrameAdjust";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x50;
+ newpair.second = "Procedure";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x51;
+ newpair.second = "Event";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x52;
+ newpair.second = "Group";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x53;
+ newpair.second = "CommandSegment";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x54;
+ newpair.second = "DeferredVariablePlay";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x55;
+ newpair.second = "RecordStrobeVariable";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x7C;
+ newpair.second = "Wait";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x7F;
+ newpair.second = "Resume";
+ mmc_cmd_map.insert (newpair);
+}
+
+
+MachineControl::MachineControl (Port &p, float version,
+ CommandSignature &csig,
+ ResponseSignature &rsig)
+
+ : _port (p)
+{
+ Parser *parser;
+
+ build_mmc_cmd_map ();
+
+ _device_id = 1;
+
+ if ((parser = _port.input()) != 0) {
+ parser->mmc.connect
+ (mem_fun (*this, &MachineControl::process_mmc_message));
+ } else {
+ warning << "MMC connected to a non-input port: useless!"
+ << endmsg;
+ }
+}
+
+void
+MachineControl::set_device_id (byte id)
+
+{
+ _device_id = id & 0x7f;
+}
+
+bool
+MachineControl::is_mmc (byte *sysex_buf, size_t len)
+
+{
+ if (len < 4 || len > 48) {
+ return false;
+ }
+
+ if (sysex_buf[1] != 0x7f) {
+ return false;
+ }
+
+ if (sysex_buf[3] != 0x6 && /* MMC Command */
+ sysex_buf[3] != 0x7) { /* MMC Response */
+ return false;
+ }
+
+ return true;
+}
+
+void
+MachineControl::process_mmc_message (Parser &p, byte *msg, size_t len)
+
+{
+ size_t skiplen;
+ byte *mmc_msg;
+ bool single_byte;
+
+ /* Reject if its not for us. 0x7f is the "all-call" device ID */
+
+ /* msg[0] = 0x7f (MMC sysex ID(
+ msg[1] = device ID
+ msg[2] = 0x6 (MMC command) or 0x7 (MMC response)
+ msg[3] = MMC command code
+ msg[4] = (typically) byte count for following part of command
+ */
+
+#if 0
+ cerr << "*** MMC message: len = " << len << "\n\t";
+ for (size_t i = 0; i < len; i++) {
+ cerr << hex << (int) msg[i] << dec << ' ';
+ }
+ cerr << endl;
+#endif
+
+ if (msg[1] != 0x7f && msg[1] != _device_id) {
+ return;
+ }
+
+ mmc_msg = &msg[3];
+ len -= 3;
+
+ do {
+
+ single_byte = false;
+
+ /* this works for all non-single-byte "counted"
+ commands. we set it to 1 for the exceptions.
+ */
+
+ std::map<int,string>::iterator x = mmc_cmd_map.find ((int)mmc_msg[0]);
+ string cmdname = "unknown";
+
+ if (x != mmc_cmd_map.end()) {
+ cmdname = (*x).second;
+ }
+
+#if 0
+ cerr << "+++ MMC type "
+ << hex
+ << ((int) *mmc_msg)
+ << dec
+ << " \"" << cmdname << "\" "
+ << " len = " << len
+ << endl;
+#endif
+
+ switch (*mmc_msg) {
+
+ /* SINGLE-BYTE, UNCOUNTED COMMANDS */
+
+ case cmdStop:
+ Stop (*this);
+ single_byte = true;
+ break;
+
+ case cmdPlay:
+ Play (*this);
+ single_byte = true;
+ break;
+
+ case cmdDeferredPlay:
+ DeferredPlay (*this);
+ single_byte = true;
+ break;
+
+ case cmdFastForward:
+ FastForward (*this);
+ single_byte = true;
+ break;
+
+ case cmdRewind:
+ Rewind (*this);
+ single_byte = true;
+ break;
+
+ case cmdRecordStrobe:
+ RecordStrobe (*this);
+ single_byte = true;
+ break;
+
+ case cmdRecordExit:
+ RecordExit (*this);
+ single_byte = true;
+ break;
+
+ case cmdRecordPause:
+ RecordPause (*this);
+ single_byte = true;
+ break;
+
+ case cmdPause:
+ Pause (*this);
+ single_byte = true;
+ break;
+
+ case cmdEject:
+ Eject (*this);
+ single_byte = true;
+ break;
+
+ case cmdChase:
+ Chase (*this);
+ single_byte = true;
+ break;
+
+ case cmdCommandErrorReset:
+ CommandErrorReset (*this);
+ single_byte = true;
+ break;
+
+ case cmdMmcReset:
+ MmcReset (*this);
+ single_byte = true;
+ break;
+
+ case cmdIllegalMackieJogStart:
+ JogStart (*this);
+ single_byte = true;
+ break;
+
+ case cmdIllegalMackieJogStop:
+ JogStop (*this);
+ single_byte = true;
+ break;
+
+ /* END OF SINGLE-BYTE, UNCOUNTED COMMANDS */
+
+ case cmdMaskedWrite:
+ do_masked_write (mmc_msg, len);
+ break;
+
+ case cmdLocate:
+ do_locate (mmc_msg, len);
+ break;
+
+ case cmdShuttle:
+ do_shuttle (mmc_msg, len);
+ break;
+
+ case cmdStep:
+ do_step (mmc_msg, len);
+ break;
+
+ case cmdWrite:
+ case cmdRead:
+ case cmdUpdate:
+ case cmdVariablePlay:
+ case cmdSearch:
+ case cmdAssignSystemMaster:
+ case cmdGeneratorCommand:
+ case cmdMtcCommand:
+ case cmdMove:
+ case cmdAdd:
+ case cmdSubtract:
+ case cmdDropFrameAdjust:
+ case cmdProcedure:
+ case cmdEvent:
+ case cmdGroup:
+ case cmdCommandSegment:
+ case cmdDeferredVariablePlay:
+ case cmdRecordStrobeVariable:
+ case cmdWait:
+ case cmdResume:
+ error << "MIDI::MachineControl: unimplemented MMC command "
+ << hex << (int) *mmc_msg << dec
+ << endmsg;
+
+ break;
+
+ default:
+ error << "MIDI::MachineControl: unknown MMC command "
+ << hex << (int) *mmc_msg << dec
+ << endmsg;
+
+ break;
+ }
+
+ /* increase skiplen to cover the command byte and
+ count byte (if it existed).
+ */
+
+ if (!single_byte) {
+ skiplen = mmc_msg[1] + 2;
+ } else {
+ skiplen = 1;
+ }
+
+ if (len <= skiplen) {
+ break;
+ }
+
+ mmc_msg += skiplen;
+ len -= skiplen;
+
+ } while (len > 1); /* skip terminating EOX byte */
+}
+
+int
+MachineControl::do_masked_write (byte *msg, size_t len)
+
+{
+ /* return the number of bytes "consumed" */
+
+ int retval = msg[1] + 2; /* bytes following + 2 */
+
+ switch (msg[2]) {
+ case 0x4f: /* Track Record Ready Status */
+ write_track_record_ready (&msg[3], len - 3);
+ break;
+
+ default:
+ warning << "MIDI::MachineControl: masked write to "
+ << hex << (int) msg[2] << dec
+ << " not implemented"
+ << endmsg;
+ }
+
+ return retval;
+}
+
+void
+MachineControl::write_track_record_ready (byte *msg, size_t len)
+
+{
+ size_t n;
+ size_t base_track;
+
+ /* Bits 0-4 of the first byte are for special tracks:
+
+ bit 0: video
+ bit 1: reserved
+ bit 2: time code
+ bit 3: aux track a
+ bit 4: aux track b
+
+ */
+
+ /* XXX check needed to make sure we don't go outside the
+ support number of tracks.
+ */
+
+ base_track = (msg[0] * 7) - 5;
+
+ for (n = 0; n < 7; n++) {
+ if (msg[1] & (1<<n)) {
+
+ /* Only touch tracks that have the "mask"
+ bit set.
+ */
+
+ if (msg[2] & (1<<n)) {
+ trackRecordStatus[base_track+n] = true;
+ TrackRecordStatusChange (*this, base_track+n,
+ true);
+ } else {
+ trackRecordStatus[base_track+n] = false;
+ TrackRecordStatusChange (*this, base_track+n,
+ false);
+ }
+ }
+
+ }
+}
+
+int
+MachineControl::do_locate (byte *msg, size_t msglen)
+
+{
+ if (msg[2] == 0) {
+ warning << "MIDI::MMC: locate [I/F] command not supported"
+ << endmsg;
+ return 0;
+ }
+
+ /* regular "target" locate command */
+
+ Locate (*this, &msg[3]);
+ return 0;
+}
+
+int
+MachineControl::do_step (byte *msg, size_t msglen)
+{
+ int steps = msg[2] & 0x3f;
+
+ if (msg[2] & 0x40) {
+ steps = -steps;
+ }
+
+ Step (*this, steps);
+ return 0;
+}
+
+int
+MachineControl::do_shuttle (byte *msg, size_t msglen)
+
+{
+ size_t forward;
+ byte sh = msg[2];
+ byte sm = msg[3];
+ byte sl = msg[4];
+ size_t left_shift;
+ size_t integral;
+ size_t fractional;
+ float shuttle_speed;
+
+ if (sh & (1<<6)) {
+ forward = false;
+ } else {
+ forward = true;
+ }
+
+ left_shift = (sh & 0x38);
+
+ integral = ((sh & 0x7) << left_shift) | (sm >> (7 - left_shift));
+ fractional = ((sm << left_shift) << 7) | sl;
+
+ shuttle_speed = integral +
+ ((float)fractional / (1 << (14 - left_shift)));
+
+ Shuttle (*this, shuttle_speed, forward);
+
+ return 0;
+}
+