目前许多数字化项目都在向智能化转型,许多企业都想在AI方面方向上靠拢。但是各大云厂商的AI api成本下不来,尤其是一些项目的特殊环境必须私有化,导致工作开展不下去。所以我们需要一些开源的工具将成本打下去。本篇文章主要是介绍语音识别ASR的一个强劲的开源利器-VOSK。
Vosk是言语识别工具包。Vosk的优势有:
- 支持二十+种语言 – 中文,英语,印度英语,德语,法语,西班牙语,葡萄牙语,俄语,土耳其语,越南语,意大利语,荷兰人,加泰罗尼亚语,阿拉伯, 希腊语, 波斯语, 菲律宾语,乌克兰语, 哈萨克语, 瑞典语, 日语, 世界语, 印地语, 捷克语, 波兰语, 乌兹别克语, 韩国语, 塔吉克语
- 移动设备上脱机工作-Raspberry Pi,Android,iOS
- 使用简单的 pip3 install vosk 安装
- 每种语言的手提式模型只有是50Mb, 但还有更大的服务器模型可用
- 提供流媒体API,以提供最佳用户体验(与流行的语音识别python包不同)
- 还有用于不同编程语言的包装器-java / csharp / javascript等
- 可以快速重新配置词汇以实现最佳准确性
- 支持说话人识别
亲测了一下,适用于移动设备的小模型识别速度超级快,识别率稍微低点,但是也能用。而标准模型的识别准确率还是比较高的,能识别绝大部分文字。
VOSK由C++编写的,但是提供了多种语言的api,其中包括Java。下面我们看一下java怎么来接入这个工具:
一 引入pom
<!-- 语音识别 -->
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>5.13.0</version>
</dependency>
<dependency>
<groupId>com.alphacephei</groupId>
<artifactId>vosk</artifactId>
<version>0.3.45</version>
</dependency>
<!-- JAVE2(Java音频视频编码器)库是ffmpeg项目上的Java包装器。 -->
<dependency>
<groupId>ws.schild</groupId>
<artifactId>jave-core</artifactId>
<version>3.1.1</version>
</dependency>
这里注意我们用到了一个jave,这个是一个音频的解码工具,我们用来做识别前的前置音频处理列如格式转换,切分。底层是调用ffmpeg。所以在使用他之前,我们需要安装ffmpeg。
Download FFmpeg

需要注意的是,windows下我们要配置环境变量。
然后,我们就可以写代码了。核心代码如下:
1 加载模型,我们在VOSK的官网上把模型down下来。VOSK Models

这里我们选择标准的中文模型:vosk-model-cn-0.22。
private VoskModel() {
String modelStr = "D:\workspace\study\study-backend\vosk-model-cn-0.22";
try {
voskModel = new Model(modelStr);
LibVosk.setLogLevel(LogLevel.INFO);
} catch (IOException e) {
e.printStackTrace();
}
}
接着,我们将拿到的音频进行一下切分转码等处理。当然,如果你的音频不长,不切分也是可以的。
MultimediaObject multimediaObject = new MultimediaObject(new File(wavFilePath),processLocator);
MultimediaInfo info = multimediaObject.getInfo();
// 时长/毫秒
long duration = info.getDuration();
AudioInfo audio = info.getAudio();
// 通道数
int channels = audio.getChannels();
// 秒
long offset = 0;
long forNum = (duration / 1000) / cutDuration;
if (duration % (cutDuration * 1000) > 0) {
forNum = forNum + 1;
}
UUID uuid = UUID.randomUUID();
// 大文件切割为固定时长的小文件
List<String> strings = new ArrayList<>();
for (int i = 0; i < forNum; i++) {
String target = "D:\vosktemp\" + uuid + "\" + i + ".wav";
Float offsetF = Float.valueOf(String.valueOf(offset));
Float cutDurationF = Float.valueOf(String.valueOf(cutDuration));
Jave2Util.cut(wavFilePath, target, offsetF, cutDurationF);
offset = offset + cutDuration;
strings.add(target);
}
然后我们将切分的音频,逐一识别:
StringBuilder result = new StringBuilder();
Model voskModel = VoskModel.getInstance().getVoskModel();
// 采样率为音频采样率的声道倍数
log.info("====加载完成,开始分析====");
try (
Recognizer recognizer = new Recognizer(voskModel, 16000 * channels);
InputStream ais = new FileInputStream(f)
) {
int nbytes;
byte[] b = new byte[4096];
while ((nbytes = ais.read(b)) >= 0) {
if (recognizer.acceptWaveForm(b, nbytes)) {
// 返回语音识别结果
result.append(getResult(recognizer.getResult()));
log.info("识别结果:{}", recognizer.getResult());
} else {
// 返回语音识别结果
// log.info("识别结果:{}", recognizer.getPartialResult());
}
}
// 返回语音识别结果。你一般在流的最后调用它来获得音频的最后部分。它刷新功能管道,以便处理所有剩余的音频块。
result.append(getResult(recognizer.getFinalResult()));
log.info("识别结果:{}", result.toString());
} catch (Exception e) {
e.printStackTrace();
}
return result.toString();
最后将所有识别内容汇总,就得到了最终的识别结果。
效果是这样的:

基本上是可以的。但是这个还需要进一步的处理,由于VOSK识别出来的文字是分词输出的,而且没有标点。所以,我们需要进一步的处理。
- 去空格
- 将文字给大模型,写一个prompt
ChatResponse chatResponse =chatClient.prompt().system("你是一个语言专家,请将以下对话内容添加标点符号。并且分析说话人。按说话人:内容。输出一个json列表。")
.user(question)
.call().chatResponse();
String response = Optional.ofNullable(chatResponse)
.map(ChatResponse::getResult)
.map(Generation::getOutput)
.map(AbstractMessage::getText)
.orElse("");
return response;
生成的结果就基本符合我们的要求了。结果如下:

© 版权声明
文章版权归作者所有,未经允许请勿转载。
相关文章
您必须登录才能参与评论!
立即登录



很有帮助
收藏了,感谢分享
感谢转发