[C#] 控制系统音量-第二章
========================================================
作者:qiujuer
博客:blog.csdn.net/qiujuer
网站:www.qiujuer.net
开源库:Genius-Android
转载请注明出处:http://blog.csdn.net/qiujuer/article/details/41575517
========================================================
引入
在很久之前写了一篇 [C#] 控制系统音量-第一章 ,忘记是什么时候写的了;不过并没有忘记有这回事儿,不过看见没有什么人需要所以就没有出后面的文章了。前天突然看见评论有人需要,所以觉得有必要完善一下;总结了一下这是第二章,同时也是终章;以前准备写多章仔细分析一下的,现在看来就介绍一下如何使用吧。
问题
在第一章中,控制电脑音量是能够实现的,但是只支持XP系统;这无疑是糟糕的;现在这个阶段使用XP的还有多少?本篇为支持Win7及其以上版本音量控制而生。
兼容性(C#)
Win7、Win8、Win8.1
前戏
在开始之前有必要介绍一下 Core Audio APIs ,什么是 Core Audio APIs ?Core Audio APIs 是微软在WIn7之后提供的一套用于控制系统音量的Api,其具有以下特点:
- 低延时,几乎无故障的音频流。
- 提高可靠性 ( 很多音频函数从核心态移到了用户态 )
- 提高了安全性 (在安全的,低优先级别的线程处理被保护的音频内容)
- 分配了特定的系统级别的规则 (console, multimedia, communications) 给单独的音频设备。
- 用户可以直接操作,相应 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
在这里还要进行一次简单的调用简化封装,封装为 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 };}}elsereturn 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 实际的音量操作,来自于封装了一次的 CoreAudioApi.dll 库
- AudioNotificationDelegate 可以看见最后面的地方其实是一个事件委托,用于事件的通知,主要作用是当系统音量改变时通知主界面进行刷新界面
- InitializeSucceed 这个是用于记录是否初始化成功的参数;当然可以去掉
static VolumeControl Instance 用于保证单列的运行
在类的构造函数中,可以看到:
MMDeviceEnumerator DevEnum = new MMDeviceEnumerator();device = DevEnum.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia);device.AudioEndpointVolume.OnVolumeNotification += new AudioEndpointVolumeNotificationDelegate(AudioEndpointVolume_OnVolumeNotification);
其中 实例化了一个 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 };}}elsereturn 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 了。
至于其他应该都是一看就懂了,界面加载完成时就 让刷新线程工作,而界面 Unloaded 时自然就停止工作,避免多余消耗。
END
附上本次的示例代码,以及 DLL 库,都打包在一个文件夹中了。
win7 win8 C# 音量控制 Volume
[C#] 控制系统音量-第二章相关推荐
- 用matlab建立控制系统的数学模型,第二章控制系统的数学模型.ppt
第二章控制系统的数学模型精选 3. 控制系统的方框图模型 若已知控制系统的方框图,使用MATLAB函数可实现方框图转换. a).串联 如图所示G1(s)和G2(s)相串联,在MATLAB中可用串联函数 ...
- 软件构造 第二章 第一节 软件生命周期和版本控制
软件构造第二章 第一节 软件生命周期和版本控制 基本内容 Software Development Lifecycle (SDLC) Traditional software process mode ...
- 常见的计算机监控系统分类,第二章 发厂计算机监控系统的基本分类.doc
第二章 发电厂计算机监控系统的基本分类 本章主要了解电厂计算机监控系统的各种分类方法,各自的特征.适用范围,以及对 计算机监控系统的基本要求. 第一节发电厂计算机监控的基本类型及其特点 在生产过程自动 ...
- 计算机的指令合成为,第二章计算机操作基础知识doc
第二章计算机操作基础知识doc (29页) 本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦! 23.9 积分 第二章 计算机操作基础知识第一节 计算机基础知识 ...
- 【Git】版本控制管理(第二版) 前言 第一章 第二章
版本控制管理 前言 第一章 第二章 资源 前言 本书结构 第一章 介绍 总结在开头 1.1 背景 1.2 Git的诞生 1.3 先例 1.4 时间线 第二章 安装Git 2.1 使用Linux上的二进 ...
- 微型计算机控制数字量输入输出,[工学]WX_微型计算机控制技术_第二章5.ppt
[工学]WX_微型计算机控制技术_第二章5 数字量输出驱动电路 三极管驱动电路 继电器驱动电路 晶闸管驱动电路 固态继电器驱动电路 达林顿驱动电路 微型计算机控制技术 第2章 输入/输出接口与过程通道 ...
- 人工智能期末——第二章知识的表示
第二章 知识的表示 知识表示是建立在符号主义的基础上的 符号主义:人类的智能活动主要是获得并运用知识,知识是智能的基础 为了使计算机具有智能.模拟人类的智能行为,就必须使它具有知识 知识需要用适当的模 ...
- 计算机控制系统的五种类型,计算机控制系统习题(1-5章
<计算机控制系统习题(1-5章>由会员分享,可在线阅读,更多相关<计算机控制系统习题(1-5章(9页珍藏版)>请在人人文库网上搜索. 1.计算机控制系复习题(1-5章)第一章 ...
- 【ERP】ERP发展阶段有哪些?对ERP发展各个阶段概念的理解(20年3月29日第二章学习笔记)
ERP发展历经五大阶段 1.ROP库存订货点法 2.MRP物料需求计划 3.闭环MRP 4.MRPII制造业资源计划 5.ERP企业资源计划 1.ROP库存订货点法 ROP,库存订货点法,很好理解,就 ...
最新文章
- 如何得到Mysql每个分组中的第N条记录
- 人工智能灵魂注入,燃烧你的卡路里——2018,你AI了吗!?
- Spring MVC基础知识整理➣国际化和异常处理
- [项目更新] 集成RabbitMQ队列与EventBus总线
- python oj 输入_Python写OJ题时输入问题
- swift通知栏推送_如何使用Swift使用推送通知构建食品交付应用
- Swin Transformer V2!MSRA原班人马提出了30亿参数版本的Swin Transformer!
- uva 714 Copying Books
- narwal无法连接机器人_库卡机器人控制系统主机出现MFC3故障维修
- 获取网络图片并异步更新UI
- NATS 分布式消息队列系统
- php爬取ins图片_python爬取【追新番】日剧资源
- 关于联想笔记本无线网老是掉线的解决方法
- python读书心得体会范文_读书心得体会范文10篇完美版
- rrpp协议如何修改_Rrpp详解
- 单链表基本操作的实现——前插法与后插法创建单链表
- 软件自动化测试可行性分析,基于 AI 的软件自动化测试思考与实践—kylinTOP 测试与监控平台...
- 易语言- 定义一个系统范围的热键 RegisterHotKey UnregisterHotKey
- android studio 自定义mk文件
- 安装LabelImage