supernova: fix for small audio vector sizes
[supercollider.git] / lang / LangPrimSource / SC_PortMIDI.cpp
blob64e52266c810372d8083bdda5bd50cb80136a821
1 /*
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
26 added prRestartMIDI
27 19/9 call different actions,disconnect midiInPort, midiout: sendmidi
28 04/feb/03 prListMIDIEndpoints modification by Ron Kuivila added jt.
31 #include "PortMIDI.h"
32 #include "PortTime.h"
34 #include "SCBase.h"
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"
46 #include "PyrSched.h"
47 #include "GC.h"
48 #include <map>
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;
63 PyrSymbol* s_midiin;
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_); }
84 ~ScopeMutexLock( ) {
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 */
96 #define LATENCY 0
98 extern bool compiledOK;
100 inline void TPmErr(PmError err) {
101 if( err != pmNoError)
102 throw err;
105 /* timer "interrupt" for processing midi data */
106 static void PMProcessMidi(PtTimestamp timestamp, void *userData)
108 ScopeMutexLock mulo(&gPmStreamMutex);
109 PmError result;
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];
116 if( midi_in )
118 while(result = Pm_Poll(midi_in))
120 long Tstatus, data1, data2;
121 if (Pm_Read(midi_in, &buffer, 1) == pmBufferOverflow)
122 continue;
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
133 if (compiledOK)
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
142 //set arguments:
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
150 switch (status)
152 case 0x80 : //noteOff
153 ++g->sp; SetInt(g->sp, data1);
154 ++g->sp; SetInt(g->sp, data2);
155 runInterpreter(g, s_midiNoteOffAction, 5);
156 break;
157 case 0x90 : //noteOn
158 ++g->sp; SetInt(g->sp, data1);
159 ++g->sp; SetInt(g->sp, data2);
160 runInterpreter(g, data2 ? s_midiNoteOnAction : s_midiNoteOffAction, 5);
161 break;
162 case 0xA0 : //polytouch
163 ++g->sp; SetInt(g->sp, data1);
164 ++g->sp; SetInt(g->sp, data2);
165 runInterpreter(g, s_midiPolyTouchAction, 5);
166 break;
167 case 0xB0 : //control
168 ++g->sp; SetInt(g->sp, data1);
169 ++g->sp; SetInt(g->sp, data2);
170 runInterpreter(g, s_midiControlAction, 5);
171 break;
172 case 0xC0 : //program
173 ++g->sp; SetInt(g->sp, data1);
174 runInterpreter(g, s_midiProgramAction, 4);
175 break;
176 case 0xD0 : //touch
177 ++g->sp; SetInt(g->sp, data1);
178 runInterpreter(g, s_midiTouchAction, 4);
179 break;
180 case 0xE0 : //bend
181 ++g->sp; SetInt(g->sp, (data2 << 7) | data1);
182 runInterpreter(g, s_midiBendAction, 4);
183 break;
184 /*case 0xF0 :
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);
188 break;
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
193 SetInt(g->sp, chan);
194 --i;
195 //goto L; // parse again with running status set // mv - get next byte
197 chan = 0;
198 i += midiProcessSystemPacket(pkt, chan); // process sysex packet
199 break; */
201 g->canCallOS = false;
203 pthread_mutex_unlock (&gLangMutex);
205 } // if (midi_in)
206 } // for loop until numMIDIInPorts
210 -------------------------------------------------------------
212 void midiCleanUp();
213 int initMIDI()
215 try {
216 midiCleanUp();
218 TPmErr(Pm_Initialize());
219 int nbDev = Pm_CountDevices();
220 int inIndex = 0;
221 int outIndex = 0;
222 int pmdid;
224 for( int i = 0; i < nbDev ; ++i ) {
225 const PmDeviceInfo* devInfo = Pm_GetDeviceInfo(i);
226 if( devInfo->input )
228 gNumMIDIInPorts++;
229 gMidiInputIndexToPmDevIndex[inIndex++] = i;
230 gMidiPmDevIndexToInputIndex[i] = inIndex;
232 if( devInfo->output )
234 gNumMIDIOutPorts++;
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 */
248 catch(PmError) {
249 return errFailed;
252 gMIDIInitialized = true;
253 return errNone;
256 -------------------------------------------------------------
258 void midiCleanUp()
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;
273 gNumMIDIInPorts = 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)
299 PyrSlot *a = g->sp;
300 int numSrc = gNumMIDIInPorts;
301 int numDst = gNumMIDIOutPorts;
303 PyrObject* idarray = newPyrArray(g->gc, 6 * sizeof(PyrObject), 0 , true);
304 SetObject(a, idarray);
306 // 0
307 PyrObject* idarraySo = newPyrArray(g->gc, numSrc * sizeof(__int32), 0 , true);
308 SetObject(idarray->slots+idarray->size++, idarraySo);
309 g->gc->GCWrite(idarray, idarraySo);
311 // 1
312 PyrObject* devarraySo = newPyrArray(g->gc, numSrc * sizeof(PyrObject), 0 , true);
313 SetObject(idarray->slots+idarray->size++, devarraySo);
314 g->gc->GCWrite(idarray, devarraySo);
316 // 2
317 PyrObject* namearraySo = newPyrArray(g->gc, numSrc * sizeof(PyrObject), 0 , true);
318 SetObject(idarray->slots+idarray->size++, namearraySo);
319 g->gc->GCWrite(idarray, namearraySo);
321 // 3
322 PyrObject* idarrayDe = newPyrArray(g->gc, numDst * sizeof(__int32), 0 , true);
323 SetObject(idarray->slots+idarray->size++, idarrayDe);
324 g->gc->GCWrite(idarray, idarrayDe);
326 // 4
327 PyrObject* namearrayDe = newPyrArray(g->gc, numDst * sizeof(PyrObject), 0 , true);
328 SetObject(idarray->slots+idarray->size++, namearrayDe);
329 g->gc->GCWrite(idarray, namearrayDe);
331 // 5
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);
343 cendname[1023] = 0;
344 strncpy(cdevname,devInfo->name,1023);
345 cdevname[1023] = 0;
347 PyrString *string = newPyrString(g->gc, cendname, 0, true);
348 SetObject(namearraySo->slots+i, string);
349 namearraySo->size++;
350 g->gc->GCWrite(namearraySo, (PyrObject*)string);
352 PyrString *devstring = newPyrString(g->gc, cdevname, 0, true);
353 SetObject(devarraySo->slots+i, devstring);
354 devarraySo->size++;
355 g->gc->GCWrite(devarraySo, (PyrObject*)devstring);
357 SetInt(idarraySo->slots+i, i);
358 idarraySo->size++;
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);
368 cendname[1023] = 0;
369 strncpy(cdevname,devInfo->name,1023);
370 cdevname[1023] = 0;
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);
384 return errNone;
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;
397 PyrSlot *c = g->sp;
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);
406 if (err)
407 return errWrongType;
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,
415 PMSTREAM_TIME_PROC,
416 PMSTREAM_TIME_INFO );
418 if(pmerr != pmNoError)
419 return errFailed;
421 gMIDIInStreams[uid] = inStream;
422 return errNone;
425 -------------------------------------------------------------
427 int prDisconnectMIDIIn(struct VMGlobals *g, int numArgsPushed)
429 ScopeMutexLock mulo(&gPmStreamMutex);
431 PyrSlot *b = g->sp - 1;
432 PyrSlot *c = g->sp;
434 int err, inputIndex, uid;
435 err = slotIntVal(b, &inputIndex);
436 if (err) return err;
437 if (inputIndex < 0 || inputIndex >= gNumMIDIInPorts) return errIndexOutOfRange;
438 err = slotIntVal(c, &uid);
439 if (err)
440 return err;
442 PmError pmerr = Pm_Close(gMIDIInStreams[uid]);
444 if(pmerr != pmNoError)
445 return errFailed;
447 gMIDIInStreams[uid] = NULL;
448 return errNone;
452 -------------------------------------------------------------
454 int prInitMIDI(struct VMGlobals *g, int numArgsPushed)
456 //PyrSlot *a = g->sp - 2;
457 PyrSlot *b = g->sp - 1;
458 PyrSlot *c = g->sp;
460 int err, numIn, numOut;
461 err = slotIntVal(b, &numIn);
462 if (err) return errWrongType;
464 err = slotIntVal(c, &numOut);
465 if (err)
466 return errWrongType;
468 return initMIDI();
471 int prDisposeMIDIClient(VMGlobals *g, int numArgsPushed)
473 midiCleanUp();
474 return errNone;
477 int prRestartMIDI(VMGlobals *g, int numArgsPushed)
479 initMIDI();
480 return errNone;
484 void freeSysex(MIDISysexSendRequest* pk)
486 free(pk->data);
487 free(pk);
491 int prSendSysex(VMGlobals *g, int numArgsPushed)
494 int err, uid, size;
495 PyrInt8Array* packet = g->sp->uob;
496 size = packet->size;
497 Byte *data = (Byte *)malloc(size);
499 memcpy(data,packet->b, size);
501 PyrSlot *u = g->sp - 1;
502 err = slotIntVal(u, &uid);
503 if (err) return err;
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);
512 return errNone;
514 return errFailed;
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;
537 float late;
538 err = slotIntVal(p, &outputIndex);
539 if (err) return err;
540 if (outputIndex < 0 || outputIndex >= gNumMIDIOutPorts) return errIndexOutOfRange;
542 err = slotIntVal(u, &uid);
543 if (err) return err;
544 err = slotIntVal(l, &length);
545 if (err) return err;
546 err = slotIntVal(his, &hiStatus);
547 if (err) return err;
548 err = slotIntVal(los, &loStatus);
549 if (err) return err;
550 err = slotIntVal(a, &aval);
551 if (err) return err;
552 err = slotIntVal(b, &bval);
553 if (err) return err;
554 err = slotFloatVal(plat, &late);
555 if (err) return err;
557 Pm_WriteShort(gMIDIOutStreams[uid], 0,
558 Pm_Message((hiStatus & 0xF0) | (loStatus & 0x0F) , aval, bval));
560 return errNone;
563 // not needed in PortMIDI:
564 int initMIDIClient()
566 return errNone;
569 int prInitMIDIClient(struct VMGlobals *g, int numArgsPushed)
571 return initMIDIClient();
574 void initMIDIPrimitives()
576 int base, index;
578 base = nextPrimitiveIndex();
579 index = 0;
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);
606 midiCleanUp();