程序员人生 网站导航

[C#] 控制系统音量-第二章

栏目:php教程时间:2014-12-23 08:50:09

========================================================
作者:qiujuer
博客:blog.csdn.net/qiujuer
网站:www.qiujuer.net
开源库:Genius-Android
转载请注明出处:http://blog.csdn.net/qiujuer/article/details/41575517

========================================================

引入

在很久之前写了1篇 [C#] 控制系统音量-第1章 ,忘记是甚么时候写的了;不过并没有忘记有这回事儿,不过看见没有甚么人需要所以就没有出后面的文章了。前天突然看见评论有人需要,所以觉得有必要完善1下;总结了1下这是第2章,同时也是终章;之前准备写多章仔细分析1下的,现在看来就介绍1下如何使用吧。

问题

在第1章中,控制电脑音量是能够实现的,但是只支持XP系统;这无疑是糟的;现在这个阶段使用XP的还有多少?本篇为支持Win7及其以上版本音量控制而生。

兼容性(C#)

Win7、Win8、Win8.1

前戏

在开始之前有必要介绍1下 Core Audio APIs ,甚么是 Core Audio APIsCore Audio APIs 是微软在WIn7以后提供的1套用于控制系统音量的Api,其具有以下特点:

  1. 低延时,几近无故障的音频流。
  2. 提高可靠性 ( 很多音频函数从核心态移到了用户态 )
  3. 提高了安全性 (在安全的,低优先级别的线程处理被保护的音频内容)
  4. 分配了特定的系统级别的规则 (console, multimedia, communications) 给单独的音频装备。
  5. 用户可以直接操作,相应 endpoint 装备的软件抽象 ( 如:扩音器,耳麦及麦克风 ) 以下的高层 API 是以 Core Audio APIs 来工作的。

相干介绍:

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.cs

namespace 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, 0 }; } } else return new double[3] { 0, 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个变量:

  • VolumeControl 固然是为了单列而生的
  • MMDevice 实际的音量操作,来自于封装了1次的 CoreAudioApi.dll
  • AudioNotificationDelegate 可以看见最后面的地方实际上是1个事件拜托,用于事件的通知,主要作用是当系统音量改变时通知主界面进行刷新界面
  • InitializeSucceed 这个是用于记录是不是初始化成功的参数;固然可以去掉

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, device.AudioMeterInformation.PeakValues[0] * 100, device.AudioMeterInformation.PeakValues[1] * 100 }; } catch { return new double[3] { 0, 0, 0 }; } } else return new double[3] { 0, 0, 0 }; } }
该方法用于获得当前的音量信息,分别是主音量左声道右声道

ViewCode

在这里使用WPF作为示例,界面代码:

<Grid Margin="10"> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition /> </Grid.ColumnDefinitions> <StackPanel> <Label Content="音量" /> <Label Content="主声道:" Margin="0,10,0,0"/> <ProgressBar x:Name="mMasterPBar" Minimum="0" Maximum="100" Width="170" HorizontalAlignment="Right" Margin="0,0,10,0"/> <Label Content="左声道:" Margin="0,10,0,0"/> <ProgressBar x:Name="mLeftPBar" Minimum="0" Maximum="100" Width="170" HorizontalAlignment="Right" Margin="0,0,10,0"/> <Label Content="右声道:" Margin="0,10,0,0"/> <ProgressBar x:Name="mRightPBar" Minimum="0" Maximum="100" Width="170" HorizontalAlignment="Right" Margin="0,0,10,0"/> <Label Content="操作:" Margin="0,20,0,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

------分隔线----------------------------
------分隔线----------------------------

最新技术推荐