Click here to Skip to main content
15,886,014 members
Articles / Programming Languages / Visual Basic

Audio Library Part I - (Windows Mixer Control)

Rate me:
Please Sign up or sign in to vote.
4.52/5 (79 votes)
1 Oct 2006CPOL3 min read 607K   15.4K   163  
Library to control Windows Mixer from C#
//
//  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
//  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
//  PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER 
//  REMAINS UNCHANGED.
//
//  Email:  gustavo_franco@hotmail.com
//
//  Copyright (C) 2005 Franco, Gustavo 
//
using System;
using System.Diagnostics;
using System.Windows.Forms;
using System.Runtime.InteropServices;

using WaveLib.WaveServices;

namespace WaveLib.AudioMixer
{
	#region Aliases
	using HMIXER	= System.IntPtr;
	using HWAVEIN	= System.IntPtr;
	using HWAVEOUT	= System.IntPtr;
	#endregion

	#region Enum
	public enum MixerType 
	{
		Playback	= 0,
		Recording	= 1
	}
	#endregion

	#region Delegates
	internal delegate void CallbackWindowControlChangeHandler(IntPtr hMixer, uint controlId);
	internal delegate void CallbackWindowLineChangeHandler(IntPtr hMixer, uint lineId);
	#endregion

	[Author("Gustavo Franco")]
	public class Mixer : IDisposable
	{
		#region Delegates
		public delegate void MixerLineChangeHandler(Mixer mixer, MixerLine line);
		#endregion

		#region Events
		public event MixerLineChangeHandler MixerLineChanged;
		#endregion

		#region Constants Declaration
		#endregion

		#region Variables Declaration
		private MixerType		mMixerType;
		private int				mDeviceId;
		private IntPtr			mHMixer;
		private MixerLines		mLines;
		private MixerLines		mUserLines;
		private CallbackWindow	mCallbackWindow; 
		private CallbackWindowControlChangeHandler	mMixerControlChangeHandler;
		private CallbackWindowLineChangeHandler		mMixerLineChangeHandler;
		#endregion

		#region Constructors
		public Mixer(MixerType mixerType)
		{
			mMixerType					= mixerType;
			mLines						= new MixerLines();
			mUserLines					= new MixerLines();
			mMixerControlChangeHandler	= new CallbackWindowControlChangeHandler(PtrMixerControlChange);
			mMixerLineChangeHandler		= new CallbackWindowLineChangeHandler(PtrMixerLineChange);
			mCallbackWindow				= new CallbackWindow(mMixerControlChangeHandler, mMixerLineChangeHandler);
			DeviceId					= -1;
		}
		#endregion

		#region Properties
		internal IntPtr CallbackWindowHandle
		{
			get{return mCallbackWindow.Handle;}
		}

		#region MixerType
		public MixerType MixerType
		{
			get{return mMixerType;}
		}
		#endregion

		#region Lines
		public MixerLines Lines
		{
			get{return mLines;}
		}

		public MixerLines UserLines
		{
			get{return mUserLines;}
		}
		#endregion

		#region DeviceId
		public int DeviceId
		{
			get{return mDeviceId;}
			set
			{
				MMErrors errorCode = 0;

				if (value == -1)
					mDeviceId = DeviceIdDefault;
				else
					mDeviceId = value;

				if (mHMixer != IntPtr.Zero)
					MixerNative.mixerClose(mHMixer);

				errorCode = (MMErrors) MixerNative.mixerOpen(out mHMixer, mDeviceId, mCallbackWindow.Handle, IntPtr.Zero, MixerNative.CALLBACK_WINDOW);
				if (errorCode != MMErrors.MMSYSERR_NOERROR)
					throw new MixerException(errorCode, Mixers.GetErrorDescription(FuncName.fnMixerOpen, errorCode));

				ReloadLines();
			}
		}
		#endregion

		#region DeviceDetail
		public MixerDetail DeviceDetail
		{
			get
			{
				foreach(MixerDetail mixerDetail in Devices)
					if (mixerDetail.DeviceId == mDeviceId)
						return mixerDetail;

				return null;
			}
		}
		#endregion

		#region DeviceIdDefault
		public int DeviceIdDefault
		{
			get
			{
				MMErrors		errorCode	= 0;
				HWAVEOUT		hWave		= IntPtr.Zero;
				HMIXER			hMixer		= IntPtr.Zero;
				int				deviceId	= -1;

				try
				{
					WAVEFORMATEX	waveFormat	= WAVEFORMATEX.Empty;

					waveFormat.formatTag = WaveFormatTag.PCM;
					waveFormat.nChannels = 1;
					waveFormat.nSamplesPerSec = 8000;
					waveFormat.wBitsPerSample = 8;
					waveFormat.nBlockAlign = (short) (waveFormat.nChannels * (waveFormat.wBitsPerSample/8));
					waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
					waveFormat.cbSize = 0;

					if (mMixerType == MixerType.Recording)
						errorCode = (MMErrors) WaveNative.waveInOpen(out hWave, WaveNative.WAVE_MAPPER, ref waveFormat, IntPtr.Zero, IntPtr.Zero, (int) CallBackFlag.CALLBACK_NULL); 
					else if (mMixerType == MixerType.Playback)
						errorCode = (MMErrors) WaveNative.waveOutOpen(out hWave, WaveNative.WAVE_MAPPER, ref waveFormat, IntPtr.Zero, IntPtr.Zero, (int) CallBackFlag.CALLBACK_NULL); 
					if (errorCode != MMErrors.MMSYSERR_NOERROR)
						throw new MixerException(errorCode, Mixers.GetErrorDescription(FuncName.fnWaveInOpen, errorCode));

					if (mMixerType == MixerType.Recording)
						errorCode = (MMErrors) MixerNative.mixerOpen(out hMixer, hWave, IntPtr.Zero, IntPtr.Zero, ((uint) MIXER_OBJECTFLAG.HWAVEIN));
					else if (mMixerType == MixerType.Playback)
						errorCode = (MMErrors) MixerNative.mixerOpen(out hMixer, hWave, IntPtr.Zero, IntPtr.Zero, ((uint) MIXER_OBJECTFLAG.HWAVEOUT));
					if (errorCode != MMErrors.MMSYSERR_NOERROR)
						throw new MixerException(errorCode, Mixers.GetErrorDescription(FuncName.fnMixerOpen, errorCode));

					errorCode = (MMErrors) MixerNative.mixerGetID(hMixer, ref deviceId, MIXER_OBJECTFLAG.MIXER);
					if (errorCode != MMErrors.MMSYSERR_NOERROR)
						throw new MixerException(errorCode, Mixers.GetErrorDescription(FuncName.fnMixerGetID, errorCode));
				}
				finally
				{
					if (hMixer != IntPtr.Zero)
						MixerNative.mixerClose(hMixer);
					if (hWave != IntPtr.Zero && mMixerType == MixerType.Playback)
						WaveNative.waveOutClose(hWave);
					if (hWave != IntPtr.Zero && mMixerType == MixerType.Recording)
						WaveNative.waveInClose(hWave);
				}

				return deviceId;
			}
		}
		#endregion

		#region Devices
		public unsafe MixerDetails Devices
		{
			get
			{
				MMErrors		errorCode	= 0;
				MixerDetails	mixDetails	= new MixerDetails();
				HMIXER			hMixer		= IntPtr.Zero;  

				try
				{
					MIXERCAPS	wic		= new MIXERCAPS();
					
					int iNumDevs = MixerNative.mixerGetNumDevs();

					for (int i=0; i<iNumDevs; i++)
					{
						// Get info about the next device 
						errorCode = (MMErrors) MixerNative.mixerGetDevCaps(i, ref wic, Marshal.SizeOf(wic));
						if (errorCode != MMErrors.MMSYSERR_NOERROR)
							throw new MixerException(errorCode, Mixers.GetErrorDescription(FuncName.fnMixerGetDevCaps, errorCode));

						errorCode = (MMErrors) MixerNative.mixerOpen(out hMixer, i, IntPtr.Zero, IntPtr.Zero, 0);
						if (errorCode != MMErrors.MMSYSERR_NOERROR)
							throw new MixerException(errorCode, Mixers.GetErrorDescription(FuncName.fnMixerOpen, errorCode));

						MixerDetail mixDetail	= new MixerDetail();
						MIXERLINE	mxl			= new MIXERLINE();
						
						mxl.cbStruct = (uint) Marshal.SizeOf(mxl);
						mxl.dwComponentType = MIXERLINE_COMPONENTTYPE.DST_WAVEIN;
						if (MixerNative.mixerGetLineInfo(hMixer, ref mxl, MIXER_GETLINEINFOF.COMPONENTTYPE) == 0)
						{
							mixDetail.DeviceId		= i;
							mixDetail.MixerName		= wic.szPname;
							mixDetail.SupportWaveIn = true;
						}

						mxl.dwComponentType = MIXERLINE_COMPONENTTYPE.DST_SPEAKERS;
						if (MixerNative.mixerGetLineInfo(hMixer, ref mxl, MIXER_GETLINEINFOF.COMPONENTTYPE) == 0)
						{
							mixDetail.DeviceId		= i;
							mixDetail.MixerName		= wic.szPname;
							mixDetail.SupportWaveOut= true;
						}
						else
						{
							mxl.dwComponentType = MIXERLINE_COMPONENTTYPE.DST_UNDEFINED;
							if (MixerNative.mixerGetLineInfo(hMixer, ref mxl, MIXER_GETLINEINFOF.COMPONENTTYPE) == 0)
							{
								mixDetail.DeviceId		= i;
								mixDetail.MixerName		= wic.szPname;
								mixDetail.SupportWaveOut= true;
							}
						}

						if (mMixerType == MixerType.Recording && mixDetail.SupportWaveIn == true)
							mixDetails.Add(mixDetail);

						if (mMixerType == MixerType.Playback && mixDetail.SupportWaveOut == true)
							mixDetails.Add(mixDetail);
					}
				}
				finally
				{
					if (hMixer != IntPtr.Zero)
						MixerNative.mixerClose(hMixer);
				}
				return mixDetails;
			}
		}
		#endregion
		#endregion

		#region Private Methods
		private void ReloadLines()
		{
			MMErrors		errorCode	= 0;

			mLines.Clear();
			mUserLines.Clear();

			MIXERLINE			mxl  = new MIXERLINE();
			MIXERLINECONTROLS   mlc  = new MIXERLINECONTROLS();
			MIXERCONTROL        mc   = new MIXERCONTROL();
			uint dwDestination;
			unchecked
			{
				dwDestination = (uint) -1;
			}

			mxl.cbStruct = (uint) Marshal.SizeOf(mxl);

			if (mMixerType == MixerType.Recording)
			{
				mxl.dwComponentType = MIXERLINE_COMPONENTTYPE.DST_WAVEIN;
				errorCode = (MMErrors) MixerNative.mixerGetLineInfo(mHMixer, ref mxl, MIXER_GETLINEINFOF.COMPONENTTYPE);
				if (errorCode != MMErrors.MMSYSERR_NOERROR)
					throw new MixerException(errorCode, Mixers.GetErrorDescription(FuncName.fnMixerGetLineInfo, errorCode));
			}
			else if (mMixerType == MixerType.Playback)
			{
				mxl.dwComponentType = MIXERLINE_COMPONENTTYPE.DST_SPEAKERS;
				errorCode = (MMErrors) MixerNative.mixerGetLineInfo(mHMixer, ref mxl, MIXER_GETLINEINFOF.COMPONENTTYPE);
				if (errorCode != MMErrors.MMSYSERR_NOERROR)
				{
					mxl.dwComponentType = MIXERLINE_COMPONENTTYPE.DST_UNDEFINED;
					errorCode = (MMErrors) MixerNative.mixerGetLineInfo(mHMixer, ref mxl, MIXER_GETLINEINFOF.COMPONENTTYPE);
					if (errorCode != MMErrors.MMSYSERR_NOERROR)
						throw new MixerException(errorCode, Mixers.GetErrorDescription(FuncName.fnMixerGetLineInfo, errorCode));
				}
			}

			dwDestination = mxl.dwDestination;

			MixerLine mixLine = new MixerLine();

			if (mMixerType == MixerType.Recording)
				mixLine.Direction	= MixerType.Recording;
			else if (mMixerType == MixerType.Playback)
				mixLine.Direction	= MixerType.Playback;

			mixLine.Mixer			= this;
			mixLine.Channels		= mxl.cChannels;
			mixLine.CControls		= mxl.cControls;
			mixLine.Connections		= mxl.cConnections;
			mixLine.Flag			= mxl.fdwLine;
			mixLine.Destination		= dwDestination;
			mixLine.Name			= mxl.szName;
			mixLine.Id				= mxl.dwLineID;
			mixLine.ComponentType	= mxl.dwComponentType;
			mixLine.Source			= mxl.dwSource;
			mixLine.HMixer			= mHMixer;

			if (mixLine.CControls != 0 && !(mixLine.CControls == 1 && mixLine.Controls[0].Type == MIXERCONTROL_CONTROLTYPE.MUX))
				mUserLines.Add(mixLine);
			mLines.Add(mixLine);

			int cConnections = (int) mxl.cConnections;
			for(int i=0; i<cConnections; i++) 
			{
				mxl.cbStruct = (uint) Marshal.SizeOf(mxl);
				mxl.dwDestination = dwDestination;
				mxl.dwSource = (uint) i;

				errorCode = (MMErrors) MixerNative.mixerGetLineInfo(mHMixer, ref mxl, MIXER_GETLINEINFOF.SOURCE);
				if (errorCode != MMErrors.MMSYSERR_NOERROR)
					throw new MixerException(errorCode, Mixers.GetErrorDescription(FuncName.fnMixerGetLineInfo, errorCode));

				MixerLine mixLineNew = new MixerLine();

				if (mMixerType == MixerType.Recording)
					mixLineNew.Direction= MixerType.Recording;
				else if (mMixerType == MixerType.Playback)
					mixLineNew.Direction= MixerType.Playback;

				mixLineNew.Mixer			= this;
				mixLineNew.Channels			= mxl.cChannels;
				mixLineNew.CControls		= mxl.cControls;
				mixLineNew.Connections		= mxl.cConnections;
				mixLineNew.Flag				= mxl.fdwLine;
				mixLineNew.Destination		= dwDestination;
				mixLineNew.Name				= mxl.szName;
				mixLineNew.Id				= mxl.dwLineID;
				mixLineNew.ComponentType	= mxl.dwComponentType;
				mixLineNew.Source			= mxl.dwSource;
				mixLineNew.HMixer			= mHMixer;

				if (mixLineNew.CControls != 0)
					mUserLines.Add(mixLineNew);
				mLines.Add(mixLineNew);
			}
		}

		private void PtrMixerControlChange(IntPtr hMixer, uint controlId)
		{
			if (hMixer != IntPtr.Zero && MixerLineChanged != null)
			{
				MixerLine line = mUserLines.GetMixerLineByControlId(controlId);
				if (line != null)
					MixerLineChanged(this, line);
				else
				{
					MixerLine lineOS = mLines.GetMixerLineByControlId(controlId);
					if (lineOS != null)
					{
						foreach(MixerLine broadcastLine in mUserLines)
						{
							MixerLineChanged(this, broadcastLine);
						}
					}
				}
			}
		}

		private void PtrMixerLineChange(IntPtr hMixer, uint lineId)
		{
			if (hMixer != IntPtr.Zero && MixerLineChanged != null)
			{
				MixerLine line = mUserLines.GetMixerLineByLineId(lineId);
				if (line != null)
					MixerLineChanged(this, line);
			}
		}
		#endregion

		#region Public Methods
		public void Dispose()
		{
			mMixerControlChangeHandler	= null;
			mMixerLineChangeHandler		= null;
			mCallbackWindow.Dispose();
		}
		#endregion	
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer Microsoft
United States United States
I started with programming about 19 years ago as a teenager, from my old Commodore moving to PC/Server environment Windows/UNIX SQLServer/Oracle doing gwBasic, QBasic, Turbo Pascal, Assembler, Turbo C, BC, Summer87, Clipper, Fox, SQL, C/C++, Pro*C, VB3/5/6, Java, and today loving C#.

Currently working as SDE on Failover Clustering team for Microsoft.

Passion for most programming languages and my kids Aidan&Nadia.

Comments and Discussions