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