summaryrefslogtreecommitdiff
path: root/TSITOplayer/TSITOplayer.pde
blob: 1de0862ffb5cfc091588fe843373f715064a2301 (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
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
//pixelstick image generator
//
//pixelstick playing at 20fps, 400 pixel frame makes a 20 second shot
//200 pixels height
//notes 36-45 velocities 3-122
//notes vary by 9
//multiply notes movement by 9 = 81
//add 60


import java.io.File;
import ddf.minim.*;
import java.util.NavigableMap;
import java.util.TreeMap;
import java.util.Iterator;
import java.util.Map;

Table table;

String audioFileName = "The Son is the One.mp3";
int NOTE_LOW = 36, NOTE_HIGH = 45;

int[] notes=new int[NOTE_HIGH+1];
int[] oldnotes=new int[NOTE_HIGH+1];
color[] cols={#FF0000,#FF00BF,#8000FF,#013ADF,#00FFFF,#00FF40,#80FF00,#FFFF00,#FF8000};

int NOTE_SCALE=12,NOTE_OFFSET=60;

Minim minim;
AudioPlayer audioPlayer;

int pw,ph;

int ln=200, hn=0, lv= 200, hv=0;

boolean loaded=false;

PImage sc;

class Event {
  int pitch;
  int velocity;
  Event(int p,int v){
    pitch=p;
    velocity=v;
  }
}
//NavigableMap<int, ArrayList<event>> events = new NavigableMap<int, ArrayList>();
NavigableMap events= new TreeMap();
Iterator iterator;
ArrayList eventlist;
int startTime;
int noteTime=0;

void setup(){
  size(400, 200);
  sc=createImage(width,height,RGB);

  
  table= loadTable("TSITO.csv", "header");
  println(table.getRowCount() + " total rows in table"); 
  for (TableRow row : table.rows()) {
    
    
    int pitch = row.getInt("pitch");
    int velocity = row.getInt("velocity");
    String event = row.getString("event");
    int time = (int)(row.getInt("time")*0.55309734513274); //tick to ms
    if (event.equals("Note_on_c")||event.equals("Note_off_c")){
      ArrayList templist;
      if (!events.containsKey(time)){
        templist=new ArrayList();
      }
      else templist=(ArrayList)events.get(time);
      templist.add(new Event(pitch,velocity));
      events.put(time,templist);
    }
    

    //println(event + " " + time+","+pitch);
  }
  println("stored "+events.size()+" note events");
  
  for (int i=NOTE_LOW;i<NOTE_HIGH;i++){
    notes[i]=0;
    oldnotes[i]=0;
  }
  

  
   //load audio file
  minim = new Minim(this);
  audioPlayer = minim.loadFile(audioFileName, 2048);
  audioPlayer.play();
  //audioPlayer.loop(9999999);
  //audioPlayer is started when midi sequencer is started
  startTime =millis();
  
  iterator = events.keySet().iterator();
  if(iterator.hasNext()){
      noteTime   = (Integer)iterator.next();
      eventlist=(ArrayList)events.get(noteTime);
      println("first event at time "+noteTime+" (time now "+millis()+")");  
  }
  
  //the default tempo is 120 beats/minute, which is equivalent to tttttt=500000
  //<delta_time>
  //is the number of 'ticks' from the previous event
  //and is represented as a variable length quantity
 /*
The formula is 60000 / (BPM * PPQ) (milliseconds).

Where BPM is the tempo of the track (Beats Per Minute).

(i.e. a 120 BPM track would have a MIDI time of (60000 / (120 * 192)) or 2.604 ms for 1 tick.
*/

//tempo = 530973
//bpm = 127.43352
//ms per tick= 60000 / (113 * 192) ms
//2.765486725663717 ms per tick
/*

apparently logic is 960 ppq (hearsay)

ms per tick = 60000 / (113 * 960) = 0.55309734513274


strategy:
create a data structure of events with time in ms
AT EVERY FRAME
check for a new event that has just passed
turn on/off notes as necessary

map data structure?
given a time in millis, need to be able to find note with biggest time less than this time
*/

  background(0);
  rectMode(CORNER);
  //noStroke();
  colorMode(RGB, 255);
  
  frameRate(50);
  
}

void draw(){
  

  loadPixels();
  for (int x=1;x<width-2;x+=2){
    for (int y=0;y<height;y++){
      pixels[(y*width)+x]=pixels[(y*width)+x+2];
      pixels[(y*width)+x+1]=pixels[(y*width)+x+2];
    }
  }
  
  for (int y=0;y<height;y++){
    pixels[(y*width)+(width-1)]=color(#000000);
  }
  
  int trackTime=millis()-startTime;
  if (noteTime<trackTime){
    eventlist=(ArrayList)events.get(noteTime);
    for (Event e : (ArrayList<Event>)eventlist){
      notes[e.pitch]=e.velocity;
      //println(e.velocity+" "+noteTime+" "+e.pitch+" (time now "+trackTime+")");
    }
    if(iterator.hasNext()){
      noteTime   = (Integer)iterator.next();
    }
  }
  
  NavigableMap laternotes = events.tailMap(millis(), true);
  //println(laternotes.size()+" notes after "+millis());
  

  for(int i = NOTE_LOW; i < NOTE_HIGH; i++){
    int notecentre=((i-NOTE_LOW)*NOTE_SCALE)+NOTE_OFFSET;
    if (notes[i]!=oldnotes[i]){
      if (notes[i]>0) {
        //println("startnote "+i);
      }
      else {
        //println("stopnote "+i);
      }
      for (int j=notecentre-(NOTE_SCALE*2);j<=notecentre+(NOTE_SCALE*2);j++){
        pixels[(j*width)+(width-1)]=cols[i-NOTE_LOW];
      }
    }
    else if (notes[i]>0){
      //println("note "+i);
      pixels[((notecentre-(NOTE_SCALE*2))*width)+(width-1)]=cols[i-NOTE_LOW];
      pixels[((notecentre+(NOTE_SCALE*2))*width)+(width-1)]=cols[i-NOTE_LOW];
    }
    oldnotes[i]=notes[i];
  }

  updatePixels();
  
    //saveFrame("frames/img####.png");
    if (!audioPlayer.isPlaying()) {
      println("notes "+ln+"-"+hn+" velocities "+lv+"-"+hv);
      exit();
    }
}

//change rate of drop?
//veering??


void keyPressed() {
 println("notes "+ln+"-"+hn+" velocities "+lv+"-"+hv);
}