画 WAV 任意指定段的波形(100分)

  • 主题发起人 主题发起人 赛特
  • 开始时间 开始时间

赛特

Unregistered / Unconfirmed
GUEST, unregistred user!
11025 16位 单声道 的 WAV 文件,如何画任意指定段的波形啊?
我是这样读取的,但好像有问题:
var
f: TFileStream;
w: TWaveHeader;
data:array of byte;
begin

f := TFileStream.create(Filename, fmOpenRead);
try
f.Read(w, Sizeof(w));
setlength(data, w.wSampleLength);
//wSampleLength 是头文件最后一个longint
f.Read(data[0], w.wSampleLength);
finally
f.free;
end;

end;

读取头文件没问题,但读出来的data数组的值始终不对,这样参数的WAV文件读出来的data应该是在32768上下波动吧。应该如何读取,并且用什么画出来最方便呢?谢谢!

也可以直接发到我邮箱 saite006@sina.com
 
要标准得PCM格式得wav文件才可以, 你看一下这个文件得属性
 
就是用delphi的mediaplayer录音生成的wav文件,应该是pcm编码吧
 
To 楼主:
一、您说您读取文件头没问题,我看未必吧。标准 PCM 文件的文件头至少要满足下面 5 个条件:
1、具有"RIFF"标识和"WAVE"标识;
2、数据域不嵌套;
3、用"fmt "标识 PCMWAVEFORMAT 结构;
4、没有作者信息、编辑时间等内容;
5、用"data"标识数据域。
我不知道您的 Wave 文件满足了其中几个条件?一旦文件头不是标准 PCM,那读出的数据自然不会正确。
二、网上流传的 TWaveHeader 格式有多种,我一般用这个:
TWaveFileHeader = record
szChunkID: array[0..3] of Char;//"RIFF"标志
dwChunkSize: DWORD;//文件大小 - 8
szFormat: array[0..3] of Char;//"WAVE"标志
szSubChunk1ID: array[0..3] of Char;//"fmt "标志
dwSubChunk1Size: DWORD;//SizeOf(PCMWAVEFORMAT)
wFormatTag: Word;//为 1 表示是PCM格式,否则就是经过压缩了
wChannels: Word;//通道数
dwSamplesPerSec: DWORD;//采样率
dwAvgBytesPerSec: DWORD;//传输速率
wBlockAlign: Word;//样本数据位数
wBitsPerSample: Word;//采样大小
szSubChunk2ID: array[0..3] of Char;//"data"标记
dwSubChunk2Size: DWORD;//音频数据大小
end;

三、我注意到您的数据域是用 data(Byte 类型)读取的,显然很令人费解!Wave 的数据域是以字(双字节)为单位的,您读取也应该读取一个字吧,读取一个字节算什么呢?再说其存储结构与声道有关:
1、单声道:
---------------------------------
采样1 | 采样2 |……
---------------------------------
低字节 高字节|低字节 高字节|……
---------------------------------
2、双声道:
---------------------------------
采样1 |……
---------------------------------
左声道 | 右声道 |……
---------------------------------
低字节 高字节|低字节 高字节|……
---------------------------------
四、我这写了个例子,它可以把数据域所有内容一次性读入内存中,至于怎么处理这些数据你自己看着办吧:
procedure GetWaveData(const FileName: string);
var
hFile: THandle;
wfh: TWaveFileHeader;
r, i: DWORD;
data, p: PWord;
begin

hFile := CreateFile(PChar(FileName), GENERIC_READ, FILE_SHARE_READ, nil,
OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0);
if hFile = INVALID_HANDLE_VALUE then
Exit;
ReadFile(hFile, wfh, SizeOf(wfh), r, nil);
if (wfh.szChunkID <> 'RIFF') or (wfh.szFormat <> 'WAVE') or
(wfh.szSubChunk1ID <> 'fmt ') or (wfh.wFormatTag <> 1) or
(wfh.szSubChunk2ID <> 'data') then
Exit;
data := AllocMem(wfh.dwSubChunk2Size);
//将所有音频数据读入内存,data 指向内存中数据的首地址
ReadFile(hFile, data^, wfh.dwSubChunk2Size, r, nil);
p := data;
//防止程序无休止循环,取 6 个数据意思一下就行了
for i := 0 to 5{wfh.dwSubChunk2Size div 16 - 1}do

begin

Inc(p, 16*i);
ShowMessage(IntToStr(p^));
end;

FreeMem(data);
CloseHandle(hFile);
end;

你可以用 "C:/WINDOWS/Media/Windows XP 关机.wav" 试一下,能不能用给个话,好吧?
 
for i := 0 to wfh.dwSubChunk2Size div 16 - 1do

会报错:access violation at address ...

之所以“div 16”,是不是就是因为wav是以双字节为单位的呢?

如果是 11025 16位 单声道 这样参数的wav,读出来的数据应该是在什么值上下波动才正确呢?谢谢!!
 
1、我的怎么不报错啊,你用的是我的代码么?或者你把你的音频文件发到我的邮箱:zh5430@yahoo.com.cn
2、“div 16”当然就是因为数据是双字节的了,图都给你画出来了...
3、至于波形基准,8 位在 0~255 波动,128 是静音(基线);16 位在 -32768~32768 波动,0 为静音(基线)。
 
我把测试程序已发到你邮箱了,代码就是你写的。
如果只读取指定几个值不会报错,但如果读取所有值“wfh.dwSubChunk2Size div 16 - 1”时就要报错了。
测试用的WAV就是"C:/WINDOWS/Media/Windows XP 关机.wav",而且这个文件就是16位的,但是按你的代码读取出来,怎么都是正数呢,你不是说16位时是在-32768~32768 波动吗?

非常感谢!!!
 
To 赛特:
首先非常抱歉,帮了倒忙。这两天有点糊涂,竟然把 2 个字节和 16 位都搞混了。
我改过来了,你全部替换就行了,应该没问题:
procedure TForm1.GetWaveData(const FileName: string);
var
hFile: THandle;
wfh: TWaveFileHeader;
r, i: DWORD;
data, p: PSmallint;
begin

hFile := CreateFile(PChar(FileName), GENERIC_READ, FILE_SHARE_READ, nil,
OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0);
if hFile = INVALID_HANDLE_VALUE then
Exit;
ReadFile(hFile, wfh, SizeOf(wfh), r, nil);
if (wfh.szChunkID <> 'RIFF') or (wfh.szFormat <> 'WAVE') or
(wfh.szSubChunk1ID <> 'fmt ') or (wfh.wFormatTag <> 1) or
(wfh.szSubChunk2ID <> 'data') then
Exit;
data := AllocMem(wfh.dwSubChunk2Size);
//将所有音频数据读入内存,data 指向内存中数据的首地址
ReadFile(hFile, data^, wfh.dwSubChunk2Size, r, nil);
p := data;
ListBox1.Items.begin
Update;
for i := 0 to wfh.dwSubChunk2Size div 2 - 1do

begin

ListBox1.Items.Add(IntToStr(p^));
Inc(p);
end;

ListBox1.Items.EndUpdate;
FreeMem(data);
CloseHandle(hFile);
end;
 
太感谢了!!
图我也画出来了,和 CoolEdit 软件画的波形比较,是那么回事了,呵呵~~
 
接受答案了.
 

Similar threads

I
回复
0
查看
804
import
I
S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
2K
SUNSTONE的Delphi笔记
S
后退
顶部