Engine sounds with dynamic audio

Some effects have a dynamic nature and cannot be achieved by simply playing a plain audio file. Imagine a car engine; the sound it makes depends on many factors such as the type of engine and the revolutions per minute it is running at. Since a user can accelerate or hit the brake at any point in time, developers need to play audio in an adaptive manner.

Many games also have a dynamic soundtrack. Shadow of the Colossus (PlayStation 2, 2005) comes to mind when thinking of music that adapts to gameplay. In this game, the player has to defeat a series of giant beasts by climbing on them and striking weak spots. Music would smoothly change to accompany the mood of the moment. We are not talking about changing music tracks when the player reaches a point via script, but blending tracks in and out depending on things such as player performance.

Throughout this recipe, we will show you how to reproduce a car sound as it moves at different speeds. Obviously, we will do so with a rather simplistic approach. Do not expect modern racing simulator quality!

Getting ready

As usual, make sure the sample projects are in your Eclipse workspace. We will use a couple of sound effect files:

  • [cookbook]/samples/android/assets/data/sfx/car-engine.wav
  • [cookbook]/samples/android/assets/data/sfx/car-idle.wav

How to do it…

The user will be able to use the Space key or the touchscreen to hit the gas pedal of an imaginary car. When the car is not being accelerated, friction will reduce its speed. Under the hood, we'll make the engine sound effect adapt to the current state of the car.

The code for this recipe is inside the CarEngineSample class. Follow these steps to learn how to dynamically manipulate audio:

  1. First, define a set of constants such as the maximum speed in km/h, acceleration, friction, and a threshold from which we no longer consider the car to be stationary to configure the car's behavior:
    private final static float MAX_SPEED = 200.0f;
    private final static float ACCELERATION = 25.0f;
    private final static float FRICTION = 15.0f;
    private final static float IDLE_THRESHOLD = 0.1f;
  2. Then, add a few members such as the current car speed, a sound effect for when the car is moving, and another for when it is idling. Finally, we have the soundId of the effect instance that is playing at the moment so as to be able to access it and modify it on the fly:
    private float speed;
    private Sound engine;
    private Sound idle;
    private long soundId;
  3. Besides instantiating our viewport, camera, font, and batch in the create() method, we also load our .wav files and start playing the idle engine sounds in a loop:
    public void create() {
      …
      idle = Gdx.audio.newSound(Gdx.files.internal("data/sfx/car-idle.wav"));
      engine = Gdx.audio.newSound(Gdx.files.internal("data/sfx/car-engine.wav"));
      soundId = idle.play();
      idle.setLooping(soundId, true);
    }
  4. Always make sure to dispose of your resources if you want to avoid memory leaks:
    public void dispose() {
      …
      engine.dispose();
      idle.dispose();
    }
  5. The render() method is quite simple; we only draw the current car speed and control instructions. The interesting work is done by updateEngine():
    public void render() {
      …
      updateEngine(Gdx.graphics.getDeltaTime());
    
      camera.update();
      batch.setProjectionMatrix(camera.combined);
      batch.begin();
      font.draw(batch, "Speed: " + speed + "km/h", 20.0f, 200.0f);
      font.draw(batch, "Press SPACE or touch to accelerate", 20.0f, 150.0f);
      batch.end();
    }
  6. Let's take a look at updateEngine(). It first figures out whether the car should accelerate or decelerate, depending on whether the user is pressing space or touching the screen. It then increases the current car speed by acceleration times delta (time since the previous frame), while making sure it stays within the bounds.
  7. The next step is to detect whether the car just started moving or has stopped, in which case we need to switch the active sound and set it to loop.
  8. Finally, if the car is in motion, we need to update the engine effect so that it matches its current speed. In order to do so, we will play around with the sound's pitch. The faster the car runs, the higher the pitch will be. We modify the pitch using the setPitch() method, passing in the sound ID and a value between 0.5f and 1.0f:
    private void updateEngine(float delta) {
      boolean wasIdle = speed < IDLE_THRESHOLD;
    
      float acceleration = -FRICTION;
    
      if (Gdx.input.isKeyPressed(Keys.SPACE) || Gdx.input.isButtonPressed(Buttons.LEFT)) {
        acceleration = ACCELERATION;
      }
    
      speed = MathUtils.clamp(speed + acceleration * delta, 0.0f, MAX_SPEED);
    
      boolean isIdle =  speed < IDLE_THRESHOLD;
    
      if (wasIdle && !isIdle) {
        idle.stop();
        soundId = engine.play();
        engine.setLooping(soundId, true);
      }
      else if (!wasIdle && isIdle) {
        engine.stop();
        soundId = idle.play();
        idle.setLooping(soundId, true);
      }
    
      if (!isIdle) {
        float pitch = 0.5f + speed / MAX_SPEED * 0.5f;
        engine.setPitch(soundId, pitch);
      }
    }

It is now time to run the sample, get our imaginary car to move at different speeds, and notice how the sound changes to reflect its state.

Note

Currently, the setPitch() method will have no effect in the HTML backend due to a browser's sound limitations. Take this into account when developing your project.

How it works…

Up until this point, we did not talk about sound for what it really is: just waves. Technically speaking, sound is a vibration that propagates as a mechanical wave of pressure and displacement through a medium such as air or water. We do not hear sounds in outer space because vibrations cannot propagate in a vacuum!

Relax, do not start sweating; this is not going to be a physics lecture. However, we do need to mention some basic concepts as they are relevant to this recipe. Take a look at the following figure showing a sound wave over time:

How it works…

We can highlight the following features:

  • Amplitude: This is the distance between the crest and the still position
  • Wavelength: This is the distance between two crests
  • Period: This is the duration of one wave cycle
  • Frequency: This is the number of wave cycles per unit of time, that is, the inverse of period

Let's focus on the frequency, which is closely related to the pitch, the value we tweaked in our car engine simulation toy.

Pitch is a perceptual property that allows us to distinguish between different sound frequencies. It is related to frequency, but strictly speaking, it is not a physical property. As you might be used to hearing, pitches are compared as higher or lower.

There's more…

Here are a couple of extra things you can do with dynamic sound.

Controlling direction through panning

Another interesting Sound instance property we can tweak is the pan, which determines the location the sound is coming from. A pan of -1.0f means the sound is coming from the left, while a pan of 1.0f indicates it is placed on the right. The following diagram shows the pan an explosion can have in relation to the listener. Obviously, multispeaker systems are needed to appreciate the effect as they use two independent audio channels.

The following diagram illustrates panning; the orange marker indicates the listener position and direction.

Controlling direction through panning

We can set the pan of a sound instance with the following method:

void setPan(long soundId, float pan, float volume)

Music transitions

Something you can look towards to expand your dynamic audio knowledge is mixing music tracks. Some sort of MusicManager will take requests to change from one track to another, and it will handle transitions and timing. The only thing it really needs to do is smoothly decrease the volume of the previous track while increasing that of the incoming one, as shown in the following diagram:

Music transitions

See also

  • You are now very familiar with Libgdx audio, which is great! In The 2D space sound system recipe, you will learn how to use audio to improve immersion by making it space aware.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.101.109