/* * Operator.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.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.session.SpectPatch; import de.sciss.fscape.spect.SpectFrame; import de.sciss.fscape.spect.SpectStreamSlot; import de.sciss.fscape.util.Slots; import java.awt.*; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.io.IOException; import java.io.SyncFailedException; import java.rmi.NotBoundException; import java.util.Enumeration; import java.util.Map; import java.util.NoSuchElementException; import java.util.Properties; import java.util.TreeMap; import java.util.Vector; /** * Operator mit Basisfunktionen */ public class Operator implements Runnable, Slots, Cloneable, Transferable { // -------- public variables -------- public static final String PACKAGE = "de.sciss.fscape.op"; public static DataFlavor flavor = null; // DataFlavor representing this class public static final int GUI_PREFS = 0; // fuer createGUI() public static final String PRN_SPECTRO = "Spectrogram"; // Property-Keynames public static final int FLAGS_ALIAS = 0x01; // Operator is an Alias-Object public static final int FLAGS_BYPASS = 0x02; // Operator is bypassed /** * wird vom Hauptthread gesetzt, wenn der Thread beendet werden soll * darf nur vom Document veraendert werden */ public boolean threadDead = true; /** * wird vom Hauptthread gesetzt, wenn der Thread pausieren soll * der Thread darf erst weiterarbeiten, wenn threadPaused wieder false ist * darf nur vom Document veraendert werden */ public boolean threadPaused = false; /** * wird vom Operator gesetzt, wenn er de facto im Pause-Modus angekommen ist */ public boolean threadPausing = false; /** * wird vom Hauptthread gesetzt, wenn der Thread gestartet wird * beim Beenden der run() Methode muss der Operator owner.operatorTerminated() aufrufen! */ public SpectPatch owner = null; // -------- private variables -------- /* * Subclassen sollten setID und setName * ausfuehren */ protected OpIcon icon; /* * Subclassen sollten addElement() * ausfuehren */ protected Vector<SpectStreamSlot> slots; // verwaltet die SpectStreamSlots (in + out) protected Vector<Operator> aliases; // alle Alia des Operators protected Operator original = null; // Original eines Aliases protected static final String ERR_ALIASSYNC = "Original is of different type"; protected static final String ERR_ALREADYALIAS= "Object is already an alias"; private static TreeMap<String, String> OPERATORS; // values sind alle Op-Klassennamen, keys deren "echte" Namen (String) private static DataFlavor flavors[] = null; // alle erlaubten DataFlavors protected boolean disposed = false; protected static final String ERR_DISPOSED = "Operator disposed"; /* * Operatoren sollten statische Variablen fuer den * Operatoren-Namen, Presets und Preferences haben. * im Konstruktor sollten die statischen Variablen * in diese Instanzvariablen uebertragen werden! */ protected String opName = null; // = class name ohne .class (INTERNAL) protected Presets presets = null; // call Operator.getDefaultPresets() when creating this! protected Prefs prefs = null; // call Operator.getDefaultPrefs() when creating this! /* * Subclassen muessen dieses pr ueberschreiben und static_pr.clone() in superPr eintragen! */ protected PropertyArray pr; protected static PropertyArray op_static_pr = null; // Properties (defaults) private static final int PR_SPECTRO = 0; // Array-Indices: pr.bool private static final int PR_FLAGS = 0; // pr.intg protected static final String PRN_FLAGS = "Flags"; // Property-Keynames private static final boolean prBool[] = { false }; private static final String prBoolName[] = { PRN_SPECTRO }; private static final int prIntg[] = { 0 }; private static final String prIntgName[] = { PRN_FLAGS }; /* * Hier wird der Laufzeit-Fehler als Text festgehalten */ private String threadError = null; /* * geschaetzter Fortschritt 0...100% * darf nur vom Operator veraendert werden */ private float threadProgress = 0; // -------- static constructor -------- static { OPERATORS = new TreeMap<String, String>(); } // -------- public methods -------- public Operator() { if (op_static_pr == null) { op_static_pr = new PropertyArray(); op_static_pr.bool = prBool; op_static_pr.boolName = prBoolName; op_static_pr.intg = prIntg; op_static_pr.intgName = prIntgName; } slots = new Vector<SpectStreamSlot>(); aliases = new Vector<Operator>(); // data flavors if (Operator.flavor == null) { Operator.flavor = new DataFlavor(getClass(), "Operator"); } } /** * Liefert eine Hashtable mit allen verfuegbaren Ops * * @return keys = "richtige" Namen; values = Klassennamen */ public static Map getOperators() { if (OPERATORS.isEmpty()) { // XXX spaeter die Liste extern nachladen! OPERATORS.put("Input file", "InputOp"); OPERATORS.put("Output file", "OutputOp"); OPERATORS.put("Analyse", "AnalysisOp"); OPERATORS.put("Synthesize", "SynthesisOp"); OPERATORS.put("Flip freq", "FlipFreqOp"); OPERATORS.put("Log freq", "LogFreqOp"); OPERATORS.put("Splitter", "SplitterOp"); OPERATORS.put("Unitor", "UnitorOp"); OPERATORS.put("Mono2Stereo", "Mono2StereoOp"); OPERATORS.put("Smear", "SmearOp"); OPERATORS.put("Zoom", "ZoomOp"); OPERATORS.put("Shrink", "ShrinkOp"); OPERATORS.put("Envelope", "EnvOp"); OPERATORS.put("Cepstral", "CepstralOp"); OPERATORS.put("Convolve", "ConvOp"); OPERATORS.put("Contrast", "ContrastOp"); OPERATORS.put("Extrapolate", "ExtrapolateOp"); OPERATORS.put("Tarnish", "TarnishOp"); OPERATORS.put("Percussion", "PercussionOp"); OPERATORS.put("Mindmachine", "MindmachineOp"); OPERATORS.put("Amp Env", "AmpEnvOp"); } return OPERATORS; } /** * Besorgt die Parameter des Operators in Form eines PropertyArray Objekts */ public PropertyArray getPropertyArray() { return pr; } /** * Liefert die Presets des Operators * diese sind statisch d.h. fuer alle Operatoren derselben Klasse identisch */ public Presets getPresets() { return presets; } /** * Liefert die Preferences des Operators * diese sind statisch d.h. fuer alle Operatoren derselben Klasse identisch */ public Prefs getPrefs() { return prefs; } /** * Liefert Flags wie FLAGS_ALIAS oder FLAGS_BYPASS */ public int getFlags() { return pr.superPr.intg[ PR_FLAGS ]; } /** * Alle vom Operator belegten Ressourcen freigeben * (danach nicht mehr benutzen!) */ public void dispose() { if (original != null) { original.forgetAlias(this); } disposed = true; } // -------- Alias-bezogene methods -------- /** * Liefert eine Auflistung aller Alia des Operators (possibly empty) */ public Enumeration getAliases() { return aliases.elements(); } /** * Liefert des Original eines Aliases * * @return null, falls Operator kein Alias ist */ public Operator getOriginal() { return original; } /** * Erklaert diesen Operator zu einem Alias * * SyncFailedException, wenn "original" nicht dieselben Operator-Subklasse * SlotAlreadyConnectedException, wenn dieser Op schon ein Alias ist */ public void turnIntoAlias(Operator orig) throws SyncFailedException, SlotAlreadyConnectedException { while (orig.getOriginal() != null) { orig = orig.getOriginal(); } if (orig.getClass() != this.getClass()) { throw new SyncFailedException(ERR_ALIASSYNC); } if (original != null) { throw new SlotAlreadyConnectedException(ERR_ALREADYALIAS); } original = orig; orig.registerAlias(this); pr.superPr.intg[PR_FLAGS] |= FLAGS_ALIAS; ((OpIcon) getIcon()).operatorFlagsChanged(pr.superPr.intg[PR_FLAGS]); } /** * Formt ein Alias in einen eigenstaendingen Operator um * (wenn dieser Op kein Alias war, passiert nichts) */ public void turnIntoGenuine() { PropertyArray oldSuper; if (original != null) { oldSuper = pr.superPr; pr = new PropertyArray(original.getPropertyArray()); pr.superPr = oldSuper; original.forgetAlias(this); this.original = null; pr.superPr.intg[PR_FLAGS] &= ~FLAGS_ALIAS; ((OpIcon) getIcon()).operatorFlagsChanged(pr.superPr.intg[PR_FLAGS]); } } /** * Registriert ein Alias */ protected void registerAlias(Operator aliasOp) { aliases.addElement(aliasOp); } /** * Meldet einen Alias ab (vor dessen Aufloesung) */ protected void forgetAlias(Operator aliasOp) { aliases.removeElement(aliasOp); } // -------- GUI methods -------- /** * MUSS VON SUBCLASS UEBERLAGERT WERDEN */ public PropertyGUI createGUI(int type) { return new PropertyGUI("lbNothing here"); } /** * Subclassen sollten dies auch benutzen statt direkt auf icon zuzugreifen! */ public Component getIcon() { return icon; } // -------- Runnable methods + Assoziiertes -------- /** * Falls der Operator seinen Thread wg. eines Fehlers beendet hat * (getIcon().isSelected() == OpIcon.STATE_ERROR) * kann hiermit die Fehlermeldung ermittelt werden * * @return null, wenn keine Meldung vorliegt ("Uns liegen zur Zeit keine Meldungen vor") */ public String getError() { return threadError; } /** * Runtime-Fehlermessage setzen */ public void setError(String threadError) { this.threadError = threadError; } /** * Fortschritt des Threads 0...100 % */ public float getProgress() { return threadProgress; } /** * Fortschritt des Threads setzen */ public void setProgress(float progress) { this.threadProgress = progress; } /* * run() initialisieren; muss von subclassen zuerst aufgerufen werden! */ protected void runInit() { PropertyArray oldSuper; setProgress(0.0f); if (original != null) { // Aliase: Daten abgleichen oldSuper = pr.superPr; pr = new PropertyArray(original.getPropertyArray()); pr.superPr = oldSuper; } } /* * aufzurufen vor der Hauptschleife * ; ggf. werden Spectrogramme eingerichtet */ protected void runSlotsReady() { Enumeration slts; SpectStreamSlot slot; if( pr.superPr.bool[ PR_SPECTRO ]) { slts = getSlots( Slots.SLOTS_WRITER ).elements(); while( slts.hasMoreElements() ) { slot = (SpectStreamSlot) slts.nextElement(); slot.createSpectrogram(); } } } /* * Operator stoppen * UNMITTELBAR NACH RUECKKEHR DIESER METHODE SOLLTE run() VERLASSEN WERDEN! * * @param e null, wenn ordnungsgemaess beendet; sonst die Fehler-Exception */ protected void runQuit(Exception e) { SpectStreamSlot slot; Enumeration slts = getSlots(Slots.SLOTS_ANY).elements(); threadDead = true; while (slts.hasMoreElements()) { slot = (SpectStreamSlot) slts.nextElement(); slot.cleanUp(); } if (e == null) { setProgress(1.0f); getIcon().repaint(); } else { ((OpIcon) getIcon()).setSelected(OpIcon.STATE_ERROR); setError(e.getMessage()); System.out.println(icon.getName() + ": aborted because of: " + threadError); } owner.operatorTerminated(this); } /* * prueft, ob der Operator pausieren soll und tut dies ggf. */ protected synchronized void runCheckPause() { try { if (threadPaused && !threadDead) { threadPausing = true; owner.operatorPaused(this); while (threadPaused && !threadDead) { wait(); } threadPausing = false; } } catch (InterruptedException e) { threadPausing = false; } } /* * Updated Icon-Progressindikator * * @param slot Slot, der ein verarbeitetes Frame empfangen hat; darf null sein * @param fr verarbeitetes Frame; darf null sein */ protected void runFrameDone(SpectStreamSlot slot, SpectFrame fr) { float progress = SpectStreamSlot.progress(slot); if (progress - getProgress() >= 0.04f) { setProgress(progress); ((OpIcon) getIcon()).paintProgress(null); } } public void run() { runInit(); runSlotsReady(); runQuit(null); } public synchronized void runStop() { threadPaused = false; threadDead = true; notify(); } public synchronized void runPause(boolean state) { threadPaused = state; notify(); } // -------- Slots methods -------- /** * @param filter Jedes gesetzte Flag im filter-Parameter muss im Suchobject gesetzt sein! */ public Vector<SpectStreamSlot> getSlots(int filter) { Vector<SpectStreamSlot> fltSlots = new Vector<SpectStreamSlot>(); SpectStreamSlot slot; for (int i = 0; i < slots.size(); i++) { slot = slots.elementAt(i); if ((slot.getFlags() & filter) == filter) { fltSlots.addElement(slot); } } return fltSlots; } /** * @return null bei Fehler */ public SpectStreamSlot getSlot(String name) { SpectStreamSlot slot; for (int i = 0; i < slots.size(); i++) { slot = slots.elementAt(i); if (slot.toString().equals(name)) return slot; } return null; } public void linkTo(String thisName, Slots destSlots, String destName) throws SyncFailedException, SlotAlreadyConnectedException, NoSuchElementException { throw new NoSuchElementException(); } public void divorceFrom(String thisName, Slots srcSlots, String srcName) throws NotBoundException, NoSuchElementException { throw new NoSuchElementException(); } // -------- Cloneable methods -------- /** * @return NB: MAY RETURN NULL IN CASE OF ERROR ! */ public Object clone() { if (disposed) return null; Operator op = null; try { op = this.getClass().newInstance(); op.pr = (PropertyArray) this.getPropertyArray().clone(); if (getOriginal() != null) { op.turnIntoAlias(getOriginal()); } ((OpIcon) op.getIcon()).setName(((OpIcon) this.getIcon()).getName()); return op; } catch (InstantiationException ignored) { } catch (IllegalAccessException ignored) { } catch (SyncFailedException e3) { op.dispose(); } catch (SlotAlreadyConnectedException e4) { op.dispose(); } return null; } // -------- Transferable methods -------- public DataFlavor[] getTransferDataFlavors() { if (flavors == null) { DataFlavor iconFlavors[] = icon.getTransferDataFlavors(); flavors = new DataFlavor[iconFlavors.length + 1]; flavors[0] = Operator.flavor; for (int i = 0; i < iconFlavors.length; i++) { flavors[i + 1] = iconFlavors[i]; } } return flavors; } public boolean isDataFlavorSupported(DataFlavor fl) { DataFlavor flavs[] = getTransferDataFlavors(); for (int i = 0; i < flavs.length; i++) { if (flavs[i].equals(fl)) return true; } return false; } public Object getTransferData(DataFlavor fl) throws UnsupportedFlavorException, IOException { if (fl.equals(Operator.flavor)) { if (disposed) throw new IOException(ERR_DISPOSED); return this; } else { return icon.getTransferData(fl); } } // -------- private methods -------- /* * this method should be invoked by subclass */ protected static Properties getDefaultPrefs() { Properties defPrefs = new Properties(); defPrefs.put(OpPrefs.PR_EDITDIM, "256,256"); return defPrefs; } }