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

來源:互聯網
上載者:User

標籤:win7   win8   c#音量控制   coreaudioapi   

========================================================
作者: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 };                    }                }                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 實際的音量操作,來自於封裝了一次的 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 };                    }                }                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() 這時 isUserChangeVolumeTrue 所以能調用進行音量改變;

而當音量改變過程中(也包括使用者使用系統的音量條時)將會觸發 volumeControl_OnAudioNotification() 方法進行回調,而在 volumeControl_OnAudioNotification() 方法中,我們首先 將isUserChangeVolume = false

然後使用委託的封裝類進行介面更改;這時介面音量條更改勢必會觸發 mMasterVolumeSlider_ValueChanged()  方法,這時 isUserChangeVolume False狀態,所以不會再次進行音量的更改調用,故而避免死迴圈狀態出現;

在最後事件觸發完後當然是把 isUserChangeVolume 重新設定為 True 了。


至於其他應該都是一看就懂了,介面載入完成時就 讓重新整理線程工作,而介面 Unloaded 時自然就停止工作,避免多餘消耗。


END

附上本次的範例程式碼,以及 DLL 庫,都打包在一個檔案夾中了。

win7 win8 C# 音量控制 Volume

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

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.