/*
* KriechstromDlg.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.Modulator;
import de.sciss.fscape.util.Param;
import de.sciss.fscape.util.ParamSpace;
import de.sciss.fscape.util.Util;
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.io.EOFException;
import java.io.File;
import java.io.IOException;
import java.util.Random;
import java.util.Vector;
/**
* Actually I have to puke when everyone feels
* granular synthesis is a great invention albeit
* it sucks in 99% of all cases, however this
* is a small imitation of the nice GRM freezer.
*/
public class KriechstromDlg
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_FLTCOLOR = 2;
private static final int PR_GAINTYPE = 3;
private static final int PR_LENUPDATE = 0; // pr.bool
private static final int PR_MINCHUNKNUM = 0; // pr.para
private static final int PR_MAXCHUNKNUM = 1;
private static final int PR_MINCHUNKLEN = 2;
private static final int PR_MAXCHUNKLEN = 3;
private static final int PR_MINCHUNKREP = 4;
private static final int PR_MAXCHUNKREP = 5;
private static final int PR_CROSSFADE = 6;
private static final int PR_ENTRYPOINT = 7;
private static final int PR_GAIN = 8;
private static final int PR_FLTAMOUNT = 9;
private static final int PR_OUTLENGTH = 10;
private static final int PR_KRIECHENV = 0; // pr.envl
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_FLTCOLOR = "FltColor";
private static final String PRN_FLTAMOUNT = "FltAmount";
private static final String PRN_MINCHUNKNUM = "MinChunkNum";
private static final String PRN_MAXCHUNKNUM = "MaxChunkNum";
private static final String PRN_MINCHUNKLEN = "MinChunkLen";
private static final String PRN_MAXCHUNKLEN = "MaxChunkLen";
private static final String PRN_MINCHUNKREP = "MinChunkRep";
private static final String PRN_MAXCHUNKREP = "MaxChunkRep";
private static final String PRN_CROSSFADE = "CrossFade";
private static final String PRN_ENTRYPOINT = "EntryPoint";
private static final String PRN_KRIECHENV = "KriechEnv";
private static final String PRN_LENUPDATE = "LenUpdate";
private static final String PRN_OUTLENGTH = "OutLength";
private static final String[] FLTCOLOR_NAMES = { "Dark", "Neutral", "Bright" };
private static final int FLTCOLOR_DARK = 0;
private static final int FLTCOLOR_NEUTRAL = 1;
private static final int FLTCOLOR_BRIGHT = 2;
private static final String prText[] = { "", "" };
private static final String prTextName[] = { PRN_INPUTFILE, PRN_OUTPUTFILE };
private static final int prIntg[] = { 0, 0, FLTCOLOR_NEUTRAL, GAIN_UNITY };
private static final String prIntgName[] = { PRN_OUTPUTTYPE, PRN_OUTPUTRES, PRN_FLTCOLOR, PRN_GAINTYPE };
private static final boolean prBool[] = { true };
private static final String prBoolName[] = { PRN_LENUPDATE };
private static final String prParaName[] = { PRN_MINCHUNKNUM, PRN_MAXCHUNKNUM, PRN_MINCHUNKLEN, PRN_MAXCHUNKLEN,
PRN_MINCHUNKREP, PRN_MAXCHUNKREP, PRN_CROSSFADE, PRN_ENTRYPOINT,
PRN_GAIN, PRN_FLTAMOUNT, PRN_OUTLENGTH };
private static final Param prPara[] = new Param[ prParaName.length ];
private static final Envelope prEnvl[] = { null };
private static final String prEnvlName[] = { PRN_KRIECHENV };
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_FLTCOLOR = GG_OFF_CHOICE + PR_FLTCOLOR;
private static final int GG_GAINTYPE = GG_OFF_CHOICE + PR_GAINTYPE;
private static final int GG_LENUPDATE = GG_OFF_CHECKBOX + PR_LENUPDATE;
private static final int GG_MINCHUNKNUM = GG_OFF_PARAMFIELD + PR_MINCHUNKNUM;
private static final int GG_MAXCHUNKNUM = GG_OFF_PARAMFIELD + PR_MAXCHUNKNUM;
private static final int GG_MINCHUNKLEN = GG_OFF_PARAMFIELD + PR_MINCHUNKLEN;
private static final int GG_MAXCHUNKLEN = GG_OFF_PARAMFIELD + PR_MAXCHUNKLEN;
private static final int GG_MINCHUNKREP = GG_OFF_PARAMFIELD + PR_MINCHUNKREP;
private static final int GG_MAXCHUNKREP = GG_OFF_PARAMFIELD + PR_MAXCHUNKREP;
private static final int GG_CROSSFADE = GG_OFF_PARAMFIELD + PR_CROSSFADE;
private static final int GG_ENTRYPOINT = GG_OFF_PARAMFIELD + PR_ENTRYPOINT;
private static final int GG_FLTAMOUNT = GG_OFF_PARAMFIELD + PR_FLTAMOUNT;
private static final int GG_OUTLENGTH = GG_OFF_PARAMFIELD + PR_OUTLENGTH;
private static final int GG_GAIN = GG_OFF_PARAMFIELD + PR_GAIN;
private static final int GG_KRIECHENV = GG_OFF_ENVICON + PR_KRIECHENV;
private static PropertyArray static_pr = null;
private static Presets static_presets = null;
// -------- public methods --------
public KriechstromDlg() {
super("Kriechstrom");
init2();
}
protected void buildGUI() {
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_MINCHUNKNUM ]= new Param( 1.0, Param.NONE );
static_pr.para[ PR_MAXCHUNKNUM ]= new Param( 10.0, Param.NONE );
static_pr.para[ PR_MINCHUNKREP ]= new Param( 1.0, Param.NONE );
static_pr.para[ PR_MAXCHUNKREP ]= new Param( 5.0, Param.NONE );
static_pr.para[ PR_MINCHUNKLEN ]= new Param( 10.0, Param.ABS_MS );
static_pr.para[ PR_MAXCHUNKLEN ]= new Param( 1000.0, Param.ABS_MS );
static_pr.para[ PR_CROSSFADE ] = new Param( 20.0, Param.FACTOR_TIME );
static_pr.para[ PR_ENTRYPOINT ] = new Param( 500.0, Param.ABS_MS );
static_pr.para[ PR_FLTAMOUNT ] = new Param( 50.0, Param.FACTOR_AMP );
static_pr.para[ PR_OUTLENGTH ] = new Param( 100.0, Param.FACTOR_TIME );
static_pr.paraName = prParaName;
static_pr.envl = prEnvl;
static_pr.envl[ PR_KRIECHENV ] = Envelope.createBasicEnvelope( Envelope.BASIC_UNSIGNED_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;
PathField[] ggInputs;
ParamField ggMinChunkNum, ggMaxChunkNum, ggMinChunkLen, ggMaxChunkLen, ggMinChunkRep, ggMaxChunkRep;
ParamField ggCrossFade, ggEntryPoint, ggFltAmount, ggOutLength;
JCheckBox ggLenUpdate;
ParamSpace[] spcChunk, spcFade, spcOutLen;
ParamSpace spcNumber;
Component[] ggGain;
EnvIcon ggKriechEnv;
JComboBox ggFltColor;
int i;
gui = new GUISupport();
con = gui.getGridBagConstraints();
con.insets = new Insets( 1, 2, 1, 2 );
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$F0Frz$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, null );
con.weightx = 0.5;
con.gridwidth = GridBagConstraints.REMAINDER;
gui.addChoice( (JComboBox) ggGain[ 1 ], GG_GAINTYPE, null );
// -------- Settings-Gadgets --------
con.fill = GridBagConstraints.BOTH;
con.gridwidth = GridBagConstraints.REMAINDER;
gui.addLabel( new GroupLabel( "Kriech Charakteristika", GroupLabel.ORIENT_HORIZONTAL,
GroupLabel.BRACE_NONE ));
spcOutLen = new ParamSpace[ 3 ];
spcOutLen[0] = Constants.spaces[ Constants.absMsSpace ];
spcOutLen[1] = Constants.spaces[ Constants.absBeatsSpace ];
spcOutLen[2] = new ParamSpace( Constants.spaces[ Constants.factorTimeSpace ]);
spcChunk = new ParamSpace[ 2 ];
spcChunk[0] = Constants.spaces[ Constants.absMsSpace ];
spcChunk[1] = Constants.spaces[ Constants.absBeatsSpace ];
spcNumber = new ParamSpace( 1.0, 100000.0, 1.0, Param.NONE );
spcFade = new ParamSpace[ 3 ];
spcFade[0] = Constants.spaces[ Constants.absMsSpace ];
spcFade[1] = Constants.spaces[ Constants.absBeatsSpace ];
spcFade[2] = new ParamSpace( Constants.spaces[ Constants.ratioTimeSpace ]);
// spcFade[2].max = 50.0;
spcFade[2] = new ParamSpace( spcFade[2].min, 50.0, spcFade[2].inc, spcFade[2].unit );
ggOutLength = new ParamField( spcOutLen );
con.weightx = 0.1;
con.gridwidth = 1;
gui.addLabel( new JLabel( "Output length", SwingConstants.RIGHT ));
con.weightx = 0.4;
con.gridwidth = 4;
gui.addParamField( ggOutLength, GG_OUTLENGTH, null );
ggKriechEnv = new EnvIcon( getComponent() );
con.weightx = 0.0;
con.gridwidth = 1;
gui.addGadget( ggKriechEnv, GG_KRIECHENV );
con.weightx = 0.0;
gui.addLabel( new JLabel() );
ggEntryPoint = new ParamField( spcChunk );
con.weightx = 0.1;
gui.addLabel( new JLabel( "Max. entry offset", SwingConstants.RIGHT ));
con.weightx = 0.4;
con.gridwidth = GridBagConstraints.REMAINDER;
gui.addParamField( ggEntryPoint, GG_ENTRYPOINT, null );
ggMinChunkNum = new ParamField( spcNumber );
con.weightx = 0.1;
con.gridwidth = 1;
gui.addLabel( new JLabel( "Min/Max simult. chunks", SwingConstants.RIGHT ));
con.weightx = 0.0;
gui.addParamField( ggMinChunkNum, GG_MINCHUNKNUM, null );
ggMaxChunkNum = new ParamField( spcNumber );
con.weightx = 0.0;
gui.addLabel( new JLabel( "...", SwingConstants.LEFT ));
con.weightx = 0.4;
con.gridwidth = 4;
gui.addParamField( ggMaxChunkNum, GG_MAXCHUNKNUM, null );
ggCrossFade = new ParamField( spcFade );
con.weightx = 0.1;
con.gridwidth = 1;
gui.addLabel( new JLabel( "Crossfades", SwingConstants.RIGHT ));
con.weightx = 0.4;
con.gridwidth = GridBagConstraints.REMAINDER;
gui.addParamField( ggCrossFade, GG_CROSSFADE, null );
ggMinChunkRep = new ParamField( spcNumber );
con.weightx = 0.1;
con.gridwidth = 1;
gui.addLabel( new JLabel( "Min/Max chunks repeats", SwingConstants.RIGHT ));
con.weightx = 0.0;
gui.addParamField( ggMinChunkRep, GG_MINCHUNKREP, null );
ggMaxChunkRep = new ParamField( spcNumber );
con.weightx = 0.0;
gui.addLabel( new JLabel( "...", SwingConstants.LEFT ));
con.weightx = 0.4;
con.gridwidth = 4;
gui.addParamField( ggMaxChunkRep, GG_MAXCHUNKREP, null );
ggFltAmount = new ParamField( Constants.spaces[ Constants.ratioAmpSpace ]);
con.weightx = 0.1;
con.gridwidth = 1;
gui.addLabel( new JLabel( "Filter amount", SwingConstants.RIGHT ));
con.weightx = 0.4;
con.gridwidth = GridBagConstraints.REMAINDER;
gui.addParamField( ggFltAmount, GG_FLTAMOUNT, null );
ggMinChunkLen = new ParamField( spcChunk );
con.weightx = 0.1;
con.gridwidth = 1;
gui.addLabel( new JLabel( "Min. chunk length", SwingConstants.RIGHT ));
con.weightx = 0.4;
con.gridwidth = 4;
gui.addParamField( ggMinChunkLen, GG_MINCHUNKLEN, null );
ggFltColor = new JComboBox();
for( i = 0; i < FLTCOLOR_NAMES.length; i++ ) {
ggFltColor.addItem( FLTCOLOR_NAMES[ i ]);
}
con.weightx = 0.1;
con.gridwidth = 3;
gui.addLabel( new JLabel( "Filter colour", SwingConstants.RIGHT ));
con.weightx = 0.4;
con.gridwidth = GridBagConstraints.REMAINDER;
gui.addChoice( ggFltColor, GG_FLTCOLOR, null );
ggMaxChunkLen = new ParamField( spcChunk );
con.weightx = 0.1;
con.gridwidth = 1;
gui.addLabel( new JLabel( "Max. chunk length", SwingConstants.RIGHT ));
con.weightx = 0.4;
con.gridwidth = 4;
gui.addParamField( ggMaxChunkLen, GG_MAXCHUNKLEN, null );
ggLenUpdate = new JCheckBox( "Instantaneous length update" );
con.weightx = 0.5;
con.gridwidth = GridBagConstraints.REMAINDER;
gui.addCheckbox( ggLenUpdate, GG_LENUPDATE, null );
ggCrossFade.setReference( ggMinChunkLen );
initGUI( this, FLAGS_PRESETS | FLAGS_PROGBAR, gui );
}
public void fillGUI() {
super.fillGUI();
super.fillGUI(gui);
}
public void fillPropertyArray() {
super.fillPropertyArray();
super.fillPropertyArray(gui);
}
// -------- Processor Interface --------
protected void process() {
long chunkLength;
int i, j, k, m;
int len, ch;
long progOff, progLen;
double d1, d2, d3, d4, d5, d6, d7;
float f1, f2, f3, f4, f5, fadeY1, fadeY2;
boolean b1;
long n;
// io
AudioFile inF = null;
AudioFile outF = null;
AudioFileDescr inStream, outStream;
FloatFile[] floatF = null;
File tempFile[] = null;
int inChanNum;
float[][] inBuf, outBuf;
float[] convBuf1, convBuf2;
long inLength, outLength;
long framesWritten;
SpectStream mStream = null;
Modulator mPosMod;
Param pInLength, pOutLength, pPos, pChunkLen;
// Region currentRegion, prevRegion, nextRegion;
Vector<KriechChunk> regionList;
KriechChunk chunk;
float chunkNumHist, chunkNumMean;
int chunkNumHistCnt;
Random rand = new Random( System.currentTimeMillis() );
int chunkNumMin, chunkNumSpan, chunkLenMin, chunkLenSpan, chunkRepMin, chunkRepSpan;
long inOffMin, relOff;
float fltAmount;
float[][] filter = null;
PathField ggOutput;
float gain = 1.0f; // gain abs amp
Param ampRef = new Param( 1.0, Param.ABS_AMP ); // transform-Referenz
float maxAmp = 0.0f;
topLevel:
try {
// ---- open input ----
inF = AudioFile.openAsRead(new File(pr.text[PR_INPUTFILE]));
inStream = inF.getDescr();
inChanNum = inStream.channels;
inLength = inStream.length;
// this helps to prevent errors from empty files!
if ((inLength < 1) || (inChanNum < 1)) throw new EOFException(ERR_EMPTY);
// ---- open output ----
ggOutput = (PathField) gui.getItemObj(GG_OUTPUTFILE);
if (ggOutput == null) throw new IOException(ERR_MISSINGPROP);
outStream = new AudioFileDescr(inStream);
ggOutput.fillStream(outStream);
outF = AudioFile.openAsWrite(outStream);
// .... check running ....
if (!threadRunning) break topLevel;
// ---- misc init ----
pInLength = new Param(AudioFileDescr.samplesToMillis(inStream, inLength), Param.ABS_MS);
pOutLength = Param.transform(pr.para[PR_OUTLENGTH], Param.ABS_MS, pInLength, null);
outLength = (long) (AudioFileDescr.millisToSamples(outStream, pOutLength.value) + 0.5);
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(outLength);
mStream.getDescr();
mPosMod = new Modulator(new Param(0.0, Param.ABS_MS), pInLength, pr.envl[PR_KRIECHENV], mStream);
// foo = mPosMod.calc();
// d1 = foo.value;
// mStream.framesRead++; // dirty! XXX
// System.out.println( "Total reads: "+mImpReads+"; morphs "+mMorphs );
chunkNumMin = (int) (pr.para[PR_MINCHUNKNUM].value + 0.5);
chunkNumSpan = (int) (pr.para[PR_MAXCHUNKNUM].value + 0.5) - chunkNumMin + 1;
if (chunkNumSpan < 0) {
chunkNumSpan = -chunkNumSpan;
chunkNumMin = (int) (pr.para[PR_MAXCHUNKNUM].value + 0.5);
}
chunkRepMin = (int) (pr.para[PR_MINCHUNKREP].value + 0.5);
chunkRepSpan = (int) (pr.para[PR_MAXCHUNKREP].value + 0.5) - chunkRepMin + 1;
if (chunkRepSpan < 0) {
chunkRepSpan = -chunkRepSpan;
chunkRepMin = (int) (pr.para[PR_MAXCHUNKREP].value + 0.5);
}
chunkLenMin = (int) (AudioFileDescr.millisToSamples( outStream,
(Param.transform( pr.para[ PR_MINCHUNKLEN ], Param.ABS_MS, null, null )).value) + 0.5);
i = (int) (AudioFileDescr.millisToSamples( outStream,
(Param.transform( pr.para[ PR_MAXCHUNKLEN ], Param.ABS_MS, null, null )).value) + 0.5);
chunkLenSpan = i - chunkLenMin + 1;
if( chunkLenSpan < 0 ) {
chunkLenSpan = -chunkLenSpan;
chunkLenMin = i;
}
inOffMin = Math.max(0, inLength - chunkLenMin);
fltAmount = (float) Param.transform(pr.para[PR_FLTAMOUNT], Param.ABS_AMP, ampRef, null).value;
if (fltAmount > 0.0f) { // create filter table
filter = new float[512][3];
d1 = 1.0;
switch (pr.intg[PR_FLTCOLOR]) {
case FLTCOLOR_DARK:
d1 = 2.0;
break;
case FLTCOLOR_NEUTRAL:
d1 = 0.8;
break;
case FLTCOLOR_BRIGHT:
d1 = 0.4;
break;
}
for (i = 0; i < 512; i++) { // 2nd order IIR filter, EDS script S. 101
d2 = Math.exp(rand.nextDouble() * d1) * 20.0;
// d2 = Constants.PI2 * d2 / inStream.smpRate; // wc
//d2 = Math.PI / 2;
// d3 = Math.log( rand.nextDouble() * 4.5 + 2.88 );
//d3 = 1.1;
// d4 = d2 * d3; // w2
// d5 = d2 / d3; // w1
// d6 = Math.cos( d2 ); // alpha
// d7 = -Math.tan( (d4 - d5 + Math.PI) / 2 ) * Math.tan( d2 / 2 ); // k
//
// filter[i][0] = (float) ((d7 - 1.0) / (d7 + 1.0)); // b0, a2
// filter[i][1] = (float) (-2.0 * d6 * d7 / (d7 + 1.0)); // b1, a1
// For example a biquad IIR looks like that:
// y[n] = x[n]*a0 + x[n-1]*a1 + x[n-2]*a2 + b1*y[n-1] + b2*y[n-2]
// If you want a 2 pole bandpass you would setup a0,a1,a2,b1,b2 like this:
//
// omega = 2.0*M_PI*center;
// alpha = sin(omega)*sinh(log(2.0)/2.0*bandwidth*omega/sin(omega));
// a0 = alpha;
// a1 = 0.0;
// a2 = -alpha;
// b1 = 2.0 * cos(omega);
// b2 = alpha - 1.0;
// gain = 1.0 + alpha;
// a0/=gain
// a1/=gain
//
// center is the normalized frequency of the bandpass:
// center = center_frequency/sample_frequency
// bandwidth is given in octaves between the -3dB attenuation levels of the passband
// d2 = Constants.PI2 * d2 / inStream.smpRate; // wc
// d3 = rand.nextDouble() * 0.9 + 0.1; // bandwidth
// d4 = Math.sin( d2 ) * sinh( Constants.ln2 / 2.0 * d3 * d2 / Math.sin( d2 )); // alpha
// filter[i][0] = (float) (d4 / (1.0 + d4)); // b0
// filter[i][1] = (float) (2.0 * Math.cos( d2 )); // a1;
// filter[i][2] = (float) (d4 - 1.0); // a2
// from Text: The Equivalence of Various Methods of Computing
// Biquad Coefficients for Audio Parametric Equalizers
// d2 = d2 / inStream.smpRate; // Omega0
// fuer d3 = 0.3:
//d2 = 2.25;
//d2 = 0.11;
//d2 = Math.pow( Math.log( rand.nextDouble() * 1.7182818 + 1.0 ), 0.6 ) * 2.49 + 0.108;
//d2 = Math.pow( rand.nextDouble(), 0.4 ) * 2.45 + 0.108;
// fuer d3 = 0.67:
//d2 = 0.24 ... 2.3;
d2 = Math.pow(rand.nextDouble(), d1) * 2.06 + 0.24;
// d3 = rand.nextDouble() * 0.9 + 0.1; // bandwidth
d3 = 0.67;
// d5 = 100.0; // gain;
d5 = 1.0;
d4 = Math.sin( d2 ) * sinh( Constants.ln2 / 2.0 * d3 * d2 / Math.sin( d2 )); // gamma
d6 = d4 * Math.sqrt( d5 );
// d7 = 1.0 + d4 / Math.sqrt( d5 ); // a0
d7 = 1.0;
filter[i][0] = (float) ((1.0 + d6) / d7); // b0
filter[i][1] = (float) (-Math.cos(d2) * 2.0 / d7); // b1, a1
filter[i][2] = (float) ((1.0 - d6) / d7); // b2, a2
}
}
progOff = 0;
progLen = outLength;
// ---- misc init ----
regionList = new Vector<KriechChunk>(); // Elemente = de.sciss.fscape.io.Region, chronologically added
inBuf = new float[inChanNum][8192];
outBuf = new float[inChanNum][8192];
pChunkLen = new Param(0.0, Param.ABS_MS);
chunk = new KriechChunk(inChanNum);
chunk.len = 0;
chunk.outOff = 0;
chunk.rep = 0;
regionList.addElement(chunk);
framesWritten = 0L;
chunkNumMean = (chunkNumSpan >> 1) + chunkNumMin;
chunkNumHist = chunkNumMean;
chunkNumHistCnt = 1;
// normalization requires temp files
if (pr.intg[PR_GAINTYPE] == GAIN_UNITY) {
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 += outLength / chunkNumMean;
} else {
gain = (float) (Param.transform(pr.para[PR_GAIN], Param.ABS_AMP, ampRef, null)).value;
}
// .... check running ....
if( !threadRunning ) break topLevel;
// ================ PASS 1: Region detection ================
while( (framesWritten < outLength) && threadRunning ) {
chunkLength = outLength - framesWritten;
b1 = false;
// calc next stop, remove obsolete chunks
k = regionList.size();
for (i = 0; i < k; i++) {
chunk = regionList.elementAt(i);
n = chunk.len - framesWritten + chunk.outOff;
if (n > 0) {
chunkLength = Math.min(chunkLength, n);
n -= chunk.fadeOut;
if ((n >= 0) && (chunk.rep-- > 1)) { // repeat
if( n == 0 ) {
chunk = new KriechChunk(chunk);
chunk.outOff = framesWritten;
if( pr.bool[ PR_LENUPDATE ]) {
chunk.len = chunk.nextLen;
chunkLength = Math.min( chunkLength, chunk.len - (chunk.rep > 1 ? chunk.fadeOut : 0) );
chunk.nextLen = (int) Math.min(inLength - chunk.inOff, (int) (rand.nextFloat() * chunkLenSpan) + chunkLenMin);
pChunkLen.value = AudioFileDescr.samplesToMillis( outStream, chunk.len );
m = (int) (AudioFileDescr.millisToSamples( outStream,
(Param.transform( pr.para[ PR_CROSSFADE ], Param.ABS_MS, pChunkLen, null )).value) + 0.5);
chunk.fadeIn = chunk.fadeOut;
chunk.fadeOut = Math.min( Math.min( chunk.nextLen >> 1, chunk.len - chunk.fadeIn ), m );
}
regionList.addElement(chunk);
} else {
chunkLength = Math.min(chunkLength, n);
}
}
} else {
regionList.removeElementAt( i );
i--;
k--;
b1 = true;
}
}
pPos = mPosMod.calc();
mStream.framesRead = framesWritten;
if (b1) { // chunks ceased to exist -> calc new chunk-num
i = (int) (Math.min(1.0f, rand.nextFloat() * (chunkNumHistCnt * chunkNumMean) / chunkNumHist) * chunkNumSpan) + chunkNumMin;
j = i - regionList.size();
for (; j > 0; j--) {
chunk = new KriechChunk( inChanNum );
d1 = (rand.nextDouble() * 2.0 - 1.0) * pr.para[ PR_ENTRYPOINT ].value + pPos.value;
chunk.inOff = Math.min( inOffMin, Math.max( 0, (int) (AudioFileDescr.millisToSamples( outStream, d1 ) + 0.5) ));
chunk.rep = (int) (rand.nextFloat() * chunkRepSpan) + chunkRepMin;
chunk.len = (int) Math.min(inLength - chunk.inOff, (int) (rand.nextFloat() * chunkLenSpan) + chunkLenMin);
pChunkLen.value = AudioFileDescr.samplesToMillis( outStream, chunk.len );
k = (int) (AudioFileDescr.millisToSamples( outStream,
(Param.transform( pr.para[ PR_CROSSFADE ], Param.ABS_MS, pChunkLen, null )).value) + 0.5);
chunk.fadeIn = Math.min( chunk.len >> 1, k );
if( pr.bool[ PR_LENUPDATE ]) {
chunk.nextLen = (int) Math.min(inLength - chunk.inOff, (int) (rand.nextFloat() * chunkLenSpan) + chunkLenMin);
chunk.fadeOut = Math.min(Math.min(chunk.nextLen >> 1, chunk.len - chunk.fadeIn), k);
} else {
chunk.fadeOut = Math.min( chunk.len - chunk.fadeIn, k );
}
chunk.outOff = framesWritten;
if (rand.nextFloat() < fltAmount) {
chunk.flt = (int) (rand.nextFloat() * 512);
} else {
chunk.flt = -1;
}
regionList.addElement(chunk);
chunkLength = Math.min(chunkLength, chunk.len - (chunk.rep > 1 ? chunk.fadeOut : 0));
}
}
// mix regions
while ((chunkLength > 0) && threadRunning) {
len = (int) Math.min(8192, chunkLength);
for (i = 0; i < regionList.size(); i++) {
chunk = regionList.elementAt(i);
relOff = framesWritten - chunk.outOff;
inF.seekFrame(chunk.inOff + relOff);
inF.readFrames(inBuf, 0, len);
// --- filter ---
if( chunk.flt >= 0 ) {
convBuf2 = filter[ chunk.flt ];
for( ch = 0; ch < inChanNum; ch++ ) {
convBuf1 = inBuf[ch];
f1 = chunk.fltMem[ch][0]; // b1
f2 = chunk.fltMem[ch][1]; // b2
f3 = chunk.fltMem[ch][2]; // a1
f4 = chunk.fltMem[ch][3]; // a2
for( j = 0; j < len; j++ ) {
f5 = convBuf1[j]; // b0
// convBuf1[j] = convBuf2[0] * f5 + convBuf2[1] * f1 + f2 - convBuf2[1] * f3 - convBuf2[0] * f4;
// convBuf1[j] = f5 - f1;
// convBuf1[j] = convBuf2[0] * f5 + convBuf2[1] * f3 - convBuf2[2] * f4;
// convBuf1[j] = convBuf2[0] * (f5 - f2) + convBuf2[1] * f3 + convBuf2[2] * f4;
convBuf1[j] = convBuf2[0] * f5 + convBuf2[1] * f1 + convBuf2[2] * f2 -
convBuf2[1] * f3 - convBuf2[2] * f4;
f1 = f5; // b0 -> b1
f4 = f3; // a1 -> a2
f3 = convBuf1[j]; // a0 -> a1
}
chunk.fltMem[ch][0] = f1;
chunk.fltMem[ch][1] = f2;
chunk.fltMem[ch][2] = f3;
chunk.fltMem[ch][3] = f4;
}
}
// --- fade in ---
n = chunk.fadeIn - relOff;
if (n > 0) {
j = (int) Math.min(n, len);
fadeY2 = (float) (n - j) / (float) chunk.fadeIn;
fadeY1 = 1.0f - (float) relOff / (float) chunk.fadeIn;
f1 = (fadeY1 - fadeY2) / (j - 1);
for (m = 0; m < j; m++) {
for (ch = 0; ch < inChanNum; ch++) {
inBuf[ch][m] *= 1.0f - (fadeY1 * fadeY1);
}
fadeY1 -= f1;
}
}
// --- fade out ---
n = chunk.len - chunk.fadeOut - relOff;
if (n < len) { // fade out
j = (int) Math.max(n, 0);
fadeY1 = (float) (j - n) / (float) chunk.fadeOut;
fadeY2 = (float) (n - len) / (float) chunk.fadeOut;
f1 = (fadeY2 + fadeY1) / (len - j - 1);
for (m = j; m < len; m++) {
for (ch = 0; ch < inChanNum; ch++) {
inBuf[ch][m] *= 1.0f - (fadeY1 * fadeY1);
}
fadeY1 -= f1;
}
}
if (i == 0) {
for (ch = 0; ch < inChanNum; ch++) {
System.arraycopy(inBuf[ch], 0, outBuf[ch], 0, len);
}
} else {
for (ch = 0; ch < inChanNum; ch++) {
Util.add(inBuf[ch], 0, outBuf[ch], 0, len);
}
}
}
// write output
if (floatF != null) {
for (ch = 0; ch < inChanNum; ch++) {
convBuf1 = outBuf[ch];
for (i = 0; i < len; i++) { // measure max amp
f1 = Math.abs(convBuf1[i]);
if (f1 > maxAmp) {
maxAmp = f1;
}
}
floatF[ch].writeFloats(convBuf1, 0, len);
}
} else { // i.e. abs gain
for (ch = 0; ch < inChanNum; ch++) {
convBuf1 = outBuf[ch];
for (i = 0; i < len; i++) { // measure max amp + adjust gain
f1 = Math.abs(convBuf1[i]);
convBuf1[i] *= gain;
if (f1 > maxAmp) {
maxAmp = f1;
}
}
}
outF.writeFrames(outBuf, 0, len);
}
framesWritten += len;
chunkLength -= len;
progOff += len;
// .... progress ....
setProgression((float) progOff / (float) progLen);
}
chunkNumHist += regionList.size();
chunkNumHistCnt++;
} // while framesWritten < outLength
// .... check running ....
if (!threadRunning) break topLevel;
inF.close();
inF = null;
// ---- normalize output ----
if (pr.intg[PR_GAINTYPE] == GAIN_UNITY) {
gain = (float) (Param.transform(pr.para[PR_GAIN], Param.ABS_AMP,
new Param(1.0 / maxAmp, Param.ABS_AMP), null)).value;
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;
}
}
// .... check running ....
if (!threadRunning) break topLevel;
outF.close();
outF = null;
// setProgression( 1.0f );
// inform about clipping/ low level
maxAmp *= gain;
handleClipping(maxAmp);
} catch (IOException e1) {
setError(e1);
} catch (OutOfMemoryError e2) {
inStream = null;
outStream = null;
inBuf = null;
convBuf1 = null;
convBuf2 = null;
System.gc();
setError(new Exception(ERR_MEMORY));
}
// ---- cleanup (topLevel) ----
if (outF != null) {
outF.cleanUp();
}
if (inF != null) {
inF.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 --------
protected double sinh(double x) {
return ((Math.exp(x) - Math.exp(-x)) / 2);
}
/**
* Set new input file
*/
protected void setInput(String fname) {
AudioFile f = null;
AudioFileDescr stream = null;
ParamField ggSlave;
Param ref;
// ---- Header lesen ----
try {
f = AudioFile.openAsRead(new File(fname));
stream = f.getDescr();
f.close();
ref = new Param(AudioFileDescr.samplesToMillis(stream, stream.length), Param.ABS_MS);
ggSlave = (ParamField) gui.getItemObj(GG_OUTLENGTH);
if (ggSlave != null) {
ggSlave.setReference(ref);
}
} catch( IOException ignored) {}
}
}
// class KriechstromDlg
/**
* internal class for synthesis
*/
class KriechChunk {
long inOff, outOff;
int len, rep;
int fadeIn, fadeOut;
int nextLen;
int flt;
float[][] fltMem;
public KriechChunk(int numChan) {
fltMem = new float[numChan][4];
}
public KriechChunk(KriechChunk src) {
this.inOff = src.inOff;
this.len = src.len;
this.rep = src.rep;
this.fadeIn = src.fadeIn;
this.fadeOut = src.fadeOut;
this.nextLen = src.nextLen;
this.flt = src.flt;
this.fltMem = new float[src.fltMem.length][4];
}
} // class KriechChunk