class Dot{
PVector position;
boolean calling;
// am I calling?
int neighbour_call_count = 0;
// variable to count how many nearby creatures are calling
float dotSize = 5; //
int call_pitch = int(random(2,8))*120;
float pitch_variation = 0.6666666667;
int pitch_variation_2 = 3;
int call_interval = int(random(90,100));
// the time a creature has to wait before calling again
int interval_counter = 0;
// the interval counter starts at 0
float call_duration = 20.0;
SineWave call_sound;
SineWave call_sound_2;
Dot(PVector _position){
// a constructor - has the same name as the class;
// builds the new object;
// takes input parameters
position = _position;
call_sound = new SineWave(call_pitch, 0.0, out.sampleRate());
call_sound_2 = new SineWave(call_pitch * pitch_variation, 0.0, out.sampleRate());
call_sound.setPan(map(position.x, 0, width, -1, 1));
call_sound_2.setPan(map(position.x, 0, width, -1, 1));
out.addSignal(call_sound);
out.addSignal(call_sound_2);
calling = true;
}
void run(){
call();
render();
}
void call(){
if (interval_counter > call_duration) calling=false;
// If the call has endured longer than the interval count, stop calling.
neighbour_count = 0;
float newAmp = max (0,call_sound.amplitude() - (call_volume/call_duration));
call_sound.setAmp(newAmp);
if (vibra==true){
call_sound_2.setAmp(newAmp);
// Play the second frequency sound as well
}
if (vibra==false){
call_sound_2.setAmp(0);
// to stop the second sound from remaining as a ghost
}
interval_counter ++; // add 1 to the interval counter
for (int i=0; i<Dots.size(); i++){
// for every dot, run the following code
Dot otherDot = (Dot) Dots.get(i);
// get another dot from the array
float distance = PVector.dist(position, otherDot.position);
// ID distance between
if (distance < range_of_hearing) neighbour_count++;
// If the other dot is within range, count it as a neighbour
if (distance < range_of_hearing && otherDot.calling == true)
neighbour_call_count ++;
// if the neighbouring dot is calling, count it as a neighbour call
}
if (neighbour_call_count > neighbour_call_threshhold && interval_counter > call_interval) {
// If you have heard enough calls, and waited long enough...
calling=true;
// call!
neighbour_call_count = 0;
// Reset the counter for your neighbours' calls
call_sound.setPan(map(position.x, 0, width, -1, 1));
// map the placement of dots to the stereo array of sound
call_sound.setAmp(call_volume);
// Set the volume of the call.
dotSize = range_of_hearing;
// Increase the dot size as much as the range of hearing
interval_counter = 0; //int(random(-55,-45));
// Reset the interval counter.
}
call_duration = 100/(neighbour_count+1);
// Set the call to endure longer if the neighbours are fewer,
// ie if the call is more rare.
}
void render(){
strokeWeight(1);
stroke(call_pitch/2,call_pitch,255-call_pitch/2,100);
// Colour the lines according to the pitch of the call - not too much red, lots
// of green and blue in inverse relationship to the frequency, ie deepest notes
// are bluest.
if (calling==true) fill (call_pitch/4,call_pitch/2,255-call_pitch/4,220);
// Fill the dot like the lines only more intense colour.
if (calling==false) fill (call_pitch/4,call_pitch/2,255-call_pitch/4,200);
dotSize = max(20,dotSize-((range_of_hearing-35)/call_duration));
ellipse (position.x, position.y, dotSize, dotSize);
noFill();
ellipse(position.x, position.y, range_of_hearing,range_of_hearing);
for (int i=0; i<Dots.size(); i++){
// for every dot, run the following code
Dot otherDot = (Dot) Dots.get(i);
float distance = PVector.dist(position, otherDot.position);
// ID distance between
if (calling==true && distance < range_of_hearing
&& otherDot.neighbour_call_count>=neighbour_call_threshhold-1) {
// If your call is about to set off another call...
stroke(call_pitch/4,call_pitch/2,255-call_pitch/4,200);
strokeWeight(20);
line (position.x, position.y, otherDot.position.x, otherDot.position.y);
}
}
strokeWeight(1);
}
}
import ddf.minim.signals.*;
import ddf.minim.*;
import ddf.minim.analysis.*;
import ddf.minim.effects.*;
Minim minim;
AudioOutput out;
Dot D; // here is a variable called D of the type named Dot
ArrayList Dots; // a bucket of things, in this case dots
int numDots = 14; // the number of dots might vary
int neighbour_call_threshhold= 80; // the number of calls a dot must hear before it can call
int neighbour_count=0; // the number of dots within hearing range
int range_of_hearing = 150; // the distance (in diameter) a dot can hear
boolean vibra = false; // use a single sine wave by default
float call_volume = 0.05; // a variable for the volume of the call
int[] ControlsBG = {100, 100, 100, 100, 100, 100, 100, 100};
// as they are, it would work to just have two control background states:
// for active (bright) and inactive (dim) but I thought to leave them individual
// so it's easy to group the controls into pairs or some such (add/kill etc)
Dot draggedDot = null;
// There is no draggedDot
Timer timer = new Timer();
TimerTask task = new MyTimerTask();
private class MyTimerTask extends TimerTask {
public void run() {
// Actions for the five controls that work by clicking on the control area
if (mouseX < 50 && mouseY > 100 && mouseY < 150) {
range_of_hearing++;
}
if (mouseX < 50 && mouseY > 150 && mouseY < 200) {
if (range_of_hearing > 0) range_of_hearing--;
}
if (mouseX < 50 && mouseY > 300 && mouseY < 350) neighbour_call_threshhold--;
if (mouseX < 50 && mouseY > 350 && mouseY < 400) neighbour_call_threshhold++;
dragDots();
if (draggedDot != null) draggedDot.calling = true;
// If there's a draggedDot, make it call
}
}
public void mousePressed(MouseEvent e) {
timer.scheduleAtFixedRate(task, 0, 30); // Time is in milliseconds
// The second parameter is delay before the first run
// The third is how often to run it
if (mouseX < 50 && mouseY > 200 && mouseY < 250) vibra = true;
if (mouseX < 50 && mouseY > 250 && mouseY < 300) vibra = false;
if (mouseX < 50 && mouseY < 50){ // if the mouse is in the top-left corner...
PVector pos = new PVector(mouseX,mouseY); // make a new PVector
Dot newDot = new Dot (pos); // make a new dot with the position 'pos'
Dots.add(newDot); // add newDot to the arraylist
draggedDot = newDot; // make the new dot draggable as soon as it's created
newDot.calling=true; // make the new dot announce its arrival
}
}
public void mouseReleased(MouseEvent e) {
task.cancel();
task = new MyTimerTask();
// Will not stop execution of task.run() if it is midway
// But will guarantee that after this call it runs no more than one more time
fill(ControlsBG[2]);
// dim the 'kill a dot' control area
if (draggedDot == null) return;
// if there's no draggable dot, stop this function
if (draggedDot.position.x <50 && draggedDot.position.y < 100 ) {
// if you drag a dot into the top-left corner (including the
// 'add a dot' area
draggedDot.call_sound.setAmp(0);
// silence the dot and
Dots.remove(draggedDot);
// kill it
}
draggedDot = null; // turn off the dragged dot
}
void setup(){
smooth();
size (900,650);
frameRate(25);
minim = new Minim(this);
out = minim.getLineOut(Minim.STEREO);
Dots = new ArrayList();
makeDots(); //make the dots
}
void draw(){
background(80);
stroke(255,75);
strokeWeight(1);
rectMode(CENTER);
// define the control areas with a square for each
fill(ControlsBG[0]);
rect (25,25,50,50);
fill(ControlsBG[1]);
rect (25,75,50,50);
fill(ControlsBG[2]);
rect (25,125,50,50);
fill(ControlsBG[3]);
rect (25,175,50,50);
fill(ControlsBG[4]);
rect (25,225,50,50);
fill(ControlsBG[5]);
rect (25,275,50,50);
fill(ControlsBG[6]);
rect (25,325,50,50);
fill(ControlsBG[7]);
rect (25,375,50,50);
stroke(255);
noFill();
// draw the control symbols
ellipse (25,125,13,13);
ellipse (25,175,25,25);
strokeWeight(4);
line (25,15,25,35);
line (15,25,35,25);
line (15,75,35,75);
ellipse (25,125,25,25);
ellipse (25,175,13,13);
bezier (15,280,28,250,22,300,35,270);
strokeWeight(2);
// do these look like sine waves?
bezier (13,230,25,200,25,250,32,220);
bezier (17,230,29,200,27,250,38,220);
arc (21, 325, 16,16, -PI/4,PI/4);
arc (21, 325, 26,26,-PI/4,PI/4);
arc (21, 325, 36,36,-PI/4,PI/4);
arc (25, 375, 16,16, -PI/4,PI/4);
fill(255);
ellipse (18,325,12,12);
ellipse (22,375,12,12);
for (int i=0, lenDots = Dots.size(); i<lenDots; i++){
Dot drawDot = (Dot) Dots.get(i);
// (Dot) declares that it is a Dot
drawDot.run();
}
}
void makeDots(){
for (int i=0; i<numDots; i++){
PVector pos = new PVector(random(50,width),random(0,height-50));
//make a new PVector anywhere other than top-lef, top-right corners
Dot newDot = new Dot (pos); // make a new dot with the position 'pos'
Dots.add(newDot); // add newDot to the arraylist
}
}
void mouseMoved() {
ControlsBG[0] = ControlsBG[1] = ControlsBG[2] = ControlsBG[3] = ControlsBG[4] = ControlsBG[5] = ControlsBG[6] = ControlsBG[7] = 100; //turn all off
if (mouseX < 50 && mouseY < 400) {
int targetColour = 140;
int currentControl = mouseY/50;
if ((currentControl == 4 & vibra == true) || (currentControl == 5 & vibra == false)) targetColour = 100; // keep some off
if (currentControl == 1) targetColour = 100;
ControlsBG[currentControl] = targetColour;
}
}
void keyPressed(){
if (keyCode == UP) neighbour_call_threshhold++;
if (keyCode == DOWN) neighbour_call_threshhold--;
if (keyCode == LEFT) range_of_hearing--;
if (keyCode == RIGHT) range_of_hearing++;
if (keyCode == ENTER) vibra = true;
if (keyCode == TAB) vibra = false;
println("thresh: " + neighbour_call_threshhold);
println("range: " + range_of_hearing);
}
void stop(){
out.close();
minim.stop();
super.stop();
}
void dragDots(){
PVector mousePos = new PVector(mouseX,mouseY);
for (int i=0, lenDots = Dots.size(); i<lenDots; i++){
// for every dot, run the following code
Dot otherDot = (Dot) Dots.get(i);
// get another dot from the array
float distance = PVector.dist(mousePos, otherDot.position);
// ID distance between
if (distance < otherDot.dotSize){
// if the mouse is over the dot...
draggedDot = otherDot;
// make the dragged dot be otherdot
draggedDot.calling=true;
// and make the dragged dot call
}
}
}
void zmousePressed(){
// Actions for the five controls that work by clicking on the control area
if (mouseX < 50 && mouseY > 100 && mouseY < 150) {
range_of_hearing++;
}
if (mouseX < 50 && mouseY > 150 && mouseY < 200) {
range_of_hearing--;
}
if (mouseX < 50 && mouseY > 200 && mouseY < 250) vibra = true;
if (mouseX < 50 && mouseY > 250 && mouseY < 300) vibra = false;
if (mouseX < 50 && mouseY > 300 && mouseY < 350) neighbour_call_threshhold--;
if (mouseX < 50 && mouseY > 350 && mouseY < 400) neighbour_call_threshhold++;
dragDots();
if (draggedDot != null) draggedDot.calling = true;
if (mouseX < 50 && mouseY < 50){ // if the mouse is in the top-left corner...
PVector pos = new PVector(mouseX,mouseY); // make a new PVector
Dot newDot = new Dot (pos); // make a new dot with the position 'pos'
Dots.add(newDot); // add newDot to the arraylist
draggedDot = newDot; // make the new dot draggable as soon as it's created
newDot.calling=true; // make the new dot announce its arrival
}
}
void mouseDragged(){
if (mouseX <50 && mouseY > 50) ControlsBG[1]=100;
// As a dot is dragged off the 'add a dot' control area, dim the area
if (mouseX <50 && mouseY > 50 && mouseY <100) ControlsBG[1] = 140;
// If a dot is dragged into the 'kill a dot' area, brighten the area
if (draggedDot != null){ // if we have a dragged dot
draggedDot.position.x = constrain(mouseX,0,width);
draggedDot.position.y = constrain(mouseY,0,height);
// constrain draggability to the frame of the screen
}
}
void zmouseReleased(){
ControlsBG[2]=100;
// dim the 'kill a dot' control area
if (draggedDot == null) return;
// if there's no draggable dot, stop this function
if (draggedDot.position.x <50 && draggedDot.position.y < 100 ) {
// if you drag a dot into the top-left corner (including the
// 'add a dot' area
draggedDot.call_sound.setAmp(0);
// silence the dot and
Dots.remove(draggedDot);
// kill it
}
draggedDot = null; // turn off the dragged dot
}