最近由于需要做一个关于语音的c#项目,经老师推荐使用了讯飞的语音识别和合成的接口,但由于网上资源关于c#实时语音识别这块实在是太少了,经查阅网上资源和研究源代码,最终完成了一个还算满意的demo,供各位后来者参考和借鉴,希望后来者能少走点弯路。

以下是界面图:
demo界面

MSC模块导入及添加枚举常量等

导入msc.dll,讯飞的语音识别和语音函数是封装起来的,需要我们import进去
MSCDLL类
除此之外,我们还需要创建需要用到的枚举常量和结构体等
枚举

语音识别模块

这部分模块我们需要用到的是麦克风音频(NAudio)和语音识别类。

首先,连续的语音识别是需要用到麦克风的,这时候就需要创建一个麦克风类,来实现声音的获取,及对声音的识别。
麦克风类
其中较为关键的函数是wis_back_DataAvailable和OnDataAvailable,前一个是处理得到的音量,后一个是处理得到的数据。
wis_back_DataAvailable和OnDataAvailable具体实现

private void wis_back_DataAvailable(object sender, WaveInEventArgs e)
{
	long sh = System.BitConverter.ToInt64(e.Buffer, 0);
	long width = (long)Math.Pow(2, 50);
	float svolume = Math.Abs(sh / width);
	if (svolume > 1500.0f) { svolume = 1500.0f; }//音量最大值
	if (svolume < 50.0f) { svolume = 50.0f; }//音量最小值
	this.volume = svolume / 15.0f;//最小3.3333333
	DataArrived(this, new DataArrivedEventArgs(this.volume));//激发音量数据到达事件
}

void OnDataAvailable(object sender, WaveInEventArgs e)
{
	totalBufferLength += e.Buffer.Length;
	secondsRecorded = (float)(totalBufferLength / 32000);

	VoiceData data = new VoiceData();
	for (int i = 0; i < e.Buffer.Length; i++)
	{
		data.data[i] = e.Buffer[i];
	}
	VoiceBuffer.Add(data);//添加录音数据

	if (volume < 4)//音量低于4则开始准备识别
	Ends = Ends - 1;
	else
	Ends = 5;

	if (Ends == 0)
	{
		if (VoiceBuffer.Count() > 5)
		{
			isr.RunIAT(VoiceBuffer);//调用语音识别 
		}
		VoiceBuffer.Clear();
		Ends = 5;
	}
}

其中VoiceData 是我创建的处理音频数据的类
VoiceData类

接下来是语音识别类,这是实现语音识别的关键类
语音识别类
其中有两个语音识别的函数,其区别是一个通过麦克风获取声音数据来识别,一个通过读取文件数据来识别。

// 语音识别(从麦克风中获取数据)
public void RunIAT(List<VoiceData> VoiceBuffer)
        {
            IntPtr session_id = IntPtr.Zero;//sessionID是本次识别的句柄
            string rec_result = string.Empty;//识别结果
            string hints = "正常结束";
            AudioStatus aud_stat = AudioStatus.ISR_AUDIO_SAMPLE_CONTINUE;//用来告知MSC音频发送是否完成
            EpStatus ep_stat = EpStatus.ISR_EP_LOOKING_FOR_SPEECH;//端点检测器所处的状态
            RecogStatus rec_stat = RecogStatus.ISR_REC_STATUS_SUCCESS;//识别器返回的状态,提醒用户及时开始\停止获取识别结果
            int errcode = (int)ErrorCode.MSP_SUCCESS;

            session_id = MSCDLL.QISRSessionBegin(null, QISRsession_begin_params, ref errcode);
            if ((int)ErrorCode.MSP_SUCCESS != errcode)
            {
                Console.WriteLine("QISRSessionBegin failed!(语音识别开始阶段) error code:" + errcode);
                return;
            }

            for (int i = 0; i < VoiceBuffer.Count(); i++)
            {
                aud_stat = AudioStatus.ISR_AUDIO_SAMPLE_CONTINUE;
                if (i == 0)
                    aud_stat = AudioStatus.ISR_AUDIO_SAMPLE_FIRST;
                errcode = MSCDLL.QISRAudioWrite(MSCDLL.PtrToStr(session_id), VoiceBuffer[i].data, (uint)VoiceBuffer[i].data.Length, aud_stat, ref ep_stat, ref rec_stat);
                if ((int)ErrorCode.MSP_SUCCESS != errcode)
                {
                    MSCDLL.QISRSessionEnd(MSCDLL.PtrToStr(session_id), null);
                    Console.WriteLine("QISRSessionEnd failed!(音频写入过程) error code:" + errcode);
                }
            }

            //写入最后一块音频(空的)
            errcode = MSCDLL.QISRAudioWrite(MSCDLL.PtrToStr(session_id), null, 0, AudioStatus.ISR_AUDIO_SAMPLE_LAST, ref ep_stat, ref rec_stat);
            if ((int)ErrorCode.MSP_SUCCESS != errcode)
            {
                Console.WriteLine("QISRAudioWrite failed!(写入最后一块音频) error code:" + errcode);
                return;
            }

            while (RecogStatus.ISR_REC_STATUS_SPEECH_COMPLETE != rec_stat)
            {
                IntPtr rslt = MSCDLL.QISRGetResult(MSCDLL.PtrToStr(session_id), ref rec_stat, 0, ref errcode);
                if ((int)ErrorCode.MSP_SUCCESS != errcode)
                {
                    Console.WriteLine("QISRGetResult failed!(获得结果阶段) error code: " + errcode);
                    break;
                }
                if (IntPtr.Zero != rslt)
                {
                    string tempRes = MSCDLL.PtrToStr(rslt);

                    rec_result = rec_result + tempRes;
                    if (rec_result.Length >= BUFFER_SIZE)
                    {
                        Console.WriteLine("no enough buffer for rec_result !\n");
                        break;
                    }
                }
                Thread.Sleep(100);//可省略
            }

            int errorcode = MSCDLL.QISRSessionEnd(MSCDLL.PtrToStr(session_id), hints);

            //语音识别结果
            if (rec_result.Length != 0)
            {   
                DataArrived(this, new DataArrivedEventArgs(rec_result));//订阅文本事件
                //返回错误代码10111时,可调用SpeechRecognition()函数执行MSPLogin
            }
        }
		/// <summary>
        /// 语音识别(通过文件的识别只能识别一分钟)
        /// </summary>
        /// <param name="audio_path"></param>
        /// <returns></returns>
        private StringBuilder RunIATFile(string audio_path)
        {
            byte[] audio_content;
            if (audio_path == null || audio_path == "")
            {
                Console.WriteLine("还没有选择语音文件");
                return null;
            }
            else
            {
                audio_content = File.ReadAllBytes(audio_path);
                player = new SoundPlayer(audio_path);
                player.Play();//播放语音
            }
            IntPtr session_id;
            StringBuilder result = new StringBuilder();//存储最终识别的结果
            string hints = "正常结束";
            AudioStatus aud_stat = AudioStatus.ISR_AUDIO_SAMPLE_CONTINUE;//用来告知MSC音频发送是否完成
            EpStatus ep_stat = EpStatus.ISR_EP_LOOKING_FOR_SPEECH;//端点检测器所处的状态
            RecogStatus rec_stat = RecogStatus.ISR_REC_STATUS_SUCCESS;//识别器返回的状态,提醒用户及时开始\停止获取识别结果
            RecogStatus rec_rslt = RecogStatus.ISR_REC_STATUS_SUCCESS;
            int errcode = (int)ErrorCode.MSP_SUCCESS;

            session_id = MSCDLL.QISRSessionBegin(null, QISRsession_begin_params, ref errcode);
            if (errcode != (int)ErrorCode.MSP_SUCCESS)
            {
                Console.WriteLine("开始一次语音识别失败!");
                MSCDLL.QISRSessionEnd(MSCDLL.PtrToStr(session_id), hints);
                return null;
            }

            #region 边读取文件边识别,效率高
            FileStream fp = new FileStream(audio_path, FileMode.Open)
            {
                Position = 44//wav文件要求
            };
            int len;
            int buff_num = 1024 * 20;

            byte[] buff = new byte[buff_num];
            IntPtr bp = Marshal.AllocHGlobal(buff_num);

            while (fp.Position != fp.Length)
            {
                if (stop_audio)
                {
                    break;
                }
                len = fp.Read(buff, 0, buff_num);
                Marshal.Copy(buff, 0, bp, buff.Length);
                errcode = MSCDLL.QISRAudioWrite(MSCDLL.PtrToStr(session_id), bp, (uint)len, aud_stat, ref ep_stat, ref rec_stat);
                if (errcode != (int)ErrorCode.MSP_SUCCESS)
                {
                    fp.Close();
                    Console.WriteLine("写入识别的音频失败!" + errcode);
                    return null;
                }

                if (rec_stat == RecogStatus.ISR_REC_STATUS_SUCCESS)
                {
                    IntPtr p = MSCDLL.QISRGetResult(MSCDLL.PtrToStr(session_id), ref rec_rslt, 0, ref errcode);
                    if (p != IntPtr.Zero)
                    {
                        string temp = MSCDLL.PtrToStr(p);
                        DataArrived(this, new DataArrivedEventArgs(temp));
                        result.Append(temp);
                        Console.WriteLine("部分结果:" + temp);
                    }
                }
                Thread.Sleep(200);
            }
            fp.Close();

            errcode = MSCDLL.QISRAudioWrite(MSCDLL.PtrToStr(session_id), bp, 1, AudioStatus.ISR_AUDIO_SAMPLE_LAST, ref ep_stat, ref rec_stat);
            if (errcode != (int)ErrorCode.MSP_SUCCESS)
            {
                Console.WriteLine("写入音频失败!" + errcode);
                return null;
            }
            Marshal.FreeHGlobal(bp);
            int loop_count = 0;
            do
            {
                IntPtr p = MSCDLL.QISRGetResult(MSCDLL.PtrToStr(session_id), ref rec_rslt, 0, ref errcode);
                if (p != IntPtr.Zero)
                {
                    string temp = MSCDLL.PtrToStr(p);
                    DataArrived(this, new DataArrivedEventArgs(temp));
                    result.Append(temp);
                    Console.WriteLine("最后一块音频:" + temp);
                }
                if (errcode != (int)ErrorCode.MSP_SUCCESS)
                {
                    Console.WriteLine("写入音频失败!" + errcode);
                    return null;
                }
                Thread.Sleep(500);
            } while (rec_rslt != RecogStatus.ISR_REC_STATUS_SPEECH_COMPLETE && loop_count++ < 30);
            #endregion
            int errorcode = MSCDLL.QISRSessionEnd(MSCDLL.PtrToStr(session_id), hints);
            Console.WriteLine("语音听写结束");
            Console.WriteLine("语音识别结果:");
            Console.WriteLine(result);
            
            return result;
        }

这里使用了两种识别方法,通过麦克风数据使用的识别是一次性读取数据后识别,而通过文件数据使用的是边读取数据边识别。由于说话的时间一般比较短,所以通过麦克风的设置为一次性读取(此处可以改进),而文件有时候过大,识别的时间过长,因此分段读取识别更好。注意,讯飞语音识别暂时只支持一分钟内的音频识别!

如果需要延长识别间隔,可以修改麦克风类的ENDS

麦克风识别效果

超过一分钟后就无法继续识别
通过文件进行语音识别

语音合成模块

语音合成类
语音合成类较为简单,主要涉及到了将得到的文本转化为语音,然后进行存储。这里加入了生成的语音的声音选择。这里生成的语音都为wav格式,主要是为了方便讯飞接口调用。选择文本文件均为txt,暂时没有进行异常处理,大伙如果需要可以自行实现。

通过文件数据合成语音

通过输入的文本合成语音

最终结果

最终封装为一个类库,方便使用

iFlyDoNet类

工程使用说明

然后我们的前期准备就完成了
最终工程包含四个项目
项目
其中iFlyDoNet为类库,TestMscDll为主要测试的项目,VoiceForm为windowsForm项目,VoiceWPF为WPF项目。

首先,你需要去讯飞官网下载SDK,因为他的APPID是和他SDK中msc.dll是配套的,如果不匹配则会报错。msc.dll文件在项目的bin目录下,其中讯飞的msc.dll由于是用dllimport引入的,所以需要放到debug或者release文件夹中。
msc..dll存放路径

然后下载NAudio.dll(提取码:i1m2),这是一个开源的音频库,里面封装了很多可以非常强大的接口,有兴趣的小伙伴可以研究一下它的源代码(github地址)。

使用说明
TestMscDll
首先iFlyDoNet类库生成iFlyDoNet.dll,TestMscDll中添加引用,包括NAudio.dll也要添加进去
将dll添加进去
然后窗口代码中using iFlyDoNet;即可以使用其中的封装的类。

VoiceForm和VoiceWPF
不需要添加iFlyDoNet.dll,其余步骤一样。

代码下载

最后,工程文件下载地址 (提取码:v4jv)

如果遇到什么问题或者有什么建议,都可以说出了,作为一个新手,你的回复就是对我最大的肯定。

最后,感谢这些博主的贡献

https://www.cnblogs.com/lingLuoChengMi/p/8397755.html
http://bbs.xfyun.cn/forum.php?mod=viewthread&tid=10182&extra=page%3D1

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐