2 SuperCollider real time audio synthesis system
3 Copyright (c) 2002 James McCartney. All rights reserved.
4 http://www.audiosynth.com
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 changes by jan trutzschler v. f. 9/9/2002
23 the midiReadProc calls doAction in the class MIDIIn.
24 with the arguments: inUid, status, chan, val1, val2
25 added prDisposeMIDIClient
27 19/9 call different actions,disconnect midiInPort, midiout: sendmidi
28 04/feb/03 prListMIDIEndpoints modification by Ron Kuivila added jt.
35 #include "VMGlobals.h"
36 #include "PyrSymbolTable.h"
37 #include "PyrInterpreter.h"
38 #include "PyrKernel.h"
40 #include "PyrPrimitive.h"
41 #include "PyrObjectProto.h"
42 #include "PyrPrimitiveProto.h"
43 #include "PyrKernelProto.h"
44 #include "SC_InlineUnaryOp.h"
45 #include "SC_InlineBinaryOp.h"
50 // symbols to call back into lang
51 PyrSymbol
* s_domidiaction
;
52 PyrSymbol
* s_midiNoteOnAction
;
53 PyrSymbol
* s_midiNoteOffAction
;
54 PyrSymbol
* s_midiTouchAction
;
55 PyrSymbol
* s_midiControlAction
;
56 PyrSymbol
* s_midiPolyTouchAction
;
57 PyrSymbol
* s_midiProgramAction
;
58 PyrSymbol
* s_midiBendAction
;
59 PyrSymbol
* s_midiSysexAction
;
60 PyrSymbol
* s_midiInvalidSysexAction
;
61 PyrSymbol
* s_midiSysrtAction
;
62 PyrSymbol
* s_midiSMPTEAction
;
64 PyrSymbol
* s_numMIDIDev
;
65 PyrSymbol
* s_midiclient
;
67 const int kMaxMidiPorts
= 16;
68 int gNumMIDIInPorts
= 0, gNumMIDIOutPorts
= 0;
69 bool gMIDIInitialized
= false;
71 PmStream
* gMIDIInStreams
[kMaxMidiPorts
];
72 PmStream
* gMIDIOutStreams
[kMaxMidiPorts
];
74 std::map
<int,int> gMidiInputIndexToPmDevIndex
;
75 std::map
<int,int> gMidiOutputIndexToPmDevIndex
;
76 std::map
<int,int> gMidiPmDevIndexToInputIndex
;
77 std::map
<int,int> gMidiPmDevIndexToOutputIndex
;
78 pthread_mutex_t gPmStreamMutex
;
80 struct ScopeMutexLock
{
81 pthread_mutex_t
* mutex_
;
82 ScopeMutexLock(pthread_mutex_t
* mutex
) : mutex_(mutex
) {
83 pthread_mutex_lock(mutex_
); }
85 pthread_mutex_unlock(mutex_
); }
88 /* if INPUT_BUFFER_SIZE is 0, PortMidi uses a default value */
89 #define PMSTREAM_INPUT_BUFFER_SIZE 0
90 #define PMSTREAM_OUTPUT_BUFFER_SIZE 100
91 #define PMSTREAM_DRIVER_INFO NULL
92 #define PMSTREAM_TIME_PROC NULL
93 #define PMSTREAM_TIME_INFO NULL
95 /* use zero latency because we want output to be immediate */
98 extern bool compiledOK
;
100 inline void TPmErr(PmError err
) {
101 if( err
!= pmNoError
)
105 /* timer "interrupt" for processing midi data */
106 static void PMProcessMidi(PtTimestamp timestamp
, void *userData
)
108 ScopeMutexLock
mulo(&gPmStreamMutex
);
110 PmEvent buffer
; /* just one message at a time */
112 for( int i
= 0 ; i
< gNumMIDIInPorts
; ++i
)
114 int pmdid
= gMidiInputIndexToPmDevIndex
[i
];
115 PmStream
* midi_in
= gMIDIInStreams
[i
];
118 while(result
= Pm_Poll(midi_in
))
120 long Tstatus
, data1
, data2
;
121 if (Pm_Read(midi_in
, &buffer
, 1) == pmBufferOverflow
)
123 // unless there was overflow, we should have a message now
125 Tstatus
= Pm_MessageStatus(buffer
.message
);
126 data1
= Pm_MessageData1(buffer
.message
);
127 data2
= Pm_MessageData2(buffer
.message
);
129 // +---------------------------------------------+
130 // | Lock the interp. mutex and dispatch message |
131 // +---------------------------------------------+
132 pthread_mutex_lock (&gLangMutex
); // it is needed -jamesmcc
135 VMGlobals
*g
= gMainVMGlobals
;
136 uint8 status
= static_cast<uint8
>(Tstatus
& 0xF0);
137 uint8 chan
= static_cast<uint8
>(Tstatus
& 0x0F);
139 g
->canCallOS
= false; // cannot call the OS
141 ++g
->sp
; SetObject(g
->sp
, s_midiin
->u
.classobj
); // Set the class MIDIIn
144 ++g
->sp
; SetInt(g
->sp
, pmdid
); //src
145 // ++g->sp; SetInt(g->sp, status); //status
146 ++g
->sp
; SetInt(g
->sp
, chan
); //chan
148 //if(status & 0x80) // set the running status for voice messages
149 //gRunningStatus = ((status >> 4) == 0xF) ? 0 : pkt->data[i]; // keep also additional info
152 case 0x80 : //noteOff
153 ++g
->sp
; SetInt(g
->sp
, data1
);
154 ++g
->sp
; SetInt(g
->sp
, data2
);
155 runInterpreter(g
, s_midiNoteOffAction
, 5);
158 ++g
->sp
; SetInt(g
->sp
, data1
);
159 ++g
->sp
; SetInt(g
->sp
, data2
);
160 runInterpreter(g
, data2
? s_midiNoteOnAction
: s_midiNoteOffAction
, 5);
162 case 0xA0 : //polytouch
163 ++g
->sp
; SetInt(g
->sp
, data1
);
164 ++g
->sp
; SetInt(g
->sp
, data2
);
165 runInterpreter(g
, s_midiPolyTouchAction
, 5);
167 case 0xB0 : //control
168 ++g
->sp
; SetInt(g
->sp
, data1
);
169 ++g
->sp
; SetInt(g
->sp
, data2
);
170 runInterpreter(g
, s_midiControlAction
, 5);
172 case 0xC0 : //program
173 ++g
->sp
; SetInt(g
->sp
, data1
);
174 runInterpreter(g
, s_midiProgramAction
, 4);
177 ++g
->sp
; SetInt(g
->sp
, data1
);
178 runInterpreter(g
, s_midiTouchAction
, 4);
181 ++g
->sp
; SetInt(g
->sp
, (data2
<< 7) | data1
);
182 runInterpreter(g
, s_midiBendAction
, 4);
185 // only the first Pm_Event will carry the 0xF0 byte
186 // sysex message will be terminated by the EOX status byte 0xF7
187 midiProcessSystemPacket(data1, data2, chan);
189 default : // data byte => continuing sysex message
190 if(gRunningStatus && !gSysexFlag) { // handle running status
191 status = gRunningStatus & 0xF0; // accept running status only inside a packet beginning
192 chan = gRunningStatus & 0x0F; // with a valid status byte
195 //goto L; // parse again with running status set // mv - get next byte
198 i += midiProcessSystemPacket(pkt, chan); // process sysex packet
201 g
->canCallOS
= false;
203 pthread_mutex_unlock (&gLangMutex
);
206 } // for loop until numMIDIInPorts
210 -------------------------------------------------------------
218 TPmErr(Pm_Initialize());
219 int nbDev
= Pm_CountDevices();
224 for( int i
= 0; i
< nbDev
; ++i
) {
225 const PmDeviceInfo
* devInfo
= Pm_GetDeviceInfo(i
);
229 gMidiInputIndexToPmDevIndex
[inIndex
++] = i
;
230 gMidiPmDevIndexToInputIndex
[i
] = inIndex
;
232 if( devInfo
->output
)
235 gMidiOutputIndexToPmDevIndex
[outIndex
++] = i
;
236 gMidiPmDevIndexToOutputIndex
[i
] = outIndex
;
240 for( int i
= 0; i
< gNumMIDIOutPorts
; i
++) {
241 pmdid
= gMidiOutputIndexToPmDevIndex
[i
];
242 Pm_OpenOutput(&gMIDIOutStreams
[i
], pmdid
, NULL
, 512, NULL
, NULL
, 0);
245 /* will call our function, PMProcessMidi() every millisecond */
246 Pt_Start(1, &PMProcessMidi
, 0); /* start a timer with millisecond accuracy */
252 gMIDIInitialized
= true;
256 -------------------------------------------------------------
260 ScopeMutexLock
mulo(&gPmStreamMutex
);
262 if(gMIDIInitialized
) {
263 for (int i
=0; i
<gNumMIDIOutPorts
; ++i
) {
264 Pm_Abort(gMIDIOutStreams
[i
]);
265 Pm_Close(gMIDIOutStreams
[i
]);
267 for (int i
=0; i
<gNumMIDIInPorts
; ++i
) {
268 Pm_Abort(gMIDIInStreams
[i
]);
269 Pm_Close(gMIDIInStreams
[i
]);
272 gNumMIDIOutPorts
= 0;
276 // set the stream pointers to NULL
277 memset(gMIDIInStreams
,0,kMaxMidiPorts
*sizeof(PmStream
*));
278 memset(gMIDIOutStreams
,0,kMaxMidiPorts
*sizeof(PmStream
*));
280 // delete the objects that map in/out indices to Pm dev indices
281 gMidiInputIndexToPmDevIndex
.clear();
282 gMidiOutputIndexToPmDevIndex
.clear();
283 gMidiPmDevIndexToInputIndex
.clear();
284 gMidiPmDevIndexToOutputIndex
.clear();
286 gMIDIInitialized
= false;
289 -------------------------------------------------------------
291 void midiListEndpoints()
295 -------------------------------------------------------------
297 int prListMIDIEndpoints(struct VMGlobals
*g
, int numArgsPushed
)
300 int numSrc
= gNumMIDIInPorts
;
301 int numDst
= gNumMIDIOutPorts
;
303 PyrObject
* idarray
= newPyrArray(g
->gc
, 6 * sizeof(PyrObject
), 0 , true);
304 SetObject(a
, idarray
);
307 PyrObject
* idarraySo
= newPyrArray(g
->gc
, numSrc
* sizeof(__int32
), 0 , true);
308 SetObject(idarray
->slots
+idarray
->size
++, idarraySo
);
309 g
->gc
->GCWrite(idarray
, idarraySo
);
312 PyrObject
* devarraySo
= newPyrArray(g
->gc
, numSrc
* sizeof(PyrObject
), 0 , true);
313 SetObject(idarray
->slots
+idarray
->size
++, devarraySo
);
314 g
->gc
->GCWrite(idarray
, devarraySo
);
317 PyrObject
* namearraySo
= newPyrArray(g
->gc
, numSrc
* sizeof(PyrObject
), 0 , true);
318 SetObject(idarray
->slots
+idarray
->size
++, namearraySo
);
319 g
->gc
->GCWrite(idarray
, namearraySo
);
322 PyrObject
* idarrayDe
= newPyrArray(g
->gc
, numDst
* sizeof(__int32
), 0 , true);
323 SetObject(idarray
->slots
+idarray
->size
++, idarrayDe
);
324 g
->gc
->GCWrite(idarray
, idarrayDe
);
327 PyrObject
* namearrayDe
= newPyrArray(g
->gc
, numDst
* sizeof(PyrObject
), 0 , true);
328 SetObject(idarray
->slots
+idarray
->size
++, namearrayDe
);
329 g
->gc
->GCWrite(idarray
, namearrayDe
);
332 PyrObject
* devarrayDe
= newPyrArray(g
->gc
, numDst
* sizeof(PyrObject
), 0 , true);
333 SetObject(idarray
->slots
+idarray
->size
++, devarrayDe
);
334 g
->gc
->GCWrite(idarray
, devarrayDe
);
336 for (int i
=0; i
<numSrc
; ++i
) {
337 const PmDeviceInfo
* devInfo
= Pm_GetDeviceInfo(gMidiInputIndexToPmDevIndex
[i
]);
339 char cendname
[1024], cdevname
[1024];
341 // currently, copy both name strings in endpoint name and dev name
342 strncpy(cendname
,devInfo
->name
,1023);
344 strncpy(cdevname
,devInfo
->name
,1023);
347 PyrString
*string
= newPyrString(g
->gc
, cendname
, 0, true);
348 SetObject(namearraySo
->slots
+i
, string
);
350 g
->gc
->GCWrite(namearraySo
, (PyrObject
*)string
);
352 PyrString
*devstring
= newPyrString(g
->gc
, cdevname
, 0, true);
353 SetObject(devarraySo
->slots
+i
, devstring
);
355 g
->gc
->GCWrite(devarraySo
, (PyrObject
*)devstring
);
357 SetInt(idarraySo
->slots
+i
, i
);
361 // post("numDst %d\n", numDst);
362 for (int i
=0; i
<numDst
; ++i
) {
363 const PmDeviceInfo
* devInfo
= Pm_GetDeviceInfo(gMidiOutputIndexToPmDevIndex
[i
]);
364 char cendname
[1024], cdevname
[1024];
366 // currently, copy both name strings in endpoint name and dev name
367 strncpy(cendname
,devInfo
->name
,1023);
369 strncpy(cdevname
,devInfo
->name
,1023);
372 PyrString
*string
= newPyrString(g
->gc
, cendname
, 0, true);
373 SetObject(namearrayDe
->slots
+namearrayDe
->size
++, string
);
374 g
->gc
->GCWrite(namearrayDe
, (PyrObject
*)string
);
376 PyrString
*devstring
= newPyrString(g
->gc
, cdevname
, 0, true);
378 SetObject(devarrayDe
->slots
+devarrayDe
->size
++, devstring
);
379 g
->gc
->GCWrite(devarrayDe
, (PyrObject
*)devstring
);
381 SetInt(idarrayDe
->slots
+idarrayDe
->size
++, i
);
388 -------------------------------------------------------------
391 int prConnectMIDIIn(struct VMGlobals
*g
, int numArgsPushed
)
393 ScopeMutexLock
mulo(&gPmStreamMutex
);
395 //PyrSlot *a = g->sp - 2;
396 PyrSlot
*b
= g
->sp
- 1;
399 int err
, inputIndex
, uid
;
400 err
= slotIntVal(b
, &inputIndex
);
401 if (err
) return errWrongType
;
402 if (inputIndex
< 0 || inputIndex
>= gNumMIDIInPorts
)
403 return errIndexOutOfRange
;
405 err
= slotIntVal(c
, &uid
);
409 PmStream
* inStream
= NULL
;
410 int pmdid
= gMidiInputIndexToPmDevIndex
[uid
];
412 PmError pmerr
= Pm_OpenInput( &inStream
, pmdid
,
413 PMSTREAM_DRIVER_INFO
,
414 PMSTREAM_INPUT_BUFFER_SIZE
,
416 PMSTREAM_TIME_INFO
);
418 if(pmerr
!= pmNoError
)
421 gMIDIInStreams
[uid
] = inStream
;
425 -------------------------------------------------------------
427 int prDisconnectMIDIIn(struct VMGlobals
*g
, int numArgsPushed
)
429 ScopeMutexLock
mulo(&gPmStreamMutex
);
431 PyrSlot
*b
= g
->sp
- 1;
434 int err
, inputIndex
, uid
;
435 err
= slotIntVal(b
, &inputIndex
);
437 if (inputIndex
< 0 || inputIndex
>= gNumMIDIInPorts
) return errIndexOutOfRange
;
438 err
= slotIntVal(c
, &uid
);
442 PmError pmerr
= Pm_Close(gMIDIInStreams
[uid
]);
444 if(pmerr
!= pmNoError
)
447 gMIDIInStreams
[uid
] = NULL
;
452 -------------------------------------------------------------
454 int prInitMIDI(struct VMGlobals
*g
, int numArgsPushed
)
456 //PyrSlot *a = g->sp - 2;
457 PyrSlot
*b
= g
->sp
- 1;
460 int err
, numIn
, numOut
;
461 err
= slotIntVal(b
, &numIn
);
462 if (err
) return errWrongType
;
464 err
= slotIntVal(c
, &numOut
);
471 int prDisposeMIDIClient(VMGlobals
*g
, int numArgsPushed
)
477 int prRestartMIDI(VMGlobals
*g
, int numArgsPushed
)
484 void freeSysex(MIDISysexSendRequest* pk)
491 int prSendSysex(VMGlobals
*g
, int numArgsPushed
)
495 PyrInt8Array* packet = g->sp->uob;
497 Byte *data = (Byte *)malloc(size);
499 memcpy(data,packet->b, size);
501 PyrSlot *u = g->sp - 1;
502 err = slotIntVal(u, &uid);
505 MIDIEndpointRef dest;
506 MIDIObjectType mtype;
507 MIDIObjectFindByUniqueID(uid, (MIDIObjectRef*)&dest, &mtype);
508 if (mtype != kMIDIObjectType_Destination) return errFailed;
509 if (!dest) return errFailed;
511 sendsysex(dest, size, data);
517 -------------------------------------------------------------
519 int prSendMIDIOut(struct VMGlobals
*g
, int numArgsPushed
)
521 ScopeMutexLock
mulo(&gPmStreamMutex
);
522 //port, uid, len, hiStatus, loStatus, a, b, latency
523 //PyrSlot *m = g->sp - 8;
524 PyrSlot
*p
= g
->sp
- 7;
526 PyrSlot
*u
= g
->sp
- 6;
527 PyrSlot
*l
= g
->sp
- 5;
529 PyrSlot
*his
= g
->sp
- 4;
530 PyrSlot
*los
= g
->sp
- 3;
532 PyrSlot
*a
= g
->sp
- 2;
533 PyrSlot
*b
= g
->sp
- 1;
534 PyrSlot
*plat
= g
->sp
;
536 int err
, outputIndex
, uid
, length
, hiStatus
, loStatus
, aval
, bval
;
538 err
= slotIntVal(p
, &outputIndex
);
540 if (outputIndex
< 0 || outputIndex
>= gNumMIDIOutPorts
) return errIndexOutOfRange
;
542 err
= slotIntVal(u
, &uid
);
544 err
= slotIntVal(l
, &length
);
546 err
= slotIntVal(his
, &hiStatus
);
548 err
= slotIntVal(los
, &loStatus
);
550 err
= slotIntVal(a
, &aval
);
552 err
= slotIntVal(b
, &bval
);
554 err
= slotFloatVal(plat
, &late
);
557 Pm_WriteShort(gMIDIOutStreams
[uid
], 0,
558 Pm_Message((hiStatus
& 0xF0) | (loStatus
& 0x0F) , aval
, bval
));
563 // not needed in PortMIDI:
569 int prInitMIDIClient(struct VMGlobals
*g
, int numArgsPushed
)
571 return initMIDIClient();
574 void initMIDIPrimitives()
578 base
= nextPrimitiveIndex();
581 s_midiin
= getsym("MIDIIn");
582 s_domidiaction
= getsym("doAction");
583 s_midiNoteOnAction
= getsym("doNoteOnAction");
584 s_midiNoteOffAction
= getsym("doNoteOffAction");
585 s_midiTouchAction
= getsym("doTouchAction");
586 s_midiControlAction
= getsym("doControlAction");
587 s_midiPolyTouchAction
= getsym("doPolyTouchAction");
588 s_midiProgramAction
= getsym("doProgramAction");
589 s_midiBendAction
= getsym("doBendAction");
590 s_midiSysexAction
= getsym("doSysexAction");
591 s_midiInvalidSysexAction
= getsym("doInvalidSysexAction"); // client can handle incorrect case
592 s_midiSysrtAction
= getsym("doSysrtAction");
593 s_midiSMPTEAction
= getsym("doSMPTEaction");
594 s_numMIDIDev
= getsym("prSetNumberOfDevices");
595 s_midiclient
= getsym("MIDIClient");
596 definePrimitive(base
, index
++, "_ListMIDIEndpoints", prListMIDIEndpoints
, 1, 0);
597 definePrimitive(base
, index
++, "_InitMIDI", prInitMIDI
, 3, 0);
598 definePrimitive(base
, index
++, "_InitMIDIClient", prInitMIDIClient
, 1, 0);
599 definePrimitive(base
, index
++, "_ConnectMIDIIn", prConnectMIDIIn
, 3, 0);
600 definePrimitive(base
, index
++, "_DisconnectMIDIIn", prDisconnectMIDIIn
, 3, 0);
601 definePrimitive(base
, index
++, "_DisposeMIDIClient", prDisposeMIDIClient
, 1, 0);
602 definePrimitive(base
, index
++, "_RestartMIDI", prRestartMIDI
, 1, 0);
603 definePrimitive(base
, index
++, "_SendMIDIOut", prSendMIDIOut
, 9, 0);
604 definePrimitive(base
, index
++, "_SendSysex", prSendSysex
, 3, 0);
605 pthread_mutex_init (&gPmStreamMutex
, NULL
);