如何读取wav文件,并画成波形图。。

时间:2023-02-03 17:34:30
如题~~~~
如何读取wav文件,并画成波形图
即画出wav 文件的频谱!!!!!!!!

35 个解决方案

#1


MMTools控件是不是能支持啊!

#2


先熟悉Wave文件格式,如Wave文件头格式、声音数据格式等
然后以时间作为横坐标,采样点作为纵坐标,然后画点连线。
可以参考《Programming Windows,Fifth Edition,Charles Petzold 》第22章 声音与音乐
需要注意的是Wave文件格式有很多种,可以在网上查找相关资料。

#3


gz

#4


wav文件是属于RIFF文件结构的。

RIFF文件结构和WAVE文件格式

  Windows支持两种RIFF(Resource Interchange File Format,"资源交互文件格式")格式的音频文件:MIDI的RMID文件和波形音频文件格式WAVE文件,其中在计算机领域最常用的数字化声音文件格式是后者,它是微软专门为Windows系统定义的波形文件格式(Waveform Audio),由于其扩展名为"*.wav",因而该类文件也被称为WAVE文件。为了突出重点,有的放矢,本文涉及到的声音文件所指的就是WAVE文件。常见的WAVE语音文件主要有两种,分别对应于单声道(11.025KHz采样率、8Bit的采样值)和双声道(44.1KHz采样率、16Bit的采样值)。这里的采样率是指声音信号在进行"模→数"转换过程中单位时间内采样的次数。采样值是指每一次采样周期内声音模拟信号的积分值。对于单声道声音文件,采样数据为八位的短整数(short int 00H-FFH);而对于双声道立体声声音文件,每次采样数据为一个16位的整数(int),高八位和低八位分别代表左右两个声道。WAVE文件数据块包含以脉冲编码调制(PCM)格式表示的样本。在进行声音编程处理以前,首先让我们来了解一下RIFF文件和WAVE文件格式。

  RIFF文件结构可以看作是树状结构,其基本构成是称为"块"(Chunk)的单元,每个块有"标志符"、"数据大小"及"数据"所组成,块的结构如图1所示:

块的标志符(4BYTES) 
数据大小 (4BYTES) 
数据 
 图一、 块的结构示意图

  从上图可以看出,其中"标志符"为4个字符所组成的代码,如"RIFF","LIST"等,指定块的标志ID;数据大小用来指定块的数据域大小,它的尺寸也为4个字符;数据用来描述具体的声音信号,它可以由若干个子块构成,一般情况下块与块是平行的,不能相互嵌套,但是有两种类型的块可以嵌套子块,他们是"RIFF"或"LIST"标志的块,其中RIFF块的级别最高,它可以包括LIST块。另外,RIFF块和LIST块与其他块不同,RIFF块的数据总是以一个指定文件中数据存储格式的四个字符码(称为格式类型)开始,如WAVE文件有一个"WAVE"的格式类型。LIST块的数据总是以一个指定列表内容的4个字符码(称为列表类型)开始,例如扩展名为".AVI"的视频文件就有一个"strl"的列表类型。RIFF和LIST的块结构如下:

RIFF/LIST标志符 
数据1大小 
数据1  格式/列表类型 
数据 
图二、RIFF/LIST块结构

  WAVE文件是非常简单的一种RIFF文件,它的格式类型为"WAVE"。RIFF块包含两个子块,这两个子块的ID分别是"fmt"和"data",其中"fmt"子块由结构PCMWAVEFORMAT所组成,其子块的大小就是sizeofof(PCMWAVEFORMAT),数据组成就是PCMWAVEFORMAT结构中的数据。WAVE文件的结构如下图三所示:

标志符(RIFF) 
数据大小 
格式类型("WAVE") 
"fmt" 
Sizeof(PCMWAVEFORMAT) 
PCMWAVEFORMAT 
"data" 
声音数据大小 
声音数据 
 图三、WAVE文件结构

  PCMWAVEFORMAT结构定义如下:

Typedef struct
{
WAVEFORMAT wf;//波形格式;
WORD wBitsPerSample;//WAVE文件的采样大小;
}PCMWAVEFORMAT;
WAVEFORMAT结构定义如下:
typedef struct
{
WORD wFormatag;//编码格式,包括WAVE_FORMAT_PCM,WAVEFORMAT_ADPCM等
WORD nChannls;//声道数,单声道为1,双声道为2;
DWORD nSamplesPerSec;//采样频率;
DWORD nAvgBytesperSec;//每秒的数据量;
WORD nBlockAlign;//块对齐;
}WAVEFORMAT; 

  "data"子块包含WAVE文件的数字化波形声音数据,其存放格式依赖于"fmt"子块中wFormatTag成员指定的格式种类,在多声道WAVE文件中,样本是交替出现的。如16bit的单声道WAVE文件和双声道WAVE文件的数据采样格式分别如图四所示:

  16位单声道:

采样一 采样二  …… 
低字节 高字节 低字节 高字节  …… 

  16位双声道:

采样一 …… 
左声道 右声道 …… 
低字节 高字节 低字节 高字节  …… 

                 图四、WAVE文件数据采样格式

#5


偶比较关心:谁有代码哈?

#6


我有点资料,显示系统声音的波形图,
如果你认为对你帮助留个email
wav的我也想知道,没有接触过

#7


richarsun@163.com

如果是程序那就最好了。。呵呵。

#8


发了记得告诉我哦。。师兄。。。

#9


没有问题,等一下我就发给你,是代码来的,可惜不是读wav的

#10


我发了,你去收了

#11


想读WAV的文件

先知道RIFF的文件格式

然后用API的mmioOpen,mmioRead,mmioSeek,mmioWrite,等
函数来操作

有个例子你看一下  不过例子不全对里面有错误
BYTE * GetData(Cstring *pString) 
//获取声音文件数据的函数,pString参数指向要打开的声音文件;
{
if (pString==NULL)
return NULL;
HMMIO file1;//定义HMMIO文件句柄;
file1=mmioOpen((LPSTR)pString,NULL,MMIO_READWRITE);//以读写模式打开所给的WAVE文件;
if(file1==NULL)
{
MessageBox("WAVE文件打开失败!");
Return NULL;
}
char style[4];//定义一个四字节的数据,用来存放文件的类型;
mmioSeek(file1,8,SEEK_SET);//定位到WAVE文件的类型位置
mmioRead(file1,style,4);
if(style[0]!=’W’||style[1]!=’A’||style[2]!=’V’||style[3]!=’E’)//判断该文件是否为"WAVE"文件格式
{
MessageBox("该文件不是WAVE格式的文件!");
Return NULL;
}
PCMWAVEFORMAT format; //定义PCMWAVEFORMAT结构对象,用来判断WAVE文件格式;
mmioSeek(file1,20,SEEK_SET);
//对打开的文件进行定位,此时指向WAVE文件的PCMWAVEFORMAT结构的数据;
mmioRead(file1,(char*)&format,sizeof(PCMWAVEFORMAT));//获取该结构的数据;
if(format.wf.nChannels!=2)//判断是否是立体声声音;
{
MessageBox("该声音文件不是双通道立体声文件");
return NULL;
}
mmioSeek(file1,24+sizeof(PCMWAVEFORMAT),SEEK_SET);
//获取WAVE文件的声音数据的大小;
long size;
mmioRead(file1,(char*)&size,4);
BYTE *pData;
pData=(BYTE*)new char[size];//根据数据的大小申请缓冲区;
mmioSeek(file1,28+sizeof(PCMWAVEFORMAT),SEEK_SET);//对文件重新定位;
mmioRead(file1,(char*)pData,size);//读取声音数据;
mmioClose(file1, MMIO_FHOPEN);//关闭WAVE文件;
return pData;
}

#12


支持

#13


回复人: constantine(飘遥的安吉儿) 

我要。。

richarson_001@163.com

TO:红客robby。。我才是楼主。。-_-###

#14


发了

#15


c++builder程序设计经典
这本书上有代码

#16


顶,顶。。。

#17


constantine(飘遥的安吉儿) :
给我也来一份
net205@126.com

#18


我也要一份!
yuruilei@21cn.com

#19


代码我就不再发了,那个不是wave的,下面我给大家贴个是的,并画出图来,不过是delphi的

unit Unit1;

interface

uses

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

StdCtrls, ExtCtrls;

type

TPCMWaveHeader = record

rID: array[0..3] of char; // 'RIFF' 标志

rLen: longint; // 文件长度

wID: array[0..3] of char; // 'WAVE' 标志

fId: array[0..3] of char; // 'fmt '标志 以空格结束(fID[3]:=Chr($20);)

fLen: longint; // 过度字节(不定)

wFormatTag: word; // 格式字节(过度字节为PCM形式的声音数据)

nChannels: word; // 通道数 单声道=1, 双声道=2 }

nSamplesPerSec: longint; // 采样频率 (每秒样本数),表示每个通道的播放速度

nAvgBytesPerSec: longint; // 波形音频数据传送速度(值:通道数X每秒数据位数X每

// 样本的数据位数/8。播放软件利用根据此值估计缓

// 冲区的大小)

nBlockAlign: word; // 数据块的调整数(按字节计算),值为通道数*没样本的数据位值/8。

// 播放软件需要一次处理多个该值大小的字节数据,以便

// 将其值用于缓冲区的调整。

nBitsPerSample: word; // 每样本数据位数,表示每个声道中各个样本的数据位

// 数。如果有多个声道,对每个声道而言,样本大

// 小都一样 8 or 16

dId: array[0..3] of char; // 'data' 标志

dLen: longint; // 语音数据长度

end;

TForm1 = class(TForm)

Button2: TButton;
    Memo1: TMemo;

procedure Button2Click(Sender: TObject);

procedure FormCreate(Sender: TObject);

private

filename: string;

pcm: TPCMWaveHeader;

Data: array[0..80 * 10240] of Byte;

procedure PrintPcm;

procedure PrintData;

public

{ Public declarations }

end;

var

Form1 : TForm1;

implementation

{$R *.DFM}

procedure TForm1.PrintPcm;

begin

// memo1.lines.add('FileName:' + Filename);

memo1.lines.add('RIFF:' + pcm.rID);

memo1.lines.add('RLEN:' + inttostr(pcm.rLen));

memo1.lines.add('WAVE:' + pcm.wID);

memo1.lines.add('fmt:' + pcm.fId);

memo1.lines.add('Fixed:' + inttostr(pcm.fLen));

memo1.lines.add('wFormatTag:' + inttostr(pcm.wFormatTag));

memo1.lines.add('nChannels:' + inttostr(pcm.nChannels));

memo1.lines.add('nSamplesPerSec:' + inttostr(pcm.nSamplesPerSec));

memo1.lines.add('nAvgBytesPerSec:' + inttostr(pcm.nAvgBytesPerSec));

memo1.lines.add('nBlockAlign:' + inttostr(pcm.nBlockAlign));

memo1.lines.add('nBitsPerSample:' + inttostr(pcm.nBitsPerSample));

memo1.lines.add('dLen:' + inttostr(pcm.dLen));

end;

procedure TForm1.Button2Click(Sender: TObject);

var

iFileHandle : Integer;

iFileLength : Integer;

iBytesRead : Integer;

Buffer : PChar;

//i, Size : Integer;

begin

iFileHandle := FileOpen(filename, fmOpenRead);

iFileLength := FileSeek(iFileHandle, 0, 2) - FileSeek(iFileHandle, 0, 0);

GetMem(Buffer, iFileLength + 1);

iBytesRead := FileRead(iFileHandle, Buffer^, iFileLength); //把文件读到buffer

FileClose(iFileHandle);

Move((Buffer)^, pcm, sizeof(pcm));

Move((Buffer)^, Data, pcm.dLen);

printpcm; //显示头信息

printData; //画波形图

FreeMem(Buffer);

end;

procedure TForm1.FormCreate(Sender: TObject);

begin

filename := 'C:\Windows XP 登录音.wav';

end;

procedure TForm1.PrintData;

var

i : Integer;

begin

for i := 0 to pcm.dLen - 1 do

begin

//memo1.lines.add(inttostr(Data[i]));

Canvas.LineTo(i, Data[i])

end;

end;

end.

#20


只要在form上放一个button,一个memo,把他们的事件对应上就可以了
我在XP+bcb6 测试通过

#21


如果满意记得提交FAQ,^_^

#22


回复人: constantine(飘遥的安吉儿) ( ) 信誉:101 
只要在form上放一个button,一个memo,把他们的事件对应上就可以了
我在XP+bcb6 测试通过

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

不是Delphi7中通过吗????

#23


哈哈,在这里习惯了,

#24


还是我来贴代码吧
呵呵。。。。。。。。。。。。。

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

#ifndef U14_4_2H
#define U14_4_2H
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <MPlayer.hpp>
//---------------------------------------------------------------------------
class Tf14_4_2 : public TForm
{
__published: // IDE-managed Components
        TLabel *Label1;
        TLabel *Label2;
        TButton *btnEnd;
        TButton *btnShowWave;
        TScrollBar *Srb;
        TEdit *Edit1;
        TEdit *Edit2;
        TMediaPlayer *MediaPlayer1;
        void __fastcall FormCreate(TObject *Sender);
        void __fastcall MediaPlayer1Click(TObject *Sender,
          TMPBtnType Button, bool &DoDefault);
        void __fastcall btnShowWaveClick(TObject *Sender);
        void __fastcall SrbChange(TObject *Sender);
        void __fastcall Edit1Change(TObject *Sender);
        void __fastcall Edit2Change(TObject *Sender);
        void __fastcall btnEndClick(TObject *Sender);
private: // User declarations
public: // User declarations
        __fastcall Tf14_4_2(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE Tf14_4_2 *f14_4_2;
//---------------------------------------------------------------------------
#endif



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

#include <vcl.h>
#pragma hdrstop

#include "U14_4_2.h"
#include <stdio.h>
#include<io.h>
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"

int FileLen,dx=0,dy=200,dd=44;
int divx,divy;
char* file_name="test.WAV";
short *data;
FILE *inf;

Tf14_4_2 *f14_4_2;
//---------------------------------------------------------------------------
__fastcall Tf14_4_2::Tf14_4_2(TComponent* Owner)
        : TForm(Owner)
{
}
//---------------------------------------------------------------------------
void show(void)
{
  f14_4_2->Refresh();
  f14_4_2->Canvas->TextOut(10,400,IntToStr(f14_4_2->Srb->Position) );

  divx=StrToInt(f14_4_2->Edit1->Text);
  divy=StrToInt(f14_4_2->Edit2->Text);

  f14_4_2->Canvas->MoveTo(0+dx,-data[0]/divy+dy);
  f14_4_2->Canvas->Pen->Color=clBlue;
  for(int i=1;(i<f14_4_2->Width*divx)&&(i<(FileLen-dd)/2-1);i++)
    f14_4_2->Canvas->LineTo(i/divx+dx,-data[i+f14_4_2->Srb->Position]/divy+dy);
}
//---------------------------------------------------------------------------
void __fastcall Tf14_4_2::FormCreate(TObject *Sender)
{
   bool open_flag;
  int handle;

  if(open_flag==true)
  {
    fclose(inf);
  }
  inf=fopen(file_name,"r");
  open_flag=true;
  handle=fileno(inf);
  FileLen= filelength(handle);

  if(feof(inf))
  {
    fclose(inf);
    open_flag=false;
  }

  data = new short[(FileLen-dd)/2];

  fseek(inf,dd,SEEK_SET);
  fread(data,sizeof(short),(FileLen-dd)/2,inf);

  MediaPlayer1->VisibleButtons=Mplayer::TButtonSet()<<btPlay<<btStop<<btPause;
  MediaPlayer1->DeviceType=dtWaveAudio;
  MediaPlayer1->FileName=file_name;
  MediaPlayer1->Display=f14_4_2;
  MediaPlayer1->Open();

  if((FileLen-dd)-f14_4_2->Width*divx<0)
    Srb->Max=0;
  else
    Srb->Max=(FileLen-dd)/2-f14_4_2->Width*divx;
}
//---------------------------------------------------------------------------
void __fastcall Tf14_4_2::MediaPlayer1Click(TObject *Sender,
      TMPBtnType Button, bool &DoDefault)
{
    if(!MediaPlayer1->AutoOpen)
     MediaPlayer1->Play();
}
//---------------------------------------------------------------------------
void __fastcall Tf14_4_2::btnShowWaveClick(TObject *Sender)
{
  show();
}
//---------------------------------------------------------------------------



void __fastcall Tf14_4_2::SrbChange(TObject *Sender)
{
  if(((FileLen-dd)/2-f14_4_2->Width*divx)<0)
    Srb->Max=0;
  else
    Srb->Max=(FileLen-dd)/2-f14_4_2->Width*divx;

  show();
}
//---------------------------------------------------------------------------


void __fastcall Tf14_4_2::Edit1Change(TObject *Sender)
{
   if(((FileLen-dd)/2-f14_4_2->Width*divx)<0)
    Srb->Max=0;
  else
    Srb->Max=(FileLen-dd)/2-f14_4_2->Width*divx;

  f14_4_2->Srb->Position=0;
  show();
}
//---------------------------------------------------------------------------
void __fastcall Tf14_4_2::Edit2Change(TObject *Sender)
{
   f14_4_2->Srb->Position=0;
  show();
}
//---------------------------------------------------------------------------
void __fastcall Tf14_4_2::btnEndClick(TObject *Sender)
{
  delete data;
  fclose(inf);
  Close();
}
//---------------------------------------------------------------------------

#25


记得揭帖啊。。。。。。。
我在XP+BCB6下编译通过

#26


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

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Button2Click(TObject *Sender)
{
      if(OpenDialog1->Execute())
         mFileName=OpenDialog1->FileName;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Button1Click(TObject *Sender)
{
TPCMWaveHeader pcm;
int iFileHandle,iFileLength,iBytesRead;

iFileHandle = FileOpen(mFileName, fmOpenRead);
iFileLength = FileSeek(iFileHandle, 0, 2) - FileSeek(iFileHandle, 0, 0);
Buffer = new char[iFileLength + 1];
FileRead(iFileHandle, Buffer, iFileLength); //把文件读到buffer
FileClose(iFileHandle);
Move(Buffer, &pcm, sizeof(pcm)); //copy  buffer to pcm
Data = new BYTE[pcm.dLen];
setmem(Data, pcm.dLen, 0);
Move(Buffer, Data, pcm.dLen);
//显示头信息
Memo1->Lines->Clear();
Memo1->Lines->Add("FileName:" + mFileName);
Memo1->Lines->Add("RIFF:"+AnsiString(pcm.rID));
Memo1->Lines->Add("RLEN:"+IntToStr(pcm.rLen));
Memo1->Lines->Add("WAVE:"+AnsiString(pcm.wID));
Memo1->Lines->Add("fmt:"+AnsiString(pcm.fID));
Memo1->Lines->Add("Fixed:" + IntToStr(pcm.fLen));
Memo1->Lines->Add("wFormatTag:" + IntToStr(pcm.wFormatTag));
Memo1->Lines->Add("nChannels:" + IntToStr(pcm.nChannels));
Memo1->Lines->Add("nSamplesPerSec:" + IntToStr(pcm.nSamplesPerSec));
Memo1->Lines->Add("nAvgBytesPerSec:" + IntToStr(pcm.nAvgBytesPerSec));
Memo1->Lines->Add("nBlockAlign:" + IntToStr(pcm.nBlockAlign));
Memo1->Lines->Add("nBitsPerSample:" + IntToStr(pcm.nBitsPerSample));
Memo1->Lines->Add("dLen:" + IntToStr(pcm.dLen));
PaintBox1->Canvas->FillRect(PaintBox1->ClientRect);
for (int i=0;i<pcm.dLen;i++)
{
PaintBox1->Canvas->MoveTo(i,128);
PaintBox1->Canvas->LineTo(i, 256-Data[i]);

}
//画波形图
delete[] Buffer;
delete[] Data;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Button3Click(TObject *Sender)
{
Close();
}
//---------------------------------------------------------------------------


void __fastcall TForm1::FormDestroy(TObject *Sender)
{
   if(NULL==Buffer)
   {
       delete[] Buffer;
       Buffer=NULL;
   }
   if(NULL==Data)
   {
       delete[] Data;
       Data=NULL;
   }
}
//---------------------------------------------------------------------------

#27


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

#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <Dialogs.hpp>
#include <ExtCtrls.hpp>
//---------------------------------------------------------------------------
struct TPCMWaveHeader{
// 'RIFF' 标志
char rID[4];
// 文件长度
long int rLen;
// 'WAVE' 标志
char wID[4];
// 'fmt '标志 以空格结束(fID[3]:=Chr($20);)
char fID[4];
// 过度字节(不定)
long int fLen;
// 格式字节(过度字节为PCM形式的声音数据)
WORD wFormatTag;
// 通道数 单声道=1, 双声道=2 }
WORD nChannels;
// 采样频率 (每秒样本数),表示每个通道的播放速度
long int nSamplesPerSec;
// 波形音频数据传送速度(值:通道数X每秒数据位数X每
// 样本的数据位数/8。播放软件利用根据此值估计缓
// 冲区的大小)
long int  nAvgBytesPerSec;
// 数据块的调整数(按字节计算),值为通道数*没样本的数据位值/8。
// 播放软件需要一次处理多个该值大小的字节数据,以便
// 将其值用于缓冲区的调整。
WORD nBlockAlign;
// 每样本数据位数,表示每个声道中各个样本的数据位
// 数。如果有多个声道,对每个声道而言,样本大
// 小都一样 8 or 16
WORD nBitsPerSample;
 // 'data' 标志
char dId[4];
// 语音数据长度
long int dLen;
} ;
class TForm1 : public TForm
{
__published: // IDE-managed Components
        TButton *Button1;
        TPaintBox *PaintBox1;
        TMemo *Memo1;
        TButton *Button2;
        TButton *Button3;
        TOpenDialog *OpenDialog1;
        void __fastcall Button2Click(TObject *Sender);
        void __fastcall Button1Click(TObject *Sender);
        void __fastcall Button3Click(TObject *Sender);
        void __fastcall FormDestroy(TObject *Sender);
private: // User declarations
        AnsiString mFileName;
        char *Buffer;
        BYTE *Data;
public: // User declarations
        __fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif

我改成了BCB的,去掉了一些BUG,不过代码还不是很完善,大家自己改

#28


这次真的是在XP+BCB6 测试通过,
 robbyzi(红客robby)的2003 系统也试过,没有问题
目前没有试过不可以的wav,
就算Form画不下全部,大家可以参考 flowercity(小唐"菜") 的方法

#29


好贴,,收藏。。

#30


to  robbyzi(红客robby) :
我的代码明显有两个地方写错,你也不告诉我,
if(NULL==Buffer)两个都要!=,我都写成==,低级错误,汗...

#31


好东西收藏。另外那位兄弟有在内存放音的代码呢?共享一下!

#32


好帖子,赶快收藏!

#33


安吉儿师兄。。。

我当时也只是在测一下哪些wav文件哪些文件不行,都没有去细看里面的代码。

#34


这样画出的图形的意义是什么呢?

#35


关注

#1


MMTools控件是不是能支持啊!

#2


先熟悉Wave文件格式,如Wave文件头格式、声音数据格式等
然后以时间作为横坐标,采样点作为纵坐标,然后画点连线。
可以参考《Programming Windows,Fifth Edition,Charles Petzold 》第22章 声音与音乐
需要注意的是Wave文件格式有很多种,可以在网上查找相关资料。

#3


gz

#4


wav文件是属于RIFF文件结构的。

RIFF文件结构和WAVE文件格式

  Windows支持两种RIFF(Resource Interchange File Format,"资源交互文件格式")格式的音频文件:MIDI的RMID文件和波形音频文件格式WAVE文件,其中在计算机领域最常用的数字化声音文件格式是后者,它是微软专门为Windows系统定义的波形文件格式(Waveform Audio),由于其扩展名为"*.wav",因而该类文件也被称为WAVE文件。为了突出重点,有的放矢,本文涉及到的声音文件所指的就是WAVE文件。常见的WAVE语音文件主要有两种,分别对应于单声道(11.025KHz采样率、8Bit的采样值)和双声道(44.1KHz采样率、16Bit的采样值)。这里的采样率是指声音信号在进行"模→数"转换过程中单位时间内采样的次数。采样值是指每一次采样周期内声音模拟信号的积分值。对于单声道声音文件,采样数据为八位的短整数(short int 00H-FFH);而对于双声道立体声声音文件,每次采样数据为一个16位的整数(int),高八位和低八位分别代表左右两个声道。WAVE文件数据块包含以脉冲编码调制(PCM)格式表示的样本。在进行声音编程处理以前,首先让我们来了解一下RIFF文件和WAVE文件格式。

  RIFF文件结构可以看作是树状结构,其基本构成是称为"块"(Chunk)的单元,每个块有"标志符"、"数据大小"及"数据"所组成,块的结构如图1所示:

块的标志符(4BYTES) 
数据大小 (4BYTES) 
数据 
 图一、 块的结构示意图

  从上图可以看出,其中"标志符"为4个字符所组成的代码,如"RIFF","LIST"等,指定块的标志ID;数据大小用来指定块的数据域大小,它的尺寸也为4个字符;数据用来描述具体的声音信号,它可以由若干个子块构成,一般情况下块与块是平行的,不能相互嵌套,但是有两种类型的块可以嵌套子块,他们是"RIFF"或"LIST"标志的块,其中RIFF块的级别最高,它可以包括LIST块。另外,RIFF块和LIST块与其他块不同,RIFF块的数据总是以一个指定文件中数据存储格式的四个字符码(称为格式类型)开始,如WAVE文件有一个"WAVE"的格式类型。LIST块的数据总是以一个指定列表内容的4个字符码(称为列表类型)开始,例如扩展名为".AVI"的视频文件就有一个"strl"的列表类型。RIFF和LIST的块结构如下:

RIFF/LIST标志符 
数据1大小 
数据1  格式/列表类型 
数据 
图二、RIFF/LIST块结构

  WAVE文件是非常简单的一种RIFF文件,它的格式类型为"WAVE"。RIFF块包含两个子块,这两个子块的ID分别是"fmt"和"data",其中"fmt"子块由结构PCMWAVEFORMAT所组成,其子块的大小就是sizeofof(PCMWAVEFORMAT),数据组成就是PCMWAVEFORMAT结构中的数据。WAVE文件的结构如下图三所示:

标志符(RIFF) 
数据大小 
格式类型("WAVE") 
"fmt" 
Sizeof(PCMWAVEFORMAT) 
PCMWAVEFORMAT 
"data" 
声音数据大小 
声音数据 
 图三、WAVE文件结构

  PCMWAVEFORMAT结构定义如下:

Typedef struct
{
WAVEFORMAT wf;//波形格式;
WORD wBitsPerSample;//WAVE文件的采样大小;
}PCMWAVEFORMAT;
WAVEFORMAT结构定义如下:
typedef struct
{
WORD wFormatag;//编码格式,包括WAVE_FORMAT_PCM,WAVEFORMAT_ADPCM等
WORD nChannls;//声道数,单声道为1,双声道为2;
DWORD nSamplesPerSec;//采样频率;
DWORD nAvgBytesperSec;//每秒的数据量;
WORD nBlockAlign;//块对齐;
}WAVEFORMAT; 

  "data"子块包含WAVE文件的数字化波形声音数据,其存放格式依赖于"fmt"子块中wFormatTag成员指定的格式种类,在多声道WAVE文件中,样本是交替出现的。如16bit的单声道WAVE文件和双声道WAVE文件的数据采样格式分别如图四所示:

  16位单声道:

采样一 采样二  …… 
低字节 高字节 低字节 高字节  …… 

  16位双声道:

采样一 …… 
左声道 右声道 …… 
低字节 高字节 低字节 高字节  …… 

                 图四、WAVE文件数据采样格式

#5


偶比较关心:谁有代码哈?

#6


我有点资料,显示系统声音的波形图,
如果你认为对你帮助留个email
wav的我也想知道,没有接触过

#7


richarsun@163.com

如果是程序那就最好了。。呵呵。

#8


发了记得告诉我哦。。师兄。。。

#9


没有问题,等一下我就发给你,是代码来的,可惜不是读wav的

#10


我发了,你去收了

#11


想读WAV的文件

先知道RIFF的文件格式

然后用API的mmioOpen,mmioRead,mmioSeek,mmioWrite,等
函数来操作

有个例子你看一下  不过例子不全对里面有错误
BYTE * GetData(Cstring *pString) 
//获取声音文件数据的函数,pString参数指向要打开的声音文件;
{
if (pString==NULL)
return NULL;
HMMIO file1;//定义HMMIO文件句柄;
file1=mmioOpen((LPSTR)pString,NULL,MMIO_READWRITE);//以读写模式打开所给的WAVE文件;
if(file1==NULL)
{
MessageBox("WAVE文件打开失败!");
Return NULL;
}
char style[4];//定义一个四字节的数据,用来存放文件的类型;
mmioSeek(file1,8,SEEK_SET);//定位到WAVE文件的类型位置
mmioRead(file1,style,4);
if(style[0]!=’W’||style[1]!=’A’||style[2]!=’V’||style[3]!=’E’)//判断该文件是否为"WAVE"文件格式
{
MessageBox("该文件不是WAVE格式的文件!");
Return NULL;
}
PCMWAVEFORMAT format; //定义PCMWAVEFORMAT结构对象,用来判断WAVE文件格式;
mmioSeek(file1,20,SEEK_SET);
//对打开的文件进行定位,此时指向WAVE文件的PCMWAVEFORMAT结构的数据;
mmioRead(file1,(char*)&format,sizeof(PCMWAVEFORMAT));//获取该结构的数据;
if(format.wf.nChannels!=2)//判断是否是立体声声音;
{
MessageBox("该声音文件不是双通道立体声文件");
return NULL;
}
mmioSeek(file1,24+sizeof(PCMWAVEFORMAT),SEEK_SET);
//获取WAVE文件的声音数据的大小;
long size;
mmioRead(file1,(char*)&size,4);
BYTE *pData;
pData=(BYTE*)new char[size];//根据数据的大小申请缓冲区;
mmioSeek(file1,28+sizeof(PCMWAVEFORMAT),SEEK_SET);//对文件重新定位;
mmioRead(file1,(char*)pData,size);//读取声音数据;
mmioClose(file1, MMIO_FHOPEN);//关闭WAVE文件;
return pData;
}

#12


支持

#13


回复人: constantine(飘遥的安吉儿) 

我要。。

richarson_001@163.com

TO:红客robby。。我才是楼主。。-_-###

#14


发了

#15


c++builder程序设计经典
这本书上有代码

#16


顶,顶。。。

#17


constantine(飘遥的安吉儿) :
给我也来一份
net205@126.com

#18


我也要一份!
yuruilei@21cn.com

#19


代码我就不再发了,那个不是wave的,下面我给大家贴个是的,并画出图来,不过是delphi的

unit Unit1;

interface

uses

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

StdCtrls, ExtCtrls;

type

TPCMWaveHeader = record

rID: array[0..3] of char; // 'RIFF' 标志

rLen: longint; // 文件长度

wID: array[0..3] of char; // 'WAVE' 标志

fId: array[0..3] of char; // 'fmt '标志 以空格结束(fID[3]:=Chr($20);)

fLen: longint; // 过度字节(不定)

wFormatTag: word; // 格式字节(过度字节为PCM形式的声音数据)

nChannels: word; // 通道数 单声道=1, 双声道=2 }

nSamplesPerSec: longint; // 采样频率 (每秒样本数),表示每个通道的播放速度

nAvgBytesPerSec: longint; // 波形音频数据传送速度(值:通道数X每秒数据位数X每

// 样本的数据位数/8。播放软件利用根据此值估计缓

// 冲区的大小)

nBlockAlign: word; // 数据块的调整数(按字节计算),值为通道数*没样本的数据位值/8。

// 播放软件需要一次处理多个该值大小的字节数据,以便

// 将其值用于缓冲区的调整。

nBitsPerSample: word; // 每样本数据位数,表示每个声道中各个样本的数据位

// 数。如果有多个声道,对每个声道而言,样本大

// 小都一样 8 or 16

dId: array[0..3] of char; // 'data' 标志

dLen: longint; // 语音数据长度

end;

TForm1 = class(TForm)

Button2: TButton;
    Memo1: TMemo;

procedure Button2Click(Sender: TObject);

procedure FormCreate(Sender: TObject);

private

filename: string;

pcm: TPCMWaveHeader;

Data: array[0..80 * 10240] of Byte;

procedure PrintPcm;

procedure PrintData;

public

{ Public declarations }

end;

var

Form1 : TForm1;

implementation

{$R *.DFM}

procedure TForm1.PrintPcm;

begin

// memo1.lines.add('FileName:' + Filename);

memo1.lines.add('RIFF:' + pcm.rID);

memo1.lines.add('RLEN:' + inttostr(pcm.rLen));

memo1.lines.add('WAVE:' + pcm.wID);

memo1.lines.add('fmt:' + pcm.fId);

memo1.lines.add('Fixed:' + inttostr(pcm.fLen));

memo1.lines.add('wFormatTag:' + inttostr(pcm.wFormatTag));

memo1.lines.add('nChannels:' + inttostr(pcm.nChannels));

memo1.lines.add('nSamplesPerSec:' + inttostr(pcm.nSamplesPerSec));

memo1.lines.add('nAvgBytesPerSec:' + inttostr(pcm.nAvgBytesPerSec));

memo1.lines.add('nBlockAlign:' + inttostr(pcm.nBlockAlign));

memo1.lines.add('nBitsPerSample:' + inttostr(pcm.nBitsPerSample));

memo1.lines.add('dLen:' + inttostr(pcm.dLen));

end;

procedure TForm1.Button2Click(Sender: TObject);

var

iFileHandle : Integer;

iFileLength : Integer;

iBytesRead : Integer;

Buffer : PChar;

//i, Size : Integer;

begin

iFileHandle := FileOpen(filename, fmOpenRead);

iFileLength := FileSeek(iFileHandle, 0, 2) - FileSeek(iFileHandle, 0, 0);

GetMem(Buffer, iFileLength + 1);

iBytesRead := FileRead(iFileHandle, Buffer^, iFileLength); //把文件读到buffer

FileClose(iFileHandle);

Move((Buffer)^, pcm, sizeof(pcm));

Move((Buffer)^, Data, pcm.dLen);

printpcm; //显示头信息

printData; //画波形图

FreeMem(Buffer);

end;

procedure TForm1.FormCreate(Sender: TObject);

begin

filename := 'C:\Windows XP 登录音.wav';

end;

procedure TForm1.PrintData;

var

i : Integer;

begin

for i := 0 to pcm.dLen - 1 do

begin

//memo1.lines.add(inttostr(Data[i]));

Canvas.LineTo(i, Data[i])

end;

end;

end.

#20


只要在form上放一个button,一个memo,把他们的事件对应上就可以了
我在XP+bcb6 测试通过

#21


如果满意记得提交FAQ,^_^

#22


回复人: constantine(飘遥的安吉儿) ( ) 信誉:101 
只要在form上放一个button,一个memo,把他们的事件对应上就可以了
我在XP+bcb6 测试通过

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

不是Delphi7中通过吗????

#23


哈哈,在这里习惯了,

#24


还是我来贴代码吧
呵呵。。。。。。。。。。。。。

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

#ifndef U14_4_2H
#define U14_4_2H
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <MPlayer.hpp>
//---------------------------------------------------------------------------
class Tf14_4_2 : public TForm
{
__published: // IDE-managed Components
        TLabel *Label1;
        TLabel *Label2;
        TButton *btnEnd;
        TButton *btnShowWave;
        TScrollBar *Srb;
        TEdit *Edit1;
        TEdit *Edit2;
        TMediaPlayer *MediaPlayer1;
        void __fastcall FormCreate(TObject *Sender);
        void __fastcall MediaPlayer1Click(TObject *Sender,
          TMPBtnType Button, bool &DoDefault);
        void __fastcall btnShowWaveClick(TObject *Sender);
        void __fastcall SrbChange(TObject *Sender);
        void __fastcall Edit1Change(TObject *Sender);
        void __fastcall Edit2Change(TObject *Sender);
        void __fastcall btnEndClick(TObject *Sender);
private: // User declarations
public: // User declarations
        __fastcall Tf14_4_2(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE Tf14_4_2 *f14_4_2;
//---------------------------------------------------------------------------
#endif



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

#include <vcl.h>
#pragma hdrstop

#include "U14_4_2.h"
#include <stdio.h>
#include<io.h>
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"

int FileLen,dx=0,dy=200,dd=44;
int divx,divy;
char* file_name="test.WAV";
short *data;
FILE *inf;

Tf14_4_2 *f14_4_2;
//---------------------------------------------------------------------------
__fastcall Tf14_4_2::Tf14_4_2(TComponent* Owner)
        : TForm(Owner)
{
}
//---------------------------------------------------------------------------
void show(void)
{
  f14_4_2->Refresh();
  f14_4_2->Canvas->TextOut(10,400,IntToStr(f14_4_2->Srb->Position) );

  divx=StrToInt(f14_4_2->Edit1->Text);
  divy=StrToInt(f14_4_2->Edit2->Text);

  f14_4_2->Canvas->MoveTo(0+dx,-data[0]/divy+dy);
  f14_4_2->Canvas->Pen->Color=clBlue;
  for(int i=1;(i<f14_4_2->Width*divx)&&(i<(FileLen-dd)/2-1);i++)
    f14_4_2->Canvas->LineTo(i/divx+dx,-data[i+f14_4_2->Srb->Position]/divy+dy);
}
//---------------------------------------------------------------------------
void __fastcall Tf14_4_2::FormCreate(TObject *Sender)
{
   bool open_flag;
  int handle;

  if(open_flag==true)
  {
    fclose(inf);
  }
  inf=fopen(file_name,"r");
  open_flag=true;
  handle=fileno(inf);
  FileLen= filelength(handle);

  if(feof(inf))
  {
    fclose(inf);
    open_flag=false;
  }

  data = new short[(FileLen-dd)/2];

  fseek(inf,dd,SEEK_SET);
  fread(data,sizeof(short),(FileLen-dd)/2,inf);

  MediaPlayer1->VisibleButtons=Mplayer::TButtonSet()<<btPlay<<btStop<<btPause;
  MediaPlayer1->DeviceType=dtWaveAudio;
  MediaPlayer1->FileName=file_name;
  MediaPlayer1->Display=f14_4_2;
  MediaPlayer1->Open();

  if((FileLen-dd)-f14_4_2->Width*divx<0)
    Srb->Max=0;
  else
    Srb->Max=(FileLen-dd)/2-f14_4_2->Width*divx;
}
//---------------------------------------------------------------------------
void __fastcall Tf14_4_2::MediaPlayer1Click(TObject *Sender,
      TMPBtnType Button, bool &DoDefault)
{
    if(!MediaPlayer1->AutoOpen)
     MediaPlayer1->Play();
}
//---------------------------------------------------------------------------
void __fastcall Tf14_4_2::btnShowWaveClick(TObject *Sender)
{
  show();
}
//---------------------------------------------------------------------------



void __fastcall Tf14_4_2::SrbChange(TObject *Sender)
{
  if(((FileLen-dd)/2-f14_4_2->Width*divx)<0)
    Srb->Max=0;
  else
    Srb->Max=(FileLen-dd)/2-f14_4_2->Width*divx;

  show();
}
//---------------------------------------------------------------------------


void __fastcall Tf14_4_2::Edit1Change(TObject *Sender)
{
   if(((FileLen-dd)/2-f14_4_2->Width*divx)<0)
    Srb->Max=0;
  else
    Srb->Max=(FileLen-dd)/2-f14_4_2->Width*divx;

  f14_4_2->Srb->Position=0;
  show();
}
//---------------------------------------------------------------------------
void __fastcall Tf14_4_2::Edit2Change(TObject *Sender)
{
   f14_4_2->Srb->Position=0;
  show();
}
//---------------------------------------------------------------------------
void __fastcall Tf14_4_2::btnEndClick(TObject *Sender)
{
  delete data;
  fclose(inf);
  Close();
}
//---------------------------------------------------------------------------

#25


记得揭帖啊。。。。。。。
我在XP+BCB6下编译通过

#26


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

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Button2Click(TObject *Sender)
{
      if(OpenDialog1->Execute())
         mFileName=OpenDialog1->FileName;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Button1Click(TObject *Sender)
{
TPCMWaveHeader pcm;
int iFileHandle,iFileLength,iBytesRead;

iFileHandle = FileOpen(mFileName, fmOpenRead);
iFileLength = FileSeek(iFileHandle, 0, 2) - FileSeek(iFileHandle, 0, 0);
Buffer = new char[iFileLength + 1];
FileRead(iFileHandle, Buffer, iFileLength); //把文件读到buffer
FileClose(iFileHandle);
Move(Buffer, &pcm, sizeof(pcm)); //copy  buffer to pcm
Data = new BYTE[pcm.dLen];
setmem(Data, pcm.dLen, 0);
Move(Buffer, Data, pcm.dLen);
//显示头信息
Memo1->Lines->Clear();
Memo1->Lines->Add("FileName:" + mFileName);
Memo1->Lines->Add("RIFF:"+AnsiString(pcm.rID));
Memo1->Lines->Add("RLEN:"+IntToStr(pcm.rLen));
Memo1->Lines->Add("WAVE:"+AnsiString(pcm.wID));
Memo1->Lines->Add("fmt:"+AnsiString(pcm.fID));
Memo1->Lines->Add("Fixed:" + IntToStr(pcm.fLen));
Memo1->Lines->Add("wFormatTag:" + IntToStr(pcm.wFormatTag));
Memo1->Lines->Add("nChannels:" + IntToStr(pcm.nChannels));
Memo1->Lines->Add("nSamplesPerSec:" + IntToStr(pcm.nSamplesPerSec));
Memo1->Lines->Add("nAvgBytesPerSec:" + IntToStr(pcm.nAvgBytesPerSec));
Memo1->Lines->Add("nBlockAlign:" + IntToStr(pcm.nBlockAlign));
Memo1->Lines->Add("nBitsPerSample:" + IntToStr(pcm.nBitsPerSample));
Memo1->Lines->Add("dLen:" + IntToStr(pcm.dLen));
PaintBox1->Canvas->FillRect(PaintBox1->ClientRect);
for (int i=0;i<pcm.dLen;i++)
{
PaintBox1->Canvas->MoveTo(i,128);
PaintBox1->Canvas->LineTo(i, 256-Data[i]);

}
//画波形图
delete[] Buffer;
delete[] Data;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Button3Click(TObject *Sender)
{
Close();
}
//---------------------------------------------------------------------------


void __fastcall TForm1::FormDestroy(TObject *Sender)
{
   if(NULL==Buffer)
   {
       delete[] Buffer;
       Buffer=NULL;
   }
   if(NULL==Data)
   {
       delete[] Data;
       Data=NULL;
   }
}
//---------------------------------------------------------------------------

#27


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

#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <Dialogs.hpp>
#include <ExtCtrls.hpp>
//---------------------------------------------------------------------------
struct TPCMWaveHeader{
// 'RIFF' 标志
char rID[4];
// 文件长度
long int rLen;
// 'WAVE' 标志
char wID[4];
// 'fmt '标志 以空格结束(fID[3]:=Chr($20);)
char fID[4];
// 过度字节(不定)
long int fLen;
// 格式字节(过度字节为PCM形式的声音数据)
WORD wFormatTag;
// 通道数 单声道=1, 双声道=2 }
WORD nChannels;
// 采样频率 (每秒样本数),表示每个通道的播放速度
long int nSamplesPerSec;
// 波形音频数据传送速度(值:通道数X每秒数据位数X每
// 样本的数据位数/8。播放软件利用根据此值估计缓
// 冲区的大小)
long int  nAvgBytesPerSec;
// 数据块的调整数(按字节计算),值为通道数*没样本的数据位值/8。
// 播放软件需要一次处理多个该值大小的字节数据,以便
// 将其值用于缓冲区的调整。
WORD nBlockAlign;
// 每样本数据位数,表示每个声道中各个样本的数据位
// 数。如果有多个声道,对每个声道而言,样本大
// 小都一样 8 or 16
WORD nBitsPerSample;
 // 'data' 标志
char dId[4];
// 语音数据长度
long int dLen;
} ;
class TForm1 : public TForm
{
__published: // IDE-managed Components
        TButton *Button1;
        TPaintBox *PaintBox1;
        TMemo *Memo1;
        TButton *Button2;
        TButton *Button3;
        TOpenDialog *OpenDialog1;
        void __fastcall Button2Click(TObject *Sender);
        void __fastcall Button1Click(TObject *Sender);
        void __fastcall Button3Click(TObject *Sender);
        void __fastcall FormDestroy(TObject *Sender);
private: // User declarations
        AnsiString mFileName;
        char *Buffer;
        BYTE *Data;
public: // User declarations
        __fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif

我改成了BCB的,去掉了一些BUG,不过代码还不是很完善,大家自己改

#28


这次真的是在XP+BCB6 测试通过,
 robbyzi(红客robby)的2003 系统也试过,没有问题
目前没有试过不可以的wav,
就算Form画不下全部,大家可以参考 flowercity(小唐"菜") 的方法

#29


好贴,,收藏。。

#30


to  robbyzi(红客robby) :
我的代码明显有两个地方写错,你也不告诉我,
if(NULL==Buffer)两个都要!=,我都写成==,低级错误,汗...

#31


好东西收藏。另外那位兄弟有在内存放音的代码呢?共享一下!

#32


好帖子,赶快收藏!

#33


安吉儿师兄。。。

我当时也只是在测一下哪些wav文件哪些文件不行,都没有去细看里面的代码。

#34


这样画出的图形的意义是什么呢?

#35


关注