13 package ai.picovoice.rhinodemo;
16 import org.apache.commons.cli.*;
19 import java.io.IOException;
20 import java.io.ByteArrayInputStream;
21 import java.io.ByteArrayOutputStream;
22 import java.nio.ByteBuffer;
23 import java.nio.ByteOrder;
25 import javax.sound.sampled.*;
28 public static void runDemo(String accessKey, String contextPath, String libraryPath, String modelPath,
29 float sensitivity,
int audioDeviceIndex, String outputPath,
boolean requireEndpoint) {
32 File outputFile =
null;
33 ByteArrayOutputStream outputStream =
null;
34 long totalBytesCaptured = 0;
35 AudioFormat format =
new AudioFormat(16000
f, 16, 1,
true,
false);
38 DataLine.Info dataLineInfo =
new DataLine.Info(TargetDataLine.class, format);
39 TargetDataLine micDataLine;
42 micDataLine.open(format);
43 }
catch (LineUnavailableException e) {
44 System.err.println(
"Failed to get a valid capture device. Use --show_audio_devices to " +
45 "show available capture devices and their indices");
62 if (outputPath !=
null) {
63 outputFile =
new File(outputPath);
64 outputStream =
new ByteArrayOutputStream();
70 System.out.println(
"Press enter to stop recording.");
71 System.out.println(
"Listening...");
75 ByteBuffer captureBuffer = ByteBuffer.allocate(frameLength * 2);
76 captureBuffer.order(ByteOrder.LITTLE_ENDIAN);
77 short[] rhinoBuffer =
new short[frameLength];
80 while (System.in.available() == 0) {
83 numBytesRead = micDataLine.read(captureBuffer.array(), 0, captureBuffer.capacity());
84 totalBytesCaptured += numBytesRead;
87 if (outputStream !=
null) {
88 outputStream.write(captureBuffer.array(), 0, numBytesRead);
92 if (numBytesRead != frameLength * 2) {
97 captureBuffer.asShortBuffer().get(rhinoBuffer);
100 boolean isFinalized = rhino.
process(rhinoBuffer);
106 System.out.println(
"{");
107 System.out.println(String.format(
" intent : '%s'", inference.
getIntent()));
108 System.out.println(
" slots : {");
109 for (Map.Entry<String, String> slot : inference.
getSlots().entrySet()) {
110 System.out.println(String.format(
" %s : '%s'", slot.getKey(), slot.getValue()));
112 System.out.println(
" }");
113 System.out.println(
"}");
115 System.out.println(
"Didn't understand the command.");
119 System.out.println(
"Stopping...");
120 }
catch (Exception e) {
121 System.err.println(e.toString());
123 if (outputStream !=
null && outputFile !=
null) {
126 ByteArrayInputStream writeArray =
new ByteArrayInputStream(outputStream.toByteArray());
127 AudioInputStream writeStream =
new AudioInputStream(writeArray, format, totalBytesCaptured / format.getFrameSize());
130 AudioSystem.write(writeStream, AudioFileFormat.Type.WAVE, outputFile);
131 }
catch (IOException e) {
132 System.err.printf(
"Failed to write audio to '%s'.\n", outputFile.getPath());
146 Mixer.Info[] allMixerInfo = AudioSystem.getMixerInfo();
147 Line.Info captureLine =
new Line.Info(TargetDataLine.class);
149 for (
int i = 0; i < allMixerInfo.length; i++) {
152 Mixer mixer = AudioSystem.getMixer(allMixerInfo[i]);
153 if (mixer.isLineSupported(captureLine)) {
154 System.out.printf(
"Device %d: %s\n", i, allMixerInfo[i].
getName());
161 if (!AudioSystem.isLineSupported(dataLineInfo)) {
162 throw new LineUnavailableException(
"Default capture device does not support the audio " +
163 "format required by Picovoice (16kHz, 16-bit, linearly-encoded, single-channel PCM).");
166 return (TargetDataLine) AudioSystem.getLine(dataLineInfo);
169 private static TargetDataLine
getAudioDevice(
int deviceIndex, DataLine.Info dataLineInfo)
throws LineUnavailableException {
171 if (deviceIndex >= 0) {
173 Mixer.Info mixerInfo = AudioSystem.getMixerInfo()[deviceIndex];
174 Mixer mixer = AudioSystem.getMixer(mixerInfo);
176 if (mixer.isLineSupported(dataLineInfo)) {
177 return (TargetDataLine) mixer.getLine(dataLineInfo);
179 System.err.printf(
"Audio capture device at index %s does not support the audio format required by " +
180 "Picovoice. Using default capture device.", deviceIndex);
182 }
catch (Exception e) {
183 System.err.printf(
"No capture device found at index %s. Using default capture device.", deviceIndex);
194 CommandLineParser parser =
new DefaultParser();
195 HelpFormatter formatter =
new HelpFormatter();
199 cmd = parser.parse(options,
args);
200 }
catch (ParseException e) {
201 System.out.println(e.getMessage());
202 formatter.printHelp(
"rhinomicdemo", options);
207 if (
cmd.hasOption(
"help")) {
208 formatter.printHelp(
"rhinomicdemo", options);
212 if (
cmd.hasOption(
"show_audio_devices")) {
217 String accessKey =
cmd.getOptionValue(
"access_key");
218 String libraryPath =
cmd.getOptionValue(
"library_path");
219 String modelPath =
cmd.getOptionValue(
"model_path");
220 String contextPath =
cmd.getOptionValue(
"context_path");
221 String sensitivityStr =
cmd.getOptionValue(
"sensitivity");
222 String audioDeviceIndexStr =
cmd.getOptionValue(
"audio_device_index");
223 String outputPath =
cmd.getOptionValue(
"output_path");
224 String requireEndpointValue =
cmd.getOptionValue(
"require_endpoint");
226 if (accessKey ==
null || accessKey.length() == 0) {
227 throw new IllegalArgumentException(
"AccessKey is required for Rhino.");
231 float sensitivity = 0.5f;
232 if (sensitivityStr !=
null) {
234 sensitivity = Float.parseFloat(sensitivityStr);
235 }
catch (Exception e) {
236 throw new IllegalArgumentException(
"Failed to parse sensitivity value. " +
237 "Must be a floating-point number between [0,1].");
240 if (sensitivity < 0 || sensitivity > 1) {
241 throw new IllegalArgumentException(String.format(
"Failed to parse sensitivity value (%s). " +
242 "Must be a floating-point number between [0,1].", sensitivity));
246 if(contextPath ==
null){
247 throw new IllegalArgumentException(
"No context file provided. This is a required argument.");
249 File contextFile =
new File(contextPath);
250 if (!contextFile.exists()) {
251 throw new IllegalArgumentException(String.format(
"Context file at path '%s' does not exist", contextPath));
254 if (libraryPath ==
null) {
258 if (modelPath ==
null) {
262 int audioDeviceIndex = -1;
263 if (audioDeviceIndexStr !=
null) {
265 audioDeviceIndex = Integer.parseInt(audioDeviceIndexStr);
266 if (audioDeviceIndex < 0) {
267 throw new IllegalArgumentException(String.format(
"Audio device index %s is not a " +
268 "valid positive integer.", audioDeviceIndexStr));
270 }
catch (Exception e) {
271 throw new IllegalArgumentException(String.format(
"Audio device index '%s' is not a " +
272 "valid positive integer.", audioDeviceIndexStr));
276 boolean requireEndpoint =
true;
277 if (requireEndpointValue !=
null && requireEndpointValue.toLowerCase().equals(
"false")) {
278 requireEndpoint =
false;
281 runDemo(accessKey, contextPath, libraryPath, modelPath, sensitivity, audioDeviceIndex, outputPath, requireEndpoint);
285 Options options =
new Options();
287 options.addOption(Option.builder(
"a")
288 .longOpt(
"access_key")
290 .desc(
"AccessKey obtained from Picovoice Console (https://picovoice.ai/console/).")
293 options.addOption(Option.builder(
"c")
294 .longOpt(
"context_path")
296 .desc(
"Absolute path to context file.")
299 options.addOption(Option.builder(
"l")
300 .longOpt(
"library_path")
302 .desc(
"Absolute path to the Rhino native runtime library.")
305 options.addOption(Option.builder(
"m")
306 .longOpt(
"model_path")
308 .desc(
"Absolute path to the file containing model parameters.")
311 options.addOption(Option.builder(
"s")
312 .longOpt(
"sensitivity")
314 .desc(
"Inference sensitivity. It should be a number within [0, 1]. A higher sensitivity value results in " +
315 "fewer misses at the cost of (potentially) increasing the erroneous inference rate. " +
316 "If not set 0.5 will be used.")
319 options.addOption(Option.builder(
"e")
320 .longOpt(
"require_endpoint")
322 .desc(
"If set to `false`, Rhino does not require an endpoint (chunk of silence) before " +
323 "finishing inference.")
326 options.addOption(Option.builder(
"o")
327 .longOpt(
"output_path")
329 .desc(
"Absolute path to recorded audio for debugging.")
332 options.addOption(Option.builder(
"di")
333 .longOpt(
"audio_device_index")
335 .desc(
"Index of input audio device.")
338 options.addOption(
new Option(
"sd",
"show_audio_devices",
false,
"Print available recording devices."));
339 options.addOption(
new Option(
"h",
"help",
false,
""));