static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...) { PAVoiceIn *pa = (PAVoiceIn *) hw; pa_operation *op; pa_cvolume v; paaudio *g = pa->g; #ifdef PA_CHECK_VERSION pa_cvolume_init (&v); #endif switch (cmd) { case VOICE_VOLUME: { SWVoiceIn *sw; va_list ap; va_start (ap, cmd); sw = va_arg (ap, SWVoiceIn *); va_end (ap); v.channels = 2; v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.l) / UINT32_MAX; v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.r) / UINT32_MAX; pa_threaded_mainloop_lock (g->mainloop); op = pa_context_set_source_output_volume (g->context, pa_stream_get_index (pa->stream), &v, NULL, NULL); if (!op) { qpa_logerr (pa_context_errno (g->context), "set_source_output_volume() failed\n"); } else { pa_operation_unref(op); } op = pa_context_set_source_output_mute (g->context, pa_stream_get_index (pa->stream), sw->vol.mute, NULL, NULL); if (!op) { qpa_logerr (pa_context_errno (g->context), "set_source_output_mute() failed\n"); } else { pa_operation_unref (op); } pa_threaded_mainloop_unlock (g->mainloop); } } return 0; }
static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) { int error; pa_sample_spec ss; struct audsettings obt_as = *as; PAVoiceIn *pa = (PAVoiceIn *) hw; paaudio *g = pa->g = drv_opaque; AudiodevPaOptions *popts = &g->dev->u.pa; AudiodevPaPerDirectionOptions *ppdo = popts->in; ss.format = audfmt_to_pa (as->fmt, as->endianness); ss.channels = as->nchannels; ss.rate = as->freq; obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness); pa->stream = qpa_simple_new ( g, "qemu", PA_STREAM_RECORD, ppdo->has_name ? ppdo->name : NULL, &ss, NULL, /* channel map */ NULL, /* buffering attributes */ &error ); if (!pa->stream) { qpa_logerr (error, "pa_simple_new for capture failed\n"); goto fail1; } audio_pcm_init_info (&hw->info, &obt_as); hw->samples = pa->samples = audio_buffer_samples( qapi_AudiodevPaPerDirectionOptions_base(ppdo), &obt_as, 46440); pa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift); pa->wpos = hw->wpos; if (!pa->pcm_buf) { dolog ("Could not allocate buffer (%d bytes)\n", hw->samples << hw->info.shift); goto fail2; } if (audio_pt_init(&pa->pt, qpa_thread_in, hw, AUDIO_CAP, __func__)) { goto fail3; } return 0; fail3: g_free (pa->pcm_buf); pa->pcm_buf = NULL; fail2: if (pa->stream) { pa_stream_unref (pa->stream); pa->stream = NULL; } fail1: return -1; }
static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as) { int error; static pa_sample_spec ss; struct audsettings obt_as = *as; PAVoiceIn *pa = (PAVoiceIn *) hw; ss.format = audfmt_to_pa (as->fmt, as->endianness); ss.channels = as->nchannels; ss.rate = as->freq; obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness); pa->s = FF(pa_simple_new) ( conf.server, "qemu", PA_STREAM_RECORD, conf.source, "pcm.capture", &ss, NULL, /* channel map */ NULL, /* buffering attributes */ &error ); if (!pa->s) { qpa_logerr (error, "pa_simple_new for capture failed\n"); goto fail1; } audio_pcm_init_info (&hw->info, &obt_as); hw->samples = conf.samples; pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); if (!pa->pcm_buf) { dolog ("Could not allocate buffer (%d bytes)\n", hw->samples << hw->info.shift); goto fail2; } if (audio_pt_init (&pa->pt, qpa_thread_in, hw, AUDIO_CAP, AUDIO_FUNC)) { goto fail3; } return 0; fail3: qemu_free (pa->pcm_buf); pa->pcm_buf = NULL; fail2: FF(pa_simple_free) (pa->s); pa->s = NULL; fail1: return -1; }
/* common */ static void *qpa_audio_init(Audiodev *dev) { paaudio *g; AudiodevPaOptions *popts = &dev->u.pa; const char *server; if (!popts->has_server) { char pidfile[64]; char *runtime; struct stat st; runtime = getenv("XDG_RUNTIME_DIR"); if (!runtime) { return NULL; } snprintf(pidfile, sizeof(pidfile), "%s/pulse/pid", runtime); if (stat(pidfile, &st) != 0) { return NULL; } } assert(dev->driver == AUDIODEV_DRIVER_PA); g = g_malloc(sizeof(paaudio)); server = popts->has_server ? popts->server : NULL; g->dev = dev; g->mainloop = NULL; g->context = NULL; g->mainloop = pa_threaded_mainloop_new (); if (!g->mainloop) { goto fail; } g->context = pa_context_new (pa_threaded_mainloop_get_api (g->mainloop), server); if (!g->context) { goto fail; } pa_context_set_state_callback (g->context, context_state_cb, g); if (pa_context_connect(g->context, server, 0, NULL) < 0) { qpa_logerr (pa_context_errno (g->context), "pa_context_connect() failed\n"); goto fail; } pa_threaded_mainloop_lock (g->mainloop); if (pa_threaded_mainloop_start (g->mainloop) < 0) { goto unlock_and_fail; } for (;;) { pa_context_state_t state; state = pa_context_get_state (g->context); if (state == PA_CONTEXT_READY) { break; } if (!PA_CONTEXT_IS_GOOD (state)) { qpa_logerr (pa_context_errno (g->context), "Wrong context state\n"); goto unlock_and_fail; } /* Wait until the context is ready */ pa_threaded_mainloop_wait (g->mainloop); } pa_threaded_mainloop_unlock (g->mainloop); return g; unlock_and_fail: pa_threaded_mainloop_unlock (g->mainloop); fail: AUD_log (AUDIO_CAP, "Failed to initialize PA context"); qpa_audio_fini(g); return NULL; }
static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque) { int error; pa_sample_spec ss; pa_buffer_attr ba; struct audsettings obt_as = *as; PAVoiceOut *pa = (PAVoiceOut *) hw; paaudio *g = pa->g = drv_opaque; AudiodevPaOptions *popts = &g->dev->u.pa; AudiodevPaPerDirectionOptions *ppdo = popts->out; ss.format = audfmt_to_pa (as->fmt, as->endianness); ss.channels = as->nchannels; ss.rate = as->freq; /* * qemu audio tick runs at 100 Hz (by default), so processing * data chunks worth 10 ms of sound should be a good fit. */ ba.tlength = pa_usec_to_bytes (10 * 1000, &ss); ba.minreq = pa_usec_to_bytes (5 * 1000, &ss); ba.maxlength = -1; ba.prebuf = -1; obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness); pa->stream = qpa_simple_new ( g, "qemu", PA_STREAM_PLAYBACK, ppdo->has_name ? ppdo->name : NULL, &ss, NULL, /* channel map */ &ba, /* buffering attributes */ &error ); if (!pa->stream) { qpa_logerr (error, "pa_simple_new for playback failed\n"); goto fail1; } audio_pcm_init_info (&hw->info, &obt_as); hw->samples = pa->samples = audio_buffer_samples( qapi_AudiodevPaPerDirectionOptions_base(ppdo), &obt_as, 46440); pa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift); pa->rpos = hw->rpos; if (!pa->pcm_buf) { dolog ("Could not allocate buffer (%d bytes)\n", hw->samples << hw->info.shift); goto fail2; } if (audio_pt_init(&pa->pt, qpa_thread_out, hw, AUDIO_CAP, __func__)) { goto fail3; } return 0; fail3: g_free (pa->pcm_buf); pa->pcm_buf = NULL; fail2: if (pa->stream) { pa_stream_unref (pa->stream); pa->stream = NULL; } fail1: return -1; }
/* capture */ static void *qpa_thread_in (void *arg) { PAVoiceIn *pa = arg; HWVoiceIn *hw = &pa->hw; if (audio_pt_lock(&pa->pt, __func__)) { return NULL; } for (;;) { int incr, to_grab, wpos; for (;;) { if (pa->done) { goto exit; } if (pa->dead > 0) { break; } if (audio_pt_wait(&pa->pt, __func__)) { goto exit; } } incr = to_grab = audio_MIN(pa->dead, pa->samples >> 5); wpos = pa->wpos; if (audio_pt_unlock(&pa->pt, __func__)) { return NULL; } while (to_grab) { int error; int chunk = audio_MIN (to_grab, hw->samples - wpos); void *buf = advance (pa->pcm_buf, wpos); if (qpa_simple_read (pa, buf, chunk << hw->info.shift, &error) < 0) { qpa_logerr (error, "pa_simple_read failed\n"); return NULL; } hw->conv (hw->conv_buf + wpos, buf, chunk); wpos = (wpos + chunk) % hw->samples; to_grab -= chunk; } if (audio_pt_lock(&pa->pt, __func__)) { return NULL; } pa->wpos = wpos; pa->dead -= incr; pa->incr += incr; } exit: audio_pt_unlock(&pa->pt, __func__); return NULL; }
static void *qpa_thread_out (void *arg) { PAVoiceOut *pa = arg; HWVoiceOut *hw = &pa->hw; if (audio_pt_lock(&pa->pt, __func__)) { return NULL; } for (;;) { int decr, to_mix, rpos; for (;;) { if (pa->done) { goto exit; } if (pa->live > 0) { break; } if (audio_pt_wait(&pa->pt, __func__)) { goto exit; } } decr = to_mix = audio_MIN(pa->live, pa->samples >> 5); rpos = pa->rpos; if (audio_pt_unlock(&pa->pt, __func__)) { return NULL; } while (to_mix) { int error; int chunk = audio_MIN (to_mix, hw->samples - rpos); struct st_sample *src = hw->mix_buf + rpos; hw->clip (pa->pcm_buf, src, chunk); if (qpa_simple_write (pa, pa->pcm_buf, chunk << hw->info.shift, &error) < 0) { qpa_logerr (error, "pa_simple_write failed\n"); return NULL; } rpos = (rpos + chunk) % hw->samples; to_mix -= chunk; } if (audio_pt_lock(&pa->pt, __func__)) { return NULL; } pa->rpos = rpos; pa->live -= decr; pa->decr += decr; } exit: audio_pt_unlock(&pa->pt, __func__); return NULL; }
/* capture */ static void *qpa_thread_in (void *arg) { PAVoiceIn *pa = arg; HWVoiceIn *hw = &pa->hw; int threshold; threshold = conf.divisor ? hw->samples / conf.divisor : 0; if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { return NULL; } for (;;) { int incr, to_grab, wpos; for (;;) { if (pa->done) { goto exit; } if (pa->dead > threshold) { break; } if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) { goto exit; } } incr = to_grab = pa->dead; wpos = hw->wpos; if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) { return NULL; } while (to_grab) { int error; int chunk = audio_MIN (to_grab, hw->samples - wpos); void *buf = advance (pa->pcm_buf, wpos); if (FF(pa_simple_read) (pa->s, buf, chunk << hw->info.shift, &error) < 0) { qpa_logerr (error, "pa_simple_read failed\n"); return NULL; } hw->conv (hw->conv_buf + wpos, buf, chunk, &nominal_volume); wpos = (wpos + chunk) % hw->samples; to_grab -= chunk; } if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { return NULL; } pa->wpos = wpos; pa->dead -= incr; pa->incr += incr; } exit: audio_pt_unlock (&pa->pt, AUDIO_FUNC); return NULL; }