Merge pull request #30 from jameshball/java-audio-engine

Add JavaAudioEngine for greater compatibility
pull/35/head
James H Ball 2021-07-04 18:46:15 +01:00 zatwierdzone przez GitHub
commit 923af64c40
4 zmienionych plików z 155 dodań i 3 usunięć

Wyświetl plik

@ -6,7 +6,7 @@
<groupId>sh.ball</groupId>
<artifactId>osci-render</artifactId>
<version>1.7.3</version>
<version>1.8.0</version>
<name>osci-render</name>

Wyświetl plik

@ -0,0 +1,61 @@
package sh.ball.audio.engine;
import sh.ball.shapes.Vector2;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
public class ConglomerateAudioEngine implements AudioEngine {
private final XtAudioEngine xtEngine = new XtAudioEngine();
private final JavaAudioEngine javaEngine = new JavaAudioEngine();
// TODO: Try and make non-static
private static List<AudioDevice> xtDevices;
private static AudioDevice javaDevice;
private boolean playing = false;
@Override
public boolean isPlaying() {
return playing;
}
@Override
public void play(Callable<Vector2> channelGenerator, AudioDevice device) throws Exception {
playing = true;
if (xtDevices.contains(device)) {
xtEngine.play(channelGenerator, device);
} else {
javaEngine.play(channelGenerator, javaDevice);
}
playing = false;
}
@Override
public void stop() {
xtEngine.stop();
javaEngine.stop();
}
@Override
public List<AudioDevice> devices() {
if (xtDevices == null) {
xtDevices = xtEngine.devices();
}
if (javaDevice == null) {
javaDevice = javaEngine.getDefaultDevice();
}
List<AudioDevice> devices = new ArrayList<>(xtDevices);
devices.add(javaDevice);
return devices;
}
@Override
public AudioDevice getDefaultDevice() {
return javaEngine.getDefaultDevice();
}
}

Wyświetl plik

@ -0,0 +1,90 @@
package sh.ball.audio.engine;
import sh.ball.shapes.Vector2;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.SourceDataLine;
import java.util.List;
import java.util.concurrent.Callable;
public class JavaAudioEngine implements AudioEngine {
private static final int DEFAULT_SAMPLE_RATE = 192000;
private static final int NUM_CHANNELS = 2;
private static final int LATENCY_MS = 10;
private static final int MAX_FRAME_LATENCY = 512;
private static final int BIT_DEPTH = 16;
private static final int FRAME_SIZE = NUM_CHANNELS * BIT_DEPTH / 8;
private static final boolean BIG_ENDIAN = false;
private static final boolean SIGNED_SAMPLE = true;
private volatile boolean stopped = false;
private SourceDataLine source;
@Override
public boolean isPlaying() {
return source.isRunning();
}
@Override
public void play(Callable<Vector2> channelGenerator, AudioDevice device) throws Exception {
this.stopped = false;
AudioFormat format = new AudioFormat((float) device.sampleRate(), BIT_DEPTH, NUM_CHANNELS, SIGNED_SAMPLE, BIG_ENDIAN);
this.source = AudioSystem.getSourceDataLine(format);
source.open(format);
int frameLatency = Math.max((int) (device.sampleRate() * LATENCY_MS * 0.0005), MAX_FRAME_LATENCY);
int bufferSize = frameLatency * FRAME_SIZE;
int remainingBufferSpace = source.getBufferSize() - bufferSize;
byte[] buffer = new byte[bufferSize * 2];
source.start();
while (!stopped) {
int delta = source.available() - remainingBufferSpace;
if (delta > 0) {
int requiredSamples = (delta + bufferSize) / FRAME_SIZE;
if (requiredSamples * NUM_CHANNELS > buffer.length / 2) {
buffer = new byte[requiredSamples * NUM_CHANNELS * 2];
}
for (int i = 0; i < requiredSamples; i++) {
try {
Vector2 channels = channelGenerator.call();
short left = (short) (channels.getX() * Short.MAX_VALUE);
short right = (short) (channels.getY() * Short.MAX_VALUE);
buffer[i * 4] = (byte) left;
buffer[i * 4 + 1] = (byte) (left >> 8);
buffer[i * 4 + 2] = (byte) right;
buffer[i * 4 + 3] = (byte) (right >> 8);
} catch (Exception e) {
e.printStackTrace();
}
}
source.write(buffer, 0, requiredSamples * FRAME_SIZE);
}
}
source.stop();
}
@Override
public void stop() {
stopped = true;
}
@Override
public List<AudioDevice> devices() {
return List.of(getDefaultDevice());
}
@Override
public AudioDevice getDefaultDevice() {
return new DefaultAudioDevice("default", "default", DEFAULT_SAMPLE_RATE, AudioSample.INT16);
}
}

Wyświetl plik

@ -11,7 +11,8 @@ import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage;
import sh.ball.audio.ShapeAudioPlayer;
import sh.ball.audio.engine.XtAudioEngine;
import sh.ball.audio.engine.ConglomerateAudioEngine;
import sh.ball.audio.engine.JavaAudioEngine;
import sh.ball.engine.Vector3;
import java.util.Objects;
@ -24,7 +25,7 @@ public class Gui extends Application {
System.setProperty("prism.lcdtext", "false");
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/osci-render.fxml"));
Controller controller = new Controller(new ShapeAudioPlayer(XtAudioEngine::new));
Controller controller = new Controller(new ShapeAudioPlayer(ConglomerateAudioEngine::new));
loader.setController(controller);
Parent root = loader.load();