Jack2
1.9.8
|
00001 /* 00002 Copyright (C) 2001 Paul Davis 00003 Copyright (C) 2004-2008 Grame 00004 00005 This program is free software; you can redistribute it and/or modify 00006 it under the terms of the GNU General Public License as published by 00007 the Free Software Foundation; either version 2 of the License, or 00008 (at your option) any later version. 00009 00010 This program is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 GNU General Public License for more details. 00014 00015 You should have received a copy of the GNU General Public License 00016 along with this program; if not, write to the Free Software 00017 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 00018 00019 */ 00020 00021 #include <iostream> 00022 #include <assert.h> 00023 #include <cassert> 00024 #include <csignal> 00025 #include <sys/types.h> 00026 #include <getopt.h> 00027 #include <cstring> 00028 #include <cstdio> 00029 #include <list> 00030 00031 #include "types.h" 00032 #include "jack.h" 00033 #include "JackConstants.h" 00034 #include "JackDriverLoader.h" 00035 00036 #if defined(JACK_DBUS) && defined(__linux__) 00037 #include <dbus/dbus.h> 00038 #include "audio_reserve.h" 00039 #endif 00040 00041 /* 00042 This is a simple port of the old jackdmp.cpp file to use the new Jack 2.0 control API. Available options for the server 00043 are "hard-coded" in the source. A much better approach would be to use the control API to: 00044 - dynamically retrieve available server parameters and then prepare to parse them 00045 - get available drivers and their possible parameters, then prepare to parse them. 00046 */ 00047 00048 #ifdef __APPLE__ 00049 #include <CoreFoundation/CFNotificationCenter.h> 00050 #include <CoreFoundation/CoreFoundation.h> 00051 00052 static void notify_server_start(const char* server_name) 00053 { 00054 // Send notification to be used in the JackRouter plugin 00055 CFStringRef ref = CFStringCreateWithCString(NULL, server_name, kCFStringEncodingMacRoman); 00056 CFNotificationCenterPostNotificationWithOptions(CFNotificationCenterGetDistributedCenter(), 00057 CFSTR("com.grame.jackserver.start"), 00058 ref, 00059 NULL, 00060 kCFNotificationDeliverImmediately | kCFNotificationPostToAllSessions); 00061 CFRelease(ref); 00062 } 00063 00064 static void notify_server_stop(const char* server_name) 00065 { 00066 // Send notification to be used in the JackRouter plugin 00067 CFStringRef ref1 = CFStringCreateWithCString(NULL, server_name, kCFStringEncodingMacRoman); 00068 CFNotificationCenterPostNotificationWithOptions(CFNotificationCenterGetDistributedCenter(), 00069 CFSTR("com.grame.jackserver.stop"), 00070 ref1, 00071 NULL, 00072 kCFNotificationDeliverImmediately | kCFNotificationPostToAllSessions); 00073 CFRelease(ref1); 00074 } 00075 00076 #else 00077 00078 static void notify_server_start(const char* server_name) 00079 {} 00080 static void notify_server_stop(const char* server_name) 00081 {} 00082 00083 #endif 00084 00085 static void copyright(FILE* file) 00086 { 00087 fprintf(file, "jackdmp " VERSION "\n" 00088 "Copyright 2001-2005 Paul Davis and others.\n" 00089 "Copyright 2004-2011 Grame.\n" 00090 "jackdmp comes with ABSOLUTELY NO WARRANTY\n" 00091 "This is free software, and you are welcome to redistribute it\n" 00092 "under certain conditions; see the file COPYING for details\n"); 00093 } 00094 00095 static void usage(FILE* file) 00096 { 00097 fprintf(file, "\n" 00098 "usage: jackdmp [ --no-realtime OR -r ]\n" 00099 " [ --realtime OR -R [ --realtime-priority OR -P priority ] ]\n" 00100 " (the two previous arguments are mutually exclusive. The default is --realtime)\n" 00101 " [ --name OR -n server-name ]\n" 00102 " [ --timeout OR -t client-timeout-in-msecs ]\n" 00103 " [ --loopback OR -L loopback-port-number ]\n" 00104 " [ --port-max OR -p maximum-number-of-ports]\n" 00105 " [ --slave-backend OR -X slave-backend-name ]\n" 00106 " [ --internal-client OR -I internal-client-name ]\n" 00107 " [ --verbose OR -v ]\n" 00108 #ifdef __linux__ 00109 " [ --clocksource OR -c [ c(ycle) | h(pet) | s(ystem) ]\n" 00110 #endif 00111 " [ --replace-registry ]\n" 00112 " [ --silent OR -s ]\n" 00113 " [ --sync OR -S ]\n" 00114 " [ --temporary OR -T ]\n" 00115 " [ --version OR -V ]\n" 00116 " -d master-backend-name [ ... master-backend args ... ]\n" 00117 #ifdef __APPLE__ 00118 " Available master backends may include: coreaudio, dummy, net or netone.\n\n" 00119 #endif 00120 #ifdef WIN32 00121 " Available master backends may include: portaudio, dummy, net or netone.\n\n" 00122 #endif 00123 #ifdef __linux__ 00124 " Available master backends may include: alsa, dummy, freebob, firewire, net or netone.\n\n" 00125 #endif 00126 #if defined(__sun__) || defined(sun) 00127 " Available master backends may include: boomer, oss, dummy or net.\n\n" 00128 #endif 00129 " jackdmp -d master-backend-name --help\n" 00130 " to display options for each master backend\n\n"); 00131 } 00132 00133 // To put in the control.h interface?? 00134 static jackctl_driver_t * 00135 jackctl_server_get_driver( 00136 jackctl_server_t *server, 00137 const char *driver_name) 00138 { 00139 const JSList * node_ptr; 00140 00141 node_ptr = jackctl_server_get_drivers_list(server); 00142 00143 while (node_ptr) 00144 { 00145 if (strcmp(jackctl_driver_get_name((jackctl_driver_t *)node_ptr->data), driver_name) == 0) 00146 { 00147 return (jackctl_driver_t *)node_ptr->data; 00148 } 00149 00150 node_ptr = jack_slist_next(node_ptr); 00151 } 00152 00153 return NULL; 00154 } 00155 00156 static jackctl_internal_t * jackctl_server_get_internal(jackctl_server_t *server, const char *internal_name) 00157 { 00158 const JSList * node_ptr = jackctl_server_get_internals_list(server); 00159 00160 while (node_ptr) { 00161 if (strcmp(jackctl_internal_get_name((jackctl_internal_t *)node_ptr->data), internal_name) == 0) { 00162 return (jackctl_internal_t *)node_ptr->data; 00163 } 00164 node_ptr = jack_slist_next(node_ptr); 00165 } 00166 00167 return NULL; 00168 } 00169 00170 static jackctl_parameter_t * 00171 jackctl_get_parameter( 00172 const JSList * parameters_list, 00173 const char * parameter_name) 00174 { 00175 while (parameters_list) 00176 { 00177 if (strcmp(jackctl_parameter_get_name((jackctl_parameter_t *)parameters_list->data), parameter_name) == 0) 00178 { 00179 return (jackctl_parameter_t *)parameters_list->data; 00180 } 00181 00182 parameters_list = jack_slist_next(parameters_list); 00183 } 00184 00185 return NULL; 00186 } 00187 00188 int main(int argc, char** argv) 00189 { 00190 jackctl_server_t * server_ctl; 00191 const JSList * server_parameters; 00192 const char* server_name = "default"; 00193 jackctl_driver_t * master_driver_ctl; 00194 jackctl_driver_t * loopback_driver_ctl = NULL; 00195 int replace_registry = 0; 00196 00197 const char *options = "-d:X:I:P:uvshVrRL:STFl:t:mn:p:" 00198 #ifdef __linux__ 00199 "c:" 00200 #endif 00201 ; 00202 00203 struct option long_options[] = { 00204 #ifdef __linux__ 00205 { "clock-source", 1, 0, 'c' }, 00206 #endif 00207 { "loopback-driver", 1, 0, 'L' }, 00208 { "audio-driver", 1, 0, 'd' }, 00209 { "midi-driver", 1, 0, 'X' }, 00210 { "internal-client", 1, 0, 'I' }, 00211 { "verbose", 0, 0, 'v' }, 00212 { "help", 0, 0, 'h' }, 00213 { "port-max", 1, 0, 'p' }, 00214 { "no-mlock", 0, 0, 'm' }, 00215 { "name", 1, 0, 'n' }, 00216 { "unlock", 0, 0, 'u' }, 00217 { "realtime", 0, 0, 'R' }, 00218 { "no-realtime", 0, 0, 'r' }, 00219 { "replace-registry", 0, &replace_registry, 0 }, 00220 { "loopback", 0, 0, 'L' }, 00221 { "realtime-priority", 1, 0, 'P' }, 00222 { "timeout", 1, 0, 't' }, 00223 { "temporary", 0, 0, 'T' }, 00224 { "version", 0, 0, 'V' }, 00225 { "silent", 0, 0, 's' }, 00226 { "sync", 0, 0, 'S' }, 00227 { 0, 0, 0, 0 } 00228 }; 00229 00230 int i,opt = 0; 00231 int option_index = 0; 00232 char* master_driver_name = NULL; 00233 char** master_driver_args = NULL; 00234 int master_driver_nargs = 1; 00235 int do_mlock = 1; 00236 int do_unlock = 0; 00237 int loopback = 0; 00238 bool show_version = false; 00239 sigset_t signals; 00240 jackctl_parameter_t* param; 00241 union jackctl_parameter_value value; 00242 00243 std::list<char*> internals_list; 00244 std::list<char*> slaves_list; 00245 std::list<char*>::iterator it; 00246 00247 // Assume that we fail. 00248 int return_value = -1; 00249 bool notify_sent = false; 00250 00251 copyright(stdout); 00252 #if defined(JACK_DBUS) && defined(__linux__) 00253 server_ctl = jackctl_server_create(audio_acquire, audio_release); 00254 #else 00255 server_ctl = jackctl_server_create(NULL, NULL); 00256 #endif 00257 if (server_ctl == NULL) { 00258 fprintf(stderr, "Failed to create server object\n"); 00259 return -1; 00260 } 00261 00262 server_parameters = jackctl_server_get_parameters(server_ctl); 00263 00264 opterr = 0; 00265 while (!master_driver_name && 00266 (opt = getopt_long(argc, argv, options, 00267 long_options, &option_index)) != EOF) { 00268 switch (opt) { 00269 00270 #ifdef __linux__ 00271 case 'c': 00272 param = jackctl_get_parameter(server_parameters, "clock-source"); 00273 if (param != NULL) { 00274 if (tolower (optarg[0]) == 'h') { 00275 value.ui = JACK_TIMER_HPET; 00276 jackctl_parameter_set_value(param, &value); 00277 } else if (tolower (optarg[0]) == 'c') { 00278 value.ui = JACK_TIMER_CYCLE_COUNTER; 00279 jackctl_parameter_set_value(param, &value); 00280 } else if (tolower (optarg[0]) == 's') { 00281 value.ui = JACK_TIMER_SYSTEM_CLOCK; 00282 jackctl_parameter_set_value(param, &value); 00283 } else { 00284 usage(stdout); 00285 goto destroy_server; 00286 } 00287 } 00288 break; 00289 #endif 00290 00291 case 'd': 00292 master_driver_name = optarg; 00293 break; 00294 00295 case 'L': 00296 loopback = atoi(optarg); 00297 break; 00298 00299 case 'X': 00300 slaves_list.push_back(optarg); 00301 break; 00302 00303 case 'I': 00304 internals_list.push_back(optarg); 00305 break; 00306 00307 case 'p': 00308 param = jackctl_get_parameter(server_parameters, "port-max"); 00309 if (param != NULL) { 00310 value.ui = atoi(optarg); 00311 jackctl_parameter_set_value(param, &value); 00312 } 00313 break; 00314 00315 case 'm': 00316 do_mlock = 0; 00317 break; 00318 00319 case 'u': 00320 do_unlock = 1; 00321 break; 00322 00323 case 'v': 00324 param = jackctl_get_parameter(server_parameters, "verbose"); 00325 if (param != NULL) { 00326 value.b = true; 00327 jackctl_parameter_set_value(param, &value); 00328 } 00329 break; 00330 00331 case 's': 00332 jack_set_error_function(silent_jack_error_callback); 00333 break; 00334 00335 case 'S': 00336 param = jackctl_get_parameter(server_parameters, "sync"); 00337 if (param != NULL) { 00338 value.b = true; 00339 jackctl_parameter_set_value(param, &value); 00340 } 00341 break; 00342 00343 case 'n': 00344 server_name = optarg; 00345 param = jackctl_get_parameter(server_parameters, "name"); 00346 if (param != NULL) { 00347 strncpy(value.str, optarg, JACK_PARAM_STRING_MAX); 00348 jackctl_parameter_set_value(param, &value); 00349 } 00350 break; 00351 00352 case 'P': 00353 param = jackctl_get_parameter(server_parameters, "realtime-priority"); 00354 if (param != NULL) { 00355 value.i = atoi(optarg); 00356 jackctl_parameter_set_value(param, &value); 00357 } 00358 break; 00359 00360 case 'r': 00361 param = jackctl_get_parameter(server_parameters, "realtime"); 00362 if (param != NULL) { 00363 value.b = false; 00364 jackctl_parameter_set_value(param, &value); 00365 } 00366 break; 00367 00368 case 'R': 00369 param = jackctl_get_parameter(server_parameters, "realtime"); 00370 if (param != NULL) { 00371 value.b = true; 00372 jackctl_parameter_set_value(param, &value); 00373 } 00374 break; 00375 00376 case 'T': 00377 param = jackctl_get_parameter(server_parameters, "temporary"); 00378 if (param != NULL) { 00379 value.b = true; 00380 jackctl_parameter_set_value(param, &value); 00381 } 00382 break; 00383 00384 case 't': 00385 param = jackctl_get_parameter(server_parameters, "client-timeout"); 00386 if (param != NULL) { 00387 value.i = atoi(optarg); 00388 jackctl_parameter_set_value(param, &value); 00389 } 00390 break; 00391 00392 case 'V': 00393 show_version = true; 00394 break; 00395 00396 default: 00397 fprintf(stderr, "unknown option character %c\n", optopt); 00398 /*fallthru*/ 00399 00400 case 'h': 00401 usage(stdout); 00402 goto destroy_server; 00403 } 00404 } 00405 00406 // Long option with no letter so treated separately 00407 param = jackctl_get_parameter(server_parameters, "replace-registry"); 00408 if (param != NULL) { 00409 value.b = replace_registry; 00410 jackctl_parameter_set_value(param, &value); 00411 } 00412 00413 if (show_version) { 00414 printf( "jackdmp version " VERSION 00415 " tmpdir " jack_server_dir 00416 " protocol %d" 00417 "\n", JACK_PROTOCOL_VERSION); 00418 return -1; 00419 } 00420 00421 if (!master_driver_name) { 00422 usage(stderr); 00423 goto destroy_server; 00424 } 00425 00426 // Master driver 00427 master_driver_ctl = jackctl_server_get_driver(server_ctl, master_driver_name); 00428 if (master_driver_ctl == NULL) { 00429 fprintf(stderr, "Unknown driver \"%s\"\n", master_driver_name); 00430 goto destroy_server; 00431 } 00432 00433 if (jackctl_driver_get_type(master_driver_ctl) != JackMaster) { 00434 fprintf(stderr, "Driver \"%s\" is not a master \n", master_driver_name); 00435 goto destroy_server; 00436 } 00437 00438 if (optind < argc) { 00439 master_driver_nargs = 1 + argc - optind; 00440 } else { 00441 master_driver_nargs = 1; 00442 } 00443 00444 if (master_driver_nargs == 0) { 00445 fprintf(stderr, "No driver specified ... hmm. JACK won't do" 00446 " anything when run like this.\n"); 00447 goto destroy_server; 00448 } 00449 00450 master_driver_args = (char **) malloc(sizeof(char *) * master_driver_nargs); 00451 master_driver_args[0] = master_driver_name; 00452 00453 for (i = 1; i < master_driver_nargs; i++) { 00454 master_driver_args[i] = argv[optind++]; 00455 } 00456 00457 if (jackctl_parse_driver_params(master_driver_ctl, master_driver_nargs, master_driver_args)) { 00458 goto destroy_server; 00459 } 00460 00461 // Setup signals 00462 signals = jackctl_setup_signals(0); 00463 00464 // Open server 00465 if (! jackctl_server_open(server_ctl, master_driver_ctl)) { 00466 fprintf(stderr, "Failed to open server\n"); 00467 goto destroy_server; 00468 } 00469 00470 // Slave drivers 00471 for (it = slaves_list.begin(); it != slaves_list.end(); it++) { 00472 jackctl_driver_t * slave_driver_ctl = jackctl_server_get_driver(server_ctl, *it); 00473 if (slave_driver_ctl == NULL) { 00474 fprintf(stderr, "Unknown driver \"%s\"\n", *it); 00475 goto close_server; 00476 } 00477 if (jackctl_driver_get_type(slave_driver_ctl) != JackSlave) { 00478 fprintf(stderr, "Driver \"%s\" is not a slave \n", *it); 00479 goto close_server; 00480 } 00481 if (!jackctl_server_add_slave(server_ctl, slave_driver_ctl)) { 00482 fprintf(stderr, "Driver \"%s\" cannot be loaded\n", *it); 00483 goto close_server; 00484 } 00485 } 00486 00487 // Loopback driver 00488 if (loopback > 0) { 00489 loopback_driver_ctl = jackctl_server_get_driver(server_ctl, "loopback"); 00490 00491 if (loopback_driver_ctl != NULL) { 00492 const JSList * loopback_parameters = jackctl_driver_get_parameters(loopback_driver_ctl); 00493 param = jackctl_get_parameter(loopback_parameters, "channels"); 00494 if (param != NULL) { 00495 value.ui = loopback; 00496 jackctl_parameter_set_value(param, &value); 00497 } 00498 if (!jackctl_server_add_slave(server_ctl, loopback_driver_ctl)) { 00499 fprintf(stderr, "Driver \"loopback\" cannot be loaded\n"); 00500 goto close_server; 00501 } 00502 } else { 00503 fprintf(stderr, "Driver \"loopback\" not found\n"); 00504 goto close_server; 00505 } 00506 } 00507 00508 // Start the server 00509 if (!jackctl_server_start(server_ctl)) { 00510 fprintf(stderr, "Failed to start server\n"); 00511 goto close_server; 00512 } 00513 00514 // Internal clients 00515 for (it = internals_list.begin(); it != internals_list.end(); it++) { 00516 jackctl_internal_t * internal_driver_ctl = jackctl_server_get_internal(server_ctl, *it); 00517 if (internal_driver_ctl == NULL) { 00518 fprintf(stderr, "Unknown internal \"%s\"\n", *it); 00519 goto stop_server; 00520 } 00521 if (!jackctl_server_load_internal(server_ctl, internal_driver_ctl)) { 00522 fprintf(stderr, "Internal client \"%s\" cannot be loaded\n", *it); 00523 goto stop_server; 00524 } 00525 } 00526 00527 notify_server_start(server_name); 00528 notify_sent = true; 00529 return_value = 0; 00530 00531 // Waits for signal 00532 jackctl_wait_signals(signals); 00533 00534 stop_server: 00535 if (!jackctl_server_stop(server_ctl)) { 00536 fprintf(stderr, "Cannot stop server...\n"); 00537 } 00538 00539 close_server: 00540 if (loopback > 0 && loopback_driver_ctl) { 00541 jackctl_server_remove_slave(server_ctl, loopback_driver_ctl); 00542 } 00543 // Slave drivers 00544 for (it = slaves_list.begin(); it != slaves_list.end(); it++) { 00545 jackctl_driver_t * slave_driver_ctl = jackctl_server_get_driver(server_ctl, *it); 00546 if (slave_driver_ctl) 00547 jackctl_server_remove_slave(server_ctl, slave_driver_ctl); 00548 } 00549 00550 // Internal clients 00551 for (it = internals_list.begin(); it != internals_list.end(); it++) { 00552 jackctl_internal_t * internal_driver_ctl = jackctl_server_get_internal(server_ctl, *it); 00553 if (internal_driver_ctl) 00554 jackctl_server_unload_internal(server_ctl, internal_driver_ctl); 00555 } 00556 jackctl_server_close(server_ctl); 00557 00558 destroy_server: 00559 jackctl_server_destroy(server_ctl); 00560 if (notify_sent) { 00561 notify_server_stop(server_name); 00562 } 00563 return return_value; 00564 }