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.
版权声明:本文标题:操作Wave 文件_Delphi 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://m.elefans.com/dongtai/1721567092a885510.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论