• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.AudioEffect;
21 import android.media.audiofx.BassBoost;
22 import android.os.Bundle;
23 import android.util.Log;
24 import android.view.KeyEvent;
25 import android.view.View;
26 import android.view.View.OnClickListener;
27 import android.widget.Button;
28 import android.widget.CompoundButton;
29 import android.widget.CompoundButton.OnCheckedChangeListener;
30 import android.widget.EditText;
31 import android.widget.SeekBar;
32 import android.widget.TextView;
33 import android.widget.ToggleButton;
34 
35 import java.nio.ByteBuffer;
36 import java.nio.ByteOrder;
37 import java.util.HashMap;
38 
39 public class BassBoostTest extends Activity implements OnCheckedChangeListener {
40 
41     private final static String TAG = "BassBoostTest";
42 
43     private static int NUM_PARAMS = 1;
44 
45     private EffectParameter mStrength;
46     private BassBoost mBassBoost = null;
47     ToggleButton mOnOffButton;
48     ToggleButton mReleaseButton;
49     EditText mSessionText;
50     static int sSession = 0;
51     EffectListner mEffectListener = new EffectListner();
52     private static HashMap<Integer, BassBoost> sInstances = new HashMap<Integer, BassBoost>(10);
53     String mSettings = "";
54 
BassBoostTest()55     public BassBoostTest() {
56         Log.d(TAG, "contructor");
57     }
58 
59     @Override
onCreate(Bundle icicle)60     public void onCreate(Bundle icicle) {
61         super.onCreate(icicle);
62 
63         SeekBar seekBar;
64         TextView textView;
65 
66         setContentView(R.layout.bassboosttest);
67 
68         mSessionText = findViewById(R.id.sessionEdit);
69         mSessionText.setOnKeyListener(mSessionKeyListener);
70 
71         mSessionText.setText(Integer.toString(sSession));
72 
73         mReleaseButton = (ToggleButton)findViewById(R.id.bbReleaseButton);
74         mOnOffButton = (ToggleButton)findViewById(R.id.bassboostOnOff);
75 
76         final Button hammerReleaseTest = (Button) findViewById(R.id.hammer_on_release_bug);
77         hammerReleaseTest.setEnabled(false);
78 
79         getEffect(sSession);
80 
81         if (mBassBoost != null) {
82             mReleaseButton.setOnCheckedChangeListener(this);
83             mOnOffButton.setOnCheckedChangeListener(this);
84 
85             textView = (TextView)findViewById(R.id.bbStrengthMin);
86             textView.setText("0");
87             textView = (TextView)findViewById(R.id.bbStrengthMax);
88             textView.setText("1000");
89             seekBar = (SeekBar)findViewById(R.id.bbStrengthSeekBar);
90             textView = (TextView)findViewById(R.id.bbStrengthValue);
91             mStrength = new BassBoostParam(mBassBoost, 0, 1000, seekBar, textView);
92             seekBar.setOnSeekBarChangeListener(mStrength);
93             mStrength.setEnabled(mBassBoost.getStrengthSupported());
94 
95             hammerReleaseTest.setEnabled(true);
96             hammerReleaseTest.setOnClickListener(new OnClickListener() {
97                 @Override
98                 public void onClick(View v) {
99                     runHammerReleaseTest(hammerReleaseTest);
100                 }
101             });
102         }
103     }
104 
105     @Override
onResume()106     public void onResume() {
107         super.onResume();
108     }
109 
110     @Override
onPause()111     public void onPause() {
112         super.onPause();
113     }
114 
115     private View.OnKeyListener mSessionKeyListener
116     = new View.OnKeyListener() {
117         public boolean onKey(View v, int keyCode, KeyEvent event) {
118             Log.d(TAG, "onKey() keyCode: "+keyCode+" event.getAction(): "+event.getAction());
119             if (event.getAction() == KeyEvent.ACTION_DOWN) {
120                 switch (keyCode) {
121                     case KeyEvent.KEYCODE_DPAD_CENTER:
122                     case KeyEvent.KEYCODE_ENTER:
123                         try {
124                             sSession = Integer.parseInt(mSessionText.getText().toString());
125                             getEffect(sSession);
126                             if (mBassBoost != null) {
127                                 mStrength.setEffect(mBassBoost);
128                                 mStrength.setEnabled(mBassBoost.getStrengthSupported());
129                             }
130                         } catch (NumberFormatException e) {
131                             Log.d(TAG, "Invalid session #: "+mSessionText.getText().toString());
132                         }
133                         return true;
134                 }
135             }
136             return false;
137         }
138     };
139 
140     // OnCheckedChangeListener
onCheckedChanged(CompoundButton buttonView, boolean isChecked)141     public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
142         if (buttonView.getId() == R.id.bassboostOnOff) {
143             if (mBassBoost != null) {
144                 mBassBoost.setEnabled(isChecked);
145                 mStrength.updateDisplay();
146             }
147         }
148         if (buttonView.getId() == R.id.bbReleaseButton) {
149             if (isChecked) {
150                 if (mBassBoost == null) {
151                     getEffect(sSession);
152                     if (mBassBoost != null) {
153                         mStrength.setEffect(mBassBoost);
154                         mStrength.setEnabled(mBassBoost.getStrengthSupported());
155                     }
156                 }
157             } else {
158                 if (mBassBoost != null) {
159                     mStrength.setEnabled(false);
160                     putEffect(sSession);
161                 }
162             }
163         }
164     }
165 
166     private class BassBoostParam extends EffectParameter {
167         private BassBoost mBassBoost;
168 
BassBoostParam(BassBoost bassboost, int min, int max, SeekBar seekBar, TextView textView)169         public BassBoostParam(BassBoost bassboost, int min, int max, SeekBar seekBar, TextView textView) {
170             super (min, max, seekBar, textView, "o/oo");
171 
172             mBassBoost = bassboost;
173             updateDisplay();
174         }
175 
176         @Override
setParameter(Integer value)177         public void setParameter(Integer value) {
178             if (mBassBoost != null) {
179                 mBassBoost.setStrength(value.shortValue());
180             }
181         }
182 
183         @Override
getParameter()184         public Integer getParameter() {
185             if (mBassBoost != null) {
186                 return new Integer(mBassBoost.getRoundedStrength());
187             }
188             return new Integer(0);
189         }
190 
191         @Override
setEffect(Object effect)192         public void setEffect(Object effect) {
193             mBassBoost = (BassBoost)effect;
194         }
195     }
196 
197     public class EffectListner implements AudioEffect.OnEnableStatusChangeListener,
198         AudioEffect.OnControlStatusChangeListener, AudioEffect.OnParameterChangeListener
199    {
EffectListner()200         public EffectListner() {
201         }
onEnableStatusChange(AudioEffect effect, boolean enabled)202         public void onEnableStatusChange(AudioEffect effect, boolean enabled) {
203             Log.d(TAG,"onEnableStatusChange: "+ enabled);
204         }
onControlStatusChange(AudioEffect effect, boolean controlGranted)205         public void onControlStatusChange(AudioEffect effect, boolean controlGranted) {
206             Log.d(TAG,"onControlStatusChange: "+ controlGranted);
207         }
onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value)208         public void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value) {
209             int p = byteArrayToInt(param, 0);
210             short v = byteArrayToShort(value, 0);
211 
212             Log.d(TAG,"onParameterChange, status: "+status+" p: "+p+" v: "+v);
213         }
214 
byteArrayToInt(byte[] valueBuf, int offset)215         private int byteArrayToInt(byte[] valueBuf, int offset) {
216             ByteBuffer converter = ByteBuffer.wrap(valueBuf);
217             converter.order(ByteOrder.nativeOrder());
218             return converter.getInt(offset);
219 
220         }
byteArrayToShort(byte[] valueBuf, int offset)221         private short byteArrayToShort(byte[] valueBuf, int offset) {
222             ByteBuffer converter = ByteBuffer.wrap(valueBuf);
223             converter.order(ByteOrder.nativeOrder());
224             return converter.getShort(offset);
225 
226         }
227 
228     }
229 
getEffect(int session)230     private void getEffect(int session) {
231         synchronized (sInstances) {
232             if (sInstances.containsKey(session)) {
233                 mBassBoost = sInstances.get(session);
234             } else {
235                 try{
236                     mBassBoost = new BassBoost(0, session);
237                 } catch (IllegalArgumentException e) {
238                     Log.e(TAG,"BassBoost effect not supported");
239                 } catch (IllegalStateException e) {
240                     Log.e(TAG,"BassBoost cannot get strength supported");
241                 } catch (UnsupportedOperationException e) {
242                     Log.e(TAG,"BassBoost library not loaded");
243                 } catch (RuntimeException e) {
244                     Log.e(TAG,"BassBoost effect not found");
245                 }
246                 sInstances.put(session, mBassBoost);
247             }
248             mReleaseButton.setEnabled(false);
249             mOnOffButton.setEnabled(false);
250 
251             if (mBassBoost != null) {
252                 if (mSettings != "") {
253                     mBassBoost.setProperties(new BassBoost.Settings(mSettings));
254                 }
255                 mBassBoost.setEnableStatusListener(mEffectListener);
256                 mBassBoost.setControlStatusListener(mEffectListener);
257                 mBassBoost.setParameterListener(mEffectListener);
258 
259                 mReleaseButton.setChecked(true);
260                 mReleaseButton.setEnabled(true);
261 
262                 mOnOffButton.setChecked(mBassBoost.getEnabled());
263                 mOnOffButton.setEnabled(true);
264             }
265         }
266     }
267 
putEffect(int session)268     private void putEffect(int session) {
269         mOnOffButton.setChecked(false);
270         mOnOffButton.setEnabled(false);
271         synchronized (sInstances) {
272             if (mBassBoost != null) {
273                 mSettings = mBassBoost.getProperties().toString();
274                 mBassBoost.release();
275                 Log.d(TAG,"BassBoost released");
276                 mBassBoost = null;
277                 sInstances.remove(session);
278             }
279         }
280     }
281 
282     // Stress-tests releasing of AudioEffect by doing repeated creation
283     // and subsequent releasing. Also forces emission of callbacks from
284     // the AudioFlinger by setting a control status listener. Since all
285     // effect instances are bound to the same session, the AF will
286     // notify them about the change in their status. This can reveal racy
287     // behavior w.r.t. releasing.
288     class HammerReleaseTest extends Thread {
289         private static final int NUM_EFFECTS = 10;
290         private static final int NUM_ITERATIONS = 100;
291         private final int mSession;
292         private final Runnable mOnComplete;
293 
HammerReleaseTest(int session, Runnable onComplete)294         HammerReleaseTest(int session, Runnable onComplete) {
295             mSession = session;
296             mOnComplete = onComplete;
297         }
298 
299         @Override
run()300         public void run() {
301             Log.w(TAG, "HammerReleaseTest started");
302             BassBoost[] effects = new BassBoost[NUM_EFFECTS];
303             for (int i = 0; i < NUM_ITERATIONS; i++) {
304                 for (int j = 0; j < NUM_EFFECTS; j++) {
305                     effects[j] = new BassBoost(0, mSession);
306                     effects[j].setControlStatusListener(mEffectListener);
307                     yield();
308                 }
309                 for (int j = NUM_EFFECTS - 1; j >= 0; j--) {
310                     Log.w(TAG, "HammerReleaseTest releasing effect " + (Object) effects[j]);
311                     effects[j].release();
312                     effects[j] = null;
313                     yield();
314                 }
315             }
316             Log.w(TAG, "HammerReleaseTest ended");
317             runOnUiThread(mOnComplete);
318         }
319     }
320 
runHammerReleaseTest(Button controlButton)321     private void runHammerReleaseTest(Button controlButton) {
322         controlButton.setEnabled(false);
323         HammerReleaseTest thread = new HammerReleaseTest(sSession,
324                 () -> {
325                     controlButton.setEnabled(true);
326                 });
327         thread.start();
328     }
329 
330 }
331