Implementing audio and effects

Implementing audio

The Player class is used for playing samples. On Series 40 devices, 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.

While the Drumkit MIDlet demonstrates how to manage audio in a low-latency application, in the Explonoid MIDlet the latency is a minor 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.

To implement audio:

  1. Define the maximum number of players, and use the playSample method to play the sample.

    Note that the addPlayerListener method call is used to register a listener for the END_OF_MEDIA events to enable effective looping.

        public Player playSample(String filename) {
            Player player = null;
            if (audioEnabled) {
                InputStream stream = AudioManager.class.getResourceAsStream(filename);
                try {
                    limitPlaybacks();
                    player = Manager.createPlayer(stream, "audio/mp3");
                    playing.addElement(player);
                    player.realize();
                    player.prefetch();
                    player.addPlayerListener(this);
                    player.start();
                }
                catch (Exception e) {
                }
            }
            return player;
        }
  2. Use the limitPlaybacks method to track the number of concurrent players, and to stop the longest playing player when the maximum number of players is reached.

        /**
         * Shuts down the player, that has been playing the longest
         */
        private synchronized static void limitPlaybacks() {
            try {
                while (playing.size() > MAX_PLAYERS) {
                    Player player = (Player) playing.firstElement();
                    playing.removeElementAt(0);
                    stop(player);
                }
            }
            catch (Exception e) {
            }
        }
  3. To loop a sample, you can use the player.setLoopCount(-1) method. However, it does not always result in an indefinite loop. Listening to the END_OF_MEDIA event and then starting the player again manually seems to be a more reliable way to implement looping.

        public Player loopSample(String filename) {
            Player p = playSample(filename);
            looping.addElement(p);
            return p;
        }
    
        // ...
    
        public void playerUpdate(Player player, String event, Object eventData) {
            if (event.equals(PlayerListener.END_OF_MEDIA)) {
                if (looping.contains(player)) {
                    try {
                        player.setMediaTime(0);
                        player.start();
                    }
                    catch (Exception e) {
                    }
                }
                else {
                    stopPlayer(player);
                }
            }
        }

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.