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 }