<!-- Download Links -->
<!-- Article image -->
The code submitted presents two managed .NET C# classes,
Beep(), which emulate the Win32 platform calls of the same names. Installation of Microsoft DirectX 9.0 is required. The code has been tested on Windows XP and Win2K using standard AC97 onboard sound cards.
The .NET Window Forms test application, MessageBeepTest, plays the six MessageBeep sounds using both managed DirectX and unmanaged PInvoke versions. If successful, the six sounds will sound the same using the two techniques (the sounds can be changed or turned off using the Windows Control Panel).
Additionally, the Beep function will play a sine or square wave of a specified frequency and duration using both methods.
Ever since the Beta 2 days, one of the questions asked most frequently in the C# newsgroups is "Where is the MessageBeep?", "Where is the MessageBeep?", and again "Where is the MessageBeep?" Until the DirectX 9.0 SDK was released last month, the answer has always been either:
- Drop into unmanaged code with PInvoke or
- Add a reference to the Microsoft Visual Basic .NET runtime dll.
The former solution is of course unmanaged and the latter solution is of course, for a C# programmer, an abomination. Tacit admission that your chosen .NET language is somehow--incomplete. With the release of DirectX 9.0 and some time made free over the recent holiday season, I was allowed to investigate if indeed the MessageBeep question could be answered once and for all.
If I've done it correctly, the MessageBeep code was actually very easy. Some research on the web was required to find where in the Windows Registry the sounds are mapped to the six sound types: System Asterisk, System Exclamation, System Hand, System Question, System Default and Simple Beep.
#define MB_OK 0x00000000L
#define MB_ICONHAND 0x00000010L
#define MB_ICONQUESTION 0x00000020L
#define MB_ICONEXCLAMATION 0x00000030L
#define MB_ICONASTERISK 0x00000040L
#define Simple Beep 0xffffffffL
public enumBeepTypes : long
MB_OK = 0x0,
MB_ICONHAND = 0x00000010,
MB_ICONQUESTION = 0x00000020,
MB_ICONEXCLAMATION = 0x00000030,
MB_ICONASTERISK = 0x00000040,
SIMPLE_BEEP = 0xffffffff
Then just cause the DirectX DirectSound function to be run on a background thread and as quick as "Bob's your Uncle**" you're finished. One difference between the techniques is that if no sound card is available, the SimpleBeep sound is generated using an internal speaker in the unmanaged version. I found no way to access the internal speaker using DirectX. An AC97 hardware independent way of accessing the speaker using unsafe pointers and a managed wrapper might be possible but I'm not sure how widespread the use of the AC97 standard is.
**This CP phrase is becomming popular in general use and I have no idea what it means.
Beep() class was more difficult. The concept is simple enough, use a DirectX secondary buffer in the "looping" mode and write the tone samples to an area in the buffer DirectX isn't using at the moment.
The problem was with playing the sound the second time. A small portion of the last tone is heard at the beginning of the new tone. But it is also heard when a different secondary buffer is played! For instance when a buffer created for a MessageBeep sound is played after Beep. The problem seems to be fixed by filling the Beep buffer with zeros when finished and playing the buffer one last time with looping disabled.
I've limited the sample rate to 8K samples per second (sps) with a maximum tone of 4KHz. But I've run the code at 32K sps on a 1 GHz PIII without problems. A sixteen bit PCM word is written out in stereo.
But as with MessageBeep the implementation isn't quite the same as the unmanaged version. The unmanaged Beep puts out a square wave. I create a sine wave and drive the signal to the rails when zero is crossed to generate a square wave. It doesn't sound the same at all frequencies. Some work needs to be done to create the same sound but I like the sine wave myself.
A necessary tool for any development work with pc sound is Cool Edit 2000 from Syntrillium (and for this plug, I fully expect my free version of Cool Edit Pro to be in the mail). An improvement in the sine wave could be made by stopping the sound when it is at zero or by using a low pass filter. The little bit at the end, as shown below, can be heard as a pop.
Using the code
The only method in the
MessageBeep() class is
Beep() is called as:
void Beep() or void
Beep(double frequency, System.Int32 duration) other methods set frequency, volume, tone duration and turn the square wave mode off and on.
MessageBeep() function returns immediately and the
Beep() function returns when the tone is complete as do the Win32 functions.
The two classes are in the Win32Emu namespace (Emu for emulation).
I'm not sure if I've answered the question "Where is the MessageBeep?" to everyone's satisfaction but it's a start.
History<!------------------------------- That's it! --------------------------->
Version 220.127.116.11/030128 -- Initial release.
Ooops. I may have left the crypto key on in the build. Turn it off in your assembly. I'll update later. The frequency for C# in the key of C is 277Hz. I'll put that as the default.