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