--- /dev/null
+/*\r
+ * Copyright (c) 2009-2012 jMonkeyEngine\r
+ * All rights reserved.\r
+ *\r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are\r
+ * met:\r
+ *\r
+ * * Redistributions of source code must retain the above copyright\r
+ * notice, this list of conditions and the following disclaimer.\r
+ *\r
+ * * Redistributions in binary form must reproduce the above copyright\r
+ * notice, this list of conditions and the following disclaimer in the\r
+ * documentation and/or other materials provided with the distribution.\r
+ *\r
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors\r
+ * may be used to endorse or promote products derived from this software\r
+ * without specific prior written permission.\r
+ *\r
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\r
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED\r
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\r
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\r
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\r
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\r
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\r
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\r
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\r
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\r
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+ */\r
+package com.jme3.audio.android;\r
+\r
+import com.jme3.audio.*;\r
+import com.jme3.audio.AudioSource.Status;\r
+import com.jme3.math.Vector3f;\r
+import com.jme3.util.BufferUtils;\r
+import com.jme3.util.NativeObjectManager;\r
+import java.nio.ByteBuffer;\r
+import java.nio.FloatBuffer;\r
+import java.nio.IntBuffer;\r
+import java.util.ArrayList;\r
+import java.util.concurrent.atomic.AtomicBoolean;\r
+import java.util.logging.Level;\r
+import java.util.logging.Logger;\r
+\r
+public class AndroidOpenALSoftAudioRenderer implements AudioRenderer, Runnable {\r
+\r
+ private static final Logger logger = Logger.getLogger(AndroidOpenALSoftAudioRenderer.class.getName());\r
+ private final NativeObjectManager objManager = new NativeObjectManager();\r
+ // When multiplied by STREAMING_BUFFER_COUNT, will equal 44100 * 2 * 2\r
+ // which is exactly 1 second of audio.\r
+ private static final int BUFFER_SIZE = 35280;\r
+ private static final int STREAMING_BUFFER_COUNT = 5;\r
+ private final static int MAX_NUM_CHANNELS = 64;\r
+ private IntBuffer ib = BufferUtils.createIntBuffer(1);\r
+ private final FloatBuffer fb = BufferUtils.createVector3Buffer(2);\r
+ private final ByteBuffer nativeBuf = BufferUtils.createByteBuffer(BUFFER_SIZE);\r
+ private final byte[] arrayBuf = new byte[BUFFER_SIZE];\r
+ private int[] channels;\r
+ private AudioSource[] chanSrcs;\r
+ private int nextChan = 0;\r
+ private ArrayList<Integer> freeChans = new ArrayList<Integer>();\r
+ private Listener listener;\r
+ private boolean audioDisabled = false;\r
+ private boolean supportEfx = false;\r
+ private int auxSends = 0;\r
+ private int reverbFx = -1;\r
+ private int reverbFxSlot = -1;\r
+ // Update audio 20 times per second\r
+ private static final float UPDATE_RATE = 0.05f;\r
+ private final Thread audioThread = new Thread(this, "jME3 Audio Thread");\r
+ private final AtomicBoolean threadLock = new AtomicBoolean(false);\r
+\r
+ public AndroidOpenALSoftAudioRenderer() {\r
+ }\r
+\r
+ public void initialize() {\r
+ if (!audioThread.isAlive()) {\r
+ audioThread.setDaemon(true);\r
+ audioThread.setPriority(Thread.NORM_PRIORITY + 1);\r
+ audioThread.start();\r
+ } else {\r
+ throw new IllegalStateException("Initialize already called");\r
+ }\r
+ }\r
+\r
+ private void checkDead() {\r
+ if (audioThread.getState() == Thread.State.TERMINATED) {\r
+ throw new IllegalStateException("Audio thread is terminated");\r
+ }\r
+ }\r
+\r
+ public void run() {\r
+ initInThread();\r
+ synchronized (threadLock) {\r
+ threadLock.set(true);\r
+ threadLock.notifyAll();\r
+ }\r
+\r
+ long updateRateNanos = (long) (UPDATE_RATE * 1000000000);\r
+ mainloop:\r
+ while (true) {\r
+ long startTime = System.nanoTime();\r
+\r
+ if (Thread.interrupted()) {\r
+ break;\r
+ }\r
+\r
+ synchronized (threadLock) {\r
+ updateInThread(UPDATE_RATE);\r
+ }\r
+\r
+ long endTime = System.nanoTime();\r
+ long diffTime = endTime - startTime;\r
+\r
+ if (diffTime < updateRateNanos) {\r
+ long desiredEndTime = startTime + updateRateNanos;\r
+ while (System.nanoTime() < desiredEndTime) {\r
+ try {\r
+ Thread.sleep(1);\r
+ } catch (InterruptedException ex) {\r
+ break mainloop;\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ logger.log(Level.INFO, "Exiting audioThread run loop");\r
+ synchronized (threadLock) {\r
+ cleanupInThread();\r
+ }\r
+ }\r
+\r
+ public void initInThread() {\r
+ try {\r
+ if (!alIsCreated()) {\r
+ //AL.create();\r
+ logger.log(Level.INFO, "Creating OpenAL Soft Renderer");\r
+ alCreate();\r
+ checkError(false);\r
+ }\r
+// } catch (OpenALException ex) {\r
+// logger.log(Level.SEVERE, "Failed to load audio library", ex);\r
+// audioDisabled = true;\r
+// return;\r
+// } catch (LWJGLException ex) {\r
+// logger.log(Level.SEVERE, "Failed to load audio library", ex);\r
+// audioDisabled = true;\r
+// return;\r
+ } catch (UnsatisfiedLinkError ex) {\r
+ logger.log(Level.SEVERE, "Failed to load audio library", ex);\r
+ audioDisabled = true;\r
+ return;\r
+ }\r
+\r
+ //ALCdevice device = AL.getDevice(); /* device maintained in jni */\r
+ //String deviceName = ALC10.alcGetString(device, ALC10.ALC_DEVICE_SPECIFIER);\r
+ String deviceName = alcGetString(AL.ALC_DEVICE_SPECIFIER);\r
+\r
+ logger.log(Level.INFO, "Audio Device: {0}", deviceName);\r
+ //logger.log(Level.INFO, "Audio Vendor: {0}", alGetString(AL_VENDOR));\r
+ //logger.log(Level.INFO, "Audio Renderer: {0}", alGetString(AL_RENDERER));\r
+ //logger.log(Level.INFO, "Audio Version: {0}", alGetString(AL_VERSION));\r
+ logger.log(Level.INFO, "Audio Vendor: {0}", alGetString(AL.AL_VENDOR));\r
+ logger.log(Level.INFO, "Audio Renderer: {0}", alGetString(AL.AL_RENDERER));\r
+ logger.log(Level.INFO, "Audio Version: {0}", alGetString(AL.AL_VERSION));\r
+\r
+ // Find maximum # of sources supported by this implementation\r
+ ArrayList<Integer> channelList = new ArrayList<Integer>();\r
+ for (int i = 0; i < MAX_NUM_CHANNELS; i++) {\r
+// logger.log(Level.INFO, "Generating Source for index: {0}", i);\r
+ int chan = alGenSources();\r
+// logger.log(Level.INFO, "chan: {0}", chan);\r
+ //if (alGetError() != 0) {\r
+ if (checkError(false) != 0) {\r
+// logger.log(Level.INFO, "alGetError detected an error");\r
+ break;\r
+ } else {\r
+ channelList.add(chan);\r
+ }\r
+ }\r
+\r
+ channels = new int[channelList.size()];\r
+ for (int i = 0; i < channels.length; i++) {\r
+ channels[i] = channelList.get(i);\r
+ }\r
+\r
+ ib = BufferUtils.createIntBuffer(channels.length);\r
+ chanSrcs = new AudioSource[channels.length];\r
+\r
+ logger.log(Level.INFO, "AudioRenderer supports {0} channels", channels.length);\r
+\r
+// supportEfx = ALC10.alcIsExtensionPresent(device, "ALC_EXT_EFX");\r
+// if (supportEfx) {\r
+// ib.position(0).limit(1);\r
+// ALC10.alcGetInteger(device, EFX10.ALC_EFX_MAJOR_VERSION, ib);\r
+// int major = ib.get(0);\r
+// ib.position(0).limit(1);\r
+// ALC10.alcGetInteger(device, EFX10.ALC_EFX_MINOR_VERSION, ib);\r
+// int minor = ib.get(0);\r
+// logger.log(Level.INFO, "Audio effect extension version: {0}.{1}", new Object[]{major, minor});\r
+//\r
+// ALC10.alcGetInteger(device, EFX10.ALC_MAX_AUXILIARY_SENDS, ib);\r
+// auxSends = ib.get(0);\r
+// logger.log(Level.INFO, "Audio max auxilary sends: {0}", auxSends);\r
+//\r
+// // create slot\r
+// ib.position(0).limit(1);\r
+// EFX10.alGenAuxiliaryEffectSlots(ib);\r
+// reverbFxSlot = ib.get(0);\r
+//\r
+// // create effect\r
+// ib.position(0).limit(1);\r
+// EFX10.alGenEffects(ib);\r
+// reverbFx = ib.get(0);\r
+// EFX10.alEffecti(reverbFx, EFX10.AL_EFFECT_TYPE, EFX10.AL_EFFECT_REVERB);\r
+//\r
+// // attach reverb effect to effect slot\r
+// EFX10.alAuxiliaryEffectSloti(reverbFxSlot, EFX10.AL_EFFECTSLOT_EFFECT, reverbFx);\r
+// } else {\r
+// logger.log(Level.WARNING, "OpenAL EFX not available! Audio effects won't work.");\r
+// }\r
+ }\r
+\r
+ public void cleanupInThread() {\r
+ logger.log(Level.INFO, "cleanupInThread");\r
+ if (audioDisabled) {\r
+ //AL.destroy();\r
+ logger.log(Level.INFO, "Destroying OpenAL Soft Renderer with audioDisabled");\r
+ alDestroy();\r
+ checkError(true);\r
+ return;\r
+ }\r
+\r
+ // stop any playing channels\r
+ for (int i = 0; i < chanSrcs.length; i++) {\r
+ if (chanSrcs[i] != null) {\r
+ clearChannel(i);\r
+ }\r
+ }\r
+\r
+ // delete channel-based sources\r
+ ib.clear();\r
+ ib.put(channels);\r
+ ib.flip();\r
+ //alDeleteSources(ib);\r
+ alDeleteSources(channels.length, ib);\r
+ checkError(true);\r
+\r
+ // delete audio buffers and filters\r
+ objManager.deleteAllObjects(this);\r
+\r
+// if (supportEfx) {\r
+// ib.position(0).limit(1);\r
+// ib.put(0, reverbFx);\r
+// EFX10.alDeleteEffects(ib);\r
+//\r
+// // If this is not allocated, why is it deleted?\r
+// // Commented out to fix native crash in OpenAL.\r
+// ib.position(0).limit(1);\r
+// ib.put(0, reverbFxSlot);\r
+// EFX10.alDeleteAuxiliaryEffectSlots(ib);\r
+// }\r
+//\r
+ //AL.destroy();\r
+ logger.log(Level.INFO, "Destroying OpenAL Soft Renderer");\r
+ alDestroy();\r
+// checkError(true);\r
+ }\r
+\r
+ public void cleanup() {\r
+ logger.log(Level.INFO, "cleanup");\r
+ // kill audio thread\r
+ if (audioThread.isAlive()) {\r
+ logger.log(Level.INFO, "Interrupting audioThread");\r
+ audioThread.interrupt();\r
+ }\r
+ }\r
+\r
+ private void updateFilter(Filter f) {\r
+// int id = f.getId();\r
+// if (id == -1) {\r
+// ib.position(0).limit(1);\r
+// EFX10.alGenFilters(ib);\r
+// id = ib.get(0);\r
+// f.setId(id);\r
+//\r
+// objManager.registerForCleanup(f);\r
+// }\r
+//\r
+// if (f instanceof LowPassFilter) {\r
+// LowPassFilter lpf = (LowPassFilter) f;\r
+// EFX10.alFilteri(id, EFX10.AL_FILTER_TYPE, EFX10.AL_FILTER_LOWPASS);\r
+// EFX10.alFilterf(id, EFX10.AL_LOWPASS_GAIN, lpf.getVolume());\r
+// EFX10.alFilterf(id, EFX10.AL_LOWPASS_GAINHF, lpf.getHighFreqVolume());\r
+// } else {\r
+// throw new UnsupportedOperationException("Filter type unsupported: "\r
+// + f.getClass().getName());\r
+// }\r
+//\r
+// f.clearUpdateNeeded();\r
+ }\r
+\r
+ public void updateSourceParam(AudioSource src, AudioParam param) {\r
+ checkDead();\r
+ synchronized (threadLock) {\r
+ while (!threadLock.get()) {\r
+ try {\r
+ threadLock.wait();\r
+ } catch (InterruptedException ex) {\r
+ }\r
+ }\r
+ if (audioDisabled) {\r
+ return;\r
+ }\r
+\r
+ // There is a race condition in AudioSource that can\r
+ // cause this to be called for a node that has been\r
+ // detached from its channel. For example, setVolume()\r
+ // called from the render thread may see that that AudioSource\r
+ // still has a channel value but the audio thread may\r
+ // clear that channel before setVolume() gets to call\r
+ // updateSourceParam() (because the audio stopped playing\r
+ // on its own right as the volume was set). In this case,\r
+ // it should be safe to just ignore the update\r
+ if (src.getChannel() < 0) {\r
+ return;\r
+ }\r
+\r
+ assert src.getChannel() >= 0;\r
+\r
+ int id = channels[src.getChannel()];\r
+ switch (param) {\r
+ case Position:\r
+ if (!src.isPositional()) {\r
+ return;\r
+ }\r
+\r
+ Vector3f pos = src.getPosition();\r
+ //alSource3f(id, AL_POSITION, pos.x, pos.y, pos.z);\r
+ alSource3f(id, AL.AL_POSITION, pos.x, pos.y, pos.z);\r
+ checkError(true);\r
+ break;\r
+ case Velocity:\r
+ if (!src.isPositional()) {\r
+ return;\r
+ }\r
+\r
+ Vector3f vel = src.getVelocity();\r
+ //alSource3f(id, AL_VELOCITY, vel.x, vel.y, vel.z);\r
+ alSource3f(id, AL.AL_VELOCITY, vel.x, vel.y, vel.z);\r
+ checkError(true);\r
+ break;\r
+ case MaxDistance:\r
+ if (!src.isPositional()) {\r
+ return;\r
+ }\r
+\r
+ //alSourcef(id, AL_MAX_DISTANCE, src.getMaxDistance());\r
+ alSourcef(id, AL.AL_MAX_DISTANCE, src.getMaxDistance());\r
+ checkError(true);\r
+ break;\r
+ case RefDistance:\r
+ if (!src.isPositional()) {\r
+ return;\r
+ }\r
+\r
+ //alSourcef(id, AL_REFERENCE_DISTANCE, src.getRefDistance());\r
+ alSourcef(id, AL.AL_REFERENCE_DISTANCE, src.getRefDistance());\r
+ checkError(true);\r
+ break;\r
+ case ReverbFilter:\r
+ if (!supportEfx || !src.isPositional() || !src.isReverbEnabled()) {\r
+ return;\r
+ }\r
+\r
+// int filter = EFX10.AL_FILTER_NULL;\r
+// if (src.getReverbFilter() != null) {\r
+// Filter f = src.getReverbFilter();\r
+// if (f.isUpdateNeeded()) {\r
+// updateFilter(f);\r
+// }\r
+// filter = f.getId();\r
+// }\r
+// AL11.alSource3i(id, EFX10.AL_AUXILIARY_SEND_FILTER, reverbFxSlot, 0, filter);\r
+ break;\r
+ case ReverbEnabled:\r
+ if (!supportEfx || !src.isPositional()) {\r
+ return;\r
+ }\r
+\r
+ if (src.isReverbEnabled()) {\r
+ updateSourceParam(src, AudioParam.ReverbFilter);\r
+ } else {\r
+// AL11.alSource3i(id, EFX10.AL_AUXILIARY_SEND_FILTER, 0, 0, EFX10.AL_FILTER_NULL);\r
+ }\r
+ break;\r
+ case IsPositional:\r
+ if (!src.isPositional()) {\r
+ // Play in headspace\r
+ //alSourcei(id, AL_SOURCE_RELATIVE, AL_TRUE);\r
+ alSourcei(id, AL.AL_SOURCE_RELATIVE, AL.AL_TRUE);\r
+ checkError(true);\r
+ //alSource3f(id, AL_POSITION, 0, 0, 0);\r
+ alSource3f(id, AL.AL_POSITION, 0, 0, 0);\r
+ checkError(true);\r
+ //alSource3f(id, AL_VELOCITY, 0, 0, 0);\r
+ alSource3f(id, AL.AL_VELOCITY, 0, 0, 0);\r
+ checkError(true);\r
+\r
+ // Disable reverb\r
+// AL11.alSource3i(id, EFX10.AL_AUXILIARY_SEND_FILTER, 0, 0, EFX10.AL_FILTER_NULL);\r
+ } else {\r
+ //alSourcei(id, AL_SOURCE_RELATIVE, AL_FALSE);\r
+ alSourcei(id, AL.AL_SOURCE_RELATIVE, AL.AL_FALSE);\r
+ checkError(true);\r
+ updateSourceParam(src, AudioParam.Position);\r
+ updateSourceParam(src, AudioParam.Velocity);\r
+ updateSourceParam(src, AudioParam.MaxDistance);\r
+ updateSourceParam(src, AudioParam.RefDistance);\r
+ updateSourceParam(src, AudioParam.ReverbEnabled);\r
+ }\r
+ break;\r
+ case Direction:\r
+ if (!src.isDirectional()) {\r
+ return;\r
+ }\r
+\r
+ Vector3f dir = src.getDirection();\r
+ //alSource3f(id, AL_DIRECTION, dir.x, dir.y, dir.z);\r
+ alSource3f(id, AL.AL_DIRECTION, dir.x, dir.y, dir.z);\r
+ checkError(true);\r
+ break;\r
+ case InnerAngle:\r
+ if (!src.isDirectional()) {\r
+ return;\r
+ }\r
+\r
+ //alSourcef(id, AL_CONE_INNER_ANGLE, src.getInnerAngle());\r
+ alSourcef(id, AL.AL_CONE_INNER_ANGLE, src.getInnerAngle());\r
+ checkError(true);\r
+ break;\r
+ case OuterAngle:\r
+ if (!src.isDirectional()) {\r
+ return;\r
+ }\r
+\r
+ //alSourcef(id, AL_CONE_OUTER_ANGLE, src.getOuterAngle());\r
+ alSourcef(id, AL.AL_CONE_OUTER_ANGLE, src.getOuterAngle());\r
+ checkError(true);\r
+ break;\r
+ case IsDirectional:\r
+ if (src.isDirectional()) {\r
+ updateSourceParam(src, AudioParam.Direction);\r
+ updateSourceParam(src, AudioParam.InnerAngle);\r
+ updateSourceParam(src, AudioParam.OuterAngle);\r
+ //alSourcef(id, AL_CONE_OUTER_GAIN, 0);\r
+ alSourcef(id, AL.AL_CONE_OUTER_GAIN, 0);\r
+ checkError(true);\r
+ } else {\r
+ //alSourcef(id, AL_CONE_INNER_ANGLE, 360);\r
+ alSourcef(id, AL.AL_CONE_INNER_ANGLE, 360);\r
+ checkError(true);\r
+ //alSourcef(id, AL_CONE_OUTER_ANGLE, 360);\r
+ alSourcef(id, AL.AL_CONE_OUTER_ANGLE, 360);\r
+ checkError(true);\r
+ //alSourcef(id, AL_CONE_OUTER_GAIN, 1f);\r
+ alSourcef(id, AL.AL_CONE_OUTER_GAIN, 1f);\r
+ checkError(true);\r
+ }\r
+ break;\r
+// case DryFilter:\r
+// if (!supportEfx) {\r
+// return;\r
+// }\r
+//\r
+// if (src.getDryFilter() != null) {\r
+// Filter f = src.getDryFilter();\r
+// if (f.isUpdateNeeded()) {\r
+// updateFilter(f);\r
+//\r
+// // NOTE: must re-attach filter for changes to apply.\r
+// alSourcei(id, EFX10.AL_DIRECT_FILTER, f.getId());\r
+// }\r
+// } else {\r
+// alSourcei(id, EFX10.AL_DIRECT_FILTER, EFX10.AL_FILTER_NULL);\r
+// }\r
+// break;\r
+ case Looping:\r
+ if (src.isLooping()) {\r
+ if (!(src.getAudioData() instanceof AudioStream)) {\r
+ //alSourcei(id, AL_LOOPING, AL_TRUE);\r
+ alSourcei(id, AL.AL_LOOPING, AL.AL_TRUE);\r
+ checkError(true);\r
+ }\r
+ } else {\r
+ //alSourcei(id, AL_LOOPING, AL_FALSE);\r
+ alSourcei(id, AL.AL_LOOPING, AL.AL_FALSE);\r
+ checkError(true);\r
+ }\r
+ break;\r
+ case Volume:\r
+ //alSourcef(id, AL_GAIN, src.getVolume());\r
+ alSourcef(id, AL.AL_GAIN, src.getVolume());\r
+ checkError(true);\r
+ break;\r
+ case Pitch:\r
+ //alSourcef(id, AL_PITCH, src.getPitch());\r
+ alSourcef(id, AL.AL_PITCH, src.getPitch());\r
+ checkError(true);\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ private void setSourceParams(int id, AudioSource src, boolean forceNonLoop) {\r
+ if (src.isPositional()) {\r
+ Vector3f pos = src.getPosition();\r
+ Vector3f vel = src.getVelocity();\r
+ //alSource3f(id, AL_POSITION, pos.x, pos.y, pos.z);\r
+ alSource3f(id, AL.AL_POSITION, pos.x, pos.y, pos.z);\r
+ checkError(true);\r
+ //alSource3f(id, AL_VELOCITY, vel.x, vel.y, vel.z);\r
+ alSource3f(id, AL.AL_VELOCITY, vel.x, vel.y, vel.z);\r
+ checkError(true);\r
+ //alSourcef(id, AL_MAX_DISTANCE, src.getMaxDistance());\r
+ alSourcef(id, AL.AL_MAX_DISTANCE, src.getMaxDistance());\r
+ checkError(true);\r
+ //alSourcef(id, AL_REFERENCE_DISTANCE, src.getRefDistance());\r
+ alSourcef(id, AL.AL_REFERENCE_DISTANCE, src.getRefDistance());\r
+ checkError(true);\r
+ //alSourcei(id, AL_SOURCE_RELATIVE, AL_FALSE);\r
+ alSourcei(id, AL.AL_SOURCE_RELATIVE, AL.AL_FALSE);\r
+ checkError(true);\r
+\r
+// if (src.isReverbEnabled() && supportEfx) {\r
+// int filter = EFX10.AL_FILTER_NULL;\r
+// if (src.getReverbFilter() != null) {\r
+// Filter f = src.getReverbFilter();\r
+// if (f.isUpdateNeeded()) {\r
+// updateFilter(f);\r
+// }\r
+// filter = f.getId();\r
+// }\r
+// AL11.alSource3i(id, EFX10.AL_AUXILIARY_SEND_FILTER, reverbFxSlot, 0, filter);\r
+// }\r
+ } else {\r
+ // play in headspace\r
+ //alSourcei(id, AL_SOURCE_RELATIVE, AL_TRUE);\r
+ alSourcei(id, AL.AL_SOURCE_RELATIVE, AL.AL_TRUE);\r
+ checkError(true);\r
+ //alSource3f(id, AL_POSITION, 0, 0, 0);\r
+ alSource3f(id, AL.AL_POSITION, 0, 0, 0);\r
+ checkError(true);\r
+ //alSource3f(id, AL_VELOCITY, 0, 0, 0);\r
+ alSource3f(id, AL.AL_VELOCITY, 0, 0, 0);\r
+ checkError(true);\r
+ }\r
+\r
+// if (src.getDryFilter() != null && supportEfx) {\r
+// Filter f = src.getDryFilter();\r
+// if (f.isUpdateNeeded()) {\r
+// updateFilter(f);\r
+//\r
+// // NOTE: must re-attach filter for changes to apply.\r
+// alSourcei(id, EFX10.AL_DIRECT_FILTER, f.getId());\r
+// }\r
+// }\r
+//\r
+ if (forceNonLoop) {\r
+ //alSourcei(id, AL_LOOPING, AL_FALSE);\r
+ alSourcei(id, AL.AL_LOOPING, AL.AL_FALSE);\r
+ checkError(true);\r
+ } else {\r
+ //alSourcei(id, AL_LOOPING, src.isLooping() ? AL_TRUE : AL_FALSE);\r
+ alSourcei(id, AL.AL_LOOPING, src.isLooping() ? AL.AL_TRUE : AL.AL_FALSE);\r
+ checkError(true);\r
+ }\r
+ //alSourcef(id, AL_GAIN, src.getVolume());\r
+ alSourcef(id, AL.AL_GAIN, src.getVolume());\r
+ checkError(true);\r
+ //alSourcef(id, AL_PITCH, src.getPitch());\r
+ alSourcef(id, AL.AL_PITCH, src.getPitch());\r
+ checkError(true);\r
+ //alSourcef(id, AL11.AL_SEC_OFFSET, src.getTimeOffset());\r
+ alSourcef(id, AL.AL_SEC_OFFSET, src.getTimeOffset());\r
+ checkError(true);\r
+\r
+ if (src.isDirectional()) {\r
+ Vector3f dir = src.getDirection();\r
+ //alSource3f(id, AL_DIRECTION, dir.x, dir.y, dir.z);\r
+ alSource3f(id, AL.AL_DIRECTION, dir.x, dir.y, dir.z);\r
+ checkError(true);\r
+ //alSourcef(id, AL_CONE_INNER_ANGLE, src.getInnerAngle());\r
+ alSourcef(id, AL.AL_CONE_INNER_ANGLE, src.getInnerAngle());\r
+ checkError(true);\r
+ //alSourcef(id, AL_CONE_OUTER_ANGLE, src.getOuterAngle());\r
+ alSourcef(id, AL.AL_CONE_OUTER_ANGLE, src.getOuterAngle());\r
+ checkError(true);\r
+ //alSourcef(id, AL_CONE_OUTER_GAIN, 0);\r
+ alSourcef(id, AL.AL_CONE_OUTER_GAIN, 0);\r
+ checkError(true);\r
+ } else {\r
+ //alSourcef(id, AL_CONE_INNER_ANGLE, 360);\r
+ alSourcef(id, AL.AL_CONE_INNER_ANGLE, 360);\r
+ checkError(true);\r
+ //alSourcef(id, AL_CONE_OUTER_ANGLE, 360);\r
+ alSourcef(id, AL.AL_CONE_OUTER_ANGLE, 360);\r
+ checkError(true);\r
+ //alSourcef(id, AL_CONE_OUTER_GAIN, 1f);\r
+ alSourcef(id, AL.AL_CONE_OUTER_GAIN, 1f);\r
+ checkError(true);\r
+ }\r
+ }\r
+\r
+ public void updateListenerParam(Listener listener, ListenerParam param) {\r
+ checkDead();\r
+ synchronized (threadLock) {\r
+ while (!threadLock.get()) {\r
+ try {\r
+ threadLock.wait();\r
+ } catch (InterruptedException ex) {\r
+ }\r
+ }\r
+ if (audioDisabled) {\r
+ return;\r
+ }\r
+\r
+ switch (param) {\r
+ case Position:\r
+ Vector3f pos = listener.getLocation();\r
+ //alListener3f(AL_POSITION, pos.x, pos.y, pos.z);\r
+ alListener3f(AL.AL_POSITION, pos.x, pos.y, pos.z);\r
+ checkError(true);\r
+ break;\r
+ case Rotation:\r
+ Vector3f dir = listener.getDirection();\r
+ Vector3f up = listener.getUp();\r
+ fb.rewind();\r
+ fb.put(dir.x).put(dir.y).put(dir.z);\r
+ fb.put(up.x).put(up.y).put(up.z);\r
+ fb.flip();\r
+ //alListener(AL_ORIENTATION, fb);\r
+ alListener(AL.AL_ORIENTATION, fb);\r
+ checkError(true);\r
+ break;\r
+ case Velocity:\r
+ Vector3f vel = listener.getVelocity();\r
+ //alListener3f(AL_VELOCITY, vel.x, vel.y, vel.z);\r
+ alListener3f(AL.AL_VELOCITY, vel.x, vel.y, vel.z);\r
+ checkError(true);\r
+ break;\r
+ case Volume:\r
+ //alListenerf(AL_GAIN, listener.getVolume());\r
+ alListenerf(AL.AL_GAIN, listener.getVolume());\r
+ checkError(true);\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ private void setListenerParams(Listener listener) {\r
+ Vector3f pos = listener.getLocation();\r
+ Vector3f vel = listener.getVelocity();\r
+ Vector3f dir = listener.getDirection();\r
+ Vector3f up = listener.getUp();\r
+\r
+ //alListener3f(AL_POSITION, pos.x, pos.y, pos.z);\r
+ alListener3f(AL.AL_POSITION, pos.x, pos.y, pos.z);\r
+ checkError(true);\r
+ //alListener3f(AL_VELOCITY, vel.x, vel.y, vel.z);\r
+ alListener3f(AL.AL_VELOCITY, vel.x, vel.y, vel.z);\r
+ checkError(true);\r
+ fb.rewind();\r
+ fb.put(dir.x).put(dir.y).put(dir.z);\r
+ fb.put(up.x).put(up.y).put(up.z);\r
+ fb.flip();\r
+ //alListener(AL_ORIENTATION, fb);\r
+ alListener(AL.AL_ORIENTATION, fb);\r
+ checkError(true);\r
+ //alListenerf(AL_GAIN, listener.getVolume());\r
+ alListenerf(AL.AL_GAIN, listener.getVolume());\r
+ checkError(true);\r
+ }\r
+\r
+ private int newChannel() {\r
+ if (freeChans.size() > 0) {\r
+ return freeChans.remove(0);\r
+ } else if (nextChan < channels.length) {\r
+ return nextChan++;\r
+ } else {\r
+ return -1;\r
+ }\r
+ }\r
+\r
+ private void freeChannel(int index) {\r
+ if (index == nextChan - 1) {\r
+ nextChan--;\r
+ } else {\r
+ freeChans.add(index);\r
+ }\r
+ }\r
+\r
+ public void setEnvironment(Environment env) {\r
+ checkDead();\r
+ synchronized (threadLock) {\r
+ while (!threadLock.get()) {\r
+ try {\r
+ threadLock.wait();\r
+ } catch (InterruptedException ex) {\r
+ }\r
+ }\r
+ if (audioDisabled || !supportEfx) {\r
+ return;\r
+ }\r
+\r
+// EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_DENSITY, env.getDensity());\r
+// EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_DIFFUSION, env.getDiffusion());\r
+// EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_GAIN, env.getGain());\r
+// EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_GAINHF, env.getGainHf());\r
+// EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_DECAY_TIME, env.getDecayTime());\r
+// EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_DECAY_HFRATIO, env.getDecayHFRatio());\r
+// EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_REFLECTIONS_GAIN, env.getReflectGain());\r
+// EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_REFLECTIONS_DELAY, env.getReflectDelay());\r
+// EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_LATE_REVERB_GAIN, env.getLateReverbGain());\r
+// EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_LATE_REVERB_DELAY, env.getLateReverbDelay());\r
+// EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_AIR_ABSORPTION_GAINHF, env.getAirAbsorbGainHf());\r
+// EFX10.alEffectf(reverbFx, EFX10.AL_REVERB_ROOM_ROLLOFF_FACTOR, env.getRoomRolloffFactor());\r
+//\r
+// // attach effect to slot\r
+// EFX10.alAuxiliaryEffectSloti(reverbFxSlot, EFX10.AL_EFFECTSLOT_EFFECT, reverbFx);\r
+ }\r
+ }\r
+\r
+ private boolean fillBuffer(AudioStream stream, int id) {\r
+// logger.log(Level.INFO, "fillBuffer for id: {0}", id);\r
+ int size = 0;\r
+ int result;\r
+\r
+ while (size < arrayBuf.length) {\r
+ result = stream.readSamples(arrayBuf, size, arrayBuf.length - size);\r
+\r
+ if (result > 0) {\r
+ size += result;\r
+ } else {\r
+ break;\r
+ }\r
+ }\r
+\r
+// logger.log(Level.INFO, "data for buffer: {0} is size: {1}",\r
+// new Object[]{id, size});\r
+\r
+ if (size == 0) {\r
+ return false;\r
+ }\r
+\r
+ nativeBuf.clear();\r
+ nativeBuf.put(arrayBuf, 0, size);\r
+ nativeBuf.flip();\r
+\r
+ //alBufferData(id, convertFormat(stream), nativeBuf, stream.getSampleRate());\r
+ alBufferData(id, convertFormat(stream), nativeBuf, size, stream.getSampleRate());\r
+ checkError(true);\r
+\r
+ return true;\r
+ }\r
+\r
+ private boolean fillStreamingSource(int sourceId, AudioStream stream) {\r
+// logger.log(Level.INFO, "fillStreamingSource for source: {0}", sourceId);\r
+ if (!stream.isOpen()) {\r
+ return false;\r
+ }\r
+\r
+ boolean active = true;\r
+ //int processed = alGetSourcei(sourceId, AL_BUFFERS_PROCESSED);\r
+ int processed = alGetSourcei(sourceId, AL.AL_BUFFERS_PROCESSED);\r
+// logger.log(Level.INFO, "fillStreamingSource buffers processed: {0}", processed);\r
+ checkError(true);\r
+\r
+ //while((processed--) != 0){\r
+ if (processed > 0) {\r
+ int buffer;\r
+\r
+ ib.position(0).limit(1);\r
+// logger.log(Level.INFO, "fillStreamingSource alSourceUnqueueBuffers for source: {0}", sourceId);\r
+ //alSourceUnqueueBuffers(sourceId, ib);\r
+ alSourceUnqueueBuffers(sourceId, 1, ib);\r
+ checkError(true);\r
+ buffer = ib.get(0);\r
+// logger.log(Level.INFO, "fillStreamingSource bufferID: {0}", buffer);\r
+\r
+ active = fillBuffer(stream, buffer);\r
+\r
+ ib.position(0).limit(1);\r
+ ib.put(0, buffer);\r
+// logger.log(Level.INFO, "fillStreamingSource alSourceQueueBuffers for source: {0}, buffer: {1}",\r
+// new Object[]{sourceId, buffer});\r
+ //alSourceQueueBuffers(sourceId, ib);\r
+ alSourceQueueBuffers(sourceId, 1, ib);\r
+ checkError(true);\r
+ }\r
+\r
+ if (!active && stream.isOpen()) {\r
+ stream.close();\r
+ }\r
+\r
+ return active;\r
+ }\r
+\r
+ private boolean attachStreamToSource(int sourceId, AudioStream stream) {\r
+// logger.log(Level.INFO, "attachStreamToSource for source: {0}", sourceId);\r
+ boolean active = true;\r
+ int activeBufferCount = 0;\r
+ for (int id : stream.getIds()) {\r
+ active = fillBuffer(stream, id);\r
+ ib.position(0).limit(1);\r
+ ib.put(id).flip();\r
+ //alSourceQueueBuffers(sourceId, ib);\r
+ // OpenAL Soft does not like 0 size buffer data in alSourceQueueBuffers\r
+ // Produces error code 40964 (0xA004) = AL_INVALID_OPERATION and\r
+ // does not return (crashes) so that the error code can be checked.\r
+ // active is FALSE when the data size is 0\r
+ if (active) {\r
+// logger.log(Level.INFO, "attachStreamToSource alSourceQueueBuffers for source: {0}, buffer: {1}",\r
+// new Object[]{sourceId, id});\r
+ alSourceQueueBuffers(sourceId, 1, ib);\r
+ checkError(true);\r
+ activeBufferCount++;\r
+ }\r
+ }\r
+ // adjust the steam id array if the audio data is smaller than STREAMING_BUFFER_COUNT\r
+ // this is to avoid an error with OpenAL Soft when alSourceUnenqueueBuffers\r
+ // is called with more buffers than were originally used with alSourceQueueBuffers\r
+ if (activeBufferCount < STREAMING_BUFFER_COUNT) {\r
+ int[] newIds = new int[activeBufferCount];\r
+ for (int i=0; i<STREAMING_BUFFER_COUNT; i++) {\r
+ if (i < activeBufferCount) {\r
+ newIds[i] = stream.getIds()[i];\r
+// logger.log(Level.INFO, "newIds[{0}] = {1}",\r
+// new Object[]{i, newIds[i]});\r
+ } else {\r
+ ib.clear();\r
+ ib.put(stream.getIds()[i]).limit(1).flip();\r
+ alDeleteBuffers(1, ib);\r
+ checkError(true);\r
+// logger.log(Level.INFO, "deleting buffer at index[{0}] = {1}",\r
+// new Object[]{i, stream.getIds()[i]});\r
+ }\r
+\r
+ }\r
+ stream.setIds(newIds);\r
+ }\r
+\r
+ return active;\r
+ }\r
+\r
+ private boolean attachBufferToSource(int sourceId, AudioBuffer buffer) {\r
+ //alSourcei(sourceId, AL_BUFFER, buffer.getId());\r
+ alSourcei(sourceId, AL.AL_BUFFER, buffer.getId());\r
+ checkError(true);\r
+ return true;\r
+ }\r
+\r
+ private boolean attachAudioToSource(int sourceId, AudioData data) {\r
+// logger.log(Level.INFO, "attachAudioToSource for data type: {0}", data.getClass().getName());\r
+ if (data instanceof AudioBuffer) {\r
+ return attachBufferToSource(sourceId, (AudioBuffer) data);\r
+ } else if (data instanceof AudioStream) {\r
+ return attachStreamToSource(sourceId, (AudioStream) data);\r
+ }\r
+ throw new UnsupportedOperationException();\r
+ }\r
+\r
+ private void clearChannel(int index) {\r
+// logger.log(Level.INFO, "Clearing channel for index: {0}", index);\r
+ // make room at this channel\r
+ if (chanSrcs[index] != null) {\r
+ AudioSource src = chanSrcs[index];\r
+\r
+ int sourceId = channels[index];\r
+// logger.log(Level.INFO, "Stopping source: {0} in clearChannel", sourceId);\r
+ alSourceStop(sourceId);\r
+\r
+ if (src.getAudioData() instanceof AudioStream) {\r
+ AudioStream str = (AudioStream) src.getAudioData();\r
+// logger.log(Level.INFO, "source is a stream with numBuffers: {0}", str.getIds().length);\r
+ for (int i=0; i<str.getIds().length; i++) {\r
+// logger.log(Level.INFO, "id[{0}]: {1}",\r
+// new Object[]{i, str.getIds()[i]});\r
+ }\r
+ //ib.position(0).limit(STREAMING_BUFFER_COUNT);\r
+ ib.position(0).limit(str.getIds().length);\r
+ ib.put(str.getIds()).flip();\r
+// logger.log(Level.INFO, "clearChannel alSourceUnqueueBuffers for source: {0}", sourceId);\r
+ int processed = alGetSourcei(sourceId, AL.AL_BUFFERS_PROCESSED);\r
+// logger.log(Level.INFO, "clearChannels buffers processed: {0}", processed);\r
+ //alSourceUnqueueBuffers(sourceId, ib);\r
+ alSourceUnqueueBuffers(sourceId, processed, ib);\r
+ checkError(true);\r
+ } else if (src.getAudioData() instanceof AudioBuffer) {\r
+ //alSourcei(sourceId, AL_BUFFER, 0);\r
+ alSourcei(sourceId, AL.AL_BUFFER, 0);\r
+ checkError(true);\r
+ }\r
+\r
+ if (src.getDryFilter() != null && supportEfx) {\r
+ // detach filter\r
+// alSourcei(sourceId, EFX10.AL_DIRECT_FILTER, EFX10.AL_FILTER_NULL);\r
+ }\r
+ if (src.isPositional()) {\r
+ AudioSource pas = (AudioSource) src;\r
+ if (pas.isReverbEnabled() && supportEfx) {\r
+// AL11.alSource3i(sourceId, EFX10.AL_AUXILIARY_SEND_FILTER, 0, 0, EFX10.AL_FILTER_NULL);\r
+ }\r
+ }\r
+\r
+ chanSrcs[index] = null;\r
+ }\r
+ }\r
+\r
+ public void update(float tpf) {\r
+ // does nothing\r
+ }\r
+\r
+ public void updateInThread(float tpf) {\r
+ if (audioDisabled) {\r
+ return;\r
+ }\r
+\r
+ for (int i = 0; i < channels.length; i++) {\r
+ AudioSource src = chanSrcs[i];\r
+ if (src == null) {\r
+ continue;\r
+ }\r
+\r
+ int sourceId = channels[i];\r
+\r
+ // is the source bound to this channel\r
+ // if false, it's an instanced playback\r
+ boolean boundSource = i == src.getChannel();\r
+\r
+ // source's data is streaming\r
+ boolean streaming = src.getAudioData() instanceof AudioStream;\r
+\r
+ // only buffered sources can be bound\r
+ assert (boundSource && streaming) || (!streaming);\r
+\r
+ //int state = alGetSourcei(sourceId, AL_SOURCE_STATE);\r
+ int state = alGetSourcei(sourceId, AL.AL_SOURCE_STATE);\r
+ checkError(true);\r
+// logger.log(Level.INFO, "source: {0}, state: {1}",\r
+// new Object[]{sourceId, state});\r
+ boolean wantPlaying = src.getStatus() == Status.Playing;\r
+// logger.log(Level.INFO, "sourceId: {0}, wantPlaying: {1}",\r
+// new Object[]{sourceId, wantPlaying});\r
+ //boolean stopped = state == AL_STOPPED;\r
+ boolean stopped = state == AL.AL_STOPPED;\r
+// logger.log(Level.INFO, "sourceId: {0}, stopped: {1}",\r
+// new Object[]{sourceId, stopped});\r
+\r
+ if (streaming && wantPlaying) {\r
+ AudioStream stream = (AudioStream) src.getAudioData();\r
+ if (stream.isOpen()) {\r
+// logger.log(Level.INFO, "stream is open && want playing for source: {0}", sourceId);\r
+ fillStreamingSource(sourceId, stream);\r
+ if (stopped) {\r
+// logger.log(Level.INFO, "source: {0} stopped, set playstate", sourceId);\r
+ alSourcePlay(sourceId);\r
+ checkError(true);\r
+ }\r
+ } else {\r
+ if (stopped) {\r
+// logger.log(Level.INFO, "stream is not open && want playing for source: {0}", sourceId);\r
+ // became inactive\r
+ src.setStatus(Status.Stopped);\r
+ src.setChannel(-1);\r
+ clearChannel(i);\r
+ freeChannel(i);\r
+\r
+ // And free the audio since it cannot be\r
+ // played again anyway.\r
+ deleteAudioData(stream);\r
+ }\r
+ }\r
+ } else if (!streaming) {\r
+ //boolean paused = state == AL_PAUSED;\r
+ boolean paused = state == AL.AL_PAUSED;\r
+// logger.log(Level.INFO, "source: {0}, pause: {1}",\r
+// new Object[]{sourceId, paused});\r
+\r
+ // make sure OAL pause state & source state coincide\r
+ assert (src.getStatus() == Status.Paused && paused) || (!paused);\r
+\r
+ if (stopped) {\r
+ if (boundSource) {\r
+ src.setStatus(Status.Stopped);\r
+ src.setChannel(-1);\r
+ }\r
+ clearChannel(i);\r
+ freeChannel(i);\r
+ }\r
+ }\r
+ }\r
+\r
+ // Delete any unused objects.\r
+ objManager.deleteUnused(this);\r
+ }\r
+\r
+ public void setListener(Listener listener) {\r
+ checkDead();\r
+ synchronized (threadLock) {\r
+ while (!threadLock.get()) {\r
+ try {\r
+ threadLock.wait();\r
+ } catch (InterruptedException ex) {\r
+ }\r
+ }\r
+ if (audioDisabled) {\r
+ return;\r
+ }\r
+\r
+ if (this.listener != null) {\r
+ // previous listener no longer associated with current\r
+ // renderer\r
+ this.listener.setRenderer(null);\r
+ }\r
+\r
+ this.listener = listener;\r
+ this.listener.setRenderer(this);\r
+ setListenerParams(listener);\r
+ }\r
+ }\r
+\r
+ public void playSourceInstance(AudioSource src) {\r
+ checkDead();\r
+ synchronized (threadLock) {\r
+ while (!threadLock.get()) {\r
+ try {\r
+ threadLock.wait();\r
+ } catch (InterruptedException ex) {\r
+ }\r
+ }\r
+ if (audioDisabled) {\r
+ return;\r
+ }\r
+\r
+ if (src.getAudioData() instanceof AudioStream) {\r
+ throw new UnsupportedOperationException(\r
+ "Cannot play instances "\r
+ + "of audio streams. Use playSource() instead.");\r
+ }\r
+\r
+ if (src.getAudioData().isUpdateNeeded()) {\r
+// logger.log(Level.INFO, "Calling updateAudioData from playSourceInstance");\r
+ updateAudioData(src.getAudioData());\r
+ }\r
+\r
+ // create a new index for an audio-channel\r
+ int index = newChannel();\r
+ if (index == -1) {\r
+ return;\r
+ }\r
+\r
+ int sourceId = channels[index];\r
+\r
+// logger.log(Level.INFO, "Calling clearChannel for index[{0}] from playSourceInstance", index);\r
+ clearChannel(index);\r
+\r
+ // set parameters, like position and max distance\r
+// logger.log(Level.INFO, "Calling setSourceParams for sourceID: {0} from playSourceInstance", index);\r
+ setSourceParams(sourceId, src, true);\r
+// logger.log(Level.INFO, "Calling attachAudioToSource for sourceID: {0} and data audiodata id: {1} from playSourceInstance",\r
+// new Object[]{sourceId, src.getAudioData().getId()});\r
+ attachAudioToSource(sourceId, src.getAudioData());\r
+ chanSrcs[index] = src;\r
+\r
+ // play the channel\r
+ alSourcePlay(sourceId);\r
+ checkError(true);\r
+ }\r
+ }\r
+\r
+ public void playSource(AudioSource src) {\r
+ checkDead();\r
+ synchronized (threadLock) {\r
+ while (!threadLock.get()) {\r
+ try {\r
+ threadLock.wait();\r
+ } catch (InterruptedException ex) {\r
+ }\r
+ }\r
+ if (audioDisabled) {\r
+ return;\r
+ }\r
+\r
+ //assert src.getStatus() == Status.Stopped || src.getChannel() == -1;\r
+\r
+ if (src.getStatus() == Status.Playing) {\r
+ return;\r
+ } else if (src.getStatus() == Status.Stopped) {\r
+\r
+ // allocate channel to this source\r
+ int index = newChannel();\r
+ if (index == -1) {\r
+ logger.log(Level.WARNING, "No channel available to play {0}", src);\r
+ return;\r
+ }\r
+ clearChannel(index);\r
+ src.setChannel(index);\r
+\r
+ AudioData data = src.getAudioData();\r
+ if (data.isUpdateNeeded()) {\r
+ updateAudioData(data);\r
+ }\r
+\r
+ chanSrcs[index] = src;\r
+ setSourceParams(channels[index], src, false);\r
+ attachAudioToSource(channels[index], data);\r
+ }\r
+\r
+ alSourcePlay(channels[src.getChannel()]);\r
+ checkError(true);\r
+ src.setStatus(Status.Playing);\r
+ }\r
+ }\r
+\r
+ public void pauseSource(AudioSource src) {\r
+// logger.log(Level.INFO, "pauseSource");\r
+ checkDead();\r
+ synchronized (threadLock) {\r
+ while (!threadLock.get()) {\r
+ try {\r
+ threadLock.wait();\r
+ } catch (InterruptedException ex) {\r
+ }\r
+ }\r
+ if (audioDisabled) {\r
+ return;\r
+ }\r
+\r
+// logger.log(Level.INFO, "source is playing: {0}", src.getStatus() == Status.Playing);\r
+ if (src.getStatus() == Status.Playing) {\r
+ assert src.getChannel() != -1;\r
+\r
+ alSourcePause(channels[src.getChannel()]);\r
+ checkError(true);\r
+ src.setStatus(Status.Paused);\r
+ }\r
+ }\r
+ }\r
+\r
+ public void stopSource(AudioSource src) {\r
+ synchronized (threadLock) {\r
+ while (!threadLock.get()) {\r
+ try {\r
+ threadLock.wait();\r
+ } catch (InterruptedException ex) {\r
+ }\r
+ }\r
+ if (audioDisabled) {\r
+ return;\r
+ }\r
+\r
+ if (src.getStatus() != Status.Stopped) {\r
+ int chan = src.getChannel();\r
+ assert chan != -1; // if it's not stopped, must have id\r
+\r
+ src.setStatus(Status.Stopped);\r
+ src.setChannel(-1);\r
+ clearChannel(chan);\r
+ freeChannel(chan);\r
+\r
+ if (src.getAudioData() instanceof AudioStream) {\r
+ AudioStream stream = (AudioStream) src.getAudioData();\r
+ if (stream.isOpen()) {\r
+ stream.close();\r
+ }\r
+\r
+ // And free the audio since it cannot be\r
+ // played again anyway.\r
+ deleteAudioData(src.getAudioData());\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ private int convertFormat(AudioData ad) {\r
+ switch (ad.getBitsPerSample()) {\r
+ case 8:\r
+ if (ad.getChannels() == 1) {\r
+ //return AL_FORMAT_MONO8;\r
+ return AL.AL_FORMAT_MONO8;\r
+ } else if (ad.getChannels() == 2) {\r
+ //return AL_FORMAT_STEREO8;\r
+ return AL.AL_FORMAT_STEREO8;\r
+ }\r
+\r
+ break;\r
+ case 16:\r
+ if (ad.getChannels() == 1) {\r
+ //return AL_FORMAT_MONO16;\r
+ return AL.AL_FORMAT_MONO16;\r
+ } else {\r
+ //return AL_FORMAT_STEREO16;\r
+ return AL.AL_FORMAT_STEREO16;\r
+ }\r
+ }\r
+ throw new UnsupportedOperationException("Unsupported channels/bits combination: "\r
+ + "bits=" + ad.getBitsPerSample() + ", channels=" + ad.getChannels());\r
+ }\r
+\r
+ private void updateAudioBuffer(AudioBuffer ab) {\r
+ int id = ab.getId();\r
+// logger.log(Level.INFO, "updateAudioBuffer for buffer id: {0}", id);\r
+ if (ab.getId() == -1) {\r
+ ib.position(0).limit(1);\r
+ alGenBuffers(1, ib);\r
+ checkError(true);\r
+ id = ib.get(0);\r
+ ab.setId(id);\r
+// logger.log(Level.INFO, "Generated Buffer: {0}", id);\r
+\r
+ objManager.registerForCleanup(ab);\r
+ }\r
+// logger.log(Level.INFO, "updateAudioBuffer new buffer id: {0}", id);\r
+\r
+ ab.getData().clear();\r
+ //alBufferData(id, convertFormat(ab), ab.getData(), ab.getSampleRate());\r
+ alBufferData(id, convertFormat(ab), ab.getData(), ab.getData().limit(), ab.getSampleRate());\r
+ checkError(true);\r
+ ab.clearUpdateNeeded();\r
+ }\r
+\r
+ private void updateAudioStream(AudioStream as) {\r
+// logger.log(Level.INFO, "updateAudioStream");\r
+ if (as.getIds() != null) {\r
+ deleteAudioData(as);\r
+ }\r
+\r
+ int[] ids = new int[STREAMING_BUFFER_COUNT];\r
+ ib.position(0).limit(STREAMING_BUFFER_COUNT);\r
+ //alGenBuffers(ib);\r
+ alGenBuffers(STREAMING_BUFFER_COUNT, ib);\r
+ checkError(true);\r
+ ib.position(0).limit(STREAMING_BUFFER_COUNT);\r
+ ib.get(ids);\r
+// for (int i=0; i<ids.length; i++) {\r
+// logger.log(Level.INFO, "Generated Streaming Buffer: {0}", ids[i]);\r
+// }\r
+\r
+ // Not registered with object manager.\r
+ // AudioStreams can be handled without object manager\r
+ // since their lifecycle is known to the audio renderer.\r
+\r
+ as.setIds(ids);\r
+ as.clearUpdateNeeded();\r
+ }\r
+\r
+ private void updateAudioData(AudioData ad) {\r
+ if (ad instanceof AudioBuffer) {\r
+ updateAudioBuffer((AudioBuffer) ad);\r
+ } else if (ad instanceof AudioStream) {\r
+ updateAudioStream((AudioStream) ad);\r
+ }\r
+ }\r
+\r
+ public void deleteFilter(Filter filter) {\r
+ int id = filter.getId();\r
+ if (id != -1) {\r
+// EFX10.alDeleteFilters(id);\r
+ }\r
+ }\r
+\r
+ public void deleteAudioData(AudioData ad) {\r
+// if (ad instanceof AudioStream) {\r
+// AudioStream as = (AudioStream) ad;\r
+// int[] ids = as.getIds();\r
+// for (int i=0; i<ids.length; i++) {\r
+// logger.log(Level.INFO, "deleteAudioData for stream buffer: {0}", ids[i]);\r
+// }\r
+// } else if (ad instanceof AudioBuffer) {\r
+// logger.log(Level.INFO, "deleteAudioData for buffer: {0}", ad.getId());\r
+// }\r
+ synchronized (threadLock) {\r
+ while (!threadLock.get()) {\r
+ try {\r
+ threadLock.wait();\r
+ } catch (InterruptedException ex) {\r
+ }\r
+ }\r
+ if (audioDisabled) {\r
+ return;\r
+ }\r
+\r
+ if (ad instanceof AudioBuffer) {\r
+ AudioBuffer ab = (AudioBuffer) ad;\r
+ int id = ab.getId();\r
+ if (id != -1) {\r
+ ib.put(0, id);\r
+ ib.position(0).limit(1);\r
+ //alDeleteBuffers(ib);\r
+ alDeleteBuffers(1, ib);\r
+ checkError(true);\r
+ ab.resetObject();\r
+ }\r
+ } else if (ad instanceof AudioStream) {\r
+ AudioStream as = (AudioStream) ad;\r
+ int[] ids = as.getIds();\r
+ if (ids != null) {\r
+ ib.clear();\r
+ ib.put(ids).flip();\r
+ //alDeleteBuffers(ib);\r
+ alDeleteBuffers(ids.length, ib);\r
+ checkError(true);\r
+ as.resetObject();\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ private int checkError(boolean stopOnError) {\r
+ int errorCode = alGetError();\r
+ String errorText = AL.GetALErrorMsg(errorCode);\r
+// logger.log(Level.INFO, "alError Code: {0}, Description: {1}",\r
+// new Object[]{errorCode, errorText});\r
+\r
+ if (errorCode != AL.AL_NO_ERROR && stopOnError) {\r
+ throw new IllegalStateException("AL Error Detected. Error Code: " + errorCode + ": " + errorText);\r
+ }\r
+\r
+ return errorCode;\r
+ }\r
+\r
+ /** Native methods, implemented in jni folder */\r
+ public static native boolean alIsCreated();\r
+ public static native boolean alCreate();\r
+ public static native boolean alDestroy();\r
+ public static native String alcGetString(int parameter);\r
+ public static native String alGetString(int parameter);\r
+ public static native int alGenSources();\r
+ public static native int alGetError();\r
+ public static native void alDeleteSources(int numSources, IntBuffer sources);\r
+ public static native void alGenBuffers(int numBuffers, IntBuffer buffers);\r
+ public static native void alDeleteBuffers(int numBuffers, IntBuffer buffers);\r
+ public static native void alSourceStop(int source);\r
+ public static native void alSourcei(int source, int param, int value);\r
+ public static native void alBufferData(int buffer, int format, ByteBuffer data, int size, int frequency);\r
+ public static native void alSourcePlay(int source);\r
+ public static native void alSourcePause(int source);\r
+ public static native void alSourcef(int source, int param, float value);\r
+ public static native void alSource3f(int source, int param, float value1, float value2, float value3);\r
+ public static native int alGetSourcei(int source, int param);\r
+ public static native void alSourceUnqueueBuffers(int source, int numBuffers, IntBuffer buffers);\r
+ public static native void alSourceQueueBuffers(int source, int numBuffers, IntBuffer buffers);\r
+ public static native void alListener(int param, FloatBuffer data);\r
+ public static native void alListenerf(int param, float value);\r
+ public static native void alListener3f(int param, float value1, float value2, float value3);\r
+\r
+\r
+ /** Load jni .so on initialization */\r
+ static {\r
+ System.loadLibrary("openalsoftjme");\r
+ }\r
+\r
+\r
+}\r
--- /dev/null
+#include "com_jme3_audio_android_AndroidOpenALSoftAudioRenderer.h"\r
+#include "AL/alc.h"\r
+#include "AL/al.h"\r
+#include "AL/alext.h"\r
+// for __android_log_print(ANDROID_LOG_INFO, "YourApp", "formatted message");\r
+#include <android/log.h>\r
+#include <jni.h>\r
+#include <stddef.h>\r
+#include <stdio.h>\r
+#include <assert.h>\r
+#include <string.h>\r
+#include <time.h>\r
+\r
+#ifdef __cplusplus\r
+extern "C" {\r
+#endif\r
+\r
+static jboolean created = JNI_FALSE;\r
+\r
+#define BUFFER_COUNT 1\r
+ALuint* buffers[BUFFER_COUNT] = { 0 };\r
+ALuint* source = 0;\r
+\r
+int getError() {\r
+ int errorcode = alGetError();\r
+// __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "getError: %d", errorcode);\r
+ return errorcode;\r
+}\r
+\r
+/* InitAL opens the default device and sets up a context using default\r
+ * attributes, making the program ready to call OpenAL functions. */\r
+int InitAL()\r
+{\r
+ ALCdevice *device;\r
+ ALCcontext *ctx;\r
+\r
+ /* Open and initialize a device with default settings */\r
+ device = alcOpenDevice(NULL);\r
+ if(!device)\r
+ {\r
+ fprintf(stderr, "Could not open a device!\n");\r
+ return 1;\r
+ }\r
+\r
+ ctx = alcCreateContext(device, NULL);\r
+// __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "NULL: %d", NULL);\r
+// __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "Created context: %d", ctx);\r
+// __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "Created context addr: %d", &ctx);\r
+ if(ctx == NULL || alcMakeContextCurrent(ctx) == ALC_FALSE)\r
+ {\r
+ if(ctx != NULL)\r
+ alcDestroyContext(ctx);\r
+ alcCloseDevice(device);\r
+ fprintf(stderr, "Could not set a context!\n");\r
+ return 1;\r
+ }\r
+\r
+ printf("Opened \"%s\"\n", alcGetString(device, ALC_DEVICE_SPECIFIER));\r
+ __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "Opened %s", alcGetString(device, ALC_DEVICE_SPECIFIER));\r
+ return 0;\r
+}\r
+\r
+/* CloseAL closes the device belonging to the current context, and destroys the\r
+ * context. */\r
+void CloseAL()\r
+{\r
+ ALCdevice *device;\r
+ ALCcontext *ctx;\r
+ ALCboolean result;\r
+\r
+// __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "Getting current context");\r
+ ctx = alcGetCurrentContext();\r
+// getError();\r
+ if(ctx == NULL){\r
+ __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "No context found");\r
+ return;\r
+ }\r
+\r
+// __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "Getting current context device");\r
+ device = alcGetContextsDevice(ctx);\r
+ if(device == NULL) {\r
+ __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "No device found");\r
+ return;\r
+ } else {\r
+// __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "alcGetContextsDevice device: %d", device);\r
+// __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "alcGetContextsDevice device addr: %d", &device);\r
+ }\r
+// getError();\r
+\r
+// __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "Setting context to NULL");\r
+ result = alcMakeContextCurrent(NULL);\r
+// __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "alcMakeContextCurrent returned");\r
+// __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "alcMakeContextCurrent returned with result: %d", result);\r
+ if(!result) {\r
+ __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "alcMakeContextCurrent failed");\r
+ return;\r
+ }\r
+\r
+// __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "Destroying context: %d", ctx);\r
+// __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "Destroying context addr: %d", &ctx);\r
+ alcDestroyContext(ctx);\r
+\r
+// __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "Closing device");\r
+ result = alcCloseDevice(device);\r
+// __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "alcCloseDevice result: %d", result);\r
+}\r
+\r
+\r
+JNIEXPORT jboolean JNICALL Java_com_jme3_audio_android_AndroidOpenALSoftAudioRenderer_alIsCreated\r
+ (JNIEnv* env, jclass)\r
+{\r
+ return created;\r
+}\r
+\r
+\r
+JNIEXPORT jboolean JNICALL Java_com_jme3_audio_android_AndroidOpenALSoftAudioRenderer_alCreate\r
+ (JNIEnv* env, jclass)\r
+{\r
+ __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "Starting Audio Engine");\r
+\r
+ InitAL();\r
+ created = JNI_TRUE;\r
+ return created;\r
+\r
+}\r
+\r
+JNIEXPORT jboolean JNICALL Java_com_jme3_audio_android_AndroidOpenALSoftAudioRenderer_alDestroy\r
+ (JNIEnv* env, jclass)\r
+{\r
+\r
+// __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "alDestroy");\r
+ CloseAL();\r
+ created = JNI_FALSE;\r
+ return created;\r
+\r
+}\r
+\r
+JNIEXPORT jstring JNICALL Java_com_jme3_audio_android_AndroidOpenALSoftAudioRenderer_alcGetString\r
+ (JNIEnv* env, jclass, jint param)\r
+{\r
+// __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "alcGetString for param: %d", param);\r
+\r
+ ALCdevice *device;\r
+ ALCcontext *ctx;\r
+\r
+ ctx = alcGetCurrentContext();\r
+ if(ctx != NULL) {\r
+ device = alcGetContextsDevice(ctx);\r
+// __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "alcGetString param value: %s", alcGetString(device, param));\r
+ return env->NewStringUTF(alcGetString(device, param));\r
+ }\r
+}\r
+\r
+JNIEXPORT jstring JNICALL Java_com_jme3_audio_android_AndroidOpenALSoftAudioRenderer_alGetString\r
+ (JNIEnv* env, jclass, jint param)\r
+{\r
+// __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "alGetString for param: %d", param);\r
+// __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "alGetString param value: %s", alGetString(param));\r
+ return env->NewStringUTF(alGetString(param));\r
+}\r
+\r
+JNIEXPORT jint JNICALL Java_com_jme3_audio_android_AndroidOpenALSoftAudioRenderer_alGenSources\r
+ (JNIEnv *, jclass)\r
+{\r
+ ALuint source;\r
+ alGenSources(1, &source);\r
+// __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "alGenSources: %d", source);\r
+ return source;\r
+}\r
+\r
+JNIEXPORT jint JNICALL Java_com_jme3_audio_android_AndroidOpenALSoftAudioRenderer_alGetError\r
+ (JNIEnv *, jclass)\r
+{\r
+ return getError();\r
+}\r
+\r
+JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidOpenALSoftAudioRenderer_alDeleteSources\r
+ (JNIEnv* env, jclass, jint numSources, jobject intbufSources)\r
+{\r
+// __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "alDeleteSources numSources: %d", numSources);\r
+\r
+ ALuint* pIntBufSources = (ALuint*) env->GetDirectBufferAddress(intbufSources);\r
+ alDeleteSources((ALsizei)numSources, pIntBufSources);\r
+}\r
+\r
+JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidOpenALSoftAudioRenderer_alGenBuffers\r
+ (JNIEnv* env, jclass, jint numBuffers, jobject intbufBuffers)\r
+{\r
+ ALuint* pIntBufBuffers = (ALuint*) env->GetDirectBufferAddress(intbufBuffers);\r
+ alGenBuffers((ALsizei)numBuffers, pIntBufBuffers);\r
+// for (int i=0; i<numBuffers; i++) {\r
+// __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "alGenBuffers[%d]: %d", i, *(pIntBufBuffers+i));\r
+// }\r
+\r
+}\r
+\r
+JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidOpenALSoftAudioRenderer_alDeleteBuffers\r
+ (JNIEnv* env, jclass, jint numBuffers, jobject intbufBuffers)\r
+{\r
+// __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "alDeleteBuffers numBuffers: %d", numBuffers);\r
+\r
+ ALuint* pIntBufBuffers = (ALuint*) env->GetDirectBufferAddress(intbufBuffers);\r
+// __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "alDeleteBuffers Buffers: %d", *pIntBufBuffers);\r
+// for (int i=0; i<numBuffers; i++) {\r
+// if(alIsBuffer(*(pIntBufBuffers+i)) == AL_TRUE) {\r
+// __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "alDeleteBuffers[%d]: %d", i, *(pIntBufBuffers+i));\r
+// __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "alDeleteBuffers buffer is a known buffer");\r
+// }\r
+// }\r
+ alDeleteBuffers((ALsizei)numBuffers, pIntBufBuffers);\r
+}\r
+\r
+JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidOpenALSoftAudioRenderer_alSourceStop\r
+ (JNIEnv *, jclass, jint source)\r
+{\r
+// __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "alSourceStop for source: %d", source);\r
+ alSourceStop((ALuint)source);\r
+}\r
+\r
+JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidOpenALSoftAudioRenderer_alSourcei\r
+ (JNIEnv *, jclass, jint source, jint param, jint value)\r
+{\r
+// __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "alSourcei for source: %d, param: %d, value: %d", source, param, value);\r
+ alSourcei((ALuint)source, (ALenum)param, (ALint)value);\r
+}\r
+\r
+JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidOpenALSoftAudioRenderer_alBufferData\r
+ (JNIEnv* env, jclass, jint buffer, jint format, jobject bufferData, jint bufferSize, jint frequency)\r
+{\r
+// __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "alBufferData for source: %d, format: %d, size: %d, frequency: %d", buffer, format, bufferSize, frequency);\r
+ ALuint* pBufferData = (ALuint*) env->GetDirectBufferAddress(bufferData);\r
+ alBufferData((ALuint)buffer, (ALenum)format, pBufferData, (ALsizei)bufferSize, (ALsizei)frequency);\r
+}\r
+\r
+JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidOpenALSoftAudioRenderer_alSourcePlay\r
+ (JNIEnv *, jclass, jint source)\r
+{\r
+// __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "alSourcePlay for source: %d", source);\r
+ alSourcePlay((ALuint)source);\r
+}\r
+\r
+JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidOpenALSoftAudioRenderer_alSourcePause\r
+ (JNIEnv *, jclass, jint source)\r
+{\r
+// __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "alSourcePause for source: %d", source);\r
+ alSourcePause((ALuint)source);\r
+}\r
+\r
+JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidOpenALSoftAudioRenderer_alSourcef\r
+ (JNIEnv *, jclass, jint source, jint param, jfloat value)\r
+{\r
+// __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "alSourcef for source: %d, param: %d, value: %f", source, param, value);\r
+ alSourcef((ALuint)source, (ALenum)param, (ALfloat)value);\r
+}\r
+\r
+JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidOpenALSoftAudioRenderer_alSource3f\r
+ (JNIEnv *, jclass, jint source, jint param, jfloat value1, jfloat value2, jfloat value3)\r
+{\r
+// __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "alSource3f for source: %d, param: %d, value1: %f, value2: %f, value3: %f", source, param, value1, value2, value3);\r
+ alSource3f((ALuint)source, (ALenum)param, (ALfloat)value1, (ALfloat)value2, (ALfloat)value3);\r
+}\r
+\r
+JNIEXPORT jint JNICALL Java_com_jme3_audio_android_AndroidOpenALSoftAudioRenderer_alGetSourcei\r
+ (JNIEnv *, jclass, jint source, jint param)\r
+{\r
+// __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "alGetSourcei for source: %d, param: %d", source, param);\r
+ ALint result;\r
+ alGetSourcei((ALuint)source, (ALenum)param, &result);\r
+ return (jint)result;\r
+}\r
+\r
+JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidOpenALSoftAudioRenderer_alSourceUnqueueBuffers\r
+ (JNIEnv* env, jclass, jint source, jint numBuffers, jobject buffers)\r
+{\r
+// __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "alSourceUnqueueBuffers for source: %d, numBuffers: %d", source, numBuffers);\r
+ ALuint* pBuffers = (ALuint*) env->GetDirectBufferAddress(buffers);\r
+\r
+// for (ALuint i=0; i<numBuffers; i++) {\r
+// __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "alSourceUnqueueBuffers, checking buffer[%d]: %d", i, *(pBuffers+i));\r
+// ALboolean isBuffer = alIsBuffer(*(pBuffers+i));\r
+// __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "buffer check result: %d", isBuffer);\r
+// }\r
+ alSourceUnqueueBuffers((ALuint)source, (ALsizei)numBuffers, pBuffers);\r
+// for (ALuint i=0; i<numBuffers; i++) {\r
+// __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "alSourceUnqueueBuffers[%d]: %d", i, *(pBuffers+i));\r
+// }\r
+}\r
+\r
+JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidOpenALSoftAudioRenderer_alSourceQueueBuffers\r
+ (JNIEnv* env, jclass, jint source, jint numBuffers, jobject buffers)\r
+{\r
+// __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "alSourceQueueBuffers for source: %d, numBuffers: %d", source, numBuffers);\r
+ ALuint* pBuffers = (ALuint*) env->GetDirectBufferAddress(buffers);\r
+ alSourceQueueBuffers((ALuint)source, (ALsizei)numBuffers, pBuffers);\r
+// for (ALuint i=0; i<numBuffers; i++) {\r
+// __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "alSourceQueueBuffers[%d]: %d", i, *(pBuffers+i));\r
+// }\r
+}\r
+\r
+JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidOpenALSoftAudioRenderer_alListener\r
+ (JNIEnv* env, jclass, jint param, jobject bufferData)\r
+{\r
+// __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "alListener for param: %d", param);\r
+ ALfloat* pBufferData = (ALfloat*) env->GetDirectBufferAddress(bufferData);\r
+ alListenerfv((ALenum)param, pBufferData);\r
+// getError();\r
+// for (int i=0; i<4; i++) {\r
+// __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "alListener[%d]: %f", i, *(pBufferData+(i*sizeof(ALfloat))));\r
+// }\r
+\r
+}\r
+\r
+JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidOpenALSoftAudioRenderer_alListenerf\r
+ (JNIEnv *, jclass, jint param, jfloat value)\r
+{\r
+// __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "alListenerf for param: %d, value: %f", param, value);\r
+ alListenerf((ALenum)param, (ALfloat)value);\r
+}\r
+\r
+JNIEXPORT void JNICALL Java_com_jme3_audio_android_AndroidOpenALSoftAudioRenderer_alListener3f\r
+ (JNIEnv *, jclass, jint param, jfloat value1, jfloat value2, jfloat value3)\r
+{\r
+// __android_log_print(ANDROID_LOG_INFO, "OpenAL Soft", "alListener3f for param: %d, value1: %f, value2: %f, value3: %f", param, value1, value2, value3);\r
+ alListener3f((ALenum)param, (ALfloat)value1, (ALfloat)value2, (ALfloat)value3);\r
+}\r
+\r
+\r
+\r
+\r
+\r
+#ifdef __cplusplus\r
+}\r
+#endif\r