This repository has been archived by the owner on Oct 21, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMIDI.cs
140 lines (111 loc) · 5.3 KB
/
MIDI.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
using Godot;
using System;
using System.Linq;
using Godot.Collections;
/// <summary>
/// A node that contains MIDI data.
/// </summary>
[Tool]
public class MIDI : Resource
{
private bool Loaded = false;
[Export] // file selection
public string FilePath { get; set; }
public Animation LoadFromFile(String filePath)
{
// load midi file from file_path
// get midi file data
File midiFile = new File();
midiFile.Open(filePath, File.ModeFlags.Read);
// get all data (bytes) from file
// convert ulong to long (because ulong is not supported by GetBuffer)
byte[] midiData = midiFile.GetBuffer((long)midiFile.GetLen());
midiFile.Close();
// create output animation
Animation anim = new Animation();
// read header chunk
RawMidiChunk headerChunk = new RawMidiChunk();
midiData = headerChunk.LoadFromBytes(midiData);
// parse header chunk
MidiHeaderChunk header = new MidiHeaderChunk();
header.ParseChunk(headerChunk, ref header);
// print header info
GD.Print("MIDI format: " + header.FileFormat);
GD.Print("Number of tracks: " + header.NumTracks);
GD.Print("Division type: " + header.DivisionType);
GD.Print("Ticks per quarter note: " + header.Division);
// create animation track for each midi track
for (int i = 0; i < header.NumTracks; i++)
{
// create animation track
anim.AddTrack(Animation.TrackType.Method);
anim.TrackSetPath(i, "../MidiManager");
}
float[] trackTimes = new float[header.NumTracks];
for (int trkIdx = 0; trkIdx < header.NumTracks; trkIdx++)
{
float trackTime = 0;
// read track chunk
RawMidiChunk trackChunk = new RawMidiChunk();
midiData = trackChunk.LoadFromBytes(midiData);
// parse track chunk
MidiTrackChunk track = new MidiTrackChunk();
track.ParseChunk(trackChunk, ref header);
// print out events
//GD.Print("Track " + trkIdx + " events:");
//GD.Print("Tempo: " + header.Tempo);
// loop through note events
float time = 0;
for (int i = 0; i < track.EventPointers.Count; i++)
{
// get event
MidiTrackChunk.MidiEventPointer eventPointer = track.EventPointers[i];
double deltaTime = 0.0f;
double tickDuration = (double)header.Tempo / (double)header.Division;
if (eventPointer.Type == MidiTrackChunk.MidiEventType.Meta)
{
MidiEventMeta metaEvent = track.MetaEvents[eventPointer.Index];
// insert as key in animation track
// key will contain type and data
Dictionary evtDict = new Dictionary();
evtDict.Add("method", "MetaEventInput");
evtDict.Add("args", new object[] { metaEvent.EventType, metaEvent.EventData, trkIdx });
// if it's a tempo change event, update the tempo
if (metaEvent.EventType == MidiEventMeta.MidiMetaEventType.SetTempo)
{
header.Tempo = GodotMidiUtils.ToInt24BigEndian(metaEvent.EventData, 0);
tickDuration = (double)header.Tempo / (double)header.Division;
}
}
if (eventPointer.Type == MidiTrackChunk.MidiEventType.Note)
{
MidiEventNote noteEvent = track.NoteEvents[eventPointer.Index];
// insert event as key in animation track
Dictionary evtDict = new Dictionary();
evtDict.Add("method", "NoteEventInput");
evtDict.Add("args", new object[] { noteEvent.Note, noteEvent.Data, noteEvent.EventType, trkIdx });
anim.TrackInsertKey(trkIdx, time, evtDict);
deltaTime = (double)noteEvent.DeltaTime;
}
if (eventPointer.Type == MidiTrackChunk.MidiEventType.System)
{
MidiEventSystem systemEvent = track.SystemEvents[eventPointer.Index];
// insert event as key in animation track
// note dict will store note, data, note type and track
Dictionary evtDict = new Dictionary();
evtDict.Add("method", "SystemEventInput");
evtDict.Add("args", new object[] { systemEvent.EventType, trkIdx });
anim.TrackInsertKey(trkIdx, time, evtDict);
deltaTime = (double)systemEvent.DeltaTime;
}
double deltaMicroseconds = (double)deltaTime * tickDuration;
double deltaSeconds = deltaMicroseconds / 1000000.0;
time += (float)deltaSeconds;
}
trackTime += time;
trackTimes[trkIdx] = trackTime;
}
anim.Length = trackTimes.Max();
return anim;
}
}