Updates to SirenMIDIPlugin from Jay Hardesty
Stephen T. Pope
stp at create.ucsb.edu
Wed Nov 29 14:53:04 UTC 2000
Hello all,
Jay Hardesty made a few extensions and fixes to the SirenMIDIPlugin;
they are attached to this letter.
Is anyone looking into porting the plugin to Linux or Windows?
stp
Jay Hardesty wrote:
>
> [ ... ]
>
> Here's a slightly altered version of sqMacMIDI.OMS.c
> which seems to fix the problem for us - I'm not sure
> if this was the right way to go about it, but so far
> so good. Since input is already working fine for
> you these changes may not be very interesting, but
> sending them back just in case they might be of some
> use (or maybe some amusement) somehow.
>
> The main changes were:
> Uncommenting out a block of your code in sqOpenMIDI which
> sets up OMS to read from all interfaces. This change made
> me kind of nervous because I assume you're getting input
> without running through this code...
> I also added a condition in that same block of code
> so that only devices checked 'Is a controller' in OMS Setup
> are actually used for input:
> (*nodeInfoList)->info[i].flags & kAnyIsController
>
> Added curly braces for a couple of nested if-else
> statements in sqMIDIPlaySizeOn. [ ... ]
>
> Also here's a mindless little smalltalk method for
> setting up the MIDICommands shared pool in MIDIPort
> that adds a few more status messages.
--
stp
Stephen Travis Pope
http://www.create.ucsb.edu/~stp
-------------- next part --------------
'From Squeak2.8 of 13 June 2000 [latest update: #2359] on 28 November 2000 at 10:29:55 pm'!
!MIDIPort class methodsFor: 'as yet unclassified' stamp: 'jh 11/28/2000 22:28'!
initializeMIDICommands
"
MIDIPort initializeMIDICommands
"
MIDICommands
ifNil: [MIDICommands _ Dictionary new]
ifNotNil: [MIDICommands keysAndValuesRemove: [:a :b | true]].
MIDICommands
at: #noteOff put: 128;
at: #noteOn put: 144;
at: #polyTouch put: 160;
at: #ctrlChange put: 176;
at: #progChange put: 192;
at: #chanTouch put: 208;
at: #pitchWheel put: 224;
at: #sysEx put: 240;
at: #mtc put: 241;
at: #spp put: 242;
at: #midiClock put: 248;
at: #midiStart put: 250;
at: #midiContinue put: 251;
at: #midiStop put: 252.
(MIDIPort sharedPools includes: MIDICommands) ifFalse: [MIDIPort addSharedPool: MIDICommands].
MIDIPort compileAll
! !
-------------- next part --------------
/***************************************************************
* Squeak/Siren Interface to the Opcode MIDI System (OMS)
* Stephen T. Pope -- stp at create.ucsb.edu
*
* Version History: see sqMIDI.h
*
* N.B.: This driver requires OMS version 2.5.3 or greater.
* See http://www.opcode.com to get the latest version of OMS for free.
*
* Because of the (Mac/DOS) portability of OMS, this code should be
* (more or less) portable to the DOS PC as well (please let me know).
*/
/***************************************************************
* Includes & Macros
*/
//#include "sq.h" // General Squeak definitions, includes sqMIDI.h
// MIDI Includes -- STP -- these used to be in sq.h
#include "OMS.h" // OMS definitions and structs
#include <MIDI.h> // Apple MIDI Libraries
#include "sqMIDI.h" // Squeak MIDI Structs and Prims
#include "sqVirtualMachine.h" // The virtual machine proxy definition
#include "Strings.h"
#define sqSignature 'SQEK' // Creator ID.
#define InputPortID 'in ' // I/O port IDs.
#define OutputPortID 'out '
//#define DEBUG_MODE // Enable/Disable printfs (see macros below)
// Debugging macros
#ifdef DEBUG_MODE
#define dprint1(str) printf(str)
#define dprint2(str, val) printf(str, val)
#define dprint3(str, v1, v2) printf(str, v1, v2)
Boolean debug_mode = true;
#else
#define dprint1(str)
#define dprint2(str, val)
#define dprint3(str, v1, v2)
Boolean debug_mode = false;
#endif DEBUG_MODE
extern struct VirtualMachine *interpreterProxy;
// A few macros for error checking
#define S_FAIL { interpreterProxy->success(false); return(-1); }
#define S_ERR { interpreterProxy->success(false); return((int) err); }
#define S_CHECK if(err != omsNoErr) { return(0 - (int) err); }
#define S_CHECK_FAIL if(err != omsNoErr) { interpreterProxy->success(false); \
return(0 - (int) err); }
#define S_CHECK_SIGNIN if(!gSignedInToMIDI) { return(-1024); }
// memory access macros from interp.c
#define longAtput(i, val) (*((int *) (i)) = val)
#define instVarAtPutInt(obj, slot, val) \
longAtput(((char *)obj + (slot << 2)), (((int)val << 1) | 1))
/***************************************************************
* Global state variabless
*/
Boolean gSignedInToMIDI; // are we signed into MIDI driver?
Boolean gEchoEnabled; // toggle to echo event input
Boolean gInputEnabled; // toggle to enable event input
Boolean gCacheControllers; // toggle to cache controller values
/***************************************************************
* Controller value caches -- This is the minimum complement
* A "larger" driver would cache 128*16 controllers and 128*16 key pressures
*/
unsigned char sqControllers[128]; // Controller value table
unsigned char sqKeyPressures[128]; // Polyphonic key pressure table
unsigned char sqChanPressures[16]; // Channel pressure value table
int sqPitchBend[16]; // The value of the pitch wheel
/***************************************************************
* OMS Driver Internals
*/
short gInPortRef; // refNum of the OMS input port
short gOutPortRef; // refNum of the OMS output port
short sqCallback; // semaphore for read callback
short gCompatMode; // OMS compatibility mode
#define NumPorts 8 // Max number of I/O ports
#define MaxNameLen 64 // Max length of device names
unsigned short gOutNodeRefs[NumPorts] = {0, 0, 0, 0, 0, 0, 0, 0};
sqMIDIioPort gNodeNames[NumPorts]; // The IO port table
#define MaxInInQ 64 // The size of the input Q
sqMIDIEvent sqMIDIInQ[MaxInInQ]; // the input event Q (an array)
int itemsInInQ; // Index to write in the input Q
int itemsInInQ2; // Pointer to input to read (<= iIIQ)
OMSMIDIPacket thePacket; // Small (4-byte) MIDI packet
OMSMIDIPacket255 theBigPacket; // Large packet for SysEx messages
/***************************************************************
* Function Prototypes for I/O call-back utilities
*/
#define USESROUTINEDESCRIPTORS
pascal void sqMIDIAppHook(OMSAppHookMsg *pkt, long myRefCon);
pascal void sqMIDIReadHook(OMSPacket *pkt, long myRefCon);
/***************************************************************
***************************************************************
* sqOpenMIDI -- Sign on to OMS, register callbacks, create ports
* and connections, etc.
*/
int sqOpenMIDI(int callbackSemaphore, int inputMRSocket) {
OSErr err;
OMSAppHookUPP appHook; // pointer to call-back function
OMSReadHookUPP readHook; // pointer to call-back function
OMSAPI (long) versionID; // OMS driver version
// OMSAPI(OMSIDListH) sqNodes; // list of I/O node IDs
OMSConnectionParams sqConn; // connection parameter structure JH - uncommented this line
OMSNodeInfoListH nodeInfoList;
int i, j; // JH added tmp var j
long LMGetCurrentA5(void);
int interfaces;
char *nname;
Boolean found = false;
interpreterProxy->success(true);
//// It's not an error if we're already signed in.
if (gSignedInToMIDI)
return (0);
dprint3("\n\tMIDI open -- sem = %d sock = %d\n", callbackSemaphore, inputMRSocket);
sqCallback = 0;
//// Make sure that OMS V2 is installed.
versionID = OMSVersion();
if (versionID == 0) {
interpreterProxy->success(false);
return (-128);
}
if ((versionID & 0xff000000) != 0x02000000) {
interpreterProxy->success(false);
return (-129);
}
//// Set input event pointers
itemsInInQ = 0;
itemsInInQ2 = 0;
//// Clear controller value tables
for (i=0; i<128; i++) {
sqControllers[i] = 64;
sqKeyPressures[i] = 0;
}
for (i=0; i<16; i++) {
sqChanPressures[i] = 0;
sqPitchBend[i] = 0;
}
//// Set up call-back routine pointers.
appHook = NewOMSAppHook(sqMIDIAppHook);
readHook = NewOMSReadHook(sqMIDIReadHook);
//// Sign in to OMS.
err = OMSSignIn(sqSignature, (long)LMGetCurrentA5(),
(unsigned char *) "Squeak/Siren",
appHook, &gCompatMode);
S_CHECK_FAIL
gSignedInToMIDI = true;
//// Add an input port.
err = OMSAddPort(sqSignature, InputPortID, omsPortTypeInput,
readHook, (long)LMGetCurrentA5(), &gInPortRef);
if (err) goto errexit;
//// Add an output port.
err = OMSAddPort(sqSignature, OutputPortID, omsPortTypeOutput,
NULL, 0L, &gOutPortRef); // no read hook for output
if (err) goto errexit;
//// Set up OMS to play to all output ports.
nodeInfoList = OMSGetNodeInfo(omsIncludeOutputs + omsIncludeReal + omsIncludeVirtual);
if (nodeInfoList != NULL) {
interfaces = (*nodeInfoList)->numNodes;
for (i=0; i<(*nodeInfoList)->numNodes; i++) {
nname = (char *)(*nodeInfoList)->info[i].name;
dprint2("\tOpen interface %s\n", nname);
strcpy(gNodeNames[i].name, nname);
gNodeNames[i].direction = 2;
gOutNodeRefs[i] = (*nodeInfoList)->info[i].ioRefNum;
}
OMSDisposeHandle(nodeInfoList);
} else
goto errexit;
// JH - uncommented following block of code
//// Read from all input ports.
sqConn.appRefCon = 0;
nodeInfoList = OMSGetNodeInfo(omsIncludeInputs + omsIncludeReal + omsIncludeVirtual);
if (nodeInfoList != NULL) {
for (i=0; i<(*nodeInfoList)->numNodes; i++) {
nname = (char *)(*nodeInfoList)->info[i].name;
// JH - Actually only read from controllers
if ((*nodeInfoList)->info[i].flags & kAnyIsController) {
sqConn.nodeUniqueID = (*nodeInfoList)->info[i].uniqueID;
err = OMSOpenConnections(sqSignature, InputPortID, 1, &sqConn, false);
if (err) goto errexit;
for (j=0; j < interfaces; j++)
if (strcmp(nname, gNodeNames[j].name) == 0) {
gNodeNames[j].direction += 1;
found = true;
}
if (!found) {
strcpy(gNodeNames[interfaces].name, nname);
gNodeNames[interfaces].direction = 1;
interfaces ++;
}
dprint2("\tConnect input %s\n", nname);
}
}
OMSDisposeHandle(nodeInfoList);
}
//// Success
gInputEnabled = false; // off by default
gCacheControllers = false; // off by default
gEchoEnabled = false; // off by default
sqCallback = callbackSemaphore;
interpreterProxy->success(true);
return(interfaces); // Answer the number of interfaces found.
errexit:
OMSSignOut(sqSignature);
gSignedInToMIDI = false;
interpreterProxy->success(false);
return ((int) err);
}
/***************************************************************
* sqCloseMIDI -- Close up shop and sign out from OMS
*/
int sqCloseMIDI() {
interpreterProxy->success(true);
S_CHECK_SIGNIN
sqCallback = 0;
// Perhaps I should do allNotesOff here...
OMSSignOut(sqSignature);
gSignedInToMIDI = false;
gInputEnabled = false;
gCacheControllers = false;
gEchoEnabled = false;
return(0);
}
/***************************************************************
* sqMIDIPlaySize -- Send a MIDI message out the output ports
*/
int sqMIDIPlaySizeOn (int message, int length, int interface) {
int i;
unsigned char *ptr = (unsigned char *) message;
// It's an error if we're not signed in
S_CHECK_SIGNIN
// Play it now (i.e., no output buffer)
if (length < 4) { // Handle a "normal" packet of 3 bytes
thePacket.flags = 0;
thePacket.len = length;
for (i=0; i<length; i++) // copy output data into packet
thePacket.data[i] = *(ptr + i);
if (interface == 0) { // Interface 0 means play out all outputs
for (i=0; i<NumPorts; i++)
if (gOutNodeRefs[i] != 0)
OMSWritePacket2(&thePacket, gOutNodeRefs[i], gOutPortRef);
} // JH - added curly braces - CW seemed to disambiguate the if-if-else other than as intended(?)
else
OMSWritePacket2(&thePacket, gOutNodeRefs[interface - 1], gOutPortRef);
} else { // Handle a big packet
theBigPacket.flags = 0;
theBigPacket.len = length;
for (i=0; i<length; i++) // copy output data into packet
theBigPacket.data[i] = *(ptr + i);
if (interface == 0) { // Interface 0 means play out all outputs
for (i=0; i<NumPorts; i++)
if (gOutNodeRefs[i] != 0)
OMSWritePacket2((struct OMSMIDIPacket *)&theBigPacket, gOutNodeRefs[i], gOutPortRef);
// OMSWritePacket255(&theBigPacket, gOutNodeRefs[i], gOutPortRef);
} // JH - added curly braces - CW seemed to disambiguate the if-if-else other than as intended(?)
else
OMSWritePacket2((struct OMSMIDIPacket *)&theBigPacket, gOutNodeRefs[interface - 1], gOutPortRef);
// OMSWritePacket255(&theBigPacket, gOutNodeRefs[interface - 1], gOutPortRef);
}
interpreterProxy->success(true);
return(length);
}
unsigned char lastCommand;
int lastLength;
/***************************************************************
* sqMIDIReadHook -- Interrupt routine called on in-coming MIDI messages
*/
pascal void sqMIDIReadHook(OMSPacket *pkt, long myRefCon) {
sqMIDIEvent *inPtr;
unsigned char *idata, *odata;
int len, i, cmd, chan, value;
long olda5 = SetA5(myRefCon);
len = pkt->len;
if (len == 0) // return if empty packet
goto errexit;
if (itemsInInQ == MaxInInQ) // return if input Q full
goto errexit;
// echo input to the output
if (gEchoEnabled)
for (i=0; i<NumPorts; i++)
if (gOutNodeRefs[i] != 0)
OMSWritePacket(pkt, gOutNodeRefs[i], gOutPortRef);
cmd = (pkt->data[0]) & 0xF0; // Get "command" from 1st byte of packet
if (gCacheControllers) { // Cache controller values in the driver
if (cmd == ControlCmd) { // Read a control command
chan = pkt->data[1];
value = pkt->data[2];
sqControllers[chan] = value;
goto errexit;
} else if (cmd == PchWheelCmd) { // Read a pitch wheel change
chan = (pkt->data[0]) & 0x0F;
i = pkt->data[1];
value = pkt->data[2];
sqPitchBend[chan] = (value << 7) + i;
goto errexit;
} else if (cmd == PolyPressCmd) { // Read polyphonic key pressure
chan = pkt->data[1];
value = pkt->data[2];
sqKeyPressures[chan] = value;
goto errexit;
} else if (cmd == ChanPressCmd) { // Read channel key pressure
chan = (pkt->data[0]) & 0x0F;
value = pkt->data[1];
sqKeyPressures[chan] = value;
goto errexit;
}
}
if (!gInputEnabled)
goto errexit;
// Get the input Q record to use
inPtr = &sqMIDIInQ[itemsInInQ++];
// Catch OMS packet length bug
if ((cmd == PgmChngCmd) || (cmd == ChanPressCmd))
len = 2;
else if (cmd != SysExCmd)
len = 3;
inPtr->len = len;
// Copy the packet to the input Q
inPtr->flags = (int)pkt->flags;
inPtr->timeStamp = clock(); // = (int)pkt->beatTimeStamp; for OMS 2 input
idata = &(inPtr->data[0]);
odata = &(pkt->data[0]);
if (len <= PKT_LEN) // Copy the data bytes
for (i = 0; i < len; i++)
*idata++ = *odata++;
if (sqCallback != 0) {
// Signal the read semaphore
i = interpreterProxy->signalSemaphoreWithIndex(sqCallback);}
errexit:
SetA5(olda5);
}
/***************************************************************
* sqReadMIDIPacket -- Sent in response to the input semaphore
* This is to up-load MIDI messages into the arguments (a MIDIPacket,
* its max length, and its data ByteArray).
*/
int sqReadMIDIPacket(int ipacket, int maxSize, int idata) {
sqMIDIEvent *outPtr;
unsigned char *cdata;
int len, i;
unsigned char *pdata = (unsigned char *)idata;
// The packet object is defined as:
// Object subclass: #MIDIPacket
// instanceVariableNames: 'length time flags data '
interpreterProxy->success(true);
if (itemsInInQ == 0)
return (0);
if (sqCallback == 0)
return (-1);
// Set up pointers
outPtr = &sqMIDIInQ[itemsInInQ2++];
dprint2("Reading %d bytes -- ", outPtr->len);
if(debug_mode) printf("%x %x %x\n", outPtr->data[0], outPtr->data[1], outPtr->data[2]);
len = outPtr->len; // copy the response fields
if (len > maxSize) // packet longer than normal--answer length
return (len);
// copy the driver data into the packet object
instVarAtPutInt(ipacket, 1, len); // length, time, flags
instVarAtPutInt(ipacket, 2, (int)(outPtr->timeStamp));
instVarAtPutInt(ipacket, 3, (int)(outPtr->flags));
cdata = &(outPtr->data[0]); // copy MIDI message bytes into the packet
for (i=0; i<len; i++)
*pdata++ = *cdata++;
if (itemsInInQ2 == itemsInInQ) // clear counters if Q is empty
itemsInInQ2 = itemsInInQ = 0;
return (len); // Answer len
}
/***************************************************************
* sqReadMIDIControl -- Answer one or more controller values from the cache
*/
int sqReadMIDIControl(int fromC, int toC, int intoArray) {
int i;
unsigned char *pdata = (unsigned char *)intoArray;
// check controller range
if ((fromC < 0) || (toC > 287) || (fromC > toC)) {
interpreterProxy->success(false);
return (-1);
}
pdata += 4; // Skip object header
for (i = fromC; i <= toC; i++) { // Loop to read controllers
if (i < 128) // 0-127 = controllers
*pdata++ = ((int)sqControllers[i]);
else if (i < 256) // 128-255 = key pressures
*pdata++ = ((int)sqKeyPressures[i - 128]);
else if (i < 272) // 256-271 = channel pressures
*pdata++ = ((int)sqChanPressures[i - 256]);
else // 272-287 = pitch bend (16-bit!)
// Write single int into "Array"
longAtput(intoArray, (sqPitchBend[i - 272]));
}
interpreterProxy->success(true);
return(toC - fromC + 1);
}
/***************************************************************
* getDeviceName -- write the name of the selected device into the 2nd arg
* Answer the ports directionality.
*/
int sqGetMIDIDeviceName(int index, int name) {
if ((index < 0) || (index > NumPorts))
return(-1);
strcpy((char *)name, gNodeNames[index].name);
return(gNodeNames[index].direction);
}
/***************************************************************
* sqMIDIioctl -- General driver queries and status
*/
int sqMIDIioctl(int which, int onOff) {
int answer = 0;
interpreterProxy->success(true);
switch (which) {
case (sqMIDIInstalled): // Is a MIDI driver installed?
answer = (int)OMSVersion(); // Can I communicate with the "real" driver?
if (answer == 0)
answer = false;
answer = true;
break;
case (sqMIDIVersion): // What driver version is this?
answer = (int)OMSVersion();
break;
case (sqMIDIHasBuffer): // Is there a time-stamped output buffer?
answer = false; // Answer false in the "default" driver
break;
case (sqMIDIHasDurs): // Is there a 1-call note command?
answer = false; // Answer false in the "default" driver
break;
case (sqMIDIHasClock): // Does the driver have its own clock?
answer = false; // Answer false in the "default" driver
break;
case (sqMIDIUseSemaphore): // Should the driver signal a semaphore on input?
switch (onOff) {
case sqMIDIQuery: // Answer value of flag
answer = gInputEnabled;
break;
case sqMIDITurnOn:
gInputEnabled = true; // Set flag
break;
case sqMIDITurnOff:
gInputEnabled = false; // Clear flag
break;
}
break;
case (sqMIDIEcho): // Should we echo in-coming events in the driver?
switch (onOff) {
case sqMIDIQuery:
answer = gEchoEnabled; // Answer value of flag
break;
case sqMIDITurnOn:
gEchoEnabled = true; // Set flag
break;
case sqMIDITurnOff:
gEchoEnabled = false; // Clear flag
break;
}
break;
case (sqMIDIControllerCache): // Should we cache controller values for polling?
switch (onOff) {
case sqMIDIQuery:
answer = gCacheControllers; // Answer value of flag
break;
case sqMIDITurnOn:
gCacheControllers = true; // Set flag
break;
case sqMIDITurnOff:
gCacheControllers = false; // Clear flag
break;
}
break;
case (sqMIDIEventsAvailable): // How many events are in the input Q?
answer = itemsInInQ - itemsInInQ2;
break;
case (sqMIDIFlushDriver): // Special flag to flush the driver's I/O buffers,
itemsInInQ = itemsInInQ2 = 0;
OMSAllNotesOff(); // and send all-notes-off
break;
default: // Default = FAIL
interpreterProxy->success(false);
}
return(answer);
}
/***************************************************************
* sqMIDIAppHook -- Sent on OMS configuration changes; NO-OPs at present
*/
pascal void sqMIDIAppHook(OMSAppHookMsg *pkt, long myRefCon) {
long olda5 = SetA5(myRefCon);
switch (pkt->msgType) {
// What to do when compatibility mode changes?
case omsMsgModeChanged:
// What to do?
break;
// What to do if a receiver is deleted?
case omsMsgDestDeleted:
// What to do?
break;
// What to do if the studio layout changes?
case omsMsgNodesChanged:
// What to do?
break;
}
SetA5(olda5);
}
/***************************************************************
* E N D
*/
More information about the Squeak-dev
mailing list
|