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