Home | History | Annotate | Download | only in loopback
      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