Add volume/mute feature to AudioInputUSBteensy4-core
uint16_t AudioInputUSB::incoming_count; | uint16_t AudioInputUSB::incoming_count; | ||||
uint8_t AudioInputUSB::receive_flag; | uint8_t AudioInputUSB::receive_flag; | ||||
struct usb_audio_features_struct AudioInputUSB::features = {0,0,FEATURE_MAX_VOLUME}; | |||||
#define DMABUFATTR __attribute__ ((section(".dmabuffers"), aligned (4))) | #define DMABUFATTR __attribute__ ((section(".dmabuffers"), aligned (4))) | ||||
uint16_t usb_audio_receive_buffer[AUDIO_RX_SIZE/2] DMABUFATTR; | uint16_t usb_audio_receive_buffer[AUDIO_RX_SIZE/2] DMABUFATTR; | ||||
uint32_t usb_audio_sync_feedback DMABUFATTR; | uint32_t usb_audio_sync_feedback DMABUFATTR; | ||||
return target * 4; | 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 // F_CPU | ||||
#endif // AUDIO_INTERFACE | #endif // AUDIO_INTERFACE |
#include "usb_desc.h" | #include "usb_desc.h" | ||||
#ifdef AUDIO_INTERFACE | #ifdef AUDIO_INTERFACE | ||||
#define FEATURE_MAX_VOLUME 0xFFF // volume accepted from 0 to 0xFFF | |||||
#ifdef __cplusplus | #ifdef __cplusplus | ||||
extern "C" { | extern "C" { | ||||
#endif | #endif | ||||
extern uint16_t usb_audio_transmit_buffer[]; | extern uint16_t usb_audio_transmit_buffer[]; | ||||
extern void usb_audio_receive_callback(unsigned int len); | extern void usb_audio_receive_callback(unsigned int len); | ||||
extern unsigned int usb_audio_transmit_callback(void); | 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 uint32_t usb_audio_sync_feedback; | ||||
extern uint8_t usb_audio_receive_setting; | extern uint8_t usb_audio_receive_setting; | ||||
extern uint8_t usb_audio_transmit_setting; | extern uint8_t usb_audio_transmit_setting; | ||||
#ifdef __cplusplus | #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" | #include "AudioStream.h" | ||||
class AudioInputUSB : public AudioStream | class AudioInputUSB : public AudioStream | ||||
virtual void update(void); | virtual void update(void); | ||||
void begin(void); | void begin(void); | ||||
friend void usb_audio_receive_callback(unsigned int len); | 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: | private: | ||||
static bool update_responsibility; | static bool update_responsibility; | ||||
static audio_block_t *incoming_left; | static audio_block_t *incoming_left; |
#define AUDIO_INTERFACE_DESC_POS KEYMEDIA_INTERFACE_DESC_POS+KEYMEDIA_INTERFACE_DESC_SIZE | #define AUDIO_INTERFACE_DESC_POS KEYMEDIA_INTERFACE_DESC_POS+KEYMEDIA_INTERFACE_DESC_SIZE | ||||
#ifdef AUDIO_INTERFACE | #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 | #else | ||||
#define AUDIO_INTERFACE_DESC_SIZE 0 | #define AUDIO_INTERFACE_DESC_SIZE 0 | ||||
#endif | #endif | ||||
0x24, // bDescriptorType, 0x24 = CS_INTERFACE | 0x24, // bDescriptorType, 0x24 = CS_INTERFACE | ||||
0x01, // bDescriptorSubtype, 1 = HEADER | 0x01, // bDescriptorSubtype, 1 = HEADER | ||||
0x00, 0x01, // bcdADC (version 1.0) | 0x00, 0x01, // bcdADC (version 1.0) | ||||
LSB(52), MSB(52), // wTotalLength | |||||
LSB(62), MSB(62), // wTotalLength | |||||
2, // bInCollection | 2, // bInCollection | ||||
AUDIO_INTERFACE+1, // baInterfaceNr(1) - Transmit to PC | AUDIO_INTERFACE+1, // baInterfaceNr(1) - Transmit to PC | ||||
AUDIO_INTERFACE+2, // baInterfaceNr(2) - Receive from PC | AUDIO_INTERFACE+2, // baInterfaceNr(2) - Receive from PC | ||||
0x03, 0x00, // wChannelConfig, 0x0003 = Left & Right Front | 0x03, 0x00, // wChannelConfig, 0x0003 = Left & Right Front | ||||
0, // iChannelNames | 0, // iChannelNames | ||||
0, // iTerminal | 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 | // Output Terminal Descriptor | ||||
// USB DCD for Audio Devices 1.0, Table 4-4, page 40 | // USB DCD for Audio Devices 1.0, Table 4-4, page 40 | ||||
9, // bLength | 9, // bLength | ||||
//0x02, 0x03, // wTerminalType, 0x0302 = Headphones | //0x02, 0x03, // wTerminalType, 0x0302 = Headphones | ||||
0x02, 0x06, // wTerminalType, 0x0602 = Digital Audio | 0x02, 0x06, // wTerminalType, 0x0602 = Digital Audio | ||||
0, // bAssocTerminal, 0 = unidirectional | 0, // bAssocTerminal, 0 = unidirectional | ||||
3, // bCSourceID, connected to input terminal, ID=3 | |||||
0x31, // bCSourceID, connected to feature, ID=31 | |||||
0, // iTerminal | 0, // iTerminal | ||||
// Standard AS Interface Descriptor | // Standard AS Interface Descriptor | ||||
// USB DCD for Audio Devices 1.0, Section 4.5.1, Table 4-18, page 59 | // USB DCD for Audio Devices 1.0, Section 4.5.1, Table 4-18, page 59 |
return; | return; | ||||
} | } | ||||
break; | 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; | 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) | case 0x81A2: // GET_CUR (wValue=0, wIndex=interface, wLength=len) | ||||
if (setup.wLength >= 3) { | if (setup.wLength >= 3) { | ||||
reply_buffer[0] = 44100 & 255; | reply_buffer[0] = 44100 & 255; | ||||
} | } | ||||
#endif | #endif | ||||
#ifdef AUDIO_INTERFACE | #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); | endpoint0_transmit(NULL, 0); | ||||
} | } | ||||
#endif | #endif |