kopia lustrzana https://github.com/jameshball/osci-render
Add Choose Folder button so multiple files can be opened
rodzic
5084a11313
commit
81e261a27e
|
@ -5,6 +5,7 @@ import javafx.animation.Timeline;
|
|||
import javafx.application.Platform;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.stage.DirectoryChooser;
|
||||
import javafx.util.Duration;
|
||||
import sh.ball.audio.*;
|
||||
import sh.ball.audio.effect.*;
|
||||
|
@ -14,10 +15,7 @@ import java.io.IOException;
|
|||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.function.Consumer;
|
||||
|
@ -47,14 +45,13 @@ import sh.ball.shapes.Vector2;
|
|||
|
||||
public class Controller implements Initializable, FrequencyListener, Listener {
|
||||
|
||||
|
||||
private static final InputStream DEFAULT_OBJ = Controller.class.getResourceAsStream("/models/cube.obj");
|
||||
|
||||
private final FileChooser fileChooser = new FileChooser();
|
||||
private final DirectoryChooser folderChooser = new DirectoryChooser();
|
||||
private final AudioPlayer<List<Shape>> audioPlayer;
|
||||
private final ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||
|
||||
|
||||
private final RotateEffect rotateEffect;
|
||||
private final TranslateEffect translateEffect;
|
||||
private final WobbleEffect wobbleEffect;
|
||||
|
@ -63,9 +60,13 @@ public class Controller implements Initializable, FrequencyListener, Listener {
|
|||
private int sampleRate;
|
||||
private FrequencyAnalyser<List<Shape>> analyser;
|
||||
private final AudioDevice defaultDevice;
|
||||
private FrameProducer<List<Shape>> producer;
|
||||
private boolean recording = false;
|
||||
|
||||
private FrameProducer<List<Shape>> producer;
|
||||
private final List<FrameSet<List<Shape>>> frameSets = new ArrayList<>();
|
||||
private final List<String> frameSetPaths = new ArrayList<>();
|
||||
private int currentFrameSet;
|
||||
|
||||
private Stage stage;
|
||||
|
||||
@FXML
|
||||
|
@ -73,6 +74,8 @@ public class Controller implements Initializable, FrequencyListener, Listener {
|
|||
@FXML
|
||||
private Button chooseFileButton;
|
||||
@FXML
|
||||
private Button chooseFolderButton;
|
||||
@FXML
|
||||
private Label fileLabel;
|
||||
@FXML
|
||||
private Button recordButton;
|
||||
|
@ -130,6 +133,8 @@ public class Controller implements Initializable, FrequencyListener, Listener {
|
|||
public Controller(AudioPlayer<List<Shape>> audioPlayer) throws IOException {
|
||||
this.audioPlayer = audioPlayer;
|
||||
FrameSet<List<Shape>> frames = new ObjParser(DEFAULT_OBJ).parse();
|
||||
frameSets.add(frames);
|
||||
currentFrameSet = 0;
|
||||
frames.addListener(this);
|
||||
this.producer = new FrameProducer<>(audioPlayer, frames);
|
||||
this.defaultDevice = audioPlayer.getDefaultDevice();
|
||||
|
@ -244,6 +249,13 @@ public class Controller implements Initializable, FrequencyListener, Listener {
|
|||
}
|
||||
});
|
||||
|
||||
chooseFolderButton.setOnAction(e -> {
|
||||
File file = folderChooser.showDialog(stage);
|
||||
if (file != null) {
|
||||
chooseFile(file);
|
||||
}
|
||||
});
|
||||
|
||||
recordButton.setOnAction(event -> toggleRecord());
|
||||
|
||||
updateObjectRotateSpeed();
|
||||
|
@ -365,7 +377,9 @@ public class Controller implements Initializable, FrequencyListener, Listener {
|
|||
}
|
||||
}
|
||||
|
||||
private void changeFrameSet(FrameSet<List<Shape>> frames) {
|
||||
private void changeFrameSet() {
|
||||
FrameSet<List<Shape>> frames = frameSets.get(currentFrameSet);
|
||||
producer.stop();
|
||||
frames.addListener(this);
|
||||
producer = new FrameProducer<>(audioPlayer, frames);
|
||||
|
||||
|
@ -380,22 +394,41 @@ public class Controller implements Initializable, FrequencyListener, Listener {
|
|||
});
|
||||
Timeline timeline = new Timeline(kf1, kf2);
|
||||
Platform.runLater(timeline::play);
|
||||
fileLabel.setText(frameSetPaths.get(currentFrameSet));
|
||||
objTitledPane.setDisable(!ObjParser.isObjFile(frameSetPaths.get(currentFrameSet)));
|
||||
}
|
||||
|
||||
private void chooseFile(File file) {
|
||||
private void chooseFile(File chosenFile) {
|
||||
try {
|
||||
producer.stop();
|
||||
String path = file.getAbsolutePath();
|
||||
changeFrameSet(ParserFactory.getParser(path).parse());
|
||||
if (chosenFile.exists()) {
|
||||
String path = chosenFile.getAbsolutePath();
|
||||
frameSets.clear();
|
||||
frameSetPaths.clear();
|
||||
|
||||
if (file.exists() && !file.isDirectory()) {
|
||||
fileLabel.setText(path);
|
||||
objTitledPane.setDisable(!ObjParser.isObjFile(path));
|
||||
} else {
|
||||
objTitledPane.setDisable(true);
|
||||
if (chosenFile.isDirectory()) {
|
||||
for (File file : chosenFile.listFiles()) {
|
||||
try {
|
||||
frameSets.add(ParserFactory.getParser(file.getAbsolutePath()).parse());
|
||||
frameSetPaths.add(file.getAbsolutePath());
|
||||
} catch (IOException ignored) {}
|
||||
}
|
||||
} else {
|
||||
frameSets.add(ParserFactory.getParser(path).parse());
|
||||
frameSetPaths.add(path);
|
||||
}
|
||||
|
||||
currentFrameSet = 0;
|
||||
changeFrameSet();
|
||||
}
|
||||
} catch (IOException | ParserConfigurationException | SAXException ioException) {
|
||||
ioException.printStackTrace();
|
||||
|
||||
// display error to user (for debugging purposes)
|
||||
String oldPath = fileLabel.getText();
|
||||
KeyFrame kf1 = new KeyFrame(Duration.seconds(0), e -> fileLabel.setText(ioException.getMessage()));
|
||||
KeyFrame kf2 = new KeyFrame(Duration.seconds(5), e -> fileLabel.setText(oldPath));
|
||||
Timeline timeline = new Timeline(kf1, kf2);
|
||||
Platform.runLater(timeline::play);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -403,6 +436,22 @@ public class Controller implements Initializable, FrequencyListener, Listener {
|
|||
this.stage = stage;
|
||||
}
|
||||
|
||||
public void nextFrameSet() {
|
||||
currentFrameSet++;
|
||||
if (currentFrameSet >= frameSets.size()) {
|
||||
currentFrameSet = 0;
|
||||
}
|
||||
changeFrameSet();
|
||||
}
|
||||
|
||||
public void previousFrameSet() {
|
||||
currentFrameSet--;
|
||||
if (currentFrameSet < 0) {
|
||||
currentFrameSet = frameSets.size() - 1;
|
||||
}
|
||||
changeFrameSet();
|
||||
}
|
||||
|
||||
protected boolean mouseRotate() {
|
||||
return rotateCheckBox.isSelected();
|
||||
}
|
||||
|
|
|
@ -33,20 +33,30 @@ public class Gui extends Application {
|
|||
stage.setTitle("osci-render");
|
||||
Scene scene = new Scene(root);
|
||||
scene.getStylesheets().add(getClass().getResource("/css/main.css").toExternalForm());
|
||||
|
||||
scene.addEventHandler(KeyEvent.KEY_PRESSED, (event -> {
|
||||
switch (event.getCode()) {
|
||||
case K -> controller.previousFrameSet();
|
||||
case J -> controller.nextFrameSet();
|
||||
}
|
||||
}));
|
||||
|
||||
scene.addEventFilter(MouseEvent.MOUSE_MOVED, event -> {
|
||||
if (controller.mouseRotate()) {
|
||||
controller.setObjRotate(new Vector3(
|
||||
3 * Math.PI * (event.getSceneY() / scene.getHeight()),
|
||||
3 * Math.PI * (event.getSceneX() / scene.getWidth()),
|
||||
3 * Math.PI * (event.getSceneX() / scene.getWidth()),
|
||||
0
|
||||
));
|
||||
}
|
||||
});
|
||||
|
||||
scene.addEventHandler(KeyEvent.KEY_PRESSED, t -> {
|
||||
if (t.getCode() == KeyCode.ESCAPE) {
|
||||
controller.disableMouseRotate();
|
||||
}
|
||||
});
|
||||
|
||||
stage.setScene(scene);
|
||||
stage.setResizable(false);
|
||||
|
||||
|
|
|
@ -10,18 +10,19 @@
|
|||
<?import javafx.scene.layout.AnchorPane?>
|
||||
<?import javafx.scene.text.Font?>
|
||||
|
||||
<AnchorPane prefHeight="498.0" prefWidth="837.0" xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1">
|
||||
<AnchorPane id="control-pane" layoutX="422.0" layoutY="10.0" prefHeight="195.0" prefWidth="402.0">
|
||||
<AnchorPane prefHeight="539.0" prefWidth="837.0" xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1">
|
||||
<AnchorPane id="control-pane" layoutX="423.0" layoutY="14.0" prefHeight="232.0" prefWidth="402.0">
|
||||
<children>
|
||||
<Button fx:id="chooseFileButton" layoutX="14.0" layoutY="53.0" mnemonicParsing="false" prefHeight="26.0" prefWidth="114.0" text="Choose File" />
|
||||
<Label fx:id="fileLabel" layoutX="146.0" layoutY="58.0" maxWidth="270.0" prefHeight="18.0" prefWidth="246.0" text="cube.obj" />
|
||||
<Button fx:id="recordButton" layoutX="14.0" layoutY="93.0" mnemonicParsing="false" prefHeight="26.0" prefWidth="114.0" text="Record" />
|
||||
<Label fx:id="recordLabel" layoutX="146.0" layoutY="98.0" maxWidth="270.0" prefHeight="18.0" prefWidth="245.0" />
|
||||
<Label id="frequency" fx:id="frequencyLabel" layoutX="14.0" layoutY="131.0" prefHeight="58.0" prefWidth="376.0" text="L/R Frequency: " />
|
||||
<Label fx:id="fileLabel" layoutX="144.0" layoutY="57.0" maxWidth="270.0" prefHeight="18.0" prefWidth="246.0" text="cube.obj" />
|
||||
<Button fx:id="recordButton" layoutX="14.0" layoutY="129.0" mnemonicParsing="false" prefHeight="26.0" prefWidth="114.0" text="Record" />
|
||||
<Label fx:id="recordLabel" layoutX="145.0" layoutY="140.0" maxWidth="270.0" prefHeight="18.0" prefWidth="245.0" />
|
||||
<Label id="frequency" fx:id="frequencyLabel" layoutX="13.0" layoutY="166.0" prefHeight="58.0" prefWidth="376.0" text="L/R Frequency: " />
|
||||
<ComboBox fx:id="deviceComboBox" layoutX="14.0" layoutY="11.0" prefHeight="26.0" prefWidth="376.0" />
|
||||
<Button fx:id="chooseFolderButton" layoutX="14.0" layoutY="91.0" mnemonicParsing="false" prefHeight="26.0" prefWidth="114.0" text="Choose Folder" />
|
||||
</children>
|
||||
</AnchorPane>
|
||||
<TitledPane animated="false" collapsible="false" layoutX="422.0" layoutY="213.0" prefHeight="272.0" prefWidth="402.0" text="Effects">
|
||||
<TitledPane animated="false" collapsible="false" layoutX="423.0" layoutY="252.0" prefHeight="272.0" prefWidth="402.0" text="Effects">
|
||||
<content>
|
||||
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="246.0" prefWidth="442.0">
|
||||
<children>
|
||||
|
@ -39,7 +40,7 @@
|
|||
</AnchorPane>
|
||||
</content>
|
||||
</TitledPane>
|
||||
<TitledPane fx:id="objTitledPane" animated="false" collapsible="false" layoutX="11.0" layoutY="275.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="12.0" layoutY="280.0" maxHeight="-Infinity" maxWidth="-Infinity" prefHeight="244.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="38.0" prefWidth="270.0" showTickLabels="true" showTickMarks="true" value="1.0" />
|
||||
<Label layoutX="30.0" layoutY="14.0" text="Focal length" />
|
||||
|
@ -55,7 +56,7 @@
|
|||
<CheckBox fx:id="rotateCheckBox" layoutX="90.0" layoutY="147.0" mnemonicParsing="false" text="Rotate with Mouse (Esc to disable)" />
|
||||
</AnchorPane>
|
||||
</TitledPane>
|
||||
<TitledPane collapsible="false" layoutX="11.0" layoutY="9.0" prefHeight="257.0" prefWidth="402.0" text="Image settings">
|
||||
<TitledPane collapsible="false" layoutX="12.0" layoutY="14.0" prefHeight="257.0" prefWidth="402.0" text="Image settings">
|
||||
<content>
|
||||
<AnchorPane minHeight="0.0" minWidth="0.0">
|
||||
<Slider fx:id="rotateSpeedSlider" blockIncrement="0.05" layoutX="116.0" layoutY="90.0" majorTickUnit="1.0" max="10.0" prefHeight="38.0" prefWidth="270.0" showTickLabels="true" showTickMarks="true" />
|
||||
|
|
Ładowanie…
Reference in New Issue