1 /*
2  Copyright (C) 2014 Cédric Schieli
3 
4  This program is free software; you can redistribute it and/or
5  modify it under the terms of the GNU General Public License
6  as published by the Free Software Foundation; either version 2
7  of the License, or (at your option) any later version.
8 
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  GNU General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with this program; if not, write to the Free Software
16  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17 
18 */
19 
20 #include "JackCompilerDeps.h"
21 #include "driver_interface.h"
22 #include "JackEngineControl.h"
23 #include "JackLockedEngine.h"
24 #include "JackWaitCallbackDriver.h"
25 #include "JackProxyDriver.h"
26 
27 using namespace std;
28 
29 namespace Jack
30 {
JackProxyDriver(const char * name,const char * alias,JackLockedEngine * engine,JackSynchro * table,const char * upstream,const char * promiscuous,char * client_name,bool auto_connect,bool auto_save)31     JackProxyDriver::JackProxyDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table,
32                                 const char* upstream, const char* promiscuous,
33                                 char* client_name, bool auto_connect, bool auto_save)
34             : JackRestarterDriver(name, alias, engine, table)
35     {
36         jack_log("JackProxyDriver::JackProxyDriver upstream %s", upstream);
37 
38         assert(strlen(upstream) < JACK_CLIENT_NAME_SIZE);
39         strcpy(fUpstream, upstream);
40 
41         assert(strlen(client_name) < JACK_CLIENT_NAME_SIZE);
42         strcpy(fClientName, client_name);
43 
44         if (promiscuous) {
45             fPromiscuous = strdup(promiscuous);
46         }
47 
48         fAutoConnect = auto_connect;
49         fAutoSave = auto_save;
50     }
51 
~JackProxyDriver()52     JackProxyDriver::~JackProxyDriver()
53     {
54         if (fHandle) {
55             UnloadJackModule(fHandle);
56         }
57     }
58 
LoadClientLib()59     int JackProxyDriver::LoadClientLib()
60     {
61         // Already loaded
62         if (fHandle) {
63             return 0;
64         }
65         fHandle = LoadJackModule(JACK_PROXY_CLIENT_LIB);
66         if (!fHandle) {
67             return -1;
68         }
69         LoadSymbols();
70         return 0;
71     }
72 
73 //open, close, attach and detach------------------------------------------------------
74 
Open(jack_nframes_t buffer_size,jack_nframes_t samplerate,bool capturing,bool playing,int inchannels,int outchannels,bool monitor,const char * capture_driver_name,const char * playback_driver_name,jack_nframes_t capture_latency,jack_nframes_t playback_latency)75     int JackProxyDriver::Open(jack_nframes_t buffer_size,
76                          jack_nframes_t samplerate,
77                          bool capturing,
78                          bool playing,
79                          int inchannels,
80                          int outchannels,
81                          bool monitor,
82                          const char* capture_driver_name,
83                          const char* playback_driver_name,
84                          jack_nframes_t capture_latency,
85                          jack_nframes_t playback_latency)
86     {
87         fDetectPlaybackChannels = (outchannels == -1);
88         fDetectCaptureChannels = (inchannels == -1);
89 
90         if (LoadClientLib() != 0) {
91             jack_error("Cannot dynamically load client library !");
92             return -1;
93         }
94 
95         return JackWaiterDriver::Open(buffer_size, samplerate,
96                                     capturing, playing,
97                                     inchannels, outchannels,
98                                     monitor,
99                                     capture_driver_name, playback_driver_name,
100                                     capture_latency, playback_latency);
101     }
102 
Close()103     int JackProxyDriver::Close()
104     {
105         FreePorts();
106         return JackWaiterDriver::Close();
107     }
108 
109     // Attach and Detach are defined as empty methods: port allocation is done when driver actually start (that is in Init)
Attach()110     int JackProxyDriver::Attach()
111     {
112         return 0;
113     }
114 
Detach()115     int JackProxyDriver::Detach()
116     {
117         return 0;
118     }
119 
120 //init and restart--------------------------------------------------------------------
121 
122     /*
123         JackProxyDriver is wrapped in a JackWaitCallbackDriver decorator that behaves
124         as a "dummy driver, until Initialize method returns.
125     */
Initialize()126     bool JackProxyDriver::Initialize()
127     {
128         jack_log("JackProxyDriver::Initialize");
129 
130         // save existing local connections if needed
131         if (fAutoSave) {
132             SaveConnections(0);
133         }
134 
135         // new loading, but existing client, restart the driver
136         if (fClient) {
137             jack_info("JackProxyDriver restarting...");
138             jack_client_close(fClient);
139         }
140         FreePorts();
141 
142         // display some additional infos
143         jack_info("JackProxyDriver started in %s mode.",
144                     (fEngineControl->fSyncMode) ? "sync" : "async");
145 
146         do {
147             jack_status_t status;
148             char *old = NULL;
149 
150             if (fPromiscuous) {
151                 // as we are fiddling with the environment variable content, save it
152                 const char* tmp = getenv("JACK_PROMISCUOUS_SERVER");
153                 if (tmp) {
154                     old = strdup(tmp);
155                 }
156                 // temporary enable promiscuous mode
157                 if (setenv("JACK_PROMISCUOUS_SERVER", fPromiscuous, 1) < 0) {
158                     free(old);
159                     jack_error("Error allocating memory.");
160                     return false;
161                 }
162             }
163 
164             jack_info("JackProxyDriver connecting to %s", fUpstream);
165             fClient = jack_client_open(fClientName, static_cast<jack_options_t>(JackNoStartServer|JackServerName), &status, fUpstream);
166 
167             if (fPromiscuous) {
168                 // restore previous environment variable content
169                 if (old) {
170                     if (setenv("JACK_PROMISCUOUS_SERVER", old, 1) < 0) {
171                         free(old);
172                         jack_error("Error allocating memory.");
173                         return false;
174                     }
175                     free(old);
176                 } else {
177                     unsetenv("JACK_PROMISCUOUS_SERVER");
178                 }
179             }
180 
181             // the connection failed, try again later
182             if (!fClient) {
183                 JackSleep(1000000);
184             }
185 
186         } while (!fClient);
187         jack_info("JackProxyDriver connected to %s", fUpstream);
188 
189         // we are connected, let's register some callbacks
190 
191         jack_on_shutdown(fClient, shutdown_callback, this);
192 
193         if (jack_set_process_callback(fClient, process_callback, this) != 0) {
194             jack_error("Cannot set process callback.");
195             return false;
196         }
197 
198         if (jack_set_buffer_size_callback(fClient, bufsize_callback, this) != 0) {
199             jack_error("Cannot set buffer size callback.");
200             return false;
201         }
202 
203         if (jack_set_sample_rate_callback(fClient, srate_callback, this) != 0) {
204             jack_error("Cannot set sample rate callback.");
205             return false;
206         }
207 
208         if (jack_set_port_connect_callback(fClient, connect_callback, this) != 0) {
209             jack_error("Cannot set port connect callback.");
210             return false;
211         }
212 
213         // detect upstream physical playback ports if needed
214         if (fDetectPlaybackChannels) {
215             fPlaybackChannels = CountIO(JACK_DEFAULT_AUDIO_TYPE, JackPortIsPhysical | JackPortIsOutput);
216         }
217 
218         // detect upstream physical capture ports if needed
219         if (fDetectCaptureChannels) {
220             fCaptureChannels = CountIO(JACK_DEFAULT_AUDIO_TYPE, JackPortIsPhysical | JackPortIsInput);
221         }
222 
223         if (AllocPorts() != 0) {
224             jack_error("Can't allocate ports.");
225             return false;
226         }
227 
228         bufsize_callback(jack_get_buffer_size(fClient));
229         srate_callback(jack_get_sample_rate(fClient));
230 
231         // restore local connections if needed
232         if (fAutoSave) {
233             LoadConnections(0);
234         }
235 
236         // everything is ready, start upstream processing
237         if (jack_activate(fClient) != 0) {
238             jack_error("Cannot activate jack client.");
239             return false;
240         }
241 
242         // connect upstream ports if needed
243         if (fAutoConnect) {
244             ConnectPorts();
245         }
246 
247         return true;
248     }
249 
Stop()250     int JackProxyDriver::Stop()
251     {
252         if (fClient && (jack_deactivate(fClient) != 0)) {
253             jack_error("Cannot deactivate jack client.");
254             return -1;
255         }
256         return 0;
257     }
258 
259 //client callbacks---------------------------------------------------------------------------
260 
process_callback(jack_nframes_t nframes,void * arg)261     int JackProxyDriver::process_callback(jack_nframes_t nframes, void* arg)
262     {
263         assert(static_cast<JackProxyDriver*>(arg));
264         return static_cast<JackProxyDriver*>(arg)->Process();
265     }
266 
bufsize_callback(jack_nframes_t nframes,void * arg)267     int JackProxyDriver::bufsize_callback(jack_nframes_t nframes, void* arg)
268     {
269         assert(static_cast<JackProxyDriver*>(arg));
270         return static_cast<JackProxyDriver*>(arg)->bufsize_callback(nframes);
271     }
bufsize_callback(jack_nframes_t nframes)272     int JackProxyDriver::bufsize_callback(jack_nframes_t nframes)
273     {
274         if (JackTimedDriver::SetBufferSize(nframes) == 0) {
275             return -1;
276         }
277         JackDriver::NotifyBufferSize(nframes);
278         return 0;
279     }
280 
srate_callback(jack_nframes_t nframes,void * arg)281     int JackProxyDriver::srate_callback(jack_nframes_t nframes, void* arg)
282     {
283         assert(static_cast<JackProxyDriver*>(arg));
284         return static_cast<JackProxyDriver*>(arg)->srate_callback(nframes);
285     }
srate_callback(jack_nframes_t nframes)286     int JackProxyDriver::srate_callback(jack_nframes_t nframes)
287     {
288         if (JackTimedDriver::SetSampleRate(nframes) == 0) {
289             return -1;
290         }
291         JackDriver::NotifySampleRate(nframes);
292         return 0;
293     }
294 
connect_callback(jack_port_id_t a,jack_port_id_t b,int connect,void * arg)295     void JackProxyDriver::connect_callback(jack_port_id_t a, jack_port_id_t b, int connect, void* arg)
296     {
297         assert(static_cast<JackProxyDriver*>(arg));
298         static_cast<JackProxyDriver*>(arg)->connect_callback(a, b, connect);
299     }
connect_callback(jack_port_id_t a,jack_port_id_t b,int connect)300     void JackProxyDriver::connect_callback(jack_port_id_t a, jack_port_id_t b, int connect)
301     {
302         jack_port_t* port;
303         int i;
304 
305         // skip port if not our own
306         port = jack_port_by_id(fClient, a);
307         if (!jack_port_is_mine(fClient, port)) {
308             port = jack_port_by_id(fClient, b);
309             if (!jack_port_is_mine(fClient, port)) {
310                 return;
311             }
312         }
313 
314         for (i = 0; i < fCaptureChannels; i++) {
315             if (fUpstreamPlaybackPorts[i] == port) {
316                 fUpstreamPlaybackPortConnected[i] = connect;
317             }
318         }
319 
320         for (i = 0; i < fPlaybackChannels; i++) {
321             if (fUpstreamCapturePorts[i] == port) {
322                 fUpstreamCapturePortConnected[i] = connect;
323             }
324         }
325     }
326 
shutdown_callback(void * arg)327     void JackProxyDriver::shutdown_callback(void* arg)
328     {
329         assert(static_cast<JackProxyDriver*>(arg));
330         static_cast<JackProxyDriver*>(arg)->RestartWait();
331     }
332 
333 //jack ports and buffers--------------------------------------------------------------
334 
CountIO(const char * type,int flags)335     int JackProxyDriver::CountIO(const char* type, int flags)
336     {
337         int count = 0;
338         const char** ports = jack_get_ports(fClient, NULL, type, flags);
339         if (ports != NULL) {
340             while (ports[count]) { count++; }
341             jack_free(ports);
342         }
343         return count;
344     }
345 
AllocPorts()346     int JackProxyDriver::AllocPorts()
347     {
348         jack_log("JackProxyDriver::AllocPorts fBufferSize = %ld fSampleRate = %ld", fEngineControl->fBufferSize, fEngineControl->fSampleRate);
349 
350         char proxy[REAL_JACK_PORT_NAME_SIZE];
351         int i;
352 
353         fUpstreamPlaybackPorts = new jack_port_t* [fCaptureChannels];
354         fUpstreamPlaybackPortConnected = new int [fCaptureChannels];
355         for (i = 0; i < fCaptureChannels; i++) {
356             snprintf(proxy, sizeof(proxy), "%s:to_client_%d", fClientName, i + 1);
357             fUpstreamPlaybackPorts[i] = jack_port_register(fClient, proxy, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput | JackPortIsTerminal, 0);
358             if (fUpstreamPlaybackPorts[i] == NULL) {
359                 jack_error("driver: cannot register upstream port %s", proxy);
360                 return -1;
361             }
362             fUpstreamPlaybackPortConnected[i] = 0;
363         }
364 
365         fUpstreamCapturePorts = new jack_port_t* [fPlaybackChannels];
366         fUpstreamCapturePortConnected = new int [fPlaybackChannels];
367         for (i = 0; i < fPlaybackChannels; i++) {
368             snprintf(proxy, sizeof(proxy), "%s:from_client_%d", fClientName, i + 1);
369             fUpstreamCapturePorts[i] = jack_port_register(fClient, proxy, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput | JackPortIsTerminal, 0);
370             if (fUpstreamCapturePorts[i] == NULL) {
371                 jack_error("driver: cannot register upstream port %s", proxy);
372                 return -1;
373             }
374             fUpstreamCapturePortConnected[i] = 0;
375         }
376 
377         // local ports are registered here
378         return JackAudioDriver::Attach();
379     }
380 
FreePorts()381     int JackProxyDriver::FreePorts()
382     {
383         jack_log("JackProxyDriver::FreePorts");
384 
385         int i;
386 
387         for (i = 0; i < fCaptureChannels; i++) {
388             if (fCapturePortList[i] > 0) {
389                 fEngine->PortUnRegister(fClientControl.fRefNum, fCapturePortList[i]);
390                 fCapturePortList[i] = 0;
391             }
392             if (fUpstreamPlaybackPorts && fUpstreamPlaybackPorts[i]) {
393                 fUpstreamPlaybackPorts[i] = NULL;
394             }
395         }
396 
397         for (i = 0; i < fPlaybackChannels; i++) {
398             if (fPlaybackPortList[i] > 0) {
399                 fEngine->PortUnRegister(fClientControl.fRefNum, fPlaybackPortList[i]);
400                 fPlaybackPortList[i] = 0;
401             }
402             if (fUpstreamCapturePorts && fUpstreamCapturePorts[i]) {
403                 fUpstreamCapturePorts[i] = NULL;
404             }
405         }
406 
407         delete[] fUpstreamPlaybackPorts;
408         delete[] fUpstreamPlaybackPortConnected;
409         delete[] fUpstreamCapturePorts;
410         delete[] fUpstreamCapturePortConnected;
411 
412         fUpstreamPlaybackPorts = NULL;
413         fUpstreamPlaybackPortConnected = NULL;
414         fUpstreamCapturePorts = NULL;
415         fUpstreamCapturePortConnected = NULL;
416 
417         return 0;
418     }
419 
ConnectPorts()420     void JackProxyDriver::ConnectPorts()
421     {
422         jack_log("JackProxyDriver::ConnectPorts");
423         const char** ports = jack_get_ports(fClient, NULL, JACK_DEFAULT_AUDIO_TYPE, JackPortIsPhysical | JackPortIsOutput);
424         if (ports != NULL) {
425             for (int i = 0; i < fCaptureChannels && ports[i]; i++) {
426                 jack_connect(fClient, ports[i], jack_port_name(fUpstreamPlaybackPorts[i]));
427             }
428             jack_free(ports);
429         }
430 
431         ports = jack_get_ports(fClient, NULL, JACK_DEFAULT_AUDIO_TYPE, JackPortIsPhysical | JackPortIsInput);
432         if (ports != NULL) {
433             for (int i = 0; i < fPlaybackChannels && ports[i]; i++) {
434                 jack_connect(fClient, jack_port_name(fUpstreamCapturePorts[i]), ports[i]);
435             }
436             jack_free(ports);
437         }
438     }
439 
440 //driver processes--------------------------------------------------------------------
441 
Read()442     int JackProxyDriver::Read()
443     {
444         // take the time at the beginning of the cycle
445         JackDriver::CycleTakeBeginTime();
446 
447         int i;
448         void *from, *to;
449         size_t buflen = sizeof(jack_default_audio_sample_t) * fEngineControl->fBufferSize;
450 
451         for (i = 0; i < fCaptureChannels; i++) {
452             if (fUpstreamPlaybackPortConnected[i]) {
453                 from = jack_port_get_buffer(fUpstreamPlaybackPorts[i], fEngineControl->fBufferSize);
454                 to = GetInputBuffer(i);
455                 memcpy(to, from, buflen);
456             }
457         }
458 
459         return 0;
460     }
461 
Write()462     int JackProxyDriver::Write()
463     {
464         int i;
465         void *from, *to;
466         size_t buflen = sizeof(jack_default_audio_sample_t) * fEngineControl->fBufferSize;
467 
468         for (i = 0; i < fPlaybackChannels; i++) {
469             if (fUpstreamCapturePortConnected[i]) {
470                 to = jack_port_get_buffer(fUpstreamCapturePorts[i], fEngineControl->fBufferSize);
471                 from = GetOutputBuffer(i);
472                 memcpy(to, from, buflen);
473             }
474         }
475 
476         return 0;
477     }
478 
479 //driver loader-----------------------------------------------------------------------
480 
481 #ifdef __cplusplus
482     extern "C"
483     {
484 #endif
485 
driver_get_descriptor()486         SERVER_EXPORT jack_driver_desc_t* driver_get_descriptor()
487         {
488             jack_driver_desc_t * desc;
489             jack_driver_desc_filler_t filler;
490             jack_driver_param_value_t value;
491 
492             desc = jack_driver_descriptor_construct("proxy", JackDriverMaster, "proxy backend", &filler);
493 
494             strcpy(value.str, DEFAULT_UPSTREAM);
495             jack_driver_descriptor_add_parameter(desc, &filler, "upstream", 'u', JackDriverParamString, &value, NULL, "Name of the upstream jack server", NULL);
496 
497             strcpy(value.str, "");
498             jack_driver_descriptor_add_parameter(desc, &filler, "promiscuous", 'p', JackDriverParamString, &value, NULL, "Promiscuous group", NULL);
499 
500             value.i = -1;
501             jack_driver_descriptor_add_parameter(desc, &filler, "input-ports", 'C', JackDriverParamInt, &value, NULL, "Number of audio input ports", "Number of audio input ports. If -1, audio physical input from the master");
502             jack_driver_descriptor_add_parameter(desc, &filler, "output-ports", 'P', JackDriverParamInt, &value, NULL, "Number of audio output ports", "Number of audio output ports. If -1, audio physical output from the master");
503 
504             strcpy(value.str, "proxy");
505             jack_driver_descriptor_add_parameter(desc, &filler, "client-name", 'n', JackDriverParamString, &value, NULL, "Name of the jack client", NULL);
506 
507             value.i = false;
508             jack_driver_descriptor_add_parameter(desc, &filler, "use-username", 'U', JackDriverParamBool, &value, NULL, "Use current username as client name", NULL);
509 
510             value.i = false;
511             jack_driver_descriptor_add_parameter(desc, &filler, "auto-connect", 'c', JackDriverParamBool, &value, NULL, "Auto connect proxy to upstream system ports", NULL);
512 
513             value.i = false;
514             jack_driver_descriptor_add_parameter(desc, &filler, "auto-save", 's', JackDriverParamBool, &value, NULL, "Save/restore connection state when restarting", NULL);
515 
516             return desc;
517         }
518 
driver_initialize(Jack::JackLockedEngine * engine,Jack::JackSynchro * table,const JSList * params)519         SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLockedEngine* engine, Jack::JackSynchro* table, const JSList* params)
520         {
521             char upstream[JACK_CLIENT_NAME_SIZE + 1];
522             char promiscuous[JACK_CLIENT_NAME_SIZE + 1] = {0};
523             char client_name[JACK_CLIENT_NAME_SIZE + 1];
524             jack_nframes_t period_size = 1024;  // to be used while waiting for master period_size
525             jack_nframes_t sample_rate = 48000; // to be used while waiting for master sample_rate
526             int capture_ports = -1;
527             int playback_ports = -1;
528             const JSList* node;
529             const jack_driver_param_t* param;
530             bool auto_connect = false;
531             bool auto_save = false;
532             bool use_promiscuous = false;
533 
534             // Possibly use env variable for upstream name
535             const char* default_upstream = getenv("JACK_PROXY_UPSTREAM");
536             strcpy(upstream, (default_upstream) ? default_upstream : DEFAULT_UPSTREAM);
537 
538             // Possibly use env variable for upstream promiscuous
539             const char* default_promiscuous = getenv("JACK_PROXY_PROMISCUOUS");
540             strcpy(promiscuous, (default_promiscuous) ? default_promiscuous : "");
541 
542             // Possibly use env variable for client name
543             const char* default_client_name = getenv("JACK_PROXY_CLIENT_NAME");
544             strcpy(client_name, (default_client_name) ? default_client_name : DEFAULT_CLIENT_NAME);
545 
546 #ifdef WIN32
547             const char* username = getenv("USERNAME");
548 #else
549             const char* username = getenv("LOGNAME");
550 #endif
551 
552             for (node = params; node; node = jack_slist_next(node)) {
553                 param = (const jack_driver_param_t*) node->data;
554                 switch (param->character)
555                 {
556                     case 'u' :
557                         assert(strlen(param->value.str) < JACK_CLIENT_NAME_SIZE);
558                         strcpy(upstream, param->value.str);
559                         break;
560                     case 'p':
561                         assert(strlen(param->value.str) < JACK_CLIENT_NAME_SIZE);
562                         use_promiscuous = true;
563                         strcpy(promiscuous, param->value.str);
564                         break;
565                     case 'C':
566                         capture_ports = param->value.i;
567                         break;
568                     case 'P':
569                         playback_ports = param->value.i;
570                         break;
571                     case 'n' :
572                         assert(strlen(param->value.str) < JACK_CLIENT_NAME_SIZE);
573                         strncpy(client_name, param->value.str, JACK_CLIENT_NAME_SIZE);
574                         break;
575                     case 'U' :
576                         if (username && *username) {
577                             assert(strlen(username) < JACK_CLIENT_NAME_SIZE);
578                             strncpy(client_name, username, JACK_CLIENT_NAME_SIZE);
579                         }
580                     case 'c':
581                         auto_connect = true;
582                         break;
583                     case 's':
584                         auto_save = true;
585                         break;
586                 }
587             }
588 
589             try {
590 
591                 Jack::JackDriverClientInterface* driver = new Jack::JackWaitCallbackDriver(
592                         new Jack::JackProxyDriver("system", "proxy_pcm", engine, table, upstream, use_promiscuous ? promiscuous : NULL, client_name, auto_connect, auto_save));
593                 if (driver->Open(period_size, sample_rate, 1, 1, capture_ports, playback_ports, false, "capture_", "playback_", 0, 0) == 0) {
594                     return driver;
595                 } else {
596                     delete driver;
597                     return NULL;
598                 }
599 
600             } catch (...) {
601                 return NULL;
602             }
603         }
604 
605 #ifdef __cplusplus
606     }
607 #endif
608 }
609