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 }