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

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

unix.superglobalmegacorp.com

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