Mercurial > jmeCapture
view src/com/aurellem/capture/audio/AudioSendRenderer.java @ 40:56dc950feaed
added README
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Thu, 03 Nov 2011 16:39:32 -0700 |
parents | 784a3f4e6202 |
children | 2f129118e2d6 |
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 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", 44100, 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);137 this.outFormat = audioSend.getAudioFormat();138 initBuffer();140 // The LWJGL context must be established as the master context before141 // any other listeners can be created on this device.142 audioSend.initDevice();143 // Now, everything is initialized, and it is safe to add more listeners.144 latch.countDown();145 }148 public void cleanup(){149 for(SoundProcessor sp : this.soundProcessorMap.values()){150 sp.cleanup();151 }152 super.cleanup();153 }155 public void updateAllListeners(){156 for (int i = 0; i < this.listeners.size(); i++){157 Listener lis = this.listeners.get(i);158 if (null != lis){159 Vector3f location = lis.getLocation();160 Vector3f velocity = lis.getVelocity();161 Vector3f orientation = lis.getUp();162 float gain = lis.getVolume();163 audioSend.setNthListener3f(AL10.AL_POSITION,164 location.x, location.y, location.z, i);165 audioSend.setNthListener3f(AL10.AL_VELOCITY,166 velocity.x, velocity.y, velocity.z, i);167 audioSend.setNthListener3f(AL10.AL_ORIENTATION,168 orientation.x, orientation.y, orientation.z, i);169 audioSend.setNthListenerf(AL10.AL_GAIN, gain, i);170 }171 }172 }175 private ByteBuffer buffer;;177 public static final int MIN_FRAMERATE = 10;179 private void initBuffer(){180 int bufferSize = (int)(this.outFormat.getSampleRate() / ((float)MIN_FRAMERATE)) *181 this.outFormat.getFrameSize();182 this.buffer = BufferUtils.createByteBuffer(bufferSize);183 }184 /*186 */187 public void dispatchAudio(float tpf){189 int samplesToGet = (int) (tpf * outFormat.getSampleRate());190 try {latch.await();}191 catch (InterruptedException e) {e.printStackTrace();}192 audioSend.step(samplesToGet);193 updateAllListeners();195 for (int i = 0; i < this.listeners.size(); i++){196 buffer.clear();197 audioSend.getSamples(buffer, samplesToGet, i);198 SoundProcessor sp =199 this.soundProcessorMap.get(this.listeners.get(i));200 if (null != sp){sp.process(buffer, samplesToGet*outFormat.getFrameSize(), outFormat);}201 }203 }205 public void update(float tpf){206 super.update(tpf);207 dispatchAudio(tpf);208 }210 }