/* * ConvolutionDlg.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 * * * Changelog: * 11-Mar-05 added minimum phase option * 28-Aug-06 provisorischer fix fuer min phase delay spike */ 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.spect.SpectStream; import de.sciss.fscape.util.Constants; import de.sciss.fscape.util.Envelope; import de.sciss.fscape.util.Filter; import de.sciss.fscape.util.Modulator; 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 de.sciss.io.Marker; import de.sciss.io.Region; import de.sciss.io.Span; 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.Vector; /** * Processing module for sound file convolution. */ public class ConvolutionDlg extends ModulePanel { // -------- private variables -------- // Properties (defaults) private static final int PR_INPUTFILE = 0; // pr.text private static final int PR_IMPULSEFILE = 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_MODE = 3; private static final int PR_MORPHPOLICY = 4; private static final int PR_LENGTH = 5; private static final int PR_GAIN = 0; // pr.para private static final int PR_FADELENGTH = 1; private static final int PR_IRNUMBER = 2; private static final int PR_WINSTEP = 3; private static final int PR_WINOVERLAP = 4; private static final int PR_NORMIMPPOWER = 0; // pr.bool private static final int PR_TRUNCOVER = 1; private static final int PR_MORPH = 2; private static final int PR_MINPHASE = 3; private static final int PR_IRMODENV = 0; // pr.envl private static final int MODE_CONV = 0; private static final int MODE_DECONV = 1; private static final int MODE_CONVINV = 2; private static final int MORPH_RECT = 0; private static final int MORPH_POLAR = 1; // private static final int MORPH_SHIFT = 2; private static final int LENGTH_FULL = 0; private static final int LENGTH_INPUT = 1; private static final int LENGTH_SKIPSUPPORT = 2; private static final String PRN_INPUTFILE = "InputFile"; private static final String PRN_IMPULSEFILE = "ImpulseFile"; private static final String PRN_OUTPUTFILE = "OutputFile"; private static final String PRN_OUTPUTTYPE = "OutputType"; private static final String PRN_OUTPUTRES = "OutputRes"; private static final String PRN_MODE = "Mode"; private static final String PRN_MORPHPOLICY = "Policy"; private static final String PRN_LENGTH = "Length"; private static final String PRN_FADELENGTH = "FadeLen"; private static final String PRN_IRNUMBER = "IRNumber"; private static final String PRN_WINSTEP = "WinStep"; private static final String PRN_WINOVERLAP = "WinOverlap"; private static final String PRN_NORMIMPPOWER = "NormImp"; private static final String PRN_TRUNCOVER = "TruncOver"; private static final String PRN_MORPH = "Morph"; private static final String PRN_IRMODENV = "IRModEnv"; private static final String PRN_MINPHASE = "MinPhase"; private static final String prText[] = { "", "", "" }; private static final String prTextName[] = { PRN_INPUTFILE, PRN_IMPULSEFILE, PRN_OUTPUTFILE }; private static final int prIntg[] = { 0, 0, GAIN_UNITY, MODE_CONV, MORPH_RECT, LENGTH_FULL }; private static final String prIntgName[] = { PRN_OUTPUTTYPE, PRN_OUTPUTRES, PRN_GAINTYPE, PRN_MODE, PRN_MORPHPOLICY, PRN_LENGTH }; private static final Param prPara[] = { null, null, null, null, null }; private static final String prParaName[] = { PRN_GAIN, PRN_FADELENGTH, PRN_IRNUMBER, PRN_WINSTEP, PRN_WINOVERLAP }; private static final boolean prBool[] = { false, false, false, false }; private static final String prBoolName[] = { PRN_NORMIMPPOWER, PRN_TRUNCOVER, PRN_MORPH, PRN_MINPHASE }; private static final Envelope prEnvl[] = { null }; private static final String prEnvlName[] = { PRN_IRMODENV }; private static final int GG_INPUTFILE = GG_OFF_PATHFIELD + PR_INPUTFILE; private static final int GG_IMPULSEFILE = GG_OFF_PATHFIELD + PR_IMPULSEFILE; 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_MODE = GG_OFF_CHOICE + PR_MODE; private static final int GG_MORPHPOLICY = GG_OFF_CHOICE + PR_MORPHPOLICY; private static final int GG_LENGTH = GG_OFF_CHOICE + PR_LENGTH; private static final int GG_GAIN = GG_OFF_PARAMFIELD + PR_GAIN; private static final int GG_FADELENGTH = GG_OFF_PARAMFIELD + PR_FADELENGTH; private static final int GG_IRNUMBER = GG_OFF_PARAMFIELD + PR_IRNUMBER; private static final int GG_WINSTEP = GG_OFF_PARAMFIELD + PR_WINSTEP; private static final int GG_WINOVERLAP = GG_OFF_PARAMFIELD + PR_WINOVERLAP; private static final int GG_NORMIMPPOWER = GG_OFF_CHECKBOX + PR_NORMIMPPOWER; private static final int GG_TRUNCOVER = GG_OFF_CHECKBOX + PR_TRUNCOVER; private static final int GG_MORPH = GG_OFF_CHECKBOX + PR_MORPH; private static final int GG_MINPHASE = GG_OFF_CHECKBOX + PR_MINPHASE; private static final int GG_IRMODENV = GG_OFF_ENVICON + PR_IRMODENV; private static PropertyArray static_pr = null; private static Presets static_presets = null; private static final String MARK_SUPPORT = FIRDesignerDlg.MARK_SUPPORT; // -------- public methods -------- /** * !! setVisible() bleibt dem Aufrufer ueberlassen */ public ConvolutionDlg() { super( "Convolution" ); 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_FADELENGTH ] = new Param( 10.0, Param.ABS_MS ); static_pr.para[ PR_IRNUMBER ] = new Param( 1.0, Param.NONE ); static_pr.para[ PR_WINSTEP ] = new Param( 20.0, Param.ABS_MS ); static_pr.para[ PR_WINOVERLAP ] = new Param( 0.0, Param.ABS_MS ); static_pr.paraName = prParaName; static_pr.bool = prBool; static_pr.boolName = prBoolName; static_pr.envl = prEnvl; static_pr.envl[ PR_IRMODENV ] = Envelope.createBasicEnvelope( Envelope.BASIC_UNSIGNED_TIME ); static_pr.envlName = prEnvlName; // 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, ggImpulseFile, ggOutputFile; JComboBox ggMode, ggMorphPolicy, ggLength; ParamField ggFadeLength, ggIRNumber, ggWinStep, ggWinOverlap; JCheckBox ggNormImpPower, ggTruncOver, ggMorph, ggMinPhase; EnvIcon ggIRModEnv; PathField[] ggInputs; Component[] ggGain; 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_TRUNCOVER: case GG_MORPH: 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 sound" ); ggInputFile.handleTypes( GenericFile.TYPES_SOUND ); con.gridwidth = 1; con.weightx = 0.1; gui.addLabel( new JLabel( "Input file", SwingConstants.RIGHT )); con.gridheight = 2; con.gridwidth = GridBagConstraints.REMAINDER; con.weightx = 0.9; gui.addPathField( ggInputFile, GG_INPUTFILE, null ); ggImpulseFile = new PathField( PathField.TYPE_INPUTFILE + PathField.TYPE_FORMATFIELD, "Select impulse response" ); ggImpulseFile.handleTypes( GenericFile.TYPES_SOUND ); con.gridheight = 1; con.gridwidth = 1; con.weightx = 0.1; gui.addLabel( new JLabel( "Impulse response", SwingConstants.RIGHT )); con.gridheight = 2; con.gridwidth = GridBagConstraints.REMAINDER; con.weightx = 0.9; gui.addPathField( ggImpulseFile, GG_IMPULSEFILE, 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 ] = ggImpulseFile; ggOutputFile.deriveFrom( ggInputs, "$D0$B0Con$B1$E" ); con.gridwidth = 1; con.weightx = 0.1; gui.addLabel( new JLabel( "Output file", SwingConstants.RIGHT )); con.gridheight = 2; 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, il ); // -------- Convolution-Parameter -------- con.fill = GridBagConstraints.BOTH; con.gridheight = 1; con.gridwidth = GridBagConstraints.REMAINDER; gui.addLabel( new GroupLabel( "Convolution Settings", GroupLabel.ORIENT_HORIZONTAL, GroupLabel.BRACE_NONE )); ggMode = new JComboBox(); ggMode.addItem( "Convolution" ); ggMode.addItem( "Deconvolution" ); ggMode.addItem( "Conv w/ inversion" ); con.weightx = 0.1; con.gridwidth = 1; gui.addLabel( new JLabel( "Mode", SwingConstants.RIGHT )); con.weightx = 0.4; gui.addChoice( ggMode, GG_MODE, il ); ggTruncOver = new JCheckBox( "Truncate overlaps" ); con.weightx = 0.5; con.gridwidth = GridBagConstraints.REMAINDER; gui.addCheckbox( ggTruncOver, GG_TRUNCOVER, il ); ggNormImpPower = new JCheckBox(); con.weightx = 0.1; con.gridwidth = 1; gui.addLabel( new JLabel( "Norm. IR energy", SwingConstants.RIGHT )); gui.addCheckbox( ggNormImpPower, GG_NORMIMPPOWER, il ); ggFadeLength = new ParamField( Constants.spaces[ Constants.absMsSpace ]); con.weightx = 0.1; con.gridwidth = 1; gui.addLabel( new JLabel( "Trunc fadeout", SwingConstants.RIGHT )); con.weightx = 0.4; con.gridwidth = GridBagConstraints.REMAINDER; gui.addParamField( ggFadeLength, GG_FADELENGTH, null ); ggMinPhase = new JCheckBox(); con.weightx = 0.1; con.gridwidth = 1; gui.addLabel( new JLabel( "Make minimum phase", SwingConstants.RIGHT )); gui.addCheckbox( ggMinPhase, GG_MINPHASE, il ); ggLength = new JComboBox(); ggLength.addItem( "Input + IR" ); ggLength.addItem( "Input (no change)" ); ggLength.addItem( "Inp + IR\\{support}" ); con.weightx = 0.1; con.gridwidth = 1; gui.addLabel( new JLabel( "File length", SwingConstants.RIGHT )); con.weightx = 0.4; con.gridwidth = GridBagConstraints.REMAINDER; gui.addChoice( ggLength, GG_LENGTH, il ); // -------- Morphing-Parameter -------- con.fill = GridBagConstraints.BOTH; con.gridheight = 1; con.gridwidth = GridBagConstraints.REMAINDER; gui.addLabel( new GroupLabel( "Morphing", GroupLabel.ORIENT_HORIZONTAL, GroupLabel.BRACE_NONE )); ggMorph = new JCheckBox(); con.weightx = 0.1; con.gridwidth = 1; gui.addLabel( new JLabel( "Morph IRs", SwingConstants.RIGHT )); con.weightx = 0.4; gui.addCheckbox( ggMorph, GG_MORPH, il ); ggMorphPolicy = new JComboBox(); ggMorphPolicy.addItem( "Rect X-Fade" ); ggMorphPolicy.addItem( "Polar X-Fade" ); ggMorphPolicy.addItem( "Correlate + shift" ); con.weightx = 0.1; con.gridwidth = 1; gui.addLabel( new JLabel( "Policy", SwingConstants.RIGHT )); con.weightx = 0.4; con.gridwidth = GridBagConstraints.REMAINDER; gui.addChoice( ggMorphPolicy, GG_MORPHPOLICY, il ); ggIRNumber = new ParamField( new ParamSpace( 1.0, 100000.0, 1.0, Param.NONE )); con.weightx = 0.1; con.gridwidth = 1; gui.addLabel( new JLabel( "# of IRs in file", SwingConstants.RIGHT )); con.weightx = 0.8; gui.addParamField( ggIRNumber, GG_IRNUMBER, null ); ggIRModEnv = new EnvIcon( getComponent() ); con.weightx = 0.0; gui.addGadget( ggIRModEnv, GG_IRMODENV ); con.weightx = 0.1; con.gridwidth = GridBagConstraints.REMAINDER; gui.addLabel( new JLabel() ); ggWinStep = new ParamField( Constants.spaces[ Constants.absMsSpace ]); con.weightx = 0.1; con.gridwidth = 1; gui.addLabel( new JLabel( "Window size", SwingConstants.RIGHT )); con.weightx = 0.4; gui.addParamField( ggWinStep, GG_WINSTEP, null ); ggWinOverlap = new ParamField( Constants.spaces[ Constants.absMsSpace ]); con.weightx = 0.1; gui.addLabel( new JLabel( "Overlap", SwingConstants.RIGHT )); con.weightx = 0.4; con.gridwidth = GridBagConstraints.REMAINDER; gui.addParamField( ggWinOverlap, GG_WINOVERLAP, 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 -------- protected void process() { long progOff, progLen; float maxAmp = 0.0f; AudioFile inF = null; AudioFile impF = null; AudioFile outF = null; FloatFile floatF[] = null; File tempFile[] = null; AudioFileDescr inStream; AudioFileDescr impStream; AudioFileDescr outStream; int inChanNum, impChanNum, outChanNum; int inLength, impLength, outLength; int fftLength, frameSize, overlap; int off, chunkLength; int framesRead, framesWritten, impFramesRead; int totalInSamples, totalImpSamples, totalOutSamples; float inBuf[][] = null; float impBuf[][] = null; float overBuf[][]; float convBuf1[], convBuf2[], convBuf5[], convBuf3[][], convBuf4[][]; float fadeWin[] = null; float f1, f2; double d1, d2, d3; double impPower; // impulse total power boolean fftPolar = false; // Berechnungen sparen bei MORPH_POLAR Param ampRef = new Param( 1.0, Param.ABS_AMP ); // transform-Referenz Param peakGain; // (abs amp) float gain = 1.0f; // gain abs amp // morphing int mNumImp = 1; // number of IRs in imp-file Modulator mImpMod = null; Param mImpBase; Param mImpDepth = null; Param mImpIndex; Param foo; SpectStream mStream = null; // sucky Modulator-class is SpectStream dependant! int oldImpIndex[] = new int[ 2 ]; // 0...mNumImp-1 int newImpIndex[] = new int[ 2 ]; // 0...mNumImp-1 int mImpReads = 0; // how many times do we read the impulse file int mMorphs = 0; // how many times do we morph the mBufs boolean needsMorph = false; float mBuf[][][] = new float[2][][]; // [2][chan][fft] // overlap float inOverBuf[][] = null; // der Teil vom Input der ein zweites Mal benutzt wird float outOverBuf[][] = null; // der Teil vom Output der ueberblendet wird // float fadeWin2[] = null; // Ueberblendfenster int inOutOverlap = 0; // Laenge von in/outOverBuf int inOverlap = 0; int outOverlap = 0; // interp float[] xa = new float[ 4 ]; float[] ya = new float[ 4 ]; PathField ggOutput; Marker mark; Region region; java.util.List<Marker> markers; java.util.List<Region> regions; int support = 0; boolean minPhase = pr.bool[ PR_MINPHASE ]; int complexFFTsize, virtualImpLen, swap, len, startIdx, stopIdx, markerIdx; float maxImpAmp; float[] win; topLevel: try { // ---- open files ---- inF = AudioFile.openAsRead( new File( pr.text[ PR_INPUTFILE ])); inStream = inF.getDescr(); inChanNum = inStream.channels; inLength = (int) inStream.length; totalInSamples= inLength * inChanNum; // this helps to prevent errors from empty files! if( totalInSamples <= 0 ) throw new EOFException( ERR_EMPTY ); // .... check running .... if( !threadRunning ) break topLevel; impF = AudioFile.openAsRead( new File( pr.text[ PR_IMPULSEFILE ])); impStream = impF.getDescr(); impChanNum = impStream.channels; impLength = (int) impStream.length; virtualImpLen = impLength * (minPhase ? 2 : 1); totalImpSamples = impLength * impChanNum; // this helps to prevent errors from empty files! if( totalImpSamples <= 0 ) throw new EOFException( ERR_EMPTY ); // .... check running .... if( !threadRunning ) break topLevel; ggOutput = (PathField) gui.getItemObj( GG_OUTPUTFILE ); if( ggOutput == null ) throw new IOException( ERR_MISSINGPROP ); IOUtil.createEmptyFile( new File( pr.text[ PR_OUTPUTFILE ])); outStream = new AudioFileDescr( inStream ); ggOutput.fillStream( outStream ); outChanNum = Math.max( inChanNum, impChanNum ); outStream.channels = outChanNum; // ---- preparations, load impulse, transform it ---- mImpBase = new Param( 0.0, Param.NONE ); mImpIndex = mImpBase; // (zunaechst) unmoduliert oldImpIndex[ 0 ] = -1; oldImpIndex[ 1 ] = -1; newImpIndex[ 0 ] = 0; newImpIndex[ 1 ] = 0; // .... morph .... if( pr.bool[ PR_MORPH ]) { mNumImp = Math.max( 1, Math.min( impLength, (int) pr.para[ PR_IRNUMBER ].value)); impLength /= mNumImp; // re-adjust virtualImpLen = impLength * (minPhase ? 2 : 1); totalImpSamples = impLength * impChanNum; frameSize = Math.max( 1, (int) (AudioFileDescr.millisToSamples( outStream, pr.para[ PR_WINSTEP ].value) + 0.5) ); // fftLength = frameSize+impulseResp.-1 auf 2er Potenz aufgerundet // phase interpolation is a kind of convolution (imp1 with imp2); therefore the // fft size must be expanded(?) // if( fftPolar ) i += impLength; for( len = frameSize + virtualImpLen - 1, fftLength = 2; fftLength < len; fftLength <<= 1 ) ; complexFFTsize = fftLength << 1; fftPolar = pr.intg[ PR_MORPHPOLICY ] == MORPH_POLAR; inOutOverlap = Math.min( (frameSize >> 1), (int) (AudioFileDescr.millisToSamples( outStream, pr.para[ PR_WINOVERLAP ].value) + 0.5) ); // window overlap if( inOutOverlap > 0 ) { inOverBuf = new float[ inChanNum ][ inOutOverlap ]; outOverBuf = new float[ inChanNum ][ inOutOverlap ]; // fadeWin2 = Filter.createKaiserWindow( inOutOverlap, 6.0f ); for( int ch = 0; ch < inChanNum; ch++ ) { convBuf1 = inOverBuf[ ch ]; for( int i = 0; i < inOutOverlap; i++ ) { // clear overlap buffer convBuf1[ i ] = 0.0f; } } for( int ch = 0; ch < outChanNum; ch++ ) { convBuf1 = outOverBuf[ ch ]; for( int i = 0; i < inOutOverlap; i++ ) { // clear overlap buffer convBuf1[ i ] = 0.0f; } } } mImpDepth = new Param( pr.para[ PR_IRNUMBER ].value - 1.0, pr.para[ PR_IRNUMBER ].unit ); mStream = new SpectStream(); mStream.setChannels( inChanNum ); mStream.setBands( 0.0f, (float) inStream.rate / 2, 1, SpectStream.MODE_LIN ); mStream.setRate( (float) inStream.rate, frameSize - inOutOverlap ); // !! mStream.setEstimatedLength( (inLength + frameSize - inOutOverlap - 1) / (frameSize - inOutOverlap) ); // !! mStream.getDescr(); impBuf = new float[ impChanNum ][ fftLength+2 ]; for( int i = 0; i < 2; i++ ) { mBuf[ i ] = new float[ impChanNum ][ minPhase ? complexFFTsize : fftLength+2 ]; // lot's of mem ;( } mImpMod = new Modulator( mImpBase, mImpDepth, pr.envl[ PR_IRMODENV ], mStream ); mImpIndex.value = -1.0; for( int i = 0; i < inLength; i += frameSize ) { // calc. mImpReads foo = mImpMod.calc(); // index d1 = foo.value; if( (d1 % 1.0) < 0.0012 ) d1 = Math.floor( d1 ); // ueberfluessiges 0.0000xyz% morphing if( (d1 % 1.0) > 0.9988 ) d1 = Math.ceil( d1 ); // ueberfluessiges 0.9999xyz% morphing foo.value = d1; if( Math.abs( d1 - mImpIndex.value) >= 0.0012 ) { // (only if +- 0.01 dB to improve speed) newImpIndex[ 0 ] = (int) Math.floor( d1 ); newImpIndex[ 1 ] = (int) Math.ceil( d1 ); mMorphs++; mImpIndex = foo; } for( int j = 0; j < 2; j++ ) { if( (newImpIndex[ j ] != oldImpIndex[ 0 ]) && (newImpIndex[ j ] != oldImpIndex[ 1 ])) { mImpReads++; if( newImpIndex[ 1-j ] == oldImpIndex[ j ]) { // be kind and don't overwrite useful mBuf swap = oldImpIndex[ j ]; oldImpIndex[ j ] = oldImpIndex[ 1-j ]; oldImpIndex[ 1-j] = swap; } oldImpIndex[ j ] = newImpIndex[ j ]; // simulate reading } } mStream.framesRead++; // dirty! XXX } mImpIndex.value = -1.0; // reset values; modulator must be reset oldImpIndex[ 0 ] = -1; oldImpIndex[ 1 ] = -1; newImpIndex[ 0 ] = 0; newImpIndex[ 1 ] = 0; mStream.framesRead = 0; mImpMod = new Modulator( mImpBase, mImpDepth, pr.envl[ PR_IRMODENV ], mStream ); // System.out.println( "Total reads: "+mImpReads+"; morphs "+mMorphs ); } else { // fftLength = 2*impulseResp.laenge auf 2er Potenz aufgerundet for( len = 2*(virtualImpLen-1), fftLength = 2; fftLength < len; fftLength <<= 1 ) ; complexFFTsize = fftLength << 1; frameSize = fftLength - virtualImpLen + 1; mImpReads = 1; impBuf = new float[ impChanNum ][ minPhase ? complexFFTsize : fftLength+2 ]; for( int i = 0; i < 2; i++ ) { mBuf[ i ] = impBuf; } } // if PR_MORPH overlap = virtualImpLen - 1; // if( fftPolar ) overlap += impLength; if( pr.bool[ PR_TRUNCOVER ]) { // truncate overlap; calculate window overlap = Math.min( overlap, (int) (AudioFileDescr.millisToSamples( outStream, pr.para[ PR_FADELENGTH ].value) + 0.5) ); if( overlap > 0 ) { fadeWin = Filter.createKaiserWindow( overlap, 6.0f ); } } // adjust "support" impF.readMarkers(); markers = (java.util.List<Marker>) impStream.getProperty( AudioFileDescr.KEY_MARKERS ); regions = (java.util.List<Region>) impStream.getProperty( AudioFileDescr.KEY_REGIONS ); if( markers != null ) { markerIdx = Marker.find( markers, MARK_SUPPORT, 0 ); if( markerIdx >= 0 ) { // impulse file contains "support" marker ==> adjust output markers support = (int) markers.get( markerIdx ).pos; if( support > impLength ) { // may be due to morphing support = 0; } if( minPhase ) support = 0; // XXX unable to calculate in advance } } if( pr.intg[ PR_LENGTH ] == LENGTH_FULL ) { if( support > 0 ) { // impulse file contains "support" marker ==> adjust output markers if( markers != null ) { for( int k = 0; k < markers.size(); k++ ) { mark = markers.get( k ); markers.set( k, new Marker( mark.pos + support, mark.name )); } if( Marker.find( markers, MARK_SUPPORT, 0 ) < 0 ) { Marker.add( markers, new Marker( support, MARK_SUPPORT )); } } else { markers = new Vector<Marker>(); Marker.add( markers, new Marker( support, MARK_SUPPORT )); outStream.setProperty( AudioFileDescr.KEY_MARKERS, markers ); } if( regions != null ) { for( int k = 0; k < regions.size(); k++ ) { region = regions.get( k ); regions.set( k, new Region( new Span( region.span.getStart() + support, region.span.getStop() + support ), region.name )); } } region = (Region) outStream.getProperty( AudioFileDescr.KEY_LOOP ); if( region != null ) { outStream.setProperty( AudioFileDescr.KEY_LOOP, new Region( new Span( region.span.getStart() + support, region.span.getStop() + support ), region.name )); } } } outF = AudioFile.openAsWrite( outStream ); // .... check running .... if( !threadRunning ) break topLevel; switch( pr.intg[ PR_LENGTH ]) { case LENGTH_FULL: outLength = inLength + overlap; break; case LENGTH_INPUT: outLength = inLength; break; case LENGTH_SKIPSUPPORT: outLength = inLength + Math.max( 0, overlap - support ); break; default: throw new IllegalArgumentException( String.valueOf( pr.intg[ PR_LENGTH ])); } totalOutSamples = outLength * outChanNum; // System.out.println( "imp.length "+impLength+"; fft length "+fftLength+"; input step"+frameSize+"; overlap "+overlap+"; totalIn "+totalInSamples+"; totalOut "+totalOutSamples ); inBuf = new float[ outChanNum ][ fftLength+2 ]; overBuf = new float[ outChanNum ][ overlap ]; progOff = 0; progLen = (long) (mImpReads*(impLength + totalImpSamples)) + (long) (inLength + totalInSamples) + (long) (outLength + totalOutSamples); // normalization requires temp files if( pr.intg[ PR_GAINTYPE ] == GAIN_UNITY ) { tempFile = new File[ outChanNum ]; floatF = new FloatFile[ outChanNum ]; for( int ch = 0; ch < outChanNum; ch++ ) { tempFile[ ch ] = IOUtil.createTempFile(); floatF[ ch ] = new FloatFile( tempFile[ ch ], GenericFile.MODE_OUTPUT ); } progLen += (long) outLength; } else { gain = (float) (Param.transform( pr.para[ PR_GAIN ], Param.ABS_AMP, ampRef, null )).value; } // .... check running .... if( !threadRunning ) break topLevel; // ----==================== da convolution ====================---- //boolean shown = false; framesRead = 0; framesWritten = 0; if( pr.intg[ PR_LENGTH ] != LENGTH_FULL ) { framesWritten = -support; // frames to skip } // includes final overlap flush for( boolean unfinished = true; threadRunning && unfinished; ) { // ==================== read input chunk ==================== if( inOverlap > 0 ) { for( int ch = 0; ch < inChanNum; ch++ ) { // input overlap buffer => input buffer System.arraycopy( inOverBuf[ ch ], 0, inBuf[ ch ], 0, inOverlap ); } } for( off = inOverlap, chunkLength = Math.min( inLength - framesRead + off, frameSize ); threadRunning && (off < chunkLength); ) { len = Math.min( 8192, chunkLength - off ); inF.readFrames( inBuf, off, len ); framesRead += len; off += len; progOff += len; // .... progress .... setProgression( (float) progOff / (float) progLen ); } // .... check running .... if( !threadRunning ) break topLevel; // zero-padding for( int ch = 0; ch < inChanNum; ch++ ) { convBuf1 = inBuf[ ch ]; for( int i = chunkLength; i < fftLength+2; i++ ) { convBuf1[ i ] = 0.0f; } } // System.out.println( "Samples Read "+framesRead+"; chunk len "+chunkLength ); if( inOutOverlap > 0 ) { inOverlap = inOutOverlap; // Math.max( 0, chunkLength - frameSize + inOutOverlap ); // System.out.println( inOverlap+" from inBuf "+(frameSize-inOutOverlap)+" => inOver" ); for( int ch = 0; ch < inChanNum; ch++ ) { // input buffer => input overlap buffer System.arraycopy( inBuf[ ch ], frameSize - inOutOverlap, inOverBuf[ ch ], 0, inOverlap ); } } // forward transform for( int ch = 0; threadRunning && (ch < inChanNum); ch++ ) { Fourier.realTransform( inBuf[ ch ], fftLength, Fourier.FORWARD ); progOff += chunkLength - inOverlap; // .... progress .... setProgression( (float) progOff / (float) progLen ); } // .... check running .... if( !threadRunning ) break topLevel; // copy transforms in case of channel expansion for( int ch = inChanNum; ch < outChanNum; ch++ ) { System.arraycopy( inBuf[ ch % inChanNum ], 0, inBuf[ ch ], 0, fftLength+2 ); } if( chunkLength > inOverlap ) { // ==================== calc impulse-index ==================== if( mImpMod != null ) { foo = mImpMod.calc(); mStream.framesRead++; d1 = Math.max( 0.0, Math.min( mImpDepth.value, foo.value)); if( (d1 % 1.0) < 0.0012 ) d1 = Math.floor( d1 ); // ueberfluessiges 0.0000xyz% morphing if( (d1 % 1.0) > 0.9988 ) d1 = Math.ceil( d1 ); // ueberfluessiges 0.9999xyz% morphing foo.value = d1; if( Math.abs( d1 - mImpIndex.value) >= 0.0012 ) { mImpIndex = foo; needsMorph = true; // System.out.println( "needs remorph; mImpIndex.value = "+d1 ); newImpIndex[ 0 ] = (int) Math.floor( d1 ); newImpIndex[ 1 ] = (int) Math.ceil( d1 ); } } // ==================== check IR validity ==================== for( int i = 0; threadRunning && (i < 2); i++ ) { // System.out.println( "newImpIndex["+i+"] = "+newImpIndex[ i ]); if( (newImpIndex[ i ] != oldImpIndex[ 0 ]) && (newImpIndex[ i ] != oldImpIndex[ 1 ])) { // System.out.println( "needs to be loaded" ); if( newImpIndex[ 1 - i ] == oldImpIndex[ i ]) { // be kind and don't overwrite useful mBuf // System.out.println( "swap buffers" ); convBuf3 = mBuf[ i ]; // swap mBuf[ i ] = mBuf[ 1-i ]; mBuf[ 1-i ] = convBuf3; swap = oldImpIndex[ i ]; oldImpIndex[ i ] = oldImpIndex[ 1-i ]; oldImpIndex[ 1-i ] = swap; } // ==================== read impulse ==================== convBuf3 = mBuf[ i ]; impF.seekFrame( newImpIndex[ i ] * impLength ); // System.out.println( "read imp from "+(newImpIndex[ i ] * impLength) ); for( impFramesRead = 0; threadRunning && (impFramesRead < impLength); ) { len = Math.min( 8192, impLength - impFramesRead ); impF.readFrames( convBuf3, impFramesRead, len ); impFramesRead += len; progOff += len; // .... progress .... setProgression( (float) progOff / (float) progLen ); } // .... check running .... if( !threadRunning ) break topLevel; // zero-padding for( int ch = 0; ch < impChanNum; ch++ ) { convBuf1 = convBuf3[ ch ]; for( int j = impLength; j < fftLength+2; j++ ) { convBuf1[ j ] = 0.0f; } } if( pr.bool[ PR_NORMIMPPOWER ]) { // normalize IR power impPower = 0.0; for( int ch = 0; ch < impChanNum; ch++ ) { convBuf1 = convBuf3[ ch ]; for( int j = 0; j < impLength; j++ ) { f1 = convBuf1[ j ]; impPower += f1*f1; // impPower += f1; } } impPower = Math.sqrt( impPower ) / impChanNum; // impPower /= impChanNum; d1 = Math.min( 1000.0, 1.0 / Math.abs( impPower )); // max. 60 dB boost for( int ch = 0; ch < impChanNum; ch++ ) { convBuf1 = convBuf3[ ch ]; for( int j = 0; j < impLength; j++ ) { convBuf1[ j ] = (float) ((double) convBuf1[ j ] * d1); } } } // ==================== impulse forward FFT ==================== for( int ch = 0; threadRunning && (ch < impChanNum); ch++ ) { convBuf1 = convBuf3[ ch ]; Fourier.realTransform( convBuf1, fftLength, Fourier.FORWARD ); if( pr.intg[ PR_MODE ] == MODE_DECONV ) { // reziprok spectrum startIdx = fftLength+4; stopIdx = -1; for( int k = 0, j = 1; k <= fftLength; k += 2, j += 2 ) { f1 = convBuf1[ k ] * convBuf1[ k ] + convBuf1[ j ] * convBuf1[ j ]; if( f1 != 0.0f ) { convBuf1[ k ] /= f1; convBuf1[ j ] /= -f1; } else { convBuf1[ k ] = Float.POSITIVE_INFINITY; convBuf1[ j ] = Float.POSITIVE_INFINITY; startIdx = Math.min( startIdx, k ); stopIdx = Math.max( stopIdx, j ); } } // Division by zero may produce NaN values; interpolate these for( int k = startIdx; k <= stopIdx; k++ ) { f1 = convBuf1[ k ]; if( f1 != Float.POSITIVE_INFINITY ) continue; for( int j = k - 2, m = 1, l = 0; m >= 0; j -= 2 ) { l = Math.abs( j ); if( l <= fftLength+1 ) { f1 = convBuf1[ l ]; if( f1 != Float.POSITIVE_INFINITY ) { ya[ m ] = f1; xa[ m-- ] = (float) j; } } else { ya[ m ] = 0.0f; xa[ m-- ] = j; } } for( int j = k + 2, m = 2, l = 0; m <= 3; j += 2 ) { l = (j <= fftLength+1) ? j : ((fftLength << 1) - j); if( l >= 0 ) { f1 = convBuf1[ l ]; if( f1 != Float.POSITIVE_INFINITY ) { ya[ m ] = f1; xa[ m++ ] = (float) j; } } else { ya[ m ] = 0.0f; xa[ m++] = (float) j; } } // System.out.println( "gonna interp" ); convBuf1[ k ] = Util.polyInterpolate( xa, ya, 4, (float) k ); // System.out.println( "interpolated value "+convBuf1[ k ]+" @"+k); } } else if( pr.intg[ PR_MODE ] == MODE_CONVINV ) { // inverse spectrum d1 = Double.POSITIVE_INFINITY; d2 = 0.0f; for( int k = 0, j = 1; k <= fftLength; k += 2, j += 2 ) { // find min/max amp d3 = convBuf1[ k ] * convBuf1[ k ] + convBuf1[ j ] * convBuf1[ j ]; if( d3 > d2 ) { d2 = d3; } else if( d3 < d1 ) { d1 = d3; } } d1 = Math.sqrt( d1 ) + Math.sqrt( d2 ); for( int k = 0, j = 1; k <= fftLength; k += 2, j += 2 ) { // make min => max & max => min f1 = (float) (d1 / Math.sqrt( convBuf1[ k ] * convBuf1[ k ] + convBuf1[ j ] * convBuf1[ j ]) - 1); convBuf1[ k ] *= f1; // i.e. scale with delta-radius divided by radius convBuf1[ j ] *= f1; } } // if( pr.intg[ PR_MODE ] == MODE_DECONV or MODE_CONVINV // MORPH_POLAR: convert to polar and unwrap phases if( fftPolar || minPhase ) { Fourier.rect2Polar( convBuf1, 0, convBuf1, 0, fftLength+2 ); Fourier.unwrapPhases( convBuf1, 0, convBuf1, 0, fftLength+2 ); } progOff += impLength; // .... progress .... setProgression( (float) progOff / (float) progLen ); // .............. convert to minimum phase .............. if( minPhase ) { for( int j = 0; j <= fftLength; j += 2 ) { convBuf1[ j ] = (float) Math.log( Math.max( 1.0e-48, convBuf1[ j ])); } // remove phase line ? XXX // make complex conjugate spectrum for( int j = fftLength + 2, k = fftLength - 2; j < complexFFTsize; k -= 2, j += 2 ) { convBuf1[ j ] = convBuf1[ k ]; convBuf1[ j+1 ] = -convBuf1[ k+1 ]; } // transform to cepstrum domain Fourier.complexTransform( convBuf1, fftLength, Fourier.INVERSE ); // fold cepstrum (make anticausal parts causal) for( int j = 2, k = complexFFTsize - 2; j < fftLength; j += 2, k -= 2 ) { convBuf1[ j ] += convBuf1[ k ]; // add conjugate left wing to right wing convBuf1[ j+1 ] -= convBuf1[ k+1 ]; } convBuf1[ fftLength + 1 ] = -convBuf1[ fftLength + 1 ]; // clear left wing for( int j = fftLength + 2; j < complexFFTsize; j++ ) { convBuf1[ j ] = 0.0f; } // back to frequency domain Fourier.complexTransform( convBuf1, fftLength, Fourier.FORWARD ); // complex exponential : mag' = exp(real); phase' = imag for( int j = 0; j <= fftLength; j += 2 ) { convBuf1[ j ] = (float) Math.exp( convBuf1[ j ]); } // go to timedomain and apply window Fourier.polar2Rect( convBuf1, 0, convBuf1, 0, fftLength + 2 ); Fourier.realTransform( convBuf1, fftLength, Fourier.INVERSE ); maxImpAmp = 0.0f; support = virtualImpLen >> 1; // will be overriden below // for( int j = 0; j < virtualImpLen; j++ ) { for( int j = 0; j < impLength; j++ ) { f1 = Math.abs( convBuf1[ j ]); if( f1 > maxImpAmp ) { maxImpAmp = f1; support = j; // empiricially determine support from greatest elongation } } len = support >> 1; //if( !shown ) { //Debug.view( convBuf1, 0, fftLength, "Time Domain" ); //System.err.println( "fftLength = "+fftLength+"; convBuf1.length =" +convBuf1.length+"; support = "+support+ "; virtualImpLen = "+virtualImpLen ); //} if( len > 0 ) { win = Filter.createWindow( len, Filter.WIN_KAISER6 ); //if( !shown ) { //Debug.view( win, 0, win.length, "Window" ); //System.err.println( "win.length = "+win.length ); //} for( int j = len - 1, k = 0; k < win.length; j--, k++ ) { convBuf1[ j ] *= win[ k ]; } // for( int j = virtualImpLen - len, k = 0; j < virtualImpLen; j++, k++ ) { for( int j = impLength - len, k = 0; j < impLength; j++, k++ ) { convBuf1[ j ] *= win[ k ]; } } // for( int j = virtualImpLen; j < fftLength; j++ ) { for( int j = impLength; j < fftLength; j++ ) { convBuf1[ j ] = 0.0f; } //if( !shown ) { //Debug.view( convBuf1, 0, fftLength, "Windowed" ); //shown = true; //} // back to spectrum Fourier.realTransform( convBuf1, fftLength, Fourier.FORWARD ); if( fftPolar ) Fourier.rect2Polar( convBuf1, 0, convBuf1, 0, fftLength + 2 ); } // if( minPhase ) } // for channels oldImpIndex[ i ] = newImpIndex[ i ]; // impulse erfolgreich geladen } // if impulse needs to be loaded } // for 2 // .... check running .... if( !threadRunning ) break topLevel; // ==================== IR morphing ==================== if( needsMorph ) { // System.out.println( "morphing "+mImpIndex.value ); // System.out.println( "convBuf3 = mBuf0 ? "+(newImpIndex[ 0 ] == oldImpIndex[ 0 ])); // System.out.println( "convBuf4 = mBuf1 ? "+(newImpIndex[ 1 ] == oldImpIndex[ 1 ])); convBuf3 = (newImpIndex[ 0 ] == oldImpIndex[ 0 ]) ? mBuf[ 0 ] : mBuf[ 1 ]; convBuf4 = (newImpIndex[ 1 ] == oldImpIndex[ 1 ]) ? mBuf[ 1 ] : mBuf[ 0 ]; if( convBuf3 != convBuf4 ) { d2 = mImpIndex.value % 1.0; d1 = 1.0 - d2; // System.out.println( "need real morph A="+f1+"; B="+f2 ); for( int ch = 0; ch < impChanNum; ch++ ) { convBuf1 = convBuf3[ ch ]; convBuf2 = convBuf4[ ch ]; convBuf5 = impBuf[ ch ]; for( int i = 0; i <= fftLength; ) { convBuf5[ i ] = (float) (d1 * convBuf1[ i ] + d2 * convBuf2[ i ]); i++; convBuf5[ i ] = (float) (d1 * convBuf1[ i ] + d2 * convBuf2[ i ]); i++; } } } else { // System.out.println( "just copy" ); for( int ch = 0; ch < impChanNum; ch++ ) { System.arraycopy( convBuf3[ ch ], 0, impBuf[ ch ], 0, fftLength+2 ); } } if( fftPolar ) { for( int ch = 0; ch < impChanNum; ch++ ) { // Fourier.wrapPhases( impBuf[ ch ], 0, impBuf[ ch ], 0, fftLength+2 ); Fourier.polar2Rect( impBuf[ ch ], 0, impBuf[ ch ], 0, fftLength+2 ); } } needsMorph = false; } // ==================== convolution = mult+inverse FFT ==================== for( int ch = 0; threadRunning && (ch < outChanNum); ch++ ) { convBuf1 = inBuf[ ch ]; convBuf2 = impBuf[ ch % impChanNum ]; // note: fftLength+1 steps for( int i = 0, j = 1; i <= fftLength; i += 2, j += 2 ) { // complex multiplication f1 = convBuf1[ i ]; // (avoid overwrite) convBuf1[ i ] = f1 * convBuf2[ i ] - convBuf1[ j ] * convBuf2[ j ]; convBuf1[ j ] = f1 * convBuf2[ j ] + convBuf1[ j ] * convBuf2[ i ]; } Fourier.realTransform( convBuf1, fftLength, Fourier.INVERSE ); // inverse transform convBuf2 = overBuf[ ch ]; len = Math.min( overlap, chunkLength ); for( int i = len - 1; i >= 0; i-- ) { // add old overlap convBuf1[ i ] += convBuf2[ i ]; } System.arraycopy( convBuf2, len, convBuf2, 0, overlap - len ); // shift buffer if( fadeWin == null ) { for( int i = 0, k = chunkLength; i < overlap - len; i++, k++ ) { // save new overlap (add) convBuf2[ i ] += convBuf1[ k ]; } for( int i = overlap - len, k = chunkLength + i; i < overlap; i++, k++ ) { // save new overlap (replace) convBuf2[ i ] = convBuf1[ k ]; } } else { for( int i = 0, k = chunkLength; i < overlap - len; i++, k++ ) { // save new overlap (add) convBuf2[ i ] += fadeWin[ i ] * convBuf1[ k ]; // (w/ window multiplication) } for( int i = overlap - len, k = chunkLength + i; i < overlap; i++, k++ ) { // save new overlap (replace) convBuf2[ i ] = fadeWin[ i ] * convBuf1[ k ]; } } progOff += chunkLength - inOverlap; // .... progress .... setProgression( (float) progOff / (float) progLen ); } // .... check running .... if( !threadRunning ) break topLevel; } // if chunkLength > 0 else { // System.out.println( framesWritten+" von "+outLength+"; outOverlap "+outOverlap+"; overlap "+overlap ); // final flushing chunkLength = Math.min( outLength - framesWritten, Math.max( outOverlap, overlap )); inOverlap = 0; unfinished = false; for( int ch = 0; ch < outChanNum; ch++ ) { convBuf1 = inBuf[ ch ]; convBuf2 = overBuf[ ch ]; for( int i = 0; i < overlap; i++ ) { // add old overlap convBuf1[ i ] += convBuf2[ i ]; } progOff += chunkLength; } } // ==================== write output chunk ==================== if( inOutOverlap > 0 ) { for( int ch = 0; ch < outChanNum; ch++ ) { // x-fade output buffer, copy output buf => overbuf convBuf1 = inBuf[ ch ]; convBuf2 = outOverBuf[ ch ]; for( int j = 0, k = frameSize - inOutOverlap; j < outOverlap; ) { f1 = j / (outOverlap - 1 ); // fadeWin2[ j ]; f2 = convBuf1[ k++ ]; convBuf1[ j ] = convBuf1[ j ] * f1 + convBuf2[ j ] * (1.0f - f1); convBuf2[ j++ ] = f2; } System.arraycopy( convBuf1, frameSize - inOutOverlap + outOverlap, convBuf2, outOverlap, inOutOverlap - outOverlap ); // for( j = outOverlap; j < chunkLength; j++ ) convBuf1[ j ] = 0.0f; } outOverlap = inOverlap; } // System.out.println( "Samples written "+framesWritten ); chunkLength -= outOverlap; off = 0; // chunkLength = Math.min( outLength - framesWritten, chunkLength - outOverlap ); if( framesWritten < 0 ) { // support skipping off = Math.min( -framesWritten, chunkLength ); framesWritten += off; } if( framesWritten >= 0 ) { while( threadRunning && (off < chunkLength) ) { len = Math.min( 8192, chunkLength - off ); if( floatF != null ) { for( int ch = 0; ch < outChanNum; ch++ ) { convBuf1 = inBuf[ ch ]; for( int i = off, k = 0; k < len; i++, k++ ) { f1 = Math.abs( convBuf1[ i ]); if( f1 > maxAmp ) { maxAmp = f1; } } floatF[ ch ].writeFloats( convBuf1, off, len ); } } else { // needs extra mult. for( int ch = 0; ch < outChanNum; ch++ ) { convBuf1 = inBuf[ ch ]; for( int i = off, k = 0; k < len; i++, k++ ) { f1 = Math.abs( convBuf1[ i ]); convBuf1[ i ] *= gain; if( f1 > maxAmp ) { maxAmp = f1; } } } outF.writeFrames( inBuf, off, len ); } framesWritten += len; off += len; progOff += len; // .... progress .... setProgression( (float) progOff / (float) progLen ); } } // .... check running .... if( !threadRunning ) break topLevel; // System.out.println( "Samples written "+framesWritten ); } // for all output samples // ---- normalize output ---- if( pr.intg[ PR_GAINTYPE ] == GAIN_UNITY ) { peakGain = new Param( (double) maxAmp, Param.ABS_AMP ); gain = (float) (Param.transform( pr.para[ PR_GAIN ], Param.ABS_AMP, new Param( 1.0 / peakGain.value, peakGain.unit ), null )).value; normalizeAudioFile( floatF, outF, inBuf, gain, 1.0f ); for( int ch = 0; ch < outChanNum; ch++ ) { floatF[ ch ].cleanUp(); floatF[ ch ] = null; tempFile[ ch ].delete(); tempFile[ ch ] = null; } } // circumvent progLen calculation bug in non-normalize mode XXX setProgression( 1.0f ); // ---- Finish ---- impF.close(); impF = null; impStream = null; convBuf1 = null; convBuf2 = null; convBuf3 = null; convBuf4 = null; convBuf5 = null; impBuf = null; mBuf = null; overBuf = null; inOverBuf = null; outOverBuf = null; fadeWin = null; // fadeWin2 = null; inF.close(); inF = null; inStream = null; inBuf = null; outF.close(); outF = null; outStream = null; // .... check running .... if( !threadRunning ) break topLevel; // inform about clipping/ low level maxAmp *= gain; handleClipping( maxAmp ); } // topLevel catch( IOException e1 ) { setError( e1 ); } catch( OutOfMemoryError e2 ) { convBuf1 = null; convBuf2 = null; convBuf3 = null; convBuf4 = null; convBuf5 = null; inBuf = null; impBuf = null; mBuf = null; overBuf = null; inOverBuf = null; outOverBuf = null; fadeWin = null; // fadeWin2 = null; inStream = null; impStream = null; outStream = null; System.gc(); setError( new Exception( ERR_MEMORY )); } // ---- cleanup (topLevel) ---- if( inF != null ) { inF.cleanUp(); } if( outF != null ) { outF.cleanUp(); } if( impF != null ) { impF.cleanUp(); } if( floatF != null ) { for( int ch = 0; ch < floatF.length; ch++ ) { if( floatF[ ch ] != null ) floatF[ ch ].cleanUp(); if( tempFile[ ch ] != null ) tempFile[ ch ].delete(); } } } protected void reflectPropertyChanges() { super.reflectPropertyChanges(); Component c; c = gui.getItemObj( GG_FADELENGTH ); if( c != null ) { c.setEnabled( pr.bool[ PR_TRUNCOVER ]); } c = gui.getItemObj( GG_MORPHPOLICY ); if( c != null ) { c.setEnabled( pr.bool[ PR_MORPH ]); } c = gui.getItemObj( GG_IRNUMBER ); if( c != null ) { c.setEnabled( pr.bool[ PR_MORPH ]); } c = gui.getItemObj( GG_IRMODENV ); if( c != null ) { c.setEnabled( pr.bool[ PR_MORPH ]); } c = gui.getItemObj( GG_WINSTEP ); if( c != null ) { c.setEnabled( pr.bool[ PR_MORPH ]); } c = gui.getItemObj( GG_WINOVERLAP ); if( c != null ) { c.setEnabled( pr.bool[ PR_MORPH ]); } } } // class ConvolutionDlg