#include "music.h" //event times & durations are absolute integer milliseconds //--------------------------------------------------------------------------------------------------------------------------------------------- note::note(int n,int v,int d){ num=n; velocity=v; duration=d; } //--------------------------------------------------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------------------------------------------------- musicscore::musicscore() { timeframe=10000; flake.loadImage("flake.png"); flake.setAnchorPercent(0.5,0.5); } 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=480; //want:: seconds per tick in float float SPT =(MPQN/1000000.0f)/TPQN; float time=0; //counts up in float seconds to avoid rounding errors but converts to millis for map index map 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::iterator iter1; map::iterator iter2; int note; bool started=false; for (iter1 = events.begin(); iter1 != events.end(); ++iter1) { if (iter1->second->duration==144) { if (!started) { note=iter1->second->num; started=true; } 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; iter1->second->updown=iter1->second->numsecond->num==note?0:1; note=iter1->second->num; notes[iter1->first]=iter1->second; printf("%i: noteon %i %i %i\n",iter1->first,iter1->second->num,iter1->second->duration,iter1->second->updown); break; } } } } iter1 = notes.end(); iter1--; printf("processed %s: length %f, %i notes in %f seconds\n",filename.c_str(),((float)(iter1->first+iter1->second->duration)*.001f),notes.size(),ofGetElapsedTimef()-wt); //decimate notes to generate flakes that can be interacted with } void musicscore::setTimeframe(int millis) {timeframe=millis;} void musicscore::draw() { ofEnableAlphaBlending(); int scoreStart=ofGetElapsedTimeMillis()-startTime; int scoreEnd=scoreStart+timeframe; //temporary drawing method 46h - 52h int numnotes=16; int firstnote=70; float widthStep=((float)ofGetWidth())/numnotes; float heightStep=((float)ofGetHeight())/timeframe; map::iterator iter; for (iter = notes.lower_bound(scoreStart); iter != notes.upper_bound(scoreEnd); ++iter) { int thisnote=iter->second->num-firstnote; int thisstart=iter->first-scoreStart; int thislength=iter->second->duration; ofSetColor(ofColor::fromHsb(((float)thisnote*255)/numnotes,200,100)); ofRect(thisnote*widthStep,ofGetHeight()-(thisstart*heightStep),widthStep,-(thislength*heightStep)); ofSetColor(ofColor::fromHsb(((float)thisnote*255)/numnotes,200,255)); //different methods for generating flakes //ideally theres a variable clumping factor, this means pre-processing the flakes though //flake.draw((thisnote+0.5f)*widthStep,ofGetHeight()-(thisstart*heightStep)); //flake.draw((iter->second->updown*ofGetWidth()*0.33)+(ofGetWidth()*0.5),ofGetHeight()-(thisstart*heightStep)); flake.draw((((thisnote/5)*5)+3.5f)*widthStep,ofGetHeight()-(thisstart*heightStep)); } ofDisableAlphaBlending(); } //--------------------------------------------------------------------------------------------------------------------------------------------- song::song(string backfile,string melfile,string notefile) { backing.loadSound(backfile); melody.loadSound(melfile); notes.parseMidi(notefile); isPlaying=false; } void song::setTimeframe(int millis) {notes.setTimeframe(millis);} void song::play() { backing.play(); melody.play(); startTime=ofGetElapsedTimeMillis(); notes.startTime=startTime; isPlaying=true; } void song::stop() { backing.stop(); melody.stop(); isPlaying=false; } void song::preRoll(long preroll) { startTime=ofGetElapsedTimeMillis()+preroll; notes.startTime=startTime; isPreroll=true; isPlaying=true; } void song::draw(){ //how to deal with end/ track length/ part of game? if (isPreroll) { if (startTime