forked from PAWPAW-Mirror/lib_xua
12
Jenkinsfile
vendored
12
Jenkinsfile
vendored
@@ -106,6 +106,12 @@ pipeline {
|
|||||||
dir("${REPO}/${REPO}/host/xmosdfu") {
|
dir("${REPO}/${REPO}/host/xmosdfu") {
|
||||||
sh 'make -f Makefile.OSX64'
|
sh 'make -f Makefile.OSX64'
|
||||||
}
|
}
|
||||||
|
dir("${REPO}/host_usb_mixer_control") {
|
||||||
|
sh 'make -f Makefile.OSX'
|
||||||
|
sh 'mkdir OSX/x86'
|
||||||
|
sh 'mv xmos_mixer OSX/x86/xmos_mixer'
|
||||||
|
archiveArtifacts artifacts: "OSX/x86/xmos_mixer", fingerprint: true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
post {
|
post {
|
||||||
cleanup {
|
cleanup {
|
||||||
@@ -141,6 +147,12 @@ pipeline {
|
|||||||
dir("${REPO}/host/xmosdfu") {
|
dir("${REPO}/host/xmosdfu") {
|
||||||
runVS('nmake /f Makefile.Win32')
|
runVS('nmake /f Makefile.Win32')
|
||||||
}
|
}
|
||||||
|
dir("host_usb_mixer_control") {
|
||||||
|
runVS('msbuild host_usb_mixer_control.vcxproj /property:Configuration=Release /property:Platform=x64')
|
||||||
|
sh 'mkdir Win/x64'
|
||||||
|
sh 'mv bin/Release/x64/host_usb_mixer_control.exe Win/x64/xmos_mixer.exe'
|
||||||
|
archiveArtifacts artifacts: "Win/x64/xmos_mixer.exe", fingerprint: true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
post {
|
post {
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
all:
|
all:
|
||||||
g++ -g -o xmos_mixer OSX/usb_mixer.cpp mixer_app.cpp -I. -IOSX OSX/libusb-1.0.0.dylib -m64
|
g++ -g -o xmos_mixer OSX/usb_mixer.cpp mixer_app.cpp -I. -IOSX OSX/libusb-1.0.0.dylib -arch x86_64
|
||||||
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
// Copyright 2022-2023 XMOS LIMITED.
|
// Copyright 2022-2023 XMOS LIMITED.
|
||||||
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
|
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
#define _WIN32_WINNT 0x0500 //Windows 2000 or later
|
#define _WIN32_WINNT 0x0500 //Windows 2000 or later
|
||||||
//#define _WIN32_WINNT 0x0501 //Windows XP or later
|
//#define _WIN32_WINNT 0x0501 //Windows XP or later
|
||||||
//#define _WIN32_WINNT 0x0600 //Windows Vista or later
|
//#define _WIN32_WINNT 0x0600 //Windows Vista or later
|
||||||
|
//#define _WIN32_WINNT 0x0A00 //Windows 10 or later
|
||||||
|
|
||||||
// exclude rarely-used stuff from Windows headers
|
// exclude rarely-used stuff from Windows headers
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
924
host_usb_mixer_control/Win/usb_mixer.cpp
Normal file
924
host_usb_mixer_control/Win/usb_mixer.cpp
Normal file
@@ -0,0 +1,924 @@
|
|||||||
|
// 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 gDrvApi.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 gDrvApi.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 gDrvApi.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 = gDrvApi.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 = gDrvApi.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 = gDrvApi.TUSBAUDIO_GetDeviceCount();
|
||||||
|
if (0 == devcnt) {
|
||||||
|
return USB_MIXER_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
st = gDrvApi.TUSBAUDIO_OpenDeviceByIndex(0, &h);
|
||||||
|
if (TSTATUS_SUCCESS != st) {
|
||||||
|
h = 0;
|
||||||
|
// skip
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
unsigned int numBytes = 0;
|
||||||
|
unsigned char descBuffer[64 * 1024];
|
||||||
|
|
||||||
|
st = gDrvApi.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;
|
||||||
|
|
||||||
|
//const char guid[] = "{E5A2658B-817D-4A02-A1DE-B628A93DDF5D}";
|
||||||
|
//TCHAR guid[39];
|
||||||
|
//strcpy(guid, "{E5A2658B-817D-4A02-A1DE-B628A93DDF5D}");
|
||||||
|
|
||||||
|
gDrvApi.LoadByGUID(_T("{E5A2658B-817D-4A02-A1DE-B628A93DDF5D}"));
|
||||||
|
|
||||||
|
|
||||||
|
// 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 = gDrvApi.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) {
|
||||||
|
gDrvApi.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;
|
||||||
|
}
|
||||||
@@ -1,919 +0,0 @@
|
|||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
177
host_usb_mixer_control/host_usb_mixer_control.vcxproj
Normal file
177
host_usb_mixer_control/host_usb_mixer_control.vcxproj
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup Label="ProjectConfigurations">
|
||||||
|
<ProjectConfiguration Include="Debug|Win32">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|Win32">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Debug|x64">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|x64">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
</ItemGroup>
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<VCProjectVersion>16.0</VCProjectVersion>
|
||||||
|
<Keyword>Win32Proj</Keyword>
|
||||||
|
<ProjectGuid>{84eacf4f-e405-4909-b440-a04a84a3f8c8}</ProjectGuid>
|
||||||
|
<RootNamespace>
|
||||||
|
</RootNamespace>
|
||||||
|
<SDKPath>C:\Program Files\XMOS\tusbaudiosdk</SDKPath>
|
||||||
|
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v142</PlatformToolset>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v142</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v142</PlatformToolset>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v142</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||||
|
<ImportGroup Label="ExtensionSettings">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="Shared">
|
||||||
|
<Import Project="$(SDKPath)\source\tusbaudioapi_inc\tusbaudioapi_inc_vs2019.vcxitems" Label="Shared" />
|
||||||
|
<Import Project="$(SDKPath)\source\libwn_min\_libwn_min_vs2019.vcxitems" Label="Shared" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<PropertyGroup Label="UserMacros" />
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<OutDir>.\bin\$(Configuration)\$(PlatformName)\</OutDir>
|
||||||
|
<IntDir>$(Configuration)\$(PlatformName)_$(PlatformToolset)</IntDir>
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<OutDir>.\bin\$(Configuration)\$(PlatformName)\</OutDir>
|
||||||
|
<IntDir>$(Configuration)\$(PlatformName)_$(PlatformToolset)</IntDir>
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<ConformanceMode>true</ConformanceMode>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<ConformanceMode>true</ConformanceMode>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level4</WarningLevel>
|
||||||
|
<SDLCheck>
|
||||||
|
</SDLCheck>
|
||||||
|
<PreprocessorDefinitions>_DEBUG;_AMD64;_CONSOLE;_UNICODE;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<ConformanceMode>true</ConformanceMode>
|
||||||
|
<AdditionalIncludeDirectories>.;.\Win;$(SDKPath)\source\shared;$(SDKPath)\source\inc;$(SDKPath)\source\libwn_min;$(SDKPath)\source\libwtl;$(SDKPath)\source\tusbaudioapi_inc</AdditionalIncludeDirectories>
|
||||||
|
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||||
|
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||||
|
<ExceptionHandling>Async</ExceptionHandling>
|
||||||
|
<PrecompiledHeaderFile />
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
<EnableUAC>false</EnableUAC>
|
||||||
|
<UACExecutionLevel />
|
||||||
|
<UACUIAccess />
|
||||||
|
<GenerateMapFile>true</GenerateMapFile>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level4</WarningLevel>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<IntrinsicFunctions>false</IntrinsicFunctions>
|
||||||
|
<SDLCheck>
|
||||||
|
</SDLCheck>
|
||||||
|
<PreprocessorDefinitions>_AMD64;_CONSOLE;_UNICODE;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<ConformanceMode>true</ConformanceMode>
|
||||||
|
<AdditionalIncludeDirectories>.;.\Win;$(SDKPath)\source\shared;$(SDKPath)\source\inc;$(SDKPath)\source\libwn_min;$(SDKPath)\source\libwtl;$(SDKPath)\source\tusbaudioapi_inc</AdditionalIncludeDirectories>
|
||||||
|
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||||
|
<ExceptionHandling>Async</ExceptionHandling>
|
||||||
|
<PrecompiledHeaderFile />
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
<EnableUAC>false</EnableUAC>
|
||||||
|
<UACExecutionLevel />
|
||||||
|
<UACUIAccess />
|
||||||
|
<GenerateMapFile>true</GenerateMapFile>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="usb_mixer.h" />
|
||||||
|
<ClInclude Include="Win\global.h" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="mixer_app.cpp" />
|
||||||
|
<ClCompile Include="Win\usb_mixer.cpp" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
</ImportGroup>
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup>
|
||||||
|
<Filter Include="Source Files">
|
||||||
|
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||||
|
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="Header Files">
|
||||||
|
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||||
|
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="Resource Files">
|
||||||
|
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||||
|
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||||
|
</Filter>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="usb_mixer.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="Win\global.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="mixer_app.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="Win\usb_mixer.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup />
|
||||||
|
</Project>
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
// Copyright 2022-2023 XMOS LIMITED.
|
// Copyright 2022-2023 XMOS LIMITED.
|
||||||
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
|
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|||||||
Reference in New Issue
Block a user