/* * CepstralOp.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.FIRDesignerDlg; import de.sciss.fscape.gui.FilterBox; 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.Filter; import de.sciss.fscape.util.Param; import de.sciss.fscape.util.Slots; import de.sciss.fscape.util.Util; import de.sciss.io.AudioFileDescr; import java.awt.*; import java.io.EOFException; import java.io.IOException; /** * @version 0.71, 14-Nov-07 */ public class CepstralOp extends Operator { // -------- public variables -------- // -------- private variables -------- protected static final String defaultName = "Cepstral"; protected static Presets static_presets = null; protected static Prefs static_prefs = null; protected static PropertyArray static_pr = null; // Slots protected static final int SLOT_INPUT = 0; protected static final int SLOT_OUTPUT = 1; // Properties (defaults) private static final int PR_LOFREQ = 0; // pr.para private static final int PR_HIFREQ = 1; private static final int PR_QUALITY = 0; // pr.intg private static final String PRN_HIFREQ = "HiFreq"; private static final String PRN_LOFREQ = "LoFreq"; private static final String PRN_QUALITY = "Quality"; private static final int prIntg[] = { FIRDesignerDlg.QUAL_MEDIUM }; private static final String prIntgName[] = { PRN_QUALITY }; private static final Param prPara[] = { null, null }; private static final String prParaName[] = { PRN_LOFREQ, PRN_HIFREQ }; protected static final String ERR_BANDS = "Band# not power of 2"; // -------- public methods -------- public CepstralOp() { 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_HIFREQ ] = new Param( 22050.0, Param.ABS_HZ ); static_pr.para[ PR_LOFREQ ] = new Param( 50.0, Param.ABS_HZ ); 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 = "CepstralOp"; prefs = static_prefs; presets = static_presets; pr = (PropertyArray) static_pr.clone(); // slots slots.addElement( new SpectStreamSlot( this, Slots.SLOTS_READER )); // SLOT_INPUT 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; float f1, f2, phase1, phase2; SpectStreamSlot runInSlot; SpectStreamSlot runOutSlot; SpectStream runInStream = null; SpectStream runOutStream; SpectFrame runInFr = null; SpectFrame runOutFr = null; // Ziel-Frame Berechnung int skip, srcBands, srcSize, fftSize, fullFFTsize; float loFreq, hiFreq; float[] fftBuf, fltBuf; AudioFileDescr tmpStream; FilterBox fltBox; Point fltLength; topLevel: try { // ------------------------------ Input-Slot ------------------------------ runInSlot = slots.elementAt( SLOT_INPUT ); if( runInSlot.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 = runInSlot.getDescr(); // throws InterruptedException initDone = true; } catch( InterruptedException ignored) {} runCheckPause(); } if( threadDead ) break topLevel; // ------------------------------ Output-Slot ------------------------------ runOutSlot = slots.elementAt( SLOT_OUTPUT ); runOutStream = new SpectStream( runInStream ); runOutSlot.initWriter( runOutStream ); // ------------------------------ Vorberechnungen ------------------------------ // freqSpacing = (runInStream.hiFreq - runInStream.loFreq) / runInStream.bands; srcBands = runInStream.bands; srcSize = srcBands << 1; loFreq = Math.min( runInStream.smpRate / 2, (float) pr.para[ PR_LOFREQ ].value); hiFreq = Math.max( 0.5f, Math.min( runInStream.smpRate / 2, (float) pr.para[ PR_HIFREQ ].value)); if( loFreq > hiFreq ) { f1 = loFreq; loFreq = hiFreq; hiFreq = f1; } fltBox = new FilterBox(); if( loFreq <= 0.1f ) { fltBox.filterType = FilterBox.FLT_LOWPASS; fltBox.cutOff = new Param( hiFreq, Param.ABS_HZ ); } else if( hiFreq >= runInStream.smpRate/2 ) { fltBox.filterType = FilterBox.FLT_HIGHPASS; fltBox.cutOff = new Param( loFreq, Param.ABS_HZ ); } else { fltBox.filterType = FilterBox.FLT_BANDPASS; fltBox.cutOff = new Param( (loFreq + hiFreq) / 2, Param.ABS_HZ ); fltBox.bandwidth = new Param( hiFreq - loFreq, Param.OFFSET_HZ ); } // System.out.println( "cutoff "+fltBox.cutOff+"; bandw "+fltBox.bandwidth ); tmpStream = new AudioFileDescr(); // unflexibel tmpStream.rate = runInStream.smpRate; fltLength = fltBox.calcLength( tmpStream, pr.intg[ PR_QUALITY ]); skip = fltLength.x; // complex support not written out i = fltLength.x + fltLength.y; j = i + srcBands - 1; // length after conv. with input for( fftSize = 2; fftSize < j; fftSize <<= 1 ) ; fullFFTsize = fftSize << 1; fftBuf = new float[ fullFFTsize ]; fltBuf = new float[ fullFFTsize ]; Util.clear( fltBuf ); fltBox.calcIR( tmpStream, pr.intg[ PR_QUALITY ], Filter.WIN_BLACKMAN, fltBuf, fltLength ); for( i = fftSize, j = fullFFTsize; i > 0; ) { fltBuf[ --j ] = 0.0f; // img fltBuf[ --j ] = fltBuf[ --i ]; // real } Util.rotate( fltBuf, fullFFTsize, fftBuf, -(skip<<1) ); // adjust support Fourier.complexTransform( fltBuf, fftSize, Fourier.FORWARD ); // Debug.view( fltBuf, "flt" ); // System.out.println( "SrcSize "+srcSize+"; fltSize "+(fltLength.x+fltLength.y)+"; fftSize "+fftSize ); // ------------------------------ Hauptschleife ------------------------------ runSlotsReady(); mainLoop: while( !threadDead ) { // ---------- Frame einlesen ---------- for( boolean readDone = false; (!readDone) && !threadDead; ) { try { runInFr = runInSlot.readFrame(); // throws InterruptedException readDone = true; runOutFr = runOutStream.allocFrame(); } catch( InterruptedException ignored) {} catch( EOFException e ) { break mainLoop; } runCheckPause(); } if( threadDead ) break mainLoop; // ---------- Process: Ziel-Frame berechnen ---------- for( ch = 0; ch < runOutStream.chanNum; ch++ ) { System.arraycopy( runInFr.data[ ch ], 0, fftBuf, 0, srcSize ); // amp1 = fftBuf[ 0 ]; // fftBuf[ 0 ] = -1.0f; for( i = 0; i < srcSize; i+= 2 ) { f1 = fftBuf[ i ]; // Re(ln z)=ln r if( f1 > 1.266416555e-14f ) { fftBuf[ i ] = (float) (Math.log( f1 )); // (float) (Math.log( f1 ) / 32); } else { fftBuf[ i ] = -32f; // -1.0f; // c. ln( 2^-47 ) / 32 } } Fourier.unwrapPhases( fftBuf, 0, fftBuf, 0, srcSize ); // amp2 = fftBuf[ srcSize - 2 ]; phase1 = fftBuf[ 1 ]; phase2 = fftBuf[ srcSize - 1 ]; f2 = (float) (srcSize - 2); for( i = 0; i < srcSize; ) { f1 = (float) i++ / f2; // fftBuf[ i++ ] += amp1 * (f1 - 1.0f) - amp2 * f1; fftBuf[ i++ ] += phase1 * (f1 - 1.0f) - phase2 * f1; } // for( i = fftSize, j = fftSize; i > 0; i -= 2, j += 2 ) { // neg.freq. complex conjugate // fftBuf[ j ] = fftBuf[ i ]; // fftBuf[ j+1 ] = -fftBuf[ i+1 ]; // } for( i = srcSize; i < fullFFTsize; ) { fftBuf[ i++ ] = 0.0f; } // if( runOutStream.framesWritten < 1 ) { // Debug.view( fftBuf, "pre" ); // } // for( i = 0, f1 = 0.0f; i < srcSize; i++ ) { // f2 = Math.abs( fftBuf[ i ]); // if( f2 > f1 ) { // f1 = f2; // } // } // Util.mult( fftBuf, 0, srcSize, 1.0f / f1 ); Fourier.complexTransform( fftBuf, fftSize, Fourier.FORWARD ); // for( i = 0; i < loBand; ) { // erase below (pos) lo // fftBuf[ i++ ] = 0.0f; // fftBuf[ i++ ] = 0.0f; // } // for( i = hiBand + 2; i < fullFFTsize - hiBand; ) { // erase above (pos) hi / below (neg) hi // fftBuf[ i++ ] = 0.0f; // fftBuf[ i++ ] = 0.0f; // } // for( i = fullFFTsize - loBand; i < fullFFTsize; ) { // erase above (neg) lo // fftBuf[ i++ ] = 0.0f; // fftBuf[ i++ ] = 0.0f; // } Fourier.complexMult( fltBuf, 0, fftBuf, 0, fftBuf, 0, fullFFTsize ); Fourier.complexTransform( fftBuf, fftSize, Fourier.INVERSE ); // Util.mult( fftBuf, 0, srcSize, f1 ); f2 = (float) (srcSize - 2); for( i = 0; i < srcSize; ) { // |exp z|=exp(Re(z)) fftBuf[ i ] = (float) Math.exp( fftBuf[ i ]); // (float) Math.exp( fftBuf[ i ] * 32 ); // fftBuf[ i++ ] = (float) Math.exp( fftBuf[ i ] + amp1 * (1.0f - f1) + amp2 * f1 ); // (float) Math.exp( fftBuf[ i ] * 32 ); f1 = (float) i++ / f2; fftBuf[ i++ ] += phase1 * (1.0f - f1) + phase2 * f1; } // fftBuf[ 0 ] = amp1; System.arraycopy( fftBuf, 0, runOutFr.data[ ch ], 0, srcSize ); // if( runOutStream.framesWritten < 1 ) { // Debug.view( fftBuf, "post" ); // } } // calculation done runInSlot.freeFrame( runInFr ); 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.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; StringBuffer qualJComboBox = new StringBuffer(); if (type != GUI_PREFS) return null; for (int i = 0; i < FIRDesignerDlg.QUAL_NAMES.length; i++) { qualJComboBox.append(",it"); qualJComboBox.append(FIRDesignerDlg.QUAL_NAMES[i]); } gui = new PropertyGUI( "gl"+GroupLabel.NAME_GENERAL+"\n" + "lbHigh frequency;pf"+Constants.absHzSpace+",pr"+PRN_HIFREQ+"\n" + "lbLow frequency;pf"+Constants.absHzSpace+",pr"+PRN_LOFREQ+"\n" + "lbQuality;ch,pr"+PRN_QUALITY+qualJComboBox.toString()+"\n" ); return gui; } }