• fullscreen
  • Dot.pde
  • acoustic_swarm_v2.pde
  • 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
    }
    

    code

    tweaks (0)

    license

    advertisement

    Cath

    Acoustic swarm

    Add to Faves Me Likey@! 32
    You must login/register to add this sketch to your favorites.

    A small swarm of dot agents call to one another, making a kind of music. Calls are created by a sine wave at various frequencies. Dots interact by listening to nearby dots, and calling only when they've heard enough calls.

    Make your own choir!

    Co-created with Subyeal and Vanessa as an intensive study, with plenty of help from @mtchl.

    bejoscha
    19 Mar 2010
    Very nice!
    I am currently toying with some network-systems as well, but I have not thought of looking int acoustics as well. Very inspireing.
    What gives the nodes in your sketch the "initial" trigger for sounds? I somehow missed that and don't want to go through the code...
    Cath
    19 Mar 2010
    Thanks bejoscha. The dots are coded to call when they are first rendered. It's the 'calling=true;' line in the Dot(PVector _position) function.
    Cath
    24 Mar 2010
    Just uploaded 'version 2' – a good guy called Paul Blackman did some code-cleaning and enhancing, so the hearing-range and call-likelihood buttons work better now. Thanks bro. Maybe you'll join the OP gang now? G'on!
    Didier Cascante
    21 May 2012
    Hello, I like the sketch. The graphics and sounds are great. Where to find such sounds? Thank you.
    You need to login/register to comment.