1 /*
2 rtjack.c:
3
4 Copyright (C) 2005, 2006 Istvan Varga
5 2016 Victor Lazzarini
6
7 This file is part of Csound.
8
9 The Csound Library is free software; you can redistribute it
10 and/or modify it under the terms of the GNU Lesser General Public
11 License as published by the Free Software Foundation; either
12 version 2.1 of the License, or (at your option) any later version.
13
14 Csound is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU Lesser General Public License for more details.
18
19 You should have received a copy of the GNU Lesser General Public
20 License along with Csound; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22 02110-1301 USA
23 */
24
25 #include <jack/jack.h>
26 #include <jack/midiport.h>
27 #include <ctype.h>
28 #include <sys/time.h>
29
30 /* no #ifdef, should always have these on systems where JACK is available */
31 #include <unistd.h>
32 #include <stdint.h>
33 #ifdef LINUX
34 #include <pthread.h>
35 #endif
36 #include "csdl.h"
37 #include "soundio.h"
38 #ifdef LINUX
39 #include <sched.h>
40 #endif
41
42 /* Modified from BSD sources for strlcpy */
43 /*
44 * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
45 *
46 * Permission to use, copy, modify, and distribute this software for any
47 * purpose with or without fee is hereby granted, provided that the above
48 * copyright notice and this permission notice appear in all copies.
49 */
50 /* modifed for speed -- JPff */
51 char *
strNcpy(char * dst,const char * src,size_t siz)52 strNcpy(char *dst, const char *src, size_t siz)
53 {
54 char *d = dst;
55 const char *s = src;
56 size_t n = siz;
57
58 /* Copy as many bytes as will fit or until NULL */
59 if (n != 0) {
60 while (--n != 0) {
61 if ((*d++ = *s++) == '\0')
62 break;
63 }
64 }
65
66 /* Not enough room in dst, add NUL */
67 if (n == 0) {
68 if (siz != 0)
69 *d = '\0'; /* NUL-terminate dst */
70
71 //while (*s++) ;
72 }
73 return dst; /* count does not include NUL */
74 }
75
76
77 #include "cs_jack.h"
78 static int listDevices(CSOUND *csound,
79 CS_AUDIODEVICE *list,
80 int isOutput);
81
82 #ifdef LINUX
83
rtJack_CreateLock(CSOUND * csound,pthread_mutex_t * p)84 static inline int rtJack_CreateLock(CSOUND *csound, pthread_mutex_t *p)
85 {
86 (void) csound;
87 return (pthread_mutex_init(p, (pthread_mutexattr_t*) NULL));
88 }
89
rtJack_Lock(CSOUND * csound,pthread_mutex_t * p)90 static inline void rtJack_Lock(CSOUND *csound, pthread_mutex_t *p)
91 {
92 (void) csound;
93 pthread_mutex_lock(p);
94 }
95
rtJack_LockTimeout(CSOUND * csound,pthread_mutex_t * p,size_t milliseconds)96 static inline int rtJack_LockTimeout(CSOUND *csound, pthread_mutex_t *p,
97 size_t milliseconds)
98 {
99 IGN(csound);
100 struct timeval tv;
101 struct timespec ts;
102 register size_t n, s;
103 register int retval = pthread_mutex_trylock(p);
104 if (!retval)
105 return retval;
106 if (!milliseconds)
107 return retval;
108 gettimeofday(&tv, NULL);
109 s = milliseconds / (size_t) 1000;
110 n = milliseconds - (s * (size_t) 1000);
111 s += (size_t) tv.tv_sec;
112 n = (size_t) (((int) n * 1000 + (int) tv.tv_usec) * 1000);
113 ts.tv_nsec = (long) (n < (size_t) 1000000000 ? n : n - 1000000000);
114 ts.tv_sec = (time_t) (n < (size_t) 1000000000 ? s : s + 1);
115 return pthread_mutex_timedlock(p, &ts);
116 }
117
rtJack_TryLock(CSOUND * csound,pthread_mutex_t * p)118 static inline int rtJack_TryLock(CSOUND *csound, pthread_mutex_t *p)
119 {
120 (void) csound;
121 return (pthread_mutex_trylock(p));
122 }
123
rtJack_Unlock(CSOUND * csound,pthread_mutex_t * p)124 static inline void rtJack_Unlock(CSOUND *csound, pthread_mutex_t *p)
125 {
126 (void) csound;
127 pthread_mutex_unlock(p);
128 }
129
rtJack_DestroyLock(CSOUND * csound,pthread_mutex_t * p)130 static inline void rtJack_DestroyLock(CSOUND *csound, pthread_mutex_t *p)
131 {
132 (void) csound;
133 pthread_mutex_unlock(p);
134 pthread_mutex_destroy(p);
135 }
136
137 #else /* LINUX */
138
rtJack_CreateLock(CSOUND * csound,void ** p)139 static inline int rtJack_CreateLock(CSOUND *csound, void **p)
140 {
141 *p = csound->CreateThreadLock();
142 return (*p != NULL ? 0 : -1);
143 }
144
rtJack_Lock(CSOUND * csound,void ** p)145 static inline void rtJack_Lock(CSOUND *csound, void **p)
146 {
147 csound->WaitThreadLockNoTimeout(*p);
148 }
149
rtJack_LockTimeout(CSOUND * csound,void ** p,size_t timeout)150 static inline int rtJack_LockTimeout(CSOUND *csound, void **p, size_t timeout)
151 {
152 return csound->WaitThreadLock(*p, timeout);
153 }
154
155
rtJack_TryLock(CSOUND * csound,void ** p)156 static inline int rtJack_TryLock(CSOUND *csound, void **p)
157 {
158 return (csound->WaitThreadLock(*p, (size_t) 0));
159 }
160
rtJack_Unlock(CSOUND * csound,void ** p)161 static inline void rtJack_Unlock(CSOUND *csound, void **p)
162 {
163 csound->NotifyThreadLock(*p);
164 }
165
rtJack_DestroyLock(CSOUND * csound,void ** p)166 static inline void rtJack_DestroyLock(CSOUND *csound, void **p)
167 {
168 csound->NotifyThreadLock(*p);
169 csound->DestroyThreadLock(*p);
170 *p = NULL;
171 }
172
173 #endif /* !LINUX */
174
175 /* print error message, close connection, and terminate performance */
176
177 static CS_NORETURN void rtJack_Error(CSOUND *, int errCode, const char *msg);
178
179 static int processCallback(jack_nframes_t nframes, void *arg);
180
181 /* callback functions */
182
sampleRateCallback(jack_nframes_t nframes,void * arg)183 static int sampleRateCallback(jack_nframes_t nframes, void *arg)
184 {
185 RtJackGlobals *p = (RtJackGlobals*) arg;
186
187 if (p->sampleRate != (int) nframes)
188 p->jackState = 1;
189 return 0;
190 }
191
bufferSizeCallback(jack_nframes_t nframes,void * arg)192 static int bufferSizeCallback(jack_nframes_t nframes, void *arg)
193 {
194 RtJackGlobals *p = (RtJackGlobals*) arg;
195
196 (void) nframes;
197 /* invalidate output port buffer pointer cache */
198 if (p->outPortBufs != NULL)
199 p->outPortBufs[0] = (jack_default_audio_sample_t*) NULL;
200 return 0;
201 }
202
203 #ifdef LINUX
freeWheelCallback(int starting,void * arg)204 static void freeWheelCallback(int starting, void *arg)
205 {
206 RtJackGlobals *p;
207 CSOUND *csound;
208
209 p = (RtJackGlobals*) arg;
210 csound = p->csound;
211 if (starting) {
212 if (UNLIKELY(sched_getscheduler(0) != SCHED_OTHER)) {
213 struct sched_param sp;
214 csound->Message(csound, "%s", Str(" *** WARNING: "
215 "disabling --sched in freewheel mode\n"));
216 memset(&sp, 0, sizeof(struct sched_param));
217 sp.sched_priority = 0;
218 sched_setscheduler(0, SCHED_OTHER, &sp);
219 }
220 }
221 }
222 #endif
223
xrunCallback(void * arg)224 static int xrunCallback(void *arg)
225 {
226 RtJackGlobals *p = (RtJackGlobals*) arg;
227
228 p->xrunFlag = 1;
229 return 0;
230 }
231
shutDownCallback(void * arg)232 static void shutDownCallback(void *arg)
233 {
234 RtJackGlobals *p = (RtJackGlobals*) arg;
235
236 p->jackState = 2;
237 if (p->bufs != NULL) {
238 int i;
239 for (i = 0; i < p->nBuffers; i++) {
240 if (p->bufs[i] != NULL &&
241 (p->bufs[i]->inBufs != NULL || p->bufs[i]->outBufs != NULL))
242 rtJack_Unlock(p->csound, &(p->bufs[i]->csndLock));
243 }
244 }
245 }
246
rtJack_AlignData(size_t ofs)247 static inline size_t rtJack_AlignData(size_t ofs)
248 {
249 return ((ofs + (size_t) 15) & (~((size_t) 15)));
250 }
251
252 /* allocate ring buffers */
rtJack_AllocateBuffers(RtJackGlobals * p)253 static void rtJack_AllocateBuffers(RtJackGlobals *p)
254 {
255 CSOUND *csound = p->csound;
256 void *ptr;
257 size_t i, j, m, nBytes, nBytesPerBuf, ofs1, ofs2, ofs3;
258
259 m = (size_t) ((p->inputEnabled ? 1 : 0) + (p->outputEnabled ? 1 : 0));
260 if (!m)
261 return;
262 /* calculate the number of bytes to allocate */
263 ofs1 = rtJack_AlignData(sizeof(RtJackBuffer*) * (size_t) p->nBuffers);
264 ofs2 = rtJack_AlignData(sizeof(RtJackBuffer));
265 ofs3 = rtJack_AlignData(sizeof(jack_default_audio_sample_t*)
266 * (size_t) p->nChannels * m);
267 nBytesPerBuf = ofs2 + ofs3;
268 nBytesPerBuf += rtJack_AlignData(sizeof(jack_default_audio_sample_t)
269 * (size_t) p->nChannels
270 * (size_t) p->bufSize
271 * m);
272 nBytes = ofs1 + (nBytesPerBuf * (size_t) p->nBuffers);
273 /* allocate memory */
274 ptr = (RtJackBuffer**) csound->Malloc(csound, nBytes);
275 if (UNLIKELY(ptr == NULL))
276 rtJack_Error(csound, CSOUND_MEMORY, Str("memory allocation failure"));
277 p->bufs = (RtJackBuffer**) ptr;
278 memset((void*) ptr, 0, nBytes);
279 /* set pointer to each buffer */
280 ptr = (void*) ((char*) ptr + (long) ofs1);
281 for (i = (size_t) 0; i < (size_t) p->nBuffers; i++) {
282 p->bufs[i] = ptr;
283 ptr = (void*) ((char*) ptr + (long) nBytesPerBuf);
284 }
285 for (i = (size_t) 0; i < (size_t) p->nBuffers; i++) {
286 /* create lock for signaling when the process callback is done */
287 /* with the buffer */
288 if (UNLIKELY(rtJack_CreateLock(csound, &(p->bufs[i]->csndLock)) != 0))
289 rtJack_Error(csound, CSOUND_MEMORY, Str("memory allocation failure"));
290 /* create lock for signaling when the Csound thread is done */
291 /* with the buffer */
292 if (UNLIKELY(rtJack_CreateLock(csound, &(p->bufs[i]->jackLock)) != 0)) {
293 rtJack_DestroyLock(csound, &(p->bufs[i]->csndLock));
294 rtJack_Error(csound, CSOUND_MEMORY, Str("memory allocation failure"));
295 }
296 ptr = (void*) p->bufs[i];
297 ptr = (void*) ((char*) ptr + (long) ofs2);
298 /* set pointers to input/output buffers */
299 if (p->inputEnabled) {
300 p->bufs[i]->inBufs = (jack_default_audio_sample_t**) ptr;
301 ptr = (void*) &(p->bufs[i]->inBufs[p->nChannels]);
302 }
303 if (p->outputEnabled)
304 p->bufs[i]->outBufs = (jack_default_audio_sample_t**) ptr;
305 ptr = (void*) p->bufs[i];
306 ptr = (void*) ((char*) ptr + (long) (ofs2 + ofs3));
307 for (j = (size_t) 0; j < (size_t) p->nChannels; j++) {
308 if (p->inputEnabled) {
309 p->bufs[i]->inBufs[j] = (jack_default_audio_sample_t*) ptr;
310 ptr = (void*) &(p->bufs[i]->inBufs[j][p->bufSize]);
311 }
312 if (p->outputEnabled) {
313 p->bufs[i]->outBufs[j] = (jack_default_audio_sample_t*) ptr;
314 ptr = (void*) &(p->bufs[i]->outBufs[j][p->bufSize]);
315 }
316 }
317 }
318 }
319
listPorts(CSOUND * csound,int isOutput)320 static void listPorts(CSOUND *csound, int isOutput){
321 int i,n = listDevices(csound,NULL,isOutput);
322 CS_AUDIODEVICE *devs = (CS_AUDIODEVICE *)
323 csound->Malloc(csound, n*sizeof(CS_AUDIODEVICE));
324 listDevices(csound,devs,isOutput);
325 csound->Message(csound, "Jack %s ports:\n",
326 isOutput ? "output" : "input");
327 for(i=0; i < n; i++)
328 csound->Message(csound, " %d: %s (dac:%s)\n",
329 i, devs[i].device_id, devs[i].device_name);
330 csound->Free(csound,devs);
331 }
332
333 /* register JACK ports */
334
rtJack_RegisterPorts(RtJackGlobals * p)335 static void rtJack_RegisterPorts(RtJackGlobals *p)
336 {
337 char portName[MAX_NAME_LEN + 4];
338 unsigned long flags = 0UL;
339 int i;
340 CSOUND *csound = p->csound;
341
342 if (!(p->inputEnabled && p->outputEnabled))
343 flags = (unsigned long) JackPortIsTerminal;
344 if (p->inputEnabled) {
345 /* register input ports */
346 for (i = 0; i < p->nChannels_i; i++) {
347 snprintf(portName, MAX_NAME_LEN + 4, "%s%d", p->inputPortName, i + 1);
348 p->inPorts[i] = jack_port_register(p->client, &(portName[0]),
349 JACK_DEFAULT_AUDIO_TYPE,
350 flags | JackPortIsInput, 0UL);
351 if (UNLIKELY(p->inPorts[i] == NULL))
352 rtJack_Error(csound, -1, Str("error registering input ports"));
353 }
354 }
355 if (p->outputEnabled) {
356 /* register output ports */
357 for (i = 0; i < p->nChannels; i++) {
358 snprintf(portName, MAX_NAME_LEN + 4, "%s%d", p->outputPortName, i + 1);
359 p->outPorts[i] = jack_port_register(p->client, &(portName[0]),
360 JACK_DEFAULT_AUDIO_TYPE,
361 flags | JackPortIsOutput, 0UL);
362 if (UNLIKELY(p->outPorts[i] == NULL))
363 rtJack_Error(csound, -1, Str("error registering output ports"));
364 }
365 }
366 }
367
368
369 /* connect to JACK server, set up ports and ring buffers, */
370 /* activate client, and connect ports if requested */
371
openJackStreams(RtJackGlobals * p)372 static void openJackStreams(RtJackGlobals *p)
373 {
374 char buf[256];
375 int i, j, k;
376 CSOUND *csound = p->csound;
377
378 /* connect to JACK server */
379 p->client = jack_client_open(&(p->clientName[0]), JackNoStartServer, NULL);
380 if (UNLIKELY(p->client == NULL))
381 rtJack_Error(csound, -1, Str("could not connect to JACK server"));
382
383 csound->system_sr(csound, jack_get_sample_rate(p->client));
384 csound->Message(csound, "system sr: %f\n", csound->system_sr(csound,0));
385 if(p->sampleRate < 0) p->sampleRate = jack_get_sample_rate(p->client);
386
387 /* check consistency of parameters */
388 if (UNLIKELY(p->nChannels < 1 || p->nChannels > 255))
389 rtJack_Error(csound, -1, Str("invalid number of channels"));
390 if (p->inputEnabled) {
391 if (UNLIKELY(p->nChannels_i < 1 || p->nChannels > 255))
392 rtJack_Error(csound, -1, Str("invalid number of input channels"));
393 }
394 if (UNLIKELY(p->sampleRate < 1000 || p->sampleRate > 768000))
395 rtJack_Error(csound, -1, Str("invalid sample rate"));
396 if (UNLIKELY(p->sampleRate != (int) jack_get_sample_rate(p->client))) {
397 snprintf(&(buf[0]), 256, Str("sample rate %d does not match "
398 "JACK sample rate %d"),
399 p->sampleRate, (int) jack_get_sample_rate(p->client));
400 rtJack_Error(p->csound, -1, &(buf[0]));
401 }
402 if (UNLIKELY(p->bufSize < 8 || p->bufSize > 32768))
403 rtJack_Error(csound, -1, Str("invalid period size (-b)"));
404 if (p->nBuffers < 2)
405 p->nBuffers = 2;
406 if (UNLIKELY((unsigned int) (p->nBuffers * p->bufSize) > (unsigned int) 65536))
407 rtJack_Error(csound, -1, Str("invalid buffer size (-B)"));
408 if (UNLIKELY(((p->nBuffers - 1) * p->bufSize)
409 < (int) jack_get_buffer_size(p->client)))
410 rtJack_Error(csound, -1, Str("buffer size (-B) is too small"));
411
412 /* register ports */
413 rtJack_RegisterPorts(p);
414
415 /* allocate ring buffers if not done yet */
416 if (p->bufs == NULL)
417 rtJack_AllocateBuffers(p);
418
419 /* initialise ring buffers */
420 p->csndBufCnt = 0;
421 p->csndBufPos = 0;
422 p->jackBufCnt = 0;
423 p->jackBufPos = 0;
424 for (i = 0; i < p->nBuffers; i++) {
425 rtJack_TryLock(p->csound, &(p->bufs[i]->csndLock));
426 rtJack_Unlock(p->csound, &(p->bufs[i]->jackLock));
427 if (p->inputEnabled) {
428 for (j = 0; j < p->nChannels_i; j++) {
429 for (k = 0; k < p->bufSize; k++)
430 p->bufs[i]->inBufs[j][k] = (jack_default_audio_sample_t) 0;
431 }
432 }
433 if (p->outputEnabled) {
434 for (j = 0; j < p->nChannels; j++) {
435 for (k = 0; k < p->bufSize; k++)
436 p->bufs[i]->outBufs[j][k] = (jack_default_audio_sample_t) 0;
437 }
438 }
439 }
440
441 /* output port buffer pointer cache is invalid initially */
442 if (p->outputEnabled)
443 p->outPortBufs[0] = (jack_default_audio_sample_t*) NULL;
444
445 /* register callback functions */
446 if (UNLIKELY(jack_set_sample_rate_callback(p->client,
447 sampleRateCallback, (void*) p)
448 != 0))
449 rtJack_Error(csound, -1, Str("error setting sample rate callback"));
450 if (UNLIKELY(jack_set_buffer_size_callback(p->client,
451 bufferSizeCallback, (void*) p)
452 != 0))
453 rtJack_Error(csound, -1, Str("error setting buffer size callback"));
454 #ifdef LINUX
455 if (UNLIKELY(jack_set_freewheel_callback(p->client,
456 freeWheelCallback, (void*) p)
457 != 0))
458 rtJack_Error(csound, -1, Str("error setting freewheel callback"));
459 #endif
460 if (UNLIKELY(jack_set_xrun_callback(p->client, xrunCallback, (void*) p) != 0))
461 rtJack_Error(csound, -1, Str("error setting xrun callback"));
462 jack_on_shutdown(p->client, shutDownCallback, (void*) p);
463 if (UNLIKELY(jack_set_process_callback(p->client,
464 processCallback, (void*) p) != 0))
465 rtJack_Error(csound, -1, Str("error setting process callback"));
466
467 /* activate client */
468 if (UNLIKELY(jack_activate(p->client) != 0))
469 rtJack_Error(csound, -1, Str("error activating JACK client"));
470
471 /* connect ports if requested */
472 if (p->inputEnabled) {
473 listPorts(csound,0);
474 if (p->inDevNum >= 0){
475 int num = p->inDevNum;
476 unsigned long portFlags = JackPortIsOutput;
477 char **portNames = (char**) jack_get_ports(p->client,
478 (char*) NULL,
479 JACK_DEFAULT_AUDIO_TYPE,
480 portFlags);
481
482 for (i = 0; i < p->nChannels_i; i++) {
483 if (portNames[num+i] != NULL){
484 csound->Message(csound, Str("connecting channel %d to %s\n"),
485 i,portNames[num+i]);
486 if (jack_connect(p->client, portNames[num+i],
487 jack_port_name(p->inPorts[i])) != 0) {
488 csound->Warning(csound,
489 Str("failed autoconnecting input channel %d\n"
490 "(needs manual connection)"), i+1);
491 }
492 } else
493 csound->Warning(csound, Str("jack port %d not valid\n"
494 "failed autoconnecting input channel %d\n"
495 "(needs manual connection)"), num+i, i+1);
496 }
497 jack_free(portNames);
498
499 }
500 else {
501 if (strcmp(p->outDevName, "null") && p->inDevName != NULL){
502 char dev[128], *dev_final, *sp;
503 strNcpy(dev, p->inDevName, 128); //dev[127]='\0';
504 dev_final = dev;
505 sp = strchr(dev_final, '\0');
506 if (!isalpha(dev_final[0])) dev_final++;
507 for (i = 0; i < p->nChannels; i++) {
508 snprintf(sp, 128-(dev-sp), "%d", i + 1);
509 csound->Message(csound, Str("connecting channel %d to %s\n"),
510 i, dev_final);
511 if (UNLIKELY(jack_connect(p->client, dev_final,
512 jack_port_name(p->inPorts[i])) != 0)) {
513 csound->Warning(csound,
514 Str("not autoconnecting input channel %d\n"
515 "(needs manual connection)"), i+1);
516 }
517 }
518 *sp = (char) 0;
519 }
520 csound->Message(csound, "%s", Str("put port not connected\n"));
521 }
522
523 }
524 if (p->outputEnabled) {
525 listPorts(csound,1);
526 if (p->outDevNum >= 0) {
527 int num = p->outDevNum;
528 unsigned long portFlags = JackPortIsInput;
529 char **portNames = (char**) jack_get_ports(p->client,
530 (char*) NULL,
531 JACK_DEFAULT_AUDIO_TYPE,
532 portFlags);
533
534 for (i = 0; i < p->nChannels; i++) {
535 if (portNames[num+i] != NULL) {
536 csound->Message(csound, Str("connecting channel %d to %s\n"),
537 i,portNames[num+i]);
538 if (jack_connect(p->client, jack_port_name(p->outPorts[i]),
539 portNames[num+i]) != 0) {
540 csound->Warning(csound,
541 Str("failed autoconnecting output channel %d\n"
542 "(needs manual connection)"), i+1);
543 }
544 } else
545 csound->Warning(csound, Str("jack port %d not valid\n"
546 "failed autoconnecting output channel %d\n"
547 "(needs manual connection)"), num+i, i+1);
548 }
549
550 jack_free(portNames);
551 }
552 else {
553 if (p->outDevName != NULL && strcmp(p->outDevName, "null")){
554 char dev[128], *dev_final, *sp;
555 strNcpy(dev, p->outDevName, 128); //dev[127]='\0';
556 dev_final = dev;
557 sp = strchr(dev_final, '\0');
558 if (!isalpha(dev_final[0])) dev_final++;
559 for (i = 0; i < p->nChannels; i++) {
560 snprintf(sp, 128-(dev-sp), "%d", i + 1);
561 csound->Message(csound, Str("connecting channel %d to %s\n"),
562 i, dev_final);
563 if (UNLIKELY(jack_connect(p->client, jack_port_name(p->outPorts[i]),
564 dev_final) != 0)) {
565 csound->Warning(csound, Str("failed to autoconnect output channel "
566 "%d\n(needs manual connection)"), i+1);
567
568 }
569 }
570 *sp = (char) 0;
571 } else
572 csound->Message(csound, "%s", Str("output port not connected\n"));
573 }
574 }
575 /* stream is now active */
576 p->jackState = 0;
577 }
578
579 /* Make a copy of the device name specified for -i adc or -o dac, */
580 /* allocating extra space for a channel number suffix. */
581 /* Also set up other device parameters, and check consistency. */
582
rtJack_CopyDevParams(RtJackGlobals * p,const csRtAudioParams * parm,int isOutput)583 static void rtJack_CopyDevParams(RtJackGlobals *p,
584 const csRtAudioParams *parm, int isOutput)
585 {
586 CSOUND *csound;
587 char *s;
588 size_t nBytes;
589
590 csound = p->csound;
591
592 if (parm->devNum != 1024) {
593 if (isOutput){
594 p->outDevNum = parm->devNum;
595 p->outDevName = NULL;
596 }
597 else {
598 p->inDevNum = parm->devNum;
599 p->inDevName = NULL;
600 }
601 }
602 else {
603 if (parm->devName != NULL && parm->devName[0] != (char) 0) {
604 /* NOTE: this assumes max. 999 channels (the current limit is 255) */
605 nBytes = strlen(parm->devName) + 4;
606 s = (char*) csound->Malloc(csound, nBytes+1);
607 if (UNLIKELY(s == NULL))
608 rtJack_Error(csound, CSOUND_MEMORY, Str("memory allocation failure"));
609 strcpy(s, parm->devName);
610 if (isOutput){
611 p->outDevNum = -1;
612 p->outDevName = s;
613 }
614 else {
615 p->inDevName = s;
616 p->inDevNum = -1;
617 }
618 }
619 if (isOutput && p->inputEnabled) {
620 /* full duplex audio I/O: check consistency of parameters */
621 if (UNLIKELY(/*p->nChannels != parm->nChannels ||*/
622 (unsigned int)p->bufSize != parm->bufSamp_SW))
623 rtJack_Error(csound, -1,
624 Str("input and output parameters are not consistent"));
625 if (UNLIKELY((unsigned int)((parm->bufSamp_SW / csound->GetKsmps(csound)) *
626 csound->GetKsmps(csound)) != parm->bufSamp_SW))
627 rtJack_Error(csound, -1,
628 Str("period size (-b) must be an integer "
629 "multiple of ksmps"));
630 }
631 }
632 p->sampleRate = (int) parm->sampleRate;
633 if (UNLIKELY((float) p->sampleRate != parm->sampleRate))
634 rtJack_Error(csound, -1, Str("sample rate must be an integer"));
635 if (isOutput) p->nChannels = parm->nChannels;
636 else p->nChannels_i = parm->nChannels;
637
638 p->bufSize = parm->bufSamp_SW;
639 p->nBuffers = (parm->bufSamp_HW + parm->bufSamp_SW - 1) / parm->bufSamp_SW;
640
641 }
642
643 /* open for audio input */
644
recopen_(CSOUND * csound,const csRtAudioParams * parm)645 static int recopen_(CSOUND *csound, const csRtAudioParams *parm)
646 {
647 RtJackGlobals *p;
648
649 p = (RtJackGlobals*) csound->QueryGlobalVariable(csound, "_rtjackGlobals");
650 if (p == NULL)
651 return -1;
652 *(csound->GetRtRecordUserData(csound)) = (void*) p;
653 rtJack_CopyDevParams(p, parm, 0);
654 p->inputEnabled = 1;
655 /* allocate pointers to input ports */
656 p->inPorts = (jack_port_t**)
657 csound->Calloc(csound, (size_t) p->nChannels_i* sizeof(jack_port_t*));
658 if (UNLIKELY(p->inPorts == NULL))
659 rtJack_Error(p->csound, CSOUND_MEMORY, Str("memory allocation failure"));
660 /* allocate pointers to input port buffers */
661 p->inPortBufs = (jack_default_audio_sample_t**)
662 csound->Calloc(csound,
663 (size_t)p->nChannels_i * sizeof(jack_default_audio_sample_t*));
664 if (UNLIKELY(p->inPortBufs == NULL))
665 rtJack_Error(p->csound, CSOUND_MEMORY, Str("memory allocation failure"));
666 return 0;
667 }
668
669 /* open for audio output */
670
playopen_(CSOUND * csound,const csRtAudioParams * parm)671 static int playopen_(CSOUND *csound, const csRtAudioParams *parm)
672 {
673 RtJackGlobals *p;
674
675 p = (RtJackGlobals*) csound->QueryGlobalVariable(csound, "_rtjackGlobals");
676 if (p == NULL)
677 return -1;
678 *(csound->GetRtPlayUserData(csound)) = (void*) p;
679 rtJack_CopyDevParams(p, parm, 1);
680
681 p->outputEnabled = 1;
682 /* allocate pointers to output ports */
683 p->outPorts = (jack_port_t**)
684 csound->Calloc(csound, (size_t) p->nChannels* sizeof(jack_port_t*));
685 if (UNLIKELY(p->outPorts == NULL))
686 rtJack_Error(p->csound, CSOUND_MEMORY, Str("memory allocation failure"));
687 /* allocate pointers to output port buffers */
688 p->outPortBufs = (jack_default_audio_sample_t**)
689 csound->Calloc(csound,
690 (size_t) p->nChannels* sizeof(jack_default_audio_sample_t*));
691 if (UNLIKELY(p->outPortBufs == NULL))
692 rtJack_Error(p->csound, CSOUND_MEMORY, Str("memory allocation failure"));
693 /* activate client to start playback */
694 openJackStreams(p);
695
696 return 0;
697 }
698
699 /* the process callback is called by the JACK client thread, */
700 /* and copies data to the input and from the output ring buffers */
701
processCallback(jack_nframes_t nframes,void * arg)702 static int processCallback(jack_nframes_t nframes, void *arg)
703 {
704 RtJackGlobals *p;
705 int i, j, k, l;
706
707 p = (RtJackGlobals*) arg;
708 /* get pointers to port buffers */
709 if (p->inputEnabled) {
710 for (i = 0; i < p->nChannels_i; i++)
711 p->inPortBufs[i] = (jack_default_audio_sample_t*)
712 jack_port_get_buffer(p->inPorts[i], nframes);
713 }
714 if (p->outputEnabled && p->outPortBufs[0] == NULL) {
715 for (i = 0; i < p->nChannels; i++)
716 p->outPortBufs[i] = (jack_default_audio_sample_t*)
717 jack_port_get_buffer(p->outPorts[i], nframes);
718 }
719 i = 0;
720 do {
721 /* if starting new buffer: */
722 if (p->jackBufPos == 0) {
723 /* check for xrun: */
724 if (rtJack_TryLock(p->csound, &(p->bufs[p->jackBufCnt]->jackLock))
725 != 0) {
726 p->xrunFlag = 1;
727 /* yes, discard input and fill output with zero samples */
728 if (p->outputEnabled) {
729 for (j = 0; j < p->nChannels; j++)
730 for (k = i; k < (int) nframes; k++)
731 p->outPortBufs[j][k] = (jack_default_audio_sample_t) 0;
732 return 0;
733 }
734 }
735 }
736 /* copy audio data on each channel */
737 k = (int) nframes - i;
738 l = p->bufSize - p->jackBufPos;
739 l = (l < k ? l : k); /* number of frames to copy */
740 if (p->inputEnabled) {
741 for (j = 0; j < p->nChannels_i; j++) {
742 jack_default_audio_sample_t *srcp, *dstp;
743 srcp = &(p->inPortBufs[j][i]);
744 dstp = &(p->bufs[p->jackBufCnt]->inBufs[j][p->jackBufPos]);
745 for (k = 0; k < l; k++)
746 dstp[k] = srcp[k];
747 }
748 }
749 if (p->outputEnabled) {
750 for (j = 0; j < p->nChannels; j++) {
751 jack_default_audio_sample_t *srcp, *dstp;
752 srcp = &(p->bufs[p->jackBufCnt]->outBufs[j][p->jackBufPos]);
753 dstp = &(p->outPortBufs[j][i]);
754 for (k = 0; k < l; k++)
755 dstp[k] = srcp[k];
756 }
757 }
758 p->jackBufPos += l;
759 i += l;
760 /* if done with a buffer, notify Csound thread and advance to next one */
761 if (p->jackBufPos >= p->bufSize) {
762 p->jackBufPos = 0;
763 rtJack_Unlock(p->csound, &(p->bufs[p->jackBufCnt]->csndLock));
764 if (++(p->jackBufCnt) >= p->nBuffers)
765 p->jackBufCnt = 0;
766 }
767 } while (i < (int) nframes);
768 return 0;
769 }
770
rtJack_Abort(CSOUND * csound,int err)771 static CS_NOINLINE CS_NORETURN void rtJack_Abort(CSOUND *csound, int err)
772 {
773 switch (err) {
774 case 1:
775 rtJack_Error(csound, -1, Str("JACK sample rate changed"));
776 break;
777 default:
778 rtJack_Error(csound, -1, Str("no connection to JACK server"));
779 }
780 }
781
rtJack_Restart(RtJackGlobals * p)782 static CS_NOINLINE void rtJack_Restart(RtJackGlobals *p)
783 {
784 CSOUND *csound = p->csound;
785
786 csound->ErrorMsg(csound, "%s", Str(" *** rtjack: connection to JACK "
787 "server was lost, reconnecting..."));
788 p->jackState = -1;
789 jack_client_close(p->client);
790 openJackStreams(p);
791 }
792
793 /* get samples from ADC */
794
rtrecord_(CSOUND * csound,MYFLT * inbuf_,int bytes_)795 static int rtrecord_(CSOUND *csound, MYFLT *inbuf_, int bytes_)
796 {
797 RtJackGlobals *p;
798 int i, j, k, nframes, bufpos, bufcnt;
799
800 p = (RtJackGlobals*) *(csound->GetRtPlayUserData(csound));
801 if (UNLIKELY(p==NULL)) rtJack_Abort(csound, 0);
802 if (p->jackState != 0) {
803 if (p->jackState < 0)
804 openJackStreams(p); /* open audio input */
805 else if (p->jackState == 2)
806 rtJack_Restart(p);
807 else
808 rtJack_Abort(csound, p->jackState);
809 }
810 nframes = bytes_ / (p->nChannels_i * (int) sizeof(MYFLT));
811 bufpos = p->csndBufPos;
812 bufcnt = p->csndBufCnt;
813 for (i = j = 0; i < nframes; i++) {
814 if (bufpos == 0) {
815 /* wait until there is enough data in ring buffer */
816 /* VL 28.03.15 -- timeout after wait for 10 buffer
817 lengths */
818 int ret = rtJack_LockTimeout(csound, &(p->bufs[bufcnt]->csndLock),
819 10000*(nframes/csound->GetSr(csound)));
820 if (ret) {
821 memset(inbuf_, 0, bytes_);
822 OPARMS oparms;
823 csound->GetOParms(csound, &oparms);
824 if (UNLIKELY(oparms.msglevel & 4))
825 csound->Warning(csound, "%s", Str("rtjack: input audio timeout"));
826 return bytes_;
827 }
828 }
829 /* copy audio data */
830 for (k = 0; k < p->nChannels_i; k++)
831 inbuf_[j++] = (MYFLT) p->bufs[bufcnt]->inBufs[k][i];
832 if (++bufpos >= p->bufSize) {
833 bufpos = 0;
834 /* notify JACK callback that this buffer has been consumed */
835 if (!p->outputEnabled)
836 rtJack_Unlock(csound, &(p->bufs[bufcnt]->jackLock));
837 /* advance to next buffer */
838 if (++bufcnt >= p->nBuffers)
839 bufcnt = 0;
840 }
841 }
842 if (!p->outputEnabled) {
843 p->csndBufPos = bufpos;
844 p->csndBufCnt = bufcnt;
845 }
846 if (p->xrunFlag) {
847 p->xrunFlag = 0;
848 OPARMS oparms;
849 csound->GetOParms(csound, &oparms);
850 if (UNLIKELY(oparms.msglevel & 4))
851 csound->Warning(csound, "%s", Str("rtjack: xrun in real time audio"));
852 }
853
854 return bytes_;
855 }
856
857 /* put samples to DAC */
858
rtplay_(CSOUND * csound,const MYFLT * outbuf_,int bytes_)859 static void rtplay_(CSOUND *csound, const MYFLT *outbuf_, int bytes_)
860 {
861 RtJackGlobals *p;
862 int i, j, k, nframes;
863
864 p = (RtJackGlobals*) *(csound->GetRtPlayUserData(csound));
865 if (p == NULL)
866 return;
867 if (p->jackState != 0) {
868 if (p->jackState == 2)
869 rtJack_Restart(p);
870 else
871 rtJack_Abort(csound, p->jackState);
872 return;
873 }
874 nframes = bytes_ / (p->nChannels * (int) sizeof(MYFLT));
875 for (i = j = 0; i < nframes; i++) {
876 if (p->csndBufPos == 0) {
877 /* wait until there is enough free space in ring buffer */
878 if (!p->inputEnabled)
879 /* **** COVERITY: claims this is a double lock **** */
880 rtJack_Lock(csound, &(p->bufs[p->csndBufCnt]->csndLock));
881 }
882 /* copy audio data */
883 for (k = 0; k < p->nChannels; k++)
884 p->bufs[p->csndBufCnt]->outBufs[k][i] =
885 (jack_default_audio_sample_t) outbuf_[j++];
886 if (++(p->csndBufPos) >= p->bufSize) {
887 p->csndBufPos = 0;
888 /* notify JACK callback that this buffer is now filled */
889 rtJack_Unlock(csound, &(p->bufs[p->csndBufCnt]->jackLock));
890 /* advance to next buffer */
891 if (++(p->csndBufCnt) >= p->nBuffers)
892 p->csndBufCnt = 0;
893 }
894 }
895 if (p->xrunFlag) {
896 p->xrunFlag = 0;
897 csound->Warning(csound, "%s", Str("rtjack: xrun in real time audio"));
898 }
899 }
900
901 /* release ring buffers */
902
rtJack_DeleteBuffers(RtJackGlobals * p)903 static void rtJack_DeleteBuffers(RtJackGlobals *p)
904 {
905 RtJackBuffer **bufs;
906 size_t i;
907
908 if (p->bufs == (RtJackBuffer**) NULL)
909 return;
910 bufs = p->bufs;
911 p->bufs = (RtJackBuffer**) NULL;
912 for (i = (size_t) 0; i < (size_t) p->nBuffers; i++) {
913 if (bufs[i]->inBufs == (jack_default_audio_sample_t**) NULL &&
914 bufs[i]->outBufs == (jack_default_audio_sample_t**) NULL)
915 continue;
916 rtJack_DestroyLock(p->csound, &(bufs[i]->csndLock));
917 rtJack_DestroyLock(p->csound, &(bufs[i]->jackLock));
918 }
919 p->csound->Free(p->csound,(void*) bufs);
920 }
921
922 /* close the I/O device entirely */
923 /* called only when both complete */
924
rtclose_(CSOUND * csound)925 static CS_NOINLINE void rtclose_(CSOUND *csound)
926 {
927 RtJackGlobals p;
928 RtJackGlobals *pp;
929 int i;
930
931 pp = (RtJackGlobals*) csound->QueryGlobalVariable(csound, "_rtjackGlobals");
932 if (pp == NULL)
933 return;
934 *(csound->GetRtPlayUserData(csound)) = NULL;
935 *(csound->GetRtRecordUserData(csound)) = NULL;
936 memcpy(&p, pp, sizeof(RtJackGlobals));
937 /* free globals */
938
939 if (p.client != (jack_client_t*) NULL) {
940 /* deactivate client */
941 //if (p.jackState != 2) {
942 //if (p.jackState == 0)
943 // csound->Sleep((size_t)
944 // ((int) ((double) (p.bufSize * p.nBuffers)
945 // * 1000.0 / (double) p.sampleRate + 0.999)));
946 jack_deactivate(p.client);
947 //}
948 csound->Sleep((size_t) 50);
949 /* unregister and free all ports */
950 if (p.inPorts != NULL) {
951 for (i = 0; i < p.nChannels_i; i++) {
952 if (p.inPorts[i] != NULL && p.jackState != 2)
953 jack_port_unregister(p.client, p.inPorts[i]);
954 }
955 }
956 if (p.outPorts != NULL) {
957 for (i = 0; i < p.nChannels; i++) {
958 if (p.outPorts[i] != NULL && p.jackState != 2)
959 jack_port_unregister(p.client, p.outPorts[i]);
960 }
961 }
962 /* close connection */
963 if (p.jackState != 2) {
964 jack_client_close(p.client);
965 }
966 }
967 /* free copy of input and output device name */
968 if (p.inDevName != NULL)
969 csound->Free(csound,p.inDevName);
970 if (p.outDevName != NULL)
971 csound->Free(csound,p.outDevName);
972 /* free ports and port buffer pointers */
973 if (p.inPorts != NULL)
974 csound->Free(csound,p.inPorts);
975 if (p.inPortBufs != NULL)
976 csound->Free(csound,p.inPortBufs);
977 if (p.outPorts != NULL)
978 csound->Free(csound,p.outPorts);
979 if (p.outPortBufs != NULL)
980 csound->Free(csound,p.outPortBufs);
981 /* free ring buffers */
982 rtJack_DeleteBuffers(&p);
983 csound->DestroyGlobalVariable(csound, "_rtjackGlobals");
984 }
985
986 /* print error message, close connection, and terminate performance */
987
rtJack_Error(CSOUND * csound,int errCode,const char * msg)988 static CS_NORETURN void rtJack_Error(CSOUND *csound,
989 int errCode, const char *msg)
990 {
991 csound->ErrorMsg(csound, " *** rtjack: %s", msg);
992 rtclose_(csound);
993 csound->LongJmp(csound, errCode);
994 }
995
listDevices(CSOUND * csound,CS_AUDIODEVICE * list,int isOutput)996 int listDevices(CSOUND *csound, CS_AUDIODEVICE *list, int isOutput){
997
998 char **portNames = (char**) NULL, port[64];
999 unsigned long portFlags;
1000 int i, n, cnt=0;
1001 jack_client_t *jackClient;
1002 RtJackGlobals* p =
1003 (RtJackGlobals*) csound->QueryGlobalVariableNoCheck(csound,
1004 "_rtjackGlobals");
1005
1006 if (p->listclient == NULL)
1007 p->listclient = jack_client_open("list", JackNoStartServer, NULL);
1008
1009 jackClient = p->listclient;
1010
1011 if (jackClient == NULL) return 0;
1012 portFlags = (isOutput ? (unsigned long) JackPortIsInput
1013 : (unsigned long) JackPortIsOutput);
1014
1015 portNames = (char**) jack_get_ports(jackClient,
1016 (char*) NULL,
1017 JACK_DEFAULT_AUDIO_TYPE,
1018 portFlags);
1019 if (portNames == NULL) {
1020 jack_client_close(jackClient);
1021 p->listclient = NULL;
1022 return 0;
1023 }
1024
1025 memset(port, '\0', 64);
1026 for(i=0; portNames[i] != NULL; i++, cnt++) {
1027 n = (int) strlen(portNames[i]);
1028 strNcpy(port, portNames[i], n+1);
1029 //port[n] = '\0';
1030 if (list != NULL) {
1031 strNcpy(list[cnt].device_name, port, 63);
1032 snprintf(list[cnt].device_id, 63, "%s%d",
1033 isOutput ? "dac" : "adc", cnt);
1034 list[cnt].max_nchnls = 1;
1035 list[cnt].isOutput = isOutput;
1036 }
1037 }
1038 jack_free(portNames);
1039 jack_client_close(jackClient);
1040 p->listclient = NULL;
1041 return cnt;
1042 }
1043
1044 typedef struct RtJackMIDIGlobals_ {
1045 char clientName[MAX_NAME_LEN];
1046 char inputPortName[MAX_NAME_LEN];
1047 char outputPortName[MAX_NAME_LEN];
1048 } RtJackMIDIGlobals;
1049
1050
1051 /* module interface functions */
csoundModuleCreate(CSOUND * csound)1052 PUBLIC int csoundModuleCreate(CSOUND *csound)
1053 {
1054 RtJackGlobals *p;
1055 int i, j;
1056 OPARMS oparms;
1057 csound->GetOParms(csound, &oparms);
1058
1059 /* allocate and initialise globals */
1060 if (UNLIKELY(oparms.msglevel & 0x400))
1061 csound->Message(csound, "%s",
1062 Str("JACK real-time audio module for Csound\n"));
1063 if (UNLIKELY(csound->CreateGlobalVariable(csound, "_rtjackGlobals",
1064 sizeof(RtJackGlobals)) != 0)) {
1065 csound->ErrorMsg(csound, "%s", Str(" *** rtjack: error allocating globals"));
1066 return -1;
1067 }
1068 p = (RtJackGlobals*) csound->QueryGlobalVariableNoCheck(csound,
1069 "_rtjackGlobals");
1070 p->csound = csound;
1071 p->jackState = -1;
1072 strcpy(&(p->clientName[0]), "csound6");
1073 strcpy(&(p->inputPortName[0]), "input");
1074 strcpy(&(p->outputPortName[0]), "output");
1075 p->sleepTime = 1000; /* this is not actually used */
1076 p->inDevName = (char*) NULL;
1077 p->outDevName = (char*) NULL;
1078 p->client = (jack_client_t*) NULL;
1079 p->inPorts = (jack_port_t**) NULL;
1080 p->inPortBufs = (jack_default_audio_sample_t**) NULL;
1081 p->outPorts = (jack_port_t**) NULL;
1082 p->outPortBufs = (jack_default_audio_sample_t**) NULL;
1083 p->bufs = (RtJackBuffer**) NULL;
1084 /* register options: */
1085 /* client name */
1086 i = jack_client_name_size();
1087 if (i > (MAX_NAME_LEN + 1))
1088 i = (MAX_NAME_LEN + 1);
1089 csound->CreateConfigurationVariable(csound, "jack_client",
1090 (void*) &(p->clientName[0]),
1091 CSOUNDCFG_STRING, 0, NULL, &i,
1092 Str("JACK client name (default: csound6)"),
1093 NULL);
1094 /* input port name */
1095 i = jack_port_name_size() - 3;
1096 if (i > (MAX_NAME_LEN + 1))
1097 i = (MAX_NAME_LEN + 1);
1098 csound->CreateConfigurationVariable(csound, "jack_inportname",
1099 (void*) &(p->inputPortName[0]),
1100 CSOUNDCFG_STRING, 0, NULL, &i,
1101 Str("JACK input port name prefix "
1102 "(default: input)"), NULL);
1103 /* output port name */
1104 i = jack_port_name_size() - 3;
1105 if (i > (MAX_NAME_LEN + 1))
1106 i = (MAX_NAME_LEN + 1);
1107 csound->CreateConfigurationVariable(csound, "jack_outportname",
1108 (void*) &(p->outputPortName[0]),
1109 CSOUNDCFG_STRING, 0, NULL, &i,
1110 Str("JACK output port name prefix"
1111 " (default: output)"), NULL);
1112 /* sleep time */
1113 i = 250; j = 25000; /* min/max value */
1114 csound->CreateConfigurationVariable(csound, "jack_sleep_time",
1115 (void*) &(p->sleepTime),
1116 CSOUNDCFG_INTEGER, 0, &i, &j,
1117 Str("Deprecated"), NULL);
1118 /* done */
1119 p->listclient = NULL;
1120
1121
1122 RtJackMIDIGlobals *pm;
1123 if (oparms.msglevel & 0x400)
1124 csound->Message(csound, "%s", Str("JACK MIDI module for Csound\n"));
1125 if (csound->CreateGlobalVariable(csound, "_rtjackMIDIGlobals",
1126 sizeof(RtJackMIDIGlobals)) != 0) {
1127 csound->ErrorMsg(csound, "%s",
1128 Str(" *** rtjack MIDI: error allocating globals"));
1129 return -1;
1130 }
1131 pm = (RtJackMIDIGlobals*)
1132 csound->QueryGlobalVariableNoCheck(csound, "_rtjackMIDIGlobals");
1133
1134 strcpy(&(pm->clientName[0]), "csound6-midi");
1135 strcpy(&(pm->inputPortName[0]), "port");
1136 strcpy(&(pm->outputPortName[0]), "port");
1137 /* client name */
1138 i = jack_client_name_size();
1139 if (i > (MAX_NAME_LEN + 1))
1140 i = (MAX_NAME_LEN + 1);
1141 csound->CreateConfigurationVariable(csound, "jack_midi_client",
1142 (void*) &(pm->clientName[0]),
1143 CSOUNDCFG_STRING, 0, NULL, &i,
1144 Str("JACK MIDI client name prefix"
1145 " (default: csound6-midi)"),
1146 NULL);
1147
1148 /* input port name */
1149 i = jack_port_name_size() - 3;
1150 if (i > (MAX_NAME_LEN + 1))
1151 i = (MAX_NAME_LEN + 1);
1152 csound->CreateConfigurationVariable(csound, "jack_midi_inportname",
1153 (void*) &(pm->inputPortName[0]),
1154 CSOUNDCFG_STRING, 0, NULL, &i,
1155 Str("JACK MIDI input port name"
1156 "(default: port)"), NULL);
1157 /* output port name */
1158 i = jack_port_name_size() - 3;
1159 if (i > (MAX_NAME_LEN + 1))
1160 i = (MAX_NAME_LEN + 1);
1161 csound->CreateConfigurationVariable(csound, "jack_midi_outportname",
1162 (void*) &(pm->outputPortName[0]),
1163 CSOUNDCFG_STRING, 0, NULL, &i,
1164 Str("JACK MIDI output port name"
1165 " (default: port)"), NULL);
1166
1167 return 0;
1168 }
1169
1170 #define JACK_MIDI_BUFFSIZE 1024
1171 typedef struct jackMidiDevice_ {
1172 jack_client_t *client;
1173 jack_port_t *port;
1174 CSOUND *csound;
1175 void *cb;
1176 } jackMidiDevice;
1177
MidiInProcessCallback(jack_nframes_t nframes,void * userData)1178 int MidiInProcessCallback(jack_nframes_t nframes, void *userData){
1179
1180 jack_midi_event_t event;
1181 jackMidiDevice *dev = (jackMidiDevice *) userData;
1182 CSOUND *csound = dev->csound;
1183 int n = 0;
1184 while(jack_midi_event_get(&event,
1185 jack_port_get_buffer(dev->port,nframes),
1186 n++) == 0) {
1187 if (UNLIKELY(csound->WriteCircularBuffer(csound,dev->cb,
1188 event.buffer,event.size)
1189 != (int) event.size)){
1190 csound->Warning(csound, "%s", Str("Jack MIDI module: buffer overflow"));
1191 return 1;
1192 }
1193 }
1194 return 0;
1195 }
1196
1197
midi_in_open(CSOUND * csound,void ** userData,const char * devName)1198 static int midi_in_open(CSOUND *csound,
1199 void **userData,
1200 const char *devName){
1201
1202 jack_client_t *jack_client;
1203 jack_port_t *jack_port;
1204 jackMidiDevice *dev;
1205 RtJackMIDIGlobals *pm;
1206 char clientName[MAX_NAME_LEN+3];
1207
1208 pm =
1209 (RtJackMIDIGlobals*) csound->QueryGlobalVariableNoCheck(csound,
1210 "_rtjackMIDIGlobals");
1211
1212 sprintf(clientName, "%s_in", pm->clientName);
1213 if (UNLIKELY((jack_client =
1214 jack_client_open(clientName, 0, NULL)) == NULL)){
1215 *userData = NULL;
1216 csound->ErrorMsg(csound, "%s",
1217 Str("Jack MIDI module: failed to create client for input"));
1218 return NOTOK;
1219 }
1220
1221
1222 if (UNLIKELY((jack_port = jack_port_register(jack_client,pm->inputPortName,
1223 JACK_DEFAULT_MIDI_TYPE,
1224 JackPortIsInput|JackPortIsTerminal,
1225 0)) == NULL)){
1226 jack_client_close(jack_client);
1227 *userData = NULL;
1228 csound->ErrorMsg(csound, "%s",
1229 Str("Jack MIDI module: failed to register input port"));
1230 return NOTOK;
1231 }
1232
1233 dev = (jackMidiDevice *) csound->Calloc(csound,sizeof(jackMidiDevice));
1234 dev->client = jack_client;
1235 dev->port = jack_port;
1236 dev->csound = csound;
1237 dev->cb = csound->CreateCircularBuffer(csound,
1238 JACK_MIDI_BUFFSIZE,
1239 sizeof(char));
1240
1241 if (UNLIKELY(jack_set_process_callback(jack_client,
1242 MidiInProcessCallback,
1243 (void*) dev) != 0)){
1244 jack_client_close(jack_client);
1245 csound->DestroyCircularBuffer(csound, dev->cb);
1246 csound->Free(csound, dev);
1247 csound->ErrorMsg(csound,
1248 "%s", Str("Jack MIDI module: failed to set input"
1249 " process callback"));
1250 return NOTOK;
1251 }
1252
1253 if (UNLIKELY(jack_activate(jack_client) != 0)){
1254 jack_client_close(jack_client);
1255 csound->DestroyCircularBuffer(csound, dev->cb);
1256 csound->Free(csound, dev);
1257 *userData = NULL;
1258 csound->ErrorMsg(csound, "%s",
1259 Str("Jack MIDI module: failed to activate input"));
1260 return NOTOK;
1261 }
1262
1263 if (strcmp(devName,"0")){
1264 if (UNLIKELY(jack_connect(jack_client,devName,
1265 jack_port_name(dev->port)) != 0)){
1266 csound->Warning(csound, Str("Jack MIDI module: failed to connect to: %s"),
1267 devName);
1268 }
1269 }
1270
1271 *userData = (void *) dev;
1272 return OK;
1273 }
1274
midi_in_read(CSOUND * csound,void * userData,unsigned char * buf,int nbytes)1275 static int midi_in_read(CSOUND *csound,
1276 void *userData, unsigned char *buf, int nbytes)
1277 {
1278 jackMidiDevice *dev = (jackMidiDevice *) userData;
1279 return csound->ReadCircularBuffer(csound,dev->cb,buf,nbytes);
1280 }
1281
midi_in_close(CSOUND * csound,void * userData)1282 static int midi_in_close(CSOUND *csound, void *userData){
1283 jackMidiDevice *dev = (jackMidiDevice *) userData;
1284 if(dev != NULL) {
1285 jack_port_disconnect(dev->client, dev->port);
1286 jack_client_close(dev->client);
1287 csound->DestroyCircularBuffer(csound, dev->cb);
1288 csound->Free(csound, dev);
1289 }
1290 return OK;
1291 }
1292
MidiOutProcessCallback(jack_nframes_t nframes,void * userData)1293 int MidiOutProcessCallback(jack_nframes_t nframes, void *userData){
1294
1295 jackMidiDevice *dev = (jackMidiDevice *) userData;
1296 CSOUND *csound = dev->csound;
1297 jack_midi_data_t buf[JACK_MIDI_BUFFSIZE];
1298 int n;
1299 jack_midi_clear_buffer(jack_port_get_buffer(dev->port,nframes));
1300 while((n = csound->ReadCircularBuffer(csound,dev->cb,
1301 buf,
1302 JACK_MIDI_BUFFSIZE)) != 0) {
1303 if(UNLIKELY(jack_midi_event_write(jack_port_get_buffer(dev->port,nframes),
1304 0, buf,n) != 0)){
1305 csound->Warning(csound, "%s", Str("Jack MIDI module: out buffer overflow"));
1306 return 1;
1307 }
1308 }
1309 return 0;
1310 }
1311
1312
midi_out_open(CSOUND * csound,void ** userData,const char * devName)1313 static int midi_out_open(CSOUND *csound, void **userData,
1314 const char *devName)
1315 {
1316 jack_client_t *jack_client;
1317 jack_port_t *jack_port;
1318 jackMidiDevice *dev;
1319 RtJackMIDIGlobals *pm;
1320 char clientName[MAX_NAME_LEN+4];
1321
1322 pm =
1323 (RtJackMIDIGlobals*) csound->QueryGlobalVariableNoCheck(csound,
1324 "_rtjackMIDIGlobals");
1325 sprintf(clientName, "%s_out", pm->clientName);
1326 if(UNLIKELY((jack_client =
1327 jack_client_open(clientName, 0, NULL)) == NULL)){
1328 *userData = NULL;
1329 csound->ErrorMsg(csound, "%s",
1330 Str("Jack MIDI module: failed to create client for output"));
1331 return NOTOK;
1332 }
1333
1334
1335 if(UNLIKELY((jack_port = jack_port_register(jack_client,pm->outputPortName,
1336 JACK_DEFAULT_MIDI_TYPE,
1337 JackPortIsOutput,
1338 0)) == NULL)){
1339 jack_client_close(jack_client);
1340 *userData = NULL;
1341 csound->ErrorMsg(csound, "%s",
1342 Str("Jack MIDI module: failed to register output port"));
1343 return NOTOK;
1344 }
1345
1346 dev = (jackMidiDevice *) csound->Calloc(csound,sizeof(jackMidiDevice));
1347 dev->client = jack_client;
1348 dev->port = jack_port;
1349 dev->csound = csound;
1350 dev->cb = csound->CreateCircularBuffer(csound,
1351 JACK_MIDI_BUFFSIZE,
1352 sizeof(char));
1353
1354 if(UNLIKELY(jack_set_process_callback(jack_client,
1355 MidiOutProcessCallback,
1356 (void*) dev) != 0)){
1357 jack_client_close(jack_client);
1358 csound->DestroyCircularBuffer(csound, dev->cb);
1359 csound->Free(csound, dev);
1360 csound->ErrorMsg(csound,
1361 "%s", Str("Jack MIDI module: failed to set input"
1362 " process callback"));
1363 return NOTOK;
1364 }
1365
1366 if(UNLIKELY(jack_activate(jack_client) != 0)){
1367 jack_client_close(jack_client);
1368 csound->DestroyCircularBuffer(csound, dev->cb);
1369 csound->Free(csound, dev);
1370 *userData = NULL;
1371 csound->ErrorMsg(csound, "%s",
1372 Str("Jack MIDI module: failed to activate output"));
1373 return NOTOK;
1374 }
1375
1376 if(strcmp(devName,"0")){
1377 if(UNLIKELY(jack_connect(jack_client,
1378 jack_port_name(dev->port),devName) != 0)){
1379 csound->Warning(csound,
1380 Str("Jack MIDI out module: failed to connect to: %s"),
1381 devName);
1382 }
1383 }
1384
1385 *userData = (void *) dev;
1386 return OK;
1387 }
1388
midi_out_write(CSOUND * csound,void * userData,const unsigned char * buf,int nbytes)1389 static int midi_out_write(CSOUND *csound,
1390 void *userData, const unsigned char *buf, int nbytes)
1391 {
1392 jackMidiDevice *dev = (jackMidiDevice *) userData;
1393 return csound->WriteCircularBuffer(csound,dev->cb,buf,nbytes);
1394 }
1395
midi_out_close(CSOUND * csound,void * userData)1396 static int midi_out_close(CSOUND *csound, void *userData){
1397 jackMidiDevice *dev = (jackMidiDevice *) userData;
1398 if(dev != NULL) {
1399 jack_port_disconnect(dev->client, dev->port);
1400 jack_client_close(dev->client);
1401 csound->DestroyCircularBuffer(csound, dev->cb);
1402 csound->Free(csound, dev);
1403 }
1404 return OK;
1405 }
1406
listDevicesM(CSOUND * csound,CS_MIDIDEVICE * list,int isOutput)1407 static int listDevicesM(CSOUND *csound, CS_MIDIDEVICE *list,
1408 int isOutput){
1409 char **portNames = (char**) NULL, port[64];
1410 unsigned long portFlags;
1411 int i, n, cnt=0;
1412 jack_client_t *jackClient;
1413 RtJackGlobals* p =
1414 (RtJackGlobals*) csound->QueryGlobalVariableNoCheck(csound,
1415 "_rtjackGlobals");
1416 char *drv = (char*) (csound->QueryGlobalVariable(csound, "_RTMIDI"));
1417
1418 if(p->listclient == NULL)
1419 p->listclient = jack_client_open("list", JackNoStartServer, NULL);
1420
1421 jackClient = p->listclient;
1422
1423 if(jackClient == NULL) return 0;
1424 portFlags = (isOutput ? (unsigned long) JackPortIsInput
1425 : (unsigned long) JackPortIsOutput);
1426
1427 portNames = (char**) jack_get_ports(jackClient,
1428 (char*) NULL,
1429 JACK_DEFAULT_MIDI_TYPE,
1430 portFlags);
1431 if(portNames == NULL) {
1432 jack_client_close(jackClient);
1433 p->listclient = NULL;
1434 return 0;
1435 }
1436
1437 memset(port, '\0', 64);
1438 for(i=0; portNames[i] != NULL; i++, cnt++) {
1439 n = (int) strlen(portNames[i]);
1440 strNcpy(port, portNames[i], n+1);
1441 //port[n] = '\0';
1442 if (list != NULL) {
1443 strNcpy(list[cnt].device_name, port, 64);
1444 snprintf(list[cnt].device_id, 63, "%d", cnt);
1445 list[cnt].isOutput = isOutput;
1446 strcpy(list[i].interface_name, "");
1447 strNcpy(list[i].midi_module, drv, 64);
1448 }
1449 }
1450 jack_free(portNames);
1451 jack_client_close(jackClient);
1452 p->listclient = NULL;
1453 return cnt;
1454 return 0;
1455 }
1456
1457 /*
1458 static int listDevices(CSOUND *csound, CS_MIDIDEVICE *list, int isOutput){
1459 int i, cnt;
1460 PmDeviceInfo *info;
1461 char tmp[64];
1462 char *drv = (char*) (csound->QueryGlobalVariable(csound, "_RTMIDI"));
1463
1464 if (UNLIKELY(start_portmidi(csound) != 0))
1465 return 0;
1466
1467 cnt = portMidi_getDeviceCount(isOutput);
1468 if (list == NULL) return cnt;
1469 for (i = 0; i < cnt; i++) {
1470 info = portMidi_getDeviceInfo(i, isOutput);
1471 if(info->name != NULL)
1472 strNcpy(list[i].device_name, info->name, 64);
1473 snprintf(tmp, 64, "%d", i);
1474 strNcpy(list[i].device_id, tmp, 64);
1475 list[i].isOutput = isOutput;
1476 if (info->interf != NULL)
1477 strNcpy(list[i].interface_name, info->interf, 64);
1478 else strcpy(list[i].interface_name, "");
1479 strNcpy(list[i].midi_module, drv, 64);
1480 }
1481 return cnt;
1482 }
1483 */
1484
1485
csoundModuleDestroy(CSOUND * csound)1486 PUBLIC int csoundModuleDestroy(CSOUND *csound)
1487 {
1488 RtJackGlobals* p =
1489 (RtJackGlobals*) csound->QueryGlobalVariableNoCheck(csound,
1490 "_rtjackGlobals");
1491 if(p && p->listclient) {
1492 jack_client_close(p->listclient);
1493 p->listclient = NULL;
1494 }
1495 return OK;
1496 }
1497
1498
1499
csoundModuleInit(CSOUND * csound)1500 PUBLIC int csoundModuleInit(CSOUND *csound)
1501 {
1502 char *drv;
1503 csound->module_list_add(csound,"jack", "audio");
1504 drv = (char*) csound->QueryGlobalVariable(csound, "_RTAUDIO");
1505 if (drv == NULL)
1506 return 0;
1507 if (!(strcmp(drv, "jack") == 0 || strcmp(drv, "Jack") == 0 ||
1508 strcmp(drv, "JACK") == 0))
1509 return 0;
1510 csound->Message(csound, "%s", Str("rtaudio: JACK module enabled\n"));
1511 {
1512 /* register Csound interface functions */
1513 csound->SetPlayopenCallback(csound, playopen_);
1514 csound->SetRecopenCallback(csound, recopen_);
1515 csound->SetRtplayCallback(csound, rtplay_);
1516 csound->SetRtrecordCallback(csound, rtrecord_);
1517 csound->SetRtcloseCallback(csound, rtclose_);
1518 csound->SetAudioDeviceListCallback(csound, listDevices);
1519 }
1520
1521 drv = (char*) csound->QueryGlobalVariable(csound, "_RTMIDI");
1522 if (drv == NULL)
1523 return 0;
1524 if (!(strcmp(drv, "jack") == 0 || strcmp(drv, "Jack") == 0 ||
1525 strcmp(drv, "JACK") == 0))
1526 return 0;
1527
1528 csound->Message(csound, "%s", Str("rtmidi: JACK module enabled\n"));
1529 {
1530 csound->SetExternalMidiInOpenCallback(csound, midi_in_open);
1531 csound->SetExternalMidiReadCallback(csound, midi_in_read);
1532 csound->SetExternalMidiInCloseCallback(csound, midi_in_close);
1533 csound->SetExternalMidiOutOpenCallback(csound, midi_out_open);
1534 csound->SetExternalMidiWriteCallback(csound, midi_out_write);
1535 csound->SetExternalMidiOutCloseCallback(csound, midi_out_close);
1536 csound->SetMIDIDeviceListCallback(csound,listDevicesM);
1537 }
1538
1539 return 0;
1540 }
1541
csoundModuleInfo(void)1542 PUBLIC int csoundModuleInfo(void)
1543 {
1544 return ((CS_APIVERSION << 16) + (CS_APISUBVER << 8) + (int) sizeof(MYFLT));
1545 }
1546