import android.content.Context;
import com.alibaba.aoq.clientsdk.AoqClientEngine;
import com.alibaba.aoq.clientsdk.AoqClientListener;
import org.json.JSONException;
import org.json.JSONObject;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
public final class TtsClient {
private AoqClientEngine engine;
private String taskId;
private String pendingText;
private String pendingVoice;
private boolean connected;
private boolean taskActive;
public TtsClient(Context context, AoqClientEngine.AoqConnectConfig connectConfig) {
AoqClientListener listener = new AoqClientListener() {
@Override
public void onConnectionStatusChange(AoqClientEngine.AoqConnectionStatus status) {
if (status == AoqClientEngine.AoqConnectionStatus.AoqConnectionStatusConnected) {
connected = true;
} else if (status == AoqClientEngine.AoqConnectionStatus
.AoqConnectionStatusDisconnected) {
connected = false;
}
}
@Override
public void onDataMsg(AoqClientEngine.AoqDataMsg msg) {
try {
JSONObject event = new JSONObject(
new String(msg.data, StandardCharsets.UTF_8));
String eventName = event.optJSONObject("header") == null
? "" : event.optJSONObject("header").optString("event");
if ("task-started".equals(eventName)) {
sendContinueTask();
sendFinishTask();
} else if ("task-finished".equals(eventName)
|| "task-failed".equals(eventName)) {
taskActive = false;
}
} catch (JSONException e) {
throw new IllegalArgumentException("Invalid server event", e);
}
}
};
AoqClientEngine.AoqCreateConfig createConfig = new AoqClientEngine.AoqCreateConfig();
createConfig.workDir = context.getFilesDir().getAbsolutePath();
engine = AoqClientEngine.createEngine(context, createConfig, listener);
// 示例参数,应与 run-task 中的输出音频格式一致。
AoqClientEngine.AoqAudioCodecConfig audioDecoderConfig =
new AoqClientEngine.AoqAudioCodecConfig();
audioDecoderConfig.trackType = AoqClientEngine.AoqTrackType.AoqTrackTypeAudio;
audioDecoderConfig.codecType = AoqClientEngine.AoqEncoderType.AoqEncoderTypeAudioPCM;
audioDecoderConfig.sampleRate = 24000;
audioDecoderConfig.channel = 1;
engine.setAudioDecoderConfig(audioDecoderConfig);
AoqClientEngine.AoqAudioPlaybackConfig playbackConfig =
new AoqClientEngine.AoqAudioPlaybackConfig();
playbackConfig.channel = 1;
playbackConfig.isDefaultSpeaker = true;
engine.startAudioPlayer(playbackConfig);
AoqClientEngine.AoqTrackParam publishDataTrack =
new AoqClientEngine.AoqTrackParam();
publishDataTrack.trackType = AoqClientEngine.AoqTrackType.AoqTrackTypeData;
connectConfig.publishTracks.add(publishDataTrack);
AoqClientEngine.AoqTrackParam subscribeAudioTrack =
new AoqClientEngine.AoqTrackParam();
subscribeAudioTrack.trackType = AoqClientEngine.AoqTrackType.AoqTrackTypeAudio;
connectConfig.subscribeTracks.add(subscribeAudioTrack);
AoqClientEngine.AoqTrackParam subscribeDataTrack =
new AoqClientEngine.AoqTrackParam();
subscribeDataTrack.trackType = AoqClientEngine.AoqTrackType.AoqTrackTypeData;
connectConfig.subscribeTracks.add(subscribeDataTrack);
engine.connect(connectConfig);
}
public void synthesize(String text, String voice) {
if (!connected || taskActive) {
throw new IllegalStateException("The connection is not ready or a task is active.");
}
taskId = UUID.randomUUID().toString();
pendingText = text;
pendingVoice = voice;
taskActive = true;
sendRunTask();
}
private void sendRunTask() {
try {
JSONObject header = new JSONObject()
.put("action", "run-task")
.put("task_id", taskId)
.put("streaming", "duplex");
JSONObject parameters = new JSONObject()
.put("text_type", "PlainText")
.put("voice", pendingVoice)
.put("format", "pcm")
.put("sample_rate", 24000);
JSONObject payload = new JSONObject()
.put("task_group", "audio")
.put("task", "tts")
.put("function", "SpeechSynthesizer")
.put("model", "qwen-audio-3.0-tts-flash")
.put("input", new JSONObject())
.put("parameters", parameters);
JSONObject runTask = new JSONObject().put("header", header).put("payload", payload);
AoqClientEngine.AoqDataMsg dataMessage = new AoqClientEngine.AoqDataMsg();
dataMessage.data = runTask.toString().getBytes(StandardCharsets.UTF_8);
engine.sendDataMsg(dataMessage);
} catch (JSONException e) {
throw new IllegalStateException("Failed to create run-task", e);
}
}
private void sendContinueTask() {
try {
JSONObject header = new JSONObject()
.put("action", "continue-task")
.put("task_id", taskId)
.put("streaming", "duplex");
JSONObject payload = new JSONObject()
.put("input", new JSONObject().put("text", pendingText));
JSONObject continueTask = new JSONObject()
.put("header", header)
.put("payload", payload);
AoqClientEngine.AoqDataMsg dataMessage = new AoqClientEngine.AoqDataMsg();
dataMessage.data = continueTask.toString().getBytes(StandardCharsets.UTF_8);
engine.sendDataMsg(dataMessage);
} catch (JSONException e) {
throw new IllegalStateException("Failed to create continue-task", e);
}
}
private void sendFinishTask() {
try {
JSONObject header = new JSONObject()
.put("action", "finish-task")
.put("task_id", taskId)
.put("streaming", "duplex");
JSONObject finishTask = new JSONObject()
.put("header", header)
.put("payload", new JSONObject().put("input", new JSONObject()));
AoqClientEngine.AoqDataMsg dataMessage = new AoqClientEngine.AoqDataMsg();
dataMessage.data = finishTask.toString().getBytes(StandardCharsets.UTF_8);
engine.sendDataMsg(dataMessage);
} catch (JSONException e) {
throw new IllegalStateException("Failed to create finish-task", e);
}
}
public void close() {
engine.disconnect();
AoqClientEngine.destroy();
}
}