/* * WaveletDlg.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: * 07-Jan-05 removed obsolete soundfile methods, * then had to remove interleaved-proc. option */ 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.Wavelet; import de.sciss.fscape.util.Constants; import de.sciss.fscape.util.Param; import de.sciss.io.AudioFile; import de.sciss.io.AudioFileDescr; import de.sciss.io.IOUtil; import javax.swing.*; import java.awt.*; import java.io.EOFException; import java.io.File; import java.io.IOException; /** * Processing module for wavelet pyramid (octave decimation) decomposition * forward + backward. */ public class WaveletDlg extends ModulePanel { // -------- private variables -------- // Properties (defaults) private static final int PR_INPUTFILE = 0; // pr.text private static final int PR_OUTPUTFILE = 1; private static final int PR_OUTPUTTYPE = 0; // pr.intg private static final int PR_OUTPUTRES = 1; private static final int PR_DIRECTION = 2; private static final int PR_FILTER = 3; // !!! CORRESPONDS TO Wavelet.COEFFS_... !!! private static final int PR_GAINTYPE = 4; private static final int PR_LENGTH = 5; private static final int PR_GAIN = 0; // pr.para private static final int PR_SCALEGAIN = 1; private static final int DIR_FORWARD = 0; private static final int DIR_BACKWARD = 1; private static final int LENGTH_EXPAND = 0; private static final int LENGTH_TRUNC = 1; private static final String PRN_INPUTFILE = "InputFile"; private static final String PRN_OUTPUTFILE = "OutputFile"; private static final String PRN_OUTPUTTYPE = "OutputType"; private static final String PRN_OUTPUTRES = "OutputReso"; private static final String PRN_DIRECTION = "Dir"; private static final String PRN_FILTER = "Filter"; private static final String PRN_SCALEGAIN = "ScaleGain"; private static final String PRN_LENGTH = "Length"; private static final String prText[] = { "", "" }; private static final String prTextName[] = { PRN_INPUTFILE, PRN_OUTPUTFILE }; private static final int prIntg[] = { 0, 0, DIR_FORWARD, Wavelet.COEFFS_DAUB4, GAIN_UNITY, LENGTH_EXPAND }; private static final String prIntgName[] = { PRN_OUTPUTTYPE, PRN_OUTPUTRES, PRN_DIRECTION, PRN_FILTER, PRN_GAINTYPE, PRN_LENGTH }; private static final Param prPara[] = { null, null }; private static final String prParaName[] = { PRN_GAIN, PRN_SCALEGAIN }; private static final int GG_INPUTFILE = GG_OFF_PATHFIELD + PR_INPUTFILE; private static final int GG_OUTPUTFILE = GG_OFF_PATHFIELD + PR_OUTPUTFILE; private static final int GG_OUTPUTTYPE = GG_OFF_CHOICE + PR_OUTPUTTYPE; private static final int GG_OUTPUTRES = GG_OFF_CHOICE + PR_OUTPUTRES; private static final int GG_LENGTH = GG_OFF_CHOICE + PR_LENGTH; private static final int GG_GAIN = GG_OFF_PARAMFIELD + PR_GAIN; private static final int GG_SCALEGAIN = GG_OFF_PARAMFIELD + PR_SCALEGAIN; private static final int GG_GAINTYPE = GG_OFF_CHOICE + PR_GAINTYPE; private static final int GG_DIRECTION = GG_OFF_CHOICE + PR_DIRECTION; private static final int GG_FILTER = GG_OFF_CHOICE + PR_FILTER; private static PropertyArray static_pr = null; private static Presets static_presets = null; // -------- public methods -------- /** * !! setVisible() bleibt dem Aufrufer ueberlassen */ public WaveletDlg() { super( "Wavelet Translation" ); 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_SCALEGAIN ] = new Param( 3.0, Param.DECIBEL_AMP ); static_pr.paraName = prParaName; // static_pr.superPr = DocumentFrame.static_pr; fillDefaultAudioDescr( static_pr.intg, PR_OUTPUTTYPE, PR_OUTPUTRES ); fillDefaultGain( static_pr.para, PR_GAIN ); static_presets = new Presets( getClass(), static_pr.toProperties( true )); } presets = static_presets; pr = (PropertyArray) static_pr.clone(); // -------- build GUI -------- GridBagConstraints con; PathField ggInputFile, ggOutputFile; JComboBox ggDirection, ggFilter, ggLength; ParamField ggScaleGain; PathField[] ggInputs; Component[] ggGain; gui = new GUISupport(); con = gui.getGridBagConstraints(); con.insets = new Insets( 1, 2, 1, 2 ); // -------- 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( "Input file", SwingConstants.RIGHT )); con.gridwidth = GridBagConstraints.REMAINDER; con.weightx = 0.9; gui.addPathField( ggInputFile, GG_INPUTFILE, null ); ggOutputFile = new PathField( PathField.TYPE_OUTPUTFILE + PathField.TYPE_FORMATFIELD + PathField.TYPE_RESFIELD, "Select output file" ); ggOutputFile.handleTypes( GenericFile.TYPES_SOUND ); ggInputs = new PathField[ 1 ]; ggInputs[ 0 ] = ggInputFile; ggOutputFile.deriveFrom( ggInputs, "$D0$F0WT$E" ); con.gridwidth = 1; con.weightx = 0.1; gui.addLabel( new JLabel( "Output file", SwingConstants.RIGHT )); con.gridwidth = GridBagConstraints.REMAINDER; con.weightx = 0.9; gui.addPathField( ggOutputFile, GG_OUTPUTFILE, null ); gui.registerGadget( ggOutputFile.getTypeGadget(), GG_OUTPUTTYPE ); gui.registerGadget( ggOutputFile.getResGadget(), GG_OUTPUTRES ); ggGain = createGadgets( GGTYPE_GAIN ); con.weightx = 0.1; con.gridwidth = 1; gui.addLabel( new JLabel( "Gain", SwingConstants.RIGHT )); con.weightx = 0.4; gui.addParamField( (ParamField) ggGain[ 0 ], GG_GAIN, null ); con.weightx = 0.5; con.gridwidth = GridBagConstraints.REMAINDER; gui.addChoice( (JComboBox) ggGain[ 1 ], GG_GAINTYPE, null ); // -------- Settings -------- gui.addLabel( new GroupLabel( "Translation", GroupLabel.ORIENT_HORIZONTAL, GroupLabel.BRACE_NONE )); ggFilter = new JComboBox(); for( int i = 4; i <= 20; i += 2 ) { ggFilter.addItem( "Daubechies "+i ); } con.gridwidth = 1; con.weightx = 0.1; gui.addLabel( new JLabel( "Filter", SwingConstants.RIGHT )); con.weightx = 0.4; gui.addChoice( ggFilter, GG_FILTER, null ); ggScaleGain = new ParamField( Constants.spaces[ Constants.decibelAmpSpace ]); con.weightx = 0.1; gui.addLabel( new JLabel( "Gain per Scale", SwingConstants.RIGHT )); con.weightx = 0.4; con.gridwidth = GridBagConstraints.REMAINDER; gui.addParamField( ggScaleGain, GG_SCALEGAIN, null ); ggDirection = new JComboBox(); ggDirection.addItem( "Forward" ); ggDirection.addItem( "Backward (Inverse)" ); con.gridwidth = 1; con.weightx = 0.1; gui.addLabel( new JLabel( "Direction", SwingConstants.RIGHT )); con.weightx = 0.4; gui.addChoice( ggDirection, GG_DIRECTION, null ); // ggInterleave = new JCheckBox( "Interleaved processing" ); // con.weightx = 0.5; // con.gridwidth = GridBagConstraints.REMAINDER; // gui.addCheckbox( ggInterleave, GG_INTERLEAVE, this ); ggLength = new JComboBox(); ggLength.addItem( "Expand to flt*2^n" ); ggLength.addItem( "Truncate to flt*2^n" ); con.gridwidth = 1; con.weightx = 0.1; gui.addLabel( new JLabel( "FWT Length", SwingConstants.RIGHT )); con.weightx = 0.4; gui.addChoice( ggLength, GG_LENGTH, 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 -------- /** * Translation durchfuehren */ public void process() { int i, j, k; int off, len, ch; int progOff, progLen; // io AudioFile inF = null; AudioFile outF = null; AudioFileDescr inStream = null; AudioFileDescr outStream = null; FloatFile[][] floatF = null; // index 0+1 = smooth, 2 = detail File[][] tempFile = null; int smoothIndex = 0; // rotates between 0+1 int inChanNum; float[][] inputBuf = null; int inputMem; float[][] detailBuf = null; float[][] smoothBuf = null; // float buf[]; int outputMem; int dataLen, passLen, transLen; int pass, passes; int overlap, offStart; PathField ggOutput; // Synthesize Param ampRef = new Param( 1.0, Param.ABS_AMP ); // transform-Referenz Param peakGain; // (abs amp) float gain; // gain abs amp float[][] flt; int fltSize; // int totalInSamples = 0; // reichen 32 bit? int inLength; int framesRead; int framesWritten; int framesToGo; float maxAmp = 0.0f; int scale, scaleNum; // Skalen-Ebene, Gesamtzahl float scaleGain; // (abs amp) System.gc(); // this algorithm may need a lot of memory topLevel: try { flt = Wavelet.getCoeffs( pr.intg[ PR_FILTER ]); fltSize = flt[ 0 ].length; // ---- first pass: load and transform input ---- inF = AudioFile.openAsRead( new File( pr.text[ PR_INPUTFILE ])); inStream = inF.getDescr(); inChanNum = inStream.channels; inLength = (int) inStream.length; // if( pr.bool[ PR_INTERLEAVE ]) { // inLength *= inChanNum; // inChanNum = 1; // } // totalInSamples= inLength * inChanNum; // this helps to prevent errors from empty files! if( inLength * inChanNum < 1 ) throw new EOFException( ERR_EMPTY ); ggOutput = (PathField) gui.getItemObj( GG_OUTPUTFILE ); if( ggOutput == null ) throw new IOException( ERR_MISSINGPROP ); outStream = new AudioFileDescr( inStream ); // outChanNum = outStream.channels; ggOutput.fillStream( outStream ); outF = AudioFile.openAsWrite( outStream ); // .... check running .... if( !threadRunning ) break topLevel; floatF = new FloatFile[ inChanNum ][ 3 ]; tempFile = new File[ inChanNum ][ 3 ]; for( ch = 0; ch < inChanNum; ch++ ) { for( i = 0; i < 2; i++ ) { tempFile[ ch ][ i ] = null; floatF[ ch ][ i ] = null; } } // buf = new float[ 8192 ]; // lowest level gotta be >= fltSize (for Daub. Boundary Wavelet Algorithm), then power of 2 for( dataLen = fltSize, scaleNum = 1; dataLen < inLength; dataLen<<=1, scaleNum++ ) ; if( (dataLen > inLength) && (pr.intg[ PR_LENGTH ] == LENGTH_TRUNC) ) { dataLen >>= 1; } // take 75% of free memory, divide by sizeof( float ), divide by 2 (50% input/ 50% output) inputMem = (int) ((Runtime.getRuntime().freeMemory() >> 5) * 3 / inChanNum) & ~1; //System.out.println( "inpMem"+ inputMem+"; fltSize "+fltSize+"; dataLen "+dataLen+"; inLength "+inLength+"; totalIn "+totalInSamples ); // if( inputMem < fltSize ) throw new OutOfMemoryError( ERR_MEMORY ); // if( inputMem > dataLen ) { // inputMem = dataLen; // } inputMem = Math.min( Math.max( inputMem, fltSize ), dataLen ); outputMem = inputMem >> 1; // je 25% fuer smooth und detail output // ************************************************** FORWARD ************************************************** if( pr.intg[ PR_DIRECTION ] == DIR_FORWARD ) { inputBuf = new float[ inChanNum ][ inputMem + (fltSize<<1) ]; // may cause OutOfMemoryError! detailBuf = new float[ inChanNum ][ outputMem ]; smoothBuf = new float[ inChanNum ][ outputMem ]; passLen = dataLen; // number of samples to filter in current pass framesRead = 0; scale = scaleNum; for( ch = 0; ch < inChanNum; ch++ ) { tempFile[ ch ][ 2 ] = IOUtil.createTempFile(); // for detail data floatF[ ch ][ 2 ] = new FloatFile( tempFile[ ch ][ 2 ], GenericFile.MODE_OUTPUT ); } progOff = 0; progLen = (dataLen - (fltSize>>1)) << 3; // number of operations (for prog-bar) // ---- each pass is one complete filtering (decimation in time, one step of the pyramid) ---- while( (passLen >= fltSize) && threadRunning ) { scale--; scaleGain = (float) (Param.transform( new Param( pr.para[ PR_SCALEGAIN ].value * scale, pr.para[ PR_SCALEGAIN ].unit ), Param.ABS_AMP, ampRef, null )).value; for( ch = 0; ch < inChanNum; ch++ ) { for( i = 0; i < fltSize; i++ ) { inputBuf[ ch ][ i ] = 0.0f; // zero padding left wing } } //System.out.println( "-----passLen "+passLen+"; zero padded left wing" ); overlap = fltSize; offStart = fltSize; for( framesToGo = passLen, passes = 0; (framesToGo > 0) && threadRunning; passes++ ) { transLen = Math.min( framesToGo, inputMem ); // number of samples to transform // System.out.println( "pass "+passes+"; transLen "+transLen ); // ---- read input (smooth) ---- off = 0; do { // len = Math.min( samplesToGo, Math.min( 8192 / ChanNum, transLen - off + overlap )); len = Math.min( framesToGo, Math.min( 8192, transLen - off + overlap )); // len2= len * inChanNum; // read from input file if( inF != null ) { // k = Math.min( totalInSamples - samplesRead, len2 ); k = Math.min( inLength - framesRead, len ); inF.readFrames( inputBuf, off + offStart, k ); // zeropadding for( ch = 0; ch < inChanNum; ch++ ) { for( i = k, j = i + off + offStart; i < len; i++, j++ ) { inputBuf[ ch ][ j ] = 0.0f; } } framesRead += k; // read from smooth tempfile } else if( floatF[ 0 ][ smoothIndex ] != null ) { // from smooth tempfile for( ch = 0; ch < inChanNum; ch++ ) { floatF[ ch ][ smoothIndex ].readFloats( inputBuf[ ch ], off + offStart, len ); } // read from RAM } else { for( ch = 0; ch < inChanNum; ch++ ) { System.arraycopy( smoothBuf[ ch ], off, inputBuf[ ch ], off + offStart, len ); } } off += len; framesToGo -= len; // .... progress .... progOff += len; setProgression( (float) progOff / (float) progLen ); } while( (len > 0) && threadRunning ); // .... check running .... if( !threadRunning ) break topLevel; if( framesToGo == 0 ) { // System.out.println( "zero padded right wing" ); for( ch = 0; ch < inChanNum; ch++ ) { for( j = 0, k = offStart + transLen; j < fltSize; j++, k++ ) { inputBuf[ ch ][ k ] = 0.0f; // zero padding right wing } } transLen += offStart - fltSize; } // ---- transform ---- // System.out.println( "transforming... "+(transLen) ); for( ch = 0; ch < inChanNum; ch++ ) { Wavelet.fwdTransform( inputBuf[ ch ], smoothBuf[ ch ], detailBuf[ ch ], fltSize, transLen, flt ); // adjust scale gain, measure max. gain in detail buf for( j = 0; j < (transLen>>1); j++ ) { detailBuf[ ch ][ j ] *= scaleGain; if( Math.abs( detailBuf[ ch ][ j ]) > maxAmp ) { maxAmp = Math.abs( detailBuf[ ch ][ j ]); } } } progOff += transLen; setProgression( (float) progOff / (float) progLen ); if( !threadRunning ) break topLevel; // ---- write detail ---- off = 0; do { len = Math.min( 8192, (transLen >> 1) - off ); for( ch = 0; ch < inChanNum; ch++ ) { floatF[ ch ][ 2 ].writeFloats( detailBuf[ ch ], off, len ); } off += len; // .... progress .... progOff += len; setProgression( (float) progOff / (float) progLen ); } while( (len > 0) && threadRunning ); // .... check running .... if( !threadRunning ) break topLevel; if( framesToGo > 0 ) { // need several passes, so copy overlap + create temp // System.out.println( "copy overlap, write to smooth" ); for( ch = 0; ch < inChanNum; ch++ ) { System.arraycopy( inputBuf[ ch ], transLen, inputBuf[ ch ], 0, fltSize << 1 ); // copy overlap } if( passes == 0 ) { offStart = fltSize << 1; overlap = 0; for( j = 0; j < 2; j++ ) { if( tempFile[ 0 ][ j ] == null ) { // ....create temp files if we haven't done it before // System.out.println( " create smooth temp files" ); for( ch = 0; ch < inChanNum; ch++ ) { tempFile[ ch ][ j ] = IOUtil.createTempFile(); floatF[ ch ][ j ] = new FloatFile( tempFile[ ch ][ j ], GenericFile.MODE_OUTPUT ); } } } } } if( floatF[ 0 ][ 1 - smoothIndex ] != null ) { // need several passes, so write out smooth // ---- write smooth ---- off = 0; do { len = Math.min( 8192, (transLen >> 1) - off ); // System.out.println( "wrote "+len+" to smooth "+(1 - smoothIndex)); for( ch = 0; ch < inChanNum; ch++ ) { floatF[ ch ][ 1 - smoothIndex ].writeFloats( smoothBuf[ ch ], off, len ); } off += len; // .... progress .... progOff += len; setProgression( (float) progOff / (float) progLen ); } while( (len > 0) && threadRunning ); } else { // quasi-inplace progOff += (transLen >> 1); } } // for( passes ) // .... check running .... if( !threadRunning ) break topLevel; // ---- do some cleanup ---- if( inF != null ) { // only needed in the first pyramid pass inF.close(); inF = null; } for( i = 0; i < 2; i++ ) { if( floatF[ 0 ][ i ] != null ) { // reset read/write positions for( ch = 0; ch < inChanNum; ch++ ) { floatF[ ch ][ i ].seekFloat( 0 ); } } } if( passes > 1 ) { // swap smooth files 1&2 if we needed several passes smoothIndex = 1 - smoothIndex; } else { // ,else delete smooth files (can continue quasi-inplace) for( i = 0; i < 2; i++ ) { if( tempFile[ 0 ][ i ] != null ) { for( ch = 0; ch < inChanNum; ch++ ) { floatF[ ch ][ i ].cleanUp(); floatF[ ch ][ i ] = null; tempFile[ ch ][ i ].delete(); tempFile[ ch ][ i ] = null; } } } } passLen >>= 1; } // for( pyramid ) // don't forget to measure max. gain in last smooth buf for( ch = 0; ch < inChanNum; ch++ ) { for( j = 0; j < passLen; j++ ) { if( Math.abs( smoothBuf[ ch ][ j ]) > maxAmp ) { maxAmp = Math.abs( smoothBuf[ ch ][ j ]); } } } // ************************************************** BACKWARD ************************************************** } else if( pr.intg[ PR_DIRECTION ] == DIR_BACKWARD ) { inputBuf = new float[ inChanNum ][ inputMem ]; // well, it's actually the output, not the input ;)))) detailBuf = new float[ inChanNum ][ outputMem + (fltSize<<1) ]; smoothBuf = new float[ inChanNum ][ outputMem + (fltSize<<1) ]; passLen = fltSize>>1; // number of samples/2 to synthesize in current pass // ---- read out smooth (>>RAM) ---- inF.readFrames( inputBuf, 0, passLen ); // if( inChanNum > 1 ) { // fucking de-interleave shit // inF.readSamples( buf, 0, passLen * inChanNum ); // for( ch = 0; ch < inChanNum; ch++ ) { // for( i = ch, j = 0; j < passLen; i += inChanNum, j++ ) { // inputBuf[ ch ][ j ] = buf[ i ]; // } // } // } else { // inF.readSamples( inputBuf[ 0 ], 0, passLen ); // System.out.println( "pre-read "+len+" from input = smooth RAM" ); // } // samplesRead = passLen * inChanNum; framesRead = passLen; scale = 0; progOff = 0; progLen = (dataLen<<3) - fltSize*3; // number of operations (for prog-bar) // ---- each pass is one complete synthesis (one backward step of the pyramid) ---- while( (passLen < dataLen) && threadRunning ) { scaleGain = (float) (Param.transform( new Param( -pr.para[ PR_SCALEGAIN ].value * scale, pr.para[ PR_SCALEGAIN ].unit ), Param.ABS_AMP, ampRef, null )).value; scale++; for( ch = 0; ch < inChanNum; ch++ ) { for( i = 0; i < fltSize; i++ ) { detailBuf[ ch ][ i ] = 0.0f; // zero padding left wing smoothBuf[ ch ][ i ] = 0.0f; } } // System.out.println( "-----passLen "+passLen+"; zero padded left wing" ); overlap = fltSize; offStart = fltSize; for( framesToGo = passLen, passes = 0; (framesToGo > 0) && threadRunning; passes++ ) { transLen = Math.min( framesToGo, outputMem ); // number of samples/2 to transform // System.out.println( "pass "+passes+"; transLen "+transLen ); // ---- read smooth+detail ---- off = 0; do { // len = Math.min( samplesToGo, Math.min( 8192 / inChanNum, transLen - off + overlap )); // len2 = len * inChanNum; len = Math.min( framesToGo, Math.min( 8192, transLen - off + overlap )); // read from smooth tempfile if( floatF[ 0 ][ smoothIndex ] != null ) { for( ch = 0; ch < inChanNum; ch++ ) { floatF[ ch ][ smoothIndex ].readFloats( smoothBuf[ ch ], off + offStart, len ); } // read from RAM } else { for( ch = 0; ch < inChanNum; ch++ ) { System.arraycopy( inputBuf[ ch ], off, smoothBuf[ ch ], off + offStart, len ); } } // read detail from input inF.readFrames( detailBuf, off + offStart, len ); // if( inChanNum > 1 ) { // needs de-interleave // inF.readSamples( buf, 0, len2 ); // for( ch = 0; ch < inChanNum; ch++ ) { // for( i = ch, j = off + offStart; i < len2; i += inChanNum, j++ ) { // detailBuf[ ch ][ j ] = buf[ i ]; // } // } // } else { // can read directly // inF.readSamples( detailBuf[ 0 ], off + offStart, len ); // } // apply scale gain for( ch = 0; ch < inChanNum; ch++ ) { for( j = 0, k = off + offStart; j < len; j++, k++ ) { detailBuf[ ch ][ k ] *= scaleGain; } } off += len; framesToGo -= len; // .... progress .... progOff += len<<1; setProgression( (float) progOff / (float) progLen ); } while( (len > 0) && threadRunning ); // .... check running .... if( !threadRunning ) break topLevel; if( framesToGo == 0 ) { transLen += offStart - fltSize; } // zero padding right wing for( ch = 0; ch < inChanNum; ch++ ) { for( k = off + offStart, j = transLen + (fltSize<<1) - k; j > 0; j--, k++ ) { smoothBuf[ ch ][ k ] = 0.0f; detailBuf[ ch ][ k ] = 0.0f; } } //System.out.println( "off+offStart" + (off+offStart)+"; trans until "+(transLen+fltSize+fltSize)); transLen <<= 1; // output samples instead of detail/smooth sample number // ---- transform ---- // System.out.println( "transforming... "+(transLen) ); for( ch = 0; ch < inChanNum; ch++ ) { Wavelet.invTransform( inputBuf[ ch ], smoothBuf[ ch ], detailBuf[ ch ], fltSize, transLen, flt ); // measure max. gain in last pass if( (passLen<<1) >= dataLen ) { for( j = 0; j < transLen; j++ ) { if( Math.abs( inputBuf[ ch ][ j ]) > maxAmp ) { maxAmp = Math.abs( inputBuf[ ch ][ j ]); } } } } progOff += transLen; setProgression( (float) progOff / (float) progLen ); if( !threadRunning ) break topLevel; if( framesToGo > 0 ) { // need several passes, so copy overlap + create temp // System.out.println( "copy overlap, write to smooth" ); for( ch = 0; ch < inChanNum; ch++ ) { System.arraycopy( smoothBuf[ ch ], transLen>>1, smoothBuf[ ch ], 0, fltSize<<1 ); // copy overlap System.arraycopy( detailBuf[ ch ], transLen>>1, detailBuf[ ch ], 0, fltSize<<1 ); } if( passes == 0 ) { offStart = fltSize << 1; overlap = 0; if( tempFile[ 0 ][ 1 - smoothIndex ] == null ) { // ....create temp file if we haven't done it before // System.out.println( " create smooth temp file "+(1 - smoothIndex) ); for( ch = 0; ch < inChanNum; ch++ ) { tempFile[ ch ][ 1 - smoothIndex ] = IOUtil.createTempFile(); floatF[ ch ][ 1 - smoothIndex ] = new FloatFile( tempFile[ ch ][ 1 - smoothIndex ], GenericFile.MODE_OUTPUT ); } } } } if( floatF[ 0 ][ 1 - smoothIndex ] != null ) { // need several passes, so write out smooth // ---- write synthesis ("smooth") ---- off = 0; do { len = Math.min( 8192, transLen - off ); // System.out.println( "wrote "+len+" to smooth "+(1 - smoothIndex)); for( ch = 0; ch < inChanNum; ch++ ) { floatF[ ch ][ 1 - smoothIndex ].writeFloats( inputBuf[ ch ], off, len ); } off += len; // .... progress .... progOff += len; setProgression( (float) progOff / (float) progLen ); } while( (len > 0) && threadRunning ); } else { // quasi-inplace progOff += transLen; } } // for( passes ) // .... check running .... if( !threadRunning ) break topLevel; // ---- do some cleanup ---- for( i = 0; i < 2; i++ ) { if( floatF[ 0 ][ i ] != null ) { // reset read/write positions for( ch = 0; ch < inChanNum; ch++ ) { floatF[ ch ][ i ].seekFloat( 0 ); } } } if( passes > 1 ) { // swap smooth files 1&2 if we needed several passes smoothIndex = 1 - smoothIndex; } passLen <<= 1; } // for( pyramid ) // ---- do some cleanup ---- if( inF != null ) { // close input inF.close(); inF = null; } if( tempFile[ 0 ][ 1 - smoothIndex ] != null ) { // delete unused temp file for( ch = 0; ch < inChanNum; ch++ ) { floatF[ ch ][ 1 - smoothIndex ].cleanUp(); floatF[ ch ][ 1 - smoothIndex ] = null; tempFile[ ch ][ 1 - smoothIndex ].delete(); tempFile[ ch ][ 1 - smoothIndex ] = null; } } } else { // (if backward) throw new IllegalArgumentException( String.valueOf( pr.intg[ PR_DIRECTION ])); } // .... check running .... if( !threadRunning ) break topLevel; // ---- intermezzo: calc gain adjustment ---- peakGain = new Param( maxAmp, Param.ABS_AMP ); if( pr.intg[ PR_GAINTYPE ] == GAIN_ABSOLUTE ) { gain = (float) (Param.transform( pr.para[ PR_GAIN ], Param.ABS_AMP, ampRef, null )).value; } else { gain = (float) (Param.transform( pr.para[ PR_GAIN ], Param.ABS_AMP, new Param( 1.0 / peakGain.value, peakGain.unit ), null )).value; } //System.out.println( "prog "+progOff+" of "+progLen ); // ************************************************** second pass: save output ************************************************** // ************************************************** FORWARD ************************************************** if( pr.intg[ PR_DIRECTION ] == DIR_FORWARD ) { // ---- write out smooth (RAM) ---- for( ch = 0; ch < inChanNum; ch++ ) { for( j = 0; j < passLen; j++ ) { smoothBuf[ ch ][ j ] *= gain; } } outF.writeFrames( smoothBuf, 0, passLen ); // if( inChanNum > 1 ) { // fucking interleave shit // for( ch = 0; ch < inChanNum; ch++ ) { // for( i = ch, j = 0; j < passLen; i += inChanNum, j++ ) { // buf[ i ] = smoothBuf[ ch ][ j ]; // } // } // outF.writeSamples( buf, 0, passLen * inChanNum ); // } else { // outF.writeSamples( smoothBuf[ 0 ], 0, passLen ); // System.out.println( "pre-read "+len+" from input = smooth RAM" ); // } framesWritten = passLen; smoothBuf = null; detailBuf = null; framesRead = 0; // ---- each pass writes out one complete detail line in the pyramid ---- while( (passLen < dataLen) && threadRunning ) { // passes = number of successive transforms of inputBuf in current pass passes = (passLen + inputMem - 1) / inputMem; framesToGo = passLen; for( ch = 0; ch < inChanNum; ch++ ) { floatF[ ch ][ 2 ].seekFloat( dataLen - passLen - framesWritten ); } for( pass = 0; (pass < passes) && threadRunning; pass++ ) { transLen = Math.min( framesToGo, inputMem ); // number of samples to copy // ---- read detail from temp ---- off = 0; do { len = Math.min( 8192, transLen - off ); // len2 = len * inChanNum; for( ch = 0; ch < inChanNum; ch++ ) { floatF[ ch ][ 2 ].readFloats( inputBuf[ ch ], off, len ); } framesRead += len; off += len; // .... progress .... progOff += len; setProgression( (float) progOff / (float) progLen ); } while( (len > 0) && threadRunning ); // .... check running .... if( !threadRunning ) break topLevel; // adjust gain for( ch = 0; ch < inChanNum; ch++ ) { for( j = 0; j < transLen; j++ ) { inputBuf[ ch ][ j ] *= gain; } } // ---- write detail to output ---- off = 0; do { // len = Math.min( 8192 / inChanNum, transLen - off ); // len2 = len * inChanNum; len = Math.min( 8192, transLen - off ); outF.writeFrames( inputBuf, off, len ); // if( inChanNum > 1 ) { // fucking interleave shit // for( ch = 0; ch < inChanNum; ch++ ) { // for( i = ch, j = off; i < len2; i += inChanNum, j++ ) { // buf[ i ] = inputBuf[ ch ][ j ]; // } // } // outF.writeSamples( buf, 0, len2 ); // } else { // outF.writeSamples( inputBuf[ 0 ], off, len2 ); // } framesWritten += len; off += len; // .... progress .... progOff += len; setProgression( (float) progOff / (float) progLen ); } while( (len > 0) && threadRunning ); // .... check running .... if( !threadRunning ) break topLevel; framesToGo -= transLen; } // for( passes ) // .... check running .... if( !threadRunning ) break topLevel; passLen <<= 1; } // for( pyramid ) // .... check running .... if( !threadRunning ) break topLevel; inputBuf = null; for( ch = 0; ch < inChanNum; ch++ ) { floatF[ ch ][ 2 ].cleanUp(); floatF[ ch ][ 2 ] = null; tempFile[ ch ][ 2 ].delete(); tempFile[ ch ][ 2 ] = null; } // ************************************************** BACKWARD ************************************************** } else { framesWritten = 0; smoothBuf = null; detailBuf = null; framesRead = 0; framesToGo = dataLen; while( (framesToGo > 0) && threadRunning ) { transLen = Math.min( framesToGo, inputMem ); // number of samples to copy // ---- read synthesis ("smooth") from temp ---- if( floatF[ 0 ][ smoothIndex ] != null ) { off = 0; do { len = Math.min( 8192, transLen - off ); // len2 = len * inChanNum; for( ch = 0; ch < inChanNum; ch++ ) { i = floatF[ ch ][ smoothIndex ].readFloats( inputBuf[ ch ], off, len ); } framesRead += len; off += len; // .... progress .... progOff += len; setProgression( (float) progOff / (float) progLen ); } while( (len > 0) && threadRunning ); // ---- read synthesis ("smooth") from RAM (inplace) ---- //System.out.println(" out of place" ); } else { //System.out.println(" in place "+transLen+"; to go "+samplesToGo ); progOff += transLen; } // .... check running .... if( !threadRunning ) break topLevel; // adjust gain for( ch = 0; ch < inChanNum; ch++ ) { for( j = 0; j < transLen; j++ ) { inputBuf[ ch ][ j ] *= gain; } } // ---- write detail to output ---- off = 0; do { // len = Math.min( 8192 / inChanNum, transLen - off ); // len2 = len * inChanNum; len = Math.min( 8192, transLen - off ); outF.writeFrames( inputBuf, off, len ); // if( inChanNum > 1 ) { // fucking interleave shit // for( ch = 0; ch < inChanNum; ch++ ) { // for( i = ch, j = off; i < len2; i += inChanNum, j++ ) { // buf[ i ] = inputBuf[ ch ][ j ]; // } // } // outF.writeSamples( buf, 0, len2 ); // } else { // outF.writeSamples( inputBuf[ 0 ], off, len2 ); // } framesWritten += len; off += len; // .... progress .... progOff += len; setProgression( (float) progOff / (float) progLen ); } while( (len > 0) && threadRunning ); // .... check running .... if( !threadRunning ) break topLevel; framesToGo -= transLen; } // while framesToGo // .... check running .... if( !threadRunning ) break topLevel; inputBuf = null; if( tempFile[ 0 ][ smoothIndex ] != null ) { // delete unused temp file for( ch = 0; ch < inChanNum; ch++ ) { floatF[ ch ][ smoothIndex ].cleanUp(); floatF[ ch ][ smoothIndex ] = null; tempFile[ ch ][ smoothIndex ].delete(); tempFile[ ch ][ smoothIndex ] = null; } } } // (else Backward) maxAmp *= gain; // now maxInputAmp = maxOutputAmp outF.close(); outF = null; // System.out.println( "progOff "+progOff+"; progLen "+progLen ); // ---- Finish ---- // inform about clipping/ low level handleClipping( maxAmp ); } catch( IOException e1 ) { setError( e1 ); } catch( OutOfMemoryError e2 ) { inputBuf = null; smoothBuf = null; detailBuf = null; System.gc(); setError( new Exception( ERR_MEMORY )); } // ---- cleanup (topLevel) ---- inputBuf = null; smoothBuf = null; detailBuf = null; if( inF != null ) { inF.cleanUp(); inF = null; } if( outF != null ) { outF.cleanUp(); outF = null; } if( floatF != null ) { for( ch = 0; ch < floatF.length; ch++ ) { for( i = 0; i < 3; i++ ) { if( floatF[ ch ][ i ] != null ) { floatF[ ch ][ i ].cleanUp(); floatF[ ch ][ i ] = null; } if( tempFile[ ch ][ i ] != null ) { tempFile[ ch ][ i ].delete(); tempFile[ ch ][ i ] = null; } } } } } // process() }