2403 lines
99 KiB
Java
2403 lines
99 KiB
Java
/*
|
|
* 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 = 3; // ms
|
|
private static final float MBC_DEFAULT_RELEASE_TIME = 80; // ms
|
|
private static final float MBC_DEFAULT_RATIO = 1; // N:1
|
|
private static final float MBC_DEFAULT_THRESHOLD = -45; // dB
|
|
private static final float MBC_DEFAULT_KNEE_WIDTH = 0; // dB
|
|
private static final float MBC_DEFAULT_NOISE_GATE_THRESHOLD = -90; // dB
|
|
private static final float MBC_DEFAULT_EXPANDER_RATIO = 1; // 1:N
|
|
private static final float MBC_DEFAULT_PRE_GAIN = 0; // dB
|
|
private static final float MBC_DEFAULT_POST_GAIN = 0; // 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 = 1; // ms
|
|
private static final float LIMITER_DEFAULT_RELEASE_TIME = 60; // ms
|
|
private static final float LIMITER_DEFAULT_RATIO = 10; // N:1
|
|
private static final float LIMITER_DEFAULT_THRESHOLD = -2; // dB
|
|
private static final float LIMITER_DEFAULT_POST_GAIN = 0; // 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 (N:1) (input:output)
|
|
* @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 (1:N) (input:output) 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 (N:1)
|
|
* @return compressor ratio (N:1)
|
|
*/
|
|
public float getRatio() { return mRatio; }
|
|
/**
|
|
* sets compressor ratio (N:1)
|
|
* @param ratio desired for the compressor (N:1)
|
|
*/
|
|
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 (1:N) for signals below the Noise Gate Threshold.
|
|
* @return Expander ratio (1:N)
|
|
*/
|
|
public float getExpanderRatio() { return mExpanderRatio; }
|
|
/**
|
|
* sets Expander ratio (1:N) for signals below the Noise Gate Threshold.
|
|
* @param expanderRatio desired expander ratio (1:N)
|
|
*/
|
|
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 (N:1) (input:output)
|
|
* @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 (N:1)
|
|
* @return limiter compressor ratio (N:1)
|
|
*/
|
|
public float getRatio() { return mRatio; }
|
|
/**
|
|
* sets limiter compressor ratio (N:1)
|
|
* @param ratio desired for the limiter compressor (N:1)
|
|
*/
|
|
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]);
|
|
}
|
|
}
|
|
}
|