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