|
|
1.1 ! root 1: /* ! 2: * QEMU ALSA audio driver ! 3: * ! 4: * Copyright (c) 2005 Vassili Karpov (malc) ! 5: * ! 6: * Permission is hereby granted, free of charge, to any person obtaining a copy ! 7: * of this software and associated documentation files (the "Software"), to deal ! 8: * in the Software without restriction, including without limitation the rights ! 9: * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ! 10: * copies of the Software, and to permit persons to whom the Software is ! 11: * furnished to do so, subject to the following conditions: ! 12: * ! 13: * The above copyright notice and this permission notice shall be included in ! 14: * all copies or substantial portions of the Software. ! 15: * ! 16: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ! 17: * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ! 18: * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ! 19: * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ! 20: * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ! 21: * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ! 22: * THE SOFTWARE. ! 23: */ ! 24: #include <alsa/asoundlib.h> ! 25: #include "vl.h" ! 26: ! 27: #define AUDIO_CAP "alsa" ! 28: #include "audio_int.h" ! 29: ! 30: typedef struct ALSAVoiceOut { ! 31: HWVoiceOut hw; ! 32: void *pcm_buf; ! 33: snd_pcm_t *handle; ! 34: } ALSAVoiceOut; ! 35: ! 36: typedef struct ALSAVoiceIn { ! 37: HWVoiceIn hw; ! 38: snd_pcm_t *handle; ! 39: void *pcm_buf; ! 40: } ALSAVoiceIn; ! 41: ! 42: static struct { ! 43: int size_in_usec_in; ! 44: int size_in_usec_out; ! 45: const char *pcm_name_in; ! 46: const char *pcm_name_out; ! 47: unsigned int buffer_size_in; ! 48: unsigned int period_size_in; ! 49: unsigned int buffer_size_out; ! 50: unsigned int period_size_out; ! 51: unsigned int threshold; ! 52: ! 53: int buffer_size_in_overriden; ! 54: int period_size_in_overriden; ! 55: ! 56: int buffer_size_out_overriden; ! 57: int period_size_out_overriden; ! 58: int verbose; ! 59: } conf = { ! 60: #ifdef HIGH_LATENCY ! 61: .size_in_usec_in = 1, ! 62: .size_in_usec_out = 1, ! 63: #endif ! 64: .pcm_name_out = "hw:0,0", ! 65: .pcm_name_in = "hw:0,0", ! 66: #ifdef HIGH_LATENCY ! 67: .buffer_size_in = 400000, ! 68: .period_size_in = 400000 / 4, ! 69: .buffer_size_out = 400000, ! 70: .period_size_out = 400000 / 4, ! 71: #else ! 72: #define DEFAULT_BUFFER_SIZE 1024 ! 73: #define DEFAULT_PERIOD_SIZE 256 ! 74: .buffer_size_in = DEFAULT_BUFFER_SIZE * 4, ! 75: .period_size_in = DEFAULT_PERIOD_SIZE * 4, ! 76: .buffer_size_out = DEFAULT_BUFFER_SIZE, ! 77: .period_size_out = DEFAULT_PERIOD_SIZE, ! 78: .buffer_size_in_overriden = 0, ! 79: .buffer_size_out_overriden = 0, ! 80: .period_size_in_overriden = 0, ! 81: .period_size_out_overriden = 0, ! 82: #endif ! 83: .threshold = 0, ! 84: .verbose = 0 ! 85: }; ! 86: ! 87: struct alsa_params_req { ! 88: int freq; ! 89: audfmt_e fmt; ! 90: int nchannels; ! 91: unsigned int buffer_size; ! 92: unsigned int period_size; ! 93: }; ! 94: ! 95: struct alsa_params_obt { ! 96: int freq; ! 97: audfmt_e fmt; ! 98: int nchannels; ! 99: snd_pcm_uframes_t samples; ! 100: }; ! 101: ! 102: static void GCC_FMT_ATTR (2, 3) alsa_logerr (int err, const char *fmt, ...) ! 103: { ! 104: va_list ap; ! 105: ! 106: va_start (ap, fmt); ! 107: AUD_vlog (AUDIO_CAP, fmt, ap); ! 108: va_end (ap); ! 109: ! 110: AUD_log (AUDIO_CAP, "Reason: %s\n", snd_strerror (err)); ! 111: } ! 112: ! 113: static void GCC_FMT_ATTR (3, 4) alsa_logerr2 ( ! 114: int err, ! 115: const char *typ, ! 116: const char *fmt, ! 117: ... ! 118: ) ! 119: { ! 120: va_list ap; ! 121: ! 122: AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ); ! 123: ! 124: va_start (ap, fmt); ! 125: AUD_vlog (AUDIO_CAP, fmt, ap); ! 126: va_end (ap); ! 127: ! 128: AUD_log (AUDIO_CAP, "Reason: %s\n", snd_strerror (err)); ! 129: } ! 130: ! 131: static void alsa_anal_close (snd_pcm_t **handlep) ! 132: { ! 133: int err = snd_pcm_close (*handlep); ! 134: if (err) { ! 135: alsa_logerr (err, "Failed to close PCM handle %p\n", *handlep); ! 136: } ! 137: *handlep = NULL; ! 138: } ! 139: ! 140: static int alsa_write (SWVoiceOut *sw, void *buf, int len) ! 141: { ! 142: return audio_pcm_sw_write (sw, buf, len); ! 143: } ! 144: ! 145: static int aud_to_alsafmt (audfmt_e fmt) ! 146: { ! 147: switch (fmt) { ! 148: case AUD_FMT_S8: ! 149: return SND_PCM_FORMAT_S8; ! 150: ! 151: case AUD_FMT_U8: ! 152: return SND_PCM_FORMAT_U8; ! 153: ! 154: case AUD_FMT_S16: ! 155: return SND_PCM_FORMAT_S16_LE; ! 156: ! 157: case AUD_FMT_U16: ! 158: return SND_PCM_FORMAT_U16_LE; ! 159: ! 160: default: ! 161: dolog ("Internal logic error: Bad audio format %d\n", fmt); ! 162: #ifdef DEBUG_AUDIO ! 163: abort (); ! 164: #endif ! 165: return SND_PCM_FORMAT_U8; ! 166: } ! 167: } ! 168: ! 169: static int alsa_to_audfmt (int alsafmt, audfmt_e *fmt, int *endianness) ! 170: { ! 171: switch (alsafmt) { ! 172: case SND_PCM_FORMAT_S8: ! 173: *endianness = 0; ! 174: *fmt = AUD_FMT_S8; ! 175: break; ! 176: ! 177: case SND_PCM_FORMAT_U8: ! 178: *endianness = 0; ! 179: *fmt = AUD_FMT_U8; ! 180: break; ! 181: ! 182: case SND_PCM_FORMAT_S16_LE: ! 183: *endianness = 0; ! 184: *fmt = AUD_FMT_S16; ! 185: break; ! 186: ! 187: case SND_PCM_FORMAT_U16_LE: ! 188: *endianness = 0; ! 189: *fmt = AUD_FMT_U16; ! 190: break; ! 191: ! 192: case SND_PCM_FORMAT_S16_BE: ! 193: *endianness = 1; ! 194: *fmt = AUD_FMT_S16; ! 195: break; ! 196: ! 197: case SND_PCM_FORMAT_U16_BE: ! 198: *endianness = 1; ! 199: *fmt = AUD_FMT_U16; ! 200: break; ! 201: ! 202: default: ! 203: dolog ("Unrecognized audio format %d\n", alsafmt); ! 204: return -1; ! 205: } ! 206: ! 207: return 0; ! 208: } ! 209: ! 210: #if defined DEBUG_MISMATCHES || defined DEBUG ! 211: static void alsa_dump_info (struct alsa_params_req *req, ! 212: struct alsa_params_obt *obt) ! 213: { ! 214: dolog ("parameter | requested value | obtained value\n"); ! 215: dolog ("format | %10d | %10d\n", req->fmt, obt->fmt); ! 216: dolog ("channels | %10d | %10d\n", ! 217: req->nchannels, obt->nchannels); ! 218: dolog ("frequency | %10d | %10d\n", req->freq, obt->freq); ! 219: dolog ("============================================\n"); ! 220: dolog ("requested: buffer size %d period size %d\n", ! 221: req->buffer_size, req->period_size); ! 222: dolog ("obtained: samples %ld\n", obt->samples); ! 223: } ! 224: #endif ! 225: ! 226: static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold) ! 227: { ! 228: int err; ! 229: snd_pcm_sw_params_t *sw_params; ! 230: ! 231: snd_pcm_sw_params_alloca (&sw_params); ! 232: ! 233: err = snd_pcm_sw_params_current (handle, sw_params); ! 234: if (err < 0) { ! 235: dolog ("Could not fully initialize DAC\n"); ! 236: alsa_logerr (err, "Failed to get current software parameters\n"); ! 237: return; ! 238: } ! 239: ! 240: err = snd_pcm_sw_params_set_start_threshold (handle, sw_params, threshold); ! 241: if (err < 0) { ! 242: dolog ("Could not fully initialize DAC\n"); ! 243: alsa_logerr (err, "Failed to set software threshold to %ld\n", ! 244: threshold); ! 245: return; ! 246: } ! 247: ! 248: err = snd_pcm_sw_params (handle, sw_params); ! 249: if (err < 0) { ! 250: dolog ("Could not fully initialize DAC\n"); ! 251: alsa_logerr (err, "Failed to set software parameters\n"); ! 252: return; ! 253: } ! 254: } ! 255: ! 256: static int alsa_open (int in, struct alsa_params_req *req, ! 257: struct alsa_params_obt *obt, snd_pcm_t **handlep) ! 258: { ! 259: snd_pcm_t *handle; ! 260: snd_pcm_hw_params_t *hw_params; ! 261: int err, freq, nchannels; ! 262: const char *pcm_name = in ? conf.pcm_name_in : conf.pcm_name_out; ! 263: unsigned int period_size, buffer_size; ! 264: snd_pcm_uframes_t obt_buffer_size; ! 265: const char *typ = in ? "ADC" : "DAC"; ! 266: ! 267: freq = req->freq; ! 268: period_size = req->period_size; ! 269: buffer_size = req->buffer_size; ! 270: nchannels = req->nchannels; ! 271: ! 272: snd_pcm_hw_params_alloca (&hw_params); ! 273: ! 274: err = snd_pcm_open ( ! 275: &handle, ! 276: pcm_name, ! 277: in ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, ! 278: SND_PCM_NONBLOCK ! 279: ); ! 280: if (err < 0) { ! 281: alsa_logerr2 (err, typ, "Failed to open `%s':\n", pcm_name); ! 282: return -1; ! 283: } ! 284: ! 285: err = snd_pcm_hw_params_any (handle, hw_params); ! 286: if (err < 0) { ! 287: alsa_logerr2 (err, typ, "Failed to initialize hardware parameters\n"); ! 288: goto err; ! 289: } ! 290: ! 291: err = snd_pcm_hw_params_set_access ( ! 292: handle, ! 293: hw_params, ! 294: SND_PCM_ACCESS_RW_INTERLEAVED ! 295: ); ! 296: if (err < 0) { ! 297: alsa_logerr2 (err, typ, "Failed to set access type\n"); ! 298: goto err; ! 299: } ! 300: ! 301: err = snd_pcm_hw_params_set_format (handle, hw_params, req->fmt); ! 302: if (err < 0) { ! 303: alsa_logerr2 (err, typ, "Failed to set format %d\n", req->fmt); ! 304: goto err; ! 305: } ! 306: ! 307: err = snd_pcm_hw_params_set_rate_near (handle, hw_params, &freq, 0); ! 308: if (err < 0) { ! 309: alsa_logerr2 (err, typ, "Failed to set frequency %d\n", req->freq); ! 310: goto err; ! 311: } ! 312: ! 313: err = snd_pcm_hw_params_set_channels_near ( ! 314: handle, ! 315: hw_params, ! 316: &nchannels ! 317: ); ! 318: if (err < 0) { ! 319: alsa_logerr2 (err, typ, "Failed to set number of channels %d\n", ! 320: req->nchannels); ! 321: goto err; ! 322: } ! 323: ! 324: if (nchannels != 1 && nchannels != 2) { ! 325: alsa_logerr2 (err, typ, ! 326: "Can not handle obtained number of channels %d\n", ! 327: nchannels); ! 328: goto err; ! 329: } ! 330: ! 331: if (!((in && conf.size_in_usec_in) || (!in && conf.size_in_usec_out))) { ! 332: if (!buffer_size) { ! 333: buffer_size = DEFAULT_BUFFER_SIZE; ! 334: period_size= DEFAULT_PERIOD_SIZE; ! 335: } ! 336: } ! 337: ! 338: if (buffer_size) { ! 339: if ((in && conf.size_in_usec_in) || (!in && conf.size_in_usec_out)) { ! 340: if (period_size) { ! 341: err = snd_pcm_hw_params_set_period_time_near ( ! 342: handle, ! 343: hw_params, ! 344: &period_size, ! 345: 0 ! 346: ); ! 347: if (err < 0) { ! 348: alsa_logerr2 (err, typ, ! 349: "Failed to set period time %d\n", ! 350: req->period_size); ! 351: goto err; ! 352: } ! 353: } ! 354: ! 355: err = snd_pcm_hw_params_set_buffer_time_near ( ! 356: handle, ! 357: hw_params, ! 358: &buffer_size, ! 359: 0 ! 360: ); ! 361: ! 362: if (err < 0) { ! 363: alsa_logerr2 (err, typ, ! 364: "Failed to set buffer time %d\n", ! 365: req->buffer_size); ! 366: goto err; ! 367: } ! 368: } ! 369: else { ! 370: int dir; ! 371: snd_pcm_uframes_t minval; ! 372: ! 373: if (period_size) { ! 374: minval = period_size; ! 375: dir = 0; ! 376: ! 377: err = snd_pcm_hw_params_get_period_size_min ( ! 378: hw_params, ! 379: &minval, ! 380: &dir ! 381: ); ! 382: if (err < 0) { ! 383: alsa_logerr ( ! 384: err, ! 385: "Could not get minmal period size for %s\n", ! 386: typ ! 387: ); ! 388: } ! 389: else { ! 390: if (period_size < minval) { ! 391: if ((in && conf.period_size_in_overriden) ! 392: || (!in && conf.period_size_out_overriden)) { ! 393: dolog ("%s period size(%d) is less " ! 394: "than minmal period size(%ld)\n", ! 395: typ, ! 396: period_size, ! 397: minval); ! 398: } ! 399: period_size = minval; ! 400: } ! 401: } ! 402: ! 403: err = snd_pcm_hw_params_set_period_size ( ! 404: handle, ! 405: hw_params, ! 406: period_size, ! 407: 0 ! 408: ); ! 409: if (err < 0) { ! 410: alsa_logerr2 (err, typ, "Failed to set period size %d\n", ! 411: req->period_size); ! 412: goto err; ! 413: } ! 414: } ! 415: ! 416: minval = buffer_size; ! 417: err = snd_pcm_hw_params_get_buffer_size_min ( ! 418: hw_params, ! 419: &minval ! 420: ); ! 421: if (err < 0) { ! 422: alsa_logerr (err, "Could not get minmal buffer size for %s\n", ! 423: typ); ! 424: } ! 425: else { ! 426: if (buffer_size < minval) { ! 427: if ((in && conf.buffer_size_in_overriden) ! 428: || (!in && conf.buffer_size_out_overriden)) { ! 429: dolog ( ! 430: "%s buffer size(%d) is less " ! 431: "than minimal buffer size(%ld)\n", ! 432: typ, ! 433: buffer_size, ! 434: minval ! 435: ); ! 436: } ! 437: buffer_size = minval; ! 438: } ! 439: } ! 440: ! 441: err = snd_pcm_hw_params_set_buffer_size ( ! 442: handle, ! 443: hw_params, ! 444: buffer_size ! 445: ); ! 446: if (err < 0) { ! 447: alsa_logerr2 (err, typ, "Failed to set buffer size %d\n", ! 448: req->buffer_size); ! 449: goto err; ! 450: } ! 451: } ! 452: } ! 453: else { ! 454: dolog ("warning: Buffer size is not set\n"); ! 455: } ! 456: ! 457: err = snd_pcm_hw_params (handle, hw_params); ! 458: if (err < 0) { ! 459: alsa_logerr2 (err, typ, "Failed to apply audio parameters\n"); ! 460: goto err; ! 461: } ! 462: ! 463: err = snd_pcm_hw_params_get_buffer_size (hw_params, &obt_buffer_size); ! 464: if (err < 0) { ! 465: alsa_logerr2 (err, typ, "Failed to get buffer size\n"); ! 466: goto err; ! 467: } ! 468: ! 469: err = snd_pcm_prepare (handle); ! 470: if (err < 0) { ! 471: alsa_logerr2 (err, typ, "Could not prepare handle %p\n", handle); ! 472: goto err; ! 473: } ! 474: ! 475: if (!in && conf.threshold) { ! 476: snd_pcm_uframes_t threshold; ! 477: int bytes_per_sec; ! 478: ! 479: bytes_per_sec = freq ! 480: << (nchannels == 2) ! 481: << (req->fmt == AUD_FMT_S16 || req->fmt == AUD_FMT_U16); ! 482: ! 483: threshold = (conf.threshold * bytes_per_sec) / 1000; ! 484: alsa_set_threshold (handle, threshold); ! 485: } ! 486: ! 487: obt->fmt = req->fmt; ! 488: obt->nchannels = nchannels; ! 489: obt->freq = freq; ! 490: obt->samples = obt_buffer_size; ! 491: *handlep = handle; ! 492: ! 493: #if defined DEBUG_MISMATCHES || defined DEBUG ! 494: if (obt->fmt != req->fmt || ! 495: obt->nchannels != req->nchannels || ! 496: obt->freq != req->freq) { ! 497: dolog ("Audio paramters mismatch for %s\n", typ); ! 498: alsa_dump_info (req, obt); ! 499: } ! 500: #endif ! 501: ! 502: #ifdef DEBUG ! 503: alsa_dump_info (req, obt); ! 504: #endif ! 505: return 0; ! 506: ! 507: err: ! 508: alsa_anal_close (&handle); ! 509: return -1; ! 510: } ! 511: ! 512: static int alsa_recover (snd_pcm_t *handle) ! 513: { ! 514: int err = snd_pcm_prepare (handle); ! 515: if (err < 0) { ! 516: alsa_logerr (err, "Failed to prepare handle %p\n", handle); ! 517: return -1; ! 518: } ! 519: return 0; ! 520: } ! 521: ! 522: static snd_pcm_sframes_t alsa_get_avail (snd_pcm_t *handle) ! 523: { ! 524: snd_pcm_sframes_t avail; ! 525: ! 526: avail = snd_pcm_avail_update (handle); ! 527: if (avail < 0) { ! 528: if (avail == -EPIPE) { ! 529: if (!alsa_recover (handle)) { ! 530: avail = snd_pcm_avail_update (handle); ! 531: } ! 532: } ! 533: ! 534: if (avail < 0) { ! 535: alsa_logerr (avail, ! 536: "Could not obtain number of available frames\n"); ! 537: return -1; ! 538: } ! 539: } ! 540: ! 541: return avail; ! 542: } ! 543: ! 544: static int alsa_run_out (HWVoiceOut *hw) ! 545: { ! 546: ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; ! 547: int rpos, live, decr; ! 548: int samples; ! 549: uint8_t *dst; ! 550: st_sample_t *src; ! 551: snd_pcm_sframes_t avail; ! 552: ! 553: live = audio_pcm_hw_get_live_out (hw); ! 554: if (!live) { ! 555: return 0; ! 556: } ! 557: ! 558: avail = alsa_get_avail (alsa->handle); ! 559: if (avail < 0) { ! 560: dolog ("Could not get number of available playback frames\n"); ! 561: return 0; ! 562: } ! 563: ! 564: decr = audio_MIN (live, avail); ! 565: samples = decr; ! 566: rpos = hw->rpos; ! 567: while (samples) { ! 568: int left_till_end_samples = hw->samples - rpos; ! 569: int len = audio_MIN (samples, left_till_end_samples); ! 570: snd_pcm_sframes_t written; ! 571: ! 572: src = hw->mix_buf + rpos; ! 573: dst = advance (alsa->pcm_buf, rpos << hw->info.shift); ! 574: ! 575: hw->clip (dst, src, len); ! 576: ! 577: while (len) { ! 578: written = snd_pcm_writei (alsa->handle, dst, len); ! 579: ! 580: if (written <= 0) { ! 581: switch (written) { ! 582: case 0: ! 583: if (conf.verbose) { ! 584: dolog ("Failed to write %d frames (wrote zero)\n", len); ! 585: } ! 586: goto exit; ! 587: ! 588: case -EPIPE: ! 589: if (alsa_recover (alsa->handle)) { ! 590: alsa_logerr (written, "Failed to write %d frames\n", ! 591: len); ! 592: goto exit; ! 593: } ! 594: if (conf.verbose) { ! 595: dolog ("Recovering from playback xrun\n"); ! 596: } ! 597: continue; ! 598: ! 599: case -EAGAIN: ! 600: goto exit; ! 601: ! 602: default: ! 603: alsa_logerr (written, "Failed to write %d frames to %p\n", ! 604: len, dst); ! 605: goto exit; ! 606: } ! 607: } ! 608: ! 609: mixeng_clear (src, written); ! 610: rpos = (rpos + written) % hw->samples; ! 611: samples -= written; ! 612: len -= written; ! 613: dst = advance (dst, written << hw->info.shift); ! 614: src += written; ! 615: } ! 616: } ! 617: ! 618: exit: ! 619: hw->rpos = rpos; ! 620: return decr; ! 621: } ! 622: ! 623: static void alsa_fini_out (HWVoiceOut *hw) ! 624: { ! 625: ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; ! 626: ! 627: ldebug ("alsa_fini\n"); ! 628: alsa_anal_close (&alsa->handle); ! 629: ! 630: if (alsa->pcm_buf) { ! 631: qemu_free (alsa->pcm_buf); ! 632: alsa->pcm_buf = NULL; ! 633: } ! 634: } ! 635: ! 636: static int alsa_init_out (HWVoiceOut *hw, audsettings_t *as) ! 637: { ! 638: ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; ! 639: struct alsa_params_req req; ! 640: struct alsa_params_obt obt; ! 641: audfmt_e effective_fmt; ! 642: int endianness; ! 643: int err; ! 644: snd_pcm_t *handle; ! 645: audsettings_t obt_as; ! 646: ! 647: req.fmt = aud_to_alsafmt (as->fmt); ! 648: req.freq = as->freq; ! 649: req.nchannels = as->nchannels; ! 650: req.period_size = conf.period_size_out; ! 651: req.buffer_size = conf.buffer_size_out; ! 652: ! 653: if (alsa_open (0, &req, &obt, &handle)) { ! 654: return -1; ! 655: } ! 656: ! 657: err = alsa_to_audfmt (obt.fmt, &effective_fmt, &endianness); ! 658: if (err) { ! 659: alsa_anal_close (&handle); ! 660: return -1; ! 661: } ! 662: ! 663: obt_as.freq = obt.freq; ! 664: obt_as.nchannels = obt.nchannels; ! 665: obt_as.fmt = effective_fmt; ! 666: ! 667: audio_pcm_init_info ( ! 668: &hw->info, ! 669: &obt_as, ! 670: audio_need_to_swap_endian (endianness) ! 671: ); ! 672: hw->samples = obt.samples; ! 673: ! 674: alsa->pcm_buf = audio_calloc (AUDIO_FUNC, obt.samples, 1 << hw->info.shift); ! 675: if (!alsa->pcm_buf) { ! 676: dolog ("Could not allocate DAC buffer (%d samples, each %d bytes)\n", ! 677: hw->samples, 1 << hw->info.shift); ! 678: alsa_anal_close (&handle); ! 679: return -1; ! 680: } ! 681: ! 682: alsa->handle = handle; ! 683: return 0; ! 684: } ! 685: ! 686: static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int pause) ! 687: { ! 688: int err; ! 689: ! 690: if (pause) { ! 691: err = snd_pcm_drop (handle); ! 692: if (err < 0) { ! 693: alsa_logerr (err, "Could not stop %s\n", typ); ! 694: return -1; ! 695: } ! 696: } ! 697: else { ! 698: err = snd_pcm_prepare (handle); ! 699: if (err < 0) { ! 700: alsa_logerr (err, "Could not prepare handle for %s\n", typ); ! 701: return -1; ! 702: } ! 703: } ! 704: ! 705: return 0; ! 706: } ! 707: ! 708: static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...) ! 709: { ! 710: ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; ! 711: ! 712: switch (cmd) { ! 713: case VOICE_ENABLE: ! 714: ldebug ("enabling voice\n"); ! 715: return alsa_voice_ctl (alsa->handle, "playback", 0); ! 716: ! 717: case VOICE_DISABLE: ! 718: ldebug ("disabling voice\n"); ! 719: return alsa_voice_ctl (alsa->handle, "playback", 1); ! 720: } ! 721: ! 722: return -1; ! 723: } ! 724: ! 725: static int alsa_init_in (HWVoiceIn *hw, audsettings_t *as) ! 726: { ! 727: ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; ! 728: struct alsa_params_req req; ! 729: struct alsa_params_obt obt; ! 730: int endianness; ! 731: int err; ! 732: audfmt_e effective_fmt; ! 733: snd_pcm_t *handle; ! 734: audsettings_t obt_as; ! 735: ! 736: req.fmt = aud_to_alsafmt (as->fmt); ! 737: req.freq = as->freq; ! 738: req.nchannels = as->nchannels; ! 739: req.period_size = conf.period_size_in; ! 740: req.buffer_size = conf.buffer_size_in; ! 741: ! 742: if (alsa_open (1, &req, &obt, &handle)) { ! 743: return -1; ! 744: } ! 745: ! 746: err = alsa_to_audfmt (obt.fmt, &effective_fmt, &endianness); ! 747: if (err) { ! 748: alsa_anal_close (&handle); ! 749: return -1; ! 750: } ! 751: ! 752: obt_as.freq = obt.freq; ! 753: obt_as.nchannels = obt.nchannels; ! 754: obt_as.fmt = effective_fmt; ! 755: ! 756: audio_pcm_init_info ( ! 757: &hw->info, ! 758: &obt_as, ! 759: audio_need_to_swap_endian (endianness) ! 760: ); ! 761: hw->samples = obt.samples; ! 762: ! 763: alsa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); ! 764: if (!alsa->pcm_buf) { ! 765: dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n", ! 766: hw->samples, 1 << hw->info.shift); ! 767: alsa_anal_close (&handle); ! 768: return -1; ! 769: } ! 770: ! 771: alsa->handle = handle; ! 772: return 0; ! 773: } ! 774: ! 775: static void alsa_fini_in (HWVoiceIn *hw) ! 776: { ! 777: ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; ! 778: ! 779: alsa_anal_close (&alsa->handle); ! 780: ! 781: if (alsa->pcm_buf) { ! 782: qemu_free (alsa->pcm_buf); ! 783: alsa->pcm_buf = NULL; ! 784: } ! 785: } ! 786: ! 787: static int alsa_run_in (HWVoiceIn *hw) ! 788: { ! 789: ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; ! 790: int hwshift = hw->info.shift; ! 791: int i; ! 792: int live = audio_pcm_hw_get_live_in (hw); ! 793: int dead = hw->samples - live; ! 794: int decr; ! 795: struct { ! 796: int add; ! 797: int len; ! 798: } bufs[2] = { ! 799: { hw->wpos, 0 }, ! 800: { 0, 0 } ! 801: }; ! 802: snd_pcm_sframes_t avail; ! 803: snd_pcm_uframes_t read_samples = 0; ! 804: ! 805: if (!dead) { ! 806: return 0; ! 807: } ! 808: ! 809: avail = alsa_get_avail (alsa->handle); ! 810: if (avail < 0) { ! 811: dolog ("Could not get number of captured frames\n"); ! 812: return 0; ! 813: } ! 814: ! 815: if (!avail && (snd_pcm_state (alsa->handle) == SND_PCM_STATE_PREPARED)) { ! 816: avail = hw->samples; ! 817: } ! 818: ! 819: decr = audio_MIN (dead, avail); ! 820: if (!decr) { ! 821: return 0; ! 822: } ! 823: ! 824: if (hw->wpos + decr > hw->samples) { ! 825: bufs[0].len = (hw->samples - hw->wpos); ! 826: bufs[1].len = (decr - (hw->samples - hw->wpos)); ! 827: } ! 828: else { ! 829: bufs[0].len = decr; ! 830: } ! 831: ! 832: for (i = 0; i < 2; ++i) { ! 833: void *src; ! 834: st_sample_t *dst; ! 835: snd_pcm_sframes_t nread; ! 836: snd_pcm_uframes_t len; ! 837: ! 838: len = bufs[i].len; ! 839: ! 840: src = advance (alsa->pcm_buf, bufs[i].add << hwshift); ! 841: dst = hw->conv_buf + bufs[i].add; ! 842: ! 843: while (len) { ! 844: nread = snd_pcm_readi (alsa->handle, src, len); ! 845: ! 846: if (nread <= 0) { ! 847: switch (nread) { ! 848: case 0: ! 849: if (conf.verbose) { ! 850: dolog ("Failed to read %ld frames (read zero)\n", len); ! 851: } ! 852: goto exit; ! 853: ! 854: case -EPIPE: ! 855: if (alsa_recover (alsa->handle)) { ! 856: alsa_logerr (nread, "Failed to read %ld frames\n", len); ! 857: goto exit; ! 858: } ! 859: if (conf.verbose) { ! 860: dolog ("Recovering from capture xrun\n"); ! 861: } ! 862: continue; ! 863: ! 864: case -EAGAIN: ! 865: goto exit; ! 866: ! 867: default: ! 868: alsa_logerr ( ! 869: nread, ! 870: "Failed to read %ld frames from %p\n", ! 871: len, ! 872: src ! 873: ); ! 874: goto exit; ! 875: } ! 876: } ! 877: ! 878: hw->conv (dst, src, nread, &nominal_volume); ! 879: ! 880: src = advance (src, nread << hwshift); ! 881: dst += nread; ! 882: ! 883: read_samples += nread; ! 884: len -= nread; ! 885: } ! 886: } ! 887: ! 888: exit: ! 889: hw->wpos = (hw->wpos + read_samples) % hw->samples; ! 890: return read_samples; ! 891: } ! 892: ! 893: static int alsa_read (SWVoiceIn *sw, void *buf, int size) ! 894: { ! 895: return audio_pcm_sw_read (sw, buf, size); ! 896: } ! 897: ! 898: static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...) ! 899: { ! 900: ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; ! 901: ! 902: switch (cmd) { ! 903: case VOICE_ENABLE: ! 904: ldebug ("enabling voice\n"); ! 905: return alsa_voice_ctl (alsa->handle, "capture", 0); ! 906: ! 907: case VOICE_DISABLE: ! 908: ldebug ("disabling voice\n"); ! 909: return alsa_voice_ctl (alsa->handle, "capture", 1); ! 910: } ! 911: ! 912: return -1; ! 913: } ! 914: ! 915: static void *alsa_audio_init (void) ! 916: { ! 917: return &conf; ! 918: } ! 919: ! 920: static void alsa_audio_fini (void *opaque) ! 921: { ! 922: (void) opaque; ! 923: } ! 924: ! 925: static struct audio_option alsa_options[] = { ! 926: {"DAC_SIZE_IN_USEC", AUD_OPT_BOOL, &conf.size_in_usec_out, ! 927: "DAC period/buffer size in microseconds (otherwise in frames)", NULL, 0}, ! 928: {"DAC_PERIOD_SIZE", AUD_OPT_INT, &conf.period_size_out, ! 929: "DAC period size", &conf.period_size_out_overriden, 0}, ! 930: {"DAC_BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_size_out, ! 931: "DAC buffer size", &conf.buffer_size_out_overriden, 0}, ! 932: ! 933: {"ADC_SIZE_IN_USEC", AUD_OPT_BOOL, &conf.size_in_usec_in, ! 934: "ADC period/buffer size in microseconds (otherwise in frames)", NULL, 0}, ! 935: {"ADC_PERIOD_SIZE", AUD_OPT_INT, &conf.period_size_in, ! 936: "ADC period size", &conf.period_size_in_overriden, 0}, ! 937: {"ADC_BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_size_in, ! 938: "ADC buffer size", &conf.buffer_size_in_overriden, 0}, ! 939: ! 940: {"THRESHOLD", AUD_OPT_INT, &conf.threshold, ! 941: "(undocumented)", NULL, 0}, ! 942: ! 943: {"DAC_DEV", AUD_OPT_STR, &conf.pcm_name_out, ! 944: "DAC device name (for instance dmix)", NULL, 0}, ! 945: ! 946: {"ADC_DEV", AUD_OPT_STR, &conf.pcm_name_in, ! 947: "ADC device name", NULL, 0}, ! 948: ! 949: {"VERBOSE", AUD_OPT_BOOL, &conf.verbose, ! 950: "Behave in a more verbose way", NULL, 0}, ! 951: ! 952: {NULL, 0, NULL, NULL, NULL, 0} ! 953: }; ! 954: ! 955: static struct audio_pcm_ops alsa_pcm_ops = { ! 956: alsa_init_out, ! 957: alsa_fini_out, ! 958: alsa_run_out, ! 959: alsa_write, ! 960: alsa_ctl_out, ! 961: ! 962: alsa_init_in, ! 963: alsa_fini_in, ! 964: alsa_run_in, ! 965: alsa_read, ! 966: alsa_ctl_in ! 967: }; ! 968: ! 969: struct audio_driver alsa_audio_driver = { ! 970: INIT_FIELD (name = ) "alsa", ! 971: INIT_FIELD (descr = ) "ALSA http://www.alsa-project.org", ! 972: INIT_FIELD (options = ) alsa_options, ! 973: INIT_FIELD (init = ) alsa_audio_init, ! 974: INIT_FIELD (fini = ) alsa_audio_fini, ! 975: INIT_FIELD (pcm_ops = ) &alsa_pcm_ops, ! 976: INIT_FIELD (can_be_default = ) 1, ! 977: INIT_FIELD (max_voices_out = ) INT_MAX, ! 978: INIT_FIELD (max_voices_in = ) INT_MAX, ! 979: INIT_FIELD (voice_size_out = ) sizeof (ALSAVoiceOut), ! 980: INIT_FIELD (voice_size_in = ) sizeof (ALSAVoiceIn) ! 981: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.