/*
* TrajectoryDlg.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.io.ImageFile;
import de.sciss.fscape.io.ImageStream;
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.Param;
import de.sciss.fscape.util.ParamSpace;
import de.sciss.fscape.util.Util;
import de.sciss.io.AudioFile;
import de.sciss.io.AudioFileDescr;
import javax.swing.*;
import java.awt.*;
import java.io.EOFException;
import java.io.File;
import java.io.IOException;
/**
* Playing around trying to generate
* images from sound, was never finished.
*/
public class TrajectoryDlg
extends ModulePanel {
// -------- private variables --------
// Properties (defaults)
private static final int PR_INPUTFILE1 = 0; // pr.text
private static final int PR_INPUTFILE2 = 1;
private static final int PR_OUTPUTFILE = 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_ZOOM = 3;
private static final int PR_DECAY = 4;
private static final int PR_GAIN = 0; // pr.para
private static final int PR_LIMITTHRESH = 1;
private static final int PR_IMGWIDTH = 2;
private static final int PR_IMGHEIGHT = 3;
private static final int PR_LENGTH = 4;
private static final int PR_OFFSET = 5;
private static final String PRN_INPUTFILE1 = "InputFile1";
private static final String PRN_INPUTFILE2 = "InputFile2";
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_LIMITTHRESH = "LimitTresh";
private static final String PRN_IMGWIDTH = "ImgWidth";
private static final String PRN_IMGHEIGHT = "ImgHeight";
private static final String PRN_LENGTH = "Length";
private static final String PRN_OFFSET = "Offset";
private static final String PRN_ZOOM = "Zoom";
private static final String PRN_DECAY = "Decay";
private static final String prText[] = { "", "", "" };
private static final String prTextName[] = { PRN_INPUTFILE1, PRN_INPUTFILE2, PRN_OUTPUTFILE };
private static final int prIntg[] = { 0, 0, GAIN_UNITY, 2, 3 };
private static final String prIntgName[] = { PRN_OUTPUTTYPE, PRN_OUTPUTRES, PRN_GAINTYPE, PRN_ZOOM, PRN_DECAY };
private static final Param prPara[] = { null, null, null, null, null, null };
private static final String prParaName[] = { PRN_GAIN, PRN_LIMITTHRESH, PRN_IMGWIDTH, PRN_IMGHEIGHT, PRN_LENGTH, PRN_OFFSET };
private static final int GG_INPUTFILE1 = GG_OFF_PATHFIELD + PR_INPUTFILE1;
private static final int GG_INPUTFILE2 = GG_OFF_PATHFIELD + PR_INPUTFILE2;
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_GAINTYPE = GG_OFF_CHOICE + PR_GAINTYPE;
private static final int GG_ZOOM = GG_OFF_CHOICE + PR_ZOOM;
private static final int GG_DECAY = GG_OFF_CHOICE + PR_DECAY;
private static final int GG_GAIN = GG_OFF_PARAMFIELD + PR_GAIN;
private static final int GG_LIMITTHRESH = GG_OFF_PARAMFIELD + PR_LIMITTHRESH;
private static final int GG_IMGWIDTH = GG_OFF_PARAMFIELD + PR_IMGWIDTH;
private static final int GG_IMGHEIGHT = GG_OFF_PARAMFIELD + PR_IMGHEIGHT;
private static final int GG_LENGTH = GG_OFF_PARAMFIELD + PR_LENGTH;
private static final int GG_OFFSET = GG_OFF_PARAMFIELD + PR_OFFSET;
private static PropertyArray static_pr = null;
private static Presets static_presets = null;
// -------- public methods --------
/**
* !! setVisible() bleibt dem Aufrufer ueberlassen
*/
public TrajectoryDlg()
{
super( "Trajectory Image Generator" );
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_LIMITTHRESH ]= new Param( 0.0, Param.DECIBEL_AMP );
static_pr.para[ PR_IMGWIDTH ] = new Param( 360.0, Param.NONE );
static_pr.para[ PR_IMGHEIGHT ] = new Param( 284.0, Param.NONE );
static_pr.para[ PR_LENGTH ] = new Param( 100.0, Param.FACTOR_TIME );
static_pr.para[ PR_OFFSET ] = new Param( 0.0, Param.OFFSET_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 ggInputFile1, ggInputFile2, ggOutputFile;
Component[] ggGain;
PathField[] ggInputs;
ParamField ggLimitThresh, ggImgWidth, ggImgHeight, ggLength, ggOffset;
ParamSpace spcLimitThresh, spcImgDim;
ParamSpace[] spcOffset, spcLength;
JComboBox ggZoom, ggDecay;
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 ));
ggInputFile1 = new PathField( PathField.TYPE_INPUTFILE + PathField.TYPE_FORMATFIELD,
"Select input file" );
ggInputFile1.handleTypes( GenericFile.TYPES_SOUND );
con.gridwidth = 1;
con.weightx = 0.1;
gui.addLabel( new JLabel( "Input file 1", SwingConstants.RIGHT ));
con.gridwidth = GridBagConstraints.REMAINDER;
con.weightx = 0.9;
gui.addPathField( ggInputFile1, GG_INPUTFILE1, null );
ggInputFile2 = new PathField( PathField.TYPE_INPUTFILE + PathField.TYPE_FORMATFIELD,
"Select input file" );
ggInputFile2.handleTypes( GenericFile.TYPES_SOUND );
con.gridwidth = 1;
con.weightx = 0.1;
gui.addLabel( new JLabel( "Input file 2", SwingConstants.RIGHT ));
con.gridwidth = GridBagConstraints.REMAINDER;
con.weightx = 0.9;
gui.addPathField( ggInputFile2, GG_INPUTFILE2, null );
ggOutputFile = new PathField( PathField.TYPE_OUTPUTFILE + PathField.TYPE_FORMATFIELD +
PathField.TYPE_RESFIELD, "Select output file" );
ggOutputFile.handleTypes( GenericFile.TYPES_IMAGE );
ggInputs = new PathField[ 2 ];
ggInputs[ 0 ] = ggInputFile1;
ggInputs[ 1 ] = ggInputFile2;
ggOutputFile.deriveFrom( ggInputs, "$D0$B0Trj$B1$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 );
spcLimitThresh = new ParamSpace( Constants.minDecibel, 0.0, 0.1, Param.DECIBEL_AMP );
ggLimitThresh = new ParamField( spcLimitThresh );
con.weightx = 0.1;
con.gridwidth = 1;
gui.addLabel( new JLabel( "Limit.thresh.", SwingConstants.RIGHT ));
con.weightx = 0.233;
con.gridwidth = GridBagConstraints.REMAINDER;
gui.addParamField( ggLimitThresh, GG_LIMITTHRESH, null );
// -------- Settings-Gadgets --------
con.fill = GridBagConstraints.BOTH;
con.gridwidth = GridBagConstraints.REMAINDER;
gui.addLabel( new GroupLabel( "Trajectory Settings", GroupLabel.ORIENT_HORIZONTAL,
GroupLabel.BRACE_NONE ));
spcImgDim = new ParamSpace( 1.0, 32768.0, 1.0, Param.NONE );
ggImgWidth = new ParamField( spcImgDim );
con.gridwidth = 1;
con.weightx = 0.1;
gui.addLabel( new JLabel( "Width [pixels]", SwingConstants.RIGHT ));
con.weightx = 0.233;
gui.addParamField( ggImgWidth, GG_IMGWIDTH, null );
ggImgHeight = new ParamField( spcImgDim );
con.weightx = 0.1;
gui.addLabel( new JLabel( "Height [pixels]", SwingConstants.RIGHT ));
con.weightx = 0.233;
con.gridwidth = GridBagConstraints.REMAINDER;
gui.addParamField( ggImgHeight, GG_IMGHEIGHT, null );
spcOffset = new ParamSpace[ 3 ];
spcOffset[0] = Constants.spaces[ Constants.offsetMsSpace ];
spcOffset[1] = Constants.spaces[ Constants.offsetBeatsSpace ];
spcOffset[2] = Constants.spaces[ Constants.offsetTimeSpace ];
spcLength = new ParamSpace[ 4 ];
spcLength[0] = Constants.spaces[ Constants.absMsSpace ];
spcLength[1] = Constants.spaces[ Constants.absBeatsSpace ];
spcLength[2] = Constants.spaces[ Constants.offsetTimeSpace ];
spcLength[3] = Constants.spaces[ Constants.factorTimeSpace ];
ggOffset = new ParamField( spcOffset );
con.gridwidth = 1;
con.weightx = 0.1;
gui.addLabel( new JLabel( "Traj. Offset", SwingConstants.RIGHT ));
con.weightx = 0.233;
gui.addParamField( ggOffset, GG_OFFSET, null );
ggLength = new ParamField( spcLength );
con.weightx = 0.1;
gui.addLabel( new JLabel( "Traj. Length", SwingConstants.RIGHT ));
con.weightx = 0.233;
con.gridwidth = GridBagConstraints.REMAINDER;
gui.addParamField( ggLength, GG_LENGTH, null );
ggZoom = new JComboBox();
for( int i = 0; i < 4; i++ ) {
ggZoom.addItem( (1 << i) + "x" );
}
con.gridwidth = 1;
con.weightx = 0.1;
gui.addLabel( new JLabel( "Zoom factor", SwingConstants.RIGHT ));
con.weightx = 0.233;
gui.addChoice( ggZoom, GG_ZOOM, null );
ggDecay = new JComboBox();
for( int i = -3; i <= 3; i++ ) {
ggDecay.addItem( String.valueOf( Math.pow( 2, i )));
}
con.weightx = 0.1;
gui.addLabel( new JLabel( "Decay", SwingConstants.RIGHT ));
con.weightx = 0.233;
con.gridwidth = GridBagConstraints.REMAINDER;
gui.addChoice( ggDecay, GG_DECAY, 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 h, i, j, k, m, n, p;
int len, ch;
long progOff, progLen;
float f1;
// io
AudioFile sndF1 = null;
AudioFile sndF2 = null;
ImageFile imgF = null;
AudioFileDescr sndStream1 = null;
AudioFileDescr sndStream2 = null;
ImageStream imgStream = null;
float[][] inBuf1, inBuf2;
float[] convBuf1, convBuf2;
byte[] row = null; // raw image (RGB interleaved)
float[][] imgBuf = null; // full image
// Synthesize
float gain = 1.0f; // gain abs amp
Param ampRef = new Param( 1.0, Param.ABS_AMP ); // transform-Referenz
// Smp Init
int inLength, inChanNum1, inChanNum2;
int framesRead;
int width, height, frameSize;
int bitsPerSmp = 8;
int outChanNum = 1; // XXX
float maxAmp = 0.0f;
PathField ggOutput;
int trajLen;
float cx, cy, rx, ry, tx, ty, wx1, wy1, wx2, wy2;
int txi, tyi;
float limit, compr, over;
int zoom = 1 << pr.intg[ PR_ZOOM ];
float decay = (float) Math.pow( 2, pr.intg[ PR_DECAY ] - 3 );
topLevel:
try {
// ---- open input, output; init ----
// input
sndF1 = AudioFile.openAsRead( new File( pr.text[ PR_INPUTFILE1 ]));
sndStream1 = sndF1.getDescr();
inChanNum1 = sndStream1.channels;
inLength = (int) sndStream1.length;
if( (inLength * inChanNum1) < 1 ) throw new EOFException( ERR_EMPTY );
sndF2 = AudioFile.openAsRead( new File( pr.text[ PR_INPUTFILE2 ]));
sndStream2 = sndF2.getDescr();
inChanNum2 = sndStream2.channels;
inLength = (int) Math.min( inLength, sndStream2.length );
if( (inLength * inChanNum2) < 1 ) throw new EOFException( ERR_EMPTY );
// .... check running ....
if( !threadRunning ) break topLevel;
// output
width = (int) pr.para[ PR_IMGWIDTH ].value;
height = (int) pr.para[ PR_IMGHEIGHT ].value;
frameSize = width * height;
rx = (float) (width - 1) / 2;
ry = (float) (height - 1) / 2;
cx = rx;
cy = ry;
rx *= zoom / (float) Constants.ln2;
ry *= zoom / (float) Constants.ln2;
j = Math.max( 0, Math.min( inLength - 1, (int) (AudioFileDescr.millisToSamples( sndStream1,
(Param.transform( pr.para[ PR_OFFSET ], Param.ABS_MS,
new Param( 0.0, Param.ABS_MS ), null )).value) + 0.5)));
sndF1.seekFrame( j );
sndF2.seekFrame( j );
trajLen = Math.min( inLength - j, (int) (AudioFileDescr.millisToSamples( sndStream1,
(Param.transform( pr.para[ PR_LENGTH ], Param.ABS_MS,
new Param( AudioFileDescr.samplesToMillis( sndStream1, inLength ), Param.ABS_MS ),
null )).value) + 0.5) );
ggOutput = (PathField) gui.getItemObj( GG_OUTPUTFILE );
if( ggOutput == null ) throw new IOException( ERR_MISSINGPROP );
imgF = new ImageFile( pr.text[ PR_OUTPUTFILE ], GenericFile.MODE_OUTPUT | ggOutput.getType() );
imgStream = new ImageStream();
imgStream.bitsPerSmp = 8; // ??? fillStream might not work correctly?
ggOutput.fillStream( imgStream );
imgStream.width = width;
imgStream.height = height;
imgStream.smpPerPixel = outChanNum;
bitsPerSmp = imgStream.bitsPerSmp;
imgF.initWriter( imgStream );
row = imgF.allocRow();
imgBuf = new float[ outChanNum ][ frameSize + 1];
inBuf1 = new float[ inChanNum1 ][ 8192 ];
inBuf2 = new float[ inChanNum2 ][ 8192 ];
// Util.clear( imgBuf );
// .... check running ....
if( !threadRunning ) break topLevel;
progOff = 0;
progLen = (long) trajLen * 2 + height;
// normalization requires temp files
if( pr.intg[ PR_GAINTYPE ] != GAIN_UNITY ) {
gain = (float) (Param.transform( pr.para[ PR_GAIN ], Param.ABS_AMP, ampRef, null )).value;
}
limit = (float) (Param.transform( pr.para[ PR_LIMITTHRESH ], Param.ABS_AMP, ampRef, null )).value;
if( limit < 1.0f ) {
compr = Math.min( 1.0f, (1.0f - limit) / (1.0f / limit - limit) );
gain /= limit;
} else {
compr = 0.0f;
}
// =================== CORE ===================
framesRead = 0;
convBuf1 = inBuf1[0];
convBuf2 = inBuf2[0];
k = 0;
//System.out.println( decay );
while( threadRunning && framesRead < trajLen ) {
len = Math.min( 8192, trajLen - framesRead );
sndF1.readFrames( inBuf1, 0, len );
sndF2.readFrames( inBuf2, 0, len );
progOff += len;
framesRead += len;
// .... progress ....
setProgression( (float) progOff / (float) progLen );
// .... check running ....
if( !threadRunning ) break topLevel;
trajLp: for( i = 0; i < len; i++, k++ ) {
f1 = Math.min( 1.0f, Math.max( -1.0f, convBuf1[ i ]));
j = f1 < 0f ? -1 : 1;
// tx = j * (float) Math.pow( f1 * f1, 0.25 ) * rx + cx;
// tx = j * (float) Math.sqrt( Math.log( Math.abs( f1 ) + 1)) * rx + cx;
tx = j * (float) Math.log( Math.abs( f1 ) + 1) * rx + cx;
f1 = Math.min( 1.0f, Math.max( -1.0f, convBuf2[ i ]));
j = f1 < 0f ? -1 : 1;
// ty = j * (float) Math.pow( f1 * f1, 0.25 ) * ry + cy;
// ty = j * (float) Math.sqrt( Math.log( Math.abs( f1 ) + 1)) * ry + cy;
ty = j * (float) Math.log( Math.abs( f1 ) + 1) * ry + cy;
wx2 = tx % 1.0f;
wy2 = ty % 1.0f;
wx1 = 1.0f - wx2;
wy1 = 1.0f - wy2;
txi = (int) tx;
tyi = (int) ty;
if( (tx < 0) || (txi > width - 2) || (ty < 0) || (tyi > height - 2) ) continue trajLp;
tyi *= width;
f1 = (float) Math.pow( (float) k / (float) trajLen, decay );
wx1 *= f1;
wx2 *= f1;
imgBuf[ 0 ][ txi + tyi ] += wx1 * wy1;
imgBuf[ 0 ][ txi+1 + tyi ] += wx2 * wy1;
imgBuf[ 0 ][ txi + tyi+width ] += wx1 * wy2;
imgBuf[ 0 ][ txi+1 + tyi+width ] += wx2 * wy2;
}
progOff += len;
// .... progress ....
setProgression( (float) progOff / (float) progLen );
// .... check running ....
if( !threadRunning ) break topLevel;
}
sndF1.close();
sndF1 = null;
sndF2.close();
sndF2 = null;
for( ch = 0; ch < outChanNum; ch++ ) {
for( i = 0; i < frameSize; i++ ) {
f1 = imgBuf[ ch ][ i ];
if( f1 > maxAmp ) {
maxAmp = f1;
}
}
}
if( pr.intg[ PR_GAINTYPE ] == GAIN_UNITY ) {
gain = (float) (Param.transform( pr.para[ PR_GAIN ], Param.ABS_AMP, new Param( 1 / maxAmp, Param.ABS_AMP ), null )).value;
if( limit < 1.0f ) {
gain /= limit;
}
}
// ============================== gain ==============================
for( ch = 0; ch < outChanNum; ch++ ) {
convBuf1 = imgBuf[ ch ];
if( gain != 1.0f ) {
Util.mult( imgBuf[ ch ], 0, frameSize, gain );
}
for( i = 0, n = 0; i < height; i++, n += width ) {
for( j = 0, k = n; j < width; j++, k++ ) {
f1 = convBuf1[ k ];
over = f1 - limit;
if( over > 0.0f ) {
f1 = limit + over * compr;
}
convBuf1[ k ] = f1;
}
}
}
// .... check running ....
if( !threadRunning ) break topLevel;
// ============================== write image ==============================
for( i = 0, n = 0; threadRunning && (i < height); i++, n += width ) {
for( ch = 0; ch < outChanNum; ch++ ) {
// ---------- interlace raw image ----------
convBuf1 = imgBuf[ ch ];
if( bitsPerSmp == 8 ) { // interleave, scale to integers
for( j = 0, m = n, k = ch; j < width; j++, m++, k += outChanNum ) {
row[ k ] = (byte) (convBuf1[ m ] * 0xFF);
// row[ k ] = (byte) ((convBuf1[ m ] + 1.0f) * 0x80);
}
} else { // 16 bit
for( j = 0, m = n, k = ch; j < width; j++, m++, k += outChanNum ) {
h = k << 1;
p = (int) (convBuf1[ m ] * 0xFFFF);
// p = (int) ((convBuf1[ m ] + 1.0f) * 0x8000);
row[ h++ ] = (byte) (p >> 8);
row[ h ] = (byte) p;
}
}
} // for channels
imgF.writeRow( row );
progOff++;
// .... progress ....
setProgression( (float) progOff / (float) progLen );
} // for rows
// .... check running ....
if( !threadRunning ) break topLevel;
// ---- cleanup ----
imgF.close();
imgF = null;
// inform about clipping/ low level
// maxAmp *= gain;
// handleClipping( maxAmp );
}
catch( IOException e1 ) {
setError( e1 );
}
catch( OutOfMemoryError e2 ) {
sndStream1 = null;
sndStream2 = null;
imgStream = null;
inBuf1 = null;
inBuf2 = null;
imgBuf = null;
convBuf1 = null;
convBuf2 = null;
System.gc();
setError( new Exception( ERR_MEMORY ));
}
// ---- cleanup (topLevel) ----
if( sndF1 != null ) {
sndF1.cleanUp();
}
if( sndF2 != null ) {
sndF2.cleanUp();
}
if( imgF != null ) {
imgF.cleanUp();
}
} // process()
}