Annotation of qemu/audio/esdaudio.c, revision 1.1.1.1

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: };

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.