/*=============================================================================

FILE: OEMSound.c

SERVICES: OEM Functions for Basic Sound Services

GENERAL DESCRIPTION:
A sample implementation of the Sound functions. The functions in this
file are essentially wrappers to QCT's Sound functions.
  
PUBLIC CLASSES AND STATIC FUNCTIONS:

   OEMSound_PlayTone

INITIALIZATION AND SEQUENCING REQUIREMENTS:
No additional module-level initialization or sequencing requirements.

        Copyright © 1999-2002 QUALCOMM Incorporated.
               All Rights Reserved.
            QUALCOMM Proprietary/GTDR
=============================================================================*/

/*-------------------------------------------------------------------
            Defines
-------------------------------------------------------------------*/
#include <stdlib.h>
#include "OEMFeatures.h"

#ifdef AEE_SIMULATOR
#define TG  T_G
#define TARG_H "target.h"
#endif

/*-------------------------------------------------------------------
            Include Files
-------------------------------------------------------------------*/
#include "snd.h"


#include "OEMSound.h"
#include "OEMCriticalSection.h"
#include "AEE_OEMSound.h"
#include "AEEComdef.h"
#include "AEEStdLib.h"

/*-------------------------------------------------------------------
            Type Declarations
-------------------------------------------------------------------*/
typedef struct
{
   uint16   sndStatus[(int)AEE_SOUND_LAST]; // snd_status_type
   uint16   sndDevice[(int)AEE_SOUND_DEVICE_LAST]; // snd_device_type
   uint16   sndMethod[(int)AEE_SOUND_METHOD_LAST]; // snd_method_type
   uint16   sndToneBase; // == SND_FIRST_TDB_TONE
   uint16   sndControlBase; // == SND_FIRST_CONTROL_TONE
} AEESoundCaps;

typedef struct CAudioDevice
{
   int32          m_nRefs;

   AEESoundInfo   m_siSave;

   boolean        m_bVibratorON;
} CAudioDevice;

/*-------------------------------------------------------------------
            Function Prototypes
-------------------------------------------------------------------*/
// Internal Callback functions
static void OEMSound_StatusCB( const void * pClientData, snd_status_type status );
static void OEMSound_VolumeCB( const void * pClientData, snd_status_type status, uint16 wVolume );
static void OEMSound_LevelCB( const void * pClientData, snd_status_type status, uint16 wLevel );

// AEE to/from snd enum conversions
static AEESoundStatus GetAEESoundStatus(snd_status_type e);
static snd_device_type GetSndDevice(AEESoundDevice e);
static snd_method_type GetSndMethod(AEESoundMethod e);
static snd_tone_type GetSndTone(AEESoundTone e);

#if defined(FEATURE_OEMSOUND_NATIVE_IF)
//
// Copy current sound info from native layer.
// Do not modify psi->eMthod and psi->eAPath
//
extern void uisnd_GetSoundInfo(AEESoundInfo * psi);

//
// Set (update) current sound info in native layer.
// psi->eMthod and psi->eAPath are of no consequence.
//
extern void uisnd_SetSoundInfo(AEESoundInfo * psi);
#endif // FEATURE_OEMSOUND_NATIVE_IF

/*-------------------------------------------------------------------
            Static variable Definitions
-------------------------------------------------------------------*/
static AEESoundCaps  gsSoundCaps;

static CAudioDevice  gsDevice;

static OEMCriticalSection gSoundCriticalSection;

/*==================================================================
Function: OEMSound_Init

Description: Maps Sound enumerations to AEE_SOUNDPLAYER enumerations

Prototype:
   void OEM_GetMMCaps( AEEMMCaps * pCaps );

Parameter(s):  
   pCaps [out]:  MM capabilities structure

Return Value:  Floating point 
Comments:      None
Side Effects:  None
See Also:      None

==================================================================*/
void OEMSound_Init(void)
{
   // Mapping: snd_status_type to AEESoundStatus
   gsSoundCaps.sndStatus[(int)AEE_SOUND_UNKNOWN]     = (uint16) SND_FAILURE;
   gsSoundCaps.sndStatus[(int)AEE_SOUND_SUCCESS]     = (uint16) SND_SUCCESS;
   gsSoundCaps.sndStatus[(int)AEE_SOUND_PLAY_DONE]   = (uint16) SND_PLAY_DONE;
   gsSoundCaps.sndStatus[(int)AEE_SOUND_FAILURE]     = (uint16) SND_FAILURE;

   // Mapping: snd_device_type to AEESoundDevice
   gsSoundCaps.sndDevice[(int)AEE_SOUND_DEVICE_UNKNOWN]    = (uint16) SND_DEVICE_MAX;
   gsSoundCaps.sndDevice[(int)AEE_SOUND_DEVICE_HANDSET]    = (uint16) SND_DEVICE_HANDSET;
   gsSoundCaps.sndDevice[(int)AEE_SOUND_DEVICE_HFK]        = (uint16) SND_DEVICE_HFK;
   gsSoundCaps.sndDevice[(int)AEE_SOUND_DEVICE_HEADSET]    = (uint16) SND_DEVICE_HEADSET;
#ifdef FEATURE_ANALOG_HFK
   gsSoundCaps.sndDevice[(int)AEE_SOUND_DEVICE_AHFK]       = (uint16) SND_DEVICE_AHFK;
#else
   gsSoundCaps.sndDevice[(int)AEE_SOUND_DEVICE_AHFK]       = (uint16) SND_DEVICE_MAX;
#endif
#ifdef FEATURE_STEREO_DAC
   gsSoundCaps.sndDevice[(int)AEE_SOUND_DEVICE_SDAC]       = (uint16) SND_DEVICE_SDAC;
#else
   gsSoundCaps.sndDevice[(int)AEE_SOUND_DEVICE_SDAC]       = (uint16) SND_DEVICE_MAX;
#endif
#ifdef FEATURE_TTY
   gsSoundCaps.sndDevice[(int)AEE_SOUND_DEVICE_TTY_HFK]    = (uint16) SND_DEVICE_TTY_HFK;
   gsSoundCaps.sndDevice[(int)AEE_SOUND_DEVICE_TTY_HEADSET]= (uint16) SND_DEVICE_TTY_HEADSET;
#else
   gsSoundCaps.sndDevice[(int)AEE_SOUND_DEVICE_TTY_HFK]    = (uint16) SND_DEVICE_MAX;
   gsSoundCaps.sndDevice[(int)AEE_SOUND_DEVICE_TTY_HEADSET]= (uint16) SND_DEVICE_MAX;
#endif
   gsSoundCaps.sndDevice[(int)AEE_SOUND_DEVICE_CURRENT]    = (uint16) SND_DEVICE_CURRENT;
#ifdef FEATURE_SPEAKER_PHONE
   gsSoundCaps.sndDevice[(int)AEE_SOUND_DEVICE_SPEAKER]    = (uint16) SND_DEVICE_SPEAKER_PHONE;
#else
   gsSoundCaps.sndDevice[(int)AEE_SOUND_DEVICE_SPEAKER]    = (uint16) SND_DEVICE_MAX;
#endif

   // Mapping: snd_method_type to AEESoundMethod
   gsSoundCaps.sndMethod[(int)AEE_SOUND_METHOD_UNKNOWN]    = (uint16) SND_METHOD_MAX;
   gsSoundCaps.sndMethod[(int)AEE_SOUND_METHOD_VOICE]      = (uint16) SND_METHOD_VOICE;
   gsSoundCaps.sndMethod[(int)AEE_SOUND_METHOD_BEEP]       = (uint16) SND_METHOD_KEY_BEEP;
   gsSoundCaps.sndMethod[(int)AEE_SOUND_METHOD_MESSAGE]    = (uint16) SND_METHOD_MESSAGE;
   gsSoundCaps.sndMethod[(int)AEE_SOUND_METHOD_RING]       = (uint16) SND_METHOD_RING;
#ifdef FEATURE_CLICK
   gsSoundCaps.sndMethod[(int)AEE_SOUND_METHOD_CLICK]      = (uint16) SND_METHOD_CLICK;
#else
   gsSoundCaps.sndMethod[(int)AEE_SOUND_METHOD_CLICK]      = (uint16) SND_METHOD_MAX;
#endif
#ifdef FEATURE_AUDIO_FORMAT
   gsSoundCaps.sndMethod[(int)AEE_SOUND_METHOD_MIDI]       = (uint16) SND_METHOD_MIDI;
#else
   gsSoundCaps.sndMethod[(int)AEE_SOUND_METHOD_MIDI]       = (uint16) SND_METHOD_MAX;
#endif
   gsSoundCaps.sndMethod[(int)AEE_SOUND_METHOD_AUX]        = (uint16) SND_METHOD_AUX;

   gsSoundCaps.sndToneBase = SND_FIRST_TDB_TONE;
   gsSoundCaps.sndControlBase = SND_FIRST_CONTROL_TONE;
}

/*===========================================================================
FUNCTION OEMSound_NewInstance

DESCRIPTION
  This function increases ref count of underlying audio device. It returns
  current AEESoundInfo.

DEPENDENCIES
  None.

RETURN VALUE
  None.

SIDE EFFECTS
  None.
===========================================================================*/
int OEMSound_NewInstance(AEESoundInfo * psi)
{
   if (!psi)
      return EFAILED;

   gsDevice.m_nRefs++;
   OEMSound_Get(psi);
   if (1 == gsDevice.m_nRefs)
      OEMSound_Get(&gsDevice.m_siSave);

   return SUCCESS;
}

/*===========================================================================
FUNCTION OEMSound_DeleteInstance

DESCRIPTION
  This function decreases ref count of underlying audio device and restores
  the default settings when ref count goes to zero.

DEPENDENCIES
  None.

RETURN VALUE
  None.

SIDE EFFECTS
  None.
===========================================================================*/
void OEMSound_DeleteInstance(AEESoundInfo * psi)
{
//   AEESoundInfo   si;

   if (gsDevice.m_nRefs <= 0)
      return;

   if (--gsDevice.m_nRefs > 0)
      return;

   //
   // Restore the state to that before the creation of first ISound instance.
   // Note: 
   // (1) Here only device setting is restored
   // (2) OEMs can restore the volume settings for various methods.
//   OEMSound_Get(&si);
//   if (si.eDevice != gsDevice.m_siSave.eDevice ||
//       si.eEarMuteCtl != gsDevice.m_siSave.eEarMuteCtl ||
//       si.eMicMuteCtl != gsDevice.m_siSave.eMicMuteCtl )
//   {
//      snd_set_device( GetSndDevice(gsDevice.m_siSave.eDevice), 
//                      (snd_mute_control_type)gsDevice.m_siSave.eEarMuteCtl, 
//                      (snd_mute_control_type)gsDevice.m_siSave.eMicMuteCtl, 
//                      NULL,
//                      NULL);
//   }
}

void OEMSound_Get(AEESoundInfo * psi)
{
   if (!psi)
      return;

   //
   // NOTE to OEMs:
   // (1) Get the sound device info from your native layer.
   // (2) Do not modify psi->eMethod.
   //
#if defined(FEATURE_OEMSOUND_NATIVE_IF)
   uisnd_GetSoundInfo(psi);
#endif
}

/*===========================================================================

FUNCTION OEMSound_SetDevice

DESCRIPTION
  This function issues a command to set the sound output device.

DEPENDENCIES
  None.

RETURN VALUE
  None.

SIDE EFFECTS
  None.

===========================================================================*/
void OEMSound_SetDevice(AEESoundInfo * psi, void * pUser)
{
   //
   // NOTE to OEMs:
   // (1) Update this sound device in your native layer.
   // (2) Ignore psi->eMethod.
   //
#if defined(FEATURE_OEMSOUND_NATIVE_IF)
   uisnd_SetSoundInfo(psi);
#endif

   snd_set_device( GetSndDevice(psi->eDevice), 
                   (snd_mute_control_type)psi->eEarMuteCtl, 
                   (snd_mute_control_type)psi->eMicMuteCtl, 
                   &OEMSound_StatusCB, 
                   pUser);
}

/*===========================================================================
FUNCTION OEMSound_PlayTone

DESCRIPTION
  This function issues a command to play a specified tone.

DEPENDENCIES
  None.

RETURN VALUE
  None.

SIDE EFFECTS
  None.
===========================================================================*/
void OEMSound_PlayTone(AEESoundInfo * psi, AEESoundToneData* toneData, void * pUser)
{
   snd_tone_start( GetSndDevice(psi->eDevice),
                   GetSndMethod(psi->eMethod), 
                   GetSndTone(toneData->eTone), 
                   toneData->wDuration, 
                   (snd_apath_type)psi->eAPath, 
                   &OEMSound_StatusCB, 
                   pUser);
}

/*===========================================================================
FUNCTION OEMSound_PlayToneList

DESCRIPTION
  This function issues a command to play a list of tones

DEPENDENCIES
  None.

RETURN VALUE
  None.

SIDE EFFECTS
  None.
===========================================================================*/
void OEMSound_PlayToneList(AEESoundInfo * psi, AEESoundToneData * pToneData, uint16 wDataLen, void * pUser)
{
   // No implementation required. Is here just to make linker happy.
}

/*===========================================================================
FUNCTION OEMSound_PlayFreqTone

DESCRIPTION
  This function issues a command to play a specified pair of tone frequencies. 

DEPENDENCIES
  None.

RETURN VALUE
  None.

SIDE EFFECTS
  None.
===========================================================================*/
void OEMSound_PlayFreqTone(AEESoundInfo * psi, uint16 wHiFreq, uint16 wLoFreq, uint16 wDuration, void * pUser)
{
   snd_freq_tone_start( GetSndDevice(psi->eDevice),
                        GetSndMethod(psi->eMethod), 
                        wHiFreq, 
                        wLoFreq, 
                        wDuration, 
                        (snd_apath_type) psi->eAPath,
                        &OEMSound_StatusCB, 
                        pUser);
}

/*===========================================================================
FUNCTION OEMSound_StopTone

DESCRIPTION
  This function issues a command to stop playing a single tone or a playlist

DEPENDENCIES
  None.

RETURN VALUE
  None.

SIDE EFFECTS
  None.
===========================================================================*/
void OEMSound_StopTone(boolean bPlayList, void * pUser)
{
   // bPlayList is not used except for MSM5000
   snd_tone_stop(&OEMSound_StatusCB, pUser);
}

/*===========================================================================
FUNCTION OEMSound_GetLevels

DESCRIPTION
 This function returns the number of volume levels supported for the 
  device/class pair. It is called when user issues ISOUND_GetVolume() or
  ISOUND_SetVolume(). 

DEPENDENCIES
  None.

RETURN VALUE
  None.

SIDE EFFECTS
  None.
===========================================================================*/
void OEMSound_GetLevels(AEESoundInfo * psi, void * pUser)
{
   snd_get_levels( GetSndDevice(psi->eDevice),
                   GetSndMethod(psi->eMethod),
                   &OEMSound_LevelCB, 
                   pUser);
}

/*===========================================================================
FUNCTION OEMSound_SetVolume

DESCRIPTION
  This function issues a command to set the volume of sound device/class pair.

DEPENDENCIES
  None.

RETURN VALUE
  None.

SIDE EFFECTS
  None.
===========================================================================*/
void OEMSound_SetVolume(AEESoundInfo * psi, uint16 wLevel, void * pUser)
{
   snd_set_volume(SND_DEVICE_CURRENT,
                  GetSndMethod(psi->eMethod), 
                  (uint8)wLevel, 
                  &OEMSound_StatusCB, 
                  pUser);
}

/*===========================================================================
FUNCTION OEMSound_SetVolume

DESCRIPTION
  This function issues a command to get the volume level of device/class pair. 

DEPENDENCIES
  None.

RETURN VALUE
  None.

SIDE EFFECTS
  None.
===========================================================================*/
void OEMSound_GetVolume(AEESoundInfo * psi, void * pUser)
{
   snd_get_volume(GetSndDevice(psi->eDevice),
                  GetSndMethod(psi->eMethod),
                  &OEMSound_VolumeCB, 
                  pUser);

}

/*==================================================================
Function: OEM_Vibrate

Description:
   It causes the handset to vibrate for the specified amount to time
   if the feature is supported; otherwise, it doesn't do anything.
                           
Prototype:
   void OEM_Vibrate(uint16 u16Duration);

Parameter(s):
   uint16 u16Duration: Duration of vibration in milliseconds

Return Value: 
   None.

Comments:
   None.

Side Effects:
   The result of the operation will be sent to the client via the
   callback function pointer.

See Also:
   AEESound_StopVibrate

==================================================================*/
void OEMSound_Vibrate(uint16 wDuration, void * pUser)
{
   static snd_compact_tone_type VibBuf[] = {
      { SND_1, {1000} },
      { SND_STOP, {0} }
   };

   static snd_compact_sound_type VibSound = {
      SND_COMPACT_SOUND,
      VibBuf
   };

   // Set the duration of vibration
   VibBuf[0].param.duration_ms = wDuration;

   snd_sound_start( SND_DEVICE_CURRENT, 
                    SND_METHOD_AUX, 
                    (snd_sound_type *) &VibSound,
                    SND_PRIO_LOW, 
                    SND_APATH_LOCAL,
                    NULL, // no callback and pClientData specified
#ifndef AEE_SIMULATOR
                    NULL 
#else
                    (void *)&wDuration
#endif
                  );

   gsDevice.m_bVibratorON = TRUE;
}

/*==================================================================
Function: OEMSound_StopVibrate

Description:
   It stops the current vibration.
                           
Prototype:
   void OEM_StopVibrate();

Parameter(s):
   None.

Return Value: 
   None.

Comments:
   None.

Side Effects:
   The result of the operation will be sent to the client via the
   callback function pointer.

See Also:
   AEESound_Vibrate

==================================================================*/
void OEMSound_StopVibrate(void * pUser)
{
   snd_sound_stop( SND_PRIO_LOW, NULL, NULL );

   gsDevice.m_bVibratorON = FALSE;
}

/*==================================================================
Function: OEMSound_IsVibratorON

Description:
   Can be used by other OEM layer code to check if vibrator is ON.
This isn't exposed to AEE layer and isn't included in OEMSound.h.

Prototype:
   void OEMSound_IsVibratorON();

Return Value: 
   TRUE - Vibrator is ON.
   FALSE - Vibrator is OFF

==================================================================*/
boolean OEMSound_IsVibratorON(void)
{
   return gsDevice.m_bVibratorON;
}
//
// Internal callbacks. Call corresponding AEE ISound callbacks
//
static void OEMSound_StatusCB( const void * pClientData, snd_status_type status )
{
   OEMCriticalSection_Enter(&gSoundCriticalSection);
   AEESound_StatusCB(pClientData, GetAEESoundStatus(status));
   OEMCriticalSection_Leave(&gSoundCriticalSection);
}

static void OEMSound_VolumeCB( const void * pClientData, snd_status_type status, uint16 wVolume )
{
   OEMCriticalSection_Enter(&gSoundCriticalSection);
   AEESound_VolumeCB(pClientData, GetAEESoundStatus(status), wVolume);
   OEMCriticalSection_Leave(&gSoundCriticalSection);
}

static void OEMSound_LevelCB( const void * pClientData, snd_status_type status, uint16 wLevel )
{
   OEMCriticalSection_Enter(&gSoundCriticalSection);
   AEESound_LevelCB(pClientData, GetAEESoundStatus(status), wLevel);
   OEMCriticalSection_Leave(&gSoundCriticalSection);
}

//
// AEE to/from SND enum conversion functions
//
static AEESoundStatus GetAEESoundStatus(snd_status_type e)
{ 
   int i;
   AEESoundStatus eResult = AEE_SOUND_UNKNOWN;
   for (i = 1; i < (int)AEE_SOUND_LAST && (uint16)e != gsSoundCaps.sndStatus[i]; i++)
      ;

   if (i < (int)AEE_SOUND_LAST)
      eResult = (AEESoundStatus) i;

   return eResult;
}

static snd_device_type GetSndDevice(AEESoundDevice e)
{
   if (e < AEE_SOUND_DEVICE_LAST)
      return (snd_device_type) gsSoundCaps.sndDevice[(int)e];
   else
      return (snd_device_type) gsSoundCaps.sndDevice[0];
}

static snd_method_type GetSndMethod(AEESoundMethod e)
{
   if (e < AEE_SOUND_METHOD_LAST)
      return (snd_method_type) gsSoundCaps.sndMethod[(int)e];
   else
      return (snd_method_type) gsSoundCaps.sndMethod[0];
}

static snd_tone_type GetSndTone(AEESoundTone e)
{
   if (e == AEE_TONE_SILENCE)
      return (snd_tone_type)(gsSoundCaps.sndControlBase);
   else if (e == AEE_TONE_PTT_ALERT_ARRIVAL)
      return (snd_tone_type)((uint16)AEE_TONE_RING_D5 + gsSoundCaps.sndToneBase);
   else if (e == AEE_TONE_PTT_CALL_ARRIVAL)
      return (snd_tone_type)((uint16)AEE_TONE_RING_D5 + gsSoundCaps.sndToneBase);
   else if (e == AEE_TONE_PTT_FAIL)
      return (snd_tone_type)((uint16)AEE_TONE_RING_D + gsSoundCaps.sndToneBase);
   else if (e == AEE_TONE_PTT_FLOOR_DENY)
      return (snd_tone_type)((uint16)AEE_TONE_RING_A5 + gsSoundCaps.sndToneBase);
   else if (e == AEE_TONE_PTT_FLOOR_GRANT)
      return (snd_tone_type)((uint16)AEE_TONE_RING_G5 + gsSoundCaps.sndToneBase);
   else if (e == AEE_TONE_PTT_FLOOR_INTERRUPTION)
      return (snd_tone_type)((uint16)AEE_TONE_RING_A5 + gsSoundCaps.sndToneBase);
   else if (e == AEE_TONE_PTT_ADD_MEMBER)
      return (snd_tone_type)((uint16)AEE_TONE_RING_C4 + gsSoundCaps.sndToneBase);
   else if (e == AEE_TONE_PTT_SUCCESS)
      return (snd_tone_type)((uint16)AEE_TONE_RING_G5 + gsSoundCaps.sndToneBase);
   else if (e == AEE_TONE_PTT_TALK_ANNOUNCE)
      return (snd_tone_type)((uint16)AEE_TONE_RING_E5 + gsSoundCaps.sndToneBase);
   else if (e == AEE_TONE_PTT_FLOOR_AVAILABLE)
      return (snd_tone_type)((uint16)AEE_TONE_RING_E5 + gsSoundCaps.sndToneBase);
   else
      return (snd_tone_type)((uint16)e + gsSoundCaps.sndToneBase);
}
