/*
* SpectFrame.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.spect;
/**
* Gelesene (nicht geklonte) Frames sind READONLY
* (zumindest solange der accessCount > 1 ist) !!!
*/
public class SpectFrame
implements Cloneable
{
// -------- public variables --------
public static final int AMP = 0;
public static final int PHASE = 1;
public static final int FLAGS_UNTOUCHED = 0; // never change since Op use it
public static final int FLAGS_LEFT = 1; // as JComboBox-Item-IDs!
public static final int FLAGS_RIGHT = 2;
public static final int FLAGS_SUM = 3;
/**
* erste Dimension = Kanal-Nr.;
* zweite Dimension = Band-Nr.*2 + Daten-Typ (Amp/Phase);
*/
public float data[][];
/**
* Zahl der gegenwaertigen Nutzer; wenn der Count auf Null geht,
* kann das Frame wieder "recyclet" werden (abfrage nur im synchronized( this )!)
*/
public int accessCount;
// -------- public methods --------
// public void gainAccess();
// public void looseAccess();
// public static clear( Frame fr );
/**
* NOTE: der Inhalt von data ist undefiniert,
* notfalls clear() benutzen!
*
* Als Benutzerzahl wird 1 eingetragen
*/
public SpectFrame( int chanNum, int bands )
{
data = new float[ chanNum ][ bands << 1 ];
accessCount = 1;
}
/**
* Neues Frame wird erzeugt und mit den
* Werten des uebergebenen Frames gefuellt
* (Cloning)
*
* @param flags FLAGS_... erlaubt zusammenmischen der Kanaele
*/
public SpectFrame( SpectFrame src, int flags )
{
this( (flags == FLAGS_UNTOUCHED) ? src.data.length : 1, src.data[ 0 ].length >> 1 );
copyData( src, this, flags );
}
public SpectFrame( SpectFrame src )
{
this( src, FLAGS_UNTOUCHED );
}
/**
* Meldet weiteren Nutzer des Frames an
*/
public void gainAccess()
{
synchronized( this ) {
accessCount++;
}
}
/**
* Meldet Nutzer des Frames ab
*/
public void looseAccess()
{
synchronized( this ) {
accessCount--;
}
}
public Object clone()
{
return new SpectFrame( this, FLAGS_UNTOUCHED );
}
/**
* Loescht die Daten eines Frames
* d.h. Amplitude und Phase werden auf 0.0 gesetzt
*/
public static void clear( SpectFrame fr )
{
for( int i = 0; i < fr.data.length; i++ ) { // channels
for( int j = 0; j < fr.data[ i ].length; j++ ) { // bands
fr.data[ i ][ j ] = 0.0f;
}
}
}
// -------- private methods --------
protected void copyData( SpectFrame src, SpectFrame dest, int flags )
{
double destImg, destReal;
float srcAmp, srcPhase;
int srcStartCh, chanNum;
// try {
switch( flags ) {
case FLAGS_SUM: // Vectoren addieren; zunaechst Polar ==> Rect
for( int i = 0; i < src.data[ 0 ].length; i += 2 ) {
destImg = 0.0;
destReal = 0.0;
for( int j = 0; j < src.data.length; j++ ) {
srcAmp = src.data[ j ][ i + AMP ];
srcPhase = src.data[ j ][ i + PHASE ];
destImg += srcAmp * Math.sin( srcPhase );
destReal += srcAmp * Math.cos( srcPhase );
}
destImg /= src.data.length; // ...und Durchschnitt bilden
destReal /= src.data.length;
dest.data[ 0 ][ i + AMP ] =
(float) Math.sqrt( destImg*destImg + destReal*destReal ); // Rect ==> Polar
dest.data[ 0 ][ i + PHASE] =
(float) Math.atan2( destImg, destReal ); // richtig rum? XXX
}
break;
default:
switch( flags ) {
case FLAGS_LEFT:
srcStartCh = 0;
chanNum = 1;
break;
case FLAGS_RIGHT:
srcStartCh = Math.min( 1, src.data.length - 1 );
chanNum = 1;
break;
default: // FLAGS_UNTOUCHED
srcStartCh = 0;
chanNum = Math.min( src.data.length, dest.data.length );
break;
}
for( int i = 0; i < chanNum; i++ ) { // 1:1 copy
System.arraycopy( src.data[ srcStartCh + i ], 0, dest.data[ i ], 0,
src.data[ srcStartCh + i ].length );
}
break;
}
// }
// catch( IndexOutOfBoundsException e ) {} // Nothing yet XXX
}
}
// class SpectFrame