/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.drrickorang.loopback; import android.app.Application; import android.content.Context; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.media.AudioFormat; import android.media.AudioManager; import android.media.AudioRecord; import android.media.AudioTrack; import android.media.MediaRecorder; import android.os.Build; import android.util.Log; /** * This class maintain global application states, so it also keeps and computes the default * values of all the audio settings. */ public class LoopbackApplication extends Application { private static final String TAG = "LoopbackApplication"; // here defines all the initial setting values, some get modified in ComputeDefaults() private int mSamplingRate = 48000; private int mChannelIndex = -1; private int mPlayerBufferSizeInBytes = 0; // for both native and java private int mRecorderBuffSizeInBytes = 0; // for both native and java private int mAudioThreadType = Constant.AUDIO_THREAD_TYPE_JAVA; //0:Java, 1:Native (JNI) private int mMicSource = 3; //maps to MediaRecorder.AudioSource.VOICE_RECOGNITION; private int mBufferTestDurationInSeconds = 5; private int mBufferTestWavePlotDurationInSeconds = 7; private int mNumberOfLoadThreads = 4; private boolean mCaptureSysTraceEnabled = false; private boolean mCaptureWavSnippetsEnabled = false; private int mNumStateCaptures = Constant.DEFAULT_NUM_CAPTURES; public void setDefaults() { if (isSafeToUseSles()) { mAudioThreadType = Constant.AUDIO_THREAD_TYPE_NATIVE; } else { mAudioThreadType = Constant.AUDIO_THREAD_TYPE_JAVA; } computeDefaults(); } int getSamplingRate() { return mSamplingRate; } void setSamplingRate(int samplingRate) { mSamplingRate = clamp(samplingRate, Constant.SAMPLING_RATE_MIN, Constant.SAMPLING_RATE_MAX); } int getChannelIndex() { return mChannelIndex; } void setChannelIndex(int channelIndex) { mChannelIndex = channelIndex; } int getAudioThreadType() { return mAudioThreadType; } void setAudioThreadType(int audioThreadType) { if (isSafeToUseSles() && audioThreadType != Constant.AUDIO_THREAD_TYPE_JAVA) { //safe to use native and Java thread not selected mAudioThreadType = Constant.AUDIO_THREAD_TYPE_NATIVE; } else { mAudioThreadType = Constant.AUDIO_THREAD_TYPE_JAVA; } } int getMicSource() { return mMicSource; } int mapMicSource(int threadType, int source) { int mappedSource = 0; //experiment with remote submix if (threadType == Constant.AUDIO_THREAD_TYPE_JAVA) { switch (source) { default: case 0: //DEFAULT mappedSource = MediaRecorder.AudioSource.DEFAULT; break; case 1: //MIC mappedSource = MediaRecorder.AudioSource.MIC; break; case 2: //CAMCORDER mappedSource = MediaRecorder.AudioSource.CAMCORDER; break; case 3: //VOICE_RECOGNITION mappedSource = MediaRecorder.AudioSource.VOICE_RECOGNITION; break; case 4: //VOICE_COMMUNICATION mappedSource = MediaRecorder.AudioSource.VOICE_COMMUNICATION; break; case 5: //REMOTE_SUBMIX (JAVA ONLY) mappedSource = MediaRecorder.AudioSource.REMOTE_SUBMIX; break; } } else if (threadType == Constant.AUDIO_THREAD_TYPE_NATIVE) { //taken form OpenSLES_AndroidConfiguration.h switch (source) { default: case 0: //DEFAULT mappedSource = 0x00; //SL_ANDROID_RECORDING_PRESET_NONE break; case 1: //MIC mappedSource = 0x01; //SL_ANDROID_RECORDING_PRESET_GENERIC break; case 2: //CAMCORDER mappedSource = 0x02; //SL_ANDROID_RECORDING_PRESET_CAMCORDER break; case 3: //VOICE_RECOGNITION mappedSource = 0x03; //SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION break; case 4: //VOICE_COMMUNICATION mappedSource = 0x04; //SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION break; } } return mappedSource; } String getMicSourceString(int source) { String name = null; String[] myArray = getResources().getStringArray(R.array.mic_source_array); if (myArray != null && source >= 0 && source < myArray.length) { name = myArray[source]; } return name; } void setMicSource(int micSource) { mMicSource = micSource; } int getPlayerBufferSizeInBytes() { return mPlayerBufferSizeInBytes; } void setPlayerBufferSizeInBytes(int playerBufferSizeInBytes) { mPlayerBufferSizeInBytes = clamp(playerBufferSizeInBytes, Constant.PLAYER_BUFFER_FRAMES_MIN, Constant.PLAYER_BUFFER_FRAMES_MAX); } int getRecorderBufferSizeInBytes() { return mRecorderBuffSizeInBytes; } void setRecorderBufferSizeInBytes(int recorderBufferSizeInBytes) { mRecorderBuffSizeInBytes = clamp(recorderBufferSizeInBytes, Constant.RECORDER_BUFFER_FRAMES_MIN, Constant.RECORDER_BUFFER_FRAMES_MAX); } int getBufferTestDuration() { return mBufferTestDurationInSeconds; } void setBufferTestDuration(int bufferTestDurationInSeconds) { mBufferTestDurationInSeconds = clamp(bufferTestDurationInSeconds, Constant.BUFFER_TEST_DURATION_SECONDS_MIN, Constant.BUFFER_TEST_DURATION_SECONDS_MAX); } int getBufferTestWavePlotDuration() { return mBufferTestWavePlotDurationInSeconds; } void setBufferTestWavePlotDuration(int bufferTestWavePlotDurationInSeconds) { mBufferTestWavePlotDurationInSeconds = clamp(bufferTestWavePlotDurationInSeconds, Constant.BUFFER_TEST_WAVE_PLOT_DURATION_SECONDS_MIN, Constant.BUFFER_TEST_WAVE_PLOT_DURATION_SECONDS_MAX); } int getNumberOfLoadThreads() { return mNumberOfLoadThreads; } void setNumberOfLoadThreads(int numberOfLoadThreads) { mNumberOfLoadThreads = clamp(numberOfLoadThreads, Constant.MIN_NUM_LOAD_THREADS, Constant.MAX_NUM_LOAD_THREADS); } public void setNumberOfCaptures (int num){ mNumStateCaptures = clamp(num, Constant.MIN_NUM_CAPTURES, Constant.MAX_NUM_CAPTURES); } public void setCaptureSysTraceEnabled (boolean enabled){ mCaptureSysTraceEnabled = enabled; } public void setCaptureWavsEnabled (boolean enabled){ mCaptureWavSnippetsEnabled = enabled; } public boolean isCaptureSysTraceEnabled () { return mCaptureSysTraceEnabled; } public int getNumStateCaptures() { return mNumStateCaptures; } public boolean isCaptureWavSnippetsEnabled() { return mCaptureWavSnippetsEnabled; } /** * Returns value if value is within inclusive bounds min through max * otherwise returns min or max according to if value is less than or greater than the range */ private int clamp(int value, int min, int max) { if (max < min) throw new UnsupportedOperationException("min must be <= max"); if (value < min) return min; else if (value > max) return max; else return value; } /** Compute Default audio settings. */ public void computeDefaults() { int samplingRate = AudioTrack.getNativeOutputSampleRate(AudioManager.STREAM_MUSIC); setSamplingRate(samplingRate); if (mAudioThreadType == Constant.AUDIO_THREAD_TYPE_NATIVE) { int minBufferSizeInFrames; if (isSafeToUseGetProperty()) { AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE); String value = am.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER); minBufferSizeInFrames = Integer.parseInt(value); } else { minBufferSizeInFrames = 1024; log("On button test micSource Name: "); } int minBufferSizeInBytes = Constant.BYTES_PER_FRAME * minBufferSizeInFrames; setPlayerBufferSizeInBytes(minBufferSizeInBytes); setRecorderBufferSizeInBytes(minBufferSizeInBytes); } else { int minPlayerBufferSizeInBytes = AudioTrack.getMinBufferSize(samplingRate, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT); setPlayerBufferSizeInBytes(minPlayerBufferSizeInBytes); int minRecorderBufferSizeInBytes = AudioRecord.getMinBufferSize(samplingRate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT); setRecorderBufferSizeInBytes(minRecorderBufferSizeInBytes); } } String getSystemInfo() { String info = null; try { int versionCode = getApplicationContext().getPackageManager().getPackageInfo( getApplicationContext().getPackageName(), 0).versionCode; String versionName = getApplicationContext().getPackageManager().getPackageInfo( getApplicationContext().getPackageName(), 0).versionName; info = "App ver. " + versionCode + "." + versionName + " | " + Build.MODEL + " | " + Build.FINGERPRINT; } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } return info; } /** Check if it's safe to use Open SLES. */ boolean isSafeToUseSles() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD; } /** Check if it's safe to use getProperty(). */ boolean isSafeToUseGetProperty() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1; } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); } @Override public void onCreate() { super.onCreate(); setDefaults(); } @Override public void onLowMemory() { super.onLowMemory(); } @Override public void onTerminate() { super.onTerminate(); } private static void log(String msg) { Log.v(TAG, msg); } }