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);

    //pt1618이아닌경우는배제

    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

 

 

'기타 > Wave' 카테고리의 다른 글

Wave파일 구조  (0) 2012.11.02
주파수 생성  (0) 2012.08.16

+ Recent posts