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