Mickey Marshall

Mickey Marshall

  • NA
  • 730
  • 201.9k

I want to create an audio recorder in managed code (c#) usin

Feb 4 2016 12:47 PM
 

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]));

if (0 != rv1)// didn't work

{

rv = mciGetErrorString(rv1, errmsg, (uint)errmsg.Capacity);

Errstring = errmsg.ToString();

Debug.Print("Failed to Add header " + errmsg);

return false;

}

}


//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);

if (0 != rv1)// didn't work

{

rv = mciGetErrorString(rv1, errmsg, (uint)errmsg.Capacity);

Errstring = errmsg.ToString();

Debug.Print("Failed to start " + errmsg );

return false;

}

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

Errstring = errmsg.ToString();

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;

bool rv1;

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)

{

rv1 = mciGetErrorString(rv, errmsg, (uint)errmsg.Capacity);

Debug.Print("waveInReset Err " + errmsg);

}

rv = waveInStop(hWaveIn);// call stop

if (0 != rv)

{

rv1 = mciGetErrorString(rv, errmsg, (uint)errmsg.Capacity);

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]));

if (0 != rv)

{

rv1 = mciGetErrorString(rv, errmsg, (uint)errmsg.Capacity);

Debug.Print("waveInUnprepareHeader[" + i.ToString() + "] Err " + errmsg);

}

}

rv = waveInClose(hWaveIn);// just close the function

if (0 != rv)

{

rv1 = mciGetErrorString(rv, errmsg, (uint)errmsg.Capacity);

Debug.Print("waveInClose Err " + errmsg);

}

rv=(uint)CloseHandle(hWaveIn);//finally just close the handle

if (rv == 0)

Debug.Print("Stopped");

else

{


}


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