/*
* DeclickDlg.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.Application;
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.io.AudioFile;
import de.sciss.io.AudioFileDescr;
import de.sciss.io.IOUtil;
import de.sciss.io.Marker;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.io.EOFException;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.Vector;
/**
* Rather strange processing module for sound file declicking
* which is not really too successful.
*/
public class DeclickDlg
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_IMPULSEFILE = 2;
private static final int PR_OUTPUTTYPE = 0; // pr.intg
private static final int PR_OUTPUTRES = 1;
private static final int PR_CHECKSIZE = 2;
private static final int PR_XFADE = 3;
private static final int PR_PROBBOUND = 4;
private static final int PR_MINAMP = 0; // pr.para
private static final int PR_FSCAPEDETECT = 0; // pr.bool
private static final int PR_FSCAPEREPAIR = 1;
private static final int PR_MARKERDETECT = 2;
private static final int PR_MARKERREPAIR = 3;
private static final int CHECK_16 = 0;
private static final int CHECK_32 = 1;
private static final int CHECK_64 = 2;
private static final int XFADE_512 = 0;
private static final int XFADE_1024 = 1;
private static final int XFADE_2048 = 2;
private static final int PROB_5PM = 0;
private static final int PROB_1PM = 1;
private static final int PROB_05PM = 2;
private static final String PRN_INPUTFILE = "InputFile";
private static final String PRN_OUTPUTFILE = "OutputFile";
private static final String PRN_IMPULSEFILE = "ImpFile";
private static final String PRN_OUTPUTTYPE = "OutputType";
private static final String PRN_OUTPUTRES = "OutputReso";
private static final String PRN_CHECKSIZE = "CheckSize";
private static final String PRN_XFADE = "XFade";
private static final String PRN_PROBBOUND = "ProbBound";
private static final String PRN_MINAMP = "MinAmp";
private static final String PRN_FSCAPEDETECT = "FScDetect";
private static final String PRN_FSCAPEREPAIR = "FScRepair";
private static final String PRN_MARKERDETECT = "MarkDetect";
private static final String PRN_MARKERREPAIR = "MarkRepair";
private static final String prText[] = { "", "", null };
private static final String prTextName[] = { PRN_INPUTFILE, PRN_OUTPUTFILE, PRN_IMPULSEFILE };
private static final int prIntg[] = { 0, 0, CHECK_32, XFADE_1024, PROB_1PM };
private static final String prIntgName[] = { PRN_OUTPUTTYPE, PRN_OUTPUTRES, PRN_CHECKSIZE,
PRN_XFADE, PRN_PROBBOUND };
private static final Param prPara[] = { null };
private static final String prParaName[] = { PRN_MINAMP };
private static final boolean prBool[] = { true, true, true, true };
private static final String prBoolName[] = { PRN_FSCAPEDETECT, PRN_FSCAPEREPAIR,
PRN_MARKERDETECT, PRN_MARKERREPAIR };
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_IMPULSEFILE = GG_OFF_PATHFIELD + PR_IMPULSEFILE;
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_CHECKSIZE = GG_OFF_CHOICE + PR_CHECKSIZE;
private static final int GG_XFADE = GG_OFF_CHOICE + PR_XFADE;
private static final int GG_PROBBOUND = GG_OFF_CHOICE + PR_PROBBOUND;
private static final int GG_MINAMP = GG_OFF_PARAMFIELD + PR_MINAMP;
private static final int GG_FSCAPEDETECT = GG_OFF_CHECKBOX + PR_FSCAPEDETECT;
private static final int GG_FSCAPEREPAIR = GG_OFF_CHECKBOX + PR_FSCAPEREPAIR;
private static final int GG_MARKERDETECT = GG_OFF_CHECKBOX + PR_MARKERDETECT;
private static final int GG_MARKERREPAIR = GG_OFF_CHECKBOX + PR_MARKERREPAIR;
private static PropertyArray static_pr = null;
private static Presets static_presets = null;
private JLabel lbClicks;
private static final String MARK_CLICK = "Click";
// -------- public methods --------
/**
* !! setVisible() bleibt dem Aufrufer ueberlassen
*/
public DeclickDlg()
{
super( "Declick" );
init2();
}
protected void buildGUI()
{
// einmalig PropertyArray initialisieren
if( static_pr == null ) {
static_pr = new PropertyArray();
static_pr.text = prText;
prText[PR_IMPULSEFILE] = new File(new File(Application.installDir, "sounds"), "declickIR.aif").getPath();
static_pr.textName = prTextName;
static_pr.intg = prIntg;
static_pr.intgName = prIntgName;
static_pr.bool = prBool;
static_pr.boolName = prBoolName;
static_pr.para = prPara;
static_pr.para[ PR_MINAMP ] = new Param( -60.0, Param.DECIBEL_AMP );
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, ggImpulseFile;
PathField ggInputs[];
JComboBox ggCheckSize, ggProbBound, ggXFade;
ParamField ggMinAmp;
ParamSpace spcMinAmp;
JCheckBox ggFScapeDetect, ggMarkerDetect, ggFScapeRepair, ggMarkerRepair;
gui = new GUISupport();
con = gui.getGridBagConstraints();
con.insets = new Insets( 1, 2, 1, 2 );
ItemListener il = new ItemListener() {
public void itemStateChanged( ItemEvent e )
{
int ID = gui.getItemID( e );
switch( ID ) {
case GG_FSCAPEDETECT:
case GG_FSCAPEREPAIR:
pr.bool[ ID - GG_OFF_CHECKBOX ] = ((JCheckBox) e.getSource()).isSelected();
reflectPropertyChanges();
break;
}
}
};
// -------- 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 );
ggInputs = new PathField[ 1 ];
ggInputs[ 0 ] = ggInputFile;
ggOutputFile.deriveFrom( ggInputs, "$D0$F0DClk$E" );
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 );
// -------- Detect-Gadgets --------
gui.addLabel( new GroupLabel( "Detection Settings", GroupLabel.ORIENT_HORIZONTAL,
GroupLabel.BRACE_NONE ));
ggFScapeDetect = new JCheckBox();
con.weightx = 0.1;
con.gridwidth = 1;
gui.addLabel( new JLabel( "Let FScape detect", SwingConstants.RIGHT ));
gui.addCheckbox( ggFScapeDetect, GG_FSCAPEDETECT, il );
ggCheckSize = new JComboBox();
ggCheckSize.addItem( "16" );
ggCheckSize.addItem( "32" );
ggCheckSize.addItem( "64" );
con.weightx = 0.1;
gui.addLabel( new JLabel( "Check size [smp]", SwingConstants.RIGHT ));
con.weightx = 0.4;
con.gridwidth = GridBagConstraints.REMAINDER;
gui.addChoice( ggCheckSize, GG_CHECKSIZE, il );
ggMarkerDetect = new JCheckBox();
con.weightx = 0.1;
con.gridwidth = 1;
gui.addLabel( new JLabel( "Read markers", SwingConstants.RIGHT ));
gui.addCheckbox( ggMarkerDetect, GG_MARKERDETECT, il );
ggProbBound = new JComboBox();
ggProbBound.addItem( "1 : 200" );
ggProbBound.addItem( "1 : 1000" );
ggProbBound.addItem( "1 : 2000" );
con.weightx = 0.1;
gui.addLabel( new JLabel( "Probablity bound", SwingConstants.RIGHT ));
con.weightx = 0.4;
con.gridwidth = GridBagConstraints.REMAINDER;
gui.addChoice( ggProbBound, GG_PROBBOUND, il );
spcMinAmp = new ParamSpace( Constants.minDecibel, 0.0, 0.1, Param.DECIBEL_AMP );
ggMinAmp = new ParamField( spcMinAmp );
con.weightx = 0.1;
con.gridwidth = 3;
gui.addLabel( new JLabel( "Min. amp.", SwingConstants.RIGHT ));
con.weightx = 0.4;
con.gridwidth = GridBagConstraints.REMAINDER;
gui.addParamField( ggMinAmp, GG_MINAMP, null );
// -------- Repair-Gadgets --------
gui.addLabel( new GroupLabel( "Repair Settings", GroupLabel.ORIENT_HORIZONTAL,
GroupLabel.BRACE_NONE ));
ggFScapeRepair = new JCheckBox();
con.weightx = 0.1;
con.gridwidth = 1;
gui.addLabel( new JLabel( "Let FScape repair", SwingConstants.RIGHT ));
gui.addCheckbox( ggFScapeRepair, GG_FSCAPEREPAIR, il );
ggXFade = new JComboBox();
ggXFade.addItem( "512" );
ggXFade.addItem( "1024" );
ggXFade.addItem( "2048" );
con.weightx = 0.1;
gui.addLabel( new JLabel( "XFade size [smp]", SwingConstants.RIGHT ));
con.weightx = 0.4;
con.gridwidth = GridBagConstraints.REMAINDER;
gui.addChoice( ggXFade, GG_XFADE, il );
ggMarkerRepair = new JCheckBox();
con.weightx = 0.1;
con.gridwidth = 1;
gui.addLabel( new JLabel( "Write markers", SwingConstants.RIGHT ));
con.gridwidth = GridBagConstraints.REMAINDER;
gui.addCheckbox( ggMarkerRepair, GG_MARKERREPAIR, il );
ggImpulseFile = new PathField( PathField.TYPE_INPUTFILE + PathField.TYPE_FORMATFIELD,
"Select IR file" );
ggImpulseFile.handleTypes( GenericFile.TYPES_SOUND );
con.gridwidth = 1;
con.weightx = 0.1;
gui.addLabel( new JLabel( "Impulse response", SwingConstants.RIGHT ));
con.gridwidth = GridBagConstraints.REMAINDER;
con.weightx = 0.9;
gui.addPathField( ggImpulseFile, GG_IMPULSEFILE, null );
// -------- Click-View --------
gui.addLabel(new GroupLabel("Click View", GroupLabel.ORIENT_HORIZONTAL,
GroupLabel.BRACE_NONE));
lbClicks = new JLabel(" ");
// lbClicks.setBackground(Color.white);
gui.addLabel(lbClicks);
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 --------
/**
* Gain-Change durchfuehren
*/
public void process()
{
int i, j, k;
int off, chunkLength;
int ch;
long progOff, progLen;
float f1;
// io
AudioFile inF = null;
AudioFile outF = null;
AudioFile impF = null;
FloatFile[] floatF = null;
File[] tempFile = null;
AudioFileDescr inStream = null;
AudioFileDescr outStream = null;
AudioFileDescr impStream = null;
int inChanNum;
int impChanNum = 0;
float[][] inBuf;
float[][] impBuf = null;
float[] zData;
float[] fftBuf;
float[] win;
float[] convBuf1, convBuf2;
PathField ggOutput;
float mean;
float stddev;
float critical;
float probBound;
Param ampRef = new Param( 1.0, Param.ABS_AMP ); // transform-Referenz
float minAmp;
int checkOff, checkInner, checkLen;
int chunkMin, chunkMax, chunkNum, chunkDone;
int fadeLen, prePost, frameSize, inOff;
int firstClk, lastClk;
int markerID = -1;
Marker marker = null;
java.util.List<Marker> markers, outMarkers;
Graphics g = null; // we use a JLabel to draw to click-positions
Dimension gDim;
int x;
int inLength;
int outLength = 0;
int impLength = 0; // reichen 32 bit?
int framesRead, framesWritten;
topLevel:
try {
// ---- init ----
minAmp = (float) (Param.transform( pr.para[ PR_MINAMP ], Param.ABS_AMP, ampRef, null )).value;
switch( pr.intg[ PR_PROBBOUND ]) {
case PROB_5PM:
probBound = 2.58f;
break;
case PROB_1PM:
probBound = 3.10f;
break;
default:
case PROB_05PM:
probBound = 3.30f;
break;
}
g = lbClicks.getGraphics();
gDim = lbClicks.getSize();
if (g != null) {
lbClicks.repaint();
g.setColor(Color.red);
}
switch( pr.intg[ PR_CHECKSIZE ]) {
case CHECK_16:
checkLen = 16;
break;
case CHECK_32:
checkLen = 32;
break;
default:
case CHECK_64:
checkLen = 64;
break;
}
switch( pr.intg[ PR_XFADE ]) {
case XFADE_512:
fadeLen = 512;
break;
case XFADE_1024:
fadeLen = 1024;
break;
default:
case XFADE_2048:
fadeLen = 2048;
break;
}
prePost = fadeLen >> 1;
frameSize = prePost + checkLen/4 + checkLen/2 + checkLen/4 + prePost; // !! inBuf Aufbau !!
checkOff = prePost + (checkLen >> 2);
checkInner = checkLen >> 1;
// checkEnd = checkOff + checkInner;
inOff = frameSize - checkInner;
// ---- open and prepare files ----
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 );
markers = (java.util.List<Marker>) inStream.getProperty( AudioFileDescr.KEY_MARKERS );
if( markers != null ) {
outMarkers = new Vector<Marker>( markers );
} else {
outMarkers = new Vector<Marker>();
}
if( pr.bool[ PR_FSCAPEREPAIR ] || pr.bool[ PR_MARKERREPAIR ]) {
impF = AudioFile.openAsRead( new File( pr.text[ PR_IMPULSEFILE ]));
impStream = impF.getDescr();
impChanNum = impStream.channels;
impLength = (int) Math.min( impStream.length, prePost );
impBuf = new float[ impChanNum ][ fadeLen + 2 ];
for( ch = 0; ch < impChanNum; ch++ ) {
convBuf1 = impBuf[ ch ];
for( i = 0; i < convBuf1.length; i++ ) {
convBuf1[ i ] = 0.0f;
}
}
ggOutput = (PathField) gui.getItemObj( GG_OUTPUTFILE );
if( ggOutput == null ) throw new IOException( ERR_MISSINGPROP );
outStream = new AudioFileDescr( inStream );
ggOutput.fillStream( outStream );
IOUtil.createEmptyFile( new File( pr.text[ PR_OUTPUTFILE ]));
outLength = inLength;
}
// .... check running ....
if( !threadRunning ) break topLevel;
inBuf = new float[ inChanNum ][ frameSize ];
zData = new float[ checkLen ];
fftBuf = new float[ fadeLen + 2 ];
chunkMin = frameSize / checkInner; // frameSize must be durch checkInner teilbar!!!
chunkNum = ((inLength + checkInner - 1) / checkInner) + (chunkMin - 1);
chunkMax = chunkNum - chunkMin;
chunkDone = 0;
win = Filter.createWindow( prePost, Filter.WIN_BLACKMAN );
for( ch = 0; ch < inChanNum; ch++ ) {
convBuf1 = inBuf[ ch ];
for( i = 0; i < frameSize; i++ ) {
convBuf1[ i ] = 0.0f;
}
}
for( i = 0; i < fftBuf.length; i++ ) {
fftBuf[ i ] = 0.0f;
}
// get first click mark
if( pr.bool[ PR_MARKERDETECT ] && markers != null ) {
Collections.sort( markers );
// markers = Marker.sort( markers );
markerID = Marker.find( markers, MARK_CLICK, 0 );
if( markerID >= 0 ) {
marker = markers.get( markerID );
}
}
progOff = 0;
progLen = (long) inLength + (long) outLength + (long) (chunkNum * checkInner);
// if we do not create markers then open output now
if( pr.bool[ PR_FSCAPEREPAIR ] && !pr.bool[ PR_MARKERREPAIR ]) {
outF = AudioFile.openAsWrite( outStream );
}
// else open temp file
if( pr.bool[ PR_MARKERREPAIR ]) {
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) outLength; // account for extra reads/writes
}
// .... check running ....
if( !threadRunning ) break topLevel;
//System.out.println( "frameSize "+frameSize+"; checkInner "+checkInner+"; chunkMin "+chunkMin );
// ---- read impulse ----
if( impF != null ) {
progLen += (long) impLength;
impF.readFrames( impBuf, 0, impLength );
// forward fft
for( ch = 0; ch < impChanNum; ch++ ) {
Fourier.realTransform( impBuf[ ch ], fadeLen, Fourier.FORWARD );
}
impF.close();
impF = null;
progOff += impLength;
// .... progress ....
setProgression( (float) progOff / (float) progLen );
}
// .... check running ....
if( !threadRunning ) break topLevel;
// ---- process loop ----
framesRead = 0;
framesWritten = 0;
while( threadRunning && (chunkDone < chunkNum) ) {
for( off = 0, chunkLength = Math.min( inLength - framesRead, checkInner );
threadRunning && (off < chunkLength); ) {
//System.out.println(" Read "+chunkLength);
j = Math.min( 8192, chunkLength - off );
inF.readFrames( inBuf, off + inOff, j );
framesRead += j;
off += j;
progOff += j;
// .... progress ....
setProgression( (float) progOff / (float) progLen );
}
// .... check running ....
if( !threadRunning ) break topLevel;
// zero-padding
for( ch = 0; ch < inChanNum; ch++ ) {
convBuf1 = inBuf[ ch ];
for( i = chunkLength + inOff; i < frameSize; i++ ) {
convBuf1[ i ] = 0.0f;
}
}
//System.out.println(" Chck ");
// check for clicks
firstClk = checkInner; // "no clicks"
lastClk = -1;
if( pr.bool[ PR_FSCAPEDETECT ]) {
for( ch = 0; ch < inChanNum; ch++ ) {
convBuf1 = inBuf[ ch ];
mean = 0f;
for( i = prePost, j = 0; j < checkLen; i++, j++ ) { // 1. Sample-Veraenderung u. Mittelwert
zData[ j ] = convBuf1[ i + 1 ] - convBuf1[ i ];
mean += zData[ j ];
}
mean /= checkLen;
stddev = 0f;
for( j = 0; j < checkLen; j++ ) { // 2. Standardabweichung
f1 = zData[ j ] - mean;
stddev += f1*f1;
}
stddev = (float) Math.sqrt( stddev / checkLen );
critical = probBound * stddev;
//System.out.println( "mean "+mean+"; stddev "+stddev+"; crit "+critical );
for( i = 0, j = (checkLen >> 2) - 1; i < checkInner; i++, j++ ) { // 3. z-normalisierte Veraenderung pruefen
if( (Math.abs( zData[ j ] - mean ) >= critical) &&
(Math.abs( zData[ j ]) >= minAmp) ) {
firstClk = Math.min( firstClk, i );
lastClk = Math.max( lastClk, i );
if( pr.bool[ PR_MARKERREPAIR ]) {
outMarkers.add( new Marker( (chunkDone-chunkMin+1)*checkInner +
i + checkOff, MARK_CLICK ));
}
}
}
}
}
if( pr.bool[ PR_MARKERDETECT ]) {
i = (chunkDone-chunkMin+1)*checkInner + checkOff;
while( marker != null ) {
if( (marker.pos >= i) && (marker.pos < (i + checkInner)) ) {
firstClk = Math.min( firstClk, (int) marker.pos - i );
lastClk = Math.max( lastClk, (int) marker.pos - i );
markerID = Marker.find( markers, MARK_CLICK, markerID + 1 );
if( markerID >= 0 ) {
marker = markers.get( markerID );
} else {
marker = null;
}
} else if( marker.pos < i ) { // "veraltet"
markerID = Marker.find( markers, MARK_CLICK, markerID + 1 );
if( markerID >= 0 ) {
marker = markers.get( markerID );
} else {
marker = null;
}
} else break; // noch zu frueh
}
}
// declick
declick: if( (lastClk >= 0) && (chunkDone >= chunkMin) && (chunkDone <= chunkMax) ) {
// visualize
if( g != null ) {
x = (int) (((float) chunkDone / (float) chunkNum) * gDim.width + 0.5f);
g.drawLine( x, 0, x, gDim.height - 1 );
}
if( !pr.bool[ PR_FSCAPEREPAIR ]) break declick;
for( ch = 0; ch < inChanNum; ch++ ) {
convBuf1 = inBuf[ ch ];
// copy left wing
System.arraycopy( convBuf1, firstClk + checkOff - prePost, fftBuf, 0, prePost );
// zero padding
for( i = prePost; i < fadeLen; i++ ) {
fftBuf[ i ] = 0.0f;
}
// apply fadein/fadeout
for( i = 0, j = prePost - 1; j >= 0; i++, j -= 2 ) {
fftBuf[ i ] *= win[ j ];
}
for( j = 0; j < prePost; i++, j += 2 ) {
fftBuf[ i ] *= win[ j ];
}
// forward transform
Fourier.realTransform( fftBuf, fadeLen, Fourier.FORWARD );
// convolve
convBuf2 = impBuf[ ch % impChanNum ];
for( i = 0; i <= fadeLen; i += 2 ) { // complex multiplication
j = i + 1;
f1 = fftBuf[ i ]; // (avoid overwrite)
fftBuf[ i ] = f1 * convBuf2[ i ] - fftBuf[ j ] * convBuf2[ j ];
fftBuf[ j ] = f1 * convBuf2[ j ] + fftBuf[ j ] * convBuf2[ i ];
}
// inverse transform
Fourier.realTransform( fftBuf, fadeLen, Fourier.INVERSE );
// xfade left wing
for( j = (prePost>>1), i = firstClk + checkOff - j, k = 0; j < prePost; i++, j++, k += 2 ) {
f1 = win[ k ];
convBuf1[ i ] = f1 * convBuf1[ i ] + (1.0f - f1) * fftBuf[ j ];
}
// overwrite clip part
for( k = lastClk + checkOff; i <= k; i++, j++ ) {
convBuf1[ i ] = fftBuf[ j ];
}
// xfade right wing
for( k = 0; k < prePost; k += 2, i++, j++ ) {
f1 = win[ k ];
convBuf1[ i ] = (1.0f - f1) * convBuf1[ i ] + f1 * fftBuf[ j ];
}
/* // apply fadeout to convolved data
for( i = prePost, j = 0; i < fadeLen; i++, j++ ) {
fftBuf[ i ] *= win[ j ];
}
// apply fadeout/fadein to orig. input ("dip")
for( i = firstClk + checkOff - prePost + 1, k = lastClk + checkOff + prePost - 1,
j = 0; j < prePost; i++, j++, k-- ) {
f1 = win[ j ];
convBuf1[ i ] *= f1;
convBuf1[ k ] *= f1;
}
for( ; i < k; i++ ) {
convBuf1[ i ] = 0.0f;
}
// add convolved chunk to orig. "dipped" data
for( i = firstClk + checkOff - prePost, j = 0; j < fadeLen; i++, j++ ) {
convBuf1[ i ] += fftBuf[ j ];
}
*/ } // for chan, declick
}
chunkDone++;
progOff += checkInner;
// .... progress ....
setProgression( (float) progOff / (float) progLen );
// .... check running ....
if( !threadRunning ) break topLevel;
// write output
if( chunkDone >= chunkMin ) {
chunkLength = Math.min( outLength - framesWritten, checkInner );
if( floatF != null ) {
// ...to temp
for( ch = 0; ch < inChanNum; ch++ ) {
floatF[ ch ].writeFloats( inBuf[ ch ], 0, chunkLength );
}
framesWritten += chunkLength;
off += chunkLength;
progOff += chunkLength;
// .... progress ....
setProgression( (float) progOff / (float) progLen );
} else if( outF != null ) {
// ...directly
for( off = 0; threadRunning && (off < chunkLength); ) {
j = Math.min( 8192, chunkLength - off );
outF.writeFrames( inBuf, off, j );
framesWritten += j;
off += j;
progOff += j;
// .... progress ....
setProgression( (float) progOff / (float) progLen );
}
}
}
// shift data
for( ch = 0; ch < inChanNum; ch++ ) {
convBuf1 = inBuf[ ch ];
System.arraycopy( convBuf1, checkInner, convBuf1, 0, frameSize - checkInner );
}
}
// .... check running ....
if( !threadRunning ) break topLevel;
// ---- copy temp => output ----
if( floatF != null ) {
Collections.sort( outMarkers );
if( outMarkers.size() > 0xFFFF ) {
System.out.println( "WARNING: too many markers (" + outMarkers.size() + "). Truncating to 65535!" );
while( outMarkers.size() > 0xFFFF ) outMarkers.remove( 0xFFFF );
}
outStream.setProperty( AudioFileDescr.KEY_MARKERS, outMarkers );
// outStream.setProperty( AudioFileDescr.KEY_MARKERS, Marker.sort( outMarkers ));
outF = AudioFile.openAsWrite( outStream );
normalizeAudioFile( floatF, outF, inBuf, 1.0f, 1.0f );
for( ch = 0; ch < inChanNum; ch++ ) {
floatF[ ch ].cleanUp();
floatF[ ch ] = null;
tempFile[ ch ].delete();
tempFile[ ch ] = null;
}
}
// ---- Finish ----
if( outF != null ) {
outF.close();
outF = null;
}
inF.close();
inF = null;
}
catch( IOException e1 ) {
setError( e1 );
}
catch( OutOfMemoryError e2 ) {
inStream = null;
outStream = null;
impStream = null;
inBuf = null;
zData = null;
fftBuf = null;
win = null;
System.gc();
setError( new Exception( ERR_MEMORY ));
}
// ---- cleanup (topLevel) ----
if( g != null ) {
g.dispose();
}
if( inF != null ) {
inF.cleanUp();
}
if( outF != null ) {
outF.cleanUp();
}
if( impF != null ) {
impF.cleanUp();
impF = null;
}
if( floatF != null ) {
for( ch = 0; ch < floatF.length; ch++ ) {
if( floatF[ ch ] != null ) floatF[ ch ].cleanUp();
if( tempFile[ ch ] != null ) tempFile[ ch ].delete();
}
}
} // process()
// -------- private methods --------
protected void reflectPropertyChanges()
{
super.reflectPropertyChanges();
Component c;
c = gui.getItemObj( GG_CHECKSIZE );
if( c != null ) {
c.setEnabled( pr.bool[ PR_FSCAPEDETECT ]);
}
c = gui.getItemObj( GG_PROBBOUND );
if( c != null ) {
c.setEnabled( pr.bool[ PR_FSCAPEDETECT ]);
}
c = gui.getItemObj( GG_MINAMP );
if( c != null ) {
c.setEnabled( pr.bool[ PR_FSCAPEDETECT ]);
}
c = gui.getItemObj( GG_XFADE );
if( c != null ) {
c.setEnabled( pr.bool[ PR_FSCAPEREPAIR ]);
}
c = gui.getItemObj( GG_IMPULSEFILE );
if( c != null ) {
c.setEnabled( pr.bool[ PR_FSCAPEREPAIR ]);
}
}
}