Record Microphone Audio in Delphi and Save as a WAV File
This article shows how to create a Delphi application that records microphone audio
and saves it as a .wav file.
Audio is captured from the microphone using "waveInAddBuffer",
and the recorded data is written to a .wav file using TFileStream.
Create a New Project
Start Delphi, then choose "File" => "New" => "Windows VCL Application – Delphi (W)" from the menu.
Drag and drop two TComboBox components and two TButton components onto the form.
Enter the Source Code
Press the "F12" key to switch to the code editor, then copy and paste the following source code.
Press "F12" again to return to the design view,
set "FormCreate" for Form1’s OnCreate event and "FormClose" for the OnClose event.
Set "Button1Click" for the OnClick event of Button1, and “Button2Click” for the OnClick event of Button2.
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, MMSystem, Vcl.ExtCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
ComboBox1: TComboBox;
ComboBox2: TComboBox;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private declarations }
WaveFmt:TWaveFormatEx;
//Sampling buffers
BufIn:array of TBytes;
WaveInHandle:HWAVEIN;
NowBufId:Integer;
WaveHeader:array of TWaveHdr;
//Wave data buffer
WaveBuf:TBytes;
BufInLen:Integer;
SaveCount:Integer;
public
{ Public declarations }
end;
var
Form1: TForm1;
const
//Number of sampling buffers
BufInNum:Integer=8;
implementation
{$R *.dfm}
//Callback function
procedure WaveInCallBackFunc(hW:HWAVEOUT;uMsg:Cardinal;
dwInstance,dwParam1,dwParam2:UINT_PTR);stdcall;
var OldBufId, n:Integer;
begin
if uMsg=MM_WIM_DATA then
begin
OldBufId := Form1.NowBufId;
inc(Form1.NowBufId);
Form1.NowBufId := Form1.NowBufId mod BufInNum;
waveInAddBuffer(
Form1.WaveInHandle,
@Form1.WaveHeader[Form1.NowBufId],
SizeOf(TWaveHdr)
);
n := Length(Form1.WaveBuf);
SetLength(Form1.WaveBuf, n + Form1.BufInLen);
Move(Form1.BufIn[OldBufId][0], Form1.WaveBuf[n], Form1.BufInLen);
end;
end;
procedure SaveWaveFile(filename:string);
var ChunkID:array[0..3] of AnsiChar;
FileStream:TFileStream;
Size:Cardinal;
begin
FileStream := TFileStream.Create(Filename, fmCreate);
try
// 'RIFF' chunk
ChunkID := 'RIFF';
FileStream.WriteBuffer(ChunkID, SizeOf(ChunkID));
Size := 4 + (8 + SizeOf(Form1.WaveFmt)) + (8 + Length(Form1.WaveBuf));
FileStream.WriteBuffer(Size, SizeOf(Size));
// 'WAVE' format
ChunkID := 'WAVE';
FileStream.WriteBuffer(ChunkID, SizeOf(ChunkID));
// 'fmt ' chunk
ChunkID := 'fmt ';
FileStream.WriteBuffer(ChunkID, SizeOf(ChunkID));
Size := SizeOf(Form1.WaveFmt);
FileStream.WriteBuffer(Size, SizeOf(Size));
FileStream.WriteBuffer(Form1.WaveFmt, size);
// 'data' chunk
ChunkID := 'data';
FileStream.WriteBuffer(ChunkID, SizeOf(ChunkID));
Size := Length(Form1.WaveBuf);
FileStream.WriteBuffer(Size, SizeOf(Size));
FileStream.WriteBuffer(Form1.WaveBuf[0], Size);
finally
FileStream.Free;
end;
end;
//Start recording
procedure TForm1.Button1Click(Sender: TObject);
var i:Integer;
begin
ComboBox1.Enabled:=False;
ComboBox2.Enabled:=False;
Button1.Enabled:=False;
Button2.Enabled:=True;
SetLength(WaveBuf, 0);
ZeroMemory(@WaveFmt,SizeOf(TWaveFormatEx));
WaveFmt.wFormatTag:=WAVE_FORMAT_PCM;
//Number of channels (mono or stereo)
WaveFmt.nChannels := ComboBox1.ItemIndex + 1;
//Sampling rate: 11025 / 22050 / 44100 / 48000
//Supported values depend on the hardware
if ComboBox2.ItemIndex=0 then
WaveFmt.nSamplesPerSec := 11025
else if ComboBox2.ItemIndex=1 then
WaveFmt.nSamplesPerSec := 22050
else if ComboBox2.ItemIndex=2 then
WaveFmt.nSamplesPerSec := 44100
else
WaveFmt.nSamplesPerSec := 48000;
//Bits per sample (signed 16‑bit integer: −32768 to 32767)
WaveFmt.wBitsPerSample := 16;
//Bytes per block
WaveFmt.nBlockAlign := WaveFmt.nChannels * WaveFmt.wBitsPerSample div 8;
//Average bytes per second
WaveFmt.nAvgBytesPerSec := WaveFmt.nBlockAlign * WaveFmt.nSamplesPerSec;
//Must be 0
WaveFmt.cbSize := 0;
waveInOpen(
@WaveInHandle, WAVE_MAPPER, @WaveFmt, DWORD_PTR(@WaveInCallBackFunc),
0, CALLBACK_FUNCTION+WAVE_ALLOWSYNC
);
BufInLen := WaveFmt.nSamplesPerSec * WaveFmt.nChannels;
SetLength(WaveHeader, BufInNum);
SetLength(BufIn, BufInNum);
for i := 0 to BufInNum-1 do
begin
//Set buffer size
SetLength(BufIn[i], BufInLen);
//Configure buffer
WaveHeader[i].lpData := PAnsiChar(BufIn[i]);
WaveHeader[i].dwBufferLength := BufInLen;
WaveHeader[i].dwBytesRecorded := 0;
WaveHeader[i].dwUser := i;
WaveHeader[i].dwFlags := 0;
WaveHeader[i].dwLoops := 0;
WaveHeader[i].lpNext := nil;
WaveHeader[i].reserved := 0;
waveInPrepareHeader(WaveInHandle, @WaveHeader[i], SizeOf(TWaveHdr));
end;
NowBufId := 0;
waveInAddBuffer(WaveInHandle, @WaveHeader[NowBufId], SizeOf(TWaveHdr));
waveInStart(WaveInHandle);
end;
//Stop recording
procedure TForm1.Button2Click(Sender: TObject);
var i:Integer;
begin
ComboBox1.Enabled := True;
ComboBox2.Enabled := True;
Button1.Enabled := True;
Button2.Enabled := False;
waveInStop(WaveInHandle);
for i := 0 to BufInNum-1 do
waveInUnprepareHeader(WaveInHandle, @WaveHeader[i], SizeOf(TWaveHdr));
waveInClose(WaveInHandle);
SaveWaveFile('sample'+SaveCount.ToString+'.wav');
Inc(SaveCount);
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action:=caFree;
//Stop recording if still active
if Button2.Enabled then Button2Click(Button2);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
BufInLen := 11025;
SaveCount := 1;
ComboBox1.Style:=csDropDownList;
ComboBox1.Items.Add('Mono');
ComboBox1.Items.Add('Stereo');
ComboBox1.ItemIndex := 0;
ComboBox2.Style:=csDropDownList;
ComboBox2.Items.Add('11025');
ComboBox2.Items.Add('22050');
ComboBox2.Items.Add('44100');
ComboBox2.Items.Add('48000');
ComboBox2.ItemIndex := 0;
Button2.Enabled := False;
end;
end.
Running the Application
Connect a built‑in or external microphone to your PC.
Run the application and click Button1 to start recording.
Click Button2 to stop recording. The audio will be saved as a file named “sample***.wav”.
The *** part will be replaced with an incrementing number.
