diff RecordAudioRenderer.java @ 0:f9476ff7637e

initial forking of open-al to create multiple listeners
author Robert McIntyre <rlm@mit.edu>
date Tue, 25 Oct 2011 13:02:31 -0700
parents
children
line wrap: on
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/RecordAudioRenderer.java	Tue Oct 25 13:02:31 2011 -0700
     1.3 @@ -0,0 +1,1183 @@
     1.4 +package com.jme3.capture;
     1.5 +
     1.6 +import static org.lwjgl.openal.AL10.AL_BUFFER;
     1.7 +import static org.lwjgl.openal.AL10.AL_BUFFERS_PROCESSED;
     1.8 +import static org.lwjgl.openal.AL10.AL_CONE_INNER_ANGLE;
     1.9 +import static org.lwjgl.openal.AL10.AL_CONE_OUTER_ANGLE;
    1.10 +import static org.lwjgl.openal.AL10.AL_CONE_OUTER_GAIN;
    1.11 +import static org.lwjgl.openal.AL10.AL_DIRECTION;
    1.12 +import static org.lwjgl.openal.AL10.AL_FALSE;
    1.13 +import static org.lwjgl.openal.AL10.AL_FORMAT_MONO16;
    1.14 +import static org.lwjgl.openal.AL10.AL_FORMAT_MONO8;
    1.15 +import static org.lwjgl.openal.AL10.AL_FORMAT_STEREO16;
    1.16 +import static org.lwjgl.openal.AL10.AL_FORMAT_STEREO8;
    1.17 +import static org.lwjgl.openal.AL10.AL_GAIN;
    1.18 +import static org.lwjgl.openal.AL10.AL_LOOPING;
    1.19 +import static org.lwjgl.openal.AL10.AL_MAX_DISTANCE;
    1.20 +import static org.lwjgl.openal.AL10.AL_ORIENTATION;
    1.21 +import static org.lwjgl.openal.AL10.AL_PAUSED;
    1.22 +import static org.lwjgl.openal.AL10.AL_PITCH;
    1.23 +import static org.lwjgl.openal.AL10.AL_POSITION;
    1.24 +import static org.lwjgl.openal.AL10.AL_REFERENCE_DISTANCE;
    1.25 +import static org.lwjgl.openal.AL10.AL_RENDERER;
    1.26 +import static org.lwjgl.openal.AL10.AL_SOURCE_RELATIVE;
    1.27 +import static org.lwjgl.openal.AL10.AL_SOURCE_STATE;
    1.28 +import static org.lwjgl.openal.AL10.AL_STOPPED;
    1.29 +import static org.lwjgl.openal.AL10.AL_TRUE;
    1.30 +import static org.lwjgl.openal.AL10.AL_VELOCITY;
    1.31 +import static org.lwjgl.openal.AL10.AL_VENDOR;
    1.32 +import static org.lwjgl.openal.AL10.AL_VERSION;
    1.33 +import static org.lwjgl.openal.AL10.alBufferData;
    1.34 +import static org.lwjgl.openal.AL10.alDeleteBuffers;
    1.35 +import static org.lwjgl.openal.AL10.alDeleteSources;
    1.36 +import static org.lwjgl.openal.AL10.alGenBuffers;
    1.37 +import static org.lwjgl.openal.AL10.alGenSources;
    1.38 +import static org.lwjgl.openal.AL10.alGetError;
    1.39 +import static org.lwjgl.openal.AL10.alGetSourcei;
    1.40 +import static org.lwjgl.openal.AL10.alGetString;
    1.41 +import static org.lwjgl.openal.AL10.alListener;
    1.42 +import static org.lwjgl.openal.AL10.alListener3f;
    1.43 +import static org.lwjgl.openal.AL10.alListenerf;
    1.44 +import static org.lwjgl.openal.AL10.alSource3f;
    1.45 +import static org.lwjgl.openal.AL10.alSourcePause;
    1.46 +import static org.lwjgl.openal.AL10.alSourcePlay;
    1.47 +import static org.lwjgl.openal.AL10.alSourceQueueBuffers;
    1.48 +import static org.lwjgl.openal.AL10.alSourceStop;
    1.49 +import static org.lwjgl.openal.AL10.alSourceUnqueueBuffers;
    1.50 +import static org.lwjgl.openal.AL10.alSourcef;
    1.51 +import static org.lwjgl.openal.AL10.alSourcei;
    1.52 +
    1.53 +import java.lang.reflect.Field;
    1.54 +import java.nio.ByteBuffer;
    1.55 +import java.nio.FloatBuffer;
    1.56 +import java.nio.IntBuffer;
    1.57 +import java.util.ArrayList;
    1.58 +import java.util.Vector;
    1.59 +import java.util.concurrent.atomic.AtomicBoolean;
    1.60 +import java.util.logging.Level;
    1.61 +import java.util.logging.Logger;
    1.62 +
    1.63 +import org.lwjgl.LWJGLException;
    1.64 +import org.lwjgl.openal.AL;
    1.65 +import org.lwjgl.openal.AL11;
    1.66 +import org.lwjgl.openal.ALC10;
    1.67 +import org.lwjgl.openal.ALCdevice;
    1.68 +import org.lwjgl.openal.EFX10;
    1.69 +import org.lwjgl.openal.OpenALException;
    1.70 +
    1.71 +import com.jme3.audio.AudioBuffer;
    1.72 +import com.jme3.audio.AudioData;
    1.73 +import com.jme3.audio.AudioNode;
    1.74 +import com.jme3.audio.AudioNode.Status;
    1.75 +import com.jme3.audio.AudioParam;
    1.76 +import com.jme3.audio.AudioRenderer;
    1.77 +import com.jme3.audio.AudioStream;
    1.78 +import com.jme3.audio.Environment;
    1.79 +import com.jme3.audio.Filter;
    1.80 +import com.jme3.audio.Listener;
    1.81 +import com.jme3.audio.ListenerParam;
    1.82 +import com.jme3.audio.LowPassFilter;
    1.83 +import com.jme3.math.Vector3f;
    1.84 +import com.jme3.util.BufferUtils;
    1.85 +
    1.86 +
    1.87 +
    1.88 +public class RecordAudioRenderer implements AudioRenderer, Runnable {
    1.89 +
    1.90 +	
    1.91 +	
    1.92 +	public static void getMainSamples(){
    1.93 +		
    1.94 +	}
    1.95 +	
    1.96 +	
    1.97 +   private static final Logger logger = Logger.getLogger(RecordAudioRenderer.class.getName());
    1.98 +
    1.99 +   // When multiplied by STREAMING_BUFFER_COUNT, will equal 44100 * 2 * 2
   1.100 +   // which is exactly 1 second of audio.
   1.101 +   private static final int BUFFER_SIZE = 35280;
   1.102 +   private static final int STREAMING_BUFFER_COUNT = 5;
   1.103 +
   1.104 +   private final static int MAX_NUM_CHANNELS = 2;
   1.105 +   private IntBuffer ib = BufferUtils.createIntBuffer(1);
   1.106 +   private final FloatBuffer fb = BufferUtils.createVector3Buffer(2);
   1.107 +   private final ByteBuffer nativeBuf = BufferUtils.createByteBuffer(BUFFER_SIZE);
   1.108 +   private final byte[] arrayBuf = new byte[BUFFER_SIZE];
   1.109 +
   1.110 +   private int[] channels;
   1.111 +   private AudioNode[] chanSrcs;
   1.112 +   private int nextChan = 0;
   1.113 +   private ArrayList<Integer> freeChans = new ArrayList<Integer>();
   1.114 +
   1.115 +   private Listener listener;
   1.116 +   private boolean audioDisabled = false;
   1.117 +
   1.118 +   private boolean supportEfx = false;
   1.119 +   private int auxSends = 0;
   1.120 +   private int reverbFx = -1;
   1.121 +   private int reverbFxSlot = -1;
   1.122 +
   1.123 +   // RLM: this is to call the native methods which require the OpenAL device ID.
   1.124 +   // currently it is obtained through reflection.
   1.125 +   private long deviceID;
   1.126 +   
   1.127 +   // Update audio 20 times per second
   1.128 +   private static final float UPDATE_RATE = 0.05f;
   1.129 +
   1.130 +   private final Thread audioThread = new Thread(this, "jME3 Audio Thread");
   1.131 +   private final AtomicBoolean threadLock = new AtomicBoolean(false);
   1.132 +
   1.133 +   public RecordAudioRenderer(){
   1.134 +   }
   1.135 +
   1.136 +   public static native void helloEveryone();
   1.137 +   
   1.138 +   
   1.139 +   public static native void nstep(long device);
   1.140 +   public void step(){
   1.141 +	   nstep(this.deviceID);
   1.142 +   }
   1.143 +	
   1.144 +	
   1.145 +	
   1.146 +	public void getMainSamples(ByteBuffer buffer){
   1.147 +		ngetMainSamples(this.deviceID, buffer, buffer.position());
   1.148 +	}
   1.149 +	public static native void ngetMainSamples(long device, ByteBuffer buffer, int position);
   1.150 +	
   1.151 +	
   1.152 +	public void getAuxSamples(ByteBuffer buffer){
   1.153 +		ngetAuxSamples(this.deviceID, buffer, buffer.position());
   1.154 +	}
   1.155 +	public static native void ngetAuxSamples(long device, ByteBuffer buffer, int position);
   1.156 +
   1.157 +   
   1.158 +   
   1.159 +   public void initialize(){
   1.160 +       if (!audioThread.isAlive()){
   1.161 +           audioThread.setDaemon(true);
   1.162 +           audioThread.setPriority(Thread.NORM_PRIORITY+1);
   1.163 +           audioThread.start();
   1.164 +       }else{
   1.165 +           throw new IllegalStateException("Initialize already called");
   1.166 +       }
   1.167 +   }
   1.168 +
   1.169 +   private void checkDead(){
   1.170 +       if (audioThread.getState() == Thread.State.TERMINATED)
   1.171 +           throw new IllegalStateException("Audio thread is terminated");
   1.172 +   }
   1.173 +
   1.174 +   public void run(){
   1.175 +       initInThread();
   1.176 +       synchronized (threadLock){
   1.177 +           threadLock.set(true);
   1.178 +           threadLock.notifyAll();
   1.179 +       }
   1.180 +       
   1.181 +       
   1.182 +       helloEveryone();
   1.183 +       System.out.println("AudioRecorder: Trying to call native methods.");
   1.184 +	   System.out.println("our device ID is : " + this.deviceID);
   1.185 +
   1.186 +	   
   1.187 +	   
   1.188 +	   
   1.189 +       long updateRateNanos = (long) (UPDATE_RATE * 1000000000);
   1.190 +       mainloop: while (true){
   1.191 +           long startTime = System.nanoTime();
   1.192 +
   1.193 +           if (Thread.interrupted())
   1.194 +               break;
   1.195 +
   1.196 +           synchronized (threadLock){
   1.197 +               updateInThread(UPDATE_RATE);
   1.198 +           }
   1.199 +
   1.200 +           long endTime = System.nanoTime();
   1.201 +           long diffTime = endTime - startTime;
   1.202 +
   1.203 +           if (diffTime < updateRateNanos){
   1.204 +               long desiredEndTime = startTime + updateRateNanos;
   1.205 +               while (System.nanoTime() < desiredEndTime){
   1.206 +                   try{
   1.207 +                       Thread.sleep(1);
   1.208 +                   }catch (InterruptedException ex){
   1.209 +                       break mainloop;
   1.210 +                   }
   1.211 +               }
   1.212 +           }
   1.213 +       }
   1.214 +
   1.215 +       synchronized (threadLock){
   1.216 +           cleanupInThread();
   1.217 +       }
   1.218 +   }
   1.219 +
   1.220 +   public void initInThread(){
   1.221 +       try{
   1.222 +           if (!AL.isCreated()){
   1.223 +        	   AL.create("Aurellem", 44100, 15, false);	
   1.224 +           }
   1.225 +       }catch (OpenALException ex){
   1.226 +           logger.log(Level.SEVERE, "Failed to load audio library", ex);
   1.227 +           audioDisabled = true;
   1.228 +           return;
   1.229 +       }catch (LWJGLException ex){
   1.230 +           logger.log(Level.SEVERE, "Failed to load audio library", ex);
   1.231 +           audioDisabled = true;
   1.232 +           return;
   1.233 +       }
   1.234 +
   1.235 +       ALCdevice device = AL.getDevice();
   1.236 +       
   1.237 +       // RLM: use reflection to grab the ID of our device for use later.
   1.238 +       try {
   1.239 +    	   Field deviceIDField;
   1.240 +    	   deviceIDField = ALCdevice.class.getDeclaredField("device");
   1.241 +    	   deviceIDField.setAccessible(true);
   1.242 +    	   try {deviceID = (Long)deviceIDField.get(device);} 
   1.243 +    	   catch (IllegalArgumentException e) {e.printStackTrace();} 
   1.244 +    	   catch (IllegalAccessException e) {e.printStackTrace();}
   1.245 +    	   deviceIDField.setAccessible(false);} 
   1.246 +       catch (SecurityException e) {e.printStackTrace();} 
   1.247 +       catch (NoSuchFieldException e) {e.printStackTrace();}
   1.248 +       
   1.249 +       
   1.250 +       
   1.251 +       String deviceName = ALC10.alcGetString(device, ALC10.ALC_DEVICE_SPECIFIER);
   1.252 +
   1.253 +       logger.log(Level.FINER, "Audio Device: {0}", deviceName);
   1.254 +       logger.log(Level.FINER, "Audio Vendor: {0}", alGetString(AL_VENDOR));
   1.255 +       logger.log(Level.FINER, "Audio Renderer: {0}", alGetString(AL_RENDERER));
   1.256 +       logger.log(Level.FINER, "Audio Version: {0}", alGetString(AL_VERSION));
   1.257 +
   1.258 +       // Find maximum # of sources supported by this implementation
   1.259 +       // RLM: this may not be wise -- exceeding the number of available channels 
   1.260 +       //      can crash some versions of OpenAL
   1.261 +       ArrayList<Integer> channelList = new ArrayList<Integer>();
   1.262 +       for (int i = 0; i < MAX_NUM_CHANNELS; i++){
   1.263 +           int chan = alGenSources();
   1.264 +           if (alGetError() != 0){
   1.265 +               break;
   1.266 +           }else{
   1.267 +               channelList.add(chan);
   1.268 +           }
   1.269 +       }
   1.270 +
   1.271 +       channels = new int[channelList.size()];
   1.272 +       for (int i = 0; i < channels.length; i++){
   1.273 +           channels[i] = channelList.get(i);
   1.274 +       }
   1.275 +
   1.276 +       ib = BufferUtils.createIntBuffer(channels.length);
   1.277 +       chanSrcs = new AudioNode[channels.length];
   1.278 +
   1.279 +       logger.log(Level.INFO, "AudioRenderer supports {0} channels", channels.length);
   1.280 +
   1.281 +       supportEfx = ALC10.alcIsExtensionPresent(device, "ALC_EXT_EFX");
   1.282 +       // RLM: disable this for now.
   1.283 +       supportEfx = false;
   1.284 +       logger.log(Level.FINER, "Audio EFX support: {0}", supportEfx);
   1.285 +
   1.286 +       if (supportEfx){
   1.287 +           ib.position(0).limit(1);
   1.288 +           ALC10.alcGetInteger(device, EFX10.ALC_EFX_MAJOR_VERSION, ib);
   1.289 +           int major = ib.get(0);
   1.290 +           ib.position(0).limit(1);
   1.291 +           ALC10.alcGetInteger(device, EFX10.ALC_EFX_MINOR_VERSION, ib);
   1.292 +           int minor = ib.get(0);
   1.293 +           logger.log(Level.INFO, "Audio effect extension version: {0}.{1}", new Object[]{major, minor});
   1.294 +
   1.295 +           ALC10.alcGetInteger(device, EFX10.ALC_MAX_AUXILIARY_SENDS, ib);
   1.296 +           auxSends = ib.get(0);
   1.297 +           logger.log(Level.INFO, "Audio max auxilary sends: {0}", auxSends);
   1.298 +
   1.299 +           // create slot
   1.300 +           ib.position(0).limit(1);
   1.301 +           EFX10.alGenAuxiliaryEffectSlots(ib);
   1.302 +           reverbFxSlot = ib.get(0);
   1.303 +
   1.304 +           // create effect
   1.305 +           ib.position(0).limit(1);
   1.306 +           EFX10.alGenEffects(ib);
   1.307 +           reverbFx = ib.get(0);
   1.308 +           EFX10.alEffecti(reverbFx, EFX10.AL_EFFECT_TYPE, EFX10.AL_EFFECT_REVERB);
   1.309 +
   1.310 +           // attach reverb effect to effect slot
   1.311 +//           EFX10.alAuxiliaryEffectSloti(reverbFxSlot, EFX10.AL_EFFECTSLOT_EFFECT, reverbFx);
   1.312 +       }
   1.313 +   }
   1.314 +
   1.315 +   public void cleanupInThread(){
   1.316 +
   1.317 +
   1.318 +	   if (audioDisabled){
   1.319 +           AL.destroy();
   1.320 +           return;
   1.321 +       }
   1.322 +
   1.323 +       // delete channel-based sources
   1.324 +       ib.clear();
   1.325 +       ib.put(channels);
   1.326 +       ib.flip();
   1.327 +       alDeleteSources(ib);
   1.328 +
   1.329 +       if (supportEfx){
   1.330 +           ib.position(0).limit(1);
   1.331 +           ib.put(0, reverbFx);
   1.332 +           EFX10.alDeleteEffects(ib);
   1.333 +
   1.334 +           ib.position(0).limit(1);
   1.335 +           ib.put(0, reverbFxSlot);
   1.336 +           EFX10.alDeleteAuxiliaryEffectSlots(ib);
   1.337 +       }
   1.338 +
   1.339 +       // XXX: Delete other buffers/sources
   1.340 +       AL.destroy();
   1.341 +   }
   1.342 +
   1.343 +   public void cleanup(){
   1.344 +       // kill audio thread
   1.345 +	   
   1.346 +       if (audioThread.isAlive()){
   1.347 +           audioThread.interrupt();
   1.348 +       }
   1.349 +       
   1.350 +       Byte[] data1 = new Byte[this.fullWaveData1.size()];
   1.351 +	   data1 = this.fullWaveData1.toArray(data1);
   1.352 +
   1.353 +	   Byte[] data2 = new Byte[this.fullWaveData2.size()];
   1.354 +	   data2 = this.fullWaveData2.toArray(data2);
   1.355 +	   System.out.println(this.fullWaveData1.size());
   1.356 +	   System.out.println("Saving WAVE data!");
   1.357 +	   /*for (int i = 0; i < data1.length;i++){
   1.358 +		   System.out.print(data1[i]+",");
   1.359 +		   if (i%32 ==0){System.out.println();}
   1.360 +	   }
   1.361 +	   */
   1.362 +	   
   1.363 +	   
   1.364 +	   StdAudio.save("/home/r/wave-output/data2.wav", data2);
   1.365 +	   StdAudio.save("/home/r/wave-output/data1.wav", data1);
   1.366 +   }
   1.367 +
   1.368 +   private void updateFilter(Filter f){
   1.369 +       int id = f.getId();
   1.370 +       if (id == -1){
   1.371 +           ib.position(0).limit(1);
   1.372 +           EFX10.alGenFilters(ib);
   1.373 +           id = ib.get(0);
   1.374 +           f.setId(id);
   1.375 +       }
   1.376 +
   1.377 +       if (f instanceof LowPassFilter){
   1.378 +           LowPassFilter lpf = (LowPassFilter) f;
   1.379 +           EFX10.alFilteri(id, EFX10.AL_FILTER_TYPE,    EFX10.AL_FILTER_LOWPASS);
   1.380 +           EFX10.alFilterf(id, EFX10.AL_LOWPASS_GAIN,   lpf.getVolume());
   1.381 +           EFX10.alFilterf(id, EFX10.AL_LOWPASS_GAINHF, lpf.getHighFreqVolume());
   1.382 +       }else{
   1.383 +           throw new UnsupportedOperationException("Filter type unsupported: "+
   1.384 +                                                   f.getClass().getName());
   1.385 +       }
   1.386 +
   1.387 +       f.clearUpdateNeeded();
   1.388 +   }
   1.389 +
   1.390 +   public void updateSourceParam(AudioNode src, AudioParam param){
   1.391 +       checkDead();
   1.392 +       synchronized (threadLock){
   1.393 +           while (!threadLock.get()){
   1.394 +               try {
   1.395 +                   threadLock.wait();
   1.396 +               } catch (InterruptedException ex) {
   1.397 +               }
   1.398 +           }
   1.399 +           if (audioDisabled)
   1.400 +               return;
   1.401 +
   1.402 +           // There is a race condition in AudioNode that can
   1.403 +           // cause this to be called for a node that has been
   1.404 +           // detached from its channel.  For example, setVolume()
   1.405 +           // called from the render thread may see that that AudioNode
   1.406 +           // still has a channel value but the audio thread may
   1.407 +           // clear that channel before setVolume() gets to call
   1.408 +           // updateSourceParam() (because the audio stopped playing
   1.409 +           // on its own right as the volume was set).  In this case, 
   1.410 +           // it should be safe to just ignore the update
   1.411 +           if (src.getChannel() < 0) 
   1.412 +               return;
   1.413 +          
   1.414 +           assert src.getChannel() >= 0;
   1.415 +
   1.416 +           int id = channels[src.getChannel()];
   1.417 +           switch (param){
   1.418 +               case Position:
   1.419 +                   if (!src.isPositional())
   1.420 +                       return;
   1.421 +
   1.422 +                   Vector3f pos = src.getWorldTranslation();
   1.423 +                   alSource3f(id, AL_POSITION, pos.x, pos.y, pos.z);
   1.424 +                   break;
   1.425 +               case Velocity:
   1.426 +                   if (!src.isPositional())
   1.427 +                       return;
   1.428 +
   1.429 +                   Vector3f vel = src.getVelocity();
   1.430 +                   alSource3f(id, AL_VELOCITY, vel.x, vel.y, vel.z);
   1.431 +                   break;
   1.432 +               case MaxDistance:
   1.433 +                   if (!src.isPositional())
   1.434 +                       return;
   1.435 +
   1.436 +                   alSourcef(id, AL_MAX_DISTANCE, src.getMaxDistance());
   1.437 +                   break;
   1.438 +               case RefDistance:
   1.439 +                   if (!src.isPositional())
   1.440 +                       return;
   1.441 +
   1.442 +                   alSourcef(id, AL_REFERENCE_DISTANCE, src.getRefDistance());
   1.443 +                   break;
   1.444 +               case ReverbFilter:
   1.445 +                   if (!src.isPositional() || !src.isReverbEnabled())
   1.446 +                       return;
   1.447 +
   1.448 +                   int filter = EFX10.AL_FILTER_NULL;
   1.449 +                   if (src.getReverbFilter() != null){
   1.450 +                       Filter f = src.getReverbFilter();
   1.451 +                       if (f.isUpdateNeeded()){
   1.452 +                           updateFilter(f);
   1.453 +                       }
   1.454 +                       filter = f.getId();
   1.455 +                   }
   1.456 +                   AL11.alSource3i(id, EFX10.AL_AUXILIARY_SEND_FILTER, reverbFxSlot, 0, filter);
   1.457 +                   break;
   1.458 +               case ReverbEnabled:
   1.459 +                   if (!src.isPositional())
   1.460 +                       return;
   1.461 +
   1.462 +                   if (src.isReverbEnabled()){
   1.463 +                       updateSourceParam(src, AudioParam.ReverbFilter);
   1.464 +                   }else{
   1.465 +                       AL11.alSource3i(id, EFX10.AL_AUXILIARY_SEND_FILTER, 0, 0, EFX10.AL_FILTER_NULL);
   1.466 +                   }
   1.467 +                   break;
   1.468 +               case IsPositional:
   1.469 +                   if (!src.isPositional()){
   1.470 +                       // play in headspace
   1.471 +                       alSourcei(id, AL_SOURCE_RELATIVE, AL_TRUE);
   1.472 +                       alSource3f(id, AL_POSITION, 0,0,0);
   1.473 +                       alSource3f(id, AL_VELOCITY, 0,0,0);
   1.474 +                   }else{
   1.475 +                       alSourcei(id, AL_SOURCE_RELATIVE, AL_FALSE);
   1.476 +                       updateSourceParam(src, AudioParam.Position);
   1.477 +                       updateSourceParam(src, AudioParam.Velocity);
   1.478 +                       updateSourceParam(src, AudioParam.MaxDistance);
   1.479 +                       updateSourceParam(src, AudioParam.RefDistance);
   1.480 +                       updateSourceParam(src, AudioParam.ReverbEnabled);
   1.481 +                   }
   1.482 +                   break;
   1.483 +               case Direction:
   1.484 +                   if (!src.isDirectional())
   1.485 +                       return;
   1.486 +
   1.487 +                   Vector3f dir = src.getDirection();
   1.488 +                   alSource3f(id, AL_DIRECTION, dir.x, dir.y, dir.z);
   1.489 +                   break;
   1.490 +               case InnerAngle:
   1.491 +                   if (!src.isDirectional())
   1.492 +                       return;
   1.493 +
   1.494 +                   alSourcef(id, AL_CONE_INNER_ANGLE, src.getInnerAngle());
   1.495 +                   break;
   1.496 +               case OuterAngle:
   1.497 +                   if (!src.isDirectional())
   1.498 +                       return;
   1.499 +
   1.500 +                   alSourcef(id, AL_CONE_OUTER_ANGLE, src.getOuterAngle());
   1.501 +                   break;
   1.502 +               case IsDirectional:
   1.503 +                   if (src.isDirectional()){
   1.504 +                       updateSourceParam(src, AudioParam.Direction);
   1.505 +                       updateSourceParam(src, AudioParam.InnerAngle);
   1.506 +                       updateSourceParam(src, AudioParam.OuterAngle);
   1.507 +                       alSourcef(id, AL_CONE_OUTER_GAIN, 0);
   1.508 +                   }else{
   1.509 +                       alSourcef(id, AL_CONE_INNER_ANGLE, 360);
   1.510 +                       alSourcef(id, AL_CONE_OUTER_ANGLE, 360);
   1.511 +                       alSourcef(id, AL_CONE_OUTER_GAIN, 1f);
   1.512 +                   }
   1.513 +                   break;
   1.514 +               case DryFilter:
   1.515 +                   if (src.getDryFilter() != null){
   1.516 +                       Filter f = src.getDryFilter();
   1.517 +                       if (f.isUpdateNeeded()){
   1.518 +                           updateFilter(f);
   1.519 +
   1.520 +                           // NOTE: must re-attach filter for changes to apply.
   1.521 +                           alSourcei(id, EFX10.AL_DIRECT_FILTER, f.getId());
   1.522 +                       }
   1.523 +                   }else{
   1.524 +                       alSourcei(id, EFX10.AL_DIRECT_FILTER, EFX10.AL_FILTER_NULL);
   1.525 +                   }
   1.526 +                   break;
   1.527 +               case Looping:
   1.528 +                   if (src.isLooping()){
   1.529 +                       if (!(src.getAudioData() instanceof AudioStream)){
   1.530 +                           alSourcei(id, AL_LOOPING, AL_TRUE);
   1.531 +                       }
   1.532 +                   }else{
   1.533 +                       alSourcei(id, AL_LOOPING, AL_FALSE);
   1.534 +                   }
   1.535 +                   break;
   1.536 +               case Volume:
   1.537 +                   alSourcef(id, AL_GAIN, src.getVolume());
   1.538 +                   break;
   1.539 +               case Pitch:
   1.540 +                   alSourcef(id, AL_PITCH, src.getPitch());
   1.541 +                   break;
   1.542 +           }
   1.543 +       }
   1.544 +   }
   1.545 +
   1.546 +   private void setSourceParams(int id, AudioNode src, boolean forceNonLoop){
   1.547 +       if (src.isPositional()){
   1.548 +           Vector3f pos = src.getWorldTranslation();
   1.549 +           Vector3f vel = src.getVelocity();
   1.550 +           alSource3f(id, AL_POSITION, pos.x, pos.y, pos.z);
   1.551 +           alSource3f(id, AL_VELOCITY, vel.x, vel.y, vel.z);
   1.552 +           alSourcef(id, AL_MAX_DISTANCE, src.getMaxDistance());
   1.553 +           alSourcef(id, AL_REFERENCE_DISTANCE, src.getRefDistance());
   1.554 +           alSourcei(id, AL_SOURCE_RELATIVE, AL_FALSE);
   1.555 +
   1.556 +           if (src.isReverbEnabled()){
   1.557 +               int filter = EFX10.AL_FILTER_NULL;
   1.558 +               if (src.getReverbFilter() != null){
   1.559 +                   Filter f = src.getReverbFilter();
   1.560 +                   if (f.isUpdateNeeded()){
   1.561 +                       updateFilter(f);
   1.562 +                   }
   1.563 +                   filter = f.getId();
   1.564 +               }
   1.565 +               AL11.alSource3i(id, EFX10.AL_AUXILIARY_SEND_FILTER, reverbFxSlot, 0, filter);
   1.566 +           }
   1.567 +       }else{
   1.568 +           // play in headspace
   1.569 +           alSourcei(id, AL_SOURCE_RELATIVE, AL_TRUE);
   1.570 +           alSource3f(id, AL_POSITION, 0,0,0);
   1.571 +           alSource3f(id, AL_VELOCITY, 0,0,0);
   1.572 +       }
   1.573 +
   1.574 +       if (src.getDryFilter() != null){
   1.575 +           Filter f = src.getDryFilter();
   1.576 +           if (f.isUpdateNeeded()){
   1.577 +               updateFilter(f);
   1.578 +               
   1.579 +               // NOTE: must re-attach filter for changes to apply.
   1.580 +               alSourcei(id, EFX10.AL_DIRECT_FILTER, f.getId());
   1.581 +           }
   1.582 +       }
   1.583 +
   1.584 +       if (forceNonLoop){
   1.585 +           alSourcei(id,  AL_LOOPING, AL_FALSE);
   1.586 +       }else{
   1.587 +           alSourcei(id,  AL_LOOPING, src.isLooping() ? AL_TRUE : AL_FALSE);
   1.588 +       }
   1.589 +       alSourcef(id,  AL_GAIN, src.getVolume());
   1.590 +       alSourcef(id,  AL_PITCH, src.getPitch());
   1.591 +       alSourcef(id,  AL11.AL_SEC_OFFSET, src.getTimeOffset());
   1.592 +
   1.593 +       if (src.isDirectional()){
   1.594 +           Vector3f dir = src.getDirection();
   1.595 +           alSource3f(id, AL_DIRECTION, dir.x, dir.y, dir.z);
   1.596 +           alSourcef(id, AL_CONE_INNER_ANGLE, src.getInnerAngle());
   1.597 +           alSourcef(id, AL_CONE_OUTER_ANGLE, src.getOuterAngle());
   1.598 +           alSourcef(id, AL_CONE_OUTER_GAIN,  0);
   1.599 +       }else{
   1.600 +           alSourcef(id, AL_CONE_INNER_ANGLE, 360);
   1.601 +           alSourcef(id, AL_CONE_OUTER_ANGLE, 360);
   1.602 +           alSourcef(id, AL_CONE_OUTER_GAIN, 1f);
   1.603 +       }
   1.604 +   }
   1.605 +
   1.606 +   public void updateListenerParam(Listener listener, ListenerParam param){
   1.607 +       checkDead();
   1.608 +       synchronized (threadLock){
   1.609 +           while (!threadLock.get()){
   1.610 +               try {
   1.611 +                   threadLock.wait();
   1.612 +               } catch (InterruptedException ex) {
   1.613 +               }
   1.614 +           }
   1.615 +           if (audioDisabled)
   1.616 +               return;
   1.617 +           
   1.618 +           switch (param){
   1.619 +               case Position:
   1.620 +                   Vector3f pos = listener.getLocation();
   1.621 +                   alListener3f(AL_POSITION, pos.x, pos.y, pos.z);
   1.622 +                   break;
   1.623 +               case Rotation:
   1.624 +                   Vector3f dir = listener.getDirection();
   1.625 +                   Vector3f up  = listener.getUp();
   1.626 +                   fb.rewind();
   1.627 +                   fb.put(dir.x).put(dir.y).put(dir.z);
   1.628 +                   fb.put(up.x).put(up.y).put(up.z);
   1.629 +                   fb.flip();
   1.630 +                   alListener(AL_ORIENTATION, fb);
   1.631 +                   break;
   1.632 +               case Velocity:
   1.633 +                   Vector3f vel = listener.getVelocity();
   1.634 +                   alListener3f(AL_VELOCITY, vel.x, vel.y, vel.z);
   1.635 +                   break;
   1.636 +               case Volume:
   1.637 +                   alListenerf(AL_GAIN, listener.getVolume());
   1.638 +                   break;
   1.639 +           }
   1.640 +       }
   1.641 +   }
   1.642 +
   1.643 +   private void setListenerParams(Listener listener){
   1.644 +       Vector3f pos = listener.getLocation();
   1.645 +       Vector3f vel = listener.getVelocity();
   1.646 +       Vector3f dir = listener.getDirection();
   1.647 +       Vector3f up  = listener.getUp();
   1.648 +
   1.649 +       alListener3f(AL_POSITION, pos.x, pos.y, pos.z);
   1.650 +       alListener3f(AL_VELOCITY, vel.x, vel.y, vel.z);
   1.651 +       fb.rewind();
   1.652 +       fb.put(dir.x).put(dir.y).put(dir.z);
   1.653 +       fb.put(up.x).put(up.y).put(up.z);
   1.654 +       fb.flip();
   1.655 +       alListener(AL_ORIENTATION, fb);
   1.656 +       alListenerf(AL_GAIN, listener.getVolume());
   1.657 +   }
   1.658 +
   1.659 +   private int newChannel(){
   1.660 +       if (freeChans.size() > 0)
   1.661 +           return freeChans.remove(0);
   1.662 +       else if (nextChan < channels.length){
   1.663 +           return nextChan++;
   1.664 +       }else{
   1.665 +           return -1;
   1.666 +       }
   1.667 +   }
   1.668 +
   1.669 +   private void freeChannel(int index){
   1.670 +       if (index == nextChan-1){
   1.671 +           nextChan--;
   1.672 +       } else{
   1.673 +           freeChans.add(index);
   1.674 +       }
   1.675 +   }
   1.676 +
   1.677 +   public void setEnvironment(Environment env){
   1.678 +       checkDead();
   1.679 +       synchronized (threadLock){
   1.680 +           while (!threadLock.get()){
   1.681 +               try {
   1.682 +                   threadLock.wait();
   1.683 +               } catch (InterruptedException ex) {
   1.684 +               }
   1.685 +           }
   1.686 +           if (audioDisabled)
   1.687 +               return;
   1.688 +           
   1.689 +           EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_DENSITY,             env.getDensity());
   1.690 +           EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_DIFFUSION,           env.getDiffusion());
   1.691 +           EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_GAIN,                env.getGain());
   1.692 +           EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_GAINHF,              env.getGainHf());
   1.693 +           EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_DECAY_TIME,          env.getDecayTime());
   1.694 +           EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_DECAY_HFRATIO,       env.getDecayHFRatio());
   1.695 +           EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_REFLECTIONS_GAIN,    env.getReflectGain());
   1.696 +           EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_REFLECTIONS_DELAY,   env.getReflectDelay());
   1.697 +           EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_LATE_REVERB_GAIN,    env.getLateReverbGain());
   1.698 +           EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_LATE_REVERB_DELAY,   env.getLateReverbDelay());
   1.699 +           EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_AIR_ABSORPTION_GAINHF, env.getAirAbsorbGainHf());
   1.700 +           EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_ROOM_ROLLOFF_FACTOR, env.getRoomRolloffFactor());
   1.701 +
   1.702 +           // attach effect to slot
   1.703 +           EFX10.alAuxiliaryEffectSloti(reverbFxSlot, EFX10.AL_EFFECTSLOT_EFFECT, reverbFx);
   1.704 +       }
   1.705 +   }
   1.706 +
   1.707 +   private boolean fillBuffer(AudioStream stream, int id){
   1.708 +       int size = 0;
   1.709 +       int result;
   1.710 +
   1.711 +       while (size < arrayBuf.length){
   1.712 +           result = stream.readSamples(arrayBuf, size, arrayBuf.length - size);
   1.713 +
   1.714 +           if(result > 0){
   1.715 +               size += result;
   1.716 +           }else{
   1.717 +               break;
   1.718 +           }
   1.719 +       }
   1.720 +
   1.721 +       if(size == 0)
   1.722 +           return false;
   1.723 +
   1.724 +       nativeBuf.clear();
   1.725 +       nativeBuf.put(arrayBuf, 0, size);
   1.726 +       nativeBuf.flip();
   1.727 +
   1.728 +       alBufferData(id, convertFormat(stream), nativeBuf, stream.getSampleRate());
   1.729 +
   1.730 +       return true;
   1.731 +   }
   1.732 +
   1.733 +   private boolean fillStreamingSource(int sourceId, AudioStream stream){
   1.734 +       if (!stream.isOpen())
   1.735 +           return false;
   1.736 +
   1.737 +       boolean active = true;
   1.738 +       int processed = alGetSourcei(sourceId, AL_BUFFERS_PROCESSED);
   1.739 +
   1.740 +//       while((processed--) != 0){
   1.741 +       if (processed > 0){
   1.742 +           int buffer;
   1.743 +
   1.744 +           ib.position(0).limit(1);
   1.745 +           alSourceUnqueueBuffers(sourceId, ib);
   1.746 +           buffer = ib.get(0);
   1.747 +
   1.748 +           active = fillBuffer(stream, buffer);
   1.749 +
   1.750 +           ib.position(0).limit(1);
   1.751 +           ib.put(0, buffer);
   1.752 +           alSourceQueueBuffers(sourceId, ib);
   1.753 +       }
   1.754 +
   1.755 +       if (!active && stream.isOpen())
   1.756 +           stream.close();
   1.757 +       
   1.758 +       return active;
   1.759 +   }
   1.760 +
   1.761 +   private boolean attachStreamToSource(int sourceId, AudioStream stream){
   1.762 +       boolean active = true;
   1.763 +       for (int id : stream.getIds()){
   1.764 +           active = fillBuffer(stream, id);
   1.765 +           ib.position(0).limit(1);
   1.766 +           ib.put(id).flip();
   1.767 +           alSourceQueueBuffers(sourceId, ib);
   1.768 +       }
   1.769 +       return active;
   1.770 +   }
   1.771 +
   1.772 +   private boolean attachBufferToSource(int sourceId, AudioBuffer buffer){
   1.773 +       alSourcei(sourceId, AL_BUFFER, buffer.getId());
   1.774 +       return true;
   1.775 +   }
   1.776 +
   1.777 +   private boolean attachAudioToSource(int sourceId, AudioData data){
   1.778 +       if (data instanceof AudioBuffer){
   1.779 +           return attachBufferToSource(sourceId, (AudioBuffer) data);
   1.780 +       }else if (data instanceof AudioStream){
   1.781 +           return attachStreamToSource(sourceId, (AudioStream) data);
   1.782 +       }
   1.783 +       throw new UnsupportedOperationException();
   1.784 +   }
   1.785 +
   1.786 +   private void clearChannel(int index){
   1.787 +       // make room at this channel
   1.788 +       if (chanSrcs[index] != null){
   1.789 +           AudioNode src = chanSrcs[index];
   1.790 +
   1.791 +           int sourceId = channels[index];
   1.792 +           alSourceStop(sourceId);
   1.793 +
   1.794 +           if (src.getAudioData() instanceof AudioStream){
   1.795 +               AudioStream str = (AudioStream) src.getAudioData();
   1.796 +               ib.position(0).limit(STREAMING_BUFFER_COUNT);
   1.797 +               ib.put(str.getIds()).flip();
   1.798 +               alSourceUnqueueBuffers(sourceId, ib);
   1.799 +           }else if (src.getAudioData() instanceof AudioBuffer){
   1.800 +               alSourcei(sourceId, AL_BUFFER, 0);
   1.801 +           }
   1.802 +
   1.803 +           if (src.getDryFilter() != null){
   1.804 +               // detach filter
   1.805 +               alSourcei(sourceId, EFX10.AL_DIRECT_FILTER, EFX10.AL_FILTER_NULL);
   1.806 +           }
   1.807 +           if (src.isPositional()){
   1.808 +               AudioNode pas = (AudioNode) src;
   1.809 +               if (pas.isReverbEnabled()) {
   1.810 +                   AL11.alSource3i(sourceId, EFX10.AL_AUXILIARY_SEND_FILTER, 0, 0, EFX10.AL_FILTER_NULL);
   1.811 +               }
   1.812 +           }
   1.813 +
   1.814 +           chanSrcs[index] = null;
   1.815 +       }
   1.816 +   }
   1.817 +
   1.818 +   public void update(float tpf){
   1.819 +	   //ByteBuffer test = BufferUtils.createByteBuffer(1);
   1.820 +	   //AurellemTransport.getAuxSamples(AL.getDevice(), test);
   1.821 +   }
   1.822 +
   1.823 +   Vector<Byte> fullWaveData1 = new Vector<Byte>();
   1.824 +   Vector<Byte> fullWaveData2 = new Vector<Byte>();
   1.825 +  
   1.826 +   public void updateInThread(float tpf){
   1.827 +       if (audioDisabled)
   1.828 +           return;
   1.829 +       
   1.830 +       step();
   1.831 +       ByteBuffer test = BufferUtils.createByteBuffer(4096);
   1.832 +	   test.clear();
   1.833 +       this.getMainSamples(test);
   1.834 +       byte[] waveData = new byte[4096];
   1.835 +       test.get(waveData, 0, 4096);
   1.836 +       //System.out.println("J DATA:");
   1.837 +       /*for (int j = 0; j < 1; j++){
   1.838 +    	   for(int i = 64 * j; i < (64*j) + 64; i++){
   1.839 +    		   System.out.print(waveData[i]);
   1.840 +    	   }
   1.841 +    	   System.out.println();
   1.842 +       }*/
   1.843 +       
   1.844 +       ByteBuffer test2 = BufferUtils.createByteBuffer(4096);
   1.845 +	   test2.clear();
   1.846 +       this.getAuxSamples(test2);
   1.847 +       byte[] waveData2 = new byte[4096];
   1.848 +       test2.get(waveData2, 0, 4096);
   1.849 +       //System.out.print("wave1:");
   1.850 +       //for (int j = 0; j< 32; j++){
   1.851 +    //	   System.out.print(waveData[j]+",");
   1.852 +      // }
   1.853 +       //System.out.println();
   1.854 +       //System.out.print("wave2:");
   1.855 +      // for (int j = 0; j< 4096; j++){
   1.856 +    	//   if (waveData2[j] != 0){
   1.857 +    	//	   System.out.println("fucked at : " + j);
   1.858 +    	 //  }
   1.859 +    	
   1.860 +    	  /* System.out.print(waveData2[j]+",");
   1.861 +    	   if (0 == (j % 64)){System.out.println();}*/
   1.862 +       //}
   1.863 +       //System.out.println();
   1.864 +       
   1.865 +       for (byte b : waveData){
   1.866 +    	   this.fullWaveData1.add(b);
   1.867 +       }
   1.868 +       
   1.869 +       for (byte b : waveData2){
   1.870 +    	   this.fullWaveData2.add(b);
   1.871 +       }
   1.872 +       
   1.873 +       
   1.874 +       for (int i = 0; i < channels.length; i++){
   1.875 +           AudioNode src = chanSrcs[i];
   1.876 +           if (src == null)
   1.877 +               continue;
   1.878 +
   1.879 +           int sourceId = channels[i];
   1.880 +
   1.881 +           // is the source bound to this channel
   1.882 +           // if false, it's an instanced playback
   1.883 +           boolean boundSource = i == src.getChannel();
   1.884 +
   1.885 +           // source's data is streaming
   1.886 +           boolean streaming = src.getAudioData() instanceof AudioStream;
   1.887 +
   1.888 +           // only buffered sources can be bound
   1.889 +           assert (boundSource && streaming) || (!streaming);
   1.890 +
   1.891 +           int state = alGetSourcei(sourceId, AL_SOURCE_STATE);
   1.892 +           boolean wantPlaying = src.getStatus() == Status.Playing;
   1.893 +           boolean stopped = state == AL_STOPPED;
   1.894 +
   1.895 +           if (streaming && wantPlaying){
   1.896 +               AudioStream stream = (AudioStream) src.getAudioData();
   1.897 +               if (stream.isOpen()){
   1.898 +                   fillStreamingSource(sourceId, stream);
   1.899 +                   if (stopped)
   1.900 +                       alSourcePlay(sourceId);
   1.901 +                   
   1.902 +               }else{
   1.903 +                   if (stopped){
   1.904 +                       // became inactive
   1.905 +                       src.setStatus(Status.Stopped);
   1.906 +                       src.setChannel(-1);
   1.907 +                       clearChannel(i);
   1.908 +                       freeChannel(i);
   1.909 +
   1.910 +                       // And free the audio since it cannot be
   1.911 +                       // played again anyway.
   1.912 +                       deleteAudioData(stream);
   1.913 +                   }
   1.914 +               }
   1.915 +           }else if (!streaming){
   1.916 +               boolean paused = state == AL_PAUSED;
   1.917 +
   1.918 +               // make sure OAL pause state & source state coincide
   1.919 +               assert (src.getStatus() == Status.Paused && paused) || (!paused);
   1.920 +
   1.921 +               if (stopped){
   1.922 +                   if (boundSource){
   1.923 +                       src.setStatus(Status.Stopped);
   1.924 +                       src.setChannel(-1);
   1.925 +                   }
   1.926 +                   clearChannel(i);
   1.927 +                   freeChannel(i);  
   1.928 +               }
   1.929 +           }
   1.930 +       }
   1.931 +   }
   1.932 +
   1.933 +   public void setListener(Listener listener) {
   1.934 +       checkDead();
   1.935 +       synchronized (threadLock){
   1.936 +           while (!threadLock.get()){
   1.937 +               try {
   1.938 +                   threadLock.wait();
   1.939 +               } catch (InterruptedException ex) {
   1.940 +               }
   1.941 +           }
   1.942 +           if (audioDisabled)
   1.943 +               return;
   1.944 +
   1.945 +           if (this.listener != null){
   1.946 +               // previous listener no longer associated with current
   1.947 +               // renderer
   1.948 +               this.listener.setRenderer(null);
   1.949 +           }
   1.950 +           
   1.951 +           this.listener = listener;
   1.952 +           this.listener.setRenderer(this);
   1.953 +           setListenerParams(listener);
   1.954 +       }
   1.955 +   }
   1.956 +
   1.957 +   public void playSourceInstance(AudioNode src){
   1.958 +       checkDead();
   1.959 +       synchronized (threadLock){
   1.960 +           while (!threadLock.get()){
   1.961 +               try {
   1.962 +                   threadLock.wait();
   1.963 +               } catch (InterruptedException ex) {
   1.964 +               }
   1.965 +           }
   1.966 +           if (audioDisabled)
   1.967 +               return;
   1.968 +           
   1.969 +           if (src.getAudioData() instanceof AudioStream)
   1.970 +               throw new UnsupportedOperationException(
   1.971 +                       "Cannot play instances " +
   1.972 +                       "of audio streams. Use playSource() instead.");
   1.973 +
   1.974 +           if (src.getAudioData().isUpdateNeeded()){
   1.975 +               updateAudioData(src.getAudioData());
   1.976 +           }
   1.977 +
   1.978 +           // create a new index for an audio-channel
   1.979 +           int index = newChannel();
   1.980 +           if (index == -1)
   1.981 +               return;
   1.982 +
   1.983 +           int sourceId = channels[index];
   1.984 +
   1.985 +           clearChannel(index);
   1.986 +
   1.987 +           // set parameters, like position and max distance
   1.988 +           setSourceParams(sourceId, src, true);
   1.989 +           attachAudioToSource(sourceId, src.getAudioData());
   1.990 +           chanSrcs[index] = src;
   1.991 +
   1.992 +           // play the channel
   1.993 +           alSourcePlay(sourceId);
   1.994 +       }
   1.995 +   }
   1.996 +
   1.997 +   
   1.998 +   public void playSource(AudioNode src) {
   1.999 +       checkDead();
  1.1000 +       synchronized (threadLock){
  1.1001 +           while (!threadLock.get()){
  1.1002 +               try {
  1.1003 +                   threadLock.wait();
  1.1004 +               } catch (InterruptedException ex) {
  1.1005 +               }
  1.1006 +           }
  1.1007 +           if (audioDisabled)
  1.1008 +               return;
  1.1009 +
  1.1010 +           //assert src.getStatus() == Status.Stopped || src.getChannel() == -1;
  1.1011 +
  1.1012 +           if (src.getStatus() == Status.Playing){
  1.1013 +               return;
  1.1014 +           }else if (src.getStatus() == Status.Stopped){
  1.1015 +
  1.1016 +               // allocate channel to this source
  1.1017 +               int index = newChannel();
  1.1018 +               if (index == -1) {
  1.1019 +                   logger.log(Level.WARNING, "No channel available to play {0}", src);
  1.1020 +                   return;
  1.1021 +               }
  1.1022 +               clearChannel(index);
  1.1023 +               src.setChannel(index);
  1.1024 +
  1.1025 +               AudioData data = src.getAudioData();
  1.1026 +               if (data.isUpdateNeeded())
  1.1027 +                   updateAudioData(data);
  1.1028 +
  1.1029 +               chanSrcs[index] = src;
  1.1030 +               setSourceParams(channels[index], src, false);
  1.1031 +               attachAudioToSource(channels[index], data);
  1.1032 +           }
  1.1033 +
  1.1034 +           alSourcePlay(channels[src.getChannel()]);
  1.1035 +           src.setStatus(Status.Playing);
  1.1036 +       }
  1.1037 +   }
  1.1038 +
  1.1039 +   
  1.1040 +   public void pauseSource(AudioNode src) {
  1.1041 +       checkDead();
  1.1042 +       synchronized (threadLock){
  1.1043 +           while (!threadLock.get()){
  1.1044 +               try {
  1.1045 +                   threadLock.wait();
  1.1046 +               } catch (InterruptedException ex) {
  1.1047 +               }
  1.1048 +           }
  1.1049 +           if (audioDisabled)
  1.1050 +               return;
  1.1051 +           
  1.1052 +           if (src.getStatus() == Status.Playing){
  1.1053 +               assert src.getChannel() != -1;
  1.1054 +
  1.1055 +               alSourcePause(channels[src.getChannel()]);
  1.1056 +               src.setStatus(Status.Paused);
  1.1057 +           }
  1.1058 +       }
  1.1059 +   }
  1.1060 +
  1.1061 +   
  1.1062 +   public void stopSource(AudioNode src) {
  1.1063 +       synchronized (threadLock){
  1.1064 +           while (!threadLock.get()){
  1.1065 +               try {
  1.1066 +                   threadLock.wait();
  1.1067 +               } catch (InterruptedException ex) {
  1.1068 +               }
  1.1069 +           }
  1.1070 +           if (audioDisabled)
  1.1071 +               return;
  1.1072 +           
  1.1073 +           if (src.getStatus() != Status.Stopped){
  1.1074 +               int chan = src.getChannel();
  1.1075 +               assert chan != -1; // if it's not stopped, must have id
  1.1076 +
  1.1077 +               src.setStatus(Status.Stopped);
  1.1078 +               src.setChannel(-1);
  1.1079 +               clearChannel(chan);
  1.1080 +               freeChannel(chan);
  1.1081 +               
  1.1082 +               if (src.getAudioData() instanceof AudioStream) {
  1.1083 +                   
  1.1084 +                   AudioStream stream = (AudioStream)src.getAudioData();
  1.1085 +                   if (stream.isOpen()) {
  1.1086 +                       stream.close();
  1.1087 +                   }
  1.1088 +               
  1.1089 +                   // And free the audio since it cannot be
  1.1090 +                   // played again anyway.
  1.1091 +                   deleteAudioData(src.getAudioData());
  1.1092 +               }                    
  1.1093 +           }
  1.1094 +       }
  1.1095 +   }
  1.1096 +
  1.1097 +   private int convertFormat(AudioData ad){
  1.1098 +       switch (ad.getBitsPerSample()){
  1.1099 +           case 8:
  1.1100 +               if (ad.getChannels() == 1)
  1.1101 +                   return AL_FORMAT_MONO8;
  1.1102 +               else if (ad.getChannels() == 2)
  1.1103 +                   return AL_FORMAT_STEREO8;
  1.1104 +
  1.1105 +               break;
  1.1106 +           case 16:
  1.1107 +               if (ad.getChannels() == 1)
  1.1108 +                   return AL_FORMAT_MONO16;
  1.1109 +               else
  1.1110 +                   return AL_FORMAT_STEREO16;
  1.1111 +       }
  1.1112 +       throw new UnsupportedOperationException("Unsupported channels/bits combination: "+
  1.1113 +                                               "bits="+ad.getBitsPerSample()+", channels="+ad.getChannels());
  1.1114 +   }
  1.1115 +
  1.1116 +   private void updateAudioBuffer(AudioBuffer ab){
  1.1117 +       int id = ab.getId();
  1.1118 +       if (ab.getId() == -1){
  1.1119 +           ib.position(0).limit(1);
  1.1120 +           alGenBuffers(ib);
  1.1121 +           id = ib.get(0);
  1.1122 +           ab.setId(id);
  1.1123 +       }
  1.1124 +
  1.1125 +       ab.getData().clear();
  1.1126 +       alBufferData(id, convertFormat(ab), ab.getData(), ab.getSampleRate());
  1.1127 +       ab.clearUpdateNeeded();
  1.1128 +   }
  1.1129 +
  1.1130 +   private void updateAudioStream(AudioStream as){
  1.1131 +       if (as.getIds() != null){
  1.1132 +           deleteAudioData(as);
  1.1133 +       }
  1.1134 +
  1.1135 +       int[] ids = new int[STREAMING_BUFFER_COUNT];
  1.1136 +       ib.position(0).limit(STREAMING_BUFFER_COUNT);
  1.1137 +       alGenBuffers(ib);
  1.1138 +       ib.position(0).limit(STREAMING_BUFFER_COUNT);        
  1.1139 +       ib.get(ids);
  1.1140 +       
  1.1141 +       as.setIds(ids);
  1.1142 +       as.clearUpdateNeeded();
  1.1143 +   }
  1.1144 +
  1.1145 +   private void updateAudioData(AudioData ad){
  1.1146 +       if (ad instanceof AudioBuffer){
  1.1147 +           updateAudioBuffer((AudioBuffer) ad);
  1.1148 +       }else if (ad instanceof AudioStream){
  1.1149 +           updateAudioStream((AudioStream) ad);
  1.1150 +       }
  1.1151 +   }
  1.1152 +
  1.1153 +   public void deleteAudioData(AudioData ad){
  1.1154 +       synchronized (threadLock){
  1.1155 +           while (!threadLock.get()){
  1.1156 +               try {
  1.1157 +                   threadLock.wait();
  1.1158 +               } catch (InterruptedException ex) {
  1.1159 +               }
  1.1160 +           }
  1.1161 +           if (audioDisabled)
  1.1162 +               return;
  1.1163 +       
  1.1164 +           if (ad instanceof AudioBuffer){
  1.1165 +               AudioBuffer ab = (AudioBuffer) ad;
  1.1166 +               int id = ab.getId();
  1.1167 +               if (id != -1){
  1.1168 +                   ib.put(0,id);
  1.1169 +                   ib.position(0).limit(1);
  1.1170 +                   alDeleteBuffers(ib);
  1.1171 +                   ab.resetObject();
  1.1172 +               }
  1.1173 +           }else if (ad instanceof AudioStream){
  1.1174 +               AudioStream as = (AudioStream) ad;
  1.1175 +               int[] ids = as.getIds();
  1.1176 +               if (ids != null){
  1.1177 +                   ib.clear();
  1.1178 +                   ib.put(ids).flip();
  1.1179 +                   alDeleteBuffers(ib);
  1.1180 +                   as.resetObject();
  1.1181 +               }
  1.1182 +           }
  1.1183 +       }            
  1.1184 +   }
  1.1185 +
  1.1186 +}