/*
* SachensucherDlg.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
*
*
* Changelog:
* 05-nov-04 bugfix
*/
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.fscape.util.Param;
import de.sciss.fscape.util.ParamSpace;
import de.sciss.io.AudioFile;
import de.sciss.io.AudioFileDescr;
import javax.swing.*;
import java.awt.*;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
/**
* Experimental processing module that will read
* any kind of file and convert it to a sound file
* making guesses about the data format (float/ int)
* and word size. Nice for uncompressed tiff
* images or uncompressed movie files.
*/
public class SachensucherDlg
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_OUTPUTRATE = 2;
private static final int PR_GAIN = 0; // pr.para
private static final int PR_LENGTH = 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_OUTPUTRATE = "OutputRate";
private static final String PRN_LENGTH = "Length";
private static final String prText[] = { "", "" };
private static final String prTextName[] = { PRN_INPUTFILE, PRN_OUTPUTFILE };
private static final int prIntg[] = { 0, 0, 0 };
private static final String prIntgName[] = { PRN_OUTPUTTYPE, PRN_OUTPUTRES, PRN_OUTPUTRATE };
private static final Param prPara[] = { null, null };
private static final String prParaName[] = { PRN_GAIN, PRN_LENGTH };
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_OUTPUTRATE = GG_OFF_CHOICE + PR_OUTPUTRATE;
private static final int GG_GAIN = GG_OFF_PARAMFIELD + PR_GAIN;
private static final int GG_LENGTH = GG_OFF_PARAMFIELD + PR_LENGTH;
private static PropertyArray static_pr = null;
private static Presets static_presets = null;
// -------- public methods --------
/**
* !! setVisible() bleibt dem Aufrufer ueberlassen
*/
public SachensucherDlg()
{
super( "Sachensucher" );
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_LENGTH ] = new Param( 768.0, Param.NONE );
static_pr.paraName = prParaName;
// static_pr.superPr = DocumentFrame.static_pr;
fillDefaultAudioDescr( static_pr.intg, PR_OUTPUTTYPE, PR_OUTPUTRES, PR_OUTPUTRATE );
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;
ParamField ggLength;
PathField[] ggInputs;
ParamField ggGain;
ParamSpace spcLength;
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( "File I/O", GroupLabel.ORIENT_HORIZONTAL,
GroupLabel.BRACE_NONE ));
ggInputFile = new PathField( PathField.TYPE_INPUTFILE, "Select input file" );
// ggInputFile.handleTypes( GenericFile.TYPES_SOUND );
con.gridwidth = 1;
con.weightx = 0.1;
gui.addLabel( new JLabel( "Any input file", SwingConstants.RIGHT ));
con.gridwidth = GridBagConstraints.REMAINDER;
con.weightx = 0.9;
gui.addPathField( ggInputFile, GG_INPUTFILE, null );
ggOutputFile = new PathField( PathField.TYPE_OUTPUTFILE + PathField.TYPE_FORMATFIELD +
PathField.TYPE_RESFIELD + PathField.TYPE_RATEFIELD,
"Select output file" );
ggOutputFile.handleTypes( GenericFile.TYPES_SOUND );
ggInputs = new PathField[ 1 ];
ggInputs[ 0 ] = ggInputFile;
ggOutputFile.deriveFrom( ggInputs, "$D0$F0Sonif$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 );
gui.registerGadget( ggOutputFile.getRateGadget(), GG_OUTPUTRATE );
ggGain = new ParamField( Constants.spaces[ Constants.decibelAmpSpace ]);
con.weightx = 0.1;
con.gridwidth = 1;
gui.addLabel( new JLabel( "Headroom", SwingConstants.RIGHT ));
con.weightx = 0.4;
con.gridwidth = GridBagConstraints.REMAINDER;
gui.addParamField( ggGain, GG_GAIN, null );
// -------- Settings --------
gui.addLabel( new GroupLabel( "Settings", GroupLabel.ORIENT_HORIZONTAL,
GroupLabel.BRACE_NONE ));
spcLength = new ParamSpace( 12.0, 98304.0, 3.0, Param.NONE );
ggLength = new ParamField( spcLength );
con.weightx = 0.1;
con.gridwidth = 1;
gui.addLabel( new JLabel( "Window length [bytes]", SwingConstants.RIGHT ));
con.weightx = 0.4;
gui.addParamField( ggLength, GG_LENGTH, 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, len;
long progOff, progLen;
float f1, f2;
// io
RandomAccessFile inF = null;
AudioFile outF = null;
AudioFileDescr outStream = null;
ByteBuffer bb;
FileChannel inCh;
int bufSize;
Info info;
float[][] outBuf;
// Synthesize
float gain;
Param ampRef = new Param( 1.0, Param.ABS_AMP ); // transform-Referenz
// Smp Init
long inLength;
long framesRead;
long pos, lastPos;
float maxAmp, lastMaxAmp, diff;
int chunkCount, type, lastType, offset, lastOffset;
int[] stat = new int[5];
float[] amp = new float[4];
float[] prob = new float[4];
PathField ggOutput;
topLevel: try {
// ---- open input, output; init ----
// input
inF = new RandomAccessFile( pr.text[ PR_INPUTFILE ], "r" );
inLength = inF.length();
inCh = inF.getChannel();
progOff = 0;
progLen = inLength*2;
bufSize = (int) pr.para[ PR_LENGTH ].value / 3 * 3;
// output
ggOutput = (PathField) gui.getItemObj( GG_OUTPUTFILE );
if( ggOutput == null ) throw new IOException( ERR_MISSINGPROP );
outStream = new AudioFileDescr();
ggOutput.fillStream( outStream );
outStream.channels = 1;
outF = AudioFile.openAsWrite( outStream );
// .... check running ....
if( !threadRunning ) break topLevel;
gain = (float) ((Param.transform( pr.para[ PR_GAIN ], Param.ABS_AMP, ampRef, null )).value);
// if( gain > 1.0f ) gain = 1.0f / gain;
// .... check running ....
if( !threadRunning ) break topLevel;
// ----==================== kulchur ====================----
// framesWritten = 0L;
framesRead = 0L;
lastType = 0;
lastMaxAmp = 0.0f;
lastPos = 0L;
chunkCount = 0;
lastOffset = 0;
info = new Info();
// info.outF = outF;
bb = ByteBuffer.allocate( bufSize );
info.bb = bb;
outBuf = new float[1][ bb.capacity() ];
info.fb = outBuf[0];
while( threadRunning && framesRead < inLength ) {
// ==================== read input chunk ====================
bb.clear();
len = (int) Math.min( inLength - framesRead, bufSize );
inCh.read( bb );
type = 0;
f2 = idByte( info, 0 );
offset = 0;
maxAmp = info.maxAmp;
diff = Float.POSITIVE_INFINITY;
for( i = 0, j = 0; i < 2; i++ ) {
prob[i] = idShort( info, i );
amp[i] = info.maxAmp;
if( info.diff < diff ) {
j = i;
diff = info.diff;
}
}
if( prob[j] < f2 ) {
f2 = prob[j];
type = 1;
maxAmp = amp[j];
offset = j;
}
diff = Float.POSITIVE_INFINITY;
for( i = 0, j = 0; i < 3; i++ ) {
prob[i] = idTri( info, i );
amp[i] = info.maxAmp;
if( info.diff < diff ) {
j = i;
diff = info.diff;
}
}
if( prob[j] < f2 ) {
f2 = prob[j];
type = 2;
maxAmp = amp[j];
offset = j;
}
diff = Float.POSITIVE_INFINITY;
for( i = 0, j = 0; i < 4; i++ ) {
prob[i] = idInt( info, i );
amp[i] = info.maxAmp;
if( info.diff < diff ) {
j = i;
diff = info.diff;
}
}
if( prob[j] < f2 ) {
f2 = prob[j];
type = 3;
maxAmp = amp[j];
offset = j;
}
diff = Float.POSITIVE_INFINITY;
for( i = 0, j = 0; i < 4; i++ ) {
prob[i] = idFloat( info, i );
amp[i] = info.maxAmp;
if( info.diff < diff ) {
j = i;
diff = info.diff;
}
}
if( prob[j] < f2 ) {
f2 = prob[j];
type = 3;
maxAmp = amp[j];
offset = j;
}
if( (type != lastType) || (offset != 0) || (len < bufSize) ) {
pos = inCh.position();
inCh.position( lastPos );
f1 = lastMaxAmp > 0.0f ? (gain / lastMaxAmp) : 0.0f;
stat[lastType]++;
// System.err.println( "new type at "+pos+"; lastType = "+lastType+"; lastMaxAmp = "+lastMaxAmp+" -> gain "+f1 );
for( i = 0; i < chunkCount; i++ ) {
bb.clear();
inCh.read( bb );
j = info.fb.length;
switch( lastType ) {
case 0:
copyBytes( info, lastOffset );
break;
case 1:
copyShorts( info, lastOffset );
j = (j - lastOffset) / 2;
// j = (j - 0) / 2;
break;
case 2:
copyTris( info, lastOffset );
j = (j - lastOffset) / 3;
break;
case 3:
copyInts( info, lastOffset );
j = (j - lastOffset) / 4;
break;
case 4:
copyFloats( info, lastOffset );
j = (j - lastOffset) / 4;
break;
}
for( k = 0; k < j; k++ ) {
info.fb[k] *= f1;
}
outF.writeFrames( outBuf, 0, j );
progOff += bufSize;
// .... progress ....
setProgression( (float) progOff / (float) progLen );
// .... check running ....
if( !threadRunning ) break topLevel;
}
lastType = type;
lastPos = pos;
lastOffset = offset;
inCh.position( pos + offset );
lastMaxAmp = maxAmp;
chunkCount = 1;
} else {
lastMaxAmp = Math.max( maxAmp, lastMaxAmp );
chunkCount++;
}
framesRead += len;
progOff += len;
// .... progress ....
setProgression( (float) progOff / (float) progLen );
} // while( threadRunning && framesRead < inLength )
// .... check running ....
if( !threadRunning ) break topLevel;
// ---- clean up, normalize ----
inF.close();
inF = null;
outF.close();
outF = null;
setProgression( 1.0f );
// ---- Finish ----
// inform about clipping/ low level
// handleClipping( maxAmp );
System.out.println( "# of chunks: "+stat[0]+" bytes, "+stat[1]+" shorts, "+
stat[2]+" tris, "+stat[3]+" ints, "+stat[4]+" floats." );
}
catch( IOException e1 ) {
setError( e1 );
}
catch( OutOfMemoryError e2 ) {
outStream = null;
System.gc();
setError( new Exception( ERR_MEMORY ));
}
// ---- cleanup (topLevel) ----
if( inF != null ) {
try {
inF.close();
} catch( Exception ignored) {}
}
if( outF != null ) {
outF.cleanUp();
}
} // process()
// -------- private methods --------
private float idByte( Info info, int offset )
{
int num = (info.bb.capacity() - offset);
int i;
float f1 = 0.0f;
float f2, f3;
double d1 = 0.0;
double d2 = 0.0;
int mul = 0xFF;
info.maxAmp = 0.0f;
info.bb.clear();
info.bb.position( offset );
for( i = 0; i < num; i++ ) {
f2 = f1;
f1 = (float) info.bb.get() / mul;
f3 = Math.abs( f1 );
if( f3 > info.maxAmp ) info.maxAmp = f3;
f1 *= f1;
d1 += f1;
d2 += Math.abs( f2 - f1 );
}
// info.energy = (float) d1;
info.diff = (float) d2;
return( (float) (d2 / d1) );
}
private float idShort( Info info, int offset )
{
int num = (info.bb.capacity() - offset) >> 1;
int i;
float f1 = 0.0f;
float f2;
float f3;
double d1 = 0.0;
double d2 = 0.0;
int mul = 0xFFFF;
info.maxAmp = 0.0f;
info.bb.clear();
info.bb.position( offset );
for( i = 0; i < num; i++ ) {
f2 = f1;
f1 = (float) info.bb.getShort() / mul;
f3 = Math.abs( f1 );
if( f3 > info.maxAmp ) info.maxAmp = f3;
f1 *= f1;
d1 += f1;
d2 += Math.abs( f2 - f1 );
}
// info.energy = (float) d1;
info.diff = (float) d2;
return( (float) (d2 / d1) );
}
private float idTri( Info info, int offset )
{
int num = (info.bb.capacity() - offset) / 3;
int i;
float f1 = 0.0f;
float f2;
float f3;
double d1 = 0.0;
double d2 = 0.0;
int mul = 0xFFFFFF;
info.maxAmp = 0.0f;
info.bb.clear();
info.bb.position( offset );
for( i = 0; i < num; i++ ) {
f2 = f1;
f1 = (float) (((info.bb.get() << 16)) | ((info.bb.get() & 0xFF) << 8) | (info.bb.get() & 0xFF) ) / mul;
f3 = Math.abs( f1 );
if( f3 > info.maxAmp ) info.maxAmp = f3;
f1 *= f1;
d1 += f1;
d2 += Math.abs( f2 - f1 );
}
// info.energy = (float) d1;
info.diff = (float) d2;
return( (float) (d2 / d1) );
}
private float idInt( Info info, int offset )
{
int num = (info.bb.capacity() - offset) >> 2;
int i;
float f1 = 0.0f;
float f2;
float f3;
double d1 = 0.0;
double d2 = 0.0;
long mul = 0xFFFFFFFF;
info.maxAmp = 0.0f;
info.bb.clear();
info.bb.position( offset );
for( i = 0; i < num; i++ ) {
f2 = f1;
f1 = (float) info.bb.getInt() / mul;
f3 = Math.abs( f1 );
if( f3 > info.maxAmp ) info.maxAmp = f3;
f1 *= f1;
d1 += f1;
d2 += Math.abs( f2 - f1 );
}
// info.energy = (float) d1;
info.diff = (float) d2;
return( (float) (d2 / d1) );
}
private float idFloat( Info info, int offset )
{
int num = (info.bb.capacity() - offset) >> 2;
int i;
float f1 = 0.0f;
float f2 = 0.0f;
float f3;
double d1 = 0.0;
double d2 = 0.0;
int valid = 0;
info.maxAmp = 0.0f;
info.bb.clear();
info.bb.position( offset );
for( i = 0; i < num; i++ ) {
f1 = info.bb.getFloat();
f3 = Math.abs( f1 );
f1 *= f1;
if( f1 > -1.0e6f & f1 < 1.0e6f ) {
if( f3 > info.maxAmp ) info.maxAmp = f3;
f2 = f1;
d1 += f1;
d2 += Math.abs( f2 - f1 );
valid++;
}
}
// info.energy = (float) d1;
info.diff = (float) d2;
if( (float) valid / (float) num < 0.8f ) return Float.POSITIVE_INFINITY;
if( d1 != 0.0 ) {
return( (float) (d2 / d1) );
} else return 1.0f;
}
private void copyBytes( Info info, int offset )
throws IOException
{
int num = (info.bb.capacity() - offset);
int mul = 0xFF;
int i;
info.bb.clear();
info.bb.position( offset );
for( i = 0; i < num; i++ ) {
info.fb[i] = (float) info.bb.get() / mul;
}
}
private void copyShorts( Info info, int offset )
throws IOException
{
int num = (info.bb.capacity() - offset) >> 1;
int mul = 0xFFFF;
int i;
info.bb.clear();
info.bb.position( offset );
for( i = 0; i < num; i++ ) {
info.fb[i] = (float) info.bb.getShort() / mul;
}
}
private void copyTris( Info info, int offset )
throws IOException
{
int num = (info.bb.capacity() - offset) / 3;
int mul = 0xFFFFFF;
int i;
info.bb.clear();
info.bb.position( offset );
for( i = 0; i < num; i++ ) {
info.fb[i] = (float) (((info.bb.get() << 16)) | ((info.bb.get() & 0xFF) << 8) | (info.bb.get() & 0xFF) ) / mul;
}
}
private void copyInts( Info info, int offset )
throws IOException
{
int num = (info.bb.capacity() - offset) >> 2;
long mul = 0xFFFFFFFF;
int i;
info.bb.clear();
info.bb.position( offset );
for( i = 0; i < num; i++ ) {
info.fb[i] = (float) info.bb.getInt() / mul;
}
}
private void copyFloats( Info info, int offset )
throws IOException
{
int num = (info.bb.capacity() - offset) >> 2;
int i;
info.bb.clear();
info.bb.position( offset );
for( i = 0; i < num; i++ ) {
info.fb[i] = info.bb.getFloat();
}
}
protected class Info {
private ByteBuffer bb;
private float maxAmp;
private float[] fb;
private float diff;
}
}