Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Shake, Rattle and Roll: Dolby Sound on Android Devices

5 Nov 2013 1  
Shake, Rattle and Roll: Dolby Sound on Android Devices

This article is in the Product Showcase section for our sponsors at CodeProject. These articles are intended to provide you with information on products and services that we consider useful and of value to developers.

Introduction

Movie preferences are, of course, a highly-personal discussion, but my personal favorite for 2013 (so far) is Pacific Rim—it has monsters, mecha, a plot so predictable you can guess the next words out of the actors’ mouths, and, perhaps most important to me, an amazing soundtrack by Ramin Djawadi. I like to listen to movie soundtracks when writing code (and articles, and yes, I’m listening to them right now). In fact, I can forgive a lot of things about a movie if the soundtrack is amazing, a habit I probably picked up as a small child when first introduced to epic soundtracks (like most geeks) through Star Wars, and its amazing John Williams score.

A great soundtrack frequently makes the difference between a "meh" and a "WOW" in movies, and with the explosion of mobile devices as gaming and entertainment platforms, the same is quickly becoming true of mobile device applications. This is much of the reason why Dolby, the same people who brought you explosions that rattle the movie theater popcorn in its greasy bucket, are now bringing their expertise in sound to the Android universe. Not every Android application is going to care about its audio effects and playback, and not every Android device will be equipped with Dolby solutions (where "solutions is a handy euphemism for "hardware plus software"), but those that do will want to extract the most ...if you’ll pardon the pun ... "bang for the buck" out of that device’s audio capabilities. Yes, game developers, I'm looking squarely at you. And Dolby now makes available an SDK for audio playback and management that will ... if you’ll pardon the pun again ... blow your ears off.

Seriously, we’re talking about this?

Sound quality is often one of those things that doesn’t seem really high on the feature list, particularly for mobile games and applications, because, let’s face it, the speakers are tiny and not really well-powered on a lot of devices. Noise-canceling over-the-ear headphones can certainly help with that, but ... to a lot of developers, putting a lot of time into audio doesn’t really seem like a huge payoff.

To put that little myth to bed, it helps to have an easy point of comparison. First, let’s create a simple Android MediaPlayer application that uses the stock MediaPlayer class (found in the Android package android.media) to play an .m4a audio file. Then we’ll use the Dolby example application (which ships with their SDK) to play the same file, just to keep it as an apples-to-apples comparison. Note that the Dolby SDK example won’t run on an Android device that doesn’t support the Dolby solutions, which as of this writing is the Amazon Kindle Fire HD devices, though many more are in the pipeline. In order to run the Dolby sample, and hear the difference, you’ll need one of those devices. (Mine is a Kindle Fire HD, which is nice because now I can read a book with a nifty soundtrack playing in the background. Nothing like catching up on Java coding techniques and hearing epic music in the background while you do.)

android.media.MediaPlayer

First, the stock Android MediaPlayer example: it’s a simple application, with a single Activity that contains a button that fills the display that, when pressed, kicks off the MediaPlayer to play the "audio_dolby_living_room.m4a" audio file, bundled as a raw resource:

public class MediaPlayerSampleActivity
  extends Activity
  implements MediaPlayer.OnCompletionListener
{
  Button btnPlay;
  MediaPlayer mPlayer;
 
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    btnPlay = new Button(this);
    btnPlay.setText("Play");
    btnPlay.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        if(mPlayer == null) {
          try {
            mPlayer = MediaPlayer.create(
              MediaPlayerSampleActivity.this,
              R.raw.audio_dolby_living_room);
            mPlayer.start();
          }
          catch (Exception e) {
            e.printStackTrace();
          }
        }
        else {
          mPlayer.stop();
          mPlayer.release();
          mPlayer = null;
        }
      }
    });
    setContentView(mPlay);
  }
 
  @Override
  public void onDestroy() {
    super.onDestroy();
    if(mPlayer != null) {
      mPlayer.release();
    }
  }
 
  //OnCompletionListener Methods
  @Override
  public void onCompletion(MediaPlayer mp) {
    mPlayer.release();
    mPlayer = null;
  }
}

It’s a pretty straightforward app, so much so that it doesn’t even bother with a resource-based layout to put the button in the center of the screen.

Compile it with the debug key and install it to your device ("ant installd", assuming the device is plugged in over USB, if you’re a command-line junkie like me), and fire up the app. The audio file goes through a series of audio gyrations, almost as if its creators wanted to exercise the speakers a little. It takes a minute or two to play, but tap the button again to stop the playback when you’re ready to move on.

com.dolby.dap.DolbyAudioProcessing

The Dolby application is a little bit trickier: we’re still using the stock MediaPlayer to play the clip, but before the MediaPlayer kicks off, we’re going to quiz the underlying device hardware to see if it has Dolby hardware on it, and if so, activate the hardware.

Before anything else, though, the Dolby SDK has to be downloaded; it’s available at http://developer.dolby.com, and once downloaded, there’s a set of Javadocs, a sample application, and a JAR file.

Practically, using the Dolby processing support is relatively easy: beyond the basic MediaPlayer usage, the application developer needs to obtain an instance of the DolbyAudioProcessing class, use it to enable processing (and, by the way, make sure to abide by user actions by stopping or adjusting your audio playback according to the standard Android behavior described in the Android Media Guide), optionally choose an audio profile for playback, and clean up the processing instance when the application shuts down.

Programmatically, it collectively looks like this:

// other imports, as before
import com.dolby.dap.*;
 
public class DolbyMediaSampleActivity
  extends Activity
  implements MediaPlayer.OnCompletionListener,
    OnDolbyAudioProcessingEventListener
{
  Button btnPlay;
  MediaPlayer mPlayer;
  DolbyAudioProcessing mDolbyAudioProcessing;
 
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    btnPlay = new Button(this);
    btnPlay.setText("Play");
    btnPlay.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        if(mPlayer == null) {
          try {
            mPlayer = MediaPlayer.create(
              DolbyMediaSampleActivity.this,
              R.raw.audio_dolby_living_room);
            mPlayer.start();
          }
          catch (Exception e) {
            e.printStackTrace();
          }
        }
        else {
          mPlayer.stop();
          mPlayer.release();
          mPlayer = null;
        }
      }
    });
    setContentView(btnPlay);
 
    mDolbyAudioProcessing =
      DolbyAudioProcessing.getDolbyAudioProcessing(this, DolbyAudioProcessing.PROFILE.GAME, this);
    if (mDolbyAudioProcessing == null) {
        Toast.makeText(this,
          "Dolby Audio Processing not available on this device.",
          Toast.LENGTH_SHORT).show();
        finish();
        return;
    }
  }
 
  @Override
  public void onDestroy() {
    super.onDestroy();
    mDolbyAudioProcessing.releaseDolbyAudioProcessing();
    if(mPlayer != null) {
      mPlayer.release();
    }
  }
 
  //OnCompletionListener Methods
  @Override
  public void onCompletion(MediaPlayer mp) {
    mPlayer.release();
    mPlayer = null;
  }
 
  @Override
  public void onClientConnected() {
  }
 
  @Override
  public void onEnabled(boolean on) {
  }
 
  @Override
  public void onProfileSelected(int profile) {
  }
 
  @Override
  public void onClientDisconnected() {
  }
}

Walking through these one by one, here’s what we have to do:

Logistics

The DolbyAudioProcessing class and its associated event listener interface are in the "com.dolby.dap" package, and the JAR file containing the code must be in the Android project’s "libs" directory.

Obtain a DolbyAudioProcessing instance

Like many of the other Android APIs, this is done through a static factory method on the DolbyAudioProcessing class itself, calling the static method "getDolbyAudioProcessing", passing in the Activity for both of the required parameters. When finished, the DolbyAudioProcessing object is essentially "attached" to the Activity, and serves as a bridge between your app and the Dolby hardware.

Implement OnDolbyAudioProcessingListener

Just as the MediaPlayer.OnCompletionListener interface provides the necessary hooks for the MediaPlayer to notify the Activity of changes in the MediaPlayer state (such as, in this case, the fact that playback has finished), the Dolby hardware system might periodically want or need to notify your application of various things happening to it. Four methods make up this interface:

  • "onClientConnected" and its sibling
  • "onClientDisconnected", informing the Activity when the Dolby processing is all wired up in the first case and shut down in the second,
  • "onEnabled", informing the Activity when the processing is enabled and disabled (depending on the value of the Boolean parameter), and
  • "onProfileSelected", informing the Activity which profile is being selected.

(We’ll talk more about "profiles" in just a second.)

Bear in mind that these callbacks could (and frequently will) be invoked from a thread other than the UI thread, so if the Activity wants to change its UI, it should do so by the Android Handler/handleMessage mechanism to get back onto the main UI thread.

Enable DolbyAudioProcessing

This is a straightforward call to setAudioProcessingEnabled(), passing in true to enable it, and false to disable it. Enabling the processing can only be done once the processing has connected up to the Activity, so usually this is best done from within the onClientConnected callback.

Release the DolbyAudioProcessing instance

Within the Activity’s onCleanup method, call releaseDolbyAudioProcessing() to clean up any allocated resources that were acquired when the Activity acquired the processing instance in the first place. There’s usually no reason not to acquire it in onCreate() and release it on onCleanup(), so these will almost always be boilerplate.

Profiles

As mentioned earlier, the Dolby system maintains several different "profiles" for optimizing the listening experience based on the kind of audio being played—for example, a "Music" profile will be optimized for most music, whereas a "Voice" profile will be more geared towards making voices more clear.

These profiles are obtained using some traditional Android tactics: the total number of profiles is given by the getNumProfiles() call, and each profile is integer-indexed as if they were in an array. So, for example, to see the name of the second profile in the list, call getProfileName(1). To determine which profile is the currently-selected profile, call getSelectedProfile() to get its integer index, and to change the profile, it’s a setSelectedProfile() again with the integer index of the profile to use.

Good citizenship

By the way, although it should go without having to say explicitly, a good Android application should relinquish control of the speakers when the app fades into the background. Although there are many things an app can continue doing when it’s not in the foreground (downloading data, playing an animation on the now-offscreen window, and so on) without negatively affecting the user’s use of the device (other than consuming some CPU when it shouldn’t), playing music through the speakers definitely qualifies as one of the more distracting things it can do. Having epic soundtrack blasting through the speakers when you’re trying to talk to Grandma about her bunions may seem like a great idea at first, but it’s probably not going to make your users happy. Trust me on this.

Summary

On the whole, the Dolby support is pretty straightforward to turn on, particularly since it’s mostly a matter of a half-dozen or so method calls around the same areas of interest that the MediaPlayer itself is being called—during initialization and cleanup of the Activity, whenever the user changes application settings, and alongside the traditional Android Media behaviors. In all honesty, these are pretty small changes to make to an Android application in exchange for some really impressive differences in audio quality, even when using the same audio files. For example, writing the above sample app took all of about an hour. Adding Dolby support to an existing app using sound already will likely take even less.

Now if you’ll all excuse me, I’m going to crank the volume on the Pacific Rim soundtrack and feel a little more epic for a while.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here