如何知道 SoundPlayer 何时完成播放声音
本文关键字:播放声音 何时完 SoundPlayer 何知道 | 更新日期: 2023-09-27 18:33:50
我使用以下代码在内存中动态创建频率音并异步播放音调:
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 类上似乎没有我可以订阅的 Done 事件。
你知道,如果你只想播放一个音调,那么有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 操作。