True OpenAL AudioDevice

Any community contributions to libgdx go here! Some may get included in the core API when permission is granted.

True OpenAL AudioDevice

Postby schmop » Tue Jun 28, 2011 4:18 pm


I was toying around with libgdx's audio devices (with the lwjgl backend, which uses OpenAL, as does Jogl) to see if I could stream incoming audio with the writeSamples methods... and I couldn't! Sampling rate and #channels parameters seemed to be ignored (or at least weirdly treated) and repeated calls to the method left gaps in the audio output. Checking out the sources, I saw there was no OpenAl audio device, but a java sound device as a temporary replacement. So I went about writing the missing device:

Code: Select all
import java.nio.IntBuffer;
import java.nio.ShortBuffer;
import java.util.Vector;

import org.lwjgl.openal.AL10;

import com.badlogic.gdx.utils.BufferUtils;
import com.badlogic.gdx.utils.GdxRuntimeException;

public class OpenALDevice implements AudioDevice
   int sourceId;
   IntBuffer bufferIds;
   boolean isMono;
   int samplingRate;
   ShortBuffer transferBuffer;
   int currentBuffer, currentCursor;
   Vector<Integer> idleBuffers;
   int bufferSize = 2048;
   int nBuffers = 4;
   int errnum;
   public OpenALDevice(int samplingRate, boolean isMono)
      this.sourceId = AL10.alGenSources();
      if ((errnum = AL10.alGetError()) != AL10.AL_NO_ERROR)
         throw new GdxRuntimeException("Couldn't create source - "+errnum);
      this.bufferIds = BufferUtils.newIntBuffer(nBuffers);
      if ((errnum = AL10.alGetError()) != AL10.AL_NO_ERROR)
         throw new GdxRuntimeException("Couldn't create buffers - "+errnum);
      this.isMono = isMono;
      this.samplingRate = samplingRate;
      this.transferBuffer = BufferUtils.newShortBuffer(bufferSize);
      this.currentBuffer = -1;
      this.currentCursor = 0;
      for (int i=0;i<transferBuffer.capacity();i++)
         transferBuffer.put(i, (short)0);
      this.idleBuffers = new Vector<Integer>(bufferIds.capacity());
      for (int i=0;i<bufferIds.capacity();i++)
         AL10.alBufferData(bufferIds.get(i), isMono ? AL10.AL_FORMAT_MONO16 : AL10.AL_FORMAT_STEREO16,
            transferBuffer, samplingRate);
   long timeout = 5000;
   int acquireBuffer()
      if (!idleBuffers.isEmpty())
         return idleBuffers.remove(idleBuffers.size()-1);
      else if (AL10.alGetSourcei(sourceId, AL10.AL_SOURCE_STATE) != AL10.AL_PLAYING)
         while (AL10.alGetSourcei(sourceId, AL10.AL_BUFFERS_PROCESSED) > 0)
         return idleBuffers.remove(idleBuffers.size()-1);
         long start = System.currentTimeMillis();
         while (AL10.alGetSourcei(sourceId, AL10.AL_BUFFERS_PROCESSED) == 0)
            if (System.currentTimeMillis()-start >= timeout)
               return -1;
            try {Thread.sleep(50);}
            catch (Exception e) {}
         return AL10.alSourceUnqueueBuffers(sourceId);
   void submitBuffer(int bufferId)
      AL10.alBufferData(bufferId, isMono ? AL10.AL_FORMAT_MONO16 : AL10.AL_FORMAT_STEREO16,
         transferBuffer, samplingRate);
      AL10.alSourceQueueBuffers(sourceId, bufferId);
      if (AL10.alGetSourcei(sourceId, AL10.AL_SOURCE_STATE) != AL10.AL_PLAYING)
   @Override public void dispose()

   @Override public int getLatency() {return (int)(1000*bufferSize*nBuffers*(isMono ? 1. : .5)/samplingRate);}

   @Override public boolean isMono() {return isMono;}

   @Override public void writeSamples(short [] samples, int offset, int numSamples)
      while (numSamples > 0)
         if (currentBuffer == -1)
            currentBuffer = acquireBuffer();
            if (currentBuffer == -1)
               //throw new GdxRuntimeException("OpenAL device timeout");
            currentCursor = 0;
         int nWrite = Math.min(transferBuffer.capacity()-currentCursor, numSamples);
         transferBuffer.put(samples, offset, nWrite);
         currentCursor += nWrite;
         offset += nWrite;
         numSamples -= nWrite;
         if (currentCursor == transferBuffer.capacity())
            currentBuffer = -1;
            currentCursor = 0;

   @Override public void writeSamples(float [] samples, int offset, int numSamples)

Probably not robust enough for something advanced, but the AudioDevice interface doesn't give much control anyway. I didn't do the writeSamples variant with floats, I got lazy...
Anyways, I hope it can be of use to someone
Posts: 17
Joined: Tue Jun 21, 2011 10:55 am

Re: True OpenAL AudioDevice

Postby mzechner » Tue Jun 28, 2011 6:15 pm

Can we integrate this in the core with your permission? You'd become a contributor. The only thing i'd need is a signed CLA (yeah, i know that sucks, legal stuff always does...).

I wanted to write this for 1.0, but didn't get around doing it yet.
Site Admin
Posts: 4879
Joined: Sat Jul 10, 2010 3:50 pm

Return to Libgdx Contributions

Who is online

Users browsing this forum: No registered users and 1 guest