最新公告
  • 欢迎您光临波比源码,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入我们
  • [C#] 控制系统音量-第二章

    ========================================================
    作者: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

    波比源码 – 精品源码模版分享 | www.bobi11.com
    1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
    2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
    3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
    4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
    5. 如有链接无法下载、失效或广告,请联系管理员处理!
    6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需!
    7. 如遇到加密压缩包,请使用WINRAR解压,如遇到无法解压的请联系管理员!

    波比源码 » [C#] 控制系统音量-第二章

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    波比源码
    一个高级程序员模板开发平台
    升级波友尊享更多特权立即升级