-
Notifications
You must be signed in to change notification settings - Fork 5
Example SimulaTrans
oldmanpushcart edited this page Sep 12, 2024
·
2 revisions
黄昏时分,客厅里温暖的灯光洒在书桌上,书桌上摆满了英语课本和练习册。我的女儿狗蛋正坐在桌前,眉头紧锁,她似乎遇到了一个小小的困难。
- 狗蛋: 爸爸,“中午吃什么”,我不会用英语说
- 蛋爸: “what do 游 want to eat at lunch time?”
- 狗蛋: 那“晚餐吃什么”呢?
我沉思良久,放下了手中的王者荣耀,敏锐的意识到:如果再这样下去,我就上不了王者了。不行,必须要为自己...哦不,要为这个家庭做点什么。
一个简单的思路是一个 ASR > LLM > TTS
的过程
-
ASR:将语音转换为中文
选用 语音识别-Paraformer 模型,这个模型可以帮我做到实时语音识别。
-
LLM:将中文翻译为英文
选用 通义千问-Turbo 模型,它是通义千问大语言模型系列速度最快、成本很低的模型,适合担任这次语音翻译的LLM角色。
-
TTS:将英文转换为语音
选用 语音合成-CosyVoice 模型,主要看上这个模型支持全双工的语音合成。当需要合成的句子比较多的时候,全双工模式可以更快的响应语音播放诉求,更适合同声翻译这个场景。就是要快、要接近同步。
<dependency>
<groupId>io.github.oldmanpushcart</groupId>
<artifactId>dashscope4j</artifactId>
<version>2.2.1</version>
</dependency>
DashScope4j是一个开源的灵积非官方 Java SDK,基于 JDK17 构建。它旨在提供一个功能丰富、易于集成和使用灵积API(通义千问模型)的Java库,以便开发者能够通灵积API轻松实现多模态对话、向量嵌入和图像处理等功能。
通义千问的语音识别的例子如下:
// 文本集合
final var strings = new String[]{
"白日依山尽,",
"黄河入海流。",
"欲穷千里目,",
"更上一层楼。"
};
/*
* 语音合成请求
* 采样率:16000
* 编码格式:WAV(PCM)
*/
final var request = SpeechSynthesisRequest.newBuilder()
.model(SpeechSynthesisModel.COSYVOICE_LONGXIAOCHUN_V1)
.option(SpeechSynthesisOptions.SAMPLE_RATE, 16000)
.option(SpeechSynthesisOptions.FORMAT, SpeechSynthesisRequest.Format.WAV)
.build();
// 以语音合成请求为模板,对每个文本生成一个语音合成请求
final var requests = Stream.of(strings)
.map(string -> SpeechSynthesisRequest.newBuilder(request)
.text(string)
.build()
)
.toList();
// 聚合成请求发布器
final var requestPublisher = FlowPublishers.fromIterator(requests);
// 进行语音合成
client.audio().synthesis(request)
// 打开语音合成数据交互通道:全双工模式,输出到audio.wav文件
.exchange(Exchange.Mode.DUPLEX, ExchangeListeners.ofPath(Path.of("./audio.wav")))
// 发送语音合成请求序列
.thenCompose(exchange -> exchange.writeDataPublisher(requestPublisher))
// 语音合成结束
.thenCompose(Exchange::finishing)
// 等待通道关闭
.thenCompose(Exchange::closeFuture)
.toCompletableFuture()
.join();
基于上一节 "语音合成" 的示例,我们得到了audio.wav,接下来我们可以用语音识别来识别这个音频文件。
// 构建音频文件的ByteBuffer发布器
final var byteBufPublisher = FlowPublishers.fromURI(Path.of("./audio.wav").toUri());
/*
* 构建语音识别请求
* 采样率:16000
* 音频格式:WAV(PCM)
*/
final var request = RecognitionRequest.newBuilder()
.model(RecognitionModel.PARAFORMER_REALTIME_V2)
.option(RecognitionOptions.SAMPLE_RATE, 16000)
.option(RecognitionOptions.FORMAT, RecognitionRequest.Format.WAV)
.build();
// 识别文本缓存
final var stringBuf = new StringBuilder();
// 进行语音识别
client.audio().recognition(request)
// 打开语音识别数据交互通道:全双工模式,输出到文本缓存
.exchange(Exchange.Mode.DUPLEX, ExchangeListeners.ofConsume(response -> {
final var sentence = response.output().sentence();
if (sentence.isEnd()) {
stringBuf.append(sentence.text()).append("\n");
}
}))
// 发送音频文件字节流数据
.thenCompose(exchange -> exchange.writeByteBufferPublisher(byteBufPublisher))
// 语音识别结束
.thenCompose(Exchange::finishing)
// 等待通道关闭
.thenCompose(Exchange::closeFuture)
.toCompletableFuture()
.join();
// 输出识别文本
System.out.println(stringBuf);
文本识别结果为
白日依山尽,黄河入海流。
欲穷千里目,更上一层楼
有了这两个API的前置知识,我们现在可以进行编写我们的代码了。完整的代码可以参考链接 SimulaTransAgent.java
这里说下如何使用这段例子代码和重点代码说明'
public static void main(String... args) throws LineUnavailableException {
final var executor = Executors.newFixedThreadPool(20);
final var client = DashScopeClient.newBuilder()
.ak("sk-***")
.executor(executor)
.timeout(Duration.ofMillis(5000))
.build();
final var format = new AudioFormat(16000, 16, 1, true, false);
try (final var target = AudioSystem.getTargetDataLine(format);
final var source = AudioSystem.getSourceDataLine(format);) {
target.open(format);
source.open(format);
new SimulaTransAgent(client, target, source)
.execute();
}
}
这里ak(API-KEY)需要你去通义千问官网申请,阿里云百炼-我的API-KEY ,申请好后填写进来,记得做好保密工作。
运行起来就可以看到效果了