Jack2  1.9.12
JackProxyDriver.cpp
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 {
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 
52  JackProxyDriver::~JackProxyDriver()
53  {
54  if (fHandle) {
55  UnloadJackModule(fHandle);
56  }
57  }
58 
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 
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 
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)
110  int JackProxyDriver::Attach()
111  {
112  return 0;
113  }
114 
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  */
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 
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 
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 
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  }
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 
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  }
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 
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  }
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 
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 
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 
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 
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 
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 
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 
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 
486  SERVER_EXPORT jack_driver_desc_t* driver_get_descriptor()
487  {
488  jack_driver_desc_t * desc;
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 
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 
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 }