2012年8月19日星期日

转:delphi 做个录音机

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls,shellapi;

type
TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    Label1: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Label1Click(Sender: TObject);
protected
    procedure WndProc(var m: TMessage); override;
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

uses MMSystem;

var
whIn1,whIn2, whOut: TWaveHdr;{wav文件是有一定结构的,其中包含储存音频数据的地方,即buf1,buf2,SaveBuf}
hWaveIn,hWaveOut: HWAVE;//输入或输出设备指针
fmt: TWaveFormatEx;
buf1,buf2,SaveBuf: Array of byte;//TBytes;

procedure TForm1.FormCreate(Sender: TObject);
begin
Button1.Caption := '开始录音';
Button2.Caption := '停止录音';
Button3.Caption := '播放录音';
end;

//开始录音
procedure TForm1.Button1Click(Sender: TObject);
begin
{指定要录制的格式}
fmt.wFormatTag := WAVE_FORMAT_PCM;{指定格式类型; 默认 WAVE_FORMAT_PCM = 1;}
fmt.nChannels := 2;{指出波形数据的声道数; 单声道为 1, 立体声为 2}
fmt.nSamplesPerSec := 22050;{指定采样频率(每秒的样本数)}
fmt.nAvgBytesPerSec := 88200;{指定数据传输的传输速率(每秒的字节数)}
fmt.nBlockAlign := 4;{指定块对齐(每个样本的字节数), 块对齐是数据的最小单位}
fmt.wBitsPerSample := 16;{采样大小(字节), 每个样本的量化位数}
fmt.cbSize := 0;{附加信息的字节大小}

SaveBuf := nil; {清除已录制的内容}
{设备ID; 可以指定为: WAVE_MAPPER, 这样函数会根据给定的波形格式选择合适的设备}
即TWaveFormat 结构的指针; TWaveFormat 包含要申请的波形格式}
{Handle即回调函数地址或窗口句柄; 若不使用回调机制, 设为 nil}
if waveInOpen(@hWaveIn, WAVE_MAPPER, @fmt, Handle, 0, CALLBACK_WINDOW) = 0 then
begin
    SetLength(buf1, 1024*8);
    SetLength(buf2, 1024*8);

    whIn1.lpData := PAnsiChar(buf1);
    whIn1.dwBufferLength := Length(buf1);
    whIn1.dwBytesRecorded := 0;
    whIn1.dwUser := 0;
    whIn1.dwFlags := 0;
    whIn1.dwLoops := 0;
    whIn1.lpNext := nil;
    whIn1.reserved := 0;

    whIn2.lpData := PAnsiChar(buf2);
    whIn2.dwBufferLength := Length(buf2);
    whIn2.dwBytesRecorded := 0;
    whIn2.dwUser := 0;
    whIn2.dwFlags := 0;
    whIn2.dwLoops := 0;
    whIn2.lpNext := nil;
    whIn2.reserved := 0;

    waveInPrepareHeader(hWaveIn, @whIn1, SizeOf(TWaveHdr)); {左或右声道whIn1即TWaveHdr 结构的指针,做准备回写数据块工作}
    waveInPrepareHeader(hWaveIn, @whIn2, SizeOf(TWaveHdr)); {左或右声道whIn2即TWaveHdr 结构的指针,做准备回写数据块工作}
    waveInAddBuffer(hWaveIn, @whIn1, SizeOf(TWaveHdr));{当设备缓存区写满后,将该次记录数据块回写应用程序}
    waveInAddBuffer(hWaveIn, @whIn2, SizeOf(TWaveHdr));{当设备缓存区写满后,将该次记录数据块回写应用程序}

    waveInStart(hWaveIn);{要求指定设备开始录音}
end;
end;

//停止录音
procedure TForm1.Button2Click(Sender: TObject);
begin
waveInStop(hWaveIn);{要求指定设备停止录音}
waveInUnprepareHeader(hWaveIn, @whIn1, SizeOf(TWaveHdr));
waveInUnprepareHeader(hWaveIn, @whIn2, SizeOf(TWaveHdr));
waveInClose(hWaveIn);{关闭指定设备}
end;

//播放录音
procedure TForm1.Button3Click(Sender: TObject);
begin
whOut.lpData := PAnsiChar(SaveBuf);
whOut.dwBufferLength := Length(SaveBuf);
whOut.dwBytesRecorded := 0;
whOut.dwUser := 0;
whOut.dwFlags := 0;
whOut.dwLoops := 1;
whOut.lpNext := nil;
whOut.reserved := 0;

waveOutOpen(@hWaveOut, WAVE_MAPPER, @fmt, Handle, 0, CALLBACK_WINDOW);
waveOutPrepareHeader(hWaveOut, @whOut, SizeOf(TWaveHdr));{做准备发送数据块工作}
waveOutWrite(hWaveOut, @whOut, SizeOf(TWaveHdr));{正式发送该数据块}
end;

procedure TForm1.WndProc(var m: TMessage);
var
ordLen: Integer;
begin
inherited;
case m.Msg of
    {处理录音消息}
    MM_WIM_OPEN: ;     {此消息只携带了设备句柄}
    MM_WIM_CLOSE: ;    {此消息只携带了设备句柄}
    MM_WIM_DATA: begin {此消息携带了设备句柄和 WaveHdr 指针(LParam)}
      {保存录制的数据}
      ordLen := Length(SaveBuf);
      SetLength(SaveBuf, ordLen + PWaveHdr(m.LParam).dwBytesRecorded);//重新设定储存模块的大小,以接受新数据块
      CopyMemory(Ptr(DWORD(SaveBuf)+ordLen), PWaveHdr(m.LParam).lpData, PWaveHdr(m.LParam).dwBytesRecorded);//接受该数据块
      {继续录制,进行下一次数据块的录制和送回}
      waveInAddBuffer(hWaveIn, PWaveHdr(m.LParam), SizeOf(TWaveHdr));
    end;

    {处理播放消息}
    MM_WOM_OPEN: ;     {此消息只携带了设备句柄}
    MM_WOM_CLOSE: ;    {此消息只携带了设备句柄}
    MM_WOM_DONE: begin {此消息携带了设备句柄和 WaveHdr 指针(LParam),实际上是设备播放完毕后,要求软件关闭该设备发回的信息}
      waveOutUnprepareHeader(hWaveOut, PWaveHdr(m.LParam), SizeOf(TWaveHdr));
      waveOutClose(hWaveOut);
    end;
end;
end;
{wav文件是一段又一段的文件结构体TWaveHdr组成的}
procedure TForm1.Label1Click(Sender: TObject);
begin
ShellExecute(handle,'open','http://www.cnblogs.com/del/',nil,nil,SW_ShowNormal);
end;

end.
{综上我们可以看出,录音和放音都需要窗口句柄和设备句柄,原因在于它们本质都是应用程序和设备的交互,都需要知道彼此}

当然,这个不是最好的录音机,最好的是使用别人的控件,这样的控件简单的录音机不在话下,编写局域网聊天软件也是易如反掌。

没有评论: