# HG changeset patch # User Robert McIntyre # Date 1319707622 25200 # Node ID 8a6b1684f53697ac8b90ae6201a9d87caf62ae1f # Parent 4c5fc53778c1c205a52b57ebdfc711b769a62c7a refactored. diff -r 4c5fc53778c1 -r 8a6b1684f536 build.xml --- a/build.xml Wed Oct 26 09:38:27 2011 -0700 +++ b/build.xml Thu Oct 27 02:27:02 2011 -0700 @@ -10,6 +10,7 @@ + diff -r 4c5fc53778c1 -r 8a6b1684f536 src/com/aurellem/capture/Capture.java --- a/src/com/aurellem/capture/Capture.java Wed Oct 26 09:38:27 2011 -0700 +++ b/src/com/aurellem/capture/Capture.java Thu Oct 27 02:27:02 2011 -0700 @@ -13,6 +13,9 @@ public static void SimpleCaptureVideo(Application app, File file) throws IOException{ app.getViewPort().setClearFlags(true, true, true); + // this prevents pixels from staying in the render buffer between frames + // and messing the video up. It's not a problem since Black is the default, and this + // can be overridden by user code. app.getViewPort().setBackgroundColor(ColorRGBA.Black); // The XuggleVideoRecorder is better than the AVIVideoRecorder in every way @@ -24,14 +27,9 @@ if (file.getCanonicalPath().endsWith(".avi")){ videoRecorder = new AVIVideoRecorder(file);} - else { videoRecorder = new XuggleVideoRecorder(file); } + else { videoRecorder = new XuggleVideoRecorder(file);} app.getStateManager().attach(videoRecorder); app.getViewPort().addFinalProcessor(videoRecorder); - } - - - - - + } } diff -r 4c5fc53778c1 -r 8a6b1684f536 src/com/aurellem/capture/audio/AudioSend.java --- a/src/com/aurellem/capture/audio/AudioSend.java Wed Oct 26 09:38:27 2011 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,260 +0,0 @@ -package com.aurellem.capture.audio; - -import java.lang.reflect.Field; -import java.nio.ByteBuffer; -import java.util.HashMap; -import java.util.Vector; -import java.util.concurrent.CountDownLatch; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.lwjgl.LWJGLException; -import org.lwjgl.openal.AL; -import org.lwjgl.openal.AL10; -import org.lwjgl.openal.ALCdevice; -import org.lwjgl.openal.OpenALException; - -import com.jme3.audio.Listener; -import com.jme3.audio.lwjgl.LwjglAudioRenderer; -import com.jme3.math.Vector3f; -import com.jme3.util.BufferUtils; - -public class AudioSend - extends LwjglAudioRenderer implements MultiListener { - - /** - * Keeps track of all the listeners which have been registered so far. - * The first element is null, which represents the zeroth - * LWJGL listener which is created automatically. - */ - public Vector listeners = new Vector(); - - public void initialize(){ - super.initialize(); - listeners.add(null); - } - - /** - * This is to call the native methods which require the OpenAL device ID. - * currently it is obtained through reflection. - */ - private long deviceID; - - /** - * To ensure that deviceID and listeners are - * properly initialized before any additional listeners are added. - */ - private CountDownLatch latch = new CountDownLatch(1); - - private void waitForInit(){ - try {latch.await();} - catch (InterruptedException e) {e.printStackTrace();} - } - - /** - * Each listener (including the main LWJGL listener) can be registered - * with a SoundProcessor, which this Renderer will call whenever - * there is new audio data to be processed. - */ - public HashMap soundProcessorMap = - new HashMap(); - - - /** - * Create a new slave context on the recorder device which will render all the - * sounds in the main LWJGL context with respect to this listener. - */ - public void addListener(Listener l) { - try {this.latch.await();} - catch (InterruptedException e) {e.printStackTrace();} - this.addListener(); - this.listeners.add(l); - } - - /** - * Whenever new data is rendered in the perspective of this listener, - * this Renderer will send that data to the SoundProcessor of your choosing. - */ - public void registerSoundProcessor(Listener l, SoundProcessor sp) { - this.soundProcessorMap.put(l, sp); - } - - /** - * Registers a SoundProcessor for the main LWJGL context. IF all you want to - * do is record the sound you would normally hear in your application, then - * this is the only method you have to worry about. - */ - public void registerSoundProcessor(SoundProcessor sp){ - // register a sound processor for the default listener. - this.soundProcessorMap.put(null, sp); - } - - private static final Logger logger = - Logger.getLogger(AudioSend.class.getName()); - - - //////////// Native Methods - - /** This establishes the LWJGL context as the context which will be copies to all - * other contexts. It must be called before any calls to addListener(); - */ - public void initDevice(){ - ninitDevice(this.deviceID);} - public static native void ninitDevice(long device); - - /** - * The send device does not automatically process sound. This step function will cause - * the desired number of samples to be processed for each listener. The results will then - * be available via calls to getSamples() for each listener. - * @param samples - */ - public void step(int samples){ - nstep(this.deviceID, samples);} - public static native void nstep(long device, int samples); - - /** - * Retrieve the final rendered sound for a particular listener. contextNum == 0 - * is the main LWJGL context. - * @param buffer - * @param samples - * @param contextNum - */ - public void getSamples(ByteBuffer buffer, int samples, int contextNum){ - ngetSamples(this.deviceID, buffer, buffer.position(), samples, contextNum);} - public static native void ngetSamples( - long device, ByteBuffer buffer, int position, int samples, int contextNum); - - /** - * Create an additional listener on the recorder device. The device itself will manage - * this listener and synchronize it with the main LWJGL context. Processed sound samples - * for this listener will be available via a call to getSamples() with - * contextNum equal to the number of times this method has been called. - */ - public void addListener(){naddListener(this.deviceID);} - public static native void naddListener(long device); - - /** - * This will internally call alListener3f in the appropriate slave context and update - * that context's listener's parameters. Calling this for a number greater than the current - * number of slave contexts will have no effect. - * @param pname - * @param v1 - * @param v2 - * @param v3 - * @param contextNum - */ - public void setNthListener3f(int pname, float v1, float v2, float v3, int contextNum){ - nsetNthListener3f(pname, v1, v2, v3, this.deviceID, contextNum);} - public static native void - nsetNthListener3f(int pname, float v1, float v2, float v3, long device, int contextNum); - - /** - * This will internally call alListenerf in the appropriate slave context and update - * that context's listener's parameters. Calling this for a number greater than the current - * number of slave contexts will have no effect. - * @param pname - * @param v1 - * @param contextNum - */ - public void setNthListenerf(int pname, float v1, int contextNum){ - nsetNthListenerf(pname, v1, this.deviceID, contextNum);} - public static native void nsetNthListenerf(int pname, float v1, long device, int contextNum); - - /** - * Instead of taking whatever device is available on the system, this call - * creates the "Multiple Audio Send" device, which supports multiple listeners in a limited - * capacity. For each listener, the device renders it not to the sound device, but - * instead to buffers which it makes available via JNI. - */ - public void initInThread(){ - try{ - if (!AL.isCreated()){ - AL.create("Multiple Audio Send", 44100, 60, false); - } - }catch (OpenALException ex){ - logger.log(Level.SEVERE, "Failed to load audio library", ex); - System.exit(1); - return; - }catch (LWJGLException ex){ - logger.log(Level.SEVERE, "Failed to load audio library", ex); - System.exit(1); - return; - } - super.initInThread(); - - ALCdevice device = AL.getDevice(); - - // RLM: use reflection to grab the ID of our device for use later. - try { - Field deviceIDField; - deviceIDField = ALCdevice.class.getDeclaredField("device"); - deviceIDField.setAccessible(true); - try {deviceID = (Long)deviceIDField.get(device);} - catch (IllegalArgumentException e) {e.printStackTrace();} - catch (IllegalAccessException e) {e.printStackTrace();} - deviceIDField.setAccessible(false);} - catch (SecurityException e) {e.printStackTrace();} - catch (NoSuchFieldException e) {e.printStackTrace();} - - // the LWJGL context must be established as the master context before - // any other listeners can be created on this device. - initDevice(); - // Now, everything is initialized, and it is safe to add more listeners. - latch.countDown(); - } - - - public void cleanup(){ - for(SoundProcessor sp : this.soundProcessorMap.values()){ - sp.cleanup(); - } - super.cleanup(); - } - - public void updateAllListeners(){ - for (int i = 0; i < this.listeners.size(); i++){ - Listener lis = this.listeners.get(i); - if (null != lis){ - Vector3f location = lis.getLocation(); - Vector3f velocity = lis.getVelocity(); - Vector3f orientation = lis.getUp(); - float gain = lis.getVolume(); - setNthListener3f(AL10.AL_POSITION, - location.x, location.y, location.z, i); - setNthListener3f(AL10.AL_VELOCITY, - velocity.x, velocity.y, velocity.z, i); - setNthListener3f(AL10.AL_ORIENTATION, - orientation.x, orientation.y, orientation.z, i); - setNthListenerf(AL10.AL_GAIN, gain, i); - } - } - } - - - public final static int BYTES_PER_SAMPLE = 4; - private ByteBuffer buffer = BufferUtils.createByteBuffer(4096); - - public void dispatchAudio(float tpf){ - int samplesToGet = (int) (tpf * 44100); - try {latch.await();} - catch (InterruptedException e) {e.printStackTrace();} - step(samplesToGet); - updateAllListeners(); - - for (int i = 0; i < this.listeners.size(); i++){ - buffer.clear(); - this.getSamples(buffer, samplesToGet, i); - SoundProcessor sp = - this.soundProcessorMap.get(this.listeners.get(i)); - if (null != sp){sp.process(buffer, samplesToGet*BYTES_PER_SAMPLE);} - } - - } - - public void update(float tpf){ - super.update(tpf); - dispatchAudio(tpf); - } - -} - diff -r 4c5fc53778c1 -r 8a6b1684f536 src/com/aurellem/capture/audio/AudioSendRenderer.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/com/aurellem/capture/audio/AudioSendRenderer.java Thu Oct 27 02:27:02 2011 -0700 @@ -0,0 +1,195 @@ +package com.aurellem.capture.audio; + +import java.lang.reflect.Field; +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.Vector; +import java.util.concurrent.CountDownLatch; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.lwjgl.LWJGLException; +import org.lwjgl.openal.AL; +import org.lwjgl.openal.AL10; +import org.lwjgl.openal.ALCdevice; +import org.lwjgl.openal.OpenALException; + +import com.aurellem.send.AudioSend; +import com.jme3.audio.Listener; +import com.jme3.audio.lwjgl.LwjglAudioRenderer; +import com.jme3.math.Vector3f; +import com.jme3.util.BufferUtils; + +public class AudioSendRenderer + + extends LwjglAudioRenderer implements MultiListener { + + private AudioSend audioSend; + + /** + * Keeps track of all the listeners which have been registered so far. + * The first element is null, which represents the zeroth + * LWJGL listener which is created automatically. + */ + public Vector listeners = new Vector(); + + public void initialize(){ + super.initialize(); + listeners.add(null); + } + + /** + * This is to call the native methods which require the OpenAL device ID. + * currently it is obtained through reflection. + */ + private long deviceID; + + /** + * To ensure that deviceID and listeners are + * properly initialized before any additional listeners are added. + */ + private CountDownLatch latch = new CountDownLatch(1); + + /** + * Each listener (including the main LWJGL listener) can be registered + * with a SoundProcessor, which this Renderer will call whenever + * there is new audio data to be processed. + */ + public HashMap soundProcessorMap = + new HashMap(); + + + /** + * Create a new slave context on the recorder device which will render all the + * sounds in the main LWJGL context with respect to this listener. + */ + public void addListener(Listener l) { + try {this.latch.await();} + catch (InterruptedException e) {e.printStackTrace();} + audioSend.addListener(); + this.listeners.add(l); + } + + /** + * Whenever new data is rendered in the perspective of this listener, + * this Renderer will send that data to the SoundProcessor of your choosing. + */ + public void registerSoundProcessor(Listener l, SoundProcessor sp) { + this.soundProcessorMap.put(l, sp); + } + + /** + * Registers a SoundProcessor for the main LWJGL context. IF all you want to + * do is record the sound you would normally hear in your application, then + * this is the only method you have to worry about. + */ + public void registerSoundProcessor(SoundProcessor sp){ + // register a sound processor for the default listener. + this.soundProcessorMap.put(null, sp); + } + + private static final Logger logger = + Logger.getLogger(AudioSendRenderer.class.getName()); + + + + /** + * Instead of taking whatever device is available on the system, this call + * creates the "Multiple Audio Send" device, which supports multiple listeners in a limited + * capacity. For each listener, the device renders it not to the sound device, but + * instead to buffers which it makes available via JNI. + */ + public void initInThread(){ + try{ + if (!AL.isCreated()){ + AL.create("Multiple Audio Send", 44100, 60, false); + } + }catch (OpenALException ex){ + logger.log(Level.SEVERE, "Failed to load audio library", ex); + System.exit(1); + return; + }catch (LWJGLException ex){ + logger.log(Level.SEVERE, "Failed to load audio library", ex); + System.exit(1); + return; + } + super.initInThread(); + + ALCdevice device = AL.getDevice(); + + // RLM: use reflection to grab the ID of our device for use later. + try { + Field deviceIDField; + deviceIDField = ALCdevice.class.getDeclaredField("device"); + deviceIDField.setAccessible(true); + try {deviceID = (Long)deviceIDField.get(device);} + catch (IllegalArgumentException e) {e.printStackTrace();} + catch (IllegalAccessException e) {e.printStackTrace();} + deviceIDField.setAccessible(false);} + catch (SecurityException e) {e.printStackTrace();} + catch (NoSuchFieldException e) {e.printStackTrace();} + + this.audioSend = new AudioSend(this.deviceID); + + // The LWJGL context must be established as the master context before + // any other listeners can be created on this device. + audioSend.initDevice(); + // Now, everything is initialized, and it is safe to add more listeners. + latch.countDown(); + } + + + public void cleanup(){ + for(SoundProcessor sp : this.soundProcessorMap.values()){ + sp.cleanup(); + } + super.cleanup(); + } + + public void updateAllListeners(){ + for (int i = 0; i < this.listeners.size(); i++){ + Listener lis = this.listeners.get(i); + if (null != lis){ + Vector3f location = lis.getLocation(); + Vector3f velocity = lis.getVelocity(); + Vector3f orientation = lis.getUp(); + float gain = lis.getVolume(); + audioSend.setNthListener3f(AL10.AL_POSITION, + location.x, location.y, location.z, i); + audioSend.setNthListener3f(AL10.AL_VELOCITY, + velocity.x, velocity.y, velocity.z, i); + audioSend.setNthListener3f(AL10.AL_ORIENTATION, + orientation.x, orientation.y, orientation.z, i); + audioSend.setNthListenerf(AL10.AL_GAIN, gain, i); + } + } + } + + + public final static int BYTES_PER_SAMPLE = 4; + private ByteBuffer buffer = BufferUtils.createByteBuffer(4096); + + public void dispatchAudio(float tpf){ + int samplesToGet = (int) (tpf * 44100); + try {latch.await();} + catch (InterruptedException e) {e.printStackTrace();} + audioSend.step(samplesToGet); + updateAllListeners(); + + for (int i = 0; i < this.listeners.size(); i++){ + buffer.clear(); + audioSend.getSamples(buffer, samplesToGet, i); + SoundProcessor sp = + this.soundProcessorMap.get(this.listeners.get(i)); + if (null != sp){sp.process(buffer, samplesToGet*BYTES_PER_SAMPLE);} + } + + } + + public void update(float tpf){ + super.update(tpf); + dispatchAudio(tpf); + } + +} + diff -r 4c5fc53778c1 -r 8a6b1684f536 src/com/aurellem/capture/audio/WaveFileWriter.java --- a/src/com/aurellem/capture/audio/WaveFileWriter.java Wed Oct 26 09:38:27 2011 -0700 +++ b/src/com/aurellem/capture/audio/WaveFileWriter.java Thu Oct 27 02:27:02 2011 -0700 @@ -26,7 +26,6 @@ for (int i = 0; i < this.fullWaveData.size(); i++){ data[i] = this.fullWaveData.get(i);} - ByteArrayInputStream input = new ByteArrayInputStream(data); AudioFormat format = new AudioFormat(44100.0f, 32, 1, true, false); AudioInputStream audioInput = new AudioInputStream(input, format, data.length / 4 ); diff -r 4c5fc53778c1 -r 8a6b1684f536 src/com/aurellem/capture/hello/HelloAudio.java --- a/src/com/aurellem/capture/hello/HelloAudio.java Wed Oct 26 09:38:27 2011 -0700 +++ b/src/com/aurellem/capture/hello/HelloAudio.java Thu Oct 27 02:27:02 2011 -0700 @@ -21,6 +21,24 @@ /** Sample 11 - playing 3D audio. */ public class HelloAudio extends SimpleApplication { + + + public static void main(String[] args) { + + // Logger.getLogger("com.jme3").setLevel(Level.OFF); + + HelloAudio app = new HelloAudio(); + AppSettings settings = new AppSettings(true); + + //settings.setAudioRenderer("Send"); + app.setSettings(settings); + app.setShowSettings(false); + app.start(); + app.setPauseOnLostFocus(false); + } + + + private AudioNode audio_gun; private AudioNode audio_nature; private Geometry player; @@ -34,20 +52,6 @@ } - public static void main(String[] args) { - - // Logger.getLogger("com.jme3").setLevel(Level.OFF); - - HelloAudio app = new HelloAudio(); - AppSettings settings = new AppSettings(true); - - settings.setAudioRenderer("Send"); - app.setSettings(settings); - app.setShowSettings(false); - app.start(); - app.setPauseOnLostFocus(false); - } - @Override public void simpleInitApp() { this.setTimer(new IsoTimer(60)); @@ -93,7 +97,7 @@ private void initAudio() { //audioRenderer.setEnvironment(Environment.Cavern); /* gun shot sound is to be triggered by a mouse click. */ - audio_gun = new AudioNode(audioRenderer, assetManager, "Sound/Effects/Gun.wav", false); + audio_gun = new AudioNode(assetManager, "Sound/Effects/Gun.wav", false); //audio_gun = new AudioNode(assetManager, "Sound/Effects/dream.wav", false, false); audio_gun.setLooping(false); audio_gun.setVolume(2); diff -r 4c5fc53778c1 -r 8a6b1684f536 src/com/aurellem/capture/hello/TestWrite.java --- a/src/com/aurellem/capture/hello/TestWrite.java Wed Oct 26 09:38:27 2011 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ -package com.aurellem.capture.hello; - -public class TestWrite { - - - - - - - - - -} diff -r 4c5fc53778c1 -r 8a6b1684f536 src/com/aurellem/capture/video/XuggleVideoRecorder.java --- a/src/com/aurellem/capture/video/XuggleVideoRecorder.java Wed Oct 26 09:38:27 2011 -0700 +++ b/src/com/aurellem/capture/video/XuggleVideoRecorder.java Thu Oct 27 02:27:02 2011 -0700 @@ -12,8 +12,7 @@ /** * Handles writing video files using Xuggle. - * - * + * * @author Robert McIntyre * */ @@ -39,8 +38,7 @@ width, height); this.videoReady = true; } - - + public void record(BufferedImage rawFrame) { if (!this.videoReady){initVideo();} // convert the Image into the form that Xuggle likes.