Wav 文件的文件头的格式是如何定义的?(50分)

  • 主题发起人 主题发起人 ayu310
  • 开始时间 开始时间
jingtao (2001-8-19 23:28:00)
建议分辨率:800X600
 
  如何用Delphi制作录音程序
陈经韬

Delphi是Inprise(前Borland)公司的优秀的可视化编程工具,它自带的Mediaplayer控件是开发多媒体的利器。用它几分钟就可以做出一个象解霸一样可以播放多媒体文件的程序来。但可能很少人知道,用它也可以做一个录音程序。
运行Delphi,在System页拖一个Mediaplayer控件到窗体上,默认名为Mediaplayer1。由于我们的程序是采用自己的按钮,所以将Mediaplayer1的Visible属性设置为False,其它属性保持默认值。再放两个按钮Button1和Button2。Button1的属性Name改为BtStart,Caption改为"开始录音", Button2的属性Name改为BtStop,Caption改为"停止录音",Enabled属性改为False。然后切换窗口到代码窗口,开始书写代码。
程序中,我们定义了一个Wav文件的文件头格式,录音时先创建一个只有文件头的Wav文件,然后将Mediaplayer1录制下来的声音写进文件。其中CreateWav过程的几个参数意义如下:第一个channels代表声道,取1时代表单声,取2时代表立体声。resolution也只有两个值可以选择,取8时代表8位声音,取16时代表16位声音,rate则代表声音频率,如11025,22050, 44100。值越大则声音越清晰,当然,所录制的文件也越大。最后一个参数则代表对应的文件名称了。所以CreateWav可以有以下形式:
CreateWav(1,8,11025,'C:/abc.wav');//在C盘根目录下创建一个8位单声道频率为11025的名为abc.wav的Wav文件
CreateWav(2,16,44100,'C:/abc.wav');//在C盘根目录下创建一个16位立体声道频率为44100的名为abc.wav的Wav文件

外国一个很出名的用Delphi编写的远程控制软件Netbus有一个声音监听功能,就是用本文的方法写的。它先把对方的声音录制下来,然后传送回来,达到监听对方的目的。当然,前提是对方必须安装有话筒,否则监听到的是对方播放的声音(如打开解霸或者Readplay播放,运行本程序,就可以把播放的声音录制下来了)。
实际上,现在的网络声音传播技术已经发展到一定阶段,语音对讲和IP电话等也开始成熟。不过它们采用的是VOX格式或者ACM格式,具体代码可以在我的主页http://Lovejingtao.126.com下载。但如果对VOX或者ACM格式不熟悉的朋友,也可以用本文的方法来制作自己的“录音机”。至于如何调用系统自带的avifil32.dll来进行网络视频播放,在有机会时再跟大家交流吧。
本程序在Pwin98+Delphi5下通过。

unit Unit1;

interface

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

type
TWavHeader = record //定义一个Wav文件头格式
rId : longint;

rLen : longint;

wId : longint;

fId : longint;

fLen : longint;

wFormatTag : word;

nChannels : word;

nSamplesPerSec : longint;

nAvgBytesPerSec : longint;

nBlockAlign : word;

wBitsPerSample : word;

dId : longint;

wSampleLength : longint;

end;

TForm1 = class(TForm)
MediaPlayer1: TMediaPlayer;
BtStart: TButton;
BtStop: TButton;
procedure CreateWav(channels : word;
resolution : word;
rate : longint;
fn : string);//自定义写一个Wav文件头过程
procedure BtStartClick(Sender: TObject);
procedure BtStopClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;


var
Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.CreateWav( channels : word;
{ 1(单声)或者2(立体声) }
resolution : word;
{ 8或者16,代表8位或16位声音 }
rate : longint;
{ 声音频率,如11025,22050, 44100}
fn : string { 对应的文件名称 } );
var
wf : file of TWavHeader;
wh : TWavHeader;
begin

wh.rId := $46464952;

wh.rLen := 36;

wh.wId := $45564157;

wh.fId := $20746d66;

wh.fLen := 16;

wh.wFormatTag := 1;

wh.nChannels := channels;

wh.nSamplesPerSec := rate;

wh.nAvgBytesPerSec := channels*rate*(resolution div 8);
wh.nBlockAlign := channels*(resolution div 8);
wh.wBitsPerSample := resolution;
wh.dId := $61746164;

wh.wSampleLength := 0;


assignfile(wf,fn);
{打开对应文件 }
rewrite(wf);
{移动指针到文件头}
write(wf,wh);
{写进文件头 }
closefile(wf);
{关闭文件 }
end;

procedure TForm1.BtStartClick(Sender: TObject);
begin

try
//在程序当前目录下创建一个Wav文件Temp.wav
CreateWav(1, 8, 11025, (ExtractFilePath(Application.ExeName)+ 'Temp.wav'));
MediaPlayer1.DeviceType := dtAutoSelect;
MediaPlayer1.FileName := (ExtractFilePath(Application.ExeName)+ 'Temp.wav');
MediaPlayer1.Open;
MediaPlayer1.StartRecording;
BtStart.Enabled:=false;
BtStop.Enabled:=true;
except
BtStart.Enabled:=True;
BtStop.Enabled:=false;
Application.MessageBox('媒体设备初始化失败!','错误',MB_ICONERROR+MB_OK);
end;

end;


procedure TForm1.BtStopClick(Sender: TObject);
begin

try
MediaPlayer1.Stop;
MediaPlayer1.Save;
MediaPlayer1.Close;
Application.MessageBox('声音录制完毕!','信息',MB_ICONINFORMATION+MB_OK);
BtStart.Enabled:=True;
BtStop.Enabled:=false;
except
Application.MessageBox('保存声音文件出错!','错误',MB_ICONERROR+MB_OK);
BtStart.Enabled:=True;
BtStop.Enabled:=false;
end;

end;

end.


补充:1、录制的文件播放时可能要把音频属性的Wav调大。
2、如果系统安装了其它一些音频驱动程序,则可能录制的Wav文件大小为零,但会同时生成一个TMP结尾的文件,将其扩展名改为Wav就是录制的声音文件。但这种情况很少发生。(机会几乎为零^-^)
3、本程序在运行解霸和Replayer下录制声音通过。

★作者:

陈经韬


Http:Lovejingtao.126.com
E-Mail: Lovejingtao@21.cn.com




©CopyRight 2000

 
给你一个WAV文件结构格式的单元
unit LinearSystem;


interface

{============== WAV Format Coding Type ==================}
type WAVHeader = record
nChannels : Word;

nBitsPerSample : LongInt;

nSamplesPerSec : LongInt;

nAvgBytesPerSec : LongInt;

RIFFSize : LongInt;

fmtSize : LongInt;

formatTag : Word;

nBlockAlign : LongInt;

DataSize : LongInt;

end;



{============== Sample DataStreams ========================}
const MaxN = 300;
{ max number of sample values }
type SampleIndex = 0 .. MaxN+3;

type DataStream = array[ SampleIndex ] of Real;


var N : SampleIndex;


{============== Observation Variables ======================}
type Observation = record
Name : String[40];
{Name of this observat
ion}
yyy : DataStream;
{Array of data points}

WAV : WAVHeader;
{WAV specs for observa
tion}
Last : SampleIndex;{Last valid index to y
yy}
MinO, MaxO : Real;
{Range values from yyy
}
end;



var K0R, K1R, K2R, K3R : Observation;

K0B, K1B, K2B, K3B : Observation;


{================== File Name Variables ===================}
var StandardDatabase : String[ 80 ];

BaseFileName : String[ 80 ];

StandardOutput : String[ 80 ];

StandardInput : String[ 80 ];


{=============== Operations ==================}
procedure ReadWAVFile (var Ki, Kj : Observation);

procedure WriteWAVFile(var Ki, Kj : Observation);

procedure ScaleData (var Kk : Observation);

procedure InitAllSignals;

procedure InitLinearSystem;



implementation
{$R *.DFM}
uses VarGraph, SysUtils;



{================== Standard WAV File Format ===================}
const MaxDataSize : LongInt = (MaxN+1)*2*2;

const MaxRIFFSize : LongInt = (MaxN+1)*2*2+36;

const StandardWAV : WAVHeader = (
nChannels : Word(2);

nBitsPerSample : LongInt(16);

nSamplesPerSec : LongInt(8000);

nAvgBytesPerSec : LongInt(32000);

RIFFSize : LongInt((MaxN+1)*2*2+36);

fmtSize : LongInt(16);

formatTag : Word(1);

nBlockAlign : LongInt(4);

DataSize : LongInt((MaxN+1)*2*2)
);



{================== Scale Observation Data ===================}

procedure ScaleData(var Kk : Observation);

var I : SampleIndex;

begin

{Initialize the scale values}
Kk.MaxO := Kk.yyy[0];

Kk.MinO := Kk.yyy[0];


{then
scan for any higher or lower values}
for I := 1 to Kk.Lastdo

begin

if Kk.MaxO < Kk.yyy then
Kk.MaxO := Kk.yyy;

if Kk.MinO > Kk.yyy then
Kk.MinO := Kk.yyy;

end;


end;

{ ScaleData }

procedure ScaleAllData;

begin

ScaleData(K0R);

ScaleData(K0B);

ScaleData(K1R);

ScaleData(K1B);

ScaleData(K2R);

ScaleData(K2B);

ScaleData(K3R);

ScaleData(K3B);

end;

{ScaleAllData}

{================== WAV Data I/O ===================}

VAR InFile, OutFile : file of Byte;


type Tag = (F0, T1, M1);

type FudgeNum = record
case X:Tag of
F0 : (chrs : array[0..3] of Byte);

T1 : (lint : LongInt);

M1 : (up,dn: Integer);

end;


var ChunkSize : FudgeNum;


procedure WriteChunkName(Name:String);

var i : Integer;

MM : Byte;

begin

for i := 1 to 4do

begin

MM := ord(Name);

write(OutFile,MM);

end;


end;

{WriteChunkName}

procedure WriteChunkSize(LL:Longint);

var I : integer;

begin

ChunkSize.x:=T1;

ChunkSize.lint:=LL;

ChunkSize.x:=F0;

for I := 0 to 3do
Write(OutFile,ChunkSize.chrs);

end;



procedure WriteChunkWord(WW:Word);

var I : integer;

begin

ChunkSize.x:=T1;

ChunkSize.up:=WW;

ChunkSize.x:=M1;

for I := 0 to 1do
Write(OutFile,ChunkSize.chrs);

end;

{WriteChunkWord}

procedure WriteOneDataBlock(var Ki, Kj : Observation);

var I : Integer;

begin

ChunkSize.x:=M1;

with Ki.WAVdo

begin

case nChannels of
1:if nBitsPerSample=16
then
begin
{1..2 16-bit samples in buffer for one channel}
ChunkSize.up := trunc(Ki.yyy[N]+0.5);

if N .5);

N := N+2;

end
else
begin
{1..4 8-bit samples in buffer for one channel}
for I:=0 to 3do
ChunkSize.chrs
:= trunc(Ki.yyy[N+I]+0.5);

N := N+4;

end;


2:if nBitsPerSample=16
then
begin
{2 16-bit samples on two channels}
ChunkSize.dn := trunc(Ki.yyy[N]+0.5);

ChunkSize.up := trunc(Kj.yyy[N]+0.5);

N := N+1;

end
else
begin
{4 8-bit samples on two channels}
ChunkSize.chrs[1] := trunc(Ki.yyy[N]+0.5);

ChunkSize.chrs[3] := trunc(Ki.yyy[N+1]+0.5);

ChunkSize.chrs[0] := trunc(Kj.yyy[N]+0.5);

ChunkSize.chrs[2] := trunc(Kj.yyy[N+1]+0.5);

N := N+2;

end;


end;

{with WAVdo
begin
..}
end;

{the four-byte variable "ChunkSize" has now been filled}

ChunkSize.x:=T1;

WriteChunkSize(ChunkSize.lint);{put 4 bytes of data}
end;

{WriteOneDataBlock}

procedure WriteWAVFile(var Ki, Kj : Observation);

var MM : Byte;

I : Integer;

OK : Boolean;

begin

{Prepare to write a file of data}
AssignFile(OutFile, StandardOutput);
{ File selected in dialog }

ReWrite( OutFile );

With Ki.WAVdo

begin
DataSize := nChannels*(nBitsPerSample div 8)*(Ki.Last+1);

RIFFSize := DataSize+36;

fmtSize := 16;

end;



{Write ChunkName "RIFF"}
WriteChunkName('RIFF');


{Write ChunkSize}
WriteChunkSize(Ki.WAV.RIFFSize);


{Write ChunkName "WAVE"}
WriteChunkName('WAVE');


{Write tag "fmt_"}
WriteChunkName('fmt ');


{Write ChunkSize}
Ki.WAV.fmtSize := 16;
{should be 16-18}
WriteChunkSize(Ki.WAV.fmtSize);


{Write formatTag, nChannels}
WriteChunkWord(Ki.WAV.formatTag);

WriteChunkWord(Ki.WAV.nChannels);


{Write nSamplesPerSec}
WriteChunkSize(Ki.WAV.nSamplesPerSec);


{Write nAvgBytesPerSec}
WriteChunkSize(Ki.WAV.nAvgBytesPerSec);


{Write nBlockAlign, nBitsPerSample}
WriteChunkWord(Ki.WAV.nBlockAlign);

WriteChunkWord(Ki.WAV.nBitsPerSample);


{WriteDataBlock tag "data"}
WriteChunkName('data');


{Write DataSize}
WriteChunkSize(Ki.WAV.DataSize);


N:=0;
{first write-out location}
while N<=Ki.Lastdo
WriteOneDataBlock(Ki,Kj);
{put 4 bytes &amp;
incr
ement N}

{Free the file buffers}
CloseFile( OutFile );

end;

{WriteWAVFile}

procedure InitSpecs;

begin

end;

{ InitSpecs }

procedure InitSignals(var Kk : Observation);

var J : Integer;

begin

for J := 0 to MaxNdo
Kk.yyy[J] := 0.0;

Kk.MinO := 0.0;

Kk.MaxO := 0.0;

Kk.Last := MaxN;

end;

{InitSignals}


procedure InitAllSignals;

begin

InitSignals(K0R);

InitSignals(K0B);

InitSignals(K1R);

InitSignals(K1B);

InitSignals(K2R);

InitSignals(K2B);

InitSignals(K3R);

InitSignals(K3B);

end;

{InitAllSignals}

var ChunkName : string[4];


procedure ReadChunkName;

var I : integer;

MM : Byte;

begin

ChunkName[0]:=chr(4);

for I := 1 to 4do

begin

Read(InFile,MM);

ChunkName:=chr(MM);

end;


end;

{ReadChunkName}

procedure ReadChunkSize;

var I : integer;

MM : Byte;

begin

ChunkSize.x := F0;

ChunkSize.lint := 0;

for I := 0 to 3do

begin

Read(InFile,MM);

ChunkSize.chrs:=MM;

end;


ChunkSize.x := T1;

end;

{ReadChunkSize}

procedure ReadOneDataBlock(var Ki,Kj:Observation);

var I : Integer;

begin

if N<=MaxN then

begin

ReadChunkSize;
{get 4 bytes of data}
ChunkSize.x:=M1;

with Ki.WAVdo

case nChannels of
1:if nBitsPerSample=16
then
begin
{1..2 16-bit samples in buffer for one channel}
Ki.yyy[N] :=1.0*ChunkSize.up;

if N N := N+2;

end
else
begin
{1..4 8-bit samples in buffer for one channel}
for I:=0 to 3do
Ki.yyy[N+I]:=1.0*ChunkSize.chrs[I
];

N := N+4;

end;


2:if nBitsPerSample=16
then
begin
{2 16-bit samples on two channels}
Ki.yyy[N]:=1.0*ChunkSize.dn;

Kj.yyy[N]:=1.0*ChunkSize.up;

N := N+1;

end
else
begin
{4 8-bit samples on two channels}
Ki.yyy[N] :=1.0*ChunkSize.chrs[1];

Ki.yyy[N+1]:=1.0*ChunkSize.chrs[3];

Kj.yyy[N] :=1.0*ChunkSize.chrs[0];

Kj.yyy[N+1]:=1.0*ChunkSize.chrs[2];

N := N+2;

end;


end;


if N<=MaxN then
begin
{LastN := N;}
Ki.Last := N;

if Ki.WAV.nChannels=2 then
Kj.Last := N;

end
else
begin
{LastN := MaxN;}
Ki.Last := MaxN;

if Ki.WAV.nChannels=2 then
Kj.Last := MaxN
;

end;


end;


end;

{ReadOneDataBlock}

procedure ReadWAVFile(var Ki, Kj :Observation);

var MM : Byte;

I : Integer;

OK : Boolean;

NoDataYet : Boolean;

DataYet : Boolean;

nDataBytes : LongInt;

begin

if FileExists(StandardInput)
then

with Ki.WAVdo

begin
{ Bring up open file dialog }
OK := True;
{unless changed somewhere below}
{Prepare to read a file of data}
AssignFile(InFile, StandardInput);
{ File selected in dialog }
Reset( InFile );


{Read ChunkName "RIFF"}
ReadChunkName;

if ChunkName<>'RIFF' then
OK := False;


{Read ChunkSize}
ReadChunkSize;

RIFFSize := ChunkSize.lint;
{should be 18,678}

{Read ChunkName "WAVE"}
ReadChunkName;

if ChunkName<>'WAVE' then
OK := False;


{Read ChunkName "fmt_"}
ReadChunkName;

if ChunkName<>'fmt ' then
OK := False;


{Read ChunkSize}
ReadChunkSize;

fmtSize := ChunkSize.lint;
{should be 18}

{Read formatTag, nChannels}
ReadChunkSize;

ChunkSize.x := M1;

formatTag := ChunkSize.up;

nChannels := ChunkSize.dn;


{Read nSamplesPerSec}
ReadChunkSize;

nSamplesPerSec := ChunkSize.lint;


{Read nAvgBytesPerSec}
ReadChunkSize;

nAvgBytesPerSec := ChunkSize.lint;


{Read nBlockAlign}
ChunkSize.x := F0;

ChunkSize.lint := 0;

for I := 0 to 3do

begin
Read(InFile,MM);

ChunkSize.chrs:=MM;

end;


ChunkSize.x := M1;

nBlockAlign := ChunkSize.up;


{Read nBitsPerSample}
nBitsPerSample := ChunkSize.dn;

for I := 17 to fmtSizedo
Read(InFile,MM);


NoDataYet := True;

while NoDataYetdo

begin

begin

{Read tag "data"}
ReadChunkName;


{Read DataSize}
ReadChunkSize;

DataSize := ChunkSize.lint;


if ChunkName<>'data' then

begin

for I := 1 to DataSizedo
{skip over any nondata stuff}
Read(InFile,MM);

end
else
NoDataYet := False;

end;



nDataBytes := DataSize;

{Finally, start reading data for nDataBytes bytes}
if nDataBytes>0 then
DataYet := True;

N:=0;
{first read-in location}
while DataYetdo

begin

ReadOneDataBlock(Ki,Kj);
{get 4 bytes}
nDataBytes := nDataBytes-4;

if nDataBytes<=4 then
DataYet := False;

end;



ScaleData(Ki);

if Ki.WAV.nChannels=2
then
begin
Kj.WAV := Ki.WAV;

ScaleData(Kj);

end;


{Free the file buffers}
CloseFile( InFile );

end
else
begin

InitSpecs;{filedo
es not exist}
InitSignals(Ki);{zero "Ki" array}
InitSignals(Kj);{zero "Kj" array}
end;


end;

{ ReadWAVFile }



{================= Database Operations ====================}

const MaxNumberOfDataBaseItems = 360;

type SignalDirectoryIndex = 0 .. MaxNumberOfDataBaseItems;


VAR DataBaseFile : file of Observation;

LastDataBaseItem : LongInt;
{Current number of database items}
ItemNameS : array[SignalDirectoryIndex] of String[40];


procedure GetDatabaseItem( Kk : Observation;
N : LongInt );

begin

if N<=LastDataBaseItem
then
begin

Seek(DataBaseFile, N);

Read(DataBaseFile, Kk);

end
else
InitSignals(Kk);

end;

{GetDatabaseItem}

procedure PutDatabaseItem( Kk : Observation;
N : LongInt );

begin

if N then

then

if N<=LastDataBaseItem
then
begin

Seek(DataBaseFile, N);

Write(DataBaseFile, Kk);

LastDataBaseItem := LastDataBaseItem+1;

end
else
while LastDataBaseItem<=Ndo

begin

Seek(DataBaseFile, LastDataBaseItem);

Write(DataBaseFile, Kk);

LastDataBaseItem := LastDataBaseItem+1;

end
else
ReportError(1);
{Attempt to read beyond MaxNumberOfDataBaseItems
}
end;

{PutDatabaseItem}

procedure InitDataBase;

begin

LastDataBaseItem := 0;

if FileExists(StandardDataBase)
then

begin

begin

Assign(DataBaseFile,StandardDataBase);

Reset(DataBaseFile);

while not EOF(DataBaseFile)do

begin

GetDataBaseItem(K0R, LastDataBaseItem);

ItemNameS[LastDataBaseItem] := K0R.Name;

LastDataBaseItem := LastDataBaseItem+1;

end;


if EOF(DataBaseFile)
then
if LastDataBaseItem>0
then
LastDataBaseItem := LastDataBaseItem-1;

end;


end;

{InitDataBase}

function FindDataBaseName( Nstg : String ):LongInt;

var ThisOne : LongInt;

begin

ThisOne := 0;

FindDataBaseName := -1;

while ThisOne begin

if Nstg=ItemNameS[ThisOne]
then
begin

FindDataBaseName := ThisOne;

Exit;

end;


ThisOne := ThisOne+1;

end;


end;

{FindDataBaseName}

{======================= Init Unit ========================}
procedure InitLinearSystem;

begin

BaseFileName := '/PROGRA~1/SIGNAL~1/';

StandardOutput := BaseFileName + 'K0.wav';

StandardInput := BaseFileName + 'K0.wav';


StandardDataBase := BaseFileName + 'Radar.sdb';


InitAllSignals;

InitDataBase;

ReadWAVFile(K0R,K0B);

ScaleAllData;

end;

{InitLinearSystem}

begin
{unit initialization code}
InitLinearSystem;

end.
{Unit LinearSystem}
 
接受答案了.
 
后退
顶部