I want to create an audio recorder in managed code (c#) using the wavein... functions in winmm.dll.
I have restarted three times and the last time has been reduced to the bare essentials; a form and a record class (derived from a base class that has the wavein... declarations.
It has start recording, HandleWaveIn(delegate) and stop recording. As you might guess, the trouble happened in HandleWaveIn.
It starts with clsRecord.StartRecording which look like this:
public bool StartRecording(uint InputDeviceIndex)
{
bool rv = true;
uint rv0, rv1;
//get the wavedelegate
BufferInProc = new WaveDelegate(HandleWaveIn);
hWaveIn = IntPtr.Zero;// create a wave in handle (global to this class)
IntPtr dwCallBack = IntPtr.Zero; // create a callback pointer
dwCallBack = Marshal.GetFunctionPointerForDelegate(BufferInProc);// get the pointer for that callback
header = new WAVEHDR[NUMBER_OF_HEADERS];// create an array of headers . . . NUMBER_OF_HEADERS=3
//create the filestream to write the recieved data to
fs = new FileStream(System.Windows.Forms.Application.StartupPath + "\\TheData.bin", FileMode.OpenOrCreate, FileAccess.ReadWrite);
bw = new BinaryWriter(fs);
//open the desired inputdevice
rv0 = waveInOpen(ref hWaveIn, InputDeviceIndex, ref wavFmt, dwCallBack, 0, (uint)WaveInOpenFlags.CALLBACK_FUNCTION);
//IntPtr user = (IntPtr)GCHandle.Alloc(this);
for (int i = 0; i < NUMBER_OF_HEADERS; i++)
// get a byte buffer of the desired size
byte[] HeaderData = new byte[size];// create an array big enough to hold the data
// get the pointer to that buffer for the data to be stored in
//HeaderDataHandle = GCHandle.Alloc(HeaderData, GCHandleType.Pinned);
IntPtr HeaderDataPointer = Marshal.AllocHGlobal(HeaderData.Length);
//define the header
header[i].lpData = HeaderDataPointer;// the address of HeaderData
header[i].dwBufferLength = size;//how big is the buffer = .2 * wavFmt.nAvgBytesPerSec
header[i].dwLoops = 0;//probably not necessary but not hurting I beleive that this is for playback headers
header[i].dwUser = new IntPtr(i);//the index of this header for debug purposes only this way I can see if headers are being dropped
// ask for the header/buffer to be prepared . . . not sure what this really does
rv1 = (uint)waveInPrepareHeader(hWaveIn, ref header[i], (uint)Marshal.SizeOf(header[i]));
if (0 != rv1)// didn't work
rv = mciGetErrorString(rv1, errmsg, (uint)errmsg.Capacity);
Errstring = errmsg.ToString();
Debug.Print("Failed to prepare header " + errmsg);
return false;
}
//add the header/buffer to the buffer queue
rv1 = (uint)waveInAddBuffer(hWaveIn, ref header[i], (uint)Marshal.SizeOf(header[i]));
Debug.Print("Failed to Add header " + errmsg);
//stopstruct is an attemp to make stop recording work
stopstruct.Stopped = false;// are all the headers out of the queue and we have actually stopped
stopstruct.NumberofStoppedBuffers = 0;// hopefully this reaches NUMBER_OF_HEADERS
stopstruct.Stopping = false;// set to true when we want to stop and the wavedelegate stops adding headers back in the queue
//start recording
rv1 = (uint)waveInStart(hWaveIn);
Debug.Print("Failed to start " + errmsg );
recording = true;
//remove me later this was an attemp to use threading
//object [] o= new object[] {InputDeviceIndex};
//Thread RecordThread = new Thread(new ParameterizedThreadStart(Record));
//RecordThread.SetApartmentState( ApartmentState.STA);
//RecordThread.Priority = ThreadPriority.Highest;
//RecordThread .Start(o);
return rv;
The wavedelegate is declared this way:
public delegate void WaveDelegate(IntPtr hdrvr, int uMsg, int dwUser, ref WAVEHDR wavhdr, int dwParam2);
private WaveDelegate BufferInProc;// which gets set in the StartRecording function
And it looks like this:
/// <summary>
///
/// </summary>
/// <param name="hdrvr"> Handle to the waveform-audio device associated with the callback function</param>
/// <param name="Msg">Waveform-audio input message. It can be one of the following messages. </param>
/// Value Meaning
///WIM_CLOSE Sent when the device is closed using the waveInClose function.
///WIM_DATA Sent when the device driver is finished with a data block sent using the waveInAddBuffer function.
///WIM_OPEN Sent when the device is opened using the waveInOpen function.
/// <param name="User">User instance data specified with waveInOpen.</param>
/// <param name="waveheader">Message parameter.</param>
/// <param name="Param2">Message parameter.</param>
//private void HandleWaveInCausesbadhandleErrors(IntPtr hdrvr, int Msg, int User, ref WAVEHDR waveheader, int Param2)// verbose
private void HandleWaveIn(IntPtr hdrvr, int Msg, int User, ref WAVEHDR waveheader, int Param2)// verbose
uint rv, i = 100, j = 200,k=0;
bool add = true;
//DateTime t1 , t2;
//t1 = DateTime.Now;
bool rv1;
if (Msg == MM_WIM_DATA)
if (waveheader.dwBytesRecorded != 0 && waveheader.dwBufferLength != 0 && waveheader.lpData != IntPtr.Zero)
i = (uint)waveheader.dwUser.ToInt32();
j = (uint)waveheader.lpData.ToInt32();
k = waveheader.dwBytesRecorded ;
if (header[i].lpData.ToInt32() != j)// bad data pointer?
Debug.Print("Bad Pointer");
add = false;
if (i > lastuser)
if (i - lastuser != 1)// we missed a header?
Debug.Print("Bad user this header " + i.ToString() + " last header " + lastuser.ToString());// I have not seen this
else
if (lastuser - i != 2)// we missed a header?
Debug.Print("Bad user this header a " + i.ToString() + " last header " + lastuser.ToString());// I have seen this
lastuser = i;
//adding any of these four lines wreaks havoc including dropped header and vshost32.exe has stopped working
//byte[] Temp = new byte[waveheader.dwBytesRecorded ];
//Marshal.Copy(waveheader.lpData, Temp, 0, (int)k);
//VU(Temp);// calls the VU meters and plotting functions on the main form
//Temp = null;
if (!stopstruct.Stopping)// not stopping so add the header back to the queue
if (add)
rv = waveInAddBuffer(hWaveIn, ref waveheader, (uint)Marshal.SizeOf(waveheader));// this header has been taken care of so add it back
if (0 != rv)// didn't work
rv1 = mciGetErrorString(rv, errmsg, (uint)errmsg.Capacity);
Debug.Print("Failed to Add header");
else// trying to stop so tally how many headers have not been added back to the queue
//number of headers that have not been added back into the queue
stopstruct.NumberofStoppedBuffers++;
//waveheader.dwFlags = 0;
//System.Windows.Forms.Application.DoEvents();
//t2 = DateTime.Now;
//Debug.Print("TimeSpan Ticks " + ((double)(t2.Ticks -t1.Ticks )/TimeSpan.TicksPerMillisecond) .ToString());
Stop looks like this :
private void Stop()//this works if no headers have bee dropped
uint rv;
StringBuilder errmsg = new StringBuilder(128);
if (recording)// are we actually recording
stopstruct.NumberofStoppedBuffers = 0;// set the stopstruct so we can stop gracefully
stopstruct.Stopping = true;
//loop until the are no more headers in the queue and the process of recording is finished
//this works very well if no buffers have been dropped
while (stopstruct.NumberofStoppedBuffers < NUMBER_OF_HEADERS)
Debug .Print("stopstruc.NumberofStoppedBuffers " + stopstruct .NumberofStoppedBuffers .ToString ());// show me what is happening
System.Windows .Forms .Application.DoEvents();
Debug.Print("stopstruc.NumberofStoppedBuffers " + stopstruct.NumberofStoppedBuffers.ToString());
rv = waveInReset(hWaveIn);//ok to reset
if (0 != rv)
Debug.Print("waveInReset Err " + errmsg);
rv = waveInStop(hWaveIn);// call stop
Debug.Print("waveInStop Err " + errmsg);
for (int i = 0; i < 3; i++)//ok to unprepare the headers if non have been dropped
rv = waveInUnprepareHeader(hWaveIn, ref header[i], (uint)Marshal.SizeOf(header[i]));
Debug.Print("waveInUnprepareHeader[" + i.ToString() + "] Err " + errmsg);
rv = waveInClose(hWaveIn);// just close the function
Debug.Print("waveInClose Err " + errmsg);
rv=(uint)CloseHandle(hWaveIn);//finally just close the handle
if (rv == 0)
Debug.Print("Stopped");
bw.Close();// close the data file so we can read it back later
fs.Close();
stopstruct.Stopped = true;//we are stopped
recording = false;//and not recording anymore
If I try to do anything with the data as it arrives in HandleWaveIn, all !@## breaks loose. I have been working on this for a couple of weeks and have found many things that I was doing wrong and learned even more.
But I just can not see what I am doing wrong.
Can anyone help please?
Thanks
MickeyMoose