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