summaryrefslogtreecommitdiff
path: root/cmt/midimgr.c
diff options
context:
space:
mode:
Diffstat (limited to 'cmt/midimgr.c')
-rw-r--r--cmt/midimgr.c784
1 files changed, 784 insertions, 0 deletions
diff --git a/cmt/midimgr.c b/cmt/midimgr.c
new file mode 100644
index 0000000..e7db136
--- /dev/null
+++ b/cmt/midimgr.c
@@ -0,0 +1,784 @@
+/* midimgr.c -- this file contains interface code to support use of Apple Midi Manager */
+/*
+ * This code is based on code supplied with the Apple Midi Manager.
+ * Copyright 1991, Carnegie Mellon University
+ */
+
+/* BUGS:
+ * If exclusive() is called to turn exclusive messages on or off DURING the
+ * receipt of an exclusive message, incoming data will be garbled. The correct
+ * handling would be to record when receipt of an exclusive message is in
+ * progress, then properly remove any partial message when exclusive is turned
+ * off, and ignore any remaining message part when exclusive is turned on.
+ * The present code does neither.
+ */
+
+#include "cext.h"
+#undef round
+#ifdef THINK_C
+#include <pascal.h> /* for ThinkC 7 */
+#endif
+
+#include "stdio.h"
+#include "userio.h"
+#include "MIDI.h"
+#include "midifns.h"
+#include "midibuff.h"
+#include "midierr.h"
+#include "midimgr.h"
+#include "midicode.h"
+#include "cmdline.h"
+
+/* Needed for KillEverybody */
+#include <toolutils.h>
+#include <AppleEvents.h>
+#include <EPPC.h>
+#include <Gestalt.h>
+#include <PPCToolbox.h>
+#include <Processes.h>
+#include <Sound.h>
+
+
+#define CMTclientID 'CMT '
+/* note the following are in alphabetical order for Patcher display */
+#define timePortID 'Atim'
+#define inputPortID 'Bin '
+#define outputPortID 'Cout'
+#define noClient ' '
+
+#define noTimeBaseRefNum 0
+#define noReadHook 0L
+#define zeroTime 0L
+#define timePortBuffSize 0L
+#define inputPortBuffSize 2048
+#define outputPortBuffSize 0L
+#define refCon0 0L
+
+pascal short CMTreader(MIDIPacket *ThePacketPtr, long TheRefCon);
+
+/* "patch" switch from command line. This switch is cached in patch_flag and tells
+ whether to look in the resource fork for a patch, or just hook up to midi in and
+ out. If the resource fork is used, the patch will be saved upon exit. */
+private boolean patch_flag;
+extern boolean ctrlFilter;
+extern boolean exclFilter;
+extern boolean realFilter;
+
+private midi_read_lock = false; /* used to stop input during data structure manipulation */
+
+private void set_error(int bit);
+#ifndef NYQUIST
+void PatchPorts(void);
+void SavePatch(OSType PortID, short PortInfoResID, char *PortInfoResName);
+#endif
+
+/* exported: */
+public short InputRefNum; /* Input port reference number. */
+public short OutputRefNum; /* Output port reference number. */
+public short TimeRefNum; /* Time base port reference number. */
+
+Boolean GManualPatch; /* True if not launched by a PatchBay Config. File. */
+
+/****************************************************************************
+*
+* variables shared with other modules
+*
+****************************************************************************/
+
+/* midi input buffer */
+long buff[BUFF_SIZE/4]; /* data buffer, declared long to get 32-bit alignment */
+int buffhead = 0; /* buffer head and tail pointers */
+int bufftail = 0;
+
+/* user supplied system exclusive buffer */
+byte *xbuff = NULL; /* address of the user-supplied buffer */
+public long xbufmask; /* mask for circular buffer address calculation */
+long xbufhead = 0; /* buffer head and tail pointers */
+long xbuftail = 0;
+boolean xbuf_flush = true; /* says to flush remainder of sysex message */
+
+#ifdef SYSEXDEBUG
+int sysexcount = 0; /* for debugging */
+int sysexdone = 0;
+int sysexheadcount = 0;
+byte sysexfirst = 0;
+int sysexsysex = 0;
+#endif
+
+/* midi_flush -- empty out buffers */
+/**/
+void midi_flush()
+{
+ midi_read_lock = true;
+ buffhead = 0;
+ bufftail = 0;
+ xbufhead = 0;
+ xbuftail = 0;
+ xbuf_flush = true; /* in case sysex continuation messages are still coming */
+ midi_read_lock = false;
+}
+
+
+/* Nyquist only uses CMT for Midi and Adagio file IO */
+#ifndef NYQUIST
+/* Get String representation of MIDI Mgr Version Num.*/
+/* See Mac Tech Note #189 for details. */
+char *StdMacVerNumToStr(long VerNum, char *VerStr)
+{
+ char *RetVal;
+ char MajVer, MinVer, VerStage, VerRev, BugFixVer = 0;
+
+ if (VerNum == 0)
+ {
+ RetVal = NULL;
+ }
+ else
+ {
+ MajVer = (VerNum & 0xFF000000) >> 24;
+ MinVer = (VerNum & 0x00FF0000) >> 16;
+ VerStage = (VerNum & 0x0000FF00) >> 8;
+ VerRev = (VerNum & 0x000000FF) >> 0;
+ BugFixVer = MinVer & 0x0F;
+
+ switch (VerStage)
+ {
+ case 0x20:
+ VerStage = 'd';
+ break;
+ case 0x40:
+ VerStage = 'a';
+ break;
+ case 0x60:
+ VerStage = 'b';
+ break;
+ case 0x80:
+ VerStage = ' ';
+ break;
+ default:
+ VerStage = '?';
+ break;
+ }
+
+ if (BugFixVer == 0)
+ {
+ sprintf(VerStr,"%X.%X%c%X",
+ MajVer, MinVer>>4, VerStage, VerRev);
+ }
+ else
+ {
+ sprintf(VerStr,"%X.%X.%X%c%X",
+ MajVer, MinVer >> 4, MinVer & 0x0F, VerStage, VerRev);
+ }
+
+ RetVal = VerStr;
+ }
+
+ return(RetVal);
+}
+
+
+/* C2PStrCpy -- Convert a C String (from Cstr) into a Pascal string */
+/*
+ * NOTE: this is not the same code as shipped with midi manager example
+ */
+char *C2PStrCpy(char *Cstr, Str255 Pstr)
+{
+ char *c = Cstr;
+ char *p = ((char *) Pstr) + 1;
+
+ while (*c) *p++ = *c++;
+ *Pstr = c - Cstr;
+ return( (char *) Pstr );
+}
+
+/* This checks to see if THINK C is running under System 7,
+ and ONLY WORKS UNDER SYSTEM 7! Don't use unless you check! */
+boolean ThinkCRunning(void)
+{
+ ProcessSerialNumber processSN;
+ OSErr myErr;
+ ProcessInfoRec infoRec;
+
+ processSN.lowLongOfPSN = kNoProcess;
+ processSN.highLongOfPSN = kNoProcess;
+ do {
+ myErr = GetNextProcess(&processSN);
+
+ infoRec.processInfoLength = sizeof(ProcessInfoRec);
+ infoRec.processName = 0L;
+ infoRec.processAppSpec = 0L;
+ myErr = GetProcessInformation(&processSN, &infoRec);
+ if (!myErr) {
+ if (infoRec.processSignature == 'KAHL') {
+ return(true);
+ }
+ }
+ } while (myErr == noErr);
+ return(false);
+}
+
+/* This kills off all the other running processes...
+ ONLY WORKS UNDER SYSTEM 7! Don't use unless you check! */
+void KillEverybody(void)
+{
+ ProcessSerialNumber myProc, processSN;
+ ProcessSerialNumber finderPSN;
+ ProcessInfoRec infoRec;
+ Str31 processName;
+ FSSpec procSpec;
+
+ OSErr myErr = noErr;
+ OSErr otherError;
+ AppleEvent theEvent;
+ AEDesc theAddress;
+ Boolean ourFlag, notFinder;
+ Boolean finderFound = false;
+
+ GetCurrentProcess(&myProc);
+ /* Preset the PSN to no PSN, see IM VI, the Process Manager */
+ processSN.lowLongOfPSN = kNoProcess;
+ processSN.highLongOfPSN = kNoProcess;
+ finderPSN.lowLongOfPSN = 0UL; /* brk: was nil */
+ finderPSN.highLongOfPSN = 0UL; /* brk: was nil */
+
+ do {
+ myErr = GetNextProcess(&processSN);
+ /* See if it's us first */
+ notFinder = true;
+ SameProcess(&myProc, &processSN, &ourFlag);
+
+ infoRec.processInfoLength = sizeof(ProcessInfoRec);
+ infoRec.processName = (StringPtr) &processName;
+ infoRec.processAppSpec = &procSpec;
+ GetProcessInformation(&processSN, &infoRec);
+ if (!ourFlag && !finderFound) {
+ /* see if it's the Finder, we have to kill the finder LAST */
+ /* or else non-sys 7 apps won't get killed */
+ /* since the Finder must be there to convert the AppleEvent to Puppet Strings */
+ /* if the app is not APpleEvent aware */
+ /* Also, FileShare HAS to be killed before the Finder */
+ /* or your life will be unpleasant */
+
+ if (infoRec.processSignature == 'MACS' && infoRec.processType == 'FNDR') {
+ /* save this number for later */
+ finderPSN = processSN;
+ notFinder = false;
+ finderFound = true;
+
+ } else {
+ notFinder = true;
+ }
+ }
+ if (!myErr && !ourFlag && notFinder) {
+ otherError = AECreateDesc(typeProcessSerialNumber, (Ptr)&processSN, sizeof(processSN), &theAddress);
+ if (!otherError)
+ otherError = AECreateAppleEvent(kCoreEventClass, kAEQuitApplication, &theAddress, kAutoGenerateReturnID,
+ kAnyTransactionID, &theEvent);
+ if (!otherError)
+ AEDisposeDesc(&theAddress);
+ /* Again, the Finder will convert the AppleEvent to puppetstrings if */
+ /* the application is a System 6 or non-AE aware app. This ONLY */
+ /* happens for the 4 required (oapp,odoc,pdoc, and quit) AppleEvents */
+ /* and ONLY if you use the PSN for the address */
+ if (!otherError)
+ AESend(&theEvent, 0L, kAENoReply + kAEAlwaysInteract + kAECanSwitchLayer, kAENormalPriority, kAEDefaultTimeout,
+ 0L, 0L);
+ AEDisposeDesc(&theEvent);
+ }
+ } while (!myErr);
+
+ /* Now, if the finder was running, it's safe to kill it */
+ if (finderPSN.lowLongOfPSN || finderPSN.highLongOfPSN) {
+ otherError = AECreateDesc(typeProcessSerialNumber, (Ptr)&finderPSN, sizeof(processSN), &theAddress);
+ if (!otherError)
+ otherError = AECreateAppleEvent(kCoreEventClass, kAEQuitApplication, &theAddress, kAutoGenerateReturnID,
+ kAnyTransactionID, &theEvent);
+ if (!otherError)
+ AEDisposeDesc(&theAddress);
+ if (!otherError)
+ AESend(&theEvent, 0L, kAENoReply + kAEAlwaysInteract + kAECanSwitchLayer, kAENormalPriority, kAEDefaultTimeout, 0L,
+ 0L);
+ AEDisposeDesc(&theEvent);
+ }
+}
+
+/* Sign into the MIDI Manager. */
+/* Set up time, input, and output ports. */
+/* Start our time base clock. */
+void setup_midimgr(void)
+{
+ MIDIPortParams Init; /* MIDI Mgr Init data structure */
+ Handle TheIconHndl;
+ OSErr TheErr;
+ long MIDIMgrVerNum; /* MIDI Manager Ver (Std Mac Ver #) */
+ Str255 name = "\pCMU MIDI Toolkit";
+ char MIDIMgrVerStr[256]; /* MIDI Manager Ver (Std Mac Ver # String) */
+ long vers;
+ EventRecord theEvent;
+
+ Gestalt(gestaltSystemVersion, &vers);
+ vers = (vers >> 8) & 0xf; /* shift result over and mask out major version number */
+ if ((vers >= 7) && (!cl_switch("keep")) && (!ThinkCRunning())) {
+ gprintf(TRANS,"Killing other processes...\n");
+ KillEverybody();
+ for (vers=0; vers<100; ++vers) {
+ while (WaitNextEvent(everyEvent, &theEvent, 0L, 0L)) ;
+ }
+ }
+
+ /* Make sure MIDIMgr is installed and save version num. */
+ MIDIMgrVerNum = SndDispVersion(midiToolNum);
+ if (MIDIMgrVerNum == 0) {
+ gprintf(ERROR, "The MIDI Manager is not installed! Exiting...\n");
+ EXIT(1);
+ } else {
+ StdMacVerNumToStr(MIDIMgrVerNum, MIDIMgrVerStr);
+ gprintf(TRANS,"MIDI Manager Version %s\n", MIDIMgrVerStr);
+ }
+
+
+ /* Sign in to the MIDI Manager. */
+ TheIconHndl = GetResource('ICN#', 1);
+ TheErr = MIDISignIn(CMTclientID,
+ 0L,
+ TheIconHndl,
+ name);
+ if (TheErr) {
+ gprintf(ERROR, "Trouble signing into MIDI Manager! Aborting...");
+ EXIT(1);
+ }
+
+ /* Assume not a Patchbay configuration. */
+ GManualPatch = true;
+
+ /* Add time port. */
+ Init.portID = timePortID;
+ Init.portType = midiPortTypeTime;
+ Init.timeBase = noTimeBaseRefNum;
+ Init.readHook = noReadHook;
+ Init.initClock.syncType = midiInternalSync;
+ Init.initClock.curTime = zeroTime;
+ Init.initClock.format = midiFormatMSec;
+ Init.refCon = SetCurrentA5();
+ C2PStrCpy("TimeBase", Init.name);
+ TheErr = MIDIAddPort(CMTclientID, timePortBuffSize, &TimeRefNum, &Init);
+ /* Has a PatchBay connection been resolved? */
+ if (TheErr == midiVConnectMade) {
+ GManualPatch = false;
+ } else if (TheErr == memFullErr) {
+ gprintf(ERROR, "Not enough room in heap zone to add time port! Aborting...");
+ MIDISignOut(CMTclientID);
+ EXIT(1);
+ }
+
+ /* Add an input port. */
+ Init.portID = inputPortID;
+ Init.portType = midiPortTypeInput;
+ Init.timeBase = TimeRefNum;
+ Init.offsetTime = midiGetCurrent;
+ Init.readHook = NewMIDIReadHookProc(CMTreader);
+ Init.refCon = SetCurrentA5();
+ C2PStrCpy("InputPort", Init.name);
+ TheErr = MIDIAddPort(CMTclientID, inputPortBuffSize, &InputRefNum, &Init);
+ /* Has a PatchBay connection been resolved? */
+ if (TheErr == midiVConnectMade) {
+ GManualPatch = false;
+ } else if (TheErr == memFullErr) {
+ gprintf(ERROR, "Not enough room in heap zone to add input port! Aborting...");
+ MIDISignOut(CMTclientID);
+ EXIT(1);
+ }
+
+ /* Add an output port. */
+ Init.portID = outputPortID;
+ Init.portType = midiPortTypeOutput;
+ Init.timeBase = TimeRefNum;
+ Init.offsetTime = midiGetCurrent;
+ Init.readHook = NULL;
+ Init.refCon = refCon0;
+ C2PStrCpy("OutputPort", Init.name);
+ TheErr = MIDIAddPort(CMTclientID, outputPortBuffSize, &OutputRefNum, &Init);
+ /* Has a PatchBay connection been resolved? */
+ if (TheErr == midiVConnectMade) {
+ GManualPatch = false;
+ } else if (TheErr == memFullErr) {
+ printf("Not enough room in heap zone to add output port! Aborting...");
+ MIDISignOut(CMTclientID);
+ EXIT(1);
+ }
+
+ if (GManualPatch) {
+ PatchPorts(); /* connect ports as they were */
+ }
+ /* to clean this up (later) call finish_midimgr() */
+ cu_register((cu_fn_type) finish_midimgr, (cu_parm_type) finish_midimgr);
+
+ /* Start our Clock. */
+ MIDIStartTime(TimeRefNum);
+}
+
+
+/* The Read Hook Function. */
+
+/* 1st 4 bytes of sysex message get saved here and enqueued later */
+char save_sysex_head[4];
+int save_sysex_head_x = 0;
+
+void sysex_insert(unsigned char data) {
+ if (save_sysex_head_x < 4) {
+ save_sysex_head[save_sysex_head_x++] = data;
+ }
+ xbuff[xbuftail++] = data;
+ xbuftail &= xbufmask;
+ if (xbuftail == xbufhead) {
+ set_error(SYSEXOVFL);
+ }
+ if (data == MIDI_EOX) { /* we're done with the message */
+ *((long *) (((byte *) buff) + bufftail)) = *((long *)save_sysex_head);
+ bufftail = (bufftail + 4) & BUFF_MASK;
+ if (bufftail == buffhead) {
+ set_error(BUFFOVFL);
+ }
+ }
+}
+
+/* Read all incomming MIDI data. */
+
+pascal short CMTreader(MIDIPacket *ThePacketPtr, long TheRefCon)
+{
+ /* Set up our A5 world. */
+ long SysA5 = SetA5(TheRefCon);
+ short RetVal = midiMorePacket, i, j;
+ unsigned char *mm_data = ThePacketPtr->data;
+ register byte data1 = mm_data[1];
+
+ if (midi_read_lock) {
+ /* Don't want to read packet now, get it later */
+ /* DOES THIS REALLY WORK? WHAT WILL CAUSE AN INTERRUPT
+ * TO OCCUR LATER? THIS IS ONLY USED BY midi_flush, IS
+ * BASED ON THE MidiArp CODE FROM APPLE, AND IS UNTESTED - RBD
+ */
+ RetVal = midiKeepPacket;
+ goto alldone;
+ }
+
+ /* see if Packet is an error message */
+ if (((ThePacketPtr->flags & midiTypeMask) == midiMgrType) &&
+ *((short *) (&(ThePacketPtr->data))) < midiMaxErr) {
+ set_error(MIDIMGRERR);
+ goto alldone;
+ }
+
+ /* filter out control changes */
+ if (ctrlFilter) {
+ register int hibits = *mm_data & 0xF0;
+ if (hibits == 0xD0 || /* Chan Pressure */
+ hibits == 0xE0 || /* Pitch Bend */
+ hibits == 0xA0 || /* Poly Pressure */
+ ((hibits == 0xB0) && /* Control change (don't count switches) */
+ ((data1 < 64) || (data1 > 121)))) {
+ /* CONTROL MESSAGE HAS BEEN FILTERED */
+ goto alldone;
+ }
+ } else if (realFilter) {
+ register int hibits = *mm_data & 0xF0;
+ if (hibits >= 0xF8) goto alldone;
+ }
+
+
+ /* if not a continuation, copy the data into cmt_data */
+ /* The logic to detect a non-continued
+ * packet or a first packet is: "flags bit 1 is clear".
+ */
+ if ((((ThePacketPtr->flags & midiContMask) == midiNoCont)) &&
+ (*mm_data != MIDI_SYSEX)) {
+ register byte *cmt_data = ((byte *) buff) + bufftail;
+ *((long *) cmt_data) = *((long *) mm_data);
+
+ bufftail = (bufftail + 4) & BUFF_MASK;
+ if (bufftail == buffhead) {
+ /* filled buffer faster than client emptied it */
+ set_error(BUFFOVFL);
+ }
+ }
+
+ /* see if we have a sysex message to copy to buffer */
+ if (xbuff && !exclFilter &&
+ ((ThePacketPtr->flags & midiContMask) || *mm_data == MIDI_SYSEX)) {
+ int i;
+ register byte *x_data = xbuff + xbuftail;
+
+ /* iterate over data in message */
+ /* NOTE: in the previous implementation, I thought Sysex messages were
+ * always starting at the beginning of the buffer, but that didn't work.
+ * This implementation assumes nothing -- it is slower because of additional
+ * testing and parsing inside the loop, but seems to work.
+ */
+ for (i = ThePacketPtr->len - 6; i > 0; i--) {
+ if (xbuf_flush) { /* we're searching for beginning of message */
+ if (*mm_data == MIDI_SYSEX) {
+ xbuf_flush = false;
+ sysex_insert(MIDI_SYSEX);
+ }
+ } else { /* we're scanning to the end of the message */
+ if (*mm_data == MIDI_SYSEX) { /* found it, insert proper EOX */
+ sysex_insert(MIDI_EOX);
+ sysex_insert(MIDI_SYSEX);
+ } else if (*mm_data == MIDI_EOX) { /* found it */
+ sysex_insert(MIDI_EOX);
+ xbuf_flush = true;
+ } else sysex_insert(*mm_data);
+
+ }
+ mm_data++;
+ }
+ }
+alldone:
+
+ /* Restore the systems A5 world. */
+ SetA5(SysA5);
+
+ return(RetVal);
+}
+
+
+/* Sign out from the MIDI Manager. */
+void finish_midimgr(void)
+{
+ if (GManualPatch && patch_flag) {
+ SavePatch(timePortID, timePortResInfoID, "timePortInfo");
+ SavePatch(inputPortID, inputPortResInfoID, "inputPortInfo");
+ SavePatch(outputPortID, outputPortResInfoID, "outputPortInfo");
+ }
+ MIDISignOut(CMTclientID);
+}
+
+
+
+/* Alert user to Resource Manager Error. */
+void
+ReportResError(char *Msg)
+{
+ OSErr TheErr;
+ char Buf[256];
+
+ if ( (TheErr = ResError()) != noErr) {
+ gprintf(ERROR, "ResError %d: %s...Aborting.", TheErr, Msg);
+ EXIT(1);
+ } else {
+ /* gprintf(ERROR, "%s OK\n", Msg); */
+ }
+}
+
+
+/****************************************************************************
+* error handling
+* Effect:
+* various error conditions are flagged by setting bits in
+* the global midi_error_flags. it is up to the client to clear this
+* word when necessary.
+****************************************************************************/
+
+private void set_error(int bit)
+{
+ midi_error_flags |= (1 << bit);
+}
+
+
+void midi_show_errors()
+{
+ if (midi_error_flags & (1<<BUFFOVFL))
+ gprintf(ERROR, "Midi Buffer Overflow Error\n");
+ if (midi_error_flags & (1<<MIDIMGRERR))
+ gprintf(ERROR, "Midi Manager Error\n");
+ if (midi_error_flags & (1<<SYSEXOVFL))
+ gprintf(ERROR, "Midi Sysex Overflow Error\n");
+}
+
+
+/**************** PATCHING CODE ***************/
+
+/*
+ MIDIArp Time, Input, and Output Port
+ Info Record Resource ID's.
+*/
+
+/* Get previously saved port connections (port info records) */
+/* from application's 'port' resource. */
+void
+PatchPorts(void)
+{
+ MIDIPortInfoHdl PortInfoH; /* Handle to port info record. */
+ MIDIPortInfoPtr PortInfoP; /* Pointer to port info record. */
+ short i, TheErr;
+
+ patch_flag = cl_switch("patch");
+
+ /* SET UP TIME PORT CONNECTIONS. */
+ if (patch_flag)
+ PortInfoH = (MIDIPortInfoHdl) GetResource(portResType, timePortResInfoID);
+ if (!patch_flag || PortInfoH == NULL) {
+ MIDIIDListHdl clients, ports;
+ OSErr err;
+
+ gprintf(TRANS, "Connecting to MIDI IN and OUT\n");
+#ifdef MIDIMGR_VERBOSE
+ clients = MIDIGetClients();
+ gprintf(TRANS, "clients = %lx\n", clients);
+ HLock((Handle) clients);
+
+ for (i = 0; i < (*clients)->numIDs; i++) {
+ OSType id = (*clients)->list[i];
+ gprintf(TRANS, "%d: %c%c%c%c\n", i, (char) (id>>24),
+ (char) ((id >> 16) & 0xFF), (char) ((id >> 8) & 0xFF),
+ (char) (id & 0xFF));
+ }
+ ports = MIDIGetPorts('amdr');
+ HLock((Handle) ports);
+ for (i = 0; i < (*ports)->numIDs; i++) {
+ OSType id = (*ports)->list[i];
+ gprintf(TRANS, "%d: %c%c%c%c\n", i, (char) (id>>24),
+ (char) ((id >> 16) & 0xFF), (char) ((id >> 8) & 0xFF),
+ (char) (id & 0xFF));
+ }
+ HUnlock((Handle) ports);
+ HUnlock((Handle) clients);
+#endif
+ /* the work starts here */
+ err = MIDIConnectData('CMT ', 'Cout', 'amdr', 'Aout');
+ /* gprintf(TRANS, "Connected CMT.Cout to amdr.Aout: %d\n", err); */
+ err = MIDIConnectData('amdr', 'Ain ', 'CMT ', 'Bin ');
+ /* gprintf(TRANS, "Connected amdr.Ain to CMT.Bin: %d\n", err); */
+
+ return;
+ }
+ HLock((Handle) PortInfoH);
+ PortInfoP = *PortInfoH;
+ if (GetHandleSize((Handle) PortInfoH) != 0)
+ {
+ /* Were we supposed to be sync'd to another client? */
+ if (PortInfoP->timeBase.clientID != noClient)
+ {
+ /* Yes, so make that client our time base. */
+ TheErr = MIDIConnectTime(
+ PortInfoP->timeBase.clientID,
+ PortInfoP->timeBase.portID,
+ CMTclientID,
+ timePortID
+ );
+#ifdef IGNORE
+ /* Is the client still signed in? */
+ if (TheErr != midiVConnectErr)
+ {
+ /* Yes, so set our sync mode to external. */
+ MIDISetSync(ArpGlobals.TimeRefNum, midiExternalSync);
+ }
+#endif
+
+ }
+ /* Were we somebody else's time base? */
+ for (i=0; i<PortInfoP->numConnects; i++)
+ {
+ MIDIConnectTime(CMTclientID,
+ timePortID,
+ PortInfoP->cList[i].clientID,
+ PortInfoP->cList[i].portID);
+ }
+ }
+ HUnlock((Handle) PortInfoH);
+ ReleaseResource((Handle) PortInfoH);
+ ReportResError("PatchPorts/ReleaseResource()");
+
+ /* SET UP INPUT PORT CONNECTIONS. */
+ PortInfoH = (MIDIPortInfoHdl) GetResource(portResType, inputPortResInfoID);
+ if (PortInfoH == NULL)
+ {
+ ReportResError("PatchPorts/GetResource()");
+ }
+ HLock((Handle) PortInfoH);
+ PortInfoP = *PortInfoH;
+ if (GetHandleSize((Handle) PortInfoH) != 0)
+ {
+ /* Were we connected to anyone? */
+ for (i=0; i<PortInfoP->numConnects; i++)
+ {
+ MIDIConnectData(CMTclientID,
+ inputPortID,
+ PortInfoP->cList[i].clientID,
+ PortInfoP->cList[i].portID);
+ }
+ }
+ HUnlock((Handle) PortInfoH);
+ ReleaseResource((Handle) PortInfoH);
+ ReportResError("PatchPorts/GetResource()");
+
+ /* SET UP OUTPUT PORT CONNECTIONS. */
+ PortInfoH = (MIDIPortInfoHdl) GetResource(portResType, outputPortResInfoID);
+ if (PortInfoH == NULL)
+ {
+ ReportResError("PatchPorts/GetResource()");
+ }
+ HLock((Handle) PortInfoH);
+ PortInfoP = *PortInfoH;
+ if (GetHandleSize((Handle) PortInfoH) != 0) {
+ /* Were we connected to anyone? */
+ for (i=0; i<PortInfoP->numConnects; i++)
+ {
+ MIDIConnectData(CMTclientID,
+ outputPortID,
+ PortInfoP->cList[i].clientID,
+ PortInfoP->cList[i].portID);
+ }
+ }
+ HUnlock((Handle) PortInfoH);
+ ReleaseResource((Handle) PortInfoH);
+ ReportResError("PatchPorts/ReleaseResource()");
+
+}
+
+/* Save current port connections (port info records) */
+/* to application's 'port' resource. */
+void
+SavePatch(OSType PortID, short PortInfoResID, char *PortInfoResName)
+{
+ Handle PortResH; /* Handle to ptch resource. */
+ CursHandle WatchCurs;
+
+ WatchCurs = GetCursor(watchCursor);
+ HLock((Handle) WatchCurs);
+ SetCursor(*WatchCurs);
+ HUnlock((Handle) WatchCurs);
+
+
+ /* Remove existing port info resource. */
+ PortResH = GetResource(portResType, PortInfoResID);
+ /* gprintf(TRANS, "PortResH: %lx, *PortResH: %lx\n", PortResH, *PortResH); */
+ if (PortResH) {
+ ReportResError("SavePatch/GetResource()");
+ RmveResource(PortResH);
+ ReportResError("SavePatch/RmveResource()");
+ DisposHandle(PortResH);
+ UpdateResFile(CurResFile());
+ ReportResError("SavePatch/UpdateResFile()");
+ }
+
+ /* Get new configurateion. */
+ PortResH = (Handle) MIDIGetPortInfo(CMTclientID, PortID);
+
+ /* Save new configurateion. */
+ CtoPstr(PortInfoResName);
+ AddResource(PortResH, portResType, PortInfoResID,
+ (ConstStr255Param) PortInfoResName);
+ PtoCstr((unsigned char *) PortInfoResName);
+
+ ReportResError("SavePatch/AddResource()");
+ WriteResource(PortResH);
+ ReportResError("SavePatch/WriteResource()");
+ UpdateResFile(CurResFile());
+ ReportResError("SavePatch/UpdateResFile()");
+ ReleaseResource(PortResH);
+ ReportResError("SavePatch/ReleaseResource()");
+
+ InitCursor();
+}
+#endif /* NYQUIST */