MTK FAQ:如何實現連續的PCM流播放

SZX511發表於2019-08-20

實現這類PCM的播放(類似於TTS)思路及samplecode如下:

使用雙buffer的機制,TTS使用一個,DSP使用另一個,兩個大小一樣。其中DSP使用的由DSP自行管理,TTS使用的由TTS自行管理。建議TTS使用ringbuffer的機制,設定一個寫指標和一個讀指標,分別指示TTS可以寫入的開始位置和DSP可以讀出的開始位置,兩者的差為可以寫入和可以讀出的長度。

使用流程:

1.開始TTS合成.

2.合成出來第一段PCM資料後呼叫tts_media_play函式,開始播放,設定callback函式Pcm_play_callback。(在開始播放後,tts_media_play只應被呼叫一次,後面喂資料應該是由Pcm_play_callback來完成)

3.TTS繼續合成資料,合成完後就放合適大小(透過計算讀指標與寫指標的差)的PCM資料到TTSbuffer寫指標指示的位置中,並暫停下來等待DSP的event發生。TTSenging如果不能暫停就只能把多出來的資料丟棄。

4.等DSP有event發生底層就會自動呼叫Pcm_play_callback,如果是MEDIA_DATA_REQUEST的event,就從TTSbuffer裡面複製合適大小(透過GetWriteBuffer獲知DSP可以接受的資料,和TTSbuffer的寫指標與讀指標的差做比較,取其中的小者)的資料到DSPbuffer.如果DSP的event上來時TTS還來不及合成資料就立刻返回。

5.如果TTS把所有資料都合成完畢並全部傳給了DSP的buffer,或者從DSP收到MEDIA_END,MEDIA_ERROR,MEDIA_TERMINATED,就呼叫Stop,Close,DataFinished停止播放。

下面是samplecode,供參考:

#defineTTS_BUFFER_LEN1*1024

typedefunsignedcharuint8;

typedefsignedcharint8;

typedefunsignedshortintuint16;

typedefsignedshortintint16;

typedefunsignedintuint32;

typedefsignedintint32;

structVParam

{

uint8*buf_p;

uint32buf_len;

uint32offset;

FS_HANDLEfilehandle;

MHdl*mhdl_handle;

}TTSParam;

kal_uint16tts_ring_buf[TTS_BUFFER_LEN];

voidtts_init(void)

{

TTSParam.buf_p=tts_ring_buf;

TTSParam.buf_len=0;

}

staticvoidPcm_play_callback(MHdl*mhdl,Media_Eventevent)

{

switch(event){

caseMEDIA_END:

caseMEDIA_ERROR:

caseMEDIA_TERMINATED:

{

TTSParam.mhdl_handle->Stop(TTSParam.mhdl_handle);

TTSParam.mhdl_handle->Close(TTSParam.mhdl_handle);

/*closethefile*/

FS_Close(TTSParam.filehandle);

break;

}

caseMEDIA_DATA_REQUEST:

{

kal_uint32read_size=0;

TTSParam.mhdl_handle->GetWriteBuffer(

TTSParam.mhdl_handle,

&TTSParam.buf_p,

&TTSParam.buf_len);

FS_Read(TTSParam.filehandle,TTSParam.buf_p,TTSParam.buf_len,&read_size);

if(read_size>0)

{

TTSParam.mhdl_handle->WriteDataDone(

TTSParam.mhdl_handle,

read_size);

TTSParam.mhdl_handle->FinishWriteData(TTSParam.mhdl_handle);

}

else

{

/*closethefile*/

FS_Close(TTSParam.filehandle);

}

}

}

}

kal_int32tts_media_play(void)

{

/*----------------------------------------------------------------*/

/*LocalVariables*/

/*----------------------------------------------------------------*/

kal_uint32read_size=0;

kal_int32audio_format;

kal_int32result;

Media_Statusaud_ret;

Media_PCM_Stream_ParamvpFormat;

void*param=NULL;

/*----------------------------------------------------------------*/

/*CodeBody*/

/*----------------------------------------------------------------*/

{

vpFormat.isStereo=0;

vpFormat.bitPerSample=16;

vpFormat.sampleFreq=8000;

param=&vpFormat;

TTSParam.filehandle=FS_Open(L"C:\\Images\\OutPcm.pcm",FS_READ_ONLY);

if((TTSParam.mhdl_handle=PCM_Strm_Open(Pcm_play_callback,param))==NULL)

{

return;

}

TTSParam.mhdl_handle->SetBuffer(

TTSParam.mhdl_handle,

(kal_uint8*)tts_ring_buf,

TTS_BUFFER_LEN*2);

TTSParam.mhdl_handle->GetWriteBuffer(

TTSParam.mhdl_handle,

&TTSParam.buf_p,

&TTSParam.buf_len);

FS_Read(TTSParam.filehandle,TTSParam.buf_p,TTSParam.buf_len*2,&read_size);

//hereshouldbeerrorhandlingforFS_Read

if(read_size>0)

{

TTSParam.mhdl_handle->WriteDataDone(

TTSParam.mhdl_handle,

read_size);

aud_ret=TTSParam.mhdl_handle->Play(TTSParam.mhdl_handle);

}

else

{

/*closethefile*/

FS_Close(TTSParam.filehandle);

}

}

}

voidtts_test1(void)

{

tts_init();

tts_media_play();

}

因為在singlebankflash的情況下,為了防止同時讀寫一個bank的情況發生,需要把wavetable的資料搬到RAM上,所以設定為RW資料。但如果你的flash本身是multibank,卻開啟了singlebanksupport,那麼只要保證wavetable放在和FAT不相同的一個bank上,就可以不必搬到RAM上,亦即可以修改為const.


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31529038/viewspace-2654280/,如需轉載,請註明出處,否則將追究法律責任。

相關文章