diff --git a/.gitignore b/.gitignore index 03c05402..7b107cce 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,9 @@ /target /.idea -# Ignore all models (excluding cube and machine) +# Ignore all models and files, (excluding defaults) /src/main/resources/models/* +/src/main/resources/images/* # ignore temporary files *~ diff --git a/README.md b/README.md index 0d9fa6f2..e95055ce 100644 --- a/README.md +++ b/README.md @@ -48,9 +48,7 @@ There are some additional controls for `.obj` files: ## Building -All dependencies are specified in the `pom.xml` file. Cloning this repo and using IntelliJ should make building a painless process. - -[`xtaudio`](https://sjoerdvankreel.github.io/xt-audio/) is the only library that is not on Maven central, and so it is provided in the `lib` folder and a system dependency is given in `pom.xml`. +All dependencies are specified in the `pom.xml` file. Cloning this repo and using IntelliJ with Maven should make building a painless process. ## Contact diff --git a/lib/com.xt-audio.xt-1.0.6.jar b/lib/com.xt-audio.xt-1.0.6.jar deleted file mode 100644 index 47b70d93..00000000 Binary files a/lib/com.xt-audio.xt-1.0.6.jar and /dev/null differ diff --git a/pom.xml b/pom.xml index 975b527f..eaca776a 100644 --- a/pom.xml +++ b/pom.xml @@ -37,13 +37,11 @@ - + - com.xtaudio.xt - xtaudio - 1.0.6 - system - ${basedir}/lib/com.xt-audio.xt-1.0.6.jar + com.github.sjoerdvankreel + xt.audio + 1.9 diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 118be082..e6383266 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -2,7 +2,7 @@ module sh.ball { requires javafx.controls; requires javafx.graphics; requires javafx.fxml; - requires com.xtaudio.xt; + requires xt.audio; requires java.xml; requires org.jsoup; requires java.data.front; diff --git a/src/main/java/sh/ball/audio/AudioPlayer.java b/src/main/java/sh/ball/audio/AudioPlayer.java index cebdf220..591acf4a 100644 --- a/src/main/java/sh/ball/audio/AudioPlayer.java +++ b/src/main/java/sh/ball/audio/AudioPlayer.java @@ -1,16 +1,25 @@ package sh.ball.audio; -import com.xtaudio.xt.XtAudio; -import com.xtaudio.xt.XtBuffer; -import com.xtaudio.xt.XtDevice; -import com.xtaudio.xt.XtFormat; -import com.xtaudio.xt.XtMix; -import com.xtaudio.xt.XtSample; -import com.xtaudio.xt.XtService; -import com.xtaudio.xt.XtSetup; -import com.xtaudio.xt.XtStream; +import xt.audio.Enums.XtSample; +import xt.audio.Enums.XtSetup; +import xt.audio.Enums.XtSystem; +import xt.audio.Structs.XtBuffer; +import xt.audio.Structs.XtBufferSize; +import xt.audio.Structs.XtChannels; +import xt.audio.Structs.XtDeviceStreamParams; +import xt.audio.Structs.XtFormat; +import xt.audio.Structs.XtMix; +import xt.audio.Structs.XtStreamParams; +import xt.audio.XtAudio; +import xt.audio.XtDevice; +import xt.audio.XtPlatform; +import xt.audio.XtSafeBuffer; +import xt.audio.XtService; +import xt.audio.XtStream; + import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; + import sh.ball.shapes.Shape; import sh.ball.shapes.Vector2; @@ -18,7 +27,11 @@ import java.util.List; public class AudioPlayer implements Runnable { - private final XtFormat FORMAT; + private static final int SAMPLE_RATE = 192000; + + private final XtMix MIX = new XtMix(SAMPLE_RATE, XtSample.FLOAT32); + private final XtChannels CHANNELS = new XtChannels(0, 0, 2, 0); + private final XtFormat FORMAT = new XtFormat(MIX, CHANNELS); private final BlockingQueue> frameQueue; private List frame; @@ -35,14 +48,13 @@ public class AudioPlayer implements Runnable { private volatile boolean stopped; - public AudioPlayer(int sampleRate, BlockingQueue> frameQueue) { - this.FORMAT = new XtFormat(new XtMix(sampleRate, XtSample.FLOAT32), 0, 0, 2, 0); + public AudioPlayer(BlockingQueue> frameQueue) { this.frameQueue = frameQueue; } - public AudioPlayer(int sampleRate, ArrayBlockingQueue> frameQueue, double rotateSpeed, - double translateSpeed, Vector2 translateVector, double scale, double weight) { - this(sampleRate, frameQueue); + public AudioPlayer(ArrayBlockingQueue> frameQueue, double rotateSpeed, + double translateSpeed, Vector2 translateVector, double scale, double weight) { + this(frameQueue); setRotateSpeed(rotateSpeed); setTranslationSpeed(translateSpeed); setTranslation(translateVector); @@ -50,10 +62,12 @@ public class AudioPlayer implements Runnable { setWeight(weight); } - private void render(XtStream stream, Object input, Object output, int audioFrames, - double time, long position, boolean timeValid, long error, Object user) - throws InterruptedException { - for (int f = 0; f < audioFrames; f++) { + private int render(XtStream stream, XtBuffer buffer, Object user) throws InterruptedException { + XtSafeBuffer safe = XtSafeBuffer.get(stream); + safe.lock(buffer); + float[] output = (float[]) safe.getOutput(); + + for (int f = 0; f < buffer.frames; f++) { Shape shape = getCurrentShape(); shape = shape.setWeight(weight); @@ -65,8 +79,8 @@ public class AudioPlayer implements Runnable { double drawingProgress = totalAudioFrames == 0 ? 1 : audioFramesDrawn / totalAudioFrames; Vector2 nextVector = shape.nextVector(drawingProgress); - ((float[]) output)[f * FORMAT.outputs] = (float) nextVector.getX(); - ((float[]) output)[f * FORMAT.outputs + 1] = (float) nextVector.getY(); + output[f * FORMAT.channels.outputs] = (float) nextVector.getX(); + output[f * FORMAT.channels.outputs + 1] = (float) nextVector.getY(); audioFramesDrawn++; @@ -80,12 +94,14 @@ public class AudioPlayer implements Runnable { frame = frameQueue.take(); } } + safe.unlock(buffer); + return 0; } private Shape rotate(Shape shape, double sampleRate) { if (rotateSpeed != 0) { shape = shape.rotate( - nextTheta(sampleRate, rotateSpeed, translatePhase) + nextTheta(sampleRate, rotateSpeed, translatePhase) ); } @@ -95,7 +111,7 @@ public class AudioPlayer implements Runnable { private Shape translate(Shape shape, double sampleRate) { if (translateSpeed != 0 && !translateVector.equals(new Vector2())) { return shape.translate(translateVector.scale( - Math.sin(nextTheta(sampleRate, translateSpeed, rotatePhase)) + Math.sin(nextTheta(sampleRate, translateSpeed, rotatePhase)) )); } @@ -152,14 +168,22 @@ public class AudioPlayer implements Runnable { throw new RuntimeException("Initial frame not found. Cannot continue."); } - try (XtAudio audio = new XtAudio(null, null, null, null)) { - XtService service = XtAudio.getServiceBySetup(XtSetup.CONSUMER_AUDIO); - try (XtDevice device = service.openDefaultDevice(true)) { - if (device != null && device.supportsFormat(FORMAT)) { - XtBuffer buffer = device.getBuffer(FORMAT); + try (XtPlatform platform = XtAudio.init(null, null)) { + XtSystem system = platform.setupToSystem(XtSetup.CONSUMER_AUDIO); + XtService service = platform.getService(system); + if (service == null) return; - try (XtStream stream = device.openStream(FORMAT, true, false, - buffer.current, this::render, null, null)) { + String defaultOutput = service.getDefaultDeviceId(true); + if (defaultOutput == null) return; + + try (XtDevice device = service.openDevice(defaultOutput)) { + if (device.supportsFormat(FORMAT)) { + + XtBufferSize size = device.getBufferSize(FORMAT); + XtStreamParams streamParams = new XtStreamParams(true, this::render, null, null); + XtDeviceStreamParams deviceParams = new XtDeviceStreamParams(streamParams, FORMAT, size.current); + try (XtStream stream = device.openStream(deviceParams, null); + XtSafeBuffer safe = XtSafeBuffer.register(stream, true)) { stream.start(); while (!stopped) { Thread.onSpinWait(); diff --git a/src/main/java/sh/ball/gui/Controller.java b/src/main/java/sh/ball/gui/Controller.java index 4d6520ef..e21fd571 100644 --- a/src/main/java/sh/ball/gui/Controller.java +++ b/src/main/java/sh/ball/gui/Controller.java @@ -30,12 +30,11 @@ import sh.ball.shapes.Vector2; public class Controller implements Initializable { private static final int BUFFER_SIZE = 20; - private static final int SAMPLE_RATE = 192000; private static final String DEFAULT_FILE = "src/main/resources/models/cube.obj"; private final FileChooser fileChooser = new FileChooser(); private final BlockingQueue> frameQueue = new ArrayBlockingQueue<>(BUFFER_SIZE); - private final AudioPlayer player = new AudioPlayer(SAMPLE_RATE, frameQueue); + private final AudioPlayer player = new AudioPlayer(frameQueue); private final FrameProducer producer = new FrameProducer(frameQueue); private Stage stage; diff --git a/src/main/java/sh/ball/parser/TextParser.java b/src/main/java/sh/ball/parser/TextParser.java index 5a402053..489c66b4 100644 --- a/src/main/java/sh/ball/parser/TextParser.java +++ b/src/main/java/sh/ball/parser/TextParser.java @@ -9,6 +9,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import javax.xml.parsers.ParserConfigurationException; + import org.xml.sax.SAXException; import sh.ball.parser.svg.SvgParser; import sh.ball.shapes.Shape; @@ -18,7 +19,7 @@ public class TextParser extends FileParser { private static final char WIDE_CHAR = 'W'; private static final double HEIGHT_SCALAR = 1.6; - private static final String DEFAULT_FONT = "fonts/SourceCodePro-ExtraLight.svg"; + private static final String DEFAULT_FONT = TextParser.class.getResource("/fonts/SourceCodePro-ExtraLight.svg").getPath(); private final Map> charToShape; private final List text; @@ -27,7 +28,7 @@ public class TextParser extends FileParser { private List shapes; public TextParser(String path, String font) - throws IOException, SAXException, ParserConfigurationException { + throws IOException, SAXException, ParserConfigurationException { checkFileExtension(path); this.filePath = path; this.charToShape = new HashMap<>(); @@ -37,7 +38,7 @@ public class TextParser extends FileParser { } public TextParser(String path) - throws IOException, SAXException, ParserConfigurationException { + throws IOException, SAXException, ParserConfigurationException { this(path, DEFAULT_FONT); } @@ -48,7 +49,7 @@ public class TextParser extends FileParser { @Override protected void parseFile(String path) - throws ParserConfigurationException, IOException, SAXException, IllegalArgumentException { + throws ParserConfigurationException, IOException, SAXException, IllegalArgumentException { SvgParser parser = new SvgParser(path); /* WIDE_CHAR used as an example character that will be wide in most languages. @@ -71,8 +72,8 @@ public class TextParser extends FileParser { char[] lineChars = text.get(i).toCharArray(); for (int j = 0; j < lineChars.length; j++) { shapes.addAll(Shape.translate( - charToShape.get(lineChars[j]), - new Vector2(j * width, -i * height) + charToShape.get(lineChars[j]), + new Vector2(j * width, -i * height) )); } }