/* built with Studio Sketchpad:
* https://sketchpad.cc
*
* observe the evolution of this sketch:
* https://studio.sketchpad.cc/sp/pad/view/ro.JNgrI8QAqNh/rev.1667
*
* authors:
* Paul D
* license (unless otherwise specified):
* creative commons attribution-share alike 3.0 license.
* https://creativecommons.org/licenses/by-sa/3.0/
*/
// This sketch builds on a prior work, "Clocks", created by James Aspnes
// http://studio.sketchpad.cc/sp/pad/view/ro.9XwMmz-oB-t42/rev.3665
// this is all just a test
String chord_filename = "/static/uploaded_resources/p.3839/chords.txt";
LinearMotion clock; // clock value in seconds since midnight
PVector origin;
String current_key = "";
float radius = 100;
int step = 0;
int cur_offset = 0;
int cur_chord = 0;
String[] keys = {"C","G","D","A","E","B","F#","C#","Ab","Eb","Bb","F"};
Chord[] chords;
int index = 0;
String[] lines;
void setup() {
//noLoop();
size(int(4*radius),int(3*radius));
//size(600,600);
smooth();
frameRate(60);
// seconds since start of day
int t = hour() * 3600 + minute() * 60 + second();
clock = new LinearMotion(t, 1);
lines = loadStrings(chord_filename);
chords = new Chord[lines.length];
while (index < lines.length) {
String[] pieces = split(lines[index], '\t');
String[] values = split(pieces[1], ' ');
String chord_name = pieces[0];
int[] chord_values = new int[values.length];
//int[] chord_values[values.length];
int i = 0;
for (i = 0; i < values.length; i++) {
chord_values[i] = int(values[i]);
}
Chord c = new Chord(chord_values, chord_name, chord_values.length, 0);
chords[index] = c;
//c.drawchord(origin, radius);
index = index + 1;
}
}
void draw() {
background(255);
stroke(0);
fill(0);
drawClock(50+radius, 50+radius, radius, clock.get());
int i;
text("use the arrow keys to seleys to select a chord",0,10);
for (i = 0; i < chords.length; i++) {
Chord c = chords[i];
text(c.chordname, radius*3, i*20+30);
}
if (cur_chord==0) {
cur_chord = chords.length;
}
if (cur_offset ==0) {
cur_offset = 12;
}
Chord c = chords[cur_chord%chords.length];
c.keyoffset = cur_offset;
//c.keyoffset = (cur_chord/chords.length)%12;
c.drawchord(origin, radius);
c.printchord(origin);
fill(200);
text(c.chordname, radius*3, (cur_chord%9)*20+30);
}
void keyPressed(){
if(key == CODED) {
if (keyCode == UP) {
cur_chord--;
}
else if (keyCode == DOWN) {
cur_chord++;
}
if (keyCode == LEFT) {
cur_offset--;
}
else if (keyCode == RIGHT) {
cur_offset++;
}
}
}
// convert radial to rectangular coordinates
// (x,y) is origin
// r is radius as fraction of min(width, height)/2
// TWO_PI*frac is angle clockwise from up
PVector toRect(PVector o, float r, float frac) {
float theta = TWO_PI * frac;
return new PVector(o.x + r * sin(theta), o.y - r * cos(theta));
}
// like line() but takes two PVectors as arguments
void vLine(PVector s, PVector e)
{
line(s.x, s.y, e.x, e.y);
}
// draw a clock centered at (x,y) with radius r
// t specifies seconds since midnight
void drawClock(float x, float y, float r, float t) {
// don't muck up external styles
pushStyle();
origin = new PVector(x, y);
// clock face
pushStyle();
noFill();
strokeWeight(1);
ellipse(origin.x, origin.y, 2*r, 2*r);
popStyle();
// pips
for(int i = 1; i < 13; i++) {
PVector place = toRect(origin, r*1.2, i / 12.0);
PVector dot_place = toRect(origin, r, i / 12.0);
textAlign(CENTER, CENTER);
textSize(0.2*r);
ellipse(dot_place.x, dot_place.y, r/8, r/8);
text(keys[i%12], place.x, place.y);
}
popStyle();
}
class Chord {
int chordcolor;
int[] chordnotes;
int num_notes;
int keyoffset;
String chordname;
Chord(int[] notes, String name, int n, int offset) {
chordcolor = offset*255/12;
chordnotes= notes;
num_notes = n;
keyoffset = offset;
chordname = name;
}
void printchord(PVector place) {
String s = "" + keys[keyoffset%12] + " " + chordname;
text(s, origin.x-(s.length()*5), origin.y);
PVector key_place = toRect(origin, radius, keyoffset / 12.0);
//text(keys[keyoffset%12], key_place.x, key_place.y);
fill(200);
ellipse(key_place.x, key_place.y, radius/8, radius/8);
}
void drawchord(PVector orig, float r) {
pushStyle();
strokeWeight(4);
//stroke(50,100,200);
stroke(chordcolor, chordcolor, chordcolor);
for(int i = 0; i < num_notes; i++) {
PVector place = toRect(orig, r, (chordnotes[i%num_notes]+keyoffset)%12 / 12.0);
PVector next_place = toRect(orig, r, (chordnotes[(i+1)%num_notes]+keyoffset)%12 / 12.0);
vLine(place, next_place);
}
popStyle();
}
}
// base class for time-dependent variables
class Motion {
float x; // position
int t; // last time x was updated as returned by millis()
Motion(float x0) {
set(x0);
}
// update and return x
float get() {
return x;
}
// set new position
void set(float x0) {
x = x0;
t = millis();
}
}
class LinearMotion extends Motion {
float rate; // velocity in units / second
LinearMotion(float x0, float r) {
super(x0);
rate = r;
}
float get() {
// note we don't change x to avoid accumulating error
return x + rate*(millis() - t)/1000.0;
}
void setRate(float r) {
// fix where we were
set(get());
// then update rate
rate = r;
}
}