Referência da API de síntese de fala em tempo real (Qwen-TTS-Realtime)
Principais interfaces e parâmetros de solicitação para a síntese de fala em tempo real Qwen no DashScope Java SDK.
Guia do usuário : Para apresentações dos modelos e recomendações de seleção, consulte Síntese de fala em tempo real – Qwen ou Síntese de fala – Qwen .
Pré-requisitos
É necessário ter o DashScope Java SDK 2.22.7 ou posterior.
Primeiros passos
appendText()import com.alibaba.dashscope.audio.qwen_tts_realtime. * ;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.google.gson.JsonObject;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.AudioSystem;
import java.io. * ;
import java.util.Base64;
import java.util.Queue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
public class Main {
static String [] textToSynthesize = {
"Right? I really love this kind of supermarket." ,
"Especially during the Chinese New Year." ,
"Going to the supermarket." ,
"It just makes me feel." ,
"Super, super happy!" ,
"I want to buy so many things!"
};
public static QwenTtsRealtimeAudioFormat ttsFormat = QwenTtsRealtimeAudioFormat . PCM_24000HZ_MONO_16BIT ;
// Real-time PCM audio player
public static class RealtimePcmPlayer {
private int sampleRate ;
private SourceDataLine line ;
private AudioFormat audioFormat ;
private Thread decoderThread ;
private Thread playerThread ;
private AtomicBoolean stopped = new AtomicBoolean ( false );
private Queue < String > b64AudioBuffer = new ConcurrentLinkedQueue <>();
private Queue < byte []> RawAudioBuffer = new ConcurrentLinkedQueue <>();
private ByteArrayOutputStream totalAudioStream = new ByteArrayOutputStream ();
// Initialize the audio format and audio line.
public RealtimePcmPlayer ( int sampleRate ) throws LineUnavailableException {
this . sampleRate = sampleRate;
this . audioFormat = new AudioFormat ( this . sampleRate , 16 , 1 , true , false );
DataLine . Info info = new DataLine. Info ( SourceDataLine . class , audioFormat);
line = (SourceDataLine) AudioSystem . getLine (info);
line . open (audioFormat);
line . start ();
decoderThread = new Thread ( new Runnable () {
@ Override
public void run () {
while ( ! stopped . get ()) {
String b64Audio = b64AudioBuffer . poll ();
if (b64Audio != null ) {
byte [] rawAudio = Base64 . getDecoder (). decode (b64Audio);
RawAudioBuffer . add (rawAudio);
// Write audio data to totalAudioStream.
try {
totalAudioStream . write (rawAudio);
} catch ( IOException e ) {
throw new RuntimeException (e);
}
} else {
try {
Thread . sleep ( 100 );
} catch ( InterruptedException e ) {
throw new RuntimeException (e);
}
}
}
}
});
playerThread = new Thread ( new Runnable () {
@ Override
public void run () {
while ( ! stopped . get ()) {
byte [] rawAudio = RawAudioBuffer . poll ();
if (rawAudio != null ) {
try {
playChunk (rawAudio);
} catch ( IOException e ) {
throw new RuntimeException (e);
} catch ( InterruptedException e ) {
throw new RuntimeException (e);
}
} else {
try {
Thread . sleep ( 100 );
} catch ( InterruptedException e ) {
throw new RuntimeException (e);
}
}
}
}
});
decoderThread . start ();
playerThread . start ();
}
// Play an audio chunk and block until playback completes.
private void playChunk ( byte [] chunk ) throws IOException , InterruptedException {
if (chunk == null || chunk . length == 0 ) return ;
int bytesWritten = 0 ;
while (bytesWritten < chunk . length ) {
bytesWritten += line . write (chunk, bytesWritten, chunk . length - bytesWritten);
}
int audioLength = chunk . length / ( this . sampleRate * 2 / 1000 );
// Wait for the buffered audio to finish playing.
Thread . sleep (audioLength - 10 );
}
public void write ( String b64Audio ) {
b64AudioBuffer . add (b64Audio);
}
public void cancel () {
b64AudioBuffer . clear ();
RawAudioBuffer . clear ();
}
public void waitForComplete () throws InterruptedException {
while ( ! b64AudioBuffer . isEmpty () || ! RawAudioBuffer . isEmpty ()) {
Thread . sleep ( 100 );
}
line . drain ();
}
public void shutdown () throws InterruptedException , IOException {
stopped . set ( true );
decoderThread . join ();
playerThread . join ();
// Save the complete audio file.
File file = new File ( "TotalAudio_" + ttsFormat . getSampleRate () + "." + ttsFormat . getFormat ());
try ( FileOutputStream fos = new FileOutputStream (file)) {
fos . write ( totalAudioStream . toByteArray ());
}
if (line != null && line . isRunning ()) {
line . drain ();
line . close ();
}
}
}
public static void main ( String [] args ) throws InterruptedException , LineUnavailableException , IOException {
QwenTtsRealtimeParam param = QwenTtsRealtimeParam . builder ()
// To use instruction control, replace the model with qwen3-tts-instruct-flash-realtime.
. model ( "qwen3-tts-flash-realtime" )
// Singapore region
. url ( "wss://dashscope.aliyuncs.com/api-ws/v1/realtime" )
// API keys differ between Singapore and China (Beijing). See https://www.alibabacloud.com/help/en/model-studio/get-api-key.
. apikey ( System . getenv ( "DASHSCOPE_API_KEY" ))
. build ();
AtomicReference < CountDownLatch > completeLatch = new AtomicReference <>( new CountDownLatch ( 1 ));
final AtomicReference < QwenTtsRealtime > qwenTtsRef = new AtomicReference <>( null );
// Create a real-time audio player instance.
RealtimePcmPlayer audioPlayer = new RealtimePcmPlayer ( 24000 );
QwenTtsRealtime qwenTtsRealtime = new QwenTtsRealtime (param, new QwenTtsRealtimeCallback () {
@ Override
public void onOpen () {
// Handle connection establishment.
}
@ Override
public void onEvent ( JsonObject message ) {
String type = message . get ( "type" ). getAsString ();
switch (type) {
case "session.created" :
// Handle session creation.
if ( message . has ( "session" )) {
String eventId = message . get ( "event_id" ). getAsString ();
String sessionId = message . get ( "session" ). getAsJsonObject (). get ( "id" ). getAsString ();
System . out . println ( "[onEvent] session.created, session_id: "
+ sessionId + ", event_id: " + eventId);
}
break ;
case "response.audio.delta" :
String recvAudioB64 = message . get ( "delta" ). getAsString ();
// Play audio in real time.
audioPlayer . write (recvAudioB64);
break ;
case "response.done" :
// Handle response completion.
break ;
case "session.finished" :
// Handle session termination.
completeLatch . get (). countDown ();
default:
break ;
}
}
@ Override
public void onClose ( int code , String reason ) {
// Handle connection closure.
}
});
qwenTtsRef . set (qwenTtsRealtime);
try {
qwenTtsRealtime . connect ();
} catch ( NoApiKeyException e ) {
throw new RuntimeException (e);
}
QwenTtsRealtimeConfig config = QwenTtsRealtimeConfig . builder ()
. voice ( "Cherry" )
. responseFormat (ttsFormat)
. mode ( "server_commit" )
// To use instruction control, uncomment the following lines and replace the model with qwen3-tts-instruct-flash-realtime.
// .instructions("")
// .optimizeInstructions(true)
. build ();
qwenTtsRealtime . updateSession (config);
for ( String text : textToSynthesize) {
qwenTtsRealtime . appendText (text);
Thread . sleep ( 100 );
}
qwenTtsRealtime . finish ();
completeLatch . get (). await ();
qwenTtsRealtime . close ();
// Wait for audio playback to complete, then shut down the player.
audioPlayer . waitForComplete ();
audioPlayer . shutdown ();
System . exit ( 0 );
}
}
commit()import com.alibaba.dashscope.audio.qwen_tts_realtime. * ;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.google.gson.JsonObject;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.AudioSystem;
import java.io. * ;
import java.util.Base64;
import java.util.Queue;
import java.util.Scanner;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
public class Main {
public static QwenTtsRealtimeAudioFormat ttsFormat = QwenTtsRealtimeAudioFormat . PCM_24000HZ_MONO_16BIT ;
// Real-time PCM audio player
public static class RealtimePcmPlayer {
private int sampleRate ;
private SourceDataLine line ;
private AudioFormat audioFormat ;
private Thread decoderThread ;
private Thread playerThread ;
private AtomicBoolean stopped = new AtomicBoolean ( false );
private Queue < String > b64AudioBuffer = new ConcurrentLinkedQueue <>();
private Queue < byte []> RawAudioBuffer = new ConcurrentLinkedQueue <>();
private ByteArrayOutputStream totalAudioStream = new ByteArrayOutputStream ();
// Initialize the audio format and audio line.
public RealtimePcmPlayer ( int sampleRate ) throws LineUnavailableException {
this . sampleRate = sampleRate;
this . audioFormat = new AudioFormat ( this . sampleRate , 16 , 1 , true , false );
DataLine . Info info = new DataLine. Info ( SourceDataLine . class , audioFormat);
line = (SourceDataLine) AudioSystem . getLine (info);
line . open (audioFormat);
line . start ();
decoderThread = new Thread ( new Runnable () {
@ Override
public void run () {
while ( ! stopped . get ()) {
String b64Audio = b64AudioBuffer . poll ();
if (b64Audio != null ) {
byte [] rawAudio = Base64 . getDecoder (). decode (b64Audio);
RawAudioBuffer . add (rawAudio);
// Write audio data to totalAudioStream.
try {
totalAudioStream . write (rawAudio);
} catch ( IOException e ) {
throw new RuntimeException (e);
}
} else {
try {
Thread . sleep ( 100 );
} catch ( InterruptedException e ) {
throw new RuntimeException (e);
}
}
}
}
});
playerThread = new Thread ( new Runnable () {
@ Override
public void run () {
while ( ! stopped . get ()) {
byte [] rawAudio = RawAudioBuffer . poll ();
if (rawAudio != null ) {
try {
playChunk (rawAudio);
} catch ( IOException e ) {
throw new RuntimeException (e);
} catch ( InterruptedException e ) {
throw new RuntimeException (e);
}
} else {
try {
Thread . sleep ( 100 );
} catch ( InterruptedException e ) {
throw new RuntimeException (e);
}
}
}
}
});
decoderThread . start ();
playerThread . start ();
}
// Play an audio chunk and block until playback completes.
private void playChunk ( byte [] chunk ) throws IOException , InterruptedException {
if (chunk == null || chunk . length == 0 ) return ;
int bytesWritten = 0 ;
while (bytesWritten < chunk . length ) {
bytesWritten += line . write (chunk, bytesWritten, chunk . length - bytesWritten);
}
int audioLength = chunk . length / ( this . sampleRate * 2 / 1000 );
// Wait for the buffered audio to finish playing.
Thread . sleep (audioLength - 10 );
}
public void write ( String b64Audio ) {
b64AudioBuffer . add (b64Audio);
}
public void cancel () {
b64AudioBuffer . clear ();
RawAudioBuffer . clear ();
}
public void waitForComplete () throws InterruptedException {
// Wait for all buffered audio data to finish playing.
while ( ! b64AudioBuffer . isEmpty () || ! RawAudioBuffer . isEmpty ()) {
Thread . sleep ( 100 );
}
// Wait for the audio line to drain.
line . drain ();
}
public void shutdown () throws InterruptedException {
stopped . set ( true );
decoderThread . join ();
playerThread . join ();
// Save the complete audio file.
File file = new File ( "TotalAudio_" + ttsFormat . getSampleRate () + "." + ttsFormat . getFormat ());
try ( FileOutputStream fos = new FileOutputStream (file)) {
fos . write ( totalAudioStream . toByteArray ());
} catch ( FileNotFoundException e ) {
throw new RuntimeException (e);
} catch ( IOException e ) {
throw new RuntimeException (e);
}
if (line != null && line . isRunning ()) {
line . drain ();
line . close ();
}
}
}
public static void main ( String [] args ) throws InterruptedException , LineUnavailableException , FileNotFoundException {
Scanner scanner = new Scanner ( System . in );
QwenTtsRealtimeParam param = QwenTtsRealtimeParam . builder ()
// To use instruction control, replace the model with qwen3-tts-instruct-flash-realtime.
. model ( "qwen3-tts-flash-realtime" )
// Singapore region
. url ( "wss://dashscope.aliyuncs.com/api-ws/v1/realtime" )
// API keys differ between Singapore and China (Beijing). See https://www.alibabacloud.com/help/en/model-studio/get-api-key.
. apikey ( System . getenv ( "DASHSCOPE_API_KEY" ))
. build ();
AtomicReference < CountDownLatch > completeLatch = new AtomicReference <>( new CountDownLatch ( 1 ));
// Create a real-time player instance.
RealtimePcmPlayer audioPlayer = new RealtimePcmPlayer ( 24000 );
final AtomicReference < QwenTtsRealtime > qwenTtsRef = new AtomicReference <>( null );
QwenTtsRealtime qwenTtsRealtime = new QwenTtsRealtime (param, new QwenTtsRealtimeCallback () {
@ Override
public void onOpen () {
System . out . println ( "connection opened" );
System . out . println ( "Enter text and press Enter to send. Enter 'quit' to exit the program." );
}
@ Override
public void onEvent ( JsonObject message ) {
String type = message . get ( "type" ). getAsString ();
switch (type) {
case "session.created" :
System . out . println ( "start session: " + message . get ( "session" ). getAsJsonObject (). get ( "id" ). getAsString ());
break ;
case "response.audio.delta" :
String recvAudioB64 = message . get ( "delta" ). getAsString ();
byte [] rawAudio = Base64 . getDecoder (). decode (recvAudioB64);
// Play audio in real time.
audioPlayer . write (recvAudioB64);
break ;
case "response.done" :
System . out . println ( "response done" );
// Wait for audio playback to complete.
try {
audioPlayer . waitForComplete ();
} catch ( InterruptedException e ) {
throw new RuntimeException (e);
}
// Prepare for the next input.
completeLatch . get (). countDown ();
break ;
case "session.finished" :
System . out . println ( "session finished" );
if ( qwenTtsRef . get () != null ) {
System . out . println ( "[Metric] response: " + qwenTtsRef . get (). getResponseId () +
", first audio delay: " + qwenTtsRef . get (). getFirstAudioDelay () + " ms" );
}
completeLatch . get (). countDown ();
default:
break ;
}
}
@ Override
public void onClose ( int code , String reason ) {
System . out . println ( "connection closed code: " + code + ", reason: " + reason);
try {
// Wait for playback to complete, then shut down the player.
audioPlayer . waitForComplete ();
audioPlayer . shutdown ();
} catch ( InterruptedException e ) {
throw new RuntimeException (e);
}
}
});
qwenTtsRef . set (qwenTtsRealtime);
try {
qwenTtsRealtime . connect ();
} catch ( NoApiKeyException e ) {
throw new RuntimeException (e);
}
QwenTtsRealtimeConfig config = QwenTtsRealtimeConfig . builder ()
. voice ( "Cherry" )
. responseFormat (ttsFormat)
. mode ( "commit" )
// To use instruction control, uncomment the following lines and replace the model with qwen3-tts-instruct-flash-realtime.
// .instructions("")
// .optimizeInstructions(true)
. build ();
qwenTtsRealtime . updateSession (config);
// Read user input in a loop.
while ( true ) {
System . out . print ( "Enter the text to synthesize: " );
String text = scanner . nextLine ();
// Exit when the user enters 'quit'.
if ( "quit" . equalsIgnoreCase ( text . trim ())) {
System . out . println ( "Closing the connection..." );
qwenTtsRealtime . finish ();
completeLatch . get (). await ();
break ;
}
// Skip empty input.
if ( text . trim (). isEmpty ()) {
continue ;
}
// Re-initialize the countdown latch.
completeLatch . set ( new CountDownLatch ( 1 ));
// Send the text.
qwenTtsRealtime . appendText (text);
qwenTtsRealtime . commit ();
// Wait for the current synthesis to complete.
completeLatch . get (). await ();
}
// Clean up resources.
audioPlayer . waitForComplete ();
audioPlayer . shutdown ();
scanner . close ();
System . exit ( 0 );
}
}
appendText()import com.alibaba.dashscope.audio.qwen_tts_realtime. * ;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.google.gson.JsonObject;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.AudioSystem;
import java.io. * ;
import java.util.Base64;
import java.util.Queue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
public class Main {
static String [] textToSynthesize = {
"Right? I really love this kind of supermarket." ,
"Especially during the Chinese New Year." ,
"Going to the supermarket." ,
"It just makes me feel." ,
"Super, super happy!" ,
"I want to buy so many things!"
};
public static QwenTtsRealtimeAudioFormat ttsFormat = QwenTtsRealtimeAudioFormat . PCM_24000HZ_MONO_16BIT ;
// Real-time PCM audio player
public static class RealtimePcmPlayer {
private int sampleRate ;
private SourceDataLine line ;
private AudioFormat audioFormat ;
private Thread decoderThread ;
private Thread playerThread ;
private AtomicBoolean stopped = new AtomicBoolean ( false );
private Queue < String > b64AudioBuffer = new ConcurrentLinkedQueue <>();
private Queue < byte []> RawAudioBuffer = new ConcurrentLinkedQueue <>();
private ByteArrayOutputStream totalAudioStream = new ByteArrayOutputStream ();
// Initialize the audio format and audio line.
public RealtimePcmPlayer ( int sampleRate ) throws LineUnavailableException {
this . sampleRate = sampleRate;
this . audioFormat = new AudioFormat ( this . sampleRate , 16 , 1 , true , false );
DataLine . Info info = new DataLine. Info ( SourceDataLine . class , audioFormat);
line = (SourceDataLine) AudioSystem . getLine (info);
line . open (audioFormat);
line . start ();
decoderThread = new Thread ( new Runnable () {
@ Override
public void run () {
while ( ! stopped . get ()) {
String b64Audio = b64AudioBuffer . poll ();
if (b64Audio != null ) {
byte [] rawAudio = Base64 . getDecoder (). decode (b64Audio);
RawAudioBuffer . add (rawAudio);
// Write audio data to totalAudioStream.
try {
totalAudioStream . write (rawAudio);
} catch ( IOException e ) {
throw new RuntimeException (e);
}
} else {
try {
Thread . sleep ( 100 );
} catch ( InterruptedException e ) {
throw new RuntimeException (e);
}
}
}
}
});
playerThread = new Thread ( new Runnable () {
@ Override
public void run () {
while ( ! stopped . get ()) {
byte [] rawAudio = RawAudioBuffer . poll ();
if (rawAudio != null ) {
try {
playChunk (rawAudio);
} catch ( IOException e ) {
throw new RuntimeException (e);
} catch ( InterruptedException e ) {
throw new RuntimeException (e);
}
} else {
try {
Thread . sleep ( 100 );
} catch ( InterruptedException e ) {
throw new RuntimeException (e);
}
}
}
}
});
decoderThread . start ();
playerThread . start ();
}
// Play an audio chunk and block until playback completes.
private void playChunk ( byte [] chunk ) throws IOException , InterruptedException {
if (chunk == null || chunk . length == 0 ) return ;
int bytesWritten = 0 ;
while (bytesWritten < chunk . length ) {
bytesWritten += line . write (chunk, bytesWritten, chunk . length - bytesWritten);
}
int audioLength = chunk . length / ( this . sampleRate * 2 / 1000 );
// Wait for the buffered audio to finish playing.
Thread . sleep (audioLength - 10 );
}
public void write ( String b64Audio ) {
b64AudioBuffer . add (b64Audio);
}
public void cancel () {
b64AudioBuffer . clear ();
RawAudioBuffer . clear ();
}
public void waitForComplete () throws InterruptedException {
while ( ! b64AudioBuffer . isEmpty () || ! RawAudioBuffer . isEmpty ()) {
Thread . sleep ( 100 );
}
line . drain ();
}
public void shutdown () throws InterruptedException , IOException {
stopped . set ( true );
decoderThread . join ();
playerThread . join ();
// Save the complete audio file.
File file = new File ( "TotalAudio_" + ttsFormat . getSampleRate () + "." + ttsFormat . getFormat ());
try ( FileOutputStream fos = new FileOutputStream (file)) {
fos . write ( totalAudioStream . toByteArray ());
}
if (line != null && line . isRunning ()) {
line . drain ();
line . close ();
}
}
}
public static void main ( String [] args ) throws InterruptedException , LineUnavailableException , IOException {
QwenTtsRealtimeParam param = QwenTtsRealtimeParam . builder ()
// To use instruction control, replace the model with qwen3-tts-instruct-flash-realtime.
. model ( "qwen3-tts-flash-realtime" )
// China (Beijing) region
. url ( "wss://dashscope-intl.aliyuncs.com/api-ws/v1/realtime" )
// API keys differ between Singapore and China (Beijing). See https://www.alibabacloud.com/help/en/model-studio/get-api-key.
. apikey ( System . getenv ( "DASHSCOPE_API_KEY" ))
. build ();
AtomicReference < CountDownLatch > completeLatch = new AtomicReference <>( new CountDownLatch ( 1 ));
final AtomicReference < QwenTtsRealtime > qwenTtsRef = new AtomicReference <>( null );
// Create a real-time audio player instance.
RealtimePcmPlayer audioPlayer = new RealtimePcmPlayer ( 24000 );
QwenTtsRealtime qwenTtsRealtime = new QwenTtsRealtime (param, new QwenTtsRealtimeCallback () {
@ Override
public void onOpen () {
// Handle connection establishment.
}
@ Override
public void onEvent ( JsonObject message ) {
String type = message . get ( "type" ). getAsString ();
switch (type) {
case "session.created" :
// Handle session creation.
if ( message . has ( "session" )) {
String eventId = message . get ( "event_id" ). getAsString ();
String sessionId = message . get ( "session" ). getAsJsonObject (). get ( "id" ). getAsString ();
System . out . println ( "[onEvent] session.created, session_id: "
+ sessionId + ", event_id: " + eventId);
}
break ;
case "response.audio.delta" :
String recvAudioB64 = message . get ( "delta" ). getAsString ();
// Play audio in real time.
audioPlayer . write (recvAudioB64);
break ;
case "response.done" :
// Handle response completion.
break ;
case "session.finished" :
// Handle session termination.
completeLatch . get (). countDown ();
default:
break ;
}
}
@ Override
public void onClose ( int code , String reason ) {
// Handle connection closure.
}
});
qwenTtsRef . set (qwenTtsRealtime);
try {
qwenTtsRealtime . connect ();
} catch ( NoApiKeyException e ) {
throw new RuntimeException (e);
}
QwenTtsRealtimeConfig config = QwenTtsRealtimeConfig . builder ()
. voice ( "Cherry" )
. responseFormat (ttsFormat)
. mode ( "server_commit" )
// To use instruction control, uncomment the following lines and replace the model with qwen3-tts-instruct-flash-realtime.
// .instructions("")
// .optimizeInstructions(true)
. build ();
qwenTtsRealtime . updateSession (config);
for ( String text : textToSynthesize) {
qwenTtsRealtime . appendText (text);
Thread . sleep ( 100 );
}
qwenTtsRealtime . finish ();
completeLatch . get (). await ();
qwenTtsRealtime . close ();
// Wait for audio playback to complete, then shut down the player.
audioPlayer . waitForComplete ();
audioPlayer . shutdown ();
System . exit ( 0 );
}
}
commit()import com.alibaba.dashscope.audio.qwen_tts_realtime. * ;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.google.gson.JsonObject;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.AudioSystem;
import java.io. * ;
import java.util.Base64;
import java.util.Queue;
import java.util.Scanner;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
public class Main {
public static QwenTtsRealtimeAudioFormat ttsFormat = QwenTtsRealtimeAudioFormat . PCM_24000HZ_MONO_16BIT ;
// Real-time PCM audio player
public static class RealtimePcmPlayer {
private int sampleRate ;
private SourceDataLine line ;
private AudioFormat audioFormat ;
private Thread decoderThread ;
private Thread playerThread ;
private AtomicBoolean stopped = new AtomicBoolean ( false );
private Queue < String > b64AudioBuffer = new ConcurrentLinkedQueue <>();
private Queue < byte []> RawAudioBuffer = new ConcurrentLinkedQueue <>();
private ByteArrayOutputStream totalAudioStream = new ByteArrayOutputStream ();
// Initialize the audio format and audio line.
public RealtimePcmPlayer ( int sampleRate ) throws LineUnavailableException {
this . sampleRate = sampleRate;
this . audioFormat = new AudioFormat ( this . sampleRate , 16 , 1 , true , false );
DataLine . Info info = new DataLine. Info ( SourceDataLine . class , audioFormat);
line = (SourceDataLine) AudioSystem . getLine (info);
line . open (audioFormat);
line . start ();
decoderThread = new Thread ( new Runnable () {
@ Override
public void run () {
while ( ! stopped . get ()) {
String b64Audio = b64AudioBuffer . poll ();
if (b64Audio != null ) {
byte [] rawAudio = Base64 . getDecoder (). decode (b64Audio);
RawAudioBuffer . add (rawAudio);
// Write audio data to totalAudioStream.
try {
totalAudioStream . write (rawAudio);
} catch ( IOException e ) {
throw new RuntimeException (e);
}
} else {
try {
Thread . sleep ( 100 );
} catch ( InterruptedException e ) {
throw new RuntimeException (e);
}
}
}
}
});
playerThread = new Thread ( new Runnable () {
@ Override
public void run () {
while ( ! stopped . get ()) {
byte [] rawAudio = RawAudioBuffer . poll ();
if (rawAudio != null ) {
try {
playChunk (rawAudio);
} catch ( IOException e ) {
throw new RuntimeException (e);
} catch ( InterruptedException e ) {
throw new RuntimeException (e);
}
} else {
try {
Thread . sleep ( 100 );
} catch ( InterruptedException e ) {
throw new RuntimeException (e);
}
}
}
}
});
decoderThread . start ();
playerThread . start ();
}
// Play an audio chunk and block until playback completes.
private void playChunk ( byte [] chunk ) throws IOException , InterruptedException {
if (chunk == null || chunk . length == 0 ) return ;
int bytesWritten = 0 ;
while (bytesWritten < chunk . length ) {
bytesWritten += line . write (chunk, bytesWritten, chunk . length - bytesWritten);
}
int audioLength = chunk . length / ( this . sampleRate * 2 / 1000 );
// Wait for the buffered audio to finish playing.
Thread . sleep (audioLength - 10 );
}
public void write ( String b64Audio ) {
b64AudioBuffer . add (b64Audio);
}
public void cancel () {
b64AudioBuffer . clear ();
RawAudioBuffer . clear ();
}
public void waitForComplete () throws InterruptedException {
// Wait for all buffered audio data to finish playing.
while ( ! b64AudioBuffer . isEmpty () || ! RawAudioBuffer . isEmpty ()) {
Thread . sleep ( 100 );
}
// Wait for the audio line to drain.
line . drain ();
}
public void shutdown () throws InterruptedException {
stopped . set ( true );
decoderThread . join ();
playerThread . join ();
// Save the complete audio file.
File file = new File ( "TotalAudio_" + ttsFormat . getSampleRate () + "." + ttsFormat . getFormat ());
try ( FileOutputStream fos = new FileOutputStream (file)) {
fos . write ( totalAudioStream . toByteArray ());
} catch ( FileNotFoundException e ) {
throw new RuntimeException (e);
} catch ( IOException e ) {
throw new RuntimeException (e);
}
if (line != null && line . isRunning ()) {
line . drain ();
line . close ();
}
}
}
public static void main ( String [] args ) throws InterruptedException , LineUnavailableException , FileNotFoundException {
Scanner scanner = new Scanner ( System . in );
QwenTtsRealtimeParam param = QwenTtsRealtimeParam . builder ()
// To use instruction control, replace the model with qwen3-tts-instruct-flash-realtime.
. model ( "qwen3-tts-flash-realtime" )
// China (Beijing) region
. url ( "wss://dashscope-intl.aliyuncs.com/api-ws/v1/realtime" )
// API keys differ between Singapore and China (Beijing). See https://www.alibabacloud.com/help/en/model-studio/get-api-key.
. apikey ( System . getenv ( "DASHSCOPE_API_KEY" ))
. build ();
AtomicReference < CountDownLatch > completeLatch = new AtomicReference <>( new CountDownLatch ( 1 ));
// Create a real-time player instance.
RealtimePcmPlayer audioPlayer = new RealtimePcmPlayer ( 24000 );
final AtomicReference < QwenTtsRealtime > qwenTtsRef = new AtomicReference <>( null );
QwenTtsRealtime qwenTtsRealtime = new QwenTtsRealtime (param, new QwenTtsRealtimeCallback () {
@ Override
public void onOpen () {
System . out . println ( "connection opened" );
System . out . println ( "Enter text and press Enter to send. Enter 'quit' to exit the program." );
}
@ Override
public void onEvent ( JsonObject message ) {
String type = message . get ( "type" ). getAsString ();
switch (type) {
case "session.created" :
System . out . println ( "start session: " + message . get ( "session" ). getAsJsonObject (). get ( "id" ). getAsString ());
break ;
case "response.audio.delta" :
String recvAudioB64 = message . get ( "delta" ). getAsString ();
byte [] rawAudio = Base64 . getDecoder (). decode (recvAudioB64);
// Play audio in real time.
audioPlayer . write (recvAudioB64);
break ;
case "response.done" :
System . out . println ( "response done" );
// Wait for audio playback to complete.
try {
audioPlayer . waitForComplete ();
} catch ( InterruptedException e ) {
throw new RuntimeException (e);
}
// Prepare for the next input.
completeLatch . get (). countDown ();
break ;
case "session.finished" :
System . out . println ( "session finished" );
if ( qwenTtsRef . get () != null ) {
System . out . println ( "[Metric] response: " + qwenTtsRef . get (). getResponseId () +
", first audio delay: " + qwenTtsRef . get (). getFirstAudioDelay () + " ms" );
}
completeLatch . get (). countDown ();
default:
break ;
}
}
@ Override
public void onClose ( int code , String reason ) {
System . out . println ( "connection closed code: " + code + ", reason: " + reason);
try {
// Wait for playback to complete, then shut down the player.
audioPlayer . waitForComplete ();
audioPlayer . shutdown ();
} catch ( InterruptedException e ) {
throw new RuntimeException (e);
}
}
});
qwenTtsRef . set (qwenTtsRealtime);
try {
qwenTtsRealtime . connect ();
} catch ( NoApiKeyException e ) {
throw new RuntimeException (e);
}
QwenTtsRealtimeConfig config = QwenTtsRealtimeConfig . builder ()
. voice ( "Cherry" )
. responseFormat (ttsFormat)
. mode ( "commit" )
// To use instruction control, uncomment the following lines and replace the model with qwen3-tts-instruct-flash-realtime.
// .instructions("")
// .optimizeInstructions(true)
. build ();
qwenTtsRealtime . updateSession (config);
// Read user input in a loop.
while ( true ) {
System . out . print ( "Enter the text to synthesize: " );
String text = scanner . nextLine ();
// Exit when the user enters 'quit'.
if ( "quit" . equalsIgnoreCase ( text . trim ())) {
System . out . println ( "Closing the connection..." );
qwenTtsRealtime . finish ();
completeLatch . get (). await ();
break ;
}
// Skip empty input.
if ( text . trim (). isEmpty ()) {
continue ;
}
// Re-initialize the countdown latch.
completeLatch . set ( new CountDownLatch ( 1 ));
// Send the text.
qwenTtsRealtime . appendText (text);
qwenTtsRealtime . commit ();
// Wait for the current synthesis to complete.
completeLatch . get (). await ();
}
// Clean up resources.
audioPlayer . waitForComplete ();
audioPlayer . shutdown ();
scanner . close ();
System . exit ( 0 );
}
}
Para mais exemplos, consulte o repositório do GitHub .
Parâmetros da solicitação
Configure os seguintes parâmetros de solicitação usando os métodos encadeados ou setters de um objeto QwenTtsRealtimeParam e, em seguida, passe o objeto para o construtor QwenTtsRealtime.
Parâmetro
Tipo
Obrigatório
Descrição
model
String
Sim
url
String
Sim
China (Pequim): wss://dashscope.aliyuncs.com/api-ws/v1/realtime
Singapura: wss://dashscope-intl.aliyuncs.com/api-ws/v1/realtime
Defina os parâmetros abaixo utilizando os métodos encadeados ou setters de um objeto QwenTtsRealtimeConfig e passe-o para o método updateSession.
Parâmetro
Tipo
Obrigatório
Descrição
voice
String
Sim
A voz usada na síntese de fala. Para mais informações, consulte Vozes suportadas .
Há suporte para vozes do sistema e vozes personalizadas:
languageType
String
Não
Especifica o idioma do áudio sintetizado. O valor padrão é Auto.
Auto: Use este valor quando o idioma do texto for incerto ou contiver vários idiomas. O modelo corresponde automaticamente à pronúncia para diferentes segmentos de idioma no texto, mas não garante precisão perfeita.
Idioma específico: Use esta opção para textos em um único idioma. Especificar um idioma melhora significativamente a qualidade da síntese e geralmente produz resultados melhores que Auto. Os valores válidos incluem:
Chinese
English
German
Italian
Portuguese
Spanish
Japanese
Korean
French
Russian
mode
String
Não
O modo de interação. Valores válidos:
server_commit (padrão): O servidor determina automaticamente quando sintetizar a fala, equilibrando latência e qualidade. Este modo é recomendado para a maioria dos cenários.
commit: O cliente aciona manualmente a síntese. Este modo oferece a menor latência, mas exige que você gerencie a integridade das frases por conta própria.
format
String
Não
O formato da saída de áudio do modelo.
Formatos suportados:
pcm (padrão)
wav
mp3
opus
Qwen-TTS-Realtime (consulte Modelos suportados ) suporta apenas pcm.
sampleRate
int
Não
A taxa de amostragem da saída de áudio do modelo, em Hz.
Taxas de amostragem suportadas:
8000
16000
24000 (padrão)
48000
Qwen-TTS-Realtime (consulte Modelos suportados ) suporta apenas 24000.
speechRate
float
Não
A velocidade da fala do áudio. Um valor de 1,0 representa a velocidade normal. Valores menores que 1,0 tornam a fala mais lenta, enquanto valores maiores que 1,0 a tornam mais rápida.
Valor padrão: 1,0.
Intervalo válido: [0,5, 2,0].
Qwen-TTS-Realtime (consulte Modelos suportados ) não suporta este parâmetro.
volume
int
Não
O volume do áudio.
Valor padrão: 50.
Intervalo válido: [0, 100].
Qwen-TTS-Realtime (consulte Modelos suportados ) não suporta este parâmetro.
pitchRate
float
Não
O tom do áudio sintetizado.
Valor padrão: 1,0.
Intervalo válido: [0,5, 2,0].
Qwen-TTS-Realtime (consulte Modelos suportados ) não suporta este parâmetro.
bitRate
int
Não
Especifica a taxa de bits do áudio em kbps. Uma taxa de bits maior resulta em melhor qualidade de áudio e tamanho de arquivo maior. Este parâmetro está disponível apenas quando o formato de áudio (response_format) está definido como opus.
Valor padrão: 128.
Intervalo válido: [6, 510].
Qwen-TTS-Realtime (consulte Modelos suportados ) não suporta este parâmetro.
instructions
String
Não
Define as instruções. Para mais informações, consulte Síntese de fala em tempo real - Qwen .
Valor padrão: Nenhum. O parâmetro fica inativo se não for definido.
Limite de comprimento: O conteúdo não pode exceder 1600 tokens.
Idiomas suportados: Apenas chinês e inglês.
Escopo: Este recurso está disponível apenas para a série de modelos Qwen3-TTS-Instruct-Flash-Realtime.
optimizeInstructions
boolean
Não
Especifica se as instructions devem ser otimizadas para melhorar a naturalidade e a expressividade da síntese de fala.
Valor padrão: false.
Comportamento: Quando definido como true, o sistema aprimora e reescreve o conteúdo de instructions para gerar instruções internas mais adequadas à síntese de fala.
Cenários: Recomendado para situações que exigem expressão vocal refinada e de alta qualidade.
Dependência: Este parâmetro depende da configuração do parâmetro instructions. Se instructions estiver vazio, este parâmetro não terá efeito.
Escopo: Este recurso está disponível apenas para a série de modelos Qwen3-TTS-Instruct-Flash-Realtime.
Interfaces principais
Classe QwenTtsRealtime
Para importar:
import com.alibaba.dashscope.audio.qwen_tts_realtime.QwenTtsRealtime;
Método Assinatura Eventos do servidor Descrição connect public void connect () throws NoApiKeyException, InterruptedException
session.created
Sessão criada
session.updated
Configuração da sessão atualizada
Abre uma conexão WebSocket com o servidor. updateSession public void updateSession ( QwenTtsRealtimeConfig config)
session.updated
Configuração da sessão atualizada
Atualiza a configuração da sessão. Consulte Parâmetros da solicitação. Após a conexão, o servidor retorna configurações padrão de entrada e saída para a sessão. Chame este método imediatamente após connect() para substituir os padrões. O servidor valida os parâmetros ao receber um evento session.update. Se algum parâmetro for inválido, o servidor retornará um erro. Caso contrário, ele atualiza a configuração da sessão no lado do servidor. appendText public void appendText ( String text)
Nenhum Adiciona um segmento de texto ao buffer de entrada no lado do servidor. O buffer armazena o texto até que você o envie.
No modo server_commit, o servidor decide quando confirmar e sintetizar o texto armazenado no buffer.
No modo commit, o cliente deve acionar a síntese chamando commit.
clearAppendedText public void clearAppendedText ()
input_text_buffer.cleared
Limpa o texto recebido pelo servidor
Limpa todo o texto no buffer de entrada do servidor. commit input_text_buffer.committed
Confirma o texto e aciona a síntese de fala
response.output_item.added
Novo conteúdo de saída aparece na resposta
response.content_part.added
Novo conteúdo de saída adicionado ao item de mensagem do assistente
response.audio.delta
O modelo gera áudio incrementalmente
response.audio.done
Geração de áudio concluída
response.content_part.done
Streaming do conteúdo de áudio para a mensagem do assistente concluído
response.output_item.done
Streaming de todo o item de saída para a mensagem do assistente concluído
response.done
Resposta concluída
Confirma o texto previamente adicionado ao buffer do servidor e sintetiza todo o texto imediatamente. Retorna um erro se o buffer estiver vazio.
No modo server_commit, o cliente não precisa chamar este método. O servidor confirma automaticamente.
No modo commit, o cliente deve chamar commit para acionar a síntese.
finish session.finished
Resposta concluída
Interrompe a tarefa atual. close Nenhum Fecha a conexão. getSessionId public String getSessionId ()
Nenhum Retorna o ID da sessão da tarefa atual. getResponseId public String getResponseId ()
Nenhum Retorna o ID da resposta mais recente. getFirstAudioDelay public long getFirstAudioDelay ()
Nenhum Retorna a latência do primeiro pacote de áudio em milissegundos.
Interface de callback (QwenTtsRealtimeCallback)
Método Parâmetros Valor de retorno Descrição Nenhum Nenhum Chamado imediatamente após o estabelecimento da conexão WebSocket. public abstract void onEvent ( JsonObject message)
message: evento de resposta do servidor. Nenhum Chamado quando o servidor envia um evento, incluindo respostas de chamadas de API e áudio gerado pelo modelo. Consulte Eventos do lado do servidor . public abstract void onClose ( int code, String reason)
code: código de status de fechamento do WebSocket. reason: motivo do fechamento. Nenhum Chamado após o servidor fechar a conexão.
Qwen-TTS non-real-time speech synthesis API reference
Parâmetros de requisição e campos de resposta da API de síntese de fala não em tempo real (Qwen-TTS).