porcupine/demo/java/src/ai/picovoice/porcupinedemo/MicDemo.java
Go to the documentation of this file.
1 /*
2  Copyright 2018-2021 Picovoice Inc.
3 
4  You may not use this file except in compliance with the license. A copy of the license is
5  located in the "LICENSE" file accompanying this source.
6 
7  Unless required by applicable law or agreed to in writing, software distributed under the
8  License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
9  express or implied. See the License for the specific language governing permissions and
10  limitations under the License.
11 */
12 
13 package ai.picovoice.porcupinedemo;
14 
16 import org.apache.commons.cli.*;
17 
18 import javax.sound.sampled.*;
19 import java.io.ByteArrayInputStream;
20 import java.io.ByteArrayOutputStream;
21 import java.io.File;
22 import java.io.IOException;
23 import java.nio.ByteBuffer;
24 import java.nio.ByteOrder;
25 import java.time.LocalTime;
26 import java.time.format.DateTimeFormatter;
27 import java.util.*;
28 
29 public class MicDemo {
30  public static void runDemo(String accessKey, String libPath, String modelPath,
31  String[] keywordPaths, float[] sensitivities,
32  int audioDeviceIndex, String outputPath) {
33 
34  // create keywords from keyword_paths
35  String[] keywords = new String[keywordPaths.length];
36  for (int i = 0; i < keywordPaths.length; i++) {
37  File keywordFile = new File(keywordPaths[i]);
38  if (!keywordFile.exists())
39  throw new IllegalArgumentException(String.format("Keyword file at '%s' " +
40  "does not exist", keywordPaths[i]));
41  keywords[i] = keywordFile.getName().split("_")[0];
42  }
43 
44  // for file output
45  File outputFile = null;
46  ByteArrayOutputStream outputStream = null;
47  long totalBytesCaptured = 0;
48  AudioFormat format = new AudioFormat(16000f, 16, 1, true, false);
49 
50  // get audio capture device
51  DataLine.Info dataLineInfo = new DataLine.Info(TargetDataLine.class, format);
52  TargetDataLine micDataLine;
53  try {
54  micDataLine = getAudioDevice(audioDeviceIndex, dataLineInfo);
55  micDataLine.open(format);
56  } catch (LineUnavailableException e) {
57  System.err.println("Failed to get a valid capture device. Use --show_audio_devices to " +
58  "show available capture devices and their indices");
59  System.exit(1);
60  return;
61  }
62 
63  Porcupine porcupine = null;
64  try {
65 
66  porcupine = new Porcupine.Builder()
67  .setAccessKey(accessKey)
68  .setLibraryPath(libPath)
69  .setModelPath(modelPath)
70  .setKeywordPaths(keywordPaths)
71  .setSensitivities(sensitivities)
72  .build();
73 
74  if (outputPath != null) {
75  outputFile = new File(outputPath);
76  outputStream = new ByteArrayOutputStream();
77  }
78 
79  micDataLine.start();
80  System.out.print("Listening for {");
81  for (int i = 0; i < keywords.length; i++) {
82  System.out.printf(" %s(%.02f)", keywords[i], sensitivities[i]);
83  }
84  System.out.print(" }\n");
85  System.out.println("Press enter to stop recording...");
86 
87  // buffers for processing audio
88  int frameLength = porcupine.getFrameLength();
89  ByteBuffer captureBuffer = ByteBuffer.allocate(frameLength * 2);
90  captureBuffer.order(ByteOrder.LITTLE_ENDIAN);
91  short[] porcupineBuffer = new short[frameLength];
92 
93  int numBytesRead;
94  while (System.in.available() == 0) {
95 
96  // read a buffer of audio
97  numBytesRead = micDataLine.read(captureBuffer.array(), 0, captureBuffer.capacity());
98  totalBytesCaptured += numBytesRead;
99 
100  // write to output if we're recording
101  if (outputStream != null) {
102  outputStream.write(captureBuffer.array(), 0, numBytesRead);
103  }
104 
105  // don't pass to porcupine if we don't have a full buffer
106  if (numBytesRead != frameLength * 2) {
107  continue;
108  }
109 
110  // copy into 16-bit buffer
111  captureBuffer.asShortBuffer().get(porcupineBuffer);
112 
113  // process with porcupine
114  int result = porcupine.process(porcupineBuffer);
115  if (result >= 0) {
116  System.out.printf("[%s] Detected '%s'\n",
117  LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")), keywords[result]);
118  }
119  }
120  } catch (Exception e) {
121  System.err.println(e.toString());
122  } finally {
123  if (outputStream != null && outputFile != null) {
124 
125  // need to transfer to input stream to write
126  ByteArrayInputStream writeArray = new ByteArrayInputStream(outputStream.toByteArray());
127  AudioInputStream writeStream = new AudioInputStream(writeArray, format, totalBytesCaptured / format.getFrameSize());
128 
129  try {
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());
133  e.printStackTrace();
134  }
135  }
136 
137  if (porcupine != null) {
138  porcupine.delete();
139  }
140  }
141  }
142 
143  private static void showAudioDevices() {
144 
145  // get available audio devices
146  Mixer.Info[] allMixerInfo = AudioSystem.getMixerInfo();
147  Line.Info captureLine = new Line.Info(TargetDataLine.class);
148 
149  for (int i = 0; i < allMixerInfo.length; i++) {
150 
151  // check if supports capture in the format we need
152  Mixer mixer = AudioSystem.getMixer(allMixerInfo[i]);
153  if (mixer.isLineSupported(captureLine)) {
154  System.out.printf("Device %d: %s\n", i, allMixerInfo[i].getName());
155  }
156  }
157  }
158 
159  private static TargetDataLine getDefaultCaptureDevice(DataLine.Info dataLineInfo) throws LineUnavailableException {
160 
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).");
164  }
165 
166  return (TargetDataLine) AudioSystem.getLine(dataLineInfo);
167  }
168 
169  private static TargetDataLine getAudioDevice(int deviceIndex, DataLine.Info dataLineInfo) throws LineUnavailableException {
170 
171  if (deviceIndex >= 0) {
172  try {
173  Mixer.Info mixerInfo = AudioSystem.getMixerInfo()[deviceIndex];
174  Mixer mixer = AudioSystem.getMixer(mixerInfo);
175 
176  if (mixer.isLineSupported(dataLineInfo)) {
177  return (TargetDataLine) mixer.getLine(dataLineInfo);
178  } else {
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);
181  }
182  } catch (Exception e) {
183  System.err.printf("No capture device found at index %s. Using default capture device.", deviceIndex);
184  }
185  }
186 
187  // use default capture device if we couldn't get the one requested
188  return getDefaultCaptureDevice(dataLineInfo);
189  }
190 
191  public static void main(String[] args) {
192 
193  Options options = BuildCommandLineOptions();
194  CommandLineParser parser = new DefaultParser();
195  HelpFormatter formatter = new HelpFormatter();
196 
197  CommandLine cmd;
198  try {
199  cmd = parser.parse(options, args);
200  } catch (ParseException e) {
201  System.out.println(e.getMessage());
202  formatter.printHelp("porcupinemicdemo", options);
203  System.exit(1);
204  return;
205  }
206 
207  if (cmd.hasOption("help")) {
208  formatter.printHelp("porcupinemicdemo", options);
209  return;
210  }
211 
212  if (cmd.hasOption("show_audio_devices")) {
214  return;
215  }
216 
217  String accessKey = cmd.getOptionValue("access_key");
218  String libraryPath = cmd.getOptionValue("library_path");
219  String modelPath = cmd.getOptionValue("model_path");
220  String[] keywords = cmd.getOptionValues("keywords");
221  String[] keywordPaths = cmd.getOptionValues("keyword_paths");
222  String[] sensitivitiesStr = cmd.getOptionValues("sensitivities");
223  String audioDeviceIndexStr = cmd.getOptionValue("audio_device_index");
224  String outputPath = cmd.getOptionValue("output_path");
225 
226  if (accessKey == null || accessKey.length() == 0) {
227  throw new IllegalArgumentException("AccessKey is required for Porcupine.");
228  }
229 
230  // parse sensitivity array
231  float[] sensitivities = null;
232  if (sensitivitiesStr != null) {
233  sensitivities = new float[sensitivitiesStr.length];
234 
235  for (int i = 0; i < sensitivitiesStr.length; i++) {
236  float sensitivity;
237  try {
238  sensitivity = Float.parseFloat(sensitivitiesStr[i]);
239  } catch (Exception e) {
240  throw new IllegalArgumentException("Failed to parse sensitivity value. " +
241  "Must be a decimal value between [0,1].");
242  }
243 
244  if (sensitivity < 0 || sensitivity > 1) {
245  throw new IllegalArgumentException(String.format("Failed to parse sensitivity value (%s). " +
246  "Must be a decimal value between [0,1].", sensitivitiesStr[i]));
247  }
248  sensitivities[i] = sensitivity;
249  }
250  }
251 
252  if (libraryPath == null) {
253  libraryPath = Porcupine.LIBRARY_PATH;
254  }
255 
256  if (modelPath == null) {
257  modelPath = Porcupine.MODEL_PATH;
258  }
259 
260  if (keywordPaths == null || keywordPaths.length == 0) {
261  if (keywords == null || keywords.length == 0) {
262  throw new IllegalArgumentException("Either '--keywords' or '--keyword_paths' must be set.");
263  }
264 
265  keywordPaths = new String[keywords.length];
266  for (int i = 0; i < keywords.length; i++) {
267  final String keyword = keywords[i].toUpperCase().replace(" ", "_");
268  try {
269  final Porcupine.BuiltInKeyword builtInKeyword = Porcupine.BuiltInKeyword.valueOf(keyword);
270  keywordPaths[i] = Porcupine.BUILT_IN_KEYWORD_PATHS.get(builtInKeyword);
271  } catch (Exception e) {
272  throw new IllegalArgumentException(String.format("'%s' not a built-in keyword", keyword));
273  }
274  }
275  }
276 
277  if (sensitivities == null) {
278  sensitivities = new float[keywordPaths.length];
279  Arrays.fill(sensitivities, 0.5f);
280  }
281 
282  if (sensitivities.length != keywordPaths.length) {
283  throw new IllegalArgumentException(String.format("Number of keywords (%d) does " +
284  "not match number of sensitivities (%d)", keywordPaths.length, sensitivities.length));
285  }
286 
287  int audioDeviceIndex = -1;
288  if (audioDeviceIndexStr != null) {
289  try {
290  audioDeviceIndex = Integer.parseInt(audioDeviceIndexStr);
291  if (audioDeviceIndex < 0) {
292  throw new IllegalArgumentException(String.format("Audio device index %s is not a " +
293  "valid positive integer.", audioDeviceIndexStr));
294  }
295  } catch (Exception e) {
296  throw new IllegalArgumentException(String.format("Audio device index '%s' is not a " +
297  "valid positive integer.", audioDeviceIndexStr));
298  }
299  }
300 
301  runDemo(accessKey, libraryPath, modelPath, keywordPaths, sensitivities, audioDeviceIndex, outputPath);
302  }
303 
304  private static Options BuildCommandLineOptions() {
305  Options options = new Options();
306 
307  options.addOption(Option.builder("a")
308  .longOpt("access_key")
309  .hasArg(true)
310  .desc("AccessKey obtained from Picovoice Console (https://picovoice.ai/console/).")
311  .build());
312 
313  options.addOption(Option.builder("l")
314  .longOpt("library_path")
315  .hasArg(true)
316  .desc("Absolute path to the Porcupine native runtime library.")
317  .build());
318 
319  options.addOption(Option.builder("m")
320  .longOpt("model_path")
321  .hasArg(true)
322  .desc("Absolute path to the file containing model parameters.")
323  .build());
324 
325  options.addOption(Option.builder("k")
326  .longOpt("keywords")
327  .hasArgs()
328  .desc(String.format("List of default keywords for detection. Available keywords: %s", Porcupine.BuiltInKeyword.options()))
329  .build());
330 
331  options.addOption(Option.builder("kp")
332  .longOpt("keyword_paths")
333  .hasArgs()
334  .desc("Absolute paths to keyword model files.")
335  .build());
336 
337  options.addOption(Option.builder("s")
338  .longOpt("sensitivities")
339  .hasArgs()
340  .desc("Sensitivities for detecting keywords. Each value should be a number within [0, 1]. A higher " +
341  "sensitivity results in fewer misses at the cost of increasing the false alarm rate. " +
342  "If not set 0.5 will be used.")
343  .build());
344 
345  options.addOption(Option.builder("o")
346  .longOpt("output_path")
347  .hasArg(true)
348  .desc("Absolute path to recorded audio for debugging.")
349  .build());
350 
351  options.addOption(Option.builder("di")
352  .longOpt("audio_device_index")
353  .hasArg(true)
354  .desc("Index of input audio device.")
355  .build());
356 
357  options.addOption(new Option("sd", "show_audio_devices", false, "Print available recording devices."));
358  options.addOption(new Option("h", "help", false, ""));
359 
360  return options;
361  }
362 }
ai.picovoice.porcupinedemo.MicDemo.getDefaultCaptureDevice
static TargetDataLine getDefaultCaptureDevice(DataLine.Info dataLineInfo)
Definition: porcupine/demo/java/src/ai/picovoice/porcupinedemo/MicDemo.java:159
ai.picovoice.porcupine
Definition: android/Porcupine/porcupine/src/main/java/ai/picovoice/porcupine/exception/PorcupineActivationException.java:11
ai.picovoice.porcupine.Porcupine.Builder
Definition: android/Porcupine/porcupine/src/main/java/ai/picovoice/porcupine/Porcupine.java:165
ai.picovoice.porcupine.Porcupine.Builder.setKeywordPaths
Builder setKeywordPaths(String[] keywordPaths)
Definition: android/Porcupine/porcupine/src/main/java/ai/picovoice/porcupine/Porcupine.java:183
ai.picovoice.porcupine.Porcupine.delete
void delete()
Definition: android/Porcupine/porcupine/src/main/java/ai/picovoice/porcupine/Porcupine.java:75
getName
ROSCONSOLE_CONSOLE_IMPL_DECL std::string getName(void *handle)
ai.picovoice.porcupine.Porcupine.LIBRARY_PATH
static final String LIBRARY_PATH
Definition: java/src/ai/picovoice/porcupine/Porcupine.java:33
ai.picovoice.porcupine.Porcupine.Builder.build
Porcupine build(Context context)
Definition: android/Porcupine/porcupine/src/main/java/ai/picovoice/porcupine/Porcupine.java:256
f
f
ai.picovoice.porcupinedemo.MicDemo.getAudioDevice
static TargetDataLine getAudioDevice(int deviceIndex, DataLine.Info dataLineInfo)
Definition: porcupine/demo/java/src/ai/picovoice/porcupinedemo/MicDemo.java:169
ai.picovoice.porcupine.Porcupine.getFrameLength
native int getFrameLength()
ai.picovoice.porcupine.Porcupine.Builder.setSensitivities
Builder setSensitivities(float[] sensitivities)
Definition: android/Porcupine/porcupine/src/main/java/ai/picovoice/porcupine/Porcupine.java:203
ai.picovoice.porcupine.Porcupine.MODEL_PATH
static final String MODEL_PATH
Definition: java/src/ai/picovoice/porcupine/Porcupine.java:34
ai.picovoice.porcupine.Porcupine.BUILT_IN_KEYWORD_PATHS
static final HashMap< BuiltInKeyword, String > BUILT_IN_KEYWORD_PATHS
Definition: android/Porcupine/porcupine/src/main/java/ai/picovoice/porcupine/Porcupine.java:42
ai
ai.picovoice
python.setup.keywords
keywords
Definition: porcupine/binding/python/setup.py:88
ai.picovoice.porcupine.Porcupine.process
int process(short[] pcm)
Definition: android/Porcupine/porcupine/src/main/java/ai/picovoice/porcupine/Porcupine.java:94
ai.picovoice.porcupinedemo.MicDemo.main
static void main(String[] args)
Definition: porcupine/demo/java/src/ai/picovoice/porcupinedemo/MicDemo.java:191
ai.picovoice.porcupinedemo.MicDemo.BuildCommandLineOptions
static Options BuildCommandLineOptions()
Definition: porcupine/demo/java/src/ai/picovoice/porcupinedemo/MicDemo.java:304
ai.picovoice.porcupine.Porcupine.Builder.setLibraryPath
Builder setLibraryPath(String libraryPath)
Definition: java/src/ai/picovoice/porcupine/Porcupine.java:156
ai.picovoice.porcupine.Porcupine.BuiltInKeyword.options
static String options()
Definition: java/src/ai/picovoice/porcupine/Porcupine.java:134
ai.picovoice.porcupine.Porcupine.Builder.setAccessKey
Builder setAccessKey(String accessKey)
Definition: android/Porcupine/porcupine/src/main/java/ai/picovoice/porcupine/Porcupine.java:173
ai.picovoice.porcupine.Porcupine
Definition: android/Porcupine/porcupine/src/main/java/ai/picovoice/porcupine/Porcupine.java:35
ai.picovoice.porcupinedemo.MicDemo.runDemo
static void runDemo(String accessKey, String libPath, String modelPath, String[] keywordPaths, float[] sensitivities, int audioDeviceIndex, String outputPath)
Definition: porcupine/demo/java/src/ai/picovoice/porcupinedemo/MicDemo.java:30
args
cmd
string cmd
ai.picovoice.porcupinedemo.MicDemo
Definition: porcupine/demo/java/src/ai/picovoice/porcupinedemo/MicDemo.java:29
ai.picovoice.porcupine.Porcupine.Builder.setModelPath
Builder setModelPath(String modelPath)
Definition: android/Porcupine/porcupine/src/main/java/ai/picovoice/porcupine/Porcupine.java:178
ai.picovoice.porcupinedemo.MicDemo.showAudioDevices
static void showAudioDevices()
Definition: porcupine/demo/java/src/ai/picovoice/porcupinedemo/MicDemo.java:143
ai.picovoice.porcupine.Porcupine.BuiltInKeyword
Definition: android/Porcupine/porcupine/src/main/java/ai/picovoice/porcupine/Porcupine.java:145


picovoice_driver
Author(s):
autogenerated on Fri Apr 1 2022 02:13:56