diff --git a/com/danitheskunk/skunkworks/TestSound.java b/com/danitheskunk/skunkworks/TestSound.java index da74d6e..9f06c6d 100644 --- a/com/danitheskunk/skunkworks/TestSound.java +++ b/com/danitheskunk/skunkworks/TestSound.java @@ -1,5 +1,6 @@ package com.danitheskunk.skunkworks; +import com.danitheskunk.skunkworks.audio.AudioEngine; import org.lwjgl.openal.AL; import org.lwjgl.openal.ALC; @@ -14,63 +15,11 @@ import static org.lwjgl.openal.AL10.*; import static org.lwjgl.openal.ALC10.*; public class TestSound { - public static void main(String args[]) { - var device = alcOpenDevice((ByteBuffer) null); - if(device == 0) { - throw new RuntimeException("couldn't init openal device"); - } - var deviceCaps = ALC.createCapabilities(device); - var context = alcCreateContext(device, (IntBuffer) null); - if(context == 0) { - throw new RuntimeException("couldn't create openal context"); - } - alcMakeContextCurrent(context); - AL.createCapabilities(deviceCaps); - - var buf = alGenBuffers(); - int sampleRate = 44100; - var samples = new short[sampleRate * 2]; - for(int i = 0; i < samples.length; ++i) { - var s = Math.sin(2 * Math.PI * 220 * i / (double) sampleRate); - var s2 = Math.sin(2 * Math.PI * 440 * i / (double) sampleRate + - s * 10 * (88200 - i) / 88200); - //s = s * s2 * i / 88200; - s = s2 * (88200 - i) / 88200; - samples[i] = (short) (s * 32760); - } - - var bb = ByteBuffer.allocate(samples.length * 2); - for(int i = 0; i < samples.length; ++i) { - bb.putShort(samples[i]); - } - - var bytes = bb.array(); - - AudioInputStream audio; - var format = new AudioFormat(sampleRate, 16, 1, true, true); - audio = new AudioInputStream( - new ByteArrayInputStream(bytes), - format, - bytes.length - ); - try { - AudioSystem.write(audio, - AudioFileFormat.Type.WAVE, - new File("meow.wav") - ); - } catch(IOException e) { - throw new RuntimeException(e); - } - - alBufferData(buf, AL_FORMAT_MONO16, samples, sampleRate); - - var src = alGenSources(); - alSourcei(src, AL_BUFFER, buf); - alSourcePlay(src); - try { - Thread.sleep(3000); - } catch(InterruptedException e) { - throw new RuntimeException(e); + public static void main(String args[]) throws InterruptedException { + var engine = new AudioEngine(44100, 256, 8); + for(int i = 0; i < 120; ++i) { + engine.refill(); + Thread.sleep(20); } } } diff --git a/com/danitheskunk/skunkworks/audio/AudioEngine.java b/com/danitheskunk/skunkworks/audio/AudioEngine.java new file mode 100644 index 0000000..26ccb23 --- /dev/null +++ b/com/danitheskunk/skunkworks/audio/AudioEngine.java @@ -0,0 +1,94 @@ +package com.danitheskunk.skunkworks.audio; + +import org.lwjgl.openal.AL; +import org.lwjgl.openal.ALC; + +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.util.Random; + +import static org.lwjgl.openal.AL10.*; +import static org.lwjgl.openal.ALC10.*; + +public class AudioEngine { + private int sampleRate; + private int bufferSize; + private int bufferCount; + private long device; + private long context; + private int source; + private int tick = 0; + + public AudioEngine(int sampleRate, int bufferSize, int bufferCount) { + this.sampleRate = sampleRate; + this.bufferSize = bufferSize; + this.bufferCount = bufferCount; + + device = alcOpenDevice((ByteBuffer) null); + if(device == 0) { + throw new RuntimeException("couldn't init openal device"); + } + var deviceCaps = ALC.createCapabilities(device); + context = alcCreateContext(device, (IntBuffer) null); + if(context == 0) { + throw new RuntimeException("couldn't create openal context"); + } + alcMakeContextCurrent(context); + AL.createCapabilities(deviceCaps); + + this.source = alGenSources(); + + for(int i = 0; i < bufferCount; ++i) { + addEmptyBuffer(); + } + alSourcePlay(source); + } + + public void close() { + alcDestroyContext(context); + alcCloseDevice(device); + } + + private void fillBuffer(int buffer) { + var buf = new double[bufferSize * 2]; + for(int i = 0; i < bufferSize; ++i) { + double time = (double) tick / (double) sampleRate; + var s = Math.sin(2 * Math.PI * 440 * time); + buf[i * 2] = s; + buf[i * 2 + 1] = s; + ++tick; + } + + var shortBuffer = new short[bufferSize * 2]; + for(int i = 0; i < bufferSize * 2; ++i) { + shortBuffer[i] = (short) (buf[i] * 32760); + } + + alBufferData(buffer, AL_FORMAT_STEREO16, shortBuffer, sampleRate); + } + + private void addEmptyBuffer() { + var buf = alGenBuffers(); + fillBuffer(buf); + alSourceQueueBuffers(source, buf); + } + + public void refill() { + var bufCount = alGetSourcei(source, AL_BUFFERS_PROCESSED); + var bufs = new int[bufCount]; + alSourceUnqueueBuffers(source, bufs); + for(int i = 0; i < bufCount; ++i) { + fillBuffer(bufs[i]); + alSourceQueueBuffers(source, bufs[i]); + } + if(alGetSourcei(source, AL_SOURCE_STATE) != AL_PLAYING) { + System.out.println("buffer underrun, addng buffer"); + addEmptyBuffer(); + alSourcePlay(source); + } + } + + public int getSampleRate() { + return sampleRate; + } +}