怎样从wav文件中提取记录频率的数据?(200分)

  • 主题发起人 主题发起人 tsxq
  • 开始时间 开始时间
Wave文件头的第$18~$19和$1C~$1D字节记录WAVE的采样频率
 
我有代码+例子
 
WAVFile.pas单元
===============================================================================
unit WAVFile;

interface

uses
Classes, SysUtils;

const
{ Used with ChannelMode property }
CHANNEL_MODE_MONO = 1;
{ Index for mono mode }
CHANNEL_MODE_STEREO = 2;
{ Index for stereo mode }

{ Channel mode names }
CHANNEL_MODE: array [0..2] of string = ('Unknown', 'Mono', 'Stereo');

type
{ Class TWAVFile }
TWAVFile = class(TObject)
private
{ Private declarations }
FValid: Boolean;
FChannelModeID: Byte;
FSampleRate: Word;
FBitsPerSample: Byte;
FFileSize: Cardinal;
procedure FResetData;
function FGetChannelMode: string;
function FGetDuration:do
uble;
public
{ Public declarations }
constructor Create;
{ Create object }
function ReadFromFile(const FileName: string): Boolean;
{ Load header }
property Valid: Boolean read FValid;
{ True if header valid }
property ChannelModeID: Byte read FChannelModeID;
{ Channel mode code }
property ChannelMode: string read FGetChannelMode;
{ Channel mode name }
property SampleRate: Word read FSampleRate;
{ Sample rate (hz) }
property BitsPerSample: Byte read FBitsPerSample;
{ Bits per sample }
property FileSize: Cardinal read FFileSize;
{ File size (bytes) }
property Duration:do
uble read FGetDuration;
{ Duration (seconds) }
end;


implementation

type
{ Real structure of WAV file header }
WAVRecord = record
{ RIFF file header }
RIFFHeader: array [1..4] of Char;
{ Must be "RIFF"
}
FileSize: Integer;
{ Must be "RealFileSize - 8"
}
WAVEHeader: array [1..4] of Char;
{ Must be "WAVE"
}
{ Format information }
FormatHeader: array [1..4] of Char;
{ Must be "fmt "
}
FormatSize: Integer;
{ Must be 16 (decimal) }
FormatCode: Word;
{ Must be 1 }
ChannelNumber: Word;
{ Number of channels }
SampleRate: Integer;
{ Sample rate (hz) }
BytesPerSecond: Integer;
{ Bytes per second }
BytesPerSample: Word;
{ Bytes per Sample }
BitsPerSample: Word;
{ Bits per sample }
{ Data area }
DataHeader: array [1..4] of Char;
{ Must be "data"
}
DataSize: Integer;
{ Data size }
end;


{ ********************* Auxiliary functions & procedures ******************** }

function ReadWAV(const FileName: string;
var WAVData: WAVRecord): Boolean;
var
SourceFile: file;
Transferred: Integer;
begin

try
Result := true;
{ Set read-access and open file }
AssignFile(SourceFile, FileName);
FileMode := 0;
Reset(SourceFile, 1);
{ Read header }
BlockRead(SourceFile, WAVData, 44, Transferred);
CloseFile(SourceFile);
{ if transfer is not complete }
if Transferred < 44 then
Result := false;
except
{ Error }
Result := false;
end;

end;


{ --------------------------------------------------------------------------- }

function HeaderIsValid(const WAVData: WAVRecord): Boolean;
begin

Result := true;
{ Validation }
if WAVData.RIFFHeader <> 'RIFF' then
Result := false;
if WAVData.WAVEHeader <> 'WAVE' then
Result := false;
if WAVData.FormatHeader <> 'fmt ' then
Result := false;
if WAVData.FormatSize <> 16 then
Result := false;
if WAVData.FormatCode <> 1 then
Result := false;
if WAVData.DataHeader <> 'data' then
Result := false;
if (WAVData.ChannelNumber <> CHANNEL_MODE_MONO) and
(WAVData.ChannelNumber <> CHANNEL_MODE_STEREO) then
Result := false;
end;


{ ********************** Private functions & procedures ********************* }

procedure TWAVFile.FResetData;
begin

FValid := false;
FChannelModeID := 0;
FSampleRate := 0;
FBitsPerSample := 0;
FFileSize := 0;
end;


{ --------------------------------------------------------------------------- }

function TWAVFile.FGetChannelMode: string;
begin

Result := CHANNEL_MODE[FChannelModeID];
end;


{ --------------------------------------------------------------------------- }

function TWAVFile.FGetDuration:do
uble;
begin

if FValid then

Result := (FFileSize - 44) * 8 /
FSampleRate / FBitsPerSample / FChannelModeID
else

Result := 0;
end;


{ ********************** Public functions & procedures ********************** }

constructor TWAVFile.Create;
begin

inherited;
FResetData;
end;


{ --------------------------------------------------------------------------- }

function TWAVFile.ReadFromFile(const FileName: string): Boolean;
var
WAVData: WAVRecord;
begin

{ Reset and load header data from file to variable }
FResetData;
Result := ReadWAV(FileName, WAVData);
{ Process data if loaded and header valid }
if (Result) and (HeaderIsValid(WAVData)) then

begin

FValid := true;
{ Fill properties with header data }
FChannelModeID := WAVData.ChannelNumber;
FSampleRate := WAVData.SampleRate;
FBitsPerSample := WAVData.BitsPerSample;
FFileSize := WAVData.FileSize + 8;
end;

end;


end.

============================================================================
使用:
unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, WAVFile;

type
TForm1 = class(TForm)
Button1: TButton;
OpenDialog1: TOpenDialog;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;


var
Form1: TForm1;

implementation

{$R *.dfm}

procedure SetWavInfo(FilesPath:string);
var
WAVFile: TWAVFile;
begin

WAVFile := TWAVFile.Create;
//获取WAV文件信息
if FileExists(FilesPath) then

if WAVFile.ReadFromFile(FilesPath) then

if WAVFile.Valid then

begin

ShowMessage(' 文件: '+ExtractFileName(FilesPath)+' '+
#13+#13+' 大小: '+IntToStr(WAVFile.FileSize)+'字节'+
#13+#13+' 声道: '+WAVFile.ChannelMode+
#13+#13+'采样率: '+IntToStr(WAVFile.SampleRate)+'hz'+
#13+#13+'比特率: '+IntToStr(WAVFile.BitsPerSample)+'bit'+
#13+#13+' 时间: '+FormatFloat('.000', WAVFile.Duration)+'秒'+
#13+#13+' 位置: '+ExtractFilePath(FilesPath));
WAVFile.Free;
end else
ShowMessage('不能读取: ' + FilesPath)
else
ShowMessage('文件不存在: ' + FilesPath);
end;


procedure TForm1.Button1Click(Sender: TObject);
begin

if not OpenDialog1.Execute then
exit;
begin

SetWavInfo(OpenDialog1.FileName);
end;

end;


end.
 
网络3K:
  如果我要手工修改文件中存储的左声道的频率数据可以吗?
  能够可否告之方法?
 
TWavHeader = record //定义一个Wav文件头格式 44字节
rId : longint;
//4字节 riff
rLen : longint;
//4字节
wId : longint;
//4字节 wave
fId : longint;
//4字节 fmt
fLen : longint;
//4字节
wFormatTag : word;
//2字节 WFormatTag用来设置wav格式
nChannels : word;
//2字节 nChannels用来设置声道个数;
[red]nSamplesPerSec : longint;
//4字节 nSamplesPerSec用来设置采样频率;[/red]
nAvgBytesPerSec : longint;
//4字节 nAvgBytesPerSec用来设置每秒所需字节数;
nBlockAlign : word;
//2字节 nBlockAlign用来设置每个采样点所需总字节数
wBitsPerSample : word;
//2字节 wBitsPerSample用来设置每个采样点所需Bit数。
dId : longint;
//4字节 data
wSampleLength : longint;
//4字节
end;
 
WAVFile.pas单元没有写信息的啊!
 
// WaveStereo2Mono.cpp: implementation of the CWaveStereo2Mono class.
//
//////////////////////////////////////////////////////////////////////

#include &quot;stdafx.h&quot;
#include &quot;WAVStereo2Mono.h&quot;
#include &quot;WaveStereo2Mono.h&quot;
#include <io.h>

// 为 MCI 函数添加库文件
#include <mmsystem.h>
#pragma comment ( lib, &quot;winmm.lib&quot;
)

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CWaveStereo2Mono::CWaveStereo2Mono ()
{

}

CWaveStereo2Mono::~CWaveStereo2Mono()
{

}

//
// 如果指定了文件名就直接返回,否则打开一个对话框来让用户选择一个文件
//
CString CWaveStereo2Mono::GetFileName(BOOL bOpen, LPCTSTR lpszFileName/*=NULL*/)
{
if (
lpszFileName && strlen(lpszFileName) > 0 &&
( ( bOpen && _access ( lpszFileName, 0 ) == 0 ) || !bOpen )
)
{
return lpszFileName;
}

char szCurDir[MAX_PATH] = {0};
DWORD dwRet = ::GetModuleFileName(NULL,(LPTSTR)szCurDir,sizeof(szCurDir));
if ( szCurDir[dwRet-1] != '//' )
strncat ( szCurDir, &quot;//&quot;, sizeof(szCurDir) );

CString csDefaultFileName = _T( &quot;chrys.wav&quot;
);
char *szFilter = &quot;*.wav|*.wav||&quot;;
CFileDialog FileDlg ( bOpen, &quot;.wav&quot;, csDefaultFileName, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, szFilter, NULL );
FileDlg.m_ofn.lpstrInitialDir = szCurDir;
if ( FileDlg.DoModal () != IDOK )
return &quot;&quot;;
return FileDlg.GetPathName();
}

//
// 获取wave文件头,返回PCM数据的偏移地址,失败时返回 -1,用户放弃返回0
//
int CWaveStereo2Mono::GetWaveFileHeader ( OUT WAVEFORMATEX &wf, OUT DWORD &dwPCMDataSize, IN LPCTSTR lpszFileName/*=NULL*/ )
{
CString csFileName = GetFileName ( TRUE, lpszFileName );
if ( csFileName.IsEmpty() ) return 0;
char szHeaderData[1024] = {0};
dwPCMDataSize = 0;

CFile File;
TRY
{
if ( !File.Open ( csFileName, CFile::modeRead |CFile::shareDenyNone ) )
return -1;
if ( File.Read ( szHeaderData, sizeof(szHeaderData) ) < 44 )
return IsNotWaveFile ();
File.Close ();
}
CATCH ( CFileException, e )
{
#ifdef _DEBUG
afxDump << &quot;Handle file failed.&quot;
<< e->m_cause << &quot;/n&quot;;
#endif
}
END_CATCH

LPDWORD pdw = NULL, pdwEnd = NULL;
DWORD dwRiff = 0, dwType = 0, dwLength = 0;
pdw = (DWORD *) szHeaderData;
dwRiff = *pdw++;
dwLength = *pdw++;
dwType = *pdw++;

// 判断文件头是否为&quot;RIFF&quot;字符
if ( dwRiff != mmioFOURCC ('R', 'I', 'F', 'F') )
return IsNotWaveFile();
// 判断文件格式是否为&quot;WAVE&quot;
if ( dwType != mmioFOURCC ('W', 'A', 'V', 'E') )
return IsNotWaveFile();

// 寻找格式块, 数据块位置及数据长度
pdwEnd = (DWORD *)( (BYTE *) szHeaderData+dwLength - 4 );
BOOL m_bend = FALSE;
int nOffset = -1;

// pdw文件没有指到末尾并且没有获取到声音数据时继续;
while ( (pdw < pdwEnd) && (!m_bend) )
{
dwType = *pdw++;
dwLength = *pdw++;
switch (dwType)
{
// 如果为&quot;fmt&quot;标志
case mmioFOURCC('f', 'm', 't', ' '):
if (dwLength < sizeof (WAVEFORMAT) )
return IsNotWaveFile();
memcpy ( &wf, pdw, sizeof (WAVEFORMATEX) );
break;
// 如果为&quot;data&quot;标志
case mmioFOURCC('d', 'a', 't', 'a'):
if ( dwPCMDataSize < 1 )
{
dwPCMDataSize = dwLength;
// 获取声音数据块的长度
m_bend = TRUE;
nOffset = ( (DWORD)pdw - (DWORD)szHeaderData );
}
break;
}
pdw = (DWORD *)((BYTE *) pdw + ((dwLength + 1)&~1));//修改pdw指针,继续循环
}

if ( !m_bend ) return IsNotWaveFile ();

return nOffset;
}

BOOL CWaveStereo2Mono::SaveWaveFileHeader ( IN WAVEFORMATEX &wf, IN DWORD dwFileSize, IN DWORD dwPCMDataSize, IN CFile &File )
{
File.Write ( &quot;RIFF&quot;, 4 );
File.Write ( &dwFileSize, sizeof(DWORD) );
File.Write ( &quot;WAVEfmt &quot;, 8 );
DWORD dwLength = sizeof(WAVEFORMATEX);
File.Write ( &dwLength, sizeof(DWORD) );
File.Write ( &wf, sizeof(WAVEFORMATEX) );
File.Write ( &quot;data&quot;, 4 );
File.Write ( &dwPCMDataSize, sizeof(DWORD) );

return TRUE;
}

int CWaveStereo2Mono::IsNotWaveFile()
{
#ifdef _DEBUG
AfxMessageBox ( &quot;Is not wave file&quot;
);
#endif
return -1;
}

BOOL CWaveStereo2Mono::CopyChannelData (
IN LPVOID lpData, // 需要处理的PCM数据
IN int nSize, // PCM数据长度
IN int nSampleSize, // 取样大小
OUT LPVOID lpLeftData, // 保存左声道数据缓冲,该缓冲不能小于nSize的一半,最好是和nSize相等
IN OUT int &nLeftSize,
OUT LPVOID lpRightData, // 同上,保存右声道数据
IN OUT int &nRightSize
)
{
if ( !lpData || nSize < 1 || nLeftSize < nSize || nRightSize < nSize )
return FALSE;
ASSERT ( lpLeftData!=NULL && AfxIsValidAddress(lpLeftData,nLeftSize,TRUE));
ASSERT ( lpRightData!=NULL && AfxIsValidAddress(lpRightData,nRightSize,TRUE));

char *pPtr1 = (char*)lpData;
char *pPtr2 = NULL;
int nCountLeft = 0, nCountRight = 0;
for ( int i=0;
i<nSize;
i+=2*nSampleSize )
{
memcpy ( (char*)lpLeftData+nCountLeft, pPtr1, nSampleSize );
nCountLeft += nSampleSize;

pPtr2 = pPtr1 + nSampleSize;
if ( (DWORD)pPtr2 - (DWORD)lpData >= (DWORD)nSize )
break;
memcpy ( (char*)lpRightData+nCountRight, pPtr2, nSampleSize );
nCountRight += nSampleSize;

pPtr1 = pPtr2+nSampleSize;
}

nLeftSize = nCountLeft;
nRightSize = nCountRight;

return TRUE;
}

//
// 提取单声道文件从立体声文件中
// return : --------------------------------------------------------------------------
// 1 - 成功
// 0 - 失败
// -1 - 用户放弃
//
int CWaveStereo2Mono::PickupStereoFile(IN LPCTSTR lpszWaveFileName/*=NULL*/)
{
CString csFileName_Src = GetFileName ( TRUE, lpszWaveFileName );
if ( csFileName_Src.GetLength() < 4 ) return -1;
CString csFileName_Left = csFileName_Src;
csFileName_Left.Insert ( csFileName_Src.GetLength() - 4, &quot;_Left&quot;
);
CString csFileName_Right = csFileName_Src;
csFileName_Right.Insert ( csFileName_Src.GetLength() - 4, &quot;_Right&quot;
);

// 先读取头信息
WAVEFORMATEX wf = {0};
DWORD dwPCMDataSize = 0;
int nOffset = GetWaveFileHeader ( wf, dwPCMDataSize, csFileName_Src );
if ( nOffset < 1 )
return 0;
// 不是立体声wave文件
if ( wf.nChannels != 2 )
{
#ifdef _DEBUG
AfxMessageBox ( &quot;Is not stereo wave file&quot;
);
#endif
return 0;
}

char szTempData[1024] = {0}, szTempData_Left[1024] = {0}, szTempData_Right[1024] = {0};
CFile File, File_Left, File_Right;

TRY
{
File.Open ( csFileName_Src, CFile::modeRead | CFile::shareDenyNone );
File_Left.Open ( csFileName_Left, CFile::modeWrite | CFile::modeCreate );
File_Right.Open ( csFileName_Right, CFile::modeWrite | CFile::modeCreate );

// 先写头数据
SaveWaveFileHeader ( wf, 0, 0, File_Left );
SaveWaveFileHeader ( wf, 0, 0, File_Right );

// 将文件指针移到PCM数据处
File.Seek ( nOffset, CFile::begin
);

// 循环读取文件数据块进行处理
UINT nReadSize = 0;
DWORD nSampleSize = wf.wBitsPerSample / 8;
DWORD dwPCMDataSize_Left = 0, dwPCMDataSize_Right = 0;
while ( (nReadSize = File.Read ( szTempData, sizeof(szTempData) )) > 0 )
{
int nLeftSize = sizeof(szTempData_Left);
int nRightSize = sizeof(szTempData_Right);
VERIFY ( CopyChannelData ( szTempData, nReadSize, nSampleSize, szTempData_Left, nLeftSize, szTempData_Right, nRightSize ) );
File_Left.Write ( szTempData_Left, nLeftSize );
File_Right.Write ( szTempData_Right, nRightSize );
dwPCMDataSize_Left += nLeftSize;
dwPCMDataSize_Right += nRightSize;

}

// 修正头信息
wf.nChannels = 1;
wf.nAvgBytesPerSec /= 2;
wf.nBlockAlign /= 2;
DWORD dwFileSize = dwPCMDataSize_Left + MIN_WAVE_HEADER_SIZE;
File_Left.SeekTobegin
();
SaveWaveFileHeader ( wf, dwFileSize, dwPCMDataSize_Left, File_Left );
File_Right.SeekTobegin
();
dwFileSize = dwPCMDataSize_Right + MIN_WAVE_HEADER_SIZE;
SaveWaveFileHeader ( wf, dwFileSize, dwPCMDataSize_Right, File_Right );
}
CATCH ( CFileException, e )
{
#ifdef _DEBUG
afxDump << &quot;Handle file failed.&quot;
<< e->m_cause << &quot;/n&quot;;
#endif
return 0;
}
END_CATCH

File.Close ();
File_Left.Close ();
File_Right.Close ();

#ifdef _DEBUG
::ShellExecute ( NULL,_T(&quot;open&quot;), csFileName_Right, NULL,NULL,SW_SHOW );
#endif

return 1;
}
 
谢谢41426277。
我想你的方法应该可行,只是我不太懂C语言。
我的目的是要静态修改WAV文件中左声道或右声道的频率。想请你们帮忙。
 
TWavHeader = record //定义一个Wav文件头格式 44字节
rId : longint;
//4字节 riff
rLen : longint;
//4字节
wId : longint;
//4字节 wave
fId : longint;
//4字节 fmt
fLen : longint;
//4字节
wFormatTag : word;
//2字节 WFormatTag用来设置wav格式
nChannels : word;
//2字节 nChannels用来设置声道个数;
[green][/green][black][/black][blue]nSamplesPerSec : longint;
//4字节 nSamplesPerSec用来设置采样频率;[/blue]
nAvgBytesPerSec : longint;
//4字节 nAvgBytesPerSec用来设置每秒所需字节数;
nBlockAlign : word;
//2字节 nBlockAlign用来设置每个采样点所需总字节数
wBitsPerSample : word;
//2字节 wBitsPerSample用来设置每个采样点所需Bit数。
dId : longint;
//4字节 data
wSampleLength : longint;
//4字节
end;

如网中戏所说
但是,改要
 
跟我联系吧:294118403(QQ)
 
后退
顶部