moved mixer control to correct path

This commit is contained in:
Tom Williams
2022-12-01 09:37:20 +00:00
parent 3130088c91
commit 785a857ca8
17 changed files with 87 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
all:
@echo =======================================================
@echo Build complete [module only - cannot be run on its own]
@echo =======================================================
clean:
@echo =======================================================
@echo Build clean [module only - cannot be run on its own]
@echo =======================================================

View File

@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>host_usb_mixer_control</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name>
<triggers>clean,full,incremental,</triggers>
<arguments>
<dictionary>
<key>?name?</key>
<value></value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.append_environment</key>
<value>true</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.autoBuildTarget</key>
<value>all</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.buildArguments</key>
<value></value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.buildCommand</key>
<value>xmake</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.cleanBuildTarget</key>
<value>clean</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.contents</key>
<value>org.eclipse.cdt.make.core.activeConfigSettings</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.enableAutoBuild</key>
<value>false</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.enableCleanBuild</key>
<value>true</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.enableFullBuild</key>
<value>true</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.fullBuildTarget</key>
<value>all</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.stopOnError</key>
<value>true</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.useDefaultBuildCmd</key>
<value>true</value>
</dictionary>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>
<nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
<nature>org.eclipse.cdt.core.cnature</nature>
</natures>
</projectDescription>

View File

@@ -0,0 +1,27 @@
Software License
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal with
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
• Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimers.
• Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimers in the documentation
and/or other materials provided with the distribution.
• Neither the name of XMOS, nor the names of its contributors may be used to
endorse or promote products derived from this Software without specific
prior written permission.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
SOFTWARE.

View File

@@ -0,0 +1,3 @@
All code contained in this package under XMOS copyright must be
licensing for any use from XMOS. Please contact support@xmos.com for
details of licensing.

View File

@@ -0,0 +1,2 @@
all:
g++ -g -o xmos_mixer OSX/usb_mixer.cpp mixer_app.cpp -I. -IOSX OSX/libusb-1.0.0.dylib -m64

View File

@@ -0,0 +1,4 @@
THESYCON_DIR = C:\Program/ Files\Thesycon\XMOSinternalUsbAudio_v5.50.0_eval_2022-11-17_SDK
all:
cl -o xmos_mixer.exe -I. -I Win32 -I $(THESYCON_DIR)\source\tusbaudioapi_inc -I $(THESYCON_DIR)\source\libwn_min mixer_app.cpp Win32\usb_mixer.cpp $(THESYCON_DIR)\lib\release\x64\tusbaudioapi.lib

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,940 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "usb_mixer.h"
#include "libusb.h"
/* Note: this is all quite generic and could be simplified ALOT for a specific design */
// TODO we dont really need to store mixer input strings
// Currently res, max, min dont get populated
#define XMOS_VID 0x20b1
uint16_t XMOS_PID[] = {
0x0002, //L1_AUDIO2
0x0003, //L1_AUDIO1
0x0004, //L2_AUDIO2
0x000E, //xk_216_AUDIO2
0x000F, //xk_216_AUDIO1
0x0016, //xk_316_AUDIO2
0x0017, //xk_316_AUDIO1
};
#define USB_REQUEST_TO_DEV 0x21 /* D7 Data direction: 0 (Host to device)
* D6:5 Type: 01 (Class)
* D4:0 Receipient: 1 (Interface) */
#define USB_REQUEST_FROM_DEV 0xa1
#define USB_CS_INTERFACE 0x24
#define USB_INPUT_TERM_TYPE 0x02
#define USB_MIXER_UNIT_TYPE 0x04
#define USB_FEATURE_UNIT_TYPE 0x06
#define INPUT_TERMINAL USB_INPUT_TERM_TYPE
#define EXTENSION_UNIT 0x9
#define CS_INTERFACE USB_CS_INTERFACE
#define FEATURE_UNIT 0x06
#define CS_XU_SEL 0x6
#define MU_MIXER_CONTROL 0x1
// Output from PC
#define USB_STREAMING 0x01
// Input to device
//#define MICROPHONE 0x02
#define ID_XU_OUT 51
#define ID_XU_IN 52
#define OFFSET_BLENGTH 0
#define OFFSET_BDESCRIPTORTYPE 1
#define OFFSET_BDESCRIPTORSUBTYPE 2
#define OFFSET_BUNITID 3
#define OFFSET_FU_BSOURCEID 4
#define OFFSET_XU_BNRINPINS 6
#define OFFSET_XU_BSOURCEID 7
#define OFFSET_IT_WTERMINALTYPE 5
#define OFFSET_IT_BNRCHANNELS 8
#define OFFSET_IT_ICHANNELNAMES 13
typedef struct
{
double min;
double max;
double res;
double weight;
} mixer_node;
typedef struct
{
unsigned int id;
unsigned int num_inputs;
char input_names[USB_MIXER_INPUTS][USB_MIXER_MAX_NAME_LEN]; /* Current mixer input names -
* we dont really need to store these */
int input_connections[USB_MIXER_INPUTS];
unsigned int num_outputs;
char output_names[USB_MIXER_INPUTS][USB_MIXER_MAX_NAME_LEN];
unsigned int num_inPins;
mixer_node nodes[USB_MIXER_INPUTS * USB_MIXER_OUTPUTS];
} usb_mixer_device;
typedef struct {
int cur;
int default_value;
char name[USB_MIXER_MAX_NAME_LEN];
enum usb_chan_type ctype;
}channel_map_node;
typedef struct {
int numInputs;
int numOutputs;
channel_map_node map[USB_MAX_CHANNEL_MAP_SIZE];
}channel_mapp;
typedef struct
{
unsigned int id;
unsigned int numInputs;
char inputStrings[USB_MIXER_INPUTS*4][USB_MIXER_MAX_NAME_LEN]; /* Complete list of all possible inputs */
unsigned int numOutputs;
unsigned int state[USB_MIXER_INPUTS];
} t_usb_mixSel;
typedef struct {
unsigned int device_open;
unsigned int num_usb_mixers;
usb_mixer_device usb_mixer[USB_MIXERS];
t_usb_mixSel usb_mixSel[USB_MIXERS];
channel_mapp audChannelMap;
channel_mapp usbChannelMap;
} usb_mixer_handle;
static usb_mixer_handle *usb_mixers = NULL;
static libusb_device_handle *devh = NULL;
bool is_supported_device(uint16_t pid)
{
for(uint16_t id : XMOS_PID)
{
if (pid == id) return true;
}
fprintf(stderr, "ERROR :: Device not supported\n");
return false;
}
/* Issue a generic control/class GET request to a specific unit in the Audio Interface */
int usb_audio_class_get(unsigned char bRequest, unsigned char cs, unsigned char cn, unsigned short unitID, unsigned short wLength, unsigned char *data)
{
return libusb_control_transfer(devh,
USB_REQUEST_FROM_DEV,
bRequest,
(cs<<8) | cn, /* wValue */
(unitID & 0xff) << 8 | 0x0,
data,
wLength,
0);
}
/* Issue a generic control/class SET request to a specific unit in the Audio Interface */
int usb_audio_class_set(unsigned char bRequest, unsigned char cs, unsigned char cn, unsigned short unitID, unsigned short wLength, unsigned char *data)
{
return libusb_control_transfer(devh,
USB_REQUEST_TO_DEV,
bRequest,
(cs<<8) | cn, /* wValue */
(unitID & 0xff) << 8 | 0x0,
data,
wLength,
0);
}
/* Note, this never get cached in an object since it can change on the device side */
int usb_mixer_mem_get(unsigned int mixer, unsigned offset, unsigned char *data)
{
return libusb_control_transfer(devh,
USB_REQUEST_FROM_DEV, /* nRequest */
MEM, /* bRequest */
offset, /* wValue */
(usb_mixers->usb_mixer[mixer].id & 0xff) << 8 | 0x0, /* wIndex */
data, 64, 0);
}
static const unsigned char *find_input_term_unit_by_id(const unsigned char *data, int length, int id)
{
const unsigned char *interface_data = data;
while (length)
{
const unsigned char *interface_len = interface_data;
int sub_type = *(interface_len + 2);
if (sub_type == USB_INPUT_TERM_TYPE)
{
int unit_id = *(interface_len + 3);
if (unit_id == id)
{
return interface_len;
}
}
interface_data+=*interface_len;
length -= *interface_len;
}
return NULL;
}
static const unsigned char *find_connected_feature_unit_by_id(const unsigned char *data, int length, int id) {
const unsigned char *interface_data = data;
while (length)
{
const unsigned char *interface_len = interface_data;
int sub_type = *(interface_len + 2);
if (sub_type == USB_FEATURE_UNIT_TYPE) {
//int unit_id = *(interface_len + 3);
int src_unit_id = *(interface_len + 4);
if (src_unit_id == id) {
return interface_len;
}
}
interface_data+=*interface_len;
length -= *interface_len;
}
return NULL;
}
static const unsigned char *findUnit(const unsigned char *descs, int length, int id)
{
const unsigned char *interface_data = descs;
while (length)
{
const unsigned char *interface_len = interface_data;
int bDescriptorType = *(interface_len + 1);
if (bDescriptorType == CS_INTERFACE)
{
int unit_id = *(interface_len + 3);
if (unit_id == id)
{
return interface_len;
}
}
interface_data+=*interface_len;
length -= *interface_len;
}
return NULL;
}
static int get_num_mixer_units(const unsigned char *data, int length) {
const unsigned char *interface_data = data;
int interface_len = length;
int num_mixer_units_found = 0;
while (interface_len) {
const unsigned char *interfaces = interface_data;
int interface_type = *(interfaces + 1);
int unit_type = *(interfaces + 2);
if (interface_type == USB_CS_INTERFACE && unit_type == USB_MIXER_UNIT_TYPE) {
num_mixer_units_found++;
}
interface_data+=*interfaces;
interface_len -= *interfaces;
}
return num_mixer_units_found;
}
static double dev_get_mixer_value(unsigned int mixer, unsigned int nodeId)
{
// MU_MIXER_CONTROL 0x01
short data;
usb_audio_class_get(CUR, 0x01<<8, nodeId, usb_mixers->usb_mixer[mixer].id, 2,(unsigned char *) &data);
return ((double) data / 256);
}
/* Populates min, max, res */
static unsigned short dev_get_mixer_range(unsigned int mixer, unsigned int channel,
double *min, double *max, double *res)
{
short data[64];
short min2, max2, res2;
usb_audio_class_get(RANGE, MU_MIXER_CONTROL, channel, usb_mixers->usb_mixer[mixer].id, 8, (unsigned char *) data);
min2 = data[1];
max2 = data[2];
res2 = data[3];
//printf("%f, %f, %f\n", (double)min2/256, (double)max2/256, (double) res2/256);
*min = (double)min2/256;
*max = (double)max2/256;
*res = (double)res2/256;
return 0;
}
int dev_get_channel_map(int channel, int unitId)
{
short data;
usb_audio_class_get(CUR, 0, channel, unitId, 2,(unsigned char *) &data);
return data;
}
static int dev_set_channel_map(int channel, int val, int unitId)
{
short value = val;
usb_audio_class_set(CUR, 0, channel, unitId, 1, (unsigned char *)&value);
return 0;
}
static int mixer_update_all_nodes(unsigned int mixer_index)
{
int i = 0;
int j = 0;
double min, max, res;
for (i = 0; i < usb_mixers->usb_mixer[mixer_index].num_inputs; i++)
{
for (j = 0; j < usb_mixers->usb_mixer[mixer_index].num_outputs; j++)
{
dev_get_mixer_range(mixer_index, (i*usb_mixers->usb_mixer[mixer_index].num_outputs)+j, &min, &max, &res);
usb_mixers->usb_mixer[mixer_index].nodes[(i*usb_mixers->usb_mixer[mixer_index].num_outputs)+j].min = min;
usb_mixers->usb_mixer[mixer_index].nodes[(i*usb_mixers->usb_mixer[mixer_index].num_outputs)+j].max = max;
usb_mixers->usb_mixer[mixer_index].nodes[(i*usb_mixers->usb_mixer[mixer_index].num_outputs)+j].res = res;
//printf("%f, %f, %f\n", min, max, res);
usb_mixers->usb_mixer[mixer_index].nodes[(i*usb_mixers->usb_mixer[mixer_index].num_outputs)+j].weight =
dev_get_mixer_value(mixer_index, (i*usb_mixers->usb_mixer[mixer_index].num_outputs)+j);
}
}
return 0;
}
/* Start at unit %id, find it in descs, keep recursively parsing up path(s) until get to Input Term and add strings */
int addStrings(const unsigned char *data, int length, int mixer_index, int id, int chanCount)
{
const unsigned char *currentUnitPtr = NULL;
/* Find this unit in the descs */
currentUnitPtr = findUnit(data, length, id);
if(currentUnitPtr != NULL)
{
/* Check if unit is a Input term */
if(*(currentUnitPtr+OFFSET_BDESCRIPTORSUBTYPE) == INPUT_TERMINAL)
{
/* Get channel names */
#ifdef DEBUG
printf("Input terminal found on path (ID: %d): %d channels, total: %d\n",*(currentUnitPtr+OFFSET_BUNITID),
*(currentUnitPtr+OFFSET_IT_BNRCHANNELS), chanCount);
#endif
int iChannelNames = *(currentUnitPtr+OFFSET_IT_ICHANNELNAMES);
int wTerminalType = *(currentUnitPtr+OFFSET_IT_WTERMINALTYPE);
#ifdef DEBUG
printf("iChannelNames: %d wTerminalType: %d\n", iChannelNames, wTerminalType);
printf("Channels found:\n");
#endif
for(int i = 0; i < *(currentUnitPtr+OFFSET_IT_BNRCHANNELS); i++)
{
unsigned char mixer_input_name[USB_MIXER_MAX_NAME_LEN];
memset(mixer_input_name, 0 ,USB_MIXER_MAX_NAME_LEN);
if (wTerminalType == 1)
{
strcpy(usb_mixers->usb_mixSel[mixer_index].inputStrings[chanCount], "DAW - ");
//usb_mixers->channel_maps[usb_mixers->num_channel_maps].ctype = (enum usb_chan_type) USB_CHAN_OUT;
usb_mixers->audChannelMap.numOutputs = usb_mixers->audChannelMap.numOutputs +1;
usb_mixers->audChannelMap.map[usb_mixers->audChannelMap.numInputs].ctype = (enum usb_chan_type) USB_CHAN_OUT;
usb_mixers->usbChannelMap.map[usb_mixers->usbChannelMap.numInputs].ctype = (enum usb_chan_type) USB_CHAN_OUT;
}
else
{
strcpy(usb_mixers->usb_mixSel[mixer_index].inputStrings[chanCount], "AUD - ");
//usb_mixers->channel_maps[usb_mixers->num_channel_maps].ctype = (enum usb_chan_type) USB_CHAN_IN;
usb_mixers->audChannelMap.map[usb_mixers->audChannelMap.numInputs].ctype = (enum usb_chan_type) USB_CHAN_IN;
usb_mixers->usbChannelMap.map[usb_mixers->usbChannelMap.numInputs].ctype = (enum usb_chan_type) USB_CHAN_IN;
usb_mixers->usbChannelMap.numOutputs = usb_mixers->usbChannelMap.numOutputs +1;
}
/* Get relevant string descriptor */
libusb_get_string_descriptor_ascii(devh, iChannelNames+i, mixer_input_name,
USB_MIXER_MAX_NAME_LEN - strlen(usb_mixers->usb_mixSel[mixer_index].inputStrings[chanCount]));
strcat(usb_mixers->usb_mixSel[mixer_index].inputStrings[chanCount], (char *)mixer_input_name);
/* Add to channel mappers also */
//strcat(usb_mixers->channel_maps[usb_mixers->num_channel_maps].name, (char *)mixer_input_name);
strcat(usb_mixers->audChannelMap.map[usb_mixers->audChannelMap.numInputs ].name, (char *)mixer_input_name);
strcat(usb_mixers->usbChannelMap.map[usb_mixers->audChannelMap.numInputs].name, (char *)mixer_input_name);
usb_mixers->audChannelMap.numInputs = usb_mixers->audChannelMap.numInputs +1;
usb_mixers->usbChannelMap.numInputs = usb_mixers->usbChannelMap.numInputs +1;
//usb_mixers->num_channel_maps = usb_mixers->num_channel_maps+1;
chanCount++;
}
#ifdef DEBUG
int meh = chanCount - *(currentUnitPtr+OFFSET_IT_BNRCHANNELS);
for(int i = 0; i < *(currentUnitPtr+OFFSET_IT_BNRCHANNELS); i++)
{
printf("%d: %s\n", i,usb_mixers->usb_mixSel[mixer_index].inputStrings[meh+i]);
}
printf("\n\n");
#endif
}
else
{
/* Unit not a input terminal, keep going... */
if(*(currentUnitPtr+OFFSET_BDESCRIPTORSUBTYPE) == FEATURE_UNIT)
{
chanCount = addStrings(data, length, mixer_index, *(currentUnitPtr+OFFSET_FU_BSOURCEID), chanCount);
}
else if(*(currentUnitPtr+OFFSET_BDESCRIPTORSUBTYPE) == EXTENSION_UNIT)
{
/* Multiple inputs for Extention units */
for (int i = 0; i < *(currentUnitPtr+OFFSET_XU_BNRINPINS); i++)
{
chanCount = addStrings(data, length, mixer_index, *(currentUnitPtr+OFFSET_XU_BSOURCEID+i), chanCount);
}
}
else
{
fprintf(stderr,"ERROR: Currently don't support this unit: %d\n",
*(currentUnitPtr+OFFSET_BDESCRIPTORSUBTYPE));
exit(1);
}
}
}
else
{
fprintf(stderr,"ERROR: Couldn't find unit %d in descs\n", id );
exit(1);
}
return chanCount;
}
/* Returns the source of an mix sel output */
static unsigned char get_mixsel_value(unsigned int mixer, unsigned int channel)
{
unsigned char data[64];
usb_audio_class_get(CUR, CS_XU_SEL, channel, usb_mixers->usb_mixSel[mixer].id, 1, (unsigned char *)data);
return data[0];
}
static int get_mixer_info(const unsigned char *data, int length, unsigned int mixer_index, libusb_config_descriptor *config_desc)
{
const unsigned char *interface_data = data;
int interface_len = length;
int num_mixer_units_found = 0;
//const unsigned char *current_input_term_unit_ptr = NULL;
//int current_input_term_unit_index = 0;
//const unsigned char *current_feature_unit_ptr = NULL;
int devChanInputCount = 0;
while (interface_len)
{
const unsigned char *interfaces = interface_data;
int interface_type = *(interfaces + 1); /* bDescriptorType */
int unit_type = *(interfaces + 2); /* bDescriptorSubType */
/* Look for a mixer unit */
if (interface_type == USB_CS_INTERFACE && unit_type == USB_MIXER_UNIT_TYPE)
{
int unit_id = *(interfaces + 3); /* bUnitId */
int bNrInPins = *(interfaces + 4);
int num_in = *(interfaces + 4); /* bNrInPins - NOTE This is pins NOT channels!! */
/* Total number of inputs is the sum of the channel counts in the input
* clusters. We need to inspect the sources to gain channel counts */
int chansIn = 0;
#ifdef DEBUG
printf("Found Mixer Unit %d with %d inputs\n", unit_id, bNrInPins);
printf("Inspecting mixer inputs... \n\n");
#endif
/* For each input pin need to find out inspect its output cluster */
for (int i = 1; i <= bNrInPins; i++)
{
int sourceId = *(interfaces+4+i);
#ifdef DEBUG
printf("baSourceID(%d): %d\n", i, sourceId);
#endif
/* Find the unit in the config desc */
int descsLength = length;
const unsigned char *descsData = data;
int found = 0;
int bDescriptorSubType;
int bDescriptorType;
int bUnitId;
while(descsLength)
{
int currentLength = *descsData;
bDescriptorSubType = *(descsData + 2);
bDescriptorType = *(descsData + 1);
if(bDescriptorType == USB_CS_INTERFACE)
{
if(bDescriptorSubType == USB_FEATURE_UNIT_TYPE ||
bDescriptorSubType == USB_INPUT_TERM_TYPE ||
bDescriptorSubType == EXTENSION_UNIT )
{
bUnitId = *(descsData+3);
if(bUnitId == sourceId)
{
found = 1;
break;
}
}
}
descsData+=currentLength;
descsLength -= currentLength;
}
if(found)
{
int bNrChannelsOffset = 0;
int bNrChannels;
/* We found the unit in the descs. Now inspect channel cluster */
#ifdef DEBUG
printf("Found unit %d, type %d\n", bUnitId, bDescriptorSubType);
#endif
/* We are looking for bNrChannels in the descs, this is in a different location in desc depending
* on unit type */
switch(bDescriptorSubType)
{
case USB_INPUT_TERM_TYPE:
bNrChannelsOffset = 8;
bNrChannels = *(descsData+bNrChannelsOffset);
break;
case EXTENSION_UNIT:
bNrChannelsOffset = 7 + *(descsData+6);
bNrChannels = *(descsData+bNrChannelsOffset);
break;
default:
printf("ERR\n");
exit(1);
break;
}
#ifdef DEBUG
printf("Output chan count: %d\n\n", bNrChannels);
#endif
chansIn += bNrChannels;
}
else
{
fprintf(stderr,"ERROR: Mixer input connected to something we dont understand...\n");
exit(1);
}
}
/* get number of output channels straight from mixer unit descriptor: bNrChannels */
int num_out = *(interfaces + 5 + num_in);
#ifdef DEBUG
printf("Mixer Unit parse complete: bUnitId: %d, Total Input Chans: %d, Output Chans: %d\n\n", unit_id, chansIn, num_out);
#endif
usb_mixers->usb_mixer[mixer_index].id = unit_id;
usb_mixers->usb_mixer[mixer_index].num_inputs = chansIn;
usb_mixers->usb_mixer[mixer_index].num_inPins = bNrInPins;
usb_mixers->usb_mixer[mixer_index].num_outputs = num_out;
/* Go through all input pins */
const unsigned char *in_unit_start_ptr = interfaces + 5;
// const unsigned char *currentUnitPtr = NULL;
// int current_input_term_unit_id = 0;
/* We expect this to be a single input from an XU, but we'll keep it slightly generic here */
for (int num = 0; num < usb_mixers->usb_mixer[mixer_index].num_inPins; num++)
{
/* Save source ID */
usb_mixers->usb_mixer[mixer_index].input_connections[num] = *(in_unit_start_ptr+num);
#ifdef DEBUG
printf("Inspecting for Input Terms from mixer unit input pin %d (id: %d)\n",
num,usb_mixers->usb_mixer[mixer_index].input_connections[num]);
#endif
devChanInputCount = addStrings(data, length, mixer_index,
usb_mixers->usb_mixer[mixer_index].input_connections[num], devChanInputCount);
}
/* The the first input pin at the mix select for the moment.
* probably should be checking if its an XU here */
usb_mixers->usb_mixSel[mixer_index].id = usb_mixers->usb_mixer[mixer_index].input_connections[0];
usb_mixers->usb_mixSel[mixer_index].numInputs = devChanInputCount;
usb_mixers->usb_mixSel[mixer_index].numOutputs = chansIn;
/* Set up mixer output names */
unsigned char mixer_output_name[USB_MIXER_MAX_NAME_LEN];
unsigned int iChannelNames = *(interfaces + 10 + bNrInPins);
for (int i = 0; i < usb_mixers->usb_mixer[mixer_index].num_outputs; i++)
{
/* Get relevant string descriptor */
strcpy(usb_mixers->usb_mixer[mixer_index].output_names[i], "MIX - ");
libusb_get_string_descriptor_ascii(devh, iChannelNames+i, mixer_output_name,
USB_MIXER_MAX_NAME_LEN - strlen(usb_mixers->usb_mixSel[mixer_index].inputStrings[i]));
strcat(usb_mixers->usb_mixer[mixer_index].output_names[i], (char *)mixer_output_name);
}
}
interface_data+=*interfaces;
interface_len -= *interfaces;
}
return num_mixer_units_found;
}
static int find_xmos_device(unsigned int id)
{
libusb_device *dev;
libusb_device **devs;
int i = 0;
int found = 0;
libusb_get_device_list(NULL, &devs);
while ((dev = devs[i++]) != NULL)
{
struct libusb_device_descriptor desc;
libusb_get_device_descriptor(dev, &desc);
// printf("VID = 0x%x, PID = 0x%x\n", desc.idVendor, desc.idProduct);
if (desc.idVendor == XMOS_VID && is_supported_device(desc.idProduct))
{
if (found == id)
{
if (libusb_open(dev, &devh) < 0)
{
return -1;
}
else
{
libusb_config_descriptor *config_desc = NULL;
libusb_get_active_config_descriptor(dev, &config_desc);
if (config_desc != NULL)
{
//unsigned int num_mixers_found = 0;
usb_mixers->device_open = 1;
usb_mixers->num_usb_mixers = 0;
for (int j = 0; j < config_desc->bNumInterfaces; j++)
{
const libusb_interface_descriptor *inter_desc =
((libusb_interface *)&config_desc->interface[j])->altsetting;
usb_mixers->num_usb_mixers += get_num_mixer_units(inter_desc->extra, inter_desc->extra_length);
}
for (int j = 0; j < config_desc->bNumInterfaces; j++)
{
const libusb_interface_descriptor *inter_desc =
((libusb_interface *)&config_desc->interface[j])->altsetting;
get_mixer_info(inter_desc->extra, inter_desc->extra_length, j, config_desc);
}
}
}
break;
}
found++;
}
}
libusb_free_device_list(devs, 1);
/* Init channel maps from device */
for(int i = 0; i < usb_mixers->audChannelMap.numOutputs; i++)
{
usb_mixers->audChannelMap.map[i].cur = dev_get_channel_map(i, ID_XU_OUT);
}
for(int i = 0; i < usb_mixers->usbChannelMap.numOutputs; i++)
{
usb_mixers->usbChannelMap.map[i].cur = dev_get_channel_map(i, ID_XU_IN);
//printf("%d\n", usb_mixers->usbChannelMap.map[i].cur);
}
/* Now add the mix outputs */
for(int i = 0; i < usb_mixers->num_usb_mixers; i++)
{
for(int j = 0; j < usb_mixers->usb_mixer[i].num_outputs;j++)
{
//strcpy(usb_mixers->channel_maps[usb_mixers->num_channel_maps].name, usb_mixers->usb_mixer[i].output_names[j]);
//usb_mixers->channel_maps[usb_mixers->num_channel_maps].ctype = (enum usb_chan_type) USB_CHAN_MIXER;
//usb_mixers->num_channel_maps = usb_mixers->num_channel_maps+1;
usb_mixers->audChannelMap.map[usb_mixers->audChannelMap.numInputs].ctype = (enum usb_chan_type) USB_CHAN_MIXER;
strcpy(usb_mixers->audChannelMap.map[usb_mixers->audChannelMap.numInputs].name, usb_mixers->usb_mixer[i].output_names[j]);
usb_mixers->audChannelMap.numInputs = usb_mixers->audChannelMap.numInputs +1;
usb_mixers->usbChannelMap.map[usb_mixers->usbChannelMap.numInputs].ctype = (enum usb_chan_type) USB_CHAN_MIXER;
strcpy(usb_mixers->usbChannelMap.map[usb_mixers->usbChannelMap.numInputs].name, usb_mixers->usb_mixer[i].output_names[j]);
usb_mixers->usbChannelMap.numInputs = usb_mixers->usbChannelMap.numInputs +1;
}
}
if(devh)
{
/* Populate mixer input strings */
for(int i = 0; i < usb_mixers->num_usb_mixers; i++)
{
mixer_update_all_nodes(i);
/* Get current each mixer input and populate channel number state and strings from device */
for (int j = 0; j < usb_mixers->usb_mixSel[i].numOutputs; j++)
{
/* Get value from device */
int inputChan = get_mixsel_value(i, j);
usb_mixers->usb_mixSel[i].state[j] = inputChan;
/* Look up channel string */
strcpy(usb_mixers->usb_mixer[i].input_names[j], usb_mixers->usb_mixSel[i].inputStrings[inputChan]);
}
}
}
return devh ? 0 : -1;
}
// End of libusb interface functions
int usb_mixer_connect()
{
int result = 0;
// Allocate internal storage
usb_mixers = (usb_mixer_handle *)malloc(sizeof(usb_mixer_handle));
memset(usb_mixers, 0, sizeof(usb_mixer_handle));
result = libusb_init(NULL);
if (result < 0)
{
// printf("libusb_init failure\n");
return USB_MIXER_FAILURE;
}
result = find_xmos_device(0);
if (result < 0)
{
// printf("find_xmos_device failure\n");
return USB_MIXER_FAILURE;
}
return USB_MIXER_SUCCESS;
}
int usb_mixer_disconnect() {
libusb_close(devh);
libusb_exit(NULL);
free(usb_mixers);
return USB_MIXER_SUCCESS;
}
/* Getter for num_usb_mixers */
int usb_mixer_get_num_mixers()
{
return usb_mixers->num_usb_mixers;
}
int usb_mixer_get_num_outputs(unsigned int mixer)
{
return usb_mixers->usb_mixer[mixer].num_outputs;
}
int usb_mixer_get_num_inputs(unsigned int mixer)
{
return usb_mixers->usb_mixer[mixer].num_inputs;
}
int usb_mixer_get_layout(unsigned int mixer, unsigned int *inputs, unsigned int *outputs) {
*inputs = usb_mixers->usb_mixer[mixer].num_inputs;
*outputs = usb_mixers->usb_mixer[mixer].num_outputs;
return 0;
}
/* MixSel getters and setters */
char *usb_mixsel_get_input_string(unsigned int mixer, unsigned int input)
{
return usb_mixers->usb_mixSel[mixer].inputStrings[input];
}
int usb_mixsel_get_input_count(unsigned int mixer)
{
return usb_mixers->usb_mixSel[mixer].numInputs;
}
int usb_mixsel_get_output_count(unsigned int mixer)
{
return usb_mixers->usb_mixSel[mixer].numOutputs;
}
char *usb_mixer_get_input_name(unsigned int mixer, unsigned int input) {
return usb_mixers->usb_mixer[mixer].input_names[input];
}
char *usb_mixer_get_output_name(unsigned int mixer, unsigned int output) {
return usb_mixers->usb_mixer[mixer].output_names[output];
}
unsigned char usb_mixsel_get_state(unsigned int mixer, unsigned int channel)
{
return usb_mixers->usb_mixSel[mixer].state[channel];
}
void usb_mixsel_set_state(unsigned int mixer, unsigned int dst, unsigned int src)
{
// write to device
usb_audio_class_set(CUR, CS_XU_SEL, dst, usb_mixers->usb_mixSel[mixer].id, 1, (unsigned char *)&src);
// update object state
usb_mixers->usb_mixSel[mixer].state[dst] = src;
// update local object strings
// TODO we don't really need to store strings since we can look them up...*/
strcpy(usb_mixers->usb_mixer[mixer].input_names[dst], usb_mixers->usb_mixSel[mixer].inputStrings[src]);
}
double usb_mixer_get_value(unsigned int mixer, unsigned int nodeId)
{
return (double)usb_mixers->usb_mixer[mixer].nodes[nodeId].weight;
}
double usb_mixer_get_res(unsigned int mixer, unsigned int nodeId)
{
return (double)usb_mixers->usb_mixer[mixer].nodes[nodeId].res;
}
double usb_mixer_get_min(unsigned int mixer, unsigned int nodeId)
{
return (double)usb_mixers->usb_mixer[mixer].nodes[nodeId].min;
}
double usb_mixer_get_max(unsigned int mixer, unsigned int nodeId)
{
return (double)usb_mixers->usb_mixer[mixer].nodes[nodeId].max;
}
int usb_mixer_set_value(unsigned int mixer, unsigned int nodeId, double val)
{
/* check if update required */
if(usb_mixers->usb_mixer[mixer].nodes[nodeId].weight != val)
{
/* update local object */
usb_mixers->usb_mixer[mixer].nodes[nodeId].weight= val;
/* write to device */
short value = (short) (val * 256);
usb_audio_class_set(CUR, 1, 1<<8 | nodeId & 0xff, usb_mixers->usb_mixer[mixer].id, 2, (unsigned char *)&value);
}
return 0;
}
int usb_mixer_get_range(unsigned int mixer, unsigned int mixer_unit, int *min, int *max, int *res)
{
// range 0x02
return 0;
}
int usb_get_usb_channel_map(int channel)
{
return usb_mixers->usbChannelMap.map[channel].cur;
}
int usb_get_aud_channel_map(int channel)
{
return usb_mixers->audChannelMap.map[channel].cur;
}
int usb_set_aud_channel_map(int channel, int val)
{
/* Check if update required */
if(usb_mixers->audChannelMap.map[channel].cur != val)
{
/* Update local object */
usb_mixers->audChannelMap.map[channel].cur = val;
/* Write setting to dev */
dev_set_channel_map(channel, val, ID_XU_OUT);
}
return 0;
}
int usb_set_usb_channel_map(int channel, int val)
{
/* Check if update required */
if(usb_mixers->usbChannelMap.map[channel].cur != val)
{
/* Update local object */
usb_mixers->usbChannelMap.map[channel].cur = val;
/* Write setting to dev */
dev_set_channel_map(channel, val, ID_XU_IN);
}
return 0;
}
enum usb_chan_type usb_get_aud_channel_map_type(int channel)
{
return usb_mixers->audChannelMap.map[channel].ctype;
}
enum usb_chan_type usb_get_usb_channel_map_type(int channel)
{
return usb_mixers->usbChannelMap.map[channel].ctype;
}
char *usb_get_aud_channel_map_name(int channel)
{
return usb_mixers->audChannelMap.map[channel].name;
}
char *usb_get_usb_channel_map_name(int channel)
{
return usb_mixers->usbChannelMap.map[channel].name;
}
int usb_get_aud_channel_map_num_outputs()
{
return usb_mixers->audChannelMap.numOutputs;
}
int usb_get_usb_channel_map_num_outputs()
{
return usb_mixers->usbChannelMap.numOutputs;
}
int usb_get_aud_channel_map_num_inputs()
{
return usb_mixers->audChannelMap.numInputs;
}
int usb_get_usb_channel_map_num_inputs()
{
return usb_mixers->usbChannelMap.numInputs;
}

View File

@@ -0,0 +1,103 @@
The XMOS USB Audio L2 Reference Design contains an 18x8 mixer unit
(note that at sample rates above 96Khz only the first two outputs are
enabled).
This unit takes input takes 18 inputs: USB OUT channels 1..10 and
DEVICE IN channels 1..6,9..10 and produces 8 outputs: Mixer Output
1..8
Before the mixer there is an unit that allows the selection of the 18 mixer inputs
from all the possible device inputs (DAW and physical audio). This is
an extention unit with id 50 in the descriptors
After the mixer unit there is are channel map units for each output terminal:
Each of these outputs can select a source from one of 28 channels sources: USB OUT
channels 1..10, DEVICE IN channels 1..10 and Mixer Output 1..8
The channel map units are extention unit with init ids 51 and 52. This unit
lets you implement arbitrary routings including loopbacks.
The mixer is control on MAC OS X via the command line utility
xmos_mixer. Running this application requires having the
libusb-1.0.0.dylib in the dynamic library load path. Sourcing the
setup.sh script will do this. Source code for the application is
provided as a guide on how to communicate with the device.
Here are the commands for the mixer application (note that the usb
audio reference design has only one unit so the mixer_id argument
should alway be 0):
--display-info
Show information about the device.
--display-mixer-nodes mixer_id
Display all the weights of all the mixer nodes (and their id) of a particular mixer.
--display-min mixer_id
Display the minimum allowable weights of a particular mixer.
--display-max mixer_id
Display the maximum allowable weights of a particular mixer.
--display-res mixer_id
Display the resolution of a particular mixer.
--set-value mixer_id mixer_unit value
Set the weight value in the mixer. The second argument should
correspond to the values shown by the --display-unit command. Values
can range from -127db to +128db with the special value -inf for mute.
--get-value mixer_id mixer_unit
Get the weight value in the mixer. The second argument should
correspond to the values shown by the --display-unit command. Values
can range from -127db to +128db with the special value -inf for mute.
--set-mixer-souces mixer_id, dst_channel_id, src_channel_id
Allows the selection of the mixer inputs. Sets mixer input (dst) to src
--display-current-mixer-sources mixer_id
Displays the current inputs to a particular mixer
--display-available-mixer-sources mixer_id
Displays all the input channels available that can be fed into the inputs of a particular mixer
--set-aud-channel-map dst src
Sets a channel map value for the device audio output
--display-aud-channel-map
Show audio output channel map i.e. for each audio output of the device what the source is.
--display-aud-channel-map-sources
Show the available audio output channel map sources.
--set-daw-channel-map dst src
Sets a channel map value for the DAW output to the host
--display-daw-channel-map
Show audio output channel map i.e. for each DAW output to host, what the source is.
--display-daw-channel-map-sources
Show the DAW output channel map sources.

View File

@@ -0,0 +1 @@
VERSION := 1v0

View File

@@ -0,0 +1,61 @@
/**
* Module: host_usb_mixer_control
* Version: 1v0
* Build: d94b0511afe40ece896637f88c6379f9b6f9f603
* File: global.h
*
* The copyrights, all other intellectual and industrial
* property rights are retained by XMOS and/or its licensors.
* Terms and conditions covering the use of this code can
* be found in the Xmos End User License Agreement.
*
* Copyright XMOS Ltd 2010
*
* In the case where this code is a modification of existing code
* under a separate license, the separate license terms are shown
* below. The modifications to the code are still covered by the
* copyright notice above.
*
**/
/************************************************************************
*
* Module: global.h
* Description:
* APP global includes, constants, declarations, etc.
*
* Author(s):
* Udo Eberhardt
*
* Companies:
* Thesycon GmbH, Germany http://www.thesycon.de
*
************************************************************************/
#ifndef __global_h__
#define __global_h__
// define the Windows versions supported by the application
#define _WIN32_WINNT 0x0500 //Windows 2000 or later
//#define _WIN32_WINNT 0x0501 //Windows XP or later
//#define _WIN32_WINNT 0x0600 //Windows Vista or later
// exclude rarely-used stuff from Windows headers
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
// version defs
//#include "version.h"
// TUSBAUDIO driver API
#include "tusbaudioapi.h"
#endif // __global_h__
/*************************** EOF **************************************/

View File

@@ -0,0 +1,925 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "usb_mixer.h"
#include "global.h"
//########## Thesycon .dll ##########
// libwn.h pulls in windows.h
#include "libwn.h"
// TUSBAUDIO driver API
#include "tusbaudioapi.h"
#include "TUsbAudioApiDll.h"
//driver interface
TUsbAudioApiDll gDrvApi;
//###################################
/* Note: this is all quite generic and could be simplified ALOT for a specific design */
// TODO we dont really need to store mixer input strings
// Currently res, max, min dont get populated
#define XMOS_VID 0x20b1
#define XMOS_L1_AUDIO2_PID 0x0002
#define XMOS_L1_AUDIO1_PID 0x0003
#define XMOS_L2_AUDIO2_PID 0x0004
#define USB_REQUEST_TO_DEV 0x21 /* D7 Data direction: 0 (Host to device)
* D6:5 Type: 01 (Class)
* D4:0 Receipient: 1 (Interface) */
#define USB_REQUEST_FROM_DEV 0xa1
#define USB_CS_INTERFACE 0x24
#define USB_INPUT_TERM_TYPE 0x02
#define USB_MIXER_UNIT_TYPE 0x04
#define USB_FEATURE_UNIT_TYPE 0x06
#define INPUT_TERMINAL USB_INPUT_TERM_TYPE
#define EXTENSION_UNIT 0x9
#define CS_INTERFACE USB_CS_INTERFACE
#define FEATURE_UNIT 0x06
#define CS_XU_SEL 0x6
#define MU_MIXER_CONTROL 0x1
// Output from PC
#define USB_STREAMING 0x01
// Input to device
//#define MICROPHONE 0x02
#define ID_XU_OUT 51
#define ID_XU_IN 52
#define OFFSET_BLENGTH 0
#define OFFSET_BDESCRIPTORTYPE 1
#define OFFSET_BDESCRIPTORSUBTYPE 2
#define OFFSET_BUNITID 3
#define OFFSET_FU_BSOURCEID 4
#define OFFSET_XU_BNRINPINS 6
#define OFFSET_XU_BSOURCEID 7
#define OFFSET_IT_WTERMINALTYPE 5
#define OFFSET_IT_BNRCHANNELS 8
#define OFFSET_IT_ICHANNELNAMES 13
typedef struct
{
double min;
double max;
double res;
double weight;
} mixer_node;
typedef struct
{
unsigned int id;
unsigned int num_inputs;
char input_names[USB_MIXER_INPUTS][USB_MIXER_MAX_NAME_LEN]; /* Current mixer input names-
* we dont really need to store these */
int input_connections[USB_MIXER_INPUTS];
unsigned int num_outputs;
char output_names[USB_MIXER_INPUTS][USB_MIXER_MAX_NAME_LEN];
unsigned int num_inPins;
mixer_node nodes[USB_MIXER_INPUTS * USB_MIXER_OUTPUTS];
} usb_mixer_device;
typedef struct {
int cur;
int default_value;
char name[USB_MIXER_MAX_NAME_LEN];
enum usb_chan_type ctype;
} channel_map_node;
typedef struct {
int numInputs;
int numOutputs;
channel_map_node map[USB_MAX_CHANNEL_MAP_SIZE];
} channel_mapp;
typedef struct
{
unsigned int id;
unsigned int numInputs;
char inputStrings[USB_MIXER_INPUTS*4][USB_MIXER_MAX_NAME_LEN]; /* Complete list of all possible inputs */
unsigned int numOutputs;
unsigned int state[USB_MIXER_INPUTS];
} t_usb_mixSel;
typedef struct {
unsigned int device_open;
unsigned int num_usb_mixers;
usb_mixer_device usb_mixer[USB_MIXERS];
t_usb_mixSel usb_mixSel[USB_MIXERS];
channel_mapp audChannelMap;
channel_mapp usbChannelMap;
} usb_mixer_handle;
static usb_mixer_handle *usb_mixers = NULL;
static TUsbAudioHandle h;
/* Issue a generic control/class GET request to a specific unit in the Audio Interface */
int usb_audio_class_get(unsigned char bRequest, unsigned char cs, unsigned char cn, unsigned short unitID, unsigned short wLength, unsigned char *data)
{
return TUSBAUDIO_AudioControlRequestGet(h,
unitID,
bRequest,
cs,
cn,
data,
wLength,
NULL,
1000);
}
/* Issue a generic control/class SET request to a specific unit in the Audio Interface */
int usb_audio_class_set(unsigned char bRequest, unsigned char cs, unsigned char cn, unsigned short unitID, unsigned short wLength, unsigned char *data)
{
return TUSBAUDIO_AudioControlRequestSet(h,
unitID,
bRequest,
cs,
cn,
data,
wLength,
NULL,
1000);
}
/* Note, this never get cached in an object since it can change on the device side */
int usb_mixer_mem_get(unsigned int mixer, unsigned offset, unsigned char *data)
{
return TUSBAUDIO_AudioControlRequestGet(h,
usb_mixers->usb_mixer[mixer].id,
MEM,
0, // cs
offset, //was cn
&data,
64,
NULL,
1000);
}
static const unsigned char *find_input_term_unit_by_id(const unsigned char *data, int length, int id)
{
const unsigned char *interface_data = data;
while (length)
{
const unsigned char *interface_len = interface_data;
int sub_type = *(interface_len + 2);
if (sub_type == USB_INPUT_TERM_TYPE)
{
int unit_id = *(interface_len + 3);
if (unit_id == id)
{
return interface_len;
}
}
interface_data+=*interface_len;
length -= *interface_len;
}
return NULL;
}
static const unsigned char *find_connected_feature_unit_by_id(const unsigned char *data, int length, int id) {
const unsigned char *interface_data = data;
while (length)
{
const unsigned char *interface_len = interface_data;
int sub_type = *(interface_len + 2);
if (sub_type == USB_FEATURE_UNIT_TYPE) {
//int unit_id = *(interface_len + 3);
int src_unit_id = *(interface_len + 4);
if (src_unit_id == id) {
return interface_len;
}
}
interface_data+=*interface_len;
length -= *interface_len;
}
return NULL;
}
static const unsigned char *findUnit(const unsigned char *descs, int length, int id)
{
const unsigned char *interface_data = descs;
while (length)
{
const unsigned char *interface_len = interface_data;
int bDescriptorType = *(interface_len + 1);
if (bDescriptorType == CS_INTERFACE)
{
int unit_id = *(interface_len + 3);
if (unit_id == id)
{
return interface_len;
}
}
interface_data+=*interface_len;
length -= *interface_len;
}
return NULL;
}
static int get_num_mixer_units(const unsigned char *data, int length) {
const unsigned char *interface_data = data;
int interface_len = length;
int num_mixer_units_found = 0;
while (interface_len) {
const unsigned char *interfaces = interface_data;
int interface_type = *(interfaces + 1);
int unit_type = *(interfaces + 2);
if (interface_type == USB_CS_INTERFACE && unit_type == USB_MIXER_UNIT_TYPE) {
num_mixer_units_found++;
}
interface_data+=*interfaces;
interface_len -= *interfaces;
}
return num_mixer_units_found;
}
static double dev_get_mixer_value(unsigned int mixer, unsigned int nodeId)
{
// MU_MIXER_CONTROL 0x01
short data;
usb_audio_class_get(CUR, 0x01<<8, nodeId, usb_mixers->usb_mixer[mixer].id, 2,(unsigned char *) &data);
return ((double) data / 256);
}
/* Populates min, max, res */
static unsigned short dev_get_mixer_range(unsigned int mixer, unsigned int channel,
double *min, double *max, double *res)
{
short data[64];
short min2, max2, res2;
usb_audio_class_get(RANGE, MU_MIXER_CONTROL, channel, usb_mixers->usb_mixer[mixer].id, 8, (unsigned char *) data);
min2 = data[1];
max2 = data[2];
res2 = data[3];
//printf("%f, %f, %f\n", (double)min2/256, (double)max2/256, (double) res2/256);
*min = (double)min2/256;
*max = (double)max2/256;
*res = (double)res2/256;
return 0;
}
int dev_get_channel_map(int channel, int unitId)
{
short data;
usb_audio_class_get(CUR, 0, channel, unitId, 2,(unsigned char *) &data);
return data;
}
static int dev_set_channel_map(int channel, int val, int unitId)
{
short value = val;
usb_audio_class_set(CUR, 0, channel, unitId, 1, (unsigned char *)&value);
return 0;
}
static int mixer_update_all_nodes(unsigned int mixer_index)
{
int i = 0;
int j = 0;
double min, max, res;
for (i = 0; i < usb_mixers->usb_mixer[mixer_index].num_inputs; i++)
{
for (j = 0; j < usb_mixers->usb_mixer[mixer_index].num_outputs; j++)
{
dev_get_mixer_range(mixer_index, (i*usb_mixers->usb_mixer[mixer_index].num_outputs)+j, &min, &max, &res);
usb_mixers->usb_mixer[mixer_index].nodes[(i*usb_mixers->usb_mixer[mixer_index].num_outputs)+j].min = min;
usb_mixers->usb_mixer[mixer_index].nodes[(i*usb_mixers->usb_mixer[mixer_index].num_outputs)+j].max = max;
usb_mixers->usb_mixer[mixer_index].nodes[(i*usb_mixers->usb_mixer[mixer_index].num_outputs)+j].res = res;
//printf("%f, %f, %f\n", min, max, res);
usb_mixers->usb_mixer[mixer_index].nodes[(i*usb_mixers->usb_mixer[mixer_index].num_outputs)+j].weight =
dev_get_mixer_value(mixer_index, (i*usb_mixers->usb_mixer[mixer_index].num_outputs)+j);
}
}
return 0;
}
/* Start at unit %id, find it in descs, keep recursively parsing up path(s) until get to Input Term and add strings */
int addStrings(const unsigned char *data, int length, int mixer_index, int id, int chanCount)
{
const unsigned char *currentUnitPtr = NULL;
/* Find this unit in the descs */
currentUnitPtr = findUnit(data, length, id);
TUsbAudioStatus st;
if(currentUnitPtr != NULL)
{
/* Check if unit is a Input term */
if(*(currentUnitPtr+OFFSET_BDESCRIPTORSUBTYPE) == INPUT_TERMINAL)
{
/* Get channel names */
#ifdef DEBUG
printf("Input terminal found on path (ID: %d): %d channels, total: %d\n",*(currentUnitPtr+OFFSET_BUNITID),
*(currentUnitPtr+OFFSET_IT_BNRCHANNELS), chanCount);
#endif
int iChannelNames = *(currentUnitPtr+OFFSET_IT_ICHANNELNAMES);
int wTerminalType = *(currentUnitPtr+OFFSET_IT_WTERMINALTYPE);
#ifdef DEBUG
printf("iChannelNames: %d wTerminalType: %d\n", iChannelNames, wTerminalType);
printf("Channels found:\n");
#endif
for(int i = 0; i < *(currentUnitPtr+OFFSET_IT_BNRCHANNELS); i++)
{
WCHAR mixer_input_name[USB_MIXER_MAX_NAME_LEN];
char mixer_input_name_copy[USB_MIXER_MAX_NAME_LEN];
if (wTerminalType == 1)
{
strcpy(usb_mixers->usb_mixSel[mixer_index].inputStrings[chanCount], "DAW - ");
//usb_mixers->channel_maps[usb_mixers->num_channel_maps].ctype = (enum usb_chan_type) USB_CHAN_OUT;
usb_mixers->audChannelMap.numOutputs = usb_mixers->audChannelMap.numOutputs +1;
usb_mixers->audChannelMap.map[usb_mixers->audChannelMap.numInputs].ctype = (enum usb_chan_type) USB_CHAN_OUT;
usb_mixers->usbChannelMap.map[usb_mixers->usbChannelMap.numInputs].ctype = (enum usb_chan_type) USB_CHAN_OUT;
}
else
{
strcpy(usb_mixers->usb_mixSel[mixer_index].inputStrings[chanCount], "AUD - ");
//usb_mixers->channel_maps[usb_mixers->num_channel_maps].ctype = (enum usb_chan_type) USB_CHAN_IN;
usb_mixers->audChannelMap.map[usb_mixers->audChannelMap.numInputs].ctype = (enum usb_chan_type) USB_CHAN_IN;
usb_mixers->usbChannelMap.map[usb_mixers->usbChannelMap.numInputs].ctype = (enum usb_chan_type) USB_CHAN_IN;
usb_mixers->usbChannelMap.numOutputs = usb_mixers->usbChannelMap.numOutputs +1;
}
/* Get relevant string descriptor */
//libusb_get_string_descriptor_ascii(devh, iChannelNames+i, mixer_input_name, USB_MIXER_MAX_NAME_LEN - strlen(usb_mixers->usb_mixSel[mixer_index].inputStrings[chanCount]));
st = TUSBAUDIO_GetUsbStringDescriptorString(h, iChannelNames+i, 0, mixer_input_name, USB_MIXER_MAX_NAME_LEN - strlen(usb_mixers->usb_mixSel[mixer_index].inputStrings[chanCount]));
if ( TSTATUS_SUCCESS != st ) {
return USB_MIXER_FAILURE;
}
wcstombs(mixer_input_name_copy, mixer_input_name, USB_MIXER_MAX_NAME_LEN);
strcat(usb_mixers->usb_mixSel[mixer_index].inputStrings[chanCount], (char *)mixer_input_name_copy);
/* Add to channel mappers also */
//strcat(usb_mixers->channel_maps[usb_mixers->num_channel_maps].name, (char *)mixer_input_name_copy);
strcat(usb_mixers->audChannelMap.map[usb_mixers->audChannelMap.numInputs ].name, (char *)mixer_input_name_copy);
strcat(usb_mixers->usbChannelMap.map[usb_mixers->audChannelMap.numInputs].name, (char *)mixer_input_name_copy);
usb_mixers->audChannelMap.numInputs = usb_mixers->audChannelMap.numInputs +1;
usb_mixers->usbChannelMap.numInputs = usb_mixers->usbChannelMap.numInputs +1;
//usb_mixers->num_channel_maps = usb_mixers->num_channel_maps+1;
chanCount++;
}
#ifdef DEBUG
int meh = chanCount - *(currentUnitPtr+OFFSET_IT_BNRCHANNELS);
for(int i = 0; i < *(currentUnitPtr+OFFSET_IT_BNRCHANNELS); i++)
{
printf("%d: %s\n", i,usb_mixers->usb_mixSel[mixer_index].inputStrings[meh+i]);
}
printf("\n\n");
#endif
}
else
{
/* Unit not a input terminal, keep going... */
if(*(currentUnitPtr+OFFSET_BDESCRIPTORSUBTYPE) == FEATURE_UNIT)
{
chanCount = addStrings(data, length, mixer_index, *(currentUnitPtr+OFFSET_FU_BSOURCEID), chanCount);
}
else if(*(currentUnitPtr+OFFSET_BDESCRIPTORSUBTYPE) == EXTENSION_UNIT)
{
/* Multiple inputs for Extention units */
for (int i = 0; i < *(currentUnitPtr+OFFSET_XU_BNRINPINS); i++)
{
chanCount = addStrings(data, length, mixer_index, *(currentUnitPtr+OFFSET_XU_BSOURCEID+i), chanCount);
}
}
else
{
fprintf(stderr,"ERROR: Currently don't support this unit: %d\n",
*(currentUnitPtr+OFFSET_BDESCRIPTORSUBTYPE));
exit(1);
}
}
}
else
{
fprintf(stderr,"ERROR: Couldn't find unit %d in descs\n", id );
exit(1);
}
return chanCount;
}
/* Returns the source of an mix sel output */
static unsigned char get_mixsel_value(unsigned int mixer, unsigned int channel)
{
unsigned char data[64];
usb_audio_class_get(CUR, CS_XU_SEL, channel, usb_mixers->usb_mixSel[mixer].id, 1, (unsigned char *)data);
return data[0];
}
static int get_mixer_info(const unsigned char *data, int length, unsigned int mixer_index)
{
const unsigned char *interface_data = data;
int interface_len = length;
int num_mixer_units_found = 0;
//const unsigned char *current_input_term_unit_ptr = NULL;
//int current_input_term_unit_index = 0;
//const unsigned char *current_feature_unit_ptr = NULL;
int devChanInputCount = 0;
while (interface_len)
{
const unsigned char *interfaces = interface_data;
int interface_type = *(interfaces + 1); /* bDescriptorType */
int unit_type = *(interfaces + 2); /* bDescriptorSubType */
/* Look for a mixer unit */
if (interface_type == USB_CS_INTERFACE && unit_type == USB_MIXER_UNIT_TYPE)
{
int unit_id = *(interfaces + 3); /* bUnitId */
int bNrInPins = *(interfaces + 4);
int num_in = *(interfaces + 4); /* bNrInPins - NOTE This is pins NOT channels!! */
/* Total number of inputs is the sum of the channel counts in the input
* clusters. We need to inspect the sources to gain channel counts */
int chansIn = 0;
#ifdef DEBUG
printf("Found Mixer Unit %d with %d inputs\n", unit_id, bNrInPins);
printf("Inspecting mixer inputs... \n\n");
#endif
/* For each input pin need to find out inspect its output cluster */
for (int i = 1; i <= bNrInPins; i++)
{
int sourceId = *(interfaces+4+i);
#ifdef DEBUG
printf("baSourceID(%d): %d\n", i, sourceId);
#endif
/* Find the unit in the config desc */
int descsLength = length;
const unsigned char *descsData = data;
int found = 0;
int bDescriptorSubType;
int bDescriptorType;
int bUnitId;
while(descsLength)
{
int currentLength = *descsData;
bDescriptorSubType = *(descsData + 2);
bDescriptorType = *(descsData + 1);
if(bDescriptorType == USB_CS_INTERFACE)
{
if(bDescriptorSubType == USB_FEATURE_UNIT_TYPE ||
bDescriptorSubType == USB_INPUT_TERM_TYPE ||
bDescriptorSubType == EXTENSION_UNIT )
{
bUnitId = *(descsData+3);
if(bUnitId == sourceId)
{
found = 1;
break;
}
}
}
descsData+=currentLength;
descsLength -= currentLength;
}
if(found)
{
int bNrChannelsOffset = 0;
int bNrChannels;
/* We found the unit in the descs. Now inspect channel cluster */
#ifdef DEBUG
printf("Found unit %d, type %d\n", bUnitId, bDescriptorSubType);
#endif
/* We are looking for bNrChannels in the descs, this is in a different location in desc depending
* on unit type */
switch(bDescriptorSubType)
{
case USB_INPUT_TERM_TYPE:
bNrChannelsOffset = 8;
bNrChannels = *(descsData+bNrChannelsOffset);
break;
case EXTENSION_UNIT:
bNrChannelsOffset = 7 + *(descsData+6);
bNrChannels = *(descsData+bNrChannelsOffset);
break;
default:
printf("ERR\n");
exit(1);
break;
}
#ifdef DEBUG
printf("Output chan count: %d\n\n", bNrChannels);
#endif
chansIn += bNrChannels;
}
else
{
fprintf(stderr,"ERROR: Mixer input connected to something we dont understand...\n");
exit(1);
}
}
/* get number of output channels straight from mixer unit descriptor: bNrChannels */
int num_out = *(interfaces + 5 + num_in);
#ifdef DEBUG
printf("Mixer Unit parse complete: bUnitId: %d, Total Input Chans: %d, Output Chans: %d\n\n", unit_id, chansIn, num_out);
#endif
usb_mixers->usb_mixer[mixer_index].id = unit_id;
usb_mixers->usb_mixer[mixer_index].num_inputs = chansIn;
usb_mixers->usb_mixer[mixer_index].num_inPins = bNrInPins;
usb_mixers->usb_mixer[mixer_index].num_outputs = num_out;
/* Go through all input pins */
const unsigned char *in_unit_start_ptr = interfaces + 5;
// const unsigned char *currentUnitPtr = NULL;
// int current_input_term_unit_id = 0;
/* We expect this to be a single input from an XU, but we'll keep it slightly generic here */
for (int num = 0; num < usb_mixers->usb_mixer[mixer_index].num_inPins; num++)
{
/* Save source ID */
usb_mixers->usb_mixer[mixer_index].input_connections[num] = *(in_unit_start_ptr+num);
#ifdef DEBUG
printf("Inspecting for Input Terms from mixer unit input pin %d (id: %d)\n",
num,usb_mixers->usb_mixer[mixer_index].input_connections[num]);
#endif
devChanInputCount = addStrings(data, length, mixer_index,
usb_mixers->usb_mixer[mixer_index].input_connections[num], devChanInputCount);
}
/* The the first input pin at the mix select for the moment.
* probably should be checking if its an XU here */
usb_mixers->usb_mixSel[mixer_index].id = usb_mixers->usb_mixer[mixer_index].input_connections[0];
usb_mixers->usb_mixSel[mixer_index].numInputs = devChanInputCount;
usb_mixers->usb_mixSel[mixer_index].numOutputs = chansIn;
/* Set up mixer output names */
WCHAR mixer_output_name[USB_MIXER_MAX_NAME_LEN];
char mixer_output_name_copy[USB_MIXER_MAX_NAME_LEN];
unsigned int iChannelNames = *(interfaces + 10 + bNrInPins);
for (int i = 0; i < usb_mixers->usb_mixer[mixer_index].num_outputs; i++)
{
/* Get relevant string descriptor */
strcpy(usb_mixers->usb_mixer[mixer_index].output_names[i], "MIX - ");
// libusb_get_string_descriptor_ascii(devh, iChannelNames+i, mixer_output_name, USB_MIXER_MAX_NAME_LEN - strlen(usb_mixers->usb_mixSel[mixer_index].inputStrings[i]));
TUsbAudioStatus st = TUSBAUDIO_GetUsbStringDescriptorString(h, iChannelNames+i, 0, mixer_output_name, USB_MIXER_MAX_NAME_LEN - strlen(usb_mixers->usb_mixSel[mixer_index].inputStrings[i]));
if ( TSTATUS_SUCCESS != st ) {
return USB_MIXER_FAILURE;
}
wcstombs(mixer_output_name_copy, mixer_output_name, USB_MIXER_MAX_NAME_LEN);
// strcat(usb_mixers->usb_mixer[mixer_index].output_names[i], (char *)mixer_output_name);
strcat(usb_mixers->usb_mixer[mixer_index].output_names[i], mixer_output_name_copy);
}
}
interface_data+=*interfaces;
interface_len -= *interfaces;
}
return num_mixer_units_found;
}
static int find_xmos_device(unsigned int id) {
//libusb_device *dev;
//libusb_device **devs;
int i = 0;
int found = 0;
TUsbAudioStatus st;
unsigned int devcnt = TUSBAUDIO_GetDeviceCount();
if ( 0 == devcnt ) {
return USB_MIXER_FAILURE;
}
st = TUSBAUDIO_OpenDeviceByIndex(0,&h);
if ( TSTATUS_SUCCESS != st ) {
h = 0;
// skip
} else {
unsigned int numBytes = 0;
unsigned char descBuffer[64*1024];
st = TUSBAUDIO_GetUsbConfigDescriptor(h, descBuffer, 64*1024, &numBytes);
if ( TSTATUS_SUCCESS != st ) {
return USB_MIXER_FAILURE;
}
usb_mixers->num_usb_mixers = get_num_mixer_units(descBuffer, numBytes);
get_mixer_info(descBuffer, numBytes, 0);
/* Init channel maps from device */
for(int i = 0; i < usb_mixers->audChannelMap.numOutputs; i++)
{
usb_mixers->audChannelMap.map[i].cur = dev_get_channel_map(i, ID_XU_OUT);
}
for(int i = 0; i < usb_mixers->usbChannelMap.numOutputs; i++)
{
usb_mixers->usbChannelMap.map[i].cur = dev_get_channel_map(i, ID_XU_IN);
//printf("%d\n", usb_mixers->usbChannelMap.map[i].cur);
}
/* Now add the mix outputs */
for(int i = 0; i < usb_mixers->num_usb_mixers; i++)
{
for(int j = 0; j < usb_mixers->usb_mixer[i].num_outputs;j++)
{
//strcpy(usb_mixers->channel_maps[usb_mixers->num_channel_maps].name, usb_mixers->usb_mixer[i].output_names[j]);
//usb_mixers->channel_maps[usb_mixers->num_channel_maps].ctype = (enum usb_chan_type) USB_CHAN_MIXER;
//usb_mixers->num_channel_maps = usb_mixers->num_channel_maps+1;
usb_mixers->audChannelMap.map[usb_mixers->audChannelMap.numInputs].ctype = (enum usb_chan_type) USB_CHAN_MIXER;
strcpy(usb_mixers->audChannelMap.map[usb_mixers->audChannelMap.numInputs].name, usb_mixers->usb_mixer[i].output_names[j]);
usb_mixers->audChannelMap.numInputs = usb_mixers->audChannelMap.numInputs +1;
usb_mixers->usbChannelMap.map[usb_mixers->usbChannelMap.numInputs].ctype = (enum usb_chan_type) USB_CHAN_MIXER;
strcpy(usb_mixers->usbChannelMap.map[usb_mixers->usbChannelMap.numInputs].name, usb_mixers->usb_mixer[i].output_names[j]);
usb_mixers->usbChannelMap.numInputs = usb_mixers->usbChannelMap.numInputs +1;
}
}
if(h)
{
/* Populate mixer input strings */
for(int i = 0; i < usb_mixers->num_usb_mixers; i++)
{
mixer_update_all_nodes(i);
/* Get current each mixer input and populate channel number state and strings from device */
for (int j = 0; j < usb_mixers->usb_mixSel[i].numOutputs; j++)
{
/* Get value from device */
int inputChan = get_mixsel_value(i, j);
usb_mixers->usb_mixSel[i].state[j] = inputChan;
/* Look up channel string */
strcpy(usb_mixers->usb_mixer[i].input_names[j], usb_mixers->usb_mixSel[i].inputStrings[inputChan]);
}
}
}
}
return h ? 0 : -1;
}
// End of libusb interface functions
int usb_mixer_connect() {
int result = 0;
gDrvApi.LoadByGUID("{E5A2658B-817D-4A02-A1DE-B628A93DDF5D}", false);
// Allocate internal storage
usb_mixers = (usb_mixer_handle *)malloc(sizeof(usb_mixer_handle));
memset(usb_mixers, 0, sizeof(usb_mixer_handle));
// enumerate devices
TUsbAudioStatus st = TUSBAUDIO_EnumerateDevices();
if ( TSTATUS_SUCCESS != st ) {
return USB_MIXER_FAILURE;
}
result = find_xmos_device(0);
if (result < 0) {
return USB_MIXER_FAILURE;
}
return USB_MIXER_SUCCESS;
}
int usb_mixer_disconnect() {
if ( 0 != h ) {
TUSBAUDIO_CloseDevice(h);
}
free(usb_mixers);
return USB_MIXER_SUCCESS;
}
/* Getter for num_usb_mixers */
int usb_mixer_get_num_mixers()
{
return usb_mixers->num_usb_mixers;
}
int usb_mixer_get_num_outputs(unsigned int mixer)
{
return usb_mixers->usb_mixer[mixer].num_outputs;
}
int usb_mixer_get_num_inputs(unsigned int mixer)
{
return usb_mixers->usb_mixer[mixer].num_inputs;
}
int usb_mixer_get_layout(unsigned int mixer, unsigned int *inputs, unsigned int *outputs) {
*inputs = usb_mixers->usb_mixer[mixer].num_inputs;
*outputs = usb_mixers->usb_mixer[mixer].num_outputs;
return 0;
}
/* MixSel getters and setters */
char *usb_mixsel_get_input_string(unsigned int mixer, unsigned int input)
{
return usb_mixers->usb_mixSel[mixer].inputStrings[input];
}
int usb_mixsel_get_input_count(unsigned int mixer)
{
return usb_mixers->usb_mixSel[mixer].numInputs;
}
int usb_mixsel_get_output_count(unsigned int mixer)
{
return usb_mixers->usb_mixSel[mixer].numOutputs;
}
char *usb_mixer_get_input_name(unsigned int mixer, unsigned int input) {
return usb_mixers->usb_mixer[mixer].input_names[input];
}
char *usb_mixer_get_output_name(unsigned int mixer, unsigned int output) {
return usb_mixers->usb_mixer[mixer].output_names[output];
}
unsigned char usb_mixsel_get_state(unsigned int mixer, unsigned int channel)
{
return usb_mixers->usb_mixSel[mixer].state[channel];
}
void usb_mixsel_set_state(unsigned int mixer, unsigned int dst, unsigned int src)
{
// write to device
usb_audio_class_set(CUR, CS_XU_SEL, dst, usb_mixers->usb_mixSel[mixer].id, 1, (unsigned char *)&src);
// update object state
usb_mixers->usb_mixSel[mixer].state[dst] = src;
// update local object strings
// TODO we don't really need to store strings since we can look them up...*/
strcpy(usb_mixers->usb_mixer[mixer].input_names[dst], usb_mixers->usb_mixSel[mixer].inputStrings[src]);
}
double usb_mixer_get_value(unsigned int mixer, unsigned int nodeId)
{
return (double)usb_mixers->usb_mixer[mixer].nodes[nodeId].weight;
}
double usb_mixer_get_res(unsigned int mixer, unsigned int nodeId)
{
return (double)usb_mixers->usb_mixer[mixer].nodes[nodeId].res;
}
double usb_mixer_get_min(unsigned int mixer, unsigned int nodeId)
{
return (double)usb_mixers->usb_mixer[mixer].nodes[nodeId].min;
}
double usb_mixer_get_max(unsigned int mixer, unsigned int nodeId)
{
return (double)usb_mixers->usb_mixer[mixer].nodes[nodeId].max;
}
int usb_mixer_set_value(unsigned int mixer, unsigned int nodeId, double val)
{
/* check if update required */
if(usb_mixers->usb_mixer[mixer].nodes[nodeId].weight != val)
{
/* update local object */
usb_mixers->usb_mixer[mixer].nodes[nodeId].weight= val;
/* write to device */
short value = (short) (val * 256);
usb_audio_class_set(CUR, 1, 1<<8 | nodeId & 0xff, usb_mixers->usb_mixer[mixer].id, 2, (unsigned char *)&value);
}
return 0;
}
int usb_mixer_get_range(unsigned int mixer, unsigned int mixer_unit, int *min, int *max, int *res)
{
// range 0x02
return 0;
}
int usb_get_usb_channel_map(int channel)
{
return usb_mixers->usbChannelMap.map[channel].cur;
}
int usb_get_aud_channel_map(int channel)
{
return usb_mixers->audChannelMap.map[channel].cur;
}
int usb_set_aud_channel_map(int channel, int val)
{
/* Check if update required */
if(usb_mixers->audChannelMap.map[channel].cur != val)
{
/* Update local object */
usb_mixers->audChannelMap.map[channel].cur = val;
/* Write setting to dev */
dev_set_channel_map(channel, val, ID_XU_OUT);
}
return 0;
}
int usb_set_usb_channel_map(int channel, int val)
{
/* Check if update required */
if(usb_mixers->usbChannelMap.map[channel].cur != val)
{
/* Update local object */
usb_mixers->usbChannelMap.map[channel].cur = val;
/* Write setting to dev */
dev_set_channel_map(channel, val, ID_XU_IN);
}
return 0;
}
enum usb_chan_type usb_get_aud_channel_map_type(int channel)
{
return usb_mixers->audChannelMap.map[channel].ctype;
}
enum usb_chan_type usb_get_usb_channel_map_type(int channel)
{
return usb_mixers->usbChannelMap.map[channel].ctype;
}
char *usb_get_aud_channel_map_name(int channel)
{
return usb_mixers->audChannelMap.map[channel].name;
}
char *usb_get_usb_channel_map_name(int channel)
{
return usb_mixers->usbChannelMap.map[channel].name;
}
int usb_get_aud_channel_map_num_outputs()
{
return usb_mixers->audChannelMap.numOutputs;
}
int usb_get_usb_channel_map_num_outputs()
{
return usb_mixers->usbChannelMap.numOutputs;
}
int usb_get_aud_channel_map_num_inputs()
{
return usb_mixers->audChannelMap.numInputs;
}
int usb_get_usb_channel_map_num_inputs()
{
return usb_mixers->usbChannelMap.numInputs;
}

View File

@@ -0,0 +1,719 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "usb_mixer.h"
#define MIXER_UNIT_DISPLAY_VALUE 2
#define MIXER_UNIT_DISPLAY_MIN 3
#define MIXER_UNIT_DISPLAY_MAX 4
#define MIXER_UNIT_DISPLAY_RES 5
// TODO
// res, min, max
int mixer_init(void)
{
/* Open the connection to the USB mixer */
if (usb_mixer_connect() == USB_MIXER_FAILURE)
{
return USB_MIXER_FAILURE;
}
return USB_MIXER_SUCCESS;
}
int mixer_deinit(void) {
// Close the connection to the USB mixer
if (usb_mixer_disconnect() == USB_MIXER_FAILURE) {
return USB_MIXER_FAILURE;
}
return USB_MIXER_SUCCESS;
}
int mixer_display(unsigned int mixer_index, unsigned int type) {
int i = 0;
int j = 0;
int num_inputs = usb_mixer_get_num_inputs(mixer_index);
int num_outputs = usb_mixer_get_num_outputs(mixer_index);
printf("\n");
switch (type) {
case MIXER_UNIT_DISPLAY_VALUE:
//mixer_update_all_values(mixer_index);
printf(" Mixer Values (%d)\n", mixer_index);
printf(" ----------------\n\n");
break;
case MIXER_UNIT_DISPLAY_MIN:
printf(" Mixer Ranges Min (%d)\n", mixer_index);
printf(" --------------------\n\n");
break;
case MIXER_UNIT_DISPLAY_MAX:
printf(" Mixer Ranges Max (%d)\n", mixer_index);
printf(" --------------------\n\n");
break;
case MIXER_UNIT_DISPLAY_RES:
printf(" Mixer Ranges Res (%d)\n", mixer_index);
printf(" --------------------\n\n");
break;
default:
return USB_MIXER_FAILURE;
break;
}
printf(" \t\t\t");
printf("Mixer Outputs\n");
printf("\t\t ");
for (i = 0; i < num_outputs; i++) {
printf(" %d", i+1);
}
printf("\n");
for (i = 0; i < num_inputs; i++) {
printf(" %-20s", usb_mixer_get_input_name(mixer_index,i));
for (j = 0; j < num_outputs; j++) {
switch (type) {
case MIXER_UNIT_DISPLAY_VALUE:
{
double mixNodeVal = usb_mixer_get_value(mixer_index, (i*num_outputs)+j);
int nodeid = (i*num_outputs)+j;
if (mixNodeVal <= -127.996)// todo shoud be < min
{
printf("\t%3d:[ %s ]", nodeid,"-inf");
}
else
{
printf("\t%3d:[%08.03f]", nodeid, mixNodeVal);
}
}
break;
case MIXER_UNIT_DISPLAY_MIN:
{
int nodeid = (i*num_outputs)+j;
printf("\t%3d:[%08.03f]", nodeid, usb_mixer_get_min(mixer_index, (i*num_outputs)+j)) ;
}
break;
case MIXER_UNIT_DISPLAY_MAX:
{
int nodeid = (i*num_outputs)+j;
printf("\t%3d:[%08.03f]", nodeid, usb_mixer_get_max(mixer_index, (i*num_outputs)+j)) ;
}
break;
case MIXER_UNIT_DISPLAY_RES:
{
int nodeid = (i*num_outputs)+j;
printf("\t%3d:[%08.03f]", nodeid, usb_mixer_get_res(mixer_index, (i*num_outputs)+j)) ;
}
break;
default:
return USB_MIXER_FAILURE;
break;
}
}
printf("\n");
}
printf("\n");
return USB_MIXER_SUCCESS;
}
/* Displays basic mixer information */
int mixer_display_info(void)
{
unsigned int i = 0;
int num_mixers = usb_mixer_get_num_mixers();
printf("\n");
printf(" Mixer Info\n");
printf(" ----------\n\n");
printf(" Mixers : %d\n\n", num_mixers);
for (i = 0; i < num_mixers; i++)
{
int num_inputs = usb_mixer_get_num_inputs(i);
int num_outputs = usb_mixer_get_num_outputs(i);
printf(" Mixer %d\n", i);
printf(" -------\n");
printf(" Inputs : %d\n"
" Outputs : %d\n\n", num_inputs, num_outputs);
printf(" Mixer Output Labels:\n");
for(int j = 0; j < num_outputs; j++)
{
printf(" %d: %s\n", j,usb_mixer_get_output_name(i,j));
}
//printf("\n Selectable Inputs (%d): \n", usb_mixsel_get_input_count(i));
//for(int j = 0; j < usb_mixsel_get_input_count(i); j++)
//{
// printf(" %d: %s\n", j, usb_mixsel_get_input_string(i,j));
//}
}
printf("\n");
return USB_MIXER_SUCCESS;
}
void display_available_mixer_sources(int mixIndex)
{
printf("\n");
printf(" Available Mixer Sources (%d)\n", mixIndex);
printf(" -------------------------\n\n");
for(int j = 0; j < usb_mixsel_get_input_count(mixIndex); j++)
{
printf(" %d: %s\n", j, usb_mixsel_get_input_string(mixIndex,j));
}
}
/* Gets the current mixer inputs from the device an displays them */
void display_mixer_sources(int mixerIndex)
{
printf("\n");
printf(" Current Mixer Sources (%d)\n", mixerIndex);
printf(" -------------------------\n\n");
/* Note, mixSel output cound and mixer input chan count should be the same! */
printf(" Number of mixer sources: %d\n", usb_mixsel_get_output_count(mixerIndex));
/* Get the current channel number for every mixer input */
for(int i = 0; i < usb_mixsel_get_output_count(mixerIndex); i++)
{
int inputChan = (int)usb_mixsel_get_state(mixerIndex, i);
char *str = usb_mixer_get_input_name(mixerIndex,i);
printf(" Mixer input %d: Source chan id: %d (%s)\n", i, inputChan, str);
}
}
/* set mixer source */
void set_mixer_source(unsigned mixerIndex, unsigned dst, unsigned src)
{
usb_mixsel_set_state(mixerIndex, dst, src);
/* String lookup */
char *str = usb_mixer_get_input_name(mixerIndex, dst);
int state = usb_mixsel_get_state(mixerIndex, dst);
printf("\n Set mixer(%d) input %d to device input %d (%s)\n", mixerIndex, dst, state, str);
}
void display_aud_channel_map()
{
printf("\n");
printf(" Audio Output Channel Map\n");
printf(" ------------------------\n\n");
for (int i=0;i<usb_get_aud_channel_map_num_outputs();i++)
{
int x = usb_get_aud_channel_map(i);
printf("%d (DEVICE OUT - %s) source is ",i, usb_get_aud_channel_map_name(i));
switch (usb_get_aud_channel_map_type(x))
{
case USB_CHAN_OUT:
printf(" %d (DAW OUT - %s)\n",x,usb_get_aud_channel_map_name(x));
break;
case USB_CHAN_IN:
printf("%d (DEVICE IN - %s)\n",x,usb_get_aud_channel_map_name(x));
break;
case USB_CHAN_MIXER:
printf("%d (%s)\n",x,usb_get_aud_channel_map_name(x));
break;
}
}
}
void display_daw_channel_map()
{
printf("\n");
printf(" DAW Output To Host Channel Map\n");
printf(" ------------------------\n\n");
for (int i=0;i<usb_get_usb_channel_map_num_outputs();i++)
{
int x = usb_get_usb_channel_map(i);
printf("%d (DAW IN - %s) source is ",i, usb_get_usb_channel_map_name(i + usb_get_aud_channel_map_num_outputs()));
switch (usb_get_usb_channel_map_type(x))
{
case USB_CHAN_OUT:
printf(" %d (DAW OUT - %s)\n",x,usb_get_usb_channel_map_name(x));
break;
case USB_CHAN_IN:
printf("%d (DEVICE IN - %s)\n",x,usb_get_usb_channel_map_name(x));
break;
case USB_CHAN_MIXER:
printf("%d (%s)\n",x,usb_get_usb_channel_map_name(x));
break;
}
}
}
void display_aud_channel_map_sources(void)
{
printf("\n");
printf(" Audio Output Channel Map Source List\n");
printf(" ------------------------------------\n\n");
for (int i=0;i<usb_get_aud_channel_map_num_inputs();i++) {
switch (usb_get_aud_channel_map_type(i))
{
case USB_CHAN_OUT:
printf("%d (DAW OUT - %s)\n",i,usb_get_aud_channel_map_name(i));
break;
case USB_CHAN_IN:
printf("%d (DEVICE IN - %s)\n",i,usb_get_aud_channel_map_name(i));
break;
case USB_CHAN_MIXER:
printf("%d (%s)\n",i,usb_get_aud_channel_map_name(i));
break;
}
}
}
void display_daw_channel_map_sources(void)
{
printf("\n");
printf(" DAW Output to Host Channel Map Source List\n");
printf(" ------------------------------------------\n\n");
for (int i=0;i<usb_get_usb_channel_map_num_inputs();i++) {
switch (usb_get_usb_channel_map_type(i))
{
case USB_CHAN_OUT:
printf("%d (DAW OUT - %s)\n",i,usb_get_usb_channel_map_name(i));
break;
case USB_CHAN_IN:
printf("%d (DEVICE IN - %s)\n",i,usb_get_usb_channel_map_name(i));
break;
case USB_CHAN_MIXER:
printf("%d (%s)\n",i,usb_get_usb_channel_map_name(i));
break;
}
}
}
int usb_audio_request_get(unsigned bRequest, unsigned cs, unsigned cn, unsigned unitId, unsigned char *data)
{
char reqStr[] = "Custom";
if(bRequest == CUR)
{
strcpy(reqStr, "CUR");
}
else if(bRequest == RANGE)
{
strcpy(reqStr, "RANGE");
}
else if(bRequest == MEM)
{
strcpy(reqStr, "MEM");
}
printf("Performing class GET request to Audio Interface:\n\
bRequest: 0x%02x (%s)\n\
wValue: 0x%04x (Control Sel: %d, Channel Number: %d)\n\
wIndex: 0x%04x (Interface: 0, Entity: %d)\n\
\n", bRequest, reqStr, (cs<<8)|cn, cs, cn, unitId<<8, unitId);
return usb_audio_class_get(bRequest, cs, cn, unitId, 64, data);
}
int usb_audio_request_set(unsigned bRequest, unsigned cs, unsigned cn, unsigned unitId,
unsigned char *data, int datalength)
{
char reqStr[] = "Custom";
if(bRequest == CUR)
{
strcpy(reqStr, "CUR");
}
else if(bRequest == RANGE)
{
strcpy(reqStr, "RANGE");
}
{
strcpy(reqStr, "MEM");
}
printf("Performing class SET request to Audio Interface:\n\
bRequest: 0x%02x (%s)\n\
wValue: 0x%04x (Control Sel: %d, Channel Number: %d)\n\
wIndex: 0x%04x (Interface: 0, Entity: %d)\n\
\n", bRequest, reqStr, (cs<<8)|cn, cs, cn, unitId<<8, unitId);
return usb_audio_class_set(bRequest, cs, cn, unitId, datalength, data);
}
int usb_audio_memreq_get(unsigned unitId, unsigned offset, unsigned char *data)
{
/* Mem requests dont have CS/CN, just an offset.. */
return usb_audio_request_get(MEM, (offset>>8), offset&0xff, unitId, data);
}
void print_levels(const char* levelTitle, unsigned char* levels, int levelBytes)
{
unsigned levelCount = levelBytes/2;
unsigned short* levelData = (unsigned short*) levels;
printf("\n %s Level Data\n"
" ----------------------\n\n"
"%d bytes (%d channels) returned:\n"
, levelTitle, levelBytes, levelCount);
for(int i = 0; i<levelCount; i++)
{
printf("%s %d: 0x%04x\n", levelTitle, i,levelData[i]);
}
}
void mixer_display_usage(void) {
fprintf(stderr, "Usage :\n");
fprintf(stderr,
" --display-info\n"
" --display-mixer-nodes mixer_id\n"
" --display-min mixer_id\n"
" --display-max mixer_id\n"
" --display-res mixer_id\n"
" --set-value mixer_id, mixer_node, value\n"
" --get-value mixer_id, mixer_node\n"
"\n"
" --set-mixer-source mixer_id dst channel_id, src_channel_id\n"
" --display-current-mixer-sources mixer_id\n"
" --display-available-mixer-sources mixer_id\n"
"\n"
" --set-aud-channel-map dst_channel_id, src_channel_id\n"
" --display-aud-channel-map \n"
" --display-aud-channel-map-sources\n"
" --set-daw-channel-map dst_channel_id, src_channel_id\n"
" --display-daw-channel-map \n"
" --display-daw-channel-map-sources\n"
"\n"
" --get-mixer-levels-input mixer_id\n"
" --get-mixer-levels-output mixer_id\n"
" --vendor-audio-request-get bRequest, ControlSelector, ChannelNumber, UnitId\n"
" --vendor-audio-request-set bRequest, ControlSelector, ChannelNumber, UnitId, Data[0], Data[1],...\n"
);
}
void usage_error()
{
fprintf(stderr, "ERROR :: incorrect number of arguments passed. See --help\n");
}
int main (int argc, char **argv) {
unsigned int mixer_index = 0;
unsigned int result = 0;
if (argc < 2) {
fprintf(stderr, "ERROR :: No options passed to mixer application\n");
mixer_display_usage();
return -1;
}
if (strcmp(argv[1], "--help") == 0) {
mixer_display_usage();
return 0;
}
if (mixer_init() != USB_MIXER_SUCCESS) {
fprintf(stderr, "ERROR :: Cannot connect\n");
return -1;
}
if (strcmp(argv[1], "--display-info") == 0)
{
mixer_display_info();
}
else if (strcmp(argv[1], "--display-mixer-nodes") == 0)
{
if (argv[2])
{
mixer_index = atoi(argv[2]);
} else {
fprintf(stderr, "ERROR :: No mixer index supplied\n");
return -1;
}
mixer_display(mixer_index, MIXER_UNIT_DISPLAY_VALUE);
} else if (strcmp(argv[1], "--display-mixer-nodes") == 0) {
if (argv[2]) {
mixer_index = atoi(argv[2]);
} else {
fprintf(stderr, "ERROR :: No mixer index supplied\n");
return -1;
}
mixer_display(mixer_index, MIXER_UNIT_DISPLAY_VALUE);
} else if (strcmp(argv[1], "--display-min") == 0) {
if (argv[2]) {
mixer_index = atoi(argv[2]);
} else {
fprintf(stderr, "ERROR :: No mixer index supplied\n");
return -1;
}
mixer_display(mixer_index, MIXER_UNIT_DISPLAY_MIN);
} else if (strcmp(argv[1], "--display-max") == 0) {
if (argv[2]) {
mixer_index = atoi(argv[2]);
} else {
fprintf(stderr, "ERROR :: No mixer index supplied\n");
return -1;
}
mixer_display(mixer_index, MIXER_UNIT_DISPLAY_MAX);
} else if (strcmp(argv[1], "--display-res") == 0) {
if (argv[2]) {
mixer_index = atoi(argv[2]);
} else {
fprintf(stderr, "ERROR :: No mixer index supplied\n");
return -1;
}
mixer_display(mixer_index, MIXER_UNIT_DISPLAY_RES);
}
else if (strcmp(argv[1], "--set-value") == 0) {
unsigned int mixer_unit = 0;
double value = 0;
if (argc < 5) {
fprintf(stderr, "ERROR :: incorrect number of arguments passed\n");
return -1;
}
mixer_index = atoi(argv[2]);
mixer_unit = atoi(argv[3]);
if (strcmp(argv[4],"-inf")==0)
value = -128;
else
value = atof(argv[4]);
usb_mixer_set_value(mixer_index, mixer_unit, value);
} else if (strcmp(argv[1], "--get-value") == 0) {
unsigned int mixer_unit = 0;
double result = 0;
if (argc < 4) {
fprintf(stderr, "ERROR :: incorrect number of arguments passed\n");
return -1;
}
mixer_index = atoi(argv[2]);
mixer_unit = atoi(argv[3]);
result = usb_mixer_get_value(mixer_index, mixer_unit);
if (result <= -127.996)
printf("%s\n", "-inf");
else
printf("%g\n",result);
}
else if (strcmp(argv[1], "--display-current-mixer-sources") == 0)
{
if(argc < 3)
{
usage_error();
return -1;
}
display_mixer_sources(atoi(argv[2]));
}
else if (strcmp(argv[1], "--display-available-mixer-sources") == 0)
{
if(argc < 3)
{
usage_error();
return -1;
}
display_available_mixer_sources(atoi(argv[2]));
}
else if(strcmp(argv[1], "--set-mixer-source") == 0)
{
if(argc < 5)
{
usage_error();
return -1;
}
set_mixer_source(atoi(argv[2]), atoi(argv[3]), atoi(argv[4]));
}
else if (strcmp(argv[1], "--display-aud-channel-map") == 0)
{
/* Display the channel mapping to the devices audio outputs */
display_aud_channel_map();
}
else if (strcmp(argv[1], "--display-aud-channel-map-sources") == 0)
{
display_aud_channel_map_sources();
}
else if (strcmp(argv[1], "--display-daw-channel-map") == 0)
{
/* Display the channel mapping to the devices DAW output to host */
display_daw_channel_map();
}
else if (strcmp(argv[1], "--display-daw-channel-map-sources") == 0)
{
display_daw_channel_map_sources();
}
else if (strcmp(argv[1], "--set-aud-channel-map") == 0)
{
unsigned int dst = 0;
unsigned int src = 0;
if (argc < 4) {
usage_error();
return -1;
}
dst = atoi(argv[2]);
src = atoi(argv[3]);
usb_set_aud_channel_map(dst, src);
}
else if (strcmp(argv[1], "--set-daw-channel-map") == 0)
{
unsigned int dst = 0;
unsigned int src = 0;
if (argc < 4) {
usage_error();
return -1;
}
dst = atoi(argv[2]);
src = atoi(argv[3]);
usb_set_usb_channel_map(dst, src);
}
else if(strcmp(argv[1], "--get-mixer-levels-input") == 0 ||
strcmp(argv[1],"--get-mixer-levels-output") == 0)
{
unsigned int dst = 0;
unsigned char levels[64];
int datalength = 0;
int offset = 0;
if (argc < 3) {
fprintf(stderr, "ERROR :: incorrect number of arguments passed\n");
return -1;
}
if(strcmp(argv[1],"--get-mixer-levels-output") == 0)
offset = 1;
for(int i = 0; i < 64; i++)
levels[i] = 0;
dst = atoi(argv[2]);
/* Mem request to mixer with offset of 0 gives input levels */
datalength = usb_mixer_mem_get(dst, offset, levels);
if(datalength < 0)
{
fprintf(stderr, "ERROR in control request: %d\n", datalength);
return -1;
}
if(offset)
print_levels("Mixer Output", levels, datalength);
else
print_levels("Mixer Input", levels, datalength);
}
else if(strcmp(argv[1], "--vendor-audio-request-get") == 0)
{
unsigned int bRequest = 0;
unsigned int cs = 0;
unsigned int cn = 0;
unsigned int unitId = 0;
int datalength = 0;
unsigned char data[64];
if(argc < 6)
{
fprintf(stderr, "ERROR :: incorrect number of arguments passed\n");
return -1;
}
for(int i = 0; i < 64; i++)
data[i] = 0;
bRequest = atoi(argv[2]);
cs = atoi(argv[3]);
cn = atoi(argv[4]);
unitId = atoi(argv[5]);
/* Do request */
datalength = usb_audio_request_get(bRequest, cs, cn, unitId, data);
/* Print result */
if(datalength < 0)
{
fprintf(stderr, "ERROR in control request: %d\n", datalength);
}
else
{
printf("Response (%d bytes):\n", datalength);
for(int i = 0; i < datalength; i++)
printf("0x%02x\n" ,data[i]);
}
}
else if(strcmp(argv[1], "--vendor-audio-request-set") == 0)
{
unsigned int bRequest = 0;
unsigned int cs = 0;
unsigned int cn = 0;
unsigned int unitId = 0;
unsigned char data[64];
for(int i=0; i<64; i++)
{
data[i] = 0;
}
if(argc < 7)
{
fprintf(stderr, "ERROR :: incorrect number of arguments passed - no data passed\n");
return -1;
}
bRequest = atoi(argv[2]);
cs = atoi(argv[3]);
cn = atoi(argv[4]);
unitId = atoi(argv[5]);
/* Get data */
for(int i=0; i < argc-6; i++)
{
data[i] = atoi(argv[i+6]);
}
result = usb_audio_request_set(bRequest, cs, cn, unitId, data, argc-6);
if(result < 0)
{
fprintf(stderr, "ERROR :: Error detected in Set request: %d\n", result);
return -1;
}
}
else
{
fprintf(stderr, "ERROR :: Invalid option passed to mixer application\n");
return -1;
}
mixer_deinit();
return result;
}

View File

@@ -0,0 +1 @@
Sample code for a host usb mixer app (not this is a host/pc program and cannot be built in the XDE)

View File

@@ -0,0 +1 @@
export DYLD_LIBRARY_PATH=$PWD/OSX:$DYLD_LIBRARY_PATH

View File

@@ -0,0 +1,124 @@
/**
* Module: host_usb_mixer_control
* Version: 1v0
* Build: c9de4042d132c4eddebaa352f91fb98ef883e272
* File: usb_mixer.h
*
* The copyrights, all other intellectual and industrial
* property rights are retained by XMOS and/or its licensors.
* Terms and conditions covering the use of this code can
* be found in the Xmos End User License Agreement.
*
* Copyright XMOS Ltd 2010
*
* In the case where this code is a modification of existing code
* under a separate license, the separate license terms are shown
* below. The modifications to the code are still covered by the
* copyright notice above.
*
**/
#define USB_MIXER_SUCCESS 0
#define USB_MIXER_FAILURE -1
#define USB_MIXERS 1
#define USB_MIXER_INPUTS 18
#define USB_MIXER_OUTPUTS 8
#define USB_MAX_CHANNEL_MAP_SIZE 40
#define USB_MIXER_MAX_NAME_LEN 64
enum usb_chan_type {
USB_CHAN_OUT=0,
USB_CHAN_IN=1,
USB_CHAN_MIXER=2
};
/* A.14 Audio Class-Specific Request Codes */
#define REQUEST_CODE_UNDEFINED 0x00
#define CUR (1)
#define RANGE (2)
#define MEM (3)
int usb_mixer_connect();
int usb_mixer_disconnect();
/* MIXER UNIT(s) INTERFACE */
/* Returns total number of mixers in device */
int usb_mixer_get_num_mixers();
/* Returns number of inputs and outputs for a selected mixer */
int usb_mixer_get_layout(unsigned int mixer, unsigned int *inputs, unsigned int *outputs);
/* Returns the name for a selected mixer input */
char *usb_mixer_get_input_name(unsigned int mixer, unsigned int input);
/* Returns the name for a selected mixer output */
char *usb_mixer_get_output_name(unsigned int mixer, unsigned int output);
/* Returns the current value of a selected mixer unit */
double usb_mixer_get_value(unsigned int mixer, unsigned int mixer_unit);
/* Sets the current value for a selected mixer unit */
int usb_mixer_set_value(unsigned int mixer, unsigned int mixer_unit, double val);
/* Returns the range values for a selected mixer unit */
int usb_mixer_get_range(unsigned int mixer, unsigned int mixer_unit, double *min, double *max, double *res);
/* Returns the number of bytes read from a mem request, data is stored in data */
int usb_mixer_mem_get(unsigned int mixer, unsigned offset, unsigned char *data);
/* INPUT / OUTPUT / MIXER MAPPING UNIT INTERFACE */
/* Get the number of selectable inputs */
int usb_mixsel_get_input_count(unsigned int mixer);
/* Get the string of a input */
char *usb_mixsel_get_input_string(unsigned int mixer, unsigned int channel);
int usb_mixsel_get_output_count(unsigned int mixer);
int usb_mixer_get_num_outputs(unsigned int mixer);
int usb_mixer_get_num_inputs(unsigned int mixer);
unsigned char usb_mixsel_get_state(unsigned int mixer, unsigned int channel);
void usb_mixsel_set_state(unsigned int mixer, unsigned int dst, unsigned int src);
int usb_set_usb_channel_map(int channel, int val);
/* Get the current map for a specified input / output / mixer channel */
int usb_get_usb_channel_map(int channel);
int usb_get_aud_channel_map(int channel);
/* Maps an input / output / mixer channel to another input / output / mixer channel */
int usb_set_aud_channel_map(int channel, int val);
int usb_set_usb_channel_map(int channel, int val);
/* Gets the name of a specified channel */
char *usb_get_aud_channel_map_name(int channel);
char *usb_get_usb_channel_map_name(int channel);
/* Get the type of a channel map */
enum usb_chan_type usb_get_aud_channel_map_type(int channel);
enum usb_chan_type usb_get_usb_channel_map_type(int channel);
int usb_get_aud_channel_map_num_outputs();
int usb_get_usb_channel_map_num_outputs();
int usb_get_aud_channel_map_num_inputs();
int usb_get_usb_channel_map_num_inputs();
/* CUSTOM/GENERIC AUDIO CLASS REQUESTS */
int usb_audio_class_get(unsigned char bRequest, unsigned char cs, unsigned char cn, unsigned short unitID, unsigned short wLength, unsigned char *data);
int usb_audio_class_set(unsigned char bRequest, unsigned char cs, unsigned char cn, unsigned short unitID, unsigned short wLength, unsigned char *data);
double usb_mixer_get_res(unsigned int mixer, unsigned int nodeId);
double usb_mixer_get_min(unsigned int mixer, unsigned int nodeId) ;
double usb_mixer_get_max(unsigned int mixer, unsigned int nodeId) ;