C#AI系列(5): 从零开始 C# 轻松语音识别
人工智能历经多年演进,昔日高门槛的图像与语音识别任务,如今已有成熟的开源框架可供免费使用,只要花点时间,就可以零成本部署。本文以语音识别为例,看如何高效的将语音识别功能集成至C#系统中,后续大家可以继续完善扩展,去处理如语音指令、语音交互、字幕生成、会议纪要分析、语音翻译等相关任务。

本文项目在笔记本电脑上用cpu就可以自己动手轻松实现,所有代码均已开源,仅需关注 萤火初芒 公众号回复AISharp即可查看仓库地址,供学习交流使用,无套路。
一、环境配置基础
语音识别的方案有很多,windows系统本身也自带有语音识别的方案(System.Speech.Recognition),但是效果查强人意。既要简单好用,又要功能强大效果好,我们选择基于Whisper(MIT,https://github.com/openai/whisper)的Whisper.Net(MIT,https://github.com/sandrohanea/whisper.net)来实现。

Whisper 是2022年OpenAI发布的一个通用语音识别模型。它基于大量多样化的音频数据进行训练,同时也是一个多任务模型,能够执行多语言语音识别、语音翻译和语言识别任务。项目创建完成后直接在nuget拉取Whisper.net(1.9.0)和Whisper.net.Runtime(1.9.0)即可。
另外为了实现语音交互,还要在nuget拉取NAudio(2.2.1)(MIT,https://github.com/naudio/NAudio),以实现通过麦克风设备对声音的捕获。
二、核心代码实现
2.1 四行核心代码实现语音转文本
上面的控制台demo的main函数代码如下:
private static readonly string OutFile = "d:\\record.wav"; //临时输出文件// 读取json配置文件,nuget拉取LumConfig,往期文章有介绍private static LumConfigManager con = new LumConfigManager("model.conf");private static void Main()
{// 1. 加载模型// 输入模型地址,model文件夹下面提供了一个tiny版模型可以直接用var wm = new WhisperManager((string)con.Get("modelPath")); while (true){bool started = false;Console.WriteLine("按 Space 开始/停止录音");while (true){var key = Console.ReadKey(true).Key;if (key == ConsoleKey.Spacebar){if (started){// 2. 开始录音StopRec();break;}else{// 3. 结束录音StartRec();}started=!started;}}// 4. 转文本 var res = wm.RunAsync(OutFile).GetAwaiter().GetResult();Console.WriteLine(res); }
}
代码通过循环控制来实现反复读取语音再转换为文本的动作。其实核心代码只有四行,分别是加载模型、开始录音、停止录音、语音转文本。后面我们将对其一一拆解。
2.2 麦克风录制声音
麦克风的录制很简单,我们只需要完成2个动作,一个是开始录制声音,另一个是停止录制声音。WaveFileWriter方法两个重载,可以选择把声音录制到文件,也可以录制到流(stream)中。以录制到文件为例具体代码如下:
private static void StartRec()
{ waveIn = new WaveInEvent{DeviceNumber = 0, // 默认麦克风WaveFormat = new WaveFormat(16000, 1), // 44.1kHz 单声道BufferMilliseconds = 100};writer = new WaveFileWriter(OutFile, waveIn.WaveFormat);// 数据到达事件waveIn.DataAvailable += (s, e) =>{writer.Write(e.Buffer, 0, e.BytesRecorded);};// 录音停止事件(负责释放 writer)waveIn.RecordingStopped += (s, e) =>{writer?.Dispose();writer = null;waveIn?.Dispose();waveIn = null;stop.Set();};waveIn.StartRecording();Console.WriteLine(">>> 正在录音……");
}private static void StopRec()
{Console.WriteLine("<<< 停止录音");waveIn?.StopRecording(); // 触发 RecordingStopped 事件// 停止录制时,流数据的处理不一定已经完成// 我们额外使用一个信号量来实现简单的同步stop.WaitOne();
}
2.3 语音转文字的实现
尽管Whisper.Net已经封装好了所有相关功能,但是我们最好额外写一个WhisperManager类来高效的反复使用他。具体代码如下:
internal class WhisperManager:IDisposable
{WhisperFactory whisperFactory;WhisperProcessor processor;public WhisperManager(string path){whisperFactory = WhisperFactory.FromPath(path);processor = whisperFactory.CreateBuilder().WithLanguage("zh").WithPrompt("以下是,简体中文普通话的句子。") // 否则大概率会输出繁体中文.WithThreads(16).Build(); }public async Task<string> RunAsync(string path){try{var sb = new StringBuilder();using var fileStream = File.OpenRead(path);// 异步获取识别后的分段结果await foreach (var seg in processor.ProcessAsync(fileStream)){var str = $"{seg.Start}->{seg.End}: {seg.Text}\r\n";sb.AppendLine(str);var per = (fileStream.Position / fileStream.Length*100).ToString("N2");}return sb.ToString();}catch (Exception ex){return ex.Message;}}public void Dispose(){processor.Dispose();whisperFactory.Dispose();}
}
Whisper重要基础参数配置:
WithLanguage,指定输入语音的语言,如zh、en、ja、auto(自动);
WithPrompt,输出提示词,这里与大语言模型不同,只能说引导模型生成,比如“以下是一个访谈。”。此处需要与指定语言一致,否则可能会强行切换,50~200 个字符足够,最好出现"标点符号"(不然可能会无标点)、"口语词、专有名词"(适配语境)。
另外还有很多与线程、输出相关的控制,大伙可根据需要自行研究了解。
2.4 模型、输入与输出
c#使用的Whisper模型是.bin的二进制格式的,现在可以在https://huggingface.co/ggerganov/whisper.cpp/tree/main下载。本项目里用到的是好久以前下载的ggml-model-whisper-tiny.bin,74mb,速度很快但效果中等。
本项目对Whisper的输入用的是wav格式的文件,且采样率必须为16kHz,否则会报错。如果导入音频时采样频率不对,可以用NAudio通过下代码进行转换:
public void Execute()
{//using (Mp3FileReader reader = new Mp3FileReader("D:\\Documents\\Temp\\WhisperDesktop\\上午对接结构录音.mp3"))using (AudioFileReader reader = new AudioFileReader("D:\\Documents\\Temp\\WhisperDesktop\\录音.m4a")){// 16kHz, 16bit,单声道var newFormat = new WaveFormat(16000, 16, 1); using (var conversionStream = new WaveFormatConversionStream(newFormat, reader)){WaveFileWriter.CreateWaveFile("D:\\Documents\\Temp\\WhisperDesktop\\录音.wav", conversionStream);} }
}
Whisper输出是分多个段(IAsyncEnumerable
public class SegmentData
{public string Text { get; } 文本public TimeSpan Start { get; }public TimeSpan End { get; }public float MinProbability { get; }public float MaxProbability { get; }public float Probability { get; }public string Language { get; }public WhisperToken[] Tokens { get; }public float NoSpeechProbability { get; }
}
打印的结果示意:
00:00:00->00:00:15: 一二三四五,上山打老虎,老虎没打到,打到了小松鼠。
三、最后
- 在使用tiny模型下,整体速度和质量还是基本可以的,如果追求更好的效果则考虑使用更大模型,但与之对内存的需求及运算时间会相应大幅增加。
- 在转换时,语音越长,转换速度越慢,因此建议在转换前,主动对长语音进行分段。尽管这样可能导致上下文丢失造成一定程度的不精确,但却能显著提高转换速度。
- 语音识别是支持多语言混合的。但翻译的功能,试了下,失灵时不灵,可能和模型本身及大小有关。
感谢您的阅读,本案例及更加完整丰富的机器学习模型案例的代码已全部开源,新朋友们可以关注公众号回复AISharp查看仓库地址,本期相关代码在仓库下面的Voic文件夹里,在model文件夹内可以找到案例使用的74mb大小的ggml-model-whisper-tiny.bin小模型,大家可以自行尝试。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/990679.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!