/*
* SmearOp.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 SmearOp
extends Operator {
// -------- private variables --------
protected static final String defaultName = "Smear";
protected static Presets static_presets = null;
protected static Prefs static_prefs = null;
protected static PropertyArray static_pr = null;
// Slots
protected static final int SLOT_INPUT = 0;
protected static final int SLOT_OUTPUT = 1;
// Properties (defaults)
private static final int PR_DECAYMOD = 0; // pr.bool
private static final int PR_MODE = 0; // pr.intg
private static final int PR_DRYAPPLY = 1;
private static final int PR_DECAY = 0; // pr.para
private static final int PR_DECAYMODDEPTH = 1;
private static final int PR_DECAYMODENV = 0; // pr.envl
private static final int PR_MODE_SMEAR = 0; // "Nachleuchten"
private static final int PR_MODE_FREEZE = 1; // akkumulierter Durchschnitt (=> "Spektrum")
private static final int PR_APPLY_NONE = 0;
private static final int PR_APPLY_SUB = 1;
private static final int PR_APPLY_THRESH = 2; // never inverts phase
private static final String PRN_DECAYMOD = "DecayMod";
private static final String PRN_MODE = "Mode";
private static final String PRN_DRYAPPLY = "LoDepth";
private static final String PRN_DECAY = "Decay";
private static final String PRN_DECAYMODDEPTH = "DecayModDepth";
private static final String PRN_DECAYMODENV = "DecayModEnv";
private static final boolean prBool[] = { false };
private static final String prBoolName[] = { PRN_DECAYMOD };
private static final int prIntg[] = { PR_MODE_SMEAR, PR_APPLY_NONE };
private static final String prIntgName[] = { PRN_MODE, PRN_DRYAPPLY };
private static final Param prPara[] = { null, null };
private static final String prParaName[] = { PRN_DECAY, PRN_DECAYMODDEPTH };
private static final Envelope prEnvl[] = { null };
private static final String prEnvlName[] = { PRN_DECAYMODENV };
// -------- public methods --------
// public Container createGUI( int type );
public SmearOp()
{
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.bool = prBool;
static_pr.boolName = prBoolName;
static_pr.intg = prIntg;
static_pr.intgName = prIntgName;
static_pr.para = prPara;
static_pr.para[ PR_DECAY ] = new Param( 60, Param.DECIBEL_AMP );
static_pr.para[ PR_DECAYMODDEPTH ] = new Param( 60, Param.DECIBEL_AMP );
static_pr.paraName = prParaName;
static_pr.envl = prEnvl;
static_pr.envl[ PR_DECAYMODENV ] = Envelope.createBasicEnvelope( Envelope.BASIC_TIME );
static_pr.envlName = prEnvlName;
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 = "SmearOp";
prefs = static_prefs;
presets = static_presets;
pr = (PropertyArray) static_pr.clone();
// slots
slots.addElement( new SpectStreamSlot( this, Slots.SLOTS_READER )); // SLOT_INPUT
slots.addElement( new SpectStreamSlot( this, Slots.SLOTS_WRITER )); // SLOT_OUTPUT
// icon
icon = new OpIcon( this, OpIcon.ID_SMEAR, defaultName );
}
// -------- Runnable methods --------
public void run()
{
runInit(); // superclass
// Haupt-Variablen fuer den Prozess
SpectStreamSlot runInSlot;
SpectStreamSlot runOutSlot;
SpectStream runInStream = null;
SpectStream runOutStream;
SpectFrame runInFr = null;
SpectFrame runOutFr = null;
SpectFrame bufFr;
// Berechnungs-Grundlagen
Param ampRef = new Param( 1.0, Param.ABS_AMP ); // transform-Referenz
Param decayBase;
// Modulations-Variablen
Param decay;
Modulator decayMod = null;
// Ziel-Frame Berechnung
float srcAmp, srcPhase;
float srcAmp2, srcPhase2;
double destReal, destImg;
int divisor = 0;
topLevel:
try {
// ------------------------------ Input-Slot ------------------------------
runInSlot = slots.elementAt( SLOT_INPUT );
if( runInSlot.getLinked() == null ) {
runStop(); // threadDead = true -> folgendes for() wird uebersprungen
}
// diese while Schleife ist noetig, da beim initReader ein Pause eingelegt werden kann
// und die InterruptException ausgeloest wird; danach versuchen wir es erneut
for( boolean initDone = false; !initDone && !threadDead; ) {
try {
runInStream = runInSlot.getDescr(); // throws InterruptedException
initDone = true;
}
catch( InterruptedException ignored) {}
runCheckPause();
}
if( threadDead ) break topLevel;
// ------------------------------ Output-Slot ------------------------------
runOutSlot = slots.elementAt( SLOT_OUTPUT );
runOutStream = new SpectStream( runInStream );
runOutSlot.initWriter( runOutStream );
// ------------------------------ Vorberechnungen ------------------------------
// constants
decayBase = Param.transform( new Param( pr.para[ PR_DECAY ].value * SpectStream.framesToMillis(
runInStream, 1 ) / 1000, pr.para[ PR_DECAY].unit ),
Param.ABS_AMP, ampRef, runInStream );
decay = decayBase; // zunaechst unmoduliert
// Modulatoren erzeugen
if( pr.bool[ PR_DECAYMOD ]) {
decayMod = new Modulator( decayBase, pr.para[ PR_DECAYMODDEPTH ],
pr.envl[ PR_DECAYMODENV ], runInStream );
}
// leeres Buffer-Frame
bufFr = new SpectFrame( runInStream.chanNum, runInStream.bands );
SpectFrame.clear( bufFr );
// ------------------------------ Hauptschleife ------------------------------
runSlotsReady();
mainLoop: while( !threadDead ) {
// ---------- Process: (modulierte) Variablen ----------
if( pr.bool[ PR_DECAYMOD ]) {
decay = decayMod.calc();
}
// ---------- 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: Ziel-Frame berechnen ----------
switch( pr.intg[ PR_MODE ]) {
case PR_MODE_SMEAR: // ---------- Smear-Modus ----------
switch( pr.intg[ PR_DRYAPPLY ]) {
case PR_APPLY_NONE:
for( int ch = 0; ch < runOutStream.chanNum; ch++ ) {
for( int band = 0; band < runOutStream.bands; band++ ) {
srcAmp = bufFr.data[ ch ][ (band << 1) + SpectFrame.AMP ] / (float) decay.value;
srcPhase = bufFr.data[ ch ][ (band << 1) + SpectFrame.PHASE ];
destImg = srcAmp * Math.sin( srcPhase );
destReal = srcAmp * Math.cos( srcPhase );
srcAmp = runInFr.data[ ch ][ (band << 1) + SpectFrame.AMP ];
srcPhase = runInFr.data[ ch ][ (band << 1) + SpectFrame.PHASE ];
destImg += srcAmp * Math.sin( srcPhase );
destReal += srcAmp * Math.cos( srcPhase );
bufFr.data[ ch ][ (band << 1) + SpectFrame.AMP ] =
(float) Math.sqrt( destImg*destImg + destReal*destReal );
bufFr.data[ ch ][ (band << 1) + SpectFrame.PHASE ] =
(float) Math.atan2( destImg, destReal );
}
}
runOutFr = new SpectFrame( bufFr );
break;
case PR_APPLY_SUB:
runOutFr = runOutStream.allocFrame();
for( int ch = 0; ch < runOutStream.chanNum; ch++ ) {
for( int band = 0; band < runOutStream.bands; band++ ) {
srcAmp = bufFr.data[ ch ][ (band << 1) + SpectFrame.AMP ] / (float) decay.value;
srcPhase = bufFr.data[ ch ][ (band << 1) + SpectFrame.PHASE ];
runOutFr.data[ ch ][ (band << 1) + SpectFrame.AMP ] = srcAmp;
runOutFr.data[ ch ][ (band << 1) + SpectFrame.PHASE ] = srcPhase;
destImg = srcAmp * Math.sin( srcPhase );
destReal = srcAmp * Math.cos( srcPhase );
srcAmp = runInFr.data[ ch ][ (band << 1) + SpectFrame.AMP ];
srcPhase = runInFr.data[ ch ][ (band << 1) + SpectFrame.PHASE ];
destImg += srcAmp * Math.sin( srcPhase );
destReal += srcAmp * Math.cos( srcPhase );
bufFr.data[ ch ][ (band << 1) + SpectFrame.AMP ] =
(float) Math.sqrt( destImg*destImg + destReal*destReal );
bufFr.data[ ch ][ (band << 1) + SpectFrame.PHASE ] =
(float) Math.atan2( destImg, destReal );
}
}
break;
case PR_APPLY_THRESH:
runOutFr = runOutStream.allocFrame();
for( int ch = 0; ch < runOutStream.chanNum; ch++ ) {
for( int band = 0; band < runOutStream.bands; band++ ) {
srcAmp = bufFr.data[ ch ][ (band << 1) + SpectFrame.AMP ] / (float) decay.value;
srcPhase = bufFr.data[ ch ][ (band << 1) + SpectFrame.PHASE ];
destImg = srcAmp * Math.sin( srcPhase );
destReal = srcAmp * Math.cos( srcPhase );
srcAmp2 = runInFr.data[ ch ][ (band << 1) + SpectFrame.AMP ];
srcPhase2 = runInFr.data[ ch ][ (band << 1) + SpectFrame.PHASE ];
destImg += srcAmp2 * Math.sin( srcPhase2 );
destReal += srcAmp2 * Math.cos( srcPhase2 );
bufFr.data[ ch ][ (band << 1) + SpectFrame.AMP ] =
(float) Math.sqrt( destImg*destImg + destReal*destReal );
bufFr.data[ ch ][ (band << 1) + SpectFrame.PHASE ] =
(float) Math.atan2( destImg, destReal );
runOutFr.data[ ch ][ (band << 1) + SpectFrame.AMP ] = Math.max( 0.0f,
srcAmp - srcAmp2 );
runOutFr.data[ ch ][ (band << 1) + SpectFrame.PHASE ] = srcPhase;
}
}
break;
}
break;
case PR_MODE_FREEZE: // ---------- Freeze-Modus ----------
divisor++;
runOutFr = runOutStream.allocFrame();
switch( pr.intg[ PR_DRYAPPLY ]) {
case PR_APPLY_NONE:
for( int ch = 0; ch < runOutStream.chanNum; ch++ ) {
for( int band = 0; band < runOutStream.bands; band++ ) {
srcAmp = runInFr.data[ ch ][ (band << 1) + SpectFrame.AMP ];
srcPhase = runInFr.data[ ch ][ (band << 1) + SpectFrame.PHASE ];
bufFr.data[ ch ][ band << 1 ] += srcAmp * Math.sin( srcPhase );
bufFr.data[ ch ][ (band << 1) + 1 ] += srcAmp * Math.cos( srcPhase );
destImg = bufFr.data[ ch ][ band << 1 ] / divisor;
destReal = bufFr.data[ ch ][ (band << 1) + 1 ] / divisor;
runOutFr.data[ ch ][ (band << 1) + SpectFrame.AMP ] =
(float) Math.sqrt( destImg*destImg + destReal*destReal );
runOutFr.data[ ch ][ (band << 1) + SpectFrame.PHASE ] =
(float) Math.atan2( destImg, destReal );
}
}
break;
case PR_APPLY_SUB:
for( int ch = 0; ch < runOutStream.chanNum; ch++ ) {
for( int band = 0; band < runOutStream.bands; band++ ) {
srcAmp = runInFr.data[ ch ][ (band << 1) + SpectFrame.AMP ];
srcPhase = runInFr.data[ ch ][ (band << 1) + SpectFrame.PHASE ];
destImg = srcAmp * Math.sin( srcPhase );
destReal = srcAmp * Math.cos( srcPhase );
bufFr.data[ ch ][ band << 1 ] += destImg;
bufFr.data[ ch ][ (band << 1) + 1 ] += destReal;
destImg -= bufFr.data[ ch ][ band << 1 ] / divisor;
destReal -= bufFr.data[ ch ][ (band << 1) + 1 ] / divisor;
runOutFr.data[ ch ][ (band << 1) + SpectFrame.AMP ] =
(float) Math.sqrt( destImg*destImg + destReal*destReal );
runOutFr.data[ ch ][ (band << 1) + SpectFrame.PHASE ] =
(float) Math.atan2( destImg, destReal );
}
}
break;
case PR_APPLY_THRESH:
for( int ch = 0; ch < runOutStream.chanNum; ch++ ) {
for( int band = 0; band < runOutStream.bands; band++ ) {
srcAmp = runInFr.data[ ch ][ (band << 1) + SpectFrame.AMP ];
srcPhase = runInFr.data[ ch ][ (band << 1) + SpectFrame.PHASE ];
bufFr.data[ ch ][ band << 1 ] += srcAmp * Math.sin( srcPhase );
bufFr.data[ ch ][ (band << 1) + 1 ] += srcAmp * Math.cos( srcPhase );
destImg = bufFr.data[ ch ][ band << 1 ] / divisor;
destReal = bufFr.data[ ch ][ (band << 1) + 1 ] / divisor;
runOutFr.data[ ch ][ (band << 1) + SpectFrame.AMP ] = Math.max( 0.0f,
srcAmp - (float) Math.sqrt( destImg*destImg + destReal*destReal ));
runOutFr.data[ ch ][ (band << 1) + SpectFrame.PHASE ] = srcPhase;
}
}
break;
}
break;
}
// calculation done
runInSlot.freeFrame( runInFr );
for( boolean writeDone = false; (!writeDone) && !threadDead; ) {
try { // Unterbrechung
runOutSlot.writeFrame( runOutFr ); // throws InterruptedException
writeDone = true;
runFrameDone( runOutSlot, runOutFr );
runOutStream.freeFrame( runOutFr );
}
catch( InterruptedException ignored) {} // mainLoop wird eh gleich verlassen
runCheckPause();
}
} // end of main loop
runInStream.closeReader();
runOutStream.closeWriter();
} // break topLevel
catch( IOException e ) {
runQuit( e );
return;
}
catch( SlotAlreadyConnectedException e ) {
runQuit( e );
return;
}
// catch( OutOfMemoryError e ) {
// abort( e );
// return;
// }
runQuit( null );
}
// -------- GUI methods --------
public PropertyGUI createGUI( int type )
{
PropertyGUI gui;
if( type != GUI_PREFS ) return null;
gui = new PropertyGUI(
"gl"+GroupLabel.NAME_GENERAL+"\n" +
"lbMode;ch,ac0|1|en|2|en,ac1|1|di|2|di,pr"+PRN_MODE+"," +
"itSmear," +
"itFreeze\n" +
"lbDecay per sec;pf"+Constants.decibelAmpSpace+",id1,pr"+PRN_DECAY+"\n" +
"lbDry application;ch,pr"+PRN_DRYAPPLY+"," +
"itNone," +
"itSubtract," +
"itThreshold\n" +
"gl"+GroupLabel.NAME_MODULATION+"\n" +
"cbDecay,actrue|3|en|4|en,acfalse|3|di|4|di,id2,pr"+PRN_DECAYMOD+";" +
"pf"+Constants.decibelAmpSpace+"|"+Constants.offsetAmpSpace+",re1,id3,pr"+PRN_DECAYMODDEPTH+
";en,id4,pr"+PRN_DECAYMODENV );
return gui;
}
}