1/*
2 * Copyright (C) 2018 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.annotation.NonNull;
20import android.annotation.Nullable;
21import android.media.AudioTrack;
22import android.media.MediaPlayer;
23import android.media.audiofx.AudioEffect;
24import android.util.Log;
25
26import java.nio.ByteBuffer;
27import java.nio.ByteOrder;
28import java.util.StringTokenizer;
29
30/**
31 * DynamicsProcessing is an audio effect for equalizing and changing dynamic range properties of the
32 * sound. It is composed of multiple stages including equalization, multi-band compression and
33 * limiter.
34 * <p>The number of bands and active stages is configurable, and most parameters can be controlled
35 * in realtime, such as gains, attack/release times, thresholds, etc.
36 * <p>The effect is instantiated and controlled by channels. Each channel has the same basic
37 * architecture, but all of their parameters are independent from other channels.
38 * <p>The basic channel configuration is:
39 * <pre>
40 *
41 *    Channel 0          Channel 1       ....       Channel N-1
42 *      Input              Input                       Input
43 *        |                  |                           |
44 *   +----v----+        +----v----+                 +----v----+
45 *   |inputGain|        |inputGain|                 |inputGain|
46 *   +---------+        +---------+                 +---------+
47 *        |                  |                           |
48 *  +-----v-----+      +-----v-----+               +-----v-----+
49 *  |   PreEQ   |      |   PreEQ   |               |   PreEQ   |
50 *  +-----------+      +-----------+               +-----------+
51 *        |                  |                           |
52 *  +-----v-----+      +-----v-----+               +-----v-----+
53 *  |    MBC    |      |    MBC    |               |    MBC    |
54 *  +-----------+      +-----------+               +-----------+
55 *        |                  |                           |
56 *  +-----v-----+      +-----v-----+               +-----v-----+
57 *  |  PostEQ   |      |  PostEQ   |               |  PostEQ   |
58 *  +-----------+      +-----------+               +-----------+
59 *        |                  |                           |
60 *  +-----v-----+      +-----v-----+               +-----v-----+
61 *  |  Limiter  |      |  Limiter  |               |  Limiter  |
62 *  +-----------+      +-----------+               +-----------+
63 *        |                  |                           |
64 *     Output             Output                      Output
65 * </pre>
66 *
67 * <p>Where the stages are:
68 * inputGain: input gain factor in decibels (dB). 0 dB means no change in level.
69 * PreEQ:  Multi-band Equalizer.
70 * MBC:    Multi-band Compressor
71 * PostEQ: Multi-band Equalizer
72 * Limiter: Single band compressor/limiter.
73 *
74 * <p>An application creates a DynamicsProcessing object to instantiate and control this audio
75 * effect in the audio framework. A DynamicsProcessor.Config and DynamicsProcessor.Config.Builder
76 * are available to help configure the multiple stages and each band parameters if desired.
77 * <p>See each stage documentation for further details.
78 * <p>If no Config is specified during creation, a default configuration is chosen.
79 * <p>To attach the DynamicsProcessing to a particular AudioTrack or MediaPlayer,
80 * specify the audio session ID of this AudioTrack or MediaPlayer when constructing the effect
81 * (see {@link AudioTrack#getAudioSessionId()} and {@link MediaPlayer#getAudioSessionId()}).
82 *
83 * <p>To attach the DynamicsProcessing to a particular AudioTrack or MediaPlayer, specify the audio
84 * session ID of this AudioTrack or MediaPlayer when constructing the DynamicsProcessing.
85 * <p>See {@link android.media.MediaPlayer#getAudioSessionId()} for details on audio sessions.
86 * <p>See {@link android.media.audiofx.AudioEffect} class for more details on controlling audio
87 * effects.
88 */
89
90public final class DynamicsProcessing extends AudioEffect {
91
92    private final static String TAG = "DynamicsProcessing";
93
94    // These parameter constants must be synchronized with those in
95    // /system/media/audio_effects/include/audio_effects/effect_dynamicsprocessing.h
96    private static final int PARAM_GET_CHANNEL_COUNT = 0x10;
97    private static final int PARAM_INPUT_GAIN = 0x20;
98    private static final int PARAM_ENGINE_ARCHITECTURE = 0x30;
99    private static final int PARAM_PRE_EQ = 0x40;
100    private static final int PARAM_PRE_EQ_BAND = 0x45;
101    private static final int PARAM_MBC = 0x50;
102    private static final int PARAM_MBC_BAND = 0x55;
103    private static final int PARAM_POST_EQ = 0x60;
104    private static final int PARAM_POST_EQ_BAND = 0x65;
105    private static final int PARAM_LIMITER = 0x70;
106
107    /**
108     * Index of variant that favors frequency resolution. Frequency domain based implementation.
109     */
110    public static final int VARIANT_FAVOR_FREQUENCY_RESOLUTION  = 0;
111
112    /**
113     * Index of variant that favors time resolution resolution. Time domain based implementation.
114     */
115    public static final int VARIANT_FAVOR_TIME_RESOLUTION       = 1;
116
117    /**
118     * Maximum expected channels to be reported by effect
119     */
120    private static final int CHANNEL_COUNT_MAX = 32;
121
122    /**
123     * Number of channels in effect architecture
124     */
125    private int mChannelCount = 0;
126
127    /**
128     * Registered listener for parameter changes.
129     */
130    private OnParameterChangeListener mParamListener = null;
131
132    /**
133     * Listener used internally to to receive raw parameter change events
134     * from AudioEffect super class
135     */
136    private BaseParameterListener mBaseParamListener = null;
137
138    /**
139     * Lock for access to mParamListener
140     */
141    private final Object mParamListenerLock = new Object();
142
143    /**
144     * Class constructor.
145     * @param audioSession system-wide unique audio session identifier. The DynamicsProcessing
146     * will be attached to the MediaPlayer or AudioTrack in the same audio session.
147     */
148    public DynamicsProcessing(int audioSession) {
149        this(0 /*priority*/, audioSession);
150    }
151
152    /**
153     * @hide
154     * Class constructor for the DynamicsProcessing audio effect.
155     * @param priority the priority level requested by the application for controlling the
156     * DynamicsProcessing engine. As the same engine can be shared by several applications,
157     * this parameter indicates how much the requesting application needs control of effect
158     * parameters. The normal priority is 0, above normal is a positive number, below normal a
159     * negative number.
160     * @param audioSession system-wide unique audio session identifier. The DynamicsProcessing
161     * will be attached to the MediaPlayer or AudioTrack in the same audio session.
162     */
163    public DynamicsProcessing(int priority, int audioSession) {
164        this(priority, audioSession, null);
165    }
166
167    /**
168     * Class constructor for the DynamicsProcessing audio effect
169     * @param priority the priority level requested by the application for controlling the
170     * DynamicsProcessing engine. As the same engine can be shared by several applications,
171     * this parameter indicates how much the requesting application needs control of effect
172     * parameters. The normal priority is 0, above normal is a positive number, below normal a
173     * negative number.
174     * @param audioSession system-wide unique audio session identifier. The DynamicsProcessing
175     * will be attached to the MediaPlayer or AudioTrack in the same audio session.
176     * @param cfg Config object used to setup the audio effect, including bands per stage, and
177     * specific parameters for each stage/band. Use
178     * {@link android.media.audiofx.DynamicsProcessing.Config.Builder} to create a
179     * Config object that suits your needs. A null cfg parameter will create and use a default
180     * configuration for the effect
181     */
182    public DynamicsProcessing(int priority, int audioSession, @Nullable Config cfg) {
183        super(EFFECT_TYPE_DYNAMICS_PROCESSING, EFFECT_TYPE_NULL, priority, audioSession);
184        if (audioSession == 0) {
185            Log.w(TAG, "WARNING: attaching a DynamicsProcessing to global output mix is"
186                    + "deprecated!");
187        }
188        final Config config;
189        mChannelCount = getChannelCount();
190        if (cfg == null) {
191            //create a default configuration and effect, with the number of channels this effect has
192            DynamicsProcessing.Config.Builder builder =
193                    new DynamicsProcessing.Config.Builder(
194                            CONFIG_DEFAULT_VARIANT,
195                            mChannelCount,
196                            CONFIG_DEFAULT_USE_PREEQ,
197                            CONFIG_DEFAULT_PREEQ_BANDS,
198                            CONFIG_DEFAULT_USE_MBC,
199                            CONFIG_DEFAULT_MBC_BANDS,
200                            CONFIG_DEFAULT_USE_POSTEQ,
201                            CONFIG_DEFAULT_POSTEQ_BANDS,
202                            CONFIG_DEFAULT_USE_LIMITER);
203            config = builder.build();
204        } else {
205            //validate channels are ok. decide what to do: replicate channels if more
206            config = new DynamicsProcessing.Config(mChannelCount, cfg);
207        }
208
209        //configure engine
210        setEngineArchitecture(config.getVariant(),
211                config.getPreferredFrameDuration(),
212                config.isPreEqInUse(),
213                config.getPreEqBandCount(),
214                config.isMbcInUse(),
215                config.getMbcBandCount(),
216                config.isPostEqInUse(),
217                config.getPostEqBandCount(),
218                config.isLimiterInUse());
219        //update all the parameters
220        for (int ch = 0; ch < mChannelCount; ch++) {
221            updateEngineChannelByChannelIndex(ch, config.getChannelByChannelIndex(ch));
222        }
223    }
224
225    /**
226     * Returns the Config object used to setup this effect.
227     * @return Config Current Config object used to setup this DynamicsProcessing effect.
228     */
229    public Config getConfig() {
230        //Query engine architecture to create config object
231        Number[] params = { PARAM_ENGINE_ARCHITECTURE };
232        Number[] values = { 0 /*0 variant */,
233                0.0f /* 1 preferredFrameDuration */,
234                0 /*2 preEqInUse */,
235                0 /*3 preEqBandCount */,
236                0 /*4 mbcInUse */,
237                0 /*5 mbcBandCount*/,
238                0 /*6 postEqInUse */,
239                0 /*7 postEqBandCount */,
240                0 /*8 limiterInUse */};
241        byte[] paramBytes = numberArrayToByteArray(params);
242        byte[] valueBytes = numberArrayToByteArray(values); //just interest in the byte size.
243        getParameter(paramBytes, valueBytes);
244        byteArrayToNumberArray(valueBytes, values);
245        DynamicsProcessing.Config.Builder builder =
246                new DynamicsProcessing.Config.Builder(
247                        values[0].intValue(),
248                        mChannelCount,
249                        values[2].intValue() > 0 /*use preEQ*/,
250                        values[3].intValue() /*pre eq bands*/,
251                        values[4].intValue() > 0 /*use mbc*/,
252                        values[5].intValue() /*mbc bands*/,
253                        values[6].intValue() > 0 /*use postEQ*/,
254                        values[7].intValue()/*postEq bands*/,
255                        values[8].intValue() > 0 /*use Limiter*/).
256                setPreferredFrameDuration(values[1].floatValue());
257        Config config = builder.build();
258        for (int ch = 0; ch < mChannelCount; ch++) {
259            Channel channel = queryEngineByChannelIndex(ch);
260            config.setChannelTo(ch, channel);
261        }
262        return config;
263    }
264
265
266    private static final int CONFIG_DEFAULT_VARIANT = VARIANT_FAVOR_FREQUENCY_RESOLUTION;
267    private static final boolean CONFIG_DEFAULT_USE_PREEQ = true;
268    private static final int CONFIG_DEFAULT_PREEQ_BANDS = 6;
269    private static final boolean CONFIG_DEFAULT_USE_MBC = true;
270    private static final int CONFIG_DEFAULT_MBC_BANDS = 6;
271    private static final boolean CONFIG_DEFAULT_USE_POSTEQ = true;
272    private static final int CONFIG_DEFAULT_POSTEQ_BANDS = 6;
273    private static final boolean CONFIG_DEFAULT_USE_LIMITER = true;
274
275    private static final float CHANNEL_DEFAULT_INPUT_GAIN = 0; // dB
276    private static final float CONFIG_PREFERRED_FRAME_DURATION_MS = 10.0f; //milliseconds
277
278    private static final float EQ_DEFAULT_GAIN = 0; // dB
279    private static final boolean PREEQ_DEFAULT_ENABLED = true;
280    private static final boolean POSTEQ_DEFAULT_ENABLED = true;
281
282    private static final boolean MBC_DEFAULT_ENABLED = true;
283    private static final float MBC_DEFAULT_ATTACK_TIME = 3; // ms
284    private static final float MBC_DEFAULT_RELEASE_TIME = 80; // ms
285    private static final float MBC_DEFAULT_RATIO = 1; // N:1
286    private static final float MBC_DEFAULT_THRESHOLD = -45; // dB
287    private static final float MBC_DEFAULT_KNEE_WIDTH = 0; // dB
288    private static final float MBC_DEFAULT_NOISE_GATE_THRESHOLD = -90; // dB
289    private static final float MBC_DEFAULT_EXPANDER_RATIO = 1; // 1:N
290    private static final float MBC_DEFAULT_PRE_GAIN = 0; // dB
291    private static final float MBC_DEFAULT_POST_GAIN = 0; // dB
292
293    private static final boolean LIMITER_DEFAULT_ENABLED = true;
294    private static final int LIMITER_DEFAULT_LINK_GROUP = 0;//;
295    private static final float LIMITER_DEFAULT_ATTACK_TIME = 1; // ms
296    private static final float LIMITER_DEFAULT_RELEASE_TIME = 60; // ms
297    private static final float LIMITER_DEFAULT_RATIO = 10; // N:1
298    private static final float LIMITER_DEFAULT_THRESHOLD = -2; // dB
299    private static final float LIMITER_DEFAULT_POST_GAIN = 0; // dB
300
301    private static final float DEFAULT_MIN_FREQUENCY = 220; // Hz
302    private static final float DEFAULT_MAX_FREQUENCY = 20000; // Hz
303    private static final float mMinFreqLog = (float)Math.log10(DEFAULT_MIN_FREQUENCY);
304    private static final float mMaxFreqLog = (float)Math.log10(DEFAULT_MAX_FREQUENCY);
305
306    /**
307     * base class for the different stages.
308     */
309    public static class Stage {
310        private boolean mInUse;
311        private boolean mEnabled;
312        /**
313         * Class constructor for stage
314         * @param inUse true if this stage is set to be used. False otherwise. Stages that are not
315         * set "inUse" at initialization time are not available to be used at any time.
316         * @param enabled true if this stage is currently used to process sound. When disabled,
317         * the stage is bypassed and the sound is copied unaltered from input to output.
318         */
319        public Stage(boolean inUse, boolean enabled) {
320            mInUse = inUse;
321            mEnabled = enabled;
322        }
323
324        /**
325         * returns enabled state of the stage
326         * @return true if stage is enabled for processing, false otherwise
327         */
328        public boolean isEnabled() {
329            return mEnabled;
330        }
331        /**
332         * sets enabled state of the stage
333         * @param enabled true for enabled, false otherwise
334         */
335        public void setEnabled(boolean enabled) {
336            mEnabled = enabled;
337        }
338
339        /**
340         * returns inUse state of the stage.
341         * @return inUse state of the stage. True if this stage is currently used to process sound.
342         * When false, the stage is bypassed and the sound is copied unaltered from input to output.
343         */
344        public boolean isInUse() {
345            return mInUse;
346        }
347
348        @Override
349        public String toString() {
350            StringBuilder sb = new StringBuilder();
351            sb.append(String.format(" Stage InUse: %b\n", isInUse()));
352            if (isInUse()) {
353                sb.append(String.format(" Stage Enabled: %b\n", mEnabled));
354            }
355            return sb.toString();
356        }
357    }
358
359    /**
360     * Base class for stages that hold bands
361     */
362    public static class BandStage extends Stage{
363        private int mBandCount;
364        /**
365         * Class constructor for BandStage
366         * @param inUse true if this stage is set to be used. False otherwise. Stages that are not
367         * set "inUse" at initialization time are not available to be used at any time.
368         * @param enabled true if this stage is currently used to process sound. When disabled,
369         * the stage is bypassed and the sound is copied unaltered from input to output.
370         * @param bandCount number of bands this stage will handle. If stage is not inUse, bandcount
371         * is set to 0
372         */
373        public BandStage(boolean inUse, boolean enabled, int bandCount) {
374            super(inUse, enabled);
375            mBandCount = isInUse() ? bandCount : 0;
376        }
377
378        /**
379         * gets number of bands held in this stage
380         * @return number of bands held in this stage
381         */
382        public int getBandCount() {
383            return mBandCount;
384        }
385
386        @Override
387        public String toString() {
388            StringBuilder sb = new StringBuilder();
389            sb.append(super.toString());
390            if (isInUse()) {
391                sb.append(String.format(" Band Count: %d\n", mBandCount));
392            }
393            return sb.toString();
394        }
395    }
396
397    /**
398     * Base class for bands
399     */
400    public static class BandBase {
401        private boolean mEnabled;
402        private float mCutoffFrequency;
403        /**
404         * Class constructor for BandBase
405         * @param enabled true if this band is currently used to process sound. When false,
406         * the band is effectively muted and sound set to zero.
407         * @param cutoffFrequency topmost frequency number (in Hz) this band will process. The
408         * effective bandwidth for the band is then computed using this and the previous band
409         * topmost frequency (or 0 Hz for band number 0). Frequencies are expected to increase with
410         * band number, thus band 0 cutoffFrequency <= band 1 cutoffFrequency, and so on.
411         */
412        public BandBase(boolean enabled, float cutoffFrequency) {
413            mEnabled = enabled;
414            mCutoffFrequency = cutoffFrequency;
415        }
416
417        @Override
418        public String toString() {
419            StringBuilder sb = new StringBuilder();
420            sb.append(String.format(" Enabled: %b\n", mEnabled));
421            sb.append(String.format(" CutoffFrequency: %f\n", mCutoffFrequency));
422            return sb.toString();
423        }
424
425        /**
426         * returns enabled state of the band
427         * @return true if bands is enabled for processing, false otherwise
428         */
429        public boolean isEnabled() {
430            return mEnabled;
431        }
432        /**
433         * sets enabled state of the band
434         * @param enabled true for enabled, false otherwise
435         */
436        public void setEnabled(boolean enabled) {
437            mEnabled = enabled;
438        }
439
440        /**
441         * gets cutoffFrequency for this band in Hertz (Hz)
442         * @return cutoffFrequency for this band in Hertz (Hz)
443         */
444        public float getCutoffFrequency() {
445            return mCutoffFrequency;
446        }
447
448        /**
449         * sets topmost frequency number (in Hz) this band will process. The
450         * effective bandwidth for the band is then computed using this and the previous band
451         * topmost frequency (or 0 Hz for band number 0). Frequencies are expected to increase with
452         * band number, thus band 0 cutoffFrequency <= band 1 cutoffFrequency, and so on.
453         * @param frequency
454         */
455        public void setCutoffFrequency(float frequency) {
456            mCutoffFrequency = frequency;
457        }
458    }
459
460    /**
461     * Class for Equalizer Bands
462     * Equalizer bands have three controllable parameters: enabled/disabled, cutoffFrequency and
463     * gain
464     */
465    public final static class EqBand extends BandBase {
466        private float mGain;
467        /**
468         * Class constructor for EqBand
469         * @param enabled true if this band is currently used to process sound. When false,
470         * the band is effectively muted and sound set to zero.
471         * @param cutoffFrequency topmost frequency number (in Hz) this band will process. The
472         * effective bandwidth for the band is then computed using this and the previous band
473         * topmost frequency (or 0 Hz for band number 0). Frequencies are expected to increase with
474         * band number, thus band 0 cutoffFrequency <= band 1 cutoffFrequency, and so on.
475         * @param gain of equalizer band in decibels (dB). A gain of 0 dB means no change in level.
476         */
477        public EqBand(boolean enabled, float cutoffFrequency, float gain) {
478            super(enabled, cutoffFrequency);
479            mGain = gain;
480        }
481
482        /**
483         * Class constructor for EqBand
484         * @param cfg copy constructor
485         */
486        public EqBand(EqBand cfg) {
487            super(cfg.isEnabled(), cfg.getCutoffFrequency());
488            mGain = cfg.mGain;
489        }
490
491        @Override
492        public String toString() {
493            StringBuilder sb = new StringBuilder();
494            sb.append(super.toString());
495            sb.append(String.format(" Gain: %f\n", mGain));
496            return sb.toString();
497        }
498
499        /**
500         * gets current gain of band in decibels (dB)
501         * @return current gain of band in decibels (dB)
502         */
503        public float getGain() {
504            return mGain;
505        }
506
507        /**
508         * sets current gain of band in decibels (dB)
509         * @param gain desired in decibels (db)
510         */
511        public void setGain(float gain) {
512            mGain = gain;
513        }
514    }
515
516    /**
517     * Class for Multi-Band compressor bands
518     * MBC bands have multiple controllable parameters: enabled/disabled, cutoffFrequency,
519     * attackTime, releaseTime, ratio, threshold, kneeWidth, noiseGateThreshold, expanderRatio,
520     * preGain and postGain.
521     */
522    public final static class MbcBand extends BandBase{
523        private float mAttackTime;
524        private float mReleaseTime;
525        private float mRatio;
526        private float mThreshold;
527        private float mKneeWidth;
528        private float mNoiseGateThreshold;
529        private float mExpanderRatio;
530        private float mPreGain;
531        private float mPostGain;
532        /**
533         * Class constructor for MbcBand
534         * @param enabled true if this band is currently used to process sound. When false,
535         * the band is effectively muted and sound set to zero.
536         * @param cutoffFrequency topmost frequency number (in Hz) this band will process. The
537         * effective bandwidth for the band is then computed using this and the previous band
538         * topmost frequency (or 0 Hz for band number 0). Frequencies are expected to increase with
539         * band number, thus band 0 cutoffFrequency <= band 1 cutoffFrequency, and so on.
540         * @param attackTime Attack Time for compressor in milliseconds (ms)
541         * @param releaseTime Release Time for compressor in milliseconds (ms)
542         * @param ratio Compressor ratio (N:1) (input:output)
543         * @param threshold Compressor threshold measured in decibels (dB) from 0 dB Full Scale
544         * (dBFS).
545         * @param kneeWidth Width in decibels (dB) around compressor threshold point.
546         * @param noiseGateThreshold Noise gate threshold in decibels (dB) from 0 dB Full Scale
547         * (dBFS).
548         * @param expanderRatio Expander ratio (1:N) (input:output) for signals below the Noise Gate
549         * Threshold.
550         * @param preGain Gain applied to the signal BEFORE the compression.
551         * @param postGain Gain applied to the signal AFTER compression.
552         */
553        public MbcBand(boolean enabled, float cutoffFrequency, float attackTime, float releaseTime,
554                float ratio, float threshold, float kneeWidth, float noiseGateThreshold,
555                float expanderRatio, float preGain, float postGain) {
556            super(enabled, cutoffFrequency);
557            mAttackTime = attackTime;
558            mReleaseTime = releaseTime;
559            mRatio = ratio;
560            mThreshold = threshold;
561            mKneeWidth = kneeWidth;
562            mNoiseGateThreshold = noiseGateThreshold;
563            mExpanderRatio = expanderRatio;
564            mPreGain = preGain;
565            mPostGain = postGain;
566        }
567
568        /**
569         * Class constructor for MbcBand
570         * @param cfg copy constructor
571         */
572        public MbcBand(MbcBand cfg) {
573            super(cfg.isEnabled(), cfg.getCutoffFrequency());
574            mAttackTime = cfg.mAttackTime;
575            mReleaseTime = cfg.mReleaseTime;
576            mRatio = cfg.mRatio;
577            mThreshold = cfg.mThreshold;
578            mKneeWidth = cfg.mKneeWidth;
579            mNoiseGateThreshold = cfg.mNoiseGateThreshold;
580            mExpanderRatio = cfg.mExpanderRatio;
581            mPreGain = cfg.mPreGain;
582            mPostGain = cfg.mPostGain;
583        }
584
585        @Override
586        public String toString() {
587            StringBuilder sb = new StringBuilder();
588            sb.append(super.toString());
589            sb.append(String.format(" AttackTime: %f (ms)\n", mAttackTime));
590            sb.append(String.format(" ReleaseTime: %f (ms)\n", mReleaseTime));
591            sb.append(String.format(" Ratio: 1:%f\n", mRatio));
592            sb.append(String.format(" Threshold: %f (dB)\n", mThreshold));
593            sb.append(String.format(" NoiseGateThreshold: %f(dB)\n", mNoiseGateThreshold));
594            sb.append(String.format(" ExpanderRatio: %f:1\n", mExpanderRatio));
595            sb.append(String.format(" PreGain: %f (dB)\n", mPreGain));
596            sb.append(String.format(" PostGain: %f (dB)\n", mPostGain));
597            return sb.toString();
598        }
599
600        /**
601         * gets attack time for compressor in milliseconds (ms)
602         * @return attack time for compressor in milliseconds (ms)
603         */
604        public float getAttackTime() { return mAttackTime; }
605        /**
606         * sets attack time for compressor in milliseconds (ms)
607         * @param attackTime desired for compressor in milliseconds (ms)
608         */
609        public void setAttackTime(float attackTime) { mAttackTime = attackTime; }
610        /**
611         * gets release time for compressor in milliseconds (ms)
612         * @return release time for compressor in milliseconds (ms)
613         */
614        public float getReleaseTime() { return mReleaseTime; }
615        /**
616         * sets release time for compressor in milliseconds (ms)
617         * @param releaseTime desired for compressor in milliseconds (ms)
618         */
619        public void setReleaseTime(float releaseTime) { mReleaseTime = releaseTime; }
620        /**
621         * gets the compressor ratio (N:1)
622         * @return compressor ratio (N:1)
623         */
624        public float getRatio() { return mRatio; }
625        /**
626         * sets compressor ratio (N:1)
627         * @param ratio desired for the compressor (N:1)
628         */
629        public void setRatio(float ratio) { mRatio = ratio; }
630        /**
631         * gets the compressor threshold measured in decibels (dB) from 0 dB Full Scale (dBFS).
632         * Thresholds are negative. A threshold of 0 dB means no compression will take place.
633         * @return compressor threshold in decibels (dB)
634         */
635        public float getThreshold() { return mThreshold; }
636        /**
637         * sets the compressor threshold measured in decibels (dB) from 0 dB Full Scale (dBFS).
638         * Thresholds are negative. A threshold of 0 dB means no compression will take place.
639         * @param threshold desired for compressor in decibels(dB)
640         */
641        public void setThreshold(float threshold) { mThreshold = threshold; }
642        /**
643         * get Knee Width in decibels (dB) around compressor threshold point. Widths are always
644         * positive, with higher values representing a wider area of transition from the linear zone
645         * to the compression zone. A knee of 0 dB means a more abrupt transition.
646         * @return Knee Width in decibels (dB)
647         */
648        public float getKneeWidth() { return mKneeWidth; }
649        /**
650         * sets knee width in decibels (dB). See
651         * {@link android.media.audiofx.DynamicsProcessing.MbcBand#getKneeWidth} for more
652         * information.
653         * @param kneeWidth desired in decibels (dB)
654         */
655        public void setKneeWidth(float kneeWidth) { mKneeWidth = kneeWidth; }
656        /**
657         * gets the noise gate threshold in decibels (dB) from 0 dB Full Scale (dBFS). Noise gate
658         * thresholds are negative. Signals below this level will be expanded according the
659         * expanderRatio parameter. A Noise Gate Threshold of -75 dB means very quiet signals might
660         * be effectively removed from the signal.
661         * @return Noise Gate Threshold in decibels (dB)
662         */
663        public float getNoiseGateThreshold() { return mNoiseGateThreshold; }
664        /**
665         * sets noise gate threshod in decibels (dB). See
666         * {@link android.media.audiofx.DynamicsProcessing.MbcBand#getNoiseGateThreshold} for more
667         * information.
668         * @param noiseGateThreshold desired in decibels (dB)
669         */
670        public void setNoiseGateThreshold(float noiseGateThreshold) {
671            mNoiseGateThreshold = noiseGateThreshold; }
672        /**
673         * gets Expander ratio (1:N) for signals below the Noise Gate Threshold.
674         * @return Expander ratio (1:N)
675         */
676        public float getExpanderRatio() { return mExpanderRatio; }
677        /**
678         * sets Expander ratio (1:N) for signals below the Noise Gate Threshold.
679         * @param expanderRatio desired expander ratio (1:N)
680         */
681        public void setExpanderRatio(float expanderRatio) { mExpanderRatio = expanderRatio; }
682        /**
683         * gets the gain applied to the signal BEFORE the compression. Measured in decibels (dB)
684         * where 0 dB means no level change.
685         * @return preGain value in decibels (dB)
686         */
687        public float getPreGain() { return mPreGain; }
688        /**
689         * sets the gain to be applied to the signal BEFORE the compression, measured in decibels
690         * (dB), where 0 dB means no level change.
691         * @param preGain desired in decibels (dB)
692         */
693        public void setPreGain(float preGain) { mPreGain = preGain; }
694        /**
695         * gets the gain applied to the signal AFTER compression. Measured in decibels (dB) where 0
696         * dB means no level change
697         * @return postGain value in decibels (dB)
698         */
699        public float getPostGain() { return mPostGain; }
700        /**
701         * sets the gain to be applied to the siganl AFTER the compression. Measured in decibels
702         * (dB), where 0 dB means no level change.
703         * @param postGain desired value in decibels (dB)
704         */
705        public void setPostGain(float postGain) { mPostGain = postGain; }
706    }
707
708    /**
709     * Class for Equalizer stage
710     */
711    public final static class Eq extends BandStage {
712        private final EqBand[] mBands;
713        /**
714         * Class constructor for Equalizer (Eq) stage
715         * @param inUse true if Eq stage will be used, false otherwise.
716         * @param enabled true if Eq stage is enabled/disabled. This can be changed while effect is
717         * running
718         * @param bandCount number of bands for this Equalizer stage. Can't be changed while effect
719         * is running
720         */
721        public Eq(boolean inUse, boolean enabled, int bandCount) {
722            super(inUse, enabled, bandCount);
723            if (isInUse()) {
724                mBands = new EqBand[bandCount];
725                for (int b = 0; b < bandCount; b++) {
726                    float freq = DEFAULT_MAX_FREQUENCY;
727                    if (bandCount > 1) {
728                        freq = (float)Math.pow(10, mMinFreqLog +
729                                b * (mMaxFreqLog - mMinFreqLog)/(bandCount -1));
730                    }
731                    mBands[b] = new EqBand(true, freq, EQ_DEFAULT_GAIN);
732                }
733            } else {
734                mBands = null;
735            }
736        }
737        /**
738         * Class constructor for Eq stage
739         * @param cfg copy constructor
740         */
741        public Eq(Eq cfg) {
742            super(cfg.isInUse(), cfg.isEnabled(), cfg.getBandCount());
743            if (isInUse()) {
744                mBands = new EqBand[cfg.mBands.length];
745                for (int b = 0; b < mBands.length; b++) {
746                    mBands[b] = new EqBand(cfg.mBands[b]);
747                }
748            } else {
749                mBands = null;
750            }
751        }
752
753        @Override
754        public String toString() {
755            StringBuilder sb = new StringBuilder();
756            sb.append(super.toString());
757            if (isInUse()) {
758                sb.append("--->EqBands: " + mBands.length + "\n");
759                for (int b = 0; b < mBands.length; b++) {
760                    sb.append(String.format("  Band %d\n", b));
761                    sb.append(mBands[b].toString());
762                }
763            }
764            return sb.toString();
765        }
766        /**
767         * Helper function to check if band index is within range
768         * @param band index to check
769         */
770        private void checkBand(int band) {
771            if (mBands == null || band < 0 || band >= mBands.length) {
772                throw new IllegalArgumentException("band index " + band +" out of bounds");
773            }
774        }
775        /**
776         * Sets EqBand object for given band index
777         * @param band index of band to be modified
778         * @param bandCfg EqBand object.
779         */
780        public void setBand(int band, EqBand bandCfg) {
781            checkBand(band);
782            mBands[band] = new EqBand(bandCfg);
783        }
784        /**
785         * Gets EqBand object for band of interest.
786         * @param band index of band of interest
787         * @return EqBand Object
788         */
789        public EqBand getBand(int band) {
790            checkBand(band);
791            return mBands[band];
792        }
793    }
794
795    /**
796     * Class for Multi-Band Compressor (MBC) stage
797     */
798    public final static class Mbc extends BandStage {
799        private final MbcBand[] mBands;
800        /**
801         * Constructor for Multi-Band Compressor (MBC) stage
802         * @param inUse true if MBC stage will be used, false otherwise.
803         * @param enabled true if MBC stage is enabled/disabled. This can be changed while effect
804         * is running
805         * @param bandCount number of bands for this MBC stage. Can't be changed while effect is
806         * running
807         */
808        public Mbc(boolean inUse, boolean enabled, int bandCount) {
809            super(inUse, enabled, bandCount);
810            if (isInUse()) {
811                mBands = new MbcBand[bandCount];
812                for (int b = 0; b < bandCount; b++) {
813                    float freq = DEFAULT_MAX_FREQUENCY;
814                    if (bandCount > 1) {
815                        freq = (float)Math.pow(10, mMinFreqLog +
816                                b * (mMaxFreqLog - mMinFreqLog)/(bandCount -1));
817                    }
818                    mBands[b] = new MbcBand(true, freq, MBC_DEFAULT_ATTACK_TIME,
819                            MBC_DEFAULT_RELEASE_TIME, MBC_DEFAULT_RATIO,
820                            MBC_DEFAULT_THRESHOLD, MBC_DEFAULT_KNEE_WIDTH,
821                            MBC_DEFAULT_NOISE_GATE_THRESHOLD, MBC_DEFAULT_EXPANDER_RATIO,
822                            MBC_DEFAULT_PRE_GAIN, MBC_DEFAULT_POST_GAIN);
823                }
824            } else {
825                mBands = null;
826            }
827        }
828        /**
829         * Class constructor for MBC stage
830         * @param cfg copy constructor
831         */
832        public Mbc(Mbc cfg) {
833            super(cfg.isInUse(), cfg.isEnabled(), cfg.getBandCount());
834            if (isInUse()) {
835                mBands = new MbcBand[cfg.mBands.length];
836                for (int b = 0; b < mBands.length; b++) {
837                    mBands[b] = new MbcBand(cfg.mBands[b]);
838                }
839            } else {
840                mBands = null;
841            }
842        }
843
844        @Override
845        public String toString() {
846            StringBuilder sb = new StringBuilder();
847            sb.append(super.toString());
848            if (isInUse()) {
849                sb.append("--->MbcBands: " + mBands.length + "\n");
850                for (int b = 0; b < mBands.length; b++) {
851                    sb.append(String.format("  Band %d\n", b));
852                    sb.append(mBands[b].toString());
853                }
854            }
855            return sb.toString();
856        }
857        /**
858         * Helper function to check if band index is within range
859         * @param band index to check
860         */
861        private void checkBand(int band) {
862            if (mBands == null || band < 0 || band >= mBands.length) {
863                throw new IllegalArgumentException("band index " + band +" out of bounds");
864            }
865        }
866        /**
867         * Sets MbcBand object for given band index
868         * @param band index of band to be modified
869         * @param bandCfg MbcBand object.
870         */
871        public void setBand(int band, MbcBand bandCfg) {
872            checkBand(band);
873            mBands[band] = new MbcBand(bandCfg);
874        }
875        /**
876         * Gets MbcBand object for band of interest.
877         * @param band index of band of interest
878         * @return MbcBand Object
879         */
880        public MbcBand getBand(int band) {
881            checkBand(band);
882            return mBands[band];
883        }
884    }
885
886    /**
887     * Class for Limiter Stage
888     * Limiter is a single band compressor at the end of the processing chain, commonly used to
889     * protect the signal from overloading and distortion. Limiters have multiple controllable
890     * parameters: enabled/disabled, linkGroup, attackTime, releaseTime, ratio, threshold, and
891     * postGain.
892     * <p>Limiters can be linked in groups across multiple channels. Linked limiters will trigger
893     * the same limiting if any of the linked limiters starts compressing.
894     */
895    public final static class Limiter extends Stage {
896        private int mLinkGroup;
897        private float mAttackTime;
898        private float mReleaseTime;
899        private float mRatio;
900        private float mThreshold;
901        private float mPostGain;
902
903        /**
904         * Class constructor for Limiter Stage
905         * @param inUse true if MBC stage will be used, false otherwise.
906         * @param enabled true if MBC stage is enabled/disabled. This can be changed while effect
907         * is running
908         * @param linkGroup index of group assigned to this Limiter. Only limiters that share the
909         * same linkGroup index will react together.
910         * @param attackTime Attack Time for limiter compressor in milliseconds (ms)
911         * @param releaseTime Release Time for limiter compressor in milliseconds (ms)
912         * @param ratio Limiter Compressor ratio (N:1) (input:output)
913         * @param threshold Limiter Compressor threshold measured in decibels (dB) from 0 dB Full
914         * Scale (dBFS).
915         * @param postGain Gain applied to the signal AFTER compression.
916         */
917        public Limiter(boolean inUse, boolean enabled, int linkGroup, float attackTime,
918                float releaseTime, float ratio, float threshold, float postGain) {
919            super(inUse, enabled);
920            mLinkGroup = linkGroup;
921            mAttackTime = attackTime;
922            mReleaseTime = releaseTime;
923            mRatio = ratio;
924            mThreshold = threshold;
925            mPostGain = postGain;
926        }
927
928        /**
929         * Class Constructor for Limiter
930         * @param cfg copy constructor
931         */
932        public Limiter(Limiter cfg) {
933            super(cfg.isInUse(), cfg.isEnabled());
934            mLinkGroup = cfg.mLinkGroup;
935            mAttackTime = cfg.mAttackTime;
936            mReleaseTime = cfg.mReleaseTime;
937            mRatio = cfg.mRatio;
938            mThreshold = cfg.mThreshold;
939            mPostGain = cfg.mPostGain;
940        }
941
942        @Override
943        public String toString() {
944            StringBuilder sb = new StringBuilder();
945            sb.append(super.toString());
946            if (isInUse()) {
947                sb.append(String.format(" LinkGroup: %d (group)\n", mLinkGroup));
948                sb.append(String.format(" AttackTime: %f (ms)\n", mAttackTime));
949                sb.append(String.format(" ReleaseTime: %f (ms)\n", mReleaseTime));
950                sb.append(String.format(" Ratio: 1:%f\n", mRatio));
951                sb.append(String.format(" Threshold: %f (dB)\n", mThreshold));
952                sb.append(String.format(" PostGain: %f (dB)\n", mPostGain));
953            }
954            return sb.toString();
955        }
956        /**
957         * Gets the linkGroup index for this Limiter Stage. Only limiters that share the same
958         * linkGroup index will react together.
959         * @return linkGroup index.
960         */
961        public int getLinkGroup() { return mLinkGroup; }
962        /**
963         * Sets the linkGroup index for this limiter Stage.
964         * @param linkGroup desired linkGroup index
965         */
966        public void setLinkGroup(int linkGroup) { mLinkGroup = linkGroup; }
967        /**
968         * gets attack time for limiter compressor in milliseconds (ms)
969         * @return attack time for limiter compressor in milliseconds (ms)
970         */
971        public float getAttackTime() { return mAttackTime; }
972        /**
973         * sets attack time for limiter compressor in milliseconds (ms)
974         * @param attackTime desired for limiter compressor in milliseconds (ms)
975         */
976        public void setAttackTime(float attackTime) { mAttackTime = attackTime; }
977        /**
978         * gets release time for limiter compressor in milliseconds (ms)
979         * @return release time for limiter compressor in milliseconds (ms)
980         */
981        public float getReleaseTime() { return mReleaseTime; }
982        /**
983         * sets release time for limiter compressor in milliseconds (ms)
984         * @param releaseTime desired for limiter compressor in milliseconds (ms)
985         */
986        public void setReleaseTime(float releaseTime) { mReleaseTime = releaseTime; }
987        /**
988         * gets the limiter compressor ratio (N:1)
989         * @return limiter compressor ratio (N:1)
990         */
991        public float getRatio() { return mRatio; }
992        /**
993         * sets limiter compressor ratio (N:1)
994         * @param ratio desired for the limiter compressor (N:1)
995         */
996        public void setRatio(float ratio) { mRatio = ratio; }
997        /**
998         * gets the limiter compressor threshold measured in decibels (dB) from 0 dB Full Scale
999         * (dBFS). Thresholds are negative. A threshold of 0 dB means no limiting will take place.
1000         * @return limiter compressor threshold in decibels (dB)
1001         */
1002        public float getThreshold() { return mThreshold; }
1003        /**
1004         * sets the limiter compressor threshold measured in decibels (dB) from 0 dB Full Scale
1005         * (dBFS). Thresholds are negative. A threshold of 0 dB means no limiting will take place.
1006         * @param threshold desired for limiter compressor in decibels(dB)
1007         */
1008        public void setThreshold(float threshold) { mThreshold = threshold; }
1009        /**
1010         * gets the gain applied to the signal AFTER limiting. Measured in decibels (dB) where 0
1011         * dB means no level change
1012         * @return postGain value in decibels (dB)
1013         */
1014        public float getPostGain() { return mPostGain; }
1015        /**
1016         * sets the gain to be applied to the siganl AFTER the limiter. Measured in decibels
1017         * (dB), where 0 dB means no level change.
1018         * @param postGain desired value in decibels (dB)
1019         */
1020        public void setPostGain(float postGain) { mPostGain = postGain; }
1021    }
1022
1023    /**
1024     * Class for Channel configuration parameters. It is composed of multiple stages, which can be
1025     * used/enabled independently. Stages not used or disabled will be bypassed and the sound would
1026     * be unaffected by them.
1027     */
1028    public final static class Channel {
1029        private float   mInputGain;
1030        private Eq      mPreEq;
1031        private Mbc     mMbc;
1032        private Eq      mPostEq;
1033        private Limiter mLimiter;
1034
1035        /**
1036         * Class constructor for Channel configuration.
1037         * @param inputGain value in decibels (dB) of level change applied to the audio before
1038         * processing. A value of 0 dB means no change.
1039         * @param preEqInUse true if PreEq stage will be used, false otherwise. This can't be
1040         * changed later.
1041         * @param preEqBandCount number of bands for PreEq stage. This can't be changed later.
1042         * @param mbcInUse true if Mbc stage will be used, false otherwise. This can't be changed
1043         * later.
1044         * @param mbcBandCount number of bands for Mbc stage. This can't be changed later.
1045         * @param postEqInUse true if PostEq stage will be used, false otherwise. This can't be
1046         * changed later.
1047         * @param postEqBandCount number of bands for PostEq stage. This can't be changed later.
1048         * @param limiterInUse true if Limiter stage will be used, false otherwise. This can't be
1049         * changed later.
1050         */
1051        public Channel (float inputGain,
1052                boolean preEqInUse, int preEqBandCount,
1053                boolean mbcInUse, int mbcBandCount,
1054                boolean postEqInUse, int postEqBandCount,
1055                boolean limiterInUse) {
1056            mInputGain = inputGain;
1057            mPreEq = new Eq(preEqInUse, PREEQ_DEFAULT_ENABLED, preEqBandCount);
1058            mMbc = new Mbc(mbcInUse, MBC_DEFAULT_ENABLED, mbcBandCount);
1059            mPostEq = new Eq(postEqInUse, POSTEQ_DEFAULT_ENABLED,
1060                    postEqBandCount);
1061            mLimiter = new Limiter(limiterInUse,
1062                    LIMITER_DEFAULT_ENABLED, LIMITER_DEFAULT_LINK_GROUP,
1063                    LIMITER_DEFAULT_ATTACK_TIME, LIMITER_DEFAULT_RELEASE_TIME,
1064                    LIMITER_DEFAULT_RATIO, LIMITER_DEFAULT_THRESHOLD, LIMITER_DEFAULT_POST_GAIN);
1065        }
1066
1067        /**
1068         * Class constructor for Channel configuration
1069         * @param cfg copy constructor
1070         */
1071        public Channel(Channel cfg) {
1072            mInputGain = cfg.mInputGain;
1073            mPreEq = new Eq(cfg.mPreEq);
1074            mMbc = new Mbc(cfg.mMbc);
1075            mPostEq = new Eq(cfg.mPostEq);
1076            mLimiter = new Limiter(cfg.mLimiter);
1077        }
1078
1079        @Override
1080        public String toString() {
1081            StringBuilder sb = new StringBuilder();
1082            sb.append(String.format(" InputGain: %f\n", mInputGain));
1083            sb.append("-->PreEq\n");
1084            sb.append(mPreEq.toString());
1085            sb.append("-->MBC\n");
1086            sb.append(mMbc.toString());
1087            sb.append("-->PostEq\n");
1088            sb.append(mPostEq.toString());
1089            sb.append("-->Limiter\n");
1090            sb.append(mLimiter.toString());
1091            return sb.toString();
1092        }
1093        /**
1094         * Gets inputGain value in decibels (dB). 0 dB means no change;
1095         * @return gain value in decibels (dB)
1096         */
1097        public float getInputGain() {
1098            return mInputGain;
1099        }
1100        /**
1101         * Sets inputGain value in decibels (dB). 0 dB means no change;
1102         * @param inputGain desired gain value in decibels (dB)
1103         */
1104        public void setInputGain(float inputGain) {
1105            mInputGain = inputGain;
1106        }
1107
1108        /**
1109         * Gets PreEq configuration stage
1110         * @return PreEq configuration stage
1111         */
1112        public Eq getPreEq() {
1113            return mPreEq;
1114        }
1115        /**
1116         * Sets PreEq configuration stage. New PreEq stage must have the same number of bands than
1117         * original PreEq stage.
1118         * @param preEq configuration
1119         */
1120        public void setPreEq(Eq preEq) {
1121            if (preEq.getBandCount() != mPreEq.getBandCount()) {
1122                throw new IllegalArgumentException("PreEqBandCount changed from " +
1123                        mPreEq.getBandCount() + " to " + preEq.getBandCount());
1124            }
1125            mPreEq = new Eq(preEq);
1126        }
1127        /**
1128         * Gets EqBand for PreEq stage for given band index.
1129         * @param band index of band of interest from PreEq stage
1130         * @return EqBand configuration
1131         */
1132        public EqBand getPreEqBand(int band) {
1133            return mPreEq.getBand(band);
1134        }
1135        /**
1136         * Sets EqBand for PreEq stage for given band index
1137         * @param band index of band of interest from PreEq stage
1138         * @param preEqBand configuration to be set.
1139         */
1140        public void setPreEqBand(int band, EqBand preEqBand) {
1141            mPreEq.setBand(band, preEqBand);
1142        }
1143
1144        /**
1145         * Gets Mbc configuration stage
1146         * @return Mbc configuration stage
1147         */
1148        public Mbc getMbc() {
1149            return mMbc;
1150        }
1151        /**
1152         * Sets Mbc configuration stage. New Mbc stage must have the same number of bands than
1153         * original Mbc stage.
1154         * @param mbc
1155         */
1156        public void setMbc(Mbc mbc) {
1157            if (mbc.getBandCount() != mMbc.getBandCount()) {
1158                throw new IllegalArgumentException("MbcBandCount changed from " +
1159                        mMbc.getBandCount() + " to " + mbc.getBandCount());
1160            }
1161            mMbc = new Mbc(mbc);
1162        }
1163        /**
1164         * Gets MbcBand configuration for Mbc stage, for given band index.
1165         * @param band index of band of interest from Mbc stage
1166         * @return MbcBand configuration
1167         */
1168        public MbcBand getMbcBand(int band) {
1169            return mMbc.getBand(band);
1170        }
1171        /**
1172         * Sets MbcBand for Mbc stage for given band index
1173         * @param band index of band of interest from Mbc Stage
1174         * @param mbcBand configuration to be set
1175         */
1176        public void setMbcBand(int band, MbcBand mbcBand) {
1177            mMbc.setBand(band, mbcBand);
1178        }
1179
1180        /**
1181         * Gets PostEq configuration stage
1182         * @return PostEq configuration stage
1183         */
1184        public Eq getPostEq() {
1185            return mPostEq;
1186        }
1187        /**
1188         * Sets PostEq configuration stage. New PostEq stage must have the same number of bands than
1189         * original PostEq stage.
1190         * @param postEq configuration
1191         */
1192        public void setPostEq(Eq postEq) {
1193            if (postEq.getBandCount() != mPostEq.getBandCount()) {
1194                throw new IllegalArgumentException("PostEqBandCount changed from " +
1195                        mPostEq.getBandCount() + " to " + postEq.getBandCount());
1196            }
1197            mPostEq = new Eq(postEq);
1198        }
1199        /**
1200         * Gets EqBand for PostEq stage for given band index.
1201         * @param band index of band of interest from PostEq stage
1202         * @return EqBand configuration
1203         */
1204        public EqBand getPostEqBand(int band) {
1205            return mPostEq.getBand(band);
1206        }
1207        /**
1208         * Sets EqBand for PostEq stage for given band index
1209         * @param band index of band of interest from PostEq stage
1210         * @param postEqBand configuration to be set.
1211         */
1212        public void setPostEqBand(int band, EqBand postEqBand) {
1213            mPostEq.setBand(band, postEqBand);
1214        }
1215
1216        /**
1217         * Gets Limiter configuration stage
1218         * @return Limiter configuration stage
1219         */
1220        public Limiter getLimiter() {
1221            return mLimiter;
1222        }
1223        /**
1224         * Sets Limiter configuration stage.
1225         * @param limiter configuration stage.
1226         */
1227        public void setLimiter(Limiter limiter) {
1228            mLimiter = new Limiter(limiter);
1229        }
1230    }
1231
1232    /**
1233     * Class for Config object, used by DynamicsProcessing to configure and update the audio effect.
1234     * use Builder to instantiate objects of this type.
1235     */
1236    public final static class Config {
1237        private final int mVariant;
1238        private final int mChannelCount;
1239        private final boolean mPreEqInUse;
1240        private final int mPreEqBandCount;
1241        private final boolean mMbcInUse;
1242        private final int mMbcBandCount;
1243        private final boolean mPostEqInUse;
1244        private final int mPostEqBandCount;
1245        private final boolean mLimiterInUse;
1246        private final float mPreferredFrameDuration;
1247        private final Channel[] mChannel;
1248
1249        /**
1250         * @hide
1251         * Class constructor for config. None of these parameters can be changed later.
1252         * @param variant index of variant used for effect engine. See
1253         * {@link #VARIANT_FAVOR_FREQUENCY_RESOLUTION} and {@link #VARIANT_FAVOR_TIME_RESOLUTION}.
1254         * @param frameDurationMs preferred frame duration in milliseconds (ms).
1255         * @param channelCount Number of channels to be configured.
1256         * @param preEqInUse true if PreEq stage will be used, false otherwise.
1257         * @param preEqBandCount number of bands for PreEq stage.
1258         * @param mbcInUse true if Mbc stage will be used, false otherwise.
1259         * @param mbcBandCount number of bands for Mbc stage.
1260         * @param postEqInUse true if PostEq stage will be used, false otherwise.
1261         * @param postEqBandCount number of bands for PostEq stage.
1262         * @param limiterInUse true if Limiter stage will be used, false otherwise.
1263         * @param channel array of Channel objects to be used for this configuration.
1264         */
1265        public Config(int variant, float frameDurationMs, int channelCount,
1266                boolean preEqInUse, int preEqBandCount,
1267                boolean mbcInUse, int mbcBandCount,
1268                boolean postEqInUse, int postEqBandCount,
1269                boolean limiterInUse,
1270                Channel[] channel) {
1271            mVariant = variant;
1272            mPreferredFrameDuration = frameDurationMs;
1273            mChannelCount = channelCount;
1274            mPreEqInUse = preEqInUse;
1275            mPreEqBandCount = preEqBandCount;
1276            mMbcInUse = mbcInUse;
1277            mMbcBandCount = mbcBandCount;
1278            mPostEqInUse = postEqInUse;
1279            mPostEqBandCount = postEqBandCount;
1280            mLimiterInUse = limiterInUse;
1281
1282            mChannel = new Channel[mChannelCount];
1283            //check if channelconfig is null or has less channels than channel count.
1284            //options: fill the missing with default options.
1285            // or fail?
1286            for (int ch = 0; ch < mChannelCount; ch++) {
1287                if (ch < channel.length) {
1288                    mChannel[ch] = new Channel(channel[ch]); //copy create
1289                } else {
1290                    //create a new one from scratch? //fail?
1291                }
1292            }
1293        }
1294        //a version that will scale to necessary number of channels
1295        /**
1296         * @hide
1297         * Class constructor for Configuration.
1298         * @param channelCount limit configuration to this number of channels. if channelCount is
1299         * greater than number of channels in cfg, the constructor will duplicate the last channel
1300         * found as many times as necessary to create a Config with channelCount number of channels.
1301         * If channelCount is less than channels in cfg, the extra channels in cfg will be ignored.
1302         * @param cfg copy constructor paremter.
1303         */
1304        public Config(int channelCount, Config cfg) {
1305            mVariant = cfg.mVariant;
1306            mPreferredFrameDuration = cfg.mPreferredFrameDuration;
1307            mChannelCount = cfg.mChannelCount;
1308            mPreEqInUse = cfg.mPreEqInUse;
1309            mPreEqBandCount = cfg.mPreEqBandCount;
1310            mMbcInUse = cfg.mMbcInUse;
1311            mMbcBandCount = cfg.mMbcBandCount;
1312            mPostEqInUse = cfg.mPostEqInUse;
1313            mPostEqBandCount = cfg.mPostEqBandCount;
1314            mLimiterInUse = cfg.mLimiterInUse;
1315
1316            if (mChannelCount != cfg.mChannel.length) {
1317                throw new IllegalArgumentException("configuration channel counts differ " +
1318                        mChannelCount + " !=" + cfg.mChannel.length);
1319            }
1320            if (channelCount < 1) {
1321                throw new IllegalArgumentException("channel resizing less than 1 not allowed");
1322            }
1323
1324            mChannel = new Channel[channelCount];
1325            for (int ch = 0; ch < channelCount; ch++) {
1326                if (ch < mChannelCount) {
1327                    mChannel[ch] = new Channel(cfg.mChannel[ch]);
1328                } else {
1329                    //duplicate last
1330                    mChannel[ch] = new Channel(cfg.mChannel[mChannelCount-1]);
1331                }
1332            }
1333        }
1334
1335        /**
1336         * @hide
1337         * Class constructor for Config
1338         * @param cfg Configuration object copy constructor
1339         */
1340        public Config(@NonNull Config cfg) {
1341            this(cfg.mChannelCount, cfg);
1342        }
1343
1344        @Override
1345        public String toString() {
1346            StringBuilder sb = new StringBuilder();
1347            sb.append(String.format("Variant: %d\n", mVariant));
1348            sb.append(String.format("PreferredFrameDuration: %f\n", mPreferredFrameDuration));
1349            sb.append(String.format("ChannelCount: %d\n", mChannelCount));
1350            sb.append(String.format("PreEq inUse: %b, bandCount:%d\n",mPreEqInUse,
1351                    mPreEqBandCount));
1352            sb.append(String.format("Mbc inUse: %b, bandCount: %d\n",mMbcInUse, mMbcBandCount));
1353            sb.append(String.format("PostEq inUse: %b, bandCount: %d\n", mPostEqInUse,
1354                    mPostEqBandCount));
1355            sb.append(String.format("Limiter inUse: %b\n", mLimiterInUse));
1356            for (int ch = 0; ch < mChannel.length; ch++) {
1357                sb.append(String.format("==Channel %d\n", ch));
1358                sb.append(mChannel[ch].toString());
1359            }
1360            return sb.toString();
1361        }
1362        private void checkChannel(int channelIndex) {
1363            if (channelIndex < 0 || channelIndex >= mChannel.length) {
1364                throw new IllegalArgumentException("ChannelIndex out of bounds");
1365            }
1366        }
1367
1368        //getters and setters
1369        /**
1370         * Gets variant for effect engine See {@link #VARIANT_FAVOR_FREQUENCY_RESOLUTION} and
1371         * {@link #VARIANT_FAVOR_TIME_RESOLUTION}.
1372         * @return variant of effect engine
1373         */
1374        public int getVariant() {
1375            return mVariant;
1376        }
1377        /**
1378         * Gets preferred frame duration in milliseconds (ms).
1379         * @return preferred frame duration in milliseconds (ms)
1380         */
1381        public float getPreferredFrameDuration() {
1382            return mPreferredFrameDuration;
1383        }
1384        /**
1385         * Gets if preEq stage is in use
1386         * @return true if preEq stage is in use;
1387         */
1388        public boolean isPreEqInUse() {
1389            return mPreEqInUse;
1390        }
1391        /**
1392         * Gets number of bands configured for the PreEq stage.
1393         * @return number of bands configured for the PreEq stage.
1394         */
1395        public int getPreEqBandCount() {
1396            return mPreEqBandCount;
1397        }
1398        /**
1399         * Gets if Mbc stage is in use
1400         * @return true if Mbc stage is in use;
1401         */
1402        public boolean isMbcInUse() {
1403            return mMbcInUse;
1404        }
1405        /**
1406         * Gets number of bands configured for the Mbc stage.
1407         * @return number of bands configured for the Mbc stage.
1408         */
1409        public int getMbcBandCount() {
1410            return mMbcBandCount;
1411        }
1412        /**
1413         * Gets if PostEq stage is in use
1414         * @return true if PostEq stage is in use;
1415         */
1416        public boolean isPostEqInUse() {
1417            return mPostEqInUse;
1418        }
1419        /**
1420         * Gets number of bands configured for the PostEq stage.
1421         * @return number of bands configured for the PostEq stage.
1422         */
1423        public int getPostEqBandCount() {
1424            return mPostEqBandCount;
1425        }
1426        /**
1427         * Gets if Limiter stage is in use
1428         * @return true if Limiter stage is in use;
1429         */
1430        public boolean isLimiterInUse() {
1431            return mLimiterInUse;
1432        }
1433
1434        //channel
1435        /**
1436         * Gets the Channel configuration object by using the channel index
1437         * @param channelIndex of desired Channel object
1438         * @return Channel configuration object
1439         */
1440        public Channel getChannelByChannelIndex(int channelIndex) {
1441            checkChannel(channelIndex);
1442            return mChannel[channelIndex];
1443        }
1444
1445        /**
1446         * Sets the chosen Channel object in the selected channelIndex
1447         * Note that all the stages should have the same number of bands than the existing Channel
1448         * object.
1449         * @param channelIndex index of channel to be replaced
1450         * @param channel Channel configuration object to be set
1451         */
1452        public void setChannelTo(int channelIndex, Channel channel) {
1453            checkChannel(channelIndex);
1454            //check all things are compatible
1455            if (mMbcBandCount != channel.getMbc().getBandCount()) {
1456                throw new IllegalArgumentException("MbcBandCount changed from " +
1457                        mMbcBandCount + " to " + channel.getPreEq().getBandCount());
1458            }
1459            if (mPreEqBandCount != channel.getPreEq().getBandCount()) {
1460                throw new IllegalArgumentException("PreEqBandCount changed from " +
1461                        mPreEqBandCount + " to " + channel.getPreEq().getBandCount());
1462            }
1463            if (mPostEqBandCount != channel.getPostEq().getBandCount()) {
1464                throw new IllegalArgumentException("PostEqBandCount changed from " +
1465                        mPostEqBandCount + " to " + channel.getPostEq().getBandCount());
1466            }
1467            mChannel[channelIndex] = new Channel(channel);
1468        }
1469
1470        /**
1471         * Sets ALL channels to the chosen Channel object. Note that all the stages should have the
1472         * same number of bands than the existing ones.
1473         * @param channel Channel configuration object to be set.
1474         */
1475        public void setAllChannelsTo(Channel channel) {
1476            for (int ch = 0; ch < mChannel.length; ch++) {
1477                setChannelTo(ch, channel);
1478            }
1479        }
1480
1481        //===channel params
1482        /**
1483         * Gets inputGain value in decibels (dB) for channel indicated by channelIndex
1484         * @param channelIndex index of channel of interest
1485         * @return inputGain value in decibels (dB). 0 dB means no change.
1486         */
1487        public float getInputGainByChannelIndex(int channelIndex) {
1488            checkChannel(channelIndex);
1489            return mChannel[channelIndex].getInputGain();
1490        }
1491        /**
1492         * Sets the inputGain value in decibels (dB) for the channel indicated by channelIndex.
1493         * @param channelIndex index of channel of interest
1494         * @param inputGain desired value in decibels (dB).
1495         */
1496        public void setInputGainByChannelIndex(int channelIndex, float inputGain) {
1497            checkChannel(channelIndex);
1498            mChannel[channelIndex].setInputGain(inputGain);
1499        }
1500        /**
1501         * Sets the inputGain value in decibels (dB) for ALL channels
1502         * @param inputGain desired value in decibels (dB)
1503         */
1504        public void setInputGainAllChannelsTo(float inputGain) {
1505            for (int ch = 0; ch < mChannel.length; ch++) {
1506                mChannel[ch].setInputGain(inputGain);
1507            }
1508        }
1509
1510        //=== PreEQ
1511        /**
1512         * Gets PreEq stage from channel indicated by channelIndex
1513         * @param channelIndex index of channel of interest
1514         * @return PreEq stage configuration object
1515         */
1516        public Eq getPreEqByChannelIndex(int channelIndex) {
1517            checkChannel(channelIndex);
1518            return mChannel[channelIndex].getPreEq();
1519        }
1520        /**
1521         * Sets the PreEq stage configuration for the channel indicated by channelIndex. Note that
1522         * new preEq stage must have the same number of bands than original preEq stage
1523         * @param channelIndex index of channel to be set
1524         * @param preEq desired PreEq configuration to be set
1525         */
1526        public void setPreEqByChannelIndex(int channelIndex, Eq preEq) {
1527            checkChannel(channelIndex);
1528            mChannel[channelIndex].setPreEq(preEq);
1529        }
1530        /**
1531         * Sets the PreEq stage configuration for ALL channels. Note that new preEq stage must have
1532         * the same number of bands than original preEq stages.
1533         * @param preEq desired PreEq configuration to be set
1534         */
1535        public void setPreEqAllChannelsTo(Eq preEq) {
1536            for (int ch = 0; ch < mChannel.length; ch++) {
1537                mChannel[ch].setPreEq(preEq);
1538            }
1539        }
1540        public EqBand getPreEqBandByChannelIndex(int channelIndex, int band) {
1541            checkChannel(channelIndex);
1542            return mChannel[channelIndex].getPreEqBand(band);
1543        }
1544        public void setPreEqBandByChannelIndex(int channelIndex, int band, EqBand preEqBand) {
1545            checkChannel(channelIndex);
1546            mChannel[channelIndex].setPreEqBand(band, preEqBand);
1547        }
1548        public void setPreEqBandAllChannelsTo(int band, EqBand preEqBand) {
1549            for (int ch = 0; ch < mChannel.length; ch++) {
1550                mChannel[ch].setPreEqBand(band, preEqBand);
1551            }
1552        }
1553
1554        //=== MBC
1555        public Mbc getMbcByChannelIndex(int channelIndex) {
1556            checkChannel(channelIndex);
1557            return mChannel[channelIndex].getMbc();
1558        }
1559        public void setMbcByChannelIndex(int channelIndex, Mbc mbc) {
1560            checkChannel(channelIndex);
1561            mChannel[channelIndex].setMbc(mbc);
1562        }
1563        public void setMbcAllChannelsTo(Mbc mbc) {
1564            for (int ch = 0; ch < mChannel.length; ch++) {
1565                mChannel[ch].setMbc(mbc);
1566            }
1567        }
1568        public MbcBand getMbcBandByChannelIndex(int channelIndex, int band) {
1569            checkChannel(channelIndex);
1570            return mChannel[channelIndex].getMbcBand(band);
1571        }
1572        public void setMbcBandByChannelIndex(int channelIndex, int band, MbcBand mbcBand) {
1573            checkChannel(channelIndex);
1574            mChannel[channelIndex].setMbcBand(band, mbcBand);
1575        }
1576        public void setMbcBandAllChannelsTo(int band, MbcBand mbcBand) {
1577            for (int ch = 0; ch < mChannel.length; ch++) {
1578                mChannel[ch].setMbcBand(band, mbcBand);
1579            }
1580        }
1581
1582        //=== PostEQ
1583        public Eq getPostEqByChannelIndex(int channelIndex) {
1584            checkChannel(channelIndex);
1585            return mChannel[channelIndex].getPostEq();
1586        }
1587        public void setPostEqByChannelIndex(int channelIndex, Eq postEq) {
1588            checkChannel(channelIndex);
1589            mChannel[channelIndex].setPostEq(postEq);
1590        }
1591        public void setPostEqAllChannelsTo(Eq postEq) {
1592            for (int ch = 0; ch < mChannel.length; ch++) {
1593                mChannel[ch].setPostEq(postEq);
1594            }
1595        }
1596        public EqBand getPostEqBandByChannelIndex(int channelIndex, int band) {
1597            checkChannel(channelIndex);
1598            return mChannel[channelIndex].getPostEqBand(band);
1599        }
1600        public void setPostEqBandByChannelIndex(int channelIndex, int band, EqBand postEqBand) {
1601            checkChannel(channelIndex);
1602            mChannel[channelIndex].setPostEqBand(band, postEqBand);
1603        }
1604        public void setPostEqBandAllChannelsTo(int band, EqBand postEqBand) {
1605            for (int ch = 0; ch < mChannel.length; ch++) {
1606                mChannel[ch].setPostEqBand(band, postEqBand);
1607            }
1608        }
1609
1610        //Limiter
1611        public Limiter getLimiterByChannelIndex(int channelIndex) {
1612            checkChannel(channelIndex);
1613            return mChannel[channelIndex].getLimiter();
1614        }
1615        public void setLimiterByChannelIndex(int channelIndex, Limiter limiter) {
1616            checkChannel(channelIndex);
1617            mChannel[channelIndex].setLimiter(limiter);
1618        }
1619        public void setLimiterAllChannelsTo(Limiter limiter) {
1620            for (int ch = 0; ch < mChannel.length; ch++) {
1621                mChannel[ch].setLimiter(limiter);
1622            }
1623        }
1624
1625        public final static class Builder {
1626            private int mVariant;
1627            private int mChannelCount;
1628            private boolean mPreEqInUse;
1629            private int mPreEqBandCount;
1630            private boolean mMbcInUse;
1631            private int mMbcBandCount;
1632            private boolean mPostEqInUse;
1633            private int mPostEqBandCount;
1634            private boolean mLimiterInUse;
1635            private float mPreferredFrameDuration = CONFIG_PREFERRED_FRAME_DURATION_MS;
1636            private Channel[] mChannel;
1637
1638            public Builder(int variant, int channelCount,
1639                    boolean preEqInUse, int preEqBandCount,
1640                    boolean mbcInUse, int mbcBandCount,
1641                    boolean postEqInUse, int postEqBandCount,
1642                    boolean limiterInUse) {
1643                mVariant = variant;
1644                mChannelCount = channelCount;
1645                mPreEqInUse = preEqInUse;
1646                mPreEqBandCount = preEqBandCount;
1647                mMbcInUse = mbcInUse;
1648                mMbcBandCount = mbcBandCount;
1649                mPostEqInUse = postEqInUse;
1650                mPostEqBandCount = postEqBandCount;
1651                mLimiterInUse = limiterInUse;
1652                mChannel = new Channel[mChannelCount];
1653                for (int ch = 0; ch < mChannelCount; ch++) {
1654                    this.mChannel[ch] = new Channel(CHANNEL_DEFAULT_INPUT_GAIN,
1655                            this.mPreEqInUse, this.mPreEqBandCount,
1656                            this.mMbcInUse, this.mMbcBandCount,
1657                            this.mPostEqInUse, this.mPostEqBandCount,
1658                            this.mLimiterInUse);
1659                }
1660            }
1661
1662            private void checkChannel(int channelIndex) {
1663                if (channelIndex < 0 || channelIndex >= mChannel.length) {
1664                    throw new IllegalArgumentException("ChannelIndex out of bounds");
1665                }
1666            }
1667
1668            public Builder setPreferredFrameDuration(float frameDuration) {
1669                if (frameDuration < 0) {
1670                    throw new IllegalArgumentException("Expected positive frameDuration");
1671                }
1672                mPreferredFrameDuration = frameDuration;
1673                return this;
1674            }
1675
1676            public Builder setInputGainByChannelIndex(int channelIndex, float inputGain) {
1677                checkChannel(channelIndex);
1678                mChannel[channelIndex].setInputGain(inputGain);
1679                return this;
1680            }
1681            public Builder setInputGainAllChannelsTo(float inputGain) {
1682                for (int ch = 0; ch < mChannel.length; ch++) {
1683                    mChannel[ch].setInputGain(inputGain);
1684                }
1685                return this;
1686            }
1687
1688            public Builder setChannelTo(int channelIndex, Channel channel) {
1689                checkChannel(channelIndex);
1690                //check all things are compatible
1691                if (mMbcBandCount != channel.getMbc().getBandCount()) {
1692                    throw new IllegalArgumentException("MbcBandCount changed from " +
1693                            mMbcBandCount + " to " + channel.getPreEq().getBandCount());
1694                }
1695                if (mPreEqBandCount != channel.getPreEq().getBandCount()) {
1696                    throw new IllegalArgumentException("PreEqBandCount changed from " +
1697                            mPreEqBandCount + " to " + channel.getPreEq().getBandCount());
1698                }
1699                if (mPostEqBandCount != channel.getPostEq().getBandCount()) {
1700                    throw new IllegalArgumentException("PostEqBandCount changed from " +
1701                            mPostEqBandCount + " to " + channel.getPostEq().getBandCount());
1702                }
1703                mChannel[channelIndex] = new Channel(channel);
1704                return this;
1705            }
1706            public Builder setAllChannelsTo(Channel channel) {
1707                for (int ch = 0; ch < mChannel.length; ch++) {
1708                    setChannelTo(ch, channel);
1709                }
1710                return this;
1711            }
1712
1713            public Builder setPreEqByChannelIndex(int channelIndex, Eq preEq) {
1714                checkChannel(channelIndex);
1715                mChannel[channelIndex].setPreEq(preEq);
1716                return this;
1717            }
1718            public Builder setPreEqAllChannelsTo(Eq preEq) {
1719                for (int ch = 0; ch < mChannel.length; ch++) {
1720                    setPreEqByChannelIndex(ch, preEq);
1721                }
1722                return this;
1723            }
1724
1725            public Builder setMbcByChannelIndex(int channelIndex, Mbc mbc) {
1726                checkChannel(channelIndex);
1727                mChannel[channelIndex].setMbc(mbc);
1728                return this;
1729            }
1730            public Builder setMbcAllChannelsTo(Mbc mbc) {
1731                for (int ch = 0; ch < mChannel.length; ch++) {
1732                    setMbcByChannelIndex(ch, mbc);
1733                }
1734                return this;
1735            }
1736
1737            public Builder setPostEqByChannelIndex(int channelIndex, Eq postEq) {
1738                checkChannel(channelIndex);
1739                mChannel[channelIndex].setPostEq(postEq);
1740                return this;
1741            }
1742            public Builder setPostEqAllChannelsTo(Eq postEq) {
1743                for (int ch = 0; ch < mChannel.length; ch++) {
1744                    setPostEqByChannelIndex(ch, postEq);
1745                }
1746                return this;
1747            }
1748
1749            public Builder setLimiterByChannelIndex(int channelIndex, Limiter limiter) {
1750                checkChannel(channelIndex);
1751                mChannel[channelIndex].setLimiter(limiter);
1752                return this;
1753            }
1754            public Builder setLimiterAllChannelsTo(Limiter limiter) {
1755                for (int ch = 0; ch < mChannel.length; ch++) {
1756                    setLimiterByChannelIndex(ch, limiter);
1757                }
1758                return this;
1759            }
1760
1761            public Config build() {
1762                return new Config(mVariant, mPreferredFrameDuration, mChannelCount,
1763                        mPreEqInUse, mPreEqBandCount,
1764                        mMbcInUse, mMbcBandCount,
1765                        mPostEqInUse, mPostEqBandCount,
1766                        mLimiterInUse, mChannel);
1767            }
1768        }
1769    }
1770    //=== CHANNEL
1771    public Channel getChannelByChannelIndex(int channelIndex) {
1772        return queryEngineByChannelIndex(channelIndex);
1773    }
1774
1775    public void setChannelTo(int channelIndex, Channel channel) {
1776        updateEngineChannelByChannelIndex(channelIndex, channel);
1777    }
1778
1779    public void setAllChannelsTo(Channel channel) {
1780        for (int ch = 0; ch < mChannelCount; ch++) {
1781            setChannelTo(ch, channel);
1782        }
1783    }
1784
1785    //=== channel params
1786    public float getInputGainByChannelIndex(int channelIndex) {
1787        return getTwoFloat(PARAM_INPUT_GAIN, channelIndex);
1788    }
1789    public void setInputGainbyChannel(int channelIndex, float inputGain) {
1790        setTwoFloat(PARAM_INPUT_GAIN, channelIndex, inputGain);
1791    }
1792    public void setInputGainAllChannelsTo(float inputGain) {
1793        for (int ch = 0; ch < mChannelCount; ch++) {
1794            setInputGainbyChannel(ch, inputGain);
1795        }
1796    }
1797
1798    //=== PreEQ
1799    public Eq getPreEqByChannelIndex(int channelIndex) {
1800        return queryEngineEqByChannelIndex(PARAM_PRE_EQ, channelIndex);
1801    }
1802    public void setPreEqByChannelIndex(int channelIndex, Eq preEq) {
1803        updateEngineEqByChannelIndex(PARAM_PRE_EQ, channelIndex, preEq);
1804    }
1805    public void setPreEqAllChannelsTo(Eq preEq) {
1806        for (int ch = 0; ch < mChannelCount; ch++) {
1807            setPreEqByChannelIndex(ch, preEq);
1808        }
1809    }
1810    public EqBand getPreEqBandByChannelIndex(int channelIndex, int band) {
1811        return queryEngineEqBandByChannelIndex(PARAM_PRE_EQ_BAND, channelIndex, band);
1812    }
1813    public void setPreEqBandByChannelIndex(int channelIndex, int band, EqBand preEqBand) {
1814        updateEngineEqBandByChannelIndex(PARAM_PRE_EQ_BAND, channelIndex, band, preEqBand);
1815    }
1816    public void setPreEqBandAllChannelsTo(int band, EqBand preEqBand) {
1817        for (int ch = 0; ch < mChannelCount; ch++) {
1818            setPreEqBandByChannelIndex(ch, band, preEqBand);
1819        }
1820    }
1821
1822    //=== MBC
1823    public Mbc getMbcByChannelIndex(int channelIndex) {
1824        return queryEngineMbcByChannelIndex(channelIndex);
1825    }
1826    public void setMbcByChannelIndex(int channelIndex, Mbc mbc) {
1827        updateEngineMbcByChannelIndex(channelIndex, mbc);
1828    }
1829    public void setMbcAllChannelsTo(Mbc mbc) {
1830        for (int ch = 0; ch < mChannelCount; ch++) {
1831            setMbcByChannelIndex(ch, mbc);
1832        }
1833    }
1834    public MbcBand getMbcBandByChannelIndex(int channelIndex, int band) {
1835        return queryEngineMbcBandByChannelIndex(channelIndex, band);
1836    }
1837    public void setMbcBandByChannelIndex(int channelIndex, int band, MbcBand mbcBand) {
1838        updateEngineMbcBandByChannelIndex(channelIndex, band, mbcBand);
1839    }
1840    public void setMbcBandAllChannelsTo(int band, MbcBand mbcBand) {
1841        for (int ch = 0; ch < mChannelCount; ch++) {
1842            setMbcBandByChannelIndex(ch, band, mbcBand);
1843        }
1844    }
1845
1846    //== PostEq
1847    public Eq getPostEqByChannelIndex(int channelIndex) {
1848        return queryEngineEqByChannelIndex(PARAM_POST_EQ, channelIndex);
1849    }
1850    public void setPostEqByChannelIndex(int channelIndex, Eq postEq) {
1851        updateEngineEqByChannelIndex(PARAM_POST_EQ, channelIndex, postEq);
1852    }
1853    public void setPostEqAllChannelsTo(Eq postEq) {
1854        for (int ch = 0; ch < mChannelCount; ch++) {
1855            setPostEqByChannelIndex(ch, postEq);
1856        }
1857    }
1858    public EqBand getPostEqBandByChannelIndex(int channelIndex, int band) {
1859        return queryEngineEqBandByChannelIndex(PARAM_POST_EQ_BAND, channelIndex, band);
1860    }
1861    public void setPostEqBandByChannelIndex(int channelIndex, int band, EqBand postEqBand) {
1862        updateEngineEqBandByChannelIndex(PARAM_POST_EQ_BAND, channelIndex, band, postEqBand);
1863    }
1864    public void setPostEqBandAllChannelsTo(int band, EqBand postEqBand) {
1865        for (int ch = 0; ch < mChannelCount; ch++) {
1866            setPostEqBandByChannelIndex(ch, band, postEqBand);
1867        }
1868    }
1869
1870    //==== Limiter
1871    public Limiter getLimiterByChannelIndex(int channelIndex) {
1872        return queryEngineLimiterByChannelIndex(channelIndex);
1873    }
1874    public void setLimiterByChannelIndex(int channelIndex, Limiter limiter) {
1875        updateEngineLimiterByChannelIndex(channelIndex, limiter);
1876    }
1877    public void setLimiterAllChannelsTo(Limiter limiter) {
1878        for (int ch = 0; ch < mChannelCount; ch++) {
1879            setLimiterByChannelIndex(ch, limiter);
1880        }
1881    }
1882
1883    /**
1884     * Gets the number of channels in the effect engine
1885     * @return number of channels currently in use by the effect engine
1886     */
1887    public int getChannelCount() {
1888        return getOneInt(PARAM_GET_CHANNEL_COUNT);
1889    }
1890
1891    //=== Engine calls
1892    private void setEngineArchitecture(int variant, float preferredFrameDuration,
1893            boolean preEqInUse, int preEqBandCount, boolean mbcInUse, int mbcBandCount,
1894            boolean postEqInUse, int postEqBandCount, boolean limiterInUse) {
1895
1896        Number[] params = { PARAM_ENGINE_ARCHITECTURE };
1897        Number[] values = { variant /* variant */,
1898                preferredFrameDuration,
1899                (preEqInUse ? 1 : 0),
1900                preEqBandCount,
1901                (mbcInUse ? 1 : 0),
1902                mbcBandCount,
1903                (postEqInUse ? 1 : 0),
1904                postEqBandCount,
1905                (limiterInUse ? 1 : 0)};
1906        setNumberArray(params, values);
1907    }
1908
1909    private void updateEngineEqBandByChannelIndex(int param, int channelIndex, int bandIndex,
1910            @NonNull EqBand eqBand) {
1911        Number[] params = {param,
1912                channelIndex,
1913                bandIndex};
1914        Number[] values = {(eqBand.isEnabled() ? 1 : 0),
1915                eqBand.getCutoffFrequency(),
1916                eqBand.getGain()};
1917        setNumberArray(params, values);
1918    }
1919    private Eq queryEngineEqByChannelIndex(int param, int channelIndex) {
1920
1921        Number[] params = {param == PARAM_PRE_EQ ? PARAM_PRE_EQ : PARAM_POST_EQ,
1922                channelIndex};
1923        Number[] values = {0 /*0 in use */,
1924                            0 /*1 enabled*/,
1925                            0 /*2 band count */};
1926        byte[] paramBytes = numberArrayToByteArray(params);
1927        byte[] valueBytes = numberArrayToByteArray(values); //just interest in the byte size.
1928        getParameter(paramBytes, valueBytes);
1929        byteArrayToNumberArray(valueBytes, values);
1930        int bandCount = values[2].intValue();
1931        Eq eq = new Eq(values[0].intValue() > 0 /* in use */,
1932                values[1].intValue() > 0 /* enabled */,
1933                bandCount /*band count*/);
1934        for (int b = 0; b < bandCount; b++) {
1935            EqBand eqBand = queryEngineEqBandByChannelIndex(param == PARAM_PRE_EQ ?
1936                    PARAM_PRE_EQ_BAND : PARAM_POST_EQ_BAND, channelIndex, b);
1937            eq.setBand(b, eqBand);
1938        }
1939        return eq;
1940    }
1941    private EqBand queryEngineEqBandByChannelIndex(int param, int channelIndex, int bandIndex) {
1942        Number[] params = {param,
1943                channelIndex,
1944                bandIndex};
1945        Number[] values = {0 /*0 enabled*/,
1946                            0.0f /*1 cutoffFrequency */,
1947                            0.0f /*2 gain */};
1948
1949        byte[] paramBytes = numberArrayToByteArray(params);
1950        byte[] valueBytes = numberArrayToByteArray(values); //just interest in the byte size.
1951        getParameter(paramBytes, valueBytes);
1952
1953        byteArrayToNumberArray(valueBytes, values);
1954
1955        return new EqBand(values[0].intValue() > 0 /* enabled */,
1956                values[1].floatValue() /* cutoffFrequency */,
1957                values[2].floatValue() /* gain*/);
1958    }
1959    private void updateEngineEqByChannelIndex(int param, int channelIndex, @NonNull Eq eq) {
1960        int bandCount = eq.getBandCount();
1961        Number[] params = {param,
1962                channelIndex};
1963        Number[] values = { (eq.isInUse() ? 1 : 0),
1964                (eq.isEnabled() ? 1 : 0),
1965                bandCount};
1966        setNumberArray(params, values);
1967        for (int b = 0; b < bandCount; b++) {
1968            EqBand eqBand = eq.getBand(b);
1969            updateEngineEqBandByChannelIndex(param == PARAM_PRE_EQ ?
1970                    PARAM_PRE_EQ_BAND : PARAM_POST_EQ_BAND, channelIndex, b, eqBand);
1971        }
1972    }
1973
1974    private Mbc queryEngineMbcByChannelIndex(int channelIndex) {
1975        Number[] params = {PARAM_MBC,
1976                channelIndex};
1977        Number[] values = {0 /*0 in use */,
1978                            0 /*1 enabled*/,
1979                            0 /*2 band count */};
1980        byte[] paramBytes = numberArrayToByteArray(params);
1981        byte[] valueBytes = numberArrayToByteArray(values); //just interest in the byte size.
1982        getParameter(paramBytes, valueBytes);
1983        byteArrayToNumberArray(valueBytes, values);
1984        int bandCount = values[2].intValue();
1985        Mbc mbc = new Mbc(values[0].intValue() > 0 /* in use */,
1986                values[1].intValue() > 0 /* enabled */,
1987                bandCount /*band count*/);
1988        for (int b = 0; b < bandCount; b++) {
1989            MbcBand mbcBand = queryEngineMbcBandByChannelIndex(channelIndex, b);
1990            mbc.setBand(b, mbcBand);
1991        }
1992        return mbc;
1993    }
1994    private MbcBand queryEngineMbcBandByChannelIndex(int channelIndex, int bandIndex) {
1995        Number[] params = {PARAM_MBC_BAND,
1996                channelIndex,
1997                bandIndex};
1998        Number[] values = {0 /*0 enabled */,
1999                0.0f /*1 cutoffFrequency */,
2000                0.0f /*2 AttackTime */,
2001                0.0f /*3 ReleaseTime */,
2002                0.0f /*4 Ratio */,
2003                0.0f /*5 Threshold */,
2004                0.0f /*6 KneeWidth */,
2005                0.0f /*7 NoiseGateThreshold */,
2006                0.0f /*8 ExpanderRatio */,
2007                0.0f /*9 PreGain */,
2008                0.0f /*10 PostGain*/};
2009
2010        byte[] paramBytes = numberArrayToByteArray(params);
2011        byte[] valueBytes = numberArrayToByteArray(values); //just interest in the byte size.
2012        getParameter(paramBytes, valueBytes);
2013
2014        byteArrayToNumberArray(valueBytes, values);
2015
2016        return new MbcBand(values[0].intValue() > 0 /* enabled */,
2017                values[1].floatValue() /* cutoffFrequency */,
2018                values[2].floatValue()/*2 AttackTime */,
2019                values[3].floatValue()/*3 ReleaseTime */,
2020                values[4].floatValue()/*4 Ratio */,
2021                values[5].floatValue()/*5 Threshold */,
2022                values[6].floatValue()/*6 KneeWidth */,
2023                values[7].floatValue()/*7 NoiseGateThreshold */,
2024                values[8].floatValue()/*8 ExpanderRatio */,
2025                values[9].floatValue()/*9 PreGain */,
2026                values[10].floatValue()/*10 PostGain*/);
2027    }
2028    private void updateEngineMbcBandByChannelIndex(int channelIndex, int bandIndex,
2029            @NonNull MbcBand mbcBand) {
2030        Number[] params = { PARAM_MBC_BAND,
2031                channelIndex,
2032                bandIndex};
2033        Number[] values = {(mbcBand.isEnabled() ? 1 : 0),
2034                mbcBand.getCutoffFrequency(),
2035                mbcBand.getAttackTime(),
2036                mbcBand.getReleaseTime(),
2037                mbcBand.getRatio(),
2038                mbcBand.getThreshold(),
2039                mbcBand.getKneeWidth(),
2040                mbcBand.getNoiseGateThreshold(),
2041                mbcBand.getExpanderRatio(),
2042                mbcBand.getPreGain(),
2043                mbcBand.getPostGain()};
2044        setNumberArray(params, values);
2045    }
2046
2047    private void updateEngineMbcByChannelIndex(int channelIndex, @NonNull Mbc mbc) {
2048        int bandCount = mbc.getBandCount();
2049        Number[] params = { PARAM_MBC,
2050                channelIndex};
2051        Number[] values = {(mbc.isInUse() ? 1 : 0),
2052                (mbc.isEnabled() ? 1 : 0),
2053                bandCount};
2054        setNumberArray(params, values);
2055        for (int b = 0; b < bandCount; b++) {
2056            MbcBand mbcBand = mbc.getBand(b);
2057            updateEngineMbcBandByChannelIndex(channelIndex, b, mbcBand);
2058        }
2059    }
2060
2061    private void updateEngineLimiterByChannelIndex(int channelIndex, @NonNull Limiter limiter) {
2062        Number[] params = { PARAM_LIMITER,
2063                channelIndex};
2064        Number[] values = {(limiter.isInUse() ? 1 : 0),
2065                (limiter.isEnabled() ? 1 : 0),
2066                limiter.getLinkGroup(),
2067                limiter.getAttackTime(),
2068                limiter.getReleaseTime(),
2069                limiter.getRatio(),
2070                limiter.getThreshold(),
2071                limiter.getPostGain()};
2072        setNumberArray(params, values);
2073    }
2074
2075    private Limiter queryEngineLimiterByChannelIndex(int channelIndex) {
2076        Number[] params = {PARAM_LIMITER,
2077                channelIndex};
2078        Number[] values = {0 /*0 in use (int)*/,
2079                0 /*1 enabled (int)*/,
2080                0 /*2 link group (int)*/,
2081                0.0f /*3 attack time (float)*/,
2082                0.0f /*4 release time (float)*/,
2083                0.0f /*5 ratio (float)*/,
2084                0.0f /*6 threshold (float)*/,
2085                0.0f /*7 post gain(float)*/};
2086
2087        byte[] paramBytes = numberArrayToByteArray(params);
2088        byte[] valueBytes = numberArrayToByteArray(values); //just interest in the byte size.
2089        getParameter(paramBytes, valueBytes);
2090        byteArrayToNumberArray(valueBytes, values);
2091
2092        return new Limiter(values[0].intValue() > 0 /*in use*/,
2093                values[1].intValue() > 0 /*enabled*/,
2094                values[2].intValue() /*linkGroup*/,
2095                values[3].floatValue() /*attackTime*/,
2096                values[4].floatValue() /*releaseTime*/,
2097                values[5].floatValue() /*ratio*/,
2098                values[6].floatValue() /*threshold*/,
2099                values[7].floatValue() /*postGain*/);
2100    }
2101
2102    private Channel queryEngineByChannelIndex(int channelIndex) {
2103        float inputGain = getTwoFloat(PARAM_INPUT_GAIN, channelIndex);
2104        Eq preEq = queryEngineEqByChannelIndex(PARAM_PRE_EQ, channelIndex);
2105        Mbc mbc = queryEngineMbcByChannelIndex(channelIndex);
2106        Eq postEq = queryEngineEqByChannelIndex(PARAM_POST_EQ, channelIndex);
2107        Limiter limiter = queryEngineLimiterByChannelIndex(channelIndex);
2108
2109        Channel channel = new Channel(inputGain,
2110                preEq.isInUse(), preEq.getBandCount(),
2111                mbc.isInUse(), mbc.getBandCount(),
2112                postEq.isInUse(), postEq.getBandCount(),
2113                limiter.isInUse());
2114        channel.setInputGain(inputGain);
2115        channel.setPreEq(preEq);
2116        channel.setMbc(mbc);
2117        channel.setPostEq(postEq);
2118        channel.setLimiter(limiter);
2119        return channel;
2120    }
2121
2122    private void updateEngineChannelByChannelIndex(int channelIndex, @NonNull Channel channel) {
2123        //send things with as few calls as possible
2124        setTwoFloat(PARAM_INPUT_GAIN, channelIndex, channel.getInputGain());
2125        Eq preEq = channel.getPreEq();
2126        updateEngineEqByChannelIndex(PARAM_PRE_EQ, channelIndex, preEq);
2127        Mbc mbc = channel.getMbc();
2128        updateEngineMbcByChannelIndex(channelIndex, mbc);
2129        Eq postEq = channel.getPostEq();
2130        updateEngineEqByChannelIndex(PARAM_POST_EQ, channelIndex, postEq);
2131        Limiter limiter = channel.getLimiter();
2132        updateEngineLimiterByChannelIndex(channelIndex, limiter);
2133    }
2134
2135    //****** convenience methods:
2136    //
2137    private int getOneInt(int param) {
2138        final int[] params = { param };
2139        final int[] result = new int[1];
2140
2141        checkStatus(getParameter(params, result));
2142        return result[0];
2143    }
2144
2145    private void setTwoFloat(int param, int paramA, float valueSet) {
2146        final int[] params = { param, paramA };
2147        final byte[] value;
2148
2149        value = floatToByteArray(valueSet);
2150        checkStatus(setParameter(params, value));
2151    }
2152
2153    private byte[] numberArrayToByteArray(Number[] values) {
2154        int expectedBytes = 0;
2155        for (int i = 0; i < values.length; i++) {
2156            if (values[i] instanceof Integer) {
2157                expectedBytes += Integer.BYTES;
2158            } else if (values[i] instanceof Float) {
2159                expectedBytes += Float.BYTES;
2160            } else {
2161                throw new IllegalArgumentException("unknown value type " +
2162                        values[i].getClass());
2163            }
2164        }
2165        ByteBuffer converter = ByteBuffer.allocate(expectedBytes);
2166        converter.order(ByteOrder.nativeOrder());
2167        for (int i = 0; i < values.length; i++) {
2168            if (values[i] instanceof Integer) {
2169                converter.putInt(values[i].intValue());
2170            } else if (values[i] instanceof Float) {
2171                converter.putFloat(values[i].floatValue());
2172            }
2173        }
2174        return converter.array();
2175    }
2176
2177    private void byteArrayToNumberArray(byte[] valuesIn, Number[] valuesOut) {
2178        int inIndex = 0;
2179        int outIndex = 0;
2180        while (inIndex < valuesIn.length && outIndex < valuesOut.length) {
2181            if (valuesOut[outIndex] instanceof Integer) {
2182                valuesOut[outIndex++] = byteArrayToInt(valuesIn, inIndex);
2183                inIndex += Integer.BYTES;
2184            } else if (valuesOut[outIndex] instanceof Float) {
2185                valuesOut[outIndex++] = byteArrayToFloat(valuesIn, inIndex);
2186                inIndex += Float.BYTES;
2187            } else {
2188                throw new IllegalArgumentException("can't convert " +
2189                        valuesOut[outIndex].getClass());
2190            }
2191        }
2192        if (outIndex != valuesOut.length) {
2193            throw new IllegalArgumentException("only converted " + outIndex +
2194                    " values out of "+ valuesOut.length + " expected");
2195        }
2196    }
2197
2198    private void setNumberArray(Number[] params, Number[] values) {
2199        byte[] paramBytes = numberArrayToByteArray(params);
2200        byte[] valueBytes = numberArrayToByteArray(values);
2201        checkStatus(setParameter(paramBytes, valueBytes));
2202    }
2203
2204    private float getTwoFloat(int param, int paramA) {
2205        final int[] params = { param, paramA };
2206        final byte[] result = new byte[4];
2207
2208        checkStatus(getParameter(params, result));
2209        return byteArrayToFloat(result);
2210    }
2211
2212    /**
2213     * @hide
2214     * The OnParameterChangeListener interface defines a method called by the DynamicsProcessing
2215     * when a parameter value has changed.
2216     */
2217    public interface OnParameterChangeListener {
2218        /**
2219         * Method called when a parameter value has changed. The method is called only if the
2220         * parameter was changed by another application having the control of the same
2221         * DynamicsProcessing engine.
2222         * @param effect the DynamicsProcessing on which the interface is registered.
2223         * @param param ID of the modified parameter. See {@link #PARAM_GENERIC_PARAM1} ...
2224         * @param value the new parameter value.
2225         */
2226        void onParameterChange(DynamicsProcessing effect, int param, int value);
2227    }
2228
2229    /**
2230     * helper method to update effect architecture parameters
2231     */
2232    private void updateEffectArchitecture() {
2233        mChannelCount = getChannelCount();
2234    }
2235
2236    /**
2237     * Listener used internally to receive unformatted parameter change events from AudioEffect
2238     * super class.
2239     */
2240    private class BaseParameterListener implements AudioEffect.OnParameterChangeListener {
2241        private BaseParameterListener() {
2242
2243        }
2244        public void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value) {
2245            // only notify when the parameter was successfully change
2246            if (status != AudioEffect.SUCCESS) {
2247                return;
2248            }
2249            OnParameterChangeListener l = null;
2250            synchronized (mParamListenerLock) {
2251                if (mParamListener != null) {
2252                    l = mParamListener;
2253                }
2254            }
2255            if (l != null) {
2256                int p = -1;
2257                int v = Integer.MIN_VALUE;
2258
2259                if (param.length == 4) {
2260                    p = byteArrayToInt(param, 0);
2261                }
2262                if (value.length == 4) {
2263                    v = byteArrayToInt(value, 0);
2264                }
2265                if (p != -1 && v != Integer.MIN_VALUE) {
2266                    l.onParameterChange(DynamicsProcessing.this, p, v);
2267                }
2268            }
2269        }
2270    }
2271
2272    /**
2273     * @hide
2274     * Registers an OnParameterChangeListener interface.
2275     * @param listener OnParameterChangeListener interface registered
2276     */
2277    public void setParameterListener(OnParameterChangeListener listener) {
2278        synchronized (mParamListenerLock) {
2279            if (mParamListener == null) {
2280                mBaseParamListener = new BaseParameterListener();
2281                super.setParameterListener(mBaseParamListener);
2282            }
2283            mParamListener = listener;
2284        }
2285    }
2286
2287    /**
2288     * @hide
2289     * The Settings class regroups the DynamicsProcessing parameters. It is used in
2290     * conjunction with the getProperties() and setProperties() methods to backup and restore
2291     * all parameters in a single call.
2292     */
2293
2294    public static class Settings {
2295        public int channelCount;
2296        public float[] inputGain;
2297
2298        public Settings() {
2299        }
2300
2301        /**
2302         * Settings class constructor from a key=value; pairs formatted string. The string is
2303         * typically returned by Settings.toString() method.
2304         * @throws IllegalArgumentException if the string is not correctly formatted.
2305         */
2306        public Settings(String settings) {
2307            StringTokenizer st = new StringTokenizer(settings, "=;");
2308            //int tokens = st.countTokens();
2309            if (st.countTokens() != 3) {
2310                throw new IllegalArgumentException("settings: " + settings);
2311            }
2312            String key = st.nextToken();
2313            if (!key.equals("DynamicsProcessing")) {
2314                throw new IllegalArgumentException(
2315                        "invalid settings for DynamicsProcessing: " + key);
2316            }
2317            try {
2318                key = st.nextToken();
2319                if (!key.equals("channelCount")) {
2320                    throw new IllegalArgumentException("invalid key name: " + key);
2321                }
2322                channelCount = Short.parseShort(st.nextToken());
2323                if (channelCount > CHANNEL_COUNT_MAX) {
2324                    throw new IllegalArgumentException("too many channels Settings:" + settings);
2325                }
2326                if (st.countTokens() != channelCount*1) { //check expected parameters.
2327                    throw new IllegalArgumentException("settings: " + settings);
2328                }
2329                //check to see it is ok the size
2330                inputGain = new float[channelCount];
2331                for (int ch = 0; ch < channelCount; ch++) {
2332                    key = st.nextToken();
2333                    if (!key.equals(ch +"_inputGain")) {
2334                        throw new IllegalArgumentException("invalid key name: " + key);
2335                    }
2336                    inputGain[ch] = Float.parseFloat(st.nextToken());
2337                }
2338             } catch (NumberFormatException nfe) {
2339                throw new IllegalArgumentException("invalid value for key: " + key);
2340            }
2341        }
2342
2343        @Override
2344        public String toString() {
2345            String str = new String (
2346                    "DynamicsProcessing"+
2347                    ";channelCount="+Integer.toString(channelCount));
2348                    for (int ch = 0; ch < channelCount; ch++) {
2349                        str = str.concat(";"+ch+"_inputGain="+Float.toString(inputGain[ch]));
2350                    }
2351            return str;
2352        }
2353    };
2354
2355
2356    /**
2357     * @hide
2358     * Gets the DynamicsProcessing properties. This method is useful when a snapshot of current
2359     * effect settings must be saved by the application.
2360     * @return a DynamicsProcessing.Settings object containing all current parameters values
2361     */
2362    public DynamicsProcessing.Settings getProperties() {
2363        Settings settings = new Settings();
2364
2365        //TODO: just for testing, we are calling the getters one by one, this is
2366        // supposed to be done in a single (or few calls) and get all the parameters at once.
2367
2368        settings.channelCount = getChannelCount();
2369
2370        if (settings.channelCount > CHANNEL_COUNT_MAX) {
2371            throw new IllegalArgumentException("too many channels Settings:" + settings);
2372        }
2373
2374        { // get inputGainmB per channel
2375            settings.inputGain = new float [settings.channelCount];
2376            for (int ch = 0; ch < settings.channelCount; ch++) {
2377//TODO:with config                settings.inputGain[ch] = getInputGain(ch);
2378            }
2379        }
2380        return settings;
2381    }
2382
2383    /**
2384     * @hide
2385     * Sets the DynamicsProcessing properties. This method is useful when bass boost settings
2386     * have to be applied from a previous backup.
2387     * @param settings a DynamicsProcessing.Settings object containing the properties to apply
2388     */
2389    public void setProperties(DynamicsProcessing.Settings settings) {
2390
2391        if (settings.channelCount != settings.inputGain.length ||
2392                settings.channelCount != mChannelCount) {
2393                throw new IllegalArgumentException("settings invalid channel count: "
2394                + settings.channelCount);
2395            }
2396
2397        //TODO: for now calling multiple times.
2398        for (int ch = 0; ch < mChannelCount; ch++) {
2399//TODO: use config            setInputGain(ch, settings.inputGain[ch]);
2400        }
2401    }
2402}
2403