Java SDK

更新时间:
复制 MD 格式

This topic describes how to use the Java software development kit (SDK) for Alibaba Cloud Intelligent Speech Interaction, including installation instructions and sample code.

Prerequisites

  • Before you use the SDK, read the API reference. For more information, see API reference.

  • Starting with version 2.1.0, nls-sdk-long-asr is renamed to nls-sdk-transcriber. When you upgrade, make sure to delete nls-sdk-long-asr and add the corresponding callback methods as prompted during compilation.

Download and installation

  1. Download the latest version of the SDK from the Maven server.

    <dependency>    
          <groupId>com.alibaba.nls</groupId>  
          <artifactId>nls-sdk-transcriber</artifactId>   
          <version>2.2.1</version>
    </dependency>

    After you decompress the demo, run the mvn package command in the pom directory. An executable JAR file named nls-example-transcriber-2.0.0-jar-with-dependencies.jar is generated in the target directory. You can copy the JAR file to the destination server for quick service validation and stress testing.

  2. Validate the service.

    Run the following command and provide the parameters as prompted. After the command is run, the logs/nls.log file is generated in the directory where you ran the command.

    java -cp nls-example-transcriber-2.0.0-jar-with-dependencies.jar com.alibaba.nls.client.SpeechTranscriberDemo
  3. Perform stress testing.

    Run the following command and provide the parameters as prompted. Set the Alibaba Cloud service URL parameter to wss://nls-gateway-cn-shanghai.aliyuncs.com/ws/v1. Use PCM audio files with a 16 kHz sample rate. Select the number of concurrent calls based on your purchased resources.

    java -jar nls-example-transcriber-2.0.0-jar-with-dependencies.jar
    Important

    Charges are incurred if you perform stress testing with more than two concurrent streams.

Key interfaces

  • NlsClient: The speech processing client. This client processes tasks for short sentence recognition, real-time speech recognition, and speech synthesis. This client is thread-safe. We recommend that you create only one global instance.

  • SpeechTranscriber: The real-time speech recognition class. This class is used to set request parameters and send requests and audio data. This class is not thread-safe.

  • SpeechTranscriberListener: The real-time speech recognition result listener that listens for recognition results. This class is not thread-safe.

For more information, see Java API reference.

Important

Notes on SDK calls:

  • NlsClient uses the Netty framework. Creating an NlsClient object consumes time and resources, but the created object can be reused. We recommend that you align the creation and shutdown of the NlsClient object with your application's lifecycle.

  • The SpeechTranscriber object cannot be reused. One recognition task corresponds to one SpeechTranscriber object. For example, to perform N recognition tasks for N audio files, you must create N SpeechTranscriber objects.

  • A SpeechTranscriberListener object and a SpeechTranscriber object have a one-to-one relationship. Do not use the same SpeechTranscriberListener object for different SpeechTranscriber objects. Otherwise, you cannot distinguish between recognition tasks.

  • The Java SDK depends on the Netty network library. If your application also depends on Netty, upgrade the Netty version to 4.1.17.Final or later.

Sample code

Note
  • Download nls-sample-16k.wav.

    The audio file used in the example has a sample rate of 16,000 Hz. To obtain correct recognition results, set the model for the project that corresponds to the appkey to Universal in the console. If you use other audio files, set the model to one that supports your audio scenario. For more information about how to set a model, see Manage projects.

  • The example uses the default public endpoint of the SDK. To use the internal endpoint on an Alibaba Cloud ECS instance in the China (Shanghai) region, set the internal endpoint when you create the NlsClient object:

    client = new NlsClient("ws://nls-gateway-cn-shanghai-internal.aliyuncs.com/ws/v1", accessToken);
  • Before you call the API operation, configure environment variables to load your access credentials. The environment variables for the AccessKey ID, AccessKey secret, and the Intelligent Speech Interaction AppKey are ALIYUN_AK_ID, ALIYUN_AK_SECRET, and NLS_APP_KEY.

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import com.alibaba.nls.client.protocol.InputFormatEnum;
import com.alibaba.nls.client.protocol.NlsClient;
import com.alibaba.nls.client.protocol.SampleRateEnum;
import com.alibaba.nls.client.protocol.asr.SpeechTranscriber;
import com.alibaba.nls.client.protocol.asr.SpeechTranscriberListener;
import com.alibaba.nls.client.protocol.asr.SpeechTranscriberResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * This example demonstrates how to:
 * Call the real-time ASR API operation.
 * Dynamically obtain a token. For details about how to obtain a token, see https://help.aliyun.com/document_detail/450514.html
 * Simulate sending a real-time stream from a local file.
 * Calculate the recognition latency.
 */
public class SpeechTranscriberDemo {
    private String appKey;
    private NlsClient client;
    private static final Logger logger = LoggerFactory.getLogger(SpeechTranscriberDemo.class);

    public SpeechTranscriberDemo(String appKey, String id, String secret, String url) {
        this.appKey = appKey;
        // Create a global NlsClient instance. The default endpoint is the public endpoint of the Alibaba Cloud service.
        // Obtain a token. In a real-world application, make sure to obtain a new token before the current one expires.
        AccessToken accessToken = new AccessToken(id, secret);
        try {
            accessToken.apply();
            System.out.println("get token: " + ", expire time: " + accessToken.getExpireTime());
            if(url.isEmpty()) {
                client = new NlsClient(accessToken.getToken());
            }else {
                client = new NlsClient(url, accessToken.getToken());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static SpeechTranscriberListener getTranscriberListener() {
        SpeechTranscriberListener listener = new SpeechTranscriberListener() {
            // Intermediate recognition result. This message is returned only when setEnableIntermediateResult is set to true.
            @Override
            public void onTranscriptionResultChange(SpeechTranscriberResponse response) {
                System.out.println("task_id: " + response.getTaskId() +
                    ", name: " + response.getName() +
                    // A status code of "20000000" indicates a successful recognition.
                    ", status: " + response.getStatus() +
                    // The sentence number, which increments starting from 1.
                    ", index: " + response.getTransSentenceIndex() +
                    // The current recognition result.
                    ", result: " + response.getTransSentenceText() +
                    // The duration of the processed audio in milliseconds.
                    ", time: " + response.getTransSentenceTime());
            }

            @Override
            public void onTranscriberStart(SpeechTranscriberResponse response) {
                // task_id is the unique identifier for the communication between the client and the server. If you encounter an issue, provide this task_id.
                System.out.println("task_id: " + response.getTaskId() + ", name: " + response.getName() + ", status: " + response.getStatus());
            }

            @Override
            public void onSentenceBegin(SpeechTranscriberResponse response) {
                System.out.println("task_id: " + response.getTaskId() + ", name: " + response.getName() + ", status: " + response.getStatus());

            }

            // A sentence is recognized. The server intelligently segments sentences and returns this message when it detects the end of a sentence.
            @Override
            public void onSentenceEnd(SpeechTranscriberResponse response) {
                System.out.println("task_id: " + response.getTaskId() +
                    ", name: " + response.getName() +
                    // A status code of "20000000" indicates a successful recognition.
                    ", status: " + response.getStatus() +
                    // The sentence number, which increments starting from 1.
                    ", index: " + response.getTransSentenceIndex() +
                    // The current recognition result.
                    ", result: " + response.getTransSentenceText() +
                    // Confidence level
                    ", confidence: " + response.getConfidence() +
                    // Start time
                    ", begin_time: " + response.getSentenceBeginTime() +
                    // The duration of the processed audio in milliseconds.
                    ", time: " + response.getTransSentenceTime());
            }

            // Recognition is complete.
            @Override
            public void onTranscriptionComplete(SpeechTranscriberResponse response) {
                System.out.println("task_id: " + response.getTaskId() + ", name: " + response.getName() + ", status: " + response.getStatus());
            }

            @Override
            public void onFail(SpeechTranscriberResponse response) {
                // task_id is the unique identifier for the communication between the client and the server. If you encounter an issue, provide this task_id.
                System.out.println("task_id: " + response.getTaskId() +  ", status: " + response.getStatus() + ", status_text: " + response.getStatusText());
            }
        };

        return listener;
    }

    // Calculate the equivalent audio duration based on the size of the binary data.
    // sampleRate: 8000 or 16000 is supported.
    public static int getSleepDelta(int dataSize, int sampleRate) {
        // Only 16-bit sampling is supported.
        int sampleBytes = 16;
        // Only a single channel is supported.
        int soundChannel = 1;
        return (dataSize * 10 * 8000) / (160 * sampleRate);
    }

    public void process(String filepath) {
        SpeechTranscriber transcriber = null;
        try {
            // Create an instance and establish a connection.
            transcriber = new SpeechTranscriber(client, getTranscriberListener());
            transcriber.setAppKey(appKey);
            // The audio encoding format of the input.
            transcriber.setFormat(InputFormatEnum.PCM);
            // The audio sample rate of the input.
            transcriber.setSampleRate(SampleRateEnum.SAMPLE_RATE_16K);
            // Specifies whether to return intermediate recognition results.
            transcriber.setEnableIntermediateResult(false);
            // Specifies whether to generate and return punctuation marks.
            transcriber.setEnablePunctuation(true);
            // Specifies whether to normalize the result. For example, convert "one hundred" to "100".
            transcriber.setEnableITN(false);

            // Set the VAD sentence segmentation parameter. Default value: 800 ms. Valid values: 200 ms to 6000 ms.
            //transcriber.addCustomedParam("max_sentence_silence", 600);
            // Specifies whether to enable semantic sentence segmentation.
            //transcriber.addCustomedParam("enable_semantic_sentence_detection",false);
            // Specifies whether to enable filtering of filler words, which is also known as disfluency detection.
            //transcriber.addCustomedParam("disfluency",true);
            // Specifies whether to enable word-level mode.
            //transcriber.addCustomedParam("enable_words",true);
           // Set the VAD noise threshold. The value can range from -1 to +1, such as -0.9, -0.8, 0.2, or 0.9.
            // A value closer to -1 increases the probability of audio being classified as speech, which may cause more noise to be misidentified as speech.
            // A value closer to +1 increases the probability of audio being classified as noise, which may cause more speech segments to be rejected as noise.
            // This is an advanced parameter. Adjust it with caution and perform thorough testing.
            //transcriber.addCustomedParam("speech_noise_threshold",0.3);
            // Set the ID of the custom language model after training.
            //transcriber.addCustomedParam("customization_id","your_custom_language_model_id");
            // Set the ID of the custom hotword model after training.
            //transcriber.addCustomedParam("vocabulary_id","your_custom_hotword_model_id");

            // This method serializes the preceding parameter settings into JSON format, sends them to the server, and waits for confirmation.
            transcriber.start();

            File file = new File(filepath);
            FileInputStream fis = new FileInputStream(file);
            byte[] b = new byte[3200];
            int len;
            while ((len = fis.read(b)) > 0) {
                logger.info("send data pack length: " + len);
                transcriber.send(b, len);
                // This example simulates obtaining and sending a real-time audio stream by reading a local file. Because the file is read quickly, a sleep interval is required here.
                // If you obtain a real-time audio stream, you do not need to set a sleep interval. If the audio sample rate is 8k, set the second parameter to 8000.
                int deltaSleep = getSleepDelta(len, 16000);
                Thread.sleep(deltaSleep);
            }

            // Notify the server that the audio data has been sent and wait for the server to finish processing.
            long now = System.currentTimeMillis();
            logger.info("ASR wait for complete");
            transcriber.stop();
            logger.info("ASR latency : " + (System.currentTimeMillis() - now) + " ms");
        } catch (Exception e) {
            System.err.println(e.getMessage());
        } finally {
            if (null != transcriber) {
                transcriber.close();
            }
        }
    }

    public void shutdown() {
        client.shutdown();
    }

    public static void main(String[] args) throws Exception {
        String appKey = System.getenv().get("NLS_APP_KEY");
        String id = System.getenv().get("ALIYUN_AK_ID");
        String secret = System.getenv().get("ALIYUN_AK_SECRET");
        String url = System.getenv().getOrDefault("NLS_GATEWAY_URL", "wss://nls-gateway-cn-shanghai.aliyuncs.com/ws/v1");
      
        // This example uses a local file to simulate sending real-time stream data. In a real-world application, you can capture or receive a real-time audio stream and send it to the ASR server.
        String filepath = "nls-sample-16k.wav";
        SpeechTranscriberDemo demo = new SpeechTranscriberDemo(appKey, id, secret, url);
        demo.process(filepath);
        demo.shutdown();
    }
}

FAQ

In real-time stream recognition mode, how do I trigger the onTranscriptionComplete callback in the Java SDK?

You can call the stop method to trigger the onTranscriptionComplete callback. The status changes to STATE_STOP_SENT. After the callback is processed, the status changes to STATE_COMPLETE.

When I test the real-time speech recognition and speech synthesis features, where is the corresponding JAR file?

Location of the JAR package for testing the real-time speech recognition and speech synthesis features

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>com.alibaba.nls</groupId>
<artifactId>nls-sdk-java-examples</artifactId>
<version>2.0.0</version>
<relativePath>../pom.xml</relativePath>
</parent>

<groupId>com.alibaba.nls</groupId>
<artifactId>nls-example-tts</artifactId>

<dependencies>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.0.13</version>
</dependency>
<dependency>
<groupId>com.alibaba.nls</groupId>
<artifactId>nls-sdk-tts</artifactId>
<version>${sdk.version}</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.0.0</version>
<configuration>
<archive>
<manifest>
<mainClass>com.alibaba.nls.client.SpeechSynthesizerMultiThreadDemo</mainClass>
</manifest>
</archive>

<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>

</configuration>
<executions>
<execution>
<id>make-assembly</id> <!-- this is used for inheritance merges -->
<phase>package</phase> <!-- bind to the packaging phase -->
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

The Java SDK for real-time recognition reports an error when the NlsClient class connects to the server: ERROR NlsClient:102 - failed to connect to server after 3 tries,error msg is :hostname can't be null. How do I fix this?

If you are not using the demo, you must specify the hostname parameter, which is the endpoint of the Alibaba Cloud Voice Service.

For more information about the endpoint, such as wss://nls-gateway-cn-shanghai.aliyuncs.com/ws/v1, see Java SDK for real-time speech recognition.

When I use the Java demo to recognize a recorded audio file, I get no result. However, recognition works correctly with the audio file from the documentation. How do I fix this?

You can use the file command to check the audio format and verify that it meets the product requirements. The standard format for 8 kHz audio data is WAV format with an 8 kHz sample rate, 16-bit audio bit depth, and a single channel. The standard format for 16 kHz audio data is WAV format with a 16 kHz sample rate, 16-bit audio bit depth, and a single channel. For testing, you can use tools such as Sox or FFmpeg to convert the audio to the standard format. For production use, refer to the relevant product documentation.

The following figure shows an example from the API reference for real-time speech recognition.No recognition result when using the Java demo to recognize an audio file

In the SDKs for short sentence recognition and real-time speech recognition, what do the send method parameters mean and how are they used?

Take the Java SDK as an example. For short sentence recognition and real-time speech recognition, the SDK provides three overloaded send() methods. See the following code:

public void send(InputStream ins);
public void send(InputStream ins, int batchSize, int sleepInterval);
public void send(byte[] data);

When you use these methods, you must continuously send audio data to the server in real time.

The demo simulates the speed of a real-time audio stream by sending audio from a file. Typically, audio data is sent in intervals of 100 ms or 200 ms (sleepInterval). The amount of data (batchSize) depends on the sample rate. A long sending interval can cause high latency and disconnections, while a short sending interval can consume excessive server and network resources. You can experiment to find a suitable value.

In the second method, ins is the simulated audio stream, and you need to control the sending rate. For data in ins, send 3,200 bytes every 100 ms for a 16,000 Hz sample rate. Example call:

public void send(ins, 3200, 100); // 16 kHz audio

In the third method, data is the data sent in a single call. You must control the interval of the recursive invocation. Example call:

recognizer.send(data); // 100 ms of audio data
try {
 Thread.sleep(100);
} catch (InterruptedException e) {
 e.printStackTrace();
}

How do I analyze latency issues using SDK logs?

Take the Java SDK logs as an example.

  • The latency of short sentence recognition is the time from when a sentence is finished to when the final recognition result is received.

    Search for the keywords StopRecognition and RecognitionCompleted in the logs to find the log entries that indicate when audio sending is complete and when short sentence recognition is complete. The time difference between these entries is the latency recorded by the SDK. In the following logs, the latency is 984 - 844 = 140 ms.

    14:24:44.844 DEBUG [           main] [c.a.n.c.transport.netty4.NettyConnection] thread:1,send:{"header":{"namespace":"SpeechRecognizer","name":"StopRecognition","message_id":"bccac69b505f4e2897d12940e5b38953","appkey":"FWpPCaVYDRp6J1rO","task_id":"8c5c28d9a40c4a229a5345c09bc9c968"}}
    14:24:44.984 DEBUG [ntLoopGroup-2-1] [  c.a.n.c.p.asr.SpeechRecognizerListener] on message:{"header":{"namespace":"SpeechRecognizer","name":"RecognitionCompleted","status":20000000,"message_id":"2869e93427b9429190206123b7a3d397","task_id":"8c5c28d9a40c4a229a5345c09bc9c968","status_text":"Gateway:SUCCESS:Success."},"payload":{"result":"The weather in Beijing.","duration":2959}}
  • For speech synthesis, focus on the first-packet latency. This is the time from when the synthesis request is sent to when the first audio packet is received.

    Search for the keyword send in the logs to find the log entry for the request and the subsequent log entry for receiving an audio packet. The time difference between these entries is the first-packet latency recorded by the SDK. In the following logs, the latency is 1035 - 813 = 222 ms.

    14:32:13.813 DEBUG [           main] [c.a.n.c.transport.netty4.NettyConnection] thread:1,send:{"payload":{"volume":50,"voice":"Ruoxi","sample_rate":8000,"format":"wav","text":"A country is composed of four elements: territory, people, culture, and government. The term country is also a term in political geography. In a broad sense, a country refers to a social group that has a common language, culture, ancestry, territory, government, or history. In a narrow sense, a country is a form of community formed by a group of people within a certain range."},"context":{"sdk":{"name":"nls-sdk-java","version":"2.1.0"},"network":{"upgrade_cost":160,"connect_cost":212}},"header":{"namespace":"SpeechSynthesizer","name":"StartSynthesis","message_id":"6bf2a84444434c0299974d8242380d6c","appkey":"FWpPCaVYDRp6J1rO","task_id":"affa5c90986e4378907fbf49eddd283a"}}
    14:32:14.035  INFO [ntLoopGroup-2-1] [  c.a.n.c.protocol.tts.SpeechSynthesizer] write array:6896
  • The logs for the real-time speech recognition SDK are similar to those for short sentence recognition. You can calculate the end-of-speech latency from the logs. The keywords are StopTranscription and TranscriptionCompleted.

  • For RESTful API access, the client-side logs do not show latency. You must write your own code to measure it or check the server-side logs.

The Java SDK cannot find the com.alibaba JAR file. How do I install the Alibaba Cloud SDK for Java?

For instructions on how to install the Alibaba Cloud SDK for Java, see V1.0 Java SDK.

When I use the Java SDK and pass the AccessKey ID and AccessKey secret of my Alibaba Cloud account, the client reports the error org.json.JSONArray.iterator()Ljava/util/Iterator. How do I fix this?

Make sure that all dependency packages are included. Find and add the following two dependency JAR packages:

<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>