Beispiel #1
0
        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();
        }
Beispiel #2
0
        public void TestProcess()
        {
            Porcupine p = new Porcupine(Path.Combine(Environment.CurrentDirectory, "porcupine_params.pv"), keywordFilePath: $"{GetAbsRootPath()}resources/keyword_files/porcupine_{GetEnvironmentName()}.ppn", sensitivity: 0.5f);

            Assert.AreEqual(PicoVoiceStatus.SUCCESS, p.Status, "the status of the creation of the recognition system has failed");
            WAVFile file = new WAVFile();

            file.Open("porcupine.wav", WAVFile.WAVFileMode.READ);
            Assert.AreEqual(p.SampleRate() / 1000, file.BitsPerSample, "The samplerate is not equal!!!");
            List <short> data = new List <short>();

            while (file.NumSamplesRemaining > 0)
            {
                data.Add(BitConverter.ToInt16(file.GetNextSample_ByteArray()));
            }

            int framecount = (int)Math.Floor((decimal)(data.Count / p.FrameLength()));
            var results    = new List <bool>();

            for (int i = 0; i < framecount; i++)
            {
                int          start = i * p.FrameLength();
                int          count = p.FrameLength();
                List <short> frame = data.GetRange(start, count);
                p.Process(frame.ToArray(), out bool result);
                results.Add(result);
            }

            var res = results.Count(x => x);

            Assert.AreEqual(1, res, $"The result is not as expected, expected {1} got {res}");
            p.Dispose();
        }
Beispiel #3
0
 public void TestFrameLength()
 {
     porcupine = Porcupine.FromBuiltInKeywords(ACCESS_KEY, new List <BuiltInKeyword> {
         BuiltInKeyword.PORCUPINE
     });
     Assert.IsTrue(porcupine.FrameLength > 0, "Specified frame length was not a valid number.");
     porcupine.Dispose();
 }
Beispiel #4
0
 public void TestVersion()
 {
     porcupine = Porcupine.FromBuiltInKeywords(ACCESS_KEY, new List <BuiltInKeyword> {
         BuiltInKeyword.PORCUPINE
     });
     Assert.IsFalse(string.IsNullOrWhiteSpace(porcupine.Version), "Porcupine did not return a valid version number.");
     porcupine.Dispose();
 }
Beispiel #5
0
        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();
        }
Beispiel #6
0
        /// <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();
            }
        }
Beispiel #7
0
        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();
        }
Beispiel #8
0
 public void TestSingleKeyword()
 {
     porcupine = Porcupine.FromBuiltInKeywords(ACCESS_KEY, new List <BuiltInKeyword> {
         BuiltInKeyword.PORCUPINE
     });
     RunTestCase(
         "porcupine.wav",
         new List <int>()
     {
         0
     });
 }
Beispiel #9
0
        /// <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();
            }
        }
Beispiel #10
0
        public void MultipleKeywords()
        {
            var modelFilePath = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, "porcupine_params.pv"));

            Assert.IsTrue(File.Exists(modelFilePath), $"File.Exists(modelFilePath) --> {modelFilePath}");
            paths.ForEach(keywordFilePath => Assert.IsTrue(File.Exists(keywordFilePath), $"File.Exists(keywordFilePath) --> {keywordFilePath}"));

            Porcupine p = new Porcupine(modelFilePath, keywordFilePaths: paths, sensitivities: senses);

            Assert.AreEqual(PicoVoiceStatus.SUCCESS, p.Status, "the status of the creation of the recognition system has failed");
            WAVFile file = new WAVFile();

            file.Open("multiple_keywords.wav", WAVFile.WAVFileMode.READ);
            Assert.AreEqual(p.SampleRate(), file.AudioFormat.SampleRateHz, "The samplerate is not equal!!!");
            List <short> data = new List <short>();

            while (file.NumSamplesRemaining > 0)
            {
                data.Add(BitConverter.ToInt16(file.GetNextSample_ByteArray()));
            }
            int framecount = (int)Math.Floor((decimal)(data.Count / p.FrameLength()));
            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);
                PicoVoiceStatus status = p.ProcessMultipleKeywords(frame.ToArray(), out int result);
                if (result >= 0)
                {
                    results.Add(result);
                }
                Assert.AreEqual(PicoVoiceStatus.SUCCESS, status, "The status is not as expected");
            }

            var requiredRes = new[] { 8, 0, 1, 2, 3, 4, 5, 7, 8, 9 };

            Assert.AreEqual(requiredRes.Length, results.Count, $"expected results length are different expected {requiredRes.Length} got {results.Count}");
            for (var i = 0; i < results.Count; i++)
            {
                Assert.AreEqual(requiredRes[i], results[i], $"The result is not as expected, expected {requiredRes[i]} got {results[i]}");
            }

            p.Dispose();
        }
Beispiel #11
0
        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();
        }
Beispiel #12
0
        public void TestProcess()
        {
            var modelFilePath = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, "porcupine_params.pv"));

            Assert.IsTrue(File.Exists(modelFilePath), $"File.Exists(modelFilePath) --> {modelFilePath}");
            keywordPaths.ForEach(keywordFilePath => Assert.IsTrue(File.Exists(keywordFilePath), $"File.Exists(keywordFilePath) --> {keywordFilePath}"));
            Porcupine p = new Porcupine(modelFilePath, keywordPaths: keywordPaths, sensitivities: sensitivities);

            List <short> data = new List <short>();

            using (BinaryReader reader = new BinaryReader(File.Open("multiple_keywords.wav", FileMode.Open))) {
                reader.ReadBytes(44);

                while (reader.BaseStream.Position != reader.BaseStream.Length)
                {
                    data.Add(reader.ReadInt16());
                }
            }

            int framecount = (int)Math.Floor((decimal)(data.Count / p.FrameLength()));
            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());
                if (result >= 0)
                {
                    results.Add(result);
                }
            }

            var requiredRes = new[] { 6, 0, 1, 2, 3, 4, 5, 6, 7 };

            Assert.AreEqual(requiredRes.Length, results.Count, $"expected results length are different expected {requiredRes.Length} got {results.Count}");
            for (var i = 0; i < results.Count; i++)
            {
                Assert.AreEqual(requiredRes[i], results[i], $"The result is not as expected, expected {requiredRes[i]} got {results[i]}");
            }

            p.Dispose();
        }
Beispiel #13
0
        public void TestSingleKeywordFr()
        {
            string        language = "fr";
            List <string> keywords = new List <string>()
            {
                "mon chouchou"
            };

            porcupine = Porcupine.FromKeywordPaths(
                ACCESS_KEY,
                GetKeywordPaths(language, keywords),
                GetModelPath(language)
                );

            RunTestCase(
                "mon_chouchou.wav",
                new List <int>()
            {
                0
            });
        }
Beispiel #14
0
        public void TestSingleKeywordEs()
        {
            string        language = "es";
            List <string> keywords = new List <string>()
            {
                "manzana"
            };

            porcupine = Porcupine.FromKeywordPaths(
                ACCESS_KEY,
                GetKeywordPaths(language, keywords),
                GetModelPath(language)
                );

            RunTestCase(
                "manzana.wav",
                new List <int>()
            {
                0
            });
        }
Beispiel #15
0
        public void TestWithNonAsciiModelName()
        {
            string        language = "es";
            List <string> keywords = new List <string>()
            {
                "murciélago",
            };

            porcupine = Porcupine.FromKeywordPaths(
                ACCESS_KEY,
                GetKeywordPaths(language, keywords),
                GetModelPath(language)
                );

            RunTestCase(
                "murciélago.wav",
                new List <int>()
            {
                0, 0
            });
        }
Beispiel #16
0
        public void TestSingleKeywordDe()
        {
            string        language = "de";
            List <string> keywords = new List <string>()
            {
                "heuschrecke"
            };

            porcupine = Porcupine.FromKeywordPaths(
                ACCESS_KEY,
                GetKeywordPaths(language, keywords),
                GetModelPath(language)
                );

            RunTestCase(
                "heuschrecke.wav",
                new List <int>()
            {
                0
            });
        }
Beispiel #17
0
        public void TestMultipleKeywordFr()
        {
            string        language = "fr";
            List <string> keywords = new List <string>()
            {
                "framboise",
                "mon chouchou",
                "parapluie"
            };

            porcupine = Porcupine.FromKeywordPaths(
                ACCESS_KEY,
                GetKeywordPaths(language, keywords),
                GetModelPath(language)
                );

            RunTestCase(
                "multiple_keywords_fr.wav",
                new List <int>()
            {
                0, 1, 0, 2
            });
        }
Beispiel #18
0
        public void TestMultipleKeywordEs()
        {
            string        language = "es";
            List <string> keywords = new List <string>()
            {
                "emparedado",
                "leopardo",
                "manzana"
            };

            porcupine = Porcupine.FromKeywordPaths(
                ACCESS_KEY,
                GetKeywordPaths(language, keywords),
                GetModelPath(language)
                );

            RunTestCase(
                "multiple_keywords_es.wav",
                new List <int>()
            {
                0, 1, 2
            });
        }
Beispiel #19
0
        public void TestMultipleKeyword()
        {
            List <BuiltInKeyword> inputKeywords = new List <BuiltInKeyword>
            {
                BuiltInKeyword.ALEXA,
                BuiltInKeyword.AMERICANO,
                BuiltInKeyword.BLUEBERRY,
                BuiltInKeyword.BUMBLEBEE,
                BuiltInKeyword.GRAPEFRUIT,
                BuiltInKeyword.GRASSHOPPER,
                BuiltInKeyword.PICOVOICE,
                BuiltInKeyword.PORCUPINE,
                BuiltInKeyword.TERMINATOR
            };

            porcupine = Porcupine.FromBuiltInKeywords(ACCESS_KEY, inputKeywords);
            RunTestCase(
                "multiple_keywords.wav",
                new List <int>()
            {
                7, 0, 1, 2, 3, 4, 5, 6, 7, 8
            });
        }
Beispiel #20
0
        public void TestMultipleKeywordDe()
        {
            string        language = "de";
            List <string> keywords = new List <string>()
            {
                "heuschrecke",
                "ananas",
                "leguan",
                "stachelschwein"
            };

            porcupine = Porcupine.FromKeywordPaths(
                ACCESS_KEY,
                GetKeywordPaths(language, keywords),
                GetModelPath(language)
                );

            RunTestCase(
                "multiple_keywords_de.wav",
                new List <int>()
            {
                1, 0, 2, 3
            });
        }
Beispiel #21
0
        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();
        }
Beispiel #22
0
        /// <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="accessKey">AccessKey obtained from Picovoice Console (https://console.picovoice.ai/).</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="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 accessKey,
            string modelPath,
            List <string> keywordPaths,
            List <float> sensitivities,
            int audioDeviceIndex,
            string outputPath = null)
        {
            Porcupine    porcupine           = null;
            BinaryWriter outputFileWriter    = null;
            int          totalSamplesWritten = 0;

            // init porcupine wake word engine
            porcupine = Porcupine.FromKeywordPaths(accessKey, keywordPaths, modelPath, sensitivities);

            // get keyword names for labeling detection results
            List <string> keywordNames = 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);
            }

            Console.CancelKeyPress += (s, o) =>
            {
                Console.WriteLine("Stopping...");

                if (outputFileWriter != null)
                {
                    // write size to header and clean up
                    WriteWavHeader(outputFileWriter, 1, 16, 16000, totalSamplesWritten);
                    outputFileWriter.Flush();
                    outputFileWriter.Dispose();
                }
                porcupine?.Dispose();
            };

            // create and start recording
            using (PvRecorder recorder = PvRecorder.Create(deviceIndex: audioDeviceIndex, frameLength: porcupine.FrameLength))
            {
                Console.WriteLine($"Using device: {recorder.SelectedDevice}");
                Console.Write($"Listening for [{string.Join(' ', keywordNames.Select(k => $"'{k}'"))}]...\n");
                recorder.Start();

                while (true)
                {
                    short[] pcm = recorder.Read();

                    int result = porcupine.Process(pcm);
                    if (result >= 0)
                    {
                        Console.WriteLine($"[{DateTime.Now.ToLongTimeString()}] Detected '{keywordNames[result]}'");
                    }

                    if (outputFileWriter != null)
                    {
                        foreach (short sample in pcm)
                        {
                            outputFileWriter.Write(sample);
                        }
                        totalSamplesWritten += pcm.Length;
                    }
                    Thread.Yield();
                }
            }
        }
Beispiel #23
0
        protected override async Task DoWorkAsync(CancellationToken cancellationToken)
        {
            Console.WriteLine("Starting voice assistant runner for " + _user.Username);
            try
            {
                var entryDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

                string[] keywordFiles;
                if (Environment.OSVersion.Platform == PlatformID.Win32NT)
                {
                    keywordFiles = new string[] {
                        Path.Join(entryDirectory, "picovoice_windows.ppn"),
                        Path.Join(entryDirectory, "porcupine_windows.ppn"),
                        Path.Join(entryDirectory, "bumblebee_windows.ppn"),
                    };
                }
                else
                {
                    keywordFiles = new string[] {
                        Path.Join(entryDirectory, "alexa_linux.ppn"),
                        Path.Join(entryDirectory, "porcupine_linux.ppn"),
                        Path.Join(entryDirectory, "snowboy_linux.ppn"),
                        Path.Join(entryDirectory, "dank_ditties_linux.ppn"),
                    };
                }

                using var porcupine = new Porcupine(
                          Path.Join(entryDirectory, "porcupine_params.pv"),
                          keywordFiles,
                          keywordFiles.Select(_ => 0.5f).ToArray()
                          );

                var userStream = _user.AudioStream;
                if (userStream == null)
                {
                    return;
                }

                var picoFrameLength = porcupine.FrameLength();
                var picoSampleRate  = porcupine.SampleRate();
                var picoBuffer      = new short[picoFrameLength];

                await using var clip = new StreamClip(userStream).UseMemoryBuffer(3840);
                await clip.PrepareAsync();

                await using var monoClip = clip.Downsample(96000, 48000);
                await monoClip.PrepareAsync();

                await using var downsampledClip = monoClip.Downsample(48000, picoSampleRate);
                await downsampledClip.PrepareAsync();

                while (!cancellationToken.IsCancellationRequested)
                {
                    Console.WriteLine("Waiting for wake word");
                    while (!cancellationToken.IsCancellationRequested)
                    {
                        await downsampledClip.ReadAsync(picoBuffer, 0, picoFrameLength, cancellationToken);

                        var status = porcupine.Process(picoBuffer);
                        if (status != -1)
                        {
                            break;
                        }
                    }

                    Console.WriteLine("Wake word detected");
                    clip.Seek(-picoFrameLength * 6 * (48000 / picoSampleRate), SeekOrigin.Current);

                    try
                    {
                        var data = await _witAiClient.ParseAudioStream(monoClip, cancellationToken);

                        if (data != null)
                        {
                            var text = data.Text?.Trim();
                            if (!string.IsNullOrWhiteSpace(text))
                            {
                                Console.WriteLine(_user.Username + ": " + text);
                                var playSongIntent = data.Intents.FirstOrDefault(i => i.Name == "play_song");

                                if (text.ToLower().StartsWith("i'm "))
                                {
                                    _voiceChannelWorker.Say("Hello " + text.Substring("i'm ".Length) + ", I'm Dank Ditties bot.");
                                }
                                else if (text.ToLower().StartsWith("play "))
                                {
                                    var searchString = text.Substring("play ".Length);

                                    if (searchString == "next")
                                    {
                                        _voiceChannelWorker?.TrySkip();
                                    }
                                    else
                                    {
                                        var matches = from post in await _metadataManager.GetReadyToPlayMetadataAsync()
                                                      let relevance = FuzzySharp.Fuzz.Ratio(post.Title, searchString)
                                                                      select(post, relevance);

                                        var topMatch = matches.OrderByDescending(m => m.relevance);
                                        Console.WriteLine("matches: \n" + string.Join("\n", topMatch.Take(3).Select(m => $"{m.post.Title}: {m.relevance}")));
                                        Console.WriteLine();
                                        var closestMatch = topMatch.FirstOrDefault().post;
                                        if (closestMatch != null)
                                        {
                                            _voiceChannelWorker.EnqueueSong(closestMatch.Id);
                                            _voiceChannelWorker.Say("I have added your song, " + closestMatch.Title + " to the queue");
                                            Console.WriteLine("Added " + closestMatch.Title + " to queue");
                                        }
                                    }
                                }
                                else if (text.ToLower() == "what song is this" || text.ToLower() == "what's playing" || text.ToLower() == "song" || text.ToLower() == "song name" || text.ToLower() == "damn son whered you find this")
                                {
                                    _voiceChannelWorker.Say("I am currently playing " + _voiceChannelWorker?.CurrentSong.Title);
                                }
                                else
                                {
                                    //_voiceChannelWorker.Say("I'm sorry, I didn't understand that!");
                                    _voiceChannelWorker.Say(text);
                                }
                            }
                            else
                            {
                                _voiceChannelWorker.Say("I'm sorry, I didn't understand that!");
                            }
                        }
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e);
                    }
                }
                _voiceChannelWorker.Say("Goodbye, " + _user.Nickname ?? _user.Username);
            }
            finally
            {
                Console.WriteLine("Stopping voice assistant runner for " + _user.Username);
            }
        }
Beispiel #24
0
        /// <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();
            }
        }