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
17package android.media.audiofx;
18
19import android.media.audiofx.AudioEffect;
20import java.util.StringTokenizer;
21
22
23/**
24 * A sound generated within a room travels in many directions. The listener first hears the
25 * direct sound from the source itself. Later, he or she hears discrete echoes caused by sound
26 * bouncing off nearby walls, the ceiling and the floor. As sound waves arrive after
27 * undergoing more and more reflections, individual reflections become indistinguishable and
28 * the listener hears continuous reverberation that decays over time.
29 * Reverb is vital for modeling a listener's environment. It can be used in music applications
30 * to simulate music being played back in various environments, or in games to immerse the
31 * listener within the game's environment.
32 * The PresetReverb class allows an application to configure the global reverb using a reverb preset.
33 * This is primarily used for adding some reverb in a music playback context. Applications
34 * requiring control over a more advanced environmental reverb are advised to use the
35 * {@link android.media.audiofx.EnvironmentalReverb} class.
36 * <p>An application creates a PresetReverb object to instantiate and control a reverb engine in the
37 * audio framework.
38 * <p>The methods, parameter types and units exposed by the PresetReverb implementation are
39 * directly mapping those defined by the OpenSL ES 1.0.1 Specification
40 * (http://www.khronos.org/opensles/) for the SLPresetReverbItf interface.
41 * Please refer to this specification for more details.
42 * <p>The PresetReverb is an output mix auxiliary effect and should be created on
43 * Audio session 0. In order for a MediaPlayer or AudioTrack to be fed into this effect,
44 * they must be explicitely attached to it and a send level must be specified. Use the effect ID
45 * returned by getId() method to designate this particular effect when attaching it to the
46 * MediaPlayer or AudioTrack.
47 * <p>Creating a reverb on the output mix (audio session 0) requires permission
48 * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}
49 * <p>See {@link android.media.audiofx.AudioEffect} class for more details on controlling
50 * audio effects.
51 */
52
53public class PresetReverb extends AudioEffect {
54
55    private final static String TAG = "PresetReverb";
56
57    // These constants must be synchronized with those in
58    // frameworks/base/include/media/EffectPresetReverbApi.h
59
60    /**
61     * Preset. Parameter ID for
62     * {@link android.media.audiofx.PresetReverb.OnParameterChangeListener}
63     */
64    public static final int PARAM_PRESET = 0;
65
66    /**
67     * No reverb or reflections
68     */
69    public static final short PRESET_NONE        = 0;
70    /**
71     * Reverb preset representing a small room less than five meters in length
72     */
73    public static final short PRESET_SMALLROOM   = 1;
74    /**
75     * Reverb preset representing a medium room with a length of ten meters or less
76     */
77    public static final short PRESET_MEDIUMROOM  = 2;
78    /**
79     * Reverb preset representing a large-sized room suitable for live performances
80     */
81    public static final short PRESET_LARGEROOM   = 3;
82    /**
83     * Reverb preset representing a medium-sized hall
84     */
85    public static final short PRESET_MEDIUMHALL  = 4;
86    /**
87     * Reverb preset representing a large-sized hall suitable for a full orchestra
88     */
89    public static final short PRESET_LARGEHALL   = 5;
90    /**
91     * Reverb preset representing a synthesis of the traditional plate reverb
92     */
93    public static final short PRESET_PLATE       = 6;
94
95    /**
96     * Registered listener for parameter changes.
97     */
98    private OnParameterChangeListener mParamListener = null;
99
100    /**
101     * Listener used internally to to receive raw parameter change event from AudioEffect super class
102     */
103    private BaseParameterListener mBaseParamListener = null;
104
105    /**
106     * Lock for access to mParamListener
107     */
108    private final Object mParamListenerLock = new Object();
109
110    /**
111     * Class constructor.
112     * @param priority the priority level requested by the application for controlling the
113     * PresetReverb engine. As the same engine can be shared by several applications, this
114     * parameter indicates how much the requesting application needs control of effect parameters.
115     * The normal priority is 0, above normal is a positive number, below normal a negative number.
116     * @param audioSession  system wide unique audio session identifier. If audioSession
117     *  is not 0, the PresetReverb will be attached to the MediaPlayer or AudioTrack in the
118     *  same audio session. Otherwise, the PresetReverb will apply to the output mix.
119     *  As the PresetReverb is an auxiliary effect it is recommended to instantiate it on
120     *  audio session 0 and to attach it to the MediaPLayer auxiliary output.
121     *
122     * @throws java.lang.IllegalArgumentException
123     * @throws java.lang.UnsupportedOperationException
124     * @throws java.lang.RuntimeException
125     */
126    public PresetReverb(int priority, int audioSession)
127    throws IllegalArgumentException, UnsupportedOperationException, RuntimeException {
128        super(EFFECT_TYPE_PRESET_REVERB, EFFECT_TYPE_NULL, priority, audioSession);
129    }
130
131    /**
132     *  Enables a preset on the reverb.
133     *  <p>The reverb PRESET_NONE disables any reverb from the current output but does not free the
134     *  resources associated with the reverb. For an application to signal to the implementation
135     *  to free the resources, it must call the release() method.
136     * @param preset this must be one of the the preset constants defined in this class.
137     * e.g. {@link #PRESET_SMALLROOM}
138     * @throws IllegalStateException
139     * @throws IllegalArgumentException
140     * @throws UnsupportedOperationException
141     */
142    public void setPreset(short preset)
143    throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
144        checkStatus(setParameter(PARAM_PRESET, preset));
145    }
146
147    /**
148     * Gets current reverb preset.
149     * @return the preset that is set at the moment.
150     * @throws IllegalStateException
151     * @throws IllegalArgumentException
152     * @throws UnsupportedOperationException
153     */
154    public short getPreset()
155    throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
156        short[] value = new short[1];
157        checkStatus(getParameter(PARAM_PRESET, value));
158        return value[0];
159    }
160
161    /**
162     * The OnParameterChangeListener interface defines a method called by the PresetReverb
163     * when a parameter value has changed.
164     */
165    public interface OnParameterChangeListener  {
166        /**
167         * Method called when a parameter value has changed. The method is called only if the
168         * parameter was changed by another application having the control of the same
169         * PresetReverb engine.
170         * @param effect the PresetReverb on which the interface is registered.
171         * @param status status of the set parameter operation.
172         * @param param ID of the modified parameter. See {@link #PARAM_PRESET} ...
173         * @param value the new parameter value.
174         */
175        void onParameterChange(PresetReverb effect, int status, int param, short value);
176    }
177
178    /**
179     * Listener used internally to receive unformatted parameter change events from AudioEffect
180     * super class.
181     */
182    private class BaseParameterListener implements AudioEffect.OnParameterChangeListener {
183        private BaseParameterListener() {
184
185        }
186        public void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value) {
187            OnParameterChangeListener l = null;
188
189            synchronized (mParamListenerLock) {
190                if (mParamListener != null) {
191                    l = mParamListener;
192                }
193            }
194            if (l != null) {
195                int p = -1;
196                short v = -1;
197
198                if (param.length == 4) {
199                    p = byteArrayToInt(param, 0);
200                }
201                if (value.length == 2) {
202                    v = byteArrayToShort(value, 0);
203                }
204                if (p != -1 && v != -1) {
205                    l.onParameterChange(PresetReverb.this, status, p, v);
206                }
207            }
208        }
209    }
210
211    /**
212     * Registers an OnParameterChangeListener interface.
213     * @param listener OnParameterChangeListener interface registered
214     */
215    public void setParameterListener(OnParameterChangeListener listener) {
216        synchronized (mParamListenerLock) {
217            if (mParamListener == null) {
218                mParamListener = listener;
219                mBaseParamListener = new BaseParameterListener();
220                super.setParameterListener(mBaseParamListener);
221            }
222        }
223    }
224
225    /**
226     * The Settings class regroups all preset reverb parameters. It is used in
227     * conjuntion with getProperties() and setProperties() methods to backup and restore
228     * all parameters in a single call.
229     */
230    public static class Settings {
231        public short preset;
232
233        public Settings() {
234        }
235
236        /**
237         * Settings class constructor from a key=value; pairs formatted string. The string is
238         * typically returned by Settings.toString() method.
239         * @throws IllegalArgumentException if the string is not correctly formatted.
240         */
241        public Settings(String settings) {
242            StringTokenizer st = new StringTokenizer(settings, "=;");
243            int tokens = st.countTokens();
244            if (st.countTokens() != 3) {
245                throw new IllegalArgumentException("settings: " + settings);
246            }
247            String key = st.nextToken();
248            if (!key.equals("PresetReverb")) {
249                throw new IllegalArgumentException(
250                        "invalid settings for PresetReverb: " + key);
251            }
252            try {
253                key = st.nextToken();
254                if (!key.equals("preset")) {
255                    throw new IllegalArgumentException("invalid key name: " + key);
256                }
257                preset = Short.parseShort(st.nextToken());
258             } catch (NumberFormatException nfe) {
259                throw new IllegalArgumentException("invalid value for key: " + key);
260            }
261        }
262
263        @Override
264        public String toString() {
265            String str = new String (
266                    "PresetReverb"+
267                    ";preset="+Short.toString(preset)
268                    );
269            return str;
270        }
271    };
272
273
274    /**
275     * Gets the preset reverb properties. This method is useful when a snapshot of current
276     * preset reverb settings must be saved by the application.
277     * @return a PresetReverb.Settings object containing all current parameters values
278     * @throws IllegalStateException
279     * @throws IllegalArgumentException
280     * @throws UnsupportedOperationException
281     */
282    public PresetReverb.Settings getProperties()
283    throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
284        Settings settings = new Settings();
285        short[] value = new short[1];
286        checkStatus(getParameter(PARAM_PRESET, value));
287        settings.preset = value[0];
288        return settings;
289    }
290
291    /**
292     * Sets the preset reverb properties. This method is useful when preset reverb settings have to
293     * be applied from a previous backup.
294     * @param settings a PresetReverb.Settings object containing the properties to apply
295     * @throws IllegalStateException
296     * @throws IllegalArgumentException
297     * @throws UnsupportedOperationException
298     */
299    public void setProperties(PresetReverb.Settings settings)
300    throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
301        checkStatus(setParameter(PARAM_PRESET, settings.preset));
302    }
303}
304