Add volume/mute feature to AudioInputUSBteensy4-core
@@ -44,6 +44,8 @@ audio_block_t * AudioInputUSB::ready_right; | |||
uint16_t AudioInputUSB::incoming_count; | |||
uint8_t AudioInputUSB::receive_flag; | |||
struct usb_audio_features_struct AudioInputUSB::features = {0,0,FEATURE_MAX_VOLUME}; | |||
#define DMABUFATTR __attribute__ ((section(".dmabuffers"), aligned (4))) | |||
uint16_t usb_audio_receive_buffer[AUDIO_RX_SIZE/2] DMABUFATTR; | |||
uint32_t usb_audio_sync_feedback DMABUFATTR; | |||
@@ -342,6 +344,65 @@ unsigned int usb_audio_transmit_callback(void) | |||
return target * 4; | |||
} | |||
int usb_audio_get_feature(void *stp, uint8_t *data, uint32_t *datalen) | |||
{ | |||
struct setup_struct setup = *((struct setup_struct *)stp); | |||
if (setup.bmRequestType==0xA1) { // should check bRequest, bChannel, and UnitID | |||
if (setup.bCS==0x01) { // mute | |||
data[0] = AudioInputUSB::features.mute; // 1=mute, 0=unmute | |||
*datalen = 1; | |||
return 1; | |||
} | |||
else if (setup.bCS==0x02) { // volume | |||
if (setup.bRequest==0x81) { // GET_CURR | |||
data[0] = AudioInputUSB::features.volume & 0xFF; | |||
data[1] = (AudioInputUSB::features.volume>>8) & 0xFF; | |||
} | |||
else if (setup.bRequest==0x82) { // GET_MIN | |||
//serial_print("vol get_min\n"); | |||
data[0] = 0; // min level is 0 | |||
data[1] = 0; | |||
} | |||
else if (setup.bRequest==0x83) { // GET_MAX | |||
data[0] = FEATURE_MAX_VOLUME & 0xFF; // max level, for range of 0 to MAX | |||
data[1] = (FEATURE_MAX_VOLUME>>8) & 0x0F; | |||
} | |||
else if (setup.bRequest==0x84) { // GET_RES | |||
data[0] = 1; // increment vol by by 1 | |||
data[1] = 0; | |||
} | |||
else { // pass over SET_MEM, etc. | |||
return 0; | |||
} | |||
*datalen = 2; | |||
return 1; | |||
} | |||
} | |||
return 0; | |||
} | |||
int usb_audio_set_feature(void *stp, uint8_t *buf) | |||
{ | |||
struct setup_struct setup = *((struct setup_struct *)stp); | |||
if (setup.bmRequestType==0x21) { // should check bRequest, bChannel and UnitID | |||
if (setup.bCS==0x01) { // mute | |||
if (setup.bRequest==0x01) { // SET_CUR | |||
AudioInputUSB::features.mute = buf[0]; // 1=mute,0=unmute | |||
AudioInputUSB::features.change = 1; | |||
return 1; | |||
} | |||
} | |||
else if (setup.bCS==0x02) { // volume | |||
if (setup.bRequest==0x01) { // SET_CUR | |||
AudioInputUSB::features.volume = buf[0] + (buf[1]<<8); | |||
AudioInputUSB::features.change = 1; | |||
return 1; | |||
} | |||
} | |||
} | |||
return 0; | |||
} | |||
#endif // F_CPU | |||
#endif // AUDIO_INTERFACE |
@@ -4,6 +4,8 @@ | |||
#include "usb_desc.h" | |||
#ifdef AUDIO_INTERFACE | |||
#define FEATURE_MAX_VOLUME 0xFFF // volume accepted from 0 to 0xFFF | |||
#ifdef __cplusplus | |||
extern "C" { | |||
#endif | |||
@@ -11,12 +13,48 @@ extern uint16_t usb_audio_receive_buffer[]; | |||
extern uint16_t usb_audio_transmit_buffer[]; | |||
extern void usb_audio_receive_callback(unsigned int len); | |||
extern unsigned int usb_audio_transmit_callback(void); | |||
int usb_audio_set_feature(void *stp, uint8_t *buf); | |||
int usb_audio_get_feature(void *stp, uint8_t *data, uint32_t *datalen); | |||
extern uint32_t usb_audio_sync_feedback; | |||
extern uint8_t usb_audio_receive_setting; | |||
extern uint8_t usb_audio_transmit_setting; | |||
#ifdef __cplusplus | |||
} | |||
// setup struct definition could be moved from usb_dev.c to usb_dev.h so we can reuse | |||
// it instead of redefining it here | |||
struct setup_struct { | |||
union { | |||
struct { | |||
uint8_t bmRequestType; | |||
uint8_t bRequest; | |||
union { | |||
struct { | |||
uint8_t bChannel; // 0=main, 1=left, 2=right | |||
uint8_t bCS; // Control Selector | |||
}; | |||
uint16_t wValue; | |||
}; | |||
union { | |||
struct { | |||
uint8_t bIfEp; // type of entity | |||
uint8_t bEntityId; // UnitID, TerminalID, etc. | |||
}; | |||
uint16_t wIndex; | |||
}; | |||
uint16_t wLength; | |||
}; | |||
}; | |||
}; | |||
// audio features supported | |||
struct usb_audio_features_struct { | |||
int change; // set to 1 when any value is changed | |||
int mute; // 1=mute, 0=unmute | |||
int volume; // volume from 0 to FEATURE_MAX_VOLUME, maybe should be float from 0.0 to 1.0 | |||
}; | |||
#include "AudioStream.h" | |||
class AudioInputUSB : public AudioStream | |||
@@ -26,6 +64,11 @@ public: | |||
virtual void update(void); | |||
void begin(void); | |||
friend void usb_audio_receive_callback(unsigned int len); | |||
friend int usb_audio_set_feature(void *stp, uint8_t *buf); | |||
friend int usb_audio_get_feature(void *stp, uint8_t *data, uint32_t *datalen); | |||
static struct usb_audio_features_struct features; | |||
private: | |||
static bool update_responsibility; | |||
static audio_block_t *incoming_left; |
@@ -475,7 +475,7 @@ static uint8_t flightsim_report_desc[] = { | |||
#define AUDIO_INTERFACE_DESC_POS KEYMEDIA_INTERFACE_DESC_POS+KEYMEDIA_INTERFACE_DESC_SIZE | |||
#ifdef AUDIO_INTERFACE | |||
#define AUDIO_INTERFACE_DESC_SIZE 9+10+12+9+12+9 + 9+9+7+11+9+7 + 9+9+7+11+9+7+9 | |||
#define AUDIO_INTERFACE_DESC_SIZE 9+10+12+9+12+10+9 + 9+9+7+11+9+7 + 9+9+7+11+9+7+9 | |||
#else | |||
#define AUDIO_INTERFACE_DESC_SIZE 0 | |||
#endif | |||
@@ -947,7 +947,7 @@ static uint8_t config_descriptor[CONFIG_DESC_SIZE] = { | |||
0x24, // bDescriptorType, 0x24 = CS_INTERFACE | |||
0x01, // bDescriptorSubtype, 1 = HEADER | |||
0x00, 0x01, // bcdADC (version 1.0) | |||
LSB(52), MSB(52), // wTotalLength | |||
LSB(62), MSB(62), // wTotalLength | |||
2, // bInCollection | |||
AUDIO_INTERFACE+1, // baInterfaceNr(1) - Transmit to PC | |||
AUDIO_INTERFACE+2, // baInterfaceNr(2) - Receive from PC | |||
@@ -987,6 +987,17 @@ static uint8_t config_descriptor[CONFIG_DESC_SIZE] = { | |||
0x03, 0x00, // wChannelConfig, 0x0003 = Left & Right Front | |||
0, // iChannelNames | |||
0, // iTerminal | |||
// Volume feature descriptor | |||
10, // bLength | |||
0x24, // bDescriptorType = CS_INTERFACE | |||
0x06, // bDescriptorSubType = FEATURE_UNIT | |||
0x31, // bUnitID | |||
0x03, // bSourceID (Input Terminal) | |||
0x01, // bControlSize (each channel is 1 byte, 3 channels) | |||
0x01, // bmaControls(0) Master: Mute | |||
0x02, // bmaControls(1) Left: Volume | |||
0x02, // bmaControls(2) Right: Volume | |||
0x00, // iFeature | |||
// Output Terminal Descriptor | |||
// USB DCD for Audio Devices 1.0, Table 4-4, page 40 | |||
9, // bLength | |||
@@ -996,7 +1007,7 @@ static uint8_t config_descriptor[CONFIG_DESC_SIZE] = { | |||
//0x02, 0x03, // wTerminalType, 0x0302 = Headphones | |||
0x02, 0x06, // wTerminalType, 0x0602 = Digital Audio | |||
0, // bAssocTerminal, 0 = unidirectional | |||
3, // bCSourceID, connected to input terminal, ID=3 | |||
0x31, // bCSourceID, connected to feature, ID=31 | |||
0, // iTerminal | |||
// Standard AS Interface Descriptor | |||
// USB DCD for Audio Devices 1.0, Section 4.5.1, Table 4-18, page 59 |
@@ -415,8 +415,25 @@ static void usb_setup(void) | |||
return; | |||
} | |||
break; | |||
case 0x0122: // SET_CUR (wValue=0, wIndex=interface, wLength=len) | |||
case 0x0121: // SET FEATURE | |||
case 0x0221: | |||
case 0x0321: | |||
case 0x0421: | |||
// handle these on the next packet. See usb_audio_set_feature() | |||
return; | |||
case 0x81A1: // GET FEATURE | |||
case 0x82A1: | |||
case 0x83A1: | |||
case 0x84A1: | |||
if (usb_audio_get_feature(&setup, reply_buffer, &datalen)) { | |||
data = reply_buffer; | |||
} | |||
else { | |||
endpoint0_stall(); | |||
return; | |||
} | |||
break; | |||
case 0x81A2: // GET_CUR (wValue=0, wIndex=interface, wLength=len) | |||
if (setup.wLength >= 3) { | |||
reply_buffer[0] = 44100 & 255; | |||
@@ -574,8 +591,7 @@ static void usb_control(uint32_t stat) | |||
} | |||
#endif | |||
#ifdef AUDIO_INTERFACE | |||
if (setup.wRequestAndType == 0x0122 /* SET_CUR */) { | |||
// TODO: actually check data, do something with it? | |||
if (usb_audio_set_feature(&setup, buf)) { | |||
endpoint0_transmit(NULL, 0); | |||
} | |||
#endif |