import ddf.minim.*; import org.json.*; import traer.physics.*; //import traer.animation.*; final float NODE_SIZE = 17; final float EDGE_LENGTH = 250; final float EDGE_STRENGTH = 0.2; final float SPACER_STRENGTH = 1000; Vector songs; int currentSong; PFont legendFont, labelFont, boxFont; int sylCounter; int selNode; color sylPalette[] = {#B7B697, #755522, #696C57, #B2862D, #914520, #639A41, #94D16E, #D9D362, #DDE2B7, #ABCDDD, #E8A07D, #1F998F, #BEBAD1, #8896AD}; int numRows, numCols, boxSize; // // P5 routines // void setup(){ size( 700, 700 ); Minim.start(this); smooth(); songs = new Vector(10); songs.addElement(new SyntaxNet("bk42w74.json")); songs.addElement(new SyntaxNet("bk43w73.json")); songs.addElement(new SyntaxNet("bk70bk62.json")); songs.addElement(new SyntaxNet("bk95bk3.json")); songs.addElement(new SyntaxNet("g81w58.json")); songs.addElement(new SyntaxNet("g83w57.json")); songs.addElement(new SyntaxNet("pk60gr7.json")); songs.addElement(new SyntaxNet("r15bl29.json")); songs.addElement(new SyntaxNet("r17pu46.json")); println("Added "+songs.size()+" songs"); currentSong = 0; SyntaxNet newNet = (SyntaxNet)songs.elementAt(currentSong); newNet.makeActive(); legendFont = loadFont("RockwellStd-20.vlw"); labelFont = loadFont("CaspariCaps-Bold-12.vlw"); boxFont = loadFont("CaspariCaps-26.vlw"); sylCounter = -1; // Set up the size of the syllable boxes area numRows = 5; numCols = 20; boxSize = width/numCols; } void draw(){ SyntaxNet net = (SyntaxNet)songs.elementAt(currentSong); background(#ebebe6); net.tick(0.5); net.draw(); if (!mousePressed && sylCounter!=-1 && sylCounter++ % 10 == 0){ net.nextSyl(); } } // // Event handling // void mousePressed(){ SyntaxNet net = (SyntaxNet)songs.elementAt(currentSong); selNode = net.hit(mouseX,mouseY); println("Click on "+selNode+" started at "+mouseX+","+mouseY); } void mouseDragged(){ if (selNode>=0){ SyntaxNet net = (SyntaxNet)songs.elementAt(currentSong); net.moveNode(selNode,mouseX,mouseY); } } void mouseReleased(){ selNode = -1; } void keyPressed() { if (key == ' '){ if (sylCounter==-1){ sylCounter = 0; } else{ sylCounter = -1; } }else if (key == CODED && (keyCode == RIGHT || keyCode == LEFT)){ SyntaxNet oldNet = (SyntaxNet)songs.elementAt(currentSong); oldNet.makeInactive(); if (keyCode == RIGHT){ currentSong++; if (currentSong >= songs.size()){ currentSong = 0; } }else if (keyCode == LEFT){ currentSong--; if (currentSong < 0){ currentSong = songs.size()-1; } } SyntaxNet newNet = (SyntaxNet)songs.elementAt(currentSong); newNet.makeActive(); }else if (key == 'c'){ SyntaxNet net = (SyntaxNet)songs.elementAt(currentSong); net.nextSyl(); } } // // Minim audio init // void stop() { Minim.stop(); super.stop(); } void resetMinim(){ Minim.stop(); Minim.start(this); } // // Birdsong code // public class SyntaxNet extends Object { //Smoother3D centroid; ParticleSystem physics; int activeSyl; // Index of the current syl in the particles array int repeatsLeft; // Index of the current repeat (if any) int activeRepeat; // Counter to help draw loops around nodes String birdName; // Taken from the filename String syls; // mapping of indices to syl names (keys for hashtables) Hashtable repeats = new Hashtable(); // arrays of probs for self-repeating syls Hashtable edges = new Hashtable(); // array of springs to downstream syls Hashtable edgeProbs = new Hashtable(); // array of doubles with downstream probs Hashtable edgeTargets = new Hashtable(); // array of Strings with downstream syl names Hashtable audioSamples = new Hashtable(); // AudioFileIn objects for the syls' .wav files Vector history = new Vector(); SyntaxNet(String jsonFile){ // expression println("Loading "+jsonFile); physics = new ParticleSystem( 0.0000001, 0.25 ); birdName = jsonFile.substring(0,jsonFile.indexOf(".")); println("Bird: "+birdName); // Load in the syntax data String synData[] = loadStrings(jsonFile); String synText = synData[0]; try { JSONObject synObj = new JSONObject(synText); JSONObject sylRepeats = synObj.getJSONObject("repeats"); JSONArray trans = synObj.getJSONArray("fwd"); int i,j, numSyls; syls = synObj.getString("syls"); activeSyl = 0; repeatsLeft = 0; numSyls = syls.length(); double transMat[] = new double[numSyls*numSyls]; // col: current syl, row: next syl // Unpack json data into object instance for (i=0; i0.05){ numDownstream++; } } if (numDownstream>0){ // Make arrays of downstream springs & probs... int nextIdx = 0; Spring downstream[] = new Spring[numDownstream]; double probs[] = new double[numDownstream]; String downstreamNames[] = new String[numDownstream]; for (j=0; j0.05){ if (i==j){continue;} downstream[nextIdx] = physics.makeSpring( p_i, p_j, EDGE_STRENGTH, EDGE_STRENGTH, (float)(EDGE_LENGTH * (1.0-transProb)) ); probs[nextIdx] = transMat[i + numSyls*j]; downstreamNames[nextIdx] = ""+syls.charAt(j); nextIdx++; } } // ... and add them to their respective hashtables String sylName = ""+syls.charAt(i); edges.put(sylName,downstream); edgeProbs.put(sylName,probs); edgeTargets.put(sylName,downstreamNames); }else{ println("No syls downstream from "+syls.charAt(i)+" in song "+jsonFile); } // Make everything repell everything else for (j=0; jbottomPadding)){ yAdjust *=-1.; } for (int i=0; i0){ // We're in a repeat cycle, see if we continue repeatsLeft--; activeRepeat++; //println(repeatsLeft+" more repeats"); }else{ // Roll the dice to decide on a new sylable ... int nextEdgeIdx = randomSel((double[])edgeProbs.get(currSyl)); String possibleTargets[] = (String [])edgeTargets.get(currSyl); int nextSylIdx = syls.indexOf(possibleTargets[nextEdgeIdx]); activeSyl = nextSylIdx; // ... and number of repeats if (repeats.containsKey(""+syls.charAt(activeSyl))){ int numRepeats = randomSel((double [])repeats.get(""+syls.charAt(activeSyl))); repeatsLeft = numRepeats+1; activeRepeat = 0; }else{ repeatsLeft = 0; activeRepeat = 0; } } AudioSample snd = (AudioSample)audioSamples.get(""+syls.charAt(activeSyl)); snd.trigger(); history.addElement(""+syls.charAt(activeSyl)); } /*print("Syls:"); for (int i=0; i0){ probs[i]+=probs[i-1];} //println("Prob "+i+": "+probs[i]); } double selection = random(1.0); for (int i=0; inumRows*numCols){ for (int i=0; i0){ String prevSyl = (String)history.elementAt(i-1); if (thisSyl.equals(prevSyl)){ shouldDraw = false; } } if (shouldDraw){ textFont(boxFont); textAlign(CENTER); text(thisSyl,cursor[0]+boxSize/2,cursor[1]+3*boxSize/4-1); } cursor[0] += boxSize; if (cursor[0]>=width){ cursor[0] = 0; cursor[1] += boxSize; } } // label this bird fill(60); textAlign(LEFT); textFont(legendFont,20); text("Bird ",10,height-8-numRows*boxSize); fill(120); text(birdName,10+textWidth("Bird "),height-8-numRows*boxSize); } }