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 }
|