Add image visibility slider for when playing multiple notes and improve reliability

pull/35/head
James Ball 2021-07-23 21:42:58 +01:00
rodzic 8bbcc1fb07
commit 0d0fad297f
5 zmienionych plików z 109 dodań i 85 usunięć

Wyświetl plik

@ -6,7 +6,7 @@
<groupId>sh.ball</groupId>
<artifactId>osci-render</artifactId>
<version>1.11.9</version>
<version>1.11.10</version>
<name>osci-render</name>

Wyświetl plik

@ -16,6 +16,8 @@ public interface AudioPlayer<S> extends Runnable {
void setOctave(int octave);
void setMainFrequencyScale(double scale);
void setBaseFrequencies(List<Double> frequency);
void setPitchBendFactor(double pitchBend);

Wyświetl plik

@ -47,6 +47,7 @@ public class ShapeAudioPlayer implements AudioPlayer<List<Shape>> {
private int count = 0;
private List<Double> frequencies = List.of(MIDDLE_C);
private double mainFrequency = MIDDLE_C;
private double mainFrequencyScale = 0.5;
private double maxFrequency = MIDDLE_C;
private double pitchBend = 1.0;
private double trace = 0.5;
@ -132,10 +133,11 @@ public class ShapeAudioPlayer implements AudioPlayer<List<Shape>> {
}
private Vector2 applyEffects(int frame, Vector2 vector) {
int numNotes = sineEffects.size() + 1;
vector.scale(1.0 / numNotes);
int numNotes = sineEffects.size();
vector = vector.scale(2 * mainFrequencyScale);
double scaledVolume = (1 - mainFrequencyScale) / numNotes;
for (SineEffect effect : sineEffects) {
effect.setVolume(1.0 / numNotes);
effect.setVolume(scaledVolume);
vector = effect.apply(frame, vector);
}
for (Effect effect : effects.values()) {
@ -235,6 +237,11 @@ public class ShapeAudioPlayer implements AudioPlayer<List<Shape>> {
this.mainFrequency = maxFrequency * Math.pow(2, octave - 1);
}
@Override
public void setMainFrequencyScale(double scale) {
this.mainFrequencyScale = scale;
}
@Override
public void addFrame(List<Shape> frame) {
try {

Wyświetl plik

@ -182,6 +182,10 @@ public class Controller implements Initializable, FrequencyListener, MidiListene
@FXML
private SVGPath octaveMidi;
@FXML
private Slider visibilitySlider;
@FXML
private SVGPath visibilityMidi;
@FXML
private ComboBox<AudioDevice> deviceComboBox;
public Controller(AudioPlayer<List<Shape>> audioPlayer) throws IOException {
@ -215,6 +219,7 @@ public class Controller implements Initializable, FrequencyListener, MidiListene
midiMap.put(bitCrushMidi, bitCrushSlider);
midiMap.put(wobbleMidi, wobbleSlider);
midiMap.put(octaveMidi, octaveSlider);
midiMap.put(visibilityMidi, visibilitySlider);
midiMap.put(verticalDistortMidi, verticalDistortSlider);
midiMap.put(horizontalDistortMidi, horizontalDistortSlider);
return midiMap;
@ -227,7 +232,8 @@ public class Controller implements Initializable, FrequencyListener, MidiListene
translationSpeedSlider, translateEffect::setSpeed,
scaleSlider, scaleEffect::setScale,
focalLengthSlider, d -> updateFocalLength(),
objectRotateSpeedSlider, d -> updateObjectRotateSpeed()
objectRotateSpeedSlider, d -> updateObjectRotateSpeed(),
visibilitySlider, audioPlayer::setMainFrequencyScale
);
}
@ -650,78 +656,80 @@ public class Controller implements Initializable, FrequencyListener, MidiListene
@Override
public void sendMidiMessage(ShortMessage message) {
int command = message.getCommand();
Platform.runLater(() -> {
int command = message.getCommand();
if (command == ShortMessage.CONTROL_CHANGE) {
int id = message.getData1();
int value = message.getData2();
if (command == ShortMessage.CONTROL_CHANGE) {
int id = message.getData1();
int value = message.getData2();
if (armedMidi != null) {
if (CCMap.containsValue(armedMidi)) {
CCMap.values().remove(armedMidi);
if (armedMidi != null) {
if (CCMap.containsValue(armedMidi)) {
CCMap.values().remove(armedMidi);
}
if (CCMap.containsKey(id)) {
CCMap.get(id).setFill(Color.color(1, 1, 1));
}
CCMap.put(id, armedMidi);
armedMidi.setFill(Color.color(0, 1, 0));
armedMidiPaint = null;
armedMidi = null;
}
if (CCMap.containsKey(id)) {
CCMap.get(id).setFill(Color.color(1, 1, 1));
Slider slider = midiButtonMap.get(CCMap.get(id));
double sliderValue = midiPressureToPressure(slider, value);
if (slider.isSnapToTicks()) {
double increment = slider.getMajorTickUnit() / (slider.getMinorTickCount() + 1);
sliderValue = increment * (Math.round(sliderValue / increment));
}
slider.setValue(sliderValue);
}
CCMap.put(id, armedMidi);
armedMidi.setFill(Color.color(0, 1, 0));
armedMidiPaint = null;
armedMidi = null;
}
if (CCMap.containsKey(id)) {
Slider slider = midiButtonMap.get(CCMap.get(id));
double sliderValue = midiPressureToPressure(slider, value);
} else if (command == ShortMessage.NOTE_ON || command == ShortMessage.NOTE_OFF) {
MidiNote note = new MidiNote(message.getData1());
int velocity = message.getData2();
if (slider.isSnapToTicks()) {
double increment = slider.getMajorTickUnit() / (slider.getMinorTickCount() + 1);
sliderValue = increment * (Math.round(sliderValue / increment));
}
slider.setValue(sliderValue);
}
} else if (command == ShortMessage.NOTE_ON || command == ShortMessage.NOTE_OFF) {
MidiNote note = new MidiNote(message.getData1());
int velocity = message.getData2();
double oldVolume = scaleSlider.getValue();
double volume = midiPressureToPressure(scaleSlider, velocity);
volume /= 10;
double oldVolume = scaleSlider.getValue();
double volume = midiPressureToPressure(scaleSlider, velocity);
volume /= 10;
if (command == ShortMessage.NOTE_OFF) {
downKeys.remove(note);
if (downKeys.isEmpty()) {
KeyValue kv = new KeyValue(scaleSlider.valueProperty(), 0, Interpolator.EASE_OUT);
KeyFrame kf = new KeyFrame(Duration.millis(500), kv);
volumeTimeline = new Timeline(kf);
volumeTimeline.play();
if (command == ShortMessage.NOTE_OFF) {
downKeys.remove(note);
if (downKeys.isEmpty()) {
KeyValue kv = new KeyValue(scaleSlider.valueProperty(), 0, Interpolator.EASE_OUT);
KeyFrame kf = new KeyFrame(Duration.millis(500), kv);
volumeTimeline = new Timeline(kf);
Platform.runLater(volumeTimeline::play);
} else {
playNotes(oldVolume);
}
} else {
playNotes(oldVolume);
downKeys.add(note);
if (volumeTimeline != null) {
volumeTimeline.stop();
volumeTimeline = null;
}
playNotes(volume);
KeyValue kv = new KeyValue(scaleSlider.valueProperty(), scaleSlider.valueProperty().get() * 0.75, Interpolator.EASE_OUT);
KeyFrame kf = new KeyFrame(Duration.millis(250), kv);
volumeTimeline = new Timeline(kf);
Platform.runLater(volumeTimeline::play);
}
} else {
downKeys.add(note);
if (volumeTimeline != null) {
volumeTimeline.stop();
volumeTimeline = null;
}
playNotes(volume);
KeyValue kv = new KeyValue(scaleSlider.valueProperty(), scaleSlider.valueProperty().get() * 0.75, Interpolator.EASE_OUT);
KeyFrame kf = new KeyFrame(Duration.millis(250), kv);
volumeTimeline = new Timeline(kf);
volumeTimeline.play();
} else if (command == ShortMessage.PITCH_BEND) {
// using these instructions https://sites.uci.edu/camp2014/2014/04/30/managing-midi-pitchbend-messages/
int pitchBend = (message.getData2() << PITCH_BEND_DATA_LENGTH) | message.getData1();
// get pitch bend in range -1 to 1
double pitchBendFactor = (double) pitchBend / PITCH_BEND_MAX;
pitchBendFactor = 2 * pitchBendFactor - 1;
pitchBendFactor *= PITCH_BEND_SEMITONES;
// 12 tone equal temperament
pitchBendFactor /= 12;
pitchBendFactor = Math.pow(2, pitchBendFactor);
audioPlayer.setPitchBendFactor(pitchBendFactor);
}
} else if (command == ShortMessage.PITCH_BEND) {
// using these instructions https://sites.uci.edu/camp2014/2014/04/30/managing-midi-pitchbend-messages/
int pitchBend = (message.getData2() << PITCH_BEND_DATA_LENGTH) | message.getData1();
// get pitch bend in range -1 to 1
double pitchBendFactor = (double) pitchBend / PITCH_BEND_MAX;
pitchBendFactor = 2 * pitchBendFactor - 1;
pitchBendFactor *= PITCH_BEND_SEMITONES;
// 12 tone equal temperament
pitchBendFactor /= 12;
pitchBendFactor = Math.pow(2, pitchBendFactor);
audioPlayer.setPitchBendFactor(pitchBendFactor);
}
});
}
// gets the volume/scale

Wyświetl plik

@ -11,7 +11,7 @@
<?import javafx.scene.shape.SVGPath?>
<?import javafx.scene.text.Font?>
<AnchorPane prefHeight="539.0" prefWidth="837.0" stylesheets="@../css/main.css" xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1">
<AnchorPane prefHeight="589.0" prefWidth="837.0" stylesheets="@../css/main.css" xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1">
<TitledPane animated="false" collapsible="false" layoutX="426.0" layoutY="8.0" prefHeight="303.0" prefWidth="402.0" text="Audio Effects">
<content>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="262.0" prefWidth="401.0">
@ -38,22 +38,22 @@
</AnchorPane>
</content>
</TitledPane>
<TitledPane fx:id="objTitledPane" animated="false" collapsible="false" layoutX="426.0" layoutY="318.0" maxHeight="-Infinity" maxWidth="-Infinity" prefHeight="210.0" prefWidth="402.0" text="3D .obj file settings">
<TitledPane fx:id="objTitledPane" animated="false" collapsible="false" layoutX="426.0" layoutY="318.0" maxHeight="-Infinity" maxWidth="-Infinity" prefHeight="257.0" prefWidth="402.0" text="3D .obj file settings">
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="359.0">
<Slider fx:id="focalLengthSlider" blockIncrement="0.01" layoutX="116.0" layoutY="15.0" majorTickUnit="0.2" max="2.0" min="1.0E-5" prefHeight="42.0" prefWidth="258.0" showTickLabels="true" showTickMarks="true" value="1.0" />
<Label layoutX="34.0" layoutY="14.0" text="Focal length" />
<TextField fx:id="cameraXTextField" layoutX="134.0" layoutY="57.0" prefHeight="26.0" prefWidth="65.0" text="0" />
<Label layoutX="35.0" layoutY="61.0" text="Camera pos" />
<Label layoutX="118.0" layoutY="61.0" text="x :" />
<Label layoutX="207.0" layoutY="60.0" text="y :" />
<TextField fx:id="cameraYTextField" layoutX="224.0" layoutY="57.0" prefHeight="26.0" prefWidth="65.0" text="0" />
<Label layoutX="299.0" layoutY="60.0" text="z :" />
<TextField fx:id="cameraZTextField" layoutX="315.0" layoutY="57.0" prefHeight="26.0" prefWidth="65.0" text="-2.5" />
<Slider fx:id="objectRotateSpeedSlider" blockIncrement="0.005" layoutX="116.0" layoutY="97.0" majorTickUnit="0.1" max="1.0" prefHeight="42.0" prefWidth="258.0" showTickLabels="true" showTickMarks="true" />
<Label layoutX="9.0" layoutY="96.0" text="3D Rotate speed" />
<CheckBox fx:id="rotateCheckBox" layoutX="90.0" layoutY="147.0" mnemonicParsing="false" text="Rotate with Mouse (Esc to disable)" />
<SVGPath fx:id="focalLengthMidi" content="M20.15 8.26H22V15.74H20.15M13 8.26H18.43C19 8.26 19.3 8.74 19.3 9.3V14.81C19.3 15.5 19 15.74 18.38 15.74H13V11H14.87V13.91H17.5V9.95H13M10.32 8.26H12.14V15.74H10.32M2 8.26H8.55C9.1 8.26 9.41 8.74 9.41 9.3V15.74H7.59V10.15H6.5V15.74H4.87V10.15H3.83V15.74H2Z" fill="WHITE" layoutX="374.0" layoutY="12.0" pickOnBounds="true" />
<SVGPath fx:id="objectRotateSpeedMidi" content="M20.15 8.26H22V15.74H20.15M13 8.26H18.43C19 8.26 19.3 8.74 19.3 9.3V14.81C19.3 15.5 19 15.74 18.38 15.74H13V11H14.87V13.91H17.5V9.95H13M10.32 8.26H12.14V15.74H10.32M2 8.26H8.55C9.1 8.26 9.41 8.74 9.41 9.3V15.74H7.59V10.15H6.5V15.74H4.87V10.15H3.83V15.74H2Z" fill="WHITE" layoutX="374.0" layoutY="94.0" pickOnBounds="true" />
<Slider fx:id="focalLengthSlider" blockIncrement="0.01" layoutX="116.0" layoutY="22.0" majorTickUnit="0.2" max="2.0" min="1.0E-5" prefHeight="42.0" prefWidth="258.0" showTickLabels="true" showTickMarks="true" value="1.0" />
<Label layoutX="34.0" layoutY="21.0" text="Focal length" />
<TextField fx:id="cameraXTextField" layoutX="134.0" layoutY="74.0" prefHeight="26.0" prefWidth="65.0" text="0" />
<Label layoutX="35.0" layoutY="78.0" text="Camera pos" />
<Label layoutX="118.0" layoutY="78.0" text="x :" />
<Label layoutX="207.0" layoutY="77.0" text="y :" />
<TextField fx:id="cameraYTextField" layoutX="224.0" layoutY="74.0" prefHeight="26.0" prefWidth="65.0" text="0" />
<Label layoutX="299.0" layoutY="77.0" text="z :" />
<TextField fx:id="cameraZTextField" layoutX="315.0" layoutY="74.0" prefHeight="26.0" prefWidth="65.0" text="-2.5" />
<Slider fx:id="objectRotateSpeedSlider" blockIncrement="0.005" layoutX="116.0" layoutY="121.0" majorTickUnit="0.1" max="1.0" prefHeight="42.0" prefWidth="258.0" showTickLabels="true" showTickMarks="true" />
<Label layoutX="9.0" layoutY="120.0" text="3D Rotate speed" />
<CheckBox fx:id="rotateCheckBox" layoutX="90.0" layoutY="179.0" mnemonicParsing="false" text="Rotate with Mouse (Esc to disable)" />
<SVGPath fx:id="focalLengthMidi" content="M20.15 8.26H22V15.74H20.15M13 8.26H18.43C19 8.26 19.3 8.74 19.3 9.3V14.81C19.3 15.5 19 15.74 18.38 15.74H13V11H14.87V13.91H17.5V9.95H13M10.32 8.26H12.14V15.74H10.32M2 8.26H8.55C9.1 8.26 9.41 8.74 9.41 9.3V15.74H7.59V10.15H6.5V15.74H4.87V10.15H3.83V15.74H2Z" fill="WHITE" layoutX="374.0" layoutY="19.0" pickOnBounds="true" />
<SVGPath fx:id="objectRotateSpeedMidi" content="M20.15 8.26H22V15.74H20.15M13 8.26H18.43C19 8.26 19.3 8.74 19.3 9.3V14.81C19.3 15.5 19 15.74 18.38 15.74H13V11H14.87V13.91H17.5V9.95H13M10.32 8.26H12.14V15.74H10.32M2 8.26H8.55C9.1 8.26 9.41 8.74 9.41 9.3V15.74H7.59V10.15H6.5V15.74H4.87V10.15H3.83V15.74H2Z" fill="WHITE" layoutX="374.0" layoutY="118.0" pickOnBounds="true" />
</AnchorPane>
</TitledPane>
<AnchorPane id="control-pane" layoutX="12.0" layoutY="7.0" prefHeight="274.0" prefWidth="402.0">
@ -71,9 +71,9 @@
<CheckBox fx:id="recordCheckBox" layoutX="143.0" layoutY="146.0" mnemonicParsing="false" text="Timed record" />
</children>
</AnchorPane>
<TitledPane collapsible="false" layoutX="12.0" layoutY="289.0" prefHeight="239.0" prefWidth="402.0" text="Image settings">
<TitledPane collapsible="false" layoutX="12.0" layoutY="289.0" prefHeight="286.0" prefWidth="402.0" text="Image settings">
<content>
<AnchorPane minHeight="0.0" minWidth="0.0">
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="250.0" prefWidth="402.0">
<children>
<Slider fx:id="rotateSpeedSlider" blockIncrement="0.05" layoutX="116.0" layoutY="90.0" majorTickUnit="1.0" max="10.0" prefHeight="42.0" prefWidth="254.0" showTickLabels="true" showTickMarks="true" />
<Label layoutX="13.0" layoutY="89.0" text="2D Rotate speed">
@ -122,6 +122,13 @@
</Label>
<Label layoutX="162.0" layoutY="71.0" text="5" />
<Label layoutX="122.0" layoutY="71.0" text="0" />
<Label layoutX="24.0" layoutY="206.0" text="Image visibility">
<font>
<Font size="13.0" />
</font>
</Label>
<Slider fx:id="visibilitySlider" blockIncrement="0.005" layoutX="116.0" layoutY="207.0" majorTickUnit="0.1" max="1.0" prefHeight="42.0" prefWidth="253.0" showTickLabels="true" showTickMarks="true" value="0.5" />
<SVGPath fx:id="visibilityMidi" content="M20.15 8.26H22V15.74H20.15M13 8.26H18.43C19 8.26 19.3 8.74 19.3 9.3V14.81C19.3 15.5 19 15.74 18.38 15.74H13V11H14.87V13.91H17.5V9.95H13M10.32 8.26H12.14V15.74H10.32M2 8.26H8.55C9.1 8.26 9.41 8.74 9.41 9.3V15.74H7.59V10.15H6.5V15.74H4.87V10.15H3.83V15.74H2Z" fill="WHITE" layoutX="371.0" layoutY="204.0" pickOnBounds="true" />
</children>
</AnchorPane>
</content>