Commit b8e59f18de9ebd0286e14ff42db217b34d4f8272

Authored by malc
1 parent 923e4521

Pulseaudio driver

git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@4827 c046a42c-6fe2-441c-8c8c-71466251a162
Makefile
... ... @@ -98,6 +98,11 @@ AUDIO_PT = yes
98 98 AUDIO_PT_INT = yes
99 99 AUDIO_OBJS += esdaudio.o
100 100 endif
  101 +ifdef CONFIG_PA
  102 +AUDIO_PT = yes
  103 +AUDIO_PT_INT = yes
  104 +AUDIO_OBJS += paaudio.o
  105 +endif
101 106 ifdef AUDIO_PT
102 107 LDFLAGS += -pthread
103 108 endif
... ...
Makefile.target
... ... @@ -270,6 +270,7 @@ libqemu.a: $(LIBOBJS)
270 270 $(AR) rcs $@ $(LIBOBJS)
271 271  
272 272 translate.o: translate.c cpu.h $(OPC_H)
  273 +translate.o: CFLAGS:=${CFLAGS} -O1 #-fno-unit-at-a-time
273 274  
274 275 translate-all.o: translate-all.c cpu.h $(OPC_H)
275 276  
... ... @@ -480,6 +481,9 @@ endif
480 481 ifdef CONFIG_ESD
481 482 LIBS += -lesd
482 483 endif
  484 +ifdef CONFIG_PA
  485 +LIBS += -lpulse-simple
  486 +endif
483 487 ifdef CONFIG_DSOUND
484 488 LIBS += -lole32 -ldxguid
485 489 endif
... ...
audio/audio_int.h
... ... @@ -203,6 +203,7 @@ extern struct audio_driver alsa_audio_driver;
203 203 extern struct audio_driver coreaudio_audio_driver;
204 204 extern struct audio_driver dsound_audio_driver;
205 205 extern struct audio_driver esd_audio_driver;
  206 +extern struct audio_driver pa_audio_driver;
206 207 extern volume_t nominal_volume;
207 208  
208 209 void audio_pcm_init_info (struct audio_pcm_info *info, audsettings_t *as);
... ...
audio/paaudio.c 0 → 100644
  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 + st_sample_t *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, audsettings_t *as)
  299 +{
  300 + int error;
  301 + static pa_sample_spec ss;
  302 + audsettings_t 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, audsettings_t *as)
  353 +{
  354 + int error;
  355 + static pa_sample_spec ss;
  356 + audsettings_t 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 +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 +};
... ...
configure
... ... @@ -196,7 +196,7 @@ SunOS)
196 196 ;;
197 197 *)
198 198 audio_drv_list="oss"
199   -audio_possible_drivers="oss alsa sdl esd"
  199 +audio_possible_drivers="oss alsa sdl esd pa"
200 200 linux="yes"
201 201 linux_user="yes"
202 202 if [ "$cpu" = "i386" -o "$cpu" = "x86_64" ] ; then
... ... @@ -767,6 +767,12 @@ for drv in $audio_drv_list; do
767 767 esd)
768 768 audio_drv_probe $drv esd.h -lesd 'return esd_play_stream(0, 0, "", 0);'
769 769 ;;
  770 +
  771 + pa)
  772 + audio_drv_probe $drv pulse/simple.h -lpulse-simple \
  773 + "pa_simple *s = NULL; pa_simple_free(s); return 0;"
  774 + ;;
  775 +
770 776 esac
771 777 done
772 778  
... ...