/*
* ResampleDlg.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.spect.SpectStream;
import de.sciss.fscape.util.Constants;
import de.sciss.fscape.util.Envelope;
import de.sciss.fscape.util.Filter;
import de.sciss.fscape.util.Modulator;
import de.sciss.fscape.util.Param;
import de.sciss.fscape.util.ParamSpace;
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 band-limited resampling using
* an algorithm described by J.O.Smith.
*/
public class ResampleDlg
extends ModulePanel {
// -------- private variables --------
// Properties (defaults)
private static final int PR_INPUTFILE = 0; // pr.text
private static final int PR_OUTPUTFILE = 1;
private static final int PR_OUTPUTTYPE = 0; // pr.intg
private static final int PR_OUTPUTRES = 1;
private static final int PR_GAINTYPE = 2;
private static final int PR_QUALITY = 3;
private static final int PR_GAIN = 0; // pr.para
private static final int PR_RATE = 1;
private static final int PR_RATEMODDEPTH = 2;
private static final int PR_RIGHTCHANMODDEPTH = 3;
private static final int PR_LENGTH = 4;
private static final int PR_RATEMOD = 0; // pr.bool
private static final int PR_KEEPHEADER = 1;
private static final int PR_RIGHTCHAN = 2;
private static final int PR_USELENGTH = 3;
private static final int PR_INTERPOLE = 4;
private static final int PR_RATEMODENV = 0; // pr.envl
private static final int PR_RIGHTCHANMODENV = 1;
private static final String PRN_INPUTFILE = "InputFile";
private static final String PRN_OUTPUTFILE = "OutputFile";
private static final String PRN_OUTPUTTYPE = "OutputType";
private static final String PRN_OUTPUTRES = "OutputReso";
private static final String PRN_QUALITY = "Quality";
private static final String PRN_RATE = "Rate";
private static final String PRN_RATEMODDEPTH = "RateModDepth";
private static final String PRN_RIGHTCHANMODDEPTH = "RightChanModDepth";
private static final String PRN_LENGTH = "Length";
private static final String PRN_RATEMOD = "RateMod";
private static final String PRN_KEEPHEADER = "KeepHeader";
private static final String PRN_RIGHTCHAN = "RightChan";
private static final String PRN_USELENGTH = "UseLength";
private static final String PRN_INTERPOLE = "Interpole";
private static final String PRN_RATEMODENV = "RateModEnv";
private static final String PRN_RIGHTCHANMODENV = "RightChanModEnv";
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 prText[] = { "", "" };
private static final String prTextName[] = { PRN_INPUTFILE, PRN_OUTPUTFILE };
private static final int prIntg[] = { 0, 0, GAIN_UNITY, QUAL_GOOD };
private static final String prIntgName[] = { PRN_OUTPUTTYPE, PRN_OUTPUTRES, PRN_GAINTYPE, PRN_QUALITY };
private static final Param prPara[] = { null, null, null, null, null };
private static final String prParaName[] = { PRN_GAIN, PRN_RATE, PRN_RATEMODDEPTH, PRN_RIGHTCHANMODDEPTH, PRN_LENGTH };
private static final boolean prBool[] = { false, true, false, false, false };
private static final String prBoolName[] = { PRN_RATEMOD, PRN_KEEPHEADER, PRN_RIGHTCHAN, PRN_USELENGTH, PRN_INTERPOLE };
private static final Envelope prEnvl[] = { null, null };
private static final String prEnvlName[] = { PRN_RATEMODENV, PRN_RIGHTCHANMODENV };
private static final int GG_INPUTFILE = GG_OFF_PATHFIELD + PR_INPUTFILE;
private static final int GG_OUTPUTFILE = GG_OFF_PATHFIELD + PR_OUTPUTFILE;
private static final int GG_OUTPUTTYPE = GG_OFF_CHOICE + PR_OUTPUTTYPE;
private static final int GG_OUTPUTRES = GG_OFF_CHOICE + PR_OUTPUTRES;
private static final int GG_QUALITY = GG_OFF_CHOICE + PR_QUALITY;
private static final int GG_RATE = GG_OFF_PARAMFIELD + PR_RATE;
private static final int GG_RATEMODDEPTH = GG_OFF_PARAMFIELD + PR_RATEMODDEPTH;
private static final int GG_RIGHTCHANMODDEPTH = GG_OFF_PARAMFIELD + PR_RIGHTCHANMODDEPTH;
private static final int GG_LENGTH = GG_OFF_PARAMFIELD + PR_LENGTH;
private static final int GG_RATEMOD = GG_OFF_CHECKBOX + PR_RATEMOD;
private static final int GG_KEEPHEADER = GG_OFF_CHECKBOX + PR_KEEPHEADER;
private static final int GG_RIGHTCHAN = GG_OFF_CHECKBOX + PR_RIGHTCHAN;
private static final int GG_INTERPOLE = GG_OFF_CHECKBOX + PR_INTERPOLE;
private static final int GG_RATEMODENV = GG_OFF_ENVICON + PR_RATEMODENV;
private static final int GG_RIGHTCHANMODENV = GG_OFF_ENVICON + PR_RIGHTCHANMODENV;
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;
private AudioFileDescr refInp = null;
// -------- public methods --------
/**
* !! setVisible() bleibt dem Aufrufer ueberlassen
*/
public ResampleDlg()
{
super( "Resample" );
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_LENGTH ] = new Param( 0.0, Param.OFFSET_TIME );
static_pr.para[ PR_RATE ] = new Param( 0.0, Param.OFFSET_SEMITONES );
static_pr.para[ PR_RATEMODDEPTH ] = new Param( 1.0, Param.OFFSET_SEMITONES );
static_pr.para[ PR_RIGHTCHANMODDEPTH ] = new Param( 1.0, Param.OFFSET_SEMITONES );
static_pr.paraName = prParaName;
static_pr.bool = prBool;
static_pr.boolName = prBoolName;
static_pr.envl = prEnvl;
static_pr.envl[ PR_RATEMODENV ] = Envelope.createBasicEnvelope( Envelope.BASIC_TIME );
static_pr.envl[ PR_RIGHTCHANMODENV ]= Envelope.createBasicEnvelope( Envelope.BASIC_TIME );
static_pr.envlName = prEnvlName;
// static_pr.superPr = DocumentFrame.static_pr;
fillDefaultAudioDescr( static_pr.intg, PR_OUTPUTTYPE, PR_OUTPUTRES );
fillDefaultGain( static_pr.para, PR_GAIN );
static_presets = new Presets( getClass(), static_pr.toProperties( true ));
}
presets = static_presets;
pr = (PropertyArray) static_pr.clone();
// -------- build GUI --------
GridBagConstraints con;
PathField ggInputFile, ggOutputFile;
JComboBox ggQuality;
ParamField ggRate, ggRateModDepth, ggRightChanModDepth, ggLength;
EnvIcon ggRateModEnv, ggRightChanModEnv;
JCheckBox ggRateMod, ggRightChan, ggKeepHeader, ggInterpole;
PathField[] ggInputs;
ParamSpace[] spcRate, spcRateModDepth, spcLength;
Component[] ggGain;
gui = new GUISupport();
con = gui.getGridBagConstraints();
con.insets = new Insets( 1, 2, 1, 2 );
ParamListener paramL = new ParamListener() {
public void paramChanged( ParamEvent e )
{
int ID = gui.getItemID( e );
switch( ID ) {
case GG_RATE:
pr.bool[ PR_USELENGTH ] = false;
rateLenInterference();
break;
case GG_LENGTH:
pr.bool[ PR_USELENGTH ] = true;
rateLenInterference();
break;
}
}
};
ItemListener il = new ItemListener() {
public void itemStateChanged( ItemEvent e )
{
int ID = gui.getItemID( e );
switch( ID ) {
case GG_RATEMOD:
case GG_RIGHTCHAN:
case GG_KEEPHEADER:
pr.bool[ ID - GG_OFF_CHECKBOX ] = ((JCheckBox) e.getSource()).isSelected();
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 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, 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;
ggOutputFile.deriveFrom( ggInputs, "$D0$F0Rsmp$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, paramL );
con.weightx = 0.5;
con.gridwidth = GridBagConstraints.REMAINDER;
gui.addChoice( (JComboBox) ggGain[ 1 ], GG_GAINTYPE, il );
// -------- Settings --------
gui.addLabel( new GroupLabel( "Sample Rate Conversion", GroupLabel.ORIENT_HORIZONTAL,
GroupLabel.BRACE_NONE ));
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 );
spcLength = new ParamSpace[ 5 ];
spcLength[0] = Constants.spaces[ Constants.absMsSpace ];
spcLength[1] = Constants.spaces[ Constants.absBeatsSpace ];
spcLength[2] = Constants.spaces[ Constants.offsetMsSpace ];
spcLength[3] = Constants.spaces[ Constants.offsetBeatsSpace ];
spcLength[4] = Constants.spaces[ Constants.offsetTimeSpace ];
spcRateModDepth = new ParamSpace[ 3 ];
spcRateModDepth[0] = Constants.spaces[ Constants.offsetSemitonesSpace ];
spcRateModDepth[1] = Constants.spaces[ Constants.offsetFreqSpace ];
spcRateModDepth[2] = spcRate[ 3 ];
ggRate = new ParamField( spcRate );
con.weightx = 0.1;
con.gridwidth = 1;
gui.addLabel( new JLabel( "New rate", SwingConstants.RIGHT ));
con.weightx = 0.4;
gui.addParamField( ggRate, GG_RATE, paramL );
ggRateMod = new JCheckBox();
con.weightx = 0.0;
gui.addCheckbox( ggRateMod, GG_RATEMOD, il );
ggRateModDepth = new ParamField( spcRateModDepth );
ggRateModDepth.setReference( ggRate );
con.weightx = 0.5;
con.gridwidth = 2;
gui.addParamField( ggRateModDepth, GG_RATEMODDEPTH, paramL );
ggRateModEnv = new EnvIcon( getComponent() );
con.weightx = 0.0;
con.gridwidth = GridBagConstraints.REMAINDER;
gui.addGadget( ggRateModEnv, GG_RATEMODENV );
ggRightChan = new JCheckBox();
con.gridwidth = 2;
con.weightx = 0.5;
gui.addLabel( new JLabel( "Distinct right channel mod.", SwingConstants.RIGHT ));
con.weightx = 0.0;
con.gridwidth = 1;
gui.addCheckbox( ggRightChan, GG_RIGHTCHAN, il );
ggRightChanModDepth = new ParamField( spcRateModDepth );
ggRightChanModDepth.setReference( ggRate );
con.weightx = 0.5;
con.gridwidth = 2;
gui.addParamField( ggRightChanModDepth, GG_RIGHTCHANMODDEPTH, paramL );
ggRightChanModEnv= new EnvIcon( getComponent() );
con.weightx = 0.0;
con.gridwidth = GridBagConstraints.REMAINDER;
gui.addGadget( ggRightChanModEnv, GG_RIGHTCHANMODENV );
ggLength = new ParamField( spcLength );
con.weightx = 0.1;
con.gridwidth = 1;
gui.addLabel( new JLabel( "Desired length", SwingConstants.RIGHT ));
con.weightx = 0.4;
gui.addParamField( ggLength, GG_LENGTH, paramL );
con.gridwidth = GridBagConstraints.REMAINDER;
gui.addLabel( new JLabel() );
ggKeepHeader = new JCheckBox("Change Pitch/Speed");
con.weightx = 0.0;
con.gridwidth = 1;
gui.addLabel( new JLabel() );
con.weightx = 0.5;
gui.addCheckbox( ggKeepHeader, GG_KEEPHEADER, il );
ggQuality = new JComboBox();
ggQuality.addItem( "Short" );
ggQuality.addItem( "Medium" );
ggQuality.addItem( "Long" );
con.weightx = 0.1;
con.gridwidth = 2;
gui.addLabel( new JLabel( "FIR length", SwingConstants.RIGHT ));
con.weightx = 0.2;
gui.addChoice( ggQuality, GG_QUALITY, il );
ggInterpole = new JCheckBox( "Interpolate" );
con.gridwidth = GridBagConstraints.REMAINDER;
gui.addCheckbox( ggInterpole, GG_INTERPOLE, il );
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;
int len, chunkLength;
long progOff, progLen;
boolean finished;
// io
AudioFile inF = null;
AudioFile outF = null;
AudioFileDescr inStream = null;
AudioFileDescr outStream = null;
FloatFile floatF[] = null;
File tempFile[] = null;
int inChanNum;
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
long totalInSamples; // reichen 32 bit?
long inLength;
long framesRead, framesWritten;
int inBufSize, outBufSize;
double inPhase;
double rsmpFactor;
double minRsmpFactor;
int quality;
double dOff, inTransLen, inputIncr;
int outTransLen;
int overlapSize, inputStep;
int offStart, overlap;
int fltSmpPerCrossing, fltCrossings, fltLen;
float flt[], fltD[], filter[][];
float fltGain, fltRolloff, fltWin;
double fltIncr;
Param rateBase, rateDepth;
boolean rateMod, rightMod;
double inRate, outRate;
// mod
SpectStream mStream = null;
double mFrames = 0.0; // mStream.framesRead
Modulator mRateMod = null;
int mStep;
// right mod
double rmFrames[] = null;
double rdOff[] = null;
double rInPhase[] = null;
double leftFactor, rightFactor;
Modulator mRightMod = null;
double rInputIncr;
float floaty;
Param rightDepth;
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 = inStream.length;
// System.out.println("inLength = " + inLength);
totalInSamples = inLength * inChanNum;
inRate = inStream.rate;
// this helps to prevent errors from empty files!
if( totalInSamples < 1 ) throw new EOFException( ERR_EMPTY );
// .... check running ....
if( !threadRunning ) break topLevel;
// initialize various stuff
rateBase = Param.transform( pr.para[ PR_RATE ], Param.ABS_HZ,
new Param( inStream.rate, Param.ABS_HZ ), null );
rateDepth = pr.para[ PR_RATEMODDEPTH ];
rightDepth = pr.para[ PR_RIGHTCHANMODDEPTH ];
rateMod = pr.bool[ PR_RATEMOD ];
rightMod = pr.bool[ PR_RIGHTCHAN ] && (inChanNum > 1) && rateMod;
quality = pr.intg[ PR_QUALITY ];
fltSmpPerCrossing = 4096; // 2048 << quality;
switch( 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( quality ));
}
// if( !rateMod && ((rateBase.value / inRate) >= 0.99) ) fltRolloff = 0.95f; // no aliasing
fltLen = (int) ((fltSmpPerCrossing * fltCrossings) / fltRolloff + 0.5f);
flt = new float[ fltLen ];
fltD = pr.bool[ PR_INTERPOLE ] ? new float[ fltLen ] : 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;
// Debug.view( flt, "filter" );
progOff = 0;
progLen = inLength*2;
// output
ggOutput = (PathField) gui.getItemObj( GG_OUTPUTFILE );
if( ggOutput == null ) throw new IOException( ERR_MISSINGPROP );
outStream = new AudioFileDescr( inStream );
ggOutput.fillStream( outStream );
if( !pr.bool[ PR_KEEPHEADER ]) {
outStream.rate = (float) rateBase.value;
}
outRate = outStream.rate;
outF = AudioFile.openAsWrite( outStream );
// .... check running ....
if( !threadRunning ) break topLevel;
// calc minimum rsmp.factor; used for overlap-size
minRsmpFactor = rateBase.value;
if( rateMod ) {
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);
mStream = new SpectStream();
mStream.setChannels( inChanNum );
mStream.setBands( 0.0f, (float) inStream.rate / 2, 1, SpectStream.MODE_LIN );
mStream.setRate( (float) inStream.rate, 1 ); // !!
mStream.setEstimatedLength( inLength ); // !!
mStream.getDescr();
mRateMod = new Modulator( rateBase, rateDepth, pr.envl[ PR_RATEMODENV ], mStream );
if( rightMod ) {
rdOff = new double[ inChanNum ];
rInPhase = new double[ inChanNum ];
rmFrames = new double[ inChanNum ];
for( ch = 0; ch < inChanNum; ch++ ) {
rInPhase[ ch ] = 0.0;
rmFrames[ ch ] = 0.0;
}
mRightMod = new Modulator( rateBase, rightDepth, pr.envl[ PR_RIGHTCHANMODENV ], mStream );
}
}
minRsmpFactor /= inRate;
if( minRsmpFactor < 1.0 ) {
fltIncr = fltSmpPerCrossing * minRsmpFactor;
} else {
fltIncr = fltSmpPerCrossing;
}
overlapSize = (int) (fltLen / fltIncr) + 1;
inBufSize = (overlapSize << 1) + inputStep;
outBufSize = inputStep + 1; // +1 als ArrayBounds security fuer Resample
inBuf = new float[ inChanNum ][ inBufSize ];
outBuf = new float[ inChanNum ][ outBufSize ];
// clear buffers
for( ch = 0; ch < inChanNum; ch++ ) {
convBuf1 = inBuf[ ch ];
for( i = 0; i < convBuf1.length; i++ ) {
convBuf1[ i ] = 0.0f;
}
convBuf1 = outBuf[ ch ];
for( i = 0; i < convBuf1.length; i++ ) {
convBuf1[ i ] = 0.0f;
}
}
// normalization requires temp files
if( (pr.intg[ PR_GAINTYPE ] == GAIN_UNITY) || rightMod ) {
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;
}
if( pr.intg[ PR_GAINTYPE ] == GAIN_ABSOLUTE ) {
gain = (float) ((Param.transform( pr.para[ PR_GAIN ], Param.ABS_AMP, ampRef, null )).value);
}
// .... check running ....
if( !threadRunning ) break topLevel;
// ----==================== da resampling ====================----
framesWritten = 0L;
// samplesWritten = 0L;
framesRead = 0L;
rsmpFactor = rateBase.value / inRate;
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 = (int) Math.min( inLength - framesRead, inputStep + overlap );
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 + len + overlapSize - overlap;
finished = true;
} else {
chunkLength = overlapSize + Math.min( inputStep, len );
}
if( overlap > 0 ) { // first pass sucky overhead
overlap = 0;
offStart = overlapSize << 1;
}
//System.out.println( "read "+len+" frames" );
//System.out.println( "dOff "+dOff+" chunkLength "+chunkLength );
// ---- resample : "normal" ----------------------------------------------------------------------
if( !rightMod ) {
dOff = overlapSize + inPhase;
while( dOff < chunkLength ) {
inTransLen = chunkLength - dOff;
// modulated
if( rateMod ) {
rsmpFactor = mRateMod.calc().value / inRate;
mStep = (int) (rsmpFactor * outRate / 50); // to be smooth just step max. 20 ms per mod.
outTransLen = Math.min( mStep, (int) (inTransLen * rsmpFactor) );
outTransLen = Math.max( 1, Math.min( outTransLen, inputStep )); // outBuf.length limit
inputIncr = outTransLen / rsmpFactor;
mFrames += inputIncr;
mStream.framesRead = (long) mFrames; // dirty little boy, dirty little boy
// ae-statikk
} else {
outTransLen = Math.max( 1, Math.min( (int) (inTransLen * rsmpFactor), inputStep ));
inputIncr = outTransLen / rsmpFactor;
}
if( inputIncr <= 0 ) break;
//System.out.println( "inTrans "+inTransLen+"; outTrans "+outTransLen+"; inpIncr "+inputIncr );
for( ch = 0; ch < inChanNum; ch++ ) {
convBuf1 = outBuf[ ch ];
Filter.resample( inBuf[ ch ], dOff, convBuf1, 0, outTransLen, rsmpFactor, filter );
// measure max. gain
for( j = 0; j < outTransLen; j++ ) {
if( Math.abs( convBuf1[ j ]) > maxAmp ) {
maxAmp = Math.abs( convBuf1[ j ]);
}
}
}
dOff += inputIncr;
//System.out.println( "dOff now "+dOff );
// ---- write output or temp ----
if( floatF != null ) {
//System.out.println( "write "+outTransLen+" to temp" );
for( ch = 0; ch < inChanNum; ch++ ) {
floatF[ ch ].writeFloats( outBuf[ ch ], 0, outTransLen );
}
} else {
//System.out.println( "write "+outTransLen+" to out" );
// adjust gain
for( ch = 0; ch < inChanNum; ch++ ) {
convBuf1 = outBuf[ ch ];
for( j = 0; j < outTransLen; j++ ) {
convBuf1[ j ] *= gain;
}
}
outF.writeFrames( outBuf, 0, outTransLen );
}
framesWritten += outTransLen;
} // while resample
inPhase = dOff - chunkLength; // offset next time
// ---- resample : "split brain" ----------------------------------------------------------------------
} else {
for( ch = 0; ch < inChanNum; ch++ ) {
rdOff[ ch ] = overlapSize + rInPhase[ ch ];
}
do {
rInputIncr = 0.0;
for( ch = 0; ch < inChanNum; ch++ ) {
if( rdOff[ ch ] >= chunkLength ) continue;
inTransLen = chunkLength - rdOff[ ch ];
mStream.framesRead = (long) rmFrames[ ch ]; // dirty little boy, dirty little boy
leftFactor = mRateMod.calc().value / inRate;
rightFactor = mRightMod.calc().value / inRate;
floaty = (float) ch / (float) (inChanNum - 1); // gewichtung; (rein theoret. >2 chn. moeglich!!!!)
rsmpFactor = leftFactor * (1.0f - floaty) +
rightFactor * floaty;
mStep = (int) (rsmpFactor * outRate / 50); // to be smooth just step max. 20 ms per mod.
outTransLen = Math.min( mStep, (int) (inTransLen * rsmpFactor) );
outTransLen = Math.max( 1, Math.min( outTransLen, inputStep )); // outBuf.length limit
inputIncr = outTransLen / rsmpFactor;
rmFrames[ ch ] += inputIncr;
rInputIncr += inputIncr;
if( inputIncr <= 0 ) continue;
convBuf1 = outBuf[ ch ];
Filter.resample( inBuf[ ch ], rdOff[ ch ], convBuf1, 0, outTransLen, rsmpFactor, filter );
// measure max. gain
for( j = 0; j < outTransLen; j++ ) {
if( Math.abs( convBuf1[ j ]) > maxAmp ) {
maxAmp = Math.abs( convBuf1[ j ]);
}
}
rdOff[ ch ] += inputIncr;
// ---- write to temp ----
floatF[ ch ].writeFloats( outBuf[ ch ], 0, outTransLen );
// samplesWritten += outTransLen;
} // for channels
} while( rInputIncr > 0.0 );
for( ch = 0; ch < inChanNum; ch++ ) {
rInPhase[ ch ] = rdOff[ ch ] - chunkLength; // offset next time
}
}
// ---- resample : dun ----------------------------------------------------------------------
// shift buffers
for( ch = 0; ch < inChanNum; ch++ ) {
System.arraycopy( inBuf[ ch ], inputStep, inBuf[ ch ], 0, overlapSize << 1 );
}
// .... progress ....
progOff += len;
setProgression( (float) progOff / (float) progLen );
}
// .... check running ....
if( !threadRunning ) break topLevel;
// ---- clean up, normalize ----
inF.close();
inF = null;
// inStream = null;
if( (pr.intg[ PR_GAINTYPE ] == GAIN_UNITY) || rightMod ) {
gain = (float) (Param.transform( pr.para[ PR_GAIN ], Param.ABS_AMP,
new Param( 1.0 / maxAmp, Param.ABS_AMP ), null )).value;
}
if( floatF != null ) {
// right mod. split brain files do not have the
// same length; paddem widd zeroz
if( rightMod ) {
framesWritten = 0;
for( ch = 0; ch < inChanNum; ch++ ) {
framesWritten = Math.max( framesWritten, floatF[ ch ].getSize());
}
convBuf1 = outBuf[ 0 ];
for( i = 0; i < outBufSize; i++ ) { // create empty space
convBuf1[ i ] = 0.0f;
}
for( ch = 0; ch < inChanNum; ch++ ) {
for( long ii = floatF[ ch ].getSize(); threadRunning && (ii < framesWritten); ) {
len = (int) Math.min( framesWritten - ii, outBufSize );
floatF[ ch ].writeFloats( convBuf1, 0, len );
ii += len;
// .... progress ....
setProgression( (float) progOff / (float) progLen );
}
// .... check running ....
if( !threadRunning ) break topLevel;
}
}
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;
}
}
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;
inBuf = null;
outBuf = null;
System.gc();
setError( new Exception( ERR_MEMORY ));
}
// ---- cleanup (topLevel) ----
if( inF != null ) {
inF.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 --------
/**
* Set new input file
*/
protected void setInput( String fname )
{
AudioFile f;
AudioFileDescr stream;
ParamField ggSlave;
Param ref;
// ---- Header lesen ----
try {
f = AudioFile.openAsRead( new File( fname ));
stream = f.getDescr();
f.close();
refInp = stream;
ref = new Param( AudioFileDescr.samplesToMillis( stream, stream.length ), Param.ABS_MS );
ggSlave = (ParamField) gui.getItemObj( GG_LENGTH );
if( ggSlave != null ) {
ggSlave.setReference( ref );
}
ref = new Param( stream.rate, Param.ABS_HZ );
ggSlave = (ParamField) gui.getItemObj( GG_RATE );
if( ggSlave != null ) {
ggSlave.setReference( ref );
}
rateLenInterference();
} catch( IOException e1 ) {
refInp = null;
}
}
protected void rateLenInterference()
{
ParamField ggLength = (ParamField) gui.getItemObj( GG_LENGTH );
ParamField ggRate = (ParamField) gui.getItemObj( GG_RATE );
JCheckBox ggKeepHeader= (JCheckBox) gui.getItemObj( GG_KEEPHEADER );
boolean keepHeader;
Param ref1, ref2;
Param p1, pa1, p2, pa2;
if( (ggLength == null) || (ggRate == null) || (ggKeepHeader == null) || (refInp == null) ) return;
keepHeader = ggKeepHeader.isSelected();
ref1 = new Param( refInp.rate, Param.ABS_HZ );
ref2 = new Param( AudioFileDescr.samplesToMillis( refInp, refInp.length ), Param.ABS_MS );
p1 = ggRate.getParam();
p2 = ggLength.getParam();
pa1 = Param.transform( p1, Param.ABS_HZ, ref1, null );
pa2 = Param.transform( p2, Param.ABS_MS, ref2, null );
if( pr.bool[ PR_USELENGTH ]) {
pa1 = new Param( (pa2.value / ref2.value) * ref1.value, Param.ABS_HZ );
p1 = Param.transform( pa1, p1.unit, ref1, null );
ggRate.setParam( p1 );
} else {
if( !keepHeader ) {
pa2 = ref2;
} else {
pa2 = new Param( (pa1.value / ref1.value) * ref2.value, Param.ABS_MS );
}
p2 = Param.transform( pa2, p2.unit, ref2, null );
ggLength.setParam( p2 );
}
}
protected void reflectPropertyChanges()
{
super.reflectPropertyChanges();
Component c;
c = gui.getItemObj( GG_RATEMODDEPTH );
if( c != null ) {
c.setEnabled( pr.bool[ PR_RATEMOD ]);
}
c = gui.getItemObj( GG_RATEMODENV );
if( c != null ) {
c.setEnabled( pr.bool[ PR_RATEMOD ]);
}
c = gui.getItemObj( GG_RIGHTCHAN );
if( c != null ) {
c.setEnabled( pr.bool[ PR_RATEMOD ]);
}
c = gui.getItemObj( GG_RIGHTCHANMODDEPTH );
if( c != null ) {
c.setEnabled( pr.bool[ PR_RATEMOD ] && pr.bool[ PR_RIGHTCHAN ]);
}
c = gui.getItemObj( GG_RIGHTCHANMODENV );
if( c != null ) {
c.setEnabled( pr.bool[ PR_RATEMOD ] && pr.bool[ PR_RIGHTCHAN ]);
}
c = gui.getItemObj( GG_LENGTH );
if( c != null ) {
c.setEnabled( !pr.bool[ PR_RATEMOD ] && pr.bool[ PR_KEEPHEADER ]);
}
}
}