Java SDK

Updated at:

This topic describes how to install the real-time speech recognition SDK for Java and use a complete example to stream audio and obtain recognition results.

Prerequisites

  • Before you use the SDK, read the real-time speech recognition API overview.

  • Starting from version 2.1.0, nls-sdk-long-asr was renamed nls-sdk-transcriber. When you upgrade, remove nls-sdk-long-asr and add the required callbacks as prompted by the compiler.

Download and install the SDK

  1. Download the SDK demo.

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

    After you extract the demo, run mvn package in the pom directory. The command generates the executable JAR file nls-example-transcriber-2.0.0-jar-with-dependencies.jar in the target directory. You can copy the JAR file to the target server for functional validation and stress testing.

  2. Validate the service.

    Run the following command and provide the required parameters as prompted. The logs/nls.log file is generated in the directory where the command runs.

    java -cp nls-example-transcriber-2.0.0-jar-with-dependencies.jar com.alibaba.nls.client.SpeechTranscriberDemo
  3. Run a stress test.

    Run the following command and provide the required parameters as prompted. The service URL is wss://nls-gateway-cn-shanghai.aliyuncs.com/ws/v1.

    Use a PCM audio file with a sample rate of 16 kHz. Set the concurrency based on the service capacity that you purchased.

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

    Charges are incurred if the stress test uses more than two concurrent calls.

Key classes

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

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

  • SpeechTranscriberListener: A real-time speech recognition result listener. The class is not thread-safe.

For more information, see the Java API reference.

Important

Considerations for SDK calls:

  • NlsClient uses the Netty framework. Creating an NlsClient object consumes time and resources, but the object can be reused. Align the creation and shutdown of NlsClient with the lifecycle of the application.

  • A SpeechTranscriber object cannot be reused. Each recognition task requires a separate SpeechTranscriber object. For example, to run recognition tasks for N audio files, create N SpeechTranscriber objects.

  • Each SpeechTranscriberListener object corresponds to one SpeechTranscriber object. Do not use the same SpeechTranscriberListener object for multiple SpeechTranscriber objects. Otherwise, the recognition tasks cannot be distinguished.

  • The SDK for Java depends on Netty. If the application also depends on Netty, use Netty 4.1.17.Final or later.

Sample code

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

    The sample audio has a sample rate of 16,000 Hz. In the console, set the model of the project associated with the AppKey to Universal Model to obtain correct recognition results. If you use other audio, select a model that supports the corresponding audio scenario. For more information about model settings, see Manage projects.

  • The sample uses the public service URL by default. To access the service from an ECS instance in the China (Shanghai) region over an internal network, specify the following internal URL 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, configure environment variables for the access credentials. Set the AccessKey ID, AccessKey secret, and AppKey in ALIYUN_AK_ID, ALIYUN_AK_SECRET, and NLS_APP_KEY, respectively.

  • The sample connects to wss://nls-gateway-cn-shanghai.aliyuncs.com/ws/v1 by default. To use another service URL, set the NLS_GATEWAY_URL environment variable.

The sample obtains a token dynamically at runtime. For more information, see Obtain a token.

The value of max_sentence_silence ranges from 200 to 6000 milliseconds.

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import com.alibaba.nls.client.AccessToken;
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 speech recognition API.
 * Simulate a real-time audio stream by using a local file.
 * Measure 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 one NlsClient instance for the lifecycle of the application.
        AccessToken accessToken = new AccessToken(id, secret);
        try {
            accessToken.apply();
            System.out.println("get token: " + ", expire time: " + accessToken.getExpireTime());
            client = new NlsClient(url, accessToken.getToken());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static SpeechTranscriberListener getTranscriberListener() {
        SpeechTranscriberListener listener = new SpeechTranscriberListener() {
            // This callback is triggered when an intermediate result is available.
            // The callback is triggered only if setEnableIntermediateResult is set to true.
            @Override
            public void onTranscriptionResultChange(SpeechTranscriberResponse response) {
                System.out.println("task_id: " + response.getTaskId() +
                    ", name: " + response.getName() +
                    // The status code 20000000 indicates a successful request.
                    ", status: " + response.getStatus() +
                    // The sentence index starts from 1.
                    ", index: " + response.getTransSentenceIndex() +
                    // The current recognition result.
                    ", result: " + response.getTransSentenceText() +
                    // The duration of processed audio in milliseconds.
                    ", time: " + response.getTransSentenceTime());
            }

            @Override
            public void onTranscriberStart(SpeechTranscriberResponse response) {
                // task_id uniquely identifies communication between the client and server.
                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());

            }

            // This callback is triggered when the server detects the end of a sentence.
            @Override
            public void onSentenceEnd(SpeechTranscriberResponse response) {
                System.out.println("task_id: " + response.getTaskId() +
                    ", name: " + response.getName() +
                    // The status code 20000000 indicates a successful request.
                    ", status: " + response.getStatus() +
                    // The sentence index starts from 1.
                    ", index: " + response.getTransSentenceIndex() +
                    // The current recognition result.
                    ", result: " + response.getTransSentenceText() +
                    // The confidence score.
                    ", confidence: " + response.getConfidence() +
                    // The sentence start time.
                    ", begin_time: " + response.getSentenceBeginTime() +
                    // The duration of 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 uniquely identifies communication between the client and server.
                System.out.println("task_id: " + response.getTaskId() +  ", status: " + response.getStatus() + ", status_text: " + response.getStatusText());
            }
        };

        return listener;
    }

    // Calculate the audio duration that corresponds to the binary data size.
    // sampleRate supports 8000 or 16000.
    public static int getSleepDelta(int dataSize, int sampleRate) {
        // Only 16-bit sampling is supported.
        int sampleBytes = 16;
        // Only mono audio 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);
            // Set the input audio format.
            transcriber.setFormat(InputFormatEnum.PCM);
            // Set the input audio sample rate.
            transcriber.setSampleRate(SampleRateEnum.SAMPLE_RATE_16K);
            // Specify whether to return intermediate recognition results.
            transcriber.setEnableIntermediateResult(false);
            // Specify whether to add punctuation to recognition results.
            transcriber.setEnablePunctuation(true);
            // Specify whether to normalize recognition results, such as converting words to numbers.
            transcriber.setEnableITN(false);

            // Set the VAD sentence silence threshold. The default value is 800 ms.
            //transcriber.addCustomedParam("max_sentence_silence", 600);
            // Specify whether to use semantic sentence detection.
            //transcriber.addCustomedParam("enable_semantic_sentence_detection",false);
            // Specify whether to filter disfluencies.
            //transcriber.addCustomedParam("disfluency",true);
            // Specify whether to enable word-level information.
            //transcriber.addCustomedParam("enable_words",true);
            // Set the VAD noise threshold. Valid values range from -1 to +1.
            // A value closer to -1 increases the probability that noise is treated as speech.
            // A value closer to +1 increases the probability that speech is treated as noise.
            // This is an advanced parameter. Test the recognition results after each adjustment.
            //transcriber.addCustomedParam("speech_noise_threshold",0.3);
            // Set the ID of a trained custom language model.
            //transcriber.addCustomedParam("customization_id","custom-language-model-id");
            // Set the ID of a trained custom hotword vocabulary.
            //transcriber.addCustomedParam("vocabulary_id","custom-vocabulary-id");

            // Serialize the parameters to JSON, send them to the server, and wait 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);
                // Reading a local file is faster than a real-time stream, so the sample pauses between packets.
                // For a live stream, remove the pause. For 8 kHz audio, pass 8000 as the second argument.
                int deltaSleep = getSleepDelta(len, 16000);
                Thread.sleep(deltaSleep);
            }

            // Notify the server that all audio data is sent and wait for processing to complete.
            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 sample uses a local file to simulate a real-time audio stream.
        // In production, capture or receive an 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

How do I trigger the onTranscriptionComplete callback in real-time streaming recognition?

Call stop to trigger onTranscriptionComplete. The state changes to STATE_STOP_SENT, and then to STATE_COMPLETE after the callback is processed.

Where is the JAR file generated when I test real-time speech recognition or speech synthesis?

After you run mvn package, the JAR file is generated in the target directory of the corresponding sample module. The following POM configuration uses a speech synthesis sample and the Maven Assembly Plugin to generate a JAR file that contains all dependencies.

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

What do I do if real-time speech recognition reports hostname can't be null?

If you do not use the demo, specify a service URL when you create the NlsClient object.

The service URL is wss://nls-gateway-cn-shanghai.aliyuncs.com/ws/v1.

Why does the Java demo recognize the sample audio but not my audio file?

Run the file command to inspect the audio format and verify that the format meets the service requirements. Standard 8 kHz audio uses an 8 kHz sample rate, 16-bit samples, mono audio, and the WAV format. Standard 16 kHz audio uses a 16 kHz sample rate, 16-bit samples, mono audio, and the WAV format. For testing, use SoX or FFmpeg to convert the audio to a standard format. For more information, see the real-time speech recognition API overview.

How do I use the send methods in the short sentence and real-time speech recognition SDKs?

For Java, the short sentence recognition and real-time speech recognition SDKs each provide the following three overloaded send() methods:

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

Continuously send audio data to the server in real time when you use these methods.

The demo uses an audio file to simulate a real-time audio stream. It typically sends 100 ms or 200 ms of audio data in each interval specified by sleepInterval. The value of batchSize depends on the sample rate. An interval that is too long increases latency and may disconnect the session. An interval that is too short consumes more server and network resources. Test different values to determine appropriate settings.

In the second method, ins is a simulated audio stream whose sending rate must be controlled. For 16 kHz audio, send 3,200 bytes from ins every 100 ms. Example:

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

In the third method, data contains the data sent in one call. Control the interval between calls in the loop. Example:

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

How do I analyze latency by using SDK logs?

The following examples use SDK for Java logs.

  • For short sentence recognition, latency is the time from the end of the utterance to the receipt of the final recognition result.

    Search the logs for StopRecognition and RecognitionCompleted to locate when audio sending ends and when recognition completes. The difference between the timestamps is the client-side latency. In the following example, 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":"What's the weather like in Beijing?","duration":2959}}
  • For speech synthesis, focus on first-packet latency, which is the time from sending the synthesis request to receiving the first audio packet.

    Search the logs for send and locate the subsequent log entry for the first received audio packet. The difference between the timestamps is the client-side first-packet latency. In the following example, 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":"Welcome to Alibaba Cloud Intelligent Speech Interaction."},"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
  • Real-time speech recognition logs are similar to short sentence recognition logs. Calculate end-of-audio latency by using the StopTranscription and TranscriptionCompleted entries.

  • For RESTful access, client logs do not contain latency information. Add timing logic to the client or inspect server logs.

How do I install Alibaba Cloud SDK for Java if the com.alibaba JAR file cannot be found?

See the installation instructions in V1.0 Java SDK.

What do I do if the client reports org.json.JSONArray.iterator()Ljava/util/Iterator?

Check whether the dependencies are complete. Add the following two dependencies if they are missing.

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