[All]
Quick and Easy Audio Recording in C++ Builder
By: David Pankhurst
Abstract: Use the MCIWnd* family of functions to implement something lacking in TMediaPlayer - easily configurable audio recording.
Quick and Easy Audio Recording in C++ Builder
Although the TMediaPlayer component in Builder
does just about everything I could want,
one problem I've encountered with it is in
recording. This article details how to provide
recording support in Builder with the MCI
Window interface.
Simple Recording?
The problem is fairly simple in TMediaPlayer
- you need to have a valid FileName entry
before calling Open(), which means the audio
has to exist before you record it! As well
it is that is it quite awkward to change
audio settings, or create brand-new audio,
since there are no VCL calls for this kind
of access. While none of this is insurmountable,
for my recording needs all I wanted was a
simple solution, ideally with just two buttons:
Record and Stop.
Surveying the Window literature gave me the
idea to use the MCIWnd* class of functions,
and the result was a very simple solution.
The MCI Window wraps all the general media
calls in a window you create on your form.
Although it can be used for more than recording,
there's little need with TMediaPlayer available,
so we'll just focus on that aspect here.
The sample code
shows how to set up a window, record audio, and close.
The program is exceedingly simple, and so
makes it straightforward for you to cut and
paste recording functionality into your own
programs. Discussing the code will show just
how easy it is to set up and record.
Coding Our Window
First off, we add code to create an MCI Window
and close it. Since the window can exist
for the life of the program, we handle construction
and destruction in the form's constructor
and destructor: extern HINSTANCE g_hInstance;
HANDLE m_hMCIWnd =NULL;
//----------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
m_hMCIWnd=MCIWndCreate(Handle,
g_hInstance,
WS_CHILD | WS_OVERLAPPED,
NULL );
if ( NULL==m_hMCIWnd ) // error?
{
MessageBox(Handle,"Error Creating MCIWnd Window!",NULL, MB_OK);
return;
}
}
//-----------------------------------------------------------------
__fastcall TForm1::~TForm1(void)
{
MCIWndDestroy(m_hMCIWnd);
}
All of the parameters to MCIWndCreate() are
straightforward, with the exception of g_hInstance,
which is the program's HINSTANCE value from
our WinMain (Project1.cpp). The actual program
source code has more options than shown here,
since you can choose to show this window
and display buttons, framing, and so forth.
However, since all we want is recording,
we've opted to leave off the WS_VISIBLE flag.
This window is like any other window in the
system. You can communicate with it via SendMessage()
and PostMessage(), but fortunately there
are macros that wrap these calls into simpler
formats, the MCIWnd* functions. As a first
example we have the commands to set up and
begin recording a new file, placed in our
Record button handler:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
// create new .WAV file
MCIWndNew(m_hMCIWnd, "waveaudio");
// begin recording
MCIWndRecord(m_hMCIWnd);
}
Stopping is equally easy, by responding to
the Stop button, thereby making audio recording
possible in five lines of code (after setup
of course):
void __fastcall TForm1::Button2Click(TObject *Sender)
{
// stop recording and save file
MCIWndStop(m_hMCIWnd);
MCIWndSave(m_hMCIWnd,file);
MCIWndClose(m_hMCIWnd);
}
As always, there are some caveats. Since
the filename is not needed until closing,
you rightly can guess that the file is maintained
in memory, and so this isn't a solution for
recording large audio files. As well, there
is no provision for monitoring of buffers
or mixing - if you need that kind of low-level
control, consider writing your own buffer
recording routines. An excellent reference
for that is 'Programming Windows' by Charles
Petzold.
Changing Settings
We now need to look at altering settings
for frequency, channels and the like. Recording
defaults to 11 kilohertz, 8 bit sampling,
and single channel (mono); if this is what
you need, no further adjustments are necessary.
However, settings can be changed with one
call (which in the program is placed between
MCIWndNew() and MCIWndRecord()):
MCI_WAVE_SET_PARMS set_parms; // audio parameters
set_parms.wFormatTag = WAVE_FORMAT_PCM;
set_parms.wBitsPerSample = 16;
set_parms.nChannels = 1;
set_parms.nSamplesPerSec = 44100;
set_parms.nBlockAlign = (set_parms.nChannels*set_parms.wBitsPerSample)/8;
set_parms.nAvgBytesPerSec = ((set_parms.wBitsPerSample) *
set_parms.nChannels *
set_parms.nSamplesPerSec)/8;
// now send the format changes with MCI_SET
int deviceID=MCIWndGetDeviceID(m_hMCIWnd);
int result = mciSendCommand( deviceID, MCI_SET,
MCI_WAIT
| MCI_WAVE_SET_FORMATTAG
| MCI_WAVE_SET_BITSPERSAMPLE
| MCI_WAVE_SET_CHANNELS
| MCI_WAVE_SET_SAMPLESPERSEC
| MCI_WAVE_SET_AVGBYTESPERSEC
| MCI_WAVE_SET_BLOCKALIGN,
(DWORD)(LPVOID)&set_parms);
if ( result ) // failed?
{
char buffer[100];
mciGetErrorString(result, buffer, sizeof(buffer));
MessageBox( NULL, buffer, "MCI_WAVE_SET_1", MB_OK);
return;
}
Because there is no MCIWnd* call for setting
the audio, we fall back on the mciSendCommand(),
which needs a device ID (provided by MCIWndGetDeviceID),
a structure containing the parameters, and
flags indicating which structure items are
to be changed.
The structure values you most likely will
change are wBitsPerSample (8 or 16), nChannels
(1 for mono, 2 for stereo), and nSamplesPerSec
(frequency in hertz). While you can change
the other parameters, there might be problems,
since not all values work. For instance if
you change the wFormatTag value to WAVE_FORMAT_ADPCM
you'll get the response 'The parameter is
out of range for the specified command'.
The morale is to stick to known values, or
be prepared to test a lot.
In conclusion, be sure to look up the Builder
documentation on the MCWnd* calls. Although
TMediaPlayer satisfies most needs, using
MCI window calls can give you easy and flexible
recording in Builder.
|
|
Connect with Us