/* * SplitterOp.java * (FScape) * * Copyright (c) 2001-2016 Hanns Holger Rutz. All rights reserved. * * This software is published under the GNU General Public License v3+ * * * For further information, please contact Hanns Holger Rutz at * contact@sciss.de */ package de.sciss.fscape.op; import de.sciss.fscape.gui.GroupLabel; import de.sciss.fscape.gui.OpIcon; import de.sciss.fscape.gui.PropertyGUI; import de.sciss.fscape.prop.OpPrefs; import de.sciss.fscape.prop.Prefs; import de.sciss.fscape.prop.Presets; import de.sciss.fscape.prop.PropertyArray; import de.sciss.fscape.spect.SpectFrame; import de.sciss.fscape.spect.SpectStream; import de.sciss.fscape.spect.SpectStreamSlot; import de.sciss.fscape.util.Constants; import de.sciss.fscape.util.Envelope; import de.sciss.fscape.util.Modulator; import de.sciss.fscape.util.Param; import de.sciss.fscape.util.Slots; import java.io.EOFException; import java.io.IOException; public class SplitterOp extends Operator { // -------- private variables -------- protected static final String defaultName = "Splitter"; // "real" name (z.B. Icons) protected static Presets static_presets = null; protected static Prefs static_prefs = null; protected static PropertyArray static_pr = null; protected static final int NUM_OUTPUT = 6; // Zahl der Slots // Slots protected static final int SLOT_INPUT = 0; protected static final int SLOT_OUTPUT = 1; // bis 6 // Properties (defaults) private static final int PR_CHANNELS = 0; // Array-Indices: pr.intg private static final int PR_NORMALIZE = 0; // pr.bool private static final int PR_GAINMOD = 1; // bis 6 private static final int PR_NORMGAIN = 0; // pr.para private static final int PR_GAIN = 1; // bis 6 private static final int PR_GAINMODDEPTH = 7; // bis 12 private static final int PR_GAINMODENV = 0; // pr.envl ; bis 5 private static final int PR_CHANNELS_ALL = 0; private static final int PR_CHANNELS_SINGLE = 1; private static final String PRN_CHANNELS = "Channels"; // Property-Keynames private static final String PRN_GAIN = "Gain"; // +0...5 private static final String PRN_GAINMOD = "GainMod"; private static final String PRN_GAINMODDEPTH = "GainModDepth"; private static final String PRN_GAINMODENV = "GainModEnv"; private static final String PRN_NORMALIZE = "Normalize"; private static final String PRN_NORMGAIN = "NormGain"; private static final int prIntg[] = { PR_CHANNELS_ALL }; private static final String prIntgName[] = { PRN_CHANNELS }; // -------- public methods -------- // public PropertyGUI createGUI( int type ); public SplitterOp() { super(); // initialize only in the first instance // preferences laden if( static_prefs == null ) { static_prefs = new OpPrefs( getClass(), getDefaultPrefs() ); } // propertyarray defaults if( static_pr == null ) { static_pr = new PropertyArray(); static_pr.intg = prIntg; static_pr.intgName = prIntgName; static_pr.bool = new boolean[ 1 + NUM_OUTPUT ]; static_pr.boolName = new String[ 1 + NUM_OUTPUT ]; static_pr.para = new Param[ 1 + (NUM_OUTPUT << 1) ]; static_pr.paraName = new String[ 1 + (NUM_OUTPUT << 1) ]; static_pr.envl = new Envelope[ NUM_OUTPUT ]; static_pr.envlName = new String[ NUM_OUTPUT ]; static_pr.bool[ PR_NORMALIZE ] = false; static_pr.boolName[ PR_NORMALIZE ] = PRN_NORMALIZE; static_pr.para[ PR_NORMGAIN ] = new Param( 0.0, Param.DECIBEL_AMP ); static_pr.paraName[ PR_NORMGAIN ] = PRN_NORMGAIN; for( int i = 0; i < NUM_OUTPUT; i++ ) { static_pr.para[ PR_GAIN + i ] = new Param( 0.0, Param.DECIBEL_AMP ); static_pr.para[ PR_GAINMODDEPTH + i ] = new Param( 96.0, Param.DECIBEL_AMP ); static_pr.paraName[ PR_GAIN + i ] = PRN_GAIN + (i+1); static_pr.paraName[ PR_GAINMODDEPTH + i ] = PRN_GAINMODDEPTH + (i+1); static_pr.envl[ PR_GAINMODENV + i ] = Envelope.createBasicEnvelope( Envelope.BASIC_TIME ); static_pr.envlName[ PR_GAINMODENV + i ] = PRN_GAINMODENV + (i+1); static_pr.bool[ PR_GAINMOD + i ] = false; static_pr.boolName[ PR_GAINMOD + i ] = PRN_GAINMOD + (i+1); } static_pr.superPr = Operator.op_static_pr; } // default preset if( static_presets == null ) { static_presets = new Presets( getClass(), static_pr.toProperties( true )); } // superclass-Felder uebertragen opName = "SplitterOp"; prefs = static_prefs; presets = static_presets; pr = (PropertyArray) static_pr.clone(); // slots slots.addElement( new SpectStreamSlot( this, Slots.SLOTS_READER )); // SLOT_INPUT for( int i = 0; i < NUM_OUTPUT; i++ ) { slots.addElement( new SpectStreamSlot( this, Slots.SLOTS_WRITER, Slots.SLOTS_DEFWRITER +(i+1) )); // SLOT_OUTPUT } // icon icon = new OpIcon( this, OpIcon.ID_SPLITTER, defaultName ); } // -------- Runnable methods -------- public void run() { runInit(); // superclass // main variables SpectStreamSlot runInSlot; SpectStreamSlot runOutSlot[] = new SpectStreamSlot[ NUM_OUTPUT ]; SpectStream runInStream = null; SpectStream runOutStream[] = new SpectStream[ NUM_OUTPUT ]; SpectFrame runInFr = null; SpectFrame runOutFr[] = new SpectFrame[ NUM_OUTPUT ]; int runSlotNum[] = new int[ NUM_OUTPUT ]; int runChanNum[] = new int[ NUM_OUTPUT ]; // basis of calculations Param ampRef = new Param( 1.0, Param.ABS_AMP ); // transform reference double normBase; Param gainBase[] = new Param[ NUM_OUTPUT ]; // modulations float gain[] = new float[ NUM_OUTPUT ]; Modulator gainMod[] = new Modulator[ NUM_OUTPUT ]; double sumGain; // others int numOut = 0; // number of used slots int oldWriteDone; int writeable; int chanNum; float srcData[]; // frame data of one channel float destData[]; topLevel: try { // ------------------------------ Input-Slot ------------------------------ runInSlot = slots.elementAt(SLOT_INPUT); if (runInSlot.getLinked() == null) { runStop(); // threadDead = true -> the following for() will be skipped } // this while loop is necessary, since during initReader a pause could be made // and the InterruptException is thrown; after that we retry for (boolean initDone = false; !initDone && !threadDead; ) { try { runInStream = runInSlot.getDescr(); // throws InterruptedException initDone = true; } catch (InterruptedException ignored) {} runCheckPause(); } if (threadDead) break topLevel; // ------------------------------ Output-Slots ------------------------------ for (int i = 0; i < NUM_OUTPUT; i++) { runOutSlot[numOut] = slots.elementAt(SLOT_OUTPUT + i); if (runOutSlot[numOut].getLinked() != null) { runOutStream[numOut] = new SpectStream(runInStream); if (pr.intg[PR_CHANNELS] == PR_CHANNELS_SINGLE) { runOutStream[numOut].setChannels(1); runChanNum[numOut] = numOut % runInStream.chanNum; } else { runChanNum[numOut] = 0; } runOutSlot[numOut].initWriter(runOutStream[numOut]); runSlotNum[numOut] = i; numOut++; } } // ------------------------------ preparatory calculation ------------------------------ normBase = Param.transform(pr.para[PR_NORMGAIN], Param.ABS_AMP, ampRef, runInStream).value; // create modulators for (int i = 0; i < numOut; i++) { gainBase[i] = Param.transform(pr.para[PR_GAIN + runSlotNum[i]], Param.ABS_AMP, ampRef, runInStream); if (pr.bool[PR_GAINMOD + runSlotNum[i]]) { gainMod[i] = new Modulator(gainBase[i], pr.para[PR_GAINMODDEPTH + runSlotNum[i]], pr.envl[PR_GAINMODENV + runSlotNum[i]], runInStream); } } if (pr.intg[PR_CHANNELS] == PR_CHANNELS_SINGLE) { chanNum = 1; } else { chanNum = runInStream.chanNum; } // ------------------------------ main loop ------------------------------ runSlotsReady(); mainLoop: while (!threadDead && (numOut > 0)) { // ---------- Process: (modulierte) Variablen ---------- // ---------- Frame einlesen ---------- for (boolean readDone = false; (!readDone) && !threadDead; ) { try { runInFr = runInSlot.readFrame(); // throws InterruptedException readDone = true; } catch (InterruptedException ignored) { } catch (EOFException e) { break mainLoop; } runCheckPause(); } if (threadDead) break mainLoop; // ---------- Process: calculate modulations ---------- sumGain = 0.0; for (int i = 0; i < numOut; i++) { if (pr.bool[PR_GAINMOD + runSlotNum[i]]) { gain[i] = (float) gainMod[i].calc().value; } else { gain[i] = (float) gainBase[i].value; } sumGain += gain[i]; } // normalize if (pr.bool[PR_NORMALIZE]) { for (int i = 0; i < numOut; i++) { gain[i] *= (float) (normBase / sumGain); } } // ---------- Process: calculate target frame ---------- //calcFrame: for (int i = 0; i < numOut; i++) { if ((Math.abs(gain[i] - 1.0f) < 1.0e-7f) && (pr.intg[PR_CHANNELS] == PR_CHANNELS_ALL)) { // 1:1 copy from InFrame runOutFr[i] = new SpectFrame(runInFr); gain[i] = 1.0f; // important for successor } else { // for( int j = 0; j < i; j++ ) { // evtl. koennen wir Vorgaenger kopieren? // // // that is, if there is the same gain and same channels // if( (Math.abs( gain[ i ] - gain[ j ]) < 0.01f) && // ((pr.intg[ PR_CHANNELS ] == PR_CHANNELS_ALL) || // ((i % runInStream.chanNum) == (j % runInStream.chanNum))) ) { // // runOutFr[ i ] = new SpectFrame( runOutFr[ j ]); // continue calcFrame; // } // } // possibly copy channels or set them to zero runOutFr[i] = runOutStream[i].allocFrame(); for (int ch = 0; ch < chanNum; ch++) { srcData = runInFr.data[runChanNum[i] + ch]; destData = runOutFr[i].data[ch]; if (gain[i] < 1.0e-7f) { // simply set to zero for (int j = 0; j < destData.length; j++) { destData[j] = 0.0f; } } else { System.arraycopy(srcData, 0, destData, 0, srcData.length); if (Math.abs(gain[i] - 1.0f) >= 1.0e-7f) { // we must compute for (int j = 0; j < destData.length; j += 2) { destData[j + SpectFrame.AMP] *= gain[i]; } } else { // 1:1 copy from channel is ok gain[i] = 1.0f; // (successor) } } } } } runFrameDone(runInSlot, runInFr); runInSlot.freeFrame(runInFr); // ---------- write frame ---------- for (int writeDone = 0; (writeDone < numOut) && !threadDead; ) { oldWriteDone = writeDone; for (int i = 0; i < numOut; i++) { try { // Unterbrechung if (runOutFr[i] != null) { // noch nicht geschrieben writeable = runOutStream[i].framesWriteable(); if (writeable > 0) { runOutSlot[i].writeFrame(runOutFr[i]); writeDone++; runOutStream[i].freeFrame(runOutFr[i]); runOutFr[i] = null; // erkennbar machen, dass wir damit fertig sind } else if (writeable < 0) { // Stream geschlossen writeDone++; runFrameDone(runOutSlot[i], runOutFr[i]); runOutStream[i].freeFrame(runOutFr[i]); // alles aufruecken for (int j = i + 1; j < numOut; j++) { runOutSlot[j] = runOutSlot[j - 1]; runOutStream[j] = runOutStream[j - 1]; runOutFr[j] = runOutFr[j - 1]; runSlotNum[j] = runSlotNum[j - 1]; runChanNum[j] = runChanNum[j - 1]; gainBase[j] = gainBase[j - 1]; gain[j] = gain[j - 1]; gainMod[j] = gainMod[j - 1]; } numOut--; i--; } } } catch (InterruptedException e) { break mainLoop; } runCheckPause(); } if (oldWriteDone == writeDone) { // nothing could be written try { Thread.sleep(500); // ...thus pause shortly } catch (InterruptedException ignored) {} // mainLoop will soon be automatically exited runCheckPause(); } } } // end of main loop runInStream.closeReader(); for (int i = 0; i < numOut; i++) { runOutStream[i].closeWriter(); } } // break topLevel catch (IOException e) { runQuit(e); return; } catch (SlotAlreadyConnectedException e) { runQuit(e); return; } // catch( OutOfMemoryException e ) { // abort( e ); // return; // } runQuit(null); } // -------- GUI methods -------- public PropertyGUI createGUI(int type) { PropertyGUI gui; StringBuffer gain = new StringBuffer(); StringBuffer gainMod = new StringBuffer(); if( type != GUI_PREFS ) return null; for( int i = 1; i <= NUM_OUTPUT; i++ ) { gain.append( "\nlbSlot "+i+";pf"+Constants.decibelAmpSpace+",id"+(i<<2)+ ",pr"+pr.paraName[ PR_GAIN + i-1 ] ); gainMod.append( "\ncbSlot "+i+" Gain,actrue|"+((i<<2)+1)+"|en|"+((i<<2)+2)+ "|en,acfalse|"+((i<<2)+1)+"|di|"+((i<<2)+2)+"|di,pr"+ (PRN_GAINMOD+i)+";pf"+Constants.decibelAmpSpace+",id"+((i<<2)+1)+ ",pr"+(PRN_GAINMODDEPTH+i)+";en,id"+((i<<2)+2)+",pr"+ (PRN_GAINMODENV+i) ); } gui = new PropertyGUI( "gl"+GroupLabel.NAME_GENERAL+"\n" + "lbEach slot carries;ch,pr"+PRN_CHANNELS+"," + "itAll channels," + "itOne channel\n" + "cbNormalize sum,actrue|100|en,acfalse|100|di,pr"+PRN_NORMALIZE+";"+ "pf"+Constants.decibelAmpSpace+",id100,pr"+PRN_NORMGAIN+"\n" + "glOutput slot gain" + gain + "\ngl"+GroupLabel.NAME_MODULATION + gainMod ); return gui; } }