• 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 + " "); } } 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/









  • 相关阅读:
    compass 制作css sprites
    net模块
    javascript -- 代理模式
    javascript -- 单例模式
    js 对象的浅拷贝和深拷贝
    js 对象的封装,继承,多态的理解
    this,call,apply
    flex 实现圣杯布局
    ubuntu中安装mongodb
    devDependencies和dependencies的区别
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/5205640.html
Copyright © 2020-2023  润新知