Implementing audio and effects

Implementing audio

The Player class is used for playing samples. A maximum of eight players are allowed at the same time, and only some of the players can play simultaneously because of memory restrictions. Due to these limitations, the MIDlet must monitor the number of players created and how many of them are playing.

In the Explonoid MIDlet the latency is not a major issue. Of course, it is convenient that a sample plays fairly fast, but the MIDlet does not require the audio handling to be implemented in exactly the same way to reach sufficient results. Basically, if the sample must play as fast as possible, the MIDlet must create a player and prefetch the sample in advance. However, as the Explonoid MIDlet has more than eight samples to be played, you cannot reserve one player per sample or predict which sample is played next. Consequently, the simple solution is to create a new player every time the MIDlet needs to play a sample. If there are too many players already playing, the one that has been playing the longest is stopped and deallocated. Notice that the audio is played in a separate thread for optimisation. Below is the thread loop that plays the contents of a synchronized playlist every 100 ms. The sample is wrapped in a PlayItem class, which contains the player, sample filename and a flag for looping the sample. If the sample needs to be looped, it is flagged and given a PlayerListener for END_OF_MEDIA events. The PlayerListener is implemented by the AudioManager.

The audio playback is a heavy operation, especially when the samples are not prefetched. To prevent blocking the UI thread and keep steady framerate whenever sounds are played the audio playback was isolated into its own thread. A message queue is used to notify the audio thread whenever a sample should be played. The audio thread dequeues the messages every 100 milliseconds and starts playing the requested sounds.

    public void playSample(final String filename) {
        PlayItem playItem = new PlayItem(filename, false);
        synchronized (playBuffer) {
            playBuffer.addElement(playItem);
            while (playBuffer.size() >= MAX_PLAYERS) {
                playBuffer.removeElementAt(0);
            }
        }
    }
public void run() {
        while (true) {
            if (audioEnabled) {
                Enumeration e = null;
                PlayItem[] queue;
                 // Copy the queued items into array
                synchronized (playBuffer) {
                    queue = new PlayItem[playBuffer.size()];
                    e = playBuffer.elements();
                    for (int i = 0; i < playBuffer.size(); i++) {
                        queue[i] = (PlayItem) e.nextElement();
                    }
                    playBuffer.removeAllElements();
                }
                // Play queued items
                for (int i = 0; i < queue.length; i++) {
                    PlayItem playItem = queue[i];
                    if (players[index] != null) {                        
                        stop(players[index].getPlayer());
                    }
                    players[index] = playItem;
                    index++;
                    if (index >= MAX_PLAYERS) {
                        index = 0;
                    }

                    try {
                        InputStream stream = AudioManager.class.getResourceAsStream(playItem.getFilename());
                        playItem.setPlayer(Manager.createPlayer(stream, "audio/mp3"));
                        playItem.getPlayer().start();
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                }
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
        }
    }

Implementing effects

To make the Explonoid MIDlet look better than just an Arkanoid clone, it contains some additional effects, such as vibrations and the following:

  • Spark bursts

    Every time the ball hits a brick, the brick explodes with a spark burst. The spark burst consists of Spark sprites that hold a fade-out animation. When shooting sparks, each spark gets a random velocity and direction and the animation is started from a random place.

  • Shaking

    Shaking is a simple Timer -based effect, which shakes the game area with a given magnitude and subsides as the time goes on.

  • Shock waves

    Shock waves occur when the ball slips past the plate. They are basically just ellipses that grow every time they are painted and disappear after their maximum size has been reached.

  • Electrical arcs

    Electrical arcs occur in the menu view when the user touches the screen. The arcs are randomly generated from the pointer coordinates outward.

Figure: Explonoid effects

When handling effects, you must consider memory as well. The garbage collector cannot always keep up if the MIDlet generates new objects too fast. To prevent problems, for example, with spark bursts, limit the maximum number of sparks and reuse the same spark instances over and over again.