1 /// Translated from C to D
2 module soundio.dummy;
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.api;
10 import soundio.atomics;
11 import soundio.soundio_private;
12 
13 import soundio.ring_buffer: SoundIoRingBufferImpl, soundio_ring_buffer_deinit, soundio_ring_buffer_init;
14 import core.stdc.stdio;
15 import core.stdc..string;
16 import core.atomic;
17 
18 struct SoundIoDummy {
19     SoundIoOsMutex* mutex;
20     SoundIoOsCond* cond;
21     bool devices_emitted;
22 }
23 
24 struct SoundIoDeviceDummy {
25     int make_the_struct_not_empty;
26 }
27 
28 struct SoundIoOutStreamDummy {
29     SoundIoOsThread* thread;
30     SoundIoOsCond* cond;
31     SoundIoAtomicFlag abort_flag;
32     double period_duration;
33     int buffer_frame_count;
34     int frames_left;
35     int write_frame_count;
36     SoundIoRingBufferImpl ring_buffer;
37     auto ring_buffer_ptr() {return cast(SoundIoRingBuffer*) &ring_buffer;}
38     double playback_start_time;
39     SoundIoAtomicFlag clear_buffer_flag;
40     SoundIoAtomicBool pause_requested;
41     SoundIoChannelArea[SOUNDIO_MAX_CHANNELS] areas;
42 }
43 
44 struct SoundIoInStreamDummy {
45     SoundIoOsThread* thread;
46     SoundIoOsCond* cond;
47     SoundIoAtomicFlag abort_flag;
48     double period_duration;
49     int frames_left;
50     int read_frame_count;
51     int buffer_frame_count;
52     SoundIoRingBufferImpl ring_buffer;
53     auto ring_buffer_ptr() {return cast(SoundIoRingBuffer*) &ring_buffer;}
54     SoundIoAtomicBool pause_requested;
55     SoundIoChannelArea[SOUNDIO_MAX_CHANNELS] areas;
56 }
57 
58 static void playback_thread_run(void* arg) {
59     SoundIoOutStreamPrivate* os = cast(SoundIoOutStreamPrivate*)arg;
60     SoundIoOutStream* outstream = &os.pub;
61     SoundIoOutStreamDummy* osd = &os.backend_data.dummy;
62 
63     {
64         int fill_bytes = soundio_ring_buffer_fill_count(osd.ring_buffer_ptr);
65         int free_bytes = soundio_ring_buffer_capacity(osd.ring_buffer_ptr) - fill_bytes;
66         int free_frames = free_bytes / outstream.bytes_per_frame;
67         osd.frames_left = free_frames;
68         if (free_frames > 0)
69             outstream.write_callback(outstream, 0, free_frames);
70     }
71     double start_time = soundio_os_get_time();
72     c_long frames_consumed = 0;
73 
74     while (SOUNDIO_ATOMIC_FLAG_TEST_AND_SET(osd.abort_flag)) {
75         double now = soundio_os_get_time();
76         double time_passed = now - start_time;
77         double next_period = start_time +
78             ceil_dbl(time_passed / osd.period_duration) * osd.period_duration;
79         double relative_time = next_period - now;
80         soundio_os_cond_timed_wait(osd.cond, null, relative_time);
81         if (!SOUNDIO_ATOMIC_FLAG_TEST_AND_SET(osd.clear_buffer_flag)) {
82             soundio_ring_buffer_clear(osd.ring_buffer_ptr);
83             int free_bytes = soundio_ring_buffer_capacity(osd.ring_buffer_ptr);
84             int free_frames = free_bytes / outstream.bytes_per_frame;
85             osd.frames_left = free_frames;
86             if (free_frames > 0)
87                 outstream.write_callback(outstream, 0, free_frames);
88             frames_consumed = 0;
89             start_time = soundio_os_get_time();
90             continue;
91         }
92 
93         if (SOUNDIO_ATOMIC_LOAD(osd.pause_requested)) {
94             start_time = now;
95             frames_consumed = 0;
96             continue;
97         }
98 
99         int fill_bytes = soundio_ring_buffer_fill_count(osd.ring_buffer_ptr);
100         int fill_frames = fill_bytes / outstream.bytes_per_frame;
101         int free_bytes = soundio_ring_buffer_capacity(osd.ring_buffer_ptr) - fill_bytes;
102         int free_frames = free_bytes / outstream.bytes_per_frame;
103 
104         double total_time = soundio_os_get_time() - start_time;
105         c_long total_frames = cast(c_long) (total_time * outstream.sample_rate);
106         int frames_to_kill = cast(int) (total_frames - frames_consumed);
107         int read_count = soundio_int_min(frames_to_kill, fill_frames);
108         int byte_count = read_count * outstream.bytes_per_frame;
109         soundio_ring_buffer_advance_read_ptr(osd.ring_buffer_ptr, byte_count);
110         frames_consumed += read_count;
111 
112         if (frames_to_kill > fill_frames) {
113             outstream.underflow_callback(outstream);
114             osd.frames_left = free_frames;
115             if (free_frames > 0)
116                 outstream.write_callback(outstream, 0, free_frames);
117             frames_consumed = 0;
118             start_time = soundio_os_get_time();
119         } else if (free_frames > 0) {
120             osd.frames_left = free_frames;
121             outstream.write_callback(outstream, 0, free_frames);
122         }
123     }
124 }
125 
126 static void capture_thread_run(void* arg) {
127     SoundIoInStreamPrivate* is_ = cast(SoundIoInStreamPrivate*)arg;
128     SoundIoInStream* instream = &is_.pub;
129     SoundIoInStreamDummy* isd = &is_.backend_data.dummy;
130 
131     c_long frames_consumed = 0;
132     double start_time = soundio_os_get_time();
133     while (SOUNDIO_ATOMIC_FLAG_TEST_AND_SET(isd.abort_flag)) {
134         double now = soundio_os_get_time();
135         double time_passed = now - start_time;
136         double next_period = start_time +
137             ceil_dbl(time_passed / isd.period_duration) * isd.period_duration;
138         double relative_time = next_period - now;
139         soundio_os_cond_timed_wait(isd.cond, null, relative_time);
140 
141         if (SOUNDIO_ATOMIC_LOAD(isd.pause_requested)) {
142             start_time = now;
143             frames_consumed = 0;
144             continue;
145         }
146 
147         int fill_bytes = soundio_ring_buffer_fill_count(isd.ring_buffer_ptr);
148         int free_bytes = soundio_ring_buffer_capacity(isd.ring_buffer_ptr) - fill_bytes;
149         int fill_frames = fill_bytes / instream.bytes_per_frame;
150         int free_frames = free_bytes / instream.bytes_per_frame;
151 
152         double total_time = soundio_os_get_time() - start_time;
153         c_long total_frames = cast(c_long) (total_time * instream.sample_rate);
154         int frames_to_kill = cast(int) (total_frames - frames_consumed);
155         int write_count = soundio_int_min(frames_to_kill, free_frames);
156         int byte_count = write_count * instream.bytes_per_frame;
157         soundio_ring_buffer_advance_write_ptr(isd.ring_buffer_ptr, byte_count);
158         frames_consumed += write_count;
159 
160         if (frames_to_kill > free_frames) {
161             instream.overflow_callback(instream);
162             frames_consumed = 0;
163             start_time = soundio_os_get_time();
164         }
165         if (fill_frames > 0) {
166             isd.frames_left = fill_frames;
167             instream.read_callback(instream, 0, fill_frames);
168         }
169     }
170 }
171 
172 static void destroy_dummy(SoundIoPrivate* si) {
173     SoundIoDummy* sid = &si.backend_data.dummy;
174 
175     if (sid.cond)
176         soundio_os_cond_destroy(sid.cond);
177 
178     if (sid.mutex)
179         soundio_os_mutex_destroy(sid.mutex);
180 }
181 
182 static void flush_events_dummy(SoundIoPrivate* si) {
183     SoundIo* soundio = &si.pub;
184     SoundIoDummy* sid = &si.backend_data.dummy;
185     if (sid.devices_emitted)
186         return;
187     sid.devices_emitted = true;
188     soundio.on_devices_change(soundio);
189 }
190 
191 static void wait_events_dummy(SoundIoPrivate* si) {
192     SoundIoDummy* sid = &si.backend_data.dummy;
193     flush_events_dummy(si);
194     soundio_os_cond_wait(sid.cond, null);
195 }
196 
197 static void wakeup_dummy(SoundIoPrivate* si) {
198     SoundIoDummy* sid = &si.backend_data.dummy;
199     soundio_os_cond_signal(sid.cond, null);
200 }
201 
202 static void force_device_scan_dummy(SoundIoPrivate* si) {
203     // nothing to do; dummy devices never change
204 }
205 
206 static void outstream_destroy_dummy(SoundIoPrivate* si, SoundIoOutStreamPrivate* os) {
207     SoundIoOutStreamDummy* osd = &os.backend_data.dummy;
208 
209     if (osd.thread) {
210         SOUNDIO_ATOMIC_FLAG_CLEAR(osd.abort_flag);
211         soundio_os_cond_signal(osd.cond, null);
212         soundio_os_thread_destroy(osd.thread);
213         osd.thread = null;
214     }
215     soundio_os_cond_destroy(osd.cond);
216     osd.cond = null;
217 
218     soundio_ring_buffer_deinit(osd.ring_buffer_ptr);
219 }
220 
221 static int outstream_open_dummy(SoundIoPrivate* si, SoundIoOutStreamPrivate* os) {
222     SoundIoOutStreamDummy* osd = &os.backend_data.dummy;
223     SoundIoOutStream* outstream = &os.pub;
224     SoundIoDevice* device = outstream.device;
225 
226     SOUNDIO_ATOMIC_FLAG_TEST_AND_SET(osd.clear_buffer_flag);
227     SOUNDIO_ATOMIC_STORE(osd.pause_requested, false);
228 
229     if (outstream.software_latency == 0.0) {
230         outstream.software_latency = soundio_double_clamp(
231                 device.software_latency_min, 1.0, device.software_latency_max);
232     }
233 
234     osd.period_duration = outstream.software_latency / 2.0;
235 
236     int buffer_size = cast(int) (outstream.bytes_per_frame * outstream.sample_rate * outstream.software_latency);
237     if (auto err = soundio_ring_buffer_init(osd.ring_buffer_ptr, buffer_size)) {
238         outstream_destroy_dummy(si, os);
239         return err;
240     }
241     int actual_capacity = soundio_ring_buffer_capacity(osd.ring_buffer_ptr);
242     osd.buffer_frame_count = actual_capacity / outstream.bytes_per_frame;
243     outstream.software_latency = osd.buffer_frame_count / cast(double) outstream.sample_rate;
244 
245     osd.cond = soundio_os_cond_create();
246     if (!osd.cond) {
247         outstream_destroy_dummy(si, os);
248         return SoundIoError.NoMem;
249     }
250 
251     return 0;
252 }
253 
254 static int outstream_pause_dummy(SoundIoPrivate* si, SoundIoOutStreamPrivate* os, bool pause) {
255     SoundIoOutStreamDummy* osd = &os.backend_data.dummy;
256     SOUNDIO_ATOMIC_STORE(osd.pause_requested, pause);
257     return 0;
258 }
259 
260 static int outstream_start_dummy(SoundIoPrivate* si, SoundIoOutStreamPrivate* os) {
261     SoundIoOutStreamDummy* osd = &os.backend_data.dummy;
262     SoundIo* soundio = &si.pub;
263     assert(!osd.thread);
264     SOUNDIO_ATOMIC_FLAG_TEST_AND_SET(osd.abort_flag);
265     if (auto err = soundio_os_thread_create(&playback_thread_run, os,
266                     soundio.emit_rtprio_warning, &osd.thread))
267     {
268         return err;
269     }
270     return 0;
271 }
272 
273 static int outstream_begin_write_dummy(SoundIoPrivate* si, SoundIoOutStreamPrivate* os, SoundIoChannelArea** out_areas, int* frame_count) {
274     SoundIoOutStream* outstream = &os.pub;
275     SoundIoOutStreamDummy* osd = &os.backend_data.dummy;
276 
277     if (*frame_count > osd.frames_left)
278         return SoundIoError.Invalid;
279 
280     char* write_ptr = soundio_ring_buffer_write_ptr(osd.ring_buffer_ptr);
281     for (int ch = 0; ch < outstream.layout.channel_count; ch += 1) {
282         osd.areas[ch].ptr = write_ptr + outstream.bytes_per_sample * ch;
283         osd.areas[ch].step = outstream.bytes_per_frame;
284     }
285 
286     osd.write_frame_count = *frame_count;
287     *out_areas = osd.areas.ptr;
288     return 0;
289 }
290 
291 static int outstream_end_write_dummy(SoundIoPrivate* si, SoundIoOutStreamPrivate* os) {
292     SoundIoOutStreamDummy* osd = &os.backend_data.dummy;
293     SoundIoOutStream* outstream = &os.pub;
294     int byte_count = osd.write_frame_count * outstream.bytes_per_frame;
295     soundio_ring_buffer_advance_write_ptr(osd.ring_buffer_ptr, byte_count);
296     osd.frames_left -= osd.write_frame_count;
297     return 0;
298 }
299 
300 static int outstream_clear_buffer_dummy(SoundIoPrivate* si, SoundIoOutStreamPrivate* os) {
301     SoundIoOutStreamDummy* osd = &os.backend_data.dummy;
302     SOUNDIO_ATOMIC_FLAG_CLEAR(osd.clear_buffer_flag);
303     soundio_os_cond_signal(osd.cond, null);
304     return 0;
305 }
306 
307 static int outstream_get_latency_dummy(SoundIoPrivate* si, SoundIoOutStreamPrivate* os, double* out_latency) {
308     SoundIoOutStream* outstream = &os.pub;
309     SoundIoOutStreamDummy* osd = &os.backend_data.dummy;
310     int fill_bytes = soundio_ring_buffer_fill_count(osd.ring_buffer_ptr);
311 
312     *out_latency = (fill_bytes / outstream.bytes_per_frame) / cast(double)outstream.sample_rate;
313     return 0;
314 }
315 
316 static void instream_destroy_dummy(SoundIoPrivate* si, SoundIoInStreamPrivate* is_) {
317     SoundIoInStreamDummy* isd = &is_.backend_data.dummy;
318 
319     if (isd.thread) {
320         SOUNDIO_ATOMIC_FLAG_CLEAR(isd.abort_flag);
321         soundio_os_cond_signal(isd.cond, null);
322         soundio_os_thread_destroy(isd.thread);
323         isd.thread = null;
324     }
325     soundio_os_cond_destroy(isd.cond);
326     isd.cond = null;
327 
328     soundio_ring_buffer_deinit(isd.ring_buffer_ptr);
329 }
330 
331 static int instream_open_dummy(SoundIoPrivate* si, SoundIoInStreamPrivate* is_) {
332     SoundIoInStreamDummy* isd = &is_.backend_data.dummy;
333     SoundIoInStream* instream = &is_.pub;
334     SoundIoDevice* device = instream.device;
335 
336     SOUNDIO_ATOMIC_STORE(isd.pause_requested, false);
337 
338     if (instream.software_latency == 0.0) {
339         instream.software_latency = soundio_double_clamp(
340                 device.software_latency_min, 1.0, device.software_latency_max);
341     }
342 
343     isd.period_duration = instream.software_latency;
344 
345     double target_buffer_duration = isd.period_duration * 4.0;
346 
347     int buffer_size = cast(int) (instream.bytes_per_frame * instream.sample_rate * target_buffer_duration);
348     if (auto err = soundio_ring_buffer_init(isd.ring_buffer_ptr, buffer_size)) {
349         instream_destroy_dummy(si, is_);
350         return err;
351     }
352 
353     int actual_capacity = soundio_ring_buffer_capacity(isd.ring_buffer_ptr);
354     isd.buffer_frame_count = actual_capacity / instream.bytes_per_frame;
355 
356     isd.cond = soundio_os_cond_create();
357     if (!isd.cond) {
358         instream_destroy_dummy(si, is_);
359         return SoundIoError.NoMem;
360     }
361 
362     return 0;
363 }
364 
365 static int instream_pause_dummy(SoundIoPrivate* si, SoundIoInStreamPrivate* is_, bool pause) {
366     SoundIoInStreamDummy* isd = &is_.backend_data.dummy;
367     SOUNDIO_ATOMIC_STORE(isd.pause_requested, pause);
368     return 0;
369 }
370 
371 static int instream_start_dummy(SoundIoPrivate* si, SoundIoInStreamPrivate* is_) {
372     SoundIoInStreamDummy* isd = &is_.backend_data.dummy;
373     SoundIo* soundio = &si.pub;
374     assert(!isd.thread);
375     SOUNDIO_ATOMIC_FLAG_TEST_AND_SET(isd.abort_flag);
376     if (auto err = soundio_os_thread_create(&capture_thread_run, is_,
377                     soundio.emit_rtprio_warning, &isd.thread))
378     {
379         return err;
380     }
381     return 0;
382 }
383 
384 static int instream_begin_read_dummy(SoundIoPrivate* si, SoundIoInStreamPrivate* is_, SoundIoChannelArea** out_areas, int* frame_count) {
385     SoundIoInStream* instream = &is_.pub;
386     SoundIoInStreamDummy* isd = &is_.backend_data.dummy;
387 
388     assert(*frame_count <= isd.frames_left);
389 
390     char* read_ptr = soundio_ring_buffer_read_ptr(isd.ring_buffer_ptr);
391     for (int ch = 0; ch < instream.layout.channel_count; ch += 1) {
392         isd.areas[ch].ptr = read_ptr + instream.bytes_per_sample * ch;
393         isd.areas[ch].step = instream.bytes_per_frame;
394     }
395 
396     isd.read_frame_count = *frame_count;
397     *out_areas = isd.areas.ptr;
398 
399     return 0;
400 }
401 
402 static int instream_end_read_dummy(SoundIoPrivate* si, SoundIoInStreamPrivate* is_) {
403     SoundIoInStreamDummy* isd = &is_.backend_data.dummy;
404     SoundIoInStream* instream = &is_.pub;
405     int byte_count = isd.read_frame_count * instream.bytes_per_frame;
406     soundio_ring_buffer_advance_read_ptr(isd.ring_buffer_ptr, byte_count);
407     isd.frames_left -= isd.read_frame_count;
408     return 0;
409 }
410 
411 static int instream_get_latency_dummy(SoundIoPrivate* si, SoundIoInStreamPrivate* is_, double* out_latency) {
412     SoundIoInStream* instream = &is_.pub;
413     SoundIoInStreamDummy* osd = &is_.backend_data.dummy;
414     int fill_bytes = soundio_ring_buffer_fill_count(osd.ring_buffer_ptr);
415 
416     *out_latency = (fill_bytes / instream.bytes_per_frame) / cast(double)instream.sample_rate;
417     return 0;
418 }
419 
420 extern(D) int set_all_device_formats(SoundIoDevice* device) {
421     device.format_count = 18;
422     device.formats = ALLOCATE!SoundIoFormat(device.format_count);
423     if (!device.formats)
424         return SoundIoError.NoMem;
425 
426     device.formats[0] = SoundIoFormat.Float32NE;
427     device.formats[1] = SoundIoFormat.Float32FE;
428     device.formats[2] = SoundIoFormat.S32NE;
429     device.formats[3] = SoundIoFormat.S32FE;
430     device.formats[4] = SoundIoFormat.U32NE;
431     device.formats[5] = SoundIoFormat.U32FE;
432     device.formats[6] = SoundIoFormat.S24NE;
433     device.formats[7] = SoundIoFormat.S24FE;
434     device.formats[8] = SoundIoFormat.U24NE;
435     device.formats[9] = SoundIoFormat.U24FE;
436     device.formats[10] = SoundIoFormat.Float64NE;
437     device.formats[11] = SoundIoFormat.Float64FE;
438     device.formats[12] = SoundIoFormat.S16NE;
439     device.formats[13] = SoundIoFormat.S16FE;
440     device.formats[14] = SoundIoFormat.U16NE;
441     device.formats[15] = SoundIoFormat.U16FE;
442     device.formats[16] = SoundIoFormat.S8;
443     device.formats[17] = SoundIoFormat.U8;
444 
445     return 0;
446 }
447 
448 extern(D) void set_all_device_sample_rates(SoundIoDevice* device) {
449     SoundIoDevicePrivate* dev = cast(SoundIoDevicePrivate*)device;
450     device.sample_rate_count = 1;
451     device.sample_rates = &dev.prealloc_sample_rate_range;
452     device.sample_rates[0].min = SOUNDIO_MIN_SAMPLE_RATE;
453     device.sample_rates[0].max = SOUNDIO_MAX_SAMPLE_RATE;
454 }
455 
456 extern(D) int set_all_device_channel_layouts(SoundIoDevice* device) {
457     device.layout_count = soundio_channel_layout_builtin_count();
458     device.layouts = ALLOCATE!SoundIoChannelLayout(device.layout_count);
459     if (!device.layouts)
460         return SoundIoError.NoMem;
461     for (int i = 0; i < device.layout_count; i += 1)
462         device.layouts[i] = *soundio_channel_layout_get_builtin(i);
463     return 0;
464 }
465 
466 int soundio_dummy_init(SoundIoPrivate* si) {
467     SoundIo* soundio = &si.pub;
468     SoundIoDummy* sid = &si.backend_data.dummy;
469 
470     sid.mutex = soundio_os_mutex_create();
471     if (!sid.mutex) {
472         destroy_dummy(si);
473         return SoundIoError.NoMem;
474     }
475 
476     sid.cond = soundio_os_cond_create();
477     if (!sid.cond) {
478         destroy_dummy(si);
479         return SoundIoError.NoMem;
480     }
481 
482     assert(!si.safe_devices_info);
483     si.safe_devices_info = ALLOCATE!SoundIoDevicesInfo(1);
484     if (!si.safe_devices_info) {
485         destroy_dummy(si);
486         return SoundIoError.NoMem;
487     }
488 
489     si.safe_devices_info.default_input_index = 0;
490     si.safe_devices_info.default_output_index = 0;
491 
492     // create output device
493     {
494         SoundIoDevicePrivate* dev = ALLOCATE!SoundIoDevicePrivate(1);
495         if (!dev) {
496             destroy_dummy(si);
497             return SoundIoError.NoMem;
498         }
499         SoundIoDevice* device = &dev.pub;
500 
501         device.ref_count = 1;
502         device.soundio = soundio;
503         device.id = strdup("dummy-out");
504         device.name = strdup("Dummy Output Device");
505         if (!device.id || !device.name) {
506             soundio_device_unref(device);
507             destroy_dummy(si);
508             return SoundIoError.NoMem;
509         }
510 
511         if (auto err = set_all_device_channel_layouts(device)) {
512             soundio_device_unref(device);
513             destroy_dummy(si);
514             return err;
515         }
516         if (auto err = set_all_device_formats(device)) {
517             soundio_device_unref(device);
518             destroy_dummy(si);
519             return err;
520         }
521         set_all_device_sample_rates(device);
522 
523         device.software_latency_current = 0.1;
524         device.software_latency_min = 0.01;
525         device.software_latency_max = 4.0;
526 
527         device.sample_rate_current = 48000;
528         device.aim = SoundIoDeviceAim.Output;
529 
530         if (si.safe_devices_info.output_devices.append(device)) {
531             soundio_device_unref(device);
532             destroy_dummy(si);
533             return SoundIoError.NoMem;
534         }
535     }
536 
537     // create input device
538     {
539         SoundIoDevicePrivate* dev = ALLOCATE!SoundIoDevicePrivate(1);
540         if (!dev) {
541             destroy_dummy(si);
542             return SoundIoError.NoMem;
543         }
544         SoundIoDevice* device = &dev.pub;
545 
546         device.ref_count = 1;
547         device.soundio = soundio;
548         device.id = strdup("dummy-in");
549         device.name = strdup("Dummy Input Device");
550         if (!device.id || !device.name) {
551             soundio_device_unref(device);
552             destroy_dummy(si);
553             return SoundIoError.NoMem;
554         }
555 
556         if (auto err = set_all_device_channel_layouts(device)) {
557             soundio_device_unref(device);
558             destroy_dummy(si);
559             return err;
560         }
561 
562         if (auto err = set_all_device_formats(device)) {
563             soundio_device_unref(device);
564             destroy_dummy(si);
565             return err;
566         }
567         set_all_device_sample_rates(device);
568         device.software_latency_current = 0.1;
569         device.software_latency_min = 0.01;
570         device.software_latency_max = 4.0;
571         device.sample_rate_current = 48000;
572         device.aim = SoundIoDeviceAim.Input;
573 
574         if (si.safe_devices_info.input_devices.append(device)) {
575             soundio_device_unref(device);
576             destroy_dummy(si);
577             return SoundIoError.NoMem;
578         }
579     }
580 
581     si.destroy = &destroy_dummy;
582     si.flush_events = &flush_events_dummy;
583     si.wait_events = &wait_events_dummy;
584     si.wakeup = &wakeup_dummy;
585     si.force_device_scan = &force_device_scan_dummy;
586 
587     si.outstream_open = &outstream_open_dummy;
588     si.outstream_destroy = &outstream_destroy_dummy;
589     si.outstream_start = &outstream_start_dummy;
590     si.outstream_begin_write = &outstream_begin_write_dummy;
591     si.outstream_end_write = &outstream_end_write_dummy;
592     si.outstream_clear_buffer = &outstream_clear_buffer_dummy;
593     si.outstream_pause = &outstream_pause_dummy;
594     si.outstream_get_latency = &outstream_get_latency_dummy;
595 
596     si.instream_open = &instream_open_dummy;
597     si.instream_destroy = &instream_destroy_dummy;
598     si.instream_start = &instream_start_dummy;
599     si.instream_begin_read = &instream_begin_read_dummy;
600     si.instream_end_read = &instream_end_read_dummy;
601     si.instream_pause = &instream_pause_dummy;
602     si.instream_get_latency = &instream_get_latency_dummy;
603 
604     return 0;
605 }