1 /* 2 * Copyright (C) 2009 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.android.effectstest; 18 19 import android.app.Activity; 20 import android.media.audiofx.Visualizer; 21 import android.os.Bundle; 22 import android.os.Handler; 23 import android.os.Looper; 24 import android.os.Message; 25 import android.util.Log; 26 import android.view.KeyEvent; 27 import android.view.View; 28 import android.view.View.OnClickListener; 29 import android.widget.Button; 30 import android.widget.CompoundButton; 31 import android.widget.CompoundButton.OnCheckedChangeListener; 32 import android.widget.EditText; 33 import android.widget.TextView; 34 import android.widget.ToggleButton; 35 36 import java.util.HashMap; 37 38 public class VisualizerTest extends Activity implements OnCheckedChangeListener { 39 40 private final static String TAG = "Visualizer Test"; 41 42 private VisualizerInstance mVisualizer; 43 ToggleButton mMultithreadedButton; 44 ToggleButton mOnOffButton; 45 ToggleButton mReleaseButton; 46 boolean mUseMTInstance; 47 boolean mEnabled; 48 EditText mSessionText; 49 static int sSession = 0; 50 ToggleButton mCallbackButton; 51 boolean mCallbackOn; 52 private static HashMap<Integer, VisualizerInstance> sInstances = 53 new HashMap<Integer, VisualizerInstance>(10); 54 private Handler mUiHandler; 55 VisualizerTest()56 public VisualizerTest() { 57 Log.d(TAG, "contructor"); 58 mUiHandler = new UiHandler(Looper.getMainLooper()); 59 } 60 61 @Override onCreate(Bundle icicle)62 public void onCreate(Bundle icicle) { 63 super.onCreate(icicle); 64 65 TextView textView; 66 67 setContentView(R.layout.visualizertest); 68 69 mSessionText = findViewById(R.id.sessionEdit); 70 mSessionText.setOnKeyListener(mSessionKeyListener); 71 mSessionText.setText(Integer.toString(sSession)); 72 73 mMultithreadedButton = (ToggleButton) findViewById(R.id.visuMultithreadedOnOff); 74 mReleaseButton = (ToggleButton) findViewById(R.id.visuReleaseButton); 75 mOnOffButton = (ToggleButton) findViewById(R.id.visualizerOnOff); 76 mCallbackButton = (ToggleButton) findViewById(R.id.visuCallbackOnOff); 77 mCallbackOn = false; 78 mCallbackButton.setChecked(mCallbackOn); 79 80 final Button hammerReleaseTest = (Button) findViewById(R.id.hammer_on_release_bug); 81 hammerReleaseTest.setEnabled(false); 82 83 mMultithreadedButton.setOnCheckedChangeListener(this); 84 if (getEffect(sSession) != null) { 85 mReleaseButton.setOnCheckedChangeListener(this); 86 mOnOffButton.setOnCheckedChangeListener(this); 87 mCallbackButton.setOnCheckedChangeListener(this); 88 89 hammerReleaseTest.setEnabled(true); 90 hammerReleaseTest.setOnClickListener(new OnClickListener() { 91 @Override 92 public void onClick(View v) { 93 runHammerReleaseTest(hammerReleaseTest); 94 } 95 }); 96 } 97 } 98 99 public static final int MSG_DISPLAY_WAVEFORM_VAL = 0; 100 public static final int MSG_DISPLAY_FFT_VAL = 1; 101 102 private class UiHandler extends Handler { UiHandler(Looper looper)103 UiHandler(Looper looper) { 104 super(looper); 105 } 106 107 @Override handleMessage(Message msg)108 public void handleMessage(Message msg) { 109 switch (msg.what) { 110 case MSG_DISPLAY_WAVEFORM_VAL: 111 case MSG_DISPLAY_FFT_VAL: 112 int[] minMaxCenter = (int[]) msg.obj; 113 boolean waveform = msg.what == MSG_DISPLAY_WAVEFORM_VAL; 114 displayVal(waveform ? R.id.waveformMin : R.id.fftMin, minMaxCenter[0]); 115 displayVal(waveform ? R.id.waveformMax : R.id.fftMax, minMaxCenter[1]); 116 displayVal(waveform ? R.id.waveformCenter : R.id.fftCenter, minMaxCenter[2]); 117 break; 118 } 119 } 120 } 121 122 private View.OnKeyListener mSessionKeyListener = new View.OnKeyListener() { 123 public boolean onKey(View v, int keyCode, KeyEvent event) { 124 if (event.getAction() == KeyEvent.ACTION_DOWN) { 125 switch (keyCode) { 126 case KeyEvent.KEYCODE_DPAD_CENTER: 127 case KeyEvent.KEYCODE_ENTER: 128 try { 129 sSession = Integer.parseInt(mSessionText.getText().toString()); 130 getEffect(sSession); 131 } catch (NumberFormatException e) { 132 Log.d(TAG, "Invalid session #: "+mSessionText.getText().toString()); 133 } 134 135 return true; 136 } 137 } 138 return false; 139 } 140 }; 141 142 // OnCheckedChangeListener 143 @Override onCheckedChanged(CompoundButton buttonView, boolean isChecked)144 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 145 if (buttonView.getId() == R.id.visuMultithreadedOnOff) { 146 mUseMTInstance = isChecked; 147 Log.d(TAG, "Multi-threaded client: " + (isChecked ? "enabled" : "disabled")); 148 } 149 if (buttonView.getId() == R.id.visualizerOnOff) { 150 if (mVisualizer != null) { 151 mEnabled = isChecked; 152 mCallbackButton.setEnabled(!mEnabled); 153 if (mCallbackOn && mEnabled) { 154 mVisualizer.enableDataCaptureListener(true); 155 } 156 mVisualizer.setEnabled(mEnabled); 157 if (mCallbackOn) { 158 if (!mEnabled) { 159 mVisualizer.enableDataCaptureListener(false); 160 } 161 } else { 162 mVisualizer.startStopCapture(isChecked); 163 } 164 } 165 } 166 if (buttonView.getId() == R.id.visuReleaseButton) { 167 if (isChecked) { 168 if (mVisualizer == null) { 169 getEffect(sSession); 170 } 171 } else { 172 if (mVisualizer != null) { 173 putEffect(sSession); 174 } 175 } 176 } 177 if (buttonView.getId() == R.id.visuCallbackOnOff) { 178 mCallbackOn = isChecked; 179 } 180 } 181 displayVal(int viewId, int val)182 private void displayVal(int viewId, int val) { 183 TextView textView = (TextView)findViewById(viewId); 184 String text = Integer.toString(val); 185 textView.setText(text); 186 } 187 188 getEffect(int session)189 private VisualizerInstance getEffect(int session) { 190 synchronized (sInstances) { 191 if (sInstances.containsKey(session)) { 192 mVisualizer = sInstances.get(session); 193 } else { 194 try { 195 mVisualizer = mUseMTInstance 196 ? new VisualizerInstanceMT(session, mUiHandler, 0 /*extraThreadCount*/) 197 : new VisualizerInstanceSync(session, mUiHandler); 198 } catch (RuntimeException e) { 199 throw e; 200 } 201 sInstances.put(session, mVisualizer); 202 } 203 } 204 mReleaseButton.setEnabled(false); 205 mOnOffButton.setEnabled(false); 206 if (mVisualizer != null) { 207 mReleaseButton.setChecked(true); 208 mReleaseButton.setEnabled(true); 209 210 mEnabled = mVisualizer.getEnabled(); 211 mOnOffButton.setChecked(mEnabled); 212 mOnOffButton.setEnabled(true); 213 214 mCallbackButton.setEnabled(!mEnabled); 215 } 216 return mVisualizer; 217 } 218 putEffect(int session)219 private void putEffect(int session) { 220 mOnOffButton.setChecked(false); 221 mOnOffButton.setEnabled(false); 222 synchronized (sInstances) { 223 if (mVisualizer != null) { 224 mVisualizer.release(); 225 sInstances.remove(session); 226 mVisualizer = null; 227 } 228 } 229 } 230 231 // Stress-tests releasing of AudioEffect by doing repeated creation 232 // and subsequent releasing. Unlike a similar class in BassBoostTest, 233 // this one doesn't sets a control status listener because Visualizer 234 // doesn't inherit from AudioEffect and doesn't implement this method 235 // by itself. 236 class HammerReleaseTest extends Thread { 237 private static final int NUM_EFFECTS = 10; 238 private static final int NUM_ITERATIONS = 100; 239 private final int mSession; 240 private final Runnable mOnComplete; 241 HammerReleaseTest(int session, Runnable onComplete)242 HammerReleaseTest(int session, Runnable onComplete) { 243 mSession = session; 244 mOnComplete = onComplete; 245 } 246 247 @Override run()248 public void run() { 249 Log.w(TAG, "HammerReleaseTest started"); 250 Visualizer[] effects = new Visualizer[NUM_EFFECTS]; 251 for (int i = 0; i < NUM_ITERATIONS; i++) { 252 for (int j = 0; j < NUM_EFFECTS; j++) { 253 effects[j] = new Visualizer(mSession); 254 this.yield(); 255 } 256 for (int j = NUM_EFFECTS - 1; j >= 0; j--) { 257 Log.w(TAG, "HammerReleaseTest releasing effect " + (Object) effects[j]); 258 effects[j].release(); 259 effects[j] = null; 260 this.yield(); 261 } 262 } 263 Log.w(TAG, "HammerReleaseTest ended"); 264 runOnUiThread(mOnComplete); 265 } 266 } 267 runHammerReleaseTest(Button controlButton)268 private void runHammerReleaseTest(Button controlButton) { 269 controlButton.setEnabled(false); 270 HammerReleaseTest thread = new HammerReleaseTest(sSession, 271 () -> { 272 controlButton.setEnabled(true); 273 }); 274 thread.start(); 275 } 276 277 } 278