NOTICE: The Processors Wiki will End-of-Life on January 15, 2021. It is recommended to download any files or other content you may need that are hosted on processors.wiki.ti.com. The site is now set to read only.

Template:RemoTI-1.4.0 ZRC Dongle Voice

From Texas Instruments Wiki
Jump to: navigation, search

Detailed guide to add Voice support for the ZRC Dongle project[edit]

This section describes all steps required to add the Voice remote functionality to RemoTI-1.4.0. There are several aspects and gotchas to consider. Each code snippet that is added comes with a brief comment of why it is added.

Project options update[edit]

  1. Add $PROJ_DIR$\..\..\..\..\COMPONENTS\audio\include to the include paths
  2. Add to the pre-compiler options the following two defines
  3.   
      VOICE_STREAM
      HAL_USB_DMA=TRUE
      
    
    IncludePath-Define-ForVoiceDongle.png
  4. We need to link with the audio library, in addition to the network library. Instead of adding the path to the compile options, we add a folder in the project, and then place both libraries there.
  5. AudioLibraryFolderInIARprojectTarget.png
    
  6. Uncheck the box which includes command line arguments for the linker. This is required since we add the library to the project in a new folder.
  7. UncheckIncludeCommandLineArgumentsForLinkerTarget.png
    
  8. To improve throughput and processing time we need to use compiler optimization settings for speed
  9. AudioIARprojectOptimizationForSpeed.png
    
  10. Add USB audio files usb_aud.c/h
  11. AddAudioUSBinIARproject.png
    

Source files update[edit]

Application[edit]

In the main application code, zrc_dongle.c

  1. Include headers
  2. 
    #ifdef VOICE_STREAM
    #include "usb_aud.h"
    #include "ratl_lib.h"
    #endif
    
    
  3. Add defines for audio buffer management, and audio decoding configuration structure
  4. 
    #ifdef VOICE_STREAM
    #define USB_AUDIO_NB_FRAME_TO_BUF               3
    #define USB_MAX_AUDIO_RX_QUEUE_SIZE            13
    #define AUDIO_DEAD_LINK_TIMER                1000   //if no packet is received during this time, link is dead, so stop everything.
    ratl_Config_t UsbDecodeconfig;
    #endif
    
    
  5. Add function prototype to handle stop event. Also add a global counter, which is used to count audio frames to toggle LED in a pattern based on received data
  6. 
    #ifdef VOICE_STREAM
    static void audio_EventStop( void );
    uint8 kk;
    #endif
    
    
  7. In the function Dongle_Init() add code to initialize and configure Audio Target library
  8. 
    #ifdef VOICE_STREAM
     // Want Audio buffer initialized as early as possible, so do it here
     extern void RATL_DecodeInit( void );
     extern CBAudioBuf_t pUsbAudioTxBuffer;
     RATL_DecodeInit();
     pUsbAudioTxBuffer.usbSamples = usbDataBuf;  
     pUsbAudioTxBuffer.size = MAX_USB_AUDIO_BUFFER_SIZE;
     
     
     UsbDecodeconfig.CircBuf            = &pUsbAudioTxBuffer;
     UsbDecodeconfig.MaxNbByteToDecode  = USB_NB_BYTES_TO_DECODE;
     UsbDecodeconfig.MaxRxQueueSize     = USB_MAX_AUDIO_RX_QUEUE_SIZE;
     UsbDecodeconfig.BufferingSize      = USB_AUDIO_NB_FRAME_TO_BUF;
     
     RATL_decodeCfg( &UsbDecodeconfig);
    #endif
    
    
  9. Add processing of Audio events to Dongle_ProcessEvent()
  10. 
    #ifdef VOICE_STREAM
      if (events & ZRC_DONGLE_EVT_DEAD_LINK )
      {    
        audio_EventStop();
        return (events ^ ZRC_DONGLE_EVT_DEAD_LINK );
      }
      if ( events & ZRC_DONGLE_EVT_AUD_DECODE )
      {
        if (RATL_DecodeAudioSamples()) 
        {
          return events;
        }
        else
        {
          return (events ^ ZRC_DONGLE_EVT_AUD_DECODE );
        }
      }
    #endif
    
    
  11. Add received data processing in RTI_ReceiveDataInd(). This includes detecting whether or not we are already receiving audio data, and if not already receiving disable Frequency Agility.
  12. 
    #if defined VOICE_STREAM
     else if (len > 0 && profileId == RTI_PROFILE_ZRC && (rxFlags & RTI_RX_FLAGS_VENDOR_SPECIFIC) )
     {
       if (pData[0] == RTI_PROTOCOL_RAS)
       {
         uint8 res;
         halIntState_t intState;
         audioDataEvent_t audData;
         audData.len = len-1;
         audData.pData = (pData+1);
         HAL_ENTER_CRITICAL_SECTION(intState);
         uint8 temp = usbStatus;
         HAL_EXIT_CRITICAL_SECTION(intState);
         osal_start_timerEx(zrcDongleTaskId, ZRC_DONGLE_EVT_DEAD_LINK, AUDIO_DEAD_LINK_TIMER);       
         if ( RATL_COPY_ALLOWED == (res = RATL_ProcessDataPacket( &audData, temp)))
         {
           streamStatus = STREAM_START_COPY;
         }
         else if (RATL_STOPPED == res)
         {
           //Stop Cmd Receive
            audio_EventStop();
         }
         else if (RATL_DECODING_ALLOWED == res)
         {
           //Start Decoding (needed for buffering...)
           osal_set_event(zrcDongleTaskId, ZRC_DONGLE_EVT_AUD_DECODE);
         }
         //Play LED:
         if (temp == USB_START)
         {
            if (kk++ == 50)
             HalLedSet(HAL_LED_1, HAL_LED_MODE_ON);
           if (kk >= 100)
           {
             HalLedSet(HAL_LED_1, HAL_LED_MODE_OFF);
             HalLedSet(HAL_LED_2, HAL_LED_MODE_OFF);
             kk = 0;
           }
         }
         else
         {
           if (kk++ == 50)
             HalLedSet(HAL_LED_2, HAL_LED_MODE_ON);
           if (kk >= 100)
           {
             HalLedSet(HAL_LED_1, HAL_LED_MODE_OFF);
             HalLedSet(HAL_LED_2, HAL_LED_MODE_OFF);
             kk = 0;
           }
         }                
       }
     }
    #endif
    
    
  13. Implement function to handle stop stream event
  14. 
    #ifdef VOICE_STREAM
    static void audio_EventStop( void )
    {
      streamStatus = STREAM_STOP;
     
      RATL_audioStop();
     
      osal_clear_event(zrcDongleTaskId, ZRC_DONGLE_EVT_AUD_DECODE);
      osal_stop_timerEx(zrcDongleTaskId, ZRC_DONGLE_EVT_DEAD_LINK);
      osal_clear_event(zrcDongleTaskId, ZRC_DONGLE_EVT_DEAD_LINK);
    }
    #endif
    
    

In zrc_dongle.h

  1. Add audio stream events to the list
  2. 
    #ifdef VOICE_STREAM
    #define ZRC_DONGLE_EVT_AUD_DECODE         0x0010 // event to decode audio frame
    #define ZRC_DONGLE_EVT_DEAD_LINK          0x0020 // timed event to detect dead link
    #endif
    
    

Profile[edit]

Audio data are sent as Texas Instruments vendor specific ZRC data. TI use the first byte of the payload to indicate the specific TI protocol. For Audio, add:

#define RTI_PROTOCOL_RAS            0x50

Driver[edit]

USB[edit]

In zrc_usb.h

  1. Add audio interface enumeration macros
  2. 
    #ifdef VOICE_STREAM
    #define INTERFACE_NUMBER_AUDIO_CONTROL      0x02
    #define INTERFACE_NUMBER_AUDIO_STREAM       0x03
    #define INTERFACE_NUMBER_ZRC                0x04
    #else
    #define INTERFACE_NUMBER_ZRC                0x02
    #endif
    
    
  3. Modify endpoint macros to better reflect endpoint direction. Also update code to use these new macros.
  4. 
    //#define ZRC_PROXY_RNP_EP_OUT_ADDR        0x03
    //#define ZRC_PROXY_RNP_EP_IN_ADDR         ZRC_PROXY_RNP_EP_OUT_ADDR
    //#define ZRC_PROXY_ZRC_EP_OUT_ADDR        0x04
    //#define ZRC_PROXY_ZRC_EP_IN_ADDR         0x05
    
    #define CE_ENPOINT_ADDR                 0x02  
    #define RNP_ENPOINT_ADDR                0x03  
    #define ZRC_ENPOINT_ADDR                0x04  
    #define AUD_ENPOINT_ADDR                0x05  
    
    #define IN_ENPOINT_ADDR                 0x80
    #define OUT_ENPOINT_ADDR                0x00
    
    #define CE_EP_IN_ADDR                   IN_ENPOINT_ADDR  | CE_ENPOINT_ADDR 
    #define CE_EP_OUT_ADDR                  OUT_ENPOINT_ADDR | CE_ENPOINT_ADDR
    #define RNP_EP_IN_ADDR                  IN_ENPOINT_ADDR  | RNP_ENPOINT_ADDR
    #define RNP_EP_OUT_ADDR                 OUT_ENPOINT_ADDR | RNP_ENPOINT_ADDR
    #define ZRC_EP_IN_ADDR                  IN_ENPOINT_ADDR  | ZRC_ENPOINT_ADDR
    #define ZRC_EP_OUT_ADDR                 OUT_ENPOINT_ADDR | ZRC_ENPOINT_ADDR
    #define AUDIO_EP_IN_ADDR                IN_ENPOINT_ADDR  | AUD_ENPOINT_ADDR
    #define AUDIO_EP_OUT_ADDR               OUT_ENPOINT_ADDR | AUD_ENPOINT_ADDR
    #define RNP_EP_OUT_PACKET_LEN           ZRC_DONGLE_OUTBUF_SIZE
    
    

In zrc_usb.c

  1. Update macros to account for the extended USB descriptor, now that audio is added
  2. 
    #ifdef VOICE_STREAM
    #define ZRC_USB_NUM_INTERFACES                5
    #else
    #define ZRC_USB_NUM_INTERFACES                3
    #endif
    ...
    #ifdef VOICE_STREAM
    #define DBLBUF_LUT_INFO_SIZE                 (sizeof(DBLBUF_LUT_INFO) * 6)
    #else //VOICE_STREAM
    #define DBLBUF_LUT_INFO_SIZE                 (sizeof(DBLBUF_LUT_INFO) * 3)
    #endif //VOICE_STREAM
    
    
  3. Add audio descriptors
  4. 
    #ifdef VOICE_STREAM
    // Interface Descriptor for audio control 
    const uint8 usb_iface_desc_audio_control[] = {
      DESC_SIZE_INTERFACE,            // bLength
      DESC_TYPE_INTERFACE,            // bDescriptorType
      INTERFACE_NUMBER_AUDIO_CONTROL, // bInterfaceNumber
      0x00,                           // bAlternateSetting (none)
      0x00,                           // bNumEndpoints (none)
      0x01,                           // bInterfaceClass (AUDIO)
      0x01,                           // bInterfaceSubClass (AUDIO CONTROL)
      0x00,                           // bInterfaceProcotol (N/A)
      0x00                            // iInterface
    };
    
    //Audio Descriptor: HEADER
    const uint8 usb_audio_desc_header[] = {
      DESC_SIZE_AUDIO_HEADER,         // bLength
      DESC_TYPE_CS_INTERFACE,         // bDescriptorType
      DESC_AUDIO_TYPE_HEADER,         // bDescriptorSubType
      0x01, 0x00,                     // bcdADC (1.0)
                                      // wTotalLength   //SIZE EQUAL TO SUM OF DESCRIPTOR of the audio control descriptor, including tis header: 
                                      //  == size of (usb_audio_desc_input_term) + 
                                      //  == size of (usb_audio_desc_feat_unit)  +
      0x27, 0x00,                     //  == size of (usb_audio_desc_out_term) 
      0x01,                           // bInCollection (1 stream interface)
      0x03,                           // bInterfaceNr (interface 4 is stream)
    };
    
    //Audio Descriptor: INPUT TERMINAL
    const uint8 usb_audio_desc_input_term[] = {
      DESC_SIZE_AUDIO_INPUT_TERM,     // bLength
      DESC_TYPE_CS_INTERFACE,         // bDescriptorType
      DESC_AUDIO_TYPE_INPUT_TERM,     // bDescriptorSubType
      0x01,                           // bTerminalID
      0x01, 0x02,                     // wTerminalType
      0x00,                           // bAssocTerminal (none)
      0x01,                           // bNrChannel
      0x00, 0x00,                     // wChannelConfig //No predifined spatial position
      0x00,                           //iChannelNames
      0x00,                           //iTerminal (none)
    };
    
    //Audio Descriptor: FEATURE UNIT
    const uint8 usb_audio_desc_feat_unit[] = {
      DESC_SIZE_AUDIO_FEAT_UNIT,  // bLength (9)
      DESC_TYPE_CS_INTERFACE,     // bDescriptorType (CS_INTERFACE)
      DESC_AUDIO_TYPE_FEAT_UNIT,  // bDescriptorSubtype (FEATURE_UNIT)
      0x02,                       // bUnitID (2)
      0x01,                       // bSourceID (input terminal 1)
      0x01,                       // bControlSize (2 bytes)
      0x03, 0x00,                 // Master controls (volume and mute supported)
      0x00                        // iFeature (none)
    };
    
    //Audio Descriptor: OUTPUT TERMINAL
    const uint8 usb_audio_desc_out_term[] = {
      DESC_SIZE_AUDIO_OUTPUT_TERM,    // bLength
      DESC_TYPE_CS_INTERFACE,         // bDescriptorType
      DESC_AUDIO_TYPE_OUTPUT_TERM,    // bDescriptorSubType
      0x03,                           // bTerminalID
      0x01, 0x01,                     // wTerminalType
      0x00,                           // bAssocTerminal (none)
      0x02,                           // bSourceID (feature unit 2)
      0x00                            // iTerminal (none)
    };
    
    // Interface Descriptor for audio stream 
    const uint8 usb_iface_desc_audio_stream0[] = {
      DESC_SIZE_INTERFACE,            // bLength
      DESC_TYPE_INTERFACE,            // bDescriptorType
      INTERFACE_NUMBER_AUDIO_STREAM , // bInterfaceNumber
      0x00,                           // bAlternateSetting (0)
      0x00,                           // bNumEndpoints (none)
      0x01,                           // bInterfaceClass (AUDIO)
      0x02,                           // bInterfaceSubClass (AUDIO STREAM)
      0x00,                           // bInterfaceProcotol (N/A)
      0x00                            // iInterface
    };
    
    // Interface Descriptor for audio stream, ALTERNATE SETTING 
    const uint8 usb_iface_desc_audio_stream1[] = {
      DESC_SIZE_INTERFACE,            // bLength
      DESC_TYPE_INTERFACE,            // bDescriptorType
      INTERFACE_NUMBER_AUDIO_STREAM , // bInterfaceNumber
      0x01,                           // bAlternateSetting (1)
      0x01,                           // bNumEndpoints (1)
      0x01,                           // bInterfaceClass (AUDIO)
      0x02,                           // bInterfaceSubClass (AUDIO STREAM)
      0x00,                           // bInterfaceProcotol (N/A)
      0x00                            // iInterface
    };
    
    //Audio Descriptor: aduio Stream, general
    const uint8 usb_audio_desc_AS_general[] = {
      DESC_SIZE_AUDIO_STREAM,         // bLength
      DESC_TYPE_CS_INTERFACE,         // bDescriptorType
      DESC_AUDIO_TYPE_GENERAL,        // bDescriptorSubType
      0x03,                           // bTerminalLink (terminal 3)
      0x00,                           // bDelay (none)
      0x01, 0x00                      // wFormatTag (PCM format)
    };
    
    //Audio Descriptor: FORMAT TYPE
    const uint8 usb_audio_desc_AS_format_type[] = {
      DESC_SIZE_AUDIO_FORMAT_TYPEI,       // bLength
      DESC_TYPE_CS_INTERFACE,             // bDescriptorType
      DESC_AUDIO_TYPE_FORMAT_TYPE,        // bDescriptorSubType
      0x01,                               // bFormatType (TYPE_I)
      0x01,                               // bNrChannels (1)
      0x02,                               // bSubFrameSize (2)
      0x10,                               // bBitResolution (16)
      0x01,                               // bSamFreqType (1 sampling frequency)
      0x80,                               // 16,000 Hz (byte 0)
      0x3E,                               // 16,000 Hz (byte 1)
      0x00                                // 16,000 Hz (byte 2)
    };
    
    //Audio Descriptor: Audio Stream endpoint
    const uint8 usb_audio_desc_AS_endpoint[] = {
      DESC_SIZE_AUDIO_ENDPOINT,       // bLength
      DESC_TYPE_ENDPOINT,             // bDescriptorType
      AUDIO_EP_IN_ADDR,               // bEndpointAddress (EP1 in)
      0x05,                           // bmAttributes (isochronous + asynchronous)
      0x20, 0x00,                     // wMaxPacketSize (256)
      0x01,                           // bInterval (1 millisecond)
      0x00,                           // bRefresh (0)
      0x00                            // bSynchAddress (no synchronization) 
    };
    
    //Audio Descriptor: Isochronous endpoint
    const uint8 usb_audio_desc_AS_isoendpoint[] = {
      DESC_SIZE_AUDIO_ISO_ENDPOINT,   // bLength
      DESC_TYPE_CS_ENDPOINT,          // bDescriptorType
      DESC_AUDIO_TYPE_EP_GENERAL,     // bDescriptorSubType
      0x00,                           // bmAttributes (none)
      0x02,                           // bLockDelayUnits (PCM samples)
      0x00, 0x00                      // wLockDelay (0)
     };
    #endif 
    
    
  5. Update Product String
  6. 
    const uint8 string2Product[] = {
    #ifdef VOICE_STREAM
      38,                          // bLength
    #else
      30,						   // bLength
    #endif
      DESC_TYPE_STRING,            // bDescriptorType
      'U', 0,                      // unicode string
    ...
      'C', 0,
    #ifdef VOICE_STREAM
      ' ', 0,
      'A', 0,
      'U', 0,
      'D', 0
    #endif
    };
    
    
  7. Update all macros to define endpoint enumeration, and also update descriptors to use it
  8.    ZRC_PROXY_RNP_EP_OUT_ADDR
    
    -->
       RNP_EP_OUT_ADDR
    
       ZRC_PROXY_RNP_EP_IN_ADDR
    
    -->
       RNP_EP_IN_ADDR
    
       ZRC_PROXY_ZRC_EP_OUT_ADDR
    
    -->
       ZRC_EP_OUT_ADDR
    
       ZRC_PROXY_ZRC_EP_IN_ADDR
    
    -->
       ZRC_EP_IN_ADDR
    
    
    -  0x85,                        // bEndpointAddress
    +  ZRC_EP_IN_ADDR,              // bEndpointAddress
    ...
    -  0x04,                        // bEndpointAddress
    +  ZRC_EP_OUT_ADDR,             // bEndpointAddress
    ...
    -  0x83,                        // bEndpointAddress
    +  RNP_EP_IN_ADDR,              // bEndpointAddress
    ...
    -  0x82,                        // bEndpointAddress
    +  CE_EP_IN_ADDR,               // bEndpointAddress
    
    
  9. Add USB audio descriptor components offset defines
  10. 
    #ifdef VOICE_STREAM
    #define ZRC_AUD_CTRL_IFACE_DESC       (ZRC_CE_IN_EP_DESC                  +  DESC_SIZE_ENDPOINT)
    #define ZRC_AUD_CTRL_HDR_DESC         (ZRC_AUD_CTRL_IFACE_DESC          +  DESC_SIZE_INTERFACE)
    #define ZRC_AUD_CTRL_IN_TERM_DESC     (ZRC_AUD_CTRL_HDR_DESC            +  DESC_SIZE_AUDIO_HEADER)
    #define ZRC_AUD_CTRL_FEAT_DESC        (ZRC_AUD_CTRL_IN_TERM_DESC        +  DESC_SIZE_AUDIO_INPUT_TERM)
    #define ZRC_AUD_CTRL_OUT_TERM_DESC    (ZRC_AUD_CTRL_FEAT_DESC           +  DESC_SIZE_AUDIO_FEAT_UNIT)
    //#define ZRC_AUD_CTRL_OUT_TERM_DESC    (ZRC_AUD_CTRL_IN_TERM_DESC           +  DESC_SIZE_AUDIO_INPUT_TERM)
    #define ZRC_AUD_STREAM_IFACE0_DESC    (ZRC_AUD_CTRL_OUT_TERM_DESC       +  DESC_SIZE_AUDIO_OUTPUT_TERM)
    #define ZRC_AUD_STREAM_IFACE1_DESC    (ZRC_AUD_STREAM_IFACE0_DESC       +  DESC_SIZE_INTERFACE)
    #define ZRC_AUD_STREAM_GENERAL_DESC   (ZRC_AUD_STREAM_IFACE1_DESC       +  DESC_SIZE_INTERFACE)
    #define ZRC_AUD_STREAM_FORMAT_DESC    (ZRC_AUD_STREAM_GENERAL_DESC      +  DESC_SIZE_AUDIO_STREAM)
    #define ZRC_AUD_STREAM_EP_DESC        (ZRC_AUD_STREAM_FORMAT_DESC       +  DESC_SIZE_AUDIO_FORMAT_TYPEI)
    #define ZRC_AUD_STREAM_ISO_EP_DESC    (ZRC_AUD_STREAM_EP_DESC           +  DESC_SIZE_AUDIO_ENDPOINT)
    #define ZRC_IFACE_DESC                (ZRC_AUD_STREAM_ISO_EP_DESC       +  DESC_SIZE_AUDIO_ISO_ENDPOINT)
    #else //VOICE STREAM
    #define ZRC_IFACE_DESC                (ZRC_CE_IN_EP_DESC       +  DESC_SIZE_AUDIO_ISO_ENDPOINT)
    #endif //VOICE STREAM
    
    
  11. Add to total length calculation
  12. 
    #ifdef VOICE_STREAM  
      // Add Audio class descriptor to the total length.
      wTotalLength += DESC_SIZE_INTERFACE; // Audio Control interface
      wTotalLength += DESC_SIZE_AUDIO_HEADER; // Audio Control Header descriptor
      wTotalLength += DESC_SIZE_AUDIO_INPUT_TERM; // Audio Control input terminal descriptor
      wTotalLength += DESC_SIZE_AUDIO_FEAT_UNIT; // Audio Control feature unit descriptor
      wTotalLength += DESC_SIZE_AUDIO_OUTPUT_TERM; // Audio Control output terminal descriptor
      wTotalLength += DESC_SIZE_INTERFACE; // Audio Stream interface, alternate 0
      wTotalLength += DESC_SIZE_INTERFACE; // Audio Stream interface, alternate 1
      wTotalLength += DESC_SIZE_AUDIO_STREAM; // Audio Stream, general descriptor
      wTotalLength += DESC_SIZE_AUDIO_FORMAT_TYPEI; // Audio Stream, format type  descriptor
      wTotalLength += DESC_SIZE_AUDIO_ENDPOINT; // Audio Stream, endpoint  descriptor
      wTotalLength += DESC_SIZE_AUDIO_ISO_ENDPOINT; // Audio Stream, isochronous endpoint  descriptor
    #endif  
    
    
  13. Add interfaces to the dynamically built descriptor
  14. 
    #ifdef VOICE_STREAM  
    //Add  Voice descriptor and interface
       ptr = osal_memcpy(ptr, usb_iface_desc_audio_control,    sizeof(usb_iface_desc_audio_control));
       ptr = osal_memcpy(ptr, usb_audio_desc_header,           sizeof(usb_audio_desc_header));
       ptr = osal_memcpy(ptr, usb_audio_desc_input_term,       sizeof(usb_audio_desc_input_term));
       ptr = osal_memcpy(ptr, usb_audio_desc_feat_unit,        sizeof(usb_audio_desc_feat_unit));
       ptr = osal_memcpy(ptr, usb_audio_desc_out_term,         sizeof(usb_audio_desc_out_term));
       ptr = osal_memcpy(ptr, usb_iface_desc_audio_stream0,    sizeof(usb_iface_desc_audio_stream0));
       ptr = osal_memcpy(ptr, usb_iface_desc_audio_stream1,    sizeof(usb_iface_desc_audio_stream1));
       ptr = osal_memcpy(ptr, usb_audio_desc_AS_general,       sizeof(usb_audio_desc_AS_general));
       ptr = osal_memcpy(ptr, usb_audio_desc_AS_format_type,   sizeof(usb_audio_desc_AS_format_type));
       ptr = osal_memcpy(ptr, usb_audio_desc_AS_endpoint,      sizeof(usb_audio_desc_AS_endpoint));
       ptr = osal_memcpy(ptr, usb_audio_desc_AS_isoendpoint,   sizeof(usb_audio_desc_AS_isoendpoint));
    #endif
    
    
  15. There are two additional USB interfaces if VOICE_STREAM is defined
  16. 
    #ifdef VOICE_STREAM  
       *ptr = 4; // bNumInterfaces
    #else
       *ptr = 2; // bNumInterfaces
    #endif
    
    
  17. Add endpoint configuration for audio interfaces
  18. 
    #ifdef VOICE_STREAM
      usbDescriptorMarker.pUsbDblbufLut[2].pInterface = (USB_INTERFACE_DESCRIPTOR *)ZRC_AUD_CTRL_IFACE_DESC;
      usbDescriptorMarker.pUsbDblbufLut[2].inMask = 0;
      usbDescriptorMarker.pUsbDblbufLut[2].outMask = 0;
    
      usbDescriptorMarker.pUsbDblbufLut[3].pInterface = (USB_INTERFACE_DESCRIPTOR *)ZRC_AUD_STREAM_IFACE0_DESC;
      usbDescriptorMarker.pUsbDblbufLut[3].inMask = 0;
      usbDescriptorMarker.pUsbDblbufLut[3].outMask = 0;
    
      usbDescriptorMarker.pUsbDblbufLut[4].pInterface = (USB_INTERFACE_DESCRIPTOR *)ZRC_AUD_STREAM_IFACE1_DESC;
      usbDescriptorMarker.pUsbDblbufLut[4].inMask = (1<<AUD_ENPOINT_ADDR ); //0x02; // Set EP1 to double buffering on IN
      usbDescriptorMarker.pUsbDblbufLut[4].outMask = 0;
    
      usbDescriptorMarker.pUsbDblbufLut[5].pInterface = (USB_INTERFACE_DESCRIPTOR *)ZRC_IFACE_DESC;
      usbDescriptorMarker.pUsbDblbufLut[5].inMask =  0; 
      usbDescriptorMarker.pUsbDblbufLut[5].outMask = 0;
    #else
      usbDescriptorMarker.pUsbDblbufLut[2].pInterface = (USB_INTERFACE_DESCRIPTOR *)ZRC_IFACE_DESC;
      usbDescriptorMarker.pUsbDblbufLut[2].inMask =  0; 
      usbDescriptorMarker.pUsbDblbufLut[2].outMask = 0;
    #endif
    
    

In usb_zrc.h

  1. Add audio events
  2. 
    #ifdef VOICE_STREAM
    #define AUDIOD_CMD_START        0x04
    #define AUDIOD_CMD_STOP         0x08
    #define AUDIOD_STREAM_STARTED   0x10
    #define AUDIOD_STREAM_REQUESTED 0x20
    #endif
    
    

In the usb_zrc_class_request.c

  1. Add USB audio class request handles
  2. 
    #if defined VOICE_STREAM
    uint8 audioCVolumeMin[2]={0x00,0x80};
    uint8 audioCVolumeMax[2]={0x00,0x06};
    uint8 audioCVolumeRes[2]={0x00,0x01};
    uint8 audioCVolumeCur[2]={0x00,0x00};
    
    uint8 audioMuteValue = 0;
    
    void usbcrGetMin(void) 
    {
      if (usbfwData.ep0Status == EP_IDLE) 
      {
        // Is the length stall correct?
        if ( (usbSetupHeader.length == 0) || (HI_UINT16(usbSetupHeader.index) > 2)) 
        {
          usbfwData.ep0Status = EP_STALL;
        } 
        else 
        {
          switch (HI_UINT16(usbSetupHeader.value)) 
          {
            
          case 2: //Volume
            usbSetupData.pBuffer = (uint8 *)&audioCVolumeMin;
            usbSetupData.bytesLeft = 2;
            usbfwData.ep0Status = EP_TX;
            break;
          default:
            usbfwData.ep0Status = EP_STALL;
            break;
          }
        }
      }
    }// usbcrGetReport
    
    void usbcrGetMax(void) 
    {
      if (usbfwData.ep0Status == EP_IDLE) 
      {
        // Is the length stall correct?
        if ( (usbSetupHeader.length == 0) || (HI_UINT16(usbSetupHeader.index) > 2)) 
        {
          usbfwData.ep0Status = EP_STALL;
        } 
        else 
        {
          switch (HI_UINT16(usbSetupHeader.value)) 
          {  
          case 2: //Volume
            usbSetupData.pBuffer = (uint8 *)&audioCVolumeMax;
            usbSetupData.bytesLeft = 2;
            usbfwData.ep0Status = EP_TX;
            break;
          default:
            usbfwData.ep0Status = EP_STALL;
            break;  
          }
        }
      }
    }// usbcrGetReport
    
    void usbcrGetRes(void) 
    {
      if (usbfwData.ep0Status == EP_IDLE) 
      {
        // Is the length stall correct?
        if ( (usbSetupHeader.length == 0) || (HI_UINT16(usbSetupHeader.index) > 2)) 
        {
          usbfwData.ep0Status = EP_STALL;
        } else 
        {
          switch (HI_UINT16(usbSetupHeader.value)) 
          {
          case 2:
            usbSetupData.pBuffer = (uint8 *)&audioCVolumeRes;
            usbSetupData.bytesLeft = 2;
            usbfwData.ep0Status = EP_TX;
            break;
          default:
            usbfwData.ep0Status = EP_STALL;
            break;  
          }
        }
      }
    }// usbcrGetReport
    
    
    void usbcrGetCur(void) 
    {
      if (usbfwData.ep0Status == EP_IDLE) 
      {
        // Is the length stall correct?
        if ( (usbSetupHeader.length == 0) || (HI_UINT16(usbSetupHeader.index) > 2)) 
        {
          usbfwData.ep0Status = EP_STALL;
        } 
        else 
        {
          switch (HI_UINT16(usbSetupHeader.value)) 
          {  
          case 1:
            usbSetupData.pBuffer = (uint8 *)&audioMuteValue;
            usbSetupData.bytesLeft = 1;
            usbfwData.ep0Status = EP_TX;
            break;
          case 2:
            usbSetupData.pBuffer = (uint8 *)&audioCVolumeCur;
            usbSetupData.bytesLeft = 2;
            usbfwData.ep0Status = EP_TX;
            break;
          default:
            usbfwData.ep0Status = EP_STALL;
            break;  
          }
        }
      }
    }// usbcrGetReport
    
    uint8 *pVolumeCur;
    uint8 *pMuteCur;
    
    void usbcrSetCur(void) 
    {
      // Received header
      if (usbfwData.ep0Status == EP_IDLE) 
      {
        // Is the length stall correct?
        if ( (usbSetupHeader.length == 0) || (HI_UINT16(usbSetupHeader.index) > 2)) 
        {
          usbfwData.ep0Status = EP_STALL;
        } 
        else 
        {
          switch (HI_UINT16(usbSetupHeader.value)) 
          {  
          case 1: //Mute
            pMuteCur = (uint8 *)osal_mem_alloc(usbSetupHeader.length);
            
            usbfwData.ep0Status = EP_RX;
            
            usbSetupData.bytesLeft = usbSetupHeader.length;
            usbSetupData.pBuffer = pMuteCur;
            break;
          case 2: //Volume
            pVolumeCur = (uint8 *)osal_mem_alloc(usbSetupHeader.length);
            
            usbfwData.ep0Status = EP_RX;
            
            usbSetupData.bytesLeft = usbSetupHeader.length;
            usbSetupData.pBuffer = pVolumeCur;
            break;
          default:
            usbfwData.ep0Status = EP_STALL;
            break;  
          }
        }
      }
      else if (usbfwData.ep0Status == EP_RX) 
      {
        switch (HI_UINT16(usbSetupHeader.value)) 
        {
        case 1: //Mute
          // Send OUT report up to Application
          //zrcPxyServeHIDClassRequests(usbSetupHeader.request, zrcClassRequestDataOut);
          audioMuteValue = *pMuteCur;
          osal_mem_free(pMuteCur);
          break;
        case 2: //Volume
          // Send OUT report up to Application
          //zrcPxyServeHIDClassRequests(usbSetupHeader.request, zrcClassRequestDataOut);
          audioCVolumeCur[0] = pVolumeCur[0];
          audioCVolumeCur[1] = pVolumeCur[1];
          osal_mem_free(pVolumeCur);
          break;
        default:
          break;
        }
      }
    }// usbcrSetReport
    
    uint8 *pVolumeRes;
    void usbcrSetRes(void)
    {
      // Received header
      if (usbfwData.ep0Status == EP_IDLE) 
      {
        // Is the length stall correct?
        if ( (usbSetupHeader.length == 0) || (HI_UINT16(usbSetupHeader.index) > 2)) 
        {
          usbfwData.ep0Status = EP_STALL;
        } 
        else 
        {
          switch (HI_UINT16(usbSetupHeader.value)) 
          {  
            //              case 2: //Volume
            //#ifdef IO_BENCHMARK
            //              HAL_BM_TURN_ON_IO1 (); //RF4CE task on
            //#endif  
            //                pVolumeRes = (uint8 *)osal_mem_alloc(usbSetupHeader.length);
            //                
            //                usbfwData.ep0Status = EP_RX;
            //
            //                usbSetupData.bytesLeft = usbSetupHeader.length;
            //                usbSetupData.pBuffer = pVolumeRes;
            //                break;
          default:
            usbfwData.ep0Status = EP_STALL;
            break;  
          }
        }
      }
      else if (usbfwData.ep0Status == EP_RX) 
      {
        // Send OUT report up to Application
        //zrcPxyServeHIDClassRequests(usbSetupHeader.request, zrcClassRequestDataOut);
        audioCVolumeRes[0] = pVolumeRes[0];
        audioCVolumeRes[1] = pVolumeRes[1];
        osal_mem_free(pVolumeRes);
      }
    }
    
    void usbcrSetMin(void) 
    {
      if (usbfwData.ep0Status == EP_IDLE) 
      {
        if ( (usbSetupHeader.value & 0xFFFE) || 
            (usbSetupHeader.length != 0) || 
              (usbSetupHeader.index > INTERFACE_NUMBER_AUDIO_STREAM)) 
        {
          usbfwData.ep0Status = EP_STALL;
        } 
        else 
        {
        }
      }
    }// usbcrSetProtocol
    #endif
    
    

In usb_zrc_class_requests.h

  1. Add audio class requests macros
  2. 
    #ifdef VOICE_STREAM
    // Audio Request Codes
    #define GET_CUR  0x81                  // Code for Get Current Value
    #define GET_MIN  0x82
    #define GET_MAX  0x83
    #define GET_RES  0x84
    #define SET_CUR  0x01                  // Code for Get Current Value
    #define SET_MIN  0x02
    #define SET_MAX  0x03
    #define SET_RES  0x04
    #endif
    
    
  3. And add audio class requests function prototypes
  4. 
    #ifdef VOICE_STREAM
    void usbcrSetCur(void);
    void usbcrSetRes(void);
    void usbcrGetCur(void);
    void usbcrGetMin(void);
    void usbcrGetMax(void);
    void usbcrGetRes(void);
    #endif
    
    

In the usb_zrc_hooks.c

  1. Add include files
  2. 
    #ifdef VOICE_STREAM
    #include "usb_zrc.h"
    #include "zrc_common.h"
    #include "zrc_usb.h"
    #endif
    
    
  3. In usbcrHookProcessOut()
  4. 
    #ifdef VOICE_STREAM    
         case SET_CUR:
           usbcrSetCur(); 
           break;
         case SET_RES:
           usbcrSetRes(); 
           break;
    #endif  //VOICE_STREAM   
    
    
  5. In usbcrHookProcessIn()
  6. 
    #ifdef VOICE_STREAM    
         case GET_CUR:
           usbcrGetCur(); break;
         case GET_MIN:
           usbcrGetMin(); break;
         case GET_MAX:
           usbcrGetMax(); break;
         case GET_RES:
           usbcrGetRes(); break;
    #endif   //VOICE_STREAM 
    
    
  7. Define start and stop streaming functions as extern hooks. These are defined in usb_aud.c
  8. 
    #ifdef VOICE_STREAM
    //extern void InitBuffer(void);
    extern void StartAudioStreamingCommand(void);
    extern void StopAudioStreamingCommand(void);
    #endif
    
    // ************************ USB standard request event processing *************************
    void usbsrHookProcessEvent(uint8 event, uint8 index)
    {
    ...
       // Process relevant events, one at a time.
       switch (event) {
     ...
    #ifdef VOICE_STREAM
         // interface 3 and alternate Setting 1 ==> start audio
         if ( (index == INTERFACE_NUMBER_AUDIO_STREAM) && (usbfwData.pAlternateSetting[usbSetupHeader.index] == 1) )
         {
           StartAudioStreamingCommand();
         }
         else if ( (index == INTERFACE_NUMBER_AUDIO_STREAM) && (usbfwData.pAlternateSetting[usbSetupHeader.index] == 0) )
         {
           StopAudioStreamingCommand();
         }
    #endif
    
    

In usb_descriptor.h

  1. Add audio descriptor macros
  2. 
    #define DESC_TYPE_CS_INTERFACE    0x24
    #define DESC_TYPE_CS_ENDPOINT     0x25
    #define DESC_AUDIO_TYPE_HEADER        0x01
    #define DESC_AUDIO_TYPE_INPUT_TERM    0x02
    #define DESC_AUDIO_TYPE_OUTPUT_TERM   0x03
    #define DESC_AUDIO_TYPE_FEAT_UNIT     0x06
    
    //Audio Stream
    #define DESC_AUDIO_TYPE_GENERAL       0x01
    #define DESC_AUDIO_TYPE_FORMAT_TYPE   0x02
    
    #define DESC_AUDIO_TYPE_EP_GENERAL    0x01
       
    #define DESC_SIZE_DEVICE              0x12
    #define DESC_SIZE_CONFIG              0x09
    #define DESC_SIZE_INTERFACE           0x09
    #define DESC_SIZE_ENDPOINT            0x07
    #define DESC_SIZE_AUDIO_HEADER        0x09
    #define DESC_SIZE_AUDIO_INPUT_TERM    0x0C
    #define DESC_SIZE_AUDIO_OUTPUT_TERM   0x09
    #define DESC_SIZE_AUDIO_FEAT_UNIT     0x09
    #define DESC_SIZE_AUDIO_STREAM        0x07
    #define DESC_SIZE_AUDIO_FORMAT_TYPEI  0x0B //for one discrete Fs
    #define DESC_SIZE_AUDIO_ENDPOINT      0x09
    #define DESC_SIZE_AUDIO_ISO_ENDPOINT  0x07
    
    

In usb_interrupt.c

  1. Add files to include
  2. 
    #ifdef VOICE_STREAM
    #include "usb_aud.h"
    #endif
    
    
  3. Add hooks for isochronous interrupt service routines, implemented in usb_aud.c
  4. 
    #ifdef VOICE_STREAM
    extern void usbIsoInIsr(void);
    extern void usbSofIsr(void);
    #endif
    
    
  5. In usbirqHandler() add #ifdef VOICE_STREAM uint8 usbiif; #endif ... #ifdef VOICE_STREAM usbiif = USBIIF; #endif ... #ifdef VOICE_STREAM if (usbcif & USBCIF_SOFIF) { usbSofIsr(); } if ( (usbiif & (1<<AUDIO_ENPOINT_ADDR) /*USBIIF_INEP1IF*/) ) //|| (usbcif & USBCIF_SOFIF) { // Handle Audio stream usbIsoInIsr(); if (usbiif & (1<<AUDIO_ENPOINT_ADDR)) { usbiif &= ~((1<<AUDIO_ENPOINT_ADDR)); } } #endif eventMask = usbcif; #ifdef VOICE_STREAM eventMask |= (uint16)usbiif << 4; #else eventMask |= (uint16)USBIIF << 4; #endif
DMA[edit]

In the DMA driver file hal_dma.c, add hooks for the audio library.

  1. Include header
  2. 
    #if (defined HAL_USB_DMA) && (HAL_USB_DMA == TRUE)
    #include "usb_aud.h"
    #endif
    
    
  3. Add HAL_USB_DMA to precompiler clause to enable DMA interrupt, and to implement interrupt handler
  4. 
    void HalDmaInit( void )
    {
    

    ...

    #if (HAL_UART_DMA || \
      ((defined HAL_SPI) && (HAL_SPI == TRUE))  || \
      ((defined HAL_USB_DMA) && (HAL_USB_DMA == TRUE)) || \
      ((defined HAL_IRGEN) && (HAL_IRGEN == TRUE)))
     DMAIE = 1;
    #endif
    }
    
    #if (HAL_UART_DMA || \
       ((defined HAL_SPI) && (HAL_SPI == TRUE))  || \
      ((defined HAL_USB_DMA) && (HAL_USB_DMA == TRUE)) || \
       ((defined HAL_IRGEN) && (HAL_IRGEN == TRUE)))
    /******************************************************************************
     * @fn      HalDMAInit
    
    
  5. Add call to usb audio driver usb_aud.c DMA interrupt handler
  6. 
    #if (defined HAL_USB_DMA) && (HAL_USB_DMA == TRUE)
      if ( HAL_DMA_CHECK_IRQ( HAL_DMA_CH_RX ) )
      {
        usbDmaIsr();
        HAL_DMA_CLEAR_IRQ( HAL_DMA_CH_RX );
      }
    #endif
    
    
  7. Add macro to check if DMA has started in hal_dma.h
  8. 
    #define HAL_DMA_CHECK_STARTED( ch )   (DMAREQ & ( 1 << (ch) ))