2193 lines
71 KiB
C
Executable File
2193 lines
71 KiB
C
Executable File
/******************************************************************************
|
|
Copyright (C), 2001-2016, Allwinner Tech. Co., Ltd.
|
|
******************************************************************************
|
|
File Name : audio_hw.c
|
|
Version : Initial Draft
|
|
Author : Allwinner BU3-PD2 Team
|
|
Created : 2016/05/25
|
|
Last Modified :
|
|
Description : mpi functions implement
|
|
Function List :
|
|
History :
|
|
******************************************************************************/
|
|
|
|
#define LOG_NDEBUG 0
|
|
#define LOG_TAG "audio_hw"
|
|
#include <utils/plat_log.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
#include <pthread.h>
|
|
#include <stdbool.h>
|
|
#include <memory.h>
|
|
#include <sys/prctl.h>
|
|
|
|
#include <SystemBase.h>
|
|
#include <media_common_aio.h>
|
|
#include <audio_hw.h>
|
|
#include <ConfigOption.h>
|
|
|
|
#define PlaybackRateDmix_DefaultSampleRate (16000) //ref to pcm.PlaybackRateDmix of asound.conf
|
|
#define PlaybackRateDmix_DefaultChnCnt (1)
|
|
#define PlaybackRateDmix_DefaultPeriodSize (960)
|
|
|
|
#if (MPPCFG_AEC == OPTION_AEC_ENABLE)
|
|
#if (MPPCFG_AEC_LIB == OPTION_AEC_LIBRARY_WEBRTC)
|
|
#include <WebRtcAec.h>
|
|
#elif(MPPCFG_AEC_LIB == OPTION_AEC_LIBRARY_UVOICE)
|
|
#include <UvoiceAec.h>
|
|
#endif
|
|
#endif
|
|
|
|
#if (MPPCFG_ANS == OPTION_ANS_ENABLE)
|
|
#if (MPPCFG_ANS_LIB == OPTION_ANS_LIBRARY_WEBRTC)
|
|
#include <WebRtcAns.h>
|
|
#elif(MPPCFG_ANS_LIB == OPTION_ANS_LIBRARY_LSTM)
|
|
#include <LstmAns.h>
|
|
#elif(MPPCFG_ANS_LIB == OPTION_ANS_LIBRARY_NOSC)
|
|
#include <NoscAns.h>
|
|
#endif
|
|
#endif
|
|
|
|
#if (MPPCFG_AGC == OPTION_AGC_ENABLE)
|
|
//#include <agc_m.h>
|
|
#include <agc_float.h>
|
|
#endif
|
|
|
|
#include "cdx_list.h"
|
|
|
|
//#define SOUND_CARD "default:CARD=audiocodec"
|
|
|
|
// #define SOUND_CARD_AUDIOCODEC "default"
|
|
#define PCM_HANDLE_IDENTIFIER_PLAY "PlaybackRateDmix"
|
|
#define SOUND_CARD_AUDIOCODEC_PLAY "plug:PlaybackRateDmix"
|
|
//#define SOUND_CARD_AUDIOCODEC_CAP "CaptureMic" //"hw:0,0"
|
|
#define SOUND_CARD_SNDHDMI "hw:1,0"
|
|
//#define PCM_HANDLE_IDENTIFIER_CaptureDouble "CaptureDouble"
|
|
#define SOUND_CARD_UAC1 "hw:UAC1Gadget"
|
|
|
|
#define SOUND_MIXER_AUDIOCODEC "hw:0"
|
|
#define SOUND_MIXER_SNDDAUDIO0 "hw:1"
|
|
|
|
#define SOUND_CARD_SNDDAUDIo0 "I2SRTX" //"hw:1,0"
|
|
|
|
//#define AI_HW_AEC_DEBUG_EN
|
|
|
|
typedef enum AI_STATES_E
|
|
{
|
|
AI_STATE_INVALID = 0,
|
|
AI_STATE_CONFIGURED,
|
|
AI_STATE_STARTED,
|
|
} AI_STATES_E;
|
|
|
|
typedef enum AO_STATES_E
|
|
{
|
|
AO_STATE_INVALID = 0,
|
|
AO_STATE_CONFIGURED,
|
|
AO_STATE_STARTED,
|
|
} AO_STATES_E;
|
|
|
|
typedef struct AudioInputDevice
|
|
{
|
|
AI_STATES_E mState;
|
|
|
|
AIO_ATTR_S mAttr; //user setting.
|
|
PCM_CONFIG_S mCfg; //underlying config of alsaLib.
|
|
AUDIO_TRACK_MODE_E mTrackMode;
|
|
pthread_t mThdId;
|
|
pthread_t mThdLoopId;
|
|
volatile BOOL mThdRunning;
|
|
|
|
struct list_head mChnList;
|
|
pthread_mutex_t mChnListLock;
|
|
pthread_mutex_t mApiCallLock; // to protect the api call,when used in two thread asynchronously.
|
|
pthread_mutex_t mAgcDbGainLock; // to protect the agc db gain call,when used in two thread asynchronously.
|
|
|
|
void *mpAecCookie;
|
|
AecProcessFuncType mpAecProcessCallback;
|
|
int aec_valid_frm; // flag used to indicate one valid output frame is ready or not.
|
|
|
|
char *pCapBuf; //used to read capture data(i.e. captureMic data)
|
|
//char *pRefBuf; //used to read ref data(i.e. daudio0 data), when use multi-plugin, it is not used.
|
|
|
|
int snd_card_id;
|
|
|
|
//for ans process
|
|
void *mpAnsCookie;
|
|
AnsProcessFuncType mpAnsProcessCallback;
|
|
int ans_valid_frm; // flag used to indicate one valid output frame is ready or not.
|
|
|
|
//int ai_agc_inited; // agc initialized or not
|
|
//short *ai_agc_tmp_buff; // tmp buffer to store data processed by agc
|
|
|
|
int ai_agc_float_inited; // agc float initialized or not
|
|
short *ai_agc_float_tmp_buff; // tmp buffer to store data processed by agc float
|
|
|
|
void *ai_agc_float_handle; /** agc float handle */
|
|
|
|
BOOL mbSuspendAns; //when in ans enable state, suspend ans and resume ans. 0:resume, 1:suspend.
|
|
BOOL mbSuspendAec; //when in aec enable state, suspend aec and resume aec. 0:resume, 1:suspend.
|
|
|
|
void *cookie;
|
|
AudioDevCallbackFuncType mpAudioDevCallback;
|
|
} AudioInputDevice;
|
|
|
|
typedef struct AudioOutputSubChl_S
|
|
{
|
|
AO_STATES_E mState;
|
|
|
|
AIO_ATTR_S mAttr;
|
|
PCM_CONFIG_S mCfg;
|
|
AUDIO_TRACK_MODE_E mTrackMode;
|
|
pthread_t mThdId;
|
|
volatile BOOL mThdRunning;
|
|
|
|
int AoChlId; // added to indicate id of current ao chl
|
|
int snd_card_id;
|
|
} AudioOutputSubChl;
|
|
typedef struct AudioOutputDevice
|
|
{
|
|
pthread_mutex_t mAOApiCallLock; // to protect the api call,when used in two thread asynchronously.
|
|
pthread_mutex_t mChnListLock;
|
|
struct list_head mChnList;
|
|
AudioOutputSubChl PlayChls[MAX_AO_CHLS]; // changed for condition that two ao chl exist
|
|
AO_CHN daudio0_ao_chl;
|
|
/// config runtime arguments of pcm.PlaybackRateDmix of asound.conf
|
|
//int mDmixSampleRate;
|
|
//int mDmixChnNum;
|
|
//int mDmixPeriodSize;
|
|
} AudioOutputDevice;
|
|
|
|
typedef struct AudioHwDevice
|
|
{
|
|
BOOL mEnableFlag;
|
|
AIO_MIXER_S mMixer;
|
|
AudioInputDevice mCap;
|
|
AudioOutputDevice mPlay;
|
|
} AudioHwDevice;
|
|
|
|
static AudioHwDevice gAudioHwDev[AIO_DEV_MAX_NUM];
|
|
|
|
/*
|
|
* Because the mixerElems created by alsa-lib will not exist at init, they will be registered to system after first
|
|
* snd_pcm_open(), and be available after second snd_pcm_open(), so for all mixerElems available before app call
|
|
* snd_pcm_open(), we call snd_pcm_open() specifically here.
|
|
*/
|
|
static void MakeAllMixerElemsAvailable()
|
|
{
|
|
int err;
|
|
snd_pcm_t *pSndPcmHandle = NULL;
|
|
//int64_t tm1, tm2;
|
|
//tm1 = CDX_GetSysTimeUsMonotonic();
|
|
err = snd_pcm_open(&pSndPcmHandle, SOUND_CARD_AUDIOCODEC_PLAY, SND_PCM_STREAM_PLAYBACK, 0);
|
|
if(0 == err)
|
|
{
|
|
err = snd_pcm_close(pSndPcmHandle);
|
|
if(0 == err)
|
|
{
|
|
alogd("pcm open and close, all MixerElems maybe exist.");
|
|
}
|
|
else
|
|
{
|
|
aloge("fatal error! snd pcm close fail[%d]", err);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
aloge("fatal error! why snd pcm open fail?[%d]", err);
|
|
}
|
|
//tm2 = CDX_GetSysTimeUsMonotonic();
|
|
//alogd("prepare alsa mixer elems, cost [%lld]us", tm2-tm1);
|
|
}
|
|
|
|
static void GeneratePlaybackRateDmixIdentifier(char *pDmixId, int nSize, int nSampleRate, int nChnNum, int nPeriodSize)
|
|
{
|
|
pDmixId[nSize-1] = '\0';
|
|
//snprintf(pDmixId, nSize-1, "%s:%d,%d,%d", PCM_HANDLE_IDENTIFIER_PLAY, nSampleRate, nChnNum,nPeriodSize);
|
|
snprintf(pDmixId, nSize-1, "%s", PCM_HANDLE_IDENTIFIER_PLAY);
|
|
//alogd("generate pcm plugin id:%s", pDmixId);
|
|
}
|
|
|
|
/**
|
|
generate audio capture identifer.
|
|
|
|
our alsa capture pcm handle identifer naming conversion:
|
|
pcm.Capture<n>Mic:<SampleRate>, <n>: the number of MIC. <SampleRate>: set sample rate to Hardware PCM.
|
|
pcm.Capture<n>MicPlusAec, if enable aec, add PlusAec.
|
|
|
|
e.g.:
|
|
pcm.Capture1Mic:16000
|
|
pcm.Capture1MicPlusAec
|
|
pcm.Capture2Mic:16000
|
|
pcm.Capture2MicPlusAec
|
|
|
|
@param pCaptureId
|
|
char array to store capture pcm handle identifier.
|
|
@param nMicNum
|
|
the number of microphone.
|
|
@param bAecEnable
|
|
indicate if enable aec.
|
|
@param nSampleRate
|
|
sample rate to be set to Hardware PCM.
|
|
*/
|
|
static void GenerateCaptureIdentifier(char *pCaptureId, int nSize, int nMicNum, int bAecEnable, int nSampleRate)
|
|
{
|
|
pCaptureId[nSize-1] = '\0';
|
|
if(bAecEnable)
|
|
{
|
|
snprintf(pCaptureId, nSize-1, "Capture%dMicPlusAec", nMicNum);
|
|
}
|
|
else
|
|
{
|
|
snprintf(pCaptureId, nSize-1, "Capture%dMic:%d", nMicNum, nSampleRate);
|
|
}
|
|
//alogd("generate capture pcm plugin id:%s", pCaptureId);
|
|
}
|
|
|
|
// 0-cap; 1-play
|
|
ERRORTYPE audioHw_Construct(void)
|
|
{
|
|
int i;
|
|
int err;
|
|
//make all controls available.
|
|
MakeAllMixerElemsAvailable();
|
|
|
|
//memset(&gAudioHwDev, 0, sizeof(AudioHwDevice)*AIO_DEV_MAX_NUM);
|
|
for (i = 0; i < AIO_DEV_MAX_NUM; ++i) {
|
|
AudioHwDevice *pDev = &gAudioHwDev[i];
|
|
if (TRUE == pDev->mEnableFlag) {
|
|
alogw("audio_hw has already been constructed!");
|
|
return SUCCESS;
|
|
}
|
|
memset(pDev, 0, sizeof(AudioHwDevice));
|
|
if(0 == i)
|
|
{
|
|
err = alsaOpenMixer(&pDev->mMixer, SOUND_MIXER_AUDIOCODEC);
|
|
if (err != 0) {
|
|
aloge("AIO device %d open mixer failed, err[%d]!", i, err);
|
|
}
|
|
pDev->mMixer.snd_card_id = 0;
|
|
|
|
pDev->mCap.snd_card_id = 0;
|
|
pDev->mCap.mCfg.snd_card_id = 0;
|
|
|
|
for(int j=0; j<MAX_AO_CHLS; ++j)
|
|
{
|
|
pDev->mPlay.PlayChls[j].snd_card_id = 0;
|
|
pDev->mPlay.PlayChls[j].mCfg.snd_card_id = 0;
|
|
}
|
|
//pDev->mPlay.mDmixSampleRate = PlaybackRateDmix_DefaultSampleRate;
|
|
//pDev->mPlay.mDmixChnNum = PlaybackRateDmix_DefaultChnCnt;
|
|
//pDev->mPlay.mDmixPeriodSize = PlaybackRateDmix_DefaultPeriodSize;
|
|
// #if (MPPCFG_AEC == OPTION_AEC_ENABLE)
|
|
// alsaMixerSetAudioCodecHubMode(&pDev->mMixer,1); // to set hub mode for the first snd card
|
|
// #endif
|
|
}
|
|
else if(1 == i)
|
|
{
|
|
err = alsaOpenMixer(&pDev->mMixer, SOUND_MIXER_SNDDAUDIO0);
|
|
if (err != 0) {
|
|
aloge("AIO device %d open mixer failed!", i);
|
|
}
|
|
pDev->mMixer.snd_card_id = 1;
|
|
|
|
pDev->mCap.snd_card_id = 1;
|
|
pDev->mCap.mCfg.snd_card_id = 1;
|
|
|
|
for(int j=0; j<MAX_AO_CHLS; ++j)
|
|
{
|
|
pDev->mPlay.PlayChls[j].snd_card_id = 1;
|
|
pDev->mPlay.PlayChls[j].mCfg.snd_card_id = 1;
|
|
}
|
|
// #if (MPPCFG_AEC == OPTION_AEC_ENABLE)
|
|
// alsaMixerSetDAudio0HubMode(&pDev->mMixer,1);// to set hub mode for the second snd card
|
|
// alsaMixerSetDAudio0LoopBackEn(&pDev->mMixer,1);// to enable loopback for the second snd card
|
|
// #endif
|
|
}
|
|
pDev->mCap.mState = AI_STATE_INVALID;
|
|
for(int j=0; j<MAX_AO_CHLS; ++j)
|
|
{
|
|
pDev->mPlay.PlayChls[j].mState = AO_STATE_INVALID;
|
|
}
|
|
pDev->mPlay.daudio0_ao_chl = -1;
|
|
INIT_LIST_HEAD(&pDev->mCap.mChnList);
|
|
INIT_LIST_HEAD(&pDev->mPlay.mChnList);
|
|
pthread_mutex_init(&pDev->mCap.mApiCallLock, NULL);
|
|
pthread_mutex_init(&pDev->mCap.mAgcDbGainLock, NULL);
|
|
pthread_mutex_init(&pDev->mPlay.mAOApiCallLock, NULL);
|
|
pthread_mutex_init(&pDev->mPlay.mChnListLock, NULL);
|
|
//INIT_LIST_HEAD(&pPlay->mChnList);
|
|
pDev->mEnableFlag = TRUE;
|
|
}
|
|
return SUCCESS;
|
|
}
|
|
|
|
ERRORTYPE audioHw_Destruct(void)
|
|
{
|
|
int i;
|
|
for (i = 0; i < AIO_DEV_MAX_NUM; ++i) {
|
|
AudioHwDevice *pDev = &gAudioHwDev[i];
|
|
if (FALSE == pDev->mEnableFlag) {
|
|
alogw("audio_hw has already been destructed!");
|
|
return SUCCESS;
|
|
}
|
|
if (pDev->mMixer.handle != NULL)
|
|
{
|
|
for(int j=0; j<MAX_AO_CHLS; ++j)
|
|
{
|
|
if(AO_STATE_STARTED==pDev->mPlay.PlayChls[j].mState)
|
|
{
|
|
aloge("Why AO still running? chl[%d],playState:%d", j,pDev->mPlay.PlayChls[j].mState);
|
|
}
|
|
}
|
|
|
|
if (AI_STATE_STARTED==pDev->mCap.mState)
|
|
{
|
|
aloge("Why AI still running? CapState:%d ", pDev->mCap.mState);
|
|
}
|
|
alsaCloseMixer(&pDev->mMixer);
|
|
}
|
|
|
|
pthread_mutex_destroy(&pDev->mPlay.mChnListLock);
|
|
pthread_mutex_destroy(&pDev->mCap.mApiCallLock);
|
|
pthread_mutex_destroy(&pDev->mCap.mAgcDbGainLock);
|
|
pthread_mutex_destroy(&pDev->mPlay.mAOApiCallLock);
|
|
|
|
pDev->mCap.mState = AI_STATE_INVALID;
|
|
|
|
for(int j=0; j<MAX_AO_CHLS; ++j)
|
|
{
|
|
pDev->mPlay.PlayChls[j].mState = AO_STATE_INVALID;
|
|
}
|
|
|
|
pDev->mEnableFlag = FALSE;
|
|
}
|
|
return SUCCESS;
|
|
}
|
|
|
|
|
|
/**************************************AI_DEV*****************************************/
|
|
ERRORTYPE audioHw_AI_Dev_lock(AUDIO_DEV AudioDevId)
|
|
{
|
|
return pthread_mutex_lock(&gAudioHwDev[AudioDevId].mCap.mChnListLock);
|
|
}
|
|
|
|
ERRORTYPE audioHw_AI_Dev_unlock(AUDIO_DEV AudioDevId)
|
|
{
|
|
return pthread_mutex_unlock(&gAudioHwDev[AudioDevId].mCap.mChnListLock);
|
|
}
|
|
|
|
ERRORTYPE audioHw_AI_searchChannel_l(AUDIO_DEV AudioDevId, AI_CHN AiChn, AI_CHANNEL_S** pChn)
|
|
{
|
|
AudioInputDevice *pCap = &gAudioHwDev[AudioDevId].mCap;
|
|
ERRORTYPE ret = FAILURE;
|
|
AI_CHANNEL_S *pEntry;
|
|
list_for_each_entry(pEntry, &pCap->mChnList, mList)
|
|
{
|
|
if(pEntry->mId == AiChn) {
|
|
if(pChn) {
|
|
*pChn = pEntry;
|
|
}
|
|
ret = SUCCESS;
|
|
break;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
ERRORTYPE audioHw_AI_searchChannel(AUDIO_DEV AudioDevId, AI_CHN AiChn, AI_CHANNEL_S** pChn)
|
|
{
|
|
AudioInputDevice *pCap = &gAudioHwDev[AudioDevId].mCap;
|
|
ERRORTYPE ret = FAILURE;
|
|
AI_CHANNEL_S *pEntry;
|
|
pthread_mutex_lock(&pCap->mChnListLock);
|
|
ret = audioHw_AI_searchChannel_l(AudioDevId, AiChn, pChn);
|
|
pthread_mutex_unlock(&pCap->mChnListLock);
|
|
return ret;
|
|
}
|
|
|
|
ERRORTYPE audioHw_AI_AddChannel_l(AUDIO_DEV AudioDevId, AI_CHANNEL_S* pChn)
|
|
{
|
|
AudioInputDevice *pCap = &gAudioHwDev[AudioDevId].mCap;
|
|
list_add_tail(&pChn->mList, &pCap->mChnList);
|
|
struct list_head* pTmp;
|
|
int cnt = 0;
|
|
list_for_each(pTmp, &pCap->mChnList)
|
|
cnt++;
|
|
updateDebugfsByChnCnt(0, cnt);
|
|
return SUCCESS;
|
|
}
|
|
|
|
ERRORTYPE audioHw_AI_AddChannel(AUDIO_DEV AudioDevId, AI_CHANNEL_S* pChn)
|
|
{
|
|
AudioInputDevice *pCap = &gAudioHwDev[AudioDevId].mCap;
|
|
pthread_mutex_lock(&pCap->mChnListLock);
|
|
ERRORTYPE ret = audioHw_AI_AddChannel_l(AudioDevId, pChn);
|
|
pthread_mutex_unlock(&pCap->mChnListLock);
|
|
return SUCCESS;
|
|
}
|
|
|
|
ERRORTYPE audioHw_AI_RemoveChannel(AUDIO_DEV AudioDevId, AI_CHANNEL_S* pChn)
|
|
{
|
|
AudioInputDevice *pCap = &gAudioHwDev[AudioDevId].mCap;
|
|
pthread_mutex_lock(&pCap->mChnListLock);
|
|
list_del(&pChn->mList);
|
|
struct list_head* pTmp;
|
|
int cnt = 0;
|
|
list_for_each(pTmp, &pCap->mChnList)
|
|
cnt++;
|
|
updateDebugfsByChnCnt(0, cnt);
|
|
pthread_mutex_unlock(&pCap->mChnListLock);
|
|
return SUCCESS;
|
|
}
|
|
|
|
MM_COMPONENTTYPE *audioHw_AI_GetChnComp(PARAM_IN MPP_CHN_S *pMppChn)
|
|
{
|
|
AI_CHANNEL_S *pChn = NULL;
|
|
if (SUCCESS != audioHw_AI_searchChannel(pMppChn->mDevId, pMppChn->mChnId, &pChn)) {
|
|
return NULL;
|
|
}
|
|
return pChn->mpComp;
|
|
}
|
|
|
|
BOOL audioHw_AI_IsDevStarted(AUDIO_DEV AudioDevId)
|
|
{
|
|
return (gAudioHwDev[AudioDevId].mCap.mState == AI_STATE_STARTED);
|
|
}
|
|
|
|
#if (MPPCFG_ANS == OPTION_ANS_ENABLE)
|
|
static ERRORTYPE audioHw_AI_AnsProcess(void *pThreadData, AUDIO_FRAME_S *pFrm)
|
|
{
|
|
AudioInputDevice *pCap = (AudioInputDevice*)pThreadData;
|
|
int ret = 0;
|
|
|
|
// if(pCap->mAttr.ai_aec_en)
|
|
// {
|
|
// if(2 != pCap->mCfg.chnCnt)
|
|
// {
|
|
// aloge("fatal error! ans_invalid_chl_number:%d", pCap->mCfg.chnCnt);
|
|
// return FAILURE;
|
|
// }
|
|
// }
|
|
// else
|
|
// {
|
|
// if(1 != pCap->mCfg.chnCnt)
|
|
// {
|
|
// aloge("ans_invalid_chl_number:%d", pCap->mCfg.chnCnt);
|
|
// return FAILURE;
|
|
// }
|
|
// }
|
|
ret = pCap->mpAnsProcessCallback(pCap->mpAnsCookie, pFrm, &pCap->mAttr, pCap->mbSuspendAns);
|
|
if(0 == ret)
|
|
{
|
|
pCap->ans_valid_frm = 1;
|
|
}
|
|
else
|
|
{
|
|
if(ret < 0)
|
|
{
|
|
aloge("fatal error! ans process fail:%d", ret);
|
|
}
|
|
pCap->ans_valid_frm = 0;
|
|
}
|
|
return SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
|
|
#if (MPPCFG_AEC == OPTION_AEC_ENABLE)
|
|
|
|
static ERRORTYPE audioHw_AI_AecProcess(void *pThreadData, AUDIO_FRAME_S *pFrm)
|
|
{
|
|
AudioInputDevice *pCap = (AudioInputDevice*)pThreadData;
|
|
int ret = 0;
|
|
|
|
// if(2 != pCap->mCfg.chnCnt)
|
|
// {
|
|
// aloge("fatal error! aec_invalid_chl_number:%d", pCap->mCfg.chnCnt);
|
|
// return FAILURE;
|
|
// }
|
|
ret = pCap->mpAecProcessCallback(pCap->mpAecCookie, pFrm, pCap->mbSuspendAec);
|
|
if(0 == ret)
|
|
{
|
|
pCap->aec_valid_frm = 1;
|
|
}
|
|
else
|
|
{
|
|
if(ret < 0)
|
|
{
|
|
aloge("fatal error! aec process fail:%d", ret);
|
|
}
|
|
pCap->aec_valid_frm = 0;
|
|
}
|
|
return SUCCESS;
|
|
}
|
|
|
|
#endif
|
|
|
|
//static void *audioHw_AI_CapLoopThread(void *pThreadData)
|
|
//{
|
|
// AudioInputDevice *pCap = (AudioInputDevice*)pThreadData;
|
|
// AudioInputDevice *pCap_daudio0 = NULL;
|
|
//
|
|
// pCap_daudio0 = &gAudioHwDev[pCap->snd_card_id+1].mCap;
|
|
// int nRet = alsaReadPcm(&pCap_daudio0->mCfg, pCap->pRefBuf, pCap_daudio0->mCfg.chunkSize);
|
|
// if (nRet != pCap_daudio0->mCfg.chunkSize)
|
|
// {
|
|
// aloge("fatal error! daudio0 fail to read pcm %d bytes-%d-%d", pCap_daudio0->mCfg.chunkBytes, pCap_daudio0->mState, nRet);
|
|
// if(nRet >= 0)
|
|
// {
|
|
// aloge("fatal error! alsa read pcm[%d] is impossible, check code!", nRet);
|
|
// }
|
|
// }
|
|
// return (void*)nRet;
|
|
//}
|
|
|
|
//static int OverflowAudioInputDevice(AudioInputDevice *pCap, char *pCapBuf)
|
|
//{
|
|
// // adc driver use default 1024x8 sample, if sampleRate is 16k, cacheTime is 1024/16*8=512ms
|
|
// int nUnit = 100; //unit:ms
|
|
// int nInterval;
|
|
// int nRet;
|
|
// int n;
|
|
// for(n=0; n<5; n++)
|
|
// {
|
|
// nInterval = nUnit*(1<<n);
|
|
// usleep(nInterval*1000);
|
|
// alogd("snd_card[%d] wait [%d]ms", pCap->snd_card_id, nInterval);
|
|
// nRet = alsaReadPcm(&pCap->mCfg, pCapBuf, pCap->mCfg.chunkSize);
|
|
// if(nRet < 0)
|
|
// {
|
|
// if(nRet != -EPIPE)
|
|
// {
|
|
// aloge("fatal error! why snd_card[%d] read ret[%d] is not -EPIPE?", pCap->snd_card_id, nRet);
|
|
// }
|
|
// break;
|
|
// }
|
|
// else
|
|
// {
|
|
// alogw("Be careful! snd_card[%d] ret[%d] still not -EPIPE.", pCap->snd_card_id, nRet);
|
|
// }
|
|
// }
|
|
// if(n >= 5)
|
|
// {
|
|
// aloge("fatal error! why snd_card[%d] don't meet -EPIPE?", pCap->snd_card_id);
|
|
// return -1;
|
|
// }
|
|
// else
|
|
// {
|
|
// return 0;
|
|
// }
|
|
//}
|
|
//#define DEBUG_SAVE_DAUDIO0_PCM (1)
|
|
//#define DEBUG_SAVE_RAW_PCM (1)
|
|
|
|
/**
|
|
judge initial sound mode of audio stream that mpi_ai get from alsa.
|
|
pAiAttr->enSoundmode is the final sound mode after being processed.
|
|
*/
|
|
AUDIO_SOUND_MODE_E judgeInitialSoundModeOfAudioInput(AIO_ATTR_S *pAiAttr)
|
|
{
|
|
AUDIO_SOUND_MODE_E eSoundMode;
|
|
if(1==pAiAttr->mChnCnt && !pAiAttr->ai_aec_en)
|
|
{
|
|
eSoundMode = AUDIO_SOUND_MODE_MONO;
|
|
}
|
|
else if(2==pAiAttr->mChnCnt && !pAiAttr->ai_aec_en)
|
|
{
|
|
eSoundMode = AUDIO_SOUND_MODE_STEREO;
|
|
}
|
|
else if(1==pAiAttr->mChnCnt && pAiAttr->ai_aec_en)
|
|
{
|
|
eSoundMode = AUDIO_SOUND_MODE_AW_1Chn1Ref;
|
|
}
|
|
else if(2==pAiAttr->mChnCnt && pAiAttr->ai_aec_en)
|
|
{
|
|
eSoundMode = AUDIO_SOUND_MODE_AW_2Chn1Ref;
|
|
}
|
|
else
|
|
{
|
|
aloge("fatal error! not support mode:%d-%d", pAiAttr->mChnCnt, pAiAttr->ai_aec_en);
|
|
eSoundMode = AUDIO_SOUND_MODE_MONO;
|
|
}
|
|
return eSoundMode;
|
|
}
|
|
|
|
static void *audioHw_AI_CapThread(void *pThreadData)
|
|
{
|
|
AudioInputDevice *pCap = (AudioInputDevice*)pThreadData;
|
|
AudioInputDevice *pCap_daudio0 = NULL;
|
|
BOOL mSaveFileFlag = 0;
|
|
char *pCapBuf = NULL;
|
|
//char *pCapBufLoopBack = NULL;
|
|
ssize_t ret;
|
|
pCap->pCapBuf = (char*)malloc(pCap->mCfg.chunkBytes);
|
|
if (NULL == pCap->pCapBuf)
|
|
{
|
|
aloge("Failed to alloc %d bytes(%s)", pCap->mCfg.chunkBytes, strerror(errno));
|
|
}
|
|
pCapBuf = pCap->pCapBuf;
|
|
|
|
if(pCap->mAttr.ai_aec_en)
|
|
{
|
|
// pCap->pRefBuf = (char *)malloc(pCap->mCfg.chunkBytes);
|
|
// if(NULL == pCap->pRefBuf)
|
|
// {
|
|
// aloge("fatal_error_to_malloc_ref_frm_buff:%d",pCap->mCfg.chunkBytes);
|
|
// }
|
|
// pCapBufLoopBack = pCap->pRefBuf;
|
|
pCap_daudio0 = &gAudioHwDev[pCap->snd_card_id+1].mCap;
|
|
}
|
|
prctl(PR_SET_NAME, "audioHw_AI_CapThread", 0, 0, 0);
|
|
#ifdef DEBUG_SAVE_DAUDIO0_PCM
|
|
FILE *pdaudio0File = fopen("/mnt/extsd/AiDaudio0.pcm", "wb");
|
|
#endif
|
|
#ifdef DEBUG_SAVE_RAW_PCM
|
|
FILE *prawFile = fopen("/tmp/AiRaw.pcm", "wb");
|
|
if (prawFile)
|
|
{
|
|
alogd("create file AiRaw.pcm to save pcm file");
|
|
mSaveFileFlag = TRUE;
|
|
}
|
|
else
|
|
{
|
|
aloge("create file AiRaw.pcm failed!");
|
|
mSaveFileFlag = FALSE;
|
|
}
|
|
#endif
|
|
|
|
while (pCap->mThdRunning)
|
|
{
|
|
ret = alsaReadPcm(&pCap->mCfg, pCapBuf, pCap->mCfg.chunkSize);
|
|
if (ret != pCap->mCfg.chunkSize)
|
|
{
|
|
if(ret >= 0)
|
|
{
|
|
aloge("aec_en[%d] fatal error! mainChn read pcm impossible[%d-%d-%d], check code!", pCap->mAttr.ai_aec_en, ret, pCap->mCfg.chunkBytes, pCap->mState);
|
|
}
|
|
else
|
|
{
|
|
if(-EPIPE == ret)
|
|
{
|
|
aloge("aec_en[%d] fatal error! mainChn xrun happened:[%lld]us", pCap->mAttr.ai_aec_en, CDX_GetSysTimeUsMonotonic());
|
|
}
|
|
else
|
|
{
|
|
aloge("aec_en[%d] fatal error! mainChn fail to read pcm[%d-%d-%d]", pCap->mAttr.ai_aec_en, ret, pCap->mCfg.chunkBytes, pCap->mState);
|
|
}
|
|
}
|
|
usleep(20*1000); //to wait daudio0 xrun
|
|
continue;
|
|
}
|
|
|
|
AI_CHANNEL_S *pEntry = NULL;
|
|
pthread_mutex_lock(&pCap->mChnListLock);
|
|
if (list_empty(&pCap->mChnList)) //if no aiChn exist, discard data and continue.
|
|
{
|
|
pthread_mutex_unlock(&pCap->mChnListLock);
|
|
continue;
|
|
}
|
|
AUDIO_FRAME_S stFrmCap;
|
|
memset(&stFrmCap,0,sizeof(AUDIO_FRAME_S));
|
|
stFrmCap.mSamplerate = pCap->mAttr.enSamplerate;
|
|
stFrmCap.mBitwidth = pCap->mAttr.enBitwidth;
|
|
stFrmCap.mSoundmode = judgeInitialSoundModeOfAudioInput(&pCap->mAttr);
|
|
stFrmCap.mLen = pCap->mCfg.chunkBytes;
|
|
stFrmCap.mpAddr = pCap->pCapBuf;
|
|
|
|
#ifdef DEBUG_SAVE_RAW_PCM
|
|
if (mSaveFileFlag)
|
|
fwrite(stFrmCap.mpAddr, 1, stFrmCap.mLen, prawFile);
|
|
#endif
|
|
|
|
if(pCap->mAttr.ai_aec_en && pCap->mAttr.mbBypassAec)
|
|
{
|
|
//if enable aec and bypase aec, then we can't do ans and agc here, because ans and agc will change
|
|
//capture_pcm, and this will lead to aec effect very bad.
|
|
}
|
|
else
|
|
{
|
|
#if (MPPCFG_AEC == OPTION_AEC_ENABLE)
|
|
if(pCap->mAttr.ai_aec_en)
|
|
{
|
|
audioHw_AI_AecProcess(pCap,&stFrmCap);
|
|
|
|
if(0 == pCap->aec_valid_frm)
|
|
{
|
|
pthread_mutex_unlock(&pCap->mChnListLock);
|
|
continue;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if (MPPCFG_ANS == OPTION_ANS_ENABLE)
|
|
if(pCap->mAttr.ai_ans_en)
|
|
{
|
|
audioHw_AI_AnsProcess(pCap,&stFrmCap);
|
|
|
|
if(0 == pCap->ans_valid_frm)
|
|
{
|
|
pthread_mutex_unlock(&pCap->mChnListLock);
|
|
continue;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*if(pCap->mAttr.ai_agc_en)
|
|
{
|
|
if(!pCap->ai_agc_inited)
|
|
{
|
|
aloge("agc_to_init:%f-%d-%d-%d",pCap->mAttr.ai_agc_cfg.fSample_rate,pCap->mAttr.ai_agc_cfg.iChannel,
|
|
pCap->mAttr.ai_agc_cfg.iSample_len,pCap->mAttr.ai_agc_cfg.iGain_level);
|
|
agc_m_init(&pCap->mAttr.ai_agc_cfg,pCap->mAttr.ai_agc_cfg.fSample_rate,
|
|
pCap->mAttr.ai_agc_cfg.iSample_len,pCap->mAttr.ai_agc_cfg.iBytePerSample,
|
|
pCap->mAttr.ai_agc_cfg.iGain_level, pCap->mAttr.ai_agc_cfg.iChannel);
|
|
pCap->ai_agc_inited = 1;
|
|
}
|
|
agc_m_process(&pCap->mAttr.ai_agc_cfg, (short*)stFrmCap.mpAddr, pCap->ai_agc_tmp_buff, pCap->mCfg.chunkSize);
|
|
memcpy(stFrmCap.mpAddr, pCap->ai_agc_tmp_buff, stFrmCap.mLen);
|
|
}*/
|
|
|
|
#if (MPPCFG_AGC == OPTION_AGC_ENABLE)
|
|
if(pCap->mAttr.ai_agc_en)
|
|
{
|
|
agc_handle *ai_agc_float_hld;
|
|
if(!pCap->ai_agc_float_inited)
|
|
{
|
|
alogd("agc_float_to_init param:%f-%f", pCap->mAttr.ai_agc_float_cfg.fTargetDb, pCap->mAttr.ai_agc_float_cfg.fMaxGainDb);
|
|
if ((pCap->mAttr.ai_agc_float_cfg.fTargetDb < -30 || pCap->mAttr.ai_agc_float_cfg.fTargetDb > 0)
|
|
|| (pCap->mAttr.ai_agc_float_cfg.fMaxGainDb < 0 || pCap->mAttr.ai_agc_float_cfg.fMaxGainDb > 30))
|
|
{
|
|
aloge("fatal error! agc float init param invalid");
|
|
pCap->ai_agc_float_handle = NULL;
|
|
}
|
|
else
|
|
{
|
|
int nSampleRate = map_AUDIO_SAMPLE_RATE_E_to_SampleRate(pCap->mAttr.enSamplerate);
|
|
int nChnCnt = pCap->mAttr.mChnCnt;
|
|
ai_agc_float_hld = func_agc_init(nSampleRate, nChnCnt, pCap->mAttr.ai_agc_float_cfg.fTargetDb,
|
|
pCap->mAttr.ai_agc_float_cfg.fMaxGainDb);
|
|
if (ai_agc_float_hld == NULL)
|
|
{
|
|
aloge("agc float init failed");
|
|
pCap->ai_agc_float_handle = NULL;
|
|
}
|
|
else
|
|
{
|
|
pCap->ai_agc_float_handle = (void*)ai_agc_float_hld;
|
|
}
|
|
}
|
|
pCap->ai_agc_float_inited = 1;
|
|
}
|
|
|
|
if (pCap->ai_agc_float_handle)
|
|
{
|
|
pthread_mutex_lock(&pCap->mAgcDbGainLock);
|
|
ai_agc_float_hld = (agc_handle *)pCap->ai_agc_float_handle;
|
|
int nBitWidth = map_AUDIO_BIT_WIDTH_E_to_BitWidth(pCap->mAttr.enBitwidth);
|
|
int nSampleNum = stFrmCap.mLen/(nBitWidth/8); //samples in all channels, not alsa frame.
|
|
ret = func_agc_proc(ai_agc_float_hld, (short*)stFrmCap.mpAddr, pCap->ai_agc_float_tmp_buff, nSampleNum);
|
|
pthread_mutex_unlock(&pCap->mAgcDbGainLock);
|
|
if (ret != 0)
|
|
{
|
|
aloge("fatal error! func agc_proc failed");
|
|
}
|
|
else
|
|
memcpy(stFrmCap.mpAddr, pCap->ai_agc_float_tmp_buff, stFrmCap.mLen);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
int64_t nPeriodInterval = (int64_t)pCap->mCfg.chunkSize*1000*1000/pCap->mCfg.sampleRate; //unit:us
|
|
int64_t nFramePts = CDX_GetSysTimeUsMonotonic() - nPeriodInterval;
|
|
list_for_each_entry(pEntry, &pCap->mChnList, mList)
|
|
{
|
|
AUDIO_FRAME_S frame;
|
|
memset(&frame, 0, sizeof(frame));
|
|
COMP_BUFFERHEADERTYPE bufferHeader;
|
|
memset(&bufferHeader, 0, sizeof(bufferHeader));
|
|
bufferHeader.nOutputPortIndex = AI_CHN_PORT_INDEX_CAP_IN;
|
|
bufferHeader.pOutputPortPrivate = &frame;
|
|
frame.mLen = stFrmCap.mLen;
|
|
frame.mBitwidth = pCap->mAttr.enBitwidth;
|
|
if(0 == pCap->mAttr.mbBypassAec)
|
|
{
|
|
if(stFrmCap.mSoundmode != pCap->mAttr.enSoundmode)
|
|
{
|
|
aloge("fatal error! check AI AIO_ATTR_S sound mode:%d!=%d", stFrmCap.mSoundmode, pCap->mAttr.enSoundmode);
|
|
}
|
|
}
|
|
frame.mSoundmode = pCap->mAttr.enSoundmode;
|
|
frame.mSamplerate = pCap->mAttr.enSamplerate;
|
|
frame.mpAddr = pCapBuf;
|
|
frame.mTimeStamp = nFramePts;// Note: the pts timestamp will be used by all ai channels
|
|
pEntry->mpComp->EmptyThisBuffer(pEntry->mpComp, &bufferHeader);
|
|
}
|
|
pthread_mutex_unlock(&pCap->mChnListLock);
|
|
}
|
|
|
|
alogd("AI_CapThread exit!");
|
|
#ifdef DEBUG_SAVE_DAUDIO0_PCM
|
|
fclose(pdaudio0File);
|
|
#endif
|
|
#ifdef DEBUG_SAVE_RAW_PCM
|
|
if (mSaveFileFlag)
|
|
fclose(prawFile);
|
|
#endif
|
|
|
|
if(NULL != pCap->pCapBuf)
|
|
{
|
|
free(pCap->pCapBuf);
|
|
pCap->pCapBuf = NULL;
|
|
}
|
|
if(pCap->mAttr.ai_aec_en)
|
|
{
|
|
// if(NULL != pCap->pRefBuf)
|
|
// {
|
|
// free(pCap->pRefBuf);
|
|
// pCap->pRefBuf = NULL;
|
|
// }
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
ERRORTYPE audioHw_AI_SetCallback(AUDIO_DEV AudioDevId, void *cookie, AudioDevCallbackFuncType pAudioDevCallback)
|
|
{
|
|
AudioInputDevice *pCap = &gAudioHwDev[AudioDevId].mCap;
|
|
pthread_mutex_lock(&pCap->mApiCallLock);
|
|
pCap->cookie = cookie;
|
|
pCap->mpAudioDevCallback = pAudioDevCallback;
|
|
pthread_mutex_unlock(&pCap->mApiCallLock);
|
|
return SUCCESS;
|
|
}
|
|
|
|
ERRORTYPE audioHw_AI_SetPubAttr(AUDIO_DEV AudioDevId, const AIO_ATTR_S *pstAttr)
|
|
{
|
|
AudioInputDevice *pCap = &gAudioHwDev[AudioDevId].mCap;
|
|
|
|
pthread_mutex_lock(&pCap->mApiCallLock);
|
|
if (pstAttr == NULL) {
|
|
aloge("pstAttr is NULL!");
|
|
pthread_mutex_unlock(&pCap->mApiCallLock);
|
|
return ERR_AI_ILLEGAL_PARAM;
|
|
}
|
|
if (AI_STATE_INVALID != pCap->mState) {
|
|
alogw("audioHw AI PublicAttr has been set!");
|
|
pthread_mutex_unlock(&pCap->mApiCallLock);
|
|
return SUCCESS;
|
|
}
|
|
pCap->mAttr = *pstAttr;
|
|
if(0==pCap->mAttr.mMicNum)
|
|
{
|
|
pCap->mAttr.mMicNum = 1;
|
|
}
|
|
if(pCap->mAttr.ai_aec_en)
|
|
{
|
|
#if (MPPCFG_AEC == OPTION_AEC_ENABLE)
|
|
AIO_MIXER_S *pMixer = &gAudioHwDev[AudioDevId].mMixer;
|
|
AIO_MIXER_S *pMixer_daudio0 = &gAudioHwDev[AudioDevId+1].mMixer;
|
|
alsaMixerSetAudioCodecHubMode(pMixer,1); // to set hub mode for the first snd card
|
|
alsaMixerSetDAudio0HubMode(pMixer_daudio0,1);// to set hub mode for the second snd card
|
|
alsaMixerSetDAudio0LoopBackEn(pMixer_daudio0,1);// to enable loopback for the second snd card
|
|
|
|
alsaMixerSetCapPlaySyncMode(pMixer, 1);
|
|
alsaMixerSetCapPlaySyncMode(pMixer_daudio0, 1);
|
|
#else
|
|
alogw("Be careful! aec is not config, so force disable.");
|
|
pCap->mAttr.ai_aec_en = 0;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
#if (MPPCFG_AEC == OPTION_AEC_ENABLE)
|
|
AIO_MIXER_S *pMixer = &gAudioHwDev[AudioDevId].mMixer;
|
|
AIO_MIXER_S *pMixer_daudio0 = &gAudioHwDev[AudioDevId+1].mMixer;
|
|
alsaMixerSetAudioCodecHubMode(pMixer,1); // to set hub mode for the first snd card
|
|
alsaMixerSetDAudio0HubMode(pMixer_daudio0,1);// to set hub mode for the second snd card
|
|
alsaMixerSetDAudio0LoopBackEn(pMixer_daudio0,1);// to enable loopback for the second snd card
|
|
|
|
alsaMixerSetCapPlaySyncMode(pMixer, 0);
|
|
alsaMixerSetCapPlaySyncMode(pMixer_daudio0, 0);
|
|
#endif
|
|
}
|
|
pCap->mState = AI_STATE_CONFIGURED;
|
|
pthread_mutex_unlock(&pCap->mApiCallLock);
|
|
return SUCCESS;
|
|
}
|
|
|
|
ERRORTYPE audioHw_AI_GetPubAttr(AUDIO_DEV AudioDevId, AIO_ATTR_S *pstAttr)
|
|
{
|
|
AudioInputDevice *pCap = &gAudioHwDev[AudioDevId].mCap;
|
|
|
|
pthread_mutex_lock(&pCap->mApiCallLock);
|
|
if (pstAttr == NULL) {
|
|
aloge("pstAttr is NULL!");
|
|
pthread_mutex_unlock(&pCap->mApiCallLock);
|
|
return ERR_AI_ILLEGAL_PARAM;
|
|
}
|
|
|
|
if (pCap->mState == AI_STATE_INVALID) {
|
|
aloge("get attr when attr is not set!");
|
|
pthread_mutex_unlock(&pCap->mApiCallLock);
|
|
return ERR_AI_NOT_PERM;
|
|
}
|
|
|
|
*pstAttr = pCap->mAttr;
|
|
pthread_mutex_unlock(&pCap->mApiCallLock);
|
|
return SUCCESS;
|
|
}
|
|
|
|
ERRORTYPE audioHw_AI_ClrPubAttr(AUDIO_DEV AudioDevId)
|
|
{
|
|
AudioInputDevice *pCap = &gAudioHwDev[AudioDevId].mCap;
|
|
|
|
pthread_mutex_lock(&pCap->mApiCallLock);
|
|
if (pCap->mState == AI_STATE_STARTED) {
|
|
aloge("please clear attr after AI disable!");
|
|
pthread_mutex_unlock(&pCap->mApiCallLock);
|
|
return ERR_AI_NOT_PERM;
|
|
}
|
|
memset(&pCap->mAttr, 0, sizeof(AIO_ATTR_S));
|
|
#if (MPPCFG_AEC == OPTION_AEC_ENABLE)
|
|
AIO_MIXER_S *pMixer = &gAudioHwDev[AudioDevId].mMixer;
|
|
AIO_MIXER_S *pMixer_daudio0 = &gAudioHwDev[AudioDevId+1].mMixer;
|
|
alsaMixerSetCapPlaySyncMode(pMixer, 0);
|
|
alsaMixerSetCapPlaySyncMode(pMixer_daudio0, 0);
|
|
#endif
|
|
pCap->mState = AI_STATE_INVALID;
|
|
pthread_mutex_unlock(&pCap->mApiCallLock);
|
|
return SUCCESS;
|
|
}
|
|
|
|
ERRORTYPE audioHw_AI_Enable(AUDIO_DEV AudioDevId)
|
|
{
|
|
AudioInputDevice *pCap = &gAudioHwDev[AudioDevId].mCap;
|
|
AIO_MIXER_S *pMixer = &gAudioHwDev[AudioDevId].mMixer;
|
|
AudioInputDevice *pCap_daudio0 = NULL;
|
|
AIO_MIXER_S *pMixer_daudio0 = NULL;
|
|
if(pCap->mAttr.ai_aec_en)
|
|
{
|
|
pCap_daudio0 = &gAudioHwDev[AudioDevId+1].mCap;
|
|
pMixer_daudio0 = &gAudioHwDev[AudioDevId+1].mMixer;
|
|
}
|
|
|
|
int ret;
|
|
|
|
pthread_mutex_lock(&pCap->mApiCallLock);
|
|
|
|
if (pCap->mState == AI_STATE_INVALID) {
|
|
pthread_mutex_unlock(&pCap->mApiCallLock);
|
|
return ERR_AI_NOT_CONFIG;
|
|
}
|
|
if (pCap->mState == AI_STATE_STARTED) {
|
|
pthread_mutex_unlock(&pCap->mApiCallLock);
|
|
return SUCCESS;
|
|
}
|
|
// if 'MIC2 Switch' enable but not disable, then next time when user want to use single mic, two mic's stream will
|
|
// be merge to one channel, it will make app error. So we depend on asound.conf hooks to enable
|
|
// other MICs. Only MIC1 will be enable in alsaOpenMixer().
|
|
// for(int i=0; i<pCap->mAttr.mMicNum; i++)
|
|
// {
|
|
// alsaMixerSetMicXEnable(pMixer, i+1, 1);
|
|
// }
|
|
pCap->mCfg.chnCnt = pCap->mAttr.mChnCnt;
|
|
pCap->mCfg.sampleRate = pCap->mAttr.enSamplerate;
|
|
|
|
pCap->mCfg.aec_delay_ms = pCap->mAttr.aec_delay_ms;
|
|
|
|
if (pCap->mAttr.enBitwidth == AUDIO_BIT_WIDTH_32) {
|
|
pCap->mCfg.format = SND_PCM_FORMAT_S32_LE;
|
|
} else if (pCap->mAttr.enBitwidth == AUDIO_BIT_WIDTH_24) {
|
|
pCap->mCfg.format = SND_PCM_FORMAT_S24_LE;
|
|
} else if (pCap->mAttr.enBitwidth == AUDIO_BIT_WIDTH_16) {
|
|
pCap->mCfg.format = SND_PCM_FORMAT_S16_LE;
|
|
} else if (pCap->mAttr.enBitwidth == AUDIO_BIT_WIDTH_8) {
|
|
pCap->mCfg.format = SND_PCM_FORMAT_S8;
|
|
} else {
|
|
pCap->mCfg.format = SND_PCM_FORMAT_S16_LE;
|
|
}
|
|
pCap->mCfg.bitsPerSample = (pCap->mAttr.enBitwidth+1)*8;
|
|
|
|
if(pCap->mAttr.ai_aec_en)
|
|
{
|
|
memcpy(&pCap_daudio0->mAttr,&pCap->mAttr,sizeof(AIO_ATTR_S));
|
|
int tmpCardId = pCap_daudio0->mCfg.snd_card_id;
|
|
memcpy(&pCap_daudio0->mCfg,&pCap->mCfg,sizeof(PCM_CONFIG_S));
|
|
pCap_daudio0->mCfg.snd_card_id = tmpCardId;
|
|
}
|
|
// if(pCap->mAttr.ai_aec_en)
|
|
// {
|
|
// alsaMixerSetCapPlaySyncMode(pMixer,1);
|
|
// }
|
|
char strCaptureIdentifier[128]; // e.g. pcm.Capture1MicPlusAec
|
|
GenerateCaptureIdentifier(strCaptureIdentifier, sizeof(strCaptureIdentifier), pCap->mAttr.mMicNum, pCap->mAttr.ai_aec_en, pCap->mCfg.sampleRate);
|
|
if(pCap->mAttr.ai_aec_en)
|
|
{
|
|
pCap->mCfg.chnCnt += 1; //daudio0 data will be capture by multi-plugin, so channel count plus 1.
|
|
ret = alsaOpenPcm(&pCap->mCfg, strCaptureIdentifier, 0);
|
|
}
|
|
else
|
|
{
|
|
ret = alsaOpenPcm(&pCap->mCfg, strCaptureIdentifier, 0);
|
|
}
|
|
if (ret != 0)
|
|
{
|
|
aloge("fatal error! open_pcm failed");
|
|
//alsaMixerSetMic2Enable(pMixer,0); // disable mic2
|
|
//alsaMixerSetCapPlaySyncMode(pMixer,0);
|
|
pthread_mutex_unlock(&pCap->mApiCallLock);
|
|
return FAILURE;
|
|
}
|
|
|
|
ret = alsaSetPcmParams(&pCap->mCfg);
|
|
if (ret < 0)
|
|
{
|
|
aloge("fatal error! SetPcmParams failed");
|
|
goto ERR_SET_PCM_PARAM;
|
|
}
|
|
//update mAttr by mCfg
|
|
pCap->mAttr.mPtNumPerFrm = pCap->mCfg.chunkSize;
|
|
|
|
#if (MPPCFG_AEC == OPTION_AEC_ENABLE)
|
|
pCap->mCfg.read_pcm_aec = 0;
|
|
if(pCap->mAttr.ai_aec_en)
|
|
{
|
|
pCap->mCfg.read_pcm_aec = 1;
|
|
pCap_daudio0->mCfg.read_pcm_aec = 1;
|
|
|
|
pCap->aec_valid_frm = 0;
|
|
|
|
#if (MPPCFG_AEC_LIB == OPTION_AEC_LIBRARY_WEBRTC)
|
|
pCap->mpAecCookie = ConstructWebRtcAecContext();
|
|
pCap->mpAecProcessCallback = &WebRtcAecProcess;
|
|
#elif (MPPCFG_AEC_LIB == OPTION_AEC_LIBRARY_UVOICE)
|
|
pCap->mpAecCookie = ConstructUvoiceAecContext(&pCap->mAttr, &pCap->mCfg, pCap->mpAudioDevCallback, pCap->cookie, AudioDevId);
|
|
pCap->mpAecProcessCallback = &UvoiceAecProcess;
|
|
#else
|
|
aloge("fatal error! no aec lib!");
|
|
pCap->mpAecCookie = NULL;
|
|
pCap->mpAecProcessCallback = NULL;
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
#if (MPPCFG_ANS == OPTION_ANS_ENABLE)
|
|
if(pCap->mAttr.ai_ans_en)
|
|
{
|
|
pCap->ans_valid_frm = 0;
|
|
|
|
#if (MPPCFG_ANS_LIB == OPTION_ANS_LIBRARY_WEBRTC)
|
|
pCap->mpAnsCookie = ConstructWebRtcAnsContext();
|
|
pCap->mpAnsProcessCallback = &WebRtcAnsProcess;
|
|
#elif(MPPCFG_ANS_LIB == OPTION_ANS_LIBRARY_LSTM)
|
|
pCap->mpAnsCookie = ConstructLstmAnsContext();
|
|
pCap->mpAnsProcessCallback = &LstmAnsProcess;
|
|
#elif(MPPCFG_ANS_LIB == OPTION_ANS_LIBRARY_NOSC)
|
|
pCap->mpAnsCookie = ConstructNoscAnsContext();
|
|
pCap->mpAnsProcessCallback = &NoscAnsProcess;
|
|
#else
|
|
aloge("fatal error! no ans lib!");
|
|
pCap->mpAnsCookie = NULL;
|
|
pCap->mpAnsProcessCallback = NULL;
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
/*if(pCap->mAttr.ai_agc_en)
|
|
{
|
|
pCap->ai_agc_tmp_buff = (short *)malloc(pCap->mCfg.chunkBytes);
|
|
if(NULL == pCap->ai_agc_tmp_buff)
|
|
{
|
|
aloge("agc_in_ai_error_malloc_tmp_buff_failed:%d",pCap->mCfg.chunkBytes);
|
|
goto ERR_EXIT5;
|
|
}
|
|
pCap->ai_agc_inited = 0;
|
|
}*/
|
|
|
|
#if (MPPCFG_AGC == OPTION_AGC_ENABLE)
|
|
if(pCap->mAttr.ai_agc_en)
|
|
{
|
|
pCap->ai_agc_float_tmp_buff = (short *)malloc(pCap->mCfg.chunkBytes);
|
|
if(NULL == pCap->ai_agc_float_tmp_buff)
|
|
{
|
|
aloge("agc_in_ai_error_malloc_tmp_buff_failed:%d",pCap->mCfg.chunkBytes);
|
|
goto ERR_EXIT5;
|
|
}
|
|
pCap->ai_agc_float_inited = 0;
|
|
}
|
|
#endif
|
|
|
|
pthread_mutex_init(&pCap->mChnListLock, NULL);
|
|
//INIT_LIST_HEAD(&pCap->mChnList);
|
|
pCap->mThdRunning = TRUE;
|
|
pthread_create(&pCap->mThdId, NULL, audioHw_AI_CapThread, pCap);
|
|
|
|
pCap->mState = AI_STATE_STARTED;
|
|
|
|
pthread_mutex_unlock(&pCap->mApiCallLock);
|
|
|
|
return SUCCESS;
|
|
ERR_EXIT5:
|
|
#if (MPPCFG_ANS == OPTION_ANS_ENABLE)
|
|
if(pCap->mpAnsCookie)
|
|
{
|
|
#if (MPPCFG_ANS_LIB == OPTION_ANS_LIBRARY_WEBRTC)
|
|
DestructWebRtcAnsContext((WebRtcAnsContext*)pCap->mpAnsCookie);
|
|
#elif(MPPCFG_ANS_LIB == OPTION_ANS_LIBRARY_LSTM)
|
|
DestructLstmAnsContext((LstmAnsContext*)pCap->mpAnsCookie);
|
|
#elif(MPPCFG_ANS_LIB == OPTION_ANS_LIBRARY_NOSC)
|
|
DestructNoscAnsContext((NoscAnsContext*)pCap->mpAnsCookie);
|
|
#endif
|
|
pCap->mpAnsCookie = NULL;
|
|
}
|
|
#endif
|
|
|
|
ERR_EXIT2:
|
|
ERR_EXIT1:
|
|
ERR_EXIT0:
|
|
#if (MPPCFG_AEC == OPTION_AEC_ENABLE)
|
|
if(pCap->mpAecCookie)
|
|
{
|
|
#if (MPPCFG_AEC_LIB == OPTION_AEC_LIBRARY_WEBRTC)
|
|
DestructWebRtcAecContext((WebRtcAecContext*)pCap->mpAecCookie);
|
|
#endif
|
|
pCap->mpAecCookie = NULL;
|
|
}
|
|
#endif
|
|
ERR_SET_PCM_PARAM2:
|
|
if(pCap->mAttr.ai_aec_en)
|
|
{
|
|
//alsaClosePcm(&pCap_daudio0->mCfg, 0); // 0: cap
|
|
//alsaMixerSetCapPlaySyncMode(pMixer_daudio0,1);
|
|
}
|
|
|
|
ERR_SET_PCM_PARAM:
|
|
//alsaMixerSetMic2Enable(pMixer,0); // disable mic2
|
|
alsaClosePcm(&pCap->mCfg, 0); // 0: cap
|
|
//alsaMixerSetCapPlaySyncMode(pMixer,0);
|
|
pthread_mutex_unlock(&pCap->mApiCallLock);
|
|
return FAILURE;
|
|
}
|
|
|
|
ERRORTYPE audioHw_AI_Disable(AUDIO_DEV AudioDevId)
|
|
{
|
|
AudioInputDevice *pCap = &gAudioHwDev[AudioDevId].mCap;
|
|
AIO_MIXER_S *pMixer = &gAudioHwDev[AudioDevId].mMixer;
|
|
AudioInputDevice *pCap_daudio0 = NULL;
|
|
AIO_MIXER_S *pMixer_daudio0 = NULL;
|
|
#if (MPPCFG_AEC == OPTION_AEC_ENABLE)
|
|
pCap_daudio0 = &gAudioHwDev[AudioDevId+1].mCap;
|
|
pMixer_daudio0 = &gAudioHwDev[AudioDevId+1].mMixer;
|
|
#endif
|
|
|
|
int ret;
|
|
|
|
pthread_mutex_lock(&pCap->mApiCallLock);
|
|
if (pCap->mState == AI_STATE_INVALID)
|
|
{
|
|
pthread_mutex_unlock(&pCap->mApiCallLock);
|
|
return ERR_AI_NOT_CONFIG;
|
|
}
|
|
if (pCap->mState != AI_STATE_STARTED)
|
|
{
|
|
pthread_mutex_unlock(&pCap->mApiCallLock);
|
|
return SUCCESS;
|
|
}
|
|
pthread_mutex_lock(&pCap->mChnListLock);
|
|
if (!list_empty(&pCap->mChnList))
|
|
{
|
|
pthread_mutex_unlock(&pCap->mChnListLock);
|
|
pthread_mutex_unlock(&pCap->mApiCallLock);
|
|
return SUCCESS;
|
|
}
|
|
pthread_mutex_unlock(&pCap->mChnListLock);
|
|
|
|
pCap->mThdRunning = FALSE;
|
|
|
|
pthread_join(pCap->mThdId, (void*) &ret);
|
|
|
|
pthread_mutex_destroy(&pCap->mChnListLock);
|
|
|
|
alsaClosePcm(&pCap->mCfg, 0); // 0: cap
|
|
pCap->mCfg.read_pcm_aec = 0;
|
|
|
|
#if (MPPCFG_AEC == OPTION_AEC_ENABLE)
|
|
if(pCap->mAttr.ai_aec_en)
|
|
{
|
|
//alsaMixerSetCapPlaySyncMode(pMixer,0);
|
|
//alsaClosePcm(&pCap_daudio0->mCfg, 0); // 0: cap
|
|
pCap_daudio0->mCfg.read_pcm_aec = 0;
|
|
|
|
if(pCap->mpAecCookie)
|
|
{
|
|
#if (MPPCFG_AEC_LIB == OPTION_AEC_LIBRARY_WEBRTC)
|
|
DestructWebRtcAecContext((WebRtcAecContext*)pCap->mpAecCookie);
|
|
#elif (MPPCFG_AEC_LIB == OPTION_AEC_LIBRARY_UVOICE)
|
|
DestructUvoiceAecContext((UvoiceAecContext*)pCap->mpAecCookie);
|
|
#endif
|
|
pCap->mpAecCookie = NULL;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if (MPPCFG_ANS == OPTION_ANS_ENABLE)
|
|
if(pCap->mAttr.ai_ans_en)
|
|
{
|
|
if(pCap->mpAnsCookie)
|
|
{
|
|
#if (MPPCFG_ANS_LIB == OPTION_ANS_LIBRARY_WEBRTC)
|
|
DestructWebRtcAnsContext((WebRtcAnsContext*)pCap->mpAnsCookie);
|
|
#elif(MPPCFG_ANS_LIB == OPTION_ANS_LIBRARY_LSTM)
|
|
DestructLstmAnsContext((LstmAnsContext*)pCap->mpAnsCookie);
|
|
#elif(MPPCFG_ANS_LIB == OPTION_ANS_LIBRARY_NOSC)
|
|
DestructNoscAnsContext((NoscAnsContext*)pCap->mpAnsCookie);
|
|
#endif
|
|
pCap->mpAnsCookie = NULL;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*if(pCap->mAttr.ai_agc_en)
|
|
{
|
|
if(NULL != pCap->ai_agc_tmp_buff)
|
|
{
|
|
free(pCap->ai_agc_tmp_buff);
|
|
pCap->ai_agc_tmp_buff = NULL;
|
|
}
|
|
pCap->ai_agc_inited = 0;
|
|
}*/
|
|
#if (MPPCFG_AGC == OPTION_AGC_ENABLE)
|
|
if(pCap->mAttr.ai_agc_en)
|
|
{
|
|
if(NULL != pCap->ai_agc_float_tmp_buff)
|
|
{
|
|
free(pCap->ai_agc_float_tmp_buff);
|
|
pCap->ai_agc_float_tmp_buff = NULL;
|
|
}
|
|
pCap->ai_agc_float_inited = 0;
|
|
func_agc_exit((agc_handle *)pCap->ai_agc_float_handle);
|
|
}
|
|
#endif
|
|
|
|
pCap->mState = AI_STATE_CONFIGURED;
|
|
pthread_mutex_unlock(&pCap->mApiCallLock);
|
|
return SUCCESS;
|
|
}
|
|
|
|
ERRORTYPE audioHw_AI_SetTrackMode(AUDIO_DEV AudioDevId, AUDIO_TRACK_MODE_E enTrackMode)
|
|
{
|
|
AudioInputDevice *pCap = &gAudioHwDev[AudioDevId].mCap;
|
|
|
|
if (pCap->mState != AI_STATE_STARTED) {
|
|
return ERR_AI_NOT_ENABLED;
|
|
}
|
|
|
|
pCap->mTrackMode = enTrackMode;
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
ERRORTYPE audioHw_AI_GetTrackMode(AUDIO_DEV AudioDevId, AUDIO_TRACK_MODE_E *penTrackMode)
|
|
{
|
|
AudioInputDevice *pCap = &gAudioHwDev[AudioDevId].mCap;
|
|
|
|
if (pCap->mState != AI_STATE_STARTED) {
|
|
return ERR_AI_NOT_ENABLED;
|
|
}
|
|
|
|
*penTrackMode = pCap->mTrackMode;
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
ERRORTYPE audioHw_AI_GetPcmConfig(AUDIO_DEV AudioDevId, PCM_CONFIG_S **ppCfg)
|
|
{
|
|
AudioInputDevice *pCap = &gAudioHwDev[AudioDevId].mCap;
|
|
|
|
if (pCap->mState != AI_STATE_STARTED) {
|
|
return ERR_AI_NOT_ENABLED;
|
|
}
|
|
*ppCfg = &pCap->mCfg;
|
|
return SUCCESS;
|
|
}
|
|
|
|
ERRORTYPE audioHw_AI_GetAIOAttr(AUDIO_DEV AudioDevId, AIO_ATTR_S **ppAttr)
|
|
{
|
|
AudioInputDevice *pCap = &gAudioHwDev[AudioDevId].mCap;
|
|
|
|
if (pCap->mState != AI_STATE_STARTED) {
|
|
return ERR_AI_NOT_ENABLED;
|
|
}
|
|
*ppAttr = &pCap->mAttr;
|
|
return SUCCESS;
|
|
}
|
|
|
|
ERRORTYPE audioHw_AI_SetAdcDrc(AUDIO_DEV AudioDevId, int enable)
|
|
{
|
|
AudioInputDevice *pCap = &gAudioHwDev[AudioDevId].mCap;
|
|
AIO_MIXER_S *pMixer = &gAudioHwDev[AudioDevId].mMixer;
|
|
|
|
if (pCap->mState != AI_STATE_STARTED) {
|
|
return ERR_AI_NOT_ENABLED;
|
|
}
|
|
|
|
return alsaMixerSetAudioCodecAdcDrc(pMixer, enable);
|
|
}
|
|
|
|
ERRORTYPE audioHw_AI_SetAdcHpf(AUDIO_DEV AudioDevId, int enable)
|
|
{
|
|
AudioInputDevice *pCap = &gAudioHwDev[AudioDevId].mCap; AIO_MIXER_S *pMixer = &gAudioHwDev[AudioDevId].mMixer;
|
|
|
|
if (pCap->mState != AI_STATE_STARTED) {
|
|
return ERR_AI_NOT_ENABLED;
|
|
}
|
|
|
|
return alsaMixerSetAudioCodecAdcHpf(pMixer, enable);
|
|
}
|
|
|
|
ERRORTYPE audioHw_AI_SetVolume(AUDIO_DEV AudioDevId, int s32VolumeDb)
|
|
{
|
|
AudioInputDevice *pCap = &gAudioHwDev[AudioDevId].mCap;
|
|
AIO_MIXER_S *pMixer = &gAudioHwDev[AudioDevId].mMixer;
|
|
|
|
if (pCap->mState != AI_STATE_STARTED) {
|
|
return ERR_AI_NOT_ENABLED;
|
|
}
|
|
|
|
return alsaMixerSetVolume(pMixer, 0, s32VolumeDb);
|
|
}
|
|
|
|
ERRORTYPE audioHw_AI_GetVolume(AUDIO_DEV AudioDevId, int *ps32VolumeDb)
|
|
{
|
|
AudioInputDevice *pCap = &gAudioHwDev[AudioDevId].mCap;
|
|
AIO_MIXER_S *pMixer = &gAudioHwDev[AudioDevId].mMixer;
|
|
|
|
if (pCap->mState != AI_STATE_STARTED) {
|
|
return ERR_AI_NOT_ENABLED;
|
|
}
|
|
|
|
return alsaMixerGetVolume(pMixer, 0, (long*)ps32VolumeDb);
|
|
}
|
|
|
|
ERRORTYPE audioHw_AI_SetMute(AUDIO_DEV AudioDevId, int bEnable)
|
|
{
|
|
AudioInputDevice *pCap = &gAudioHwDev[AudioDevId].mCap;
|
|
AIO_MIXER_S *pMixer = &gAudioHwDev[AudioDevId].mMixer;
|
|
|
|
if (pCap->mState != AI_STATE_STARTED) {
|
|
return ERR_AI_NOT_ENABLED;
|
|
}
|
|
|
|
return alsaMixerSetMute(pMixer, 0, bEnable);
|
|
}
|
|
|
|
ERRORTYPE audioHw_AI_GetMute(AUDIO_DEV AudioDevId, int *pbEnable)
|
|
{
|
|
AudioInputDevice *pCap = &gAudioHwDev[AudioDevId].mCap;
|
|
AIO_MIXER_S *pMixer = &gAudioHwDev[AudioDevId].mMixer;
|
|
|
|
if (pCap->mState != AI_STATE_STARTED) {
|
|
return ERR_AI_NOT_ENABLED;
|
|
}
|
|
|
|
return alsaMixerGetMute(pMixer, 0, pbEnable);
|
|
}
|
|
|
|
/**************************************AO_DEV*****************************************/
|
|
ERRORTYPE audioHw_AO_Dev_lock(AUDIO_DEV AudioDevId)
|
|
{
|
|
return pthread_mutex_lock(&gAudioHwDev[AudioDevId].mPlay.mChnListLock);
|
|
}
|
|
|
|
ERRORTYPE audioHw_AO_Dev_unlock(AUDIO_DEV AudioDevId)
|
|
{
|
|
return pthread_mutex_unlock(&gAudioHwDev[AudioDevId].mPlay.mChnListLock);
|
|
}
|
|
|
|
ERRORTYPE audioHw_AO_searchChannel_l(AUDIO_DEV AudioDevId, AO_CHN AoChn, AO_CHANNEL_S** pChn)
|
|
{
|
|
AudioOutputDevice *pPlay = &gAudioHwDev[AudioDevId].mPlay;
|
|
ERRORTYPE ret = FAILURE;
|
|
AO_CHANNEL_S *pEntry;
|
|
list_for_each_entry(pEntry, &pPlay->mChnList, mList)
|
|
{
|
|
if(pEntry->mId == AoChn) {
|
|
if(pChn) {
|
|
*pChn = pEntry;
|
|
}
|
|
ret = SUCCESS;
|
|
break;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
ERRORTYPE audioHw_AO_searchChannel(AUDIO_DEV AudioDevId, AO_CHN AoChn, AO_CHANNEL_S** pChn)
|
|
{
|
|
AudioOutputDevice *pPlay = &gAudioHwDev[AudioDevId].mPlay;
|
|
ERRORTYPE ret = FAILURE;
|
|
AO_CHANNEL_S *pEntry;
|
|
pthread_mutex_lock(&pPlay->mChnListLock);
|
|
ret = audioHw_AO_searchChannel_l(AudioDevId, AoChn, pChn);
|
|
pthread_mutex_unlock(&pPlay->mChnListLock);
|
|
return ret;
|
|
}
|
|
|
|
ERRORTYPE audioHw_AO_AddChannel_l(AUDIO_DEV AudioDevId, AO_CHANNEL_S* pChn)
|
|
{
|
|
AudioOutputDevice *pPlay = &gAudioHwDev[AudioDevId].mPlay;
|
|
list_add_tail(&pChn->mList, &pPlay->mChnList);
|
|
struct list_head* pTmp;
|
|
int cnt = 0;
|
|
list_for_each(pTmp, &pPlay->mChnList)
|
|
cnt++;
|
|
updateDebugfsByChnCnt(1, cnt);
|
|
return SUCCESS;
|
|
}
|
|
|
|
ERRORTYPE audioHw_AO_AddChannel(AUDIO_DEV AudioDevId, AO_CHANNEL_S* pChn)
|
|
{
|
|
AudioOutputDevice *pPlay = &gAudioHwDev[AudioDevId].mPlay;
|
|
pthread_mutex_lock(&pPlay->mChnListLock);
|
|
ERRORTYPE ret = audioHw_AO_AddChannel_l(AudioDevId, pChn);
|
|
pthread_mutex_unlock(&pPlay->mChnListLock);
|
|
return ret;
|
|
}
|
|
|
|
ERRORTYPE audioHw_AO_RemoveChannel(AUDIO_DEV AudioDevId, AO_CHANNEL_S* pChn)
|
|
{
|
|
AudioOutputDevice *pPlay = &gAudioHwDev[AudioDevId].mPlay;
|
|
pthread_mutex_lock(&pPlay->mChnListLock);
|
|
list_del(&pChn->mList);
|
|
struct list_head* pTmp;
|
|
int cnt = 0;
|
|
list_for_each(pTmp, &pPlay->mChnList)
|
|
cnt++;
|
|
updateDebugfsByChnCnt(1, cnt);
|
|
pthread_mutex_unlock(&pPlay->mChnListLock);
|
|
return SUCCESS;
|
|
}
|
|
|
|
MM_COMPONENTTYPE *audioHw_AO_GetChnComp(PARAM_IN MPP_CHN_S *pMppChn)
|
|
{
|
|
AO_CHANNEL_S *pChn = NULL;
|
|
if (SUCCESS != audioHw_AO_searchChannel(pMppChn->mDevId, pMppChn->mChnId, &pChn)) {
|
|
return NULL;
|
|
}
|
|
return pChn->mpComp;
|
|
}
|
|
|
|
/**
|
|
set attr of underlying ao output alsa-plugin, i.e. pcm.PlaybackRateDmix of asound.conf.
|
|
|
|
@param AudioDevId
|
|
audioDevice
|
|
@param pstAttr
|
|
use pstAttr->enSamplerate, pstAttr->mPtNumPerFrm(i.e. period_size), pstAttr->mChnCnt now.
|
|
*/
|
|
//ERRORTYPE AudioHw_AO_SetPubAttr(AUDIO_DEV AudioDevId, const AIO_ATTR_S *pstAttr)
|
|
//{
|
|
// if (pstAttr == NULL)
|
|
// {
|
|
// aloge("pstAttr is NULL!");
|
|
// return ERR_AO_ILLEGAL_PARAM;
|
|
// }
|
|
// AudioOutputDevice *pPlay = &gAudioHwDev[AudioDevId].mPlay;
|
|
// pthread_mutex_lock(&pPlay->mAOApiCallLock);
|
|
// pPlay->mDmixSampleRate = pstAttr->enSamplerate;
|
|
// pPlay->mDmixChnNum = pstAttr->mChnCnt;
|
|
// pPlay->mDmixPeriodSize = pstAttr->mPtNumPerFrm;
|
|
// pthread_mutex_unlock(&pPlay->mAOApiCallLock);
|
|
// return SUCCESS;
|
|
//}
|
|
/**
|
|
get attr of underlying ao output alsa-plugin, i.e. pcm.PlaybackRateDmix of asound.conf.
|
|
|
|
@param AudioDevId
|
|
audioDevice
|
|
@param pstAttr
|
|
use pstAttr->enSamplerate, pstAttr->mPtNumPerFrm(i.e. period_size), pstAttr->mChnCnt now.
|
|
*/
|
|
//ERRORTYPE AudioHw_AO_GetPubAttr(AUDIO_DEV AudioDevId, AIO_ATTR_S *pstAttr)
|
|
//{
|
|
// if (pstAttr == NULL)
|
|
// {
|
|
// aloge("pstAttr is NULL!");
|
|
// return ERR_AO_ILLEGAL_PARAM;
|
|
// }
|
|
// AudioOutputDevice *pPlay = &gAudioHwDev[AudioDevId].mPlay;
|
|
// pstAttr->enSamplerate = pPlay->mDmixSampleRate;
|
|
// pstAttr->mChnCnt = pPlay->mDmixChnNum;
|
|
// pstAttr->mPtNumPerFrm = pPlay->mDmixPeriodSize;
|
|
// return SUCCESS;
|
|
//}
|
|
/**
|
|
restore attr of underlying ao output alsa-plugin to default value.
|
|
*/
|
|
//ERRORTYPE audioHw_AO_ClrPubAttr(AUDIO_DEV AudioDevId)
|
|
//{
|
|
// AudioOutputDevice *pPlay = &gAudioHwDev[AudioDevId].mPlay;
|
|
// pthread_mutex_lock(&pPlay->mAOApiCallLock);
|
|
// pPlay->mDmixSampleRate = PlaybackRateDmix_DefaultSampleRate;
|
|
// pPlay->mDmixChnNum = PlaybackRateDmix_DefaultChnCnt;
|
|
// pPlay->mDmixPeriodSize = PlaybackRateDmix_DefaultPeriodSize;
|
|
// pthread_mutex_unlock(&pPlay->mAOApiCallLock);
|
|
// return SUCCESS;
|
|
//}
|
|
|
|
|
|
BOOL audioHw_AO_IsDevConfigured(AUDIO_DEV AudioDevId,AO_CHN AoChn) // specify SndCard and ao chn id
|
|
{
|
|
pthread_mutex_lock(&gAudioHwDev[AudioDevId].mPlay.mAOApiCallLock);
|
|
BOOL bFlag = (gAudioHwDev[AudioDevId].mPlay.PlayChls[AoChn].mState == AO_STATE_CONFIGURED);
|
|
pthread_mutex_unlock(&gAudioHwDev[AudioDevId].mPlay.mAOApiCallLock);
|
|
return bFlag;
|
|
}
|
|
|
|
BOOL audioHw_AO_IsDevStarted(AUDIO_DEV AudioDevId, AO_CHN AoChn)
|
|
{
|
|
pthread_mutex_lock(&gAudioHwDev[AudioDevId].mPlay.mAOApiCallLock);
|
|
BOOL bFlag = (gAudioHwDev[AudioDevId].mPlay.PlayChls[AoChn].mState == AO_STATE_STARTED);
|
|
pthread_mutex_unlock(&gAudioHwDev[AudioDevId].mPlay.mAOApiCallLock);
|
|
return bFlag;
|
|
}
|
|
/* set attribute of pcm data from given ao chn */
|
|
ERRORTYPE audioHw_AO_SetChnPubAttr(AUDIO_DEV AudioDevId,AO_CHN AoChn, const AIO_ATTR_S *pstAttr)
|
|
{
|
|
if (pstAttr == NULL) {
|
|
aloge("pstAttr is NULL!");
|
|
return ERR_AO_ILLEGAL_PARAM;
|
|
}
|
|
AudioOutputDevice *pPlay = &gAudioHwDev[AudioDevId].mPlay;
|
|
pthread_mutex_lock(&pPlay->mAOApiCallLock);
|
|
aloge("ao_set_attr_chl:%d,stat:%d",AoChn,pPlay->PlayChls[AoChn].mState);
|
|
|
|
if (pPlay->PlayChls[AoChn].mState == AO_STATE_CONFIGURED) {
|
|
alogw("Update AoAttr? cur_card:%d -> wanted_card:%d-%d",
|
|
pPlay->PlayChls[AoChn].mAttr.mPcmCardId, pstAttr->mPcmCardId,AoChn);
|
|
} else if (pPlay->PlayChls[AoChn].mState == AO_STATE_STARTED) {
|
|
alogw("Careful for 2 AoChns at the same time! They must have the same param:%d!",AoChn);
|
|
pthread_mutex_unlock(&pPlay->mAOApiCallLock);
|
|
return SUCCESS;
|
|
}
|
|
pPlay->PlayChls[AoChn].mAttr = *pstAttr;
|
|
|
|
#if (MPPCFG_AEC == OPTION_AEC_ENABLE)
|
|
AIO_MIXER_S *pMixer = &gAudioHwDev[AudioDevId].mMixer;
|
|
AIO_MIXER_S *pMixer_daudio0 = &gAudioHwDev[AudioDevId+1].mMixer;
|
|
alsaMixerSetAudioCodecHubMode(pMixer,1); // to set hub mode for the first snd card
|
|
alsaMixerSetDAudio0HubMode(pMixer_daudio0,1);// to set hub mode for the second snd card
|
|
alsaMixerSetDAudio0LoopBackEn(pMixer_daudio0,1);// to enable loopback for the second snd card
|
|
#endif
|
|
pPlay->PlayChls[AoChn].mState = AO_STATE_CONFIGURED;
|
|
pthread_mutex_unlock(&pPlay->mAOApiCallLock);
|
|
return SUCCESS;
|
|
}
|
|
/* get attribute of pcm that come from given ao chn */
|
|
ERRORTYPE audioHw_AO_GetChnPubAttr(AUDIO_DEV AudioDevId,AO_CHN AoChn, AIO_ATTR_S *pstAttr)
|
|
{
|
|
if (pstAttr == NULL) {
|
|
aloge("pstAttr is NULL!");
|
|
return ERR_AO_ILLEGAL_PARAM;
|
|
}
|
|
|
|
AudioOutputDevice *pPlay = &gAudioHwDev[AudioDevId].mPlay;
|
|
if (pPlay->PlayChls[AoChn].mState == AO_STATE_INVALID) {
|
|
aloge("get attr when attr is not set!");
|
|
return ERR_AO_NOT_PERM;
|
|
}
|
|
|
|
*pstAttr = pPlay->PlayChls[AoChn].mAttr;
|
|
return SUCCESS;
|
|
}
|
|
/* clear the attribute for specific ao chn */
|
|
ERRORTYPE audioHw_AO_ClrChnPubAttr(AUDIO_DEV AudioDevId,AO_CHN AoChn)
|
|
{
|
|
AudioOutputDevice *pPlay = &gAudioHwDev[AudioDevId].mPlay;
|
|
pthread_mutex_lock(&pPlay->mAOApiCallLock);
|
|
if (pPlay->PlayChls[AoChn].mState == AO_STATE_STARTED) {
|
|
aloge("please clear attr after AI disable!");
|
|
pthread_mutex_unlock(&pPlay->mAOApiCallLock);
|
|
return ERR_AO_NOT_PERM;
|
|
}
|
|
memset(&pPlay->PlayChls[AoChn].mAttr, 0, sizeof(AIO_ATTR_S));
|
|
pPlay->PlayChls[AoChn].mState = AO_STATE_INVALID;
|
|
pthread_mutex_unlock(&pPlay->mAOApiCallLock);
|
|
return SUCCESS;
|
|
}
|
|
|
|
ERRORTYPE audioHw_AO_SetTrackMode(AUDIO_DEV AudioDevId, AO_CHN AoChn,AUDIO_TRACK_MODE_E enTrackMode)
|
|
{
|
|
AudioOutputDevice *pPlay = &gAudioHwDev[AudioDevId].mPlay;
|
|
|
|
if (pPlay->PlayChls[AoChn].mState != AO_STATE_STARTED) {
|
|
return ERR_AO_NOT_ENABLED;
|
|
}
|
|
|
|
pPlay->PlayChls[AoChn].mTrackMode = enTrackMode;
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
ERRORTYPE audioHw_AO_GetTrackMode(AUDIO_DEV AudioDevId, AO_CHN AoChn,AUDIO_TRACK_MODE_E *penTrackMode)
|
|
{
|
|
AudioOutputDevice *pPlay = &gAudioHwDev[AudioDevId].mPlay;
|
|
|
|
if (pPlay->PlayChls[AoChn].mState != AO_STATE_STARTED) {
|
|
return ERR_AO_NOT_ENABLED;
|
|
}
|
|
|
|
*penTrackMode = pPlay->PlayChls[AoChn].mTrackMode;
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
ERRORTYPE audioHw_AO_EnableChn(AUDIO_DEV AudioDevId,AO_CHN AoChn)
|
|
{
|
|
AudioOutputDevice *pPlay = &gAudioHwDev[AudioDevId].mPlay;
|
|
AIO_MIXER_S *pMixer = &gAudioHwDev[AudioDevId].mMixer;
|
|
int j = 0;
|
|
|
|
#if (MPPCFG_AEC == OPTION_AEC_ENABLE)
|
|
AudioOutputDevice *pPlay_daudio0 = &gAudioHwDev[AudioDevId+1].mPlay; // related with second snd card
|
|
AIO_MIXER_S *pMixer_daudio0 = &gAudioHwDev[AudioDevId+1].mMixer;
|
|
#endif
|
|
|
|
int ret;
|
|
|
|
aloge("ao_enable_chl:%d",AoChn);
|
|
|
|
pthread_mutex_lock(&pPlay->mAOApiCallLock);
|
|
if (pPlay->PlayChls[AoChn].mState == AO_STATE_INVALID) {
|
|
aloge("fatal error! error_state:%d", pPlay->PlayChls[AoChn].mState);
|
|
pthread_mutex_unlock(&pPlay->mAOApiCallLock);
|
|
return ERR_AO_NOT_CONFIG;
|
|
}
|
|
if (pPlay->PlayChls[AoChn].mState == AO_STATE_STARTED) {
|
|
pthread_mutex_unlock(&pPlay->mAOApiCallLock);
|
|
return SUCCESS;
|
|
}
|
|
PCM_CONFIG_S *p_cfg = &pPlay->PlayChls[AoChn].mCfg;
|
|
AIO_ATTR_S *p_attr = &pPlay->PlayChls[AoChn].mAttr;
|
|
|
|
p_cfg->chnCnt = p_attr->mChnCnt;
|
|
p_cfg->sampleRate = p_attr->enSamplerate;
|
|
switch (p_attr->enBitwidth)
|
|
{
|
|
case AUDIO_BIT_WIDTH_32:
|
|
p_cfg->format = SND_PCM_FORMAT_S32_LE;
|
|
break;
|
|
case AUDIO_BIT_WIDTH_24:
|
|
p_cfg->format = SND_PCM_FORMAT_S24_LE;
|
|
break;
|
|
case AUDIO_BIT_WIDTH_16:
|
|
p_cfg->format = SND_PCM_FORMAT_S16_LE;
|
|
break;
|
|
case AUDIO_BIT_WIDTH_8:
|
|
p_cfg->format = SND_PCM_FORMAT_S8;
|
|
break;
|
|
default :
|
|
p_cfg->format = SND_PCM_FORMAT_S16_LE;
|
|
break;
|
|
}
|
|
|
|
p_cfg->bitsPerSample = (p_attr->enBitwidth+1)*8;
|
|
|
|
#if (MPPCFG_AEC == OPTION_AEC_ENABLE)
|
|
if(-1 == pPlay_daudio0->daudio0_ao_chl)
|
|
{
|
|
memcpy(&pPlay_daudio0->PlayChls[AoChn].mAttr,&pPlay->PlayChls[AoChn].mAttr,sizeof(AIO_ATTR_S));
|
|
memcpy(&pPlay_daudio0->PlayChls[AoChn].mCfg,&pPlay->PlayChls[AoChn].mCfg,sizeof(PCM_CONFIG_S));
|
|
alogw("params of play_daudio0 chn[%d] [%d-%d-%d] don't matter now. asound.conf make sure same to PlaybackRateDmix",
|
|
AoChn, pPlay_daudio0->PlayChls[AoChn].mCfg.sampleRate, pPlay_daudio0->PlayChls[AoChn].mCfg.chnCnt,
|
|
pPlay_daudio0->PlayChls[AoChn].mCfg.chunkSize);
|
|
//alogw("params of play_daudio0 chn[%d] must follow PlaybackRateDmix in asound.conf! [%d-%d-%d]", AoChn, pPlay->mDmixSampleRate, pPlay->mDmixChnNum, pPlay->mDmixPeriodSize);
|
|
// pPlay_daudio0->PlayChls[AoChn].mAttr.enSamplerate = map_SampleRate_to_AUDIO_SAMPLE_RATE_E(pPlay->mDmixSampleRate);
|
|
// pPlay_daudio0->PlayChls[AoChn].mAttr.mChnCnt = pPlay->mDmixChnNum;
|
|
// pPlay_daudio0->PlayChls[AoChn].mCfg.sampleRate = pPlay->mDmixSampleRate;
|
|
// pPlay_daudio0->PlayChls[AoChn].mCfg.chnCnt = pPlay->mDmixChnNum;
|
|
}
|
|
else
|
|
{
|
|
alogd("daudio0 chn[%d] already exists, keep its value! Ignore aoChn[%d] param!", pPlay_daudio0->daudio0_ao_chl, AoChn);
|
|
}
|
|
for(j=0; j<MAX_AO_CHLS; ++j)
|
|
{
|
|
if(pPlay->PlayChls[j].mState == AO_STATE_STARTED)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
// if(j == MAX_AO_CHLS) // only need to set AudioCodecHubMode when the AoChn is the first one
|
|
// {
|
|
// alsaMixerSetAudioCodecHubMode(pMixer,1); // to set hub mode for the first snd card
|
|
// }
|
|
// else
|
|
// {
|
|
// alogd("Be careful! aoChn[%d] is started, not set AudioCodecHubMode to 1", j);
|
|
// }
|
|
#endif
|
|
#if (MPPCFG_AEC == OPTION_AEC_ENABLE)
|
|
if(j == MAX_AO_CHLS) // only need to set daudio0 when the AoChn is the first one
|
|
{
|
|
//alsaMixerSetDAudio0HubMode(pMixer_daudio0,1);// to set hub mode for the second snd card
|
|
//alsaMixerSetDAudio0LoopBackEn(pMixer_daudio0,1);// to enable loopback for the second snd card
|
|
ret = alsaOpenPcm(&pPlay_daudio0->PlayChls[AoChn].mCfg, SOUND_CARD_SNDDAUDIo0, 1); // to open the second snd card
|
|
if (ret != 0)
|
|
{
|
|
aloge("fatal error! daudio0 open_pcm failed, ch:%d", AoChn);
|
|
pthread_mutex_unlock(&pPlay->mAOApiCallLock);
|
|
return FAILURE;
|
|
}
|
|
pPlay_daudio0->daudio0_ao_chl = AoChn; // record the ao chn index to set daudio0
|
|
// "hw:snddaudio0,0" play params must accord to audiocodec playback. alsa-plugins don't take effect in "hw:snddaudio0,0"
|
|
// play, but take effect in "hw:snddaudio0,0" capture. So when use plugin I2SRTX, play params don't matter.
|
|
ret = alsaSetPcmParams(&pPlay_daudio0->PlayChls[AoChn].mCfg); // to set params for the second snd card
|
|
if (ret < 0)
|
|
{
|
|
aloge("fatal error! SetPcmParams failed, ch:%d", AoChn);
|
|
goto ERR_SET_PCM_PARAM2;
|
|
}
|
|
alsaPreparePcm(&pPlay_daudio0->PlayChls[AoChn].mCfg); // and call the prepare api for the second snd card
|
|
alogd("daudio0 chn:%d prepare pcm done.", pPlay_daudio0->daudio0_ao_chl);
|
|
}
|
|
#endif
|
|
//const char *pCardType = (pPlay->PlayChls[AoChn].mAttr.mPcmCardId==PCM_CARD_TYPE_AUDIOCODEC) ? SOUND_CARD_AUDIOCODEC_PLAY:SOUND_CARD_SNDHDMI;
|
|
char *pCardType = NULL;
|
|
char strDmixIdentifier[128]; // e.g., PlaybackRateDmix:16000,1,960
|
|
if(PCM_CARD_TYPE_AUDIOCODEC == pPlay->PlayChls[AoChn].mAttr.mPcmCardId)
|
|
{
|
|
GeneratePlaybackRateDmixIdentifier(strDmixIdentifier, sizeof(strDmixIdentifier), 0, 0, 0);//pPlay->mDmixSampleRate, pPlay->mDmixChnNum, pPlay->mDmixPeriodSize
|
|
pCardType = strDmixIdentifier;
|
|
}
|
|
else if (PCM_CARD_TYPE_SNDHDMI == pPlay->PlayChls[AoChn].mAttr.mPcmCardId)
|
|
{
|
|
pCardType = SOUND_CARD_SNDHDMI;
|
|
}
|
|
else if (PCM_CARD_TYPE_UAC1 == pPlay->PlayChls[AoChn].mAttr.mPcmCardId)
|
|
{
|
|
pCardType = SOUND_CARD_UAC1;
|
|
}
|
|
ret = alsaOpenPcm(&pPlay->PlayChls[AoChn].mCfg, pCardType, 1);
|
|
if (ret != 0)
|
|
{
|
|
aloge("fatal error! open_pcm failed");
|
|
pthread_mutex_unlock(&pPlay->mAOApiCallLock);
|
|
return FAILURE;
|
|
}
|
|
ret = alsaSetPcmParams(&pPlay->PlayChls[AoChn].mCfg);
|
|
if (ret < 0)
|
|
{
|
|
aloge("fatal error! SetPcmParams failed");
|
|
goto ERR_SET_PCM_PARAM;
|
|
}
|
|
alsaPreparePcm(&pPlay->PlayChls[AoChn].mCfg); // and call the prepare api for the first snd card
|
|
|
|
//pPlay->mThdRunning = TRUE;
|
|
//pthread_create(&pPlay->mThdId, NULL, audioHw_AO_PlayThread, &pPlay);
|
|
|
|
pPlay->PlayChls[AoChn].mState = AO_STATE_STARTED;
|
|
pthread_mutex_unlock(&pPlay->mAOApiCallLock);
|
|
return SUCCESS;
|
|
|
|
ERR_SET_PCM_PARAM:
|
|
alsaClosePcm(&pPlay->PlayChls[AoChn].mCfg, 1); // 1: playback
|
|
#if (MPPCFG_AEC == OPTION_AEC_ENABLE)
|
|
ERR_SET_PCM_PARAM2:
|
|
alsaClosePcm(&pPlay_daudio0->PlayChls[AoChn].mCfg, 1); // close the second snd card
|
|
#endif
|
|
pthread_mutex_unlock(&pPlay->mAOApiCallLock);
|
|
return FAILURE;
|
|
}
|
|
|
|
ERRORTYPE audioHw_AO_DisableChn(AUDIO_DEV AudioDevId,AO_CHN AoChn)
|
|
{
|
|
AudioOutputDevice *pPlay = &gAudioHwDev[AudioDevId].mPlay;
|
|
AIO_MIXER_S *pMixer = &gAudioHwDev[AudioDevId].mMixer;
|
|
int tmp_cnt = 0;
|
|
|
|
#if (MPPCFG_AEC == OPTION_AEC_ENABLE)
|
|
AudioOutputDevice *pPlay_daudio0 = &gAudioHwDev[AudioDevId+1].mPlay; // related with second snd card
|
|
AIO_MIXER_S *pMixer_daudio0 = &gAudioHwDev[AudioDevId+1].mMixer;
|
|
|
|
#endif
|
|
pthread_mutex_lock(&pPlay->mAOApiCallLock);
|
|
if (pPlay->PlayChls[AoChn].mState == AO_STATE_INVALID) {
|
|
pthread_mutex_unlock(&pPlay->mAOApiCallLock);
|
|
return ERR_AO_NOT_CONFIG;
|
|
}
|
|
if (pPlay->PlayChls[AoChn].mState != AO_STATE_STARTED) {
|
|
pthread_mutex_unlock(&pPlay->mAOApiCallLock);
|
|
return SUCCESS;
|
|
}
|
|
|
|
//pPlay->mThdRunning = FALSE;
|
|
//pthread_join(pPlay->mThdId, (void*) &ret);
|
|
|
|
/* if (!list_empty(&pPlay->mChnList)) {
|
|
alogw("When ao_disable, still exist channle in PlayChnList?! list them below:");
|
|
AO_CHANNEL_S *pEntry;
|
|
list_for_each_entry(pEntry, &pPlay->mChnList, mList)
|
|
{
|
|
alogw("AoCardType[%d] AoChn[%d] still run!", pPlay->PlayChls[AoChn].mAttr.mPcmCardId, pEntry->mId);
|
|
}
|
|
pthread_mutex_unlock(&pPlay->mAOApiCallLock);
|
|
return SUCCESS;
|
|
} */
|
|
|
|
alogd("close pcm! current AoCardType:[%d-%d]",AoChn, pPlay->PlayChls[AoChn].mAttr.mPcmCardId);
|
|
alsaClosePcm(&pPlay->PlayChls[AoChn].mCfg, 1); // 1: playback
|
|
#if (MPPCFG_AEC == OPTION_AEC_ENABLE)
|
|
for(int j=0; j<MAX_AO_CHLS; ++j)
|
|
{
|
|
if(pPlay->PlayChls[j].mState == AO_STATE_STARTED)
|
|
{
|
|
tmp_cnt++;
|
|
}
|
|
}
|
|
if(1 == tmp_cnt)
|
|
{
|
|
//alsaMixerSetAudioCodecHubMode(pMixer,0); // to clean hub mode for the first snd card
|
|
//alsaMixerSetDAudio0HubMode(pMixer_daudio0,0);// to clean hub mode for the second snd card
|
|
//alsaMixerSetDAudio0LoopBackEn(pMixer_daudio0,0);// to disable loopback for the second snd card
|
|
alogd("close daudio0 pcm! current AoCardType:[%d-%d], daudio0:[%d-%d]",
|
|
AoChn, pPlay->PlayChls[AoChn].mAttr.mPcmCardId,
|
|
pPlay_daudio0->daudio0_ao_chl, pPlay_daudio0->PlayChls[pPlay_daudio0->daudio0_ao_chl].mAttr.mPcmCardId);
|
|
alsaClosePcm(&pPlay_daudio0->PlayChls[pPlay_daudio0->daudio0_ao_chl].mCfg, 1); // to close the the second snd card
|
|
pPlay_daudio0->daudio0_ao_chl = -1;
|
|
}
|
|
#endif
|
|
pPlay->PlayChls[AoChn].mState = AO_STATE_CONFIGURED;
|
|
pthread_mutex_unlock(&pPlay->mAOApiCallLock);
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
ERRORTYPE audioHw_AO_SetDacDrc(AUDIO_DEV AudioDevId, int enable)
|
|
{
|
|
AudioOutputDevice *pPlay = &gAudioHwDev[AudioDevId].mPlay;
|
|
AIO_MIXER_S *pMixer = &gAudioHwDev[AudioDevId].mMixer;
|
|
|
|
/* if (pPlay->mState != AO_STATE_STARTED) {
|
|
return ERR_AO_NOT_ENABLED;
|
|
} */
|
|
|
|
return alsaMixerSetAudioCodecDacDrc(pMixer, enable);
|
|
}
|
|
|
|
ERRORTYPE audioHw_AO_SetDacHpf(AUDIO_DEV AudioDevId, int enable)
|
|
{
|
|
AudioOutputDevice *pPlay = &gAudioHwDev[AudioDevId].mPlay;
|
|
AIO_MIXER_S *pMixer = &gAudioHwDev[AudioDevId].mMixer;
|
|
|
|
/* if (pPlay->mState != AO_STATE_STARTED) {
|
|
return ERR_AO_NOT_ENABLED;
|
|
} */
|
|
|
|
return alsaMixerSetAudioCodecDacHpf(pMixer, enable);
|
|
}
|
|
|
|
|
|
ERRORTYPE audioHw_AO_SetVolume(AUDIO_DEV AudioDevId, int s32VolumeDb)
|
|
{
|
|
AudioOutputDevice *pPlay = &gAudioHwDev[AudioDevId].mPlay;
|
|
AIO_MIXER_S *pMixer = &gAudioHwDev[AudioDevId].mMixer;
|
|
|
|
/* if (pPlay->PlayChls[AoChn].mState != AO_STATE_STARTED) {
|
|
return ERR_AO_NOT_ENABLED;
|
|
} */
|
|
|
|
return alsaMixerSetVolume(pMixer, 1, s32VolumeDb);
|
|
}
|
|
|
|
ERRORTYPE audioHw_AO_GetVolume(AUDIO_DEV AudioDevId, int *ps32VolumeDb)
|
|
{
|
|
AudioOutputDevice *pPlay = &gAudioHwDev[AudioDevId].mPlay;
|
|
AIO_MIXER_S *pMixer = &gAudioHwDev[AudioDevId].mMixer;
|
|
|
|
/* if (pPlay->PlayChls[AoChn].mState != AO_STATE_STARTED) {
|
|
return ERR_AO_NOT_ENABLED;
|
|
} */
|
|
|
|
return alsaMixerGetVolume(pMixer, 1, (long*)ps32VolumeDb);
|
|
}
|
|
|
|
ERRORTYPE audioHw_AO_SetSoftVolume(AUDIO_DEV AudioDevId, int s32Volume)
|
|
{
|
|
AudioOutputDevice *pPlay = &gAudioHwDev[AudioDevId].mPlay;
|
|
AIO_MIXER_S *pMixer = &gAudioHwDev[AudioDevId].mMixer;
|
|
|
|
/* if (pPlay->PlayChls[AoChn].mState != AO_STATE_STARTED) {
|
|
return ERR_AO_NOT_ENABLED;
|
|
} */
|
|
|
|
return alsaMixerSetSoftVolume(pMixer, 1, s32Volume);
|
|
}
|
|
|
|
ERRORTYPE audioHw_AO_GetSoftVolume(AUDIO_DEV AudioDevId, int *ps32Volume)
|
|
{
|
|
AudioOutputDevice *pPlay = &gAudioHwDev[AudioDevId].mPlay;
|
|
AIO_MIXER_S *pMixer = &gAudioHwDev[AudioDevId].mMixer;
|
|
|
|
/* if (pPlay->PlayChls[AoChn].mState != AO_STATE_STARTED) {
|
|
return ERR_AO_NOT_ENABLED;
|
|
} */
|
|
|
|
return alsaMixerGetSoftVolume(pMixer, 1, (long*)ps32Volume);
|
|
}
|
|
|
|
ERRORTYPE audioHw_AO_SetMute(AUDIO_DEV AudioDevId, BOOL bEnable, AUDIO_FADE_S *pstFade)
|
|
{
|
|
AudioOutputDevice *pPlay = &gAudioHwDev[AudioDevId].mPlay;
|
|
AIO_MIXER_S *pMixer = &gAudioHwDev[AudioDevId].mMixer;
|
|
|
|
/* if (pPlay->PlayChls[AoChn].mState != AO_STATE_STARTED) {
|
|
return ERR_AO_NOT_ENABLED;
|
|
} */
|
|
|
|
return alsaMixerSetMute(pMixer, 1, (int)bEnable);
|
|
}
|
|
|
|
ERRORTYPE audioHw_AO_GetMute(AUDIO_DEV AudioDevId, BOOL *pbEnable, AUDIO_FADE_S *pstFade)
|
|
{
|
|
AudioOutputDevice *pPlay = &gAudioHwDev[AudioDevId].mPlay;
|
|
AIO_MIXER_S *pMixer = &gAudioHwDev[AudioDevId].mMixer;
|
|
|
|
/* if (pPlay->PlayChls[AoChn].mState != AO_STATE_STARTED) {
|
|
return ERR_AO_NOT_ENABLED;
|
|
} */
|
|
|
|
int MainVolVal;
|
|
alsaMixerGetMute(pMixer, 1, &MainVolVal);
|
|
if (MainVolVal > 0)
|
|
*pbEnable = FALSE;
|
|
else
|
|
*pbEnable = TRUE;
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
ERRORTYPE audioHw_AO_SetPA(AUDIO_DEV AudioDevId, BOOL bHighLevel)
|
|
{
|
|
ERRORTYPE ret;
|
|
AudioOutputDevice *pPlay = &gAudioHwDev[AudioDevId].mPlay;
|
|
AIO_MIXER_S *pMixer = &gAudioHwDev[AudioDevId].mMixer;
|
|
|
|
ret = alsaMixerSetPlayBackPA(pMixer, (int)bHighLevel);
|
|
if(0 != ret)
|
|
{
|
|
aloge("fatal error! alsaMixer SetPlayBackPA fail[0x%x]!", ret);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
ERRORTYPE audioHw_AO_GetPA(AUDIO_DEV AudioDevId, BOOL *pbHighLevel)
|
|
{
|
|
ERRORTYPE ret;
|
|
AudioOutputDevice *pPlay = &gAudioHwDev[AudioDevId].mPlay;
|
|
AIO_MIXER_S *pMixer = &gAudioHwDev[AudioDevId].mMixer;
|
|
|
|
int bHighLevel = 0;
|
|
ret = alsaMixerGetPlayBackPA(pMixer, &bHighLevel);
|
|
if(0 == ret)
|
|
{
|
|
*pbHighLevel = bHighLevel?TRUE:FALSE;
|
|
}
|
|
else
|
|
{
|
|
aloge("fatal error! alsaMixer GetPlayBackPA fail[0x%x]!", ret);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
ERRORTYPE audioHw_AO_FillPcmRingBuf(AUDIO_DEV AudioDevId,AO_CHN AoChn, void* pData, int Len)
|
|
{
|
|
AudioOutputDevice *pPlay = &gAudioHwDev[AudioDevId].mPlay;
|
|
|
|
size_t frame_cnt = Len / (pPlay->PlayChls[AoChn].mCfg.bitsPerFrame >> 3);
|
|
ssize_t ret;
|
|
|
|
if (pPlay->PlayChls[AoChn].mState != AO_STATE_STARTED) {
|
|
return ERR_AO_NOT_ENABLED;
|
|
}
|
|
ret = alsaWritePcm(&pPlay->PlayChls[AoChn].mCfg, pData, frame_cnt);
|
|
if (ret != frame_cnt) {
|
|
aloge("alsaWritePcm error!");
|
|
return FAILURE;
|
|
}
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
ERRORTYPE audioHw_AO_DrainPcmRingBuf(AUDIO_DEV AudioDevId,AO_CHN AoChn)
|
|
{
|
|
AudioOutputDevice *pPlay = &gAudioHwDev[AudioDevId].mPlay;
|
|
alsaDrainPcm(&pPlay->PlayChls[AoChn].mCfg);
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
ERRORTYPE audioHw_AO_FeedPcmData(AUDIO_DEV AudioDevId,AO_CHN AoChn, AUDIO_FRAME_S *pFrm)
|
|
{
|
|
AudioOutputDevice *pPlay = &gAudioHwDev[AudioDevId].mPlay;
|
|
size_t frame_cnt = pFrm->mLen / (pPlay->PlayChls[AoChn].mCfg.bitsPerFrame >> 3);
|
|
ssize_t ret;
|
|
|
|
if (pPlay->PlayChls[AoChn].mState != AO_STATE_STARTED) {
|
|
return ERR_AO_NOT_ENABLED;
|
|
}
|
|
|
|
ret = alsaWritePcm(&pPlay->PlayChls[AoChn].mCfg, pFrm->mpAddr, frame_cnt);
|
|
if (ret != frame_cnt) {
|
|
aloge("alsaWritePcm error!");
|
|
return FAILURE;
|
|
}
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
ERRORTYPE audioHw_AO_GetPcmConfig(AUDIO_DEV AudioDevId,AO_CHN AoChn, PCM_CONFIG_S **ppCfg)
|
|
{
|
|
AudioOutputDevice *pPlay = &gAudioHwDev[AudioDevId].mPlay;
|
|
|
|
if (pPlay->PlayChls[AoChn].mState != AO_STATE_STARTED) {
|
|
return ERR_AO_NOT_ENABLED;
|
|
}
|
|
*ppCfg = &pPlay->PlayChls[AoChn].mCfg;
|
|
return SUCCESS;
|
|
}
|
|
|
|
ERRORTYPE audioHw_AO_GetAIOAttr(AUDIO_DEV AudioDevId,AO_CHN AoChn, AIO_ATTR_S **ppAttr)
|
|
{
|
|
AudioOutputDevice *pPlay = &gAudioHwDev[AudioDevId].mPlay;
|
|
|
|
if (pPlay->PlayChls[AoChn].mState != AO_STATE_STARTED) {
|
|
return ERR_AO_NOT_ENABLED;
|
|
}
|
|
*ppAttr = &pPlay->PlayChls[AoChn].mAttr;
|
|
return SUCCESS;
|
|
}
|
|
|
|
ERRORTYPE audioHw_AI_SuspendAns(AUDIO_DEV AudioDevId)
|
|
{
|
|
AudioInputDevice *pCap = &gAudioHwDev[AudioDevId].mCap;
|
|
AIO_MIXER_S *pMixer = &gAudioHwDev[AudioDevId].mMixer;
|
|
|
|
pthread_mutex_lock(&pCap->mChnListLock);
|
|
if (pCap->mState != AI_STATE_STARTED)
|
|
{
|
|
aloge("fatal error! audioDev[%d] is not started!", AudioDevId);
|
|
pthread_mutex_unlock(&pCap->mChnListLock);
|
|
return ERR_AI_NOT_ENABLED;
|
|
}
|
|
pCap->mbSuspendAns = TRUE;
|
|
pthread_mutex_unlock(&pCap->mChnListLock);
|
|
return SUCCESS;
|
|
}
|
|
ERRORTYPE audioHw_AI_ResumeAns(AUDIO_DEV AudioDevId)
|
|
{
|
|
AudioInputDevice *pCap = &gAudioHwDev[AudioDevId].mCap;
|
|
AIO_MIXER_S *pMixer = &gAudioHwDev[AudioDevId].mMixer;
|
|
|
|
pthread_mutex_lock(&pCap->mChnListLock);
|
|
if (pCap->mState != AI_STATE_STARTED)
|
|
{
|
|
aloge("fatal error! audioDev[%d] is not started!", AudioDevId);
|
|
pthread_mutex_unlock(&pCap->mChnListLock);
|
|
return ERR_AI_NOT_ENABLED;
|
|
}
|
|
pCap->mbSuspendAns = FALSE;
|
|
pthread_mutex_unlock(&pCap->mChnListLock);
|
|
return SUCCESS;
|
|
}
|
|
|
|
ERRORTYPE audioHw_AI_SuspendAec(AUDIO_DEV AudioDevId)
|
|
{
|
|
AudioInputDevice *pCap = &gAudioHwDev[AudioDevId].mCap;
|
|
AIO_MIXER_S *pMixer = &gAudioHwDev[AudioDevId].mMixer;
|
|
|
|
pthread_mutex_lock(&pCap->mChnListLock);
|
|
if (pCap->mState != AI_STATE_STARTED)
|
|
{
|
|
aloge("fatal error! audioDev[%d] is not started!", AudioDevId);
|
|
pthread_mutex_unlock(&pCap->mChnListLock);
|
|
return ERR_AI_NOT_ENABLED;
|
|
}
|
|
pCap->mbSuspendAec = TRUE;
|
|
pthread_mutex_unlock(&pCap->mChnListLock);
|
|
return SUCCESS;
|
|
}
|
|
|
|
ERRORTYPE audioHw_AI_ResumeAec(AUDIO_DEV AudioDevId)
|
|
{
|
|
AudioInputDevice *pCap = &gAudioHwDev[AudioDevId].mCap;
|
|
AIO_MIXER_S *pMixer = &gAudioHwDev[AudioDevId].mMixer;
|
|
|
|
pthread_mutex_lock(&pCap->mChnListLock);
|
|
if (pCap->mState != AI_STATE_STARTED)
|
|
{
|
|
aloge("fatal error! audioDev[%d] is not started!", AudioDevId);
|
|
pthread_mutex_unlock(&pCap->mChnListLock);
|
|
return ERR_AI_NOT_ENABLED;
|
|
}
|
|
pCap->mbSuspendAec = FALSE;
|
|
pthread_mutex_unlock(&pCap->mChnListLock);
|
|
return SUCCESS;
|
|
}
|
|
|
|
ERRORTYPE audioHw_AI_SetAgcDb(AUDIO_DEV AudioDevId, float fDbGain)
|
|
{
|
|
AudioInputDevice *pCap = &gAudioHwDev[AudioDevId].mCap;
|
|
//agc_handle *ai_agc_float_hld;
|
|
int ret = -1;
|
|
|
|
if (pCap->mState != AI_STATE_STARTED) {
|
|
return ERR_AI_NOT_ENABLED;
|
|
}
|
|
|
|
if (fDbGain < 0 || fDbGain > 95)
|
|
{
|
|
aloge("agc db gain %f invalid", fDbGain);
|
|
return ERR_AI_ILLEGAL_PARAM;
|
|
}
|
|
|
|
#if (MPPCFG_AGC == OPTION_AGC_ENABLE)
|
|
pthread_mutex_lock(&pCap->mAgcDbGainLock);
|
|
ret = func_agc_set_maxgain_db((agc_handle *)pCap->ai_agc_float_handle, fDbGain);
|
|
if (ret != 0)
|
|
{
|
|
aloge("agc float set db failed");
|
|
pthread_mutex_unlock(&pCap->mAgcDbGainLock);
|
|
return ERR_AI_ILLEGAL_PARAM;
|
|
}
|
|
else
|
|
{
|
|
pCap->mAttr.ai_agc_float_cfg.fMaxGainDb = fDbGain;
|
|
alogd("agc db gain set:%f", fDbGain);
|
|
}
|
|
pthread_mutex_unlock(&pCap->mAgcDbGainLock);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
ERRORTYPE audioHw_AI_GetAgcDb(AUDIO_DEV AudioDevId, float *pfVolumeDb)
|
|
{
|
|
AudioInputDevice *pCap = &gAudioHwDev[AudioDevId].mCap;
|
|
AIO_MIXER_S *pMixer = &gAudioHwDev[AudioDevId].mMixer;
|
|
|
|
if (pCap->mState != AI_STATE_STARTED) {
|
|
return ERR_AI_NOT_ENABLED;
|
|
}
|
|
|
|
pthread_mutex_lock(&pCap->mAgcDbGainLock);
|
|
*pfVolumeDb = pCap->mAttr.ai_agc_float_cfg.fMaxGainDb;
|
|
pthread_mutex_unlock(&pCap->mAgcDbGainLock);
|
|
|
|
alogd("get_ai_agc_db_gain:%f", *pfVolumeDb);
|
|
return 0;
|
|
}
|
|
|