对接这个语音转文字主要遇到的一个问题就是文件转换, 前端一般传过来的时mp3文件, 而它需要的文件类型是wav, 使用java自带的一些转文件依赖文件会受损, 所以就用了一个ffmpeg, 还是用现成的好, 转了就能用哈哈.
可以先看看官网的参考API
Paraformer实时语音识别Java API_大模型服务平台百炼(Model Studio)-阿里云帮助中心
前提条件一定要看, 先把要用到的东西准备好.
另外说一下配置API-KEY到环境变量, 这个就看自己了, 你能直接写到.apikey()中, 也能放到yml文件中, 也可以放环境变量, 这个都可以.
以下是逻辑代码, 注释尽量写的很详细了.
这个代码块中的convertMp3ToWav(mp3Path,wavPath)方法我写到下边另外一个代码块了, 这两个方法在一个类中, 如果要看包引用就看第一个代码块的import就好.
import cn.dev33.satoken.stp.StpUtil;
import com.alibaba.dashscope.audio.asr.recognition.Recognition;
import com.alibaba.dashscope.audio.asr.recognition.RecognitionParam;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import org.jbase.common.utils.ResultBean;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@RestController
@RequestMapping("/obj")
public class UploadImageRest {
//临时存放mp3文件的文件夹
private final String MP3_UPLOAD_DIR = "C:\\Users\\Administrator\\Desktop\\yinpin\\mp3\\";
//临时存放wav文件的文件夹
private final String WAV_UPLOAD_DIR = "C:\\Users\\Administrator\\Desktop\\yinpin\\wav\\";
@PostMapping("voiceToText")
public ResultBean voiceToText(@RequestParam("file") MultipartFile file)throws NoApiKeyException, InterruptedException {
// 检查文件是否为空
if (file.isEmpty()) {
return ResultBean.fireFail().setMsg("文件不能为空");
}
try {
// 创建mp3上传目录(如果不存在)
File uploadDir = new File(MP3_UPLOAD_DIR);
if (!uploadDir.exists()) {
uploadDir.mkdirs();
}
// 创建wav上传目录(如果不存在)
File wavUploadDir = new File(WAV_UPLOAD_DIR);
if (!wavUploadDir.exists()) {
wavUploadDir.mkdirs();
}
//获取了一下当前登录用户的id, 用来拼接文件名, 可忽视
int mid = StpUtil.getLoginIdAsInt();
//时间戳
long l = System.currentTimeMillis();
//拼接mp3文件名
String fileName = mid + "_" + l + ".mp3";
//拼接wav文件名
String wavFileName = mid + "_" + l + ".wav";
// 创建两个文件的文件路径
Path mp3FilePath = Paths.get(MP3_UPLOAD_DIR + fileName);
Path wavFilePath = Paths.get(WAV_UPLOAD_DIR + wavFileName);
//上传mp3文件
Files.write(mp3FilePath, file.getBytes());
//上传成功之后调用转换文件格式的方法创建wav文件, 该方法在下边, 可以看详情
convertMp3ToWav(mp3FilePath.toString(),wavFilePath.toString());
//此时wav文件就可以使用了, 符合了阿里云的格式要求, 开始转文字
//获取文件
File audioFile = new File(wavFilePath.toString());
//文件是否存在
if (!audioFile.exists() || !audioFile.isFile()) {
System.out.println("音频文件不存在或路径无效: " + wavFilePath.toString());
System.exit(1);
}
// 识别参数
RecognitionParam param = RecognitionParam.builder()
//模型选择, 在阿里云广场挑选合适的模型
.model("paraformer-realtime-v2")
//传入的文件格式, 我们转成了wav格式, 就使用wav
.format("wav") // 可选格式: pcm, wav, opus, speex, aac, amr
//采样率这个模型支持16000及以上, 我们在转文件格式时也设置的16000,所以直接设置16000就可以
.sampleRate(16000) // 8000 或 16000
//应该是识别语言, 看需求吧, 可以参考官网参数
.parameter("language_hints", new String[]{"zh", "en"})
//API-KEY 可以写到配置文件中, 这里我就直接填写了
.apiKey("sk-50bba6b3f5874a0aab22031e2dcbc978")
.build();
Recognition recognizer = new Recognition();
System.out.println("正在处理文件: " + wavFilePath.toString());
// 开始识别
String result = recognizer.call(param, audioFile);
// 解析并输出文本
Gson gson = new GsonBuilder().setPrettyPrinting().create();
JsonObject jsonObject = gson.fromJson(result, JsonObject.class);
//创建一个接收结果的str
StringBuilder str = new StringBuilder();
if (jsonObject.has("sentences")) {
System.out.println("\n识别文本:");
for (JsonElement sent : jsonObject.get("sentences").getAsJsonArray()) {
JsonObject sentObj = sent.getAsJsonObject();
//将结果放入str
str.append(sentObj.get("text").getAsString());
}
System.out.println(str.toString());
} else {
System.out.println("未找到识别文本");
}
//在识别成功后将两个文件夹的文件删除, 也可以删除, 反正没啥用, 一次性的文件
// 构建文件路径
File mp3 = mp3FilePath.toFile();
File wav = wavFilePath.toFile();
mp3.delete();
wav.delete();
//将结果返回 记的改为你自己项目的结果封装类
return ResultBean.fireSuccess().setMsg(str.toString());
//System.exit(0);
} catch (IOException e) {
e.printStackTrace();
return ResultBean.fireFail().setMsg("文件失败");
}
}
}
接下来说convertMp3ToWav(mp3Path,wavPath)方法, 该方法就是通过ffmpeg将mp3转为wav, 但是在使用之前需要下载ffmpeg, 首先找到它的官网: Download FFmpeg 有Linux, Win和Mac, 我用的是Win的, 直接下载就行, 免费的, 我就不贴压缩包了.
下载下来之后解压到目标文件夹, 然后将bin目录放入到环境变量中, 还有重要的一点就是重启电脑, 不重启电脑在代码中使用指令会报错 :"Cannot run program “ffmpeg“: CreateProcess error=2, 系统找不到指定的文件。"
这些都弄好之后就可以直接调该方法了
private void convertMp3ToWav(String mp3FilePath, String wavFilePath) {
// 使用ProcessBuilder调用ffmpeg命令,设置采样率为16000
ProcessBuilder processBuilder = new ProcessBuilder(
"ffmpeg",
"-i", mp3FilePath,
"-ar", "16000", // 设置采样率为16000Hz
wavFilePath
);
processBuilder.redirectErrorStream(true); // 合并错误输出
try {
// 启动进程执行命令
Process process = processBuilder.start();
// 等待进程执行完成
int exitCode = process.waitFor();
if (exitCode == 0) {
System.out.println("转换成功!");
} else {
System.out.println("转换失败!退出代码:" + exitCode);
}
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
最后还有一点可能遇到的问题, 就是请求被阻塞, 会一直在等待, 原因就是convertMp3ToWav()方法中实际是在发送指令
ffmpeg -i "C:\Users\Administrator\Desktop\yinpin\mp3\aaaa.mp3" -ar 16000 "C:\Users\Administrator\Desktop\yinpin\wav\aaaa.wav"
而调用这个指令时, 如果要转换的文件比如"C:\Users\Administrator\Desktop\yinpin\wav\aaaa.wav"已经存在了, 那么他会询问是否覆盖源文件, N/Y, 需要再输入一下Y来执行下一步, 这是我在测试的时候发现的, 正常来说上边的代码不会出现这个问题, 因为文件名都是随机的, 而且文件在使用完毕之后会删除掉. 如果想保险起见可以在店指令时附加-y参数, 就是总是选择是就好了, 就像这样
// 使用ProcessBuilder调用ffmpeg命令,设置采样率为16000
ProcessBuilder processBuilder = new ProcessBuilder(
"ffmpeg",
"-y", //在此处添加-y参数
"-i", mp3FilePath,
"-ar", "16000", // 设置采样率为16000Hz
wavFilePath
);
不过我没这样试过, 不知道能不能行得通, 但是原因肯定是对的, 如果不行可以搜一下其他方法.
好了, 现在就可以调接口了.
大家觉得哪里可以优化或者有问题可以告诉我, 我是小白, 错误可以让我学到更多东西.