[C#] 控制系统音量-第二章
======================================================== 引入在很久之前写了1篇 [C#] 控制系统音量-第1章 ,忘记是甚么时候写的了;不过并没有忘记有这回事儿,不过看见没有甚么人需要所以就没有出后面的文章了。前天突然看见评论有人需要,所以觉得有必要完善1下;总结了1下这是第2章,同时也是终章;之前准备写多章仔细分析1下的,现在看来就介绍1下如何使用吧。 问题在第1章中,控制电脑音量是能够实现的,但是只支持XP系统;这无疑是糟的;现在这个阶段使用XP的还有多少?本篇为支持Win7及其以上版本音量控制而生。 兼容性(C#)Win7、Win8、Win8.1 前戏在开始之前有必要介绍1下 Core Audio APIs ,甚么是 Core Audio APIs ?Core Audio APIs 是微软在WIn7以后提供的1套用于控制系统音量的Api,其具有以下特点:
相干介绍:http://msdn.microsoft.com/en-us/library/dd370802(VS.85).aspx http://msdn.microsoft.com/en-us/library/dd370784(v=vs.85).aspx 固然这里我们其实不是直接使用此API,由于其是C++的调用接口,在这里对其进行了封装,封装成C#下能直接调用的dll文件;后面添加。 CoreAudioApi.dll 包结构:在这里就不详细介绍其中代码,打包时会同时把 CoreAudioApi.pdb 文件打包,在调试时能进入其中查看代码。 至于导入 DLL 到项目中,这个也无需说了吧。 CodeTime在这里还要进行1次简单的调用简化封装,封装为 VolumeControl class. VolumeControl.csnamespace Volume
{
public class VolumeControl
{
private static VolumeControl _VolumeControl;
private MMDevice device;
public event AudioNotificationDelegate OnAudioNotification;
public bool InitializeSucceed;
public static VolumeControl Instance
{
get
{
if (_VolumeControl == null)
_VolumeControl = new VolumeControl();
return _VolumeControl;
}
}
private VolumeControl()
{
MMDeviceEnumerator DevEnum = new MMDeviceEnumerator();
try
{
device = DevEnum.GetDefaultAudioEndpoint(EDataFlow.eRender,ERole.eMultimedia);
device.AudioEndpointVolume.OnVolumeNotification += new AudioEndpointVolumeNotificationDelegate(AudioEndpointVolume_OnVolumeNotification);
InitializeSucceed = true;
}
catch
{
InitializeSucceed = false;
}
}
private void AudioEndpointVolume_OnVolumeNotification(AudioVolumeNotificationData data)
{
if (InitializeSucceed && this.OnAudioNotification != null)
{
this.OnAudioNotification(null,new AudioNotificationEventArgs() { MasterVolume = data.MasterVolume * 100,Muted = data.Muted });
}
}
public double MasterVolume
{
get { return InitializeSucceed ? device.AudioEndpointVolume.MasterVolumeLevelScalar * 100 : 0; }
set
{
if (InitializeSucceed)
{
device.AudioEndpointVolume.MasterVolumeLevelScalar = (float)(value / 100.0f);
if (this.IsMute)
this.IsMute = false;
}
}
}
public bool IsMute
{
get { return InitializeSucceed ? device.AudioEndpointVolume.Mute : true; }
set { if (InitializeSucceed)device.AudioEndpointVolume.Mute = value; }
}
public double[] AudioMeterInformation
{
get
{
if (InitializeSucceed)
{
try
{
return new double[3]{
device.AudioMeterInformation.MasterPeakValue * 100.00,device.AudioMeterInformation.PeakValues[0] * 100,device.AudioMeterInformation.PeakValues[1] * 100
};
}
catch
{
return new double[3] { 0,0 };
}
}
else
return new double[3] { 0,0 };
}
}
}
public delegate void AudioNotificationDelegate(object sender,AudioNotificationEventArgs e);
public class AudioNotificationEventArgs : EventArgs
{
public double MasterVolume { get; set; }
public bool Muted { get; set; }
}
} 可以看到,在代码中为了外面调用的方便性,我们采取了单列模式,固然这里没有单独对多线程添加锁的机制。可自行添加其 Instance 部份。
其中有4个变量:
static VolumeControl Instance 用于保证单列的运行 在类的构造函数中,可以看到: MMDeviceEnumerator DevEnum = new MMDeviceEnumerator();
device = DevEnum.GetDefaultAudioEndpoint(EDataFlow.eRender,ERole.eMultimedia);
device.AudioEndpointVolume.OnVolumeNotification += new AudioEndpointVolumeNotificationDelegate(AudioEndpointVolume_OnVolumeNotification);
其中 实例化了1个 MMDeviceEnumerator 类,然后初始化了 MMDevice 属性;同时在这里进行了注册事件,让音量改变时调用方法 AudioEndpointVolume_OnVolumeNotification()
而在方法 AudioEndpointVolume_OnVolumeNotification() 中又调用了当前类的拜托事件,用于触发事件刷新界面;同时对传递的参数进行了封装;封装为了类:AudioNotificationEventArgs 在类 AudioNotificationEventArgs 中: public class AudioNotificationEventArgs : EventArgs
{
public double MasterVolume { get; set; }
public bool Muted { get; set; }
} 包括两个属性,分别是当前音量大小和是不是静音。
继续分析我们的主类: public double MasterVolume
{
get { return InitializeSucceed ? device.AudioEndpointVolume.MasterVolumeLevelScalar * 100 : 0; }
set
{
if (InitializeSucceed)
{
device.AudioEndpointVolume.MasterVolumeLevelScalar = (float)(value / 100.0f);
if (this.IsMute)
this.IsMute = false;
}
}
}
public bool IsMute
{
get { return InitializeSucceed ? device.AudioEndpointVolume.Mute : true; }
set { if (InitializeSucceed)device.AudioEndpointVolume.Mute = value; }
} 这两个属性,分别用于设置与获得当前主音量大小和是不是静音操作的封装。
public double[] AudioMeterInformation
{
get
{
if (InitializeSucceed)
{
try
{
return new double[3]{
device.AudioMeterInformation.MasterPeakValue * 100.00,0 };
}
} 该方法用于获得当前的音量信息,分别是主音量、左声道、右声道。
ViewCode在这里使用WPF作为示例,界面代码: <Grid Margin="10">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<StackPanel>
<Label Content="音量" />
<Label Content="主声道:" Margin="0,10,0"/>
<ProgressBar x:Name="mMasterPBar"
Minimum="0"
Maximum="100"
Width="170"
HorizontalAlignment="Right"
Margin="0,0"/>
<Label Content="左声道:" Margin="0,0"/>
<ProgressBar x:Name="mLeftPBar"
Minimum="0"
Maximum="100"
Width="170"
HorizontalAlignment="Right"
Margin="0,0"/>
<Label Content="右声道:" Margin="0,0"/>
<ProgressBar x:Name="mRightPBar"
Minimum="0"
Maximum="100"
Width="170"
HorizontalAlignment="Right"
Margin="0,0"/>
<Label Content="操作:" Margin="0,20,0" HorizontalAlignment="Right" />
</StackPanel>
<Slider Grid.Column="1"
Name="mMasterVolumeSlider"
Orientation="Vertical"
Height="200"
Maximum="100"
ValueChanged="mMasterVolumeSlider_ValueChanged" />
</Grid> 对应的界面:界面代码: public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
InitializeAudioControl();
}
private void Page_Loaded(object sender,RoutedEventArgs e)
{
volumeControlTimer.Start();
}
private void Page_Unloaded(object sender,RoutedEventArgs e)
{
volumeControlTimer.Stop();
}
private VolumeControl volumeControl;
private bool isUserChangeVolume = true;
private DispatcherTimer volumeControlTimer;
private void InitializeAudioControl()
{
volumeControl = VolumeControl.Instance;
volumeControl.OnAudioNotification += volumeControl_OnAudioNotification;
volumeControl_OnAudioNotification(null,new AudioNotificationEventArgs() { MasterVolume = volumeControl.MasterVolume });
volumeControlTimer = new DispatcherTimer();
volumeControlTimer.Interval = TimeSpan.FromTicks(150);
volumeControlTimer.Tick += volumeControlTimer_Tick;
}
void volumeControl_OnAudioNotification(object sender,AudioNotificationEventArgs e)
{
this.isUserChangeVolume = false;
try
{
this.Dispatcher.Invoke(new Action(() => { mMasterVolumeSlider.Value = e.MasterVolume; }));
}
catch { }
this.isUserChangeVolume = true;
}
void volumeControlTimer_Tick(object sender,EventArgs e)
{
double[] information = volumeControl.AudioMeterInformation;
mMasterPBar.Value = information[0];
mLeftPBar.Value = information[1];
mRightPBar.Value = information[2];
}
private void mMasterVolumeSlider_ValueChanged(object sender,RoutedPropertyChangedEventArgs<double> e)
{
if (isUserChangeVolume)
{
volumeControl.MasterVolume = mMasterVolumeSlider.Value;
}
}
} 还是从属性开始,3个属性:VolumeControl 这个很简单了吧,就是上面封装的成果 isUserChangeVolume 这个是用于排除系统回调时触发 Slider 控件的 ValueChanged() 事件,避免无穷循环 DispatcherTimer 用于刷新界面中的音量条 开始说说方法:InitializeAudioControl() 用于初始化 VolumeControl 同时,添加事件回调,和初始化 DispatcherTimer Timer volumeControl_OnAudioNotification() 回调方法 volumeControlTimer_Tick() 这个就是 DispatcherTimer 刷新界面的方法 mMasterVolumeSlider_ValueChanged() 这个就更加简单了,界面的事件触发方法 必要说明:在用户拨动界面的属性条的时候会触发 mMasterVolumeSlider_ValueChanged() 这时候 isUserChangeVolume 是 True 所以能调用进行音量改变; 而当音量改变进程中(也包括用户使用系统的音量条时)将会触发 volumeControl_OnAudioNotification() 方法进行回调,而在 volumeControl_OnAudioNotification() 方法中,我们首先 将isUserChangeVolume = false; 然后使用拜托的封装类进行界面更改;这时候界面音量条更改必将会触发 mMasterVolumeSlider_ValueChanged() 方法,这时候 isUserChangeVolume 是 False 状态,所以不会再次进行音量的更改调用,故而避免死循环状态出现; 在最后事件触发完后固然是把 isUserChangeVolume 重新设置为 True 了。
至于其他应当都是1看就懂了,界面加载完成时就 让刷新线程工作,而界面 Unloaded 时自然就停止工作,避免过剩消耗。
END附上本次的示例代码,和 DLL 库,都打包在1个文件夹中了。 win7 win8 C# 音量控制 Volume (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |