Vosk是开源的语音识别工具包。Vosk支持的事情包括:
支持十九种语言 - 中文,英语,印度英语,德语,法语,西班牙语,葡萄牙语,俄语,土耳其语,越南语,意大利语,荷兰人,加泰罗尼亚语,阿拉伯, 希腊语, 波斯语, 菲律宾语,乌克兰语, 哈萨克语。
移动设备上脱机工作-Raspberry Pi,Android,iOS。
使用简单的 pip3 install vosk 安装。
每种语言的手提式模型只有是50Mb, 但还有更大的服务器模型可用。
提供流媒体API,以提供最佳用户体验(与流行的语音识别python包不同)。
还有用于不同编程语言的包装器-java / csharp / javascript等。
可以快速重新配置词汇以实现最佳准确性。
支持说话人识别。
离线语音识别API,适用于Android,iOS,Raspberry Pi和具有Python,Java,C#等
链接: vosk-api github地址
有各语言的使用的示例
基于Vosk和Kaldi库的WebSocket,gRPC和WebRTC语音识别服务器
链接: vosk-server github地址
有各语言的使用的示例
net.java.dev.jna jna 5.13.0 com.alphacephei vosk 0.3.45 ws.schild jave-core 3.1.1 ws.schild jave-nativebin-win32 3.1.1 ws.schild jave-nativebin-win64 3.1.1
public class VoskResult { private String text; public String getText() { return text; } public void setText(String text) { this.text = text; } }
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; } // 进行切块处理 Liststrings = 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 cutWavFile(String wavFilePath, long cutDuration, long offset, long forNum) throws EncoderException { UUID uuid = UUID.randomUUID(); // 大文件切割为固定时长的小文件 List 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(""); } }
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); } }
https://alphacephei.com/vosk/models
链接: 模型下载地址
java.io.IOException: Failed to create a model at org.vosk.Model.(Model.java:14)
java.lang.Error: Invalid memory access at org.vosk.LibVosk.vosk_recognizer_new(Native Method) at org.vosk.Recognizer.(Recognizer.java:19)
链接: Exception: Failed to create a model 原因
项目启动未加载模型时 使用了500M
项目加载模型时 使用了4G多内存
链接: vosk开源语音识别
链接: 基于Whisper的音频转录服务汇总
链接: 几款免费的语音转文字工具推荐
链接: java 离线中文语音文字识别
链接: Asr - python使用vosk进行中文语音识别
链接: NeMo非常强大,覆盖了ASR, NLP, TTS,提供了预训练模型及完整的训练模块。其商业版本为RIVA。
链接: ASRT语音识别文档
ASRT是一个基于深度学习的语音识别工具,可以用于开发最先进的语音识别系统,是由AI柠檬博主(西安电子科技大学 · 西安市大数据与视觉智能重点实验室)从2016年起做的开源语音识别项目,基线为85%识别准确率,在某些条件下可做到95%左右的识别准确率。ASRT包含了语音识别算法服务端(用于训练或部署API服务)和多种平台及编程语言的客户端SDK,支持一句话识别和实时流式识别,相关的代码已经开源在GitHub和Gitee上。
ASRT语音识别系统的API已经为AI柠檬站内搜索引擎提供了语音识别服务,用于该站语音搜索功能的实现。
一些方向和思路:
首先,需要选择一个适合的语音识别引擎。常见的一些引擎有CMU Sphinx、Kaldi、百度语音、讯飞开放平台等等。选定引擎后,需要对其进行配置和训练,使其能够适应自己的应用场景。
接下来,需要进行搭建离线语音识别系统的工作。可以通过使用Ubuntu等Linux系统进行安装和配置。在系统中需要安装上一步中选择的语音识别引擎和相关依赖包。
为了使得离线语音识别系统能够方便地被访问和使用,需要提供相应的Web API。您可以使用Flask等框架搭建Web服务,并在其上下文中调用语音识别引擎进行语音识别工作。
最后,为了保证语音识别的精度和流畅度,还需要进行一系列优化和调试工作,例如声音降噪、语速控制、模型调优等等。希望以上方向可以帮助到您。
Whisper 是一种自动语音识别模型,基于从网络上收集的 680,000 小时多语言数据进行训练。
Whisper是一个语音识别引擎,可以用于开发语音控制应用程序,但它通常用于移动设备和嵌入式设备上,以提供离线语音识别的功能。如果您想使用Java搭建离线语音识别,您可以尝试使用其他语音识别引擎,如CMU Sphinx和Kaldi。 这些引擎都支持离线语音识别,并提供Java API供开发人员使用。
https://blog.csdn.net/sailist/article/details/95751825
专注E2E语音识别,腾讯AI Lab开源轻量级语音处理工具包PIKA-CSDN社区
https://blog.csdn.net/devid008/article/details/129656356
离线语音识别第三方服务提供商
https://www.xfyun.cn/service/offline_iat
科大讯飞离线包仅基于安卓,也不支持java离线版
还像可以调用本地dll 实现离线语音
https://ai.baidu.com/tech/speech/realtime_asr
不支持离线
https://ai.aliyun.com/nls/trans