• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 import android.os.Handler;
22 import android.os.Message;
23 import android.util.Log;
24 
25 /**
26  * A thread that runs a native audio loopback analyzer.
27  */
28 public class NativeAnalyzerThread {
29     private static final String TAG = "NativeAnalyzerThread";
30 
31     private Context mContext;
32 
33     private final int mSecondsToRun = 5;
34     private Handler mMessageHandler;
35     private Thread mThread;
36     private volatile boolean mEnabled = false;
37     private volatile double mLatencyMillis = 0.0;
38     private volatile double mConfidence = 0.0;
39     private volatile int mSampleRate = 0;
40     private volatile boolean mIsLowLatencyStream = false;
41     private volatile boolean mHas24BitHardwareSupport = false;
42 
43     private int mInputPreset = 0;
44 
45     private int mInputDeviceId;
46     private int mOutputDeviceId;
47 
48     static final int NATIVE_AUDIO_THREAD_MESSAGE_REC_STARTED = 892;
49     static final int NATIVE_AUDIO_THREAD_MESSAGE_OPEN_ERROR = 893;
50     static final int NATIVE_AUDIO_THREAD_MESSAGE_REC_ERROR = 894;
51     static final int NATIVE_AUDIO_THREAD_MESSAGE_REC_COMPLETE = 895;
52     static final int NATIVE_AUDIO_THREAD_MESSAGE_REC_COMPLETE_ERRORS = 896;
53     static final int NATIVE_AUDIO_THREAD_MESSAGE_ANALYZING = 897;
54 
NativeAnalyzerThread(Context context)55     public NativeAnalyzerThread(Context context) {
56         mContext = context;
57     }
58 
setInputPreset(int inputPreset)59     public void setInputPreset(int inputPreset) {
60         mInputPreset = inputPreset;
61     }
62 
63     //JNI load
64     static {
65         try {
66             System.loadLibrary("audioloopback_jni");
67         } catch (UnsatisfiedLinkError e) {
68             log("Error loading loopback JNI library");
69             log("e: " + e);
70             e.printStackTrace();
71         }
72 
73         /* TODO: gracefully fail/notify if the library can't be loaded */
74     }
75 
76     /**
77      * @return native audio context
78      */
openAudio(int inputDeviceID, int outputDeviceId)79     private native long openAudio(int inputDeviceID, int outputDeviceId);
startAudio(long audioContext)80     private native int startAudio(long audioContext);
stopAudio(long audioContext)81     private native int stopAudio(long audioContext);
closeAudio(long audioContext)82     private native int closeAudio(long audioContext);
getError(long audioContext)83     private native int getError(long audioContext);
isRecordingComplete(long audioContext)84     private native boolean isRecordingComplete(long audioContext);
analyze(long audioContext)85     private native int analyze(long audioContext);
getLatencyMillis(long audioContext)86     private native double getLatencyMillis(long audioContext);
getConfidence(long audioContext)87     private native double getConfidence(long audioContext);
isLowlatency(long audioContext)88     private native boolean isLowlatency(long audioContext);
has24BitHardwareSupport(long audioContext)89     private native boolean has24BitHardwareSupport(long audioContext);
90 
getSampleRate(long audio_context)91     private native int getSampleRate(long audio_context);
92 
getLatencyMillis()93     public double getLatencyMillis() {
94         return mLatencyMillis;
95     }
96 
getConfidence()97     public double getConfidence() {
98         return mConfidence;
99     }
100 
getSampleRate()101     public int getSampleRate() { return mSampleRate; }
102 
isLowLatencyStream()103     public boolean isLowLatencyStream() { return mIsLowLatencyStream; }
104 
105     /**
106      * @return whether 24 bit data formats are supported for the hardware
107      */
has24BitHardwareSupport()108     public boolean has24BitHardwareSupport() {
109         return mHas24BitHardwareSupport;
110     }
111 
startTest(int inputDeviceId, int outputDeviceId)112     public synchronized void startTest(int inputDeviceId, int outputDeviceId) {
113         mInputDeviceId = inputDeviceId;
114         mOutputDeviceId = outputDeviceId;
115 
116         if (mThread == null) {
117             mEnabled = true;
118             mThread = new Thread(mBackGroundTask);
119             mThread.start();
120         }
121     }
122 
stopTest(int millis)123     public synchronized void stopTest(int millis) throws InterruptedException {
124         mEnabled = false;
125         if (mThread != null) {
126             mThread.interrupt();
127             mThread.join(millis);
128             mThread = null;
129         }
130     }
131 
sendMessage(int what)132     private void sendMessage(int what) {
133         if (mMessageHandler != null) {
134             Message msg = Message.obtain();
135             msg.what = what;
136             mMessageHandler.sendMessage(msg);
137         }
138     }
139 
140     private Runnable mBackGroundTask = () -> {
141         mLatencyMillis = 0.0;
142         mConfidence = 0.0;
143         mSampleRate = 0;
144 
145         boolean analysisComplete = false;
146 
147         log(" Started capture test");
148         sendMessage(NATIVE_AUDIO_THREAD_MESSAGE_REC_STARTED);
149 
150         //TODO - route parameters
151         long audioContext = openAudio(mInputDeviceId, mOutputDeviceId);
152         log(String.format("audioContext = 0x%X",audioContext));
153 
154         if (audioContext == 0 ) {
155             log(" ERROR at JNI initialization");
156             sendMessage(NATIVE_AUDIO_THREAD_MESSAGE_OPEN_ERROR);
157         }  else if (mEnabled) {
158             int result = startAudio(audioContext);
159             if (result < 0) {
160                 sendMessage(NATIVE_AUDIO_THREAD_MESSAGE_REC_ERROR);
161                 mEnabled = false;
162             }
163             mIsLowLatencyStream = isLowlatency(audioContext);
164             mHas24BitHardwareSupport = has24BitHardwareSupport(audioContext);
165 
166             final long timeoutMillis = mSecondsToRun * 1000;
167             final long startedAtMillis = System.currentTimeMillis();
168             boolean timedOut = false;
169             int loopCounter = 0;
170             while (mEnabled && !timedOut) {
171                 result = getError(audioContext);
172                 if (result < 0) {
173                     sendMessage(NATIVE_AUDIO_THREAD_MESSAGE_REC_ERROR);
174                     break;
175                 } else if (isRecordingComplete(audioContext)) {
176                     stopAudio(audioContext);
177 
178                     // Analyze the recording and measure latency.
179                     mThread.setPriority(Thread.MAX_PRIORITY);
180                     sendMessage(NATIVE_AUDIO_THREAD_MESSAGE_ANALYZING);
181                     result = analyze(audioContext);
182                     if (result < 0) {
183                         break;
184                     } else {
185                         analysisComplete = true;
186                     }
187                     mLatencyMillis = getLatencyMillis(audioContext);
188                     mConfidence = getConfidence(audioContext);
189                     mSampleRate = getSampleRate(audioContext);
190                     break;
191                 } else {
192                     try {
193                         Thread.sleep(100);
194                     } catch (InterruptedException e) {
195                         e.printStackTrace();
196                     }
197                 }
198                 long now = System.currentTimeMillis();
199                 timedOut = (now - startedAtMillis) > timeoutMillis;
200             }
201             log("latency: analyze returns " + result);
202             closeAudio(audioContext);
203 
204             int what = (analysisComplete && result == 0)
205                     ? NATIVE_AUDIO_THREAD_MESSAGE_REC_COMPLETE
206                     : NATIVE_AUDIO_THREAD_MESSAGE_REC_COMPLETE_ERRORS;
207             sendMessage(what);
208         }
209     };
210 
setMessageHandler(Handler messageHandler)211     public void setMessageHandler(Handler messageHandler) {
212         mMessageHandler = messageHandler;
213     }
214 
log(String msg)215     private static void log(String msg) {
216         Log.v("Loopback", msg);
217     }
218 
219 }  //end thread.
220