diff mbox

[RFC,v2] qapi for audio backends

Message ID 2e2fabdd19c4c81d40535244813fe42646557089.1433424889.git.DirtY.iCE.hu@gmail.com
State New
Headers show

Commit Message

=?UTF-8?B?Wm9sdMOhbiBLxZF2w6Fnw7M=?= June 4, 2015, 1:39 p.m. UTC
Changes from v1:
* fixed style issues
* moved definitions into a separate file
* documented undocumented options (hopefully)
* removed plive option. It was useless even years ago so it can probably safely
  go away: https://lists.nongnu.org/archive/html/qemu-devel/2012-03/msg02427.html
* removed verbose, debug options. Backends should use trace events instead.
* removed *_retries options from dsound. It's a kludge.
* moved buffer_usecs and buffer_count to the global config options. Some driver
  might ignore it (as they do not expose API to change them).
* wav backend: removed frequecy, format, channels as AudiodevPerDirectionOptions
  already have them.

Not sure about this one, so it's not yet in this patch:
* remove poll_mode: another obscure setting, and it's only matter of time until
  the code bitrots enough to break it.

This is a proposal to add structures into qapi to replace the existing
configuration structures used by audio backends currently. I'm going to use it
to implement a new way to specify audio backend options (an -audiodev command
line option, instead of environment variables. This will also allow us to use
multiple audio backends in one qemu instance), so the structure used here will
be the basis of the command line syntax.

This is currently more or less a direct translation of the current audio backend
options. I've changed some names, trying to accomplish a more consistent naming
scheme. I wouldn't be surprised if there were options that doesn't work if you
set their value to anything other than the default (for example, log to monitor
can crash qemu, QEMU_DSOUND_RESTOURE_RETRIES has a typo, so probably nobody used
it, etc). I've also tried to reduce copy-paste, when the same set of options can
be given to output and input (QEMU_*_DAC_* and QEMU_*_ADC_* options), also using
in and out instead of ADC and DAC, as in the world of SPDIF and HDMI it's
completely possible that your computer has nothing to do with analog converters.
Plus a non technician user probably has no idea what ADC and DAC stands for.

Any comment is appreciated.

Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
---
 qapi-schema.json |   3 +
 qapi/audio.json  | 225 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 228 insertions(+)
 create mode 100644 qapi/audio.json

Comments

Gerd Hoffmann June 4, 2015, 3:30 p.m. UTC | #1
Hi,

> Not sure about this one, so it's not yet in this patch:
> * remove poll_mode: another obscure setting, and it's only matter of time until
>   the code bitrots enough to break it.

I'd tend to drop this too, but it's probably good to check what exactly
it is doing and to test whenever it actually works.

> Any comment is appreciated.

Looks good to me as draft to start working with.  I expect we'll find
some details which need tweeking when implementing this.

cheers,
  Gerd
=?UTF-8?B?Wm9sdMOhbiBLxZF2w6Fnw7M=?= June 4, 2015, 10:08 p.m. UTC | #2
Hi,

2015-06-04 17:30 keltezéssel, Gerd Hoffmann írta:
>> Not sure about this one, so it's not yet in this patch:
>> * remove poll_mode: another obscure setting, and it's only matter of time until
>>    the code bitrots enough to break it.
>
> I'd tend to drop this too, but it's probably good to check what exactly
> it is doing and to test whenever it actually works.

In my quick test it actually worked (but that was with pulseaudio's alsa 
emulation). Plus currently only alsa an oss seem to care about this 
option, so even if we keep it, we should probably move it into alsa's 
and oss's backend options.

>> Any comment is appreciated.
>
> Looks good to me as draft to start working with.  I expect we'll find
> some details which need tweeking when implementing this.
>

Yeah, I've already hit a problem. The opts_visitor doesn't really handle 
nested structs (it just flattens it into a single, non hierarchic 
namespace), which is a problem because of the input and output options. 
First I need to make them required (the in and out in Audiodev) if I 
want the current visitor to visit them at all, but it's still not enough.

Doing something like -audiodev frequency=8000 sets the input frequency 
to 8000 and leaves output frequency undefined. I think I should add an 
additional syntax: -audiodev in.frequency=8000,out.frequency=16000 (of 
course it should support deeper nesting like foo.bar.baz.asd=13). The 
question is what should happen if the user specifies frequency=8000. I 
see two alternatives:

0. set every frequency field to 8000 (i.e. the same as 
in.frequency=8000,out.frequency=8000 in this example)
1. bail out with a parameter ambiguous error

In the case of audiodev, the 0. approach seems more straightforward (if 
the user sets frequency, he wants to set both input and output 
frequency), but in more generic scenarios, the 1. is maybe better.

Thanks,
Zoltan
Gerd Hoffmann June 5, 2015, 10:57 a.m. UTC | #3
Hi,

> Plus currently only alsa an oss seem to care about this 
> option, so even if we keep it, we should probably move it into alsa's 
> and oss's backend options.

Makes sense.

> > Looks good to me as draft to start working with.  I expect we'll find
> > some details which need tweeking when implementing this.
> 
> Yeah, I've already hit a problem. The opts_visitor doesn't really handle 
> nested structs (it just flattens it into a single, non hierarchic 
> namespace), which is a problem because of the input and output options. 
> First I need to make them required (the in and out in Audiodev) if I 
> want the current visitor to visit them at all, but it's still not enough.

I think we should improve the visitor instead of making in & out
mandatory just because the current implementation (which simply
implements the stuff needed for Netdev) can't handle it.

> Doing something like -audiodev frequency=8000 sets the input frequency 
> to 8000 and leaves output frequency undefined. I think I should add an 
> additional syntax: -audiodev in.frequency=8000,out.frequency=16000 (of 
> course it should support deeper nesting like foo.bar.baz.asd=13). The 
> question is what should happen if the user specifies frequency=8000. I 
> see two alternatives:
> 
> 0. set every frequency field to 8000 (i.e. the same as 
> in.frequency=8000,out.frequency=8000 in this example)
> 1. bail out with a parameter ambiguous error
> 
> In the case of audiodev, the 0. approach seems more straightforward (if 
> the user sets frequency, he wants to set both input and output 
> frequency),

Agree.

>  but in more generic scenarios, the 1. is maybe better.

There is always the option to be more specific (in.frequency=...) if
setting all parameters named 'frequency' doesn't do what you want.  Also
I wouldn't worry too much about possible cases which don't exist right
now.  I'd suggest to go for (0).

cheers,
  Gerd
=?UTF-8?B?Wm9sdMOhbiBLxZF2w6Fnw7M=?= June 5, 2015, 1:54 p.m. UTC | #4
Hi,

2015-06-05 12:57 keltezéssel, Gerd Hoffmann írta:
>> Yeah, I've already hit a problem. The opts_visitor doesn't really handle
>> nested structs (it just flattens it into a single, non hierarchic
>> namespace), which is a problem because of the input and output options.
>> First I need to make them required (the in and out in Audiodev) if I
>> want the current visitor to visit them at all, but it's still not enough.
>
> I think we should improve the visitor instead of making in & out
> mandatory just because the current implementation (which simply
> implements the stuff needed for Netdev) can't handle it.

It's not that simple I think. The visit_optional only receives a field 
name, but no info about the type of the field, but it has to decide if 
it wants the field using only this info. So sort of hacking an if 
(strcmp(name, "in") == 0 || strcmp(name, "out") == 0) ... into the 
option visitor code, the only way is probably to add a type parameter to 
visit_optional (struct/union/uint32/whatever) and in the opts visitor if 
type is struct or union, force visiting it. Is it ok?

> There is always the option to be more specific (in.frequency=...) if
> setting all parameters named 'frequency' doesn't do what you want.  Also
> I wouldn't worry too much about possible cases which don't exist right
> now.  I'd suggest to go for (0).

Alright.

Thanks,
Zoltan
Gerd Hoffmann June 8, 2015, 7:42 a.m. UTC | #5
On Fr, 2015-06-05 at 15:54 +0200, Kővágó Zoltán wrote:
> Hi,
> 
> 2015-06-05 12:57 keltezéssel, Gerd Hoffmann írta:
> >> Yeah, I've already hit a problem. The opts_visitor doesn't really handle
> >> nested structs (it just flattens it into a single, non hierarchic
> >> namespace), which is a problem because of the input and output options.
> >> First I need to make them required (the in and out in Audiodev) if I
> >> want the current visitor to visit them at all, but it's still not enough.
> >
> > I think we should improve the visitor instead of making in & out
> > mandatory just because the current implementation (which simply
> > implements the stuff needed for Netdev) can't handle it.
> 
> It's not that simple I think. The visit_optional only receives a field 
> name, but no info about the type of the field, but it has to decide if 
> it wants the field using only this info. So sort of hacking an if 
> (strcmp(name, "in") == 0 || strcmp(name, "out") == 0) ... into the 
> option visitor code, the only way is probably to add a type parameter to 
> visit_optional (struct/union/uint32/whatever) and in the opts visitor if 
> type is struct or union, force visiting it. Is it ok?

Thinking about this a bit more, I suspect it becomes tricky when it
comes to allocation.  We want allocate *in and *out only in case there
are actually parameters for it on the command line.

But I think we have to pass something down to the struct visitor (given
how visitors are designed).  So allocate, pass down, then free again in
case nothing was filled?  How do we figure nothing was filled, in the
generic visit_optional function?

Of course we can avoid this complexity by simply allocating *in and *out
unconditionally, but then it is pointless to make it optional in the
first place.  So maybe it is better to go with your original idea to
just make them mandatory (and all elements inside optional).

cheers,
  Gerd
diff mbox

Patch

diff --git a/qapi-schema.json b/qapi-schema.json
index 0662a9b..ebec127 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -5,6 +5,9 @@ 
 # QAPI common definitions
 { 'include': 'qapi/common.json' }
 
+# QAPI audio definitions
+{ 'include': 'qapi/audio.json' }
+
 # QAPI block definitions
 { 'include': 'qapi/block.json' }
 
diff --git a/qapi/audio.json b/qapi/audio.json
new file mode 100644
index 0000000..c864a77
--- /dev/null
+++ b/qapi/audio.json
@@ -0,0 +1,225 @@ 
+# -*- mode: python -*-
+
+##
+# @AudiodevNoneOptions
+#
+# The none, coreaudio, sdl and spice audio backend has no options.
+#
+# Since: 2.4
+##
+{ 'struct': 'AudiodevNoneOptions',
+  'data': { } }
+
+##
+# @AudiodevAlsaPerDirectionOptions
+#
+# Options of the alsa backend that are used for both playback and recording.
+#
+# @dev: #optional the name of the alsa device to use
+#
+# Since: 2.4
+##
+{ 'struct': 'AudiodevAlsaPerDirectionOptions',
+  'data': {
+    '*dev':                'str' } }
+
+##
+# @AudiodevAlsaOptions
+#
+# Options of the alsa audio backend.
+#
+# @in: #optional options of the capture stream
+#
+# @out: #optional options of the playback stream
+#
+# @threshold: #optional set the threshold (in frames) when playback starts
+#
+# Since: 2.4
+##
+{ 'struct': 'AudiodevAlsaOptions',
+  'data': {
+    '*in':  'AudiodevAlsaPerDirectionOptions',
+    '*out': 'AudiodevAlsaPerDirectionOptions',
+    '*threshold': 'int' } }
+
+##
+# @AudiodevDsoundOptions
+#
+# Options of the dsound audio backend.
+#
+# @set-primary: #optional set the parameters of primary buffer
+#
+# @latency-millis: #optional add extra latency to playback
+#
+# @primary-frequency: #optional primary buffer frequency
+#
+# @primary-channels: #optional primary buffer number of channels
+#
+# @primary-format: #optional primary buffer format
+#
+# Since: 2.4
+##
+{ 'struct': 'AudiodevDsoundOptions',
+  'data': {
+    '*set-primary':       'bool',
+    '*latency-millis':    'int',
+    '*primary-frequency': 'int',
+    '*primary-channels':  'int',
+    '*primary-format':    'AudioFormat' } }
+
+##
+# @AudiodevOssPerDirectionOptions
+#
+# Options of the oss backend that are used for both playback and recording.
+#
+# @dev: #optional path of the oss device
+#
+# Since: 2.4
+##
+{ 'struct': 'AudiodevOssPerDirectionOptions',
+  'data': {
+    '*dev': 'str' } }
+
+##
+# @AudiodevOssOptions
+#
+# Options of the oss audio backend.
+#
+# @in: #optional options of the capture stream
+#
+# @out: #optional options of the playback stream
+#
+# @mmap: #optional try using memory mapped access
+#
+# @exclusive: #optional open device in exclusive mode (vmix wont work)
+#
+# @dsp-policy: #optional set the timing policy of the device, -1 to use fragment
+#              mode (option ignored on some platforms)
+#
+# Since: 2.4
+##
+{ 'struct': 'AudiodevOssOptions',
+  'data': {
+    '*in':         'AudiodevOssPerDirectionOptions',
+    '*out':        'AudiodevOssPerDirectionOptions',
+    '*mmap':       'bool',
+    '*exclusive':  'bool',
+    '*dsp-policy': 'int' } }
+
+##
+# @AudiodevPaOptions
+#
+# Options of the pa (PulseAudio) audio backend.
+#
+# @server: #optional PulseAudio server address
+#
+# @sink: #optional sink device name
+#
+# @source: #optional source device name
+#
+# Since: 2.4
+##
+{ 'struct': 'AudiodevPaOptions',
+  'data': {
+    '*server':  'str',
+    '*sink':    'str',
+    '*source':  'str' } }
+
+##
+# @AudiodevWavOptions
+#
+# Options of the wav audio backend.
+#
+# @path: #optional path of the wav file to record
+#
+# Since: 2.4
+##
+{ 'struct': 'AudiodevWavOptions',
+  'data': {
+    '*path':      'str' } }
+
+
+##
+# @AudiodevBackendOptions
+#
+# A discriminated record of audio backends.
+#
+# Since: 2.4
+##
+{ 'union': 'AudiodevBackendOptions',
+  'data': {
+    'none':      'AudiodevNoneOptions',
+    'alsa':      'AudiodevAlsaOptions',
+    'coreaudio': 'AudiodevNoneOptions',
+    'dsound':    'AudiodevDsoundOptions',
+    'oss':       'AudiodevOssOptions',
+    'pa':        'AudiodevPaOptions',
+    'sdl':       'AudiodevNoneOptions',
+    'spice':     'AudiodevNoneOptions',
+    'wav':       'AudiodevWavOptions' } }
+
+##
+# @AudioFormat
+#
+# An enumeration of possible audio formats.
+#
+# Since: 2.4
+##
+{ 'enum': 'AudioFormat',
+  'data': [ 'u8', 's8', 'u16', 's16', 'u32', 's32' ] }
+
+##
+# @AudiodevPerDirectionOptions
+#
+# General audio backend options that are used for both playback and recording.
+#
+# @fixed-settings: #optional use fixed settings for host DAC/ADC
+#
+# @frequency: #optional frequency to use when using fixed settings
+#
+# @channels: #optional number of channels when using fixed settings
+#
+# @format: #optional sample fortmat to use when using fixed settings
+#
+# @buffer-usecs: #optional the buffer size in microseconds
+#
+# @buffer-count: #optional nuber of buffers
+#
+# @try-poll: #optional attempt to use poll mode
+#
+# Since: 2.4
+##
+{ 'struct': 'AudiodevPerDirectionOptions',
+  'data': {
+    '*fixed-settings': 'bool',
+    '*frequency':      'int',
+    '*channels':       'int',
+    '*format':         'AudioFormat',
+    '*buffer-usecs':   'int',
+    '*buffer-count':   'int',
+    '*try-poll':       'bool' } }
+
+##
+# @Audiodev
+#
+# Captures the configuration of an audio backend.
+#
+# @id: identifier of the backend
+#
+# @in: #optional options of the capture stream
+#
+# @out: #optional options of the playback stream
+#
+# @timer-period: #optional timer period in HZ (0 - use lowest possible)
+#
+# @opts: audio backend specific options
+#
+# Since: 2.4
+##
+{ 'struct': 'Audiodev',
+  'data': {
+    'id':            'str',
+    '*in':           'AudiodevPerDirectionOptions',
+    '*out':          'AudiodevPerDirectionOptions',
+    '*timer-period': 'int',
+    'opts':          'AudiodevBackendOptions' } }