Annotation of qemu/audio/paaudio.c, revision 1.1.1.4

1.1       root        1: /* public domain */
                      2: #include "qemu-common.h"
                      3: #include "audio.h"
                      4: 
                      5: #include <pulse/simple.h>
                      6: #include <pulse/error.h>
                      7: 
                      8: #define AUDIO_CAP "pulseaudio"
                      9: #include "audio_int.h"
                     10: #include "audio_pt_int.h"
                     11: 
                     12: typedef struct {
                     13:     HWVoiceOut hw;
                     14:     int done;
                     15:     int live;
                     16:     int decr;
                     17:     int rpos;
                     18:     pa_simple *s;
                     19:     void *pcm_buf;
                     20:     struct audio_pt pt;
                     21: } PAVoiceOut;
                     22: 
                     23: typedef struct {
                     24:     HWVoiceIn hw;
                     25:     int done;
                     26:     int dead;
                     27:     int incr;
                     28:     int wpos;
                     29:     pa_simple *s;
                     30:     void *pcm_buf;
                     31:     struct audio_pt pt;
                     32: } PAVoiceIn;
                     33: 
                     34: static struct {
                     35:     int samples;
                     36:     char *server;
                     37:     char *sink;
                     38:     char *source;
                     39: } conf = {
1.1.1.4 ! root       40:     .samples = 4096,
1.1       root       41: };
                     42: 
                     43: static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...)
                     44: {
                     45:     va_list ap;
                     46: 
                     47:     va_start (ap, fmt);
                     48:     AUD_vlog (AUDIO_CAP, fmt, ap);
                     49:     va_end (ap);
                     50: 
                     51:     AUD_log (AUDIO_CAP, "Reason: %s\n", pa_strerror (err));
                     52: }
                     53: 
                     54: static void *qpa_thread_out (void *arg)
                     55: {
                     56:     PAVoiceOut *pa = arg;
                     57:     HWVoiceOut *hw = &pa->hw;
                     58: 
                     59:     if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
                     60:         return NULL;
                     61:     }
                     62: 
                     63:     for (;;) {
                     64:         int decr, to_mix, rpos;
                     65: 
                     66:         for (;;) {
                     67:             if (pa->done) {
                     68:                 goto exit;
                     69:             }
                     70: 
1.1.1.4 ! root       71:             if (pa->live > 0) {
1.1       root       72:                 break;
                     73:             }
                     74: 
                     75:             if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) {
                     76:                 goto exit;
                     77:             }
                     78:         }
                     79: 
1.1.1.4 ! root       80:         decr = to_mix = audio_MIN (pa->live, conf.samples >> 2);
        !            81:         rpos = pa->rpos;
1.1       root       82: 
                     83:         if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
                     84:             return NULL;
                     85:         }
                     86: 
                     87:         while (to_mix) {
                     88:             int error;
                     89:             int chunk = audio_MIN (to_mix, hw->samples - rpos);
                     90:             struct st_sample *src = hw->mix_buf + rpos;
                     91: 
                     92:             hw->clip (pa->pcm_buf, src, chunk);
                     93: 
                     94:             if (pa_simple_write (pa->s, pa->pcm_buf,
                     95:                                  chunk << hw->info.shift, &error) < 0) {
                     96:                 qpa_logerr (error, "pa_simple_write failed\n");
                     97:                 return NULL;
                     98:             }
                     99: 
                    100:             rpos = (rpos + chunk) % hw->samples;
                    101:             to_mix -= chunk;
                    102:         }
                    103: 
                    104:         if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
                    105:             return NULL;
                    106:         }
                    107: 
                    108:         pa->rpos = rpos;
                    109:         pa->live -= decr;
                    110:         pa->decr += decr;
                    111:     }
                    112: 
                    113:  exit:
                    114:     audio_pt_unlock (&pa->pt, AUDIO_FUNC);
                    115:     return NULL;
                    116: }
                    117: 
1.1.1.3   root      118: static int qpa_run_out (HWVoiceOut *hw, int live)
1.1       root      119: {
1.1.1.3   root      120:     int decr;
1.1       root      121:     PAVoiceOut *pa = (PAVoiceOut *) hw;
                    122: 
                    123:     if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
                    124:         return 0;
                    125:     }
                    126: 
                    127:     decr = audio_MIN (live, pa->decr);
                    128:     pa->decr -= decr;
                    129:     pa->live = live - decr;
                    130:     hw->rpos = pa->rpos;
                    131:     if (pa->live > 0) {
                    132:         audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
                    133:     }
                    134:     else {
                    135:         audio_pt_unlock (&pa->pt, AUDIO_FUNC);
                    136:     }
                    137:     return decr;
                    138: }
                    139: 
                    140: static int qpa_write (SWVoiceOut *sw, void *buf, int len)
                    141: {
                    142:     return audio_pcm_sw_write (sw, buf, len);
                    143: }
                    144: 
                    145: /* capture */
                    146: static void *qpa_thread_in (void *arg)
                    147: {
                    148:     PAVoiceIn *pa = arg;
                    149:     HWVoiceIn *hw = &pa->hw;
                    150: 
                    151:     if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
                    152:         return NULL;
                    153:     }
                    154: 
                    155:     for (;;) {
                    156:         int incr, to_grab, wpos;
                    157: 
                    158:         for (;;) {
                    159:             if (pa->done) {
                    160:                 goto exit;
                    161:             }
                    162: 
1.1.1.4 ! root      163:             if (pa->dead > 0) {
1.1       root      164:                 break;
                    165:             }
                    166: 
                    167:             if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) {
                    168:                 goto exit;
                    169:             }
                    170:         }
                    171: 
1.1.1.4 ! root      172:         incr = to_grab = audio_MIN (pa->dead, conf.samples >> 2);
        !           173:         wpos = pa->wpos;
1.1       root      174: 
                    175:         if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
                    176:             return NULL;
                    177:         }
                    178: 
                    179:         while (to_grab) {
                    180:             int error;
                    181:             int chunk = audio_MIN (to_grab, hw->samples - wpos);
                    182:             void *buf = advance (pa->pcm_buf, wpos);
                    183: 
                    184:             if (pa_simple_read (pa->s, buf,
                    185:                                 chunk << hw->info.shift, &error) < 0) {
                    186:                 qpa_logerr (error, "pa_simple_read failed\n");
                    187:                 return NULL;
                    188:             }
                    189: 
1.1.1.4 ! root      190:             hw->conv (hw->conv_buf + wpos, buf, chunk);
1.1       root      191:             wpos = (wpos + chunk) % hw->samples;
                    192:             to_grab -= chunk;
                    193:         }
                    194: 
                    195:         if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
                    196:             return NULL;
                    197:         }
                    198: 
                    199:         pa->wpos = wpos;
                    200:         pa->dead -= incr;
                    201:         pa->incr += incr;
                    202:     }
                    203: 
                    204:  exit:
                    205:     audio_pt_unlock (&pa->pt, AUDIO_FUNC);
                    206:     return NULL;
                    207: }
                    208: 
                    209: static int qpa_run_in (HWVoiceIn *hw)
                    210: {
                    211:     int live, incr, dead;
                    212:     PAVoiceIn *pa = (PAVoiceIn *) hw;
                    213: 
                    214:     if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
                    215:         return 0;
                    216:     }
                    217: 
                    218:     live = audio_pcm_hw_get_live_in (hw);
                    219:     dead = hw->samples - live;
                    220:     incr = audio_MIN (dead, pa->incr);
                    221:     pa->incr -= incr;
                    222:     pa->dead = dead - incr;
                    223:     hw->wpos = pa->wpos;
                    224:     if (pa->dead > 0) {
                    225:         audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
                    226:     }
                    227:     else {
                    228:         audio_pt_unlock (&pa->pt, AUDIO_FUNC);
                    229:     }
                    230:     return incr;
                    231: }
                    232: 
                    233: static int qpa_read (SWVoiceIn *sw, void *buf, int len)
                    234: {
                    235:     return audio_pcm_sw_read (sw, buf, len);
                    236: }
                    237: 
                    238: static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness)
                    239: {
                    240:     int format;
                    241: 
                    242:     switch (afmt) {
                    243:     case AUD_FMT_S8:
                    244:     case AUD_FMT_U8:
                    245:         format = PA_SAMPLE_U8;
                    246:         break;
                    247:     case AUD_FMT_S16:
                    248:     case AUD_FMT_U16:
                    249:         format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE;
                    250:         break;
                    251:     case AUD_FMT_S32:
                    252:     case AUD_FMT_U32:
                    253:         format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE;
                    254:         break;
                    255:     default:
                    256:         dolog ("Internal logic error: Bad audio format %d\n", afmt);
                    257:         format = PA_SAMPLE_U8;
                    258:         break;
                    259:     }
                    260:     return format;
                    261: }
                    262: 
                    263: static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness)
                    264: {
                    265:     switch (fmt) {
                    266:     case PA_SAMPLE_U8:
                    267:         return AUD_FMT_U8;
                    268:     case PA_SAMPLE_S16BE:
                    269:         *endianness = 1;
                    270:         return AUD_FMT_S16;
                    271:     case PA_SAMPLE_S16LE:
                    272:         *endianness = 0;
                    273:         return AUD_FMT_S16;
                    274:     case PA_SAMPLE_S32BE:
                    275:         *endianness = 1;
                    276:         return AUD_FMT_S32;
                    277:     case PA_SAMPLE_S32LE:
                    278:         *endianness = 0;
                    279:         return AUD_FMT_S32;
                    280:     default:
                    281:         dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt);
                    282:         return AUD_FMT_U8;
                    283:     }
                    284: }
                    285: 
                    286: static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as)
                    287: {
                    288:     int error;
                    289:     static pa_sample_spec ss;
1.1.1.4 ! root      290:     static pa_buffer_attr ba;
1.1       root      291:     struct audsettings obt_as = *as;
                    292:     PAVoiceOut *pa = (PAVoiceOut *) hw;
                    293: 
                    294:     ss.format = audfmt_to_pa (as->fmt, as->endianness);
                    295:     ss.channels = as->nchannels;
                    296:     ss.rate = as->freq;
                    297: 
1.1.1.4 ! root      298:     /*
        !           299:      * qemu audio tick runs at 250 Hz (by default), so processing
        !           300:      * data chunks worth 4 ms of sound should be a good fit.
        !           301:      */
        !           302:     ba.tlength = pa_usec_to_bytes (4 * 1000, &ss);
        !           303:     ba.minreq = pa_usec_to_bytes (2 * 1000, &ss);
        !           304:     ba.maxlength = -1;
        !           305:     ba.prebuf = -1;
        !           306: 
1.1       root      307:     obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
                    308: 
                    309:     pa->s = pa_simple_new (
                    310:         conf.server,
                    311:         "qemu",
                    312:         PA_STREAM_PLAYBACK,
                    313:         conf.sink,
                    314:         "pcm.playback",
                    315:         &ss,
                    316:         NULL,                   /* channel map */
1.1.1.4 ! root      317:         &ba,                    /* buffering attributes */
1.1       root      318:         &error
                    319:         );
                    320:     if (!pa->s) {
                    321:         qpa_logerr (error, "pa_simple_new for playback failed\n");
                    322:         goto fail1;
                    323:     }
                    324: 
                    325:     audio_pcm_init_info (&hw->info, &obt_as);
                    326:     hw->samples = conf.samples;
                    327:     pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
1.1.1.4 ! root      328:     pa->rpos = hw->rpos;
1.1       root      329:     if (!pa->pcm_buf) {
                    330:         dolog ("Could not allocate buffer (%d bytes)\n",
                    331:                hw->samples << hw->info.shift);
                    332:         goto fail2;
                    333:     }
                    334: 
                    335:     if (audio_pt_init (&pa->pt, qpa_thread_out, hw, AUDIO_CAP, AUDIO_FUNC)) {
                    336:         goto fail3;
                    337:     }
                    338: 
                    339:     return 0;
                    340: 
                    341:  fail3:
1.1.1.2   root      342:     qemu_free (pa->pcm_buf);
1.1       root      343:     pa->pcm_buf = NULL;
                    344:  fail2:
                    345:     pa_simple_free (pa->s);
                    346:     pa->s = NULL;
                    347:  fail1:
                    348:     return -1;
                    349: }
                    350: 
                    351: static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as)
                    352: {
                    353:     int error;
                    354:     static pa_sample_spec ss;
                    355:     struct audsettings obt_as = *as;
                    356:     PAVoiceIn *pa = (PAVoiceIn *) hw;
                    357: 
                    358:     ss.format = audfmt_to_pa (as->fmt, as->endianness);
                    359:     ss.channels = as->nchannels;
                    360:     ss.rate = as->freq;
                    361: 
                    362:     obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
                    363: 
                    364:     pa->s = pa_simple_new (
                    365:         conf.server,
                    366:         "qemu",
                    367:         PA_STREAM_RECORD,
                    368:         conf.source,
                    369:         "pcm.capture",
                    370:         &ss,
                    371:         NULL,                   /* channel map */
                    372:         NULL,                   /* buffering attributes */
                    373:         &error
                    374:         );
                    375:     if (!pa->s) {
                    376:         qpa_logerr (error, "pa_simple_new for capture failed\n");
                    377:         goto fail1;
                    378:     }
                    379: 
                    380:     audio_pcm_init_info (&hw->info, &obt_as);
                    381:     hw->samples = conf.samples;
                    382:     pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
1.1.1.4 ! root      383:     pa->wpos = hw->wpos;
1.1       root      384:     if (!pa->pcm_buf) {
                    385:         dolog ("Could not allocate buffer (%d bytes)\n",
                    386:                hw->samples << hw->info.shift);
                    387:         goto fail2;
                    388:     }
                    389: 
                    390:     if (audio_pt_init (&pa->pt, qpa_thread_in, hw, AUDIO_CAP, AUDIO_FUNC)) {
                    391:         goto fail3;
                    392:     }
                    393: 
                    394:     return 0;
                    395: 
                    396:  fail3:
1.1.1.2   root      397:     qemu_free (pa->pcm_buf);
1.1       root      398:     pa->pcm_buf = NULL;
                    399:  fail2:
                    400:     pa_simple_free (pa->s);
                    401:     pa->s = NULL;
                    402:  fail1:
                    403:     return -1;
                    404: }
                    405: 
                    406: static void qpa_fini_out (HWVoiceOut *hw)
                    407: {
                    408:     void *ret;
                    409:     PAVoiceOut *pa = (PAVoiceOut *) hw;
                    410: 
                    411:     audio_pt_lock (&pa->pt, AUDIO_FUNC);
                    412:     pa->done = 1;
                    413:     audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
                    414:     audio_pt_join (&pa->pt, &ret, AUDIO_FUNC);
                    415: 
                    416:     if (pa->s) {
                    417:         pa_simple_free (pa->s);
                    418:         pa->s = NULL;
                    419:     }
                    420: 
                    421:     audio_pt_fini (&pa->pt, AUDIO_FUNC);
                    422:     qemu_free (pa->pcm_buf);
                    423:     pa->pcm_buf = NULL;
                    424: }
                    425: 
                    426: static void qpa_fini_in (HWVoiceIn *hw)
                    427: {
                    428:     void *ret;
                    429:     PAVoiceIn *pa = (PAVoiceIn *) hw;
                    430: 
                    431:     audio_pt_lock (&pa->pt, AUDIO_FUNC);
                    432:     pa->done = 1;
                    433:     audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
                    434:     audio_pt_join (&pa->pt, &ret, AUDIO_FUNC);
                    435: 
                    436:     if (pa->s) {
                    437:         pa_simple_free (pa->s);
                    438:         pa->s = NULL;
                    439:     }
                    440: 
                    441:     audio_pt_fini (&pa->pt, AUDIO_FUNC);
                    442:     qemu_free (pa->pcm_buf);
                    443:     pa->pcm_buf = NULL;
                    444: }
                    445: 
                    446: static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
                    447: {
                    448:     (void) hw;
                    449:     (void) cmd;
                    450:     return 0;
                    451: }
                    452: 
                    453: static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
                    454: {
                    455:     (void) hw;
                    456:     (void) cmd;
                    457:     return 0;
                    458: }
                    459: 
                    460: /* common */
                    461: static void *qpa_audio_init (void)
                    462: {
                    463:     return &conf;
                    464: }
                    465: 
                    466: static void qpa_audio_fini (void *opaque)
                    467: {
                    468:     (void) opaque;
                    469: }
                    470: 
                    471: struct audio_option qpa_options[] = {
1.1.1.3   root      472:     {
                    473:         .name  = "SAMPLES",
                    474:         .tag   = AUD_OPT_INT,
                    475:         .valp  = &conf.samples,
                    476:         .descr = "buffer size in samples"
                    477:     },
                    478:     {
                    479:         .name  = "SERVER",
                    480:         .tag   = AUD_OPT_STR,
                    481:         .valp  = &conf.server,
                    482:         .descr = "server address"
                    483:     },
                    484:     {
                    485:         .name  = "SINK",
                    486:         .tag   = AUD_OPT_STR,
                    487:         .valp  = &conf.sink,
                    488:         .descr = "sink device name"
                    489:     },
                    490:     {
                    491:         .name  = "SOURCE",
                    492:         .tag   = AUD_OPT_STR,
                    493:         .valp  = &conf.source,
                    494:         .descr = "source device name"
                    495:     },
                    496:     { /* End of list */ }
1.1       root      497: };
                    498: 
                    499: static struct audio_pcm_ops qpa_pcm_ops = {
1.1.1.3   root      500:     .init_out = qpa_init_out,
                    501:     .fini_out = qpa_fini_out,
                    502:     .run_out  = qpa_run_out,
                    503:     .write    = qpa_write,
                    504:     .ctl_out  = qpa_ctl_out,
                    505: 
                    506:     .init_in  = qpa_init_in,
                    507:     .fini_in  = qpa_fini_in,
                    508:     .run_in   = qpa_run_in,
                    509:     .read     = qpa_read,
                    510:     .ctl_in   = qpa_ctl_in
1.1       root      511: };
                    512: 
                    513: struct audio_driver pa_audio_driver = {
1.1.1.3   root      514:     .name           = "pa",
                    515:     .descr          = "http://www.pulseaudio.org/",
                    516:     .options        = qpa_options,
                    517:     .init           = qpa_audio_init,
                    518:     .fini           = qpa_audio_fini,
                    519:     .pcm_ops        = &qpa_pcm_ops,
                    520:     .can_be_default = 1,
                    521:     .max_voices_out = INT_MAX,
                    522:     .max_voices_in  = INT_MAX,
                    523:     .voice_size_out = sizeof (PAVoiceOut),
                    524:     .voice_size_in  = sizeof (PAVoiceIn)
1.1       root      525: };

unix.superglobalmegacorp.com

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