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 
17 package android.media.audiofx;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.media.AudioTrack;
22 import android.media.MediaPlayer;
23 import android.media.audiofx.AudioEffect;
24 import android.util.Log;
25 
26 import java.nio.ByteBuffer;
27 import java.nio.ByteOrder;
28 import 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 
90 public 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      */
DynamicsProcessing(int audioSession)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      */
DynamicsProcessing(int priority, int audioSession)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      */
DynamicsProcessing(int priority, int audioSession, @Nullable Config cfg)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      */
getConfig()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          */
Stage(boolean inUse, boolean enabled)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          */
isEnabled()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          */
setEnabled(boolean enabled)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          */
isInUse()344         public boolean isInUse() {
345             return mInUse;
346         }
347 
348         @Override
toString()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          */
BandStage(boolean inUse, boolean enabled, int bandCount)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          */
getBandCount()382         public int getBandCount() {
383             return mBandCount;
384         }
385 
386         @Override
toString()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          */
BandBase(boolean enabled, float cutoffFrequency)412         public BandBase(boolean enabled, float cutoffFrequency) {
413             mEnabled = enabled;
414             mCutoffFrequency = cutoffFrequency;
415         }
416 
417         @Override
toString()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          */
isEnabled()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          */
setEnabled(boolean enabled)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          */
getCutoffFrequency()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          */
setCutoffFrequency(float frequency)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          */
EqBand(boolean enabled, float cutoffFrequency, float gain)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          */
EqBand(EqBand cfg)486         public EqBand(EqBand cfg) {
487             super(cfg.isEnabled(), cfg.getCutoffFrequency());
488             mGain = cfg.mGain;
489         }
490 
491         @Override
toString()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          */
getGain()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          */
setGain(float gain)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          */
MbcBand(boolean enabled, float cutoffFrequency, float attackTime, float releaseTime, float ratio, float threshold, float kneeWidth, float noiseGateThreshold, float expanderRatio, float preGain, float postGain)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          */
MbcBand(MbcBand cfg)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
toString()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          */
getAttackTime()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          */
setAttackTime(float attackTime)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          */
getReleaseTime()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          */
setReleaseTime(float releaseTime)619         public void setReleaseTime(float releaseTime) { mReleaseTime = releaseTime; }
620         /**
621          * gets the compressor ratio (N:1)
622          * @return compressor ratio (N:1)
623          */
getRatio()624         public float getRatio() { return mRatio; }
625         /**
626          * sets compressor ratio (N:1)
627          * @param ratio desired for the compressor (N:1)
628          */
setRatio(float ratio)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          */
getThreshold()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          */
setThreshold(float threshold)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          */
getKneeWidth()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          */
setKneeWidth(float kneeWidth)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          */
getNoiseGateThreshold()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          */
setNoiseGateThreshold(float noiseGateThreshold)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          */
getExpanderRatio()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          */
setExpanderRatio(float expanderRatio)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          */
getPreGain()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          */
setPreGain(float preGain)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          */
getPostGain()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          */
setPostGain(float postGain)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          */
Eq(boolean inUse, boolean enabled, int bandCount)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          */
Eq(Eq cfg)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
toString()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          */
checkBand(int band)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          */
setBand(int band, EqBand bandCfg)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          */
getBand(int band)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          */
Mbc(boolean inUse, boolean enabled, int bandCount)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          */
Mbc(Mbc cfg)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
toString()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          */
checkBand(int band)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          */
setBand(int band, MbcBand bandCfg)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          */
getBand(int band)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          */
Limiter(boolean inUse, boolean enabled, int linkGroup, float attackTime, float releaseTime, float ratio, float threshold, float postGain)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          */
Limiter(Limiter cfg)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
toString()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          */
getLinkGroup()961         public int getLinkGroup() { return mLinkGroup; }
962         /**
963          * Sets the linkGroup index for this limiter Stage.
964          * @param linkGroup desired linkGroup index
965          */
setLinkGroup(int linkGroup)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          */
getAttackTime()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          */
setAttackTime(float attackTime)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          */
getReleaseTime()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          */
setReleaseTime(float releaseTime)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          */
getRatio()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          */
setRatio(float ratio)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          */
getThreshold()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          */
setThreshold(float threshold)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          */
getPostGain()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          */
setPostGain(float postGain)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          */
Channel(float inputGain, boolean preEqInUse, int preEqBandCount, boolean mbcInUse, int mbcBandCount, boolean postEqInUse, int postEqBandCount, boolean limiterInUse)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          */
Channel(Channel cfg)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
toString()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          */
getInputGain()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          */
setInputGain(float inputGain)1104         public void setInputGain(float inputGain) {
1105             mInputGain = inputGain;
1106         }
1107 
1108         /**
1109          * Gets PreEq configuration stage
1110          * @return PreEq configuration stage
1111          */
getPreEq()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          */
setPreEq(Eq preEq)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          */
getPreEqBand(int band)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          */
setPreEqBand(int band, EqBand preEqBand)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          */
getMbc()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          */
setMbc(Mbc mbc)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          */
getMbcBand(int band)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          */
setMbcBand(int band, MbcBand mbcBand)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          */
getPostEq()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          */
setPostEq(Eq postEq)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          */
getPostEqBand(int band)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          */
setPostEqBand(int band, EqBand postEqBand)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          */
getLimiter()1220         public Limiter getLimiter() {
1221             return mLimiter;
1222         }
1223         /**
1224          * Sets Limiter configuration stage.
1225          * @param limiter configuration stage.
1226          */
setLimiter(Limiter limiter)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          */
Config(int variant, float frameDurationMs, int channelCount, boolean preEqInUse, int preEqBandCount, boolean mbcInUse, int mbcBandCount, boolean postEqInUse, int postEqBandCount, boolean limiterInUse, Channel[] channel)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          */
Config(int channelCount, Config cfg)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          */
Config(@onNull Config cfg)1340         public Config(@NonNull Config cfg) {
1341             this(cfg.mChannelCount, cfg);
1342         }
1343 
1344         @Override
toString()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         }
checkChannel(int channelIndex)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          */
getVariant()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          */
getPreferredFrameDuration()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          */
isPreEqInUse()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          */
getPreEqBandCount()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          */
isMbcInUse()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          */
getMbcBandCount()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          */
isPostEqInUse()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          */
getPostEqBandCount()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          */
isLimiterInUse()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          */
getChannelByChannelIndex(int channelIndex)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          */
setChannelTo(int channelIndex, Channel channel)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          */
setAllChannelsTo(Channel channel)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          */
getInputGainByChannelIndex(int channelIndex)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          */
setInputGainByChannelIndex(int channelIndex, float inputGain)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          */
setInputGainAllChannelsTo(float inputGain)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          */
getPreEqByChannelIndex(int channelIndex)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          */
setPreEqByChannelIndex(int channelIndex, Eq preEq)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          */
setPreEqAllChannelsTo(Eq preEq)1535         public void setPreEqAllChannelsTo(Eq preEq) {
1536             for (int ch = 0; ch < mChannel.length; ch++) {
1537                 mChannel[ch].setPreEq(preEq);
1538             }
1539         }
getPreEqBandByChannelIndex(int channelIndex, int band)1540         public EqBand getPreEqBandByChannelIndex(int channelIndex, int band) {
1541             checkChannel(channelIndex);
1542             return mChannel[channelIndex].getPreEqBand(band);
1543         }
setPreEqBandByChannelIndex(int channelIndex, int band, EqBand preEqBand)1544         public void setPreEqBandByChannelIndex(int channelIndex, int band, EqBand preEqBand) {
1545             checkChannel(channelIndex);
1546             mChannel[channelIndex].setPreEqBand(band, preEqBand);
1547         }
setPreEqBandAllChannelsTo(int band, EqBand preEqBand)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
getMbcByChannelIndex(int channelIndex)1555         public Mbc getMbcByChannelIndex(int channelIndex) {
1556             checkChannel(channelIndex);
1557             return mChannel[channelIndex].getMbc();
1558         }
setMbcByChannelIndex(int channelIndex, Mbc mbc)1559         public void setMbcByChannelIndex(int channelIndex, Mbc mbc) {
1560             checkChannel(channelIndex);
1561             mChannel[channelIndex].setMbc(mbc);
1562         }
setMbcAllChannelsTo(Mbc mbc)1563         public void setMbcAllChannelsTo(Mbc mbc) {
1564             for (int ch = 0; ch < mChannel.length; ch++) {
1565                 mChannel[ch].setMbc(mbc);
1566             }
1567         }
getMbcBandByChannelIndex(int channelIndex, int band)1568         public MbcBand getMbcBandByChannelIndex(int channelIndex, int band) {
1569             checkChannel(channelIndex);
1570             return mChannel[channelIndex].getMbcBand(band);
1571         }
setMbcBandByChannelIndex(int channelIndex, int band, MbcBand mbcBand)1572         public void setMbcBandByChannelIndex(int channelIndex, int band, MbcBand mbcBand) {
1573             checkChannel(channelIndex);
1574             mChannel[channelIndex].setMbcBand(band, mbcBand);
1575         }
setMbcBandAllChannelsTo(int band, MbcBand mbcBand)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
getPostEqByChannelIndex(int channelIndex)1583         public Eq getPostEqByChannelIndex(int channelIndex) {
1584             checkChannel(channelIndex);
1585             return mChannel[channelIndex].getPostEq();
1586         }
setPostEqByChannelIndex(int channelIndex, Eq postEq)1587         public void setPostEqByChannelIndex(int channelIndex, Eq postEq) {
1588             checkChannel(channelIndex);
1589             mChannel[channelIndex].setPostEq(postEq);
1590         }
setPostEqAllChannelsTo(Eq postEq)1591         public void setPostEqAllChannelsTo(Eq postEq) {
1592             for (int ch = 0; ch < mChannel.length; ch++) {
1593                 mChannel[ch].setPostEq(postEq);
1594             }
1595         }
getPostEqBandByChannelIndex(int channelIndex, int band)1596         public EqBand getPostEqBandByChannelIndex(int channelIndex, int band) {
1597             checkChannel(channelIndex);
1598             return mChannel[channelIndex].getPostEqBand(band);
1599         }
setPostEqBandByChannelIndex(int channelIndex, int band, EqBand postEqBand)1600         public void setPostEqBandByChannelIndex(int channelIndex, int band, EqBand postEqBand) {
1601             checkChannel(channelIndex);
1602             mChannel[channelIndex].setPostEqBand(band, postEqBand);
1603         }
setPostEqBandAllChannelsTo(int band, EqBand postEqBand)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
getLimiterByChannelIndex(int channelIndex)1611         public Limiter getLimiterByChannelIndex(int channelIndex) {
1612             checkChannel(channelIndex);
1613             return mChannel[channelIndex].getLimiter();
1614         }
setLimiterByChannelIndex(int channelIndex, Limiter limiter)1615         public void setLimiterByChannelIndex(int channelIndex, Limiter limiter) {
1616             checkChannel(channelIndex);
1617             mChannel[channelIndex].setLimiter(limiter);
1618         }
setLimiterAllChannelsTo(Limiter limiter)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 
Builder(int variant, int channelCount, boolean preEqInUse, int preEqBandCount, boolean mbcInUse, int mbcBandCount, boolean postEqInUse, int postEqBandCount, boolean limiterInUse)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 
checkChannel(int channelIndex)1662             private void checkChannel(int channelIndex) {
1663                 if (channelIndex < 0 || channelIndex >= mChannel.length) {
1664                     throw new IllegalArgumentException("ChannelIndex out of bounds");
1665                 }
1666             }
1667 
setPreferredFrameDuration(float frameDuration)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 
setInputGainByChannelIndex(int channelIndex, float inputGain)1676             public Builder setInputGainByChannelIndex(int channelIndex, float inputGain) {
1677                 checkChannel(channelIndex);
1678                 mChannel[channelIndex].setInputGain(inputGain);
1679                 return this;
1680             }
setInputGainAllChannelsTo(float inputGain)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 
setChannelTo(int channelIndex, Channel channel)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             }
setAllChannelsTo(Channel channel)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 
setPreEqByChannelIndex(int channelIndex, Eq preEq)1713             public Builder setPreEqByChannelIndex(int channelIndex, Eq preEq) {
1714                 checkChannel(channelIndex);
1715                 mChannel[channelIndex].setPreEq(preEq);
1716                 return this;
1717             }
setPreEqAllChannelsTo(Eq preEq)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 
setMbcByChannelIndex(int channelIndex, Mbc mbc)1725             public Builder setMbcByChannelIndex(int channelIndex, Mbc mbc) {
1726                 checkChannel(channelIndex);
1727                 mChannel[channelIndex].setMbc(mbc);
1728                 return this;
1729             }
setMbcAllChannelsTo(Mbc mbc)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 
setPostEqByChannelIndex(int channelIndex, Eq postEq)1737             public Builder setPostEqByChannelIndex(int channelIndex, Eq postEq) {
1738                 checkChannel(channelIndex);
1739                 mChannel[channelIndex].setPostEq(postEq);
1740                 return this;
1741             }
setPostEqAllChannelsTo(Eq postEq)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 
setLimiterByChannelIndex(int channelIndex, Limiter limiter)1749             public Builder setLimiterByChannelIndex(int channelIndex, Limiter limiter) {
1750                 checkChannel(channelIndex);
1751                 mChannel[channelIndex].setLimiter(limiter);
1752                 return this;
1753             }
setLimiterAllChannelsTo(Limiter limiter)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 
build()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
getChannelByChannelIndex(int channelIndex)1771     public Channel getChannelByChannelIndex(int channelIndex) {
1772         return queryEngineByChannelIndex(channelIndex);
1773     }
1774 
setChannelTo(int channelIndex, Channel channel)1775     public void setChannelTo(int channelIndex, Channel channel) {
1776         updateEngineChannelByChannelIndex(channelIndex, channel);
1777     }
1778 
setAllChannelsTo(Channel channel)1779     public void setAllChannelsTo(Channel channel) {
1780         for (int ch = 0; ch < mChannelCount; ch++) {
1781             setChannelTo(ch, channel);
1782         }
1783     }
1784 
1785     //=== channel params
getInputGainByChannelIndex(int channelIndex)1786     public float getInputGainByChannelIndex(int channelIndex) {
1787         return getTwoFloat(PARAM_INPUT_GAIN, channelIndex);
1788     }
setInputGainbyChannel(int channelIndex, float inputGain)1789     public void setInputGainbyChannel(int channelIndex, float inputGain) {
1790         setTwoFloat(PARAM_INPUT_GAIN, channelIndex, inputGain);
1791     }
setInputGainAllChannelsTo(float inputGain)1792     public void setInputGainAllChannelsTo(float inputGain) {
1793         for (int ch = 0; ch < mChannelCount; ch++) {
1794             setInputGainbyChannel(ch, inputGain);
1795         }
1796     }
1797 
1798     //=== PreEQ
getPreEqByChannelIndex(int channelIndex)1799     public Eq getPreEqByChannelIndex(int channelIndex) {
1800         return queryEngineEqByChannelIndex(PARAM_PRE_EQ, channelIndex);
1801     }
setPreEqByChannelIndex(int channelIndex, Eq preEq)1802     public void setPreEqByChannelIndex(int channelIndex, Eq preEq) {
1803         updateEngineEqByChannelIndex(PARAM_PRE_EQ, channelIndex, preEq);
1804     }
setPreEqAllChannelsTo(Eq preEq)1805     public void setPreEqAllChannelsTo(Eq preEq) {
1806         for (int ch = 0; ch < mChannelCount; ch++) {
1807             setPreEqByChannelIndex(ch, preEq);
1808         }
1809     }
getPreEqBandByChannelIndex(int channelIndex, int band)1810     public EqBand getPreEqBandByChannelIndex(int channelIndex, int band) {
1811         return queryEngineEqBandByChannelIndex(PARAM_PRE_EQ_BAND, channelIndex, band);
1812     }
setPreEqBandByChannelIndex(int channelIndex, int band, EqBand preEqBand)1813     public void setPreEqBandByChannelIndex(int channelIndex, int band, EqBand preEqBand) {
1814         updateEngineEqBandByChannelIndex(PARAM_PRE_EQ_BAND, channelIndex, band, preEqBand);
1815     }
setPreEqBandAllChannelsTo(int band, EqBand preEqBand)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
getMbcByChannelIndex(int channelIndex)1823     public Mbc getMbcByChannelIndex(int channelIndex) {
1824         return queryEngineMbcByChannelIndex(channelIndex);
1825     }
setMbcByChannelIndex(int channelIndex, Mbc mbc)1826     public void setMbcByChannelIndex(int channelIndex, Mbc mbc) {
1827         updateEngineMbcByChannelIndex(channelIndex, mbc);
1828     }
setMbcAllChannelsTo(Mbc mbc)1829     public void setMbcAllChannelsTo(Mbc mbc) {
1830         for (int ch = 0; ch < mChannelCount; ch++) {
1831             setMbcByChannelIndex(ch, mbc);
1832         }
1833     }
getMbcBandByChannelIndex(int channelIndex, int band)1834     public MbcBand getMbcBandByChannelIndex(int channelIndex, int band) {
1835         return queryEngineMbcBandByChannelIndex(channelIndex, band);
1836     }
setMbcBandByChannelIndex(int channelIndex, int band, MbcBand mbcBand)1837     public void setMbcBandByChannelIndex(int channelIndex, int band, MbcBand mbcBand) {
1838         updateEngineMbcBandByChannelIndex(channelIndex, band, mbcBand);
1839     }
setMbcBandAllChannelsTo(int band, MbcBand mbcBand)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
getPostEqByChannelIndex(int channelIndex)1847     public Eq getPostEqByChannelIndex(int channelIndex) {
1848         return queryEngineEqByChannelIndex(PARAM_POST_EQ, channelIndex);
1849     }
setPostEqByChannelIndex(int channelIndex, Eq postEq)1850     public void setPostEqByChannelIndex(int channelIndex, Eq postEq) {
1851         updateEngineEqByChannelIndex(PARAM_POST_EQ, channelIndex, postEq);
1852     }
setPostEqAllChannelsTo(Eq postEq)1853     public void setPostEqAllChannelsTo(Eq postEq) {
1854         for (int ch = 0; ch < mChannelCount; ch++) {
1855             setPostEqByChannelIndex(ch, postEq);
1856         }
1857     }
getPostEqBandByChannelIndex(int channelIndex, int band)1858     public EqBand getPostEqBandByChannelIndex(int channelIndex, int band) {
1859         return queryEngineEqBandByChannelIndex(PARAM_POST_EQ_BAND, channelIndex, band);
1860     }
setPostEqBandByChannelIndex(int channelIndex, int band, EqBand postEqBand)1861     public void setPostEqBandByChannelIndex(int channelIndex, int band, EqBand postEqBand) {
1862         updateEngineEqBandByChannelIndex(PARAM_POST_EQ_BAND, channelIndex, band, postEqBand);
1863     }
setPostEqBandAllChannelsTo(int band, EqBand postEqBand)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
getLimiterByChannelIndex(int channelIndex)1871     public Limiter getLimiterByChannelIndex(int channelIndex) {
1872         return queryEngineLimiterByChannelIndex(channelIndex);
1873     }
setLimiterByChannelIndex(int channelIndex, Limiter limiter)1874     public void setLimiterByChannelIndex(int channelIndex, Limiter limiter) {
1875         updateEngineLimiterByChannelIndex(channelIndex, limiter);
1876     }
setLimiterAllChannelsTo(Limiter limiter)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      */
getChannelCount()1887     public int getChannelCount() {
1888         return getOneInt(PARAM_GET_CHANNEL_COUNT);
1889     }
1890 
1891     //=== Engine calls
setEngineArchitecture(int variant, float preferredFrameDuration, boolean preEqInUse, int preEqBandCount, boolean mbcInUse, int mbcBandCount, boolean postEqInUse, int postEqBandCount, boolean limiterInUse)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 
updateEngineEqBandByChannelIndex(int param, int channelIndex, int bandIndex, @NonNull EqBand eqBand)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     }
queryEngineEqByChannelIndex(int param, int channelIndex)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     }
queryEngineEqBandByChannelIndex(int param, int channelIndex, int bandIndex)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     }
updateEngineEqByChannelIndex(int param, int channelIndex, @NonNull Eq eq)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 
queryEngineMbcByChannelIndex(int channelIndex)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     }
queryEngineMbcBandByChannelIndex(int channelIndex, int bandIndex)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     }
updateEngineMbcBandByChannelIndex(int channelIndex, int bandIndex, @NonNull MbcBand mbcBand)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 
updateEngineMbcByChannelIndex(int channelIndex, @NonNull Mbc mbc)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 
updateEngineLimiterByChannelIndex(int channelIndex, @NonNull Limiter limiter)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 
queryEngineLimiterByChannelIndex(int channelIndex)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 
queryEngineByChannelIndex(int channelIndex)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 
updateEngineChannelByChannelIndex(int channelIndex, @NonNull Channel channel)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     //
getOneInt(int param)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 
setTwoFloat(int param, int paramA, float valueSet)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 
numberArrayToByteArray(Number[] values)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 
byteArrayToNumberArray(byte[] valuesIn, Number[] valuesOut)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 
setNumberArray(Number[] params, Number[] values)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 
getTwoFloat(int param, int paramA)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          */
onParameterChange(DynamicsProcessing effect, int param, int value)2226         void onParameterChange(DynamicsProcessing effect, int param, int value);
2227     }
2228 
2229     /**
2230      * helper method to update effect architecture parameters
2231      */
updateEffectArchitecture()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 {
BaseParameterListener()2241         private BaseParameterListener() {
2242 
2243         }
onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value)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      */
setParameterListener(OnParameterChangeListener listener)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 
Settings()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          */
Settings(String settings)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
toString()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      */
getProperties()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      */
setProperties(DynamicsProcessing.Settings settings)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