In this tutorial, I will show you how to use the Dolby Audio API to enhance the sound in your Android applications. To show you how to use the Dolby Audio API, we will create a simple yet fun puzzle game.
Introduction
In today's crowded mobile market, it's important to make your applications as compelling as they can possibly be. Enhancing an application's audial experience can be as appealing to the user as having a stunning user interface.
The sound created by an application is a form of interaction between the user and the application that is all too often overlooked. However, this means that offering a great audial experience can help your application stand out from the crowd.
Dolby Digital Plus is an advanced audio codec that can be used in mobile applications using Dolby's easy-to-use Audio API. Dolby has made its API available for several platforms, including Android and Kindle Fire. In this tutorial, we will look at the Android implementation of the API.
The Dolby Audio API for Android is compatible with a wide range of Android devices. This means that your Android applications and games can enjoy high-fidelity, immersive audio with only a few minutes of work integrating Dolby's Audio API. Let's explore what it takes to integrate the API by creating a puzzle game.
1. Overview
In the first part of this tutorial, I will show you how to create a fun puzzle game. Because the focus of this tutorial is on integrating the Dolby Audio API, I won't be going into too much detail and I expect you're already familiar with the basics of Android development. In the second part of this article, we'll zoom in on the integration of the Dolby Audio API in an Android application.
We're going to make a traditional puzzle game for Android. The goal of the game is to slide a puzzle piece into the empty slot of the puzzle board to move the puzzle pieces around. The player needs to repeat this process until every piece of the puzzle is in the correct order. As you can see in the screenshot below, I've added a number to each puzzle piece. This will make it easier to keep track of the pieces of the puzzle and the order they're in.
To make the game more appealing, I'll show you how to use custom images as well as how to take a photo to create your own unique puzzles. We'll also add a shuffle button to rearrange the pieces of the puzzle to start a new game.
2. Getting Started
Step 1
It's not important what IDE you use, but for this tutorial I'll be using JetBrains IntelliJ Idea. Open your IDE of choice and create a new project for your Android application. Make sure to create a main Activity class and an XML layout.
Step 2
Let's first configure the application's manifest file. In the application node of the manifest file, set hardwareAccelerated to true. This will increase your application's rendering performance even for 2D games like the one we're about to create.
In the next step, we specify the screen sizes our application will support. For games, I usually focus on devices with larger screens, but this choice is entirely up to you.
In the activity node of the manifest file, add a node named configChanges and set its value to orientation as shown below. You can find more information about this setting on the developer website.
Before we move on, add two uses-permission nodes to enable vibration and write access for our game. Insert the following snippet before the application node in the manifest file.
Step 3
Let's also add the resources that we'll be using later in this tutorial. Start by adding the image that you want to use for the puzzle. Add it to the drawable folder of your project. I've chosen to add the image to the drawable-hdpi folder of my project.
Last but not least, add the sound files that you want to use in your game. In your project's res folder, create a new directory named raw and add the sound files to this folder. For the purpose of this tutorial, I've added two audio files. The first sound is played when the player moves a puzzle piece while the second sound is played when the game is finished, that is, when the player completes the puzzle. Both sounds are available on SoundBible. The first sound is licensed under the Creative Commons Attribution 3.0 license and was recorded by Mike Koenig.
3. Creating the Brain of the Game
As I mentioned earlier, I won't explain the game creation process in detail since the focus of this tutorial is integrating the Dolby Audio API. In the next steps, I will walk you through the steps you need to take to create the puzzle game.
We start by creating a new class, SlidePuzzle, that will be the brain of the game. Every move made in the puzzle is processed and tracked by an instance of this class using simple math.
It's an important part of the game as it will determine which tiles can be moved and in what direction. The class will also notify us when the puzzle is completed.
We'll start by declaring a number of variables that we'll need a bit later. Take a look at the next code snippet in which I declare variables for the four possible directions the puzzle pieces can move in, two arrays of integers for the horizontal and vertical directions, and an array for the tiles of the puzzle. We also declare and create an instance of the Random class, which we'll use later in this tutorial.
The next step is to create an init method for the SlidePuzzle class. The init method accepts two arguments that determine the width and height of the SlidePuzzle object. Using the width and height instance variables, we instantiate the tiles array and set handleLocation as shown below.
The SlidePuzzle class also needs a setter and getter method for the tiles property. Their implementations aren't that complicated as you can see below.
In addition to the accessors for the tiles property, I've also created a handful of convenience methods that will come in handy later in this tutorial. The getColumnAt and getRowAt methods, for example, return the column and row of a particular location in the puzzle.
The distance method, another helper method we'll use in a few moments, calculates the distance between tiles using simple math and the tiles array.
The next method is getPossibleMoves, which we'll use to determine the possible positions the puzzle pieces can move to. In the following screenshot, there are four puzzle pieces that can be moved to the empty slot of the puzzle board. The pieces the player can move are 5, 2, 8 and 4. Didn't I tell you the numbers would come in handy?
The implementation of getPossibleMoves might seem daunting at first, but it's nothing more than basic math.
In the pickRandomMove method, we use the Random object we created earlier. As its name indicates, the pickRandomMove method moves a random piece of the puzzle. The Random object is used to generate a random integer, which is returned by the pickRandomMove method. The method also accepts one argument, an integer, which is the location we ignore, that is, the empty slot of the puzzle board.
The invertMove method, which is used a bit later in the shuffle method, inverts the integer used for a chosen direction.
The moveTile method accepts two integers, which are used to calculate the moves needed using basic math. The method returns true or false.
The shuffle method is used to shuffle the pieces of the puzzle when a new game begins. Take a moment to inspect its implementation as it's an important part of the game. In shuffle, we determine the limit based on the height and the width of the puzzle. As you can see, we use the distance method to determine the number of tiles that need to be moved.
There are two more helper methods we need to implement, getDirection and getHandleLocation. The getDirection method returns the direction to which the puzzle piece at location is moved and getHandleLocation returns the empty slot of the puzzle board.
4. Creating the Puzzle Board
Create a new class and call it SlidePuzzleView. This class is the view of the puzzle board, it extends the View class and will take up the entire screen of the device. The class is responsible for drawing the puzzle pieces as well as handling touch events.
In addition to a Context object, the constructor of SlidePuzzleView also accepts an instance of the SlidePuzzle class as you can see below.
We override the class's onSizeChanged method and in this method we set puzzleWidth and puzzleHeight to 0.
The refreshDimensions method is invoked when the dimensions of the view change and the puzzle needs to be rebuilt. This method is invoked in the class's onDraw method.
In the onDraw method of the SlidePuzzleView class, the actual drawing of the puzzle takes place, including drawing the lines of the puzzle board, but we also set the dimensions of the puzzle pieces to make sure they neatly fit the screen of the device. The view's SlidePuzzle instance helps us laying out the view as you can see in the implementation of onDraw below.
To handle touch events, we need to override the class's onTouchEvent method. To keep onTouchEvent concise and readable, I've also declared a few helper methods, finishDrag, doMove, startDrag, and updateDrag. These methods help implementing the dragging behavior.
I've also declared getter methods for targetWidth and targetHeight and accessors for bitmap.
5. Creating the Activity Class
With the implementation of the SlidePuzzle and SlidePuzzleView classes finished, it is time to focus on the main Activity class that your IDE created for you. The main Activity class in this example is named SlidePuzzleMain, but yours may be named differently. The SlidePuzzleMain class will bring together everything that we've created so far.
In the activity's onCreate method, we instantiate the bitmapOptions object, setting its inScaled attribute to false. We also create an instance of the SlidePuzzle class and an instance of the SlidePuzzleView class, passing the activity as the view's context. We then set the activity's view by invoking setContentView and passing in the view object.
In loadBitmap, we load the image that you added to the project at the beginning of this tutorial and that we'll use for the puzzle. The method accepts the location of the image as its only argument, which it uses to fetch the image.
In loadBitmap , we also invoke setBitmap. The implementation of setBitmap is shown below.
To make the puzzle game more appealing to the player, we'll add an option to customize the game by allowing the player to select an image for the puzzle from the user's photo gallery or take one with the device's camera. We'll also create a menu option for each method.
To make all this work, we implement two new methods, selectImage and takePicture, in which we create an intent to fetch the image we need. The onActivityResult method handles the result of the user's selection. Take a look at the code snippet below to understand the complete picture.
All that's left for us to do is create a menu option for each method. The below implementation illustrates how you can create a menu with options, which is shown to the user when they tap the device's options button.
The options menu should look similar to the one shown below.
By tapping Select image or Take photo, you should be able to select an image from your device's photo gallery or take one with the camera, and use it in the puzzle game.
You may have noticed that I've also added a third menu option to shuffle the pieces of the puzzle board. This menu option invokes the shuffle method that we implemented in the SlidePuzzle class a bit earlier in this tutorial.
Before we implement the Dolby Audio API, let's create the two methods that will trigger the playback of the audio files we added earlier. You can leave the implementations of these methods blank for now. The onFinish method is invoked when the game is finished while playSound is called whenever a puzzle piece is moved.
All that's left for us to do is invoke loadBitmap from the activity's onCreate method and pass it the location of the image you want to use for the puzzle.
Take a look at the image below for an example of what your game should look like, depending on the image you've used for the puzzle.
6. Implementing the Dolby Audio API
Step 1
As I mentioned at the beginning of this tutorial, integrating the Dolby Audio API is easy and only takes a few minutes. Let's see how we can leverage the Dolby Audio API in our game.
Start by downloading the Dolby Audio API from Dolby's developer website. To do so, create a free developer account or sign in if you already have one. Once you've downloaded the API, add the library to your project.
Step 2
Before you integrate the Dolby Audio API, it's a good idea to add volume controls to your application. This is easy to do and takes only a single line of code. Add the following code snippet to your activity's onCreate method
Step 3
The next step is to declare two variables in your Activity class, an instance of the MediaPlayer class and an instance of the DolbyAudioProcessing class. Don't forget to add the required imports at the top.
Step 4
We'll now make the Activity class adopt the OnDolbyAudioProcessingEventListener and MediaPlayer.OnCompletionListener interfaces.
To adopt these interfaces, we need to implement a few methods as shown in the code snippet below.
We enable the DolbyAudioProcessing object when onDolbyAudioProcessingClientConnected is invoked and disable it again when onDolbyAudioProcessingClientDisconnected is called.
As you can see in the previous code snippet, we release the MediaPlayer object when it has finished playing the audio file.
To play a sound when the player moves a puzzle piece, we need to implement the playSound method. Before we focus on playSound, we first create an instance of SlidePuzzleMain in the SlidePuzzleView class and in the view's playSlide method, we call playSound on the SlidePuzzleMain instance.
In the playSound method, we create an instance of the MediaPlayer class and make use of the Dolby Audio API to initiate the processing of the audio. If the Dolby Audio API isn't supported by the user's device, the getDolbyAudioProcessing method will return null.
As you can see below, the implementation of the onFinish method is very similar to that of playSound. The main difference is that we show a message to the user if the Dolby Audio API isn't available. As you may remember, the onFinish method is played when the game is finished and the player has completed the puzzle.
We also call shuffle at the end of onFinish to start a new game when the player has finished the puzzle.
Step 5
It's important that we release the DolbyAudioProcessing and MediaPlayer objects when they are no longer needed. Releasing these objects ensures we don't compromise the device's battery life and negatively impact the device's performance.
We start by declaring three methods. The first method, releaseDolbyAudioProcessing, releases the DolbyAudioProcessing object and sets it mDolbyAudioProcessing to null. The second method, restartSession, restarts the session managed by the DolbyAudioProcessing object and in the third method, suspendSession, the audio session is suspended and the current configuration is saved for later use.
As you can see in the above code snippet, I've also created a handful of methods to handle any exceptions that may be thrown in releaseDolbyAudioProcessing, restartSession, and suspendSession.
The three methods we just created need to be invoked at several key moments of the application's lifecycle. We accomplish this by overriding the onStop, onStart, onDestroy, onResume, and onPause methods in our SlidePuzzleMain class.
In onStop, we tell the MediaPlayer object to pause and in onStart the MediaPlayer object continues playback if it isn't null. The onDestroy method is called when the application is closed. In this method, we release the MediaPlayer object, set mPlayer to null, and invoke releaseDolbyAudioProcessing, which we implemented earlier.
Finally, in onPause and onResume we suspend and restart the audio session by invoking suspendSession and restartSession respectively.
If you've followed the steps outlined in this tutorial, then your game should now be fully functional with the Dolby Audio API integrated. Build the project to play with the final result.
7. Summary
I'm sure you agree that integrating the Dolby Audio API is easy and doesn't take more than five minutes. Let's briefly summarize the steps we've taken to integrate the API.
import the Dolby Audio API library
create an instance of the DolbyAudioProcessing class
implement the OnDolbyAudioProcessingEventListener interface.
enable the DolbyAudioProcessing instance in onDolbyAudioProcessingClientConnected
disable the DolbyAudioProcessing instance in onDolbyAudioProcessingClientDisconnected
after starting the media player, initialize the DolbyAudioProcessing instance using the GAME profile
check if the DolbyAudioProcessing object is null to verify if the Dolby Audio API is supported by the device
to conserve battery life and optimize performance, suspend and release the DolbyAudioProcessing instance when the application is destroyed or moved to the background
Conclusion
Even though the game we created is fairly simple, it is important to remember the focus of this tutorial, the Dolby Audio API. The mobile market is a crowded place and standing out from other games isn't easy. Adding superior sound to your game won't go unnoticed by your users and it will get your game noticed. Head over to Dolby's developer website to give it a try.