1 /* 2 * Copyright 2018 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 com.google.sample.oboe.manualtest; 18 19 import android.app.Activity; 20 import android.os.Bundle; 21 import android.view.View; 22 import android.widget.Button; 23 import android.widget.SeekBar; 24 import android.widget.TextView; 25 26 import java.io.IOException; 27 28 /** 29 * Activity to capture audio and then send a delayed copy to output. 30 * There is a fader for setting delay time 31 */ 32 public class EchoActivity extends TestInputActivity { 33 34 AudioOutputTester mAudioOutTester; 35 36 protected TextView mTextDelayTime; 37 protected SeekBar mFaderDelayTime; 38 protected ExponentialTaper mTaperDelayTime; 39 private static final double MIN_DELAY_TIME_SECONDS = 0.0; 40 private static final double MAX_DELAY_TIME_SECONDS = 3.0; 41 private double mDelayTime; 42 private Button mStartButton; 43 private Button mStopButton; 44 private TextView mStatusTextView; 45 46 private ColdStartSniffer mNativeSniffer = new ColdStartSniffer(this); 47 48 protected static final int MAX_DELAY_TIME_PROGRESS = 1000; 49 50 private SeekBar.OnSeekBarChangeListener mDelayListener = new SeekBar.OnSeekBarChangeListener() { 51 @Override 52 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 53 setDelayTimeByPosition(progress); 54 } 55 56 @Override 57 public void onStartTrackingTouch(SeekBar seekBar) { 58 } 59 60 @Override 61 public void onStopTrackingTouch(SeekBar seekBar) { 62 } 63 }; 64 65 // Periodically query for cold start latency from the native code until it is ready. 66 protected class ColdStartSniffer extends NativeSniffer { 67 68 private int stableCallCount = 0; 69 private static final int STABLE_CALLS_NEEDED = 20; 70 private int mInputLatency; 71 private int mOutputLatency; 72 ColdStartSniffer(Activity activity)73 public ColdStartSniffer(Activity activity) { 74 super(activity); 75 } 76 77 @Override startSniffer()78 public void startSniffer() { 79 stableCallCount = 0; 80 mInputLatency = -1; 81 mOutputLatency = -1; 82 super.startSniffer(); 83 } 84 run()85 public void run() { 86 mInputLatency = getColdStartInputMillis(); 87 mOutputLatency = getColdStartOutputMillis(); 88 updateStatusText(); 89 if (!isComplete()) { 90 reschedule(); 91 } 92 } 93 isComplete()94 private boolean isComplete() { 95 if (mInputLatency > 0 && mOutputLatency > 0) { 96 stableCallCount++; 97 } 98 return stableCallCount > STABLE_CALLS_NEEDED; 99 } 100 getCurrentStatusReport()101 private String getCurrentStatusReport() { 102 StringBuffer message = new StringBuffer(); 103 message.append("cold.start.input.msec = " + 104 ((mInputLatency > 0) 105 ? mInputLatency 106 : "?") 107 + "\n"); 108 message.append("cold.start.output.msec = " + 109 ((mOutputLatency > 0) 110 ? mOutputLatency 111 : "?") 112 + "\n"); 113 message.append("stable.call.count = " + stableCallCount + "\n"); 114 return message.toString(); 115 } 116 117 @Override getShortReport()118 public String getShortReport() { 119 return getCurrentStatusReport(); 120 } 121 122 @Override updateStatusText()123 public void updateStatusText() { 124 String message = getCurrentStatusReport(); 125 mStatusTextView.setText(message); 126 } 127 128 } 129 getColdStartInputMillis()130 private native int getColdStartInputMillis(); getColdStartOutputMillis()131 private native int getColdStartOutputMillis(); 132 133 @Override inflateActivity()134 protected void inflateActivity() { 135 setContentView(R.layout.activity_echo); 136 } 137 138 @Override onCreate(Bundle savedInstanceState)139 protected void onCreate(Bundle savedInstanceState) { 140 super.onCreate(savedInstanceState); 141 142 updateEnabledWidgets(); 143 144 mAudioOutTester = addAudioOutputTester(); 145 146 mStartButton = (Button) findViewById(R.id.button_start_echo); 147 mStopButton = (Button) findViewById(R.id.button_stop_echo); 148 mStopButton.setEnabled(false); 149 150 mStatusTextView = (TextView) findViewById(R.id.text_status); 151 152 mTextDelayTime = (TextView) findViewById(R.id.text_delay_time); 153 mFaderDelayTime = (SeekBar) findViewById(R.id.fader_delay_time); 154 mFaderDelayTime.setOnSeekBarChangeListener(mDelayListener); 155 mTaperDelayTime = new ExponentialTaper( 156 MIN_DELAY_TIME_SECONDS, 157 MAX_DELAY_TIME_SECONDS, 158 100.0); 159 mFaderDelayTime.setProgress(MAX_DELAY_TIME_PROGRESS / 2); 160 161 hideSettingsViews(); 162 } 163 setDelayTimeByPosition(int progress)164 private void setDelayTimeByPosition(int progress) { 165 mDelayTime = mTaperDelayTime.linearToExponential( 166 ((double)progress)/MAX_DELAY_TIME_PROGRESS); 167 setDelayTime(mDelayTime); 168 mTextDelayTime.setText("DelayLine: " + (int)(mDelayTime * 1000) + " (msec)"); 169 } 170 setDelayTime(double delayTimeSeconds)171 private native void setDelayTime(double delayTimeSeconds); 172 getActivityType()173 int getActivityType() { 174 return ACTIVITY_ECHO; 175 } 176 177 178 @Override resetConfiguration()179 protected void resetConfiguration() { 180 super.resetConfiguration(); 181 mAudioOutTester.reset(); 182 } 183 onStartEcho(View view)184 public void onStartEcho(View view) { 185 try { 186 openAudio(); 187 startAudio(); 188 setDelayTime(mDelayTime); 189 mStartButton.setEnabled(false); 190 mStopButton.setEnabled(true); 191 keepScreenOn(true); 192 mNativeSniffer.startSniffer(); 193 } catch (IOException e) { 194 showErrorToast(e.getMessage()); 195 } 196 } 197 onStopEcho(View view)198 public void onStopEcho(View view) { 199 mNativeSniffer.stopSniffer(); 200 stopAudio(); 201 closeAudio(); 202 mStartButton.setEnabled(true); 203 mStopButton.setEnabled(false); 204 keepScreenOn(false); 205 } 206 207 @Override isOutput()208 boolean isOutput() { 209 return false; 210 } 211 212 @Override setupEffects(int sessionId)213 public void setupEffects(int sessionId) { 214 } 215 } 216