[C#] 控制系統音量-第二章
來源:程序員人生 發布時間:2014-12-23 08:50:09 閱讀次數:3277次
========================================================
作者: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 APIs ?Core Audio APIs
是微軟在WIn7以后提供的1套用于控制系統音量的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
在這里還要進行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
生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈