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