Implementation

For information about the design and functionality of the MIDlet, see section Design.

For information about the key aspects of implementing the MIDlet, see:

Sound sources

The GlobalManager handles the creation of SoundSource3Ds. LocationControl is an interface for manipulating the virtual location of an object (in this case a SoundSource3D) in the virtual acoustic space. SoundSource3D represents a sound source in a virtual acoustic space.

A Source object is a container for a 3D sound source:

    public Source(int x, int y, String fileName, House house, Canvas canvas, int color) {
        this.x = x;
        this.y = y;
        this.house = house;
        this.canvas = canvas;
        this.color = color;

        InputStream is = getClass().getResourceAsStream(fileName);
        if (is == null) {
            throw new IllegalArgumentException("error creating InputStream of" + fileName);
        }
        try {
            player = Manager.createPlayer(is, "audio/X-wav");
            if (player == null) {
                throw new IllegalArgumentException("error creating Player of" + fileName);
            }
            player.realize();
            player.setLoopCount(-1); //indefinetely
        } catch (IOException e) {
            System.out.println(e);
        } catch (MediaException e) {
            System.out.println(e);
        }
        volC = (VolumeControl) player.getControl("VolumeControl");
        if (volC != null) {
            volC.setLevel(100);
        }

        try {
            ss3D = GlobalManager.createSoundSource3D();
            if (ss3D != null) {
                ss3D.addPlayer(player);
                locC = (LocationControl) ss3D.getControl("javax.microedition.amms.control.audio3d.LocationControl");
                if (locC != null) {
                    locC.setCartesian(x, 0, y);
                } else {
                    //LocationControl not available
                    ss3D.removePlayer(player);
                }
                player.prefetch();
            }
        } catch (MediaException e) {
            System.out.println(e);
        }
    }

The createSoundSource3D method in the GlobalManager creates a SoundSource3D. The addPlayer method adds a Player to the module. The setCartesian method moves the object to the new location, and the removePlayer method removes a Player or all channels of a Player from the module.

The following are methods for starting and stopping the player:

    public final void start() {
        try {
            player.start();
        } catch (MediaException e) {
            System.out.println(e);
        } catch (IllegalStateException ise) {
            //synchronization problems with close.
        }
    }

    public final void stop() {
        if (player.getState() == Player.STARTED) {
            Thread t = new Thread() {

                public void run() {
                    try {
                        player.stop();
                    } catch (MediaException e) {
                        System.out.println(e);
                    } catch (IllegalStateException ise) {
                        //synchronization problems with close.
                    }
                }
            };
            t.start();
        }
    }

The following method closes the player:

    protected void destroy() {
        closed = true;
        if (player != null) {
            player.close();
        }
    }

The following method sets the location:

    public final void setLocation(int x, int y) {
        this.x = x;
        this.y = y;
        if (locC != null) {
            locC.setCartesian(x, 0, y);
        }
    }

The Source class implements the Runnable interface to update the position randomly:

    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) {
            }
        }
    }

Spectator

The Spectator class represents the listener in the virtual acoustic space. OrientationControl is an interface for manipulating the virtual orientation of an object in the virtual acoustic space. ReverbControl is an interface for manipulating the settings of an audio effect called reverb.

A Walker object is a container for the spectator:

    public Walker(House house) {
        try {            spectator = GlobalManager.getSpectator();
        } catch (MediaException me) {
            System.out.println(iae);
            }
        }

        this.house = house;
        changeReverbToTheCurrentRoomPreset();
    }
        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);
            }
        }
    }

The getSpectator method retrieves the Spectator. The getControl method obtains the object that implements the specified Control interface. The setOrientation method turns the object to the new orientation. The setPreset method sets the effect according to the given preset. The setEnabled method enables or disables a given effect, and the isEnabled method returns true if the effect is enabled and false otherwise.

The following method updates the Walker's orientation:

    private void updateSpecRot(int newRot) {
        newRot += 18;
        if (specOriC != null) {
            specOriC.setOrientation(newRot * 10, 0, 0);
        }
    }

The following method updates the Walker's location, provided the space is free and not a wall:

    /**
     * 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) {
        if (house.isInside(newX, newY)) {
            specX = newX;
            specY = newY;
            if (specLocC != null) {
                specLocC.setCartesian(newX, 0, newY);
            }
            changeReverbToTheCurrentRoomPreset();
        }
    }

    /**
     * Forces the Reverb of Spectator independently of the room.
     */
    protected void changeReverbTo(int index) {
        revIndex = index;
        if (reverb != null) {
            reverb.setPreset(getCurrentReverbName());
        }
    }

    /**
     * Forces the Reverb of Spectator to the current room preset.
     */
    private void changeReverbToTheCurrentRoomPreset() {
        // update reverb settings if necessary
        int currentRoom = house.inWhichRoom(specX);
        if (currentRoom != prevRoom) {
            prevRoom = currentRoom;
            changeReverbTo(House.presetIndexByRoom(currentRoom));
        }
    }