diff --git a/pom.xml b/pom.xml
index 5c42214f..f9a283f4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -39,6 +39,17 @@
lwjgl-opengles
${lwjgl.version}
+
+ org.lwjgl
+ lwjgl-openal
+ ${lwjgl.version}
+
+
+ org.lwjgl
+ lwjgl-stb
+ ${lwjgl.version}
+
+
org.lwjgl
lwjgl
@@ -46,6 +57,7 @@
${lwjgl.natives}
runtime
+
org.lwjgl
lwjgl-assimp
@@ -53,6 +65,7 @@
${lwjgl.natives}
runtime
+
org.lwjgl
lwjgl-glfw
@@ -60,6 +73,7 @@
${lwjgl.natives}
runtime
+
org.lwjgl
lwjgl-opengl
@@ -67,6 +81,7 @@
${lwjgl.natives}
runtime
+
org.lwjgl
lwjgl-opengles
@@ -74,6 +89,21 @@
${lwjgl.natives}
runtime
+
+
+ org.lwjgl
+ lwjgl-openal
+ ${lwjgl.version}
+ ${lwjgl.natives}
+ runtime
+
+
+ org.lwjgl
+ lwjgl-stb
+ ${lwjgl.version}
+ ${lwjgl.natives}
+ runtime
+
org.joml
joml
diff --git a/src/main/java/electrosphere/audio/AudioBuffer.java b/src/main/java/electrosphere/audio/AudioBuffer.java
index 13ac9a41..6272ef15 100644
--- a/src/main/java/electrosphere/audio/AudioBuffer.java
+++ b/src/main/java/electrosphere/audio/AudioBuffer.java
@@ -1,5 +1,110 @@
package electrosphere.audio;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.IntBuffer;
+import java.nio.ShortBuffer;
+import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.SeekableByteChannel;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.lwjgl.BufferUtils;
+import static org.lwjgl.BufferUtils.createByteBuffer;
+import static org.lwjgl.openal.AL10.*;
+import static org.lwjgl.stb.STBVorbis.*;
+import org.lwjgl.stb.STBVorbisInfo;
+import org.lwjgl.system.MemoryStack;
+import org.lwjgl.system.MemoryUtil;
+import static org.lwjgl.system.MemoryUtil.NULL;
+
public class AudioBuffer {
+ private int bufferId;
+
+ private ByteBuffer vorbis = null;
+
+ private ShortBuffer pcm = null;
+
+ public AudioBuffer(String fileName) throws Exception {
+ bufferId = alGenBuffers();
+ try (STBVorbisInfo info = STBVorbisInfo.malloc()) {
+ ShortBuffer pcm = readVorbis(fileName, 32 * 1024, info);
+ // Copy to buffer
+ alBufferData(bufferId, info.channels() == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16, pcm, info.sample_rate());
+ }
+ }
+
+
+ private ShortBuffer readVorbis(String resource, int bufferSize, STBVorbisInfo info) throws Exception {
+ try (MemoryStack stack = MemoryStack.stackPush()) {
+ vorbis = AudioBuffer.ioResourceToByteBuffer(resource, bufferSize);
+ IntBuffer error = stack.mallocInt(1);
+ long decoder = stb_vorbis_open_memory(vorbis, error, null);
+ if (decoder == NULL) {
+ throw new RuntimeException("Failed to open Ogg Vorbis file. Error: " + error.get(0));
+ }
+
+ stb_vorbis_get_info(decoder, info);
+
+ int channels = info.channels();
+
+ int lengthSamples = stb_vorbis_stream_length_in_samples(decoder);
+
+ pcm = MemoryUtil.memAllocShort(lengthSamples);
+
+ pcm.limit(stb_vorbis_get_samples_short_interleaved(decoder, channels, pcm) * channels);
+ stb_vorbis_close(decoder);
+
+ return pcm;
+ }
+ }
+
+ public static ByteBuffer ioResourceToByteBuffer(String resource, int bufferSize) throws IOException {
+ ByteBuffer buffer;
+
+ Path path = Paths.get(resource);
+ if (Files.isReadable(path)) {
+ try (SeekableByteChannel fc = Files.newByteChannel(path)) {
+ buffer = BufferUtils.createByteBuffer((int) fc.size() + 1);
+ while (fc.read(buffer) != -1) ;
+ }
+ } else {
+ try (
+ InputStream source = AudioBuffer.class.getResourceAsStream(resource);
+ ReadableByteChannel rbc = Channels.newChannel(source)) {
+ buffer = createByteBuffer(bufferSize);
+
+ while (true) {
+ int bytes = rbc.read(buffer);
+ if (bytes == -1) {
+ break;
+ }
+ if (buffer.remaining() == 0) {
+ int capacity = buffer.capacity();
+ ByteBuffer newBuffer = createByteBuffer(capacity * 2);
+ for(int i = 0; i < capacity; i++){
+ newBuffer.put(buffer.get());
+ }
+ buffer = newBuffer;
+ }
+ }
+ }
+ }
+
+ buffer.flip();
+ return buffer;
+ }
+
+
+ public int getBufferId() {
+ return this.bufferId;
+ }
+
+ public void cleanup() {
+ alDeleteBuffers(this.bufferId);
+ }
+
}
diff --git a/src/main/java/electrosphere/audio/AudioEngine.java b/src/main/java/electrosphere/audio/AudioEngine.java
index a78428f2..2d872fea 100644
--- a/src/main/java/electrosphere/audio/AudioEngine.java
+++ b/src/main/java/electrosphere/audio/AudioEngine.java
@@ -1,5 +1,46 @@
package electrosphere.audio;
+import java.nio.ByteBuffer;
+import java.nio.IntBuffer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.joml.Matrix4f;
+import org.lwjgl.openal.*;
+import static org.lwjgl.openal.ALC10.*;
+import static org.lwjgl.system.MemoryUtil.NULL;
+
public class AudioEngine {
-
+ private long device;
+
+ private long context;
+
+ private AudioListener listener;
+
+ private final List soundBufferList;
+
+ private final Map soundSourceMap;
+
+ private final Matrix4f cameraMatrix;
+
+ public AudioEngine() {
+ soundBufferList = new ArrayList<>();
+ soundSourceMap = new HashMap<>();
+ cameraMatrix = new Matrix4f();
+ }
+
+ public void init() throws Exception {
+ this.device = alcOpenDevice((ByteBuffer) null);
+ if (device == NULL) {
+ throw new IllegalStateException("Failed to open the default OpenAL device.");
+ }
+ ALCCapabilities deviceCaps = ALC.createCapabilities(device);
+ this.context = alcCreateContext(device, (IntBuffer) null);
+ if (context == NULL) {
+ throw new IllegalStateException("Failed to create OpenAL context.");
+ }
+ alcMakeContextCurrent(context);
+ AL.createCapabilities(deviceCaps);
+ }
}
diff --git a/src/main/java/electrosphere/audio/AudioListener.java b/src/main/java/electrosphere/audio/AudioListener.java
new file mode 100644
index 00000000..2de5b51b
--- /dev/null
+++ b/src/main/java/electrosphere/audio/AudioListener.java
@@ -0,0 +1,42 @@
+package electrosphere.audio;
+
+import org.joml.Vector3f;
+import static org.lwjgl.openal.AL10.*;
+
+/**
+ *
+ * @author amaterasu
+ */
+public class AudioListener {
+
+ public AudioListener() {
+ this(new Vector3f(0, 0, 0));
+ }
+
+ public AudioListener(Vector3f position) {
+ alListener3f(AL_POSITION, position.x, position.y, position.z);
+ alListener3f(AL_VELOCITY, 0, 0, 0);
+
+ }
+
+ public void setSpeed(Vector3f speed) {
+ alListener3f(AL_VELOCITY, speed.x, speed.y, speed.z);
+ }
+
+ public void setPosition(Vector3f position) {
+ alListener3f(AL_POSITION, position.x, position.y, position.z);
+ }
+
+ public void setOrientation(Vector3f at, Vector3f up) {
+ float[] data = new float[6];
+ data[0] = at.x;
+ data[1] = at.y;
+ data[2] = at.z;
+ data[3] = up.x;
+ data[4] = up.y;
+ data[5] = up.z;
+ alListenerfv(AL_ORIENTATION, data);
+ }
+
+
+}
diff --git a/src/main/java/electrosphere/audio/AudioSource.java b/src/main/java/electrosphere/audio/AudioSource.java
new file mode 100644
index 00000000..65ed6f9d
--- /dev/null
+++ b/src/main/java/electrosphere/audio/AudioSource.java
@@ -0,0 +1,67 @@
+package electrosphere.audio;
+
+import org.joml.Vector3f;
+import static org.lwjgl.openal.AL10.*;
+
+/**
+ *
+ * @author amaterasu
+ */
+public class AudioSource {
+
+ int sourceId;
+
+
+ public AudioSource(boolean loop, boolean relative){
+ this.sourceId = alGenSources();
+ if (loop) {
+ alSourcei(sourceId, AL_LOOPING, AL_TRUE);
+ }
+ if (relative) {
+ alSourcei(sourceId, AL_SOURCE_RELATIVE, AL_TRUE);
+ }
+ }
+
+
+ public void setBuffer(int bufferId) {
+ stop();
+ alSourcei(sourceId, AL_BUFFER, bufferId);
+ }
+
+ public void setPosition(Vector3f position) {
+ alSource3f(sourceId, AL_POSITION, position.x, position.y, position.z);
+ }
+
+ public void setSpeed(Vector3f speed) {
+ alSource3f(sourceId, AL_VELOCITY, speed.x, speed.y, speed.z);
+ }
+
+ public void setGain(float gain) {
+ alSourcef(sourceId, AL_GAIN, gain);
+ }
+
+ public void setProperty(int param, float value) {
+ alSourcef(sourceId, param, value);
+ }
+
+ public void play() {
+ alSourcePlay(sourceId);
+ }
+
+ public boolean isPlaying() {
+ return alGetSourcei(sourceId, AL_SOURCE_STATE) == AL_PLAYING;
+ }
+
+ public void pause() {
+ alSourcePause(sourceId);
+ }
+
+ public void stop() {
+ alSourceStop(sourceId);
+ }
+
+ public void cleanup() {
+ stop();
+ alDeleteSources(sourceId);
+ }
+}