|
문서번호 |
|
보존기간 |
영구보존 |
작성일자 |
2013.12.31(화) |
Midi Engine v1.0
BIT 26기 손정훈
구 성
1.Midi에 대한 이해
2.Musical Tack Lib
3.Midi Chunk Data Lib
4.Midi Play Lib
5. Midi Engine Lib
1. Midi에 대한 이해
· 역사
1988년 Opcode System의 창시자 David Oppenheim씨에 의해 MIDI의 연주 데이터의 표준 파일 포맷으로서 제창된 것이 Standard MIDI File(SMF) 입니다. SMF에 대응하고 있는 시퀀서끼리 라면 플로피디스크나 CD-ROM 등의 미디어나 통신을 통해서 시퀀스 데이터(연주 정보, 템포 정보, 박자 정보, 그 외)를 주고받을 수 있기 때문에 서양의 시퀀스 소프트웨어 메이커를 중심으로 점차 채용되어 현재에는 완전히 표준 포맷으로 정착 되었습니다. [출처 : Midi 컴플리트, <타카하시 노부유키 저>]
· Format
- Format 0 단일 트랙에 모든 채널 메시지를 포함한 포맷
- Format 1 MIDI 채널마다 트랙을 나눠, 여러 트랙을 이용할 수 있는 포맷. 연주시 모든 트랙이 동시에 비동기식으로 연주
- Format 2 Format 1과 마찬가지로 여러 트랙의 기록을 할 수 있는 포맷이나 연주시 트랙이 차례대로 동기식으로 연주, 거의 사용되지 않음
· Chunk
데이터를 이루고 있는 일련의 덩어리. Chunk에는 SMF 전체의 정보를 포함한
"Header Chunk"와 실제 연주 정보가 들어있는 "Track Chunk" 2 종류가 있다
Header Chunk Data |
설명 |
Header ID |
(항상)MTrd |
Length of header data |
6 |
Format specification |
Format 0, 1, 2 |
Number of track blocks in the file |
Track 의 수 |
Unit of delta-time values. |
4분 음표의 길이 |
Header Chunk Data의 구조
Track의 정보 블록 부분 |
설명 |
Track ID |
(항상)MTrk |
Length of header data |
트랙 길이 |
<delta-time>, <event> |
트랙의 이름, 4분 음표, 템포 정보 등 |
The Track data format
· Event
Header Chunk 와 Track Chunk 시작 부분을 제외한 나머지 내용은 다음과 같은 Event 형식으로 되어있습니다.
<가변 길이 Delta-time> + <Event 명령> + <기타 파라미터>
이 중 Delta-time 은 앞의 Event를 처리하고 다음 명령을 할 때까지 대기하는 시간에 속합니다. 각각의 명령은 2개의 바이트로 구성되어 있다. xxxx 부분은 16개의 채널(악기 연주자)를 의미합니다.
Hex |
Binary |
Data |
Description |
8x |
1000xxxx |
nn vv |
Note off (key is released) // 소리를 끈다.
nn: note number vv: Velocity |
9x |
1001xxxx |
nn vv |
Note on (key is pressed) //소리를 킨다.
nn: note number vv: Velocity |
Ax |
1010xxxx |
nn vv |
Key after-touch nn: note number vv: Velocity |
Bx |
1011xxxx |
cc vv |
Control Change cc: controller number vv: new value |
Cx |
1100xxxx |
pp |
Program (patch) change //악기를 바꾼다. pp: new program number |
Dx |
1101xxxx |
cc |
Channel after-touch cc: channel number |
Ex |
1110xxxx |
bb tt |
Pitch wheel change (2000H is normal or no change) bb: bottom (least sig) 7 bits of value tt : top (most sig) 7 bits of value |
이 테이블은 Midi Channel 번호를 포함하고 있습니다.
다음으로는 Meta-Event 정보입니다. Meta-Event 에는 조, 박자, 트랙 이름, 저작권 정보 등 연주에 영향을 주지 않는 정보에 해당합니다. FF xx nn dd 의 형태를 따르고 있습니다.
<가변 길이 Delta-Time>+<FF>+<Meta-Event 명령>+<Data Length>+<Data>
여기서 가변 길이를 제외한 Meta Event 에 관한 명령어만 보자면 아래 표와 같습니다.
Hex |
Binary |
Data |
Description |
00 |
00000000 |
nn ssss |
Sets the track's sequence number. (시퀀스 번호) nn : 02 (length of 2-byte sequence number) ssss : sequence number |
01 |
00000001 |
nn tt |
Text event- any text you want. (제목/ 작곡자) nn: length in bytes of text tt: text characters |
02 |
00000010 |
nn tt |
Same as text event, but used for copyright info. (저작권 정보) nn tt : same as text event |
03 |
00000011 |
nn tt |
Sequence or Track name (시퀀스에 쓰여진 악보 이름, 트랙명은 시퀀스의 각 트랙에 붙여진 이름이다. Format 0의 SMF에서는 트랙명과 시퀀스 명은 같다.) nn tt same as text event |
04 |
00000100 |
nn tt |
Track instrument name (악기 이름) nn tt : same as text event |
05 |
00000101 |
nn tt |
Lyric (가사 정보 ASCII 문자를 표시하기 위한 이벤트) nn tt : same as text event |
06 |
00000110 |
nn tt |
Marker nn tt : same as text event |
07 |
00000111 |
nn tt |
Cue point nn tt : same as text event |
2F |
00101111 |
00 |
This event must come at the end of each track (악보 끝) |
51 |
01010001 |
03 tttttt |
Set tempo (빠르기 마이크로초당 4분음표의 시간) tttttt : microseconds/quarter note |
58 |
01011000 |
04 nn dd cc bb |
Time Signature (악곡의 박자) nn : numerator of time sig. dd : denominator of time sig. 2 -> quarter 3 -> eighth Etc cc : number of ticks in metronome click bb : number of 32nd notes to the quarter note |
59 |
01011001 |
02 sf mi |
Key signature (높이, 장조 단조) sf : sharps/flats -7 ~ -1 : flats 0 : key of C 1 ~ 7 : sharps mi : major/minor 0 : major 1 : minor |
7F |
01111111 |
xx dd |
Sequencer specific information xx : number of bytes to be sent dd : data |
Meta_Event
2. Musical Track Lib
프로젝트의 각 기능간의 음악 및 악보 정보를 주고 받기 위한
사용자 정의 라이브러리
· 클래스간의 관계
· 사용자 정의 형식
MusicScale - 음의 높낮이 정보를 정의한 열거형 사용자 정의 형식이다.
Octave |
0 |
1 |
…
|
5 |
…
|
10 | ||||
|
int |
MusicScale |
int |
MusicScale |
int |
MusicScale |
int |
MusicScale | ||
C |
0 |
Do0 |
12 |
Do1 |
60 |
Do5 |
120 |
Do10 | ||
C# |
1 |
DoSharp0 |
13 |
DoSharp1 |
61 |
DoSharp5 |
121 |
DoSharp10 | ||
D |
2 |
Re0 |
14 |
Re1 |
62 |
Re5 |
122 |
Re10 | ||
D# |
3 |
ReSharp0 |
15 |
ReSharp1 |
63 |
ReSharp5 |
123 |
ReSharp10 | ||
E |
4 |
Mi0 |
16 |
Mi1 |
64 |
Mi5 |
124 |
Mi10 | ||
F |
5 |
Fa0 |
17 |
Fa1 |
65 |
Fa5 |
125 |
Fa10 | ||
F# |
6 |
FaSharp0 |
18 |
FaSharp1 |
66 |
FaSharp5 |
126 |
FaSharp10 | ||
G |
7 |
Sol0 |
19 |
Sol1 |
67 |
Sol5 |
127 |
Sol10 | ||
G# |
8 |
SolSharp0 |
20 |
SolSharp1 |
68 |
SolSharp5 |
|
| ||
A |
9 |
La0 |
21 |
La1 |
69 |
La5 |
|
| ||
A# |
10 |
LaSharp0 |
22 |
LaSharp1 |
70 |
LaSharp5 |
|
| ||
B |
11 |
Si0 |
23 |
Si1 |
71 |
Si5 |
|
|
MusicBeat - 음의 박자 정보를 32분음표의 박자를 기본으로 정의한 열거형 사용자 정의 형식이다.
· Note
악보에 표시되는 음표와 쉼표의 정보를 클래스화하였다.
Note의 종류로는 기본적으로 한음을 표시하는 NomalNote, 동시에 여러음을 표시하는 Harmony, 쉼표를 표시하는 RestNote가 있다.
· MusicInfo
해당 트랙에 대하여 기본적인 음악적 정보를 저장하는 클래스이다.
· MusicaITrack
MusicInfo와 Note를 저장하는 클래스이다.
트랙 하나에 해당 클래스 하나라고 볼 수 있다.
· 실제 악보와 해당 라이브러리의 예
위와 같은 악보가 있다고 할 때 해당 라이브러리로 어떻게 표현 되는지 알아보자
우선 음표와 쉼표들을 Note클래스로 바꿔보면
이런식으로 나타낼 수 있다.
그리고 보표정보, 박자정보, 키정보를 MusicInfo클래스로 바꿔보면
MusicInfo | |||
Key = “#*1 Major” | |||
staffType = 0 | |||
tempoList
| |||
timeSignatureList
|
이와 같이 나타낼 수 있다
위 Note들은 MusicalTrack의 notelist에 차례대로 들어가며 위 MusicInfo는 MusicalTrack의 musicinfo에 대입된다
그리고 MusicalTrack의 instType은 기본적으로 0이며 trackName은 따로 정해진 것이 없으니 빈값이 들어 간다
3. Midi Chunk Data Lib
미디의 데이터를 다루는 라이브러리
미디 파일을 읽어와 분석하거나 MusicalTrackLib를 통해 미디를 생성, 미디파일로 저장 하는 역할을 한다.
· 클래스간의 관계
· MidiMetaEventInfo
여러 트랙들의 MetaEvent의 데이터들을 모아둔 클래스이다.
이름, 저작권정보, 가사 등
일부를 제외하고 연주에 큰 영향이 없는 정보들이 모여있다
자세한 설명은 생략한다.
· Event
미디 파일의 이벤트의 정보들을 클래스화 하였다.
Event의 종류로는 연주에 영향이 없는 정보위주의 MetaEvent,
연주와 관련된 MidiEvent 가 있다.
· MidiTrackChunkData
미디의 트랙부분의 데이터를 다루는 클래스이다.
미디의 트랙 수 만큼 해당 클래스도 존재하게 된다.
· MidiHeaderChunkData
미디의 헤더부분의 데이터를 다루는 클래스이다.
· MidiChunkData
미디의 전체적인 데이터를 다루는 클래스이다.
MidiHeaderChunkData와 MidiTrackChunkData를 내포하고 있다.
· 미디파일에서 MusicalTrack구하는 원리
위의 악보의 미디가 있다고 가정할 때 해당 미디 파일에서
MusicalTrack(악보데이터)를 구하는 원리를 알아보자
우선 MusicInfo에 해당하는 데이터들은 MetaEvent들에게서 쉽게 얻어 올 수 있으니 생략한다
문제는 Note들이다
미디파일내에는 박자데이터가 있는 것이 아니라
어떤시간에 소리를 켜고 소리를 끄고에 해당하는 데이터가 있는 것이다
이 데이터들에서 박자정보를 구해오는 것은 생각보다 쉽지 않다
이 방법을 간단하게 설명해보면 아래와 같다.
위 악보의 미디파일의 메타이벤트들을 제외한 소리를 내는 부분의
byte단위의 데이터들을 16진수로 보면 이와 같을 것이다.
00 |
C0 |
00 |
00 |
90 |
47 |
64 |
78 |
80 |
47 |
00 |
00 |
90 |
45 |
64 |
81 |
70 |
80 |
45 |
00 |
3c |
90 |
40 |
64 |
00 |
90 |
43 |
64 |
3c |
80 |
40 |
00 |
00 |
80 |
43 |
00 |
이를 이벤트 단위로 나눠 보면 아래와 같다 (timebase 120으로 가정)
악기설정 |
NoteOn |
NoteOff |
NoteOn |
NoteOff | |||||||||||||||||
00 |
C0 |
00 |
00 |
90 |
47 |
64 |
78 |
80 |
47 |
00 |
00 |
90 |
45 |
64 |
81 |
70 |
80 | ||||
|
NoteOn |
NoteOn |
NoteOff |
NoteOff | |||||||||||||||||
45 |
00 |
3c |
90 |
40 |
64 |
00 |
90 |
43 |
64 |
3c |
80 |
40 |
00 |
00 |
80 |
43 |
00 | ||||
| |||||||||||||||||||||
델타타임 |
메시지+채널 |
음계 |
음크기(속도) |
| |||||||||||||||||
이와 같이 이벤트들로 나누어 구하여 Event클래스로 저장한다
노트온과 노트오프의 위치를 악보의 위치에 표시하면 위와 같은데
노트온과 노트오프의 사이의 타임텀을 구하여 박자를 구하며
노트온의 카운트 개수를 구하여 0이면 쉼표, 0이 아니면 음표로 구분한다
위 설명으로 간략하게나마 설명하였는데 자세한 설명은 생략한다
해당 방법을 프로젝트 내의 소스를 보면 다음 페이지들과 같다.
-TimeTerm을 구하는 함수 소스
-TimeTerm과 NoteOn Count를 분석하여 음표,쉼표 구하는 함수소스
4. Midi Play Lib
미디의 플레이를 위한 라이브러리
· MidiPlay
미디의 플레이를 컨트롤 하는 클래스
· MidiShortMsgPlayer
실질적으로 미디의 소리를 내주는 클래스이다.
winmm.dll을 Import해주어 사용한다.
5. Midi Engine Lib
미디의 데이터를 다루며 플레이 기능 등 미디의 총괄적인 기능을 담당 하는 라이브러리
· MidiEngine
속성
MidiChunkData midiData |
미디의 데이터를 다루는 곳 |
메서드
MidiEndine(string) |
인자로 받은 filepath로 미디 파일을 불러와 분석하여 생성해주는 생성자 |
MidiEndine(MusicalTrack, string) |
인자로 받은 MusicalTrack를 분석하여 생성해주는 생성자 |
MidiEndine(List<MusicalTrack>, string) |
인자로 받은 MusicalTrack리스트를 분석하여 여러 트랙으로 생성해주는 생성자 |
SaveMidiFile(string) |
인자로 받은 filepath에 미디파일을 만들어준다. |
AdjustMetaEvent(string, string) |
입력 받은 메타이벤트타입의 메타이벤트 정보를 입력 받은 데이터로 수정해준다 |
AdjustMetaEvent(string, string, int) |
입력받은 트랙번호의 트랙에서 입력 받은 메타이벤트타입의 메타이벤트 정보를 입력 받은 데이터로 수정해준다 |
GetChList() |
각 트랙별 사용중인 채널을 반환해준다 |
GetMusicalTrackList() |
모든 트랙의 MusicalTrack을 리스트 형태로 반환해준다. |
GetMidiName() |
미디의 이름을 반환해주는다 |
PlayStrat() |
미디를 플레이 해준다 |
PlayStrat(float) |
미디를 플레이 해준다 |
PlayStrat(Dictionary<int, List<int>>) |
미디를 플레이 해준다. |
PlayStrat(float, Dictionary<int, List<int>>) |
미디를 플레이 해준다. |
PlayPause() |
플레이중인 미디를 일시정지 해준다. |
PlayStartNPause() |
플레이 여부에 따라 플레이와 일시정지를 해준다 |
PlayStartNPause(float) |
플레이 여부에 따라 플레이와 일시정지를 해준다 |
PlayStartNPause(Dictionary<int, List<int>>) |
플레이 여부에 따라 플레이와 일시정지를 해준다 |
PlayStartNPause(float, Dictionary<int, List<int>>) |
플레이 여부에 따라 플레이와 일시정지를 해준다 |
PlayStop() |
미디의 플레이를 멈춰준다 |
이벤트
MidiEventPlayEventHandler |
미디이벤트가 Winmm에 메시지를 보낼 때 발생되는 이벤트 이다. |
StartMusicEventHandler |
플레이를 시작할 때 발생되는 이벤트. |
PauseMusicEventHandler |
플레이를 일시정지할 때 발생되는 이벤트 |
StopMusicEventHandler |
플레이가 끝나거나 멈출 때 발생 되는 이벤트 |