package jass.render; import javax.sound.sampled.*; /** Utility class to read/write audio in real-time using native libs from RtAudio C++ classes @author Kees van den Doel (kvdoel@cs.ubc.ca) */ public class RTAudioFullDuplexRtAudio extends Thread { private float srate; private int bitsPerFrame; private int nchannels; private boolean signed; private static final boolean bigEndian = false; private long nativeObjectPointer = 0; private int buffersizeJASS; private float[] readBuffer; /** Constructor. Uses native audio write. Also specify RtAudio tweak parameter numberofbuffers used Needs librtaudio.so (LINUX) or rtaudio.dll (Windows) @param srate sampling rate in Hertz. @param nchannels number of audio channels. @param buffersizeJass jass buffersize @param numRtAudioBuffers number of rtaudio buffers (0 is lowest) */ public RTAudioFullDuplexRtAudio(float srate,int nchannels,int buffersizeJASS,int numRtAudioBuffers) { // load shared library with native sound implementations try { System.loadLibrary("rtaudio"); } catch(UnsatisfiedLinkError e) { System.out.println("Could not load shared library rtaudio: "+e); } readBuffer = new float[buffersizeJASS]; this.buffersizeJASS = buffersizeJASS; initAudioNative(srate,nchannels,buffersizeJASS,numRtAudioBuffers); } /** Initialize native sound using RtAudio, setting buffersize and an internal RtAudio buffersize @param nchannels number of audio channels. @param srate sampling rate in Hertz. @param buffersizeJASS buffers will be rendered in these chunks @param nRtaudioBuffers internal buffers, 0 = lowest possiblse @return long representing C++ object pointer */ public native long initNativeSound(int nchannels,int srate,int buffersizeJASS, int nRtaudioBuffers); /** Close native sound This is a native method and needs librtaudio.so (LINUX) or rtaudio.dll (Windows) or whatever its called on MAC OSX @param nativeObjectPointer representing C++ object pointer */ public native void closeNativeSound(long nativeObjectPointer); /** write/read a buffer of floats to native sound. This is a native method and needs librtaudio.so (LINUX) or rtaudio.dll (Windows) @param nativeObjectPointer representing C++ object pointer. @param outbuf array of floats with sound buffer to be output to audio card @param outbuflen length of buffer. @param readbuf array of floats with sound buffer to be read from audio input @param readbuflen length of read buffer. */ public native void writeReadNativeSoundFloat(long nativeObjectPointer,float[] outbuf,int outbuflen,float[] readbuf, int readbuflen); private void initAudioNative(float srate,int nchannels,int buffersizeJASS,int numRtAudioBuffers) { this.srate = srate; this.bitsPerFrame = bitsPerFrame; this.nchannels = nchannels; this.signed = signed; // get pointer to C++ object nativeObjectPointer = initNativeSound(nchannels,(int)srate,buffersizeJASS,numRtAudioBuffers); //This will ensure that close() gets called before the program exits Runtime.getRuntime().addShutdownHook(new Thread() { public void run() { System.out.println("The shutdown hook in RTAUdioFullDuplex is executing"); close(); try{ sleep(100); } catch(Exception e) { System.out.print("The sleep function is broken"); } } }); } /** Read audio buffer from input queue and block if queue is empty. @param y buffer to write to. @param nsamples number of samples required. */ public void read(float [] y,int nsamples) { if(nsamples > buffersizeJASS) { nsamples = buffersizeJASS; } // readBuffer is kept up to dat by SourcePlayer thread, caches is always previous result // of tick()ing RtAudio object for(int i=0;i<nsamples;i++) { y[i] = readBuffer[i]; } } /** Write audio buffer to output queue and block if queue is full. @param y output buffer. */ public void write(float [] y) { writeReadNativeSoundFloat(nativeObjectPointer,y,y.length,readBuffer,readBuffer.length); } //** Close resources */ public void close() { System.out.println("RTAudioFullDuplexRtAudio.close() will call closeNativeSound: nativeObjectPointer= "+nativeObjectPointer); if(nativeObjectPointer != 0) { closeNativeSound(nativeObjectPointer); nativeObjectPointer = 0; } try{ sleep(100); } catch(Exception e) { } } }