99import com .alibaba .dashscope .exception .InputRequiredException ;
1010import com .alibaba .dashscope .exception .NoApiKeyException ;
1111import com .alibaba .dashscope .protocol .*;
12+ import com .alibaba .dashscope .utils .JsonUtils ;
1213import com .google .gson .JsonObject ;
1314import io .reactivex .BackpressureStrategy ;
1415import 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