Annotation of qemu/audio/paaudio.c, revision 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.