Mercurial > jmeCapture
view src/com/aurellem/capture/audio/AudioSendRenderer.java @ 28:7184bc09a92e
need to solve audio recording clicks
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sun, 30 Oct 2011 10:00:29 -0700 |
parents | 5249c8a9603c |
children |
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 public static final AudioFormat outputFormat = 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)outputFormat.getSampleRate(), 60, false); }109 }catch (OpenALException ex){110 logger.log(Level.SEVERE, "Failed to load audio library", ex);111 System.exit(1);112 return;113 }catch (LWJGLException ex){114 logger.log(Level.SEVERE, "Failed to load audio library", ex);115 System.exit(1);116 return;117 }118 super.initInThread();120 ALCdevice device = AL.getDevice();122 // RLM: use reflection to grab the ID of our device for use later.123 try {124 Field deviceIDField;125 deviceIDField = ALCdevice.class.getDeclaredField("device");126 deviceIDField.setAccessible(true);127 try {deviceID = (Long)deviceIDField.get(device);}128 catch (IllegalArgumentException e) {e.printStackTrace();}129 catch (IllegalAccessException e) {e.printStackTrace();}130 deviceIDField.setAccessible(false);}131 catch (SecurityException e) {e.printStackTrace();}132 catch (NoSuchFieldException e) {e.printStackTrace();}134 this.audioSend = new AudioSend(this.deviceID);136 // The LWJGL context must be established as the master context before137 // any other listeners can be created on this device.138 audioSend.initDevice();139 // Now, everything is initialized, and it is safe to add more listeners.140 latch.countDown();141 }144 public void cleanup(){145 for(SoundProcessor sp : this.soundProcessorMap.values()){146 sp.cleanup();147 }148 super.cleanup();149 }151 public void updateAllListeners(){152 for (int i = 0; i < this.listeners.size(); i++){153 Listener lis = this.listeners.get(i);154 if (null != lis){155 Vector3f location = lis.getLocation();156 Vector3f velocity = lis.getVelocity();157 Vector3f orientation = lis.getUp();158 float gain = lis.getVolume();159 audioSend.setNthListener3f(AL10.AL_POSITION,160 location.x, location.y, location.z, i);161 audioSend.setNthListener3f(AL10.AL_VELOCITY,162 velocity.x, velocity.y, velocity.z, i);163 audioSend.setNthListener3f(AL10.AL_ORIENTATION,164 orientation.x, orientation.y, orientation.z, i);165 audioSend.setNthListenerf(AL10.AL_GAIN, gain, i);166 }167 }168 }172 private ByteBuffer buffer = BufferUtils.createByteBuffer(4096);178 public void dispatchAudio(float tpf){180 int samplesToGet = (int)(tpf * 44100);181 org.lwjgl.input.Mouse.setGrabbed(false);182 try {latch.await();}183 catch (InterruptedException e) {e.printStackTrace();}184 audioSend.step(samplesToGet);185 updateAllListeners();187 for (int i = 0; i < this.listeners.size(); i++){188 buffer.clear();189 audioSend.getSamples(buffer, samplesToGet, i);190 SoundProcessor sp =191 this.soundProcessorMap.get(this.listeners.get(i));192 if (null != sp){sp.process(buffer, outputFormat,193 samplesToGet*4);}194 }196 }198 public void update(float tpf){199 super.update(tpf);200 dispatchAudio(tpf);201 }203 }