CosyVoice speech synthesis Java SDK

Updated at:
Copy as MD

Synthesize speech with CosyVoice using the DashScope Java SDK.

User guide: For model introduction and selection recommendations, see Speech synthesis.

Service endpoints

By default, the SDK connects to the Beijing region endpoint. To use a different region, set Constants.baseWebsocketApiUrl before initializing the SDK.

Singapore

wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference

Replace {WorkspaceId} with your actual workspace ID.

China (Beijing)

wss://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api-ws/v1/inference

Replace {WorkspaceId} with your actual workspace ID.

Switch to the Singapore region:

import com.alibaba.dashscope.utils.Constants;

// Set this before any SDK initialization
Constants.baseWebsocketApiUrl = "wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference";
Important

Alibaba Cloud Model Studio has released workspace-specific domains for the China (Beijing) and Singapore regions. The new dedicated domains deliver superior performance and higher stability for inference requests. We recommend migrating to the new domains:

  • China (Beijing): from dashscope.aliyuncs.com to {WorkspaceId}.cn-beijing.maas.aliyuncs.com

  • Singapore: from dashscope-intl.aliyuncs.com to {WorkspaceId}.ap-southeast-1.maas.aliyuncs.com

Replace {WorkspaceId} with your actual Workspace ID. The existing domains remain fully functional.

SpeechSynthesizer

Package: com.alibaba.dashscope.audio.ttsv2.SpeechSynthesizer

Constructor

public SpeechSynthesizer(SpeechSynthesisParam param, ResultCallback<SpeechSynthesisResult> callback)

Parameters:

  • param: Speech synthesis parameters, built with SpeechSynthesisParam.builder()

  • callback: Callback for streaming calls. Pass null for non-streaming calls.

call() - Non-streaming/unidirectional streaming synthesis

Method signature:

public ByteBuffer call(String text)

Parameters:

Parameter

Type

Required

Description

text

String

Yes

Text to synthesize. Maximum length: 20,000 characters.

Return value: ByteBuffer or null. For non-streaming calls, returns the complete audio data. For unidirectional streaming calls, this method returns null; audio is delivered through the callback.

streamingCall() - Bidirectional streaming synthesis

Method signature:

public void streamingCall(String text)

Parameters:

Parameter

Type

Required

Description

text

String

Yes

Text to synthesize. Maximum length: 20,000 characters. You can call this method multiple times to append text.

streamingComplete() - End bidirectional streaming

Method signature:

public void streamingComplete()

Ends the bidirectional streaming call and notifies the server that all text has been sent.

callAsFlowable() - Unidirectional streaming synthesis (reactive)

Method signature:

public Flowable<SpeechSynthesisResult> callAsFlowable(String text)

Parameters:

Parameter

Type

Required

Description

text

String

Yes

Text to synthesize.

Return value: Flowable<SpeechSynthesisResult> reactive stream.

streamingCallAsFlowable() - Bidirectional streaming synthesis (reactive)

Method signature:

public Flowable<SpeechSynthesisResult> streamingCallAsFlowable(Flowable<String> textStream)

Parameters:

Parameter

Type

Required

Description

textStream

Flowable<String>

Yes

Reactive stream of text.

Return value: Flowable<SpeechSynthesisResult> reactive stream.

getDuplexApi().close() - Close WebSocket connection

Method signature:

public boolean getDuplexApi().close(int code, String reason)

Parameters:

Parameter

Type

Required

Description

code

int

Yes

Close code.

reason

String

Yes

Close reason.

Return value: boolean. Returns true if the connection was closed successfully, false otherwise.

getLastRequestId() - Get request ID

Method signature:

public String getLastRequestId()

Return value: String, the request ID.

getFirstPackageDelay() - Get first-packet latency

Method signature:

public long getFirstPackageDelay()

Return value: long. The first-packet latency in milliseconds, measured from sending the first text segment to receiving the first audio packet.

SpeechSynthesisParam

Package: com.alibaba.dashscope.audio.ttsv2.SpeechSynthesisParam

Example:

SpeechSynthesisParam param = SpeechSynthesisParam.builder()
    .model("cosyvoice-v3-flash") // Model
    .voice("longanyang") // Voice
    .format(SpeechSynthesisAudioFormat.WAV_8000HZ_MONO_16BIT) // Audio encoding format and sample rate
    .volume(50) // Volume. Value range: [0, 100]
    .speechRate(1.0f) // Speech rate. Value range: [0.5, 2]
    .pitchRate(1.0f) // Pitch. Value range: [0.5, 2]
    .build();

Builder methods

Method

Parameter type

Required

Description

model(String)

String

Yes

The model name.

voice(String)

String

Yes

voice string (required)

The voice used for speech synthesis.

  • System voices: See CosyVoice Voice list

  • Cloned voices: Custom voices created through voice cloning

  • Custom voices: Custom voices created through voice design

format(SpeechSynthesisAudioFormat)

enum

No

Audio encoding format and sample rate.

Default: SpeechSynthesisAudioFormat.MP3_22050HZ_MONO_256KBPS.

SpeechSynthesisAudioFormat package: com.alibaba.dashscope.audio.ttsv2.SpeechSynthesisAudioFormat.

volume(int)

int

No

The volume level.

Default value: 50.

Valid values: [0, 100].

speechRate(float)

float

No

The speech rate.

Default value: 1.0.

Valid values: [0.5, 2.0].

pitchRate(float)

float

No

The pitch.

Default value: 1.0.

Valid values: [0.5, 2.0].

enableWordTimestamp(boolean)

boolean

No

Specifies whether to enable word-level timestamps.

Default value: false.

Available only in streaming output mode. Supported voices: cloned voices of cosyvoice-v3.5-plus, cosyvoice-v3.5-flash, cosyvoice-v3-flash, cosyvoice-v3-plus, and cosyvoice-v2, and system voices marked as supported in CosyVoice Voice list. Cloned voices of other models do not support this feature.

seed(int)

int

No

A random seed for controlling variation in the synthesis output. When the model version, text, voice, and other parameters are unchanged, using the same seed produces identical results.

Default value: 0.

Valid values: [0, 65535].

For SDK versions earlier than 2.21.7, set seed through additional parameters.

languageHints(List<String>)

List<String>

No

Important
  • This parameter is an array, but the current version only processes the first element. Pass a single value.

  • This parameter specifies the target language for speech synthesis. It's unrelated to the language of the audio sample used in voice cloning. To set the source language for a cloning task, see the voice cloning API reference.

Specifies the target language for speech synthesis to improve output quality.

When digit pronunciation, abbreviation expansion, symbol reading, or minority-language synthesis doesn't meet expectations, use this parameter. For example:

  • Unexpected digit pronunciation: "hello, this is 110" is read as "hello, this is one zero" instead of the expected Chinese pronunciation

  • Inaccurate symbol pronunciation: "@" is read as the Chinese equivalent instead of "at"

  • Poor minor language synthesis quality with unnatural results

Valid values:

  • zh: Chinese

  • en: English

  • fr: French

  • de: German

  • ja: Japanese

  • ko: Korean

  • ru: Russian

  • pt: Portuguese

  • th: Thai

  • id: Indonesian

  • vi: Vietnamese

instruction(String)

String

No

Controls synthesis characteristics such as dialect, emotion, or speaking style.

For usage details, see Instruction-based control.

hotFix(ParamHotFix)

ParamHotFix

No

Configures pronunciation corrections and text replacements applied before synthesis.

This feature isn't supported by cosyvoice-v2.

Parameters:

  • pronunciation: Custom pronunciation. Specifies pinyin annotations for words to correct inaccurate default pronunciations.

  • replace: Text replacement. Replaces specified words with target text before synthesis. The replaced text is used as the actual synthesis input.

Example:

List<ParamHotFix.PronunciationItem> pronunciationItems = new ArrayList<>();
pronunciationItems.add(new ParamHotFix.PronunciationItem("weather", "tian1 qi4"));

List<ParamHotFix.ReplaceItem> replaceItems = new ArrayList<>();
replaceItems.add(new ParamHotFix.ReplaceItem("today", "gold day"));

ParamHotFix paramHotFix = new ParamHotFix();
paramHotFix.setPronunciation(pronunciationItems);
paramHotFix.setReplace(replaceItems);

SpeechSynthesisParam param = SpeechSynthesisParam.builder()
                        .model("cosyvoice-v3-flash") // Model
                        .voice("your_voice") // Replace with a cosyvoice-v3-flash cloned voice
                        .hotFix(paramHotFix)
                        .build();

parameter(String key, Object value)

String, Object

No

Sets Additional parameters.

parameters(Map<String, Object>)

Map

No

Sets Additional parameters.

Additional parameters

Set through parameter() or parameters().

Example:

SpeechSynthesisParam param = SpeechSynthesisParam.builder()
  .model("cosyvoice-v3-flash")
  .voice("longanyang")
  .parameter("enable_markdown_filter", true)
  .build();

Parameter

Type

Required

Description

bit_rate

integer

No

The audio bit rate in kbps. When the audio format is opus, use bit_rate to adjust the bit rate.

Default value: 32.

Valid values: [6, 510].

enable_aigc_tag

boolean

No

Specifies whether to embed an AIGC watermark in the generated audio. When set to true, the watermark is embedded in audio files of supported formats (wav/mp3/opus).

Default value: false.

Only cosyvoice-v3-flash, cosyvoice-v3-plus, and cosyvoice-v2 support this feature.

aigc_propagator

String

No

Sets the ContentPropagator field in the AIGC watermark, identifying the content propagator. Takes effect only when enable_aigc_tag is true.

Default value: Alibaba Cloud UID.

Only cosyvoice-v3-flash, cosyvoice-v3-plus, and cosyvoice-v2 support this feature.

aigc_propagate_id

String

No

Sets the PropagateID field in the AIGC watermark, uniquely identifying a specific propagation action. Takes effect only when enable_aigc_tag is true.

Default value: The request ID of the current speech synthesis request.

Only cosyvoice-v3-flash, cosyvoice-v3-plus, and cosyvoice-v2 support this feature.

enable_markdown_filter

boolean

No

Important

Only cloned voices of cosyvoice-v3-flash support this feature.

Specifies whether to enable Markdown filtering. When enabled, the system automatically strips Markdown markup symbols from the input text before synthesis, preventing them from being read aloud.

Default value: false.

Valid values:

  • true: Enable Markdown filtering

  • false: Disable Markdown filtering

ResultCallback

Package: com.alibaba.dashscope.common.ResultCallback

onEvent() - Receive audio data

Method signature:

public void onEvent(SpeechSynthesisResult result)

Parameters:

Parameter

Type

Required

Description

result

SpeechSynthesisResult

Yes

Triggered when a synthesis event is received. Contains the audio frame, timestamp information, and output information (event type, original text, etc.).

onComplete() - Synthesis complete

Method signature:

public void onComplete()

Triggered when speech synthesis completes.

onError() - Error handling

Method signature:

public void onError(Exception e)

Parameters:

Parameter

Type

Required

Description

e

Exception

Yes

Triggered when an error occurs. Contains the exception information.

SpeechSynthesisResult

Package: com.alibaba.dashscope.audio.tts.SpeechSynthesisResult

getAudioFrame() - Get audio data frame

Method signature:

public ByteBuffer getAudioFrame()

Return value: ByteBuffer, the audio data frame.

getTimestamp() - Get timestamp information

Method signature:

public Sentence getTimestamp()

Return value: Sentence, the timestamp information.

getOutput() - Get output information

Method signature:

public JsonObject getOutput()

Return value: com.google.gson.JsonObject, the output information of the synthesis event, containing event type and text content. Requires SDK version >= 2.22.0.

Sentence-level timestamp information (Sentence)

Sentence encapsulates sentence-level timestamp information.

getBeginTime() - Get sentence start time

Method signature:

public int getBeginTime()

Return value: Sentence start time in milliseconds.

getEndTime() - Get sentence end time

Method signature:

public int getEndTime()

Return value: Sentence end time in milliseconds.

getWords() - Get word-level timestamps

Method signature:

public List<Word> getWords()

Return value: A List of Word objects containing word-level timestamp information. May be empty.

Word-level timestamp information (Word)

Word encapsulates word-level timestamp information.

getBeginTime() - Get word start time

Method signature:

public int getBeginTime()

Return value: Word start time in milliseconds.

getEndTime() - Get word end time

Method signature:

public int getEndTime()

Return value: Word end time in milliseconds.

getText() - Get text

Method signature:

public String getText()

Return value: String, the text content.

getPhonemes() - Get phoneme-level timestamps

Method signature:

public List<Phoneme> getPhonemes()

Return value: A List of Phoneme objects containing phoneme-level timestamp information. May be empty.

Phoneme-level timestamp information (Phoneme)

Phoneme encapsulates phoneme-level timestamp information.

getBeginTime() - Get phoneme start time

Method signature:

public int getBeginTime()

Return value: Phoneme start time in milliseconds.

getEndTime() - Get phoneme end time

Method signature:

public int getEndTime()

Return value: Phoneme end time in milliseconds.

getText() - Get text

Method signature:

public String getText()

Return value: String, the text content.

getTone() - Get tone

Method signature:

public int getTone()

Return value: The tone value.

  • In English, 0, 1, and 2 represent unstressed, primary stress, and secondary stress respectively.

  • In Chinese pinyin, 1, 2, 3, 4, and 5 represent the first, second, third, fourth, and neutral tones respectively.

Output information (output)

getOutput() returns a JsonObject that encapsulates synthesis event output information. Retrieve it in the onEvent callback or the Flowable stream. It contains the following fields:

Field

Type

Description

type

String

Event type. Possible values: sentence-begin (sentence start; returns the text to be synthesized), sentence-synthesis (audio synthesis in progress; returns an audio data chunk), sentence-end (sentence end; returns text content and word-level timestamps).

original_text

String

Original text of the current sentence. Returned in sentence-begin and sentence-end events.

sentence

JsonObject

Sentence information, containing the sentence index (index) and word-level timestamps (words). The sentence-end event includes full word-level timestamp information.

Sample code

The SDK supports the following synthesis modes:

  • Non-streaming: A blocking call that sends the complete text at once and returns the full audio directly. Best suited for short-text speech synthesis.

  • Unidirectional streaming: A non-blocking call that sends the complete text at once and delivers audio data (potentially in chunks) through a callback function. Best suited for short-text scenarios that require low latency.

  • Bidirectional streaming: A non-blocking call that sends text in multiple segments and delivers incrementally synthesized audio through a callback function in real time. Best suited for long-text scenarios that require low latency.

Non-streaming calls

image

The text length per request must not exceed 20,000 characters.

Important

Reinitialize the SpeechSynthesizer instance before each call to the call method.

import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesisParam;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesizer;
import com.alibaba.dashscope.utils.Constants;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;

public class Main {
    // Model
    private static String model = "cosyvoice-v3-flash";
    // Voice
    private static String voice = "longanyang";

    public static void streamAudioDataToSpeaker() {
        // Request parameters
        SpeechSynthesisParam param =
                SpeechSynthesisParam.builder()
                        // The API Keys for the Singapore and Beijing regions are different. Get an API Key: https://www.alibabacloud.com/help/en/model-studio/get-api-key
                        // If the environment variable is not configured, replace the following line with your Model Studio API Key: .apiKey("sk-xxx")
                        .apiKey(System.getenv("DASHSCOPE_API_KEY"))
                        .model(model) // Model
                        .voice(voice) // Voice
                        .build();

        // Synchronous mode: disable callback (second parameter is null)
        SpeechSynthesizer synthesizer = new SpeechSynthesizer(param, null);
        ByteBuffer audio = null;
        try {
            // Block until audio is returned
            audio = synthesizer.call("What's the weather like today?");
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            // Close the WebSocket connection after the task is completed
            synthesizer.getDuplexApi().close(1000, "bye");
        }
        if (audio != null) {
            // Save the audio data to a local file "output.mp3"
            File file = new File("output.mp3");
            // The first text transmission requires establishing a WebSocket connection, so the first packet latency includes the connection setup time
            System.out.println(
                    "[Metric] requestId: "
                            + synthesizer.getLastRequestId()
                            + ", first packet latency (ms): "
                            + synthesizer.getFirstPackageDelay());
            try (FileOutputStream fos = new FileOutputStream(file)) {
                fos.write(audio.array());
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public static void main(String[] args) {
        // The following configuration is for the Singapore region. Replace "{WorkspaceId}" with your actual workspace ID. The configuration varies by region.
        Constants.baseWebsocketApiUrl = "wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference";
        streamAudioDataToSpeaker();
        System.exit(0);
    }
}

Unidirectional streaming calls

image

The text length per request must not exceed 20,000 characters.

Important

Reinitialize the SpeechSynthesizer instance before each call to the call method.

import com.alibaba.dashscope.audio.tts.SpeechSynthesisResult;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesisParam;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesizer;
import com.alibaba.dashscope.common.ResultCallback;
import com.alibaba.dashscope.utils.Constants;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.CountDownLatch;

class TimeUtils {
    private static final DateTimeFormatter formatter =
            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");

    public static String getTimestamp() {
        return LocalDateTime.now().format(formatter);
    }
}

public class Main {
    // Model
    private static String model = "cosyvoice-v3-flash";
    // Voice
    private static String voice = "longanyang";

    public static void streamAudioDataToSpeaker() {
        CountDownLatch latch = new CountDownLatch(1);

        // Implement the ResultCallback interface
        ResultCallback<SpeechSynthesisResult> callback = new ResultCallback<SpeechSynthesisResult>() {
            @Override
            public void onEvent(SpeechSynthesisResult result) {
                if (result.getAudioFrame() != null) {
                    // Implement the logic to save audio data locally here
                    System.out.println(TimeUtils.getTimestamp() + " Audio received");
                }
                // Get output information, including event type and original text
                if (result.getOutput() != null && result.getOutput().has("type")) {
                    System.out.println("Event type: " + result.getOutput().get("type").getAsString()
                            + ", Original text: " + (result.getOutput().has("original_text") ? result.getOutput().get("original_text").getAsString() : ""));
                }
            }

            @Override
            public void onComplete() {
                System.out.println(TimeUtils.getTimestamp() + " Complete received, speech synthesis finished");
                latch.countDown();
            }

            @Override
            public void onError(Exception e) {
                System.out.println("Exception occurred: " + e.toString());
                latch.countDown();
            }
        };

        // Request parameters
        SpeechSynthesisParam param =
                SpeechSynthesisParam.builder()
                        // The API Keys for the Singapore and Beijing regions are different. Get an API Key: https://www.alibabacloud.com/help/en/model-studio/get-api-key
                        // If the environment variable is not configured, replace the following line with your Model Studio API Key: .apiKey("sk-xxx")
                        .apiKey(System.getenv("DASHSCOPE_API_KEY"))
                        .model(model) // Model
                        .voice(voice) // Voice
                        .build();
        // Passing "callback" as the second parameter enables asynchronous mode
        SpeechSynthesizer synthesizer = new SpeechSynthesizer(param, callback);
        // Non-blocking call that returns null immediately (actual results are delivered asynchronously via the callback interface). Binary audio is received in real time through the onEvent method of the callback interface
        try {
            synthesizer.call("What's the weather like today?");
            // Wait for synthesis to complete
            latch.await();
            // Wait for playback thread to finish playing
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            // Close the WebSocket connection after the task is completed
            synthesizer.getDuplexApi().close(1000, "bye");
        }
        // The first text transmission requires establishing a WebSocket connection, so the first packet latency includes the connection setup time
        System.out.println(
                "[Metric] requestId: "
                        + synthesizer.getLastRequestId()
                        + ", first packet latency (ms): "
                        + synthesizer.getFirstPackageDelay());
    }

    public static void main(String[] args) {
        // The following configuration is for the Singapore region. Replace "{WorkspaceId}" with your actual workspace ID. The configuration varies by region.
        Constants.baseWebsocketApiUrl = "wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference";
        streamAudioDataToSpeaker();
        System.exit(0);
    }
}

Bidirectional streaming calls

image

The text length per individual call must not exceed 20,000 characters, and the cumulative text length across all calls must not exceed 200,000 characters.

  • During streaming input, call streamingCall multiple times to submit text segments in order. The server automatically splits received text into sentences:

    • Complete sentences are synthesized immediately.

    • Incomplete sentences are buffered until they form a complete sentence.

    When streamingComplete is called, the server force-synthesizes all received but unprocessed text segments, including incomplete sentences.

  • The interval between consecutive text segments must not exceed 23 seconds; exceeding this limit triggers a "request timeout after 23 seconds" exception.

    If no text is pending, call streamingComplete promptly to end the task.

    Important

    Always call the streamingComplete method. Otherwise, trailing text segments may not be converted to speech.

    The server enforces a 23-second timeout that cannot be changed on the client side.
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.ResultCallback;
import com.alibaba.dashscope.utils.Constants;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

class TimeUtils {
    private static final DateTimeFormatter formatter =
            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");

    public static String getTimestamp() {
        return LocalDateTime.now().format(formatter);
    }
}

public class Main {
    private static String[] textArray = {"Streaming text-to-speech SDK, ",
            "can convert input text ", "into audio binary data. ", "Compared to non-streaming speech synthesis, ",
            "streaming synthesis offers better real-time performance. ", "While users input text, ",
            "they can hear nearly synchronous audio output, ", "greatly enhancing the interactive experience ",
            "and reducing user wait time. ", "It is suitable for calling large-scale ", "language models (LLMs) to ",
            "perform speech synthesis ", "with streaming text input."};
    private static String model = "cosyvoice-v3-flash"; // Model
    private static String voice = "longanyang"; // Voice

    public static void streamAudioDataToSpeaker() {
        // Configure the callback function
        ResultCallback<SpeechSynthesisResult> callback = new ResultCallback<SpeechSynthesisResult>() {
            @Override
            public void onEvent(SpeechSynthesisResult result) {
                // System.out.println("Message received: " + result);
                if (result.getAudioFrame() != null) {
                    // Implement the logic to process audio data here
                    System.out.println(TimeUtils.getTimestamp() + " Audio received");
                }
            }

            @Override
            public void onComplete() {
                System.out.println(TimeUtils.getTimestamp() + " Complete received, speech synthesis finished");
            }

            @Override
            public void onError(Exception e) {
                System.out.println("Exception occurred: " + e.toString());
            }
        };

        // Request parameters
        SpeechSynthesisParam param =
                SpeechSynthesisParam.builder()
                        // The API Keys for the Singapore and Beijing regions are different. Get an API Key: https://www.alibabacloud.com/help/en/model-studio/get-api-key
                        // If the environment variable is not configured, replace the following line with your Model Studio API Key: .apiKey("sk-xxx")
                        .apiKey(System.getenv("DASHSCOPE_API_KEY"))
                        .model(model)
                        .voice(voice)
                        .format(SpeechSynthesisAudioFormat
                                .PCM_22050HZ_MONO_16BIT) // Use PCM or MP3 for streaming synthesis
                        .build();
        SpeechSynthesizer synthesizer = new SpeechSynthesizer(param, callback);
        // The call method with Callback will not block the current thread
        try {
            for (String text : textArray) {
                // Send text fragments and receive binary audio in real time through the onEvent method of the callback interface
                synthesizer.streamingCall(text);
            }
            // Wait for streaming speech synthesis to finish
            synthesizer.streamingComplete();
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            // Close the WebSocket connection after the task is completed
            synthesizer.getDuplexApi().close(1000, "bye");
        }

        // The first text transmission requires establishing a WebSocket connection, so the first packet latency includes the connection setup time
        System.out.println(
                "[Metric] requestId: "
                        + synthesizer.getLastRequestId()
                        + ", first packet latency (ms): "
                        + synthesizer.getFirstPackageDelay());
    }

    public static void main(String[] args) {
        // The following configuration is for the Singapore region. Replace "{WorkspaceId}" with your actual workspace ID. The configuration varies by region.
        Constants.baseWebsocketApiUrl = "wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference";
        streamAudioDataToSpeaker();
        System.exit(0);
    }
}

Flowable-based calls

Flowable is an RxJava type representing a reactive stream that supports backpressure. For more information, see RxJava Flowable documentation.

Before using Flowable, make sure the RxJava library is integrated and you understand the basics of reactive programming.

The text length per individual call must not exceed 20,000 characters, and the cumulative text length across all calls must not exceed 200,000 characters.

Unidirectional streaming calls

The following example shows how to use the blockingForEach interface of a Flowable object to retrieve each streamed SpeechSynthesisResult object in a blocking manner.

import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesisParam;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesizer;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.alibaba.dashscope.utils.Constants;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

class TimeUtils {
    private static final DateTimeFormatter formatter =
            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");

    public static String getTimestamp() {
        return LocalDateTime.now().format(formatter);
    }
}

public class Main {
    private static String model = "cosyvoice-v3-flash"; // Model
    private static String voice = "longanyang"; // Voice

    public static void streamAudioDataToSpeaker() throws NoApiKeyException {
        // Request parameters
        SpeechSynthesisParam param =
                SpeechSynthesisParam.builder()
                        // The API Keys for the Singapore and Beijing regions are different. Get an API Key: https://www.alibabacloud.com/help/en/model-studio/get-api-key
                        // If the environment variable is not configured, replace the following line with your Model Studio API Key: .apiKey("sk-xxx")
                        .apiKey(System.getenv("DASHSCOPE_API_KEY"))
                        .model(model) // Model
                        .voice(voice) // Voice
                        .build();
        SpeechSynthesizer synthesizer = new SpeechSynthesizer(param, null);
        synthesizer.callAsFlowable("What's the weather like today?").blockingForEach(result -> {
            if (result.getAudioFrame() != null) {
                // Implement the logic to process audio data here
                System.out.println(TimeUtils.getTimestamp() + " Audio received");
            }
            // Get output information, including event type and original text
            if (result.getOutput() != null && result.getOutput().has("type")) {
                System.out.println("Event type: " + result.getOutput().get("type").getAsString()
                        + ", Original text: " + (result.getOutput().has("original_text") ? result.getOutput().get("original_text").getAsString() : ""));
            }
        });
        // Close the WebSocket connection after the task is completed
        synthesizer.getDuplexApi().close(1000, "bye");
        // The first text transmission requires establishing a WebSocket connection, so the first packet latency includes the connection setup time
        System.out.println(
                "[Metric] requestId: "
                        + synthesizer.getLastRequestId()
                        + ", first packet latency (ms): "
                        + synthesizer.getFirstPackageDelay());
    }

    public static void main(String[] args) throws NoApiKeyException {
        // The following configuration is for the Singapore region. Replace "{WorkspaceId}" with your actual workspace ID. The configuration varies by region.
        Constants.baseWebsocketApiUrl = "wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference";
        streamAudioDataToSpeaker();
        System.exit(0);
    }
}

Bidirectional streaming calls

The following example shows how to use a Flowable object as an input parameter for text streaming, and use the blockingForEach interface of the returned Flowable object to retrieve each streamed SpeechSynthesisResult object in a blocking manner.

import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesisParam;
import com.alibaba.dashscope.audio.ttsv2.SpeechSynthesizer;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.alibaba.dashscope.utils.Constants;
import io.reactivex.BackpressureStrategy;
import io.reactivex.Flowable;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

class TimeUtils {
    private static final DateTimeFormatter formatter =
            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");

    public static String getTimestamp() {
        return LocalDateTime.now().format(formatter);
    }
}

public class Main {
    private static String[] textArray = {"Streaming text-to-speech SDK, ",
            "can convert input text ", "into audio binary data. ", "Compared to non-streaming speech synthesis, ",
            "streaming synthesis offers better real-time performance. ", "While users input text, ",
            "they can hear nearly synchronous audio output, ", "greatly enhancing the interactive experience ",
            "and reducing user wait time. ", "It is suitable for calling large-scale ", "language models (LLMs) to ",
            "perform speech synthesis ", "with streaming text input."};
    private static String model = "cosyvoice-v3-flash";
    private static String voice = "longanyang";

    public static void streamAudioDataToSpeaker() throws NoApiKeyException {
        // Simulate streaming input
        Flowable<String> textSource = Flowable.create(emitter -> {
            new Thread(() -> {
                for (int i = 0; i < textArray.length; i++) {
                    emitter.onNext(textArray[i]);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
                emitter.onComplete();
            }).start();
        }, BackpressureStrategy.BUFFER);

        // Request parameters
        SpeechSynthesisParam param =
                SpeechSynthesisParam.builder()
                        // The API Keys for the Singapore and Beijing regions are different. Get an API Key: https://www.alibabacloud.com/help/en/model-studio/get-api-key
                        // If the environment variable is not configured, replace the following line with your Model Studio API Key: .apiKey("sk-xxx")
                        .apiKey(System.getenv("DASHSCOPE_API_KEY"))
                        .model(model) // Model
                        .voice(voice) // Voice
                        .build();
        SpeechSynthesizer synthesizer = new SpeechSynthesizer(param, null);
        synthesizer.streamingCallAsFlowable(textSource).blockingForEach(result -> {
            if (result.getAudioFrame() != null) {
                // Implement the logic to play audio here
                System.out.println(
                        TimeUtils.getTimestamp() +
                                " Binary audio size: " + result.getAudioFrame().capacity());
            }
            // Get output information, including event type and original text
            if (result.getOutput() != null && result.getOutput().has("type")) {
                System.out.println("Event type: " + result.getOutput().get("type").getAsString()
                        + ", Original text: " + (result.getOutput().has("original_text") ? result.getOutput().get("original_text").getAsString() : ""));
            }
        });
        synthesizer.getDuplexApi().close(1000, "bye");
        // The first text transmission requires establishing a WebSocket connection, so the first packet latency includes the connection setup time
        System.out.println(
                "[Metric] requestId: "
                        + synthesizer.getLastRequestId()
                        + ", first packet latency (ms): "
                        + synthesizer.getFirstPackageDelay());
    }

    public static void main(String[] args) throws NoApiKeyException {
        // The following configuration is for the Singapore region. Replace "{WorkspaceId}" with your actual workspace ID. The configuration varies by region.
        Constants.baseWebsocketApiUrl = "wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference";
        streamAudioDataToSpeaker();
        System.exit(0);
    }
}

High-concurrency calls

The DashScope Java SDK uses OkHttp3 connection pooling to reduce the overhead of repeatedly establishing connections. For more information, see High-concurrency best practices.