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

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

unix.superglobalmegacorp.com

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