Re: [PATCH v3] audio/jack: add JACK client audiodev
On 2020-04-29 22:59, Eric Blake wrote: On 4/29/20 12:53 AM, Geoffrey McRae wrote: This commit adds a new audiodev backend to allow QEMU to use JACK as both an audio sink and source. Signed-off-by: Geoffrey McRae --- audio/Makefile.objs| 5 + audio/audio.c | 1 + audio/audio_template.h | 2 + audio/jackaudio.c | 615 + configure | 17 ++ qapi/audio.json| 50 +++- Focusing just on UI: +++ b/qapi/audio.json @@ -152,6 +152,51 @@ '*out': 'AudiodevPerDirectionOptions', '*latency': 'uint32' } } +## +# @AudiodevJackPerDirectionOptions: +# +# Options of the JACK backend that are used for both playback and +# recording. +# +# @server_name: select from among several possible concurrent server instances. +# If unspecified, use "default" unless $JACK_DEFAULT_SERVER is defined in the +# process environment. Our convention is to prefer '-' over '_' except in cases of pre-existing consistency. This should be 'server-name' unless you have an example of what we have to be consistent with. No reason at all just didn't think about it, I will correct it :) +# +# @client_name: the client name to use. The server will modify this name to +# create a unique variant, if needed unless @exact_name is true. +# +# @start_server: set to true to start a jack server instance if one is not +# present. +# +# @exact_name: use the exact name requested otherwise JACK automatically +# generates a unique one, if needed. Ditto for these three. +# +# Since: 4.0 The earliest this will be added is 5.1, not 4.0. Yup, copy & paste error :) +## +{ 'struct': 'AudiodevJackPerDirectionOptions', + 'base': 'AudiodevPerDirectionOptions', + 'data': { +'*server_name': 'str', +'*client_name': 'str', +'*start_server': 'bool', +'*exact_name': 'bool' } } + +## +# @AudiodevJackOptions: +# +# Options of the JACK audio backend. +# +# @in: options of the capture stream +# +# @out: options of the playback stream +# +# Since: 4.0 5.1 +## +{ 'struct': 'AudiodevJackOptions', + 'data': { +'*in': 'AudiodevJackPerDirectionOptions', +'*out': 'AudiodevJackPerDirectionOptions' } } + ## # @AudiodevOssPerDirectionOptions: # @@ -300,8 +345,8 @@ # Since: 4.0 ## { 'enum': 'AudiodevDriver', - 'data': [ 'none', 'alsa', 'coreaudio', 'dsound', 'oss', 'pa', 'sdl', -'spice', 'wav' ] } + 'data': [ 'none', 'alsa', 'coreaudio', 'dsound', 'jack', 'oss', 'pa', +'sdl', 'spice', 'wav' ] } It's worth adding a doc comment that @jack was added in 5.1 (I didn't check if audio.json has an example of adding to an enum, but other .json files do) No worries, I will see if I can figure this out. -Geoff
Re: [PATCH v3] audio/jack: add JACK client audiodev
On 4/29/20 12:53 AM, Geoffrey McRae wrote: This commit adds a new audiodev backend to allow QEMU to use JACK as both an audio sink and source. Signed-off-by: Geoffrey McRae --- audio/Makefile.objs| 5 + audio/audio.c | 1 + audio/audio_template.h | 2 + audio/jackaudio.c | 615 + configure | 17 ++ qapi/audio.json| 50 +++- Focusing just on UI: +++ b/qapi/audio.json @@ -152,6 +152,51 @@ '*out': 'AudiodevPerDirectionOptions', '*latency': 'uint32' } } +## +# @AudiodevJackPerDirectionOptions: +# +# Options of the JACK backend that are used for both playback and +# recording. +# +# @server_name: select from among several possible concurrent server instances. +# If unspecified, use "default" unless $JACK_DEFAULT_SERVER is defined in the +# process environment. Our convention is to prefer '-' over '_' except in cases of pre-existing consistency. This should be 'server-name' unless you have an example of what we have to be consistent with. +# +# @client_name: the client name to use. The server will modify this name to +# create a unique variant, if needed unless @exact_name is true. +# +# @start_server: set to true to start a jack server instance if one is not +# present. +# +# @exact_name: use the exact name requested otherwise JACK automatically +# generates a unique one, if needed. Ditto for these three. +# +# Since: 4.0 The earliest this will be added is 5.1, not 4.0. +## +{ 'struct': 'AudiodevJackPerDirectionOptions', + 'base': 'AudiodevPerDirectionOptions', + 'data': { +'*server_name': 'str', +'*client_name': 'str', +'*start_server': 'bool', +'*exact_name': 'bool' } } + +## +# @AudiodevJackOptions: +# +# Options of the JACK audio backend. +# +# @in: options of the capture stream +# +# @out: options of the playback stream +# +# Since: 4.0 5.1 +## +{ 'struct': 'AudiodevJackOptions', + 'data': { +'*in': 'AudiodevJackPerDirectionOptions', +'*out': 'AudiodevJackPerDirectionOptions' } } + ## # @AudiodevOssPerDirectionOptions: # @@ -300,8 +345,8 @@ # Since: 4.0 ## { 'enum': 'AudiodevDriver', - 'data': [ 'none', 'alsa', 'coreaudio', 'dsound', 'oss', 'pa', 'sdl', -'spice', 'wav' ] } + 'data': [ 'none', 'alsa', 'coreaudio', 'dsound', 'jack', 'oss', 'pa', +'sdl', 'spice', 'wav' ] } It's worth adding a doc comment that @jack was added in 5.1 (I didn't check if audio.json has an example of adding to an enum, but other .json files do) -- Eric Blake, Principal Software Engineer Red Hat, Inc. +1-919-301-3226 Virtualization: qemu.org | libvirt.org
[PATCH v3] audio/jack: add JACK client audiodev
This commit adds a new audiodev backend to allow QEMU to use JACK as both an audio sink and source. Signed-off-by: Geoffrey McRae --- audio/Makefile.objs| 5 + audio/audio.c | 1 + audio/audio_template.h | 2 + audio/jackaudio.c | 615 + configure | 17 ++ qapi/audio.json| 50 +++- 6 files changed, 688 insertions(+), 2 deletions(-) create mode 100644 audio/jackaudio.c diff --git a/audio/Makefile.objs b/audio/Makefile.objs index d7490a379f..b4a4c11f31 100644 --- a/audio/Makefile.objs +++ b/audio/Makefile.objs @@ -28,3 +28,8 @@ common-obj-$(CONFIG_AUDIO_SDL) += sdl.mo sdl.mo-objs = sdlaudio.o sdl.mo-cflags := $(SDL_CFLAGS) sdl.mo-libs := $(SDL_LIBS) + +# jack module +common-obj-$(CONFIG_AUDIO_JACK) += jack.mo +jack.mo-objs = jackaudio.o +jack.mo-libs := $(JACK_LIBS) diff --git a/audio/audio.c b/audio/audio.c index 7a9e680355..95d9fb16ca 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -1969,6 +1969,7 @@ void audio_create_pdos(Audiodev *dev) CASE(ALSA, alsa, Alsa); CASE(COREAUDIO, coreaudio, Coreaudio); CASE(DSOUND, dsound, ); +CASE(JACK, jack, Jack); CASE(OSS, oss, Oss); CASE(PA, pa, Pa); CASE(SDL, sdl, ); diff --git a/audio/audio_template.h b/audio/audio_template.h index 7013d3041f..8dd48ce14e 100644 --- a/audio/audio_template.h +++ b/audio/audio_template.h @@ -330,6 +330,8 @@ AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)(Audiodev *dev) dev->u.coreaudio.TYPE); case AUDIODEV_DRIVER_DSOUND: return dev->u.dsound.TYPE; +case AUDIODEV_DRIVER_JACK: +return qapi_AudiodevJackPerDirectionOptions_base(dev->u.jack.TYPE); case AUDIODEV_DRIVER_OSS: return qapi_AudiodevOssPerDirectionOptions_base(dev->u.oss.TYPE); case AUDIODEV_DRIVER_PA: diff --git a/audio/jackaudio.c b/audio/jackaudio.c new file mode 100644 index 00..a93d361ac4 --- /dev/null +++ b/audio/jackaudio.c @@ -0,0 +1,615 @@ +/* + * QEMU JACK Audio Connection Kit Client + * + * Copyright (c) 2020 Geoffrey McRae (gnif) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "qemu/module.h" +#include "qemu/fifo8.h" +#include "qemu-common.h" +#include "audio.h" + +#define AUDIO_CAP "jack" +#include "audio_int.h" + +#include +#include +#include + +struct QJack; + +typedef enum QJackState { + QJACK_STATE_DISCONNECTED, + QJACK_STATE_CONNECTED, + QJACK_STATE_IDLE, + QJACK_STATE_RUNNING, + QJACK_STATE_STOPPING, + QJACK_STATE_STOPPED, +} +QJackState; + +typedef struct QJackBuffer { + int channels; + int frames; + _Atomic(int) used; + int rptr, wptr; + float **data; +} +QJackBuffer; + +typedef struct QJackClient { + boolout; + QJackState state; + jack_client_t *client; + jack_nframes_t freq; + + struct QJack *j; + int nchannels; + int buffersize; + jack_port_t **port; + QJackBuffer fifo; +} +QJackClient; + +typedef struct QJackOut { +HWVoiceOut hw; +QJackClient c; +} +QJackOut; + +typedef struct QJackIn { +HWVoiceIn hw; +QJackClient c; +} +QJackIn; + +static void qjack_buffer_create(QJackBuffer *buffer, int channels, int frames) +{ +buffer->channels = channels; +buffer->frames = frames; +buffer->used = 0; +buffer->rptr = 0; +buffer->wptr = 0; +buffer->data = g_malloc(channels * sizeof(float *)); +for (int i = 0; i < channels; ++i) { +buffer->data[i] = g_malloc(frames * sizeof(float)); +} +} + +static void qjack_buffer_clear(QJackBuffer *buffer) +{ +atomic_store_explicit(>used, 0, memory_order_relaxed); +buffer->rptr = 0; +buffer->wptr = 0; +} + +static void qjack_buffer_free(QJackBuffer *buffer) +{ +for (int i = 0; i < buffer->channels; ++i) { +g_free(buffer->data[i]); +}