diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2021-01-15 22:21:21 +0000 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2021-01-15 22:21:21 +0000 |
commit | 825a215c003cd028e26c7d19aa5049d957345f43 (patch) | |
tree | a3bedcc1d73490abbe5994b065289147f8d0b10e | |
parent | 7cb6b97300f0405b4c6856c49bdc33fa3265852f (diff) | |
parent | 8abf3feb4d464abadd5133d8810c8a3232cbbe6e (diff) |
Merge remote-tracking branch 'remotes/kraxel/tags/audio-20210115-pull-request' into staging
audio: improvements for sdl, pulse, fsound.
audio: cleanups & codestyle fixes.
# gpg: Signature made Fri 15 Jan 2021 13:20:56 GMT
# gpg: using RSA key A0328CFFB93A17A79901FE7D4CB6D8EED3E87138
# gpg: Good signature from "Gerd Hoffmann (work) <kraxel@redhat.com>" [full]
# gpg: aka "Gerd Hoffmann <gerd@kraxel.org>" [full]
# gpg: aka "Gerd Hoffmann (private) <kraxel@gmail.com>" [full]
# Primary key fingerprint: A032 8CFF B93A 17A7 9901 FE7D 4CB6 D8EE D3E8 7138
* remotes/kraxel/tags/audio-20210115-pull-request: (30 commits)
audio: space prohibited between function name and parenthesis'('
audio: Suspect code indent for conditional statements
audio: Don't use '%#' in format strings
audio: Fix lines over 90 characters
audio: foo* bar" should be "foo *bar".
audio: Add spaces around operator/delete redundant spaces
audio: Add braces for statements/fix braces' position
dsoundaudio: fix log message
dsoundaudio: enable f32 audio sample format
dsoundaudio: rename dsound_open()
dsoundaudio: replace GetForegroundWindow()
paaudio: send recorded data in smaller chunks
paaudio: limit minreq to 75% of audio timer_rate
paaudio: comment bugs in functions qpa_init_*
paaudio: remove unneeded code
paaudio: wait until the playback stream is ready
paaudio: wait for PA_STREAM_READY in qpa_write()
paaudio: avoid to clip samples multiple times
audio: remove remaining unused plive code
sdlaudio: enable (in|out).mixing-engine=off
...
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r-- | audio/alsaaudio.c | 18 | ||||
-rw-r--r-- | audio/audio.c | 74 | ||||
-rw-r--r-- | audio/audio_int.h | 2 | ||||
-rw-r--r-- | audio/audio_legacy.c | 3 | ||||
-rw-r--r-- | audio/audio_template.h | 26 | ||||
-rw-r--r-- | audio/audio_win_int.c | 73 | ||||
-rw-r--r-- | audio/coreaudio.c | 17 | ||||
-rw-r--r-- | audio/dsound_template.h | 2 | ||||
-rw-r--r-- | audio/dsoundaudio.c | 56 | ||||
-rw-r--r-- | audio/jackaudio.c | 3 | ||||
-rw-r--r-- | audio/noaudio.c | 1 | ||||
-rw-r--r-- | audio/ossaudio.c | 13 | ||||
-rw-r--r-- | audio/paaudio.c | 73 | ||||
-rw-r--r-- | audio/sdlaudio.c | 305 | ||||
-rw-r--r-- | audio/spiceaudio.c | 1 | ||||
-rw-r--r-- | qapi/audio.json | 33 | ||||
-rw-r--r-- | qemu-options.hx | 8 |
17 files changed, 472 insertions, 236 deletions
diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c index a8e62542f9..fcc2f62864 100644 --- a/audio/alsaaudio.c +++ b/audio/alsaaudio.c @@ -278,32 +278,28 @@ static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness) case AUDIO_FORMAT_S16: if (endianness) { return SND_PCM_FORMAT_S16_BE; - } - else { + } else { return SND_PCM_FORMAT_S16_LE; } case AUDIO_FORMAT_U16: if (endianness) { return SND_PCM_FORMAT_U16_BE; - } - else { + } else { return SND_PCM_FORMAT_U16_LE; } case AUDIO_FORMAT_S32: if (endianness) { return SND_PCM_FORMAT_S32_BE; - } - else { + } else { return SND_PCM_FORMAT_S32_LE; } case AUDIO_FORMAT_U32: if (endianness) { return SND_PCM_FORMAT_U32_BE; - } - else { + } else { return SND_PCM_FORMAT_U32_LE; } @@ -599,7 +595,7 @@ static int alsa_open(bool in, struct alsa_params_req *req, } #ifdef DEBUG - alsa_dump_info(req, obt, obtfmt, pdo); + alsa_dump_info(req, obt, obtfmt, apdo); #endif return 0; @@ -722,8 +718,7 @@ static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int ctl) alsa_logerr (err, "Could not stop %s\n", typ); return -1; } - } - else { + } else { err = snd_pcm_prepare (handle); if (err < 0) { alsa_logerr (err, "Could not prepare handle for %s\n", typ); @@ -929,6 +924,7 @@ static struct audio_pcm_ops alsa_pcm_ops = { .init_in = alsa_init_in, .fini_in = alsa_fini_in, .read = alsa_read, + .run_buffer_in = audio_generic_run_buffer_in, .enable_in = alsa_enable_in, }; diff --git a/audio/audio.c b/audio/audio.c index b48471bb3f..6734c8af70 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -344,8 +344,7 @@ void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len) if (info->is_signed || info->is_float) { memset(buf, 0x00, len * info->bytes_per_frame); - } - else { + } else { switch (info->bits) { case 8: memset(buf, 0x80, len * info->bytes_per_frame); @@ -584,8 +583,7 @@ static size_t audio_pcm_sw_get_rpos_in(SWVoiceIn *sw) rpos = hw->conv_buf->pos - live; if (rpos >= 0) { return rpos; - } - else { + } else { return hw->conv_buf->size + rpos; } } @@ -788,10 +786,14 @@ static int audio_is_timer_needed(AudioState *s) HWVoiceOut *hwo = NULL; while ((hwo = audio_pcm_hw_find_any_enabled_out(s, hwo))) { - if (!hwo->poll_mode) return 1; + if (!hwo->poll_mode) { + return 1; + } } while ((hwi = audio_pcm_hw_find_any_enabled_in(s, hwi))) { - if (!hwi->poll_mode) return 1; + if (!hwi->poll_mode) { + return 1; + } } return 0; } @@ -908,8 +910,7 @@ void AUD_set_active_out (SWVoiceOut *sw, int on) audio_reset_timer (s); } } - } - else { + } else { if (hw->enabled) { int nb_active = 0; @@ -956,8 +957,7 @@ void AUD_set_active_in (SWVoiceIn *sw, int on) } } sw->total_hw_samples_acquired = hw->total_samples_captured; - } - else { + } else { if (hw->enabled) { int nb_active = 0; @@ -1132,7 +1132,7 @@ static void audio_run_out (AudioState *s) while ((hw = audio_pcm_hw_find_any_enabled_out(s, hw))) { size_t played, live, prev_rpos, free; - int nb_live, cleanup_required; + int nb_live; live = audio_pcm_hw_get_live_out (hw, &nb_live); if (!nb_live) { @@ -1194,7 +1194,6 @@ static void audio_run_out (AudioState *s) audio_capture_mix_and_clear (hw, prev_rpos, played); } - cleanup_required = 0; for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { if (!sw->active && sw->empty) { continue; @@ -1210,7 +1209,6 @@ static void audio_run_out (AudioState *s) if (!sw->total_hw_samples_mixed) { sw->empty = 1; - cleanup_required |= !sw->active && !sw->callback.fn; } if (sw->active) { @@ -1220,19 +1218,6 @@ static void audio_run_out (AudioState *s) } } } - - if (cleanup_required) { - SWVoiceOut *sw1; - - sw = hw->sw_head.lh_first; - while (sw) { - sw1 = sw->entries.le_next; - if (!sw->active && !sw->callback.fn) { - audio_close_out (sw); - } - sw = sw1; - } - } } } @@ -1241,6 +1226,10 @@ static size_t audio_pcm_hw_run_in(HWVoiceIn *hw, size_t samples) size_t conv = 0; STSampleBuffer *conv_buf = hw->conv_buf; + if (hw->pcm_ops->run_buffer_in) { + hw->pcm_ops->run_buffer_in(hw); + } + while (samples) { size_t proc; size_t size = samples * hw->info.bytes_per_frame; @@ -1381,14 +1370,11 @@ void audio_run(AudioState *s, const char *msg) #endif } -void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size) +void audio_generic_run_buffer_in(HWVoiceIn *hw) { - ssize_t start; - if (unlikely(!hw->buf_emul)) { - size_t calc_size = hw->conv_buf->size * hw->info.bytes_per_frame; - hw->buf_emul = g_malloc(calc_size); - hw->size_emul = calc_size; + 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; } @@ -1403,8 +1389,12 @@ void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size) break; } } +} + +void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size) +{ + ssize_t start = (ssize_t)hw->pos_emul - hw->pending_emul; - start = ((ssize_t) hw->pos_emul) - hw->pending_emul; if (start < 0) { start += hw->size_emul; } @@ -1446,10 +1436,8 @@ void audio_generic_run_buffer_out(HWVoiceOut *hw) void *audio_generic_get_buffer_out(HWVoiceOut *hw, size_t *size) { if (unlikely(!hw->buf_emul)) { - size_t calc_size = hw->mix_buf->size * hw->info.bytes_per_frame; - - hw->buf_emul = g_malloc(calc_size); - hw->size_emul = calc_size; + 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; } @@ -1505,6 +1493,10 @@ size_t audio_generic_read(HWVoiceIn *hw, void *buf, size_t size) { size_t total = 0; + if (hw->pcm_ops->run_buffer_in) { + hw->pcm_ops->run_buffer_in(hw); + } + while (total < size) { size_t src_size = size - total; void *src = hw->pcm_ops->get_buffer_in(hw, &src_size); @@ -1540,8 +1532,7 @@ static int audio_driver_init(AudioState *s, struct audio_driver *drv, audio_init_nb_voices_in(s, drv); s->drv = drv; return 0; - } - else { + } else { if (msg) { dolog("Could not init `%s' audio driver\n", drv->name); } @@ -1856,8 +1847,7 @@ CaptureVoiceOut *AUD_add_capture( if (cap) { QLIST_INSERT_HEAD (&cap->cb_head, cb, entries); return cap; - } - else { + } else { HWVoiceOut *hw; CaptureVoiceOut *cap; @@ -2003,7 +1993,7 @@ void audio_create_pdos(Audiodev *dev) CASE(JACK, jack, Jack); CASE(OSS, oss, Oss); CASE(PA, pa, Pa); - CASE(SDL, sdl, ); + CASE(SDL, sdl, Sdl); CASE(SPICE, spice, ); CASE(WAV, wav, ); diff --git a/audio/audio_int.h b/audio/audio_int.h index 4775857bf2..06f0913835 100644 --- a/audio/audio_int.h +++ b/audio/audio_int.h @@ -172,12 +172,14 @@ struct audio_pcm_ops { int (*init_in) (HWVoiceIn *hw, audsettings *as, void *drv_opaque); void (*fini_in) (HWVoiceIn *hw); size_t (*read) (HWVoiceIn *hw, void *buf, size_t size); + void (*run_buffer_in)(HWVoiceIn *hw); void *(*get_buffer_in)(HWVoiceIn *hw, size_t *size); void (*put_buffer_in)(HWVoiceIn *hw, void *buf, size_t size); void (*enable_in)(HWVoiceIn *hw, bool enable); void (*volume_in)(HWVoiceIn *hw, Volume *vol); }; +void audio_generic_run_buffer_in(HWVoiceIn *hw); 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); diff --git a/audio/audio_legacy.c b/audio/audio_legacy.c index ffdbd0bcce..0fe827b057 100644 --- a/audio/audio_legacy.c +++ b/audio/audio_legacy.c @@ -286,7 +286,8 @@ static void handle_sdl(Audiodev *dev) { /* SDL is output only */ get_samples_to_usecs("QEMU_SDL_SAMPLES", &dev->u.sdl.out->buffer_length, - &dev->u.sdl.out->has_buffer_length, dev->u.sdl.out); + &dev->u.sdl.out->has_buffer_length, + qapi_AudiodevSdlPerDirectionOptions_base(dev->u.sdl.out)); } /* wav */ diff --git a/audio/audio_template.h b/audio/audio_template.h index 8dd48ce14e..c6714946aa 100644 --- a/audio/audio_template.h +++ b/audio/audio_template.h @@ -47,8 +47,7 @@ static void glue(audio_init_nb_voices_, TYPE)(AudioState *s, #ifdef DAC dolog ("Driver `%s' does not support " NAME "\n", drv->name); #endif - } - else { + } else { dolog ("Driver `%s' does not support %d " NAME " voices, max %d\n", drv->name, glue (s->nb_hw_voices_, TYPE), @@ -204,13 +203,13 @@ static void glue (audio_pcm_hw_gc_, TYPE) (HW **hwp) if (!hw->sw_head.lh_first) { #ifdef DAC - audio_detach_capture (hw); + audio_detach_capture(hw); #endif - QLIST_REMOVE (hw, entries); - glue (hw->pcm_ops->fini_, TYPE) (hw); - glue (s->nb_hw_voices_, TYPE) += 1; - glue (audio_pcm_hw_free_resources_ ,TYPE) (hw); - g_free (hw); + QLIST_REMOVE(hw, entries); + glue(hw->pcm_ops->fini_, TYPE) (hw); + glue(s->nb_hw_voices_, TYPE) += 1; + glue(audio_pcm_hw_free_resources_ , TYPE) (hw); + g_free(hw); *hwp = NULL; } } @@ -337,7 +336,7 @@ AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)(Audiodev *dev) case AUDIODEV_DRIVER_PA: return qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.TYPE); case AUDIODEV_DRIVER_SDL: - return dev->u.sdl.TYPE; + return qapi_AudiodevSdlPerDirectionOptions_base(dev->u.sdl.TYPE); case AUDIODEV_DRIVER_SPICE: return dev->u.spice.TYPE; case AUDIODEV_DRIVER_WAV: @@ -387,8 +386,7 @@ static SW *glue(audio_pcm_create_voice_pair_, TYPE)( if (pdo->fixed_settings) { hw_as = audiodev_to_audsettings(pdo); - } - else { + } else { hw_as = *as; } @@ -498,8 +496,7 @@ SW *glue (AUD_open_, TYPE) ( if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, name, as)) { goto fail; } - } - else { + } else { sw = glue(audio_pcm_create_voice_pair_, TYPE)(s, name, as); if (!sw) { dolog ("Failed to create voice `%s'\n", name); @@ -553,8 +550,7 @@ uint64_t glue (AUD_get_elapsed_usec_, TYPE) (SW *sw, QEMUAudioTimeStamp *ts) if (cur_ts >= old_ts) { delta = cur_ts - old_ts; - } - else { + } else { delta = UINT64_MAX - old_ts + cur_ts; } diff --git a/audio/audio_win_int.c b/audio/audio_win_int.c index b938fd667b..5ea8157dfc 100644 --- a/audio/audio_win_int.c +++ b/audio/audio_win_int.c @@ -5,6 +5,7 @@ #define AUDIO_CAP "win-int" #include <windows.h> +#include <mmreg.h> #include <mmsystem.h> #include "audio.h" @@ -16,7 +17,6 @@ int waveformat_from_audio_settings (WAVEFORMATEX *wfx, { memset (wfx, 0, sizeof (*wfx)); - wfx->wFormatTag = WAVE_FORMAT_PCM; wfx->nChannels = as->nchannels; wfx->nSamplesPerSec = as->freq; wfx->nAvgBytesPerSec = as->freq << (as->nchannels == 2); @@ -26,11 +26,13 @@ int waveformat_from_audio_settings (WAVEFORMATEX *wfx, switch (as->fmt) { case AUDIO_FORMAT_S8: case AUDIO_FORMAT_U8: + wfx->wFormatTag = WAVE_FORMAT_PCM; wfx->wBitsPerSample = 8; break; case AUDIO_FORMAT_S16: case AUDIO_FORMAT_U16: + wfx->wFormatTag = WAVE_FORMAT_PCM; wfx->wBitsPerSample = 16; wfx->nAvgBytesPerSec <<= 1; wfx->nBlockAlign <<= 1; @@ -38,13 +40,21 @@ int waveformat_from_audio_settings (WAVEFORMATEX *wfx, case AUDIO_FORMAT_S32: case AUDIO_FORMAT_U32: + wfx->wFormatTag = WAVE_FORMAT_PCM; + wfx->wBitsPerSample = 32; + wfx->nAvgBytesPerSec <<= 2; + wfx->nBlockAlign <<= 2; + break; + + case AUDIO_FORMAT_F32: + wfx->wFormatTag = WAVE_FORMAT_IEEE_FLOAT; wfx->wBitsPerSample = 32; wfx->nAvgBytesPerSec <<= 2; wfx->nBlockAlign <<= 2; break; default: - dolog ("Internal logic error: Bad audio format %d\n", as->freq); + dolog("Internal logic error: Bad audio format %d\n", as->fmt); return -1; } @@ -54,12 +64,6 @@ int waveformat_from_audio_settings (WAVEFORMATEX *wfx, int waveformat_to_audio_settings (WAVEFORMATEX *wfx, struct audsettings *as) { - if (wfx->wFormatTag != WAVE_FORMAT_PCM) { - dolog ("Invalid wave format, tag is not PCM, but %d\n", - wfx->wFormatTag); - return -1; - } - if (!wfx->nSamplesPerSec) { dolog ("Invalid wave format, frequency is zero\n"); return -1; @@ -83,23 +87,42 @@ int waveformat_to_audio_settings (WAVEFORMATEX *wfx, return -1; } - switch (wfx->wBitsPerSample) { - case 8: - as->fmt = AUDIO_FORMAT_U8; - break; - - case 16: - as->fmt = AUDIO_FORMAT_S16; - break; - - case 32: - as->fmt = AUDIO_FORMAT_S32; - break; - - default: - dolog ("Invalid wave format, bits per sample is not " - "8, 16 or 32, but %d\n", - wfx->wBitsPerSample); + if (wfx->wFormatTag == WAVE_FORMAT_PCM) { + switch (wfx->wBitsPerSample) { + case 8: + as->fmt = AUDIO_FORMAT_U8; + break; + + case 16: + as->fmt = AUDIO_FORMAT_S16; + break; + + case 32: + as->fmt = AUDIO_FORMAT_S32; + break; + + default: + dolog("Invalid PCM wave format, bits per sample is not " + "8, 16 or 32, but %d\n", + wfx->wBitsPerSample); + return -1; + } + } else if (wfx->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) { + switch (wfx->wBitsPerSample) { + case 32: + as->fmt = AUDIO_FORMAT_F32; + break; + + default: + dolog("Invalid IEEE_FLOAT wave format, bits per sample is not " + "32, but %d\n", + wfx->wBitsPerSample); + return -1; + } + } else { + dolog("Invalid wave format, tag is not PCM and not IEEE_FLOAT, " + "but %d\n", + wfx->wFormatTag); return -1; } diff --git a/audio/coreaudio.c b/audio/coreaudio.c index 79a9d40bf8..b7c02e0e51 100644 --- a/audio/coreaudio.c +++ b/audio/coreaudio.c @@ -270,7 +270,7 @@ static void coreaudio_logstatus (OSStatus status) { const char *str = "BUG"; - switch(status) { + switch (status) { case kAudioHardwareNoError: str = "kAudioHardwareNoError"; break; @@ -421,12 +421,12 @@ COREAUDIO_WRAPPER_FUNC(write, size_t, (HWVoiceOut *hw, void *buf, size_t size), /* callback to feed audiooutput buffer */ static OSStatus audioDeviceIOProc( AudioDeviceID inDevice, - const AudioTimeStamp* inNow, - const AudioBufferList* inInputData, - const AudioTimeStamp* inInputTime, - AudioBufferList* outOutputData, - const AudioTimeStamp* inOutputTime, - void* hwptr) + const AudioTimeStamp *inNow, + const AudioBufferList *inInputData, + const AudioTimeStamp *inInputTime, + AudioBufferList *outOutputData, + const AudioTimeStamp *inOutputTime, + void *hwptr) { UInt32 frameCount, pending_frames; void *out = outOutputData->mBuffers[0].mData; @@ -524,8 +524,7 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, } else if (frameRange.mMaximum < frames) { core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum; dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum); - } - else { + } else { core->audioDevicePropertyBufferFrameSize = frames; } diff --git a/audio/dsound_template.h b/audio/dsound_template.h index 9c5ce625ab..0678f2de38 100644 --- a/audio/dsound_template.h +++ b/audio/dsound_template.h @@ -205,7 +205,7 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as, NULL ); #else - bd.dwFlags = DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2; + bd.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2; hr = IDirectSound_CreateSoundBuffer ( s->dsound, &bd, diff --git a/audio/dsoundaudio.c b/audio/dsoundaudio.c index 4cdf19ab67..cfc79c129e 100644 --- a/audio/dsoundaudio.c +++ b/audio/dsoundaudio.c @@ -89,7 +89,9 @@ static void dsound_log_hresult (HRESULT hr) #endif #ifdef DSERR_ALLOCATED case DSERR_ALLOCATED: - str = "The request failed because resources, such as a priority level, were already in use by another caller"; + str = "The request failed because resources, " + "such as a priority level, were already in use " + "by another caller"; break; #endif #ifdef DSERR_ALREADYINITIALIZED @@ -104,7 +106,8 @@ static void dsound_log_hresult (HRESULT hr) #endif #ifdef DSERR_BADSENDBUFFERGUID case DSERR_BADSENDBUFFERGUID: - str = "The GUID specified in an audiopath file does not match a valid mix-in buffer"; + str = "The GUID specified in an audiopath file " + "does not match a valid mix-in buffer"; break; #endif #ifdef DSERR_BUFFERLOST @@ -114,26 +117,35 @@ static void dsound_log_hresult (HRESULT hr) #endif #ifdef DSERR_BUFFERTOOSMALL case DSERR_BUFFERTOOSMALL: - str = "The buffer size is not great enough to enable effects processing"; + str = "The buffer size is not great enough to " + "enable effects processing"; break; #endif #ifdef DSERR_CONTROLUNAVAIL case DSERR_CONTROLUNAVAIL: - str = "The buffer control (volume, pan, and so on) requested by the caller is not available. Controls must be specified when the buffer is created, using the dwFlags member of DSBUFFERDESC"; + str = "The buffer control (volume, pan, and so on) " + "requested by the caller is not available. " + "Controls must be specified when the buffer is created, " + "using the dwFlags member of DSBUFFERDESC"; break; #endif #ifdef DSERR_DS8_REQUIRED case DSERR_DS8_REQUIRED: - str = "A DirectSound object of class CLSID_DirectSound8 or later is required for the requested functionality. For more information, see IDirectSound8 Interface"; + str = "A DirectSound object of class CLSID_DirectSound8 or later " + "is required for the requested functionality. " + "For more information, see IDirectSound8 Interface"; break; #endif #ifdef DSERR_FXUNAVAILABLE case DSERR_FXUNAVAILABLE: - str = "The effects requested could not be found on the system, or they are in the wrong order or in the wrong location; for example, an effect expected in hardware was found in software"; + str = "The effects requested could not be found on the system, " + "or they are in the wrong order or in the wrong location; " + "for example, an effect expected in hardware " + "was found in software"; break; #endif #ifdef DSERR_GENERIC - case DSERR_GENERIC : + case DSERR_GENERIC: str = "An undetermined error occurred inside the DirectSound subsystem"; break; #endif @@ -154,7 +166,8 @@ static void dsound_log_hresult (HRESULT hr) #endif #ifdef DSERR_NODRIVER case DSERR_NODRIVER: - str = "No sound driver is available for use, or the given GUID is not a valid DirectSound device ID"; + str = "No sound driver is available for use, " + "or the given GUID is not a valid DirectSound device ID"; break; #endif #ifdef DSERR_NOINTERFACE @@ -169,12 +182,14 @@ static void dsound_log_hresult (HRESULT hr) #endif #ifdef DSERR_OTHERAPPHASPRIO case DSERR_OTHERAPPHASPRIO: - str = "Another application has a higher priority level, preventing this call from succeeding"; + str = "Another application has a higher priority level, " + "preventing this call from succeeding"; break; #endif #ifdef DSERR_OUTOFMEMORY case DSERR_OUTOFMEMORY: - str = "The DirectSound subsystem could not allocate sufficient memory to complete the caller's request"; + str = "The DirectSound subsystem could not allocate " + "sufficient memory to complete the caller's request"; break; #endif #ifdef DSERR_PRIOLEVELNEEDED @@ -189,7 +204,9 @@ static void dsound_log_hresult (HRESULT hr) #endif #ifdef DSERR_UNINITIALIZED case DSERR_UNINITIALIZED: - str = "The Initialize method has not been called or has not been called successfully before other methods were called"; + str = "The Initialize method has not been called " + "or has not been called successfully " + "before other methods were called"; break; #endif #ifdef DSERR_UNSUPPORTED @@ -198,7 +215,7 @@ static void dsound_log_hresult (HRESULT hr) break; #endif default: - AUD_log (AUDIO_CAP, "Reason: Unknown (HRESULT %#lx)\n", hr); + AUD_log (AUDIO_CAP, "Reason: Unknown (HRESULT 0x%lx)\n", hr); return; } @@ -342,12 +359,12 @@ static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb, dsound_unlock_out (dsb, p1, p2, blen1, blen2); } -static int dsound_open (dsound *s) +static int dsound_set_cooperative_level(dsound *s) { HRESULT hr; HWND hwnd; - hwnd = GetForegroundWindow (); + hwnd = GetDesktopWindow(); hr = IDirectSound_SetCooperativeLevel ( s->dsound, hwnd, @@ -404,8 +421,7 @@ static void dsound_enable_out(HWVoiceOut *hw, bool enable) dsound_logerr (hr, "Could not stop playing buffer\n"); return; } - } - else { + } else { dolog ("warning: Voice is not playing\n"); } } @@ -509,8 +525,7 @@ static void dsound_enable_in(HWVoiceIn *hw, bool enable) dsound_logerr (hr, "Could not stop capturing\n"); return; } - } - else { + } else { dolog ("warning: Voice is not capturing\n"); } } @@ -659,8 +674,7 @@ static void *dsound_audio_init(Audiodev *dev) ); if (FAILED (hr)) { dsound_logerr (hr, "Could not create DirectSoundCapture instance\n"); - } - else { + } else { hr = IDirectSoundCapture_Initialize (s->dsound_capture, NULL); if (FAILED (hr)) { dsound_logerr (hr, "Could not initialize DirectSoundCapture\n"); @@ -673,7 +687,7 @@ static void *dsound_audio_init(Audiodev *dev) } } - err = dsound_open (s); + err = dsound_set_cooperative_level(s); if (err) { dsound_audio_fini (s); return NULL; diff --git a/audio/jackaudio.c b/audio/jackaudio.c index 3b7c18443d..3031c4e29b 100644 --- a/audio/jackaudio.c +++ b/audio/jackaudio.c @@ -277,7 +277,7 @@ static int qjack_process(jack_nframes_t nframes, void *arg) if (likely(c->enabled)) { qjack_buffer_read_l(&c->fifo, buffers, nframes); } else { - for(int i = 0; i < c->nchannels; ++i) { + for (int i = 0; i < c->nchannels; ++i) { memset(buffers[i], 0, nframes * sizeof(float)); } } @@ -657,6 +657,7 @@ static struct audio_pcm_ops jack_pcm_ops = { .init_in = qjack_init_in, .fini_in = qjack_fini_in, .read = qjack_read, + .run_buffer_in = audio_generic_run_buffer_in, .enable_in = qjack_enable_in }; diff --git a/audio/noaudio.c b/audio/noaudio.c index 05798ea210..aac87dbc93 100644 --- a/audio/noaudio.c +++ b/audio/noaudio.c @@ -124,6 +124,7 @@ static struct audio_pcm_ops no_pcm_ops = { .init_in = no_init_in, .fini_in = no_fini_in, .read = no_read, + .run_buffer_in = audio_generic_run_buffer_in, .enable_in = no_enable_in }; diff --git a/audio/ossaudio.c b/audio/ossaudio.c index a7dcaa31ad..60eff66424 100644 --- a/audio/ossaudio.c +++ b/audio/ossaudio.c @@ -142,16 +142,14 @@ static int aud_to_ossfmt (AudioFormat fmt, int endianness) case AUDIO_FORMAT_S16: if (endianness) { return AFMT_S16_BE; - } - else { + } else { return AFMT_S16_LE; } case AUDIO_FORMAT_U16: if (endianness) { return AFMT_U16_BE; - } - else { + } else { return AFMT_U16_LE; } @@ -542,16 +540,14 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as, int trig = 0; if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n"); - } - else { + } else { trig = PCM_ENABLE_OUTPUT; if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { oss_logerr ( errno, "SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n" ); - } - else { + } else { oss->mmapped = 1; } } @@ -762,6 +758,7 @@ static struct audio_pcm_ops oss_pcm_ops = { .init_in = oss_init_in, .fini_in = oss_fini_in, .read = oss_read, + .run_buffer_in = audio_generic_run_buffer_in, .enable_in = oss_enable_in }; diff --git a/audio/paaudio.c b/audio/paaudio.c index b052084698..c97b22e970 100644 --- a/audio/paaudio.c +++ b/audio/paaudio.c @@ -207,19 +207,34 @@ static void *qpa_get_buffer_out(HWVoiceOut *hw, size_t *size) PAVoiceOut *p = (PAVoiceOut *) hw; PAConnection *c = p->g->conn; void *ret; + size_t l; int r; 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; + ret = NULL; + 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"); *size = -1; r = pa_stream_begin_write(p->stream, &ret, size); CHECK_SUCCESS_GOTO(c, r >= 0, unlock_and_fail, "pa_stream_begin_write failed\n"); +unlock: pa_threaded_mainloop_unlock(c->mainloop); + if (*size > l) { + *size = l; + } return ret; unlock_and_fail: @@ -228,6 +243,28 @@ unlock_and_fail: 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; @@ -239,6 +276,11 @@ static size_t qpa_write(HWVoiceOut *hw, void *data, size_t length) 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); @@ -252,6 +294,7 @@ static size_t qpa_write(HWVoiceOut *hw, void *data, size_t length) 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; @@ -437,7 +480,7 @@ static pa_stream *qpa_simple_new ( } if (r < 0) { - goto fail; + goto fail; } pa_threaded_mainloop_unlock(c->mainloop); @@ -474,7 +517,8 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as, 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; @@ -495,9 +539,12 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as, } audio_pcm_init_info (&hw->info, &obt_as); + /* + * This is wrong. hw->samples counts in frames. hw->samples will be + * number of channels times larger than expected. + */ hw->samples = audio_buffer_samples( - qapi_AudiodevPaPerDirectionOptions_base(ppdo), - &obt_as, ppdo->buffer_length); + qapi_AudiodevPaPerDirectionOptions_base(ppdo), &obt_as, 46440); return 0; @@ -521,8 +568,9 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) 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; @@ -543,9 +591,12 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) } audio_pcm_init_info (&hw->info, &obt_as); + /* + * This is wrong. hw->samples counts in frames. hw->samples will be + * number of channels times larger than expected. + */ hw->samples = audio_buffer_samples( - qapi_AudiodevPaPerDirectionOptions_base(ppdo), - &obt_as, ppdo->buffer_length); + qapi_AudiodevPaPerDirectionOptions_base(ppdo), &obt_as, 46440); return 0; @@ -695,10 +746,6 @@ static void qpa_volume_in(HWVoiceIn *hw, Volume *vol) 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; @@ -861,7 +908,7 @@ static struct audio_pcm_ops qpa_pcm_ops = { .fini_out = qpa_fini_out, .write = qpa_write, .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, diff --git a/audio/sdlaudio.c b/audio/sdlaudio.c index 21b7a0484b..c68c62a3e4 100644 --- a/audio/sdlaudio.c +++ b/audio/sdlaudio.c @@ -41,15 +41,19 @@ typedef struct SDLVoiceOut { HWVoiceOut hw; + int exit; + int initialized; + Audiodev *dev; + SDL_AudioDeviceID devid; } SDLVoiceOut; -static struct SDLAudioState { +typedef struct SDLVoiceIn { + HWVoiceIn hw; int exit; int initialized; - bool driver_created; Audiodev *dev; -} glob_sdl; -typedef struct SDLAudioState SDLAudioState; + SDL_AudioDeviceID devid; +} SDLVoiceIn; static void GCC_FMT_ATTR (1, 2) sdl_logerr (const char *fmt, ...) { @@ -155,9 +159,10 @@ static int sdl_to_audfmt(int sdlfmt, AudioFormat *fmt, int *endianness) return 0; } -static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt) +static SDL_AudioDeviceID sdl_open(SDL_AudioSpec *req, SDL_AudioSpec *obt, + int rec) { - int status; + SDL_AudioDeviceID devid; #ifndef _WIN32 int err; sigset_t new, old; @@ -166,18 +171,19 @@ static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt) err = sigfillset (&new); if (err) { dolog ("sdl_open: sigfillset failed: %s\n", strerror (errno)); - return -1; + return 0; } err = pthread_sigmask (SIG_BLOCK, &new, &old); if (err) { dolog ("sdl_open: pthread_sigmask failed: %s\n", strerror (err)); - return -1; + return 0; } #endif - status = SDL_OpenAudio (req, obt); - if (status) { - sdl_logerr ("SDL_OpenAudio failed\n"); + devid = SDL_OpenAudioDevice(NULL, rec, req, obt, 0); + if (!devid) { + sdl_logerr("SDL_OpenAudioDevice for %s failed\n", + rec ? "recording" : "playback"); } #ifndef _WIN32 @@ -190,112 +196,175 @@ static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt) exit (EXIT_FAILURE); } #endif - return status; + return devid; } -static void sdl_close (SDLAudioState *s) +static void sdl_close_out(SDLVoiceOut *sdl) { - if (s->initialized) { - SDL_LockAudio(); - s->exit = 1; - SDL_UnlockAudio(); - SDL_PauseAudio (1); - SDL_CloseAudio (); - s->initialized = 0; + 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 (void *opaque, Uint8 *buf, int len) +static void sdl_callback_out(void *opaque, Uint8 *buf, int len) { SDLVoiceOut *sdl = opaque; - SDLAudioState *s = &glob_sdl; HWVoiceOut *hw = &sdl->hw; - if (s->exit) { - return; - } + if (!sdl->exit) { - /* dolog ("in callback samples=%zu live=%zu\n", samples, sdl->live); */ + /* dolog("callback_out: len=%d avail=%zu\n", len, hw->pending_emul); */ - while (hw->pending_emul && len) { - size_t write_len; - 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); + while (hw->pending_emul && len) { + size_t write_len; + 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(MIN(hw->pending_emul, len), - hw->size_emul - start); + write_len = MIN(MIN(hw->pending_emul, len), + hw->size_emul - start); - memcpy(buf, hw->buf_emul + start, write_len); - hw->pending_emul -= write_len; - len -= write_len; - buf += write_len; + memcpy(buf, hw->buf_emul + start, write_len); + hw->pending_emul -= write_len; + len -= write_len; + buf += write_len; + } } /* clear remaining buffer that we couldn't fill with data */ if (len) { - memset(buf, 0, len); + audio_pcm_info_clear_buf(&hw->info, buf, + len / hw->info.bytes_per_frame); + } +} + +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, fail, unlock) \ - static ret_type glue(sdl_, name)args_decl \ - { \ - ret_type ret; \ - \ - SDL_LockAudio(); \ - \ - ret = glue(audio_generic_, name)args; \ - \ - SDL_UnlockAudio(); \ - return ret; \ +#define SDL_WRAPPER_FUNC(name, ret_type, args_decl, args, dir) \ + static ret_type glue(sdl_, name)args_decl \ + { \ + ret_type ret; \ + glue(SDLVoice, dir) *sdl = (glue(SDLVoice, dir) *)hw; \ + \ + SDL_LockAudioDevice(sdl->devid); \ + ret = glue(audio_generic_, name)args; \ + SDL_UnlockAudioDevice(sdl->devid); \ + \ + 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), *size = 0, sdl_unlock) + (hw, size), Out) SDL_WRAPPER_FUNC(put_buffer_out, size_t, - (HWVoiceOut *hw, void *buf, size_t size), (hw, buf, size), - /*nothing*/, sdl_unlock_and_post) + (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), - /*nothing*/, sdl_unlock_and_post) - + (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) +static void sdl_fini_out(HWVoiceOut *hw) { - (void) hw; + SDLVoiceOut *sdl = (SDLVoiceOut *)hw; - sdl_close (&glob_sdl); + sdl_close_out(sdl); } static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque) { - SDLVoiceOut *sdl = (SDLVoiceOut *) hw; - SDLAudioState *s = &glob_sdl; + SDLVoiceOut *sdl = (SDLVoiceOut *)hw; SDL_AudioSpec req, obt; int endianness; int err; AudioFormat effective_fmt; + Audiodev *dev = drv_opaque; + AudiodevSdlPerDirectionOptions *spdo = dev->u.sdl.out; struct audsettings obt_as; req.freq = as->freq; req.format = aud_to_sdlfmt (as->fmt); req.channels = as->nchannels; - req.samples = audio_buffer_samples(s->dev->u.sdl.out, as, 11610); - req.callback = sdl_callback; + /* + * This is wrong. SDL samples are QEMU frames. The buffer size will be + * the requested buffer size multiplied by the number of channels. + */ + req.samples = audio_buffer_samples( + qapi_AudiodevSdlPerDirectionOptions_base(spdo), as, 11610); + req.callback = sdl_callback_out; req.userdata = sdl; - if (sdl_open (&req, &obt)) { + sdl->dev = dev; + sdl->devid = sdl_open(&req, &obt, 0); + if (!sdl->devid) { return -1; } err = sdl_to_audfmt(obt.format, &effective_fmt, &endianness); if (err) { - sdl_close (s); + sdl_close_out(sdl); return -1; } @@ -305,44 +374,97 @@ static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as, obt_as.endianness = endianness; audio_pcm_init_info (&hw->info, &obt_as); - hw->samples = obt.samples; + hw->samples = (spdo->has_buffer_count ? spdo->buffer_count : 4) * + obt.samples; - s->initialized = 1; - s->exit = 0; - SDL_PauseAudio (0); + sdl->initialized = 1; + sdl->exit = 0; return 0; } static void sdl_enable_out(HWVoiceOut *hw, bool enable) { - SDL_PauseAudio(!enable); + SDLVoiceOut *sdl = (SDLVoiceOut *)hw; + + SDL_PauseAudioDevice(sdl->devid, !enable); } -static void *sdl_audio_init(Audiodev *dev) +static void sdl_fini_in(HWVoiceIn *hw) { - SDLAudioState *s = &glob_sdl; - if (s->driver_created) { - sdl_logerr("Can't create multiple sdl backends\n"); - return NULL; + 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)) { sdl_logerr ("SDL failed to initialize audio subsystem\n"); return NULL; } - s->driver_created = true; - s->dev = dev; - return s; + return dev; } static void sdl_audio_fini (void *opaque) { - SDLAudioState *s = opaque; - sdl_close (s); SDL_QuitSubSystem (SDL_INIT_AUDIO); - s->driver_created = false; - s->dev = NULL; } static struct audio_pcm_ops sdl_pcm_ops = { @@ -355,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 = { @@ -364,10 +495,10 @@ static struct audio_driver sdl_audio_driver = { .fini = sdl_audio_fini, .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_out = INT_MAX, + .max_voices_in = INT_MAX, + .voice_size_out = sizeof(SDLVoiceOut), + .voice_size_in = sizeof(SDLVoiceIn), }; static void register_audio_sdl(void) diff --git a/audio/spiceaudio.c b/audio/spiceaudio.c index 8967cca129..999bfbde47 100644 --- a/audio/spiceaudio.c +++ b/audio/spiceaudio.c @@ -293,6 +293,7 @@ static struct audio_pcm_ops audio_callbacks = { .init_in = line_in_init, .fini_in = line_in_fini, .read = line_in_read, + .run_buffer_in = audio_generic_run_buffer_in, .enable_in = line_in_enable, #if ((SPICE_INTERFACE_RECORD_MAJOR >= 2) && (SPICE_INTERFACE_RECORD_MINOR >= 2)) .volume_in = line_in_volume, diff --git a/qapi/audio.json b/qapi/audio.json index 072ed79def..9cba0df8a4 100644 --- a/qapi/audio.json +++ b/qapi/audio.json @@ -302,6 +302,37 @@ '*server': 'str' } } ## +# @AudiodevSdlPerDirectionOptions: +# +# Options of the SDL audio backend that are used for both playback and +# recording. +# +# @buffer-count: number of buffers (default 4) +# +# Since: 6.0 +## +{ 'struct': 'AudiodevSdlPerDirectionOptions', + 'base': 'AudiodevPerDirectionOptions', + 'data': { + '*buffer-count': 'uint32' } } + +## +# @AudiodevSdlOptions: +# +# Options of the SDL audio backend. +# +# @in: options of the recording stream +# +# @out: options of the playback stream +# +# Since: 6.0 +## +{ 'struct': 'AudiodevSdlOptions', + 'data': { + '*in': 'AudiodevSdlPerDirectionOptions', + '*out': 'AudiodevSdlPerDirectionOptions' } } + +## # @AudiodevWavOptions: # # Options of the wav audio backend. @@ -385,6 +416,6 @@ 'jack': 'AudiodevJackOptions', 'oss': 'AudiodevOssOptions', 'pa': 'AudiodevPaOptions', - 'sdl': 'AudiodevGenericOptions', + 'sdl': 'AudiodevSdlOptions', 'spice': 'AudiodevGenericOptions', 'wav': 'AudiodevWavOptions' } } diff --git a/qemu-options.hx b/qemu-options.hx index 05fe35ceb6..9dc90ffbfb 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -588,6 +588,7 @@ DEF("audiodev", HAS_ARG, QEMU_OPTION_audiodev, #endif #ifdef CONFIG_AUDIO_SDL "-audiodev sdl,id=id[,prop[=value][,...]]\n" + " in|out.buffer-count= number of buffers\n" #endif #ifdef CONFIG_SPICE "-audiodev spice,id=id[,prop[=value][,...]]\n" @@ -745,7 +746,12 @@ SRST ``-audiodev sdl,id=id[,prop[=value][,...]]`` Creates a backend using SDL. This backend is available on most systems, but you should use your platform's native backend if - possible. This backend has no backend specific properties. + possible. + + SDL specific options are: + + ``in|out.buffer-count=count`` + Sets the count of the buffers. ``-audiodev spice,id=id[,prop[=value][,...]]`` Creates a backend that sends audio through SPICE. This backend |