kopia lustrzana https://github.com/jameshball/osci-render
Merge pull request #32 from jameshball/folder-load
Implement loading a whole folder of filespull/35/head
commit
fa9f84b19b
2
pom.xml
2
pom.xml
|
@ -6,7 +6,7 @@
|
|||
|
||||
<groupId>sh.ball</groupId>
|
||||
<artifactId>osci-render</artifactId>
|
||||
<version>1.8.2</version>
|
||||
<version>1.9.0</version>
|
||||
|
||||
<name>osci-render</name>
|
||||
|
||||
|
|
|
@ -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,8 +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 String lastVisitedDirectory;
|
||||
|
||||
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;
|
||||
|
||||
|
@ -73,8 +75,12 @@ public class Controller implements Initializable, FrequencyListener, Listener {
|
|||
@FXML
|
||||
private Button chooseFileButton;
|
||||
@FXML
|
||||
private Button chooseFolderButton;
|
||||
@FXML
|
||||
private Label fileLabel;
|
||||
@FXML
|
||||
private Label jkLabel;
|
||||
@FXML
|
||||
private Button recordButton;
|
||||
@FXML
|
||||
private Label recordLabel;
|
||||
|
@ -130,6 +136,9 @@ 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);
|
||||
frameSetPaths.add("cube.obj");
|
||||
currentFrameSet = 0;
|
||||
frames.addListener(this);
|
||||
this.producer = new FrameProducer<>(audioPlayer, frames);
|
||||
this.defaultDevice = audioPlayer.getDefaultDevice();
|
||||
|
@ -241,6 +250,15 @@ public class Controller implements Initializable, FrequencyListener, Listener {
|
|||
File file = fileChooser.showOpenDialog(stage);
|
||||
if (file != null) {
|
||||
chooseFile(file);
|
||||
updateLastVisitedDirectory(new File(file.getParent()));
|
||||
}
|
||||
});
|
||||
|
||||
chooseFolderButton.setOnAction(e -> {
|
||||
File file = folderChooser.showDialog(stage);
|
||||
if (file != null) {
|
||||
chooseFile(file);
|
||||
updateLastVisitedDirectory(file);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -268,6 +286,13 @@ public class Controller implements Initializable, FrequencyListener, Listener {
|
|||
});
|
||||
}
|
||||
|
||||
private void updateLastVisitedDirectory(File file) {
|
||||
lastVisitedDirectory = file != null ? file.getAbsolutePath() : System.getProperty("user.home");
|
||||
File dir = new File(lastVisitedDirectory);
|
||||
fileChooser.setInitialDirectory(dir);
|
||||
folderChooser.setInitialDirectory(dir);
|
||||
}
|
||||
|
||||
private void switchAudioDevice(AudioDevice device) {
|
||||
try {
|
||||
audioPlayer.reset();
|
||||
|
@ -365,34 +390,59 @@ public class Controller implements Initializable, FrequencyListener, Listener {
|
|||
}
|
||||
}
|
||||
|
||||
private void chooseFile(File file) {
|
||||
private void changeFrameSet() {
|
||||
FrameSet<List<Shape>> frames = frameSets.get(currentFrameSet);
|
||||
producer.stop();
|
||||
frames.addListener(this);
|
||||
producer = new FrameProducer<>(audioPlayer, frames);
|
||||
|
||||
updateObjectRotateSpeed();
|
||||
updateFocalLength();
|
||||
executor.submit(producer);
|
||||
|
||||
KeyFrame kf1 = new KeyFrame(Duration.seconds(0), e -> wobbleEffect.setVolume(0));
|
||||
KeyFrame kf2 = new KeyFrame(Duration.seconds(1), e -> {
|
||||
wobbleEffect.update();
|
||||
wobbleEffect.setVolume(wobbleSlider.getValue());
|
||||
});
|
||||
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 chosenFile) {
|
||||
try {
|
||||
producer.stop();
|
||||
String path = file.getAbsolutePath();
|
||||
FrameSet<List<Shape>> frames = ParserFactory.getParser(path).parse();
|
||||
frames.addListener(this);
|
||||
producer = new FrameProducer<>(audioPlayer, frames);
|
||||
if (chosenFile.exists()) {
|
||||
frameSets.clear();
|
||||
frameSetPaths.clear();
|
||||
|
||||
updateObjectRotateSpeed();
|
||||
updateFocalLength();
|
||||
executor.submit(producer);
|
||||
if (chosenFile.isDirectory()) {
|
||||
jkLabel.setVisible(true);
|
||||
for (File file : chosenFile.listFiles()) {
|
||||
try {
|
||||
frameSets.add(ParserFactory.getParser(file.getAbsolutePath()).parse());
|
||||
frameSetPaths.add(file.getName());
|
||||
} catch (IOException ignored) {}
|
||||
}
|
||||
} else {
|
||||
jkLabel.setVisible(false);
|
||||
frameSets.add(ParserFactory.getParser(chosenFile.getAbsolutePath()).parse());
|
||||
frameSetPaths.add(chosenFile.getName());
|
||||
}
|
||||
|
||||
KeyFrame kf1 = new KeyFrame(Duration.seconds(0), e -> wobbleEffect.setVolume(0));
|
||||
KeyFrame kf2 = new KeyFrame(Duration.seconds(1), e -> {
|
||||
wobbleEffect.update();
|
||||
wobbleEffect.setVolume(wobbleSlider.getValue());
|
||||
});
|
||||
Timeline timeline = new Timeline(kf1, kf2);
|
||||
Platform.runLater(timeline::play);
|
||||
|
||||
if (file.exists() && !file.isDirectory()) {
|
||||
fileLabel.setText(path);
|
||||
objTitledPane.setDisable(!ObjParser.isObjFile(path));
|
||||
} else {
|
||||
objTitledPane.setDisable(true);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -400,6 +450,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();
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@ import javafx.scene.input.MouseEvent;
|
|||
import javafx.stage.Stage;
|
||||
import sh.ball.audio.ShapeAudioPlayer;
|
||||
import sh.ball.audio.engine.ConglomerateAudioEngine;
|
||||
import sh.ball.audio.engine.JavaAudioEngine;
|
||||
import sh.ball.engine.Vector3;
|
||||
|
||||
import java.util.Objects;
|
||||
|
@ -33,20 +32,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 J -> controller.nextFrameSet();
|
||||
case K -> controller.previousFrameSet();
|
||||
}
|
||||
}));
|
||||
|
||||
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,20 @@
|
|||
<?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="144.0" layoutY="133.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" />
|
||||
<Label fx:id="jkLabel" layoutX="143.0" layoutY="95.0" maxWidth="270.0" prefHeight="18.0" prefWidth="246.0" text="Use j and k to cycle between files" visible="false" />
|
||||
</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 +41,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 +57,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