This section provides the source code for the AMMSMIDlet example. For a complete Eclipse project ZIP file, see the S60 MIDP SDK, from Third Edition Feature Pack 1 onwards.
The examples include the following classes:
package com.nokia.mid.mansion; import javax.microedition.midlet.*; import javax.microedition.lcdui.*; /** * AMMSMIDlet class. * */ public class AMMSMIDlet extends MIDlet implements CommandListener { Display display; MansionCanvas canvas; // The main screen private Command exitCommand = new Command("Exit", Command.EXIT, 1); private Command toggleCommand = new Command("Start", Command.SCREEN, 1); private Command helpCommand = new Command("Help", Command.HELP, 1); private Command aboutCommand = new Command("About", Command.HELP, 2); private Form helpScreen, aboutScreen; /* * Create the canvas */ public AMMSMIDlet() { display = Display.getDisplay(this); canvas = new MansionCanvas(display); canvas.addCommand(exitCommand); canvas.addCommand(toggleCommand); canvas.addCommand(helpCommand); canvas.addCommand(aboutCommand); canvas.setCommandListener(this); } public void startApp() throws MIDletStateChangeException { canvas.start(); } public void pauseApp() { canvas.pause(); } public void destroyApp(boolean unconditional) throws MIDletStateChangeException { canvas.destroy(); } /* * Respond to a command issued on the Canvas. */ public void commandAction(Command c, Displayable s) { if (c == toggleCommand) { if (canvas.isPaused()) canvas.start(); else canvas.pause(); } else if (c == helpCommand) { canvas.pause(); showHelp(); } else if (c == exitCommand) { try { destroyApp(false); notifyDestroyed(); } catch (MIDletStateChangeException ex) { } } else if (c == aboutCommand) { canvas.pause(); showAbout(); } } /* * Put up the help screen. Create it if necessary. * Add only the Resume command. */ void showHelp() { if (helpScreen == null) { helpScreen = new Form("Walking Help"); helpScreen.append("^ = walk forward\n"); helpScreen.append("v = walk backwards\n"); helpScreen.append("< = turn left\n"); helpScreen.append("> = turn right\n"); helpScreen.append("fire = change reverb manually\n"); } helpScreen.addCommand(toggleCommand); helpScreen.setCommandListener(this); display.setCurrent(helpScreen); } /* * Put up the about screen. Create it if necessary. * Add only the Resume command. */ void showAbout() { if (aboutScreen == null) { aboutScreen = new Form("About AMMS Mansion demo"); aboutScreen.append("This MIDlet demonstrates the 3D audio capabilities of AMMS API.\n"); aboutScreen.append("\n"); aboutScreen.append("Copyright (c) 2006 Nokia. All rights reserved.\n"); aboutScreen.append("\n"); aboutScreen.append("\n"); } aboutScreen.addCommand(toggleCommand); aboutScreen.setCommandListener(this); display.setCurrent(aboutScreen); } }
package com.nokia.mid.mansion; import javax.microedition.lcdui.Graphics; /** * Contains the walls and the reverb settings. * */ public class House { public static final int NUM_REV_PRESETS = 10; private static final int ALLEY=0,ARENA=1, AUDITORIUM=2,BATHROOM=3,CAVE=4, HALLWAY=5,HANGAR=6,LIVINGROOM=7,MOUNTAINS=8,ROOM=9; static final String REV_PRESETS[] = { "alley", "arena", "auditorium", "bathroom", "cave", "hallway", "hangar", "livingroom", "mountains", "room" }; // coordinates and reverb settigns of the rooms: private static final int[][] ROOMS = {//{Y_MIN,Y_MAX,X_MAX,REVERB_PRESET} {6000,6000,0,MOUNTAINS}, // dummy (sentinel) {3000,10000,4000,MOUNTAINS}, //yard {6000,7000,8000,HALLWAY}, {2000,10000,19000,AUDITORIUM}, {3500,3500,19000,ALLEY} // dummy (sentinel) }; public House (){ } /** * @return true, if given coordinates are inside of the house. */ public boolean isInside(int x, int y) { boolean inside = false; final int room = inWhichRoom(x); if (y+100 < ROOMS[room][1] && y-100 > ROOMS[room][0]) inside = true; return inside; } /** * Tells in which room is the given x-coordinate in. * * @return room index */ public int inWhichRoom(int x) { int room = ROOMS.length-1; for (int i = 0; i<ROOMS.length-1; i++) { if (x > ROOMS[i][2]) { room = i+1; } else { break; } } if (room >= ROOMS.length) System.out.println("bug in House.inWhichRoom()! room=" + room); return room; } /** * Gets the reverb preset name for the given room */ public static String presetName(int room) { if(room < ROOMS.length && room >= 0) { return REV_PRESETS[ROOMS[room][3]]; } else { return ""; } } /** * Gets reverb preset name by index */ public static String presetNameByIndex(int index) { if(index < NUM_REV_PRESETS && index >= 0) { return REV_PRESETS[index]; } else { return ""; } } /** * Draws the house to given Graphics. * @param dx x translation in millipixels * @param dy y translation in millipixels * @param rot rotation in deci-angles */ public void draw(Graphics g, int dx, int dy, int rot) { for (int i=1; i for (int i=1; i
package com.nokia.mid.mansion; import javax.microedition.lcdui.*; /** * Handles canvas and also Spectator. */ public class MansionCanvas extends Canvas { final Display display; final int width, height; boolean paused; final static int NUM_SOURCES = 4; private Source[] sources = new Source[NUM_SOURCES]; private House house; private Walker walker; int forcedReverb = -1; String msg = ""; private int msgColor = 0xcc11cc; public MansionCanvas(Display d) { display = d; paused = true; width = getWidth(); height = getHeight(); house = new House(); // init sources: sources[0] = new Source(6000, 6500, "/largedog16k.wav", house, this); sources[1] = new Source(10000, 3500, "/budgie_16k.wav", house, this); sources[2] = new Source(1100, 6000, "/owl_16k.wav", house, this); sources[3] = new Source(1150, 6000, "/cock_16k.wav", house, this); for (int i = 0; i<NUM_SOURCES; i++){ System.out.println("starting source "+ i); Thread t = new Thread(sources[i]); t.start(); } walker = new Walker(); msg = "Welcome to Mansion!"; } /** * Draws everything. */ protected void paint(Graphics g) { int x_min = g.getClipX(); int y_min = g.getClipY(); int w = g.getClipWidth(); int h = g.getClipHeight(); // Empty the frame g.setColor(0xffffff); g.fillRect(x_min, y_min, w, h); g.translate(width/2, height/2); // move origin to the center // Draw sources for (int i = 0; i<NUM_SOURCES; i++) { sources[i].draw(g, walker.getX(), walker.getY(), walker.getRot()); } // Draw house (walls and doors) house.draw(g, walker.getX(), walker.getY(), walker.getRot()); // Draw player (walker) at origin: g.setColor(0xdd2222); g.fillRect(-4, -1, 9, 2); // shoulders g.setColor(0x66660a); g.drawLine(0, -3, 0, -3); // nose g.setColor(0); g.fillArc(-2, -2, 5, 5, 0, 360); // head g.translate(-width/2, -height/2); // reset translation // Draw the frame g.setColor(0); g.drawRect(0, 0, width-1, height-1); // Draw message: if (msg != null) { g.setColor(0xffffff); g.setClip(0, height-14, width, height); g.fillRect(0, height-20, width-2, 18); g.setColor(msgColor); g.drawString(msg, 5, height-14, 0); g.setColor(0); g.drawRect(0, 0, width-1, height-1); } occludeSources(); }//paint() /** * Starts sources that are in the same room (or space) and stops the rest */ private void occludeSources() { int listeningRoom = house.inWhichRoom(walker.getX()); for (int i = 0; i<NUM_SOURCES; i++) { if(listeningRoom == house.inWhichRoom(sources[i].getX())) { sources[i].start(); } else { sources[i].stop(); } } } public void keyPressed(int keyCode) { keyAction(keyCode); } public void keyRepeated(int keyCode) { keyAction(keyCode); } private void keyAction(int keyCode) { int action = getGameAction(keyCode); switch (action) { case LEFT: walker.rotateLeft(); break; case RIGHT: walker.rotateRight(); break; case UP: walker.moveForward(house); break; case DOWN: walker.moveBackward(house); break; case FIRE: forcedReverb += 1; forcedReverb = forcedReverb % House.NUM_REV_PRESETS; walker.forceReverb(forcedReverb); break; } msg = "" + walker.getRevName(); /*msg = "X:" + walker.getX() + "Y:" + walker.getY() + "r:" + walker.getRot()*10 + " " + walker.getRevName();*/ repaint(); }//keyAction() /** * Closes the sound sources. */ void destroy() { for(int i=0; i< NUM_SOURCES; i++) { sources[i].destroy(); } } boolean isPaused() { return paused; } void pause() { if (!paused) { paused = true; } repaint(); } void start() { if (paused) { paused = false; display.setCurrent(this); } repaint(); } }
package com.nokia.mid.mansion; import javax.microedition.lcdui.Graphics; import javax.microedition.media.*; import javax.microedition.media.control.*; import java.io.InputStream; import java.io.IOException; import javax.microedition.amms.control.audio3d.LocationControl; import javax.microedition.amms.*; import java.util.Random; import javax.microedition.lcdui.Canvas; /** * Sound source with graphics, too */ public class Source implements Runnable { final static int BALL_RADIUS = 5; final static int BALL_DIAM = BALL_RADIUS*2; private int x = 0, y = 0; //sound source's location private Player p; private VolumeControl volC; private LocationControl locC; private SoundSource3D ss3D; /** keeps the mover thread active until true*/ protected boolean closed = false; // needed for collision detection in random movements: House house = null; Canvas canvas = null; // for repaint after movement static Random random = new Random(); private static int yellow = 0xcccc00; public Source(int x, int y, String fileName, House house, Canvas canvas) { this.x=x; this.y=y; this.house = house; this.canvas = canvas; InputStream is = getClass().getResourceAsStream(fileName); if (is == null) System.out.println("MIDlet: error creating InputStream of" + fileName); try { System.out.println("MIDlet: Creating Player " + fileName); p = Manager.createPlayer(is, "audio/X-wav"); if (p == null) System.out.println("error creating Player of" + fileName); p.realize(); p.setLoopCount(-1); //indefinetely System.out.println("MIDlet: realized: " + fileName); } catch (IOException e) { System.out.println(e); } catch (MediaException e) { System.out.println(e); } volC=(VolumeControl)p.getControl("VolumeControl"); if (volC!=null) { System.out.println("MIDlet: got VolumeControl"); int v = volC.setLevel(100); System.out.println("MIDlet: set Volume to "+v); } try { ss3D = GlobalManager.createSoundSource3D(); if (ss3D != null) { System.out.println("MIDlet: got a new SS3D"); ss3D.addPlayer(p); System.out.println("MIDlet: Player added to a SS3D"); locC=(LocationControl)ss3D.getControl("javax.microedition.amms.control.audio3d.LocationControl"); if (locC != null) { System.out.println("MIDlet: got LocationControl from a SS3D"); locC.setCartesian(x, 0, y); } else { System.out.println("LocationControl not available for " + fileName); ss3D.removePlayer(p); } p.prefetch(); } } catch (MediaException e) { System.out.println(e); } } /** * Deinits players */ protected void destroy() { closed = true; if(p != null) { p.close(); } } public final void start() { try { p.start(); } catch (MediaException e) { System.out.println(e); } catch (IllegalStateException ise) { //synchronization problems with close. } } public final void stop() { if(p.getState() == Player.STARTED) { Thread t = new Thread () { public void run() { try { p.stop(); } catch (MediaException e) { System.out.println(e); } catch (IllegalStateException ise) { //synchronization problems with close. } } }; t.start(); } } public final void setLocation(int x, int y) { this.x=x; this.y=y; if (locC != null) { locC.setCartesian(x, 0, y); } } public final int getX() { return x; } final int getY() { //package visibility for FastSource return y; } public void run() { int tempX = 0; int tempY = 0; while(!closed) { tempX = x + (random.nextInt() & 256) -128; tempY = y + (random.nextInt() & 256) -128; if (house.isInside(tempX, tempY)) { setLocation(tempX, tempY); canvas.repaint(); } try { Thread.sleep(250); } catch (InterruptedException e) {} } } /** * Draws the sound source to given Graphics. * @param dx x translation in millipixels * @param dy y translation in millipixels * @param rot rotation in deci-angles */ public void draw(Graphics g, int dx, int dy, int rot) { int newX = Trig.transXS(x-dx, y-dy, rot) ;//+g.getClipWidth()/2; int newY = Trig.transYS(x-dx, y-dy, rot) ;//+g.getClipHeight()/2; drawFace(g, newX, newY); } /** * Draws a face to given graphics */ void drawFace(Graphics g, int x, int y) { // Draw ball: g.setColor(yellow); g.fillArc(x-BALL_RADIUS, y-BALL_RADIUS, BALL_DIAM, BALL_DIAM, 0, 360); // Draw eyes: g.setColor(0); g.fillArc(x-2, y-2, 2, 2, 0, 360); g.fillArc(x+2, y-2, 2, 2, 0, 360); // mouth: g.drawArc(x-BALL_RADIUS+1, y-BALL_RADIUS+1, BALL_DIAM-3, BALL_DIAM-3, 200, 140); } }
package com.nokia.mid.mansion; /** * Trigonometrical function estimates. * */ public class Trig{ private final static int SIN[]= { 0, 174, 342, 500, 643, 766, 866, 940, 985, 1000, 985, 940, 866, 766, 643, 500, 342, 174, 0, -174, -342, -500, -643, -766, -866, -940, -985, -1000, -985, -940, -866, -766, -643, -500, -342, -174 }; private final static int COS[]= { 1000, 985, 940, 866, 766, 643, 500, 342, 174, 0, -174, -342, -500, -643, -766, -866, -940, -985, -1000, -985, -940, -866, -766, -643, -500, -342, -174, 0, 174, 342, 500, 643, 766, 866, 940, 985 }; /** * Sine estimator * * @param alpha in deci-degrees, between 0 and 35 * @return in thousandths */ private static int sin(int alpha) { return SIN[alpha]; } /** * Cosine estimator * * @param alpha in deci-degrees, between 0 and 35 * @return in thousandths */ private static int cos(int alpha) { return COS[alpha]; } /** * Calculates the rotation * * @param theta in deci-degrees, between 0 and 35 * @return new x */ public static int transX(int x, int y, int theta){ return (x * cos(theta) - y * sin(theta))/1000; } /** * Calculates the rotation * * @param theta in deci-degrees, between 0 and 35 * @return new y */ public static int transY(int x, int y, int theta){ // minus because of y-coordinate of the screen upside down(?) return -(x * sin(theta) + y * cos(theta))/1000; } /** * Calculates the rotation for the screen coordinates directly * * @param theta in deci-degrees, between 0 and 35 * @return new x */ public static int transXS(int x, int y, int theta){ return -(x * cos(theta) - y * sin(theta))/50000; } /** * Calculates the rotation for the screen coordinates directly * * @param theta in deci-degrees, between 0 and 35 * @return new y */ public static int transYS(int x, int y, int theta){ // minus because of y-coordinate of the screen upside down(?) return (x * sin(theta) + y * cos(theta))/50000; } }
package com.nokia.mid.mansion; import javax.microedition.media.*; import javax.microedition.amms.Spectator; import javax.microedition.amms.GlobalManager; import javax.microedition.amms.control.audio3d.OrientationControl; import javax.microedition.amms.control.audio3d.LocationControl; import javax.microedition.amms.control.audioeffect.ReverbControl; /** * Spectator functionality. * */ public class Walker { private static final int STEP = 200; //step size in mm private Spectator spectator; private LocationControl specLocC; // locC of spectator private OrientationControl specOriC; // OriC of spectator private ReverbControl reverb; private String revName = "none"; private int specX = 1000, specY = 6000; // spectator location private int specRot = 0; // spectator rotation // for checking the needs for a reverb settings updates: private int prevRoom = -999; public Walker (){ initSpectator(); } /** * Init spectator and get controls for it. */ private void initSpectator() { try { spectator = GlobalManager.getSpectator(); } catch (MediaException me) { System.out.println(me); } if(spectator != null) { if ((specLocC = (LocationControl)spectator.getControl("javax.microedition.amms.control.audio3d.LocationControl")) != null) { specLocC.setCartesian(specX, 0, specY); } else { System.out.println("Spectator couldn't get LocationControl."); } if ((specOriC = (OrientationControl) spectator.getControl("javax.microedition.amms.control.audio3d.OrientationControl")) != null) { specOriC.setOrientation(specRot*10, 0, 0); } else { System.out.println("Spectator couldn't get OrientationControl."); } try { if ((reverb = (ReverbControl)GlobalManager.getControl("javax.microedition.amms.control.audioeffect.ReverbControl")) != null) { try { reverb.setPreset("mountains"); } catch (IllegalArgumentException iae) { System.out.println(iae); } reverb.setEnabled(true); if(!(reverb.isEnabled())) System.out.println("MIDlet: enabling reverb failed."); } else { System.out.println("Spectator couldn't get Reverb."); } } catch (IllegalArgumentException iae) { System.out.println(iae); } } }//initSpectator() public void rotateLeft() { // rotate spectator left specRot = specRot - 1; if (specRot == -1) specRot = 35; updateSpecRot(specRot); } public void rotateRight() { // rotate spectator right specRot = (specRot + 1)%36; updateSpecRot(specRot); } public void moveForward(House house) { int newSpecX = specX + Trig.transX(0, STEP, specRot); int newSpecY = specY + Trig.transY(0, STEP, specRot); updateSpecLoc(newSpecX, newSpecY, house); } public void moveBackward(House house) { int newSpecX = specX + Trig.transX(0, -STEP, specRot); int newSpecY = specY + Trig.transY(0, -STEP, specRot); updateSpecLoc(newSpecX, newSpecY, house); } public int getX() { return specX; } public int getY() { return specY; } public int getRot() { return specRot; } private void updateSpecRot(int newRot) { newRot += 18; if(specOriC != null) specOriC.setOrientation(newRot*10, 0, 0); } /** * Updates location if collision to the wall doesn't prevent. * Updates possible new reverb settings as well, if the spy moves to a new room. */ private void updateSpecLoc(int newX, int newY, House house) { if (house.isInside(newX, newY)) { specX = newX; specY = newY; if(specLocC != null) specLocC.setCartesian(newX, 0, newY); // update reverb settings if necessary int currentRoom = house.inWhichRoom(specX); if (currentRoom != prevRoom) { prevRoom = currentRoom; if(reverb != null) { reverb.setPreset(House.presetName(currentRoom)); revName = House.presetName(currentRoom); System.out.println("New reverb: " + House.presetName(currentRoom)); } } } } /** * Forces the Reverb of Spectator independently of the room. */ protected void forceReverb(int index) { revName = House.presetNameByIndex(index); reverb.setPreset(revName); System.out.println("Manually forced reverb: " + House.presetNameByIndex(index)); } /** * Gets the current reverb preset's name. */ protected String getRevName() { return revName; } }