diff --git a/src/main/java/sh/ball/audio/effect/EffectAnimator.java b/src/main/java/sh/ball/audio/effect/EffectAnimator.java index 7ad1b506..48c6a567 100644 --- a/src/main/java/sh/ball/audio/effect/EffectAnimator.java +++ b/src/main/java/sh/ball/audio/effect/EffectAnimator.java @@ -9,13 +9,21 @@ public class EffectAnimator extends PhaseEffect implements SettableEffect { private final SettableEffect effect; private AnimationType type = AnimationType.STATIC; - private double value = 0.5; + private double targetValue = 0.5; private double actualValue = 0.5; private boolean linearDirection = true; + private double min; + private double max; - public EffectAnimator(int sampleRate, SettableEffect effect) { + public EffectAnimator(int sampleRate, SettableEffect effect, double min, double max) { super(sampleRate, 1.0); this.effect = effect; + this.min = min; + this.max = max; + } + + public EffectAnimator(int sampleRate, SettableEffect effect) { + this(sampleRate, effect, 0, 1); } public void setAnimation(AnimationType type) { @@ -23,14 +31,30 @@ public class EffectAnimator extends PhaseEffect implements SettableEffect { this.linearDirection = true; } + public void setMin(double min) { + this.min = min; + } + + public void setMax(double max) { + this.max = max; + } + @Override public Vector2 apply(int count, Vector2 vector) { + double minValue = min; + double maxValue = max; + double range = maxValue - minValue; + if (range <= 0) { + return vector; + } + double normalisedTargetValue = (targetValue - minValue) / range; + double normalisedActualValue = (actualValue - minValue) / range; switch (type) { - case STATIC -> actualValue = value; + case STATIC -> actualValue = targetValue; case SEESAW -> { - double scalar = 10 * Math.max(Math.min(actualValue, 1 - actualValue), 0.01); - double change = scalar * SPEED_SCALE * value / sampleRate; - if (actualValue + change > 1 || actualValue - change < 0) { + double scalar = 10 * Math.max(Math.min(normalisedActualValue, 1 - normalisedActualValue), 0.01); + double change = range * scalar * SPEED_SCALE * normalisedTargetValue / sampleRate; + if (actualValue + change > maxValue || actualValue - change < minValue) { linearDirection = !linearDirection; } if (linearDirection) { @@ -40,8 +64,8 @@ public class EffectAnimator extends PhaseEffect implements SettableEffect { } } case LINEAR -> { - double change = SPEED_SCALE * value / sampleRate; - if (actualValue + change > 1 || actualValue - change < 0) { + double change = range * SPEED_SCALE * normalisedTargetValue / sampleRate; + if (actualValue + change > maxValue || actualValue - change < minValue) { linearDirection = !linearDirection; } if (linearDirection) { @@ -51,15 +75,15 @@ public class EffectAnimator extends PhaseEffect implements SettableEffect { } } case FORWARD -> { - actualValue += 0.5 * SPEED_SCALE * value / sampleRate; - if (actualValue > 1) { - actualValue = 0; + actualValue += range * 0.5 * SPEED_SCALE * normalisedTargetValue / sampleRate; + if (actualValue > maxValue) { + actualValue = minValue; } } case REVERSE -> { - actualValue -= 0.5 * SPEED_SCALE * value / sampleRate; - if (actualValue < 0) { - actualValue = 1; + actualValue -= range * 0.5 * SPEED_SCALE * normalisedTargetValue / sampleRate; + if (actualValue < minValue) { + actualValue = maxValue; } } } @@ -69,7 +93,7 @@ public class EffectAnimator extends PhaseEffect implements SettableEffect { @Override public void setValue(double value) { - this.value = value; + this.targetValue = value; effect.setValue(value); } } diff --git a/src/main/java/sh/ball/gui/controller/EffectsController.java b/src/main/java/sh/ball/gui/controller/EffectsController.java index 54ae44cf..a95308c7 100644 --- a/src/main/java/sh/ball/gui/controller/EffectsController.java +++ b/src/main/java/sh/ball/gui/controller/EffectsController.java @@ -35,7 +35,7 @@ public class EffectsController implements Initializable, SubController { private final EffectAnimator traceAnimator; private final EffectAnimator vectorCancellingAnimator; private final EffectAnimator bitCrushAnimator; - private final EffectAnimator smoothAnimator; + private final EffectAnimator smoothingAnimator; private final EffectAnimator verticalDistortAnimator; private final EffectAnimator horizontalDistortAnimator; @@ -116,7 +116,7 @@ public class EffectsController implements Initializable, SubController { this.traceAnimator = new EffectAnimator(DEFAULT_SAMPLE_RATE, new TraceEffect(audioPlayer)); this.vectorCancellingAnimator = new EffectAnimator(DEFAULT_SAMPLE_RATE, new VectorCancellingEffect()); this.bitCrushAnimator = new EffectAnimator(DEFAULT_SAMPLE_RATE, new BitCrushEffect()); - this.smoothAnimator = new EffectAnimator(DEFAULT_SAMPLE_RATE, new SmoothEffect(1)); + this.smoothingAnimator = new EffectAnimator(DEFAULT_SAMPLE_RATE, new SmoothEffect(1)); this.verticalDistortAnimator = new EffectAnimator(DEFAULT_SAMPLE_RATE, new VerticalDistortEffect(0.2)); this.horizontalDistortAnimator = new EffectAnimator(DEFAULT_SAMPLE_RATE, new HorizontalDistortEffect(0.2)); } @@ -139,7 +139,7 @@ public class EffectsController implements Initializable, SubController { vectorCancellingComboBox, vectorCancellingAnimator, bitCrushComboBox, bitCrushAnimator, wobbleComboBox, wobbleAnimator, - smoothingComboBox, smoothAnimator, + smoothingComboBox, smoothingAnimator, traceComboBox, traceAnimator, verticalDistortComboBox, verticalDistortAnimator, horizontalDistortComboBox, horizontalDistortAnimator @@ -217,7 +217,7 @@ public class EffectsController implements Initializable, SubController { InvalidationListener wobbleListener = e -> updateEffect(EffectType.WOBBLE, wobbleCheckBox.isSelected(), wobbleAnimator, wobbleSlider.getValue()); InvalidationListener smoothListener = e -> - updateEffect(EffectType.SMOOTH, smoothingCheckBox.isSelected(), smoothAnimator, smoothingSlider.getValue()); + updateEffect(EffectType.SMOOTH, smoothingCheckBox.isSelected(), smoothingAnimator, smoothingSlider.getValue()); InvalidationListener vectorCancellingListener = e -> updateEffect(EffectType.VECTOR_CANCELLING, vectorCancellingCheckBox.isSelected(), vectorCancellingAnimator, vectorCancellingSlider.getValue()); InvalidationListener traceListener = e -> @@ -261,6 +261,16 @@ public class EffectsController implements Initializable, SubController { entry.getValue().setAnimation(animationType); }); } + + List animators = animators(); + List sliders = sliders(); + for (int i = 0; i < sliders.size(); i++) { + int finalI = i; + sliders.get(i).minProperty().addListener((e, old, min) -> + animators.get(finalI).setMin(min.doubleValue())); + sliders.get(i).maxProperty().addListener((e, old, max) -> + animators.get(finalI).setMax(max.doubleValue())); + } } private List> comboBoxes() { @@ -270,6 +280,10 @@ public class EffectsController implements Initializable, SubController { return List.of(vectorCancellingCheckBox, bitCrushCheckBox, verticalDistortCheckBox, horizontalDistortCheckBox, wobbleCheckBox, smoothingCheckBox, traceCheckBox); } + private List animators() { + return List.of(vectorCancellingAnimator, bitCrushAnimator, verticalDistortAnimator, + horizontalDistortAnimator, wobbleAnimator, smoothingAnimator, traceAnimator); + } @Override public List micCheckBoxes() { return List.of(vectorCancellingMic, bitCrushMic, verticalDistortMic, horizontalDistortMic, diff --git a/src/main/java/sh/ball/gui/controller/MainController.java b/src/main/java/sh/ball/gui/controller/MainController.java index 00b4dd43..612ce4b7 100644 --- a/src/main/java/sh/ball/gui/controller/MainController.java +++ b/src/main/java/sh/ball/gui/controller/MainController.java @@ -265,14 +265,16 @@ public class MainController implements Initializable, FrequencyListener, MidiLis }); sliderComboBox.setValue(printableSliders.get(0)); - sliderMinTextField.textProperty().addListener((e, old, text) -> { - if (parseable(text)) { + sliderMinTextField.focusedProperty().addListener((e, old, focused) -> { + String text = sliderMinTextField.getText(); + if (!focused && parseable(text)) { sliderComboBox.getValue().slider.setMin(Double.parseDouble(text)); updateSliderUnits(sliderComboBox.getValue().slider); } }); - sliderMaxTextField.textProperty().addListener((e, old, text) -> { - if (parseable(text)) { + sliderMaxTextField.focusedProperty().addListener((e, old, focused) -> { + String text = sliderMaxTextField.getText(); + if (!focused && parseable(text)) { sliderComboBox.getValue().slider.setMax(Double.parseDouble(text)); updateSliderUnits(sliderComboBox.getValue().slider); }