diff options
Diffstat (limited to 'cmt/midimgr.c')
-rw-r--r-- | cmt/midimgr.c | 784 |
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 */ |