kopia lustrzana https://github.com/jameshball/osci-render
Update AudioEngine and add AudioDevice and AudioSample
rodzic
b823a0fd69
commit
45289200a4
|
@ -0,0 +1,9 @@
|
|||
package sh.ball.audio.engine;
|
||||
|
||||
public interface AudioDevice {
|
||||
String name();
|
||||
|
||||
int sampleRate();
|
||||
|
||||
AudioSample sample();
|
||||
}
|
|
@ -2,11 +2,14 @@ package sh.ball.audio.engine;
|
|||
|
||||
import sh.ball.shapes.Vector2;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
public interface AudioEngine {
|
||||
void play(Callable<Vector2> channelGenerator, ReentrantLock renderLock);
|
||||
void play(Callable<Vector2> channelGenerator, ReentrantLock renderLock, AudioDevice device);
|
||||
|
||||
void stop();
|
||||
int sampleRate();
|
||||
|
||||
List<AudioDevice> devices();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
package sh.ball.audio.engine;
|
||||
|
||||
public enum AudioSample {
|
||||
UINT8,
|
||||
INT8,
|
||||
INT16,
|
||||
INT24,
|
||||
INT32,
|
||||
FLOAT32,
|
||||
FLOAT64
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package sh.ball.audio.engine;
|
||||
|
||||
public class DefaultAudioDevice implements AudioDevice {
|
||||
|
||||
final String name;
|
||||
final int sampleRate;
|
||||
final AudioSample sample;
|
||||
|
||||
public DefaultAudioDevice(String name, int sampleRate, AudioSample sample) {
|
||||
this.name = name;
|
||||
this.sampleRate = sampleRate;
|
||||
this.sample = sample;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int sampleRate() {
|
||||
return sampleRate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AudioSample sample() {
|
||||
return sample;
|
||||
}
|
||||
}
|
|
@ -3,33 +3,23 @@ package sh.ball.audio.engine;
|
|||
import sh.ball.shapes.Vector2;
|
||||
import xt.audio.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
public class XtAudioEngine implements AudioEngine {
|
||||
|
||||
private static final int DEFAULT_SAMPLE_RATE = 192000;
|
||||
// Stereo audio
|
||||
private static final int NUM_OUTPUTS = 2;
|
||||
|
||||
private final int sampleRate;
|
||||
|
||||
private volatile boolean stopped = false;
|
||||
private ReentrantLock renderLock;
|
||||
private Callable<Vector2> channelGenerator;
|
||||
|
||||
public XtAudioEngine() {
|
||||
try (XtPlatform platform = XtAudio.init(null, null)) {
|
||||
XtService service = getService(platform);
|
||||
String deviceId = getDeviceId(service);
|
||||
|
||||
try (XtDevice device = service.openDevice(deviceId)) {
|
||||
// Gets the sample rate of the device. E.g. 192000 hz.
|
||||
this.sampleRate = device.getMix().map(xtMix -> xtMix.rate).orElse(DEFAULT_SAMPLE_RATE);
|
||||
}
|
||||
}
|
||||
}
|
||||
public XtAudioEngine() {}
|
||||
|
||||
private int render(XtStream stream, Structs.XtBuffer buffer, Object user) throws Exception {
|
||||
XtSafeBuffer safe = XtSafeBuffer.get(stream);
|
||||
|
@ -52,25 +42,24 @@ public class XtAudioEngine implements AudioEngine {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void play(Callable<Vector2> channelGenerator, ReentrantLock renderLock) {
|
||||
public void play(Callable<Vector2> channelGenerator, ReentrantLock renderLock, AudioDevice device) {
|
||||
this.channelGenerator = channelGenerator;
|
||||
this.renderLock = renderLock;
|
||||
try (XtPlatform platform = XtAudio.init(null, null)) {
|
||||
XtService service = getService(platform);
|
||||
String deviceId = getDeviceId(service);
|
||||
|
||||
try (XtDevice device = service.openDevice(deviceId)) {
|
||||
try (XtDevice xtDevice = service.openDevice(device.name())) {
|
||||
// TODO: Make this generic to the type of XtSample of the current device.
|
||||
Structs.XtMix mix = new Structs.XtMix(sampleRate, Enums.XtSample.FLOAT32);
|
||||
Structs.XtMix mix = new Structs.XtMix(xtDevice.getMix().orElseThrow().rate, Enums.XtSample.FLOAT32);
|
||||
Structs.XtChannels channels = new Structs.XtChannels(0, 0, NUM_OUTPUTS, 0);
|
||||
Structs.XtFormat format = new Structs.XtFormat(mix, channels);
|
||||
|
||||
if (device.supportsFormat(format)) {
|
||||
Structs.XtBufferSize size = device.getBufferSize(format);
|
||||
if (xtDevice.supportsFormat(format)) {
|
||||
Structs.XtBufferSize size = xtDevice.getBufferSize(format);
|
||||
Structs.XtStreamParams streamParams = new Structs.XtStreamParams(true, this::render, null, null);
|
||||
Structs.XtDeviceStreamParams deviceParams = new Structs.XtDeviceStreamParams(streamParams, format, size.current);
|
||||
|
||||
try (XtStream stream = device.openStream(deviceParams, null);
|
||||
try (XtStream stream = xtDevice.openStream(deviceParams, null);
|
||||
XtSafeBuffer safe = XtSafeBuffer.register(stream, true)) {
|
||||
stream.start();
|
||||
while (!stopped) {
|
||||
|
@ -91,10 +80,37 @@ public class XtAudioEngine implements AudioEngine {
|
|||
}
|
||||
|
||||
@Override
|
||||
public int sampleRate() {
|
||||
return sampleRate;
|
||||
public List<AudioDevice> devices() {
|
||||
List<AudioDevice> devices = new ArrayList<>();
|
||||
|
||||
try (XtPlatform platform = XtAudio.init(null, null)) {
|
||||
XtService service = getService(platform);
|
||||
XtDeviceList xtDevices = service.openDeviceList(EnumSet.of(Enums.XtEnumFlags.OUTPUT));
|
||||
|
||||
for (int i = 0; i < xtDevices.getCount(); i++) {
|
||||
String device = xtDevices.getId(i);
|
||||
|
||||
try (XtDevice xtDevice = service.openDevice(device)) {
|
||||
Optional<Structs.XtMix> mix = xtDevice.getMix();
|
||||
|
||||
if (mix.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Structs.XtChannels channels = new Structs.XtChannels(0, 0, NUM_OUTPUTS, 0);
|
||||
Structs.XtFormat format = new Structs.XtFormat(mix.get(), channels);
|
||||
|
||||
if (xtDevice.supportsFormat(format)) {
|
||||
devices.add(new DefaultAudioDevice(device, mix.get().rate, XtSampleToAudioSample(mix.get().sample)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return devices;
|
||||
}
|
||||
|
||||
|
||||
private XtService getService(XtPlatform platform) {
|
||||
XtService service = platform.getService(platform.setupToSystem(Enums.XtSetup.SYSTEM_AUDIO));
|
||||
if (service == null) {
|
||||
|
@ -125,4 +141,14 @@ public class XtAudioEngine implements AudioEngine {
|
|||
private String getFirstDevice(XtService service) {
|
||||
return service.openDeviceList(EnumSet.of(Enums.XtEnumFlags.OUTPUT)).getId(0);
|
||||
}
|
||||
|
||||
private AudioSample XtSampleToAudioSample(Enums.XtSample sample) {
|
||||
return switch (sample) {
|
||||
case UINT8 -> AudioSample.UINT8;
|
||||
case INT16 -> AudioSample.INT16;
|
||||
case INT24 -> AudioSample.INT24;
|
||||
case INT32 -> AudioSample.INT32;
|
||||
case FLOAT32 -> AudioSample.FLOAT32;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.CheckBox?>
|
||||
<?import javafx.scene.control.ComboBox?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.Slider?>
|
||||
<?import javafx.scene.control.TextField?>
|
||||
|
@ -9,7 +10,6 @@
|
|||
<?import javafx.scene.layout.AnchorPane?>
|
||||
<?import javafx.scene.text.Font?>
|
||||
|
||||
|
||||
<AnchorPane prefHeight="498.0" prefWidth="837.0" xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1">
|
||||
<AnchorPane id="control-pane" layoutX="422.0" layoutY="19.0" prefHeight="185.0" prefWidth="402.0">
|
||||
<children>
|
||||
|
@ -18,6 +18,7 @@
|
|||
<Button fx:id="recordButton" layoutX="14.0" layoutY="54.0" mnemonicParsing="false" prefHeight="26.0" prefWidth="114.0" text="Record" />
|
||||
<Label fx:id="recordLabel" layoutX="146.0" layoutY="59.0" maxWidth="270.0" prefHeight="18.0" prefWidth="245.0" />
|
||||
<Label id="frequency" fx:id="frequencyLabel" layoutX="14.0" layoutY="113.0" prefHeight="58.0" prefWidth="376.0" text="L/R Frequency: " />
|
||||
<ComboBox layoutX="251.0" layoutY="113.0" prefWidth="150.0" />
|
||||
</children>
|
||||
</AnchorPane>
|
||||
<TitledPane animated="false" collapsible="false" layoutX="422.0" layoutY="213.0" prefHeight="272.0" prefWidth="402.0" text="Effects">
|
||||
|
|
Ładowanie…
Reference in New Issue