/* * AmpShaperDlg.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.Envelope; 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 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; /** * Processing module for modification * of a sound's amplitude envelope. */ public class AmpShaperDlg 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_ENVINFILE = 2; private static final int PR_ENVOUTFILE = 3; private static final int PR_OUTPUTTYPE = 0; // pr.intg private static final int PR_OUTPUTRES = 1; private static final int PR_ENVOUTTYPE = 2; private static final int PR_ENVOUTRES = 3; private static final int PR_GAINTYPE = 4; private static final int PR_ENVGAINTYPE = 5; private static final int PR_ENVSOURCE = 6; private static final int PR_MODE = 7; private static final int PR_GAIN = 0; // pr.para private static final int PR_ENVGAIN = 1; private static final int PR_MAXCHANGE = 2; private static final int PR_AVERAGE = 3; private static final int PR_ENVOUTPUT = 0; // pr.bool private static final int PR_INVERT = 1; private static final int PR_RIGHTCHAN = 2; private static final int PR_ENV = 0; // pr.envl private static final int PR_RIGHTCHANENV = 1; private static final int SRC_INPUT = 0; private static final int SRC_SOUNDFILE = 1; private static final int SRC_ENVFILE = 2; private static final int SRC_ENV = 3; private static final int MODE_SUPERPOSE = 0; private static final int MODE_REPLACE = 1; private static final String PRN_INPUTFILE = "InputFile"; private static final String PRN_OUTPUTFILE = "OutputFile"; private static final String PRN_ENVINFILE = "EnvInFile"; private static final String PRN_ENVOUTFILE = "EnvOutFile"; private static final String PRN_OUTPUTTYPE = "OutputType"; private static final String PRN_OUTPUTRES = "OutputReso"; private static final String PRN_ENVOUTTYPE = "EnvOutType"; private static final String PRN_ENVOUTRES = "EnvOutReso"; private static final String PRN_ENVSOURCE = "EnvSource"; private static final String PRN_MODE = "Mode"; private static final String PRN_ENVGAINTYPE = "EnvGainType"; private static final String PRN_ENVGAIN = "EnvGain"; private static final String PRN_MAXCHANGE = "MaxChange"; private static final String PRN_AVERAGE = "Average"; private static final String PRN_ENVOUTPUT = "EnvOuput"; private static final String PRN_INVERT = "Invert"; private static final String PRN_RIGHTCHAN = "RightChan"; private static final String PRN_ENV = "Env"; private static final String PRN_RIGHTCHANENV = "RightEnv"; private static final String prText[] = { "", "", "", "" }; private static final String prTextName[] = { PRN_INPUTFILE, PRN_OUTPUTFILE, PRN_ENVINFILE, PRN_ENVOUTFILE }; private static final int prIntg[] = { 0, 0, 0, 0, GAIN_UNITY, GAIN_UNITY, SRC_INPUT, MODE_SUPERPOSE }; private static final String prIntgName[] = { PRN_OUTPUTTYPE, PRN_OUTPUTRES, PRN_ENVOUTTYPE, PRN_ENVOUTRES, PRN_GAINTYPE, PRN_ENVGAINTYPE, PRN_ENVSOURCE, PRN_MODE }; private static final boolean prBool[] = { false, true, false }; private static final String prBoolName[] = { PRN_ENVOUTPUT, PRN_INVERT, PRN_RIGHTCHAN }; private static final Param prPara[] = { null, null, null, null }; private static final String prParaName[] = { PRN_GAIN, PRN_ENVGAIN, PRN_MAXCHANGE, PRN_AVERAGE }; private static final Envelope prEnvl[] = { null, null }; private static final String prEnvlName[] = { PRN_ENV, PRN_RIGHTCHANENV }; 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_ENVINFILE = GG_OFF_PATHFIELD + PR_ENVINFILE; private static final int GG_ENVOUTFILE = GG_OFF_PATHFIELD + PR_ENVOUTFILE; 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_ENVOUTTYPE = GG_OFF_CHOICE + PR_ENVOUTTYPE; private static final int GG_ENVOUTRES = GG_OFF_CHOICE + PR_ENVOUTRES; private static final int GG_ENVSOURCE = GG_OFF_CHOICE + PR_ENVSOURCE; private static final int GG_MODE = GG_OFF_CHOICE + PR_MODE; private static final int GG_GAINTYPE = GG_OFF_CHOICE + PR_GAINTYPE; private static final int GG_GAIN = GG_OFF_PARAMFIELD + PR_GAIN; private static final int GG_ENVGAINTYPE = GG_OFF_CHOICE + PR_ENVGAINTYPE; private static final int GG_ENVGAIN = GG_OFF_PARAMFIELD + PR_ENVGAIN; private static final int GG_MAXCHANGE = GG_OFF_PARAMFIELD + PR_MAXCHANGE; private static final int GG_AVERAGE = GG_OFF_PARAMFIELD + PR_AVERAGE; private static final int GG_ENVOUTPUT = GG_OFF_CHECKBOX + PR_ENVOUTPUT; private static final int GG_INVERT = GG_OFF_CHECKBOX + PR_INVERT; private static final int GG_RIGHTCHAN = GG_OFF_CHECKBOX + PR_RIGHTCHAN; private static final int GG_ENV = GG_OFF_ENVICON + PR_ENV; private static final int GG_RIGHTCHANENV = GG_OFF_ENVICON + PR_RIGHTCHANENV; private static PropertyArray static_pr = null; private static Presets static_presets = null; // -------- public mehods -------- public AmpShaperDlg() { super("Amplitude Shaper"); 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_MAXCHANGE ] = new Param( 96.0, Param.DECIBEL_AMP ); static_pr.para[ PR_AVERAGE ] = new Param( 1000.0, Param.ABS_MS ); static_pr.paraName = prParaName; static_pr.envl = prEnvl; static_pr.envl[ PR_ENV ] = Envelope.createBasicEnvelope(Envelope.BASIC_UNSIGNED_TIME); static_pr.envl[ PR_RIGHTCHANENV ] = Envelope.createBasicEnvelope(Envelope.BASIC_UNSIGNED_TIME); static_pr.envlName = prEnvlName; // static_pr.superPr = DocumentFrame.static_pr; fillDefaultAudioDescr(static_pr.intg, PR_OUTPUTTYPE, PR_OUTPUTRES); fillDefaultAudioDescr(static_pr.intg, PR_ENVOUTTYPE, PR_ENVOUTRES); fillDefaultGain(static_pr.para, PR_GAIN); fillDefaultGain(static_pr.para, PR_ENVGAIN); static_presets = new Presets(getClass(), static_pr.toProperties(true)); } presets = static_presets; pr = (PropertyArray) static_pr.clone(); // -------- build GUI -------- GridBagConstraints con; PathField ggInputFile, ggOutputFile, ggEnvInFile, ggEnvOutFile; PathField[] ggInputs; JComboBox ggEnvSource, ggMode; ParamField ggMaxChange, ggAverage; JCheckBox ggEnvOutput, ggInvert, ggRightChan; EnvIcon ggEnv, ggRightChanEnv; Component[] ggGain, ggEnvGain; ParamSpace[] spcAverage; ParamSpace spcMaxChange; 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_ENVSOURCE: pr.intg[ ID - GG_OFF_CHOICE ] = ((JComboBox) e.getSource()).getSelectedIndex(); reflectPropertyChanges(); break; case GG_ENVOUTPUT: case GG_RIGHTCHAN: pr.bool[ ID - GG_OFF_CHECKBOX ] = ((JCheckBox) e.getSource()).isSelected(); reflectPropertyChanges(); break; } } }; // -------- Input-Gadgets -------- con.fill = GridBagConstraints.BOTH; con.gridwidth = GridBagConstraints.REMAINDER; gui.addLabel( new GroupLabel( "Waveform I/O", GroupLabel.ORIENT_HORIZONTAL, GroupLabel.BRACE_NONE )); ggInputFile = new PathField( PathField.TYPE_INPUTFILE + PathField.TYPE_FORMATFIELD, "Select input file" ); ggInputFile.handleTypes( GenericFile.TYPES_SOUND ); con.gridwidth = 1; con.weightx = 0.1; gui.addLabel( new JLabel( "Input file:", SwingConstants.RIGHT )); con.gridwidth = GridBagConstraints.REMAINDER; con.weightx = 0.9; gui.addPathField( ggInputFile, GG_INPUTFILE, null ); ggEnvInFile = new PathField(PathField.TYPE_INPUTFILE + PathField.TYPE_FORMATFIELD, "Select input envelope file"); ggEnvInFile.handleTypes(GenericFile.TYPES_SOUND); con.gridwidth = 1; con.weightx = 0.1; gui.addLabel(new JLabel("Env input:", SwingConstants.RIGHT)); con.gridwidth = GridBagConstraints.REMAINDER; con.weightx = 0.9; gui.addPathField(ggEnvInFile, GG_ENVINFILE, 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$F0Amp$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, il); // -------- Env-Output-Gadgets -------- gui.addLabel(new GroupLabel("Separate envelope output", GroupLabel.ORIENT_HORIZONTAL, GroupLabel.BRACE_NONE)); ggEnvOutFile = new PathField(PathField.TYPE_OUTPUTFILE + PathField.TYPE_FORMATFIELD + PathField.TYPE_RESFIELD, "Select output envelope file"); ggEnvOutFile.handleTypes(GenericFile.TYPES_SOUND); ggEnvOutFile.deriveFrom(ggInputs, "$D0$F0Env$E"); con.gridwidth = 1; con.weightx = 0.1; ggEnvOutput = new JCheckBox( "Env output" ); gui.addCheckbox( ggEnvOutput, GG_ENVOUTPUT, il ); con.gridwidth = GridBagConstraints.REMAINDER; con.weightx = 0.9; gui.addPathField( ggEnvOutFile, GG_ENVOUTFILE, null ); gui.registerGadget( ggEnvOutFile.getTypeGadget(), GG_ENVOUTTYPE ); gui.registerGadget( ggEnvOutFile.getResGadget(), GG_ENVOUTRES ); // cannot call createGadgets twice (BUG!) XXX ggEnvGain = new Component[ 2 ]; // createGadgets( GGTYPE_GAIN ); ggEnvGain[ 0 ] = new ParamField( Constants.spaces[ Constants.decibelAmpSpace ]); JComboBox ch = new JComboBox(); ch.addItem( "normalized" ); ch.addItem( "immediate" ); ggEnvGain[ 1 ] = ch; con.weightx = 0.1; con.gridwidth = 1; gui.addLabel(new JLabel("Gain:", SwingConstants.RIGHT)); con.weightx = 0.4; gui.addParamField( (ParamField) ggEnvGain[ 0 ], GG_ENVGAIN, null ); con.weightx = 0.5; con.gridwidth = GridBagConstraints.REMAINDER; gui.addChoice( (JComboBox) ggEnvGain[ 1 ], GG_ENVGAINTYPE, il ); // -------- Settings -------- gui.addLabel( new GroupLabel( "Shaper Settings", GroupLabel.ORIENT_HORIZONTAL, GroupLabel.BRACE_NONE )); ggEnvSource = new JComboBox(); ggEnvSource.addItem("Input file"); ggEnvSource.addItem("Sound file"); ggEnvSource.addItem("Envelope file"); ggEnvSource.addItem("Envelope"); con.gridwidth = 1; con.weightx = 0.1; gui.addLabel(new JLabel("Source:", SwingConstants.RIGHT)); con.weightx = 0.4; gui.addChoice( ggEnvSource, GG_ENVSOURCE, il ); ggInvert = new JCheckBox(); con.weightx = 0.1; gui.addLabel(new JLabel("Inversion:", SwingConstants.RIGHT)); con.gridwidth = GridBagConstraints.REMAINDER; con.weightx = 0.4; gui.addCheckbox( ggInvert, GG_INVERT, il ); ggMode = new JComboBox(); ggMode.addItem("Superposition"); ggMode.addItem("Replacement"); con.gridwidth = 1; con.weightx = 0.1; gui.addLabel(new JLabel("Apply mode:", SwingConstants.RIGHT)); con.weightx = 0.4; con.gridwidth = GridBagConstraints.REMAINDER; gui.addChoice( ggMode, GG_MODE, il ); ggEnv = new EnvIcon(getComponent()); con.gridwidth = 1; con.weightx = 0.1; gui.addLabel(new JLabel("Envelope:", SwingConstants.RIGHT)); con.weightx = 0.4; gui.addGadget(ggEnv, GG_ENV); spcMaxChange = new ParamSpace( Constants.spaces[ Constants.decibelAmpSpace ]); // spcMaxChange.min= spcMaxChange.inc; spcMaxChange = new ParamSpace( spcMaxChange.inc, spcMaxChange.max, spcMaxChange.inc, spcMaxChange.unit ); ggMaxChange = new ParamField( spcMaxChange ); con.weightx = 0.1; gui.addLabel(new JLabel("Max boost:", SwingConstants.RIGHT)); con.weightx = 0.4; con.gridwidth = GridBagConstraints.REMAINDER; gui.addParamField( ggMaxChange, GG_MAXCHANGE, null ); ggRightChan = new JCheckBox( "Right chan." ); ggRightChanEnv = new EnvIcon(getComponent()); con.weightx = 0.1; con.gridwidth = 1; gui.addCheckbox(ggRightChan, GG_RIGHTCHAN, il); con.weightx = 0.4; gui.addGadget(ggRightChanEnv, GG_RIGHTCHANENV); spcAverage = new ParamSpace[ 3 ]; spcAverage[0] = Constants.spaces[ Constants.absMsSpace ]; spcAverage[1] = Constants.spaces[ Constants.absBeatsSpace ]; spcAverage[2] = Constants.spaces[ Constants.ratioTimeSpace ]; ggAverage = new ParamField( spcAverage ); con.weightx = 0.1; gui.addLabel(new JLabel("Smoothing:", SwingConstants.RIGHT)); con.weightx = 0.4; con.gridwidth = GridBagConstraints.REMAINDER; gui.addParamField( ggAverage, GG_AVERAGE, 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, ch, len, off, chunkLength; long progOff, progLen; float f1, f2; double d1; boolean extraAudioFile; // io AudioFile inF = null; AudioFile outF = null; AudioFile envInF = null; AudioFile envOutF = null; AudioFileDescr inStream; AudioFileDescr outStream; AudioFileDescr envInStream; AudioFileDescr envOutStream; FloatFile[] outFloatF = null; FloatFile[] envFloatF = null; File outTempFile[] = null; File envTempFile[] = null; int inChanNum, outChanNum, envInChanNum, envOutChanNum, shapeChanNum; int[][] shapeChan; int[][] inChan; float[][] shapeChanWeight; float[][] inChanWeight; // buffers float[][] inBuf = null; // Sound-In float[][] outBuf = null; // Sound-Out float[][] inEnvBuf = null; // Envelope of Input float[][] shapeEnvBuf = null; // Envelope of Shaper float[][] envInBuf = null; // Direct-In of Shaper-File float[] convBuf1, convBuf2; int inLength, outLength, envInLength, envOutLength; int framesRead, framesWritten; // re sound-files int framesRead2, framesWritten2; // re env-files Param ampRef = new Param( 1.0, Param.ABS_AMP ); // transform-Referenz Param peakGain; float gain = 1.0f; // gain abs amp float envGain = 1.0f; // gain abs amp float maxAmp = 0.0f; float envMaxAmp = 0.0f; float maxChange; int average, avrOff; double[] inEnergy, envInEnergy; PathField ggOutput; 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; envInLength = 0; envInChanNum = inChanNum; shapeChanNum = 0; // shape input switch( pr.intg[ PR_ENVSOURCE ]) { case SRC_SOUNDFILE: case SRC_ENVFILE: envInF = AudioFile.openAsRead( new File( pr.text[ PR_ENVINFILE ])); envInStream = envInF.getDescr(); envInChanNum = envInStream.channels; shapeChanNum = envInChanNum; envInLength = (int) envInStream.length; // this helps to prevent errors from empty files! if( (envInLength < 1) || (envInChanNum < 1) ) throw new EOFException( ERR_EMPTY ); i = Math.min( inLength, envInLength ); inLength = i; envInLength = i; break; case SRC_ENV: if( pr.bool[ PR_RIGHTCHAN ]) { shapeChanNum = 2; envInChanNum = Math.max( envInChanNum, shapeChanNum ); // ggf. mono => stereo } else { shapeChanNum = 1; } break; case SRC_INPUT: shapeChanNum = inChanNum; break; } // .... check running .... if( !threadRunning ) break topLevel; outChanNum = Math.max( inChanNum, envInChanNum ); outLength = inLength; shapeChan = new int[ outChanNum ][ 2 ]; shapeChanWeight = new float[ outChanNum ][ 2 ]; inChan = new int[ outChanNum ][ 2 ]; inChanWeight = new float[ outChanNum ][ 2 ]; extraAudioFile = (envInF != null) && (pr.intg[ PR_ENVSOURCE ] == SRC_SOUNDFILE); // not if SRC_ENVFILE!!! // calc weights for( ch = 0; ch < outChanNum; ch++ ) { if( shapeChanNum == 1 ) { shapeChan[ ch ][ 0 ] = 0; shapeChan[ ch ][ 1 ] = 0; shapeChanWeight[ ch ][ 0 ] = 1.0f; shapeChanWeight[ ch ][ 1 ] = 0.0f; } else { f1 = ((float) ch / (float) (outChanNum - 1)) * (float) (shapeChanNum - 1); shapeChan[ ch ][ 0 ] = (int) f1; // Math.max verhindert ArrayIndex-Fehler shapeChan[ ch ][ 1 ] = Math.min( (int) f1 + 1, shapeChanNum-1 ); // (Weight ist dabei eh Null) f1 %= 1.0f; shapeChanWeight[ ch ][ 0 ] = 1.0f - f1; shapeChanWeight[ ch ][ 1 ] = f1; } if( inChanNum == 1 ) { inChan[ ch ][ 0 ] = 0; inChan[ ch ][ 1 ] = 0; inChanWeight[ ch ][ 0 ] = 1.0f; inChanWeight[ ch ][ 1 ] = 0.0f; } else { f1 = ((float) ch / (float) (outChanNum - 1)) * (float) (inChanNum - 1); inChan[ ch ][ 0 ] = (int) f1; inChan[ ch ][ 1 ] = Math.min( (int) f1 + 1, inChanNum-1 ); f1 %= 1.0f; inChanWeight[ ch ][ 0 ] = 1.0f - f1; inChanWeight[ ch ][ 1 ] = f1; } /* for( i = 0; i < 2; i++ ) { System.out.println( "shapeChan["+ch+"]["+i+"] = "+shapeChan[ch][i] ); System.out.println( "shapeWeig["+ch+"]["+i+"] = "+shapeChanWeight[ch][i] ); System.out.println( "inputChan["+ch+"]["+i+"] = "+inChan[ch][i] ); System.out.println( "inputWeig["+ch+"]["+i+"] = "+inChanWeight[ch][i] ); } */ } // output ggOutput = (PathField) gui.getItemObj( GG_OUTPUTFILE ); if( ggOutput == null ) throw new IOException( ERR_MISSINGPROP ); outStream = new AudioFileDescr( inStream ); ggOutput.fillStream( outStream ); outStream.channels = outChanNum; outF = AudioFile.openAsWrite( outStream ); // .... check running .... if( !threadRunning ) break topLevel; envOutLength = 0; envOutChanNum = 0; // envelope output if( pr.bool[ PR_ENVOUTPUT ]) { ggOutput = (PathField) gui.getItemObj( GG_ENVOUTFILE ); if( ggOutput == null ) throw new IOException( ERR_MISSINGPROP ); envOutStream = new AudioFileDescr( inStream ); ggOutput.fillStream( envOutStream ); envOutStream.file = new File( pr.text[ PR_ENVOUTFILE ]); envOutF = AudioFile.openAsWrite( envOutStream ); envOutLength = inLength; envOutChanNum = inChanNum; } // .... check running .... if( !threadRunning ) break topLevel; // average buffer size d1 = Param.transform( pr.para[ PR_AVERAGE ], Param.ABS_MS, new Param( AudioFileDescr.samplesToMillis( inStream, inLength ), Param.ABS_MS ), null ).value; // average in millis average = ((int) (AudioFileDescr.millisToSamples( inStream, d1 ) + 0.5) & ~1) + 1; // always odd avrOff = (average >> 1) + 1; // first element needed for subtraction (see calcEnv()) progOff = 0; progLen = (long) Math.max( average - avrOff, inLength) + (long) (extraAudioFile ? Math.max( average - avrOff, envInLength ) : envInLength) + (long) outLength + (long) envOutLength; // normalization requires temp files if( pr.intg[ PR_GAINTYPE ] == GAIN_UNITY ) { 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 ); } progLen += (long) outLength; } else { gain = (float) (Param.transform( pr.para[ PR_GAIN ], Param.ABS_AMP, ampRef, null )).value; } // .... check running .... if( !threadRunning ) break topLevel; // normalization requires temp files if( pr.intg[ PR_ENVGAINTYPE ] == GAIN_UNITY ) { envTempFile = new File[ envOutChanNum ]; envFloatF = new FloatFile[ envOutChanNum ]; for( ch = 0; ch < envOutChanNum; ch++ ) { // first zero them because an exception might be thrown envTempFile[ ch ] = null; envFloatF[ ch ] = null; } for( ch = 0; ch < envOutChanNum; ch++ ) { envTempFile[ ch ] = IOUtil.createTempFile(); envFloatF[ ch ] = new FloatFile( envTempFile[ ch ], GenericFile.MODE_OUTPUT ); } progLen += (long) envOutLength; } else { envGain = (float) (Param.transform( pr.para[ PR_ENVGAIN ], Param.ABS_AMP, ampRef, null )).value; } // .... check running .... if( !threadRunning ) break topLevel; // ---- further inits ---- maxChange = (float) (Param.transform( pr.para[ PR_MAXCHANGE ], Param.ABS_AMP, ampRef, null )).value; inBuf = new float[ inChanNum ][ 8192 + average ]; Util.clear( inBuf ); outBuf = new float[ outChanNum ][ 8192 ]; Util.clear( outBuf ); if( extraAudioFile ) { envInBuf= new float[ envInChanNum ][ 8192 + average ]; Util.clear( envInBuf ); } inEnvBuf = new float[ inChanNum ][ 8192 ]; // = envOutBuf Util.clear( inEnvBuf ); shapeEnvBuf = new float[ envInChanNum ][ 8192 ]; Util.clear( shapeEnvBuf ); inEnergy = new double[ inChanNum ]; for( ch = 0; ch < inChanNum; ch++ ) { inEnergy[ ch ] = 0.0; } envInEnergy = new double[ envInChanNum ]; for( ch = 0; ch < envInChanNum; ch++ ) { envInEnergy[ ch ] = 0.0; } // System.out.println( "inLength "+inLength+"; envInLength "+envInLength+"; envOutLength "+envOutLength+"; outLength "+outLength ); // System.out.println( "average "+average+"; avrOff "+avrOff ); // ----==================== buffer init ====================---- framesRead = 0; // re inF framesRead2 = 0; // re envInF // ---- init buffers ---- for( off = avrOff; threadRunning && (off < average); ) { len = Math.min( inLength - framesRead, Math.min( 8192, average - off )); if( len == 0 ) break; inF.readFrames( inBuf, off, len ); // calc initial energy per channel (see calcEnv()) for( ch = 0; ch < inChanNum; ch++ ) { convBuf1 = inBuf[ ch ]; d1 = 0.0; for( i = 0, j = off; i < len; i++ ) { f1 = convBuf1[ j++ ]; d1 += f1*f1; } inEnergy[ ch ] += d1; } framesRead += len; off += len; progOff += len; // .... progress .... setProgression( (float) progOff / (float) progLen ); } // zero padding bereits durch initialisierung mit Util.clear() passiert! if( extraAudioFile ) { for( off = avrOff; threadRunning && (off < average); ) { len = Math.min( envInLength - framesRead2, Math.min( 8192, average - off )); if( len == 0 ) break; envInF.readFrames( envInBuf, off, len ); // calc initial energy per channel (see calcEnv()) for( ch = 0; ch < envInChanNum; ch++ ) { convBuf1 = envInBuf[ ch ]; d1 = 0.0; for( i = 0, j = off; i < len; i++ ) { f1 = convBuf1[ j++ ]; d1 += f1*f1; } envInEnergy[ ch ] += d1; } framesRead2 += len; off += len; progOff += len; // .... progress .... setProgression( (float) progOff / (float) progLen ); } // zero padding bereits durch initialisierung mit Util.clear() passiert! } // .... check running .... if( !threadRunning ) break topLevel; // ----==================== the real stuff ====================---- framesWritten = 0; // re OutF framesWritten2 = 0; // re envOutF while( threadRunning && (framesWritten < outLength) ) { chunkLength = Math.min( 8192, outLength - framesWritten ); // ---- read input chunk ---- len = Math.min( inLength - framesRead, chunkLength ); inF.readFrames( inBuf, average, len ); // zero padding for( ch = 0; ch < inChanNum; ch++ ) { convBuf1 = inBuf[ ch ]; for( i = len, j = len + average; i < chunkLength; i++ ) { convBuf1[ j++ ] = 0.0f; } } framesRead += len; progOff += len; // .... progress .... setProgression( (float) progOff / (float) progLen ); // .... check running .... if( !threadRunning ) break topLevel; // ---- read input env chunk ---- if( envInF != null ) { len = Math.min( envInLength - framesRead2, chunkLength ); if( extraAudioFile) { // ........ needs averaging ........ envInF.readFrames( envInBuf, average, len ); // zero padding for( ch = 0; ch < envInChanNum; ch++ ) { convBuf1 = envInBuf[ ch ]; for( i = len, j = len + average; i < chunkLength; i++ ) { convBuf1[ j++ ] = 0.0f; } } } else { // ........ is already env ........ envInF.readFrames( shapeEnvBuf, 0, len ); // zero padding for( ch = 0; ch < envInChanNum; ch++ ) { convBuf1 = shapeEnvBuf[ ch ]; for( i = len; i < chunkLength; i++ ) { convBuf1[ i ] = 0.0f; } } } framesRead2 += len; progOff += len; // .... progress .... setProgression( (float) progOff / (float) progLen ); } // .... check running .... if( !threadRunning ) break topLevel; // ---- calc input envelope ---- for( ch = 0; ch < inChanNum; ch++ ) { inEnergy[ ch ] = calcEnv( inBuf[ ch ], inEnvBuf[ ch ], average, chunkLength, inEnergy[ ch ]); } // ---- write output env file ---- if( pr.bool[ PR_ENVOUTPUT ]) { if( envFloatF != null ) { // i.e. unity gain for( ch = 0; ch < envOutChanNum; ch++ ) { convBuf1 = inEnvBuf[ ch ]; for( i = 0; i < chunkLength; i++ ) { // measure max amp f1 = Math.abs( convBuf1[ i ]); if( f1 > envMaxAmp ) { envMaxAmp = f1; } } envFloatF[ ch ].writeFloats( convBuf1, 0, chunkLength ); } } else { // i.e. abs gain for( ch = 0; ch < envOutChanNum; ch++ ) { convBuf1 = inEnvBuf[ ch ]; for( i = 0; i < chunkLength; i++ ) { // measure max amp + adjust gain f1 = Math.abs( convBuf1[ i ]); convBuf1[ i ] *= envGain; if( f1 > envMaxAmp ) { envMaxAmp = f1; } } } envOutF.writeFrames( inEnvBuf, 0, chunkLength ); } framesWritten2 += chunkLength; progOff += chunkLength; // .... progress .... setProgression( (float) progOff / (float) progLen ); } // .... check running .... if( !threadRunning ) break topLevel; // ---- calc shape envelope ---- switch( pr.intg[ PR_ENVSOURCE ]) { case SRC_INPUT: // shape env = input env for( ch = 0; ch < inChanNum; ch++ ) { System.arraycopy( inEnvBuf[ ch ], 0, shapeEnvBuf[ ch ], 0, chunkLength ); } break; case SRC_SOUNDFILE: // calc shape env from envInBuf for( ch = 0; ch < envInChanNum; ch++ ) { envInEnergy[ ch ] = calcEnv( envInBuf[ ch ], shapeEnvBuf[ ch ], average, chunkLength, envInEnergy[ ch ]); } break; case SRC_ENVFILE: // nothing to do, we have already loaded the env break; // in the correct buffer case SRC_ENV: throw new IOException( "Graphic env not yet supported" ); } // ---- calc output ---- // first generate output envelope switch( pr.intg[ PR_MODE ]) { case MODE_SUPERPOSE: if( !pr.bool[ PR_INVERT ]) { // multiply by shape for( ch = 0; ch < outChanNum; ch++ ) { convBuf1 = outBuf[ ch ]; for( i = 0; i < chunkLength; i++ ) { f1 = shapeEnvBuf[ shapeChan[ ch ][ 0 ]][ i ] * shapeChanWeight[ ch ][ 0 ] + shapeEnvBuf[ shapeChan[ ch ][ 1 ]][ i ] * shapeChanWeight[ ch ][ 1 ]; convBuf1[ i ] = Math.min( maxChange, f1 ); } } } else { // divide by shape for( ch = 0; ch < outChanNum; ch++ ) { convBuf1 = outBuf[ ch ]; for( i = 0; i < chunkLength; i++ ) { f1 = shapeEnvBuf[ shapeChan[ ch ][ 0 ]][ i ] * shapeChanWeight[ ch ][ 0 ] + shapeEnvBuf[ shapeChan[ ch ][ 1 ]][ i ] * shapeChanWeight[ ch ][ 1 ]; if( f1 > 0.0f ) { convBuf1[ i ] = Math.min( maxChange, 1.0f / f1 ); } else { convBuf1[ i ] = maxChange; } } } } break; case MODE_REPLACE: if( !pr.bool[ PR_INVERT ]) { // shape / input for( ch = 0; ch < outChanNum; ch++ ) { convBuf1 = outBuf[ ch ]; for( i = 0; i < chunkLength; i++ ) { f1 = shapeEnvBuf[ shapeChan[ ch ][ 0 ]][ i ] * shapeChanWeight[ ch ][ 0 ] + shapeEnvBuf[ shapeChan[ ch ][ 1 ]][ i ] * shapeChanWeight[ ch ][ 1 ]; f2 = inEnvBuf[ inChan[ ch ][ 0 ]][ i ] * inChanWeight[ ch ][ 0 ] + inEnvBuf[ inChan[ ch ][ 1 ]][ i ] * inChanWeight[ ch ][ 1 ]; if( f2 > 0.0f ) { convBuf1[ i ] = Math.min( maxChange, f1 / f2 ); } else { convBuf1[ i ] = 0.0f; // input ist eh ueberall null, somit unveraenderlich } } } } else { // 1 / (shape * input) for( ch = 0; ch < outChanNum; ch++ ) { convBuf1 = outBuf[ ch ]; for( i = 0; i < chunkLength; i++ ) { f1 = shapeEnvBuf[ shapeChan[ ch ][ 0 ]][ i ] * shapeChanWeight[ ch ][ 0 ] + shapeEnvBuf[ shapeChan[ ch ][ 1 ]][ i ] * shapeChanWeight[ ch ][ 1 ]; f1*= inEnvBuf[ inChan[ ch ][ 0 ]][ i ] * inChanWeight[ ch ][ 0 ] + inEnvBuf[ inChan[ ch ][ 1 ]][ i ] * inChanWeight[ ch ][ 1 ]; if( f1 > 0.0f ) { convBuf1[ i ] = Math.min( maxChange, 1.0f / f1 ); } else { convBuf1[ i ] = maxChange; } } } } break; } // then multiply input bites if( inChanNum == outChanNum ) { // no weighting - use faster routine for( ch = 0; ch < outChanNum; ch++ ) { convBuf1 = outBuf[ ch ]; convBuf2 = inBuf[ ch ]; for( i = 0, j = avrOff; i < chunkLength; i++, j++ ) { convBuf1[ i ] *= convBuf2[ j ]; } } } else { for( ch = 0; ch < outChanNum; ch++ ) { convBuf1 = outBuf[ ch ]; for( i = 0, j = avrOff; i < chunkLength; i++, j++ ) { f1 = inBuf[ inChan[ ch ][ 0 ]][ j ] * inChanWeight[ ch ][ 0 ] + inBuf[ inChan[ ch ][ 1 ]][ j ] * inChanWeight[ ch ][ 1 ]; convBuf1[ i ] *= f1; } } } // ---- write output sound file ---- if( outFloatF != null ) { // i.e. unity gain 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; } } outFloatF[ 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 ); // ---- shift buffers ---- for( ch = 0; ch < inChanNum; ch++ ) { // zero padding is performed after AudioFile.readFrames()! System.arraycopy( inBuf[ ch ], chunkLength, inBuf[ ch ], 0, average ); } if( extraAudioFile ) { for( ch = 0; ch < envInChanNum; ch++ ) { // zero padding is performed after AudioFile.readFrames()! System.arraycopy( envInBuf[ ch ], chunkLength, envInBuf[ ch ], 0, average ); } } } // until framesWritten == outLength // .... check running .... if( !threadRunning ) break topLevel; // ---- normalize output ---- // sound file if( pr.intg[ PR_GAINTYPE ] == GAIN_UNITY ) { peakGain = new Param( (double) maxAmp, Param.ABS_AMP ); gain = (float) (Param.transform( pr.para[ PR_GAIN ], Param.ABS_AMP, new Param( 1.0 / peakGain.value, peakGain.unit ), null )).value; f1 = 1.0f; if( (envOutF != null) && (pr.intg[ PR_ENVGAINTYPE ] == GAIN_UNITY) ) { // leave prog space f1 = (1.0f + getProgression()) / 2; } normalizeAudioFile( outFloatF, outF, outBuf, gain, f1 ); for( ch = 0; ch < outChanNum; ch++ ) { outFloatF[ ch ].cleanUp(); outFloatF[ ch ] = null; outTempFile[ ch ].delete(); outTempFile[ ch ] = null; } } // .... check running .... if( !threadRunning ) break topLevel; // envelope file if( (envOutF != null) && (pr.intg[ PR_ENVGAINTYPE ] == GAIN_UNITY) ) { peakGain = new Param( (double) envMaxAmp, Param.ABS_AMP ); envGain = (float) (Param.transform( pr.para[ PR_ENVGAIN ], Param.ABS_AMP, new Param( 1.0 / peakGain.value, peakGain.unit ), null )).value; normalizeAudioFile( envFloatF, envOutF, inEnvBuf, envGain, 1.0f ); for( ch = 0; ch < envOutChanNum; ch++ ) { envFloatF[ ch ].cleanUp(); envFloatF[ ch ] = null; envTempFile[ ch ].delete(); envTempFile[ ch ] = null; } } // .... check running .... if( !threadRunning ) break topLevel; // ---- Finish ---- outF.close(); outF = null; outStream = null; if( envOutF != null ) { envOutF.close(); envOutF = null; envOutStream= null; } if( envInF != null ) { envInF.close(); envInF = null; envInStream = null; } inF.close(); inF = null; inStream = null; outBuf = null; inBuf = null; inEnvBuf = null; envInBuf = null; shapeEnvBuf = null; // inform about clipping/ low level maxAmp *= gain; handleClipping( maxAmp ); envMaxAmp *= envGain; // handleClipping( envMaxAmp ); // ;( routine nicht flexibel genug! } catch( IOException e1 ) { setError( e1 ); } catch( OutOfMemoryError e2 ) { inStream = null; outStream = null; envInStream = null; envOutStream= null; inBuf = null; outBuf = null; inEnvBuf = null; envInBuf = null; shapeEnvBuf = null; convBuf1 = null; convBuf2 = null; System.gc(); setError( new Exception( ERR_MEMORY )); } // ---- cleanup (topLevel) ---- if( inF != null ) { inF.cleanUp(); inF = null; } if( outF != null ) { outF.cleanUp(); outF = null; } if( envInF != null ) { envInF.cleanUp(); envInF = null; } if( envOutF != null ) { envOutF.cleanUp(); envOutF = 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( envFloatF != null ) { for( ch = 0; ch < envFloatF.length; ch++ ) { if( envFloatF[ ch ] != null ) envFloatF[ ch ].cleanUp(); if( envTempFile[ ch ] != null ) envTempFile[ ch ].delete(); } } } // process() // -------- private methods -------- /* * @param a Quell-Wellenform * @param env Ziel-RMS * @param average Laenge der Samples in a, aus denen jeweils ein RMS berechnet wird * (RMS = sqrt( energy/average )) * @param length Zahl der generierten RMS (in env) * @param lastEnergy Rueckgabewert aus dem letzten Aufruf dieser Routine * (richtige Initialisierung siehe process(): summe der quadrate der prebuffered samples) * @return neuer Energiewert, der beim naechsten Aufruf als lastEnergy uebergeben werden muss */ protected double calcEnv( float[] a, float[] env, int average, int length, double lastEnergy ) { for( int i = 0, j = average; i < length; i++, j++ ) { // zu alten leistungswert "vergessen" und lastEnergy = lastEnergy - a[ i ]*a[ i ] + a[ j ]*a[ j ]; // neuen addieren env[ i ] = (float) Math.sqrt( lastEnergy / average ); } return lastEnergy; } protected void reflectPropertyChanges() { super.reflectPropertyChanges(); Component c; c = gui.getItemObj( GG_ENVINFILE ); if( c != null ) { c.setEnabled( (pr.intg[ PR_ENVSOURCE ] == SRC_ENVFILE) || (pr.intg[ PR_ENVSOURCE ] == SRC_SOUNDFILE) ); } c = gui.getItemObj( GG_ENV ); if( c != null ) { c.setEnabled( pr.intg[ PR_ENVSOURCE ] == SRC_ENV ); } c = gui.getItemObj( GG_RIGHTCHAN ); if( c != null ) { c.setEnabled( pr.intg[ PR_ENVSOURCE ] == SRC_ENV ); } c = gui.getItemObj( GG_RIGHTCHANENV ); if( c != null ) { c.setEnabled( (pr.intg[ PR_ENVSOURCE ] == SRC_ENV) && pr.bool[ PR_RIGHTCHAN ]); } // c = gui.getItemObj( GG_AVERAGE ); // if( c != null ) { // c.setEnabled( (pr.intg[ PR_ENVSOURCE ] == SRC_INPUT) || // (pr.intg[ PR_ENVSOURCE ] == SRC_SOUNDFILE) ); // } c = gui.getItemObj( GG_ENVOUTFILE ); if( c != null ) { c.setEnabled( pr.bool[ PR_ENVOUTPUT ]); } } }