1 /*
2  * Copyright (C) 2010 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 android.media.audiofx;
18 
19 import android.media.audiofx.AudioEffect;
20 import android.util.Log;
21 
22 import java.util.StringTokenizer;
23 
24 
25 /**
26  * Bass boost is an audio effect to boost or amplify low frequencies of the sound. It is comparable
27  * to a simple equalizer but limited to one band amplification in the low frequency range.
28  * <p>An application creates a BassBoost object to instantiate and control a bass boost engine in
29  * the audio framework.
30  * <p>The methods, parameter types and units exposed by the BassBoost implementation are directly
31  * mapping those defined by the OpenSL ES 1.0.1 Specification (http://www.khronos.org/opensles/)
32  * for the SLBassBoostItf interface. Please refer to this specification for more details.
33  * <p>To attach the BassBoost to a particular AudioTrack or MediaPlayer, specify the audio session
34  * ID of this AudioTrack or MediaPlayer when constructing the BassBoost.
35  * <p>NOTE: attaching a BassBoost to the global audio output mix by use of session 0 is deprecated.
36  * <p>See {@link android.media.MediaPlayer#getAudioSessionId()} for details on audio sessions.
37  * <p>See {@link android.media.audiofx.AudioEffect} class for more details on
38  * controlling audio effects.
39  */
40 
41 public class BassBoost extends AudioEffect {
42 
43     private final static String TAG = "BassBoost";
44 
45     // These constants must be synchronized with those in
46     // frameworks/base/include/media/EffectBassBoostApi.h
47     /**
48      * Is strength parameter supported by bass boost engine. Parameter ID for getParameter().
49      */
50     public static final int PARAM_STRENGTH_SUPPORTED = 0;
51     /**
52      * Bass boost effect strength. Parameter ID for
53      * {@link android.media.audiofx.BassBoost.OnParameterChangeListener}
54      */
55     public static final int PARAM_STRENGTH = 1;
56 
57     /**
58      * Indicates if strength parameter is supported by the bass boost engine
59      */
60     private boolean mStrengthSupported = false;
61 
62     /**
63      * Registered listener for parameter changes.
64      */
65     private OnParameterChangeListener mParamListener = null;
66 
67     /**
68      * Listener used internally to to receive raw parameter change event from AudioEffect super class
69      */
70     private BaseParameterListener mBaseParamListener = null;
71 
72     /**
73      * Lock for access to mParamListener
74      */
75     private final Object mParamListenerLock = new Object();
76 
77     /**
78      * Class constructor.
79      * @param priority the priority level requested by the application for controlling the BassBoost
80      * engine. As the same engine can be shared by several applications, this parameter indicates
81      * how much the requesting application needs control of effect parameters. The normal priority
82      * is 0, above normal is a positive number, below normal a negative number.
83      * @param audioSession system wide unique audio session identifier. The BassBoost will be
84      * attached to the MediaPlayer or AudioTrack in the same audio session.
85      *
86      * @throws java.lang.IllegalStateException
87      * @throws java.lang.IllegalArgumentException
88      * @throws java.lang.UnsupportedOperationException
89      * @throws java.lang.RuntimeException
90      */
BassBoost(int priority, int audioSession)91     public BassBoost(int priority, int audioSession)
92     throws IllegalStateException, IllegalArgumentException,
93            UnsupportedOperationException, RuntimeException {
94         super(EFFECT_TYPE_BASS_BOOST, EFFECT_TYPE_NULL, priority, audioSession);
95 
96         if (audioSession == 0) {
97             Log.w(TAG, "WARNING: attaching a BassBoost to global output mix is deprecated!");
98         }
99 
100         int[] value = new int[1];
101         checkStatus(getParameter(PARAM_STRENGTH_SUPPORTED, value));
102         mStrengthSupported = (value[0] != 0);
103     }
104 
105     /**
106      * Indicates whether setting strength is supported. If this method returns false, only one
107      * strength is supported and the setStrength() method always rounds to that value.
108      * @return true is strength parameter is supported, false otherwise
109      */
getStrengthSupported()110     public boolean getStrengthSupported() {
111        return mStrengthSupported;
112     }
113 
114     /**
115      * Sets the strength of the bass boost effect. If the implementation does not support per mille
116      * accuracy for setting the strength, it is allowed to round the given strength to the nearest
117      * supported value. You can use the {@link #getRoundedStrength()} method to query the
118      * (possibly rounded) value that was actually set.
119      * @param strength strength of the effect. The valid range for strength strength is [0, 1000],
120      * where 0 per mille designates the mildest effect and 1000 per mille designates the strongest.
121      * @throws IllegalStateException
122      * @throws IllegalArgumentException
123      * @throws UnsupportedOperationException
124      */
setStrength(short strength)125     public void setStrength(short strength)
126     throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
127         checkStatus(setParameter(PARAM_STRENGTH, strength));
128     }
129 
130     /**
131      * Gets the current strength of the effect.
132      * @return the strength of the effect. The valid range for strength is [0, 1000], where 0 per
133      * mille designates the mildest effect and 1000 per mille the strongest
134      * @throws IllegalStateException
135      * @throws IllegalArgumentException
136      * @throws UnsupportedOperationException
137      */
getRoundedStrength()138     public short getRoundedStrength()
139     throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
140         short[] value = new short[1];
141         checkStatus(getParameter(PARAM_STRENGTH, value));
142         return value[0];
143     }
144 
145     /**
146      * The OnParameterChangeListener interface defines a method called by the BassBoost when a
147      * parameter value has changed.
148      */
149     public interface OnParameterChangeListener  {
150         /**
151          * Method called when a parameter value has changed. The method is called only if the
152          * parameter was changed by another application having the control of the same
153          * BassBoost engine.
154          * @param effect the BassBoost on which the interface is registered.
155          * @param status status of the set parameter operation.
156          * @param param ID of the modified parameter. See {@link #PARAM_STRENGTH} ...
157          * @param value the new parameter value.
158          */
onParameterChange(BassBoost effect, int status, int param, short value)159         void onParameterChange(BassBoost effect, int status, int param, short value);
160     }
161 
162     /**
163      * Listener used internally to receive unformatted parameter change events from AudioEffect
164      * super class.
165      */
166     private class BaseParameterListener implements AudioEffect.OnParameterChangeListener {
BaseParameterListener()167         private BaseParameterListener() {
168 
169         }
onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value)170         public void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value) {
171             OnParameterChangeListener l = null;
172 
173             synchronized (mParamListenerLock) {
174                 if (mParamListener != null) {
175                     l = mParamListener;
176                 }
177             }
178             if (l != null) {
179                 int p = -1;
180                 short v = -1;
181 
182                 if (param.length == 4) {
183                     p = byteArrayToInt(param, 0);
184                 }
185                 if (value.length == 2) {
186                     v = byteArrayToShort(value, 0);
187                 }
188                 if (p != -1 && v != -1) {
189                     l.onParameterChange(BassBoost.this, status, p, v);
190                 }
191             }
192         }
193     }
194 
195     /**
196      * Registers an OnParameterChangeListener interface.
197      * @param listener OnParameterChangeListener interface registered
198      */
setParameterListener(OnParameterChangeListener listener)199     public void setParameterListener(OnParameterChangeListener listener) {
200         synchronized (mParamListenerLock) {
201             if (mParamListener == null) {
202                 mParamListener = listener;
203                 mBaseParamListener = new BaseParameterListener();
204                 super.setParameterListener(mBaseParamListener);
205             }
206         }
207     }
208 
209     /**
210      * The Settings class regroups all bass boost parameters. It is used in
211      * conjuntion with getProperties() and setProperties() methods to backup and restore
212      * all parameters in a single call.
213      */
214     public static class Settings {
215         public short strength;
216 
Settings()217         public Settings() {
218         }
219 
220         /**
221          * Settings class constructor from a key=value; pairs formatted string. The string is
222          * typically returned by Settings.toString() method.
223          * @throws IllegalArgumentException if the string is not correctly formatted.
224          */
Settings(String settings)225         public Settings(String settings) {
226             StringTokenizer st = new StringTokenizer(settings, "=;");
227             int tokens = st.countTokens();
228             if (st.countTokens() != 3) {
229                 throw new IllegalArgumentException("settings: " + settings);
230             }
231             String key = st.nextToken();
232             if (!key.equals("BassBoost")) {
233                 throw new IllegalArgumentException(
234                         "invalid settings for BassBoost: " + key);
235             }
236             try {
237                 key = st.nextToken();
238                 if (!key.equals("strength")) {
239                     throw new IllegalArgumentException("invalid key name: " + key);
240                 }
241                 strength = Short.parseShort(st.nextToken());
242              } catch (NumberFormatException nfe) {
243                 throw new IllegalArgumentException("invalid value for key: " + key);
244             }
245         }
246 
247         @Override
toString()248         public String toString() {
249             String str = new String (
250                     "BassBoost"+
251                     ";strength="+Short.toString(strength)
252                     );
253             return str;
254         }
255     };
256 
257 
258     /**
259      * Gets the bass boost properties. This method is useful when a snapshot of current
260      * bass boost settings must be saved by the application.
261      * @return a BassBoost.Settings object containing all current parameters values
262      * @throws IllegalStateException
263      * @throws IllegalArgumentException
264      * @throws UnsupportedOperationException
265      */
getProperties()266     public BassBoost.Settings getProperties()
267     throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
268         Settings settings = new Settings();
269         short[] value = new short[1];
270         checkStatus(getParameter(PARAM_STRENGTH, value));
271         settings.strength = value[0];
272         return settings;
273     }
274 
275     /**
276      * Sets the bass boost properties. This method is useful when bass boost settings have to
277      * be applied from a previous backup.
278      * @param settings a BassBoost.Settings object containing the properties to apply
279      * @throws IllegalStateException
280      * @throws IllegalArgumentException
281      * @throws UnsupportedOperationException
282      */
setProperties(BassBoost.Settings settings)283     public void setProperties(BassBoost.Settings settings)
284     throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
285         checkStatus(setParameter(PARAM_STRENGTH, settings.strength));
286     }
287 }
288