Java SDK
Use the SDK for Java to synthesize speech from text and receive streaming audio. Configure the voice, audio format, speech rate, and subtitles as needed.
Prerequisites
An enabled Intelligent Speech Interaction service and a project appkey. For instructions, see Create a project.
An AccessKey ID and AccessKey secret with permission to call the service. These credentials are used to obtain an NLS token. Use the appkey, credentials, and service endpoint for the same project configuration.
A Java development environment and a Maven or Gradle project. The sample uses JDK 21 and
nls-sdk-tts2.2.19.
Install the SDK
Install the SDK, configure credentials, and run the synthesis sample. For request parameters, voices, and service endpoints, see the speech synthesis API reference.
Add the dependencies with a package manager, or download a sample project. The SDK depends on Netty. If the project already uses Netty, use version 4.1.17.Final or later.
Maven
Add the following dependencies to dependencies in pom.xml. The jaxb-api dependency supplies the classes required by the built-in AccessToken class on JDK 21.
<dependency>
<groupId>com.alibaba.nls</groupId>
<artifactId>nls-sdk-tts</artifactId>
<version>2.2.19</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
Gradle
Add the Maven Central repository and the following dependencies to build.gradle. The jaxb-api dependency supplies the classes required by the built-in AccessToken class on JDK 21.
repositories {
mavenCentral()
}
dependencies {
implementation 'com.alibaba.nls:nls-sdk-tts:2.2.19'
implementation 'javax.xml.bind:jaxb-api:2.3.1'
}
Sample project
Download the Java SDK sample project. This project uses SDK 2.2.1.
Extract the archive and run the following command in the nls-sdk-java-demo directory:
mvn package
The executable synthesis JAR is nls-example-tts/target/nls-example-tts-2.0.0-jar-with-dependencies.jar. It includes runtime dependencies. The 2.0.0 in the filename is the sample project version, not the SDK version.
From the directory containing the JAR, pass an appkey, NLS token, and endpoint to the synthesis main class. First configure the sample project variables described in "Configure credentials" below, and then run the following command:
java -cp nls-example-tts-2.0.0-jar-with-dependencies.jar \
com.alibaba.nls.client.SpeechSynthesizerDemo \
"$NLS_APP_KEY" "$NLS_TOKEN" "$NLS_GATEWAY_URL"
The load-test entry point takes the appkey, NLS token, endpoint, text, audio filename, and concurrency in that order:
java -jar nls-example-tts-2.0.0-jar-with-dependencies.jar \
"$NLS_APP_KEY" "$NLS_TOKEN" "$NLS_GATEWAY_URL" \
"Hello world." "tts-test.wav" 1
The program does not prompt for interactive input. If arguments are missing, it prints usage information and exits. Logs are saved to logs/nls.log in the working directory. Set concurrency according to the provisioned service capacity.
The sample project accepts a token as a command-line argument, which local process inspection tools may expose. Run it only in a controlled environment, and do not share commands or logs that contain credentials. For application integration, use the complete sample that reads credentials from environment variables.
Configure credentials
The complete sample reads the following environment variables and obtains an NLS token through the SDK. Do not include AccessKey credentials or tokens in source code or logs.
|
Environment variable |
Description |
|
|
The appkey of the Intelligent Speech Interaction project. |
|
|
An AccessKey ID with permission to call the service. |
|
|
The corresponding AccessKey secret. |
For token acquisition methods and expiration, see Obtain a token.
Replace the placeholder values with the actual configuration. These variables apply to the current terminal session. For an IDE, set the same variables in its run configuration.
Linux / macOS
export NLS_APP_KEY="YOUR_APP_KEY"
export ALIYUN_AK_ID="YOUR_ACCESS_KEY_ID"
export ALIYUN_AK_SECRET="YOUR_ACCESS_KEY_SECRET"
Windows PowerShell
$env:NLS_APP_KEY="YOUR_APP_KEY"
$env:ALIYUN_AK_ID="YOUR_ACCESS_KEY_ID"
$env:ALIYUN_AK_SECRET="YOUR_ACCESS_KEY_SECRET"
Windows CMD
set NLS_APP_KEY=YOUR_APP_KEY
set ALIYUN_AK_ID=YOUR_ACCESS_KEY_ID
set ALIYUN_AK_SECRET=YOUR_ACCESS_KEY_SECRET
To run the downloaded sample project, also set NLS_TOKEN to an unexpired NLS token and NLS_GATEWAY_URL to the following endpoint. The complete Java sample obtains a token and configures its endpoint internally, so it does not read these two variables.
NLS_GATEWAY_URL: wss://nls-gateway-cn-shanghai.aliyuncs.com/ws/v1.
Tokens expire. In a long-running application, obtain a new token before expiration and call NlsClient.setToken to update the token for subsequent connections. Do not log the token value.
Synthesize speech
The sample obtains a token, initializes a client, and synthesizes 16 kHz WAV audio with the siyue voice. It saves the audio to a temporary file, measures first-packet latency, and checks for synthesis failures, timeouts, and file write errors.
NlsClient uses Netty, is expensive to create, and can be shared across threads. Create one instance when the application starts, reuse it, and call shutdown() when the application exits. Use a separate SpeechSynthesizer and listener for each task, and call close() when the task ends. Do not reuse either object across tasks.
import com.alibaba.nls.client.AccessToken;
import com.alibaba.nls.client.protocol.NlsClient;
import com.alibaba.nls.client.protocol.OutputFormatEnum;
import com.alibaba.nls.client.protocol.SampleRateEnum;
import com.alibaba.nls.client.protocol.tts.SpeechSynthesizer;
import com.alibaba.nls.client.protocol.tts.SpeechSynthesizerListener;
import com.alibaba.nls.client.protocol.tts.SpeechSynthesizerResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
public class SpeechSynthesizerDemo {
private static String requireEnv(String name) {
String value = System.getenv(name);
if (value == null || value.trim().isEmpty()) {
throw new IllegalArgumentException("Missing environment variable: " + name);
}
return value;
}
public static void main(String[] args) throws Exception {
String appKey = requireEnv("NLS_APP_KEY");
String accessKeyId = requireEnv("ALIYUN_AK_ID");
String accessKeySecret = requireEnv("ALIYUN_AK_SECRET");
AccessToken accessToken = new AccessToken(accessKeyId, accessKeySecret);
accessToken.apply();
if (accessToken.getToken() == null || accessToken.getToken().isEmpty()) {
throw new IllegalStateException("Failed to obtain an NLS token");
}
NlsClient client = new NlsClient("wss://nls-gateway-cn-shanghai.aliyuncs.com/ws/v1", accessToken.getToken());
try {
Path output = Files.createTempFile("tts-", ".wav");
try (OutputStream audio = Files.newOutputStream(output)) {
AtomicBoolean completed = new AtomicBoolean(false);
AtomicBoolean firstPacket = new AtomicBoolean(true);
AtomicReference<Exception> failure = new AtomicReference<>();
final long[] startedAt = new long[1];
SpeechSynthesizerListener listener = new SpeechSynthesizerListener() {
@Override
public void onMessage(ByteBuffer message) {
if (firstPacket.compareAndSet(true, false)) {
long elapsedMs = (System.nanoTime() - startedAt[0]) / 1_000_000;
System.out.println("First packet latency (ms): " + elapsedMs);
}
byte[] bytes = new byte[message.remaining()];
message.get(bytes);
try {
audio.write(bytes);
} catch (IOException e) {
failure.compareAndSet(null, e);
}
}
@Override
public void onComplete(SpeechSynthesizerResponse response) {
if (response.getStatus() == 20000000) {
completed.set(true);
System.out.println("Synthesis completed, task_id: " + response.getTaskId());
} else {
onFail(response);
}
}
@Override
public void onFail(SpeechSynthesizerResponse response) {
failure.compareAndSet(null, new IOException(
"task_id=" + response.getTaskId() + ", status=" + response.getStatus()
+ ", status_text=" + response.getStatusText()));
}
@Override
public void onMetaInfo(SpeechSynthesizerResponse response) {
System.out.println("Subtitles: " + response.getObject("subtitles"));
}
};
SpeechSynthesizer synthesizer = new SpeechSynthesizer(client, listener);
try {
synthesizer.setAppKey(appKey);
synthesizer.setVoice("siyue");
synthesizer.setFormat(OutputFormatEnum.WAV);
synthesizer.setSampleRate(SampleRateEnum.SAMPLE_RATE_16K);
synthesizer.setVolume(50);
synthesizer.setSpeechRate(0);
synthesizer.setPitchRate(0);
synthesizer.setText("Hello world. Welcome to the speech synthesis service.");
synthesizer.addCustomedParam("enable_subtitle", false);
startedAt[0] = System.nanoTime();
synthesizer.start();
synthesizer.waitForComplete(30_000L);
if (failure.get() != null) {
throw failure.get();
}
if (!completed.get()) {
throw new TimeoutException("Synthesis did not complete within 30 seconds");
}
} finally {
synthesizer.close();
}
}
System.out.println("Audio file: " + output.toAbsolutePath());
} finally {
client.shutdown();
}
}
}
On success, the program prints Synthesis completed and the audio file path. Open the generated WAV file to check its content. If an exception occurs, the temporary file may be incomplete and must not be used as a successful synthesis result.
The sample waits up to 30 seconds. This is an application setting, not a service latency guarantee. For low-latency playback, play audio as it arrives in onMessage. Streaming playback does not change the input text length limit.
For an application on an ECS instance in the Shanghai region that requires internal access, select an applicable internal endpoint from the speech synthesis service endpoints. Do not use a public endpoint as an internal endpoint.
Key interfaces
Obtain a token, create a client and synthesis object, set the parameters, start the task, process callbacks, and release resources.
Objects and authentication
|
Interface |
Description |
|
|
Creates an object for obtaining tokens with the default configuration. The constructor does not send a request. For the service configuration, see the complete example. |
|
|
Specifies the token service domain, region, and API version. Use values that match the service configuration. |
|
|
Requests a token. Check whether |
|
|
Returns the acquired token. |
|
|
Returns the token expiration timestamp. |
|
|
Creates a client with a service endpoint and NLS token. |
|
|
Updates the token used for subsequent client connections. |
|
|
Creates a synthesis task object and establishes a connection. |
Synthesis parameters
Set parameters before calling start(). The following table lists the main parameters used in this sample.
|
Parameter |
Type |
Description |
|
appKey (required) |
String |
The project appkey. Set it with |
|
text (required) |
String |
The text to synthesize, set with |
|
voice (optional) |
String |
The voice, set with |
|
format (optional) |
OutputFormatEnum |
The audio format, set with |
|
sampleRate (optional) |
SampleRateEnum |
The sample rate, set with |
|
volume (optional) |
int |
The volume, set with |
|
speechRate (optional) |
int |
The speech rate, set with |
|
pitchRate (optional) |
int |
The pitch, set with |
|
enable_subtitle (optional) |
Boolean |
Whether to return word-level timestamps, set with |
text supports basic SSML tags. For example, <speak>Hello.<break time="1s"/>Welcome.</speak> inserts a one-second pause between the two sentences.
addCustomedParam(String key, Object value) sets custom request parameters and is not limited to subtitles. Use parameter names and values supported by the service. For subtitle output, see Speech synthesis timestamps.
A success status code does not prove that overlength text was synthesized in full. Limit the text length before sending a request, and check the audio content.
For a multi-emotion voice, specify an emotion with the SSML emotion tag in text. For syntax and voice requirements, see SSML-based synthesis. Only multi-emotion voices support this tag. Using an unsupported voice may cause synthesis to fail.
Result callbacks
Implement the following SpeechSynthesizerListener callbacks. Implement onMetaInfo when subtitle processing is needed.
|
Callback |
Description |
|
|
Receives binary audio data to write to a file or pass to a player. |
|
|
Receives the synthesis completion event, indicating that audio reception has ended. Also check the status code. |
|
|
Handles task failures. Record |
|
|
Receives subtitle information when |
Task control and cleanup
|
Interface |
Description |
|
|
Sends the synthesis request. It does not indicate that audio generation has finished. |
|
|
Waits for the task to end without a timeout. |
|
|
Waits up to the specified duration for the task to end. Starting with SDK 2.1.7, the unit changed from seconds to milliseconds. Check the success or failure state after the method returns. If the method returns because the timeout expires, the task may still be running. |
|
|
Closes the current task connection. |
|
|
Releases client resources when the application exits. |
FAQ
How do I troubleshoot ClosedChannelException?
Check network connectivity, the service endpoint, token validity, and dependency conflicts, and compare the application with the sample project. The SDK generates task_id on the client before sending a request. Its presence or absence does not establish whether the server received the request. Check exceptions, service callbacks, and logs together.
How do I resolve a missing JAXB class?
If the built-in AccessToken class throws NoClassDefFoundError: javax/xml/bind/DatatypeConverter on JDK 21, add the jaxb-api dependency shown in the installation configuration and include it in the runtime classpath.
How do I troubleshoot org.json.JSONArray.iterator()Ljava/util/Iterator errors?
Check for missing dependencies and version conflicts. For projects that use the following JSON libraries, verify the JAR versions loaded at runtime.
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20170516</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.2</version>
</dependency>
How do I analyze synthesis latency?
First-packet latency is the time from sending the synthesis request to receiving the first audio packet. Full synthesis latency is the time from sending the request to receiving the completion event. Record the start time before start(), and calculate elapsed time in the first onMessage callback and in onComplete. Do not share timing variables across tasks.
For SDK log analysis, locate the StartSynthesis send entry and the first audio packet entry for the same task_id. Streaming playback depends on first-packet latency; the time to generate a complete file is not first-packet latency.