Paul Kocialkowski | 1579692 | 2017-08-21 18:27:19 +0300 | [diff] [blame] | 1 | /* |
| 2 | * Copyright © 2017 Intel Corporation |
| 3 | * |
| 4 | * Permission is hereby granted, free of charge, to any person obtaining a |
| 5 | * copy of this software and associated documentation files (the "Software"), |
| 6 | * to deal in the Software without restriction, including without limitation |
| 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| 8 | * and/or sell copies of the Software, and to permit persons to whom the |
| 9 | * Software is furnished to do so, subject to the following conditions: |
| 10 | * |
| 11 | * The above copyright notice and this permission notice (including the next |
| 12 | * paragraph) shall be included in all copies or substantial portions of the |
| 13 | * Software. |
| 14 | * |
| 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| 18 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| 20 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
| 21 | * IN THE SOFTWARE. |
| 22 | * |
| 23 | * Authors: |
| 24 | * Paul Kocialkowski <paul.kocialkowski@linux.intel.com> |
| 25 | */ |
| 26 | |
| 27 | #include "config.h" |
| 28 | |
| 29 | #include <alsa/asoundlib.h> |
| 30 | |
Daniel Vetter | 8c1fcc6 | 2017-09-08 11:23:15 +0200 | [diff] [blame] | 31 | #include "igt_alsa.h" |
| 32 | #include "igt_core.h" |
Paul Kocialkowski | 1579692 | 2017-08-21 18:27:19 +0300 | [diff] [blame] | 33 | |
| 34 | #define HANDLES_MAX 8 |
| 35 | |
| 36 | /** |
| 37 | * SECTION:igt_alsa |
| 38 | * @short_description: Library with ALSA helpers |
| 39 | * @title: ALSA |
| 40 | * @include: igt_alsa.h |
| 41 | * |
| 42 | * This library contains helpers for ALSA playback and capture. |
| 43 | */ |
| 44 | |
| 45 | struct alsa { |
| 46 | snd_pcm_t *output_handles[HANDLES_MAX]; |
| 47 | int output_handles_count; |
| 48 | int output_sampling_rate; |
| 49 | int output_channels; |
| 50 | |
| 51 | int (*output_callback)(void *data, short *buffer, int samples); |
| 52 | void *output_callback_data; |
| 53 | int output_samples_trigger; |
| 54 | |
| 55 | snd_pcm_t *input_handle; |
| 56 | int input_sampling_rate; |
| 57 | int input_channels; |
| 58 | |
| 59 | int (*input_callback)(void *data, short *buffer, int samples); |
| 60 | void *input_callback_data; |
| 61 | int input_samples_trigger; |
| 62 | }; |
| 63 | |
| 64 | static void alsa_error_handler(const char *file, int line, const char *function, |
| 65 | int err, const char *fmt, ...) |
| 66 | { |
| 67 | if (err) |
| 68 | igt_debug("[ALSA] %s: %s\n", function, snd_strerror(err)); |
| 69 | } |
| 70 | |
| 71 | /** |
| 72 | * alsa_init: |
| 73 | * Allocate and initialize an alsa structure and configure the error handler. |
| 74 | * |
| 75 | * Returns: A newly-allocated alsa structure |
| 76 | */ |
| 77 | struct alsa *alsa_init(void) |
| 78 | { |
| 79 | struct alsa *alsa; |
| 80 | |
| 81 | alsa = malloc(sizeof(struct alsa)); |
| 82 | memset(alsa, 0, sizeof(struct alsa)); |
| 83 | |
| 84 | /* Redirect errors to igt_debug instead of stderr. */ |
| 85 | snd_lib_error_set_handler(alsa_error_handler); |
| 86 | |
| 87 | return alsa; |
| 88 | } |
| 89 | |
| 90 | static char *alsa_resolve_indentifier(const char *device_name, int skip) |
| 91 | { |
| 92 | snd_ctl_card_info_t *card_info; |
| 93 | snd_pcm_info_t *pcm_info; |
| 94 | snd_ctl_t *handle = NULL; |
| 95 | const char *pcm_name; |
| 96 | char *identifier = NULL; |
| 97 | char name[32]; |
| 98 | int card = -1; |
| 99 | int dev; |
| 100 | int ret; |
| 101 | |
| 102 | snd_ctl_card_info_alloca(&card_info); |
| 103 | snd_pcm_info_alloca(&pcm_info); |
| 104 | |
| 105 | /* First try to open the device as-is. */ |
| 106 | if (!skip) { |
| 107 | ret = snd_ctl_open(&handle, device_name, 0); |
| 108 | if (!ret) { |
| 109 | identifier = strdup(device_name); |
| 110 | goto resolved; |
| 111 | } |
| 112 | } |
| 113 | |
| 114 | do { |
| 115 | ret = snd_card_next(&card); |
| 116 | if (ret < 0 || card < 0) |
| 117 | break; |
| 118 | |
| 119 | snprintf(name, sizeof(name), "hw:%d", card); |
| 120 | |
| 121 | ret = snd_ctl_open(&handle, name, 0); |
| 122 | if (ret < 0) |
| 123 | continue; |
| 124 | |
| 125 | ret = snd_ctl_card_info(handle, card_info); |
| 126 | if (ret < 0) { |
| 127 | snd_ctl_close(handle); |
| 128 | handle = NULL; |
| 129 | continue; |
| 130 | } |
| 131 | |
| 132 | dev = -1; |
| 133 | |
| 134 | do { |
| 135 | ret = snd_ctl_pcm_next_device(handle, &dev); |
| 136 | if (ret < 0 || dev < 0) |
| 137 | break; |
| 138 | |
| 139 | snd_pcm_info_set_device(pcm_info, dev); |
| 140 | snd_pcm_info_set_subdevice(pcm_info, 0); |
| 141 | |
| 142 | ret = snd_ctl_pcm_info(handle, pcm_info); |
| 143 | if (ret < 0) |
| 144 | continue; |
| 145 | |
| 146 | pcm_name = snd_pcm_info_get_name(pcm_info); |
| 147 | if (!pcm_name) |
| 148 | continue; |
| 149 | |
| 150 | ret = strncmp(device_name, pcm_name, |
| 151 | strlen(device_name)); |
| 152 | |
| 153 | if (ret == 0) { |
| 154 | if (skip > 0) { |
| 155 | skip--; |
| 156 | continue; |
| 157 | } |
| 158 | |
| 159 | snprintf(name, sizeof(name), "hw:%d,%d", card, |
| 160 | dev); |
| 161 | |
| 162 | identifier = strdup(name); |
| 163 | goto resolved; |
| 164 | } |
| 165 | } while (dev >= 0); |
| 166 | |
| 167 | snd_ctl_close(handle); |
| 168 | handle = NULL; |
| 169 | } while (card >= 0); |
| 170 | |
| 171 | resolved: |
| 172 | if (handle) |
| 173 | snd_ctl_close(handle); |
| 174 | |
| 175 | return identifier; |
| 176 | } |
| 177 | |
| 178 | /** |
| 179 | * alsa_open_output: |
| 180 | * @alsa: The target alsa structure |
| 181 | * @device_name: The name prefix of the output device(s) to open |
| 182 | * |
| 183 | * Open ALSA output devices whose name prefixes match the provided name prefix. |
| 184 | * |
| 185 | * Returns: An integer equal to zero for success and negative for failure |
| 186 | */ |
| 187 | int alsa_open_output(struct alsa *alsa, const char *device_name) |
| 188 | { |
| 189 | snd_pcm_t *handle; |
| 190 | char *identifier; |
| 191 | int skip; |
| 192 | int index; |
| 193 | int ret; |
| 194 | |
| 195 | skip = alsa->output_handles_count; |
| 196 | index = alsa->output_handles_count; |
| 197 | |
| 198 | while (index < HANDLES_MAX) { |
| 199 | identifier = alsa_resolve_indentifier(device_name, skip++); |
| 200 | if (!identifier) |
| 201 | break; |
| 202 | |
| 203 | ret = snd_pcm_open(&handle, identifier, SND_PCM_STREAM_PLAYBACK, |
| 204 | SND_PCM_NONBLOCK); |
| 205 | if (ret < 0) { |
| 206 | free(identifier); |
| 207 | continue; |
| 208 | } |
| 209 | |
| 210 | igt_debug("Opened output %s\n", identifier); |
| 211 | |
| 212 | alsa->output_handles[index++] = handle; |
| 213 | free(identifier); |
| 214 | } |
| 215 | |
| 216 | if (index == 0) |
| 217 | return -1; |
| 218 | |
| 219 | alsa->output_handles_count = index; |
| 220 | |
| 221 | return 0; |
| 222 | } |
| 223 | |
| 224 | /** |
| 225 | * alsa_open_input: |
| 226 | * @alsa: The target alsa structure |
| 227 | * @device_name: The name of the input device to open |
| 228 | * |
| 229 | * Open the ALSA input device whose name matches the provided name prefix. |
| 230 | * |
| 231 | * Returns: An integer equal to zero for success and negative for failure |
| 232 | */ |
| 233 | int alsa_open_input(struct alsa *alsa, const char *device_name) |
| 234 | { |
| 235 | snd_pcm_t *handle; |
| 236 | char *identifier; |
| 237 | int ret; |
| 238 | |
| 239 | identifier = alsa_resolve_indentifier(device_name, 0); |
| 240 | |
| 241 | ret = snd_pcm_open(&handle, device_name, SND_PCM_STREAM_CAPTURE, |
| 242 | SND_PCM_NONBLOCK); |
| 243 | if (ret < 0) |
| 244 | goto complete; |
| 245 | |
| 246 | igt_debug("Opened input %s\n", identifier); |
| 247 | |
| 248 | alsa->input_handle = handle; |
| 249 | |
| 250 | ret = 0; |
| 251 | |
| 252 | complete: |
| 253 | free(identifier); |
| 254 | |
| 255 | return ret; |
| 256 | } |
| 257 | |
| 258 | /** |
| 259 | * alsa_close_output: |
| 260 | * @alsa: The target alsa structure |
| 261 | * |
| 262 | * Close all the open ALSA outputs. |
| 263 | */ |
| 264 | void alsa_close_output(struct alsa *alsa) |
| 265 | { |
| 266 | snd_pcm_t *handle; |
| 267 | int i; |
| 268 | |
| 269 | for (i = 0; i < alsa->output_handles_count; i++) { |
| 270 | handle = alsa->output_handles[i]; |
| 271 | if (!handle) |
| 272 | continue; |
| 273 | |
| 274 | snd_pcm_close(handle); |
| 275 | alsa->output_handles[i] = NULL; |
| 276 | } |
| 277 | |
| 278 | alsa->output_handles_count = 0; |
| 279 | |
| 280 | alsa->output_callback = NULL; |
| 281 | } |
| 282 | |
| 283 | /** |
| 284 | * alsa_close_output: |
| 285 | * @alsa: The target alsa structure |
| 286 | * |
| 287 | * Close the open ALSA input. |
| 288 | */ |
| 289 | void alsa_close_input(struct alsa *alsa) |
| 290 | { |
| 291 | snd_pcm_t *handle = alsa->input_handle; |
| 292 | if (!handle) |
| 293 | return; |
| 294 | |
| 295 | snd_pcm_close(handle); |
| 296 | alsa->input_handle = NULL; |
| 297 | |
| 298 | alsa->input_callback = NULL; |
| 299 | } |
| 300 | |
| 301 | static bool alsa_test_configuration(snd_pcm_t *handle, int channels, |
| 302 | int sampling_rate) |
| 303 | { |
| 304 | snd_pcm_hw_params_t *params; |
| 305 | int ret; |
| 306 | |
| 307 | snd_pcm_hw_params_alloca(¶ms); |
| 308 | |
| 309 | ret = snd_pcm_hw_params_any(handle, params); |
| 310 | if (ret < 0) |
| 311 | return false; |
| 312 | |
| 313 | ret = snd_pcm_hw_params_test_rate(handle, params, sampling_rate, 0); |
| 314 | if (ret < 0) |
| 315 | return false; |
| 316 | |
| 317 | ret = snd_pcm_hw_params_test_channels(handle, params, channels); |
| 318 | if (ret < 0) |
| 319 | return false; |
| 320 | |
| 321 | return true; |
| 322 | } |
| 323 | |
| 324 | /** |
| 325 | * alsa_test_output_configuration: |
| 326 | * @alsa: The target alsa structure |
| 327 | * @channels: The number of channels to test |
| 328 | * @sampling_rate: The sampling rate to test |
| 329 | * |
| 330 | * Test the output configuration specified by @channels and @sampling_rate |
| 331 | * for the output devices. |
| 332 | * |
| 333 | * Returns: A boolean indicating whether the test succeeded |
| 334 | */ |
| 335 | bool alsa_test_output_configuration(struct alsa *alsa, int channels, |
| 336 | int sampling_rate) |
| 337 | { |
| 338 | snd_pcm_t *handle; |
| 339 | bool ret; |
| 340 | int i; |
| 341 | |
| 342 | for (i = 0; i < alsa->output_handles_count; i++) { |
| 343 | handle = alsa->output_handles[i]; |
| 344 | |
| 345 | ret = alsa_test_configuration(handle, channels, sampling_rate); |
| 346 | if (!ret) |
| 347 | return false; |
| 348 | } |
| 349 | |
| 350 | return true; |
| 351 | } |
| 352 | |
| 353 | /** |
| 354 | * alsa_test_input_configuration: |
| 355 | * @alsa: The target alsa structure |
| 356 | * @channels: The number of channels to test |
| 357 | * @sampling_rate: The sampling rate to test |
| 358 | * |
| 359 | * Test the input configuration specified by @channels and @sampling_rate |
| 360 | * for the input device. |
| 361 | * |
| 362 | * Returns: A boolean indicating whether the test succeeded |
| 363 | */ |
| 364 | bool alsa_test_input_configuration(struct alsa *alsa, int channels, |
| 365 | int sampling_rate) |
| 366 | { |
| 367 | return alsa_test_configuration(alsa->input_handle, channels, |
| 368 | sampling_rate); |
| 369 | } |
| 370 | |
| 371 | /** |
| 372 | * alsa_configure_output: |
| 373 | * @alsa: The target alsa structure |
| 374 | * @channels: The number of channels to test |
| 375 | * @sampling_rate: The sampling rate to test |
| 376 | * |
| 377 | * Configure the output devices with the configuration specified by @channels |
| 378 | * and @sampling_rate. |
| 379 | */ |
| 380 | void alsa_configure_output(struct alsa *alsa, int channels, |
| 381 | int sampling_rate) |
| 382 | { |
| 383 | snd_pcm_t *handle; |
| 384 | int ret; |
| 385 | int i; |
| 386 | |
| 387 | for (i = 0; i < alsa->output_handles_count; i++) { |
| 388 | handle = alsa->output_handles[i]; |
| 389 | |
| 390 | ret = snd_pcm_set_params(handle, SND_PCM_FORMAT_S16_LE, |
| 391 | SND_PCM_ACCESS_RW_INTERLEAVED, |
| 392 | channels, sampling_rate, 0, 0); |
| 393 | igt_assert(ret >= 0); |
| 394 | } |
| 395 | |
| 396 | alsa->output_channels = channels; |
| 397 | alsa->output_sampling_rate = sampling_rate; |
| 398 | } |
| 399 | |
| 400 | /** |
| 401 | * alsa_configure_input: |
| 402 | * @alsa: The target alsa structure |
| 403 | * @channels: The number of channels to test |
| 404 | * @sampling_rate: The sampling rate to test |
| 405 | * |
| 406 | * Configure the input device with the configuration specified by @channels |
| 407 | * and @sampling_rate. |
| 408 | */ |
| 409 | void alsa_configure_input(struct alsa *alsa, int channels, |
| 410 | int sampling_rate) |
| 411 | { |
| 412 | snd_pcm_t *handle; |
| 413 | int ret; |
| 414 | |
| 415 | handle = alsa->input_handle; |
| 416 | |
| 417 | ret = snd_pcm_set_params(handle, SND_PCM_FORMAT_S16_LE, |
| 418 | SND_PCM_ACCESS_RW_INTERLEAVED, channels, |
| 419 | sampling_rate, 0, 0); |
| 420 | igt_assert(ret >= 0); |
| 421 | |
| 422 | alsa->input_channels = channels; |
| 423 | alsa->input_sampling_rate = sampling_rate; |
| 424 | |
| 425 | } |
| 426 | |
| 427 | /** |
| 428 | * alsa_register_output_callback: |
| 429 | * @alsa: The target alsa structure |
| 430 | * @callback: The callback function to call to fill output data |
| 431 | * @callback_data: The data pointer to pass to the callback function |
| 432 | * @samples_trigger: The required number of samples to trigger the callback |
| 433 | * |
| 434 | * Register a callback function to be called to fill output data during a run. |
| 435 | * The callback is called when @samples_trigger samples are required. |
| 436 | * |
| 437 | * The callback should return an integer equal to zero for success and negative |
| 438 | * for failure. |
| 439 | */ |
| 440 | void alsa_register_output_callback(struct alsa *alsa, |
| 441 | int (*callback)(void *data, short *buffer, int samples), |
| 442 | void *callback_data, int samples_trigger) |
| 443 | { |
| 444 | alsa->output_callback = callback; |
| 445 | alsa->output_callback_data = callback_data; |
| 446 | alsa->output_samples_trigger = samples_trigger; |
| 447 | } |
| 448 | |
| 449 | /** |
| 450 | * alsa_register_input_callback: |
| 451 | * @alsa: The target alsa structure |
| 452 | * @callback: The callback function to call when input data is available |
| 453 | * @callback_data: The data pointer to pass to the callback function |
| 454 | * @samples_trigger: The required number of samples to trigger the callback |
| 455 | * |
| 456 | * Register a callback function to be called when input data is available during |
| 457 | * a run. The callback is called when @samples_trigger samples are available. |
| 458 | * |
| 459 | * The callback should return an integer equal to zero for success, negative for |
| 460 | * failure and positive to indicate that the run should stop. |
| 461 | */ |
| 462 | void alsa_register_input_callback(struct alsa *alsa, |
| 463 | int (*callback)(void *data, short *buffer, int samples), |
| 464 | void *callback_data, int samples_trigger) |
| 465 | { |
| 466 | alsa->input_callback = callback; |
| 467 | alsa->input_callback_data = callback_data; |
| 468 | alsa->input_samples_trigger = samples_trigger; |
| 469 | } |
| 470 | |
| 471 | /** |
| 472 | * alsa_run: |
| 473 | * @alsa: The target alsa structure |
| 474 | * @duration_ms: The maximum duration of the run in milliseconds |
| 475 | * |
| 476 | * Run ALSA playback and capture on the input and output devices for at |
| 477 | * most @duration_ms milliseconds, calling the registered callbacks when needed. |
| 478 | * |
| 479 | * Returns: An integer equal to zero for success, positive for a stop caused |
| 480 | * by the input callback and negative for failure |
| 481 | */ |
| 482 | int alsa_run(struct alsa *alsa, int duration_ms) |
| 483 | { |
| 484 | snd_pcm_t *handle; |
| 485 | short *output_buffer = NULL; |
| 486 | short *input_buffer = NULL; |
| 487 | int output_limit; |
| 488 | int output_total = 0; |
| 489 | int output_counts[alsa->output_handles_count]; |
| 490 | bool output_ready = false; |
| 491 | int output_channels; |
| 492 | int output_trigger; |
| 493 | int input_limit; |
| 494 | int input_total = 0; |
| 495 | int input_count = 0; |
| 496 | int input_channels; |
| 497 | int input_trigger; |
| 498 | bool reached; |
| 499 | int index; |
| 500 | int count; |
| 501 | int avail; |
| 502 | int i; |
| 503 | int ret; |
| 504 | |
| 505 | output_limit = alsa->output_sampling_rate * duration_ms / 1000; |
| 506 | output_channels = alsa->output_channels; |
| 507 | output_trigger = alsa->output_samples_trigger; |
| 508 | output_buffer = malloc(sizeof(short) * output_channels * |
| 509 | output_trigger); |
| 510 | |
| 511 | if (alsa->input_callback) { |
| 512 | input_limit = alsa->input_sampling_rate * duration_ms / 1000; |
| 513 | input_trigger = alsa->input_samples_trigger; |
| 514 | input_channels = alsa->input_channels; |
| 515 | input_buffer = malloc(sizeof(short) * input_channels * |
| 516 | input_trigger); |
| 517 | } |
| 518 | |
| 519 | do { |
| 520 | reached = true; |
| 521 | |
| 522 | if (output_total < output_limit) { |
| 523 | reached = false; |
| 524 | |
| 525 | if (!output_ready) { |
| 526 | output_ready = true; |
| 527 | |
| 528 | for (i = 0; i < alsa->output_handles_count; i++) |
| 529 | output_counts[i] = 0; |
| 530 | |
| 531 | ret = alsa->output_callback(alsa->output_callback_data, |
| 532 | output_buffer, |
| 533 | output_trigger); |
| 534 | if (ret < 0) |
| 535 | goto complete; |
| 536 | } |
| 537 | |
| 538 | for (i = 0; i < alsa->output_handles_count; i++) { |
| 539 | handle = alsa->output_handles[i]; |
| 540 | |
| 541 | ret = snd_pcm_avail(handle); |
| 542 | if (output_counts[i] < output_trigger && |
| 543 | ret > 0) { |
| 544 | index = output_counts[i] * |
| 545 | output_channels; |
| 546 | count = output_trigger - |
| 547 | output_counts[i]; |
| 548 | avail = snd_pcm_avail(handle); |
| 549 | |
| 550 | count = avail < count ? avail : count; |
| 551 | |
| 552 | ret = snd_pcm_writei(handle, |
| 553 | &output_buffer[index], |
| 554 | count); |
| 555 | if (ret < 0) { |
| 556 | ret = snd_pcm_recover(handle, |
| 557 | ret, 0); |
| 558 | if (ret < 0) |
| 559 | goto complete; |
| 560 | } |
| 561 | |
| 562 | output_counts[i] += ret; |
| 563 | } else if (output_counts[i] < output_trigger && |
| 564 | ret < 0) { |
| 565 | ret = snd_pcm_recover(handle, ret, 0); |
| 566 | if (ret < 0) |
| 567 | goto complete; |
| 568 | } |
| 569 | } |
| 570 | |
| 571 | output_ready = false; |
| 572 | |
| 573 | for (i = 0; i < alsa->output_handles_count; i++) |
| 574 | if (output_counts[i] < output_trigger) |
| 575 | output_ready = true; |
| 576 | |
| 577 | if (!output_ready) |
| 578 | output_total += output_trigger; |
| 579 | |
| 580 | } |
| 581 | |
| 582 | if (alsa->input_callback && input_total < input_limit) { |
| 583 | reached = false; |
| 584 | |
| 585 | if (input_count == input_trigger) { |
| 586 | input_count = 0; |
| 587 | |
| 588 | ret = alsa->input_callback(alsa->input_callback_data, |
| 589 | input_buffer, |
| 590 | input_trigger); |
| 591 | if (ret != 0) |
| 592 | goto complete; |
| 593 | } |
| 594 | |
| 595 | handle = alsa->input_handle; |
| 596 | |
| 597 | ret = snd_pcm_avail(handle); |
| 598 | if (input_count < input_trigger && |
| 599 | (ret > 0 || input_total == 0)) { |
| 600 | index = input_count * input_channels; |
| 601 | count = input_trigger - input_count; |
| 602 | avail = snd_pcm_avail(handle); |
| 603 | |
| 604 | count = avail > 0 && avail < count ? avail : |
| 605 | count; |
| 606 | |
| 607 | ret = snd_pcm_readi(handle, |
| 608 | &input_buffer[index], |
| 609 | count); |
| 610 | if (ret == -EAGAIN) { |
| 611 | ret = 0; |
| 612 | } else if (ret < 0) { |
| 613 | ret = snd_pcm_recover(handle, ret, 0); |
| 614 | if (ret < 0) |
| 615 | goto complete; |
| 616 | } |
| 617 | |
| 618 | input_count += ret; |
| 619 | input_total += ret; |
| 620 | } else if (input_count < input_trigger && ret < 0) { |
| 621 | ret = snd_pcm_recover(handle, ret, 0); |
| 622 | if (ret < 0) |
| 623 | goto complete; |
| 624 | } |
| 625 | } |
| 626 | } while (!reached); |
| 627 | |
| 628 | ret = 0; |
| 629 | |
| 630 | complete: |
| 631 | if (output_buffer) |
| 632 | free(output_buffer); |
| 633 | |
| 634 | if (input_buffer) |
| 635 | free(input_buffer); |
| 636 | |
| 637 | return ret; |
| 638 | } |