Click here to Skip to main content
15,881,424 members
Articles / Desktop Programming / MFC
Article

Wave Class for Playing and Recording Wave Files

Rate me:
Please Sign up or sign in to vote.
4.78/5 (41 votes)
5 Dec 20012 min read 441K   16.9K   140   121
This class allows you to record and play wave files
Sample Image

Introduction

This is a simple class to record various sounds from sound cards and to play sound buffers recorded or loaded from a file.

How it works

CWave is the base class for loading or saving wave files. You can also build your own wave format for recording.

CWaveDevice take care of sound devices for the CWave class.

CWaveInterface lists all available devices and their names.

CWaveBufferis is a class that encapsulate buffers for CWave and CWaveIn/CWaveOut.

CWaveIn is the base class for recording (record, pause, continue, stop) and making a new CWave from the recording. In future development, we will add a virtual function to allow some work on new buffers during recording.

CWaveOut allows you to play the CWave in various ways.

Functions

Class CWaveInterface

//Return the device name for input device at index nIndex.
static CString GetWaveInName(UINT nIndex); 

//Return number of input devices detected.
static UINT GetWaveInCount(); 

// Return the device name for output device at index nIndex.
static CString GetWaveOutName(UINT nIndex); 

static UINT GetWaveOutCount(); //Return number of output devices detected. 

Class CWaveBuffer

int GetSampleSize() const; // Return the 'sizeof' a sample (short or long).

// Link pBuffer to the waveBuffer internal buffer, assuming there is 
// dwNumSamples samples of size nSize.
void SetBuffer(void* pBuffer, DWORD dwNumSamples, int nSize); 

// Create a new buffer for dwNumSamples samples of size nSize.
void SetNumSamples(DWORD dwNumSamples, int nSize = sizeof(short)); 

// Copy the buffer pBuffer of dwNumSamples samples.
void CopyBuffer(void* pBuffer, DWORD dwNumSamples, int nSize = sizeof(short)); 

DWORD GetNumSamples() const; // Return the number of samples. 
void* GetBuffer() const;// Return a pointer to the internal buffer.
CWaveBuffer();
virtual ~CWaveBuffer();

Class CWave : public CObject

// Copy or link the data buffer pBuffer of dwNumSamples samples to the 
// internal wave data buffer.
void SetBuffer(void* pBuffer, DWORD dwNumSamples, bool bCopy = false); 

void Load(const CString& strFile);
void Load(CFile* f); // Load a wave file.

void Save(const CString& strFile);
void Save(CFile* f); // Save the current instance to a wave file.

DWORD GetBufferLength() const; // Return the internal buffer length in bytes.
WORD GetNumSamples() const; // Return the internal buffer length in samples.
void* GetBuffer() const; // Return a link to the internal data buffer.

// Return the MS Windows wave format needed for some other functions.
WAVEFORMATEX GetFormat() const; 

// Build a MS Windows wave format from parameters (channels, sample 
// rate, 8 or 16 bits).
void BuildFormat(WORD nChannels, DWORD nFrequency, WORD nBits); 

CWave();
CWave(const CWave& copy);
CWave& operator=(const CWave& wave);
virtual ~CWave();
virtual void Serialize( CArchive& archive );

Class CWaveDevice

UINT GetDevice() const;// Return the MS Windows device number.
bool IsInputFormat(const CWave& wave); // Return true if the wave can be record.
bool IsOutputFormat(const CWave& wave); // Return true if the wave can be played.
CWaveDevice(UINT nDevice = WAVE_MAPPER);
CWaveDevice(const CWaveDevice& copy); virtual ~CWaveDevice();

Class CWaveOut

CString GetError() const;// Return the last string audio error.
DWORD GetPosition(); // Return the current position in the sample scale.
bool IsPlaying(); // Return true if the the instance is playing a buffer.

bool Close(); // Return true if the instance have closed the device for output. 
              // If false, use GetError() to get an explanation.
bool Continue(); // Return true if the instance has restarted playing the buffer. 
                 // If false, use GetError() to get an explanation.

// Play the whole buffer and allow to loop (INFINITE_LOOP allow to loop 
// for a long time…). Buffer can also be played from start to end (in sample scale).
bool FullPlay(int nLoop = -1, DWORD dwStart = -1, DWORD dwEnd = -1); 

bool Open(); // Return true if the instance has correctly been opened. If false, 
             // use GetError() to have an explanation. 
             
bool Pause(); // Return true if the instance has put the playing buffer in pause 
              // state. If false, use GetError() to have an explanation.

// Play the buffer. Buffer can also be played from start to end (in sample scale). 
>bool Play(DWORD dwStart = -1, DWORD dwEnd = -1); 
bool Stop(); // Return true if the instance has stopped playing the buffer. If false, 
             // use GetError() to get an explanation.
void ModifyWaveOutBufferLength(DWORD dwLength); // Set the read buffer length to this 
                                                // value (this mean the instance read dwLength 
                                                // bytes at a time in the Play function). 
void SetDevice(const CWaveDevice& aDevice); // Set the internal device to this one. 
void SetWave(const CWave& aWave); // Set the wave to be managed to this one. 
CWaveOut(); <BR>CWaveOut(const CWave& aWave, const CWaveDevice& aDevice); 
virtual ~CWaveOut();

Class CWaveIn

CString GetError() const;
DWORD GetPosition();
bool IsRecording();// Return true if current instance is recording. 
CWave MakeWave(); // Build a wave from instance buffer and MS Windows wave format.
bool Close(); 
bool Continue();
bool Open();
bool Pause(); 
bool Stop();
bool Record(UINT nTaille = 4096); // Start recording. Return true if no problem occur.
void SetDevice(const CWaveDevice& aDevice);
void SetWaveFormat(WAVEFORMATEX tagFormat); 
CWaveIn();
CWaveIn(WAVEFORMATEX tagFormat, const CWaveDevice& aDevice);
virtual ~CWaveIn(); 

Problems I encountered

I got some samples code for recording and playing wave buffers, but they almost all had  memory leaks. They also all use messages, threads or events. So, as it was not as easy as I expected for my own use, I decide to make my own classes, using the callback method.

The first problem was for playing buffers: there were noises and  interruptions, so I tried to use double buffering, and finally triple buffering (NUMWAVEOUTHDR  parameter). So I prepare 3 WAVEHDR structure from a class member buffer (see AddNewHeader() and Play() ) and the callback function takes care of adding a new one (function of the index to be prepared) and un-prepare the completed one.

Another bug arose as I worked on recording: I could not reset the device. As

waveOutReset
sends a MM_WOM_DONE message to the callback function, the AddNewHeader don't have to be execute. I introduced the ResetRequired() function to test if the reset has been requested.

Recording is almost the same process as playing, but you have to store recorded buffers. So I use a PtrList (see AddNewBuffer()).

As recommended in MSDN, for playing or recording, make a waveXUnprepareHeader, and then free the waveheader.lpData buffer.

An MFC app is included in the source to test these classes.

Have fun, and feel free to send any information concerning bugs, questions or improvements.

Updates

The last update corrected a wave format building error.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States
Software engineer since 1998, I worked on a medical software as designer and developper for e-med innovations inc.
Now out of job, but working on PHP and still VC++

Comments and Discussions

 
Generalmore details on wav Pin
mohsen nourian3-Jul-04 1:44
mohsen nourian3-Jul-04 1:44 
Generalrecording in c# Pin
ali_iac26-Apr-04 21:10
ali_iac26-Apr-04 21:10 
QuestionCan I record the 5.1 channel wave to the wave file by MME. Pin
White Lie16-Mar-04 15:58
White Lie16-Mar-04 15:58 
GeneralRecord &amp; Play simultanously Pin
ned-ned17-Feb-04 0:29
ned-ned17-Feb-04 0:29 
GeneralWin98 Problems Pin
Ed Dixon8-Jan-04 1:43
Ed Dixon8-Jan-04 1:43 
GeneralRe: Win98 Problems Pin
[ Pierre ]6-Feb-04 9:58
[ Pierre ]6-Feb-04 9:58 
GeneralBug in IsRecording and IsPlaying Pin
pegasusplus17-Nov-03 16:50
pegasusplus17-Nov-03 16:50 
GeneralOhh, resource leaks much! Pin
pegasusplus28-Oct-03 18:47
pegasusplus28-Oct-03 18:47 
Hi!

Do you monitor the application by task manager with handle count displayed?
When it is playing or recording sound, the handle count increases fast!

Examine the code, I found you set the dwFlags before call waveInUnprepareHeader/waveOutUnprepareHeader in waveInProc()/waveOutProc(), so that
these calls takes no effect at all! The resources allocated at waveInAddBuffer/waveOutAddBuffer are not released.

But if comment your assignment of the dwFlags, the application will dead lock when stop playing or recording. It is because you can not call waveXXX functions within waveInProc/waveOutProc while a waveInReset() was called.

So better add some judgement code to prevent resource leak while keep the application live.

Thanks!
GeneralRe: Ohh, resource leaks much! Pin
bengreen53-Oct-04 11:45
bengreen53-Oct-04 11:45 
GeneralDont do this! Pin
Patrick Hoffmann25-Oct-03 14:02
Patrick Hoffmann25-Oct-03 14:02 
GeneralRe: Dont do this! Pin
FlyingTinman21-Dec-06 14:57
FlyingTinman21-Dec-06 14:57 
GeneralUSB Audio Devices Pin
Krishna Chaitanya8-Oct-03 20:26
Krishna Chaitanya8-Oct-03 20:26 
GeneralRe: USB Audio Devices Pin
Sheng Jiang 蒋晟15-Oct-03 22:08
Sheng Jiang 蒋晟15-Oct-03 22:08 
GeneralLink error Pin
chito4-Oct-03 22:19
chito4-Oct-03 22:19 
GeneralRe: Link error Pin
yaseryacoob16-Feb-04 9:25
yaseryacoob16-Feb-04 9:25 
GeneralRe: Link error Pin
Mehdi Ebrahimian21-Apr-04 23:22
Mehdi Ebrahimian21-Apr-04 23:22 
Questioncan i draw the wave ?? Pin
ducasoft30-Sep-03 23:57
ducasoft30-Sep-03 23:57 
GeneralSammpling Rate problem Pin
Jezzy Lee16-Sep-03 22:27
Jezzy Lee16-Sep-03 22:27 
QuestionHow to support a sound input device in C# Pin
CNWORM22-Aug-03 20:48
CNWORM22-Aug-03 20:48 
QuestionHow to use the buffer? Pin
abob20-Jul-03 20:37
abob20-Jul-03 20:37 
QuestionCould I display the wav in the screen? Pin
ICE_WIZARD18-Jul-03 22:52
ICE_WIZARD18-Jul-03 22:52 
GeneralWave file Pin
Rahul Thapliyal27-Jun-03 1:22
Rahul Thapliyal27-Jun-03 1:22 
Generaltwo channels Pin
Anonymous4-Jun-03 6:05
Anonymous4-Jun-03 6:05 
GeneralComparing Pin
wouter devers13-May-03 6:13
wouter devers13-May-03 6:13 
GeneralClarity Pin
mabasa7-May-03 22:00
mabasa7-May-03 22:00 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.