summaryrefslogtreecommitdiff
path: root/vfg/src/music.cpp
blob: 82e953ef4c9f21075493e296807d503a564a6219 (plain)
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
}