/*
* VoocooderDlg.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.gui;
import de.sciss.fscape.io.FloatFile;
import de.sciss.fscape.io.GenericFile;
import de.sciss.fscape.prop.Presets;
import de.sciss.fscape.prop.PropertyArray;
import de.sciss.fscape.session.ModulePanel;
import de.sciss.fscape.spect.Fourier;
import de.sciss.fscape.util.Constants;
import de.sciss.fscape.util.Filter;
import de.sciss.fscape.util.Param;
import de.sciss.fscape.util.ParamSpace;
import de.sciss.fscape.util.Util;
import de.sciss.io.AudioFile;
import de.sciss.io.AudioFileDescr;
import de.sciss.io.IOUtil;
import javax.swing.*;
import java.awt.*;
import java.io.EOFException;
import java.io.File;
import java.io.IOException;
/**
* Processing module for multiband combination of two sounds.
*/
public class VoocooderDlg
extends ModulePanel {
// -------- private variables --------
// Properties (defaults)
private static final int PR_INPUTFILE = 0; // pr.text
private static final int PR_MODFILE = 1;
private static final int PR_OUTPUTFILE = 2;
private static final int PR_OUTPUTTYPE = 0; // pr.intg
private static final int PR_OUTPUTRES = 1;
private static final int PR_GAINTYPE = 2;
private static final int PR_FILTERLEN = 3;
private static final int PR_KOMBINATION = 4;
private static final int PR_GAIN = 0; // pr.para
private static final int PR_LOFREQ = 1;
private static final int PR_HIFREQ = 2;
private static final int PR_DRYMIX = 3;
private static final int PR_WETMIX = 4;
private static final int PR_ROLLOFF = 5;
private static final int PR_BANDSPEROCT = 6;
private static final int FLT_SHORT = 0;
// private static final int FLT_MEDIUM = 1;
// private static final int FLT_LONG = 2;
// private static final int FLT_VERYLONG = 3;
private static final int KMB_MULTIPLY = 0;
private static final int KMB_MODULO = 1;
private static final int KMB_MIN = 2;
private static final int KMB_MAX = 3;
private static final int KMB_VOCODER = 4;
private static final String[] KMB_NAMES = { "Multiply", "Modulo", "Min", "Max", "Vocoder" };
private static final String PRN_INPUTFILE = "InputFile";
private static final String PRN_MODFILE = "ModFile";
private static final String PRN_OUTPUTFILE = "OutputFile";
private static final String PRN_OUTPUTTYPE = "OutputType";
private static final String PRN_OUTPUTRES = "OutputReso";
private static final String PRN_FILTERLEN = "FilterLen";
private static final String PRN_KOMBINATION = "Kombi";
private static final String PRN_LOFREQ = "LoFreq";
private static final String PRN_HIFREQ = "HiFreq";
private static final String PRN_DRYMIX = "DryMix";
private static final String PRN_WETMIX = "WetMix";
private static final String PRN_ROLLOFF = "RollOff";
private static final String PRN_BANDSPEROCT = "BandsPerOct";
private static final String prText[] = { "", "", "" };
private static final String prTextName[] = { PRN_INPUTFILE, PRN_MODFILE, PRN_OUTPUTFILE };
private static final int prIntg[] = { 0, 0, 0, FLT_SHORT, KMB_MULTIPLY };
private static final String prIntgName[] = { PRN_OUTPUTTYPE, PRN_OUTPUTRES, PRN_GAINTYPE,
PRN_FILTERLEN, PRN_KOMBINATION };
private static final Param prPara[] = new Param[ 7 ];
private static final String prParaName[] = { PRN_GAIN, PRN_LOFREQ, PRN_HIFREQ,
PRN_DRYMIX, PRN_WETMIX, PRN_ROLLOFF, PRN_BANDSPEROCT };
private static final int GG_INPUTFILE = GG_OFF_PATHFIELD + PR_INPUTFILE;
private static final int GG_MODFILE = GG_OFF_PATHFIELD + PR_MODFILE;
private static final int GG_OUTPUTFILE = GG_OFF_PATHFIELD + PR_OUTPUTFILE;
private static final int GG_OUTPUTTYPE = GG_OFF_CHOICE + PR_OUTPUTTYPE;
private static final int GG_OUTPUTRES = GG_OFF_CHOICE + PR_OUTPUTRES;
private static final int GG_GAINTYPE = GG_OFF_CHOICE + PR_GAINTYPE;
private static final int GG_FILTERLEN = GG_OFF_CHOICE + PR_FILTERLEN;
private static final int GG_KOMBINATION = GG_OFF_CHOICE + PR_KOMBINATION;
private static final int GG_GAIN = GG_OFF_PARAMFIELD + PR_GAIN;
private static final int GG_LOFREQ = GG_OFF_PARAMFIELD + PR_LOFREQ;
private static final int GG_HIFREQ = GG_OFF_PARAMFIELD + PR_HIFREQ;
// private static final int GG_DRYMIX = GG_OFF_PARAMFIELD + PR_DRYMIX;
// private static final int GG_WETMIX = GG_OFF_PARAMFIELD + PR_WETMIX;
// private static final int GG_ROLLOFF = GG_OFF_PARAMFIELD + PR_ROLLOFF;
private static final int GG_BANDSPEROCT = GG_OFF_PARAMFIELD + PR_BANDSPEROCT;
private static PropertyArray static_pr = null;
private static Presets static_presets = null;
private static final String ERR_CHAN = "Input 2 must not have\nmore channels than input 1";
// -------- public methods --------
/**
* !! setVisible() bleibt dem Aufrufer ueberlassen
*/
public VoocooderDlg()
{
super( "Voocooder" );
init2();
}
protected void buildGUI()
{
// einmalig PropertyArray initialisieren
if( static_pr == null ) {
static_pr = new PropertyArray();
static_pr.text = prText;
static_pr.textName = prTextName;
static_pr.intg = prIntg;
static_pr.intgName = prIntgName;
static_pr.para = prPara;
static_pr.para[ PR_LOFREQ ] = new Param( 400.0, Param.ABS_HZ );
static_pr.para[ PR_HIFREQ ] = new Param( 9000.0, Param.ABS_HZ );
static_pr.para[ PR_DRYMIX ] = new Param( 100.0, Param.FACTOR_AMP );
static_pr.para[ PR_WETMIX ] = new Param( 25.0, Param.FACTOR_AMP );
static_pr.para[ PR_ROLLOFF ] = new Param( 12.0, Param.OFFSET_SEMITONES );
static_pr.para[ PR_BANDSPEROCT ] = new Param( 12.0, Param.NONE );
static_pr.paraName = prParaName;
// static_pr.superPr = DocumentFrame.static_pr;
fillDefaultAudioDescr( static_pr.intg, PR_OUTPUTTYPE, PR_OUTPUTRES );
fillDefaultGain( static_pr.para, PR_GAIN );
static_presets = new Presets( getClass(), static_pr.toProperties( true ));
}
presets = static_presets;
pr = (PropertyArray) static_pr.clone();
// -------- build GUI --------
GridBagConstraints con;
PathField ggInputFile, ggOutputFile, ggModFile;
JComboBox ggFltLen, ggKombination;
ParamField ggLoFreq, ggHiFreq, ggBandsPerOct;
ParamSpace[] spcHiCut;
Component[] ggGain;
PathField[] ggInputs;
gui = new GUISupport();
con = gui.getGridBagConstraints();
con.insets = new Insets( 1, 2, 1, 2 );
// -------- I/O-Gadgets --------
con.fill = GridBagConstraints.BOTH;
con.gridwidth = GridBagConstraints.REMAINDER;
gui.addLabel( new GroupLabel( "Waveform I/O", GroupLabel.ORIENT_HORIZONTAL,
GroupLabel.BRACE_NONE ));
ggInputFile = new PathField( PathField.TYPE_INPUTFILE + PathField.TYPE_FORMATFIELD,
"Select first input file" );
ggInputFile.handleTypes( GenericFile.TYPES_SOUND );
con.gridwidth = 1;
con.weightx = 0.1;
gui.addLabel( new JLabel( "Input 1", SwingConstants.RIGHT ));
con.gridwidth = GridBagConstraints.REMAINDER;
con.weightx = 0.9;
gui.addPathField( ggInputFile, GG_INPUTFILE, null );
ggModFile = new PathField( PathField.TYPE_INPUTFILE + PathField.TYPE_FORMATFIELD,
"Select second input file" );
ggModFile.handleTypes( GenericFile.TYPES_SOUND );
con.gridwidth = 1;
con.weightx = 0.1;
gui.addLabel( new JLabel( "Input 2", SwingConstants.RIGHT ));
con.gridwidth = GridBagConstraints.REMAINDER;
con.weightx = 0.9;
gui.addPathField( ggModFile, GG_MODFILE, null );
ggOutputFile = new PathField( PathField.TYPE_OUTPUTFILE + PathField.TYPE_FORMATFIELD +
PathField.TYPE_RESFIELD, "Select output file" );
ggOutputFile.handleTypes( GenericFile.TYPES_SOUND );
ggInputs = new PathField[ 2 ];
ggInputs[ 0 ] = ggInputFile;
ggInputs[ 1 ] = ggModFile;
ggOutputFile.deriveFrom( ggInputs, "$D0$B0Vcd$B1$E" );
con.gridwidth = 1;
con.weightx = 0.1;
gui.addLabel( new JLabel( "Output", SwingConstants.RIGHT ));
con.gridwidth = GridBagConstraints.REMAINDER;
con.weightx = 0.9;
gui.addPathField( ggOutputFile, GG_OUTPUTFILE, null );
gui.registerGadget( ggOutputFile.getTypeGadget(), GG_OUTPUTTYPE );
gui.registerGadget( ggOutputFile.getResGadget(), GG_OUTPUTRES );
ggGain = createGadgets( GGTYPE_GAIN );
con.weightx = 0.1;
con.gridwidth = 1;
gui.addLabel( new JLabel( "Gain", SwingConstants.RIGHT ));
con.weightx = 0.4;
gui.addParamField( (ParamField) ggGain[ 0 ], GG_GAIN, null );
con.weightx = 0.5;
con.gridwidth = GridBagConstraints.REMAINDER;
gui.addChoice( (JComboBox) ggGain[ 1 ], GG_GAINTYPE, null );
// -------- Settings-Gadgets --------
gui.addLabel( new GroupLabel( "Voodoo Settings", GroupLabel.ORIENT_HORIZONTAL,
GroupLabel.BRACE_NONE ));
ggLoFreq = new ParamField( Constants.spaces[ Constants.absHzSpace ]);
con.weightx = 0.1;
con.gridwidth = 1;
gui.addLabel( new JLabel( "Low Freq", SwingConstants.RIGHT ));
con.weightx = 0.4;
gui.addParamField( ggLoFreq, GG_LOFREQ, null );
spcHiCut = new ParamSpace[ 3 ];
spcHiCut[0] = Constants.spaces[ Constants.absHzSpace ];
spcHiCut[1] = Constants.spaces[ Constants.offsetHzSpace ];
spcHiCut[2] = Constants.spaces[ Constants.offsetSemitonesSpace ];
ggHiFreq = new ParamField( spcHiCut );
ggHiFreq.setReference( ggLoFreq );
con.weightx = 0.1;
gui.addLabel( new JLabel( "High Freq", SwingConstants.RIGHT ));
con.weightx = 0.4;
con.gridwidth = GridBagConstraints.REMAINDER;
gui.addParamField( ggHiFreq, GG_HIFREQ, null );
ggBandsPerOct = new ParamField( new ParamSpace( 1.0, 256.0, 1.0, Param.NONE ));
con.weightx = 0.1;
con.gridwidth = 1;
gui.addLabel( new JLabel( "Bands per Oct.", SwingConstants.RIGHT ));
con.weightx = 0.4;
gui.addParamField( ggBandsPerOct, GG_BANDSPEROCT, null );
ggFltLen = new JComboBox();
ggFltLen.addItem( "Short" );
ggFltLen.addItem( "Medium" );
ggFltLen.addItem( "Long" );
ggFltLen.addItem( "Very long" );
con.weightx = 0.1;
gui.addLabel( new JLabel( "Filter length", SwingConstants.RIGHT ));
con.weightx = 0.4;
con.gridwidth = GridBagConstraints.REMAINDER;
gui.addChoice( ggFltLen, GG_FILTERLEN, null );
ggKombination = new JComboBox();
for( int i = 0; i < KMB_NAMES.length; i++ ) {
ggKombination.addItem( KMB_NAMES[i] );
}
con.weightx = 0.1;
con.gridwidth = 1;
gui.addLabel( new JLabel( "Combination", SwingConstants.RIGHT ));
con.weightx = 0.4;
gui.addChoice( ggKombination, GG_KOMBINATION, null );
initGUI( this, FLAGS_PRESETS | FLAGS_PROGBAR, gui );
}
/**
* Transfer values from prop-array to GUI
*/
public void fillGUI()
{
super.fillGUI();
super.fillGUI( gui );
}
/**
* Transfer values from GUI to prop-array
*/
public void fillPropertyArray()
{
super.fillPropertyArray();
super.fillPropertyArray( gui );
}
// -------- Processor Interface --------
/*
* How it works: input is low and highpass filtered. low/high are processed separately as
* "dry parts", the difference from the original signal is fed to the shaper stage: it is
* split up into a user defined number of bands per octave (cosine-modulated sinc FIR filters
* with variable taps) -> peak amp. for each band is tracked; gain is normalized, chebychev
* polynomial distortion is applied and original gain restored.
*/
// XXX TO-DO : dry/wet mix
protected void process()
{
int i, j, k, len, off, ch, chunkLength, chunkLength2, band;
long progOff, progLen;
double d1, d2, loFreq, hiFreq, freqFactor;
float f1, f2, f3, f4;
// io
AudioFile inF = null;
AudioFile modF = null;
AudioFile outF = null;
AudioFileDescr inStream;
AudioFileDescr modStream;
AudioFileDescr outStream;
FloatFile[] floatF = null;
File[] tempFile = null;
// buffers
float[] lowpass, highpass, hpFFTBuf, lpFFTBuf, dwFFTBuf;
float[][] shpFFTBuf, inLPOverBuf, modLPOverBuf, inHPOverBuf, modHPOverBuf, inConvFFTBuf, modConvFFTBuf;
float[][][] inConvOverBuf, modConvOverBuf, inSliceFFTBuf, modSliceFFTBuf;
float[] convBuf1, convBuf2, convBuf3;
float[] dwWin, shpLowWin, shpHighWin;
float[][] inBuf, modBuf, outBuf;
int inChanNum, inLength, modChanNum, outChanNum, inputLen, outSkip;
int framesRead, framesWritten;
Param ampRef = new Param( 1.0, Param.ABS_AMP ); // transform-Referenz
float gain = 1.0f; // gain abs amp
float maxAmp = 0.0f;
PathField ggOutput;
int dwNumPeriods, dwHalfWinSize, dwFltLength, dwFFTLength, dwOverLen;
int shpNumPeriods, shpMinHalfWin, shpMaxHalfWin, shpHighHalfWin, shpLowHalfWin;
int shpMinFFTLen, shpMaxFFTLen, shpFFTLen, shpFFTLenIdx, numShpLen, shpFFTCounter;
int inputConv = 0, convNum = 0, convOverLen = 0, convShift;
float[] crossFreqs, cosineFreqs;
int[] shpFFTLength, shpFFTCount, shpHalfWinSize, shpConvNum;
double freqNorm, freqBase, cosineNorm, cosineBase, cosineBaseSqr;
int numBands;
float dryGain, nyquist;
// kombi
float[][] kmbEnv = null; // first order IIR save for envelope following
float[] kmbEnvDec = null; // decay of IIR
topLevel: try {
// ---- open input, output ----
// input
inF = AudioFile.openAsRead( new File( pr.text[ PR_INPUTFILE ]));
inStream = inF.getDescr();
inChanNum = inStream.channels;
inLength = (int) inStream.length;
// this helps to prevent errors from empty files!
if( (inLength * inChanNum) < 1 ) throw new EOFException( ERR_EMPTY );
// .... check running ....
if( !threadRunning ) break topLevel;
modF = AudioFile.openAsRead( new File( pr.text[ PR_MODFILE ]));
modStream = modF.getDescr();
modChanNum = modStream.channels;
inLength = (int) Math.min( inLength, modStream.length );
outChanNum = Math.max( modChanNum, inChanNum );
// this helps to prevent errors from empty files!
if( inLength < 1 ) throw new EOFException( ERR_EMPTY );
if( modChanNum > inChanNum ) throw new IOException( ERR_CHAN );
// .... check running ....
if( !threadRunning ) break topLevel;
// output
ggOutput = (PathField) gui.getItemObj( GG_OUTPUTFILE );
if( ggOutput == null ) throw new IOException( ERR_MISSINGPROP );
outStream = new AudioFileDescr( inStream );
ggOutput.fillStream( outStream );
outStream.channels = outChanNum;
outF = AudioFile.openAsWrite( outStream );
// .... check running ....
if( !threadRunning ) break topLevel;
// ---- calculate filters ----
loFreq = Param.transform( pr.para[ PR_LOFREQ ], Param.ABS_HZ, null, null ).value;
// hiFreq = Math.min( inStream.smpRate * 0.5,
// Param.transform( pr.para[ PR_HIFREQ ], Param.ABS_HZ, pr.para[ PR_LOFREQ ], null ).value );
hiFreq = Param.transform( pr.para[ PR_HIFREQ ], Param.ABS_HZ, pr.para[ PR_LOFREQ ], null ).value;
freqFactor = Math.pow( 2.0, 1.0 / pr.para[ PR_BANDSPEROCT ].value);
numBands = (int) (Math.log( hiFreq / loFreq ) / Math.log( freqFactor ));
crossFreqs = new float[ numBands+1 ];
cosineFreqs = new float[ numBands+1 ];
shpHalfWinSize = new int[ numBands ];
d1 = loFreq;
d2 = d1 * freqFactor;
nyquist = (float) (inStream.rate * 0.5);
// ---- calc bandpass freqs ----
for( i = 0; i <= numBands; i++ ) {
crossFreqs[i] = (float) d1;
cosineFreqs[i] = (float) (Math.sqrt( d2 * d1 ) - d1);
d1 = d2;
d2 = d1 * freqFactor;
if( d2 > nyquist ) {
numBands = i;
break;
}
}
hiFreq = crossFreqs[numBands];
dwNumPeriods = 6; // << pr.intg[ PR_FILTERLEN ];
dwHalfWinSize = Math.max( 1, (int) (dwNumPeriods * inStream.rate / loFreq + 0.5) );
freqNorm = Constants.PI2 / inStream.rate;
cosineNorm = 4.0 / (Math.PI*Math.PI);
dwFltLength = dwHalfWinSize + dwHalfWinSize;
j = dwFltLength + dwFltLength - 1;
for( dwFFTLength = 2; dwFFTLength < j; dwFFTLength <<= 1 ) ;
inputLen = dwFFTLength - dwFltLength + 1;
dwOverLen = dwFFTLength - inputLen;
// ---- calc highpass filter ----
// use a narrow window here to decrease time smearing!! (large window calculated in lowpass section)
k = Math.max( 1, (int) (dwNumPeriods * inStream.rate / hiFreq + 0.5) );
dwWin = Filter.createFullWindow( k << 1, Filter.WIN_BLACKMAN );
freqBase = freqNorm * (inStream.rate/2);
highpass = new float[ dwFFTLength + 2 ];
Util.clear( highpass );
highpass[ dwHalfWinSize ] = (float) freqBase;
freqBase = freqNorm * hiFreq * 0.8; // / rollOff;
cosineBase = freqNorm * hiFreq * 0.04; // (1.0 - 1.0 / rollOff);
cosineBaseSqr = cosineBase * cosineBase;
for( j = 1; j < k; j++ ) {
d1 = (Math.sin( freqBase * j ) / j);
// raised cosine modulation
d2 = cosineNorm * cosineBaseSqr * j * j;
d1 *= (Math.cos( cosineBase * j ) / (1.0 - d2));
highpass[ dwHalfWinSize+j ]-= (float) d1;
highpass[ dwHalfWinSize-j ]-= (float) d1;
}
highpass[ dwHalfWinSize ] -= (float) freqBase;
Util.mult( dwWin, 0, highpass, dwHalfWinSize - k, dwWin.length );
Fourier.realTransform( highpass, dwFFTLength, Fourier.FORWARD );
Util.mult( highpass, 0, highpass.length, (float) (1.0 / Math.PI) );
// for( i = 0; i < highpass.length; i += 2 ) {
// highpass[ i ] = (float) Math.sqrt( highpass[ i ]*highpass[ i ]+highpass[ i+1 ]*highpass[ i+1 ] );
// highpass[ i+1 ] = highpass[ i ];
// }
// highpass[ (int) (hiFreq / inStream.smpRate * 2 * highpass.length) ] = -1.0f;
// Debug.view( highpass, "highpass" );
// ---- calc lowpass filter ----
dwWin = Filter.createFullWindow( dwFltLength, Filter.WIN_BLACKMAN );
freqBase = freqNorm * (inStream.rate/2);
lowpass = new float[ dwFFTLength + 2 ];
Util.clear( lowpass );
lowpass[ dwHalfWinSize ] = (float) (Math.PI - freqBase);
freqBase = freqNorm * loFreq * 1.2;
cosineBase = freqNorm * loFreq * 0.04;
cosineBaseSqr = cosineBase * cosineBase;
for( j = 1; j < dwHalfWinSize; j++ ) {
d1 = (Math.sin( freqBase * j ) / j);
// raised cosine modulation
d2 = cosineNorm * cosineBaseSqr * j * j;
d1 *= (Math.cos( cosineBase * j ) / (1.0 - d2));
lowpass[ dwHalfWinSize+j ] = (float) d1;
lowpass[ dwHalfWinSize-j ] = (float) d1;
}
lowpass[ dwHalfWinSize ] += (float) freqBase;
// windowing
Util.mult( dwWin, 0, lowpass, 0, dwFltLength );
Fourier.realTransform( lowpass, dwFFTLength, Fourier.FORWARD );
Util.mult( lowpass, 0, lowpass.length, (float) (1.0 / Math.PI) );
// for( i = 0; i < lowpass.length; i += 2 ) {
// lowpass[ i ] = (float) Math.sqrt( lowpass[ i ]*lowpass[ i ]+lowpass[ i+1 ]*lowpass[ i+1 ] );
// lowpass[ i+1 ] = lowpass[ i ];
// }
// lowpass[ (int) (loFreq / inStream.smpRate * 2 * lowpass.length) ] = -1.0f;
// Debug.view( lowpass, "lowpass" );
// LP = +1.0 fc -1.0 Zero
// HP = +1.0 pi/2 -1.0 fc
// BP = +1.0 fc2 -1.0 fc1
// ---- calculate impulse response of the bandpasses + init tracker ----
shpNumPeriods = 3 << pr.intg[ PR_FILTERLEN ];
shpMaxHalfWin = Math.max( 1, (int) (shpNumPeriods * inStream.rate / crossFreqs[0] + 0.5) );
shpMinHalfWin = Math.max( 1, (int) (shpNumPeriods * inStream.rate / crossFreqs[numBands-1] + 0.5) );
i = shpMinHalfWin + shpMinHalfWin;
j = i + i - 1;
for( shpMinFFTLen = 2; shpMinFFTLen < j; shpMinFFTLen <<= 1 ) ; // FFTlength for shortest BP
i = shpMaxHalfWin + shpMaxHalfWin;
j = i + i - 1;
for( shpMaxFFTLen = 2; shpMaxFFTLen < j; shpMaxFFTLen <<= 1 ) ; // FFTlength for longest BP
for( numShpLen = 1, i = shpMinFFTLen; i < shpMaxFFTLen; i <<= 1, numShpLen ++ ) ; // how many different FFT lengths?
shpFFTLength = new int[ numShpLen ]; // each of the different lengths
shpFFTCount = new int[ numShpLen ]; // how many successive FFT of each FFT length
shpConvNum = new int[ numShpLen ];
shpFFTBuf = new float[ numBands ][];
inConvFFTBuf = new float[ inChanNum ][ shpMaxFFTLen + 2 ];
modConvFFTBuf = new float[ modChanNum ][ shpMaxFFTLen + 2 ];
inConvOverBuf = new float[ inChanNum ][ numBands ][];
modConvOverBuf = new float[ modChanNum ][ numBands ][];
shpFFTLen = -1; // shpMaxFFTLen;
shpFFTLenIdx = -1;
shpFFTLength[0] = shpFFTLen;
shpFFTCount[0] = 0;
i = shpMaxFFTLen - (shpMaxHalfWin << 1) + 1;
shpConvNum[0] = (inputLen + i - 1) / i;
shpHighHalfWin = shpMaxHalfWin;
shpHighWin = Filter.createFullWindow( shpHighHalfWin << 1, Filter.WIN_BLACKMAN );
for( band = 0; band < numBands; band++ ) {
shpLowHalfWin = shpHighHalfWin;
shpLowWin = shpHighWin;
shpHalfWinSize[band]=shpLowHalfWin;
shpHighHalfWin = Math.max( 1, (int) (shpNumPeriods * inStream.rate / crossFreqs[band+1] + 0.5) );
shpHighWin = Filter.createFullWindow( shpHighHalfWin << 1, Filter.WIN_BLACKMAN );
j = ((shpLowHalfWin + shpLowHalfWin) << 1) - 1;
for( k = 2; k < j; k <<= 1 ) ; // current FFTlength
convBuf1 = new float[ k + 2 ];
shpFFTBuf[band] = convBuf1;
// System.out.println( "Band "+band+" (freq "+crossFreqs[band]+" ... "+crossFreqs[band+1]+") => halfwin "+shpLowHalfWin+"/"+shpHighHalfWin );
if( k != shpFFTLen ) {
// System.out.println( "new FFT size "+k );
shpFFTLen = k;
shpFFTLenIdx++;
shpFFTLength[ shpFFTLenIdx ] = shpFFTLen;
j = shpFFTLen - (shpLowHalfWin << 1) + 1;
shpConvNum[ shpFFTLenIdx ] = (inputLen + j - 1) / j;
}
shpFFTCount[ shpFFTLenIdx ]++;
// overlap buffer
inputConv = shpFFTLen - (shpLowHalfWin << 1) + 1;
convOverLen = shpFFTLen - inputConv;
for( ch = 0; ch < inChanNum; ch++ ) {
inConvOverBuf[ ch ][ band ] = new float[ convOverLen ];
}
for( ch = 0; ch < modChanNum; ch++ ) {
modConvOverBuf[ ch ][ band ] = new float[ convOverLen ];
}
// first part of filter
if( band < numBands-1 ) { // damn overhead for highest band (we don't want an extra slope, we have the hp-slope already)
freqBase = freqNorm * crossFreqs[ band+1 ];
cosineBase = freqNorm * cosineFreqs[ band+1 ];
cosineBaseSqr = cosineBase * cosineBase;
for( j = 1; j < shpHighHalfWin; j++ ) {
// sinc-filter
d1 = (Math.sin( freqBase * j ) / j);
// raised cosine modulation
d2 = cosineNorm * cosineBaseSqr * j * j;
d1 *= (Math.cos( cosineBase * j ) / (1.0 - d2));
convBuf1[ shpLowHalfWin+j ] = (float) d1 * shpHighWin[ shpHighHalfWin+j ];
convBuf1[ shpLowHalfWin-j ] = (float) d1 * shpHighWin[ shpHighHalfWin-j ];
}
convBuf1[ shpLowHalfWin ] = (float) freqBase * shpHighWin[ shpHighHalfWin ];
} else {
Util.clear( convBuf1 );
convBuf1[ shpLowHalfWin ] = (float) Math.PI * shpHighWin[ shpHighHalfWin ];
}
// second part of filter
if( band > 0 ) { // damn overhead for lowest band (we don't want an extra slope, we have the lp-slope already)
freqBase = freqNorm * crossFreqs[ band ];
cosineBase = freqNorm * cosineFreqs[ band ];
cosineBaseSqr = cosineBase * cosineBase;
for( j = 1; j < shpLowHalfWin; j++ ) {
d1 = (Math.sin( freqBase * j ) / j);
// raised cosine modulation
d2 = cosineNorm * cosineBaseSqr * j * j;
d1 *= (Math.cos( cosineBase * j ) / (1.0 - d2));
convBuf1[ shpLowHalfWin+j ] -= (float) d1 * shpLowWin[ shpLowHalfWin+j ];
convBuf1[ shpLowHalfWin-j ] -= (float) d1 * shpLowWin[ shpLowHalfWin-j ];
}
convBuf1[ shpLowHalfWin ] -= (float) freqBase * shpLowWin[ shpLowHalfWin ];
// zero padding
for( j = (shpLowHalfWin << 1); j < shpFFTLen; j++ ) {
convBuf1[ j ] = 0.0f;
}
} // no else ;)
// all filters are pre-transformed to fourier domain and normalized
Fourier.realTransform( convBuf1, shpFFTLen, Fourier.FORWARD );
Util.mult( convBuf1, 0, convBuf1.length, (float) (1.0 / Math.PI) );
} // for bands
numShpLen = shpFFTLenIdx + 1; // muss korrigiert werden, weil u.U. eine FFT-Laenge "uebersprungen" wurde!!
// ---- further inits ----
outBuf = new float[ outChanNum ][ shpMaxHalfWin + inputLen ];
dwFFTBuf = new float[ dwFFTLength + 2 ];
lpFFTBuf = new float[ dwFFTLength + 2 ];
hpFFTBuf = new float[ dwFFTLength + 2 ];
inLPOverBuf = new float[ inChanNum ][ dwOverLen ];
modLPOverBuf = new float[ modChanNum ][ dwOverLen ];
inHPOverBuf = new float[ inChanNum ][ dwOverLen ];
modHPOverBuf = new float[ modChanNum ][ dwOverLen ];
inBuf = new float[ inChanNum ][ inputLen + dwHalfWinSize ];
modBuf = new float[ modChanNum ][ inputLen + dwHalfWinSize ];
inSliceFFTBuf = new float[ inChanNum ][ shpConvNum[ numShpLen - 1 ]][ shpMaxFFTLen + 2 ]; // largets convNum x largest FFT size
modSliceFFTBuf = new float[ inChanNum ][ shpConvNum[ numShpLen - 1 ]][ shpMaxFFTLen + 2 ]; // largets convNum x largest FFT size
Util.clear( inBuf );
Util.clear( modBuf );
switch( pr.intg[ PR_KOMBINATION ]) {
case KMB_VOCODER:
kmbEnv = new float[ outChanNum ][ numBands ];
kmbEnvDec = new float[ numBands ];
Util.clear( kmbEnv );
for( band = 0; band < numBands; band++ ) {
f1 = (float) inStream.rate / crossFreqs[ band ]; // period [smp]
f1 = (float) Math.pow( 0.125, 1.0 / (f1 * 4) ); // -18 dB after 4 periods
kmbEnvDec[band] = f1;
}
break;
}
// System.out.println( "inputLen "+inputLen+"; dwFltLength "+dwFltLength+"; shpMaxFFTLen "+shpMaxFFTLen+"; shpMinFFTLen "+shpMinFFTLen+"; sliceFFTBuf dim "+(shpConvNum[ numShpLen - 1 ])+" x "+shpMaxFFTLen );
progOff = 0;
progLen = (long) inLength * 4; // read, 2x process, write
// m = 0; // java compiler complains if not initialized
dryGain = 0.0f; // XXX 1.0f;
// normalization requires temp files
if( pr.intg[ PR_GAINTYPE ] == GAIN_UNITY ) {
tempFile = new File[ inChanNum ];
floatF = new FloatFile[ inChanNum ];
for( ch = 0; ch < inChanNum; ch++ ) { // first zero them because an exception might be thrown
tempFile[ ch ] = null;
floatF[ ch ] = null;
}
for( ch = 0; ch < inChanNum; ch++ ) {
tempFile[ ch ] = IOUtil.createTempFile();
floatF[ ch ] = new FloatFile( tempFile[ ch ], GenericFile.MODE_OUTPUT );
}
progLen += inLength;
} else { // account for gain loss RealFFT => CmplxIFFT
gain = (float) ((Param.transform( pr.para[ PR_GAIN ], Param.ABS_AMP, ampRef, null )).value);
}
// .... check running ....
if( !threadRunning ) break topLevel;
// ----==================== the real stuff ====================----
framesRead = 0;
framesWritten = 0;
outSkip = dwHalfWinSize + shpMaxHalfWin;
while( threadRunning && (framesWritten < inLength) ) {
chunkLength = Math.min( inputLen, inLength - framesRead );
chunkLength2 = Math.min( inputLen, inLength - framesWritten );
// ---- read input chunk ----
for( off = 0; threadRunning && (off < chunkLength); ) {
len = Math.min( 8192, chunkLength - off );
inF.readFrames( inBuf, off + dwHalfWinSize, len ); // inBuf delayed by dwHalfWinSize
modF.readFrames( modBuf, off + dwHalfWinSize, len );
framesRead += len;
progOff += len;
off += len;
// .... progress ....
setProgression( (float) progOff / (float) progLen );
}
// .... check running ....
if( !threadRunning ) break topLevel;
// ---- preprocess in -----------------------------------------------------------------------
// after this inBuf is the bandpass filtered pre-FFT stage
for( ch = 0; threadRunning && (ch < inChanNum); ch++ ) {
// ---- dry/wet pre-filtering ----
convBuf1 = outBuf[ ch ];
convBuf2 = inBuf[ ch ];
System.arraycopy( convBuf2, dwHalfWinSize, dwFFTBuf, 0, chunkLength ); // inBuf copy without delay
for( i = chunkLength; i < dwFFTLength; i++ ) {
dwFFTBuf[ i ] = 0.0f;
}
Fourier.realTransform( dwFFTBuf, dwFFTLength, Fourier.FORWARD ); // input chunk -> FFT
Fourier.complexMult( lowpass, 0, dwFFTBuf, 0, lpFFTBuf, 0, lowpass.length ); // convolve with LP
Fourier.realTransform( lpFFTBuf, dwFFTLength, Fourier.INVERSE ); // back to time domain (delayed by dwHalfWinSize)
Util.add( inLPOverBuf[ ch ], 0, lpFFTBuf, 0, dwOverLen ); // add old overlap
System.arraycopy( lpFFTBuf, inputLen, inLPOverBuf[ ch ], 0, dwOverLen ); // save new overlap
Fourier.complexMult( highpass, 0, dwFFTBuf, 0, hpFFTBuf, 0, highpass.length ); // convolve with HP
Fourier.realTransform( hpFFTBuf, dwFFTLength, Fourier.INVERSE ); // back to time domain (delayed by dwHalfWinSize)
Util.add( inHPOverBuf[ ch ], 0, hpFFTBuf, 0, dwOverLen ); // add old overlap
System.arraycopy( hpFFTBuf, inputLen, inHPOverBuf[ ch ], 0, dwOverLen ); // save new overlap
// now the signal to be fed into the waveshaper is input (delayed by dwHalfWinSize)
// - lowpass - highpass
Util.sub( lpFFTBuf, 0, convBuf2, 0, inputLen );
Util.sub( hpFFTBuf, 0, convBuf2, 0, inputLen );
System.arraycopy( lpFFTBuf, 0, convBuf1, shpMaxHalfWin, inputLen ); // dry lp/hp to output
Util.add( hpFFTBuf, 0, convBuf1, shpMaxHalfWin, inputLen );
Util.mult( convBuf1, shpMaxHalfWin, inputLen, dryGain );
} // for chanNum
// .... check running ....
if( !threadRunning ) break topLevel;
// ---- preprocess mod -----------------------------------------------------------------------
// after this modBuf is the bandpass filtered pre-FFT stage
for( ch = 0; threadRunning && (ch < modChanNum); ch++ ) {
// ---- dry/wet pre-filtering ----
convBuf2 = modBuf[ ch ];
System.arraycopy( convBuf2, dwHalfWinSize, dwFFTBuf, 0, chunkLength ); // modBuf copy without delay
for( i = chunkLength; i < dwFFTLength; i++ ) {
dwFFTBuf[ i ] = 0.0f;
}
Fourier.realTransform( dwFFTBuf, dwFFTLength, Fourier.FORWARD ); // input chunk -> FFT
Fourier.complexMult( lowpass, 0, dwFFTBuf, 0, lpFFTBuf, 0, lowpass.length ); // convolve with LP
Fourier.realTransform( lpFFTBuf, dwFFTLength, Fourier.INVERSE ); // back to time domain (delayed by dwHalfWinSize)
Util.add( modLPOverBuf[ ch ], 0, lpFFTBuf, 0, dwOverLen ); // add old overlap
System.arraycopy( lpFFTBuf, inputLen, modLPOverBuf[ ch ], 0, dwOverLen ); // save new overlap
Fourier.complexMult( highpass, 0, dwFFTBuf, 0, hpFFTBuf, 0, highpass.length ); // convolve with HP
Fourier.realTransform( hpFFTBuf, dwFFTLength, Fourier.INVERSE ); // back to time domain (delayed by dwHalfWinSize)
Util.add( modHPOverBuf[ ch ], 0, hpFFTBuf, 0, dwOverLen ); // add old overlap
System.arraycopy( hpFFTBuf, inputLen, modHPOverBuf[ ch ], 0, dwOverLen ); // save new overlap
// now the signal to be fed into the waveshaper is input (delayed by dwHalfWinSize)
// - lowpass - highpass
Util.sub( lpFFTBuf, 0, convBuf2, 0, inputLen );
Util.sub( hpFFTBuf, 0, convBuf2, 0, inputLen );
// System.arraycopy( lpFFTBuf, 0, convBuf1, shpMaxHalfWin, inputLen ); // dry lp/hp to output
// Util.add( hpFFTBuf, 0, convBuf1, shpMaxHalfWin, inputLen );
// Util.mult( convBuf1, shpMaxHalfWin, inputLen, dryGain );
} // for chanNum
// .... check running ....
if( !threadRunning ) break topLevel;
// ---- bands loop ----------------------------------------------------------------------
shpFFTLenIdx = -1;
shpFFTCounter = 0;
for( band = 0; threadRunning && (band < numBands); band++ ) {
shpLowHalfWin = shpHalfWinSize[ band ];
if( shpFFTCounter == 0) {
shpFFTLenIdx++;
shpFFTCounter = shpFFTCount[ shpFFTLenIdx ];
shpFFTLen = shpFFTLength[ shpFFTLenIdx ];
inputConv = shpFFTLen - (shpLowHalfWin << 1) + 1; // max. input chunk for convolution
convNum = shpConvNum[ shpFFTLenIdx ]; // number of conv's per inputLen
for( i = 0, off = 0; i < convNum; i++ ) { // FFT all the sliced input chunk pieces
len = Math.min( inputConv, inputLen - off );
for( ch = 0; ch < inChanNum; ch++ ) {
convBuf3 = inSliceFFTBuf[ ch ][ i ];
convBuf2 = inBuf[ ch ];
System.arraycopy( convBuf2, off, convBuf3, 0, len );
for( j = len; j < shpFFTLen; j++ ) {
convBuf3[ j ] = 0.0f;
}
Fourier.realTransform( convBuf3, shpFFTLen, Fourier.FORWARD );
}
for( ch = 0; ch < modChanNum; ch++ ) {
convBuf3 = modSliceFFTBuf[ ch ][ i ];
convBuf2 = modBuf[ ch ];
System.arraycopy( convBuf2, off, convBuf3, 0, len );
for( j = len; j < shpFFTLen; j++ ) {
convBuf3[ j ] = 0.0f;
}
Fourier.realTransform( convBuf3, shpFFTLen, Fourier.FORWARD );
}
off += len;
}
}
shpFFTCounter--;
convOverLen = (shpLowHalfWin << 1) - 1; // =< inputConv !
convShift = shpMaxHalfWin - shpLowHalfWin; // align all filters with uniform dly of shpMaxHalfWin
// convolve slices, inverse transform, overlap-add
for( i = 0, off = 0; i < convNum; i++ ) {
len = Math.min( inputConv, inputLen - off );
for( ch = 0; ch < inChanNum; ch++ ) {
Fourier.complexMult( shpFFTBuf[ band ], 0, inSliceFFTBuf[ ch ][ i ], 0,
inConvFFTBuf[ ch ], 0, shpFFTBuf[ band ].length );
Fourier.realTransform( inConvFFTBuf[ ch ], shpFFTLen, Fourier.INVERSE ); // back to time domain (delayed by shpLowHalfWin)
Util.add( inConvOverBuf[ ch ][ band ], 0, inConvFFTBuf[ ch ], 0, convOverLen ); // add old overlap
System.arraycopy( inConvFFTBuf[ ch ], len, inConvOverBuf[ ch ][ band ], 0, convOverLen ); // save new overlap
}
for( ch = 0; ch < modChanNum; ch++ ) {
Fourier.complexMult( shpFFTBuf[ band ], 0, modSliceFFTBuf[ ch ][ i ], 0,
modConvFFTBuf[ ch ], 0, shpFFTBuf[ band ].length );
Fourier.realTransform( modConvFFTBuf[ ch ], shpFFTLen, Fourier.INVERSE ); // back to time domain (delayed by shpLowHalfWin)
Util.add( modConvOverBuf[ ch ][ band ], 0, modConvFFTBuf[ ch ], 0, convOverLen ); // add old overlap
System.arraycopy( modConvFFTBuf[ ch ], len, modConvOverBuf[ ch ][ band ], 0, convOverLen ); // save new overlap
}
// ======KOMBINATION===============================================================
switch( pr.intg[ PR_KOMBINATION ]) {
case KMB_MULTIPLY:
for( ch = 0; ch < outChanNum; ch++ ) {
convBuf1 = outBuf[ ch ];
convBuf2 = inConvFFTBuf[ ch % inChanNum ];
convBuf3 = modConvFFTBuf[ ch % modChanNum ];
for( j = 0, k = off + convShift; j < len; j++, k++ ) { // all samples in a slice
convBuf1[ k ] += convBuf2[ j ] * convBuf3[ j ] * numBands; // * wetGain XXX
}
}
break;
case KMB_MODULO:
for( ch = 0; ch < outChanNum; ch++ ) {
convBuf1 = outBuf[ ch ];
convBuf2 = inConvFFTBuf[ ch % inChanNum ];
convBuf3 = modConvFFTBuf[ ch % modChanNum ];
for( j = 0, k = off + convShift; j < len; j++, k++ ) { // all samples in a slice
convBuf1[ k ] += convBuf2[ j ] % convBuf3[ j ]; // * wetGain XXX
}
}
break;
case KMB_MIN:
for( ch = 0; ch < outChanNum; ch++ ) {
convBuf1 = outBuf[ ch ];
convBuf2 = inConvFFTBuf[ ch % inChanNum ];
convBuf3 = modConvFFTBuf[ ch % modChanNum ];
for( j = 0, k = off + convShift; j < len; j++, k++ ) { // all samples in a slice
f1 = convBuf2[ j ];
f2 = convBuf3[ j ];
if( f1 > 0.0f ) {
if( f2 < 0.0f ) {
convBuf1[ k ] += (-f2 > f1) ? f1 : f2;
} else {
convBuf1[ k ] += (f2 > f1) ? f1 : f2;
}
} else {
if( f2 < 0.0f ) {
convBuf1[ k ] += (f2 < f1) ? f1 : f2;
} else {
convBuf1[ k ] += (f2 > -f1) ? f1 : f2;
}
}
}
}
break;
case KMB_MAX:
for( ch = 0; ch < outChanNum; ch++ ) {
convBuf1 = outBuf[ ch ];
convBuf2 = inConvFFTBuf[ ch % inChanNum ];
convBuf3 = modConvFFTBuf[ ch % modChanNum ];
for( j = 0, k = off + convShift; j < len; j++, k++ ) { // all samples in a slice
f1 = convBuf2[ j ];
f2 = convBuf3[ j ];
if( f1 > 0.0f ) {
if( f2 < 0.0f ) {
convBuf1[ k ] += (-f2 > f1) ? f2 : f1;
} else {
convBuf1[ k ] += (f2 > f1) ? f2 : f1;
}
} else {
if( f2 < 0.0f ) {
convBuf1[ k ] += (f2 < f1) ? f2 : f1;
} else {
convBuf1[ k ] += (f2 > -f1) ? f2 : f1;
}
}
}
}
break;
case KMB_VOCODER:
for( ch = 0; ch < outChanNum; ch++ ) {
convBuf1 = outBuf[ ch ];
convBuf2 = inConvFFTBuf[ ch % inChanNum ];
convBuf3 = modConvFFTBuf[ ch % modChanNum ];
f2 = kmbEnv[ ch ][ band ];
f3 = kmbEnvDec[ band ];
f4 = (1.0f - f3) * numBands;
for( j = 0, k = off + convShift; j < len; j++, k++ ) { // all samples in a slice
f1 = convBuf3[ j ];
f2 = Math.abs( f1 ) * f4 + f3 * f2;
convBuf1[ k ] += convBuf2[ j ] * f2;
}
kmbEnv[ ch ][ band ] = f2;
}
break;
}
off += len;
} // for slices
} // for bands
// .... check running ....
if( !threadRunning ) break topLevel;
// overlap handling input
for( ch = 0; ch < inChanNum; ch++ ) {
convBuf2 = inBuf[ ch ];
System.arraycopy( convBuf2, inputLen, convBuf2, 0, dwHalfWinSize );
}
for( ch = 0; ch < modChanNum; ch++ ) {
convBuf2 = modBuf[ ch ];
System.arraycopy( convBuf2, inputLen, convBuf2, 0, dwHalfWinSize );
}
progOff += chunkLength2 + chunkLength2;
// .... progress ....
setProgression( (float) progOff / (float) progLen );
// .... check running ....
if( !threadRunning ) break topLevel;
for( off = outSkip; threadRunning && (off < chunkLength2); ) {
len = Math.min( 8192, chunkLength2 - off );
if( floatF != null ) {
for( ch = 0; ch < outChanNum; ch++ ) {
floatF[ ch ].writeFloats( outBuf[ ch ], off, len );
}
} else {
for( ch = 0; ch < outChanNum; ch++ ) {
Util.mult( outBuf[ ch ], off, len, gain );
}
outF.writeFrames( outBuf, off, len );
}
progOff += len;
off += len;
// .... progress ....
setProgression( (float) progOff / (float) progLen );
}
for( ch = 0; ch < outChanNum; ch++ ) {
convBuf1 = outBuf[ ch ];
// max measurement
for( off = outSkip; off < chunkLength2; off++ ) {
f1 = Math.abs( convBuf1[ off ]);
if( f1 > maxAmp ) {
maxAmp = f1;
}
}
// overlap handling output
System.arraycopy( convBuf1, inputLen, convBuf1, 0, shpMaxHalfWin );
// for( i = shpMaxHalfWin; i < convBuf1.length; i++ ) convBuf1[ i ] = 0.0f;
}
if( outSkip > 0 ) {
framesWritten += Math.max( 0, chunkLength2 - outSkip );
outSkip = Math.max( 0, outSkip - chunkLength2 );
} else {
framesWritten += chunkLength2;
}
} // until framesWritten == outLength
// .... check running ....
if( !threadRunning ) break topLevel;
// ----==================== normalize output ====================----
if( pr.intg[ PR_GAINTYPE ] == GAIN_UNITY ) {
gain = (float) (Param.transform( pr.para[ PR_GAIN ], Param.ABS_AMP,
new Param( 1.0 / maxAmp, Param.ABS_AMP ), null )).value;
f1 = 1.0f;
normalizeAudioFile( floatF, outF, outBuf, gain, f1 );
maxAmp *= gain;
for( ch = 0; ch < inChanNum; ch++ ) {
floatF[ ch ].cleanUp();
floatF[ ch ] = null;
tempFile[ ch ].delete();
tempFile[ ch ] = null;
}
}
// .... check running ....
if( !threadRunning ) break topLevel;
// ---- Finish ----
outF.close();
outF = null;
outStream = null;
inF.close();
inF = null;
inStream = null;
modF.close();
modF = null;
modStream = null;
shpFFTBuf = null;
inLPOverBuf = null;
modLPOverBuf= null;
inHPOverBuf = null;
modHPOverBuf= null;
inSliceFFTBuf= null;
modSliceFFTBuf= null;
inConvOverBuf= null;
modConvOverBuf= null;
convBuf1 = null;
convBuf2 = null;
convBuf3 = null;
inBuf = null;
modBuf = null;
outBuf = null;
// inform about clipping/ low level
handleClipping( maxAmp );
}
catch( IOException e1 ) {
setError( e1 );
}
catch( OutOfMemoryError e2 ) {
inStream = null;
modStream = null;
outStream = null;
lowpass = null;
highpass = null;
hpFFTBuf = null;
lpFFTBuf = null;
dwFFTBuf = null;
inConvFFTBuf= null;
modConvFFTBuf= null;
shpFFTBuf = null;
inLPOverBuf = null;
modLPOverBuf= null;
inHPOverBuf = null;
modHPOverBuf= null;
inSliceFFTBuf= null;
modSliceFFTBuf= null;
inConvOverBuf= null;
modConvOverBuf= null;
convBuf1 = null;
convBuf2 = null;
convBuf3 = null;
inBuf = null;
modBuf = null;
outBuf = null;
System.gc();
setError( new Exception( ERR_MEMORY ));
}
// ---- cleanup (topLevel) ----
if( inF != null ) {
inF.cleanUp();
inF = null;
}
if( modF != null ) {
modF.cleanUp();
modF = null;
}
if( outF != null ) {
outF.cleanUp();
outF = null;
}
if( floatF != null ) {
for( ch = 0; ch < floatF.length; ch++ ) {
if( floatF[ ch ] != null ) {
floatF[ ch ].cleanUp();
floatF[ ch ] = null;
}
if( tempFile[ ch ] != null ) {
tempFile[ ch ].delete();
tempFile[ ch ] = null;
}
}
}
} // process()
}