jogamp.opengl.openal.av.ALAudioSink Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jogl-all Show documentation
Show all versions of jogl-all Show documentation
Java™ Binding for the OpenGL® API
/**
* Copyright 2013 JogAmp Community. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of JogAmp Community.
*/
package jogamp.opengl.openal.av;
import jogamp.opengl.util.av.SyncedRingbuffer;
import com.jogamp.openal.AL;
import com.jogamp.openal.ALC;
import com.jogamp.openal.ALCcontext;
import com.jogamp.openal.ALCdevice;
import com.jogamp.openal.ALFactory;
import com.jogamp.opengl.util.av.AudioSink;
/***
* OpenAL Audio Sink
*/
public class ALAudioSink implements AudioSink {
/** Chunk of audio processed at one time. FIXME: Parameterize .. */
public static final int BUFFER_SIZE = 4096;
public static final int SAMPLES_PER_BUFFER = BUFFER_SIZE / 2;
private static final ALC alc;
private static final AL al;
private static final boolean staticAvailable;
private String deviceSpecifier;
private ALCdevice device;
private ALCcontext context;
/** Sample period in seconds */
public float samplePeriod;
/** Buffer period in seconds */
public float bufferPeriod;
static class ActiveBuffer {
ActiveBuffer(Integer name, int size) {
this.name = name;
this.size = size;
}
public final Integer name;
public final int size;
public String toString() { return "ABuffer[name "+name+", size "+size+"]"; }
}
int[] alBuffers = null;
private SyncedRingbuffer alBufferAvail = null;
private SyncedRingbuffer alBufferPlaying = null;
private int alBufferBytesQueued = 0;
private int[] alSource = null;
private AudioDataFormat chosenFormat;
private int alFormat;
private boolean initialized;
static {
ALC _alc = null;
AL _al = null;
try {
_alc = ALFactory.getALC();
_al = ALFactory.getAL();
} catch(Throwable t) {
if( DEBUG ) {
System.err.println("ALAudioSink: Catched "+t.getClass().getName()+": "+t.getMessage());
t.printStackTrace();
}
}
alc = _alc;
al = _al;
staticAvailable = null != alc && null != al;
}
public ALAudioSink() {
initialized = false;
chosenFormat = null;
if( !staticAvailable ) {
return;
}
try {
// Get handle to default device.
device = alc.alcOpenDevice(null);
if (device == null) {
throw new RuntimeException("ALAudioSink: Error opening default OpenAL device");
}
// Get the device specifier.
deviceSpecifier = alc.alcGetString(device, ALC.ALC_DEVICE_SPECIFIER);
if (deviceSpecifier == null) {
throw new RuntimeException("ALAudioSink: Error getting specifier for default OpenAL device");
}
// Create audio context.
context = alc.alcCreateContext(device, null);
if (context == null) {
throw new RuntimeException("ALAudioSink: Error creating OpenAL context");
}
// Set active context.
alc.alcMakeContextCurrent(context);
// Check for an error.
if ( alc.alcGetError(device) != ALC.ALC_NO_ERROR ) {
throw new RuntimeException("ALAudioSink: Error making OpenAL context current");
}
// Create source
{
alSource = new int[1];
al.alGenSources(1, alSource, 0);
final int err = al.alGetError();
if( err != AL.AL_NO_ERROR ) {
alSource = null;
throw new RuntimeException("ALAudioSink: Error generating Source: 0x"+Integer.toHexString(err));
}
}
if( DEBUG ) {
System.err.println("ALAudioSink: Using device: " + deviceSpecifier);
}
initialized = true;
return;
} catch ( Exception e ) {
if( DEBUG ) {
System.err.println(e.getMessage());
}
destroy();
}
}
@Override
public String toString() {
final int alSrcName = null != alSource ? alSource[0] : 0;
final int alBuffersLen = null != alBuffers ? alBuffers.length : 0;
return "ALAudioSink[init "+initialized+", device "+deviceSpecifier+", ctx "+context+", alSource "+alSrcName+
", chosen "+chosenFormat+", alFormat "+toHexString(alFormat)+
", buffers[total "+alBuffersLen+", avail "+alBufferAvail.size()+", "+alBufferPlaying.getFreeSlots()+
", queued[bufferCount "+alBufferPlaying.size()+", "+getQueuedTime() + " ms, " + alBufferBytesQueued+" bytes]";
}
@Override
public AudioDataFormat getPreferredFormat() {
return DefaultFormat;
}
@Override
public AudioDataFormat initSink(AudioDataFormat requestedFormat, int bufferCount) {
if( !staticAvailable ) {
return null;
}
samplePeriod = 1.0f / requestedFormat.sampleRate;
bufferPeriod = samplePeriod * SAMPLES_PER_BUFFER;
switch( requestedFormat.channelCount ) {
case 1: {
switch ( requestedFormat.sampleSize ) {
case 8:
alFormat = AL.AL_FORMAT_MONO8; break;
case 16:
alFormat = AL.AL_FORMAT_MONO16; break;
default:
return null;
}
} break;
case 2:
switch ( requestedFormat.sampleSize ) {
case 8:
alFormat = AL.AL_FORMAT_STEREO8; break;
case 16:
alFormat = AL.AL_FORMAT_STEREO16; break;
default:
return null;
}
}
// Allocate buffers
destroyBuffers();
{
alBuffers = new int[bufferCount];
al.alGenBuffers(bufferCount, alBuffers, 0);
final int err = al.alGetError();
if( err != AL.AL_NO_ERROR ) {
alBuffers = null;
throw new RuntimeException("ALAudioSink: Error generating Buffers: 0x"+Integer.toHexString(err));
}
final Integer[] alBufferRingArray = new Integer[bufferCount];
for(int i=0; i(alBufferRingArray, true /* full */);
alBufferPlaying = new SyncedRingbuffer(new ActiveBuffer[bufferCount], false /* full */);
}
chosenFormat = requestedFormat;
return chosenFormat;
}
private void destroyBuffers() {
if( !staticAvailable ) {
return;
}
if( null != alBuffers ) {
try {
al.alDeleteBuffers(alBufferAvail.capacity(), alBuffers, 0);
} catch (Throwable t) {
if( DEBUG ) {
System.err.println("Catched "+t.getClass().getName()+": "+t.getMessage());
t.printStackTrace();
}
}
alBufferAvail.clear(true);
alBufferAvail = null;
alBufferPlaying.clear(true);
alBufferPlaying = null;
alBufferBytesQueued = 0;
alBuffers = null;
}
}
@Override
public void destroy() {
initialized = false;
if( !staticAvailable ) {
return;
}
if( null != alSource ) {
try {
al.alDeleteSources(1, alSource, 0);
} catch (Throwable t) {
if( DEBUG ) {
System.err.println("Catched "+t.getClass().getName()+": "+t.getMessage());
t.printStackTrace();
}
}
alSource = null;
}
destroyBuffers();
if( null != context ) {
try {
alc.alcDestroyContext(context);
} catch (Throwable t) {
if( DEBUG ) {
System.err.println("Catched "+t.getClass().getName()+": "+t.getMessage());
t.printStackTrace();
}
}
context = null;
}
if( null != device ) {
try {
alc.alcCloseDevice(device);
} catch (Throwable t) {
if( DEBUG ) {
System.err.println("Catched "+t.getClass().getName()+": "+t.getMessage());
t.printStackTrace();
}
}
device = null;
}
chosenFormat = null;
}
@Override
public boolean isInitialized() {
return initialized;
}
private final void dequeueBuffer(boolean wait) {
int alErr = AL.AL_NO_ERROR;
final int[] val=new int[1];
do {
al.alGetSourcei(alSource[0], AL.AL_BUFFERS_PROCESSED, val, 0);
alErr = al.alGetError();
if( AL.AL_NO_ERROR != alErr ) {
throw new RuntimeException("ALError "+toHexString(alErr)+" while quering processed buffers at source. "+this);
}
if( wait && val[0] <= 0 ) {
try {
Thread.sleep(1);
} catch (InterruptedException e){
}
}
} while (val[0] <= 0);
final int processedBuffers = val[0];
if( processedBuffers > 0 ) {
int[] buffers=new int[processedBuffers];
al.alSourceUnqueueBuffers(alSource[0], processedBuffers, buffers, 0);
alErr = al.alGetError();
if( AL.AL_NO_ERROR != alErr ) {
throw new RuntimeException("ALError "+toHexString(alErr)+" while dequeueing "+processedBuffers+" processed buffers. "+this);
}
for ( int i=0; i