tags:

views:

1044

answers:

3

Hi all,

I have a device that a user interacts with alongside of a C# WPF program. This program must beep when the user presses a button on the device, for either a specified amount of time or for as long as the user presses the button, whichever is shorter. The only speaker available to produce the beep/tone is the computer BIOS speaker; we cannot assume that other speakers are around (and it's actually safe to assume that there won't be any other speakers).

How can I produce a continuous tone for the necessary duration?

What I have so far produces lots of beeps, but not a continuous tone.

First, a thread is started:

    private void UserControl_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) {
        if(this.Visibility == Visibility.Visible){
            mBeepThread = new Thread(new ThreadStart(ProduceTone));
            mBeepThread.Name = "Beep Thread";
            mBeepThread.Start();
        }
    }

The thread itself:

    bool mMakeTone = false;
    private void ProduceTone(){
        while(this.Visibility == Visibility.Visible){
            if(mMakeTone ){
                Console.Beep();
            }
            else{
                Thread.Sleep(10);
            }
        }
    }

And then the mMakeTone boolean is flipped to true during the duration of the button press, up to a time specified by the device itself.

I suspect that it's just a quick change to the Console.Beep() line above, but I'm not sure what it would be.

A: 

Unfortunately I don't think what you are wanting is possible without some low level hardware access.

The Console.Beep() method has two overloads, the one you are using with no parameters just sounds a short beep. The second overload takes two integers, the first for the frequency of the tone and the second for the number of milliseconds to sound the tone for.

Even if you bypass .net and go to the Windows API calls you still have basically the same Beep function (with the frequency and length parameters).

So the best you can hope for, without some much lower level coding is to do something like sound a 500 millisecond tone and end up with a series of interrupted half second beeps strung together.

bool mMakeTone = false;
private void ProduceTone(){
    while(this.Visibility == Visibility.Visible){
        if(mMakeTone ){
            Console.Beep(800, 500); // 800Hz tone for half a second
        }
        else{
            Thread.Sleep(10);
        }
    }
}

And be prepared to drive yourself mad during testing due to the lack of volume control on most PC beepers nowadays...

andynormancx
+7  A: 

There is an overload for Console.Beep that takes a frequency and duration, which is useful if you want to produce a sound for a specified duration. The ability to turn on a sound and then later turn it off using the BIOS speaker is not directly supported by the Console.Beep method, or any other API that I am aware of, without perhaps installing a fake sound card driver.

A little experimentation has discovered the following:

  • Console.Beep() is synchronous and does not return until the sound is finished.
  • Calling Console.Beep() from a separate thread interrupts whatever is currently in progress.

So... You should be able to accomplish your task by creating a new background thread to call Console.Beep() with the frequency of your choosing and a very long duration. To later terminate the sound, you may just call Console.Beep() on your main thread with any frequency and an extremely, non-zero duration (e.g. 1) to terminate the sound playing from the background thread. You should not make the sound playing on the background thread any longer that a reasonable duration because the thread will live until the sound would have ordinarily stopped playing and you don't want a lot of background threads piling up on you.

private void button1_Click(object sender, EventArgs e)
{
    // Starts beep on background thread
    Thread beepThread = new Thread(new ThreadStart(PlayBeep));
    beepThread.IsBackground = true;
    beepThread.Start();
}

private void button2_Click(object sender, EventArgs e)
{
    // Terminates beep from main thread
    Console.Beep(1000, 1);
}

private void PlayBeep()
{
    // Play 1000 Hz for 5 seconds
    Console.Beep(1000, 5000);
}
Michael McCloskey
On first inspection that looks like a much better solution to mine. However in my testing, supplying longer console beeps from .net appears to be able to cause serious problems. I have had my two development machines both bluescreen on me when experimenting with 5 second and longer beeps. I can't remember the last time any of my machines blue screened before this...
andynormancx
In that case, the alternatives seem to be installing a fake sound card driver or looping beeps in the background thread using a duration less than that which causes BSOD's for you, with a mechanism to fall out of the loop.
Michael McCloskey
No blue screen for me with longer durations. I did notice though, that if I use a duration of, say, 10 seconds, but interrupt before that, starting the beep again does not last for the full 10 seconds - almost as if it is flushing out the remainder of the initial call.
Steve Beedie
The blue screens didn't happen every time for me, but enough to encourage me to stop playing with them :(
andynormancx
I'm not getting the BSOD's, and more than 4 seconds appears to be outside the realistic expectations of this particular device.
mmr
+1  A: 

If you have a wave file of a continuous sound, you could use a SoundPlayer:

SoundPlayer player = new SoundPlayer();
player.SoundLocation = @"C:\Program Files\MyApp\sound.wav";
player.PlayLooping();
...
// Later, when you know it's time to stop the sound:
player.Stop();

I don't know if you would be able to hear the start and stop.

If that won't work for you, you probably need to go down a level to Win32 calls. You might want to look at Multimedia Audio or DirectSound.

Paul Williams
Also note that Windows will happily play sounds through the system speaker if no other audio device is present.
Paul Williams
Maybe I missed something, but to whoever downvoted me: I was thinking of, say, a very short 50 ms tone that is continuous, i.e. can be looped without any detection that it has changed. The end of the waveform would match the beginning-- think of a perfect sine wave across a single wavelength. Just loop it until you need to stop it. Seems like that would accomplish the same thing.
Paul Williams