sdlaudio: add recording functions

Add audio recording functions. SDL 2.0.5 or later is required to
use the recording functions. Playback continues to work with
earlier SDL 2.0 versions.

Signed-off-by: Volker Rümelin <vr_qemu@t-online.de>
Message-id: 9315afe5-5958-c0b4-ea1e-14769511a9d5@t-online.de
Message-Id: <20210110100239.27588-9-vr_qemu@t-online.de>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
This commit is contained in:
Volker Rümelin 2021-01-10 11:02:25 +01:00 committed by Gerd Hoffmann
parent a2893c8303
commit c2031dea89

View file

@ -47,6 +47,14 @@ typedef struct SDLVoiceOut {
SDL_AudioDeviceID devid;
} SDLVoiceOut;
typedef struct SDLVoiceIn {
HWVoiceIn hw;
int exit;
int initialized;
Audiodev *dev;
SDL_AudioDeviceID devid;
} SDLVoiceIn;
static void GCC_FMT_ATTR (1, 2) sdl_logerr (const char *fmt, ...)
{
va_list ap;
@ -240,6 +248,45 @@ static void sdl_callback_out(void *opaque, Uint8 *buf, int len)
}
}
static void sdl_close_in(SDLVoiceIn *sdl)
{
if (sdl->initialized) {
SDL_LockAudioDevice(sdl->devid);
sdl->exit = 1;
SDL_UnlockAudioDevice(sdl->devid);
SDL_PauseAudioDevice(sdl->devid, 1);
sdl->initialized = 0;
}
if (sdl->devid) {
SDL_CloseAudioDevice(sdl->devid);
sdl->devid = 0;
}
}
static void sdl_callback_in(void *opaque, Uint8 *buf, int len)
{
SDLVoiceIn *sdl = opaque;
HWVoiceIn *hw = &sdl->hw;
if (sdl->exit) {
return;
}
/* dolog("callback_in: len=%d pending=%zu\n", len, hw->pending_emul); */
while (hw->pending_emul < hw->size_emul && len) {
size_t read_len = MIN(len, MIN(hw->size_emul - hw->pos_emul,
hw->size_emul - hw->pending_emul));
memcpy(hw->buf_emul + hw->pos_emul, buf, read_len);
hw->pending_emul += read_len;
hw->pos_emul = (hw->pos_emul + read_len) % hw->size_emul;
len -= read_len;
buf += read_len;
}
}
#define SDL_WRAPPER_FUNC(name, ret_type, args_decl, args, dir) \
static ret_type glue(sdl_, name)args_decl \
{ \
@ -253,13 +300,30 @@ static void sdl_callback_out(void *opaque, Uint8 *buf, int len)
return ret; \
}
#define SDL_WRAPPER_VOID_FUNC(name, args_decl, args, dir) \
static void glue(sdl_, name)args_decl \
{ \
glue(SDLVoice, dir) *sdl = (glue(SDLVoice, dir) *)hw; \
\
SDL_LockAudioDevice(sdl->devid); \
glue(audio_generic_, name)args; \
SDL_UnlockAudioDevice(sdl->devid); \
}
SDL_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size),
(hw, size), Out)
SDL_WRAPPER_FUNC(put_buffer_out, size_t,
(HWVoiceOut *hw, void *buf, size_t size), (hw, buf, size), Out)
SDL_WRAPPER_FUNC(write, size_t,
(HWVoiceOut *hw, void *buf, size_t size), (hw, buf, size), Out)
SDL_WRAPPER_FUNC(read, size_t, (HWVoiceIn *hw, void *buf, size_t size),
(hw, buf, size), In)
SDL_WRAPPER_FUNC(get_buffer_in, void *, (HWVoiceIn *hw, size_t *size),
(hw, size), In)
SDL_WRAPPER_VOID_FUNC(put_buffer_in, (HWVoiceIn *hw, void *buf, size_t size),
(hw, buf, size), In)
#undef SDL_WRAPPER_FUNC
#undef SDL_WRAPPER_VOID_FUNC
static void sdl_fini_out(HWVoiceOut *hw)
{
@ -325,6 +389,69 @@ static void sdl_enable_out(HWVoiceOut *hw, bool enable)
SDL_PauseAudioDevice(sdl->devid, !enable);
}
static void sdl_fini_in(HWVoiceIn *hw)
{
SDLVoiceIn *sdl = (SDLVoiceIn *)hw;
sdl_close_in(sdl);
}
static int sdl_init_in(HWVoiceIn *hw, audsettings *as, void *drv_opaque)
{
SDLVoiceIn *sdl = (SDLVoiceIn *)hw;
SDL_AudioSpec req, obt;
int endianness;
int err;
AudioFormat effective_fmt;
Audiodev *dev = drv_opaque;
AudiodevSdlPerDirectionOptions *spdo = dev->u.sdl.in;
struct audsettings obt_as;
req.freq = as->freq;
req.format = aud_to_sdlfmt(as->fmt);
req.channels = as->nchannels;
/* SDL samples are QEMU frames */
req.samples = audio_buffer_frames(
qapi_AudiodevSdlPerDirectionOptions_base(spdo), as, 11610);
req.callback = sdl_callback_in;
req.userdata = sdl;
sdl->dev = dev;
sdl->devid = sdl_open(&req, &obt, 1);
if (!sdl->devid) {
return -1;
}
err = sdl_to_audfmt(obt.format, &effective_fmt, &endianness);
if (err) {
sdl_close_in(sdl);
return -1;
}
obt_as.freq = obt.freq;
obt_as.nchannels = obt.channels;
obt_as.fmt = effective_fmt;
obt_as.endianness = endianness;
audio_pcm_init_info(&hw->info, &obt_as);
hw->samples = (spdo->has_buffer_count ? spdo->buffer_count : 4) *
obt.samples;
hw->size_emul = hw->samples * hw->info.bytes_per_frame;
hw->buf_emul = g_malloc(hw->size_emul);
hw->pos_emul = hw->pending_emul = 0;
sdl->initialized = 1;
sdl->exit = 0;
return 0;
}
static void sdl_enable_in(HWVoiceIn *hw, bool enable)
{
SDLVoiceIn *sdl = (SDLVoiceIn *)hw;
SDL_PauseAudioDevice(sdl->devid, !enable);
}
static void *sdl_audio_init(Audiodev *dev)
{
if (SDL_InitSubSystem (SDL_INIT_AUDIO)) {
@ -350,6 +477,15 @@ static struct audio_pcm_ops sdl_pcm_ops = {
/* wrapper for audio_generic_put_buffer_out */
.put_buffer_out = sdl_put_buffer_out,
.enable_out = sdl_enable_out,
.init_in = sdl_init_in,
.fini_in = sdl_fini_in,
/* wrapper for audio_generic_read */
.read = sdl_read,
/* wrapper for audio_generic_get_buffer_in */
.get_buffer_in = sdl_get_buffer_in,
/* wrapper for audio_generic_put_buffer_in */
.put_buffer_in = sdl_put_buffer_in,
.enable_in = sdl_enable_in,
};
static struct audio_driver sdl_audio_driver = {
@ -360,9 +496,9 @@ static struct audio_driver sdl_audio_driver = {
.pcm_ops = &sdl_pcm_ops,
.can_be_default = 1,
.max_voices_out = 1,
.max_voices_in = 0,
.voice_size_out = sizeof (SDLVoiceOut),
.voice_size_in = 0
.max_voices_in = 1,
.voice_size_out = sizeof(SDLVoiceOut),
.voice_size_in = sizeof(SDLVoiceIn),
};
static void register_audio_sdl(void)