Difference between revisions of "Tutorial: Playing Sounds in a Separate Thread"

From dftwiki3
Jump to: navigation, search
 
Line 5: Line 5:
 
<br />
 
<br />
 
{|
 
{|
|
+
| style="width: 30%; background-color: white;"|
 
__TOC__
 
__TOC__
|
+
| style="width: 10%;" |
 +
&nbsp;
 +
| style="width: 60%;" |
 
<center>
 
<center>
[[Image:noisyBalls.png]]
+
[[Image:noisyBalls.png|400px]]
 
</center>
 
</center>
 
|}
 
|}

Latest revision as of 10:50, 11 January 2014

--D. Thiebaut (talk) 17:54, 10 January 2014 (EST)


This tutorial present a simple Processing sketch that uses a thread to play sounds. Two circles bounce inside a rectangular box and a "beep" is generated every time they touche the boundaries. Two different versions are proposed, one using Processing's native IDE, one using Eclipse.


 

NoisyBalls.png


Processing IDE Version

The Main Sketch


// NoisyBalls.pde
// D. Thiebaut
// A sketch that animates two balls bouncing off the walls.
// It uses the minim library to play sounds.
import ddf.minim.signals.*;
import ddf.minim.*;
import ddf.minim.analysis.*;
import ddf.minim.ugens.*;
import ddf.minim.effects.*;

// the coordinates and speeds of two circles
int x1, y1, deltax1, deltay1;
int x2, y2, deltax2, deltay2;

// the thread that plays the sounds...
MyThread sonar;

void setup() {
    // create the sound player
    Minim minim = new Minim(this);
    AudioPlayer player = minim.loadFile( "sonar.wav" );

    // define geometry and window
    textAlign(CENTER);
    size( 600, 400 );
    smooth();

    // position and activate the two circles
    x1 = width/2;
    y1 = height/2;
    deltax1 = 4;
    deltay1 = 3;

    x2 = width/2;
    y2 = height/2;
    deltax2 = 2;
    deltay2 = 5;

    // create the thread and start it.
    sonar = new MyThread( minim, player );
    sonar.start();
}
  

void draw() {
    // erase window
    background( 0 );

    // put message in middle
    text( "Press any key to quit...", width/2, height/2 );

    // show the two moving balls
    ellipse( x1, y1, 20, 20 );
    ellipse( x2, y2, 20, 20 );

    // update positions of Ball 1, and if necessary change direction of movement
    x1 += deltax1;
    if ( x1 < 0 || x1 > width ) { 
      deltax1 = -deltax1;
      // beep! 
      sonar.playNow();  
    }
    y1 += deltay1;
    if ( y1 < 0 || y1 > height ) { 
      deltay1 = -deltay1; 
      // beep!
      sonar.playNow();
    }

    // update positions of Ball 2, and if necessary change direction of movement
    x2 += deltax2;
    if ( x2 < 0 || x2 > width ) { 
      deltax2 = -deltax2; 
      sonar.playNow();
    }
    y2 += deltay2;
    if ( y2 < 0 || y2 > height ) { 
      deltay2 = -deltay2; 
      sonar.playNow();
    }
}

// keyPressed():  if a key is pressed, simply stop the sketch
void keyPressed() {
  // set sonar to stop running
  sonar.quit();

  // wait 500 ms (1/2 sec)
  try {
    Thread.sleep( 500 );
  }
    catch ( InterruptedException e ) {
  }

  // then exit completely
  exit( );
}


The Thread Class


The thread contains an endless loop in its run() method, and will keep on checking the playNow boolean. If it is true, then the beep must be played. The endless loop is controlled by a boolean, which if true will cause the loop to end, and the run() method to stop.

// MyThread.pde
// D. Thiebaut
import ddf.minim.spi.*;
import ddf.minim.signals.*;
import ddf.minim.*;
import ddf.minim.analysis.*;
import ddf.minim.ugens.*;
import ddf.minim.effects.*;

class MyThread extends Thread {
   Minim minim;
   AudioPlayer player;
   boolean quit;
   boolean playNow;
  
   MyThread( Minim m, AudioPlayer p ) {
     minim = m;
     player = p;
     quit    = false;
     playNow = false;
   }
  
   public void playNow() {
     playNow = true;
   }
  
   public void quit() {
     quit = true;
   }
  
   void run() {
     while ( !quit ) {
       // wait 10 ms, then check if need to play
       try {
         Thread.sleep( 10 );
       } catch ( InterruptedException e ) {
         return;
       }
      
       // if we have to play the sound, do it!
       if ( playNow ) {
         playNow = false;
         player.play();
         player.rewind();
       }
      
       // go back and wait again for 10 ms...
    }
 }
}


The Sound File


The sound file used is called "sonar.wav" and is available here. It is stored in a folder called data in the main sketch folder.


File Hierarchy


The file hierarchy for this project is the following:

  • NoisyBalls (folder)
    • NoisyBalls.pde (Processing program)
    • MyThread.pde (Thread program)
    • data
      • sonar.wav (the sound file)



Eclipse Version


To port this sketch to Eclipse, you need to

  1. create two Java classes in a new project, and import several library files from the Processing 2 Application folder.
  2. Once the files are imported, add them to the build path (right click on the jar file, select Build Path, then Add to Build Path).
  3. Create a data folder in the bin folder containing the class files, and store the sonar.wav file in the new data folder.


PlaySoundEclipseLibraries.png


Main Java Class


// BeepingBalls.java
// D. Thiebaut
// A Java Class that animates two balls bouncing off the walls.
// It uses the minim library and a thread to play a sound file (beep) when
// the balls hit the walls.

import processing.core.PApplet;
import ddf.minim.*;

public class BeepingBalls extends PApplet {
	// the coordinates and speeds of two circles
	int x1, y1, deltax1, deltay1;
	int x2, y2, deltax2, deltay2;

	// the thread that plays the sounds...
	MyThread sonar;

	public void setup() {
		// create the sound player
		Minim minim = new Minim(this);
		AudioPlayer player = minim.loadFile("sonar.wav");

		// define geometry and window
		textAlign(CENTER);
		size(600, 400);
		smooth();

		// position and activate the two circles
		x1 = width / 2;
		y1 = height / 2;
		deltax1 = 4;
		deltay1 = 3;

		x2 = width / 2;
		y2 = height / 2;
		deltax2 = 2;
		deltay2 = 5;

		// create the thread and start it.
		sonar = new MyThread(minim, player);
		sonar.start();
	}

	public void draw() {
		// erase window
		background(0);

		// put message in middle
		text("Press any key to quit...", width / 2, height / 2);

		// show the two moving balls
		ellipse(x1, y1, 20, 20);
		ellipse(x2, y2, 20, 20);

		// update positions of Ball 1, and if necessary change direction of
		// movement
		x1 += deltax1;
		if (x1 < 0 || x1 > width) {
			deltax1 = -deltax1;
			// beep!
			sonar.playNow();
		}
		y1 += deltay1;
		if (y1 < 0 || y1 > height) {
			deltay1 = -deltay1;
			// beep!
			sonar.playNow();
		}

		// update positions of Ball 2, and if necessary change direction of
		// movement
		x2 += deltax2;
		if (x2 < 0 || x2 > width) {
			deltax2 = -deltax2;
			sonar.playNow();
		}
		y2 += deltay2;
		if (y2 < 0 || y2 > height) {
			deltay2 = -deltay2;
			sonar.playNow();
		}
	}

	// keyPressed(): if a key is pressed, simply stop the sketch
	public void keyPressed() {
		// set sonar to stop running
		sonar.quit();

		// wait 500 ms (1/2 sec)
		try {
			Thread.sleep(500);
		} catch (InterruptedException e) {
		}

		// then exit completely
		exit();
	}
}


Thread Class


// MyThread.java
// D. Thiebaut
// A class that implements a thread that runs a continuous loop where
// every 10 ms it checks to see if the member variable playNow is true.
// If it is, it starts playing a sound, then goes back to sleep for 10 ms.
//

import ddf.minim.*;

class MyThread extends Thread {
   Minim minim;
   AudioPlayer player;
   boolean quit;
   boolean playNow;
  
   MyThread( Minim m, AudioPlayer p ) {
     minim = m;
     player = p;
     quit    = false;
     playNow = false;
   }
  
   public void playNow() {
     playNow = true;
   }
  
   public void quit() {
     quit = true;
   }
  
   public void run() {
     while ( !quit ) {
       // wait 10 ms, then check if need to play
       try {
         Thread.sleep( 10 );
       } catch ( InterruptedException e ) {
         return;
       }
      
       // if we have to play the sound, do it!
       if ( playNow ) {
         playNow = false;
         player.play();
         player.rewind();
       }
      
       // go back and wait again for 10 ms...
    }
   }
}