summarylogtreecommitdiffstats
diff options
context:
space:
mode:
authorVaporeon2019-01-01 15:22:27 +1300
committerVaporeon2019-01-01 15:22:27 +1300
commitd6ee75f49216ae57eea6c66b29f9c8d6a7a517b7 (patch)
treefe1542ad1e6cd092f8b03fe311313e774dbc9a4b
parent75c14ca98170ecef6278b48d19b71b46b5f8b0a7 (diff)
downloadaur-d6ee75f49216ae57eea6c66b29f9c8d6a7a517b7.tar.gz
update
-rw-r--r--.SRCINFO6
-rw-r--r--PKGBUILD6
-rw-r--r--pa-fixes.patch865
3 files changed, 873 insertions, 4 deletions
diff --git a/.SRCINFO b/.SRCINFO
index 5ac52d9eec22..e0eeddae340c 100644
--- a/.SRCINFO
+++ b/.SRCINFO
@@ -1,9 +1,9 @@
# Generated by mksrcinfo v8
-# Sun Dec 16 03:04:12 UTC 2018
+# Tue Jan 1 02:22:23 UTC 2019
pkgbase = qemu-patched
pkgdesc = A generic and open source machine emulator and virtualizer - Patched for extra functionality
pkgver = 3.1.0
- pkgrel = 1
+ pkgrel = 2
url = http://wiki.qemu.org/
arch = x86_64
license = GPL2
@@ -38,6 +38,7 @@ pkgbase = qemu-patched
source = qemu-ga.service
source = 65-kvm.rules
source = allow_elf64.patch
+ source = pa-fixes.patch
source = cpu-pinning.patch::https://github.com/saveriomiroddi/qemu-pinning/commit/cf5294579e4b43e9bea7d681154dc1737e56e323.patch
source = pcie-enhanced-link-speed-and-width.patch::https://patchwork.kernel.org/series/43129/mbox/
sha256sums = 6a0508df079a0a33c2487ca936a56c12122f105b8a96a44374704bef6c69abfc
@@ -45,6 +46,7 @@ pkgbase = qemu-patched
sha256sums = c39bcde4a09165e64419fd2033b3532378bba84d509d39e2d51694d44c1f8d88
sha256sums = a66f0e791b16b03b91049aac61a25950d93e962e1b2ba64a38c6ad7f609b532c
sha256sums = 59751f1ed26ea61b2a37ebee4be6979e584a450b611282138a0893aa9173e2e4
+ sha256sums = 848b1766b3ea6e75f0e1c69a1e964131f3884bf31e940f3bf7cf7e0737bcd0da
sha256sums = a6e9c046555aca07a234ab2ec75223bfb3fb156eab37331a418b7de66d25331e
sha256sums = 49f697aa8858692b6a0bc7b43fe569f83b7bcc1b5976634e08c202eccbc35e67
diff --git a/PKGBUILD b/PKGBUILD
index 8735c60ec5eb..b1725121f5e1 100644
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -8,7 +8,7 @@ pkgname=(qemu-patched qemu-patched-headless qemu-patched-arch-extra qemu-patched
_pkgname=qemu
pkgdesc="A generic and open source machine emulator and virtualizer - Patched for extra functionality"
pkgver=3.1.0
-pkgrel=1
+pkgrel=2
arch=(x86_64)
license=(GPL2 LGPL2.1)
url="http://wiki.qemu.org/"
@@ -20,6 +20,7 @@ source=("$url/download/${_pkgname}-${pkgver}.tar.xz"{,.sig}
qemu-ga.service
65-kvm.rules
allow_elf64.patch
+ pa-fixes.patch
cpu-pinning.patch::https://github.com/saveriomiroddi/qemu-pinning/commit/cf5294579e4b43e9bea7d681154dc1737e56e323.patch
pcie-enhanced-link-speed-and-width.patch::https://patchwork.kernel.org/series/43129/mbox/)
sha256sums=('6a0508df079a0a33c2487ca936a56c12122f105b8a96a44374704bef6c69abfc'
@@ -27,6 +28,7 @@ sha256sums=('6a0508df079a0a33c2487ca936a56c12122f105b8a96a44374704bef6c69abfc'
'c39bcde4a09165e64419fd2033b3532378bba84d509d39e2d51694d44c1f8d88'
'a66f0e791b16b03b91049aac61a25950d93e962e1b2ba64a38c6ad7f609b532c'
'59751f1ed26ea61b2a37ebee4be6979e584a450b611282138a0893aa9173e2e4'
+ '848b1766b3ea6e75f0e1c69a1e964131f3884bf31e940f3bf7cf7e0737bcd0da'
'a6e9c046555aca07a234ab2ec75223bfb3fb156eab37331a418b7de66d25331e'
'49f697aa8858692b6a0bc7b43fe569f83b7bcc1b5976634e08c202eccbc35e67')
validpgpkeys=('CEACC9E15534EBABB82D3FA03353C9CEF108B584')
@@ -46,7 +48,7 @@ prepare() {
patch -p1 < ../allow_elf64.patch
# FS#60141
patch -p1 < ../cpu-pinning.patch
- #patch -p1 < ../pa-fixes.patch
+ patch -p1 < ../pa-fixes.patch
patch -p1 < ../pcie-enhanced-link-speed-and-width.patch
}
diff --git a/pa-fixes.patch b/pa-fixes.patch
new file mode 100644
index 000000000000..815d3fe21839
--- /dev/null
+++ b/pa-fixes.patch
@@ -0,0 +1,865 @@
+From fa5f68de47d120b4f6a5f973f21567ef389b42ac Mon Sep 17 00:00:00 2001
+From: Geoffrey McRae <geoff@hostfission.com>
+Date: Tue, 8 May 2018 02:59:17 +1000
+Subject: [PATCH] PA fixes
+
+---
+ audio/audio.c | 5 +
+ audio/audio_int.h | 2 +
+ audio/paaudio.c | 635 +++++++++++++++++--------------------------
+ hw/audio/hda-codec.c | 2 +
+ 4 files changed, 264 insertions(+), 380 deletions(-)
+
+diff --git a/audio/audio.c b/audio/audio.c
+index 1ace47f510..253e470d12 100644
+--- a/audio/audio.c
++++ b/audio/audio.c
+@@ -2118,3 +2118,8 @@ void AUD_set_volume_in (SWVoiceIn *sw, int mute, uint8_t lvol, uint8_t rvol)
+ }
+ }
+ }
++
++int64_t audio_get_timer_ticks(void)
++{
++ return conf.period.ticks;
++}
+diff --git a/audio/audio_int.h b/audio/audio_int.h
+index 244b454012..19ba2d7aa4 100644
+--- a/audio/audio_int.h
++++ b/audio/audio_int.h
+@@ -210,6 +210,8 @@ extern const struct mixeng_volume nominal_volume;
+ void audio_driver_register(audio_driver *drv);
+ audio_driver *audio_driver_lookup(const char *name);
+
++int64_t audio_get_timer_ticks(void);
++
+ void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as);
+ void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len);
+
+diff --git a/audio/paaudio.c b/audio/paaudio.c
+index 949769774d..bd5a1781bf 100644
+--- a/audio/paaudio.c
++++ b/audio/paaudio.c
+@@ -1,16 +1,22 @@
+ /* public domain */
+ #include "qemu/osdep.h"
+-#include "qemu-common.h"
++#include "qemu/timer.h"
+ #include "audio.h"
+
+ #include <pulse/pulseaudio.h>
+
+ #define AUDIO_CAP "pulseaudio"
++#define DEBUG
+ #include "audio_int.h"
+-#include "audio_pt_int.h"
+
+ typedef struct {
+- int samples;
++ int buffer_size_out;
++ int buffer_size_in;
++ int tlength;
++ int fragsize;
++ int maxlength_in;
++ int adjust_latency_out;
++ int adjust_latency_in;
+ char *server;
+ char *sink;
+ char *source;
+@@ -24,28 +30,18 @@ typedef struct {
+
+ typedef struct {
+ HWVoiceOut hw;
+- int done;
+- int live;
+- int decr;
+- int rpos;
+ pa_stream *stream;
+- void *pcm_buf;
+- struct audio_pt pt;
+ paaudio *g;
++ pa_sample_spec ss;
++ pa_buffer_attr ba;
+ } PAVoiceOut;
+
+ typedef struct {
+ HWVoiceIn hw;
+- int done;
+- int dead;
+- int incr;
+- int wpos;
+ pa_stream *stream;
+- void *pcm_buf;
+- struct audio_pt pt;
+- const void *read_data;
+- size_t read_index, read_length;
+ paaudio *g;
++ pa_sample_spec ss;
++ pa_buffer_attr ba;
+ } PAVoiceIn;
+
+ static void qpa_audio_fini(void *opaque);
+@@ -109,182 +105,59 @@ static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x)
+ } \
+ } while (0)
+
+-static int qpa_simple_read (PAVoiceIn *p, void *data, size_t length, int *rerror)
+-{
+- paaudio *g = p->g;
+-
+- pa_threaded_mainloop_lock (g->mainloop);
+-
+- CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail);
+-
+- while (length > 0) {
+- size_t l;
+-
+- while (!p->read_data) {
+- int r;
+-
+- r = pa_stream_peek (p->stream, &p->read_data, &p->read_length);
+- CHECK_SUCCESS_GOTO (g, rerror, r == 0, unlock_and_fail);
+-
+- if (!p->read_data) {
+- pa_threaded_mainloop_wait (g->mainloop);
+- CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail);
+- } else {
+- p->read_index = 0;
+- }
+- }
+-
+- l = p->read_length < length ? p->read_length : length;
+- memcpy (data, (const uint8_t *) p->read_data+p->read_index, l);
+-
+- data = (uint8_t *) data + l;
+- length -= l;
+-
+- p->read_index += l;
+- p->read_length -= l;
+-
+- if (!p->read_length) {
+- int r;
+-
+- r = pa_stream_drop (p->stream);
+- p->read_data = NULL;
+- p->read_length = 0;
+- p->read_index = 0;
+-
+- CHECK_SUCCESS_GOTO (g, rerror, r == 0, unlock_and_fail);
+- }
+- }
+-
+- pa_threaded_mainloop_unlock (g->mainloop);
+- return 0;
+-
+-unlock_and_fail:
+- pa_threaded_mainloop_unlock (g->mainloop);
+- return -1;
+-}
+-
+-static int qpa_simple_write (PAVoiceOut *p, const void *data, size_t length, int *rerror)
++static int qpa_run_out(HWVoiceOut *hw, int live)
+ {
+- paaudio *g = p->g;
+-
+- pa_threaded_mainloop_lock (g->mainloop);
+-
+- CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail);
+-
+- while (length > 0) {
+- size_t l;
+- int r;
+-
+- while (!(l = pa_stream_writable_size (p->stream))) {
+- pa_threaded_mainloop_wait (g->mainloop);
+- CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail);
+- }
+-
+- CHECK_SUCCESS_GOTO (g, rerror, l != (size_t) -1, unlock_and_fail);
+-
+- if (l > length) {
+- l = length;
+- }
+-
+- r = pa_stream_write (p->stream, data, l, NULL, 0LL, PA_SEEK_RELATIVE);
+- CHECK_SUCCESS_GOTO (g, rerror, r >= 0, unlock_and_fail);
+-
+- data = (const uint8_t *) data + l;
+- length -= l;
+- }
+-
+- pa_threaded_mainloop_unlock (g->mainloop);
+- return 0;
+-
+-unlock_and_fail:
+- pa_threaded_mainloop_unlock (g->mainloop);
+- return -1;
+-}
+-
+-static void *qpa_thread_out (void *arg)
+-{
+- PAVoiceOut *pa = arg;
+- HWVoiceOut *hw = &pa->hw;
+-
+- if (audio_pt_lock(&pa->pt, __func__)) {
+- return NULL;
+- }
++ PAVoiceOut *pa = (PAVoiceOut *) hw;
++ int rpos, decr, samples;
++ size_t avail_bytes, max_bytes;
++ struct st_sample *src;
++ void *pa_dst;
++ int error = 0;
++ int *rerror = &error;
++ int r;
+
+- for (;;) {
+- int decr, to_mix, rpos;
++ decr = 0;
++ rpos = hw->rpos;
+
+- for (;;) {
+- if (pa->done) {
+- goto exit;
+- }
++ pa_threaded_mainloop_lock(pa->g->mainloop);
++ CHECK_DEAD_GOTO(pa->g, pa->stream, rerror, fail);
+
+- if (pa->live > 0) {
+- break;
+- }
++ avail_bytes = (size_t) live << hw->info.shift;
+
+- if (audio_pt_wait(&pa->pt, __func__)) {
+- goto exit;
+- }
+- }
++ max_bytes = pa_stream_writable_size(pa->stream);
++ CHECK_SUCCESS_GOTO(pa->g, rerror, max_bytes != -1, fail);
+
+- decr = to_mix = audio_MIN(pa->live, pa->g->conf.samples >> 5);
+- rpos = pa->rpos;
++ samples = (int)(audio_MIN(avail_bytes, max_bytes)) >> hw->info.shift;
++ while (samples) {
++ int convert_samples = audio_MIN(samples, hw->samples - rpos);
++ size_t b_wanted = (size_t) convert_samples << hw->info.shift;
++ size_t b_effective = b_wanted;
+
+- if (audio_pt_unlock(&pa->pt, __func__)) {
+- return NULL;
+- }
++ r = pa_stream_begin_write(pa->stream, &pa_dst, &b_effective);
++ CHECK_SUCCESS_GOTO(pa->g, rerror, r == 0, fail);
++ CHECK_SUCCESS_GOTO(pa->g, (int *)0, b_effective == b_wanted, fail);
+
+- while (to_mix) {
+- int error;
+- int chunk = audio_MIN (to_mix, hw->samples - rpos);
+- struct st_sample *src = hw->mix_buf + rpos;
++ src = hw->mix_buf + rpos;
++ hw->clip(pa_dst, src, convert_samples);
+
+- 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;
+- }
++ r = pa_stream_write(pa->stream, pa_dst, b_effective,
++ NULL, 0LL, PA_SEEK_RELATIVE);
++ CHECK_SUCCESS_GOTO(pa->g, rerror, r >= 0, fail);
+
+- 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;
++ rpos = (rpos + convert_samples) % hw->samples;
++ samples -= convert_samples;
++ decr += convert_samples;
+ }
+
+- exit:
+- audio_pt_unlock(&pa->pt, __func__);
+- return NULL;
+-}
+-
+-static int qpa_run_out (HWVoiceOut *hw, int live)
+-{
+- int decr;
+- PAVoiceOut *pa = (PAVoiceOut *) hw;
+-
+- if (audio_pt_lock(&pa->pt, __func__)) {
+- return 0;
+- }
++ bail:
++ pa_threaded_mainloop_unlock(pa->g->mainloop);
+
+- decr = audio_MIN (live, pa->decr);
+- pa->decr -= decr;
+- pa->live = live - decr;
+- hw->rpos = pa->rpos;
+- if (pa->live > 0) {
+- audio_pt_unlock_and_signal(&pa->pt, __func__);
+- }
+- else {
+- audio_pt_unlock(&pa->pt, __func__);
+- }
++ hw->rpos = rpos;
+ return decr;
++
++fail:
++ qpa_logerr(error, "qpa_run_out failed\n");
++ goto bail;
+ }
+
+ static int qpa_write (SWVoiceOut *sw, void *buf, int len)
+@@ -292,92 +165,68 @@ static int qpa_write (SWVoiceOut *sw, void *buf, int len)
+ return audio_pcm_sw_write (sw, buf, len);
+ }
+
+-/* capture */
+-static void *qpa_thread_in (void *arg)
++static int qpa_run_in(HWVoiceIn *hw)
+ {
+- PAVoiceIn *pa = arg;
+- HWVoiceIn *hw = &pa->hw;
++ PAVoiceIn *pa = (PAVoiceIn *) hw;
++ int wpos, incr;
++ char *pa_src;
++ int error = 0;
++ int *rerror = &error;
++ int r;
++ size_t pa_avail;
++ incr = 0;
++ wpos = hw->wpos;
+
+- if (audio_pt_lock(&pa->pt, __func__)) {
+- return NULL;
+- }
++ pa_threaded_mainloop_lock(pa->g->mainloop);
++ CHECK_DEAD_GOTO(pa->g, pa->stream, rerror, fail);
+
+- for (;;) {
+- int incr, to_grab, wpos;
++ size_t bytes_wanted = ((unsigned int)
++ (hw->samples - audio_pcm_hw_get_live_in(hw)) << hw->info.shift);
+
+- for (;;) {
+- if (pa->done) {
+- goto exit;
+- }
++ if (bytes_wanted == 0) {
++ /* no room */
++ goto bail;
++ }
+
+- if (pa->dead > 0) {
+- break;
+- }
++ size_t bytes_avail = pa_stream_readable_size(pa->stream);
+
+- if (audio_pt_wait(&pa->pt, __func__)) {
+- goto exit;
+- }
+- }
++ if (bytes_wanted > bytes_avail) {
++ bytes_wanted = bytes_avail;
++ }
+
+- incr = to_grab = audio_MIN(pa->dead, pa->g->conf.samples >> 5);
+- wpos = pa->wpos;
++ while (bytes_wanted) {
++ r = pa_stream_peek(pa->stream, (const void **)&pa_src, &pa_avail);
++ CHECK_SUCCESS_GOTO(pa->g, rerror, r == 0, fail);
+
+- if (audio_pt_unlock(&pa->pt, __func__)) {
+- return NULL;
++ if (pa_avail == 0 || pa_avail > bytes_wanted) {
++ break;
+ }
+
+- while (to_grab) {
+- int error;
+- int chunk = audio_MIN (to_grab, hw->samples - wpos);
+- void *buf = advance (pa->pcm_buf, wpos);
++ bytes_wanted -= pa_avail;
+
+- 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);
++ while (pa_avail) {
++ int chunk = audio_MIN(
++ (int)(pa_avail >> hw->info.shift), hw->samples - wpos);
++ hw->conv(hw->conv_buf + wpos, pa_src, chunk);
+ wpos = (wpos + chunk) % hw->samples;
+- to_grab -= chunk;
+- }
+-
+- if (audio_pt_lock(&pa->pt, __func__)) {
+- return NULL;
++ pa_src += chunk << hw->info.shift;
++ pa_avail -= chunk << hw->info.shift;
++ incr += chunk;
+ }
+
+- pa->wpos = wpos;
+- pa->dead -= incr;
+- pa->incr += incr;
++ r = pa_stream_drop(pa->stream);
++ CHECK_SUCCESS_GOTO(pa->g, rerror, r == 0, fail);
+ }
+
+- exit:
+- audio_pt_unlock(&pa->pt, __func__);
+- return NULL;
+-}
+-
+-static int qpa_run_in (HWVoiceIn *hw)
+-{
+- int live, incr, dead;
+- PAVoiceIn *pa = (PAVoiceIn *) hw;
+-
+- if (audio_pt_lock(&pa->pt, __func__)) {
+- return 0;
+- }
++bail:
++ pa_threaded_mainloop_unlock(pa->g->mainloop);
+
+- live = audio_pcm_hw_get_live_in (hw);
+- dead = hw->samples - live;
+- incr = audio_MIN (dead, pa->incr);
+- pa->incr -= incr;
+- pa->dead = dead - incr;
+- hw->wpos = pa->wpos;
+- if (pa->dead > 0) {
+- audio_pt_unlock_and_signal(&pa->pt, __func__);
+- }
+- else {
+- audio_pt_unlock(&pa->pt, __func__);
+- }
++ hw->wpos = wpos;
+ return incr;
++
++fail:
++ qpa_logerr(error, "qpa_run_in failed\n");
++ goto bail;
+ }
+
+ static int qpa_read (SWVoiceIn *sw, void *buf, int len)
+@@ -470,13 +319,6 @@ static void stream_state_cb (pa_stream *s, void * userdata)
+ }
+ }
+
+-static void stream_request_cb (pa_stream *s, size_t length, void *userdata)
+-{
+- paaudio *g = userdata;
+-
+- pa_threaded_mainloop_signal (g->mainloop, 0);
+-}
+-
+ static pa_stream *qpa_simple_new (
+ paaudio *g,
+ const char *name,
+@@ -498,23 +340,17 @@ static pa_stream *qpa_simple_new (
+ }
+
+ pa_stream_set_state_callback (stream, stream_state_cb, g);
+- pa_stream_set_read_callback (stream, stream_request_cb, g);
+- pa_stream_set_write_callback (stream, stream_request_cb, g);
+
+ if (dir == PA_STREAM_PLAYBACK) {
+- r = pa_stream_connect_playback (stream, dev, attr,
+- PA_STREAM_INTERPOLATE_TIMING
+-#ifdef PA_STREAM_ADJUST_LATENCY
+- |PA_STREAM_ADJUST_LATENCY
+-#endif
+- |PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL);
++ r = pa_stream_connect_playback(stream, dev, attr,
++ PA_STREAM_INTERPOLATE_TIMING
++ | (g->conf.adjust_latency_out ? PA_STREAM_ADJUST_LATENCY : 0)
++ | PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL);
+ } else {
+- r = pa_stream_connect_record (stream, dev, attr,
+- PA_STREAM_INTERPOLATE_TIMING
+-#ifdef PA_STREAM_ADJUST_LATENCY
+- |PA_STREAM_ADJUST_LATENCY
+-#endif
+- |PA_STREAM_AUTO_TIMING_UPDATE);
++ r = pa_stream_connect_record(stream, dev, attr,
++ PA_STREAM_INTERPOLATE_TIMING
++ | (g->conf.adjust_latency_in ? PA_STREAM_ADJUST_LATENCY : 0)
++ | PA_STREAM_AUTO_TIMING_UPDATE);
+ }
+
+ if (r < 0) {
+@@ -541,165 +377,167 @@ 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;
+
+- 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,
+- g->conf.sink,
+- &ss,
+- NULL, /* channel map */
+- &ba, /* buffering attributes */
+- &error
+- );
++ int64_t timer_tick_duration =
++ audio_MAX(audio_get_timer_ticks(), 1 * SCALE_MS);
++ int64_t frames_per_tick_x1000 =
++ ((timer_tick_duration * as->freq * 1000LL) / NANOSECONDS_PER_SECOND);
++
++ int64_t tlength = g->conf.tlength;
++ if (tlength == 0) {
++ tlength = (frames_per_tick_x1000) / 400;
++ }
++ int64_t buflen = g->conf.buffer_size_out;
++ if (buflen == 0) {
++ buflen = frames_per_tick_x1000 / 400;
++ }
++
++ ldebug("tick duration: %.2f ms (%.3f frames)\n",
++ ((float)timer_tick_duration) / SCALE_MS,
++ (float)frames_per_tick_x1000 / 1000.0f);
++
++ ldebug("OUT internal buffer: %.2f ms (%"PRId64" frames)\n",
++ buflen * (1000.0f / as->freq),
++ buflen);
++
++ ldebug("OUT tlength: %.2f ms (%"PRId64" frames)\n",
++ tlength * (1000.0f / as->freq),
++ tlength);
++
++ ldebug("OUT adjust latency: %s\n",
++ g->conf.adjust_latency_out ? "yes" : "no");
++
++ pa->ss.format = audfmt_to_pa(as->fmt, as->endianness);
++ pa->ss.channels = as->nchannels;
++ pa->ss.rate = as->freq;
++
++ pa->ba.tlength = tlength * pa_frame_size(&pa->ss);
++ pa->ba.maxlength = -1;
++ pa->ba.minreq = -1;
++ pa->ba.prebuf = -1;
++
++ obt_as.fmt = pa_to_audfmt(pa->ss.format, &obt_as.endianness);
++
++ pa->stream = qpa_simple_new(
++ g,
++ "qemu",
++ PA_STREAM_PLAYBACK,
++ g->conf.sink,
++ &pa->ss,
++ NULL, /* channel map */
++ &pa->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 = g->conf.samples;
+- 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;
+- }
++ audio_pcm_init_info(&hw->info, &obt_as);
++ hw->samples = buflen;
+
+ 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:
++fail1:
+ return -1;
+ }
+
+ 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;
+
+- 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,
+- g->conf.source,
+- &ss,
+- NULL, /* channel map */
+- NULL, /* buffering attributes */
+- &error
+- );
++ int64_t timer_tick_duration =
++ audio_MAX(audio_get_timer_ticks(), 1 * SCALE_MS);
++ int64_t frames_per_tick_x1000 =
++ ((timer_tick_duration * as->freq * 1000LL) / NANOSECONDS_PER_SECOND);
++
++ int64_t fragsize = g->conf.fragsize;
++ if (fragsize == 0) {
++ fragsize = frames_per_tick_x1000 / 1000;
++ }
++ int64_t buflen = g->conf.buffer_size_in;
++ if (buflen == 0) {
++ buflen = frames_per_tick_x1000 / 400;
++ }
++ int64_t maxlength = g->conf.maxlength_in;
++ if (maxlength == 0) {
++ maxlength = fragsize * 2;
++ }
++
++ ldebug("IN internal buffer: %.2f ms (%"PRId64" frames)\n",
++ buflen * (1000.0f / as->freq),
++ buflen);
++
++ ldebug("IN fragsize: %.2f ms (%"PRId64" frames)\n",
++ fragsize * (1000.0f / as->freq),
++ fragsize);
++
++ ldebug("IN maxlength: %.2f ms (%"PRId64" frames)\n",
++ maxlength * (1000.0f / as->freq),
++ maxlength);
++
++ ldebug("IN adjust latency: %s\n",
++ g->conf.adjust_latency_in ? "yes" : "no");
++
++ pa->ss.format = audfmt_to_pa(as->fmt, as->endianness);
++ pa->ss.channels = as->nchannels;
++ pa->ss.rate = as->freq;
++
++ pa->ba.fragsize = fragsize * pa_frame_size(&pa->ss);
++ pa->ba.maxlength = maxlength * pa_frame_size(&pa->ss);
++ pa->ba.minreq = -1;
++ pa->ba.prebuf = -1;
++
++ obt_as.fmt = pa_to_audfmt(pa->ss.format, &obt_as.endianness);
++
++ pa->stream = qpa_simple_new(
++ g,
++ "qemu",
++ PA_STREAM_RECORD,
++ g->conf.source,
++ &pa->ss,
++ NULL, /* channel map */
++ &pa->ba, /* 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 = g->conf.samples;
+- 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;
+- }
++ audio_pcm_init_info(&hw->info, &obt_as);
++ hw->samples = buflen;
+
+ 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:
++ fail1:
+ return -1;
+ }
+
+ static void qpa_fini_out (HWVoiceOut *hw)
+ {
+- void *ret;
+ PAVoiceOut *pa = (PAVoiceOut *) hw;
+
+- audio_pt_lock(&pa->pt, __func__);
+- pa->done = 1;
+- audio_pt_unlock_and_signal(&pa->pt, __func__);
+- audio_pt_join(&pa->pt, &ret, __func__);
+-
+ if (pa->stream) {
+ pa_stream_unref (pa->stream);
+ pa->stream = NULL;
+ }
+-
+- audio_pt_fini(&pa->pt, __func__);
+- g_free (pa->pcm_buf);
+- pa->pcm_buf = NULL;
+ }
+
+ static void qpa_fini_in (HWVoiceIn *hw)
+ {
+- void *ret;
+ PAVoiceIn *pa = (PAVoiceIn *) hw;
+
+- audio_pt_lock(&pa->pt, __func__);
+- pa->done = 1;
+- audio_pt_unlock_and_signal(&pa->pt, __func__);
+- audio_pt_join(&pa->pt, &ret, __func__);
+-
+ if (pa->stream) {
+ pa_stream_unref (pa->stream);
+ pa->stream = NULL;
+ }
+-
+- audio_pt_fini(&pa->pt, __func__);
+- g_free (pa->pcm_buf);
+- pa->pcm_buf = NULL;
+ }
+
+ static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
+@@ -809,7 +647,8 @@ static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
+
+ /* common */
+ static PAConf glob_conf = {
+- .samples = 4096,
++ .adjust_latency_out = 0,
++ .adjust_latency_in = 1,
+ };
+
+ static void *qpa_audio_init (void)
+@@ -897,10 +736,46 @@ static void qpa_audio_fini (void *opaque)
+
+ struct audio_option qpa_options[] = {
+ {
+- .name = "SAMPLES",
++ .name = "BUFFER_SIZE_OUT",
++ .tag = AUD_OPT_INT,
++ .valp = &glob_conf.buffer_size_out,
++ .descr = "internal buffer size in frames for playback device"
++ },
++ {
++ .name = "BUFFER_SIZE_IN",
++ .tag = AUD_OPT_INT,
++ .valp = &glob_conf.buffer_size_in,
++ .descr = "internal buffer size in frames for recording device"
++ },
++ {
++ .name = "TLENGTH",
+ .tag = AUD_OPT_INT,
+- .valp = &glob_conf.samples,
+- .descr = "buffer size in samples"
++ .valp = &glob_conf.tlength,
++ .descr = "playback buffer target length in frames"
++ },
++ {
++ .name = "FRAGSIZE",
++ .tag = AUD_OPT_INT,
++ .valp = &glob_conf.fragsize,
++ .descr = "fragment length of recording device in frames"
++ },
++ {
++ .name = "MAXLENGTH_IN",
++ .tag = AUD_OPT_INT,
++ .valp = &glob_conf.maxlength_in,
++ .descr = "maximum length of PA recording buffer in frames"
++ },
++ {
++ .name = "ADJUST_LATENCY_OUT",
++ .tag = AUD_OPT_BOOL,
++ .valp = &glob_conf.adjust_latency_out,
++ .descr = "instruct PA to adjust latency for playback device"
++ },
++ {
++ .name = "ADJUST_LATENCY_IN",
++ .tag = AUD_OPT_BOOL,
++ .valp = &glob_conf.adjust_latency_in,
++ .descr = "instruct PA to adjust latency for recording device"
+ },
+ {
+ .name = "SERVER",
+diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c
+index 617a1c1016..6627c5cbfd 100644
+--- a/hw/audio/hda-codec.c
++++ b/hw/audio/hda-codec.c
+@@ -18,6 +18,7 @@
+ */
+
+ #include "qemu/osdep.h"
++#include "qemu/atomic.h"
+ #include "hw/hw.h"
+ #include "hw/pci/pci.h"
+ #include "intel-hda.h"
+@@ -128,6 +129,7 @@ static void hda_codec_parse_fmt(uint32_t format, struct audsettings *as)
+ #include "hda-codec-common.h"
+
+ #define HDA_TIMER_TICKS (SCALE_MS)
++#define MAX_CORR (SCALE_US * 100)
+ #define B_SIZE sizeof(st->buf)
+ #define B_MASK (sizeof(st->buf) - 1)
+
+--
+2.18.0
+