diff options
| -rw-r--r-- | media/java/android/media/audiofx/DynamicsProcessing.java | 699 |
1 files changed, 422 insertions, 277 deletions
diff --git a/media/java/android/media/audiofx/DynamicsProcessing.java b/media/java/android/media/audiofx/DynamicsProcessing.java index d09c9a895e0c..4c17ae1d93b3 100644 --- a/media/java/android/media/audiofx/DynamicsProcessing.java +++ b/media/java/android/media/audiofx/DynamicsProcessing.java @@ -16,10 +16,11 @@ 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.media.audiofx.DynamicsProcessing.Settings; import android.util.Log; import java.nio.ByteBuffer; @@ -90,66 +91,18 @@ public final class DynamicsProcessing extends AudioEffect { private final static String TAG = "DynamicsProcessing"; - /** - * Config object used to initialize and change effect parameters at runtime. - */ - private Config mConfig = null; - - // 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 = 0x0; - private static final int PARAM_EQ_BAND_COUNT = 0x1; - private static final int PARAM_MBC_BAND_COUNT = 0x2; - private static final int PARAM_INPUT_GAIN = 0x3; - private static final int PARAM_PRE_EQ_ENABLED = 0x10; - private static final int PARAM_PRE_EQ_BAND_ENABLED = 0x11; - private static final int PARAM_PRE_EQ_BAND_FREQUENCY = 0x12; - private static final int PARAM_PRE_EQ_BAND_GAIN = 0x13; - private static final int PARAM_EQ_FREQUENCY_RANGE = 0x22; - private static final int PARAM_EQ_GAIN_RANGE = 0x23; - private static final int PARAM_MBC_ENABLED = 0x30; - private static final int PARAM_MBC_BAND_ENABLED = 0x31; - private static final int PARAM_MBC_BAND_FREQUENCY = 0x32; - private static final int PARAM_MBC_BAND_ATTACK_TIME = 0x33; - private static final int PARAM_MBC_BAND_RELEASE_TIME = 0x34; - private static final int PARAM_MBC_BAND_RATIO = 0x35; - private static final int PARAM_MBC_BAND_THRESHOLD = 0x36; - private static final int PARAM_MBC_BAND_KNEE_WIDTH = 0x37; - private static final int PARAM_MBC_BAND_NOISE_GATE_THRESHOLD = 0x38; - private static final int PARAM_MBC_BAND_EXPANDER_RATIO = 0x39; - private static final int PARAM_MBC_BAND_GAIN_PRE = 0x3A; - private static final int PARAM_MBC_BAND_GAIN_POST = 0x3B; - private static final int PARAM_MBC_FREQUENCY_RANGE = 0x42; - private static final int PARAM_MBC_ATTACK_TIME_RANGE = 0x43; - private static final int PARAM_MBC_RELEASE_TIME_RANGE = 0x44; - private static final int PARAM_MBC_RATIO_RANGE = 0x45; - private static final int PARAM_MBC_THRESHOLD_RANGE = 0x46; - private static final int PARAM_MBC_KNEE_WIDTH_RANGE = 0x47; - private static final int PARAM_MBC_NOISE_GATE_THRESHOLD_RANGE = 0x48; - private static final int PARAM_MBC_EXPANDER_RATIO_RANGE = 0x49; - private static final int PARAM_MBC_GAIN_RANGE = 0x4A; - private static final int PARAM_POST_EQ_ENABLED = 0x50; - private static final int PARAM_POST_EQ_BAND_ENABLED = 0x51; - private static final int PARAM_POST_EQ_BAND_FREQUENCY = 0x52; - private static final int PARAM_POST_EQ_BAND_GAIN = 0x53; - private static final int PARAM_LIMITER_ENABLED = 0x60; - private static final int PARAM_LIMITER_LINK_GROUP = 0x61; - private static final int PARAM_LIMITER_ATTACK_TIME = 0x62; - private static final int PARAM_LIMITER_RELEASE_TIME = 0x63; - private static final int PARAM_LIMITER_RATIO = 0x64; - private static final int PARAM_LIMITER_THRESHOLD = 0x65; - private static final int PARAM_LIMITER_GAIN_POST = 0x66; - private static final int PARAM_LIMITER_ATTACK_TIME_RANGE = 0x72; - private static final int PARAM_LIMITER_RELEASE_TIME_RANGE = 0x73; - private static final int PARAM_LIMITER_RATIO_RANGE = 0x74; - private static final int PARAM_LIMITER_THRESHOLD_RANGE = 0x75; - private static final int PARAM_LIMITER_GAIN_RANGE = 0x76; - private static final int PARAM_VARIANT = 0x100; - private static final int PARAM_VARIANT_DESCRIPTION = 0x101; - private static final int PARAM_VARIANT_COUNT = 0x102; - private static final int PARAM_SET_ENGINE_ARCHITECTURE = 0x200; + 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. @@ -226,12 +179,13 @@ public final class DynamicsProcessing extends AudioEffect { * 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, Config cfg) { + 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 @@ -239,21 +193,33 @@ public final class DynamicsProcessing extends AudioEffect { new DynamicsProcessing.Config.Builder( CONFIG_DEFAULT_VARIANT, mChannelCount, - true /*use preEQ*/, 6 /*pre eq bands*/, - true /*use mbc*/, 6 /*mbc bands*/, - true /*use postEQ*/, 6 /*postEq bands*/, - true /*use Limiter*/); - mConfig = builder.build(); + 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, or fail, or - mConfig = new DynamicsProcessing.Config(mChannelCount, cfg); + //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)); } - - setEngineArchitecture(mConfig.getVariant(), - mConfig.isPreEqInUse(), mConfig.getPreEqBandCount(), - mConfig.isMbcInUse(), mConfig.getMbcBandCount(), - mConfig.isPostEqInUse(), mConfig.getPostEqBandCount(), - mConfig.isLimiterInUse()); } /** @@ -261,11 +227,51 @@ public final class DynamicsProcessing extends AudioEffect { * @return Config Current Config object used to setup this DynamicsProcessing effect. */ public Config getConfig() { - return mConfig; + //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 = 0; //favor frequency + 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 @@ -1276,7 +1282,7 @@ public final class DynamicsProcessing extends AudioEffect { 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? + // or fail? for (int ch = 0; ch < mChannelCount; ch++) { if (ch < channel.length) { mChannel[ch] = new Channel(channel[ch]); //copy create @@ -1331,7 +1337,7 @@ public final class DynamicsProcessing extends AudioEffect { * Class constructor for Config * @param cfg Configuration object copy constructor */ - public Config(Config cfg) { + public Config(@NonNull Config cfg) { this(cfg.mChannelCount, cfg); } @@ -1763,138 +1769,115 @@ public final class DynamicsProcessing extends AudioEffect { } //=== CHANNEL public Channel getChannelByChannelIndex(int channelIndex) { - return mConfig.getChannelByChannelIndex(channelIndex); + return queryEngineByChannelIndex(channelIndex); } public void setChannelTo(int channelIndex, Channel channel) { - mConfig.setChannelTo(channelIndex, channel); + updateEngineChannelByChannelIndex(channelIndex, channel); } public void setAllChannelsTo(Channel channel) { - mConfig.setAllChannelsTo(channel); + for (int ch = 0; ch < mChannelCount; ch++) { + setChannelTo(ch, channel); + } } //=== channel params public float getInputGainByChannelIndex(int channelIndex) { - //TODO: return info from engine instead of cached config - return mConfig.getInputGainByChannelIndex(channelIndex); + return getTwoFloat(PARAM_INPUT_GAIN, channelIndex); } public void setInputGainbyChannel(int channelIndex, float inputGain) { - mConfig.setInputGainByChannelIndex(channelIndex, inputGain); - //TODO: communicate change to engine + setTwoFloat(PARAM_INPUT_GAIN, channelIndex, inputGain); } public void setInputGainAllChannelsTo(float inputGain) { - mConfig.setInputGainAllChannelsTo(inputGain); - //TODO: communicate change to engine + for (int ch = 0; ch < mChannelCount; ch++) { + setInputGainbyChannel(ch, inputGain); + } } //=== PreEQ public Eq getPreEqByChannelIndex(int channelIndex) { - //TODO: return info from engine instead of cached config - return mConfig.getPreEqByChannelIndex(channelIndex); + return queryEngineEqByChannelIndex(PARAM_PRE_EQ, channelIndex); } - public void setPreEqByChannelIndex(int channelIndex, Eq preEq) { - mConfig.setPreEqByChannelIndex(channelIndex, preEq); - //TODO: communicate change to engine + updateEngineEqByChannelIndex(PARAM_PRE_EQ, channelIndex, preEq); } - public void setPreEqAllChannelsTo(Eq preEq) { - mConfig.setPreEqAllChannelsTo(preEq); - //TODO: communicate change to engine + for (int ch = 0; ch < mChannelCount; ch++) { + setPreEqByChannelIndex(ch, preEq); + } } - public EqBand getPreEqBandByChannelIndex(int channelIndex, int band) { - //TODO: return info from engine instead of cached config - return mConfig.getPreEqBandByChannelIndex(channelIndex, band); + return queryEngineEqBandByChannelIndex(PARAM_PRE_EQ_BAND, channelIndex, band); } - public void setPreEqBandByChannelIndex(int channelIndex, int band, EqBand preEqBand) { - mConfig.setPreEqBandByChannelIndex(channelIndex, band, preEqBand); - //TODO: communicate change to engine + updateEngineEqBandByChannelIndex(PARAM_PRE_EQ_BAND, channelIndex, band, preEqBand); } - public void setPreEqBandAllChannelsTo(int band, EqBand preEqBand) { - mConfig.setPreEqBandAllChannelsTo(band, preEqBand); - //TODO: communicate change to engine + for (int ch = 0; ch < mChannelCount; ch++) { + setPreEqBandByChannelIndex(ch, band, preEqBand); + } } //=== MBC public Mbc getMbcByChannelIndex(int channelIndex) { - //TODO: return info from engine instead of cached config - return mConfig.getMbcByChannelIndex(channelIndex); + return queryEngineMbcByChannelIndex(channelIndex); } - public void setMbcByChannelIndex(int channelIndex, Mbc mbc) { - mConfig.setMbcByChannelIndex(channelIndex, mbc); - //TODO: communicate change to engine + updateEngineMbcByChannelIndex(channelIndex, mbc); } - public void setMbcAllChannelsTo(Mbc mbc) { - mConfig.setMbcAllChannelsTo(mbc); - //TODO: communicate change to engine + for (int ch = 0; ch < mChannelCount; ch++) { + setMbcByChannelIndex(ch, mbc); + } } - public MbcBand getMbcBandByChannelIndex(int channelIndex, int band) { - //TODO: return info from engine instead of cached config - return mConfig.getMbcBandByChannelIndex(channelIndex, band); + return queryEngineMbcBandByChannelIndex(channelIndex, band); } - public void setMbcBandByChannelIndex(int channelIndex, int band, MbcBand mbcBand) { - mConfig.setMbcBandByChannelIndex(channelIndex, band, mbcBand); - //TODO: communicate change to engine + updateEngineMbcBandByChannelIndex(channelIndex, band, mbcBand); } - public void setMbcBandAllChannelsTo(int band, MbcBand mbcBand) { - mConfig.setMbcBandAllChannelsTo(band, mbcBand); - //TODO: communicate change to engine + for (int ch = 0; ch < mChannelCount; ch++) { + setMbcBandByChannelIndex(ch, band, mbcBand); + } } //== PostEq public Eq getPostEqByChannelIndex(int channelIndex) { - //TODO: return info from engine instead of cached config - return mConfig.getPostEqByChannelIndex(channelIndex); + return queryEngineEqByChannelIndex(PARAM_POST_EQ, channelIndex); } - public void setPostEqByChannelIndex(int channelIndex, Eq postEq) { - mConfig.setPostEqByChannelIndex(channelIndex, postEq); - //TODO: communicate change to engine + updateEngineEqByChannelIndex(PARAM_POST_EQ, channelIndex, postEq); } - public void setPostEqAllChannelsTo(Eq postEq) { - mConfig.setPostEqAllChannelsTo(postEq); - //TODO: communicate change to engine + for (int ch = 0; ch < mChannelCount; ch++) { + setPostEqByChannelIndex(ch, postEq); + } } - public EqBand getPostEqBandByChannelIndex(int channelIndex, int band) { - //TODO: return info from engine instead of cached config - return mConfig.getPostEqBandByChannelIndex(channelIndex, band); + return queryEngineEqBandByChannelIndex(PARAM_POST_EQ_BAND, channelIndex, band); } - public void setPostEqBandByChannelIndex(int channelIndex, int band, EqBand postEqBand) { - mConfig.setPostEqBandByChannelIndex(channelIndex, band, postEqBand); - //TODO: communicate change to engine + updateEngineEqBandByChannelIndex(PARAM_POST_EQ_BAND, channelIndex, band, postEqBand); } - public void setPostEqBandAllChannelsTo(int band, EqBand postEqBand) { - mConfig.setPostEqBandAllChannelsTo(band, postEqBand); - //TODO: communicate change to engine + for (int ch = 0; ch < mChannelCount; ch++) { + setPostEqBandByChannelIndex(ch, band, postEqBand); + } } //==== Limiter public Limiter getLimiterByChannelIndex(int channelIndex) { - //TODO: return info from engine instead of cached config - return mConfig.getLimiterByChannelIndex(channelIndex); + return queryEngineLimiterByChannelIndex(channelIndex); } - public void setLimiterByChannelIndex(int channelIndex, Limiter limiter) { - mConfig.setLimiterByChannelIndex(channelIndex, limiter); - //TODO: communicate change to engine + updateEngineLimiterByChannelIndex(channelIndex, limiter); } - public void setLimiterAllChannelsTo(Limiter limiter) { - mConfig.setLimiterAllChannelsTo(limiter); - //TODO: communicate change to engine + for (int ch = 0; ch < mChannelCount; ch++) { + setLimiterByChannelIndex(ch, limiter); + } } /** @@ -1905,165 +1888,327 @@ public final class DynamicsProcessing extends AudioEffect { return getOneInt(PARAM_GET_CHANNEL_COUNT); } - private void setEngineArchitecture(int variant, boolean preEqInUse, int preEqBandCount, - boolean mbcInUse, int mbcBandCount, boolean postEqInUse, int postEqBandCount, - boolean limiterInUse) { - int[] values = { variant, (preEqInUse ? 1 : 0), preEqBandCount, - (mbcInUse ? 1 : 0), mbcBandCount, (postEqInUse ? 1 : 0), postEqBandCount, + //=== 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)}; - //TODO: enable later setIntArray(PARAM_SET_ENGINE_ARCHITECTURE, values); + 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 paramGet) { - int[] param = new int[1]; - int[] result = new int[1]; + private int getOneInt(int param) { + final int[] params = { param }; + final int[] result = new int[1]; - param[0] = paramGet; - checkStatus(getParameter(param, result)); + checkStatus(getParameter(params, result)); return result[0]; } - private int getTwoInt(int paramGet, int paramA) { - int[] param = new int[2]; - int[] result = new int[1]; + private void setTwoFloat(int param, int paramA, float valueSet) { + final int[] params = { param, paramA }; + final byte[] value; - param[0] = paramGet; - param[1] = paramA; - checkStatus(getParameter(param, result)); - return result[0]; - } - - private int getThreeInt(int paramGet, int paramA, int paramB) { - //have to use bytearrays, with more than 2 parameters. - byte[] paramBytes = concatArrays(intToByteArray(paramGet), - intToByteArray(paramA), - intToByteArray(paramB)); - byte[] resultBytes = new byte[4]; //single int - - checkStatus(getParameter(paramBytes, resultBytes)); - - return byteArrayToInt(resultBytes); - } - - private void setOneInt(int paramSet, int valueSet) { - int[] param = new int[1]; - int[] value = new int[1]; - - param[0] = paramSet; - value[0] = valueSet; - checkStatus(setParameter(param, value)); - } - - private void setTwoInt(int paramSet, int paramA, int valueSet) { - int[] param = new int[2]; - int[] value = new int[1]; - - param[0] = paramSet; - param[1] = paramA; - value[0] = valueSet; - checkStatus(setParameter(param, value)); - } - - private void setThreeInt(int paramSet, int paramA, int paramB, int valueSet) { - //have to use bytearrays, with more than 2 parameters. - byte[] paramBytes = concatArrays(intToByteArray(paramSet), - intToByteArray(paramA), - intToByteArray(paramB)); - byte[] valueBytes = intToByteArray(valueSet); - - checkStatus(setParameter(paramBytes, valueBytes)); - } - - private void setOneFloat(int paramSet, float valueSet) { - int[] param = new int[1]; - byte[] value; - - param[0] = paramSet; - value = floatToByteArray(valueSet); - checkStatus(setParameter(param, value)); - } - - private void setTwoFloat(int paramSet, int paramA, float valueSet) { - int[] param = new int[2]; - byte[] value; - - param[0] = paramSet; - param[1] = paramA; value = floatToByteArray(valueSet); - checkStatus(setParameter(param, value)); + checkStatus(setParameter(params, value)); } - private void setThreeFloat(int paramSet, int paramA, int paramB, float valueSet) { - //have to use bytearrays, with more than 2 parameters. - byte[] paramBytes = concatArrays(intToByteArray(paramSet), - intToByteArray(paramA), - intToByteArray(paramB)); - byte[] valueBytes = floatToByteArray(valueSet); - - checkStatus(setParameter(paramBytes, valueBytes)); - } - private byte[] intArrayToByteArray(int[] values) { - int expectedBytes = values.length * 4; + 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 k = 0; k < values.length; k++) { - converter.putFloat(values[k]); + 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 setIntArray(int paramSet, int[] paramArray) { - //have to use bytearrays, with more than 2 parameters. - byte[] paramBytes = intToByteArray(paramSet); - byte[] valueBytes = intArrayToByteArray(paramArray); - checkStatus(setParameter(paramBytes, valueBytes)); + 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 float getOneFloat(int paramGet) { - int[] param = new int[1]; - byte[] result = new byte[4]; - - param[0] = paramGet; - checkStatus(getParameter(param, result)); - return byteArrayToFloat(result); + private void setNumberArray(Number[] params, Number[] values) { + byte[] paramBytes = numberArrayToByteArray(params); + byte[] valueBytes = numberArrayToByteArray(values); + checkStatus(setParameter(paramBytes, valueBytes)); } - private float getTwoFloat(int paramGet, int paramA) { - int[] param = new int[2]; - byte[] result = new byte[4]; + private float getTwoFloat(int param, int paramA) { + final int[] params = { param, paramA }; + final byte[] result = new byte[4]; - param[0] = paramGet; - param[1] = paramA; - checkStatus(getParameter(param, result)); + checkStatus(getParameter(params, result)); return byteArrayToFloat(result); } - private float getThreeFloat(int paramGet, int paramA, int paramB) { - //have to use bytearrays, with more than 2 parameters. - byte[] paramBytes = concatArrays(intToByteArray(paramGet), - intToByteArray(paramA), - intToByteArray(paramB)); - byte[] resultBytes = new byte[4]; //single float - - checkStatus(getParameter(paramBytes, resultBytes)); - - return byteArrayToFloat(resultBytes); - } - - private float[] getOneFloatArray(int paramGet, int expectedSize) { - int[] param = new int[1]; - byte[] result = new byte[4 * expectedSize]; - - param[0] = paramGet; - checkStatus(getParameter(param, result)); - float[] returnArray = new float[expectedSize]; - for (int k = 0; k < expectedSize; k++) { - returnArray[k] = byteArrayToFloat(result, 4 * k); - } - return returnArray; - } /** * @hide * The OnParameterChangeListener interface defines a method called by the DynamicsProcessing |