프로그래밍 기술/미디 파일 구조 및 미디 분석 프로그램 만들기

[미디 분석 프로그램 만들기] 4. 헤더 청크 분석하기

언제나휴일 2018. 5. 2. 16:44
반응형

[미디 분석 프로그램 만들기] 4. 헤더 청크 분석하기



안녕하세요. 언제나 휴일, 언휴예요.


앞에서 미디 파일을 열어 청크 목록을 리스트 박스에 보여주는 부분까지 구현하였습니다.

[미디 분석 프로그램 만들기] 1. 구현할 프로그램 소개

[미디 분석 프로그램 만들기] 2. 프로젝트 생성 및 Layout

[미디 분석 프로그램 만들기] 3. 미디 파일 열기 및 청크로 분할하기



이번에는 청크 중에 헤더 청크 부분을 분석하는 부분을 구현하기로 할게요. 이미 미디 파일 분석에 관한 게시글에서 헤더 청크에 관한 설명을 했었습니다.

[미디 파일] 미디 파일 구조 분석 및 프로그래밍 2 - Head 청크, Mthd


헤더 청크의 구조

헤더 청크의 구조


헤더 청크 부분을 헥사 코드와 각 항목을 출력한 예

헤더 청크 부분을 헥사 코드와 각 항목을 출력한 예


앞에서 작성한 코드 중에 Chuck 클래스의 Parse 메서드에서 청크 개체를 생성하여 반환하는 코드 앞 부분에 switch문을 추가합니다.

 

        public static Chunk Parse(Stream stream)

        {

            try

            {

                BinaryReader br = new BinaryReader(stream);

                int ctype = br.ReadInt32();

                int length = br.ReadInt32();

                length = MidiHelper.ConvertHostorder(length);

                byte[] buffer = br.ReadBytes(length);

여기서부터 return 이전까지가 이번에 작성할 부분입니다.


먼저 타입을 호스트 오더로 변경하세요.

                int cval = MidiHelper.ConvertHostorder(ctype);

타입에 해당한는 값에 따라 헤더 청크 혹은 트랙 청크를 생성하여 반환하는 코드를 작성합니다. 여기에서는 먼저 헤더 청크를 생성하는 부분을 작성하세요. 헤더 청크를 표현할 Header 클래스를 새로 생성하고 타입과 길이, 데이터 버퍼를 입력 인자로 받는 생성자도 추가하세요.

                switch (cval)

                {

                    case magic_head: return new Header(ctype, length, buffer);

                    //case magic_track: return new Track(ctype, length, buffer);

                }

                return new Chunk(ctype, length, buffer);

            }

            catch

            {

                return null;

            }

        }




이제 Header 클래스를 구현합시다.

using System;

 

namespace 미디_분석_프로그램

{

    public class Header : Chunk

    {

헤더 청크는 청크 유형, 길이 뒤에 포멧(2바이트)과 트랙 개수(2바이트), Division(2바이트)가 옵니다. 이러한 값을 가져오기 할 수 있는 속성을 제공합니다.

        public int Format//포멧

        {

            get

            {

포멧은 데이터 버퍼의 시작 위치에서 2바이트의 값입니다. 이 값을 얻어와 호스트 정렬 값으로 변환하는 것을 담당하는 메서드를 MidiHelper 클래스에 ConvertHostorderS 이름의 메서드를 추가하여 구현합시다.

                return MidiHelper.ConvertHostorderS(Data, 0);

            }

        }

 

        public int TrackCount//트랙 개수

        {

            get

            {

트랙 개수는 데이터 버퍼에서 포멧(2바이트) 뒤에 옵니다. 그리고 길이는 2바이트입니다. 앞에서 포멧을 얻어온 것과 같은 방식으로 데이터 버퍼의 오프셋 2부터 2바이트 값을 얻어와 호스트 정렬 값으로 변환하여 반환합니다.

                return MidiHelper.ConvertHostorderS(Data, 2);

            }

        }

 

Division 부분의 값이 양수일 때는 1개의 4분 음표를 나눈 값을 의미하며 음수일 때는 프레임 당 Ticks(하이 1바이트)와 -1초당 프레임 수(상위 1바이트 중 7비트)를 나타냅니다. 여기에서는 이를 쉽게 판별할 수 있게 IsTicks 속성을 제공합시다.

        public bool IsTicks

        {

            get;

            private set;

        }

DIvision 속성도 제공하며 값은 생성자에서 설정합시다.

        public int Division

        {

            get;

            private set;

        }

        public Header(int ctype, int length, byte[] buffer) : base(ctype, length, buffer)

        {

먼저 데이터 버퍼의 오프셋 4위치부터 2바이트 값을 얻어와서 호스트 정렬로 변환합니다.

            short dd = MidiHelper.ConvertHostorderS(buffer, 4);

해당 값이 양수이면 틱이 아닌 4분 음표를 나눈 값입니다.

            if (dd >= 0)

            {

                IsTicks = false;

                Division = dd;

            }

해당 값이 음수일 때는 (프레임 당 틱수 * 초당 프레임 수)를 통해 초당 틱수를 계산하여 설정합니다.

            else

            {

                IsTicks = true;

                Division = (buffer[4] & 0x7F) * buffer[5];

            }

        }

        public override string ToString()

        {

            return string.Format("<헤더 청크> " + base.ToString());

        }

    }

}



MidiHelper 클래스에도 ConvertHostOrderS 메서드를 구현합시다. 여기에서는 먼저 호스트 정렬 방식으로 변환하는 메서드 중에 입력 인자로 short 형식이 오는 메서드를 추가로 정의할게요. 다음은 수정한 MidiHelper 클래스 내용입니다.


▶MidiHelper.cs

using System;

using System.Net;

using System.Text;

 

namespace 미디_분석_프로그램

{

    public class MidiHelper

    {

        public static int ConvertHostorder(int data)

        {

            return IPAddress.NetworkToHostOrder(data);

        }

        public static short ConvertHostorder(short data)

        {

            return IPAddress.NetworkToHostOrder(data);

        }

 

        public static short ConvertHostorderS(byte[] data, int offset)

        {

            return ConvertHostorder(BitConverter.ToInt16(data, offset));

        }

 

        public static string GetString(int magic)

        {

            byte[] data = BitConverter.GetBytes(magic);

            Encoding en = Encoding.Default;

            return en.GetString(data);

        }

    }

} 



▶Chunk.cs

using System;

using System.IO;

 

namespace 미디_분석_프로그램

{

    public class Chunk

    {

        const int magic_head = 0x4d546864;

        const int magic_track = 0x4d54726b;

        public int CT//청크 유형

        {

            get;

            private set;

        }

        public int Length//청크 길이

        {

            get;

            private set;

        }

        public byte[] Data//데이터

        {

            get;

            private set;

        }

        public string CTString//청크 유형(문자열)

        {

            get

            {

                return MidiHelper.GetString(CT);

            }

        }

        public byte[] Buffer

        {

            get

            {

                byte[] ct_buf = BitConverter.GetBytes(CT);

                int belen = MidiHelper.ConvertHostorder(Length);

                byte[] len_buf = BitConverter.GetBytes(belen);

                byte[] buffer = new byte[ct_buf.Length + len_buf.Length + Data.Length];

                Array.Copy(ct_buf, buffer, ct_buf.Length);

                Array.Copy(len_buf, 0, buffer, ct_buf.Length, len_buf.Length);

                Array.Copy(Data, 0, buffer, ct_buf.Length + len_buf.Length, Data.Length);

                return buffer;

            }

        }

        public static Chunk Parse(Stream stream)

        {

            try

            {

                BinaryReader br = new BinaryReader(stream);

                int ctype = br.ReadInt32();

                int length = br.ReadInt32();

                length = MidiHelper.ConvertHostorder(length);

                byte[] buffer = br.ReadBytes(length);

                int cval = MidiHelper.ConvertHostorder(ctype);

                switch (cval)

                {

                    case magic_head: return new Header(ctype, length, buffer);

                    //case magic_track: return new Track(ctype, length, buffer);

                }

                return new Chunk(ctype, length, buffer);

            }

            catch

            {

                return null;

            }

        }

        public Chunk(int ctype, int length, byte[] buffer)

        {

            CT = ctype;

            Length = length;

            Data = buffer;

        }

        public override string ToString()

        {

            return string.Format("{0}:{1} bytes", CTString, Length);

        }

    }

} 


▶MidiHelper.cs



 using System;

 

namespace 미디_분석_프로그램

{

    public class Header : Chunk

    {

        public int Format//포멧

        {

            get

            {

                return MidiHelper.ConvertHostorderS(Data, 0);

            }

        }

 

        public int TrackCount//트랙 개수

        {

            get

            {

                return MidiHelper.ConvertHostorderS(Data, 2);

            }

        }

 

        public bool IsTicks

        {

            get;

            private set;

        }

        public int Division

        {

            get;

            private set;

        }

        public Header(int ctype, int length, byte[] buffer) : base(ctype, length, buffer)

        {

            short dd = MidiHelper.ConvertHostorderS(buffer, 4); //BitConverter.ToInt16(buffer, 4);

            if (dd > 0)

            {

                IsTicks = false;

                Division = dd;

            }

            else

            {

                IsTicks = true;

                Division = (buffer[4] & 0x7F) * buffer[5];

            }

        }

        public override string ToString()

        {

            return string.Format("<헤더 청크> " + base.ToString());

        }

    }

}


이 외에 다른 부분의 코드는 이전과 차이가 없으며 실제 동작에 변화도 없습니다.

반응형