Click here to Skip to main content
15,883,799 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 441.4K   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

 
QuestionA little problem in CWaveOut::FullPlay(124) line Pin
gocyann3-Jul-14 21:14
gocyann3-Jul-14 21:14 
QuestionCan I use this code in my commercial project? Pin
Member 87468193-Apr-12 16:26
Member 87468193-Apr-12 16:26 
GeneralCallback Problem Pin
RedFraggle22-May-10 2:02
RedFraggle22-May-10 2:02 
GeneralMemory owerflow problems when recording Pin
Anders Gustafsson7-Mar-09 22:48
Anders Gustafsson7-Mar-09 22:48 
Generalabout Concurrency Pin
DSPCottage9-Jan-09 8:35
DSPCottage9-Jan-09 8:35 
QuestionNeed to append a recording to existing wave file Pin
bha_code8-Jan-09 18:04
bha_code8-Jan-09 18:04 
AnswerRe: Need to append a recording to existing wave file Pin
lozan4727-Jan-09 15:43
lozan4727-Jan-09 15:43 
QuestionLegal ? Pin
techking29-Jun-08 2:47
techking29-Jun-08 2:47 
GeneralA bug may cause crash Pin
williamfang13-May-08 11:51
williamfang13-May-08 11:51 
QuestionHow do i open up the setup Pin
Canetoad19-Mar-08 18:56
Canetoad19-Mar-08 18:56 
QuestionRecord audio and display audio level meter... Pin
celoftis18-Jul-07 9:51
celoftis18-Jul-07 9:51 
GeneralProblem in Recording - WinCE Pin
lakshmanakumar_varada1-Apr-07 19:21
lakshmanakumar_varada1-Apr-07 19:21 
Generalencode audio after record by GSM 6.10 Pin
taolacha11323-Nov-06 19:03
taolacha11323-Nov-06 19:03 
Generalnot working correctly Pin
recoil25-Oct-06 3:59
recoil25-Oct-06 3:59 
QuestionWhere is the device Selected??? Pin
~Jabeen~13-Oct-06 21:39
~Jabeen~13-Oct-06 21:39 
QuestionRecord &#8211; play streaming directly from input to output (no files) Pin
madfitz21-Sep-06 5:49
madfitz21-Sep-06 5:49 
GeneralHelp me with default recording device!!! Pin
Hung In26-Jul-06 16:00
Hung In26-Jul-06 16:00 
GeneralYour program and .Net question Pin
Parik Advani19-Jun-06 0:04
Parik Advani19-Jun-06 0:04 
Questionconverter Pin
hafz12-Apr-06 22:29
hafz12-Apr-06 22:29 
GeneralOverload + Oerator Pin
Ambrish Dwivedi23-Jan-06 21:24
Ambrish Dwivedi23-Jan-06 21:24 
Generalget buffer Pin
whatsmyid22-Jan-06 6:18
whatsmyid22-Jan-06 6:18 
Questioncan it play/record on Windows 2000? Pin
Seven Pham9-Nov-05 16:49
Seven Pham9-Nov-05 16:49 
Questionis it possible in C# Pin
Kumar Muthu29-Oct-05 0:20
Kumar Muthu29-Oct-05 0:20 
GeneralUpdate Pin
[ Pierre ]19-May-05 23:51
[ Pierre ]19-May-05 23:51 
GeneralRe: Update Pin
ysyoon19-Nov-05 21:47
ysyoon19-Nov-05 21:47 

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.