========================================================
作者: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 APIsCore Audio APIs 是微软在WIn7之后提供的一套用于控制系统音量的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

在这里还要进行一次简单的调用简化封装,封装为 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() 这时 isUserChangeVolumeTrue 所以能调用进行音量改变;

而当音量改变过程中(也包括用户使用系统的音量条时)将会触发 volumeControl_OnAudioNotification() 方法进行回调,而在 volumeControl_OnAudioNotification() 方法中,我们首先 将isUserChangeVolume = false

然后使用委托的封装类进行界面更改;这时界面音量条更改势必会触发 mMasterVolumeSlider_ValueChanged()  方法,这时 isUserChangeVolumeFalse 状态,所以不会再次进行音量的更改调用,故而避免死循环状态出现;

在最后事件触发完后当然是把 isUserChangeVolume 重新设置为 True 了。

至于其他应该都是一看就懂了,界面加载完成时就 让刷新线程工作,而界面 Unloaded 时自然就停止工作,避免多余消耗。

END

附上本次的示例代码,以及 DLL 库,都打包在一个文件夹中了。

win7 win8 C# 音量控制 Volume

[C#] 控制系统音量-第二章相关推荐

  1. 用matlab建立控制系统的数学模型,第二章控制系统的数学模型.ppt

    第二章控制系统的数学模型精选 3. 控制系统的方框图模型 若已知控制系统的方框图,使用MATLAB函数可实现方框图转换. a).串联 如图所示G1(s)和G2(s)相串联,在MATLAB中可用串联函数 ...

  2. 软件构造 第二章 第一节 软件生命周期和版本控制

    软件构造第二章 第一节 软件生命周期和版本控制 基本内容 Software Development Lifecycle (SDLC) Traditional software process mode ...

  3. 常见的计算机监控系统分类,第二章 发厂计算机监控系统的基本分类.doc

    第二章 发电厂计算机监控系统的基本分类 本章主要了解电厂计算机监控系统的各种分类方法,各自的特征.适用范围,以及对 计算机监控系统的基本要求. 第一节发电厂计算机监控的基本类型及其特点 在生产过程自动 ...

  4. 计算机的指令合成为,第二章计算机操作基础知识doc

    第二章计算机操作基础知识doc (29页) 本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦! 23.9 积分 第二章  计算机操作基础知识第一节  计算机基础知识 ...

  5. 【Git】版本控制管理(第二版) 前言 第一章 第二章

    版本控制管理 前言 第一章 第二章 资源 前言 本书结构 第一章 介绍 总结在开头 1.1 背景 1.2 Git的诞生 1.3 先例 1.4 时间线 第二章 安装Git 2.1 使用Linux上的二进 ...

  6. 微型计算机控制数字量输入输出,[工学]WX_微型计算机控制技术_第二章5.ppt

    [工学]WX_微型计算机控制技术_第二章5 数字量输出驱动电路 三极管驱动电路 继电器驱动电路 晶闸管驱动电路 固态继电器驱动电路 达林顿驱动电路 微型计算机控制技术 第2章 输入/输出接口与过程通道 ...

  7. 人工智能期末——第二章知识的表示

    第二章 知识的表示 知识表示是建立在符号主义的基础上的 符号主义:人类的智能活动主要是获得并运用知识,知识是智能的基础 为了使计算机具有智能.模拟人类的智能行为,就必须使它具有知识 知识需要用适当的模 ...

  8. 计算机控制系统的五种类型,计算机控制系统习题(1-5章

    <计算机控制系统习题(1-5章>由会员分享,可在线阅读,更多相关<计算机控制系统习题(1-5章(9页珍藏版)>请在人人文库网上搜索. 1.计算机控制系复习题(1-5章)第一章 ...

  9. 【ERP】ERP发展阶段有哪些?对ERP发展各个阶段概念的理解(20年3月29日第二章学习笔记)

    ERP发展历经五大阶段 1.ROP库存订货点法 2.MRP物料需求计划 3.闭环MRP 4.MRPII制造业资源计划 5.ERP企业资源计划 1.ROP库存订货点法 ROP,库存订货点法,很好理解,就 ...

最新文章

  1. 如何得到Mysql每个分组中的第N条记录
  2. 人工智能灵魂注入,燃烧你的卡路里——2018,你AI了吗!?
  3. Spring MVC基础知识整理➣国际化和异常处理
  4. [项目更新] 集成RabbitMQ队列与EventBus总线
  5. python oj 输入_Python写OJ题时输入问题
  6. swift通知栏推送_如何使用Swift使用推送通知构建食品交付应用
  7. Swin Transformer V2!MSRA原班人马提出了30亿参数版本的Swin Transformer!
  8. uva 714 Copying Books
  9. narwal无法连接机器人_库卡机器人控制系统主机出现MFC3故障维修
  10. 获取网络图片并异步更新UI
  11. NATS 分布式消息队列系统
  12. php爬取ins图片_python爬取【追新番】日剧资源
  13. 关于联想笔记本无线网老是掉线的解决方法
  14. python读书心得体会范文_读书心得体会范文10篇完美版
  15. rrpp协议如何修改_Rrpp详解
  16. 单链表基本操作的实现——前插法与后插法创建单链表
  17. 软件自动化测试可行性分析,基于 AI 的软件自动化测试思考与实践—kylinTOP 测试与监控平台...
  18. 易语言- 定义一个系统范围的热键 RegisterHotKey UnregisterHotKey
  19. android studio 自定义mk文件
  20. 安装LabelImage

热门文章

  1. 微信公众平台后台数据如何分析
  2. 【Flocking算法】海王的鱼塘是怎样炼成的
  3. Java Web基础
  4. 卡西欧计算机蓝屏的处理方法,电脑出现蓝屏怎么办 电脑出现蓝屏解决方法【图文详解】...
  5. PC端浏览器自动填充账号密码输入框问题该如何解决?
  6. 按Right-BICEP的测试用例
  7. QNX系列:五、资源管理器(1)官方文档的翻译
  8. 使用Consol线连接路由器
  9. python27安装get-pip
  10. 腹有诗书气自华——记环宇通软CEO骆永华 1