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