# JME 3 Tutorial (11) - Hello Audio

Previous: Hello Terrain, Next: Hello Effects

This tutorial explains how to add 3D sound to a game, and how to trigger when sounds are to be played. We will use an Audio Listener, an Audio Renderer, two Audio Nodes, and an Action Listener.

## Sample Code

    package jme3test.helloworld;

    import com.jme3.app.SimpleApplication;
    import com.jme3.audio.AudioNode;
    import com.jme3.input.controls.ActionListener;
    import com.jme3.input.controls.MouseButtonTrigger;
    import com.jme3.material.Material;
    import com.jme3.math.ColorRGBA;
    import com.jme3.math.Vector3f;
    import com.jme3.scene.Geometry;
    import com.jme3.scene.shape.Box;

    /** Sample 11 - playing 3D audio. */
    public class HelloAudio extends SimpleApplication {

      private AudioNode audio_gun;
      private AudioNode audio_nature;
      private Geometry player;

      public static void main(String[] args) {
        HelloAudio app = new HelloAudio();
        app.start();
      }

      @Override
      public void simpleInitApp() {
        flyCam.setMoveSpeed(40);

        /** just a blue box floating in space */
        Box(Vector3f.ZERO, 1, 1, 1);
        player = new Geometry("Player", box1);
        Material mat1 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        mat1.setColor("Color", ColorRGBA.Blue);
        player.setMaterial(mat1);
        rootNode.attachChild(player);

        /** custom init methods, see below */
        initKeys();
        initAudio();
      }

      /** We create two audio nodes. */
      private void initAudio() {
        /* gun shot sound is to be triggered by a mouse click. */
        audio_gun = new AudioNode(assetManager, "Sound/Effects/Gun.wav", false);
        audio_gun.setLooping(false);
        // Volume goes from 0.1f to 1.0f. At 0 it's muted.
        audio_gun.setVolume(0.2f);

        /* nature sound - keeps playing in a loop. */
        audio_nature = new AudioNode(assetManager, "Sound/Environment/Nature.ogg", false);
        audio_nature.setLooping(true);
        audio_nature.setPositional(true);
        audio_nature.setLocalTranslation(Vector3f.ZERO.clone());
        // Volume goes from 0.1f to 1.0f. At 0 it's muted.
        audio_nature.setVolume(0.3f);
        audioRenderer.playSource(audio_nature); // play continuously!
      }

      /** Declaring the "Shoot" action, and
       *  mapping it to a trigger (mouse click). */
      private void initKeys() {
        inputManager.addMapping("Shoot", new MouseButtonTrigger(0));
        inputManager.addListener(actionListener, "Shoot");
      }

      /** Defining the "Shoot" action: Play a gun sound. */
      private ActionListener() {
        @Override
        public void onAction(String name, boolean keyPressed, float tpf) {
          if (name.equals("Shoot") && !keyPressed) {
            audioRenderer.playSource(audio_gun); // play once!
          }
        }
      };

      /** Move the listener with the a camera - for 3D audio. */
      @Override
      public void simpleUpdate(float tpf) {
        listener.setLocation(cam.getLocation());
        listener.setRotation(cam.getRotation());
      }

    }

When you run the sample, you should see a blue cube, and hear nature ambient sounds. When you click you hear a loud shot.

## Understanding the Code Sample

In the game's `initSampleApp()` method, we create a simple blue cube geometry called `player` and attach it to the scene – it's just sample content so you see something when running the audio sample.

From `initSampleApp()`, we initialize the game by calling the two custom methods `initKeys()` and `initAudio()`. You could call the lines of code in these two methods directly from `initSampleApp()`; we simply moved them into extra methods to keep the code more readable.

Let's look at `initKeys()`: As we learned in previous tutorials, we use jme's `inputManager` to respond to user input. We add a mapping for a left mouse button click, and we name this new action `Shoot`.

    /** Declaring the "Shoot" action and mapping to a trigger. */
      private void initKeys() {
        // click to shoot
        inputManager.addMapping("Shoot", new MouseButtonTrigger(0));
        inputManager.addListener(actionListener, "Shoot");
      }

Setting up the ActionListener should also be familiar from previous tutorials. We declare that, when the trigger (the mouse button) is pressed and released, we want to play a gun sound.

      /** Defining the "Shoot" action: play a sound. */
      private ActionListener() {
        @Override
        public void onAction(String name, boolean keyPressed, float tpf) {
          if (name.equals("Shoot") && !keyPressed) {
            audioRenderer.playSource(audio_gun);
          }
        }
      };

Next we have a closer look at `initAudio()` to learn how to use the `AudioRenderer` and `AudioNode`s.

## AudioNodes

Using audio is quite simple. Save your audio files into your `assets/Sound` directory. jME3 supports both Ogg Vorbis (.ogg) and Wave (.wav) files.

For each sound you create an audio node. A sound node can be used like any node in the jME scene graph. We create one node for a gunshot sound, and one for a nature sound.

      private AudioNode audio_gun;
      private AudioNode audio_nature;

Look at `initAudio()`: Here we initialize the sound objects and set some parameters.

        audio_gun = new AudioNode(assetManager, "Sound/Effects/Gun.wav", false);
        audio_nature = new AudioNode(assetManager, "Sound/Environment/Nature.ogg", true);

These two lines create new sound nodes from the given audio files in the assetManager. The Boolean parameter specifies whether you want to stream the sound or not. The nature sound is quite long, so we set this to true because we want to stream it.

In the next lines we specify that we want the gunshot sound to play only once (do not loop) and we specify its volume on a scale of 0.1 to 1.0. At 0, it is muted.

        audio_gun.setLooping(false);
        audio_gun.setVolume(0.4f);

The nature sound is different: We want it to loop continuously as background sound. We also make it a positional sound and give the node an explicit translation (Vector3f.ZERO stands for `0.0f,0.0f,0.0f`). Since jME supports 3D audio, you will be able to hear this sound coming from a particular direction.

        audio_nature.setLooping(true);
        audio_nature.setPositional(true);
        audio_nature.setLocalTranslation(new Vector3f.ZERO.clone());
        audio_nature.setVolume(0.3f);
      }

## Triggering Audio

The two sounds are used differently:

- The gunshot is situational. We want to play it only once, right when it is triggered.
  - This is why we made it `setLooping(false)`
- The nature sound is a background noise. We want it to start playing and loop on as long as the game runs.
  - This is why we made it `setLooping(true)`

So every sound knows whether it loops or not. The actual `play` command is the same for both files:

        audioRenderer.playSource(audio_nature);
    ...
        audioRenderer.playSource(audio_gun);

The only difference is where they are called:

- We start playing the background nature sound right after we have created it, in the initAudio() method.
- The gunshot sound, however, is triggered situationally, as part of the `Shoot` input action that we defined.

## 3D Audio Listener

To keep up the 3D audio effect, jME needs to know the position of the sound source, and the position of the audio listener. The `listener` is a built-in object in a SimpleApplication and it is not related to an ActonListener.

I order to make the most of 3D audio, we use the `simpleUpdate()` method to move the listener with the camera.

       public void simpleUpdate(float tpf) {
        listener.setLocation(cam.getLocation());
        listener.setRotation(cam.getRotation());
      }

## Global or Positional?

In this example, we defined the nature sound as coming from a certain position, but not the gunshot sound. This means the gunshot can be heard everywhere with the same volume.

- In a game with moving enemies you maight want to make the audio_gun node positional, and move it to the location of the shooting enemy before playing it – this way a player with stereo speakers could hear from which direction the gunshot is coming.
- Similarly, you may have game levels where you want one background sound to play globally, in this case, you would not make the audio_nature a positional sound.

In short, you must choose in every situation whether you want a sound to be global or positional.

## Conclusion

You now know how to add two types of sound to your game: global sounds and positional sounds. You can play sounds in two ways: Either as loop or just once. You also learned to use sound files that are in either .ogg or .wav format.

Tip: jME's Audio implementation also supports more advanced effects such as reverberation and the Doppler effect. Find out more about these from the sample code included in the jme3test directory.

Want some fire and explosions to go with your sounds? Read on to learn more about effects.

sound, documentation, beginner,, beginner, intro

view online version