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

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

unix.superglobalmegacorp.com

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