annotate 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
rev   line source
rlm@0 1 package com.jme3.capture;
rlm@0 2
rlm@0 3 import static org.lwjgl.openal.AL10.AL_BUFFER;
rlm@0 4 import static org.lwjgl.openal.AL10.AL_BUFFERS_PROCESSED;
rlm@0 5 import static org.lwjgl.openal.AL10.AL_CONE_INNER_ANGLE;
rlm@0 6 import static org.lwjgl.openal.AL10.AL_CONE_OUTER_ANGLE;
rlm@0 7 import static org.lwjgl.openal.AL10.AL_CONE_OUTER_GAIN;
rlm@0 8 import static org.lwjgl.openal.AL10.AL_DIRECTION;
rlm@0 9 import static org.lwjgl.openal.AL10.AL_FALSE;
rlm@0 10 import static org.lwjgl.openal.AL10.AL_FORMAT_MONO16;
rlm@0 11 import static org.lwjgl.openal.AL10.AL_FORMAT_MONO8;
rlm@0 12 import static org.lwjgl.openal.AL10.AL_FORMAT_STEREO16;
rlm@0 13 import static org.lwjgl.openal.AL10.AL_FORMAT_STEREO8;
rlm@0 14 import static org.lwjgl.openal.AL10.AL_GAIN;
rlm@0 15 import static org.lwjgl.openal.AL10.AL_LOOPING;
rlm@0 16 import static org.lwjgl.openal.AL10.AL_MAX_DISTANCE;
rlm@0 17 import static org.lwjgl.openal.AL10.AL_ORIENTATION;
rlm@0 18 import static org.lwjgl.openal.AL10.AL_PAUSED;
rlm@0 19 import static org.lwjgl.openal.AL10.AL_PITCH;
rlm@0 20 import static org.lwjgl.openal.AL10.AL_POSITION;
rlm@0 21 import static org.lwjgl.openal.AL10.AL_REFERENCE_DISTANCE;
rlm@0 22 import static org.lwjgl.openal.AL10.AL_RENDERER;
rlm@0 23 import static org.lwjgl.openal.AL10.AL_SOURCE_RELATIVE;
rlm@0 24 import static org.lwjgl.openal.AL10.AL_SOURCE_STATE;
rlm@0 25 import static org.lwjgl.openal.AL10.AL_STOPPED;
rlm@0 26 import static org.lwjgl.openal.AL10.AL_TRUE;
rlm@0 27 import static org.lwjgl.openal.AL10.AL_VELOCITY;
rlm@0 28 import static org.lwjgl.openal.AL10.AL_VENDOR;
rlm@0 29 import static org.lwjgl.openal.AL10.AL_VERSION;
rlm@0 30 import static org.lwjgl.openal.AL10.alBufferData;
rlm@0 31 import static org.lwjgl.openal.AL10.alDeleteBuffers;
rlm@0 32 import static org.lwjgl.openal.AL10.alDeleteSources;
rlm@0 33 import static org.lwjgl.openal.AL10.alGenBuffers;
rlm@0 34 import static org.lwjgl.openal.AL10.alGenSources;
rlm@0 35 import static org.lwjgl.openal.AL10.alGetError;
rlm@0 36 import static org.lwjgl.openal.AL10.alGetSourcei;
rlm@0 37 import static org.lwjgl.openal.AL10.alGetString;
rlm@0 38 import static org.lwjgl.openal.AL10.alListener;
rlm@0 39 import static org.lwjgl.openal.AL10.alListener3f;
rlm@0 40 import static org.lwjgl.openal.AL10.alListenerf;
rlm@0 41 import static org.lwjgl.openal.AL10.alSource3f;
rlm@0 42 import static org.lwjgl.openal.AL10.alSourcePause;
rlm@0 43 import static org.lwjgl.openal.AL10.alSourcePlay;
rlm@0 44 import static org.lwjgl.openal.AL10.alSourceQueueBuffers;
rlm@0 45 import static org.lwjgl.openal.AL10.alSourceStop;
rlm@0 46 import static org.lwjgl.openal.AL10.alSourceUnqueueBuffers;
rlm@0 47 import static org.lwjgl.openal.AL10.alSourcef;
rlm@0 48 import static org.lwjgl.openal.AL10.alSourcei;
rlm@0 49
rlm@0 50 import java.lang.reflect.Field;
rlm@0 51 import java.nio.ByteBuffer;
rlm@0 52 import java.nio.FloatBuffer;
rlm@0 53 import java.nio.IntBuffer;
rlm@0 54 import java.util.ArrayList;
rlm@0 55 import java.util.Vector;
rlm@0 56 import java.util.concurrent.atomic.AtomicBoolean;
rlm@0 57 import java.util.logging.Level;
rlm@0 58 import java.util.logging.Logger;
rlm@0 59
rlm@0 60 import org.lwjgl.LWJGLException;
rlm@0 61 import org.lwjgl.openal.AL;
rlm@0 62 import org.lwjgl.openal.AL11;
rlm@0 63 import org.lwjgl.openal.ALC10;
rlm@0 64 import org.lwjgl.openal.ALCdevice;
rlm@0 65 import org.lwjgl.openal.EFX10;
rlm@0 66 import org.lwjgl.openal.OpenALException;
rlm@0 67
rlm@0 68 import com.jme3.audio.AudioBuffer;
rlm@0 69 import com.jme3.audio.AudioData;
rlm@0 70 import com.jme3.audio.AudioNode;
rlm@0 71 import com.jme3.audio.AudioNode.Status;
rlm@0 72 import com.jme3.audio.AudioParam;
rlm@0 73 import com.jme3.audio.AudioRenderer;
rlm@0 74 import com.jme3.audio.AudioStream;
rlm@0 75 import com.jme3.audio.Environment;
rlm@0 76 import com.jme3.audio.Filter;
rlm@0 77 import com.jme3.audio.Listener;
rlm@0 78 import com.jme3.audio.ListenerParam;
rlm@0 79 import com.jme3.audio.LowPassFilter;
rlm@0 80 import com.jme3.math.Vector3f;
rlm@0 81 import com.jme3.util.BufferUtils;
rlm@0 82
rlm@0 83
rlm@0 84
rlm@0 85 public class RecordAudioRenderer implements AudioRenderer, Runnable {
rlm@0 86
rlm@0 87
rlm@0 88
rlm@0 89 public static void getMainSamples(){
rlm@0 90
rlm@0 91 }
rlm@0 92
rlm@0 93
rlm@0 94 private static final Logger logger = Logger.getLogger(RecordAudioRenderer.class.getName());
rlm@0 95
rlm@0 96 // When multiplied by STREAMING_BUFFER_COUNT, will equal 44100 * 2 * 2
rlm@0 97 // which is exactly 1 second of audio.
rlm@0 98 private static final int BUFFER_SIZE = 35280;
rlm@0 99 private static final int STREAMING_BUFFER_COUNT = 5;
rlm@0 100
rlm@0 101 private final static int MAX_NUM_CHANNELS = 2;
rlm@0 102 private IntBuffer ib = BufferUtils.createIntBuffer(1);
rlm@0 103 private final FloatBuffer fb = BufferUtils.createVector3Buffer(2);
rlm@0 104 private final ByteBuffer nativeBuf = BufferUtils.createByteBuffer(BUFFER_SIZE);
rlm@0 105 private final byte[] arrayBuf = new byte[BUFFER_SIZE];
rlm@0 106
rlm@0 107 private int[] channels;
rlm@0 108 private AudioNode[] chanSrcs;
rlm@0 109 private int nextChan = 0;
rlm@0 110 private ArrayList<Integer> freeChans = new ArrayList<Integer>();
rlm@0 111
rlm@0 112 private Listener listener;
rlm@0 113 private boolean audioDisabled = false;
rlm@0 114
rlm@0 115 private boolean supportEfx = false;
rlm@0 116 private int auxSends = 0;
rlm@0 117 private int reverbFx = -1;
rlm@0 118 private int reverbFxSlot = -1;
rlm@0 119
rlm@0 120 // RLM: this is to call the native methods which require the OpenAL device ID.
rlm@0 121 // currently it is obtained through reflection.
rlm@0 122 private long deviceID;
rlm@0 123
rlm@0 124 // Update audio 20 times per second
rlm@0 125 private static final float UPDATE_RATE = 0.05f;
rlm@0 126
rlm@0 127 private final Thread audioThread = new Thread(this, "jME3 Audio Thread");
rlm@0 128 private final AtomicBoolean threadLock = new AtomicBoolean(false);
rlm@0 129
rlm@0 130 public RecordAudioRenderer(){
rlm@0 131 }
rlm@0 132
rlm@0 133 public static native void helloEveryone();
rlm@0 134
rlm@0 135
rlm@0 136 public static native void nstep(long device);
rlm@0 137 public void step(){
rlm@0 138 nstep(this.deviceID);
rlm@0 139 }
rlm@0 140
rlm@0 141
rlm@0 142
rlm@0 143 public void getMainSamples(ByteBuffer buffer){
rlm@0 144 ngetMainSamples(this.deviceID, buffer, buffer.position());
rlm@0 145 }
rlm@0 146 public static native void ngetMainSamples(long device, ByteBuffer buffer, int position);
rlm@0 147
rlm@0 148
rlm@0 149 public void getAuxSamples(ByteBuffer buffer){
rlm@0 150 ngetAuxSamples(this.deviceID, buffer, buffer.position());
rlm@0 151 }
rlm@0 152 public static native void ngetAuxSamples(long device, ByteBuffer buffer, int position);
rlm@0 153
rlm@0 154
rlm@0 155
rlm@0 156 public void initialize(){
rlm@0 157 if (!audioThread.isAlive()){
rlm@0 158 audioThread.setDaemon(true);
rlm@0 159 audioThread.setPriority(Thread.NORM_PRIORITY+1);
rlm@0 160 audioThread.start();
rlm@0 161 }else{
rlm@0 162 throw new IllegalStateException("Initialize already called");
rlm@0 163 }
rlm@0 164 }
rlm@0 165
rlm@0 166 private void checkDead(){
rlm@0 167 if (audioThread.getState() == Thread.State.TERMINATED)
rlm@0 168 throw new IllegalStateException("Audio thread is terminated");
rlm@0 169 }
rlm@0 170
rlm@0 171 public void run(){
rlm@0 172 initInThread();
rlm@0 173 synchronized (threadLock){
rlm@0 174 threadLock.set(true);
rlm@0 175 threadLock.notifyAll();
rlm@0 176 }
rlm@0 177
rlm@0 178
rlm@0 179 helloEveryone();
rlm@0 180 System.out.println("AudioRecorder: Trying to call native methods.");
rlm@0 181 System.out.println("our device ID is : " + this.deviceID);
rlm@0 182
rlm@0 183
rlm@0 184
rlm@0 185
rlm@0 186 long updateRateNanos = (long) (UPDATE_RATE * 1000000000);
rlm@0 187 mainloop: while (true){
rlm@0 188 long startTime = System.nanoTime();
rlm@0 189
rlm@0 190 if (Thread.interrupted())
rlm@0 191 break;
rlm@0 192
rlm@0 193 synchronized (threadLock){
rlm@0 194 updateInThread(UPDATE_RATE);
rlm@0 195 }
rlm@0 196
rlm@0 197 long endTime = System.nanoTime();
rlm@0 198 long diffTime = endTime - startTime;
rlm@0 199
rlm@0 200 if (diffTime < updateRateNanos){
rlm@0 201 long desiredEndTime = startTime + updateRateNanos;
rlm@0 202 while (System.nanoTime() < desiredEndTime){
rlm@0 203 try{
rlm@0 204 Thread.sleep(1);
rlm@0 205 }catch (InterruptedException ex){
rlm@0 206 break mainloop;
rlm@0 207 }
rlm@0 208 }
rlm@0 209 }
rlm@0 210 }
rlm@0 211
rlm@0 212 synchronized (threadLock){
rlm@0 213 cleanupInThread();
rlm@0 214 }
rlm@0 215 }
rlm@0 216
rlm@0 217 public void initInThread(){
rlm@0 218 try{
rlm@0 219 if (!AL.isCreated()){
rlm@0 220 AL.create("Aurellem", 44100, 15, false);
rlm@0 221 }
rlm@0 222 }catch (OpenALException ex){
rlm@0 223 logger.log(Level.SEVERE, "Failed to load audio library", ex);
rlm@0 224 audioDisabled = true;
rlm@0 225 return;
rlm@0 226 }catch (LWJGLException ex){
rlm@0 227 logger.log(Level.SEVERE, "Failed to load audio library", ex);
rlm@0 228 audioDisabled = true;
rlm@0 229 return;
rlm@0 230 }
rlm@0 231
rlm@0 232 ALCdevice device = AL.getDevice();
rlm@0 233
rlm@0 234 // RLM: use reflection to grab the ID of our device for use later.
rlm@0 235 try {
rlm@0 236 Field deviceIDField;
rlm@0 237 deviceIDField = ALCdevice.class.getDeclaredField("device");
rlm@0 238 deviceIDField.setAccessible(true);
rlm@0 239 try {deviceID = (Long)deviceIDField.get(device);}
rlm@0 240 catch (IllegalArgumentException e) {e.printStackTrace();}
rlm@0 241 catch (IllegalAccessException e) {e.printStackTrace();}
rlm@0 242 deviceIDField.setAccessible(false);}
rlm@0 243 catch (SecurityException e) {e.printStackTrace();}
rlm@0 244 catch (NoSuchFieldException e) {e.printStackTrace();}
rlm@0 245
rlm@0 246
rlm@0 247
rlm@0 248 String deviceName = ALC10.alcGetString(device, ALC10.ALC_DEVICE_SPECIFIER);
rlm@0 249
rlm@0 250 logger.log(Level.FINER, "Audio Device: {0}", deviceName);
rlm@0 251 logger.log(Level.FINER, "Audio Vendor: {0}", alGetString(AL_VENDOR));
rlm@0 252 logger.log(Level.FINER, "Audio Renderer: {0}", alGetString(AL_RENDERER));
rlm@0 253 logger.log(Level.FINER, "Audio Version: {0}", alGetString(AL_VERSION));
rlm@0 254
rlm@0 255 // Find maximum # of sources supported by this implementation
rlm@0 256 // RLM: this may not be wise -- exceeding the number of available channels
rlm@0 257 // can crash some versions of OpenAL
rlm@0 258 ArrayList<Integer> channelList = new ArrayList<Integer>();
rlm@0 259 for (int i = 0; i < MAX_NUM_CHANNELS; i++){
rlm@0 260 int chan = alGenSources();
rlm@0 261 if (alGetError() != 0){
rlm@0 262 break;
rlm@0 263 }else{
rlm@0 264 channelList.add(chan);
rlm@0 265 }
rlm@0 266 }
rlm@0 267
rlm@0 268 channels = new int[channelList.size()];
rlm@0 269 for (int i = 0; i < channels.length; i++){
rlm@0 270 channels[i] = channelList.get(i);
rlm@0 271 }
rlm@0 272
rlm@0 273 ib = BufferUtils.createIntBuffer(channels.length);
rlm@0 274 chanSrcs = new AudioNode[channels.length];
rlm@0 275
rlm@0 276 logger.log(Level.INFO, "AudioRenderer supports {0} channels", channels.length);
rlm@0 277
rlm@0 278 supportEfx = ALC10.alcIsExtensionPresent(device, "ALC_EXT_EFX");
rlm@0 279 // RLM: disable this for now.
rlm@0 280 supportEfx = false;
rlm@0 281 logger.log(Level.FINER, "Audio EFX support: {0}", supportEfx);
rlm@0 282
rlm@0 283 if (supportEfx){
rlm@0 284 ib.position(0).limit(1);
rlm@0 285 ALC10.alcGetInteger(device, EFX10.ALC_EFX_MAJOR_VERSION, ib);
rlm@0 286 int major = ib.get(0);
rlm@0 287 ib.position(0).limit(1);
rlm@0 288 ALC10.alcGetInteger(device, EFX10.ALC_EFX_MINOR_VERSION, ib);
rlm@0 289 int minor = ib.get(0);
rlm@0 290 logger.log(Level.INFO, "Audio effect extension version: {0}.{1}", new Object[]{major, minor});
rlm@0 291
rlm@0 292 ALC10.alcGetInteger(device, EFX10.ALC_MAX_AUXILIARY_SENDS, ib);
rlm@0 293 auxSends = ib.get(0);
rlm@0 294 logger.log(Level.INFO, "Audio max auxilary sends: {0}", auxSends);
rlm@0 295
rlm@0 296 // create slot
rlm@0 297 ib.position(0).limit(1);
rlm@0 298 EFX10.alGenAuxiliaryEffectSlots(ib);
rlm@0 299 reverbFxSlot = ib.get(0);
rlm@0 300
rlm@0 301 // create effect
rlm@0 302 ib.position(0).limit(1);
rlm@0 303 EFX10.alGenEffects(ib);
rlm@0 304 reverbFx = ib.get(0);
rlm@0 305 EFX10.alEffecti(reverbFx, EFX10.AL_EFFECT_TYPE, EFX10.AL_EFFECT_REVERB);
rlm@0 306
rlm@0 307 // attach reverb effect to effect slot
rlm@0 308 // EFX10.alAuxiliaryEffectSloti(reverbFxSlot, EFX10.AL_EFFECTSLOT_EFFECT, reverbFx);
rlm@0 309 }
rlm@0 310 }
rlm@0 311
rlm@0 312 public void cleanupInThread(){
rlm@0 313
rlm@0 314
rlm@0 315 if (audioDisabled){
rlm@0 316 AL.destroy();
rlm@0 317 return;
rlm@0 318 }
rlm@0 319
rlm@0 320 // delete channel-based sources
rlm@0 321 ib.clear();
rlm@0 322 ib.put(channels);
rlm@0 323 ib.flip();
rlm@0 324 alDeleteSources(ib);
rlm@0 325
rlm@0 326 if (supportEfx){
rlm@0 327 ib.position(0).limit(1);
rlm@0 328 ib.put(0, reverbFx);
rlm@0 329 EFX10.alDeleteEffects(ib);
rlm@0 330
rlm@0 331 ib.position(0).limit(1);
rlm@0 332 ib.put(0, reverbFxSlot);
rlm@0 333 EFX10.alDeleteAuxiliaryEffectSlots(ib);
rlm@0 334 }
rlm@0 335
rlm@0 336 // XXX: Delete other buffers/sources
rlm@0 337 AL.destroy();
rlm@0 338 }
rlm@0 339
rlm@0 340 public void cleanup(){
rlm@0 341 // kill audio thread
rlm@0 342
rlm@0 343 if (audioThread.isAlive()){
rlm@0 344 audioThread.interrupt();
rlm@0 345 }
rlm@0 346
rlm@0 347 Byte[] data1 = new Byte[this.fullWaveData1.size()];
rlm@0 348 data1 = this.fullWaveData1.toArray(data1);
rlm@0 349
rlm@0 350 Byte[] data2 = new Byte[this.fullWaveData2.size()];
rlm@0 351 data2 = this.fullWaveData2.toArray(data2);
rlm@0 352 System.out.println(this.fullWaveData1.size());
rlm@0 353 System.out.println("Saving WAVE data!");
rlm@0 354 /*for (int i = 0; i < data1.length;i++){
rlm@0 355 System.out.print(data1[i]+",");
rlm@0 356 if (i%32 ==0){System.out.println();}
rlm@0 357 }
rlm@0 358 */
rlm@0 359
rlm@0 360
rlm@0 361 StdAudio.save("/home/r/wave-output/data2.wav", data2);
rlm@0 362 StdAudio.save("/home/r/wave-output/data1.wav", data1);
rlm@0 363 }
rlm@0 364
rlm@0 365 private void updateFilter(Filter f){
rlm@0 366 int id = f.getId();
rlm@0 367 if (id == -1){
rlm@0 368 ib.position(0).limit(1);
rlm@0 369 EFX10.alGenFilters(ib);
rlm@0 370 id = ib.get(0);
rlm@0 371 f.setId(id);
rlm@0 372 }
rlm@0 373
rlm@0 374 if (f instanceof LowPassFilter){
rlm@0 375 LowPassFilter lpf = (LowPassFilter) f;
rlm@0 376 EFX10.alFilteri(id, EFX10.AL_FILTER_TYPE, EFX10.AL_FILTER_LOWPASS);
rlm@0 377 EFX10.alFilterf(id, EFX10.AL_LOWPASS_GAIN, lpf.getVolume());
rlm@0 378 EFX10.alFilterf(id, EFX10.AL_LOWPASS_GAINHF, lpf.getHighFreqVolume());
rlm@0 379 }else{
rlm@0 380 throw new UnsupportedOperationException("Filter type unsupported: "+
rlm@0 381 f.getClass().getName());
rlm@0 382 }
rlm@0 383
rlm@0 384 f.clearUpdateNeeded();
rlm@0 385 }
rlm@0 386
rlm@0 387 public void updateSourceParam(AudioNode src, AudioParam param){
rlm@0 388 checkDead();
rlm@0 389 synchronized (threadLock){
rlm@0 390 while (!threadLock.get()){
rlm@0 391 try {
rlm@0 392 threadLock.wait();
rlm@0 393 } catch (InterruptedException ex) {
rlm@0 394 }
rlm@0 395 }
rlm@0 396 if (audioDisabled)
rlm@0 397 return;
rlm@0 398
rlm@0 399 // There is a race condition in AudioNode that can
rlm@0 400 // cause this to be called for a node that has been
rlm@0 401 // detached from its channel. For example, setVolume()
rlm@0 402 // called from the render thread may see that that AudioNode
rlm@0 403 // still has a channel value but the audio thread may
rlm@0 404 // clear that channel before setVolume() gets to call
rlm@0 405 // updateSourceParam() (because the audio stopped playing
rlm@0 406 // on its own right as the volume was set). In this case,
rlm@0 407 // it should be safe to just ignore the update
rlm@0 408 if (src.getChannel() < 0)
rlm@0 409 return;
rlm@0 410
rlm@0 411 assert src.getChannel() >= 0;
rlm@0 412
rlm@0 413 int id = channels[src.getChannel()];
rlm@0 414 switch (param){
rlm@0 415 case Position:
rlm@0 416 if (!src.isPositional())
rlm@0 417 return;
rlm@0 418
rlm@0 419 Vector3f pos = src.getWorldTranslation();
rlm@0 420 alSource3f(id, AL_POSITION, pos.x, pos.y, pos.z);
rlm@0 421 break;
rlm@0 422 case Velocity:
rlm@0 423 if (!src.isPositional())
rlm@0 424 return;
rlm@0 425
rlm@0 426 Vector3f vel = src.getVelocity();
rlm@0 427 alSource3f(id, AL_VELOCITY, vel.x, vel.y, vel.z);
rlm@0 428 break;
rlm@0 429 case MaxDistance:
rlm@0 430 if (!src.isPositional())
rlm@0 431 return;
rlm@0 432
rlm@0 433 alSourcef(id, AL_MAX_DISTANCE, src.getMaxDistance());
rlm@0 434 break;
rlm@0 435 case RefDistance:
rlm@0 436 if (!src.isPositional())
rlm@0 437 return;
rlm@0 438
rlm@0 439 alSourcef(id, AL_REFERENCE_DISTANCE, src.getRefDistance());
rlm@0 440 break;
rlm@0 441 case ReverbFilter:
rlm@0 442 if (!src.isPositional() || !src.isReverbEnabled())
rlm@0 443 return;
rlm@0 444
rlm@0 445 int filter = EFX10.AL_FILTER_NULL;
rlm@0 446 if (src.getReverbFilter() != null){
rlm@0 447 Filter f = src.getReverbFilter();
rlm@0 448 if (f.isUpdateNeeded()){
rlm@0 449 updateFilter(f);
rlm@0 450 }
rlm@0 451 filter = f.getId();
rlm@0 452 }
rlm@0 453 AL11.alSource3i(id, EFX10.AL_AUXILIARY_SEND_FILTER, reverbFxSlot, 0, filter);
rlm@0 454 break;
rlm@0 455 case ReverbEnabled:
rlm@0 456 if (!src.isPositional())
rlm@0 457 return;
rlm@0 458
rlm@0 459 if (src.isReverbEnabled()){
rlm@0 460 updateSourceParam(src, AudioParam.ReverbFilter);
rlm@0 461 }else{
rlm@0 462 AL11.alSource3i(id, EFX10.AL_AUXILIARY_SEND_FILTER, 0, 0, EFX10.AL_FILTER_NULL);
rlm@0 463 }
rlm@0 464 break;
rlm@0 465 case IsPositional:
rlm@0 466 if (!src.isPositional()){
rlm@0 467 // play in headspace
rlm@0 468 alSourcei(id, AL_SOURCE_RELATIVE, AL_TRUE);
rlm@0 469 alSource3f(id, AL_POSITION, 0,0,0);
rlm@0 470 alSource3f(id, AL_VELOCITY, 0,0,0);
rlm@0 471 }else{
rlm@0 472 alSourcei(id, AL_SOURCE_RELATIVE, AL_FALSE);
rlm@0 473 updateSourceParam(src, AudioParam.Position);
rlm@0 474 updateSourceParam(src, AudioParam.Velocity);
rlm@0 475 updateSourceParam(src, AudioParam.MaxDistance);
rlm@0 476 updateSourceParam(src, AudioParam.RefDistance);
rlm@0 477 updateSourceParam(src, AudioParam.ReverbEnabled);
rlm@0 478 }
rlm@0 479 break;
rlm@0 480 case Direction:
rlm@0 481 if (!src.isDirectional())
rlm@0 482 return;
rlm@0 483
rlm@0 484 Vector3f dir = src.getDirection();
rlm@0 485 alSource3f(id, AL_DIRECTION, dir.x, dir.y, dir.z);
rlm@0 486 break;
rlm@0 487 case InnerAngle:
rlm@0 488 if (!src.isDirectional())
rlm@0 489 return;
rlm@0 490
rlm@0 491 alSourcef(id, AL_CONE_INNER_ANGLE, src.getInnerAngle());
rlm@0 492 break;
rlm@0 493 case OuterAngle:
rlm@0 494 if (!src.isDirectional())
rlm@0 495 return;
rlm@0 496
rlm@0 497 alSourcef(id, AL_CONE_OUTER_ANGLE, src.getOuterAngle());
rlm@0 498 break;
rlm@0 499 case IsDirectional:
rlm@0 500 if (src.isDirectional()){
rlm@0 501 updateSourceParam(src, AudioParam.Direction);
rlm@0 502 updateSourceParam(src, AudioParam.InnerAngle);
rlm@0 503 updateSourceParam(src, AudioParam.OuterAngle);
rlm@0 504 alSourcef(id, AL_CONE_OUTER_GAIN, 0);
rlm@0 505 }else{
rlm@0 506 alSourcef(id, AL_CONE_INNER_ANGLE, 360);
rlm@0 507 alSourcef(id, AL_CONE_OUTER_ANGLE, 360);
rlm@0 508 alSourcef(id, AL_CONE_OUTER_GAIN, 1f);
rlm@0 509 }
rlm@0 510 break;
rlm@0 511 case DryFilter:
rlm@0 512 if (src.getDryFilter() != null){
rlm@0 513 Filter f = src.getDryFilter();
rlm@0 514 if (f.isUpdateNeeded()){
rlm@0 515 updateFilter(f);
rlm@0 516
rlm@0 517 // NOTE: must re-attach filter for changes to apply.
rlm@0 518 alSourcei(id, EFX10.AL_DIRECT_FILTER, f.getId());
rlm@0 519 }
rlm@0 520 }else{
rlm@0 521 alSourcei(id, EFX10.AL_DIRECT_FILTER, EFX10.AL_FILTER_NULL);
rlm@0 522 }
rlm@0 523 break;
rlm@0 524 case Looping:
rlm@0 525 if (src.isLooping()){
rlm@0 526 if (!(src.getAudioData() instanceof AudioStream)){
rlm@0 527 alSourcei(id, AL_LOOPING, AL_TRUE);
rlm@0 528 }
rlm@0 529 }else{
rlm@0 530 alSourcei(id, AL_LOOPING, AL_FALSE);
rlm@0 531 }
rlm@0 532 break;
rlm@0 533 case Volume:
rlm@0 534 alSourcef(id, AL_GAIN, src.getVolume());
rlm@0 535 break;
rlm@0 536 case Pitch:
rlm@0 537 alSourcef(id, AL_PITCH, src.getPitch());
rlm@0 538 break;
rlm@0 539 }
rlm@0 540 }
rlm@0 541 }
rlm@0 542
rlm@0 543 private void setSourceParams(int id, AudioNode src, boolean forceNonLoop){
rlm@0 544 if (src.isPositional()){
rlm@0 545 Vector3f pos = src.getWorldTranslation();
rlm@0 546 Vector3f vel = src.getVelocity();
rlm@0 547 alSource3f(id, AL_POSITION, pos.x, pos.y, pos.z);
rlm@0 548 alSource3f(id, AL_VELOCITY, vel.x, vel.y, vel.z);
rlm@0 549 alSourcef(id, AL_MAX_DISTANCE, src.getMaxDistance());
rlm@0 550 alSourcef(id, AL_REFERENCE_DISTANCE, src.getRefDistance());
rlm@0 551 alSourcei(id, AL_SOURCE_RELATIVE, AL_FALSE);
rlm@0 552
rlm@0 553 if (src.isReverbEnabled()){
rlm@0 554 int filter = EFX10.AL_FILTER_NULL;
rlm@0 555 if (src.getReverbFilter() != null){
rlm@0 556 Filter f = src.getReverbFilter();
rlm@0 557 if (f.isUpdateNeeded()){
rlm@0 558 updateFilter(f);
rlm@0 559 }
rlm@0 560 filter = f.getId();
rlm@0 561 }
rlm@0 562 AL11.alSource3i(id, EFX10.AL_AUXILIARY_SEND_FILTER, reverbFxSlot, 0, filter);
rlm@0 563 }
rlm@0 564 }else{
rlm@0 565 // play in headspace
rlm@0 566 alSourcei(id, AL_SOURCE_RELATIVE, AL_TRUE);
rlm@0 567 alSource3f(id, AL_POSITION, 0,0,0);
rlm@0 568 alSource3f(id, AL_VELOCITY, 0,0,0);
rlm@0 569 }
rlm@0 570
rlm@0 571 if (src.getDryFilter() != null){
rlm@0 572 Filter f = src.getDryFilter();
rlm@0 573 if (f.isUpdateNeeded()){
rlm@0 574 updateFilter(f);
rlm@0 575
rlm@0 576 // NOTE: must re-attach filter for changes to apply.
rlm@0 577 alSourcei(id, EFX10.AL_DIRECT_FILTER, f.getId());
rlm@0 578 }
rlm@0 579 }
rlm@0 580
rlm@0 581 if (forceNonLoop){
rlm@0 582 alSourcei(id, AL_LOOPING, AL_FALSE);
rlm@0 583 }else{
rlm@0 584 alSourcei(id, AL_LOOPING, src.isLooping() ? AL_TRUE : AL_FALSE);
rlm@0 585 }
rlm@0 586 alSourcef(id, AL_GAIN, src.getVolume());
rlm@0 587 alSourcef(id, AL_PITCH, src.getPitch());
rlm@0 588 alSourcef(id, AL11.AL_SEC_OFFSET, src.getTimeOffset());
rlm@0 589
rlm@0 590 if (src.isDirectional()){
rlm@0 591 Vector3f dir = src.getDirection();
rlm@0 592 alSource3f(id, AL_DIRECTION, dir.x, dir.y, dir.z);
rlm@0 593 alSourcef(id, AL_CONE_INNER_ANGLE, src.getInnerAngle());
rlm@0 594 alSourcef(id, AL_CONE_OUTER_ANGLE, src.getOuterAngle());
rlm@0 595 alSourcef(id, AL_CONE_OUTER_GAIN, 0);
rlm@0 596 }else{
rlm@0 597 alSourcef(id, AL_CONE_INNER_ANGLE, 360);
rlm@0 598 alSourcef(id, AL_CONE_OUTER_ANGLE, 360);
rlm@0 599 alSourcef(id, AL_CONE_OUTER_GAIN, 1f);
rlm@0 600 }
rlm@0 601 }
rlm@0 602
rlm@0 603 public void updateListenerParam(Listener listener, ListenerParam param){
rlm@0 604 checkDead();
rlm@0 605 synchronized (threadLock){
rlm@0 606 while (!threadLock.get()){
rlm@0 607 try {
rlm@0 608 threadLock.wait();
rlm@0 609 } catch (InterruptedException ex) {
rlm@0 610 }
rlm@0 611 }
rlm@0 612 if (audioDisabled)
rlm@0 613 return;
rlm@0 614
rlm@0 615 switch (param){
rlm@0 616 case Position:
rlm@0 617 Vector3f pos = listener.getLocation();
rlm@0 618 alListener3f(AL_POSITION, pos.x, pos.y, pos.z);
rlm@0 619 break;
rlm@0 620 case Rotation:
rlm@0 621 Vector3f dir = listener.getDirection();
rlm@0 622 Vector3f up = listener.getUp();
rlm@0 623 fb.rewind();
rlm@0 624 fb.put(dir.x).put(dir.y).put(dir.z);
rlm@0 625 fb.put(up.x).put(up.y).put(up.z);
rlm@0 626 fb.flip();
rlm@0 627 alListener(AL_ORIENTATION, fb);
rlm@0 628 break;
rlm@0 629 case Velocity:
rlm@0 630 Vector3f vel = listener.getVelocity();
rlm@0 631 alListener3f(AL_VELOCITY, vel.x, vel.y, vel.z);
rlm@0 632 break;
rlm@0 633 case Volume:
rlm@0 634 alListenerf(AL_GAIN, listener.getVolume());
rlm@0 635 break;
rlm@0 636 }
rlm@0 637 }
rlm@0 638 }
rlm@0 639
rlm@0 640 private void setListenerParams(Listener listener){
rlm@0 641 Vector3f pos = listener.getLocation();
rlm@0 642 Vector3f vel = listener.getVelocity();
rlm@0 643 Vector3f dir = listener.getDirection();
rlm@0 644 Vector3f up = listener.getUp();
rlm@0 645
rlm@0 646 alListener3f(AL_POSITION, pos.x, pos.y, pos.z);
rlm@0 647 alListener3f(AL_VELOCITY, vel.x, vel.y, vel.z);
rlm@0 648 fb.rewind();
rlm@0 649 fb.put(dir.x).put(dir.y).put(dir.z);
rlm@0 650 fb.put(up.x).put(up.y).put(up.z);
rlm@0 651 fb.flip();
rlm@0 652 alListener(AL_ORIENTATION, fb);
rlm@0 653 alListenerf(AL_GAIN, listener.getVolume());
rlm@0 654 }
rlm@0 655
rlm@0 656 private int newChannel(){
rlm@0 657 if (freeChans.size() > 0)
rlm@0 658 return freeChans.remove(0);
rlm@0 659 else if (nextChan < channels.length){
rlm@0 660 return nextChan++;
rlm@0 661 }else{
rlm@0 662 return -1;
rlm@0 663 }
rlm@0 664 }
rlm@0 665
rlm@0 666 private void freeChannel(int index){
rlm@0 667 if (index == nextChan-1){
rlm@0 668 nextChan--;
rlm@0 669 } else{
rlm@0 670 freeChans.add(index);
rlm@0 671 }
rlm@0 672 }
rlm@0 673
rlm@0 674 public void setEnvironment(Environment env){
rlm@0 675 checkDead();
rlm@0 676 synchronized (threadLock){
rlm@0 677 while (!threadLock.get()){
rlm@0 678 try {
rlm@0 679 threadLock.wait();
rlm@0 680 } catch (InterruptedException ex) {
rlm@0 681 }
rlm@0 682 }
rlm@0 683 if (audioDisabled)
rlm@0 684 return;
rlm@0 685
rlm@0 686 EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_DENSITY, env.getDensity());
rlm@0 687 EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_DIFFUSION, env.getDiffusion());
rlm@0 688 EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_GAIN, env.getGain());
rlm@0 689 EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_GAINHF, env.getGainHf());
rlm@0 690 EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_DECAY_TIME, env.getDecayTime());
rlm@0 691 EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_DECAY_HFRATIO, env.getDecayHFRatio());
rlm@0 692 EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_REFLECTIONS_GAIN, env.getReflectGain());
rlm@0 693 EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_REFLECTIONS_DELAY, env.getReflectDelay());
rlm@0 694 EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_LATE_REVERB_GAIN, env.getLateReverbGain());
rlm@0 695 EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_LATE_REVERB_DELAY, env.getLateReverbDelay());
rlm@0 696 EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_AIR_ABSORPTION_GAINHF, env.getAirAbsorbGainHf());
rlm@0 697 EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_ROOM_ROLLOFF_FACTOR, env.getRoomRolloffFactor());
rlm@0 698
rlm@0 699 // attach effect to slot
rlm@0 700 EFX10.alAuxiliaryEffectSloti(reverbFxSlot, EFX10.AL_EFFECTSLOT_EFFECT, reverbFx);
rlm@0 701 }
rlm@0 702 }
rlm@0 703
rlm@0 704 private boolean fillBuffer(AudioStream stream, int id){
rlm@0 705 int size = 0;
rlm@0 706 int result;
rlm@0 707
rlm@0 708 while (size < arrayBuf.length){
rlm@0 709 result = stream.readSamples(arrayBuf, size, arrayBuf.length - size);
rlm@0 710
rlm@0 711 if(result > 0){
rlm@0 712 size += result;
rlm@0 713 }else{
rlm@0 714 break;
rlm@0 715 }
rlm@0 716 }
rlm@0 717
rlm@0 718 if(size == 0)
rlm@0 719 return false;
rlm@0 720
rlm@0 721 nativeBuf.clear();
rlm@0 722 nativeBuf.put(arrayBuf, 0, size);
rlm@0 723 nativeBuf.flip();
rlm@0 724
rlm@0 725 alBufferData(id, convertFormat(stream), nativeBuf, stream.getSampleRate());
rlm@0 726
rlm@0 727 return true;
rlm@0 728 }
rlm@0 729
rlm@0 730 private boolean fillStreamingSource(int sourceId, AudioStream stream){
rlm@0 731 if (!stream.isOpen())
rlm@0 732 return false;
rlm@0 733
rlm@0 734 boolean active = true;
rlm@0 735 int processed = alGetSourcei(sourceId, AL_BUFFERS_PROCESSED);
rlm@0 736
rlm@0 737 // while((processed--) != 0){
rlm@0 738 if (processed > 0){
rlm@0 739 int buffer;
rlm@0 740
rlm@0 741 ib.position(0).limit(1);
rlm@0 742 alSourceUnqueueBuffers(sourceId, ib);
rlm@0 743 buffer = ib.get(0);
rlm@0 744
rlm@0 745 active = fillBuffer(stream, buffer);
rlm@0 746
rlm@0 747 ib.position(0).limit(1);
rlm@0 748 ib.put(0, buffer);
rlm@0 749 alSourceQueueBuffers(sourceId, ib);
rlm@0 750 }
rlm@0 751
rlm@0 752 if (!active && stream.isOpen())
rlm@0 753 stream.close();
rlm@0 754
rlm@0 755 return active;
rlm@0 756 }
rlm@0 757
rlm@0 758 private boolean attachStreamToSource(int sourceId, AudioStream stream){
rlm@0 759 boolean active = true;
rlm@0 760 for (int id : stream.getIds()){
rlm@0 761 active = fillBuffer(stream, id);
rlm@0 762 ib.position(0).limit(1);
rlm@0 763 ib.put(id).flip();
rlm@0 764 alSourceQueueBuffers(sourceId, ib);
rlm@0 765 }
rlm@0 766 return active;
rlm@0 767 }
rlm@0 768
rlm@0 769 private boolean attachBufferToSource(int sourceId, AudioBuffer buffer){
rlm@0 770 alSourcei(sourceId, AL_BUFFER, buffer.getId());
rlm@0 771 return true;
rlm@0 772 }
rlm@0 773
rlm@0 774 private boolean attachAudioToSource(int sourceId, AudioData data){
rlm@0 775 if (data instanceof AudioBuffer){
rlm@0 776 return attachBufferToSource(sourceId, (AudioBuffer) data);
rlm@0 777 }else if (data instanceof AudioStream){
rlm@0 778 return attachStreamToSource(sourceId, (AudioStream) data);
rlm@0 779 }
rlm@0 780 throw new UnsupportedOperationException();
rlm@0 781 }
rlm@0 782
rlm@0 783 private void clearChannel(int index){
rlm@0 784 // make room at this channel
rlm@0 785 if (chanSrcs[index] != null){
rlm@0 786 AudioNode src = chanSrcs[index];
rlm@0 787
rlm@0 788 int sourceId = channels[index];
rlm@0 789 alSourceStop(sourceId);
rlm@0 790
rlm@0 791 if (src.getAudioData() instanceof AudioStream){
rlm@0 792 AudioStream str = (AudioStream) src.getAudioData();
rlm@0 793 ib.position(0).limit(STREAMING_BUFFER_COUNT);
rlm@0 794 ib.put(str.getIds()).flip();
rlm@0 795 alSourceUnqueueBuffers(sourceId, ib);
rlm@0 796 }else if (src.getAudioData() instanceof AudioBuffer){
rlm@0 797 alSourcei(sourceId, AL_BUFFER, 0);
rlm@0 798 }
rlm@0 799
rlm@0 800 if (src.getDryFilter() != null){
rlm@0 801 // detach filter
rlm@0 802 alSourcei(sourceId, EFX10.AL_DIRECT_FILTER, EFX10.AL_FILTER_NULL);
rlm@0 803 }
rlm@0 804 if (src.isPositional()){
rlm@0 805 AudioNode pas = (AudioNode) src;
rlm@0 806 if (pas.isReverbEnabled()) {
rlm@0 807 AL11.alSource3i(sourceId, EFX10.AL_AUXILIARY_SEND_FILTER, 0, 0, EFX10.AL_FILTER_NULL);
rlm@0 808 }
rlm@0 809 }
rlm@0 810
rlm@0 811 chanSrcs[index] = null;
rlm@0 812 }
rlm@0 813 }
rlm@0 814
rlm@0 815 public void update(float tpf){
rlm@0 816 //ByteBuffer test = BufferUtils.createByteBuffer(1);
rlm@0 817 //AurellemTransport.getAuxSamples(AL.getDevice(), test);
rlm@0 818 }
rlm@0 819
rlm@0 820 Vector<Byte> fullWaveData1 = new Vector<Byte>();
rlm@0 821 Vector<Byte> fullWaveData2 = new Vector<Byte>();
rlm@0 822
rlm@0 823 public void updateInThread(float tpf){
rlm@0 824 if (audioDisabled)
rlm@0 825 return;
rlm@0 826
rlm@0 827 step();
rlm@0 828 ByteBuffer test = BufferUtils.createByteBuffer(4096);
rlm@0 829 test.clear();
rlm@0 830 this.getMainSamples(test);
rlm@0 831 byte[] waveData = new byte[4096];
rlm@0 832 test.get(waveData, 0, 4096);
rlm@0 833 //System.out.println("J DATA:");
rlm@0 834 /*for (int j = 0; j < 1; j++){
rlm@0 835 for(int i = 64 * j; i < (64*j) + 64; i++){
rlm@0 836 System.out.print(waveData[i]);
rlm@0 837 }
rlm@0 838 System.out.println();
rlm@0 839 }*/
rlm@0 840
rlm@0 841 ByteBuffer test2 = BufferUtils.createByteBuffer(4096);
rlm@0 842 test2.clear();
rlm@0 843 this.getAuxSamples(test2);
rlm@0 844 byte[] waveData2 = new byte[4096];
rlm@0 845 test2.get(waveData2, 0, 4096);
rlm@0 846 //System.out.print("wave1:");
rlm@0 847 //for (int j = 0; j< 32; j++){
rlm@0 848 // System.out.print(waveData[j]+",");
rlm@0 849 // }
rlm@0 850 //System.out.println();
rlm@0 851 //System.out.print("wave2:");
rlm@0 852 // for (int j = 0; j< 4096; j++){
rlm@0 853 // if (waveData2[j] != 0){
rlm@0 854 // System.out.println("fucked at : " + j);
rlm@0 855 // }
rlm@0 856
rlm@0 857 /* System.out.print(waveData2[j]+",");
rlm@0 858 if (0 == (j % 64)){System.out.println();}*/
rlm@0 859 //}
rlm@0 860 //System.out.println();
rlm@0 861
rlm@0 862 for (byte b : waveData){
rlm@0 863 this.fullWaveData1.add(b);
rlm@0 864 }
rlm@0 865
rlm@0 866 for (byte b : waveData2){
rlm@0 867 this.fullWaveData2.add(b);
rlm@0 868 }
rlm@0 869
rlm@0 870
rlm@0 871 for (int i = 0; i < channels.length; i++){
rlm@0 872 AudioNode src = chanSrcs[i];
rlm@0 873 if (src == null)
rlm@0 874 continue;
rlm@0 875
rlm@0 876 int sourceId = channels[i];
rlm@0 877
rlm@0 878 // is the source bound to this channel
rlm@0 879 // if false, it's an instanced playback
rlm@0 880 boolean boundSource = i == src.getChannel();
rlm@0 881
rlm@0 882 // source's data is streaming
rlm@0 883 boolean streaming = src.getAudioData() instanceof AudioStream;
rlm@0 884
rlm@0 885 // only buffered sources can be bound
rlm@0 886 assert (boundSource && streaming) || (!streaming);
rlm@0 887
rlm@0 888 int state = alGetSourcei(sourceId, AL_SOURCE_STATE);
rlm@0 889 boolean wantPlaying = src.getStatus() == Status.Playing;
rlm@0 890 boolean stopped = state == AL_STOPPED;
rlm@0 891
rlm@0 892 if (streaming && wantPlaying){
rlm@0 893 AudioStream stream = (AudioStream) src.getAudioData();
rlm@0 894 if (stream.isOpen()){
rlm@0 895 fillStreamingSource(sourceId, stream);
rlm@0 896 if (stopped)
rlm@0 897 alSourcePlay(sourceId);
rlm@0 898
rlm@0 899 }else{
rlm@0 900 if (stopped){
rlm@0 901 // became inactive
rlm@0 902 src.setStatus(Status.Stopped);
rlm@0 903 src.setChannel(-1);
rlm@0 904 clearChannel(i);
rlm@0 905 freeChannel(i);
rlm@0 906
rlm@0 907 // And free the audio since it cannot be
rlm@0 908 // played again anyway.
rlm@0 909 deleteAudioData(stream);
rlm@0 910 }
rlm@0 911 }
rlm@0 912 }else if (!streaming){
rlm@0 913 boolean paused = state == AL_PAUSED;
rlm@0 914
rlm@0 915 // make sure OAL pause state & source state coincide
rlm@0 916 assert (src.getStatus() == Status.Paused && paused) || (!paused);
rlm@0 917
rlm@0 918 if (stopped){
rlm@0 919 if (boundSource){
rlm@0 920 src.setStatus(Status.Stopped);
rlm@0 921 src.setChannel(-1);
rlm@0 922 }
rlm@0 923 clearChannel(i);
rlm@0 924 freeChannel(i);
rlm@0 925 }
rlm@0 926 }
rlm@0 927 }
rlm@0 928 }
rlm@0 929
rlm@0 930 public void setListener(Listener listener) {
rlm@0 931 checkDead();
rlm@0 932 synchronized (threadLock){
rlm@0 933 while (!threadLock.get()){
rlm@0 934 try {
rlm@0 935 threadLock.wait();
rlm@0 936 } catch (InterruptedException ex) {
rlm@0 937 }
rlm@0 938 }
rlm@0 939 if (audioDisabled)
rlm@0 940 return;
rlm@0 941
rlm@0 942 if (this.listener != null){
rlm@0 943 // previous listener no longer associated with current
rlm@0 944 // renderer
rlm@0 945 this.listener.setRenderer(null);
rlm@0 946 }
rlm@0 947
rlm@0 948 this.listener = listener;
rlm@0 949 this.listener.setRenderer(this);
rlm@0 950 setListenerParams(listener);
rlm@0 951 }
rlm@0 952 }
rlm@0 953
rlm@0 954 public void playSourceInstance(AudioNode src){
rlm@0 955 checkDead();
rlm@0 956 synchronized (threadLock){
rlm@0 957 while (!threadLock.get()){
rlm@0 958 try {
rlm@0 959 threadLock.wait();
rlm@0 960 } catch (InterruptedException ex) {
rlm@0 961 }
rlm@0 962 }
rlm@0 963 if (audioDisabled)
rlm@0 964 return;
rlm@0 965
rlm@0 966 if (src.getAudioData() instanceof AudioStream)
rlm@0 967 throw new UnsupportedOperationException(
rlm@0 968 "Cannot play instances " +
rlm@0 969 "of audio streams. Use playSource() instead.");
rlm@0 970
rlm@0 971 if (src.getAudioData().isUpdateNeeded()){
rlm@0 972 updateAudioData(src.getAudioData());
rlm@0 973 }
rlm@0 974
rlm@0 975 // create a new index for an audio-channel
rlm@0 976 int index = newChannel();
rlm@0 977 if (index == -1)
rlm@0 978 return;
rlm@0 979
rlm@0 980 int sourceId = channels[index];
rlm@0 981
rlm@0 982 clearChannel(index);
rlm@0 983
rlm@0 984 // set parameters, like position and max distance
rlm@0 985 setSourceParams(sourceId, src, true);
rlm@0 986 attachAudioToSource(sourceId, src.getAudioData());
rlm@0 987 chanSrcs[index] = src;
rlm@0 988
rlm@0 989 // play the channel
rlm@0 990 alSourcePlay(sourceId);
rlm@0 991 }
rlm@0 992 }
rlm@0 993
rlm@0 994
rlm@0 995 public void playSource(AudioNode src) {
rlm@0 996 checkDead();
rlm@0 997 synchronized (threadLock){
rlm@0 998 while (!threadLock.get()){
rlm@0 999 try {
rlm@0 1000 threadLock.wait();
rlm@0 1001 } catch (InterruptedException ex) {
rlm@0 1002 }
rlm@0 1003 }
rlm@0 1004 if (audioDisabled)
rlm@0 1005 return;
rlm@0 1006
rlm@0 1007 //assert src.getStatus() == Status.Stopped || src.getChannel() == -1;
rlm@0 1008
rlm@0 1009 if (src.getStatus() == Status.Playing){
rlm@0 1010 return;
rlm@0 1011 }else if (src.getStatus() == Status.Stopped){
rlm@0 1012
rlm@0 1013 // allocate channel to this source
rlm@0 1014 int index = newChannel();
rlm@0 1015 if (index == -1) {
rlm@0 1016 logger.log(Level.WARNING, "No channel available to play {0}", src);
rlm@0 1017 return;
rlm@0 1018 }
rlm@0 1019 clearChannel(index);
rlm@0 1020 src.setChannel(index);
rlm@0 1021
rlm@0 1022 AudioData data = src.getAudioData();
rlm@0 1023 if (data.isUpdateNeeded())
rlm@0 1024 updateAudioData(data);
rlm@0 1025
rlm@0 1026 chanSrcs[index] = src;
rlm@0 1027 setSourceParams(channels[index], src, false);
rlm@0 1028 attachAudioToSource(channels[index], data);
rlm@0 1029 }
rlm@0 1030
rlm@0 1031 alSourcePlay(channels[src.getChannel()]);
rlm@0 1032 src.setStatus(Status.Playing);
rlm@0 1033 }
rlm@0 1034 }
rlm@0 1035
rlm@0 1036
rlm@0 1037 public void pauseSource(AudioNode src) {
rlm@0 1038 checkDead();
rlm@0 1039 synchronized (threadLock){
rlm@0 1040 while (!threadLock.get()){
rlm@0 1041 try {
rlm@0 1042 threadLock.wait();
rlm@0 1043 } catch (InterruptedException ex) {
rlm@0 1044 }
rlm@0 1045 }
rlm@0 1046 if (audioDisabled)
rlm@0 1047 return;
rlm@0 1048
rlm@0 1049 if (src.getStatus() == Status.Playing){
rlm@0 1050 assert src.getChannel() != -1;
rlm@0 1051
rlm@0 1052 alSourcePause(channels[src.getChannel()]);
rlm@0 1053 src.setStatus(Status.Paused);
rlm@0 1054 }
rlm@0 1055 }
rlm@0 1056 }
rlm@0 1057
rlm@0 1058
rlm@0 1059 public void stopSource(AudioNode src) {
rlm@0 1060 synchronized (threadLock){
rlm@0 1061 while (!threadLock.get()){
rlm@0 1062 try {
rlm@0 1063 threadLock.wait();
rlm@0 1064 } catch (InterruptedException ex) {
rlm@0 1065 }
rlm@0 1066 }
rlm@0 1067 if (audioDisabled)
rlm@0 1068 return;
rlm@0 1069
rlm@0 1070 if (src.getStatus() != Status.Stopped){
rlm@0 1071 int chan = src.getChannel();
rlm@0 1072 assert chan != -1; // if it's not stopped, must have id
rlm@0 1073
rlm@0 1074 src.setStatus(Status.Stopped);
rlm@0 1075 src.setChannel(-1);
rlm@0 1076 clearChannel(chan);
rlm@0 1077 freeChannel(chan);
rlm@0 1078
rlm@0 1079 if (src.getAudioData() instanceof AudioStream) {
rlm@0 1080
rlm@0 1081 AudioStream stream = (AudioStream)src.getAudioData();
rlm@0 1082 if (stream.isOpen()) {
rlm@0 1083 stream.close();
rlm@0 1084 }
rlm@0 1085
rlm@0 1086 // And free the audio since it cannot be
rlm@0 1087 // played again anyway.
rlm@0 1088 deleteAudioData(src.getAudioData());
rlm@0 1089 }
rlm@0 1090 }
rlm@0 1091 }
rlm@0 1092 }
rlm@0 1093
rlm@0 1094 private int convertFormat(AudioData ad){
rlm@0 1095 switch (ad.getBitsPerSample()){
rlm@0 1096 case 8:
rlm@0 1097 if (ad.getChannels() == 1)
rlm@0 1098 return AL_FORMAT_MONO8;
rlm@0 1099 else if (ad.getChannels() == 2)
rlm@0 1100 return AL_FORMAT_STEREO8;
rlm@0 1101
rlm@0 1102 break;
rlm@0 1103 case 16:
rlm@0 1104 if (ad.getChannels() == 1)
rlm@0 1105 return AL_FORMAT_MONO16;
rlm@0 1106 else
rlm@0 1107 return AL_FORMAT_STEREO16;
rlm@0 1108 }
rlm@0 1109 throw new UnsupportedOperationException("Unsupported channels/bits combination: "+
rlm@0 1110 "bits="+ad.getBitsPerSample()+", channels="+ad.getChannels());
rlm@0 1111 }
rlm@0 1112
rlm@0 1113 private void updateAudioBuffer(AudioBuffer ab){
rlm@0 1114 int id = ab.getId();
rlm@0 1115 if (ab.getId() == -1){
rlm@0 1116 ib.position(0).limit(1);
rlm@0 1117 alGenBuffers(ib);
rlm@0 1118 id = ib.get(0);
rlm@0 1119 ab.setId(id);
rlm@0 1120 }
rlm@0 1121
rlm@0 1122 ab.getData().clear();
rlm@0 1123 alBufferData(id, convertFormat(ab), ab.getData(), ab.getSampleRate());
rlm@0 1124 ab.clearUpdateNeeded();
rlm@0 1125 }
rlm@0 1126
rlm@0 1127 private void updateAudioStream(AudioStream as){
rlm@0 1128 if (as.getIds() != null){
rlm@0 1129 deleteAudioData(as);
rlm@0 1130 }
rlm@0 1131
rlm@0 1132 int[] ids = new int[STREAMING_BUFFER_COUNT];
rlm@0 1133 ib.position(0).limit(STREAMING_BUFFER_COUNT);
rlm@0 1134 alGenBuffers(ib);
rlm@0 1135 ib.position(0).limit(STREAMING_BUFFER_COUNT);
rlm@0 1136 ib.get(ids);
rlm@0 1137
rlm@0 1138 as.setIds(ids);
rlm@0 1139 as.clearUpdateNeeded();
rlm@0 1140 }
rlm@0 1141
rlm@0 1142 private void updateAudioData(AudioData ad){
rlm@0 1143 if (ad instanceof AudioBuffer){
rlm@0 1144 updateAudioBuffer((AudioBuffer) ad);
rlm@0 1145 }else if (ad instanceof AudioStream){
rlm@0 1146 updateAudioStream((AudioStream) ad);
rlm@0 1147 }
rlm@0 1148 }
rlm@0 1149
rlm@0 1150 public void deleteAudioData(AudioData ad){
rlm@0 1151 synchronized (threadLock){
rlm@0 1152 while (!threadLock.get()){
rlm@0 1153 try {
rlm@0 1154 threadLock.wait();
rlm@0 1155 } catch (InterruptedException ex) {
rlm@0 1156 }
rlm@0 1157 }
rlm@0 1158 if (audioDisabled)
rlm@0 1159 return;
rlm@0 1160
rlm@0 1161 if (ad instanceof AudioBuffer){
rlm@0 1162 AudioBuffer ab = (AudioBuffer) ad;
rlm@0 1163 int id = ab.getId();
rlm@0 1164 if (id != -1){
rlm@0 1165 ib.put(0,id);
rlm@0 1166 ib.position(0).limit(1);
rlm@0 1167 alDeleteBuffers(ib);
rlm@0 1168 ab.resetObject();
rlm@0 1169 }
rlm@0 1170 }else if (ad instanceof AudioStream){
rlm@0 1171 AudioStream as = (AudioStream) ad;
rlm@0 1172 int[] ids = as.getIds();
rlm@0 1173 if (ids != null){
rlm@0 1174 ib.clear();
rlm@0 1175 ib.put(ids).flip();
rlm@0 1176 alDeleteBuffers(ib);
rlm@0 1177 as.resetObject();
rlm@0 1178 }
rlm@0 1179 }
rlm@0 1180 }
rlm@0 1181 }
rlm@0 1182
rlm@0 1183 }