Skip to content

Commit

Permalink
Add project files
Browse files Browse the repository at this point in the history
  • Loading branch information
jameshball committed Dec 30, 2022
1 parent 8d9947d commit c0b7ed8
Show file tree
Hide file tree
Showing 17 changed files with 346 additions and 30 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
/logs
osci-render.iml
*.osci
*.patchable
*.log
.DS_Store

Expand Down
79 changes: 67 additions & 12 deletions src/main/java/sh/ball/graph/GraphController.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@

import static sh.ball.gui.Gui.logger;

import java.io.File;
import java.io.FileInputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import javafx.application.Platform;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleObjectProperty;
import javafx.geometry.Bounds;
Expand All @@ -26,9 +29,10 @@

import java.util.ArrayList;
import java.util.List;
import sh.ball.graph.blocks.BlockInput;
import sh.ball.graph.blocks.BlockConnection;
import sh.ball.graph.blocks.types.AddBlock;
import sh.ball.graph.blocks.types.MultiplyBlock;
import sh.ball.graph.blocks.types.ReturnBlock;
import sh.ball.graph.blocks.types.SineBlock;
import sh.ball.graph.blocks.types.SliderBlock;
import sh.ball.graph.blocks.types.SpinnerBlock;
Expand All @@ -37,10 +41,9 @@ public class GraphController {

private final Group group;
private final List<Block> blocks = new ArrayList<>();
private final List<Node> nodes = new ArrayList<>();
private final AudioEngine audioEngine;
private final Property<KeyEvent> keyEventProperty = new SimpleObjectProperty<>();
private final Map<Node, Node> inputToCableMap = new HashMap<>();
private final Map<BlockConnection, Node> inputToCableMap = new HashMap<>();

private double offsetX;
private double offsetY;
Expand All @@ -49,7 +52,6 @@ public class GraphController {
private Block returnBlock;
private int outputIndex = -1;


public GraphController(Group group, ContextMenu contextMenu, AudioEngine audioEngine) {
Menu newBlock = new Menu("New Block");

Expand Down Expand Up @@ -80,15 +82,41 @@ public GraphController(Group group, ContextMenu contextMenu, AudioEngine audioEn
this.group = group;
}

public void setReturnBlock(Block block) {
this.returnBlock = block;
addBlock(block);
public void addConnection(BlockConnection connection) {
Line line = new Line();
int sourceIndex = connection.sourceIndex();
int destIndex = connection.destIndex();
List<Node> sourceOutputs = connection.source().getOutputNodes();
List<Node> destInputs = connection.dest().getInputNodes();

Node output = sourceOutputs.get(sourceIndex);
Node input = destInputs.get(destIndex);
Bounds inputBounds = input.getBoundsInParent();
connection.dest().setInput(connection);

output.boundsInParentProperty().addListener((observable, oldValue, outputBounds) -> {
line.startXProperty().bind(output.getParent().layoutXProperty().add(outputBounds.getCenterX()));
line.startYProperty().bind(output.getParent().layoutYProperty().add(outputBounds.getCenterY()));
line.endXProperty().bind(input.getParent().layoutXProperty().add(inputBounds.getCenterX()));
line.endYProperty().bind(input.getParent().layoutYProperty().add(inputBounds.getCenterY()));
});

group.getChildren().add(line);
inputToCableMap.put(connection, line);
}

public void addBlock(Block block) {
if (block.type().equals("return")) {
returnBlock = block;
}

blocks.add(block);
Node node = block.getNode();
nodes.add(node);

if (block.getOutputNodes().size() > 0) {
Node output = block.getOutputNodes().get(0);
Bounds outputBounds = output.getBoundsInParent();
}
group.getChildren().add(node);

node.addEventFilter(MouseEvent.MOUSE_PRESSED, event -> {
Expand Down Expand Up @@ -161,18 +189,19 @@ public void addBlock(Block block) {
Block startBlock = blocks.get(blockIndex);
Block endBlock = blocks.get(i);
if (endBlock.currentInputs() < endBlock.totalInputs()) {
endBlock.setInput(new BlockInput(startBlock, outputIndex), j);
BlockConnection blockConnection = new BlockConnection(startBlock, outputIndex, endBlock, j);
endBlock.setInput(blockConnection);
connected = true;
inputToCableMap.put(input, cable);
inputToCableMap.put(blockConnection, cable);
cable = null;
blockIndex = -1;
outputIndex = -1;
}
} else {
// remove the input from the block
Block endBlock = blocks.get(i);
endBlock.removeInput(j);
Node cable = inputToCableMap.remove(input);
BlockConnection blockConnection = endBlock.removeInput(j);
Node cable = inputToCableMap.remove(blockConnection);
group.getChildren().remove(cable);
}

Expand All @@ -191,6 +220,32 @@ public void addBlock(Block block) {
});
}

public void save() {
GraphState graphState = new GraphState(blocks, new ArrayList<>(inputToCableMap.keySet()));
try {
graphState.save(new File("project.patchable"));
} catch (Exception e) {
throw new RuntimeException(e);
}
}

public void load() {
try {
group.getChildren().clear();
blocks.clear();
inputToCableMap.clear();
cable = null;
blockIndex = -1;
outputIndex = -1;

GraphState graphState = GraphState.load(new FileInputStream("project.patchable"));
graphState.blocks().forEach(this::addBlock);
graphState.connections().forEach(this::addConnection);
} catch (Exception e) {
throw new RuntimeException(e);
}
}

public void start() throws Exception {
AudioDevice device = audioEngine.getDefaultOutputDevice();
blocks.forEach(block -> block.audioDeviceChanged(device));
Expand Down
119 changes: 119 additions & 0 deletions src/main/java/sh/ball/graph/GraphState.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package sh.ball.graph;


import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import sh.ball.graph.blocks.Block;
import sh.ball.graph.blocks.BlockFactory;
import sh.ball.graph.blocks.BlockConnection;

public record GraphState(List<Block> blocks, List<BlockConnection> connections) {

public void save(File file) throws Exception {
DocumentBuilderFactory documentFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentFactory.newDocumentBuilder();
Document document = documentBuilder.newDocument();

Element root = document.createElement("project");
document.appendChild(root);

Element blocksElement = document.createElement("blocks");
for (int i = 0; i < blocks.size(); i++) {
Block block = blocks.get(i);
Element blockElement = document.createElement("block");
List<Element> blockElements = block.save(document);

for (Element element : blockElements) {
blockElement.appendChild(element);
}

blockElement.setAttribute("type", block.type());
blockElement.setAttribute("id", String.valueOf(i));
blockElement.setAttribute("layoutX", String.valueOf(block.getNode().getLayoutX()));
blockElement.setAttribute("layoutY", String.valueOf(block.getNode().getLayoutY()));
blocksElement.appendChild(blockElement);
}

root.appendChild(blocksElement);

Element connectionsElement = document.createElement("connections");
for (BlockConnection connection : connections) {
Element connectionElement = document.createElement("connection");
connectionElement.setAttribute("source", String.valueOf(blocks.indexOf(connection.source())));
connectionElement.setAttribute("sourceIndex", String.valueOf(connection.sourceIndex()));
connectionElement.setAttribute("dest", String.valueOf(blocks.indexOf(connection.dest())));
connectionElement.setAttribute("destIndex", String.valueOf(connection.destIndex()));
connectionsElement.appendChild(connectionElement);
}

root.appendChild(connectionsElement);

TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
DOMSource domSource = new DOMSource(document);
StreamResult streamResult = new StreamResult(file);

transformer.transform(domSource, streamResult);
}

public static GraphState load(InputStream inputStream) throws Exception {
List<Block> blocks = new ArrayList<>();
List<BlockConnection> blockConnections = new ArrayList<>();

DocumentBuilderFactory documentFactory = DocumentBuilderFactory.newInstance();
documentFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
DocumentBuilder documentBuilder = documentFactory.newDocumentBuilder();

Document document = documentBuilder.parse(inputStream);

document.getDocumentElement().normalize();

Element root = document.getDocumentElement();
Element blocksElement = (Element) root.getElementsByTagName("blocks").item(0);
Map<Integer, Block> blockMap = new HashMap<>();

for (int i = 0; i < blocksElement.getElementsByTagName("block").getLength(); i++) {
Element blockElement = (Element) blocksElement.getElementsByTagName("block").item(i);
String type = blockElement.getAttribute("type");
int id = Integer.parseInt(blockElement.getAttribute("id"));
double layoutX = Double.parseDouble(blockElement.getAttribute("layoutX"));
double layoutY = Double.parseDouble(blockElement.getAttribute("layoutY"));

Block block = BlockFactory.create(type);
block.load(blockElement);
blockMap.put(id, block);
blocks.add(block);
block.getNode().setLayoutX(layoutX);
block.getNode().setLayoutY(layoutY);
}

Element connectionsElement = (Element) root.getElementsByTagName("connections").item(0);

for (int i = 0; i < connectionsElement.getElementsByTagName("connection").getLength(); i++) {
Element connectionElement = (Element) connectionsElement.getElementsByTagName("connection")
.item(i);
int source = Integer.parseInt(connectionElement.getAttribute("source"));
int sourceIndex = Integer.parseInt(connectionElement.getAttribute("sourceIndex"));
int dest = Integer.parseInt(connectionElement.getAttribute("dest"));
int destIndex = Integer.parseInt(connectionElement.getAttribute("destIndex"));
blockConnections.add(
new BlockConnection(blockMap.get(source), sourceIndex, blockMap.get(dest), destIndex));
}

return new GraphState(blocks, blockConnections);
}

}
10 changes: 9 additions & 1 deletion src/main/java/sh/ball/graph/GraphView.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public GraphView(ContextMenu contextMenu, AudioEngine audioEngine) {
pane.setPrefWidth(300);
pane.setPrefHeight(300);

controller.setReturnBlock(new ReturnBlock());
controller.addBlock(new ReturnBlock());
controller.addBlock(new SineBlock());
controller.addBlock(new SliderBlock(0, 1000, 440));

Expand All @@ -48,4 +48,12 @@ public void setKeyEventProperty(KeyEvent event) {
public Parent getParent() {
return pane;
}

public void save() {
controller.save();
}

public void load() {
controller.load();
}
}
11 changes: 8 additions & 3 deletions src/main/java/sh/ball/graph/blocks/Block.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,22 @@
import javafx.scene.Node;

import java.util.List;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import sh.ball.audio.engine.AudioDeviceListener;

public interface Block extends AudioDeviceListener {
double process(int sampleNumber, int index);
List<BlockInput> getInputs();
void setInput(BlockInput input, int index);
void removeInput(int index);
List<BlockConnection> getInputs();
void setInput(BlockConnection input);
BlockConnection removeInput(int index);
int totalInputs();
int totalOutputs();
int currentInputs();
Node getNode();
List<Node> getInputNodes();
List<Node> getOutputNodes();
List<Element> save(Document document);
void load(Element root);
String type();
}
3 changes: 3 additions & 0 deletions src/main/java/sh/ball/graph/blocks/BlockConnection.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package sh.ball.graph.blocks;

public record BlockConnection(Block source, int sourceIndex, Block dest, int destIndex) {}
1 change: 1 addition & 0 deletions src/main/java/sh/ball/graph/blocks/BlockDesigner.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public static List<Node> outputNodes(int totalOutputs) {
output.setTranslateX(1.25 * INPUT_WIDTH * i - width / 2);
StackPane.setAlignment(output, Pos.BOTTOM_CENTER);
nodes.add(output);

}
return nodes;
}
Expand Down
24 changes: 24 additions & 0 deletions src/main/java/sh/ball/graph/blocks/BlockFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package sh.ball.graph.blocks;

import sh.ball.graph.blocks.types.AddBlock;
import sh.ball.graph.blocks.types.MultiplyBlock;
import sh.ball.graph.blocks.types.ReturnBlock;
import sh.ball.graph.blocks.types.SineBlock;
import sh.ball.graph.blocks.types.SliderBlock;
import sh.ball.graph.blocks.types.SpinnerBlock;

public class BlockFactory {

public static Block create(String type) {
return switch (type) {
case "add" -> new AddBlock();
case "multiply" -> new MultiplyBlock();
case "return" -> new ReturnBlock();
case "sine" -> new SineBlock();
case "slider" -> new SliderBlock(0, 1, 0);
case "spinner" -> new SpinnerBlock(0, 1, 0, 0.01);
default -> null;
};
}

}
3 changes: 0 additions & 3 deletions src/main/java/sh/ball/graph/blocks/BlockInput.java

This file was deleted.

5 changes: 5 additions & 0 deletions src/main/java/sh/ball/graph/blocks/types/AddBlock.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,9 @@ public class AddBlock extends BasicBlock {
public AddBlock() {
super((inputs, outputs) -> outputs[0] = inputs[0] + inputs[1], 2, 1, Paint.valueOf("#ff0000"), "Add");
}

@Override
public String type() {
return "add";
}
}
Loading

0 comments on commit c0b7ed8

Please sign in to comment.