qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH 5/5] usb-audio: support more than two channels of au


From: Kővágó, Zoltán
Subject: [Qemu-devel] [PATCH 5/5] usb-audio: support more than two channels of audio
Date: Mon, 17 Aug 2015 20:11:58 +0200

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 <address@hidden>

---

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




reply via email to

[Prev in Thread] Current Thread [Next in Thread]