Skip to content

Instantly share code, notes, and snippets.

@luqmana
Created December 16, 2020 07:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save luqmana/489f9d06bfeb3f863fe1e59011c1a76c to your computer and use it in GitHub Desktop.
Save luqmana/489f9d06bfeb3f863fe1e59011c1a76c to your computer and use it in GitHub Desktop.
From 5af077d7b25869437d48ba3371f8874c9eeb4667 Mon Sep 17 00:00:00 2001
From: Martin Schrodt <martin@schrodt.org>
Date: Sat, 7 Oct 2017 01:19:01 +0200
Subject: [PATCH 01/30] Remove several layers of latency producing shells
---
audio/paaudio.c | 529 +++++++-------------------------------------------------
1 file changed, 60 insertions(+), 469 deletions(-)
diff --git a/audio/paaudio.c b/audio/paaudio.c
index 65beb6f010..08ce7b1d48 100644
--- a/audio/paaudio.c
+++ b/audio/paaudio.c
@@ -1,13 +1,35 @@
-/* public domain */
+/*
+ * QEMU ALSA audio driver
+ *
+ * Copyright (c) 2017 Martin Schrodt (spheenik)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
#include "qemu/osdep.h"
-#include "qemu-common.h"
#include "audio.h"
#include <pulse/pulseaudio.h>
#define AUDIO_CAP "pulseaudio"
#include "audio_int.h"
-#include "audio_pt_int.h"
+
+#define dolog(...) AUD_log ("PA", __VA_ARGS__)
typedef struct {
int samples;
@@ -24,30 +46,10 @@ 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;
} 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;
-} PAVoiceIn;
-
static void qpa_audio_fini(void *opaque);
static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...)
@@ -81,209 +83,55 @@ static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x)
}
#endif
-#define CHECK_SUCCESS_GOTO(c, rerror, expression, label) \
- do { \
- if (!(expression)) { \
- if (rerror) { \
- *(rerror) = pa_context_errno ((c)->context); \
- } \
- goto label; \
- } \
- } while (0);
-
-#define CHECK_DEAD_GOTO(c, stream, rerror, label) \
- do { \
- if (!(c)->context || !PA_CONTEXT_IS_GOOD (pa_context_get_state((c)->context)) || \
- !(stream) || !PA_STREAM_IS_GOOD (pa_stream_get_state ((stream)))) { \
- if (((c)->context && pa_context_get_state ((c)->context) == PA_CONTEXT_FAILED) || \
- ((stream) && pa_stream_get_state ((stream)) == PA_STREAM_FAILED)) { \
- if (rerror) { \
- *(rerror) = pa_context_errno ((c)->context); \
- } \
- } else { \
- if (rerror) { \
- *(rerror) = PA_ERR_BADSTATE; \
- } \
- } \
- goto label; \
- } \
- } while (0);
-
-static int qpa_simple_read (PAVoiceIn *p, 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;
-
- 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);
- }
- }
+ PAVoiceOut *pa = (PAVoiceOut *) hw;
+ int rpos, decr, samples;
+ size_t avail_bytes, max_bytes;
+ struct st_sample *src;
+ void *pa_dst;
- pa_threaded_mainloop_unlock (g->mainloop);
- return 0;
+ pa_threaded_mainloop_lock (pa->g->mainloop);
-unlock_and_fail:
- pa_threaded_mainloop_unlock (g->mainloop);
- return -1;
-}
+ avail_bytes = (size_t) live << hw->info.shift;
+ max_bytes = pa_stream_writable_size(pa->stream);
-static int qpa_simple_write (PAVoiceOut *p, const void *data, size_t length, int *rerror)
-{
- paaudio *g = p->g;
+ samples = (int)(audio_MIN (avail_bytes, max_bytes)) >> hw->info.shift;
- pa_threaded_mainloop_lock (g->mainloop);
+ decr = samples;
+ rpos = hw->rpos;
- CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail);
+ dolog("TRANSFER avail: %d bytes, max %d bytes -> %d samples from %d\n", (int)avail_bytes, (int)max_bytes, samples, rpos);
- while (length > 0) {
- size_t l;
- int r;
+ while (samples) {
+ int left_till_end_samples = hw->samples - rpos;
- while (!(l = pa_stream_writable_size (p->stream))) {
- pa_threaded_mainloop_wait (g->mainloop);
- CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail);
- }
+ int convert_samples = audio_MIN (samples, left_till_end_samples);
+ size_t convert_bytes_wanted = (size_t) convert_samples << hw->info.shift;
+ size_t convert_bytes = convert_bytes_wanted;
- CHECK_SUCCESS_GOTO (g, rerror, l != (size_t) -1, unlock_and_fail);
+ pa_stream_begin_write(pa->stream, &pa_dst, &convert_bytes);
- if (l > length) {
- l = length;
+ if (convert_bytes != convert_bytes_wanted) {
+ dolog(" OOOPS wanted %d, got %d\n", (int)convert_bytes_wanted, (int)convert_bytes);
}
- r = pa_stream_write (p->stream, data, l, NULL, 0LL, PA_SEEK_RELATIVE);
- CHECK_SUCCESS_GOTO (g, rerror, r >= 0, unlock_and_fail);
+ src = hw->mix_buf + rpos;
+ hw->clip (pa_dst, src, convert_samples);
- data = (const uint8_t *) data + l;
- length -= l;
- }
-
- pa_threaded_mainloop_unlock (g->mainloop);
- return 0;
+ int r = pa_stream_write (pa->stream, pa_dst, convert_bytes, NULL, 0LL, PA_SEEK_RELATIVE);
+ dolog(" CHUNK %d samples from %d, result %d\n", convert_samples, rpos, r);
-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, AUDIO_FUNC)) {
- return NULL;
+ rpos = (rpos + convert_samples) % hw->samples;
+ samples -= convert_samples;
}
- for (;;) {
- int decr, to_mix, rpos;
+ pa_threaded_mainloop_unlock (pa->g->mainloop);
- for (;;) {
- if (pa->done) {
- goto exit;
- }
+ dolog("\n");
- if (pa->live > 0) {
- break;
- }
-
- if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) {
- goto exit;
- }
- }
-
- decr = to_mix = audio_MIN (pa->live, pa->g->conf.samples >> 2);
- rpos = pa->rpos;
-
- if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
- return NULL;
- }
+ hw->rpos = rpos;
- 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, AUDIO_FUNC)) {
- return NULL;
- }
-
- pa->rpos = rpos;
- pa->live -= decr;
- pa->decr += decr;
- }
-
- exit:
- audio_pt_unlock (&pa->pt, AUDIO_FUNC);
- return NULL;
-}
-
-static int qpa_run_out (HWVoiceOut *hw, int live)
-{
- int decr;
- PAVoiceOut *pa = (PAVoiceOut *) hw;
-
- if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
- return 0;
- }
-
- 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, AUDIO_FUNC);
- }
- else {
- audio_pt_unlock (&pa->pt, AUDIO_FUNC);
- }
return decr;
}
@@ -292,102 +140,9 @@ 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)
-{
- PAVoiceIn *pa = arg;
- HWVoiceIn *hw = &pa->hw;
-
- 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 > 0) {
- break;
- }
-
- if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) {
- goto exit;
- }
- }
-
- incr = to_grab = audio_MIN (pa->dead, pa->g->conf.samples >> 2);
- wpos = pa->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 (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, AUDIO_FUNC)) {
- return NULL;
- }
-
- pa->wpos = wpos;
- pa->dead -= incr;
- pa->incr += incr;
- }
-
- exit:
- audio_pt_unlock (&pa->pt, AUDIO_FUNC);
- return NULL;
-}
-
-static int qpa_run_in (HWVoiceIn *hw)
-{
- int live, incr, dead;
- PAVoiceIn *pa = (PAVoiceIn *) hw;
-
- if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
- return 0;
- }
-
- 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, AUDIO_FUNC);
- }
- else {
- audio_pt_unlock (&pa->pt, AUDIO_FUNC);
- }
- return incr;
-}
-
-static int qpa_read (SWVoiceIn *sw, void *buf, int len)
-{
- return audio_pcm_sw_read (sw, buf, len);
-}
-
static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness)
{
- int format;
+ pa_sample_format_t format;
switch (afmt) {
case AUD_FMT_S8:
@@ -555,8 +310,8 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
* 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.tlength = pa_usec_to_bytes (40 * 1000, &ss);
+ ba.minreq = -1;
ba.maxlength = -1;
ba.prebuf = -1;
@@ -579,127 +334,22 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
audio_pcm_init_info (&hw->info, &obt_as);
hw->samples = g->conf.samples;
- pa->pcm_buf = audio_calloc (AUDIO_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, AUDIO_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, 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
- );
- 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 (AUDIO_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, AUDIO_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 void qpa_fini_out (HWVoiceOut *hw)
{
- void *ret;
PAVoiceOut *pa = (PAVoiceOut *) hw;
- audio_pt_lock (&pa->pt, AUDIO_FUNC);
- pa->done = 1;
- audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
- audio_pt_join (&pa->pt, &ret, AUDIO_FUNC);
-
if (pa->stream) {
pa_stream_unref (pa->stream);
pa->stream = NULL;
}
-
- audio_pt_fini (&pa->pt, AUDIO_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, AUDIO_FUNC);
- pa->done = 1;
- audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
- audio_pt_join (&pa->pt, &ret, AUDIO_FUNC);
-
- if (pa->stream) {
- pa_stream_unref (pa->stream);
- pa->stream = NULL;
- }
-
- audio_pt_fini (&pa->pt, AUDIO_FUNC);
- g_free (pa->pcm_buf);
- pa->pcm_buf = NULL;
}
static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
@@ -754,59 +404,6 @@ static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
return 0;
}
-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;
-}
-
/* common */
static PAConf glob_conf = {
.samples = 4096,
@@ -929,12 +526,6 @@ static struct audio_pcm_ops qpa_pcm_ops = {
.run_out = qpa_run_out,
.write = qpa_write,
.ctl_out = qpa_ctl_out,
-
- .init_in = qpa_init_in,
- .fini_in = qpa_fini_in,
- .run_in = qpa_run_in,
- .read = qpa_read,
- .ctl_in = qpa_ctl_in
};
struct audio_driver pa_audio_driver = {
@@ -946,8 +537,8 @@ struct audio_driver pa_audio_driver = {
.pcm_ops = &qpa_pcm_ops,
.can_be_default = 1,
.max_voices_out = INT_MAX,
- .max_voices_in = INT_MAX,
+ .max_voices_in = 0,
.voice_size_out = sizeof (PAVoiceOut),
- .voice_size_in = sizeof (PAVoiceIn),
+ .voice_size_in = 0,
.ctl_caps = VOICE_VOLUME_CAP
};
--
2.14.1
From 60e18b9e1234ba0c8431a3a874a506625aa3014c Mon Sep 17 00:00:00 2001
From: Martin Schrodt <martin@schrodt.org>
Date: Sat, 7 Oct 2017 02:14:35 +0200
Subject: [PATCH 02/30] auto adjust buffer sizes
---
audio/audio.c | 12 ++++++++++++
audio/audio_int.h | 4 ++++
audio/paaudio.c | 7 +++++--
3 files changed, 21 insertions(+), 2 deletions(-)
diff --git a/audio/audio.c b/audio/audio.c
index beafed209b..6584ff3231 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -2066,3 +2066,15 @@ void AUD_set_volume_in (SWVoiceIn *sw, int mute, uint8_t lvol, uint8_t rvol)
}
}
}
+
+int audio_get_timer_ticks() {
+ return (int) conf.period.ticks;
+}
+
+int audio_get_timer_frequency() {
+ return (int) (NANOSECONDS_PER_SECOND / conf.period.ticks);
+}
+
+int audio_get_dac_frequency() {
+ return conf.fixed_out.settings.freq;
+}
diff --git a/audio/audio_int.h b/audio/audio_int.h
index 5bcb1c60e1..86fb2067e8 100644
--- a/audio/audio_int.h
+++ b/audio/audio_int.h
@@ -214,6 +214,10 @@ extern struct audio_driver pa_audio_driver;
extern struct audio_driver spice_audio_driver;
extern const struct mixeng_volume nominal_volume;
+int audio_get_timer_ticks(void);
+int audio_get_timer_frequency(void);
+int audio_get_dac_frequency(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 08ce7b1d48..71f845ee9b 100644
--- a/audio/paaudio.c
+++ b/audio/paaudio.c
@@ -310,7 +310,7 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
* 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 (40 * 1000, &ss);
+ ba.tlength = pa_usec_to_bytes (2 * audio_get_timer_ticks() / 1000, &ss);
ba.minreq = -1;
ba.maxlength = -1;
ba.prebuf = -1;
@@ -333,7 +333,10 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
}
audio_pcm_init_info (&hw->info, &obt_as);
- hw->samples = g->conf.samples;
+ //hw->samples = g->conf.samples;
+
+ // TODO
+ hw->samples = (4 * audio_get_dac_frequency()) / audio_get_timer_frequency();
return 0;
--
2.14.1
From 8e2014e20973f92c096a1820dadee49dcbc31cc7 Mon Sep 17 00:00:00 2001
From: Martin Schrodt <martin@schrodt.org>
Date: Sat, 7 Oct 2017 17:39:32 +0200
Subject: [PATCH 03/30] expose settings, set reasonable defaults
---
audio/audio.c | 12 +-----
audio/audio_int.h | 4 +-
audio/paaudio.c | 111 +++++++++++++++++++++++++++++++++++++++---------------
3 files changed, 83 insertions(+), 44 deletions(-)
diff --git a/audio/audio.c b/audio/audio.c
index 6584ff3231..fba1604c34 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -2067,14 +2067,6 @@ void AUD_set_volume_in (SWVoiceIn *sw, int mute, uint8_t lvol, uint8_t rvol)
}
}
-int audio_get_timer_ticks() {
- return (int) conf.period.ticks;
-}
-
-int audio_get_timer_frequency() {
- return (int) (NANOSECONDS_PER_SECOND / conf.period.ticks);
-}
-
-int audio_get_dac_frequency() {
- return conf.fixed_out.settings.freq;
+int64_t audio_get_timer_ticks(void) {
+ return conf.period.ticks;
}
diff --git a/audio/audio_int.h b/audio/audio_int.h
index 86fb2067e8..2f7fc4f8ac 100644
--- a/audio/audio_int.h
+++ b/audio/audio_int.h
@@ -214,9 +214,7 @@ extern struct audio_driver pa_audio_driver;
extern struct audio_driver spice_audio_driver;
extern const struct mixeng_volume nominal_volume;
-int audio_get_timer_ticks(void);
-int audio_get_timer_frequency(void);
-int audio_get_dac_frequency(void);
+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 71f845ee9b..59bda28768 100644
--- a/audio/paaudio.c
+++ b/audio/paaudio.c
@@ -23,16 +23,20 @@
*/
#include "qemu/osdep.h"
#include "audio.h"
+#include "hw/audio/intel-hda-defs.h"
#include <pulse/pulseaudio.h>
+#include <include/qemu/timer.h>
#define AUDIO_CAP "pulseaudio"
#include "audio_int.h"
-#define dolog(...) AUD_log ("PA", __VA_ARGS__)
-
typedef struct {
- int samples;
+ int buffer_size;
+ int tlength;
+#ifdef PA_STREAM_ADJUST_LATENCY
+ int adjust_latency_out;
+#endif
char *server;
char *sink;
char *source;
@@ -101,7 +105,11 @@ static int qpa_run_out (HWVoiceOut *hw, int live)
decr = samples;
rpos = hw->rpos;
- dolog("TRANSFER avail: %d bytes, max %d bytes -> %d samples from %d\n", (int)avail_bytes, (int)max_bytes, samples, rpos);
+ if (avail_bytes < max_bytes) {
+ dolog("avail: %d, wanted: %d \n", (int)avail_bytes, (int)max_bytes);
+ }
+
+ //dolog("TRANSFER avail: %d bytes, max %d bytes -> %d samples from %d\n", (int)avail_bytes, (int)max_bytes, samples, rpos);
while (samples) {
int left_till_end_samples = hw->samples - rpos;
@@ -119,8 +127,7 @@ static int qpa_run_out (HWVoiceOut *hw, int live)
src = hw->mix_buf + rpos;
hw->clip (pa_dst, src, convert_samples);
- int r = pa_stream_write (pa->stream, pa_dst, convert_bytes, NULL, 0LL, PA_SEEK_RELATIVE);
- dolog(" CHUNK %d samples from %d, result %d\n", convert_samples, rpos, r);
+ pa_stream_write (pa->stream, pa_dst, convert_bytes, NULL, 0LL, PA_SEEK_RELATIVE);
rpos = (rpos + convert_samples) % hw->samples;
samples -= convert_samples;
@@ -128,7 +135,7 @@ static int qpa_run_out (HWVoiceOut *hw, int live)
pa_threaded_mainloop_unlock (pa->g->mainloop);
- dolog("\n");
+ //dolog("\n");
hw->rpos = rpos;
@@ -225,13 +232,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,
@@ -253,14 +253,12 @@ 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
+ | (g->conf.adjust_latency_out ? PA_STREAM_ADJUST_LATENCY : 0)
#endif
|PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL);
} else {
@@ -292,6 +290,20 @@ fail:
return NULL;
}
+
+static int64_t hob (int64_t num)
+{
+ if (!num)
+ return 0;
+
+ int ret = 1;
+
+ while (num >>= 1)
+ ret <<= 1;
+
+ return ret;
+}
+
static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
void *drv_opaque)
{
@@ -302,17 +314,40 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
PAVoiceOut *pa = (PAVoiceOut *) hw;
paaudio *g = pa->g = drv_opaque;
+ 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 + 999) / 1000;
+ }
+ int64_t buflen = g->conf.buffer_size;
+ if (buflen == 0) {
+ buflen = hob(frames_per_tick_x1000 / 125);
+ buflen = audio_MAX(HDA_BUFFER_SIZE, buflen); // must be at least HDA_BUFFER_SIZE bytes for HDA to work at all!
+ }
+
+ float ms_per_frame = 1000.0f / as->freq;
+
+ dolog("tick duration: %.2f ms (%.3f frames)\n",
+ ((float) timer_tick_duration) / SCALE_MS,
+ (float)frames_per_tick_x1000 / 1000.0f);
+
+ dolog("internal buffer: %.2f ms (%"PRId64" frames)\n",
+ buflen * ms_per_frame,
+ buflen);
+
+ dolog("tlength: %.2f ms (%"PRId64" frames)\n",
+ tlength * ms_per_frame,
+ tlength);
+
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 (2 * audio_get_timer_ticks() / 1000, &ss);
- ba.minreq = -1;
+ ba.tlength = tlength * pa_frame_size (&ss);
ba.maxlength = -1;
+ ba.minreq = -1;
ba.prebuf = -1;
obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
@@ -333,10 +368,7 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
}
audio_pcm_init_info (&hw->info, &obt_as);
- //hw->samples = g->conf.samples;
-
- // TODO
- hw->samples = (4 * audio_get_dac_frequency()) / audio_get_timer_frequency();
+ hw->samples = buflen;
return 0;
@@ -409,9 +441,12 @@ static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
/* common */
static PAConf glob_conf = {
- .samples = 4096,
+#ifdef PA_STREAM_ADJUST_LATENCY
+ .adjust_latency_out = 0,
+#endif
};
+
static void *qpa_audio_init (void)
{
paaudio *g = g_malloc(sizeof(paaudio));
@@ -497,11 +532,25 @@ static void qpa_audio_fini (void *opaque)
struct audio_option qpa_options[] = {
{
- .name = "SAMPLES",
+ .name = "INT_BUF_SIZE",
+ .tag = AUD_OPT_INT,
+ .valp = &glob_conf.buffer_size,
+ .descr = "internal buffer size in frames"
+ },
+ {
+ .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"
},
+#ifdef PA_STREAM_ADJUST_LATENCY
+ {
+ .name = "ADJUST_LATENCY_OUT",
+ .tag = AUD_OPT_BOOL,
+ .valp = &glob_conf.adjust_latency_out,
+ .descr = "let PA adjust latency for playback device"
+ },
+#endif
{
.name = "SERVER",
.tag = AUD_OPT_STR,
--
2.14.1
From d7df7f8c7c35f9aa4f00152733e9c56fc5f8347c Mon Sep 17 00:00:00 2001
From: Martin Schrodt <martin@schrodt.org>
Date: Sun, 8 Oct 2017 03:33:16 +0200
Subject: [PATCH 04/30] reduce logging
---
audio/paaudio.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/audio/paaudio.c b/audio/paaudio.c
index 59bda28768..a1a7e94d4b 100644
--- a/audio/paaudio.c
+++ b/audio/paaudio.c
@@ -105,9 +105,9 @@ static int qpa_run_out (HWVoiceOut *hw, int live)
decr = samples;
rpos = hw->rpos;
- if (avail_bytes < max_bytes) {
- dolog("avail: %d, wanted: %d \n", (int)avail_bytes, (int)max_bytes);
- }
+// if (avail_bytes < max_bytes) {
+// dolog("avail: %d, wanted: %d \n", (int)avail_bytes, (int)max_bytes);
+// }
//dolog("TRANSFER avail: %d bytes, max %d bytes -> %d samples from %d\n", (int)avail_bytes, (int)max_bytes, samples, rpos);
@@ -319,7 +319,7 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
int64_t tlength = g->conf.tlength;
if (tlength == 0) {
- tlength = (frames_per_tick_x1000 + 999) / 1000;
+ tlength = (frames_per_tick_x1000) / 500;
}
int64_t buflen = g->conf.buffer_size;
if (buflen == 0) {
@@ -341,6 +341,8 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
tlength * ms_per_frame,
tlength);
+ dolog("adjust latency: %s\n", g->conf.adjust_latency_out ? "yes" : "no");
+
ss.format = audfmt_to_pa (as->fmt, as->endianness);
ss.channels = as->nchannels;
ss.rate = as->freq;
--
2.14.1
From 7a36efa0a889ad01d538f59e5f38fca001c7ac4d Mon Sep 17 00:00:00 2001
From: Martin Schrodt <martin@schrodt.org>
Date: Sun, 8 Oct 2017 03:35:25 +0200
Subject: [PATCH 05/30] improve audio timer
---
audio/audio.c | 34 +++++++++++++++++++++++++++++++++-
1 file changed, 33 insertions(+), 1 deletion(-)
diff --git a/audio/audio.c b/audio/audio.c
index fba1604c34..fae6233caf 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -1121,10 +1121,42 @@ static void audio_reset_timer (AudioState *s)
}
}
+
+static int64_t ref_ns = -1;
+static int64_t last_ns = -1;
+
static void audio_timer (void *opaque)
{
+ int64_t start_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ if (ref_ns == -1) {
+ ref_ns = start_ns - conf.period.ticks;
+ last_ns = ref_ns;
+ }
audio_run ("timer");
- audio_reset_timer (opaque);
+ int64_t end_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+
+
+ if ((end_ns - start_ns) > conf.period.ticks) {
+ dolog("audio timer after %"PRId64" us (deviation %"PRId64" us), took %"PRId64" us\n",
+ (start_ns - last_ns) / SCALE_US,
+ (start_ns - (ref_ns + conf.period.ticks)) / SCALE_US,
+ (end_ns - start_ns) / SCALE_US
+ );
+ }
+
+ last_ns = start_ns;
+
+ AudioState *s = opaque;
+
+ if (audio_is_timer_needed ()) {
+ ref_ns += conf.period.ticks;
+ timer_mod_anticipate_ns(s->ts, audio_MAX(ref_ns + conf.period.ticks, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 1));
+ }
+ else {
+ ref_ns = -1;
+ timer_del (s->ts);
+ }
+
}
/*
--
2.14.1
From 0dc3b7e85733088c342df609b76905e72971b4cf Mon Sep 17 00:00:00 2001
From: Martin Schrodt <martin@schrodt.org>
Date: Mon, 9 Oct 2017 00:11:15 +0200
Subject: [PATCH 06/30] query HDA with a separate timer
---
hw/audio/hda-codec.c | 157 ++++++++++++++++++++++++++++++++++------------
hw/audio/intel-hda-defs.h | 1 +
hw/audio/intel-hda.c | 14 ++---
3 files changed, 126 insertions(+), 46 deletions(-)
diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c
index 5402cd196c..009d9bc2eb 100644
--- a/hw/audio/hda-codec.c
+++ b/hw/audio/hda-codec.c
@@ -154,8 +154,11 @@ struct HDAAudioStream {
SWVoiceIn *in;
SWVoiceOut *out;
} voice;
- uint8_t buf[HDA_BUFFER_SIZE];
- uint32_t bpos;
+ uint8_t buf[8192];
+ int64_t rpos;
+ int64_t wpos;
+ QEMUTimer *buft;
+ int64_t buft_start;
};
#define TYPE_HDA_AUDIO "hda-audio"
@@ -178,54 +181,117 @@ struct HDAAudioState {
static void hda_audio_input_cb(void *opaque, int avail)
{
+// HDAAudioStream *st = opaque;
+// int recv = 0;
+// int len;
+// bool rc;
+//
+// while (avail - recv >= sizeof(st->buf)) {
+// if (st->bpos != sizeof(st->buf)) {
+// len = AUD_read(st->voice.in, st->buf + st->bpos,
+// sizeof(st->buf) - st->bpos);
+// st->bpos += len;
+// recv += len;
+// if (st->bpos != sizeof(st->buf)) {
+// break;
+// }
+// }
+// rc = hda_codec_xfer(&st->state->hda, st->stream, false,
+// st->buf, sizeof(st->buf));
+// if (!rc) {
+// break;
+// }
+// st->bpos = 0;
+// }
+}
+
+
+#define dolog(fmt, ...) AUD_log("XX", fmt, ## __VA_ARGS__)
+
+static void hda_audio_output_timer(void *opaque) {
+
+#define B_SIZE sizeof(st->buf)
+#define B_MASK (sizeof(st->buf) - 1)
+
HDAAudioStream *st = opaque;
- int recv = 0;
- int len;
- bool rc;
-
- while (avail - recv >= sizeof(st->buf)) {
- if (st->bpos != sizeof(st->buf)) {
- len = AUD_read(st->voice.in, st->buf + st->bpos,
- sizeof(st->buf) - st->bpos);
- st->bpos += len;
- recv += len;
- if (st->bpos != sizeof(st->buf)) {
- break;
- }
- }
- rc = hda_codec_xfer(&st->state->hda, st->stream, false,
- st->buf, sizeof(st->buf));
+
+ int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+
+ int64_t wanted_wpos = (st->as.freq * 4 * (now - st->buft_start)) / NANOSECONDS_PER_SECOND;
+ wanted_wpos &= -4; // IMPORTANT! clip to frames
+
+ int64_t wpos = st->wpos;
+ int64_t rpos = st->rpos;
+
+ if (wanted_wpos <= wpos) {
+ // we already have the data
+ goto out_timer;
+ }
+
+ if (wpos - rpos >= B_SIZE) {
+ goto out_timer;
+ }
+
+ //dolog("%"PRId64"\n", wpos - rpos);
+
+ //dolog("rpos: %"PRId64", wpos: %"PRId64", wanted: %"PRId64"\n", rpos, wpos, wanted_wpos);
+ int64_t to_transfer = audio_MIN(B_SIZE - (wpos - rpos), wanted_wpos - wpos);
+ while (to_transfer) {
+ uint32_t start = (wpos & B_MASK);
+ uint32_t chunk = audio_MIN(B_SIZE - start, to_transfer);
+ int rc = hda_codec_xfer(&st->state->hda, st->stream, true, st->buf + start, chunk);
if (!rc) {
break;
}
- st->bpos = 0;
+ wpos += chunk;
+ to_transfer -= chunk;
+ }
+ st->wpos = wpos;
+
+#undef B_MASK
+#undef B_SIZE
+
+ out_timer:
+
+ if (st->running) {
+ timer_mod_anticipate_ns(st->buft, now + HDA_TIMER_TICKS);
}
}
static void hda_audio_output_cb(void *opaque, int avail)
{
+#define B_SIZE sizeof(st->buf)
+#define B_MASK (sizeof(st->buf) - 1)
+
HDAAudioStream *st = opaque;
- int sent = 0;
- int len;
- bool rc;
-
- while (avail - sent >= sizeof(st->buf)) {
- if (st->bpos == sizeof(st->buf)) {
- rc = hda_codec_xfer(&st->state->hda, st->stream, true,
- st->buf, sizeof(st->buf));
- if (!rc) {
- break;
- }
- st->bpos = 0;
- }
- len = AUD_write(st->voice.out, st->buf + st->bpos,
- sizeof(st->buf) - st->bpos);
- st->bpos += len;
- sent += len;
- if (st->bpos != sizeof(st->buf)) {
+
+ int64_t wpos = st->wpos;
+ int64_t rpos = st->rpos;
+
+ int64_t to_transfer = audio_MIN(wpos - rpos, avail);
+
+ int64_t overflow = wpos - rpos - to_transfer - (B_SIZE >> 3);
+ if (overflow > 0) {
+ int64_t corr = NANOSECONDS_PER_SECOND * overflow / (4 * st->as.freq);
+ //dolog("CORR %"PRId64"\n", corr);
+ st->buft_start += corr;
+ }
+
+ while (to_transfer) {
+ uint32_t start = (uint32_t) (rpos & B_MASK);
+ uint32_t chunk = (uint32_t) audio_MIN(B_SIZE - start, to_transfer);
+ uint32_t written = AUD_write(st->voice.out, st->buf + start, chunk);
+ rpos += written;
+ to_transfer -= written;
+ if (chunk != written) {
break;
}
}
+
+ st->rpos = rpos;
+
+#undef B_MASK
+#undef B_SIZE
}
static void hda_audio_set_running(HDAAudioStream *st, bool running)
@@ -237,6 +303,17 @@ static void hda_audio_set_running(HDAAudioStream *st, bool running)
return;
}
st->running = running;
+
+ if (running) {
+ int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ st->rpos = 0;
+ st->wpos = 0;
+ st->buft_start = now;
+ timer_mod_anticipate_ns(st->buft, now + HDA_TIMER_TICKS);
+ } else {
+ timer_del (st->buft);
+ }
+
dprint(st->state, 1, "%s: %s (stream %d)\n", st->node->name,
st->running ? "on" : "off", st->stream);
if (st->output) {
@@ -286,6 +363,7 @@ static void hda_audio_setup(HDAAudioStream *st)
st->voice.out = AUD_open_out(&st->state->card, st->voice.out,
st->node->name, st,
hda_audio_output_cb, &st->as);
+ st->buft = timer_new_ns(QEMU_CLOCK_VIRTUAL, hda_audio_output_timer, st);
} else {
st->voice.in = AUD_open_in(&st->state->card, st->voice.in,
st->node->name, st,
@@ -505,7 +583,6 @@ static int hda_audio_init(HDACodecDevice *hda, const struct desc_codec *desc)
/* unmute output by default */
st->gain_left = QEMU_HDA_AMP_STEPS;
st->gain_right = QEMU_HDA_AMP_STEPS;
- st->bpos = sizeof(st->buf);
st->output = true;
} else {
st->output = false;
@@ -533,6 +610,7 @@ static void hda_audio_exit(HDACodecDevice *hda)
continue;
}
if (st->output) {
+ timer_del (st->buft);
AUD_close_out(&a->card, st->voice.out);
} else {
AUD_close_in(&a->card, st->voice.in);
@@ -592,8 +670,9 @@ static const VMStateDescription vmstate_hda_audio_stream = {
VMSTATE_UINT32(gain_right, HDAAudioStream),
VMSTATE_BOOL(mute_left, HDAAudioStream),
VMSTATE_BOOL(mute_right, HDAAudioStream),
- VMSTATE_UINT32(bpos, HDAAudioStream),
VMSTATE_BUFFER(buf, HDAAudioStream),
+ VMSTATE_INT64(rpos, HDAAudioStream),
+ VMSTATE_INT64(wpos, HDAAudioStream),
VMSTATE_END_OF_LIST()
}
};
diff --git a/hw/audio/intel-hda-defs.h b/hw/audio/intel-hda-defs.h
index 2e37e5b874..900a2695ec 100644
--- a/hw/audio/intel-hda-defs.h
+++ b/hw/audio/intel-hda-defs.h
@@ -3,6 +3,7 @@
/* qemu */
#define HDA_BUFFER_SIZE 256
+#define HDA_TIMER_TICKS (SCALE_MS)
/* --------------------------------------------------------------------- */
/* from linux/sound/pci/hda/hda_intel.c */
diff --git a/hw/audio/intel-hda.c b/hw/audio/intel-hda.c
index 18a50a8f83..9d3da3185b 100644
--- a/hw/audio/intel-hda.c
+++ b/hw/audio/intel-hda.c
@@ -407,13 +407,13 @@ static bool intel_hda_xfer(HDACodecDevice *dev, uint32_t stnr, bool output,
if (st->bpl == NULL) {
return false;
}
- if (st->ctl & (1 << 26)) {
- /*
- * Wait with the next DMA xfer until the guest
- * has acked the buffer completion interrupt
- */
- return false;
- }
+// if (st->ctl & (1 << 26)) {
+// /*
+// * Wait with the next DMA xfer until the guest
+// * has acked the buffer completion interrupt
+// */
+// return false;
+// }
left = len;
s = st->bentries;
--
2.14.1
From 0e6c0f15659c56e87ca7bf21e9a2ef7053a82a0d Mon Sep 17 00:00:00 2001
From: Martin Schrodt <martin@schrodt.org>
Date: Mon, 9 Oct 2017 00:33:41 +0200
Subject: [PATCH 07/30] adjust defaults
---
audio/paaudio.c | 18 ++----------------
1 file changed, 2 insertions(+), 16 deletions(-)
diff --git a/audio/paaudio.c b/audio/paaudio.c
index a1a7e94d4b..894fcb0f71 100644
--- a/audio/paaudio.c
+++ b/audio/paaudio.c
@@ -291,19 +291,6 @@ fail:
}
-static int64_t hob (int64_t num)
-{
- if (!num)
- return 0;
-
- int ret = 1;
-
- while (num >>= 1)
- ret <<= 1;
-
- return ret;
-}
-
static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
void *drv_opaque)
{
@@ -319,12 +306,11 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
int64_t tlength = g->conf.tlength;
if (tlength == 0) {
- tlength = (frames_per_tick_x1000) / 500;
+ tlength = (frames_per_tick_x1000) / 400;
}
int64_t buflen = g->conf.buffer_size;
if (buflen == 0) {
- buflen = hob(frames_per_tick_x1000 / 125);
- buflen = audio_MAX(HDA_BUFFER_SIZE, buflen); // must be at least HDA_BUFFER_SIZE bytes for HDA to work at all!
+ buflen = frames_per_tick_x1000 / 400;
}
float ms_per_frame = 1000.0f / as->freq;
--
2.14.1
From fe63cb9defe7f256bf11002316d641e21ffb0328 Mon Sep 17 00:00:00 2001
From: Martin Schrodt <martin@schrodt.org>
Date: Tue, 10 Oct 2017 12:01:04 +0200
Subject: [PATCH 08/30] Revert "improve audio timer"
This reverts commit aba100abab90382355731404e90d91b257366a8f.
---
audio/audio.c | 34 +---------------------------------
1 file changed, 1 insertion(+), 33 deletions(-)
diff --git a/audio/audio.c b/audio/audio.c
index fae6233caf..fba1604c34 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -1121,42 +1121,10 @@ static void audio_reset_timer (AudioState *s)
}
}
-
-static int64_t ref_ns = -1;
-static int64_t last_ns = -1;
-
static void audio_timer (void *opaque)
{
- int64_t start_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
- if (ref_ns == -1) {
- ref_ns = start_ns - conf.period.ticks;
- last_ns = ref_ns;
- }
audio_run ("timer");
- int64_t end_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
-
-
- if ((end_ns - start_ns) > conf.period.ticks) {
- dolog("audio timer after %"PRId64" us (deviation %"PRId64" us), took %"PRId64" us\n",
- (start_ns - last_ns) / SCALE_US,
- (start_ns - (ref_ns + conf.period.ticks)) / SCALE_US,
- (end_ns - start_ns) / SCALE_US
- );
- }
-
- last_ns = start_ns;
-
- AudioState *s = opaque;
-
- if (audio_is_timer_needed ()) {
- ref_ns += conf.period.ticks;
- timer_mod_anticipate_ns(s->ts, audio_MAX(ref_ns + conf.period.ticks, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 1));
- }
- else {
- ref_ns = -1;
- timer_del (s->ts);
- }
-
+ audio_reset_timer (opaque);
}
/*
--
2.14.1
From 673dd8aaab8c6554474d7abd95f032a3e739b320 Mon Sep 17 00:00:00 2001
From: Martin Schrodt <martin@schrodt.org>
Date: Tue, 10 Oct 2017 12:42:22 +0200
Subject: [PATCH 09/30] fail safety in qpa_run_out
---
audio/paaudio.c | 55 ++++++++++++++++++++++++++++++++++++++++++-------------
1 file changed, 42 insertions(+), 13 deletions(-)
diff --git a/audio/paaudio.c b/audio/paaudio.c
index 894fcb0f71..7c15b091fa 100644
--- a/audio/paaudio.c
+++ b/audio/paaudio.c
@@ -87,6 +87,28 @@ static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x)
}
#endif
+#define CHECK_SUCCESS_GOTO(c, rerror, expression, label) \
+ do { \
+ if (!(expression)) { \
+ *(rerror) = pa_context_errno ((c)->context); \
+ goto label; \
+ } \
+ } while (0);
+
+#define CHECK_DEAD_GOTO(c, stream, rerror, label) \
+ do { \
+ if (!(c)->context || !PA_CONTEXT_IS_GOOD (pa_context_get_state((c)->context)) || \
+ !(stream) || !PA_STREAM_IS_GOOD (pa_stream_get_state ((stream)))) { \
+ if (((c)->context && pa_context_get_state ((c)->context) == PA_CONTEXT_FAILED) || \
+ ((stream) && pa_stream_get_state ((stream)) == PA_STREAM_FAILED)) { \
+ *(rerror) = pa_context_errno ((c)->context); \
+ } else { \
+ *(rerror) = PA_ERR_BADSTATE; \
+ } \
+ goto label; \
+ } \
+} while (0);
+
static int qpa_run_out (HWVoiceOut *hw, int live)
{
PAVoiceOut *pa = (PAVoiceOut *) hw;
@@ -94,22 +116,26 @@ static int qpa_run_out (HWVoiceOut *hw, int live)
size_t avail_bytes, max_bytes;
struct st_sample *src;
void *pa_dst;
+ int error = 0;
+ int r;
+
+ decr = 0;
+ rpos = hw->rpos;
pa_threaded_mainloop_lock (pa->g->mainloop);
+ CHECK_DEAD_GOTO (pa->g, pa->stream, &error, fail);
avail_bytes = (size_t) live << hw->info.shift;
max_bytes = pa_stream_writable_size(pa->stream);
+ CHECK_SUCCESS_GOTO(pa->g, &error, max_bytes != -1, fail);
samples = (int)(audio_MIN (avail_bytes, max_bytes)) >> hw->info.shift;
- decr = samples;
- rpos = hw->rpos;
-
// if (avail_bytes < max_bytes) {
// dolog("avail: %d, wanted: %d \n", (int)avail_bytes, (int)max_bytes);
// }
- //dolog("TRANSFER avail: %d bytes, max %d bytes -> %d samples from %d\n", (int)avail_bytes, (int)max_bytes, samples, rpos);
+// dolog("TRANSFER avail: %d bytes, max %d bytes -> %d samples from %d\n", (int)avail_bytes, (int)max_bytes, samples, rpos);
while (samples) {
int left_till_end_samples = hw->samples - rpos;
@@ -118,28 +144,31 @@ static int qpa_run_out (HWVoiceOut *hw, int live)
size_t convert_bytes_wanted = (size_t) convert_samples << hw->info.shift;
size_t convert_bytes = convert_bytes_wanted;
- pa_stream_begin_write(pa->stream, &pa_dst, &convert_bytes);
-
- if (convert_bytes != convert_bytes_wanted) {
- dolog(" OOOPS wanted %d, got %d\n", (int)convert_bytes_wanted, (int)convert_bytes);
- }
+ r = pa_stream_begin_write(pa->stream, &pa_dst, &convert_bytes);
+ CHECK_SUCCESS_GOTO(pa->g, &error, r == 0, fail);
+ CHECK_SUCCESS_GOTO(pa->g, &error, convert_bytes == convert_bytes_wanted, fail);
src = hw->mix_buf + rpos;
hw->clip (pa_dst, src, convert_samples);
- pa_stream_write (pa->stream, pa_dst, convert_bytes, NULL, 0LL, PA_SEEK_RELATIVE);
+ r = pa_stream_write (pa->stream, pa_dst, convert_bytes, NULL, 0LL, PA_SEEK_RELATIVE);
+ CHECK_SUCCESS_GOTO(pa->g, &error, r >= 0, fail);
rpos = (rpos + convert_samples) % hw->samples;
samples -= convert_samples;
+ decr += convert_samples;
}
+ bail:
pa_threaded_mainloop_unlock (pa->g->mainloop);
- //dolog("\n");
-
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)
--
2.14.1
From be6fcef0075fae6c664bf3164d7dd5a88ccfafba Mon Sep 17 00:00:00 2001
From: Martin Schrodt <martin@schrodt.org>
Date: Tue, 10 Oct 2017 18:10:16 +0200
Subject: [PATCH 10/30] add input functionality back to paaudio.c
---
audio/paaudio.c | 296 ++++++++++++++++++++++++++++++++++++++++++++++-----
hw/audio/hda-codec.c | 127 ++++++++++++++++------
2 files changed, 367 insertions(+), 56 deletions(-)
diff --git a/audio/paaudio.c b/audio/paaudio.c
index 7c15b091fa..089af32e4d 100644
--- a/audio/paaudio.c
+++ b/audio/paaudio.c
@@ -23,7 +23,7 @@
*/
#include "qemu/osdep.h"
#include "audio.h"
-#include "hw/audio/intel-hda-defs.h"
+
#include <pulse/pulseaudio.h>
#include <include/qemu/timer.h>
@@ -34,8 +34,10 @@
typedef struct {
int buffer_size;
int tlength;
+ int fragsize;
#ifdef PA_STREAM_ADJUST_LATENCY
int adjust_latency_out;
+ int adjust_latency_in;
#endif
char *server;
char *sink;
@@ -52,8 +54,19 @@ typedef struct {
HWVoiceOut hw;
pa_stream *stream;
paaudio *g;
+ pa_sample_spec ss;
+ pa_buffer_attr ba;
} PAVoiceOut;
+typedef struct {
+ HWVoiceIn hw;
+ pa_stream *stream;
+ paaudio *g;
+ pa_sample_spec ss;
+ pa_buffer_attr ba;
+} PAVoiceIn;
+
+
static void qpa_audio_fini(void *opaque);
static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...)
@@ -95,6 +108,7 @@ static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x)
} \
} while (0);
+
#define CHECK_DEAD_GOTO(c, stream, rerror, label) \
do { \
if (!(c)->context || !PA_CONTEXT_IS_GOOD (pa_context_get_state((c)->context)) || \
@@ -168,7 +182,6 @@ static int qpa_run_out (HWVoiceOut *hw, int live)
fail:
qpa_logerr (error, "qpa_run_out failed\n");
goto bail;
-
}
static int qpa_write (SWVoiceOut *sw, void *buf, int len)
@@ -176,6 +189,93 @@ static int qpa_write (SWVoiceOut *sw, void *buf, int len)
return audio_pcm_sw_write (sw, buf, len);
}
+static int qpa_run_in (HWVoiceIn *hw)
+{
+ PAVoiceIn *pa = (PAVoiceIn *) hw;
+ int wpos, incr;
+ char *pa_src;
+ int error = 0;
+ int r;
+
+ incr = 0;
+ wpos = hw->wpos;
+
+ pa_threaded_mainloop_lock (pa->g->mainloop);
+ CHECK_DEAD_GOTO (pa->g, pa->stream, &error, fail);
+
+ size_t bytes_wanted = ((unsigned int)(hw->samples - audio_pcm_hw_get_live_in(hw)) << hw->info.shift);
+ if (bytes_wanted == 0) {
+ // no room
+ goto bail;
+ }
+
+ size_t bytes_avail = pa_stream_readable_size(pa->stream);
+
+ //dolog("WANT %d, HAVE %d\n", (int)bytes_wanted, (int) bytes_avail);
+
+ size_t pa_avail;
+
+ if (bytes_avail > bytes_wanted) {
+#if 0
+ size_t to_drop = bytes_avail - bytes_wanted;
+ while (to_drop) {
+ r = pa_stream_peek(pa->stream, (const void **)&pa_src, &pa_avail);
+ CHECK_SUCCESS_GOTO(pa->g, &error, r == 0, fail);
+ if (to_drop < pa_avail) {
+ break;
+ }
+ r = pa_stream_drop(pa->stream);
+ CHECK_SUCCESS_GOTO(pa->g, &error, r == 0, fail);
+ to_drop -= pa_avail;
+ }
+ int n_dropped = (int)(bytes_avail - bytes_wanted - to_drop);
+ if(n_dropped) {
+ dolog("dropped %d bytes\n", n_dropped);
+ }
+#endif
+ } else if (bytes_wanted < bytes_avail) {
+ bytes_wanted = bytes_avail;
+ }
+
+ while (bytes_wanted) {
+ r = pa_stream_peek(pa->stream, (const void **)&pa_src, &pa_avail);
+ CHECK_SUCCESS_GOTO(pa->g, &error, r == 0, fail);
+ if (pa_avail == 0 || pa_avail > bytes_wanted) {
+ break;
+ }
+
+ bytes_wanted -= pa_avail;
+
+ 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;
+ pa_src += chunk << hw->info.shift;
+ pa_avail -= chunk << hw->info.shift;
+ incr += chunk;
+ }
+
+ r = pa_stream_drop(pa->stream);
+ CHECK_SUCCESS_GOTO(pa->g, &error, r == 0, fail);
+ }
+
+ bail:
+ pa_threaded_mainloop_unlock (pa->g->mainloop);
+
+ 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)
+{
+ return audio_pcm_sw_read (sw, buf, len);
+}
+
static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness)
{
pa_sample_format_t format;
@@ -294,7 +394,7 @@ static pa_stream *qpa_simple_new (
r = pa_stream_connect_record (stream, dev, attr,
PA_STREAM_INTERPOLATE_TIMING
#ifdef PA_STREAM_ADJUST_LATENCY
- |PA_STREAM_ADJUST_LATENCY
+ | (g->conf.adjust_latency_in ? PA_STREAM_ADJUST_LATENCY : 0)
#endif
|PA_STREAM_AUTO_TIMING_UPDATE);
}
@@ -324,8 +424,6 @@ 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;
@@ -348,35 +446,35 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
((float) timer_tick_duration) / SCALE_MS,
(float)frames_per_tick_x1000 / 1000.0f);
- dolog("internal buffer: %.2f ms (%"PRId64" frames)\n",
+ dolog("OUT internal buffer: %.2f ms (%"PRId64" frames)\n",
buflen * ms_per_frame,
buflen);
- dolog("tlength: %.2f ms (%"PRId64" frames)\n",
+ dolog("OUT tlength: %.2f ms (%"PRId64" frames)\n",
tlength * ms_per_frame,
tlength);
- dolog("adjust latency: %s\n", g->conf.adjust_latency_out ? "yes" : "no");
+ dolog("OUT adjust latency: %s\n", g->conf.adjust_latency_out ? "yes" : "no");
- ss.format = audfmt_to_pa (as->fmt, as->endianness);
- ss.channels = as->nchannels;
- ss.rate = as->freq;
+ pa->ss.format = audfmt_to_pa (as->fmt, as->endianness);
+ pa->ss.channels = as->nchannels;
+ pa->ss.rate = as->freq;
- ba.tlength = tlength * pa_frame_size (&ss);
- ba.maxlength = -1;
- ba.minreq = -1;
- ba.prebuf = -1;
+ 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 (ss.format, &obt_as.endianness);
+ 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,
- &ss,
+ &pa->ss,
NULL, /* channel map */
- &ba, /* buffering attributes */
+ &pa->ba, /* buffering attributes */
&error
);
if (!pa->stream) {
@@ -394,6 +492,74 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
}
+static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as,
+ void *drv_opaque)
+{
+ int error;
+ struct audsettings obt_as = *as;
+ PAVoiceIn *pa = (PAVoiceIn *) hw;
+ paaudio *g = pa->g = drv_opaque;
+
+ 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 / 2500;
+ }
+ int64_t buflen = g->conf.buffer_size;
+ if (buflen == 0) {
+ buflen = frames_per_tick_x1000 / 400;
+ }
+
+ float ms_per_frame = 1000.0f / as->freq;
+
+ dolog("IN internal buffer: %.2f ms (%"PRId64" frames)\n",
+ buflen * ms_per_frame,
+ buflen);
+
+ dolog("IN fragsize: %.2f ms (%"PRId64" frames)\n",
+ fragsize * ms_per_frame,
+ fragsize);
+
+ dolog("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 = pa->ba.fragsize * 10;
+ 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 playback failed\n");
+ goto fail1;
+ }
+
+ audio_pcm_init_info (&hw->info, &obt_as);
+ hw->samples = buflen;
+
+ return 0;
+
+ fail1:
+ return -1;
+}
+
+
static void qpa_fini_out (HWVoiceOut *hw)
{
PAVoiceOut *pa = (PAVoiceOut *) hw;
@@ -404,6 +570,16 @@ static void qpa_fini_out (HWVoiceOut *hw)
}
}
+static void qpa_fini_in (HWVoiceIn *hw)
+{
+ PAVoiceIn *pa = (PAVoiceIn *) hw;
+
+ if (pa->stream) {
+ pa_stream_unref (pa->stream);
+ pa->stream = NULL;
+ }
+}
+
static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
{
PAVoiceOut *pa = (PAVoiceOut *) hw;
@@ -456,10 +632,64 @@ static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
return 0;
}
+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;
+}
+
/* common */
static PAConf glob_conf = {
#ifdef PA_STREAM_ADJUST_LATENCY
.adjust_latency_out = 0,
+ .adjust_latency_in = 1,
#endif
};
@@ -560,12 +790,24 @@ struct audio_option qpa_options[] = {
.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"
+ },
#ifdef PA_STREAM_ADJUST_LATENCY
{
- .name = "ADJUST_LATENCY_OUT",
- .tag = AUD_OPT_BOOL,
- .valp = &glob_conf.adjust_latency_out,
- .descr = "let PA adjust latency for playback device"
+ .name = "ADJUST_LATENCY_OUT",
+ .tag = AUD_OPT_BOOL,
+ .valp = &glob_conf.adjust_latency_out,
+ .descr = "let PA adjust latency for playback device"
+ },
+ {
+ .name = "ADJUST_LATENCY_IN",
+ .tag = AUD_OPT_BOOL,
+ .valp = &glob_conf.adjust_latency_in,
+ .descr = "let PA adjust latency for recording device"
},
#endif
{
@@ -595,6 +837,12 @@ static struct audio_pcm_ops qpa_pcm_ops = {
.run_out = qpa_run_out,
.write = qpa_write,
.ctl_out = qpa_ctl_out,
+
+ .init_in = qpa_init_in,
+ .fini_in = qpa_fini_in,
+ .run_in = qpa_run_in,
+ .read = qpa_read,
+ .ctl_in = qpa_ctl_in
};
struct audio_driver pa_audio_driver = {
@@ -606,8 +854,8 @@ struct audio_driver pa_audio_driver = {
.pcm_ops = &qpa_pcm_ops,
.can_be_default = 1,
.max_voices_out = INT_MAX,
- .max_voices_in = 0,
+ .max_voices_in = INT_MAX,
.voice_size_out = sizeof (PAVoiceOut),
- .voice_size_in = 0,
+ .voice_size_in = sizeof (PAVoiceIn),
.ctl_caps = VOICE_VOLUME_CAP
};
diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c
index 009d9bc2eb..760bfe40d4 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"
@@ -179,30 +180,91 @@ struct HDAAudioState {
bool mixer;
};
+static void hda_audio_input_timer(void *opaque) {
+
+#define B_SIZE sizeof(st->buf)
+#define B_MASK (sizeof(st->buf) - 1)
+
+ HDAAudioStream *st = opaque;
+
+ int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+
+ int64_t buft_start = atomic_fetch_add(&st->buft_start, 0);
+ int64_t wpos = atomic_fetch_add(&st->wpos, 0);
+ int64_t rpos = atomic_fetch_add(&st->rpos, 0);
+
+ int64_t wanted_rpos = (st->as.freq * 4 * (now - buft_start)) / NANOSECONDS_PER_SECOND;
+ wanted_rpos &= -4; // IMPORTANT! clip to frames
+
+ if (wanted_rpos <= rpos) {
+ // we already transmitted the data
+ goto out_timer;
+ }
+
+ if (wpos - rpos >= B_SIZE) {
+ goto out_timer;
+ }
+
+ //dolog("%"PRId64"\n", wpos - rpos);
+
+ //dolog("rpos: %"PRId64", wpos: %"PRId64", wanted: %"PRId64"\n", rpos, wpos, wanted_wpos);
+ int64_t to_transfer = audio_MIN(B_SIZE - (wpos - rpos), wanted_rpos - rpos);
+ while (to_transfer) {
+ uint32_t start = (rpos & B_MASK);
+ uint32_t chunk = audio_MIN(B_SIZE - start, to_transfer);
+ int rc = hda_codec_xfer(&st->state->hda, st->stream, false, st->buf + start, chunk);
+ if (!rc) {
+ break;
+ }
+ rpos += chunk;
+ to_transfer -= chunk;
+ atomic_fetch_add(&st->rpos, chunk);
+ }
+
+#undef B_MASK
+#undef B_SIZE
+
+ out_timer:
+
+ if (st->running) {
+ timer_mod_anticipate_ns(st->buft, now + HDA_TIMER_TICKS);
+ }
+}
+
+
static void hda_audio_input_cb(void *opaque, int avail)
{
-// HDAAudioStream *st = opaque;
-// int recv = 0;
-// int len;
-// bool rc;
-//
-// while (avail - recv >= sizeof(st->buf)) {
-// if (st->bpos != sizeof(st->buf)) {
-// len = AUD_read(st->voice.in, st->buf + st->bpos,
-// sizeof(st->buf) - st->bpos);
-// st->bpos += len;
-// recv += len;
-// if (st->bpos != sizeof(st->buf)) {
-// break;
-// }
-// }
-// rc = hda_codec_xfer(&st->state->hda, st->stream, false,
-// st->buf, sizeof(st->buf));
-// if (!rc) {
-// break;
-// }
-// st->bpos = 0;
+#define B_SIZE sizeof(st->buf)
+#define B_MASK (sizeof(st->buf) - 1)
+
+ HDAAudioStream *st = opaque;
+
+ int64_t wpos = atomic_fetch_add(&st->wpos, 0);
+ int64_t rpos = atomic_fetch_add(&st->rpos, 0);
+
+ int64_t to_transfer = audio_MIN(wpos - rpos, avail);
+
+// int64_t overflow = wpos - rpos - to_transfer - (B_SIZE >> 3);
+// if (overflow > 0) {
+// int64_t corr = NANOSECONDS_PER_SECOND * overflow / (4 * st->as.freq);
+// //dolog("CORR %"PRId64"\n", corr);
+// atomic_fetch_add(&st->buft_start, corr);
// }
+
+ while (to_transfer) {
+ uint32_t start = (uint32_t) (wpos & B_MASK);
+ uint32_t chunk = (uint32_t) audio_MIN(B_SIZE - start, to_transfer);
+ uint32_t read = AUD_read(st->voice.in, st->buf + start, chunk);
+ wpos += read;
+ to_transfer -= read;
+ atomic_fetch_add(&st->wpos, read);
+ if (chunk != read) {
+ break;
+ }
+ }
+
+#undef B_MASK
+#undef B_SIZE
}
@@ -217,14 +279,15 @@ static void hda_audio_output_timer(void *opaque) {
int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
- int64_t wanted_wpos = (st->as.freq * 4 * (now - st->buft_start)) / NANOSECONDS_PER_SECOND;
- wanted_wpos &= -4; // IMPORTANT! clip to frames
+ int64_t buft_start = atomic_fetch_add(&st->buft_start, 0);
+ int64_t wpos = atomic_fetch_add(&st->wpos, 0);
+ int64_t rpos = atomic_fetch_add(&st->rpos, 0);
- int64_t wpos = st->wpos;
- int64_t rpos = st->rpos;
+ int64_t wanted_wpos = (st->as.freq * 4 * (now - buft_start)) / NANOSECONDS_PER_SECOND;
+ wanted_wpos &= -4; // IMPORTANT! clip to frames
if (wanted_wpos <= wpos) {
- // we already have the data
+ // we already received the data
goto out_timer;
}
@@ -245,8 +308,8 @@ static void hda_audio_output_timer(void *opaque) {
}
wpos += chunk;
to_transfer -= chunk;
+ atomic_fetch_add(&st->wpos, chunk);
}
- st->wpos = wpos;
#undef B_MASK
#undef B_SIZE
@@ -265,8 +328,8 @@ static void hda_audio_output_cb(void *opaque, int avail)
HDAAudioStream *st = opaque;
- int64_t wpos = st->wpos;
- int64_t rpos = st->rpos;
+ int64_t wpos = atomic_fetch_add(&st->wpos, 0);
+ int64_t rpos = atomic_fetch_add(&st->rpos, 0);
int64_t to_transfer = audio_MIN(wpos - rpos, avail);
@@ -274,7 +337,7 @@ static void hda_audio_output_cb(void *opaque, int avail)
if (overflow > 0) {
int64_t corr = NANOSECONDS_PER_SECOND * overflow / (4 * st->as.freq);
//dolog("CORR %"PRId64"\n", corr);
- st->buft_start += corr;
+ atomic_fetch_add(&st->buft_start, corr);
}
while (to_transfer) {
@@ -283,13 +346,12 @@ static void hda_audio_output_cb(void *opaque, int avail)
uint32_t written = AUD_write(st->voice.out, st->buf + start, chunk);
rpos += written;
to_transfer -= written;
+ atomic_fetch_add(&st->rpos, written);
if (chunk != written) {
break;
}
}
- st->rpos = rpos;
-
#undef B_MASK
#undef B_SIZE
}
@@ -368,6 +430,7 @@ static void hda_audio_setup(HDAAudioStream *st)
st->voice.in = AUD_open_in(&st->state->card, st->voice.in,
st->node->name, st,
hda_audio_input_cb, &st->as);
+ st->buft = timer_new_ns(QEMU_CLOCK_VIRTUAL, hda_audio_input_timer, st);
}
}
--
2.14.1
From c06237f676ff4d75037678f829504006c55cdb04 Mon Sep 17 00:00:00 2001
From: Martin Schrodt <martin@schrodt.org>
Date: Sat, 14 Oct 2017 21:27:39 +0200
Subject: [PATCH 11/30] fix wrong calculation of input buffer transfer size
---
hw/audio/hda-codec.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c
index 760bfe40d4..60a5e40827 100644
--- a/hw/audio/hda-codec.c
+++ b/hw/audio/hda-codec.c
@@ -180,6 +180,8 @@ struct HDAAudioState {
bool mixer;
};
+#define dolog(fmt, ...) AUD_log("", fmt, ## __VA_ARGS__)
+
static void hda_audio_input_timer(void *opaque) {
#define B_SIZE sizeof(st->buf)
@@ -242,7 +244,7 @@ static void hda_audio_input_cb(void *opaque, int avail)
int64_t wpos = atomic_fetch_add(&st->wpos, 0);
int64_t rpos = atomic_fetch_add(&st->rpos, 0);
- int64_t to_transfer = audio_MIN(wpos - rpos, avail);
+ int64_t to_transfer = audio_MIN(B_SIZE - (wpos - rpos), avail);
// int64_t overflow = wpos - rpos - to_transfer - (B_SIZE >> 3);
// if (overflow > 0) {
@@ -268,7 +270,6 @@ static void hda_audio_input_cb(void *opaque, int avail)
}
-#define dolog(fmt, ...) AUD_log("XX", fmt, ## __VA_ARGS__)
static void hda_audio_output_timer(void *opaque) {
--
2.14.1
From fb256bfbb1d19c28c5fdaeaddc3d17b205cee2c5 Mon Sep 17 00:00:00 2001
From: Martin Schrodt <martin@schrodt.org>
Date: Sun, 15 Oct 2017 01:22:55 +0200
Subject: [PATCH 12/30] try better correction
---
hw/audio/hda-codec.c | 32 +++++++++++++++++++++-----------
1 file changed, 21 insertions(+), 11 deletions(-)
diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c
index 60a5e40827..872840f602 100644
--- a/hw/audio/hda-codec.c
+++ b/hw/audio/hda-codec.c
@@ -182,6 +182,8 @@ struct HDAAudioState {
#define dolog(fmt, ...) AUD_log("", fmt, ## __VA_ARGS__)
+#define MAX_CORR (SCALE_US * 100)
+
static void hda_audio_input_timer(void *opaque) {
#define B_SIZE sizeof(st->buf)
@@ -246,12 +248,16 @@ static void hda_audio_input_cb(void *opaque, int avail)
int64_t to_transfer = audio_MIN(B_SIZE - (wpos - rpos), avail);
-// int64_t overflow = wpos - rpos - to_transfer - (B_SIZE >> 3);
-// if (overflow > 0) {
-// int64_t corr = NANOSECONDS_PER_SECOND * overflow / (4 * st->as.freq);
-// //dolog("CORR %"PRId64"\n", corr);
-// atomic_fetch_add(&st->buft_start, corr);
-// }
+
+ int64_t c = (wpos - rpos) + to_transfer - (B_SIZE >> 1);
+ int64_t corr = NANOSECONDS_PER_SECOND * c / (4 * st->as.freq);
+ if (corr > MAX_CORR) {
+ corr = MAX_CORR;
+ } else if (corr < -MAX_CORR) {
+ corr = -MAX_CORR;
+ }
+ //dolog("CORR %"PRId64"\n", -corr);
+ atomic_fetch_add(&st->buft_start, -corr);
while (to_transfer) {
uint32_t start = (uint32_t) (wpos & B_MASK);
@@ -334,12 +340,16 @@ static void hda_audio_output_cb(void *opaque, int avail)
int64_t to_transfer = audio_MIN(wpos - rpos, avail);
- int64_t overflow = wpos - rpos - to_transfer - (B_SIZE >> 3);
- if (overflow > 0) {
- int64_t corr = NANOSECONDS_PER_SECOND * overflow / (4 * st->as.freq);
- //dolog("CORR %"PRId64"\n", corr);
- atomic_fetch_add(&st->buft_start, corr);
+
+ int64_t c = (wpos - rpos) - to_transfer - (B_SIZE >> 1);
+ int64_t corr = NANOSECONDS_PER_SECOND * c / (4 * st->as.freq);
+ if (corr > MAX_CORR) {
+ corr = MAX_CORR;
+ } else if (corr < -MAX_CORR) {
+ corr = -MAX_CORR;
}
+ //dolog("CORR %"PRId64"\n", corr);
+ atomic_fetch_add(&st->buft_start, corr);
while (to_transfer) {
uint32_t start = (uint32_t) (rpos & B_MASK);
--
2.14.1
From 5562825af1ffd49b509032ec4e5370d259f04963 Mon Sep 17 00:00:00 2001
From: Martin Schrodt <martin@schrodt.org>
Date: Sun, 15 Oct 2017 11:35:05 +0200
Subject: [PATCH 13/30] PA_STREAM_ADJUST_LATENCY supported since PA 0.9.11
(July 2008), can now safely require it
---
audio/paaudio.c | 8 --------
1 file changed, 8 deletions(-)
diff --git a/audio/paaudio.c b/audio/paaudio.c
index 089af32e4d..fd94d73fe4 100644
--- a/audio/paaudio.c
+++ b/audio/paaudio.c
@@ -35,10 +35,8 @@ typedef struct {
int buffer_size;
int tlength;
int fragsize;
-#ifdef PA_STREAM_ADJUST_LATENCY
int adjust_latency_out;
int adjust_latency_in;
-#endif
char *server;
char *sink;
char *source;
@@ -386,16 +384,12 @@ static pa_stream *qpa_simple_new (
if (dir == PA_STREAM_PLAYBACK) {
r = pa_stream_connect_playback (stream, dev, attr,
PA_STREAM_INTERPOLATE_TIMING
-#ifdef PA_STREAM_ADJUST_LATENCY
| (g->conf.adjust_latency_out ? PA_STREAM_ADJUST_LATENCY : 0)
-#endif
|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
| (g->conf.adjust_latency_in ? PA_STREAM_ADJUST_LATENCY : 0)
-#endif
|PA_STREAM_AUTO_TIMING_UPDATE);
}
@@ -796,7 +790,6 @@ struct audio_option qpa_options[] = {
.valp = &glob_conf.fragsize,
.descr = "fragment length of recording device in frames"
},
-#ifdef PA_STREAM_ADJUST_LATENCY
{
.name = "ADJUST_LATENCY_OUT",
.tag = AUD_OPT_BOOL,
@@ -809,7 +802,6 @@ struct audio_option qpa_options[] = {
.valp = &glob_conf.adjust_latency_in,
.descr = "let PA adjust latency for recording device"
},
-#endif
{
.name = "SERVER",
.tag = AUD_OPT_STR,
--
2.14.1
From 71f329de776f55a493069b2008cbfb4a1871e2f9 Mon Sep 17 00:00:00 2001
From: Martin Schrodt <martin@schrodt.org>
Date: Sun, 15 Oct 2017 11:36:15 +0200
Subject: [PATCH 14/30] leave code in public domain
---
audio/paaudio.c | 24 +-----------------------
1 file changed, 1 insertion(+), 23 deletions(-)
diff --git a/audio/paaudio.c b/audio/paaudio.c
index fd94d73fe4..55dd5d755d 100644
--- a/audio/paaudio.c
+++ b/audio/paaudio.c
@@ -1,26 +1,4 @@
-/*
- * QEMU ALSA audio driver
- *
- * Copyright (c) 2017 Martin Schrodt (spheenik)
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
+/* public domain */
#include "qemu/osdep.h"
#include "audio.h"
--
2.14.1
From 934d0176620c95b787466aac8ac17fc1ed063ad6 Mon Sep 17 00:00:00 2001
From: Martin Schrodt <martin@schrodt.org>
Date: Sun, 15 Oct 2017 13:40:23 +0200
Subject: [PATCH 15/30] cleanup HDA timer code
---
hw/audio/hda-codec.c | 113 ++++++++++++++++-----------------------------------
hw/audio/intel-hda.c | 7 ----
2 files changed, 34 insertions(+), 86 deletions(-)
diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c
index 872840f602..af686eb1f3 100644
--- a/hw/audio/hda-codec.c
+++ b/hw/audio/hda-codec.c
@@ -127,6 +127,10 @@ static void hda_codec_parse_fmt(uint32_t format, struct audsettings *as)
#define PARAM nomixemu
#include "hda-codec-common.h"
+#define MAX_CORR (SCALE_US * 100)
+#define B_SIZE sizeof(st->buf)
+#define B_MASK (sizeof(st->buf) - 1)
+
/* -------------------------------------------------------------------------- */
static const char *fmt2name[] = {
@@ -155,7 +159,7 @@ struct HDAAudioStream {
SWVoiceIn *in;
SWVoiceOut *out;
} voice;
- uint8_t buf[8192];
+ uint8_t buf[8192]; // size must be power of two
int64_t rpos;
int64_t wpos;
QEMUTimer *buft;
@@ -180,15 +184,24 @@ struct HDAAudioState {
bool mixer;
};
-#define dolog(fmt, ...) AUD_log("", fmt, ## __VA_ARGS__)
-
-#define MAX_CORR (SCALE_US * 100)
-
-static void hda_audio_input_timer(void *opaque) {
+static inline int64_t hda_bytes_per_second(HDAAudioStream *st)
+{
+ return 2 * st->as.nchannels * st->as.freq;
+}
-#define B_SIZE sizeof(st->buf)
-#define B_MASK (sizeof(st->buf) - 1)
+static inline void hda_timer_sync_adjust(HDAAudioStream *st, int64_t target_pos)
+{
+ int64_t corr = NANOSECONDS_PER_SECOND * target_pos / hda_bytes_per_second(st);
+ if (corr > MAX_CORR) {
+ corr = MAX_CORR;
+ } else if (corr < -MAX_CORR) {
+ corr = -MAX_CORR;
+ }
+ atomic_fetch_add(&st->buft_start, corr);
+}
+static void hda_audio_input_timer(void *opaque)
+{
HDAAudioStream *st = opaque;
int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
@@ -197,7 +210,7 @@ static void hda_audio_input_timer(void *opaque) {
int64_t wpos = atomic_fetch_add(&st->wpos, 0);
int64_t rpos = atomic_fetch_add(&st->rpos, 0);
- int64_t wanted_rpos = (st->as.freq * 4 * (now - buft_start)) / NANOSECONDS_PER_SECOND;
+ int64_t wanted_rpos = hda_bytes_per_second(st) * (now - buft_start) / NANOSECONDS_PER_SECOND;
wanted_rpos &= -4; // IMPORTANT! clip to frames
if (wanted_rpos <= rpos) {
@@ -205,14 +218,7 @@ static void hda_audio_input_timer(void *opaque) {
goto out_timer;
}
- if (wpos - rpos >= B_SIZE) {
- goto out_timer;
- }
-
- //dolog("%"PRId64"\n", wpos - rpos);
-
- //dolog("rpos: %"PRId64", wpos: %"PRId64", wanted: %"PRId64"\n", rpos, wpos, wanted_wpos);
- int64_t to_transfer = audio_MIN(B_SIZE - (wpos - rpos), wanted_rpos - rpos);
+ int64_t to_transfer = audio_MIN(wpos - rpos, wanted_rpos - rpos);
while (to_transfer) {
uint32_t start = (rpos & B_MASK);
uint32_t chunk = audio_MIN(B_SIZE - start, to_transfer);
@@ -225,22 +231,15 @@ static void hda_audio_input_timer(void *opaque) {
atomic_fetch_add(&st->rpos, chunk);
}
-#undef B_MASK
-#undef B_SIZE
-
- out_timer:
+out_timer:
if (st->running) {
timer_mod_anticipate_ns(st->buft, now + HDA_TIMER_TICKS);
}
}
-
static void hda_audio_input_cb(void *opaque, int avail)
{
-#define B_SIZE sizeof(st->buf)
-#define B_MASK (sizeof(st->buf) - 1)
-
HDAAudioStream *st = opaque;
int64_t wpos = atomic_fetch_add(&st->wpos, 0);
@@ -248,16 +247,7 @@ static void hda_audio_input_cb(void *opaque, int avail)
int64_t to_transfer = audio_MIN(B_SIZE - (wpos - rpos), avail);
-
- int64_t c = (wpos - rpos) + to_transfer - (B_SIZE >> 1);
- int64_t corr = NANOSECONDS_PER_SECOND * c / (4 * st->as.freq);
- if (corr > MAX_CORR) {
- corr = MAX_CORR;
- } else if (corr < -MAX_CORR) {
- corr = -MAX_CORR;
- }
- //dolog("CORR %"PRId64"\n", -corr);
- atomic_fetch_add(&st->buft_start, -corr);
+ hda_timer_sync_adjust(st, -((wpos - rpos) + to_transfer - (B_SIZE >> 1)));
while (to_transfer) {
uint32_t start = (uint32_t) (wpos & B_MASK);
@@ -270,18 +260,10 @@ static void hda_audio_input_cb(void *opaque, int avail)
break;
}
}
-
-#undef B_MASK
-#undef B_SIZE
}
-
-
-static void hda_audio_output_timer(void *opaque) {
-
-#define B_SIZE sizeof(st->buf)
-#define B_MASK (sizeof(st->buf) - 1)
-
+static void hda_audio_output_timer(void *opaque)
+{
HDAAudioStream *st = opaque;
int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
@@ -290,7 +272,7 @@ static void hda_audio_output_timer(void *opaque) {
int64_t wpos = atomic_fetch_add(&st->wpos, 0);
int64_t rpos = atomic_fetch_add(&st->rpos, 0);
- int64_t wanted_wpos = (st->as.freq * 4 * (now - buft_start)) / NANOSECONDS_PER_SECOND;
+ int64_t wanted_wpos = hda_bytes_per_second(st) * (now - buft_start) / NANOSECONDS_PER_SECOND;
wanted_wpos &= -4; // IMPORTANT! clip to frames
if (wanted_wpos <= wpos) {
@@ -298,13 +280,6 @@ static void hda_audio_output_timer(void *opaque) {
goto out_timer;
}
- if (wpos - rpos >= B_SIZE) {
- goto out_timer;
- }
-
- //dolog("%"PRId64"\n", wpos - rpos);
-
- //dolog("rpos: %"PRId64", wpos: %"PRId64", wanted: %"PRId64"\n", rpos, wpos, wanted_wpos);
int64_t to_transfer = audio_MIN(B_SIZE - (wpos - rpos), wanted_wpos - wpos);
while (to_transfer) {
uint32_t start = (wpos & B_MASK);
@@ -318,10 +293,7 @@ static void hda_audio_output_timer(void *opaque) {
atomic_fetch_add(&st->wpos, chunk);
}
-#undef B_MASK
-#undef B_SIZE
-
- out_timer:
+out_timer:
if (st->running) {
timer_mod_anticipate_ns(st->buft, now + HDA_TIMER_TICKS);
@@ -330,9 +302,6 @@ static void hda_audio_output_timer(void *opaque) {
static void hda_audio_output_cb(void *opaque, int avail)
{
-#define B_SIZE sizeof(st->buf)
-#define B_MASK (sizeof(st->buf) - 1)
-
HDAAudioStream *st = opaque;
int64_t wpos = atomic_fetch_add(&st->wpos, 0);
@@ -340,16 +309,7 @@ static void hda_audio_output_cb(void *opaque, int avail)
int64_t to_transfer = audio_MIN(wpos - rpos, avail);
-
- int64_t c = (wpos - rpos) - to_transfer - (B_SIZE >> 1);
- int64_t corr = NANOSECONDS_PER_SECOND * c / (4 * st->as.freq);
- if (corr > MAX_CORR) {
- corr = MAX_CORR;
- } else if (corr < -MAX_CORR) {
- corr = -MAX_CORR;
- }
- //dolog("CORR %"PRId64"\n", corr);
- atomic_fetch_add(&st->buft_start, corr);
+ hda_timer_sync_adjust(st, (wpos - rpos) - to_transfer - (B_SIZE >> 1));
while (to_transfer) {
uint32_t start = (uint32_t) (rpos & B_MASK);
@@ -362,9 +322,6 @@ static void hda_audio_output_cb(void *opaque, int avail)
break;
}
}
-
-#undef B_MASK
-#undef B_SIZE
}
static void hda_audio_set_running(HDAAudioStream *st, bool running)
@@ -376,7 +333,8 @@ static void hda_audio_set_running(HDAAudioStream *st, bool running)
return;
}
st->running = running;
-
+ dprint(st->state, 1, "%s: %s (stream %d)\n", st->node->name,
+ st->running ? "on" : "off", st->stream);
if (running) {
int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
st->rpos = 0;
@@ -384,11 +342,8 @@ static void hda_audio_set_running(HDAAudioStream *st, bool running)
st->buft_start = now;
timer_mod_anticipate_ns(st->buft, now + HDA_TIMER_TICKS);
} else {
- timer_del (st->buft);
+ timer_del(st->buft);
}
-
- dprint(st->state, 1, "%s: %s (stream %d)\n", st->node->name,
- st->running ? "on" : "off", st->stream);
if (st->output) {
AUD_set_active_out(st->voice.out, st->running);
} else {
@@ -683,8 +638,8 @@ static void hda_audio_exit(HDACodecDevice *hda)
if (st->node == NULL) {
continue;
}
+ timer_del(st->buft);
if (st->output) {
- timer_del (st->buft);
AUD_close_out(&a->card, st->voice.out);
} else {
AUD_close_in(&a->card, st->voice.in);
diff --git a/hw/audio/intel-hda.c b/hw/audio/intel-hda.c
index 9d3da3185b..721eba792d 100644
--- a/hw/audio/intel-hda.c
+++ b/hw/audio/intel-hda.c
@@ -407,13 +407,6 @@ static bool intel_hda_xfer(HDACodecDevice *dev, uint32_t stnr, bool output,
if (st->bpl == NULL) {
return false;
}
-// if (st->ctl & (1 << 26)) {
-// /*
-// * Wait with the next DMA xfer until the guest
-// * has acked the buffer completion interrupt
-// */
-// return false;
-// }
left = len;
s = st->bentries;
--
2.14.1
From 0c2e3ea755f205b3060b8d93987766c8268293f6 Mon Sep 17 00:00:00 2001
From: Martin Schrodt <martin@schrodt.org>
Date: Sun, 15 Oct 2017 13:53:50 +0200
Subject: [PATCH 16/30] cleanup PA driver code
---
audio/paaudio.c | 153 ++++++++++++++++++++++++--------------------------------
1 file changed, 66 insertions(+), 87 deletions(-)
diff --git a/audio/paaudio.c b/audio/paaudio.c
index 55dd5d755d..4f6fb4965c 100644
--- a/audio/paaudio.c
+++ b/audio/paaudio.c
@@ -1,16 +1,16 @@
/* public domain */
#include "qemu/osdep.h"
+#include "qemu/timer.h"
#include "audio.h"
-
#include <pulse/pulseaudio.h>
-#include <include/qemu/timer.h>
#define AUDIO_CAP "pulseaudio"
#include "audio_int.h"
typedef struct {
- int buffer_size;
+ int buffer_size_out;
+ int buffer_size_in;
int tlength;
int fragsize;
int adjust_latency_out;
@@ -42,7 +42,6 @@ typedef struct {
pa_buffer_attr ba;
} PAVoiceIn;
-
static void qpa_audio_fini(void *opaque);
static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...)
@@ -79,25 +78,30 @@ static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x)
#define CHECK_SUCCESS_GOTO(c, rerror, expression, label) \
do { \
if (!(expression)) { \
- *(rerror) = pa_context_errno ((c)->context); \
+ if (rerror) { \
+ *(rerror) = pa_context_errno ((c)->context); \
+ } \
goto label; \
} \
} while (0);
-
#define CHECK_DEAD_GOTO(c, stream, rerror, label) \
do { \
if (!(c)->context || !PA_CONTEXT_IS_GOOD (pa_context_get_state((c)->context)) || \
!(stream) || !PA_STREAM_IS_GOOD (pa_stream_get_state ((stream)))) { \
if (((c)->context && pa_context_get_state ((c)->context) == PA_CONTEXT_FAILED) || \
((stream) && pa_stream_get_state ((stream)) == PA_STREAM_FAILED)) { \
- *(rerror) = pa_context_errno ((c)->context); \
+ if (rerror) { \
+ *(rerror) = pa_context_errno ((c)->context); \
+ } \
} else { \
- *(rerror) = PA_ERR_BADSTATE; \
+ if (rerror) { \
+ *(rerror) = PA_ERR_BADSTATE; \
+ } \
} \
goto label; \
} \
-} while (0);
+ } while (0);
static int qpa_run_out (HWVoiceOut *hw, int live)
{
@@ -112,36 +116,30 @@ static int qpa_run_out (HWVoiceOut *hw, int live)
decr = 0;
rpos = hw->rpos;
- pa_threaded_mainloop_lock (pa->g->mainloop);
- CHECK_DEAD_GOTO (pa->g, pa->stream, &error, fail);
+ pa_threaded_mainloop_lock(pa->g->mainloop);
+ CHECK_DEAD_GOTO(pa->g, pa->stream, &error, fail);
avail_bytes = (size_t) live << hw->info.shift;
+
max_bytes = pa_stream_writable_size(pa->stream);
CHECK_SUCCESS_GOTO(pa->g, &error, max_bytes != -1, fail);
- samples = (int)(audio_MIN (avail_bytes, max_bytes)) >> hw->info.shift;
-
-// if (avail_bytes < max_bytes) {
-// dolog("avail: %d, wanted: %d \n", (int)avail_bytes, (int)max_bytes);
-// }
-
-// dolog("TRANSFER avail: %d bytes, max %d bytes -> %d samples from %d\n", (int)avail_bytes, (int)max_bytes, samples, rpos);
-
+ samples = (int)(audio_MIN(avail_bytes, max_bytes)) >> hw->info.shift;
while (samples) {
int left_till_end_samples = hw->samples - rpos;
- int convert_samples = audio_MIN (samples, left_till_end_samples);
+ int convert_samples = audio_MIN(samples, left_till_end_samples);
size_t convert_bytes_wanted = (size_t) convert_samples << hw->info.shift;
size_t convert_bytes = convert_bytes_wanted;
r = pa_stream_begin_write(pa->stream, &pa_dst, &convert_bytes);
CHECK_SUCCESS_GOTO(pa->g, &error, r == 0, fail);
- CHECK_SUCCESS_GOTO(pa->g, &error, convert_bytes == convert_bytes_wanted, fail);
+ CHECK_SUCCESS_GOTO(pa->g, (int *)0, convert_bytes == convert_bytes_wanted, fail);
src = hw->mix_buf + rpos;
- hw->clip (pa_dst, src, convert_samples);
+ hw->clip(pa_dst, src, convert_samples);
- r = pa_stream_write (pa->stream, pa_dst, convert_bytes, NULL, 0LL, PA_SEEK_RELATIVE);
+ r = pa_stream_write(pa->stream, pa_dst, convert_bytes, NULL, 0LL, PA_SEEK_RELATIVE);
CHECK_SUCCESS_GOTO(pa->g, &error, r >= 0, fail);
rpos = (rpos + convert_samples) % hw->samples;
@@ -149,10 +147,10 @@ static int qpa_run_out (HWVoiceOut *hw, int live)
decr += convert_samples;
}
- bail:
- pa_threaded_mainloop_unlock (pa->g->mainloop);
+bail:
+ pa_threaded_mainloop_unlock(pa->g->mainloop);
- hw->rpos = rpos;
+hw->rpos = rpos;
return decr;
fail:
@@ -172,12 +170,12 @@ static int qpa_run_in (HWVoiceIn *hw)
char *pa_src;
int error = 0;
int r;
-
+ size_t pa_avail;
incr = 0;
wpos = hw->wpos;
- pa_threaded_mainloop_lock (pa->g->mainloop);
- CHECK_DEAD_GOTO (pa->g, pa->stream, &error, fail);
+ pa_threaded_mainloop_lock(pa->g->mainloop);
+ CHECK_DEAD_GOTO(pa->g, pa->stream, &error, fail);
size_t bytes_wanted = ((unsigned int)(hw->samples - audio_pcm_hw_get_live_in(hw)) << hw->info.shift);
if (bytes_wanted == 0) {
@@ -187,35 +185,14 @@ static int qpa_run_in (HWVoiceIn *hw)
size_t bytes_avail = pa_stream_readable_size(pa->stream);
- //dolog("WANT %d, HAVE %d\n", (int)bytes_wanted, (int) bytes_avail);
-
- size_t pa_avail;
-
- if (bytes_avail > bytes_wanted) {
-#if 0
- size_t to_drop = bytes_avail - bytes_wanted;
- while (to_drop) {
- r = pa_stream_peek(pa->stream, (const void **)&pa_src, &pa_avail);
- CHECK_SUCCESS_GOTO(pa->g, &error, r == 0, fail);
- if (to_drop < pa_avail) {
- break;
- }
- r = pa_stream_drop(pa->stream);
- CHECK_SUCCESS_GOTO(pa->g, &error, r == 0, fail);
- to_drop -= pa_avail;
- }
- int n_dropped = (int)(bytes_avail - bytes_wanted - to_drop);
- if(n_dropped) {
- dolog("dropped %d bytes\n", n_dropped);
- }
-#endif
- } else if (bytes_wanted < bytes_avail) {
+ if (bytes_wanted > bytes_avail) {
bytes_wanted = bytes_avail;
}
while (bytes_wanted) {
r = pa_stream_peek(pa->stream, (const void **)&pa_src, &pa_avail);
CHECK_SUCCESS_GOTO(pa->g, &error, r == 0, fail);
+
if (pa_avail == 0 || pa_avail > bytes_wanted) {
break;
}
@@ -223,8 +200,8 @@ static int qpa_run_in (HWVoiceIn *hw)
bytes_wanted -= pa_avail;
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);
+ 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;
pa_src += chunk << hw->info.shift;
pa_avail -= chunk << hw->info.shift;
@@ -235,16 +212,15 @@ static int qpa_run_in (HWVoiceIn *hw)
CHECK_SUCCESS_GOTO(pa->g, &error, r == 0, fail);
}
- bail:
- pa_threaded_mainloop_unlock (pa->g->mainloop);
+bail:
+ pa_threaded_mainloop_unlock(pa->g->mainloop);
hw->wpos = wpos;
return incr;
- fail:
+fail:
qpa_logerr (error, "qpa_run_in failed\n");
goto bail;
-
}
static int qpa_read (SWVoiceIn *sw, void *buf, int len)
@@ -391,7 +367,6 @@ fail:
return NULL;
}
-
static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
void *drv_opaque)
{
@@ -407,7 +382,7 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
if (tlength == 0) {
tlength = (frames_per_tick_x1000) / 400;
}
- int64_t buflen = g->conf.buffer_size;
+ int64_t buflen = g->conf.buffer_size_out;
if (buflen == 0) {
buflen = frames_per_tick_x1000 / 400;
}
@@ -440,15 +415,15 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
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
- );
+ 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;
@@ -459,13 +434,13 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
return 0;
- fail1:
+fail1:
return -1;
}
static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as,
- void *drv_opaque)
+ void *drv_opaque)
{
int error;
struct audsettings obt_as = *as;
@@ -479,7 +454,7 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as,
if (fragsize == 0) {
fragsize = frames_per_tick_x1000 / 2500;
}
- int64_t buflen = g->conf.buffer_size;
+ int64_t buflen = g->conf.buffer_size_in;
if (buflen == 0) {
buflen = frames_per_tick_x1000 / 400;
}
@@ -501,7 +476,7 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as,
pa->ss.rate = as->freq;
pa->ba.fragsize = fragsize * pa_frame_size (&pa->ss);
- pa->ba.maxlength = pa->ba.fragsize * 10;
+ pa->ba.maxlength = pa->ba.fragsize * 4;
pa->ba.minreq = -1;
pa->ba.prebuf = -1;
@@ -518,7 +493,7 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as,
&error
);
if (!pa->stream) {
- qpa_logerr (error, "pa_simple_new for playback failed\n");
+ qpa_logerr (error, "pa_simple_new for capture failed\n");
goto fail1;
}
@@ -527,11 +502,10 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as,
return 0;
- fail1:
+fail1:
return -1;
}
-
static void qpa_fini_out (HWVoiceOut *hw)
{
PAVoiceOut *pa = (PAVoiceOut *) hw;
@@ -616,7 +590,7 @@ static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
#endif
switch (cmd) {
- case VOICE_VOLUME:
+ case VOICE_VOLUME:
{
SWVoiceIn *sw;
va_list ap;
@@ -632,8 +606,8 @@ static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
pa_threaded_mainloop_lock (g->mainloop);
op = pa_context_set_source_output_volume (g->context,
- pa_stream_get_index (pa->stream),
- &v, NULL, NULL);
+ pa_stream_get_index (pa->stream),
+ &v, NULL, NULL);
if (!op) {
qpa_logerr (pa_context_errno (g->context),
"set_source_output_volume() failed\n");
@@ -642,8 +616,8 @@ static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
}
op = pa_context_set_source_output_mute (g->context,
- pa_stream_get_index (pa->stream),
- sw->vol.mute, NULL, NULL);
+ 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");
@@ -665,7 +639,6 @@ static PAConf glob_conf = {
#endif
};
-
static void *qpa_audio_init (void)
{
paaudio *g = g_malloc(sizeof(paaudio));
@@ -751,10 +724,16 @@ static void qpa_audio_fini (void *opaque)
struct audio_option qpa_options[] = {
{
- .name = "INT_BUF_SIZE",
+ .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,
- .descr = "internal buffer size in frames"
+ .valp = &glob_conf.buffer_size_in,
+ .descr = "internal buffer size in frames for recording device"
},
{
.name = "TLENGTH",
@@ -772,13 +751,13 @@ struct audio_option qpa_options[] = {
.name = "ADJUST_LATENCY_OUT",
.tag = AUD_OPT_BOOL,
.valp = &glob_conf.adjust_latency_out,
- .descr = "let PA adjust latency for playback device"
+ .descr = "instruct PA to adjust latency for playback device"
},
{
.name = "ADJUST_LATENCY_IN",
.tag = AUD_OPT_BOOL,
.valp = &glob_conf.adjust_latency_in,
- .descr = "let PA adjust latency for recording device"
+ .descr = "instruct PA to adjust latency for recording device"
},
{
.name = "SERVER",
--
2.14.1
From 54da26898dfddfdfab1cd9dcb09ec8e877d33208 Mon Sep 17 00:00:00 2001
From: Martin Schrodt <martin@schrodt.org>
Date: Sun, 15 Oct 2017 14:46:52 +0200
Subject: [PATCH 17/30] expose maxlength parameter for recording device
---
audio/paaudio.c | 33 +++++++++++++++++++++++++--------
1 file changed, 25 insertions(+), 8 deletions(-)
diff --git a/audio/paaudio.c b/audio/paaudio.c
index 4f6fb4965c..06f04b02b7 100644
--- a/audio/paaudio.c
+++ b/audio/paaudio.c
@@ -13,6 +13,7 @@ typedef struct {
int buffer_size_in;
int tlength;
int fragsize;
+ int maxlength_in;
int adjust_latency_out;
int adjust_latency_in;
char *server;
@@ -111,18 +112,19 @@ static int qpa_run_out (HWVoiceOut *hw, int live)
struct st_sample *src;
void *pa_dst;
int error = 0;
+ int *rerror = &error;
int r;
decr = 0;
rpos = hw->rpos;
pa_threaded_mainloop_lock(pa->g->mainloop);
- CHECK_DEAD_GOTO(pa->g, pa->stream, &error, fail);
+ CHECK_DEAD_GOTO(pa->g, pa->stream, rerror, fail);
avail_bytes = (size_t) live << hw->info.shift;
max_bytes = pa_stream_writable_size(pa->stream);
- CHECK_SUCCESS_GOTO(pa->g, &error, max_bytes != -1, fail);
+ CHECK_SUCCESS_GOTO(pa->g, rerror, max_bytes != -1, fail);
samples = (int)(audio_MIN(avail_bytes, max_bytes)) >> hw->info.shift;
while (samples) {
@@ -133,14 +135,14 @@ static int qpa_run_out (HWVoiceOut *hw, int live)
size_t convert_bytes = convert_bytes_wanted;
r = pa_stream_begin_write(pa->stream, &pa_dst, &convert_bytes);
- CHECK_SUCCESS_GOTO(pa->g, &error, r == 0, fail);
+ CHECK_SUCCESS_GOTO(pa->g, rerror, r == 0, fail);
CHECK_SUCCESS_GOTO(pa->g, (int *)0, convert_bytes == convert_bytes_wanted, fail);
src = hw->mix_buf + rpos;
hw->clip(pa_dst, src, convert_samples);
r = pa_stream_write(pa->stream, pa_dst, convert_bytes, NULL, 0LL, PA_SEEK_RELATIVE);
- CHECK_SUCCESS_GOTO(pa->g, &error, r >= 0, fail);
+ CHECK_SUCCESS_GOTO(pa->g, rerror, r >= 0, fail);
rpos = (rpos + convert_samples) % hw->samples;
samples -= convert_samples;
@@ -169,13 +171,14 @@ static int qpa_run_in (HWVoiceIn *hw)
int wpos, incr;
char *pa_src;
int error = 0;
+ int *rerror = &error;
int r;
size_t pa_avail;
incr = 0;
wpos = hw->wpos;
pa_threaded_mainloop_lock(pa->g->mainloop);
- CHECK_DEAD_GOTO(pa->g, pa->stream, &error, fail);
+ CHECK_DEAD_GOTO(pa->g, pa->stream, rerror, fail);
size_t bytes_wanted = ((unsigned int)(hw->samples - audio_pcm_hw_get_live_in(hw)) << hw->info.shift);
if (bytes_wanted == 0) {
@@ -191,7 +194,7 @@ static int qpa_run_in (HWVoiceIn *hw)
while (bytes_wanted) {
r = pa_stream_peek(pa->stream, (const void **)&pa_src, &pa_avail);
- CHECK_SUCCESS_GOTO(pa->g, &error, r == 0, fail);
+ CHECK_SUCCESS_GOTO(pa->g, rerror, r == 0, fail);
if (pa_avail == 0 || pa_avail > bytes_wanted) {
break;
@@ -209,7 +212,7 @@ static int qpa_run_in (HWVoiceIn *hw)
}
r = pa_stream_drop(pa->stream);
- CHECK_SUCCESS_GOTO(pa->g, &error, r == 0, fail);
+ CHECK_SUCCESS_GOTO(pa->g, rerror, r == 0, fail);
}
bail:
@@ -458,6 +461,10 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as,
if (buflen == 0) {
buflen = frames_per_tick_x1000 / 400;
}
+ int64_t maxlength = g->conf.maxlength_in;
+ if (maxlength == 0) {
+ maxlength = fragsize * 4;
+ }
float ms_per_frame = 1000.0f / as->freq;
@@ -469,6 +476,10 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as,
fragsize * ms_per_frame,
fragsize);
+ dolog("IN maxlength: %.2f ms (%"PRId64" frames)\n",
+ maxlength * ms_per_frame,
+ maxlength);
+
dolog("IN adjust latency: %s\n", g->conf.adjust_latency_in ? "yes" : "no");
pa->ss.format = audfmt_to_pa (as->fmt, as->endianness);
@@ -476,7 +487,7 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as,
pa->ss.rate = as->freq;
pa->ba.fragsize = fragsize * pa_frame_size (&pa->ss);
- pa->ba.maxlength = pa->ba.fragsize * 4;
+ pa->ba.maxlength = maxlength * pa_frame_size (&pa->ss);
pa->ba.minreq = -1;
pa->ba.prebuf = -1;
@@ -747,6 +758,12 @@ struct audio_option qpa_options[] = {
.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,
--
2.14.1
From 3497c07ef85b94f5248bcd271391b67eb5570f8b Mon Sep 17 00:00:00 2001
From: Martin Schrodt <martin@schrodt.org>
Date: Sun, 15 Oct 2017 14:51:08 +0200
Subject: [PATCH 18/30] do not change unrelated code
---
audio/paaudio.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/audio/paaudio.c b/audio/paaudio.c
index 06f04b02b7..9e454cb9ce 100644
--- a/audio/paaudio.c
+++ b/audio/paaudio.c
@@ -233,7 +233,7 @@ static int qpa_read (SWVoiceIn *sw, void *buf, int len)
static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness)
{
- pa_sample_format_t format;
+ int format;
switch (afmt) {
case AUD_FMT_S8:
--
2.14.1
From 73bf70f6c67c7d6b1ff87dbe1c86091721006d32 Mon Sep 17 00:00:00 2001
From: Martin Schrodt <martin@schrodt.org>
Date: Sun, 15 Oct 2017 15:35:38 +0200
Subject: [PATCH 19/30] ignore old fields in live migration
---
hw/audio/hda-codec.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c
index af686eb1f3..7976c4dc93 100644
--- a/hw/audio/hda-codec.c
+++ b/hw/audio/hda-codec.c
@@ -159,6 +159,8 @@ struct HDAAudioStream {
SWVoiceIn *in;
SWVoiceOut *out;
} voice;
+ uint8_t compat_buf[HDA_BUFFER_SIZE];
+ uint32_t compat_bpos;
uint8_t buf[8192]; // size must be power of two
int64_t rpos;
int64_t wpos;
@@ -690,7 +692,7 @@ static void hda_audio_reset(DeviceState *dev)
static const VMStateDescription vmstate_hda_audio_stream = {
.name = "hda-audio-stream",
- .version_id = 1,
+ .version_id = 2,
.fields = (VMStateField[]) {
VMSTATE_UINT32(stream, HDAAudioStream),
VMSTATE_UINT32(channel, HDAAudioStream),
@@ -699,9 +701,8 @@ static const VMStateDescription vmstate_hda_audio_stream = {
VMSTATE_UINT32(gain_right, HDAAudioStream),
VMSTATE_BOOL(mute_left, HDAAudioStream),
VMSTATE_BOOL(mute_right, HDAAudioStream),
- VMSTATE_BUFFER(buf, HDAAudioStream),
- VMSTATE_INT64(rpos, HDAAudioStream),
- VMSTATE_INT64(wpos, HDAAudioStream),
+ VMSTATE_UINT32(compat_bpos, HDAAudioStream),
+ VMSTATE_BUFFER(compat_buf, HDAAudioStream),
VMSTATE_END_OF_LIST()
}
};
--
2.14.1
From 4053b7b1ee100c556dbe7e276890b80bb41b5061 Mon Sep 17 00:00:00 2001
From: Martin Schrodt <martin@schrodt.org>
Date: Sun, 15 Oct 2017 16:02:09 +0200
Subject: [PATCH 20/30] move HDA_TIMER_TICKS to codec
---
hw/audio/hda-codec.c | 1 +
hw/audio/intel-hda-defs.h | 1 -
2 files changed, 1 insertion(+), 1 deletion(-)
diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c
index 7976c4dc93..d8faa6fd6a 100644
--- a/hw/audio/hda-codec.c
+++ b/hw/audio/hda-codec.c
@@ -127,6 +127,7 @@ static void hda_codec_parse_fmt(uint32_t format, struct audsettings *as)
#define PARAM nomixemu
#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)
diff --git a/hw/audio/intel-hda-defs.h b/hw/audio/intel-hda-defs.h
index 900a2695ec..2e37e5b874 100644
--- a/hw/audio/intel-hda-defs.h
+++ b/hw/audio/intel-hda-defs.h
@@ -3,7 +3,6 @@
/* qemu */
#define HDA_BUFFER_SIZE 256
-#define HDA_TIMER_TICKS (SCALE_MS)
/* --------------------------------------------------------------------- */
/* from linux/sound/pci/hda/hda_intel.c */
--
2.14.1
From 48ea11bce21d574117ca5bdbf0cd70c9dea47df4 Mon Sep 17 00:00:00 2001
From: Martin Schrodt <martin@schrodt.org>
Date: Sun, 15 Oct 2017 16:06:04 +0200
Subject: [PATCH 21/30] do not up version
---
hw/audio/hda-codec.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c
index d8faa6fd6a..1c0474543d 100644
--- a/hw/audio/hda-codec.c
+++ b/hw/audio/hda-codec.c
@@ -693,7 +693,7 @@ static void hda_audio_reset(DeviceState *dev)
static const VMStateDescription vmstate_hda_audio_stream = {
.name = "hda-audio-stream",
- .version_id = 2,
+ .version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32(stream, HDAAudioStream),
VMSTATE_UINT32(channel, HDAAudioStream),
--
2.14.1
From ba8c2fb55cb7c010d38604c80b3d0102fd9aef84 Mon Sep 17 00:00:00 2001
From: Martin Schrodt <martin@schrodt.org>
Date: Sun, 15 Oct 2017 16:38:17 +0200
Subject: [PATCH 22/30] debug output only when it's enabled
---
audio/paaudio.c | 32 ++++++++++++++------------------
1 file changed, 14 insertions(+), 18 deletions(-)
diff --git a/audio/paaudio.c b/audio/paaudio.c
index 9e454cb9ce..3d24712483 100644
--- a/audio/paaudio.c
+++ b/audio/paaudio.c
@@ -390,21 +390,19 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
buflen = frames_per_tick_x1000 / 400;
}
- float ms_per_frame = 1000.0f / as->freq;
-
- dolog("tick duration: %.2f ms (%.3f frames)\n",
- ((float) timer_tick_duration) / SCALE_MS,
+ ldebug("tick duration: %.2f ms (%.3f frames)\n",
+ ((float)timer_tick_duration) / SCALE_MS,
(float)frames_per_tick_x1000 / 1000.0f);
- dolog("OUT internal buffer: %.2f ms (%"PRId64" frames)\n",
- buflen * ms_per_frame,
+ ldebug("OUT internal buffer: %.2f ms (%"PRId64" frames)\n",
+ buflen * (1000.0f / as->freq),
buflen);
- dolog("OUT tlength: %.2f ms (%"PRId64" frames)\n",
- tlength * ms_per_frame,
+ ldebug("OUT tlength: %.2f ms (%"PRId64" frames)\n",
+ tlength * (1000.0f / as->freq),
tlength);
- dolog("OUT adjust latency: %s\n", g->conf.adjust_latency_out ? "yes" : "no");
+ 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;
@@ -466,21 +464,19 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as,
maxlength = fragsize * 4;
}
- float ms_per_frame = 1000.0f / as->freq;
-
- dolog("IN internal buffer: %.2f ms (%"PRId64" frames)\n",
- buflen * ms_per_frame,
+ ldebug("IN internal buffer: %.2f ms (%"PRId64" frames)\n",
+ buflen * (1000.0f / as->freq),
buflen);
- dolog("IN fragsize: %.2f ms (%"PRId64" frames)\n",
- fragsize * ms_per_frame,
+ ldebug("IN fragsize: %.2f ms (%"PRId64" frames)\n",
+ fragsize * (1000.0f / as->freq),
fragsize);
- dolog("IN maxlength: %.2f ms (%"PRId64" frames)\n",
- maxlength * ms_per_frame,
+ ldebug("IN maxlength: %.2f ms (%"PRId64" frames)\n",
+ maxlength * (1000.0f / as->freq),
maxlength);
- dolog("IN adjust latency: %s\n", g->conf.adjust_latency_in ? "yes" : "no");
+ 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;
--
2.14.1
From 5639f1869a027ffb30fb94afdd7c215cbc06fa0c Mon Sep 17 00:00:00 2001
From: Martin Schrodt <martin@schrodt.org>
Date: Sun, 15 Oct 2017 17:46:03 +0200
Subject: [PATCH 23/30] checkpatch cleanup 1
Signed-off-by: Martin Schrodt <martin@schrodt.org>
---
audio/audio.c | 3 +-
audio/paaudio.c | 366 ++++++++++++++++++++++++++++----------------------------
2 files changed, 185 insertions(+), 184 deletions(-)
diff --git a/audio/audio.c b/audio/audio.c
index fba1604c34..6f42a019b0 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -2067,6 +2067,7 @@ void AUD_set_volume_in (SWVoiceIn *sw, int mute, uint8_t lvol, uint8_t rvol)
}
}
-int64_t audio_get_timer_ticks(void) {
+int64_t audio_get_timer_ticks(void)
+{
return conf.period.ticks;
}
diff --git a/audio/paaudio.c b/audio/paaudio.c
index 3d24712483..b180d213cb 100644
--- a/audio/paaudio.c
+++ b/audio/paaudio.c
@@ -104,7 +104,7 @@ static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x)
} \
} while (0);
-static int qpa_run_out (HWVoiceOut *hw, int live)
+static int qpa_run_out(HWVoiceOut *hw, int live)
{
PAVoiceOut *pa = (PAVoiceOut *) hw;
int rpos, decr, samples;
@@ -149,14 +149,14 @@ static int qpa_run_out (HWVoiceOut *hw, int live)
decr += convert_samples;
}
-bail:
+ bail:
pa_threaded_mainloop_unlock(pa->g->mainloop);
-hw->rpos = rpos;
+ hw->rpos = rpos;
return decr;
fail:
- qpa_logerr (error, "qpa_run_out failed\n");
+ qpa_logerr(error, "qpa_run_out failed\n");
goto bail;
}
@@ -165,7 +165,7 @@ static int qpa_write (SWVoiceOut *sw, void *buf, int len)
return audio_pcm_sw_write (sw, buf, len);
}
-static int qpa_run_in (HWVoiceIn *hw)
+static int qpa_run_in(HWVoiceIn *hw)
{
PAVoiceIn *pa = (PAVoiceIn *) hw;
int wpos, incr;
@@ -182,7 +182,7 @@ static int qpa_run_in (HWVoiceIn *hw)
size_t bytes_wanted = ((unsigned int)(hw->samples - audio_pcm_hw_get_live_in(hw)) << hw->info.shift);
if (bytes_wanted == 0) {
- // no room
+ /* no room */
goto bail;
}
@@ -215,14 +215,14 @@ static int qpa_run_in (HWVoiceIn *hw)
CHECK_SUCCESS_GOTO(pa->g, rerror, r == 0, fail);
}
-bail:
+ bail:
pa_threaded_mainloop_unlock(pa->g->mainloop);
hw->wpos = wpos;
return incr;
-fail:
- qpa_logerr (error, "qpa_run_in failed\n");
+ fail:
+ qpa_logerr(error, "qpa_run_in failed\n");
goto bail;
}
@@ -236,22 +236,22 @@ static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness)
int format;
switch (afmt) {
- case AUD_FMT_S8:
- case AUD_FMT_U8:
- format = PA_SAMPLE_U8;
- break;
- case AUD_FMT_S16:
- case AUD_FMT_U16:
- format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE;
- break;
- case AUD_FMT_S32:
- case AUD_FMT_U32:
- format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE;
- break;
- default:
- dolog ("Internal logic error: Bad audio format %d\n", afmt);
- format = PA_SAMPLE_U8;
- break;
+ case AUD_FMT_S8:
+ case AUD_FMT_U8:
+ format = PA_SAMPLE_U8;
+ break;
+ case AUD_FMT_S16:
+ case AUD_FMT_U16:
+ format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE;
+ break;
+ case AUD_FMT_S32:
+ case AUD_FMT_U32:
+ format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE;
+ break;
+ default:
+ dolog ("Internal logic error: Bad audio format %d\n", afmt);
+ format = PA_SAMPLE_U8;
+ break;
}
return format;
}
@@ -259,23 +259,23 @@ static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness)
static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness)
{
switch (fmt) {
- case PA_SAMPLE_U8:
- return AUD_FMT_U8;
- case PA_SAMPLE_S16BE:
- *endianness = 1;
- return AUD_FMT_S16;
- case PA_SAMPLE_S16LE:
- *endianness = 0;
- return AUD_FMT_S16;
- case PA_SAMPLE_S32BE:
- *endianness = 1;
- return AUD_FMT_S32;
- case PA_SAMPLE_S32LE:
- *endianness = 0;
- return AUD_FMT_S32;
- default:
- dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt);
- return AUD_FMT_U8;
+ case PA_SAMPLE_U8:
+ return AUD_FMT_U8;
+ case PA_SAMPLE_S16BE:
+ *endianness = 1;
+ return AUD_FMT_S16;
+ case PA_SAMPLE_S16LE:
+ *endianness = 0;
+ return AUD_FMT_S16;
+ case PA_SAMPLE_S32BE:
+ *endianness = 1;
+ return AUD_FMT_S32;
+ case PA_SAMPLE_S32LE:
+ *endianness = 0;
+ return AUD_FMT_S32;
+ default:
+ dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt);
+ return AUD_FMT_U8;
}
}
@@ -284,17 +284,17 @@ static void context_state_cb (pa_context *c, void *userdata)
paaudio *g = userdata;
switch (pa_context_get_state(c)) {
- case PA_CONTEXT_READY:
- case PA_CONTEXT_TERMINATED:
- case PA_CONTEXT_FAILED:
- pa_threaded_mainloop_signal (g->mainloop, 0);
- break;
-
- case PA_CONTEXT_UNCONNECTED:
- case PA_CONTEXT_CONNECTING:
- case PA_CONTEXT_AUTHORIZING:
- case PA_CONTEXT_SETTING_NAME:
- break;
+ case PA_CONTEXT_READY:
+ case PA_CONTEXT_TERMINATED:
+ case PA_CONTEXT_FAILED:
+ pa_threaded_mainloop_signal (g->mainloop, 0);
+ break;
+
+ case PA_CONTEXT_UNCONNECTED:
+ case PA_CONTEXT_CONNECTING:
+ case PA_CONTEXT_AUTHORIZING:
+ case PA_CONTEXT_SETTING_NAME:
+ break;
}
}
@@ -304,15 +304,15 @@ static void stream_state_cb (pa_stream *s, void * userdata)
switch (pa_stream_get_state (s)) {
- case PA_STREAM_READY:
- case PA_STREAM_FAILED:
- case PA_STREAM_TERMINATED:
- pa_threaded_mainloop_signal (g->mainloop, 0);
- break;
+ case PA_STREAM_READY:
+ case PA_STREAM_FAILED:
+ case PA_STREAM_TERMINATED:
+ pa_threaded_mainloop_signal (g->mainloop, 0);
+ break;
- case PA_STREAM_UNCONNECTED:
- case PA_STREAM_CREATING:
- break;
+ case PA_STREAM_UNCONNECTED:
+ case PA_STREAM_CREATING:
+ break;
}
}
@@ -351,14 +351,14 @@ static pa_stream *qpa_simple_new (
}
if (r < 0) {
- goto fail;
+ goto fail;
}
pa_threaded_mainloop_unlock (g->mainloop);
return stream;
-fail:
+ fail:
pa_threaded_mainloop_unlock (g->mainloop);
if (stream) {
@@ -391,20 +391,20 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
}
ldebug("tick duration: %.2f ms (%.3f frames)\n",
- ((float)timer_tick_duration) / SCALE_MS,
- (float)frames_per_tick_x1000 / 1000.0f);
+ ((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);
+ buflen * (1000.0f / as->freq),
+ buflen);
ldebug("OUT tlength: %.2f ms (%"PRId64" frames)\n",
- tlength * (1000.0f / as->freq),
- tlength);
+ 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.format = audfmt_to_pa(as->fmt, as->endianness);
pa->ss.channels = as->nchannels;
pa->ss.rate = as->freq;
@@ -413,9 +413,9 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
pa->ba.minreq = -1;
pa->ba.prebuf = -1;
- obt_as.fmt = pa_to_audfmt (pa->ss.format, &obt_as.endianness);
+ obt_as.fmt = pa_to_audfmt(pa->ss.format, &obt_as.endianness);
- pa->stream = qpa_simple_new (
+ pa->stream = qpa_simple_new(
g,
"qemu",
PA_STREAM_PLAYBACK,
@@ -430,12 +430,12 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
goto fail1;
}
- audio_pcm_init_info (&hw->info, &obt_as);
+ audio_pcm_init_info(&hw->info, &obt_as);
hw->samples = buflen;
return 0;
-fail1:
+ fail1:
return -1;
}
@@ -465,31 +465,31 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as,
}
ldebug("IN internal buffer: %.2f ms (%"PRId64" frames)\n",
- buflen * (1000.0f / as->freq),
- buflen);
+ buflen * (1000.0f / as->freq),
+ buflen);
ldebug("IN fragsize: %.2f ms (%"PRId64" frames)\n",
- fragsize * (1000.0f / as->freq),
- fragsize);
+ fragsize * (1000.0f / as->freq),
+ fragsize);
ldebug("IN maxlength: %.2f ms (%"PRId64" frames)\n",
- maxlength * (1000.0f / as->freq),
- maxlength);
+ 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.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.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);
+ obt_as.fmt = pa_to_audfmt(pa->ss.format, &obt_as.endianness);
- pa->stream = qpa_simple_new (
+ pa->stream = qpa_simple_new(
g,
"qemu",
PA_STREAM_RECORD,
@@ -504,12 +504,12 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as,
goto fail1;
}
- audio_pcm_init_info (&hw->info, &obt_as);
+ audio_pcm_init_info(&hw->info, &obt_as);
hw->samples = buflen;
return 0;
-fail1:
+ fail1:
return -1;
}
@@ -545,7 +545,7 @@ static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
#endif
switch (cmd) {
- case VOICE_VOLUME:
+ case VOICE_VOLUME:
{
SWVoiceOut *sw;
va_list ap;
@@ -561,8 +561,8 @@ static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
pa_threaded_mainloop_lock (g->mainloop);
op = pa_context_set_sink_input_volume (g->context,
- pa_stream_get_index (pa->stream),
- &v, NULL, NULL);
+ pa_stream_get_index (pa->stream),
+ &v, NULL, NULL);
if (!op)
qpa_logerr (pa_context_errno (g->context),
"set_sink_input_volume() failed\n");
@@ -570,8 +570,8 @@ static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
pa_operation_unref (op);
op = pa_context_set_sink_input_mute (g->context,
- pa_stream_get_index (pa->stream),
- sw->vol.mute, NULL, NULL);
+ pa_stream_get_index (pa->stream),
+ sw->vol.mute, NULL, NULL);
if (!op) {
qpa_logerr (pa_context_errno (g->context),
"set_sink_input_mute() failed\n");
@@ -597,7 +597,7 @@ static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
#endif
switch (cmd) {
- case VOICE_VOLUME:
+ case VOICE_VOLUME:
{
SWVoiceIn *sw;
va_list ap;
@@ -613,8 +613,8 @@ static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
pa_threaded_mainloop_lock (g->mainloop);
op = pa_context_set_source_output_volume (g->context,
- pa_stream_get_index (pa->stream),
- &v, NULL, NULL);
+ pa_stream_get_index (pa->stream),
+ &v, NULL, NULL);
if (!op) {
qpa_logerr (pa_context_errno (g->context),
"set_source_output_volume() failed\n");
@@ -623,8 +623,8 @@ static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
}
op = pa_context_set_source_output_mute (g->context,
- pa_stream_get_index (pa->stream),
- sw->vol.mute, NULL, NULL);
+ 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");
@@ -701,9 +701,9 @@ static void *qpa_audio_init (void)
return g;
-unlock_and_fail:
+ unlock_and_fail:
pa_threaded_mainloop_unlock (g->mainloop);
-fail:
+ fail:
AUD_log (AUDIO_CAP, "Failed to initialize PA context");
qpa_audio_fini(g);
return NULL;
@@ -730,94 +730,94 @@ static void qpa_audio_fini (void *opaque)
}
struct audio_option qpa_options[] = {
- {
- .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.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",
- .tag = AUD_OPT_STR,
- .valp = &glob_conf.server,
- .descr = "server address"
- },
- {
- .name = "SINK",
- .tag = AUD_OPT_STR,
- .valp = &glob_conf.sink,
- .descr = "sink device name"
- },
- {
- .name = "SOURCE",
- .tag = AUD_OPT_STR,
- .valp = &glob_conf.source,
- .descr = "source device name"
- },
- { /* End of list */ }
+ {
+ .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.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",
+ .tag = AUD_OPT_STR,
+ .valp = &glob_conf.server,
+ .descr = "server address"
+ },
+ {
+ .name = "SINK",
+ .tag = AUD_OPT_STR,
+ .valp = &glob_conf.sink,
+ .descr = "sink device name"
+ },
+ {
+ .name = "SOURCE",
+ .tag = AUD_OPT_STR,
+ .valp = &glob_conf.source,
+ .descr = "source device name"
+ },
+ { /* End of list */ }
};
static struct audio_pcm_ops qpa_pcm_ops = {
- .init_out = qpa_init_out,
- .fini_out = qpa_fini_out,
- .run_out = qpa_run_out,
- .write = qpa_write,
- .ctl_out = qpa_ctl_out,
-
- .init_in = qpa_init_in,
- .fini_in = qpa_fini_in,
- .run_in = qpa_run_in,
- .read = qpa_read,
- .ctl_in = qpa_ctl_in
+ .init_out = qpa_init_out,
+ .fini_out = qpa_fini_out,
+ .run_out = qpa_run_out,
+ .write = qpa_write,
+ .ctl_out = qpa_ctl_out,
+
+ .init_in = qpa_init_in,
+ .fini_in = qpa_fini_in,
+ .run_in = qpa_run_in,
+ .read = qpa_read,
+ .ctl_in = qpa_ctl_in
};
struct audio_driver pa_audio_driver = {
- .name = "pa",
- .descr = "http://www.pulseaudio.org/",
- .options = qpa_options,
- .init = qpa_audio_init,
- .fini = qpa_audio_fini,
- .pcm_ops = &qpa_pcm_ops,
- .can_be_default = 1,
- .max_voices_out = INT_MAX,
- .max_voices_in = INT_MAX,
- .voice_size_out = sizeof (PAVoiceOut),
- .voice_size_in = sizeof (PAVoiceIn),
- .ctl_caps = VOICE_VOLUME_CAP
+ .name = "pa",
+ .descr = "http://www.pulseaudio.org/",
+ .options = qpa_options,
+ .init = qpa_audio_init,
+ .fini = qpa_audio_fini,
+ .pcm_ops = &qpa_pcm_ops,
+ .can_be_default = 1,
+ .max_voices_out = INT_MAX,
+ .max_voices_in = INT_MAX,
+ .voice_size_out = sizeof (PAVoiceOut),
+ .voice_size_in = sizeof (PAVoiceIn),
+ .ctl_caps = VOICE_VOLUME_CAP
};
--
2.14.1
From 8a8883bfe94c04a1e693ca0fa506147681c42bc6 Mon Sep 17 00:00:00 2001
From: Martin Schrodt <martin@schrodt.org>
Date: Sun, 15 Oct 2017 18:00:22 +0200
Subject: [PATCH 24/30] checkpatch cleanup 2
Signed-off-by: Martin Schrodt <martin@schrodt.org>
---
audio/paaudio.c | 56 ++++++++++++++++++++++++++++++++------------------------
1 file changed, 32 insertions(+), 24 deletions(-)
diff --git a/audio/paaudio.c b/audio/paaudio.c
index b180d213cb..fbc30fd08e 100644
--- a/audio/paaudio.c
+++ b/audio/paaudio.c
@@ -128,20 +128,19 @@ static int qpa_run_out(HWVoiceOut *hw, int live)
samples = (int)(audio_MIN(avail_bytes, max_bytes)) >> hw->info.shift;
while (samples) {
- int left_till_end_samples = hw->samples - rpos;
+ 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;
- int convert_samples = audio_MIN(samples, left_till_end_samples);
- size_t convert_bytes_wanted = (size_t) convert_samples << hw->info.shift;
- size_t convert_bytes = convert_bytes_wanted;
-
- r = pa_stream_begin_write(pa->stream, &pa_dst, &convert_bytes);
+ 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, convert_bytes == convert_bytes_wanted, fail);
+ CHECK_SUCCESS_GOTO(pa->g, (int *)0, b_effective == b_wanted, fail);
src = hw->mix_buf + rpos;
hw->clip(pa_dst, src, convert_samples);
- r = pa_stream_write(pa->stream, pa_dst, convert_bytes, NULL, 0LL, PA_SEEK_RELATIVE);
+ 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 + convert_samples) % hw->samples;
@@ -180,7 +179,9 @@ static int qpa_run_in(HWVoiceIn *hw)
pa_threaded_mainloop_lock(pa->g->mainloop);
CHECK_DEAD_GOTO(pa->g, pa->stream, rerror, fail);
- size_t bytes_wanted = ((unsigned int)(hw->samples - audio_pcm_hw_get_live_in(hw)) << hw->info.shift);
+ size_t bytes_wanted = ((unsigned int)
+ (hw->samples - audio_pcm_hw_get_live_in(hw)) << hw->info.shift);
+
if (bytes_wanted == 0) {
/* no room */
goto bail;
@@ -203,7 +204,8 @@ static int qpa_run_in(HWVoiceIn *hw)
bytes_wanted -= pa_avail;
while (pa_avail) {
- int chunk = audio_MIN((int)(pa_avail >> hw->info.shift), hw->samples - wpos);
+ 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;
pa_src += chunk << hw->info.shift;
@@ -339,15 +341,15 @@ static pa_stream *qpa_simple_new (
pa_stream_set_state_callback (stream, stream_state_cb, g);
if (dir == PA_STREAM_PLAYBACK) {
- 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);
+ 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
- | (g->conf.adjust_latency_in ? PA_STREAM_ADJUST_LATENCY : 0)
- |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) {
@@ -378,8 +380,10 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
PAVoiceOut *pa = (PAVoiceOut *) hw;
paaudio *g = pa->g = drv_opaque;
- 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 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) {
@@ -402,7 +406,8 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
tlength * (1000.0f / as->freq),
tlength);
- ldebug("OUT adjust latency: %s\n", g->conf.adjust_latency_out ? "yes" : "no");
+ 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;
@@ -448,8 +453,10 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as,
PAVoiceIn *pa = (PAVoiceIn *) hw;
paaudio *g = pa->g = drv_opaque;
- 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 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) {
@@ -476,7 +483,8 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as,
maxlength * (1000.0f / as->freq),
maxlength);
- ldebug("IN adjust latency: %s\n", g->conf.adjust_latency_in ? "yes" : "no");
+ 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;
--
2.14.1
From db0c4353a9c98b357aca93deaac0e07b61475608 Mon Sep 17 00:00:00 2001
From: Martin Schrodt <martin@schrodt.org>
Date: Sun, 15 Oct 2017 18:03:39 +0200
Subject: [PATCH 25/30] checkpatch cleanup 3
Signed-off-by: Martin Schrodt <martin@schrodt.org>
---
hw/audio/hda-codec.c | 23 ++++++++++++++---------
1 file changed, 14 insertions(+), 9 deletions(-)
diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c
index 1c0474543d..38c8eb1561 100644
--- a/hw/audio/hda-codec.c
+++ b/hw/audio/hda-codec.c
@@ -194,7 +194,8 @@ static inline int64_t hda_bytes_per_second(HDAAudioStream *st)
static inline void hda_timer_sync_adjust(HDAAudioStream *st, int64_t target_pos)
{
- int64_t corr = NANOSECONDS_PER_SECOND * target_pos / hda_bytes_per_second(st);
+ int64_t corr =
+ NANOSECONDS_PER_SECOND * target_pos / hda_bytes_per_second(st);
if (corr > MAX_CORR) {
corr = MAX_CORR;
} else if (corr < -MAX_CORR) {
@@ -213,11 +214,12 @@ static void hda_audio_input_timer(void *opaque)
int64_t wpos = atomic_fetch_add(&st->wpos, 0);
int64_t rpos = atomic_fetch_add(&st->rpos, 0);
- int64_t wanted_rpos = hda_bytes_per_second(st) * (now - buft_start) / NANOSECONDS_PER_SECOND;
- wanted_rpos &= -4; // IMPORTANT! clip to frames
+ int64_t wanted_rpos = hda_bytes_per_second(st) * (now - buft_start)
+ / NANOSECONDS_PER_SECOND;
+ wanted_rpos &= -4; /* IMPORTANT! clip to frames */
if (wanted_rpos <= rpos) {
- // we already transmitted the data
+ /* we already transmitted the data */
goto out_timer;
}
@@ -225,7 +227,8 @@ static void hda_audio_input_timer(void *opaque)
while (to_transfer) {
uint32_t start = (rpos & B_MASK);
uint32_t chunk = audio_MIN(B_SIZE - start, to_transfer);
- int rc = hda_codec_xfer(&st->state->hda, st->stream, false, st->buf + start, chunk);
+ int rc = hda_codec_xfer(
+ &st->state->hda, st->stream, false, st->buf + start, chunk);
if (!rc) {
break;
}
@@ -275,11 +278,12 @@ static void hda_audio_output_timer(void *opaque)
int64_t wpos = atomic_fetch_add(&st->wpos, 0);
int64_t rpos = atomic_fetch_add(&st->rpos, 0);
- int64_t wanted_wpos = hda_bytes_per_second(st) * (now - buft_start) / NANOSECONDS_PER_SECOND;
- wanted_wpos &= -4; // IMPORTANT! clip to frames
+ int64_t wanted_wpos = hda_bytes_per_second(st) * (now - buft_start)
+ / NANOSECONDS_PER_SECOND;
+ wanted_wpos &= -4; /* IMPORTANT! clip to frames */
if (wanted_wpos <= wpos) {
- // we already received the data
+ /* we already received the data */
goto out_timer;
}
@@ -287,7 +291,8 @@ static void hda_audio_output_timer(void *opaque)
while (to_transfer) {
uint32_t start = (wpos & B_MASK);
uint32_t chunk = audio_MIN(B_SIZE - start, to_transfer);
- int rc = hda_codec_xfer(&st->state->hda, st->stream, true, st->buf + start, chunk);
+ int rc = hda_codec_xfer(
+ &st->state->hda, st->stream, true, st->buf + start, chunk);
if (!rc) {
break;
}
--
2.14.1
From ad4e7761ba00850e4b0e77fb98b9bdcf6462dadb Mon Sep 17 00:00:00 2001
From: Martin Schrodt <martin@schrodt.org>
Date: Sun, 15 Oct 2017 18:04:13 +0200
Subject: [PATCH 26/30] checkpatch cleanup 4
Signed-off-by: Martin Schrodt <martin@schrodt.org>
---
hw/audio/hda-codec.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c
index 38c8eb1561..ab89158bfc 100644
--- a/hw/audio/hda-codec.c
+++ b/hw/audio/hda-codec.c
@@ -162,7 +162,7 @@ struct HDAAudioStream {
} voice;
uint8_t compat_buf[HDA_BUFFER_SIZE];
uint32_t compat_bpos;
- uint8_t buf[8192]; // size must be power of two
+ uint8_t buf[8192]; /* size must be power of two */
int64_t rpos;
int64_t wpos;
QEMUTimer *buft;
--
2.14.1
From 5f1780c553a85e9b24450c5fcbf581514892729f Mon Sep 17 00:00:00 2001
From: Martin Schrodt <martin@schrodt.org>
Date: Sun, 15 Oct 2017 18:45:49 +0200
Subject: [PATCH 27/30] whitespace...
GODDAMNIT!
---
audio/paaudio.c | 314 ++++++++++++++++++++++++++++----------------------------
1 file changed, 155 insertions(+), 159 deletions(-)
diff --git a/audio/paaudio.c b/audio/paaudio.c
index fbc30fd08e..d3ab82d65d 100644
--- a/audio/paaudio.c
+++ b/audio/paaudio.c
@@ -154,7 +154,7 @@ static int qpa_run_out(HWVoiceOut *hw, int live)
hw->rpos = rpos;
return decr;
- fail:
+fail:
qpa_logerr(error, "qpa_run_out failed\n");
goto bail;
}
@@ -217,13 +217,13 @@ static int qpa_run_in(HWVoiceIn *hw)
CHECK_SUCCESS_GOTO(pa->g, rerror, r == 0, fail);
}
- bail:
+bail:
pa_threaded_mainloop_unlock(pa->g->mainloop);
hw->wpos = wpos;
return incr;
- fail:
+fail:
qpa_logerr(error, "qpa_run_in failed\n");
goto bail;
}
@@ -238,22 +238,22 @@ static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness)
int format;
switch (afmt) {
- case AUD_FMT_S8:
- case AUD_FMT_U8:
- format = PA_SAMPLE_U8;
- break;
- case AUD_FMT_S16:
- case AUD_FMT_U16:
- format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE;
- break;
- case AUD_FMT_S32:
- case AUD_FMT_U32:
- format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE;
- break;
- default:
- dolog ("Internal logic error: Bad audio format %d\n", afmt);
- format = PA_SAMPLE_U8;
- break;
+ case AUD_FMT_S8:
+ case AUD_FMT_U8:
+ format = PA_SAMPLE_U8;
+ break;
+ case AUD_FMT_S16:
+ case AUD_FMT_U16:
+ format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE;
+ break;
+ case AUD_FMT_S32:
+ case AUD_FMT_U32:
+ format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE;
+ break;
+ default:
+ dolog ("Internal logic error: Bad audio format %d\n", afmt);
+ format = PA_SAMPLE_U8;
+ break;
}
return format;
}
@@ -261,23 +261,23 @@ static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness)
static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness)
{
switch (fmt) {
- case PA_SAMPLE_U8:
- return AUD_FMT_U8;
- case PA_SAMPLE_S16BE:
- *endianness = 1;
- return AUD_FMT_S16;
- case PA_SAMPLE_S16LE:
- *endianness = 0;
- return AUD_FMT_S16;
- case PA_SAMPLE_S32BE:
- *endianness = 1;
- return AUD_FMT_S32;
- case PA_SAMPLE_S32LE:
- *endianness = 0;
- return AUD_FMT_S32;
- default:
- dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt);
- return AUD_FMT_U8;
+ case PA_SAMPLE_U8:
+ return AUD_FMT_U8;
+ case PA_SAMPLE_S16BE:
+ *endianness = 1;
+ return AUD_FMT_S16;
+ case PA_SAMPLE_S16LE:
+ *endianness = 0;
+ return AUD_FMT_S16;
+ case PA_SAMPLE_S32BE:
+ *endianness = 1;
+ return AUD_FMT_S32;
+ case PA_SAMPLE_S32LE:
+ *endianness = 0;
+ return AUD_FMT_S32;
+ default:
+ dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt);
+ return AUD_FMT_U8;
}
}
@@ -286,17 +286,17 @@ static void context_state_cb (pa_context *c, void *userdata)
paaudio *g = userdata;
switch (pa_context_get_state(c)) {
- case PA_CONTEXT_READY:
- case PA_CONTEXT_TERMINATED:
- case PA_CONTEXT_FAILED:
- pa_threaded_mainloop_signal (g->mainloop, 0);
- break;
-
- case PA_CONTEXT_UNCONNECTED:
- case PA_CONTEXT_CONNECTING:
- case PA_CONTEXT_AUTHORIZING:
- case PA_CONTEXT_SETTING_NAME:
- break;
+ case PA_CONTEXT_READY:
+ case PA_CONTEXT_TERMINATED:
+ case PA_CONTEXT_FAILED:
+ pa_threaded_mainloop_signal (g->mainloop, 0);
+ break;
+
+ case PA_CONTEXT_UNCONNECTED:
+ case PA_CONTEXT_CONNECTING:
+ case PA_CONTEXT_AUTHORIZING:
+ case PA_CONTEXT_SETTING_NAME:
+ break;
}
}
@@ -306,15 +306,15 @@ static void stream_state_cb (pa_stream *s, void * userdata)
switch (pa_stream_get_state (s)) {
- case PA_STREAM_READY:
- case PA_STREAM_FAILED:
- case PA_STREAM_TERMINATED:
- pa_threaded_mainloop_signal (g->mainloop, 0);
- break;
+ case PA_STREAM_READY:
+ case PA_STREAM_FAILED:
+ case PA_STREAM_TERMINATED:
+ pa_threaded_mainloop_signal (g->mainloop, 0);
+ break;
- case PA_STREAM_UNCONNECTED:
- case PA_STREAM_CREATING:
- break;
+ case PA_STREAM_UNCONNECTED:
+ case PA_STREAM_CREATING:
+ break;
}
}
@@ -353,14 +353,14 @@ static pa_stream *qpa_simple_new (
}
if (r < 0) {
- goto fail;
+ goto fail;
}
pa_threaded_mainloop_unlock (g->mainloop);
return stream;
- fail:
+fail:
pa_threaded_mainloop_unlock (g->mainloop);
if (stream) {
@@ -440,13 +440,11 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
return 0;
- fail1:
+fail1:
return -1;
}
-
-static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as,
- void *drv_opaque)
+static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
{
int error;
struct audsettings obt_as = *as;
@@ -553,7 +551,7 @@ static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
#endif
switch (cmd) {
- case VOICE_VOLUME:
+ case VOICE_VOLUME:
{
SWVoiceOut *sw;
va_list ap;
@@ -569,8 +567,8 @@ static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
pa_threaded_mainloop_lock (g->mainloop);
op = pa_context_set_sink_input_volume (g->context,
- pa_stream_get_index (pa->stream),
- &v, NULL, NULL);
+ pa_stream_get_index (pa->stream),
+ &v, NULL, NULL);
if (!op)
qpa_logerr (pa_context_errno (g->context),
"set_sink_input_volume() failed\n");
@@ -578,8 +576,8 @@ static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
pa_operation_unref (op);
op = pa_context_set_sink_input_mute (g->context,
- pa_stream_get_index (pa->stream),
- sw->vol.mute, NULL, NULL);
+ pa_stream_get_index (pa->stream),
+ sw->vol.mute, NULL, NULL);
if (!op) {
qpa_logerr (pa_context_errno (g->context),
"set_sink_input_mute() failed\n");
@@ -605,7 +603,7 @@ static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
#endif
switch (cmd) {
- case VOICE_VOLUME:
+ case VOICE_VOLUME:
{
SWVoiceIn *sw;
va_list ap;
@@ -621,8 +619,8 @@ static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
pa_threaded_mainloop_lock (g->mainloop);
op = pa_context_set_source_output_volume (g->context,
- pa_stream_get_index (pa->stream),
- &v, NULL, NULL);
+ pa_stream_get_index (pa->stream),
+ &v, NULL, NULL);
if (!op) {
qpa_logerr (pa_context_errno (g->context),
"set_source_output_volume() failed\n");
@@ -631,8 +629,8 @@ static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
}
op = pa_context_set_source_output_mute (g->context,
- pa_stream_get_index (pa->stream),
- sw->vol.mute, NULL, NULL);
+ 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");
@@ -648,10 +646,8 @@ static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
/* common */
static PAConf glob_conf = {
-#ifdef PA_STREAM_ADJUST_LATENCY
.adjust_latency_out = 0,
.adjust_latency_in = 1,
-#endif
};
static void *qpa_audio_init (void)
@@ -709,9 +705,9 @@ static void *qpa_audio_init (void)
return g;
- unlock_and_fail:
+unlock_and_fail:
pa_threaded_mainloop_unlock (g->mainloop);
- fail:
+fail:
AUD_log (AUDIO_CAP, "Failed to initialize PA context");
qpa_audio_fini(g);
return NULL;
@@ -738,94 +734,94 @@ static void qpa_audio_fini (void *opaque)
}
struct audio_option qpa_options[] = {
- {
- .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.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",
- .tag = AUD_OPT_STR,
- .valp = &glob_conf.server,
- .descr = "server address"
- },
- {
- .name = "SINK",
- .tag = AUD_OPT_STR,
- .valp = &glob_conf.sink,
- .descr = "sink device name"
- },
- {
- .name = "SOURCE",
- .tag = AUD_OPT_STR,
- .valp = &glob_conf.source,
- .descr = "source device name"
- },
- { /* End of list */ }
+ {
+ .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.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",
+ .tag = AUD_OPT_STR,
+ .valp = &glob_conf.server,
+ .descr = "server address"
+ },
+ {
+ .name = "SINK",
+ .tag = AUD_OPT_STR,
+ .valp = &glob_conf.sink,
+ .descr = "sink device name"
+ },
+ {
+ .name = "SOURCE",
+ .tag = AUD_OPT_STR,
+ .valp = &glob_conf.source,
+ .descr = "source device name"
+ },
+ { /* End of list */ }
};
static struct audio_pcm_ops qpa_pcm_ops = {
- .init_out = qpa_init_out,
- .fini_out = qpa_fini_out,
- .run_out = qpa_run_out,
- .write = qpa_write,
- .ctl_out = qpa_ctl_out,
-
- .init_in = qpa_init_in,
- .fini_in = qpa_fini_in,
- .run_in = qpa_run_in,
- .read = qpa_read,
- .ctl_in = qpa_ctl_in
+ .init_out = qpa_init_out,
+ .fini_out = qpa_fini_out,
+ .run_out = qpa_run_out,
+ .write = qpa_write,
+ .ctl_out = qpa_ctl_out,
+
+ .init_in = qpa_init_in,
+ .fini_in = qpa_fini_in,
+ .run_in = qpa_run_in,
+ .read = qpa_read,
+ .ctl_in = qpa_ctl_in
};
struct audio_driver pa_audio_driver = {
- .name = "pa",
- .descr = "http://www.pulseaudio.org/",
- .options = qpa_options,
- .init = qpa_audio_init,
- .fini = qpa_audio_fini,
- .pcm_ops = &qpa_pcm_ops,
- .can_be_default = 1,
- .max_voices_out = INT_MAX,
- .max_voices_in = INT_MAX,
- .voice_size_out = sizeof (PAVoiceOut),
- .voice_size_in = sizeof (PAVoiceIn),
- .ctl_caps = VOICE_VOLUME_CAP
+ .name = "pa",
+ .descr = "http://www.pulseaudio.org/",
+ .options = qpa_options,
+ .init = qpa_audio_init,
+ .fini = qpa_audio_fini,
+ .pcm_ops = &qpa_pcm_ops,
+ .can_be_default = 1,
+ .max_voices_out = INT_MAX,
+ .max_voices_in = INT_MAX,
+ .voice_size_out = sizeof (PAVoiceOut),
+ .voice_size_in = sizeof (PAVoiceIn),
+ .ctl_caps = VOICE_VOLUME_CAP
};
--
2.14.1
From 803d51104e6bab61faf1a7833682a0c7a4dbce56 Mon Sep 17 00:00:00 2001
From: Martin Schrodt <martin@schrodt.org>
Date: Sun, 15 Oct 2017 16:44:19 +0200
Subject: [PATCH 28/30] enable debug output
---
audio/paaudio.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/audio/paaudio.c b/audio/paaudio.c
index d3ab82d65d..9bd086de5f 100644
--- a/audio/paaudio.c
+++ b/audio/paaudio.c
@@ -6,6 +6,7 @@
#include <pulse/pulseaudio.h>
#define AUDIO_CAP "pulseaudio"
+#define DEBUG
#include "audio_int.h"
typedef struct {
--
2.14.1
From 2818f4c210353e56b21caf1def2341fa6473af4b Mon Sep 17 00:00:00 2001
From: Martin Schrodt <martin@schrodt.org>
Date: Sun, 15 Oct 2017 22:15:12 +0200
Subject: [PATCH 29/30] whitespace
Signed-off-by: Martin Schrodt <martin@schrodt.org>
---
audio/paaudio.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/audio/paaudio.c b/audio/paaudio.c
index 9bd086de5f..e76e1e006f 100644
--- a/audio/paaudio.c
+++ b/audio/paaudio.c
@@ -345,12 +345,12 @@ static pa_stream *qpa_simple_new (
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);
+ | PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL);
} else {
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);
+ | PA_STREAM_AUTO_TIMING_UPDATE);
}
if (r < 0) {
@@ -414,7 +414,7 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
pa->ss.channels = as->nchannels;
pa->ss.rate = as->freq;
- pa->ba.tlength = tlength * pa_frame_size (&pa->ss);
+ pa->ba.tlength = tlength * pa_frame_size(&pa->ss);
pa->ba.maxlength = -1;
pa->ba.minreq = -1;
pa->ba.prebuf = -1;
--
2.14.1
From c44573413940ef2fc9e41e05ec7f72dbda6b101b Mon Sep 17 00:00:00 2001
From: Martin Schrodt <martin@schrodt.org>
Date: Sun, 15 Oct 2017 22:19:49 +0200
Subject: [PATCH 30/30] bigger default fraglength, to reduce PA CPU usage
Signed-off-by: Martin Schrodt <martin@schrodt.org>
---
audio/paaudio.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/audio/paaudio.c b/audio/paaudio.c
index e76e1e006f..b46beeea92 100644
--- a/audio/paaudio.c
+++ b/audio/paaudio.c
@@ -459,7 +459,7 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
int64_t fragsize = g->conf.fragsize;
if (fragsize == 0) {
- fragsize = frames_per_tick_x1000 / 2500;
+ fragsize = frames_per_tick_x1000 / 1000;
}
int64_t buflen = g->conf.buffer_size_in;
if (buflen == 0) {
@@ -467,7 +467,7 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
}
int64_t maxlength = g->conf.maxlength_in;
if (maxlength == 0) {
- maxlength = fragsize * 4;
+ maxlength = fragsize * 2;
}
ldebug("IN internal buffer: %.2f ms (%"PRId64" frames)\n",
--
2.14.1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment