using NAudio.CoreAudioApi; using NAudio.Wave; using System; using System.Collections.Generic; using System.Net; using System.Threading; namespace RobvanderWoude { internal class DetectSound { static readonly string progver = "2.00"; static bool debugmode = false; static bool micaccess = true; static bool timedout = false; static Recorder recorder; [STAThread] static int Main( string[] args ) { #region Declare Variables #if DEBUG debugmode = true; #endif bool exitatonce = false; bool quietmode = false; bool thresholdexceeded = false; bool thresholdset = false; bool timeoutset = false; bool useaverage = false; int threshold = 50; int timeout = 1000; int interval = 100; int peakmax = 0; int peakmin = 0; int peakavg = 0; int peakcnt = 0; #endregion Declare Variables #region Parse Command Line foreach ( string arg in args ) { switch ( arg.ToUpper( ) ) { case "/?": return ShowHelp( ); case "/A": if ( useaverage ) { return ShowHelp( "Duplicate command line switch /A" ); } useaverage = true; break; case "/D": #if DEBUG #else if ( debugmode ) { return ShowHelp( "Duplicate command line switch /D" ); } #endif debugmode = true; break; case "/Q": if ( useaverage ) { return ShowHelp( "Duplicate command line switch /Q" ); } useaverage = true; break; case "/X": if ( exitatonce ) { return ShowHelp( "Duplicate command line switch /X" ); } exitatonce = true; break; default: if ( int.TryParse( arg, out int test ) ) { if ( thresholdset && timeoutset ) { return ShowHelp( "Invalid numeric argument {0}", arg ); } if ( !thresholdset ) { if ( test > 0 && test < 100 ) { threshold = test; thresholdset = true; } else { return ShowHelp( "Invalid threshold value {0}", arg ); } } else { if ( !timeoutset ) { if ( test > 99 ) { timeout = test; timeoutset = true; } else { return ShowHelp( "Invalid threshold value {0}", arg ); } } } break; } else { return ShowHelp( "Invalid command line argument {0}", arg ); } } } if ( exitatonce && useaverage ) { return ShowHelp( "Command line switches /A and /X are mutually exclusive" ); } #endregion Parse Command Line // start timeout timer Timer timer = new Timer( TimerExpired, null, timeout, Timeout.Infinite ); if ( debugmode ) { Console.WriteLine( DateTime.Now.ToString( ) ); } // Start listening while ( !timedout ) { Dictionary allaudioendpoints = GetMicrophones( ); if ( allaudioendpoints.Count == 0 ) { if ( debugmode || !quietmode ) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine( "No microphone detected" ); Console.ResetColor( ); } return -1; } else { Thread waverecorder = new Thread( new ThreadStart( WaveRecorder ) ); if ( !micaccess ) { if ( debugmode || !quietmode ) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine( "Access to microphone denied" ); Console.ResetColor( ); } return -1; } waverecorder.Start( ); foreach ( AudioEndPoint endpoint in allaudioendpoints.Values ) { if ( endpoint.InOut == DataFlow.Capture ) { if ( endpoint.State == DeviceState.Active ) { peakcnt++; string devicename = endpoint.Name; int peak = (int) ( 100 * endpoint.MasterPeakValue ); peakavg += peak; peakmax = Math.Max( peak, peakmax ); peakmin = Math.Min( peak, peakmin ); if ( debugmode ) { Console.WriteLine( "{0}\t{1}\t{2}", DateTime.Now.TimeOfDay.ToString( ), devicename, endpoint.GetPeakLevel( ) ); } if ( peak >= threshold ) { thresholdexceeded = true; if ( exitatonce && !quietmode ) { Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine( "Sound detected" ); Console.ResetColor( ); waverecorder.Abort( ); timer.Dispose( ); return 1; } } Thread.Sleep( interval ); } } } waverecorder.Abort( ); } } // Cleanup timeout timer timer.Dispose( ); #region Show Results if ( peakcnt == 0 ) { if ( debugmode || !quietmode ) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine( "No microphone or access denied" ); Console.ResetColor( ); } return -1; } peakavg = (int) ( peakavg / peakcnt ); if ( useaverage ) { thresholdexceeded = ( peakavg > threshold ); } if ( debugmode ) { Console.WriteLine( "{0}\tmin {1}%, max {2}%, average {3}%", DateTime.Now.ToString( ), peakmin, peakmax, peakavg ); } if ( useaverage ) { if ( !quietmode ) { Console.WriteLine( "Sound level {0}%", peakavg ); } } if ( !quietmode ) { if ( thresholdexceeded ) { Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine( "Sound detected" ); Console.ResetColor( ); } else { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine( "No sound exceeding the threshold" ); Console.ResetColor( ); } } if ( useaverage ) { return peakavg; } return ( thresholdexceeded ? 1 : 0 ); #endregion Show Results } private static Dictionary GetMicrophones( ) { Dictionary audioendpoints = new Dictionary( ); MMDeviceEnumerator enumerator = new MMDeviceEnumerator( ); int index = 0; foreach ( MMDevice endpoint in enumerator.EnumerateAudioEndPoints( DataFlow.Capture, DeviceState.Active ) ) { AudioEndPoint audioendpoint = new AudioEndPoint { Index = index, InOut = endpoint.DataFlow, Name = endpoint.FriendlyName, State = endpoint.State, ID = endpoint.ID, }; audioendpoint.MasterVolumeLevelScalar = endpoint.AudioEndpointVolume.MasterVolumeLevelScalar; audioendpoint.MasterPeakValue = endpoint.AudioMeterInformation.MasterPeakValue; audioendpoint.PeakValues = endpoint.AudioMeterInformation.PeakValues; audioendpoints[index] = audioendpoint; index++; } return audioendpoints; } #region Error handling static int ShowHelp( params string[] errmsg ) { #region Error Message if ( errmsg.Length > 0 ) { List errargs = new List( errmsg ); errargs.RemoveAt( 0 ); Console.Error.WriteLine( ); Console.ForegroundColor = ConsoleColor.Red; Console.Error.Write( "ERROR:\t" ); Console.ForegroundColor = ConsoleColor.White; Console.Error.WriteLine( errmsg[0], errargs.ToArray( ) ); Console.ResetColor( ); } #endregion Error Message #region Help Text /* DetectSound.exe, Version 2.00 Detect sound input on default microphone before detection period expires ? Usage: DetectSound.exe [ threshold ] [ timeout ] [ /A | /X ] [ /D ] [ /Q ] ? Where: threshold the minimum peak level to detect (1..99; default 50) timeout detection period in milliseconds (100 or up; default: 1000) /A Average peak value is returned as errorlevel /D Debug mode: display value for each sample /Q Quiet mode: no messages, only errorlevel to check result /X eXit immediately if threshold is exceeded before timeout expires (default: wait till timeout expires) Notes: This program uses Mark Heath's NAudio, available at https://github.com/naudio/NAudio to detect sound. Return code 1 or average peak level if sound is detected within the timeout period, 0 if not, or -1 in case of errors. ? Written by Rob van der Woude https://www.robvanderwoude.com */ #endregion Help Text #region Display Help Text Console.Error.WriteLine( ); Console.Error.WriteLine( "DetectSound.exe, Version {0}", progver ); Console.Error.WriteLine( "Detect sound input on default microphone before detection period expires" ); Console.Error.WriteLine( ); Console.Error.Write( "Usage: " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.WriteLine( "DetectSound.exe [ threshold ] [ timeout ] [ /A | /X ] [ /D ] [ /Q ]" ); Console.ResetColor( ); Console.Error.WriteLine( ); Console.Error.Write( "Where: " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "threshold" ); Console.ResetColor( ); Console.Error.WriteLine( " the minimum peak level to detect (1..99; default 50)" ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( " timeout" ); Console.ResetColor( ); Console.Error.WriteLine( " detection period in milliseconds (100 or up; default: 1000)" ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( " /A A" ); Console.ResetColor( ); Console.Error.WriteLine( "verage peak value is returned as errorlevel" ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( " /D D" ); Console.ResetColor( ); Console.Error.WriteLine( "ebug mode: display value for each sample" ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( " /Q Q" ); Console.ResetColor( ); Console.Error.WriteLine( "uiet mode: no messages, only errorlevel to check result" ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( " /X" ); Console.ResetColor( ); Console.Error.Write( " e" ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "X" ); Console.ResetColor( ); Console.Error.WriteLine( "it immediately if threshold is exceeded before timeout" ); Console.Error.WriteLine( " expires (default: wait till timeout expires)" ); Console.Error.WriteLine( ); Console.Error.WriteLine( "Notes: This program uses Mark Heath's NAudio, available at" ); Console.ForegroundColor = ConsoleColor.DarkGray; Console.Error.Write( " https://github.com/naudio/NAudio" ); Console.ResetColor( ); Console.Error.WriteLine( " to detect sound." ); Console.Error.WriteLine( " Return code 1 or average peak level if sound is detected within the" ); Console.Error.WriteLine( " timeout period, 0 if not, or -1 in case of errors." ); Console.Error.WriteLine( ); Console.Error.WriteLine( "Written by Rob van der Woude" ); Console.Error.WriteLine( "https://www.robvanderwoude.com" ); #endregion Display Help Text return -1; } #endregion Error handling private static void TimerExpired( object state ) { if ( debugmode ) { Console.WriteLine( "{0}\tTime's Up", DateTime.Now.ToString( ) ); } timedout = true; } public static void WaveRecorder( ) { try { recorder = new Recorder( ); recorder.StartRecording( ); micaccess = true; } catch ( Exception ) { micaccess = false; } } } public class AudioEndPoint { public int Index { get; set; } public DataFlow InOut { get; set; } public string Name { get; set; } public DeviceState State { get; set; } public string ID { get; set; } public float MasterVolumeLevelScalar { get; set; } public float MasterPeakValue { get; set; } public AudioMeterInformationChannels PeakValues { get; set; } public string GetVolume( ) { if ( State == DeviceState.Active ) { return string.Format( "{0} %", (int) ( MasterVolumeLevelScalar * 100 ) ); } else { return string.Empty; } } public string GetPeakLevel( int channel = -1 ) { if ( State == DeviceState.Active ) { switch ( channel ) { case 0: if ( PeakValues.Count > 0 ) { return string.Format( "{0} %", (int) ( 100 * float.Parse( PeakValues[0].ToString( ) ) ) ); } else { return string.Empty; } case 1: if ( PeakValues.Count > 1 ) { return string.Format( "{0} %", (int) ( 100 * float.Parse( PeakValues[1].ToString( ) ) ) ); } else { return string.Empty; } default: return string.Format( "{0} %", (int) ( 100 * MasterPeakValue ) ); } } else { return string.Empty; } } } public class Recorder { public WaveInEvent wavein = new WaveInEvent( ); public void StartRecording( ) { wavein.StartRecording( ); } } }