audio: multi channel audio support
v2 cover audio: multi channel audio support
This patch series adds support to more than two channels of audio (5.1, 7.1, etc.). Currently only usb-audio frontend and alsa and pa backends are updated. Using more than two channels requires turning off mixeng (-audiodev backend,id=foo,out.mixeng=off,...).
For backward compatibility reasons, you have to specify multi=on when creating the usb-audio device:
-device usb-audio,id=foo,multi=on,...
Please review.
Kővágó, Zoltán (9):
audio: add mixeng option (documentation)
audio: make mixeng optional
paaudio: get/put_buffer functions
audio: support more than two channels in volume setting
audio: replace shift in audio_pcm_info with bytes_per_frame
audio: basic support for multichannel audio
paaudio: channel-map option
usb-audio: do not count on avail bytes actually available
usb-audio: support more than two channels of audio
audio/alsaaudio.c | 17 +- audio/audio.c | 176 +++++++++++++------ audio/audio.h | 10 ++ audio/audio_int.h | 7 +- audio/audio_template.h | 22 ++- audio/coreaudio.c | 4 +- audio/dsound_template.h | 10 +- audio/dsoundaudio.c | 4 +- audio/noaudio.c | 2 +- audio/ossaudio.c | 14 +- audio/paaudio.c | 121 +++++++++++-- audio/spiceaudio.c | 19 ++- audio/wavaudio.c | 6 +- hw/usb/dev-audio.c | 446 +++++++++++++++++++++++++++++++++++++++++------- qapi/audio.json | 10 +- qemu-options.hx | 15 ++ 16 files changed, 703 insertions(+), 180 deletions(-)
Signature · 1 line
2.5.0
PATCH v2 1/9 audio: add mixeng option (documentation)
This will allow us to disable mixeng when we use a decent backend.
Disabling mixeng have a few advantages:
* we no longer convert the audio output from one format to another, when the underlying audio system would just convert it to a third format. We no longer convert, only the underlying system, when needed.
* the underlying system probably has better resampling and sample format converting methods anyway...
* we may support formats that the mixeng currently does not support (S24 or float samples, more than two channels)
* when using an audio server (like pulseaudio) different sound card outputs will show up as separate streams, even if we use only one backend
Disadvantages:
* audio capturing no longer works (wavcapture, and vnc audio extension)
* some backends only support a single playback stream or very picky about the audio format. In this case we can't disable mixeng.
However mixeng is not removed, only made optional, so this shouldn't be a big concern.
--- qapi/audio.json | 5 +++++ qemu-options.hx | 6 ++++++ 2 files changed, 11 insertions(+)
qapi/audio.json +5 -0
@@ -184,6 +184,10 @@ # # General audio backend options that are used for both playback and recording. # +# @mixeng: #optional use QEMU's mixing engine to mix all streams inside QEMU. +# When set to off, fixed-settings must be also off. Not every backend +# compatible with the off setting (default on) +# # @fixed-settings: #optional use fixed settings for host input/output. When # off, frequency, channels and format must not be specified # (default on)
@@ -207,6 +211,7 @@ ## { 'struct': 'AudiodevPerDirectionOptions', 'data': { + '*mixeng': 'bool', '*fixed-settings': 'bool', '*frequency': 'int', '*channels': 'int',
qemu-options.hx +6 -0
@@ -329,6 +329,7 @@ DEF("audiodev", HAS_ARG, QEMU_OPTION_audiodev, " specifies the audio backend to use\n" " id= identifier of the backend\n" " timer-period= timer period in microseconds\n" + " in|out.mixeng= use mixeng to mix streams inside QEMU\n" " in|out.fixed-settings= use fixed settings for host audio\n" " in|out.frequency= frequency to use with fixed settings\n" " in|out.channels= number of channels to use with fixed settings\n"
@@ -397,6 +398,11 @@ Identifies the audio backend. Sets the timer @var{period} used by the audio subsystem in microseconds. Default is 10000 (10 ms). +@item in|out.mixeng=on|off +Use QEMU's mixing engine to mix all streams inside QEMU. When off, +@var{fixed-settings} must be off too. Not every backend is fully +compatible with the off setting. Default is on. + @item in|out.fixed-settings=on|off Use fixed settings for host audio. When off, it will change based on how the guest opens the sound card. In this case you must not specify
Signature · 1 line
2.5.0
PATCH v2 2/9 audio: make mixeng optional
In reply to Kővágó, ZoltánImplementation of the previously added mixeng option.
--- audio/audio.c | 70 +++++++++++++++++++++++++++++++++++++++++++++----- audio/audio_template.h | 22 +++++++++++----- 2 files changed, 79 insertions(+), 13 deletions(-)
audio/audio.c +64 -6
@@ -793,32 +793,46 @@ static void audio_timer (void *opaque)*/ size_t AUD_write (SWVoiceOut *sw,void *buf,size_t size) { +HWVoiceOut *hw; +if (!sw) {/* XXX: Consider options */ return size; } + hw = sw->hw; -if (!sw->hw->enabled) { +if (!hw->enabled) {dolog ("Writing to disabled voice %s\n" ,SW_NAME (sw));return 0 ; } -return audio_pcm_sw_write (sw, buf, size); +if (hw->s->dev->out->mixeng) { +return audio_pcm_sw_write (sw, buf, size); + }else { +return hw->pcm_ops->write (hw, buf, size); + } }size_t AUD_read (SWVoiceIn *sw,void *buf,size_t size) { +HWVoiceIn *hw; +if (!sw) {/* XXX: Consider options */ return size; } + hw = sw->hw; -if (!sw->hw->enabled) { +if (!hw->enabled) {dolog ("Reading from disabled voice %s\n" ,SW_NAME (sw));return 0 ; } -return audio_pcm_sw_read (sw, buf, size); +if (hw->s->dev->in->mixeng) { +return audio_pcm_sw_read (sw, buf, size); + }else { +return hw->pcm_ops->read (hw, buf, size); + } }int AUD_get_buffer_size_out (SWVoiceOut *sw)
@@ -1038,6 +1052,26 @@ static void audio_run_out (AudioState *s)HWVoiceOut *hw =NULL ;SWVoiceOut *sw; +if (!s->dev->out->mixeng) { +while ((hw =audio_pcm_hw_find_any_enabled_out (s, hw))) { +/* there is exactly 1 sw for each hw with no mixeng */ + sw = hw->sw_head.lh_first; + +if (hw->pending_disable) { + hw->enabled =0 ; + hw->pending_disable =0 ; +if (hw->pcm_ops->enable_out) { + hw->pcm_ops->enable_out (hw, false); + } + } + +if (sw->active) { + sw->callback.fn (sw->callback.opaque,INT_MAX ); + } + } +return ; + } +while ((hw =audio_pcm_hw_find_any_enabled_out (s, hw))) {size_t played, live, prev_rpos, free;int nb_live, cleanup_required;
@@ -1174,6 +1208,17 @@ static void audio_run_in (AudioState *s) {HWVoiceIn *hw =NULL ; +if (!s->dev->in->mixeng) { +while ((hw =audio_pcm_hw_find_any_enabled_in (s, hw))) { +/* there is exactly 1 sw for each hw with no mixeng */ +SWVoiceIn *sw = hw->sw_head.lh_first; +if (sw->active) { + sw->callback.fn (sw->callback.opaque,INT_MAX ); + } + } +return ; + } +while ((hw =audio_pcm_hw_find_any_enabled_in (s, hw))) {SWVoiceIn *sw;size_t captured, min;
@@ -1654,6 +1699,11 @@ CaptureVoiceOut *AUD_add_capture( s =QTAILQ_FIRST (&audio_states); } +if (!s->dev->out->mixeng) { +dolog ("Can't capture with mixeng disabled\n" ); + goto err0; + } +if (audio_validate_settings (as)) {dolog ("Invalid settings were passed when trying to add capture\n" );audio_print_settings (as);
@@ -1811,9 +1861,13 @@ QemuOptsList qemu_audiodev_opts = {static void validate_per_direction_opts (AudiodevPerDirectionOptions *pdo,Error **errp) { +if (!pdo->has_mixeng) { + pdo->has_mixeng = true; + pdo->mixeng = true; + }if (!pdo->has_fixed_settings) { pdo->has_fixed_settings = true; - pdo->fixed_settings = true; + pdo->fixed_settings = pdo->mixeng; }if (!pdo->fixed_settings && (pdo->has_frequency || pdo->has_channels || pdo->has_format)) {
@@ -1821,6 +1875,10 @@ static void validate_per_direction_opts(AudiodevPerDirectionOptions *pdo, "You can't use frequency, channels or format with fixed-settings=off");return ; } +if (!pdo->mixeng && pdo->fixed_settings) { +error_setg (errp,"You can't use fixed-settings without mixeng" ); +return ; + }if (!pdo->has_frequency) { pdo->has_frequency = true;
@@ -1832,7 +1890,7 @@ static void validate_per_direction_opts(AudiodevPerDirectionOptions *pdo, }if (!pdo->has_voices) { pdo->has_voices = true; - pdo->voices =1 ; + pdo->voices = pdo->mixeng ?1 :INT_MAX ; }if (!pdo->has_format) { pdo->has_format = true;
audio/audio_template.h +15 -7
@@ -78,15 +78,19 @@ static void glue (audio_pcm_hw_free_resources_, TYPE) (HW *hw)static void glue (audio_pcm_hw_alloc_resources_ ,TYPE )(HW *hw) { -size_t samples; -if (hw->pcm_ops->glue (buffer_size_,TYPE )) { - samples = hw->pcm_ops->glue (buffer_size_,TYPE )(hw); +if (hw->s->dev->TYPE->mixeng) { +size_t samples; +if (hw->pcm_ops->glue (buffer_size_,TYPE )) { + samples = hw->pcm_ops->glue (buffer_size_,TYPE )(hw); + }else { + samples =1024 ;/* todo better default */ + } + +HWBUF =g_malloc0 (sizeof (STSampleBuffer) +sizeof (st_sample)*samples); +HWBUF ->size = samples; }else { - samples =1024 ;/* todo better default */ +HWBUF =NULL ; } - -HWBUF =g_malloc0 (sizeof (STSampleBuffer) +sizeof (st_sample)*samples); -HWBUF ->size = samples; }static void glue (audio_pcm_sw_free_resources_ ,TYPE ) (SW *sw)
@@ -105,6 +109,10 @@ static int glue (audio_pcm_sw_alloc_resources_, TYPE) (SW *sw) {int samples; +if (!sw->s->dev->TYPE->mixeng) { +return 0 ; + } + samples = ((int64_t ) sw->HWBUF->size <<32 ) / sw->ratio; sw->buf =audio_calloc (AUDIO_FUNC , samples,sizeof (struct st_sample ));
Signature · 1 line
2.5.0
PATCH v2 3/9 paaudio: get/put_buffer functions
In reply to Kővágó, ZoltánThis lets us avoid some buffer copying when using mixeng.
--- audio/paaudio.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+)
audio/paaudio.c +83 -0
@@ -96,6 +96,59 @@ static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x) } \ }while (0 ); +static void *qpa_get_buffer_in (HWVoiceIn *hw,size_t *size) +{ +PAVoiceIn *p = (PAVoiceIn *) 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" ); + +if (!p->read_length) { + r =pa_stream_peek (p->stream, &p->read_data, &p->read_length); +CHECK_SUCCESS_GOTO (c, r ==0 , unlock_and_fail, +"pa_stream_peek failed\n" ); + } + + *size =MIN (p->read_length, *size); + +pa_threaded_mainloop_unlock (c->mainloop); +return (void *) p->read_data; + +unlock_and_fail: +pa_threaded_mainloop_unlock (c->mainloop); + *size =0 ; +return NULL ; +} + +static void qpa_put_buffer_in (HWVoiceIn *hw,void *buf,size_t size) +{ +PAVoiceIn *p = (PAVoiceIn *) hw; +PAConnection *c = p->g->conn; +int r; + +pa_threaded_mainloop_lock (c->mainloop); + +CHECK_DEAD_GOTO (c, p->stream, unlock, +"pa_threaded_mainloop_lock failed\n" ); + +assert (buf == p->read_data && size <= p->read_length); + + p->read_data += size; + p->read_length -= size; + +if (size && !p->read_length) { + r =pa_stream_drop (p->stream); +CHECK_SUCCESS_GOTO (c, r ==0 , unlock,"pa_stream_drop failed\n" ); + } + +unlock: +pa_threaded_mainloop_unlock (c->mainloop); +} +static size_t qpa_read (HWVoiceIn *hw,void *data,size_t length) {PAVoiceIn *p = (PAVoiceIn *) hw;
@@ -134,6 +187,32 @@ unlock_and_fail:return 0 ; } +static void *qpa_get_buffer_out (HWVoiceOut *hw,size_t *size) +{ +PAVoiceOut *p = (PAVoiceOut *) hw; +PAConnection *c = p->g->conn; +void *ret; +int r; + +pa_threaded_mainloop_lock (c->mainloop); + +CHECK_DEAD_GOTO (c, p->stream, unlock_and_fail, +"pa_threaded_mainloop_lock 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" ); + +pa_threaded_mainloop_unlock (c->mainloop); +return ret; + +unlock_and_fail: +pa_threaded_mainloop_unlock (c->mainloop); + *size =0 ; +return NULL ; +} +static size_t qpa_write (HWVoiceOut *hw,void *data,size_t length) {PAVoiceOut *p = (PAVoiceOut *) hw;
@@ -668,12 +747,16 @@ static struct audio_pcm_ops qpa_pcm_ops = { .fini_out = qpa_fini_out, .write = qpa_write, .buffer_size_out = qpa_buffer_size_out, + .get_buffer_out = qpa_get_buffer_out, + .put_buffer_out = qpa_write, /* pa handles it */ .volume_out = qpa_volume_out, .init_in = qpa_init_in, .fini_in = qpa_fini_in, .read = qpa_read, .buffer_size_in = qpa_buffer_size_in, + .get_buffer_in = qpa_get_buffer_in, + .put_buffer_in = qpa_put_buffer_in, .volume_in = qpa_volume_in };
Signature · 1 line
2.5.0
PATCH v2 4/9 audio: support more than two channels in volume setting
In reply to Kővágó, Zoltán--- audio/audio.c | 28 ++++++++++++++++++++-------- audio/audio.h | 10 ++++++++++ audio/audio_int.h | 4 ++-- audio/paaudio.c | 20 ++++++++++++-------- audio/spiceaudio.c | 14 ++++++++------ 5 files changed, 52 insertions(+), 24 deletions(-)
audio/audio.c +20 -8
@@ -1817,30 +1817,42 @@ void AUD_del_capture (CaptureVoiceOut *cap, void *cb_opaque)void AUD_set_volume_out (SWVoiceOut *sw,int mute,uint8_t lvol,uint8_t rvol) { +Volume vol = { .mute = mute, .channels =2 , .vol = { lvol, rvol } }; +audio_set_volume_out (sw, &vol); +} + +void audio_set_volume_out (SWVoiceOut *sw,Volume *vol) +{if (sw) {HWVoiceOut *hw = sw->hw; - sw->vol.mute = mute; - sw->vol.l = nominal_volume.l * lvol /255 ; - sw->vol.r = nominal_volume.r * rvol /255 ; + sw->vol.mute = vol->mute; + sw->vol.l = nominal_volume.l * vol->vol[0 ] /255 ; + sw->vol.r = nominal_volume.l * vol->vol[vol->channels >1 ?1 :0 ] /255 ;if (hw->pcm_ops->volume_out) { - hw->pcm_ops->volume_out (hw, &sw->vol); + hw->pcm_ops->volume_out (hw, vol); } } }void AUD_set_volume_in (SWVoiceIn *sw,int mute,uint8_t lvol,uint8_t rvol) { +Volume vol = { .mute = mute, .channels =2 , .vol = { lvol, rvol } }; +audio_set_volume_in (sw, &vol); +} + +void audio_set_volume_in (SWVoiceIn *sw,Volume *vol) +{if (sw) {HWVoiceIn *hw = sw->hw; - sw->vol.mute = mute; - sw->vol.l = nominal_volume.l * lvol /255 ; - sw->vol.r = nominal_volume.r * rvol /255 ; + sw->vol.mute = vol->mute; + sw->vol.l = nominal_volume.l * vol->vol[0 ] /255 ; + sw->vol.r = nominal_volume.r * vol->vol[vol->channels >1 ?1 :0 ] /255 ;if (hw->pcm_ops->volume_in) { - hw->pcm_ops->volume_in (hw, &sw->vol); + hw->pcm_ops->volume_in (hw, vol); } } }
audio/audio.h +10 -0
@@ -127,6 +127,16 @@ uint64_t AUD_get_elapsed_usec_out (SWVoiceOut *sw, QEMUAudioTimeStamp *ts);void AUD_set_volume_out (SWVoiceOut *sw,int mute,uint8_t lvol,uint8_t rvol);void AUD_set_volume_in (SWVoiceIn *sw,int mute,uint8_t lvol,uint8_t rvol); +#define AUDIO_MAX_CHANNELS 16 +typedef struct Volume { +bool mute; +int channels; +uint8_t vol[AUDIO_MAX_CHANNELS ]; +}Volume ; + +void audio_set_volume_out (SWVoiceOut *sw,Volume *vol); +void audio_set_volume_in (SWVoiceIn *sw,Volume *vol); + SWVoiceIn *AUD_open_in ( QEMUSoundCard *card, SWVoiceIn *sw,
audio/audio_int.h +2 -2
@@ -158,7 +158,7 @@ struct audio_pcm_ops {* size may be smaller */ size_t (*put_buffer_out)(HWVoiceOut *hw,void *buf,size_t size);void (*enable_out)(HWVoiceOut *hw,bool enable); -void (*volume_out)(HWVoiceOut *hw,struct mixeng_volume *vol); +void (*volume_out)(HWVoiceOut *hw,Volume *vol);int (*init_in) (HWVoiceIn *hw,audsettings *as,void *drv_opaque);void (*fini_in) (HWVoiceIn *hw);
@@ -167,7 +167,7 @@ struct audio_pcm_ops {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,struct mixeng_volume *vol); +void (*volume_in)(HWVoiceIn *hw,Volume *vol); };void *audio_generic_get_buffer_in (HWVoiceIn *hw,size_t *size);
audio/paaudio.c +12 -8
@@ -534,20 +534,22 @@ static void qpa_fini_in (HWVoiceIn *hw) } } -static void qpa_volume_out (HWVoiceOut *hw,struct mixeng_volume *vol) +static void qpa_volume_out (HWVoiceOut *hw,Volume *vol) {PAVoiceOut *pa = (PAVoiceOut *) hw;pa_operation *op;pa_cvolume v;PAConnection *c = pa->g->conn; +int i;#ifdef PA_CHECK_VERSION /* macro is present in 0.9.16+ */ pa_cvolume_init (&v);/* function is present in 0.9.13+ */ #endif - v.channels =2 ; - v.values[0 ] = ((PA_VOLUME_NORM -PA_VOLUME_MUTED ) * vol->l) /UINT32_MAX ; - v.values[1 ] = ((PA_VOLUME_NORM -PA_VOLUME_MUTED ) * vol->r) /UINT32_MAX ; + v.channels = vol->channels; +for (i =0 ; i < vol->channels; ++i) { + v.values[i] = ((PA_VOLUME_NORM -PA_VOLUME_MUTED ) * vol->vol[i]) /255 ; + }pa_threaded_mainloop_lock (c->mainloop);
@@ -574,20 +576,22 @@ static void qpa_volume_out(HWVoiceOut *hw, struct mixeng_volume *vol)pa_threaded_mainloop_unlock (c->mainloop); } -static void qpa_volume_in (HWVoiceIn *hw,struct mixeng_volume *vol) +static void qpa_volume_in (HWVoiceIn *hw,Volume *vol) {PAVoiceIn *pa = (PAVoiceIn *) hw;pa_operation *op;pa_cvolume v;PAConnection *c = pa->g->conn; +int i;#ifdef PA_CHECK_VERSION pa_cvolume_init (&v);#endif - v.channels =2 ; - v.values[0 ] = ((PA_VOLUME_NORM -PA_VOLUME_MUTED ) * vol->l) /UINT32_MAX ; - v.values[1 ] = ((PA_VOLUME_NORM -PA_VOLUME_MUTED ) * vol->r) /UINT32_MAX ; + v.channels = vol->channels; +for (i =0 ; i < vol->channels; ++i) { + v.values[i] = ((PA_VOLUME_NORM -PA_VOLUME_MUTED ) * vol->vol[i]) /255 ; + }pa_threaded_mainloop_lock (c->mainloop);
audio/spiceaudio.c +8 -6
@@ -177,13 +177,14 @@ static void line_out_enable(HWVoiceOut *hw, bool enable) }#if ((SPICE_INTERFACE_PLAYBACK_MAJOR >=1 ) && (SPICE_INTERFACE_PLAYBACK_MINOR >=2 )) -static void line_out_volume (HWVoiceOut *hw,struct mixeng_volume *vol) +static void line_out_volume (HWVoiceOut *hw,Volume *vol) {SpiceVoiceOut *out =container_of (hw, SpiceVoiceOut, hw);uint16_t svol[2 ]; - svol[0 ] = vol->l / ((1ULL <<16 ) +1 ); - svol[1 ] = vol->r / ((1ULL <<16 ) +1 ); +assert (vol->channels ==2 ); + svol[0 ] = vol->vol[0 ] *257 ; + svol[1 ] = vol->vol[1 ] *257 ;spice_server_playback_set_volume (&out->sin,2 , svol);spice_server_playback_set_mute (&out->sin, vol->mute); }
@@ -264,13 +265,14 @@ static void line_in_enable(HWVoiceIn *hw, bool enable) }#if ((SPICE_INTERFACE_RECORD_MAJOR >=2 ) && (SPICE_INTERFACE_RECORD_MINOR >=2 )) -static void line_in_volume (HWVoiceIn *hw,struct mixeng_volume *vol) +static void line_in_volume (HWVoiceIn *hw,Volume *vol) {SpiceVoiceIn *in =container_of (hw, SpiceVoiceIn, hw);uint16_t svol[2 ]; - svol[0 ] = vol->l / ((1ULL <<16 ) +1 ); - svol[1 ] = vol->r / ((1ULL <<16 ) +1 ); +assert (vol->channels ==2 ); + svol[0 ] = vol->vol[0 ] *257 ; + svol[1 ] = vol->vol[1 ] *257 ;spice_server_record_set_volume (&in->sin,2 , svol);spice_server_record_set_mute (&in->sin, vol->mute); }
Signature · 1 line
2.5.0
PATCH v2 5/9 audio: replace shift in audio_pcm_info with bytes_per_frame
In reply to Kővágó, ZoltánThe bit shifting trick worked because the number of bytes per frame was always a power-of-two (since QEMU only supports mono, stereo and 8, 16 and 32 bit samples). But if we want to add support for surround sound, this no longer holds true.
--- audio/alsaaudio.c | 10 +++---- audio/audio.c | 76 ++++++++++++++++++++++++------------------------- audio/audio_int.h | 3 +- audio/coreaudio.c | 4 +-- audio/dsound_template.h | 10 +++---- audio/dsoundaudio.c | 4 +-- audio/noaudio.c | 2 +- audio/ossaudio.c | 14 ++++----- audio/spiceaudio.c | 5 ++-- audio/wavaudio.c | 6 ++-- 10 files changed, 67 insertions(+), 67 deletions(-)
audio/alsaaudio.c +5 -5
@@ -613,7 +613,7 @@ static size_t alsa_write(HWVoiceOut *hw, void *buf, size_t len) {ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;size_t pos =0 ; -size_t len_frames = len >> hw->info.shift; +size_t len_frames = len / hw->info.bytes_per_frame;while (len_frames) {char *src =advance (buf, pos);
@@ -657,7 +657,7 @@ static size_t alsa_write(HWVoiceOut *hw, void *buf, size_t len) } } - pos += written << hw->info.shift; + pos += written * hw->info.bytes_per_frame;if (written < len_frames) {break ; }
@@ -823,7 +823,7 @@ static size_t alsa_read(HWVoiceIn *hw, void *buf, size_t len)void *dst =advance (buf, pos);snd_pcm_sframes_t nread; - nread =snd_pcm_readi (alsa->handle, dst, len >> hw->info.shift); + nread =snd_pcm_readi (alsa->handle, dst, len / hw->info.bytes_per_frame);if (nread <=0 ) {switch (nread) {
@@ -849,8 +849,8 @@ static size_t alsa_read(HWVoiceIn *hw, void *buf, size_t len) } } - pos += nread << hw->info.shift; - len -= nread << hw->info.shift; + pos += nread * hw->info.bytes_per_frame; + len -= nread * hw->info.bytes_per_frame; }return pos;
audio/audio.c +38 -38
267 lines not on this page · raw message
audio/audio_int.h +1 -2
@@ -42,8 +42,7 @@ struct audio_pcm_info {int sign;int freq;int nchannels; -int align; -int shift; +int bytes_per_frame;int bytes_per_second;int swap_endianness; };
audio/coreaudio.c +2 -2
@@ -225,7 +225,7 @@ static OSStatus audioDeviceIOProc( } frameCount = core->audioDevicePropertyBufferFrameSize; - pending_frames = hw->pending_emul >> hw->info.shift; + pending_frames = hw->pending_emul / hw->info.bytes_per_frame;/* if there are not enough samples, set signal and return */ if (pending_frames < frameCount) {
@@ -234,7 +234,7 @@ static OSStatus audioDeviceIOProc(return 0 ; } - len = frameCount << hw->info.shift; + len = frameCount * hw->info.bytes_per_frame;while (len) {size_t write_len;ssize_t start = ((ssize_t ) hw->pos_emul) - hw->pending_emul;
audio/dsound_template.h +5 -5
@@ -98,8 +98,8 @@ static int glue (dsound_lock_, TYPE) ( goto fail; } -if ((p1p && *p1p && (*blen1p & info->align)) || - (p2p && *p2p && (*blen2p & info->align))) { +if ((p1p && *p1p && (*blen1p % info->bytes_per_frame)) || + (p2p && *p2p && (*blen2p % info->bytes_per_frame))) {dolog ("DirectSound returned misaligned buffer %ld %ld\n" , *blen1p, *blen2p); glue (dsound_unlock_, TYPE) (buf, *p1p, p2p ? *p2p : NULL, *blen1p,
@@ -247,14 +247,14 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as, obt_as.endianness =0 ;audio_pcm_init_info (&hw->info, &obt_as); -if (bc.dwBufferBytes & hw->info.align) { +if (bc.dwBufferBytes % hw->info.bytes_per_frame) {dolog ("GetCaps returned misaligned buffer size %ld, alignment %d\n" , - bc.dwBufferBytes, hw->info.align +1 + bc.dwBufferBytes, hw->info.bytes_per_frame ); } hw->size_emul = bc.dwBufferBytes; - ds->samples = bc.dwBufferBytes >> hw->info.shift; + ds->samples = bc.dwBufferBytes / hw->info.bytes_per_frame; ds->s = s;#ifdef DEBUG_DSOUND
audio/dsoundaudio.c +2 -2
@@ -321,8 +321,8 @@ static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb,return ; } - len1 = blen1 >> hw->info.shift; - len2 = blen2 >> hw->info.shift; + len1 = blen1 / hw->info.bytes_per_frame; + len2 = blen2 / hw->info.bytes_per_frame;#ifdef DEBUG_DSOUND dolog ("clear %p,%ld,%ld %p,%ld,%ld\n",
audio/noaudio.c +1 -1
@@ -86,7 +86,7 @@ static size_t no_read(HWVoiceIn *hw, void *buf, size_t size)NoVoiceIn *no = (NoVoiceIn *) hw;int64_t bytes =audio_rate_get_bytes (&hw->info, &no->rate, size); -audio_pcm_info_clear_buf (&hw->info, buf, bytes >> hw->info.shift); +audio_pcm_info_clear_buf (&hw->info, buf, bytes / hw->info.bytes_per_frame);return bytes; }
audio/ossaudio.c +7 -7
@@ -510,16 +510,16 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as, oss->nfrags = obt.nfrags; oss->fragsize = obt.fragsize; -if (obt.nfrags * obt.fragsize & hw->info.align) { +if (obt.nfrags * obt.fragsize % hw->info.bytes_per_frame) {dolog ("warning: Misaligned DAC buffer, size %d, alignment %d\n" , - obt.nfrags * obt.fragsize, hw->info.align +1 ); + obt.nfrags * obt.fragsize, hw->info.bytes_per_frame); } - oss->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift; + oss->samples = (obt.nfrags * obt.fragsize) / hw->info.bytes_per_frame; oss->mmapped =0 ;if (oopts->has_try_mmap && oopts->try_mmap) { - hw->size_emul = oss->samples << hw->info.shift; + hw->size_emul = oss->samples * hw->info.bytes_per_frame; hw->buf_emul = mmap ( NULL, hw->size_emul,
@@ -654,12 +654,12 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) oss->nfrags = obt.nfrags; oss->fragsize = obt.fragsize; -if (obt.nfrags * obt.fragsize & hw->info.align) { +if (obt.nfrags * obt.fragsize % hw->info.bytes_per_frame) {dolog ("warning: Misaligned ADC buffer, size %d, alignment %d\n" , - obt.nfrags * obt.fragsize, hw->info.align +1 ); + obt.nfrags * obt.fragsize, hw->info.bytes_per_frame); } - oss->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift; + oss->samples = (obt.nfrags * obt.fragsize) / hw->info.bytes_per_frame; oss->fd = fd; oss->dev = dev;
audio/spiceaudio.c +3 -2
@@ -131,8 +131,9 @@ static void *line_out_get_buffer(HWVoiceOut *hw, size_t *size) out->fpos =0 ; } - *size =audio_rate_get_bytes (&hw->info, &out->rate, - (out->fsize - out->fpos) << hw->info.shift); + *size =audio_rate_get_bytes ( + &hw->info, &out->rate, + (out->fsize - out->fpos) * hw->info.bytes_per_frame);return out->frame + out->fpos; }
audio/wavaudio.c +3 -3
@@ -40,14 +40,14 @@ static size_t wav_write_out(HWVoiceOut *hw, void *buf, size_t len) {WAVVoiceOut *wav = (WAVVoiceOut *) hw;int64_t bytes =audio_rate_get_bytes (&hw->info, &wav->rate, len); -assert (bytes >> hw->info.shift << hw->info.shift == bytes); +assert (bytes % hw->info.bytes_per_frame ==0 );if (bytes &&fwrite (buf, bytes,1 , wav->f) !=1 ) {dolog ("wav_write_out: fwrite of %zu bytes failed\nReaons: %s\n" , bytes,strerror (errno)); } - wav->total_samples += bytes >> hw->info.shift; + wav->total_samples += bytes / hw->info.bytes_per_frame;return bytes; }
@@ -130,7 +130,7 @@ static void wav_fini_out (HWVoiceOut *hw)WAVVoiceOut *wav = (WAVVoiceOut *) hw;uint8_t rlen[4 ];uint8_t dlen[4 ]; -uint32_t datalen = wav->total_samples << hw->info.shift; +uint32_t datalen = wav->total_samples * hw->info.bytes_per_frame;uint32_t rifflen = datalen +36 ;if (!wav->f) {
Signature · 1 line
2.5.0
PATCH v2 6/9 audio: basic support for multichannel audio
In reply to Kővágó, ZoltánWhich currently only means removing some checks. Old code won't require more than two channels, but new code will need it.
--- audio/alsaaudio.c | 7 ------- audio/audio.c | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-)
audio/alsaaudio.c +0 -7
@@ -499,13 +499,6 @@ static int alsa_open(bool in, struct alsa_params_req *req, goto err; } -if (nchannels !=1 && nchannels !=2 ) { -alsa_logerr2 (err, typ, -"Can not handle obtained number of channels %d\n" , - nchannels); - goto err; - } -if (pdo->buffer_count) {if (pdo->buffer_len) {int64_t req = pdo->buffer_len * pdo->buffer_count;
audio/audio.c +1 -1
@@ -210,7 +210,7 @@ static int audio_validate_settings (struct audsettings *as) {int invalid; - invalid = as->nchannels !=1 && as->nchannels !=2 ; + invalid = as->nchannels <1 ; invalid |= as->endianness !=0 && as->endianness !=1 ;switch (as->fmt) {
Signature · 1 line
2.5.0
PATCH v2 7/9 paaudio: channel-map option
In reply to Kővágó, ZoltánAdd an option to change the channel map used by pulseaudio. If not specified, falls back to an OSS compatible channel map.
--- audio/paaudio.c | 18 ++++++++++++++---- qapi/audio.json | 5 ++++- qemu-options.hx | 9 +++++++++ 3 files changed, 27 insertions(+), 5 deletions(-)
audio/paaudio.c +14 -4
@@ -336,17 +336,27 @@ static pa_stream *qpa_simple_new (pa_stream_direction_t dir, const char *dev, const pa_sample_spec *ss, - const pa_channel_map *map, + const char *map, const pa_buffer_attr *attr, int *rerror) { int r;pa_stream *stream;pa_stream_flags_t flags; +pa_channel_map pa_map;pa_threaded_mainloop_lock (c->mainloop); - stream =pa_stream_new (c->context, name, ss, map); +if (map && !pa_channel_map_parse (&pa_map, map)) { +dolog ("Invalid channel map specified: '%s'\n" , map); + map =NULL ; + } +if (!map) { +pa_channel_map_init_extend (&pa_map, ss->channels, +PA_CHANNEL_MAP_OSS ); + } + + stream =pa_stream_new (c->context, name, ss, &pa_map);if (!stream) { goto fail; }
@@ -423,7 +433,7 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,PA_STREAM_PLAYBACK , ppdo->has_name ? ppdo->name :NULL , &ss, -NULL ,/* channel map */ + ppdo->has_channel_map ? ppdo->channel_map :NULL , &ba,/* buffering attributes */ &error );
@@ -471,7 +481,7 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)PA_STREAM_RECORD , ppdo->has_name ? ppdo->name :NULL , &ss, -NULL ,/* channel map */ + ppdo->has_channel_map ? ppdo->channel_map :NULL ,NULL ,/* buffering attributes */ &error );
qapi/audio.json +4 -1
@@ -119,11 +119,14 @@ # # @name: #optional name of the sink/source to use # +# @channel-map: #optional channel map to use (default: OSS compatible map) +# # Since: 2.5 ## { 'struct': 'AudiodevPaPerDirectionOptions', 'data': { - '*name': 'str' } } + '*name': 'str', + '*channel-map': 'str' } } ## # @AudiodevPaOptions
qemu-options.hx +9 -0
@@ -365,6 +365,7 @@ DEF("audiodev", HAS_ARG, QEMU_OPTION_audiodev, "-audiodev pa,id=id[,prop[=value][,...]]\n" " server= PulseAudio server address\n" " sink|source.name= sink/source device name\n" + " sink|source.channel-map= channel map to use\n" #endif #ifdef CONFIG_SDL "-audiodev sdl,id=id[,prop[=value][,...]]\n"
@@ -518,6 +519,14 @@ Sets the PulseAudio @var{server} to connect to. @item sink|source.name=@var{sink} Use the specified sink/source for playback/recording. +@item sink|source.channel-map=@var{map} +Use the specified channel map. The default is an OSS compatible +channel map. Do not forget to escape commas inside the map: + +@example +-audiodev pa,id=example,sink.channel-map=front-left,,front-right +@end example + @end table @item -audiodev sdl,id=@var{id}[,@var{prop}[=@var{value}][,...]]
Signature · 1 line
2.5.0
PATCH v2 8/9 usb-audio: do not count on avail bytes actually available
In reply to Kővágó, ZoltánThis assumption is no longer true when mixeng is turned off.
--- hw/usb/dev-audio.c | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-)
hw/usb/dev-audio.c +18 -12
@@ -317,27 +317,28 @@ static int streambuf_put(struct streambuf *buf, USBPacket *p) {uint32_t free = buf->size - (buf->prod - buf->cons); -if (!free) { +if (free <USBAUDIO_PACKET_SIZE ) {return 0 ; } -assert (free >=USBAUDIO_PACKET_SIZE ); +usb_packet_copy (p, buf->data + (buf->prod % buf->size),USBAUDIO_PACKET_SIZE ); buf->prod +=USBAUDIO_PACKET_SIZE ;return USBAUDIO_PACKET_SIZE ; } -static uint8_t *streambuf_get (struct streambuf *buf) +static uint8_t *streambuf_get (struct streambuf *buf,size_t *len) {uint32_t used = buf->prod - buf->cons;uint8_t *data;if (!used) { + *len =0 ;return NULL ; } -assert (used >=USBAUDIO_PACKET_SIZE ); data = buf->data + (buf->cons % buf->size); - buf->cons +=USBAUDIO_PACKET_SIZE ; + *len =MIN (buf->prod - buf->cons, + buf->size - (buf->cons % buf->size));return data; }
@@ -369,16 +370,21 @@ static void output_callback(void *opaque, int avail)USBAudioState *s = opaque;uint8_t *data; -for (;;) { -if (avail <USBAUDIO_PACKET_SIZE ) { -return ; - } - data =streambuf_get (&s->out.buf); +while (avail) { +size_t written, len; + + data =streambuf_get (&s->out.buf, &len);if (!data) {return ; } -AUD_write (s->out.voice, data,USBAUDIO_PACKET_SIZE ); - avail -=USBAUDIO_PACKET_SIZE ; + + written =AUD_write (s->out.voice, data, len); + avail -= written; + s->out.buf.cons += written; + +if (written < len) { +return ; + } } }
Signature · 1 line
2.5.0