Thursday, August 26, 2010

Making the Mac Speak in Java

The Mac "say" program is an endless source of fun and mischief. In class, I like to make the Macs talk to the students sometimes using the say command from an ssh login from across the room.

While preparing an article on calling system commands in Java, I decided to combine that with the Mac "say" command in my Simple Video Game Kernel.

I'm posting this here, since I avoid posting platform-specific code at my Java programming blog.

Give it a try, if you've got a Mac handy, and enjoy.

/* A simple video game style kernel
by Mark Graybill, August 2010
Uses the Timer Class to move a ball on a playfield
30 times per second. Add additional "players" to
the playfield with appropriate control routines to
make a full game.

This version adds speech to the poor little ball getting
bounced off the walls. Mac-only, I'm afraid.

// Import Timer and other useful stuff:
import java.util.*;
// Import the basic graphics classes.
import java.awt.*;
import javax.swing.*;
// import java.lang.Math;

public class VGKernel extends JPanel{

// This is not a recommended coding practice, just a shortcut.
public Rectangle screen, ball; // The screen area and ball location/size.
public Rectangle bounds; // The boundaries of the drawing area.
public JFrame frame; // A JFrame to put the graphics into.
public VGTimerTask vgTask; // The TimerTask that runs the game.
public boolean down, right; // Direction of ball's travel.

// Create a constructor method:
public VGKernel(){
screen = new Rectangle(0, 0, 600, 400);
ball = new Rectangle(0, 0, 20, 20);
bounds = new Rectangle(0, 0, 600, 400); // Give some temporary values.
frame = new JFrame("VGKernel");
vgTask = new VGTimerTask();

// Create an inner TimerTask class that has access to the
// members of the VGKernel.
class VGTimerTask extends TimerTask{
public void run(){

// Now the instance methods:
public void paintComponent(Graphics g){
// Get the drawing area bounds for game logic.
bounds = g.getClipBounds();
// Clear the drawing area, then draw the ball.
g.clearRect(screen.x, screen.y, screen.width, screen.height);
g.fillRect(ball.x, ball.y, ball.width, ball.height);

public void moveBall(){
// Ball should really be its own class.
if (right) ball.x+=ball.width/4; // If right is true, move ball right,
else ball.x-=ball.width/4; // otherwise move left.
if (down) ball.y+=ball.height/4; // Same for up/down.
else ball.y-=ball.width/4;
if (ball.x > (bounds.width - ball.width)) // Detect edges and bounce.
{ right = false; ball.x = bounds.width - ball.width;
try { Process p = new ProcessBuilder("say", "-v", "Ralph",
"[[pbas +18]] [[rate +70]] Ow").start();}
catch( io){}
if (ball.y > (bounds.height - ball.height))
{ down = false; ball.y = bounds.height - ball.height;
try { Process p = new ProcessBuilder("say", "-v", "Ralph",
"[[pbas +18]] [[rate +70]] uh").start();}
catch( io){}
if (ball.x <= 0) { right = true; ball.x = 0;
try { Process p = new ProcessBuilder("say", "-v", "Ralph",
"[[pbas +18]] [[rate +70]] arg").start();}
catch( io){}
if (ball.y <= 0) { down = true; ball.y = 0;
try { Process p = new ProcessBuilder("say", "-v", "Ralph",
"[[pbas +18]] [[rate +70]] uh").start();}
catch( io){}

public static void main(String arg[]){

// Create a Timer object and an instance of VGKernel
java.util.Timer vgTimer = new java.util.Timer();
VGKernel panel = new VGKernel();

panel.down = true;
panel.right = true;
panel.frame.setSize(panel.screen.width, panel.screen.height);


// Set up a timer to do the vgTask regularly.
vgTimer.schedule(panel.vgTask, 0, 20);

The funny looking [[pbas +18]] type things in the "say" commands are inline commands to the Mac speech synthesizer to change the characteristics of the voice. You can omit these if you want to make the commands easier to read, then add them back in to see what affect they have (or play with the numbers.) You can find out more about using the Mac speech synthesizer at Apple's developer site.

No comments:

Post a Comment