Mercurial > jmeCapture
view src/com/aurellem/capture/audio/AudioSendRenderer.java @ 35:c969b04fa0dc
working on divergence
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Mon, 31 Oct 2011 00:14:46 -0700 |
parents | 13d354e1184b |
children | 2a525e937d86 |
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 l.setRenderer(this);75 }77 /**78 * Whenever new data is rendered in the perspective of this listener,79 * this Renderer will send that data to the SoundProcessor of your choosing.80 */81 public void registerSoundProcessor(Listener l, SoundProcessor sp) {82 this.soundProcessorMap.put(l, sp);83 }85 /**86 * Registers a SoundProcessor for the main LWJGL context. IF all you want to87 * do is record the sound you would normally hear in your application, then88 * this is the only method you have to worry about.89 */90 public void registerSoundProcessor(SoundProcessor sp){91 // register a sound processor for the default listener.92 this.soundProcessorMap.put(null, sp);93 }95 private static final Logger logger =96 Logger.getLogger(AudioSendRenderer.class.getName());100 /**101 * Instead of taking whatever device is available on the system, this call102 * creates the "Multiple Audio Send" device, which supports multiple listeners in a limited103 * capacity. For each listener, the device renders it not to the sound device, but104 * instead to buffers which it makes available via JNI.105 */106 public void initInThread(){107 try{108 if (!AL.isCreated()){109 AL.create("Multiple Audio Send", (int) outFormat.getSampleRate(), 60, false);110 }111 }catch (OpenALException ex){112 logger.log(Level.SEVERE, "Failed to load audio library", ex);113 System.exit(1);114 return;115 }catch (LWJGLException ex){116 logger.log(Level.SEVERE, "Failed to load audio library", ex);117 System.exit(1);118 return;119 }120 super.initInThread();122 ALCdevice device = AL.getDevice();124 // RLM: use reflection to grab the ID of our device for use later.125 try {126 Field deviceIDField;127 deviceIDField = ALCdevice.class.getDeclaredField("device");128 deviceIDField.setAccessible(true);129 try {deviceID = (Long)deviceIDField.get(device);}130 catch (IllegalArgumentException e) {e.printStackTrace();}131 catch (IllegalAccessException e) {e.printStackTrace();}132 deviceIDField.setAccessible(false);}133 catch (SecurityException e) {e.printStackTrace();}134 catch (NoSuchFieldException e) {e.printStackTrace();}136 this.audioSend = new AudioSend(this.deviceID);138 // The LWJGL context must be established as the master context before139 // any other listeners can be created on this device.140 audioSend.initDevice();141 // Now, everything is initialized, and it is safe to add more listeners.142 latch.countDown();143 }146 public void cleanup(){147 for(SoundProcessor sp : this.soundProcessorMap.values()){148 sp.cleanup();149 }150 super.cleanup();151 }153 public void updateAllListeners(){154 for (int i = 0; i < this.listeners.size(); i++){155 Listener lis = this.listeners.get(i);156 if (null != lis){157 Vector3f location = lis.getLocation();158 Vector3f velocity = lis.getVelocity();159 Vector3f orientation = lis.getUp();160 float gain = lis.getVolume();161 audioSend.setNthListener3f(AL10.AL_POSITION,162 location.x, location.y, location.z, i);163 audioSend.setNthListener3f(AL10.AL_VELOCITY,164 velocity.x, velocity.y, velocity.z, i);165 audioSend.setNthListener3f(AL10.AL_ORIENTATION,166 orientation.x, orientation.y, orientation.z, i);167 audioSend.setNthListenerf(AL10.AL_GAIN, gain, i);168 }169 }170 }173 //public final static int BYTES_PER_SAMPLE = 4;174 private ByteBuffer buffer = BufferUtils.createByteBuffer(4096);176 public void dispatchAudio(float tpf){177 int samplesToGet = (int) (tpf * outFormat.getSampleRate());178 try {latch.await();}179 catch (InterruptedException e) {e.printStackTrace();}180 audioSend.step(samplesToGet);181 updateAllListeners();183 for (int i = 0; i < this.listeners.size(); i++){184 buffer.clear();185 audioSend.getSamples(buffer, samplesToGet, i);186 SoundProcessor sp =187 this.soundProcessorMap.get(this.listeners.get(i));188 if (null != sp){sp.process(buffer, samplesToGet*outFormat.getFrameSize(), outFormat);}189 }191 }193 public void update(float tpf){194 super.update(tpf);195 dispatchAudio(tpf);196 }198 }