Audio engine basics
This commit is contained in:
parent
0f4a764470
commit
dd30e73660
30
pom.xml
30
pom.xml
@ -39,6 +39,17 @@
|
|||||||
<artifactId>lwjgl-opengles</artifactId>
|
<artifactId>lwjgl-opengles</artifactId>
|
||||||
<version>${lwjgl.version}</version>
|
<version>${lwjgl.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.lwjgl</groupId>
|
||||||
|
<artifactId>lwjgl-openal</artifactId>
|
||||||
|
<version>${lwjgl.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.lwjgl</groupId>
|
||||||
|
<artifactId>lwjgl-stb</artifactId>
|
||||||
|
<version>${lwjgl.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- generic LWJGL runtimes -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.lwjgl</groupId>
|
<groupId>org.lwjgl</groupId>
|
||||||
<artifactId>lwjgl</artifactId>
|
<artifactId>lwjgl</artifactId>
|
||||||
@ -46,6 +57,7 @@
|
|||||||
<classifier>${lwjgl.natives}</classifier>
|
<classifier>${lwjgl.natives}</classifier>
|
||||||
<scope>runtime</scope>
|
<scope>runtime</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- assimp runtimes -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.lwjgl</groupId>
|
<groupId>org.lwjgl</groupId>
|
||||||
<artifactId>lwjgl-assimp</artifactId>
|
<artifactId>lwjgl-assimp</artifactId>
|
||||||
@ -53,6 +65,7 @@
|
|||||||
<classifier>${lwjgl.natives}</classifier>
|
<classifier>${lwjgl.natives}</classifier>
|
||||||
<scope>runtime</scope>
|
<scope>runtime</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- GLFW runtimes -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.lwjgl</groupId>
|
<groupId>org.lwjgl</groupId>
|
||||||
<artifactId>lwjgl-glfw</artifactId>
|
<artifactId>lwjgl-glfw</artifactId>
|
||||||
@ -60,6 +73,7 @@
|
|||||||
<classifier>${lwjgl.natives}</classifier>
|
<classifier>${lwjgl.natives}</classifier>
|
||||||
<scope>runtime</scope>
|
<scope>runtime</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- opengl runtimes -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.lwjgl</groupId>
|
<groupId>org.lwjgl</groupId>
|
||||||
<artifactId>lwjgl-opengl</artifactId>
|
<artifactId>lwjgl-opengl</artifactId>
|
||||||
@ -67,6 +81,7 @@
|
|||||||
<classifier>${lwjgl.natives}</classifier>
|
<classifier>${lwjgl.natives}</classifier>
|
||||||
<scope>runtime</scope>
|
<scope>runtime</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- Embedded opengl runtimes -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.lwjgl</groupId>
|
<groupId>org.lwjgl</groupId>
|
||||||
<artifactId>lwjgl-opengles</artifactId>
|
<artifactId>lwjgl-opengles</artifactId>
|
||||||
@ -74,6 +89,21 @@
|
|||||||
<classifier>${lwjgl.natives}</classifier>
|
<classifier>${lwjgl.natives}</classifier>
|
||||||
<scope>runtime</scope>
|
<scope>runtime</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- Audio runtimes -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.lwjgl</groupId>
|
||||||
|
<artifactId>lwjgl-openal</artifactId>
|
||||||
|
<version>${lwjgl.version}</version>
|
||||||
|
<classifier>${lwjgl.natives}</classifier>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.lwjgl</groupId>
|
||||||
|
<artifactId>lwjgl-stb</artifactId>
|
||||||
|
<version>${lwjgl.version}</version>
|
||||||
|
<classifier>${lwjgl.natives}</classifier>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.joml</groupId>
|
<groupId>org.joml</groupId>
|
||||||
<artifactId>joml</artifactId>
|
<artifactId>joml</artifactId>
|
||||||
|
|||||||
@ -1,5 +1,110 @@
|
|||||||
package electrosphere.audio;
|
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 {
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,46 @@
|
|||||||
package electrosphere.audio;
|
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 {
|
public class AudioEngine {
|
||||||
|
private long device;
|
||||||
|
|
||||||
|
private long context;
|
||||||
|
|
||||||
|
private AudioListener listener;
|
||||||
|
|
||||||
|
private final List<AudioBuffer> soundBufferList;
|
||||||
|
|
||||||
|
private final Map<String, AudioSource> 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
42
src/main/java/electrosphere/audio/AudioListener.java
Normal file
42
src/main/java/electrosphere/audio/AudioListener.java
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
67
src/main/java/electrosphere/audio/AudioSource.java
Normal file
67
src/main/java/electrosphere/audio/AudioSource.java
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user