icfans

如何实现连续的PCM流播放

0
阅读(2807)

[SOLUTION]
实现这类PCM的播放(类似于TTS)思路及sample code如下:

使用双buffer的机制,TTS使用一个,DSP使用另一个,两个大小一样。其中DSP使用的由DSP自行管理,TTS使用的由TTS自行管理。建议TTS使用ring buffer的机制,设置一个写指针和一个读指针,分别指示TTS可以写入的开始位置和DSP可以读出的开始位置,两者的差为可以写入和可以读出的长度。来自半导体社群

使用流程:
1.开始TTS合成.
2.合成出来第一段PCM数据后调用tts_media_play函数,开始播放,设置call back函数Pcm_play_callback。(在开始播放后,tts_media_play只应被调用一次,后面喂数据应该是由Pcm_play_callback来完成)

3.TTS继续合成数据,合成完后就放合适大小(通过计算读指针与写指针的差)的PCM数据到TTS buffer写指针指示的位置中,并暂停下来等待DSP的event发生。TTS enging如果不能暂停就只能把多出来的数据丢弃。

4.等DSP有event发生底层就会自动调用Pcm_play_callback,如果是MEDIA_DATA_REQUEST的event,就从TTS buffer里面拷贝合适大小(通过GetWriteBuffer获知DSP可以接受的数据,和TTS buffer的写指针与读指针的差做比较,取其中的小者)的数据到DSP buffer.如果DSP的event上来时TTS还来不及合成数据就立刻返回。

5.如果TTS把所有数据都合成完毕并全部传给了DSP的buffer,或者从DSP收到MEDIA_END,MEDIA_ERROR,MEDIA_TERMINATED,就调用Stop,Close,DataFinished停止播放。

下面是sample code,供参考:
#define TTS_BUFFER_LEN 1*1024
typedef unsigned char uint8;
typedef signed char int8;
typedef unsigned short int uint16;
typedef signed short int int16;
typedef unsigned int uint32;
typedef signed int int32;
struct VParam
{
uint8 *buf_p;
uint32 buf_len;
uint32 offset;
FS_HANDLE filehandle;
MHdl *mhdl_handle;
}TTSParam;
kal_uint16 tts_ring_buf[TTS_BUFFER_LEN];
void tts_init(void)
{
TTSParam.buf_p = tts_ring_buf;
TTSParam.buf_len = 0;
}
static void Pcm_play_callback( MHdl *mhdl, Media_Event event )
{
switch( event ){
case MEDIA_END:
case MEDIA_ERROR:
case MEDIA_TERMINATED:
{
TTSParam.mhdl_handle->Stop(TTSParam.mhdl_handle);
TTSParam.mhdl_handle->Close(TTSParam.mhdl_handle);
/* close the file */
FS_Close(TTSParam.filehandle);
break;
}
case MEDIA_DATA_REQUEST :
{
kal_uint32 read_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
{
/* close the file */
FS_Close(TTSParam.filehandle);
}
}
}
}
kal_int32 tts_media_play(void)
{
/*----------------------------------------------------------------*/
/* Local Variables */
/*----------------------------------------------------------------*/
kal_uint32 read_size=0;
kal_int32 audio_format;
kal_int32 result;
Media_Status aud_ret;
Media_PCM_Stream_Param vpFormat;
void *param = NULL;
/*----------------------------------------------------------------*/
/* Code Body */
/*----------------------------------------------------------------*/
{
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);
//here should be error handling for FS_Read
if (read_size > 0)
{
TTSParam.mhdl_handle->WriteDataDone(
TTSParam.mhdl_handle,
read_size);
aud_ret = TTSParam.mhdl_handle->Play(TTSParam.mhdl_handle);
}
else
{
/* close the file */
FS_Close(TTSParam.filehandle);
}
}
}
void tts_test1(void)
{
tts_init();
tts_media_play();
}