Introduction

This tutorial shows you how to build a simple audio fingerprinting Android app from scratch. Audio fingerprinting involves taking a small audio sample and sending it to Gracenote for identification. The Mobile Client provides numerous methods to access Gracenote's metadata from an Android device - most of these are demonstrated in the SDK's sample app. However, unless you are a seasoned Android developer, (and I'm certainly not) the sample app's depth tends to hide how really simple it is to use the SDK.

Setting Up Your Development Environment

The first thing you need to do is set up your development environment. This can be a little tedious and slightly tricky, but as long as you follow the Set Up Your Android Development Environment section of the Getting Started Guide you'll be fine. I found that using a physical device for debugging was easier than using the emulator. Either way, it is worthwhile to follow the rest of the Getting Started Guide and make sure you can get the sample app running.

Creating Your Project

Once your environment is set up, the next step is to create a new Android Application Project (File > New > Project > Android > Android Application Project). The default settings should all be fine. I chose to start with a BlankActivity. Before we forget, let's add the libraries we'll need. Navigate to the libs directory in the SDK. Select the armeabi directory and GN_Music_SDK.jar and drag them into the libs directory inside the Package Explorer pane (see the red arrow in the image below). In the dialog box, select "copy files and folders".

Modifying the Android Manifest File

To access the Internet and record device audio, you need to update the AndroidManifest.xml file, which you can find under the main project directory (see the blue arrow in the screenshot below). Add the following code anywhere inside the <manifest> tag.

<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

Creating the UI

The next step is to set up an extremely basic UI consisting of one TextView and one Button. That's it. I deleted the default TextView that says "Hello World" and dragged a larger TextView from the "Form Widgets" menu onto the top of the view. In the main layout's XML page, you can edit the TextView details. I changed the ID to "song_info" because this is where we'll write the song's metadata after a fingerprint lookup. I also changed the default text.

<TextView
    android:id="@+id/song_info"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentTop="true"
    android:layout_centerHorizontal="true"
    android:layout_marginTop="40dp"
    android:text="Song Info"
    android:textAppearance="?android:attr/textAppearanceLarge"/>

In the same way, I created a button to start the fingerprinting. I named it "fp_button" and set the text to "Fingerprint!". 

<Button
    android:id="@+id/fp_button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_centerHorizontal="true"
    android:layout_marginBottom="130dp"
    android:text="Fingerprint!" />

Below is a screenshot of what the final layout looks like.

Coding the App

Now for some actual coding. Open the MainActivity.java file - mine is in src/com.example.demo2/. Unless you also named your project "demo2", yours will be in a different directory. In general, Eclipse is very helpful in figuring out which files to import. As you add new classes to your code, the IDE will underline the error and suggest imports to resolve the class. To keep things simple, I'm going to go ahead and import the following classes:

import com.gracenote.mmid.MobileSDK.GNOperations;
import com.gracenote.mmid.MobileSDK.GNSearchResponse;
import com.gracenote.mmid.MobileSDK.GNSearchResult;
import com.gracenote.mmid.MobileSDK.GNSearchResultReady;
import com.gracenote.mmid.MobileSDK.GNConfig;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

The app's logic is fairly straight-forward:

  1. When the button is pressed, a function is called that records music via the microphone.
  2. The Gracenote database is searched to identify the track.
  3. If a match is found, the artist name and song title are displayed.

Luckily, the SDK has a simple function call - GNOperations.recognizeMIDStreamFromMic - that does the fingerprint lookup. We are simply left with setting up a GNConfig object (required for any GNOperation), triggering the lookup, and handling the response. For this, the MainActivity class needs three member variables:

private GNConfig config;
private TextView song_info;
private Button fp_button;

Inside the onCreate function, we initialize the GNConfig object.

config = GNConfig.init("NNNNNNN-NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN",this.getApplicationContext());

Note: You need to replace the 'Ns' with your personal client ID, which can be found in your "My Apps" account profile page.

Besides a GNConfig object, all of the GNOperation functions require a result function that is called when the operation is finished. We have already initialized the config object, so now we need to create a class that implements the GNSearchResultReady interface. To do so, our new class, which I named RecognizeFromMic, will need to implement the function GNResultReady. In addition to GNResultReady, I created a function - doFingerprint - that simply calls GNOperations.recognizeMIDStreamFromMic.

class RecognizeFromMic implements GNSearchResultReady {
    void doFingerprint(
        GNOperations.recognizeMIDStreamFromMic(this,config);
    }
    public void GNResultReady(GNSearchResult result) {
        song_info = (TextView) findViewById(R.id.song_info);
        if (result.isFingerprintSearchNoMatchStatus()) {
            song_info.setText("no match");
        } else {
            GNSearchResponse response = result.getBestResponse();
            song_info.setText(response.getTrackTitle() + " by " + response.getArtist());
        }
    }
}

The GNResultReady function checks if a match is returned. If not, it writes "no match" to the song_info TextView. If the lookup returned a match, we print the track title and the artist name of the best response to the song_info TextView.

Now that we have the result callback function defined, we can bind the fingerprint lookup to the button click. In the onCreate function we're going to initialize fp_button:

fp_button = (Button) findViewById(R.id.fp_button);

And we'll set a listener to create a RecognizeFromMic object and call doFingerprint when the button is pressed

fp_button.setOnClickListener(new View.OnClickListener() {
    public void onClick(View v) {
        RecognizeFromMic task = new RecognizeFromMic();
        task.doFingerprint();
    }
});

Conclusion

Now you are ready to test the app. Build it and fingerprint away!

This app is about as easy as it gets. There are plenty of other functions in the SDK to be explored. Check out the full documentation and the demo app that shipped with the SDK to see how to search by text or an existing file and the types of metadata that you can access through our APIs. Happy hacking!

Reference

Your final MainActivity.java file should look like this:

package com.example.demo2;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;

import com.gracenote.mmid.MobileSDK.GNOperations;
import com.gracenote.mmid.MobileSDK.GNSearchResponse;
import com.gracenote.mmid.MobileSDK.GNSearchResult;
import com.gracenote.mmid.MobileSDK.GNSearchResultReady;
import com.gracenote.mmid.MobileSDK.GNConfig;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity {

	private GNConfig config;
	private TextView song_info;
	private Button fp_button;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        config = GNConfig.init("NNNNNNN-NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN",this.getApplicationContext());
        fp_button = (Button) findViewById(R.id.fp_button);
        fp_button.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                RecognizeFromMic task = new RecognizeFromMic();
                task.doFingerprint();
            }
        });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }

    class RecognizeFromMic implements GNSearchResultReady {
        void doFingerprint(){
            GNOperations.recognizeMIDStreamFromMic(this,config);
        }
        public void GNResultReady(GNSearchResult result) {
            song_info = (TextView) findViewById(R.id.song_info);
            if (result.isFingerprintSearchNoMatchStatus()) {
                song_info.setText("no match");
            } else {
                GNSearchResponse response = result.getBestResponse();
                song_info.setText(response.getTrackTitle() + " by " + response.getArtist());
            }
        }
    }
}

Comments

One way to look for variations would be to change (Effect > Invert) one the the paths then add them together (Project > Fast Mix in v1.2 or Tracks > Mix and Provide in v1.3 and later). Where the paths are similar the mountains will terminate each other out as Write My Assignment For Me, making nothing but quiet. Diiferences between the paths leaves a disturbance.

Wow I am always excited to learn on how to create own application and your concept is so good. This audio fingerprinting with Android app is really amazing I would like to try this and upload my results on my writemypapers.org reviews blog so that everyone could know about your app.  I hope this would really work out well.