MFC Multithreaded Recording, Saving to WAV files and Playback of Sound
by Paul Cheffers
The following classes, CRecordSound and CPlaySound, record
sound and play PCM sound simultaneously. An example dialog-based
program is provided that records, saves to disk and then echos sound.
This example also
contains two more classes, CWriteSoundFile and CPlayMMSound.
CWriteSoundFile receives sound buffers from CRecordSound and writes them
to a WAV disk file. CPlayMMSound opens these WAV files and plays them to
the sound device. All sound files are currently PCM samples. Be careful
with the sample application as it records a PCM WAV file. The PCM WAV file
can grow at 16 kilobytes a second, so keep the recorded samples short.
With these
classes, sound can be both recorded and played at the same time.
Continuous sound can be monitored and upon appropriate
queues, sound can be played back. Simple sound recognition
could also, for instance, be put into CRecordSound. The multithreading
allows other actions to take place while sound recording and playback
go on. As well, recorded sound can be saved to WAV files and played back.
CRecordSound can be invoked by the following code:
m_pRecordSound = new CRecordSound();
m_pRecordSound->CreateThread();
CPlaySound can be invoked by the following code:
m_pPlaySound = new CPlaySound();
m_pPlaySound->CreateThread();
To initiate sound recording from CRecordSound do the following:
m_RecordThread->
PostThreadMessage(WM_RECORDSOUND_STARTRECORDING, 0, 0L);
To stop recording sound:
m_RecordThread->
PostThreadMessage(WM_RECORDSOUND_STOPRECORDING,0,0);
Similar calls begin playing and stopping of sound:
m_PlayThread->
PostThreadMessage(WM_PLAYSOUND_STARTPLAYING,0,0);
m_PlayThread->
PostThreadMessage(WM_PLAYSOUND_STOPPLAYING,0,0);
The CRecordSound class ferries sound blocks that have been
recorded over to the CPlaySound class. The CRecordSound
class is notified of the CPlaySound thread via:
m_RecordSound->PostThreadMessage(WM_RECORDSOUND_SOUNDPLAYER, 0,(LPARAM)m_PlaySound);
For both CRecordSound and CPlaySound, the virtual member,
ProcessSoundData(short int* sound, DWORD dwSamples);
can be overridden to get access to the actual sound data.
CWriteSoundFile has the following messages associated with it:
m_WriteSoundThread->PostThreadMessage(WM_WRITESOUNDFILE_FILENAME,0,(LPARAM)
(PWRITESOUNDFILE)&structWriteSoundFile);
m_WriteSoundThread->PostThreadMessage(WM_WRITESOUNDFILE_WRITEBLOCK,0,(LPARAM)(WAVEHDR)pWaveHdr);
m_WriteSoundThread->PostThreadMessage(WM_WRITESOUNDFILE_CLOSEFILE,0,0);
To write a WAV file you must provide a WRITESOUNDFILE structure.
The WRITESOUNDFILE structure has the following definition:
typedef struct writesoundfile_tag {
char lpszFileName[MAX_PATH];
WAVEFORMATEX waveFormatEx;
TCHAR buffer[100];
} WRITESOUNDFILE, *PWRITESOUNDFILE;
You must provide the filename, then the WAVEFORMATEX structure that defines
the file to be written. With nonPCM formats there are extra style specific
information at the end of the structure, hence the 100 bytes of buffer space.
This class receives WAVEHDR blocks created by CRecordSound. In the example
application, pushing the "Start Recording" button will echo sound and
save it to a "sound.wav" file at the same time.
CPlayMMSound will read a WAV file and play it to the sound device. It uses
a pointer to a CPlaySound thread to acheive this. Its messages are:
m_pPlayMMSound->PostThreadMessage(WM_PLAYMMSOUND_PLAYFILE,0,(LPARAM)"sound.wav")
m_pPlayMMSound->PostThreadMessage(WM_PLAYMMSOUND_CLOSEFILE,0,0);
m_pPlayMMSound->PostThreadMessage(WM_PLAYMMSOUND_PLAYSOUNDPTR,0,(LPARAM)
(CPlaySound*)m_pPlaySound);
The WM_PLAYMMSOUND_PLAYFILE messages opens a WAV file for processing. It
automatically sends off a worker thread to play the file.
You must provide a CPlaySound thread for this to work. That is the job of
the WM_PLAYMMSOUND_PLAYSOUNDPTR message. You can stop the play at any time
by the WM_PLAYMMSOUND_CLOSEFILE message. Currently, CPlaySound is set to
play around 1.5 seconds ahead of actual heard sound.
Download the sample application here