미디 분석 프로그램 – 2. 청크 목록 분석

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

1. 해야 할 일

이번에는 미디 파일 구조를 간단히 알아보고 이를 분석하는 간단한 응용 프로그램을 작성해 보기로 할게요.

먼저 미디(Midi)는 Musical Instrument Digital interface로 전자악기의 디지털 데이터를 주고 받기 위한 표준 규격입니다.

여기에서는 미디 파일 구조를 점진적으로 분석하고 이를 이용하는 프로그램을 단계적으로 작성해 나가기로 할게요.

미디 파일은 덩어리(청크, Chunk)들의 집합입니다. 미디 파일은 여러 개의 청크들로 구성하고 있다는 거예요.

그리고 청크는 청크 유형(4바이트), 청크 길이(4바이트), 청크 데이터(청크 길이)의 조합이예요.

[그림] Chunk 구조

2. StaticFuns 정적 클래스 정의

StaticFuns는 공통적으로 사용할 함수를 래핑한 클래스입니다.

미디 파일의 데이터가 Big Endian 바이트 정렬 방식을 따르고 있기 때문에 호스트 정렬 방식으로 조정이 필요합니다.

그리고 Chunk Type은 4바이트 데이터인데 ASCII 코드 4개로 구성하고 있어요. 4개의 영문 알파벳인데 매직이라고 부릅니다. 여기에서는 4바이트 매직 데이터를 문자열로 변환하는 기능도 제공할게요.

using System;
using System.Net;
using System.Text;

namespace 청크_목록_분석
{
    public static class StaticFuns
    {
        public static string GetString(int magic)
        {
            byte[] data = BitConverter.GetBytes(magic);
            ASCIIEncoding en = new ASCIIEncoding();
            return en.GetString(data);
        }        
        public static int ConverHostorder(int data)
        {
            return IPAddress.NetworkToHostOrder(data);
        }
    }
}

3. Chunk 클래스 정의

프로젝트에 Chunk 클래스를 추가하세요.

Chunk는 청크 타입, 청크 길이, 데이터로 구성합니다.

        public int CT
        {
            get;
        }
        public int Length
        {
            get;
        }
        public byte[] Data
        {
            get;
        }

Stream 개체를 입력 인자로 받아 청크 개체를 생성하는 정적 메서드를 정의합니다.

다음은 C#으로 미디 파일을 로딩하여 어떠한 청크들로 구성하고 있는지를 분석하는 간단한 응용 프로그램 소스 코드입니다.

        public string CTString
        {
            get
            {
                return StaticFuns.GetString(CT);
            }
        }
        public static Chunk Parse(Stream stream)
        {
            try
            {
                BinaryReader br = new BinaryReader(stream);
                int ctype = br.ReadInt32();
                int length = br.ReadInt32();
                length = StaticFuns.ConverHostorder(length);
                byte[] buffer = br.ReadBytes(length);
                return new Chunk(ctype, length, buffer);
            }
            catch
            {
                return null;
            }
        }

다음은 Chunk.cs 소스 코드입니다.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 청크_목록_분석
{
    public class Chunk
    {

        public int CT
        {
            get;
        }
        public int Length
        {
            get;
        }
        public byte[] Data
        {
            get;
        }
        public Chunk(int ctype, int length, byte[] buffer)
        {
            CT = ctype;
            Length = length;
            Data = buffer;
        }
        public string CTString
        {
            get
            {
                return StaticFuns.GetString(CT);
            }
        }
        public static Chunk Parse(Stream stream)
        {
            try
            {
                BinaryReader br = new BinaryReader(stream);
                int ctype = br.ReadInt32();
                int length = br.ReadInt32();
                length = StaticFuns.ConverHostorder(length);
                byte[] buffer = br.ReadBytes(length);
                return new Chunk(ctype, length, buffer);
            }
            catch
            {
                return null;
            }
        }
        public override string ToString()
        {
            return CTString;
        }
    }
}

4. 미디 파일에서 청크 목록을 분석하기

미디 파일을 열어 파일의 끝까지 청크 개체로 변환하여 콘솔 화면에 출력합니다.

using System;
using System.IO;

namespace 청크_목록_분석
{
    class Program
    {
        static string fname = "example.mid";
        static void Main(string[] args)
        {
            FileStream fs = new FileStream(fname, FileMode.Open);
            while(fs.Position<fs.Length)
            {
                Chunk chunk = Chunk.Parse(fs);
                if(chunk!=null)
                {
                    Console.WriteLine("{0}:{1}bytes", chunk.CTString, chunk.Length);
                }
            }
        }
    }
}