/*
* SchizoDlg.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.Fourier;
import de.sciss.fscape.util.Constants;
import de.sciss.fscape.util.Param;
import de.sciss.fscape.util.ParamSpace;
import de.sciss.fscape.util.Util;
import de.sciss.io.AudioFile;
import de.sciss.io.AudioFileDescr;
import de.sciss.io.IOUtil;
import javax.swing.*;
import java.awt.*;
import java.io.EOFException;
import java.io.File;
import java.io.IOException;
/**
* Fake mono to stereo processing module
* using different filtering of the left
* and right channel.
*/
public class SchizoDlg
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_CALLOSUMFILE = 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_GAIN = 0; // pr.para
private static final int PR_OFFSET = 1;
private static final int PR_MGAIN = 2;
private static final int PR_SGAIN = 3;
private static final int PR_CONVLENGTH = 4;
private static final int PR_FLATSPECT = 0; // pr.bool
private static final int PR_ZEROPHASE = 1;
private static final String PRN_INPUTFILE = "InputFile";
private static final String PRN_OUTPUTFILE = "OutputFile";
private static final String PRN_CALLOSUMFILE = "CallosumFile";
private static final String PRN_OUTPUTTYPE = "OutputType";
private static final String PRN_OUTPUTRES = "OutputReso";
private static final String PRN_OFFSET = "Offset";
private static final String PRN_MGAIN = "MGain";
private static final String PRN_SGAIN = "SGain";
private static final String PRN_CONVLENGTH = "ConvLen";
private static final String PRN_FLATSPECT = "FlatSpect";
private static final String PRN_ZEROPHASE = "ZeroPhase";
private static final String prText[] = { "", "", "" };
private static final String prTextName[] = { PRN_INPUTFILE, PRN_OUTPUTFILE, PRN_CALLOSUMFILE };
private static final int prIntg[] = { 0, 0, GAIN_UNITY };
private static final String prIntgName[] = { PRN_OUTPUTTYPE, PRN_OUTPUTRES, PRN_GAINTYPE };
private static final boolean prBool[] = { true, false };
private static final String prBoolName[] = { PRN_FLATSPECT, PRN_ZEROPHASE };
private static final Param prPara[] = { null, null, null, null, null };
private static final String prParaName[] = { PRN_GAIN, PRN_OFFSET, PRN_MGAIN, PRN_SGAIN, PRN_CONVLENGTH };
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_CALLOSUMFILE = GG_OFF_PATHFIELD + PR_CALLOSUMFILE;
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_GAIN = GG_OFF_PARAMFIELD + PR_GAIN;
private static final int GG_OFFSET = GG_OFF_PARAMFIELD + PR_OFFSET;
private static final int GG_MGAIN = GG_OFF_PARAMFIELD + PR_MGAIN;
private static final int GG_SGAIN = GG_OFF_PARAMFIELD + PR_SGAIN;
private static final int GG_CONVLENGTH = GG_OFF_PARAMFIELD + PR_CONVLENGTH;
private static final int GG_FLATSPECT = GG_OFF_CHECKBOX + PR_FLATSPECT;
private static final int GG_ZEROPHASE = GG_OFF_CHECKBOX + PR_ZEROPHASE;
private static PropertyArray static_pr = null;
private static Presets static_presets = null;
private static final String ERR_CHANNELS = "Callosum chan# cannot be\ngreater than output chan#!";
// -------- public methods --------
/**
* !! setVisible() bleibt dem Aufrufer ueberlassen
*/
public SchizoDlg()
{
super( "Schizophrenia" );
init2();
}
protected void buildGUI()
{
// einmalig PropertyArray initialisieren
if( static_pr == null ) {
static_pr = new PropertyArray();
static_pr.text = prText;
static_pr.textName = prTextName;
static_pr.intg = prIntg;
static_pr.intgName = prIntgName;
static_pr.bool = prBool;
static_pr.boolName = prBoolName;
static_pr.para = prPara;
static_pr.para[ PR_OFFSET ] = new Param( -100.0, Param.ABS_MS );
static_pr.para[ PR_MGAIN ] = new Param( -1.4, Param.DECIBEL_AMP );
static_pr.para[ PR_SGAIN ] = new Param( -17.0, Param.DECIBEL_AMP );
static_pr.para[ PR_CONVLENGTH ] = new Param( 5.0, Param.ABS_MS );
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, ggCallosumFile;
PathField[] ggInputs;
ParamField ggOffset, ggMGain, ggSGain, ggConvLength;
JCheckBox ggFlatSpect, ggZeroPhase;
Component[] ggGain;
ParamSpace[] spcOffset;
gui = new GUISupport();
con = gui.getGridBagConstraints();
con.insets = new Insets( 1, 2, 1, 2 );
// -------- Input-Gadgets --------
con.fill = GridBagConstraints.BOTH;
con.gridwidth = GridBagConstraints.REMAINDER;
gui.addLabel( new GroupLabel( "Waveform I/O", GroupLabel.ORIENT_HORIZONTAL,
GroupLabel.BRACE_NONE ));
ggInputFile = new PathField( PathField.TYPE_INPUTFILE + PathField.TYPE_FORMATFIELD,
"Select input file" );
ggInputFile.handleTypes( GenericFile.TYPES_SOUND );
con.gridwidth = 1;
con.weightx = 0.1;
gui.addLabel( new JLabel( "Input file", SwingConstants.RIGHT ));
con.gridwidth = GridBagConstraints.REMAINDER;
con.weightx = 0.9;
gui.addPathField( ggInputFile, GG_INPUTFILE, null );
ggCallosumFile = new PathField( PathField.TYPE_INPUTFILE + PathField.TYPE_FORMATFIELD,
"Select c.callosum file" );
ggCallosumFile.handleTypes( GenericFile.TYPES_SOUND );
con.gridwidth = 1;
con.weightx = 0.1;
gui.addLabel( new JLabel( "Corpus callosum", SwingConstants.RIGHT ));
con.gridwidth = GridBagConstraints.REMAINDER;
con.weightx = 0.9;
gui.addPathField( ggCallosumFile, GG_CALLOSUMFILE, 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$F0Schz$E" );
con.gridwidth = 1;
con.weightx = 0.1;
gui.addLabel( new JLabel( "Output file", SwingConstants.RIGHT ));
con.gridwidth = GridBagConstraints.REMAINDER;
con.weightx = 0.9;
gui.addPathField( ggOutputFile, GG_OUTPUTFILE, null );
gui.registerGadget( ggOutputFile.getTypeGadget(), GG_OUTPUTTYPE );
gui.registerGadget( ggOutputFile.getResGadget(), GG_OUTPUTRES );
ggGain = createGadgets( GGTYPE_GAIN );
con.weightx = 0.1;
con.gridwidth = 1;
gui.addLabel( new JLabel( "Gain", SwingConstants.RIGHT ));
con.weightx = 0.4;
gui.addParamField( (ParamField) ggGain[ 0 ], GG_GAIN, null );
con.weightx = 0.5;
con.gridwidth = GridBagConstraints.REMAINDER;
gui.addChoice( (JComboBox) ggGain[ 1 ], GG_GAINTYPE, null );
// -------- Settings --------
gui.addLabel( new GroupLabel( "Surgeon Settings", GroupLabel.ORIENT_HORIZONTAL,
GroupLabel.BRACE_NONE ));
spcOffset = new ParamSpace[ 2 ];
spcOffset[ 0 ] = Constants.spaces[ Constants.offsetMsSpace ];
spcOffset[ 1 ] = Constants.spaces[ Constants.offsetBeatsSpace ];
ggOffset = new ParamField( spcOffset );
con.weightx = 0.1;
con.gridwidth = 1;
gui.addLabel( new JLabel( "Callosum offset", SwingConstants.RIGHT ));
con.weightx = 0.4;
gui.addParamField( ggOffset, GG_OFFSET, null );
ggFlatSpect = new JCheckBox( "Flatten spect" );
con.weightx = 0.5;
con.gridwidth = GridBagConstraints.REMAINDER;
gui.addCheckbox( ggFlatSpect, GG_FLATSPECT, null );
ggMGain = new ParamField( Constants.spaces[ Constants.decibelAmpSpace ]);
con.weightx = 0.1;
con.gridwidth = 1;
gui.addLabel( new JLabel( "Mid. gain", SwingConstants.RIGHT ));
con.weightx = 0.4;
gui.addParamField( ggMGain, GG_MGAIN, null );
ggZeroPhase = new JCheckBox( "Zero phases" );
con.weightx = 0.5;
con.gridwidth = GridBagConstraints.REMAINDER;
gui.addCheckbox( ggZeroPhase, GG_ZEROPHASE, null );
ggSGain = new ParamField( Constants.spaces[ Constants.decibelAmpSpace ]);
con.weightx = 0.1;
con.gridwidth = 1;
gui.addLabel( new JLabel( "Side gain", SwingConstants.RIGHT ));
con.weightx = 0.4;
gui.addParamField( ggSGain, GG_SGAIN, null );
ggConvLength = new ParamField( Constants.spaces[ Constants.absMsSpace ]);
con.weightx = 0.1;
gui.addLabel( new JLabel( "Conv. length", SwingConstants.RIGHT ));
con.weightx = 0.4;
con.gridwidth = GridBagConstraints.REMAINDER;
gui.addParamField( ggConvLength, GG_CONVLENGTH, 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, ch, len, off;
long progOff, progLen;
float f1;
double d1, d2, d3, d4;
// io
AudioFile inF = null;
AudioFile outF = null;
AudioFile callF = null;
AudioFileDescr inStream = null;
AudioFileDescr outStream = null;
AudioFileDescr callStream = null;
FloatFile[] outFloatF = null;
File outTempFile[] = null;
int inChanNum, outChanNum, callChanNum;
// buffers
float[][] inBuf = null;
float[][] outBuf = null;
float[][] callBuf = null;
float[] fftBuf1 = null;
float[] fftBuf2 = null;
float[] fftBuf3 = null;
float[][] overBuf = null;
float[] convBuf1, convBuf2;
int inBufSize;
int[][] callChan = null;
float[][] callChanWeight = null;
double[][] phase = null;
boolean easyCallChan, easyPhase, newData;
int inLength, outLength, callLength, callOffset;
int preCall, postCall, frameSize, fftLength;
int framesRead, framesWritten;
Param ampRef = new Param( 1.0, Param.ABS_AMP ); // transform-Referenz
Param peakGain;
float gain = 1.0f; // gain abs amp
float mGain, sGain;
float maxAmp = 0.0f;
PathField ggOutput;
topLevel: try {
// ---- open input ----
// 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;
// callosum input
callF = AudioFile.openAsRead( new File( pr.text[ PR_CALLOSUMFILE ]));
callStream = callF.getDescr();
callChanNum = callStream.channels;
callLength = (int) callStream.length;
// this helps to prevent errors from empty files!
if( (callLength < 1) || (callChanNum < 1) ) throw new EOFException( ERR_EMPTY );
callOffset = ((pr.para[ PR_OFFSET ].value >= 0.0) ? 1 : -1) * (int)
(AudioFileDescr.millisToSamples( callStream, Math.abs( pr.para[ PR_OFFSET ].value)) + 0.5);
if( callOffset >= 0 ) {
callOffset = Math.min( callOffset, callLength );
callF.seekFrame( callOffset );
callLength -= callOffset;
preCall = 0;
} else {
callOffset = Math.max( callOffset, -inLength );
preCall = -callOffset;
}
callLength = Math.min( inLength - preCall, callLength );
postCall = inLength - preCall - callLength;
// .... check running ....
if( !threadRunning ) break topLevel;
outChanNum = Math.max( 2, inChanNum );
outLength = inLength;
if( callChanNum > outChanNum ) throw new EOFException( ERR_CHANNELS );
// System.out.println( "inLen "+inLength+"; preCall "+preCall+"; calllen "+callLength+"; postCall "+postCall );
frameSize = Math.max( pr.bool[ PR_FLATSPECT ] ? 128 : 32, (int)
(AudioFileDescr.millisToSamples( inStream, pr.para[ PR_CONVLENGTH ].value) + 0.5) );
// fftLength = frameSize+impulseResp.-1 auf 2er Potenz aufgerundet
i = frameSize * ((pr.bool[ PR_FLATSPECT ] || pr.bool[ PR_ZEROPHASE ]) ? 3 : 2) - 1;
for( fftLength = 2; fftLength < i; fftLength <<= 1 ) ;
// System.out.println( "frameLen "+frameSize+"; fftLen "+fftLength );
// ---- open 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;
progOff = 0;
progLen = (long) inLength + (long) callLength + outLength;
// 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 += outLength;
} else {
gain = (float) (Param.transform( pr.para[ PR_GAIN ], Param.ABS_AMP, ampRef, null )).value;
}
// .... check running ....
if( !threadRunning ) break topLevel;
// ---- further inits ----
inBufSize = 8192 + frameSize - 1;
inBufSize -= inBufSize % frameSize;
// System.out.println( "inBufSize "+inBufSize );
inBuf = new float[ inChanNum ][ inBufSize ];
callBuf = new float[ callChanNum ][ inBufSize ];
outBuf = new float[ outChanNum ][ inBufSize ];
fftBuf1 = new float[ fftLength + 2 ];
fftBuf2 = new float[ fftLength + 2 ];
fftBuf3 = new float[ fftLength + 2 ];
overBuf = new float[ outChanNum ][ frameSize ]; // for overlap-add convolution method
Util.clear( overBuf );
mGain = (float) ((Param.transform( pr.para[ PR_MGAIN ], Param.ABS_AMP, ampRef, null )).value * gain);
sGain = (float) ((Param.transform( pr.para[ PR_SGAIN ], Param.ABS_AMP, ampRef, null )).value * gain);
// System.out.println( "mGain "+mGain+"; sGain "+sGain );
easyCallChan = (callChanNum == 1) || (callChanNum == outChanNum);
if( !easyCallChan ) {
callChan = new int[ outChanNum ][ 2 ];
callChanWeight = new float[ outChanNum ][ 2 ];
// calc weights
for( ch = 0; ch < outChanNum; ch++ ) {
f1 = ((float) ch / (float) (outChanNum - 1)) * (callChanNum - 1);
callChan[ ch ][ 0 ] = (int) f1;
callChan[ ch ][ 1 ] = ((int) f1 + 1) % callChanNum; // last one just wrapped around w/ weight 0
f1 %= 1.0f;
callChanWeight[ ch ][ 0 ] = 1.0f - f1;
callChanWeight[ ch ][ 1 ] = f1;
}
}
easyPhase = outChanNum == 2; // rotation by 180 deg can be calc'ed without trigonometrics
if( !easyPhase ) {
phase = new double[ outChanNum ][ 2 ];
d1 = 0.0;
d2 = Constants.PI2 / outChanNum;
for( ch = 0; ch < outChanNum; ch++, d1 += d2 ) {
phase[ ch ][ 0 ] = Math.cos( d1 );
phase[ ch ][ 1 ] = Math.sin( d1 );
}
}
// ----==================== preCall ====================----
framesRead = 0;
framesWritten = 0;
while( threadRunning && (framesRead < preCall) ) {
len = Math.min( preCall - framesRead, inBufSize );
// ---- read ----
inF.readFrames( inBuf, 0, len );
for( ch = 0; ch < outChanNum; ch++ ) {
convBuf1 = outBuf[ ch ];
convBuf2 = inBuf[ ch % inChanNum ]; // either inChanNum == 1 or inChanNum == outChanNum
System.arraycopy( convBuf2, 0, convBuf1, 0, len );
Util.mult( convBuf1, 0, len, mGain );
}
framesRead += len;
progOff += len;
// .... progress ....
setProgression( (float) progOff / (float) progLen );
// ---- write ----
if( outFloatF != null ) { // unity gain > temp
for( ch = 0; ch < outChanNum; ch++ ) {
convBuf1 = outBuf[ ch ];
outFloatF[ ch ].writeFloats( convBuf1, 0, len );
}
} else { // abs gain > out
outF.writeFrames( outBuf, 0, len );
}
// check max amp
for( ch = 0; ch < outChanNum; ch++ ) {
convBuf1 = outBuf[ ch ];
for( i = 0; i < len; i++ ) {
f1 = Math.abs( convBuf1[ i ]);
if( f1 > maxAmp ) {
maxAmp = f1;
}
}
}
framesWritten += len;
progOff += len;
// .... progress ....
setProgression( (float) progOff / (float) progLen );
} // while framesRead < preCall
// ----==================== the real stuff ====================----
framesRead = 0;
framesWritten = 0;
while( threadRunning && (framesWritten < callLength) ) {
len = Math.min( callLength - framesRead, inBufSize );
// ---- read ----
inF.readFrames( inBuf, 0, len );
callF.readFrames( callBuf, 0, len );
framesRead += len;
progOff += len << 1;
// .... progress ....
setProgression( (float) progOff / (float) progLen );
// .... check running ....
if( !threadRunning ) break topLevel;
// zero pad
if( len < inBufSize ) {
for( ch = 0; ch < inChanNum; ch++ ) {
convBuf1 = inBuf[ ch ];
for( i = len; i < inBufSize; i++ ) {
convBuf1[ i ] = 0.0f;
}
}
for( ch = 0; ch < callChanNum; ch++ ) {
convBuf1 = callBuf[ ch ];
for( i = len; i < inBufSize; i++ ) {
convBuf1[ i ] = 0.0f;
}
}
}
// ---- the main part ----
for( off = 0; threadRunning && (off < len); off += frameSize ) {
for( ch = 0; ch < outChanNum; ch++ ) {
// step 1: copy mid part to outBuf
convBuf1 = outBuf[ ch ];
convBuf2 = inBuf[ ch % inChanNum ]; // either inChanNum == 1 or inChanNum == outChanNum
System.arraycopy( convBuf2, off, convBuf1, off, frameSize );
Util.mult( convBuf1, off, frameSize, mGain );
// System.out.println( "pre energy "+Filter.calcEnergy( convBuf1, off, frameSize ));
Util.add( overBuf[ ch ], 0, convBuf1, off, frameSize );
newData = !easyPhase;
// step 2: take FFT of input
if( (ch == 0) || (inChanNum > 1) ) {
System.arraycopy( convBuf2, off, fftBuf1, 0, frameSize );
Util.mult( fftBuf1, 0, frameSize, sGain );
for( i = frameSize; i < fftLength; i++ ) {
fftBuf1[ i ] = 0.0f; // zero pad
}
Fourier.realTransform( fftBuf1, fftLength, Fourier.FORWARD );
newData = true;
// System.out.println( "fftBuf1 energy "+(Filter.calcEnergy( fftBuf1, 0, fftLength + 2 ) / (2*fftLength)));
}
// step 3: take FFT of callosum
if( (ch == 0) || (callChanNum > 1) ) {
convBuf2 = pr.bool[ PR_FLATSPECT ] ? fftBuf3 : fftBuf2;
if( easyCallChan ) { // faster
System.arraycopy( callBuf[ ch ], off, convBuf2, 0, frameSize );
} else {
for( i = 0, j = off; i < frameSize; i++, j++ ) {
convBuf2[ i ] = callBuf[ callChan[ ch ][ 0 ]][ j ] * callChanWeight[ ch ][ 0 ] +
callBuf[ callChan[ ch ][ 1 ]][ j ] * callChanWeight[ ch ][ 1 ];
}
}
for( i = frameSize; i < fftLength; i++ ) {
convBuf2[ i ] = 0.0f; // zero pad
}
Fourier.realTransform( convBuf2, fftLength, Fourier.FORWARD );
if( pr.bool[ PR_FLATSPECT ]) {
flattenSpect( convBuf2, fftBuf2, fftLength + 2 );
}
if( pr.bool[ PR_ZEROPHASE ]) {
zeroPhase( fftBuf2, fftLength + 2 );
}
// System.out.println( "fftBuf2 energy "+(Filter.calcEnergy( convBuf2, 0, fftLength + 2 ) / (2*fftLength)));
newData = true;
}
// step 4: convolve + phase shift + ifft
if( newData ) {
Fourier.complexMult( fftBuf1, 0, fftBuf2, 0, fftBuf3, 0, fftLength + 2 );
if( ch > 0 ) { // phase
if( easyPhase ) { // easier: a+ib => -a-ib
for( i = 0; i < fftLength + 2; i++ ) {
fftBuf3[ i ] = -fftBuf3[ i ];
}
} else {
d1 = phase[ ch ][ 0 ];
d2 = phase[ ch ][ 1 ];
for( i = 0; i < fftLength + 2; ) {
d3 = fftBuf3[ i ];
d4 = fftBuf3[ i+1 ];
fftBuf3[ i++ ] = (float) (d3 * d1 - d4 * d2);
fftBuf3[ i++ ] = (float) (d3 * d2 + d4 * d1);
}
}
}
Fourier.realTransform( fftBuf3, fftLength, Fourier.INVERSE );
} else { // easyPhase constant data; take old IFFT and change sign
for( i = 0; i < fftLength + 2; i++ ) {
fftBuf3[ i ] = -fftBuf3[ i ];
}
}
// step 5: mix side to output + save overlap
Util.add( fftBuf3, 0, convBuf1, off, frameSize );
// System.out.println( "post energy "+Filter.calcEnergy( convBuf1, off, frameSize ));
System.arraycopy( fftBuf3, frameSize, overBuf[ ch ], 0, frameSize );
} // for outChanNum
} // for frames
// .... check running ....
if( !threadRunning ) break topLevel;
// ---- write ----
if( outFloatF != null ) { // unity gain > temp
for( ch = 0; ch < outChanNum; ch++ ) {
convBuf1 = outBuf[ ch ];
outFloatF[ ch ].writeFloats( convBuf1, 0, len );
}
} else { // abs gain > out
outF.writeFrames( outBuf, 0, len );
}
// check max amp
for( ch = 0; ch < outChanNum; ch++ ) {
convBuf1 = outBuf[ ch ];
for( i = 0; i < len; i++ ) {
f1 = Math.abs( convBuf1[ i ]);
if( f1 > maxAmp ) {
maxAmp = f1;
}
}
}
framesWritten += len;
progOff += len;
// .... progress ....
setProgression( (float) progOff / (float) progLen );
} // while framesRead < preCall
// ----==================== postCall ====================----
framesRead = 0;
framesWritten = 0;
off = 0; // don't forget to carry the remaining overlap!
while( threadRunning && (framesRead < postCall) ) {
len = Math.min( postCall - framesRead, inBufSize );
// ---- read ----
inF.readFrames( inBuf, 0, len );
for( ch = 0; ch < outChanNum; ch++ ) {
convBuf1 = outBuf[ ch ];
convBuf2 = inBuf[ ch % inChanNum ]; // either inChanNum == 1 or inChanNum == outChanNum
System.arraycopy( convBuf2, 0, convBuf1, 0, len );
Util.mult( convBuf1, 0, len, mGain );
if( off < frameSize ) {
Util.add( overBuf[ ch ], off, convBuf1, 0, Math.min( len, frameSize - off ));
}
}
framesRead += len;
off += len;
progOff += len;
// .... progress ....
setProgression( (float) progOff / (float) progLen );
// ---- write ----
if( outFloatF != null ) { // unity gain > temp
for( ch = 0; ch < outChanNum; ch++ ) {
convBuf1 = outBuf[ ch ];
outFloatF[ ch ].writeFloats( convBuf1, 0, len );
}
} else { // abs gain > out
outF.writeFrames( outBuf, 0, len );
}
// check max amp
for( ch = 0; ch < outChanNum; ch++ ) {
convBuf1 = outBuf[ ch ];
for( i = 0; i < len; i++ ) {
f1 = Math.abs( convBuf1[ i ]);
if( f1 > maxAmp ) {
maxAmp = f1;
}
}
}
framesWritten += len;
progOff += len;
// .... progress ....
setProgression( (float) progOff / (float) progLen );
} // while framesRead < postCall
// ----==================== normalize output ====================----
if( pr.intg[ PR_GAINTYPE ] == GAIN_UNITY ) {
peakGain = new Param( 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;
normalizeAudioFile( outFloatF, outF, outBuf, gain, 1.0f );
maxAmp *= gain;
for( ch = 0; ch < outChanNum; ch++ ) {
outFloatF[ ch ].cleanUp();
outFloatF[ ch ] = null;
outTempFile[ ch ].delete();
outTempFile[ ch ] = null;
}
}
// .... check running ....
if( !threadRunning ) break topLevel;
// ---- Finish ----
outF.close();
outF = null;
outStream = null;
callF.close();
callF = null;
callStream = null;
inF.close();
inF = null;
inStream = null;
outBuf = null;
inBuf = null;
callBuf = null;
// inform about clipping/ low level
handleClipping( maxAmp );
}
catch( IOException e1 ) {
setError( e1 );
}
catch( OutOfMemoryError e2 ) {
inStream = null; // make available to garbage collector
outStream = null;
callStream = null;
inBuf = null;
outBuf = null;
callBuf = null;
fftBuf1 = null;
fftBuf2 = null;
fftBuf3 = null;
overBuf = 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( callF != null ) {
callF.cleanUp();
callF = 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();
}
}
} // process()
// -------- private methods --------
/*
* Entfernt Formanten eines Spektrums; ein Moving-RMS der Laenge FFT-Size/32
* wird berechnet und zur Gesamtenergie ins Verhaeltnis gesetzt
*
* @param src Quell-Spectrum
* @param dest Ziel mit herausgerechneten Formanten
* @param length Spectrum Laenge (complex * 2)
*/
protected void flattenSpect( float[] src, float[] dest, int length )
{
int average = length >> 5; // 1/32 bandwidth i.e. 689Hz, 1378Hz etc.
int i, j, k;
double energy = 0.0;
double tot = 0.0;
double d1;
float f1;
for( i = 0, j = average >> 1; i < j; i++ ) { // initial right wing
energy += src[ i ] * src[ i ];
}
// System.out.println( "i = "+i+"; j = "+j+"; energy = "+energy );
for( j = 0; i < average; i++, j++ ) { // unvollstaendiger anfang
dest[ j ] = Math.max( 1e-15f, (float) (energy / i) );
energy += src[ i ] * src[ i ];
}
// System.out.println( "i = "+i+"; j = "+j+"; tot = "+energy );
tot = energy;
for( k = 0; i < length; i++, j++, k++ ) { // mitte
dest[ j ] = Math.max( 1e-15f, (float) (energy / average) );
d1 = src[ i ] * src[ i ];
energy = energy - src[ k ] * src[ k ] + d1; // moving average
tot += d1; // total accum.
}
tot /= (length >> 1);
// System.out.println( "i = "+i+"; j = "+j+"; k = "+k+"; energy = "+energy+"; tot = "+tot );
for( i = average; j < length; j++, i--, k++ ) { // unvollstaendiges ende
dest[ j ] = Math.max( 1e-15f, (float) (energy / i) );
energy -= src[ k ] * src[ k ];
}
// System.out.println( "i = "+i+"; j = "+j+"; k = "+k+"; energy = "+energy );
// envelope removal
for( i = 0; i < length; ) { // max +96 dB
f1 = Math.min( 63096.0f, (float) Math.sqrt( tot / (dest[ i ] + dest[ i+1 ])));
dest[ i ] = src[ i ] * f1; i++;
dest[ i ] = src[ i ] * f1; i++;
}
}
/*
* Entfernt Phase eines Spektrums (setzt alle auf Null)
*
* @param a Spectrum
* @param length Spectrum Laenge (complex * 2)
*/
protected void zeroPhase( float[] a, int length )
{
double re, im;
int i, j;
for( i = 0; i < length; ) {
j = i;
re = a[ i++ ];
im = a[ i ];
a[ j ] = (float) Math.sqrt( re*re + im*im );
a[ i++ ]= 0.0f;
}
}
}