[미디 분석 프로그램 만들기] 6. 트랙 청크 분석하기 - 메타 이벤트 분석
안녕하세요. 언제나 휴일, 언휴예요.
이전 글에서 트랙 청크의 delta 타임을 구하는 부분까지 구현했었죠.
[미디 분석 프로그램 만들기] 1. 구현할 프로그램 소개
[미디 분석 프로그램 만들기] 2. 프로젝트 생성 및 Layout
[미디 분석 프로그램 만들기] 3. 미디 파일 열기 및 청크로 분할하기
[미디 분석 프로그램 만들기] 4. 헤더 청크 분석하기
[미디 분석 프로그램 만들기] 5. 트랙 청크 분석하기 - delta time 구하기
이번에는 트랙 청크의 이벤트 중에 메타 이벤트를 분석하는 부분을 구현합시다. 메타 이벤트에 관한 자세한 정보는 다음 게시글을 참고하세요.
[미디 파일] 미디 파일 구조 분석 및 프로그래밍 4 - Track 청크 2, Meta Event
[미디 파일] 미디 파일 구조 분석 및 프로그래밍 5 - Track 청크 3, 박자, 키 정보 등
트랙 청크의 이벤트는 delta 타임 뒤에 오는 1바이트 값에 따라 어떠한 이벤트인지 결정할 수 있어요. 만약 해당 값이 0xFF라면 메타 이벤트이며 0xF0에서 0xFE 사이라면 시스템 이벤트, 그 외의 값일 때 미디 이벤트입니다. 참고로 delta 타임 뒤에 오는 1 바이트를 Status Byte(상태 바이트)라고 부릅니다.
여기에서는 Meta 이벤트를 다룰 거예요.
Meta 이벤트는 상태 바이트 뒤에 어떠한 데이터인지 구분하는 정보가 옵니다. 그리고 그리고 각 정보에 따라 다른 의미의 데이터가 따라옵니다. 이 부분은 가변 길이여서 맨 처음에 길이가 오고 해당 길이만큼 데이터가 옵니다. 다음은 Meta 데이터가 어떠한 의미를 지니는지 설명한 것입니다.
Status byte |
Meta Data byte |
||
2ndbyte |
Other bytes |
Description |
|
FF |
0 |
02 ss ss |
Set track’s sequence # |
FF |
1 |
nn tt… |
Any Text user wants |
FF |
2 |
nn tt… |
Text for copyright info. |
FF |
3 |
nn tt… |
Track name |
FF |
4 |
nn tt… |
Track instrument name |
FF |
5 |
nn tt… |
Lyric |
FF |
6 |
nn tt… |
Marker |
FF |
7 |
nn tt… |
Cue point |
FF |
20 |
01 channel |
Channel prefix |
FF |
21 |
01 pp |
MIDI port |
FF |
2F |
0 |
End of Track |
FF |
51 |
03 tt tt tt |
Set tempo |
(microseconds/quarter note) |
|||
FF |
54 |
05 hh mm ss ff sf |
SMPTE Offset |
hour/minute/second |
|||
/frame/subframe |
|||
FF |
58 |
04 nn dd cc bb |
Time signature |
numerator/ |
|||
denominator/ |
|||
metronome ticks #/ |
|||
32nd notes# per quarter note |
|||
FF |
59 |
02 sf mi |
Key signature |
key(sharp/flat#) |
(C when Key=0) |
||
scale(0:major, 1:minor) |
|
||
FF |
7F |
nn tt… |
Sequencer specific info. |
먼저 MDEvent 클래스에 이벤트 하나를 분석하는 정적 메서드인 Parsing에 메타 이벤트를 분석하는 코드를 추가합니다.
public static MDEvent Parsing(byte[] buffer, ref int offset, MDEvent bef_event)
{
int oldoffset = offset;
int delta = MidiHelper.ReadDeltaTime(buffer, ref offset);
이 부분에 Status Byte의 값이 0xFF일 때 메타 이벤트를 분석하여 반환하는 코드를 추가하세요.
if (buffer[offset] == 0xFF)
{
offset++;
return MetaEvent.MakeEvent(delta, buffer, ref offset, oldoffset);
}
이 부분에 미디 이벤트와 시스템 이벤트를 분석하는 코드를 작성할 부분이며 뒤에서 다룰 거예요.
return null;
//if (buffer[offset] < 0xF0)
//{
// return MidiEvent.MakeEvent(buffer[offset++], delta, buffer, ref offset, oldoffset, bef_event.EventType);
//}
//return SysEvent.MakeEvent(buffer[offset++], delta, buffer, ref offset, oldoffset);
}
이제 MetaEvent 클래스를 구현합시다.
using System;
namespace 미디_분석_프로그램
{
public class MetaEvent : MDEvent
{
public static MDEvent MakeEvent(int delta, byte[] buffer, ref int offset, int oldoffset)
{
메시지 종류가 맨 앞에 위치합니다.
byte msg = buffer[offset++];
그리고 메시지 길이가 따라옵니다.
byte len = buffer[offset++];
byte[] data = null;
만약 메시지 값이 0x2F일 때는 트랙 종료를 의미합니다. 그렇지 않을 때는 메시지 길이만큼 데이터가 있으니 이를 보관할 버퍼를 생성하여 복사하세요.
if (msg != 0x2F)
{
data = new byte[len];
Array.Copy(buffer, offset, data, 0, len);
offset += len;
}
전체 데이터의 크기는 현재 offset에서 이전 offset을 뺀 크기입니다. 이 부분을 복사할게요.
byte[] buffer2 = new byte[offset - oldoffset];
Array.Copy(buffer, oldoffset, buffer2, 0, buffer2.Length);
delta 값과 메타 이벤트 종류, 데이터 길이, 데이터, 이벤트 전체 내용이 있는 버퍼를 입력 인자로 메타 이벤트 개체를 생성하여 반환합니다.
return new MetaEvent(delta, msg, len, data, buffer2);
}
메타 이벤트에는 어떠한 종류의 메타 이벤트인지 판별하는 속성을 캡슐화하세요.
public byte Msg //어떤 종류의 메타 이벤트인지를 판별
{
get;
private set;
}
메타 이벤트에서 데이터 길이가 다르며 이를 판별하는 속성을 캡슐화하세요.
public byte Length //메타 데이터 길이
{
get;
private set;
}
메타 데이터를 위한 속성을 캡슐화하세요.
public byte[] Data//메타 데이터
{
get;
private set;
}
사용자 편의를 위해 메타 데이터를 문자열로 변환하여 반환하는 가져오기 속성을 제공합시다. 변환하는 메서드는 MidiHelper 클래스에 구현하기로 할게요.(GetString 메서드 중복 정의)
public string DataString//메타 데이터를 문자열로 변환한 값
{
get
{
if (Data == null)
{
return string.Empty;
}
return MidiHelper.GetString(Data);
}
}
메시지 종류에 따라 상세 정보를 문자열로 제공하는 가져오기 속성도 정의합시다.
public string MetaDescription
{
get
{
switch (Msg)
{
case 0x00: return string.Format("SeqNo:{0}" + BitConverter.ToInt16(Data, 0));
case 0x01: return DataString;
case 0x02: return "Copyright:" + DataString;
case 0x03: return "Track Name:" + DataString;
case 0x04: return "Instument:" + DataString;
case 0x05: return "Lyric:" + DataString;
case 0x06: return "Marker:" + DataString;
case 0x07: return "CuePoint:" + DataString;
case 0x08: return "ProgramName" + DataString;
case 0x09: return "DeviceName" + DataString;
case 0x20: return "Channel:" + Data[0].ToString();
case 0x21: return "Midi Port:" + Data[0].ToString();
case 0x2F: return "End of Track";
case 0x51: return "Tempo:" + MakeTempo();
case 0x54: return "SmpteOffSet";
case 0x58: return "TimeSignature:" + MakeTimeSig(); ;
case 0x59: return "KeySignature" + MakeKeySignature();
case 0x7F: return "SeqEvent";
default: return "ETC";
}
}
}
템포 문자열을 만드는 메서드입니다.
private string MakeTempo()
{
int tempo = Data[0] << 16 | Data[1] << 8 | Data[0];
return tempo.ToString() + "microseconds/quarter note";
}
장조의 키 진행입니다.
static string[] keystr = new string[]
{
"C Flat","G Flat","D Flat","A Flat","E Flat","B Flat","F Flat",
"C",
"G","D","A","E","B","F Sharp","C Sharp"
};
단조의 키 진행입니다.
static string[] keystr2 = new string[]
{
"A Flat","E Flat","B Flat","F","C","G","D",
"A",
"E","B","F Sharp","C Sharp","G Sharp", "D Sharp", "A Sharp"
};
키 문자열을 만드는 메서드입니다.
private string MakeKeySignature()
{
byte ki = (byte)(Data[0] + 7);
if (Data[1] == 0)
{
return keystr[ki] + " Major";
}
return keystr2[ki] + " minor";
}
박자를 만드는 메서드입니다.
private string MakeTimeSig()
{
return string.Format("{0}/{1}", Data[0], Math.Pow(2, Data[1]));
}
메타 이벤트의 생성자입니다.
public MetaEvent(int delta, byte msg, byte len, byte[] data, byte[] orgbuffer) : base(0xFF, delta, orgbuffer)
{
Msg = msg;
Length = len;
Data = data;
}
}
}
다음은 MidiHelper 클래스에 중복정의한 GetString 메서드입니다.
public static string GetString(byte[] data)
{
Encoding en = Encoding.Default;
return en.GetString(data);
}
'프로그래밍 기술 > 미디 파일 구조 및 미디 분석 프로그램 만들기' 카테고리의 다른 글
[미디 분석 프로그램 만들기] 8. 트리 노드에 이벤트 정보 표시 (2) | 2018.05.28 |
---|---|
[미디 분석 프로그램 만들기] 7. 트랙 청크 분석하기 - 미디 이벤트 분석 (0) | 2018.05.03 |
[미디 분석 프로그램 만들기] 5. 트랙 청크 분석하기 - delta time 구하기 (0) | 2018.05.03 |
[미디 분석 프로그램 만들기] 4. 헤더 청크 분석하기 (0) | 2018.05.02 |
[미디 분석 프로그램 만들기] 3. 미디 파일 열기 및 청크로 분할하기 (0) | 2018.05.01 |
[미디 분석 프로그램 만들기] 2. 프로젝트 생성 및 Layout (0) | 2018.05.01 |
[미디 분석 프로그램 만들기] 1. 구현할 프로그램 소개 (0) | 2018.05.01 |
[미디 파일] 미디 파일 구조 분석 및 프로그래밍 9 - 시스템 이벤트 (5) | 2018.04.19 |
[미디 파일] 미디 파일 구조 분석 및 프로그래밍 8 -악기 번호 (5) | 2018.04.18 |
[미디 파일] 미디 파일 구조 분석 및 프로그래밍 7 - 컨트롤 번호 (0) | 2018.04.18 |