mirror of
https://github.com/cookiengineer/audacity
synced 2025-04-30 15:49:41 +02:00
785 lines
28 KiB
C
785 lines
28 KiB
C
/* 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 */
|