Skip to content

Commit 6ffb111

Browse files
committed
(model/cosyvoice): support flush api
1 parent 77f9020 commit 6ffb111

1 file changed

Lines changed: 69 additions & 15 deletions

File tree

src/main/java/com/alibaba/dashscope/audio/ttsv2/SpeechSynthesizer.java

Lines changed: 69 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import com.alibaba.dashscope.exception.InputRequiredException;
1010
import com.alibaba.dashscope.exception.NoApiKeyException;
1111
import com.alibaba.dashscope.protocol.*;
12+
import com.alibaba.dashscope.utils.JsonUtils;
1213
import com.google.gson.JsonObject;
1314
import io.reactivex.BackpressureStrategy;
1415
import io.reactivex.Emitter;
@@ -44,7 +45,7 @@ public final class SpeechSynthesizer {
4445
SynchronizeFullDuplexApi<SpeechSynthesisParam> duplexApi;
4546

4647
private ApiServiceOption serviceOption;
47-
private Emitter<String> textEmitter;
48+
private Emitter<TextStreamItem> textEmitter;
4849
private ResultCallback<SpeechSynthesisResult> callback;
4950
private SpeechSynthesisState state = SpeechSynthesisState.IDLE;
5051

@@ -235,10 +236,11 @@ public Flowable<SpeechSynthesisResult> streamingCallAsFlowable(Flowable<String>
235236
startStreamTimeStamp = System.currentTimeMillis();
236237
recvAudioLength = 0;
237238
preRequestId = UUID.randomUUID().toString();
239+
Flowable<TextStreamItem> inputStream = textStream.map(text -> new TextStreamItem(text));
238240
return duplexApi
239241
.duplexCall(
240242
StreamInputTtsParamWithStream.fromStreamInputTtsParam(
241-
this.parameters, textStream, preRequestId, false))
243+
this.parameters, inputStream, preRequestId, false))
242244
.filter(item -> item.getEvent() != WebSocketEventType.TASK_STARTED.getValue())
243245
.map(SpeechSynthesisResult::fromDashScopeResult)
244246
.filter(item -> !canceled.get())
@@ -284,7 +286,7 @@ public Flowable<SpeechSynthesisResult> callAsFlowable(String text)
284286
emitter -> {
285287
new Thread(
286288
() -> {
287-
emitter.onNext(text);
289+
emitter.onNext(new TextStreamItem(text));
288290
emitter.onComplete();
289291
})
290292
.start();
@@ -336,7 +338,7 @@ private void startStream(boolean enableSsml) {
336338
// timestamps.clear();
337339
WritableByteChannel channel = Channels.newChannel(outputStream);
338340

339-
Flowable<String> textFrames =
341+
Flowable<TextStreamItem> textFrames =
340342
Flowable.create(
341343
emitter -> {
342344
synchronized (SpeechSynthesizer.this) {
@@ -346,7 +348,7 @@ private void startStream(boolean enableSsml) {
346348
emitter.onComplete();
347349
return;
348350
} else {
349-
emitter.onNext(buffer.text);
351+
emitter.onNext(buffer.item);
350352
}
351353
}
352354
cmdBuffer.clear();
@@ -496,7 +498,7 @@ public void onError(Exception e) {
496498
* @param text utf-8 encoded text
497499
*/
498500
private void submitText(String text) {
499-
if (Objects.equals(text, "")) {
501+
if (text == null || text.isEmpty()) {
500502
throw new ApiException(new InputRequiredException("Parameter invalid: text is null"));
501503
}
502504
synchronized (this) {
@@ -507,10 +509,10 @@ private void submitText(String text) {
507509
}
508510
if (textEmitter == null) {
509511
log.debug("submitText to new emitter: " + text);
510-
cmdBuffer.add(AsyncCmdBuffer.builder().text(text).build());
512+
cmdBuffer.add(AsyncCmdBuffer.builder().item(new TextStreamItem(text)).build());
511513
} else {
512514
log.debug("submitText to emitter: " + text);
513-
textEmitter.onNext(text);
515+
textEmitter.onNext(new TextStreamItem(text));
514516
}
515517
}
516518
}
@@ -660,6 +662,27 @@ public void streamingCall(String text) {
660662
}
661663
}
662664

665+
public void streamingFlush() {
666+
streamingFlush(null);
667+
}
668+
669+
public void streamingFlush(JsonObject params) {
670+
synchronized (this) {
671+
if (state != SpeechSynthesisState.TTS_STARTED) {
672+
throw new ApiException(
673+
new InputRequiredException(
674+
"State invalid: expect stream input tts state is started but " + state.getValue()));
675+
}
676+
if (textEmitter == null) {
677+
log.debug("submitFlush to new emitter");
678+
cmdBuffer.add(AsyncCmdBuffer.builder().item(new TextStreamItem(true, params)).build());
679+
} else {
680+
log.debug("submitFlush to emitter");
681+
textEmitter.onNext(new TextStreamItem(true, params));
682+
}
683+
}
684+
}
685+
663686
/**
664687
* Speech synthesis If a callback is set, the audio will be returned in real-time through the
665688
* on_event interface Otherwise, this function blocks until all audio is received and then returns
@@ -750,17 +773,39 @@ public ByteBuffer call(String text) {
750773
@SuperBuilder
751774
private static class AsyncCmdBuffer {
752775
@Builder.Default private boolean isStop = false;
753-
private String text;
776+
private TextStreamItem item;
777+
}
778+
779+
private static class TextStreamItem {
780+
public TextStreamItem(String text) {
781+
this.text = text;
782+
this.flush = false;
783+
}
784+
785+
public TextStreamItem(boolean flush) {
786+
this.text = null;
787+
this.flush = flush;
788+
}
789+
790+
public TextStreamItem(boolean flush, JsonObject params) {
791+
this.text = null;
792+
this.flush = flush;
793+
this.params = params;
794+
}
795+
796+
public String text;
797+
public boolean flush;
798+
public JsonObject params;
754799
}
755800

756801
@SuperBuilder
757802
private static class StreamInputTtsParamWithStream extends SpeechSynthesisParam {
758803

759-
@NonNull private Flowable<String> textStream;
804+
@NonNull private Flowable<TextStreamItem> textStream;
760805

761806
public static StreamInputTtsParamWithStream fromStreamInputTtsParam(
762807
SpeechSynthesisParam param,
763-
Flowable<String> textStream,
808+
Flowable<TextStreamItem> textStream,
764809
String preRequestId,
765810
boolean enableSsml) {
766811
return StreamInputTtsParamWithStream.builder()
@@ -780,10 +825,19 @@ public static StreamInputTtsParamWithStream fromStreamInputTtsParam(
780825
public Flowable<Object> getStreamingData() {
781826
return textStream
782827
.map(
783-
text -> {
784-
JsonObject jsonObject = new JsonObject();
785-
jsonObject.addProperty("text", text);
786-
return jsonObject;
828+
item -> {
829+
if (!item.flush) {
830+
JsonObject jsonObject = new JsonObject();
831+
jsonObject.addProperty("text", item.text);
832+
return jsonObject;
833+
} else {
834+
JsonObject jsonObject = new JsonObject();
835+
jsonObject.addProperty("flush", true);
836+
if (item.params != null) {
837+
JsonUtils.merge(jsonObject, item.params);
838+
}
839+
return jsonObject;
840+
}
787841
})
788842
.cast(Object.class);
789843
}

0 commit comments

Comments
 (0)