Home | History | Annotate | Download | only in audiofx
      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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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          */
    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 {
    452         private BaseParameterListener() {
    453 
    454         }
    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      */
    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 
    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          */
    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
    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      */
    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      */
    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