在windows下装虚拟机UBuntu的机器上玩。你的虚拟机内存要大些(4g--8g最好)。不然很痛苦。
找感觉一定要从中文开始,我们直接拿那个清华大学的中文例子(thchs30)开练,
1 首先进入githab下载的源码中egs目录下找到这个例子,然后要看一遍它的介绍。在readme里面有语料库的下载方式。把它下完(3个压缩包全下)。4个多G比较大。
2 考到对应的文件夹下(与s5同级别即可),取名叫thchs30-openslr,将所有压缩包解压到这下面
3 打开s5目录,编辑cmd.sh. 如下:()
export train_cmd=run.pl
export decode_cmd=run.pl
export mkgraph_cmd=run.pl
export cuda_cmd=run.pl
4 打开run.sh,看到#data preparation这句,在它之后就全是shell的命令。建议一条一条的跑。不然中间会有莫名奇妙的断档和错误。如何一条条跑呢? 使用注释:
<<!EOF! !EOF! 这两句相当于C语言的/* */. 但愿这个你能看懂。在此谢谢小凡妹妹的在线指导。
5按照上面的一句一句的来。它大概有几个过程:数据准备,monophone单音素训练, tri1三因素训练, trib2进行lda_mllt特征变换,trib3进行sat自然语言适应,trib4做quick(这个我也不懂),后面就是dnn了
6当运行到dnn时候会报错,因为默认dnn都是用GPU来跑的。它会检查一下,发现只在CPU下,就终止了。这里建议不要跑dnn了。因为我试过,改成CPU之后跑了7,8天,才迭代17,18次。太慢了。而一次训练怎么的也得20多次。还要训练好几回。所以,想跑dnn的话还是找GPU吧。
7.下面说说前面的几个步骤干啥用。其实每一次都会有个模型,这些模型都可以用了,你可以看它的exp目录,所有的步骤都在这里面输出。我们先简单看一个,tri1,打开后,有个decode_test_word,里面有个scoring_kaldi,best_wer就是它的错误率,36.15%.好回到tri1下,看到final.mdl了吗,这个就是有用的东西,学出来的模型。另外还得到graph_word下面,找到words.txt,和HCLG.fst,一个是字典,一个是有限状态机。有这3个文件,就可以来使用你的识别功能了。
8.这步是比较好玩的了。我们用这个模型来识别自己的语言。(是非常的不准,只是感受一下而已),回到源码的src下。make ext 编译扩展程序。(在这之前确定你的tools文件夹下的portaudio已经装好)之后,会看到onlinebin文件夹。里面有两个程序,online-wav-gmm-decode-faster 用来回放wav文件来识别的,online-gmm-decode-faster用来从麦克风输入声音来识别的。
8.1 这里开个小差:portaudio 装好后,有可能收不到声音,可以装个audio recoder(用apt-get),之后用它录音试试,测测是否有声音,只要能录音,portaudio就没问题,一般装完就好了,不行就再重启一下。不知道为啥。
8.2 介绍下online。很鄙视kaldi在这块的设计,居然搞出来两个版本。online是不再维护了。但是非常好用,online2是新的。参数n多。也不支持从麦克风采集。特别不人性化,如果有那个朋友把online2研究好了,请把经验也分享一下。
9.我们找一个例子吧:去egs下,打开voxforge,里面有个online_demo,直接考到thchs30下。在online_demo里面建2个文件夹online-data work,在online-data下建两个文件夹audio和models,audio下放你要回放的wav,models建个文件夹tri1,把s5下的exp下的tri1下的final.mdl和35.mdl(final.mdl是快捷方式)考过去。把s5下的exp下的tri1下的graph_word里面的words.txt,和HCLG.fst,考到models的tri1下。
10。打开online_demo的run.sh
a)将下面这段注释掉:(这段是voxforge例子中下载现网的测试语料和识别模型的。我们测试语料自己准备,模型就是tri1了)
if [ ! -s ${data_file}.tar.bz2 ]; then
echo "Downloading test models and data ..."
wget -T 10 -t 3 $data_url;
if [ ! -s ${data_file}.tar.bz2 ]; then
echo "Download of $data_file has failed!"
exit 1
fi
fi
b) 然后再找到如下这句,将其路径改成tri1
ac_model_type=tri2b_mmi
# Alignments and decoding results
---------------------------------------------------------------
改成:
ac_model_type=tri1
c)
online-wav-gmm-decode-faster --verbose=1 --rt-min=0.8 --rt-max=0.85\
--max-active=4000 --beam=12.0 --acoustic-scale=0.0769 \
scp:$decode_dir/input.scp $ac_model/model
————————————————————————————
改成:online-wav-gmm-decode-faster --verbose=1 --rt-min=0.8 --rt-max=0.85\
--max-active=4000 --beam=12.0 --acoustic-scale=0.0769 \
scp:$decode_dir/input.scp $ac_model/final.mdl
11。直接./run.sh吧,就是开始回放识别了。里面有提示,./run.sh --test-mode live命令就是从麦克风识别。
12.升华部分在这里。我们试完tri1的模型后,一定很想试试tri2或3.但当你操作时,会遇到如下的问题:
ERROR (online-wav-gmm-decode-faster:LogLikelihoods():diag-gmm.cc:533) DiagGmm::ComponentLogLikelihood, dimension mismatch 39vs. 40
怎么解决? 仔细看看这个源文件,它是dieta的。如果要是ldp还得加matrix参数(拿tri2b举例)。
于是修改run.sh成如下这个样子 :(就是把final.mat考过来,引入命令中)
if [ -s $ac_model/matrix ]; then
trans_matrix=$ac_model/12.mat
fi
同时把把s5下的exp下的tri2b下的12.mat考到models的tri2b下。
13 再次修改run.sh成如下这个样子(添加2个参数--left-context=3 --right-context=3)
online-wav-gmm-decode-faster --verbose=1 --rt-min=0.8 --rt-max=0.85 \
--max-active=4000 --beam=12.0 --acoustic-scale=0.0769 --left-context=3 --right-context=3\
scp:$decode_dir/input.scp $ac_model/final.mdl $ac_model/HCLG.fst \
$ac_model/words.txt '1:2:3:4:5' ark,t:$decode_dir/trans.txt \
ark,t:$decode_dir/ali.txt $trans_matrix;;
14 运行./run.sh,结果如下。怎么样,有点酷不? 如果想使用tri2等模型做麦克风在线的,也同理修改就可以了。
online-wav-gmm-decode-faster --verbose=1 --rt-min=0.8 --rt-max=0.85 --max-active=4000 --beam=12.0 --acoustic-scale=0.0769 --left-context=3 --right-context=3 scp:./work/input.scp online-data/models/tri2b/final.mdl online-data/models/tri2b/HCLG.fst online-data/models/tri2b/words.txt 1:2:3:4:5 ark,t:./work/trans.txt ark,t:./work/ali.txt online-data/models/tri2b/12.mat
File: D4_750
苏北 军礼 下跪 将 是 马 湛 杀人 里 杜 唐 据 五 苏 并 案 但 甜美 但 也 分析 抗战
科大讯飞的语音识别库,感觉只有后半部分,想实现前半部分的采集却找不到可以用的东西。于是自己做一个。
可以随便拿个开源的代码改一下,我们就使用arecord吧。
这只是个简单的端点检测,根据声音能量,也就是分贝大小来调整的。
下载源码:
http://www.alsa-project.org/main/index.PHP/Main_Page
arecord在alsa的alsa-utils里面。当然依赖于Library (alsa-lib),所以两个都要下下来
下载页在这:
http://www.alsa-project.org/main/index.php/Download
然后在本地就可以编译了。编译时会提示需要各种工具,直接按提示下载即可。 我用的是ubuntu直接apt-get,就得到了。
编好之后,进入aplay文件夹下。可以看到arecord其实是一个链接链到aplay了。aplay.c就是源码,我们直接改它就行
一:定义变量:
95行左右,
static char *command;
static snd_pcm_t *handle;
static struct {
snd_pcm_format_t format;
unsigned int channels;
unsigned int rate;
} hwparams, rhwparams;
static int timelimit = 0;
//add by ljh
static int silflag = 0; //出现静音情况
static off64_t silcount= 0; //j静音时要收到的包
static int enablesil = 0; //启用静音检测状态
1648行,修改函数如下
static void print_vu_meter_mono(int perc, int maxperc)//因为是单音输入,所以我们只改这一块,调用时配合mono设置。
{
const int bar_length = 50;
char line[80];
int val;
//add by ljh
if(perc>=2)//speak
{
enablesil = 1;// sel open
silflag = 0;
}else if(silflag==0)//into silflag
{
//init silcount 1s
silcount = 2* snd_pcm_format_size(hwparams.format, hwparams.rate * hwparams.channels);
silflag =1;
}else // already silence
{
}
for (val = 0; val <= perc * bar_length / 100 && val < bar_length; val++)
line[val] = '#';
for (; val <= maxperc * bar_length / 100 && val < bar_length; val++)
line[val] = ' ';
line[val] = '+';
for (++val; val <= bar_length; val++)
line[val] = ' ';
if (maxperc > 99)
sprintf(line + val, "| MAX");
else
sprintf(line + val, "| %02i%%", maxperc);
fputs(line, stderr);
if (perc > 100)
fprintf(stderr, _(" !clip "));
}
最后是capture函数,分别在 3050和3103处修改:
static void capture(char *orig_name)
{
int tostdout=0;/* boolean which describes output stream */
int filecount=0;/* number of files written */
char *name = orig_name;/* current filename */
char namebuf[PATH_MAX+1];
off64_t count, rest;/* number of bytes to capture */
struct stat statbuf;
/* get number of bytes to capture */
count = calc_count();
if (count == 0)
count = LLONG_MAX;
/* compute the number of bytes per file */
max_file_size = max_file_time *
snd_pcm_format_size(hwparams.format,
hwparams.rate * hwparams.channels);
/* WAVE-file should be even (I'm not sure), but wasting one byte
isn't a problem (this can only be in 8 bit mono) */
if (count < LLONG_MAX)
count += count % 2;
else
count -= count % 2;
/* display verbose output to console */
header(file_type, name);
/* setup sound hardware */
set_params();
/* write to stdout? */
if (!name || !strcmp(name, "-")) {
fd = fileno(stdout);
name = "stdout";
tostdout=1;
if (count > fmt_rec_table[file_type].max_filesize)
count = fmt_rec_table[file_type].max_filesize;
}
init_stdin();
//add by ljh
silcount = 2* snd_pcm_format_size(hwparams.format, hwparams.rate * hwparams.channels);
do {
/* open a file to write */
if(!tostdout) {
/* upon the second file we start the numbering scheme */
if (filecount || use_strftime) {
filecount = new_capture_file(orig_name, namebuf,
sizeof(namebuf),
filecount);
name = namebuf;
}
/* open a new file */
if (!lstat(name, &statbuf)) {
if (S_ISREG(statbuf.st_mode))
remove(name);
}
fd = safe_open(name);
if (fd < 0) {
perror(name);
prg_exit(EXIT_FAILURE);
}
filecount++;
}
rest = count;
if (rest > fmt_rec_table[file_type].max_filesize)
rest = fmt_rec_table[file_type].max_filesize;
if (max_file_size && (rest > max_file_size))
rest = max_file_size;
/* setup sample header */
if (fmt_rec_table[file_type].start)
fmt_rec_table[file_type].start(fd, rest);
/* capture */
fdcount = 0;
while (rest > 0 && recycle_capture_file == 0 && !in_aborting) {
size_t c = (rest <= (off64_t)chunk_bytes) ?
(size_t)rest : chunk_bytes;
size_t f = c * 8 / bits_per_frame;
if (pcm_read(audiobuf, f) != f)
break;
if (write(fd, audiobuf, c) != c) {
perror(name);
prg_exit(EXIT_FAILURE);
}
count -= c;
rest -= c;
fdcount += c;
//add by ljh
if(silflag == 1 && enablesil == 1)//silence sil open
{
silcount -= c;
if(silcount <= 0)// after 1s
{
// game over
count = 0;
rest = 0;
}
}
}
/* re-enable SIGUSR1 signal */
if (recycle_capture_file) {
recycle_capture_file = 0;
signal(SIGUSR1, signal_handler_recycle);
}
/* finish sample Container */
if (fmt_rec_table[file_type].end && !tostdout) {
fmt_rec_table[file_type].end(fd);
fd = -1;
}
if (in_aborting)
break;
/* repeat the loop when format is raw without timelimit or
* requested counts of data are recorded
*/
} while ((file_type == FORMAT_RAW && !timelimit) || count > 0);
}
然后再编译就好了。配合到代码里使用可以这样:
system("./arecord -D hw:0,0 -f S16_LE -V mono -r8000 -c 2 -t wav wav/mytest.wav");
把aplay拷过去,建立个链接ln ./alplay arecord
记得把依赖的库libasound.so.2也得考过去哦。
原理:capture中,每次会把读到的数据做个能量计算并打印出来,通过其计算的部分来设置个静音开始状态,同时在设置接下来需要读多少个字节,这部分可以仿照其原有的count计算方法。这样,当进入静音状态开始时,系统再读入设定好的字节后,就自动退出了。当然在此期间,假如用户又说话了。那么静音状态关闭,silcount将不会生效。直到再此进入静音状态开始
还有一个点是当录音刚启动时,如果用户不说话,我们不希望它退出,要一直等在那里,于是又需要个变量就是启用录音状态enablesil ,默认是不启用,一旦有用户说话,就把它启用。这样silflag就开始生效了。
如果想直接拿来用,这里有编译好的程序,和源码可以下载:
http://download.csdn.NET/detail/lijin6249/9580171