mirror of
https://gitlab.com/qemu-project/qemu
synced 2024-11-05 20:35:44 +00:00
audio: api for mixeng code free backends
This will make it possible to skip mixeng with audio playback and recording, allowing us to free ourselves from the limitations of the current mixeng (stereo, int64 samples only). In this case, HW and SW voices will be essentially the same, for every SW voice we will create a HW voice, since we can no longer mix multiple voices together. Some backends expect us to call a function when we have data ready write()/read() style, while others provide a buffer and expects us to directly write/read it, so for optimal performance audio_pcm_ops provide methods for both cases. Previously backends asked mixeng for more data in run_out/run_it, now instead mixeng or the frontends will call the backends, so that's why two sets of functions required. audio.c contains glue code between the two styles, so backends only ever have to implement one style and frontends are free to call whichever is more convenient for them. Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com> Message-id: 15a33c03a62228922d851f7324c52f73cb8d2414.1568927990.git.DirtY.iCE.hu@gmail.com Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
This commit is contained in:
parent
dfc5434384
commit
ff095e5231
3 changed files with 250 additions and 8 deletions
216
audio/audio.c
216
audio/audio.c
|
@ -573,6 +573,25 @@ size_t audio_pcm_hw_clip_out(HWVoiceOut *hw, void *pcm_buf,
|
|||
return clipped;
|
||||
}
|
||||
|
||||
static void audio_pcm_hw_clip_out2(HWVoiceOut *hw, void *pcm_buf, size_t len)
|
||||
{
|
||||
size_t clipped = 0;
|
||||
size_t pos = hw->rpos;
|
||||
|
||||
while (len) {
|
||||
st_sample *src = hw->mix_buf + pos;
|
||||
uint8_t *dst = advance(pcm_buf, clipped << hw->info.shift);
|
||||
size_t samples_till_end_of_buf = hw->samples - pos;
|
||||
size_t samples_to_clip = MIN(len, samples_till_end_of_buf);
|
||||
|
||||
hw->clip(dst, src, samples_to_clip);
|
||||
|
||||
pos = (pos + samples_to_clip) % hw->samples;
|
||||
len -= samples_to_clip;
|
||||
clipped += samples_to_clip;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Soft voice (capture)
|
||||
*/
|
||||
|
@ -1050,6 +1069,36 @@ static void audio_capture_mix_and_clear(HWVoiceOut *hw, size_t rpos,
|
|||
mixeng_clear(hw->mix_buf, samples - n);
|
||||
}
|
||||
|
||||
static size_t audio_pcm_hw_run_out(HWVoiceOut *hw, size_t live)
|
||||
{
|
||||
size_t clipped = 0;
|
||||
|
||||
while (live) {
|
||||
size_t size, decr, proc;
|
||||
void *buf = hw->pcm_ops->get_buffer_out(hw, &size);
|
||||
if (!buf) {
|
||||
/* retrying will likely won't help, drop everything. */
|
||||
hw->rpos = (hw->rpos + live) % hw->samples;
|
||||
return clipped + live;
|
||||
}
|
||||
|
||||
decr = MIN(size >> hw->info.shift, live);
|
||||
audio_pcm_hw_clip_out2(hw, buf, decr);
|
||||
proc = hw->pcm_ops->put_buffer_out(hw, buf, decr << hw->info.shift) >>
|
||||
hw->info.shift;
|
||||
|
||||
live -= proc;
|
||||
clipped += proc;
|
||||
hw->rpos = (hw->rpos + proc) % hw->samples;
|
||||
|
||||
if (proc == 0 || proc < decr) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return clipped;
|
||||
}
|
||||
|
||||
static void audio_run_out (AudioState *s)
|
||||
{
|
||||
HWVoiceOut *hw = NULL;
|
||||
|
@ -1097,7 +1146,11 @@ static void audio_run_out (AudioState *s)
|
|||
}
|
||||
|
||||
prev_rpos = hw->rpos;
|
||||
played = hw->pcm_ops->run_out (hw, live);
|
||||
if (hw->pcm_ops->run_out) {
|
||||
played = hw->pcm_ops->run_out(hw, live);
|
||||
} else {
|
||||
played = audio_pcm_hw_run_out(hw, live);
|
||||
}
|
||||
replay_audio_out(&played);
|
||||
if (audio_bug(__func__, hw->rpos >= hw->samples)) {
|
||||
dolog("hw->rpos=%zu hw->samples=%zu played=%zu\n",
|
||||
|
@ -1156,6 +1209,35 @@ static void audio_run_out (AudioState *s)
|
|||
}
|
||||
}
|
||||
|
||||
static size_t audio_pcm_hw_run_in(HWVoiceIn *hw, size_t samples)
|
||||
{
|
||||
size_t conv = 0;
|
||||
|
||||
while (samples) {
|
||||
size_t proc;
|
||||
size_t size = samples << hw->info.shift;
|
||||
void *buf = hw->pcm_ops->get_buffer_in(hw, &size);
|
||||
|
||||
assert((size & hw->info.align) == 0);
|
||||
if (size == 0) {
|
||||
hw->pcm_ops->put_buffer_in(hw, buf, size);
|
||||
break;
|
||||
}
|
||||
|
||||
proc = MIN(size >> hw->info.shift,
|
||||
hw->samples - hw->wpos);
|
||||
|
||||
hw->conv(hw->conv_buf + hw->wpos, buf, proc);
|
||||
hw->wpos = (hw->wpos + proc) % hw->samples;
|
||||
|
||||
samples -= proc;
|
||||
conv += proc;
|
||||
hw->pcm_ops->put_buffer_in(hw, buf, proc << hw->info.shift);
|
||||
}
|
||||
|
||||
return conv;
|
||||
}
|
||||
|
||||
static void audio_run_in (AudioState *s)
|
||||
{
|
||||
HWVoiceIn *hw = NULL;
|
||||
|
@ -1165,7 +1247,12 @@ static void audio_run_in (AudioState *s)
|
|||
size_t captured = 0, min;
|
||||
|
||||
if (replay_mode != REPLAY_MODE_PLAY) {
|
||||
captured = hw->pcm_ops->run_in(hw);
|
||||
if (hw->pcm_ops->run_in) {
|
||||
captured = hw->pcm_ops->run_in(hw);
|
||||
} else {
|
||||
captured = audio_pcm_hw_run_in(
|
||||
hw, hw->samples - audio_pcm_hw_get_live_in(hw));
|
||||
}
|
||||
}
|
||||
replay_audio_in(&captured, hw->conv_buf, &hw->wpos, hw->samples);
|
||||
|
||||
|
@ -1259,12 +1346,137 @@ void audio_run(AudioState *s, const char *msg)
|
|||
#endif
|
||||
}
|
||||
|
||||
void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size)
|
||||
{
|
||||
ssize_t start;
|
||||
|
||||
if (unlikely(!hw->buf_emul)) {
|
||||
size_t calc_size = hw->samples << hw->info.shift;
|
||||
hw->buf_emul = g_malloc(calc_size);
|
||||
hw->size_emul = calc_size;
|
||||
hw->pos_emul = hw->pending_emul = 0;
|
||||
}
|
||||
|
||||
while (hw->pending_emul < hw->size_emul) {
|
||||
size_t read_len = MIN(hw->size_emul - hw->pos_emul,
|
||||
hw->size_emul - hw->pending_emul);
|
||||
size_t read = hw->pcm_ops->read(hw, hw->buf_emul + hw->pos_emul,
|
||||
read_len);
|
||||
hw->pending_emul += read;
|
||||
if (read < read_len) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
start = ((ssize_t) hw->pos_emul) - hw->pending_emul;
|
||||
if (start < 0) {
|
||||
start += hw->size_emul;
|
||||
}
|
||||
assert(start >= 0 && start < hw->size_emul);
|
||||
|
||||
*size = MIN(hw->pending_emul, hw->size_emul - start);
|
||||
return hw->buf_emul + start;
|
||||
}
|
||||
|
||||
void audio_generic_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size)
|
||||
{
|
||||
assert(size <= hw->pending_emul);
|
||||
hw->pending_emul -= size;
|
||||
}
|
||||
|
||||
void *audio_generic_get_buffer_out(HWVoiceOut *hw, size_t *size)
|
||||
{
|
||||
if (unlikely(!hw->buf_emul)) {
|
||||
size_t calc_size = hw->samples << hw->info.shift;
|
||||
|
||||
hw->buf_emul = g_malloc(calc_size);
|
||||
hw->size_emul = calc_size;
|
||||
hw->pos_emul = hw->pending_emul = 0;
|
||||
}
|
||||
|
||||
*size = MIN(hw->size_emul - hw->pending_emul,
|
||||
hw->size_emul - hw->pos_emul);
|
||||
return hw->buf_emul + hw->pos_emul;
|
||||
}
|
||||
|
||||
size_t audio_generic_put_buffer_out_nowrite(HWVoiceOut *hw, void *buf,
|
||||
size_t size)
|
||||
{
|
||||
assert(buf == hw->buf_emul + hw->pos_emul &&
|
||||
size + hw->pending_emul <= hw->size_emul);
|
||||
|
||||
hw->pending_emul += size;
|
||||
hw->pos_emul = (hw->pos_emul + size) % hw->size_emul;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t audio_generic_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size)
|
||||
{
|
||||
audio_generic_put_buffer_out_nowrite(hw, buf, size);
|
||||
|
||||
while (hw->pending_emul) {
|
||||
size_t write_len, written;
|
||||
ssize_t start = ((ssize_t) hw->pos_emul) - hw->pending_emul;
|
||||
if (start < 0) {
|
||||
start += hw->size_emul;
|
||||
}
|
||||
assert(start >= 0 && start < hw->size_emul);
|
||||
|
||||
write_len = MIN(hw->pending_emul, hw->size_emul - start);
|
||||
|
||||
written = hw->pcm_ops->write(hw, hw->buf_emul + start, write_len);
|
||||
hw->pending_emul -= written;
|
||||
|
||||
if (written < write_len) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* fake we have written everything. non-written data remain in pending_emul,
|
||||
* so we do not have to clip them multiple times
|
||||
*/
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t audio_generic_write(HWVoiceOut *hw, void *buf, size_t size)
|
||||
{
|
||||
size_t dst_size, copy_size;
|
||||
void *dst = hw->pcm_ops->get_buffer_out(hw, &dst_size);
|
||||
copy_size = MIN(size, dst_size);
|
||||
|
||||
memcpy(dst, buf, copy_size);
|
||||
return hw->pcm_ops->put_buffer_out(hw, buf, copy_size);
|
||||
}
|
||||
|
||||
size_t audio_generic_read(HWVoiceIn *hw, void *buf, size_t size)
|
||||
{
|
||||
size_t dst_size, copy_size;
|
||||
void *dst = hw->pcm_ops->get_buffer_in(hw, &dst_size);
|
||||
copy_size = MIN(size, dst_size);
|
||||
|
||||
memcpy(dst, buf, copy_size);
|
||||
hw->pcm_ops->put_buffer_in(hw, buf, copy_size);
|
||||
return copy_size;
|
||||
}
|
||||
|
||||
|
||||
static int audio_driver_init(AudioState *s, struct audio_driver *drv,
|
||||
bool msg, Audiodev *dev)
|
||||
{
|
||||
s->drv_opaque = drv->init(dev);
|
||||
|
||||
if (s->drv_opaque) {
|
||||
if (!drv->pcm_ops->get_buffer_in) {
|
||||
drv->pcm_ops->get_buffer_in = audio_generic_get_buffer_in;
|
||||
drv->pcm_ops->put_buffer_in = audio_generic_put_buffer_in;
|
||||
}
|
||||
if (!drv->pcm_ops->get_buffer_out) {
|
||||
drv->pcm_ops->get_buffer_out = audio_generic_get_buffer_out;
|
||||
drv->pcm_ops->put_buffer_out = audio_generic_put_buffer_out;
|
||||
}
|
||||
|
||||
audio_init_nb_voices_out(s, drv);
|
||||
audio_init_nb_voices_in(s, drv);
|
||||
s->drv = drv;
|
||||
|
|
|
@ -65,6 +65,8 @@ typedef struct HWVoiceOut {
|
|||
uint64_t ts_helper;
|
||||
|
||||
struct st_sample *mix_buf;
|
||||
void *buf_emul;
|
||||
size_t pos_emul, pending_emul, size_emul;
|
||||
|
||||
size_t samples;
|
||||
QLIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head;
|
||||
|
@ -87,6 +89,8 @@ typedef struct HWVoiceIn {
|
|||
uint64_t ts_helper;
|
||||
|
||||
struct st_sample *conv_buf;
|
||||
void *buf_emul;
|
||||
size_t pos_emul, pending_emul, size_emul;
|
||||
|
||||
size_t samples;
|
||||
QLIST_HEAD (sw_in_listhead, SWVoiceIn) sw_head;
|
||||
|
@ -147,17 +151,42 @@ struct audio_driver {
|
|||
};
|
||||
|
||||
struct audio_pcm_ops {
|
||||
int (*init_out)(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque);
|
||||
void (*fini_out)(HWVoiceOut *hw);
|
||||
int (*init_out)(HWVoiceOut *hw, audsettings *as, void *drv_opaque);
|
||||
void (*fini_out)(HWVoiceOut *hw);
|
||||
size_t (*run_out)(HWVoiceOut *hw, size_t live);
|
||||
int (*ctl_out) (HWVoiceOut *hw, int cmd, ...);
|
||||
size_t (*write) (HWVoiceOut *hw, void *buf, size_t size);
|
||||
/*
|
||||
* get a buffer that after later can be passed to put_buffer_out; optional
|
||||
* returns the buffer, and writes it's size to size (in bytes)
|
||||
* this is unrelated to the above buffer_size_out function
|
||||
*/
|
||||
void *(*get_buffer_out)(HWVoiceOut *hw, size_t *size);
|
||||
/*
|
||||
* put back the buffer returned by get_buffer_out; optional
|
||||
* buf must be equal the pointer returned by get_buffer_out,
|
||||
* size may be smaller
|
||||
*/
|
||||
size_t (*put_buffer_out)(HWVoiceOut *hw, void *buf, size_t size);
|
||||
int (*ctl_out) (HWVoiceOut *hw, int cmd, ...);
|
||||
|
||||
int (*init_in) (HWVoiceIn *hw, struct audsettings *as, void *drv_opaque);
|
||||
void (*fini_in) (HWVoiceIn *hw);
|
||||
int (*init_in) (HWVoiceIn *hw, audsettings *as, void *drv_opaque);
|
||||
void (*fini_in) (HWVoiceIn *hw);
|
||||
size_t (*run_in)(HWVoiceIn *hw);
|
||||
int (*ctl_in) (HWVoiceIn *hw, int cmd, ...);
|
||||
size_t (*read) (HWVoiceIn *hw, void *buf, size_t size);
|
||||
void *(*get_buffer_in)(HWVoiceIn *hw, size_t *size);
|
||||
void (*put_buffer_in)(HWVoiceIn *hw, void *buf, size_t size);
|
||||
int (*ctl_in) (HWVoiceIn *hw, int cmd, ...);
|
||||
};
|
||||
|
||||
void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size);
|
||||
void audio_generic_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size);
|
||||
void *audio_generic_get_buffer_out(HWVoiceOut *hw, size_t *size);
|
||||
size_t audio_generic_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size);
|
||||
size_t audio_generic_put_buffer_out_nowrite(HWVoiceOut *hw, void *buf,
|
||||
size_t size);
|
||||
size_t audio_generic_write(HWVoiceOut *hw, void *buf, size_t size);
|
||||
size_t audio_generic_read(HWVoiceIn *hw, void *buf, size_t size);
|
||||
|
||||
struct capture_callback {
|
||||
struct audio_capture_ops ops;
|
||||
void *opaque;
|
||||
|
|
|
@ -71,6 +71,7 @@ static void glue(audio_init_nb_voices_, TYPE)(AudioState *s,
|
|||
|
||||
static void glue (audio_pcm_hw_free_resources_, TYPE) (HW *hw)
|
||||
{
|
||||
g_free(hw->buf_emul);
|
||||
g_free (HWBUF);
|
||||
HWBUF = NULL;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue