From ade45fa9e60b22787e1a49bdd1539ab6796fc83f Mon Sep 17 00:00:00 2001 From: James Ball Date: Mon, 9 May 2022 21:18:42 +0100 Subject: [PATCH] Improve stability with js oscilloscope --- pom.xml | 10 +++ src/main/java/sh/ball/gui/Gui.java | 1 + .../ball/gui/controller/MainController.java | 40 +++++++-- .../oscilloscope/ByteWebSocketServer.java | 56 +++++++++++++ .../ball/oscilloscope/OscilloscopeServer.java | 83 ------------------- 5 files changed, 100 insertions(+), 90 deletions(-) create mode 100644 src/main/java/sh/ball/oscilloscope/ByteWebSocketServer.java delete mode 100644 src/main/java/sh/ball/oscilloscope/OscilloscopeServer.java diff --git a/pom.xml b/pom.xml index b62a17dd..47e86251 100644 --- a/pom.xml +++ b/pom.xml @@ -181,6 +181,16 @@ Java-WebSocket 1.5.3 + + org.slf4j + slf4j-api + 1.7.36 + + + org.slf4j + slf4j-simple + 1.7.36 + diff --git a/src/main/java/sh/ball/gui/Gui.java b/src/main/java/sh/ball/gui/Gui.java index 07af99d1..65848d5c 100644 --- a/src/main/java/sh/ball/gui/Gui.java +++ b/src/main/java/sh/ball/gui/Gui.java @@ -90,6 +90,7 @@ public class Gui extends Application { stage.show(); stage.setOnCloseRequest(t -> { + controller.shutdown(); Platform.exit(); System.exit(0); }); diff --git a/src/main/java/sh/ball/gui/controller/MainController.java b/src/main/java/sh/ball/gui/controller/MainController.java index e7f77340..8d74e489 100644 --- a/src/main/java/sh/ball/gui/controller/MainController.java +++ b/src/main/java/sh/ball/gui/controller/MainController.java @@ -51,7 +51,7 @@ import sh.ball.audio.midi.MidiNote; import sh.ball.engine.ObjectServer; import sh.ball.engine.ObjectSet; import sh.ball.gui.Gui; -import sh.ball.oscilloscope.OscilloscopeServer; +import sh.ball.oscilloscope.ByteWebSocketServer; import sh.ball.parser.obj.ObjFrameSettings; import sh.ball.parser.obj.ObjParser; import sh.ball.parser.ParserFactory; @@ -73,7 +73,7 @@ public class MainController implements Initializable, FrequencyListener, MidiLis private String openProjectPath; private final FileChooser wavFileChooser = new FileChooser(); - private ObjectServer server; + private ObjectServer objectServer; // audio private int sampleRate; @@ -106,6 +106,13 @@ public class MainController implements Initializable, FrequencyListener, MidiLis private final FileChooser osciFileChooser = new FileChooser(); private Stage stage; + // software oscilloscope + private static final int SOSCI_NUM_VERTICES = 4096; + private static final int SOSCI_VERTEX_SIZE = 2; + private static final int FRAME_SIZE = 2; + private byte[] buffer; + private ByteWebSocketServer webSocketServer; + @FXML private EffectsController effectsController; @FXML @@ -206,6 +213,10 @@ public class MainController implements Initializable, FrequencyListener, MidiLis channelClosestToZero.put(slider, closestToZero); } + public void shutdown() { + webSocketServer.shutdown(); + } + // alternates between recording and not recording when called. // If it is a non-timed recording, it is saved when this is called and // recording is stopped. If it is a time recording, this function will cancel @@ -457,15 +468,30 @@ public class MainController implements Initializable, FrequencyListener, MidiLis } } - server = new ObjectServer(this::enableObjectServerRendering, this::disableObjectServerRendering); - new Thread(server).start(); - new OscilloscopeServer<>(audioPlayer, 2).start(); + objectServer = new ObjectServer(this::enableObjectServerRendering, this::disableObjectServerRendering); + new Thread(objectServer).start(); + + webSocketServer = new ByteWebSocketServer(); + webSocketServer.start(); + this.buffer = new byte[FRAME_SIZE * SOSCI_NUM_VERTICES * SOSCI_VERTEX_SIZE]; + new Thread(() -> sendAudioDataToWebSocket(webSocketServer)).start(); + } + + private void sendAudioDataToWebSocket(ByteWebSocketServer server) { + while (true) { + try { + audioPlayer.read(buffer); + server.send(buffer); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } } private void enableObjectServerRendering() { Platform.runLater(() -> { objectServerRendering = true; - ObjectSet set = server.getObjectSet(); + ObjectSet set = objectServer.getObjectSet(); frameSources.forEach(FrameSource::disable); set.enable(); @@ -483,7 +509,7 @@ public class MainController implements Initializable, FrequencyListener, MidiLis private void disableObjectServerRendering() { Platform.runLater(() -> { - server.getObjectSet().disable(); + objectServer.getObjectSet().disable(); objectServerRendering = false; changeFrameSource(currentFrameSource); }); diff --git a/src/main/java/sh/ball/oscilloscope/ByteWebSocketServer.java b/src/main/java/sh/ball/oscilloscope/ByteWebSocketServer.java new file mode 100644 index 00000000..11130d46 --- /dev/null +++ b/src/main/java/sh/ball/oscilloscope/ByteWebSocketServer.java @@ -0,0 +1,56 @@ +package sh.ball.oscilloscope; + +import org.java_websocket.WebSocket; +import org.java_websocket.handshake.ClientHandshake; +import org.java_websocket.server.WebSocketServer; + +import java.net.InetSocketAddress; +import java.util.HashSet; +import java.util.Set; + +public class ByteWebSocketServer extends WebSocketServer { + + private static final int TCP_PORT = 42988; + + private final Set conns; + + public ByteWebSocketServer() { + super(new InetSocketAddress(TCP_PORT)); + this.conns = new HashSet<>(); + } + + public synchronized void send(byte[] bytes) { + for (WebSocket sock : conns) { + sock.send(bytes); + } + } + + @Override + public synchronized void onOpen(WebSocket conn, ClientHandshake handshake) { + conns.add(conn); + } + + @Override + public synchronized void onClose(WebSocket conn, int code, String reason, boolean remote) { + conns.remove(conn); + } + + @Override + public synchronized void onMessage(WebSocket conn, String message) {} + + @Override + public synchronized void onError(WebSocket conn, Exception ex) { + ex.printStackTrace(); + if (conn != null) { + conns.remove(conn); + } + } + + public synchronized void shutdown() { + conns.forEach(WebSocket::close); + conns.clear(); + } + + @Override + public void onStart() {} +} \ No newline at end of file diff --git a/src/main/java/sh/ball/oscilloscope/OscilloscopeServer.java b/src/main/java/sh/ball/oscilloscope/OscilloscopeServer.java deleted file mode 100644 index a27fc9fe..00000000 --- a/src/main/java/sh/ball/oscilloscope/OscilloscopeServer.java +++ /dev/null @@ -1,83 +0,0 @@ -package sh.ball.oscilloscope; - -import org.java_websocket.WebSocket; -import org.java_websocket.handshake.ClientHandshake; -import org.java_websocket.server.WebSocketServer; -import sh.ball.audio.AudioPlayer; - -import java.net.InetSocketAddress; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.ShortBuffer; -import java.util.HashSet; -import java.util.Set; - -public class OscilloscopeServer extends WebSocketServer { - - private static final int TCP_PORT = 4444; - private static final int NUM_VERTICES = 2048; - private static final int VERTEX_SIZE = 2; - - private final Set conns; - private final AudioPlayer audioPlayer; - private final int frameSize; - private final byte[] buffer; - - public OscilloscopeServer(AudioPlayer audioPlayer, int frameSize) { - super(new InetSocketAddress(TCP_PORT)); - this.conns = new HashSet<>(); - this.audioPlayer = audioPlayer; - this.frameSize = frameSize; - this.buffer = new byte[frameSize * NUM_VERTICES * VERTEX_SIZE]; - } - - public void send(byte[] bytes) { - for (WebSocket sock : conns) { - sock.send(bytes); - } - } - - @Override - public void onOpen(WebSocket conn, ClientHandshake handshake) { - conns.add(conn); - System.out.println("New connection from " + conn.getRemoteSocketAddress().getAddress().getHostAddress()); - } - - @Override - public void onClose(WebSocket conn, int code, String reason, boolean remote) { - conns.remove(conn); - System.out.println("Closed connection to " + conn.getRemoteSocketAddress().getAddress().getHostAddress()); - } - - @Override - public void onMessage(WebSocket conn, String message) { - System.out.println("Message from client: " + message); - for (WebSocket sock : conns) { - sock.send(message); - } - } - - @Override - public void onError(WebSocket conn, Exception ex) { - //ex.printStackTrace(); - if (conn != null) { - conns.remove(conn); - // do some thing if required - } - System.out.println("ERROR from " + conn.getRemoteSocketAddress().getAddress().getHostAddress()); - } - - @Override - public void onStart() { - new Thread(() -> { - while (true) { - try { - audioPlayer.read(buffer); - send(buffer); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - }).start(); - } -} \ No newline at end of file