How to know when SoundPlayer has finished playing a sound

你说的曾经没有我的故事 提交于 2019-12-01 05:17:10

问题


I am using the following code to dynamically create a frequency tone in memory and play the tone asynchronously:

public static void PlayTone(UInt16 frequency, int msDuration, UInt16 volume = 16383)
{
    using (var mStrm = new MemoryStream())
    {
        using (var writer = new BinaryWriter(mStrm))
        {
            const double tau = 2*Math.PI;
            const int formatChunkSize = 16;
            const int headerSize = 8;
            const short formatType = 1;
            const short tracks = 1;
            const int samplesPerSecond = 44100;
            const short bitsPerSample = 16;
            const short frameSize = (short) (tracks*((bitsPerSample + 7)/8));
            const int bytesPerSecond = samplesPerSecond*frameSize;
            const int waveSize = 4;
            var samples = (int) ((decimal) samplesPerSecond*msDuration/1000);
            int dataChunkSize = samples*frameSize;
            int fileSize = waveSize + headerSize + formatChunkSize + headerSize + dataChunkSize;

            writer.Write(0x46464952);
            writer.Write(fileSize);
            writer.Write(0x45564157);
            writer.Write(0x20746D66);
            writer.Write(formatChunkSize);
            writer.Write(formatType);
            writer.Write(tracks);
            writer.Write(samplesPerSecond);
            writer.Write(bytesPerSecond);
            writer.Write(frameSize);
            writer.Write(bitsPerSample);
            writer.Write(0x61746164);
            writer.Write(dataChunkSize);

            double theta = frequency*tau/samplesPerSecond;
            double amp = volume >> 2;
            for (int step = 0; step < samples; step++)
            {
                writer.Write((short) (amp*Math.Sin(theta*step)));
            }

            mStrm.Seek(0, SeekOrigin.Begin);
            using (var player = new System.Media.SoundPlayer(mStrm))
            {
                player.Play();
            }
        }
    }
}

The code is working fine. The only issue is, how do I know when the tone has stopped playing? There doesn't appear to be a Completed event on the SoundPlayer class that I can subscribe to.


回答1:


You know, if all you want to do is play a single tone, there is Console.Beep. Granted, it doesn't do it in the background, but the technique I describe below will work fine for Console.Beep, and it prevents you having to create a memory stream just to play a tone.

In any case, SoundPlayer doesn't have the functionality that you want, but you can simulate it.

First, create your event handler:

void SoundPlayed(object sender, EventArgs e)
{
    // do whatever here
}

Change your PlayTone method so that it takes a callback function parameter:

public static void PlayTone(UInt16 frequency, int msDuration, UInt16 volume = 16383, EventHandler doneCallback = null)

Then, change the end of the method so that it calls PlaySync rather than Play, and calls the doneCallback after it's done:

using (var player = new System.Media.SoundPlayer(mStrm))
{
    player.PlaySync();
}    
if (doneCallback != null)
{
    // the callback is executed on the thread.
    doneCallback(this, new EventArgs());
}

Then, execute it in a thread or a task. For example:

var t = Task.Factory.StartNew(() => {PlayTone(100, 1000, 16383, SoundPlayed));

The primary problem with this is that the event notification occurs on the background thread. Probably the best thing to do if you need to affect the UI is to have the event handler synchronize with the UI thread. So in the SoundPlayed method, you'd call Form.Invoke (for WinForms) or Dispatcher.Invoke (WPF) to execute any UI actions.



来源:https://stackoverflow.com/questions/27392396/how-to-know-when-soundplayer-has-finished-playing-a-sound

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!