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 java.util.StringTokenizer;
21 
22 /**
23  * A sound generated within a room travels in many directions. The listener first hears the direct
24  * sound from the source itself. Later, he or she hears discrete echoes caused by sound bouncing off
25  * nearby walls, the ceiling and the floor. As sound waves arrive after undergoing more and more
26  * reflections, individual reflections become indistinguishable and the listener hears continuous
27  * reverberation that decays over time.
28  * Reverb is vital for modeling a listener's environment. It can be used in music applications
29  * to simulate music being played back in various environments, or in games to immerse the
30  * listener within the game's environment.
31  * The EnvironmentalReverb class allows an application to control each reverb engine property in a
32  * global reverb environment and is more suitable for games. For basic control, more suitable for
33  * music applications, it is recommended to use the
34  * {@link android.media.audiofx.PresetReverb} class.
35  * <p>An application creates a EnvironmentalReverb object to instantiate and control a reverb engine
36  * in the audio framework.
37  * <p>The methods, parameter types and units exposed by the EnvironmentalReverb implementation are
38  * directly mapping those defined by the OpenSL ES 1.0.1 Specification
39  * (http://www.khronos.org/opensles/) for the SLEnvironmentalReverbItf interface.
40  * Please refer to this specification for more details.
41  * <p>The EnvironmentalReverb is an output mix auxiliary effect and should be created on
42  * Audio session 0. In order for a MediaPlayer or AudioTrack to be fed into this effect,
43  * they must be explicitely attached to it and a send level must be specified. Use the effect ID
44  * returned by getId() method to designate this particular effect when attaching it to the
45  * MediaPlayer or AudioTrack.
46  * <p>Creating a reverb on the output mix (audio session 0) requires permission
47  * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}
48  * <p>See {@link android.media.audiofx.AudioEffect} class for more details on controlling
49  * audio effects.
50  */
51 
52 public class EnvironmentalReverb extends AudioEffect {
53 
54     private final static String TAG = "EnvironmentalReverb";
55 
56     // These constants must be synchronized with those in
57     // frameworks/base/include/media/EffectEnvironmentalReverbApi.h
58 
59     /**
60      * Room level. Parameter ID for OnParameterChangeListener
61      */
62     public static final int PARAM_ROOM_LEVEL = 0;
63     /**
64      * Room HF level. Parameter ID for OnParameterChangeListener
65      */
66     public static final int PARAM_ROOM_HF_LEVEL = 1;
67     /**
68      * Decay time. Parameter ID for OnParameterChangeListener
69      */
70     public static final int PARAM_DECAY_TIME = 2;
71     /**
72      * Decay HF ratio. Parameter ID for
73      * {@link android.media.audiofx.EnvironmentalReverb.OnParameterChangeListener}
74      */
75     public static final int PARAM_DECAY_HF_RATIO = 3;
76     /**
77      * Early reflections level. Parameter ID for OnParameterChangeListener
78      */
79     public static final int PARAM_REFLECTIONS_LEVEL = 4;
80     /**
81      * Early reflections delay. Parameter ID for OnParameterChangeListener
82      */
83     public static final int PARAM_REFLECTIONS_DELAY = 5;
84     /**
85      * Reverb level. Parameter ID for OnParameterChangeListener
86      */
87     public static final int PARAM_REVERB_LEVEL = 6;
88     /**
89      * Reverb delay. Parameter ID for OnParameterChangeListener
90      */
91     public static final int PARAM_REVERB_DELAY = 7;
92     /**
93      * Diffusion. Parameter ID for OnParameterChangeListener
94      */
95     public static final int PARAM_DIFFUSION = 8;
96     /**
97      * Density. Parameter ID for OnParameterChangeListener
98      */
99     public static final int PARAM_DENSITY = 9;
100 
101     // used by setProperties()/getProperties
102     private static final int PARAM_PROPERTIES = 10;
103 
104     /**
105      * Registered listener for parameter changes
106      */
107     private OnParameterChangeListener mParamListener = null;
108 
109     /**
110      * Listener used internally to to receive raw parameter change event from AudioEffect super
111      * class
112      */
113     private BaseParameterListener mBaseParamListener = null;
114 
115     /**
116      * Lock for access to mParamListener
117      */
118     private final Object mParamListenerLock = new Object();
119 
120     /**
121      * Class constructor.
122      * @param priority the priority level requested by the application for controlling the
123      * EnvironmentalReverb engine. As the same engine can be shared by several applications, this
124      * parameter indicates how much the requesting application needs control of effect parameters.
125      * The normal priority is 0, above normal is a positive number, below normal a negative number.
126      * @param audioSession  system wide unique audio session identifier. If audioSession
127      *  is not 0, the EnvironmentalReverb will be attached to the MediaPlayer or AudioTrack in the
128      *  same audio session. Otherwise, the EnvironmentalReverb will apply to the output mix.
129      *  As the EnvironmentalReverb is an auxiliary effect it is recommended to instantiate it on
130      *  audio session 0 and to attach it to the MediaPLayer auxiliary output.
131      *
132      * @throws java.lang.IllegalArgumentException
133      * @throws java.lang.UnsupportedOperationException
134      * @throws java.lang.RuntimeException
135      */
EnvironmentalReverb(int priority, int audioSession)136     public EnvironmentalReverb(int priority, int audioSession)
137     throws IllegalArgumentException, UnsupportedOperationException, RuntimeException {
138         super(EFFECT_TYPE_ENV_REVERB, EFFECT_TYPE_NULL, priority, audioSession);
139     }
140 
141     /**
142      * Sets the master volume level of the environmental reverb effect.
143      * @param room room level in millibels. The valid range is [-9000, 0].
144      * @throws IllegalStateException
145      * @throws IllegalArgumentException
146      * @throws UnsupportedOperationException
147      */
setRoomLevel(short room)148     public void setRoomLevel(short room)
149     throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
150         byte[] param = shortToByteArray(room);
151         checkStatus(setParameter(PARAM_ROOM_LEVEL, param));
152     }
153 
154     /**
155      * Gets the master volume level of the environmental reverb effect.
156      * @return the room level in millibels.
157      * @throws IllegalStateException
158      * @throws IllegalArgumentException
159      * @throws UnsupportedOperationException
160      */
getRoomLevel()161     public short getRoomLevel()
162     throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
163         byte[] param = new byte[2];
164         checkStatus(getParameter(PARAM_ROOM_LEVEL, param));
165         return byteArrayToShort(param);
166     }
167 
168     /**
169      * Sets the volume level at 5 kHz relative to the volume level at low frequencies of the
170      * overall reverb effect.
171      * <p>This controls a low-pass filter that will reduce the level of the high-frequency.
172      * @param roomHF high frequency attenuation level in millibels. The valid range is [-9000, 0].
173      * @throws IllegalStateException
174      * @throws IllegalArgumentException
175      * @throws UnsupportedOperationException
176      */
setRoomHFLevel(short roomHF)177     public void setRoomHFLevel(short roomHF)
178     throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
179         byte[] param = shortToByteArray(roomHF);
180         checkStatus(setParameter(PARAM_ROOM_HF_LEVEL, param));
181     }
182 
183     /**
184      * Gets the room HF level.
185      * @return the room HF level in millibels.
186      * @throws IllegalStateException
187      * @throws IllegalArgumentException
188      * @throws UnsupportedOperationException
189      */
getRoomHFLevel()190     public short getRoomHFLevel()
191     throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
192         byte[] param = new byte[2];
193         checkStatus(getParameter(PARAM_ROOM_HF_LEVEL, param));
194         return byteArrayToShort(param);
195     }
196 
197     /**
198      * Sets the time taken for the level of reverberation to decay by 60 dB.
199      * @param decayTime decay time in milliseconds. The valid range is [100, 20000].
200      * @throws IllegalStateException
201      * @throws IllegalArgumentException
202      * @throws UnsupportedOperationException
203      */
setDecayTime(int decayTime)204     public void setDecayTime(int decayTime)
205     throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
206         byte[] param = intToByteArray(decayTime);
207         checkStatus(setParameter(PARAM_DECAY_TIME, param));
208     }
209 
210     /**
211      * Gets the decay time.
212      * @return the decay time in milliseconds.
213      * @throws IllegalStateException
214      * @throws IllegalArgumentException
215      * @throws UnsupportedOperationException
216      */
getDecayTime()217     public int getDecayTime()
218     throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
219         byte[] param = new byte[4];
220         checkStatus(getParameter(PARAM_DECAY_TIME, param));
221         return byteArrayToInt(param);
222     }
223 
224     /**
225      * Sets the ratio of high frequency decay time (at 5 kHz) relative to the decay time at low
226      * frequencies.
227      * @param decayHFRatio high frequency decay ratio using a permille scale. The valid range is
228      * [100, 2000]. A ratio of 1000 indicates that all frequencies decay at the same rate.
229      * @throws IllegalStateException
230      * @throws IllegalArgumentException
231      * @throws UnsupportedOperationException
232      */
setDecayHFRatio(short decayHFRatio)233     public void setDecayHFRatio(short decayHFRatio)
234     throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
235         byte[] param = shortToByteArray(decayHFRatio);
236         checkStatus(setParameter(PARAM_DECAY_HF_RATIO, param));
237     }
238 
239     /**
240      * Gets the ratio of high frequency decay time (at 5 kHz) relative to low frequencies.
241      * @return the decay HF ration. See {@link #setDecayHFRatio(short)} for units.
242      * @throws IllegalStateException
243      * @throws IllegalArgumentException
244      * @throws UnsupportedOperationException
245      */
getDecayHFRatio()246     public short getDecayHFRatio()
247     throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
248         byte[] param = new byte[2];
249         checkStatus(getParameter(PARAM_DECAY_HF_RATIO, param));
250         return byteArrayToShort(param);
251     }
252 
253     /**
254      * Sets the volume level of the early reflections.
255      * <p>This level is combined with the overall room level
256      * (set using {@link #setRoomLevel(short)}).
257      * @param reflectionsLevel reflection level in millibels. The valid range is [-9000, 1000].
258      * @throws IllegalStateException
259      * @throws IllegalArgumentException
260      * @throws UnsupportedOperationException
261      */
setReflectionsLevel(short reflectionsLevel)262     public void setReflectionsLevel(short reflectionsLevel)
263     throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
264         byte[] param = shortToByteArray(reflectionsLevel);
265         checkStatus(setParameter(PARAM_REFLECTIONS_LEVEL, param));
266     }
267 
268     /**
269      * Gets the volume level of the early reflections.
270      * @return the early reflections level in millibels.
271      * @throws IllegalStateException
272      * @throws IllegalArgumentException
273      * @throws UnsupportedOperationException
274      */
getReflectionsLevel()275     public short getReflectionsLevel()
276     throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
277         byte[] param = new byte[2];
278         checkStatus(getParameter(PARAM_REFLECTIONS_LEVEL, param));
279         return byteArrayToShort(param);
280     }
281 
282     /**
283      * Sets the delay time for the early reflections.
284      * <p>This method sets the time between when the direct path is heard and when the first
285      * reflection is heard.
286      * @param reflectionsDelay reflections delay in milliseconds. The valid range is [0, 300].
287      * @throws IllegalStateException
288      * @throws IllegalArgumentException
289      * @throws UnsupportedOperationException
290      */
setReflectionsDelay(int reflectionsDelay)291     public void setReflectionsDelay(int reflectionsDelay)
292     throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
293         byte[] param = intToByteArray(reflectionsDelay);
294         checkStatus(setParameter(PARAM_REFLECTIONS_DELAY, param));
295     }
296 
297     /**
298      * Gets the reflections delay.
299      * @return the early reflections delay in milliseconds.
300      * @throws IllegalStateException
301      * @throws IllegalArgumentException
302      * @throws UnsupportedOperationException
303      */
getReflectionsDelay()304     public int getReflectionsDelay()
305     throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
306         byte[] param = new byte[4];
307         checkStatus(getParameter(PARAM_REFLECTIONS_DELAY, param));
308         return byteArrayToInt(param);
309     }
310 
311     /**
312      * Sets the volume level of the late reverberation.
313      * <p>This level is combined with the overall room level (set using {@link #setRoomLevel(short)}).
314      * @param reverbLevel reverb level in millibels. The valid range is [-9000, 2000].
315      * @throws IllegalStateException
316      * @throws IllegalArgumentException
317      * @throws UnsupportedOperationException
318      */
setReverbLevel(short reverbLevel)319     public void setReverbLevel(short reverbLevel)
320     throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
321         byte[] param = shortToByteArray(reverbLevel);
322         checkStatus(setParameter(PARAM_REVERB_LEVEL, param));
323     }
324 
325     /**
326      * Gets the reverb level.
327      * @return the reverb level in millibels.
328      * @throws IllegalStateException
329      * @throws IllegalArgumentException
330      * @throws UnsupportedOperationException
331      */
getReverbLevel()332     public short getReverbLevel()
333     throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
334         byte[] param = new byte[2];
335         checkStatus(getParameter(PARAM_REVERB_LEVEL, param));
336         return byteArrayToShort(param);
337     }
338 
339     /**
340      * Sets the time between the first reflection and the reverberation.
341      * @param reverbDelay reverb delay in milliseconds. The valid range is [0, 100].
342      * @throws IllegalStateException
343      * @throws IllegalArgumentException
344      * @throws UnsupportedOperationException
345      */
setReverbDelay(int reverbDelay)346     public void setReverbDelay(int reverbDelay)
347     throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
348         byte[] param = intToByteArray(reverbDelay);
349         checkStatus(setParameter(PARAM_REVERB_DELAY, param));
350     }
351 
352     /**
353      * Gets the reverb delay.
354      * @return the reverb delay in milliseconds.
355      * @throws IllegalStateException
356      * @throws IllegalArgumentException
357      * @throws UnsupportedOperationException
358      */
getReverbDelay()359     public int getReverbDelay()
360     throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
361         byte[] param = new byte[4];
362         checkStatus(getParameter(PARAM_REVERB_DELAY, param));
363         return byteArrayToInt(param);
364     }
365 
366     /**
367      * Sets the echo density in the late reverberation decay.
368      * <p>The scale should approximately map linearly to the perceived change in reverberation.
369      * @param diffusion diffusion specified using a permille scale. The diffusion valid range is
370      * [0, 1000]. A value of 1000 o/oo indicates a smooth reverberation decay.
371      * Values below this level give a more <i>grainy</i> character.
372      * @throws IllegalStateException
373      * @throws IllegalArgumentException
374      * @throws UnsupportedOperationException
375      */
setDiffusion(short diffusion)376     public void setDiffusion(short diffusion)
377     throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
378         byte[] param = shortToByteArray(diffusion);
379         checkStatus(setParameter(PARAM_DIFFUSION, param));
380     }
381 
382     /**
383      * Gets diffusion level.
384      * @return the diffusion level. See {@link #setDiffusion(short)} for units.
385      * @throws IllegalStateException
386      * @throws IllegalArgumentException
387      * @throws UnsupportedOperationException
388      */
getDiffusion()389     public short getDiffusion()
390     throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
391         byte[] param = new byte[2];
392         checkStatus(getParameter(PARAM_DIFFUSION, param));
393         return byteArrayToShort(param);
394     }
395 
396 
397     /**
398      * Controls the modal density of the late reverberation decay.
399      * <p> The scale should approximately map linearly to the perceived change in reverberation.
400      * A lower density creates a hollow sound that is useful for simulating small reverberation
401      * spaces such as bathrooms.
402      * @param density density specified using a permille scale. The valid range is [0, 1000].
403      * A value of 1000 o/oo indicates a natural sounding reverberation. Values below this level
404      * produce a more colored effect.
405      * @throws IllegalStateException
406      * @throws IllegalArgumentException
407      * @throws UnsupportedOperationException
408      */
setDensity(short density)409     public void setDensity(short density)
410     throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
411         byte[] param = shortToByteArray(density);
412         checkStatus(setParameter(PARAM_DENSITY, param));
413     }
414 
415     /**
416      * Gets the density level.
417      * @return the density level. See {@link #setDiffusion(short)} for units.
418      * @throws IllegalStateException
419      * @throws IllegalArgumentException
420      * @throws UnsupportedOperationException
421      */
getDensity()422     public short getDensity()
423     throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
424         byte[] param = new byte[2];
425         checkStatus(getParameter(PARAM_DENSITY, param));
426         return byteArrayToShort(param);
427     }
428 
429 
430     /**
431      * The OnParameterChangeListener interface defines a method called by the EnvironmentalReverb
432      * when a parameter value has changed.
433      */
434     public interface OnParameterChangeListener  {
435         /**
436          * Method called when a parameter value has changed. The method is called only if the
437          * parameter was changed by another application having the control of the same
438          * EnvironmentalReverb engine.
439          * @param effect the EnvironmentalReverb on which the interface is registered.
440          * @param status status of the set parameter operation.
441          * @param param ID of the modified parameter. See {@link #PARAM_ROOM_LEVEL} ...
442          * @param value the new parameter value.
443          */
onParameterChange(EnvironmentalReverb effect, int status, int param, int value)444         void onParameterChange(EnvironmentalReverb effect, int status, int param, int value);
445     }
446 
447     /**
448      * Listener used internally to receive unformatted parameter change events from AudioEffect
449      * super class.
450      */
451     private class BaseParameterListener implements AudioEffect.OnParameterChangeListener {
BaseParameterListener()452         private BaseParameterListener() {
453 
454         }
onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value)455         public void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value) {
456             OnParameterChangeListener l = null;
457 
458             synchronized (mParamListenerLock) {
459                 if (mParamListener != null) {
460                     l = mParamListener;
461                 }
462             }
463             if (l != null) {
464                 int p = -1;
465                 int v = -1;
466 
467                 if (param.length == 4) {
468                     p = byteArrayToInt(param, 0);
469                 }
470                 if (value.length == 2) {
471                     v = (int)byteArrayToShort(value, 0);
472                 } else if (value.length == 4) {
473                     v = byteArrayToInt(value, 0);
474                 }
475                 if (p != -1 && v != -1) {
476                     l.onParameterChange(EnvironmentalReverb.this, status, p, v);
477                 }
478             }
479         }
480     }
481 
482     /**
483      * Registers an OnParameterChangeListener interface.
484      * @param listener OnParameterChangeListener interface registered
485      */
setParameterListener(OnParameterChangeListener listener)486     public void setParameterListener(OnParameterChangeListener listener) {
487         synchronized (mParamListenerLock) {
488             if (mParamListener == null) {
489                 mParamListener = listener;
490                 mBaseParamListener = new BaseParameterListener();
491                 super.setParameterListener(mBaseParamListener);
492             }
493         }
494     }
495 
496     /**
497      * The Settings class regroups all environmental reverb parameters. It is used in
498      * conjuntion with getProperties() and setProperties() methods to backup and restore
499      * all parameters in a single call.
500      */
501     public static class Settings {
502         public short roomLevel;
503         public short roomHFLevel;
504         public int decayTime;
505         public short decayHFRatio;
506         public short reflectionsLevel;
507         public int reflectionsDelay;
508         public short reverbLevel;
509         public int reverbDelay;
510         public short diffusion;
511         public short density;
512 
Settings()513         public Settings() {
514         }
515 
516         /**
517          * Settings class constructor from a key=value; pairs formatted string. The string is
518          * typically returned by Settings.toString() method.
519          * @throws IllegalArgumentException if the string is not correctly formatted.
520          */
Settings(String settings)521         public Settings(String settings) {
522             StringTokenizer st = new StringTokenizer(settings, "=;");
523             int tokens = st.countTokens();
524             if (st.countTokens() != 21) {
525                 throw new IllegalArgumentException("settings: " + settings);
526             }
527             String key = st.nextToken();
528             if (!key.equals("EnvironmentalReverb")) {
529                 throw new IllegalArgumentException(
530                         "invalid settings for EnvironmentalReverb: " + key);
531             }
532 
533             try {
534                 key = st.nextToken();
535                 if (!key.equals("roomLevel")) {
536                     throw new IllegalArgumentException("invalid key name: " + key);
537                 }
538                 roomLevel = Short.parseShort(st.nextToken());
539                 key = st.nextToken();
540                 if (!key.equals("roomHFLevel")) {
541                     throw new IllegalArgumentException("invalid key name: " + key);
542                 }
543                 roomHFLevel = Short.parseShort(st.nextToken());
544                 key = st.nextToken();
545                 if (!key.equals("decayTime")) {
546                     throw new IllegalArgumentException("invalid key name: " + key);
547                 }
548                 decayTime = Integer.parseInt(st.nextToken());
549                 key = st.nextToken();
550                 if (!key.equals("decayHFRatio")) {
551                     throw new IllegalArgumentException("invalid key name: " + key);
552                 }
553                 decayHFRatio = Short.parseShort(st.nextToken());
554                 key = st.nextToken();
555                 if (!key.equals("reflectionsLevel")) {
556                     throw new IllegalArgumentException("invalid key name: " + key);
557                 }
558                 reflectionsLevel = Short.parseShort(st.nextToken());
559                 key = st.nextToken();
560                 if (!key.equals("reflectionsDelay")) {
561                     throw new IllegalArgumentException("invalid key name: " + key);
562                 }
563                 reflectionsDelay = Integer.parseInt(st.nextToken());
564                 key = st.nextToken();
565                 if (!key.equals("reverbLevel")) {
566                     throw new IllegalArgumentException("invalid key name: " + key);
567                 }
568                 reverbLevel = Short.parseShort(st.nextToken());
569                 key = st.nextToken();
570                 if (!key.equals("reverbDelay")) {
571                     throw new IllegalArgumentException("invalid key name: " + key);
572                 }
573                 reverbDelay = Integer.parseInt(st.nextToken());
574                 key = st.nextToken();
575                 if (!key.equals("diffusion")) {
576                     throw new IllegalArgumentException("invalid key name: " + key);
577                 }
578                 diffusion = Short.parseShort(st.nextToken());
579                 key = st.nextToken();
580                 if (!key.equals("density")) {
581                     throw new IllegalArgumentException("invalid key name: " + key);
582                 }
583                 density = Short.parseShort(st.nextToken());
584              } catch (NumberFormatException nfe) {
585                 throw new IllegalArgumentException("invalid value for key: " + key);
586             }
587         }
588 
589         @Override
toString()590         public String toString() {
591             return new String (
592                     "EnvironmentalReverb"+
593                     ";roomLevel="+Short.toString(roomLevel)+
594                     ";roomHFLevel="+Short.toString(roomHFLevel)+
595                     ";decayTime="+Integer.toString(decayTime)+
596                     ";decayHFRatio="+Short.toString(decayHFRatio)+
597                     ";reflectionsLevel="+Short.toString(reflectionsLevel)+
598                     ";reflectionsDelay="+Integer.toString(reflectionsDelay)+
599                     ";reverbLevel="+Short.toString(reverbLevel)+
600                     ";reverbDelay="+Integer.toString(reverbDelay)+
601                     ";diffusion="+Short.toString(diffusion)+
602                     ";density="+Short.toString(density)
603                     );
604         }
605     };
606 
607     // Keep this in sync with sizeof(s_reverb_settings) defined in
608     // frameworks/base/include/media/EffectEnvironmentalReverbApi.h
609     static private int PROPERTY_SIZE = 26;
610 
611     /**
612      * Gets the environmental reverb properties. This method is useful when a snapshot of current
613      * reverb settings must be saved by the application.
614      * @return an EnvironmentalReverb.Settings object containing all current parameters values
615      * @throws IllegalStateException
616      * @throws IllegalArgumentException
617      * @throws UnsupportedOperationException
618      */
getProperties()619     public EnvironmentalReverb.Settings getProperties()
620     throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
621         byte[] param = new byte[PROPERTY_SIZE];
622         checkStatus(getParameter(PARAM_PROPERTIES, param));
623         Settings settings = new Settings();
624         settings.roomLevel = byteArrayToShort(param, 0);
625         settings.roomHFLevel = byteArrayToShort(param, 2);
626         settings.decayTime = byteArrayToInt(param, 4);
627         settings.decayHFRatio = byteArrayToShort(param, 8);
628         settings.reflectionsLevel = byteArrayToShort(param, 10);
629         settings.reflectionsDelay = byteArrayToInt(param, 12);
630         settings.reverbLevel = byteArrayToShort(param, 16);
631         settings.reverbDelay = byteArrayToInt(param, 18);
632         settings.diffusion = byteArrayToShort(param, 22);
633         settings.density = byteArrayToShort(param, 24);
634         return settings;
635     }
636 
637     /**
638      * Sets the environmental reverb properties. This method is useful when reverb settings have to
639      * be applied from a previous backup.
640      * @param settings a EnvironmentalReverb.Settings object containing the properties to apply
641      * @throws IllegalStateException
642      * @throws IllegalArgumentException
643      * @throws UnsupportedOperationException
644      */
setProperties(EnvironmentalReverb.Settings settings)645     public void setProperties(EnvironmentalReverb.Settings settings)
646     throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
647 
648         byte[] param = concatArrays(shortToByteArray(settings.roomLevel),
649                                     shortToByteArray(settings.roomHFLevel),
650                                     intToByteArray(settings.decayTime),
651                                     shortToByteArray(settings.decayHFRatio),
652                                     shortToByteArray(settings.reflectionsLevel),
653                                     intToByteArray(settings.reflectionsDelay),
654                                     shortToByteArray(settings.reverbLevel),
655                                     intToByteArray(settings.reverbDelay),
656                                     shortToByteArray(settings.diffusion),
657                                     shortToByteArray(settings.density));
658 
659         checkStatus(setParameter(PARAM_PROPERTIES, param));
660     }
661 }
662