WaveIn problems

K

Keith

We are having trouble with WaveIn. We are using it with GSM 6.1 compression
mode, and for short recordings (<1 minute), everything is fine. For long
recordings, we start to have trouble receiving the callback windows messages
when a block is done. It appears that WaveIn is lost. This behavior varies a
lot depending on the size of the app, length of blocks, device being used,
phase of moon. We have tried all sorts of variations in allocating memory
(size, user heap, freeing immediately, etc.), but the problem just seems to
move. We are checking to make sure we get good allocations, and the devices
have plenty free. Anyone ever see this kind of thing? By the way, tried a
polling arrangement and the dwBytesRecorded isn't getting updated when
WaveIn becomes lost. Thanks for any help.
Keith Welch
Mooseworks Software
 
A

Alex Yakhnin, MVP

I beleive Waveform Audio interface on Windows CE supports PCM codecs only.
 
K

Keith

Actually, it's working for short records, and the waveforms are definitely
compressed. I am dealing with PocketPCs only. It's the longer records where
it gets lost. Thanks,
Keith
 
A

Alex Feinman [MVP]

The ACM seems to be there alright. And the gsm610 codec is found in the
codec enumeration, so techncally there is nothing to prevent one from using
it. In the reality it might prove to be a little tricky - which it did.
I wonder if I could see a code snippet
 
K

Keith

Hi Alex,
There is a lot of code in this one, much of which is proprietary. If you let
me know what area of the code you had in mind (preload, adding buffers,
messaging, etc), perhaps I could send a piece to you privately. The control
has been based on the MSDN article
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetcomp/html/WaveInOut.asp. I
am not having trouble opening the wave in, or starting it. I am using a
message window to catch block done messages as in the sample. The problem is
that WaveIn stops sending the messages. It is not consistent when that
occurs, and I have verified that it is getting buffers added properly. This
seems to occur more often when there is other activity on the device
(messing with a calculator or a scrollbar), and when in an application that
uses more memory. Is it possible that GSM is so resource intensive that it
can't be used for long recordings with CF overhead? Thanks again,
Keith Welch
 
U

Urban Kaegi

Hi

I experience exactly the same problem with recording audio as Keith
does. Short recordings work fine, but longer recordings are critical.
Mostly the recording stops without any error message.
My program is also written in C# and it uses P/Invoke to make use of
the Win API functions (waveInOpen, ...). The audio recording is also
based on the MSDN article Keith mentioned. The program uses a
MessageWindow to catch and process the callbacks (i.e. block full)
from the wave-in device. However it doesn't work with compressed
audio, but 'normal' PCM Wave recording at 8000 Hz, 8 bit, mono.
The application is executed on a Qtek2020 Pocket PC with XScale PXA
263.

I'm using double buffering to have a continuously input stream of
audio data. When a buffer is full, it's data is fetched and the buffer
given back to the audio device with waveInAddBuffer. The fetched data
is given to a seperate Thread. It works fine, but after a while it
seems there are no callbacks anymore from the wave in device.

I tried many ways to get rid of this problem:

- Logging
- Updating the .NET Compact Framework to SP1
- Updating the .NET Compact Framework to SP2
- Make a separate Thread to process audio data
- Trying other buffer sizes and other numbers of buffers.
For example: 2 x 16000 byte (double buffering), 128 x 256 byte
(circular buffer), ...

As far I could evaluate, it's working better with tendential big
buffer size and using double buffering. But despite all my efforts I
could not solve this tricky problem.

Could this be a bug in the .NET CF? Or is it the garbage collector
creating inteferences?

I would be deeply grateful for any help concerning this topic.

I attached two code snippets. First the MessageWindow, second the
Method which is called by the MessageWindow after a block is done by
the audio recording.

Best regards, Urban


/// <summary>
/// Defines the MessageWindow used to receive messages from the audio
/// system.
/// </summary>
protected class SoundMessageWindow : MessageWindow
{
public const int MM_WIM_OPEN = 0x3BE;
public const int MM_WIM_CLOSE = 0x3BF;
public const int MM_WIM_DATA = 0x3C0;

// Instance of a recording interface
protected WaveIn m_wi = null;

public SoundMessageWindow(WaveIn wi)
{
m_wi = wi;
}

protected override void WndProc(ref Message msg)
{
switch(msg.Msg)
{
// When this message is encountered, a block is
// done recording, so notify the WaveIn instance.
case MM_WIM_DATA:
{
if (m_wi != null)
m_wi.BlockDone();
}
break;
}
base.WndProc(ref msg);
}
}



/// <summary>
/// Called when the Windows message specifying that a block has
finished
/// recording is encountered. It is critical that the application
stay
/// one buffer ahead because at the point this function is called, the
system
/// has already started writing to the next buffer.
/// </summary>
public void BlockDone()
{
// count the recorded blocks
this.m_countBlocks++;

// buffer for recorded wave data
byte[] data = null;

if (m_whdr != null)
{
// Get data from Wave Buffer
Wave.MMSYSERR result = m_whdr[m_curBlock].GetBuffer(out data,
this.m_bufferSize);

// Simulate a circular buffer. If highest block is full start over
with lowest block.
// If m_numBlocks = 2, this results in alternating between 2
blocks.
waveInAddBuffer(this.m_hwi, this.m_whdr[m_curBlock],
(uint)Marshal.SizeOf(m_whdr[m_curBlock]));

// indicate next block for writing
if (m_curBlock < (m_numBlocks - 1))
{
m_curBlock++;
}
else {
m_curBlock = 0;
}

// check if recorded data has been fetched successfully
if (result != Wave.MMSYSERR.NOERROR) //no
{
log.write("ERROR: Could not fetch buffer data
(WaveFile.BlockDone). " + result.ToString());
}
else //yes
{
// Creeate a thread to process fetched data using ThreadPool
class
ThreadPool.QueueUserWorkItem(wcb, (object) data);
}
}
}
 
A

Alex Feinman [MVP]

Onbe thing that comes to mind - try changing the code so that it does not
create/destroy a lot of objects. Otherwise eventually you are bound to
"blank out" while GC performs collection, which can take enough time to
cause trouble.

--
Alex Feinman
---
Visit http://www.opennetcf.org
Urban Kaegi said:
Hi

I experience exactly the same problem with recording audio as Keith
does. Short recordings work fine, but longer recordings are critical.
Mostly the recording stops without any error message.
My program is also written in C# and it uses P/Invoke to make use of
the Win API functions (waveInOpen, ...). The audio recording is also
based on the MSDN article Keith mentioned. The program uses a
MessageWindow to catch and process the callbacks (i.e. block full)
from the wave-in device. However it doesn't work with compressed
audio, but 'normal' PCM Wave recording at 8000 Hz, 8 bit, mono.
The application is executed on a Qtek2020 Pocket PC with XScale PXA
263.

I'm using double buffering to have a continuously input stream of
audio data. When a buffer is full, it's data is fetched and the buffer
given back to the audio device with waveInAddBuffer. The fetched data
is given to a seperate Thread. It works fine, but after a while it
seems there are no callbacks anymore from the wave in device.

I tried many ways to get rid of this problem:

- Logging
- Updating the .NET Compact Framework to SP1
- Updating the .NET Compact Framework to SP2
- Make a separate Thread to process audio data
- Trying other buffer sizes and other numbers of buffers.
For example: 2 x 16000 byte (double buffering), 128 x 256 byte
(circular buffer), ...

As far I could evaluate, it's working better with tendential big
buffer size and using double buffering. But despite all my efforts I
could not solve this tricky problem.

Could this be a bug in the .NET CF? Or is it the garbage collector
creating inteferences?

I would be deeply grateful for any help concerning this topic.

I attached two code snippets. First the MessageWindow, second the
Method which is called by the MessageWindow after a block is done by
the audio recording.

Best regards, Urban


/// <summary>
/// Defines the MessageWindow used to receive messages from the audio
/// system.
/// </summary>
protected class SoundMessageWindow : MessageWindow
{
public const int MM_WIM_OPEN = 0x3BE;
public const int MM_WIM_CLOSE = 0x3BF;
public const int MM_WIM_DATA = 0x3C0;

// Instance of a recording interface
protected WaveIn m_wi = null;

public SoundMessageWindow(WaveIn wi)
{
m_wi = wi;
}

protected override void WndProc(ref Message msg)
{
switch(msg.Msg)
{
// When this message is encountered, a block is
// done recording, so notify the WaveIn instance.
case MM_WIM_DATA:
{
if (m_wi != null)
m_wi.BlockDone();
}
break;
}
base.WndProc(ref msg);
}
}



/// <summary>
/// Called when the Windows message specifying that a block has
finished
/// recording is encountered. It is critical that the application
stay
/// one buffer ahead because at the point this function is called, the
system
/// has already started writing to the next buffer.
/// </summary>
public void BlockDone()
{
// count the recorded blocks
this.m_countBlocks++;

// buffer for recorded wave data
byte[] data = null;

if (m_whdr != null)
{
// Get data from Wave Buffer
Wave.MMSYSERR result = m_whdr[m_curBlock].GetBuffer(out data,
this.m_bufferSize);

// Simulate a circular buffer. If highest block is full start over
with lowest block.
// If m_numBlocks = 2, this results in alternating between 2
blocks.
waveInAddBuffer(this.m_hwi, this.m_whdr[m_curBlock],
(uint)Marshal.SizeOf(m_whdr[m_curBlock]));

// indicate next block for writing
if (m_curBlock < (m_numBlocks - 1))
{
m_curBlock++;
}
else {
m_curBlock = 0;
}

// check if recorded data has been fetched successfully
if (result != Wave.MMSYSERR.NOERROR) //no
{
log.write("ERROR: Could not fetch buffer data
(WaveFile.BlockDone). " + result.ToString());
}
else //yes
{
// Creeate a thread to process fetched data using ThreadPool
class
ThreadPool.QueueUserWorkItem(wcb, (object) data);
}
}
}

"Keith" <keith@no_spam_mooseworkssoftware.com> wrote in message
Hi Alex,
There is a lot of code in this one, much of which is proprietary. If you let
me know what area of the code you had in mind (preload, adding buffers,
messaging, etc), perhaps I could send a piece to you privately. The control
has been based on the MSDN article
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetcomp/html/WaveInOut.asp. I
am not having trouble opening the wave in, or starting it. I am using a
message window to catch block done messages as in the sample. The problem is
that WaveIn stops sending the messages. It is not consistent when that
occurs, and I have verified that it is getting buffers added properly. This
seems to occur more often when there is other activity on the device
(messing with a calculator or a scrollbar), and when in an application that
uses more memory. Is it possible that GSM is so resource intensive that it
can't be used for long recordings with CF overhead? Thanks again,
Keith Welch

For
long windows
messages behavior
varies
a being
used, the
devices
 
K

Keith Welch

That's an interesting thought Alex. We have tried combinations of freeing
headers immediately when the buffer is done or waiting until the end of the
recording. Our experience was that freeing the memory seemed to be of some
help. One interesting thing we are currently looking at is that we write the
data to file as the wavein buffers are done. We have found that this
correlates to wavein eventually failing. If we wait until the end of the
recording to write to file, it appears to be more reliable. This makes me
wonder about conflicts in IO? It is also far worse in 2002 than 2003. Any
thoughts? Thanks,
Keith Welch
Mooseworks Software

Alex Feinman said:
Onbe thing that comes to mind - try changing the code so that it does not
create/destroy a lot of objects. Otherwise eventually you are bound to
"blank out" while GC performs collection, which can take enough time to
cause trouble.

--
Alex Feinman
---
Visit http://www.opennetcf.org
Urban Kaegi said:
Hi

I experience exactly the same problem with recording audio as Keith
does. Short recordings work fine, but longer recordings are critical.
Mostly the recording stops without any error message.
My program is also written in C# and it uses P/Invoke to make use of
the Win API functions (waveInOpen, ...). The audio recording is also
based on the MSDN article Keith mentioned. The program uses a
MessageWindow to catch and process the callbacks (i.e. block full)
from the wave-in device. However it doesn't work with compressed
audio, but 'normal' PCM Wave recording at 8000 Hz, 8 bit, mono.
The application is executed on a Qtek2020 Pocket PC with XScale PXA
263.

I'm using double buffering to have a continuously input stream of
audio data. When a buffer is full, it's data is fetched and the buffer
given back to the audio device with waveInAddBuffer. The fetched data
is given to a seperate Thread. It works fine, but after a while it
seems there are no callbacks anymore from the wave in device.

I tried many ways to get rid of this problem:

- Logging
- Updating the .NET Compact Framework to SP1
- Updating the .NET Compact Framework to SP2
- Make a separate Thread to process audio data
- Trying other buffer sizes and other numbers of buffers.
For example: 2 x 16000 byte (double buffering), 128 x 256 byte
(circular buffer), ...

As far I could evaluate, it's working better with tendential big
buffer size and using double buffering. But despite all my efforts I
could not solve this tricky problem.

Could this be a bug in the .NET CF? Or is it the garbage collector
creating inteferences?

I would be deeply grateful for any help concerning this topic.

I attached two code snippets. First the MessageWindow, second the
Method which is called by the MessageWindow after a block is done by
the audio recording.

Best regards, Urban


/// <summary>
/// Defines the MessageWindow used to receive messages from the audio
/// system.
/// </summary>
protected class SoundMessageWindow : MessageWindow
{
public const int MM_WIM_OPEN = 0x3BE;
public const int MM_WIM_CLOSE = 0x3BF;
public const int MM_WIM_DATA = 0x3C0;

// Instance of a recording interface
protected WaveIn m_wi = null;

public SoundMessageWindow(WaveIn wi)
{
m_wi = wi;
}

protected override void WndProc(ref Message msg)
{
switch(msg.Msg)
{
// When this message is encountered, a block is
// done recording, so notify the WaveIn instance.
case MM_WIM_DATA:
{
if (m_wi != null)
m_wi.BlockDone();
}
break;
}
base.WndProc(ref msg);
}
}



/// <summary>
/// Called when the Windows message specifying that a block has
finished
/// recording is encountered. It is critical that the application
stay
/// one buffer ahead because at the point this function is called, the
system
/// has already started writing to the next buffer.
/// </summary>
public void BlockDone()
{
// count the recorded blocks
this.m_countBlocks++;

// buffer for recorded wave data
byte[] data = null;

if (m_whdr != null)
{
// Get data from Wave Buffer
Wave.MMSYSERR result = m_whdr[m_curBlock].GetBuffer(out data,
this.m_bufferSize);

// Simulate a circular buffer. If highest block is full start over
with lowest block.
// If m_numBlocks = 2, this results in alternating between 2
blocks.
waveInAddBuffer(this.m_hwi, this.m_whdr[m_curBlock],
(uint)Marshal.SizeOf(m_whdr[m_curBlock]));

// indicate next block for writing
if (m_curBlock < (m_numBlocks - 1))
{
m_curBlock++;
}
else {
m_curBlock = 0;
}

// check if recorded data has been fetched successfully
if (result != Wave.MMSYSERR.NOERROR) //no
{
log.write("ERROR: Could not fetch buffer data
(WaveFile.BlockDone). " + result.ToString());
}
else //yes
{
// Creeate a thread to process fetched data using ThreadPool
class
ThreadPool.QueueUserWorkItem(wcb, (object) data);
}
}
}

"Keith" <keith@no_spam_mooseworkssoftware.com> wrote in message
Hi Alex,
There is a lot of code in this one, much of which is proprietary. If
you
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetcomp/html/WaveInOut.asp. I
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top