From 22eb83fd9aa149248c37a8ff66b9189f898df5c3 Mon Sep 17 00:00:00 2001 From: "nor..67" Date: Wed, 20 Feb 2013 18:33:42 +0000 Subject: [PATCH] - add generic AudioSource to abstract audio system away from AudioNode git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@10416 75d07b2b-3a1a-0410-a2c5-0572b91ccdca --- engine/src/core/com/jme3/audio/AudioNode.java | 18 +- .../core/com/jme3/audio/AudioRenderer.java | 12 +- .../src/core/com/jme3/audio/AudioSource.java | 189 ++++++++++++++++++ .../jme3/audio/joal/JoalAudioRenderer.java | 54 ++--- .../jme3/audio/lwjgl/LwjglAudioRenderer.java | 46 ++--- 5 files changed, 257 insertions(+), 62 deletions(-) create mode 100644 engine/src/core/com/jme3/audio/AudioSource.java diff --git a/engine/src/core/com/jme3/audio/AudioNode.java b/engine/src/core/com/jme3/audio/AudioNode.java index 64bdfdb5c..5ee74dbd5 100644 --- a/engine/src/core/com/jme3/audio/AudioNode.java +++ b/engine/src/core/com/jme3/audio/AudioNode.java @@ -60,7 +60,7 @@ import java.util.logging.Logger; * @author normenhansen * @author Kirill Vainer */ -public class AudioNode extends Node { +public class AudioNode extends Node implements AudioSource { //Version #1 : AudioKey is now stored into "audio_key" instead of "key" public static final int SAVABLE_VERSION = 1; @@ -71,7 +71,7 @@ public class AudioNode extends Node { protected Filter dryFilter; protected AudioKey audioKey; protected transient AudioData data = null; - protected transient volatile Status status = Status.Stopped; + protected transient volatile AudioSource.Status status = AudioSource.Status.Stopped; protected transient volatile int channel = -1; protected Vector3f velocity = new Vector3f(); protected boolean reverbEnabled = true; @@ -86,7 +86,9 @@ public class AudioNode extends Node { /** * Status indicates the current status of the audio node. + * @deprecated - use AudioSource.Status instead */ + @Deprecated public enum Status { /** * The audio node is currently playing. This will be set if @@ -223,7 +225,7 @@ public class AudioNode extends Node { * Do not use. */ public final void setChannel(int channel) { - if (status != Status.Stopped) { + if (status != AudioSource.Status.Stopped) { throw new IllegalStateException("Can only set source id when stopped"); } @@ -294,14 +296,14 @@ public class AudioNode extends Node { * The status will be changed when either the {@link AudioNode#play() } * or {@link AudioNode#stop() } methods are called. */ - public Status getStatus() { + public AudioSource.Status getStatus() { return status; } /** * Do not use. */ - public final void setStatus(Status status) { + public final void setStatus(AudioSource.Status status) { this.status = status; } @@ -399,12 +401,16 @@ public class AudioNode extends Node { this.timeOffset = timeOffset; if (data instanceof AudioStream) { ((AudioStream) data).setTime(timeOffset); - }else if(status == Status.Playing){ + }else if(status == AudioSource.Status.Playing){ stop(); play(); } } + public Vector3f getPosition() { + return getWorldTranslation(); + } + /** * @return The velocity of the audio node. * diff --git a/engine/src/core/com/jme3/audio/AudioRenderer.java b/engine/src/core/com/jme3/audio/AudioRenderer.java index 5e47e0bb3..ee15bf77e 100644 --- a/engine/src/core/com/jme3/audio/AudioRenderer.java +++ b/engine/src/core/com/jme3/audio/AudioRenderer.java @@ -47,17 +47,17 @@ public interface AudioRenderer { /** * Sets the environment, used for reverb effects. * - * @see AudioNode#setReverbEnabled(boolean) + * @see AudioSource#setReverbEnabled(boolean) * @param env The environment to set. */ public void setEnvironment(Environment env); - public void playSourceInstance(AudioNode src); - public void playSource(AudioNode src); - public void pauseSource(AudioNode src); - public void stopSource(AudioNode src); + public void playSourceInstance(AudioSource src); + public void playSource(AudioSource src); + public void pauseSource(AudioSource src); + public void stopSource(AudioSource src); - public void updateSourceParam(AudioNode src, AudioParam param); + public void updateSourceParam(AudioSource src, AudioParam param); public void updateListenerParam(Listener listener, ListenerParam param); public void deleteFilter(Filter filter); diff --git a/engine/src/core/com/jme3/audio/AudioSource.java b/engine/src/core/com/jme3/audio/AudioSource.java new file mode 100644 index 000000000..a5fdd94cc --- /dev/null +++ b/engine/src/core/com/jme3/audio/AudioSource.java @@ -0,0 +1,189 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package com.jme3.audio; + +import com.jme3.math.Vector3f; + +/** + * + * @author normenhansen + */ +public interface AudioSource { + /** + * Status indicates the current status of the audio source. + */ + public enum Status { + /** + * The audio source is currently playing. This will be set if + * {@link AudioSource#play() } is called. + */ + Playing, + + /** + * The audio source is currently paused. + */ + Paused, + + /** + * The audio source is currently stopped. + * This will be set if {@link AudioSource#stop() } is called + * or the audio has reached the end of the file. + */ + Stopped, + } + + + /** + * Do not use. + */ + public void setChannel(int channel); + + /** + * Do not use. + */ + public int getChannel(); + + /** + * Start playing the audio. + */ + public void play(); + + /** + * Start playing an instance of this audio. This method can be used + * to play the same AudioSource multiple times. Note + * that changes to the parameters of this AudioSource will not effect the + * instances already playing. + */ + public void playInstance(); + + /** + * @return The {#link Filter dry filter} that is set. + * @see AudioSource#setDryFilter(com.jme3.audio.Filter) + */ + public Filter getDryFilter(); + + /** + * @return The {@link AudioData} set previously with + * {@link AudioSource#setAudioData(com.jme3.audio.AudioData, com.jme3.audio.AudioKey) } + * or any of the constructors that initialize the audio data. + */ + public AudioData getAudioData(); + + /** + * Do not use. + */ + public void setStatus(Status status); + + /** + * @return The {@link Status} of the audio source. + * The status will be changed when either the {@link AudioSource#play() } + * or {@link AudioSource#stop() } methods are called. + */ + public Status getStatus(); + + /** + * @return True if the audio will keep looping after it is done playing, + * otherwise, false. + * @see AudioSource#setLooping(boolean) + */ + public boolean isLooping(); + + /** + * @return The pitch of the audio, also the speed of playback. + * + * @see AudioSource#setPitch(float) + */ + public float getPitch(); + + /** + * @return The volume of this audio source. + * + * @see AudioSource#setVolume(float) + */ + public float getVolume(); + + /** + * @return the time offset in the sound sample when to start playing. + */ + public float getTimeOffset(); + + /** + * @return The velocity of the audio source. + * + * @see AudioSource#setVelocity(com.jme3.math.Vector3f) + */ + public Vector3f getPosition(); + + /** + * @return The velocity of the audio source. + * + * @see AudioSource#setVelocity(com.jme3.math.Vector3f) + */ + public Vector3f getVelocity(); + + /** + * @return True if reverb is enabled, otherwise false. + * + * @see AudioSource#setReverbEnabled(boolean) + */ + public boolean isReverbEnabled(); + + /** + * @return Filter for the reverberations of this audio source. + * + * @see AudioSource#setReverbFilter(com.jme3.audio.Filter) + */ + public Filter getReverbFilter(); + + /** + * @return Max distance for this audio source. + * + * @see AudioSource#setMaxDistance(float) + */ + public float getMaxDistance(); + + /** + * @return The reference playing distance for the audio source. + * + * @see AudioSource#setRefDistance(float) + */ + public float getRefDistance(); + + /** + * @return True if the audio source is directional + * + * @see AudioSource#setDirectional(boolean) + */ + public boolean isDirectional(); + + /** + * @return The direction of this audio source. + * + * @see AudioSource#setDirection(com.jme3.math.Vector3f) + */ + public Vector3f getDirection(); + + /** + * @return The directional audio source, cone inner angle. + * + * @see AudioSource#setInnerAngle(float) + */ + public float getInnerAngle(); + + /** + * @return The directional audio source, cone outer angle. + * + * @see AudioSource#setOuterAngle(float) + */ + public float getOuterAngle(); + + /** + * @return True if the audio source is positional. + * + * @see AudioSource#setPositional(boolean) + */ + public boolean isPositional(); + +} diff --git a/engine/src/jogl/com/jme3/audio/joal/JoalAudioRenderer.java b/engine/src/jogl/com/jme3/audio/joal/JoalAudioRenderer.java index b89d626bd..77bcdbed5 100644 --- a/engine/src/jogl/com/jme3/audio/joal/JoalAudioRenderer.java +++ b/engine/src/jogl/com/jme3/audio/joal/JoalAudioRenderer.java @@ -31,7 +31,7 @@ */ package com.jme3.audio.joal; -import com.jme3.audio.AudioNode.Status; +import com.jme3.audio.AudioSource.Status; import com.jme3.audio.*; import com.jme3.math.Vector3f; import com.jme3.util.BufferUtils; @@ -61,7 +61,7 @@ public class JoalAudioRenderer implements AudioRenderer, Runnable { private final ByteBuffer nativeBuf = BufferUtils.createByteBuffer(BUFFER_SIZE); private final byte[] arrayBuf = new byte[BUFFER_SIZE]; private int[] channels; - private AudioNode[] chanSrcs; + private AudioSource[] chanSrcs; private int nextChan = 0; private ArrayList freeChans = new ArrayList(); private Listener listener; @@ -194,7 +194,7 @@ public class JoalAudioRenderer implements AudioRenderer, Runnable { } ib = BufferUtils.createIntBuffer(channels.length); - chanSrcs = new AudioNode[channels.length]; + chanSrcs = new AudioSource[channels.length]; logger.log(Level.FINE, "AudioRenderer supports {0} channels", channels.length); @@ -308,7 +308,7 @@ public class JoalAudioRenderer implements AudioRenderer, Runnable { f.clearUpdateNeeded(); } - public void updateSourceParam(AudioNode src, AudioParam param) { + public void updateSourceParam(AudioSource src, AudioParam param) { checkDead(); synchronized (threadLock) { while (!threadLock.get()) { @@ -321,10 +321,10 @@ public class JoalAudioRenderer implements AudioRenderer, Runnable { return; } - // There is a race condition in AudioNode that can + // There is a race condition in AudioSource that can // cause this to be called for a node that has been // detached from its channel. For example, setVolume() - // called from the render thread may see that that AudioNode + // called from the render thread may see that that AudioSource // still has a channel value but the audio thread may // clear that channel before setVolume() gets to call // updateSourceParam() (because the audio stopped playing @@ -343,7 +343,7 @@ public class JoalAudioRenderer implements AudioRenderer, Runnable { return; } - Vector3f pos = src.getWorldTranslation(); + Vector3f pos = src.getPosition(); al.alSource3f(id, ALConstants.AL_POSITION, pos.x, pos.y, pos.z); break; case Velocity: @@ -482,9 +482,9 @@ public class JoalAudioRenderer implements AudioRenderer, Runnable { } } - private void setSourceParams(int id, AudioNode src, boolean forceNonLoop) { + private void setSourceParams(int id, AudioSource src, boolean forceNonLoop) { if (src.isPositional()) { - Vector3f pos = src.getWorldTranslation(); + Vector3f pos = src.getPosition(); Vector3f vel = src.getVelocity(); al.alSource3f(id, ALConstants.AL_POSITION, pos.x, pos.y, pos.z); al.alSource3f(id, ALConstants.AL_VELOCITY, vel.x, vel.y, vel.z); @@ -731,7 +731,7 @@ public class JoalAudioRenderer implements AudioRenderer, Runnable { private void clearChannel(int index) { // make room at this channel if (chanSrcs[index] != null) { - AudioNode src = chanSrcs[index]; + AudioSource src = chanSrcs[index]; int sourceId = channels[index]; al.alSourceStop(sourceId); @@ -750,7 +750,7 @@ public class JoalAudioRenderer implements AudioRenderer, Runnable { al.alSourcei(sourceId, AL.AL_DIRECT_FILTER, AL.AL_FILTER_NULL); } if (src.isPositional()) { - AudioNode pas = (AudioNode) src; + AudioSource pas = (AudioSource) src; if (pas.isReverbEnabled() && supportEfx) { al.alSource3i(sourceId, AL.AL_AUXILIARY_SEND_FILTER, 0, 0, AL.AL_FILTER_NULL); } @@ -770,7 +770,7 @@ public class JoalAudioRenderer implements AudioRenderer, Runnable { } for (int i = 0; i < channels.length; i++) { - AudioNode src = chanSrcs[i]; + AudioSource src = chanSrcs[i]; if (src == null) { continue; } @@ -790,7 +790,7 @@ public class JoalAudioRenderer implements AudioRenderer, Runnable { ib.position(0).limit(1); al.alGetSourcei(sourceId, AL.AL_SOURCE_STATE, ib); int state = ib.get(0); - boolean wantPlaying = src.getStatus() == AudioNode.Status.Playing; + boolean wantPlaying = src.getStatus() == AudioSource.Status.Playing; boolean stopped = state == ALConstants.AL_STOPPED; if (streaming && wantPlaying) { @@ -803,7 +803,7 @@ public class JoalAudioRenderer implements AudioRenderer, Runnable { } else { if (stopped) { // became inactive - src.setStatus(AudioNode.Status.Stopped); + src.setStatus(AudioSource.Status.Stopped); src.setChannel(-1); clearChannel(i); freeChannel(i); @@ -817,11 +817,11 @@ public class JoalAudioRenderer implements AudioRenderer, Runnable { boolean paused = state == ALConstants.AL_PAUSED; // make sure OAL pause state & source state coincide - assert (src.getStatus() == AudioNode.Status.Paused && paused) || (!paused); + assert (src.getStatus() == AudioSource.Status.Paused && paused) || (!paused); if (stopped) { if (boundSource) { - src.setStatus(AudioNode.Status.Stopped); + src.setStatus(AudioSource.Status.Stopped); src.setChannel(-1); } clearChannel(i); @@ -859,7 +859,7 @@ public class JoalAudioRenderer implements AudioRenderer, Runnable { } } - public void playSourceInstance(AudioNode src) { + public void playSourceInstance(AudioSource src) { checkDead(); synchronized (threadLock) { while (!threadLock.get()) { @@ -902,7 +902,7 @@ public class JoalAudioRenderer implements AudioRenderer, Runnable { } } - public void playSource(AudioNode src) { + public void playSource(AudioSource src) { checkDead(); synchronized (threadLock) { while (!threadLock.get()) { @@ -917,9 +917,9 @@ public class JoalAudioRenderer implements AudioRenderer, Runnable { //assert src.getStatus() == Status.Stopped || src.getChannel() == -1; - if (src.getStatus() == AudioNode.Status.Playing) { + if (src.getStatus() == AudioSource.Status.Playing) { return; - } else if (src.getStatus() == AudioNode.Status.Stopped) { + } else if (src.getStatus() == AudioSource.Status.Stopped) { // allocate channel to this source int index = newChannel(); @@ -941,11 +941,11 @@ public class JoalAudioRenderer implements AudioRenderer, Runnable { } al.alSourcePlay(channels[src.getChannel()]); - src.setStatus(AudioNode.Status.Playing); + src.setStatus(AudioSource.Status.Playing); } } - public void pauseSource(AudioNode src) { + public void pauseSource(AudioSource src) { checkDead(); synchronized (threadLock) { while (!threadLock.get()) { @@ -958,16 +958,16 @@ public class JoalAudioRenderer implements AudioRenderer, Runnable { return; } - if (src.getStatus() == AudioNode.Status.Playing) { + if (src.getStatus() == AudioSource.Status.Playing) { assert src.getChannel() != -1; al.alSourcePause(channels[src.getChannel()]); - src.setStatus(AudioNode.Status.Paused); + src.setStatus(AudioSource.Status.Paused); } } } - public void stopSource(AudioNode src) { + public void stopSource(AudioSource src) { synchronized (threadLock) { while (!threadLock.get()) { try { @@ -979,11 +979,11 @@ public class JoalAudioRenderer implements AudioRenderer, Runnable { return; } - if (src.getStatus() != AudioNode.Status.Stopped) { + if (src.getStatus() != AudioSource.Status.Stopped) { int chan = src.getChannel(); assert chan != -1; // if it's not stopped, must have id - src.setStatus(AudioNode.Status.Stopped); + src.setStatus(AudioSource.Status.Stopped); src.setChannel(-1); clearChannel(chan); freeChannel(chan); diff --git a/engine/src/lwjgl/com/jme3/audio/lwjgl/LwjglAudioRenderer.java b/engine/src/lwjgl/com/jme3/audio/lwjgl/LwjglAudioRenderer.java index e995ae609..4d80b3b8f 100644 --- a/engine/src/lwjgl/com/jme3/audio/lwjgl/LwjglAudioRenderer.java +++ b/engine/src/lwjgl/com/jme3/audio/lwjgl/LwjglAudioRenderer.java @@ -31,7 +31,7 @@ */ package com.jme3.audio.lwjgl; -import com.jme3.audio.AudioNode.Status; +import com.jme3.audio.AudioSource.Status; import com.jme3.audio.*; import com.jme3.math.Vector3f; import com.jme3.util.BufferUtils; @@ -61,7 +61,7 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable { private final ByteBuffer nativeBuf = BufferUtils.createByteBuffer(BUFFER_SIZE); private final byte[] arrayBuf = new byte[BUFFER_SIZE]; private int[] channels; - private AudioNode[] chanSrcs; + private AudioSource[] chanSrcs; private int nextChan = 0; private ArrayList freeChans = new ArrayList(); private Listener listener; @@ -156,10 +156,10 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable { ALCdevice device = AL.getDevice(); String deviceName = ALC10.alcGetString(device, ALC10.ALC_DEVICE_SPECIFIER); - logger.log(Level.FINER, "Audio Device: {0}", deviceName); - logger.log(Level.FINER, "Audio Vendor: {0}", alGetString(AL_VENDOR)); - logger.log(Level.FINER, "Audio Renderer: {0}", alGetString(AL_RENDERER)); - logger.log(Level.FINER, "Audio Version: {0}", alGetString(AL_VERSION)); + logger.log(Level.INFO, "Audio Device: {0}", deviceName); + logger.log(Level.INFO, "Audio Vendor: {0}", alGetString(AL_VENDOR)); + logger.log(Level.INFO, "Audio Renderer: {0}", alGetString(AL_RENDERER)); + logger.log(Level.INFO, "Audio Version: {0}", alGetString(AL_VERSION)); // Find maximum # of sources supported by this implementation ArrayList channelList = new ArrayList(); @@ -178,9 +178,9 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable { } ib = BufferUtils.createIntBuffer(channels.length); - chanSrcs = new AudioNode[channels.length]; + chanSrcs = new AudioSource[channels.length]; - logger.log(Level.FINE, "AudioRenderer supports {0} channels", channels.length); + logger.log(Level.INFO, "AudioRenderer supports {0} channels", channels.length); supportEfx = ALC10.alcIsExtensionPresent(device, "ALC_EXT_EFX"); if (supportEfx) { @@ -190,11 +190,11 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable { ib.position(0).limit(1); ALC10.alcGetInteger(device, EFX10.ALC_EFX_MINOR_VERSION, ib); int minor = ib.get(0); - logger.log(Level.FINE, "Audio effect extension version: {0}.{1}", new Object[]{major, minor}); + logger.log(Level.INFO, "Audio effect extension version: {0}.{1}", new Object[]{major, minor}); ALC10.alcGetInteger(device, EFX10.ALC_MAX_AUXILIARY_SENDS, ib); auxSends = ib.get(0); - logger.log(Level.FINE, "Audio max auxilary sends: {0}", auxSends); + logger.log(Level.INFO, "Audio max auxilary sends: {0}", auxSends); // create slot ib.position(0).limit(1); @@ -282,7 +282,7 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable { f.clearUpdateNeeded(); } - public void updateSourceParam(AudioNode src, AudioParam param) { + public void updateSourceParam(AudioSource src, AudioParam param) { checkDead(); synchronized (threadLock) { while (!threadLock.get()) { @@ -295,10 +295,10 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable { return; } - // There is a race condition in AudioNode that can + // There is a race condition in AudioSource that can // cause this to be called for a node that has been // detached from its channel. For example, setVolume() - // called from the render thread may see that that AudioNode + // called from the render thread may see that that AudioSource // still has a channel value but the audio thread may // clear that channel before setVolume() gets to call // updateSourceParam() (because the audio stopped playing @@ -317,7 +317,7 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable { return; } - Vector3f pos = src.getWorldTranslation(); + Vector3f pos = src.getPosition(); alSource3f(id, AL_POSITION, pos.x, pos.y, pos.z); break; case Velocity: @@ -456,9 +456,9 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable { } } - private void setSourceParams(int id, AudioNode src, boolean forceNonLoop) { + private void setSourceParams(int id, AudioSource src, boolean forceNonLoop) { if (src.isPositional()) { - Vector3f pos = src.getWorldTranslation(); + Vector3f pos = src.getPosition(); Vector3f vel = src.getVelocity(); alSource3f(id, AL_POSITION, pos.x, pos.y, pos.z); alSource3f(id, AL_VELOCITY, vel.x, vel.y, vel.z); @@ -704,7 +704,7 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable { private void clearChannel(int index) { // make room at this channel if (chanSrcs[index] != null) { - AudioNode src = chanSrcs[index]; + AudioSource src = chanSrcs[index]; int sourceId = channels[index]; alSourceStop(sourceId); @@ -723,7 +723,7 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable { alSourcei(sourceId, EFX10.AL_DIRECT_FILTER, EFX10.AL_FILTER_NULL); } if (src.isPositional()) { - AudioNode pas = (AudioNode) src; + AudioSource pas = (AudioSource) src; if (pas.isReverbEnabled() && supportEfx) { AL11.alSource3i(sourceId, EFX10.AL_AUXILIARY_SEND_FILTER, 0, 0, EFX10.AL_FILTER_NULL); } @@ -743,7 +743,7 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable { } for (int i = 0; i < channels.length; i++) { - AudioNode src = chanSrcs[i]; + AudioSource src = chanSrcs[i]; if (src == null) { continue; } @@ -830,7 +830,7 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable { } } - public void playSourceInstance(AudioNode src) { + public void playSourceInstance(AudioSource src) { checkDead(); synchronized (threadLock) { while (!threadLock.get()) { @@ -873,7 +873,7 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable { } } - public void playSource(AudioNode src) { + public void playSource(AudioSource src) { checkDead(); synchronized (threadLock) { while (!threadLock.get()) { @@ -916,7 +916,7 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable { } } - public void pauseSource(AudioNode src) { + public void pauseSource(AudioSource src) { checkDead(); synchronized (threadLock) { while (!threadLock.get()) { @@ -938,7 +938,7 @@ public class LwjglAudioRenderer implements AudioRenderer, Runnable { } } - public void stopSource(AudioNode src) { + public void stopSource(AudioSource src) { synchronized (threadLock) { while (!threadLock.get()) { try {