[C#] 控制系統(tǒng)音量-第二章
來(lái)源:程序員人生 發(fā)布時(shí)間:2014-12-23 08:50:09 閱讀次數(shù):3272次
========================================================
作者:qiujuer
博客:blog.csdn.net/qiujuer
網(wǎng)站:www.qiujuer.net
開(kāi)源庫(kù):Genius-Android
轉(zhuǎn)載請(qǐng)注明出處:http://blog.csdn.net/qiujuer/article/details/41575517
========================================================
引入
在很久之前寫(xiě)了1篇 [C#] 控制系統(tǒng)音量-第1章 ,忘記是甚么時(shí)候?qū)懙牧耍徊贿^(guò)并沒(méi)有忘記有這回事兒,不過(guò)看見(jiàn)沒(méi)有甚么人需要所以就沒(méi)有出后面的文章了。前天突然看見(jiàn)評(píng)論有人需要,所以覺(jué)得有必要完善1下;總結(jié)了1下這是第2章,同時(shí)也是終章;之前準(zhǔn)備寫(xiě)多章仔細(xì)分析1下的,現(xiàn)在看來(lái)就介紹1下如何使用吧。
問(wèn)題
在第1章中,控制電腦音量是能夠?qū)崿F(xiàn)的,但是只支持XP系統(tǒng);這無(wú)疑是糟的;現(xiàn)在這個(gè)階段使用XP的還有多少?本篇為支持Win7及其以上版本音量控制而生。
兼容性(C#)
Win7、Win8、Win8.1
前戲
在開(kāi)始之前有必要介紹1下 Core Audio APIs ,甚么是 Core Audio APIs ?Core Audio APIs
是微軟在WIn7以后提供的1套用于控制系統(tǒng)音量的Api,其具有以下特點(diǎn):
- 低延時(shí),幾近無(wú)故障的音頻流。
- 提高可靠性 ( 很多音頻函數(shù)從核心態(tài)移到了用戶態(tài) )
- 提高了安全性 (在安全的,低優(yōu)先級(jí)別的線程處理被保護(hù)的音頻內(nèi)容)
- 分配了特定的系統(tǒng)級(jí)別的規(guī)則 (console, multimedia, communications) 給單獨(dú)的音頻裝備。
- 用戶可以直接操作,相應(yīng) endpoint 裝備的軟件抽象 ( 如:擴(kuò)音器,耳麥及麥克風(fēng) ) 以下的高層 API 是以 Core Audio APIs 來(lái)工作的。
相干介紹:
http://msdn.microsoft.com/en-us/library/dd370802(VS.85).aspx
http://msdn.microsoft.com/en-us/library/dd370784(v=vs.85).aspx
固然這里我們其實(shí)不是直接使用此API,由于其是C++的調(diào)用接口,在這里對(duì)其進(jìn)行了封裝,封裝成C#下能直接調(diào)用的dll文件;后面添加。
CoreAudioApi.dll 包結(jié)構(gòu):

在這里就不詳細(xì)介紹其中代碼,打包時(shí)會(huì)同時(shí)把 CoreAudioApi.pdb 文件打包,在調(diào)試時(shí)能進(jìn)入其中查看代碼。
至于導(dǎo)入 DLL 到項(xiàng)目中,這個(gè)也無(wú)需說(shuō)了吧。
CodeTime
在這里還要進(jìn)行1次簡(jiǎn)單的調(diào)用簡(jiǎn)化封裝,封裝為 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; }
}
}
可以看到,在代碼中為了外面調(diào)用的方便性,我們采取了單列模式,固然這里沒(méi)有單獨(dú)對(duì)多線程添加鎖的機(jī)制。可自行添加其 Instance 部份。
其中有4個(gè)變量:
- VolumeControl 固然是為了單列而生的
- MMDevice 實(shí)際的音量操作,來(lái)自于封裝了1次的 CoreAudioApi.dll
庫(kù)
- AudioNotificationDelegate 可以看見(jiàn)最后面的地方實(shí)際上是1個(gè)事件拜托,用于事件的通知,主要作用是當(dāng)系統(tǒng)音量改變時(shí)通知主界面進(jìn)行刷新界面
- InitializeSucceed 這個(gè)是用于記錄是不是初始化成功的參數(shù);固然可以去掉
static VolumeControl Instance 用于保證單列的運(yùn)行
在類的構(gòu)造函數(shù)中,可以看到:
MMDeviceEnumerator DevEnum = new MMDeviceEnumerator();
device = DevEnum.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia);
device.AudioEndpointVolume.OnVolumeNotification += new AudioEndpointVolumeNotificationDelegate(AudioEndpointVolume_OnVolumeNotification);
其中 實(shí)例化了1個(gè)
MMDeviceEnumerator 類,然后初始化了
MMDevice 屬性;同時(shí)在這里進(jìn)行了注冊(cè)事件,讓音量改變時(shí)調(diào)用方法
AudioEndpointVolume_OnVolumeNotification()
而在方法 AudioEndpointVolume_OnVolumeNotification() 中又調(diào)用了當(dāng)前類的拜托事件,用于觸發(fā)事件刷新界面;同時(shí)對(duì)傳遞的參數(shù)進(jìn)行了封裝;封裝為了類:AudioNotificationEventArgs
在類 AudioNotificationEventArgs 中:
public class AudioNotificationEventArgs : EventArgs
{
public double MasterVolume { get; set; }
public bool Muted { get; set; }
}
包括兩個(gè)屬性,分別是當(dāng)前音量大小和是不是靜音。
繼續(xù)分析我們的主類:
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; }
}
這兩個(gè)屬性,分別用于設(shè)置與獲得當(dāng)前主音量大小和是不是靜音操作的封裝。
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 };
}
}
該方法用于獲得當(dāng)前的音量信息,分別是
主音量、
左聲道、
右聲道。
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>
對(duì)應(yīng)的界面:

界面代碼:
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;
}
}
}
還是從屬性開(kāi)始,3個(gè)屬性:
VolumeControl 這個(gè)很簡(jiǎn)單了吧,就是上面封裝的成果
isUserChangeVolume 這個(gè)是用于排除系統(tǒng)回調(diào)時(shí)觸發(fā) Slider 控件的 ValueChanged()
事件,避免無(wú)窮循環(huán)
DispatcherTimer 用于刷新界面中的音量條
開(kāi)始說(shuō)說(shuō)方法:
InitializeAudioControl() 用于初始化 VolumeControl 同時(shí),添加事件回調(diào),和初始化 DispatcherTimer Timer
volumeControl_OnAudioNotification() 回調(diào)方法
volumeControlTimer_Tick() 這個(gè)就是 DispatcherTimer 刷新界面的方法
mMasterVolumeSlider_ValueChanged() 這個(gè)就更加簡(jiǎn)單了,界面的事件觸發(fā)方法
必要說(shuō)明:
在用戶撥動(dòng)界面的屬性條的時(shí)候會(huì)觸發(fā) mMasterVolumeSlider_ValueChanged() 這時(shí)候 isUserChangeVolume
是 True 所以能調(diào)用進(jìn)行音量改變;
而當(dāng)音量改變進(jìn)程中(也包括用戶使用系統(tǒng)的音量條時(shí))將會(huì)觸發(fā) volumeControl_OnAudioNotification()
方法進(jìn)行回調(diào),而在 volumeControl_OnAudioNotification() 方法中,我們首先 將isUserChangeVolume = false;
然后使用拜托的封裝類進(jìn)行界面更改;這時(shí)候界面音量條更改必將會(huì)觸發(fā) mMasterVolumeSlider_ValueChanged()
方法,這時(shí)候 isUserChangeVolume 是 False
狀態(tài),所以不會(huì)再次進(jìn)行音量的更改調(diào)用,故而避免死循環(huán)狀態(tài)出現(xiàn);
在最后事件觸發(fā)完后固然是把 isUserChangeVolume 重新設(shè)置為
True 了。
至于其他應(yīng)當(dāng)都是1看就懂了,界面加載完成時(shí)就 讓刷新線程工作,而界面 Unloaded 時(shí)自然就停止工作,避免過(guò)剩消耗。
END
附上本次的示例代碼,和 DLL 庫(kù),都打包在1個(gè)文件夾中了。
win7 win8 C# 音量控制 Volume
生活不易,碼農(nóng)辛苦
如果您覺(jué)得本網(wǎng)站對(duì)您的學(xué)習(xí)有所幫助,可以手機(jī)掃描二維碼進(jìn)行捐贈(zèng)