diff --git a/speech/grpc/pom.xml b/speech/grpc/pom.xml index ebeba6abf0c..7815ea0479c 100644 --- a/speech/grpc/pom.xml +++ b/speech/grpc/pom.xml @@ -152,6 +152,17 @@ limitations under the License. 1.1.33.Fork14 ${tcnative.classifier} + + com.google.truth + truth + 0.28 + test + + + log4j + log4j + 1.2.17 + diff --git a/speech/grpc/resources/audio32KHz.raw b/speech/grpc/resources/audio32KHz.raw new file mode 100644 index 00000000000..6b52fc326f8 Binary files /dev/null and b/speech/grpc/resources/audio32KHz.raw differ diff --git a/speech/grpc/src/main/java/com/examples/cloud/speech/StreamingRecognizeClient.java b/speech/grpc/src/main/java/com/examples/cloud/speech/StreamingRecognizeClient.java index dad9c2d27fe..a302f2415c4 100644 --- a/speech/grpc/src/main/java/com/examples/cloud/speech/StreamingRecognizeClient.java +++ b/speech/grpc/src/main/java/com/examples/cloud/speech/StreamingRecognizeClient.java @@ -16,6 +16,8 @@ package com.examples.cloud.speech; +import static org.apache.log4j.ConsoleAppender.SYSTEM_OUT; + import com.google.cloud.speech.v1beta1.RecognitionConfig; import com.google.cloud.speech.v1beta1.RecognitionConfig.AudioEncoding; import com.google.cloud.speech.v1beta1.SpeechGrpc; @@ -26,7 +28,6 @@ import com.google.protobuf.TextFormat; import io.grpc.ManagedChannel; -import io.grpc.Status; import io.grpc.stub.StreamObserver; import org.apache.commons.cli.CommandLine; @@ -35,6 +36,10 @@ import org.apache.commons.cli.OptionBuilder; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; +import org.apache.log4j.ConsoleAppender; +import org.apache.log4j.Level; +import org.apache.log4j.Logger; +import org.apache.log4j.SimpleLayout; import java.io.File; import java.io.FileInputStream; @@ -43,8 +48,7 @@ import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import java.util.logging.Level; -import java.util.logging.Logger; + /** * Client that sends streaming audio to Speech.Recognize and returns streaming transcript. @@ -60,6 +64,9 @@ public class StreamingRecognizeClient { private final SpeechGrpc.SpeechStub speechClient; + private static final int BYTES_PER_BUFFER = 3200; //buffer size in bytes + private static final int BYTES_PER_SAMPLE = 2; //bytes per sample for LINEAR16 + private static final List OAUTH2_SCOPES = Arrays.asList("https://www.googleapis.com/auth/cloud-platform"); @@ -73,6 +80,13 @@ public StreamingRecognizeClient(ManagedChannel channel, String file, int samplin this.channel = channel; speechClient = SpeechGrpc.newStub(channel); + + //Send log4j logs to Console + //If you are going to run this on GCE, you might wish to integrate with gcloud-java logging. + //See https://github.com/GoogleCloudPlatform/gcloud-java/blob/master/README.md#stackdriver-logging-alpha + + ConsoleAppender appender = new ConsoleAppender(new SimpleLayout(), SYSTEM_OUT); + logger.addAppender(appender); } public void shutdown() throws InterruptedException { @@ -91,8 +105,7 @@ public void onNext(StreamingRecognizeResponse response) { @Override public void onError(Throwable error) { - Status status = Status.fromThrowable(error); - logger.log(Level.WARNING, "recognize failed: {0}", status); + logger.log(Level.WARN, "recognize failed: {0}", error); finishLatch.countDown(); } @@ -127,9 +140,12 @@ public void onCompleted() { // Open audio file. Read and send sequential buffers of audio as additional RecognizeRequests. FileInputStream in = new FileInputStream(new File(file)); // For LINEAR16 at 16000 Hz sample rate, 3200 bytes corresponds to 100 milliseconds of audio. - byte[] buffer = new byte[3200]; + byte[] buffer = new byte[BYTES_PER_BUFFER]; int bytesRead; int totalBytes = 0; + int samplesPerBuffer = BYTES_PER_BUFFER / BYTES_PER_SAMPLE; + int samplesPerMillis = samplingRate / 1000; + while ((bytesRead = in.read(buffer)) != -1) { totalBytes += bytesRead; StreamingRecognizeRequest request = @@ -138,8 +154,7 @@ public void onCompleted() { .build(); requestObserver.onNext(request); // To simulate real-time audio, sleep after sending each audio buffer. - // For 16000 Hz sample rate, sleep 100 milliseconds. - Thread.sleep(samplingRate / 160); + Thread.sleep(samplesPerBuffer / samplesPerMillis); } logger.info("Sent " + totalBytes + " bytes from audio file: " + file); } catch (RuntimeException e) { diff --git a/speech/grpc/src/test/java/com/examples/cloud/speech/StreamingRecognizeClientTest.java b/speech/grpc/src/test/java/com/examples/cloud/speech/StreamingRecognizeClientTest.java new file mode 100644 index 00000000000..c42e521ebff --- /dev/null +++ b/speech/grpc/src/test/java/com/examples/cloud/speech/StreamingRecognizeClientTest.java @@ -0,0 +1,87 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.examples.cloud.speech; + +import static com.google.common.truth.Truth.assertThat; + +import io.grpc.ManagedChannel; +import org.apache.log4j.Logger; +import org.apache.log4j.SimpleLayout; +import org.apache.log4j.WriterAppender; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.io.File; +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; +import java.net.URI; +import java.nio.file.Path; +import java.nio.file.Paths; + + +/** + * Unit tests for {@link StreamingRecognizeClient }. + */ +@RunWith(JUnit4.class) +public class StreamingRecognizeClientTest { + private Writer writer; + private WriterAppender appender; + + @Before + public void setUp() { + writer = new StringWriter(); + appender = new WriterAppender(new SimpleLayout(), writer); + Logger.getRootLogger().addAppender(appender); + } + + @After + public void tearDown() { + Logger.getRootLogger().removeAppender(appender); + } + + @Test + public void test16KHzAudio() throws InterruptedException, IOException { + URI uri = new File("resources/audio.raw").toURI(); + Path path = Paths.get(uri); + + String host = "speech.googleapis.com"; + int port = 443; + ManagedChannel channel = AsyncRecognizeClient.createChannel(host, port); + StreamingRecognizeClient client = new StreamingRecognizeClient(channel, path.toString(), 16000); + + client.recognize(); + assertThat(writer.toString()).contains("transcript: \"how old is the Brooklyn Bridge\""); + } + + @Test + public void test32KHzAudio() throws InterruptedException, IOException { + URI uri = new File("resources/audio32KHz.raw").toURI(); + Path path = Paths.get(uri); + + String host = "speech.googleapis.com"; + int port = 443; + ManagedChannel channel = AsyncRecognizeClient.createChannel(host, port); + StreamingRecognizeClient client = new StreamingRecognizeClient(channel, path.toString(), 32000); + + client.recognize(); + assertThat(writer.toString()).contains("transcript: \"how old is the Brooklyn Bridge\""); + } +}