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