Echoprint系列--Android编译与调用

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/









评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值