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