Mercurial > jmeCapture
view src/com/aurellem/capture/audio/AudioSendRenderer.java @ 30:be37291c62b8
propagated AudioFormat to other classes.
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sun, 30 Oct 2011 10:11:21 -0700 |
parents | 9f58273090df |
children | 13d354e1184b |
line wrap: on
line source
1 package com.aurellem.capture.audio;3 import java.lang.reflect.Field;4 import java.nio.ByteBuffer;5 import java.util.HashMap;6 import java.util.Vector;7 import java.util.concurrent.CountDownLatch;8 import java.util.logging.Level;9 import java.util.logging.Logger;11 import javax.sound.sampled.AudioFormat;13 import org.lwjgl.LWJGLException;14 import org.lwjgl.openal.AL;15 import org.lwjgl.openal.AL10;16 import org.lwjgl.openal.ALCdevice;17 import org.lwjgl.openal.OpenALException;19 import com.aurellem.send.AudioSend;20 import com.jme3.audio.Listener;21 import com.jme3.audio.lwjgl.LwjglAudioRenderer;22 import com.jme3.math.Vector3f;23 import com.jme3.util.BufferUtils;25 public class AudioSendRenderer27 extends LwjglAudioRenderer implements MultiListener {29 private AudioSend audioSend;30 private static final AudioFormat outFormat = new AudioFormat(44100.0f, 32, 1, true, false);32 /**33 * Keeps track of all the listeners which have been registered so far.34 * The first element is <code>null</code>, which represents the zeroth35 * LWJGL listener which is created automatically.36 */37 public Vector<Listener> listeners = new Vector<Listener>();39 public void initialize(){40 super.initialize();41 listeners.add(null);42 }44 /**45 * This is to call the native methods which require the OpenAL device ID.46 * currently it is obtained through reflection.47 */48 private long deviceID;50 /**51 * To ensure that <code>deviceID<code> and <code>listeners<code> are52 * properly initialized before any additional listeners are added.53 */54 private CountDownLatch latch = new CountDownLatch(1);56 /**57 * Each listener (including the main LWJGL listener) can be registered58 * with a <code>SoundProcessor</code>, which this Renderer will call whenever59 * there is new audio data to be processed.60 */61 public HashMap<Listener, SoundProcessor> soundProcessorMap =62 new HashMap<Listener, SoundProcessor>();65 /**66 * Create a new slave context on the recorder device which will render all the67 * sounds in the main LWJGL context with respect to this listener.68 */69 public void addListener(Listener l) {70 try {this.latch.await();}71 catch (InterruptedException e) {e.printStackTrace();}72 audioSend.addListener();73 this.listeners.add(l);74 }76 /**77 * Whenever new data is rendered in the perspective of this listener,78 * this Renderer will send that data to the SoundProcessor of your choosing.79 */80 public void registerSoundProcessor(Listener l, SoundProcessor sp) {81 this.soundProcessorMap.put(l, sp);82 }84 /**85 * Registers a SoundProcessor for the main LWJGL context. IF all you want to86 * do is record the sound you would normally hear in your application, then87 * this is the only method you have to worry about.88 */89 public void registerSoundProcessor(SoundProcessor sp){90 // register a sound processor for the default listener.91 this.soundProcessorMap.put(null, sp);92 }94 private static final Logger logger =95 Logger.getLogger(AudioSendRenderer.class.getName());99 /**100 * Instead of taking whatever device is available on the system, this call101 * creates the "Multiple Audio Send" device, which supports multiple listeners in a limited102 * capacity. For each listener, the device renders it not to the sound device, but103 * instead to buffers which it makes available via JNI.104 */105 public void initInThread(){106 try{107 if (!AL.isCreated()){108 AL.create("Multiple Audio Send", (int) outFormat.getSampleRate(), 60, false);109 }110 }catch (OpenALException ex){111 logger.log(Level.SEVERE, "Failed to load audio library", ex);112 System.exit(1);113 return;114 }catch (LWJGLException ex){115 logger.log(Level.SEVERE, "Failed to load audio library", ex);116 System.exit(1);117 return;118 }119 super.initInThread();121 ALCdevice device = AL.getDevice();123 // RLM: use reflection to grab the ID of our device for use later.124 try {125 Field deviceIDField;126 deviceIDField = ALCdevice.class.getDeclaredField("device");127 deviceIDField.setAccessible(true);128 try {deviceID = (Long)deviceIDField.get(device);}129 catch (IllegalArgumentException e) {e.printStackTrace();}130 catch (IllegalAccessException e) {e.printStackTrace();}131 deviceIDField.setAccessible(false);}132 catch (SecurityException e) {e.printStackTrace();}133 catch (NoSuchFieldException e) {e.printStackTrace();}135 this.audioSend = new AudioSend(this.deviceID);137 // The LWJGL context must be established as the master context before138 // any other listeners can be created on this device.139 audioSend.initDevice();140 // Now, everything is initialized, and it is safe to add more listeners.141 latch.countDown();142 }145 public void cleanup(){146 for(SoundProcessor sp : this.soundProcessorMap.values()){147 sp.cleanup();148 }149 super.cleanup();150 }152 public void updateAllListeners(){153 for (int i = 0; i < this.listeners.size(); i++){154 Listener lis = this.listeners.get(i);155 if (null != lis){156 Vector3f location = lis.getLocation();157 Vector3f velocity = lis.getVelocity();158 Vector3f orientation = lis.getUp();159 float gain = lis.getVolume();160 audioSend.setNthListener3f(AL10.AL_POSITION,161 location.x, location.y, location.z, i);162 audioSend.setNthListener3f(AL10.AL_VELOCITY,163 velocity.x, velocity.y, velocity.z, i);164 audioSend.setNthListener3f(AL10.AL_ORIENTATION,165 orientation.x, orientation.y, orientation.z, i);166 audioSend.setNthListenerf(AL10.AL_GAIN, gain, i);167 }168 }169 }172 //public final static int BYTES_PER_SAMPLE = 4;173 private ByteBuffer buffer = BufferUtils.createByteBuffer(4096);175 public void dispatchAudio(float tpf){176 int samplesToGet = (int) (tpf * outFormat.getSampleRate());177 try {latch.await();}178 catch (InterruptedException e) {e.printStackTrace();}179 audioSend.step(samplesToGet);180 updateAllListeners();182 for (int i = 0; i < this.listeners.size(); i++){183 buffer.clear();184 audioSend.getSamples(buffer, samplesToGet, i);185 SoundProcessor sp =186 this.soundProcessorMap.get(this.listeners.get(i));187 if (null != sp){sp.process(buffer, samplesToGet*outFormat.getFrameSize(), outFormat);}188 }190 }192 public void update(float tpf){193 super.update(tpf);194 dispatchAudio(tpf);195 }197 }