|
|
1.1 ! root 1: /* ! 2: * QEMU ESD audio driver ! 3: * ! 4: * Copyright (c) 2006 Frederick Reeve (brushed up by 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 <esd.h> ! 25: #include "qemu-common.h" ! 26: #include "audio.h" ! 27: #include <signal.h> ! 28: ! 29: #define AUDIO_CAP "esd" ! 30: #include "audio_int.h" ! 31: #include "audio_pt_int.h" ! 32: ! 33: typedef struct { ! 34: HWVoiceOut hw; ! 35: int done; ! 36: int live; ! 37: int decr; ! 38: int rpos; ! 39: void *pcm_buf; ! 40: int fd; ! 41: struct audio_pt pt; ! 42: } ESDVoiceOut; ! 43: ! 44: typedef struct { ! 45: HWVoiceIn hw; ! 46: int done; ! 47: int dead; ! 48: int incr; ! 49: int wpos; ! 50: void *pcm_buf; ! 51: int fd; ! 52: struct audio_pt pt; ! 53: } ESDVoiceIn; ! 54: ! 55: static struct { ! 56: int samples; ! 57: int divisor; ! 58: char *dac_host; ! 59: char *adc_host; ! 60: } conf = { ! 61: 1024, ! 62: 2, ! 63: NULL, ! 64: NULL ! 65: }; ! 66: ! 67: static void GCC_FMT_ATTR (2, 3) qesd_logerr (int err, const char *fmt, ...) ! 68: { ! 69: va_list ap; ! 70: ! 71: va_start (ap, fmt); ! 72: AUD_vlog (AUDIO_CAP, fmt, ap); ! 73: va_end (ap); ! 74: ! 75: AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err)); ! 76: } ! 77: ! 78: /* playback */ ! 79: static void *qesd_thread_out (void *arg) ! 80: { ! 81: ESDVoiceOut *esd = arg; ! 82: HWVoiceOut *hw = &esd->hw; ! 83: int threshold; ! 84: ! 85: threshold = conf.divisor ? hw->samples / conf.divisor : 0; ! 86: ! 87: if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) { ! 88: return NULL; ! 89: } ! 90: ! 91: for (;;) { ! 92: int decr, to_mix, rpos; ! 93: ! 94: for (;;) { ! 95: if (esd->done) { ! 96: goto exit; ! 97: } ! 98: ! 99: if (esd->live > threshold) { ! 100: break; ! 101: } ! 102: ! 103: if (audio_pt_wait (&esd->pt, AUDIO_FUNC)) { ! 104: goto exit; ! 105: } ! 106: } ! 107: ! 108: decr = to_mix = esd->live; ! 109: rpos = hw->rpos; ! 110: ! 111: if (audio_pt_unlock (&esd->pt, AUDIO_FUNC)) { ! 112: return NULL; ! 113: } ! 114: ! 115: while (to_mix) { ! 116: ssize_t written; ! 117: int chunk = audio_MIN (to_mix, hw->samples - rpos); ! 118: struct st_sample *src = hw->mix_buf + rpos; ! 119: ! 120: hw->clip (esd->pcm_buf, src, chunk); ! 121: ! 122: again: ! 123: written = write (esd->fd, esd->pcm_buf, chunk << hw->info.shift); ! 124: if (written == -1) { ! 125: if (errno == EINTR || errno == EAGAIN) { ! 126: goto again; ! 127: } ! 128: qesd_logerr (errno, "write failed\n"); ! 129: return NULL; ! 130: } ! 131: ! 132: if (written != chunk << hw->info.shift) { ! 133: int wsamples = written >> hw->info.shift; ! 134: int wbytes = wsamples << hw->info.shift; ! 135: if (wbytes != written) { ! 136: dolog ("warning: Misaligned write %d (requested %d), " ! 137: "alignment %d\n", ! 138: wbytes, written, hw->info.align + 1); ! 139: } ! 140: to_mix -= wsamples; ! 141: rpos = (rpos + wsamples) % hw->samples; ! 142: break; ! 143: } ! 144: ! 145: rpos = (rpos + chunk) % hw->samples; ! 146: to_mix -= chunk; ! 147: } ! 148: ! 149: if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) { ! 150: return NULL; ! 151: } ! 152: ! 153: esd->rpos = rpos; ! 154: esd->live -= decr; ! 155: esd->decr += decr; ! 156: } ! 157: ! 158: exit: ! 159: audio_pt_unlock (&esd->pt, AUDIO_FUNC); ! 160: return NULL; ! 161: } ! 162: ! 163: static int qesd_run_out (HWVoiceOut *hw) ! 164: { ! 165: int live, decr; ! 166: ESDVoiceOut *esd = (ESDVoiceOut *) hw; ! 167: ! 168: if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) { ! 169: return 0; ! 170: } ! 171: ! 172: live = audio_pcm_hw_get_live_out (hw); ! 173: decr = audio_MIN (live, esd->decr); ! 174: esd->decr -= decr; ! 175: esd->live = live - decr; ! 176: hw->rpos = esd->rpos; ! 177: if (esd->live > 0) { ! 178: audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC); ! 179: } ! 180: else { ! 181: audio_pt_unlock (&esd->pt, AUDIO_FUNC); ! 182: } ! 183: return decr; ! 184: } ! 185: ! 186: static int qesd_write (SWVoiceOut *sw, void *buf, int len) ! 187: { ! 188: return audio_pcm_sw_write (sw, buf, len); ! 189: } ! 190: ! 191: static int qesd_init_out (HWVoiceOut *hw, struct audsettings *as) ! 192: { ! 193: ESDVoiceOut *esd = (ESDVoiceOut *) hw; ! 194: struct audsettings obt_as = *as; ! 195: int esdfmt = ESD_STREAM | ESD_PLAY; ! 196: int err; ! 197: sigset_t set, old_set; ! 198: ! 199: sigfillset (&set); ! 200: ! 201: esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO; ! 202: switch (as->fmt) { ! 203: case AUD_FMT_S8: ! 204: case AUD_FMT_U8: ! 205: esdfmt |= ESD_BITS8; ! 206: obt_as.fmt = AUD_FMT_U8; ! 207: break; ! 208: ! 209: case AUD_FMT_S32: ! 210: case AUD_FMT_U32: ! 211: dolog ("Will use 16 instead of 32 bit samples\n"); ! 212: ! 213: case AUD_FMT_S16: ! 214: case AUD_FMT_U16: ! 215: deffmt: ! 216: esdfmt |= ESD_BITS16; ! 217: obt_as.fmt = AUD_FMT_S16; ! 218: break; ! 219: ! 220: default: ! 221: dolog ("Internal logic error: Bad audio format %d\n", as->fmt); ! 222: goto deffmt; ! 223: ! 224: } ! 225: obt_as.endianness = AUDIO_HOST_ENDIANNESS; ! 226: ! 227: audio_pcm_init_info (&hw->info, &obt_as); ! 228: ! 229: hw->samples = conf.samples; ! 230: esd->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); ! 231: if (!esd->pcm_buf) { ! 232: dolog ("Could not allocate buffer (%d bytes)\n", ! 233: hw->samples << hw->info.shift); ! 234: return -1; ! 235: } ! 236: ! 237: esd->fd = -1; ! 238: err = pthread_sigmask (SIG_BLOCK, &set, &old_set); ! 239: if (err) { ! 240: qesd_logerr (err, "pthread_sigmask failed\n"); ! 241: goto fail1; ! 242: } ! 243: ! 244: esd->fd = esd_play_stream (esdfmt, as->freq, conf.dac_host, NULL); ! 245: if (esd->fd < 0) { ! 246: qesd_logerr (errno, "esd_play_stream failed\n"); ! 247: goto fail2; ! 248: } ! 249: ! 250: if (audio_pt_init (&esd->pt, qesd_thread_out, esd, AUDIO_CAP, AUDIO_FUNC)) { ! 251: goto fail3; ! 252: } ! 253: ! 254: err = pthread_sigmask (SIG_SETMASK, &old_set, NULL); ! 255: if (err) { ! 256: qesd_logerr (err, "pthread_sigmask(restore) failed\n"); ! 257: } ! 258: ! 259: return 0; ! 260: ! 261: fail3: ! 262: if (close (esd->fd)) { ! 263: qesd_logerr (errno, "%s: close on esd socket(%d) failed\n", ! 264: AUDIO_FUNC, esd->fd); ! 265: } ! 266: esd->fd = -1; ! 267: ! 268: fail2: ! 269: err = pthread_sigmask (SIG_SETMASK, &old_set, NULL); ! 270: if (err) { ! 271: qesd_logerr (err, "pthread_sigmask(restore) failed\n"); ! 272: } ! 273: ! 274: fail1: ! 275: qemu_free (esd->pcm_buf); ! 276: esd->pcm_buf = NULL; ! 277: return -1; ! 278: } ! 279: ! 280: static void qesd_fini_out (HWVoiceOut *hw) ! 281: { ! 282: void *ret; ! 283: ESDVoiceOut *esd = (ESDVoiceOut *) hw; ! 284: ! 285: audio_pt_lock (&esd->pt, AUDIO_FUNC); ! 286: esd->done = 1; ! 287: audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC); ! 288: audio_pt_join (&esd->pt, &ret, AUDIO_FUNC); ! 289: ! 290: if (esd->fd >= 0) { ! 291: if (close (esd->fd)) { ! 292: qesd_logerr (errno, "failed to close esd socket\n"); ! 293: } ! 294: esd->fd = -1; ! 295: } ! 296: ! 297: audio_pt_fini (&esd->pt, AUDIO_FUNC); ! 298: ! 299: qemu_free (esd->pcm_buf); ! 300: esd->pcm_buf = NULL; ! 301: } ! 302: ! 303: static int qesd_ctl_out (HWVoiceOut *hw, int cmd, ...) ! 304: { ! 305: (void) hw; ! 306: (void) cmd; ! 307: return 0; ! 308: } ! 309: ! 310: /* capture */ ! 311: static void *qesd_thread_in (void *arg) ! 312: { ! 313: ESDVoiceIn *esd = arg; ! 314: HWVoiceIn *hw = &esd->hw; ! 315: int threshold; ! 316: ! 317: threshold = conf.divisor ? hw->samples / conf.divisor : 0; ! 318: ! 319: if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) { ! 320: return NULL; ! 321: } ! 322: ! 323: for (;;) { ! 324: int incr, to_grab, wpos; ! 325: ! 326: for (;;) { ! 327: if (esd->done) { ! 328: goto exit; ! 329: } ! 330: ! 331: if (esd->dead > threshold) { ! 332: break; ! 333: } ! 334: ! 335: if (audio_pt_wait (&esd->pt, AUDIO_FUNC)) { ! 336: goto exit; ! 337: } ! 338: } ! 339: ! 340: incr = to_grab = esd->dead; ! 341: wpos = hw->wpos; ! 342: ! 343: if (audio_pt_unlock (&esd->pt, AUDIO_FUNC)) { ! 344: return NULL; ! 345: } ! 346: ! 347: while (to_grab) { ! 348: ssize_t nread; ! 349: int chunk = audio_MIN (to_grab, hw->samples - wpos); ! 350: void *buf = advance (esd->pcm_buf, wpos); ! 351: ! 352: again: ! 353: nread = read (esd->fd, buf, chunk << hw->info.shift); ! 354: if (nread == -1) { ! 355: if (errno == EINTR || errno == EAGAIN) { ! 356: goto again; ! 357: } ! 358: qesd_logerr (errno, "read failed\n"); ! 359: return NULL; ! 360: } ! 361: ! 362: if (nread != chunk << hw->info.shift) { ! 363: int rsamples = nread >> hw->info.shift; ! 364: int rbytes = rsamples << hw->info.shift; ! 365: if (rbytes != nread) { ! 366: dolog ("warning: Misaligned write %d (requested %d), " ! 367: "alignment %d\n", ! 368: rbytes, nread, hw->info.align + 1); ! 369: } ! 370: to_grab -= rsamples; ! 371: wpos = (wpos + rsamples) % hw->samples; ! 372: break; ! 373: } ! 374: ! 375: hw->conv (hw->conv_buf + wpos, buf, nread >> hw->info.shift, ! 376: &nominal_volume); ! 377: wpos = (wpos + chunk) % hw->samples; ! 378: to_grab -= chunk; ! 379: } ! 380: ! 381: if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) { ! 382: return NULL; ! 383: } ! 384: ! 385: esd->wpos = wpos; ! 386: esd->dead -= incr; ! 387: esd->incr += incr; ! 388: } ! 389: ! 390: exit: ! 391: audio_pt_unlock (&esd->pt, AUDIO_FUNC); ! 392: return NULL; ! 393: } ! 394: ! 395: static int qesd_run_in (HWVoiceIn *hw) ! 396: { ! 397: int live, incr, dead; ! 398: ESDVoiceIn *esd = (ESDVoiceIn *) hw; ! 399: ! 400: if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) { ! 401: return 0; ! 402: } ! 403: ! 404: live = audio_pcm_hw_get_live_in (hw); ! 405: dead = hw->samples - live; ! 406: incr = audio_MIN (dead, esd->incr); ! 407: esd->incr -= incr; ! 408: esd->dead = dead - incr; ! 409: hw->wpos = esd->wpos; ! 410: if (esd->dead > 0) { ! 411: audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC); ! 412: } ! 413: else { ! 414: audio_pt_unlock (&esd->pt, AUDIO_FUNC); ! 415: } ! 416: return incr; ! 417: } ! 418: ! 419: static int qesd_read (SWVoiceIn *sw, void *buf, int len) ! 420: { ! 421: return audio_pcm_sw_read (sw, buf, len); ! 422: } ! 423: ! 424: static int qesd_init_in (HWVoiceIn *hw, struct audsettings *as) ! 425: { ! 426: ESDVoiceIn *esd = (ESDVoiceIn *) hw; ! 427: struct audsettings obt_as = *as; ! 428: int esdfmt = ESD_STREAM | ESD_RECORD; ! 429: int err; ! 430: sigset_t set, old_set; ! 431: ! 432: sigfillset (&set); ! 433: ! 434: esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO; ! 435: switch (as->fmt) { ! 436: case AUD_FMT_S8: ! 437: case AUD_FMT_U8: ! 438: esdfmt |= ESD_BITS8; ! 439: obt_as.fmt = AUD_FMT_U8; ! 440: break; ! 441: ! 442: case AUD_FMT_S16: ! 443: case AUD_FMT_U16: ! 444: esdfmt |= ESD_BITS16; ! 445: obt_as.fmt = AUD_FMT_S16; ! 446: break; ! 447: ! 448: case AUD_FMT_S32: ! 449: case AUD_FMT_U32: ! 450: dolog ("Will use 16 instead of 32 bit samples\n"); ! 451: esdfmt |= ESD_BITS16; ! 452: obt_as.fmt = AUD_FMT_S16; ! 453: break; ! 454: } ! 455: obt_as.endianness = AUDIO_HOST_ENDIANNESS; ! 456: ! 457: audio_pcm_init_info (&hw->info, &obt_as); ! 458: ! 459: hw->samples = conf.samples; ! 460: esd->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); ! 461: if (!esd->pcm_buf) { ! 462: dolog ("Could not allocate buffer (%d bytes)\n", ! 463: hw->samples << hw->info.shift); ! 464: return -1; ! 465: } ! 466: ! 467: esd->fd = -1; ! 468: ! 469: err = pthread_sigmask (SIG_BLOCK, &set, &old_set); ! 470: if (err) { ! 471: qesd_logerr (err, "pthread_sigmask failed\n"); ! 472: goto fail1; ! 473: } ! 474: ! 475: esd->fd = esd_record_stream (esdfmt, as->freq, conf.adc_host, NULL); ! 476: if (esd->fd < 0) { ! 477: qesd_logerr (errno, "esd_record_stream failed\n"); ! 478: goto fail2; ! 479: } ! 480: ! 481: if (audio_pt_init (&esd->pt, qesd_thread_in, esd, AUDIO_CAP, AUDIO_FUNC)) { ! 482: goto fail3; ! 483: } ! 484: ! 485: err = pthread_sigmask (SIG_SETMASK, &old_set, NULL); ! 486: if (err) { ! 487: qesd_logerr (err, "pthread_sigmask(restore) failed\n"); ! 488: } ! 489: ! 490: return 0; ! 491: ! 492: fail3: ! 493: if (close (esd->fd)) { ! 494: qesd_logerr (errno, "%s: close on esd socket(%d) failed\n", ! 495: AUDIO_FUNC, esd->fd); ! 496: } ! 497: esd->fd = -1; ! 498: ! 499: fail2: ! 500: err = pthread_sigmask (SIG_SETMASK, &old_set, NULL); ! 501: if (err) { ! 502: qesd_logerr (err, "pthread_sigmask(restore) failed\n"); ! 503: } ! 504: ! 505: fail1: ! 506: qemu_free (esd->pcm_buf); ! 507: esd->pcm_buf = NULL; ! 508: return -1; ! 509: } ! 510: ! 511: static void qesd_fini_in (HWVoiceIn *hw) ! 512: { ! 513: void *ret; ! 514: ESDVoiceIn *esd = (ESDVoiceIn *) hw; ! 515: ! 516: audio_pt_lock (&esd->pt, AUDIO_FUNC); ! 517: esd->done = 1; ! 518: audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC); ! 519: audio_pt_join (&esd->pt, &ret, AUDIO_FUNC); ! 520: ! 521: if (esd->fd >= 0) { ! 522: if (close (esd->fd)) { ! 523: qesd_logerr (errno, "failed to close esd socket\n"); ! 524: } ! 525: esd->fd = -1; ! 526: } ! 527: ! 528: audio_pt_fini (&esd->pt, AUDIO_FUNC); ! 529: ! 530: qemu_free (esd->pcm_buf); ! 531: esd->pcm_buf = NULL; ! 532: } ! 533: ! 534: static int qesd_ctl_in (HWVoiceIn *hw, int cmd, ...) ! 535: { ! 536: (void) hw; ! 537: (void) cmd; ! 538: return 0; ! 539: } ! 540: ! 541: /* common */ ! 542: static void *qesd_audio_init (void) ! 543: { ! 544: return &conf; ! 545: } ! 546: ! 547: static void qesd_audio_fini (void *opaque) ! 548: { ! 549: (void) opaque; ! 550: ldebug ("esd_fini"); ! 551: } ! 552: ! 553: struct audio_option qesd_options[] = { ! 554: {"SAMPLES", AUD_OPT_INT, &conf.samples, ! 555: "buffer size in samples", NULL, 0}, ! 556: ! 557: {"DIVISOR", AUD_OPT_INT, &conf.divisor, ! 558: "threshold divisor", NULL, 0}, ! 559: ! 560: {"DAC_HOST", AUD_OPT_STR, &conf.dac_host, ! 561: "playback host", NULL, 0}, ! 562: ! 563: {"ADC_HOST", AUD_OPT_STR, &conf.adc_host, ! 564: "capture host", NULL, 0}, ! 565: ! 566: {NULL, 0, NULL, NULL, NULL, 0} ! 567: }; ! 568: ! 569: static struct audio_pcm_ops qesd_pcm_ops = { ! 570: qesd_init_out, ! 571: qesd_fini_out, ! 572: qesd_run_out, ! 573: qesd_write, ! 574: qesd_ctl_out, ! 575: ! 576: qesd_init_in, ! 577: qesd_fini_in, ! 578: qesd_run_in, ! 579: qesd_read, ! 580: qesd_ctl_in, ! 581: }; ! 582: ! 583: struct audio_driver esd_audio_driver = { ! 584: INIT_FIELD (name = ) "esd", ! 585: INIT_FIELD (descr = ) ! 586: "http://en.wikipedia.org/wiki/Esound", ! 587: INIT_FIELD (options = ) qesd_options, ! 588: INIT_FIELD (init = ) qesd_audio_init, ! 589: INIT_FIELD (fini = ) qesd_audio_fini, ! 590: INIT_FIELD (pcm_ops = ) &qesd_pcm_ops, ! 591: INIT_FIELD (can_be_default = ) 0, ! 592: INIT_FIELD (max_voices_out = ) INT_MAX, ! 593: INIT_FIELD (max_voices_in = ) INT_MAX, ! 594: INIT_FIELD (voice_size_out = ) sizeof (ESDVoiceOut), ! 595: INIT_FIELD (voice_size_in = ) sizeof (ESDVoiceIn) ! 596: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.