//
// 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
}
}