/*
* FreqModDlg.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.Filter;
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.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.io.EOFException;
import java.io.File;
import java.io.IOException;
/**
* Processing module for frequency modulation
* of a sound file using high quality band-limited
* resampling.
*/
public class FreqModDlg
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_MODFILE = 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_QUALITY = 3;
private static final int PR_SOURCE = 4;
private static final int PR_GAIN = 0; // pr.para
private static final int PR_OSCFREQ = 1;
private static final int PR_RATEMODDEPTH = 2;
private static final String PRN_INPUTFILE = "InputFile";
private static final String PRN_OUTPUTFILE = "OutputFile";
private static final String PRN_MODFILE = "ModFile";
private static final String PRN_OUTPUTTYPE = "OutputType";
private static final String PRN_OUTPUTRES = "OutputReso";
private static final String PRN_QUALITY = "Quality";
private static final String PRN_SOURCE = "Source";
private static final String PRN_OSCFREQ = "OscFreq";
private static final String PRN_RATEMODDEPTH = "RateModDepth";
private static final int QUAL_MEDIUM = 0; // PR_QUALITY
private static final int QUAL_GOOD = 1;
private static final int QUAL_VERYGOOD = 2;
private static final String[] QUAL_NAMES = { "Medium", "Good", "Very good" };
private static final int SRC_FILE = 0; // PR_SOURCE
private static final int SRC_SINE = 1;
private static final int SRC_TRI = 2;
private static final String[] SRC_NAMES = { "Soundfile", "Sine", "Triangle" };
private static final String prText[] = { "", "", "" };
private static final String prTextName[] = { PRN_INPUTFILE, PRN_OUTPUTFILE, PRN_MODFILE };
private static final int prIntg[] = { 0, 0, GAIN_UNITY, QUAL_GOOD, SRC_SINE };
private static final String prIntgName[] = { PRN_OUTPUTTYPE, PRN_OUTPUTRES, PRN_GAINTYPE, PRN_QUALITY,
PRN_SOURCE };
private static final Param prPara[] = { null, null, null };
private static final String prParaName[] = { PRN_GAIN, PRN_OSCFREQ, PRN_RATEMODDEPTH };
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_MODFILE = GG_OFF_PATHFIELD + PR_MODFILE;
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_QUALITY = GG_OFF_CHOICE + PR_QUALITY;
private static final int GG_SOURCE = GG_OFF_CHOICE + PR_SOURCE;
private static final int GG_OSCFREQ = GG_OFF_PARAMFIELD + PR_OSCFREQ;
private static final int GG_RATEMODDEPTH = GG_OFF_PARAMFIELD + PR_RATEMODDEPTH;
private static final int GG_GAINTYPE = GG_OFF_CHOICE + PR_GAINTYPE;
private static final int GG_GAIN = GG_OFF_PARAMFIELD + PR_GAIN;
private static PropertyArray static_pr = null;
private static Presets static_presets = null;
// -------- public methods --------
/**
* !! setVisible() bleibt dem Aufrufer ueberlassen
*/
public FreqModDlg()
{
super( "Frequency Modulation" );
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.para = prPara;
static_pr.para[ PR_OSCFREQ ] = new Param( 2000.0, Param.ABS_HZ );
static_pr.para[ PR_RATEMODDEPTH ] = new Param( 3.0, Param.OFFSET_SEMITONES );
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, ggModFile;
JComboBox ggQuality, ggSource;
ParamField ggRate, ggRateModDepth;
PathField[] ggInputs;
ParamSpace[] spcRateModDepth;
Component[] ggGain;
int i;
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_SOURCE:
pr.intg[ ID - GG_OFF_CHOICE ] = ((JComboBox) e.getSource()).getSelectedIndex();
reflectPropertyChanges();
break;
}
}
};
PathListener pathL = new PathListener() {
public void pathChanged( PathEvent e )
{
int ID = gui.getItemID( e );
switch( ID ) {
case GG_INPUTFILE:
setInput( ((PathField) e.getSource()).getPath().getPath() );
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 carrier file" );
ggInputFile.handleTypes( GenericFile.TYPES_SOUND );
con.gridwidth = 1;
con.weightx = 0.1;
gui.addLabel( new JLabel( "Carrier input", SwingConstants.RIGHT ));
con.gridwidth = GridBagConstraints.REMAINDER;
con.weightx = 0.9;
gui.addPathField( ggInputFile, GG_INPUTFILE, pathL );
ggModFile = new PathField( PathField.TYPE_INPUTFILE + PathField.TYPE_FORMATFIELD,
"Select modulator file" );
ggModFile.handleTypes( GenericFile.TYPES_SOUND );
con.gridwidth = 1;
con.weightx = 0.1;
gui.addLabel( new JLabel( "Modulator input", SwingConstants.RIGHT ));
con.gridwidth = GridBagConstraints.REMAINDER;
con.weightx = 0.9;
gui.addPathField( ggModFile, GG_MODFILE, pathL );
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;
// ggInputs[ 1 ] = ggModFile;
ggOutputFile.deriveFrom( ggInputs, "$D0$F0FM$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, il );
// -------- Settings --------
gui.addLabel( new GroupLabel( "Modulation", GroupLabel.ORIENT_HORIZONTAL,
GroupLabel.BRACE_NONE ));
ggSource = new JComboBox();
for( i = 0; i < SRC_NAMES.length; i++ ) {
ggSource.addItem( SRC_NAMES[ i ]);
}
con.weightx = 0.1;
con.gridwidth = 1;
gui.addLabel( new JLabel( "Source", SwingConstants.RIGHT ));
con.weightx = 0.4;
gui.addChoice( ggSource, GG_SOURCE, il );
ggQuality = new JComboBox();
for( i = 0; i < QUAL_NAMES.length; i++ ) {
ggQuality.addItem( QUAL_NAMES[ i ]);
}
con.weightx = 0.1;
gui.addLabel( new JLabel( "Quality", SwingConstants.RIGHT ));
con.weightx = 0.4;
con.gridwidth = GridBagConstraints.REMAINDER;
gui.addChoice( ggQuality, GG_QUALITY, il );
// spcRate = new ParamSpace[ 4 ];
// spcRate[0] = new ParamSpace( 689.1, 768000.0, 0.1, Param.ABS_HZ );
// spcRate[1] = Constants.spaces[ Constants.offsetSemitonesSpace ];
// spcRate[2] = Constants.spaces[ Constants.offsetFreqSpace ];
// spcRate[3] = new ParamSpace( -96000.0, 96000.0, 0.1, Param.OFFSET_HZ );
spcRateModDepth = new ParamSpace[ 3 ];
spcRateModDepth[0] = Constants.spaces[ Constants.offsetSemitonesSpace ];
spcRateModDepth[1] = Constants.spaces[ Constants.offsetFreqSpace ];
spcRateModDepth[2] = Constants.spaces[ Constants.offsetHzSpace ];
ggRateModDepth = new ParamField( spcRateModDepth );
// ggRateModDepth.setReference( ggRate );
con.weightx = 0.1;
con.gridwidth = 1;
gui.addLabel( new JLabel( "Mod. depth", SwingConstants.RIGHT ));
con.weightx = 0.4;
gui.addParamField( ggRateModDepth, GG_RATEMODDEPTH, null );
ggRate = new ParamField( Constants.spaces[ Constants.absHzSpace ]);
con.weightx = 0.1;
gui.addLabel( new JLabel( "Osc. freq.", SwingConstants.RIGHT ));
con.weightx = 0.4;
con.gridwidth = GridBagConstraints.REMAINDER;
gui.addParamField( ggRate, GG_OSCFREQ, 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;
int len, chunkLength;
long progOff, progLen;
boolean finished;
double d1, d2;
// io
AudioFile inF = null;
AudioFile modF = null;
AudioFile outF = null;
AudioFileDescr inStream = null;
AudioFileDescr modStream = null;
AudioFileDescr outStream = null;
FloatFile[] floatF = null;
File[] tempFile = null;
float[][] inBuf = null;
float[][] outBuf = null;
float[] convBuf1;
// Synthesize
float gain = 1.0f; // gain abs amp
Param ampRef = new Param( 1.0, Param.ABS_AMP ); // transform-Referenz
int ch;
// Smp Init
int inLength, inChanNum, modChanNum, outChanNum;
int modLength = 0;
int framesRead, framesRead2, framesWritten;
double inRate, inPhase;
double minRsmpFactor;
double dOff, inputIncr;
int outTransLen, factorOff;
int overlapSize, inputStep, inBufSize, outBufSize;
int offStart;
int fltSmpPerCrossing, fltLen, fltCrossings;
float[] flt, fltD;
double[] factor;
float[][] filter;
float fltGain, fltRolloff, fltWin;
double fltIncr;
double oscFreq = 1.0;
Param rateBase, rateDepth;
float maxAmp = 0.0f;
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;
inRate = inStream.rate;
// this helps to prevent errors from empty files!
if( inLength * inChanNum < 1 ) throw new EOFException( ERR_EMPTY );
// .... check running ....
if( !threadRunning ) break topLevel;
outChanNum = inChanNum;
modChanNum = 1;
switch( pr.intg[ PR_SOURCE ]) {
case SRC_FILE:
modF = AudioFile.openAsRead( new File( pr.text[ PR_MODFILE ]));
modStream = modF.getDescr();
modChanNum = modStream.channels;
modLength = (int) modStream.length;
outChanNum = Math.max( modChanNum, inChanNum );
// this helps to prevent errors from empty files!
if( modLength * modChanNum < 1 ) throw new EOFException( ERR_EMPTY );
// .... check running ....
if( !threadRunning ) break topLevel;
break;
case SRC_SINE:
case SRC_TRI:
oscFreq = pr.para[ PR_OSCFREQ ].value / inRate;
break;
}
// initialize various stuff
rateBase = new Param( inRate, Param.ABS_HZ );
rateDepth = pr.para[ PR_RATEMODDEPTH ];
fltSmpPerCrossing = 4096; // 2048 << quality;
switch( pr.intg[ PR_QUALITY ]) {
case QUAL_MEDIUM:
fltRolloff = 0.70f;
fltWin = 6.5f;
fltCrossings = 5;
break;
case QUAL_GOOD:
fltRolloff = 0.80f;
fltWin = 7.0f;
fltCrossings = 9;
break;
case QUAL_VERYGOOD:
fltRolloff = 0.86f;
fltWin = 7.5f;
fltCrossings = 15;
break;
default:
throw new IllegalArgumentException( String.valueOf( pr.intg[ PR_QUALITY ]));
}
// if( !rateMod && ((rateBase.value / inRate) >= 0.99) ) fltRolloff = 0.95f; // no aliasing
fltLen = (int) ((float) (fltSmpPerCrossing * fltCrossings) / fltRolloff + 0.5f);
flt = new float[ fltLen ];
// fltD = pr.bool[ PR_INTERPOLE ] ? new float[ fltLen ] : null;
fltD = null;
fltGain = Filter.createAntiAliasFilter( flt, fltD, fltLen, fltSmpPerCrossing, fltRolloff, fltWin );
filter = new float[ 3 ][];
filter[ 0 ] = flt;
filter[ 1 ] = fltD;
filter[ 2 ] = new float[ 2 ];
filter[ 2 ][ 0 ] = fltSmpPerCrossing;
filter[ 2 ][ 1 ] = fltGain;
inputStep = 32768 / inChanNum;
inPhase = 0.0;
progOff = 0;
progLen = (long) inLength*2;
// 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;
// calc minimum rsmp.factor; used for overlap-size
minRsmpFactor = rateBase.value;
minRsmpFactor = Math.min( minRsmpFactor,
(Param.transform( rateDepth, Param.ABS_HZ, rateBase, null )).value);
minRsmpFactor = Math.min( minRsmpFactor,
(Param.transform( new Param( -rateDepth.value, rateDepth.unit ),
Param.ABS_HZ, rateBase, null )).value);
minRsmpFactor /= inRate;
fltIncr = fltSmpPerCrossing * minRsmpFactor;
overlapSize = (int) ((double) fltLen / fltIncr) + 1;
inBufSize = (overlapSize << 1) + inputStep;
outBufSize = (int) ((double) inputStep / minRsmpFactor) + 1; // +1 als ArrayBounds security fuer FreqMod
inBuf = new float[ inChanNum ][ inBufSize ];
outBuf = new float[ Math.max( modChanNum, outChanNum )][ outBufSize ];
factor = new double[ outBufSize ];
Util.clear( inBuf );
// System.out.println( "minRsmpFactor "+minRsmpFactor+"; overlapSize "+overlapSize );
// 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 );
}
progLen += (long) inLength; // (outLength unknown)
} else {
gain = (float) (Param.transform( pr.para[ PR_GAIN ], Param.ABS_AMP, ampRef, null )).value;
}
// .... check running ....
if( !threadRunning ) break topLevel;
// ----==================== da resampling ====================----
framesWritten = 0;
framesRead = 0; // re input
framesRead2 = 0; // re mod
factorOff = 0;
// overlap = overlapSize;
offStart = overlapSize;
finished = false;
//System.out.println( "overlap "+overlapSize+"; inputStep "+inputStep );
//System.out.println( "rsmp "+rsmpFactor+"; frames to go"+inLength );
while( threadRunning && !finished ) {
// ==================== read input chunk ====================
// len = Math.min( inLength - framesRead, inputStep + overlap );
len = Math.min( inLength - framesRead, inBufSize - offStart );
chunkLength = offStart + len;
inF.readFrames( inBuf, offStart, len );
framesRead += len;
progOff += len;
// .... progress ....
setProgression( (float) progOff / (float) progLen );
// .... check running ....
if( !threadRunning ) break topLevel;
// zero-padding last chunk
if( chunkLength < inBufSize ) {
//System.out.println( "zero "+(inBufSize-chunkLength)+" frames" );
for( ch = 0; ch < inChanNum; ch++ ) {
convBuf1 = inBuf[ ch ];
for( i = chunkLength; i < inBufSize; i++ ) {
convBuf1[ i ] = 0.0f;
}
}
chunkLength += overlapSize; // YYY
if( chunkLength <= inBufSize ) {
finished = true;
} else {
chunkLength = inBufSize;
}
}
// System.out.println( "read "+len+" => "+offStart+"; chunkLength "+chunkLength+"; inPhase "+inPhase );
// ==================== read mod chunk ====================
switch( pr.intg[ PR_SOURCE ]) {
case SRC_FILE:
i = Math.min( modLength - framesRead2, outBufSize - factorOff );
modF.readFrames( outBuf, 0, i );
framesRead2 += i;
// progOff += len;
// .... progress ....
setProgression( (float) progOff / (float) progLen );
// .... check running ....
if( !threadRunning ) break topLevel;
convBuf1 = outBuf[ 0 ]; // XXX just take the left channel
for( j = factorOff + i; j < outBufSize; ) {
factor[ j++ ] = 1.0;
}
for( j = factorOff, k = 0; k < i; j++, k++ ) {
factor[ j ] = Param.transform( new Param( rateDepth.value * convBuf1[ k ], rateDepth.unit ),
Param.ABS_HZ, rateBase, null ).value / inRate;
}
break;
case SRC_SINE:
i = outBufSize - factorOff;
for( j = factorOff, k = 0; k < i; j++, k++ ) {
d1 = Math.sin( (framesRead2++) * oscFreq );
factor[ j ] = Param.transform( new Param( rateDepth.value * d1, rateDepth.unit ),
Param.ABS_HZ, rateBase, null ).value / inRate;
}
break;
case SRC_TRI:
throw new IOException( "Not yet implemented!" );
}
//System.out.println( "read "+len+" frames" );
//System.out.println( "dOff "+dOff+" chunkLength "+chunkLength );
// ---- FreqMod ----------------------------------------------------------------------
dOff = (double) overlapSize + inPhase;
d1 = (double) chunkLength - overlapSize; // YYY
d2 = dOff;
// double f99 = 9999.0;
for( outTransLen = 0; (d2 < d1) && (outTransLen < outBufSize); outTransLen++ ) {
// if( factor[ outTransLen ] < f99 ) {
// f99 = factor[ outTransLen ];
// }
inputIncr = 1.0 / factor[ outTransLen ];
d2 += inputIncr;
}
// System.out.println( "Min factor "+f99+"; dOff "+dOff+"; outTransLen "+outTransLen );
for( ch = 0; ch < outChanNum; ch++ ) {
convBuf1 = outBuf[ ch ];
resample( inBuf[ ch % inChanNum ], dOff, convBuf1, 0, outTransLen, factor, filter );
// measure max. gain
for( j = 0; j < outTransLen; j++ ) {
if( Math.abs( convBuf1[ j ]) > maxAmp ) {
maxAmp = Math.abs( convBuf1[ j ]);
}
}
}
dOff = d2;
// System.out.println( "dOff now "+dOff );
// ---- write output or temp ----
if( floatF != null ) {
//System.out.println( "write "+outTransLen+" to temp" );
for( ch = 0; ch < outChanNum; ch++ ) {
floatF[ ch ].writeFloats( outBuf[ ch ], 0, outTransLen );
}
} else {
//System.out.println( "write "+outTransLen+" to out" );
// adjust gain
for( ch = 0; ch < outChanNum; ch++ ) {
Util.mult( outBuf[ ch ], 0, outTransLen, gain );
}
outF.writeFrames( outBuf, 0, outTransLen );
}
framesWritten += outTransLen;
// ---- FreqMod : dun ----------------------------------------------------------------------
i = (int) (dOff - overlapSize);
j = chunkLength - i;
inPhase = dOff % 1.0;
offStart= j; // YYY
// shift buffers
for( ch = 0; ch < inChanNum; ch++ ) {
System.arraycopy( inBuf[ ch ], i, inBuf[ ch ], 0, j );
}
factorOff = outBufSize - outTransLen;
System.arraycopy( factor, outTransLen, factor, 0, factorOff );
// // ---- FreqMod : dun ----------------------------------------------------------------------
//
// inPhase = dOff - d1; // offset next time
// // shift buffers
// for( ch = 0; ch < inChanNum; ch++ ) {
// System.arraycopy( inBuf[ ch ], inputStep, inBuf[ ch ], 0, overlapSize << 1 );
// }
// factorOff = outBufSize - outTransLen;
// System.arraycopy( factor, outTransLen, factor, 0, factorOff );
// .... progress ....
progOff += len;
setProgression( (float) progOff / (float) progLen );
}
// .... check running ....
if( !threadRunning ) break topLevel;
// ---- clean up, normalize ----
inF.close();
inF = null;
inStream = null;
if( modF != null ) {
modF.close();
modF = null;
modStream = null;
}
if( pr.intg[ PR_GAINTYPE ] == GAIN_UNITY ) {
gain = (float) (Param.transform( pr.para[ PR_GAIN ], Param.ABS_AMP,
new Param( 1.0 / (double) 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;
}
}
outF.close();
outF = null;
// ---- Finish ----
// inform about clipping/ low level
maxAmp *= gain;
handleClipping( maxAmp );
}
catch( IOException e1 ) {
setError( e1 );
}
catch( OutOfMemoryError e2 ) {
inStream = null;
outStream = null;
modStream = null;
inBuf = null;
outBuf = null;
System.gc();
setError( new Exception( ERR_MEMORY ));
}
// ---- cleanup (topLevel) ----
if( inF != null ) {
inF.cleanUp();
}
if( modF != null ) {
modF.cleanUp();
}
if( outF != null ) {
outF.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 --------
/**
* Daten resamplen; "bandlimited interpolation"
*
* @param srcOff erlaubt verschiebung unterhalb sample-laenge!
* @param length bzgl. dest!! src muss laenge 'length/factor' aufgerundet haben!!
* @param factor dest-smpRate/src-smpRate
* @param filter Dimension 0: flt (erstes createAntiAliasFilter Argument), 1: fltD;
* 2: fltSmpPerCrossing; 3: fltGain (createAAF result)
*
* keine lineare Interpolation (wesentlich schneller!)
*/
protected void resample( float[] src, double srcOff, float[] dest, int destOff, int length,
double[] factor, float[][] filter )
{
int i, fltOffI, srcOffI;
double q, val, f, fltIncr, fltOff;
double phase = srcOff;
float[] flt = filter[ 0 ];
// float fltD[] = filter[ 1 ];
double fltSmpPerCrossing = (double) filter[ 2 ][ 0 ];
double gain = (double) filter[ 2 ][ 1 ];
double rsmpGain;
int fltLen = flt.length;
int srcLen = src.length;
// System.out.println( "Rsmp "+srcOff+" ["+srcLen+"] => "+destOff+" +"+length+" ["+dest.length+"]" );
for( i = 0; i < length; i++, phase += 1.0 / f ) {
f = factor[ i ];
if( f < 1.0 ) {
fltIncr = fltSmpPerCrossing * f;
rsmpGain= gain;
} else {
fltIncr = fltSmpPerCrossing;
rsmpGain= gain / f;
}
q = phase % 1.0;
val = 0.0;
srcOffI = (int) phase;
fltOff = q * fltIncr + 0.5f; // wenn wir schon keine interpolation mehr benutzen...
fltOffI = (int) fltOff;
while( (fltOffI < fltLen) && (srcOffI >= 0) ) {
val += (double) src[ srcOffI ] * flt[ fltOffI ];
srcOffI--;
fltOff += fltIncr;
fltOffI = (int) fltOff;
}
srcOffI = (int) phase + 1;
fltOff = (1.0 - q) * fltIncr;
fltOffI = (int) fltOff;
while( (fltOffI < fltLen) && (srcOffI < srcLen) ) {
val += (double) src[ srcOffI ] * flt[ fltOffI ];
srcOffI++;
fltOff += fltIncr;
fltOffI = (int) fltOff;
}
dest[ destOff++ ] = (float) (val * rsmpGain);
}
}
/**
* 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();
// refInp = stream;
ref = new Param( stream.rate, Param.ABS_HZ );
ggSlave = (ParamField) gui.getItemObj( GG_RATEMODDEPTH );
if( ggSlave != null ) {
ggSlave.setReference( ref );
}
} catch( IOException e1 ) {
// refInp = null;
}
}
protected void reflectPropertyChanges() {
super.reflectPropertyChanges();
Component c;
c = gui.getItemObj(GG_MODFILE);
if (c != null) {
c.setEnabled(pr.intg[PR_SOURCE] == SRC_FILE);
}
c = gui.getItemObj(GG_OSCFREQ);
if (c != null) {
c.setEnabled(pr.intg[PR_SOURCE] != SRC_FILE);
}
}
}