Logo Search packages:      
Sourcecode: ardour version File versions  Download package

mmc.cc

/*
    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: mmc.cc,v 1.13 2005/04/18 19:08:06 pauld Exp $
*/

#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 
                  (slot (*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:
                  warning << "MIDI::MachineControl: unimplemented MMC command "
                        << hex << (int) *mmc_msg << dec
                        << endmsg;

                  break;

            default:
                  warning << "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) >> 3;

      integral = ((sh & 0x7) << left_shift) | (sm >> (7 - left_shift));
      fractional = (((sm << left_shift) << 7) | (sl << left_shift)) & 0x3FFF;

      shuttle_speed = integral + 
            ((float)fractional / (1 << (14)));

      Shuttle (*this, shuttle_speed, forward);

      return 0;
}


Generated by  Doxygen 1.6.0   Back to index