qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH 0/5] audio: multi channel audio support
@ 2015-08-17 16:41 Kővágó, Zoltán
  2015-08-17 16:55 ` [Qemu-devel] [PATCH 2/5] audio: basic support for multichannel audio Kővágó, Zoltán
                   ` (5 more replies)
  0 siblings, 6 replies; 7+ messages in thread
From: Kővágó, Zoltán @ 2015-08-17 16:41 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

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,...).

Currently you have to specify the number of channels you want to use
when creating the usb-audio device, see the last commit for details.
To do this I currently dynamically allocate the whole USBDesc structure,
which looks ugly.  If there's a better way, please let me know!

These patches requires my previous -audiodev patches:
https://lists.gnu.org/archive/html/qemu-devel/2015-08/msg00827.html

Please review.

Kővágó, Zoltán (5):
  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           |  70 ++++----
 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/paaudio.c         |  18 +-
 audio/wavaudio.c        |   6 +-
 hw/usb/dev-audio.c      | 441 +++++++++++++++++++++++++++---------------------
 qapi/audio.json         |   5 +-
 qemu-options.hx         |   9 +
 13 files changed, 330 insertions(+), 273 deletions(-)

-- 
2.5.0

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [Qemu-devel] [PATCH 2/5] audio: basic support for multichannel audio
  2015-08-17 16:41 [Qemu-devel] [PATCH 0/5] audio: multi channel audio support Kővágó, Zoltán
@ 2015-08-17 16:55 ` Kővágó, Zoltán
  2015-08-17 16:55 ` [Qemu-devel] [PATCH 3/5] paaudio: channel-map option Kővágó, Zoltán
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Kővágó, Zoltán @ 2015-08-17 16:55 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

Which currently only means removing some checks.  Old code won't require
more than two channels, but new code will need it.

Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
---
 audio/alsaaudio.c | 7 -------
 audio/audio.c     | 2 +-
 2 files changed, 1 insertion(+), 8 deletions(-)

diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c
index 148df13..0e3fa1a 100644
--- a/audio/alsaaudio.c
+++ b/audio/alsaaudio.c
@@ -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;
diff --git a/audio/audio.c b/audio/audio.c
index bc32312..2aaedd4 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -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) {
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [Qemu-devel] [PATCH 3/5] paaudio: channel-map option
  2015-08-17 16:41 [Qemu-devel] [PATCH 0/5] audio: multi channel audio support Kővágó, Zoltán
  2015-08-17 16:55 ` [Qemu-devel] [PATCH 2/5] audio: basic support for multichannel audio Kővágó, Zoltán
@ 2015-08-17 16:55 ` Kővágó, Zoltán
  2015-08-17 16:55 ` [Qemu-devel] [PATCH 4/5] usb-audio: do not count on avail bytes actually available Kővágó, Zoltán
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Kővágó, Zoltán @ 2015-08-17 16:55 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann, Markus Armbruster

Add an option to change the channel map used by pulseaudio.  If not
specified, falls back to an ALSA compatible channel map.

Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
---
 audio/paaudio.c | 18 +++++++++++-------
 qapi/audio.json |  5 ++++-
 qemu-options.hx |  9 +++++++++
 3 files changed, 24 insertions(+), 8 deletions(-)

diff --git a/audio/paaudio.c b/audio/paaudio.c
index d2267ee..10fc54f 100644
--- a/audio/paaudio.c
+++ b/audio/paaudio.c
@@ -336,23 +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 my_map;
+    pa_channel_map pa_map;
 
     pa_threaded_mainloop_lock(c->mainloop);
 
+    if (map && !pa_channel_map_parse(&pa_map, map)) {
+        dolog("Invalid channel map specified: '%s'\n", map);
+        map = NULL;
+    }
     if (!map) {
-        map = pa_channel_map_init_extend(&my_map, ss->channels,
-                                         PA_CHANNEL_MAP_ALSA);
+        pa_channel_map_init_extend(&pa_map, ss->channels,
+                                   PA_CHANNEL_MAP_ALSA);
     }
 
-    stream = pa_stream_new(c->context, name, ss, map);
+    stream = pa_stream_new(c->context, name, ss, &pa_map);
     if (!stream) {
         goto fail;
     }
@@ -428,7 +432,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
         );
@@ -476,7 +480,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
         );
diff --git a/qapi/audio.json b/qapi/audio.json
index 0216a10..9070ac5 100644
--- a/qapi/audio.json
+++ b/qapi/audio.json
@@ -119,11 +119,14 @@
 #
 # @name: #optional name of the sink/source to use
 #
+# @channel-map: #optional channel map to use (default: ALSA compatible map)
+#
 # Since: 2.5
 ##
 { 'struct': 'AudiodevPaPerDirectionOptions',
   'data': {
-    '*name': 'str' } }
+    '*name':        'str',
+    '*channel-map': 'str' } }
 
 ##
 # @AudiodevPaOptions
diff --git a/qemu-options.hx b/qemu-options.hx
index 8e5ab7c..382b0ab 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -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 ALSA 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}][,...]]
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [Qemu-devel] [PATCH 4/5] usb-audio: do not count on avail bytes actually available
  2015-08-17 16:41 [Qemu-devel] [PATCH 0/5] audio: multi channel audio support Kővágó, Zoltán
  2015-08-17 16:55 ` [Qemu-devel] [PATCH 2/5] audio: basic support for multichannel audio Kővágó, Zoltán
  2015-08-17 16:55 ` [Qemu-devel] [PATCH 3/5] paaudio: channel-map option Kővágó, Zoltán
@ 2015-08-17 16:55 ` Kővágó, Zoltán
  2015-08-17 18:11 ` [Qemu-devel] [PATCH 5/5] usb-audio: support more than two channels of audio Kővágó, Zoltán
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Kővágó, Zoltán @ 2015-08-17 16:55 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

This assumption is no longer true when mixeng is turned off.

Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
---
 hw/usb/dev-audio.c | 30 ++++++++++++++++++------------
 1 file changed, 18 insertions(+), 12 deletions(-)

diff --git a/hw/usb/dev-audio.c b/hw/usb/dev-audio.c
index 48ac992..f916ccc 100644
--- a/hw/usb/dev-audio.c
+++ b/hw/usb/dev-audio.c
@@ -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 = audio_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;
+        }
     }
 }
 
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [Qemu-devel] [PATCH 5/5] usb-audio: support more than two channels of audio
  2015-08-17 16:41 [Qemu-devel] [PATCH 0/5] audio: multi channel audio support Kővágó, Zoltán
                   ` (2 preceding siblings ...)
  2015-08-17 16:55 ` [Qemu-devel] [PATCH 4/5] usb-audio: do not count on avail bytes actually available Kővágó, Zoltán
@ 2015-08-17 18:11 ` Kővágó, Zoltán
  2015-08-17 18:12 ` [Qemu-devel] [PATCH 1/5] audio: replace shift in audio_pcm_info with bytes_per_frame Kővágó, Zoltán
  2015-08-19 23:46 ` [Qemu-devel] [PATCH 0/5] audio: multi channel audio support Gerd Hoffmann
  5 siblings, 0 replies; 7+ messages in thread
From: Kővágó, Zoltán @ 2015-08-17 18:11 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

This commit adds support for non stereo audio playback.  This commit
adds two new properties to usb-audio:

* channels = the number of channels
* channel-config = the channel config to use.  Should be 3 for stereo,
  0x3f for 5.1 and 0x63f for 7.1 audio.  See USB Device Class Definition
  for Audio Devices for other possible values
  (http://www.usb.org/developers/docs/devclass_docs/audio10.pdf, p34)

Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>

---

According to the spec the channel order is front left, front right,
center, lfe, surround left, surround right for 5.1 sound.  But Linux
with alsa seems to use front left, front right, surround left, surround
right, center, lfe, while Windows uses the order in the specification.

The default pulseaudio channel map currently is an ALSA compatible,
which means by default Linux guests will have correct audio while
Windows guests will have surround and center/lfe swapped.  I could
change the pulseaudio default to OSS like, but in that case Linux guest
would be wrong and Windows ok.

With alsa there is not much to do sort of writing a mini mixeng or
something like that, but you can easily add a new device to
/etc/asound.conf that swaps the channels:

pcm.swap {
    type route
    slave.pcm "default" # or whatever
    slave.channels 6

    ttable.0.0 1
    ttable.1.1 1
    ttable.2.4 1
    ttable.3.5 1
    ttable.4.2 1
    ttable.5.3 1
}

and use -audiodev alsa,id=foo,out.mixeng=off,alsa-out.dev=swap,...

(due to how usb and usb-audio works, you'll probably need
alsa-out.try-poll=off and some playing with threshold)


 hw/usb/dev-audio.c | 413 +++++++++++++++++++++++++++++------------------------
 1 file changed, 228 insertions(+), 185 deletions(-)

diff --git a/hw/usb/dev-audio.c b/hw/usb/dev-audio.c
index f916ccc..c0637a5 100644
--- a/hw/usb/dev-audio.c
+++ b/hw/usb/dev-audio.c
@@ -85,165 +85,190 @@ static const USBDescStrings usb_audio_stringtable = {
 /*
  * A Basic Audio Device uses these specific values
  */
-#define USBAUDIO_PACKET_SIZE     192
+#define USBAUDIO_PACKET_SIZE_BASE 96
+#define USBAUDIO_PACKET_SIZE(channels) (USBAUDIO_PACKET_SIZE_BASE * channels)
 #define USBAUDIO_SAMPLE_RATE     48000
 #define USBAUDIO_PACKET_INTERVAL 1
 
-static const USBDescIface desc_iface[] = {
-    {
-        .bInterfaceNumber              = 0,
-        .bNumEndpoints                 = 0,
-        .bInterfaceClass               = USB_CLASS_AUDIO,
-        .bInterfaceSubClass            = USB_SUBCLASS_AUDIO_CONTROL,
-        .bInterfaceProtocol            = 0x04,
-        .iInterface                    = STRING_USBAUDIO_CONTROL,
-        .ndesc                         = 4,
-        .descs = (USBDescOther[]) {
-            {
-                /* Headphone Class-Specific AC Interface Header Descriptor */
-                .data = (uint8_t[]) {
-                    0x09,                       /*  u8  bLength */
-                    USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
-                    DST_AC_HEADER,              /*  u8  bDescriptorSubtype */
-                    U16(0x0100),                /* u16  bcdADC */
-                    U16(0x2b),                  /* u16  wTotalLength */
-                    0x01,                       /*  u8  bInCollection */
-                    0x01,                       /*  u8  baInterfaceNr */
-                }
-            },{
-                /* Generic Stereo Input Terminal ID1 Descriptor */
-                .data = (uint8_t[]) {
-                    0x0c,                       /*  u8  bLength */
-                    USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
-                    DST_AC_INPUT_TERMINAL,      /*  u8  bDescriptorSubtype */
-                    0x01,                       /*  u8  bTerminalID */
-                    U16(0x0101),                /* u16  wTerminalType */
-                    0x00,                       /*  u8  bAssocTerminal */
-                    0x02,                       /* u16  bNrChannels */
-                    U16(0x0003),                /* u16  wChannelConfig */
-                    0x00,                       /*  u8  iChannelNames */
-                    STRING_INPUT_TERMINAL,      /*  u8  iTerminal */
-                }
-            },{
-                /* Generic Stereo Feature Unit ID2 Descriptor */
-                .data = (uint8_t[]) {
-                    0x0d,                       /*  u8  bLength */
-                    USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
-                    DST_AC_FEATURE_UNIT,        /*  u8  bDescriptorSubtype */
-                    0x02,                       /*  u8  bUnitID */
-                    0x01,                       /*  u8  bSourceID */
-                    0x02,                       /*  u8  bControlSize */
-                    U16(0x0001),                /* u16  bmaControls(0) */
-                    U16(0x0002),                /* u16  bmaControls(1) */
-                    U16(0x0002),                /* u16  bmaControls(2) */
-                    STRING_FEATURE_UNIT,        /*  u8  iFeature */
-                }
-            },{
-                /* Headphone Ouptut Terminal ID3 Descriptor */
-                .data = (uint8_t[]) {
-                    0x09,                       /*  u8  bLength */
-                    USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
-                    DST_AC_OUTPUT_TERMINAL,     /*  u8  bDescriptorSubtype */
-                    0x03,                       /*  u8  bUnitID */
-                    U16(0x0301),                /* u16  wTerminalType (SPK) */
-                    0x00,                       /*  u8  bAssocTerminal */
-                    0x02,                       /*  u8  bSourceID */
-                    STRING_OUTPUT_TERMINAL,     /*  u8  iTerminal */
-                }
-            }
-        },
-    },{
-        .bInterfaceNumber              = 1,
-        .bAlternateSetting             = 0,
-        .bNumEndpoints                 = 0,
-        .bInterfaceClass               = USB_CLASS_AUDIO,
-        .bInterfaceSubClass            = USB_SUBCLASS_AUDIO_STREAMING,
-        .iInterface                    = STRING_NULL_STREAM,
-    },{
-        .bInterfaceNumber              = 1,
-        .bAlternateSetting             = 1,
-        .bNumEndpoints                 = 1,
-        .bInterfaceClass               = USB_CLASS_AUDIO,
-        .bInterfaceSubClass            = USB_SUBCLASS_AUDIO_STREAMING,
-        .iInterface                    = STRING_REAL_STREAM,
-        .ndesc                         = 2,
-        .descs = (USBDescOther[]) {
-            {
-                /* Headphone Class-specific AS General Interface Descriptor */
-                .data = (uint8_t[]) {
-                    0x07,                       /*  u8  bLength */
-                    USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
-                    DST_AS_GENERAL,             /*  u8  bDescriptorSubtype */
-                    0x01,                       /*  u8  bTerminalLink */
-                    0x00,                       /*  u8  bDelay */
-                    0x01, 0x00,                 /* u16  wFormatTag */
-                }
-            },{
-                /* Headphone Type I Format Type Descriptor */
-                .data = (uint8_t[]) {
-                    0x0b,                       /*  u8  bLength */
-                    USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
-                    DST_AS_FORMAT_TYPE,         /*  u8  bDescriptorSubtype */
-                    0x01,                       /*  u8  bFormatType */
-                    0x02,                       /*  u8  bNrChannels */
-                    0x02,                       /*  u8  bSubFrameSize */
-                    0x10,                       /*  u8  bBitResolution */
-                    0x01,                       /*  u8  bSamFreqType */
-                    U24(USBAUDIO_SAMPLE_RATE),  /* u24  tSamFreq */
-                }
-            }
-        },
-        .eps = (USBDescEndpoint[]) {
-            {
-                .bEndpointAddress      = USB_DIR_OUT | 0x01,
-                .bmAttributes          = 0x0d,
-                .wMaxPacketSize        = USBAUDIO_PACKET_SIZE,
-                .bInterval             = 1,
-                .is_audio              = 1,
-                /* Stereo Headphone Class-specific
-                   AS Audio Data Endpoint Descriptor */
-                .extra = (uint8_t[]) {
-                    0x07,                       /*  u8  bLength */
-                    USB_DT_CS_ENDPOINT,         /*  u8  bDescriptorType */
-                    DST_EP_GENERAL,             /*  u8  bDescriptorSubtype */
-                    0x00,                       /*  u8  bmAttributes */
-                    0x00,                       /*  u8  bLockDelayUnits */
-                    U16(0x0000),                /* u16  wLockDelay */
-                },
-            },
+static void *memdup(void *src, size_t len)
+{
+    void *ret = g_malloc(len);
+    memcpy(ret, src, len);
+    return ret;
+}
+
+static USBDesc *alloc_desc(uint32_t channels, uint32_t channel_config)
+{
+    USBDescIface *desc_iface;
+    USBDescDevice *desc_device;
+    USBDescConfig *desc_config;
+    USBDesc *desc_audio;
+
+    desc_iface = g_malloc0(sizeof(USBDescIface) * 3);
+
+    desc_iface[0].bInterfaceNumber   = 0;
+    desc_iface[0].bNumEndpoints      = 0;
+    desc_iface[0].bInterfaceClass    = USB_CLASS_AUDIO;
+    desc_iface[0].bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL;
+    desc_iface[0].bInterfaceProtocol = 0x04;
+    desc_iface[0].iInterface         = STRING_USBAUDIO_CONTROL;
+    desc_iface[0].ndesc              = 4;
+    desc_iface[0].descs              = g_malloc0(sizeof(USBDescOther) * 4);
+    desc_iface[0].descs[0].data = memdup(
+        /* Headphone Class-Specific AC Interface Header Descriptor */
+        (uint8_t[]) {
+            0x09,                       /*  u8  bLength */
+            USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
+            DST_AC_HEADER,              /*  u8  bDescriptorSubtype */
+            U16(0x0100),                /* u16  bcdADC */
+            U16(0x2b),                  /* u16  wTotalLength */
+            0x01,                       /*  u8  bInCollection */
+            0x01,                       /*  u8  baInterfaceNr */
+        }, 9);
+    desc_iface[0].descs[1].data = memdup(
+        /* Generic Stereo Input Terminal ID1 Descriptor */
+        (uint8_t[]) {
+            0x0c,                       /*  u8  bLength */
+            USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
+            DST_AC_INPUT_TERMINAL,      /*  u8  bDescriptorSubtype */
+            0x01,                       /*  u8  bTerminalID */
+            U16(0x0101),                /* u16  wTerminalType */
+            0x00,                       /*  u8  bAssocTerminal */
+            channels,                   /* u16  bNrChannels */
+            U16(channel_config),        /* u16  wChannelConfig */
+            0x00,                       /*  u8  iChannelNames */
+            STRING_INPUT_TERMINAL,      /*  u8  iTerminal */
+        }, 12);
+    desc_iface[0].descs[2].data = memdup(
+        /* Generic Stereo Feature Unit ID2 Descriptor */
+        (uint8_t[]) {
+            0x0d,                       /*  u8  bLength */
+            USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
+            DST_AC_FEATURE_UNIT,        /*  u8  bDescriptorSubtype */
+            0x02,                       /*  u8  bUnitID */
+            0x01,                       /*  u8  bSourceID */
+            0x02,                       /*  u8  bControlSize */
+            U16(0x0001),                /* u16  bmaControls(0) */
+            U16(0x0002),                /* u16  bmaControls(1) */
+            U16(0x0002),                /* u16  bmaControls(2) */
+            STRING_FEATURE_UNIT,        /*  u8  iFeature */
+        }, 13);
+    desc_iface[0].descs[3].data = memdup(
+        /* Headphone Ouptut Terminal ID3 Descriptor */
+        (uint8_t[]) {
+            0x09,                       /*  u8  bLength */
+            USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
+            DST_AC_OUTPUT_TERMINAL,     /*  u8  bDescriptorSubtype */
+            0x03,                       /*  u8  bUnitID */
+            U16(0x0301),                /* u16  wTerminalType (SPK) */
+            0x00,                       /*  u8  bAssocTerminal */
+            0x02,                       /*  u8  bSourceID */
+            STRING_OUTPUT_TERMINAL,     /*  u8  iTerminal */
+        }, 9);
+
+    desc_iface[1].bInterfaceNumber   = 1;
+    desc_iface[1].bAlternateSetting  = 0;
+    desc_iface[1].bNumEndpoints      = 0;
+    desc_iface[1].bInterfaceClass    = USB_CLASS_AUDIO;
+    desc_iface[1].bInterfaceSubClass = USB_SUBCLASS_AUDIO_STREAMING;
+    desc_iface[1].iInterface         = STRING_NULL_STREAM;
+
+    desc_iface[2].bInterfaceNumber   = 1;
+    desc_iface[2].bAlternateSetting  = 1;
+    desc_iface[2].bNumEndpoints      = 1;
+    desc_iface[2].bInterfaceClass    = USB_CLASS_AUDIO;
+    desc_iface[2].bInterfaceSubClass = USB_SUBCLASS_AUDIO_STREAMING;
+    desc_iface[2].iInterface         = STRING_REAL_STREAM;
+    desc_iface[2].ndesc              = 2;
+    desc_iface[2].descs              = g_malloc0(sizeof(USBDescOther) * 2);
+    desc_iface[2].descs[0].data = memdup(
+        /* Headphone Class-specific AS General Interface Descriptor */
+        (uint8_t[]) {
+            0x07,                       /*  u8  bLength */
+            USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
+            DST_AS_GENERAL,             /*  u8  bDescriptorSubtype */
+            0x01,                       /*  u8  bTerminalLink */
+            0x00,                       /*  u8  bDelay */
+            0x01, 0x00,                 /* u16  wFormatTag */
+        }, 7);
+    desc_iface[2].descs[1].data = memdup(
+        /* Headphone Type I Format Type Descriptor */
+        (uint8_t[]) {
+            0x0b,                       /*  u8  bLength */
+            USB_DT_CS_INTERFACE,        /*  u8  bDescriptorType */
+            DST_AS_FORMAT_TYPE,         /*  u8  bDescriptorSubtype */
+            0x01,                       /*  u8  bFormatType */
+            channels,                   /*  u8  bNrChannels */
+            0x02,                       /*  u8  bSubFrameSize */
+            0x10,                       /*  u8  bBitResolution */
+            0x01,                       /*  u8  bSamFreqType */
+            U24(USBAUDIO_SAMPLE_RATE),  /* u24  tSamFreq */
+        }, 11);
+    desc_iface[2].eps                     = g_malloc0(sizeof(USBDescEndpoint));
+    desc_iface[2].eps[0].bEndpointAddress = USB_DIR_OUT | 0x01;
+    desc_iface[2].eps[0].bmAttributes     = 0x0d;
+    desc_iface[2].eps[0].wMaxPacketSize   = USBAUDIO_PACKET_SIZE(channels);
+    desc_iface[2].eps[0].bInterval        = 1;
+    desc_iface[2].eps[0].is_audio         = 1;
+    /* Stereo Headphone Class-specific
+       AS Audio Data Endpoint Descriptor */
+    desc_iface[2].eps[0].extra = memdup((uint8_t[]) {
+            0x07,                       /*  u8  bLength */
+            USB_DT_CS_ENDPOINT,         /*  u8  bDescriptorType */
+            DST_EP_GENERAL,             /*  u8  bDescriptorSubtype */
+            0x00,                       /*  u8  bmAttributes */
+            0x00,                       /*  u8  bLockDelayUnits */
+            U16(0x0000),                /* u16  wLockDelay */
+        }, 7);
+
+    desc_device = g_malloc0(sizeof(USBDescDevice));
+    desc_device->bcdUSB              = 0x0100;
+    desc_device->bMaxPacketSize0     = 64;
+    desc_device->bNumConfigurations  = 1;
+    desc_config                      = g_malloc0(sizeof(USBDescConfig));
+    desc_device->confs               = desc_config;
+    desc_config->bNumInterfaces      = 2;
+    desc_config->bConfigurationValue = DEV_CONFIG_VALUE;
+    desc_config->iConfiguration      = STRING_CONFIG;
+    desc_config->bmAttributes        = USB_CFG_ATT_ONE | USB_CFG_ATT_SELFPOWER;
+    desc_config->bMaxPower           = 0x32;
+    desc_config->nif                 = 3;
+    desc_config->ifs                 = desc_iface;
+
+    desc_audio = g_malloc0(sizeof(USBDesc));
+    desc_audio->id.idVendor      = USBAUDIO_VENDOR_NUM;
+    desc_audio->id.idProduct     = USBAUDIO_PRODUCT_NUM;
+    desc_audio->id.bcdDevice     = 0;
+    desc_audio->id.iManufacturer = STRING_MANUFACTURER;
+    desc_audio->id.iProduct      = STRING_PRODUCT;
+    desc_audio->id.iSerialNumber = STRING_SERIALNUMBER;
+    desc_audio->full             = desc_device;
+    desc_audio->str              = usb_audio_stringtable;
+
+    return desc_audio;
+};
+
+static void free_desc(USBDesc *desc)
+{
+    int i, j;
+    USBDescIface *desc_iface = (USBDescIface *) desc->full->confs->ifs;
+
+    for (i = 0; i < desc->full->confs->nif; ++i) {
+        for (j = 0; j < desc_iface[i].ndesc; ++j) {
+            g_free((void *) desc_iface[i].descs[j].data);
+        }
+        g_free(desc_iface[i].descs);
+        if (desc_iface[i].eps) {
+            g_free(desc_iface[i].eps->extra);
+            g_free(desc_iface[i].eps);
         }
     }
-};
+    g_free(desc_iface);
 
-static const USBDescDevice desc_device = {
-    .bcdUSB                        = 0x0100,
-    .bMaxPacketSize0               = 64,
-    .bNumConfigurations            = 1,
-    .confs = (USBDescConfig[]) {
-        {
-            .bNumInterfaces        = 2,
-            .bConfigurationValue   = DEV_CONFIG_VALUE,
-            .iConfiguration        = STRING_CONFIG,
-            .bmAttributes          = USB_CFG_ATT_ONE | USB_CFG_ATT_SELFPOWER,
-            .bMaxPower             = 0x32,
-            .nif = ARRAY_SIZE(desc_iface),
-            .ifs = desc_iface,
-        },
-    },
-};
-
-static const USBDesc desc_audio = {
-    .id = {
-        .idVendor          = USBAUDIO_VENDOR_NUM,
-        .idProduct         = USBAUDIO_PRODUCT_NUM,
-        .bcdDevice         = 0,
-        .iManufacturer     = STRING_MANUFACTURER,
-        .iProduct          = STRING_PRODUCT,
-        .iSerialNumber     = STRING_SERIALNUMBER,
-    },
-    .full = &desc_device,
-    .str  = usb_audio_stringtable,
-};
+    g_free((void *) desc->full->confs);
+    g_free((void *) desc->full);
+    g_free(desc);
+}
 
 /*
  * A USB audio device supports an arbitrary number of alternate
@@ -298,10 +323,11 @@ struct streambuf {
     uint32_t cons;
 };
 
-static void streambuf_init(struct streambuf *buf, uint32_t size)
+static void streambuf_init(struct streambuf *buf, uint32_t size,
+                           uint32_t channels)
 {
     g_free(buf->data);
-    buf->size = size - (size % USBAUDIO_PACKET_SIZE);
+    buf->size = size - (size % USBAUDIO_PACKET_SIZE(channels));
     buf->data = g_malloc(buf->size);
     buf->prod = 0;
     buf->cons = 0;
@@ -313,18 +339,18 @@ static void streambuf_fini(struct streambuf *buf)
     buf->data = NULL;
 }
 
-static int streambuf_put(struct streambuf *buf, USBPacket *p)
+static int streambuf_put(struct streambuf *buf, USBPacket *p, uint32_t channels)
 {
     uint32_t free = buf->size - (buf->prod - buf->cons);
 
-    if (free < USBAUDIO_PACKET_SIZE) {
+    if (free < USBAUDIO_PACKET_SIZE(channels)) {
         return 0;
     }
 
     usb_packet_copy(p, buf->data + (buf->prod % buf->size),
-                    USBAUDIO_PACKET_SIZE);
-    buf->prod += USBAUDIO_PACKET_SIZE;
-    return USBAUDIO_PACKET_SIZE;
+                    USBAUDIO_PACKET_SIZE(channels));
+    buf->prod += USBAUDIO_PACKET_SIZE(channels);
+    return USBAUDIO_PACKET_SIZE(channels);
 }
 
 static uint8_t *streambuf_get(struct streambuf *buf, size_t *len)
@@ -352,14 +378,14 @@ typedef struct USBAudioState {
         enum usb_audio_altset altset;
         struct audsettings as;
         SWVoiceOut *voice;
-        bool mute;
-        uint8_t vol[2];
+        Volume vol;
         struct streambuf buf;
     } out;
 
     /* properties */
     uint32_t debug;
     uint32_t buffer;
+    uint32_t channels, channel_config;
 } USBAudioState;
 
 #define TYPE_USB_AUDIO "usb-audio"
@@ -392,7 +418,7 @@ static int usb_audio_set_output_altset(USBAudioState *s, int altset)
 {
     switch (altset) {
     case ALTSET_OFF:
-        streambuf_init(&s->out.buf, s->buffer);
+        streambuf_init(&s->out.buf, s->buffer, s->channels);
         AUD_set_active_out(s->out.voice, false);
         break;
     case ALTSET_ON:
@@ -426,33 +452,33 @@ static int usb_audio_get_control(USBAudioState *s, uint8_t attrib,
 
     switch (aid) {
     case ATTRIB_ID(MUTE_CONTROL, CR_GET_CUR, 0x0200):
-        data[0] = s->out.mute;
+        data[0] = s->out.vol.mute;
         ret = 1;
         break;
     case ATTRIB_ID(VOLUME_CONTROL, CR_GET_CUR, 0x0200):
-        if (cn < 2) {
-            uint16_t vol = (s->out.vol[cn] * 0x8800 + 127) / 255 + 0x8000;
+        if (cn < s->channels) {
+            uint16_t vol = (s->out.vol.vol[cn] * 0x8800 + 127) / 255 + 0x8000;
             data[0] = vol;
             data[1] = vol >> 8;
             ret = 2;
         }
         break;
     case ATTRIB_ID(VOLUME_CONTROL, CR_GET_MIN, 0x0200):
-        if (cn < 2) {
+        if (cn < s->channels) {
             data[0] = 0x01;
             data[1] = 0x80;
             ret = 2;
         }
         break;
     case ATTRIB_ID(VOLUME_CONTROL, CR_GET_MAX, 0x0200):
-        if (cn < 2) {
+        if (cn < s->channels) {
             data[0] = 0x00;
             data[1] = 0x08;
             ret = 2;
         }
         break;
     case ATTRIB_ID(VOLUME_CONTROL, CR_GET_RES, 0x0200):
-        if (cn < 2) {
+        if (cn < s->channels) {
             data[0] = 0x88;
             data[1] = 0x00;
             ret = 2;
@@ -474,12 +500,12 @@ static int usb_audio_set_control(USBAudioState *s, uint8_t attrib,
 
     switch (aid) {
     case ATTRIB_ID(MUTE_CONTROL, CR_SET_CUR, 0x0200):
-        s->out.mute = data[0] & 1;
+        s->out.vol.mute = data[0] & 1;
         set_vol = true;
         ret = 0;
         break;
     case ATTRIB_ID(VOLUME_CONTROL, CR_SET_CUR, 0x0200):
-        if (cn < 2) {
+        if (cn < s->channels) {
             uint16_t vol = data[0] + (data[1] << 8);
 
             if (s->debug) {
@@ -492,7 +518,7 @@ static int usb_audio_set_control(USBAudioState *s, uint8_t attrib,
                 vol = 255;
             }
 
-            s->out.vol[cn] = vol;
+            s->out.vol.vol[cn] = vol;
             set_vol = true;
             ret = 0;
         }
@@ -502,10 +528,9 @@ static int usb_audio_set_control(USBAudioState *s, uint8_t attrib,
     if (set_vol) {
         if (s->debug) {
             fprintf(stderr, "usb-audio: mute %d, lvol %3d, rvol %3d\n",
-                    s->out.mute, s->out.vol[0], s->out.vol[1]);
+                    s->out.vol.mute, s->out.vol.vol[0], s->out.vol.vol[1]);
         }
-        AUD_set_volume_out(s->out.voice, s->out.mute,
-                           s->out.vol[0], s->out.vol[1]);
+        audio_set_volume_out(s->out.voice, &s->out.vol);
     }
 
     return ret;
@@ -598,7 +623,7 @@ static void usb_audio_handle_dataout(USBAudioState *s, USBPacket *p)
         return;
     }
 
-    streambuf_put(&s->out.buf, p);
+    streambuf_put(&s->out.buf, p, s->channels);
     if (p->actual_length < p->iov.size && s->debug > 1) {
         fprintf(stderr, "usb-audio: output overrun (%zd bytes)\n",
                 p->iov.size - p->actual_length);
@@ -635,11 +660,27 @@ static void usb_audio_handle_destroy(USBDevice *dev)
     AUD_remove_card(&s->card);
 
     streambuf_fini(&s->out.buf);
+
+    free_desc((USBDesc *) dev->usb_desc);
 }
 
 static void usb_audio_realize(USBDevice *dev, Error **errp)
 {
     USBAudioState *s = USB_AUDIO(dev);
+    int i;
+
+    /* validate user options */
+    if (s->channels > AUDIO_MAX_CHANNELS) {
+        fprintf(stderr, "usb-audio: only %d channels supported\n",
+                AUDIO_MAX_CHANNELS);
+        s->channels = AUDIO_MAX_CHANNELS;
+    }
+    if (!s->buffer) {
+        /* 1 packet -> 1ms audio, the default audio timer period is 10ms */
+        s->buffer = 12 * USBAUDIO_PACKET_SIZE(s->channels);
+    }
+
+    dev->usb_desc = alloc_desc(s->channels, s->channel_config);
 
     usb_desc_create_serial(dev);
     usb_desc_init(dev);
@@ -647,18 +688,20 @@ static void usb_audio_realize(USBDevice *dev, Error **errp)
     AUD_register_card(TYPE_USB_AUDIO, &s->card);
 
     s->out.altset        = ALTSET_OFF;
-    s->out.mute          = false;
-    s->out.vol[0]        = 240; /* 0 dB */
-    s->out.vol[1]        = 240; /* 0 dB */
+    s->out.vol.mute      = false;
+    s->out.vol.channels  = s->channels;
+    for (i = 0; i < s->channels; ++i) {
+        s->out.vol.vol[i] = 240; /* 0 dB */
+    }
     s->out.as.freq       = USBAUDIO_SAMPLE_RATE;
-    s->out.as.nchannels  = 2;
+    s->out.as.nchannels  = s->channels;
     s->out.as.fmt        = AUDIO_FORMAT_S16;
     s->out.as.endianness = 0;
-    streambuf_init(&s->out.buf, s->buffer);
+    streambuf_init(&s->out.buf, s->buffer, s->channels);
 
     s->out.voice = AUD_open_out(&s->card, s->out.voice, TYPE_USB_AUDIO,
                                 s, output_callback, &s->out.as);
-    AUD_set_volume_out(s->out.voice, s->out.mute, s->out.vol[0], s->out.vol[1]);
+    audio_set_volume_out(s->out.voice, &s->out.vol);
     AUD_set_active_out(s->out.voice, 0);
 }
 
@@ -670,8 +713,9 @@ static const VMStateDescription vmstate_usb_audio = {
 static Property usb_audio_properties[] = {
     DEFINE_AUDIO_PROPERTIES(USBAudioState, card),
     DEFINE_PROP_UINT32("debug", USBAudioState, debug, 0),
-    DEFINE_PROP_UINT32("buffer", USBAudioState, buffer,
-                       8 * USBAUDIO_PACKET_SIZE),
+    DEFINE_PROP_UINT32("buffer", USBAudioState, buffer, 0),
+    DEFINE_PROP_UINT32("channels", USBAudioState, channels, 2),
+    DEFINE_PROP_UINT32("channel-config", USBAudioState, channel_config, 3),
     DEFINE_PROP_END_OF_LIST(),
 };
 
@@ -684,7 +728,6 @@ static void usb_audio_class_init(ObjectClass *klass, void *data)
     dc->props         = usb_audio_properties;
     set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
     k->product_desc   = "QEMU USB Audio Interface";
-    k->usb_desc       = &desc_audio;
     k->realize        = usb_audio_realize;
     k->handle_reset   = usb_audio_handle_reset;
     k->handle_control = usb_audio_handle_control;
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [Qemu-devel] [PATCH 1/5] audio: replace shift in audio_pcm_info with bytes_per_frame
  2015-08-17 16:41 [Qemu-devel] [PATCH 0/5] audio: multi channel audio support Kővágó, Zoltán
                   ` (3 preceding siblings ...)
  2015-08-17 18:11 ` [Qemu-devel] [PATCH 5/5] usb-audio: support more than two channels of audio Kővágó, Zoltán
@ 2015-08-17 18:12 ` Kővágó, Zoltán
  2015-08-19 23:46 ` [Qemu-devel] [PATCH 0/5] audio: multi channel audio support Gerd Hoffmann
  5 siblings, 0 replies; 7+ messages in thread
From: Kővágó, Zoltán @ 2015-08-17 18:12 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

The 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.

Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
---
 audio/alsaaudio.c       | 10 ++++----
 audio/audio.c           | 68 ++++++++++++++++++++++++-------------------------
 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/wavaudio.c        |  6 ++---
 9 files changed, 60 insertions(+), 61 deletions(-)

diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c
index 7834633..148df13 100644
--- a/audio/alsaaudio.c
+++ b/audio/alsaaudio.c
@@ -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;
diff --git a/audio/audio.c b/audio/audio.c
index ebe241d..bc32312 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -267,26 +267,27 @@ static int audio_pcm_info_eq (struct audio_pcm_info *info, struct audsettings *a
 
 void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as)
 {
-    int bits = 8, sign = 0, shift = 0;
+    int bits = 8, sign = 0, mul;
 
     switch (as->fmt) {
     case AUDIO_FORMAT_S8:
         sign = 1;
     case AUDIO_FORMAT_U8:
+        mul = 1;
         break;
 
     case AUDIO_FORMAT_S16:
         sign = 1;
     case AUDIO_FORMAT_U16:
         bits = 16;
-        shift = 1;
+        mul = 2;
         break;
 
     case AUDIO_FORMAT_S32:
         sign = 1;
     case AUDIO_FORMAT_U32:
         bits = 32;
-        shift = 2;
+        mul = 4;
         break;
 
     default:
@@ -297,9 +298,8 @@ void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as)
     info->bits = bits;
     info->sign = sign;
     info->nchannels = as->nchannels;
-    info->shift = (as->nchannels == 2) + shift;
-    info->align = (1 << info->shift) - 1;
-    info->bytes_per_second = info->freq << info->shift;
+    info->bytes_per_frame = as->nchannels * mul;
+    info->bytes_per_second = info->freq * info->bytes_per_frame;
     info->swap_endianness = (as->endianness != AUDIO_HOST_ENDIANNESS);
 }
 
@@ -310,26 +310,25 @@ void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len)
     }
 
     if (info->sign) {
-        memset (buf, 0x00, len << info->shift);
+        memset (buf, 0x00, len * info->bytes_per_frame);
     }
     else {
         switch (info->bits) {
         case 8:
-            memset (buf, 0x80, len << info->shift);
+            memset (buf, 0x80, len * info->bytes_per_frame);
             break;
 
         case 16:
             {
                 int i;
                 uint16_t *p = buf;
-                int shift = info->nchannels - 1;
                 short s = INT16_MAX;
 
                 if (info->swap_endianness) {
                     s = bswap16 (s);
                 }
 
-                for (i = 0; i < len << shift; i++) {
+                for (i = 0; i < len * info->nchannels; i++) {
                     p[i] = s;
                 }
             }
@@ -339,14 +338,13 @@ void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len)
             {
                 int i;
                 uint32_t *p = buf;
-                int shift = info->nchannels - 1;
                 int32_t s = INT32_MAX;
 
                 if (info->swap_endianness) {
                     s = bswap32 (s);
                 }
 
-                for (i = 0; i < len << shift; i++) {
+                for (i = 0; i < len * info->nchannels; i++) {
                     p[i] = s;
                 }
             }
@@ -530,7 +528,7 @@ static void audio_pcm_hw_clip_out(HWVoiceOut *hw, void *pcm_buf,
 
     while (len) {
         st_sample *src = hw->mix_buf->samples + pos;
-        uint8_t *dst = advance (pcm_buf, clipped << hw->info.shift);
+        uint8_t *dst = advance(pcm_buf, clipped * hw->info.bytes_per_frame);
         size_t samples_till_end_of_buf = hw->mix_buf->size - pos;
         size_t samples_to_clip = audio_MIN(len, samples_till_end_of_buf);
 
@@ -579,7 +577,7 @@ static size_t audio_pcm_sw_read(SWVoiceIn *sw, void *buf, size_t size)
         return 0;
     }
 
-    samples = size >> sw->info.shift;
+    samples = size / sw->info.bytes_per_frame;
     if (!live) {
         return 0;
     }
@@ -614,7 +612,7 @@ static size_t audio_pcm_sw_read(SWVoiceIn *sw, void *buf, size_t size)
 
     sw->clip (buf, sw->buf, ret);
     sw->total_hw_samples_acquired += total;
-    return ret << sw->info.shift;
+    return ret * sw->info.bytes_per_frame;
 }
 
 /*
@@ -688,7 +686,7 @@ static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t size)
     }
 
     wpos = (sw->hw->mix_buf->pos + live) % hwsamples;
-    samples = size >> sw->info.shift;
+    samples = size / sw->info.bytes_per_frame;
 
     dead = hwsamples - live;
     swlim = ((int64_t) dead << 32) / sw->ratio;
@@ -732,13 +730,13 @@ static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t size)
     dolog (
         "%s: write size %d ret %d total sw %d\n",
         SW_NAME (sw),
-        size >> sw->info.shift,
+        size / sw->info.bytes_per_frame,
         ret,
         sw->total_hw_samples_mixed
         );
 #endif
 
-    return ret << sw->info.shift;
+    return ret * sw->info.bytes_per_frame;
 }
 
 #ifdef DEBUG_AUDIO
@@ -838,7 +836,7 @@ size_t AUD_read(SWVoiceIn *sw, void *buf, size_t size)
 
 int AUD_get_buffer_size_out (SWVoiceOut *sw)
 {
-    return sw->hw->mix_buf->size << sw->hw->info.shift;
+    return sw->hw->mix_buf->size * sw->hw->info.bytes_per_frame;
 }
 
 void AUD_set_active_out (SWVoiceOut *sw, int on)
@@ -953,10 +951,10 @@ static size_t audio_get_avail (SWVoiceIn *sw)
     ldebug (
         "%s: get_avail live %d ret %" PRId64 "\n",
         SW_NAME (sw),
-        live, (((int64_t) live << 32) / sw->ratio) << sw->info.shift
+        live, (((int64_t) live << 32) / sw->ratio) * sw->info.bytes_per_frame
         );
 
-    return (((int64_t) live << 32) / sw->ratio) << sw->info.shift;
+    return (((int64_t) live << 32) / sw->ratio) * sw->info.bytes_per_frame;
 }
 
 static size_t audio_get_free(SWVoiceOut *sw)
@@ -979,10 +977,11 @@ static size_t audio_get_free(SWVoiceOut *sw)
 #ifdef DEBUG_OUT
     dolog ("%s: get_free live %d dead %d ret %" PRId64 "\n",
            SW_NAME (sw),
-           live, dead, (((int64_t) dead << 32) / sw->ratio) << sw->info.shift);
+           live, dead, (((int64_t) dead << 32) / sw->ratio) *
+           sw->info.bytes_per_frame);
 #endif
 
-    return (((int64_t) dead << 32) / sw->ratio) << sw->info.shift;
+    return (((int64_t) dead << 32) / sw->ratio) * sw->info.bytes_per_frame;
 }
 
 static void audio_capture_mix_and_clear(HWVoiceOut *hw, size_t rpos,
@@ -1001,7 +1000,7 @@ static void audio_capture_mix_and_clear(HWVoiceOut *hw, size_t rpos,
             while (n) {
                 size_t till_end_of_hw = hw->mix_buf->size - rpos2;
                 size_t to_write = audio_MIN (till_end_of_hw, n);
-                size_t bytes = to_write << hw->info.shift;
+                size_t bytes = to_write * hw->info.bytes_per_frame;
                 size_t written;
 
                 sw->buf = hw->mix_buf->samples + rpos2;
@@ -1031,10 +1030,11 @@ static size_t audio_pcm_hw_run_out(HWVoiceOut *hw, size_t live)
         size_t size, decr, proc;
         void *buf = hw->pcm_ops->get_buffer_out(hw, &size);
 
-        decr = audio_MIN(size >> hw->info.shift, live);
+        decr = audio_MIN(size / hw->info.bytes_per_frame, live);
         audio_pcm_hw_clip_out(hw, buf, decr);
-        proc = hw->pcm_ops->put_buffer_out(hw, buf, decr << hw->info.shift) >>
-            hw->info.shift;
+        proc = hw->pcm_ops->put_buffer_out(hw, buf,
+                                           decr * hw->info.bytes_per_frame) /
+            hw->info.bytes_per_frame;
 
         live -= proc;
         clipped += proc;
@@ -1269,7 +1269,7 @@ static void audio_run_capture (AudioState *s)
 
             for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) {
                 cb->ops.capture (cb->opaque, cap->buf,
-                                 to_capture << hw->info.shift);
+                                 to_capture * hw->info.bytes_per_frame);
             }
             rpos = (rpos + to_capture) % hw->mix_buf->size;
             live -= to_capture;
@@ -1322,7 +1322,7 @@ void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size)
     ssize_t start;
 
     if (unlikely(!hw->buf_emul)) {
-        size_t calc_size = hw->conv_buf->size << hw->info.shift;
+        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->pos_emul = hw->pending_emul = 0;
@@ -1358,7 +1358,7 @@ void audio_generic_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size)
 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.shift;
+        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;
@@ -1745,11 +1745,11 @@ CaptureVoiceOut *AUD_add_capture(
         audio_pcm_init_info (&hw->info, as);
 
         cap->buf = audio_calloc(AUDIO_FUNC, hw->mix_buf->size,
-                                1 << hw->info.shift);
+                                hw->info.bytes_per_frame);
         if (!cap->buf) {
             dolog ("Could not allocate capture buffer "
                    "(%zu samples, each %d bytes)\n",
-                   hw->mix_buf->size, 1 << hw->info.shift);
+                   hw->mix_buf->size, hw->info.bytes_per_frame);
             goto err3;
         }
 
@@ -2058,14 +2058,14 @@ size_t audio_rate_get_bytes(struct audio_pcm_info *info, RateCtl *rate,
     now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
     ticks = now - rate->start_ticks;
     bytes = muldiv64(ticks, info->bytes_per_second, get_ticks_per_sec());
-    samples = (bytes - rate->bytes_sent) >> info->shift;
+    samples = (bytes - rate->bytes_sent) / info->bytes_per_frame;
     if (samples < 0 || samples > 65536) {
         AUD_log(NULL, "Resetting rate control (%" PRId64 " samples)", samples);
         audio_rate_start(rate);
         samples = 0;
     }
 
-    ret = audio_MIN(samples << info->shift, bytes_avail);
+    ret = audio_MIN(samples * info->bytes_per_frame, bytes_avail);
     rate->bytes_sent += ret;
     return ret;
 }
diff --git a/audio/audio_int.h b/audio/audio_int.h
index 8b53c52..0c0289d 100644
--- a/audio/audio_int.h
+++ b/audio/audio_int.h
@@ -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;
 };
diff --git a/audio/coreaudio.c b/audio/coreaudio.c
index bf35b0b..deb2d99 100644
--- a/audio/coreaudio.c
+++ b/audio/coreaudio.c
@@ -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;
diff --git a/audio/dsound_template.h b/audio/dsound_template.h
index 6a10b67..d262c1e 100644
--- a/audio/dsound_template.h
+++ b/audio/dsound_template.h
@@ -98,8 +98,8 @@ static int glue (dsound_lock_, TYPE) (
         goto fail;
     }
 
-    if ((p1p && *p1p && (*blen1p & info->align)) ||
-        (p2p && *p2p && (*blen2p & info->align))) {
+    if ((p1 && (blen1 % info->bytes_per_frame)) ||
+        (p2 && (blen2 % 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
diff --git a/audio/dsoundaudio.c b/audio/dsoundaudio.c
index e55dd46..3efe218 100644
--- a/audio/dsoundaudio.c
+++ b/audio/dsoundaudio.c
@@ -320,8 +320,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",
diff --git a/audio/noaudio.c b/audio/noaudio.c
index 0618b60..32fbf64 100644
--- a/audio/noaudio.c
+++ b/audio/noaudio.c
@@ -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;
 }
 
diff --git a/audio/ossaudio.c b/audio/ossaudio.c
index a854d5a..b5efbbf 100644
--- a/audio/ossaudio.c
+++ b/audio/ossaudio.c
@@ -511,16 +511,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,
@@ -655,12 +655,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;
diff --git a/audio/wavaudio.c b/audio/wavaudio.c
index 5b35173..487a1bb 100644
--- a/audio/wavaudio.c
+++ b/audio/wavaudio.c
@@ -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) {
-- 
2.5.0

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [Qemu-devel] [PATCH 0/5] audio: multi channel audio support
  2015-08-17 16:41 [Qemu-devel] [PATCH 0/5] audio: multi channel audio support Kővágó, Zoltán
                   ` (4 preceding siblings ...)
  2015-08-17 18:12 ` [Qemu-devel] [PATCH 1/5] audio: replace shift in audio_pcm_info with bytes_per_frame Kővágó, Zoltán
@ 2015-08-19 23:46 ` Gerd Hoffmann
  5 siblings, 0 replies; 7+ messages in thread
From: Gerd Hoffmann @ 2015-08-19 23:46 UTC (permalink / raw)
  To: Kővágó, Zoltán; +Cc: qemu-devel

  Hi,

> Currently you have to specify the number of channels you want to use
> when creating the usb-audio device, see the last commit for details.
> To do this I currently dynamically allocate the whole USBDesc structure,
> which looks ugly.  If there's a better way, please let me know!

You can have multiple USBDesc stuctures defined and pick one at device
initialization time (see hw/usb/dev-hid.c, there are usb 1.1 and usb 2.0
versions of all hid devices).

For usb-audio I think we need two: the current one (stereo) and a new
one with stereo + 5.1 and 7.1 support.  Then for the new one have 4
alternative interface configurations (off, stereo, 5.1, 7.1) instead of
only two (off, stereo).  Or possibly we need 3 interfaces (stereo, 5.1,
7.1) with on and off alternatives for each of them.  Didn't check
usb-audio spec.

cheers,
  Gerd

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2015-08-19 23:46 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-08-17 16:41 [Qemu-devel] [PATCH 0/5] audio: multi channel audio support Kővágó, Zoltán
2015-08-17 16:55 ` [Qemu-devel] [PATCH 2/5] audio: basic support for multichannel audio Kővágó, Zoltán
2015-08-17 16:55 ` [Qemu-devel] [PATCH 3/5] paaudio: channel-map option Kővágó, Zoltán
2015-08-17 16:55 ` [Qemu-devel] [PATCH 4/5] usb-audio: do not count on avail bytes actually available Kővágó, Zoltán
2015-08-17 18:11 ` [Qemu-devel] [PATCH 5/5] usb-audio: support more than two channels of audio Kővágó, Zoltán
2015-08-17 18:12 ` [Qemu-devel] [PATCH 1/5] audio: replace shift in audio_pcm_info with bytes_per_frame Kővágó, Zoltán
2015-08-19 23:46 ` [Qemu-devel] [PATCH 0/5] audio: multi channel audio support Gerd Hoffmann

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).