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 package org.drrickorang.loopback; 18 19 import android.app.Application; 20 import android.content.Context; 21 import android.content.pm.PackageManager; 22 import android.content.res.Configuration; 23 import android.media.MediaRecorder; 24 import android.os.Build; 25 import android.util.Log; 26 27 28 /** 29 * This class maintain global application states, so it also keeps and computes the default 30 * values of all the audio settings. 31 */ 32 33 public class LoopbackApplication extends Application { 34 private static final String TAG = "LoopbackApplication"; 35 36 // here defines all the initial setting values, some get modified in ComputeDefaults() 37 private TestSettings mSettings = new TestSettings(48000 /*samplingRate*/, 38 0 /*playerBufferSizeInBytes*/, 0 /*recorderBuffSizeInBytes*/); 39 private int mChannelIndex = -1; 40 private int mAudioThreadType = Constant.AUDIO_THREAD_TYPE_JAVA; //0:Java, 1:Native (JNI) 41 private int mMicSource = 3; //maps to MediaRecorder.AudioSource.VOICE_RECOGNITION; 42 private int mPerformanceMode = -1; // DEFAULT 43 private int mIgnoreFirstFrames = 0; 44 private int mBufferTestDurationInSeconds = 5; 45 private int mBufferTestWavePlotDurationInSeconds = 7; 46 private int mNumberOfLoadThreads = 4; 47 private boolean mCaptureSysTraceEnabled = false; 48 private boolean mCaptureBugreportEnabled = false; 49 private boolean mCaptureWavSnippetsEnabled = false; 50 private boolean mSoundLevelCalibrationEnabled = false; 51 private int mNumStateCaptures = Constant.DEFAULT_NUM_CAPTURES; 52 setDefaults()53 public void setDefaults() { 54 // Prefer SLES until buffer test is implemented for AAudio. 55 if (isSafeToUseSles()) { 56 mAudioThreadType = Constant.AUDIO_THREAD_TYPE_NATIVE_SLES; 57 } else if (isSafeToUseAAudio()) { 58 mAudioThreadType = Constant.AUDIO_THREAD_TYPE_NATIVE_AAUDIO; 59 } else { 60 mAudioThreadType = Constant.AUDIO_THREAD_TYPE_JAVA; 61 } 62 63 computeDefaults(); 64 } 65 getSamplingRate()66 int getSamplingRate() { 67 return mSettings.getSamplingRate(); 68 } 69 setSamplingRate(int samplingRate)70 void setSamplingRate(int samplingRate) { 71 mSettings.setSamplingRate(samplingRate); 72 } 73 getChannelIndex()74 int getChannelIndex() { return mChannelIndex; } 75 setChannelIndex(int channelIndex)76 void setChannelIndex(int channelIndex) { mChannelIndex = channelIndex; } 77 getAudioThreadType()78 int getAudioThreadType() { 79 return mAudioThreadType; 80 } 81 82 setAudioThreadType(int audioThreadType)83 void setAudioThreadType(int audioThreadType) { 84 if (isSafeToUseAAudio() && audioThreadType == Constant.AUDIO_THREAD_TYPE_NATIVE_AAUDIO) { 85 mAudioThreadType = Constant.AUDIO_THREAD_TYPE_NATIVE_AAUDIO; 86 } else if (isSafeToUseSles() && ( 87 audioThreadType == Constant.AUDIO_THREAD_TYPE_NATIVE_SLES || 88 audioThreadType == Constant.AUDIO_THREAD_TYPE_NATIVE_AAUDIO)) { 89 mAudioThreadType = Constant.AUDIO_THREAD_TYPE_NATIVE_SLES; 90 } else { 91 mAudioThreadType = Constant.AUDIO_THREAD_TYPE_JAVA; 92 } 93 } 94 95 getMicSource()96 int getMicSource() { 97 return mMicSource; 98 } 99 100 mapMicSource(int threadType, int source)101 int mapMicSource(int threadType, int source) { 102 int mappedSource = 0; 103 104 //experiment with remote submix 105 if (threadType == Constant.AUDIO_THREAD_TYPE_JAVA) { 106 switch (source) { 107 default: 108 case 0: //DEFAULT 109 mappedSource = MediaRecorder.AudioSource.DEFAULT; 110 break; 111 case 1: //MIC 112 mappedSource = MediaRecorder.AudioSource.MIC; 113 break; 114 case 2: //CAMCORDER 115 mappedSource = MediaRecorder.AudioSource.CAMCORDER; 116 break; 117 case 3: //VOICE_RECOGNITION 118 mappedSource = MediaRecorder.AudioSource.VOICE_RECOGNITION; 119 break; 120 case 4: //VOICE_COMMUNICATION 121 mappedSource = MediaRecorder.AudioSource.VOICE_COMMUNICATION; 122 break; 123 case 5: //REMOTE_SUBMIX (JAVA ONLY) 124 mappedSource = MediaRecorder.AudioSource.REMOTE_SUBMIX; 125 break; 126 case 6: //UNPROCESSED 127 if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) { 128 mappedSource = 9 /*MediaRecorder.AudioSource.UNPROCESSED*/; 129 } else { 130 mappedSource = MediaRecorder.AudioSource.DEFAULT; 131 } 132 break; 133 } 134 } else if (threadType == Constant.AUDIO_THREAD_TYPE_NATIVE_SLES) { 135 // FIXME taken from OpenSLES_AndroidConfiguration.h 136 switch (source) { 137 default: 138 case 0: //DEFAULT 139 mappedSource = 0x00; //SL_ANDROID_RECORDING_PRESET_NONE 140 break; 141 case 1: //MIC 142 mappedSource = 0x01; //SL_ANDROID_RECORDING_PRESET_GENERIC 143 break; 144 case 2: //CAMCORDER 145 mappedSource = 0x02; //SL_ANDROID_RECORDING_PRESET_CAMCORDER 146 break; 147 case 3: //VOICE_RECOGNITION 148 mappedSource = 0x03; //SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION 149 break; 150 case 4: //VOICE_COMMUNICATION 151 mappedSource = 0x04; //SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION 152 break; 153 case 5: //REMOTE_SUBMIX (JAVA ONLY) 154 mappedSource = 0x00; //SL_ANDROID_RECORDING_PRESET_NONE; 155 break; 156 case 6: //UNPROCESSED 157 // FIXME should use >= 158 if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) { 159 mappedSource = 0x05; //SL_ANDROID_RECORDING_PRESET_UNPROCESSED; 160 } else { 161 mappedSource = 0x00; //SL_ANDROID_RECORDING_PRESET_NONE 162 } 163 break; 164 } 165 // Doesn't matter for AUDIO_THREAD_TYPE_NATIVE_AAUDIO. 166 } 167 168 return mappedSource; 169 } 170 171 getMicSourceString(int source)172 String getMicSourceString(int source) { 173 String name = null; 174 String[] myArray = getResources().getStringArray(R.array.mic_source_array); 175 176 if (myArray != null && source >= 0 && source < myArray.length) { 177 name = myArray[source]; 178 } 179 return name; 180 } 181 setMicSource(int micSource)182 void setMicSource(int micSource) { mMicSource = micSource; } 183 mapPerformanceMode(int performanceMode)184 int mapPerformanceMode(int performanceMode) { 185 int mappedPerformanceMode = -1; 186 187 // FIXME taken from OpenSLES_AndroidConfiguration.h 188 switch (performanceMode) { 189 case 0: // NONE 190 case 1: // LATENCY 191 case 2: // LATENCY_EFFECTS 192 case 3: // POWER_SAVING 193 // FIXME should use >= 194 if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) { 195 mappedPerformanceMode = performanceMode; 196 break; 197 } 198 // fall through 199 case -1: 200 default: 201 mappedPerformanceMode = -1; 202 break; 203 } 204 205 return mappedPerformanceMode; 206 } 207 208 getPerformanceMode()209 int getPerformanceMode() { 210 return mPerformanceMode; 211 } 212 getPerformanceModeString(int performanceMode)213 String getPerformanceModeString(int performanceMode) { 214 String name = null; 215 String[] myArray = getResources().getStringArray(R.array.performance_mode_array); 216 217 if (myArray != null && performanceMode >= -1 && performanceMode < myArray.length - 1) { 218 name = myArray[performanceMode + 1]; 219 } 220 return name; 221 } 222 223 setPerformanceMode(int performanceMode)224 void setPerformanceMode(int performanceMode) { 225 mPerformanceMode = performanceMode; 226 } 227 getIgnoreFirstFrames()228 int getIgnoreFirstFrames() { 229 return mIgnoreFirstFrames; 230 } 231 setIgnoreFirstFrames(int ignoreFirstFrames)232 void setIgnoreFirstFrames(int ignoreFirstFrames) { 233 mIgnoreFirstFrames = ignoreFirstFrames; 234 } 235 getPlayerBufferSizeInBytes()236 int getPlayerBufferSizeInBytes() { 237 return mSettings.getPlayerBufferSizeInBytes(); 238 } 239 setPlayerBufferSizeInBytes(int playerBufferSizeInBytes)240 void setPlayerBufferSizeInBytes(int playerBufferSizeInBytes) { 241 mSettings.setPlayerBufferSizeInBytes(playerBufferSizeInBytes); 242 } 243 244 getRecorderBufferSizeInBytes()245 int getRecorderBufferSizeInBytes() { 246 return mSettings.getRecorderBufferSizeInBytes(); 247 } 248 249 setRecorderBufferSizeInBytes(int recorderBufferSizeInBytes)250 void setRecorderBufferSizeInBytes(int recorderBufferSizeInBytes) { 251 mSettings.setRecorderBufferSizeInBytes(recorderBufferSizeInBytes); 252 } 253 254 getBufferTestDuration()255 int getBufferTestDuration() { 256 return mBufferTestDurationInSeconds; 257 } 258 259 setBufferTestDuration(int bufferTestDurationInSeconds)260 void setBufferTestDuration(int bufferTestDurationInSeconds) { 261 mBufferTestDurationInSeconds = Utilities.clamp(bufferTestDurationInSeconds, 262 Constant.BUFFER_TEST_DURATION_SECONDS_MIN, 263 Constant.BUFFER_TEST_DURATION_SECONDS_MAX); 264 } 265 266 getBufferTestWavePlotDuration()267 int getBufferTestWavePlotDuration() { 268 return mBufferTestWavePlotDurationInSeconds; 269 } 270 271 setBufferTestWavePlotDuration(int bufferTestWavePlotDurationInSeconds)272 void setBufferTestWavePlotDuration(int bufferTestWavePlotDurationInSeconds) { 273 mBufferTestWavePlotDurationInSeconds = Utilities.clamp(bufferTestWavePlotDurationInSeconds, 274 Constant.BUFFER_TEST_WAVE_PLOT_DURATION_SECONDS_MIN, 275 Constant.BUFFER_TEST_WAVE_PLOT_DURATION_SECONDS_MAX); 276 } 277 getNumberOfLoadThreads()278 int getNumberOfLoadThreads() { 279 return mNumberOfLoadThreads; 280 } 281 setNumberOfLoadThreads(int numberOfLoadThreads)282 void setNumberOfLoadThreads(int numberOfLoadThreads) { 283 mNumberOfLoadThreads = Utilities.clamp(numberOfLoadThreads, Constant.MIN_NUM_LOAD_THREADS, 284 Constant.MAX_NUM_LOAD_THREADS); 285 } 286 setNumberOfCaptures(int num)287 public void setNumberOfCaptures (int num) { 288 mNumStateCaptures = Utilities.clamp(num, Constant.MIN_NUM_CAPTURES, 289 Constant.MAX_NUM_CAPTURES); 290 } 291 setCaptureSysTraceEnabled(boolean enabled)292 public void setCaptureSysTraceEnabled (boolean enabled) { 293 mCaptureSysTraceEnabled = enabled; 294 } 295 setCaptureBugreportEnabled(boolean enabled)296 public void setCaptureBugreportEnabled(boolean enabled) { 297 mCaptureBugreportEnabled = enabled; 298 } 299 setCaptureWavsEnabled(boolean enabled)300 public void setCaptureWavsEnabled(boolean enabled) { 301 mCaptureWavSnippetsEnabled = enabled; 302 } 303 setSoundLevelCalibrationEnabled(boolean enabled)304 public void setSoundLevelCalibrationEnabled(boolean enabled) { 305 mSoundLevelCalibrationEnabled = enabled; 306 } 307 isCaptureEnabled()308 public boolean isCaptureEnabled() { 309 return isCaptureSysTraceEnabled() || isCaptureBugreportEnabled(); 310 } 311 isCaptureSysTraceEnabled()312 public boolean isCaptureSysTraceEnabled() { 313 return mCaptureSysTraceEnabled; 314 } 315 isCaptureBugreportEnabled()316 public boolean isCaptureBugreportEnabled() { 317 return mCaptureBugreportEnabled; 318 } 319 isSoundLevelCalibrationEnabled()320 public boolean isSoundLevelCalibrationEnabled() { 321 return mSoundLevelCalibrationEnabled; 322 } 323 getNumStateCaptures()324 public int getNumStateCaptures() { 325 return mNumStateCaptures; 326 } 327 isCaptureWavSnippetsEnabled()328 public boolean isCaptureWavSnippetsEnabled() { 329 return mCaptureWavSnippetsEnabled; 330 } 331 332 333 /** Compute Default audio settings. */ computeDefaults()334 public void computeDefaults() { 335 if (mAudioThreadType == Constant.AUDIO_THREAD_TYPE_NATIVE_SLES || 336 mAudioThreadType == Constant.AUDIO_THREAD_TYPE_NATIVE_AAUDIO) { 337 mSettings = NativeAudioThread.computeDefaultSettings( 338 this, mAudioThreadType, mPerformanceMode); 339 } else { 340 mSettings = LoopbackAudioThread.computeDefaultSettings(); 341 } 342 } 343 344 getSystemInfo()345 String getSystemInfo() { 346 String info = null; 347 try { 348 Context context = getApplicationContext(); 349 android.content.pm.PackageInfo packageInfo = context.getPackageManager().getPackageInfo( 350 context.getPackageName(), 0); 351 int versionCode = packageInfo.versionCode; 352 String versionName = packageInfo.versionName; 353 info = "App ver. " + versionCode + "." + versionName + " | " + Build.MODEL + " | " + 354 Build.FINGERPRINT; 355 } catch (PackageManager.NameNotFoundException e) { 356 e.printStackTrace(); 357 } 358 359 return info; 360 } 361 362 363 /** Check if it's safe to use OpenSL ES. */ isSafeToUseSles()364 static boolean isSafeToUseSles() { 365 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD; 366 } 367 368 /** Check if it's safe to use AAudio. */ isSafeToUseAAudio()369 static boolean isSafeToUseAAudio() { 370 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O; 371 } 372 373 374 /* 375 @Override 376 public void onConfigurationChanged(Configuration newConfig) { 377 super.onConfigurationChanged(newConfig); 378 } 379 */ 380 381 382 @Override onCreate()383 public void onCreate() { 384 super.onCreate(); 385 386 setDefaults(); 387 } 388 389 390 /* 391 @Override 392 public void onLowMemory() { 393 super.onLowMemory(); 394 } 395 */ 396 397 398 /* 399 @Override 400 public void onTerminate() { 401 super.onTerminate(); 402 } 403 */ 404 405 log(String msg)406 private static void log(String msg) { 407 Log.v(TAG, msg); 408 } 409 410 } 411