/*
* EnvOp.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.FIRDesignerDlg;
import de.sciss.fscape.gui.FilterBox;
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.Fourier;
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.Filter;
import de.sciss.fscape.util.Param;
import de.sciss.fscape.util.Slots;
import de.sciss.fscape.util.Util;
import de.sciss.io.AudioFileDescr;
import java.awt.*;
import java.io.EOFException;
import java.io.IOException;
public class EnvOp
extends Operator {
// -------- private variables --------
protected static final String defaultName = "Envelope";
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_GAIN = 0; // pr.para
private static final int PR_INVERT = 0; // pr.bool
private static final String PRN_GAIN = "Gain";
private static final String PRN_INVERT = "Invert";
private static final Param prPara[] = { null };
private static final String prParaName[] = { PRN_GAIN };
private static final boolean prBool[] = { false };
private static final String prBoolName[] = { PRN_INVERT };
// -------- public methods --------
// public Container createGUI( int type );
public EnvOp()
{
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.para = prPara;
static_pr.para[ PR_GAIN ] = new Param( 0.0, Param.DECIBEL_AMP );
static_pr.paraName = prParaName;
static_pr.bool = prBool;
static_pr.boolName = prBoolName;
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 = "EnvOp";
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 // XXX
icon = new OpIcon( this, OpIcon.ID_FLIPFREQ, defaultName );
}
// -------- Runnable methods --------
public void run()
{
runInit(); // superclass
// Haupt-Variablen fuer den Prozess
int ch, i, j;
double d1, d2;
SpectStreamSlot runInSlot;
SpectStreamSlot runOutSlot;
SpectStream runInStream = null;
SpectStream runOutStream;
SpectFrame runInFr = null;
SpectFrame runOutFr = null;
Param ampRef = new Param( 1.0, Param.ABS_AMP ); // transform-Referenz
float gain;
// Ziel-Frame Berechnung
int srcBands;
float[] fftBuf1, fftBuf2, convBuf1;
double fltFreq, fltShift;
int fftLength, skip;
AudioFileDescr tmpStream;
FilterBox fltBox;
Point fltLength;
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;
srcBands = runInStream.bands;
// ------------------------------ Output-Slot ------------------------------
runOutSlot = slots.elementAt( SLOT_OUTPUT );
runOutStream = new SpectStream( runInStream );
runOutSlot.initWriter( runOutStream );
// ------------------------------ Vorberechnungen ------------------------------
gain = 2 * (float) Param.transform( pr.para[ PR_GAIN ], Param.ABS_AMP, ampRef, runInStream ).value;
fltFreq = runInStream.smpRate * 0.245; // account for lp transition band
fltShift = Constants.PI2 * 0.245; // normalized freq ready for Math.cos/sin
fltBox = new FilterBox();
fltBox.filterType= FilterBox.FLT_LOWPASS;
fltBox.cutOff = new Param( fltFreq, Param.ABS_HZ );
tmpStream = new AudioFileDescr(); // unflexibel
tmpStream.rate = runInStream.smpRate;
fltLength = fltBox.calcLength( tmpStream, FIRDesignerDlg.QUAL_VERYGOOD );
skip = fltLength.x; // support not written out
i = fltLength.x + fltLength.y;
j = i + srcBands - 1; // length after conv. with input
for( fftLength = 2; fftLength < j; fftLength <<= 1 ) ;
fftBuf1 = new float[ fftLength << 1 ]; // *2 = complex
fftBuf2 = new float[ fftLength << 1 ];
Util.clear( fftBuf1 );
// we design a half-nyquist lp filter and shift it up by that freq. to have a real=>analytic filter
// see comp.dsp algorithms-faq Q2.10
fltBox.calcIR( tmpStream, FIRDesignerDlg.QUAL_VERYGOOD, Filter.WIN_BLACKMAN, fftBuf1, fltLength );
// ---- real=>complex + modulation with exp(ismpRate/4 +/- (?) antialias) ----
for( i = fftLength - 1, j = fftBuf1.length - 1; i >= 0; i-- ) {
d1 = -fltShift * i;
fftBuf1[ j-- ] = (float) (fftBuf1[ i ] * Math.sin( d1 )); // img
fftBuf1[ j-- ] = (float) (fftBuf1[ i ] * Math.cos( d1 )); // real
}
Fourier.complexTransform( fftBuf1, fftLength, Fourier.FORWARD );
// ------------------------------ Hauptschleife ------------------------------
runSlotsReady();
mainLoop: while( !threadDead ) {
// ---------- Frame einlesen ----------
for( boolean readDone = false; (!readDone) && !threadDead; ) {
try {
runInFr = runInSlot.readFrame(); // throws InterruptedException
readDone = true;
runOutFr = runOutStream.allocFrame();
}
catch( InterruptedException ignored) {}
catch( EOFException e ) {
break mainLoop;
}
runCheckPause();
}
if( threadDead ) break mainLoop;
// ---------- Process: Ziel-Frame berechnen ----------
for( ch = 0; ch < runOutStream.chanNum; ch++ ) {
convBuf1 = runInFr.data[ ch ];
Fourier.polar2Rect( convBuf1, 0, fftBuf2, 0, srcBands << 1 );
for( i = srcBands << 1; i < fftBuf2.length; i++ ) { // zero pad
fftBuf2[ i ] = 0.0f;
}
Fourier.complexTransform( fftBuf2, fftLength, Fourier.FORWARD );
Fourier.complexMult( fftBuf1, 0, fftBuf2, 0, fftBuf2, 0, fftLength << 1 );
Fourier.complexTransform( fftBuf2, fftLength, Fourier.INVERSE );
convBuf1 = runOutFr.data[ ch ];
for( i = 0, j = (skip << 1); i < (srcBands << 1); ) {
d1 = fftBuf2[ j++ ];
d2 = fftBuf2[ j++ ];
convBuf1[ i++ ] = gain * (float) Math.sqrt( d1*d1 + d2*d2 ); // env.amp
convBuf1[ i++ ] = 0.0f; // zero phase
}
if( pr.bool[ PR_INVERT ]) {
for( i = 0, j = 0; i < srcBands; i++, j += 2 ) {
convBuf1[ j ] = 1.0f - convBuf1[ j ];
}
}
}
// 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+"\ncbInvert,pr"+PRN_INVERT+
"\nlbGain;pf"+Constants.decibelAmpSpace+",pr"+pr.paraName[ PR_GAIN ]);
return gui;
}
}