Skip to content

Example SimulaTrans

oldmanpushcart edited this page Sep 12, 2024 · 2 revisions

通义千问例子-同声传译

黄昏时分,客厅里温暖的灯光洒在书桌上,书桌上摆满了英语课本和练习册。我的女儿狗蛋正坐在桌前,眉头紧锁,她似乎遇到了一个小小的困难。

  • 狗蛋: 爸爸,“中午吃什么”,我不会用英语说
  • 蛋爸: “what do 游 want to eat at lunch time?”
  • 狗蛋: 那“晚餐吃什么”呢?

我沉思良久,放下了手中的王者荣耀,敏锐的意识到:如果再这样下去,我就上不了王者了。不行,必须要为自己...哦不,要为这个家庭做点什么。

如何用通义千问做一个同声传译

整体思路和模型选择

一个简单的思路是一个 ASR > LLM > TTS 的过程

  1. ASR:将语音转换为中文

    选用 语音识别-Paraformer 模型,这个模型可以帮我做到实时语音识别。

  2. LLM:将中文翻译为英文

    选用 通义千问-Turbo 模型,它是通义千问大语言模型系列速度最快、成本很低的模型,适合担任这次语音翻译的LLM角色。

  3. TTS:将英文转换为语音

    选用 语音合成-CosyVoice 模型,主要看上这个模型支持全双工的语音合成。当需要合成的句子比较多的时候,全双工模式可以更快的响应语音播放诉求,更适合同声翻译这个场景。就是要快、要接近同步。

技术准备

引入通义千问的SDK:dashscope4j

<dependency>
    <groupId>io.github.oldmanpushcart</groupId>
    <artifactId>dashscope4j</artifactId>
    <version>2.2.1</version>
</dependency>

DashScope4j是一个开源的灵积非官方 Java SDK,基于 JDK17 构建。它旨在提供一个功能丰富、易于集成和使用灵积API(通义千问模型)的Java库,以便开发者能够通灵积API轻松实现多模态对话、向量嵌入和图像处理等功能。

语音合成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();

语音识别API介绍

基于上一节 "语音合成" 的示例,我们得到了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 ,申请好后填写进来,记得做好保密工作。

运行起来就可以看到效果了

同声传译演示视频