| /* |
| * Copyright (C) 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.media.audiofx; |
| |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.media.AudioTrack; |
| import android.media.MediaPlayer; |
| import android.media.audiofx.AudioEffect; |
| import android.util.Log; |
| |
| import java.nio.ByteBuffer; |
| import java.nio.ByteOrder; |
| import java.util.StringTokenizer; |
| |
| /** |
| * DynamicsProcessing is an audio effect for equalizing and changing dynamic range properties of the |
| * sound. It is composed of multiple stages including equalization, multi-band compression and |
| * limiter. |
| * <p>The number of bands and active stages is configurable, and most parameters can be controlled |
| * in realtime, such as gains, attack/release times, thresholds, etc. |
| * <p>The effect is instantiated and controlled by channels. Each channel has the same basic |
| * architecture, but all of their parameters are independent from other channels. |
| * <p>The basic channel configuration is: |
| * <pre> |
| * |
| * Channel 0 Channel 1 .... Channel N-1 |
| * Input Input Input |
| * | | | |
| * +----v----+ +----v----+ +----v----+ |
| * |inputGain| |inputGain| |inputGain| |
| * +---------+ +---------+ +---------+ |
| * | | | |
| * +-----v-----+ +-----v-----+ +-----v-----+ |
| * | PreEQ | | PreEQ | | PreEQ | |
| * +-----------+ +-----------+ +-----------+ |
| * | | | |
| * +-----v-----+ +-----v-----+ +-----v-----+ |
| * | MBC | | MBC | | MBC | |
| * +-----------+ +-----------+ +-----------+ |
| * | | | |
| * +-----v-----+ +-----v-----+ +-----v-----+ |
| * | PostEQ | | PostEQ | | PostEQ | |
| * +-----------+ +-----------+ +-----------+ |
| * | | | |
| * +-----v-----+ +-----v-----+ +-----v-----+ |
| * | Limiter | | Limiter | | Limiter | |
| * +-----------+ +-----------+ +-----------+ |
| * | | | |
| * Output Output Output |
| * </pre> |
| * |
| * <p>Where the stages are: |
| * inputGain: input gain factor in decibels (dB). 0 dB means no change in level. |
| * PreEQ: Multi-band Equalizer. |
| * MBC: Multi-band Compressor |
| * PostEQ: Multi-band Equalizer |
| * Limiter: Single band compressor/limiter. |
| * |
| * <p>An application creates a DynamicsProcessing object to instantiate and control this audio |
| * effect in the audio framework. A DynamicsProcessor.Config and DynamicsProcessor.Config.Builder |
| * are available to help configure the multiple stages and each band parameters if desired. |
| * <p>See each stage documentation for further details. |
| * <p>If no Config is specified during creation, a default configuration is chosen. |
| * <p>To attach the DynamicsProcessing to a particular AudioTrack or MediaPlayer, |
| * specify the audio session ID of this AudioTrack or MediaPlayer when constructing the effect |
| * (see {@link AudioTrack#getAudioSessionId()} and {@link MediaPlayer#getAudioSessionId()}). |
| * |
| * <p>To attach the DynamicsProcessing to a particular AudioTrack or MediaPlayer, specify the audio |
| * session ID of this AudioTrack or MediaPlayer when constructing the DynamicsProcessing. |
| * <p>See {@link android.media.MediaPlayer#getAudioSessionId()} for details on audio sessions. |
| * <p>See {@link android.media.audiofx.AudioEffect} class for more details on controlling audio |
| * effects. |
| */ |
| |
| public final class DynamicsProcessing extends AudioEffect { |
| |
| private final static String TAG = "DynamicsProcessing"; |
| |
| // These parameter constants must be synchronized with those in |
| // /system/media/audio_effects/include/audio_effects/effect_dynamicsprocessing.h |
| private static final int PARAM_GET_CHANNEL_COUNT = 0x10; |
| private static final int PARAM_INPUT_GAIN = 0x20; |
| private static final int PARAM_ENGINE_ARCHITECTURE = 0x30; |
| private static final int PARAM_PRE_EQ = 0x40; |
| private static final int PARAM_PRE_EQ_BAND = 0x45; |
| private static final int PARAM_MBC = 0x50; |
| private static final int PARAM_MBC_BAND = 0x55; |
| private static final int PARAM_POST_EQ = 0x60; |
| private static final int PARAM_POST_EQ_BAND = 0x65; |
| private static final int PARAM_LIMITER = 0x70; |
| |
| /** |
| * Index of variant that favors frequency resolution. Frequency domain based implementation. |
| */ |
| public static final int VARIANT_FAVOR_FREQUENCY_RESOLUTION = 0; |
| |
| /** |
| * Index of variant that favors time resolution resolution. Time domain based implementation. |
| */ |
| public static final int VARIANT_FAVOR_TIME_RESOLUTION = 1; |
| |
| /** |
| * Maximum expected channels to be reported by effect |
| */ |
| private static final int CHANNEL_COUNT_MAX = 32; |
| |
| /** |
| * Number of channels in effect architecture |
| */ |
| private int mChannelCount = 0; |
| |
| /** |
| * Registered listener for parameter changes. |
| */ |
| private OnParameterChangeListener mParamListener = null; |
| |
| /** |
| * Listener used internally to to receive raw parameter change events |
| * from AudioEffect super class |
| */ |
| private BaseParameterListener mBaseParamListener = null; |
| |
| /** |
| * Lock for access to mParamListener |
| */ |
| private final Object mParamListenerLock = new Object(); |
| |
| /** |
| * Class constructor. |
| * @param audioSession system-wide unique audio session identifier. The DynamicsProcessing |
| * will be attached to the MediaPlayer or AudioTrack in the same audio session. |
| */ |
| public DynamicsProcessing(int audioSession) { |
| this(0 /*priority*/, audioSession); |
| } |
| |
| /** |
| * @hide |
| * Class constructor for the DynamicsProcessing audio effect. |
| * @param priority the priority level requested by the application for controlling the |
| * DynamicsProcessing engine. As the same engine can be shared by several applications, |
| * this parameter indicates how much the requesting application needs control of effect |
| * parameters. The normal priority is 0, above normal is a positive number, below normal a |
| * negative number. |
| * @param audioSession system-wide unique audio session identifier. The DynamicsProcessing |
| * will be attached to the MediaPlayer or AudioTrack in the same audio session. |
| */ |
| public DynamicsProcessing(int priority, int audioSession) { |
| this(priority, audioSession, null); |
| } |
| |
| /** |
| * Class constructor for the DynamicsProcessing audio effect |
| * @param priority the priority level requested by the application for controlling the |
| * DynamicsProcessing engine. As the same engine can be shared by several applications, |
| * this parameter indicates how much the requesting application needs control of effect |
| * parameters. The normal priority is 0, above normal is a positive number, below normal a |
| * negative number. |
| * @param audioSession system-wide unique audio session identifier. The DynamicsProcessing |
| * will be attached to the MediaPlayer or AudioTrack in the same audio session. |
| * @param cfg Config object used to setup the audio effect, including bands per stage, and |
| * specific parameters for each stage/band. Use |
| * {@link android.media.audiofx.DynamicsProcessing.Config.Builder} to create a |
| * Config object that suits your needs. A null cfg parameter will create and use a default |
| * configuration for the effect |
| */ |
| public DynamicsProcessing(int priority, int audioSession, @Nullable Config cfg) { |
| super(EFFECT_TYPE_DYNAMICS_PROCESSING, EFFECT_TYPE_NULL, priority, audioSession); |
| if (audioSession == 0) { |
| Log.w(TAG, "WARNING: attaching a DynamicsProcessing to global output mix is" |
| + "deprecated!"); |
| } |
| final Config config; |
| mChannelCount = getChannelCount(); |
| if (cfg == null) { |
| //create a default configuration and effect, with the number of channels this effect has |
| DynamicsProcessing.Config.Builder builder = |
| new DynamicsProcessing.Config.Builder( |
| CONFIG_DEFAULT_VARIANT, |
| mChannelCount, |
| CONFIG_DEFAULT_USE_PREEQ, |
| CONFIG_DEFAULT_PREEQ_BANDS, |
| CONFIG_DEFAULT_USE_MBC, |
| CONFIG_DEFAULT_MBC_BANDS, |
| CONFIG_DEFAULT_USE_POSTEQ, |
| CONFIG_DEFAULT_POSTEQ_BANDS, |
| CONFIG_DEFAULT_USE_LIMITER); |
| config = builder.build(); |
| } else { |
| //validate channels are ok. decide what to do: replicate channels if more |
| config = new DynamicsProcessing.Config(mChannelCount, cfg); |
| } |
| |
| //configure engine |
| setEngineArchitecture(config.getVariant(), |
| config.getPreferredFrameDuration(), |
| config.isPreEqInUse(), |
| config.getPreEqBandCount(), |
| config.isMbcInUse(), |
| config.getMbcBandCount(), |
| config.isPostEqInUse(), |
| config.getPostEqBandCount(), |
| config.isLimiterInUse()); |
| //update all the parameters |
| for (int ch = 0; ch < mChannelCount; ch++) { |
| updateEngineChannelByChannelIndex(ch, config.getChannelByChannelIndex(ch)); |
| } |
| } |
| |
| /** |
| * Returns the Config object used to setup this effect. |
| * @return Config Current Config object used to setup this DynamicsProcessing effect. |
| */ |
| public Config getConfig() { |
| //Query engine architecture to create config object |
| Number[] params = { PARAM_ENGINE_ARCHITECTURE }; |
| Number[] values = { 0 /*0 variant */, |
| 0.0f /* 1 preferredFrameDuration */, |
| 0 /*2 preEqInUse */, |
| 0 /*3 preEqBandCount */, |
| 0 /*4 mbcInUse */, |
| 0 /*5 mbcBandCount*/, |
| 0 /*6 postEqInUse */, |
| 0 /*7 postEqBandCount */, |
| 0 /*8 limiterInUse */}; |
| byte[] paramBytes = numberArrayToByteArray(params); |
| byte[] valueBytes = numberArrayToByteArray(values); //just interest in the byte size. |
| getParameter(paramBytes, valueBytes); |
| byteArrayToNumberArray(valueBytes, values); |
| DynamicsProcessing.Config.Builder builder = |
| new DynamicsProcessing.Config.Builder( |
| values[0].intValue(), |
| mChannelCount, |
| values[2].intValue() > 0 /*use preEQ*/, |
| values[3].intValue() /*pre eq bands*/, |
| values[4].intValue() > 0 /*use mbc*/, |
| values[5].intValue() /*mbc bands*/, |
| values[6].intValue() > 0 /*use postEQ*/, |
| values[7].intValue()/*postEq bands*/, |
| values[8].intValue() > 0 /*use Limiter*/). |
| setPreferredFrameDuration(values[1].floatValue()); |
| Config config = builder.build(); |
| for (int ch = 0; ch < mChannelCount; ch++) { |
| Channel channel = queryEngineByChannelIndex(ch); |
| config.setChannelTo(ch, channel); |
| } |
| return config; |
| } |
| |
| |
| private static final int CONFIG_DEFAULT_VARIANT = VARIANT_FAVOR_FREQUENCY_RESOLUTION; |
| private static final boolean CONFIG_DEFAULT_USE_PREEQ = true; |
| private static final int CONFIG_DEFAULT_PREEQ_BANDS = 6; |
| private static final boolean CONFIG_DEFAULT_USE_MBC = true; |
| private static final int CONFIG_DEFAULT_MBC_BANDS = 6; |
| private static final boolean CONFIG_DEFAULT_USE_POSTEQ = true; |
| private static final int CONFIG_DEFAULT_POSTEQ_BANDS = 6; |
| private static final boolean CONFIG_DEFAULT_USE_LIMITER = true; |
| |
| private static final float CHANNEL_DEFAULT_INPUT_GAIN = 0; // dB |
| private static final float CONFIG_PREFERRED_FRAME_DURATION_MS = 10.0f; //milliseconds |
| |
| private static final float EQ_DEFAULT_GAIN = 0; // dB |
| private static final boolean PREEQ_DEFAULT_ENABLED = true; |
| private static final boolean POSTEQ_DEFAULT_ENABLED = true; |
| |
| |
| private static final boolean MBC_DEFAULT_ENABLED = true; |
| private static final float MBC_DEFAULT_ATTACK_TIME = 50; // ms |
| private static final float MBC_DEFAULT_RELEASE_TIME = 120; // ms |
| private static final float MBC_DEFAULT_RATIO = 2; // 1:N |
| private static final float MBC_DEFAULT_THRESHOLD = -30; // dB |
| private static final float MBC_DEFAULT_KNEE_WIDTH = 0; // dB |
| private static final float MBC_DEFAULT_NOISE_GATE_THRESHOLD = -80; // dB |
| private static final float MBC_DEFAULT_EXPANDER_RATIO = 1.5f; // N:1 |
| private static final float MBC_DEFAULT_PRE_GAIN = 0; // dB |
| private static final float MBC_DEFAULT_POST_GAIN = 10; // dB |
| |
| private static final boolean LIMITER_DEFAULT_ENABLED = true; |
| private static final int LIMITER_DEFAULT_LINK_GROUP = 0;//; |
| private static final float LIMITER_DEFAULT_ATTACK_TIME = 50; // ms |
| private static final float LIMITER_DEFAULT_RELEASE_TIME = 120; // ms |
| private static final float LIMITER_DEFAULT_RATIO = 2; // 1:N |
| private static final float LIMITER_DEFAULT_THRESHOLD = -30; // dB |
| private static final float LIMITER_DEFAULT_POST_GAIN = 10; // dB |
| |
| private static final float DEFAULT_MIN_FREQUENCY = 220; // Hz |
| private static final float DEFAULT_MAX_FREQUENCY = 20000; // Hz |
| private static final float mMinFreqLog = (float)Math.log10(DEFAULT_MIN_FREQUENCY); |
| private static final float mMaxFreqLog = (float)Math.log10(DEFAULT_MAX_FREQUENCY); |
| |
| /** |
| * base class for the different stages. |
| */ |
| public static class Stage { |
| private boolean mInUse; |
| private boolean mEnabled; |
| /** |
| * Class constructor for stage |
| * @param inUse true if this stage is set to be used. False otherwise. Stages that are not |
| * set "inUse" at initialization time are not available to be used at any time. |
| * @param enabled true if this stage is currently used to process sound. When disabled, |
| * the stage is bypassed and the sound is copied unaltered from input to output. |
| */ |
| public Stage(boolean inUse, boolean enabled) { |
| mInUse = inUse; |
| mEnabled = enabled; |
| } |
| |
| /** |
| * returns enabled state of the stage |
| * @return true if stage is enabled for processing, false otherwise |
| */ |
| public boolean isEnabled() { |
| return mEnabled; |
| } |
| /** |
| * sets enabled state of the stage |
| * @param enabled true for enabled, false otherwise |
| */ |
| public void setEnabled(boolean enabled) { |
| mEnabled = enabled; |
| } |
| |
| /** |
| * returns inUse state of the stage. |
| * @return inUse state of the stage. True if this stage is currently used to process sound. |
| * When false, the stage is bypassed and the sound is copied unaltered from input to output. |
| */ |
| public boolean isInUse() { |
| return mInUse; |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder sb = new StringBuilder(); |
| sb.append(String.format(" Stage InUse: %b\n", isInUse())); |
| if (isInUse()) { |
| sb.append(String.format(" Stage Enabled: %b\n", mEnabled)); |
| } |
| return sb.toString(); |
| } |
| } |
| |
| /** |
| * Base class for stages that hold bands |
| */ |
| public static class BandStage extends Stage{ |
| private int mBandCount; |
| /** |
| * Class constructor for BandStage |
| * @param inUse true if this stage is set to be used. False otherwise. Stages that are not |
| * set "inUse" at initialization time are not available to be used at any time. |
| * @param enabled true if this stage is currently used to process sound. When disabled, |
| * the stage is bypassed and the sound is copied unaltered from input to output. |
| * @param bandCount number of bands this stage will handle. If stage is not inUse, bandcount |
| * is set to 0 |
| */ |
| public BandStage(boolean inUse, boolean enabled, int bandCount) { |
| super(inUse, enabled); |
| mBandCount = isInUse() ? bandCount : 0; |
| } |
| |
| /** |
| * gets number of bands held in this stage |
| * @return number of bands held in this stage |
| */ |
| public int getBandCount() { |
| return mBandCount; |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder sb = new StringBuilder(); |
| sb.append(super.toString()); |
| if (isInUse()) { |
| sb.append(String.format(" Band Count: %d\n", mBandCount)); |
| } |
| return sb.toString(); |
| } |
| } |
| |
| /** |
| * Base class for bands |
| */ |
| public static class BandBase { |
| private boolean mEnabled; |
| private float mCutoffFrequency; |
| /** |
| * Class constructor for BandBase |
| * @param enabled true if this band is currently used to process sound. When false, |
| * the band is effectively muted and sound set to zero. |
| * @param cutoffFrequency topmost frequency number (in Hz) this band will process. The |
| * effective bandwidth for the band is then computed using this and the previous band |
| * topmost frequency (or 0 Hz for band number 0). Frequencies are expected to increase with |
| * band number, thus band 0 cutoffFrequency <= band 1 cutoffFrequency, and so on. |
| */ |
| public BandBase(boolean enabled, float cutoffFrequency) { |
| mEnabled = enabled; |
| mCutoffFrequency = cutoffFrequency; |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder sb = new StringBuilder(); |
| sb.append(String.format(" Enabled: %b\n", mEnabled)); |
| sb.append(String.format(" CutoffFrequency: %f\n", mCutoffFrequency)); |
| return sb.toString(); |
| } |
| |
| /** |
| * returns enabled state of the band |
| * @return true if bands is enabled for processing, false otherwise |
| */ |
| public boolean isEnabled() { |
| return mEnabled; |
| } |
| /** |
| * sets enabled state of the band |
| * @param enabled true for enabled, false otherwise |
| */ |
| public void setEnabled(boolean enabled) { |
| mEnabled = enabled; |
| } |
| |
| /** |
| * gets cutoffFrequency for this band in Hertz (Hz) |
| * @return cutoffFrequency for this band in Hertz (Hz) |
| */ |
| public float getCutoffFrequency() { |
| return mCutoffFrequency; |
| } |
| |
| /** |
| * sets topmost frequency number (in Hz) this band will process. The |
| * effective bandwidth for the band is then computed using this and the previous band |
| * topmost frequency (or 0 Hz for band number 0). Frequencies are expected to increase with |
| * band number, thus band 0 cutoffFrequency <= band 1 cutoffFrequency, and so on. |
| * @param frequency |
| */ |
| public void setCutoffFrequency(float frequency) { |
| mCutoffFrequency = frequency; |
| } |
| } |
| |
| /** |
| * Class for Equalizer Bands |
| * Equalizer bands have three controllable parameters: enabled/disabled, cutoffFrequency and |
| * gain |
| */ |
| public final static class EqBand extends BandBase { |
| private float mGain; |
| /** |
| * Class constructor for EqBand |
| * @param enabled true if this band is currently used to process sound. When false, |
| * the band is effectively muted and sound set to zero. |
| * @param cutoffFrequency topmost frequency number (in Hz) this band will process. The |
| * effective bandwidth for the band is then computed using this and the previous band |
| * topmost frequency (or 0 Hz for band number 0). Frequencies are expected to increase with |
| * band number, thus band 0 cutoffFrequency <= band 1 cutoffFrequency, and so on. |
| * @param gain of equalizer band in decibels (dB). A gain of 0 dB means no change in level. |
| */ |
| public EqBand(boolean enabled, float cutoffFrequency, float gain) { |
| super(enabled, cutoffFrequency); |
| mGain = gain; |
| } |
| |
| /** |
| * Class constructor for EqBand |
| * @param cfg copy constructor |
| */ |
| public EqBand(EqBand cfg) { |
| super(cfg.isEnabled(), cfg.getCutoffFrequency()); |
| mGain = cfg.mGain; |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder sb = new StringBuilder(); |
| sb.append(super.toString()); |
| sb.append(String.format(" Gain: %f\n", mGain)); |
| return sb.toString(); |
| } |
| |
| /** |
| * gets current gain of band in decibels (dB) |
| * @return current gain of band in decibels (dB) |
| */ |
| public float getGain() { |
| return mGain; |
| } |
| |
| /** |
| * sets current gain of band in decibels (dB) |
| * @param gain desired in decibels (db) |
| */ |
| public void setGain(float gain) { |
| mGain = gain; |
| } |
| } |
| |
| /** |
| * Class for Multi-Band compressor bands |
| * MBC bands have multiple controllable parameters: enabled/disabled, cutoffFrequency, |
| * attackTime, releaseTime, ratio, threshold, kneeWidth, noiseGateThreshold, expanderRatio, |
| * preGain and postGain. |
| */ |
| public final static class MbcBand extends BandBase{ |
| private float mAttackTime; |
| private float mReleaseTime; |
| private float mRatio; |
| private float mThreshold; |
| private float mKneeWidth; |
| private float mNoiseGateThreshold; |
| private float mExpanderRatio; |
| private float mPreGain; |
| private float mPostGain; |
| /** |
| * Class constructor for MbcBand |
| * @param enabled true if this band is currently used to process sound. When false, |
| * the band is effectively muted and sound set to zero. |
| * @param cutoffFrequency topmost frequency number (in Hz) this band will process. The |
| * effective bandwidth for the band is then computed using this and the previous band |
| * topmost frequency (or 0 Hz for band number 0). Frequencies are expected to increase with |
| * band number, thus band 0 cutoffFrequency <= band 1 cutoffFrequency, and so on. |
| * @param attackTime Attack Time for compressor in milliseconds (ms) |
| * @param releaseTime Release Time for compressor in milliseconds (ms) |
| * @param ratio Compressor ratio (1:N) |
| * @param threshold Compressor threshold measured in decibels (dB) from 0 dB Full Scale |
| * (dBFS). |
| * @param kneeWidth Width in decibels (dB) around compressor threshold point. |
| * @param noiseGateThreshold Noise gate threshold in decibels (dB) from 0 dB Full Scale |
| * (dBFS). |
| * @param expanderRatio Expander ratio (N:1) for signals below the Noise Gate Threshold. |
| * @param preGain Gain applied to the signal BEFORE the compression. |
| * @param postGain Gain applied to the signal AFTER compression. |
| */ |
| public MbcBand(boolean enabled, float cutoffFrequency, float attackTime, float releaseTime, |
| float ratio, float threshold, float kneeWidth, float noiseGateThreshold, |
| float expanderRatio, float preGain, float postGain) { |
| super(enabled, cutoffFrequency); |
| mAttackTime = attackTime; |
| mReleaseTime = releaseTime; |
| mRatio = ratio; |
| mThreshold = threshold; |
| mKneeWidth = kneeWidth; |
| mNoiseGateThreshold = noiseGateThreshold; |
| mExpanderRatio = expanderRatio; |
| mPreGain = preGain; |
| mPostGain = postGain; |
| } |
| |
| /** |
| * Class constructor for MbcBand |
| * @param cfg copy constructor |
| */ |
| public MbcBand(MbcBand cfg) { |
| super(cfg.isEnabled(), cfg.getCutoffFrequency()); |
| mAttackTime = cfg.mAttackTime; |
| mReleaseTime = cfg.mReleaseTime; |
| mRatio = cfg.mRatio; |
| mThreshold = cfg.mThreshold; |
| mKneeWidth = cfg.mKneeWidth; |
| mNoiseGateThreshold = cfg.mNoiseGateThreshold; |
| mExpanderRatio = cfg.mExpanderRatio; |
| mPreGain = cfg.mPreGain; |
| mPostGain = cfg.mPostGain; |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder sb = new StringBuilder(); |
| sb.append(super.toString()); |
| sb.append(String.format(" AttackTime: %f (ms)\n", mAttackTime)); |
| sb.append(String.format(" ReleaseTime: %f (ms)\n", mReleaseTime)); |
| sb.append(String.format(" Ratio: 1:%f\n", mRatio)); |
| sb.append(String.format(" Threshold: %f (dB)\n", mThreshold)); |
| sb.append(String.format(" NoiseGateThreshold: %f(dB)\n", mNoiseGateThreshold)); |
| sb.append(String.format(" ExpanderRatio: %f:1\n", mExpanderRatio)); |
| sb.append(String.format(" PreGain: %f (dB)\n", mPreGain)); |
| sb.append(String.format(" PostGain: %f (dB)\n", mPostGain)); |
| return sb.toString(); |
| } |
| |
| /** |
| * gets attack time for compressor in milliseconds (ms) |
| * @return attack time for compressor in milliseconds (ms) |
| */ |
| public float getAttackTime() { return mAttackTime; } |
| /** |
| * sets attack time for compressor in milliseconds (ms) |
| * @param attackTime desired for compressor in milliseconds (ms) |
| */ |
| public void setAttackTime(float attackTime) { mAttackTime = attackTime; } |
| /** |
| * gets release time for compressor in milliseconds (ms) |
| * @return release time for compressor in milliseconds (ms) |
| */ |
| public float getReleaseTime() { return mReleaseTime; } |
| /** |
| * sets release time for compressor in milliseconds (ms) |
| * @param releaseTime desired for compressor in milliseconds (ms) |
| */ |
| public void setReleaseTime(float releaseTime) { mReleaseTime = releaseTime; } |
| /** |
| * gets the compressor ratio (1:N) |
| * @return compressor ratio (1:N) |
| */ |
| public float getRatio() { return mRatio; } |
| /** |
| * sets compressor ratio (1:N) |
| * @param ratio desired for the compressor (1:N) |
| */ |
| public void setRatio(float ratio) { mRatio = ratio; } |
| /** |
| * gets the compressor threshold measured in decibels (dB) from 0 dB Full Scale (dBFS). |
| * Thresholds are negative. A threshold of 0 dB means no compression will take place. |
| * @return compressor threshold in decibels (dB) |
| */ |
| public float getThreshold() { return mThreshold; } |
| /** |
| * sets the compressor threshold measured in decibels (dB) from 0 dB Full Scale (dBFS). |
| * Thresholds are negative. A threshold of 0 dB means no compression will take place. |
| * @param threshold desired for compressor in decibels(dB) |
| */ |
| public void setThreshold(float threshold) { mThreshold = threshold; } |
| /** |
| * get Knee Width in decibels (dB) around compressor threshold point. Widths are always |
| * positive, with higher values representing a wider area of transition from the linear zone |
| * to the compression zone. A knee of 0 dB means a more abrupt transition. |
| * @return Knee Width in decibels (dB) |
| */ |
| public float getKneeWidth() { return mKneeWidth; } |
| /** |
| * sets knee width in decibels (dB). See |
| * {@link android.media.audiofx.DynamicsProcessing.MbcBand#getKneeWidth} for more |
| * information. |
| * @param kneeWidth desired in decibels (dB) |
| */ |
| public void setKneeWidth(float kneeWidth) { mKneeWidth = kneeWidth; } |
| /** |
| * gets the noise gate threshold in decibels (dB) from 0 dB Full Scale (dBFS). Noise gate |
| * thresholds are negative. Signals below this level will be expanded according the |
| * expanderRatio parameter. A Noise Gate Threshold of -75 dB means very quiet signals might |
| * be effectively removed from the signal. |
| * @return Noise Gate Threshold in decibels (dB) |
| */ |
| public float getNoiseGateThreshold() { return mNoiseGateThreshold; } |
| /** |
| * sets noise gate threshod in decibels (dB). See |
| * {@link android.media.audiofx.DynamicsProcessing.MbcBand#getNoiseGateThreshold} for more |
| * information. |
| * @param noiseGateThreshold desired in decibels (dB) |
| */ |
| public void setNoiseGateThreshold(float noiseGateThreshold) { |
| mNoiseGateThreshold = noiseGateThreshold; } |
| /** |
| * gets Expander ratio (N:1) for signals below the Noise Gate Threshold. |
| * @return Expander ratio (N:1) |
| */ |
| public float getExpanderRatio() { return mExpanderRatio; } |
| /** |
| * sets Expander ratio (N:1) for signals below the Noise Gate Threshold. |
| * @param expanderRatio desired expander ratio (N:1) |
| */ |
| public void setExpanderRatio(float expanderRatio) { mExpanderRatio = expanderRatio; } |
| /** |
| * gets the gain applied to the signal BEFORE the compression. Measured in decibels (dB) |
| * where 0 dB means no level change. |
| * @return preGain value in decibels (dB) |
| */ |
| public float getPreGain() { return mPreGain; } |
| /** |
| * sets the gain to be applied to the signal BEFORE the compression, measured in decibels |
| * (dB), where 0 dB means no level change. |
| * @param preGain desired in decibels (dB) |
| */ |
| public void setPreGain(float preGain) { mPreGain = preGain; } |
| /** |
| * gets the gain applied to the signal AFTER compression. Measured in decibels (dB) where 0 |
| * dB means no level change |
| * @return postGain value in decibels (dB) |
| */ |
| public float getPostGain() { return mPostGain; } |
| /** |
| * sets the gain to be applied to the siganl AFTER the compression. Measured in decibels |
| * (dB), where 0 dB means no level change. |
| * @param postGain desired value in decibels (dB) |
| */ |
| public void setPostGain(float postGain) { mPostGain = postGain; } |
| } |
| |
| /** |
| * Class for Equalizer stage |
| */ |
| public final static class Eq extends BandStage { |
| private final EqBand[] mBands; |
| /** |
| * Class constructor for Equalizer (Eq) stage |
| * @param inUse true if Eq stage will be used, false otherwise. |
| * @param enabled true if Eq stage is enabled/disabled. This can be changed while effect is |
| * running |
| * @param bandCount number of bands for this Equalizer stage. Can't be changed while effect |
| * is running |
| */ |
| public Eq(boolean inUse, boolean enabled, int bandCount) { |
| super(inUse, enabled, bandCount); |
| if (isInUse()) { |
| mBands = new EqBand[bandCount]; |
| for (int b = 0; b < bandCount; b++) { |
| float freq = DEFAULT_MAX_FREQUENCY; |
| if (bandCount > 1) { |
| freq = (float)Math.pow(10, mMinFreqLog + |
| b * (mMaxFreqLog - mMinFreqLog)/(bandCount -1)); |
| } |
| mBands[b] = new EqBand(true, freq, EQ_DEFAULT_GAIN); |
| } |
| } else { |
| mBands = null; |
| } |
| } |
| /** |
| * Class constructor for Eq stage |
| * @param cfg copy constructor |
| */ |
| public Eq(Eq cfg) { |
| super(cfg.isInUse(), cfg.isEnabled(), cfg.getBandCount()); |
| if (isInUse()) { |
| mBands = new EqBand[cfg.mBands.length]; |
| for (int b = 0; b < mBands.length; b++) { |
| mBands[b] = new EqBand(cfg.mBands[b]); |
| } |
| } else { |
| mBands = null; |
| } |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder sb = new StringBuilder(); |
| sb.append(super.toString()); |
| if (isInUse()) { |
| sb.append("--->EqBands: " + mBands.length + "\n"); |
| for (int b = 0; b < mBands.length; b++) { |
| sb.append(String.format(" Band %d\n", b)); |
| sb.append(mBands[b].toString()); |
| } |
| } |
| return sb.toString(); |
| } |
| /** |
| * Helper function to check if band index is within range |
| * @param band index to check |
| */ |
| private void checkBand(int band) { |
| if (mBands == null || band < 0 || band >= mBands.length) { |
| throw new IllegalArgumentException("band index " + band +" out of bounds"); |
| } |
| } |
| /** |
| * Sets EqBand object for given band index |
| * @param band index of band to be modified |
| * @param bandCfg EqBand object. |
| */ |
| public void setBand(int band, EqBand bandCfg) { |
| checkBand(band); |
| mBands[band] = new EqBand(bandCfg); |
| } |
| /** |
| * Gets EqBand object for band of interest. |
| * @param band index of band of interest |
| * @return EqBand Object |
| */ |
| public EqBand getBand(int band) { |
| checkBand(band); |
| return mBands[band]; |
| } |
| } |
| |
| /** |
| * Class for Multi-Band Compressor (MBC) stage |
| */ |
| public final static class Mbc extends BandStage { |
| private final MbcBand[] mBands; |
| /** |
| * Constructor for Multi-Band Compressor (MBC) stage |
| * @param inUse true if MBC stage will be used, false otherwise. |
| * @param enabled true if MBC stage is enabled/disabled. This can be changed while effect |
| * is running |
| * @param bandCount number of bands for this MBC stage. Can't be changed while effect is |
| * running |
| */ |
| public Mbc(boolean inUse, boolean enabled, int bandCount) { |
| super(inUse, enabled, bandCount); |
| if (isInUse()) { |
| mBands = new MbcBand[bandCount]; |
| for (int b = 0; b < bandCount; b++) { |
| float freq = DEFAULT_MAX_FREQUENCY; |
| if (bandCount > 1) { |
| freq = (float)Math.pow(10, mMinFreqLog + |
| b * (mMaxFreqLog - mMinFreqLog)/(bandCount -1)); |
| } |
| mBands[b] = new MbcBand(true, freq, MBC_DEFAULT_ATTACK_TIME, |
| MBC_DEFAULT_RELEASE_TIME, MBC_DEFAULT_RATIO, |
| MBC_DEFAULT_THRESHOLD, MBC_DEFAULT_KNEE_WIDTH, |
| MBC_DEFAULT_NOISE_GATE_THRESHOLD, MBC_DEFAULT_EXPANDER_RATIO, |
| MBC_DEFAULT_PRE_GAIN, MBC_DEFAULT_POST_GAIN); |
| } |
| } else { |
| mBands = null; |
| } |
| } |
| /** |
| * Class constructor for MBC stage |
| * @param cfg copy constructor |
| */ |
| public Mbc(Mbc cfg) { |
| super(cfg.isInUse(), cfg.isEnabled(), cfg.getBandCount()); |
| if (isInUse()) { |
| mBands = new MbcBand[cfg.mBands.length]; |
| for (int b = 0; b < mBands.length; b++) { |
| mBands[b] = new MbcBand(cfg.mBands[b]); |
| } |
| } else { |
| mBands = null; |
| } |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder sb = new StringBuilder(); |
| sb.append(super.toString()); |
| if (isInUse()) { |
| sb.append("--->MbcBands: " + mBands.length + "\n"); |
| for (int b = 0; b < mBands.length; b++) { |
| sb.append(String.format(" Band %d\n", b)); |
| sb.append(mBands[b].toString()); |
| } |
| } |
| return sb.toString(); |
| } |
| /** |
| * Helper function to check if band index is within range |
| * @param band index to check |
| */ |
| private void checkBand(int band) { |
| if (mBands == null || band < 0 || band >= mBands.length) { |
| throw new IllegalArgumentException("band index " + band +" out of bounds"); |
| } |
| } |
| /** |
| * Sets MbcBand object for given band index |
| * @param band index of band to be modified |
| * @param bandCfg MbcBand object. |
| */ |
| public void setBand(int band, MbcBand bandCfg) { |
| checkBand(band); |
| mBands[band] = new MbcBand(bandCfg); |
| } |
| /** |
| * Gets MbcBand object for band of interest. |
| * @param band index of band of interest |
| * @return MbcBand Object |
| */ |
| public MbcBand getBand(int band) { |
| checkBand(band); |
| return mBands[band]; |
| } |
| } |
| |
| /** |
| * Class for Limiter Stage |
| * Limiter is a single band compressor at the end of the processing chain, commonly used to |
| * protect the signal from overloading and distortion. Limiters have multiple controllable |
| * parameters: enabled/disabled, linkGroup, attackTime, releaseTime, ratio, threshold, and |
| * postGain. |
| * <p>Limiters can be linked in groups across multiple channels. Linked limiters will trigger |
| * the same limiting if any of the linked limiters starts compressing. |
| */ |
| public final static class Limiter extends Stage { |
| private int mLinkGroup; |
| private float mAttackTime; |
| private float mReleaseTime; |
| private float mRatio; |
| private float mThreshold; |
| private float mPostGain; |
| |
| /** |
| * Class constructor for Limiter Stage |
| * @param inUse true if MBC stage will be used, false otherwise. |
| * @param enabled true if MBC stage is enabled/disabled. This can be changed while effect |
| * is running |
| * @param linkGroup index of group assigned to this Limiter. Only limiters that share the |
| * same linkGroup index will react together. |
| * @param attackTime Attack Time for limiter compressor in milliseconds (ms) |
| * @param releaseTime Release Time for limiter compressor in milliseconds (ms) |
| * @param ratio Limiter Compressor ratio (1:N) |
| * @param threshold Limiter Compressor threshold measured in decibels (dB) from 0 dB Full |
| * Scale (dBFS). |
| * @param postGain Gain applied to the signal AFTER compression. |
| */ |
| public Limiter(boolean inUse, boolean enabled, int linkGroup, float attackTime, |
| float releaseTime, float ratio, float threshold, float postGain) { |
| super(inUse, enabled); |
| mLinkGroup = linkGroup; |
| mAttackTime = attackTime; |
| mReleaseTime = releaseTime; |
| mRatio = ratio; |
| mThreshold = threshold; |
| mPostGain = postGain; |
| } |
| |
| /** |
| * Class Constructor for Limiter |
| * @param cfg copy constructor |
| */ |
| public Limiter(Limiter cfg) { |
| super(cfg.isInUse(), cfg.isEnabled()); |
| mLinkGroup = cfg.mLinkGroup; |
| mAttackTime = cfg.mAttackTime; |
| mReleaseTime = cfg.mReleaseTime; |
| mRatio = cfg.mRatio; |
| mThreshold = cfg.mThreshold; |
| mPostGain = cfg.mPostGain; |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder sb = new StringBuilder(); |
| sb.append(super.toString()); |
| if (isInUse()) { |
| sb.append(String.format(" LinkGroup: %d (group)\n", mLinkGroup)); |
| sb.append(String.format(" AttackTime: %f (ms)\n", mAttackTime)); |
| sb.append(String.format(" ReleaseTime: %f (ms)\n", mReleaseTime)); |
| sb.append(String.format(" Ratio: 1:%f\n", mRatio)); |
| sb.append(String.format(" Threshold: %f (dB)\n", mThreshold)); |
| sb.append(String.format(" PostGain: %f (dB)\n", mPostGain)); |
| } |
| return sb.toString(); |
| } |
| /** |
| * Gets the linkGroup index for this Limiter Stage. Only limiters that share the same |
| * linkGroup index will react together. |
| * @return linkGroup index. |
| */ |
| public int getLinkGroup() { return mLinkGroup; } |
| /** |
| * Sets the linkGroup index for this limiter Stage. |
| * @param linkGroup desired linkGroup index |
| */ |
| public void setLinkGroup(int linkGroup) { mLinkGroup = linkGroup; } |
| /** |
| * gets attack time for limiter compressor in milliseconds (ms) |
| * @return attack time for limiter compressor in milliseconds (ms) |
| */ |
| public float getAttackTime() { return mAttackTime; } |
| /** |
| * sets attack time for limiter compressor in milliseconds (ms) |
| * @param attackTime desired for limiter compressor in milliseconds (ms) |
| */ |
| public void setAttackTime(float attackTime) { mAttackTime = attackTime; } |
| /** |
| * gets release time for limiter compressor in milliseconds (ms) |
| * @return release time for limiter compressor in milliseconds (ms) |
| */ |
| public float getReleaseTime() { return mReleaseTime; } |
| /** |
| * sets release time for limiter compressor in milliseconds (ms) |
| * @param releaseTime desired for limiter compressor in milliseconds (ms) |
| */ |
| public void setReleaseTime(float releaseTime) { mReleaseTime = releaseTime; } |
| /** |
| * gets the limiter compressor ratio (1:N) |
| * @return limiter compressor ratio (1:N) |
| */ |
| public float getRatio() { return mRatio; } |
| /** |
| * sets limiter compressor ratio (1:N) |
| * @param ratio desired for the limiter compressor (1:N) |
| */ |
| public void setRatio(float ratio) { mRatio = ratio; } |
| /** |
| * gets the limiter compressor threshold measured in decibels (dB) from 0 dB Full Scale |
| * (dBFS). Thresholds are negative. A threshold of 0 dB means no limiting will take place. |
| * @return limiter compressor threshold in decibels (dB) |
| */ |
| public float getThreshold() { return mThreshold; } |
| /** |
| * sets the limiter compressor threshold measured in decibels (dB) from 0 dB Full Scale |
| * (dBFS). Thresholds are negative. A threshold of 0 dB means no limiting will take place. |
| * @param threshold desired for limiter compressor in decibels(dB) |
| */ |
| public void setThreshold(float threshold) { mThreshold = threshold; } |
| /** |
| * gets the gain applied to the signal AFTER limiting. Measured in decibels (dB) where 0 |
| * dB means no level change |
| * @return postGain value in decibels (dB) |
| */ |
| public float getPostGain() { return mPostGain; } |
| /** |
| * sets the gain to be applied to the siganl AFTER the limiter. Measured in decibels |
| * (dB), where 0 dB means no level change. |
| * @param postGain desired value in decibels (dB) |
| */ |
| public void setPostGain(float postGain) { mPostGain = postGain; } |
| } |
| |
| /** |
| * Class for Channel configuration parameters. It is composed of multiple stages, which can be |
| * used/enabled independently. Stages not used or disabled will be bypassed and the sound would |
| * be unaffected by them. |
| */ |
| public final static class Channel { |
| private float mInputGain; |
| private Eq mPreEq; |
| private Mbc mMbc; |
| private Eq mPostEq; |
| private Limiter mLimiter; |
| |
| /** |
| * Class constructor for Channel configuration. |
| * @param inputGain value in decibels (dB) of level change applied to the audio before |
| * processing. A value of 0 dB means no change. |
| * @param preEqInUse true if PreEq stage will be used, false otherwise. This can't be |
| * changed later. |
| * @param preEqBandCount number of bands for PreEq stage. This can't be changed later. |
| * @param mbcInUse true if Mbc stage will be used, false otherwise. This can't be changed |
| * later. |
| * @param mbcBandCount number of bands for Mbc stage. This can't be changed later. |
| * @param postEqInUse true if PostEq stage will be used, false otherwise. This can't be |
| * changed later. |
| * @param postEqBandCount number of bands for PostEq stage. This can't be changed later. |
| * @param limiterInUse true if Limiter stage will be used, false otherwise. This can't be |
| * changed later. |
| */ |
| public Channel (float inputGain, |
| boolean preEqInUse, int preEqBandCount, |
| boolean mbcInUse, int mbcBandCount, |
| boolean postEqInUse, int postEqBandCount, |
| boolean limiterInUse) { |
| mInputGain = inputGain; |
| mPreEq = new Eq(preEqInUse, PREEQ_DEFAULT_ENABLED, preEqBandCount); |
| mMbc = new Mbc(mbcInUse, MBC_DEFAULT_ENABLED, mbcBandCount); |
| mPostEq = new Eq(postEqInUse, POSTEQ_DEFAULT_ENABLED, |
| postEqBandCount); |
| mLimiter = new Limiter(limiterInUse, |
| LIMITER_DEFAULT_ENABLED, LIMITER_DEFAULT_LINK_GROUP, |
| LIMITER_DEFAULT_ATTACK_TIME, LIMITER_DEFAULT_RELEASE_TIME, |
| LIMITER_DEFAULT_RATIO, LIMITER_DEFAULT_THRESHOLD, LIMITER_DEFAULT_POST_GAIN); |
| } |
| |
| /** |
| * Class constructor for Channel configuration |
| * @param cfg copy constructor |
| */ |
| public Channel(Channel cfg) { |
| mInputGain = cfg.mInputGain; |
| mPreEq = new Eq(cfg.mPreEq); |
| mMbc = new Mbc(cfg.mMbc); |
| mPostEq = new Eq(cfg.mPostEq); |
| mLimiter = new Limiter(cfg.mLimiter); |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder sb = new StringBuilder(); |
| sb.append(String.format(" InputGain: %f\n", mInputGain)); |
| sb.append("-->PreEq\n"); |
| sb.append(mPreEq.toString()); |
| sb.append("-->MBC\n"); |
| sb.append(mMbc.toString()); |
| sb.append("-->PostEq\n"); |
| sb.append(mPostEq.toString()); |
| sb.append("-->Limiter\n"); |
| sb.append(mLimiter.toString()); |
| return sb.toString(); |
| } |
| /** |
| * Gets inputGain value in decibels (dB). 0 dB means no change; |
| * @return gain value in decibels (dB) |
| */ |
| public float getInputGain() { |
| return mInputGain; |
| } |
| /** |
| * Sets inputGain value in decibels (dB). 0 dB means no change; |
| * @param inputGain desired gain value in decibels (dB) |
| */ |
| public void setInputGain(float inputGain) { |
| mInputGain = inputGain; |
| } |
| |
| /** |
| * Gets PreEq configuration stage |
| * @return PreEq configuration stage |
| */ |
| public Eq getPreEq() { |
| return mPreEq; |
| } |
| /** |
| * Sets PreEq configuration stage. New PreEq stage must have the same number of bands than |
| * original PreEq stage. |
| * @param preEq configuration |
| */ |
| public void setPreEq(Eq preEq) { |
| if (preEq.getBandCount() != mPreEq.getBandCount()) { |
| throw new IllegalArgumentException("PreEqBandCount changed from " + |
| mPreEq.getBandCount() + " to " + preEq.getBandCount()); |
| } |
| mPreEq = new Eq(preEq); |
| } |
| /** |
| * Gets EqBand for PreEq stage for given band index. |
| * @param band index of band of interest from PreEq stage |
| * @return EqBand configuration |
| */ |
| public EqBand getPreEqBand(int band) { |
| return mPreEq.getBand(band); |
| } |
| /** |
| * Sets EqBand for PreEq stage for given band index |
| * @param band index of band of interest from PreEq stage |
| * @param preEqBand configuration to be set. |
| */ |
| public void setPreEqBand(int band, EqBand preEqBand) { |
| mPreEq.setBand(band, preEqBand); |
| } |
| |
| /** |
| * Gets Mbc configuration stage |
| * @return Mbc configuration stage |
| */ |
| public Mbc getMbc() { |
| return mMbc; |
| } |
| /** |
| * Sets Mbc configuration stage. New Mbc stage must have the same number of bands than |
| * original Mbc stage. |
| * @param mbc |
| */ |
| public void setMbc(Mbc mbc) { |
| if (mbc.getBandCount() != mMbc.getBandCount()) { |
| throw new IllegalArgumentException("MbcBandCount changed from " + |
| mMbc.getBandCount() + " to " + mbc.getBandCount()); |
| } |
| mMbc = new Mbc(mbc); |
| } |
| /** |
| * Gets MbcBand configuration for Mbc stage, for given band index. |
| * @param band index of band of interest from Mbc stage |
| * @return MbcBand configuration |
| */ |
| public MbcBand getMbcBand(int band) { |
| return mMbc.getBand(band); |
| } |
| /** |
| * Sets MbcBand for Mbc stage for given band index |
| * @param band index of band of interest from Mbc Stage |
| * @param mbcBand configuration to be set |
| */ |
| public void setMbcBand(int band, MbcBand mbcBand) { |
| mMbc.setBand(band, mbcBand); |
| } |
| |
| /** |
| * Gets PostEq configuration stage |
| * @return PostEq configuration stage |
| */ |
| public Eq getPostEq() { |
| return mPostEq; |
| } |
| /** |
| * Sets PostEq configuration stage. New PostEq stage must have the same number of bands than |
| * original PostEq stage. |
| * @param postEq configuration |
| */ |
| public void setPostEq(Eq postEq) { |
| if (postEq.getBandCount() != mPostEq.getBandCount()) { |
| throw new IllegalArgumentException("PostEqBandCount changed from " + |
| mPostEq.getBandCount() + " to " + postEq.getBandCount()); |
| } |
| mPostEq = new Eq(postEq); |
| } |
| /** |
| * Gets EqBand for PostEq stage for given band index. |
| * @param band index of band of interest from PostEq stage |
| * @return EqBand configuration |
| */ |
| public EqBand getPostEqBand(int band) { |
| return mPostEq.getBand(band); |
| } |
| /** |
| * Sets EqBand for PostEq stage for given band index |
| * @param band index of band of interest from PostEq stage |
| * @param postEqBand configuration to be set. |
| */ |
| public void setPostEqBand(int band, EqBand postEqBand) { |
| mPostEq.setBand(band, postEqBand); |
| } |
| |
| /** |
| * Gets Limiter configuration stage |
| * @return Limiter configuration stage |
| */ |
| public Limiter getLimiter() { |
| return mLimiter; |
| } |
| /** |
| * Sets Limiter configuration stage. |
| * @param limiter configuration stage. |
| */ |
| public void setLimiter(Limiter limiter) { |
| mLimiter = new Limiter(limiter); |
| } |
| } |
| |
| /** |
| * Class for Config object, used by DynamicsProcessing to configure and update the audio effect. |
| * use Builder to instantiate objects of this type. |
| */ |
| public final static class Config { |
| private final int mVariant; |
| private final int mChannelCount; |
| private final boolean mPreEqInUse; |
| private final int mPreEqBandCount; |
| private final boolean mMbcInUse; |
| private final int mMbcBandCount; |
| private final boolean mPostEqInUse; |
| private final int mPostEqBandCount; |
| private final boolean mLimiterInUse; |
| private final float mPreferredFrameDuration; |
| private final Channel[] mChannel; |
| |
| /** |
| * @hide |
| * Class constructor for config. None of these parameters can be changed later. |
| * @param variant index of variant used for effect engine. See |
| * {@link #VARIANT_FAVOR_FREQUENCY_RESOLUTION} and {@link #VARIANT_FAVOR_TIME_RESOLUTION}. |
| * @param frameDurationMs preferred frame duration in milliseconds (ms). |
| * @param channelCount Number of channels to be configured. |
| * @param preEqInUse true if PreEq stage will be used, false otherwise. |
| * @param preEqBandCount number of bands for PreEq stage. |
| * @param mbcInUse true if Mbc stage will be used, false otherwise. |
| * @param mbcBandCount number of bands for Mbc stage. |
| * @param postEqInUse true if PostEq stage will be used, false otherwise. |
| * @param postEqBandCount number of bands for PostEq stage. |
| * @param limiterInUse true if Limiter stage will be used, false otherwise. |
| * @param channel array of Channel objects to be used for this configuration. |
| */ |
| public Config(int variant, float frameDurationMs, int channelCount, |
| boolean preEqInUse, int preEqBandCount, |
| boolean mbcInUse, int mbcBandCount, |
| boolean postEqInUse, int postEqBandCount, |
| boolean limiterInUse, |
| Channel[] channel) { |
| mVariant = variant; |
| mPreferredFrameDuration = frameDurationMs; |
| mChannelCount = channelCount; |
| mPreEqInUse = preEqInUse; |
| mPreEqBandCount = preEqBandCount; |
| mMbcInUse = mbcInUse; |
| mMbcBandCount = mbcBandCount; |
| mPostEqInUse = postEqInUse; |
| mPostEqBandCount = postEqBandCount; |
| mLimiterInUse = limiterInUse; |
| |
| mChannel = new Channel[mChannelCount]; |
| //check if channelconfig is null or has less channels than channel count. |
| //options: fill the missing with default options. |
| // or fail? |
| for (int ch = 0; ch < mChannelCount; ch++) { |
| if (ch < channel.length) { |
| mChannel[ch] = new Channel(channel[ch]); //copy create |
| } else { |
| //create a new one from scratch? //fail? |
| } |
| } |
| } |
| //a version that will scale to necessary number of channels |
| /** |
| * @hide |
| * Class constructor for Configuration. |
| * @param channelCount limit configuration to this number of channels. if channelCount is |
| * greater than number of channels in cfg, the constructor will duplicate the last channel |
| * found as many times as necessary to create a Config with channelCount number of channels. |
| * If channelCount is less than channels in cfg, the extra channels in cfg will be ignored. |
| * @param cfg copy constructor paremter. |
| */ |
| public Config(int channelCount, Config cfg) { |
| mVariant = cfg.mVariant; |
| mPreferredFrameDuration = cfg.mPreferredFrameDuration; |
| mChannelCount = cfg.mChannelCount; |
| mPreEqInUse = cfg.mPreEqInUse; |
| mPreEqBandCount = cfg.mPreEqBandCount; |
| mMbcInUse = cfg.mMbcInUse; |
| mMbcBandCount = cfg.mMbcBandCount; |
| mPostEqInUse = cfg.mPostEqInUse; |
| mPostEqBandCount = cfg.mPostEqBandCount; |
| mLimiterInUse = cfg.mLimiterInUse; |
| |
| if (mChannelCount != cfg.mChannel.length) { |
| throw new IllegalArgumentException("configuration channel counts differ " + |
| mChannelCount + " !=" + cfg.mChannel.length); |
| } |
| if (channelCount < 1) { |
| throw new IllegalArgumentException("channel resizing less than 1 not allowed"); |
| } |
| |
| mChannel = new Channel[channelCount]; |
| for (int ch = 0; ch < channelCount; ch++) { |
| if (ch < mChannelCount) { |
| mChannel[ch] = new Channel(cfg.mChannel[ch]); |
| } else { |
| //duplicate last |
| mChannel[ch] = new Channel(cfg.mChannel[mChannelCount-1]); |
| } |
| } |
| } |
| |
| /** |
| * @hide |
| * Class constructor for Config |
| * @param cfg Configuration object copy constructor |
| */ |
| public Config(@NonNull Config cfg) { |
| this(cfg.mChannelCount, cfg); |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder sb = new StringBuilder(); |
| sb.append(String.format("Variant: %d\n", mVariant)); |
| sb.append(String.format("PreferredFrameDuration: %f\n", mPreferredFrameDuration)); |
| sb.append(String.format("ChannelCount: %d\n", mChannelCount)); |
| sb.append(String.format("PreEq inUse: %b, bandCount:%d\n",mPreEqInUse, |
| mPreEqBandCount)); |
| sb.append(String.format("Mbc inUse: %b, bandCount: %d\n",mMbcInUse, mMbcBandCount)); |
| sb.append(String.format("PostEq inUse: %b, bandCount: %d\n", mPostEqInUse, |
| mPostEqBandCount)); |
| sb.append(String.format("Limiter inUse: %b\n", mLimiterInUse)); |
| for (int ch = 0; ch < mChannel.length; ch++) { |
| sb.append(String.format("==Channel %d\n", ch)); |
| sb.append(mChannel[ch].toString()); |
| } |
| return sb.toString(); |
| } |
| private void checkChannel(int channelIndex) { |
| if (channelIndex < 0 || channelIndex >= mChannel.length) { |
| throw new IllegalArgumentException("ChannelIndex out of bounds"); |
| } |
| } |
| |
| //getters and setters |
| /** |
| * Gets variant for effect engine See {@link #VARIANT_FAVOR_FREQUENCY_RESOLUTION} and |
| * {@link #VARIANT_FAVOR_TIME_RESOLUTION}. |
| * @return variant of effect engine |
| */ |
| public int getVariant() { |
| return mVariant; |
| } |
| /** |
| * Gets preferred frame duration in milliseconds (ms). |
| * @return preferred frame duration in milliseconds (ms) |
| */ |
| public float getPreferredFrameDuration() { |
| return mPreferredFrameDuration; |
| } |
| /** |
| * Gets if preEq stage is in use |
| * @return true if preEq stage is in use; |
| */ |
| public boolean isPreEqInUse() { |
| return mPreEqInUse; |
| } |
| /** |
| * Gets number of bands configured for the PreEq stage. |
| * @return number of bands configured for the PreEq stage. |
| */ |
| public int getPreEqBandCount() { |
| return mPreEqBandCount; |
| } |
| /** |
| * Gets if Mbc stage is in use |
| * @return true if Mbc stage is in use; |
| */ |
| public boolean isMbcInUse() { |
| return mMbcInUse; |
| } |
| /** |
| * Gets number of bands configured for the Mbc stage. |
| * @return number of bands configured for the Mbc stage. |
| */ |
| public int getMbcBandCount() { |
| return mMbcBandCount; |
| } |
| /** |
| * Gets if PostEq stage is in use |
| * @return true if PostEq stage is in use; |
| */ |
| public boolean isPostEqInUse() { |
| return mPostEqInUse; |
| } |
| /** |
| * Gets number of bands configured for the PostEq stage. |
| * @return number of bands configured for the PostEq stage. |
| */ |
| public int getPostEqBandCount() { |
| return mPostEqBandCount; |
| } |
| /** |
| * Gets if Limiter stage is in use |
| * @return true if Limiter stage is in use; |
| */ |
| public boolean isLimiterInUse() { |
| return mLimiterInUse; |
| } |
| |
| //channel |
| /** |
| * Gets the Channel configuration object by using the channel index |
| * @param channelIndex of desired Channel object |
| * @return Channel configuration object |
| */ |
| public Channel getChannelByChannelIndex(int channelIndex) { |
| checkChannel(channelIndex); |
| return mChannel[channelIndex]; |
| } |
| |
| /** |
| * Sets the chosen Channel object in the selected channelIndex |
| * Note that all the stages should have the same number of bands than the existing Channel |
| * object. |
| * @param channelIndex index of channel to be replaced |
| * @param channel Channel configuration object to be set |
| */ |
| public void setChannelTo(int channelIndex, Channel channel) { |
| checkChannel(channelIndex); |
| //check all things are compatible |
| if (mMbcBandCount != channel.getMbc().getBandCount()) { |
| throw new IllegalArgumentException("MbcBandCount changed from " + |
| mMbcBandCount + " to " + channel.getPreEq().getBandCount()); |
| } |
| if (mPreEqBandCount != channel.getPreEq().getBandCount()) { |
| throw new IllegalArgumentException("PreEqBandCount changed from " + |
| mPreEqBandCount + " to " + channel.getPreEq().getBandCount()); |
| } |
| if (mPostEqBandCount != channel.getPostEq().getBandCount()) { |
| throw new IllegalArgumentException("PostEqBandCount changed from " + |
| mPostEqBandCount + " to " + channel.getPostEq().getBandCount()); |
| } |
| mChannel[channelIndex] = new Channel(channel); |
| } |
| |
| /** |
| * Sets ALL channels to the chosen Channel object. Note that all the stages should have the |
| * same number of bands than the existing ones. |
| * @param channel Channel configuration object to be set. |
| */ |
| public void setAllChannelsTo(Channel channel) { |
| for (int ch = 0; ch < mChannel.length; ch++) { |
| setChannelTo(ch, channel); |
| } |
| } |
| |
| //===channel params |
| /** |
| * Gets inputGain value in decibels (dB) for channel indicated by channelIndex |
| * @param channelIndex index of channel of interest |
| * @return inputGain value in decibels (dB). 0 dB means no change. |
| */ |
| public float getInputGainByChannelIndex(int channelIndex) { |
| checkChannel(channelIndex); |
| return mChannel[channelIndex].getInputGain(); |
| } |
| /** |
| * Sets the inputGain value in decibels (dB) for the channel indicated by channelIndex. |
| * @param channelIndex index of channel of interest |
| * @param inputGain desired value in decibels (dB). |
| */ |
| public void setInputGainByChannelIndex(int channelIndex, float inputGain) { |
| checkChannel(channelIndex); |
| mChannel[channelIndex].setInputGain(inputGain); |
| } |
| /** |
| * Sets the inputGain value in decibels (dB) for ALL channels |
| * @param inputGain desired value in decibels (dB) |
| */ |
| public void setInputGainAllChannelsTo(float inputGain) { |
| for (int ch = 0; ch < mChannel.length; ch++) { |
| mChannel[ch].setInputGain(inputGain); |
| } |
| } |
| |
| //=== PreEQ |
| /** |
| * Gets PreEq stage from channel indicated by channelIndex |
| * @param channelIndex index of channel of interest |
| * @return PreEq stage configuration object |
| */ |
| public Eq getPreEqByChannelIndex(int channelIndex) { |
| checkChannel(channelIndex); |
| return mChannel[channelIndex].getPreEq(); |
| } |
| /** |
| * Sets the PreEq stage configuration for the channel indicated by channelIndex. Note that |
| * new preEq stage must have the same number of bands than original preEq stage |
| * @param channelIndex index of channel to be set |
| * @param preEq desired PreEq configuration to be set |
| */ |
| public void setPreEqByChannelIndex(int channelIndex, Eq preEq) { |
| checkChannel(channelIndex); |
| mChannel[channelIndex].setPreEq(preEq); |
| } |
| /** |
| * Sets the PreEq stage configuration for ALL channels. Note that new preEq stage must have |
| * the same number of bands than original preEq stages. |
| * @param preEq desired PreEq configuration to be set |
| */ |
| public void setPreEqAllChannelsTo(Eq preEq) { |
| for (int ch = 0; ch < mChannel.length; ch++) { |
| mChannel[ch].setPreEq(preEq); |
| } |
| } |
| public EqBand getPreEqBandByChannelIndex(int channelIndex, int band) { |
| checkChannel(channelIndex); |
| return mChannel[channelIndex].getPreEqBand(band); |
| } |
| public void setPreEqBandByChannelIndex(int channelIndex, int band, EqBand preEqBand) { |
| checkChannel(channelIndex); |
| mChannel[channelIndex].setPreEqBand(band, preEqBand); |
| } |
| public void setPreEqBandAllChannelsTo(int band, EqBand preEqBand) { |
| for (int ch = 0; ch < mChannel.length; ch++) { |
| mChannel[ch].setPreEqBand(band, preEqBand); |
| } |
| } |
| |
| //=== MBC |
| public Mbc getMbcByChannelIndex(int channelIndex) { |
| checkChannel(channelIndex); |
| return mChannel[channelIndex].getMbc(); |
| } |
| public void setMbcByChannelIndex(int channelIndex, Mbc mbc) { |
| checkChannel(channelIndex); |
| mChannel[channelIndex].setMbc(mbc); |
| } |
| public void setMbcAllChannelsTo(Mbc mbc) { |
| for (int ch = 0; ch < mChannel.length; ch++) { |
| mChannel[ch].setMbc(mbc); |
| } |
| } |
| public MbcBand getMbcBandByChannelIndex(int channelIndex, int band) { |
| checkChannel(channelIndex); |
| return mChannel[channelIndex].getMbcBand(band); |
| } |
| public void setMbcBandByChannelIndex(int channelIndex, int band, MbcBand mbcBand) { |
| checkChannel(channelIndex); |
| mChannel[channelIndex].setMbcBand(band, mbcBand); |
| } |
| public void setMbcBandAllChannelsTo(int band, MbcBand mbcBand) { |
| for (int ch = 0; ch < mChannel.length; ch++) { |
| mChannel[ch].setMbcBand(band, mbcBand); |
| } |
| } |
| |
| //=== PostEQ |
| public Eq getPostEqByChannelIndex(int channelIndex) { |
| checkChannel(channelIndex); |
| return mChannel[channelIndex].getPostEq(); |
| } |
| public void setPostEqByChannelIndex(int channelIndex, Eq postEq) { |
| checkChannel(channelIndex); |
| mChannel[channelIndex].setPostEq(postEq); |
| } |
| public void setPostEqAllChannelsTo(Eq postEq) { |
| for (int ch = 0; ch < mChannel.length; ch++) { |
| mChannel[ch].setPostEq(postEq); |
| } |
| } |
| public EqBand getPostEqBandByChannelIndex(int channelIndex, int band) { |
| checkChannel(channelIndex); |
| return mChannel[channelIndex].getPostEqBand(band); |
| } |
| public void setPostEqBandByChannelIndex(int channelIndex, int band, EqBand postEqBand) { |
| checkChannel(channelIndex); |
| mChannel[channelIndex].setPostEqBand(band, postEqBand); |
| } |
| public void setPostEqBandAllChannelsTo(int band, EqBand postEqBand) { |
| for (int ch = 0; ch < mChannel.length; ch++) { |
| mChannel[ch].setPostEqBand(band, postEqBand); |
| } |
| } |
| |
| //Limiter |
| public Limiter getLimiterByChannelIndex(int channelIndex) { |
| checkChannel(channelIndex); |
| return mChannel[channelIndex].getLimiter(); |
| } |
| public void setLimiterByChannelIndex(int channelIndex, Limiter limiter) { |
| checkChannel(channelIndex); |
| mChannel[channelIndex].setLimiter(limiter); |
| } |
| public void setLimiterAllChannelsTo(Limiter limiter) { |
| for (int ch = 0; ch < mChannel.length; ch++) { |
| mChannel[ch].setLimiter(limiter); |
| } |
| } |
| |
| public final static class Builder { |
| private int mVariant; |
| private int mChannelCount; |
| private boolean mPreEqInUse; |
| private int mPreEqBandCount; |
| private boolean mMbcInUse; |
| private int mMbcBandCount; |
| private boolean mPostEqInUse; |
| private int mPostEqBandCount; |
| private boolean mLimiterInUse; |
| private float mPreferredFrameDuration = CONFIG_PREFERRED_FRAME_DURATION_MS; |
| private Channel[] mChannel; |
| |
| public Builder(int variant, int channelCount, |
| boolean preEqInUse, int preEqBandCount, |
| boolean mbcInUse, int mbcBandCount, |
| boolean postEqInUse, int postEqBandCount, |
| boolean limiterInUse) { |
| mVariant = variant; |
| mChannelCount = channelCount; |
| mPreEqInUse = preEqInUse; |
| mPreEqBandCount = preEqBandCount; |
| mMbcInUse = mbcInUse; |
| mMbcBandCount = mbcBandCount; |
| mPostEqInUse = postEqInUse; |
| mPostEqBandCount = postEqBandCount; |
| mLimiterInUse = limiterInUse; |
| mChannel = new Channel[mChannelCount]; |
| for (int ch = 0; ch < mChannelCount; ch++) { |
| this.mChannel[ch] = new Channel(CHANNEL_DEFAULT_INPUT_GAIN, |
| this.mPreEqInUse, this.mPreEqBandCount, |
| this.mMbcInUse, this.mMbcBandCount, |
| this.mPostEqInUse, this.mPostEqBandCount, |
| this.mLimiterInUse); |
| } |
| } |
| |
| private void checkChannel(int channelIndex) { |
| if (channelIndex < 0 || channelIndex >= mChannel.length) { |
| throw new IllegalArgumentException("ChannelIndex out of bounds"); |
| } |
| } |
| |
| public Builder setPreferredFrameDuration(float frameDuration) { |
| if (frameDuration < 0) { |
| throw new IllegalArgumentException("Expected positive frameDuration"); |
| } |
| mPreferredFrameDuration = frameDuration; |
| return this; |
| } |
| |
| public Builder setInputGainByChannelIndex(int channelIndex, float inputGain) { |
| checkChannel(channelIndex); |
| mChannel[channelIndex].setInputGain(inputGain); |
| return this; |
| } |
| public Builder setInputGainAllChannelsTo(float inputGain) { |
| for (int ch = 0; ch < mChannel.length; ch++) { |
| mChannel[ch].setInputGain(inputGain); |
| } |
| return this; |
| } |
| |
| public Builder setChannelTo(int channelIndex, Channel channel) { |
| checkChannel(channelIndex); |
| //check all things are compatible |
| if (mMbcBandCount != channel.getMbc().getBandCount()) { |
| throw new IllegalArgumentException("MbcBandCount changed from " + |
| mMbcBandCount + " to " + channel.getPreEq().getBandCount()); |
| } |
| if (mPreEqBandCount != channel.getPreEq().getBandCount()) { |
| throw new IllegalArgumentException("PreEqBandCount changed from " + |
| mPreEqBandCount + " to " + channel.getPreEq().getBandCount()); |
| } |
| if (mPostEqBandCount != channel.getPostEq().getBandCount()) { |
| throw new IllegalArgumentException("PostEqBandCount changed from " + |
| mPostEqBandCount + " to " + channel.getPostEq().getBandCount()); |
| } |
| mChannel[channelIndex] = new Channel(channel); |
| return this; |
| } |
| public Builder setAllChannelsTo(Channel channel) { |
| for (int ch = 0; ch < mChannel.length; ch++) { |
| setChannelTo(ch, channel); |
| } |
| return this; |
| } |
| |
| public Builder setPreEqByChannelIndex(int channelIndex, Eq preEq) { |
| checkChannel(channelIndex); |
| mChannel[channelIndex].setPreEq(preEq); |
| return this; |
| } |
| public Builder setPreEqAllChannelsTo(Eq preEq) { |
| for (int ch = 0; ch < mChannel.length; ch++) { |
| setPreEqByChannelIndex(ch, preEq); |
| } |
| return this; |
| } |
| |
| public Builder setMbcByChannelIndex(int channelIndex, Mbc mbc) { |
| checkChannel(channelIndex); |
| mChannel[channelIndex].setMbc(mbc); |
| return this; |
| } |
| public Builder setMbcAllChannelsTo(Mbc mbc) { |
| for (int ch = 0; ch < mChannel.length; ch++) { |
| setMbcByChannelIndex(ch, mbc); |
| } |
| return this; |
| } |
| |
| public Builder setPostEqByChannelIndex(int channelIndex, Eq postEq) { |
| checkChannel(channelIndex); |
| mChannel[channelIndex].setPostEq(postEq); |
| return this; |
| } |
| public Builder setPostEqAllChannelsTo(Eq postEq) { |
| for (int ch = 0; ch < mChannel.length; ch++) { |
| setPostEqByChannelIndex(ch, postEq); |
| } |
| return this; |
| } |
| |
| public Builder setLimiterByChannelIndex(int channelIndex, Limiter limiter) { |
| checkChannel(channelIndex); |
| mChannel[channelIndex].setLimiter(limiter); |
| return this; |
| } |
| public Builder setLimiterAllChannelsTo(Limiter limiter) { |
| for (int ch = 0; ch < mChannel.length; ch++) { |
| setLimiterByChannelIndex(ch, limiter); |
| } |
| return this; |
| } |
| |
| public Config build() { |
| return new Config(mVariant, mPreferredFrameDuration, mChannelCount, |
| mPreEqInUse, mPreEqBandCount, |
| mMbcInUse, mMbcBandCount, |
| mPostEqInUse, mPostEqBandCount, |
| mLimiterInUse, mChannel); |
| } |
| } |
| } |
| //=== CHANNEL |
| public Channel getChannelByChannelIndex(int channelIndex) { |
| return queryEngineByChannelIndex(channelIndex); |
| } |
| |
| public void setChannelTo(int channelIndex, Channel channel) { |
| updateEngineChannelByChannelIndex(channelIndex, channel); |
| } |
| |
| public void setAllChannelsTo(Channel channel) { |
| for (int ch = 0; ch < mChannelCount; ch++) { |
| setChannelTo(ch, channel); |
| } |
| } |
| |
| //=== channel params |
| public float getInputGainByChannelIndex(int channelIndex) { |
| return getTwoFloat(PARAM_INPUT_GAIN, channelIndex); |
| } |
| public void setInputGainbyChannel(int channelIndex, float inputGain) { |
| setTwoFloat(PARAM_INPUT_GAIN, channelIndex, inputGain); |
| } |
| public void setInputGainAllChannelsTo(float inputGain) { |
| for (int ch = 0; ch < mChannelCount; ch++) { |
| setInputGainbyChannel(ch, inputGain); |
| } |
| } |
| |
| //=== PreEQ |
| public Eq getPreEqByChannelIndex(int channelIndex) { |
| return queryEngineEqByChannelIndex(PARAM_PRE_EQ, channelIndex); |
| } |
| public void setPreEqByChannelIndex(int channelIndex, Eq preEq) { |
| updateEngineEqByChannelIndex(PARAM_PRE_EQ, channelIndex, preEq); |
| } |
| public void setPreEqAllChannelsTo(Eq preEq) { |
| for (int ch = 0; ch < mChannelCount; ch++) { |
| setPreEqByChannelIndex(ch, preEq); |
| } |
| } |
| public EqBand getPreEqBandByChannelIndex(int channelIndex, int band) { |
| return queryEngineEqBandByChannelIndex(PARAM_PRE_EQ_BAND, channelIndex, band); |
| } |
| public void setPreEqBandByChannelIndex(int channelIndex, int band, EqBand preEqBand) { |
| updateEngineEqBandByChannelIndex(PARAM_PRE_EQ_BAND, channelIndex, band, preEqBand); |
| } |
| public void setPreEqBandAllChannelsTo(int band, EqBand preEqBand) { |
| for (int ch = 0; ch < mChannelCount; ch++) { |
| setPreEqBandByChannelIndex(ch, band, preEqBand); |
| } |
| } |
| |
| //=== MBC |
| public Mbc getMbcByChannelIndex(int channelIndex) { |
| return queryEngineMbcByChannelIndex(channelIndex); |
| } |
| public void setMbcByChannelIndex(int channelIndex, Mbc mbc) { |
| updateEngineMbcByChannelIndex(channelIndex, mbc); |
| } |
| public void setMbcAllChannelsTo(Mbc mbc) { |
| for (int ch = 0; ch < mChannelCount; ch++) { |
| setMbcByChannelIndex(ch, mbc); |
| } |
| } |
| public MbcBand getMbcBandByChannelIndex(int channelIndex, int band) { |
| return queryEngineMbcBandByChannelIndex(channelIndex, band); |
| } |
| public void setMbcBandByChannelIndex(int channelIndex, int band, MbcBand mbcBand) { |
| updateEngineMbcBandByChannelIndex(channelIndex, band, mbcBand); |
| } |
| public void setMbcBandAllChannelsTo(int band, MbcBand mbcBand) { |
| for (int ch = 0; ch < mChannelCount; ch++) { |
| setMbcBandByChannelIndex(ch, band, mbcBand); |
| } |
| } |
| |
| //== PostEq |
| public Eq getPostEqByChannelIndex(int channelIndex) { |
| return queryEngineEqByChannelIndex(PARAM_POST_EQ, channelIndex); |
| } |
| public void setPostEqByChannelIndex(int channelIndex, Eq postEq) { |
| updateEngineEqByChannelIndex(PARAM_POST_EQ, channelIndex, postEq); |
| } |
| public void setPostEqAllChannelsTo(Eq postEq) { |
| for (int ch = 0; ch < mChannelCount; ch++) { |
| setPostEqByChannelIndex(ch, postEq); |
| } |
| } |
| public EqBand getPostEqBandByChannelIndex(int channelIndex, int band) { |
| return queryEngineEqBandByChannelIndex(PARAM_POST_EQ_BAND, channelIndex, band); |
| } |
| public void setPostEqBandByChannelIndex(int channelIndex, int band, EqBand postEqBand) { |
| updateEngineEqBandByChannelIndex(PARAM_POST_EQ_BAND, channelIndex, band, postEqBand); |
| } |
| public void setPostEqBandAllChannelsTo(int band, EqBand postEqBand) { |
| for (int ch = 0; ch < mChannelCount; ch++) { |
| setPostEqBandByChannelIndex(ch, band, postEqBand); |
| } |
| } |
| |
| //==== Limiter |
| public Limiter getLimiterByChannelIndex(int channelIndex) { |
| return queryEngineLimiterByChannelIndex(channelIndex); |
| } |
| public void setLimiterByChannelIndex(int channelIndex, Limiter limiter) { |
| updateEngineLimiterByChannelIndex(channelIndex, limiter); |
| } |
| public void setLimiterAllChannelsTo(Limiter limiter) { |
| for (int ch = 0; ch < mChannelCount; ch++) { |
| setLimiterByChannelIndex(ch, limiter); |
| } |
| } |
| |
| /** |
| * Gets the number of channels in the effect engine |
| * @return number of channels currently in use by the effect engine |
| */ |
| public int getChannelCount() { |
| return getOneInt(PARAM_GET_CHANNEL_COUNT); |
| } |
| |
| //=== Engine calls |
| private void setEngineArchitecture(int variant, float preferredFrameDuration, |
| boolean preEqInUse, int preEqBandCount, boolean mbcInUse, int mbcBandCount, |
| boolean postEqInUse, int postEqBandCount, boolean limiterInUse) { |
| |
| Number[] params = { PARAM_ENGINE_ARCHITECTURE }; |
| Number[] values = { variant /* variant */, |
| preferredFrameDuration, |
| (preEqInUse ? 1 : 0), |
| preEqBandCount, |
| (mbcInUse ? 1 : 0), |
| mbcBandCount, |
| (postEqInUse ? 1 : 0), |
| postEqBandCount, |
| (limiterInUse ? 1 : 0)}; |
| setNumberArray(params, values); |
| } |
| |
| private void updateEngineEqBandByChannelIndex(int param, int channelIndex, int bandIndex, |
| @NonNull EqBand eqBand) { |
| Number[] params = {param, |
| channelIndex, |
| bandIndex}; |
| Number[] values = {(eqBand.isEnabled() ? 1 : 0), |
| eqBand.getCutoffFrequency(), |
| eqBand.getGain()}; |
| setNumberArray(params, values); |
| } |
| private Eq queryEngineEqByChannelIndex(int param, int channelIndex) { |
| |
| Number[] params = {param == PARAM_PRE_EQ ? PARAM_PRE_EQ : PARAM_POST_EQ, |
| channelIndex}; |
| Number[] values = {0 /*0 in use */, |
| 0 /*1 enabled*/, |
| 0 /*2 band count */}; |
| byte[] paramBytes = numberArrayToByteArray(params); |
| byte[] valueBytes = numberArrayToByteArray(values); //just interest in the byte size. |
| getParameter(paramBytes, valueBytes); |
| byteArrayToNumberArray(valueBytes, values); |
| int bandCount = values[2].intValue(); |
| Eq eq = new Eq(values[0].intValue() > 0 /* in use */, |
| values[1].intValue() > 0 /* enabled */, |
| bandCount /*band count*/); |
| for (int b = 0; b < bandCount; b++) { |
| EqBand eqBand = queryEngineEqBandByChannelIndex(param == PARAM_PRE_EQ ? |
| PARAM_PRE_EQ_BAND : PARAM_POST_EQ_BAND, channelIndex, b); |
| eq.setBand(b, eqBand); |
| } |
| return eq; |
| } |
| private EqBand queryEngineEqBandByChannelIndex(int param, int channelIndex, int bandIndex) { |
| Number[] params = {param, |
| channelIndex, |
| bandIndex}; |
| Number[] values = {0 /*0 enabled*/, |
| 0.0f /*1 cutoffFrequency */, |
| 0.0f /*2 gain */}; |
| |
| byte[] paramBytes = numberArrayToByteArray(params); |
| byte[] valueBytes = numberArrayToByteArray(values); //just interest in the byte size. |
| getParameter(paramBytes, valueBytes); |
| |
| byteArrayToNumberArray(valueBytes, values); |
| |
| return new EqBand(values[0].intValue() > 0 /* enabled */, |
| values[1].floatValue() /* cutoffFrequency */, |
| values[2].floatValue() /* gain*/); |
| } |
| private void updateEngineEqByChannelIndex(int param, int channelIndex, @NonNull Eq eq) { |
| int bandCount = eq.getBandCount(); |
| Number[] params = {param, |
| channelIndex}; |
| Number[] values = { (eq.isInUse() ? 1 : 0), |
| (eq.isEnabled() ? 1 : 0), |
| bandCount}; |
| setNumberArray(params, values); |
| for (int b = 0; b < bandCount; b++) { |
| EqBand eqBand = eq.getBand(b); |
| updateEngineEqBandByChannelIndex(param == PARAM_PRE_EQ ? |
| PARAM_PRE_EQ_BAND : PARAM_POST_EQ_BAND, channelIndex, b, eqBand); |
| } |
| } |
| |
| private Mbc queryEngineMbcByChannelIndex(int channelIndex) { |
| Number[] params = {PARAM_MBC, |
| channelIndex}; |
| Number[] values = {0 /*0 in use */, |
| 0 /*1 enabled*/, |
| 0 /*2 band count */}; |
| byte[] paramBytes = numberArrayToByteArray(params); |
| byte[] valueBytes = numberArrayToByteArray(values); //just interest in the byte size. |
| getParameter(paramBytes, valueBytes); |
| byteArrayToNumberArray(valueBytes, values); |
| int bandCount = values[2].intValue(); |
| Mbc mbc = new Mbc(values[0].intValue() > 0 /* in use */, |
| values[1].intValue() > 0 /* enabled */, |
| bandCount /*band count*/); |
| for (int b = 0; b < bandCount; b++) { |
| MbcBand mbcBand = queryEngineMbcBandByChannelIndex(channelIndex, b); |
| mbc.setBand(b, mbcBand); |
| } |
| return mbc; |
| } |
| private MbcBand queryEngineMbcBandByChannelIndex(int channelIndex, int bandIndex) { |
| Number[] params = {PARAM_MBC_BAND, |
| channelIndex, |
| bandIndex}; |
| Number[] values = {0 /*0 enabled */, |
| 0.0f /*1 cutoffFrequency */, |
| 0.0f /*2 AttackTime */, |
| 0.0f /*3 ReleaseTime */, |
| 0.0f /*4 Ratio */, |
| 0.0f /*5 Threshold */, |
| 0.0f /*6 KneeWidth */, |
| 0.0f /*7 NoiseGateThreshold */, |
| 0.0f /*8 ExpanderRatio */, |
| 0.0f /*9 PreGain */, |
| 0.0f /*10 PostGain*/}; |
| |
| byte[] paramBytes = numberArrayToByteArray(params); |
| byte[] valueBytes = numberArrayToByteArray(values); //just interest in the byte size. |
| getParameter(paramBytes, valueBytes); |
| |
| byteArrayToNumberArray(valueBytes, values); |
| |
| return new MbcBand(values[0].intValue() > 0 /* enabled */, |
| values[1].floatValue() /* cutoffFrequency */, |
| values[2].floatValue()/*2 AttackTime */, |
| values[3].floatValue()/*3 ReleaseTime */, |
| values[4].floatValue()/*4 Ratio */, |
| values[5].floatValue()/*5 Threshold */, |
| values[6].floatValue()/*6 KneeWidth */, |
| values[7].floatValue()/*7 NoiseGateThreshold */, |
| values[8].floatValue()/*8 ExpanderRatio */, |
| values[9].floatValue()/*9 PreGain */, |
| values[10].floatValue()/*10 PostGain*/); |
| } |
| private void updateEngineMbcBandByChannelIndex(int channelIndex, int bandIndex, |
| @NonNull MbcBand mbcBand) { |
| Number[] params = { PARAM_MBC_BAND, |
| channelIndex, |
| bandIndex}; |
| Number[] values = {(mbcBand.isEnabled() ? 1 : 0), |
| mbcBand.getCutoffFrequency(), |
| mbcBand.getAttackTime(), |
| mbcBand.getReleaseTime(), |
| mbcBand.getRatio(), |
| mbcBand.getThreshold(), |
| mbcBand.getKneeWidth(), |
| mbcBand.getNoiseGateThreshold(), |
| mbcBand.getExpanderRatio(), |
| mbcBand.getPreGain(), |
| mbcBand.getPostGain()}; |
| setNumberArray(params, values); |
| } |
| |
| private void updateEngineMbcByChannelIndex(int channelIndex, @NonNull Mbc mbc) { |
| int bandCount = mbc.getBandCount(); |
| Number[] params = { PARAM_MBC, |
| channelIndex}; |
| Number[] values = {(mbc.isInUse() ? 1 : 0), |
| (mbc.isEnabled() ? 1 : 0), |
| bandCount}; |
| setNumberArray(params, values); |
| for (int b = 0; b < bandCount; b++) { |
| MbcBand mbcBand = mbc.getBand(b); |
| updateEngineMbcBandByChannelIndex(channelIndex, b, mbcBand); |
| } |
| } |
| |
| private void updateEngineLimiterByChannelIndex(int channelIndex, @NonNull Limiter limiter) { |
| Number[] params = { PARAM_LIMITER, |
| channelIndex}; |
| Number[] values = {(limiter.isInUse() ? 1 : 0), |
| (limiter.isEnabled() ? 1 : 0), |
| limiter.getLinkGroup(), |
| limiter.getAttackTime(), |
| limiter.getReleaseTime(), |
| limiter.getRatio(), |
| limiter.getThreshold(), |
| limiter.getPostGain()}; |
| setNumberArray(params, values); |
| } |
| |
| private Limiter queryEngineLimiterByChannelIndex(int channelIndex) { |
| Number[] params = {PARAM_LIMITER, |
| channelIndex}; |
| Number[] values = {0 /*0 in use (int)*/, |
| 0 /*1 enabled (int)*/, |
| 0 /*2 link group (int)*/, |
| 0.0f /*3 attack time (float)*/, |
| 0.0f /*4 release time (float)*/, |
| 0.0f /*5 ratio (float)*/, |
| 0.0f /*6 threshold (float)*/, |
| 0.0f /*7 post gain(float)*/}; |
| |
| byte[] paramBytes = numberArrayToByteArray(params); |
| byte[] valueBytes = numberArrayToByteArray(values); //just interest in the byte size. |
| getParameter(paramBytes, valueBytes); |
| byteArrayToNumberArray(valueBytes, values); |
| |
| return new Limiter(values[0].intValue() > 0 /*in use*/, |
| values[1].intValue() > 0 /*enabled*/, |
| values[2].intValue() /*linkGroup*/, |
| values[3].floatValue() /*attackTime*/, |
| values[4].floatValue() /*releaseTime*/, |
| values[5].floatValue() /*ratio*/, |
| values[6].floatValue() /*threshold*/, |
| values[7].floatValue() /*postGain*/); |
| } |
| |
| private Channel queryEngineByChannelIndex(int channelIndex) { |
| float inputGain = getTwoFloat(PARAM_INPUT_GAIN, channelIndex); |
| Eq preEq = queryEngineEqByChannelIndex(PARAM_PRE_EQ, channelIndex); |
| Mbc mbc = queryEngineMbcByChannelIndex(channelIndex); |
| Eq postEq = queryEngineEqByChannelIndex(PARAM_POST_EQ, channelIndex); |
| Limiter limiter = queryEngineLimiterByChannelIndex(channelIndex); |
| |
| Channel channel = new Channel(inputGain, |
| preEq.isInUse(), preEq.getBandCount(), |
| mbc.isInUse(), mbc.getBandCount(), |
| postEq.isInUse(), postEq.getBandCount(), |
| limiter.isInUse()); |
| channel.setInputGain(inputGain); |
| channel.setPreEq(preEq); |
| channel.setMbc(mbc); |
| channel.setPostEq(postEq); |
| channel.setLimiter(limiter); |
| return channel; |
| } |
| |
| private void updateEngineChannelByChannelIndex(int channelIndex, @NonNull Channel channel) { |
| //send things with as few calls as possible |
| setTwoFloat(PARAM_INPUT_GAIN, channelIndex, channel.getInputGain()); |
| Eq preEq = channel.getPreEq(); |
| updateEngineEqByChannelIndex(PARAM_PRE_EQ, channelIndex, preEq); |
| Mbc mbc = channel.getMbc(); |
| updateEngineMbcByChannelIndex(channelIndex, mbc); |
| Eq postEq = channel.getPostEq(); |
| updateEngineEqByChannelIndex(PARAM_POST_EQ, channelIndex, postEq); |
| Limiter limiter = channel.getLimiter(); |
| updateEngineLimiterByChannelIndex(channelIndex, limiter); |
| } |
| |
| //****** convenience methods: |
| // |
| private int getOneInt(int param) { |
| final int[] params = { param }; |
| final int[] result = new int[1]; |
| |
| checkStatus(getParameter(params, result)); |
| return result[0]; |
| } |
| |
| private void setTwoFloat(int param, int paramA, float valueSet) { |
| final int[] params = { param, paramA }; |
| final byte[] value; |
| |
| value = floatToByteArray(valueSet); |
| checkStatus(setParameter(params, value)); |
| } |
| |
| private byte[] numberArrayToByteArray(Number[] values) { |
| int expectedBytes = 0; |
| for (int i = 0; i < values.length; i++) { |
| if (values[i] instanceof Integer) { |
| expectedBytes += Integer.BYTES; |
| } else if (values[i] instanceof Float) { |
| expectedBytes += Float.BYTES; |
| } else { |
| throw new IllegalArgumentException("unknown value type " + |
| values[i].getClass()); |
| } |
| } |
| ByteBuffer converter = ByteBuffer.allocate(expectedBytes); |
| converter.order(ByteOrder.nativeOrder()); |
| for (int i = 0; i < values.length; i++) { |
| if (values[i] instanceof Integer) { |
| converter.putInt(values[i].intValue()); |
| } else if (values[i] instanceof Float) { |
| converter.putFloat(values[i].floatValue()); |
| } |
| } |
| return converter.array(); |
| } |
| |
| private void byteArrayToNumberArray(byte[] valuesIn, Number[] valuesOut) { |
| int inIndex = 0; |
| int outIndex = 0; |
| while (inIndex < valuesIn.length && outIndex < valuesOut.length) { |
| if (valuesOut[outIndex] instanceof Integer) { |
| valuesOut[outIndex++] = byteArrayToInt(valuesIn, inIndex); |
| inIndex += Integer.BYTES; |
| } else if (valuesOut[outIndex] instanceof Float) { |
| valuesOut[outIndex++] = byteArrayToFloat(valuesIn, inIndex); |
| inIndex += Float.BYTES; |
| } else { |
| throw new IllegalArgumentException("can't convert " + |
| valuesOut[outIndex].getClass()); |
| } |
| } |
| if (outIndex != valuesOut.length) { |
| throw new IllegalArgumentException("only converted " + outIndex + |
| " values out of "+ valuesOut.length + " expected"); |
| } |
| } |
| |
| private void setNumberArray(Number[] params, Number[] values) { |
| byte[] paramBytes = numberArrayToByteArray(params); |
| byte[] valueBytes = numberArrayToByteArray(values); |
| checkStatus(setParameter(paramBytes, valueBytes)); |
| } |
| |
| private float getTwoFloat(int param, int paramA) { |
| final int[] params = { param, paramA }; |
| final byte[] result = new byte[4]; |
| |
| checkStatus(getParameter(params, result)); |
| return byteArrayToFloat(result); |
| } |
| |
| /** |
| * @hide |
| * The OnParameterChangeListener interface defines a method called by the DynamicsProcessing |
| * when a parameter value has changed. |
| */ |
| public interface OnParameterChangeListener { |
| /** |
| * Method called when a parameter value has changed. The method is called only if the |
| * parameter was changed by another application having the control of the same |
| * DynamicsProcessing engine. |
| * @param effect the DynamicsProcessing on which the interface is registered. |
| * @param param ID of the modified parameter. See {@link #PARAM_GENERIC_PARAM1} ... |
| * @param value the new parameter value. |
| */ |
| void onParameterChange(DynamicsProcessing effect, int param, int value); |
| } |
| |
| /** |
| * helper method to update effect architecture parameters |
| */ |
| private void updateEffectArchitecture() { |
| mChannelCount = getChannelCount(); |
| } |
| |
| /** |
| * Listener used internally to receive unformatted parameter change events from AudioEffect |
| * super class. |
| */ |
| private class BaseParameterListener implements AudioEffect.OnParameterChangeListener { |
| private BaseParameterListener() { |
| |
| } |
| public void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value) { |
| // only notify when the parameter was successfully change |
| if (status != AudioEffect.SUCCESS) { |
| return; |
| } |
| OnParameterChangeListener l = null; |
| synchronized (mParamListenerLock) { |
| if (mParamListener != null) { |
| l = mParamListener; |
| } |
| } |
| if (l != null) { |
| int p = -1; |
| int v = Integer.MIN_VALUE; |
| |
| if (param.length == 4) { |
| p = byteArrayToInt(param, 0); |
| } |
| if (value.length == 4) { |
| v = byteArrayToInt(value, 0); |
| } |
| if (p != -1 && v != Integer.MIN_VALUE) { |
| l.onParameterChange(DynamicsProcessing.this, p, v); |
| } |
| } |
| } |
| } |
| |
| /** |
| * @hide |
| * Registers an OnParameterChangeListener interface. |
| * @param listener OnParameterChangeListener interface registered |
| */ |
| public void setParameterListener(OnParameterChangeListener listener) { |
| synchronized (mParamListenerLock) { |
| if (mParamListener == null) { |
| mBaseParamListener = new BaseParameterListener(); |
| super.setParameterListener(mBaseParamListener); |
| } |
| mParamListener = listener; |
| } |
| } |
| |
| /** |
| * @hide |
| * The Settings class regroups the DynamicsProcessing parameters. It is used in |
| * conjunction with the getProperties() and setProperties() methods to backup and restore |
| * all parameters in a single call. |
| */ |
| |
| public static class Settings { |
| public int channelCount; |
| public float[] inputGain; |
| |
| public Settings() { |
| } |
| |
| /** |
| * Settings class constructor from a key=value; pairs formatted string. The string is |
| * typically returned by Settings.toString() method. |
| * @throws IllegalArgumentException if the string is not correctly formatted. |
| */ |
| public Settings(String settings) { |
| StringTokenizer st = new StringTokenizer(settings, "=;"); |
| //int tokens = st.countTokens(); |
| if (st.countTokens() != 3) { |
| throw new IllegalArgumentException("settings: " + settings); |
| } |
| String key = st.nextToken(); |
| if (!key.equals("DynamicsProcessing")) { |
| throw new IllegalArgumentException( |
| "invalid settings for DynamicsProcessing: " + key); |
| } |
| try { |
| key = st.nextToken(); |
| if (!key.equals("channelCount")) { |
| throw new IllegalArgumentException("invalid key name: " + key); |
| } |
| channelCount = Short.parseShort(st.nextToken()); |
| if (channelCount > CHANNEL_COUNT_MAX) { |
| throw new IllegalArgumentException("too many channels Settings:" + settings); |
| } |
| if (st.countTokens() != channelCount*1) { //check expected parameters. |
| throw new IllegalArgumentException("settings: " + settings); |
| } |
| //check to see it is ok the size |
| inputGain = new float[channelCount]; |
| for (int ch = 0; ch < channelCount; ch++) { |
| key = st.nextToken(); |
| if (!key.equals(ch +"_inputGain")) { |
| throw new IllegalArgumentException("invalid key name: " + key); |
| } |
| inputGain[ch] = Float.parseFloat(st.nextToken()); |
| } |
| } catch (NumberFormatException nfe) { |
| throw new IllegalArgumentException("invalid value for key: " + key); |
| } |
| } |
| |
| @Override |
| public String toString() { |
| String str = new String ( |
| "DynamicsProcessing"+ |
| ";channelCount="+Integer.toString(channelCount)); |
| for (int ch = 0; ch < channelCount; ch++) { |
| str = str.concat(";"+ch+"_inputGain="+Float.toString(inputGain[ch])); |
| } |
| return str; |
| } |
| }; |
| |
| |
| /** |
| * @hide |
| * Gets the DynamicsProcessing properties. This method is useful when a snapshot of current |
| * effect settings must be saved by the application. |
| * @return a DynamicsProcessing.Settings object containing all current parameters values |
| */ |
| public DynamicsProcessing.Settings getProperties() { |
| Settings settings = new Settings(); |
| |
| //TODO: just for testing, we are calling the getters one by one, this is |
| // supposed to be done in a single (or few calls) and get all the parameters at once. |
| |
| settings.channelCount = getChannelCount(); |
| |
| if (settings.channelCount > CHANNEL_COUNT_MAX) { |
| throw new IllegalArgumentException("too many channels Settings:" + settings); |
| } |
| |
| { // get inputGainmB per channel |
| settings.inputGain = new float [settings.channelCount]; |
| for (int ch = 0; ch < settings.channelCount; ch++) { |
| //TODO:with config settings.inputGain[ch] = getInputGain(ch); |
| } |
| } |
| return settings; |
| } |
| |
| /** |
| * @hide |
| * Sets the DynamicsProcessing properties. This method is useful when bass boost settings |
| * have to be applied from a previous backup. |
| * @param settings a DynamicsProcessing.Settings object containing the properties to apply |
| */ |
| public void setProperties(DynamicsProcessing.Settings settings) { |
| |
| if (settings.channelCount != settings.inputGain.length || |
| settings.channelCount != mChannelCount) { |
| throw new IllegalArgumentException("settings invalid channel count: " |
| + settings.channelCount); |
| } |
| |
| //TODO: for now calling multiple times. |
| for (int ch = 0; ch < mChannelCount; ch++) { |
| //TODO: use config setInputGain(ch, settings.inputGain[ch]); |
| } |
| } |
| } |