在Echoprint系列--编译中编译了源码,这次将Echoprint移植到Android平台并测试识别歌曲功能。
codegen_response_t *codegen_file(char* filename, int start_offset, int duration, int tag)
Notes about libcodegen: Code generation takes a buffer of floating point PCM data sampled at 11025 Hz and mono. Codegen * pCodegen = new Codegen(const float* pcm, uint numSamples, int start_offset); pcm: a buffer of floats, mono, 11025 Hz numSamples: the number of samples start_offset: creates a hint to the server on where the sample is taken from in the original file if known string code = pCodegen->getCodeString(); The code string is just a base64 encoding of a zlib compression of the original code string, which is a hex encoded series of ASCII numbers. See API/fp.py in echoprint-server for decoding help. You only need to query for 20 seconds of audio to get a result.打开Android Studio创建一个Android项目,新建一个文件名为Codegen.java,在里面写入
native String codegen(float data[], int numSamples);这次在移动设备上识别的是通过麦克风录音,所以参数不一样的,使用javah将 Codegen.java生成头文件
zhangjiedeMacBook-Pro:java zhangjie$ javah -classpath . -jni cc.jwzhangjie.echoprintandroid.Codegen
在终端的当前目录会生成一个cc_jwzhangjie_echoprintandroid_Codegen.h
#include <string.h> #include "cc_jwzhangjie_echoprintandroid_Codegen.h" #include "Codegen.h" JNIEXPORT jstring JNICALL Java_cc_jwzhangjie_echoprintandroid_Codegen_codegen (JNIEnv *env, jobject thiz, jfloatArray pcmData, jint numSamples){ float *data = (float *)env->GetFloatArrayElements(pcmData, 0); Codegen c = Codegen(data, (unsigned int)numSamples, 0); const char *code = c.getCodeString().c_str(); env->ReleaseFloatArrayElements(pcmData, data, 0); return env->NewStringUTF(code); }
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := echoprint-jni LOCAL_SRC_FILES := cc_jwzhangjie_echoprintandroid_Codegen.cpp \ Codegen.cpp \ Whitening.cpp \ SubbandAnalysis.cpp \ MatrixUtility.cpp \ Fingerprint.cpp \ Base64.cpp \ AudioStreamInput.cpp \ AudioBufferInput.cpp LOCAL_LDLIBS := -llog\ -lz LOCAL_C_INCLUDES := . \ ./boost_1_58_0 LOCAL_CPPFLAGS += -fexceptions include $(BUILD_SHARED_LIBRARY)
APP_STL := gnustl_static APP_ABI := armeabi armeabi-v7a
zhangjiedeMacBook-Pro:jni zhangjie$ ndk-build
[armeabi] Compile++ thumb: echoprint-jni <= cc_jwzhangjie_echoprintandroid_Codegen.cpp
[armeabi] Compile++ thumb: echoprint-jni <= Codegen.cpp
[armeabi] Compile++ thumb: echoprint-jni <= Whitening.cpp
[armeabi] Compile++ thumb: echoprint-jni <= SubbandAnalysis.cpp
[armeabi] Compile++ thumb: echoprint-jni <= MatrixUtility.cpp
[armeabi] Compile++ thumb: echoprint-jni <= Fingerprint.cpp
[armeabi] Compile++ thumb: echoprint-jni <= Base64.cpp
[armeabi] Compile++ thumb: echoprint-jni <= AudioStreamInput.cpp
[armeabi] Compile++ thumb: echoprint-jni <= AudioBufferInput.cpp
[armeabi] SharedLibrary : libechoprint-jni.so
[armeabi] Install : libechoprint-jni.so => libs/armeabi/libechoprint-jni.so
[armeabi-v7a] Compile++ thumb: echoprint-jni <= cc_jwzhangjie_echoprintandroid_Codegen.cpp
[armeabi-v7a] Compile++ thumb: echoprint-jni <= Codegen.cpp
[armeabi-v7a] Compile++ thumb: echoprint-jni <= Whitening.cpp
[armeabi-v7a] Compile++ thumb: echoprint-jni <= SubbandAnalysis.cpp
[armeabi-v7a] Compile++ thumb: echoprint-jni <= MatrixUtility.cpp
[armeabi-v7a] Compile++ thumb: echoprint-jni <= Fingerprint.cpp
[armeabi-v7a] Compile++ thumb: echoprint-jni <= Base64.cpp
[armeabi-v7a] Compile++ thumb: echoprint-jni <= AudioStreamInput.cpp
[armeabi-v7a] Compile++ thumb: echoprint-jni <= AudioBufferInput.cpp
[armeabi-v7a] SharedLibrary : libechoprint-jni.so
[armeabi-v7a] Install : libechoprint-jni.so => libs/armeabi-v7a/libechoprint-jni.so
现在成功编译出libechoprint-jni.so文件
Codegen.java这个类用来调用so将ccm生成code
package cc.jwzhangjie.echoprintandroid; /** * Created by zhangjie on 15/6/9. */ public class Codegen { private final float normalizingValue = Short.MAX_VALUE; native String codegen(float data[], int numSamples); static { System.loadLibrary("echoprint-jni"); } /** * Invoke the echoprint native library and generate the fingerprint code.<br> * Echoprint REQUIRES PCM encoded audio with the following parameters:<br> * Frequency: 11025 khz<br> * Data: MONO - PCM enconded float array * * @param data PCM encoded data as floats [-1, 1] * @param numSamples number of PCM samples at 11025 KHz * @return The generated fingerprint as a compressed - base64 string. */ public String generate(float data[], int numSamples) { return codegen(data, numSamples); } /** * Invoke the echoprint native library and generate the fingerprint code.<br> * Since echoprint requires the audio data to be an array of floats in the<br> * range [-1, 1] this method will normalize the data array transforming the<br> * 16 bit signed shorts into floats. * * @param data PCM encoded data as shorts * @param numSamples number of PCM samples at 11025 KHz * @return The generated fingerprint as a compressed - base64 string. */ public String generate(short data[], int numSamples) { // echoprint expects data as floats, which is the native value for // core audio data, and I guess ffmpeg // Android records data as 16 bit shorts, so we need to normalize the // data before sending it to echoprint float normalizeAudioData[] = new float[numSamples]; for (int i = 0; i < numSamples - 1; i++) normalizeAudioData[i] = data[i] / normalizingValue; return this.codegen(normalizeAudioData, numSamples); } }
AudioFingerprinter.java这个主要录音,首先要到
http://www.mooma.sh/api.html 申请一个api_key
package cc.jwzhangjie.echoprintandroid;
import android.app.Activity; import android.media.AudioFormat; import android.media.AudioRecord; import android.media.MediaRecorder; import android.util.Log; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import org.json.JSONArray; import org.json.JSONObject; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import java.util.Hashtable; public class AudioFingerprinter implements Runnable { public final static String META_SCORE_KEY = "meta_score"; public final static String SCORE_KEY = "score"; public final static String ALBUM_KEY = "release"; public final static String TITLE_KEY = "track"; public final static String TRACK_ID_KEY = "track_id"; public final static String ARTIST_KEY = "artist"; // Instead now using the MooMash API (http://www.mooma.sh/api.html). // 之前的Api接口已经弃用,现在Echoprint使用MooMash API,可以到http://www.mooma.sh/api.html申请一个api_key private final String SERVER_URL = "http://api.mooma.sh/v1/song/identify?api_key=YOURMOOMASHAPIKEYHERE&code="; private final int FREQUENCY = 11025; private final int CHANNEL = AudioFormat.CHANNEL_IN_MONO; private final int ENCODING = AudioFormat.ENCODING_PCM_16BIT; private Thread thread; private volatile boolean isRunning = false; AudioRecord mRecordInstance = null; private short audioData[]; private int bufferSize; private int secondsToRecord; private volatile boolean continuous; private AudioFingerprinterListener listener; /** * Constructor for the class * * @param listener is the AudioFingerprinterListener that will receive the callbacks */ public AudioFingerprinter(AudioFingerprinterListener listener) { this.listener = listener; } /** * Starts the listening / fingerprinting process using the default parameters:<br> * A single listening pass of 20 seconds */ public void fingerprint() { // set dafault listening time to 20 seconds this.fingerprint(20); } /** * Starts a single listening / fingerprinting pass * * @param seconds the seconds of audio to record. */ public void fingerprint(int seconds) { // no continuous listening this.fingerprint(seconds, false); } /** * Starts the listening / fingerprinting process * * @param seconds the number of seconds to record per pass * @param continuous if true, the class will start a new fingerprinting pass after each pass */ public void fingerprint(int seconds, boolean continuous) { if (this.isRunning) return; this.continuous = continuous; // cap to 30 seconds max, 10 seconds min. this.secondsToRecord = Math.max(Math.min(seconds, 30), 10); // start the recording thread thread = new Thread(this); thread.start(); } /** * stops the listening / fingerprinting process if there's one in process */ public void stop() { this.continuous = false; if (mRecordInstance != null) mRecordInstance.stop(); } /** * The main thread<br> * Records audio and generates the audio fingerprint, then it queries the server for a match and forwards the results to the listener. */ public void run() { this.isRunning = true; try { // create the audio buffer // get the minimum buffer size int minBufferSize = AudioRecord.getMinBufferSize(FREQUENCY, CHANNEL, ENCODING); // and the actual buffer size for the audio to record // frequency * seconds to record. bufferSize = Math.max(minBufferSize, this.FREQUENCY * this.secondsToRecord); audioData = new short[bufferSize]; // start recorder mRecordInstance = new AudioRecord( MediaRecorder.AudioSource.MIC, FREQUENCY, CHANNEL, ENCODING, minBufferSize); willStartListening(); mRecordInstance.startRecording(); boolean firstRun = true; do { try { willStartListeningPass(); long time = System.currentTimeMillis(); // fill audio buffer with mic data. int samplesIn = 0; do { samplesIn += mRecordInstance.read(audioData, samplesIn, bufferSize - samplesIn); if (mRecordInstance.getRecordingState() == AudioRecord.RECORDSTATE_STOPPED) break; } while (samplesIn < bufferSize); Log.e("Fingerprinter", "Audio recorded: " + (System.currentTimeMillis() - time) + " millis"); // see if the process was stopped. if (mRecordInstance.getRecordingState() == AudioRecord.RECORDSTATE_STOPPED || (!firstRun && !this.continuous)) break; // create an echoprint codegen wrapper and get the code time = System.currentTimeMillis(); Codegen codegen = new Codegen(); String code = codegen.generate(audioData, samplesIn); Log.e("Fingerprinter", "Codegen created in: " + (System.currentTimeMillis() - time) + " millis"); if (code.length() == 0) { // no code? // not enough audio data? continue; } didGenerateFingerprintCode(code); // fetch data from echonest time = System.currentTimeMillis(); String urlstr = SERVER_URL + code; URL url = new URL(SERVER_URL+code); HttpURLConnection urlConnection = (HttpURLConnection)url.openConnection(); InputStream instream = new BufferedInputStream(urlConnection.getInputStream()); String result = convertStreamToString(instream); instream.close(); Log.e("Fingerprinter", "Results fetched in: " + (System.currentTimeMillis() - time) + " millis"); // On successful recognition the MooMash API returns a JSON structure such as: // {"response":{"songs":[{"artist_id":"","artist_name":"P!nk","id":"","score":54,"title":"Don't Let Me Get Me","message":"OK"}],"status":{"version":"1.0","message":"Success","code":0}}} Log.e("AudioFingerprinter", "run - result: " + result); // parse JSON JSONObject jobj = new JSONObject(result); if (jobj.has("response")) { JSONObject responseObject = jobj.getJSONObject("response"); if (responseObject.has("songs")) { JSONArray songsArray = responseObject.getJSONArray("songs"); if (songsArray.length() > 0) { JSONObject songObject = songsArray.getJSONObject(0); Hashtable<String, String> match = new Hashtable<String, String>(); match.put("artist_name", songObject.getString("artist_name")); match.put("title", songObject.getString("title")); didFindMatchForCode(match, code); } else { didNotFindMatchForCode(code); } } } else { didFailWithException(new Exception("result JSON parsing error")); } firstRun = false; didFinishListeningPass(); } catch (Exception e) { e.printStackTrace(); Log.e("Fingerprinter", e.getLocalizedMessage()); didFailWithException(e); } } while (this.continuous); } catch (Exception e) { e.printStackTrace(); Log.e("Fingerprinter", e.getLocalizedMessage()); didFailWithException(e); } if (mRecordInstance != null) { mRecordInstance.stop(); mRecordInstance.release(); mRecordInstance = null; } this.isRunning = false; didFinishListening(); } private static String convertStreamToString(InputStream is) { /* * To convert the InputStream to String we use the BufferedReader.readLine() * method. We iterate until the BufferedReader return null which means * there's no more data to read. Each line will appended to a StringBuilder * and returned as String. */ BufferedReader reader = new BufferedReader(new InputStreamReader(is)); StringBuilder sb = new StringBuilder(); String line = null; try { while ((line = reader.readLine()) != null) { sb.append(line + "\n"); } } catch (IOException e) { e.printStackTrace(); } finally { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } return sb.toString(); } private String messageForCode(int code) { try { String codes[] = { "NOT_ENOUGH_CODE", "CANNOT_DECODE", "SINGLE_BAD_MATCH", "SINGLE_GOOD_MATCH", "NO_RESULTS", "MULTIPLE_GOOD_MATCH_HISTOGRAM_INCREASED", "MULTIPLE_GOOD_MATCH_HISTOGRAM_DECREASED", "MULTIPLE_BAD_HISTOGRAM_MATCH", "MULTIPLE_GOOD_MATCH" }; return codes[code]; } catch (ArrayIndexOutOfBoundsException e) { return "UNKNOWN"; } } private void didFinishListening() { Log.v("AudioFingerprinter", "didFinishListening"); if (listener == null) return; if (listener instanceof Activity) { Activity activity = (Activity) listener; activity.runOnUiThread(new Runnable() { public void run() { listener.didFinishListening(); } }); } else listener.didFinishListening(); } private void didFinishListeningPass() { Log.v("AudioFingerprinter", "didFinishListeningPass"); if (listener == null) return; if (listener instanceof Activity) { Activity activity = (Activity) listener; activity.runOnUiThread(new Runnable() { public void run() { listener.didFinishListeningPass(); } }); } else listener.didFinishListeningPass(); } private void willStartListening() { Log.v("AudioFingerprinter", "willStartListening"); if (listener == null) return; if (listener instanceof Activity) { Activity activity = (Activity) listener; activity.runOnUiThread(new Runnable() { public void run() { listener.willStartListening(); } }); } else listener.willStartListening(); } private void willStartListeningPass() { Log.v("AudioFingerprinter", "willStartListeningPass"); if (listener == null) return; if (listener instanceof Activity) { Activity activity = (Activity) listener; activity.runOnUiThread(new Runnable() { public void run() { listener.willStartListeningPass(); } }); } else listener.willStartListeningPass(); } private void didGenerateFingerprintCode(final String code) { Log.e("AudioFingerprinter", "didGenerateFingerprintCode - code: " + code); if (listener == null) return; if (listener instanceof Activity) { Activity activity = (Activity) listener; activity.runOnUiThread(new Runnable() { public void run() { listener.didGenerateFingerprintCode(code); } }); } else listener.didGenerateFingerprintCode(code); } private void didFindMatchForCode(final Hashtable<String, String> table, final String code) { Log.v("AudioFingerprinter", "didFindMatchForCode - table: " + table); if (listener == null) return; if (listener instanceof Activity) { Activity activity = (Activity) listener; activity.runOnUiThread(new Runnable() { public void run() { listener.didFindMatchForCode(table, code); } }); } else listener.didFindMatchForCode(table, code); } private void didNotFindMatchForCode(final String code) { Log.v("AudioFingerprinter", "didNotFindMatchForCode"); if (listener == null) return; if (listener instanceof Activity) { Activity activity = (Activity) listener; activity.runOnUiThread(new Runnable() { public void run() { listener.didNotFindMatchForCode(code); } }); } else listener.didNotFindMatchForCode(code); } private void didFailWithException(final Exception e) { Log.v("AudioFingerprinter", "didFailWithException - e: " + e.getLocalizedMessage()); if (listener == null) return; if (listener instanceof Activity) { Activity activity = (Activity) listener; activity.runOnUiThread(new Runnable() { public void run() { listener.didFailWithException(e); } }); } else listener.didFailWithException(e); } /** * Interface for the fingerprinter listener<br> * Contains the different delegate methods for the fingerprinting process * @author Alex Restrepo * */ public interface AudioFingerprinterListener { /** * Called when the fingerprinter process loop has finished */ public void didFinishListening(); /** * Called when a single fingerprinter pass has finished */ public void didFinishListeningPass(); /** * Called when the fingerprinter is about to start */ public void willStartListening(); /** * Called when a single listening pass is about to start */ public void willStartListeningPass(); /** * Called when the codegen libary generates a fingerprint code * @param code the generated fingerprint as a zcompressed, base64 string */ public void didGenerateFingerprintCode(String code); /** * Called if the server finds a match for the submitted fingerprint code * @param table a hashtable with the metadata returned from the server * @param code the submited fingerprint code */ public void didFindMatchForCode(Hashtable<String, String> table, String code); /** * Called if the server DOES NOT find a match for the submitted fingerprint code * @param code the submited fingerprint code */ public void didNotFindMatchForCode(String code); /** * Called if there is an error / exception in the fingerprinting process * @param e an exception with the error */ public void didFailWithException(Exception e); } }
MainActivity.java界面类
package cc.jwzhangjie.echoprintandroid; import android.support.v4.app.FragmentActivity; import android.os.Bundle; import android.util.Log; import android.widget.CompoundButton; import android.widget.ToggleButton; import java.util.Hashtable; public class MainActivity extends FragmentActivity implements AudioFingerprinter.AudioFingerprinterListener{ private ToggleButton recordMicBtn; private AudioFingerprinter mAudioFingerprinter; private String TAG = "MainActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mAudioFingerprinter = new AudioFingerprinter(this); recordMicBtn = (ToggleButton)findViewById(R.id.recordMicBtn); recordMicBtn.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (isChecked){ Log.e(TAG, "开启"); mAudioFingerprinter.fingerprint(); }else{ Log.e(TAG, "关闭"); mAudioFingerprinter.stop(); } } }); } @Override public void didFinishListening() { Log.e(TAG, "录音完成监听"); } @Override public void didFinishListeningPass() { Log.e(TAG, "录音监听完成"); } @Override public void willStartListening() { Log.e(TAG, "录音将要开始监听"); } @Override public void willStartListeningPass() { Log.e(TAG, "录音监听开始"); } @Override public void didGenerateFingerprintCode(String code) { Log.e(TAG, "生成指纹Code"); } @Override public void didFindMatchForCode(Hashtable<String, String> table, String code) { Log.e(TAG, "匹配指纹Code"); } @Override public void didNotFindMatchForCode(String code) { Log.e(TAG, "没有匹配的指纹Code"); } @Override public void didFailWithException(Exception e) { Log.e(TAG, "失败匹配的指纹Code"); } }结果:
06-10 14:39:08.496 12416-12416/cc.jwzhangjie.echoprintandroid E/MainActivity﹕ 开启 06-10 14:39:08.529 12416-21688/cc.jwzhangjie.echoprintandroid E/dalvikvm﹕ GC_FOR_ALLOC freed 1120K, 23% free 6806K/8775K, paused 14ms+2ms, total 34ms 06-10 14:39:08.552 12416-21688/cc.jwzhangjie.echoprintandroid V/AudioFingerprinter﹕ willStartListening 06-10 14:39:08.552 12416-12416/cc.jwzhangjie.echoprintandroid E/MainActivity﹕ 录音将要开始监听 06-10 14:39:08.626 12416-21688/cc.jwzhangjie.echoprintandroid V/AudioFingerprinter﹕ willStartListeningPass 06-10 14:39:08.626 12416-12416/cc.jwzhangjie.echoprintandroid E/MainActivity﹕ 录音监听开始 06-10 14:39:28.626 12416-21688/cc.jwzhangjie.echoprintandroid E/Fingerprinter﹕ Audio recorded: 20000 millis 06-10 14:39:28.663 12416-21688/cc.jwzhangjie.echoprintandroid E/dalvikvm﹕ GC_FOR_ALLOC freed 437K, 23% free 6806K/8775K, paused 4ms+4ms, total 36ms 06-10 14:39:28.666 12416-21688/cc.jwzhangjie.echoprintandroid I/dalvikvm-heap﹕ Grow heap (frag case) to 7.721MB for 882016-byte allocation 06-10 14:39:28.959 12416-12423/cc.jwzhangjie.echoprintandroid E/dalvikvm﹕ GC_FOR_ALLOC freed <1K, 21% free 7667K/9671K, paused 2ms+1ms, total 20ms 06-10 14:39:28.969 12416-21688/cc.jwzhangjie.echoprintandroid D/Fingerprinter﹕ Codegen created in: 343 millis 06-10 14:39:28.969 12416-21688/cc.jwzhangjie.echoprintandroid E/AudioFingerprinter﹕ didGenerateFingerprintCode - code: eJyll21uBC0OhK8E2IA5jvnw_Y-wD5F2530jDfmxUlSJOjPdplxVdqeUdKYHNHmBrReMeMFqL4j1gCzjASX5C9p5QMr9BaoveLOxygvOfsBf580vyOMFai9o-wU2XwDX3yHl_IJiL3h3YfUXxH5Avg__DrJfMOcLznrAH4q9dX-HoS_w9oBUygvePHt_wVvtu78gzgPyPfV3ePdozBfc4PkKf_TI1wNS9hdIfsEfmVNe8NPkr3DKA246PMDtBddqX-GPvJLyAj8PSMlfUPIL_kiV-oK3ns98QC77BXJeoPGC7i94q_09F2y8wOcDSLwX_JE55QUrPyAnecENvO8wn7BfUJK94B76O1R9QbcH_DE2_h-eZ3_B1gfkPF8w9gvmesH_04U_eG5fQdponmz2qGnJzNO87h6Sou_QtnafZdjOeK7ttmvWZbOETS1rn15zGylbPWP3XTG19S5ecvFS926irYqeskO4axqyVrfBfU9Pe2rtJ-Pl2qtqCRxvamfraBJVRjaPtLbOaW1prUeXCh9rK1qkI_luls34QM3jeHfhfnMW815bz7G4Txqz8L0PEFlqv659oLhwwJmzBXfpqnKnhSdtTT2d2j3vtfNO0ylzNR4xlNMV667jqIdmydGdHJndvbS0fVT3lU8xGb3Wn2d1vgtTMFr35A-rZhzVLPoKSIGmUmO4DG393sjCR86xZ5TgY8KrzZFhZ-jZ3iInM1292qTKsFppz4ECT17HhvAhGH1p5LY3t7k1qYTYtj61zRol9bPUxOJYHkM5znKjkXm0w3PiLBq7c1sitUpz9Xa8Tj6fJ205mdet-YGy5fiva__4b1pnhfgJErd07TJD-GvNreqjKMqLKFMyPJyTJnUPHjETohx3Zrkdk0RdcTXTzsJ_9NXoCd-YZ1odvLYsh8tZHZWPM6m3mjRZ_Shl-6ytec9j5WJQVzp7g5yM_AsNb9Ra6IdX62lZOtq12iqhEXXkFbOvRDNy9qJQMtI5JFfum8-yNm1ZmxMqrdF0ssiBPiGO0f-epc1VaGdynXoYaGMi8l7kFKlY2u-jlHe6dmqmmQk19yk1VRSAnaZZDwYS_txzu8_TZup724wP8KvJr2sf6D0QprlZ7RmVjTkjRdms9NapsaFTLIgpaFnjhXkjgUvJsLE0G17cOY8qaVejOlkywvnmaZVK0pXTahBdzrZ1qjbfdV8ppRbI89juWrIKpfcD4YiJB42YqI5TrpaNBKumpQzZC0GL45ZVY-PyaUlirUDEm7gRlcW31-hW5q5t5NZC6UXqRIm0FJi3plEte-dE3aPBvh-JnzhCxMKclUqTUTp9lr61ZU53JAn-W31ch6-lEGctPiB5U_C_r32gj4PXq_cbVPgbOc6NXJtmLFkXDEVGmpX7N4SOwXySpL5qqxPKObb4WjctqkjRPORwvO3VM54s2KLsvQYZ2KU2oUjOERyjQoejg-xOwqO5KJAyFWsQKYRcIsGP7tEQHIFHhAdcT_iyuW1Uhdo0yIFUjnu7Mey0CK2R-FpXXa4JWU_LEYIeWtUleR4aso7gbUo8OBo1o-ewMbrsG9SerZ10qUBMQsU0rhVuyDjsje7uQsisdd9A_gt17iW_rn3ACDB6VxoJiwA0hxZrsNIof1ROCqXGOGOikqwzEQBYcHV0IKvgtpLafenM6FzPxKRIfyifRx4Hc5QxbdBQ9OSS1hzS6qlbJjJPrd9oj2UEYjSOMrUT93i6mWyxhl8LVM-uNqxRymz4lrnAGJUy9YJTZF3lDEMYeo4whAg3i3mi-pIdyObA6MJzsjFv4T9CVjsJ6UzPOoPEJCZ-tlFY8SSMa4Zi38KE9yxO1DQ4j8ExCqfnFDIE_55yCJVkeJMp5FfW26GUi4wuTYwVEgoBDSisXvAfnapxPkDyw8y_r_0DYjKRKnVS-jBWAvoZRHYkjnxF3nMd0RIJWyG4aUBbRkuLQOdXLzrxNzwwNic367CYMeaqObhOtlx5I1X8TPoxI8om7nfDNZ1IQOXXr4fhkM24W2D2Zlgubv4EOl7CgtOzMCGEfWGEnc4PrSCGoaoMxioLyOTL1LWDmOe9sba7uBIflfRwvlgdqlaKGyK8lLCh5M0ApcO84JhkxvYdULpvW6GTsnthnyrsKa0X00o7x90o1NimdEr7wMxI69e1D-wbt3RweFuNLEisa-nO2M0DGKrsY4Nhnxj_obo7JG2pZd1wtUSOojXGAGJm7rACok0WPPY6dgW4o_ilLFLbf4xgE0YqQXrX4MKMomfU0fNirxDYPYaCmNS7N24qd01hO4mNspSlyrCIut10jMrE4OhkH4qvRXgGlUeCAGyMByaa-EmeMViB2CcIfpsyKgdRRJYC9xBomaDddx_Ka7J28pbkjbaQP0g2j7v77rs8Z2KuOI9mlCSmDkIjSv4HO8aIX9c-8B9xCJcT 06-10 14:39:28.969 12416-12416/cc.jwzhangjie.echoprintandroid E/MainActivity﹕ 生成指纹CodeeJyll21uBC0OhK8E2IA5jvnw_Y-wD5F2530jDfmxUlSJOjPdplxVdqeUdKYHNHmBrReMeMFqL4j1gCzjASX5C9p5QMr9BaoveLOxygvOfsBf580vyOMFai9o-wU2XwDX3yHl_IJiL3h3YfUXxH5Avg__DrJfMOcLznrAH4q9dX-HoS_w9oBUygvePHt_wVvtu78gzgPyPfV3ePdozBfc4PkKf_TI1wNS9hdIfsEfmVNe8NPkr3DKA246PMDtBddqX-GPvJLyAj8PSMlfUPIL_kiV-oK3ns98QC77BXJeoPGC7i94q_09F2y8wOcDSLwX_JE55QUrPyAnecENvO8wn7BfUJK94B76O1R9QbcH_DE2_h-eZ3_B1gfkPF8w9gvmesH_04U_eG5fQdponmz2qGnJzNO87h6Sou_QtnafZdjOeK7ttmvWZbOETS1rn15zGylbPWP3XTG19S5ecvFS926irYqeskO4axqyVrfBfU9Pe2rtJ-Pl2qtqCRxvamfraBJVRjaPtLbOaW1prUeXCh9rK1qkI_luls34QM3jeHfhfnMW815bz7G4Txqz8L0PEFlqv659oLhwwJmzBXfpqnKnhSdtTT2d2j3vtfNO0ylzNR4xlNMV667jqIdmydGdHJndvbS0fVT3lU8xGb3Wn2d1vgtTMFr35A-rZhzVLPoKSIGmUmO4DG393sjCR86xZ5TgY8KrzZFhZ-jZ3iInM1292qTKsFppz4ECT17HhvAhGH1p5LY3t7k1qYTYtj61zRol9bPUxOJYHkM5znKjkXm0w3PiLBq7c1sitUpz9Xa8Tj6fJ205mdet-YGy5fiva__4b1pnhfgJErd07TJD-GvNreqjKMqLKFMyPJyTJnUPHjETohx3Zrkdk0RdcTXTzsJ_9NXoCd-YZ1odvLYsh8tZHZWPM6m3mjRZ_Shl-6ytec9j5WJQVzp7g5yM_AsNb9Ra6IdX62lZOtq12iqhEXXkFbOvRDNy9qJQMtI5JFfum8-yNm1ZmxMqrdF0ssiBPiGO0f-epc1VaGdynXoYaGMi8l7kFKlY2u-jlHe6dmqmmQk19yk1VRSAnaZZDwYS_txzu8_TZup724wP8KvJr2sf6D0QprlZ7RmVjTkjRdms9NapsaFTLIgpaFnjhXkjgUvJsLE0G17cOY8qaVejOlkywvnmaZVK0pXTahBdzrZ1qjbfdV8ppRbI89juWrIKpfcD4YiJB42YqI5TrpaNBKumpQzZC0GL45ZVY-PyaUlirUDEm7gRlcW31-hW5q5t5NZC6UXqRIm0FJi3plEte-dE3aPBvh-JnzhCxMKclUqTUTp9lr61ZU53JAn-W31ch6-lEGctPiB5U_C_r32gj4PXq_cbVPgbOc6NXJtmLFkXDEVGmpX7N4SOwXySpL5qqxPKObb4WjctqkjRPORwvO3VM54s2KLsvQYZ2KU2oUjOERyjQoejg-xOwqO5KJAyFWsQKYRcIsGP7tEQHIFHhAdcT_iyuW1Uhdo0yIFUjnu7Mey0CK2R-FpXXa4JWU_LEYIeWtUleR4aso7gbUo8OBo1o-ewMbrsG9SerZ10qUBMQsU0rhVuyDjsje7uQsisdd9A_gt17iW_rn3ACDB6VxoJiwA0hxZrsNIof1ROCqXGOGOikqwzEQBYcHV0IKvgtpLafenM6FzPxKRIfyifRx4Hc5QxbdBQ9OSS1hzS6qlbJjJPrd9oj2UEYjSOMrUT93i6mWyxhl8LVM-uNqxRymz4lrnAGJUy9YJTZF3lDEMYeo4whAg3i3mi-pIdyObA6MJzsjFv4T9CVjsJ6UzPOoPEJCZ-tlFY8SSMa4Zi38KE9yxO1DQ4j8ExCqfnFDIE_55yCJVkeJMp5FfW26GUi4wuTYwVEgoBDSisXvAfnapxPkDyw8y_r_0DYjKRKnVS-jBWAvoZRHYkjnxF3nMd0RIJWyG4aUBbRkuLQOdXLzrxNzwwNic367CYMeaqObhOtlx5I1X8TPoxI8om7nfDNZ1IQOXXr4fhkM24W2D2Zlgubv4EOl7CgtOzMCGEfWGEnc4PrSCGoaoMxioLyOTL1LWDmOe9sba7uBIflfRwvlgdqlaKGyK8lLCh5M0ApcO84JhkxvYdULpvW6GTsnthnyrsKa0X00o7x90o1NimdEr7wMxI69e1D-wbt3RweFuNLEisa-nO2M0DGKrsY4Nhnxj_obo7JG2pZd1wtUSOojXGAGJm7rACok0WPPY6dgW4o_ilLFLbf4xgE0YqQXrX4MKMomfU0fNirxDYPYaCmNS7N24qd01hO4mNspSlyrCIut10jMrE4OhkH4qvRXgGlUeCAGyMByaa-EmeMViB2CcIfpsyKgdRRJYC9xBomaDddx_Ka7J28pbkjbaQP0g2j7v77rs8Z2KuOI9mlCSmDkIjSv4HO8aIX9c-8B9xCJcT 06-10 14:39:40.490 12416-21688/cc.jwzhangjie.echoprintandroid D/Fingerprinter﹕ Results fetched in: 11520 millis 06-10 14:39:40.493 12416-21688/cc.jwzhangjie.echoprintandroid V/AudioFingerprinter﹕ run - result: {"response":{"status":{"version":"1.0","message":"Invalid or missing api key (unknown api key)","code":2}}} 06-10 14:39:40.496 12416-21688/cc.jwzhangjie.echoprintandroid V/AudioFingerprinter﹕ didFinishListeningPass 06-10 14:39:40.496 12416-12416/cc.jwzhangjie.echoprintandroid E/MainActivity﹕ 录音监听完成 06-10 14:39:40.520 12416-21688/cc.jwzhangjie.echoprintandroid V/AudioFingerprinter﹕ didFinishListening 06-10 14:39:40.520 12416-12416/cc.jwzhangjie.echoprintandroid E/MainActivity﹕ 录音完成监听
下载地址:包含jni整个项目 http://pan.baidu.com/s/1jGu1yce
参考网址:
http://www.mooma.sh/api.html
http://masl.cis.gvsu.edu/2012/01/25/android-echoprint/