/*
* ExciterDlg.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 waveshaping
* using cubic distortion.
*/
public class ExciterDlg
extends ModulePanel {
// -------- private variables --------
// Properties (defaults)
private static final int PR_INPUTFILE = 0; // pr.text
private static final int PR_OUTPUTFILE = 1;
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_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 String PRN_INPUTFILE = "InputFile";
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_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_OUTPUTFILE };
private static final int prIntg[] = { 0, 0, 0, FLT_LONG };
private static final String prIntgName[] = { PRN_OUTPUTTYPE, PRN_OUTPUTRES, PRN_GAINTYPE,
PRN_FILTERLEN };
private static final Param prPara[] = { null, null, null, null, null, null, null };
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_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_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;
// -------- public methods --------
public ExciterDlg() {
super("Exciter");
init2();
}
protected void buildGUI() {
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( 36.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;
PathField[] ggParent1;
JComboBox ggFltLen;
ParamField ggLoFreq, ggHiFreq, ggRollOff, ggBandsPerOct, ggDryMix, ggWetMix;
ParamSpace[] spcHiCut;
Component[] ggGain;
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 input file" );
ggInputFile.handleTypes( GenericFile.TYPES_SOUND );
con.gridwidth = 1;
con.weightx = 0.1;
gui.addLabel( new JLabel( "Input file", SwingConstants.RIGHT ));
con.gridwidth = GridBagConstraints.REMAINDER;
con.weightx = 0.9;
gui.addPathField( ggInputFile, GG_INPUTFILE, null );
ggOutputFile = new PathField( PathField.TYPE_OUTPUTFILE + PathField.TYPE_FORMATFIELD +
PathField.TYPE_RESFIELD, "Select output file" );
ggOutputFile.handleTypes( GenericFile.TYPES_SOUND );
con.gridwidth = 1;
con.weightx = 0.1;
gui.addLabel( new JLabel( "Output file", 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 );
ggParent1 = new PathField[ 1 ];
ggParent1[ 0 ] = ggInputFile;
ggOutputFile.deriveFrom( ggParent1, "$D0$F0Exc$E" );
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( "Post processing", 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 CrossOver", 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 ];
ggRollOff = new ParamField( spcHiCut );
ggRollOff.setReference( ggLoFreq );
con.weightx = 0.1;
gui.addLabel( new JLabel( "RollOff", SwingConstants.RIGHT ));
con.weightx = 0.4;
con.gridwidth = GridBagConstraints.REMAINDER;
gui.addParamField( ggRollOff, GG_ROLLOFF, null );
ggHiFreq = new ParamField( spcHiCut );
ggHiFreq.setReference( ggLoFreq );
con.weightx = 0.1;
con.gridwidth = 1;
gui.addLabel( new JLabel( "High CrossOver", SwingConstants.RIGHT ));
con.weightx = 0.4;
gui.addParamField( ggHiFreq, GG_HIFREQ, null );
ggBandsPerOct = new ParamField( new ParamSpace( 1.0, 256.0, 1.0, Param.NONE ));
con.weightx = 0.1;
gui.addLabel( new JLabel( "Bands per Oct.", SwingConstants.RIGHT ));
con.weightx = 0.4;
con.gridwidth = GridBagConstraints.REMAINDER;
gui.addParamField( ggBandsPerOct, GG_BANDSPEROCT, null );
ggFltLen = new JComboBox();
ggFltLen.addItem( "Short" );
ggFltLen.addItem( "Medium" );
ggFltLen.addItem( "Long" );
ggFltLen.addItem( "Very long" );
con.gridwidth = 1;
con.weightx = 0.1;
gui.addLabel( new JLabel( "Filter length", SwingConstants.RIGHT ));
con.weightx = 0.4;
gui.addChoice( ggFltLen, GG_FILTERLEN, null );
ggDryMix = new ParamField( Constants.spaces[ Constants.ratioAmpSpace ]);
con.weightx = 0.1;
gui.addLabel( new JLabel( "Dry mix", SwingConstants.RIGHT ));
con.weightx = 0.4;
con.gridwidth = GridBagConstraints.REMAINDER;
gui.addParamField( ggDryMix, GG_DRYMIX, null );
con.gridwidth = 2;
con.weightx = 0.1;
gui.addLabel( new JLabel() );
ggWetMix = new ParamField( Constants.spaces[ Constants.ratioAmpSpace ]);
gui.addLabel( new JLabel( "Wet mix", SwingConstants.RIGHT ));
con.weightx = 0.4;
con.gridwidth = GridBagConstraints.REMAINDER;
gui.addParamField( ggWetMix, GG_WETMIX, null );
initGUI( this, FLAGS_PRESETS | FLAGS_PROGBAR, gui );
}
public void fillGUI() {
super.fillGUI();
super.fillGUI(gui);
}
public void fillPropertyArray() {
super.fillPropertyArray();
super.fillPropertyArray(gui);
}
// -------- Processor Interface --------
protected void process() {
int i, j, k, m, len, off, ch, chunkLength, chunkLength2, band;
long progOff, progLen;
double d1, d2, d3, loFreq, hiFreq;
float f1, f2;
// io
AudioFile inF = null;
AudioFile outF = null;
AudioFileDescr inStream;
AudioFileDescr outStream;
FloatFile[] floatF = null;
File tempFile[] = null;
// buffers
float[] fftBuf1, fftBuf2, highpass;
float[][] fftBuf, fftBuf3;
float[] convBuf1, convBuf2;
float[] win;
float[][] inBuf;
float[][][] overBuf;
float[][] overBuf2;
// float[][] dcMem;
int[] peakLen;
int inChanNum, inLength, fftLength, inputLen;
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;
float dryGain, wetGain;
boolean hasDry;
PathField ggOutput;
int numPeriods;
int halfWinSize, fltLength, overLen, skip;
float[] crossFreqs, cosineFreqs;
double freqNorm, freqBase, cosineNorm, cosineBase, rollOff;
int numBands;
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;
// output
ggOutput = (PathField) gui.getItemObj( GG_OUTPUTFILE );
if( ggOutput == null ) throw new IOException( ERR_MISSINGPROP );
outStream = new AudioFileDescr( inStream );
ggOutput.fillStream( outStream );
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 = Param.transform( pr.para[ PR_HIFREQ ], Param.ABS_HZ, pr.para[ PR_LOFREQ ], null ).value;
rollOff = Param.transform( pr.para[ PR_ROLLOFF ], Param.ABS_HZ, pr.para[ PR_LOFREQ ], null ).value;
if( loFreq > rollOff ) {
d1 = loFreq;
loFreq = rollOff;
rollOff = d1;
}
d2 = Math.pow( 2.0, 1.0 / pr.para[ PR_BANDSPEROCT ].value);
dryGain = (float) (pr.para[ PR_DRYMIX ].value / 100);
wetGain = (float) (pr.para[ PR_WETMIX ].value / 100);
hasDry = dryGain > 0.0f;
numBands = (int) (Math.log( hiFreq / loFreq ) / Math.log( d2 ));
crossFreqs = new float[ numBands+1 ];
cosineFreqs = new float[ numBands+1 ];
d1 = loFreq;
d3 = d1 * d2;
for( i = 0; i <= numBands; i++ ) {
crossFreqs[i] = (float) d1;
cosineFreqs[i] = (float) (Math.sqrt( d3 * d1 ) - d1);
d1 = d3;
d3 = d1 * d2;
}
// for( i = 0; i <= numBands; i++ ) {
// System.out.println( i +" : cut "+crossFreqs[i]+"; roll "+cosineFreqs[i] );
// }
numPeriods = 3 << pr.intg[ PR_FILTERLEN ];
halfWinSize = Math.max( 1, (int) ((double) numPeriods * inStream.rate / crossFreqs[0] + 0.5) );
freqNorm = Constants.PI2 / inStream.rate;
cosineNorm = 4.0 / (Math.PI*Math.PI);
fltLength = halfWinSize + halfWinSize;
win = Filter.createFullWindow( fltLength, Filter.WIN_BLACKMAN );
j = fltLength + fltLength - 1;
for( fftLength = 2; fftLength < j; fftLength <<= 1 ) ;
inputLen = fftLength - fltLength + 1;
overLen = fftLength - inputLen;
// System.out.println( "halfWinSize "+halfWinSize+"; fltLength "+fltLength+"; fftLength "+fftLength+"; inputLen "+inputLen+"; overLen "+overLen+"; numBands "+numBands );
fftBuf = new float[ numBands ][ fftLength + 2 ];
fftBuf1 = new float[ fftLength + 2 ];
fftBuf2 = new float[ fftLength + 2 ];
fftBuf3 = new float[ inChanNum ][ fftLength + 2 ];
highpass = new float[ fftLength + 2 ];
inBuf = new float[ inChanNum ][ inputLen + fltLength ];
overBuf = new float[ inChanNum ][ numBands ][ overLen ];
overBuf2 = new float[ inChanNum ][ overLen ];
peakLen = new int[ numBands ];
for( i = 0; i < inChanNum; i++ ) {
Util.clear( overBuf[ i ]);
}
Util.clear( inBuf );
// 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 ----
for( i = 0; i < numBands; i++ ) {
convBuf1 = fftBuf[ i ];
freqBase = freqNorm * crossFreqs[ i+1 ];
cosineBase = freqNorm * cosineFreqs[ i+1 ];
for( j = 1; j < halfWinSize; j++ ) {
// sinc-filter
d1 = (Math.sin( freqBase * j ) / (double) j);
// raised cosine modulation
d2 = cosineNorm * cosineBase * j * cosineBase * j;
d1 *= (Math.cos( cosineBase * j ) / (1.0 - d2));
convBuf1[ halfWinSize + j ] = (float) d1;
convBuf1[ halfWinSize - j ] = (float) d1;
}
convBuf1[ halfWinSize ] = (float) freqBase;
freqBase = freqNorm * crossFreqs[ i ];
cosineBase = freqNorm * cosineFreqs[ i ];
for( j = 1; j < halfWinSize; j++ ) {
d1 = (Math.sin( freqBase * j ) / (double) j);
// raised cosine modulation
d2 = cosineNorm * cosineBase * j * cosineBase * j;
d1 *= (Math.cos( cosineBase * j ) / (1.0 - d2));
convBuf1[ halfWinSize + j ] -= (float) d1;
convBuf1[ halfWinSize - j ] -= (float) d1;
}
convBuf1[ halfWinSize ] -= (float) freqBase;
// zero padding
for( j = fltLength; j < fftLength; j++ ) {
convBuf1[ j ] = 0.0f;
}
// windowing
Util.mult( win, 0, convBuf1, 0, fltLength );
Fourier.realTransform( convBuf1, fftLength, Fourier.FORWARD );
// normalize
d1 = 0.0f;
for( j = 0; j < convBuf1.length; ) {
d2 = Math.sqrt( convBuf1[ j ]*convBuf1[ j++ ]+convBuf1[ j ]*convBuf1[ j++ ]);
if( d2 > d1 ) {
d1 = d2;
}
}
// System.out.println( d1 );
f1 = (float) (1.0 / Math.sqrt( d1 ));
Util.mult( convBuf1, 0, convBuf1.length, f1 );
// peakLen is the length of 1.5 periods of a band's frequency
// it is the length used for peak scanning
peakLen[ i ] = Math.min( overLen, (int) (inStream.rate / crossFreqs[ i ] * 1.5f + 0.5f) );
}
// ---- calc highpass filter ----
freqBase = freqNorm * (inStream.rate/2);
highpass[ halfWinSize ] = (float) freqBase;
freqBase = freqNorm * rollOff; // Math.sqrt( rollOff * loFreq ); // ((rollOff + loFreq) / 2 ); // ? XXX
cosineBase = freqNorm * (rollOff - loFreq);
for( j = 1; j < halfWinSize; j++ ) {
d1 = (Math.sin( freqBase * j ) / (double) j);
// raised cosine modulation
d2 = cosineNorm * cosineBase * j * cosineBase * j;
d1 *= (Math.cos( cosineBase * j ) / (1.0 - d2));
highpass[ halfWinSize + j ] -= (float) d1;
highpass[ halfWinSize - j ] -= (float) d1;
}
highpass[ halfWinSize ] -= (float) freqBase;
// zero padding
for( j = fltLength; j < fftLength; j++ ) {
highpass[ j ] = 0.0f;
}
// windowing
Util.mult( win, 0, highpass, 0, fltLength );
Fourier.realTransform( highpass, fftLength, Fourier.FORWARD );
// normalize
d1 = 0.0f;
for( j = 0; j < highpass.length; ) {
d2 = Math.sqrt( highpass[ j ]*highpass[ j++ ]+highpass[ j ]*highpass[ j++ ]);
if( d2 > d1 ) {
d1 = d2;
}
}
f1 = (float) (0.25 / Math.sqrt( d1 )); // 1.0
Util.mult( highpass, 0, highpass.length, f1 );
// for( i = 0; i < fftLength; i++ ) {
// reBuf[ 0 ][ i ] = (float) Math.sqrt( fftBuf1[ i<<1 ]*fftBuf1[ i<<1 ]+fftBuf1[ (i<<1)+1 ]*fftBuf1[ (i<<1)+1 ]);
// }
// Debug.view( reBuf[ 0 ], "hilbert cmplx fft" );
// DC block init
// dcMem = new float[ inChanNum ][ 2 ];
// Util.clear( dcMem );
progOff = 0;
progLen = (long) inLength * (2 + inChanNum + inChanNum);
// 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 += (long) inLength;
} else { // account for gain loss RealFFT => CmplxIFFT
gain = (float) ((Param.transform( pr.para[ PR_GAIN ], Param.ABS_AMP, ampRef, null )).value);
dryGain *= gain;
wetGain *= gain;
}
// .... check running ....
if( !threadRunning ) break topLevel;
// ----==================== the real stuff ====================----
framesRead = 0;
framesWritten = 0;
// skip = halfWinSize;
skip = halfWinSize; // 1x bandpass filter, 2x complete highpass
while( threadRunning && (framesWritten < inLength) ) {
chunkLength = Math.min( inputLen, inLength - framesRead );
// ---- read input chunk ----
for( off = 0; threadRunning && (off < chunkLength); ) {
len = Math.min( 8192, chunkLength - off );
inF.readFrames( inBuf, off + fltLength, len );
framesRead += len;
progOff += len;
off += len;
// .... progress ....
setProgression( (float) progOff / (float) progLen );
}
// .... check running ....
if( !threadRunning ) break topLevel;
chunkLength2 = Math.min( inputLen, inLength - framesWritten );
// if( floatF == null ) {
// for( ch = 0; ch < inChanNum; ch++ ) {
// Util.mult( inBuf[ ch ], 0, chunkLength2, gain );
// }
// }
// ---- channels loop -----------------------------------------------------------------------
for( ch = 0; threadRunning && (ch < inChanNum); ch++ ) {
convBuf1 = fftBuf3[ ch ];
convBuf2 = inBuf[ ch ];
Util.clear( convBuf1 );
System.arraycopy( convBuf2, fltLength, fftBuf1, 0, chunkLength );
for( i = chunkLength; i < fftLength; i++ ) {
fftBuf1[ i ] = 0.0f;
}
Fourier.realTransform( fftBuf1, fftLength, Fourier.FORWARD );
// ---- bands loop ----------------------------------------------------------------------
for( band = 0; threadRunning && (band < numBands); band++ ) {
Fourier.complexMult( fftBuf1, 0, fftBuf[ band ], 0, fftBuf2, 0, fftBuf1.length );
Fourier.realTransform( fftBuf2, fftLength, Fourier.INVERSE );
Util.add( overBuf[ ch ][ band ], 0, fftBuf2, 0, overLen );
System.arraycopy( fftBuf2, inputLen, overBuf[ ch ][ band ], 0, overLen );
// 020220 begin
// d1 = Math.sqrt( crossFreqs[ band ] * crossFreqs[ band+1 ]) * freqNorm;
m = -1;
f1 = 0.0f;
for( i = 0, k = peakLen[ band ]; i < chunkLength2; i++, k++ ) {
if( i > m ) {
f1 = 0.0f;
}
// peak scan; we reuse old max (f1) if it still lies in our search area (i <= m)
for( j = m + 1; j < k; j++ ) {
f2 = Math.abs( fftBuf2[ j ]);
if( f2 >= f1 ) {
f1 = f2;
m = j;
}
}
if( f1 > 0.0f ) {
convBuf1[ i ] += fftBuf2[ i ]*fftBuf2[ i ] / f1; // self modulated + gain adjusted
}
}
// for( i = 0, j = framesWritten; i < chunkLength2; i++, j++ ) {
// for( i = 0; i < chunkLength2; i++ ) {
// convBuf1[ i ] += fftBuf2[ i ]*fftBuf2[ i ]; // (float) (fftBuf2[ i ] * Math.cos( d1 * j ));
// }
// 020220 end
/* 020220 removed:
f1 = 0.0f;
f3 = peakBuf[ ch ][ band ];
decay = 0.0;
decayNum= 1;
for( i = 0; i < chunkLength2; i++ ) {
f2 = Math.abs( fftBuf2[ i ]);
if( f2 > f1 ) {
f1 = f2;
if( f1 > f3 ) {
d1 = ((double) f1 - (double) f3) / (i + 1);
if( d1 > decay ) {
decay = d1;
decayNum = i + 1;
}
}
}
}
if( f1 > 1.0e-3f ) {
if( f1 < f3 ) {
decay = ((double) f1 - (double) f3) / chunkLength2;
decayNum= chunkLength2;
}
// decay * chunkLength2 + f1
decay = Math.min( 0.0227, Math.max( -0.0227, decay )); // max -1 dB/mSec
d2 = (double) f3;
for( i = 0; i < chunkLength2; i++ ) {
if( decayNum-- > 0 ) {
d2 += decay;
}
if( d2 > 1.0e-3 ) {
d3 = 0.5 / d2;
d1 = (double) fftBuf2[ i ] * d3 + 0.5;
convBuf1[ i ] += (float) ((Math.min( 2.0, (d1 * d1) * 2 ) - 1.0) * d2);
} else {
convBuf1[ i ] += fftBuf2[ i ];
}
}
peakBuf[ ch ][ band ] = (float) d2;
} else {
// XXX knackser bei 0 <= f1 <= 1.0e-3 ?!
Util.add( fftBuf2, 0, convBuf1, 0, chunkLength2 );
// peakBuf[ ch ][ band ] = f1;
}
*/
} // for bands
// ---- remove DC ---- adapted from CSound
// if( pr.bool[ PR_DCBLOCK ]) {
// convBuf2 = dcMem[ ch ];
// for( i = 0; i < chunkLength2; i++ ) {
// f1 = convBuf1[ i ]; // X1
// convBuf1[ i ] = f1 - convBuf2[ 0 ] + 0.99f * convBuf2[ 1 ]; // Y1 = X1-X0+Y0*gain
// convBuf2[ 0 ] = f1; // next X0
// convBuf2[ 1 ] = convBuf1[ i ]; // next Y0
// }
// }
// ---- highpass ----
Fourier.realTransform( convBuf1, fftLength, Fourier.FORWARD );
Fourier.complexMult( convBuf1, 0, highpass, 0, convBuf1, 0, highpass.length );
Fourier.realTransform( convBuf1, fftLength, Fourier.INVERSE );
Util.add( overBuf2[ ch ], 0, convBuf1, 0, overLen );
System.arraycopy( convBuf1, inputLen, overBuf2[ ch ], 0, overLen );
// ---- dry/wet ----
if( hasDry ) {
for( i = 0; i < chunkLength2; i++ ) {
convBuf1[ i ] = convBuf1[ i ] * wetGain + convBuf2[ i ] * dryGain;
}
// inBuf always delayed by 2x halfWin to get correct dry/wet synchro
System.arraycopy( convBuf2, chunkLength2, convBuf2, 0, fltLength );
} else if( wetGain != 1.0f ) {
Util.mult( convBuf1, 0, chunkLength2, wetGain );
}
progOff += chunkLength2 + chunkLength2;
// .... progress ....
setProgression( (float) progOff / (float) progLen );
} // for chanNum
// .... check running ....
if( !threadRunning ) break topLevel;
for( off = skip; threadRunning && (off < chunkLength2); ) {
len = Math.min( 8192, chunkLength2 - off );
if( floatF != null ) {
for( ch = 0; ch < inChanNum; ch++ ) {
floatF[ ch ].writeFloats( fftBuf3[ ch ], off, len );
}
} else {
outF.writeFrames( fftBuf3, off, len );
}
progOff += len;
off += len;
// .... progress ....
setProgression( (float) progOff / (float) progLen );
}
for( ch = 0; ch < inChanNum; ch++ ) {
convBuf1 = fftBuf3[ ch ];
for( off = skip; off < chunkLength2; off++ ) {
f1 = Math.abs( convBuf1[ off ]);
if( f1 > maxAmp ) {
maxAmp = f1;
}
}
}
framesWritten += Math.max( 0, chunkLength2 - skip );
if( skip > 0 ) {
skip = Math.max( 0, skip - 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, fftBuf3, 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;
fftBuf1 = null;
fftBuf2 = null;
fftBuf3 = null;
fftBuf = null;
highpass = null;
// peakBuf = null;
// inform about clipping/ low level
handleClipping( maxAmp );
}
catch( IOException e1 ) {
setError( e1 );
}
catch( OutOfMemoryError e2 ) {
inStream = null;
outStream = null;
fftBuf1 = null;
fftBuf2 = null;
fftBuf3 = null;
fftBuf = null;
highpass = null;
// peakBuf = null;
convBuf1 = null;
System.gc();
setError( new Exception( ERR_MEMORY ));
}
// ---- cleanup (topLevel) ----
if( inF != null ) {
inF.cleanUp();
inF = 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()
}