Echoprint系列--编译中编译了源代码,这次将Echoprint移植到Android平台并測试识别歌曲功能。

一、编译库

1、环境准备

  • Android NDK,我的是android-ndk-r10e
  • 改动源代码,把src中的.cxx的文件重命名为.cpp。把src文件夹重命名为jni
  • Boost源代码。在PC上编译的时候也安装boost的,我的时boost_1_58_0,复制到jni文件夹

2、编写编译配置文件

  • 打开源代码中的main.cpp,看到核心部分就是

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

  • 在libs同文件夹创建一个jni文件夹,把cc_jwzhangjie_echoprintandroid_Codegen.h拷贝进去,然后创建cc_jwzhangjie_echoprintandroid_Codegen.cpp文件,内容例如以下:
#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);
}
  • 在jni文件夹以下创建Android.mk和Application.mk。内容分别例如以下:
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)

Application.mk

APP_STL := gnustl_static
APP_ABI := armeabi armeabi-v7a
  • 把之前命名jni文件中面的内容复制到项目中的jni以下,在终端下输入:

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/

Echoprint系列--Android编译与调用的更多相关文章

  1. Android编译优化系列-kapt篇

    作者:字节跳动终端技术---王龙海 封光 兰军健 一.背景 本文是编译优化系列文章之 kapt 优化篇,后续还会有 build cache, kotlin, dex 优化等文章,敬请期待.本文由Cli ...

  2. 系列篇|编译可在Android上运行的依赖库(一):glib库

    前言 这是系列文章,它们由<编译可在Android上运行的glib库>及其他4篇文章组成,这4篇文章在“编译依赖库”一节中列出.由于glib库依赖于其他第三方库,所以需要先将依赖的第三方库 ...

  3. 在Android开发中调用Rest web服务(转)

    首先从维基百科上拷贝一点Rest的基本概念给大家看看,然后我们再开始详解在Android中如何调用Rest服务.表象化状态转变(英文:Representational State Transfer,简 ...

  4. Cocos2d-x3.6 Android编译问题

    在Cocod2d-x论坛上看到越来越多人吐槽新版本更新太快,改动太大,而且经常有BUG导致升级要折腾很久很久..但我就是喜欢折腾,喜欢升级到最新版本,看看有了哪些新功能,哪些改进.为此也折腾了不少,遇 ...

  5. Android编译过程详解(一)

    Android编译过程详解(一) 注:本文转载自Android编译过程详解(一):http://www.cnblogs.com/mr-raptor/archive/2012/06/07/2540359 ...

  6. [置顶] Android学习系列-Android中解析xml(7)

    Android学习系列-Android中解析xml(7) 一,概述 1,一个是DOM,它是生成一个树,有了树以后你搜索.查找都可以做. 2,另一种是基于流的,就是解析器从头到尾解析一遍xml文件.   ...

  7. Cocos2d-x3.3RC0的Android编译Activity启动流程分析

    本文将从引擎源代码Jni分析Cocos2d-x3.3RC0的Android Activity的启动流程,以下是具体分析. 1.引擎源代码Jni.部分Java层和C++层代码分析 watermark/2 ...

  8. ADO.NET系列之事务和调用存储过程

    ADO.NET系列之Connection对象 ADO.NET系列之Command对象 ADO.NET系列之DataAdapter对象 ADO.NET系列之事务和调用存储过程 前几篇我们介绍了Conne ...

  9. Android编译系统环境初始化过程分析

    Android源代码在编译之前,要先对编译环境进行初始化,其中最主要就是指定编译的类型和目标设备的型号.Android的编译类型主要有eng.userdebug和user三种,而支持的目标设备型号则是 ...

随机推荐

  1. CSS3 transition 属性

    transition是css3中新添加的特性,在W3C标准中是这样描述的:“css的transition允许css的属性值在一定的时间内从一个状态平滑的过渡到另一个状态.这种状态可以在鼠标单击.获得焦 ...

  2. Drools

    http://www.infoq.com/news/2009/06/drools-5.0-release http://www.javacodegeeks.com/2014/03/event-proc ...

  3. 如何在Delphi中调用VC6.0开发的COM

    上次写了如何在VC6.0下对Delphi写的COM进行调用,原本想马上写如何在Delphi中调用VC6.0开发的COM时,由于在写事例程序中碰到了个很怪的问题,在我机子上用VC写的接口程序编译能通过. ...

  4. Centos 升级MySQL版本或者Yum安装Mysql5.6

    Centos 升级MySQL版本或者Yum安装Mysql5.6 1.从MySQL Yum仓库下载最新的rpm文件:http://dev.mysql.com/downloads/repo/yum/Cen ...

  5. java 多线程学习笔记

    这篇文章主要是个人的学习笔记,是以例子来驱动的,加深自己对多线程的理解. 一:实现多线程的两种方法 1.继承Thread class MyThread1 extends Thread{ public ...

  6. nfs nobody,nobody 需要在nfs客户端修改从nfs服务器端共享过来的目录怎么办?

    1,加入我们使用nfs共享安装oracle, 安装oracle需要修改base,data,orainventory等等目录及自目录的属主及权限,一般会继承nfs客户端目录的权限及属主 groupadd ...

  7. WCF初步学习

    一.理解面向服务(Service-Oriented-Architecture)    是指为了解决在Internet环境下业务集成的需要,通过连接能完成特定任务的独立功能实体实现的一种软件系统架构.S ...

  8. MD5和sha1加密算法

    在很多电子商务和社区应用中,我们都要存放很多的客户的资料,其中包括了很多的隐私信息和客户不愿被别人看到的信息,当然好有客户执行各种操作的密码,此时就需要对客户的信息进行加密再存储,目前有两种比较好的加 ...

  9. My97DaePicker 用js实现文本框日期相减求天数

    <tr>                <td align="center" style="background-color: #cccccc;font ...

  10. JavaScript之<script>标签简介

    向html页面中插入JavaScrpt的主要方法,就是使用<script>元素,下面是Html 4.01为<script>定义的6个属性. 1.async:可选表示应该立即下载 ...