/* * SedimentDlg.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: * 17-Jun-07 created */ package de.sciss.fscape.gui; 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 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.Random; /** * Processing module for replacing occurrences of * one sound by another. The idea is to correlate * an input sound by an "icon" sound and whenever * the correlation exceeds a threshold, a "replacement" * sound is plotted to the output file. Never really worked. */ public class SedimentDlg extends ModulePanel { // -------- private variables -------- // Properties (defaults) private static final int PR_INPUTFILE = 0; // pr.text private static final int PR_PATTERNFILE = 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_CHANTYPE = 3; private static final int PR_GAIN = 0; // pr.para private static final int PR_WINSIZE = 1; private static final int PR_MAXBOOST = 2; private static final int PR_TIMESCALE = 3; private static final int PR_MINGRAINLEN = 4; private static final int PR_MAXGRAINLEN = 5; private static final int PR_CLUMP = 6; private static final String PRN_INPUTFILE = "InputFile"; private static final String PRN_OUTPUTFILE = "OutputFile"; private static final String PRN_PATTERNFILE = "PtrnFile"; private static final String PRN_OUTPUTTYPE = "OutputType"; private static final String PRN_OUTPUTRES = "OutputReso"; private static final String PRN_WINSIZE = "WinSize"; private static final String PRN_MAXBOOST = "MaxBoost"; private static final String PRN_TIMESCALE = "TimeScale"; private static final String PRN_CHANTYPE = "ChanType"; private static final String PRN_MINGRAINLEN = "MinGrainLen"; private static final String PRN_MAXGRAINLEN = "MaxGrainLen"; private static final String PRN_CLUMP = "Clump"; private static final int CHAN_MIN = 0; private static final int CHAN_MAX = 1; private static final String prText[] = { "", "", "" }; private static final String prTextName[] = { PRN_INPUTFILE, PRN_PATTERNFILE, PRN_OUTPUTFILE }; private static final int prIntg[] = { 0, 0, GAIN_UNITY, CHAN_MAX }; private static final String prIntgName[] = { PRN_OUTPUTTYPE, PRN_OUTPUTRES, PRN_GAINTYPE, PRN_CHANTYPE }; private static final Param prPara[] = { null, null, null, null, null, null, null }; private static final String prParaName[] = { PRN_GAIN, PRN_WINSIZE, PRN_MAXBOOST, PRN_TIMESCALE, PRN_MINGRAINLEN, PRN_MAXGRAINLEN, PRN_CLUMP }; 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_PATTERNFILE = GG_OFF_PATHFIELD + PR_PATTERNFILE; 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_CHANTYPE = GG_OFF_CHOICE + PR_CHANTYPE; private static final int GG_GAIN = GG_OFF_PARAMFIELD + PR_GAIN; private static final int GG_WINSIZE = GG_OFF_PARAMFIELD + PR_WINSIZE; private static final int GG_MAXBOOST = GG_OFF_PARAMFIELD + PR_MAXBOOST; private static final int GG_TIMESCALE = GG_OFF_PARAMFIELD + PR_TIMESCALE; private static final int GG_MINGRAINLEN = GG_OFF_PARAMFIELD + PR_MINGRAINLEN; private static final int GG_MAXGRAINLEN = GG_OFF_PARAMFIELD + PR_MAXGRAINLEN; private static final int GG_CLUMP = GG_OFF_PARAMFIELD + PR_CLUMP; private static PropertyArray static_pr = null; private static Presets static_presets = null; private static final String ERR_CHANNELS = "Input + pattern must share\nsame # of channels!"; // debuggingdorfer private static final boolean checkNaN = false; private static final boolean verbose = false; // -------- public methods -------- /** * !! setVisible() bleibt dem Aufrufer ueberlassen */ public SedimentDlg() { super( "Sediment" ); 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.bool = prBool; // static_pr.boolName = prBoolName; static_pr.para = prPara; static_pr.para[ PR_WINSIZE ] = new Param( 20.0, Param.FACTOR_TIME ); static_pr.para[ PR_MAXBOOST ] = new Param( 20.0, Param.DECIBEL_AMP ); static_pr.para[ PR_TIMESCALE ] = new Param( 100.0, Param.FACTOR_TIME ); static_pr.para[ PR_MINGRAINLEN ] = new Param( 100.0, Param.ABS_MS ); static_pr.para[ PR_MAXGRAINLEN ] = new Param( 1000.0, Param.ABS_MS ); static_pr.para[ PR_CLUMP ] = new Param( 1.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, ggPtrnFile; PathField[] ggInputs; JComboBox ggChanType; Component[] ggGain; ParamField ggWinSize, ggMaxBoost, ggTimeScale, ggMinGrainLen, ggMaxGrainLen, ggClump; ParamSpace[] spcGrain; 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_PLOTQUANT: // case GG_PLOTMAX: // pr.bool[ ID - GG_OFF_CHECKBOX ] = ((JCheckBox) e.getSource()).isSelected(); // reflectPropertyChanges(); // break; // } } }; // -------- Input-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( "Control input", SwingConstants.RIGHT )); con.gridwidth = GridBagConstraints.REMAINDER; con.weightx = 0.9; gui.addPathField( ggInputFile, GG_INPUTFILE, null ); ggPtrnFile = new PathField( PathField.TYPE_INPUTFILE + PathField.TYPE_FORMATFIELD, "Select matching pattern file" ); ggPtrnFile.handleTypes( GenericFile.TYPES_SOUND ); con.gridwidth = 1; con.weightx = 0.1; gui.addLabel( new JLabel( "Pattern input", SwingConstants.RIGHT )); con.gridwidth = GridBagConstraints.REMAINDER; con.weightx = 0.9; gui.addPathField( ggPtrnFile, GG_PATTERNFILE, 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 ] = ggPtrnFile; ggOutputFile.deriveFrom( ggInputs, "$D0$B0Sedi$B1$E" ); con.gridwidth = 1; con.weightx = 0.1; gui.addLabel( new JLabel( "Plot 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, il ); // -------- Plot Settings -------- gui.addLabel( new GroupLabel( "Plotter settings", GroupLabel.ORIENT_HORIZONTAL, GroupLabel.BRACE_NONE )); // spcTimeScale = new ParamSpace[] { new ParamSpace( Constants.spaces[ Constants.factorTimeSpace ])}; ggTimeScale = new ParamField( Constants.spaces[ Constants.factorTimeSpace ]); con.weightx = 0.1; con.gridwidth = 1; gui.addLabel( new JLabel( "Time scale", SwingConstants.RIGHT )); con.weightx = 0.4; gui.addParamField( ggTimeScale, GG_TIMESCALE, null ); ggClump = new ParamField( new ParamSpace( 1, 65536, 1, Param.NONE )); con.weightx = 0.1; gui.addLabel( new JLabel( "Clump", SwingConstants.RIGHT )); con.weightx = 0.4; con.gridwidth = GridBagConstraints.REMAINDER; gui.addParamField( ggClump, GG_CLUMP, null ); spcGrain = new ParamSpace[ 2 ]; spcGrain[0] = Constants.spaces[ Constants.absMsSpace ]; spcGrain[1] = Constants.spaces[ Constants.absBeatsSpace ]; ggMinGrainLen = new ParamField( spcGrain ); con.weightx = 0.1; con.gridwidth = 1; gui.addLabel( new JLabel( "Min. grain length", SwingConstants.RIGHT )); con.weightx = 0.4; // con.gridwidth = GridBagConstraints.REMAINDER; gui.addParamField( ggMinGrainLen, GG_MINGRAINLEN, null ); ggMaxBoost = new ParamField( new ParamSpace[] { Constants.spaces[ Constants.ratioAmpSpace ], Constants.spaces[ Constants.decibelAmpSpace ]}); con.weightx = 0.1; // con.gridwidth = 1; gui.addLabel( new JLabel( "Max boost", SwingConstants.RIGHT )); con.weightx = 0.4; con.gridwidth = GridBagConstraints.REMAINDER; gui.addParamField( ggMaxBoost, GG_MAXBOOST, null ); ggMaxGrainLen = new ParamField( spcGrain ); con.weightx = 0.1; con.gridwidth = 1; gui.addLabel( new JLabel( "Max. grain length", SwingConstants.RIGHT )); con.weightx = 0.4; // con.gridwidth = GridBagConstraints.REMAINDER; gui.addParamField( ggMaxGrainLen, GG_MAXGRAINLEN, null ); ggWinSize = new ParamField( Constants.spaces[ Constants.ratioTimeSpace ] ); con.weightx = 0.1; // con.gridwidth = 1; gui.addLabel( new JLabel( "Win size", SwingConstants.RIGHT )); con.weightx = 0.4; con.gridwidth = GridBagConstraints.REMAINDER; gui.addParamField( ggWinSize, GG_WINSIZE, null ); ggChanType = new JComboBox(); ggChanType.addItem( "Minimum" ); ggChanType.addItem( "Maximum" ); con.weightx = 0.1; con.gridwidth = 3; gui.addLabel( new JLabel( "Multichannel correlation", SwingConstants.RIGHT )); con.weightx = 0.2; con.gridwidth = GridBagConstraints.REMAINDER; gui.addChoice( ggChanType, GG_CHANTYPE, il ); 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; final long progLen; float f1, f2; double d1; // io AudioFile inF = null; AudioFile outF = null; AudioFile ptrnF = null; final AudioFile tmpF; final AudioFileDescr inStream; final AudioFileDescr outStream; final AudioFileDescr ptrnStream; final int inChanNum, outChanNum, ptrnChanNum; final long inLength, outLength, ptrnLength; // buffers final float[][] inBuf; float[] win; final int maxGrainLength, maxFFTLength, inBufSize; int grainLength, grainLengthH, grainFFTLength, winLen, winLenH; long framesRead, framesRead2, framesWritten, inOff; final Param ampRef = new Param( 1.0, Param.ABS_AMP ); // transform-Referenz float gain = 1.0f; // gain abs amp float maxAmp = 0.0f; final double winSizeFactor = pr.para[ PR_WINSIZE ].value / 100; final int winType = Filter.WIN_HANNING; // XXX pr.intg[ PR_FADEWIN ]; final double timeScale = pr.para[ PR_TIMESCALE ].value / 100; final int chanType = pr.intg[ PR_CHANTYPE ]; final float maxBoost = (float) (Param.transform( pr.para[ PR_MAXBOOST ], Param.ABS_AMP, ampRef, null )).value; final double minGrainLen = Math.min( pr.para[ PR_MINGRAINLEN ].value, pr.para[ PR_MAXGRAINLEN ].value); final double maxGrainLen = Math.max( pr.para[ PR_MINGRAINLEN ].value, pr.para[ PR_MAXGRAINLEN ].value); final double factorGrainLen = maxGrainLen / minGrainLen; final PathField ggOutput; float corrAbsMaxRMS; int idx; boolean inv; long maxPos; final float[] ptrnRMS; final float[][][] ptrnBuf, ptrnFFTBuf; final float[][] corrAbsMaxs; final float[] corrFFTBuf; final float[] totalCorrAbsMaxRMS; final long[][] corrAbsMaxPoss; final boolean[][] corrAbsMaxInvs; final float[] corrAbsMax; final long[] corrAbsMaxPos; final boolean[] corrAbsMaxInv; final float[] totalCorrAbsMax; final long[] totalCorrAbsMaxPos; final boolean[] totalCorrAbsMaxInv; // final long[] ptrnRead; // the idea of caching the forward FFTs doesn't work, // because the input is windowed according to grainLength // which can fluctuate despite FFTsize remaining the same ... ;-/ // final int minGrainLength, minFFTLength; // final boolean[] fftDone; // final float[][] offBuf; // final int numFFTs; int clump, chunkLen; // int fftIdx; final int numClump; // = 10 // AudioFile[] fftF = null; long progDelta, progOff2; final Random rnd = new Random(); topLevel: try { // ---- open input, output; init ---- // input inF = AudioFile.openAsRead( new File( pr.text[ PR_INPUTFILE ])); inStream = inF.getDescr(); inChanNum = inStream.channels; inLength = inStream.length; // this helps to prevent errors from empty files! if( (inLength < 1) || (inChanNum < 1) ) throw new EOFException( ERR_EMPTY ); // .... check running .... if( !threadRunning ) break topLevel; // ptrn input ptrnF = AudioFile.openAsRead( new File( pr.text[ PR_PATTERNFILE ])); ptrnStream = ptrnF.getDescr(); ptrnChanNum = ptrnStream.channels; ptrnLength = ptrnStream.length; // this helps to prevent errors from empty files! if( (ptrnLength < 1) || (ptrnChanNum < 1) ) throw new EOFException( ERR_EMPTY ); // .... check running .... if( !threadRunning ) break topLevel; if( inChanNum != ptrnChanNum ) { throw new IOException( ERR_CHANNELS ); } // output ggOutput = (PathField) gui.getItemObj( GG_OUTPUTFILE ); if( ggOutput == null ) throw new IOException( ERR_MISSINGPROP ); outStream = new AudioFileDescr( inStream ); ggOutput.fillStream( outStream ); outChanNum = inChanNum; outStream.channels = outChanNum; outF = AudioFile.openAsWrite( outStream ); tmpF = createTempFile( outStream ); // .... check running .... if( !threadRunning ) break topLevel; // ---- further inits ---- numClump = (int) pr.para[ PR_CLUMP ].value; corrAbsMaxs = new float[ numClump ][ inChanNum ]; corrAbsMaxPoss = new long[ numClump ][ inChanNum ]; corrAbsMaxInvs = new boolean[ numClump ][ inChanNum ]; // minGrainLength = (int) (AudioFileDescr.millisToSamples( inStream, minGrainLen ) + 0.5); maxGrainLength = (int) (AudioFileDescr.millisToSamples( inStream, maxGrainLen ) + 0.5); // minFFTLength = Util.nextPowerOfTwo( minGrainLength + minGrainLength - 1 ); maxFFTLength = Util.nextPowerOfTwo( maxGrainLength + maxGrainLength - 1 ); // { // int testNumFFTs = 1; // for( int i = minFFTLength; i < maxFFTLength; i <<= 1, testNumFFTs++ ) ; // numFFTs = testNumFFTs; // if( verbose ) System.out.println( "numFFTs = " + numFFTs ); // } // fftDone = new boolean[ numFFTs ]; // fftF = new AudioFile[ numFFTs ]; // offBuf = new float[ inChanNum ][ 4 ]; inBufSize = Math.max( 8192, maxFFTLength + 2 ); inBuf = new float[ inChanNum ][ inBufSize ]; ptrnFFTBuf = new float[ numClump ][ inChanNum ][ maxFFTLength + 2 ]; ptrnBuf = new float[ numClump ][ inChanNum ][ maxGrainLength ]; corrAbsMax = new float[ numClump ]; corrAbsMaxInv = new boolean[ numClump ]; corrAbsMaxPos = new long[ numClump ]; ptrnRMS = new float[ numClump ]; totalCorrAbsMax = new float[ numClump ]; totalCorrAbsMaxInv = new boolean[ numClump ]; totalCorrAbsMaxPos = new long[ numClump ]; totalCorrAbsMaxRMS = new float[ numClump ]; // ptrnRead = new long[ numClump ]; corrFFTBuf = new float[ maxFFTLength + 2 ]; outLength = (long) (inLength * timeScale + 0.5) + maxGrainLength; // progLen = ptrnLength + inLength + (outLength * 3); // progLen = (ptrnLength << 2) + (inLength * inChanNum) + (outLength * 4); progLen = ptrnLength + (ptrnLength * inLength) + (outLength * 3); progOff = 0; framesWritten = 0; // ----==================== read ptrn file ====================---- ptrnLoop: for( framesRead = 0; threadRunning && (framesRead < ptrnLength); ) { //try { //Thread.sleep( 2000 ); //} catch( InterruptedException e1 ) {} // if( verbose ) System.err.println( "ptrnLoop " + framesRead ); grainLength = Math.max( 2, (int) (AudioFileDescr.millisToSamples( inStream, Math.pow( factorGrainLen, rnd.nextFloat() ) * minGrainLen ) + 0.5) & ~1 ); grainLengthH = grainLength >> 1; grainFFTLength = Util.nextPowerOfTwo( grainLength + grainLength - 1 ); // fftIdx = 0; // for( int i = minFFTLength; i < grainFFTLength; i <<= 1, fftIdx ++ ) ; winLen = Math.max( 2, (int) (grainLength * winSizeFactor + 0.5) & ~1 ); winLenH = winLen >> 1; win = Filter.createFullWindow( winLen, winType ); clump = 0; progDelta = 0; for( int clumpIdx = 0; threadRunning && (clumpIdx < numClump) && (framesRead < ptrnLength); clumpIdx++, clump++ ) { chunkLen = (int) Math.min( ptrnLength - framesRead, grainLength ); ptrnF.readFrames( ptrnBuf[ clumpIdx ], 0, chunkLen ); framesRead += chunkLen; // zero padding if( chunkLen < grainLength ) { Util.clear( ptrnBuf[ clumpIdx ], chunkLen, grainLength - chunkLen); } // apply window Util.mult( win, 0, ptrnBuf[ clumpIdx ], 0, winLenH ); Util.mult( win, winLenH, ptrnBuf[ clumpIdx ], grainLength - winLenH, winLenH ); // copy to fft buffer Util.copy( ptrnBuf[ clumpIdx ], 0, ptrnFFTBuf[ clumpIdx ], 0, chunkLen ); Util.clear( ptrnFFTBuf[ clumpIdx ], chunkLen, grainFFTLength - chunkLen ); // reverse Util.reverse( ptrnFFTBuf[ clumpIdx ], 0, grainLength ); // remove DC // ... normalize RMS (so we do not need the rms-division in the original pearson's formula!) // ... and transform to fourier domain ptrnRMS[ clumpIdx ] = 0f; for( int ch = 0; ch < ptrnChanNum; ch++ ) { Util.removeDC( ptrnFFTBuf[ clumpIdx ][ ch ], 0, grainLength ); d1 = Math.sqrt( Filter.calcEnergy( ptrnFFTBuf[ clumpIdx ][ ch ], 0, chunkLen )); // i.e. sqrt( sum(y^2) ) // too small values produce inf values in the below Util.mult !!! d1 = d1 * (1000 * grainLength); if( d1 > 0.0 ) { // (double) fftLength Util.mult( ptrnFFTBuf[ clumpIdx ][ ch ], 0, chunkLen, (float) (1.0 / d1) ); } else continue ptrnLoop; Fourier.realTransform( ptrnFFTBuf[ clumpIdx ][ ch ], grainFFTLength, Fourier.FORWARD ); ptrnRMS[ clumpIdx ] += (float) d1; } totalCorrAbsMax[ clumpIdx ] = 0f; totalCorrAbsMaxPos[ clumpIdx ] = 0; totalCorrAbsMaxInv[ clumpIdx ] = false; totalCorrAbsMaxRMS[ clumpIdx ] = ptrnRMS[ clumpIdx ]; // ptrnRead[ clumpIdx ] = chunkLen; // progOff += (chunkLen << 2); progOff += chunkLen; progDelta += chunkLen; // .... progress .... setProgression( (float) progOff / (float) progLen ); } // for clumpIdx if( !threadRunning ) break topLevel; /////////////////// read through input and calc best correlation inOff = 0; if( verbose ) System.err.println( "readInput " + grainLength + "; clump = " + clump ); //System.gc(); // if( fftF[ fftIdx ] == null ) { // fftF[ fftIdx ] = createTempFile( inChanNum, inStream.rate ); // } else { // fftF[ fftIdx ].seekFrame( 0L ); // } progOff2 = progOff; readInput: for( framesRead2 = 0; threadRunning && (framesRead2 < inLength); ) { chunkLen = (int) Math.min( inLength - framesRead2, grainLength ); if( verbose ) System.out.println( "framesRead2 = " + framesRead2 ); // if( fftDone[ fftIdx ]) { // fftF[ fftIdx ].readFrames( offBuf, 0, 4 ); // fftF[ fftIdx ].readFrames( inBuf, 0, grainFFTLength + 2 ); //// inOff = (((long) Float.floatToRawIntBits( offBuf[ 0 ][ 0 ])) << 32) | //// (((long) Float.floatToRawIntBits( offBuf[ 0 ][ 1 ])) & 0xFFFFFFFF ); //// corrAbsMaxRMS = offBuf[ 0 ][ 2 ]; // inOff = (((long) offBuf[ 0 ][ 0 ]) << 48) | // (((long) offBuf[ 0 ][ 1 ]) << 24) | // ((long) offBuf[ 0 ][ 2 ]); // corrAbsMaxRMS = offBuf[ 0 ][ 3 ]; // if( verbose ) System.err.println( "fftIdx = " + fftIdx + "; read off " + inOff ); // // } else { inF.seekFrame( inOff ); inF.readFrames( inBuf, 0, chunkLen ); // if( chunkLen < grainLength ) { // Util.clear( inBuf, chunkLen, grainLength - chunkLen ); // } Util.clear( inBuf, chunkLen, grainFFTLength - chunkLen ); // apply window Util.mult( win, 0, inBuf, 0, winLenH ); Util.mult( win, winLenH, inBuf, grainLength - winLenH, winLenH ); corrAbsMaxRMS = 0f; for( int ch = 0; ch < inChanNum; ch++ ) { Util.removeDC( inBuf[ ch ], 0, chunkLen ); d1 = Math.sqrt( Filter.calcEnergy( inBuf[ ch ], 0, chunkLen )); // i.e. sqrt( sum(y^2) ) if( checkNaN ) checkForNaN( inBuf[ ch ], "A" ); //too small values produce inf values in the below Util.mult !!! d1 = d1 * (1000 * grainLength); if( d1 > 0.0 ) { Util.mult( inBuf[ ch ], 0, chunkLen, (float) (1.0 / d1) ); if( checkNaN ) checkForNaN( inBuf[ ch ], "B" ); } else { framesRead2 = inOff + chunkLen; inOff = Math.min( inOff + grainLengthH, inLength ); // i.e. overlap progOff += (chunkLen * progDelta); continue readInput; } corrAbsMaxRMS += (float) d1; Fourier.realTransform( inBuf[ ch ], grainFFTLength, Fourier.FORWARD ); if( checkNaN ) checkForNaN( inBuf[ ch ], "C" ); } // for ch // offBuf[ 0 ][ 0 ] = (float) ((inOff >> 48) & 0x00FFFFFF); // offBuf[ 0 ][ 1 ] = (float) ((inOff >> 24) & 0x00FFFFFF); // offBuf[ 0 ][ 2 ] = (float) (inOff & 0x00FFFFFF); // offBuf[ 0 ][ 3 ] = corrAbsMaxRMS; // fftF[ fftIdx ].writeFrames( offBuf, 0, 4 ); // fftF[ fftIdx ].writeFrames( inBuf, 0, grainFFTLength + 2 ); // if( verbose ) System.err.println( "fftIdx = " + fftIdx + "; wrote off " + inOff ); // } // remove DC // ... normalize RMS (so we do not need the rms-division in the original pearson's formula!) // ... transform to fourier domain // ... convolve with revsere-pattern FFT (= convolution in time domain) // ... go back to time domain // ... find position of abs max for( int clumpIdx = 0; clumpIdx < clump; clumpIdx++ ) { corrAbsMax[ clumpIdx ] = 0f; corrAbsMaxPos[ clumpIdx ] = 0; corrAbsMaxInv[ clumpIdx ] = false; } for( int ch = 0; ch < inChanNum; ch++ ) { for( int clumpIdx = 0; clumpIdx < clump; clumpIdx++ ) { Fourier.complexMult( ptrnFFTBuf[ clumpIdx ][ ch ], 0, inBuf[ ch ], 0, corrFFTBuf, 0, grainFFTLength + 2 ); if( checkNaN ) checkForNaN( corrFFTBuf, "D" ); Fourier.realTransform( corrFFTBuf, grainFFTLength, Fourier.INVERSE ); if( checkNaN ) checkForNaN( corrFFTBuf, "E" ); // XXX inf at 0 !!! f2 = 0.0f; inv = false; maxPos = inOff; for( int i = 0; i < chunkLen; i++ ) { f1 = corrFFTBuf[ i ]; if( f1 < 0f ) { if( -f1 > f2 ) { f2 = -f1; inv = true; maxPos = inOff + i; } } else { if( f1 > f2 ) { f2 = f1; inv = false; maxPos = inOff + i; } } } corrAbsMaxs[ clumpIdx ][ ch ] = f2; corrAbsMaxPoss[ clumpIdx ][ ch ] = maxPos; corrAbsMaxInvs[ clumpIdx ][ ch ] = inv; } // progOff += chunkLen * progDelta; // ptrnRead[ clumpIdx ]; progOff = progOff2 + progDelta * framesRead2 / inChanNum; // .... progress .... setProgression( (float) progOff / (float) progLen ); } //System.err.println( " chanType" ); switch( chanType ) { case CHAN_MIN: for( int clumpIdx = 0; clumpIdx < clump; clumpIdx++ ) { f1 = corrAbsMaxs[ clumpIdx ][ 0 ]; idx = 0; for( int ch = 1; ch < inChanNum; ch++ ) { f2 = corrAbsMaxs[ clumpIdx ][ ch ]; if( f2 < f1 ) { f1 = f2; idx = ch; } } corrAbsMax[ clumpIdx ] = corrAbsMaxs[ clumpIdx ][ idx ]; corrAbsMaxPos[ clumpIdx ] = corrAbsMaxPoss[ clumpIdx ][ idx ]; corrAbsMaxInv[ clumpIdx ] = corrAbsMaxInvs[ clumpIdx ][ idx ]; } break; case CHAN_MAX: for( int clumpIdx = 0; clumpIdx < clump; clumpIdx++ ) { f1 = corrAbsMaxs[ clumpIdx ][ 0 ]; idx = 0; for( int ch = 1; ch < inChanNum; ch++ ) { f2 = corrAbsMaxs[ clumpIdx ][ ch ]; if( f2 > f1 ) { f1 = f2; idx = ch; } } corrAbsMax[ clumpIdx ] = corrAbsMaxs[ clumpIdx ][ idx ]; corrAbsMaxPos[ clumpIdx ] = corrAbsMaxPoss[ clumpIdx ][ idx ]; corrAbsMaxInv[ clumpIdx ] = corrAbsMaxInvs[ clumpIdx ][ idx ]; } break; // case CHAN_AVG: // break; default: assert false : chanType; break; } for( int clumpIdx = 0; clumpIdx < clump; clumpIdx++ ) { if( corrAbsMax[ clumpIdx ] > totalCorrAbsMax[ clumpIdx ]) { totalCorrAbsMax [ clumpIdx ] = corrAbsMax[ clumpIdx ]; totalCorrAbsMaxPos[ clumpIdx ] = (long) (corrAbsMaxPos[ clumpIdx ] * timeScale + 0.5); totalCorrAbsMaxInv[ clumpIdx ] = corrAbsMaxInv[ clumpIdx ]; totalCorrAbsMaxRMS[ clumpIdx ] = corrAbsMaxRMS; } } // n = framesRead2; framesRead2 = inOff + chunkLen; inOff = Math.min( inOff + grainLengthH, inLength ); // i.e. overlap // progOff += inOff - n; // progOff += chunkLen; // .... progress .... setProgression( (float) progOff / (float) progLen ); } // readInput // .... check running .... if( !threadRunning ) break topLevel; // fftDone[ fftIdx ] = true; // progOff2 = progOff + (inLength * ptrnDelta); progOff = progOff2 + (inLength * progDelta); // ??? korekt /////////////////// plot if( verbose ) System.err.println( "plot" ); for( int clumpIdx = 0; threadRunning && (clumpIdx < clump); clumpIdx++ ) { // adjust gain f1 = Math.min( maxBoost, totalCorrAbsMaxRMS[ clumpIdx ] / ptrnRMS[ clumpIdx ]) * (totalCorrAbsMaxInv[ clumpIdx ] ? -1 : 1); Util.mult( ptrnBuf[ clumpIdx ], 0, grainLength, f1 ); // zero padding output file if( framesWritten < totalCorrAbsMaxPos[ clumpIdx ]) { if( verbose ) System.err.println( "clumpIdx = " + clumpIdx + " padding from " + framesWritten + " to " + totalCorrAbsMaxPos[ clumpIdx ]); Util.clear( inBuf ); tmpF.seekFrame( framesWritten ); while( threadRunning && (framesWritten < totalCorrAbsMaxPos[ clumpIdx ])) { chunkLen = (int) Math.min( inBufSize, totalCorrAbsMaxPos[ clumpIdx ] - framesWritten ); tmpF.writeFrames( inBuf, 0, chunkLen ); framesWritten += chunkLen; // progOff += chunkLen; // .... progress .... setProgression( (float) progOff / (float) progLen ); } // .... check running .... if( !threadRunning ) break topLevel; } else { if( verbose ) System.err.println( "clumpIdx = " + clumpIdx + " jumping to " + totalCorrAbsMaxPos[ clumpIdx ]); tmpF.seekFrame( totalCorrAbsMaxPos[ clumpIdx ]); } // mix with previous content chunkLen = (int) Math.min( framesWritten - totalCorrAbsMaxPos[ clumpIdx ], grainLength ); if( chunkLen > 0 ) { if( verbose ) System.err.println( "...mixing " + chunkLen + " frames" ); tmpF.readFrames( inBuf, 0, chunkLen ); Util.add( inBuf, 0, ptrnBuf[ clumpIdx ], 0, chunkLen ); tmpF.seekFrame( totalCorrAbsMaxPos[ clumpIdx ]); } if( verbose ) System.err.println( "...writing " + grainLength + " frames" ); tmpF.writeFrames( ptrnBuf[ clumpIdx ], 0, grainLength ); chunkLen = (int) Math.max( 0, totalCorrAbsMaxPos[ clumpIdx ] + grainLength - framesWritten ); framesWritten += chunkLen; progOff += chunkLen; // .... progress .... setProgression( (float) progOff / (float) progLen ); } // for clumpIdx // .... check running .... if( !threadRunning ) break topLevel; } // ------------------------------------ END ptrnLoop ------------------------------------ // progOff = ptrnLength + inLength + outLength; // progOff = (ptrnLength << 4) + (outLength * 2); // .... progress .... setProgression( (float) progOff / (float) progLen ); if( verbose ) System.err.println( "zero pad" ); // for( int i = 0; i < numFFTs; i++ ) { // if( fftF[ i ] != null ) { // deleteTempFile( fftF[ i ]); // fftF[ i ] = null; // } // } // zero padding output file Util.clear( inBuf ); tmpF.seekFrame( framesWritten ); while( threadRunning && (framesWritten < outLength) ) { chunkLen = (int) Math.min( inBufSize, outLength - framesWritten ); tmpF.writeFrames( inBuf, 0, chunkLen ); framesWritten += chunkLen; progOff += chunkLen; // .... progress .... setProgression( (float) progOff / (float) progLen ); } // .... check running .... if( !threadRunning ) break topLevel; if( verbose ) System.err.println( "calc max" ); // calc max amp tmpF.seekFrame( 0L ); for( framesRead = 0; threadRunning && (framesRead < outLength); ) { chunkLen = (int) Math.min( inBufSize, outLength - framesRead ); tmpF.readFrames( inBuf, 0, chunkLen ); maxAmp = Math.max( maxAmp, Util.maxAbs( inBuf, 0, chunkLen )); framesRead += chunkLen; progOff += chunkLen; // .... progress .... setProgression( (float) progOff / (float) progLen ); } // .... check running .... if( !threadRunning ) break topLevel; ptrnF.close(); ptrnF = null; inF.close(); inF = null; // adjust gain 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; } else { gain = (float) (Param.transform( pr.para[ PR_GAIN ], Param.ABS_AMP, ampRef, null )).value; } if( verbose ) System.err.println( "norm" ); normalizeAudioFile( tmpF, outF, inBuf, gain, 1.0f ); deleteTempFile( tmpF ); // .... check running .... if( !threadRunning ) break topLevel; // ---- Finish ---- outF.close(); outF = null; // inform about clipping/ low level maxAmp *= gain; // handleClipping( maxAmp ); } catch( IOException e1 ) { setError( e1 ); } catch( OutOfMemoryError e2 ) { setError( new Exception( ERR_MEMORY )); } // ---- cleanup (topLevel) ---- if( inF != null ) inF.cleanUp(); if( outF != null ) outF.cleanUp(); if( ptrnF != null ) ptrnF.cleanUp(); } // process() // -------- private methods -------- private static void checkForNaN( float[] a, String label ) { for( int i = 0; i < a.length; i++ ) { if( Float.isNaN( a[ i ])) { System.err.println( "!!!!! NaN (" + label + ") at " + i ); return; } if( Float.isInfinite( a[ i ])) { System.err.println( "!!!!! inf (" + label + ") at " + i ); return; } } } // protected void reflectPropertyChanges() // { // super.reflectPropertyChanges(); // // Component c; // // c = gui.getItemObj( GG_PLOTQUANTAMOUNT ); // if( c != null ) { // c.setEnabled( pr.bool[ PR_PLOTQUANT ]); // } // c = gui.getItemObj( GG_PLOTNUM ); // if( c != null ) { // c.setEnabled( pr.bool[ PR_PLOTMAX ]); // } // c = gui.getItemObj( GG_PLOTCHANGAIN ); // if( c != null ) { // c.setEnabled( pr.intg[ PR_TRIGSOURCE ] != SRC_SUM ); // } // } }