开源语音代码eSpeak1.06 的移植到单片机的过程(一)0之分析下espeak.c 文件

以下所有的内容基于eSpeak1.06 的移植过程

1. 分析下espeak.c 文件先写的main 函数,大致就是初始化,设置回调函数,转换文件,里面的具体参数还不了解。

目前还存在的问题,怎么支持中文,源码里面的 非 C 非 H 的文件怎么放到工程中

void espeak_trans_text()
{
  int synth_flags = espeakCHARS_AUTO | espeakPHONEMES | espeakENDPAUSE;
  const char* sText = "tralalalallala";
  char file_type[] = ".wav";
  //初始化
  //nRate 是什么?
  samples_split = espeak_Initialize(AUDIO_OUTPUT_SYNCHRONOUS, 10000, NULL, 0);
  samples_split = samplerate * samples_split_seconds;
    
  espeak_SetSynthCallback(SynthCallback);
  espeak_SetParameter(espeakRATE, 175, 0);

  espeak_Synth(sText,15,0,POS_CHARACTER,0,synth_flags,NULL,NULL);
  espeak_Terminate();
}

 

2. 关键应该是这个回调函数

wav代表什么? 从下面的写函数 推测出 fwrite(wav,numsamples*2,1,f_wavfile); ,推测出  wav就是 音频数据,写到文件中,但是为啥要乘以2。

numsamples就是 音频数据的长度/2

还要处理两个事件,不懂

FILE *f_wavfile = NULL;  应该就是生成的.wav文件了。

samples_split 是样本

filetype 文件类型,已经提取是 .wav

samples_split = samples_split_seconds * samplerate; 怎么看,样本数就是 时间 乘以 采样率,解释的通,距离=时间乘以速度。类似的

跟下面的对不上啊 sprintf(fname,"%s_%.2d%s",wavefile,wavefile_count+1,filetype);   应该是多个  文件, hello01.wav,hello02.wav,是不是文件大小有限制,超过了,就生成多个文件,那么为啥用samples_split大于0 ,就分割了? 这里是个疑问?

不对,sentence  句子,下面说了,文件有限制,超过的话需要分割,那么分割的是samples_split_seconds ,就是制定 几秒大小的wav文件,超过了就分成多个,那么为啥我测试的时候,只能一个单词,不科学啊?以后再研究吧,难道是参数不对?

 

static int SynthCallback(short *wav, int numsamples, espeak_EVENT *events)
{//========================================================================
    char fname[210];
    //if(quiet) return(0);  // -q quiet mode,应该是命令的
    if(wav == NULL)
    {
        CloseWavFile();   //wav代表什么
        return(0);
    }

    while(events->type != 0) //事件类型
    {
        if(events->type == espeakEVENT_SAMPLERATE)  //取样频率
        {
            samplerate = events->id.number;
            samples_split = samples_split_seconds * samplerate;
        }
        else
        if(events->type == espeakEVENT_SENTENCE) //宣判,什么意思?
        {
            // start a new WAV file when the limit is reached, at this sentence boundary
            if((samples_split > 0) && (samples_total > samples_split))
            {
                CloseWavFile();
                samples_total = 0;
                wavefile_count++;
            }
        }
        events++;
    }

    if(f_wavfile == NULL)
    {
        if(samples_split > 0) //样本
        {
            sprintf(fname,"%s_%.2d%s",wavefile,wavefile_count+1,filetype);
            if(OpenWavFile(fname, samplerate) != 0)
                return(1);
        }
        else
        {
            if(OpenWavFile(wavefile, samplerate) != 0)
                return(1);
        }
    }

    if(numsamples > 0)
    {
        samples_total += numsamples;
        fwrite(wav,numsamples*2,1,f_wavfile);
    }
    return(0);
}

 

3. fflush 函数,会强迫将缓冲区的数据写会参数stream 指定的文件中。是文件系统中的文件吗?

stout 是什么?一个宏,应该是指定某个文件!

函数 ftell 用于得到文件位置指针当前位置相对于文件首的偏移字节数。

找到 它的 定义,是个文件。应该是linux 的写法。 FILE *f_wavfile = NULL;
static void CloseWavFile()
{
    unsigned int pos;
    if((f_wavfile==NULL) || (f_wavfile == stdout))
        return;
    fflush(f_wavfile);
    pos = ftell(f_wavfile);
    fseek(f_wavfile,4,SEEK_SET);
    Write4Bytes(f_wavfile,pos - 8);
    fseek(f_wavfile,40,SEEK_SET);
    Write4Bytes(f_wavfile,pos - 44);
    fclose(f_wavfile);
    f_wavfile = NULL;
} // end of CloseWavFile

 

4. 看下,wave_hdr 应该是  wav 文件的格式,这个文件的头应该是这样的,我找个试试

定义函数:int isspace(int c);
函数说明:检查参数c是否为空格字符,也就是判断是否为空格(' ')、定位字符(' \t ')、CR(' \r ')、换行(' \n ')、垂直定位字符(' \v ')或翻页(' \f ')的情况。

int OpenWavFile(char *path, int rate)
{
    static unsigned char wave_hdr[44] = {
        'R','I','F','F',0x24,0xf0,0xff,0x7f,'W','A','V','E','f','m','t',' ',
        0x10,0,0,0,1,0,1,0,  9,0x3d,0,0,0x12,0x7a,0,0,
        2,0,0x10,0,'d','a','t','a',  0x00,0xf0,0xff,0x7f};
    if(path == NULL)
        return(2);
    while(isspace(*path)) path++;

    f_wavfile = NULL;
    if(path[0] != 0)
    {
        if(strcmp(path,"stdout")==0)
            f_wavfile = stdout;
        else
            f_wavfile = fopen(path,"wb");
    }
    if(f_wavfile == NULL)
    {
        fprintf(stderr,"Can't write to: '%s'\n",path);
        return(1);
    }
    fwrite(wave_hdr,1,24,f_wavfile);
    Write4Bytes(f_wavfile,rate);
    Write4Bytes(f_wavfile,rate * 2);
    fwrite(&wave_hdr[32],1,12,f_wavfile);
    return(0);
} 

 

打开winhex 看看,对比了

 

对比下文档的说明

 

 

 

查询文件的长度,像是标准Linux 的写法

int GetFileLength(const char *filename)
{//====================================
    struct stat statbuf;
    if(stat(filename,&statbuf) != 0)
        return(0);
    if((statbuf.st_mode & S_IFMT) == S_IFDIR)
        return(-2);  // a directory
    return(statbuf.st_size);
}  // end of GetFileLength

 

播放声音的函数

void DisplayVoices(FILE *f_out, char *language),为啥找不到是哪里播放的?搞不懂播放的原理是啥?
 
5. 接下来需要确认,哪些是需要的文件? 关键现在确定哪些文件要用到?如果说跟文件系统有关的只有自己写的那部分就好了,查一查
posted @ 2021-06-01 18:07  429512065  阅读(299)  评论(0编辑  收藏  举报