Wave
파일 분석
선문비트예비 21기
김동영
목 차
- Wave 파일이란?
- Wave 포맷 구성
- Wave 포맷 분석
- Wave 포맷 분석 구현
- Wave 파일이란?
Wave 파일이란 마이크로소프트와 IBM의 오디오 파일 포맷 표준으로 윈도우에서 표준적으로 사용되는 PCM데이터 파일입니다. PCM이란 아날로그 신호를 디지털 신호로 바꾸어주는 방식입니다.
- Wave 포맷구성
윈도우의 녹음기를 통하여 녹음 하였을 때, 생성되는 wave파일의 포맷입니다.
맨 처음 RIFF chunk로 시작하여 FMT chunk, FACT chunk, Data chunk가 오게 되는데
FACT chunk는 존재 할 수도 있고 존재 하지 않을 수도 있습니다.
기본적으로는 RIFF chunk, FMT chunk, Data chunk로 이루어져있습니다.
- Wave 포맷 분석
1) RIFF chunk
내용 |
type |
byte |
의미 |
"RIFF" |
char |
4 |
파일의 종류가 RIFF 파일을 의미 |
FileSize |
DWORD |
4 |
현재부터 끝까지의 파일 크기 |
"WAVE" |
char |
4 |
Wave 파일을 의미 |
2) FMT sub-chunk
내용 |
type |
byte |
의미 |
"fmt" |
char |
4 |
Chunk ID |
16 | 18 |
DWORD |
4 |
Chunk Size |
wFormatTag |
short |
2 |
PCMWAVEFORMAT의 값(1:PCM ) |
nChannels |
short |
2 |
채널수(1:모노, 2:스테레오) |
nSamplesPerSec |
DWORD |
4 |
샘플링 수 |
nAvgBytesperSec |
DWORD |
4 |
초당 샘플 바이트 |
SampleSize |
short |
2 |
샘플당 바이트 |
wBitsPerSample |
short |
2 |
샘플당 비트수 |
ExtraFormatBytes |
short |
2 |
여분의 바이트를 의미함 |
ExtraFormatBytes는 Chunk Size가 18일 때만 존재합니다.
3) FACT sub-chunk
내용 |
type |
byte |
의미 |
"fact" |
char |
4 |
Chunk ID |
4 |
DWORD |
4 |
Chunk Size |
SampleLength |
DWORD |
4 |
샘플의 길이 |
FACT sub-chunk는 존재 할 수도 있고 존재하지 않을 수도 있습니다.
4) Data sub-chunk
내용 |
type |
byte |
의미 |
"data" |
char |
4 |
Chunk ID |
DATA SIZE |
DWORD |
4 |
데이터의 크기 |
DATA |
|
|
데이터 |
- Wave 포맷 분석 구현
SoundInfo 클래스는 RIFF Chunk(ParentChunk 클래스)와 FMT Sub-Chunk(SubChunk 클래스)와 Data Sub-Chunk(DataChunk)를 관리해주는 클래스입니다.
SoundInfo.cpp
생성자에서 각각의 Chunk별로 데이터들을 얻습니다.
SoundInfo::SoundInfo(HANDLE hFile)
{
parent_chunk=new ParentChunk(hFile);
sub_chunk=new SubChunk(hFile);
data_chunk=new DataChunk(hFile);
}
ParentChunk의 데이터를 얻어옵니다.
//===================ParentChunk========================
char *SoundInfo::GetFiletype()
{
return parent_chunk->GetFiletypeC();
}
int SoundInfo::GetFilesize()
{
return parent_chunk->GetFilesizeC();
}
char *SoundInfo::GetMediatype()
{
return parent_chunk->GetMediatypeC();
}
//=====================================================
각 Chunk별로 Wave파일의 속성이 맞는지 확인합니다.
bool SoundInfo::CheckParentChunk()
{
return parent_chunk->IsAvailChunk();
}
bool SoundInfo::CheckSubChunk()
{
return sub_chunk->IsAvailChunk();
}
bool SoundInfo::CheckDataChunk()
{
return data_chunk->IsAvailChunk();
}
SubChunk의 데이터를 얻어옵니다.
//===================SubChunk========================
char *SoundInfo::GetMagic()//매직
{
return sub_chunk->GetMagicC();
}
int SoundInfo::GetPt()
{
return sub_chunk->GetPtC();
}
short SoundInfo::GetFormat()//포맷
{
return sub_chunk->GetFormatC();
}
short SoundInfo::GetChannelcnt()//채널수
{
return sub_chunk->GetChannelcntC();
}
int SoundInfo::GetSamples_persec()//초당샘플수
{
return sub_chunk->GetSamples_persecC();
}
int SoundInfo::GetBytes_persec() //초당바이트수- 평균값임
{
return sub_chunk->GetBytes_persecC();
}
short SoundInfo::GetSize_sample() //샘플1개의사이즈
{
return sub_chunk->GetSize_sampleC();
}
short SoundInfo::GetBits_persample()//샘플한개의비트수
{
return sub_chunk->GetBits_persampleC();
}
short *SoundInfo::GetSize_extend()//옵션의사이즈
{
return sub_chunk->GetSize_extendC();
}
//=====================================================
DataChunk의 데이터를 얻어옵니다.
//===================DataChunk========================
char *SoundInfo::GetDataMagic()
{
return data_chunk->GetDataMagicC();
}
int SoundInfo::GetDataSize()
{
return data_chunk->GetDataSizeC();
}
short* SoundInfo::GetData()
{
return data_chunk->GetDataC();
}
ParentChunk.cpp
Filetype,filesize,mediatype을 읽어옵니다.
ParentChunk::ParentChunk(HANDLE hFile)
{
DWORD dwRead;
ReadFile(hFile,filetype,4,&dwRead,0);
ReadFile(hFile,&filesize,4,&dwRead,0);
ReadFile(hFile,mediatype,4,&dwRead,0);
}
만약 filetype이 "RIFF"이 아니거나 mediatype이 "WAVE"가 아닌 경우 0을 리턴합니다.
bool ParentChunk::IsAvailChunk()
{
return (strcmp(MAGIC_FILE_TYPE,filetype)==0)&&(strcmp(MAGIC_WAVE_TYPE,mediatype)==0);
}
char *ParentChunk::GetFiletypeC()
{
return filetype;
}
int ParentChunk::GetFilesizeC()
{
return filesize;
}
char *ParentChunk::GetMediatypeC()
{
return mediatype;
}
SubChunk.cpp
ChunkID,ChunkSize, PCMWAVEFORMAT의 값,채널수,샘필링수,초당 샘플 바이트,샘플당 바이트, 샘플당 비트수를 얻어옵니다.
Pt의 값(chunk size)이 18일 경우에는 옵션의 사이즈와 fact chunk가 존재합니다.
SubChunk::SubChunk(HANDLE hFile)
{
DWORD dwRead;
ReadFile(hFile,magic,4,&dwRead,0);
ReadFile(hFile,&pt,4,&dwRead,0);
ReadFile(hFile,&format,2,&dwRead,0);
ReadFile(hFile,&channelcnt,2,&dwRead,0);
ReadFile(hFile,&samples_persec,4,&dwRead,0);
ReadFile(hFile,&bytes_persec,4,&dwRead,0);
ReadFile(hFile,&size_sample,2,&dwRead,0);
ReadFile(hFile,&bits_persample,2,&dwRead,0);
//pt가16과18이아닌경우는배제
if(pt==18 )
{
ReadFile(hFile,&extra_formatbytes,2,&dwRead,0);
ReadFile(hFile,fmagics,4,&dwRead,0);//fact chunk
ReadFile(hFile,&fackchunk_size,4,&dwRead,0);
ReadFile(hFile,&sample_length,4,&dwRead,0);
}
}
ChunkID(magic)이 "fmt"이 아니거나 format(PCMWAVEFORMAT)값이 1(Wave Format이 PCM 방식) 이 아닐 때 0을 리턴합니다.
bool SubChunk::IsAvailChunk()
{
return (strcmp(MAGIC_SUB_TYPE,magic)==0)&&(format==PCMWAVE_FORMAT);
}
char *SubChunk::GetMagicC()//ChunkID
{
return magic;
}
int SubChunk::GetPtC()//ChunkSize
{
return pt;
}
short SubChunk::GetFormatC()//포맷
{
return format;
}
short SubChunk::GetChannelcntC()//채널수
{
return channelcnt;
}
int SubChunk::GetSamples_persecC()//초당샘플수
{
return samples_persec;
}
int SubChunk::GetBytes_persecC() //초당바이트수- 평균값임
{
return bytes_persec;
}
short SubChunk::GetSize_sampleC() //샘플1개의사이즈
{
return size_sample;
}
short SubChunk::GetBits_persampleC()//샘플한개의비트수
{
return bits_persample;
}
short *SubChunk::GetSize_extendC()
{
return size_extend;
}
DataChunk.cpp
ChunkID,DataSize,Data를 얻어옵니다.
DataChunk::DataChunk(HANDLE hFile)
{
DWORD dwRead;
ReadFile(hFile,magic,4,&dwRead,0);
ReadFile(hFile,&datasize,4,&dwRead,0);
data=(short *)malloc(datasize);
short _data;
int j=0;
for(int i=0;i<datasize;i=i+2)
{
ReadFile(hFile,&_data,sizeof(short),&dwRead,0);
data[j]=_data;
if(dwRead == 0)
{
break;
}
j++;
}
}
ChunkID가 "DATA"가 아니면 0을 리턴합니다.
bool DataChunk::IsAvailChunk()
{
return (strcmp(MAGIC_DATA_TYPE,magic)==0);
}
char *DataChunk::GetDataMagicC()
{
return magic;
}
int DataChunk::GetDataSizeC()
{
return datasize;
}
short* DataChunk::GetDataC()
{
return data;
}
Analysis.cpp
Analsis 클래스는 입력한 신호의 chunk 데이터에서 얻은 수치에 대한 평균 세기, 총 에너지, 총 주파수를 계산합니다.
평균 세기는 데이터값의 절대값들의 총 합에서 데이터 개수를 나누어주면 됩니다.
int Analysis::GetPower(short *data,int datasize)
{
unsigned temp=0;
for(int i=0;i<datasize/2;i++)
{
if(data[i]<0)
{
data[i]=data[i]*-1;
}
temp=temp+data[i];
}
return temp/(datasize/2);
}
총 에너지는 데이터값의 절대값들의 절대값들의 총 합에서 데이터 개수를 곱해주면 됩니다.
int Analysis::GetEnergy(short *data,int datasize,HWND hDlg)
{
int temp=0;
for(int i=0;i<datasize/2;i++)
{
if(data[i]<0)
{
data[i]=data[i]*-1;
}
temp=temp+data[i];
}
return ((temp/1000000)*(datasize/2));
}
총 주파수는 처음 데이터 값과 다음 데이터 값을 비교해 한 주기는 dcnt가 2개 증가 하였을때이므로 총 주파수는 dcnt/2를 리턴합니다.
int Analysis::GetFrequency(short *data,int datasize)
{
int size=datasize/2;
bool direction=true;
int old = 0;
int dcnt = 0;
for(int i=0;i<size;i++)
{
if(direction)//현재증가하는경우라면
{
if(data[i]>old) //새로운값이기존값보다크다면방향유지
{
}
else //방향이바뀌었음
{
direction = false;
dcnt++;
}
}
else
{
if(data[i]<old)
{
}
else
{
direction = true;
dcnt++;
}
}
old = data[i];
}
return dcnt/2;
}
[21기_김동영]wave파일_분석_기술문서[1].doc