/*
* SeekDestroyDlg.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.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.io.AudioFile;
import de.sciss.io.AudioFileDescr;
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 "really large scale" waveshaping.
*/
public class SeekDestroyDlg
extends ModulePanel {
// -------- private variables --------
// Properties (defaults)
private static final int PR_SHAPEFILE = 0; // pr.text
private static final int PR_OUTPUTFILE = 1;
private static final int PR_TRAJFILE = 2;
private static final int PR_OUTPUTTYPE = 0; // pr.intg
private static final int PR_OUTPUTRES = 1;
private static final int PR_SHAPE = 2;
private static final int PR_SIGMODE = 3;
private static final int PR_DCBLOCK = 0; // pr.bool
private static final int SHAPE_FILE = 0;
private static final int SHAPE_SINE = 1;
private static final int SHAPE_TRI = 2;
private static final int SHAPE_SQRT = 3;
private static final int SHAPE_SQR = 4;
private static final int SIGMODE_BICENTER = 0;
private static final int SIGMODE_BIWRAP = 1;
private static final int SIGMODE_UNI = 2;
private static final int SIGMODE_UNISIGN = 3;
private static final String PRN_SHAPEFILE = "ShapeFile";
private static final String PRN_OUTPUTFILE = "OutputFile";
private static final String PRN_TRAJFILE = "TrajFile";
private static final String PRN_OUTPUTTYPE = "OutputType";
private static final String PRN_OUTPUTRES = "OutputReso";
private static final String PRN_SHAPE = "Shape";
private static final String PRN_SIGMODE = "SigMode";
private static final String PRN_DCBLOCK = "DCBlock";
private static final String prText[] = { "", "", "" };
private static final String prTextName[] = { PRN_SHAPEFILE, PRN_OUTPUTFILE, PRN_TRAJFILE };
private static final int prIntg[] = { 0, 0, SHAPE_SINE, SIGMODE_BIWRAP };
private static final String prIntgName[] = { PRN_OUTPUTTYPE, PRN_OUTPUTRES, PRN_SHAPE, PRN_SIGMODE };
private static final boolean prBool[] = { false };
private static final String prBoolName[] = { PRN_DCBLOCK };
private static final int GG_SHAPEFILE = GG_OFF_PATHFIELD + PR_SHAPEFILE;
private static final int GG_OUTPUTFILE = GG_OFF_PATHFIELD + PR_OUTPUTFILE;
private static final int GG_TRAJFILE = GG_OFF_PATHFIELD + PR_TRAJFILE;
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_SHAPE = GG_OFF_CHOICE + PR_SHAPE;
private static final int GG_SIGMODE = GG_OFF_CHOICE + PR_SIGMODE;
private static final int GG_DCBLOCK = GG_OFF_CHECKBOX + PR_DCBLOCK;
private static PropertyArray static_pr = null;
private static Presets static_presets = null;
// -------- public methods --------
/**
* !! setVisible() bleibt dem Aufrufer ueberlassen
*/
public SeekDestroyDlg()
{
super( "Seek + Destroy" );
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.superPr = DocumentFrame.static_pr;
fillDefaultAudioDescr( static_pr.intg, PR_OUTPUTTYPE, PR_OUTPUTRES );
static_presets = new Presets( getClass(), static_pr.toProperties( true ));
}
presets = static_presets;
pr = (PropertyArray) static_pr.clone();
// -------- build GUI --------
GridBagConstraints con;
PathField ggShapeFile, ggOutputFile, ggTrajFile;
PathField[] ggInputs;
JComboBox ggShape, ggSigMode;
JCheckBox ggDCBlock;
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_SHAPE:
pr.intg[ ID - GG_OFF_CHOICE ] = ((JComboBox) e.getSource()).getSelectedIndex();
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 ));
ggTrajFile = new PathField( PathField.TYPE_INPUTFILE + PathField.TYPE_FORMATFIELD,
"Select input file" );
ggTrajFile.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( ggTrajFile, GG_TRAJFILE, null );
ggShapeFile = new PathField( PathField.TYPE_INPUTFILE + PathField.TYPE_FORMATFIELD,
"Select shape file" );
ggShapeFile.handleTypes( GenericFile.TYPES_SOUND );
con.gridwidth = 1;
con.weightx = 0.1;
gui.addLabel( new JLabel( "Shape file", SwingConstants.RIGHT ));
con.gridwidth = GridBagConstraints.REMAINDER;
con.weightx = 0.9;
gui.addPathField( ggShapeFile, GG_SHAPEFILE, 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 ] = ggTrajFile;
// ggInputs[ 1 ] = ggShapeFile;
ggOutputFile.deriveFrom( ggInputs, "$D0$F0Shp$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 );
// -------- Settings --------
gui.addLabel( new GroupLabel( "Shaper Settings", GroupLabel.ORIENT_HORIZONTAL,
GroupLabel.BRACE_NONE ));
ggShape = new JComboBox();
ggShape.addItem( "Soundfile" );
ggShape.addItem( "Sine" );
ggShape.addItem( "Triangle" );
ggShape.addItem( "Sqrt(x)" );
ggShape.addItem( "Square(x)" );
con.gridwidth = 1;
con.weightx = 0.1;
gui.addLabel( new JLabel( "Function", SwingConstants.RIGHT ));
con.weightx = 0.4;
gui.addChoice( ggShape, GG_SHAPE, il );
ggSigMode = new JComboBox();
ggSigMode.addItem( "Bipolar centered" );
ggSigMode.addItem( "Bipolar wrapped" );
ggSigMode.addItem( "Unipolar" );
ggSigMode.addItem( "Unipolar*sign(x)" );
con.gridwidth = 1;
con.weightx = 0.1;
gui.addLabel( new JLabel( "Signal mode", SwingConstants.RIGHT ));
con.gridwidth = GridBagConstraints.REMAINDER;
con.weightx = 0.4;
gui.addChoice( ggSigMode, GG_SIGMODE, il );
ggDCBlock = new JCheckBox();
con.gridwidth = 1;
con.weightx = 0.1;
gui.addLabel( new JLabel( "DC blocking", SwingConstants.RIGHT ));
con.gridwidth = GridBagConstraints.REMAINDER;
con.weightx = 0.4;
gui.addCheckbox( ggDCBlock, GG_DCBLOCK, 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, ch, len;
long progOff, progLen;
float f1;
double d1, d2, d3;
boolean b1;
// io
AudioFile shapeF = null;
AudioFile trajF = null;
AudioFile outF = null;
AudioFileDescr shapeStream = null;
AudioFileDescr trajStream = null;
AudioFileDescr outStream = null;
int trajChanNum, outChanNum;
int shapeChanNum = 0;
int[][] shapeChan = null;
int[][] trajChan = null;
float[][] shapeChanWeight = null;
float[][] trajChanWeight = null;
// buffers
int shapeMem = 0;
float[][] shapeBuf = null;
float[][] trajBuf = null;
float[][] outBuf = null;
float[][] dcMem;
float[] convBuf1, convBuf2;
// Smp Init
int trajLength, outLength;
int shapeLength = 0;
int framesRead, framesWritten;
int pass, passes, inOff;
int passLen = 0;
PathField ggOutput;
topLevel: try {
// ---- open input, output; init ----
// traj input
trajF = AudioFile.openAsRead( new File( pr.text[ PR_TRAJFILE ]));
trajStream = trajF.getDescr();
trajChanNum = trajStream.channels;
trajLength = (int) trajStream.length;
// this helps to prevent errors from empty files!
if( (trajLength < 1) || (trajChanNum < 1) ) throw new EOFException( ERR_EMPTY );
// .... check running ....
if( !threadRunning ) break topLevel;
outChanNum = trajChanNum;
passes = 1;
shapeLength = 0;
// shape input
if( pr.intg[ PR_SHAPE ] == SHAPE_FILE ) {
shapeF = AudioFile.openAsRead( new File( pr.text[ PR_SHAPEFILE ]));
shapeStream = shapeF.getDescr();
shapeChanNum = shapeStream.channels;
shapeLength = (int) shapeStream.length;
// this helps to prevent errors from empty files!
if( (shapeLength < 1) || (shapeChanNum < 1) ) throw new EOFException( ERR_EMPTY );
outChanNum = Math.max( shapeChanNum, trajChanNum );
shapeChan = new int[ outChanNum ][ 2 ];
shapeChanWeight = new float[ outChanNum ][ 2 ];
trajChan = new int[ outChanNum ][ 2 ];
trajChanWeight = new float[ outChanNum ][ 2 ];
// 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)) * (shapeChanNum - 1);
shapeChan[ ch ][ 0 ] = (int) f1;
shapeChan[ ch ][ 1 ] = (int) f1 + 1;
f1 %= 1.0f;
shapeChanWeight[ ch ][ 0 ] = 1.0f - f1;
shapeChanWeight[ ch ][ 1 ] = f1;
}
if( trajChanNum == 1 ) {
trajChan[ ch ][ 0 ] = 0;
trajChan[ ch ][ 1 ] = 0;
trajChanWeight[ ch ][ 0 ] = 1.0f;
trajChanWeight[ ch ][ 1 ] = 0.0f;
} else {
f1 = ((float) ch / (float) (outChanNum - 1)) * (trajChanNum - 1);
trajChan[ ch ][ 0 ] = (int) f1;
trajChan[ ch ][ 1 ] = (int) f1 + 1;
f1 %= 1.0f;
trajChanWeight[ ch ][ 0 ] = 1.0f - f1;
trajChanWeight[ ch ][ 1 ] = f1;
}
}
// take 75% of free memory, divide by sizeof( float ), divide by shapeChanNum
shapeMem = (int) ((Runtime.getRuntime().freeMemory() >> 4) * 3 / shapeChanNum);
//System.out.println( "inpMem"+ shapeMem+"; fltSize "+fltSize+"; dataLen "+dataLen+"; shapeLength "+shapeLength+"; totalIn "+totalInSamples );
//shapeMem = 1024;
//outF.setBufSize( 1 );
if( shapeMem < 1024 ) throw new OutOfMemoryError( ERR_MEMORY );
if( shapeMem > shapeLength ) {
shapeMem = shapeLength;
}
passes = (shapeLength + shapeMem - 1) / shapeMem;
}
// .... check running ....
if( !threadRunning ) break topLevel;
// output
ggOutput = (PathField) gui.getItemObj( GG_OUTPUTFILE );
if( ggOutput == null ) throw new IOException( ERR_MISSINGPROP );
outStream = new AudioFileDescr( trajStream );
ggOutput.fillStream( outStream );
outStream.channels = outChanNum;
outF = AudioFile.openAsWrite( outStream );
outLength = trajLength;
// .... check running ....
if( !threadRunning ) break topLevel;
//System.out.println( passes+" passes shapeMem "+shapeMem +" frames" );
progOff = 0;
progLen = (long) trajLength * passes + shapeLength + (long) outLength * passes;
// buffers
if( shapeF != null ) {
shapeBuf = new float[ shapeChanNum+1 ][]; // +1 to avoid arrayindex-out-of-bounds
for( ch = 0; ch < shapeChanNum; ch++ ) {
shapeBuf[ ch ] = new float[ shapeMem ];
}
shapeBuf[ shapeChanNum ] = shapeBuf[ 0 ]; // will always be zero-weighted so content is unimportant
}
trajBuf = new float[ trajChanNum+1 ][];
for( ch = 0; ch < trajChanNum; ch++ ) {
trajBuf[ ch ] = new float[ 8192 ];
}
trajBuf[ trajChanNum ] = trajBuf[ 0 ];
outBuf = new float[ outChanNum ][ 8192 ];
// DC block init
dcMem = new float[ outChanNum ][ 2 ];
for( ch = 0; ch < outChanNum; ch++ ) {
dcMem[ ch ][ 0 ]= 0.0f;
dcMem[ ch ][ 1 ]= 0.0f;
}
// ----==================== da massacre ====================----
for( pass = 0, inOff = 0; threadRunning && (pass < passes); pass++, inOff += shapeMem ) {
framesWritten = 0;
outF.seekFrame( 0 );
trajF.seekFrame( 0 );
// ---- step 1: read input portion ----
if( shapeF != null ) {
shapeF.seekFrame( inOff );
passLen = Math.min( shapeLength - inOff, shapeMem );
// inStop = inOff + passLen;
for( framesRead = 0; threadRunning && (framesRead < passLen); ) {
len = Math.min( 8192, passLen - framesRead );
shapeF.readFrames( shapeBuf, framesRead, len );
framesRead += len;
progOff += len;
// .... progress ....
setProgression( (float) progOff / (float) progLen );
}
// .... check running ....
if( !threadRunning ) break topLevel;
}
// ---- step 2: read traj and calc output for available input portion ----
for( framesRead = 0; threadRunning && (framesRead < trajLength); ) {
len = Math.min( 8192, trajLength - framesRead );
trajF.readFrames( trajBuf, 0, len );
framesRead += len;
progOff += len;
// .... progress ....
setProgression( (float) progOff / (float) progLen );
// .... check running ....
if( !threadRunning ) break topLevel;
// ---------------------------- shape file -------------------------
if( shapeF != null ) {
// read output that was generated in the recent pass
b1 = (pass == 0); // always update in first pass, otherwise only if sth. changes
if( !b1 ) {
outF.readFrames( outBuf, 0, len );
} else { // all zero in first pass
for( ch = 0; ch < outChanNum; ch++ ) {
convBuf1 = outBuf[ ch ];
for( i = 0; i < len; i++ ) {
convBuf1[ i ] = 0.0f;
}
}
}
// update output for available input portion
for( ch = 0; ch < outChanNum; ch++ ) {
convBuf1 = outBuf[ ch ];
for( i = 0; i < len; i++ ) {
d1 = trajBuf[ trajChan[ ch ][ 0 ]][ i ] * trajChanWeight[ ch ][ 0 ] +
trajBuf[ trajChan[ ch ][ 1 ]][ i ] * trajChanWeight[ ch ][ 1 ];
j = (int) (Math.min( 1.0, Math.abs( d1 )) * (shapeLength - 1)) - inOff;
if( (j >= 0) && (j < passLen) ) {
f1 = shapeBuf[ shapeChan[ ch ][ 0 ]][ j ] * shapeChanWeight[ ch ][ 0 ] +
shapeBuf[ shapeChan[ ch ][ 1 ]][ j ] * shapeChanWeight[ ch ][ 1 ];
convBuf1[ i ] = f1;
b1 = true; // needs rewrite
}
}
}
// write output
if( b1 ) {
outF.seekFrame( framesWritten ); // go back to last read pos
outF.writeFrames( outBuf, 0, len ); // and replace content
}
// ---------------------------- internal function -------------------------
} else {
for( ch = 0; ch < outChanNum; ch++ ) {
convBuf1 = outBuf[ ch ];
convBuf2 = trajBuf[ ch ];
switch( pr.intg[ PR_SHAPE ]) {
case SHAPE_SINE: // ........ sine(x) ........
switch( pr.intg[ PR_SIGMODE ]) {
case SIGMODE_BICENTER:
d2 = -Math.PI;
break;
case SIGMODE_UNI:
for( i = 0; i < len; i++ ) {
convBuf2[ i ] = Math.abs( convBuf2[ i ]);
}
// THRU
case SIGMODE_BIWRAP:
default: // case SIGMODE_UNISIGN:
d2 = Constants.PI2;
break;
}
for( i = 0; i < len; i++ ) {
d1 = convBuf2[ i ];
convBuf1[ i ] = (float) Math.sin( d1 * d2 );
}
break;
case SHAPE_TRI: // ........ tri(x) ........
switch( pr.intg[ PR_SIGMODE ]) {
case SIGMODE_BICENTER:
d2 = -0.5;
break;
case SIGMODE_UNI:
for( i = 0; i < len; i++ ) {
convBuf2[ i ] = Math.abs( convBuf2[ i ]);
}
// THRU
case SIGMODE_BIWRAP:
default: // case SIGMODE_UNISIGN:
d2 = 1.0;
break;
}
for( i = 0; i < len; i++ ) {
d1 = convBuf2[ i ];
j = (d1 >= 0.0) ? 2 : -2;
d3 = (d1 * d2 * j) % 2.0;
if( d3 < 0.5 ) {
// d3 *= 1.0;
} else if( d3 < 1.5 ) {
d3 = 1.0 - d3;
} else {
d3 -= 2.0;
}
convBuf1[ i ] = (float) (d3 * j);
}
break;
case SHAPE_SQRT: // ........ sqrt(x) ........
// XXX
throw new IOException( "Sqrt not yet implemented!" );
// break;
case SHAPE_SQR: // ........ sqr(x) ........
switch( pr.intg[ PR_SIGMODE ]) {
case SIGMODE_BICENTER:
for( i = 0; i < len; i++ ) {
d1 = convBuf2[ i ];
convBuf1[ i ] = (float) (d1 * d1 * 2 - 1.0f);
}
break;
case SIGMODE_UNI:
for( i = 0; i < len; i++ ) {
d1 = convBuf2[ i ];
d1 = Math.abs( d1 + 1.0 ) / 2;
convBuf1[ i ] = (float) (d1 * d1);
}
break;
case SIGMODE_BIWRAP:
for( i = 0; i < len; i++ ) {
d1 = convBuf2[ i ];
d1 = 1.0 - Math.abs( d1 ) * 2;
convBuf1[ i ] = (float) (d1 * d1 * 2 - 1.0f);
}
break;
case SIGMODE_UNISIGN:
for( i = 0; i < len; i++ ) {
d1 = convBuf2[ i ];
j = (d1 >= 0.0) ? 1 : -1;
d1 = Math.abs( d1 + 1.0 ) / 2;
convBuf1[ i ] = (float) (d1 * d1 * j);
}
break;
}
break;
}
// ---- remove DC ---- adapted from CSound
if( pr.bool[ PR_DCBLOCK ]) {
convBuf2 = dcMem[ ch ];
for( i = 0; i < len; i++ ) {
f1 = convBuf1[ i ]; // X1
convBuf1[ i ] = f1 - convBuf2[ 0 ] + 0.99f * convBuf2[ 1 ]; // Y1 = X1-X0+Y0*gain
convBuf2[ 0 ] = f1; // next X0
convBuf2[ 1 ] = convBuf1[ i ]; // next Y0
}
}
} // for outChanNum
outF.writeFrames( outBuf, 0, len ); // and replace content
}
// ----------------------------
framesWritten += len;
progOff += len;
// .... progress ....
setProgression( (float) progOff / (float) progLen );
} // for trajLength
} // for passes
// .... check running ....
if( !threadRunning ) break topLevel;
// ---- Finish ----
outF.close();
outF = null;
outStream = null;
if( shapeF != null ) {
shapeF.close();
shapeF = null;
shapeStream = null;
shapeBuf = null;
}
trajF.close();
trajF = null;
trajStream = null;
outBuf = null;
trajBuf = null;
}
catch( IOException e1 ) {
setError( e1 );
}
catch( OutOfMemoryError e2 ) {
shapeStream = null;
trajStream = null;
outStream = null;
shapeBuf = null;
outBuf = null;
trajBuf = null;
convBuf1 = null;
convBuf2 = null;
System.gc();
setError( new Exception( ERR_MEMORY ));
}
// ---- cleanup (topLevel) ----
if( shapeF != null ) {
shapeF.cleanUp();
shapeF = null;
}
if( trajF != null ) {
trajF.cleanUp();
trajF = null;
}
if( outF != null ) {
outF.cleanUp();
outF = null;
}
} // process()
// -------- private methods --------
protected void reflectPropertyChanges()
{
super.reflectPropertyChanges();
Component c;
c = gui.getItemObj( GG_SHAPEFILE );
if( c != null ) {
c.setEnabled( pr.intg[ PR_SHAPE ] == SHAPE_FILE );
}
c = gui.getItemObj( GG_DCBLOCK );
if( c != null ) {
c.setEnabled( pr.intg[ PR_SHAPE ] != SHAPE_FILE );
}
}
}