0


springboot整合vosk实现简单的语音识别功能

vosk开源语音识别

Vosk是开源的语音识别工具包。Vosk支持的事情包括:

  1. 支持十九种语言 - 中文,英语,印度英语,德语,法语,西班牙语,葡萄牙语,俄语,土耳其语,越南语,意大利语,荷兰人,加泰罗尼亚语,阿拉伯, 希腊语, 波斯语, 菲律宾语,乌克兰语, 哈萨克语。
  2. 移动设备上脱机工作-Raspberry Pi,Android,iOS。
  3. 使用简单的 pip3 install vosk 安装。
  4. 每种语言的手提式模型只有是50Mb, 但还有更大的服务器模型可用。
  5. 提供流媒体API,以提供最佳用户体验(与流行的语音识别python包不同)。
  6. 还有用于不同编程语言的包装器-java / csharp / javascript等。
  7. 可以快速重新配置词汇以实现最佳准确性。
  8. 支持说话人识别。

vosk-api

离线语音识别API,适用于Android,iOS,Raspberry Pi和具有Python,Java,C#等

链接: vosk-api github地址

有各语言的使用的示例

vosk-server

基于Vosk和Kaldi库的WebSocket,gRPC和WebRTC语音识别服务器

链接: vosk-server github地址

有各语言的使用的示例

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

publicclassVoskResult{privateString text;publicStringgetText(){return text;}publicvoidsetText(String text){this.text = text;}}

vosk模型加载

packagecom.fjdci.vosk;importorg.vosk.LibVosk;importorg.vosk.LogLevel;importorg.vosk.Model;importjava.io.IOException;/**
 * vosk模型加载
 * @author zhou
 */publicclassVoskModel{/**
     * 3. 使用 volatile 保证线程安全
     * 禁止指令重排
     * 保证可见性
     * 不保证原子性
     */privatestaticvolatileVoskModel instance;privateModel voskModel;publicModelgetVoskModel(){return voskModel;}/**
     * 1.私有构造函数
     */privateVoskModel(){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 =newModel(modelStr);LibVosk.setLogLevel(LogLevel.INFO);}catch(IOException e){
            e.printStackTrace();}}/**
     * 2.通过静态方法获取一个唯一实例
     * DCL 双重检查锁定 (Double-CheckedLocking)
     * 在多线程情况下保持⾼性能
     */publicstaticVoskModelgetInstance(){if(instance ==null){synchronized(VoskModel.class){if(instance ==null){// 1. 分配内存空间 2、执行构造方法,初始化对象 3、把这个对象指向这个空间
                    instance =newVoskModel();}}}return instance;}/**
     * 多线程测试加载
     * @param args
     */publicstaticvoidmain(String[] args){for(int i =0; i <5; i++){newThread(()->{VoskModel.getInstance();}).start();}}}

语言识别工具类

packagecom.fjdci.vosk;importlombok.extern.slf4j.Slf4j;importorg.springframework.stereotype.Component;importorg.vosk.Model;importorg.vosk.Recognizer;importws.schild.jave.EncoderException;importws.schild.jave.MultimediaObject;importws.schild.jave.info.AudioInfo;importws.schild.jave.info.MultimediaInfo;importjava.io.File;importjava.io.FileInputStream;importjava.io.InputStream;importjava.util.ArrayList;importjava.util.List;importjava.util.Optional;importjava.util.UUID;@Slf4j@ComponentpublicclassVoiceUtil{publicstaticvoidmain(String[] args)throwsEncoderException{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
     */privatestaticStringacceptWaveForm(String wavFilePath,long cutDuration)throwsEncoderException{// 判断视频的长度long startTime =System.currentTimeMillis();MultimediaObject multimediaObject =newMultimediaObject(newFile(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 =newStringBuilder();for(String string : strings){File f =newFile(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
     */privatestaticList<String>cutWavFile(String wavFilePath,long cutDuration,long offset,long forNum)throwsEncoderException{UUID uuid =UUID.randomUUID();// 大文件切割为固定时长的小文件List<String> strings =newArrayList<>();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
     */publicstaticStringgetRecognizerResult(File f,int channels){StringBuilder result =newStringBuilder();Model voskModel =VoskModel.getInstance().getVoskModel();// 采样率为音频采样率的声道倍数
        log.info("====加载完成,开始分析====");try(Recognizer recognizer =newRecognizer(voskModel,16000* channels);InputStream ais =newFileInputStream(f)){int nbytes;byte[] b =newbyte[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
     */privatestaticStringgetResult(String result){VoskResult voskResult =JacksonMapperUtils.json2pojo(result,VoskResult.class);returnOptional.ofNullable(voskResult).map(VoskResult::getText).orElse("");}}

jave2 音频处理工具类

packagecom.fjdci.vosk;importws.schild.jave.Encoder;importws.schild.jave.EncoderException;importws.schild.jave.InputFormatException;importws.schild.jave.MultimediaObject;importws.schild.jave.encode.AudioAttributes;importws.schild.jave.encode.EncodingAttributes;importws.schild.jave.info.AudioInfo;importws.schild.jave.info.MultimediaInfo;importjava.io.File;publicclassJave2Util{/**
     * @param src      来源文件路径
     * @param target   目标文件路径
     * @param offset   设置起始偏移量(秒)
     * @param duration 设置切片的音频长度(秒)
     * @throws EncoderException
     */publicstaticvoidcut(String src,String target,Float offset,Float duration)throwsEncoderException{File targetFile =newFile(target);if(targetFile.exists()){
            targetFile.delete();}File srcFile =newFile(src);MultimediaObject srcMultiObj =newMultimediaObject(srcFile);MultimediaInfo srcMediaInfo = srcMultiObj.getInfo();Encoder encoder =newEncoder();EncodingAttributes encodingAttributes =newEncodingAttributes();//设置起始偏移量(秒)
        encodingAttributes.setOffset(offset);//设置切片的音频长度(秒)
        encodingAttributes.setDuration(duration);// 输入格式
        encodingAttributes.setInputFormat("wav");//设置音频属性AudioAttributes audio =newAudioAttributes();
        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,newFile(target), encodingAttributes);}/**
     * 转化音频格式
     *
     * @param oldFormatPath : 原音乐路径
     * @param newFormatPath : 目标音乐路径
     * @return
     */publicstaticbooleantransforMusicFormat(String oldFormatPath,String newFormatPath){File source =newFile(oldFormatPath);File target =newFile(newFormatPath);// 音频转换格式类Encoder encoder =newEncoder();// 设置音频属性AudioAttributes audio =newAudioAttributes();
        audio.setCodec(null);// 设置转码属性EncodingAttributes attrs =newEncodingAttributes();
        attrs.setInputFormat("wav");
        attrs.setAudioAttributes(audio);try{
            encoder.encode(newMultimediaObject(source), target, attrs);System.out.println("传唤已完成...");returntrue;}catch(IllegalArgumentException e){
            e.printStackTrace();}catch(InputFormatException e){
            e.printStackTrace();}catch(EncoderException e){
            e.printStackTrace();}returnfalse;}publicstaticvoidmain(String[] args)throwsEncoderException{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
     */privatestaticvoidinfo(String filePath)throwsEncoderException{File file =newFile(filePath);MultimediaObject multimediaObject =newMultimediaObject(file);MultimediaInfo info = multimediaObject.getInfo();// 时长long duration = info.getDuration();String format = info.getFormat();// format:movSystem.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 = 16KHzint 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
     */publicstaticbooleanaudioEncode(String inputFormatPath,String outputFormatPath){String outputFormat =getSuffix(outputFormatPath);String inputFormat =getSuffix(inputFormatPath);File source =newFile(inputFormatPath);File target =newFile(outputFormatPath);try{MultimediaObject multimediaObject =newMultimediaObject(source);// 获取音频文件的编码信息MultimediaInfo info = multimediaObject.getInfo();AudioInfo audioInfo = info.getAudio();//设置音频属性AudioAttributes audio =newAudioAttributes();
            audio.setBitRate(audioInfo.getBitRate());
            audio.setSamplingRate(audioInfo.getSamplingRate());
            audio.setChannels(audioInfo.getChannels());// 设置转码属性EncodingAttributes attrs =newEncodingAttributes();
            attrs.setInputFormat(inputFormat);
            attrs.setOutputFormat(outputFormat);
            attrs.setAudioAttributes(audio);// 音频转换格式类Encoder encoder =newEncoder();// 进行转换
            encoder.encode(newMultimediaObject(source), target, attrs);returntrue;}catch(IllegalArgumentException|EncoderException e){
            e.printStackTrace();}returnfalse;}/**
     * 获取文件路径的.后缀
     * @param outputFormatPath
     * @return
     */privatestaticStringgetSuffix(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.<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多内存
在这里插入图片描述

参考链接

链接: 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柠檬站内搜索引擎提供了语音识别服务,用于该站语音搜索功能的实现。

搭建一个离线的语音识别系统 并提供webApi访问

一些方向和思路:

  1. 确定语音识别引擎

首先,需要选择一个适合的语音识别引擎。常见的一些引擎有CMU Sphinx、Kaldi、百度语音、讯飞开放平台等等。选定引擎后,需要对其进行配置和训练,使其能够适应自己的应用场景。

  1. 搭建离线语音识别系统

接下来,需要进行搭建离线语音识别系统的工作。可以通过使用Ubuntu等Linux系统进行安装和配置。在系统中需要安装上一步中选择的语音识别引擎和相关依赖包。

  1. 提供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


本文转载自: https://blog.csdn.net/qq_41604890/article/details/130546401
版权归原作者 qq_250056868 所有, 如有侵权,请联系我们删除。

“springboot整合vosk实现简单的语音识别功能”的评论:

还没有评论