diff --git a/src/main/java/sh/ball/audio/AudioPlayer.java b/src/main/java/sh/ball/audio/AudioPlayer.java index 8e77ea8..f34ad3d 100644 --- a/src/main/java/sh/ball/audio/AudioPlayer.java +++ b/src/main/java/sh/ball/audio/AudioPlayer.java @@ -14,7 +14,7 @@ public interface AudioPlayer extends Runnable { boolean isPlaying(); - void setQuality(double quality); + void setFrequency(double quality); void addFrame(S frame); diff --git a/src/main/java/sh/ball/audio/ShapeAudioPlayer.java b/src/main/java/sh/ball/audio/ShapeAudioPlayer.java index 4142682..5f66d58 100644 --- a/src/main/java/sh/ball/audio/ShapeAudioPlayer.java +++ b/src/main/java/sh/ball/audio/ShapeAudioPlayer.java @@ -13,8 +13,6 @@ import sh.ball.shapes.Vector2; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioInputStream; -import javax.sound.sampled.LineUnavailableException; -import java.util.concurrent.locks.ReentrantLock; public class ShapeAudioPlayer implements AudioPlayer> { @@ -27,6 +25,7 @@ public class ShapeAudioPlayer implements AudioPlayer> { private static final boolean BIG_ENDIAN = false; // Stereo audio private static final int NUM_OUTPUTS = 2; + private static final double MIN_LENGTH_INCREMENT = 0.0001; private final Callable audioEngineBuilder; private final BlockingQueue> frameQueue = new ArrayBlockingQueue<>(BUFFER_SIZE); @@ -39,10 +38,11 @@ public class ShapeAudioPlayer implements AudioPlayer> { private int framesRecorded = 0; private List frame; private int currentShape = 0; - private int audioFramesDrawn = 0; + private double lengthIncrement = MIN_LENGTH_INCREMENT; + private double lengthDrawn = 0; private int count = 0; + private double frequency = 261.63; - private double weight = Shape.DEFAULT_WEIGHT; private AudioDevice device; public ShapeAudioPlayer(Callable audioEngineBuilder) throws Exception { @@ -51,29 +51,30 @@ public class ShapeAudioPlayer implements AudioPlayer> { } private Vector2 generateChannels() throws InterruptedException { - Shape shape = getCurrentShape().setWeight(weight); + Shape shape = getCurrentShape(); - double totalAudioFrames = shape.getWeight() * shape.getLength(); - double drawingProgress = totalAudioFrames == 0 ? 1 : audioFramesDrawn / totalAudioFrames; + double length = shape.getLength(); + double drawingProgress = length == 0 ? 1 : lengthDrawn / length; Vector2 nextVector = applyEffects(count, shape.nextVector(drawingProgress)); Vector2 channels = cutoff(nextVector); writeChannels((float) channels.getX(), (float) channels.getY()); - audioFramesDrawn++; + lengthDrawn += lengthIncrement; if (++count > MAX_COUNT) { count = 0; } - if (audioFramesDrawn > totalAudioFrames) { - audioFramesDrawn = 0; + if (lengthDrawn > length) { + lengthDrawn = lengthDrawn - length; currentShape++; } if (currentShape >= frame.size()) { currentShape = 0; frame = frameQueue.take(); + updateTotalAudioFrames(); } return channels; @@ -128,8 +129,8 @@ public class ShapeAudioPlayer implements AudioPlayer> { } @Override - public void setQuality(double quality) { - this.weight = quality; + public void setFrequency(double frequency) { + this.frequency = frequency; } private Shape getCurrentShape() { @@ -140,10 +141,17 @@ public class ShapeAudioPlayer implements AudioPlayer> { return frame.get(currentShape); } + private void updateTotalAudioFrames() { + double totalLength = Shape.totalLength(frame); + int sampleRate = device.sampleRate(); + lengthIncrement = Math.max(totalLength / (sampleRate / frequency), MIN_LENGTH_INCREMENT); + } + @Override public void run() { try { frame = frameQueue.take(); + updateTotalAudioFrames(); } catch (InterruptedException e) { throw new RuntimeException("Initial frame not found. Cannot continue."); } diff --git a/src/main/java/sh/ball/audio/engine/AudioEngine.java b/src/main/java/sh/ball/audio/engine/AudioEngine.java index 7cca723..1714e31 100644 --- a/src/main/java/sh/ball/audio/engine/AudioEngine.java +++ b/src/main/java/sh/ball/audio/engine/AudioEngine.java @@ -15,4 +15,6 @@ public interface AudioEngine { List devices(); AudioDevice getDefaultDevice(); + + AudioDevice currentDevice(); } diff --git a/src/main/java/sh/ball/audio/engine/ConglomerateAudioEngine.java b/src/main/java/sh/ball/audio/engine/ConglomerateAudioEngine.java index e9af3ee..1e6eb13 100644 --- a/src/main/java/sh/ball/audio/engine/ConglomerateAudioEngine.java +++ b/src/main/java/sh/ball/audio/engine/ConglomerateAudioEngine.java @@ -20,6 +20,7 @@ public class ConglomerateAudioEngine implements AudioEngine { private static AudioDevice javaDevice; private boolean playing = false; + private AudioDevice device; @Override public boolean isPlaying() { @@ -29,12 +30,14 @@ public class ConglomerateAudioEngine implements AudioEngine { @Override public void play(Callable channelGenerator, AudioDevice device) throws Exception { playing = true; + this.device = device; if (xtDevices.contains(device)) { xtEngine.play(channelGenerator, device); } else { javaEngine.play(channelGenerator, javaDevice); } playing = false; + this.device = null; } @Override @@ -66,4 +69,9 @@ public class ConglomerateAudioEngine implements AudioEngine { public AudioDevice getDefaultDevice() { return javaEngine.getDefaultDevice(); } + + @Override + public AudioDevice currentDevice() { + return device; + } } diff --git a/src/main/java/sh/ball/audio/engine/JavaAudioEngine.java b/src/main/java/sh/ball/audio/engine/JavaAudioEngine.java index 0c00895..78b45f4 100644 --- a/src/main/java/sh/ball/audio/engine/JavaAudioEngine.java +++ b/src/main/java/sh/ball/audio/engine/JavaAudioEngine.java @@ -22,6 +22,7 @@ public class JavaAudioEngine implements AudioEngine { private volatile boolean stopped = false; private SourceDataLine source; + private AudioDevice device; @Override public boolean isPlaying() { @@ -31,6 +32,7 @@ public class JavaAudioEngine implements AudioEngine { @Override public void play(Callable channelGenerator, AudioDevice device) throws Exception { this.stopped = false; + this.device = device; AudioFormat format = new AudioFormat((float) device.sampleRate(), BIT_DEPTH, NUM_CHANNELS, SIGNED_SAMPLE, BIG_ENDIAN); @@ -71,6 +73,7 @@ public class JavaAudioEngine implements AudioEngine { } } source.stop(); + this.device = null; } @Override @@ -87,4 +90,9 @@ public class JavaAudioEngine implements AudioEngine { public AudioDevice getDefaultDevice() { return new DefaultAudioDevice("default", "default", DEFAULT_SAMPLE_RATE, AudioSample.INT16); } + + @Override + public AudioDevice currentDevice() { + return device; + } } diff --git a/src/main/java/sh/ball/audio/engine/XtAudioEngine.java b/src/main/java/sh/ball/audio/engine/XtAudioEngine.java index 25a02a7..ea5565a 100644 --- a/src/main/java/sh/ball/audio/engine/XtAudioEngine.java +++ b/src/main/java/sh/ball/audio/engine/XtAudioEngine.java @@ -120,6 +120,7 @@ public class XtAudioEngine implements AudioEngine { } } playing = false; + this.device = null; } @Override @@ -196,6 +197,11 @@ public class XtAudioEngine implements AudioEngine { } } + @Override + public AudioDevice currentDevice() { + return device; + } + private XtService getService(XtPlatform platform) { XtService service = platform.getService(platform.setupToSystem(Enums.XtSetup.SYSTEM_AUDIO)); diff --git a/src/main/java/sh/ball/gui/Controller.java b/src/main/java/sh/ball/gui/Controller.java index 005e55e..4b185ce 100644 --- a/src/main/java/sh/ball/gui/Controller.java +++ b/src/main/java/sh/ball/gui/Controller.java @@ -105,9 +105,9 @@ public class Controller implements Initializable, FrequencyListener, MidiListene @FXML private TextField translationYTextField; @FXML - private Slider weightSlider; + private Slider frequencySlider; @FXML - private SVGPath weightMidi; + private SVGPath frequencyMidi; @FXML private Slider rotateSpeedSlider; @FXML @@ -192,7 +192,7 @@ public class Controller implements Initializable, FrequencyListener, MidiListene private Map initializeMidiButtonMap() { Map midiMap = new HashMap<>(); - midiMap.put(weightMidi, weightSlider); + midiMap.put(frequencyMidi, frequencySlider); midiMap.put(rotateSpeedMidi, rotateSpeedSlider); midiMap.put(translationSpeedMidi, translationSpeedSlider); midiMap.put(scaleMidi, scaleSlider); @@ -208,7 +208,7 @@ public class Controller implements Initializable, FrequencyListener, MidiListene private Map> initializeSliderMap() { return Map.of( - weightSlider, audioPlayer::setQuality, + frequencySlider, audioPlayer::setFrequency, rotateSpeedSlider, rotateEffect::setSpeed, translationSpeedSlider, translateEffect::setSpeed, scaleSlider, scaleEffect::setScale, diff --git a/src/main/java/sh/ball/shapes/CubicBezierCurve.java b/src/main/java/sh/ball/shapes/CubicBezierCurve.java index 6867e9a..782d5c1 100644 --- a/src/main/java/sh/ball/shapes/CubicBezierCurve.java +++ b/src/main/java/sh/ball/shapes/CubicBezierCurve.java @@ -7,19 +7,14 @@ public class CubicBezierCurve extends Shape { private final Vector2 p2; private final Vector2 p3; - public CubicBezierCurve(Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3, double weight) { + public CubicBezierCurve(Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3) { this.p0 = p0; this.p1 = p1; this.p2 = p2; this.p3 = p3; - this.weight = weight; this.length = new Line(p0, p3).length; } - public CubicBezierCurve(Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3) { - this(p0, p1, p2, p3, DEFAULT_WEIGHT); - } - @Override public Vector2 nextVector(double t) { return p0.scale(Math.pow(1 - t, 3)) @@ -31,7 +26,7 @@ public class CubicBezierCurve extends Shape { @Override public CubicBezierCurve rotate(double theta) { return new CubicBezierCurve(p0.rotate(theta), p1.rotate(theta), p2.rotate(theta), - p3.rotate(theta), weight); + p3.rotate(theta)); } @Override @@ -42,17 +37,12 @@ public class CubicBezierCurve extends Shape { @Override public CubicBezierCurve scale(Vector2 vector) { return new CubicBezierCurve(p0.scale(vector), p1.scale(vector), p2.scale(vector), - p3.scale(vector), weight); + p3.scale(vector)); } @Override public CubicBezierCurve translate(Vector2 vector) { return new CubicBezierCurve(p0.translate(vector), p1.translate(vector), p2.translate(vector), - p3.translate(vector), weight); - } - - @Override - public CubicBezierCurve setWeight(double weight) { - return new CubicBezierCurve(p0, p1, p2, p3, weight); + p3.translate(vector)); } } diff --git a/src/main/java/sh/ball/shapes/Ellipse.java b/src/main/java/sh/ball/shapes/Ellipse.java index 5b08893..0d6ccc8 100644 --- a/src/main/java/sh/ball/shapes/Ellipse.java +++ b/src/main/java/sh/ball/shapes/Ellipse.java @@ -7,10 +7,9 @@ public final class Ellipse extends Shape { private final double rotation; private final Vector2 position; - public Ellipse(double a, double b, double weight, double rotation, Vector2 position) { + public Ellipse(double a, double b, double rotation, Vector2 position) { this.a = a; this.b = b; - this.weight = weight; this.rotation = rotation; this.position = position; // Approximation of length. @@ -18,11 +17,11 @@ public final class Ellipse extends Shape { } public Ellipse(double a, double b, Vector2 position) { - this(a, b, Shape.DEFAULT_WEIGHT, 0, position); + this(a, b, 0, position); } public Ellipse(double a, double b) { - this(a, b, Shape.DEFAULT_WEIGHT, 0, new Vector2()); + this(a, b, 0, new Vector2()); } @Override @@ -40,7 +39,7 @@ public final class Ellipse extends Shape { theta -= 2 * Math.PI; } - return new Ellipse(a, b, weight, theta, position); + return new Ellipse(a, b, theta, position); } @Override @@ -50,17 +49,12 @@ public final class Ellipse extends Shape { @Override public Ellipse scale(Vector2 vector) { - return new Ellipse(a * vector.getX(), b * vector.getY(), weight, rotation, + return new Ellipse(a * vector.getX(), b * vector.getY(), rotation, position.scale(vector)); } @Override public Ellipse translate(Vector2 vector) { - return new Ellipse(a, b, weight, rotation, position.translate(vector)); - } - - @Override - public Ellipse setWeight(double weight) { - return new Ellipse(a, b, weight, rotation, position); + return new Ellipse(a, b, rotation, position.translate(vector)); } } diff --git a/src/main/java/sh/ball/shapes/Line.java b/src/main/java/sh/ball/shapes/Line.java index a77566b..c81e63f 100644 --- a/src/main/java/sh/ball/shapes/Line.java +++ b/src/main/java/sh/ball/shapes/Line.java @@ -8,21 +8,12 @@ public final class Line extends Shape { private final Vector2 a; private final Vector2 b; - public Line(Vector2 a, Vector2 b, double weight) { + public Line(Vector2 a, Vector2 b) { this.a = a; this.b = b; - this.weight = weight; this.length = calculateLength(); } - public Line(Vector2 a, Vector2 b) { - this(a, b, Shape.DEFAULT_WEIGHT); - } - - public Line(double x1, double y1, double x2, double y2, double weight) { - this(new Vector2(x1, y1), new Vector2(x2, y2), weight); - } - public Line(double x1, double y1, double x2, double y2) { this(new Vector2(x1, y1), new Vector2(x2, y2)); } @@ -33,26 +24,26 @@ public final class Line extends Shape { @Override public Line rotate(double theta) { - return new Line(a.rotate(theta), b.rotate(theta), weight); + return new Line(a.rotate(theta), b.rotate(theta)); } @Override public Line translate(Vector2 vector) { - return new Line(a.translate(vector), b.translate(vector), weight); + return new Line(a.translate(vector), b.translate(vector)); } @Override public Line scale(double factor) { - return new Line(a.scale(factor), b.scale(factor), weight); + return new Line(a.scale(factor), b.scale(factor)); } @Override public Line scale(Vector2 vector) { - return new Line(a.scale(vector), b.scale(vector), weight); + return new Line(a.scale(vector), b.scale(vector)); } public Line copy() { - return new Line(a.copy(), b.copy(), weight); + return new Line(a.copy(), b.copy()); } @Override @@ -114,11 +105,6 @@ public final class Line extends Shape { return lines; } - @Override - public Line setWeight(double weight) { - return new Line(getX1(), getY1(), getX2(), getY2(), weight); - } - @Override public boolean equals(Object obj) { if (obj == this) { diff --git a/src/main/java/sh/ball/shapes/QuadraticBezierCurve.java b/src/main/java/sh/ball/shapes/QuadraticBezierCurve.java index 6795bdd..dd53a8e 100644 --- a/src/main/java/sh/ball/shapes/QuadraticBezierCurve.java +++ b/src/main/java/sh/ball/shapes/QuadraticBezierCurve.java @@ -6,18 +6,13 @@ public class QuadraticBezierCurve extends Shape { private final Vector2 p1; private final Vector2 p2; - public QuadraticBezierCurve(Vector2 p0, Vector2 p1, Vector2 p2, double weight) { + public QuadraticBezierCurve(Vector2 p0, Vector2 p1, Vector2 p2) { this.p0 = p0; this.p1 = p1; this.p2 = p2; - this.weight = weight; this.length = new Line(p0, p2).length; } - public QuadraticBezierCurve(Vector2 p0, Vector2 p1, Vector2 p2) { - this(p0, p1, p2, DEFAULT_WEIGHT); - } - @Override public Vector2 nextVector(double t) { return p1.add(p0.sub(p1).scale(Math.pow(1 - t, 2))) @@ -26,27 +21,22 @@ public class QuadraticBezierCurve extends Shape { @Override public QuadraticBezierCurve rotate(double theta) { - return new QuadraticBezierCurve(p0.rotate(theta), p1.rotate(theta), p2.rotate(theta), weight); + return new QuadraticBezierCurve(p0.rotate(theta), p1.rotate(theta), p2.rotate(theta)); } @Override public QuadraticBezierCurve scale(double factor) { - return new QuadraticBezierCurve(p0.scale(factor), p1.scale(factor), p2.scale(factor), weight); + return new QuadraticBezierCurve(p0.scale(factor), p1.scale(factor), p2.scale(factor)); } @Override public QuadraticBezierCurve scale(Vector2 vector) { - return new QuadraticBezierCurve(p0.scale(vector), p1.scale(vector), p2.scale(vector), weight); + return new QuadraticBezierCurve(p0.scale(vector), p1.scale(vector), p2.scale(vector)); } @Override public QuadraticBezierCurve translate(Vector2 vector) { return new QuadraticBezierCurve(p0.translate(vector), p1.translate(vector), - p2.translate(vector), weight); - } - - @Override - public QuadraticBezierCurve setWeight(double weight) { - return new QuadraticBezierCurve(p0, p1, p2, weight); + p2.translate(vector)); } } diff --git a/src/main/java/sh/ball/shapes/Shape.java b/src/main/java/sh/ball/shapes/Shape.java index 0d07c31..8fb0127 100644 --- a/src/main/java/sh/ball/shapes/Shape.java +++ b/src/main/java/sh/ball/shapes/Shape.java @@ -7,9 +7,6 @@ import java.util.List; public abstract class Shape { - public static final int DEFAULT_WEIGHT = 80; - - protected double weight = DEFAULT_WEIGHT; protected double length; public abstract Vector2 nextVector(double drawingProgress); @@ -22,12 +19,6 @@ public abstract class Shape { public abstract Shape translate(Vector2 vector); - public abstract Shape setWeight(double weight); - - public double getWeight() { - return weight; - } - public double getLength() { return length; } @@ -186,16 +177,15 @@ public abstract class Shape { return translatedShapes; } - public static List generatePolygram(int sides, int angleJump, Vector2 start, - double weight) { + public static List generatePolygram(int sides, int angleJump, Vector2 start) { List polygon = new ArrayList<>(); double theta = angleJump * 2 * Math.PI / sides; Vector2 rotated = start.rotate(theta); - polygon.add(new Line(start, rotated, weight)); + polygon.add(new Line(start, rotated)); while (!rotated.equals(start)) { - polygon.add(new Line(rotated.copy(), rotated.rotate(theta), weight)); + polygon.add(new Line(rotated.copy(), rotated.rotate(theta))); rotated = rotated.rotate(theta); } @@ -203,31 +193,14 @@ public abstract class Shape { return polygon; } - public static List generatePolygram(int sides, int angleJump, Vector2 start) { - return generatePolygram(sides, angleJump, start, Line.DEFAULT_WEIGHT); - } - - public static List generatePolygram(int sides, int angleJump, double scale, - double weight) { - return generatePolygram(sides, angleJump, new Vector2(scale, scale), weight); - } - public static List generatePolygram(int sides, int angleJump, double scale) { return generatePolygram(sides, angleJump, new Vector2(scale, scale)); } - public static List generatePolygon(int sides, Vector2 start, double weight) { - return generatePolygram(sides, 1, start, weight); - } - public static List generatePolygon(int sides, Vector2 start) { return generatePolygram(sides, 1, start); } - public static List generatePolygon(int sides, double scale, double weight) { - return generatePolygon(sides, new Vector2(scale, scale), weight); - } - public static List generatePolygon(int sides, double scale) { return generatePolygon(sides, new Vector2(scale, scale)); } diff --git a/src/main/java/sh/ball/shapes/Vector2.java b/src/main/java/sh/ball/shapes/Vector2.java index 968bb5b..1326dac 100644 --- a/src/main/java/sh/ball/shapes/Vector2.java +++ b/src/main/java/sh/ball/shapes/Vector2.java @@ -7,18 +7,13 @@ public final class Vector2 extends Shape { private final double x; private final double y; - public Vector2(double x, double y, double weight) { + public Vector2(double x, double y) { this.x = x; this.y = y; - this.weight = weight; - } - - public Vector2(double x, double y) { - this(x, y, Shape.DEFAULT_WEIGHT); } public Vector2(double xy) { - this(xy, xy, Shape.DEFAULT_WEIGHT); + this(xy, xy); } public Vector2() { @@ -85,11 +80,6 @@ public final class Vector2 extends Shape { return new Vector2(getX() + vector.getX(), getY() + vector.getY()); } - @Override - public Vector2 setWeight(double weight) { - return new Vector2(x, y, weight); - } - @Override public boolean equals(Object obj) { if (this == obj) { diff --git a/src/main/resources/fxml/osci-render.fxml b/src/main/resources/fxml/osci-render.fxml index 1c933f1..915f66f 100644 --- a/src/main/resources/fxml/osci-render.fxml +++ b/src/main/resources/fxml/osci-render.fxml @@ -73,24 +73,24 @@ -