멀티미디어 파일들을 읽기 위해서는 대부분 복잡한 포맷형식 때문에 이미 만들어진 라이브러리들을 통하여 읽는 것이 일반적이다. 하지만 PCM형식의 WAV파일은 비교적 간단하기 때문에 손쉽게 읽을 수 있을 것이라 생각된다.

WAV파일의 DFT변환을 위해 WAV파일의 저장된 내용을 읽을 필요가 있었는데.. 실제 음파의 값들을 읽을수 있는 라이브러리를 찾다가 직접 WAV파일을 읽는 프로그램을 작성하더라도 큰 어려움이 없을 것같아 만들어 보았다.

우선  PCM WAV파일의 포맷부터 보자..

 

이름

형식

Chunk Head

Chunk ID

4 Bytes ASCII String

“RIFF” - MS에서 사용하는 멀티미디어를 저장하는 포맷으로 항상 RIFF라 생각하면 됩니다.

Chunk Size

4 Bytes Little Endian

전체파일의 총 길입니다.

Format

4 Bytes ASCII String

“WAVE” - WAV파일일 경우

Sub Chunk 1

Sub Chunk 1 ID

4 Bytes ASCII String

“fmt “ - WAV파일일 경우

Sub Chunk 1 Size

4 Bytes Little Endian

Sub Chunk1의 길이

Audio Format

2 Bytes Little Endian

1 – PCM

Number of Channels

2 Bytes Little Endian

1 – Mono

2 – Stereo

Sample Rate

4 Bytes Little Endian

8000, 22000, 44000…… 등등

Byte Rate

4 Bytes Little Endian

(Sample Rate) *  (Number of Channels) * (Bit Per Samples) / 8

Block Align

2 Bytes Little Endian

(Number of Channels) * (Bit Per Samples) / 8

Bit Per Sample

2 Bytes Little Endian

8 또는 16

Extra Pram Size(선택적)

2 Bytes Little Endian

PCM의 경우는 사용하지 않음

Extra Pram(선택적)

N Bytes

확장 옵션을 위해

Sub Chunk 2

Sub Chunk 2 ID

4 Bytes ASCII String

“data”

Sub Chunk 2 Size

4 Bytes Little Endian

Sub Chunk 2의 크기

Data

N Bytes

실제 사운드 데이터

스테레오의 경우 좌 샘플 하나 우 샘플 하나씩 번갈아 가며 저장됨.


PCM의 경우 압축을 사용하지 않으므로 매우 간단하게 읽을 수 있다.

수치 값들은 모두 Little Endian으로 저장되며 모두 Unsigned 값들이다. 실수만 하지 않는 다면 간단하게 읽을 수 있을 것이다.

이곳으로 가면 조금더 자세한 WAV형식에 대한 설명을 볼 수 있습니다.
http://ccrma.stanford.edu/courses/422/projects/WaveFormat/

C#으로 작성해 보았으며 PCM형식의 WAV파일들의 값을읽습니다. 아 그리고 아래 소스는 실행가능 한 코드가 아니고 프로그램의 일부입니다..

또한 스트레오의 필요가 없어서 스트레오의 경우 모노로 바꾸어 읽어 버립니다..

=====================================================================================

using
System;
using System.Linq;
using System.Text;
using System.IO;
using System.Collections;
using System.Windows;
using System.Windows.Forms;

namespace WaveFile

{

    public class Wave

    {

       // string filePath;

        const string ChunkID = "RIFF";

        const string Format = "WAVE";

        const string Subchunk1ID = "fmt ";

        const string Subchunk2ID = "data";

        const int PCM = 1;

        public const int SampleUnit = 1024;

        bool successToOpen = false;

        //Stream waveFileStream;

                            //Offset    size

        string chunkId;     //0         4

        uint chunkSize;     //4         4       전체파일 크기

        string format;      //8         4

        string subchunk1ID; //12        4

        uint subchunk1Size; //16        4

        uint audioFormat;   //20        2

        uint numChannels;   //22        2

        uint sampleRate = 8000;    //24        4 

        uint byteRate;      //28        4       == SampleRate * NumChannels * BitsPerSample/8

        ushort blockAlign;    //32        2       == NumChannels * BitsPerSample/8

        uint bitsPerSample; //34        2       8 bits = 8, 16 bits = 16, etc.

        string subchunk2ID;   //36        4

        uint subchunk2Size; //40        4       == NumSamples * NumChannels * BitsPerSample/8

 

        const int chunkDescriptorSize = 12;

        const int subchunk2HeadSize = 8;

        private string fileName;

 

        private Stream pcmStream = new MemoryStream();

 

        public uint TotalLength

        {

            get { return (this.subchunk2Size-8) / this.BlockAlign ; }

        }

 

        public uint NumChannels

        {

            get { return this.numChannels; }

        }

 

        public uint SampleRate

        {

            get

            {

                return this.sampleRate;

            }

        }

 

        public bool SuccessToOpen

        {

            get

            {

                return successToOpen;

            }

        }

 

        public bool isStreo()

        {

            if (this.numChannels == 2)

                return true;

            return false;

        }

 

        public uint BlockAlign

        {

            get

            {

                return this.blockAlign;

            }

        }

 

        public uint BitsPerSample

        {

            get { return this.bitsPerSample; }

        }

 

        public string FileName

        {

            get{return this.fileName;}

            set { this.fileName = value; }

        }

 

        //현재 위치를 하나 읽음

        public int readSample()

        {

            BinaryReader br = new BinaryReader(this.pcmStream);

            if(br.BaseStream.Position >= br.BaseStream.Length) return 0;

            if (this.isStreo())

            {

                if (this.bitsPerSample == 8)

                {

                    return (int)((br.ReadSByte() + br.ReadSByte())/2);

                }

                else if (this.bitsPerSample == 16)

                {

                    return (int)((br.ReadInt16() + br.ReadInt16()) / 2);

                }

 

            }

            else

            {

                if (this.bitsPerSample == 8)

                {

                    return (int)br.ReadSByte();

                }

                else if (this.bitsPerSample == 16)

                {

                    return (int)br.ReadInt16();

                }

            }

            return 0;

        }

 

        public uint Subchunk2Size

        {

            get{ return this.subchunk2Size;}

        }

 

        public int FrameSize

        {

            get { return (int)(TotalLength / Wave.SampleUnit+1); }

       

        }       

           

          

        //오픈시 헤드를 읽음

        public Wave(Stream stream)

        {

            stream.Seek(0, SeekOrigin.Begin);

            byte[] buffer = new byte[stream.Length];

            stream.Read(buffer, 0, (int)stream.Length);

            this.pcmStream.Write(buffer, 0, (int)stream.Length);

            this.pcmStream.Seek(0, SeekOrigin.Begin);

           

            /////////////////////////////////

            // The "RIFF" chunk descriptor //

            /////////////////////////////////

            this.chunkId = readAscii4Byte(this.pcmStream);

            if (Wave.ChunkID != this.chunkId)

            {

                this.pcmStream.Close();

                //successToOpen = false;

            }

 

            BinaryReader br = new BinaryReader(this.pcmStream);

            this.chunkSize = br.ReadUInt32();

 

            this.format = readAscii4Byte(this.pcmStream);

            if (Wave.Format != this.format)

            {

                this.pcmStream.Close();

                return;

            }

 

            /////////////////////////

            // The "fmt" sub-chunk //

            /////////////////////////

            this.subchunk1ID = readAscii4Byte(this.pcmStream);

            if (this.subchunk1ID != Wave.Subchunk1ID)

            {

                this.pcmStream.Close();

                return;

            }

 

            this.subchunk1Size = br.ReadUInt32();

            this.audioFormat = br.ReadUInt16();

            this.numChannels = br.ReadUInt16();

            this.sampleRate = br.ReadUInt32();

            this.byteRate = br.ReadUInt32();

            this.blockAlign = br.ReadUInt16();

            this.bitsPerSample = br.ReadUInt16();

 

            //부가 정보가 있을 경우 넘김

            if (16 != this.subchunk1Size)

            {

                br.ReadBytes((int)this.subchunk1Size - 16);

            }

 

            //////////////////////////

            // The "data" sub-chunk //

            //////////////////////////

            this.subchunk2ID = readAscii4Byte(this.pcmStream);

            if (this.subchunk2ID != Wave.Subchunk2ID)

            {

                this.pcmStream.Close();

                return;

            }

           

            this.subchunk2Size = br.ReadUInt32();

 

            this.successToOpen = true;

           

            this.dftArray = new int[Wave.SampleUnit];

            //br.Close();

            return;

        } 

        private string readAscii4Byte(Stream fs)

        {

            byte[] buf = new byte[5];

 

            fs.Read(buf, 0, 4);

            buf[4] = 0;

            ASCIIEncoding asciiEn = new ASCIIEncoding();

            return asciiEn.GetString(buf, 0, 4);

           

        } 

 

    }

}


신고
크리에이티브 커먼즈 라이선스
Creative Commons License
, , ,


티스토리 툴바