/* SCCSID = "src/dev/usb/USBAUDIO/AUDSUBS.C, usb, c.basedd 98/07/10" */
/*
*   Licensed Material -- Property of IBM
*
*   (c) Copyright IBM Corp. 1998  All Rights Reserved
*/
/************************** START OF SPECIFICATIONS ***************************/
/*                                                                            */
/*   SOURCE FILE NAME:  AUDSUBS.C                                             */
/*                                                                            */
/*   DESCRIPTIVE NAME: Audio device driver subroutines.                       */
/*                                                                            */
/*                                                                            */
/*   FUNCTION:                                                                */
/*              Provides routines to assist execution of the primary          */
/*              logic of the audio device driver (audiodd.c).                 */
/*              The routines in this sample are callable, but being stubs,    */
/*              don't do anything.  Still, the structure is useful.           */
/*                                                                            */
/*              Most of the routines equate directly to OS/2 2.0              */
/*              kernel <-> PDD interfaces.  Accordingly, the OS/2 2.0         */
/*              Physical Device Driver reference should be referenced for     */
/*              the routines that serve the purpose of kernel communication   */
/*                                                                            */                                          
/*              Other routines are specific to the Audio Device Driver.       */                                          
/*                                                                            */
/*                                                                            */
/*   NOTES:                                                                   */
/*      DEPENDENCIES: None                                                    */
/*      RESTRICTIONS: None                                                    */
/*                                                                            */
/* Change Log                                                                 */
/*                                                                            */
/*  Mark    yy/mm/dd  Programmer          Comment                             */
/*  ----    --------  ----------          -------                             */
/*          98/10/17  Vjacheslav Chibis   Original developer.                 */
/*                                                                            */
/**************************** END OF SPECIFICATIONS ***************************/


#define  INCL_DOSINFOSEG
#include "usbaud.h"
#include "audsubs.h"    // Function declarations

BOOL static CompBinaries( ULONG FAR *a, ULONG FAR *b, USHORT size );  

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  GetStreamTime                                    */
/*                                                                    */
/* DESCRIPTIVE NAME: GetStreamTime                                    */
/*                                                                    */
/* FUNCTION:    Get Stream Time                                       */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  GetStreamTime                                       */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  PSTREAM pStream  - a pointer to a stream                   */
/*                                                                    */
/* EXIT-NORMAL:  stream current time                                  */
/*                                                                    */
/* EXIT-ERROR:  N/A                                                   */
/*                                                                    */
/* EFFECTS:  NONE                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  NONE                                         */
/*                                                                    */
/* EXTERNAL REFERENCES:  NONE                                         */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/
PULONG GetStreamTime (PSTREAM pStream) 
{
   return(&pStream->ulCumTime);
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  SetStreamTime                                    */
/*                                                                    */
/* DESCRIPTIVE NAME: SetStreamTime                                    */
/*                                                                    */
/* FUNCTION:    Set time for the stream specified                     */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  SetStreamTime                                       */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  PSTREAM pStream - ptr to stream                            */
/*         ULONG ulSetPos  - stream time in ms                        */
/*                                                                    */
/* EXIT-NORMAL:  always                                               */
/*                                                                    */
/* EXIT-ERROR:  N/A                                                   */
/*                                                                    */
/* EFFECTS:  pStream->ulCumTime                                       */
/*                                                                    */
/* INTERNAL REFERENCES:  NONE                                         */  
/*                                                                    */
/* EXTERNAL REFERENCES:                                               */
/*                                                                    */
/*    ROUTINES:  ReportEvent                                          */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/

VOID SetStreamTime (PSTREAM pStream, ULONG ulSetPos)
{
   #ifdef DEBUG
   dsPrint1(DBG_SPECIFIC, "USBAUD: SetStreamTime at %ldms\r\n", ulSetPos);
   #endif
   pStream->ulCumTime = ulSetPos;
   ReportEvent(pStream, FALSE); // report event if bypassed
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  DevOpen                                          */
/*                                                                    */
/* DESCRIPTIVE NAME: Open device                                      */
/*                                                                    */
/* FUNCTION:    The function of this routine is to process OS/2 IOCtl */
/*                       OPEN call                                    */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  DevOpen                                             */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  ULONG ulSysFileNum                                         */
/*                                                                    */
/* EXIT-NORMAL:  TRUE - device opened succesfully                     */
/*                                                                    */
/* EXIT-ERROR:  FALSE                                                 */
/*                                                                    */
/* EFFECTS:  NONE                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  NONE                                         */  
/*                                                                    */
/*                                                                    */
/* EXTERNAL REFERENCES:  NONE                                         */
/*                                                                    */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/

USHORT DevOpen          (ULONG ulSysFileNum)
{
   register PSTREAM pStream;
   register USHORT i;

   // RTMIDI uses this open and does not need
   // a stream object so he passes in a -1
   // sys_file_num
   if( ulSysFileNum != 0xFFFFFFFF )
   {
      //find a stream and init it
      pStream = GlobalTable.paStream;

      for( i = 0; i < GlobalTable.usMaxNumStreams; i++ )
      {
         if( (pStream->hStream == -1) && (pStream->ulSysFileNum == -1) )
            break;

         ++pStream;
      } //end for

      if( i == GlobalTable.usMaxNumStreams )
         //return (FALSE);
         return(TRUE);

//**************************************
// Found empty stream entry, so use it
// Fill Stream structure
//**************************************
      for( i=0; i<MAXIOBUFFS; i++ )
      {
         pStream->IOBuff[i].lSize        =  0L;
         pStream->IOBuff[i].pHead        =  NULL;
         pStream->IOBuff[i].pTail        =  NULL;
         pStream->IOBuff[i].lCount       =  0L;
         pStream->IOBuff[i].ulPosition   =  0L;
         pStream->IOBuff[i].usRunFlags   =  IOB_CHAIN_BUFFERS;
         pStream->IOBuff[i].pBuffer      =  NULL;
         pStream->IOBuff[i].bEmpty       =  TRUE;
      }
      pStream->ulSysFileNum        = ulSysFileNum;
      pStream->ulOperation         = 0L;
      pStream->ulFlags             = 0L;    // clear flags
      pStream->ulCumTime           = 0L;
      pStream->usCurrIOBuffIndex   = 0;
      pStream->usMode              = 0;
   }

   usInUse = NOT_AVAILABLE;


   return(TRUE);
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  DevClose                                         */
/*                                                                    */
/* DESCRIPTIVE NAME: Close evice                                      */
/*                                                                    */
/* FUNCTION:    The function of this routine is to process OS/2 IOCtl */
/*                       CLOSE call                                   */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  DevClose                                            */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  ULONG ulSysFileNum                                         */
/*                                                                    */
/* EXIT-NORMAL:  N/A                                                  */
/*                                                                    */
/* EXIT-ERROR:  N/A                                                   */
/*                                                                    */
/* EFFECTS:  NONE                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  NONE                                         */  
/*                                                                    */
/*                                                                    */
/* EXTERNAL REFERENCES:                                               */
/*            ROUTINES : GetFileStream                                */
/*                                                                    */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/

VOID DevClose  (ULONG ulSysFileNum)
{
   register PSTREAM pStream ;

   if( ulSysFileNum != 0xFFFFFFFF )
   {
      // Clean up the stream
      pStream = GetFileStream(ulSysFileNum);
      if( pStream )
      {
         pStream->hStream = -1;
         pStream->ulSysFileNum = -1;
         pStream->ulFlags = 0;        // clear flags
      }
   }  // if ulSysFileNum not FFFFFFFFF

   usInUse = AVAILABLE;

}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  DevChange                                        */
/*                                                                    */
/* DESCRIPTIVE NAME: Close evice                                      */
/*                                                                    */
/* FUNCTION:    Device specific operations to change device           */
/*                                operationality 
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  DevChange                                           */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  MCI_AUDIO_CHANGE FAR *pRP_AC - ptr to                      */
/*                            Audio Change structure                  */
/*                                                                    */
/* EXIT-NORMAL:  N/A                                                  */
/*                                                                    */
/* EXIT-ERROR:  N/A                                                   */
/*                                                                    */
/* EFFECTS:  NONE                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  NONE                                         */  
/*                                                                    */
/*                                                                    */
/* EXTERNAL REFERENCES:  MAKEULONG()                                  */
/*                       MAKEUSHORT()                                 */
/*                       HIBYTE()                                     */
/*                       SetUnitControl()                             */
/*                       GetUnitControlValue()                        */
/*                                                                    */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/

VOID DevChange  (MCI_AUDIO_CHANGE FAR *pRP_AC)          // Change device type
{
   MCI_TRACK_INFO FAR *pTrackInfo = (MCI_TRACK_INFO FAR *) pRP_AC->pvDevInfo;
   USHORT  usMasterVolume;

   // if there is no one USB audio device attached
   if( !gNoOfActiveAudioDevices )
      return;

   if( pTrackInfo!= (MCI_TRACK_INFO FAR *) 0 )
      usMasterVolume = pTrackInfo->usMasterVolume;
   else
      usMasterVolume= 0;

   #ifdef DEBUG
   dsPrint3(DBG_SPECIFIC, "USBAUD: Vol=%lxh MVol=%lxh OutDevType=%xh\r\n",pRP_AC->lVolume,usMasterVolume,pRP_AC->rOutputList[0].ulDevType);
   #endif


   // check, if  we can set set the control for the sepcified endpoint
   if( pRP_AC->lVolume!=AUDIO_IGNORE && ((pRP_AC->rOutputList[0].ulDevType!= STEREO_LINE_OUTPUT) ||
   (pRP_AC->rOutputList[0].ulDevType != LEFT_LINE_OUTPUT)||
   (pRP_AC->rOutputList[0].ulDevType != RIGHT_LINE_OUTPUT)||
   (pRP_AC->rOutputList[0].ulDevType != SPEAKER_OUTPUT)) )
   {
      register UCHAR ucChannel;      

      pRP_AC->lVolume&=MAKEULONG(0, usMasterVolume);

      #ifdef DEBUG
      dsPrint1(DBG_SPECIFIC, "USBAUD: Vol=%lxh\r\n", pRP_AC->lVolume);
      #endif
      gAudioDevices[GlobalTable.ucActiveDeviceIndex].ulBass     =  pRP_AC->lBass;
      gAudioDevices[GlobalTable.ucActiveDeviceIndex].ulTreble   =  pRP_AC->lTreble;
      gAudioDevices[GlobalTable.ucActiveDeviceIndex].ulVolume   =  pRP_AC->lVolume;
      gAudioDevices[GlobalTable.ucActiveDeviceIndex].ulBalance  =  pRP_AC->lBalance;

      gOLDAC.lVolume = pRP_AC->lVolume;

      for( ucChannel=0; ucChannel<=gAudioDevices[GlobalTable.ucActiveDeviceIndex].ucChannels;  ucChannel++ )
      {
         if( gAudioDevices[GlobalTable.ucActiveDeviceIndex].ulDevCh[ucChannel]&CHANNEL_FEATURE_VOLUME )
         {
            gUSBAC.usLeftVolume  = GetUnitControlValue(HIUSHORT(pRP_AC->lVolume)*2-USB_MAX_VOLUME,
            gAudioDevices[GlobalTable.ucActiveDeviceIndex].usMinVolume[ucChannel],    
            gAudioDevices[GlobalTable.ucActiveDeviceIndex].usMaxVolume[ucChannel]);
            gUSBAC.usRightVolume = gUSBAC.usLeftVolume;
            gOLDAC.lVolume = pRP_AC->lVolume;
            #ifdef DEBUG
            dsPrint4(DBG_CRITICAL, "USBAUD : Set USB chan #%d Volume to %xh (min=%xh max=%xh)\r\n", ucChannel, gUSBAC.usLeftVolume, gAudioDevices[GlobalTable.ucActiveDeviceIndex].usMinVolume[ucChannel], gAudioDevices[GlobalTable.ucActiveDeviceIndex].usMaxVolume[ucChannel]);
            #endif
            // set volume
            SetUnitControl(GlobalTable.ucActiveDeviceIndex, VOLUME_CONTROL, ucChannel, sizeof(gUSBAC.usLeftVolume), (PUCHAR)&gUSBAC.usLeftVolume);

         }
      }

      if( ( pRP_AC->lBass != AUDIO_IGNORE ) )
         for( ucChannel=0; ucChannel<=gAudioDevices[GlobalTable.ucActiveDeviceIndex].ucChannels;  ucChannel++ )
            if( gAudioDevices[GlobalTable.ucActiveDeviceIndex].ulDevCh[ucChannel]&CHANNEL_FEATURE_BASS )
            {
               gUSBAC.ucLeftBass = HIBYTE(GetUnitControlValue( HIUSHORT(pRP_AC->lBass)*2-USB_MAX_BASS,
               MAKEUSHORT(0, gAudioDevices[GlobalTable.ucActiveDeviceIndex].ucMinBass[ucChannel]),    
               MAKEUSHORT(0, gAudioDevices[GlobalTable.ucActiveDeviceIndex].ucMaxBass[ucChannel]))); 
               gOLDAC.lBass = pRP_AC->lBass;

              #ifdef DEBUG
               dsPrint4(DBG_SPECIFIC, "USBAUD : Set USB ch=%d Bass to %xh (min=%xh max=%xh)\r\n",ucChannel, gUSBAC.ucLeftBass, gAudioDevices[GlobalTable.ucActiveDeviceIndex].ucMinBass[ucChannel], gAudioDevices[GlobalTable.ucActiveDeviceIndex].ucMaxBass[ucChannel]);
              #endif
              // set bass
               SetUnitControl(GlobalTable.ucActiveDeviceIndex, BASS_CONTROL, ucChannel, sizeof(gUSBAC.ucLeftBass), (PUCHAR)&gUSBAC.ucLeftBass);
            }
      if( ( pRP_AC->lTreble != AUDIO_IGNORE ) )
         for( ucChannel=0; ucChannel<=gAudioDevices[GlobalTable.ucActiveDeviceIndex].ucChannels;  ucChannel++ )
            if( gAudioDevices[GlobalTable.ucActiveDeviceIndex].ulDevCh[ucChannel]&CHANNEL_FEATURE_TREBLE )
            {
               gUSBAC.ucLeftTreble  =  HIBYTE(GetUnitControlValue((HIUSHORT(pRP_AC->lTreble)*2-USB_MAX_TREBLE),
               MAKEUSHORT(0, gAudioDevices[GlobalTable.ucActiveDeviceIndex].ucMinTreble[ucChannel]),    
               MAKEUSHORT(0, gAudioDevices[GlobalTable.ucActiveDeviceIndex].ucMaxTreble[ucChannel])));
               gOLDAC.lTreble = pRP_AC->lTreble;

               #ifdef DEBUG
               dsPrint4(DBG_SPECIFIC, "USBAUD : Set USB ch=%d treble to %xh (min=%xh max=%xh\r\n",ucChannel, gUSBAC.ucLeftTreble, gAudioDevices[GlobalTable.ucActiveDeviceIndex].ucMinTreble[ucChannel], gAudioDevices[GlobalTable.ucActiveDeviceIndex].ucMaxTreble[ucChannel]);
               #endif
               // set treble
               SetUnitControl(GlobalTable.ucActiveDeviceIndex, TREBLE_CONTROL, ucChannel, sizeof(gUSBAC.ucLeftTreble), (PUCHAR)&gUSBAC.ucLeftTreble);
            }
   }
   else
   {
      #ifdef DEBUG
      dsPrint3(DBG_SPECIFIC, "USBAUD: Not setting Unit controls vol=%lxh, Master=%lxh devtype=%xh\r\n",
      pRP_AC->lVolume, usMasterVolume, pRP_AC->rOutputList[0].ulDevType);
      #endif
   }

   #ifdef DEBUG
   dsPrint2(DBG_SPECIFIC, "USBAUD: Bass=%xh Treble=%xh\r\n",  pRP_AC->lBass, pRP_AC->lTreble);
   #endif

}


/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  EnqueueEvent                                     */
/*                                                                    */
/* DESCRIPTIVE NAME: Enqueue Event                                    */
/*                                                                    */
/* FUNCTION:    Generates detach procedure for specified device       */
/*              When processing data, the PDD watches for             */
/*              specific events.  For example, when data at           */
/*              certain stream position is processed, the             */
/*              PDD tells the stream handler of the encountered       */
/*              event (eg setpositionadvise on every ...)             */
/*             The PDD has to have a mechanish of noticing            */
/*             these events.                                          */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  EnqueueEvent                                        */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  HEVENT hEvent  - event handler                             */
/*         ULONG ulCuePoint - event time in ms                        */
/*         PSTREAM pStream  - a pointer to a stream                   */
/*                                                                    */
/*                                                                    */
/* EXIT-NORMAL:  NO_ERROR                                             */
/*                                                                    */
/* EXIT-ERROR:  ERROR_TOO_MANY_EVENTS                                 */
/*                                                                    */
/* EFFECTS:  NONE                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:                                               */  
/*                                                                    */
/*    ROUTINES:  NONE                                                 */
/*                                                                    */
/* EXTERNAL REFERENCES:                                               */
/*                                                                    */
/*    ROUTINES:  NONE                                                 */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/

ULONG EnqueueEvent (HEVENT hEvent, ULONG ulCuePoint, PSTREAM pStream)
{
   register USHORT   i;
   EVENT_QUEUE *pEvent;
   BOOL     bNodeAvail = FALSE;


   // find an empty node in the queue
   for( i = 0, pEvent = pStream->pEventQueue; i < MAX_EVENT_QUEUE; i++,pEvent++ )
   {
      if( pEvent->hEvent == (HEVENT)(-1) )
      {
         bNodeAvail = TRUE;
         break;
      }
   }

   // if no node available, return error
   if( !bNodeAvail )
      return(ERROR_TOO_MANY_EVENTS);

   // set the event queue node
   pEvent->hEvent = hEvent;
   pEvent->ulCuePoint = ulCuePoint;

   #ifdef DEBUG
   dsPrint1(DBG_SPECIFIC, "USBAUD: EnqueueEvent : ulCuePoint=%lxh\r\n", ulCuePoint);
   #endif
   return(NO_ERROR);
}


/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  DequeueEvent                                     */
/*                                                                    */
/* DESCRIPTIVE NAME: Dequeue Event                                    */
/*                                                                    */
/* FUNCTION:                                                          */
/*                                                                    */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  DequeueEvent                                        */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  HEVENT hEvent - event Handler ID                           */
/*         PSTREAM pStream - a pointer to a stream                    */
/*                                                                    */
/*                                                                    */
/* EXIT-NORMAL:  NO_ERROR                                             */
/*                                                                    */
/* EXIT-ERROR:  ERROR_TOO_MANY_EVENTS                                 */
/*                                                                    */
/* EFFECTS:  NONE                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:                                               */  
/*                                                                    */
/*    ROUTINES:  NONE                                                 */
/*                                                                    */
/* EXTERNAL REFERENCES:                                               */
/*                                                                    */
/*    ROUTINES:  NONE                                                 */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/

ULONG DequeueEvent (HEVENT hEvent, PSTREAM pStream)
{
   register USHORT i = 0;
   EVENT_QUEUE *pEvent = pStream->pEventQueue;

   // search for the event
   for( ; i<MAX_EVENT_QUEUE; i++, pEvent++ )
   {
      if( pEvent->hEvent == hEvent )
      {
         pEvent->hEvent = (HEVENT)(-1); // clean up the node //
         pEvent->ulCuePoint = 0L;
         break;
      }
   }
   return(NO_ERROR);
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  GetFileStream                                    */
/*                                                                    */
/* DESCRIPTIVE NAME: Get File Stream                                  */
/*                                                                    */
/* FUNCTION:  The function of this routine is to obtain stream        */
/*             for specified system file #                            */
/*                                                                    */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  GetFileStream                                       */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  ULONG ulSysFileNum                                         */
/*                                                                    */
/*                                                                    */
/* EXIT-NORMAL:  pointer to stream data structure                     */
/*                                                                    */
/* EXIT-ERROR:  NULL                                                  */
/*                                                                    */
/* EFFECTS:  NONE                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:                                               */  
/*                                                                    */
/*    ROUTINES:  NONE                                                 */
/*                                                                    */
/* EXTERNAL REFERENCES:                                               */
/*                                                                    */
/*    ROUTINES:  NONE                                                 */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/

PSTREAM GetFileStream (ULONG ulSysFileNum )
{
   PSTREAM pStream = GlobalTable.paStream; // get pointer to active stream
   register USHORT i = 0;

   //search for the stream
   for( ; i < GlobalTable.usMaxNumStreams; i++ )
   {
      if( pStream->ulSysFileNum == ulSysFileNum )
         return( pStream );
      ++pStream;
   }
   return( NULL );
}  // GetFileStream //


/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  GetBestFit                                       */
/*                                                                    */
/* DESCRIPTIVE NAME:  Get best fit for audio data format              */
/*                                                                    */
/* FUNCTION:  The function of this routine is find interface          */
/*            that gives bst fit for required audio format.           */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task time                                                 */
/*                                                                    */
/* ENTRY POINT:  GetBestFit                                           */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  UCHAR FAR *configurationData - far pointer to device       */
/*                      configuration data buffer                     */
/*         UCHAR bNumConfigurations - number of configurations for    */
/*                      device                                        */
/*         UCHAR configurationValue - current configuration           */
/*         UCHAR interface - interface                                */
/*         ULONG FAR *dataType - required data type value, changed    */
/*                           to the best fit if does not match        */
/*         ULONG FAR    *samplingRate - required sampling rate,       */
/*                           changed to the best fit if does not match*/
/*         ULONG FAR    *channels - required no of channels, changed  */
/*                           to the best fit if does not match        */
/*         ULONG FAR    *bitsPerSample- required bits per sample,     */
/*                          changed to the best fit if does not match */
/*         UCHAR FAR    *altInterface - set to the best fit interface */
/*                          number
/*                                                                    */
/* EXIT-NORMAL: TRUE if format descriptor unit found                  */
/*                                                                    */
/* EXIT-ERROR: FALSE if failed to locate format descriptor            */
/*                                                                    */
/* EFFECTS: none                                                      */
/*                                                                    */
/* INTERNAL REFERENCES:  GetNextDescriptor                            */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/

// Format type codes
#define  FORMAT_TYPE_UNDEFINED         0x00
#define  FORMAT_TYPE_I                 0x01
#define  FORMAT_TYPE_II                0x02
#define  FORMAT_TYPE_III               0x03

// Audio Data Format Type I Codes
#define  TYPE_I_UNDEFINED              0x0000
#define  TYPE_I_PCM                    0x0001
#define  TYPE_I_PCM8                   0x0002
#define  TYPE_I_IEEE_FLOAT             0x0003
#define  TYPE_I_ALAW                   0x0004
#define  TYPE_I_MULAW                  0x0005

// Audio Data Format Type II Codes
#define  TYPE_II_UNDEFINED             0x1000
#define  TYPE_II_MPEG                  0x1001
#define  TYPE_II_AC3                   0x1002

// Audio Data Format Type II Codes
#define  TYPE_III_UNDEFINED            0x2000
#define  TYPE_III_IEC1937_AC3          0x2001
#define  TYPE_III_IEC1937_MPEG1_L1     0x2002
#define  TYPE_III_IEC1937_MPEG1_L23    0x2003
#define  TYPE_III_IEC1937_MPEG2_NOEXT  0x2003
#define  TYPE_III_IEC1937_MPEG2_EXT    0x2004
#define  TYPE_III_IEC1937_MPEG2_L1_LS  0x2005
#define  TYPE_III_IEC1937_MPEG2_L23_LS 0x2006


#pragma optimize ("",off)
BOOL GetBestFit( UCHAR FAR *configurationData,
UCHAR        bNumConfigurations,
UCHAR        bConfigurationValue,
UCHAR        interface,
ULONG FAR    *dataType,
ULONG FAR    *samplingRate,
ULONG FAR    *channels,
ULONG FAR    *bitsPerSample,
UCHAR FAR    *altInterface
)
{
   DeviceConfiguration FAR *devConf;
   UCHAR                   configIndex;
   UCHAR FAR               *currBytePtr, FAR *lastBytePtr;
   DeviceDescHead FAR      *descHead;
   DeviceInterface FAR     *interfaceDesc;
   Type1FDescriptor FAR    *formatDescriptor=NULL;
   ASIntfDescriptor FAR    *asIntfDescriptor;
   BOOL                    interfaceFound=FALSE, srcCode=FALSE;
   UCHAR                   curAltInterface, bestAltInterface=0;
   USHORT                  wFormatTag, reqFormatTag;
   ULONG                   minVal[4]={0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff};
   ULONG                   curVal[4];
   ULONG                   samplingFreq, diff, bestRate;
   UCHAR                   samplIndx;
   UCHAR                   bestFitChan=0, bestFitBits=0;
   ULONG                   bestFitRate=0;
   ULONG                   bestFitType=0;

   // convert MMPM/2 data type value to USB value
   switch( *dataType )
   {
      case PCM://DATATYPE_WAVEFORM:
      case ADPCM:
         if( *bitsPerSample==8 )
            reqFormatTag=TYPE_I_PCM8;
         else
            reqFormatTag=TYPE_I_PCM;
         break;
      case DATATYPE_MULAW:
         reqFormatTag=TYPE_I_MULAW;
         break;
      case DATATYPE_ALAW:
         reqFormatTag=TYPE_I_ALAW;
         break;
      case DATATYPE_MPEG1AUDIO:
         reqFormatTag=TYPE_III_IEC1937_MPEG1_L1;
         break;
      default:
         reqFormatTag=0;
         break;
   }

   devConf=(DeviceConfiguration FAR *)configurationData;
   for( configIndex=0; configIndex<bNumConfigurations;
   configIndex++ )
   {
      currBytePtr=(UCHAR FAR *)devConf;
      lastBytePtr=currBytePtr+devConf->wTotalLength;
      descHead=(DeviceDescHead FAR *)(currBytePtr+devConf->bLength);
      if( devConf->bConfigurationValue==bConfigurationValue )
      {  // process required configuration descriptors
         for( descHead=(DeviceDescHead FAR *)(currBytePtr+devConf->bLength); descHead;
         descHead=GetNextDescriptor( descHead, lastBytePtr) )
         {
            switch( descHead->bDescriptorType )
            {
               case DESC_INTERFACE:
                  interfaceDesc=(DeviceInterface FAR *)descHead;
                  interfaceFound= interfaceDesc->bInterfaceNumber==interface; // TRUE if this is required interface
                  curAltInterface=interfaceDesc->bAlternateSetting;  // save current alternate setting value
                  wFormatTag=0;  // reset format tag
                  break;
               case CS_INTERFACE:
                  if( !interfaceFound ) // bypass processing if descriptor does not belong to required interface
                     break;
                  switch( descHead->bDescriptorSubtype )
                  {
                     case AS_GENERAL:
                        asIntfDescriptor=(ASIntfDescriptor FAR *)descHead;
                        wFormatTag=asIntfDescriptor->wFormatTag;  // save interface audio data type value
                        break;
                     case FORMAT_TYPE:
                        srcCode=TRUE;  // format descriptor found
                        formatDescriptor=(Type1FDescriptor FAR *)descHead;

                        // calculate data type discrepancy
                        if( reqFormatTag==wFormatTag )
                           curVal[0]=0;
                        else
                           curVal[0]=wFormatTag;

                        switch( formatDescriptor->bFormatType )
                        {
                           case FORMAT_TYPE_I:
                              formatDescriptor=(Type1FDescriptor FAR *)descHead;
                              // calculate current sampling rate, channel no, bits per sample discrepancies

                              // process number of channels, prefer settings with higher no of channels
                              if( *channels<=(ULONG)formatDescriptor->bNrChannels )
                                 curVal[1]=formatDescriptor->bNrChannels-*channels;
                              else
                                 curVal[1]=(*channels-formatDescriptor->bNrChannels)|0x0000ff00;

                              // process bits per sample
                              if( formatDescriptor->bBitResolution==8 && curVal[0] )
                              {
                                 if( *bitsPerSample<=(ULONG)formatDescriptor->bBitResolution )
                                    curVal[2]=formatDescriptor->bBitResolution-*bitsPerSample|0x0000ff00;
                                 else
                                    curVal[2]=*bitsPerSample-formatDescriptor->bBitResolution;
                              }
                              else
                              {
                                 if( *bitsPerSample<(ULONG)formatDescriptor->bBitResolution )
                                    curVal[2]=formatDescriptor->bBitResolution-*bitsPerSample;
                                 else
                                    curVal[2]=*bitsPerSample-formatDescriptor->bBitResolution;
                              }

                              // process sampling frequiencies
                              if( formatDescriptor->bSamFreqType )
                              {  // discrete number of sampling frequencies
                                 curVal[3]=0xffffffff; bestRate=0;
                                 for( samplIndx=0; samplIndx<formatDescriptor->bSamFreqType; samplIndx++ )
                                 {
                                    samplingFreq=*((ULONG  FAR *)(&formatDescriptor->tSamFreq[0][samplIndx]));
                                    samplingFreq&=0x00ffffff;
                                    diff=samplingFreq>*samplingRate ? samplingFreq-*samplingRate : *samplingRate-samplingFreq;
                                    if( diff<curVal[3] )
                                    {
                                       curVal[3]=diff;
                                       bestRate=samplingFreq;
                                    }
                                 }
                              }
                              else
                              {  // continuos sampling range
                                 curVal[3]=0xffffffff; bestRate=0;
                                 samplingFreq=*((ULONG  FAR *)(&formatDescriptor->tSamFreq[0][0]));
                                 samplingFreq&=0x00ffffff;
                                 if( samplingFreq>*samplingRate )
                                 {
                                    curVal[3]=samplingFreq-*samplingRate;
                                    bestRate=samplingFreq;
                                 }
                                 else
                                 {
                                    samplingFreq=*((ULONG  FAR *)(&formatDescriptor->tSamFreq[0][1]));
                                    samplingFreq&=0x00ffffff;
                                    if( samplingFreq<*samplingRate )
                                    {
                                       curVal[3]=*samplingRate-samplingFreq;
                                       bestRate=samplingFreq;
                                    }
                                    else
                                    {
                                       curVal[3]=0;
                                       bestRate=*samplingRate;
                                    }
                                 }
                              }

                              if( CompBinaries( minVal, curVal, sizeof(minVal)/sizeof(ULONG) ) )
                              {  // save values if current interface is closer to requirements than previous
                                 movmem((PSZ)&minVal, (PSZ)&curVal, sizeof(minVal));
                                 bestFitChan=formatDescriptor->bNrChannels;
                                 bestFitBits=formatDescriptor->bBitResolution;
                                 bestFitRate=bestRate;
                                 bestFitType=wFormatTag;
                                 bestAltInterface=curAltInterface;
                                 break;
                              }
                              break;

                           case FORMAT_TYPE_II:
                              break;

                           case FORMAT_TYPE_III:
                              break;
                           default:
                              break;
                        }
                        break;

                     default:
                        break;
                  }
                  break;
               default:
                  break;
            }
         }
         break;
      }
      devConf=(DeviceConfiguration FAR *)lastBytePtr; // point to next configuration block
   }

   *altInterface=bestAltInterface;
   *samplingRate=bestFitRate;
   *channels=(ULONG)bestFitChan;
   *bitsPerSample=(ULONG)bestFitBits;

   // convert USB data type value to MMPM/2 value
   switch( bestFitType )
   {
      case TYPE_I_PCM8:
      case TYPE_I_PCM:
         *dataType=DATATYPE_WAVEFORM;
         break;
      case TYPE_I_MULAW:
         *dataType=DATATYPE_MULAW;
         break;
      case TYPE_I_ALAW:
         *dataType=DATATYPE_ALAW;
         break;
      case TYPE_III_IEC1937_MPEG1_L1:
         *dataType=DATATYPE_MPEG1AUDIO;
         break;
      default:
         reqFormatTag=DATATYPE_NULL;
         break;
   }

   return(srcCode);
}

/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  CompBinaries                                     */
/*                                                                    */
/* DESCRIPTIVE NAME:  Compare two binary values                       */
/*                                                                    */
/* FUNCTION:  The function of this routine is to compare binary       */
/*            values stored in 2 arrays.                              */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task time                                                 */
/*                                                                    */
/* ENTRY POINT:  CompBinaries                                         */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  ULONG FAR *a - far pointer to the first ULONG array        */
/*                      storing binary value                          */
/*         ULONG FAR *b - far pointer to the second ULONG array       */
/*                      storing binary value                          */
/*         USHORT size - no of elements in arrays                     */
/*                                                                    */
/* EXIT-NORMAL: TRUE if a>b                                           */
/*              FALSE a<=b                                            */
/*                                                                    */
/* EXIT-ERROR: none                                                   */
/*                                                                    */
/* EFFECTS: none                                                      */
/*                                                                    */
/* INTERNAL REFERENCES:  none                                         */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
#pragma optimize ("",on)
BOOL static CompBinaries( ULONG FAR *a, ULONG FAR *b, USHORT size )
{
   USHORT   index;

   for( index=0; index<size; index++ )
   {
      if( a[index]>b[index] )
      {
         return(TRUE);
      }
      else if( a[index]<b[index] )
         break;
   }

   return(FALSE);
}


/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  CheckChannelFeature                              */
/*                                                                    */
/* DESCRIPTIVE NAME:  Check channel feature                           */
/*                                                                    */
/* FUNCTION:  The function of this routine is to check audio channel  */
/*            capability to support specific feature.                 */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task time                                                 */
/*                                                                    */
/* ENTRY POINT:  CheckChannelFeature                                  */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  FUnitDescriptor FAR *featureDesc - far pointer to feature  */
/*                      unit descriptor                               */
/*         USHORT feature - feature to check                          */
/*                      device                                        */
/*         UCHAR channel - channel number to check (0 is used for     */
/*                      master channel)                               */
/*                                                                    */
/* EXIT-NORMAL: TRUE if feature supported by specified channel        */
/*              FALSE if feature is not supported by specified channel*/
/*                                                                    */
/* EXIT-ERROR: FALSE if wrong channel no specified                    */
/*                                                                    */
/* EFFECTS: none                                                      */
/*                                                                    */
/* INTERNAL REFERENCES:  none                                         */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
BOOL CheckChannelFeature( FUnitDescriptor FAR *featureDesc, USHORT feature, UCHAR channel)
{
   USHORT FAR  *chanFeature;
   UCHAR FAR   *featureBMAddr;
   UCHAR FAR   *lastDescByte;


   if( !featureDesc )
      return(FALSE);


   featureBMAddr= ((UCHAR FAR *)&featureDesc->bmaControls)+channel*featureDesc->bControlSize;
   lastDescByte=((UCHAR FAR *)featureDesc)+featureDesc->bLength;

   if( lastDescByte<featureBMAddr+featureDesc->bControlSize )
      return(FALSE);

   chanFeature=(USHORT FAR *)featureBMAddr;  

   return(((*chanFeature)&feature)!=0);
}

/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  GetChannelCount                                  */
/*                                                                    */
/* DESCRIPTIVE NAME:  Get Channel Count                               */
/*                                                                    */
/* FUNCTION:  The function of this routine is to retrieve number of   */
/*            logical channels described in feature unit descriptor.  */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task time                                                 */
/*                                                                    */
/* ENTRY POINT:  GetChannelCount                                      */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  FUnitDescriptor FAR *featureDesc - far pointer to feature  */
/*                      unit descriptor                               */
/*                                                                    */
/* EXIT-NORMAL: no of logical channels described in feature unit      */
/*              (this does not include master channel)                */
/*                                                                    */
/* EXIT-ERROR: none                                                   */
/*                                                                    */
/* EFFECTS: none                                                      */
/*                                                                    */
/* INTERNAL REFERENCES:  none                                         */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
UCHAR GetChannelCount( FUnitDescriptor FAR *featureDesc )
{
   return(featureDesc?(UCHAR )((featureDesc->bLength-7)/featureDesc->bControlSize-1):(UCHAR)0);
}

/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  SetChannelsCapabilities                          */
/*                                                                    */
/* DESCRIPTIVE NAME:  Set Channels Capabilities                       */
/*                                                                    */
/* FUNCTION:  The function of this routine is to                      */
/*                            Set Channels Capabilities               */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task time                                                 */
/*                                                                    */
/* ENTRY POINT:  GetChannelCount                                      */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  FUnitDescriptor FAR *featureDesc - far pointer to feature  */
/*                      unit descriptor                               */
/*                                                                    */
/* EXIT-NORMAL: no of logical channels described in feature unit      */
/*              (this does not include master channel)                */
/*                                                                    */
/* EXIT-ERROR: none                                                   */
/*                                                                    */
/* EFFECTS: none                                                      */
/*                                                                    */
/* INTERNAL REFERENCES:  none                                         */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
VOID SetChannelsCapabilities(USHORT usDeviceIndex, FUnitDescriptor FAR *featureDesc)
{
   register UCHAR ucChannel;

   // clear up channel  nodes, first
   for( ucChannel=0; ucChannel<=gAudioDevices[usDeviceIndex].ucChannels; ucChannel++ )
   {

      gAudioDevices[usDeviceIndex].ulDevCh[ucChannel]     = 0L;
      gAudioDevices[usDeviceIndex].usMaxVolume[ucChannel] = 0;
      gAudioDevices[usDeviceIndex].usMinVolume[ucChannel] = 0;
      gAudioDevices[usDeviceIndex].ucMaxBass[ucChannel]   = 0;
      gAudioDevices[usDeviceIndex].ucMinBass[ucChannel]   = 0;
      gAudioDevices[usDeviceIndex].ucMaxTreble[ucChannel] = 0;
      gAudioDevices[usDeviceIndex].ucMinTreble[ucChannel] = 0;

   }
   //  set up channel capabilities for all avalable controls
   for( ucChannel=0;  ucChannel<=gAudioDevices[usDeviceIndex].ucChannels; ucChannel++ )
   {
      gAudioDevices[usDeviceIndex].ulDevCh[ucChannel] |= CheckChannelFeature(featureDesc, CHANNEL_FEATURE_MUTE,    ucChannel) ? CHANNEL_FEATURE_MUTE   :  0;
      gAudioDevices[usDeviceIndex].ulDevCh[ucChannel] |= CheckChannelFeature(featureDesc, CHANNEL_FEATURE_VOLUME,  ucChannel) ? CHANNEL_FEATURE_VOLUME :  0;
      gAudioDevices[usDeviceIndex].ulDevCh[ucChannel] |= CheckChannelFeature(featureDesc, CHANNEL_FEATURE_BASS,    ucChannel) ? CHANNEL_FEATURE_BASS   :  0;
      gAudioDevices[usDeviceIndex].ulDevCh[ucChannel] |= CheckChannelFeature(featureDesc, CHANNEL_FEATURE_MID,     ucChannel) ? CHANNEL_FEATURE_MID    :  0;
      gAudioDevices[usDeviceIndex].ulDevCh[ucChannel] |= CheckChannelFeature(featureDesc, CHANNEL_FEATURE_TREBLE,  ucChannel) ? CHANNEL_FEATURE_TREBLE :  0;
      gAudioDevices[usDeviceIndex].ulDevCh[ucChannel] |= CheckChannelFeature(featureDesc, CHANNEL_FEATURE_GEQ,     ucChannel) ? CHANNEL_FEATURE_GEQ    :  0;
      gAudioDevices[usDeviceIndex].ulDevCh[ucChannel] |= CheckChannelFeature(featureDesc, CHANNEL_FEATURE_AGAIN,   ucChannel) ? CHANNEL_FEATURE_AGAIN  :  0;
      gAudioDevices[usDeviceIndex].ulDevCh[ucChannel] |= CheckChannelFeature(featureDesc, CHANNEL_FEATURE_DELAY,   ucChannel) ? CHANNEL_FEATURE_DELAY  :  0;
      gAudioDevices[usDeviceIndex].ulDevCh[ucChannel] |= CheckChannelFeature(featureDesc, CHANNEL_FEATURE_BBOOST,  ucChannel) ? CHANNEL_FEATURE_BBOOST :  0;
      gAudioDevices[usDeviceIndex].ulDevCh[ucChannel] |= CheckChannelFeature(featureDesc, CHANNEL_FEATURE_LOUDNESS,ucChannel) ? CHANNEL_FEATURE_LOUDNESS: 0;
   }

   return;
}

/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  SetControlCapabilities                           */
/*                                                                    */
/* DESCRIPTIVE NAME:  SetControlCapabilities                          */
/*                                                                    */
/* FUNCTION:  The function of this routine is                         */
/*                to Set Control Capabilities  fot the device         */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task time                                                 */
/*                                                                    */
/* ENTRY POINT:  SetControlCapabilities                               */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:    UCHAR deviceIndex - USB Audio deice internal index       */
/*           ULONG FAR *pulFlags  - pointer to capability flags       */
/*                                                                    */
/* EXIT-NORMAL: ALWAYS                                                */
/*                                                                    */
/* EXIT-ERROR: none                                                   */
/*                                                                    */
/* EFFECTS: none                                                      */
/*                                                                    */
/* INTERNAL REFERENCES:  none                                         */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/

VOID SetControlCapabilities(UCHAR deviceIndex, ULONG FAR *pulFlags)
{
   register UCHAR ucChannel;

   //set up volume treble and bass capabilities
   for( ucChannel=0; ucChannel<=gAudioDevices[deviceIndex].ucChannels; ucChannel++ )
   {
      *pulFlags|=(gAudioDevices[deviceIndex].ulDevCh[ucChannel]&CHANNEL_FEATURE_VOLUME)?VOLUME:0;
      *pulFlags|=(gAudioDevices[deviceIndex].ulDevCh[ucChannel]&CHANNEL_FEATURE_TREBLE)?TREBLE:0;
      *pulFlags|=(gAudioDevices[deviceIndex].ulDevCh[ucChannel]&CHANNEL_FEATURE_BASS)?BASS:0;
   }


   return; 
}


/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  InitNewDevice                                    */
/*                                                                    */
/* DESCRIPTIVE NAME:  Get Channel Count                               */
/*                                                                    */
/* FUNCTION:  The function of this routine is to initialize new       */
/*        attached USB device for  active stream  playback            */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task time                                                 */
/*                                                                    */
/* ENTRY POINT:  InitNewDevice                                        */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  None                                                       */
/*                                                                    */
/* EXIT-NORMAL: NO_ERROR                                              */
/*                                                                    */
/* EXIT-ERROR: ERROR                                                  */
/*                                                                    */
/* EFFECTS: none                                                      */
/*                                                                    */
/* INTERNAL REFERENCES:  none                                         */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/
                       
RC InitNewDevice( VOID )
{
   PSTREAM pStream         =  GlobalTable.paStream; // pointer to active stream
   ULONG  ulDataType       =  (ULONG)pStream->usMode; // active stream datatype
   ULONG  ulSamplingRate   =  pStream->ulSamplingFreq; // active stream sampling frequence                                               
   ULONG  ulChannels       =  pStream->usChannels; // active stream channels setting
   ULONG  ulBitsPerSample  =  pStream->ulBitsPerSample;  // active stream bits per sample value


   // to prevent errors
   if( !pStream )
      return(ERROR);

   // first, check for new attached USB audio device capabilities
   if( ! GetBestFit((UCHAR FAR *)&gAudioDevices[GlobalTable.ucActiveDeviceIndex].pDeviceInfo->configurationData, // device specific information
   gAudioDevices[GlobalTable.ucActiveDeviceIndex].bNumConfigurations, // no of the configurations for the device
   gAudioDevices[GlobalTable.ucActiveDeviceIndex].devConf->bConfigurationValue, 
   gAudioDevices[GlobalTable.ucActiveDeviceIndex].ucStreamingInterface, // USB streaming interaface value for the device
   (ULONG FAR *)&ulDataType, // data type
   (ULONG FAR *)&ulSamplingRate, // sampling frequence
   (ULONG FAR *)&ulChannels, // channels No
   (ULONG FAR *)&ulBitsPerSample, // bits per sample
   (UCHAR FAR *)&gAudioDevices[GlobalTable.ucActiveDeviceIndex].ucAltInterface) ) // alternative interface
   {
      #ifdef DEBUG
      dsPrint(DBG_CRITICAL, "USBAUD : InitNewDevice : GetBestFit error!\r\n");
      #endif
      return(ERROR);
   }

   // check for correct stream data type
   if( ulDataType!=GlobalTable.paStream->usMode && ulDataType>2 )
   {
      #ifdef DEBUG
      dsPrint2(DBG_CRITICAL, "USBAUD : InitNewDevice error invalid datatype=%xh expected=%lxh\r\n", GlobalTable.paStream->usMode, ulDataType);
      #endif
      return(ERROR);
   }

   // if device cannot support specified stream data setting
   // it allows for sampling rate to be in range +-10%
   if( (ulSamplingRate   >  pStream->ulSamplingFreq+pStream->ulSamplingFreq/10)||
   (ulSamplingRate   <  pStream->ulSamplingFreq-pStream->ulSamplingFreq/10)||
   (ulChannels       !=  pStream->usChannels)||      
   (ulBitsPerSample  !=  pStream->ulBitsPerSample) )
   {
      #ifdef DEBUG
      dsPrint3(DBG_CRITICAL, "USBAUD : InitNewDevice : Error : Invalid format -  freq=%ld bits=%ld chan=%d\r\n",
      pStream->ulSamplingFreq,pStream->ulBitsPerSample,pStream->usChannels);
      dsPrint4(DBG_CRITICAL, "USBAUD : InitNewDevice : expected data format = freq=[%ld..%ld] bits=%ld chan=%d\r\n",
      ulSamplingRate-ulSamplingRate/10,ulSamplingRate+ulSamplingRate/10,ulBitsPerSample,ulChannels);
      #endif

      return(ERROR);
   }

   // save stream data format
   gusChannels      = (USHORT)ulChannels;
   gulBitsPerSample = ulBitsPerSample;
   gulSamplingFreq  = ulSamplingRate;


   {
      register UCHAR ucChannel;

      // all new device channels from 0 (master channel) settings must be updated to match previous ones
      for( ucChannel=0; ucChannel<=gAudioDevices[GlobalTable.ucActiveDeviceIndex].ucChannels;  ucChannel++ )
      {
         // if channel supports VOLUME control setting
         if( gAudioDevices[GlobalTable.ucActiveDeviceIndex].ulDevCh[ucChannel]&CHANNEL_FEATURE_VOLUME )
         {
            #ifdef DEBUG
            dsPrint4(DBG_SPECIFIC, "USBAUD: Set USB chan #%d Volume to = %xh min=%xh max=%xh\r\n", ucChannel, gUSBAC.usLeftVolume, gAudioDevices[GlobalTable.ucActiveDeviceIndex].usMinVolume[ucChannel],gAudioDevices[GlobalTable.ucActiveDeviceIndex].usMaxVolume[ucChannel]);
            #endif
            // set up volume
            SetUnitControl(GlobalTable.ucActiveDeviceIndex, VOLUME_CONTROL, ucChannel, sizeof(gUSBAC.usLeftVolume), (PUCHAR)&gUSBAC.usLeftVolume);
         }
         // if channel supports TREBLE control setting
         if( gAudioDevices[GlobalTable.ucActiveDeviceIndex].ulDevCh[ucChannel]&CHANNEL_FEATURE_TREBLE )
         {
            #ifdef DEBUG
            dsPrint4(DBG_SPECIFIC, "USBAUD: Set USB chan #%d Treble to = %xh min=%xh max=%xh\r\n",ucChannel, gUSBAC.ucLeftTreble, gAudioDevices[GlobalTable.ucActiveDeviceIndex].ucMinTreble[ucChannel],gAudioDevices[GlobalTable.ucActiveDeviceIndex].ucMaxTreble[ucChannel]);
            #endif
            // set up treble
            SetUnitControl(GlobalTable.ucActiveDeviceIndex, TREBLE_CONTROL, ucChannel, sizeof(gUSBAC.ucLeftTreble), (PUCHAR)&gUSBAC.ucLeftTreble);
         }

         // if channel support BASS control setting
         if( gAudioDevices[GlobalTable.ucActiveDeviceIndex].ulDevCh[ucChannel]&CHANNEL_FEATURE_BASS )
         {
            #ifdef DEBUG
            dsPrint4(DBG_SPECIFIC, "USBAUD: Set USB chan #%d Bass to = %xh min=%xh max=%xh\r\n", 
            ucChannel, gUSBAC.ucLeftBass,gAudioDevices[GlobalTable.ucActiveDeviceIndex].ucMinBass[ucChannel],gAudioDevices[GlobalTable.ucActiveDeviceIndex].ucMaxBass[ucChannel]);
            #endif
            // set up bass
            SetUnitControl(GlobalTable.ucActiveDeviceIndex, BASS_CONTROL, ucChannel, sizeof(gUSBAC.ucLeftBass), (PUCHAR)&gUSBAC.ucLeftBass);
         }
      }
   }
   return(NO_ERROR);
}
/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  SetStreamState                                   */
/*                                                                    */
/* DESCRIPTIVE NAME:  SetStreamState                                  */
/*                                                                    */
/* FUNCTION:    The function of this routine is to set                */
/*                specified stream state                              */
/*                                                                    */
/* NOTES: This function is not required for MMPM/2                    */
/*                                                                    */
/* CONTEXT: Task time                                                 */
/*                                                                    */
/* ENTRY POINT:  GetChannelCount                                      */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT: PSTREAM pStream - pointer to the stream                     */
/*        USHORT usState - the state of the stream                    */
/*                                                                    */
/*                                                                    */
/* EXIT-NORMAL: always                                                */
/*                                                                    */
/* EXIT-ERROR: N/A                                                    */
/*                                                                    */
/* EFFECTS: none                                                      */
/*                                                                    */
/* INTERNAL REFERENCES:  none                                         */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/


VOID SetStreamState(PSTREAM pStream, USHORT usState)
{
   //check for the correct stream first
   if( !pStream )
      return;
// this function is not requried by MMPM/2 
   switch( usState )
   {
      case STREAM_STATE_STOP:
         break;
      case STREAM_STATE_START:
         break;
      case STREAM_STATE_PAUSE:
         break;
      case STREAM_STATE_RESUME:
         break;
      default:
         break;
   }
   return;
}
/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  GetUnitControlValue                              */
/*                                                                    */
/* DESCRIPTIVE NAME:  Get Unit Control Value                          */
/*                                                                    */
/* FUNCTION:  The function of this routine is to calculate USB        */
/*                related setting control value number of             */
/*                                                                    */
/* CONTEXT: Task time                                                 */
/*                                                                    */
/* ENTRY POINT:  GetUnitControlValue                                  */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  USHORT usValue  - OS/2 standard control value              */
/*      USHORT usMinValue  - minimal USB control value for the device */
/*      USHORT usMaxValue  - maximum USB control value for the device */
/*                                                                    */
/*                                                                    */
/* EXIT-NORMAL: Calculated value of the control setting for USB device*/
/*                                                                    */
/* EXIT-ERROR: none                                                   */
/*                                                                    */
/* EFFECTS: none                                                      */
/*                                                                    */
/* INTERNAL REFERENCES:  none                                         */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/

USHORT GetUnitControlValue(USHORT usValue, USHORT usMinValue, USHORT usMaxValue )
{
   USHORT usPerc=((usValue+USB_MAX_VOLUME)/2)/(USB_MAX_VOLUME/100); // get value in percents
   USHORT usDevPerc=(usMaxValue-usMinValue)/100; // get device specific control setting value in percents

   return( usMinValue+usDevPerc*usPerc ); // calculate and return device control stting value
}
/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  GetNextBuffer                                    */
/*                                                                    */
/* DESCRIPTIVE NAME:  Get Next Buffer                                 */
/*                                                                    */
/* FUNCTION:  The function of this routine is to obtain pointer       */
/*                to next buffer.                                     */
/*                                                                    */
/* CONTEXT: Task time                                                 */
/*                                                                    */
/* ENTRY POINT:  GetNextBuffer                                        */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  PSTREAM pStream - a pointer to a stream                    */
/*                                                                    */
/* EXIT-NORMAL: STATUS_DONE                                           */
/*                                                                    */
/* EXIT-ERROR: STATUS_DONE|STERR  - next buffer does not exist        */
/*                                                                    */
/* EFFECTS: none                                                      */
/*                                                                    */
/* INTERNAL REFERENCES:  none                                         */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/

RC GetNextBuffer (PSTREAM pStream)
{
   register USHORT i;

   if( !pStream )
      return(STATUS_DONE|STERR);

   // check the next buffer for data
   i  = pStream->usCurrIOBuffIndex;
   pStream->usLastIOBuffIndex = i;

   // check for wrap
   if( ++i==MAXIOBUFFS )
      i=0;


   if( !pStream->IOBuff[i].bEmpty )
      pStream->usCurrIOBuffIndex=i;
   else
   { // the next buffer is empty (already playbacked)
    #ifdef DEBUG
      dsPrint(DBG_SPECIFIC, "USBAUD: GetNextBuffer : No more buffers to send\r\n");
    #endif
      return(STATUS_DONE|STERR);
   }
   // next buffer can be played
   return(STATUS_DONE);
}

/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  FindMatchingSampleRate                           */
/*                                                                    */
/* DESCRIPTIVE NAME:  Find Matching Sample Rate                       */
/*                                                                    */
/* FUNCTION:  The function of this routine is to find closest possible*/
/*             sampling rate                                          */  
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task time                                                 */
/*                                                                    */
/* ENTRY POINT:  FindMatchingSampleRate                               */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT: PULONG pulSampleRate - pointer to sampling rate value       */
/*                                                                    */
/* EXIT-NORMAL: Sampling rate                                         */
/*                                                                    */
/* EXIT-ERROR: none                                                   */
/*                                                                    */
/* EFFECTS: none                                                      */
/*                                                                    */
/* INTERNAL REFERENCES:  none                                         */
/*                                                                    */
/* EXTERNAL REFERENCES:  None                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/

#define distance(x, y) ((x>y)? x-y : y-x)

USHORT FindMatchingSampleRate(PULONG pulSampleRate)
{
   ULONG  ulMinDistance = 0x7fffffff;
   ULONG  aulSuppSampleRates[] = {11025, 22050, 44100, 8000};  //possible sampling rate setting
   USHORT usMatching;
   USHORT us;

   for( us = 0; us < 4; us++ )
   {
      ULONG ulDistance = distance( *pulSampleRate, aulSuppSampleRates[us] );
      if( ulDistance < ulMinDistance )
      {
         ulMinDistance = ulDistance;
         usMatching = us;
      }
   }
   *pulSampleRate = aulSuppSampleRates[usMatching];
   return(usMatching);
}


/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  GetBuffSize                                      */
/*                                                                    */
/* DESCRIPTIVE NAME:  Get Audio Data Buffer Size                      */
/*                                                                    */
/* FUNCTION:  The function of this routine is to calculate USB related*/
/*            audio buffer size                                       */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task time                                                 */
/*                                                                    */
/* ENTRY POINT:  GetBuffSize                                          */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  PSTREAM pStream - pointer to the active stream             */
/*                                                                    */
/* EXIT-NORMAL: ALWAYS                                                */
/*                                                                    */
/* EXIT-ERROR: NEVER                                                  */
/*                                                                    */
/* EFFECTS: NONE                                                      */
/*                                                                    */
/* INTERNAL REFERENCES:  NONE                                         */
/*                                                                    */
/* EXTERNAL REFERENCES:  NONE                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/

ULONG GetBuffSize(PSTREAM pStream)
{
   // if invalid data, then return default value for USB buffer size
   if( !pStream || !pStream->usFrameSize ) 
      return(DEFAULT_IOBUFF_SIZE);

   //else calculate value 
   return((DEFAULT_IOBUFF_SIZE/pStream->usFrameSize+1)*pStream->usFrameSize);
}


/********************* START OF SPECIFICATIONS *********************
* SUBROUTINE NAME: GetStreamEntry
*
* DESCRIPTIVE NAME: Get the stream table entry.
*
* FUNCTION: To search the stream table finding a match with the given parm.
*
* NOTES: This routine is called internally.
*
* ENTRY POINTS:
*
*     LINKAGE:   CALL near
*
* INPUT: pointer to stream table, stream handle to find
*
* EXIT-NORMAL: NO_ERROR
*
* EXIT_ERROR: ERROR_INVALID_STREAM if stream not found in table
*
* INTERNAL REFERENCES: none
*
* EXTERNAL REFERENCES: none
*
*********************** END OF SPECIFICATIONS **********************/
RC GetStreamEntry(PSTREAM FAR *ppStream, HSTREAM hStream)
{
   register USHORT  i = 0;
   PSTREAM  pStream = *ppStream;

   if( !pStream )
      return(ERROR_INVALID_STREAM);

   while( pStream->hStream != hStream )
   {            // find stream entry
      if( ++i >= GlobalTable.usMaxNumStreams )
         return(ERROR_INVALID_STREAM);
      pStream++;
   };
   *ppStream = pStream;
   return(NO_ERROR);
}

