vosk开源语音识别
vosk是开源的语音识别工具包。vosk支持的事情包括:
-
支持十九种语言 - 中文,英语,印度英语,德语,法语,西班牙语,葡萄牙语,俄语,土耳其语,越南语,意大利语,荷兰人,加泰罗尼亚语,阿拉伯, 希腊语, 波斯语, 菲律宾语,乌克兰语, 哈萨克语。
-
移动设备上脱机工作-raspberry pi,android,ios。
-
使用简单的 pip3 install vosk 安装。
-
每种语言的手提式模型只有是50mb, 但还有更大的服务器模型可用。
-
提供流媒体api,以提供最佳用户体验(与流行的语音识别python包不同)。
-
还有用于不同编程语言的包装器-java / csharp / javascript等。
-
可以快速重新配置词汇以实现最佳准确性。
-
支持说话人识别。
vosk-api
离线语音识别api,适用于android,ios,raspberry pi和具有python,java,c#等
有各语言的使用的示例
vosk-server
基于vosk和kaldi库的websocket,grpc和webrtc语音识别服务器
有各语言的使用的示例
vosk-api - java - springboot中的使用
导入依赖包
<!-- 语音识别 -->
<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>
<!-- 在windows上开发 开发机可实现压缩效果 window64位 -->
<dependency>
<groupid>ws.schild</groupid>
<artifactid>jave-nativebin-win32</artifactid>
<version>3.1.1</version>
</dependency>
<dependency>
<groupid>ws.schild</groupid>
<artifactid>jave-nativebin-win64</artifactid>
<version>3.1.1</version>
</dependency>
voskresult
public class voskresult {
private string text;
public string gettext() {
return text;
}
public void settext(string text) {
this.text = text;
}
}
vosk模型加载
package com.fjdci.vosk;
import org.vosk.libvosk;
import org.vosk.loglevel;
import org.vosk.model;
import java.io.ioexception;
/**
* vosk模型加载
* @author zhou
*/
public class voskmodel {
/**
* 3. 使用 volatile 保证线程安全
* 禁止指令重排
* 保证可见性
* 不保证原子性
*/
private static volatile voskmodel instance;
private model voskmodel;
public model getvoskmodel() {
return voskmodel;
}
/**
* 1.私有构造函数
*/
private voskmodel() {
system.out.println("singlelazypattern实例化了");
//string modelstr = "d:\\work\\project\\fjdci-vosk\\src\\main\\resources\\vosk-model-small-cn-0.22";
string modelstr = "d:\\work\\fjdci\\docker\\vosk\\vosk-model-cn-0.22";
try {
voskmodel = new model(modelstr);
libvosk.setloglevel(loglevel.info);
} catch (ioexception e) {
e.printstacktrace();
}
}
/**
* 2.通过静态方法获取一个唯一实例
* dcl 双重检查锁定 (double-checkedlocking)
* 在多线程情况下保持⾼性能
*/
public static voskmodel getinstance() {
if (instance == null) {
synchronized (voskmodel.class) {
if (instance == null) {
// 1. 分配内存空间 2、执行构造方法,初始化对象 3、把这个对象指向这个空间
instance = new voskmodel();
}
}
}
return instance;
}
/**
* 多线程测试加载
* @param args
*/
public static void main(string[] args) {
for (int i = 0; i < 5; i++) {
new thread(() -> {
voskmodel.getinstance();
}).start();
}
}
}
语言识别工具类
package com.fjdci.vosk;
import lombok.extern.slf4j.slf4j;
import org.springframework.stereotype.component;
import org.vosk.model;
import org.vosk.recognizer;
import ws.schild.jave.encoderexception;
import ws.schild.jave.multimediaobject;
import ws.schild.jave.info.audioinfo;
import ws.schild.jave.info.multimediainfo;
import java.io.file;
import java.io.fileinputstream;
import java.io.inputstream;
import java.util.arraylist;
import java.util.list;
import java.util.optional;
import java.util.uuid;
@slf4j
@component
public class voiceutil {
public static void main(string[] args) throws encoderexception {
string wavfilepath = "d:\\fjfile\\annex\\xwbl\\tem_2.wav";
// 秒
long cutduration = 20;
string waveform = acceptwaveform(wavfilepath, cutduration);
system.out.println(waveform);
}
/**
* 对wav格式音频文件进行语音识别翻译
*
* @param wavfilepath
* @param cutduration
* @return
* @throws encoderexception
*/
private static string acceptwaveform(string wavfilepath, long cutduration) throws encoderexception {
// 判断视频的长度
long starttime = system.currenttimemillis();
multimediaobject multimediaobject = new multimediaobject(new file(wavfilepath));
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;
}
// 进行切块处理
list<string> strings = cutwavfile(wavfilepath, cutduration, offset, fornum);
// 循环进行翻译
stringbuilder result = new stringbuilder();
for (string string : strings) {
file f = new file(string);
result.append(voiceutil.getrecognizerresult(f, channels));
}
long endtime = system.currenttimemillis();
string msg = "耗时:" + (endtime - starttime) + "ms";
system.out.println(msg);
return result.tostring();
}
/**
* 对wav进行切块处理
*
* @param wavfilepath 处理的wav文件路径
* @param cutduration 切割的固定长度/秒
* @param offset 设置起始偏移量(秒)
* @param fornum 切块的次数
* @return
* @throws encoderexception
*/
private static list<string> cutwavfile(string wavfilepath, long cutduration, long offset, long fornum) throws encoderexception {
uuid uuid = uuid.randomuuid();
// 大文件切割为固定时长的小文件
list<string> strings = new arraylist<>();
for (int i = 0; i < fornum; i++) {
string target = "d:\\fjfile\\annex\\xwbl\\" + 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);
}
return strings;
}
/**
* 进行翻译
*
* @param f
* @param channels
*/
public static string getrecognizerresult(file f, int channels) {
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()));
}
}
// 返回语音识别结果。和结果一样,但不要等待沉默。你通常在流的最后调用它来获得音频的最后部分。它刷新功能管道,以便处理所有剩余的音频块。
result.append(getresult(recognizer.getfinalresult()));
log.info("识别结果:{}", result.tostring());
} catch (exception e) {
e.printstacktrace();
}
return result.tostring();
}
/**
* 获取返回结果
*
* @param result
* @return
*/
private static string getresult(string result) {
voskresult voskresult = jacksonmapperutils.json2pojo(result, voskresult.class);
return optional.ofnullable(voskresult).map(voskresult::gettext).orelse("");
}
}
jave2 音频处理工具类
package com.fjdci.vosk;
import ws.schild.jave.encoder;
import ws.schild.jave.encoderexception;
import ws.schild.jave.inputformatexception;
import ws.schild.jave.multimediaobject;
import ws.schild.jave.encode.audioattributes;
import ws.schild.jave.encode.encodingattributes;
import ws.schild.jave.info.audioinfo;
import ws.schild.jave.info.multimediainfo;
import java.io.file;
public class jave2util {
/**
* @param src 来源文件路径
* @param target 目标文件路径
* @param offset 设置起始偏移量(秒)
* @param duration 设置切片的音频长度(秒)
* @throws encoderexception
*/
public static void cut(string src, string target, float offset, float duration) throws encoderexception {
file targetfile = new file(target);
if (targetfile.exists()) {
targetfile.delete();
}
file srcfile = new file(src);
multimediaobject srcmultiobj = new multimediaobject(srcfile);
multimediainfo srcmediainfo = srcmultiobj.getinfo();
encoder encoder = new encoder();
encodingattributes encodingattributes = new encodingattributes();
//设置起始偏移量(秒)
encodingattributes.setoffset(offset);
//设置切片的音频长度(秒)
encodingattributes.setduration(duration);
// 输入格式
encodingattributes.setinputformat("wav");
//设置音频属性
audioattributes audio = new audioattributes();
audio.setbitrate(srcmediainfo.getaudio().getbitrate());
//audio.setsamplingrate(srcmediainfo.getaudio().getsamplingrate());
// 转换为16khz 满足vosk识别的标准
audio.setsamplingrate(16000);
audio.setchannels(srcmediainfo.getaudio().getchannels());
//如果截取的时候,希望同步调整编码,可以设置不同的编码
// audio.setcodec("pcm_u8");
//audio.setcodec(srcmediainfo.getaudio().getdecoder().split(" ")[0]);
encodingattributes.setaudioattributes(audio);
//写文件
encoder.encode(srcmultiobj, new file(target), encodingattributes);
}
/**
* 转化音频格式
*
* @param oldformatpath : 原音乐路径
* @param newformatpath : 目标音乐路径
* @return
*/
public static boolean transformusicformat(string oldformatpath, string newformatpath) {
file source = new file(oldformatpath);
file target = new file(newformatpath);
// 音频转换格式类
encoder encoder = new encoder();
// 设置音频属性
audioattributes audio = new audioattributes();
audio.setcodec(null);
// 设置转码属性
encodingattributes attrs = new encodingattributes();
attrs.setinputformat("wav");
attrs.setaudioattributes(audio);
try {
encoder.encode(new multimediaobject(source), target, attrs);
system.out.println("传唤已完成...");
return true;
} catch (illegalargumentexception e) {
e.printstacktrace();
} catch (inputformatexception e) {
e.printstacktrace();
} catch (encoderexception e) {
e.printstacktrace();
}
return false;
}
public static void main(string[] args) throws encoderexception {
string src = "d:\\fjfile\\annex\\xwbl\\ly8603f22f24e0409fa9747d50a78ff7e5.wav";
string target = "d:\\fjfile\\annex\\xwbl\\tem_2.wav";
jave2util.cut(src, target, 0.0f, 60.0f);
string inputformatpath = "d:\\fjfile\\annex\\xwbl\\ly8603f22f24e0409fa9747d50a78ff7e5.m4a";
string outputformatpath = "d:\\fjfile\\annex\\xwbl\\ly8603f22f24e0409fa9747d50a78ff7e5.wav";
info(inputformatpath);
// audioencode(inputformatpath, outputformatpath);
}
/**
* 获取音频文件的编码信息
*
* @param filepath
* @throws encoderexception
*/
private static void info(string filepath) throws encoderexception {
file file = new file(filepath);
multimediaobject multimediaobject = new multimediaobject(file);
multimediainfo info = multimediaobject.getinfo();
// 时长
long duration = info.getduration();
string format = info.getformat();
// format:mov
system.out.println("format:" + format);
audioinfo audio = info.getaudio();
// 它设置将在重新编码的音频流中使用的音频通道数(1 =单声道,2 =立体声)。如果未设置任何通道值,则编码器将选择默认值。
int channels = audio.getchannels();
// 它为新的重新编码的音频流设置比特率值。如果未设置比特率值,则编码器将选择默认值。
// 该值应以每秒位数表示。例如,如果您想要128 kb / s的比特率,则应调用setbitrate(new integer(128000))。
int bitrate = audio.getbitrate();
// 它为新的重新编码的音频流设置采样率。如果未设置采样率值,则编码器将选择默认值。该值应以赫兹表示。例如,如果您想要类似cd
// 采样率、音频采样级别 16000 = 16khz
int samplingrate = audio.getsamplingrate();
// 设置音频音量
// 可以调用此方法来更改音频流的音量。值为256表示音量不变。因此,小于256的值表示音量减小,而大于256的值将增大音频流的音量。
// setvolume(integer volume)
string decoder = audio.getdecoder();
system.out.println("声音时长:毫秒" + duration);
system.out.println("声道:" + channels);
system.out.println("bitrate:" + bitrate);
system.out.println("samplingrate 采样率、音频采样级别 16000 = 16khz:" + samplingrate);
// aac (lc) (mp4a / 0x6134706d)
system.out.println("decoder:" + decoder);
}
/**
* 音频格式转换
* @param inputformatpath
* @param outputformatpath
* @return
*/
public static boolean audioencode(string inputformatpath, string outputformatpath) {
string outputformat = getsuffix(outputformatpath);
string inputformat = getsuffix(inputformatpath);
file source = new file(inputformatpath);
file target = new file(outputformatpath);
try {
multimediaobject multimediaobject = new multimediaobject(source);
// 获取音频文件的编码信息
multimediainfo info = multimediaobject.getinfo();
audioinfo audioinfo = info.getaudio();
//设置音频属性
audioattributes audio = new audioattributes();
audio.setbitrate(audioinfo.getbitrate());
audio.setsamplingrate(audioinfo.getsamplingrate());
audio.setchannels(audioinfo.getchannels());
// 设置转码属性
encodingattributes attrs = new encodingattributes();
attrs.setinputformat(inputformat);
attrs.setoutputformat(outputformat);
attrs.setaudioattributes(audio);
// 音频转换格式类
encoder encoder = new encoder();
// 进行转换
encoder.encode(new multimediaobject(source), target, attrs);
return true;
} catch (illegalargumentexception | encoderexception e) {
e.printstacktrace();
}
return false;
}
/**
* 获取文件路径的.后缀
* @param outputformatpath
* @return
*/
private static string getsuffix(string outputformatpath) {
return outputformatpath.substring(outputformatpath.lastindexof(".") + 1);
}
}
import com.fasterxml.jackson.annotation.jsoninclude;
import com.fasterxml.jackson.core.jsonprocessingexception;
import com.fasterxml.jackson.core.type.typereference;
import com.fasterxml.jackson.databind.deserializationfeature;
import com.fasterxml.jackson.databind.javatype;
import com.fasterxml.jackson.databind.objectmapper;
import java.util.arraylist;
import java.util.hashmap;
import java.util.list;
import java.util.map;
public class jacksonmapperutils {
private final static objectmapper objectmapper = new objectmapper();
public static objectmapper getinstance() {
return objectmapper;
}
/**
* 转换为 json 字符串
*
* @param obj
* @return
* @throws exception
*/
public static string obj2json(object obj) throws exception {
objectmapper mapper = new objectmapper();
mapper.setserializationinclusion(jsoninclude.include.always);
return mapper.writevalueasstring(obj);
}
/**
* 转换为 json 字符串,忽略空值
*
* @param obj
* @return
* @throws exception
*/
public static string obj2jsonignorenull(object obj) throws exception {
objectmapper mapper = new objectmapper();
mapper.setserializationinclusion(jsoninclude.include.non_null);
return mapper.writevalueasstring(obj);
}
/**
* 转换为 javabean
*
* @param jsonstring
* @param clazz
* @return
* @throws exception
*/
public static <t> t json2pojo(string jsonstring, class<t> clazz){
try {
objectmapper.configure(deserializationfeature.accept_single_value_as_array, true);
return objectmapper.readvalue(jsonstring, clazz);
} catch (jsonprocessingexception e) {
e.printstacktrace();
}
return null;
}
/**
* 字符串转换为 map<string, object>
*
* @param jsonstring
* @return
* @throws exception
*/
public static <t> map<string, object> json2map(string jsonstring) throws exception {
objectmapper mapper = new objectmapper();
mapper.setserializationinclusion(jsoninclude.include.non_null);
return mapper.readvalue(jsonstring, map.class);
}
/**
* 字符串转换为 map<string, t>
*/
public static <t> map<string, t> json2map(string jsonstring, class<t> clazz) throws exception {
map<string, map<string, object>> map = (map<string, map<string, object>>) objectmapper.readvalue(jsonstring, new typereference<map<string, t>>() {
});
map<string, t> result = new hashmap<string, t>();
for (map.entry<string, map<string, object>> entry : map.entryset()) {
result.put(entry.getkey(), map2pojo(entry.getvalue(), clazz));
}
return result;
}
/**
* 深度转换 json 成 map
*
* @param json
* @return
*/
public static map<string, object> json2mapdeeply(string json) throws exception {
return json2maprecursion(json, objectmapper);
}
/**
* 把 json 解析成 list,如果 list 内部的元素存在 jsonstring,继续解析
*
* @param json
* @param mapper 解析工具
* @return
* @throws exception
*/
private static list<object> json2listrecursion(string json, objectmapper mapper) throws exception {
if (json == null) {
return null;
}
list<object> list = mapper.readvalue(json, list.class);
for (object obj : list) {
if (obj != null && obj instanceof string) {
string str = (string) obj;
if (str.startswith("[")) {
obj = json2listrecursion(str, mapper);
} else if (obj.tostring().startswith("{")) {
obj = json2maprecursion(str, mapper);
}
}
}
return list;
}
/**
* 把 json 解析成 map,如果 map 内部的 value 存在 jsonstring,继续解析
*
* @param json
* @param mapper
* @return
* @throws exception
*/
private static map<string, object> json2maprecursion(string json, objectmapper mapper) throws exception {
if (json == null) {
return null;
}
map<string, object> map = mapper.readvalue(json, map.class);
for (map.entry<string, object> entry : map.entryset()) {
object obj = entry.getvalue();
if (obj != null && obj instanceof string) {
string str = ((string) obj);
if (str.startswith("[")) {
list<?> list = json2listrecursion(str, mapper);
map.put(entry.getkey(), list);
} else if (str.startswith("{")) {
map<string, object> maprecursion = json2maprecursion(str, mapper);
map.put(entry.getkey(), maprecursion);
}
}
}
return map;
}
/**
* 将 json 数组转换为集合
*
* @param jsonarraystr
* @param clazz
* @return
* @throws exception
*/
public static <t> list<t> json2list(string jsonarraystr, class<t> clazz) throws exception {
javatype javatype = getcollectiontype(arraylist.class, clazz);
list<t> list = (list<t>) objectmapper.readvalue(jsonarraystr, javatype);
return list;
}
/**
* 获取泛型的 collection type
*
* @param collectionclass 泛型的collection
* @param elementclasses 元素类
* @return javatype java类型
* @since 1.0
*/
public static javatype getcollectiontype(class<?> collectionclass, class<?>... elementclasses) {
return objectmapper.gettypefactory().constructparametrictype(collectionclass, elementclasses);
}
/**
* 将 map 转换为 javabean
*
* @param map
* @param clazz
* @return
*/
public static <t> t map2pojo(map map, class<t> clazz) {
return objectmapper.convertvalue(map, clazz);
}
/**
* 将 map 转换为 json
*
* @param map
* @return
*/
public static string maptojson(map map) {
try {
return objectmapper.writevalueasstring(map);
} catch (exception e) {
e.printstacktrace();
}
return "";
}
/**
* 将 json 对象转换为 javabean
*
* @param obj
* @param clazz
* @return
*/
public static <t> t obj2pojo(object obj, class<t> clazz) {
return objectmapper.convertvalue(obj, clazz);
}
}
语音模型下载地址 翻墙
https://alphacephei.com/vosk/models
链接: 模型下载地址
踩坑记录
java.io.ioexception: failed to create a model
at org.vosk.model.<init>(model.java:14)
java.lang.error: invalid memory access
at org.vosk.libvosk.vosk_recognizer_new(native method)
at org.vosk.recognizer.<init>(recognizer.java:19)
链接: exception: failed to create a model 原因
vosk模型加载需要服务器有足够的内存
项目启动未加载模型时 使用了500m
项目加载模型时 使用了4g多内存
参考链接
链接:
链接: 几款免费的语音转文字工具推荐
链接: java 离线中文语音文字识别
链接: asr - python使用vosk进行中文语音识别
链接: nemo非常强大,覆盖了asr, nlp, tts,提供了预训练模型及完整的训练模块。其商业版本为riva。
链接: asrt语音识别文档
asrt是一个基于深度学习的语音识别工具,可以用于开发最先进的语音识别系统,是由ai柠檬博主(西安电子科技大学 · 西安市大数据与视觉智能重点实验室)从2016年起做的开源语音识别项目,基线为85%识别准确率,在某些条件下可做到95%左右的识别准确率。asrt包含了语音识别算法服务端(用于训练或部署api服务)和多种平台及编程语言的客户端sdk,支持一句话识别和实时流式识别,相关的代码已经开源在github和gitee上。
asrt语音识别系统的api已经为ai柠檬站内搜索引擎提供了语音识别服务,用于该站语音搜索功能的实现。
搭建一个离线的语音识别系统 并提供webapi访问
一些方向和思路:
- 确定语音识别引擎
首先,需要选择一个适合的语音识别引擎。常见的一些引擎有cmu sphinx、kaldi、百度语音、讯飞开放平台等等。选定引擎后,需要对其进行配置和训练,使其能够适应自己的应用场景。
- 搭建离线语音识别系统
接下来,需要进行搭建离线语音识别系统的工作。可以通过使用ubuntu等linux系统进行安装和配置。在系统中需要安装上一步中选择的语音识别引擎和相关依赖包。
- 提供web api访问
为了使得离线语音识别系统能够方便地被访问和使用,需要提供相应的web api。您可以使用flask等框架搭建web服务,并在其上下文中调用语音识别引擎进行语音识别工作。
最后,为了保证语音识别的精度和流畅度,还需要进行一系列优化和调试工作,例如声音降噪、语速控制、模型调优等等。希望以上方向可以帮助到您。
2 whisper
whisper 是一种自动语音识别模型,基于从网络上收集的 680,000 小时多语言数据进行训练。
whisper是一个语音识别引擎,可以用于开发语音控制应用程序,但它通常用于移动设备和嵌入式设备上,以提供离线语音识别的功能。如果您想使用java搭建离线语音识别,您可以尝试使用其他语音识别引擎,如cmu sphinx和kaldi。 这些引擎都支持离线语音识别,并提供java api供开发人员使用。
3 kaldi
开源中文语音识别项目介绍:asrframe
https://blog.csdn.net/sailist/article/details/95751825
腾讯ai lab开源轻量级语音处理工具包pika
专注e2e语音识别,腾讯ai lab开源轻量级语音处理工具包pika-csdn社区
有什么开源的python汉语语音转文字项目?
https://blog.csdn.net/devid008/article/details/129656356
离线语音识别第三方服务提供商
1 科大讯飞
https://www.xfyun.cn/service/offline_iat
科大讯飞离线包仅基于安卓,也不支持java离线版
还像可以调用本地dll 实现离线语音
2 百度语音识别
https://ai.baidu.com/tech/speech/realtime_asr
不支持离线
3 阿里云语音识别
https://ai.aliyun.com/nls/trans
发表评论