private static Dictionary <string, double[]> loadKeywordHistograms() { var result = new Dictionary <string, double[]>(); foreach (var keywordFilename in Directory.EnumerateFiles(Directory.GetCurrentDirectory(), "*.wav", SearchOption.TopDirectoryOnly)) { var wavFile = new WavFile(keywordFilename); if (wavFile.ReadMono16bit(out short[] samples)) { string keyword = Path.GetFileNameWithoutExtension(keywordFilename); result[keyword] = calculateFFT(samples); } } return(result); }
static async Task Main(string[] args) { // ============================================================================================================= // inspired by: https://stackoverflow.com/questions/4087727/openal-how-to-create-simple-microphone-echo-programm string captureDeviceSpecifier = Alc.GetString(IntPtr.Zero, Alc.CaptureDeviceSpecifier); Console.WriteLine("Capture device name: " + captureDeviceSpecifier); //var audioDevice = Alc.OpenDevice(null); ////var errorCode = Alc.GetError(audioDevice); // TODO: check this after each Alc call ////var errorCode = Al.GetError(); // TODO: check this after each Al call //var context = Alc.CreateContext(audioDevice, null); //Alc.MakeContextCurrent(context); const int frequency = 44100; const int keywordLength = (int)(frequency); var captureDevice = Alc.CaptureOpenDevice(captureDeviceSpecifier, frequency, format: Al.FormatMono16, buffersize: frequency); string deviceName = Alc.GetString(captureDevice, Alc.DeviceSpecifier); Console.WriteLine($"{deviceName} is open for capture."); if (args.Length > 0 && args[0] == "record") { while (true) { Console.Write("Enter keyword name: "); string keywordName = Console.ReadLine(); if (string.IsNullOrWhiteSpace(keywordName)) // exit if nothing entered { break; } Alc.CaptureStart(captureDevice); Console.WriteLine("recording keyword..."); // need new line for intensity visualization short[] samples = captureSamples(captureDevice, frequency, keywordLength); Alc.CaptureStop(captureDevice); int fileIndex = 0; while (File.Exists($"{keywordName}.{fileIndex}.wav")) { fileIndex += 1; } // write sound file { string filename = $"{keywordName}.{fileIndex}.wav"; Debug.Assert(samples.Length >= keywordLength); var wavFile = new WavFile(filename) { SamplesPerSecond = frequency, SamplesTotalCount = keywordLength /* trim samples */ }; wavFile.WriteMono16bit(samples); Console.WriteLine($"wav file was saved."); } // create spectrogram and save as image { int width = (int)Math.Sqrt(samples.Length * 2); // TODO: make better formula for square images var bins = samples.Partition(size: width); var spectrogram = new List <double[]>(); foreach (var bin in bins) { double[] histogram = calculateFFT(bin.ToArray()); spectrogram.Add(histogram); } using (Bitmap bitmap = new Bitmap(spectrogram.Count, spectrogram[0].Length)) { for (int i = 0; i < spectrogram.Count; i++) { for (int j = 0; j < spectrogram[i].Length; j++) { double value = spectrogram[i][j]; double pixelValue = Math.Max(0, Math.Min(255, value * 10)); // TODO: do not trim values, make better multiplying factor byte color = (byte)(pixelValue); bitmap.SetPixel(i, j, Color.FromArgb(color, color, color)); } } string filename = $"{keywordName}.{fileIndex}.png"; bitmap.Save(filename, ImageFormat.Png); Console.WriteLine($"png file was saved."); } } } } else { Alc.CaptureStart(captureDevice); Console.WriteLine("listening for keyword..."); // need new line for intensity visualization var reporter = new ClapDetector.Client.Reporter(); while (true) { short[] samples = captureSamples(captureDevice, frequency, keywordLength); // TODO: generate spectrogram image and compare to existing models Console.WriteLine("samples taken"); string replyMessage = await reporter.ReportClapAsync(); System.Console.WriteLine($"[{DateTime.UtcNow:HH:mm:ss.fff}]: {replyMessage}"); } Alc.CaptureStop(captureDevice); } // clean up Alc.CaptureCloseDevice(captureDevice); Alc.MakeContextCurrent(IntPtr.Zero); //Alc.DestroyContext(context); //Alc.CloseDevice(audioDevice); }