From 7a4ede0047a8613b0e3b72c9d351038f013dd357 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 20 Jan 2020 11:18:04 +0100 Subject: [PATCH 01/12] audio/oss: fix buffer pos calculation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: 3ba4066d085f ("ossaudio: port to the new audio backend api") Reported-by: ziming zhang Signed-off-by: Gerd Hoffmann Message-Id: <20200120101804.29578-1-kraxel@redhat.com> Reviewed-by: Philippe Mathieu-Daudé --- audio/ossaudio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audio/ossaudio.c b/audio/ossaudio.c index c43faeeea4..94564916fb 100644 --- a/audio/ossaudio.c +++ b/audio/ossaudio.c @@ -420,7 +420,7 @@ static size_t oss_write(HWVoiceOut *hw, void *buf, size_t len) size_t to_copy = MIN(len, hw->size_emul - hw->pos_emul); memcpy(hw->buf_emul + hw->pos_emul, buf, to_copy); - hw->pos_emul = (hw->pos_emul + to_copy) % hw->pos_emul; + hw->pos_emul = (hw->pos_emul + to_copy) % hw->size_emul; buf += to_copy; len -= to_copy; } From d3ed0996712900161ef2ae7fab6b41616086ad30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volker=20R=C3=BCmelin?= Date: Thu, 23 Jan 2020 08:49:35 +0100 Subject: [PATCH 02/12] audio: fix audio_generic_write MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pcm_ops function put_buffer_out expects the returned pointer of function get_buffer_out as argument. Fix this. Signed-off-by: Volker Rümelin Message-Id: <20200123074943.6699-1-vr_qemu@t-online.de> Signed-off-by: Gerd Hoffmann --- audio/audio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audio/audio.c b/audio/audio.c index f63f39769a..7226aa64ff 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -1476,7 +1476,7 @@ size_t audio_generic_write(HWVoiceOut *hw, void *buf, size_t size) copy_size = MIN(size, dst_size); memcpy(dst, buf, copy_size); - return hw->pcm_ops->put_buffer_out(hw, buf, copy_size); + return hw->pcm_ops->put_buffer_out(hw, dst, copy_size); } size_t audio_generic_read(HWVoiceIn *hw, void *buf, size_t size) From 4da58faa5b5d014ede0839b606bed5ea91e321f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volker=20R=C3=BCmelin?= Date: Thu, 23 Jan 2020 08:49:36 +0100 Subject: [PATCH 03/12] audio: fix audio_generic_read MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It seems the function audio_generic_read started as a copy of function audio_generic_write and some necessary changes were forgotten. Fix the mixed up source and destination pointers and rename misnamed variables. Signed-off-by: Volker Rümelin Message-Id: <20200123074943.6699-2-vr_qemu@t-online.de> Signed-off-by: Gerd Hoffmann --- audio/audio.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/audio/audio.c b/audio/audio.c index 7226aa64ff..9cd76a331d 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -1481,12 +1481,12 @@ size_t audio_generic_write(HWVoiceOut *hw, void *buf, size_t 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); + size_t src_size, copy_size; + void *src = hw->pcm_ops->get_buffer_in(hw, &src_size); + copy_size = MIN(size, src_size); - memcpy(dst, buf, copy_size); - hw->pcm_ops->put_buffer_in(hw, buf, copy_size); + memcpy(buf, src, copy_size); + hw->pcm_ops->put_buffer_in(hw, src, copy_size); return copy_size; } From a76e6b8794727ae777e33856e1ccd2b410d0bde2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volker=20R=C3=BCmelin?= Date: Thu, 23 Jan 2020 08:49:37 +0100 Subject: [PATCH 04/12] paaudio: remove unused variables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The unused variables were last used before commit 49ddd7e122 "paaudio: port to the new audio backend api". Fixes: 49ddd7e122 Signed-off-by: Volker Rümelin Message-Id: <20200123074943.6699-3-vr_qemu@t-online.de> Signed-off-by: Gerd Hoffmann --- audio/paaudio.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/audio/paaudio.c b/audio/paaudio.c index dbfe48c03a..8f37c61851 100644 --- a/audio/paaudio.c +++ b/audio/paaudio.c @@ -32,7 +32,6 @@ typedef struct { HWVoiceOut hw; pa_stream *stream; paaudio *g; - size_t samples; } PAVoiceOut; typedef struct { @@ -41,7 +40,6 @@ typedef struct { const void *read_data; size_t read_length; paaudio *g; - size_t samples; } PAVoiceIn; static void qpa_conn_fini(PAConnection *c); @@ -488,7 +486,7 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as, } audio_pcm_init_info (&hw->info, &obt_as); - hw->samples = pa->samples = audio_buffer_samples( + hw->samples = audio_buffer_samples( qapi_AudiodevPaPerDirectionOptions_base(ppdo), &obt_as, ppdo->buffer_length); @@ -536,7 +534,7 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) } audio_pcm_init_info (&hw->info, &obt_as); - hw->samples = pa->samples = audio_buffer_samples( + hw->samples = audio_buffer_samples( qapi_AudiodevPaPerDirectionOptions_base(ppdo), &obt_as, ppdo->buffer_length); From 69ac07863205ec281bcd2f2e6571202da0c315f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volker=20R=C3=BCmelin?= Date: Thu, 23 Jan 2020 08:49:38 +0100 Subject: [PATCH 05/12] audio: prevent SIGSEGV in AUD_get_buffer_size_out MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With audiodev parameter out.mixing-engine=off hw->mix_buf is NULL. This leads to a segmentation fault in AUD_get_buffer_size_out. This patch reverts a small part of dc88e38fa7 "audio: unify input and output mixeng buffer management". To reproduce the problem start qemu with -soundhw adlib -audiodev pa,id=audio0,out.mixing-engine=off Signed-off-by: Volker Rümelin Message-Id: <20200123074943.6699-4-vr_qemu@t-online.de> Signed-off-by: Gerd Hoffmann --- audio/audio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/audio/audio.c b/audio/audio.c index 9cd76a331d..12ed318813 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -879,9 +879,9 @@ size_t AUD_read(SWVoiceIn *sw, void *buf, size_t size) } } -int AUD_get_buffer_size_out (SWVoiceOut *sw) +int AUD_get_buffer_size_out(SWVoiceOut *sw) { - return sw->hw->mix_buf->size * sw->hw->info.bytes_per_frame; + return sw->hw->samples * sw->hw->info.bytes_per_frame; } void AUD_set_active_out (SWVoiceOut *sw, int on) From fdc8c5f4717f42f4b19b733ada30da2ba92a3ad1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volker=20R=C3=BCmelin?= Date: Thu, 23 Jan 2020 08:49:39 +0100 Subject: [PATCH 06/12] audio: fix bug 1858488 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The combined generic buffer management code and buffer run out code in function audio_generic_put_buffer_out has a problematic behaviour. A few hundred milliseconds after playback starts the mixing buffer and the generic buffer are nearly full and the following pattern can be seen. On first call of audio_pcm_hw_run_out the buffer run code in audio_generic_put_buffer_out writes some data to the audio hardware but the generic buffer will fill faster and is full when audio_pcm_hw_run_out returns. This is because emulated audio devices can produce playback data at a higher rate than the audio backend hardware consumes this data. On next call of audio_pcm_hw_run_out the buffer run code in audio_generic_put_buffer_out writes some data to the audio hardware but no audio data is transferred to the generic buffer because the buffer is already full. Then the pattern repeats. For the emulated audio device this looks like the audio timer period has doubled. This patch splits the combined generic buffer management code and buffer run out code and calls the buffer run out code after buffer management code to break this pattern. The bug report is for the wav audio backend. But the problem is not limited to this backend. All audio backends which use the audio_generic_put_buffer_out function show this problem. Buglink: https://bugs.launchpad.net/qemu/+bug/1858488 Signed-off-by: Volker Rümelin Message-Id: <20200123074943.6699-5-vr_qemu@t-online.de> Signed-off-by: Gerd Hoffmann --- audio/alsaaudio.c | 1 + audio/audio.c | 58 ++++++++++++++++++++++------------------------- audio/audio_int.h | 4 ++-- audio/coreaudio.c | 7 ++++-- audio/noaudio.c | 1 + audio/ossaudio.c | 10 ++++++++ audio/sdlaudio.c | 7 ++++-- audio/wavaudio.c | 1 + 8 files changed, 52 insertions(+), 37 deletions(-) diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c index f37ce1ce85..4ef26818be 100644 --- a/audio/alsaaudio.c +++ b/audio/alsaaudio.c @@ -906,6 +906,7 @@ static struct audio_pcm_ops alsa_pcm_ops = { .init_out = alsa_init_out, .fini_out = alsa_fini_out, .write = alsa_write, + .run_buffer_out = audio_generic_run_buffer_out, .enable_out = alsa_enable_out, .init_in = alsa_init_in, diff --git a/audio/audio.c b/audio/audio.c index 12ed318813..b686429203 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -1097,6 +1097,10 @@ static size_t audio_pcm_hw_run_out(HWVoiceOut *hw, size_t live) } } + if (hw->pcm_ops->run_buffer_out) { + hw->pcm_ops->run_buffer_out(hw); + } + return clipped; } @@ -1413,6 +1417,28 @@ void audio_generic_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size) hw->pending_emul -= size; } +void audio_generic_run_buffer_out(HWVoiceOut *hw) +{ + 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; + } + } +} + void *audio_generic_get_buffer_out(HWVoiceOut *hw, size_t *size) { if (unlikely(!hw->buf_emul)) { @@ -1428,8 +1454,7 @@ void *audio_generic_get_buffer_out(HWVoiceOut *hw, size_t *size) return hw->buf_emul + hw->pos_emul; } -size_t audio_generic_put_buffer_out_nowrite(HWVoiceOut *hw, void *buf, - size_t size) +size_t audio_generic_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size) { assert(buf == hw->buf_emul + hw->pos_emul && size + hw->pending_emul <= hw->size_emul); @@ -1440,35 +1465,6 @@ size_t audio_generic_put_buffer_out_nowrite(HWVoiceOut *hw, void *buf, 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; diff --git a/audio/audio_int.h b/audio/audio_int.h index 5ba2078346..3c8e48b55b 100644 --- a/audio/audio_int.h +++ b/audio/audio_int.h @@ -152,6 +152,7 @@ struct audio_pcm_ops { int (*init_out)(HWVoiceOut *hw, audsettings *as, void *drv_opaque); void (*fini_out)(HWVoiceOut *hw); size_t (*write) (HWVoiceOut *hw, void *buf, size_t size); + void (*run_buffer_out)(HWVoiceOut *hw); /* * 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) @@ -178,10 +179,9 @@ struct audio_pcm_ops { 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_run_buffer_out(HWVoiceOut *hw); 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); diff --git a/audio/coreaudio.c b/audio/coreaudio.c index 66f0f459cf..c7a7196c2d 100644 --- a/audio/coreaudio.c +++ b/audio/coreaudio.c @@ -411,7 +411,7 @@ static int coreaudio_unlock (coreaudioVoiceOut *core, const char *fn_name) } COREAUDIO_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size), (hw, size)) -COREAUDIO_WRAPPER_FUNC(put_buffer_out_nowrite, size_t, +COREAUDIO_WRAPPER_FUNC(put_buffer_out, size_t, (HWVoiceOut *hw, void *buf, size_t size), (hw, buf, size)) COREAUDIO_WRAPPER_FUNC(write, size_t, (HWVoiceOut *hw, void *buf, size_t size), @@ -687,9 +687,12 @@ static void coreaudio_audio_fini (void *opaque) static struct audio_pcm_ops coreaudio_pcm_ops = { .init_out = coreaudio_init_out, .fini_out = coreaudio_fini_out, + /* wrapper for audio_generic_write */ .write = coreaudio_write, + /* wrapper for audio_generic_get_buffer_out */ .get_buffer_out = coreaudio_get_buffer_out, - .put_buffer_out = coreaudio_put_buffer_out_nowrite, + /* wrapper for audio_generic_put_buffer_out */ + .put_buffer_out = coreaudio_put_buffer_out, .enable_out = coreaudio_enable_out }; diff --git a/audio/noaudio.c b/audio/noaudio.c index ff99b253ff..05798ea210 100644 --- a/audio/noaudio.c +++ b/audio/noaudio.c @@ -118,6 +118,7 @@ static struct audio_pcm_ops no_pcm_ops = { .init_out = no_init_out, .fini_out = no_fini_out, .write = no_write, + .run_buffer_out = audio_generic_run_buffer_out, .enable_out = no_enable_out, .init_in = no_init_in, diff --git a/audio/ossaudio.c b/audio/ossaudio.c index 94564916fb..576b5b5b20 100644 --- a/audio/ossaudio.c +++ b/audio/ossaudio.c @@ -382,6 +382,15 @@ static size_t oss_get_available_bytes(OSSVoiceOut *oss) return audio_ring_dist(cntinfo.ptr, oss->hw.pos_emul, oss->hw.size_emul); } +static void oss_run_buffer_out(HWVoiceOut *hw) +{ + OSSVoiceOut *oss = (OSSVoiceOut *)hw; + + if (!oss->mmapped) { + audio_generic_run_buffer_out(hw); + } +} + static void *oss_get_buffer_out(HWVoiceOut *hw, size_t *size) { OSSVoiceOut *oss = (OSSVoiceOut *) hw; @@ -748,6 +757,7 @@ static struct audio_pcm_ops oss_pcm_ops = { .init_out = oss_init_out, .fini_out = oss_fini_out, .write = oss_write, + .run_buffer_out = oss_run_buffer_out, .get_buffer_out = oss_get_buffer_out, .put_buffer_out = oss_put_buffer_out, .enable_out = oss_enable_out, diff --git a/audio/sdlaudio.c b/audio/sdlaudio.c index 5c6bcfcb3e..c00e7d7845 100644 --- a/audio/sdlaudio.c +++ b/audio/sdlaudio.c @@ -227,7 +227,7 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len) SDL_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size), (hw, size), *size = 0, sdl_unlock) -SDL_WRAPPER_FUNC(put_buffer_out_nowrite, size_t, +SDL_WRAPPER_FUNC(put_buffer_out, size_t, (HWVoiceOut *hw, void *buf, size_t size), (hw, buf, size), /*nothing*/, sdl_unlock_and_post) SDL_WRAPPER_FUNC(write, size_t, @@ -320,9 +320,12 @@ static void sdl_audio_fini (void *opaque) static struct audio_pcm_ops sdl_pcm_ops = { .init_out = sdl_init_out, .fini_out = sdl_fini_out, + /* wrapper for audio_generic_write */ .write = sdl_write, + /* wrapper for audio_generic_get_buffer_out */ .get_buffer_out = sdl_get_buffer_out, - .put_buffer_out = sdl_put_buffer_out_nowrite, + /* wrapper for audio_generic_put_buffer_out */ + .put_buffer_out = sdl_put_buffer_out, .enable_out = sdl_enable_out, }; diff --git a/audio/wavaudio.c b/audio/wavaudio.c index e46d834bd3..20e6853f85 100644 --- a/audio/wavaudio.c +++ b/audio/wavaudio.c @@ -197,6 +197,7 @@ static struct audio_pcm_ops wav_pcm_ops = { .init_out = wav_init_out, .fini_out = wav_fini_out, .write = wav_write_out, + .run_buffer_out = audio_generic_run_buffer_out, .enable_out = wav_enable_out, }; From 3e0c1bbab52d124231f56bdaa99b6d0e4a6e3fa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volker=20R=C3=BCmelin?= Date: Thu, 23 Jan 2020 08:49:40 +0100 Subject: [PATCH 07/12] ossaudio: prevent SIGSEGV in oss_enable_out MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With audiodev parameter out.mixing-engine=off hw->mix_buf is NULL. This patch reverts a small part of dc88e38fa7 "audio: unify input and output mixeng buffer management". To reproduce the problem start qemu with -audiodev oss,id=audio0,try-mmap=on,out.mixing-engine=off Signed-off-by: Volker Rümelin Message-Id: <20200123074943.6699-6-vr_qemu@t-online.de> Signed-off-by: Gerd Hoffmann --- audio/ossaudio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audio/ossaudio.c b/audio/ossaudio.c index 576b5b5b20..39a6fc09e5 100644 --- a/audio/ossaudio.c +++ b/audio/ossaudio.c @@ -592,7 +592,7 @@ static void oss_enable_out(HWVoiceOut *hw, bool enable) return; } - audio_pcm_info_clear_buf(&hw->info, hw->buf_emul, hw->mix_buf->size); + audio_pcm_info_clear_buf(&hw->info, hw->buf_emul, hw->samples); trig = PCM_ENABLE_OUTPUT; if (ioctl(oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { oss_logerr(errno, From f03cd06814ab282196165808c01d4433773a6e0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volker=20R=C3=BCmelin?= Date: Thu, 23 Jan 2020 08:49:42 +0100 Subject: [PATCH 08/12] ossaudio: disable poll mode can't be reached MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently there is no way to disable poll mode in oss_enable_out and oss_enable_in when it was enabled before. The enable code path always resets the poll mode state variable. Fixes: b027a538c6 "oss: Remove unused error handling of qemu_set_fd_handler" Signed-off-by: Volker Rümelin Message-Id: <20200123074943.6699-8-vr_qemu@t-online.de> Signed-off-by: Gerd Hoffmann --- audio/ossaudio.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/audio/ossaudio.c b/audio/ossaudio.c index 39a6fc09e5..f88d076ec2 100644 --- a/audio/ossaudio.c +++ b/audio/ossaudio.c @@ -579,14 +579,12 @@ static void oss_enable_out(HWVoiceOut *hw, bool enable) AudiodevOssPerDirectionOptions *opdo = oss->dev->u.oss.out; if (enable) { - bool poll_mode = opdo->try_poll; + hw->poll_mode = opdo->try_poll; ldebug("enabling voice\n"); - if (poll_mode) { + if (hw->poll_mode) { oss_poll_out(hw); - poll_mode = 0; } - hw->poll_mode = poll_mode; if (!oss->mmapped) { return; @@ -708,17 +706,15 @@ static void oss_enable_in(HWVoiceIn *hw, bool enable) AudiodevOssPerDirectionOptions *opdo = oss->dev->u.oss.out; if (enable) { - bool poll_mode = opdo->try_poll; + hw->poll_mode = opdo->try_poll; - if (poll_mode) { + if (hw->poll_mode) { oss_poll_in(hw); - poll_mode = 0; } - hw->poll_mode = poll_mode; } else { if (hw->poll_mode) { - hw->poll_mode = 0; qemu_set_fd_handler (oss->fd, NULL, NULL, NULL); + hw->poll_mode = 0; } } } From 599eac4e5a41e828645594097daee39373acc3c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volker=20R=C3=BCmelin?= Date: Thu, 23 Jan 2020 08:49:43 +0100 Subject: [PATCH 09/12] audio: audio_generic_get_buffer_in should honor *size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function generic_get_buffer_in currently ignores the *size parameter and may return a buffer larger than *size. As a result the variable samples in function audio_pcm_hw_run_in may underflow. The while loop then most likely will never termiate. Buglink: http://bugs.debian.org/948658 Signed-off-by: Volker Rümelin Message-Id: <20200123074943.6699-9-vr_qemu@t-online.de> Signed-off-by: Gerd Hoffmann --- audio/audio.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/audio/audio.c b/audio/audio.c index b686429203..f9859408f3 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -1407,7 +1407,8 @@ void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size) } assert(start >= 0 && start < hw->size_emul); - *size = MIN(hw->pending_emul, hw->size_emul - start); + *size = MIN(*size, hw->pending_emul); + *size = MIN(*size, hw->size_emul - start); return hw->buf_emul + start; } From fb35c2cec58985f0b8d2733f1b91927542eeb3fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C5=91v=C3=A1g=C3=B3=2C=20Zolt=C3=A1n?= Date: Mon, 3 Feb 2020 00:02:23 +0100 Subject: [PATCH 10/12] audio/dsound: fix invalid parameters error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Windows (unlike wine) bails out when IDirectSoundBuffer8::Lock is called with zero length. Also, hw->pos_emul handling was incorrect when calling this function for the first time. Signed-off-by: Kővágó, Zoltán Reported-by: KJ Liew Tested-by: Howard Spoelstra Message-id: fe9744216d9d421a2dbb09bcf5fa0dbd18f77ac5.1580684275.git.DirtY.iCE.hu@gmail.com Signed-off-by: Gerd Hoffmann --- audio/audio.c | 6 ++---- audio/dsound_template.h | 1 + audio/dsoundaudio.c | 27 +++++++++++++++++++++++---- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/audio/audio.c b/audio/audio.c index f9859408f3..3bfd808bc6 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -1076,10 +1076,8 @@ static size_t audio_pcm_hw_run_out(HWVoiceOut *hw, size_t live) 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->mix_buf->pos = (hw->mix_buf->pos + live) % hw->mix_buf->size; - return clipped + live; + if (!buf || size == 0) { + break; } decr = MIN(size / hw->info.bytes_per_frame, live); diff --git a/audio/dsound_template.h b/audio/dsound_template.h index 7a15f91ce5..9c5ce625ab 100644 --- a/audio/dsound_template.h +++ b/audio/dsound_template.h @@ -244,6 +244,7 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as, goto fail0; } + ds->first_time = true; obt_as.endianness = 0; audio_pcm_init_info (&hw->info, &obt_as); diff --git a/audio/dsoundaudio.c b/audio/dsoundaudio.c index c265c0094b..bd57082a8d 100644 --- a/audio/dsoundaudio.c +++ b/audio/dsoundaudio.c @@ -53,12 +53,14 @@ typedef struct { typedef struct { HWVoiceOut hw; LPDIRECTSOUNDBUFFER dsound_buffer; + bool first_time; dsound *s; } DSoundVoiceOut; typedef struct { HWVoiceIn hw; LPDIRECTSOUNDCAPTUREBUFFER dsound_capture_buffer; + bool first_time; dsound *s; } DSoundVoiceIn; @@ -414,21 +416,32 @@ static void *dsound_get_buffer_out(HWVoiceOut *hw, size_t *size) DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer; HRESULT hr; - DWORD ppos, act_size; + DWORD ppos, wpos, act_size; size_t req_size; int err; void *ret; - hr = IDirectSoundBuffer_GetCurrentPosition(dsb, &ppos, NULL); + hr = IDirectSoundBuffer_GetCurrentPosition( + dsb, &ppos, ds->first_time ? &wpos : NULL); if (FAILED(hr)) { dsound_logerr(hr, "Could not get playback buffer position\n"); *size = 0; return NULL; } + if (ds->first_time) { + hw->pos_emul = wpos; + ds->first_time = false; + } + req_size = audio_ring_dist(ppos, hw->pos_emul, hw->size_emul); req_size = MIN(req_size, hw->size_emul - hw->pos_emul); + if (req_size == 0) { + *size = 0; + return NULL; + } + err = dsound_lock_out(dsb, &hw->info, hw->pos_emul, req_size, &ret, NULL, &act_size, NULL, false, ds->s); if (err) { @@ -508,18 +521,24 @@ static void *dsound_get_buffer_in(HWVoiceIn *hw, size_t *size) DSoundVoiceIn *ds = (DSoundVoiceIn *) hw; LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer; HRESULT hr; - DWORD cpos, act_size; + DWORD cpos, rpos, act_size; size_t req_size; int err; void *ret; - hr = IDirectSoundCaptureBuffer_GetCurrentPosition(dscb, &cpos, NULL); + hr = IDirectSoundCaptureBuffer_GetCurrentPosition( + dscb, &cpos, ds->first_time ? &rpos : NULL); if (FAILED(hr)) { dsound_logerr(hr, "Could not get capture buffer position\n"); *size = 0; return NULL; } + if (ds->first_time) { + hw->pos_emul = rpos; + ds->first_time = false; + } + req_size = audio_ring_dist(cpos, hw->pos_emul, hw->size_emul); req_size = MIN(req_size, hw->size_emul - hw->pos_emul); From 180b044ffde2cdd4a7209c727b5a8ce93d36741f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volker=20R=C3=BCmelin?= Date: Sun, 2 Feb 2020 15:06:41 +0100 Subject: [PATCH 11/12] coreaudio: fix coreaudio playback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are reports that since commit 2ceb8240fa "coreaudio: port to the new audio backend api" audio playback with CoreAudio is broken. This patch reverts some parts the commit. Because of changes in the audio subsystem the audio clip function in v4.1.0 of coreaudio.c had to be moved to mixeng.c and the generic buffer management code needed a hint about the size of the float type. This patch is based on a patch from Zoltán Kővágó found at https://lists.nongnu.org/archive/html/qemu-devel/2020-01/msg02142.html. Fixes: 2ceb8240fa "coreaudio: port to the new audio backend api" Signed-off-by: Volker Rümelin Message-id: 20200202140641.4737-1-vr_qemu@t-online.de Signed-off-by: Gerd Hoffmann --- audio/audio_template.h | 7 ++++++ audio/coreaudio.c | 32 ++++++++-------------------- audio/mixeng.c | 48 ++++++++++++++++++++++++++++++++++++++++++ audio/mixeng.h | 5 +++++ 4 files changed, 69 insertions(+), 23 deletions(-) diff --git a/audio/audio_template.h b/audio/audio_template.h index 3287d7075e..0336d2670c 100644 --- a/audio/audio_template.h +++ b/audio/audio_template.h @@ -276,6 +276,13 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s, goto err1; } + if (s->dev->driver == AUDIODEV_DRIVER_COREAUDIO) { +#ifdef DAC + hw->clip = clip_natural_float_from_stereo; +#else + hw->conv = conv_natural_float_to_stereo; +#endif + } else #ifdef DAC hw->clip = mixeng_clip #else diff --git a/audio/coreaudio.c b/audio/coreaudio.c index c7a7196c2d..e3620b274b 100644 --- a/audio/coreaudio.c +++ b/audio/coreaudio.c @@ -471,20 +471,6 @@ static OSStatus audioDeviceIOProc( return 0; } -static UInt32 coreaudio_get_flags(struct audio_pcm_info *info, - struct audsettings *as) -{ - UInt32 flags = info->sign ? kAudioFormatFlagIsSignedInteger : 0; - if (as->endianness) { /* 0 = little, 1 = big */ - flags |= kAudioFormatFlagIsBigEndian; - } - - if (flags == 0) { /* must not be 0 */ - flags = kAudioFormatFlagsAreAllClear; - } - return flags; -} - static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque) { @@ -496,6 +482,7 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, Audiodev *dev = drv_opaque; AudiodevCoreaudioPerDirectionOptions *cpdo = dev->u.coreaudio.out; int frames; + struct audsettings fake_as; /* create mutex */ err = pthread_mutex_init(&core->mutex, NULL); @@ -504,6 +491,14 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, return -1; } + /* + * The canonical audio format for CoreAudio on macOS is float. Currently + * there is no generic code for AUDIO_FORMAT_F32 in qemu. Here we select + * AUDIO_FORMAT_S32 instead because only the sample size has to match. + */ + fake_as = *as; + as = &fake_as; + as->fmt = AUDIO_FORMAT_S32; audio_pcm_init_info (&hw->info, as); status = coreaudio_get_voice(&core->outputDeviceID); @@ -572,15 +567,6 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, /* set Samplerate */ core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq; - core->outputStreamBasicDescription.mFormatID = kAudioFormatLinearPCM; - core->outputStreamBasicDescription.mFormatFlags = - coreaudio_get_flags(&hw->info, as); - core->outputStreamBasicDescription.mBytesPerPacket = - core->outputStreamBasicDescription.mBytesPerFrame = - hw->info.nchannels * hw->info.bits / 8; - core->outputStreamBasicDescription.mFramesPerPacket = 1; - core->outputStreamBasicDescription.mChannelsPerFrame = hw->info.nchannels; - core->outputStreamBasicDescription.mBitsPerChannel = hw->info.bits; status = coreaudio_set_streamformat(core->outputDeviceID, &core->outputStreamBasicDescription); diff --git a/audio/mixeng.c b/audio/mixeng.c index 2f5ba71381..16b646d48c 100644 --- a/audio/mixeng.c +++ b/audio/mixeng.c @@ -267,6 +267,54 @@ f_sample *mixeng_clip[2][2][2][3] = { } }; +void conv_natural_float_to_stereo(struct st_sample *dst, const void *src, + int samples) +{ + float *in = (float *)src; +#ifndef FLOAT_MIXENG + const float scale = UINT_MAX; +#endif + + while (samples--) { +#ifdef FLOAT_MIXENG + dst->l = *in++; + dst->r = *in++; +#else + dst->l = *in++ * scale; + dst->r = *in++ * scale; +#endif + dst++; + } +} + +void clip_natural_float_from_stereo(void *dst, const struct st_sample *src, + int samples) +{ + float *out = (float *)dst; +#ifndef FLOAT_MIXENG +#ifdef RECIPROCAL + const float scale = 1.f / UINT_MAX; +#else + const float scale = UINT_MAX; +#endif +#endif + + while (samples--) { +#ifdef FLOAT_MIXENG + *out++ = src->l; + *out++ = src->r; +#else +#ifdef RECIPROCAL + *out++ = src->l * scale; + *out++ = src->r * scale; +#else + *out++ = src->l / scale; + *out++ = src->r / scale; +#endif +#endif + src++; + } +} void audio_sample_to_uint64(void *samples, int pos, uint64_t *left, uint64_t *right) diff --git a/audio/mixeng.h b/audio/mixeng.h index 18e62c7c49..7ef61763e8 100644 --- a/audio/mixeng.h +++ b/audio/mixeng.h @@ -41,6 +41,11 @@ typedef void (f_sample) (void *dst, const struct st_sample *src, int samples); extern t_sample *mixeng_conv[2][2][2][3]; extern f_sample *mixeng_clip[2][2][2][3]; +void conv_natural_float_to_stereo(struct st_sample *dst, const void *src, + int samples); +void clip_natural_float_from_stereo(void *dst, const struct st_sample *src, + int samples); + void *st_rate_start (int inrate, int outrate); void st_rate_flow(void *opaque, st_sample *ibuf, st_sample *obuf, size_t *isamp, size_t *osamp); From ed2a4a794184df3dbd5ee4cc06e86fe220663faf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C5=91v=C3=A1g=C3=B3=2C=20Zolt=C3=A1n?= Date: Sun, 2 Feb 2020 20:38:07 +0100 Subject: [PATCH 12/12] audio: proper support for float samples in mixeng MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds proper support for float samples in mixeng by adding a new audio format for it. Limitations: only native endianness is supported. None of the virtual sound cards support float samples (it looks like most of them only support 8 and 16 bit, only hda supports 32 bit), it is only used for the audio backends (i.e. host side). Signed-off-by: Kővágó, Zoltán Acked-by: Markus Armbruster Message-id: 8a8b0b5698401b78d3c4c8ec90aef83b95babb06.1580672076.git.DirtY.iCE.hu@gmail.com Signed-off-by: Gerd Hoffmann --- audio/alsaaudio.c | 17 ++++++++ audio/audio.c | 56 ++++++++++++++++--------- audio/audio_int.h | 3 +- audio/audio_template.h | 41 +++++++++++-------- audio/coreaudio.c | 7 +--- audio/mixeng.c | 92 ++++++++++++++++++++++++++---------------- audio/mixeng.h | 8 ++-- audio/paaudio.c | 9 +++++ audio/sdlaudio.c | 28 +++++++++++++ qapi/audio.json | 2 +- 10 files changed, 182 insertions(+), 81 deletions(-) diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c index 4ef26818be..a23a5a0b60 100644 --- a/audio/alsaaudio.c +++ b/audio/alsaaudio.c @@ -307,6 +307,13 @@ static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness) return SND_PCM_FORMAT_U32_LE; } + case AUDIO_FORMAT_F32: + if (endianness) { + return SND_PCM_FORMAT_FLOAT_BE; + } else { + return SND_PCM_FORMAT_FLOAT_LE; + } + default: dolog ("Internal logic error: Bad audio format %d\n", fmt); #ifdef DEBUG_AUDIO @@ -370,6 +377,16 @@ static int alsa_to_audfmt (snd_pcm_format_t alsafmt, AudioFormat *fmt, *fmt = AUDIO_FORMAT_U32; break; + case SND_PCM_FORMAT_FLOAT_LE: + *endianness = 0; + *fmt = AUDIO_FORMAT_F32; + break; + + case SND_PCM_FORMAT_FLOAT_BE: + *endianness = 1; + *fmt = AUDIO_FORMAT_F32; + break; + default: dolog ("Unrecognized audio format %d\n", alsafmt); return -1; diff --git a/audio/audio.c b/audio/audio.c index 3bfd808bc6..9ac9a20c41 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -218,6 +218,9 @@ static void audio_print_settings (struct audsettings *as) case AUDIO_FORMAT_U32: AUD_log (NULL, "U32"); break; + case AUDIO_FORMAT_F32: + AUD_log (NULL, "F32"); + break; default: AUD_log (NULL, "invalid(%d)", as->fmt); break; @@ -252,6 +255,7 @@ static int audio_validate_settings (struct audsettings *as) case AUDIO_FORMAT_U16: case AUDIO_FORMAT_S32: case AUDIO_FORMAT_U32: + case AUDIO_FORMAT_F32: break; default: invalid = 1; @@ -264,24 +268,28 @@ static int audio_validate_settings (struct audsettings *as) static int audio_pcm_info_eq (struct audio_pcm_info *info, struct audsettings *as) { - int bits = 8, sign = 0; + int bits = 8; + bool is_signed = false, is_float = false; switch (as->fmt) { case AUDIO_FORMAT_S8: - sign = 1; + is_signed = true; /* fall through */ case AUDIO_FORMAT_U8: break; case AUDIO_FORMAT_S16: - sign = 1; + is_signed = true; /* fall through */ case AUDIO_FORMAT_U16: bits = 16; break; + case AUDIO_FORMAT_F32: + is_float = true; + /* fall through */ case AUDIO_FORMAT_S32: - sign = 1; + is_signed = true; /* fall through */ case AUDIO_FORMAT_U32: bits = 32; @@ -292,33 +300,38 @@ static int audio_pcm_info_eq (struct audio_pcm_info *info, struct audsettings *a } return info->freq == as->freq && info->nchannels == as->nchannels - && info->sign == sign + && info->is_signed == is_signed + && info->is_float == is_float && info->bits == bits && info->swap_endianness == (as->endianness != AUDIO_HOST_ENDIANNESS); } void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as) { - int bits = 8, sign = 0, mul; + int bits = 8, mul; + bool is_signed = false, is_float = false; switch (as->fmt) { case AUDIO_FORMAT_S8: - sign = 1; + is_signed = true; /* fall through */ case AUDIO_FORMAT_U8: mul = 1; break; case AUDIO_FORMAT_S16: - sign = 1; + is_signed = true; /* fall through */ case AUDIO_FORMAT_U16: bits = 16; mul = 2; break; + case AUDIO_FORMAT_F32: + is_float = true; + /* fall through */ case AUDIO_FORMAT_S32: - sign = 1; + is_signed = true; /* fall through */ case AUDIO_FORMAT_U32: bits = 32; @@ -331,7 +344,8 @@ void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as) info->freq = as->freq; info->bits = bits; - info->sign = sign; + info->is_signed = is_signed; + info->is_float = is_float; info->nchannels = as->nchannels; info->bytes_per_frame = as->nchannels * mul; info->bytes_per_second = info->freq * info->bytes_per_frame; @@ -344,7 +358,7 @@ void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len) return; } - if (info->sign) { + if (info->is_signed || info->is_float) { memset(buf, 0x00, len * info->bytes_per_frame); } else { @@ -770,8 +784,9 @@ static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t size) #ifdef DEBUG_AUDIO static void audio_pcm_print_info (const char *cap, struct audio_pcm_info *info) { - dolog ("%s: bits %d, sign %d, freq %d, nchan %d\n", - cap, info->bits, info->sign, info->freq, info->nchannels); + dolog("%s: bits %d, sign %d, float %d, freq %d, nchan %d\n", + cap, info->bits, info->is_signed, info->is_float, info->freq, + info->nchannels); } #endif @@ -1832,11 +1847,15 @@ CaptureVoiceOut *AUD_add_capture( cap->buf = g_malloc0_n(hw->mix_buf->size, hw->info.bytes_per_frame); - hw->clip = mixeng_clip - [hw->info.nchannels == 2] - [hw->info.sign] - [hw->info.swap_endianness] - [audio_bits_to_index (hw->info.bits)]; + if (hw->info.is_float) { + hw->clip = mixeng_clip_float[hw->info.nchannels == 2]; + } else { + hw->clip = mixeng_clip + [hw->info.nchannels == 2] + [hw->info.is_signed] + [hw->info.swap_endianness] + [audio_bits_to_index(hw->info.bits)]; + } QLIST_INSERT_HEAD (&s->cap_head, cap, entries); QLIST_INSERT_HEAD (&cap->cb_head, cb, entries); @@ -2075,6 +2094,7 @@ int audioformat_bytes_per_sample(AudioFormat fmt) case AUDIO_FORMAT_U32: case AUDIO_FORMAT_S32: + case AUDIO_FORMAT_F32: return 4; case AUDIO_FORMAT__MAX: diff --git a/audio/audio_int.h b/audio/audio_int.h index 3c8e48b55b..4775857bf2 100644 --- a/audio/audio_int.h +++ b/audio/audio_int.h @@ -40,7 +40,8 @@ struct audio_callback { struct audio_pcm_info { int bits; - int sign; + bool is_signed; + bool is_float; int freq; int nchannels; int bytes_per_frame; diff --git a/audio/audio_template.h b/audio/audio_template.h index 0336d2670c..7013d3041f 100644 --- a/audio/audio_template.h +++ b/audio/audio_template.h @@ -153,15 +153,23 @@ static int glue (audio_pcm_sw_init_, TYPE) ( sw->ratio = ((int64_t) sw->info.freq << 32) / sw->hw->info.freq; #endif + if (sw->info.is_float) { #ifdef DAC - sw->conv = mixeng_conv + sw->conv = mixeng_conv_float[sw->info.nchannels == 2]; #else - sw->clip = mixeng_clip + sw->clip = mixeng_clip_float[sw->info.nchannels == 2]; #endif - [sw->info.nchannels == 2] - [sw->info.sign] - [sw->info.swap_endianness] - [audio_bits_to_index (sw->info.bits)]; + } else { +#ifdef DAC + sw->conv = mixeng_conv +#else + sw->clip = mixeng_clip +#endif + [sw->info.nchannels == 2] + [sw->info.is_signed] + [sw->info.swap_endianness] + [audio_bits_to_index(sw->info.bits)]; + } sw->name = g_strdup (name); err = glue (audio_pcm_sw_alloc_resources_, TYPE) (sw); @@ -276,22 +284,23 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s, goto err1; } - if (s->dev->driver == AUDIODEV_DRIVER_COREAUDIO) { + if (hw->info.is_float) { #ifdef DAC - hw->clip = clip_natural_float_from_stereo; + hw->clip = mixeng_clip_float[hw->info.nchannels == 2]; #else - hw->conv = conv_natural_float_to_stereo; + hw->conv = mixeng_conv_float[hw->info.nchannels == 2]; #endif - } else + } else { #ifdef DAC - hw->clip = mixeng_clip + hw->clip = mixeng_clip #else - hw->conv = mixeng_conv + hw->conv = mixeng_conv #endif - [hw->info.nchannels == 2] - [hw->info.sign] - [hw->info.swap_endianness] - [audio_bits_to_index (hw->info.bits)]; + [hw->info.nchannels == 2] + [hw->info.is_signed] + [hw->info.swap_endianness] + [audio_bits_to_index(hw->info.bits)]; + } glue(audio_pcm_hw_alloc_resources_, TYPE)(hw); diff --git a/audio/coreaudio.c b/audio/coreaudio.c index e3620b274b..4b4365660f 100644 --- a/audio/coreaudio.c +++ b/audio/coreaudio.c @@ -491,14 +491,9 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, return -1; } - /* - * The canonical audio format for CoreAudio on macOS is float. Currently - * there is no generic code for AUDIO_FORMAT_F32 in qemu. Here we select - * AUDIO_FORMAT_S32 instead because only the sample size has to match. - */ fake_as = *as; as = &fake_as; - as->fmt = AUDIO_FORMAT_S32; + as->fmt = AUDIO_FORMAT_F32; audio_pcm_init_info (&hw->info, as); status = coreaudio_get_voice(&core->outputDeviceID); diff --git a/audio/mixeng.c b/audio/mixeng.c index 16b646d48c..c14b0d874c 100644 --- a/audio/mixeng.c +++ b/audio/mixeng.c @@ -267,55 +267,77 @@ f_sample *mixeng_clip[2][2][2][3] = { } }; -void conv_natural_float_to_stereo(struct st_sample *dst, const void *src, - int samples) -{ - float *in = (float *)src; -#ifndef FLOAT_MIXENG - const float scale = UINT_MAX; +#ifdef FLOAT_MIXENG +#define FLOAT_CONV_TO(x) (x) +#define FLOAT_CONV_FROM(x) (x) +#else +static const float float_scale = UINT_MAX; +#define FLOAT_CONV_TO(x) ((x) * float_scale) + +#ifdef RECIPROCAL +static const float float_scale_reciprocal = 1.f / UINT_MAX; +#define FLOAT_CONV_FROM(x) ((x) * float_scale_reciprocal) +#else +#define FLOAT_CONV_FROM(x) ((x) / float_scale) +#endif #endif +static void conv_natural_float_to_mono(struct st_sample *dst, const void *src, + int samples) +{ + float *in = (float *)src; + while (samples--) { -#ifdef FLOAT_MIXENG - dst->l = *in++; - dst->r = *in++; -#else - dst->l = *in++ * scale; - dst->r = *in++ * scale; -#endif + dst->r = dst->l = FLOAT_CONV_TO(*in++); dst++; } } -void clip_natural_float_from_stereo(void *dst, const struct st_sample *src, - int samples) +static void conv_natural_float_to_stereo(struct st_sample *dst, const void *src, + int samples) { - float *out = (float *)dst; -#ifndef FLOAT_MIXENG -#ifdef RECIPROCAL - const float scale = 1.f / UINT_MAX; -#else - const float scale = UINT_MAX; -#endif -#endif + float *in = (float *)src; while (samples--) { -#ifdef FLOAT_MIXENG - *out++ = src->l; - *out++ = src->r; -#else -#ifdef RECIPROCAL - *out++ = src->l * scale; - *out++ = src->r * scale; -#else - *out++ = src->l / scale; - *out++ = src->r / scale; -#endif -#endif + dst->l = FLOAT_CONV_TO(*in++); + dst->r = FLOAT_CONV_TO(*in++); + dst++; + } +} + +t_sample *mixeng_conv_float[2] = { + conv_natural_float_to_mono, + conv_natural_float_to_stereo, +}; + +static void clip_natural_float_from_mono(void *dst, const struct st_sample *src, + int samples) +{ + float *out = (float *)dst; + + while (samples--) { + *out++ = FLOAT_CONV_FROM(src->l) + FLOAT_CONV_FROM(src->r); src++; } } +static void clip_natural_float_from_stereo( + void *dst, const struct st_sample *src, int samples) +{ + float *out = (float *)dst; + + while (samples--) { + *out++ = FLOAT_CONV_FROM(src->l); + *out++ = FLOAT_CONV_FROM(src->r); + src++; + } +} + +f_sample *mixeng_clip_float[2] = { + clip_natural_float_from_mono, + clip_natural_float_from_stereo, +}; + void audio_sample_to_uint64(void *samples, int pos, uint64_t *left, uint64_t *right) { diff --git a/audio/mixeng.h b/audio/mixeng.h index 7ef61763e8..2dcd6df245 100644 --- a/audio/mixeng.h +++ b/audio/mixeng.h @@ -38,13 +38,13 @@ typedef struct st_sample st_sample; typedef void (t_sample) (struct st_sample *dst, const void *src, int samples); typedef void (f_sample) (void *dst, const struct st_sample *src, int samples); +/* indices: [stereo][signed][swap endiannes][8, 16 or 32-bits] */ extern t_sample *mixeng_conv[2][2][2][3]; extern f_sample *mixeng_clip[2][2][2][3]; -void conv_natural_float_to_stereo(struct st_sample *dst, const void *src, - int samples); -void clip_natural_float_from_stereo(void *dst, const struct st_sample *src, - int samples); +/* indices: [stereo] */ +extern t_sample *mixeng_conv_float[2]; +extern f_sample *mixeng_clip_float[2]; void *st_rate_start (int inrate, int outrate); void st_rate_flow(void *opaque, st_sample *ibuf, st_sample *obuf, diff --git a/audio/paaudio.c b/audio/paaudio.c index 8f37c61851..b052084698 100644 --- a/audio/paaudio.c +++ b/audio/paaudio.c @@ -277,6 +277,9 @@ static pa_sample_format_t audfmt_to_pa (AudioFormat afmt, int endianness) 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; @@ -302,6 +305,12 @@ static AudioFormat pa_to_audfmt (pa_sample_format_t fmt, int *endianness) 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; diff --git a/audio/sdlaudio.c b/audio/sdlaudio.c index c00e7d7845..21b7a0484b 100644 --- a/audio/sdlaudio.c +++ b/audio/sdlaudio.c @@ -77,6 +77,14 @@ static int aud_to_sdlfmt (AudioFormat fmt) case AUDIO_FORMAT_U16: return AUDIO_U16LSB; + case AUDIO_FORMAT_S32: + return AUDIO_S32LSB; + + /* no unsigned 32-bit support in SDL */ + + case AUDIO_FORMAT_F32: + return AUDIO_F32LSB; + default: dolog ("Internal logic error: Bad audio format %d\n", fmt); #ifdef DEBUG_AUDIO @@ -119,6 +127,26 @@ static int sdl_to_audfmt(int sdlfmt, AudioFormat *fmt, int *endianness) *fmt = AUDIO_FORMAT_U16; break; + case AUDIO_S32LSB: + *endianness = 0; + *fmt = AUDIO_FORMAT_S32; + break; + + case AUDIO_S32MSB: + *endianness = 1; + *fmt = AUDIO_FORMAT_S32; + break; + + case AUDIO_F32LSB: + *endianness = 0; + *fmt = AUDIO_FORMAT_F32; + break; + + case AUDIO_F32MSB: + *endianness = 1; + *fmt = AUDIO_FORMAT_F32; + break; + default: dolog ("Unrecognized SDL audio format %d\n", sdlfmt); return -1; diff --git a/qapi/audio.json b/qapi/audio.json index 83312b2339..d8c507cced 100644 --- a/qapi/audio.json +++ b/qapi/audio.json @@ -276,7 +276,7 @@ # Since: 4.0 ## { 'enum': 'AudioFormat', - 'data': [ 'u8', 's8', 'u16', 's16', 'u32', 's32' ] } + 'data': [ 'u8', 's8', 'u16', 's16', 'u32', 's32', 'f32' ] } ## # @AudiodevDriver: