1 /// Translated from C to D 2 module sio_sine; 3 4 extern(C): @nogc: nothrow: __gshared: 5 6 import soundio.api; 7 import soundio.util: printf_stderr; 8 9 import core.stdc.stdio; 10 import core.stdc.stdlib; 11 import core.stdc..string; 12 import core.stdc.stdint; 13 import core.stdc.math; 14 15 /// Note: the original C example assumes an amplitude of 1, but that is really loud, 16 /// so I added this constant to make the sound less annoying 17 enum sineAmplitude = 0.25; 18 19 static int usage(char* exe) { 20 printf_stderr("Usage: %s [options]\n" 21 ~ "Options:\n" 22 ~ " [--backend dummy|alsa|pulseaudio|jack|coreaudio|wasapi]\n" 23 ~ " [--device id]\n" 24 ~ " [--raw]\n" 25 ~ " [--name stream_name]\n" 26 ~ " [--latency seconds]\n" 27 ~ " [--sample-rate hz]\n" 28 , exe); 29 return 1; 30 } 31 32 static void write_sample_s16ne(char* ptr, double sample) { 33 short* buf = cast(short*)ptr; 34 double range = cast(double)short.max - cast(double)short.min; 35 double val = sample * range / 2.0; 36 *buf = cast(short) val; 37 } 38 39 static void write_sample_s32ne(char* ptr, double sample) { 40 int* buf = cast(int*)ptr; 41 double range = cast(double)int.max - cast(double)int.min; 42 double val = sample * range / 2.0; 43 *buf = cast(int) val; 44 } 45 46 static void write_sample_float32ne(char* ptr, double sample) { 47 float* buf = cast(float*)ptr; 48 *buf = sample; 49 } 50 51 static void write_sample_float64ne(char* ptr, double sample) { 52 double* buf = cast(double*)ptr; 53 *buf = sample; 54 } 55 56 static void function(char* ptr, double sample) write_sample; 57 static const(double) PI = 3.14159265358979323846264338328; 58 static double seconds_offset = 0.0; 59 static /*volatile*/ bool want_pause = false; 60 static void write_callback(SoundIoOutStream* outstream, int frame_count_min, int frame_count_max) { 61 double float_sample_rate = outstream.sample_rate; 62 double seconds_per_frame = 1.0 / float_sample_rate; 63 SoundIoChannelArea* areas; 64 65 int frames_left = frame_count_max; 66 67 for (;;) { 68 int frame_count = frames_left; 69 if (auto err = soundio_outstream_begin_write(outstream, &areas, &frame_count)) { 70 printf_stderr("unrecoverable stream error: %s\n", soundio_strerror(err)); 71 exit(1); 72 } 73 74 if (!frame_count) 75 break; 76 77 const(SoundIoChannelLayout)* layout = &outstream.layout; 78 79 double pitch = 440.0; 80 double radians_per_second = pitch * 2.0 * PI; 81 for (int frame = 0; frame < frame_count; frame += 1) { 82 double sample = sineAmplitude * sin((seconds_offset + frame * seconds_per_frame) * radians_per_second); 83 for (int channel = 0; channel < layout.channel_count; channel += 1) { 84 write_sample(areas[channel].ptr, sample); 85 areas[channel].ptr += areas[channel].step; 86 } 87 } 88 seconds_offset = fmod(seconds_offset + seconds_per_frame * frame_count, 1.0); 89 90 if (auto err = soundio_outstream_end_write(outstream)) { 91 if (err == SoundIoError.Underflow) 92 return; 93 printf_stderr("unrecoverable stream error: %s\n", soundio_strerror(err)); 94 exit(1); 95 } 96 97 frames_left -= frame_count; 98 if (frames_left <= 0) 99 break; 100 } 101 102 soundio_outstream_pause(outstream, want_pause); 103 } 104 105 static void underflow_callback(SoundIoOutStream* outstream) { 106 static int count = 0; 107 printf_stderr("underflow %d\n", count++); 108 } 109 110 int main(int argc, char** argv) { 111 char* exe = argv[0]; 112 SoundIoBackend backend = SoundIoBackend.None; 113 char* device_id = null; 114 bool raw = false; 115 char* stream_name = null; 116 double latency = 0.0; 117 int sample_rate = 0; 118 for (int i = 1; i < argc; i += 1) { 119 char* arg = argv[i]; 120 if (arg[0] == '-' && arg[1] == '-') { 121 if (strcmp(arg, "--raw") == 0) { 122 raw = true; 123 } else { 124 i += 1; 125 if (i >= argc) { 126 return usage(exe); 127 } else if (strcmp(arg, "--backend") == 0) { 128 if (strcmp(argv[i], "dummy") == 0) { 129 backend = SoundIoBackend.Dummy; 130 } else if (strcmp(argv[i], "alsa") == 0) { 131 backend = SoundIoBackend.Alsa; 132 } else if (strcmp(argv[i], "pulseaudio") == 0) { 133 backend = SoundIoBackend.PulseAudio; 134 } else if (strcmp(argv[i], "jack") == 0) { 135 backend = SoundIoBackend.Jack; 136 } else if (strcmp(argv[i], "coreaudio") == 0) { 137 backend = SoundIoBackend.CoreAudio; 138 } else if (strcmp(argv[i], "wasapi") == 0) { 139 backend = SoundIoBackend.Wasapi; 140 } else { 141 printf_stderr("Invalid backend: %s\n", argv[i]); 142 return 1; 143 } 144 } else if (strcmp(arg, "--device") == 0) { 145 device_id = argv[i]; 146 } else if (strcmp(arg, "--name") == 0) { 147 stream_name = argv[i]; 148 } else if (strcmp(arg, "--latency") == 0) { 149 latency = atof(argv[i]); 150 } else if (strcmp(arg, "--sample-rate") == 0) { 151 sample_rate = atoi(argv[i]); 152 } else { 153 return usage(exe); 154 } 155 } 156 } else { 157 return usage(exe); 158 } 159 } 160 161 SoundIo* soundio = soundio_create(); 162 if (!soundio) { 163 printf_stderr("out of memory\n"); 164 return 1; 165 } 166 167 if (auto err = (backend == SoundIoBackend.None) ? soundio_connect(soundio) : soundio_connect_backend(soundio, backend)) { 168 printf_stderr("Unable to connect to backend: %s\n", soundio_strerror(err)); 169 return 1; 170 } 171 172 printf_stderr("Backend: %s\n", soundio_backend_name(soundio.current_backend)); 173 174 soundio_flush_events(soundio); 175 176 int selected_device_index = -1; 177 if (device_id) { 178 int device_count = soundio_output_device_count(soundio); 179 for (int i = 0; i < device_count; i += 1) { 180 SoundIoDevice* device = soundio_get_output_device(soundio, i); 181 bool select_this_one = strcmp(device.id, device_id) == 0 && device.is_raw == raw; 182 soundio_device_unref(device); 183 if (select_this_one) { 184 selected_device_index = i; 185 break; 186 } 187 } 188 } else { 189 selected_device_index = soundio_default_output_device_index(soundio); 190 } 191 192 if (selected_device_index < 0) { 193 printf_stderr("Output device not found\n"); 194 return 1; 195 } 196 197 SoundIoDevice* device = soundio_get_output_device(soundio, selected_device_index); 198 if (!device) { 199 printf_stderr("out of memory\n"); 200 return 1; 201 } 202 203 printf_stderr("Output device: %s\n", device.name); 204 205 if (device.probe_error) { 206 printf_stderr("Cannot probe device: %s\n", soundio_strerror(device.probe_error)); 207 return 1; 208 } 209 210 SoundIoOutStream* outstream = soundio_outstream_create(device); 211 if (!outstream) { 212 printf_stderr("out of memory\n"); 213 return 1; 214 } 215 216 outstream.write_callback = &write_callback; 217 outstream.underflow_callback = &underflow_callback; 218 outstream.name = stream_name; 219 outstream.software_latency = latency; 220 outstream.sample_rate = sample_rate; 221 222 if (soundio_device_supports_format(device, SoundIoFormatFloat32NE)) { 223 outstream.format = SoundIoFormatFloat32NE; 224 write_sample = &write_sample_float32ne; 225 } else if (soundio_device_supports_format(device, SoundIoFormatFloat64NE)) { 226 outstream.format = SoundIoFormatFloat64NE; 227 write_sample = &write_sample_float64ne; 228 } else if (soundio_device_supports_format(device, SoundIoFormatS32NE)) { 229 outstream.format = SoundIoFormatS32NE; 230 write_sample = &write_sample_s32ne; 231 } else if (soundio_device_supports_format(device, SoundIoFormatS16NE)) { 232 outstream.format = SoundIoFormatS16NE; 233 write_sample = &write_sample_s16ne; 234 } else { 235 printf_stderr("No suitable device format available.\n"); 236 return 1; 237 } 238 239 if (auto err = soundio_outstream_open(outstream)) { 240 printf_stderr("unable to open device: %s", soundio_strerror(err)); 241 return 1; 242 } 243 244 printf_stderr("Software latency: %f\n", outstream.software_latency); 245 printf_stderr( 246 "'p\\n' - pause\n" 247 ~ "'u\\n' - unpause\n" 248 ~ "'P\\n' - pause from within callback\n" 249 ~ "'c\\n' - clear buffer\n" 250 ~ "'q\\n' - quit\n"); 251 252 if (outstream.layout_error) 253 printf_stderr("unable to set channel layout: %s\n", soundio_strerror(outstream.layout_error)); 254 255 if (auto err = soundio_outstream_start(outstream)) { 256 printf_stderr("unable to start device: %s\n", soundio_strerror(err)); 257 return 1; 258 } 259 260 for (;;) { 261 soundio_flush_events(soundio); 262 version(CRuntime_Microsoft) { 263 int c = 0; // stdin not initialized 264 if (c == 0) continue; 265 } else { 266 int c = getc(stdin); 267 } 268 if (c == 'p') { 269 printf_stderr("pausing result: %s\n", 270 soundio_strerror(soundio_outstream_pause(outstream, true))); 271 } else if (c == 'P') { 272 want_pause = true; 273 } else if (c == 'u') { 274 want_pause = false; 275 printf_stderr("unpausing result: %s\n", 276 soundio_strerror(soundio_outstream_pause(outstream, false))); 277 } else if (c == 'c') { 278 printf_stderr("clear buffer result: %s\n", 279 soundio_strerror(soundio_outstream_clear_buffer(outstream))); 280 } else if (c == 'q') { 281 break; 282 } else if (c == '\r' || c == '\n') { 283 // ignore 284 } else { 285 printf_stderr("Unrecognized command: %c\n", c); 286 } 287 } 288 289 soundio_outstream_destroy(outstream); 290 soundio_device_unref(device); 291 soundio_destroy(soundio); 292 return 0; 293 }