Vosk 中文语音识别中的 JNA 编码问题及解决方案
背景
在使用 Vosk 进行中文语音识别时,遇到了一个令人困惑的问题:同样的代码在不同的运行环境下会产生不同的识别结果。更奇怪的是,英文语音识别完全正常,只有中文识别会出现这种情况。
环境信息
JDK 版本:OpenJDK 17
Vosk 版本:0.3.45
中文模型:vosk-model-cn-0.2
框架:Quarkus(后期集成)
问题现象
典型症状
英文语音识别正常:使用英文模型进行语音识别,无论在 IDE 中运行还是命令行运行,结果都是一致的
中文语音识别异常:使用 vosk-model-cn-0.2 模型时,同样的音频文件在不同运行方式下产生不同的识别结果
字节数组差异:即使使用相同的函数和相同的输入,返回的 byte[] 也不相同
点击查看代码
public class VoskChineseRecognizer {
public byte[] recognizeAudio(byte[] audioData) {
// 每次都重新创建实例
VoskModel model = new VoskModel("path/to/vosk-model-cn-0.2");
VoskRecognizer recognizer = new VoskRecognizer(model, 16000);
// 处理音频数据
recognizer.acceptWaveForm(audioData, audioData.length);
String result = recognizer.getResult();
// 清理资源
recognizer.close();
model.close();
return result.getBytes();
}
}
这个看似正常的代码在不同环境下会产生不同的结果。
根本原因分析
JNA(Java Native Access)编码问题
Vosk 是基于 C++ 的 native 库,通过 JNA 与 Java 进行交互。问题的根本原因是:
JNA 默认编码不一致:
Windows 系统:通常默认 GBK 或系统编码
Linux/Mac 系统:通常默认 UTF-8
IDE 环境:可能有自己的编码设置
命令行环境:继承系统默认编码
中文字符编码敏感:
点击查看代码
// 同一个中文字符在不同编码下的字节表示
String chinese = "你好";
byte[] utf8Bytes = chinese.getBytes(StandardCharsets.UTF_8);
byte[] gbkBytes = chinese.getBytes("GBK");
System.out.println("UTF-8: " + Arrays.toString(utf8Bytes)); // [-28, -67, -96, -27, -91, -67]
System.out.println("GBK: " + Arrays.toString(gbkBytes)); // [-60, -29, -70, -61]
Native 交互过程:
Java 字符串 → JNA 编码转换 → C++ native 库
C++ 处理结果 → JNA 编码转换 → Java 字符串
编码不一致导致中文字符在转换过程中损坏或变化
解决方案
核心解决方法
- 在程序启动时设置 JNA 编码:
static { System.setProperty("jna.encoding", "UTF-8"); }
2. 程序运行指定系统变量
-Djna.encoding=UTF-8 -Dfile.encoding=UTF-8
浙公网安备 33010602011771号