Add comments for audio classes

pull/35/head
James Ball 2021-09-01 22:06:02 +01:00
rodzic 455d9c2ffd
commit fec55fabd7
14 zmienionych plików z 44 dodań i 35 usunięć

Wyświetl plik

@ -2,6 +2,10 @@ package sh.ball.audio.effect;
import sh.ball.shapes.Vector2;
// The Effect interface is intended for audio effects, but can be used for any
// effect that manipulates a Vector2 overtime. Audio effects iterate their
// frame count, which can result in different results according to the count,
// along with the vector can apply an audio effect.
public interface Effect {
Vector2 apply(int count, Vector2 vector);
}

Wyświetl plik

@ -4,6 +4,7 @@ import sh.ball.shapes.Vector2;
import static sh.ball.math.Math.round;
// Used for creating various audio effects
public class EffectFactory {
public static Effect vectorCancelling(int frequency) {
return (count, v) -> count % frequency == 0 ? v.scale(-1) : v;

Wyświetl plik

@ -3,7 +3,6 @@ package sh.ball.audio.effect;
public enum EffectType {
VECTOR_CANCELLING,
BIT_CRUSH,
SCALE,
ROTATE,
TRANSLATE,
VERTICAL_DISTORT,

Wyświetl plik

@ -2,6 +2,8 @@ package sh.ball.audio.effect;
public abstract class PhaseEffect implements Effect {
// sufficiently large so that nextTheta doesn't commonly reach it, otherwise
// there are audio artifacts
private static final double LARGE_VAL = 2 << 20;
protected final int sampleRate;

Wyświetl plik

@ -2,6 +2,7 @@ package sh.ball.audio.effect;
import sh.ball.shapes.Vector2;
// rotates the vector about (0,0)
public class RotateEffect extends PhaseEffect {
public RotateEffect(int sampleRate, double speed) {

Wyświetl plik

@ -1,25 +0,0 @@
package sh.ball.audio.effect;
import sh.ball.shapes.Vector2;
public class ScaleEffect implements Effect {
private double scale;
public ScaleEffect(double scale) {
this.scale = scale;
}
public ScaleEffect() {
this(1);
}
public void setScale(double scale) {
this.scale = scale;
}
@Override
public Vector2 apply(int count, Vector2 vector) {
return vector.scale(scale);
}
}

Wyświetl plik

@ -1,8 +1,8 @@
package sh.ball.audio.effect;
import sh.ball.audio.FrequencyListener;
import sh.ball.shapes.Vector2;
// Plays a sine wave at the given frequency and volume
public class SineEffect extends PhaseEffect {
private static final double DEFAULT_VOLUME = 1;

Wyświetl plik

@ -2,6 +2,7 @@ package sh.ball.audio.effect;
import sh.ball.shapes.Vector2;
// Translates the given vector in a sinusoidal fashion
public class TranslateEffect extends PhaseEffect {
private Vector2 translation;

Wyświetl plik

@ -3,6 +3,9 @@ package sh.ball.audio.effect;
import sh.ball.audio.FrequencyListener;
import sh.ball.shapes.Vector2;
// Plays a sine wave at the same frequency as provided by the FrequencyListener
// to apply an audio effect that when visualised makes the image slightly wobble
// because it is played at a similar frequency
public class WobbleEffect extends PhaseEffect implements FrequencyListener {
private static final double DEFAULT_VOLUME = 0.2;

Wyświetl plik

@ -1,5 +1,7 @@
package sh.ball.audio.engine;
// defines the different kinds of support audio samples for different audio
// devices
public enum AudioSample {
UINT8,
INT8,

Wyświetl plik

@ -7,8 +7,12 @@ import java.util.List;
import java.util.Locale;
import java.util.concurrent.Callable;
// combines all types of AudioEngines into a single AudioEngine to allow for
// maximum compatibility and support for all audio drivers that have been
// implemented
public class ConglomerateAudioEngine implements AudioEngine {
// used to determine which OS we are on
private static final String OS = System.getProperty("os.name", "generic").toLowerCase(Locale.ENGLISH);
private static final boolean MAC_OS = OS.contains("mac") || OS.contains("darwin");
@ -55,6 +59,7 @@ public class ConglomerateAudioEngine implements AudioEngine {
}
if (xtDevices == null) {
// XtAudio does not support MacOS so we should ignore all devices on XtAudio if on a mac
if (MAC_OS) {
xtDevices = new ArrayList<>();
} else {

Wyświetl plik

@ -11,9 +11,11 @@ import java.util.concurrent.Callable;
public class JavaAudioEngine implements AudioEngine {
private static final int DEFAULT_SAMPLE_RATE = 192000;
// stereo audio
private static final int NUM_CHANNELS = 2;
private static final int LATENCY_MS = 10;
private static final int MAX_FRAME_LATENCY = 512;
// java sound doesn't support anything more than 16 bit :(
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;
@ -36,6 +38,7 @@ public class JavaAudioEngine implements AudioEngine {
AudioFormat format = new AudioFormat((float) device.sampleRate(), BIT_DEPTH, NUM_CHANNELS, SIGNED_SAMPLE, BIG_ENDIAN);
// connects to a device that can support the format above (i.e. default audio device)
this.source = AudioSystem.getSourceDataLine(format);
source.open(format);
@ -58,6 +61,8 @@ public class JavaAudioEngine implements AudioEngine {
for (int i = 0; i < requiredSamples; i++) {
try {
Vector2 channels = channelGenerator.call();
// converting doubles from Vector2 into shorts and then bytes so
// that the byte buffer supports them
short left = (short) (channels.getX() * Short.MAX_VALUE);
short right = (short) (channels.getY() * Short.MAX_VALUE);
buffer[i * 4] = (byte) left;
@ -88,7 +93,7 @@ public class JavaAudioEngine implements AudioEngine {
@Override
public AudioDevice getDefaultDevice() {
return new DefaultAudioDevice("default", "default", DEFAULT_SAMPLE_RATE, AudioSample.INT16);
return new SimpleAudioDevice("default", "default", DEFAULT_SAMPLE_RATE, AudioSample.INT16);
}
@Override

Wyświetl plik

@ -2,14 +2,14 @@ package sh.ball.audio.engine;
import java.util.Objects;
public class DefaultAudioDevice implements AudioDevice {
public class SimpleAudioDevice implements AudioDevice {
final String id;
final String name;
final int sampleRate;
final AudioSample sample;
public DefaultAudioDevice(String id, String name, int sampleRate, AudioSample sample) {
public SimpleAudioDevice(String id, String name, int sampleRate, AudioSample sample) {
this.id = id;
this.name = name;
this.sampleRate = sampleRate;
@ -47,7 +47,7 @@ public class DefaultAudioDevice implements AudioDevice {
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DefaultAudioDevice that = (DefaultAudioDevice) o;
SimpleAudioDevice that = (SimpleAudioDevice) o;
return sampleRate == that.sampleRate && Objects.equals(id, that.id) && Objects.equals(name, that.name) && sample == that.sample;
}

Wyświetl plik

@ -9,8 +9,11 @@ import java.util.List;
import java.util.Optional;
import java.util.concurrent.Callable;
// Audio engine that connects to devices using the XtAudio library
public class XtAudioEngine implements AudioEngine {
private static final int DEFAULT_SAMPLE_RATE = 192000;
private static final Enums.XtSample DEFAULT_AUDIO_SAMPLE = Enums.XtSample.FLOAT32;
// Stereo audio
private static final int NUM_OUTPUTS = 2;
@ -35,6 +38,8 @@ public class XtAudioEngine implements AudioEngine {
return 0;
}
// fully compatible function to write a Vector2 (i.e. stereo) channels to
// any AudioSample. This ensures maximum compatibility for audio devices.
private void writeChannels(Vector2 channels, Object output, int frame) {
int index = frame * NUM_OUTPUTS;
switch (device.sample()) {
@ -88,6 +93,7 @@ public class XtAudioEngine implements AudioEngine {
return playing;
}
// XtAudio boilerplate for connecting to an audio device and playing audio
@Override
public void play(Callable<Vector2> channelGenerator, AudioDevice device) {
this.playing = true;
@ -128,6 +134,7 @@ public class XtAudioEngine implements AudioEngine {
stopped = true;
}
// XtAudio boilerplate for getting a list of connected audio devices
@Override
public List<AudioDevice> devices() {
List<AudioDevice> devices = new ArrayList<>();
@ -148,14 +155,14 @@ public class XtAudioEngine implements AudioEngine {
Optional<Structs.XtMix> mix = xtDevice.getMix();
if (mix.isEmpty()) {
mix = Optional.of(new Structs.XtMix(192000, Enums.XtSample.FLOAT32));
mix = Optional.of(new Structs.XtMix(DEFAULT_SAMPLE_RATE, DEFAULT_AUDIO_SAMPLE));
}
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(deviceId, deviceName, mix.get().rate, XtSampleToAudioSample(mix.get().sample)));
devices.add(new SimpleAudioDevice(deviceId, deviceName, mix.get().rate, XtSampleToAudioSample(mix.get().sample)));
}
} catch (XtException e) {
e.printStackTrace();
@ -170,6 +177,7 @@ public class XtAudioEngine implements AudioEngine {
return devices;
}
// XtAudio boilerplate for getting default device
@Override
public AudioDevice getDefaultDevice() {
try (XtPlatform platform = XtAudio.init(null, null)) {
@ -180,7 +188,7 @@ public class XtAudioEngine implements AudioEngine {
Optional<Structs.XtMix> mix = xtDevice.getMix();
if (mix.isEmpty()) {
mix = Optional.of(new Structs.XtMix(192000, Enums.XtSample.FLOAT32));
mix = Optional.of(new Structs.XtMix(DEFAULT_SAMPLE_RATE, DEFAULT_AUDIO_SAMPLE));
}
String deviceName = service.openDeviceList(EnumSet.of(Enums.XtEnumFlags.OUTPUT)).getName(deviceId);
@ -189,7 +197,7 @@ public class XtAudioEngine implements AudioEngine {
Structs.XtFormat format = new Structs.XtFormat(mix.get(), channels);
if (xtDevice.supportsFormat(format)) {
return new DefaultAudioDevice(deviceId, deviceName, mix.get().rate, XtSampleToAudioSample(mix.get().sample));
return new SimpleAudioDevice(deviceId, deviceName, mix.get().rate, XtSampleToAudioSample(mix.get().sample));
} else {
return null;
}
@ -203,6 +211,7 @@ public class XtAudioEngine implements AudioEngine {
}
// connects to an XtAudio XtService in order of lowest latency to highest latency
private XtService getService(XtPlatform platform) {
XtService service = platform.getService(platform.setupToSystem(Enums.XtSetup.SYSTEM_AUDIO));
if (service == null) {
@ -222,6 +231,7 @@ public class XtAudioEngine implements AudioEngine {
return service;
}
// helper for converting XtSamples into AudioSamples
private AudioSample XtSampleToAudioSample(Enums.XtSample sample) {
return switch (sample) {
case UINT8 -> AudioSample.UINT8;
@ -232,6 +242,7 @@ public class XtAudioEngine implements AudioEngine {
};
}
// helper for converting AudioSamples into XtSamples
private Enums.XtSample AudioSampleToXtSample(AudioSample sample) {
return switch (sample) {
case UINT8 -> Enums.XtSample.UINT8;