//------------------------------------------------------------------------------
// Copyright (c) 2012 by Silicon Laboratories. 
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Silicon Laboratories End User 
// License Agreement which accompanies this distribution, and is available at
// http://developer.silabs.com/legal/version/v10/License_Agreement_v10.htm
// Original content and implementation provided by Silicon Laboratories.
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
// Includes
//------------------------------------------------------------------------------

#include "myUsbAudioDevice.h"

// The control plane thread is responsible for volume control.
#include "myControlPlane.h"

// The data plane thread is responsible for audio playback and recording.
// The data plane out thread communicates via myDataPlaneOutEp.
#include "myDataPlane.h"

#define INCLUDE_MIC 1

si32UsbDeviceDescriptorType myUsbAudioDeviceDescriptor =
{
  .bcdUsb              = 0x0200,
  .bDeviceClass        = 0x00,
  .bDeviceSubclass     = 0x00,
  .bDeviceProtocol     = 0x00,
  .bMaxPacketSize0     = 64,
  .wIdVendor           = USB_VENDOR_ID_SILICON_LABS,
  .wIdProduct          = 0xff5a,
  .bcdDevice           = 0x0100,
  .bIndexManufacturer  = 0x01,
  .bIndexProduct       = 0x02,
  .bIndexSerialNumber  = 0x03,
  .bNumConfigurations  = 0x01
} ;

si32UsbConfigurationDescriptorType  myUsbAudioConfigurationDescriptor =
{
  .bConfigurationValue = 1,
  .bMaxPower           = 0x32,
  .bmAttributes        = 0x80,
  .bIndexConfiguration = 1,
};

si32UsbInterfaceDescriptorType      myUsbAudioControlInterfaceDescriptor =
{
  .bAlternateSetting    = 0x00,
  .bNumEndpoints        = 0x00,
  .bInterfaceClass      = USB_AUDIO,
  .bInterfaceSubClass   = USB_AUDIO_CONTROL,
  .bInterfaceProtocol   = SI32_BASIC_AUDIO_DEVICE_S_HS_HS1,
  .iInterface           = 0x00
};

si32UsbInterfaceDescriptorType  myUsbAudioStreamingOutInterfaceDescriptor[2] =
{
  {
    .bInterfaceNumber     = 1,
    .bAlternateSetting    = 0x00,
    .bNumEndpoints        = 0x00,
    .bInterfaceClass      = USB_AUDIO,
    .bInterfaceSubClass   = USB_AUDIO_STREAMING,
    .iInterface           = 0x00
  },
  {
    .bInterfaceNumber     = 1,
    .bAlternateSetting    = 0x01,
    .bNumEndpoints        = 0x00,
    .bInterfaceClass      = USB_AUDIO,
    .bInterfaceSubClass   = USB_AUDIO_STREAMING,
    .iInterface           = 0x00
  }
} ;

si32AudioUsbIsoEndpointDescriptorType myUsbAudioStreamingEndpointOutDescriptor =
{
  .bEndpointAddress       = 0x03,
  .bmAttributes           = USB_EP_ATTRIBUTES_TYPE_ISO | USB_EP_ATTRIBUTES_ISO_SYNCHRONOUS,
  .wMaxPacketSize         = 192,
  .bInterval              = 1,
  .bRefresh               = 0,
  .bSynchAddress          = 0
};


si32UsbInterfaceDescriptorType myUsbAudioStreamingInInterfaceDescriptor[2] =
{
  {
    .bInterfaceNumber     = 2,
    .bAlternateSetting    = 0x00,
    .bNumEndpoints        = 0x00,
    .bInterfaceClass      = USB_AUDIO,
    .bInterfaceSubClass   = USB_AUDIO_STREAMING,
    .iInterface           = 0x00
  },
  {
    .bInterfaceNumber     = 2,
    .bAlternateSetting    = 0x01,
    .bNumEndpoints        = 0x00,
    .bInterfaceClass      = USB_AUDIO,
    .bInterfaceSubClass   = USB_AUDIO_STREAMING,
    .iInterface           = 0x00
  }
};

si32AudioUsbIsoEndpointDescriptorType myUsbAudioStreamingEndpointInDescriptor =
{
  .bEndpointAddress       = 0x84,
  .bmAttributes           = USB_EP_ATTRIBUTES_TYPE_ISO | USB_EP_ATTRIBUTES_ISO_SYNCHRONOUS,
  .wMaxPacketSize         = 96,
  .bInterval              = 1,
  .bRefresh               = 0,
  .bSynchAddress          = 0
};



// define the string table language IDs
si32UsbStringDescriptorType myUsbAudioStringLanguageIDs =
  _USB_STRING_DESCRIPTOR_STATIC_INIT( ((uint16_t [])
  {
    USB_LANGUAGE_ID_ENGLISH_UNITED_STATES
  }));


// define the string table for language id 0 (english canadian)
si32UsbStringDescriptorType * myUsbAudioStringsEnglishUnitedStates[] =
{
  (si32UsbStringDescriptorType []) { _USB_STRING_DESCRIPTOR_STATIC_INIT( (uint16_t []) { USB_LANGUAGE_ID_ENGLISH_UNITED_STATES } ) } ,
  (si32UsbStringDescriptorType []) { _USB_STRING_DESCRIPTOR_STATIC_INIT_CSTRING( "Silicon Laboratories" ) } ,
  (si32UsbStringDescriptorType []) { _USB_STRING_DESCRIPTOR_STATIC_INIT_CSTRING( "USB Audio Class Device" ) },
  (si32UsbStringDescriptorType []) { _USB_STRING_DESCRIPTOR_STATIC_INIT_CSTRING( "0000000001" ) },
  (si32UsbStringDescriptorType []) { _USB_STRING_DESCRIPTOR_STATIC_INIT_CSTRING( "si32UsbAudioControl Interface" ) },
  (si32UsbStringDescriptorType []) { _USB_STRING_DESCRIPTOR_STATIC_INIT_CSTRING( "si32UsbAudioStreaming Input Interface" ) },
  (si32UsbStringDescriptorType []) { _USB_STRING_DESCRIPTOR_STATIC_INIT_CSTRING( "si32UsbAudioStreaming Output Interface" ) },
  NULL
} ;

// define the string table as aggregation of language IDs, and the string arrays
si32UsbStringTableType myUsbAudioStringTable =
{
  .LanguageIDs   = &myUsbAudioStringLanguageIDs,
  .LanguageArray = (si32UsbStringDescriptorType **[])
  {
    myUsbAudioStringsEnglishUnitedStates,
    NULL
  },
};



si32UsbDeviceObject           myUsbAudioDeviceObject        = si32UsbDeviceObject();
si32UsbConfigurationObject    myUsbAudioConfigurationObject = si32UsbConfigurationObject();
si32UsbConfigurationObject  * myUsbAudioConfigurationList[] = { &myUsbAudioConfigurationObject, NULL };






////////////////////////////////////////////////////////////////////////////////////////////////////
si32UsbAudioControlInterfaceObject myUsbAudioControlInterfaceObject             = si32UsbAudioControlInterfaceObject();
si32UsbAudioInterfaceObject        myUsbAudioControlSpeakerHeaderObject         = si32UsbAudioInterfaceObject();
si32UsbAudioInterfaceObject        myUsbAudioControlSpeakerUsbInputTerminalObject = si32UsbAudioInterfaceObject();
si32UsbAudioInterfaceObject        myUsbAudioControlSpeakerFeatureUnitObject    = si32UsbAudioInterfaceObject();
si32UsbAudioInterfaceObject        myUsbAudioControlSpeakerOutputTerminalObject = si32UsbAudioInterfaceObject();
si32UsbAudioInterfaceObject        myUsbAudioControlMicInputTerminalObject      = si32UsbAudioInterfaceObject();
si32UsbAudioInterfaceObject        myUsbAudioControlMicFeatureUnitObject        = si32UsbAudioInterfaceObject();
si32UsbAudioInterfaceObject        myUsbAudioControlMicUsbOutputTerminalObject  = si32UsbAudioInterfaceObject();

si32UsbAudioStreamingInterfaceObject myUsbAudioStreamingInterfaceOutObject_0    = si32UsbAudioStreamingInterfaceObject();
si32UsbAudioStreamingInterfaceObject myUsbAudioStreamingInterfaceOutObject_1    = si32UsbAudioStreamingInterfaceObject();
si32UsbAudioInterfaceObject          myUsbAudioStreamingGeneralOutObject        = si32UsbAudioInterfaceObject();
si32UsbAudioInterfaceObject          myUsbAudioStreamingFormatOutObject         = si32UsbAudioInterfaceObject();
si32UsbAudioStreamingEndpointObject  myUsbAudioStreamingEndpointOutObject       = si32UsbAudioStreamingEndpointObject();
si32UsbAudioEndpointObject           myUsbAudioStreamingEndpointGeneralOutObject = si32UsbAudioEndpointObject();

si32UsbAudioStreamingInterfaceObject myUsbAudioStreamingInterfaceInObject_0     = si32UsbAudioStreamingInterfaceObject();
si32UsbAudioStreamingInterfaceObject myUsbAudioStreamingInterfaceInObject_1     = si32UsbAudioStreamingInterfaceObject();
si32UsbAudioInterfaceObject          myUsbAudioInterfaceGeneralInObject         = si32UsbAudioInterfaceObject();
si32UsbAudioInterfaceObject          myUsbAudioInterfaceFormatInObject          = si32UsbAudioInterfaceObject();
si32UsbAudioStreamingEndpointObject  myUsbAudioStreamingEndpointInObject        = si32UsbAudioStreamingEndpointObject();
si32UsbAudioEndpointObject           myUsbAudioStreamingEndpointGeneralInObject = si32UsbAudioEndpointObject();



////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////



extern si32UsbAudioInputTeminalType                       AcUsbInputTerminalDescriptor;
extern si32UsbAudioOutputTerminalDescriptorType           AcStereoOutputTerminalDescriptor;
extern si32UsbAudioStereoFeatureUnitDescriptorType        AcStereoFeatureUnitDescriptor;
extern si32UsbAudioInputTeminalType                       myMicInputTerminalDescriptor ;
extern si32UsbAudioMonoFeatureUnitDescriptorType          myMicMonoFeatureUnitDescriptor ;
extern si32UsbAudioOutputTerminalDescriptorType           myMicUsbOutputTerminalDescriptor ;


#if INCLUDE_MIC
#define AUDIO_CONTROL_HEADER_TOTAL_LENGTH ( sizeof(AcHeaderDescriptor) \
                                          + sizeof(AcUsbInputTerminalDescriptor)  \
                                          + sizeof(AcStereoFeatureUnitDescriptor)  \
                                          + sizeof(AcStereoOutputTerminalDescriptor) \
                                          + sizeof(myMicInputTerminalDescriptor) \
                                          + sizeof(myMicMonoFeatureUnitDescriptor)  \
                                          + sizeof(myMicUsbOutputTerminalDescriptor) )
si32PackedHeadsetUsbAudioInterfaceHeaderHeadsetDescriptorType AcHeaderDescriptor =
{
    .bLength            = sizeof(AcHeaderDescriptor),
    .bDescriptorType    = USB_AUDIO_CS_INTERFACE,
    .bDescriptorSubtype = USB_AUDIO_AC_HEADER,
    .bcdACD             =  0x0100,
    .wTotalLength       =  AUDIO_CONTROL_HEADER_TOTAL_LENGTH,
    .bInCollection      =  2,
    .baInterface[0]     =  1,
    .baInterface[1]     =  2,
} ;
#else
#define AUDIO_CONTROL_HEADER_TOTAL_LENGTH ( sizeof(AcHeaderDescriptor) \
                                          + sizeof(AcUsbInputTerminalDescriptor)  \
                                          + sizeof(AcStereoFeatureUnitDescriptor)  \
                                          + sizeof(AcStereoOutputTerminalDescriptor))

si32PackedHeadphoneUsbAudioInterfaceHeaderDescriptorType      AcHeaderDescriptor =
{
    .bLength            = sizeof(AcHeaderDescriptor),
    .bDescriptorType    = USB_AUDIO_CS_INTERFACE,
    .bDescriptorSubtype = USB_AUDIO_AC_HEADER,
    .bcdACD             =  0x0100,
    .wTotalLength       =  AUDIO_CONTROL_HEADER_TOTAL_LENGTH+1,
    .bInCollection      =  1,
    .baInterface[0]     =  1,
};
#endif


si32UsbAudioInputTeminalType AcUsbInputTerminalDescriptor =
{
   .bLength             = sizeof(AcUsbInputTerminalDescriptor),
   .bDescriptorType     = USB_AUDIO_CS_INTERFACE,
   .bDescriptorSubtype  = USB_AUDIO_AC_INPUT_TERMINAL,
   .bTerminalID         = 1,
   .wTerminalType       = USB_Streaming,
   .bAssocTerminal      = 0,
   .bNrChannels         = 2,
   .wChannelConfig      = ( USB_AUDIO_CHANNEL_CONFIG_LEFT_FRONT |
                            USB_AUDIO_CHANNEL_CONFIG_RIGHT_FRONT ),
   .iChannelNames       = 0,
   .iTerminal           = 0
} ;

si32UsbAudioStereoFeatureUnitDescriptorType AcStereoFeatureUnitDescriptor =
{
    .bLength            = sizeof(AcStereoFeatureUnitDescriptor),
    .bDescriptorType    = USB_AUDIO_CS_INTERFACE,
    .bDescriptorSubtype = USB_AUDIO_AC_FEATURE_UNIT,
    .bUnitID            = 2,
    .bSourceID          = 1,
    .bControlSize       = 2,
    .bmaControls        =
     { [USB_AUDIO_CHANNEL_MASTER]      = AUDIO_FU_MUTE_CONTROL_MASK,
       [USB_AUDIO_CHANNEL_LEFT_FRONT]  = AUDIO_FU_VOLUME_CONTROL_MASK,
       [USB_AUDIO_CHANNEL_RIGHT_FRONT] = AUDIO_FU_VOLUME_CONTROL_MASK,
     },
    .iFeature           = 0,
};

si32UsbAudioOutputTerminalDescriptorType AcStereoOutputTerminalDescriptor =
{
   .bLength             = sizeof(AcStereoOutputTerminalDescriptor),
   .bDescriptorType     = USB_AUDIO_CS_INTERFACE,
   .bDescriptorSubtype  = USB_AUDIO_AC_OUTPUT_TERMINAL,
   .bTerminalID         = 3,
   .wTerminalType       = Output_Headphones,
   .bAssocTerminal      = 0,
   .iSourceID           = 2,
   .iTerminal           = 0,
} ;

#if INCLUDE_MIC

si32UsbAudioInputTeminalType myMicInputTerminalDescriptor =
{
   .bLength             = sizeof(myMicInputTerminalDescriptor),
   .bDescriptorType     = USB_AUDIO_CS_INTERFACE,
   .bDescriptorSubtype  = USB_AUDIO_AC_INPUT_TERMINAL,
   .bTerminalID         = 4,
   .wTerminalType       = Input_Microphone,
   .bAssocTerminal      = 0,
   .bNrChannels         = 1,
   .wChannelConfig      = ( USB_AUDIO_CHANNEL_CONFIG_CENTER_FRONT ),
   .iChannelNames       = 0,
   .iTerminal           = 0
} ;

si32UsbAudioMonoFeatureUnitDescriptorType myMicMonoFeatureUnitDescriptor =
{
    .bLength            = sizeof(myMicMonoFeatureUnitDescriptor),
    .bDescriptorType    = USB_AUDIO_CS_INTERFACE,
    .bDescriptorSubtype = USB_AUDIO_AC_FEATURE_UNIT,
    .bUnitID            = 5,
    .bSourceID          = 4,
    .bControlSize       = 2,
/*
    .bmaControls        =
     { [USB_AUDIO_CHANNEL_MASTER]  = AUDIO_FU_MUTE_CONTROL_MASK,
       // this looks odd, but it's how to define sequential channels, where
       // the first channel isn't FRONT_LEFT.  basically the actual first channel
       // becomes index '1', so this can be thought of as
       // _INDEX_OF(channel, first_channel) (channel-first_channel)+1
       [USB_AUDIO_CHANNEL_CENTER-USB_AUDIO_CHANNEL_CENTER+1]  = AUDIO_FU_VOLUME_CONTROL_MASK,
     },
*/
    .bmaControls[USB_AUDIO_CHANNEL_MASTER]       = AUDIO_FU_MUTE_CONTROL_MASK,
    .bmaControls[USB_AUDIO_CHANNEL_CENTER_FRONT-USB_AUDIO_CHANNEL_CENTER_FRONT+1]       = AUDIO_FU_VOLUME_CONTROL_MASK,

    .iFeature           = 0,
};

si32UsbAudioOutputTerminalDescriptorType myMicUsbOutputTerminalDescriptor =
{
   .bLength             = sizeof(myMicUsbOutputTerminalDescriptor),
   .bDescriptorType     = USB_AUDIO_CS_INTERFACE,
   .bDescriptorSubtype  = USB_AUDIO_AC_OUTPUT_TERMINAL,
   .bTerminalID         = 6,
   .wTerminalType       = USB_Streaming,
   .bAssocTerminal      = 0,
   .iSourceID           = 5,
   .iTerminal           = 0,
} ;
#endif



////////////////////////////////////////////////////////////////////////////////
// Mono Mute Control
////////////////////////////////////////////////////////////////////////////////
void si32UsbAudioInterfaceObject_fu_mute_mono_get(  void * self, uint16_t * bMute, uint16_t * Min, uint16_t * Max, uint16 * Res )
{

  si32UsbAudioInterfaceObject_fu_mute_control_type * control = (si32UsbAudioInterfaceObject_fu_mute_control_type *) self;
  printf("mic: get mute (master): %d\n", control->bMute);
  *bMute = control->bMute;
  return;
}

// -----------------------------------------------------------------------------
//
void si32UsbAudioInterfaceObject_fu_mute_mono_set( void * self, uint16_t bMute )
{

  si32UsbAudioInterfaceObject_fu_mute_control_type * control = (si32UsbAudioInterfaceObject_fu_mute_control_type *) self;
  control->bMute=bMute;
  printf("mic: set mute (master): %d\n", control->bMute);
  
  // notify control plane
  myControlPlaneMute[2] = control->bMute;
  si32PseudoThreadObject_resume(myControlPlane, 
                                SI32_PSEUDO_THREAD_PRIORITY_LOW);
  return;
}

// -----------------------------------------------------------------------------
//
si32UsbAudioInterfaceObject_fu_mute_control_type si32AudioMuteMonoControl =
{
  .ctl.Get = &si32UsbAudioInterfaceObject_fu_mute_mono_get,
  .ctl.Set = &si32UsbAudioInterfaceObject_fu_mute_mono_set,
  .ctl.context = &si32AudioMuteMonoControl,
  .bMute = false,
};

////////////////////////////////////////////////////////////////////////////////
// Mono Volume Control
////////////////////////////////////////////////////////////////////////////////
void si32UsbAudioInterfaceObject_fu_volume_mono_get( void * self, uint16_t * u16Value, uint16_t * Min, uint16_t * Max, uint16 * Res)
{

  si32UsbAudioInterfaceObject_fu_volume_control_type * control = (si32UsbAudioInterfaceObject_fu_volume_control_type *) self;
  printf("mic: get volume (center)");
  if(u16Value)
  {
    printf(": %d\n", control->Volume);
    *u16Value = control->Volume;
  }

  if (Min)
  {
    printf(" min: %d\n", control->Range[0]);
    *Min = (uint16_t) control->Range[0];
  }
  if (Max)
  {
    printf(" max: %d\n", control->Range[1]);
    *Max = (uint16_t) control->Range[1];
  }
  if (Res)
  {
    printf(" res: %d\n", control->Range[2]);
    *Res = (uint16_t) control->Range[2];
  }
}

// -----------------------------------------------------------------------------
//
void si32UsbAudioInterfaceObject_fu_volume_mono_set( void * self, uint16_t u16Value)
{

  si32UsbAudioInterfaceObject_fu_volume_control_type * control = (si32UsbAudioInterfaceObject_fu_volume_control_type *) self;
  control->Volume= u16Value ;
  printf("mic: set volume (center): %d\n", control->Volume);
  
  // notify control plane
  myControlPlaneVolume[2] = (int16)u16Value;
  si32PseudoThreadObject_resume(myControlPlane, 
                                SI32_PSEUDO_THREAD_PRIORITY_LOW);
}

// -----------------------------------------------------------------------------
// These values are for the wm8976.
int16_t myMicrophoneVolumeRange[3] =
{
  [0] = -127,
  [1] = 0,
  [2] = 1,
};

// -----------------------------------------------------------------------------
si32UsbAudioInterfaceObject_fu_volume_control_type si32AudioVolumeMonoControl =
{
  .ctl.context = &si32AudioVolumeMonoControl,
  .ctl.Get = si32UsbAudioInterfaceObject_fu_volume_mono_get,
  .ctl.Set = si32UsbAudioInterfaceObject_fu_volume_mono_set,
  .Volume = 0x8000,
  .Range  = myMicrophoneVolumeRange,
};

////////////////////////////////////////////////////////////////////////////////
// Mono Feature Unit w/ Mute & Volume Control
////////////////////////////////////////////////////////////////////////////////
si32UsbAudioFeatureUnitMonoType si32UsbAudioMonoFeatureUnit =
{
  .NumChannels   = 2,
  .ChannelControls[0].bmControls = (AUDIO_FU_MUTE_CONTROL_MASK),
  .ChannelControls[0].mute       = &si32AudioMuteMonoControl,
  .ChannelControls[1].bmControls = (AUDIO_FU_VOLUME_CONTROL_MASK),
  .ChannelControls[1].volume     = &si32AudioVolumeMonoControl,
};

// -----------------------------------------------------------------------------
//
void si32UsbAudioInterfaceObject_fu_mute_stereo_get(  void * self, uint16_t * bMute, uint16_t * Min, uint16_t * Max, uint16 * Res )
{

  si32UsbAudioInterfaceObject_fu_mute_control_type * control=(si32UsbAudioInterfaceObject_fu_mute_control_type *) self;
  *bMute = control->bMute;
  printf("spk: get mute (master): %d\n", ((control->bMute)& 0x01) );
  return;
}

// -----------------------------------------------------------------------------
//
void si32UsbAudioInterfaceObject_fu_mute_stereo_set( void * self, uint16_t bMute )
{
  si32UsbAudioInterfaceObject_fu_mute_control_type * control = (si32UsbAudioInterfaceObject_fu_mute_control_type *) self;
  control->bMute=bMute;
  printf("spk: set mute (master): %d\n", control->bMute);
  
  // notify control plane
  myControlPlaneMute[0] = control->bMute;
  myControlPlaneMute[1] = control->bMute;
  si32PseudoThreadObject_resume(myControlPlane, 
                                SI32_PSEUDO_THREAD_PRIORITY_LOW);
  return;
}

// -----------------------------------------------------------------------------
//
si32UsbAudioInterfaceObject_fu_mute_control_type si32AudioMuteStereoControl =
{
///
  .ctl.context = &si32AudioMuteStereoControl,
  .ctl.Get = &si32UsbAudioInterfaceObject_fu_mute_stereo_get,
  .ctl.Set = &si32UsbAudioInterfaceObject_fu_mute_stereo_set,
///
  .bMute = false,
} ;

// -----------------------------------------------------------------------------
//
void si32UsbAudioInterfaceObject_fu_volume_stereo_get( void * self, uint16_t * u16Value, uint16_t * Min, uint16_t * Max, uint16 * Res)
{
  extern si32UsbAudioInterfaceObject_fu_volume_control_type si32AudioVolumeStereoControl[2];

  si32UsbAudioInterfaceObject_fu_volume_control_type * control = (si32UsbAudioInterfaceObject_fu_volume_control_type * )self;

  printf("spk: get volume ");
  if( self == &si32AudioVolumeStereoControl[0] )
  {
    printf("(front left)");
  }
  else if (self == &si32AudioVolumeStereoControl[1] )
  {
    printf("(front right)");
  }

  if(u16Value)
  {
    printf(": %d\n", control->Volume);
    *u16Value = control->Volume;
  }
  if (Min)
  {
    *Min = (uint16_t) control->Range[0];
    printf(" min: %d\n", control->Range[0]);
  }

  if (Max)
  {
    *Max = (uint16_t) control->Range[1];
    printf(" max: %d\n", control->Range[1]);
  }

  if (Res)
  {
    *Res = (uint16_t) control->Range[2];
    printf(" res: %d\n", control->Range[2]);
  }
}

// -----------------------------------------------------------------------------
//
void si32UsbAudioInterfaceObject_fu_volume_stereo_set( void * self, uint16_t u16Value)
{
  extern si32UsbAudioInterfaceObject_fu_volume_control_type si32AudioVolumeStereoControl[2];
  si32UsbAudioInterfaceObject_fu_volume_control_type * control = (si32UsbAudioInterfaceObject_fu_volume_control_type *) self;
  
  printf("spk:  Set Volume ");
  if( self == &si32AudioVolumeStereoControl[0] )
  {
    printf("(front left)");
    // notify control plane
    myControlPlaneVolume[0] = (int16)u16Value;
    si32PseudoThreadObject_resume(myControlPlane, 
                                  SI32_PSEUDO_THREAD_PRIORITY_LOW);
  }
  else if (self == &si32AudioVolumeStereoControl[1] )
  {
    printf("(front right)");
    // notify control plane
    myControlPlaneVolume[1] = (int16)u16Value;
    si32PseudoThreadObject_resume(myControlPlane, 
                                  SI32_PSEUDO_THREAD_PRIORITY_LOW);
  }
  control->Volume = (int16) u16Value;
  printf(": %d\n", control->Volume);
};


// -----------------------------------------------------------------------------
// These values are for the wm8976.
int16_t mySpeakerVolumeRange[3] =
{
  [0] = -57,
  [1] = 6,
  [2] = 1,
};

// -----------------------------------------------------------------------------
// Update this with object/macro initialization for
//   - si32AudioVolumeStereoControl
//   - si32UsbOutStereoFeatureUnit
si32UsbAudioInterfaceObject_fu_volume_control_type si32AudioVolumeStereoControl[2] =
{
  [0].ctl.context = &si32AudioVolumeStereoControl[0],
  [0].ctl.Get = si32UsbAudioInterfaceObject_fu_volume_stereo_get,
  [0].ctl.Set = si32UsbAudioInterfaceObject_fu_volume_stereo_set,
  [0].Volume = 0x8000,
  [0].Range = mySpeakerVolumeRange,

  [1].ctl.context = &si32AudioVolumeStereoControl[1],
  [1].ctl.Get = si32UsbAudioInterfaceObject_fu_volume_stereo_get,
  [1].ctl.Set = si32UsbAudioInterfaceObject_fu_volume_stereo_set,
  [1].Volume = 0x8000,
  [1].Range = mySpeakerVolumeRange,
} ;

// -----------------------------------------------------------------------------
// Update with object/macro initialization
si32UsbAudioFeatureUnitStereoType si32UsbOutStereoFeatureUnit =
{
  .NumChannels   = 3,
  .ChannelControls[0].bmControls = (AUDIO_FU_MUTE_CONTROL_MASK),
  .ChannelControls[0].mute       = &si32AudioMuteStereoControl,
  .ChannelControls[1].bmControls = (AUDIO_FU_VOLUME_CONTROL_MASK),
  .ChannelControls[1].volume     = &si32AudioVolumeStereoControl[0],
  .ChannelControls[2].bmControls = (AUDIO_FU_VOLUME_CONTROL_MASK),
  .ChannelControls[2].volume     = &si32AudioVolumeStereoControl[1],
};

////////////////////////////////////////////////////////////////////////////////////////////////////
//------------------------------------------------------------------------------

void init_audio_control_interface(void)
{
  //////////////////////////////////////////////////////////////////////////////
  si32UsbAudioInterfaceObject_initialize_ac_header(&myUsbAudioControlSpeakerHeaderObject);
  {
    myUsbAudioControlSpeakerHeaderObject.usbAudioInterfaceObject.pDescriptor = (uint8_t *) &AcHeaderDescriptor;
  }

  si32UsbAudioInterfaceObject_initialize_ac_input_terminal(
    &myUsbAudioControlSpeakerUsbInputTerminalObject,
    &AcUsbInputTerminalDescriptor);

  si32UsbAudioInterfaceObject_initialize_ac_feature_unit(
     &myUsbAudioControlSpeakerFeatureUnitObject,
     (si32UsbAudioFeatureUnitType*) &si32UsbOutStereoFeatureUnit,
     (si32UsbAudioFeatureUnitDescriptorType*) &AcStereoFeatureUnitDescriptor);

  si32UsbAudioInterfaceObject_initialize_ac_output_terminal(
    &myUsbAudioControlSpeakerOutputTerminalObject,
    &AcStereoOutputTerminalDescriptor);

#if INCLUDE_MIC
  si32UsbAudioInterfaceObject_initialize_ac_input_terminal(
    &myUsbAudioControlMicInputTerminalObject,
    &myMicInputTerminalDescriptor);

  si32UsbAudioInterfaceObject_initialize_ac_feature_unit(
    &myUsbAudioControlMicFeatureUnitObject,
    (si32UsbAudioFeatureUnitType*) &si32UsbAudioMonoFeatureUnit,
    (si32UsbAudioFeatureUnitDescriptorType*) &myMicMonoFeatureUnitDescriptor);

  si32UsbAudioInterfaceObject_initialize_ac_output_terminal(
    &myUsbAudioControlMicUsbOutputTerminalObject,
  &myMicUsbOutputTerminalDescriptor);
#endif

  static si32UsbAudioInterfaceObject * AudioControlInterfaceObjectList[] =
  {
    &myUsbAudioControlSpeakerHeaderObject,
    &myUsbAudioControlSpeakerUsbInputTerminalObject,
    &myUsbAudioControlSpeakerFeatureUnitObject,
    &myUsbAudioControlSpeakerOutputTerminalObject,
#if INCLUDE_MIC
    &myUsbAudioControlMicInputTerminalObject,
    &myUsbAudioControlMicFeatureUnitObject,
    &myUsbAudioControlMicUsbOutputTerminalObject,
#endif
    NULL
  };

  si32UsbAudioControlInterfaceObject_initialize(
    &myUsbAudioControlInterfaceObject,
    &myUsbAudioControlInterfaceDescriptor,
    NULL,
    AudioControlInterfaceObjectList );

  //////////////////////////////////////////////////////////////////////////////
  // Test audio control interfaces
  #if 0
  {
    si32UsbAudioInterfaceObject ** pObj = pAudioControlObjectList;
    while(pObj && *pObj)
    {
      extern void test_UsbAudioInterfaceObject(si32UsbAudioInterfaceObject *);
      test_UsbAudioInterfaceObject(*pObj);
      pObj++;
    }
  }
  #endif
  //
  //////////////////////////////////////////////////////////////////////////////
  return;
}




si32UsbAudioEndpointGeneralDescriptorType          out_ep_general=
  {
    .bLength             =  sizeof(out_ep_general),
    .bDescriptorType     =  USB_AUDIO_CS_INTERFACE,
    .bDescriptorSubtype  =  USB_AUDIO_EP_GENERAL,
    .bmAttributes        =  0, /* D6:0 = 1, control supported */
    .bLockDelayUnits     =  USB_AUDIO_LOCKING_DELAY_UNITS_MSEC,
    .wLockDelay          =  0,
  };

si32UsbAudioInterfaceStreamingGeneralDescriptorType out_as_general=
  {
   .bLength             = sizeof(si32UsbAudioInterfaceStreamingGeneralDescriptorType),
   .bDescriptorType     = USB_AUDIO_CS_INTERFACE,
   .bDescriptorSubtype  = USB_AUDIO_AS_GENERAL,
   .TerminalLink        = 1,
   .Delay               = 0,
   .FormatTag           = Fmt_1_PCM,
  };


si32SampleRateType mySampleRate = // changing this has no efect?
{
  .U32=48000
  //.U32=8000
};

as_format1_type out_as_format=
{
  .bLength             = sizeof(out_as_format),
  .bDescriptorType     = USB_AUDIO_CS_INTERFACE,
  .bDescriptorSubtype  = USB_AUDIO_AS_FORMAT_TYPE,
  .bFormatType         = AUDIO_FORMAT_TYPE_1,
  .bNrChannels         = 2,
  .bSubframeSize       = 2,
  .bBitResolution      = 16,       // No. used bits in subs
  .bNumSamFreq         = 1,         // Sampling freqs. = ns
  .iSamFreq            = { {0x80, 0xbb, 0x00} },// 24-bit freq. in Hz (48KHz)
  //.iSamFreq            = { {0x40, 0x1f, 0x00} },// 24-bit freq. in Hz (8KHz)
  .iString              = 0,
};


//------------------------------------------------------------------------------
// 1. Initialize USB Audio Streaming Endpoint Object
//   a.  Initialize all audio endpoint controls
//   b.  Create audio endpoint control list
//   c.  initialize audio endpoint object
//
// 2. Initialize USB Audio Streaming Interface
//   a.  Initialize all audio streaming interface controls
//       - as_general
//       - as_format
//   b.  Create audio streaming interface control list
//       - as_ifc_ctl_list = { as_general, as_format }
//   c.  Create audio streaming endpoint list
//       - as_ifc_ep_list = { asep }
//   d.  initialize USB Audio Streaming Interface Object
//       with ifc ctl list & asep list
//------------------------------------------------------------------------------
void init_audio_streaming_out_interface(void)
{
  //////////////////////////////////////////////////////////////////////////////
  //
  si32UsbAudioStreamingInterfaceObject_initialize(
    &myUsbAudioStreamingInterfaceOutObject_0,
    1,
    0,
    NULL,
    NULL);
  //
  //////////////////////////////////////////////////////////////////////////////

  //////////////////////////////////////////////////////////////////////////////
  //

  // Initialize EP General object
  {
    si32UsbAudioEndpointObject_initialize(
      &myUsbAudioStreamingEndpointGeneralOutObject);

    myUsbAudioStreamingEndpointGeneralOutObject.usbAudioEndpointObject.subtype=USB_AUDIO_EP_GENERAL;
    si32UsbAudioEndpointControlType * Control = &myUsbAudioStreamingEndpointGeneralOutObject.usbAudioEndpointObject.Control;

    // set descriptor
    Control->ep_general.bmAttributes      = 0;
    Control->ep_general.bLockDelayUnits   = USB_AUDIO_LOCKING_DELAY_UNITS_MSEC;
    Control->ep_general.wLockDelay        = 0;
  }


  static si32UsbAudioEndpointObject * AudioEndpointOutList[] =
  {
    &myUsbAudioStreamingEndpointGeneralOutObject,
    NULL
  };

  si32UsbAudioStreamingEndpointObject_initialize(
    &myUsbAudioStreamingEndpointOutObject,
    &myUsbAudioStreamingEndpointOutDescriptor,
    AudioEndpointOutList
    );


  static si32UsbAudioStreamingEndpointObject * AudioEndpointOutObjectList[] =
  {
    &myUsbAudioStreamingEndpointOutObject,
    NULL
  };

  // Initialize AS General object
  {
    si32UsbAudioInterfaceObject_initialize(&myUsbAudioStreamingGeneralOutObject);
    si32UsbAudioInterfaceObject_initialize_as_general(&myUsbAudioStreamingGeneralOutObject);
    myUsbAudioStreamingGeneralOutObject.usbAudioInterfaceObject.pDescriptor = (uint8_t *) &out_as_general;
  }

  // Initialize AS_Format object
  {
    si32UsbAudioInterfaceObject_initialize(&myUsbAudioStreamingFormatOutObject);
    si32UsbAudioInterfaceObject_initialize_as_format_type1(&myUsbAudioStreamingFormatOutObject);
    myUsbAudioStreamingFormatOutObject.usbAudioInterfaceObject.pDescriptor = (uint8_t *) &out_as_format;
  }

  static si32UsbAudioInterfaceObject * AudioInterfaceOutList[] =
  {
    &myUsbAudioStreamingGeneralOutObject,
    &myUsbAudioStreamingFormatOutObject,
    NULL
  };

  si32UsbAudioStreamingInterfaceObject_initialize(
    &myUsbAudioStreamingInterfaceOutObject_1,
    1,
    1,
    AudioInterfaceOutList,
    AudioEndpointOutObjectList);
  //
  //////////////////////////////////////////////////////////////////////////////
}



#if INCLUDE_MIC

si32UsbAudioInterfaceStreamingGeneralDescriptorType in_as_general=
  {
   .bLength             = sizeof(si32UsbAudioInterfaceStreamingGeneralDescriptorType),
   .bDescriptorType     = USB_AUDIO_CS_INTERFACE,
   .bDescriptorSubtype  = USB_AUDIO_AS_GENERAL,
   .TerminalLink        = 6,
   .Delay               = 0,
   .FormatTag           = Fmt_1_PCM,
  };



as_format1_type in_as_format=
{
  .bLength             = sizeof(out_as_format),
  .bDescriptorType     = USB_AUDIO_CS_INTERFACE,
  .bDescriptorSubtype  = USB_AUDIO_AS_FORMAT_TYPE,
  .bFormatType         = AUDIO_FORMAT_TYPE_1,
  .bNrChannels         = 1,
  .bSubframeSize       = 2,
  .bBitResolution      = 16,       // No. used bits in subs
  .bNumSamFreq         = 1,         // Sampling freqs. = ns
  .iSamFreq            = { {0x80, 0xbb, 0x00} },// 24-bit freq. in Hz
  .iString              = 0,
};

//------------------------------------------------------------------------------
// 1. Initialize USB Audio Streaming Endpoint Object
//   a.  Initialize all audio endpoint controls
//   b.  Create audio endpoint control list
//   c.  initialize audio endpoint object
//
// 2. Initialize USB Audio Streaming Interface
//   a.  Initialize all audio streaming interface controls
//       - as_general
//       - as_format
//   b.  Create audio streaming interface control list
//       - as_ifc_ctl_list = { as_general, as_format }
//   c.  Create audio streaming endpoint list
//       - as_ifc_ep_list = { asep }
//   d.  initialize USB Audio Streaming Interface Object
//       with ifc ctl list & asep list
//------------------------------------------------------------------------------
void init_audio_streaming_in_interface(void)
{
  //////////////////////////////////////////////////////////////////////////////
  si32UsbAudioStreamingInterfaceObject_initialize(
                                                  &myUsbAudioStreamingInterfaceInObject_0,
                                                  2,
                                                  0,
                                                  NULL,
                                                  NULL);
  
  //////////////////////////////////////////////////////////////////////////////
  //
  {
    si32UsbAudioEndpointObject_initialize(&myUsbAudioStreamingEndpointGeneralInObject);
    myUsbAudioStreamingEndpointGeneralInObject.usbAudioEndpointObject.subtype=USB_AUDIO_EP_GENERAL;
    si32UsbAudioEndpointControlType * Control = &myUsbAudioStreamingEndpointGeneralInObject.usbAudioEndpointObject.Control;
    Control->ep_general.bmAttributes      = 0;
    Control->ep_general.bLockDelayUnits   = USB_AUDIO_LOCKING_DELAY_UNITS_MSEC;
    Control->ep_general.wLockDelay        = 0;
  }
  
  
  static si32UsbAudioEndpointObject * AudioEndpointInList[] =
  {
    &myUsbAudioStreamingEndpointGeneralInObject,
    NULL
  };
  
  si32UsbAudioStreamingEndpointObject_initialize(
                                                 &myUsbAudioStreamingEndpointInObject,
                                                 &myUsbAudioStreamingEndpointInDescriptor,
                                                 AudioEndpointInList
                                                 );
  
  
  static si32UsbAudioStreamingEndpointObject * AudioEndpointInObjectList[] =
  {
    &myUsbAudioStreamingEndpointInObject,
    NULL
  };
  
  // 2a - Initialize USB Audio Interface Controls (as_general & as_format)
  {
    si32UsbAudioInterfaceObject_initialize(&myUsbAudioInterfaceGeneralInObject);
    si32UsbAudioInterfaceObject_initialize_as_general(&myUsbAudioInterfaceGeneralInObject);
    myUsbAudioInterfaceGeneralInObject.usbAudioInterfaceObject.pDescriptor = (uint8_t *) &in_as_general;
    
    si32UsbAudioInterfaceObject_initialize(&myUsbAudioInterfaceFormatInObject);
    si32UsbAudioInterfaceObject_initialize_as_format_type1(&myUsbAudioInterfaceFormatInObject);
    myUsbAudioInterfaceFormatInObject.usbAudioInterfaceObject.pDescriptor = (uint8_t *) &in_as_format;
  }
  
  // Create USB Audio Streaming Interface Control List
  static si32UsbAudioInterfaceObject * AudioInterfaceInList[] =
  {
    &myUsbAudioInterfaceGeneralInObject,
    &myUsbAudioInterfaceFormatInObject,
    NULL
  };
  
  // Finally, initialize the USB Audio Streaming Interface, using
  // Audio Interface Control List and Audio Streaming Endpoint list
  si32UsbAudioStreamingInterfaceObject_initialize(
                                                  &myUsbAudioStreamingInterfaceInObject_1,
                                                  2,
                                                  1,
                                                  AudioInterfaceInList,
                                                  AudioEndpointInObjectList);
}

#endif




//------------------------------------------------------------------------------
//
si32UsbInterfaceObject ** init_usb_audio_function(void)
{
  init_audio_control_interface();
  
  init_audio_streaming_out_interface();
  
#if INCLUDE_MIC
  init_audio_streaming_in_interface();
#endif
  
  static si32UsbInterfaceObject * AudioInterfaceObjectList [] =
  {
    (si32UsbInterfaceObject *) &myUsbAudioControlInterfaceObject,
    (si32UsbInterfaceObject *) &myUsbAudioStreamingInterfaceOutObject_0,
    (si32UsbInterfaceObject *) &myUsbAudioStreamingInterfaceOutObject_1,
#if INCLUDE_MIC
    (si32UsbInterfaceObject *) &myUsbAudioStreamingInterfaceInObject_0,
    (si32UsbInterfaceObject *) &myUsbAudioStreamingInterfaceInObject_1,
#endif
    NULL,
  };
  
  return AudioInterfaceObjectList;
}


static uint8_t my_out_buffer[192];
static volatile int rx_cb_count=0;
static volatile bool bReadyToReceive = true;
//------------------------------------------------------------------------------
void audio_out_callback(obj * object,
                        void * context,
                        uint32_t tag,
                        obj * buffer,
                        si32IoRequestResultType result,
                        uint32_t residue)
{
  si32UsbAudioStreamingInterfaceObject * my_interface = (si32UsbAudioStreamingInterfaceObject *) object;
  si32UsbEndpointObject *ep = my_interface->usbInterfaceObject.Endpoints[0];
  
  rx_cb_count++;
  
  if( my_interface->usbInterfaceObject.bIsSet )
  {
    si32UsbEndpointObject_receive(ep,
                                  my_out_buffer,
                                  sizeof(my_out_buffer),
                                  audio_out_callback,
                                  object,
                                  NULL);
  }
  else
  {
    bReadyToReceive=true;
  }
}

//------------------------------------------------------------------------------
void demo_audio_out_poller(obj* obj, void* context)
{
  si32UsbAudioStreamingInterfaceObject * my_interface = (si32UsbAudioStreamingInterfaceObject *) obj;
  si32UsbEndpointObject *ep = my_interface->usbInterfaceObject.Endpoints[0];
  
  static volatile bool bWasSet = false;
  volatile bool bIsSet;
  
  bIsSet = my_interface->usbInterfaceObject.bIsSet;
  
  if ( !bWasSet && bIsSet )
  {
    bWasSet=bIsSet;
    printf("spk: enable audio streaming out\n");
    if (bReadyToReceive)
    {
      // Kick off one receive....
      printf("starting to receive audio...\n");
      bReadyToReceive=false;
      /*si32UsbEndpointObject_receive(ep,
       my_out_buffer,
       sizeof(my_out_buffer),
       audio_out_callback,
       my_interface,
       NULL); */
    }
    // Currently the ep and the data plane thread communicate via this variable.                                
    myDataPlaneOutEp = ep;
    si32PseudoThreadObject_resume(myDataPlaneOut,
                                  SI32_PSEUDO_THREAD_PRIORITY_LOW);
  }
  else if(bWasSet && !bIsSet)
  {
    bWasSet=bIsSet;
    printf("spk: disable streaming out - received %d callbacks\n", rx_cb_count);
    rx_cb_count = 0;
    // Currently the ep and the data plane thread communicate via this variable.                                
    myDataPlaneOutEp = NULL;
    si32PseudoThreadObject_resume(myDataPlaneOut,
                                  SI32_PSEUDO_THREAD_PRIORITY_LOW);
  }
  
  si32RunLoopObject_post_work_request(si32GetCurrentRunLoop(),
                                        SI32_RUN_LOOP_PRIORITY_LOW,
                                        demo_audio_out_poller,
                                        obj,
                                        NULL);
}


//------------------------------------------------------------------------------
//
void demo_audio_in_poller(obj* obj, void* context)
{
  si32UsbAudioStreamingInterfaceObject * my_interface = (si32UsbAudioStreamingInterfaceObject *) obj;
  static volatile bool bWasSet = false;
  volatile bool bIsSet;
  bIsSet = my_interface->usbInterfaceObject.bIsSet;
  si32UsbEndpointObject *ep = my_interface->usbInterfaceObject.Endpoints[0];
  
  if ( !bWasSet && bIsSet )
  {
    bWasSet = bIsSet;
    printf("mic: enable streaming in\n");
    myDataPlaneInEp = ep;
    si32PseudoThreadObject_resume(myDataPlaneIn,
                                  SI32_PSEUDO_THREAD_PRIORITY_LOW);
  }
  else if( bWasSet && !bIsSet )
  {
    bWasSet = bIsSet;
    printf("mic: disable streaming in\n");
    myDataPlaneInEp = NULL;
    si32PseudoThreadObject_resume(myDataPlaneIn,
                                  SI32_PSEUDO_THREAD_PRIORITY_LOW);
  }
  
  si32RunLoopObject_post_work_request(si32GetCurrentRunLoop(),
                                        SI32_RUN_LOOP_PRIORITY_LOW,
                                        demo_audio_in_poller,
                                        obj,
                                        NULL);
}




//------------------------------------------------------------------------------
bool my_control_pipe_extension(obj* ignored,
                               si32UsbDeviceObject* device,
                               si32UsbSetupType* setup)
{
  if (setup->wRequest == USB_REQUEST_STANDARD_DEVICE_SET_CONFIG)
  {
    printf("cfg: %d\n", setup->wValue);
  }
  else if (setup->wRequest == USB_REQUEST_STANDARD_DEVICE_SET_INTERFACE)
  {
    printf("ifc: %d\n", setup->wValue);
    printf("ep: %p\n", myUsbAudioStreamingEndpointOutObject.usbEndpointObject.io_obj);
  }
  return false;
}

//------------------------------------------------------------------------------
si32UsbRequestHandlerType myCpex =
{
  .RequestHandler = my_control_pipe_extension,
  .Object         = NULL,
  .Next           = NULL,
};

//------------------------------------------------------------------------------
//
void myUsbAudioDevice_main( void )
{
  si32UsbInterfaceObject ** audio_interface_list;
  
  audio_interface_list = init_usb_audio_function();
  
  // Add interfaces to the config object
  si32UsbConfigurationObject_initialize(&myUsbAudioConfigurationObject,
                                        &myUsbAudioConfigurationDescriptor,
                                        audio_interface_list);
  
  // Add the configuration to the device object
  si32UsbDeviceObject_initialize(&myUsbAudioDeviceObject,
                                 &myUsbAudioDeviceDescriptor,
                                 myUsbAudioConfigurationList,
                                 &myUsbAudioStringTable);
  
  // audio device is ready
  myUsb0_main(&myUsbAudioDeviceObject);
  
  // poll for activity on the input side.
  si32RunLoopObject_post_work_request(si32GetCurrentRunLoop(),
                                        SI32_RUN_LOOP_PRIORITY_LOW,
                                        demo_audio_in_poller,
                                        &myUsbAudioStreamingInterfaceInObject_1,
                                        NULL);     
  
  // poll for activity on the output side.
  si32RunLoopObject_post_work_request(si32GetCurrentRunLoop(),
                                        SI32_RUN_LOOP_PRIORITY_LOW,
                                        demo_audio_out_poller,
                                        &myUsbAudioStreamingInterfaceOutObject_1,
                                        NULL);
}

//-eof--------------------------------------------------------------------------

