前言:上一篇文章写了关于如何录音,这篇咱们做个升级,让录音内容转为文本。废话不多说,直接上手,以下正文:
1、新建一个控制台项目,引用CommandLineParser和Whisper.net
2、新建Options实体类,用于提供使用模型的一些参数
代码语言:javascript复制
代码语言:javascript复制public class Options
{
/// <summary>
/// 指令类型
/// </summary>
[Option('t', "command", Required = false, HelpText = "运行指令 (lang-detect, transcribe 或 translate)", Default = "transcribe")]
public string Command { get; set; } = "transcribe";
/// <summary>
/// 音频文件,默认要存放bin目录下
/// </summary>
[Option('f', "file", Required = false, HelpText = "需要处理的音频文件", Default = "test.mp3")]
public string FileName { get; set; }
/// <summary>
/// 语言,默认自动选择
/// </summary>
[Option('l', "lang", Required = false, HelpText = "语言", Default = "auto")]
public string Language { get; set; } = "chinese";
/// <summary>
/// 模型文件,默认也存在在bin同级目录下
/// </summary>
[Option('m', "modelFile", Required = false, HelpText = "使用的模型的文件名", Default = "ggml-base.bin")]
public string ModelName { get; set; } = "ggml-base.bin";
/// <summary>
/// 模型类型
/// </summary>
[Option('g', "ggml", Required = false, HelpText = "根据模型类型,验证是否存在模型文件,不存在则下载", Default = GgmlType.Base)]
public GgmlType ModelType { get; set; }= GgmlType.Base;
}
代码语言:javascript复制
3、ggml模型类别枚举参考:
代码语言:javascript复制public enum GgmlType
{
Tiny,
TinyEn,
Base,
BaseEn,
Small,
SmallEn,
Medium,
MediumEn,
LargeV1,
Large
}
代码语言:javascript复制
它们会对应在线的模型,会根据需要自动下载。在线模型文件参考:
模型地址:
https://huggingface.co/sandrohanea/whisper.net/tree/main/classic
4、验证模型文件不存在,则下载
代码语言:javascript复制if (!File.Exists(opt.ModelName))
{
Console.WriteLine($"准备下载模型:{opt.ModelName}");
using (var modelStream = await WhisperGgmlDownloader.GetGgmlModelAsync(opt.ModelType))
{
using (var fileWriter = File.OpenWrite(opt.ModelName))
{
await modelStream.CopyToAsync(fileWriter);
}
}
}
代码语言:javascript复制
上面代码下载没进度提示,稍微改造下:
代码语言:javascript复制var bytesRead = 0;
var buffer = new byte[8192]; // 8KB 的缓冲区
var lastUpdateTime = DateTime.Now; // 记录上次更新的时间
int bytesJustRead;
while ((bytesJustRead = await modelStream.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
await fileWriter.WriteAsync(buffer, 0, bytesJustRead);
bytesRead = bytesJustRead;
// 检查是否已经过去了1秒
if ((DateTime.Now - lastUpdateTime).TotalSeconds >= 1)
{
// 将已下载的字节数转换为MB
double downloadedMB = bytesRead / 1024.0 / 1024.0;
// 将光标移回到上一行的开始
Console.SetCursorPosition(0, Console.CursorTop - 1);
// 使用 r 来覆盖当前行并显示已下载的MB
Console.WriteLine($"已下载: {downloadedMB:0.00} MB ({downloadedMB / 1024.0} GB)r");
// 更新上次更新的时间
lastUpdateTime = DateTime.Now;
}
}
代码语言:javascript复制
5、新建音频转文本的处理函数,具体内容看代码和有关注释:
代码语言:javascript复制static async Task Audio2Text(Options opt)
{
// 从给定的模型名称路径创建一个 WhisperFactory 实例
using var factory = WhisperFactory.FromPath(opt.ModelName);
var builder = factory.CreateBuilder()
.WithLanguage(opt.Language);
// 如果值为 "translate",则在构建器中启用翻译功能
if (opt.Command == "translate")
{
builder.WithTranslate();
}
using var processor = builder.Build();
// 打开一个文件流来读取由 opt.FileName 指定的音频文件
using var fileStream = File.OpenRead(opt.FileName);
// 使用处理器异步处理音频文件。对于处理器返回的每个段(segment),它将段的开始时间、结束时间和文本打印到控制台。
await foreach (var segment in processor.ProcessAsync(fileStream, CancellationToken.None))
{
Console.WriteLine($"{segment.Start} ==> {segment.End} : {segment.Text}");
}
}
代码语言:javascript复制
6、创建一个检测语言的方法:
代码语言:javascript复制void LanguageIdentification(Options opt)
{
// 使用 File.ReadAllBytes 方法将模型文件读取到内存中。
var bufferedModel = File.ReadAllBytes(opt.ModelName);
// 多个任务可以使用同一个工厂来创建处理器
using var factory = WhisperFactory.FromBuffer(bufferedModel);
// 使用工厂创建一个新的构建器,并设置其语言
var builder = factory.CreateBuilder()
.WithLanguage(opt.Language);
using var processor = builder.Build();
// 打开一个文件流来读取由 opt.FileName 指定的音频文件
using var fileStream = File.OpenRead(opt.FileName);
// 使用 WaveParser 类来解析音频文件
var wave = new WaveParser(fileStream);
// 使用 WaveParser 的 GetAvgSamples 方法获取音频文件的平均样本
var samples = wave.GetAvgSamples();
// 使用处理器的 DetectLanguage 方法检测音频样本中的语言
var language = processor.DetectLanguage(samples, speedUp: true);
Console.WriteLine("Language is " language);
}
代码语言:javascript复制
processor.DetectLanguage(samples, speedUp: true) 的speedUp说明:
当speedUp为true时,使用whisper_pcm_to_mel_phase_vocoder_with_state方法进行处理。这个方法可能使用了phase vocoder技术,这是一种在不改变音高的情况下改变音频速度的技术。
当speedUp为false时,使用whisper_pcm_to_mel_with_state方法进行处理。这个方法可能是一个更传统或基本的方法来从PCM数据转换到Mel频谱。
7、根据指令,执行内容
代码语言:javascript复制switch (opt.Command)
{
case "lang-detect":
LanguageIdentification(opt);
break;
case "transcribe":
case "translate":
await Audio2Text(opt);
break;
default:
Console.WriteLine("未知指令类型");
break;
}
代码语言:javascript复制
8、制作一个录音文件备用。录音教程,可以参考之前文章:
https://mp.weixin.qq.com/s/vAZUyuT5F_S5Oee-BkV_dw
备注:录制音频需要使用16KHZ,可在链接文章內的录音部分代码中,新增以下参数:
waveIn.WaveFormat = new WaveFormat(16000,16,1);
如果需要录制系统音频,可以使用:
using var waveIn = new WasapiLoopbackCapture();
我录制了个系统音频,网络上随便找了一个频道进行播放其中一小段,然后录制成音频:
https://www.ximalaya.com/sound/565311273
把录音文件放到根目录下:
先用本地播放器播放下,看下长度和是否能正常播放先:
可以播放,说明文件是正常的,开始运行音频转文本程序:
可以看到音频内容被输出了。
如果以上内容对你有帮助,欢迎一键三连,点赞、关注、转发,不定时更新技术文章。