Real-time speech synthesis

Updated at:

Learn about real-time speech synthesis features, streaming calls, and ways to improve synthesis quality.

Features

Real-time speech synthesis accepts text segments and streams audio back, enabling speech output as a large language model generates text.

  • Languages and dialects: Select a model and voice for the target language, or use an instruction to specify a dialect. See Dialects.
  • Expressive speech: Adjust delivery with natural-language instructions, or insert emotion and nonverbal tags into the text. See Instruction control and Emotion and nonverbal tags.
  • Custom voices: Create a voice from an audio sample with voice cloning, or from a text description with voice design.
  • Fine-grained control and formulas: Use SSML and LaTeX to control pauses and pronunciation, or convert formulas to speech.
  • Audio controls: Adjust speed, pitch, volume, and bitrate, and output common audio formats such as PCM, WAV, MP3, and Opus at sample rates up to 48 kHz. See the corresponding API parameters for supported values and conditions.

NoteSupported features vary by model and voice. Check the scope in the corresponding feature documentation.

To submit complete text in one HTTP request, see Non-real-time speech synthesis.

Get started

NoteMobile applications that require low latency and stable audio transmission on weak networks can use AOQ. For AOQ access, protocol differences, and model support across AOQ, WebRTC, and WebSocket, see Realtime API overview.

CosyVoice

This example uses cosyvoice-v3-flash and the longanyang system voice in Beijing. It sends text in segments and saves the streamed audio to output.mp3.

import os
import dashscope
from dashscope.audio.tts_v2 import SpeechSynthesizer, ResultCallback, AudioFormat

dashscope.api_key = os.environ["DASHSCOPE_API_KEY"]
dashscope.base_websocket_api_url = "wss://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api-ws/v1/inference"

with open("output.mp3", "wb") as audio:
    class Callback(ResultCallback):
        def on_data(self, data: bytes):
            audio.write(data)

        def on_error(self, message):
            print(f"Synthesis failed: {message}")

    synthesizer = SpeechSynthesizer(
        model="cosyvoice-v3-flash",
        voice="longanyang",
        format=AudioFormat.MP3_24000HZ_MONO_256KBPS,
        callback=Callback(),
    )
    for text in ["你好,欢迎使用阿里云百炼。", "文本可以分段发送,音频会逐段返回。"]:
        synthesizer.streaming_call(text)
    synthesizer.streaming_complete()
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.io.FileOutputStream;
import java.nio.ByteBuffer;

public class Main {
    public static void main(String[] args) throws Exception {
        Constants.baseWebsocketApiUrl = "wss://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api-ws/v1/inference";
        try (FileOutputStream audio = new FileOutputStream("output.mp3")) {
            SpeechSynthesisParam param = SpeechSynthesisParam.builder()
                .apiKey(System.getenv("DASHSCOPE_API_KEY"))
                .model("cosyvoice-v3-flash")
                .voice("longanyang")
                .format(SpeechSynthesisAudioFormat.MP3_24000HZ_MONO_256KBPS)
                .build();
            ResultCallback<SpeechSynthesisResult> callback = new ResultCallback<>() {
                @Override
                public void onEvent(SpeechSynthesisResult result) {
                    ByteBuffer buffer = result.getAudioFrame();
                    if (buffer != null) {
                        byte[] bytes = new byte[buffer.remaining()];
                        buffer.get(bytes);
                        try { audio.write(bytes); }
                        catch (Exception e) { throw new RuntimeException(e); }
                    }
                }
                @Override
                public void onComplete() {}
                public void onError(Exception e) { e.printStackTrace(); }
            };
            SpeechSynthesizer synthesizer = new SpeechSynthesizer(param, callback);
            try {
                for (String text : new String[]{"你好,欢迎使用阿里云百炼。", "文本可以分段发送,音频会逐段返回。"}) {
                    synthesizer.streamingCall(text);
                }
                synthesizer.streamingComplete(60000);
            } finally {
                synthesizer.getDuplexApi().close(1000, "done");
            }
        }
    }
}

Improve synthesis quality

Languages and pronunciation

Choose an approach based on what you need to control:

RequirementApproach

Set the target language of synthesized speech

Use language_hints to specify the overall synthesis language, guide the pronunciation of numbers, abbreviations, and symbols, and improve synthesis in less widely used languages.

Correct specific words or replace text

Use hot_fix to assign pinyin to specific words or replace words before synthesis. This is useful for consistent corrections to names and other local text.

Control pauses, number reading, or pronunciation precisely

Mark the relevant text with SSML tags to control pauses, reading rules, and pronunciation.

Read mathematical formulas

Use the CosyVoice LaTeX feature to convert supported mathematical formulas into Chinese speech.

For supported models and parameter configuration, see the Qwen-Audio-TTS API or CosyVoice API. For the models, voices, interfaces, and usage of SSML and LaTeX, see SSML and LaTeX.

NoteWhen using SSML, submit the complete SSML text in a single input and receive audio incrementally. Fragmented text input is not supported.

Instruction control

Use instruction to describe the speed, emotion, or speaking style in natural language and control the synthesized speech.

NoteTo create a new voice with a specific gender, age, or vocal character, or for guidance and examples on writing voice descriptions, see voice design.

Qwen-Audio-TTS

System, cloned, and designed voices for qwen-audio-3.1-tts-flash, qwen-audio-3.0-tts-plus, and qwen-audio-3.0-tts-flash all support natural-language instructions through instruction in synthesis requests.

For example, set instruction to the following text to slow down speech:

请用非常缓慢的语速说话,每个字都慢慢说。

CosyVoice

NoteFor CosyVoice, instruction must not exceed 100 characters. Chinese characters, including simplified and traditional characters and Chinese characters used in Japanese and Korean, count as two characters each. Other characters, including punctuation, letters, digits, kana, and Hangul, count as one each.

ModelApplicable voicesInstruction requirementsInstruction text languages

cosyvoice-v3.5-plus, cosyvoice-v3.5-flash

Cloned and designed voices

No system voices.

Natural-language instructions.

Chinese, English, French, German, Japanese, Korean, Russian, Portuguese, Thai, Indonesian, and Vietnamese.

cosyvoice-v3-plus

System voices

Use the fixed format and content in the voice list.

Cloned and designed voices do not support instruction control.

Use the language specified in the voice list.

cosyvoice-v3-flash

System voices

Use the fixed format and content in the voice list.

Chinese.

Cloned and designed voices

Natural-language instructions.

Chinese, English, French, German, Japanese, Korean, and Russian.

Dialects

Use dialect synthesis to generate speech in Chinese dialects such as Henan or Sichuan dialect. How you select a dialect depends on the model and voice type.

Qwen-Audio-TTS

  • System voices: Select a voice from the Qwen-Audio-TTS voice list that supports the dialect. A voice that directly supports the dialect requires no additional setting. For voices that support dialect instructions, use instruction.
  • Cloned voices: Specify a dialect with instruction, such as 请用河南话表达。.

CosyVoice

  • System voices: Select a voice that directly supports the dialect, such as longshange_v3, or a voice that supports dialect instructions, such as longanhuan_v3. Follow the format in the voice list.
  • Cloned voices: Use instruction with a model that supports instruction control to specify a dialect.
  • Designed voices: Dialects are not supported.

In the CosyVoice Python getting-started example, replace the initialization and text submission code with the following to synthesize Henan dialect speech using longanhuan_v3:

synthesizer = SpeechSynthesizer(
    model="cosyvoice-v3-flash",
    voice="longanhuan_v3",
    instruction="请用河南话表达。",
    format=AudioFormat.MP3_24000HZ_MONO_256KBPS,
    callback=Callback(),
)
synthesizer.streaming_call("叫你去买盐,你买回来一袋面,这不是弄啥嘞吗!")
synthesizer.streaming_complete()

For Qwen3-TTS, select a voice that supports the target dialect from the Qwen3-TTS voice list.

Emotion and nonverbal tags

NoteApplies to qwen-audio-3.1-tts-flash, qwen-audio-3.0-tts-plus, and qwen-audio-3.0-tts-flash.

Emotion and paralinguistic tags control the emotion and style of speech and insert sounds such as laughter or sighs at specific positions. Write the tags directly in the text to synthesize, text, without additional parameters.

Usage examples

This text starts with an excited delivery, inserts laughter, and then continues speaking:

[excited]今天的天气真不错![laughing]我们一起出去玩吧!

This text switches to an excited delivery in the second sentence:

[serious]请注意安全事项。[excited]好了,现在让我们开始吧!

Supported tags

Control tags

A control tag sets the emotion or style of the following text until the next control tag. When long sentences are automatically segmented, the effect is bounded by the resulting text segment.

TagDescription

[sad]

Sad

[amazed]

Amazed

[deep and loud shouting]

Deep, loud shouting

[trembling]

Trembling

[angry]

Angry

[excited]

Excited

[sarcastic]

Sarcastic

[curious]

Curious

[like dracula]

Dracula-like delivery

[bored]

Bored

[tired]

Tired

[scornful]

Scornful

[shouting]

Shouting

[asmr]

Soft ASMR delivery

[panicked]

Panicked

[mischievously]

Mischievous

[empathetic]

Empathetic

[whispers]

Whispering

[reluctantly]

Reluctant

[crying]

Crying

[serious]

Serious

[very slowly]

Very slow

[very fast]

Very fast

Nonverbal tags

A nonverbal tag inserts a sound at its position without changing the emotional style of the surrounding text.

TagDescription

[gasp]

Gasp

[sighing]

Sigh

[clears throat]

Clear the throat

[giggles]

Giggle

[laughing]

Laugh

[cough]

Cough

[snorts]

Snort

Apply in production

Cancel task

With Qwen-Audio-TTS or CosyVoice, send a cancellation instruction to interrupt the current synthesis task. The server ends the task and returns task-finished.

MethodVersion requirementUsage

Python SDK

1.26.4 or later

Call SpeechSynthesizer.streaming_cancel().

Java SDK

2.22.26 or later

Call SpeechSynthesizer.streamingCancel().

Raw WebSocket protocol

No SDK required

Send finish-task with directive=cancel in input.

WebSocket connection reuse

A WebSocket connection can handle successive synthesis rounds. Start the next round as follows for each model series:

Model seriesEnd-of-round eventStart the next round

Qwen-Audio-TTS, CosyVoice

task-finished

Send run-task with a new task_id, then send text using the series-specific flow. A new task_id is required for every round, including after cancellation.

NoteWhen calling Qwen-Audio-TTS or CosyVoice with the Python SDK, a directly created SpeechSynthesizer closes its connection when the task completes or is canceled. To reuse a connection, borrow an object from SpeechSynthesizerObjectPool.

Sambert

task-finished

Send run-task with a new task_id and the complete text for this round. Do not send continue-task or finish-task.

Qwen3-TTS

response.done

Continue sending input_text_buffer.append in the same session. Synthesis starts automatically or after input_text_buffer.commit, depending on the session mode. Send session.finish only after all text has been submitted.

For SDK object pool configuration and complete examples, see High-concurrency speech synthesis. For the Qwen3-TTS session flow, see the WebSocket API.

For Qwen-Audio-TTS / CosyVoice / Sambert, a connection closes automatically if no new task starts within 60 seconds after the previous task ends. Reconnect if the connection has closed or the task state is invalid.

Rate limits

Control request frequency and concurrency according to the model’s rate limits. For limits, common errors, and handling instructions, see Rate limits.

High-concurrency best practices

Use object pools to reuse synthesis objects and connections, and configure concurrency, warm-up, and error handling for your workload. See High-concurrency speech synthesis.

Supported models

China (Beijing)

Use an API key and an endpoint for this region to call these models:

  • Qwen-Audio-TTS: qwen-audio-3.1-tts-flash, qwen-audio-3.0-tts-plus, qwen-audio-3.0-tts-flash
  • CosyVoice: cosyvoice-v3.5-plus, cosyvoice-v3.5-flash, cosyvoice-v3-plus, cosyvoice-v3-flash, cosyvoice-v2
  • Qwen3-TTS: qwen3-tts-flash-realtime
  • Sambert: sambert-zhinan-v1, sambert-zhiqi-v1, sambert-zhichu-v1, sambert-zhide-v1, sambert-zhijia-v1, sambert-zhiru-v1, sambert-zhiqian-v1, sambert-zhixiang-v1, sambert-zhiwei-v1, sambert-zhihao-v1, sambert-zhijing-v1, sambert-zhiming-v1, sambert-zhimo-v1, sambert-zhina-v1, sambert-zhishu-v1, sambert-zhistella-v1, sambert-zhiting-v1, sambert-zhixiao-v1, sambert-zhiya-v1, sambert-zhiye-v1, sambert-zhiying-v1, sambert-zhiyuan-v1, sambert-zhiyue-v1, sambert-zhigui-v1, sambert-zhishuo-v1, sambert-zhimiao-emo-v1, sambert-zhimao-v1, sambert-zhilun-v1, sambert-zhifei-v1, sambert-zhida-v1, sambert-camila-v1, sambert-perla-v1, sambert-indah-v1, sambert-clara-v1, sambert-hanna-v1, sambert-beth-v1, sambert-betty-v1, sambert-cally-v1, sambert-cindy-v1, sambert-eva-v1, sambert-donna-v1, sambert-brian-v1, sambert-waan-v1. For details, see Sambert voice list

Singapore

Use an API key and an endpoint for this region to call these models:

  • Qwen-Audio-TTS: qwen-audio-3.1-tts-flash, qwen-audio-3.0-tts-plus, qwen-audio-3.0-tts-flash
  • CosyVoice: cosyvoice-v3-plus, cosyvoice-v3-flash
  • Qwen3-TTS: qwen3-tts-flash-realtime

For system voices and audio samples, see the Qwen-Audio-TTS, CosyVoice, Qwen3-TTS, and Sambert voice lists. A cloned or designed voice must be used with the target model specified when the voice was created.

API reference

For the connection workflow and examples, see AOQ integration. For supported models and versions, see Model and protocol support.

For complete raw WebSocket examples, see the following pages. Use the API entry for each series to find Python and Java SDK parameters and methods.

FAQ

How do I troubleshoot silent audio when using a cloned voice?

Check that voice is the ID of a successfully created voice and that its target_model matches the synthesis model. For voice status, source audio requirements, and quality guidance, see Voice cloning.

Why does the actual duration of synthesized audio differ from the duration shown in the WAV file?

For streamed WAV output, the file header may not reflect the final audio length. To obtain accurate duration metadata, set format to pcm, receive the complete audio, and add a WAV header containing the actual length.

Why can't the audio file play?

Troubleshoot based on your scenario:

  1. Audio saved as a complete file (such as xx.mp3)

    1. Audio format consistency: The audio format in the request parameters must match the file extension (for example, if the parameter is wav, the file must be .wav).
    2. Player compatibility: Confirm that the player supports the audio format and sample rate.
  2. Streaming audio playback

    1. Save the audio stream as a complete file and try playing it with a media player. If the file doesn't play, refer to scenario 1 above.
    2. If the file plays correctly, the issue is in the streaming playback implementation. Confirm that the player supports streaming playback (such as ffmpeg, pyaudio, AudioFormat, or MediaSource).

Why is audio playback stuttering?

Troubleshoot with the following steps:

  1. Check the text sending rate: Make sure the sending interval is reasonable to avoid the previous audio segment finishing before the next text arrives.

  2. Check callback function performance:

    • Confirm that no blocking logic exists in the callback function.
    • Callbacks run on the WebSocket thread. Blocking operations affect data reception. Write audio data to a separate buffer and process it in another thread.
  3. Check network stability: Network fluctuations can cause audio transmission interruptions or delays.

Why is speech synthesis taking a long time?

With segmented input, long gaps between text submissions increase total synthesis time. The first call also includes WebSocket connection overhead. For warm-up and timing in high-concurrency scenarios, see High-concurrency speech synthesis.

How do I restrict an API key to only the speech synthesis service (permission isolation)?

Create a new workspace and grant access to specific models only. This limits the scope of the API key. For details, see Manage workspaces.

Can an API key from a sub-workspace call Qwen-Audio-TTS/CosyVoice models?

In the default workspace, all models are accessible.

In a sub-workspace, you need to authorize models for the sub-workspace associated with the API key. For details, see Model calls in a sub-workspace.