audio - waveInPrepareHeader() problem
-
I am writing a "C" application (not C++, not MFC) running on Win98SE which captures audio from line-in into a wrap-around ring of 40mSec buffers. When the user stops capture, the last audio which was captured is combined with PAL video frames (25Hz) from a frame grabber into an AVI file. The capture works fine with a small numbers of buffers (125 buffers for 5 seconds), but when I try 1 minute (1500 buffers), the program gets to about buffer 300 during the waveInPrepareHeader() stage, which then returns an error value of 7 (MMSYSERR_NOMEM, explained in the mmsystem.h file as "memory allocation error".) About this time, Win98 becomes unstable! Once, Windows gave the error "System is dangerously low on resources . . .". I ran resource meter, and when MMSYSERR_NOMEM is reported by my program, resource meter reports 92% system resources, 92% user resources, 95% GDI resources. System monitor reports 550 Meg of free physical memory. Does anyone know what waveInPrepareHeader() is actually doing, what the system is running out of, and how I can fix it? I can work around the problem by grabbing the audio in half-second chunks instead, but that complicates the code. The test program runs fine on Windows XP, so I could also try to migrate to XP, but I want to understand what is happening! Any ideas? Here is a console application which demonstrates the problem (on my PC, it fails at header 301 or so)
// AudioTest.c : console application. #include "stdafx.h" #include <windows.h> #include <vfw.h> #include <mmsystem.h> #define DEF_BlocksPerSecond 25 #define DEF_NumAudioBlocks 1500 // grabbing at 25Hz to match PAL video frames, 1500 frames = 60 seconds int main(int argc, char* argv[]) { unsigned int uiNumWaveInDevices; unsigned int uiCounter; unsigned int uiReturn; char szTempString[100]; WAVEINCAPS WaveInCaps; BOOL bSoundModeValid; HWAVEIN hWaveInDevice; WAVEFORMATEX WaveFormat; unsigned int uiAudioBufferNumBytes; WAVEHDR WaveHeaders[DEF_NumAudioBlocks]; // going to capture at 11.025Hz, Mono, 16 bps uiAudioBufferNumBytes = 11025 * 2 / DEF_BlocksPerSecond; uiNumWaveInDevices = waveInGetNumDevs(); for (uiCounter=0; uiCounter < uiNumWaveInDevices; uiCounter++) { // Check device can do mono, 11.025kHz, 16 bit/sample waveInGetDevCaps(uiCounter, &WaveInCaps, sizeof(WaveInCaps)); bSoundModeValid = WaveInCaps.dwFormats & WAVE_FORMAT_1M16; if (bSoundModeValid) break;
-
I am writing a "C" application (not C++, not MFC) running on Win98SE which captures audio from line-in into a wrap-around ring of 40mSec buffers. When the user stops capture, the last audio which was captured is combined with PAL video frames (25Hz) from a frame grabber into an AVI file. The capture works fine with a small numbers of buffers (125 buffers for 5 seconds), but when I try 1 minute (1500 buffers), the program gets to about buffer 300 during the waveInPrepareHeader() stage, which then returns an error value of 7 (MMSYSERR_NOMEM, explained in the mmsystem.h file as "memory allocation error".) About this time, Win98 becomes unstable! Once, Windows gave the error "System is dangerously low on resources . . .". I ran resource meter, and when MMSYSERR_NOMEM is reported by my program, resource meter reports 92% system resources, 92% user resources, 95% GDI resources. System monitor reports 550 Meg of free physical memory. Does anyone know what waveInPrepareHeader() is actually doing, what the system is running out of, and how I can fix it? I can work around the problem by grabbing the audio in half-second chunks instead, but that complicates the code. The test program runs fine on Windows XP, so I could also try to migrate to XP, but I want to understand what is happening! Any ideas? Here is a console application which demonstrates the problem (on my PC, it fails at header 301 or so)
// AudioTest.c : console application. #include "stdafx.h" #include <windows.h> #include <vfw.h> #include <mmsystem.h> #define DEF_BlocksPerSecond 25 #define DEF_NumAudioBlocks 1500 // grabbing at 25Hz to match PAL video frames, 1500 frames = 60 seconds int main(int argc, char* argv[]) { unsigned int uiNumWaveInDevices; unsigned int uiCounter; unsigned int uiReturn; char szTempString[100]; WAVEINCAPS WaveInCaps; BOOL bSoundModeValid; HWAVEIN hWaveInDevice; WAVEFORMATEX WaveFormat; unsigned int uiAudioBufferNumBytes; WAVEHDR WaveHeaders[DEF_NumAudioBlocks]; // going to capture at 11.025Hz, Mono, 16 bps uiAudioBufferNumBytes = 11025 * 2 / DEF_BlocksPerSecond; uiNumWaveInDevices = waveInGetNumDevs(); for (uiCounter=0; uiCounter < uiNumWaveInDevices; uiCounter++) { // Check device can do mono, 11.025kHz, 16 bit/sample waveInGetDevCaps(uiCounter, &WaveInCaps, sizeof(WaveInCaps)); bSoundModeValid = WaveInCaps.dwFormats & WAVE_FORMAT_1M16; if (bSoundModeValid) break;
You should only have one or two buffers prepared at any given time. Then when you respond to the callbacks, you give the MM system the next buffer, calling waveInUnprepareHeader on the buffer it just finished using. So by double or triple buffering the data, you are allowing the multimedia to fill one while you are preparing another, and you unprepare the one it just finished filling. I think you are overflowing some internal buffer that can only handle up to 300 prepared headers at a time. something like this: prepare 3 headers and feed the MM system the first buffer when it calls back and says it has filled it, give it second, and COPY the data from your first buffer to your own local storage when it has filled second buffer, give it your thrid, etc. repeat cycle until your waveform sampling is complete I found this all works best from a thread that can handle the events from the mm system instead of doing it frojw ith the same thread, then I can copy the data out of a buffer and 'reprepare' that buffer while the MM system is busy sampling, instead of being blocked waiting for a sample buffer to fill.
-
You should only have one or two buffers prepared at any given time. Then when you respond to the callbacks, you give the MM system the next buffer, calling waveInUnprepareHeader on the buffer it just finished using. So by double or triple buffering the data, you are allowing the multimedia to fill one while you are preparing another, and you unprepare the one it just finished filling. I think you are overflowing some internal buffer that can only handle up to 300 prepared headers at a time. something like this: prepare 3 headers and feed the MM system the first buffer when it calls back and says it has filled it, give it second, and COPY the data from your first buffer to your own local storage when it has filled second buffer, give it your thrid, etc. repeat cycle until your waveform sampling is complete I found this all works best from a thread that can handle the events from the mm system instead of doing it frojw ith the same thread, then I can copy the data out of a buffer and 'reprepare' that buffer while the MM system is busy sampling, instead of being blocked waiting for a sample buffer to fill.
(At home and can't remember my password so not logged in!) Thanks - your suggestion sounds good but it means I have to handle the buffers in real time, and I'm lazy! I guess it's either do it your way or change to Windows XP, which I was considering anyway. By the way, I tried my application on 2 other Windows 98SE PCs, and their behaviour was not the same as the original PC. My original PC gave me a message when preparing buffer number 301, then gave the error code 7 - NOMEM - dialog box. The other two would prepare 1000 buffers, but when I tried 10000 buffers, they terminated with no message. I guess it's just poor Win98 audio drivers! NormanS
-
I am writing a "C" application (not C++, not MFC) running on Win98SE which captures audio from line-in into a wrap-around ring of 40mSec buffers. When the user stops capture, the last audio which was captured is combined with PAL video frames (25Hz) from a frame grabber into an AVI file. The capture works fine with a small numbers of buffers (125 buffers for 5 seconds), but when I try 1 minute (1500 buffers), the program gets to about buffer 300 during the waveInPrepareHeader() stage, which then returns an error value of 7 (MMSYSERR_NOMEM, explained in the mmsystem.h file as "memory allocation error".) About this time, Win98 becomes unstable! Once, Windows gave the error "System is dangerously low on resources . . .". I ran resource meter, and when MMSYSERR_NOMEM is reported by my program, resource meter reports 92% system resources, 92% user resources, 95% GDI resources. System monitor reports 550 Meg of free physical memory. Does anyone know what waveInPrepareHeader() is actually doing, what the system is running out of, and how I can fix it? I can work around the problem by grabbing the audio in half-second chunks instead, but that complicates the code. The test program runs fine on Windows XP, so I could also try to migrate to XP, but I want to understand what is happening! Any ideas? Here is a console application which demonstrates the problem (on my PC, it fails at header 301 or so)
// AudioTest.c : console application. #include "stdafx.h" #include <windows.h> #include <vfw.h> #include <mmsystem.h> #define DEF_BlocksPerSecond 25 #define DEF_NumAudioBlocks 1500 // grabbing at 25Hz to match PAL video frames, 1500 frames = 60 seconds int main(int argc, char* argv[]) { unsigned int uiNumWaveInDevices; unsigned int uiCounter; unsigned int uiReturn; char szTempString[100]; WAVEINCAPS WaveInCaps; BOOL bSoundModeValid; HWAVEIN hWaveInDevice; WAVEFORMATEX WaveFormat; unsigned int uiAudioBufferNumBytes; WAVEHDR WaveHeaders[DEF_NumAudioBlocks]; // going to capture at 11.025Hz, Mono, 16 bps uiAudioBufferNumBytes = 11025 * 2 / DEF_BlocksPerSecond; uiNumWaveInDevices = waveInGetNumDevs(); for (uiCounter=0; uiCounter < uiNumWaveInDevices; uiCounter++) { // Check device can do mono, 11.025kHz, 16 bit/sample waveInGetDevCaps(uiCounter, &WaveInCaps, sizeof(WaveInCaps)); bSoundModeValid = WaveInCaps.dwFormats & WAVE_FORMAT_1M16; if (bSoundModeValid) break;
Hi Oliv - thanks for your comments. The application I posted was just one I put together to investigate the problem, not my "real" project (which is about 9000 lines of code.) Since I found the problem in audio header preparation in my real project, that is where I stopped in the test application. It's a console application because I did not want anything to distract me from the real problem with the audio. The actual project is an SDK-level Windows program, mainly because (1) I based my project on a sample project which was written in SDK-level C, and (2) I learned programming long ago, and I haven't bothered to learn this "modern" C++ / MFC stuff! (Also, I program in other environments using ANSI C, so I don't want to pick up C++ habits.) The GlobalAlloc is not the problem. To test this, I changed the program slightly, so it allocates memory in one loop, and once all the data blocks are allocated, it starts to prepare the headers. The error occurs at about the same header (number 301 on my main development PC.) I have allocated 10000 blocks with no problems. Unfortunately, I do need to capture 1 minute of audio. My main application is a video capture and processing program, which displays processed video in real-time. The user can stop at any stage, and step back and forward through the LAST minute of video, which I keep in memory in a ring of video buffers. He might have been playing the video for an hour, but I just keep the last minute of video frames. Once he stops, he can also save the 1 minute of video as an AVI file. That bit works, but I thought it would be a good idea to add audio to the AVI file, which is when I hit the problems. I think the solution will probably be to use a smaller number of audio buffers (5 or 10), and transfer these into my own array of buffers whenever they are full.
-
(At home and can't remember my password so not logged in!) Thanks - your suggestion sounds good but it means I have to handle the buffers in real time, and I'm lazy! I guess it's either do it your way or change to Windows XP, which I was considering anyway. By the way, I tried my application on 2 other Windows 98SE PCs, and their behaviour was not the same as the original PC. My original PC gave me a message when preparing buffer number 301, then gave the error code 7 - NOMEM - dialog box. The other two would prepare 1000 buffers, but when I tried 10000 buffers, they terminated with no message. I guess it's just poor Win98 audio drivers! NormanS
The event driven, ring buffering technique is the 'preferred' method. It worked for me back in 1993 on Windows 95 and Windows NT 4.0 machines (slow 66 MHz 80486 systems) with no problem. Yours should be fine if you adopt the same technique, given the speed of today's processors.