/* * SeekEnjoyDlg.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.util.Constants; 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.Region; import de.sciss.io.Span; import javax.swing.*; import java.awt.*; import java.io.EOFException; import java.io.File; import java.io.IOException; import java.util.Vector; /** * Time fragment rearrangier using probably output * from the serial killa. */ public class SeekEnjoyDlg extends ModulePanel { // -------- private variables -------- // Properties (defaults) private static final int PR_INPUTFILE = 0; // pr.text private static final int PR_CTRLFILE = 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_FADETYPE = 2; private static final int PR_GAINTYPE = 3; private static final int PR_SORTING = 4; private static final int PR_MINCHUNK = 0; // pr.para private static final int PR_CROSSFADE = 1; private static final int PR_TOLERANCE = 2; private static final int PR_GAIN = 3; private static final String PRN_INPUTFILE = "InputFile"; private static final String PRN_CTRLFILE = "CtrlFile"; 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_FADETYPE = "FadeType"; private static final String PRN_MINCHUNK = "MinChunk"; private static final String PRN_CROSSFADE = "CrossFade"; private static final String PRN_TOLERANCE = "Tolerance"; private static final String PRN_SORTING = "Sorting"; private static final String[] TYPE_NAMES = { "Equal Energy", "Equal Power" }; private static final int TYPE_ENERGY = 0; private static final int TYPE_POWER = 1; private static final String[] SORT_NAMES = { "Ascending", "Descending" }; private static final int SORT_ASCEND = 0; private static final int SORT_DESCEND = 1; private static final String prText[] = { "", "", "" }; private static final String prTextName[] = { PRN_INPUTFILE, PRN_CTRLFILE, PRN_OUTPUTFILE }; private static final int prIntg[] = { 0, 0, TYPE_POWER, GAIN_UNITY, SORT_ASCEND }; private static final String prIntgName[] = { PRN_OUTPUTTYPE, PRN_OUTPUTRES, PRN_FADETYPE, PRN_GAINTYPE, PRN_SORTING }; private static final Param prPara[] = { null, null, null, null }; private static final String prParaName[] = { PRN_MINCHUNK, PRN_CROSSFADE, PRN_TOLERANCE, PRN_GAIN }; private static final int GG_INPUTFILE = GG_OFF_PATHFIELD + PR_INPUTFILE; private static final int GG_CTRLFILE = GG_OFF_PATHFIELD + PR_CTRLFILE; 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_FADETYPE = GG_OFF_CHOICE + PR_FADETYPE; private static final int GG_GAINTYPE = GG_OFF_CHOICE + PR_GAINTYPE; private static final int GG_SORTING = GG_OFF_CHOICE + PR_SORTING; private static final int GG_MINCHUNK = GG_OFF_PARAMFIELD + PR_MINCHUNK; private static final int GG_CROSSFADE = GG_OFF_PARAMFIELD + PR_CROSSFADE; private static final int GG_TOLERANCE = GG_OFF_PARAMFIELD + PR_TOLERANCE; private static final int GG_GAIN = GG_OFF_PARAMFIELD + PR_GAIN; private static final int GG_CURRENTINFO = GG_OFF_OTHER + 0; private static PropertyArray static_pr = null; private static Presets static_presets = null; private static final String ERR_LENGTH = "Control file cannot be\nlonger than input file!"; // -------- public methods -------- /** * !! setVisible() bleibt dem Aufrufer ueberlassen */ public SeekEnjoyDlg() { super( "Seek + Enjoy" ); 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_MINCHUNK ] = new Param( 50.0, Param.ABS_MS ); static_pr.para[ PR_CROSSFADE ] = new Param( 50.0, Param.FACTOR_TIME ); static_pr.para[ PR_TOLERANCE ] = 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, ggCtrlFile, ggOutputFile; PathField[] ggInputs; ParamField ggMinChunk, ggCrossFade, ggTolerance; ParamSpace[] spcChunk, spcFade, spcTolerance; Component[] ggGain; JTextField ggCurrentInfo; JComboBox ggFadeType, ggSorting; 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 ); ggCtrlFile = new PathField( PathField.TYPE_INPUTFILE + PathField.TYPE_FORMATFIELD, "Select control file" ); ggCtrlFile.handleTypes( GenericFile.TYPES_SOUND ); con.gridwidth = 1; con.weightx = 0.1; gui.addLabel( new JLabel( "Control file", SwingConstants.RIGHT )); con.gridwidth = GridBagConstraints.REMAINDER; con.weightx = 0.9; gui.addPathField( ggCtrlFile, GG_CTRLFILE, 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$F0Enj$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-Gadgets -------- con.fill = GridBagConstraints.BOTH; con.gridwidth = GridBagConstraints.REMAINDER; gui.addLabel( new GroupLabel( "Segmentation Settings", GroupLabel.ORIENT_HORIZONTAL, GroupLabel.BRACE_NONE )); spcChunk = new ParamSpace[ 2 ]; spcChunk[0] = Constants.spaces[ Constants.absMsSpace ]; spcChunk[1] = Constants.spaces[ Constants.absBeatsSpace ]; ggMinChunk = new ParamField( spcChunk ); con.weightx = 0.1; con.gridwidth = 1; gui.addLabel( new JLabel( "Min. Chunk Length", SwingConstants.RIGHT )); con.weightx = 0.4; gui.addParamField( ggMinChunk, GG_MINCHUNK, null ); spcFade = new ParamSpace[ 3 ]; spcFade[0] = Constants.spaces[ Constants.absMsSpace ]; spcFade[1] = Constants.spaces[ Constants.absBeatsSpace ]; spcFade[2] = new ParamSpace( Constants.spaces[ Constants.ratioTimeSpace ]); // spcFade[2].max = 50.0; spcFade[2] = new ParamSpace( spcFade[2].min, 50.0, spcFade[2].inc, spcFade[2].unit ); ggCrossFade = new ParamField( spcFade ); ggCrossFade.setReference( ggMinChunk ); con.weightx = 0.1; gui.addLabel( new JLabel( "Crossfades", SwingConstants.RIGHT )); con.weightx = 0.4; con.gridwidth = GridBagConstraints.REMAINDER; gui.addParamField( ggCrossFade, GG_CROSSFADE, null ); spcTolerance = new ParamSpace[ 2 ]; spcTolerance[0] = Constants.spaces[ Constants.offsetAmpSpace ]; spcTolerance[1] = Constants.spaces[ Constants.decibelAmpSpace ]; ggTolerance = new ParamField( spcTolerance ); ggTolerance.setReference( new Param( 1.0, Param.ABS_AMP )); con.weightx = 0.1; con.gridwidth = 1; gui.addLabel( new JLabel( "Tolerance", SwingConstants.RIGHT )); con.weightx = 0.4; gui.addParamField( ggTolerance, GG_TOLERANCE, null ); ggFadeType = new JComboBox(); for( int i = 0; i < TYPE_NAMES.length; i++ ) { ggFadeType.addItem( TYPE_NAMES[ i ]); } con.weightx = 0.1; gui.addLabel( new JLabel( "Crossfade Type", SwingConstants.RIGHT )); con.weightx = 0.4; con.gridwidth = GridBagConstraints.REMAINDER; gui.addChoice( ggFadeType, GG_FADETYPE, null ); ggCurrentInfo = new JTextField(); ggCurrentInfo.setEditable( false ); ggCurrentInfo.setBackground( null ); con.weightx = 0.1; con.gridwidth = 1; gui.addLabel( new JLabel( "# of Regions", SwingConstants.RIGHT )); con.weightx = 0.4; gui.addTextField( ggCurrentInfo, GG_CURRENTINFO, null ); ggSorting = new JComboBox(); for( int i = 0; i < SORT_NAMES.length; i++ ) { ggSorting.addItem( SORT_NAMES[ i ]); } con.weightx = 0.1; gui.addLabel( new JLabel( "Sorting", SwingConstants.RIGHT )); con.weightx = 0.4; con.gridwidth = GridBagConstraints.REMAINDER; gui.addChoice( ggSorting, GG_SORTING, null ); initGUI( this, FLAGS_PRESETS | FLAGS_PROGBAR, gui ); } /** * Transfer values from prop-array to GUI */ public void fillGUI() { super.fillGUI(); super.fillGUI( gui ); } /** * Transfer values from GUI to prop-array */ public void fillPropertyArray() { super.fillPropertyArray(); super.fillPropertyArray( gui ); } // -------- Processor Interface -------- protected void process() { int i, j, k, m, chunkLength; int off, prevOff, len, ch; long progOff, progLen, oldProg; double d1; float f1, f2; // io AudioFile inF = null; AudioFile ctrlF = null; AudioFile outF = null; AudioFileDescr inStream, ctrlStream, outStream; FloatFile[] floatF = null; File tempFile[] = null; int inChanNum, ctrlChanNum; float[][] inBuf, outBuf, ctrlBuf2; float[] ctrlBuf, ctrlBufSort; int[] ctrlIdx, ctrlIdxRvs, ctrlIdxFoo; float[] convBuf1, convBuf2; int inLength, ctrlLength; int framesRead; int minCtrl, fadeOut; float topTol, botTol, ctrlRate, ctrl, ctrlBot, ctrlTop; Region currentRegion, prevRegion, nextRegion; Vector<Region> regionList; Param chunkLenParam = new Param( 0.0, Param.ABS_MS ); PathField ggOutput; float gain = 1.0f; // gain abs amp Param ampRef = new Param( 1.0, Param.ABS_AMP ); // transform-Referenz float maxAmp = 0.0f; topLevel: try { gui.stringToJTextField( "", GG_CURRENTINFO ); // ---- 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 ); ctrlF = AudioFile.openAsRead( new File( pr.text[ PR_CTRLFILE ])); ctrlStream = ctrlF.getDescr(); ctrlChanNum = ctrlStream.channels; ctrlLength = (int) ctrlStream.length; // this helps to prevent errors from empty files! if( (ctrlLength < 1) || (ctrlChanNum < 1) ) throw new EOFException( ERR_EMPTY ); if( ctrlLength > inLength ) throw new IOException( ERR_LENGTH ); ctrlRate = (float) ctrlLength / (float) inLength; // ---- open output ---- ggOutput = (PathField) gui.getItemObj( GG_OUTPUTFILE ); if( ggOutput == null ) throw new IOException( ERR_MISSINGPROP ); outStream = new AudioFileDescr( inStream ); ggOutput.fillStream( outStream ); outF = AudioFile.openAsWrite( outStream ); // .... check running .... if( !threadRunning ) break topLevel; progOff = 0; progLen = ((long) inLength << 1) + ((long) ctrlLength << 1); // normalization requires temp files if( pr.intg[ PR_GAINTYPE ] == GAIN_UNITY ) { tempFile = new File[ inChanNum ]; floatF = new FloatFile[ inChanNum ]; for( ch = 0; ch < inChanNum; ch++ ) { // first zero them because an exception might be thrown tempFile[ ch ] = null; floatF[ ch ] = null; } for( ch = 0; ch < inChanNum; ch++ ) { tempFile[ ch ] = IOUtil.createTempFile(); floatF[ ch ] = new FloatFile( tempFile[ ch ], GenericFile.MODE_OUTPUT ); } progLen += inLength; } else { gain = (float) (Param.transform( pr.para[ PR_GAIN ], Param.ABS_AMP, ampRef, null )).value; } // .... check running .... if( !threadRunning ) break topLevel; // ---- completely read in control file ---- ctrlBuf2 = new float[ ctrlChanNum ][ 8192 ]; ctrlBuf = new float[ ctrlLength ]; ctrlBufSort = new float[ ctrlLength ]; ctrlIdx = new int[ ctrlLength ]; // mapping ctrlBufSort -> ctrlBuf ctrlIdxRvs = new int[ ctrlLength ]; // mapping ctrlBuf -> ctrlBufSort ctrlIdxFoo = new int[ ctrlLength ]; for( i = 0; i < ctrlLength; i++ ) ctrlIdx[ i ] = i; System.arraycopy( ctrlIdx, 0, ctrlIdxRvs, 0, ctrlLength ); for( framesRead = 0; threadRunning && (framesRead < ctrlLength); ) { len = Math.min( 8192, ctrlLength - framesRead ); ctrlF.readFrames( ctrlBuf2, 0, len ); System.arraycopy( ctrlBuf2[0], 0, ctrlBuf, framesRead, len ); framesRead += len; progOff += len; // .... progress .... setProgression( (float) progOff / (float) progLen ); } // .... check running .... if( !threadRunning ) break topLevel; ctrlF.close(); ctrlF = null; // ---- normalize control file ---- ctrlTop = Float.NEGATIVE_INFINITY; ctrlBot = Float.POSITIVE_INFINITY; for( i = 0; i < ctrlLength; i++ ) { ctrl = ctrlBuf[ i ]; if( ctrl < ctrlBot ) { ctrlBot = ctrl; } if( ctrl > ctrlTop ) { ctrlTop = ctrl; } } if( ctrlBot != ctrlTop ) { f1 = 1.0f / (ctrlTop - ctrlBot); } else { f1 = 1.0f; } for( i = 0; i < ctrlLength; i++ ) { ctrlBuf[ i ] = (ctrlBuf[ i ] - ctrlBot) * f1; } // ---- sort controlfile ---- // float[] test = new float[ ctrlLength ]; // for( i = 0; i < ctrlLength; i++ ) test[i]=ctrlIdx[i]; // Debug.view( ctrlBuf, "Ctrl" ); // Debug.view( test, "CtrlIdx" ); System.arraycopy( ctrlBuf, 0, ctrlBufSort, 0, ctrlLength ); Util.quickSortFloat( ctrlBufSort, ctrlIdx, ctrlLength ); // sorts in ascending order if( pr.intg[ PR_SORTING ] == SORT_DESCEND ) { for( i = 0, j = ctrlLength-1; i < j; i++, j-- ) { ctrl = ctrlBufSort[ i ]; ctrlBufSort[ i ] = ctrlBufSort[ j]; ctrlBufSort[ j] = ctrl; k = ctrlIdx[ i ]; ctrlIdx[ i ] = ctrlIdx[ j ]; ctrlIdx[ j ] = k; } } // for( i = 1; i < ctrlLength; i++ ) { // if( ctrlBufSort[ i-1 ] > ctrlBufSort[ i ]) { System.out.println( "Sort error" ); break; } // } // for( i = 0; i < ctrlLength; i++ ) { // if( ctrlBufSort[ i ] != ctrlBuf[ ctrlIdx[ i ]]) { System.out.println( "Idx error" ); break; } // } // Debug.view( ctrlBufSort, "Ctrl sort" ); // for( i = 0; i < ctrlLength; i++ ) test[i]=ctrlIdx[i]; // Debug.view( test, "CtrlIdx sort" ); System.arraycopy( ctrlIdx, 0, ctrlIdxFoo, 0, ctrlLength ); Util.quickSortInt( ctrlIdxFoo, ctrlIdxRvs, ctrlLength ); progOff += ctrlLength; // .... progress .... setProgression( (float) progOff / (float) progLen ); // for( i = 0; i < ctrlLength; i++ ) { // if( ctrlIdxRvs[ ctrlIdx[ i ]] != i ) { System.out.println( "Reversal error1" ); break; } // if( ctrlIdx[ ctrlIdxRvs[ i ]] != i ) { System.out.println( "Reversal error2" ); break; } // } // ---- misc inits ---- d1 = Param.transform( pr.para[ PR_MINCHUNK ], Param.ABS_MS, null, null ).value; d1 = AudioFileDescr.millisToSamples( inStream, d1 ); // minChunk = Math.max( 2, (int) (d1 + 0.5) ); minCtrl = Math.max( 1, (int) (Math.ceil( d1 * ctrlRate ) + 0.5) ); d1 = Param.transform( pr.para[ PR_TOLERANCE ], Param.ABS_AMP, new Param( 1.0, Param.ABS_AMP ), null ).value; if( d1 > 1.0 ) { topTol = (float) d1; botTol = (float) (1.0 / d1); } else { botTol = (float) d1; topTol = (float) (1.0 / d1); } // System.out.println( "inLength "+inLength+"; ctrlLength "+ctrlLength+"; ctrlRate "+ctrlRate+"; minChunk "+minChunk+"; minCtrl "+minCtrl+"; fade "+crossFade ); regionList = new Vector<Region>(); // Elemente = de.sciss.fscape.io.Region, chronologically added inBuf = new float[ inChanNum ][ 8192 ]; outBuf = new float[ inChanNum ][ 8192 ]; // ================ PASS 1: Region detection ================ off = 0; regionLp: while( true ) { // find next valid offset while( (off < ctrlLength) && (ctrlIdx[ off ] < 0) ) off++; if( off == ctrlLength ) break regionLp; // no more regions left i = ctrlIdx[ off ]; ctrl = ctrlBufSort[ off ]; ctrlTop = ctrl * topTol; ctrlBot = ctrl * botTol; // j = Math.min( ctrlLength, i + minChunk ); // while( (j < ctrlLength) && (ctrlBuf[ j ] <= ctrlTop) && (ctrlBuf[ j ] >= ctrlBot) ) j++; // i--; // while( (i > 0) && (ctrlBuf[ i ] <= ctrlTop) && (ctrlBuf[ i ] >= ctrlBot) ) i--; // i++; i = Math.max( 0, i - (minCtrl >> 1) ); j = Math.min( ctrlLength, i + minCtrl ); while( (j < ctrlLength) && (ctrlBuf[ j ] <= ctrlTop) && (ctrlBuf[ j ] >= ctrlBot) && (ctrlIdx[ ctrlIdxRvs[ j ]] >= 0) ) j++; i--; while( (i > 0) && (ctrlBuf[ i ] <= ctrlTop) && (ctrlBuf[ i ] >= ctrlBot) && (ctrlIdx[ ctrlIdxRvs[ i ]] >= 0) ) i--; i++; // clear out ctrlIdx's so they will be skipped in the next region search for( k = i; k < j; k++ ) { ctrlIdx[ ctrlIdxRvs[ k ]] = -1; } // chunks -> samples i = (int) (i / ctrlRate + 0.5f); j = Math.max( i, Math.min( inLength, (int) (j / ctrlRate + 0.5f) )); // System.out.println( "Region : "+i+" ... "+j ); currentRegion = new Region( new Span( i, j ), null ); regionList.addElement( currentRegion ); } // .... check running .... if( !threadRunning ) break topLevel; gui.stringToJTextField( String.valueOf( regionList.size()), GG_CURRENTINFO ); // ================ PASS 2: Region copying ================ len = 0; for( i = 1; i < regionList.size(); i++ ) { currentRegion = regionList.elementAt( i ); len += currentRegion.span.getLength(); } progLen = (long) len * (pr.intg[ PR_GAINTYPE ] == GAIN_UNITY ? 3 : 2); d1 = getProgression(); progOff = (long) (progLen * d1 / (1.0 - d1)); progLen += progOff; regionList.addElement( new Region( new Span( inLength, inLength ), null )); currentRegion = new Region( new Span( 0, 0 ), null ); nextRegion = regionList.elementAt( 0 ); off = 0; fadeOut = 0; // System.out.println( "off "+ progOff + " ; len "+progLen +"; delta "+(progLen - progOff)+"; allregions "+len ); for( i = 1; i < regionList.size(); i++ ) { oldProg = progOff; // System.out.println( i + " : "+fadeOut ); prevRegion = currentRegion; currentRegion = nextRegion; nextRegion = regionList.elementAt( i ); prevOff = off; off = 0; chunkLength = fadeOut; while( chunkLength > 0 ) { len = Math.min( 8192, chunkLength ); inF.seekFrame( prevOff + prevRegion.span.getStart() ); inF.readFrames( outBuf, 0, len ); inF.seekFrame( off + currentRegion.span.getStart() ); inF.readFrames( inBuf, 0, len ); switch( pr.intg[ PR_FADETYPE ]) { case TYPE_ENERGY: for( ch = 0; ch < inChanNum; ch++ ) { convBuf1 = inBuf[ ch ]; convBuf2 = outBuf[ ch ]; for( j = 0, k = off; j < len; j++, k++ ) { f1 = (float) k / (float) fadeOut; convBuf2[ j ] = convBuf1[ j ] * f1 + convBuf2[ j ] * (1.0f - f1); // X-Fade } } break; case TYPE_POWER: for( ch = 0; ch < inChanNum; ch++ ) { convBuf1 = inBuf[ ch ]; convBuf2 = outBuf[ ch ]; for( j = 0, k = off; j < len; j++, k++ ) { f1 = (float) k / (float) fadeOut; f2 = 1.0f - f1; convBuf2[ j ] = convBuf1[ j ] * (1.0f - f2*f2) + convBuf2[ j ] * (1.0f - f1*f1); // X-Fade } } break; } if( floatF != null ) { for( ch = 0; ch < inChanNum; ch++ ) { convBuf1 = outBuf[ ch ]; for( j = 0; j < len; j++ ) { // measure max amp f1 = Math.abs( convBuf1[ j ]); if( f1 > maxAmp ) { maxAmp = f1; } } floatF[ ch ].writeFloats( convBuf1, 0, len ); } } else { // i.e. abs gain for( ch = 0; ch < inChanNum; ch++ ) { convBuf1 = outBuf[ ch ]; for( j = 0; j < len; j++ ) { // measure max amp + adjust gain f1 = Math.abs( convBuf1[ j ]); convBuf1[ j ] *= gain; if( f1 > maxAmp ) { maxAmp = f1; } } } outF.writeFrames( outBuf, 0, len ); } prevOff += len; off += len; chunkLength -= len; progOff += len; // .... progress .... setProgression( (float) progOff / (float) progLen ); // .... check running .... if( !threadRunning ) break topLevel; } j = (int) currentRegion.span.getLength(); k = (int) nextRegion.span.getLength(); m = Math.min( j, k ); // System.out.println(" :::: "+j+" ; "+k+"; "+m +"; "+ (Math.min( k >> 1, j - off )) ); chunkLenParam.value = AudioFileDescr.samplesToMillis( inStream, m ); d1 = Param.transform( pr.para[ PR_CROSSFADE ], Param.ABS_MS, chunkLenParam, null ).value; fadeOut = Math.min( Math.min( k >> 1, j - off ), (int) (AudioFileDescr.millisToSamples( inStream, d1 ) + 0.5) ); chunkLength = j - fadeOut; while( off < chunkLength ) { len = Math.min( 8192, chunkLength - off ); inF.readFrames( outBuf, 0, len ); if( floatF != null ) { for( ch = 0; ch < inChanNum; ch++ ) { convBuf1 = outBuf[ ch ]; for( k = 0; k < len; k++ ) { // measure max amp f1 = Math.abs( convBuf1[ k ]); if( f1 > maxAmp ) { maxAmp = f1; } } floatF[ ch ].writeFloats( convBuf1, 0, len ); } } else { // i.e. abs gain for( ch = 0; ch < inChanNum; ch++ ) { convBuf1 = outBuf[ ch ]; for( k = 0; k < len; k++ ) { // measure max amp + adjust gain f1 = Math.abs( convBuf1[ k ]); convBuf1[ k ] *= gain; if( f1 > maxAmp ) { maxAmp = f1; } } } outF.writeFrames( outBuf, 0, len ); } off += len; progOff += len; // .... progress .... setProgression( (float) progOff / (float) progLen ); // .... check running .... if( !threadRunning ) break topLevel; } progOff = oldProg + j + j; } inF.close(); inF = null; // ---- normalize output ---- 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; normalizeAudioFile( floatF, outF, outBuf, gain, 1.0f ); for( ch = 0; ch < inChanNum; ch++ ) { floatF[ ch ].cleanUp(); floatF[ ch ] = null; tempFile[ ch ].delete(); tempFile[ ch ] = null; } } // .... check running .... if( !threadRunning ) break topLevel; outF.close(); outF = null; setProgression( 1.0f ); // inform about clipping/ low level maxAmp *= gain; handleClipping( maxAmp ); } catch( IOException e1 ) { setError( e1 ); } catch( OutOfMemoryError e2 ) { inStream = null; ctrlStream = null; outStream = null; inBuf = null; ctrlBuf = null; ctrlBuf2 = null; ctrlIdx = null; convBuf1 = null; convBuf2 = null; System.gc(); setError( new Exception( ERR_MEMORY )); } // ---- cleanup (topLevel) ---- if( outF != null ) { outF.cleanUp(); } if( inF != null ) { inF.cleanUp(); } if( ctrlF != null ) { ctrlF.cleanUp(); } if( floatF != null ) { for( ch = 0; ch < floatF.length; ch++ ) { if( floatF[ ch ] != null ) floatF[ ch ].cleanUp(); if( tempFile[ ch ] != null ) tempFile[ ch ].delete(); } } } // process() }