一、结构分析
要实现语音合并首先要弄清Wav文件的结构。 1.用UltraEdit工具软件打开一个Wav文件(如图1所示)直接查看其16进制码: PT1.wav

图1 打开wav文件
Wav文件存储格式如表1:
表1 Wav 文件存储格式表
地 址 |
内 容 |
说 明 |
对照实际文件说明 |
00H |
"RIFF" |
"RIFF"标志 |
0x52494646 字符串"RIFF",表示此文件遵循一种标准格式名为"资源互换文件格式"(Resource Intercharge File Format) |
04H |
×××× |
文件大小 |
0x0001F8EC=129260 实际文件大小为129260+8=129268字节 (因RIFF和文件大小两项内容共占用8个字节) |
08H |
"WAVE" |
"WAVE"标志 |
0x57415645 字符串"WAVE"标志 |
0CH |
"fmt " |
"fmt "标志 |
0x666D7420 字符串"fmt "标志,注意t后有个空格字节0x20 |
10H |
×××× |
PCMWAWFORMAT内容大小 |
0x00000010=16 格式内容长度16字节 |
14H |
×××× …… ×××× |
"PCMWAVEFORMAT"内容 |
0x0001 格式类别(为PCM形式的声音数据) |
16H |
0x0001 通道数,单声道为1,双声道为2 |
18H |
0x00005622=22050 采样频率,表示每个通道的播放速度 |
1CH |
0x0000AC44=44100 波形音频数据传送速度 |
20H |
0x0020=32
数据块的调整数(按字节算),其值为通道数×每样本的数据位值/8 |
22H |
0x0010=16 每样本数据位数,表示每个声道中各个样本的数据位数。如果有多个声道,对每个声道而言,样本大小都一样 8 or 16 |
24H |
"data" |
"data"标志 |
0x64617461 // 'data' 标志 |
28H |
×××× |
语音数据大小 |
0x0001F8C8=129224 语音数据的长度 |
|
∶ ∶ |
语音数据 |
F1FF1500……文件尾 |
通过使用Windows系统附件中的录音机工具,打开此Wav文件,查看属性,可获得如下信息:

图2 Wav属性显示
2.Wav文件的结构分析
(1) 分析D:\WINNT\system32\ALSNDMGR.WAV

图3

图4
从16进制码中可看出,PCMWAWFORMAT的大小为0x000012=18个字节,即红色框中均为PCMWAWFORMAT的内容。需要注意的是,有很多资料都认为PCM格式的PCMWAWFORMAT的大小应为16个字节,而ALSNDMGR.WAV这个文件虽为PCM格式,但它的PCMWAWFORMAT的大小却为18个字节。尝试删除其尾部的0x0000,并相应地修改PCMWAWFORMAT的大小为0x000010,用录音机工具验证发现对文件没有造成破坏,因此该PCMWAWFORMAT可视作PCM格式的一个特例。此外图3中蓝色选中部份在图1中未曾出现,这可能是有些硬件在采集下来的文件中添加了自己的一些信息,只起某些注释作用,对文件内容没有实质性影响。

图5
(2) 分析D:\WINNT\Media\ir_begin.wav
用“录音机”工具查看其音频格式为:Microsoft ADPCM 22.050 kHz, 4 位, 立体声。从其16进制码中可看出,PCMWAWFORMAT的大小为0x000032=50个字节,即红色框中均为PCMWAWFORMAT的内容,可见各种格式的PCMWAWFORMAT的大小是不相同的。除去16字节的主格式内容,其地址24H处的0x0020指出附加格式的内容大小为32个字节,地址26H-45H中的内容即为附加格式的内容。PCMWAWFORMAT中的格式决定了各格式的PCMWAWFORMAT长度是不同的,其中PCMWAWFORMAT长度由“fmt ”后的数据给出,该长度包括16字节的fmt主格式内容和附加格式内容,其第17字节则是附加格式内容的长度。关于各类别附加格式的内容根据有关资料共有36种,限于篇幅不一一在此例举。此外值得注意的是文件中也存在图3中蓝色选中部分的某些注释信息,可见PCMWAWFORMAT后不一定紧跟“data”标志,中间有可能出现注释信息。
Wav文件的基本结构如图6所示。
↓
"fmt "标志 |
PCMWAWFORMAT内容大小 |
fmt主格式内容(长度为16字节) |
fmt附加格式大小 |
fmt附加格式内容 |
↓
↓
图6 Wav文件:基本结构
三、代码描述
1.类型 (伪代码)
type
TRiffChuck = record //定义表2中的RIFF块
RiffID:array[0..3] of char; // 'RIFF' 标志
WaveFileSize: longint; // 文件长度
WaveID:array[0..3] of char; // 'WAVE' 标志
end;
TFmtChuck = record //定义表2中的fmt块
fmtId:array[0..3] of char; // 'fmt '标志
FormatSize: longint; // PCMWAWFORMAT内容长度
wFormatTag: word; // 编码类型
nChannels: word; // 通道数 单声道=1, 双声道=2 }
nSamplesPerSec: longint; // 采样频率
nAvgBytesPerSec: longint; // 波形音频数据传送速度
nBlockAlign: word; // 数据块的调整数
nBitsPerSample: word; // 每样本数据位数
ExtraParmSize:word; //附加格式的长度
XX //以下为附加格式的内容
: //注意在下面算法部分中,附加格式的长度及其内容是
XX //存放在FmtChuckOthers的数组中的
end;
TDataChuck = record //定义表2中的data块
dataId: array[0..3]of char; // 'data' 标志
dataSize: longint; // 语音数据长度
XX // 语音数据,注意在下面算法部分中
: // DataChuck不包括语音数据
XX
end;
2.算法
目标文件的建立方法
(1) 先将其中一个Wav文件的RiffChunck,FmtChuck, DataChuck分别复制到目标文件中
(2) 修改目标文件的数据大小 DataChuck3.dataSize:=DataChuck1.dataSize+DataChuck2.dataSize
(3) 修改目标文件的文件长度 RiffChuck3.WaveFileSize:=DataChuck3.dataSize+sizeof(DataChuck3)+FmtChuck
(4) 写入目标文件 写RiffChuck ,FmtChuck: WaveFile.Write(RiffChuck3,SizeOf(RiffChuck3));
WaveFile.Write(FmtChuck3,SizeOf(FmtChuck3));
需要说明的是:FmtChuck的大小是不定长的,而wFormatTag即编码类型有36种,为降低程序的复杂性,采用先定义各编码类型的共性部分,即前16字节的基本音频格式信息,而各编码类型的后余部分则由一个数组FmtChuckothers来存取使用。
if FmtChuck1.FormatSize>16 then
begin
for i:=0 to FmtChuck1.FormatSize-16-1 do
waveFile.Write(FmtChuck3others[i],sizeof(FmtChuck3others[i]));
end;
写DataChuck及语音数据,因源文件的格式相同,所以只需将源文件2的语音数据简单追加到源文件1的语音数据之后即可。
WaveFile.Write(DataChuck3,SizeOf(DataChuck3));
WaveFile.CopyFrom(SoureFile1,DataChuck1.dataSize);
WaveFile.CopyFrom(SoureFile2,DataChuck2.dataSize);
四、测试与验证
通过采用Windows系统自带附件“录音机”将测试源文件分别转换成其所支持的所有音频格式,包括本文前部所提及的PCM格式的一个特例,执行合并程序后再用“录音机”打开,查看属性与本文结构分析中的内容相一致,可见合并方法是正确可行的!
|