在.NET框架中,C#语言提供了丰富的类库支持,但有时候会发现一些基本的Windows功能在.NET中并没有直接的实现,比如Windows消息提示音(MessageBeep)。本文将介绍如何通过C#实现这一功能,以及如何自定义提示音的播放。
自从.NET框架Beta 2版本以来,C#新闻组中经常有人询问“MessageBeep在哪里?”这个问题。直到DirectX9.0 SDK发布之前,答案通常只有两个:要么使用PInvoke调用非托管代码,要么引用Microsoft Visual Basic .NET运行时库。对于C#程序员来说,前者是使用非托管代码,后者则是一种难以接受的解决方案。随着DirectX 9.0的发布,有机会彻底解决这个问题。
在.NET中实现MessageBeep功能相对简单。首先需要在Windows注册表中查找声音类型与系统声音的映射关系。例如,定义了以下枚举类型:
public enum BeepTypes : long {
MB_OK = 0x0,
MB_ICONHAND = 0x00000010,
MB_ICONQUESTION = 0x00000020,
MB_ICONEXCLAMATION = 0x00000030,
MB_ICONASTERISK = 0x00000040,
SIMPLE_BEEP = 0xffffffff
};
然后,通过后台线程调用DirectX的DirectSound函数即可实现。需要注意的是,如果没有声卡,SimpleBeep声音将通过内部扬声器播放,而DirectX无法访问内部扬声器。
实现Beep类比MessageBeep类要复杂一些。基本概念是使用DirectX的次级缓冲区,并将其设置为循环模式,然后将音调样本写入缓冲区未使用的区域。
播放第二次声音时,可能会遇到一个问题:新音调的开始会听到上一个音调的一小部分。这个问题可以通过在播放完声音后用零填充Beep缓冲区,并在循环模式禁用的情况下再播放一次来解决。
将采样率限制为每秒8K个样本(sps),最大音调为4KHz。但在1 GHz PIII上以32K sps运行代码也没有问题。输出的是16位PCM立体声信号。
与MessageBeep不同,Beep的实现与非托管版本并不完全相同。非托管Beep输出的是方波,而创建的是正弦波,并在零点交叉时将信号驱动到极值以生成方波。在所有频率下听起来并不完全相同。需要做一些工作来创建相同的声音,但个人更喜欢正弦波。
MessageBeep类中只有一个方法:
MessageBeep(BeepTypes mbType);
Beep类可以这样调用:
void Beep();
void Beep(double frequency, System.Int32 duration);
其他方法可以设置频率、音量、音调持续时间,并开启或关闭方波模式。MessageBeep函数立即返回,Beep函数在音调完成时返回,与Win32函数相同。这两个类都在Win32Emu命名空间中(Emu代表仿真)。