admin管理员组

文章数量:1535374

2024年7月21日发(作者:)

/del/category/

操作 Wave 文件(1): 关于 Wave 文件的基础知识与文件格式

最近准备学习 DirectSound、DirectMusic、DirectShow, 但刚一接触就碰到了关于 Wave 文件的诸多问题, 只好先回头学学 Wave 文件.

Wave 文件的基础知识

经常见到这样的描述: 44100HZ 16bit stereo 或者 22050HZ 8bit mono 等等.

44100HZ 16bit stereo : 每秒钟有 44100 次采样, 采样数据用 16 位(2字节)记录, 双声道(立体声);

22050HZ 8bit mono : 每秒钟有 22050 次采样, 采样数据用 8 位(1字节)记录, 单声道;

当然也可以有 16bit 的单声道或 8bit 的立体声, 等等.

人对频率的识别范围是 20HZ - 20000HZ, 如果每秒钟能对声音做 20000 个采样, 回放时就足可以满足人耳的需求. 所以 22050 的采样

频率是常用的, 44100 已是 CD 音质, 超过 48000 的采样对人耳已经没有意义. 这和电影的每秒 24 帧图片的道理差不多.

每个采样数据记录的是振幅, 采样精度取决于储存空间的大小:

1 字节(也就是8bit) 只能记录 256 个数, 也就是只能对振幅做 256 种识别;

2 字节(也就是16bit) 可以细到 65536 个数, 这已是 CD 标准了;

4 字节(也就是32bit) 能把振幅细化到 4294967296 种可能性, 实在是没必要了.

如果是双声道(stereo), 采样就是双份的, 文件也差不多要大一倍.

这样我们就可以根据一个 wav 文件的大小、采样频率和采样大小估算出一个 wav 文件的长度; 譬如 "Windows XP 启动.wav" 的文件长度

是 424,644 字节, 它是 "22050HZ / 16bit / 立体声" 格式(这可以从其 "属性->摘要" 里看到).

它的每秒的传输速率是 22050*16*2 = 705600(bit), 换算成字节是 705600/8 = 88200(字节);

424644(总字节数) / 88200(每秒字节数) ≈ 4.8145578(秒).

这还不够精确, 在标准的 PCM 格式的 WAVE 文件中还有 44 个字节是采样数据之外的内容, 应该去掉:

(424644-44) / (22050*16*2/8) ≈ 4.8140589(秒). 这比较精确了.

关于声音文件还有一个概念: "位速", 也有叫做比特率、取样率, 譬如上面文件的位速是 705.6kbps 或 705600bps, 其中的 b 是 bit, ps

是每秒的意思; 压缩的音频文件常常用位速来表示, 譬如达到 CD 音质的 mp3 是: 128kbps / 44100HZ.

Wave 文件的文件格式

微软的多媒体文件(wav、avi、tif 等)都有一个 RIFF 头, Wave 文件基本是这个样子:

RIFF 头

fmt 子块

data 子块

Wave 文件的编码方式有好多, 最常用最简单的就是 PCM 编码.

其他编码会包含更多的"块", 但至少会包含上面的块, PCM 编码只包含上面的块.

下面是 PCM 编码的祥表:

"RIFF" 标识

ckid 4

RIFF 头

cksize 4

文件大小; 这个大小不包括 ckid 和 cksize 本身, 下面的子块大小也是这样

fccType 4

类型, 这里是 "WAVE" 标识

ckid 4

"fmt " 标识

cksize 4

块大小; 对 PCM 编码这里是 16, 其他编码也不小于 16

wFormatTag 2

编码格式; 1 表示是 PCM 编码

nChannels 2

声道数; 1 是单声道、2 是立体声

24

fmt 子块

nSamplesPerSec 4

采样频率(每秒的样本数); 譬如 44100

nAvgBytesPerSec 4

传输速率 = 采样频率 * 每次采样大小, 单位是字节

每次采样的大小 = 采样精度 * 声道数 / 8(因单位是字节所以要/8);

nBlockAlign 2

这也是字节对齐的最小单位, 譬如 16bit 立体声在这里的值是 4 字节

wBitsPerSample

2

采样精度; 譬如 16bit 在这里的值就是 16

ckid 4

"data" 标识

data 子块

cksize 4

块大小

?

采样数据

?

双声道数据排列: 左右左右...; 8bit: 0-255, 16bit: -32768-32767

其他编码可能会包含的块有: 事实块(Fact)、提示块(Cue)、标签块(Label)、注释块(Note)、标签文本块(Labeled Text)、采样器块(Sampl

er)、乐器块(Instrument)、列表块(List)等等, 如果有 List 块, 它还会包含更多子块.

接下来要存取、播放、录制, 说来容易, 操作起来都挺麻烦.

操作 Wave 文件(2): 判断一个文件是否是 Wave 文件

Wave 文件的前 12 个字节可以这样描述:

TRiff = record

ckId : DWORD; {'RIFF'}

ckSize : DWORD; {文件大小, 不包括前 8 个字节}

fccType : DWORD; {'WAVE'}

end;

我们读出文件的前 12 个字节进行判断, 就基本可以确认它是不是 Wave 文件.

uses MMSystem, IOUtils; {这里准备用 ad 方便地建立文件流}

procedure eate(Sender: TObject);

var

riff: record ckId, ckSize, fccType: DWORD; end; {可以同时定义结构并声明结构变量}

begin

with ad('C:WINDOWSMediaWindows XP 启动.wav') do

begin

Read(riff, SizeOf(riff));

Free;

end;

if ( = FOURCC_RIFF) and (e = mmioStringToFOURCC('WAVE',0)) then

ShowMessageFmt('这是个 Wave 文件, 其大小是 %d 字节', [ + 8]);

end;

还是把它写成一个函数吧, 最好也别再引用 MMSystem 单元.

{如果是 Wave 文件则返回文件大小, 不是则返回 0}

function IsWave(FilePath: string): Integer;

function mmioFOURCC(Chr0,Chr1,Chr2,Chr3: AnsiChar): DWORD;

begin

Result := DWORD(Chr0) + DWORD(Chr1) shl 8 + DWORD(Chr2) shl 16 + DWORD(Chr3) shl 24;

end;

var

riff: record ckId, ckSize, fccType: DWORD; end;

begin

Result := 0;

with (FilePath, fmOpenRead) do begin

Read(riff, SizeOf(riff));

Free;

end;

if ( = mmioFOURCC('R', 'I', 'F', 'F')) and

(e = mmioFOURCC('W', 'A', 'V', 'E')) then

Result := + 8;

end;

依次道理, 也可以判断一个 RIFF 文件具体是什么格式.

{返回 RIFF 文件格式的函数, 如果不是 RIFF 文件, 则返回 'noneRIFF'}

function GetRiffType(FilePath: string): String;

function mmioFOURCC(Chr0,Chr1,Chr2,Chr3: AnsiChar): DWORD;

begin

Result := DWORD(Chr0) + DWORD(Chr1) shl 8 + DWORD(Chr2) shl 16 + DWORD(Chr3) shl 24;

end;

var

riff: record ckId, ckSize, fccType: DWORD; end;

type

TChars = array[0..3] of AnsiChar; {用于类型转换}

begin

Result := 'noneRIFF';

with (FilePath, fmOpenRead) do begin

Read(riff, SizeOf(riff));

Free;

end;

if ( = mmioFOURCC('R', 'I', 'F', 'F')) then Result := TChars(e);

end;

//测试:

begin

ShowMessage(GetRiffType('C:WINDOWSMediaWindows XP 启动.wav')); {WAVE}

ShowMessage(GetRiffType(

'C:')); {AVI }

ShowMessage(GetRiffType('C:')); {noneRIFF}

end;

操作 Wave 文件(3): 接触 mmio 系列函数

mmio 系列函数用于 Wave 等多媒体文件的 I/O 操作, 相关函数有:

mmioOpen

mmioClose

mmioRead

mmioWrite

mmioFlush

mmioSeek

mmioRename

mmioGetInfo

mmioSetInfo

mmioCreateChunk

mmioAscend

mmioDescend

mmioAdvance

mmioSetBuffer

mmioStringToFOURCC

mmioSendMessage

mmioInstallIOProc

mmio 系列函数比一般的 I/O 函数更适合操作 RIFF 格式的多媒体文件, 主要是能更方便地操作 RIFF 的文件块, 官方还说它们更优化.

和其他 I/O 函数一样, 它们也是要 Open 获取句柄, 然后读写, 最后关闭; 但它们文件句柄和其他 I/O 函数的句柄并不兼容, 不过部分函数

(上面前 7 个)也可以用于一般文件的操作.

就先操作个一般文件吧.

uses MMSystem;

procedure eate(Sender: TObject);

const

FilePath = 'C:';

var

hFile: HMMIO;

str: RawByteString;

begin

hFile := mmioOpen(PChar(FilePath), {要打开的文件}

nil, {接受 TMMIOInfo 结构信息的指针, 暂时没用到}

MMIO_CREATE or MMIO_READWRITE {打开选项; 这是建立并以读写权限打开}

);

mmioWrite(hFile, 'Delphi', 6); {写入 6 个字符}

mmioSeek(hFile, 0, SEEK_SET); {把读写指针移动到文件头}

SetLength(str, 6);

mmioRead(hFile, PAnsiChar(str), 6); {读出 6 个字符}

ShowMessage(str); {Delphi}

mmioClose(hFile, 0); {关闭文件; 第二个参数还可以是 MMIO_FHOPEN, 另有它用}

{最后再删除这个文件, 既然已删除就无需 Close 了}

mmioOpen(PChar(FilePath), nil, MMIO_DELETE);

end;

操作 Wave 文件(4): 获取 Wave 文件主块与子块的信息

有两个相关的结构体: TMMIOInfo、TMMCKInfo.

TMMIOInfo 是多媒体文件打开后的状态信息, mmioOpen 函数的第二个参数就是这个结构的指针.

现在先用到了 TMMCKInfo, 这是文件内部 "块" 的信息, 构成如下:

TMMCKInfo = record

ckid: FOURCC; {块标识}

cksize: DWORD; {块大小}

fccType: FOURCC; {格式类型标识}

dwDataOffset: DWORD; {偏移地址}

dwFlags: DWORD; {附加信息}

end;

查找 "块" 需要通过 mmioDescend、mmioAscend 两个函数.

mmioAscend 是从子块跳出;

mmioDescend 是进入到子块; 进入子块是需要指定子块的 ckid 和父块信息;

mmioDescend 也用来查找主块(RIFF), 此时需要很少的信息就可以找到主块.

测试代码:

unit Unit1;

interface

uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs, StdCtrls;

type

TForm1 = class(TForm)

Button1: TButton;

Button2: TButton;

procedure Button1Click(Sender: TObject);

procedure Button2Click(Sender: TObject);

end;

var

Form1: TForm1;

implementation

{$R *.dfm}

uses MMSystem;

const FilePath = 'C:WINDOWSMediaWindows XP 启动.wav';

//获取 RIFF 块的信息

procedure 1Click(Sender: TObject);

var

hFile: HMMIO;

ckiRIFF: TMMCKInfo;

begin

//清空 ckiRIFF 结构体; 有些函数使用前要求必须清空, 即使不要求也还是清空的好.

FillChar(ckiRIFF, SizeOf(TMMCKInfo), 0); {局部变量在清空前有垃圾数据}

//打开文件, 获取句柄

hFile := mmioOpen(PChar(FilePath), nil, MMIO_READ);

//获取 RIFF 块的信息

mmioDescend(hFile, {文件句柄}

@ckiRIFF, {块信息结构的指针, 用于获取块的信息}

nil, {这父块的结构信息, RIFF 没有父块, 无需指定}

MMIO_FINDRIFF {如果是查询子块这里的标志是 MMIO_FINDCHUNK}

); {返回 0 表示查找成功, 这里忽略了验证}

//以下是查证获取到的信息

ShowMessageFmt('%d, %d, %d, %d, %d', [, , e,

Offset, s ]); {1179011410, 424636, 1163280727, 8, 0}

if = FOURCC_RIFF then ShowMessage('是 RIFF');

if e = mmioStringToFOURCC('WAVE',0) then ShowMessage('是 WAVE');

//关闭

mmioClose(hFile, 0);

end;

//获取子块的信息

procedure 2Click(Sender: TObject);

var

hFile: HMMIO;

ckiRIFF,ckiSub: TMMCKInfo;

n: Integer;

begin

//清空准备接受信息的结构

FillChar(ckiRIFF, SizeOf(TMMCKInfo), 0);

FillChar(ckiSub, SizeOf(TMMCKInfo), 0);

hFile := mmioOpen(PChar(FilePath), nil, MMIO_READ);

//先获取主块(RIFF)信息

mmioDescend(hFile, @ckiRIFF, nil, MMIO_FINDRIFF);

//获取 fmt 子块信息

:= mmioStringToFOURCC('fmt', 0);

if mmioDescend(hFile, @ckiSub, @ckiRIFF, MMIO_FINDCHUNK) = MMSYSERR_NOERROR then

begin

ShowMessageFmt('%d, %d, %d, %d, %d', [, , e,

Offset, s]);

end;

//如果继续查找需要跳出子块; 下面将从偏移地址 20 跳到 36 处

mmioAscend(hFile, @ckiSub, 0); {其第三个参数一直是 0, 是备用参数}

//获取 data 子块信息

:= mmioStringToFOURCC('data', 0);

if mmioDescend(hFile, @ckiSub, @ckiRIFF, MMIO_FINDCHUNK) = MMSYSERR_NOERROR then

begin

ShowMessageFmt('%d, %d, %d, %d, %d', [, , e,

Offset, s]);

end;

mmioClose(hFile, 0);

end;

end.

操作 Wave 文件(5): 获取 Wave 文件的格式信息

装载格式信息的结构有:

TWaveFormat = packed record

wFormatTag: Word;

nChannels: Word;

nSamplesPerSec: DWORD;

nAvgBytesPerSec: DWORD;

nBlockAlign: Word;

end;

TPCMWaveFormat = record

wf: TWaveFormat;

wBitsPerSample: Word;

end;

TWaveFormatEx = packed record

wFormatTag: Word; {格式类型; 主要使用的是 WAVE_FORMAT_PCM}

nChannels: Word; {声道数; 1 是单声道、2 是立体声}

nSamplesPerSec: DWORD; {采样频率}

nAvgBytesPerSec: DWORD; {传输速率}

nBlockAlign: Word; {每次采样的大小}

wBitsPerSample: Word; {采样精度}

cbSize: Word; {附加数据的大小; PCM 编码的文件没这个字段}

end;

能看出它们是依次递增一个字段, 并且也是 Wave 文件的一个构成部分; 现在要做的就是从 Wave 文件中把它们取出来.

获取函数及测试代码:

unit Unit1;

interface

uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs, StdCtrls;

type

TForm1 = class(TForm)

Memo1: TMemo;

Button1: TButton;

procedure Button1Click(Sender: TObject);

end;

var

Form1: TForm1;

implementation

{$R *.dfm}

uses MMSystem;

//获取 Wave 中格式数据的函数; 常用的是 TWaveFormatEx, 但 PCM 缺它一个字段

function GetWaveFmt(FilePath: string; var fmt: TWaveFormatEx): Boolean;

var

hFile: HMMIO;

ckiRIFF,ckiFmt: TMMCKInfo;

begin

Result := False;

hFile := mmioOpen(PChar(FilePath), nil, MMIO_READ);

if hFile = 0 then Exit;

ZeroMemory(@ckiRIFF, SizeOf(TMMCKInfo));

ZeroMemory(@ckiFmt, SizeOf(TMMCKInfo));

ZeroMemory(@fmt, SizeOf(TWaveFormatEx)); {也先清空准备接受数据的结构体}

:= mmioStringToFOURCC('fmt', 0); {给查找格式块准备}

//先获取主块的信息

mmioDescend(hFile, @ckiRIFF, nil, MMIO_FINDRIFF);

//再获取 fmt 块的信息后, 指针将自动指向格式数据起点; 然后读出格式数据

if ( = FOURCC_RIFF) and

(e = mmioStringToFOURCC('WAVE',0)) and

(mmioDescend(hFile, @ckiFmt, @ckiRIFF, MMIO_FINDCHUNK) = MMSYSERR_NOERROR) then

Result := (mmioRead(hFile, @fmt, ) = );

end;

//调用测试

procedure 1Click(Sender: TObject);

const

FilePath = 'C:WINDOWSMediaWindows XP 启动.wav';

var

WaveFormat: TWaveFormatEx;

begin

if GetWaveFmt(FilePath, WaveFormat) then with do

begin

Clear;

Add(Format('wFormatTag: %d', [tTag]));

Add(Format('nChannels: %d', [els]));

Add(Format('nSamplesPerSec: %d', [esPerSec]));

Add(Format('nAvgBytesPerSec: %d', [tesPerSec]));

Add(Format('nBlockAlign: %d', [Align]));

Add(Format('wBitsPerSample: %d', [erSample]));

Add(Format('cbSize: %d', []));

end;

{ 显示结果:

wFormatTag: 1

nChannels: 2

nSamplesPerSec: 22050

nAvgBytesPerSec: 88200

nBlockAlign: 4

wBitsPerSample: 16

cbSize: 0

}

end;

end.

操作 Wave 文件(6): 获取 Wave 文件的波形数据

读取函数及测试代码:

unit Unit1;

interface

uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs, StdCtrls;

type

TForm1 = class(TForm)

Button1: TButton;

procedure Button1Click(Sender: TObject);

end;

var

Form1: TForm1;

implementation

{$R *.dfm}

uses MMSystem;

function GetWaveData(FilePath: string; var stream: TMemoryStream): Boolean;

var

hFile: HMMIO;

ckiRIFF,ckiData: TMMCKInfo;

begin

Result := False;

hFile := mmioOpen(PChar(FilePath), nil, MMIO_READ);

if hFile = 0 then Exit;

ZeroMemory(@ckiRIFF, SizeOf(TMMCKInfo));

ZeroMemory(@ckiData, SizeOf(TMMCKInfo));

:= mmioStringToFOURCC('data', 0);

//先获取主块的信息

mmioDescend(hFile, @ckiRIFF, nil, MMIO_FINDRIFF);

//再获取 data 块的信息后, 指针将自动指向 data 数据的起点; 然后读出数据

if ( = FOURCC_RIFF) and

(e = mmioStringToFOURCC('WAVE',0)) and

(mmioDescend(hFile, @ckiData, @ckiRIFF, MMIO_FINDCHUNK) = MMSYSERR_NOERROR) then

begin

:= ;

Result := (mmioRead(hFile, , ) = );

end;

mmioClose(hFile, 0);

end;

//调用测试

procedure 1Click(Sender: TObject);

const

FilePath = 'C:WINDOWSMediaWindows XP 启动.wav';

var

stream: TMemoryStream;

begin

stream := ;

if GetWaveData(FilePath, stream) then

ShowMessageFmt('读出的数据大小是: %d', []); {424600}

;

end;

end.

操作 Wave 文件(7): 建立一个空的 Wave 文件(三种方法)

unit Unit1;

interface

uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs;

type

TForm1 = class(TForm)

procedure FormCreate(Sender: TObject);

end;

var

Form1: TForm1;

implementation

{$R *.dfm}

uses MMSystem;

//chan: 1 单声道、2 立体声;

//freq: 频率, 取值: 11025, 22050, 44100

//bit : 每个样本的大小, 取值 8、16

function CreateWav1(chan, freq, bit: Word; const FilePath: string): Boolean;

var

h: HMMIO;

ckiRiff, ckiFmt, ckiData: TMMCKInfo;

fmt: TPCMWaveFormat;

begin

//此函数是使用 mmioCreateChunk 函数来分别建立 Wave 文件的每个块.

{初识化相关结构}

ZeroMemory(@ckiRiff, SizeOf(TMMCKInfo));

:= 36; {mmioCreateChunk 函数会自动写上 ckid, 但其 cksize 需要手动给}

e := mmioStringToFOURCC('WAVE', 0);

ZeroMemory(@ckiFmt, SizeOf(TMMCKInfo));

:= mmioStringToFOURCC('fmt', 0);

ZeroMemory(@ckiData, SizeOf(TMMCKInfo));

:= mmioStringToFOURCC('data', 0);

{指定 Wave 格式}

tTag := WAVE_FORMAT_PCM;

els := chan;

esPerSec := freq;

tesPerSec := freq * chan * bit div 8;

Align := chan * bit div 8;

erSample := bit;

h := mmioOpen(PChar(FilePath), nil, MMIO_CREATE or MMIO_WRITE);

if h = 0 then Exit(False);

{分别建立 RIFF、fmt、data 块}

if (mmioCreateChunk(h, @ckiRiff, MMIO_CREATERIFF) = MMSYSERR_NOERROR) and

(mmioCreateChunk(h, @ckiFmt, 0) = MMSYSERR_NOERROR) and

(mmioWrite(h, PAnsiChar(@fmt), SizeOf(TPCMWaveFormat)) = SizeOf(TPCMWaveFormat)) and

(mmioAscend(h, @ckiFmt, 0) = MMSYSERR_NOERROR) and

(mmioCreateChunk(h, @ckiData, 0) = MMSYSERR_NOERROR) then Result := True;

mmioClose(h, 0);

end;

//把 PCM 编码的 WAVE

文件的前 44 个字节看成一个结构来操作:

function CreateWav2(chan, freq, bit: Word; const FilePath: string): Boolean;

type

TWaveHeader = record

Riff_ckid : DWORD;

Riff_cksize : DWORD;

Riff_fccType : DWORD;

fmt_ckid : DWORD;

fmt_cksize : DWORD;

wFormatTag : Word;

nChannels : Word;

nSamplesPerSec : DWORD;

nAvgBytesPerSec: DWORD;

nBlockAlign : Word;

wBitsPerSample : Word;

data_ckid : DWORD;

data_cksize : DWORD;

end;

var

wh: TWaveHeader;

hFile: Integer;

begin

_ckid := FOURCC_RIFF;

_cksize := 36;

_fccType := mmioStringToFOURCC('WAVE', 0);

_ckid := mmioStringToFOURCC('fmt', 0);

_cksize := 16;

tTag := WAVE_FORMAT_PCM;

els := chan;

esPerSec := freq;

tesPerSec := freq * chan * bit div 8;

Align := chan * bit div 8;

erSample := bit;

_ckid := mmioStringToFOURCC('data', 0);

_cksize := 0;

hFile := FileCreate(FilePath);

Result := (FileWrite(hFile, wh, SizeOf(TWaveHeader)) <> -1);

FileClose(hFile);

end;

//同上, 只是改用流来写文件

function CreateWav3(chan, freq, bit: Word; const FilePath: string): Boolean;

type

TWaveHeader = record

Riff_ckid : DWORD;

Riff_cksize : DWORD;

Riff_fccType : DWORD;

fmt_ckid : DWORD;

fmt_cksize : DWORD;

wFormatTag : Word;

nChannels : Word;

nSamplesPerSec : DWORD;

nAvgBytesPerSec: DWORD;

nBlockAlign : Word;

wBitsPerSample : Word;

data_ckid : DWORD;

data_cksize : DWORD;

end;

var

wh: TWaveHeader;

begin

_ckid := FOURCC_RIFF;

_cksize := 36;

_fccType := mmioStringToFOURCC('WAVE', 0);

_ckid := mmioStringToFOURCC('fmt', 0);

_cksize := 16;

tTag := WAVE_FORMAT_PCM;

els := chan;

esPerSec := freq;

tesPerSec := freq * chan * bit div 8;

Align := chan * bit

div 8;

erSample := bit;

_ckid := mmioStringToFOURCC('data', 0);

_cksize := 0;

with (FilePath, fmCreate) do begin

Result := (Write(wh, SizeOf(TWaveHeader)) = SizeOf(TWaveHeader));

Free;

end;

end;

procedure eate(Sender: TObject);

begin

CreateWav1(1, 11025, 8, 'C:');

CreateWav2(2, 22050, 16, 'C:');

CreateWav3(2, 44100, 16, 'C:');

end;

end.

操作 Wave 文件(8): 使用 TMediaPlayer 录制 wav 文件

TMediaPlayer 录音是基于一个已存在的 wav 文件, 上次建立空白 wav 的函数可派上用场了.

TMediaPlayer 的功能是基于 MCI 的, 都是该淘汰的东西了, 只是简单了解下.接下来还要学习用 系列函数录音、用 DirectSound

录音.

unit Unit1;

interface

uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs, MPlayer, StdCtrls;

type

TForm1 = class(TForm)

MediaPlayer1: TMediaPlayer;

Button1: TButton;

Button2: TButton;

procedure FormCreate(Sender: TObject);

procedure Button1Click(Sender: TObject);

procedure Button2Click(Sender: TObject);

end;

var

Form1: TForm1;

implementation

{$R *.dfm}

uses MMSystem;

//建立一个空白 Wave 文件的函数

function CreateWav(chan, freq, bit: Word; const FilePath: string): Boolean;

var

h: HMMIO;

ckiRiff, ckiFmt, ckiData: TMMCKInfo;

fmt: TPCMWaveFormat;

begin

ZeroMemory(@ckiRiff, SizeOf(TMMCKInfo));

:= 36;

e := mmioStringToFOURCC('WAVE', 0);

ZeroMemory(@ckiFmt, SizeOf(TMMCKInfo));

:= mmioStringToFOURCC('fmt', 0);

ZeroMemory(@ckiData, SizeOf(TMMCKInfo));

:= mmioStringToFOURCC('data', 0);

tTag := WAVE_FORMAT_PCM;

els := chan;

esPerSec := freq;

tesPerSec := freq * chan * bit div 8;

Align := chan * bit div 8;

erSample := bit;

h := mmioOpen(PChar(FilePath), nil, MMIO_CREATE or MMIO_WRITE);

if h = 0 then Exit(False);

if (mmioCreateChunk(h, @ckiRiff, MMIO_CREATERIFF) = MMSYSERR_NOERROR) and

(mmioCreateChunk(h, @ckiFmt, 0) = MMSYSERR_NOERROR) and

(mmioWrite(h, PAnsiChar(@fmt), SizeOf(TPCMWaveFormat)) = SizeOf(TPCMWaveFormat)) and

(mmioAscend(h, @ckiFmt, 0) = MMSYSERR_NOERROR) and

(mmioCreateChunk(h, @ckiData, 0) = MMSYSERR_NOERROR) then Result := True;

mmioClose(h, 0);

end;

//文件路径

const path =

'C:';

//开始录音

procedure 1Click(Sender: TObject);

begin

CreateWav(2, 22050, 16, path);

me := path;

;

ecording;

d := True;

end;

//停止录音并播放

procedure 2Click(Sender: TObject);

begin

;

;

end;

procedure eate(Sender: TObject);

begin

e := False;

d := FileExists(path);

end;

end.

操作 Wave 文件(9): 使用 函数播放 wav 文件

下面是使用低级音频函数播放 wav 的两个方法, 对这个感兴趣的人恐怕很少, 免注释了.

使用窗口接受音频输出设备的消息:

unit Unit1;

interface

uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs, StdCtrls;

type

TForm1 = class(TForm)

Button1: TButton;

procedure Button1Click(Sender: TObject);

private

protected

procedure WndProc(var Message: TMessage); override;

public

end;

var

Form1: TForm1;

implementation

{$R *.dfm}

uses MMSystem;

//获取文件格式和波形数据的函数

function GetWaveFmtData(path: string; var fmt: TWaveFormatEx; var buf: TBytes): Boolean;

var

hFile: HMMIO;

ckiRIFF,ckiFmt,ckiData: TMMCKInfo;

begin

Result := False;

hFile := mmioOpen(PChar(path), nil, MMIO_READ);

if hFile = 0 then Exit;

ZeroMemory(@ckiRIFF, SizeOf(TMMCKInfo));

ZeroMemory(@ckiFmt, SizeOf(TMMCKInfo));

ZeroMemory(@ckiData, SizeOf(TMMCKInfo));

e := mmioStringToFOURCC('WAVE', 0);

:= mmioStringToFOURCC('fmt', 0);

:= mmioStringToFOURCC('data', 0);

ZeroMemory(@fmt, SizeOf(TWaveFormatEx));

mmioDescend(hFile, @ckiRIFF, nil, MMIO_FINDRIFF);

if ( = FOURCC_RIFF) and (e = mmioStringToFOURCC('WAVE',0)) and

(mmioDescend(hFile, @ckiFmt, @ckiRIFF, MMIO_FINDCHUNK) = MMSYSERR_NOERROR) and

(mmioRead(hFile, @fmt, ) = ) and

(mmioAscend(hFile, @ckiFmt, 0) = MMSYSERR_NOERROR) and

(mmioDescend(hFile, @ckiData, @ckiRIFF, MMIO_FINDCHUNK) = MMSYSERR_NOERROR) then

begin

SetLength(buf, );

Result := (mmioRead(hFile, PAnsiChar(buf), ) = );

end;

mmioClose(hFile, 0);

end;

//------------------------------------------------------------------------------

var

wh: TWaveHdr;

hOut: HWAVEOUT;

fmt: TWaveFormatEx;

buf: TBytes;

procedure 1Click(Sender: TObject);

const

path = 'C:WINDOWSMediaWindows XP 启动.wav';

begin

GetWaveFmtData(path, fmt, buf);

:= PAnsiChar(buf);

erLength := Length(buf);

sRecorded := 0;

:= 0;

s := 0;

s := 1;

:= nil;

ed := 0;

waveOutOpen(@hOut, WAVE_MAPPER, @fmt, Handle, 0, CALLBACK_WINDOW);

waveOutPrepareHeader(hOut, @wh, SizeOf(TWaveHdr));

waveOutWrite(hOut, @wh, SizeOf(TWaveHdr));

end;

procedure c(var Message: TMessage);

begin

inherited;

case of

MM_WOM_OPEN: ;

MM_WOM_CLOSE: ;

MM_WOM_DONE: begin

waveOutUnprepareHeader(hOut, @wh, SizeOf(TWaveHdr));

waveOutClose(hOut);

end;

end;

end;

end.

使用回调函数:

unit Unit1;

interface

uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs, StdCtrls;

type

TForm1 = class(TForm)

Button1: TButton;

Button2: TButton;

Button3: TButton;

procedure Button1Click(Sender: TObject);

procedure Button2Click(Sender: TObject);

procedure Button3Click(Sender: TObject);

end;

var

Form1: TForm1;

implementation

{$R *.dfm}

uses MMSystem;

function GetWaveFmtData(path: string; var fmt: TWaveFormatEx; var buf: TBytes): Boolean;

var

hFile: HMMIO;

ckiRIFF,ckiFmt,ckiData: TMMCKInfo;

begin

Result := False;

hFile := mmioOpen(PChar(path), nil, MMIO_READ);

if hFile = 0 then Exit;

ZeroMemory(@ckiRIFF, SizeOf(TMMCKInfo));

ZeroMemory(@ckiFmt, SizeOf(TMMCKInfo));

ZeroMemory(@ckiData, SizeOf(TMMCKInfo));

e := mmioStringToFOURCC('WAVE', 0);

:= mmioStringToFOURCC('fmt', 0);

:= mmioStringToFOURCC('data', 0);

ZeroMemory(@fmt, SizeOf(TWaveFormatEx));

mmioDescend(hFile, @ckiRIFF, nil, MMIO_FINDRIFF);

if ( = FOURCC_RIFF) and (e = mmioStringToFOURCC('WAVE',0)) and

(mmioDescend(hFile, @ckiFmt, @ckiRIFF, MMIO_FINDCHUNK) = MMSYSERR_NOERROR) and

(mmioRead(hFile, @fmt, ) = ) and

(mmioAscend(hFile, @ckiFmt, 0) = MMSYSERR_NOERROR) and

(mmioDescend(hFile, @ckiData, @ckiRIFF, MMIO_FINDCHUNK) = MMSYSERR_NOERROR) then

begin

SetLength(buf, );

Result := (mmioRead(hFile, PAnsiChar(buf), ) = );

end;

mmioClose(hFile, 0);

end;

//------------------------------------------------------------------------------

var

wh: TWaveHdr;

hOut: HWAVEOUT;

fmt: TWaveFormatEx;

buf: TBytes;

procedure WaveProc(hWave: HWAVE; uMsg, dwInstance, dwParam1, dwParam2: DWORD);

stdcall;

begin

case uMsg of

MM_WOM_OPEN: ;

MM_WOM_CLOSE: ;

MM_WOM_DONE: begin

waveOutUnprepareHeader(hWave, PWaveHdr(dwParam1), SizeOf(TWaveHdr));

waveOutClose(hWave);

end;

end;

end;

procedure 1Click(Sender: TObject);

const

path = 'C:WINDOWSMediaWindows XP 启动.wav';

begin

GetWaveFmtData(path, fmt, buf);

:= PAnsiChar(buf);

erLength := Length(buf);

sRecorded := 0;

:= 0;

s := 0;

s := 1;

:= nil;

ed := 0;

waveOutOpen(@hOut, WAVE_MAPPER, @fmt, DWORD(@WaveProc), 0, CALLBACK_FUNCTION);

waveOutPrepareHeader(hOut, @wh, SizeOf(TWaveHdr));

waveOutWrite(hOut, @wh, SizeOf(TWaveHdr));

end;

//暂停

procedure 2Click(Sender: TObject);

begin

waveOutPause(hOut);

end;

//继续

procedure 3Click(Sender: TObject);

begin

waveOutRestart(hOut);

end;

end.

操作 Wave 文件(10): 输入输出设备与格式支持

unit Unit1;

interface

uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs, StdCtrls;

type

TForm1 = class(TForm)

ListBox1: TListBox;

ListBox2: TListBox;

Button1: TButton;

Button2: TButton;

procedure Button1Click(Sender: TObject);

procedure Button2Click(Sender: TObject);

end;

var

Form1: TForm1;

implementation

{$R *.dfm}

uses MMSystem;

//设备列表; 指定设备时经常使用 WAVE_MAPPER 参数, 这样会自动选用合适的设备.

procedure 1Click(Sender: TObject);

var

i: Integer;

waveOutCaps: TWaveOutCaps;

waveInCaps: TWaveInCaps;

begin

('音频输出设备列表:');

for i := 0 to waveOutGetNumDevs do

begin

ZeroMemory(@waveOutCaps, SizeOf(TWaveOutCaps));

waveOutGetDevCaps(i, @waveOutCaps, SizeOf(TWaveOutCaps));

(e);

end;

('音频输入设备列表:');

for i := 0 to waveInGetNumDevs do

begin

ZeroMemory(@waveInCaps, SizeOf(TWaveInCaps));

waveOutGetDevCaps(i, @waveInCaps, SizeOf(TWaveInCaps));

(e);

end;

end;

//判断是否支持指定的 Wave 格式

procedure 2Click(Sender: TObject);

var

fmt: TPCMWaveFormat;

begin

tTag := WAVE_FORMAT_PCM;

els := 2;

esPerSec := 22050;

tesPerSec := 88200;

Align := 4;

erSample := 16;

if waveOutOpen(nil, 0, PWaveFormatEx(@fmt), 0, 0, WAVE_FORMAT_QUERY) = 0 then

ShowMessage('第一个输出设备支持此格式');

if waveInOpen(nil, 0, PWaveFormatEx(@fmt), 0, 0, WAVE_FORMAT_QUERY) = 0 then

ShowMessage('第一个输入设备支持此格式');

end;

end.

有把格式支持的判断写成函数的, 如:

function IsFormatSupported(fmt: Pointer; DeviceId: DWORD): Boolean;

begin

Result := (waveOutOpen(nil, DeviceId, PWaveFormatEx(fmt), 0, 0, WAVE_FORMAT_QUERY) = 0);

end;

操作 Wave 文件(11): 使用 函数录制 wav 文件

使用窗口接受音频设备发出的消息:

unit Unit1;

interface

uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs, StdCtrls;

type

TForm1 = class(TForm)

Button1: TButton;

Button2: TButton;

Button3: TButton;

procedure FormCreate(Sender: TObject);

procedure Button1Click(Sender: TObject);

procedure Button2Click(Sender: TObject);

procedure Button3Click(Sender: TObject);

protected

procedure WndProc(var m: TMessage); override;

end;

var

Form1: TForm1;

implementation

{$R *.dfm}

uses MMSystem;

var

whIn1,whIn2, whOut: TWaveHdr;

hWaveIn,hWaveOut: HWAVE;

fmt: TWaveFormatEx;

buf1,buf2,SaveBuf: TBytes;

procedure eate(Sender: TObject);

begin

n := '开始录音';

n := '停止录音';

n := '播放录音';

end;

//开始录音

procedure 1Click(Sender: TObject);

begin

{指定要录制的格式}

tTag := WAVE_FORMAT_PCM;

els := 2;

esPerSec := 22050;

tesPerSec := 88200;

Align := 4;

erSample := 16;

:= 0;

SaveBuf := nil; {清除已录制的内容}

if waveInOpen(@hWaveIn, WAVE_MAPPER, @fmt, Handle, 0, CALLBACK_WINDOW) = 0 then

begin

SetLength(buf1, 1024*8);

SetLength(buf2, 1024*8);

:= PAnsiChar(buf1);

erLength := Length(buf1);

sRecorded := 0;

:= 0;

s :=

0;

s := 0;

:= nil;

ed := 0;

:= PAnsiChar(buf2);

erLength := Length(buf2);

sRecorded := 0;

:= 0;

s := 0;

s := 0;

:= nil;

ed := 0;

waveInPrepareHeader(hWaveIn, @whIn1, SizeOf(TWaveHdr));

waveInPrepareHeader(hWaveIn, @whIn2, SizeOf(TWaveHdr));

waveInAddBuffer(hWaveIn, @whIn1, SizeOf(TWaveHdr));

waveInAddBuffer(hWaveIn, @whIn2, SizeOf(TWaveHdr));

waveInStart(hWaveIn);

end;

end;

//停止录音

procedure 2Click(Sender: TObject);

begin

waveInStop(hWaveIn);

waveInUnprepareHeader(hWaveIn, @whIn1, SizeOf(TWaveHdr));

waveInUnprepareHeader(hWaveIn, @whIn2, SizeOf(TWaveHdr));

waveInClose(hWaveIn);

end;

//播放录音

procedure 3Click(Sender: TObject);

begin

:= PAnsiChar(SaveBuf);

erLength := Length(SaveBuf);

sRecorded := 0;

:= 0;

s := 0;

s := 1;

:= nil;

ed := 0;

waveOutOpen(@hWaveOut, WAVE_MAPPER, @fmt, Handle, 0, CALLBACK_WINDOW);

waveOutPrepareHeader(hWaveOut, @whOut, SizeOf(TWaveHdr));

waveOutWrite(hWaveOut, @whOut, SizeOf(TWaveHdr));

end;

procedure c(var m: TMessage);

var

ordLen: Integer;

begin

inherited;

case of

{处理录音消息}

MM_WIM_OPEN: ; {此消息只携带了设备句柄}

MM_WIM_CLOSE: ; {此消息只携带了设备句柄}

MM_WIM_DATA: begin {此消息携带了设备句柄和 WaveHdr 指针(LParam)}

{保存录制的数据}

ordLen := Length(SaveBuf);

SetLength(SaveBuf, ordLen + PWaveHdr().dwBytesRecorded);

CopyMemory(Ptr(DWORD(SaveBuf)+ordLen), PWaveHdr().lpData, PWaveHdr().dwBytesRecorde

d);

{继续录制}

waveInAddBuffer(hWaveIn, PWaveHdr(), SizeOf(TWaveHdr));

end;

{处理播放消息}

MM_WOM_OPEN: ; {此消息只携带了设备句柄}

MM_WOM_CLOSE: ; {此消息只携带了设备句柄}

MM_WOM_DONE: begin {此消息携带了设备句柄和 WaveHdr 指针(LParam)}

waveOutUnprepareHeader(hWaveOut, PWaveHdr(), SizeOf(TWaveHdr));

waveOutClose(hWaveOut);

end;

end;

end;

end.

使用回调函数处理音频设备发出的消息:

unit Unit1;

interface

uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs, StdCtrls;

type

TForm1 = class(TForm)

Button1: TButton;

Button2: TButton;

Button3: TButton;

procedure FormCreate(Sender: TObject);

procedure Button1Click(Sender: TObject);

procedure Button2Click(Sender: TObject);

procedure Button3Click(Sender: TObject);

end;

var

Form1: TForm1;

implementation

{$R *.dfm}

uses MMSystem;

var

whIn1,whIn2, whOut: TWaveHdr;

hWaveIn,hWaveOut: HWAVE;

fmt: TWaveFormatEx;

buf1,buf2,SaveBuf: TBytes;

//回调函数; 容易出错的是: 系统回调函数中不能使用本地变量

procedure WaveProc(hWave: HWAVE; uMsg, dwInstance, dwParam1, dwParam2: DWORD); stdcall;

var

ordLen: Integer;

begin

case uMsg of

{处理录音消息}

MM_WIM_OPEN: ; {此消息只携带了设备句柄}

MM_WIM_CLOSE: ; {此消息只携带了设备句柄}

MM_WIM_DATA: begin {此消息携带了设备句柄和 WaveHdr 指针(dwParam1)}

{保存录制的数据}

ordLen := Length(SaveBuf);

SetLength(SaveBuf, ordLen + PWaveHdr(dwParam1).dwBytesRecorded);

CopyMemory(Ptr(DWORD(SaveBuf)+ordLen), PWaveHdr(dwParam1).lpData, PWaveHdr(dwParam1).dwBytesRecorde

d);

{继续录制}

waveInAddBuffer(hWave, PWaveHdr(dwParam1), SizeOf(TWaveHdr));

end;

{处理播放消息}

MM_WOM_OPEN: ; {此消息只携带了设备句柄}

MM_WOM_CLOSE: ; {此消息只携带了设备句柄}

MM_WOM_DONE: begin {此消息携带了设备句柄和 WaveHdr 指针(dwParam1)}

waveOutUnprepareHeader(hWave, PWaveHdr(dwParam1), SizeOf(TWaveHdr));

waveOutClose(hWave);

end;

end;

end;

procedure eate(Sender: TObject);

begin

n := '开始录音';

n := '停止录音';

n := '播放录音';

end;

//开始录音

procedure 1Click(Sender: TObject);

begin

{指定要录制的格式}

tTag := WAVE_FORMAT_PCM;

els := 2;

esPerSec := 22050;

tesPerSec := 88200;

Align := 4;

erSample := 16;

:= 0;

SaveBuf := nil; {清除已录制的内容}

if waveInOpen(@hWaveIn, WAVE_MAPPER, @fmt, DWORD(@WaveProc), 0, CALLBACK_FUNCTION) = 0 then

begin

SetLength(buf1, 1024*8);

SetLength(buf2, 1024*8);

:= PAnsiChar(buf1);

erLength := Length(buf1);

sRecorded := 0;

:= 0;

s := 0;

s := 0;

:= nil;

ed := 0;

:= PAnsiChar(buf2);

erLength := Length(buf2);

sRecorded := 0;

:= 0;

s := 0;

s := 0;

:= nil;

ed := 0;

waveInPrepareHeader(hWaveIn, @whIn1, SizeOf(TWaveHdr));

waveInPrepareHeader(hWaveIn, @whIn2, SizeOf(TWaveHdr));

waveInAddBuffer(hWaveIn, @whIn1, SizeOf(TWaveHdr));

waveInAddBuffer(hWaveIn, @whIn2, SizeOf(TWaveHdr));

waveInStart(hWaveIn);

end;

end;

//停止录音

procedure 2Click(Sender: TObject);

begin

waveInStop(hWaveIn);

waveInUnprepareHeader(hWaveIn, @whIn1, SizeOf(TWaveHdr));

waveInUnprepareHeader(hWaveIn, @whIn2, SizeOf(TWaveHdr));

waveInClose(hWaveIn);

end;

//播放录音

procedure 3Click(Sender: TObject);

begin

:= PAnsiChar(SaveBuf);

erLength := Length(SaveBuf);

sRecorded := 0;

:= 0;

s := 0;

s := 1;

:= nil;

ed := 0;

waveOutOpen(@hWaveOut, WAVE_MAPPER, @fmt, DWORD(@WaveProc), 0, CALLBACK_FUNCTION);

waveOutPrepareHeader(hWaveOut, @whOut, SizeOf(TWaveHdr));

waveOutWrite(hWaveOut, @whOut, SizeOf(TWaveHdr));

end;

end.

操作 Wave 文件(12): 使用 重复播放 wav 文件

unit Unit1;

interface

uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs, StdCtrls;

type

TForm1 = class(TForm)

Button1: TButton;

Button2: TButton;

Button3: TButton;

procedure FormCreate(Sender: TObject);

procedure Button1Click(Sender: TObject);

procedure Button2Click(Sender: TObject);

procedure Button3Click(Sender: TObject);

end;

var

Form1: TForm1;

implementation

{$R *.dfm}

uses MMSystem;

function GetWaveFmtData(path: string; var fmt: TWaveFormatEx; var buf: TBytes): Boolean;

var

hFile: HMMIO;

ckiRIFF,ckiFmt,ckiData: TMMCKInfo;

begin

Result := False;

hFile := mmioOpen(PChar(path), nil, MMIO_READ);

if hFile = 0 then Exit;

ZeroMemory(@ckiRIFF, SizeOf(TMMCKInfo));

ZeroMemory(@ckiFmt, SizeOf(TMMCKInfo));

ZeroMemory(@ckiData, SizeOf(TMMCKInfo));

e := mmioStringToFOURCC('WAVE', 0);

:= mmioStringToFOURCC('fmt', 0);

:= mmioStringToFOURCC('data', 0);

ZeroMemory(@fmt, SizeOf(TWaveFormatEx));

mmioDescend(hFile, @ckiRIFF, nil, MMIO_FINDRIFF);

if ( = FOURCC_RIFF) and (e = mmioStringToFOURCC('WAVE',0)) and

(mmioDescend(hFile, @ckiFmt, @ckiRIFF, MMIO_FINDCHUNK) = MMSYSERR_NOERROR) and

(mmioRead(hFile, @fmt, ) = ) and

(mmioAscend(hFile, @ckiFmt, 0) = MMSYSERR_NOERROR) and

(mmioDescend(hFile, @ckiData, @ckiRIFF, MMIO_FINDCHUNK) = MMSYSERR_NOERROR) then

begin

SetLength(buf, );

Result := (mmioRead(hFile, PAnsiChar(buf), ) = );

end;

mmioClose(hFile, 0);

end;

//------------------------------------------------------------------------------

var

wh: TWaveHdr;

hOut: HWAVEOUT;

fmt: TWaveFormatEx;

buf: TBytes;

procedure eate(Sender: TObject);

begin

n := '打开并播放';

n := '暂停';

n := '继续';

end;

procedure WaveProc(hWave: HWAVE; uMsg, dwInstance, dwParam1, dwParam2: DWORD); stdcall;

begin

case uMsg

of

MM_WOM_OPEN: ;

MM_WOM_CLOSE: ;

MM_WOM_DONE: begin

waveOutUnprepareHeader(hWave, PWaveHdr(dwParam1), SizeOf(TWaveHdr));

waveOutClose(hWave);

end;

end;

end;

procedure 1Click(Sender: TObject);

const

path = 'C:WINDOWSMediaWindows XP 启动.wav';

begin

GetWaveFmtData(path, fmt, buf);

:= PAnsiChar(buf);

erLength := Length(buf);

sRecorded := 0;

:= 0;

s := WHDR_BEGINLOOP or WHDR_ENDLOOP; {关键设置}

s := 3; {重复播放的次数}

:= nil;

ed := 0;

waveOutOpen(@hOut, WAVE_MAPPER, @fmt, DWORD(@WaveProc), 0, CALLBACK_FUNCTION);

waveOutPrepareHeader(hOut, @wh, SizeOf(TWaveHdr));

waveOutWrite(hOut, @wh, SizeOf(TWaveHdr));

end;

//暂停

procedure 2Click(Sender: TObject);

begin

waveOutPause(hOut);

end;

//继续

procedure 3Click(Sender: TObject);

begin

waveOutRestart(hOut);

end;

end.

操作 Wave 文件(13): waveOutGetVolume、waveOutSetVolume

左右声道的音量是单调的; 表示音量的 32 位整数的低 16 位是左声道、高 16 位是右声道.

代码文件:

unit Unit1;

interface

uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs, StdCtrls;

type

TForm1 = class(TForm)

Button1: TButton;

Button2: TButton;

Button3: TButton;

ScrollBar1: TScrollBar;

ScrollBar2: TScrollBar;

procedure FormCreate(Sender: TObject);

procedure Button1Click(Sender: TObject);

procedure Button2Click(Sender: TObject);

procedure Button3Click(Sender: TObject);

procedure ScrollBar1Change(Sender: TObject);

procedure FormDestroy(Sender: TObject);

end;

var

Form1: TForm1;

implementation

{$R *.dfm}

uses MMSystem;

function GetWaveFmtData(path: string; var fmt: TWaveFormatEx; var buf: TBytes): Boolean;

var

hFile: HMMIO;

ckiRIFF,ckiFmt,ckiData: TMMCKInfo;

begin

Result := False;

hFile := mmioOpen(PChar(path), nil, MMIO_READ);

if hFile = 0 then Exit;

ZeroMemory(@ckiRIFF, SizeOf(TMMCKInfo));

ZeroMemory(@ckiFmt, SizeOf(TMMCKInfo));

ZeroMemory(@ckiData, SizeOf(TMMCKInfo));

e := mmioStringToFOURCC('WAVE', 0);

:= mmioStringToFOURCC('fmt', 0);

:= mmioStringToFOURCC('data', 0);

ZeroMemory(@fmt, SizeOf(TWaveFormatEx));

mmioDescend(hFile, @ckiRIFF, nil, MMIO_FINDRIFF);

if ( = FOURCC_RIFF) and (e = mmioStringToFOURCC('WAVE',0)) and

(mmioDescend(hFile, @ckiFmt, @ckiRIFF, MMIO_FINDCHUNK) = MMSYSERR_NOERROR) and

(mmioRead(hFile, @fmt, ) = ) and

(mmioAscend(hFile, @ckiFmt,

0) = MMSYSERR_NOERROR) and

(mmioDescend(hFile, @ckiData, @ckiRIFF, MMIO_FINDCHUNK) = MMSYSERR_NOERROR) then

begin

SetLength(buf, );

Result := (mmioRead(hFile, PAnsiChar(buf), ) = );

end;

mmioClose(hFile, 0);

end;

//------------------------------------------------------------------------------

var

wh: TWaveHdr;

hWaveOut: HWAVE;

fmt: TWaveFormatEx;

buf: TBytes;

procedure eate(Sender: TObject);

begin

n := '打开并播放';

n := '暂停';

n := '继续';

:= 0;

:= 100;

:= 0;

:= 100;

ge := ge;

end;

procedure Bar1Change(Sender: TObject);

var

L,R: Word;

begin

if hWaveOut = 0 then Exit;

L := Trunc(on / 100 * MAXWORD);

R := Trunc(on / 100 * MAXWORD);

waveOutSetVolume(hWaveOut, MakeLong(L, R));

end;

procedure WaveProc(hWave: HWAVE; uMsg, dwInstance, dwParam1, dwParam2: DWORD); stdcall;

begin

case uMsg of

MM_WOM_DONE: waveOutUnprepareHeader(hWave, PWaveHdr(dwParam1), SizeOf(TWaveHdr));

end;

end;

procedure 1Click(Sender: TObject);

const

path = 'C:WINDOWSMediaWindows XP 启动.wav';

var

volume: DWORD;

begin

GetWaveFmtData(path, fmt, buf);

:= PAnsiChar(buf);

erLength := Length(buf);

sRecorded := 0;

:= 0;

s := WHDR_BEGINLOOP or WHDR_ENDLOOP;

s := 3;

:= nil;

ed := 0;

waveOutOpen(@hWaveOut, WAVE_MAPPER, @fmt, DWORD(@WaveProc), 0, CALLBACK_FUNCTION);

waveOutGetVolume(hWaveOut, @volume);

on := Trunc(LoWord(volume) / MAXWORD * 100);

on := Trunc(HiWord(volume) / MAXWORD * 100);

waveOutPrepareHeader(hWaveOut, @wh, SizeOf(TWaveHdr));

waveOutWrite(hWaveOut, @wh, SizeOf(TWaveHdr));

end;

//暂停

procedure 2Click(Sender: TObject);

begin

waveOutPause(hWaveOut);

end;

//继续

procedure 3Click(Sender: TObject);

begin

waveOutRestart(hWaveOut);

end;

procedure stroy(Sender: TObject);

begin

if hWaveOut <> 0 then waveOutClose(hWaveOut);

end;

end.

窗体文件:

object Form1: TForm1

Left = 0

Top = 0

Caption = 'Form1'

ClientHeight = 182

ClientWidth = 342

Color = clBtnFace

t = DEFAULT_CHARSET

= clWindowText

= -11

= 'Tahoma'

= []

OldCreateOrder = False

OnCreate = FormCreate

OnDestroy = FormDestroy

PixelsPerInch = 96

TextHeight = 13

object Button1: TButton

Left = 32

Top = 24

Width = 75

Height = 25

Caption = #25171#24320#24182#25773#25918

TabOrder = 0

OnClick = Button1Click

end

object Button2: TButton

Left = 136

Top = 24

Width = 75

Height = 25

Caption = #26242#20572

TabOrder = 1

OnClick = Button2Click

end

object Button3: TButton

Left = 240

Top = 24

Width = 75

Height = 25

Caption = #32487#32493

TabOrder = 2

OnClick = Button3Click

end

object ScrollBar1: TScrollBar

Left = 32

Top = 80

Width = 283

Height = 17

PageSize = 0

TabOrder =

3

OnChange = ScrollBar1Change

end

object ScrollBar2: TScrollBar

Left = 32

Top = 120

Width = 283

Height = 17

PageSize = 0

TabOrder = 4

end

end

操作 Wave 文件(14): waveOutSetPlaybackRate、waveOutSetPitch

这两个参数也都是可以 Get(waveOutGetPlaybackRate、waveOutGetPitch)

设备默认的播放速度是 $00010000, 此值乘以 2 是快一倍, 除以 2 是慢一倍; 最快可到 $000F8000.

设备默认的音高参数是 $00010000, 此值乘以 2 是高一倍, 除以 2 是低一倍; 最高可到 $000F8000.

可能有很多声卡不支持, 我的 IBM 手提就不支持; 不过通过其他技术可以实现的.

代码文件(仅有播放速度的设置代码):

unit Unit1;

interface

uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs, StdCtrls, ComCtrls;

type

TForm1 = class(TForm)

Button1: TButton;

Button2: TButton;

Button3: TButton;

Button4: TButton;

TrackBar1: TTrackBar;

Button5: TButton;

procedure FormCreate(Sender: TObject);

procedure Button1Click(Sender: TObject);

procedure Button2Click(Sender: TObject);

procedure Button3Click(Sender: TObject);

procedure Button4Click(Sender: TObject);

procedure Button5Click(Sender: TObject);

procedure TrackBar1Change(Sender: TObject);

procedure FormDestroy(Sender: TObject);

end;

var

Form1: TForm1;

implementation

{$R *.dfm}

uses MMSystem;

function GetWaveFmtData(path: string; var fmt: TWaveFormatEx; var buf: TBytes): Boolean;

var

hFile: HMMIO;

ckiRIFF,ckiFmt,ckiData: TMMCKInfo;

begin

Result := False;

hFile := mmioOpen(PChar(path), nil, MMIO_READ);

if hFile = 0 then Exit;

ZeroMemory(@ckiRIFF, SizeOf(TMMCKInfo));

ZeroMemory(@ckiFmt, SizeOf(TMMCKInfo));

ZeroMemory(@ckiData, SizeOf(TMMCKInfo));

e := mmioStringToFOURCC('WAVE', 0);

:= mmioStringToFOURCC('fmt', 0);

:= mmioStringToFOURCC('data', 0);

ZeroMemory(@fmt, SizeOf(TWaveFormatEx));

mmioDescend(hFile, @ckiRIFF, nil, MMIO_FINDRIFF);

if ( = FOURCC_RIFF) and (e = mmioStringToFOURCC('WAVE',0)) and

(mmioDescend(hFile, @ckiFmt, @ckiRIFF, MMIO_FINDCHUNK) = MMSYSERR_NOERROR) and

(mmioRead(hFile, @fmt, ) = ) and

(mmioAscend(hFile, @ckiFmt, 0) = MMSYSERR_NOERROR) and

(mmioDescend(hFile, @ckiData, @ckiRIFF, MMIO_FINDCHUNK) = MMSYSERR_NOERROR) then

begin

SetLength(buf, );

Result := (mmioRead(hFile, PAnsiChar(buf), ) = );

end;

mmioClose(hFile, 0);

end;

//------------------------------------------------------------------------------

var

wh: TWaveHdr;

hWaveOut: HWAVE;

fmt: TWaveFormatEx;

buf: TBytes;

procedure eate(Sender: TObject);

begin

n := '打开并播放';

n := '暂停';

n := '继续';

:= -4;

:=

4;

on := 0;

end;

procedure WaveProc(hWave: HWAVE; uMsg, dwInstance, dwParam1, dwParam2: DWORD); stdcall;

begin

case uMsg of

MM_WOM_DONE: waveOutUnprepareHeader(hWave, PWaveHdr(dwParam1), SizeOf(TWaveHdr));

end;

end;

procedure 1Click(Sender: TObject);

const

path = 'C:WINDOWSMediaWindows XP 启动.wav';

var

volume: DWORD;

begin

GetWaveFmtData(path, fmt, buf);

:= PAnsiChar(buf);

erLength := Length(buf);

sRecorded := 0;

:= 0;

s := WHDR_BEGINLOOP or WHDR_ENDLOOP;

s := 3;

:= nil;

ed := 0;

waveOutOpen(@hWaveOut, WAVE_MAPPER, @fmt, DWORD(@WaveProc), 0, CALLBACK_FUNCTION);

waveOutPrepareHeader(hWaveOut, @wh, SizeOf(TWaveHdr));

waveOutWrite(hWaveOut, @wh, SizeOf(TWaveHdr));

end;

//暂停

procedure 2Click(Sender: TObject);

begin

waveOutPause(hWaveOut);

end;

//继续

procedure 3Click(Sender: TObject);

begin

waveOutRestart(hWaveOut);

end;

//调整播放速度

procedure ar1Change(Sender: TObject);

const

mid = $00010000;

var

pos, rate: Integer;

begin

pos := TTrackBar(Sender).Position;

if pos > 0 then

rate := mid shl pos

else

rate := mid shr Abs(pos);

waveOutSetPlaybackRate(hWaveOut, rate);

Text := IntToStr(pos);

end;

//判断设备是否支持播放速度调整

procedure 4Click(Sender: TObject);

var

waveOutCaps: TWaveOutCaps;

begin

waveOutGetDevCaps(WAVE_MAPPER, @waveOutCaps, SizeOf(TWaveOutCaps));

if ort and WAVECAPS_PLAYBACKRATE = WAVECAPS_PLAYBACKRATE then

ShowMessage('默认设备支持播放速度调整.')

else

ShowMessage('默认设备不支持播放速度调整!');

end;

//判断设备是否支持声调变化

procedure 5Click(Sender: TObject);

var

waveOutCaps: TWaveOutCaps;

begin

waveOutGetDevCaps(WAVE_MAPPER, @waveOutCaps, SizeOf(TWaveOutCaps));

if ort and WAVECAPS_PITCH = WAVECAPS_PITCH then

ShowMessage('默认设备支持声调变化.')

else

ShowMessage('默认设备不支持声调变化!');

end;

procedure stroy(Sender: TObject);

begin

if hWaveOut <> 0 then waveOutClose(hWaveOut);

end;

end.

窗体文件:

object Form1: TForm1

Left = 0

Top = 0

Caption = 'Form1'

ClientHeight = 204

ClientWidth = 342

Color = clBtnFace

t = DEFAULT_CHARSET

= clWindowText

= -11

= 'Tahoma'

= []

OldCreateOrder = False

OnCreate = FormCreate

OnDestroy = FormDestroy

PixelsPerInch = 96

TextHeight = 13

object Button1: TButton

Left = 32

Top = 24

Width = 75

Height = 25

Caption = #25171#24320#24182#25773#25918

TabOrder = 0

OnClick = Button1Click

end

object Button2: TButton

Left = 136

Top = 24

Width = 75

Height = 25

Caption = #26242#20572

TabOrder = 1

OnClick = Button2Click

end

object Button3: TButton

Left = 240

Top = 24

Width = 75

Height = 25

Caption = #32487#32493

TabOrder = 2

OnClick = Button3Click

end

object TrackBar1: TTrackBar

Left = 32

Top = 88

Width = 283

Height = 45

TabOrder = 3

OnChange = TrackBar1Change

end

object Button4: TButton

Left = 152

Top = 139

Width = 163

Height = 25

Caption = #21028#26029#35774#22791#26159#21542#25903#25345#36895#24230#35843#25972

TabOrder = 4

OnClick = Button4Click

end

object Button5: TButton

Left = 152

Top = 170

Width = 163

Height = 25

Caption = #21028#26029#35774#22791#26159#21542#25903#25345#22768#35843#21464#21270

TabOrder = 5

OnClick = Button5Click

end

end

操作 Wave 文件(15): 合并与剪裁 wav 文件

unit Unit1;

interface

uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs, StdCtrls;

type

TForm1 = class(TForm)

Button1: TButton;

Button2: TButton;

procedure Button1Click(Sender: TObject);

procedure Button2Click(Sender: TObject);

end;

var

Form1: TForm1;

implementation

{$R *.dfm}

uses MMSystem;

//从指定 wav 文件中获取格式信息和波形数据的函数

function GetWaveFmtData(const path: string; var fmt: TWaveFormatEx; var buf: TBytes): Boolean;

var

hFile: HMMIO;

ckiRIFF,ckiFmt,ckiData: TMMCKInfo;

begin

Result := False;

hFile := mmioOpen(PChar(path), nil, MMIO_READ);

if hFile = 0 then Exit;

ZeroMemory(@ckiRIFF, SizeOf(TMMCKInfo));

ZeroMemory(@ckiFmt, SizeOf(TMMCKInfo));

ZeroMemory(@ckiData, SizeOf(TMMCKInfo));

e := mmioStringToFOURCC('WAVE', 0);

:= mmioStringToFOURCC('fmt', 0);

:= mmioStringToFOURCC('data', 0);

ZeroMemory(@fmt, SizeOf(TWaveFormatEx));

mmioDescend(hFile, @ckiRIFF, nil, MMIO_FINDRIFF);

if ( = FOURCC_RIFF) and (e = mmioStringToFOURCC('WAVE',0)) and

(mmioDescend(hFile, @ckiFmt, @ckiRIFF, MMIO_FINDCHUNK) = MMSYSERR_NOERROR) and

(mmioRead(hFile, @fmt, ) = ) and

(mmioAscend(hFile, @ckiFmt, 0) = MMSYSERR_NOERROR) and

(mmioDescend(hFile, @ckiData, @ckiRIFF, MMIO_FINDCHUNK) = MMSYSERR_NOERROR) then

begin

SetLength(buf, );

Result := (mmioRead(hFile, PAnsiChar(buf), ) = );

end;

mmioClose(hFile, 0);

end;

//根据格式信息和波形数据建立 wav 文件的函数

function CreateWave(const path: string; const fmt: TWaveFormatEx; const buf: TBytes): Boolean;

var

h: HMMIO;

ckiRiff, ckiFmt, ckiData: TMMCKInfo;

begin

ZeroMemory(@ckiRiff, SizeOf(TMMCKInfo));

:= 44 - 8 + Length(buf);

e := mmioStringToFOURCC('WAVE', 0);

ZeroMemory(@ckiFmt, SizeOf(TMMCKInfo));

:= mmioStringToFOURCC('fmt', 0);

ZeroMemory(@ckiData, SizeOf(TMMCKInfo));

:= mmioStringToFOURCC('data', 0);

:= Length(buf);

h := mmioOpen(PChar(path), nil, MMIO_CREATE or MMIO_WRITE);

if (h <> 0) and (mmioCreateChunk(h, @ckiRiff, MMIO_CREATERIFF) = MMSYSERR_NOERROR) and

(mmioCreateChunk(h, @ckiFmt, 0) = MMSYSERR_NOERROR)

and

(mmioWrite(h, PAnsiChar(@fmt), SizeOf(TPCMWaveFormat)) = SizeOf(TPCMWaveFormat)) and

(mmioAscend(h, @ckiFmt, 0) = MMSYSERR_NOERROR) and

(mmioCreateChunk(h, @ckiData, 0) = MMSYSERR_NOERROR) then

Result := (mmioWrite(h, PAnsiChar(buf), Length(buf)) = Length(buf));

mmioClose(h, 0);

end;

//截取 wav 文件, 本例截留了文件的 1/4

procedure 1Click(Sender: TObject);

const

pathSource = 'C:WINDOWSMediaWindows XP 启动.wav';

pathDest = 'C:';

var

fmt: TWaveFormatEx;

buf: TBytes;

begin

GetWaveFmtData(pathSource, fmt, buf);

SetLength(buf, Length(buf) div 4);

CreateWave(pathDest, fmt, buf);

end;

//合并 wav 文件

procedure 2Click(Sender: TObject);

const

path1 = 'C:WINDOWSMediaWindows XP 启动.wav';

path2 = 'C:WINDOWSMediaWindows XP 关机.wav';

pathDest = 'C:';

var

fmt1,fmt2: TWaveFormatEx;

buf1,buf2: TBytes;

oldLen: Integer;

begin

GetWaveFmtData(path1, fmt1, buf1);

GetWaveFmtData(path2, fmt2, buf2);

if CompareMem(@fmt1, @fmt2, SizeOf(TWaveFormatEx)) then

begin

oldLen := Length(buf1);

SetLength(buf1, Length(buf1) + Length(buf2));

CopyMemory(@buf1[oldLen], Pointer(buf2), Length(buf2));

CreateWave(pathDest, fmt1, buf1);

end else ShowMessage('文件格式不一致, 没有执行合并!');

end;

end.

本文标签: 文件设备数据采样函数