Wave Class for Playing and Recording Wave Files






4.78/5 (38 votes)
Aug 23, 2001
2 min read

455980

16915
This class allows you to record and play wave files

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.
CWaveBuffer
is 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();
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.