/* * PearsonPlotDlg.java * (FScape) * * Copyright (c) 2001-2016 Hanns Holger Rutz. All rights reserved. * * This software is published under the GNU General Public License v3+ * * * For further information, please contact Hanns Holger Rutz at * contact@sciss.de */ package de.sciss.fscape.gui; import de.sciss.fscape.io.FloatFile; import de.sciss.fscape.io.GenericFile; import de.sciss.fscape.prop.Presets; import de.sciss.fscape.prop.PropertyArray; import de.sciss.fscape.session.ModulePanel; import de.sciss.fscape.spect.Fourier; import de.sciss.fscape.util.Constants; import de.sciss.fscape.util.Filter; import de.sciss.fscape.util.Param; import de.sciss.fscape.util.ParamSpace; import de.sciss.fscape.util.Util; import de.sciss.io.AudioFile; import de.sciss.io.AudioFileDescr; import de.sciss.io.IOUtil; import 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.io.RandomAccessFile; import java.util.Vector; /** * 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 PearsonPlotDlg 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_ICONFILE = 2; private static final int PR_OUTPUTFILE = 3; 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_TRIGSOURCE = 3; private static final int PR_TRIGSIGN = 4; private static final int PR_GAIN = 0; // pr.para private static final int PR_TRIGTHRESH = 1; private static final int PR_TRIGINTERVAL = 2; private static final int PR_PLOTGAIN = 3; private static final int PR_PLOTCHANGAIN = 4; private static final int PR_PLOTQUANTAMOUNT = 5; private static final int PR_PLOTOFFSET = 6; private static final int PR_PLOTNUM = 7; private static final int PR_WRITEMARK = 0; // pr.bool private static final int PR_PLOTQUANT = 1; private static final int PR_PLOTMAX = 2; private static final int SRC_SUM = 0; // private static final int SRC_OR = 1; private static final int SRC_AND = 2; private static final int SIGN_POS = 0; private static final int SIGN_NEG = 1; // private static final int SIGN_BOTH = 2; 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_ICONFILE = "IconFile"; private static final String PRN_OUTPUTTYPE = "OutputType"; private static final String PRN_OUTPUTRES = "OutputReso"; private static final String PRN_WRITEMARK = "WriteMark"; private static final String PRN_TRIGTHRESH = "TrigTresh"; private static final String PRN_TRIGINTERVAL = "TrigInterv"; private static final String PRN_PLOTGAIN = "PltGain"; private static final String PRN_PLOTCHANGAIN = "PltChGain"; private static final String PRN_PLOTQUANT = "PltQuant"; private static final String PRN_PLOTQUANTAMOUNT = "PltQuantAmt"; private static final String PRN_PLOTOFFSET = "PltOffset"; private static final String PRN_PLOTMAX = "PltMax"; private static final String PRN_PLOTNUM = "PltNum"; private static final String PRN_TRIGSOURCE = "TrigSrc"; private static final String PRN_TRIGSIGN = "TrigSign"; private static final String prText[] = { "", "", "", "" }; private static final String prTextName[] = { PRN_INPUTFILE, PRN_PATTERNFILE, PRN_ICONFILE, PRN_OUTPUTFILE }; private static final int prIntg[] = { 0, 0, GAIN_UNITY, SRC_SUM, SIGN_POS }; private static final String prIntgName[] = { PRN_OUTPUTTYPE, PRN_OUTPUTRES, PRN_GAINTYPE, PRN_TRIGSOURCE, PRN_TRIGSIGN }; private static final boolean prBool[] = { true, false, false }; private static final String prBoolName[] = { PRN_WRITEMARK, PRN_PLOTQUANT, PRN_PLOTMAX }; private static final Param prPara[] = { null, null, null, null, null, null, null, null }; private static final String prParaName[] = { PRN_GAIN, PRN_TRIGTHRESH, PRN_TRIGINTERVAL, PRN_PLOTGAIN, PRN_PLOTCHANGAIN, PRN_PLOTQUANTAMOUNT, PRN_PLOTOFFSET, PRN_PLOTNUM }; 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_ICONFILE = GG_OFF_PATHFIELD + PR_ICONFILE; 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_TRIGSOURCE = GG_OFF_CHOICE + PR_TRIGSOURCE; private static final int GG_TRIGSIGN = GG_OFF_CHOICE + PR_TRIGSIGN; private static final int GG_GAIN = GG_OFF_PARAMFIELD + PR_GAIN; private static final int GG_TRIGTHRESH = GG_OFF_PARAMFIELD + PR_TRIGTHRESH; private static final int GG_TRIGINTERVAL = GG_OFF_PARAMFIELD + PR_TRIGINTERVAL; private static final int GG_PLOTGAIN = GG_OFF_PARAMFIELD + PR_PLOTGAIN; private static final int GG_PLOTCHANGAIN = GG_OFF_PARAMFIELD + PR_PLOTCHANGAIN; private static final int GG_PLOTQUANTAMOUNT = GG_OFF_PARAMFIELD + PR_PLOTQUANTAMOUNT; private static final int GG_PLOTOFFSET = GG_OFF_PARAMFIELD + PR_PLOTOFFSET; private static final int GG_PLOTNUM = GG_OFF_PARAMFIELD + PR_PLOTNUM; private static final int GG_WRITEMARK = GG_OFF_CHECKBOX + PR_WRITEMARK; private static final int GG_PLOTQUANT = GG_OFF_CHECKBOX + PR_PLOTQUANT; private static final int GG_PLOTMAX = GG_OFF_CHECKBOX + PR_PLOTMAX; private static PropertyArray static_pr = null; private static Presets static_presets = null; private JLabel lbTrigs; private static final String ERR_CHANNELS = "Input + pattern must share\nsame # of channels!\n(or use sum source)"; private static final String ERR_TOOSMALL = "Input cannot be shorter than pattern"; private static final String ERR_SILENCE = "Pattern contains pure silence"; private static final String MARK_TRIG = "Trig"; // -------- public methods -------- /** * !! setVisible() bleibt dem Aufrufer ueberlassen */ public PearsonPlotDlg() { super( "Pearson Plotter" ); 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_TRIGTHRESH ] = new Param( 50.0, Param.FACTOR_AMP ); static_pr.para[ PR_TRIGINTERVAL ] = new Param( 50.0, Param.ABS_MS ); static_pr.para[ PR_PLOTGAIN ] = new Param( 50.0, Param.FACTOR_AMP ); static_pr.para[ PR_PLOTCHANGAIN ] = new Param( 50.0, Param.FACTOR_AMP ); static_pr.para[ PR_PLOTQUANTAMOUNT ]= new Param( 1.0, Param.ABS_BEATS ); static_pr.para[ PR_PLOTOFFSET ] = new Param( 0.0, Param.ABS_MS ); static_pr.para[ PR_PLOTNUM ] = new Param( 10.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, ggIconFile; PathField[] ggInputs; JCheckBox ggWriteMark, ggPltQuant, ggPltMax; JComboBox ggTrigSrc, ggTrigSign; Component[] ggGain; ParamField ggTrigThresh, ggTrigInterval, ggPltGain, ggPltChGain, ggPltOffset, ggPltQuantAmt, ggPlotNum; ParamSpace[] spcInterval, spcOffset; 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; case GG_TRIGSOURCE: pr.intg[ ID - GG_OFF_CHOICE ] = ((JComboBox) e.getSource()).getSelectedIndex(); 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 ); ggIconFile = new PathField( PathField.TYPE_INPUTFILE + PathField.TYPE_FORMATFIELD, "Select plot source file" ); ggIconFile.handleTypes( GenericFile.TYPES_SOUND ); con.gridwidth = 1; con.weightx = 0.1; gui.addLabel( new JLabel( "Icon input", SwingConstants.RIGHT )); con.gridwidth = GridBagConstraints.REMAINDER; con.weightx = 0.9; gui.addPathField( ggIconFile, GG_ICONFILE, 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 ] = ggIconFile; ggOutputFile.deriveFrom( ggInputs, "$D0$B0Plot$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 ); // -------- Trig Settings -------- gui.addLabel( new GroupLabel( "Trigger settings", GroupLabel.ORIENT_HORIZONTAL, GroupLabel.BRACE_NONE )); ggTrigSrc = new JComboBox(); ggTrigSrc.addItem( "Sum" ); ggTrigSrc.addItem( "Parallel (OR)" ); ggTrigSrc.addItem( "Serial (AND)" ); con.weightx = 0.1; con.gridwidth = 1; gui.addLabel( new JLabel( "Source channels", SwingConstants.RIGHT )); con.weightx = 0.4; gui.addChoice( ggTrigSrc, GG_TRIGSOURCE, il ); ggTrigThresh = new ParamField( Constants.spaces[ Constants.ratioAmpSpace ]); con.weightx = 0.1; gui.addLabel( new JLabel( "Threshold", SwingConstants.RIGHT )); con.weightx = 0.4; con.gridwidth = GridBagConstraints.REMAINDER; gui.addParamField( ggTrigThresh, GG_TRIGTHRESH, null ); ggTrigSign = new JComboBox(); ggTrigSign.addItem( "Pos.(in phase)" ); ggTrigSign.addItem( "Neg.(antiphase)" ); ggTrigSign.addItem( "Both" ); con.weightx = 0.1; con.gridwidth = 1; gui.addLabel( new JLabel( "Thresh sign", SwingConstants.RIGHT )); con.weightx = 0.4; gui.addChoice( ggTrigSign, GG_TRIGSIGN, il ); spcInterval = new ParamSpace[ 2 ]; spcInterval[0] = Constants.spaces[ Constants.absMsSpace ]; spcInterval[1] = Constants.spaces[ Constants.absBeatsSpace ]; ggTrigInterval = new ParamField( spcInterval ); con.weightx = 0.1; gui.addLabel( new JLabel( "Min.interval", SwingConstants.RIGHT )); con.weightx = 0.4; con.gridwidth = GridBagConstraints.REMAINDER; gui.addParamField( ggTrigInterval, GG_TRIGINTERVAL, null ); // -------- Plot Settings -------- gui.addLabel( new GroupLabel( "Plotter settings", GroupLabel.ORIENT_HORIZONTAL, GroupLabel.BRACE_NONE )); ggPltGain = new ParamField( Constants.spaces[ Constants.ratioAmpSpace ]); con.weightx = 0.1; con.gridwidth = 1; gui.addLabel( new JLabel( "Variable gain", SwingConstants.RIGHT )); con.weightx = 0.4; gui.addParamField( ggPltGain, GG_PLOTGAIN, null ); spcOffset = new ParamSpace[ 2 ]; spcOffset[0] = Constants.spaces[ Constants.offsetMsSpace ]; spcOffset[1] = Constants.spaces[ Constants.offsetBeatsSpace ]; ggPltOffset = new ParamField( spcOffset ); con.weightx = 0.1; gui.addLabel( new JLabel( "Offset", SwingConstants.RIGHT )); con.weightx = 0.4; con.gridwidth = GridBagConstraints.REMAINDER; gui.addParamField( ggPltOffset, GG_PLOTOFFSET, null ); ggPltChGain = new ParamField( Constants.spaces[ Constants.ratioAmpSpace ]); con.weightx = 0.1; con.gridwidth = 1; gui.addLabel( new JLabel( "Channel gain", SwingConstants.RIGHT )); con.weightx = 0.4; gui.addParamField( ggPltChGain, GG_PLOTCHANGAIN, null ); ggPltQuant = new JCheckBox( "Quantisize" ); con.weightx = 0.1; gui.addCheckbox( ggPltQuant, GG_PLOTQUANT, il ); ggPltQuantAmt = new ParamField( spcInterval ); con.weightx = 0.4; con.gridwidth = GridBagConstraints.REMAINDER; gui.addParamField( ggPltQuantAmt, GG_PLOTQUANTAMOUNT, null ); ggWriteMark = new JCheckBox(); con.weightx = 0.1; con.gridwidth = 1; gui.addLabel( new JLabel( "Write markers", SwingConstants.RIGHT )); con.weightx = 0.4; gui.addCheckbox( ggWriteMark, GG_WRITEMARK, il ); ggPltMax = new JCheckBox( "Limit #plots" ); con.weightx = 0.1; gui.addCheckbox( ggPltMax, GG_PLOTMAX, il ); ggPlotNum = new ParamField( new ParamSpace( 1.0, 10000.0, 1.0, Param.NONE )); con.weightx = 0.4; con.gridwidth = GridBagConstraints.REMAINDER; gui.addParamField( ggPlotNum, GG_PLOTNUM, null ); // -------- Trigger-View -------- gui.addLabel( new GroupLabel( "Trigger View", GroupLabel.ORIENT_HORIZONTAL, GroupLabel.BRACE_NONE )); lbTrigs = new JLabel(" "); // lbTrigs.setBackground(Color.white); gui.addLabel(lbTrigs); 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() { int i, j, k, m, n, off, ch, len; long progOff, progLen; float f1, f2; double d1; boolean b1; // io AudioFile inF = null; AudioFile outF = null; AudioFile ptrnF = null; AudioFile iconF = null; AudioFileDescr inStream = null; AudioFileDescr outStream = null; AudioFileDescr ptrnStream = null; AudioFileDescr iconStream = null; FloatFile[] outFloatF = null; File outTempFile[] = null; int inChanNum, outChanNum, ptrnChanNum, iconChanNum, trigChanNum; // buffers float[][] inBuf, chunkBuf, ptrnBuf, overBuf; float[] convBuf1, convBuf2; int inOff, outOff, chunkLength; int inLength, outLength, ptrnLength, iconLength; int frameSize, overOff, overLength, fftLength; int framesRead, framesWritten, framesWritten2; Param ampRef = new Param( 1.0, Param.ABS_AMP ); // transform-Referenz float gain = 1.0f; // gain abs amp float maxAmp = 0.0f; boolean sumChannels = pr.intg[ PR_TRIGSOURCE ] == SRC_SUM; float trigThresh, trigPos, trigNeg; // abs amp int trigCount, trigInterval, lastTrig; // minimum spacing [smp] int plotQuant, plotIdx, plotIdx2, plotTableLen, plotEntryLen, plotOff; int plotNum = 0; int[] plotTable = null; byte[] byteBuf = null; RandomAccessFile plotF = null; File plotTempFile = null; float varGain, chanGain; int iconMem, pass, passes, passLen; PathField ggOutput; Graphics g = null; Dimension gDim; java.util.List<Marker> markers; topLevel: try { // ---- open input, output; init ---- // input inF = AudioFile.openAsRead( new File( pr.text[ PR_INPUTFILE ])); inStream = inF.getDescr(); inChanNum = inStream.channels; inLength = (int) inStream.length; // this helps to prevent errors from empty files! if( (inLength < 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 = (int) 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) && !sumChannels ) { throw new IOException( ERR_CHANNELS ); } if( inLength < ptrnLength ) throw new IOException( ERR_TOOSMALL ); // icon input iconF = AudioFile.openAsRead( new File( pr.text[ PR_ICONFILE ])); iconStream = iconF.getDescr(); iconChanNum = iconStream.channels; iconLength = (int) iconStream.length; // this helps to prevent errors from empty files! if( (iconLength < 1) || (iconChanNum < 1) ) throw new EOFException( ERR_EMPTY ); // .... check running .... if( !threadRunning ) break topLevel; trigChanNum = sumChannels ? 1 : inChanNum; outChanNum = Math.max( trigChanNum, iconChanNum ); // output 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 ); outStream.channels = outChanNum; markers = (java.util.List<Marker>) outStream.getProperty( AudioFileDescr.KEY_MARKERS ); if( markers == null && pr.bool[ PR_WRITEMARK ]) { markers = new Vector<Marker>(); outStream.setProperty( AudioFileDescr.KEY_MARKERS, markers ); } // .... check running .... if( !threadRunning ) break topLevel; // ---- further inits ---- g = lbTrigs.getGraphics(); gDim = lbTrigs.getSize(); if (g != null) { lbTrigs.repaint(); g.setColor(Color.red); } trigThresh = (float) (Param.transform( pr.para[ PR_TRIGTHRESH ], Param.ABS_AMP, ampRef, null )).value; trigInterval= (int) (AudioFileDescr.millisToSamples( inStream, pr.para[ PR_TRIGINTERVAL ].value) + 0.5); trigPos = trigThresh; trigNeg = -trigThresh; switch( pr.intg[ PR_TRIGSIGN ]) { case SIGN_POS: trigNeg = Float.NEGATIVE_INFINITY; // -2.0e99f; break; case SIGN_NEG: trigPos = Float.POSITIVE_INFINITY; // +2.0e99f; // higher than we could get break; } plotQuant = pr.bool[ PR_PLOTQUANT ] ? (int) (AudioFileDescr.millisToSamples( inStream, (Param.transform( pr.para[ PR_PLOTQUANTAMOUNT ], Param.ABS_MS, new Param( 0.0, Param.ABS_MS ), null )).value) + 0.5) : 1; // i.e. no quantization plotOff = (int) (AudioFileDescr.millisToSamples( inStream, (Param.transform( pr.para[ PR_PLOTOFFSET ], Param.ABS_MS, new Param( 0.0, Param.ABS_MS ), null )).value) + 0.5); // fftLength = 2*impulseResp.laenge auf 2er Potenz aufgerundet for( i = 2*ptrnLength-1, fftLength = 2; fftLength < i; fftLength <<= 1 ) ; frameSize = fftLength - ptrnLength + 1; overOff = frameSize - 2; overLength = fftLength - overOff; inBuf = new float[ Math.max( Math.max( inChanNum, ptrnChanNum ), iconChanNum )][ 8192 ]; ptrnBuf = new float[ sumChannels ? 1 : ptrnChanNum ][ fftLength+2 ]; chunkBuf = new float[ trigChanNum ][ fftLength+2 ]; overBuf = new float[ trigChanNum ][ overLength ]; Util.clear( ptrnBuf ); Util.clear( chunkBuf ); plotEntryLen= (outChanNum + 1); plotTableLen= plotEntryLen * 256; plotTable = new int[ plotTableLen ]; byteBuf = new byte[ 256 ]; plotIdx = 0; plotTempFile= IOUtil.createTempFile(); plotF = new RandomAccessFile( plotTempFile, "rw" ); varGain = (float) (Param.transform( pr.para[ PR_PLOTGAIN ], Param.ABS_AMP, ampRef, null )).value; chanGain = (float) (Param.transform( pr.para[ PR_PLOTCHANGAIN ], Param.ABS_AMP, ampRef, null )).value; // inEnergy = new double[ inChanNum ]; // for( ch = 0; ch < inChanNum; ch++ ) { // inEnergy[ ch ] = 0.0; // } progOff = 0; // read ptrn transform ptrn progLen = (long) ptrnLength * (1 + (sumChannels ? 1 : ptrnChanNum)) + // read input transform input (long) inLength * (1 + trigChanNum); progLen *= 2; // i.e. progBar stops at 50% in first pass // ---- create temp files ---- outTempFile = new File[ outChanNum ]; outFloatF = new FloatFile[ outChanNum ]; for( ch = 0; ch < outChanNum; ch++ ) { // first zero them because an exception might be thrown outTempFile[ ch ] = null; outFloatF[ ch ] = null; } for( ch = 0; ch < outChanNum; ch++ ) { outTempFile[ ch ] = IOUtil.createTempFile(); outFloatF[ ch ] = new FloatFile( outTempFile[ ch ], GenericFile.MODE_OUTPUT ); } // .... check running .... if( !threadRunning ) break topLevel; // ----==================== read ptrn file ====================---- for( framesRead = 0; threadRunning && (framesRead < ptrnLength); ) { len = Math.min( ptrnLength - framesRead, 8192 ); ptrnF.readFrames( inBuf, 0, len ); if( sumChannels && (ptrnChanNum > 1) ) { convBuf2 = ptrnBuf[ 0 ]; // is pre-cleared! for( ch = 0; ch < ptrnChanNum; ch++ ) { convBuf1 = inBuf[ ch ]; for( i = 0, j = ptrnLength - framesRead; i < len; ) { // rueckwaerts addieren convBuf2[ --j ] += convBuf1[ i++ ]; } } } else { for( ch = 0; ch < ptrnChanNum; ch++ ) { convBuf1 = inBuf[ ch ]; convBuf2 = ptrnBuf[ ch ]; for( i = 0, j = ptrnLength - framesRead; i < len; ) { // rueckwaerts kopieren convBuf2[ --j ] = convBuf1[ i++ ]; } } } framesRead += len; progOff += len; // .... progress .... setProgression( (float) progOff / (float) progLen ); } // .... check running .... if( !threadRunning ) break topLevel; if( sumChannels ) ptrnChanNum = 1; // don't need original value any longer // remove DC + normalize ptrn (so we do not need the rms-division in the original pearson's formula!) // then take FFT for( ch = 0; threadRunning && (ch < ptrnChanNum); ch++ ) { convBuf1 = ptrnBuf[ ch ]; // remove DC for( j = 0, d1 = 0.0; j < ptrnLength; ) { d1 += convBuf1[ j++ ]; } f1 = (float) (-d1/ptrnLength); for( j = 0, d1 = 0.0; j < ptrnLength; ) { convBuf1[ j++ ] += f1; } // normalize d1 = Math.sqrt( Filter.calcEnergy( convBuf1, 0, ptrnLength )); // i.e. sqrt( sum(y^2) ) if( d1 > 0.0 ) { // (double) fftLength Util.mult( convBuf1, 0, ptrnLength, (float) (1.0 / d1) ); // System.out.println( "ptrn root-energy ch."+ch+" = "+d1+"; normalized to "+(Math.sqrt( Filter.calcEnergy( ptrnBuf[ ch ], 0, ptrnLength )))); } else throw new IOException( ERR_SILENCE ); // transform Fourier.realTransform( convBuf1, fftLength, Fourier.FORWARD ); progOff += ptrnLength; // .... progress .... setProgression( (float) progOff / (float) progLen ); } // .... check running .... if( !threadRunning ) break topLevel; ptrnF.close(); ptrnF = null; ptrnStream = null; // ----==================== the real stuff pt1 ====================---- inOff = 1; framesRead = 0; framesWritten = 0; lastTrig = -trigInterval; while( threadRunning && (framesRead < inLength) ) { // ---- read input chunk ---- for( off = inOff, chunkLength = Math.min( inLength - framesRead + inOff, frameSize ); threadRunning && (off < chunkLength); ) { len = Math.min( 8192, chunkLength - off ); if( sumChannels && (inChanNum > 1) ) { inF.readFrames( inBuf, 0, len ); System.arraycopy( inBuf[ 0 ], 0, chunkBuf[ 0 ], off, len ); // copy first channel for( ch = 1; ch < inChanNum; ch++ ) { // ...and add the others Util.add( inBuf[ ch ], 0, chunkBuf[ 0 ], off, len ); } } else { inF.readFrames( chunkBuf, off, len ); } framesRead += len; off += len; progOff += len; // .... progress .... setProgression( (float) progOff / (float) progLen ); } // .... check running .... if( !threadRunning ) break topLevel; // zero-padding for( ch = 0; ch < trigChanNum; ch++ ) { convBuf1 = chunkBuf[ ch ]; for( i = 0; i < inOff; ) { convBuf1[ i++ ] = 0.0f; } for( i = chunkLength; i < fftLength; ) { convBuf1[ i++ ] = 0.0f; } } // ---- convolve ---- for( ch = 0; threadRunning && (ch < trigChanNum); ch++ ) { Fourier.realTransform( chunkBuf[ ch ], fftLength, Fourier.FORWARD ); Fourier.complexMult( ptrnBuf[ ch % ptrnChanNum ], 0, chunkBuf[ch], 0, chunkBuf[ch], 0, fftLength+2 ); Fourier.realTransform( chunkBuf[ ch ], fftLength, Fourier.INVERSE ); Util.add( overBuf[ ch ], 0, chunkBuf[ ch ], 0, overLength ); System.arraycopy( chunkBuf[ ch ], frameSize - 2, overBuf[ ch ], 0, overLength ); progOff += chunkLength; // .... progress .... setProgression( (float) progOff / (float) progLen ); } // .... check running .... if( !threadRunning ) break topLevel; // ---- find local minima+maxima above thresh ---- for( ch = 0, trigCount = 0; ch < trigChanNum; ch++ ) { convBuf1 = chunkBuf[ ch ]; trigLp: for( i = 1; i < chunkLength-1; i++ ) { f1 = convBuf1[ i ]; f2 = f1 - convBuf1[ i-1 ]; if( f2 > 0.0f ) { // check for maximum f2 = convBuf1[ i+1 ] - f1; if( (f2 < 0.0f) && (f1 > trigPos) ) { // local max above thresh trigCount++; continue trigLp; } } else if( f2 < 0.0f ) { // check for minimum f2 = convBuf1[ i+1 ] - f1; if( (f2 > 0.0f) && (f1 < trigNeg) ) { // local min below thresh trigCount++; continue trigLp; } } convBuf1[ i ] = 0.0f; // no trigger } } // ---- report triggers ---- if( trigCount > 0 ) { switch( pr.intg[ PR_TRIGSOURCE ]) { case SRC_AND: andLp: for( i = 1; i < chunkLength-1; i++ ) { for( ch = 0; ch < trigChanNum; ch++ ) { if( chunkBuf[ ch ][ i ] == 0.0f ) continue andLp; } j = framesWritten+i-1; // offset if( (j - lastTrig) >= trigInterval ) { lastTrig = j; if( pr.bool[ PR_WRITEMARK ]) { markers.add( new Marker( j, MARK_TRIG )); } // visualize if( g != null ) { k = (int) (((float) j / (float) inLength) * gDim.width + 0.5f); g.drawLine( k, 0, k, gDim.height - 1 ); } // quantisize + shift j += plotQuant >> 1; j += plotOff - (j % plotQuant); for( ch = 0, f1 = 0.0f; ch < trigChanNum; ch++ ) { f1 += chunkBuf[ ch ][ i ]; } f1 /= trigChanNum; // average "gain" plotTable[ plotIdx++ ] = j; for( ch = 0; ch < outChanNum; ch++ ) { f2 = 1.0f + varGain * ((f1 + chanGain * (chunkBuf[ ch%trigChanNum ][i] - f1)) - 1.0f); plotTable[ plotIdx++ ] = Float.floatToIntBits( f2 ); } if( plotIdx == plotTableLen ) { // flush writeBytes( plotF, plotTable, 0, plotTableLen, byteBuf ); plotNum += plotIdx; plotIdx = 0; } } } break; default: orLp: for( i = 1; i < chunkLength-1; i++ ) { for( ch = 0; ch < trigChanNum; ch++ ) { if( chunkBuf[ ch ][ i ] != 0.0f ) { j = framesWritten+i-1; // offset if( (j - lastTrig) >= trigInterval ) { lastTrig = j; if( pr.bool[ PR_WRITEMARK ]) { markers.add( new Marker( j, MARK_TRIG )); } // visualize if( g != null ) { k = (int) (((float) j / (float) inLength) * gDim.width + 0.5f); g.drawLine( k, 0, k, gDim.height - 1 ); } // quantisize + shift j += plotQuant >> 1; j += plotOff - (j % plotQuant); // System.out.println( "trig @ "+ j ); for( ch = 0, f1 = 0.0f; ch < trigChanNum; ch++ ) { f1 += chunkBuf[ ch ][ i ]; } f1 /= trigChanNum; // average "gain" plotTable[ plotIdx++ ] = j; for( ch = 0; ch < outChanNum; ch++ ) { f2 = 1.0f + varGain * ((f1 + chanGain * (chunkBuf[ ch%trigChanNum ][i] - f1)) - 1.0f); plotTable[ plotIdx++ ] = Float.floatToIntBits( f2 ); } if( plotIdx == plotTableLen ) { // flush writeBytes( plotF, plotTable, 0, plotTableLen, byteBuf ); plotNum += plotIdx; plotIdx = 0; } } continue orLp; } } } break; } } framesWritten = framesRead - 1; inOff = 2; } // loop through input // .... check running .... if( !threadRunning ) break topLevel; inF.close(); inF = null; inStream = null; ptrnBuf = null; chunkBuf = null; if( g != null ) { g.dispose(); g = null; } // ----==================== the real stuff pt2 ====================---- writeBytes( plotF, plotTable, 0, plotIdx, byteBuf ); // flush plotF.seek( 0L ); plotNum += plotIdx; plotNum /= plotEntryLen; // erst hier wg. evtl. Marker-Eintraegen! outF = AudioFile.openAsWrite( outStream ); if( plotNum > 0 ) { System.gc(); // realloc plotTable and read it completely plotTableLen = plotNum * plotEntryLen; plotTable = new int[ plotTableLen ]; for( off = 0; threadRunning && (off < plotTableLen); ) { len = Math.min( plotTable.length - off, 8192 ); readBytes( plotF, plotTable, off, len, byteBuf ); off += len; // .... progress .... setProgression( getProgression() ); // allow pause } // .... check running .... if( !threadRunning ) break topLevel; // take 75% of free memory, divide by sizeof( float ), divide by iconChanNum iconMem = (int) ((Runtime.getRuntime().freeMemory() >> 4) * 3 / iconChanNum); // System.out.println( "inpMem"+ shapeMem+"; fltSize "+fltSize+"; dataLen "+dataLen+"; shapeLength "+shapeLength+"; totalIn "+totalInSamples ); if( iconMem >= iconLength ) { iconMem = iconLength; } else if( iconMem < 1024 ) throw new OutOfMemoryError( ERR_MEMORY ); ptrnBuf = new float[ iconChanNum ][ iconMem ]; passes = (iconLength - 1) / iconMem; // actual pass# - 1 ! outLength = plotTable[ plotTableLen - plotEntryLen ] + iconLength; outOff = plotTable[ 0 ]; // System.out.println( "#plots "+plotNum+"; passes "+(passes+1)+"; outLength "+outLength+"; initial "+outOff ); // calc proglen read icon normalize initial clear progLen = (long) iconLength + (long) outLength + outOff; for( pass = 0, inOff = 0; pass <= passes; pass++, inOff += iconMem ) { framesWritten = outOff + inOff; progLen += outLength - framesWritten; // *2 } progOff = progLen; // start at progLen *= 2; // ...50% // write initial zero padding framesWritten2 = 0; if( outOff > 0 ) { Util.clear( inBuf ); while( threadRunning && (framesWritten2 < outOff) ) { len = Math.min( 8192, outOff - framesWritten2 ); for( ch = 0; ch < outChanNum; ch++ ) { outFloatF[ ch ].writeFloats( inBuf[ 0 ], 0, len ); } framesWritten2 += len; progOff += len; // .... progress .... setProgression( (float) progOff / (float) progLen ); } // .... check running .... if( !threadRunning ) break topLevel; } // ---- kernel ---- for( pass = 0, inOff = 0; threadRunning && (pass <= passes); pass++, inOff += iconMem ) { framesWritten = outOff + inOff; // ---- step 1: read input portion ---- iconF.seekFrame( inOff ); passLen = Math.min( iconLength - inOff, iconMem ); for( off = 0; threadRunning && (off < passLen); ) { len = Math.min( 8192, passLen - off ); iconF.readFrames( ptrnBuf, off, len ); off += len; progOff += len; // .... progress .... setProgression( (float) progOff / (float) progLen ); } // .... check running .... if( !threadRunning ) break topLevel; // ---- step 2: plot pass ---- // calc initial first plot entry for this chunk chunkLength = Math.min( 8192, outLength - framesWritten ); for( plotIdx = 0; plotIdx < plotTable.length; plotIdx += plotEntryLen ) { if( plotTable[ plotIdx ] + inOff + passLen > framesWritten ) break; } // calc initial last one (exclusive) for( plotIdx2 = plotIdx; plotIdx2 < plotTable.length; plotIdx2 += plotEntryLen ) { if( plotTable[ plotIdx2 ] >= framesWritten + chunkLength ) break; } while( threadRunning && (framesWritten < outLength) ) { // ---- calc plot indices ---- chunkLength = Math.min( 8192, outLength - framesWritten ); // update first plot entry for this chunk // for( ; plotIdx < plotTable.length; plotIdx += plotEntryLen ) { // if( plotTable[ plotIdx ] < framesWritten + chunkLength ) break; // } for( ; plotIdx < plotTable.length; plotIdx += plotEntryLen ) { if( plotTable[ plotIdx ] + inOff + passLen > framesWritten ) break; } // update last one (exclusive) for( ; plotIdx2 < plotTable.length; plotIdx2 += plotEntryLen ) { if( plotTable[ plotIdx2 ] >= framesWritten + chunkLength ) break; } b1 = (plotIdx2 > plotIdx) || (pass == passes); // false means easy skipping ;) if( b1 ) { // System.out.println( "pass "+pass+"; written "+framesWritten+"; idx "+plotIdx+" plotIdx2 "+plotIdx2 ); // reload output i = Math.min( chunkLength, Math.max( 0, framesWritten2 - framesWritten )); for( ch = 0; ch < outChanNum; ch++ ) { convBuf1 = inBuf[ ch ]; outFloatF[ ch ].seekFloat( framesWritten ); outFloatF[ ch ].readFloats( convBuf1, 0, i ); for( j = i; j < chunkLength; ) { // pad convBuf1[ j++ ] = 0.0f; } } // progOff += chunkLength; // // .... progress .... // setProgression( (float) progOff / (float) progLen ); // // .... check running .... // if( !threadRunning ) break topLevel; // add icon file for( i = plotIdx; i < plotIdx2; ) { j = plotTable[ i++ ] + inOff; off = j - framesWritten; if( off < 0 ) { k = -off; off = 0; } else { k = 0; } len = Math.min( chunkLength, passLen - k + off ); // System.out.println( "idx "+i+": "+off+" len "+len+"; ("+k+") "); for( ch = 0; ch < outChanNum; ch++ ) { f1 = Float.intBitsToFloat( plotTable[ i++ ]); // channel gain convBuf1 = ptrnBuf[ ch % iconChanNum ]; convBuf2 = inBuf[ ch ]; for( m = off, n = k; m < len; ) { convBuf2[ m++ ] += f1 * convBuf1[ n++ ]; } } } // write back for( ch = 0; ch < outChanNum; ch++ ) { outFloatF[ ch ].seekFloat( framesWritten ); outFloatF[ ch ].writeFloats( inBuf[ ch ], 0, chunkLength ); } } // if !b1 framesWritten += chunkLength; progOff += chunkLength; // .... progress .... setProgression( (float) progOff / (float) progLen ); // .... check running .... if( !threadRunning ) break topLevel; } // while framesWritten < outLength framesWritten2 = (int) outFloatF[ 0 ].getSize(); } // for passes // .... check running .... if( !threadRunning ) break topLevel; } // if plotNum>0 // ---- normalize output ---- // sound file 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; } normalizeAudioFile( outFloatF, outF, inBuf, gain, 1.0f ); for( ch = 0; ch < outChanNum; ch++ ) { outFloatF[ ch ].cleanUp(); outFloatF[ ch ] = null; outTempFile[ ch ].delete(); outTempFile[ ch ] = null; } // .... check running .... if( !threadRunning ) break topLevel; // ---- Finish ---- outF.close(); outF = null; outStream = null; iconF.close(); iconF = null; iconStream = null; inBuf = null; // inform about clipping/ low level maxAmp *= gain; // handleClipping( maxAmp ); } catch( IOException e1 ) { setError( e1 ); } catch( OutOfMemoryError e2 ) { inStream = null; outStream = null; ptrnStream = null; iconStream = null; inBuf = null; ptrnBuf = null; chunkBuf = null; convBuf1 = null; convBuf2 = null; System.gc(); setError( new Exception( ERR_MEMORY )); } // ---- cleanup (topLevel) ---- if( g != null ) { g.dispose(); g = null; } if( inF != null ) { inF.cleanUp(); inF = null; } if( outF != null ) { outF.cleanUp(); outF = null; } if( iconF != null ) { iconF.cleanUp(); iconF = null; } if( ptrnF != null ) { ptrnF.cleanUp(); ptrnF = null; } if( outFloatF != null ) { for( ch = 0; ch < outFloatF.length; ch++ ) { if( outFloatF[ ch ] != null ) outFloatF[ ch ].cleanUp(); if( outTempFile[ ch ] != null ) outTempFile[ ch ].delete(); } } if( plotF != null ) { try { plotF.close(); } catch( Exception ignored) {} } if( plotTempFile != null ) plotTempFile.delete(); } // process() // -------- private methods -------- protected void writeBytes( RandomAccessFile f, int[] intBuf, int off, int len, byte[] byteBuf ) throws IOException { int num, i, j, k; len += off; while( off < len ) { num = Math.min( len - off, byteBuf.length >> 2 ); for( i = 0, k = 0; i < num; i++ ) { j = intBuf[ off++ ]; byteBuf[ k++ ] = (byte) (j >> 24); byteBuf[ k++ ] = (byte) (j >> 16); byteBuf[ k++ ] = (byte) (j >> 8); byteBuf[ k++ ] = (byte) j; } f.write( byteBuf, 0, k ); } } protected void readBytes( RandomAccessFile f, int[] intBuf, int off, int len, byte[] byteBuf ) throws IOException { int num, k; len += off; while( off < len ) { num = Math.min( (len - off) << 2, byteBuf.length ); f.readFully( byteBuf, 0, num ); for( k = 0; k < num; ) { intBuf[ off++ ] = (byteBuf[ k++ ] << 24) | ((byteBuf[ k++ ] & 0xFF) << 16) | ((byteBuf[ k++ ] & 0xFF) << 8) | (byteBuf[ k++ ] & 0xFF); } } } 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 ); } } }