Rob van der Woude's Scripting Pages
Powered by GeSHi

Source code for detectsound.cs

(view source code of detectsound.cs as plain text)

  1. using NAudio.CoreAudioApi;
  2. using NAudio.Wave;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Net;
  6. using System.Threading;
  7.  
  8.  
  9. namespace RobvanderWoude
  10. {
  11. 	internal class DetectSound
  12. 	{
  13. 		static readonly string progver = "2.00";
  14.  
  15.  
  16. 		static bool debugmode = false;
  17. 		static bool micaccess = true;
  18. 		static bool timedout = false;
  19. 		static Recorder recorder;
  20.  
  21.  
  22. 		[STAThread]
  23. 		static int Main( string[] args )
  24. 		{
  25. 			#region Declare Variables
  26.  
  27. #if DEBUG
  28. 			debugmode = true;
  29. #endif
  30. 			bool exitatonce = false;
  31. 			bool quietmode = false;
  32. 			bool thresholdexceeded = false;
  33. 			bool thresholdset = false;
  34. 			bool timeoutset = false;
  35. 			bool useaverage = false;
  36. 			int threshold = 50;
  37. 			int timeout = 1000;
  38. 			int interval = 100;
  39. 			int peakmax = 0;
  40. 			int peakmin = 0;
  41. 			int peakavg = 0;
  42. 			int peakcnt = 0;
  43.  
  44. 			#endregion Declare Variables
  45.  
  46.  
  47. 			#region Parse Command Line
  48.  
  49. 			foreach ( string arg in args )
  50. 			{
  51. 				switch ( arg.ToUpper( ) )
  52. 				{
  53. 					case "/?":
  54. 						return ShowHelp( );
  55. 					case "/A":
  56. 						if ( useaverage )
  57. 						{
  58. 							return ShowHelp( "Duplicate command line switch /A" );
  59. 						}
  60. 						useaverage = true;
  61. 						break;
  62. 					case "/D":
  63. #if DEBUG
  64.  
  65. #else
  66. 						if ( debugmode )
  67. 						{
  68. 							return ShowHelp( "Duplicate command line switch /D" );
  69. 						}
  70. #endif
  71. 						debugmode = true;
  72. 						break;
  73. 					case "/Q":
  74. 						if ( useaverage )
  75. 						{
  76. 							return ShowHelp( "Duplicate command line switch /Q" );
  77. 						}
  78. 						useaverage = true;
  79. 						break;
  80. 					case "/X":
  81. 						if ( exitatonce )
  82. 						{
  83. 							return ShowHelp( "Duplicate command line switch /X" );
  84. 						}
  85. 						exitatonce = true;
  86. 						break;
  87. 					default:
  88. 						if ( int.TryParse( arg, out int test ) )
  89. 						{
  90. 							if ( thresholdset && timeoutset )
  91. 							{
  92. 								return ShowHelp( "Invalid numeric argument {0}", arg );
  93. 							}
  94. 							if ( !thresholdset )
  95. 							{
  96. 								if ( test > 0 && test < 100 )
  97. 								{
  98. 									threshold = test;
  99. 									thresholdset = true;
  100. 								}
  101. 								else
  102. 								{
  103. 									return ShowHelp( "Invalid threshold value {0}", arg );
  104. 								}
  105. 							}
  106. 							else
  107. 							{
  108. 								if ( !timeoutset )
  109. 								{
  110. 									if ( test > 99 )
  111. 									{
  112. 										timeout = test;
  113. 										timeoutset = true;
  114. 									}
  115. 									else
  116. 									{
  117. 										return ShowHelp( "Invalid threshold value {0}", arg );
  118. 									}
  119. 								}
  120. 							}
  121. 							break;
  122. 						}
  123. 						else
  124. 						{
  125. 							return ShowHelp( "Invalid command line argument {0}", arg );
  126. 						}
  127. 				}
  128. 			}
  129.  
  130. 			if ( exitatonce && useaverage )
  131. 			{
  132. 				return ShowHelp( "Command line switches /A and /X are mutually exclusive" );
  133. 			}
  134.  
  135. 			#endregion Parse Command Line
  136.  
  137.  
  138. 			// start timeout timer
  139. 			Timer timer = new Timer( TimerExpired, null, timeout, Timeout.Infinite );
  140.  
  141. 			if ( debugmode )
  142. 			{
  143. 				Console.WriteLine( DateTime.Now.ToString( ) );
  144. 			}
  145.  
  146. 			// Start listening
  147. 			while ( !timedout )
  148. 			{
  149. 				Dictionary<int, AudioEndPoint> allaudioendpoints = GetMicrophones( );
  150.  
  151. 				if ( allaudioendpoints.Count == 0 )
  152. 				{
  153. 					if ( debugmode || !quietmode )
  154. 					{
  155. 						Console.ForegroundColor = ConsoleColor.Red;
  156. 						Console.WriteLine( "No microphone detected" );
  157. 						Console.ResetColor( );
  158. 					}
  159. 					return -1;
  160. 				}
  161. 				else
  162. 				{
  163. 					Thread waverecorder = new Thread( new ThreadStart( WaveRecorder ) );
  164. 					if ( !micaccess )
  165. 					{
  166. 						if ( debugmode || !quietmode )
  167. 						{
  168. 							Console.ForegroundColor = ConsoleColor.Red;
  169. 							Console.WriteLine( "Access to microphone denied" );
  170. 							Console.ResetColor( );
  171. 						}
  172. 						return -1;
  173. 					}
  174. 					waverecorder.Start( );
  175.  
  176. 					foreach ( AudioEndPoint endpoint in allaudioendpoints.Values )
  177. 					{
  178. 						if ( endpoint.InOut == DataFlow.Capture )
  179. 						{
  180. 							if ( endpoint.State == DeviceState.Active )
  181. 							{
  182. 								peakcnt++;
  183. 								string devicename = endpoint.Name;
  184. 								int peak = (int) ( 100 * endpoint.MasterPeakValue );
  185. 								peakavg += peak;
  186. 								peakmax = Math.Max( peak, peakmax );
  187. 								peakmin = Math.Min( peak, peakmin );
  188. 								if ( debugmode )
  189. 								{
  190. 									Console.WriteLine( "{0}\t{1}\t{2}", DateTime.Now.TimeOfDay.ToString( ), devicename, endpoint.GetPeakLevel( ) );
  191. 								}
  192. 								if ( peak >= threshold )
  193. 								{
  194. 									thresholdexceeded = true;
  195. 									if ( exitatonce && !quietmode )
  196. 									{
  197. 										Console.ForegroundColor = ConsoleColor.Green;
  198. 										Console.WriteLine( "Sound detected" );
  199. 										Console.ResetColor( );
  200. 										waverecorder.Abort( );
  201. 										timer.Dispose( );
  202. 										return 1;
  203. 									}
  204. 								}
  205. 								Thread.Sleep( interval );
  206. 							}
  207. 						}
  208. 					}
  209. 					waverecorder.Abort( );
  210. 				}
  211. 			}
  212.  
  213. 			// Cleanup timeout timer
  214. 			timer.Dispose( );
  215.  
  216.  
  217. 			#region Show Results
  218.  
  219. 			if ( peakcnt == 0 )
  220. 			{
  221. 				if ( debugmode || !quietmode )
  222. 				{
  223. 					Console.ForegroundColor = ConsoleColor.Red;
  224. 					Console.WriteLine( "No microphone or access denied" );
  225. 					Console.ResetColor( );
  226. 				}
  227. 				return -1;
  228. 			}
  229.  
  230. 			peakavg = (int) ( peakavg / peakcnt );
  231. 			if ( useaverage )
  232. 			{
  233. 				thresholdexceeded = ( peakavg > threshold );
  234. 			}
  235.  
  236. 			if ( debugmode )
  237. 			{
  238. 				Console.WriteLine( "{0}\tmin {1}%, max {2}%, average {3}%", DateTime.Now.ToString( ), peakmin, peakmax, peakavg );
  239. 			}
  240.  
  241. 			if ( useaverage )
  242. 			{
  243. 				if ( !quietmode )
  244. 				{
  245. 					Console.WriteLine( "Sound level {0}%", peakavg );
  246. 				}
  247. 			}
  248.  
  249. 			if ( !quietmode )
  250. 			{
  251. 				if ( thresholdexceeded )
  252. 				{
  253. 					Console.ForegroundColor = ConsoleColor.Green;
  254. 					Console.WriteLine( "Sound detected" );
  255. 					Console.ResetColor( );
  256. 				}
  257. 				else
  258. 				{
  259. 					Console.ForegroundColor = ConsoleColor.Red;
  260. 					Console.WriteLine( "No sound exceeding the threshold" );
  261. 					Console.ResetColor( );
  262. 				}
  263. 			}
  264.  
  265. 			if ( useaverage )
  266. 			{
  267. 				return peakavg;
  268. 			}
  269.  
  270. 			return ( thresholdexceeded ? 1 : 0 );
  271.  
  272. 			#endregion Show Results
  273. 		}
  274.  
  275.  
  276. 		private static Dictionary<int, AudioEndPoint> GetMicrophones( )
  277. 		{
  278. 			Dictionary<int, AudioEndPoint> audioendpoints = new Dictionary<int, AudioEndPoint>( );
  279. 			MMDeviceEnumerator enumerator = new MMDeviceEnumerator( );
  280. 			int index = 0;
  281. 			foreach ( MMDevice endpoint in enumerator.EnumerateAudioEndPoints( DataFlow.Capture, DeviceState.Active ) )
  282. 			{
  283. 				AudioEndPoint audioendpoint = new AudioEndPoint
  284. 				{
  285. 					Index = index,
  286. 					InOut = endpoint.DataFlow,
  287. 					Name = endpoint.FriendlyName,
  288. 					State = endpoint.State,
  289. 					ID = endpoint.ID,
  290. 				};
  291. 				audioendpoint.MasterVolumeLevelScalar = endpoint.AudioEndpointVolume.MasterVolumeLevelScalar;
  292. 				audioendpoint.MasterPeakValue = endpoint.AudioMeterInformation.MasterPeakValue;
  293. 				audioendpoint.PeakValues = endpoint.AudioMeterInformation.PeakValues;
  294. 				audioendpoints[index] = audioendpoint;
  295. 				index++;
  296. 			}
  297. 			return audioendpoints;
  298. 		}
  299.  
  300.  
  301. 		#region Error handling
  302.  
  303. 		static int ShowHelp( params string[] errmsg )
  304. 		{
  305. 			#region Error Message
  306.  
  307. 			if ( errmsg.Length > 0 )
  308. 			{
  309. 				List<string> errargs = new List<string>( errmsg );
  310. 				errargs.RemoveAt( 0 );
  311. 				Console.Error.WriteLine( );
  312. 				Console.ForegroundColor = ConsoleColor.Red;
  313. 				Console.Error.Write( "ERROR:\t" );
  314. 				Console.ForegroundColor = ConsoleColor.White;
  315. 				Console.Error.WriteLine( errmsg[0], errargs.ToArray( ) );
  316. 				Console.ResetColor( );
  317. 			}
  318.  
  319. 			#endregion Error Message
  320.  
  321.  
  322. 			#region Help Text
  323.  
  324. 			/*
  325. 			DetectSound.exe,  Version 2.00
  326. 			Detect sound input on default microphone before detection period expires
  327. ?
  328. 			Usage:  DetectSound.exe  [ threshold ]  [ timeout ]  [ /A | /X ]  [ /D ]  [ /Q ]
  329. ?
  330. 			Where:  threshold   the minimum peak level to detect (1..99; default 50)
  331. 			        timeout     detection period in milliseconds (100 or up; default: 1000)
  332. 			        /A          Average peak value is returned as errorlevel
  333. 			        /D          Debug mode: display value for each sample
  334. 			        /Q          Quiet mode: no messages, only errorlevel to check result
  335. 			        /X          eXit immediately if threshold is exceeded before timeout
  336. 			                    expires (default: wait till timeout expires)
  337.  
  338. 			Notes:  This program uses Mark Heath's NAudio, available at
  339. 			        https://github.com/naudio/NAudio to detect sound.
  340. 			        Return code 1 or average peak level if sound is detected within the
  341. 			        timeout period, 0 if not, or -1 in case of errors.
  342. ?
  343. 			Written by Rob van der Woude
  344. 			https://www.robvanderwoude.com
  345. 			*/
  346.  
  347. 			#endregion Help Text
  348.  
  349.  
  350. 			#region Display Help Text
  351.  
  352. 			Console.Error.WriteLine( );
  353.  
  354. 			Console.Error.WriteLine( "DetectSound.exe,  Version {0}", progver );
  355.  
  356. 			Console.Error.WriteLine( "Detect sound input on default microphone before detection period expires" );
  357.  
  358. 			Console.Error.WriteLine( );
  359.  
  360. 			Console.Error.Write( "Usage:  " );
  361. 			Console.ForegroundColor = ConsoleColor.White;
  362. 			Console.Error.WriteLine( "DetectSound.exe  [ threshold ]  [ timeout ]  [ /A | /X ]  [ /D ]  [ /Q ]" );
  363. 			Console.ResetColor( );
  364.  
  365. 			Console.Error.WriteLine( );
  366.  
  367. 			Console.Error.Write( "Where:  " );
  368. 			Console.ForegroundColor = ConsoleColor.White;
  369. 			Console.Error.Write( "threshold" );
  370. 			Console.ResetColor( );
  371. 			Console.Error.WriteLine( "   the minimum peak level to detect (1..99; default 50)" );
  372.  
  373.  
  374. 			Console.ForegroundColor = ConsoleColor.White;
  375. 			Console.Error.Write( "        timeout" );
  376. 			Console.ResetColor( );
  377. 			Console.Error.WriteLine( "     detection period in milliseconds (100 or up; default: 1000)" );
  378.  
  379. 			Console.ForegroundColor = ConsoleColor.White;
  380. 			Console.Error.Write( "        /A          A" );
  381. 			Console.ResetColor( );
  382. 			Console.Error.WriteLine( "verage peak value is returned as errorlevel" );
  383.  
  384. 			Console.ForegroundColor = ConsoleColor.White;
  385. 			Console.Error.Write( "        /D          D" );
  386. 			Console.ResetColor( );
  387. 			Console.Error.WriteLine( "ebug mode: display value for each sample" );
  388.  
  389. 			Console.ForegroundColor = ConsoleColor.White;
  390. 			Console.Error.Write( "        /Q          Q" );
  391. 			Console.ResetColor( );
  392. 			Console.Error.WriteLine( "uiet mode: no messages, only errorlevel to check result" );
  393.  
  394. 			Console.ForegroundColor = ConsoleColor.White;
  395. 			Console.Error.Write( "        /X" );
  396. 			Console.ResetColor( );
  397. 			Console.Error.Write( "          e" );
  398. 			Console.ForegroundColor = ConsoleColor.White;
  399. 			Console.Error.Write( "X" );
  400. 			Console.ResetColor( );
  401. 			Console.Error.WriteLine( "it immediately if threshold is exceeded before timeout" );
  402.  
  403. 			Console.Error.WriteLine( "                    expires (default: wait till timeout expires)" );
  404.  
  405. 			Console.Error.WriteLine( );
  406.  
  407. 			Console.Error.WriteLine( "Notes:  This program uses Mark Heath's NAudio, available at" );
  408.  
  409. 			Console.ForegroundColor = ConsoleColor.DarkGray;
  410. 			Console.Error.Write( "        https://github.com/naudio/NAudio" );
  411. 			Console.ResetColor( );
  412. 			Console.Error.WriteLine( " to detect sound." );
  413.  
  414. 			Console.Error.WriteLine( "        Return code 1 or average peak level if sound is detected within the" );
  415.  
  416. 			Console.Error.WriteLine( "        timeout period, 0 if not, or -1 in case of errors." );
  417.  
  418. 			Console.Error.WriteLine( );
  419.  
  420. 			Console.Error.WriteLine( "Written by Rob van der Woude" );
  421.  
  422. 			Console.Error.WriteLine( "https://www.robvanderwoude.com" );
  423.  
  424. 			#endregion Display Help Text
  425.  
  426.  
  427. 			return -1;
  428. 		}
  429.  
  430. 		#endregion Error handling
  431.  
  432.  
  433. 		private static void TimerExpired( object state )
  434. 		{
  435. 			if ( debugmode )
  436. 			{
  437. 				Console.WriteLine( "{0}\tTime's Up", DateTime.Now.ToString( ) );
  438. 			}
  439. 			timedout = true;
  440. 		}
  441.  
  442.  
  443. 		public static void WaveRecorder( )
  444. 		{
  445. 			try
  446. 			{
  447. 				recorder = new Recorder( );
  448. 				recorder.StartRecording( );
  449. 				micaccess = true;
  450. 			}
  451. 			catch ( Exception )
  452. 			{
  453. 				micaccess = false;
  454. 			}
  455. 		}
  456. 	}
  457.  
  458.  
  459. 	public class AudioEndPoint
  460. 	{
  461. 		public int Index
  462. 		{
  463. 			get; set;
  464. 		}
  465.  
  466. 		public DataFlow InOut
  467. 		{
  468. 			get; set;
  469. 		}
  470.  
  471. 		public string Name
  472. 		{
  473. 			get; set;
  474. 		}
  475.  
  476. 		public DeviceState State
  477. 		{
  478. 			get; set;
  479. 		}
  480.  
  481. 		public string ID
  482. 		{
  483. 			get; set;
  484. 		}
  485.  
  486. 		public float MasterVolumeLevelScalar
  487. 		{
  488. 			get; set;
  489. 		}
  490.  
  491. 		public float MasterPeakValue
  492. 		{
  493. 			get; set;
  494. 		}
  495.  
  496. 		public AudioMeterInformationChannels PeakValues
  497. 		{
  498. 			get; set;
  499. 		}
  500.  
  501. 		public string GetVolume( )
  502. 		{
  503. 			if ( State == DeviceState.Active )
  504. 			{
  505. 				return string.Format( "{0} %", (int) ( MasterVolumeLevelScalar * 100 ) );
  506. 			}
  507. 			else
  508. 			{
  509. 				return string.Empty;
  510. 			}
  511. 		}
  512.  
  513. 		public string GetPeakLevel( int channel = -1 )
  514. 		{
  515. 			if ( State == DeviceState.Active )
  516. 			{
  517. 				switch ( channel )
  518. 				{
  519. 					case 0:
  520. 						if ( PeakValues.Count > 0 )
  521. 						{
  522. 							return string.Format( "{0} %", (int) ( 100 * float.Parse( PeakValues[0].ToString( ) ) ) );
  523. 						}
  524. 						else
  525. 						{
  526. 							return string.Empty;
  527. 						}
  528. 					case 1:
  529. 						if ( PeakValues.Count > 1 )
  530. 						{
  531. 							return string.Format( "{0} %", (int) ( 100 * float.Parse( PeakValues[1].ToString( ) ) ) );
  532. 						}
  533. 						else
  534. 						{
  535. 							return string.Empty;
  536. 						}
  537. 					default:
  538. 						return string.Format( "{0} %", (int) ( 100 * MasterPeakValue ) );
  539. 				}
  540. 			}
  541. 			else
  542. 			{
  543. 				return string.Empty;
  544. 			}
  545. 		}
  546. 	}
  547.  
  548.  
  549. 	public class Recorder
  550. 	{
  551. 		public WaveInEvent wavein = new WaveInEvent( );
  552.  
  553. 		public void StartRecording( )
  554. 		{
  555. 			wavein.StartRecording( );
  556. 		}
  557. 	}
  558. }

page last modified: 2025-10-11; loaded in 0.0117 seconds