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

I am Android Developer based in Mumbai, India. APIs return perfect results inside my home. My question is "How much noise level is OK for these APIs?"

Mobile client has a hard limit and refuses to return a fingerprint when noise level is higher than 90 and loudness level is too low. But there are many different devices out there with different microphones and configurations (some devices also use some sort of AGC in their drivers). So you can't rely on hard coded values and may have to implement something that adapts to the device.

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

but i have found this error in my code..
02-24 07:51:33.704: E/AndroidRuntime(1227): java.lang.NoClassDefFoundError: com.gracenote.mmid.MobileSDK.GNConfig

The above example is great.but how can i get covert art in above code 

GNCoverArt coverArt = response.getCoverArt();

i always get coverArt=null;

what is the reason? i want to get cover Art fron GNSearchResult. what is the solution to get cover Art.

Hi, Just wondering where I can get the GN_Music_SDK.jar library from? I have been looking everywhere for it on gracenote. The only thing Gracenote provides with this is GNSDK.jar

Hi Jalal,
This guide is for the deprecated Mobile Client SDK and therefore you will not be able to find that library in the new GNSDK for Mobile.
We are currently working on providing a new version of intro docs for the new GNSDK for Mobile.
Thank you for your patience.

Thank you for letting me know, i just have one more problem, and i didn't know where to post it. I downloaded the gnsdk sample app, after inserting the my license details in and when i run the app on my phone it states "gnsdk_manager_initialize: An Invalid License was specified"(this comes on the app).

having it, looked at the details carefully, the license is correct on the website.

Hi Jalal,
Are you using the sample app for iOS?
If so, you may be able to find your answer here: http://stackoverflow.com/questions/22831788/gracenote-error-invalid-user
Hope this helps!

Hi can you provide this Android simple tutorial but using the latest GNSDK?because this page is very outdated. It is very useful to newbie like me if you update this page. Thanks!

Hi can you provide simple Android tutorial like this but using the latest GNSDK? it is very useful to newbie like me if you update this page. Thanks!

Thank you for your feedback. We are working on making a new quick start guide similar to this page. Please stay tuned!
In the meantime, you can try out the sample app which is included in the GNSDK for Android package.

Can we know a date for quick start guide similar to this page?

Any news about the starting guide ?

me too any progress?

Yes, I would like to know about updates for this as well! I plan to use the API for a final project in one of my classes.

 

Thank you

I am getting this error when running the program!

 

java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String com.gracenote.mmid.MobileSDK.GNSearchResponse.getTrackTitle()' on a null object reference
                                                                         at ytstudios.gracenote.MainActivity$RecognizeFromMic.GNResultReady(MainActivity.java:60)
                                                                         at com.gracenote.mmid.MobileSDK.GNOperations$GNOperationSearchTask.onPostExecute(GNOperations.java:277)
                                                                         at com.gracenote.mmid.MobileSDK.GNOperations$GNOperationSearchTask.onPostExecute(GNOperations.java:173)
                                                                         at android.os.AsyncTask.finish(AsyncTask.java:673)
                                                                         at android.os.AsyncTask.-wrap1(AsyncTask.java)
                                                                         at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:690)
                                                                         at android.os.Handler.dispatchMessage(Handler.java:102)
                                                                         at android.os.Looper.loop(Looper.java:154)
                                                                         at android.app.ActivityThread.main(ActivityThread.java:6236)
                                                                         at java.lang.reflect.Method.invoke(Native Method)
                                                                         at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:891)
                                                                         at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:781)

 

I love to read informative posts and you are doing it exellently.
<a href="http://www.abcchildcare.com/">Child Care</a>