public void TestProcess() { Porcupine p = Porcupine.Create(keywords: new List <string> { "porcupine" }); int frameLen = p.FrameLength; string testAudioPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "resources/audio_samples/porcupine.wav"); List <short> data = GetPcmFromFile(testAudioPath, p.SampleRate); int framecount = (int)Math.Floor((float)(data.Count / frameLen)); var results = new List <int>(); for (int i = 0; i < framecount; i++) { int start = i * p.FrameLength; int count = p.FrameLength; List <short> frame = data.GetRange(start, count); int result = p.Process(frame.ToArray()); Assert.IsTrue(result == -1 || result == 0, "Porcupine returned an unexpected result (should return 0 or -1)"); if (result >= 0) { results.Add(result); } } Assert.IsTrue(results.Count == 1 && results[0] == 0, "Expected to find keyword 'porcupine', but no keyword was detected."); p.Dispose(); }
/// <summary> /// Reads through input file and, upon detecting the specified wake word(s), prints the detection timecode and the wake word. /// </summary> /// <param name="inputAudioPath">Required argument. Absolute path to input audio file.</param> /// <param name="modelPath">Absolute path to the file containing model parameters. If not set it will be set to the default location.</param> /// <param name="keywordPaths">Absolute paths to keyword model files. If not set it will be populated from `keywords` argument.</param> /// <param name="sensitivities"> /// Sensitivities for detecting keywords. Each value should be a number within [0, 1]. A higher sensitivity results in fewer /// misses at the cost of increasing the false alarm rate. If not set 0.5 will be used. /// </param> /// <param name="keywords"> /// List of keywords (phrases) for detection. The list of available (default) keywords can be retrieved /// using `Porcupine.KEYWORDS`. If `keyword_paths` is set then this argument will be ignored. /// </param> public static void RunDemo(string inputAudioPath, string modelPath, List <string> keywordPaths, List <string> keywords, List <float> sensitivities) { Porcupine porcupine = null; try { // init porcupine wake word engine porcupine = Porcupine.Create(modelPath, keywordPaths, keywords, sensitivities); // get keyword names for labeling detection results if (keywords == null) { keywords = keywordPaths.Select(k => Path.GetFileNameWithoutExtension(k).Split("_")[0]).ToList(); } using BinaryReader reader = new BinaryReader(File.Open(inputAudioPath, FileMode.Open)); ValidateWavFile(reader, porcupine.SampleRate, 16, out short numChannels); // read audio and send frames to porcupine short[] porcupineFrame = new short[porcupine.FrameLength]; int frameIndex = 0; long totalSamplesRead = 0; Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); while (reader.BaseStream.Position != reader.BaseStream.Length) { totalSamplesRead++; porcupineFrame[frameIndex++] = reader.ReadInt16(); if (frameIndex == porcupineFrame.Length) { int result = porcupine.Process(porcupineFrame); if (result >= 0) { Console.WriteLine($"Detected {keywords[result]} at " + $"{Math.Round(totalSamplesRead / (double)porcupine.SampleRate, 2)} sec"); } frameIndex = 0; } // skip right channel if (numChannels == 2) { reader.ReadInt16(); } } stopWatch.Stop(); double audioLen = Math.Round(totalSamplesRead / (double)porcupine.SampleRate, 2); double realtimeFactor = Math.Round(audioLen / stopWatch.Elapsed.TotalSeconds, 2); Console.WriteLine($"Realtime factor: {realtimeFactor}x"); } finally { porcupine?.Dispose(); } }
public void TestVersion() { Porcupine p = Porcupine.Create(keywords: new List <string> { "porcupine" }); Assert.IsFalse(string.IsNullOrWhiteSpace(p.Version), "Porcupine did not return a valid version number."); p.Dispose(); }
public void TestFrameLength() { Porcupine p = Porcupine.Create(keywords: new List <string> { "porcupine" }); Assert.IsTrue(p.FrameLength > 0, "Specified frame length was not a valid number."); p.Dispose(); }
/// <summary> /// Reads through input file and, upon detecting the specified wake word(s), prints the detection timecode and the wake word. /// </summary> /// <param name="inputAudioPath">Required argument. Absolute path to input audio file.</param> /// <param name="modelPath">Absolute path to the file containing model parameters. If not set it will be set to the default location.</param> /// <param name="keywordPaths">Absolute paths to keyword model files. If not set it will be populated from `keywords` argument.</param> /// <param name="sensitivities"> /// Sensitivities for detecting keywords. Each value should be a number within [0, 1]. A higher sensitivity results in fewer /// misses at the cost of increasing the false alarm rate. If not set 0.5 will be used. /// </param> /// <param name="keywords"> /// List of keywords (phrases) for detection. The list of available (default) keywords can be retrieved /// using `Porcupine.KEYWORDS`. If `keyword_paths` is set then this argument will be ignored. /// </param> public static void RunDemo(string inputAudioPath, string modelPath, List <string> keywordPaths, List <string> keywords, List <float> sensitivities) { Porcupine porcupine = null; try { // init porcupine wake word engine porcupine = Porcupine.Create(modelPath, keywordPaths, keywords, sensitivities); using (BinaryReader reader = new BinaryReader(File.Open(inputAudioPath, FileMode.Open))) { short numChannels; ValidateWavFile(reader, porcupine.SampleRate, 16, out numChannels); // read audio and send frames to porcupine short[] porcupineFrame = new short[porcupine.FrameLength]; int frameIndex = 0; long totalSamplesRead = 0; while (reader.BaseStream.Position != reader.BaseStream.Length) { totalSamplesRead++; porcupineFrame[frameIndex++] = reader.ReadInt16(); if (frameIndex == porcupineFrame.Length) { int result = porcupine.Process(porcupineFrame); if (result >= 0) { Console.WriteLine($"Detected {keywords[result]} at " + $"{Math.Round(totalSamplesRead / (double)porcupine.SampleRate, 2)} sec"); } frameIndex = 0; } // skip right channel if (numChannels == 2) { reader.ReadInt16(); } } } } finally { porcupine?.Dispose(); } }
public void TestProcessMultiple() { List <string> inputKeywords = new List <string> { "alexa", "americano", "blueberry", "bumblebee", "grapefruit", "grasshopper", "picovoice", "porcupine", "terminator" }; List <int> expectedResults = new List <int>() { 7, 0, 1, 2, 3, 4, 5, 6, 7, 8 }; Porcupine p = Porcupine.Create(keywords: inputKeywords); int frameLen = p.FrameLength; string testAudioPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "resources/audio_samples/multiple_keywords.wav"); List <short> data = GetPcmFromFile(testAudioPath, p.SampleRate); int framecount = (int)Math.Floor((float)(data.Count / frameLen)); var results = new List <int>(); for (int i = 0; i < framecount; i++) { int start = i * frameLen; int count = frameLen; List <short> frame = data.GetRange(start, count); int result = p.Process(frame.ToArray()); Assert.IsTrue(result >= -1, "Porcupine returned an unexpected result (<-1)"); if (result >= 0) { results.Add(result); } } Assert.AreEqual(expectedResults.Count, results.Count, $"Should have found {expectedResults.Count} keywords, but {results.Count} were found."); for (int i = 0; i < results.Count; i++) { Assert.AreEqual(expectedResults[i], results[i], $"Expected '{Porcupine.KEYWORDS[expectedResults[i]]}', but '{Porcupine.KEYWORDS[results[i]]}' was detected."); } p.Dispose(); }
public void TestProcessMultiple() { Porcupine p = Porcupine.Create(keywords: Porcupine.KEYWORDS); int frameLen = p.FrameLength; string testAudioPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "resources/audio_samples/multiple_keywords.wav"); List <short> data = GetPcmFromFile(testAudioPath, p.SampleRate); int framecount = (int)Math.Floor((float)(data.Count / frameLen)); var results = new List <int>(); for (int i = 0; i < framecount; i++) { int start = i * p.FrameLength; int count = p.FrameLength; List <short> frame = data.GetRange(start, count); int result = p.Process(frame.ToArray()); Assert.IsTrue(result >= -1, "Porcupine returned an unexpected result (<-1)"); if (result >= 0) { results.Add(result); } } var expectedResults = new[] { 6, 0, 1, 2, 3, 4, 5, 6, 7 }; Assert.AreEqual(expectedResults.Length, results.Count, $"Should have found {expectedResults.Length} keywords, but {results.Count} were found."); for (int i = 0; i < results.Count; i++) { Assert.AreEqual(expectedResults[i], results[i], $"Expected '{Porcupine.KEYWORDS[expectedResults[i]]}', but '{Porcupine.KEYWORDS[results[i]]}' was detected."); } p.Dispose(); }
/// <summary> /// Creates an input audio stream, instantiates an instance of Porcupine object, and monitors the audio stream for /// occurrencec of the wake word(s). It prints the time of detection for each occurrence and the wake word. /// </summary> /// <param name="modelPath">Absolute path to the file containing model parameters. If not set it will be set to the default location.</param> /// <param name="keywordPaths">Absolute paths to keyword model files. If not set it will be populated from `keywords` argument.</param> /// <param name="keywordPaths">Absolute paths to keyword model files. If not set it will be populated from `keywords` argument.</param> /// <param name="sensitivities"> /// Sensitivities for detecting keywords. Each value should be a number within [0, 1]. A higher sensitivity results in fewer /// misses at the cost of increasing the false alarm rate. If not set 0.5 will be used. /// </param> /// <param name="keywords"> /// List of keywords (phrases) for detection. The list of available (default) keywords can be retrieved /// using `Porcupine.KEYWORDS`. If `keyword_paths` is set then this argument will be ignored. /// </param> /// <param name="audioDeviceIndex">Optional argument. If provided, audio is recorded from this input device. Otherwise, the default audio input device is used.</param> /// <param name="outputPath">Optional argument. If provided, recorded audio will be stored in this location at the end of the run.</param> public static void RunDemo(string modelPath, List <string> keywordPaths, List <string> keywords, List <float> sensitivities, int?audioDeviceIndex = null, string outputPath = null) { Porcupine porcupine = null; BinaryWriter outputFileWriter = null; int totalSamplesWritten = 0; try { // init porcupine wake word engine porcupine = Porcupine.Create(modelPath, keywordPaths, keywords, sensitivities); // get keyword names for labeling detection results if (keywords == null) { keywords = keywordPaths.Select(k => Path.GetFileNameWithoutExtension(k).Split("_")[0]).ToList(); } // open stream to output file if (!string.IsNullOrWhiteSpace(outputPath)) { outputFileWriter = new BinaryWriter(new FileStream(outputPath, FileMode.OpenOrCreate, FileAccess.Write)); WriteWavHeader(outputFileWriter, 1, 16, 16000, 0); } // choose audio device string deviceName = null; if (audioDeviceIndex != null) { List <string> captureDeviceList = ALC.GetStringList(GetEnumerationStringList.CaptureDeviceSpecifier).ToList(); if (captureDeviceList != null && audioDeviceIndex.Value < captureDeviceList.Count) { deviceName = captureDeviceList[audioDeviceIndex.Value]; } else { throw new ArgumentException("No input device found with the specified index. Use --show_audio_devices to show" + "available inputs", "--audio_device_index"); } } Console.Write("Listening for {"); for (int i = 0; i < keywords.Count; i++) { Console.Write($" {keywords[i]}({sensitivities[i]})"); } Console.Write(" }\n"); // create and start recording short[] recordingBuffer = new short[porcupine.FrameLength]; ALCaptureDevice captureDevice = ALC.CaptureOpenDevice(deviceName, 16000, ALFormat.Mono16, porcupine.FrameLength * 2); { ALC.CaptureStart(captureDevice); while (!Console.KeyAvailable) { int samplesAvailable = ALC.GetAvailableSamples(captureDevice); if (samplesAvailable > porcupine.FrameLength) { ALC.CaptureSamples(captureDevice, ref recordingBuffer[0], porcupine.FrameLength); int result = porcupine.Process(recordingBuffer); if (result >= 0) { Console.WriteLine($"[{DateTime.Now.ToLongTimeString()}] Detected '{keywords[result]}'"); } if (outputFileWriter != null) { foreach (short sample in recordingBuffer) { outputFileWriter.Write(sample); } totalSamplesWritten += recordingBuffer.Length; } } Thread.Yield(); } // stop and clean up resources Console.WriteLine("Stopping..."); ALC.CaptureStop(captureDevice); ALC.CaptureCloseDevice(captureDevice); } } finally { if (outputFileWriter != null) { // write size to header and clean up WriteWavHeader(outputFileWriter, 1, 16, 16000, totalSamplesWritten); outputFileWriter.Flush(); outputFileWriter.Dispose(); } porcupine?.Dispose(); } }