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