/* * ConcatDlg.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: * 25-Jun-06 fixed bug in analyze / synthesize names ; reverse order works */ 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.gui.PathEvent; import de.sciss.gui.PathListener; 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; import java.text.MessageFormat; import java.util.Vector; /** * Processing module for concatening * a bunch of sound files with optional * overlap and cross-fade. */ public class ConcatDlg extends ModulePanel { // -------- private variables -------- // Properties (defaults) private static final int PR_INPUTFILE1 = 0; // pr.text private static final int PR_INPUTFILE2 = 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_FADESHAPE = 3; private static final int PR_FADETYPE = 4; private static final int PR_GAIN = 0; // pr.para private static final int PR_OFFSET = 1; private static final int PR_LENGTH = 2; private static final int PR_OVERLAP = 3; private static final int PR_FADE = 4; private static final String PRN_INPUTFILE1 = "InputFile1"; private static final String PRN_INPUTFILE2 = "InputFile2"; 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_FADESHAPE = "FadeShape"; private static final String PRN_FADETYPE = "FadeType"; private static final String PRN_OFFSET = "Offset"; private static final String PRN_LENGTH = "Length"; private static final String PRN_OVERLAP = "Overlap"; private static final String PRN_FADE = "Fade"; protected static final String[] SHAPE_NAMES = { "Normal", "Fast in", "Slow in", "Easy in Easy out" }; private static final int SHAPE_NORMAL = 0; // private static final int SHAPE_FASTIN = 1; // private static final int SHAPE_SLOWIN = 2; // private static final int SHAPE_EASY = 3; 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 prText[] = { "", "", "" }; private static final String prTextName[] = { PRN_INPUTFILE1, PRN_INPUTFILE2, PRN_OUTPUTFILE }; private static final int prIntg[] = { 0, 0, GAIN_UNITY, SHAPE_NORMAL, TYPE_POWER }; private static final String prIntgName[] = { PRN_OUTPUTTYPE, PRN_OUTPUTRES, PRN_GAINTYPE, PRN_FADESHAPE, PRN_FADETYPE }; private static final Param prPara[] = { null, null, null, null, null }; private static final String prParaName[] = { PRN_GAIN, PRN_OFFSET, PRN_LENGTH, PRN_OVERLAP, PRN_FADE }; private static final int GG_INPUTFILE1 = GG_OFF_PATHFIELD + PR_INPUTFILE1; private static final int GG_INPUTFILE2 = GG_OFF_PATHFIELD + PR_INPUTFILE2; private static final int GG_OUTPUTFILE = GG_OFF_PATHFIELD + PR_OUTPUTFILE; private static final int GG_OUTPUTTYPE = GG_OFF_CHOICE + PR_OUTPUTTYPE; private static final int GG_OUTPUTRES = GG_OFF_CHOICE + PR_OUTPUTRES; private static final int GG_GAINTYPE = GG_OFF_CHOICE + PR_GAINTYPE; private static final int GG_FADESHAPE = GG_OFF_CHOICE + PR_FADESHAPE; private static final int GG_FADETYPE = GG_OFF_CHOICE + PR_FADETYPE; private static final int GG_GAIN = GG_OFF_PARAMFIELD + PR_GAIN; private static final int GG_OFFSET = GG_OFF_PARAMFIELD + PR_OFFSET; private static final int GG_LENGTH = GG_OFF_PARAMFIELD + PR_LENGTH; private static final int GG_OVERLAP = GG_OFF_PARAMFIELD + PR_OVERLAP; private static final int GG_FADE = GG_OFF_PARAMFIELD + PR_FADE; 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_NOFILES = "No files detected"; private static final String FILENAMEPTRN = "{1}{0}{2}"; private Object[] fileNameArgs = new Object[5]; // private MessageFormat fileNameForm = new MessageFormat( FILENAMEPTRN ); private boolean usePad; private boolean reverse; // -------- public methods -------- public ConcatDlg() { super("Concat"); init2(); } protected void buildGUI() { 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_OFFSET ] = new Param( 0.0, Param.ABS_MS ); static_pr.para[ PR_LENGTH ] = new Param( 100.0, Param.FACTOR_TIME ); static_pr.para[ PR_OVERLAP ] = new Param( 0.0, Param.FACTOR_TIME ); static_pr.para[ PR_FADE ] = new Param( 0.0, Param.FACTOR_TIME ); 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(); // -------- Misc init -------- // fileNameForm.setLocale( Locale.US ); // fileNameForm.applyPattern( FILENAMEPTRN ); // -------- build GUI -------- GridBagConstraints con; PathField ggInputFile1, ggInputFile2, ggOutputFile; Component[] ggGain; PathField[] ggInputs; ParamField ggOffset, ggLength, ggOverlap, ggFade; JComboBox ggFadeShape, ggFadeType; ParamSpace[] spcOffset, spcLength; ParamSpace spc; JTextField ggCurrentInfo; gui = new GUISupport(); con = gui.getGridBagConstraints(); con.insets = new Insets( 1, 2, 1, 2 ); PathListener pathL = new PathListener() { public void pathChanged( PathEvent e ) { int ID = gui.getItemID( e ); switch( ID ) { case GG_INPUTFILE1: case GG_INPUTFILE2: pr.text[ ID - GG_OFF_PATHFIELD ] = ((PathField) e.getSource()).getPath().getPath(); if( ID == GG_INPUTFILE1 ) { setInput( pr.text[ ID - GG_OFF_PATHFIELD ]); } analyseFileNames(); break; } } }; // -------- Input-Gadgets -------- con.fill = GridBagConstraints.BOTH; con.gridwidth = GridBagConstraints.REMAINDER; gui.addLabel( new GroupLabel( "Waveform I/O", GroupLabel.ORIENT_HORIZONTAL, GroupLabel.BRACE_NONE )); ggInputFile1 = new PathField( PathField.TYPE_INPUTFILE + PathField.TYPE_FORMATFIELD, "Select first input file" ); ggInputFile1.handleTypes( GenericFile.TYPES_SOUND ); con.gridwidth = 1; con.weightx = 0.1; gui.addLabel( new JLabel( "First input file", SwingConstants.RIGHT )); con.gridwidth = GridBagConstraints.REMAINDER; con.weightx = 0.9; gui.addPathField( ggInputFile1, GG_INPUTFILE1, pathL ); ggInputFile2 = new PathField( PathField.TYPE_INPUTFILE + PathField.TYPE_FORMATFIELD, "Select first input file" ); ggInputFile2.handleTypes( GenericFile.TYPES_SOUND ); con.gridwidth = 1; con.weightx = 0.1; gui.addLabel( new JLabel( "Last input file", SwingConstants.RIGHT )); con.gridwidth = GridBagConstraints.REMAINDER; con.weightx = 0.9; gui.addPathField( ggInputFile2, GG_INPUTFILE2, pathL ); ggCurrentInfo = new JTextField(); ggCurrentInfo.setEditable( false ); ggCurrentInfo.setBackground( null ); con.weightx = 0.1; con.gridwidth = 1; gui.addLabel( new JLabel( "Current input", SwingConstants.RIGHT )); con.gridwidth = GridBagConstraints.REMAINDER; con.weightx = 0.9; gui.addTextField( ggCurrentInfo, GG_CURRENTINFO, 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 ] = ggInputFile1; ggOutputFile.deriveFrom( ggInputs, "$D0$F0CC$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, pathL ); 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 -------- gui.addLabel( new GroupLabel( "Scissor Settings", GroupLabel.ORIENT_HORIZONTAL, GroupLabel.BRACE_NONE )); spcOffset = new ParamSpace[ 3 ]; spcOffset[0] = Constants.spaces[ Constants.absMsSpace ]; spcOffset[1] = Constants.spaces[ Constants.absBeatsSpace ]; spcOffset[2] = Constants.spaces[ Constants.ratioTimeSpace ]; spcLength = new ParamSpace[ 6 ]; spcLength[0] = Constants.spaces[ Constants.absMsSpace ]; spc = new ParamSpace( Constants.spaces[ Constants.offsetMsSpace ]); // spc.max = 0.0; spc = new ParamSpace( spc.min, 0.0, spc.inc, spc.unit ); spcLength[1] = spc; spcLength[2] = Constants.spaces[ Constants.absBeatsSpace ]; spc = new ParamSpace( Constants.spaces[ Constants.offsetBeatsSpace ]); // spc.max = 0.0; spc = new ParamSpace( spc.min, 0.0, spc.inc, spc.unit ); spcLength[3] = spc; spc = new ParamSpace( Constants.spaces[ Constants.offsetTimeSpace ]); // spc.max = 0.0; spc = new ParamSpace( spc.min, 0.0, spc.inc, spc.unit ); spcLength[4] = spc; spcLength[5] = Constants.spaces[ Constants.ratioTimeSpace ]; con.fill = GridBagConstraints.BOTH; con.gridwidth = GridBagConstraints.REMAINDER; ggOffset = new ParamField( spcOffset ); con.weightx = 0.1; con.gridwidth = 1; gui.addLabel( new JLabel( "Chunk Offset", SwingConstants.RIGHT )); con.weightx = 0.4; gui.addParamField( ggOffset, GG_OFFSET, null ); ggLength = new ParamField( spcLength ); con.weightx = 0.1; con.gridwidth = 1; gui.addLabel( new JLabel( "Chunk Length", SwingConstants.RIGHT )); con.weightx = 0.4; con.gridwidth = GridBagConstraints.REMAINDER; gui.addParamField( ggLength, GG_LENGTH, null ); ggOverlap = new ParamField( spcLength ); ggOverlap.setReference( ggLength ); con.weightx = 0.1; con.gridwidth = 1; gui.addLabel( new JLabel( "Chunk Overlap", SwingConstants.RIGHT )); con.weightx = 0.4; gui.addParamField( ggOverlap, GG_OVERLAP, null ); ggFade = new ParamField( spcOffset ); ggFade.setReference( ggOverlap ); con.weightx = 0.1; con.gridwidth = 1; gui.addLabel( new JLabel( "Overlap Cross-fade", SwingConstants.RIGHT )); con.weightx = 0.4; con.gridwidth = GridBagConstraints.REMAINDER; gui.addParamField( ggFade, GG_FADE, null ); ggFadeShape = new JComboBox(); ggFadeShape.setEnabled( false ); for( int i = 0; i < SHAPE_NAMES.length; i++ ) { ggFadeShape.addItem( SHAPE_NAMES[ i ]); } con.weightx = 0.1; con.gridwidth = 1; gui.addLabel( new JLabel( "Cross-fade Shape", SwingConstants.RIGHT )); con.weightx = 0.4; gui.addChoice( ggFadeShape, GG_FADESHAPE, null ); ggFadeType = new JComboBox(); for( int i = 0; i < TYPE_NAMES.length; i++ ) { ggFadeType.addItem( TYPE_NAMES[ i ]); } con.weightx = 0.1; con.gridwidth = 1; gui.addLabel( new JLabel( "Cross-fade Type", SwingConstants.RIGHT )); con.weightx = 0.4; con.gridwidth = GridBagConstraints.REMAINDER; gui.addChoice( ggFadeType, GG_FADETYPE, null ); initGUI( this, FLAGS_PRESETS | FLAGS_PROGBAR, gui ); } public void fillGUI() { super.fillGUI(); super.fillGUI(gui); } public void fillPropertyArray() { super.fillPropertyArray(); super.fillPropertyArray(gui); } // -------- Processor Interface -------- protected void process() { int i, j, k, m, n, p; int ch; long progOff, progLen; double d1; float f1; // io SoundChunk[] sndChunks = null; SoundChunk currentChunk; AudioFile outF = null; AudioFileDescr outStream = null; FloatFile[] floatF = null; File tempFile[] = null; float[][] inBuf, outBuf; float[] convBuf1; float gain = 1.0f; // gain abs amp Param ampRef = new Param( 1.0, Param.ABS_AMP ); // transform-Referenz Param ref; int numChunks, outChanNum, outLength, nextOpened; int framesWritten, chunkLength, relOff; float maxAmp = 0.0f; float fadeY1, fadeY2; PathField ggOutput; Vector<SoundChunk> activeChunks = new Vector<SoundChunk>(); topLevel: try { // ---- open input, output; init ---- numChunks = analyseFileNames(); if( numChunks <= 0 ) throw new EOFException( ERR_NOFILES ); sndChunks = new SoundChunk[ numChunks ]; progOff = 0; progLen = numChunks * 25; outChanNum = 1; outLength = 0; for( i = 0; i < numChunks; i++ ) { sndChunks[i] = new SoundChunk(); sndChunks[i].name = synthesizeFileName( reverse ? (numChunks - i - 1) : i ); gui.stringToJTextField( "Opening '" + sndChunks[i].name + "'...", GG_CURRENTINFO ); //File xxx = new File( sndChunks[i].name ); //System.err.println( "\""+xxx.toString()+"\" ; exists ? "+xxx.exists() ); sndChunks[i].f = AudioFile.openAsRead( new File( sndChunks[i].name )); sndChunks[i].stream = sndChunks[i].f.getDescr(); if( sndChunks[i].stream.channels <= 0 ) throw new EOFException( ERR_EMPTY ); outChanNum = Math.max( outChanNum, sndChunks[i].stream.channels ); ref = new Param( AudioFileDescr.samplesToMillis( sndChunks[i].stream, sndChunks[i].stream.length ), Param.ABS_MS ); sndChunks[i].off = (int) Math.max( 0, Math.min( sndChunks[i].stream.length, (int) (AudioFileDescr.millisToSamples( sndChunks[i].stream, (Param.transform( pr.para[ PR_OFFSET ], Param.ABS_MS, ref, null )).value) + 0.5) )); sndChunks[i].len = (int) Math.max( 0, Math.min( sndChunks[i].stream.length - sndChunks[i].off, (int) (AudioFileDescr.millisToSamples( sndChunks[i].stream, (Param.transform( pr.para[ PR_LENGTH ], Param.ABS_MS, ref, null )).value) + 0.5) )); ref = new Param( AudioFileDescr.samplesToMillis( sndChunks[i].stream, sndChunks[i].len ), Param.ABS_MS ); sndChunks[i].over = Math.max( 0, Math.min( sndChunks[i].len, (int) (AudioFileDescr.millisToSamples( sndChunks[i].stream, (Param.transform( pr.para[ PR_OVERLAP ], Param.ABS_MS, ref, null )).value) + 0.5) )); ref = new Param( AudioFileDescr.samplesToMillis( sndChunks[i].stream, sndChunks[i].over ), Param.ABS_MS ); j = Math.max( 0, Math.min( sndChunks[i].over, (int) (AudioFileDescr.millisToSamples( sndChunks[i].stream, (Param.transform( pr.para[ PR_FADE ], Param.ABS_MS, ref, null )).value) + 0.5) )); if( i > 0 ) { sndChunks[i].fadeIn = Math.min( j, sndChunks[i-1].fadeOut ); sndChunks[i-1].fadeOut = sndChunks[i].fadeIn; sndChunks[i].punchIn = sndChunks[i-1].punchIn + sndChunks[i-1].len - sndChunks[i-1].over; } else { sndChunks[i].fadeIn = 0; sndChunks[i].punchIn = 0; } sndChunks[i].fadeOut= Math.min( j, sndChunks[i].len - sndChunks[i].fadeIn ); outLength = Math.max( outLength, sndChunks[i].punchIn + sndChunks[i].len ); progOff++; // .... progress .... setProgression( (float) progOff / (float) progLen ); // .... check running .... if( !threadRunning ) break topLevel; } sndChunks[numChunks-1].fadeOut = 0; /* for( i = 0; i < numChunks; i++ ) { System.out.println( sndChunks[i].name ); System.out.println( "offset "+ sndChunks[i].off ); System.out.println( "length "+ sndChunks[i].len ); System.out.println( "overlap "+ sndChunks[i].over ); System.out.println( "fade in "+ sndChunks[i].fadeIn ); System.out.println( "fade out "+ sndChunks[i].fadeOut ); System.out.println( "punch in "+ sndChunks[i].punchIn ); } System.out.println( "outLen "+ outLength ); */ // output ggOutput = (PathField) gui.getItemObj( GG_OUTPUTFILE ); if( ggOutput == null ) throw new IOException( ERR_MISSINGPROP ); outStream = new AudioFileDescr( sndChunks[0].stream ); ggOutput.fillStream( outStream ); outF = AudioFile.openAsWrite( outStream ); // .... check running .... if( !threadRunning ) break topLevel; inBuf = new float[ outChanNum ][ 8192 ]; outBuf = new float[ outChanNum ][ 8192 ]; progLen = (long) outLength * (pr.intg[ PR_GAINTYPE ] == GAIN_UNITY ? 3 : 2); d1 = getProgression(); progOff = (long) ((double) progLen * d1 / (1.0 - d1)); progLen += progOff; // normalization requires temp files if( pr.intg[ PR_GAINTYPE ] == GAIN_UNITY ) { tempFile = new File[ outChanNum ]; floatF = new FloatFile[ outChanNum ]; for( ch = 0; ch < outChanNum; ch++ ) { // first zero them because an exception might be thrown tempFile[ ch ] = null; floatF[ ch ] = null; } for( ch = 0; ch < outChanNum; ch++ ) { tempFile[ ch ] = IOUtil.createTempFile(); floatF[ ch ] = new FloatFile( tempFile[ ch ], GenericFile.MODE_OUTPUT ); } } else { gain = (float) (Param.transform( pr.para[ PR_GAIN ], Param.ABS_AMP, ampRef, null )).value; } // .... check running .... if( !threadRunning ) break topLevel; // =================== CORE =================== framesWritten = 0; nextOpened = 0; while( threadRunning && (framesWritten < outLength) ) { chunkLength = Math.min( 8192, outLength - framesWritten ); Util.clear( outBuf ); // check for new chunks for( ; nextOpened < numChunks; nextOpened++ ) { if( sndChunks[nextOpened].punchIn >= framesWritten + chunkLength ) break; activeChunks.addElement( sndChunks[nextOpened] ); gui.stringToJTextField( "Concatening '" + sndChunks[nextOpened].name + "'...", GG_CURRENTINFO ); sndChunks[nextOpened].f.seekFrame( sndChunks[nextOpened].off ); } for( i = 0; i < activeChunks.size(); i++ ) { currentChunk = (SoundChunk) activeChunks.elementAt( i ); relOff = currentChunk.punchIn - framesWritten; j = Math.max( 0, relOff ); // if( j > 0 ) { // punchIn // for( ch = 0; ch < currentChunk.stream.chanNum; ch++ ) { // convBuf1 = inBuf[ ch ]; // for( m = 0; m < j; m++ ) { // convBuf1[m] = 0.0f; // } // } // } k = Math.min( chunkLength, relOff + currentChunk.len ); currentChunk.f.readFrames( inBuf, j, k - j ); // ---- fades ---- m = relOff + currentChunk.fadeIn; if( m > j ) { // fade in p = m; m = Math.min( m, chunkLength ); fadeY2 = 1.0f - (float) (p - m) / (float) currentChunk.fadeIn; fadeY1 = (float) (j - relOff) / (float) currentChunk.fadeIn; f1 = (fadeY2 - fadeY1) / (m - j - 1); // System.out.println( "II Y1 "+fadeY1+"; Y2 "+fadeY2 ); switch( pr.intg[ PR_FADETYPE ]) { case TYPE_ENERGY: for( n = j; n < m; n++ ) { for( ch = 0; ch < currentChunk.stream.channels; ch++ ) { inBuf[ch][n] *= fadeY1; } fadeY1 += f1; } break; case TYPE_POWER: fadeY1 = 1.0f - fadeY1; for( n = j; n < m; n++ ) { for( ch = 0; ch < currentChunk.stream.channels; ch++ ) { inBuf[ch][n] *= 1.0f - (fadeY1*fadeY1); } fadeY1 -= f1; } break; } } m = relOff + currentChunk.len - currentChunk.fadeOut; if( m < k ) { // fade out p = m; m = Math.max( m, 0 ); fadeY1 = 1.0f - (float) (m - p) / (float) currentChunk.fadeOut; fadeY2 = 1.0f + (float) (p - k) / (float) currentChunk.fadeOut; f1 = (fadeY2 - fadeY1) / (k - m - 1); // System.out.println( "OO Y1 "+fadeY1+"; Y2 "+fadeY2+" ; m "+m+"; k "+k ); switch( pr.intg[ PR_FADETYPE ]) { case TYPE_ENERGY: for( n = m; n < k; n++ ) { for( ch = 0; ch < currentChunk.stream.channels; ch++ ) { inBuf[ch][n] *= fadeY1; } fadeY1 += f1; } break; case TYPE_POWER: fadeY1 = 1.0f - fadeY1; for( n = m; n < k; n++ ) { for( ch = 0; ch < currentChunk.stream.channels; ch++ ) { inBuf[ch][n] *= 1.0f - (fadeY1*fadeY1); } fadeY1 -= f1; } break; } } if( k < chunkLength ) { // punchOut // for( ch = 0; ch < currentChunk.stream.chanNum; ch++ ) { // convBuf1 = inBuf[ ch ]; // for( m = k; m < chunkLength; m++ ) { // convBuf1[m] = 0.0f; // } // } activeChunks.removeElement( currentChunk ); currentChunk.f.close(); currentChunk.f = null; i--; } // mix to output buffer for( ch = 0; ch < outChanNum; ch ++ ) { Util.add( inBuf[ ch % currentChunk.stream.channels ], j, outBuf[ ch ], j, k - j ); } } progOff += chunkLength; // .... progress .... setProgression( (float) progOff / (float) progLen ); // ---- save output ---- if( floatF != null ) { for( ch = 0; ch < outChanNum; ch++ ) { convBuf1 = outBuf[ ch ]; for( i = 0; i < chunkLength; i++ ) { // measure max amp f1 = Math.abs( convBuf1[ i ]); if( f1 > maxAmp ) { maxAmp = f1; } } floatF[ ch ].writeFloats( convBuf1, 0, chunkLength ); } } else { // i.e. abs gain for( ch = 0; ch < outChanNum; ch++ ) { convBuf1 = outBuf[ ch ]; for( i = 0; i < chunkLength; i++ ) { // measure max amp + adjust gain f1 = Math.abs( convBuf1[ i ]); convBuf1[ i ] *= gain; if( f1 > maxAmp ) { maxAmp = f1; } } } outF.writeFrames( outBuf, 0, chunkLength ); } framesWritten += chunkLength; progOff += chunkLength; // .... progress .... setProgression( (float) progOff / (float) progLen ); } // while framesWritten < outLength // .... check running .... if( !threadRunning ) break topLevel; // ---- 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 < outChanNum; ch++ ) { floatF[ ch ].cleanUp(); floatF[ ch ] = null; tempFile[ ch ].delete(); tempFile[ ch ] = null; } } // .... check running .... if( !threadRunning ) break topLevel; outF.close(); outF = null; // inform about clipping/ low level maxAmp *= gain; handleClipping( maxAmp ); } catch( IOException e1 ) { setError( e1 ); } catch( OutOfMemoryError e2 ) { outStream = null; outBuf = null; convBuf1 = null; System.gc(); setError( new Exception( ERR_MEMORY )); } // ---- cleanup (topLevel) ---- if( outF != null ) { outF.cleanUp(); } if( sndChunks != null ) { for( i = 0; i < sndChunks.length; i++ ) { if( (sndChunks[ i ] != null) && (sndChunks[ i ].f != null) ) { sndChunks[ i ].f.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() // -------- private methods -------- /** * Set new input file */ protected void setInput( String fname ) { AudioFile f = null; AudioFileDescr stream = null; ParamField ggSlave; Param ref; // ---- Header lesen ---- try { f = AudioFile.openAsRead( new File( fname )); stream = f.getDescr(); f.close(); ref = new Param( AudioFileDescr.samplesToMillis( stream, stream.length ), Param.ABS_MS ); ggSlave = (ParamField) gui.getItemObj( GG_OFFSET ); if( ggSlave != null ) { ggSlave.setReference( ref ); } ggSlave = (ParamField) gui.getItemObj( GG_LENGTH ); if( ggSlave != null ) { ggSlave.setReference( ref ); } } catch( IOException ignored) {} } /** * Maske zur Berechnung der Chunk Filenamen erstellen * * gibt Zahl der Files zurueck oder Null bei Fehler */ protected int analyseFileNames() { String s1 = pr.text[ PR_INPUTFILE1 ]; String s2 = pr.text[ PR_INPUTFILE2 ]; int len1 = s1.length(); int len2 = s2.length(); int i, j, numStart, numEnd, numMin, numMax, num; char[] numPad; // Mechanismus: erste Abweichung links->rechts + erste Abweichung rechts->links // finden, dann Bereich auf evtl. benachbarte numerische Charaktere ausdehnen // und als Integer interpretieren for( numStart = 0; numStart < Math.min( len1, len2 ); numStart++ ) { if( s1.charAt( numStart ) != s2.charAt( numStart )) break; } for( ; numStart > 0; numStart-- ) { if( !Character.isDigit( s1.charAt( numStart - 1 )) || !Character.isDigit( s2.charAt( numStart - 1 ))) break; } for( numEnd = 0; numEnd < Math.min( len1, len2 ); numEnd++ ) { if( s1.charAt( len1 - numEnd - 1 ) != s2.charAt( len2 - numEnd - 1 )) break; } for( ; numEnd > 0; numEnd-- ) { if( !Character.isDigit( s1.charAt( len1 - numEnd )) || !Character.isDigit( s2.charAt( len2 - numEnd ))) break; } //System.out.println( "numStart "+numStart+"; numEnd "+numEnd+"; len1 "+len1+"; len2 "+len2 ); try { i = Integer.parseInt( s1.substring( numStart, len1 - numEnd )); j = Integer.parseInt( s2.substring( numStart, len2 - numEnd )); numMin = Math.min( i, j ); numMax = Math.max( i, j ); reverse = i > j; num = numMax - numMin + 1; gui.stringToJTextField( num + " files detected.", GG_CURRENTINFO ); fileNameArgs[1] = s1.substring( 0, numStart ); //System.err.println( "beg \""+ fileNameArgs[1] + "\"" ); fileNameArgs[2] = s1.substring( len1 - numEnd ); //System.err.println( "end \""+ fileNameArgs[2] + "\"" ); fileNameArgs[3] = new Integer( numMin ); //System.err.println( "num \""+ fileNameArgs[3] + "\"" ); i = Math.min( len1, len2 ) - numEnd - numStart; numPad = new char[ i + Math.max( len1, len2 ) - Math.min( len1, len2 )]; // for( j = 0; j < i; j++ ) numPad[j] = '0'; for( j = 0; j < numPad.length; j++ ) numPad[j] = '0'; fileNameArgs[4] = new String( numPad ); usePad = len1 == len2; //System.err.println( "pad \""+ fileNameArgs[4] + "\"" ); return num; // } catch( NumberFormatException e1 ) { } catch( Exception e1 ) { // either NumberFormat or StringIndexOutOfBounds (when s1 == s2) numMin = -1; numMax = -1; gui.stringToJTextField( "Filename analysis failed!", GG_CURRENTINFO ); return 0; } } /* * Generiert Filenamen aus mit analyseFilenames() erstellter Maske * * id laueft von 0 bis numFiles-1 */ protected String synthesizeFileName( int id ) { String s = String.valueOf( id + ((Integer) fileNameArgs[3]).intValue() ); if( usePad ) { fileNameArgs[0] = ((String) fileNameArgs[4]).substring( s.length() ) + s; } else { fileNameArgs[0] = s; } return( MessageFormat.format( FILENAMEPTRN, fileNameArgs )); } } // class ConcatDlg /** * internal class fuer die Synthese */ class SoundChunk { String name; AudioFile f = null; AudioFileDescr stream; int off, len, over, fadeIn, fadeOut, punchIn; } // class SoundChunk