1. 程式人生 > 其它 >海思3536輸出PCM多路混音

海思3536輸出PCM多路混音

技術標籤:筆記c++演算法音訊編碼解碼linux

由於專案需求,需要將進行多路混音的功能。

對於海思音訊庫的處理程式

//初始化
int AudioInit(AIO_MODE_E enWorkmode)
{
	HI_S32      s32Ret = HI_SUCCESS;
	ADEC_CHN 	AdChn = 0; 
	AUDIO_DEV	AoDev = 0;
	
	ADEC_CHN_ATTR_S stAdecAttr;
	ADEC_ATTR_AAC_S stAdecAac;
	
	stAdecAttr.enType = PT_AAC;
	stAdecAttr.u32BufSize = 20;
	stAdecAttr.enMode = ADEC_MODE_STREAM;/* propose use pack mode in your app */
	stAdecAttr.pValue = &stAdecAac;
	stAdecAttr.enMode = ADEC_MODE_STREAM;	/* aac should be stream mode */
	stAdecAac.enTransType = AAC_TRANS_TYPE_ADTS;

	
	/* create adec chn*/
	s32Ret = HI_MPI_ADEC_CreateChn(AdChn, &stAdecAttr);
	if (s32Ret)
	{
		printf("%s: HI_MPI_ADEC_CreateChn(%d) failed with %#x!\n", __FUNCTION__,AdChn,s32Ret);
		return s32Ret;
	}

    AIO_ATTR_S stAioAttr;
    stAioAttr.enSamplerate   = 44100; // 輸出取樣率可以是32k, 44.1k或者48k
    stAioAttr.enBitwidth     = AUDIO_BIT_WIDTH_16;
    stAioAttr.enWorkmode     = enWorkmode; 
    stAioAttr.enSoundmode 	 = AUDIO_SOUND_MODE_STEREO;
    
    stAioAttr.u32EXFlag      = 1;
    stAioAttr.u32FrmNum      = MAX_AUDIO_FRAME_NUM;
    stAioAttr.u32PtNumPerFrm = 1024;
    stAioAttr.u32ChnCnt      = 2; //立體聲模式需要設定為2
    stAioAttr.u32ClkChnCnt   = 1;
    stAioAttr.u32ClkSel      = 0;


    if (stAioAttr.u32ClkChnCnt == 0)
    {
        stAioAttr.u32ClkChnCnt = stAioAttr.u32ChnCnt;
    }
    
	//A0屬性設定
    s32Ret = HI_MPI_AO_SetPubAttr(AoDev, &stAioAttr);
    if (HI_SUCCESS != s32Ret)
    {
        printf("%sAoDev HI_MPI_AO_SetPubAttr(%d) failed with %#x!\n", __FUNCTION__, AoDev, s32Ret);
        return HI_FAILURE;
    }

	//使能AO
    s32Ret = HI_MPI_AO_Enable(AoDev);
    if (HI_SUCCESS != s32Ret)
    {
        printf("%s: HI_MPI_AO_Enable(%d) failed with %#x!\n", __FUNCTION__, AoDev, s32Ret);
        return HI_FAILURE;
    }

	//使能AO通道
    s32Ret = HI_MPI_AO_EnableChn(AoDev, 0);
    if (HI_SUCCESS != s32Ret)
    {
        printf("%s: HI_MPI_AO_EnableChn(0) failed with %#x!\n", __FUNCTION__, s32Ret);
        return HI_FAILURE;
    }    	
    
}

//ADEC傳送幀函式
int AdecSendFrame(unsigned int dwAdeChn, unsigned long long u64FrameNo, unsigned char * pbyBuffer, unsigned int dwDataLen,HI_BOOL bBlock)
{
	HI_S32      s32Ret = HI_SUCCESS;
	AUDIO_STREAM_S stAudioStream;  
	
	stAudioStream.pStream = pbyBuffer;
	stAudioStream.u32Len = dwDataLen;
	stAudioStream.u64TimeStamp = u64FrameNo;
	
	s32Ret = HI_MPI_ADEC_SendStream(dwAdeChn, &stAudioStream, bBlock);
	if(HI_SUCCESS != s32Ret)
    {
        printf("%s: HI_MPI_ADEC_SendStream(%d) failed with %#x!\n",\
               __FUNCTION__, dwAdeChn, s32Ret);
    }
	
	return s32Ret;
}

//ADEC獲取幀函式
int GetAdecEncodedFrame(unsigned int dwAencChn,AUDIO_FRAME_INFO_S *ptFrmInfo,HI_BOOL bBlock)
{

	HI_S32 s32Ret = HI_SUCCESS;

	if(ptFrmInfo == NULL)
	{
		return HI_FAILURE;

	}
	
	s32Ret = HI_MPI_ADEC_GetFrame(dwAencChn, ptFrmInfo, bBlock);
	if (s32Ret != HI_SUCCESS && s32Ret != HI_ERR_AENC_BUF_EMPTY)
	{
		printf("Failed to get frame from ADEC, s32Ret: %#x\n", s32Ret);
		return s32Ret;
	}
	
	return HI_SUCCESS;
}

//釋放ADEC幀函式
void ReleaseADECFrame(unsigned int dwAencChn,AUDIO_FRAME_INFO_S *ptFrmInfo)
{
	HI_MPI_ADEC_ReleaseFrame(dwAencChn,ptFrmInfo);
}

//A0傳送幀函式
int MPP_SendAudioFrameToAo(short *pLeftPcmBuffer,
                              short *pRightPcmBuffer,
                              int length,int sdwTimeoutMs)
{
	AUDIO_DEV	AoDev = 0;

    AUDIO_FRAME_S aoFrame;
    aoFrame.enBitwidth = AUDIO_BIT_WIDTH_16;
    aoFrame.enSoundmode = AUDIO_SOUND_MODE_STEREO;
    aoFrame.pVirAddr[0] = pLeftPcmBuffer;
    aoFrame.pVirAddr[1] = pRightPcmBuffer;
    aoFrame.u32Len =  length;
    HI_S32 ret = HI_MPI_AO_SendFrame(AoDev,
                                     0,
                                     &aoFrame,
                                     sdwTimeoutMs);
    if (ret != HI_SUCCESS)
    {
        printf("HI_MPI_AO_SendFrame %#x\n", ret);
        return ret;
    }
    return HI_SUCCESS;
}

混音處理程式碼:
PCM多路混音演算法參考:https://blog.csdn.net/daska110/article/details/80322696

#define AUDIO_DATA_TYPE short       
                                    
#define AUDIO_DATA_TYPE_MAX (32767)   // 2^15(short)
#define AUDIO_DATA_TYPE_MIN (-32768)

#define WIDEN_TEMP_TYPE int         // 4位元組有符號的中間變數,用於混音時防止溢位

#define AUDIO_MAX_CHN (32)

typedef int SDWORD

#include <math.h>
#include <vector>
#include <mutex>


using namespace std;

class AudioPcmMix
{
public:

    ~AudioPcmMix();

    static AudioPcmMix* GetInstance(void);

    // 疊加,然後歸一化
    void AddAndNormalization(vector< vector<AUDIO_DATA_TYPE> >&   allMixingSounds,
        DWORD                           dwRawDataCnt,
        vector<AUDIO_DATA_TYPE>&        rawDataBuffer);


    SDWORD PcmMixHandle();

    void DelAudioChnID(SDWORD sdwAudioChnID);
    void AddAudioChnID(SDWORD sdwAudioChnID);
    

private:
    AudioPcmMix();

public:  
    vector<AUDIO_DATA_TYPE>        m_leftRawDataBuffer;
    vector<AUDIO_DATA_TYPE>        m_rightRawDataBuffer;
    SDWORD m_sdwFrameLen;

private:
    vector<SDWORD> m_audioChnIDList;
    std::mutex m_audioMutex;
    SDWORD m_getFrameRet[AUDIO_MAX_CHN];	
    
};
AudioPcmMix::AudioPcmMix()  :m_sdwFrameLen(0),m_audioChnIDList(AUDIO_MAX_CHN,-1)
{

}

AudioPcmMix::~AudioPcmMix()
{
   
}

AudioPcmMix* AudioPcmMix::GetInstance(void)
{
    static AudioPcmMix mixStance;

    return &mixStance;
}

void AudioPcmMix::AddAndNormalization(vector< vector<AUDIO_DATA_TYPE> >&   allMixingSounds,
    DWORD                           dwRawDataCnt,
    vector<AUDIO_DATA_TYPE>&        rawDataBuffer)
{
    WIDEN_TEMP_TYPE Sum = 0;                                    // 用更大的範圍來表示(用有符號的int,而不要用無符號的DWORD)
    double decayFactor = 1;                                     // 衰減因子(防止溢位)

    for (int i = 0; i < dwRawDataCnt; ++i)
    {
        Sum = 0;                                                // 復位疊加的值
        for (int wavNum = 0; wavNum < allMixingSounds.size(); ++wavNum)
        {
            Sum += allMixingSounds[wavNum][i];
        }
        Sum *= decayFactor;                                     // 將衰減因子作用在疊加的音訊上

                                                                // 計算衰減因子
                                                                // 1. 疊加之後,會溢位,計算溢位的倍數(即衰減因子)
        if (Sum > AUDIO_DATA_TYPE_MAX)
        {
            decayFactor = static_cast<double>(AUDIO_DATA_TYPE_MAX) / static_cast<double>(Sum);  // 算大了,就用小數0.8衰減
            Sum = AUDIO_DATA_TYPE_MAX;
        }
        else if (Sum < AUDIO_DATA_TYPE_MIN)
        {
            decayFactor = static_cast<double>(AUDIO_DATA_TYPE_MIN) / static_cast<double>(Sum);  // 算小了,就用大數1.2增加
            Sum = AUDIO_DATA_TYPE_MIN;
        }

        // 2. 衰減因子的平滑(為了防止個別點偶然的溢位)
        if (decayFactor < 1)
        {
            decayFactor += static_cast<double>(1 - decayFactor) / static_cast<double>(32);
        }

        rawDataBuffer.push_back(AUDIO_DATA_TYPE(Sum));      // 把int再強制轉換回為short
    }
}

void AudioPcmMix::DelAudioChnID(SDWORD sdwAudioChnID)
{  
	m_audioMutex.lock();
     
    for(auto it = m_audioChnIDList.begin();it != m_audioChnIDList.end();it++)
    {
        if(*it == sdwAudioChnID)
        {
            m_getFrameRet[sdwAudioChnID] = -1;
            //丟棄這一幀的資料
            m_sdwFrameLen = 0;
            m_audioChnIDList.erase(it);
            break;
        }    
    }
    
    m_audioMutex.unlock();
}

void AudioPcmMix::AddAudioChnID(SDWORD sdwAudioChnID)
{   

	m_audioMutex.lock();
    

    for(auto it = m_audioChnIDList.begin();it != m_audioChnIDList.end();it++)
    {
        if(*it == sdwAudioChnID)
        {
            m_audioMutex.unlock();
            return;
        }    
    }
    
    m_audioChnIDList.push_back(sdwAudioChnID);
    
    m_audioMutex.unlock();
}



SDWORD AudioPcmMix::PcmMixHandle()
{
    m_sdwFrameLen = 0;
    
    //資料處理時加鎖
	m_audioMutex.lock()
    
    if(m_audioChnIDList.size() == 0)
    {       
        m_audioMutex.unlock();
        return -1;
    }
    
    memset(m_getFrameRet,-1,AUDIO_MAX_CHN);

    AUDIO_FRAME_INFO_S stFrmInfo[AUDIO_MAX_CHN];
    vector< vector<AUDIO_DATA_TYPE> >   allMixingSounds;
 
    m_leftRawDataBuffer.clear();
    m_rightRawDataBuffer.clear();

    for (auto &it : m_audioChnIDList)
    {
        SDWORD sdwChnID = it;
        if( (sdwChnID < 0) || (sdwChnID >= AUDIO_MAX_CHN))
        {
           continue; 
        }

        printf("sdwChnID = %d size() %d\n",sdwChnID,m_audioChnIDList.size());
		
		//需要進行阻塞處理,否則混的聲音將不對
        m_getFrameRet[sdwChnID] = GetAdecEncodedFrame(sdwChnID, stFrmInfo + sdwChnID, Block);

        printf("=========== channel[%d] get frame finish: %#x================", sdwChnID, m_getFrameRet[sdwChnID]);
    
        if (m_getFrameRet[sdwChnID] == 0)
        {
            printf("channel[%d] frame len: %#x\n", sdwChnID, stFrmInfo[sdwChnID].pstFrame->u32Len);
            
            if ((m_sdwFrameLen == 0) || (m_sdwFrameLen > stFrmInfo[sdwChnID].pstFrame->u32Len))
            {
                m_sdwFrameLen   = stFrmInfo[sdwChnID].pstFrame->u32Len; 
            } 
            
        }    
    }
    
    for (auto &it : m_audioChnIDList)
    {
        SDWORD sdwChnID = it;

        if( (sdwChnID < 0) || (sdwChnID >= AUDIO_MAX_CHN) )
        {
           continue; 
        }

        if ((m_getFrameRet[sdwChnID] == 0))
        {           
            AUDIO_DATA_TYPE *tmp = (AUDIO_DATA_TYPE *)stFrmInfo[sdwChnID].pstFrame->pVirAddr[0];
            if(tmp != nullptr)
            {
                allMixingSounds.push_back(vector<AUDIO_DATA_TYPE>(tmp,tmp + m_sdwFrameLen/sizeof(AUDIO_DATA_TYPE)));						      
            }
            else
            {
                printf("Get Left Frame Data Error\n");
            }   
        }
    } 

    //左聲道進行混音
    AddAndNormalization(allMixingSounds,m_sdwFrameLen/2,m_leftRawDataBuffer);

    allMixingSounds.clear();

    for (auto &it : m_audioChnIDList)
    {
        SDWORD sdwChnID = it;

        if( (sdwChnID < 0) || (sdwChnID >= AUDIO_MAX_CHN) )
        {
           continue; 
        }

        if ((m_getFrameRet[sdwChnID] == 0))
        { 
            AUDIO_DATA_TYPE *tmp = (AUDIO_DATA_TYPE *)stFrmInfo[sdwChnID].pstFrame->pVirAddr[1];
            						
            if(tmp != nullptr)
            {
                allMixingSounds.push_back(vector<AUDIO_DATA_TYPE>(tmp,tmp + m_sdwFrameLen/sizeof(AUDIO_DATA_TYPE)));						      
            }
            else
            {
                printf("Get Right Frame Data Error\n");
            }
        }
    }

    //右聲道進行混音
    AddAndNormalization(allMixingSounds,m_sdwFrameLen/2,m_rightRawDataBuffer);

    for (auto &it : m_audioChnIDList)
    {
        SDWORD sdwChnID = it;

        if( (sdwChnID < 0) || (sdwChnID >= AUDIO_MAX_CHN) )
        {
            continue; 
        }

        if ((m_getFrameRet[sdwChnID] != 0))
        {    
            continue;
        }
        
        //釋放獲取的幀
        ReleaseADECFrame(sdwChnID, stFrmInfo + sdwChnID);
    }
    
    
    m_audioMutex.unlock();

	return 0;
}

混音處理執行緒:


void AudioPcmMixThread(void)
{
    prctl(PR_SET_NAME,"AudioPcmMixThread");
    SDWORD sdwTimeoutMs = 0;
    SDWORD sdwRet = -1;

    while(1)
    {            
     	//獲取PCM混音資料
        if( AudioPcmMix::GetInstance()->PcmMixHandle() == 0)
        {      
        	//將PCM資料傳送到AO
            if(AudioPcmMix::GetInstance()->m_sdwFrameLen > 0)
            {
                sdwRet = SendAudioFrameToAo(AudioPcmMix::GetInstance()->m_leftRawDataBuffer.data(),AudioPcmMix::GetInstance()->m_rightRawDataBuffer.data(),AudioPcmMix::GetInstance()->m_sdwFrameLen,sdwTimeoutMs);
                           
            }   

            usleep(1*1000);               
        }
        else
        {
            sleep(1);
        }  
    } 
}

由於我們專案使用的是AAC音訊進行資料傳輸,因此需要使用ADEC庫將AAC解碼為PCM,然後在將PCM混音,最後傳送到A0中去,實現多路混音輸出的目的。

在程式當中,對於每個AAC的音訊連線都建立一個執行緒進行處理,將收到的AAC資料,通過AdecSendFrame進行傳送解碼,然後在混音處理執行緒當中統一通過GetAdecEncodedFrame取出解碼後的PCM資料,再進行多路混音處理。