编程语言
首页 > 编程语言> > c# – 如何知道SoundPlayer播放声音的时间

c# – 如何知道SoundPlayer播放声音的时间

作者:互联网

我使用以下代码在内存中动态创建频率音调并异步播放音调:

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();
            }
        }
    }
}

代码工作正常.唯一的问题是,我如何知道音调何时停止播放? SoundPlayer类上似乎没有可以订阅的Completed事件.

解决方法:

你知道,如果你想要做的只是单音,那就是Console.Beep.但是,它不是在后台执行,但我在下面描述的技术对于Console.Beep可以正常工作,它会阻止你创建一个内存流只是为了播放音调.

在任何情况下,SoundPlayer都没有您想要的功能,但您可以模拟它.

首先,创建您的事件处理程序:

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

更改您的PlayTone方法,以便它采用回调函数参数:

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

然后,更改方法的结尾,使其调用PlaySync而不是Play,并在完成后调用doneCallback:

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

然后,在线程或任务中执行它.例如:

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

这个问题的主要问题是事件通知发生在后台线程上.如果需要影响UI,最好的办法就是让事件处理程序与UI线程同步.因此,在SoundPlayed方法中,您将调用Form.Invoke(对于WinForms)或Dispatcher.Invoke(WPF)来执行任何UI操作.

标签:c,asynchronous,soundplayer
来源: https://codeday.me/bug/20190612/1223896.html