C#后端实现文字转语音、语音转文字,语音识别技术。
文字转语音意思是前端上传一段文字时,我们可以在后台生成一段wav语音回传给前端,前端可以直接播放出来。
语音转文字是前端录制好一段语音上传上来,我们可以在后台对语音文件进行解析,生成一段文字返回给前端。
1. 文字转语音
使用到的库只有一个,Speech,跟之前一样引用管理里面搜索并安装即可。
代码较为简单,有两种选择,一种是生成文件,一种是生成数据流,两种方法都很好用,我这边使用的是生成数据流,这样不用在服务器上存文件,比较节省存储空间。
如果涉及到html文本转纯文本的,建议使用下面这个正则将字符串处理一下。
Regex.Replace(Regex.Replace(content, "<.*?>", ""), " ", "")
这个文字转语音比较强大,我试过转六千多字的长文本,速度很快只用了几秒,播放器来也没有卡顿,大小也只有几十兆,比较好用。
上线时遇到一个问题,因为我们服务器是部署在windows server上的IIS上的,上线后报了个错。不能设置语音。未安装匹配的语音,或语音被禁用。虽然能用GetInstalledVoices方法获取到已安装的语音信息,但是使用不了。这个错误的原因在于IIS上程序池的权限太低了,不能够访问系统语音所在的文件目录。解决办法为调高IIS程序池的标识,设置程序池标识为LocalSystem即可。
2. 语音转文字(仅支持机器语音或自定义语意识别后转文字,自然语音基本识别不到)
语音转文字与文字转语音用的是同一个库,Speech,不用再下载安装了,直接上代码。
识别前要先定义好语法,接口将会识别第一个匹配到的单词并返回。
语音转文字也有两种模式,Recognize适合短文本,RecognizeAsync适合长文本。使用Recognize可以同步获取到识别结果,但仅限于头一次识别,如果是长文本用这个来做会自动把后面信息忽略掉。RecognizeAsync需要搭配SpeechReCognized监听一并使用,会逐条将识别到的语音返回回来,缺点就是没办法确定这个方法什么时候执行完成,以及执行完成后内容如何返回给前端,我的建议是如果要使用RecognizeAsync,可以搭配SignalR做一个实时通信,识别完成后实时返回。
下面补充一下前端播放音频文件的方法:
1.纯js前端播放
纯js前端播放可以使用js提供的Audio对象,Audio对象初始化时传入我们请求文件的地址即可,后面注册两个监听,一个playing监听是文件下载好开始播放时的回调,一个ended是播放完成后停止的回调,我们在这两个回调里面可以处理一些页面相关的逻辑。回调注册好就可以调用music.play()方法了,这个方法会先下载文件,下载好后开始播放。
<script>
function articleToStream() {
music = new Audio('/api/AI/articleToStream?articleId=' + @Model.Id);
music.addEventListener('playing', function () {
loadingEnd();
var video_pause = document.getElementById("video_layout");
var text_to_speech = document.getElementById("text_to_speech");
video_pause.style.display = "block";
text_to_speech.style.display = "none";
});
music.addEventListener('ended', function () {
var video_pause = document.getElementById("video_layout");
var text_to_speech = document.getElementById("text_to_speech");
video_pause.style.display = "none";
text_to_speech.style.display = "block";
music = null;
});
loading();
music.play();
}
</script>
暂停的方法简单,music.pause(),重新开始播放页简单,再调一次music.play()即可,但我没找到关于停止播放的方法,所以停止时还是用了music.pause(),并且把music置空,然后处理一些页面逻辑,差不多一个意思。
2.uni-app前端播放
uni-app前端播放也是使用的框架提供的控件uni.createInnerAudioContext(),也是定义几个回调方法,onPlay是开始播放时的回调,onEnded是播放完成停止时的回调,onCanplay是可以播放时的回调。
InnerAudioContext文档上写的有些浏览器不支持autoplay,所以我们就不用自动播放了,在onCanplay回调中调用播放方法进行播放。
首先把文件路径赋值给src字段,等文件准备好了就会走onCanplay方法。
下面贴一下布局代码跟播放的主要代码:
<view style="position: absolute; top: 16px; right: 16px;">
<view v-if="showAudioLayout"
style="border: 3px solid #000; border-radius: 40px; padding: 2px 6px;">
<image v-if="showAudioPause" class="audio_img" src="/static/images/audio_pause.png"
@tap="pause()" />
<image v-else class="audio_img" src="/static/images/audio_play.png" @tap="play()" />
<image class="audio_img" src="/static/images/audio_stop.png" @tap="stop()" />
</view>
<view v-else @tap="articleToStream()">
<image class="audio_img" src="/static/images/text_to_speech.png" />
</view>
</view>
articleToStream: function() {
var that = this;
console.log("开始")
uni.showLoading({
title: "加载中,请稍候..."
});
const innerAudioContext = uni.createInnerAudioContext();
innerAudioContext.onError((res) => {
console.log(res.errMsg);
console.log(res.errCode);
});
innerAudioContext.onPlay(() => {
uni.hideLoading();
console.log("开始播放")
that.setData({
showAudioLayout: true
})
});
innerAudioContext.onEnded(() => {
console.log("停止播放")
that.innerAudioContext.destroy();
that.setData({
innerAudioContext: null,
showAudioLayout: false
});
});
innerAudioContext.onCanplay(() => {
console.log("可以播放")
that.innerAudioContext.play();
});
innerAudioContext.src = that.hostUrl + '/api/AI/articleToStream?articleId=247';
that.setData({
innerAudioContext
});
},
uni-app这个InnerAudioContext播放器很强大,有很多方法可以自己研究一下,这边就不赘述了。