问题
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;
}
}
}
}
回答1:
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 to null
, so your other checks will still pass through because the object is not null, only disposed. So I would do
waveOut.Dispose();
waveout = null;
to make sure that it is explictly set to null.
来源:https://stackoverflow.com/questions/710650/winmm-library-issues