Python
Python の例を実行する前に、pip を使用してサードパーティの音声再生ライブラリをインストールする必要があります。
# coding=utf-8
# pyaudio のインストール手順:
# APPLE Mac OS X
# brew install portaudio
# pip install pyaudio
# Debian/Ubuntu
# sudo apt-get install python-pyaudio python3-pyaudio
# または
# pip install pyaudio
# CentOS
# sudo yum install -y portaudio portaudio-devel && pip install pyaudio
# Microsoft Windows
# python -m pip install pyaudio
import pyaudio
import dashscope
from dashscope.audio.tts_v2 import *
from http import HTTPStatus
from dashscope import Generation
# 環境変数に API キーを設定していない場合は、次の行のコメントを解除し、「apiKey」を実際の API キーに置き換えてください。
# dashscope.api_key = "apiKey"
# モデルのバージョンごとに、対応する音声バージョンが必要です:
# cosyvoice-v3-flash/cosyvoice-v3-plus:longanyang などの音声を使用します。
# cosyvoice-v2:longxiaochun_v2 などの音声を使用します。
model = "cosyvoice-v3-flash"
voice = "longanyang"
class Callback(ResultCallback):
_player = None
_stream = None
def on_open(self):
print("websocket is open.")
self._player = pyaudio.PyAudio()
self._stream = self._player.open(
format=pyaudio.paInt16, channels=1, rate=22050, output=True
)
def on_complete(self):
print("speech synthesis task complete successfully.")
def on_error(self, message: str):
print(f"speech synthesis task failed, {message}")
def on_close(self):
print("websocket is closed.")
# プレーヤーを停止
self._stream.stop_stream()
self._stream.close()
self._player.terminate()
def on_event(self, message):
print(f"recv speech synthsis message {message}")
def on_data(self, data: bytes) -> None:
print("audio result length:", len(data))
self._stream.write(data)
def synthesizer_with_llm():
callback = Callback()
synthesizer = SpeechSynthesizer(
model=model,
voice=voice,
format=AudioFormat.PCM_22050HZ_MONO_16BIT,
callback=callback,
)
messages = [{"role": "user", "content": "自己紹介をしてください。"}]
responses = Generation.call(
model="qwen-turbo",
messages=messages,
result_format="message", # 結果のフォーマットを 'message' に設定
stream=True, # ストリーム出力を有効化
incremental_output=True, # インクリメンタル出力を有効化
)
for response in responses:
if response.status_code == HTTPStatus.OK:
print(response.output.choices[0]["message"]["content"], end="")
synthesizer.streaming_call(response.output.choices[0]["message"]["content"])
else:
print(
"Request id: %s, Status code: %s, error code: %s, error message: %s"
% (
response.request_id,
response.status_code,
response.code,
response.message,
)
)
synthesizer.streaming_complete()
print('Request ID: ', synthesizer.get_last_request_id())
if __name__ == "__main__":
synthesizer_with_llm()
Java
import com.alibaba.dashscope.aigc.generation.Generation;
import com.alibaba.dashscope.aigc.generation.GenerationParam;
import com.alibaba.dashscope.aigc.generation.GenerationResult;
import com.alibaba.dashscope.audio.tts.SpeechSynthesisResult;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesisAudioFormat;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesisParam;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesizer;
import com.alibaba.dashscope.common.Message;
import com.alibaba.dashscope.common.ResultCallback;
import com.alibaba.dashscope.common.Role;
import com.alibaba.dashscope.exception.InputRequiredException;
import com.alibaba.dashscope.exception.NoApiKeyException;
import io.reactivex.Flowable;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.sound.sampled.*;
public class Main {
// モデルのバージョンごとに、対応する音声バージョンが必要です:
// cosyvoice-v3-flash/cosyvoice-v3-plus:longanyang などの音声を使用します。
// cosyvoice-v2:longxiaochun_v2 などの音声を使用します。
private static String model = "cosyvoice-v3-flash";
private static String voice = "longanyang";
public static void process() throws NoApiKeyException, InputRequiredException {
// 再生スレッド
class PlaybackRunnable implements Runnable {
// オーディオフォーマットを設定します。デバイス、
// 合成音声のパラメーター、プラットフォームに基づいて設定してください。ここでは、
// 22050 Hz 16 ビットモノラルに設定されています。モデルのサンプルレートとデバイスの
// 互換性に基づいて、他の
// サンプルレートとフォーマットを選択することを推奨します。
private AudioFormat af = new AudioFormat(22050, 16, 1, true, false);
private DataLine.Info info = new DataLine.Info(SourceDataLine.class, af);
private SourceDataLine targetSource = null;
private AtomicBoolean runFlag = new AtomicBoolean(true);
private ConcurrentLinkedQueue<ByteBuffer> queue =
new ConcurrentLinkedQueue<>();
// プレーヤーを準備します。
public void prepare() throws LineUnavailableException {
targetSource = (SourceDataLine) AudioSystem.getLine(info);
targetSource.open(af, 4096);
targetSource.start();
}
public void put(ByteBuffer buffer) {
queue.add(buffer);
}
// 再生を停止します。
public void stop() {
runFlag.set(false);
}
@Override
public void run() {
if (targetSource == null) {
return;
}
while (runFlag.get()) {
if (queue.isEmpty()) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
continue;
}
ByteBuffer buffer = queue.poll();
if (buffer == null) {
continue;
}
byte[] data = buffer.array();
targetSource.write(data, 0, data.length);
}
// 残りのキャッシュされた音声をすべて再生します。
if (!queue.isEmpty()) {
ByteBuffer buffer = null;
while ((buffer = queue.poll()) != null) {
byte[] data = buffer.array();
targetSource.write(data, 0, data.length);
}
}
// プレーヤーを解放します。
targetSource.drain();
targetSource.stop();
targetSource.close();
}
}
// ResultCallback<SpeechSynthesisResult> を継承するサブクラスを作成して
// コールバックインターフェイスを実装します。
class ReactCallback extends ResultCallback<SpeechSynthesisResult> {
private PlaybackRunnable playbackRunnable = null;
public ReactCallback(PlaybackRunnable playbackRunnable) {
this.playbackRunnable = playbackRunnable;
}
// サービスがストリーミング合成結果を返したときのコールバック。
@Override
public void onEvent(SpeechSynthesisResult result) {
// getAudioFrame を使用してストリーミング結果のバイナリデータを取得します。
if (result.getAudioFrame() != null) {
// データをプレーヤーにストリーミングします。
playbackRunnable.put(result.getAudioFrame());
}
}
// サービスが合成を完了したときのコールバック。
@Override
public void onComplete() {
// 再生スレッドに終了を通知します。
playbackRunnable.stop();
}
// エラーが発生したときのコールバック。
@Override
public void onError(Exception e) {
// 再生スレッドに終了を通知します。
System.out.println(e);
playbackRunnable.stop();
}
}
PlaybackRunnable playbackRunnable = new PlaybackRunnable();
try {
playbackRunnable.prepare();
} catch (LineUnavailableException e) {
throw new RuntimeException(e);
}
Thread playbackThread = new Thread(playbackRunnable);
// 再生スレッドを開始します。
playbackThread.start();
/******* 生成 AI モデルを呼び出してストリーミングテキストを取得 *******/
// LLM 呼び出しの準備をします。
Generation gen = new Generation();
Message userMsg = Message.builder()
.role(Role.USER.getValue())
.content("自己紹介をしてください。")
.build();
GenerationParam genParam =
GenerationParam.builder()
// 環境変数に API キーを設定していない場合は、次の行のコメントを解除し、「apiKey」を実際の API キーに置き換えてください。
// .apiKey("apikey")
.model("qwen-turbo")
.messages(Arrays.asList(userMsg))
.resultFormat(GenerationParam.ResultFormat.MESSAGE)
.topP(0.8)
.incrementalOutput(true)
.build();
// 音声合成タスクを準備します。
SpeechSynthesisParam param =
SpeechSynthesisParam.builder()
// 環境変数に API キーを設定していない場合は、次の行のコメントを解除し、「apiKey」を実際の API キーに置き換えてください。
// .apiKey("apikey")
.model(model)
.voice(voice)
.format(SpeechSynthesisAudioFormat
.PCM_22050HZ_MONO_16BIT)
.build();
SpeechSynthesizer synthesizer =
new SpeechSynthesizer(param, new ReactCallback(playbackRunnable));
Flowable<GenerationResult> result = gen.streamCall(genParam);
result.blockingForEach(message -> {
String text =
message.getOutput().getChoices().get(0).getMessage().getContent().trim();
if (text != null && !text.isEmpty()) {
System.out.println("LLM output: " + text);
synthesizer.streamingCall(text);
}
});
synthesizer.streamingComplete();
System.out.print("Request ID: " + synthesizer.getLastRequestId());
try {
// 再生スレッドがすべての音声の再生を終えるのを待ちます。
playbackThread.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) throws NoApiKeyException, InputRequiredException {
process();
System.exit(0);
}
}