Steven DeYoreo - Electronics Design, Testing & Construction Services

Using Your Soundcard As An Input Device

Home | ROBO PICS | PROFESSIONAL PROJECTS (or the stuff I get paid to do) | DirectShow Stuff | Groton's Robotics Team | Reviews Page

Want to capture in real time a signal thru your soundcard? The following article shows you how to program your computer to fill an array with samples from the soundcard.

Two structures and four steps to recording from your soundcard.
WAVEFORMATEX structure sets up the soundcard's sample rate. bits/sample, etc.
WAVEHDR structure holds the data buffer, length of buffer, bits recorded, etc.
waveInOpen, waveInPrepareHdr, waveInAddBuffer & waveInStart are the four required commands.

Using wave devices requires you to include to mmsystem.h header file and link to the winmm.lb library. The library and header file hold wave device actions and properties. To storing waveform data from the soundcard, you need to set it up, and assign buffers to it. The set up info is held in a WAVEFORMATEX structure. The buffer info is held in a WAVEHDR structure. Once these have been created, then you can begin issuing commands to the sound card.

Before you can start recording, you have to set up the sound card for a few things, such as mono or stereo,
sample rate, bits per sample, etc. The WAVEFORMATEX structure holds all these items. This structure needs to
be initialized before you can open the sound card. Here's a snippet that initializes WAVEFORMATEX:

// Initialize WAVEFORMATEX members:
m_WFXInSettings->wFormatTag=WAVE_FORMAT_PCM;
m_WFXInSettings->nChannels=1;
m_WFXInSettings->wBitsPerSample=16;
m_WFXInSettings->nSamplesPerSec=11025;
m_WFXInSettings->nBlockAlign=m_WFXInSettings->nChannels *(m_WFXInSettings->wBitsPerSample/8);
m_WFXInSettings->nAvgBytesPerSec=m_WFXInSettings->nSamplesPerSec*(m_WFXInSettings->nBlockAlign);
m_WFXInSettings->cbSize=0;

WAVEHDR structure defines a block of memory for waveform-audio input data. It holds a pointer to the data
array used, the length of the data array, bytes used, among other things. Create and initialize an object
of this structure after you do the WAVEFORMATEX structure. Here's a snippet that initializes WAVEHDR :

// allocate input data buffers:
m_pInputBuffer[i]=NULL;
if( (m_pInputBuffer[i]=new WAVEHDR[sizeof(WAVEHDR)] )==NULL)
{MessageBox("MEMORY ERROR","INPUTBUFFER",MB_OK);}

m_pInputBuffer->dwBufferLength=4096;
m_pInputBuffer->dwFlags=0;
m_pInputBuffer->dwUser=NULL;
m_pInputBuffer->dwBytesRecorded=NULL;
if ( ( m_pInputBuffer->lpData=newchar [ m_InBufferSize ] ) == NULL )
MessageBox("MEMORY ERROR","DATA BUFFERS",MB_OK);

After the structures are ceated, use the waveInOpen command to initialize the sound card. In this command
you also specify what callback function or event the sound card notifies when it's done. Among other
things, the waveInOpen command needs a device ID, (use WAVEMAPPER to use the default sound card), a pointer
to the WAVEFORMATEX object, a pointer to the call back function or event, a flag indicating whether a
callback function or event is being used. Here's a snippet openning the sound card:

// Open sound card for input:
m_ErrorCode = waveInOpen(
m_hwvin, // handle for opened device
WAVE_MAPPER, // default audio input device
m_WFXInSettings, // address of WAVEFORMAT structure
(DWORD) &WaveInCallback, //address of callback function
(DWORD) this, // app who owns callback
CALLBACK_FUNCTION // call back type
);

// Check for error in waveInOpen:
if (m_ErrorCode != 0)
{ MessageBox("OPEN FAILED","waveInOpen",MB_OK); }

Once the sound card is open, prepare the data buffers using the waveInPrepareHeader command, passing it the
handle to the sound card returned from the waveInOpen command, a pointer to the WAVEHDR object and the
size of the WAVEHDR object. Then assign the buffer to the sound card with the command waveInAddBuffer,
passing it the handle to the sound card returned from the waveInOpen command, a pointer to the WAVEHDR object
and the size of the WAVEHDR object. Here's a snippet preparing & adding the buffer :

// prepare headers:
if ( ( m_ErrorCode=waveInPrepareHeader ( *m_hwvin,m_pInputBuffer, sizeof *m_pInputBuffer ) ) !=NULL )
MessageBox("Prep hdr error","PREP HDRS",MB_OK);

// add buffers:
if ( ( m_ErrorCode=waveInAddBuffer ( *m_hwvin,m_pInputBuffer, sizeof *m_pInputBuffer ) ) != NULL )
MessageBox("Add buffer error","ADD BUFFERS",MB_OK);

Don't forget to add your call back function oer event. These snippets used a callback function the set an event that the app is waiting for. Here it is:

// ***** GLOBAL callback function for sound card:
void CALLBACK WaveInCallback(HWAVEIN m_hwvin,UINT uMsg, CApp* pCApp,
DWORD dwParam1, DWORD dwParam2)
{
if (uMsg==MM_WIM_DATA) // message from Sound card
m_Event.SetEvent(); // signal waiting thread to continue
}

Here's a snippet of the InRead function that's waiting on the sound card to set the call back event :

CSingleLock SyncOb(&m_Event); // Create Sync Object
// wait for mmsystem to fill new buffer
SyncOb.Lock( 5000); // wait for sound card to set event
// read data out of WAVEHDR data array
SyncOb.Unlock(); // reset event

All's that left is to start thesound card recording, using waveInOpenStart, passing the sound card handle.
Here's a snippet starting the sound card :

// start input capture to buffer:
if ( ( m_ErrorCode=waveInStart ( *m_hwvin ) ) != NULL )
MessageBox("Sound card error" , "SND CARD START" , MB_OK) ;