/* * MindmachineOp.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.op; import de.sciss.fscape.gui.GroupLabel; import de.sciss.fscape.gui.OpIcon; import de.sciss.fscape.gui.PropertyGUI; import de.sciss.fscape.prop.OpPrefs; import de.sciss.fscape.prop.Prefs; import de.sciss.fscape.prop.Presets; import de.sciss.fscape.prop.PropertyArray; import de.sciss.fscape.spect.Fourier; import de.sciss.fscape.spect.SpectFrame; import de.sciss.fscape.spect.SpectStream; import de.sciss.fscape.spect.SpectStreamSlot; import de.sciss.fscape.util.Constants; import de.sciss.fscape.util.Param; import de.sciss.fscape.util.Slots; import de.sciss.fscape.util.Util; import java.io.EOFException; import java.io.IOException; public class MindmachineOp extends Operator { // -------- private variables -------- protected static final String defaultName = "Mindmachine"; protected static Presets static_presets = null; protected static Prefs static_prefs = null; protected static PropertyArray static_pr = null; // Properties (defaults) private static final int PR_ANGLE = 0; // pr.para private static final String PRN_ANGLE = "Angle"; // Slots protected static final int SLOT_INPUT1 = 0; protected static final int SLOT_INPUT2 = 1; protected static final int SLOT_OUTPUT = 2; // Properties (defaults) private static final Param prPara[] = { null }; private static final String prParaName[] = { PRN_ANGLE }; // -------- public methods -------- // public Container createGUI( int type ); public MindmachineOp() { super(); // initialize only in the first instance // preferences laden if( static_prefs == null ) { static_prefs = new OpPrefs( getClass(), getDefaultPrefs() ); } // propertyarray defaults if( static_pr == null ) { static_pr = new PropertyArray(); // static_pr.intg = prIntg; // static_pr.intgName = prIntgName; static_pr.para = prPara; static_pr.para[ PR_ANGLE ] = new Param( 25.0, Param.NONE ); static_pr.paraName = prParaName; static_pr.superPr = Operator.op_static_pr; } // default preset if( static_presets == null ) { static_presets = new Presets( getClass(), static_pr.toProperties( true )); } // superclass-Felder uebertragen opName = "MindmachineOp"; prefs = static_prefs; presets = static_presets; pr = (PropertyArray) static_pr.clone(); // slots slots.addElement( new SpectStreamSlot( this, Slots.SLOTS_READER, "in1" )); // SLOT_INPUT1 slots.addElement( new SpectStreamSlot( this, Slots.SLOTS_READER, "in2" )); // SLOT_INPUT2 slots.addElement( new SpectStreamSlot( this, Slots.SLOTS_WRITER )); // SLOT_OUTPUT // icon // XXX icon = new OpIcon( this, OpIcon.ID_FLIPFREQ, defaultName ); } // -------- Runnable methods -------- public void run() { runInit(); // superclass // Haupt-Variablen fuer den Prozess int ch, i, j, k, m; double d1, d2; SpectStreamSlot[] runInSlot = new SpectStreamSlot[ 2 ]; SpectStreamSlot runOutSlot; SpectStream[] runInStream = new SpectStream[ 2 ]; SpectStream runOutStream; SpectFrame[] runInFr = new SpectFrame[ 2 ]; SpectFrame runOutFr; // Ziel-Frame Berechnung int[] srcBands = new int[ 2 ]; int fftSize, fullFFTsize, complexFFTsize; float[] convBuf1, convBuf2; float[][] fftBuf; float[] win; int readDone, oldReadDone; int[] phase = new int[2]; topLevel: try { // ------------------------------ Input-Slot ------------------------------ for( i = 0; i < 2; i++ ) { runInSlot[i] = slots.elementAt( SLOT_INPUT1 + i ); if( runInSlot[i].getLinked() == null ) { runStop(); // threadDead = true -> folgendes for() wird uebersprungen } // diese while Schleife ist noetig, da beim initReader ein Pause eingelegt werden kann // und die InterruptException ausgeloest wird; danach versuchen wir es erneut for( boolean initDone = false; !initDone && !threadDead; ) { try { runInStream[i] = runInSlot[i].getDescr(); // throws InterruptedException initDone = true; srcBands[i] = runInStream[i].bands; } catch( InterruptedException ignored) {} runCheckPause(); } } if( threadDead ) break topLevel; // ------------------------------ Output-Slot ------------------------------ runOutSlot = slots.elementAt( SLOT_OUTPUT ); runOutStream = new SpectStream( runInStream[0] ); runOutSlot.initWriter( runOutStream ); // ------------------------------ Vorberechnungen ------------------------------ fftSize = srcBands[0] - 1; fullFFTsize = fftSize << 1; complexFFTsize = fullFFTsize << 1; fftBuf = new float[2][ complexFFTsize ]; win = new float[ fullFFTsize ]; d1 = 1.0 / (double) fullFFTsize * Math.PI; for( i = 0; i < fullFFTsize; i++ ) { d2 = Math.cos( i * d1 ); win[ i ] = (float) (d2 * d2); } phase[0] = (int) (pr.para[ PR_ANGLE ].value / 100.0 * fullFFTsize + 0.5) % fullFFTsize; phase[1] = (phase[0] + fftSize) % fullFFTsize; //convBuf2 = fftBuf[0]; //Util.clear( convBuf2 ); //for( k = 0; k < 2; k++ ) { // m = k * fftSize; // for( i = 0; m < fullFFTsize; ) { // convBuf2[ i++ ] += win[ m ]; // convBuf2[ i++ ] += win[ m++ ]; // } // for( m = 0; i < complexFFTsize; ) { // convBuf2[ i++ ] += win[ m ]; // convBuf2[ i++ ] += win[ m++ ]; // } //} //Debug.view( convBuf2, "win overlap" ); // ------------------------------ Hauptschleife ------------------------------ runSlotsReady(); mainLoop: while( !threadDead ) { // ---------- Frame einlesen ---------- for( readDone = 0; (readDone < 2) && !threadDead; ) { oldReadDone = readDone; for( i = 0; i < 2; i++ ) { try { if( runInStream[ i ].framesReadable() > 0 ) { runInFr[ i ] = runInSlot[ i ].readFrame(); readDone++; } } catch( InterruptedException ignored) {} catch( EOFException e ) { break mainLoop; } runCheckPause(); } if (oldReadDone == readDone) { // konnte nix gelesen werden try { Thread.sleep(500); // ...deshalb kurze Pause } catch (InterruptedException ignored) {} // mainLoop wird gleich automatisch verlassen runCheckPause(); } } if( threadDead ) break mainLoop; runOutFr = runOutStream.allocFrame(); // ---------- Process: Ziel-Frame berechnen ---------- for( ch = 0; ch < runOutStream.chanNum; ch++ ) { for( k = 0; k < 2; k++ ) { convBuf1 = runInFr[k].data[ ch ]; convBuf2 = fftBuf[k]; // calculate complex log spectrum : // Re( target ) = Log( Mag( source )) // Im( target ) = Phase( source ) // convBuf1 is already in polar style // -> fftBuf will be in rect style for( i = 0; i <= fullFFTsize; ) { convBuf2[ i ] = (float) Math.log( Math.max( 1.0e-24, convBuf1[ i ])); // Re( target ) i++; convBuf2[ i ] = convBuf1[ i ]; // Im( target ) i++; } // make full spectrum (negative frequencies = conjugate positive frequencies) for( i = fullFFTsize + 2, j = fullFFTsize - 2; i < complexFFTsize; j -= 2 ) { // bug but nice? - 1 convBuf2[ i++ ] = convBuf2[ j ]; convBuf2[ i++ ] = -convBuf2[ j+1 ]; } // cepstrum domain Fourier.complexTransform( convBuf2, fullFFTsize, Fourier.INVERSE ); // window m = phase[k]; for( i = 0; m < fullFFTsize; ) { convBuf2[ i++ ] *= win[ m ]; convBuf2[ i++ ] *= win[ m++ ]; } for( m = 0; i < complexFFTsize; ) { convBuf2[ i++ ] *= win[ m ]; convBuf2[ i++ ] *= win[ m++ ]; } } // mix cepstra convBuf1 = fftBuf[0]; convBuf2 = runOutFr.data[ ch ]; Util.add( fftBuf[1], 0, convBuf1, 0, complexFFTsize ); // back to frequency domain Fourier.complexTransform( convBuf1, fullFFTsize, Fourier.FORWARD ); // calculate real exponential spectrum : // Mag( target ) = Exp( Re( source )) // Phase( target ) = Im( source ) // ->convBuf2 shall be polar style, that makes things easy for( i = 0; i <= fullFFTsize; ) { convBuf2[ i ] = (float) Math.exp( convBuf1[ i ]); i++; convBuf2[ i ] = convBuf1[ i ]; i++; } } // calculation done runInSlot[0].freeFrame( runInFr[0] ); runInSlot[1].freeFrame( runInFr[1] ); for( boolean writeDone = false; (!writeDone) && !threadDead; ) { try { // Unterbrechung runOutSlot.writeFrame( runOutFr ); // throws InterruptedException writeDone = true; runFrameDone( runOutSlot, runOutFr ); runOutStream.freeFrame( runOutFr ); } catch( InterruptedException ignored) {} // mainLoop wird eh gleich verlassen runCheckPause(); } } // end of main loop runInStream[0].closeReader(); runInStream[1].closeReader(); runOutStream.closeWriter(); } // break topLevel catch( IOException e ) { runQuit( e ); return; } catch( SlotAlreadyConnectedException e ) { runQuit( e ); return; } // catch( OutOfMemoryError e ) { // abort( e ); // return; // } runQuit( null ); } // -------- GUI methods -------- public PropertyGUI createGUI(int type) { PropertyGUI gui; if (type != GUI_PREFS) return null; gui = new PropertyGUI( "gl" + GroupLabel.NAME_GENERAL + "\n" + "lbShift;pf" + Constants.unsignedModSpace + ",pr" + PRN_ANGLE + "\n"); return gui; } }