/****************************************************************************** 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 #include #include #include #include #include #include #include #include #include #include #include #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 #elif(MPPCFG_AEC_LIB == OPTION_AEC_LIBRARY_UVOICE) #include #endif #endif #if (MPPCFG_ANS == OPTION_ANS_ENABLE) #if (MPPCFG_ANS_LIB == OPTION_ANS_LIBRARY_WEBRTC) #include #elif(MPPCFG_ANS_LIB == OPTION_ANS_LIBRARY_LSTM) #include #elif(MPPCFG_ANS_LIB == OPTION_ANS_LIBRARY_NOSC) #include #endif #endif #if (MPPCFG_AGC == OPTION_AGC_ENABLE) //#include #include #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.CaptureMic:, : the number of MIC. : set sample rate to Hardware PCM. pcm.CaptureMicPlusAec, 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; jmPlay.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; jmPlay.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; jmPlay.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; jmPlay.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; jmPlay.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<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; imAttr.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; jPlayChls[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; jPlayChls[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; }