/* 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 #include #include #include #include using namespace std; using namespace MIDI; static std::map mmc_cmd_map; static void build_mmc_cmd_map () { pair 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::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<> (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; }