forked from PAWPAW-Mirror/lib_xua
Merge remote-tracking branch 'upstream' into develop
This commit is contained in:
10
host_usb_mixer_control/.makefile
Normal file
10
host_usb_mixer_control/.makefile
Normal 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 =======================================================
|
||||
|
||||
77
host_usb_mixer_control/.project
Normal file
77
host_usb_mixer_control/.project
Normal 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>
|
||||
2
host_usb_mixer_control/Makefile.OSX
Normal file
2
host_usb_mixer_control/Makefile.OSX
Normal 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
|
||||
4
host_usb_mixer_control/Makefile.Win32
Normal file
4
host_usb_mixer_control/Makefile.Win32
Normal 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
|
||||
BIN
host_usb_mixer_control/OSX/libusb-1.0.0.dylib
Normal file
BIN
host_usb_mixer_control/OSX/libusb-1.0.0.dylib
Normal file
Binary file not shown.
1233
host_usb_mixer_control/OSX/libusb.h
Normal file
1233
host_usb_mixer_control/OSX/libusb.h
Normal file
File diff suppressed because it is too large
Load Diff
942
host_usb_mixer_control/OSX/usb_mixer.cpp
Normal file
942
host_usb_mixer_control/OSX/usb_mixer.cpp
Normal file
@@ -0,0 +1,942 @@
|
||||
// Copyright 2022-2023 XMOS LIMITED.
|
||||
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
|
||||
#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;
|
||||
}
|
||||
|
||||
115
host_usb_mixer_control/README
Normal file
115
host_usb_mixer_control/README
Normal file
@@ -0,0 +1,115 @@
|
||||
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 extension 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 extension 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 always be 0):
|
||||
|
||||
--help
|
||||
|
||||
|
||||
--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-source 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.
|
||||
|
||||
--get-mixer-levels-input
|
||||
|
||||
--get-mixer-levels-output
|
||||
|
||||
--vendor-audio-request-get bRequest, ControlSelector, ChannelNumber, UnitId
|
||||
|
||||
--vendor-audio-request-set bRequest, ControlSelector, ChannelNumber, UnitId, Data[0], Data[1],...
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
47
host_usb_mixer_control/Win32/global.h
Normal file
47
host_usb_mixer_control/Win32/global.h
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright 2022-2023 XMOS LIMITED.
|
||||
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
|
||||
|
||||
/************************************************************************
|
||||
*
|
||||
* 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"
|
||||
|
||||
// libwn.h pulls in windows.h
|
||||
#include "libwn.h"
|
||||
// TUSBAUDIO driver API
|
||||
#include "tusbaudioapi.h"
|
||||
#include "TUsbAudioApiDll.h"
|
||||
|
||||
|
||||
#endif // __global_h__
|
||||
|
||||
/*************************** EOF **************************************/
|
||||
919
host_usb_mixer_control/Win32/usb_mixer.cpp
Normal file
919
host_usb_mixer_control/Win32/usb_mixer.cpp
Normal file
@@ -0,0 +1,919 @@
|
||||
// Copyright 2022-2023 XMOS LIMITED.
|
||||
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "usb_mixer.h"
|
||||
#include "global.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;
|
||||
}
|
||||
|
||||
721
host_usb_mixer_control/mixer_app.cpp
Normal file
721
host_usb_mixer_control/mixer_app.cpp
Normal file
@@ -0,0 +1,721 @@
|
||||
// Copyright 2022-2023 XMOS LIMITED.
|
||||
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
|
||||
#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;
|
||||
}
|
||||
|
||||
|
||||
1
host_usb_mixer_control/module_description
Normal file
1
host_usb_mixer_control/module_description
Normal 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)
|
||||
1
host_usb_mixer_control/setup.sh
Normal file
1
host_usb_mixer_control/setup.sh
Normal file
@@ -0,0 +1 @@
|
||||
export DYLD_LIBRARY_PATH=$PWD/OSX:$DYLD_LIBRARY_PATH
|
||||
108
host_usb_mixer_control/usb_mixer.h
Normal file
108
host_usb_mixer_control/usb_mixer.h
Normal file
@@ -0,0 +1,108 @@
|
||||
// Copyright 2022-2023 XMOS LIMITED.
|
||||
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
|
||||
|
||||
#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) ;
|
||||
@@ -87,6 +87,9 @@ intended as an example of how you might add mixer control to your own control ap
|
||||
intended to be exposed to end users.
|
||||
|
||||
For details, consult the README file in the host_usb_mixer_control directory.
|
||||
A list of arguments can also be seen with::
|
||||
|
||||
$ ./xmos_mixer --help
|
||||
|
||||
The main requirements of this control utility are to
|
||||
|
||||
@@ -102,78 +105,147 @@ The main requirements of this control utility are to
|
||||
functionality to their end users.
|
||||
|
||||
Whilst using the XMOS Host control example application, consider the example of setting the
|
||||
mixer to perform a loop-back from analogue inputs 1 and 2 to analogue outputs 1 and 2.
|
||||
mixer to perform a loop-back from analogue inputs 1 & 2 to analogue outputs 1 & 2.
|
||||
|
||||
Firstly consider the inputs to the mixer. The following will displays which channels are mapped
|
||||
to which mixer inputs::
|
||||
.. note::
|
||||
|
||||
./xmos_mixer --display-aud-channel-map 0
|
||||
The command outputs shown are examples; the actual output will depend on the mixer configuration.
|
||||
|
||||
The following command will displays which channels could possibly be mapped to mixer inputs. Notice
|
||||
that analogue inputs 1 and 2 are on mixer inputs 10 and 11::
|
||||
The following will show the index for each device output along with which channel is currently mapped to it.
|
||||
In this example the analogue outputs 1 & 2 are 0 & 1 respectively::
|
||||
|
||||
./xmos_mixer --display-aud-channel-map-sources 0
|
||||
$ ./xmos_mixer --display-aud-channel-map
|
||||
|
||||
Now examine the audio output mapping using the following command::
|
||||
Audio Output Channel Map
|
||||
------------------------
|
||||
|
||||
0 (DEVICE OUT - Analogue 1) source is 0 (DAW OUT - Analogue 1)
|
||||
1 (DEVICE OUT - Analogue 2) source is 1 (DAW OUT - Analogue 2)
|
||||
2 (DEVICE OUT - SPDIF 1) source is 2 (DAW OUT - SPDIF 1)
|
||||
3 (DEVICE OUT - SPDIF 2) source is 3 (DAW OUT - SPDIF 2)
|
||||
$ _
|
||||
|
||||
./xmos_mixer --display-aud-channel-map 0
|
||||
The DAW Output Map can be seen with::
|
||||
|
||||
This displays which channels are mapped to which outputs. By default all
|
||||
of these bypass the mixer. We can also see what all the possible
|
||||
mappings are with the following command::
|
||||
$ ./xmos_mixer --display-daw-channel-map
|
||||
|
||||
./xmos_mixer --display-aud-channel-map-sources 0
|
||||
DAW Output To Host Channel Map
|
||||
------------------------
|
||||
|
||||
0 (DEVICE IN - Analogue 1) source is 4 (DEVICE IN - Analogue 1)
|
||||
1 (DEVICE IN - Analogue 2) source is 5 (DEVICE IN - Analogue 2)
|
||||
$ _
|
||||
|
||||
We will now map the first two mixer outputs to physical outputs 1 and 2::
|
||||
.. note::
|
||||
|
||||
./xmos_mixer --set-aud-channel-map 0 26
|
||||
./xmos_mixer --set-aud-channel-map 1 27
|
||||
In both cases, by default, these bypass the mixer.
|
||||
|
||||
The following command will list the channels which can be mapped to the device outputs from the
|
||||
Audio Output Channel Map. Note that, in this example, analogue inputs 1 & 2 are source 4 & 5 and
|
||||
Mix 1 & 2 are source 6 & 7::
|
||||
|
||||
$ ./xmos_mixer --display-aud-channel-map-sources
|
||||
|
||||
Audio Output Channel Map Source List
|
||||
------------------------------------
|
||||
|
||||
0 (DAW OUT - Analogue 1)
|
||||
1 (DAW OUT - Analogue 2)
|
||||
2 (DAW OUT - SPDIF 1)
|
||||
3 (DAW OUT - SPDIF 2)
|
||||
4 (DEVICE IN - Analogue 1)
|
||||
5 (DEVICE IN - Analogue 2)
|
||||
6 (MIX - Mix 1)
|
||||
7 (MIX - Mix 2)
|
||||
$ _
|
||||
|
||||
Using the indices from the previous commands, we will now re-map the first two mixer channels (Mix 1 & Mix 2) to device outputs 1 & 2::
|
||||
|
||||
$ ./xmos_mixer --set-aud-channel-map 0 6
|
||||
$ ./xmos_mixer --set-aud-channel-map 1 7
|
||||
$ _
|
||||
|
||||
You can confirm the effect of this by re-checking the map::
|
||||
|
||||
./xmos_mixer --display-aud-channel-map 0
|
||||
$ ./xmos_mixer --display-aud-channel-map
|
||||
|
||||
This now derives analogue outputs 1 and 2 from the mixer, rather than directly from USB. However,
|
||||
since the mixer is still mapped to pass the USB channels through to the outputs there will be no
|
||||
Audio Output Channel Map
|
||||
------------------------
|
||||
|
||||
0 (DEVICE OUT - Analogue 1) source is 6 (MIX - Mix 1)
|
||||
1 (DEVICE OUT - Analogue 2) source is 7 (MIX - Mix 2)
|
||||
2 (DEVICE OUT - SPDIF 1) source is 2 (DAW OUT - SPDIF 1)
|
||||
3 (DEVICE OUT - SPDIF 2) source is 3 (DAW OUT - SPDIF 2)
|
||||
$ _
|
||||
|
||||
This now derives analogue outputs 1 & 2 from the mixer, rather than directly from USB. However,
|
||||
since the mixer is mapped, by default, to just pass the USB channels through to the outputs there will be no
|
||||
functional change.
|
||||
|
||||
The mixer nodes need to be individually set. They can be displayed
|
||||
|
||||
.. note::
|
||||
|
||||
The USB audio reference design has only one unit so the mixer_id argument should always be 0.
|
||||
|
||||
The mixer nodes need to be individually set. The nodes in mixer_id 0 can be displayed
|
||||
with the following command::
|
||||
|
||||
./xmos_mixer --display-mixer-nodes 0
|
||||
$ ./xmos_mixer --display-mixer-nodes 0
|
||||
|
||||
To get the audio from the analogue inputs to outputs 1 and 2, nodes 80
|
||||
and 89 need to be set::
|
||||
Mixer Values (0)
|
||||
----------------
|
||||
|
||||
./xmos_mixer --set-value 0 80 0
|
||||
./xmos_mixer --set-value 0 89 0
|
||||
Mixer outputs
|
||||
1 2
|
||||
DAW - Analogue 1 0:[0000.000] 1:[ -inf ]
|
||||
DAW - Analogue 2 2:[ -inf ] 3:[0000.000]
|
||||
DAW - SPDIF 1 4:[ -inf ] 5:[ -inf ]
|
||||
DAW - SPDIF 2 6:[ -inf ] 7:[ -inf ]
|
||||
AUD - Analogue 1 8:[ -inf ] 9:[ -inf ]
|
||||
AUD - Analogue 2 10:[ -inf ] 11:[ -inf ]
|
||||
$ _
|
||||
|
||||
With mixer outputs 1 & 2 mapped to device outputs analogue 1 & 2; to get the audio from the analogue inputs to device
|
||||
outputs mixer_id 0 node 8 and node 11 need to be set to 0db::
|
||||
|
||||
$ ./xmos_mixer --set-value 0 8 0
|
||||
$ ./xmos_mixer --set-value 0 11 0
|
||||
$ _
|
||||
|
||||
At the same time, the original mixer outputs can be muted::
|
||||
|
||||
./xmos_mixer --set-value 0 0 -inf
|
||||
./xmos_mixer --set-value 0 9 -inf
|
||||
$ ./xmos_mixer --set-value 0 0 -inf
|
||||
$ ./xmos_mixer --set-value 0 3 -inf
|
||||
$ _
|
||||
|
||||
Now audio inputs on analogue 1/2 should be heard on outputs 1/2.
|
||||
Now audio inputs on analogue 1 and 2 should be heard on outputs 1 and 2 respectively.
|
||||
|
||||
As mentioned above, the flexibility of the mixer is such that there will be multiple ways to create
|
||||
a particular mix. Another option to create the same routing would be to change the mixer sources
|
||||
such that mixer 1/2 outputs come from the analogue inputs.
|
||||
.. As mentioned above, the flexibility of the mixer is such that there will be multiple ways to create
|
||||
.. a particular mix. Another option to create the same routing would be to change the mixer sources
|
||||
.. such that mixer outputs 1 and 2 come from the analogue inputs 1 and 2.
|
||||
|
||||
To demonstrate this, firstly undo the changes above (or simply reset the device)::
|
||||
.. To demonstrate this, firstly undo the changes above (or simply reset the device)::
|
||||
|
||||
./xmos_mixer --set-value 0 80 -inf
|
||||
./xmos_mixer --set-value 0 89 -inf
|
||||
./xmos_mixer --set-value 0 0 0
|
||||
./xmos_mixer --set-value 0 9 0
|
||||
.. $ ./xmos_mixer --set-value 0 8 -inf
|
||||
.. $ ./xmos_mixer --set-value 0 11 -inf
|
||||
.. $ ./xmos_mixer --set-value 0 0 0
|
||||
.. $ ./xmos_mixer --set-value 0 3 0
|
||||
.. $ _
|
||||
|
||||
The mixer should now have the default values. The sources for mixer 1/2 can now be changed::
|
||||
.. The mixer should now have the default values. The sources for mixer 0 output 1 and 2 can now be changed
|
||||
.. using indices from the Audio Output Channel Map Source List::
|
||||
|
||||
./xmos_mixer --set-mixer-source 0 0 10
|
||||
./xmos_mixer --set-mixer-source 0 1 11
|
||||
.. $ ./xmos_mixer --set-mixer-source 0 0 4
|
||||
|
||||
If you re-run the following command then the first column now has "AUD - Analogue 1 and 2" rather
|
||||
than "DAW (Digital Audio Workstation i.e. the host) - Analogue 1 and 2" confirming the new mapping.
|
||||
Again, by playing audio into analogue inputs 1/2 this can be heard looped through to analogue outputs 1/2::
|
||||
.. Set mixer(0) input 0 to device input 4 (AUD - Analogue 1)
|
||||
.. $ ./xmos_mixer --set-mixer-source 0 1 5
|
||||
|
||||
.. Set mixer(0) input 1 to device input 5 (AUD - Analogue 2)
|
||||
.. $ _
|
||||
|
||||
.. If you re-run the following command then the first column now has "AUD - Analogue 1 and 2" rather
|
||||
.. than "DAW (Digital Audio Workstation i.e. the host) - Analogue 1 and 2" confirming the new mapping.
|
||||
.. Again, by playing audio into analogue inputs 1/2 this can be heard looped through to analogue outputs 1/2::
|
||||
|
||||
./xmos_mixer --display-mixer-nodes 0
|
||||
.. $ ./xmos_mixer --display-mixer-nodes 0
|
||||
|
||||
|
||||
Reference in New Issue
Block a user