1 /// Translated from C to D 2 module soundio.alsa; 3 4 extern(C): @nogc: nothrow: __gshared: 5 6 import soundio.soundio_internal; 7 import soundio.soundio_private; 8 import soundio.os; 9 import soundio.list; 10 import soundio.atomics; 11 import soundio.headers.alsaheader; 12 13 import core.sys.linux.sys.inotify; 14 import core.sys.posix.fcntl; 15 import core.sys.posix.unistd; 16 import core.sys.posix.poll; //pollfd, POLLERR etc. 17 import core.stdc.errno; 18 import core.stdc..string: strcmp, strncmp, strlen, strstr, strdup; 19 import core.stdc.stdlib: free; 20 21 private: 22 23 static if (__VERSION__ < 2094) { 24 // @nogc inotify headers 25 int inotify_init(); 26 int inotify_init1(int flags); 27 int inotify_add_watch(int fd, const(char)* name, uint mask); 28 int inotify_rm_watch(int fd, uint wd); 29 } 30 // in unistd.h, missing from core.sys.posix.unistd 31 int pipe2(int* __pipedes, int __flags); 32 33 package struct SoundIoDeviceAlsa { int make_the_struct_not_empty; } 34 35 enum SOUNDIO_MAX_ALSA_SND_FILE_LEN = 16; 36 struct SoundIoAlsaPendingFile { 37 char[SOUNDIO_MAX_ALSA_SND_FILE_LEN] name; 38 } 39 40 alias SoundIoListAlsaPendingFile = SOUNDIO_LIST!SoundIoAlsaPendingFile; 41 42 package struct SoundIoAlsa { 43 SoundIoOsMutex* mutex; 44 SoundIoOsCond* cond; 45 46 SoundIoOsThread* thread; 47 SoundIoAtomicFlag abort_flag; 48 int notify_fd; 49 int notify_wd; 50 bool have_devices_flag; 51 int[2] notify_pipe_fd; 52 SoundIoListAlsaPendingFile pending_files; 53 54 // this one is ready to be read with flush_events. protected by mutex 55 SoundIoDevicesInfo* ready_devices_info; 56 57 int shutdown_err; 58 bool emitted_shutdown_cb; 59 } 60 61 package struct SoundIoOutStreamAlsa { 62 snd_pcm_t* handle; 63 snd_pcm_chmap_t* chmap; 64 int chmap_size; 65 snd_pcm_uframes_t offset; 66 snd_pcm_access_t access; 67 snd_pcm_uframes_t buffer_size_frames; 68 int sample_buffer_size; 69 char* sample_buffer; 70 int poll_fd_count; 71 int poll_fd_count_with_extra; 72 pollfd* poll_fds; 73 int[2] poll_exit_pipe_fd; 74 SoundIoOsThread* thread; 75 SoundIoAtomicFlag thread_exit_flag; 76 snd_pcm_uframes_t period_size; 77 int write_frame_count; 78 bool is_paused; 79 SoundIoAtomicFlag clear_buffer_flag; 80 SoundIoChannelArea[SOUNDIO_MAX_CHANNELS] areas; 81 } 82 83 package struct SoundIoInStreamAlsa { 84 snd_pcm_t* handle; 85 snd_pcm_chmap_t* chmap; 86 int chmap_size; 87 snd_pcm_uframes_t offset; 88 snd_pcm_access_t access; 89 int sample_buffer_size; 90 char* sample_buffer; 91 int poll_fd_count; 92 pollfd* poll_fds; 93 SoundIoOsThread* thread; 94 SoundIoAtomicFlag thread_exit_flag; 95 int period_size; 96 int read_frame_count; 97 bool is_paused; 98 SoundIoChannelArea[SOUNDIO_MAX_CHANNELS] areas; 99 } 100 101 immutable snd_pcm_stream_t[2] stream_types = [SND_PCM_STREAM_PLAYBACK, SND_PCM_STREAM_CAPTURE]; 102 103 static snd_pcm_access_t[5] prioritized_access_types = [ 104 SND_PCM_ACCESS_MMAP_INTERLEAVED, 105 SND_PCM_ACCESS_MMAP_NONINTERLEAVED, 106 SND_PCM_ACCESS_MMAP_COMPLEX, 107 SND_PCM_ACCESS_RW_INTERLEAVED, 108 SND_PCM_ACCESS_RW_NONINTERLEAVED, 109 ]; 110 111 static void wakeup_device_poll(SoundIoAlsa* sia) { 112 ssize_t amt = write(sia.notify_pipe_fd[1], "a".ptr, 1); 113 if (amt == -1) { 114 assert(errno != EBADF); 115 assert(errno != EIO); 116 assert(errno != ENOSPC); 117 assert(errno != EPERM); 118 assert(errno != EPIPE); 119 } 120 } 121 122 static void wakeup_outstream_poll(SoundIoOutStreamAlsa* osa) { 123 ssize_t amt = write(osa.poll_exit_pipe_fd[1], "a".ptr, 1); 124 if (amt == -1) { 125 assert(errno != EBADF); 126 assert(errno != EIO); 127 assert(errno != ENOSPC); 128 assert(errno != EPERM); 129 assert(errno != EPIPE); 130 } 131 } 132 133 static void destroy_alsa(SoundIoPrivate* si) { 134 SoundIoAlsa* sia = &si.backend_data.alsa; 135 136 if (sia.thread) { 137 SOUNDIO_ATOMIC_FLAG_CLEAR(sia.abort_flag); 138 wakeup_device_poll(sia); 139 soundio_os_thread_destroy(sia.thread); 140 } 141 142 sia.pending_files.deinit(); 143 144 if (sia.cond) 145 soundio_os_cond_destroy(sia.cond); 146 147 if (sia.mutex) 148 soundio_os_mutex_destroy(sia.mutex); 149 150 soundio_destroy_devices_info(sia.ready_devices_info); 151 152 153 154 close(sia.notify_pipe_fd[0]); 155 close(sia.notify_pipe_fd[1]); 156 close(sia.notify_fd); 157 } 158 159 pragma(inline, true) static snd_pcm_uframes_t ceil_dbl_to_uframes(double x) { 160 const(double) truncation = cast(snd_pcm_uframes_t)x; 161 return cast(snd_pcm_uframes_t) (truncation + (truncation < x)); 162 } 163 164 static char* str_partition_on_char(char* str, char c) { 165 if (!str) 166 return null; 167 while (*str) { 168 if (*str == c) { 169 *str = 0; 170 return str + 1; 171 } 172 str += 1; 173 } 174 return null; 175 } 176 177 static snd_pcm_stream_t aim_to_stream(SoundIoDeviceAim aim) { 178 final switch (aim) { 179 case SoundIoDeviceAim.Output: return SND_PCM_STREAM_PLAYBACK; 180 case SoundIoDeviceAim.Input: return SND_PCM_STREAM_CAPTURE; 181 } 182 //assert(0); // Invalid aim 183 //return SND_PCM_STREAM_PLAYBACK; 184 } 185 186 static SoundIoChannelId from_alsa_chmap_pos(uint pos) { 187 switch (/*cast(snd_pcm_chmap_position)*/ pos) { 188 case SND_CHMAP_UNKNOWN: return SoundIoChannelId.Invalid; 189 case SND_CHMAP_NA: return SoundIoChannelId.Invalid; 190 case SND_CHMAP_MONO: return SoundIoChannelId.FrontCenter; 191 case SND_CHMAP_FL: return SoundIoChannelId.FrontLeft; // front left 192 case SND_CHMAP_FR: return SoundIoChannelId.FrontRight; // front right 193 case SND_CHMAP_RL: return SoundIoChannelId.BackLeft; // rear left 194 case SND_CHMAP_RR: return SoundIoChannelId.BackRight; // rear right 195 case SND_CHMAP_FC: return SoundIoChannelId.FrontCenter; // front center 196 case SND_CHMAP_LFE: return SoundIoChannelId.Lfe; // LFE 197 case SND_CHMAP_SL: return SoundIoChannelId.SideLeft; // side left 198 case SND_CHMAP_SR: return SoundIoChannelId.SideRight; // side right 199 case SND_CHMAP_RC: return SoundIoChannelId.BackCenter; // rear center 200 case SND_CHMAP_FLC: return SoundIoChannelId.FrontLeftCenter; // front left center 201 case SND_CHMAP_FRC: return SoundIoChannelId.FrontRightCenter; // front right center 202 case SND_CHMAP_RLC: return SoundIoChannelId.BackLeftCenter; // rear left center 203 case SND_CHMAP_RRC: return SoundIoChannelId.BackRightCenter; // rear right center 204 case SND_CHMAP_FLW: return SoundIoChannelId.FrontLeftWide; // front left wide 205 case SND_CHMAP_FRW: return SoundIoChannelId.FrontRightWide; // front right wide 206 case SND_CHMAP_FLH: return SoundIoChannelId.FrontLeftHigh; // front left high 207 case SND_CHMAP_FCH: return SoundIoChannelId.FrontCenterHigh; // front center high 208 case SND_CHMAP_FRH: return SoundIoChannelId.FrontRightHigh; // front right high 209 case SND_CHMAP_TC: return SoundIoChannelId.TopCenter; // top center 210 case SND_CHMAP_TFL: return SoundIoChannelId.TopFrontLeft; // top front left 211 case SND_CHMAP_TFR: return SoundIoChannelId.TopFrontRight; // top front right 212 case SND_CHMAP_TFC: return SoundIoChannelId.TopFrontCenter; // top front center 213 case SND_CHMAP_TRL: return SoundIoChannelId.TopBackLeft; // top rear left 214 case SND_CHMAP_TRR: return SoundIoChannelId.TopBackRight; // top rear right 215 case SND_CHMAP_TRC: return SoundIoChannelId.TopBackCenter; // top rear center 216 case SND_CHMAP_TFLC: return SoundIoChannelId.TopFrontLeftCenter; // top front left center 217 case SND_CHMAP_TFRC: return SoundIoChannelId.TopFrontRightCenter; // top front right center 218 case SND_CHMAP_TSL: return SoundIoChannelId.TopSideLeft; // top side left 219 case SND_CHMAP_TSR: return SoundIoChannelId.TopSideRight; // top side right 220 case SND_CHMAP_LLFE: return SoundIoChannelId.LeftLfe; // left LFE 221 case SND_CHMAP_RLFE: return SoundIoChannelId.RightLfe; // right LFE 222 case SND_CHMAP_BC: return SoundIoChannelId.BottomCenter; // bottom center 223 case SND_CHMAP_BLC: return SoundIoChannelId.BottomLeftCenter; // bottom left center 224 case SND_CHMAP_BRC: return SoundIoChannelId.BottomRightCenter; // bottom right center 225 default: break; 226 } 227 return SoundIoChannelId.Invalid; 228 } 229 230 static int to_alsa_chmap_pos(SoundIoChannelId channel_id) { 231 switch (channel_id) { 232 case SoundIoChannelId.FrontLeft: return SND_CHMAP_FL; 233 case SoundIoChannelId.FrontRight: return SND_CHMAP_FR; 234 case SoundIoChannelId.BackLeft: return SND_CHMAP_RL; 235 case SoundIoChannelId.BackRight: return SND_CHMAP_RR; 236 case SoundIoChannelId.FrontCenter: return SND_CHMAP_FC; 237 case SoundIoChannelId.Lfe: return SND_CHMAP_LFE; 238 case SoundIoChannelId.SideLeft: return SND_CHMAP_SL; 239 case SoundIoChannelId.SideRight: return SND_CHMAP_SR; 240 case SoundIoChannelId.BackCenter: return SND_CHMAP_RC; 241 case SoundIoChannelId.FrontLeftCenter: return SND_CHMAP_FLC; 242 case SoundIoChannelId.FrontRightCenter: return SND_CHMAP_FRC; 243 case SoundIoChannelId.BackLeftCenter: return SND_CHMAP_RLC; 244 case SoundIoChannelId.BackRightCenter: return SND_CHMAP_RRC; 245 case SoundIoChannelId.FrontLeftWide: return SND_CHMAP_FLW; 246 case SoundIoChannelId.FrontRightWide: return SND_CHMAP_FRW; 247 case SoundIoChannelId.FrontLeftHigh: return SND_CHMAP_FLH; 248 case SoundIoChannelId.FrontCenterHigh: return SND_CHMAP_FCH; 249 case SoundIoChannelId.FrontRightHigh: return SND_CHMAP_FRH; 250 case SoundIoChannelId.TopCenter: return SND_CHMAP_TC; 251 case SoundIoChannelId.TopFrontLeft: return SND_CHMAP_TFL; 252 case SoundIoChannelId.TopFrontRight: return SND_CHMAP_TFR; 253 case SoundIoChannelId.TopFrontCenter: return SND_CHMAP_TFC; 254 case SoundIoChannelId.TopBackLeft: return SND_CHMAP_TRL; 255 case SoundIoChannelId.TopBackRight: return SND_CHMAP_TRR; 256 case SoundIoChannelId.TopBackCenter: return SND_CHMAP_TRC; 257 case SoundIoChannelId.TopFrontLeftCenter: return SND_CHMAP_TFLC; 258 case SoundIoChannelId.TopFrontRightCenter: return SND_CHMAP_TFRC; 259 case SoundIoChannelId.TopSideLeft: return SND_CHMAP_TSL; 260 case SoundIoChannelId.TopSideRight: return SND_CHMAP_TSR; 261 case SoundIoChannelId.LeftLfe: return SND_CHMAP_LLFE; 262 case SoundIoChannelId.RightLfe: return SND_CHMAP_RLFE; 263 case SoundIoChannelId.BottomCenter: return SND_CHMAP_BC; 264 case SoundIoChannelId.BottomLeftCenter: return SND_CHMAP_BLC; 265 case SoundIoChannelId.BottomRightCenter: return SND_CHMAP_BRC; 266 267 default: 268 return SND_CHMAP_UNKNOWN; 269 } 270 } 271 272 static void get_channel_layout(SoundIoChannelLayout* dest, snd_pcm_chmap_t* chmap) { 273 int channel_count = soundio_int_min(SOUNDIO_MAX_CHANNELS, chmap.channels); 274 dest.channel_count = channel_count; 275 for (int i = 0; i < channel_count; i += 1) { 276 // chmap.pos is a variable length array, typed as a `uint[0]`. 277 // The `.ptr` is needed to avoid a range violation with array bounds checks 278 dest.channels[i] = from_alsa_chmap_pos(chmap.pos.ptr[i]); 279 } 280 soundio_channel_layout_detect_builtin(dest); 281 } 282 283 static int handle_channel_maps(SoundIoDevice* device, snd_pcm_chmap_query_t** maps) { 284 if (!maps) 285 return 0; 286 287 snd_pcm_chmap_query_t** p; 288 snd_pcm_chmap_query_t* v; 289 290 // one iteration to count 291 int layout_count = 0; 292 for (p = maps; cast(bool) (v = *p) && layout_count < SOUNDIO_MAX_CHANNELS; p += 1, layout_count += 1) { } 293 device.layouts = ALLOCATE!SoundIoChannelLayout(layout_count); 294 if (!device.layouts) { 295 snd_pcm_free_chmaps(maps); 296 return SoundIoError.NoMem; 297 } 298 device.layout_count = layout_count; 299 300 // iterate again to collect data 301 int layout_index; 302 for (p = maps, layout_index = 0; 303 cast(bool) (v = *p) && layout_index < layout_count; 304 p += 1, layout_index += 1) 305 { 306 get_channel_layout(&device.layouts[layout_index], &v.map); 307 } 308 snd_pcm_free_chmaps(maps); 309 310 return 0; 311 } 312 313 static snd_pcm_format_t to_alsa_fmt(SoundIoFormat fmt) { 314 switch (fmt) { 315 case SoundIoFormat.S8: return SND_PCM_FORMAT_S8; 316 case SoundIoFormat.U8: return SND_PCM_FORMAT_U8; 317 case SoundIoFormat.S16LE: return SND_PCM_FORMAT_S16_LE; 318 case SoundIoFormat.S16BE: return SND_PCM_FORMAT_S16_BE; 319 case SoundIoFormat.U16LE: return SND_PCM_FORMAT_U16_LE; 320 case SoundIoFormat.U16BE: return SND_PCM_FORMAT_U16_BE; 321 case SoundIoFormat.S24LE: return SND_PCM_FORMAT_S24_LE; 322 case SoundIoFormat.S24BE: return SND_PCM_FORMAT_S24_BE; 323 case SoundIoFormat.U24LE: return SND_PCM_FORMAT_U24_LE; 324 case SoundIoFormat.U24BE: return SND_PCM_FORMAT_U24_BE; 325 case SoundIoFormat.S32LE: return SND_PCM_FORMAT_S32_LE; 326 case SoundIoFormat.S32BE: return SND_PCM_FORMAT_S32_BE; 327 case SoundIoFormat.U32LE: return SND_PCM_FORMAT_U32_LE; 328 case SoundIoFormat.U32BE: return SND_PCM_FORMAT_U32_BE; 329 case SoundIoFormat.Float32LE: return SND_PCM_FORMAT_FLOAT_LE; 330 case SoundIoFormat.Float32BE: return SND_PCM_FORMAT_FLOAT_BE; 331 case SoundIoFormat.Float64LE: return SND_PCM_FORMAT_FLOAT64_LE; 332 case SoundIoFormat.Float64BE: return SND_PCM_FORMAT_FLOAT64_BE; 333 case SoundIoFormat.Invalid: 334 default: break; 335 } 336 return SND_PCM_FORMAT_UNKNOWN; 337 } 338 339 static void test_fmt_mask(SoundIoDevice* device, const(snd_pcm_format_mask_t)* fmt_mask, SoundIoFormat fmt) { 340 if (snd_pcm_format_mask_test(fmt_mask, to_alsa_fmt(fmt))) { 341 device.formats[device.format_count] = fmt; 342 device.format_count += 1; 343 } 344 } 345 346 static int set_access(snd_pcm_t* handle, snd_pcm_hw_params_t* hwparams, snd_pcm_access_t* out_access) { 347 for (int i = 0; i < prioritized_access_types.length; i += 1) { 348 snd_pcm_access_t access = prioritized_access_types[i]; 349 int err = snd_pcm_hw_params_set_access(handle, hwparams, access); 350 if (err >= 0) { 351 if (out_access) 352 *out_access = access; 353 return 0; 354 } 355 } 356 return SoundIoError.OpeningDevice; 357 } 358 359 // this function does not override device->formats, so if you want it to, deallocate and set it to NULL 360 static int probe_open_device(SoundIoDevice* device, snd_pcm_t* handle, int resample, int* out_channels_min, int* out_channels_max) { 361 SoundIoDevicePrivate* dev = cast(SoundIoDevicePrivate*)device; 362 int err; 363 364 snd_pcm_hw_params_t* hwparams; 365 snd_pcm_hw_params_malloc(&hwparams); 366 if (!hwparams) 367 return SoundIoError.NoMem; 368 scope(exit) snd_pcm_hw_params_free(hwparams); 369 370 err = snd_pcm_hw_params_any(handle, hwparams); 371 if (err < 0) 372 return SoundIoError.OpeningDevice; 373 374 err = snd_pcm_hw_params_set_rate_resample(handle, hwparams, resample); 375 if (err < 0) 376 return SoundIoError.OpeningDevice; 377 378 if (auto err1 = set_access(handle, hwparams, null)) 379 return err1; 380 381 uint channels_min; 382 uint channels_max; 383 384 err = snd_pcm_hw_params_get_channels_min(hwparams, &channels_min); 385 if (err < 0) 386 return SoundIoError.OpeningDevice; 387 err = snd_pcm_hw_params_set_channels_last(handle, hwparams, &channels_max); 388 if (err < 0) 389 return SoundIoError.OpeningDevice; 390 391 *out_channels_min = channels_min; 392 *out_channels_max = channels_max; 393 394 uint rate_min; 395 uint rate_max; 396 397 err = snd_pcm_hw_params_get_rate_min(hwparams, &rate_min, null); 398 if (err < 0) 399 return SoundIoError.OpeningDevice; 400 401 err = snd_pcm_hw_params_set_rate_last(handle, hwparams, &rate_max, null); 402 if (err < 0) 403 return SoundIoError.OpeningDevice; 404 405 device.sample_rate_count = 1; 406 device.sample_rates = &dev.prealloc_sample_rate_range; 407 device.sample_rates[0].min = rate_min; 408 device.sample_rates[0].max = rate_max; 409 410 double one_over_actual_rate = 1.0 / cast(double)rate_max; 411 412 // Purposefully leave the parameters with the highest rate, highest channel count. 413 414 snd_pcm_uframes_t min_frames; 415 snd_pcm_uframes_t max_frames; 416 417 418 err = snd_pcm_hw_params_get_buffer_size_min(hwparams, &min_frames); 419 if (err < 0) 420 return SoundIoError.OpeningDevice; 421 err = snd_pcm_hw_params_get_buffer_size_max(hwparams, &max_frames); 422 if (err < 0) 423 return SoundIoError.OpeningDevice; 424 425 device.software_latency_min = min_frames * one_over_actual_rate; 426 device.software_latency_max = max_frames * one_over_actual_rate; 427 428 err = snd_pcm_hw_params_set_buffer_size_first(handle, hwparams, &min_frames); 429 if (err < 0) 430 return SoundIoError.OpeningDevice; 431 432 433 snd_pcm_format_mask_t* fmt_mask; 434 snd_pcm_format_mask_malloc(&fmt_mask); 435 if (!fmt_mask) 436 return SoundIoError.NoMem; 437 scope(exit) snd_pcm_format_mask_free(fmt_mask); 438 snd_pcm_format_mask_none(fmt_mask); 439 snd_pcm_format_mask_set(fmt_mask, SND_PCM_FORMAT_S8); 440 snd_pcm_format_mask_set(fmt_mask, SND_PCM_FORMAT_U8); 441 snd_pcm_format_mask_set(fmt_mask, SND_PCM_FORMAT_S16_LE); 442 snd_pcm_format_mask_set(fmt_mask, SND_PCM_FORMAT_S16_BE); 443 snd_pcm_format_mask_set(fmt_mask, SND_PCM_FORMAT_U16_LE); 444 snd_pcm_format_mask_set(fmt_mask, SND_PCM_FORMAT_U16_BE); 445 snd_pcm_format_mask_set(fmt_mask, SND_PCM_FORMAT_S24_LE); 446 snd_pcm_format_mask_set(fmt_mask, SND_PCM_FORMAT_S24_BE); 447 snd_pcm_format_mask_set(fmt_mask, SND_PCM_FORMAT_U24_LE); 448 snd_pcm_format_mask_set(fmt_mask, SND_PCM_FORMAT_U24_BE); 449 snd_pcm_format_mask_set(fmt_mask, SND_PCM_FORMAT_S32_LE); 450 snd_pcm_format_mask_set(fmt_mask, SND_PCM_FORMAT_S32_BE); 451 snd_pcm_format_mask_set(fmt_mask, SND_PCM_FORMAT_U32_LE); 452 snd_pcm_format_mask_set(fmt_mask, SND_PCM_FORMAT_U32_BE); 453 snd_pcm_format_mask_set(fmt_mask, SND_PCM_FORMAT_FLOAT_LE); 454 snd_pcm_format_mask_set(fmt_mask, SND_PCM_FORMAT_FLOAT_BE); 455 snd_pcm_format_mask_set(fmt_mask, SND_PCM_FORMAT_FLOAT64_LE); 456 snd_pcm_format_mask_set(fmt_mask, SND_PCM_FORMAT_FLOAT64_BE); 457 458 err = snd_pcm_hw_params_set_format_mask(handle, hwparams, fmt_mask); 459 if (err < 0) 460 return SoundIoError.OpeningDevice; 461 462 if (!device.formats) { 463 snd_pcm_hw_params_get_format_mask(hwparams, fmt_mask); 464 device.formats = ALLOCATE!SoundIoFormat(18); 465 if (!device.formats) 466 return SoundIoError.NoMem; 467 468 device.format_count = 0; 469 test_fmt_mask(device, fmt_mask, SoundIoFormat.S8); 470 test_fmt_mask(device, fmt_mask, SoundIoFormat.U8); 471 test_fmt_mask(device, fmt_mask, SoundIoFormat.S16LE); 472 test_fmt_mask(device, fmt_mask, SoundIoFormat.S16BE); 473 test_fmt_mask(device, fmt_mask, SoundIoFormat.U16LE); 474 test_fmt_mask(device, fmt_mask, SoundIoFormat.U16BE); 475 test_fmt_mask(device, fmt_mask, SoundIoFormat.S24LE); 476 test_fmt_mask(device, fmt_mask, SoundIoFormat.S24BE); 477 test_fmt_mask(device, fmt_mask, SoundIoFormat.U24LE); 478 test_fmt_mask(device, fmt_mask, SoundIoFormat.U24BE); 479 test_fmt_mask(device, fmt_mask, SoundIoFormat.S32LE); 480 test_fmt_mask(device, fmt_mask, SoundIoFormat.S32BE); 481 test_fmt_mask(device, fmt_mask, SoundIoFormat.U32LE); 482 test_fmt_mask(device, fmt_mask, SoundIoFormat.U32BE); 483 test_fmt_mask(device, fmt_mask, SoundIoFormat.Float32LE); 484 test_fmt_mask(device, fmt_mask, SoundIoFormat.Float32BE); 485 test_fmt_mask(device, fmt_mask, SoundIoFormat.Float64LE); 486 test_fmt_mask(device, fmt_mask, SoundIoFormat.Float64BE); 487 } 488 489 return 0; 490 } 491 492 extern(D) int probe_device(SoundIoDevice* device, snd_pcm_chmap_query_t** maps) { 493 int err; 494 snd_pcm_t* handle; 495 496 snd_pcm_stream_t stream = aim_to_stream(device.aim); 497 498 err = snd_pcm_open(&handle, device.id, stream, 0); 499 if (err < 0) { 500 handle_channel_maps(device, maps); 501 return SoundIoError.OpeningDevice; 502 } 503 504 int channels_min; 505 int channels_max; 506 err = probe_open_device(device, handle, 0, &channels_min, &channels_max); 507 if (err) { 508 handle_channel_maps(device, maps); 509 snd_pcm_close(handle); 510 return err; 511 } 512 513 if (!maps) { 514 maps = snd_pcm_query_chmaps(handle); 515 if (!maps) { 516 // device gave us no channel maps. we're forced to conclude that 517 // the min and max channel counts are correct. 518 int layout_count = 0; 519 for (int i = 0; i < soundio_channel_layout_builtin_count(); i += 1) { 520 const(SoundIoChannelLayout)* layout = soundio_channel_layout_get_builtin(i); 521 if (layout.channel_count >= channels_min && layout.channel_count <= channels_max) { 522 layout_count += 1; 523 } 524 } 525 device.layout_count = layout_count; 526 device.layouts = ALLOCATE!SoundIoChannelLayout( device.layout_count); 527 if (!device.layouts) { 528 snd_pcm_close(handle); 529 return SoundIoError.NoMem; 530 } 531 int layout_index = 0; 532 for (int i = 0; i < soundio_channel_layout_builtin_count(); i += 1) { 533 const(SoundIoChannelLayout)* layout = soundio_channel_layout_get_builtin(i); 534 if (layout.channel_count >= channels_min && layout.channel_count <= channels_max) { 535 device.layouts[layout_index++] = *soundio_channel_layout_get_builtin(i); 536 } 537 } 538 } 539 } 540 541 snd_pcm_chmap_t* chmap = snd_pcm_get_chmap(handle); 542 if (chmap) { 543 get_channel_layout(&device.current_layout, chmap); 544 free(chmap); 545 } 546 err = handle_channel_maps(device, maps); 547 if (err) { 548 snd_pcm_close(handle); 549 return err; 550 } 551 maps = null; 552 553 if (!device.is_raw) { 554 if (device.sample_rates[0].min == device.sample_rates[0].max) 555 device.sample_rate_current = device.sample_rates[0].min; 556 557 if (device.software_latency_min == device.software_latency_max) 558 device.software_latency_current = device.software_latency_min; 559 560 // now say that resampling is OK and see what the real min and max is. 561 err = probe_open_device(device, handle, 1, &channels_min, &channels_max); 562 if (err < 0) { 563 snd_pcm_close(handle); 564 return SoundIoError.OpeningDevice; 565 } 566 } 567 568 snd_pcm_close(handle); 569 return 0; 570 } 571 572 pragma(inline, true) static bool str_has_prefix(const(char)* big_str, const(char)* prefix) { 573 return strncmp(big_str, prefix, strlen(prefix)) == 0; 574 } 575 576 extern(D) int refresh_devices(SoundIoPrivate* si) { 577 SoundIo* soundio = &si.pub; 578 SoundIoAlsa* sia = &si.backend_data.alsa; 579 580 int err; 581 err = snd_config_update_free_global(); 582 if (err < 0) 583 return SoundIoError.SystemResources; 584 err = snd_config_update(); 585 if (err < 0) 586 return SoundIoError.SystemResources; 587 588 SoundIoDevicesInfo* devices_info = ALLOCATE!SoundIoDevicesInfo(1); 589 if (!devices_info) 590 return SoundIoError.NoMem; 591 devices_info.default_output_index = -1; 592 devices_info.default_input_index = -1; 593 594 void** hints; 595 if (snd_device_name_hint(-1, "pcm", &hints) < 0) { 596 soundio_destroy_devices_info(devices_info); 597 return SoundIoError.NoMem; 598 } 599 600 int default_output_index = -1; 601 int sysdefault_output_index = -1; 602 int default_input_index = -1; 603 int sysdefault_input_index = -1; 604 605 for (void** hint_ptr = hints; *hint_ptr; hint_ptr += 1) { 606 char* name = snd_device_name_get_hint(*hint_ptr, "NAME"); 607 // null - libsoundio has its own dummy backend. API clients should use 608 // that instead of alsa null device. 609 if (strcmp(name, "null") == 0 || 610 // all these surround devices are clutter 611 str_has_prefix(name, "front:") || 612 str_has_prefix(name, "surround21:") || 613 str_has_prefix(name, "surround40:") || 614 str_has_prefix(name, "surround41:") || 615 str_has_prefix(name, "surround50:") || 616 str_has_prefix(name, "surround51:") || 617 str_has_prefix(name, "surround71:")) 618 { 619 free(name); 620 continue; 621 } 622 623 // One or both of descr and descr1 can be NULL. 624 char* descr = snd_device_name_get_hint(*hint_ptr, "DESC"); 625 char* descr1 = str_partition_on_char(descr, '\n'); 626 627 char* io = snd_device_name_get_hint(*hint_ptr, "IOID"); 628 bool is_playback; 629 bool is_capture; 630 631 // Workaround for Raspberry Pi driver bug, reporting itself as output 632 // when really it is input. 633 if (descr && strcmp(descr, "bcm2835 ALSA, bcm2835 ALSA") == 0 && 634 descr1 && strcmp(descr1, "Direct sample snooping device") == 0) 635 { 636 is_playback = false; 637 is_capture = true; 638 } else if (descr && strcmp(descr, "bcm2835 ALSA, bcm2835 IEC958/HDMI") == 0 && 639 descr1 && strcmp(descr1, "Direct sample snooping device") == 0) 640 { 641 is_playback = false; 642 is_capture = true; 643 } else if (io) { 644 if (strcmp(io, "Input") == 0) { 645 is_playback = false; 646 is_capture = true; 647 } else { 648 assert(strcmp(io, "Output") == 0); 649 is_playback = true; 650 is_capture = false; 651 } 652 free(io); 653 } else { 654 is_playback = true; 655 is_capture = true; 656 } 657 658 for (int stream_type_i = 0; stream_type_i < stream_types.length; stream_type_i += 1) { 659 snd_pcm_stream_t stream = stream_types[stream_type_i]; 660 if (stream == SND_PCM_STREAM_PLAYBACK && !is_playback) continue; 661 if (stream == SND_PCM_STREAM_CAPTURE && !is_capture) continue; 662 if (stream == SND_PCM_STREAM_CAPTURE && descr1 && 663 (strstr(descr1, "Output") || strstr(descr1, "output"))) 664 { 665 continue; 666 } 667 668 669 SoundIoDevicePrivate* dev = ALLOCATE!SoundIoDevicePrivate(1); 670 if (!dev) { 671 free(name); 672 free(descr); 673 soundio_destroy_devices_info(devices_info); 674 snd_device_name_free_hint(hints); 675 return SoundIoError.NoMem; 676 } 677 SoundIoDevice* device = &dev.pub; 678 device.ref_count = 1; 679 device.soundio = soundio; 680 device.is_raw = false; 681 device.id = strdup(name); 682 if (descr1) { 683 device.name = soundio_alloc_sprintf(null, "%s: %s", descr, descr1); 684 } else if (descr) { 685 device.name = strdup(descr); 686 } else { 687 device.name = strdup(name); 688 } 689 690 if (!device.id || !device.name) { 691 soundio_device_unref(device); 692 free(name); 693 free(descr); 694 soundio_destroy_devices_info(devices_info); 695 snd_device_name_free_hint(hints); 696 return SoundIoError.NoMem; 697 } 698 699 SoundIoListDevicePtr* device_list; 700 bool is_default = str_has_prefix(name, "default:") || strcmp(name, "default") == 0; 701 bool is_sysdefault = str_has_prefix(name, "sysdefault:") || strcmp(name, "sysdefault") == 0; 702 703 if (stream == SND_PCM_STREAM_PLAYBACK) { 704 device.aim = SoundIoDeviceAim.Output; 705 device_list = &devices_info.output_devices; 706 if (is_default) 707 default_output_index = device_list.length; 708 if (is_sysdefault) 709 sysdefault_output_index = device_list.length; 710 if (devices_info.default_output_index == -1) 711 devices_info.default_output_index = device_list.length; 712 } else { 713 assert(stream == SND_PCM_STREAM_CAPTURE); 714 device.aim = SoundIoDeviceAim.Input; 715 device_list = &devices_info.input_devices; 716 if (is_default) 717 default_input_index = device_list.length; 718 if (is_sysdefault) 719 sysdefault_input_index = device_list.length; 720 if (devices_info.default_input_index == -1) 721 devices_info.default_input_index = device_list.length; 722 } 723 724 device.probe_error = probe_device(device, null); 725 726 if (device_list.append(device)) { 727 soundio_device_unref(device); 728 free(name); 729 free(descr); 730 soundio_destroy_devices_info(devices_info); 731 snd_device_name_free_hint(hints); 732 return SoundIoError.NoMem; 733 } 734 } 735 736 free(name); 737 free(descr); 738 } 739 740 if (default_input_index >= 0) { 741 devices_info.default_input_index = default_input_index; 742 } else if (sysdefault_input_index >= 0) { 743 devices_info.default_input_index = sysdefault_input_index; 744 } 745 746 if (default_output_index >= 0) { 747 devices_info.default_output_index = default_output_index; 748 } else if (sysdefault_output_index >= 0) { 749 devices_info.default_output_index = sysdefault_output_index; 750 } 751 752 snd_device_name_free_hint(hints); 753 754 int card_index = -1; 755 756 if (snd_card_next(&card_index) < 0) 757 return SoundIoError.SystemResources; 758 759 snd_ctl_card_info_t* card_info; 760 snd_ctl_card_info_malloc(&card_info); 761 if (!card_info) 762 return SoundIoError.NoMem; 763 scope(exit) snd_ctl_card_info_free(card_info); 764 765 snd_pcm_info_t* pcm_info; 766 snd_pcm_info_malloc(&pcm_info); 767 if (!pcm_info) 768 return SoundIoError.NoMem; 769 scope(exit) snd_pcm_info_free(pcm_info); 770 771 while (card_index >= 0) { 772 snd_ctl_t* handle; 773 char[32] name; 774 import core.stdc.stdio: sprintf; 775 sprintf(name.ptr, "hw:%d", card_index); 776 err = snd_ctl_open(&handle, name.ptr, 0); 777 if (err < 0) { 778 if (err == -ENOENT) { 779 break; 780 } else { 781 soundio_destroy_devices_info(devices_info); 782 return SoundIoError.OpeningDevice; 783 } 784 } 785 786 err = snd_ctl_card_info(handle, card_info); 787 if (err < 0) { 788 snd_ctl_close(handle); 789 soundio_destroy_devices_info(devices_info); 790 return SoundIoError.SystemResources; 791 } 792 const(char)* card_name = snd_ctl_card_info_get_name(card_info); 793 794 int device_index = -1; 795 for (;;) { 796 if (snd_ctl_pcm_next_device(handle, &device_index) < 0) { 797 snd_ctl_close(handle); 798 soundio_destroy_devices_info(devices_info); 799 return SoundIoError.SystemResources; 800 } 801 if (device_index < 0) 802 break; 803 804 snd_pcm_info_set_device(pcm_info, device_index); 805 snd_pcm_info_set_subdevice(pcm_info, 0); 806 807 for (int stream_type_i = 0; stream_type_i < stream_types.length; stream_type_i += 1) { 808 snd_pcm_stream_t stream = stream_types[stream_type_i]; 809 snd_pcm_info_set_stream(pcm_info, stream); 810 811 err = snd_ctl_pcm_info(handle, pcm_info); 812 if (err < 0) { 813 if (err == -ENOENT) { 814 continue; 815 } else { 816 snd_ctl_close(handle); 817 soundio_destroy_devices_info(devices_info); 818 return SoundIoError.SystemResources; 819 } 820 } 821 822 const(char)* device_name = snd_pcm_info_get_name(pcm_info); 823 824 SoundIoDevicePrivate* dev = ALLOCATE!SoundIoDevicePrivate(1); 825 if (!dev) { 826 snd_ctl_close(handle); 827 soundio_destroy_devices_info(devices_info); 828 return SoundIoError.NoMem; 829 } 830 SoundIoDevice* device = &dev.pub; 831 device.ref_count = 1; 832 device.soundio = soundio; 833 device.id = soundio_alloc_sprintf(null, "hw:%d,%d", card_index, device_index); 834 device.name = soundio_alloc_sprintf(null, "%s %s", card_name, device_name); 835 device.is_raw = true; 836 837 if (!device.id || !device.name) { 838 soundio_device_unref(device); 839 snd_ctl_close(handle); 840 soundio_destroy_devices_info(devices_info); 841 return SoundIoError.NoMem; 842 } 843 844 SoundIoListDevicePtr* device_list; 845 if (stream == SND_PCM_STREAM_PLAYBACK) { 846 device.aim = SoundIoDeviceAim.Output; 847 device_list = &devices_info.output_devices; 848 } else { 849 assert(stream == SND_PCM_STREAM_CAPTURE); 850 device.aim = SoundIoDeviceAim.Input; 851 device_list = &devices_info.input_devices; 852 } 853 854 snd_pcm_chmap_query_t** maps = snd_pcm_query_chmaps_from_hw(card_index, device_index, -1, stream); 855 device.probe_error = probe_device(device, maps); 856 857 if (device_list.append(device)) { 858 soundio_device_unref(device); 859 soundio_destroy_devices_info(devices_info); 860 return SoundIoError.NoMem; 861 } 862 } 863 } 864 snd_ctl_close(handle); 865 if (snd_card_next(&card_index) < 0) { 866 soundio_destroy_devices_info(devices_info); 867 return SoundIoError.SystemResources; 868 } 869 } 870 871 soundio_os_mutex_lock(sia.mutex); 872 soundio_destroy_devices_info(sia.ready_devices_info); 873 sia.ready_devices_info = devices_info; 874 sia.have_devices_flag = true; 875 soundio_os_cond_signal(sia.cond, sia.mutex); 876 soundio.on_events_signal(soundio); 877 soundio_os_mutex_unlock(sia.mutex); 878 return 0; 879 } 880 881 static void shutdown_backend(SoundIoPrivate* si, int err) { 882 SoundIo* soundio = &si.pub; 883 SoundIoAlsa* sia = &si.backend_data.alsa; 884 soundio_os_mutex_lock(sia.mutex); 885 sia.shutdown_err = err; 886 soundio_os_cond_signal(sia.cond, sia.mutex); 887 soundio.on_events_signal(soundio); 888 soundio_os_mutex_unlock(sia.mutex); 889 } 890 891 static bool copy_str(char* dest, const(char)* src, int buf_len) { 892 for (;;) { 893 buf_len -= 1; 894 if (buf_len <= 0) 895 return false; 896 *dest = *src; 897 dest += 1; 898 src += 1; 899 if (!*src) 900 break; 901 } 902 *dest = '\0'; 903 return true; 904 } 905 906 static void device_thread_run(void* arg) { 907 SoundIoPrivate* si = cast(SoundIoPrivate*)arg; 908 SoundIoAlsa* sia = &si.backend_data.alsa; 909 910 // Some systems cannot read integer variables if they are not 911 // properly aligned. On other systems, incorrect alignment may 912 // decrease performance. Hence, the buffer used for reading from 913 // the inotify file descriptor should have the same alignment as 914 // struct inotify_event. 915 char[4096] buf; const(inotify_event)* event; 916 917 pollfd[2] fds; 918 fds[0].fd = sia.notify_fd; 919 fds[0].events = POLLIN; 920 921 fds[1].fd = sia.notify_pipe_fd[0]; 922 fds[1].events = POLLIN; 923 924 int err; 925 for (;;) { 926 int poll_num = poll(fds.ptr, 2, -1); 927 if (!SOUNDIO_ATOMIC_FLAG_TEST_AND_SET(sia.abort_flag)) 928 break; 929 if (poll_num == -1) { 930 if (errno == EINTR) 931 continue; 932 assert(errno != EFAULT); 933 assert(errno != EINVAL); 934 assert(errno == ENOMEM); 935 // Kernel ran out of polling memory. 936 shutdown_backend(si, SoundIoError.SystemResources); 937 return; 938 } 939 if (poll_num <= 0) 940 continue; 941 bool got_rescan_event = false; 942 if (fds[0].revents & POLLIN) { 943 for (;;) { 944 ssize_t len = read(sia.notify_fd, buf.ptr, buf.sizeof); 945 if (len == -1) { 946 assert(errno != EBADF); 947 assert(errno != EFAULT); 948 assert(errno != EINVAL); 949 assert(errno != EIO); 950 assert(errno != EISDIR); 951 if (errno == EBADF || errno == EFAULT || errno == EINVAL || 952 errno == EIO || errno == EISDIR) 953 { 954 shutdown_backend(si, SoundIoError.SystemResources); 955 return; 956 } 957 } 958 959 // catches EINTR and EAGAIN 960 if (len <= 0) 961 break; 962 963 // loop over all events in the buffer 964 for (char* ptr = buf.ptr; ptr < buf.ptr + len; ptr += inotify_event.sizeof + event.len) { 965 event = cast(const(inotify_event)*) ptr; 966 967 if (!((event.mask & IN_CLOSE_WRITE) || (event.mask & IN_DELETE) || (event.mask & IN_CREATE))) 968 continue; 969 if (event.mask & IN_ISDIR) 970 continue; 971 if (!event.len || event.len < 8) 972 continue; 973 if (strncmp(event.name.ptr, "controlC", 8) != 0) { 974 continue; 975 } 976 if (event.mask & IN_CREATE) { 977 err = sia.pending_files.add_one(); 978 if (err) { 979 shutdown_backend(si, SoundIoError.NoMem); 980 return; 981 } 982 SoundIoAlsaPendingFile* pending_file = sia.pending_files.last_ptr(); 983 if (!copy_str(pending_file.name.ptr, event.name.ptr, SOUNDIO_MAX_ALSA_SND_FILE_LEN)) { 984 sia.pending_files.pop(); 985 } 986 continue; 987 } 988 if (sia.pending_files.length > 0) { 989 // At this point ignore IN_DELETE in favor of waiting until the files 990 // opened with IN_CREATE have their IN_CLOSE_WRITE event. 991 if (!(event.mask & IN_CLOSE_WRITE)) 992 continue; 993 for (int i = 0; i < sia.pending_files.length; i += 1) { 994 SoundIoAlsaPendingFile* pending_file = sia.pending_files.ptr_at(i); 995 if (strcmp(pending_file.name.ptr, event.name.ptr) == 0) { 996 sia.pending_files.swap_remove(i); 997 if (sia.pending_files.length == 0) { 998 got_rescan_event = true; 999 } 1000 break; 1001 } 1002 } 1003 } else if (event.mask & IN_DELETE) { 1004 // We are not waiting on created files to be closed, so when 1005 // a delete happens we act on it. 1006 got_rescan_event = true; 1007 } 1008 } 1009 } 1010 } 1011 if (fds[1].revents & POLLIN) { 1012 got_rescan_event = true; 1013 for (;;) { 1014 ssize_t len = read(sia.notify_pipe_fd[0], buf.ptr, buf.sizeof); 1015 if (len == -1) { 1016 assert(errno != EBADF); 1017 assert(errno != EFAULT); 1018 assert(errno != EINVAL); 1019 assert(errno != EIO); 1020 assert(errno != EISDIR); 1021 if (errno == EBADF || errno == EFAULT || errno == EINVAL || 1022 errno == EIO || errno == EISDIR) 1023 { 1024 shutdown_backend(si, SoundIoError.SystemResources); 1025 return; 1026 } 1027 } 1028 if (len <= 0) 1029 break; 1030 } 1031 } 1032 if (got_rescan_event) { 1033 err = refresh_devices(si); 1034 if (err) { 1035 shutdown_backend(si, err); 1036 return; 1037 } 1038 } 1039 } 1040 } 1041 1042 extern(D) void my_flush_events(SoundIoPrivate* si, bool wait) { 1043 SoundIo* soundio = &si.pub; 1044 SoundIoAlsa* sia = &si.backend_data.alsa; 1045 1046 bool change = false; 1047 bool cb_shutdown = false; 1048 SoundIoDevicesInfo* old_devices_info = null; 1049 1050 soundio_os_mutex_lock(sia.mutex); 1051 1052 // block until have devices 1053 while (wait || (!sia.have_devices_flag && !sia.shutdown_err)) { 1054 soundio_os_cond_wait(sia.cond, sia.mutex); 1055 wait = false; 1056 } 1057 1058 if (sia.shutdown_err && !sia.emitted_shutdown_cb) { 1059 sia.emitted_shutdown_cb = true; 1060 cb_shutdown = true; 1061 } else if (sia.ready_devices_info) { 1062 old_devices_info = si.safe_devices_info; 1063 si.safe_devices_info = sia.ready_devices_info; 1064 sia.ready_devices_info = null; 1065 change = true; 1066 } 1067 1068 soundio_os_mutex_unlock(sia.mutex); 1069 1070 if (cb_shutdown) 1071 soundio.on_backend_disconnect(soundio, sia.shutdown_err); 1072 else if (change) 1073 soundio.on_devices_change(soundio); 1074 1075 soundio_destroy_devices_info(old_devices_info); 1076 } 1077 1078 void flush_events_alsa(SoundIoPrivate* si) { 1079 my_flush_events(si, false); 1080 } 1081 1082 void wait_events_alsa(SoundIoPrivate* si) { 1083 my_flush_events(si, false); 1084 my_flush_events(si, true); 1085 } 1086 1087 void wakeup_alsa(SoundIoPrivate* si) { 1088 SoundIoAlsa* sia = &si.backend_data.alsa; 1089 soundio_os_mutex_lock(sia.mutex); 1090 soundio_os_cond_signal(sia.cond, sia.mutex); 1091 soundio_os_mutex_unlock(sia.mutex); 1092 } 1093 1094 void force_device_scan_alsa(SoundIoPrivate* si) { 1095 SoundIoAlsa* sia = &si.backend_data.alsa; 1096 wakeup_device_poll(sia); 1097 } 1098 1099 void outstream_destroy_alsa(SoundIoPrivate* si, SoundIoOutStreamPrivate* os) { 1100 SoundIoOutStreamAlsa* osa = &os.backend_data.alsa; 1101 1102 if (osa.thread) { 1103 SOUNDIO_ATOMIC_FLAG_CLEAR(osa.thread_exit_flag); 1104 wakeup_outstream_poll(osa); 1105 soundio_os_thread_destroy(osa.thread); 1106 osa.thread = null; 1107 } 1108 1109 if (osa.handle) { 1110 snd_pcm_close(osa.handle); 1111 osa.handle = null; 1112 } 1113 1114 free(osa.poll_fds); 1115 osa.poll_fds = null; 1116 1117 free(osa.chmap); 1118 osa.chmap = null; 1119 1120 free(osa.sample_buffer); 1121 osa.sample_buffer = null; 1122 } 1123 1124 int outstream_xrun_recovery(SoundIoOutStreamPrivate* os, int err) { 1125 SoundIoOutStream* outstream = &os.pub; 1126 SoundIoOutStreamAlsa* osa = &os.backend_data.alsa; 1127 if (err == -EPIPE) { 1128 err = snd_pcm_prepare(osa.handle); 1129 if (err >= 0) 1130 outstream.underflow_callback(outstream); 1131 } else if (err == -ESTRPIPE) { 1132 while ((err = snd_pcm_resume(osa.handle)) == -EAGAIN) { 1133 // wait until suspend flag is released 1134 poll(null, 0, 1); 1135 } 1136 if (err < 0) 1137 err = snd_pcm_prepare(osa.handle); 1138 if (err >= 0) 1139 outstream.underflow_callback(outstream); 1140 } 1141 return err; 1142 } 1143 1144 int instream_xrun_recovery(SoundIoInStreamPrivate* is_, int err) { 1145 SoundIoInStream* instream = &is_.pub; 1146 SoundIoInStreamAlsa* isa = &is_.backend_data.alsa; 1147 if (err == -EPIPE) { 1148 err = snd_pcm_prepare(isa.handle); 1149 if (err >= 0) 1150 instream.overflow_callback(instream); 1151 } else if (err == -ESTRPIPE) { 1152 while ((err = snd_pcm_resume(isa.handle)) == -EAGAIN) { 1153 // wait until suspend flag is released 1154 poll(null, 0, 1); 1155 } 1156 if (err < 0) 1157 err = snd_pcm_prepare(isa.handle); 1158 if (err >= 0) 1159 instream.overflow_callback(instream); 1160 } 1161 return err; 1162 } 1163 1164 int outstream_wait_for_poll(SoundIoOutStreamPrivate* os) { 1165 SoundIoOutStreamAlsa* osa = &os.backend_data.alsa; 1166 int err; 1167 ushort revents; 1168 for (;;) { 1169 err = poll(osa.poll_fds, osa.poll_fd_count_with_extra, -1); 1170 if (err < 0) { 1171 return SoundIoError.Streaming; 1172 } 1173 if (!SOUNDIO_ATOMIC_FLAG_TEST_AND_SET(osa.thread_exit_flag)) 1174 return SoundIoError.Interrupted; 1175 if ((err = snd_pcm_poll_descriptors_revents(osa.handle, 1176 osa.poll_fds, osa.poll_fd_count, &revents)) < 0) 1177 { 1178 return SoundIoError.Streaming; 1179 } 1180 if (revents & (POLLERR|POLLNVAL|POLLHUP)) { 1181 return 0; 1182 } 1183 if (revents & POLLOUT) 1184 return 0; 1185 } 1186 } 1187 1188 int instream_wait_for_poll(SoundIoInStreamPrivate* is_) { 1189 SoundIoInStreamAlsa* isa = &is_.backend_data.alsa; 1190 int err; 1191 ushort revents; 1192 for (;;) { 1193 err = poll(isa.poll_fds, isa.poll_fd_count, -1); 1194 if (err < 0) { 1195 return err; 1196 } 1197 if ((err = snd_pcm_poll_descriptors_revents(isa.handle, 1198 isa.poll_fds, isa.poll_fd_count, &revents)) < 0) 1199 { 1200 return err; 1201 } 1202 if (revents & (POLLERR|POLLNVAL|POLLHUP)) { 1203 return 0; 1204 } 1205 if (revents & POLLIN) 1206 return 0; 1207 } 1208 } 1209 1210 void outstream_thread_run(void* arg) { 1211 SoundIoOutStreamPrivate* os = cast(SoundIoOutStreamPrivate*) arg; 1212 SoundIoOutStream* outstream = &os.pub; 1213 SoundIoOutStreamAlsa* osa = &os.backend_data.alsa; 1214 1215 int err; 1216 1217 for (;;) { 1218 snd_pcm_state_t state = snd_pcm_state(osa.handle); 1219 switch (state) { 1220 case SND_PCM_STATE_SETUP: 1221 { 1222 err = snd_pcm_prepare(osa.handle); 1223 if (err < 0) { 1224 outstream.error_callback(outstream, SoundIoError.Streaming); 1225 return; 1226 } 1227 continue; 1228 } 1229 case SND_PCM_STATE_PREPARED: 1230 { 1231 snd_pcm_sframes_t avail = snd_pcm_avail(osa.handle); 1232 if (avail < 0) { 1233 outstream.error_callback(outstream, SoundIoError.Streaming); 1234 return; 1235 } 1236 1237 if (cast(snd_pcm_uframes_t)avail == osa.buffer_size_frames) { 1238 outstream.write_callback(outstream, 0, cast(int) avail); 1239 if (!SOUNDIO_ATOMIC_FLAG_TEST_AND_SET(osa.thread_exit_flag)) 1240 return; 1241 continue; 1242 } 1243 1244 err = snd_pcm_start(osa.handle); 1245 if (err < 0) { 1246 outstream.error_callback(outstream, SoundIoError.Streaming); 1247 return; 1248 } 1249 continue; 1250 } 1251 case SND_PCM_STATE_RUNNING: 1252 case SND_PCM_STATE_PAUSED: 1253 { 1254 err = outstream_wait_for_poll(os); 1255 if (err) { 1256 if (err == SoundIoError.Interrupted) 1257 return; 1258 outstream.error_callback(outstream, err); 1259 return; 1260 } 1261 if (!SOUNDIO_ATOMIC_FLAG_TEST_AND_SET(osa.thread_exit_flag)) 1262 return; 1263 if (!SOUNDIO_ATOMIC_FLAG_TEST_AND_SET(osa.clear_buffer_flag)) { 1264 err = snd_pcm_drop(osa.handle); 1265 if (err < 0) { 1266 outstream.error_callback(outstream, SoundIoError.Streaming); 1267 return; 1268 } 1269 err = snd_pcm_reset(osa.handle); 1270 if (err < 0) { 1271 if (err == -EBADFD) { 1272 // If this happens the snd_pcm_drop will have done 1273 // the function of the reset so it's ok that this 1274 // did not work. 1275 } else { 1276 outstream.error_callback(outstream, SoundIoError.Streaming); 1277 return; 1278 } 1279 } 1280 continue; 1281 } 1282 1283 snd_pcm_sframes_t avail = snd_pcm_avail_update(osa.handle); 1284 if (avail < 0) { 1285 err = outstream_xrun_recovery(os, cast(int) avail); 1286 if (err < 0) { 1287 outstream.error_callback(outstream, SoundIoError.Streaming); 1288 return; 1289 } 1290 continue; 1291 } 1292 1293 if (avail > 0) 1294 outstream.write_callback(outstream, 0, cast(int) avail); 1295 continue; 1296 } 1297 case SND_PCM_STATE_XRUN: 1298 err = outstream_xrun_recovery(os, -EPIPE); 1299 if (err < 0) { 1300 outstream.error_callback(outstream, SoundIoError.Streaming); 1301 return; 1302 } 1303 continue; 1304 case SND_PCM_STATE_SUSPENDED: 1305 err = outstream_xrun_recovery(os, -ESTRPIPE); 1306 if (err < 0) { 1307 outstream.error_callback(outstream, SoundIoError.Streaming); 1308 return; 1309 } 1310 continue; 1311 case SND_PCM_STATE_OPEN: 1312 case SND_PCM_STATE_DRAINING: 1313 case SND_PCM_STATE_DISCONNECTED: 1314 outstream.error_callback(outstream, SoundIoError.Streaming); 1315 return; 1316 default: 1317 continue; 1318 } 1319 } 1320 } 1321 1322 static void instream_thread_run(void* arg) { 1323 SoundIoInStreamPrivate* is_ = cast(SoundIoInStreamPrivate*) arg; 1324 SoundIoInStream* instream = &is_.pub; 1325 SoundIoInStreamAlsa* isa = &is_.backend_data.alsa; 1326 1327 int err; 1328 1329 for (;;) { 1330 snd_pcm_state_t state = snd_pcm_state(isa.handle); 1331 switch (state) { 1332 case SND_PCM_STATE_SETUP: 1333 err = snd_pcm_prepare(isa.handle); 1334 if (err < 0) { 1335 instream.error_callback(instream, SoundIoError.Streaming); 1336 return; 1337 } 1338 continue; 1339 case SND_PCM_STATE_PREPARED: 1340 err = snd_pcm_start(isa.handle); 1341 if (err < 0) { 1342 instream.error_callback(instream, SoundIoError.Streaming); 1343 return; 1344 } 1345 continue; 1346 case SND_PCM_STATE_RUNNING: 1347 case SND_PCM_STATE_PAUSED: 1348 { 1349 err = instream_wait_for_poll(is_); 1350 if (err < 0) { 1351 if (!SOUNDIO_ATOMIC_FLAG_TEST_AND_SET(isa.thread_exit_flag)) 1352 return; 1353 instream.error_callback(instream, SoundIoError.Streaming); 1354 return; 1355 } 1356 if (!SOUNDIO_ATOMIC_FLAG_TEST_AND_SET(isa.thread_exit_flag)) 1357 return; 1358 1359 snd_pcm_sframes_t avail = snd_pcm_avail_update(isa.handle); 1360 1361 if (avail < 0) { 1362 err = instream_xrun_recovery(is_, cast(int) avail); 1363 if (err < 0) { 1364 instream.error_callback(instream, SoundIoError.Streaming); 1365 return; 1366 } 1367 continue; 1368 } 1369 1370 if (avail > 0) 1371 instream.read_callback(instream, 0, cast(int) avail); 1372 continue; 1373 } 1374 case SND_PCM_STATE_XRUN: 1375 err = instream_xrun_recovery(is_, -EPIPE); 1376 if (err < 0) { 1377 instream.error_callback(instream, SoundIoError.Streaming); 1378 return; 1379 } 1380 continue; 1381 case SND_PCM_STATE_SUSPENDED: 1382 err = instream_xrun_recovery(is_, -ESTRPIPE); 1383 if (err < 0) { 1384 instream.error_callback(instream, SoundIoError.Streaming); 1385 return; 1386 } 1387 continue; 1388 case SND_PCM_STATE_OPEN: 1389 case SND_PCM_STATE_DRAINING: 1390 case SND_PCM_STATE_DISCONNECTED: 1391 instream.error_callback(instream, SoundIoError.Streaming); 1392 return; 1393 default: 1394 continue; 1395 } 1396 } 1397 } 1398 1399 static int outstream_open_alsa(SoundIoPrivate* si, SoundIoOutStreamPrivate* os) { 1400 SoundIoOutStreamAlsa* osa = &os.backend_data.alsa; 1401 SoundIoOutStream* outstream = &os.pub; 1402 SoundIoDevice* device = outstream.device; 1403 1404 SOUNDIO_ATOMIC_FLAG_TEST_AND_SET(osa.clear_buffer_flag); 1405 1406 if (outstream.software_latency == 0.0) 1407 outstream.software_latency = 1.0; 1408 outstream.software_latency = soundio_double_clamp(device.software_latency_min, outstream.software_latency, device.software_latency_max); 1409 1410 int ch_count = outstream.layout.channel_count; 1411 1412 osa.chmap_size = cast(int) (int.sizeof + int.sizeof * ch_count); 1413 osa.chmap = cast(snd_pcm_chmap_t*)ALLOCATE!char(osa.chmap_size); 1414 if (!osa.chmap) { 1415 outstream_destroy_alsa(si, os); 1416 return SoundIoError.NoMem; 1417 } 1418 1419 int err; 1420 1421 snd_pcm_hw_params_t* hwparams; 1422 snd_pcm_hw_params_malloc(&hwparams); 1423 if (!hwparams) 1424 return SoundIoError.NoMem; 1425 scope(exit) snd_pcm_hw_params_free(hwparams); 1426 1427 snd_pcm_stream_t stream = aim_to_stream(outstream.device.aim); 1428 1429 err = snd_pcm_open(&osa.handle, outstream.device.id, stream, 0); 1430 if (err < 0) { 1431 outstream_destroy_alsa(si, os); 1432 return SoundIoError.OpeningDevice; 1433 } 1434 1435 err = snd_pcm_hw_params_any(osa.handle, hwparams); 1436 if (err < 0) { 1437 outstream_destroy_alsa(si, os); 1438 return SoundIoError.OpeningDevice; 1439 } 1440 1441 int want_resample = !outstream.device.is_raw; 1442 err = snd_pcm_hw_params_set_rate_resample(osa.handle, hwparams, want_resample); 1443 if (err < 0) { 1444 outstream_destroy_alsa(si, os); 1445 return SoundIoError.OpeningDevice; 1446 } 1447 1448 err = set_access(osa.handle, hwparams, &osa.access); 1449 if (err) { 1450 outstream_destroy_alsa(si, os); 1451 return err; 1452 } 1453 1454 err = snd_pcm_hw_params_set_channels(osa.handle, hwparams, ch_count); 1455 if (err < 0) { 1456 outstream_destroy_alsa(si, os); 1457 return SoundIoError.OpeningDevice; 1458 } 1459 1460 err = snd_pcm_hw_params_set_rate(osa.handle, hwparams, outstream.sample_rate, 0); 1461 if (err < 0) { 1462 outstream_destroy_alsa(si, os); 1463 return SoundIoError.OpeningDevice; 1464 } 1465 1466 snd_pcm_format_t format = to_alsa_fmt(outstream.format); 1467 int phys_bits_per_sample = snd_pcm_format_physical_width(format); 1468 if (phys_bits_per_sample % 8 != 0) { 1469 outstream_destroy_alsa(si, os); 1470 return SoundIoError.IncompatibleDevice; 1471 } 1472 int phys_bytes_per_sample = phys_bits_per_sample / 8; 1473 err = snd_pcm_hw_params_set_format(osa.handle, hwparams, format); 1474 if (err < 0) { 1475 outstream_destroy_alsa(si, os); 1476 return SoundIoError.OpeningDevice; 1477 } 1478 1479 osa.buffer_size_frames = cast(ulong) (outstream.software_latency * outstream.sample_rate); 1480 err = snd_pcm_hw_params_set_buffer_size_near(osa.handle, hwparams, &osa.buffer_size_frames); 1481 if (err < 0) { 1482 outstream_destroy_alsa(si, os); 1483 return SoundIoError.OpeningDevice; 1484 } 1485 outstream.software_latency = (cast(double)osa.buffer_size_frames) / cast(double)outstream.sample_rate; 1486 1487 // write the hardware parameters to device 1488 err = snd_pcm_hw_params(osa.handle, hwparams); 1489 if (err < 0) { 1490 outstream_destroy_alsa(si, os); 1491 return (err == -EINVAL) ? SoundIoError.IncompatibleDevice : SoundIoError.OpeningDevice; 1492 } 1493 1494 if ((snd_pcm_hw_params_get_period_size(hwparams, &osa.period_size, null)) < 0) { 1495 outstream_destroy_alsa(si, os); 1496 return SoundIoError.OpeningDevice; 1497 } 1498 1499 1500 // set channel map 1501 osa.chmap.channels = ch_count; 1502 for (int i = 0; i < ch_count; i += 1) { 1503 // `pos` is variable length array typed `uint[0]`, .ptr to avoid range violation 1504 osa.chmap.pos.ptr[i] = to_alsa_chmap_pos(outstream.layout.channels[i]); 1505 } 1506 err = snd_pcm_set_chmap(osa.handle, osa.chmap); 1507 if (err < 0) 1508 outstream.layout_error = SoundIoError.IncompatibleDevice; 1509 1510 // get current swparams 1511 snd_pcm_sw_params_t* swparams; 1512 snd_pcm_sw_params_malloc(&swparams); 1513 if (!swparams) 1514 return SoundIoError.NoMem; 1515 scope(exit) snd_pcm_sw_params_free(swparams); 1516 1517 err = snd_pcm_sw_params_current(osa.handle, swparams); 1518 if (err < 0) { 1519 outstream_destroy_alsa(si, os); 1520 return SoundIoError.OpeningDevice; 1521 } 1522 1523 err = snd_pcm_sw_params_set_start_threshold(osa.handle, swparams, 0); 1524 if (err < 0) { 1525 outstream_destroy_alsa(si, os); 1526 return SoundIoError.OpeningDevice; 1527 } 1528 1529 err = snd_pcm_sw_params_set_avail_min(osa.handle, swparams, osa.period_size); 1530 if (err < 0) { 1531 outstream_destroy_alsa(si, os); 1532 return SoundIoError.OpeningDevice; 1533 } 1534 1535 // write the software parameters to device 1536 err = snd_pcm_sw_params(osa.handle, swparams); 1537 if (err < 0) { 1538 outstream_destroy_alsa(si, os); 1539 return (err == -EINVAL) ? SoundIoError.IncompatibleDevice : SoundIoError.OpeningDevice; 1540 } 1541 1542 if (osa.access == SND_PCM_ACCESS_RW_INTERLEAVED || osa.access == SND_PCM_ACCESS_RW_NONINTERLEAVED) { 1543 osa.sample_buffer_size = cast(int) (ch_count * osa.period_size * phys_bytes_per_sample); 1544 osa.sample_buffer = ALLOCATE_NONZERO!(char)(osa.sample_buffer_size); 1545 if (!osa.sample_buffer) { 1546 outstream_destroy_alsa(si, os); 1547 return SoundIoError.NoMem; 1548 } 1549 } 1550 1551 osa.poll_fd_count = snd_pcm_poll_descriptors_count(osa.handle); 1552 if (osa.poll_fd_count <= 0) { 1553 outstream_destroy_alsa(si, os); 1554 return SoundIoError.OpeningDevice; 1555 } 1556 1557 osa.poll_fd_count_with_extra = osa.poll_fd_count + 1; 1558 osa.poll_fds = ALLOCATE!pollfd( osa.poll_fd_count_with_extra); 1559 if (!osa.poll_fds) { 1560 outstream_destroy_alsa(si, os); 1561 return SoundIoError.NoMem; 1562 } 1563 1564 err = snd_pcm_poll_descriptors(osa.handle, osa.poll_fds, osa.poll_fd_count); 1565 if (err < 0) { 1566 outstream_destroy_alsa(si, os); 1567 return SoundIoError.OpeningDevice; 1568 } 1569 1570 pollfd* extra_fd = &osa.poll_fds[osa.poll_fd_count]; 1571 if (pipe2(osa.poll_exit_pipe_fd.ptr, O_NONBLOCK)) { 1572 assert(errno != EFAULT); 1573 assert(errno != EINVAL); 1574 assert(errno == EMFILE || errno == ENFILE); 1575 outstream_destroy_alsa(si, os); 1576 return SoundIoError.SystemResources; 1577 } 1578 extra_fd.fd = osa.poll_exit_pipe_fd[0]; 1579 extra_fd.events = POLLIN; 1580 1581 return 0; 1582 } 1583 1584 static int outstream_start_alsa(SoundIoPrivate* si, SoundIoOutStreamPrivate* os) { 1585 SoundIoOutStreamAlsa* osa = &os.backend_data.alsa; 1586 SoundIo* soundio = &si.pub; 1587 1588 assert(!osa.thread); 1589 1590 SOUNDIO_ATOMIC_FLAG_TEST_AND_SET(osa.thread_exit_flag); 1591 if (auto err = soundio_os_thread_create(&outstream_thread_run, os, soundio.emit_rtprio_warning, &osa.thread)) 1592 return err; 1593 1594 return 0; 1595 } 1596 1597 static int outstream_begin_write_alsa(SoundIoPrivate* si, SoundIoOutStreamPrivate* os, SoundIoChannelArea** out_areas, int* frame_count) { 1598 *out_areas = null; 1599 SoundIoOutStreamAlsa* osa = &os.backend_data.alsa; 1600 SoundIoOutStream* outstream = &os.pub; 1601 1602 if (osa.access == SND_PCM_ACCESS_RW_INTERLEAVED) { 1603 for (int ch = 0; ch < outstream.layout.channel_count; ch += 1) { 1604 osa.areas[ch].ptr = osa.sample_buffer + ch * outstream.bytes_per_sample; 1605 osa.areas[ch].step = outstream.bytes_per_frame; 1606 } 1607 1608 osa.write_frame_count = soundio_int_min(*frame_count, cast(int) osa.period_size); 1609 *frame_count = osa.write_frame_count; 1610 } else if (osa.access == SND_PCM_ACCESS_RW_NONINTERLEAVED) { 1611 for (int ch = 0; ch < outstream.layout.channel_count; ch += 1) { 1612 osa.areas[ch].ptr = osa.sample_buffer + ch * outstream.bytes_per_sample * osa.period_size; 1613 osa.areas[ch].step = outstream.bytes_per_sample; 1614 } 1615 1616 osa.write_frame_count = soundio_int_min(*frame_count, cast(int) osa.period_size); 1617 *frame_count = osa.write_frame_count; 1618 } else { 1619 const(snd_pcm_channel_area_t)* areas; 1620 snd_pcm_uframes_t frames = *frame_count; 1621 int err; 1622 1623 err = snd_pcm_mmap_begin(osa.handle, &areas, &osa.offset, &frames); 1624 if (err < 0) { 1625 if (err == -EPIPE || err == -ESTRPIPE) 1626 return SoundIoError.Underflow; 1627 else 1628 return SoundIoError.Streaming; 1629 } 1630 1631 for (int ch = 0; ch < outstream.layout.channel_count; ch += 1) { 1632 if ((areas[ch].first % 8 != 0) || (areas[ch].step % 8 != 0)) 1633 return SoundIoError.IncompatibleDevice; 1634 osa.areas[ch].step = areas[ch].step / 8; 1635 osa.areas[ch].ptr = (cast(char*)areas[ch].addr) + (areas[ch].first / 8) + 1636 (osa.areas[ch].step * osa.offset); 1637 } 1638 1639 osa.write_frame_count = cast(int) frames; 1640 *frame_count = osa.write_frame_count; 1641 } 1642 1643 *out_areas = osa.areas.ptr; 1644 return 0; 1645 } 1646 1647 static int outstream_end_write_alsa(SoundIoPrivate* si, SoundIoOutStreamPrivate* os) { 1648 SoundIoOutStreamAlsa* osa = &os.backend_data.alsa; 1649 SoundIoOutStream* outstream = &os.pub; 1650 1651 snd_pcm_sframes_t commitres; 1652 if (osa.access == SND_PCM_ACCESS_RW_INTERLEAVED) { 1653 commitres = snd_pcm_writei(osa.handle, osa.sample_buffer, osa.write_frame_count); 1654 } else if (osa.access == SND_PCM_ACCESS_RW_NONINTERLEAVED) { 1655 char*[SOUNDIO_MAX_CHANNELS] ptrs; 1656 for (int ch = 0; ch < outstream.layout.channel_count; ch += 1) { 1657 ptrs[ch] = osa.sample_buffer + ch * outstream.bytes_per_sample * osa.period_size; 1658 } 1659 commitres = snd_pcm_writen(osa.handle, cast(void**)ptrs, osa.write_frame_count); 1660 } else { 1661 commitres = snd_pcm_mmap_commit(osa.handle, osa.offset, osa.write_frame_count); 1662 } 1663 1664 if (commitres < 0 || commitres != osa.write_frame_count) { 1665 int err = cast(int) ((commitres >= 0) ? -EPIPE : commitres); 1666 if (err == -EPIPE || err == -ESTRPIPE) 1667 return SoundIoError.Underflow; 1668 else 1669 return SoundIoError.Streaming; 1670 } 1671 return 0; 1672 } 1673 1674 static int outstream_clear_buffer_alsa(SoundIoPrivate* si, SoundIoOutStreamPrivate* os) { 1675 SoundIoOutStreamAlsa* osa = &os.backend_data.alsa; 1676 SOUNDIO_ATOMIC_FLAG_CLEAR(osa.clear_buffer_flag); 1677 return 0; 1678 } 1679 1680 static int outstream_pause_alsa(SoundIoPrivate* si, SoundIoOutStreamPrivate* os, bool pause) { 1681 if (!si) 1682 return SoundIoError.Invalid; 1683 1684 SoundIoOutStreamAlsa* osa = &os.backend_data.alsa; 1685 1686 if (!osa.handle) 1687 return SoundIoError.Invalid; 1688 1689 if (osa.is_paused == pause) 1690 return 0; 1691 1692 int err; 1693 err = snd_pcm_pause(osa.handle, pause); 1694 if (err < 0) { 1695 return SoundIoError.IncompatibleDevice; 1696 } 1697 1698 osa.is_paused = pause; 1699 return 0; 1700 } 1701 1702 static int outstream_get_latency_alsa(SoundIoPrivate* si, SoundIoOutStreamPrivate* os, double* out_latency) { 1703 SoundIoOutStream* outstream = &os.pub; 1704 SoundIoOutStreamAlsa* osa = &os.backend_data.alsa; 1705 int err; 1706 1707 snd_pcm_sframes_t delay; 1708 err = snd_pcm_delay(osa.handle, &delay); 1709 if (err < 0) { 1710 return SoundIoError.Streaming; 1711 } 1712 1713 *out_latency = delay / cast(double)outstream.sample_rate; 1714 return 0; 1715 } 1716 1717 static void instream_destroy_alsa(SoundIoPrivate* si, SoundIoInStreamPrivate* is_) { 1718 SoundIoInStreamAlsa* isa = &is_.backend_data.alsa; 1719 1720 if (isa.thread) { 1721 SOUNDIO_ATOMIC_FLAG_CLEAR(isa.thread_exit_flag); 1722 soundio_os_thread_destroy(isa.thread); 1723 isa.thread = null; 1724 } 1725 1726 if (isa.handle) { 1727 snd_pcm_close(isa.handle); 1728 isa.handle = null; 1729 } 1730 1731 free(isa.poll_fds); 1732 isa.poll_fds = null; 1733 1734 free(isa.chmap); 1735 isa.chmap = null; 1736 1737 free(isa.sample_buffer); 1738 isa.sample_buffer = null; 1739 } 1740 1741 static int instream_open_alsa(SoundIoPrivate* si, SoundIoInStreamPrivate* is_) { 1742 SoundIoInStreamAlsa* isa = &is_.backend_data.alsa; 1743 SoundIoInStream* instream = &is_.pub; 1744 SoundIoDevice* device = instream.device; 1745 1746 if (instream.software_latency == 0.0) 1747 instream.software_latency = 1.0; 1748 instream.software_latency = soundio_double_clamp(device.software_latency_min, instream.software_latency, device.software_latency_max); 1749 1750 int ch_count = instream.layout.channel_count; 1751 1752 isa.chmap_size = cast(int) (int.sizeof + int.sizeof * ch_count); 1753 isa.chmap = cast(snd_pcm_chmap_t*) ALLOCATE!char(isa.chmap_size); 1754 if (!isa.chmap) { 1755 instream_destroy_alsa(si, is_); 1756 return SoundIoError.NoMem; 1757 } 1758 1759 int err; 1760 1761 snd_pcm_hw_params_t* hwparams; 1762 snd_pcm_hw_params_malloc(&hwparams); 1763 if (!hwparams) 1764 return SoundIoError.NoMem; 1765 scope(exit) snd_pcm_hw_params_free(hwparams); 1766 1767 snd_pcm_stream_t stream = aim_to_stream(instream.device.aim); 1768 1769 err = snd_pcm_open(&isa.handle, instream.device.id, stream, 0); 1770 if (err < 0) { 1771 instream_destroy_alsa(si, is_); 1772 return SoundIoError.OpeningDevice; 1773 } 1774 1775 err = snd_pcm_hw_params_any(isa.handle, hwparams); 1776 if (err < 0) { 1777 instream_destroy_alsa(si, is_); 1778 return SoundIoError.OpeningDevice; 1779 } 1780 1781 int want_resample = !instream.device.is_raw; 1782 err = snd_pcm_hw_params_set_rate_resample(isa.handle, hwparams, want_resample); 1783 if (err < 0) { 1784 instream_destroy_alsa(si, is_); 1785 return SoundIoError.OpeningDevice; 1786 } 1787 1788 err = set_access(isa.handle, hwparams, &isa.access); 1789 if (err) { 1790 instream_destroy_alsa(si, is_); 1791 return err; 1792 } 1793 1794 err = snd_pcm_hw_params_set_channels(isa.handle, hwparams, ch_count); 1795 if (err < 0) { 1796 instream_destroy_alsa(si, is_); 1797 return SoundIoError.OpeningDevice; 1798 } 1799 1800 err = snd_pcm_hw_params_set_rate(isa.handle, hwparams, instream.sample_rate, 0); 1801 if (err < 0) { 1802 instream_destroy_alsa(si, is_); 1803 return SoundIoError.OpeningDevice; 1804 } 1805 1806 snd_pcm_format_t format = to_alsa_fmt(instream.format); 1807 int phys_bits_per_sample = snd_pcm_format_physical_width(format); 1808 if (phys_bits_per_sample % 8 != 0) { 1809 instream_destroy_alsa(si, is_); 1810 return SoundIoError.IncompatibleDevice; 1811 } 1812 int phys_bytes_per_sample = phys_bits_per_sample / 8; 1813 err = snd_pcm_hw_params_set_format(isa.handle, hwparams, format); 1814 if (err < 0) { 1815 instream_destroy_alsa(si, is_); 1816 return SoundIoError.OpeningDevice; 1817 } 1818 1819 snd_pcm_uframes_t period_frames = ceil_dbl_to_uframes(0.5 * instream.software_latency * cast(double)instream.sample_rate); 1820 err = snd_pcm_hw_params_set_period_size_near(isa.handle, hwparams, &period_frames, null); 1821 if (err < 0) { 1822 instream_destroy_alsa(si, is_); 1823 return SoundIoError.OpeningDevice; 1824 } 1825 instream.software_latency = (cast(double)period_frames) / cast(double)instream.sample_rate; 1826 isa.period_size = cast(int) period_frames; 1827 1828 1829 snd_pcm_uframes_t buffer_size_frames; 1830 err = snd_pcm_hw_params_set_buffer_size_last(isa.handle, hwparams, &buffer_size_frames); 1831 if (err < 0) { 1832 instream_destroy_alsa(si, is_); 1833 return SoundIoError.OpeningDevice; 1834 } 1835 1836 // write the hardware parameters to device 1837 err = snd_pcm_hw_params(isa.handle, hwparams); 1838 if (err < 0) { 1839 instream_destroy_alsa(si, is_); 1840 return (err == -EINVAL) ? SoundIoError.IncompatibleDevice : SoundIoError.OpeningDevice; 1841 } 1842 1843 // set channel map 1844 isa.chmap.channels = ch_count; 1845 for (int i = 0; i < ch_count; i += 1) { 1846 // `pos` is variable length array typed `uint[0]`, .ptr to avoid range violation 1847 isa.chmap.pos.ptr[i] = to_alsa_chmap_pos(instream.layout.channels[i]); 1848 } 1849 err = snd_pcm_set_chmap(isa.handle, isa.chmap); 1850 if (err < 0) 1851 instream.layout_error = SoundIoError.IncompatibleDevice; 1852 1853 // get current swparams 1854 snd_pcm_sw_params_t* swparams; 1855 snd_pcm_sw_params_malloc(&swparams); 1856 if (!swparams) 1857 return SoundIoError.NoMem; 1858 scope(exit) snd_pcm_sw_params_free(swparams); 1859 1860 err = snd_pcm_sw_params_current(isa.handle, swparams); 1861 if (err < 0) { 1862 instream_destroy_alsa(si, is_); 1863 return SoundIoError.OpeningDevice; 1864 } 1865 1866 // write the software parameters to device 1867 err = snd_pcm_sw_params(isa.handle, swparams); 1868 if (err < 0) { 1869 instream_destroy_alsa(si, is_); 1870 return (err == -EINVAL) ? SoundIoError.IncompatibleDevice : SoundIoError.OpeningDevice; 1871 } 1872 1873 if (isa.access == SND_PCM_ACCESS_RW_INTERLEAVED || isa.access == SND_PCM_ACCESS_RW_NONINTERLEAVED) { 1874 isa.sample_buffer_size = ch_count * isa.period_size * phys_bytes_per_sample; 1875 isa.sample_buffer = ALLOCATE_NONZERO!char(isa.sample_buffer_size); 1876 if (!isa.sample_buffer) { 1877 instream_destroy_alsa(si, is_); 1878 return SoundIoError.NoMem; 1879 } 1880 } 1881 1882 isa.poll_fd_count = snd_pcm_poll_descriptors_count(isa.handle); 1883 if (isa.poll_fd_count <= 0) { 1884 instream_destroy_alsa(si, is_); 1885 return SoundIoError.OpeningDevice; 1886 } 1887 1888 isa.poll_fds = ALLOCATE!pollfd(isa.poll_fd_count); 1889 if (!isa.poll_fds) { 1890 instream_destroy_alsa(si, is_); 1891 return SoundIoError.NoMem; 1892 } 1893 1894 err = snd_pcm_poll_descriptors(isa.handle, isa.poll_fds, isa.poll_fd_count); 1895 if (err < 0) { 1896 instream_destroy_alsa(si, is_); 1897 return SoundIoError.OpeningDevice; 1898 } 1899 1900 return 0; 1901 } 1902 1903 static int instream_start_alsa(SoundIoPrivate* si, SoundIoInStreamPrivate* is_) { 1904 SoundIoInStreamAlsa* isa = &is_.backend_data.alsa; 1905 SoundIo* soundio = &si.pub; 1906 1907 assert(!isa.thread); 1908 1909 SOUNDIO_ATOMIC_FLAG_TEST_AND_SET(isa.thread_exit_flag); 1910 if (auto err = soundio_os_thread_create(&instream_thread_run, is_, soundio.emit_rtprio_warning, &isa.thread)) { 1911 instream_destroy_alsa(si, is_); 1912 return err; 1913 } 1914 1915 return 0; 1916 } 1917 1918 static int instream_begin_read_alsa(SoundIoPrivate* si, SoundIoInStreamPrivate* is_, SoundIoChannelArea** out_areas, int* frame_count) { 1919 *out_areas = null; 1920 SoundIoInStreamAlsa* isa = &is_.backend_data.alsa; 1921 SoundIoInStream* instream = &is_.pub; 1922 1923 if (isa.access == SND_PCM_ACCESS_RW_INTERLEAVED) { 1924 for (int ch = 0; ch < instream.layout.channel_count; ch += 1) { 1925 isa.areas[ch].ptr = isa.sample_buffer + ch * instream.bytes_per_sample; 1926 isa.areas[ch].step = instream.bytes_per_frame; 1927 } 1928 1929 isa.read_frame_count = soundio_int_min(*frame_count, isa.period_size); 1930 *frame_count = isa.read_frame_count; 1931 1932 snd_pcm_sframes_t commitres = snd_pcm_readi(isa.handle, isa.sample_buffer, isa.read_frame_count); 1933 if (commitres < 0 || commitres != isa.read_frame_count) { 1934 int err = cast(int) ((commitres >= 0) ? -EPIPE : commitres); 1935 err = instream_xrun_recovery(is_, err); 1936 if (err < 0) 1937 return SoundIoError.Streaming; 1938 } 1939 } else if (isa.access == SND_PCM_ACCESS_RW_NONINTERLEAVED) { 1940 char*[SOUNDIO_MAX_CHANNELS] ptrs; 1941 for (int ch = 0; ch < instream.layout.channel_count; ch += 1) { 1942 isa.areas[ch].ptr = isa.sample_buffer + ch * instream.bytes_per_sample * isa.period_size; 1943 isa.areas[ch].step = instream.bytes_per_sample; 1944 ptrs[ch] = isa.areas[ch].ptr; 1945 } 1946 1947 isa.read_frame_count = soundio_int_min(*frame_count, isa.period_size); 1948 *frame_count = isa.read_frame_count; 1949 1950 snd_pcm_sframes_t commitres = snd_pcm_readn(isa.handle, cast(void**)ptrs, isa.read_frame_count); 1951 if (commitres < 0 || commitres != isa.read_frame_count) { 1952 int err = cast(int) ((commitres >= 0) ? -EPIPE : commitres); 1953 err = instream_xrun_recovery(is_, err); 1954 if (err < 0) 1955 return SoundIoError.Streaming; 1956 } 1957 } else { 1958 const(snd_pcm_channel_area_t)* areas; 1959 snd_pcm_uframes_t frames = *frame_count; 1960 int err; 1961 1962 err = snd_pcm_mmap_begin(isa.handle, &areas, &isa.offset, &frames); 1963 if (err < 0) { 1964 err = instream_xrun_recovery(is_, err); 1965 if (err < 0) 1966 return SoundIoError.Streaming; 1967 } 1968 1969 for (int ch = 0; ch < instream.layout.channel_count; ch += 1) { 1970 if ((areas[ch].first % 8 != 0) || (areas[ch].step % 8 != 0)) 1971 return SoundIoError.IncompatibleDevice; 1972 isa.areas[ch].step = areas[ch].step / 8; 1973 isa.areas[ch].ptr = (cast(char*)areas[ch].addr) + (areas[ch].first / 8) + 1974 (isa.areas[ch].step * isa.offset); 1975 } 1976 1977 isa.read_frame_count = cast(int) frames; 1978 *frame_count = isa.read_frame_count; 1979 } 1980 1981 *out_areas = isa.areas.ptr; 1982 return 0; 1983 } 1984 1985 static int instream_end_read_alsa(SoundIoPrivate* si, SoundIoInStreamPrivate* is_) { 1986 SoundIoInStreamAlsa* isa = &is_.backend_data.alsa; 1987 1988 if (isa.access == SND_PCM_ACCESS_RW_INTERLEAVED) { 1989 // nothing to do 1990 } else if (isa.access == SND_PCM_ACCESS_RW_NONINTERLEAVED) { 1991 // nothing to do 1992 } else { 1993 snd_pcm_sframes_t commitres = snd_pcm_mmap_commit(isa.handle, isa.offset, isa.read_frame_count); 1994 if (commitres < 0 || commitres != isa.read_frame_count) { 1995 int err = cast(int) ((commitres >= 0) ? -EPIPE : commitres); 1996 err = instream_xrun_recovery(is_, err); 1997 if (err < 0) 1998 return SoundIoError.Streaming; 1999 } 2000 } 2001 2002 return 0; 2003 } 2004 2005 static int instream_pause_alsa(SoundIoPrivate* si, SoundIoInStreamPrivate* is_, bool pause) { 2006 SoundIoInStreamAlsa* isa = &is_.backend_data.alsa; 2007 2008 if (isa.is_paused == pause) 2009 return 0; 2010 2011 int err; 2012 err = snd_pcm_pause(isa.handle, pause); 2013 if (err < 0) 2014 return SoundIoError.IncompatibleDevice; 2015 2016 isa.is_paused = pause; 2017 return 0; 2018 } 2019 2020 static int instream_get_latency_alsa(SoundIoPrivate* si, SoundIoInStreamPrivate* is_, double* out_latency) { 2021 SoundIoInStream* instream = &is_.pub; 2022 SoundIoInStreamAlsa* isa = &is_.backend_data.alsa; 2023 int err; 2024 2025 snd_pcm_sframes_t delay; 2026 err = snd_pcm_delay(isa.handle, &delay); 2027 if (err < 0) { 2028 return SoundIoError.Streaming; 2029 } 2030 2031 *out_latency = delay / cast(double)instream.sample_rate; 2032 return 0; 2033 } 2034 2035 package int soundio_alsa_init(SoundIoPrivate* si) { 2036 SoundIoAlsa* sia = &si.backend_data.alsa; 2037 int err; 2038 2039 sia.notify_fd = -1; 2040 sia.notify_wd = -1; 2041 SOUNDIO_ATOMIC_FLAG_TEST_AND_SET(sia.abort_flag); 2042 2043 sia.mutex = soundio_os_mutex_create(); 2044 if (!sia.mutex) { 2045 destroy_alsa(si); 2046 return SoundIoError.NoMem; 2047 } 2048 2049 sia.cond = soundio_os_cond_create(); 2050 if (!sia.cond) { 2051 destroy_alsa(si); 2052 return SoundIoError.NoMem; 2053 } 2054 2055 2056 // set up inotify to watch /dev/snd for devices added or removed 2057 sia.notify_fd = inotify_init1(IN_NONBLOCK); 2058 if (sia.notify_fd == -1) { 2059 err = errno; 2060 assert(err != EINVAL); 2061 destroy_alsa(si); 2062 if (err == EMFILE || err == ENFILE) { 2063 return SoundIoError.SystemResources; 2064 } else { 2065 assert(err == ENOMEM); 2066 return SoundIoError.NoMem; 2067 } 2068 } 2069 2070 sia.notify_wd = inotify_add_watch(sia.notify_fd, "/dev/snd", IN_CREATE | IN_CLOSE_WRITE | IN_DELETE); 2071 if (sia.notify_wd == -1) { 2072 err = errno; 2073 assert(err != EACCES); 2074 assert(err != EBADF); 2075 assert(err != EFAULT); 2076 assert(err != EINVAL); 2077 assert(err != ENAMETOOLONG); 2078 destroy_alsa(si); 2079 if (err == ENOSPC) { 2080 return SoundIoError.SystemResources; 2081 } else if (err == ENOMEM) { 2082 return SoundIoError.NoMem; 2083 } else { 2084 // Kernel must not have ALSA support. 2085 return SoundIoError.InitAudioBackend; 2086 } 2087 } 2088 2089 if (pipe2(sia.notify_pipe_fd.ptr, O_NONBLOCK)) { 2090 assert(errno != EFAULT); 2091 assert(errno != EINVAL); 2092 assert(errno == EMFILE || errno == ENFILE); 2093 return SoundIoError.SystemResources; 2094 } 2095 2096 wakeup_device_poll(sia); 2097 2098 //device_thread_run(si); TODO removeme 2099 err = soundio_os_thread_create(&device_thread_run, si, null, &sia.thread); 2100 if (err) { 2101 destroy_alsa(si); 2102 return err; 2103 } 2104 2105 si.destroy = &destroy_alsa; 2106 si.flush_events = &flush_events_alsa; 2107 si.wait_events = &wait_events_alsa; 2108 si.wakeup = &wakeup_alsa; 2109 si.force_device_scan = &force_device_scan_alsa; 2110 2111 si.outstream_open = &outstream_open_alsa; 2112 si.outstream_destroy = &outstream_destroy_alsa; 2113 si.outstream_start = &outstream_start_alsa; 2114 si.outstream_begin_write = &outstream_begin_write_alsa; 2115 si.outstream_end_write = &outstream_end_write_alsa; 2116 si.outstream_clear_buffer = &outstream_clear_buffer_alsa; 2117 si.outstream_pause = &outstream_pause_alsa; 2118 si.outstream_get_latency = &outstream_get_latency_alsa; 2119 2120 si.instream_open = &instream_open_alsa; 2121 si.instream_destroy = &instream_destroy_alsa; 2122 si.instream_start = &instream_start_alsa; 2123 si.instream_begin_read = &instream_begin_read_alsa; 2124 si.instream_end_read = &instream_end_read_alsa; 2125 si.instream_pause = &instream_pause_alsa; 2126 si.instream_get_latency = &instream_get_latency_alsa; 2127 2128 return 0; 2129 }