Click here to Skip to main content
15,880,543 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 440.9K   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

 
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 
Questioncan u help me Pin
Muralish22-Apr-05 23:20
Muralish22-Apr-05 23:20 
AnswerRe: can u help me Pin
gooshbob31-Aug-05 9:23
gooshbob31-Aug-05 9:23 
Generalwav header Pin
Muralish22-Apr-05 23:14
Muralish22-Apr-05 23:14 
can u please help me in setting a wav header class
GeneralSir! can you help me! Pin
mssu6-Apr-05 20:55
mssu6-Apr-05 20:55 
GeneralMulti-channel recording Pin
CosmoNova14-Mar-05 14:41
CosmoNova14-Mar-05 14:41 
GeneralCouldn't PLAY in _UNICODE Pin
alan9328-Feb-05 14:21
alan9328-Feb-05 14:21 
GeneralControlling the recorded Sound Pin
nabilelia27-Jan-05 2:59
nabilelia27-Jan-05 2:59 
Generaldevice problem Pin
toto567810-Dec-04 6:06
toto567810-Dec-04 6:06 
Questionhow it works ,need help Pin
atul123456789027-Oct-04 16:46
atul123456789027-Oct-04 16:46 
GeneralGreat but... Pin
bengreen53-Oct-04 11:42
bengreen53-Oct-04 11:42 
GeneralWave Format Transforming Question Pin
IsaacLitingjun19-Jan-05 19:42
IsaacLitingjun19-Jan-05 19:42 
GeneralTo ALL YOU intrested in playing or recording wave Pin
mohsen nourian7-Jul-04 21:49
mohsen nourian7-Jul-04 21:49 
Generalproblem with wave files &gt;1MB !!! Pin
mohsen nourian4-Jul-04 0:52
mohsen nourian4-Jul-04 0:52 
Questionthere is no size func ??? Pin
mohsen nourian3-Jul-04 21:47
mohsen nourian3-Jul-04 21:47 
Questionproblem with wave device ? Pin
ramtinbahal3-Jul-04 17:24
ramtinbahal3-Jul-04 17:24 
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 

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.