#include "qemu/osdep.h"
#include "qemu/module.h"
-#include "qemu-common.h"
#include "audio.h"
#include "qapi/opts-visitor.h"
static void qpa_conn_fini(PAConnection *c);
-static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...)
+static void G_GNUC_PRINTF (2, 3) qpa_logerr (int err, const char *fmt, ...)
{
va_list ap;
return 0;
}
+static size_t qpa_buffer_get_free(HWVoiceOut *hw)
+{
+ PAVoiceOut *p = (PAVoiceOut *)hw;
+ PAConnection *c = p->g->conn;
+ size_t l;
+
+ pa_threaded_mainloop_lock(c->mainloop);
+
+ CHECK_DEAD_GOTO(c, p->stream, unlock_and_fail,
+ "pa_threaded_mainloop_lock failed\n");
+ if (pa_stream_get_state(p->stream) != PA_STREAM_READY) {
+ /* wait for stream to become ready */
+ l = 0;
+ goto unlock;
+ }
+
+ l = pa_stream_writable_size(p->stream);
+ CHECK_SUCCESS_GOTO(c, l != (size_t) -1, unlock_and_fail,
+ "pa_stream_writable_size failed\n");
+
+unlock:
+ pa_threaded_mainloop_unlock(c->mainloop);
+ return l;
+
+unlock_and_fail:
+ pa_threaded_mainloop_unlock(c->mainloop);
+ return 0;
+}
+
static void *qpa_get_buffer_out(HWVoiceOut *hw, size_t *size)
{
- PAVoiceOut *p = (PAVoiceOut *) hw;
+ PAVoiceOut *p = (PAVoiceOut *)hw;
PAConnection *c = p->g->conn;
void *ret;
int r;
return NULL;
}
+static size_t qpa_put_buffer_out(HWVoiceOut *hw, void *data, size_t length)
+{
+ PAVoiceOut *p = (PAVoiceOut *)hw;
+ PAConnection *c = p->g->conn;
+ int r;
+
+ pa_threaded_mainloop_lock(c->mainloop);
+
+ CHECK_DEAD_GOTO(c, p->stream, unlock_and_fail,
+ "pa_threaded_mainloop_lock failed\n");
+
+ r = pa_stream_write(p->stream, data, length, NULL, 0LL, PA_SEEK_RELATIVE);
+ CHECK_SUCCESS_GOTO(c, r >= 0, unlock_and_fail, "pa_stream_write failed\n");
+
+ pa_threaded_mainloop_unlock(c->mainloop);
+ return length;
+
+unlock_and_fail:
+ pa_threaded_mainloop_unlock(c->mainloop);
+ return 0;
+}
+
static size_t qpa_write(HWVoiceOut *hw, void *data, size_t length)
{
PAVoiceOut *p = (PAVoiceOut *) hw;
CHECK_DEAD_GOTO(c, p->stream, unlock_and_fail,
"pa_threaded_mainloop_lock failed\n");
+ if (pa_stream_get_state(p->stream) != PA_STREAM_READY) {
+ /* wait for stream to become ready */
+ l = 0;
+ goto unlock;
+ }
l = pa_stream_writable_size(p->stream);
r = pa_stream_write(p->stream, data, l, NULL, 0LL, PA_SEEK_RELATIVE);
CHECK_SUCCESS_GOTO(c, r >= 0, unlock_and_fail, "pa_stream_write failed\n");
+unlock:
pa_threaded_mainloop_unlock(c->mainloop);
return l;
case AUDIO_FORMAT_U32:
format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE;
break;
+ case AUDIO_FORMAT_F32:
+ format = endianness ? PA_SAMPLE_FLOAT32BE : PA_SAMPLE_FLOAT32LE;
+ break;
default:
dolog ("Internal logic error: Bad audio format %d\n", afmt);
format = PA_SAMPLE_U8;
case PA_SAMPLE_S32LE:
*endianness = 0;
return AUDIO_FORMAT_S32;
+ case PA_SAMPLE_FLOAT32BE:
+ *endianness = 1;
+ return AUDIO_FORMAT_F32;
+ case PA_SAMPLE_FLOAT32LE:
+ *endianness = 0;
+ return AUDIO_FORMAT_F32;
default:
dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt);
return AUDIO_FORMAT_U8;
pa_stream_set_state_callback(stream, stream_state_cb, c);
- flags =
- PA_STREAM_INTERPOLATE_TIMING
- | PA_STREAM_AUTO_TIMING_UPDATE
- | PA_STREAM_EARLY_REQUESTS;
+ flags = PA_STREAM_EARLY_REQUESTS;
if (dev) {
/* don't move the stream if the user specified a sink/source */
}
if (r < 0) {
- goto fail;
+ goto fail;
}
pa_threaded_mainloop_unlock(c->mainloop);
ss.rate = as->freq;
ba.tlength = pa_usec_to_bytes(ppdo->latency, &ss);
- ba.minreq = -1;
+ ba.minreq = pa_usec_to_bytes(MIN(ppdo->latency >> 2,
+ (g->dev->timer_period >> 2) * 3), &ss);
ba.maxlength = -1;
ba.prebuf = -1;
}
audio_pcm_init_info (&hw->info, &obt_as);
- hw->samples = audio_buffer_samples(
- qapi_AudiodevPaPerDirectionOptions_base(ppdo),
- &obt_as, ppdo->buffer_length);
+ /* hw->samples counts in frames */
+ hw->samples = audio_buffer_frames(
+ qapi_AudiodevPaPerDirectionOptions_base(ppdo), &obt_as, 46440);
return 0;
ss.channels = as->nchannels;
ss.rate = as->freq;
- ba.fragsize = pa_usec_to_bytes(ppdo->latency, &ss);
- ba.maxlength = pa_usec_to_bytes(ppdo->latency * 2, &ss);
+ ba.fragsize = pa_usec_to_bytes((g->dev->timer_period >> 1) * 3, &ss);
+ ba.maxlength = pa_usec_to_bytes(
+ MAX(ppdo->latency, g->dev->timer_period * 3), &ss);
ba.minreq = -1;
ba.prebuf = -1;
}
audio_pcm_init_info (&hw->info, &obt_as);
- hw->samples = audio_buffer_samples(
- qapi_AudiodevPaPerDirectionOptions_base(ppdo),
- &obt_as, ppdo->buffer_length);
+ /* hw->samples counts in frames */
+ hw->samples = audio_buffer_frames(
+ qapi_AudiodevPaPerDirectionOptions_base(ppdo), &obt_as, 46440);
return 0;
static int qpa_validate_per_direction_opts(Audiodev *dev,
AudiodevPaPerDirectionOptions *pdo)
{
- if (!pdo->has_buffer_length) {
- pdo->has_buffer_length = true;
- pdo->buffer_length = 46440;
- }
if (!pdo->has_latency) {
pdo->has_latency = true;
- pdo->latency = 15000;
+ pdo->latency = 46440;
}
return 1;
}
/* common */
static void *qpa_conn_init(const char *server)
{
- const char *vm_name;
- PAConnection *c = g_malloc0(sizeof(PAConnection));
+ PAConnection *c = g_new0(PAConnection, 1);
QTAILQ_INSERT_TAIL(&pa_conns, c, list);
c->mainloop = pa_threaded_mainloop_new();
goto fail;
}
- vm_name = qemu_get_vm_name();
c->context = pa_context_new(pa_threaded_mainloop_get_api(c->mainloop),
- vm_name ? vm_name : "qemu");
+ audio_application_name());
if (!c->context) {
goto fail;
}
return NULL;
}
- g = g_malloc0(sizeof(paaudio));
+ g = g_new0(paaudio, 1);
server = popts->has_server ? popts->server : NULL;
g->dev = dev;
.init_out = qpa_init_out,
.fini_out = qpa_fini_out,
.write = qpa_write,
+ .buffer_get_free = qpa_buffer_get_free,
.get_buffer_out = qpa_get_buffer_out,
- .put_buffer_out = qpa_write, /* pa handles it */
+ .put_buffer_out = qpa_put_buffer_out,
.volume_out = qpa_volume_out,
.init_in = qpa_init_in,