Rob van der Woude's Scripting Pages
Powered by GeSHi

Source code for audioendpoints.cs

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

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Drawing;
  5. using System.IO;
  6. using System.Windows.Forms;
  7. using NAudio.CoreAudioApi;
  8. using NAudio.Wave;
  9.  
  10.  
  11. namespace RobvanderWoude
  12. {
  13. 	internal class AudioEndPoints
  14. 	{
  15. 		static readonly string progver = "2.00";
  16.  
  17.  
  18. 		#region Global Variables
  19.  
  20. 		static bool bgcolors = true;
  21. 		static bool fgcolors = false;
  22. 		static bool helprequested = false;
  23. 		static bool stereo = true;
  24. 		static DataGridView datagrid;
  25. 		static readonly Dictionary<DeviceState, Color> foregroundcolors = new Dictionary<DeviceState, Color>
  26. 		{
  27. 			[DeviceState.Active] = Color.Green,
  28. 			[DeviceState.Disabled] = Color.Orange,
  29. 			[DeviceState.NotPresent] = Color.SlateGray,
  30. 			[DeviceState.Unplugged] = Color.Red
  31. 		};
  32. 		static readonly Dictionary<DeviceState, Color> backgroundcolors = new Dictionary<DeviceState, Color>
  33. 		{
  34. 			[DeviceState.Active] = Color.LightGreen,
  35. 			[DeviceState.Disabled] = Color.Yellow,
  36. 			[DeviceState.NotPresent] = Color.LightGray,
  37. 			[DeviceState.Unplugged] = Color.Red
  38. 		};
  39. 		static Dictionary<int, AudioEndPoint> audioendpoints;
  40. 		static Form form;
  41. 		static int interval = 0;
  42. 		static Timer timer = new Timer( );
  43. 		static WaveIn recorder;
  44.  
  45. 		#endregion Global Variables
  46.  
  47.  
  48. 		[STAThread]
  49. 		static int Main( string[] args )
  50. 		{
  51. 			#region Parse Command Line
  52.  
  53. 			foreach ( string arg in args )
  54. 			{
  55. 				string[] kvp = arg.Split( ":=".ToCharArray( ), 2 );
  56. 				string key = kvp[0].ToUpper( );
  57. 				string val = ( kvp.Length == 2 ? kvp[1] : string.Empty );
  58. 				switch ( key )
  59. 				{
  60. 					case "/?":
  61. 						return ShowHelp( true );
  62. 					case "/B":
  63. 						bgcolors = false;
  64. 						fgcolors = false;
  65. 						break;
  66. 					case "/F":
  67. 						bgcolors = false;
  68. 						fgcolors = true;
  69. 						break;
  70. 					case "/M":
  71. 						stereo = false;
  72. 						break;
  73. 					case "/R":
  74. 						interval = 1;
  75. 						if ( string.IsNullOrWhiteSpace( val ) )
  76. 						{
  77. 							if ( !int.TryParse( val, out interval ) )
  78. 							{
  79. 								interval = 1;
  80. 							}
  81. 						}
  82. 						if ( interval < 0 )
  83. 						{
  84. 							interval = 1;
  85. 						}
  86. 						timer.Interval = interval * 1000;
  87. 						timer.Tick += Timer_Tick;
  88. 						timer.Start( );
  89. 						break;
  90. 					default:
  91. 						return ShowHelp( true );
  92. 				}
  93. 			}
  94.  
  95. 			#endregion Parse Command Line
  96.  
  97.  
  98. 			Application.EnableVisualStyles( );
  99.  
  100. 			form = new Form
  101. 			{
  102. 				BackColor = Color.White,
  103. 				Font = new Font( FontFamily.GenericSansSerif, 12 ),
  104. 				Size = new Size( 1380, 840 ),
  105. 				StartPosition = FormStartPosition.CenterScreen,
  106. 				Text = "AudioEndPoints.exe, \u00A0 Version " + progver + " \u00A0?\u00A0 \u00A0 Copyright \u00A9 2025 Rob van der Woude",
  107. 				Visible = true
  108. 			};
  109. 			form.FormClosing += Form_FormClosing;
  110.  
  111. 			audioendpoints = new Dictionary<int, AudioEndPoint>( );
  112. 			recorder = new WaveIn( );
  113. 			recorder.StartRecording( );
  114.  
  115. 			SetupDataGrid( );
  116. 			PopulateDataGrid( );
  117.  
  118. 			while ( form.Enabled )
  119. 			{
  120. 				Application.DoEvents( );
  121. 			}
  122.  
  123. 			return 0;
  124. 		}
  125.  
  126.  
  127. 		private static void Datagrid_KeyUp( object sender, KeyEventArgs e )
  128. 		{
  129. 			if ( e.KeyCode == Keys.F1 )
  130. 			{
  131. 				ShowHelp( false );
  132. 			}
  133. 			if ( e.KeyCode == Keys.F5 )
  134. 			{
  135. 				RefreshDataGrid( );
  136. 			}
  137. 			if ( e.KeyCode == Keys.Escape )
  138. 			{
  139. 				form.Close( );
  140. 			}
  141. 			if ( e.Control && e.KeyCode == Keys.P )
  142. 			{
  143. 				PrintDataGrid( );
  144. 			}
  145. 			if ( e.Control && e.KeyCode == Keys.S )
  146. 			{
  147. 				string filename = string.Format( "AudioEndPoints.{0}.{1}.txt", Environment.GetEnvironmentVariable( "COMPUTERNAME" ), DateTime.Now.ToString( "yyyy-MM-dd_HHmmss" ) );
  148. 				string outfile = Path.Combine( Environment.CurrentDirectory, filename );
  149. 				SaveDataGrid( outfile );
  150. 				MessageBox.Show( "Saved as \"" + filename + "\" in folder \"" + Path.GetDirectoryName( outfile ) + "\"", "File Saved", MessageBoxButtons.OK, MessageBoxIcon.Information );
  151. 			}
  152. 		}
  153.  
  154.  
  155. 		private static void Datagrid_SortCompare( object sender, DataGridViewSortCompareEventArgs e )
  156. 		{
  157. 			if ( e.Column.Index == 0 )
  158. 			{
  159. 				e.SortResult = int.Parse( e.CellValue1.ToString( ) ).CompareTo( int.Parse( e.CellValue2.ToString( ) ) );
  160. 				e.Handled = true; // skip the default sorting
  161. 			}
  162. 		}
  163.  
  164.  
  165. 		private static void Form_FormClosing( object sender, FormClosingEventArgs e )
  166. 		{
  167. 			try
  168. 			{
  169. 				timer.Stop( );
  170. 			}
  171. 			catch { }
  172. 			try
  173. 			{
  174. 				recorder.StopRecording( );
  175. 			}
  176. 			catch { }
  177. 			if ( helprequested )
  178. 			{
  179. 				ShowHelp( true );
  180. 			}
  181. 			form.Enabled = false;
  182. 		}
  183.  
  184.  
  185. 		private static void Timer_Tick( object sender, EventArgs e )
  186. 		{
  187. 			RefreshDataGrid( );
  188. 		}
  189.  
  190.  
  191. 		private static void InventoryAudioEndPoints( )
  192. 		{
  193. 			MMDeviceEnumerator enumerator = new MMDeviceEnumerator( );
  194. 			int index = 0;
  195. 			foreach ( MMDevice endpoint in enumerator.EnumerateAudioEndPoints( DataFlow.All, DeviceState.All ) )
  196. 			{
  197. 				AudioEndPoint audioendpoint = new AudioEndPoint
  198. 				{
  199. 					Index = index,
  200. 					InOut = endpoint.DataFlow,
  201. 					Name = endpoint.FriendlyName,
  202. 					State = endpoint.State,
  203. 					ID = endpoint.ID,
  204. 				};
  205. 				if ( audioendpoint.State == DeviceState.Active )
  206. 				{
  207. 					audioendpoint.MasterVolumeLevelScalar = endpoint.AudioEndpointVolume.MasterVolumeLevelScalar;
  208. 					audioendpoint.MasterPeakValue = endpoint.AudioMeterInformation.MasterPeakValue;
  209. 					audioendpoint.PeakValues = endpoint.AudioMeterInformation.PeakValues;
  210. 				}
  211. 				else
  212. 				{
  213. 					audioendpoint.MasterVolumeLevelScalar = 0f;
  214. 					audioendpoint.MasterPeakValue = 0f;
  215. 					audioendpoint.PeakValues = null;
  216. 				}
  217. 				audioendpoints[index] = audioendpoint;
  218. 				index++;
  219. 			}
  220. 		}
  221.  
  222.  
  223. 		private static void PopulateDataGrid( )
  224. 		{
  225. 			InventoryAudioEndPoints( );
  226. 			foreach ( AudioEndPoint endpoint in audioendpoints.Values )
  227. 			{
  228. 				PopulateDataGridRow( endpoint );
  229. 			}
  230. 			for ( int i = 0; i < datagrid.ColumnCount; i++ )
  231. 			{
  232. 				if ( i == datagrid.ColumnCount - 1 )
  233. 				{
  234. 					datagrid.Columns[i].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
  235. 				}
  236. 				else
  237. 				{
  238. 					datagrid.Columns[i].AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells;
  239. 				}
  240. 			}
  241. 		}
  242.  
  243.  
  244. 		private static void PopulateDataGridRow( AudioEndPoint endpoint )
  245. 		{
  246. 			string[] line;
  247. 			if ( stereo )
  248. 			{
  249. 				line = new string[] { endpoint.Index.ToString( ), ( endpoint.InOut == DataFlow.Capture ? "IN" : "OUT" ), endpoint.Name, endpoint.State.ToString( ), endpoint.ID, endpoint.GetVolume( ), endpoint.GetPeakLevel( 0 ), endpoint.GetPeakLevel( 1 ) };
  250. 			}
  251. 			else
  252. 			{
  253. 				line = new string[] { endpoint.Index.ToString( ), ( endpoint.InOut == DataFlow.Capture ? "IN" : "OUT" ), endpoint.Name, endpoint.State.ToString( ), endpoint.ID, endpoint.GetVolume( ), endpoint.GetPeakLevel( -1 ) };
  254. 			}
  255. 			int row;
  256. 			if ( endpoint.Index < datagrid.Rows.Count - 1 )
  257. 			{
  258. 				row = endpoint.Index;
  259. 				datagrid.Rows[row].SetValues( line );
  260. 			}
  261. 			else
  262. 			{
  263. 				row = datagrid.Rows.Count - 1;
  264. 				datagrid.Rows.Add( line );
  265. 			}
  266. 			if ( bgcolors )
  267. 			{
  268. 				datagrid.Rows[row].DefaultCellStyle.BackColor = backgroundcolors[endpoint.State];
  269. 			}
  270. 			if ( fgcolors )
  271. 			{
  272. 				datagrid.Rows[row].DefaultCellStyle.ForeColor = foregroundcolors[endpoint.State];
  273. 			}
  274. 		}
  275.  
  276.  
  277. 		private static void PrintDataGrid( )
  278. 		{
  279. 			string printout = Path.GetTempFileName( );
  280. 			SaveDataGrid( printout );
  281. 			ProcessStartInfo psi = new ProcessStartInfo
  282. 			{
  283. 				UseShellExecute = false,
  284. 				Arguments = string.Format( "/p \"{0}\"", printout ),
  285. 				FileName = "notepad.exe"
  286. 			};
  287. 			Process proc = Process.Start( psi );
  288. 			proc.WaitForExit( );
  289. 			File.Delete( printout );
  290. 		}
  291.  
  292.  
  293. 		private static void RefreshDataGrid( )
  294. 		{
  295. 			PopulateDataGrid( );
  296. 		}
  297.  
  298.  
  299. 		private static void SaveDataGrid( string outfile )
  300. 		{
  301. 			// line out colums
  302. 			int[] cw;
  303. 			if ( stereo )
  304. 			{
  305. 				cw = new int[] { 5, 6, 0, 0, 0, 6, 6, 6 };
  306. 			}
  307. 			else
  308. 			{
  309. 				cw = new int[] { 5, 6, 0, 0, 0, 6, 4 };
  310. 			}
  311. 			for ( int i = 0; i < datagrid.Columns.Count; i++ )
  312. 			{
  313. 				for ( int j = 0; j < datagrid.Rows.Count; j++ )
  314. 				{
  315. 					if ( datagrid.Rows[j].Cells[i].Value != null && datagrid.Rows[j].Cells[i].Value.ToString( ).Length > cw[i] )
  316. 					{
  317. 						cw[i] = datagrid.Rows[j].Cells[i].Value.ToString( ).Length;
  318. 					}
  319. 				}
  320. 			}
  321.  
  322. 			// format printout
  323. 			List<string> lines = new List<string>( );
  324. 			string linetemplate;
  325. 			if ( stereo )
  326. 			{
  327. 				linetemplate = "{0,-" + cw[0] + "}    {1,-" + cw[1] + "}    {2,-" + cw[2] + "}    {3,-" + cw[3] + "}    {4,-" + cw[4] + "}    {5," + cw[5] + "}    {6," + cw[6] + "}    {7," + cw[7] + "}";
  328. 			}
  329. 			else
  330. 			{
  331. 				linetemplate = "{0,-" + cw[0] + "}    {1,-" + cw[1] + "}    {2,-" + cw[2] + "}    {3,-" + cw[3] + "}    {4,-" + cw[4] + "}    {5," + cw[5] + "}    {6," + cw[6] + "}";
  332. 			}
  333. 			// header
  334. 			var cols = datagrid.Columns;
  335. 			string headerline;
  336. 			if ( stereo )
  337. 			{
  338. 				headerline = string.Format( linetemplate, cols[0].HeaderText, cols[1].HeaderText.Replace( "\u00A0", "" ), cols[2].HeaderText, cols[3].HeaderText, cols[4].HeaderText, cols[5].HeaderText, cols[6].HeaderText, cols[7].HeaderText );
  339. 			}
  340. 			else
  341. 			{
  342. 				headerline = string.Format( linetemplate, cols[0].HeaderText, cols[1].HeaderText.Replace( "\u00A0", "" ), cols[2].HeaderText, cols[3].HeaderText, cols[4].HeaderText, cols[5].HeaderText, cols[6].HeaderText );
  343. 			}
  344. 			lines.Add( headerline );
  345. 			// rows
  346. 			for ( int i = 0; i < datagrid.Rows.Count; i++ )
  347. 			{
  348. 				var cells = datagrid.Rows[i].Cells;
  349. 				string line;
  350. 				if ( stereo )
  351. 				{
  352. 					line = string.Format( linetemplate, cells[0].Value, cells[1].Value, cells[2].Value, cells[3].Value, cells[4].Value, cells[5].Value, cells[6].Value, cells[7].Value );
  353.  
  354. 				}
  355. 				else
  356. 				{
  357. 					line = string.Format( linetemplate, cells[0].Value, cells[1].Value, cells[2].Value, cells[3].Value, cells[4].Value, cells[5].Value, cells[6].Value );
  358. 				}
  359. 				lines.Add( line );
  360. 			}
  361.  
  362. 			using ( StreamWriter print = new StreamWriter( outfile ) )
  363. 			{
  364. 				for ( int i = 0; i < lines.Count; i++ )
  365. 				{
  366. 					print.WriteLine( lines[i] );
  367. 				}
  368. 			}
  369. 		}
  370.  
  371.  
  372. 		private static void SetupDataGrid( )
  373. 		{
  374. 			datagrid = new DataGridView
  375. 			{
  376. 				Location = new Point( 0, 0 ),
  377. 				Size = new Size( 1200, 250 ),
  378. 				AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.DisplayedCellsExceptHeaders,
  379. 				ColumnHeadersBorderStyle = DataGridViewHeaderBorderStyle.Single,
  380. 				CellBorderStyle = DataGridViewCellBorderStyle.Single,
  381. 				GridColor = Color.Black,
  382. 				RowHeadersVisible = false,
  383. 				SelectionMode = DataGridViewSelectionMode.FullRowSelect,
  384. 				MultiSelect = false,
  385. 				Dock = DockStyle.Fill
  386. 			};
  387. 			if ( stereo )
  388. 			{
  389. 				datagrid.ColumnCount = 8;
  390. 			}
  391. 			else
  392. 			{
  393. 				datagrid.ColumnCount = 7;
  394. 				form.Width -= 85;
  395. 			}
  396. 			datagrid.ColumnHeadersDefaultCellStyle.BackColor = Color.Navy;
  397. 			datagrid.ColumnHeadersDefaultCellStyle.ForeColor = Color.White;
  398. 			datagrid.ColumnHeadersDefaultCellStyle.Font = new Font( FontFamily.GenericSansSerif, 12, FontStyle.Bold );
  399. 			datagrid.Columns[0].Name = "Index";
  400. 			datagrid.Columns[1].Name = "In\u00A0/\u00A0Out";
  401. 			datagrid.Columns[2].Name = "Name";
  402. 			datagrid.Columns[3].Name = "Status";
  403. 			datagrid.Columns[4].Name = "ID";
  404. 			datagrid.Columns[5].Name = "Volume";
  405. 			datagrid.Columns[5].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight;
  406. 			if ( stereo )
  407. 			{
  408. 				datagrid.Columns[6].Name = "Peak\u00A0L";
  409. 				datagrid.Columns[6].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight;
  410. 				datagrid.Columns[7].Name = "Peak\u00A0R";
  411. 				datagrid.Columns[7].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight;
  412. 			}
  413. 			else
  414. 			{
  415. 				datagrid.Columns[6].Name = "Peak";
  416. 				datagrid.Columns[6].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight;
  417. 			}
  418. 			datagrid.KeyUp += Datagrid_KeyUp;
  419. 			datagrid.SortCompare += Datagrid_SortCompare;
  420.  
  421. 			form.Controls.Add( datagrid );
  422. 			datagrid.Focus( );
  423. 		}
  424.  
  425.  
  426. 		private static int ShowHelp( bool cli = false )
  427. 		{
  428. 			string message = string.Format( "\nAudioEndPoints.exe,  Version {0}\n", progver );
  429. 			message += "List the local computer's audio endpoints\n\n";
  430. 			message += "Usage:\tAudioEndPoints.exe\t[ /? ]  [ /F | /B ]  [ /M ]  [ /R[:nn] ]\n\n";
  431. 			message += "Where:\t                  \t/?     \tdisplay this help text and exit\n";
  432. 			message += "      \t                  \t/F     \tuse status dependent\n";
  433. 			message += "      \t                  \t       \tFOREGROUND colors,\n";
  434. 			message += "      \t                  \t/B     \tuse BLACK and WHITE,\n";
  435. 			message += "      \t                  \t       \tdefault is status dependent\n";
  436. 			message += "      \t                  \t       \tBACKGROUND colors\n";
  437. 			message += "      \t                  \t/M     \tpeak levels for L and R combined,\n";
  438. 			message += "      \t                  \t       \tdefault is separate peak levels\n";
  439. 			message += "      \t                  \t/R[:nn]\trefresh values every nn seconds,\n";
  440. 			message += "      \t                  \t       \tdefault interval is 1 second\n\n";
  441. 			message += "Click on ANY column header to sort the table by that column.\n";
  442. 			message += "Note that the table's sort order will be reset to its default at\n";
  443. 			message += "every refresh, be it by pressing F5 or by specifying a refresh\n";
  444. 			message += "interval with the /R command line switch.\n";
  445. 			message += "You can edit all cells, if you like.\n\n";
  446. 			message += "Keys:   \tCtrl+C will copy the SELECTED LINE to the clipboard,\n";
  447. 			message += "        \tCtrl+P will print the list,\n";
  448. 			message += "        \tCtrl+S will save it in a text file,\n";
  449. 			message += "        \tF1 will show this help text,\n";
  450. 			message += "        \tF5 will refresh volume and peak level values,\n";
  451. 			message += "        \tEscape will abort the program.\n\n";
  452. 			message += "Credits:\tTo access audio endpoints, this program uses\n";
  453. 			message += "        \tNAudio created by Mark Heath:\n";
  454. 			message += "        \thttps://github.com/naudio/NAudio\n";
  455. 			message += "        \tVolume reading based on code by Mike de Klerk:\n";
  456. 			message += "        \thttps://stackoverflow.com/a/12534584\n\n";
  457. 			message += "Written by Rob van der Woude\n";
  458. 			message += "https://www.robvanderwoude.com\n";
  459.  
  460. 			if ( cli )
  461. 			{
  462. 				Console.Error.WriteLine( message );
  463. 				Environment.Exit( -1 );
  464. 				Application.Exit( );
  465. 			}
  466. 			else
  467. 			{
  468. 				helprequested = true;
  469. 				MessageBox.Show( message, "AudioEndPoints.exe, \u00A0 Version " + progver );
  470. 			}
  471. 			return -1;
  472. 		}
  473. 	}
  474.  
  475.  
  476. 	public class AudioEndPoint
  477. 	{
  478. 		public int Index
  479. 		{
  480. 			get; set;
  481. 		}
  482.  
  483. 		public DataFlow InOut
  484. 		{
  485. 			get; set;
  486. 		}
  487.  
  488. 		public string Name
  489. 		{
  490. 			get; set;
  491. 		}
  492.  
  493. 		public DeviceState State
  494. 		{
  495. 			get; set;
  496. 		}
  497.  
  498. 		public string ID
  499. 		{
  500. 			get; set;
  501. 		}
  502.  
  503. 		public float MasterVolumeLevelScalar
  504. 		{
  505. 			get; set;
  506. 		}
  507.  
  508. 		public float MasterPeakValue
  509. 		{
  510. 			get; set;
  511. 		}
  512.  
  513. 		public AudioMeterInformationChannels PeakValues
  514. 		{
  515. 			get; set;
  516. 		}
  517.  
  518. 		public string GetVolume( )
  519. 		{
  520. 			if ( State == DeviceState.Active )
  521. 			{
  522. 				return string.Format( "{0} %", (int)( MasterVolumeLevelScalar * 100 ) );
  523. 			}
  524. 			else
  525. 			{
  526. 				return string.Empty;
  527. 			}
  528. 		}
  529.  
  530. 		public string GetPeakLevel( int channel = -1 )
  531. 		{
  532. 			if ( State == DeviceState.Active )
  533. 			{
  534. 				switch ( channel )
  535. 				{
  536. 					case 0:
  537. 						return string.Format( "{0} %", (int)( 100 * float.Parse( PeakValues[0].ToString( ) ) ) );
  538. 					case 1:
  539. 						return string.Format( "{0} %", (int)( 100 * float.Parse( PeakValues[1].ToString( ) ) ) );
  540. 					default:
  541. 						return string.Format( "{0} %", (int)( 100 * MasterPeakValue ) );
  542. 				}
  543. 			}
  544. 			else
  545. 			{
  546. 				return string.Empty;
  547. 			}
  548. 		}
  549. 	}
  550. }

page last modified: 2024-04-16; loaded in 0.0096 seconds