/*
* StatisticsDlg.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:
* 07-Jan-05 fixed painter method
* 16-Feb-05 uses VectorDisplay instead of own custom paint methods
*/
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.Filter;
import de.sciss.fscape.util.Util;
import de.sciss.gui.Axis;
import de.sciss.gui.VectorSpace;
import de.sciss.io.AudioFile;
import de.sciss.io.AudioFileDescr;
import de.sciss.io.IOUtil;
import de.sciss.io.Marker;
import javax.swing.*;
import javax.swing.event.MouseInputAdapter;
import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.io.EOFException;
import java.io.File;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.Locale;
/**
* Various graphic info displays about a sound file,
* such as amplitude or power spectrum. Only mono at the moment.
*/
public class StatisticsDlg
extends ModulePanel {
// -------- private variables --------
// Properties (defaults)
private static final int PR_INPUTFILE = 0; // pr.text
private static final int PR_PROPERTY = 0; // pr.intg
private static final int PR_CHANNEL = 1;
private static final int PR_DATALEN = 2;
private static final int PR_ANALYSISWIN = 3;
private static final int PR_SUM = 0; // pr.bool
private static final int PR_HLOG = 1;
private static final int PR_VLOG = 2;
private static final int PROP_ASPECT = 0;
private static final int PROP_PSPECT = 1;
private static final int PROP_PHSPECT = 2;
private static final int PROP_ELONG = 3;
private static final int NUMPROP = 4;
// private static final int CHANNEL_LEFT = 0;
// private static final int CHANNEL_RIGHT = 1;
private static final int CHANNEL_SUM = 2;
// private static final int CHANNEL_SUB = 3;
private static final String PRN_INPUTFILE = "InputFile";
private static final String PRN_PROPERTY = "Property";
private static final String PRN_CHANNEL = "Channel";
private static final String PRN_DATALEN = "DataLen";
private static final String PRN_ANALYSISWIN = "AnalysisWin";
private static final String PRN_SUM = "Histo"; // named this way for compatibility reasons
private static final String PRN_HLOG = "HLog";
private static final String PRN_VLOG = "VLog";
private static final String prText[] = { "" };
private static final String prTextName[] = { PRN_INPUTFILE };
private static final int prIntg[] = { PROP_ASPECT, CHANNEL_SUM, 3, 0 };
private static final String prIntgName[] = { PRN_PROPERTY, PRN_CHANNEL, PRN_DATALEN, PRN_ANALYSISWIN };
private static final boolean prBool[] = { false, false, false };
private static final String prBoolName[] = { PRN_SUM, PRN_HLOG, PRN_VLOG };
private static final int GG_INPUTFILE = GG_OFF_PATHFIELD + PR_INPUTFILE;
private static final int GG_PROPERTY = GG_OFF_CHOICE + PR_PROPERTY;
private static final int GG_CHANNEL = GG_OFF_CHOICE + PR_CHANNEL;
private static final int GG_DATALEN = GG_OFF_CHOICE + PR_DATALEN;
private static final int GG_ANALYSISWIN = GG_OFF_CHOICE + PR_ANALYSISWIN;
private static final int GG_SUM = GG_OFF_CHECKBOX + PR_SUM;
private static final int GG_HLOG = GG_OFF_CHECKBOX + PR_HLOG;
private static final int GG_VLOG = GG_OFF_CHECKBOX + PR_VLOG;
private static final int GG_DATASET = GG_OFF_OTHER + 0;
private static PropertyArray static_pr = null;
private static Presets static_presets = null;
// private ImageCanvas ggVectorDisplay;
private VectorDisplay ggVectorDisplay;
private DataRecord properties[];
private Point lastPt = null; // X-Hair
private String lastTxt;
private boolean paintCrossHair = false;
private final Cursor xhairCursor = new Cursor( Cursor.CROSSHAIR_CURSOR );
private Axis haxis, vaxis;
private FontMetrics fntMetr;
private static final Color colrCross = new Color( 0x00, 0x00, 0x00, 0x7F );
private static final Color colrTextBg = new Color( 0xFF, 0xFF, 0xFF, 0xA0 );
private final MessageFormat msgHertz = new MessageFormat( "{0,number,0.0} Hz", Locale.US ); // XXX US locale
private final MessageFormat msgDecibel = new MessageFormat( "{0,number,0.0} dB", Locale.US ); // XXX US locale
private final MessageFormat msgPlain = new MessageFormat( "{0,number,0.000}", Locale.US ); // XXX US locale
private final MessageFormat msgDegree = new MessageFormat( "{0,number,0.000} rad", Locale.US ); // XXX US locale
// -------- public methods --------
/**
* !! setVisible() bleibt dem Aufrufer ueberlassen
*/
public StatisticsDlg()
{
super( "Statistics" );
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;
}
// default preset
if( static_presets == null ) {
static_presets = new Presets( getClass(), static_pr.toProperties( true ));
}
presets = static_presets;
pr = (PropertyArray) static_pr.clone();
// -------- init --------
properties = new DataRecord[ NUMPROP ];
for( int i = 0; i < properties.length; i++ ) {
properties[ i ] = null;
}
// -------- build GUI --------
GridBagConstraints con;
PathField ggInputFile;
JComboBox ggProperty, ggChannel, ggDataLen, ggAnalysisWin;
JCheckBox ggSum, ggHLog, ggVLog;
JPanel displayPane;
Box box;
gui = new GUISupport();
con = gui.getGridBagConstraints();
con.insets = new Insets( 1, 2, 1, 2 );
MouseInputAdapter mia = new MouseInputAdapter() {
public void mousePressed( MouseEvent e )
{
redrawCrosshair( e );
}
public void mouseEntered( MouseEvent e )
{
ggVectorDisplay.setCursor( xhairCursor );
}
public void mouseExited( MouseEvent e )
{
ggVectorDisplay.setCursor( null );
}
public void mouseDragged( MouseEvent e )
{
redrawCrosshair( e );
}
};
TopPainter tp = new TopPainter() {
public void paintOnTop( Graphics2D g )
{
Dimension dim = ggVectorDisplay.getSize();
synchronized( properties ) {
if( paintCrossHair ) {
g.setColor( colrCross );
g.drawLine( 0, lastPt.y, dim.width - 1, lastPt.y );
g.drawLine( lastPt.x, 0, lastPt.x, dim.height - 1 );
g.setColor( colrTextBg );
g.fillRect( 1, 1, fntMetr.stringWidth( lastTxt ) + 6, fntMetr.getHeight() + 4 );
g.setColor( Color.blue );
g.drawString( lastTxt, 4, fntMetr.getHeight() + 1 );
}
}
}
};
ItemListener il = new ItemListener() {
public void itemStateChanged( ItemEvent e )
{
int ID = gui.getItemID( e );
DataRecord rec;
switch( ID ) {
case GG_PROPERTY:
synchronized( properties ) {
rec = properties[ pr.intg[ PR_PROPERTY ]];
if( rec != null ) {
rec.data = null;
}
}
// THRU
case GG_CHANNEL:
pr.intg[ ID - GG_OFF_CHOICE ] = ((JComboBox) e.getSource()).getSelectedIndex();
redrawDataset();
break;
case GG_SUM:
case GG_HLOG:
case GG_VLOG:
pr.bool[ ID - GG_OFF_CHECKBOX ] = ((JCheckBox) e.getSource()).isSelected();
redrawDataset();
break;
}
}
};
// -------- I/O-Gadgets --------
con.fill = GridBagConstraints.BOTH;
con.gridwidth = GridBagConstraints.REMAINDER;
gui.addLabel( new GroupLabel( "Sound Source", 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( "File name", SwingConstants.RIGHT ));
con.gridheight = 2;
con.gridwidth = GridBagConstraints.REMAINDER;
con.weightx = 0.9;
gui.addPathField( ggInputFile, GG_INPUTFILE, null );
// -------- --------
gui.addLabel( new GroupLabel( "Description", GroupLabel.ORIENT_HORIZONTAL,
GroupLabel.BRACE_NONE ));
ggProperty = new JComboBox();
ggProperty.addItem( "Amp. spectrum" );
ggProperty.addItem( "Power spectrum" );
ggProperty.addItem( "Diff'ed phase spect" );
ggProperty.addItem( "Sample histogram" );
con.weightx = 0.133;
con.gridwidth = 1;
gui.addLabel( new JLabel( "Property", SwingConstants.RIGHT ));
con.weightx = 0.2;
con.gridwidth = 2;
gui.addChoice( ggProperty, GG_PROPERTY, il );
ggDataLen = new JComboBox();
for( int i = 32; i <= 65536; i <<= 1 ) {
ggDataLen.addItem( String.valueOf( i ));
}
con.gridwidth = 1;
con.weightx = 0.2;
gui.addLabel( new JLabel( "Record size", SwingConstants.RIGHT ));
con.weightx = 0.133;
gui.addChoice( ggDataLen, GG_DATALEN, il );
ggAnalysisWin = new JComboBox();
GUISupport.addItemsToChoice( Filter.getWindowNames(), ggAnalysisWin );
con.weightx = 0.133;
gui.addLabel( new JLabel( "Analysis win", SwingConstants.RIGHT ));
con.weightx = 0.2;
con.gridwidth = GridBagConstraints.REMAINDER;
gui.addChoice( ggAnalysisWin, GG_ANALYSISWIN, il );
final Color c1 = getForeground ();
final Color c2 = getBackground ();
ggVectorDisplay = new VectorDisplay();
con.weightx = 1.0;
con.weighty = 1.0;
gui.registerGadget( ggVectorDisplay, GG_DATASET );
ggVectorDisplay.addMouseListener( mia );
ggVectorDisplay.addMouseMotionListener( mia );
ggVectorDisplay.addTopPainter( tp );
ggVectorDisplay.setPreferredSize( new Dimension( 256, 256 )); // XXX
displayPane = new JPanel( new BorderLayout() );
displayPane.add( ggVectorDisplay, BorderLayout.CENTER );
haxis = new Axis( Axis.HORIZONTAL);
vaxis = new Axis( Axis.VERTICAL );
box = Box.createHorizontalBox();
box.add( Box.createHorizontalStrut( vaxis.getPreferredSize().width ));
box.add( haxis );
displayPane.add( box , BorderLayout.NORTH );
displayPane.add( vaxis, BorderLayout.WEST );
gui.addGadget( displayPane, -1 );
ggChannel = new JComboBox();
ggChannel.addItem( "Left" );
ggChannel.addItem( "Right" );
ggChannel.addItem( "Sum" );
ggChannel.addItem( "Difference" );
con.weightx = 0.15;
con.weighty = 0.0;
con.gridwidth = 2;
gui.addLabel( new JLabel( "View channel(s)", SwingConstants.RIGHT ));
con.weightx = 0.1;
con.gridwidth = 1;
gui.addChoice( ggChannel, GG_CHANNEL, il );
ggSum = new JCheckBox( "Integ." );
con.weightx = 0.25;
gui.addCheckbox( ggSum, GG_SUM, il );
ggHLog = new JCheckBox( "Log h.scale" );
con.weightx = 0.25;
gui.addCheckbox( ggHLog, GG_HLOG, il );
ggVLog = new JCheckBox( "Log v.scale" );
con.weightx = 0.25;
con.gridwidth = GridBagConstraints.REMAINDER;
gui.addCheckbox( ggVLog, GG_VLOG, il );
initGUI( this, FLAGS_PRESETS | FLAGS_PROGBAR, gui );
Font fnt = getFont();
fntMetr = getComponent().getFontMetrics(fnt);
}
/**
* 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, ch;
long progOff, progLen;
float max;
AudioFile inF = null;
AudioFileDescr inStream;
File tempFile = null;
FloatFile floatF = null;
int inLength;
int inChanNum;
int off;
int chunkLength;
// int normFactor;
int framesRead;
int totalInSamples;
float inBuf[][];
float procBuf[] = null;
float rotBuf[] = null;
float convBuf1[];
DataRecord rec;
float data[];
float data2[];
int dataLen;
int frameSize, stepSize;
float win[] = null;
int support;
int propertyID;
long dispTime, dispDelta;
// Param ampRef = new Param( 1.0, Param.ABS_AMP ); // transform-Referenz
java.util.List markers;
topLevel: try {
synchronized( properties ) {
propertyID = pr.intg[ PR_PROPERTY ];
rec = properties[ propertyID ];
properties[ propertyID ] = null;
if( rec != null ) {
rec.tempFile.delete();
rec.data = null;
rec.tempFile = null;
}
}
// ---- open files ----
inF = AudioFile.openAsRead( new File( pr.text[ PR_INPUTFILE ]));
inStream = inF.getDescr();
inChanNum = inStream.channels;
inLength = (int) inStream.length;
totalInSamples = inLength * inChanNum;
// this helps to prevent errors from empty files!
if( totalInSamples <= 0 ) throw new EOFException( ERR_EMPTY );
// .... check running ....
if( !threadRunning ) break topLevel;
// ---- preparations ----
tempFile = IOUtil.createTempFile();
floatF = new FloatFile( tempFile, GenericFile.MODE_OUTPUT );
dataLen = 1 << (pr.intg[ PR_DATALEN ] + 5);
data = new float[ dataLen ];
data2 = new float[ dataLen ];
frameSize = dataLen;
stepSize = frameSize;
support = 0;
rec = new DataRecord();
// rec.data = data2;
rec.tempFile= tempFile;
rec.afd = inStream;
switch( pr.intg[ PR_PROPERTY ]) {
case PROP_PHSPECT:
markers = (java.util.List) inStream.getProperty( AudioFileDescr.KEY_MARKERS );
if( markers != null ) {
i = Marker.find( markers, FIRDesignerDlg.MARK_SUPPORT, 0 );
if( i >= 0 ) { // impulse file contains "support" marker ==> adjust rotation so we get zero phase
support = (int) ((Marker) markers.get( i )).pos;
}
}
rotBuf = new float[ frameSize << 1 ];
// THRU
case PROP_ASPECT:
case PROP_PSPECT:
frameSize <<= 1;
// stepSize >>= 1; // 4x overlap
win = Filter.createWindow( dataLen, pr.intg[ PR_ANALYSISWIN ]);
procBuf = new float[ frameSize + 2 ];
break;
}
inBuf = new float[ inChanNum ][ frameSize + 2 ];
// clear buffers
Util.clear( data );
Util.clear( inBuf );
progOff = 0;
progLen = ((long) inLength << 1) + dataLen;
dispDelta = 10000; // first prelim. display after 10 sec.
dispTime = System.currentTimeMillis() + dispDelta;
// ---- calc ----
// normFactor = 0;
off = 0;
chunkLength = Math.min( frameSize, inLength );
inF.readFrames( inBuf, 0, chunkLength );
framesRead = chunkLength;
progOff += chunkLength;
// .... progress ....
setProgression( (float) progOff / (float) progLen );
// .... check running ....
if( !threadRunning ) break topLevel;
do {
switch( pr.intg[ PR_PROPERTY ]) {
case PROP_ASPECT: // XXX
case PROP_PSPECT:
case PROP_PHSPECT:
for( ch = 0; ch < 1; ch++ ) {
// so kopieren, dass chronologie stimmt wg. windowing
System.arraycopy( inBuf[ ch ], off, procBuf, 0, frameSize - off );
System.arraycopy( inBuf[ ch ], 0, procBuf, frameSize - off, off );
for( i = 1, j = dataLen; j < frameSize-1; i++, j++ ) {
procBuf[ j ] *= win[ i ];
}
procBuf[ j ] = 0.0f;
for( i = 0, j = dataLen - 1; j >= 0; i++, j-- ) {
procBuf[ j ] *= win[ i ];
}
if( pr.intg[ PR_PROPERTY ] == PROP_PHSPECT ) { // XXX
Util.rotate( procBuf, frameSize, rotBuf, -((support - off) % frameSize) );
}
Fourier.realTransform( procBuf, frameSize, Fourier.FORWARD );
switch( pr.intg[ PR_PROPERTY ]) {
case PROP_ASPECT: // XXX
Fourier.rect2Polar( procBuf, 0, procBuf, 0, frameSize + 2 );
for( i = 0, j = 0; i < dataLen; i++, j += 2 ) {
data[ i ] += procBuf[ j ];
}
// normFactor++;
break;
case PROP_PSPECT:
for( i = 0, j = 0; i < dataLen; i++ ) {
data[ i ] += (float) Math.abs( (double) procBuf[ j++ ] * (double) procBuf[ j++ ]);
}
break;
case PROP_PHSPECT:
Fourier.rect2Polar( procBuf, 0, procBuf, 0, frameSize + 2 );
Fourier.unwrapPhases( procBuf, 0, procBuf, 0, frameSize + 2 );
for( i = 0, j = 1; i < dataLen; i++ ) {
k = j + 2;
data[ i ] += procBuf[ k ] - procBuf[ j ];
j = k;
}
break;
}
}
break;
case PROP_ELONG: // XXX
for( ch = 0; ch < 1; ch++ ) {
convBuf1 = inBuf[ ch ];
for( i = 0; i < chunkLength; i++ ) {
// j = (int) ((1.0 + Math.log( Math.max( 1.192092896e-07, Math.abs( convBuf1[ i ]))) / 15.94238515) *
// (dataLen - 1) + 0.5f);
j = (int) (Math.abs( convBuf1[ i ]) * (dataLen - 1) + 0.5f);
data[ Math.min( dataLen - 1, j )]++;
}
}
break;
}
progOff += chunkLength;
// .... progress ....
setProgression( (float) progOff / (float) progLen );
// .... read next chunk ....
chunkLength = Math.min( stepSize, inLength - framesRead );
inF.readFrames( inBuf, off, chunkLength );
framesRead += chunkLength;
progOff += chunkLength;
// .... progress ....
setProgression( (float) progOff / (float) progLen );
// .... check running ....
if( !threadRunning ) break topLevel;
// zero-padding
if( chunkLength < stepSize ) {
for( ch = 0; ch < inChanNum; ch++ ) {
convBuf1 = inBuf[ ch ];
for( i = chunkLength, j = i + off; i < stepSize; i++, j++ ) {
convBuf1[ j ] = 0.0f;
}
}
}
off = (off + stepSize) % frameSize;
// ---- update display after increasing time deltas and after last chunk ----
if( (System.currentTimeMillis() > dispTime) || (chunkLength == 0) ) {
System.arraycopy( data, 0, data2, 0, dataLen );
max = 0.0f;
for( i = 0; i < dataLen; i++ ) {
if( data2[ i ] > max ) {
max = data2[ i ];
}
}
if( max > 0.0f ) {
for( i = 0; i < dataLen; i++ ) {
data2[ i ] /= max;
}
}
// calcMinMax( rec );
floatF.seekFloat( 0 );
floatF.writeFloats( data2, 0, dataLen );
floatF.flush();
synchronized( properties ) {
properties[ propertyID ] = rec;
rec.data = null; // force reload
redrawDataset();
}
dispDelta <<= 1;
dispTime += dispDelta;
}
} while( threadRunning && (chunkLength > 0) );
// .... check running ....
if( !threadRunning ) break topLevel;
// rec.data = data2;
floatF.close();
floatF = null;
inF.close();
inF = null;
inStream = null;
progOff += dataLen;
// .... progress ....
setProgression( (float) progOff / (float) progLen );
// .... check running ....
if( !threadRunning ) break topLevel;
// ---- Finish ----
// System.out.println( "progOff "+progOff+"; progLen "+progLen );
} // topLevel
catch( IOException e1 ) {
setError( e1 );
}
catch( OutOfMemoryError e2 ) {
inBuf = null;
procBuf = null;
rotBuf = null;
convBuf1 = null;
inStream = null;
data = null;
data2 = null;
win = null;
System.gc();
setError( new Exception( ERR_MEMORY ));
}
// ---- cleanup (topLevel) ----
if( inF != null ) {
inF.cleanUp();
}
if( floatF != null ) {
floatF.cleanUp();
if( getError() != null ) tempFile.delete();
}
}
// -------- private methods --------
protected void redrawDataset()
{
DataRecord rec = properties[ pr.intg[ PR_PROPERTY ]];
FloatFile floatF = null;
boolean needsReload;
int decimate = 0;
int i, j, dispLen, dataLen;
float[] dispBuf, data;
float f1, max = Float.NEGATIVE_INFINITY, min = Float.POSITIVE_INFINITY;
double d2, d3, log, scale, weight, offset, fmin, fmax, fc = 1.0, f0;
double decibelWeight = 20.0 / Constants.ln10;
synchronized( properties ) {
if( rec != null ) {
needsReload = (rec.sum && !pr.bool[ PR_SUM ]) ||
(rec.hlog && !pr.bool[ PR_HLOG ]) ||
(rec.vlog && !pr.bool[ PR_VLOG ]) ||
(pr.bool[ PR_SUM ] &&
((rec.hlog != pr.bool[ PR_HLOG ]) ||
(rec.vlog != pr.bool[ PR_VLOG ]))) ||
((rec.hlog != pr.bool[ PR_HLOG ]) && pr.bool[ PR_VLOG ]);
if( needsReload || (rec.data == null) ) {
try {
floatF = new FloatFile( rec.tempFile, GenericFile.MODE_INPUT );
rec.sum = false;
rec.hlog = false;
rec.vlog = false;
rec.data = new float[ (int) floatF.getSize() ];
floatF.seekFloat( 0 );
floatF.readFloats( rec.data, 0, rec.data.length );
floatF.close();
// calcMinMax( rec );
}
catch( Exception e1 ) {
System.err.println( e1.getLocalizedMessage() );
rec.data = null;
if( floatF != null ) {
floatF.cleanUp();
floatF = null;
}
}
}
// adjust + display dataset
if( rec.data != null ) {
data = rec.data;
dataLen = data.length;
// sum
if( pr.bool[ PR_SUM ] && !rec.sum ) {
d2 = 0.0;
for( i = 0; i < dataLen; i++ ) {
d2 += data[ i ];
data[ i ] = (float) d2;
}
rec.sum = true;
}
if( pr.bool[ PR_HLOG ] && !rec.hlog ) {
dispLen = 2049; // 4097;
dispBuf = new float[ dispLen ];
switch( pr.intg[ PR_PROPERTY ]) {
case PROP_ELONG:
fc = Math.sqrt( 0.5 ); // gewuenschte centerfreq
fmin = 0.0001; // gewuenschte min. freq
fmax = 1.0; // oberste freq.
break;
default:
fc = 1000.0; // gewuenschte centerfreq
fmin = 16.0; // 16.0; // gewuenschte min. freq
fmax = rec.afd.rate/2; // oberste freq.
break;
}
f0 = fc*fc / fmax;
log = Math.log( fmax/f0 );
offset = Math.log( fmin/f0 ) / log;
weight = (1.0 - offset) / (dispLen - 1);
scale = f0 / fmax * (dataLen - 1);
for( j = 0; j < dispLen; j++ ) {
d2 = Math.exp( (j * weight + offset) * log );
d3 = d2 * scale;
i = ((int) d3);
d2 = d3 % 1.0;
f1 = data[ i ];
d3 = f1 * (1.0 - d2);
if( i+1 < dataLen ) {
f1 = data[ i+1 ];
f1 = (float) (d3 + f1 * d2);
} else {
f1 = (float) d3;
}
dispBuf[ j ] = f1;
if( f1 > max ) max = f1;
if( f1 < min ) min = f1;
}
} else {
for( i = 4096; i < dataLen; i <<= 1, decimate++ ) ;
dispLen = (dataLen >> decimate) + 1;
dispBuf = new float[ dispLen ];
for( i = 0, j = 0, decimate = 1 << decimate; i < dataLen; i += decimate, j++ ) {
f1 = data[ i ];
dispBuf[ j ] = f1;
if( f1 > max ) max = f1;
if( f1 < min ) min = f1;
}
fmin = 0.0;
fmax = rec.afd.rate/2;
}
if( pr.bool[ PR_VLOG ] && !rec.vlog ) {
for( j = 0; j < dispLen; j++ ) {
dispBuf[ j ] = (float) (decibelWeight * Math.log( Math.max( 1.0e-8, dispBuf[ j ])));
}
min = (float) (decibelWeight * Math.log( Math.max( 1.0e-8, min )));
max = (float) (decibelWeight * Math.log( Math.max( 1.0e-8, max )));
if( (max - min) >= 40.0 ) { // round to multiples of 6 dB for high dynamics
max = (float) (Math.ceil( max / 6.0 ) * 6.0);
min = (float) (Math.floor( min / 6.0 ) * 6.0);
} else if( (max - min) >= 20.0 ) { // multiples of 3 dB for medium dynamics
max = (float) (Math.ceil( max / 3.0 ) * 3.0);
min = (float) (Math.floor( min / 3.0 ) * 3.0);
} else { // multiples of 1 dB elsewise
max = (float) (Math.ceil( max / 1.0 ) * 1.0);
min = (float) (Math.floor( min / 1.0 ) * 1.0);
}
} else {
max = (float) (Math.ceil( max * 10.0 ) / 10.0);
min = (float) (Math.floor( min * 10.0 ) / 10.0);
}
if( pr.bool[ PR_HLOG ] ) {
// if( pr.bool[ PR_VLOG ]) {
// rec.space = VectorSpace.createLogSpace( fmin, fmax, 1000.0, min, max, (min+max)/2, null, null, null, null );
// } else {
rec.space = VectorSpace.createLogLinSpace( fmin, fmax, fc, min, max, null, null, null, null );
// }
} else {
// if( pr.bool[ PR_VLOG ]) {
// rec.space = VectorSpace.createLinLogSpace( fmin, fmax, min, max, (min+max)/2, null, null, null, null );
// } else {
rec.space = VectorSpace.createLinSpace( fmin, fmax, min, max, null, null, null, null );
// }
}
ggVectorDisplay.setMinMax( min, max );
ggVectorDisplay.setVector( this, dispBuf );
haxis.setSpace( rec.space );
vaxis.setSpace( rec.space );
}
}
}
}
protected void redrawCrosshair( MouseEvent e )
{
Dimension dim = ggVectorDisplay.getSize();
int x = e.getX();
int y = e.getY();
int propertyID;
DataRecord rec;
String xTxt, yTxt;
int dataLen;
double dx, dy;
float[] v;
synchronized( properties ) {
propertyID = pr.intg[ PR_PROPERTY ];
rec = properties[ propertyID ];
paintCrossHair = false;
if( (rec != null) && (rec.data != null) && !e.isAltDown() ) {
v = ggVectorDisplay.getVector();
dataLen = v.length;
if( (x >= 0) && (y >= 0) && (x < dim.width) && (y < dim.height) ) {
if( e.isShiftDown() ) {
dy = rec.space.vUnityToSpace( 1.0 - (double) y / (dim.height - 1) );
dx = rec.space.hUnityToSpace( (double) x / (dim.width - 1) );
} else {
x = (int) ((double) x / (dim.width - 1) * (dataLen - 1) + 0.5);
dy = v[ x ];
dx = rec.space.hUnityToSpace( (double) x / (dataLen - 1) );
y = (int) ((1.0 - rec.space.vSpaceToUnity( dy )) * (dim.height - 1) + 0.5);
x = e.getX(); // (int) (rec.space.hSpaceToUnity( dx ) * (dim.width - 1) + 0.5);
}
lastPt = new Point( x, y );
if( pr.bool[ PR_VLOG ] ) {
yTxt = msgDecibel.format( new Object[] { new Double( dy )});
} else if( !pr.bool[ PR_SUM ] && propertyID == PROP_PHSPECT ) {
yTxt = msgDegree.format( new Object[] { new Double( dy )});
} else {
yTxt = msgPlain.format( new Object[] { new Double( dy )});
}
switch( propertyID ) {
case PROP_ASPECT:
case PROP_PSPECT:
case PROP_PHSPECT:
xTxt = msgHertz.format( new Object[] { new Double( dx )});
break;
default:
case PROP_ELONG:
xTxt = msgPlain.format( new Object[] { new Double( dx )});
break;
}
lastTxt = yTxt + " @ " + xTxt;
paintCrossHair = true;
}
}
}
ggVectorDisplay.repaint();
}
// -------- interne DataRecord-Klasse --------
protected static class DataRecord
{
private File tempFile = null;
private float[] data = null;
// private float dataMin = 0.0f;
// private float dataRange = 0.0f;
private boolean sum = false;
private boolean hlog = false;
private boolean vlog = false;
private AudioFileDescr afd = null;
private VectorSpace space;
} // class DataRecord
}