package jass.engine; import java.util.*; /** Mixer that runs a thread on each input. Add sources, then call init() method, then you can use it as a normal mixer. Unlike other UG's you can't add or remove sources while running. @author Kees van den Doel (kvdoel@cs.ubc.ca) */ public final class ThreadMixer extends Out implements Sink { private float[] gains; // gains of sources private float[] tmp_buf; // scratchpad private Vector<Source> sourceContainer; public float[][] srcBuffers; // Array of buffers of the sources private boolean isInitted = false; private Source [] src; // array of sources private int nSourcesDone; private SourceThread[] sourceThreads; private Object lock; public synchronized void signalDone() { D.prt("Enter signalDone"); nSourcesDone++; notifyAll(); D.prt("Exit signalDone"); } private int getNSourcesDone() { return nSourcesDone; } private void clearNSourcesDone() { nSourcesDone = 0; } /** Set input gain control vector. @param k index of gain @param gains input gain */ public void setGain(int k, float g) { if(isInitted) { if(k<0 || k >= gains.length) { return; } else { gains[k] = g; } } else { System.out.println("ThreadMixer.setGain called before init(), ignored "); } } /** Get input gain control vector. @param gains input gains */ public float[] getGains() { return gains; } /** add source to Sink. @param s Source to add. @return object representing Source in Sink (may be null). */ public synchronized Object addSource(Source s) throws SinkIsFullException { if(isInitted) { throw new SinkIsFullException(); } sourceContainer.addElement(s); s.setTime(getTime()); return null; } public synchronized void removeSource(Source s) { if(!isInitted) { sourceContainer.removeElement(s); } } /** Get array of sources. @return array of the Sources, null if there are none. */ public Source [] getSources() { return src; } public ThreadMixer(int bufferSize) { super(bufferSize); sourceContainer = new Vector<Source>(); srcBuffers = new float[1][]; } public void init() { isInitted = true; src = sourceContainer.toArray(new Source[0]); int n = src.length; // number of sources srcBuffers = new float[n][]; tmp_buf = new float[bufferSize]; gains = new float[n]; sourceThreads = new SourceThread[n]; lock = new Object(); startThreads(); } private void startThreads() { int n = src.length; clearNSourcesDone(); try { for(int i=0;i<n;i++) { sourceThreads[i] = new SourceThread(this,src[i],i,lock); sourceThreads[i].start(); } } catch(Exception e) { System.out.println("ThreadMixer.startThreads: "+this+" "+e); } } /** Call all the sources and cache their returned buffers. */ private final void callSources() { int n = srcBuffers.length; // number of source buffers allocated clearNSourcesDone(); for(int i=0;i<n;i++) { D.prt("callSources going to wake up thread "+i); sourceThreads[i].wakeUp(getTime()); D.prt("callSources has woken up thread "+i); } while(nSourcesDone < n) { try { D.prt("main waiting (" +nSourcesDone+" sources done)"); wait(); } catch(InterruptedException e) {} } D.prt("main done waiting ("+nSourcesDone+" sources done)"); } /** Get buffer with frame index t. Return old buffer if have it in cache. Compute next buffer and advance time if requested, throw exception if requested buffer lies in the past or future. This method will be called "behind the scenes" when processing filtergraphs. @param t timestamp of buffer = frame index. */ public final synchronized float[] getBuffer(long t) throws BufferNotAvailableException { if(!isInitted) { init(); } D.prt("getBufferCalled "); if(t == (getTime()+1)) { // requested next buffer setTime(t); callSources(); computeBuffer(); // use cached source buffers to compute buf. } else if(t != getTime()) { // neither current or next buffer requested: deny request System.out.println("Error! "+this+" Out.java: t="+t+" currentTime="+getTime()); throw new BufferNotAvailableException(); } // return new or old buffer: return buf; } /** Reset time of self and all inputs @param t time to reset to. Patch must be in a state s.t. none of the current times == t */ public synchronized void resetTime(long t) { setTime(t); resetTimeSources(t); } /** Call all the sources and reset time */ private final void resetTimeSources(long t) { Object [] src = getSources(); int n = src.length; // number of sources for(int i=0;i<n;i++) { if(src[i] instanceof Out) { if(((Out)src[i]).getTime() != t) { ((Out)src[i]).resetTime(t); } } } } /** Compute the next buffer and store in member float[] buf. */ protected final void computeBuffer() { int bufsz = getBufferSize(); int nsrc = sourceContainer.size(); if(nsrc > gains.length) { nsrc = gains.length; System.out.println("Warning: ThreadMixer has more sources than allowed"); } for(int k=0;k<bufsz;k++) { // can't overwrite buf[] yet as one of the srcBuffers may be pointing to it! tmp_buf[k] = 0; } for(int i=0;i<nsrc;i++) { float[] tmpsrc = srcBuffers[i]; //System.out.println("i= "+i+ "src[] = " + srcBuffers[i][5]); float g = gains[i]; for(int k=0;k<bufsz;k++) { tmp_buf[k] += g*tmpsrc[k]; } } for(int k=0;k<bufsz;k++) { buf[k] = tmp_buf[k]; } D.prt("Compute buffer"); } } class SourceThread extends Thread { private ThreadMixer tm; private Source s; private int index; private long localtime; private Object lock; public SourceThread(ThreadMixer tm,Source s,int index,Object lock) { this.tm = tm; this.s = s; this.index = index; this.localtime = 0; this.lock = lock; } public synchronized void wakeUp(long t) { D.prt("Enter wakeUp"); localtime = t; //D.prt("thread "+index+ " wakeUp" +" t="+s.getTime()+" tsys="+tm.getTime()); notifyAll(); D.prt("Exit wakeUp"); } private synchronized void process() { while(true) { D.prt("another loop in process starts"); D.prt("source time = "+s.getTime()+" localtime= "+localtime); while(s.getTime() == localtime) { D.prt("process got here"); try { D.prt("thread "+index+ " waiting"+ " t= "+s.getTime()+" tsys="+localtime); wait(); } catch(InterruptedException e) {} } D.prt("thread "+index+ " done waiting"); try { tm.srcBuffers[index] = s.getBuffer(localtime); tm.signalDone(); D.prt("thread "+index+" done getting buffer"); } catch(Exception e) { System.out.println("SourceThread: "+this+" "+e); } } } public void run() { process(); } } class D { public static void prt(String s) { System.out.println(s); } }