1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 18 package com.android.cts.verifier.audio; 19 20 import android.content.Context; 21 22 import android.media.AudioFormat; 23 import android.media.AudioManager; 24 import android.media.AudioTrack; 25 import android.media.MediaRecorder; 26 import android.media.AudioRecord; 27 import android.media.MediaRecorder; 28 import android.util.Log; 29 30 import android.os.Handler; 31 import android.os.Message; 32 33 import com.android.cts.verifier.audio.audiolib.AudioSystemParams; 34 35 /** 36 * A thread that runs a native audio loopback analyzer. 37 */ 38 public class NativeAnalyzerThread { 39 private static final String TAG = "NativeAnalyzerThread"; 40 41 private Context mContext; 42 43 private final int mSecondsToRun = 5; 44 private Handler mMessageHandler; 45 private Thread mThread; 46 private volatile boolean mEnabled = false; 47 private volatile double mLatencyMillis = 0.0; 48 private volatile double mConfidence = 0.0; 49 private volatile int mSampleRate = 0; 50 private volatile boolean mIsLowLatencyStream = false; 51 52 private int mInputPreset = 0; 53 54 static final int NATIVE_AUDIO_THREAD_MESSAGE_REC_STARTED = 892; 55 static final int NATIVE_AUDIO_THREAD_MESSAGE_OPEN_ERROR = 893; 56 static final int NATIVE_AUDIO_THREAD_MESSAGE_REC_ERROR = 894; 57 static final int NATIVE_AUDIO_THREAD_MESSAGE_REC_COMPLETE = 895; 58 static final int NATIVE_AUDIO_THREAD_MESSAGE_REC_COMPLETE_ERRORS = 896; 59 static final int NATIVE_AUDIO_THREAD_MESSAGE_ANALYZING = 897; 60 NativeAnalyzerThread(Context context)61 public NativeAnalyzerThread(Context context) { 62 mContext = context; 63 } 64 setInputPreset(int inputPreset)65 public void setInputPreset(int inputPreset) { 66 mInputPreset = inputPreset; 67 } 68 69 //JNI load 70 static { 71 try { 72 System.loadLibrary("audioloopback_jni"); 73 } catch (UnsatisfiedLinkError e) { 74 log("Error loading loopback JNI library"); 75 log("e: " + e); 76 e.printStackTrace(); 77 } 78 79 /* TODO: gracefully fail/notify if the library can't be loaded */ 80 } 81 82 /** 83 * @return native audio context 84 */ openAudio(int micSource)85 private native long openAudio(int micSource); startAudio(long audio_context)86 private native int startAudio(long audio_context); stopAudio(long audio_context)87 private native int stopAudio(long audio_context); closeAudio(long audio_context)88 private native int closeAudio(long audio_context); getError(long audio_context)89 private native int getError(long audio_context); isRecordingComplete(long audio_context)90 private native boolean isRecordingComplete(long audio_context); analyze(long audio_context)91 private native int analyze(long audio_context); getLatencyMillis(long audio_context)92 private native double getLatencyMillis(long audio_context); getConfidence(long audio_context)93 private native double getConfidence(long audio_context); isLowlatency(long audio_context)94 private native boolean isLowlatency(long audio_context); 95 getSampleRate(long audio_context)96 private native int getSampleRate(long audio_context); 97 getLatencyMillis()98 public double getLatencyMillis() { 99 return mLatencyMillis; 100 } 101 getConfidence()102 public double getConfidence() { 103 return mConfidence; 104 } 105 getSampleRate()106 public int getSampleRate() { return mSampleRate; } 107 isLowLatencyStream()108 public boolean isLowLatencyStream() { return mIsLowLatencyStream; } 109 startTest()110 public synchronized void startTest() { 111 if (mThread == null) { 112 mEnabled = true; 113 mThread = new Thread(mBackGroundTask); 114 mThread.start(); 115 } 116 } 117 stopTest(int millis)118 public synchronized void stopTest(int millis) throws InterruptedException { 119 mEnabled = false; 120 if (mThread != null) { 121 mThread.interrupt(); 122 mThread.join(millis); 123 mThread = null; 124 } 125 } 126 sendMessage(int what)127 private void sendMessage(int what) { 128 if (mMessageHandler != null) { 129 Message msg = Message.obtain(); 130 msg.what = what; 131 mMessageHandler.sendMessage(msg); 132 } 133 } 134 135 private Runnable mBackGroundTask = () -> { 136 mLatencyMillis = 0.0; 137 mConfidence = 0.0; 138 mSampleRate = 0; 139 140 boolean analysisComplete = false; 141 142 log(" Started capture test"); 143 sendMessage(NATIVE_AUDIO_THREAD_MESSAGE_REC_STARTED); 144 145 long audioContext = openAudio(mInputPreset); 146 log(String.format("audioContext = 0x%X",audioContext)); 147 148 if (audioContext == 0 ) { 149 log(" ERROR at JNI initialization"); 150 sendMessage(NATIVE_AUDIO_THREAD_MESSAGE_OPEN_ERROR); 151 } else if (mEnabled) { 152 int result = startAudio(audioContext); 153 if (result < 0) { 154 sendMessage(NATIVE_AUDIO_THREAD_MESSAGE_REC_ERROR); 155 mEnabled = false; 156 } 157 mIsLowLatencyStream = isLowlatency(audioContext); 158 159 final long timeoutMillis = mSecondsToRun * 1000; 160 final long startedAtMillis = System.currentTimeMillis(); 161 boolean timedOut = false; 162 int loopCounter = 0; 163 while (mEnabled && !timedOut) { 164 result = getError(audioContext); 165 if (result < 0) { 166 sendMessage(NATIVE_AUDIO_THREAD_MESSAGE_REC_ERROR); 167 break; 168 } else if (isRecordingComplete(audioContext)) { 169 stopAudio(audioContext); 170 171 // Analyze the recording and measure latency. 172 mThread.setPriority(Thread.MAX_PRIORITY); 173 sendMessage(NATIVE_AUDIO_THREAD_MESSAGE_ANALYZING); 174 result = analyze(audioContext); 175 if (result < 0) { 176 break; 177 } else { 178 analysisComplete = true; 179 } 180 mLatencyMillis = getLatencyMillis(audioContext); 181 mConfidence = getConfidence(audioContext); 182 mSampleRate = getSampleRate(audioContext); 183 break; 184 } else { 185 try { 186 Thread.sleep(100); 187 } catch (InterruptedException e) { 188 e.printStackTrace(); 189 } 190 } 191 long now = System.currentTimeMillis(); 192 timedOut = (now - startedAtMillis) > timeoutMillis; 193 } 194 log("latency: analyze returns " + result); 195 closeAudio(audioContext); 196 197 int what = (analysisComplete && result == 0) 198 ? NATIVE_AUDIO_THREAD_MESSAGE_REC_COMPLETE 199 : NATIVE_AUDIO_THREAD_MESSAGE_REC_COMPLETE_ERRORS; 200 sendMessage(what); 201 } 202 }; 203 setMessageHandler(Handler messageHandler)204 public void setMessageHandler(Handler messageHandler) { 205 mMessageHandler = messageHandler; 206 } 207 log(String msg)208 private static void log(String msg) { 209 Log.v("Loopback", msg); 210 } 211 212 } //end thread. 213