1 /// Translated from C to D 2 module soundio.jack; 3 4 version(SOUNDIO_HAVE_JACK): 5 @nogc nothrow: 6 extern(C): __gshared: 7 8 import core.stdc.config: c_long, c_ulong; 9 10 import soundio.api; 11 import soundio.os; 12 import soundio.atomics; 13 14 import soundio.soundio_private; 15 import soundio.list; 16 import core.stdc.stdio; 17 import core.stdc.stdlib: free; 18 import core.stdc.string: strlen, strcmp, memcpy; 19 20 import soundio.headers.jackheader; 21 22 package: 23 24 struct SoundIoDeviceJackPort { 25 char* full_name; 26 int full_name_len; 27 SoundIoChannelId channel_id; 28 jack_latency_range_t latency_range; 29 } 30 31 struct SoundIoDeviceJack { 32 int port_count; 33 SoundIoDeviceJackPort* ports; 34 } 35 36 struct SoundIoJack { 37 jack_client_t* client; 38 SoundIoOsMutex* mutex; 39 SoundIoOsCond* cond; 40 SoundIoAtomicFlag refresh_devices_flag; 41 int sample_rate; 42 int period_size; 43 bool is_shutdown; 44 bool emitted_shutdown_cb; 45 } 46 47 struct SoundIoOutStreamJackPort { 48 jack_port_t* source_port; 49 const(char)* dest_port_name; 50 int dest_port_name_len; 51 } 52 53 struct SoundIoOutStreamJack { 54 jack_client_t* client; 55 int period_size; 56 int frames_left; 57 double hardware_latency; 58 SoundIoOutStreamJackPort[SOUNDIO_MAX_CHANNELS] ports; 59 SoundIoChannelArea[SOUNDIO_MAX_CHANNELS] areas; 60 } 61 62 struct SoundIoInStreamJackPort { 63 jack_port_t* dest_port; 64 const(char)* source_port_name; 65 int source_port_name_len; 66 } 67 68 struct SoundIoInStreamJack { 69 jack_client_t* client; 70 int period_size; 71 int frames_left; 72 double hardware_latency; 73 SoundIoInStreamJackPort[SOUNDIO_MAX_CHANNELS] ports; 74 SoundIoChannelArea[SOUNDIO_MAX_CHANNELS] areas; 75 char*[SOUNDIO_MAX_CHANNELS] buf_ptrs; 76 } 77 78 static SoundIoAtomicFlag global_msg_callback_flag = SOUNDIO_ATOMIC_FLAG_INIT; 79 80 struct SoundIoJackPort { 81 const(char)* full_name; 82 int full_name_len; 83 const(char)* name; 84 int name_len; 85 SoundIoChannelId channel_id; 86 jack_latency_range_t latency_range; 87 } 88 89 struct SoundIoJackClient { 90 const(char)* name; 91 int name_len; 92 bool is_physical; 93 SoundIoDeviceAim aim; 94 int port_count; 95 SoundIoJackPort[SOUNDIO_MAX_CHANNELS] ports; 96 } 97 98 alias SoundIoListJackClient = SOUNDIO_LIST!SoundIoJackClient; 99 100 static void split_str(const(char)* input_str, int input_str_len, char c, const(char)** out_1, int* out_len_1, const(char)** out_2, int* out_len_2) { 101 *out_1 = input_str; 102 while (*input_str) { 103 if (*input_str == c) { 104 *out_len_1 = cast(int) (input_str - *out_1); 105 *out_2 = input_str + 1; 106 *out_len_2 = input_str_len - 1 - *out_len_1; 107 return; 108 } 109 input_str += 1; 110 } 111 } 112 113 static SoundIoJackClient* find_or_create_client(SoundIoListJackClient* clients, SoundIoDeviceAim aim, bool is_physical, const(char)* client_name, int client_name_len) { 114 for (int i = 0; i < clients.length; i += 1) { 115 SoundIoJackClient* client = clients.ptr_at(i); 116 if (client.is_physical == is_physical && 117 client.aim == aim && 118 soundio_streql(client.name, client.name_len, client_name, client_name_len)) 119 { 120 return client; 121 } 122 } 123 if (auto err = clients.add_one()) 124 return null; 125 SoundIoJackClient* client = clients.last_ptr(); 126 client.is_physical = is_physical; 127 client.aim = aim; 128 client.name = client_name; 129 client.name_len = client_name_len; 130 client.port_count = 0; 131 return client; 132 } 133 134 static void destruct_device(SoundIoDevicePrivate* dp) { 135 SoundIoDeviceJack* dj = &dp.backend_data.jack; 136 for (int i = 0; i < dj.port_count; i += 1) { 137 SoundIoDeviceJackPort* djp = &dj.ports[i]; 138 free(djp.full_name); 139 } 140 free(dj.ports); 141 } 142 143 static int refresh_devices_bare(SoundIoPrivate* si) { 144 SoundIo* soundio = &si.pub; 145 SoundIoJack* sij = &si.backend_data.jack; 146 147 if (sij.is_shutdown) 148 return SoundIoErrorBackendDisconnected; 149 150 151 SoundIoDevicesInfo* devices_info = ALLOCATE!SoundIoDevicesInfo(1); 152 if (!devices_info) 153 return SoundIoErrorNoMem; 154 155 devices_info.default_output_index = -1; 156 devices_info.default_input_index = -1; 157 const(char)** port_names = jack_get_ports(sij.client, null, null, 0); 158 if (!port_names) { 159 soundio_destroy_devices_info(devices_info); 160 return SoundIoErrorNoMem; 161 } 162 163 SoundIoListJackClient clients = SoundIoListJackClient.init; // todo: zero? 164 const(char)** port_name_ptr = port_names; 165 for (; *port_name_ptr; port_name_ptr += 1) { 166 const(char)* client_and_port_name = *port_name_ptr; 167 int client_and_port_name_len = cast(int) strlen(client_and_port_name); 168 jack_port_t* jport = jack_port_by_name(sij.client, client_and_port_name); 169 if (!jport) { 170 // This refresh devices scan is already outdated. Just give up and 171 // let refresh_devices be called again. 172 jack_free(port_names); 173 soundio_destroy_devices_info(devices_info); 174 return SoundIoErrorInterrupted; 175 } 176 177 int flags = jack_port_flags(jport); 178 const(char)* port_type = jack_port_type(jport); 179 if (strcmp(port_type, JACK_DEFAULT_AUDIO_TYPE) != 0) { 180 // we don't know how to support such a port 181 continue; 182 } 183 184 SoundIoDeviceAim aim = (flags & JackPortIsInput) ? 185 SoundIoDeviceAimOutput : SoundIoDeviceAimInput; 186 bool is_physical = cast(bool) (flags & JackPortIsPhysical); 187 188 const(char)* client_name = null; 189 const(char)* port_name = null; 190 int client_name_len; 191 int port_name_len; 192 split_str(client_and_port_name, client_and_port_name_len, ':', 193 &client_name, &client_name_len, &port_name, &port_name_len); 194 if (!client_name || !port_name) { 195 // device does not have colon, skip it 196 continue; 197 } 198 SoundIoJackClient* client = find_or_create_client(&clients, aim, is_physical, 199 client_name, client_name_len); 200 if (!client) { 201 jack_free(port_names); 202 soundio_destroy_devices_info(devices_info); 203 return SoundIoErrorNoMem; 204 } 205 if (client.port_count >= SOUNDIO_MAX_CHANNELS) { 206 // we hit the channel limit, skip the leftovers 207 continue; 208 } 209 SoundIoJackPort* port = &client.ports[client.port_count++]; 210 port.full_name = client_and_port_name; 211 port.full_name_len = client_and_port_name_len; 212 port.name = port_name; 213 port.name_len = port_name_len; 214 port.channel_id = soundio_parse_channel_id(port_name, port_name_len); 215 216 jack_latency_callback_mode_t latency_mode = (aim == SoundIoDeviceAimOutput) ? 217 JackPlaybackLatency : JackCaptureLatency; 218 jack_port_get_latency_range(jport, latency_mode, &port.latency_range); 219 } 220 221 for (int i = 0; i < clients.length; i += 1) { 222 SoundIoJackClient* client = clients.ptr_at(i); 223 if (client.port_count <= 0) 224 continue; 225 226 SoundIoDevicePrivate* dev = ALLOCATE!SoundIoDevicePrivate(1); 227 if (!dev) { 228 jack_free(port_names); 229 soundio_destroy_devices_info(devices_info); 230 return SoundIoErrorNoMem; 231 } 232 SoundIoDevice* device = &dev.pub; 233 SoundIoDeviceJack* dj = &dev.backend_data.jack; 234 int description_len = client.name_len + 3 + 2 * client.port_count; 235 for (int port_index = 0; port_index < client.port_count; port_index += 1) { 236 SoundIoJackPort* port = &client.ports[port_index]; 237 238 description_len += port.name_len; 239 } 240 241 dev.destruct = &destruct_device; 242 243 device.ref_count = 1; 244 device.soundio = soundio; 245 device.is_raw = false; 246 device.aim = client.aim; 247 device.id = soundio_str_dupe(client.name, client.name_len); 248 device.name = ALLOCATE!char(description_len); 249 device.current_format = SoundIoFormatFloat32NE; 250 device.sample_rate_count = 1; 251 device.sample_rates = &dev.prealloc_sample_rate_range; 252 device.sample_rates[0].min = sij.sample_rate; 253 device.sample_rates[0].max = sij.sample_rate; 254 device.sample_rate_current = sij.sample_rate; 255 256 device.software_latency_current = sij.period_size / cast(double) sij.sample_rate; 257 device.software_latency_min = sij.period_size / cast(double) sij.sample_rate; 258 device.software_latency_max = sij.period_size / cast(double) sij.sample_rate; 259 260 dj.port_count = client.port_count; 261 dj.ports = ALLOCATE!SoundIoDeviceJackPort(dj.port_count); 262 263 if (!device.id || !device.name || !dj.ports) { 264 jack_free(port_names); 265 soundio_device_unref(device); 266 soundio_destroy_devices_info(devices_info); 267 return SoundIoErrorNoMem; 268 } 269 270 for (int port_index = 0; port_index < client.port_count; port_index += 1) { 271 SoundIoJackPort* port = &client.ports[port_index]; 272 SoundIoDeviceJackPort* djp = &dj.ports[port_index]; 273 djp.full_name = soundio_str_dupe(port.full_name, port.full_name_len); 274 djp.full_name_len = port.full_name_len; 275 djp.channel_id = port.channel_id; 276 djp.latency_range = port.latency_range; 277 278 if (!djp.full_name) { 279 jack_free(port_names); 280 soundio_device_unref(device); 281 soundio_destroy_devices_info(devices_info); 282 return SoundIoErrorNoMem; 283 } 284 } 285 286 memcpy(device.name, client.name, client.name_len); 287 memcpy(&device.name[client.name_len], ": ".ptr, 2); 288 int index = client.name_len + 2; 289 for (int port_index = 0; port_index < client.port_count; port_index += 1) { 290 SoundIoJackPort* port = &client.ports[port_index]; 291 memcpy(&device.name[index], port.name, port.name_len); 292 index += port.name_len; 293 if (port_index + 1 < client.port_count) { 294 memcpy(&device.name[index], ", ".ptr, 2); 295 index += 2; 296 } 297 } 298 299 device.current_layout.channel_count = client.port_count; 300 bool any_invalid = false; 301 for (int port_index = 0; port_index < client.port_count; port_index += 1) { 302 SoundIoJackPort* port = &client.ports[port_index]; 303 device.current_layout.channels[port_index] = port.channel_id; 304 any_invalid = any_invalid || (port.channel_id == SoundIoChannelId.Invalid); 305 } 306 if (any_invalid) { 307 const(SoundIoChannelLayout)* layout = soundio_channel_layout_get_default(client.port_count); 308 if (layout) 309 device.current_layout = *layout; 310 } else { 311 soundio_channel_layout_detect_builtin(&device.current_layout); 312 } 313 314 device.layout_count = 1; 315 device.layouts = &device.current_layout; 316 device.format_count = 1; 317 device.formats = &dev.prealloc_format; 318 device.formats[0] = device.current_format; 319 320 SoundIoListDevicePtr* device_list; 321 if (device.aim == SoundIoDeviceAimOutput) { 322 device_list = &devices_info.output_devices; 323 if (devices_info.default_output_index < 0 && client.is_physical) 324 devices_info.default_output_index = device_list.length; 325 } else { 326 assert(device.aim == SoundIoDeviceAimInput); 327 device_list = &devices_info.input_devices; 328 if (devices_info.default_input_index < 0 && client.is_physical) 329 devices_info.default_input_index = device_list.length; 330 } 331 332 if (device_list.append(device)) { 333 soundio_device_unref(device); 334 soundio_destroy_devices_info(devices_info); 335 return SoundIoErrorNoMem; 336 } 337 338 } 339 jack_free(port_names); 340 341 soundio_destroy_devices_info(si.safe_devices_info); 342 si.safe_devices_info = devices_info; 343 344 return 0; 345 } 346 347 static int refresh_devices(SoundIoPrivate* si) { 348 int err = SoundIoErrorInterrupted; 349 while (err == SoundIoErrorInterrupted) 350 err = refresh_devices_bare(si); 351 return err; 352 } 353 354 private extern(D) void my_flush_events(SoundIoPrivate* si, bool wait) { 355 SoundIo* soundio = &si.pub; 356 SoundIoJack* sij = &si.backend_data.jack; 357 358 bool cb_shutdown = false; 359 360 soundio_os_mutex_lock(sij.mutex); 361 362 if (wait) 363 soundio_os_cond_wait(sij.cond, sij.mutex); 364 365 if (sij.is_shutdown && !sij.emitted_shutdown_cb) { 366 sij.emitted_shutdown_cb = true; 367 cb_shutdown = true; 368 } 369 370 soundio_os_mutex_unlock(sij.mutex); 371 372 if (cb_shutdown) { 373 soundio.on_backend_disconnect(soundio, SoundIoErrorBackendDisconnected); 374 } else { 375 if (!SOUNDIO_ATOMIC_FLAG_TEST_AND_SET(sij.refresh_devices_flag)) { 376 if (auto err = refresh_devices(si)) { 377 SOUNDIO_ATOMIC_FLAG_CLEAR(sij.refresh_devices_flag); 378 } else { 379 soundio.on_devices_change(soundio); 380 } 381 } 382 } 383 } 384 385 static void flush_events_jack(SoundIoPrivate* si) { 386 my_flush_events(si, false); 387 } 388 389 static void wait_events_jack(SoundIoPrivate* si) { 390 my_flush_events(si, false); 391 my_flush_events(si, true); 392 } 393 394 static void wakeup_jack(SoundIoPrivate* si) { 395 SoundIoJack* sij = &si.backend_data.jack; 396 soundio_os_mutex_lock(sij.mutex); 397 soundio_os_cond_signal(sij.cond, sij.mutex); 398 soundio_os_mutex_unlock(sij.mutex); 399 } 400 401 static void force_device_scan_jack(SoundIoPrivate* si) { 402 SoundIo* soundio = &si.pub; 403 SoundIoJack* sij = &si.backend_data.jack; 404 SOUNDIO_ATOMIC_FLAG_CLEAR(sij.refresh_devices_flag); 405 soundio_os_mutex_lock(sij.mutex); 406 soundio_os_cond_signal(sij.cond, sij.mutex); 407 soundio.on_events_signal(soundio); 408 soundio_os_mutex_unlock(sij.mutex); 409 } 410 411 static int outstream_process_callback(jack_nframes_t nframes, void* arg) { 412 SoundIoOutStreamPrivate* os = cast(SoundIoOutStreamPrivate*)arg; 413 SoundIoOutStreamJack* osj = &os.backend_data.jack; 414 SoundIoOutStream* outstream = &os.pub; 415 osj.frames_left = nframes; 416 for (int ch = 0; ch < outstream.layout.channel_count; ch += 1) { 417 SoundIoOutStreamJackPort* osjp = &osj.ports[ch]; 418 osj.areas[ch].ptr = cast(char*)jack_port_get_buffer(osjp.source_port, nframes); 419 osj.areas[ch].step = outstream.bytes_per_sample; 420 } 421 outstream.write_callback(outstream, osj.frames_left, osj.frames_left); 422 return 0; 423 } 424 425 static void outstream_destroy_jack(SoundIoPrivate* is_, SoundIoOutStreamPrivate* os) { 426 SoundIoOutStreamJack* osj = &os.backend_data.jack; 427 428 jack_client_close(osj.client); 429 osj.client = null; 430 } 431 432 static SoundIoDeviceJackPort* find_port_matching_channel(SoundIoDevice* device, SoundIoChannelId id) { 433 SoundIoDevicePrivate* dev = cast(SoundIoDevicePrivate*)device; 434 SoundIoDeviceJack* dj = &dev.backend_data.jack; 435 436 for (int ch = 0; ch < device.current_layout.channel_count; ch += 1) { 437 SoundIoChannelId chan_id = device.current_layout.channels[ch]; 438 if (chan_id == id) 439 return &dj.ports[ch]; 440 } 441 442 return null; 443 } 444 445 static int outstream_xrun_callback(void* arg) { 446 SoundIoOutStreamPrivate* os = cast(SoundIoOutStreamPrivate*)arg; 447 SoundIoOutStream* outstream = &os.pub; 448 outstream.underflow_callback(outstream); 449 return 0; 450 } 451 452 static int outstream_buffer_size_callback(jack_nframes_t nframes, void* arg) { 453 SoundIoOutStreamPrivate* os = cast(SoundIoOutStreamPrivate*)arg; 454 SoundIoOutStreamJack* osj = &os.backend_data.jack; 455 SoundIoOutStream* outstream = &os.pub; 456 if (cast(jack_nframes_t)osj.period_size == nframes) { 457 return 0; 458 } else { 459 outstream.error_callback(outstream, SoundIoErrorStreaming); 460 return -1; 461 } 462 } 463 464 static int outstream_sample_rate_callback(jack_nframes_t nframes, void* arg) { 465 SoundIoOutStreamPrivate* os = cast(SoundIoOutStreamPrivate*)arg; 466 SoundIoOutStream* outstream = &os.pub; 467 if (nframes == cast(jack_nframes_t)outstream.sample_rate) { 468 return 0; 469 } else { 470 outstream.error_callback(outstream, SoundIoErrorStreaming); 471 return -1; 472 } 473 } 474 475 static void outstream_shutdown_callback(void* arg) { 476 SoundIoOutStreamPrivate* os = cast(SoundIoOutStreamPrivate*)arg; 477 SoundIoOutStream* outstream = &os.pub; 478 outstream.error_callback(outstream, SoundIoErrorStreaming); 479 } 480 481 pragma(inline, true) static jack_nframes_t nframes_max(jack_nframes_t a, jack_nframes_t b) { 482 return (a >= b) ? a : b; 483 } 484 485 static int outstream_open_jack(SoundIoPrivate* si, SoundIoOutStreamPrivate* os) { 486 SoundIoJack* sij = &si.backend_data.jack; 487 SoundIoOutStreamJack* osj = &os.backend_data.jack; 488 SoundIoOutStream* outstream = &os.pub; 489 SoundIoDevice* device = outstream.device; 490 SoundIoDevicePrivate* dev = cast(SoundIoDevicePrivate*)device; 491 SoundIoDeviceJack* dj = &dev.backend_data.jack; 492 493 if (sij.is_shutdown) 494 return SoundIoErrorBackendDisconnected; 495 496 if (!outstream.name) 497 outstream.name = "SoundIoOutStream"; 498 499 outstream.software_latency = device.software_latency_current; 500 osj.period_size = sij.period_size; 501 502 jack_status_t status; 503 osj.client = jack_client_open(outstream.name, JackNoStartServer, &status); 504 if (!osj.client) { 505 outstream_destroy_jack(si, os); 506 assert(!(status & JackInvalidOption)); 507 if (status & JackShmFailure) 508 return SoundIoErrorSystemResources; 509 if (status & JackNoSuchClient) 510 return SoundIoErrorNoSuchClient; 511 return SoundIoErrorOpeningDevice; 512 } 513 514 if (auto err = jack_set_process_callback(osj.client, &outstream_process_callback, os)) { 515 outstream_destroy_jack(si, os); 516 return SoundIoErrorOpeningDevice; 517 } 518 if (auto err = jack_set_buffer_size_callback(osj.client, &outstream_buffer_size_callback, os)) { 519 outstream_destroy_jack(si, os); 520 return SoundIoErrorOpeningDevice; 521 } 522 if (auto err = jack_set_sample_rate_callback(osj.client, &outstream_sample_rate_callback, os)) { 523 outstream_destroy_jack(si, os); 524 return SoundIoErrorOpeningDevice; 525 } 526 if (auto err = jack_set_xrun_callback(osj.client, &outstream_xrun_callback, os)) { 527 outstream_destroy_jack(si, os); 528 return SoundIoErrorOpeningDevice; 529 } 530 jack_on_shutdown(osj.client, &outstream_shutdown_callback, os); 531 532 533 jack_nframes_t max_port_latency = 0; 534 535 // register ports and map channels 536 int connected_count = 0; 537 for (int ch = 0; ch < outstream.layout.channel_count; ch += 1) { 538 SoundIoChannelId my_channel_id = outstream.layout.channels[ch]; 539 const(char)* channel_name = soundio_get_channel_name(my_channel_id); 540 c_ulong flags = JackPortIsOutput; 541 if (!outstream.non_terminal_hint) 542 flags |= JackPortIsTerminal; 543 jack_port_t* jport = jack_port_register(osj.client, channel_name, JACK_DEFAULT_AUDIO_TYPE, flags, 0); 544 if (!jport) { 545 outstream_destroy_jack(si, os); 546 return SoundIoErrorOpeningDevice; 547 } 548 SoundIoOutStreamJackPort* osjp = &osj.ports[ch]; 549 osjp.source_port = jport; 550 // figure out which dest port this connects to 551 SoundIoDeviceJackPort* djp = find_port_matching_channel(device, my_channel_id); 552 if (djp) { 553 osjp.dest_port_name = djp.full_name; 554 osjp.dest_port_name_len = djp.full_name_len; 555 connected_count += 1; 556 max_port_latency = nframes_max(max_port_latency, djp.latency_range.max); 557 } 558 } 559 // If nothing got connected, channel layouts aren't working. Just send the 560 // data in the order of the ports. 561 if (connected_count == 0) { 562 max_port_latency = 0; 563 outstream.layout_error = SoundIoErrorIncompatibleDevice; 564 565 int ch_count = soundio_int_min(outstream.layout.channel_count, dj.port_count); 566 for (int ch = 0; ch < ch_count; ch += 1) { 567 SoundIoOutStreamJackPort* osjp = &osj.ports[ch]; 568 SoundIoDeviceJackPort* djp = &dj.ports[ch]; 569 osjp.dest_port_name = djp.full_name; 570 osjp.dest_port_name_len = djp.full_name_len; 571 max_port_latency = nframes_max(max_port_latency, djp.latency_range.max); 572 } 573 } 574 575 osj.hardware_latency = max_port_latency / cast(double)outstream.sample_rate; 576 577 return 0; 578 } 579 580 static int outstream_pause_jack(SoundIoPrivate* si, SoundIoOutStreamPrivate* os, bool pause) { 581 SoundIoJack* sij = &si.backend_data.jack; 582 583 if (sij.is_shutdown) 584 return SoundIoErrorBackendDisconnected; 585 586 return SoundIoErrorIncompatibleBackend; 587 } 588 589 static int outstream_start_jack(SoundIoPrivate* si, SoundIoOutStreamPrivate* os) { 590 SoundIoOutStreamJack* osj = &os.backend_data.jack; 591 SoundIoOutStream* outstream = &os.pub; 592 SoundIoJack* sij = &si.backend_data.jack; 593 594 if (sij.is_shutdown) 595 return SoundIoErrorBackendDisconnected; 596 597 if (auto err = jack_activate(osj.client)) 598 return SoundIoErrorStreaming; 599 600 for (int ch = 0; ch < outstream.layout.channel_count; ch += 1) { 601 SoundIoOutStreamJackPort* osjp = &osj.ports[ch]; 602 const(char)* dest_port_name = osjp.dest_port_name; 603 // allow unconnected ports 604 if (!dest_port_name) 605 continue; 606 const(char)* source_port_name = jack_port_name(osjp.source_port); 607 if (auto err = jack_connect(osj.client, source_port_name, dest_port_name)) 608 return SoundIoErrorStreaming; 609 } 610 611 return 0; 612 } 613 614 static int outstream_begin_write_jack(SoundIoPrivate* si, SoundIoOutStreamPrivate* os, SoundIoChannelArea** out_areas, int* frame_count) { 615 SoundIoOutStreamJack* osj = &os.backend_data.jack; 616 617 if (*frame_count != osj.frames_left) 618 return SoundIoErrorInvalid; 619 620 *out_areas = osj.areas.ptr; 621 622 return 0; 623 } 624 625 static int outstream_end_write_jack(SoundIoPrivate* si, SoundIoOutStreamPrivate* os) { 626 SoundIoOutStreamJack* osj = &os.backend_data.jack; 627 osj.frames_left = 0; 628 return 0; 629 } 630 631 static int outstream_clear_buffer_jack(SoundIoPrivate* si, SoundIoOutStreamPrivate* os) { 632 return SoundIoErrorIncompatibleBackend; 633 } 634 635 static int outstream_get_latency_jack(SoundIoPrivate* si, SoundIoOutStreamPrivate* os, double* out_latency) { 636 SoundIoOutStreamJack* osj = &os.backend_data.jack; 637 *out_latency = osj.hardware_latency; 638 return 0; 639 } 640 641 642 static void instream_destroy_jack(SoundIoPrivate* si, SoundIoInStreamPrivate* is_) { 643 SoundIoInStreamJack* isj = &is_.backend_data.jack; 644 645 jack_client_close(isj.client); 646 isj.client = null; 647 } 648 649 static int instream_xrun_callback(void* arg) { 650 SoundIoInStreamPrivate* is_ = cast(SoundIoInStreamPrivate*)arg; 651 SoundIoInStream* instream = &is_.pub; 652 instream.overflow_callback(instream); 653 return 0; 654 } 655 656 static int instream_buffer_size_callback(jack_nframes_t nframes, void* arg) { 657 SoundIoInStreamPrivate* is_ = cast(SoundIoInStreamPrivate*)arg; 658 SoundIoInStreamJack* isj = &is_.backend_data.jack; 659 SoundIoInStream* instream = &is_.pub; 660 661 if (cast(jack_nframes_t)isj.period_size == nframes) { 662 return 0; 663 } else { 664 instream.error_callback(instream, SoundIoErrorStreaming); 665 return -1; 666 } 667 } 668 669 static int instream_sample_rate_callback(jack_nframes_t nframes, void* arg) { 670 SoundIoInStreamPrivate* is_ = cast(SoundIoInStreamPrivate*)arg; 671 SoundIoInStream* instream = &is_.pub; 672 if (nframes == cast(jack_nframes_t)instream.sample_rate) { 673 return 0; 674 } else { 675 instream.error_callback(instream, SoundIoErrorStreaming); 676 return -1; 677 } 678 } 679 680 static void instream_shutdown_callback(void* arg) { 681 SoundIoInStreamPrivate* is_ = cast(SoundIoInStreamPrivate*)arg; 682 SoundIoInStream* instream = &is_.pub; 683 instream.error_callback(instream, SoundIoErrorStreaming); 684 } 685 686 static int instream_process_callback(jack_nframes_t nframes, void* arg) { 687 SoundIoInStreamPrivate* is_ = cast(SoundIoInStreamPrivate*)arg; 688 SoundIoInStream* instream = &is_.pub; 689 SoundIoInStreamJack* isj = &is_.backend_data.jack; 690 isj.frames_left = nframes; 691 for (int ch = 0; ch < instream.layout.channel_count; ch += 1) { 692 SoundIoInStreamJackPort* isjp = &isj.ports[ch]; 693 isj.areas[ch].ptr = cast(char*)jack_port_get_buffer(isjp.dest_port, nframes); 694 isj.areas[ch].step = instream.bytes_per_sample; 695 } 696 instream.read_callback(instream, isj.frames_left, isj.frames_left); 697 return 0; 698 } 699 700 static int instream_open_jack(SoundIoPrivate* si, SoundIoInStreamPrivate* is_) { 701 SoundIoInStream* instream = &is_.pub; 702 SoundIoInStreamJack* isj = &is_.backend_data.jack; 703 SoundIoJack* sij = &si.backend_data.jack; 704 SoundIoDevice* device = instream.device; 705 SoundIoDevicePrivate* dev = cast(SoundIoDevicePrivate*)device; 706 SoundIoDeviceJack* dj = &dev.backend_data.jack; 707 708 if (sij.is_shutdown) 709 return SoundIoErrorBackendDisconnected; 710 711 if (!instream.name) 712 instream.name = "SoundIoInStream"; 713 714 instream.software_latency = device.software_latency_current; 715 isj.period_size = sij.period_size; 716 717 jack_status_t status; 718 isj.client = jack_client_open(instream.name, JackNoStartServer, &status); 719 if (!isj.client) { 720 instream_destroy_jack(si, is_); 721 assert(!(status & JackInvalidOption)); 722 if (status & JackShmFailure) 723 return SoundIoErrorSystemResources; 724 if (status & JackNoSuchClient) 725 return SoundIoErrorNoSuchClient; 726 return SoundIoErrorOpeningDevice; 727 } 728 729 if (auto err = jack_set_process_callback(isj.client, &instream_process_callback, is_)) { 730 instream_destroy_jack(si, is_); 731 return SoundIoErrorOpeningDevice; 732 } 733 if (auto err = jack_set_buffer_size_callback(isj.client, &instream_buffer_size_callback, is_)) { 734 instream_destroy_jack(si, is_); 735 return SoundIoErrorOpeningDevice; 736 } 737 if (auto err = jack_set_sample_rate_callback(isj.client, &instream_sample_rate_callback, is_)) { 738 instream_destroy_jack(si, is_); 739 return SoundIoErrorOpeningDevice; 740 } 741 if (auto err = jack_set_xrun_callback(isj.client, &instream_xrun_callback, is_)) { 742 instream_destroy_jack(si, is_); 743 return SoundIoErrorOpeningDevice; 744 } 745 jack_on_shutdown(isj.client, &instream_shutdown_callback, is_); 746 747 jack_nframes_t max_port_latency = 0; 748 749 // register ports and map channels 750 int connected_count = 0; 751 for (int ch = 0; ch < instream.layout.channel_count; ch += 1) { 752 SoundIoChannelId my_channel_id = instream.layout.channels[ch]; 753 const(char)* channel_name = soundio_get_channel_name(my_channel_id); 754 c_ulong flags = JackPortIsInput; 755 if (!instream.non_terminal_hint) 756 flags |= JackPortIsTerminal; 757 jack_port_t* jport = jack_port_register(isj.client, channel_name, JACK_DEFAULT_AUDIO_TYPE, flags, 0); 758 if (!jport) { 759 instream_destroy_jack(si, is_); 760 return SoundIoErrorOpeningDevice; 761 } 762 SoundIoInStreamJackPort* isjp = &isj.ports[ch]; 763 isjp.dest_port = jport; 764 // figure out which source port this connects to 765 SoundIoDeviceJackPort* djp = find_port_matching_channel(device, my_channel_id); 766 if (djp) { 767 isjp.source_port_name = djp.full_name; 768 isjp.source_port_name_len = djp.full_name_len; 769 connected_count += 1; 770 max_port_latency = nframes_max(max_port_latency, djp.latency_range.max); 771 } 772 } 773 // If nothing got connected, channel layouts aren't working. Just send the 774 // data in the order of the ports. 775 if (connected_count == 0) { 776 max_port_latency = 0; 777 instream.layout_error = SoundIoErrorIncompatibleDevice; 778 779 int ch_count = soundio_int_min(instream.layout.channel_count, dj.port_count); 780 for (int ch = 0; ch < ch_count; ch += 1) { 781 SoundIoInStreamJackPort* isjp = &isj.ports[ch]; 782 SoundIoDeviceJackPort* djp = &dj.ports[ch]; 783 isjp.source_port_name = djp.full_name; 784 isjp.source_port_name_len = djp.full_name_len; 785 max_port_latency = nframes_max(max_port_latency, djp.latency_range.max); 786 } 787 } 788 789 isj.hardware_latency = max_port_latency / cast(double)instream.sample_rate; 790 791 return 0; 792 } 793 794 static int instream_pause_jack(SoundIoPrivate* si, SoundIoInStreamPrivate* is_, bool pause) { 795 SoundIoJack* sij = &si.backend_data.jack; 796 797 if (sij.is_shutdown) 798 return SoundIoErrorBackendDisconnected; 799 800 return SoundIoErrorIncompatibleBackend; 801 } 802 803 static int instream_start_jack(SoundIoPrivate* si, SoundIoInStreamPrivate* is_) { 804 SoundIoInStreamJack* isj = &is_.backend_data.jack; 805 SoundIoInStream* instream = &is_.pub; 806 SoundIoJack* sij = &si.backend_data.jack; 807 808 if (sij.is_shutdown) 809 return SoundIoErrorBackendDisconnected; 810 811 if (auto err = jack_activate(isj.client)) 812 return SoundIoErrorStreaming; 813 814 for (int ch = 0; ch < instream.layout.channel_count; ch += 1) { 815 SoundIoInStreamJackPort* isjp = &isj.ports[ch]; 816 const(char)* source_port_name = isjp.source_port_name; 817 // allow unconnected ports 818 if (!source_port_name) 819 continue; 820 const(char)* dest_port_name = jack_port_name(isjp.dest_port); 821 if (auto err = jack_connect(isj.client, source_port_name, dest_port_name)) 822 return SoundIoErrorStreaming; 823 } 824 825 return 0; 826 } 827 828 static int instream_begin_read_jack(SoundIoPrivate* si, SoundIoInStreamPrivate* is_, SoundIoChannelArea** out_areas, int* frame_count) { 829 SoundIoInStreamJack* isj = &is_.backend_data.jack; 830 831 if (*frame_count != isj.frames_left) 832 return SoundIoErrorInvalid; 833 834 *out_areas = isj.areas.ptr; 835 836 return 0; 837 } 838 839 static int instream_end_read_jack(SoundIoPrivate* si, SoundIoInStreamPrivate* is_) { 840 SoundIoInStreamJack* isj = &is_.backend_data.jack; 841 isj.frames_left = 0; 842 return 0; 843 } 844 845 static int instream_get_latency_jack(SoundIoPrivate* si, SoundIoInStreamPrivate* is_, double* out_latency) { 846 SoundIoInStreamJack* isj = &is_.backend_data.jack; 847 *out_latency = isj.hardware_latency; 848 return 0; 849 } 850 851 static void notify_devices_change(SoundIoPrivate* si) { 852 SoundIo* soundio = &si.pub; 853 SoundIoJack* sij = &si.backend_data.jack; 854 SOUNDIO_ATOMIC_FLAG_CLEAR(sij.refresh_devices_flag); 855 soundio_os_mutex_lock(sij.mutex); 856 soundio_os_cond_signal(sij.cond, sij.mutex); 857 soundio.on_events_signal(soundio); 858 soundio_os_mutex_unlock(sij.mutex); 859 } 860 861 static int buffer_size_callback(jack_nframes_t nframes, void* arg) { 862 SoundIoPrivate* si = cast(SoundIoPrivate*)arg; 863 SoundIoJack* sij = &si.backend_data.jack; 864 sij.period_size = nframes; 865 notify_devices_change(si); 866 return 0; 867 } 868 869 static int sample_rate_callback(jack_nframes_t nframes, void* arg) { 870 SoundIoPrivate* si = cast(SoundIoPrivate*)arg; 871 SoundIoJack* sij = &si.backend_data.jack; 872 sij.sample_rate = nframes; 873 notify_devices_change(si); 874 return 0; 875 } 876 877 static void port_registration_callback(jack_port_id_t port_id, int reg, void* arg) { 878 SoundIoPrivate* si = cast(SoundIoPrivate*)arg; 879 notify_devices_change(si); 880 } 881 882 static void port_rename_calllback(jack_port_id_t port_id, const(char)* old_name, const(char)* new_name, void* arg) { 883 SoundIoPrivate* si = cast(SoundIoPrivate*)arg; 884 notify_devices_change(si); 885 } 886 887 static void shutdown_callback(void* arg) { 888 SoundIoPrivate* si = cast(SoundIoPrivate*)arg; 889 SoundIo* soundio = &si.pub; 890 SoundIoJack* sij = &si.backend_data.jack; 891 soundio_os_mutex_lock(sij.mutex); 892 sij.is_shutdown = true; 893 soundio_os_cond_signal(sij.cond, sij.mutex); 894 soundio.on_events_signal(soundio); 895 soundio_os_mutex_unlock(sij.mutex); 896 } 897 898 static void destroy_jack(SoundIoPrivate* si) { 899 SoundIoJack* sij = &si.backend_data.jack; 900 901 if (sij.client) 902 jack_client_close(sij.client); 903 904 if (sij.cond) 905 soundio_os_cond_destroy(sij.cond); 906 907 if (sij.mutex) 908 soundio_os_mutex_destroy(sij.mutex); 909 } 910 911 int soundio_jack_init(SoundIoPrivate* si) { 912 SoundIoJack* sij = &si.backend_data.jack; 913 SoundIo* soundio = &si.pub; 914 915 if (!SOUNDIO_ATOMIC_FLAG_TEST_AND_SET(global_msg_callback_flag)) { 916 if (soundio.jack_error_callback) 917 jack_set_error_function(soundio.jack_error_callback); 918 if (soundio.jack_info_callback) 919 jack_set_info_function(soundio.jack_info_callback); 920 SOUNDIO_ATOMIC_FLAG_CLEAR(global_msg_callback_flag); 921 } 922 923 sij.mutex = soundio_os_mutex_create(); 924 if (!sij.mutex) { 925 destroy_jack(si); 926 return SoundIoErrorNoMem; 927 } 928 929 sij.cond = soundio_os_cond_create(); 930 if (!sij.cond) { 931 destroy_jack(si); 932 return SoundIoErrorNoMem; 933 } 934 935 // We pass JackNoStartServer due to 936 // https://github.com/jackaudio/jack2/issues/138 937 jack_status_t status; 938 sij.client = jack_client_open(soundio.app_name, JackNoStartServer, &status); 939 if (!sij.client) { 940 destroy_jack(si); 941 assert(!(status & JackInvalidOption)); 942 if (status & JackShmFailure) 943 return SoundIoErrorSystemResources; 944 if (status & JackNoSuchClient) 945 return SoundIoErrorNoSuchClient; 946 947 return SoundIoErrorInitAudioBackend; 948 } 949 950 if (auto err = jack_set_buffer_size_callback(sij.client, &buffer_size_callback, si)) { 951 destroy_jack(si); 952 return SoundIoErrorInitAudioBackend; 953 } 954 if (auto err = jack_set_sample_rate_callback(sij.client, &sample_rate_callback, si)) { 955 destroy_jack(si); 956 return SoundIoErrorInitAudioBackend; 957 } 958 if (auto err = jack_set_port_registration_callback(sij.client, &port_registration_callback, si)) { 959 destroy_jack(si); 960 return SoundIoErrorInitAudioBackend; 961 } 962 if (auto err = jack_set_port_rename_callback(sij.client, &port_rename_calllback, si)) { 963 destroy_jack(si); 964 return SoundIoErrorInitAudioBackend; 965 } 966 jack_on_shutdown(sij.client, &shutdown_callback, si); 967 968 SOUNDIO_ATOMIC_FLAG_CLEAR(sij.refresh_devices_flag); 969 sij.period_size = jack_get_buffer_size(sij.client); 970 sij.sample_rate = jack_get_sample_rate(sij.client); 971 972 if (auto err = jack_activate(sij.client)) { 973 destroy_jack(si); 974 return SoundIoErrorInitAudioBackend; 975 } 976 977 if (auto err = refresh_devices(si)) { 978 destroy_jack(si); 979 return err; 980 } 981 982 si.destroy = &destroy_jack; 983 si.flush_events = &flush_events_jack; 984 si.wait_events = &wait_events_jack; 985 si.wakeup = &wakeup_jack; 986 si.force_device_scan = &force_device_scan_jack; 987 988 si.outstream_open = &outstream_open_jack; 989 si.outstream_destroy = &outstream_destroy_jack; 990 si.outstream_start = &outstream_start_jack; 991 si.outstream_begin_write = &outstream_begin_write_jack; 992 si.outstream_end_write = &outstream_end_write_jack; 993 si.outstream_clear_buffer = &outstream_clear_buffer_jack; 994 si.outstream_pause = &outstream_pause_jack; 995 si.outstream_get_latency = &outstream_get_latency_jack; 996 997 si.instream_open = &instream_open_jack; 998 si.instream_destroy = &instream_destroy_jack; 999 si.instream_start = &instream_start_jack; 1000 si.instream_begin_read = &instream_begin_read_jack; 1001 si.instream_end_read = &instream_end_read_jack; 1002 si.instream_pause = &instream_pause_jack; 1003 si.instream_get_latency = &instream_get_latency_jack; 1004 1005 return 0; 1006 }