WinMM 库问题
我编写了一个 WinMM 库包装库,它公开 WaveOut 和 WaveIn 类,以便录制和播放原始音频流。
一切都很好,但为了遵循操作系统关于如何处理已完成缓冲区的规范,我添加了一个线程来取消准备缓冲区并释放内存。 我还关闭了所有同步,以便这些类是可靠且线程安全的。
然而,似乎有一个罕见的问题,我向 WaveOut 设备添加缓冲区并且操作系统返回成功代码,但如果设备立即重置,操作系统不会将缓冲区标记为已完成并将其返回到应用。
看起来它正在失去缓冲区。 所以,问题是,我正在跟踪发送到设备的各个缓冲区的计数,并阻止将 Close() 函数加入到清理它们的线程中。
有什么想法或已知的错误吗?
PS:这似乎不会发生在 Vista 的四核处理器上,但会发生在我的 XP pro 的双核处理器上。
EDIT1:一旦我将其上传并在 CodePlex 上获得适当许可,我完全愿意公开完整的源代码,如果这对任何人有帮助的话。
EDIT2:将库发布到 CodePlex:http://winmm.codeplex.com/
以下是导致问题的原因:
public partial class MainView : Form
{
private WaveIn waveIn = new WaveIn(WaveIn.WaveInMapperDeviceId);
private WaveOut waveOut = new WaveOut(WaveOut.WaveOutMapperDeviceId);
public MainView()
{
InitializeComponent();
WaveFormat format = WaveFormat.Pcm44Khz16BitMono;
waveOut.Open(format);
waveIn.DataReady += new EventHandler<DataReadyEventArgs>(WaveIn_DataReady);
// Tweaking these values affects the internal buffering thread.
// Setting too small of a QueueSize with too small of a BufferSize
// will cause buffer underruns, which will sound like choppy audio.
waveIn.BufferQueueSize = 200;
waveIn.BufferSize = 64;
waveIn.Open(format);
waveIn.Start();
}
void WaveIn_DataReady(object sender, DataReadyEventArgs e)
{
if (waveOut != null)
{
lock (waveOut)
{
// We have to check for null after the lock,
// because waveOut may have been disposed
// inside another lock.
if (waveOut != null)
{
waveOut.Write(e.Data);
}
}
}
}
private void MainView_FormClosed(object sender, FormClosedEventArgs e)
{
if (waveIn != null)
{
lock (waveIn)
{
waveIn.Dispose();
waveIn = null;
}
}
if (waveOut != null)
{
lock (waveOut)
{
waveOut.Dispose();
waveOut = null;
}
}
}
}
I wrote a WinMM library wrapper library that exposes WaveOut and WaveIn classes for the purpose of recording and playing raw audio streams.
Everything works great, but in order to follow the operating system specs on how to handle the finished buffers, I added a thread that unprepares the buffers and frees the memory. I also got all of the synchronization down, so that the classes are solid and thread-safe.
However, there seems to be a rare issue where I add a buffer to the WaveOut device and the operating system returns a success code, but if the device is reset immediately afterwords, the OS does not mark the buffer as finished and return it to the application.
It seems that it is loosing the buffers. So, the problem is, I'm keeping track of the count of individual buffers sent to the device and blocking joining the Close() function to the thread that cleans them up.
Any ideas, or known bugs?
PS: This does NOT seem to happen on a Quad Core in Vista, but does happen on my Dual Core in XP pro.
EDIT1: I'm totally willing to expose the full source code, once I get it uploaded and propery licensed on codeplex, if that would help anybody.
EDIT2: Posted library to CodePlex: http://winmm.codeplex.com/
Here is an idea of what causes the issue:
public partial class MainView : Form
{
private WaveIn waveIn = new WaveIn(WaveIn.WaveInMapperDeviceId);
private WaveOut waveOut = new WaveOut(WaveOut.WaveOutMapperDeviceId);
public MainView()
{
InitializeComponent();
WaveFormat format = WaveFormat.Pcm44Khz16BitMono;
waveOut.Open(format);
waveIn.DataReady += new EventHandler<DataReadyEventArgs>(WaveIn_DataReady);
// Tweaking these values affects the internal buffering thread.
// Setting too small of a QueueSize with too small of a BufferSize
// will cause buffer underruns, which will sound like choppy audio.
waveIn.BufferQueueSize = 200;
waveIn.BufferSize = 64;
waveIn.Open(format);
waveIn.Start();
}
void WaveIn_DataReady(object sender, DataReadyEventArgs e)
{
if (waveOut != null)
{
lock (waveOut)
{
// We have to check for null after the lock,
// because waveOut may have been disposed
// inside another lock.
if (waveOut != null)
{
waveOut.Write(e.Data);
}
}
}
}
private void MainView_FormClosed(object sender, FormClosedEventArgs e)
{
if (waveIn != null)
{
lock (waveIn)
{
waveIn.Dispose();
waveIn = null;
}
}
if (waveOut != null)
{
lock (waveOut)
{
waveOut.Dispose();
waveOut = null;
}
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我首先创建一个单独的
对象
来锁定。 这应该会简化很多空检查逻辑(自从您在锁定之前和之后进行检查以来,大约减少了一半)。其次,
Dispose
不会将您的变量设置为null
,因此您的其他检查仍会通过,因为该对象不为 null,只是已处置。 所以我会确保它被明确设置为空。
I would first create a separate
object
on which to lock. That should simplify a lot of your null-checking logic (by about half since you check before and after the lock).Second,
Dispose
will not set your variable tonull
, so your other checks will still pass through because the object is not null, only disposed. So I would doto make sure that it is explictly set to null.