实现这类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.
来源:http://bbs.16rd.com/thread-479503-1-1.html
网友评论