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
|
#include "music.h"
//event times & durations are absolute integer milliseconds
note::note(int n,int v,int d){
num=n;
velocity=v;
duration=d;
}
void musicscore::parseMidi(string filename){
// millis = 60000 / (BPM * PPQ)
// BPM = 60000000 / MQPN (last 3 bytes of midi tempoSet)
// http://www.lastrayofhope.com/2009/12/23/midi-delta-time-ticks-to-seconds/
// presume no change in time signature?
//2 passes:: extract notes & set abs times, then scan for
float wt=ofGetElapsedTimef();
float BPM=120.0f;
//input:: MPQN :: default 500000
float MPQN=60000000.0f/BPM;
//unknown:: ticks per quarter note
int TPQN=96;
//want:: seconds per tick in float
float SPT =(MPQN/1000000.0f)/TPQN;
float time=0; //counts up in float seconds but converts to millis for map index
map<int,note*> events;
if( !XML.loadFile(filename) ){
printf("unable to load %s check data/ folder\n",filename.c_str());
}else{
if(XML.pushTag("MidiFile")) {
for (int i=0;i<XML.getNumTags("TrackChunk");i++) {
XML.pushTag("TrackChunk",i);
for (int i=0;i<XML.getNumTags("Event");i++) {
time+=(SPT*XML.getAttribute("Event", "DeltaTimeTicks",0,i));
if (XML.getAttribute("Event", "Label","",i)=="TempoSet") {
string data=XML.getAttribute("Event", "Data","",i);
char* endptr;
int d1=strtoul(data.substr(6,2).c_str(),&endptr,16);
int d2=strtoul(data.substr(9,2).c_str(),&endptr,16);
int d3=strtoul(data.substr(12,2).c_str(),&endptr,16);
int MPQN=(d1<<16)+(d2<<8)+d3;
SPT =(MPQN/1000000.0f)/TPQN;
//printf("Tempo change: seconds per tick now: %f\n",SPT);
}
if (XML.getAttribute("Event", "Label","",i)=="NoteOn"||XML.getAttribute("Event", "Label","",i)=="NoteOff") {
string data=XML.getAttribute("Event", "Data","",i);
char* endptr;
int d1=strtoul(data.substr(0,2).c_str(),&endptr,16);
int d2=strtoul(data.substr(3,2).c_str(),&endptr,16);
int id=strtoul(XML.getAttribute("Event", "Id","",i).c_str(),&endptr,16);
if (id==128||id==144) events[(int)(time*1000.0f)]=new note(d1,d2,id); //noteon/off
}
}
XML.popTag();
}
}
XML.popTag();
}
//iterate events and compute durations now the absolute times are established: extract to notes
map<int,note*>::iterator iter1;
map<int,note*>::iterator iter2;
for (iter1 = events.begin(); iter1 != events.end(); ++iter1) {
if (iter1->second->duration==144) {
iter1->second->duration=0;
iter2=iter1;
while (++iter2 != events.end()) {
if ((iter1->second->num==iter2->second->num)&&(iter2->second->duration==128)) {
iter1->second->duration=iter2->first-iter1->first;
notes[iter1->first]=iter1->second;
//printf("%i: noteon %i %i\n",iter1->first,iter1->second->num,iter1->second->duration);
break;
}
}
}
}
printf("processed %s: %i notes in %f seconds\n",filename.c_str(),notes.size(),ofGetElapsedTimef()-wt);
}
song::song(string backfile,string melfile,string notefile) {
backing.loadSound(backfile);
melody.loadSound(melfile);
notes.parseMidi(notefile);
bpm=bpm; //can be used to set tempo eventually? keeps everything simple to do it this way
}
|