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

학습 내용

조 : 2조

작성자 : 김동영

작성일 : 2011. 5. 29

제목 : Wave 포맷 분석

 

 

1. Wave파일이란?

기본적으로 MS사의 기본 Sound Format이다.

전체 구조는 Electronic Arts사의 IFF(Interchange File Format)에 기초를 두고 있고, MS사의 RIFF(Resource IFF)와 Apple의 AIFF로 나뉜다.

 

wave파일의 구조는 다음과 같다.

오른쪽 숫자는 읽어야 할 byte, *은 가변적인 길이이다.

 

 

 

 

 

2. Chunk의 기본형식

chunk의 기본형식은 다음과 같다.

 

 

 

Chunk ID가 있고, 해당 Chunk의 Size, Size만큼의 Data가 들어있다.

 

3. fmt_ Chunk

해당 Chunk는 이 wave파일의 정보들을 보관하고 있다.

 

"fmt_"

Chunk ID

Chunk Size

Chunk Size

Compression Code

PCMWAVEFORMAT의 값(1:PCM )

Number Channels

채널수(1:모노, 2:스테레오)

SampleRate

샘플링 수

BytePerSecond

초당 샘플 바이트

BytePerSample

샘플당 바이트

BitsPerSample

샘플당 비트수

Extraformatsize

부가정보 사이즈

extraformatdata

부가정보

 

 

 

 

 

Channel의 수에 따라서 저장되는 형태는

1채널일 때

2채널일 때

3채널일 때

숫자는 채널 번호이고, 채널의 수에 따라서 반복되면서 저장되어있다.

 

Extraforma은 존재하지 않을 수도 있는데, 그에 대한 판별은 Chunk Size를 확인했을 때 16보다 크게 되면 존재할 것이다.

 

 

 

 

4. data Chunk

 

 

이곳에 파형 데이터가 저장되어있다.

Chunk ID는 "data"이지만 "wavl"로 존재할 수도 있다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

5. 기타 Chunk

Chunk ID의 종류에는

    

등이 있다.

 

"fmt"와 "cue"는 3byte지만 ID의 크기는 4byte이기 때문에 뒤에 공백으로 채워진다.

위에서는 공백을 '_'로 표현하였다.

Chunk ID는 항상 소문자로 존재하지 않고, 대문자로 존재하는 경우도 있다.

 

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

Wave 파일 분석  (0) 2012.11.02
주파수 생성  (0) 2012.08.16

학습 내용

조 : 2조

작성자 : 김동영

작성일 : 2011. 6. 1

제목 : 주파수 생성

 

주파수를 생성하여 주파수에 해당 하는 음을 재생하는 wav파일을 만들어 보았다.

 

 

아래의 이미지는 음에 관한 주파수이다.

 

 

C3에 해당하는 주파수를 생성하여 wav파일로 만들어 재생하였다.

생성된 wav파일을 midi파일로 변환해보니 미디 60에 해당하는 C3가 재생되었다.

 

 

 

private void button1_Click(object sender, EventArgs e)

{

double amplitude = 0.25 * short.MaxValue;

 

double frequency = double.Parse(textBox2.Text);

double samplerate = double.Parse(textBox1.Text);

 

for (int i = 0; i < buf.Length; i++)

{

buf[i] = (short)(amplitude * Math.Sin((2 * Math.PI * i * frequency) / samplerate));

}

 

Invalidate();

}

주파수를 생성하는 부분이다.

 

 

private void MakeFile(string filename,double sampleRate, int channelCount)

{

FileStream fs = new FileStream(filename, FileMode.Create);

BinaryWriter bw = new BinaryWriter(fs);

 

bw.Write(0x46464952); // RIFF

bw.Write((buf.Length*sizeof(short)+44));

bw.Write(0x45564157); // WAVE

bw.Write(0x20746d66); // fmt_

bw.Write(16);

bw.Write((Int16)1);

bw.Write((Int16)channelCount);

bw.Write((Int32)sampleRate);

bw.Write((Int32)sampleRate * 2 * channelCount);

bw.Write((Int16)(channelCount * 2));

bw.Write((Int16)(16));

 

bw.Write(0x61746164); // data

bw.Write(buf.Length*sizeof(short));

for (int i = 0; i < buf.Length; i++)

{

bw.Write(buf[i]);

}

bw.Close();

}

파일을 생성하는 부분이다.

 

참조 : [기술 및 구현]2010.10.20 주파수 생성하기 - 유진명

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

Wave 파일 분석  (0) 2012.11.02
Wave파일 구조  (0) 2012.11.02

+ Recent posts