/* * SpectStreamSlot.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; import de.sciss.fscape.gui.Spectrogram; import de.sciss.fscape.op.Operator; import de.sciss.fscape.op.SlotAlreadyConnectedException; import de.sciss.fscape.util.Slots; import java.io.IOException; import java.io.SyncFailedException; import java.rmi.NotBoundException; import java.util.NoSuchElementException; /** * Administration of a spectral stream slot */ public class SpectStreamSlot { // -------- public variables -------- public static final int STATE_UNKNOWN = 0; public static final int STATE_ACTIVE = 1; public static final int STATE_DEAD = 2; public static final int STATE_WAITING = 3; public static final int STATE_DUMMY = 4; // kein Reader verlinkt public int state = STATE_UNKNOWN; // signalisert ggf. Warte-Prozesse // ZUGRIFF NUR IN SYNCHRONIZED( THIS) BLOCK // -------- private variables -------- protected int flags; // SLOTS_... protected String name; // wird zurueckgegeben ueber toString() protected Operator owner; protected Thread ownerThread = null; protected SpectStreamSlot linked = null; protected SpectStream stream = null; protected Spectrogram spectro = null; protected static final String ERR_INTERRUPTED = "Slot was interrupted"; protected static final String ERR_STILLINUSE = "Slot still in use"; // -------- public methods -------- /** * @param type Typ wie z.B. Slots.SLOTS_READER */ public SpectStreamSlot(Operator owner, int type, String name) { flags = (type & Slots.SLOTS_TYPEMASK) | Slots.SLOTS_FREE; // unlinked so far this.name = name; this.owner = owner; } public SpectStreamSlot(Operator owner, int type) { this(owner, type, (type & Slots.SLOTS_TYPEMASK) == Slots.SLOTS_READER ? Slots.SLOTS_DEFREADER : Slots.SLOTS_DEFWRITER); } public int getFlags() { return flags; } public String toString() { return name; } public Operator getOwner() { return owner; } /** * Ermittelt korrespondierenden Reader zu einem Writer und vice versa * * @return null, wenn nicht verlinkt! */ public SpectStreamSlot getLinked() { return linked; } /** * Verknuepft diesen Slot mit einem anderen */ public void linkTo(SpectStreamSlot dest) throws SyncFailedException, SlotAlreadyConnectedException { if ((this.linked != null) || (dest.linked != null)) { throw new SlotAlreadyConnectedException(); } if ((this.flags & Slots.SLOTS_TYPEMASK) == (dest.flags & Slots.SLOTS_TYPEMASK)) { throw new SyncFailedException(""); } this.linked = dest; this.flags |= Slots.SLOTS_LINKED; this.flags &= ~Slots.SLOTS_FREE; dest.linked = this; dest.flags |= Slots.SLOTS_LINKED; dest.flags &= ~Slots.SLOTS_FREE; } /** * Trennt diesen Slot von seinem Counterpart */ public void divorce() throws NotBoundException { if (linked != null) { linked.linked = null; linked.flags &= ~Slots.SLOTS_LINKED; linked.flags |= Slots.SLOTS_FREE; this.linked = null; this.flags &= ~Slots.SLOTS_LINKED; this.flags |= Slots.SLOTS_FREE; } else { throw new NotBoundException(); } } public Thread getOwnerThread() { return ownerThread; } /** * stellt einen SpectStream zur Verfuegung, der * vom Counterpart-Slot gelesen werden kann (benachrichtigt diesen ggf.); * * diese Methode ruft SpectStream.initWriter() auf! Siehe dort fuer Details * wenn ein Reader auf den Stream wartet, wird ihm das signalisiert * * erzeugt eine SlotAlreadyConnectedException, wenn der Slot bereits einen Stream fuehrt */ public void initWriter(SpectStream strm) throws SlotAlreadyConnectedException { synchronized (this) { if (stream != null) { // nanu, schon ein Stream praesent?! throw new SlotAlreadyConnectedException(ERR_STILLINUSE); } strm.initWriter(); ownerThread = Thread.currentThread(); stream = strm; state = STATE_ACTIVE; } if (linked != null) { synchronized (linked) { linked.stream = strm; if (linked.state == STATE_WAITING) { linked.notify(); // tell dem to start } } } else { state = STATE_DUMMY; } } /** * besorgt den SpectStream des Counterpart-Writers, * ggf. wird mit wait() auf dessen Einrichtung gewartet! * * diese Methode ruft SpectStream.getDescr() auf! Siehe dort fuer Details * * erzeugt eine InterruptedException, wenn der Warteprozess unterbrochen wurde * (dies darf nicht als "Fehler" behandelt werden, sondern sollte zur Ueberpruefung * des threadPaused + threadDead Flags fuehren!) * NICHT AUFRUFEN, WENN DER SLOT NICHT VERLINKT IST */ public SpectStream getDescr() throws InterruptedException { synchronized (this) { try { ownerThread = Thread.currentThread(); while (stream == null) { state = STATE_WAITING; wait(); state = STATE_ACTIVE; } stream.getDescr(); } catch (InterruptedException e) { state = STATE_ACTIVE; throw e; } return stream; } } /** * Erzeugt ein laufendes Spectrogram des Slot-Streams */ public void createSpectrogram() { synchronized (this) { if (stream != null) { if (spectro == null) { spectro = new Spectrogram(this); } spectro.newStream(stream); } } } /** * liest einen Frame von der Input-Pipe; * sollte statt SpectStream.readFrame() benutzt werden, da diese Methode * ggf. wartet und den Warte-Status markiert und somit einen Abbruch durch den Hauptthread * gestattet! Ausserdem wird bei vollem Puffer der Writer benachrichtigt, wenn wieder * Platz ist * * erzeugt eine InterruptedException, wenn der Warteprozess unterbrochen wurde * (dies darf nicht als "Fehler" behandelt werden, sondern sollte zur Ueberpruefung * des threadPaused + threadDead Flags fuehren!) */ public SpectFrame readFrame() throws IOException, InterruptedException { SpectFrame fr = null; synchronized (this) { try { while (fr == null) { try { // System.out.println( "framesReadable : " + stream.framesReadable() ); if (stream.framesReadable() != 0) { // auch -1, loest ja EOFException aus fr = stream.readFrame(); if (linked.state == STATE_WAITING) { // geschachtelte synchronized nur, wenn klar ist, dass der andere wartet! synchronized (linked) { linked.notify(); // yo, du kannst wieder schreiben } } } else { state = STATE_WAITING; wait(); state = STATE_ACTIVE; } } catch (NoSuchElementException ignored) {} // dann warten wir eben } } catch (InterruptedException e) { state = STATE_ACTIVE; throw e; } } return fr; } /** * schreibt einen Frame * sollte statt SpectStream.writeFrame() benutzt werden, da diese Methode * einen evtl. wartenden Counterpart benachrichtigt; ausserdem wird gewartet, * bis Platz im Puffer ist * * erzeugt eine InterruptedException, wenn der Warteprozess unterbrochen wurde * (dies darf nicht als "Fehler" behandelt werden, sondern sollte zur Ueberpruefung * des threadPaused + threadDead Flags fuehren!) * * NICHT AUFRUFEN, WENN DER SLOT NICHT VERLINKT IST */ public void writeFrame(SpectFrame fr) throws IOException, InterruptedException { if (state == STATE_DUMMY) { stream.writeDummy(fr); return; } synchronized (this) { try { while (fr != null) { try { if (stream.framesWriteable() != 0) { // auch -1, loest ja EOFException aus stream.writeFrame(fr); if (spectro != null) { spectro.addFrame(fr); } fr = null; if (linked.state == STATE_WAITING) { // geschachtelte synchronized nur, wenn klar ist, dass der andere wartet! synchronized (linked) { linked.notify(); // yo, du kannst wieder lesen } } } else { state = STATE_WAITING; wait(); state = STATE_ACTIVE; } } catch (IndexOutOfBoundsException ignored) {} // dann warten wir eben } } catch (InterruptedException e) { state = STATE_ACTIVE; throw e; } } } /** * Gibt einen ueber dieses Objekt erzeugtes Frame frei */ public void freeFrame(SpectFrame fr) { stream.freeFrame(fr); } /** * Raeumt den Slot nach Benutzung auf, schiesst ggf. den Stream * UNBEDINGT AUFRUFEN AUCH BEIM FEHLER-ABBRUCH, WEIL DER SLOT * SONST NICHT WIEDERBENUTZT WERDEN KANN! */ public void cleanUp() { synchronized (this) { if (spectro != null) { spectro.ownerTerminated(); spectro = null; } if (stream != null) { try { if ((flags & Slots.SLOTS_TYPEMASK) == Slots.SLOTS_READER) { stream.closeReader(); } } catch (IOException ignored) {} // spielt keine Rolle mehr try { if ((flags & Slots.SLOTS_TYPEMASK) == Slots.SLOTS_WRITER) { stream.closeWriter(); } } catch (IOException ignored) {} // spielt keine Rolle mehr stream = null; } this.state = STATE_DEAD; ownerThread = null; } } /** * Liefert den zugehoerigen SpectStream */ public SpectStream getStream() { synchronized (this) { return stream; } } /** * Ermittelt den Fortschritt des Slots im Bereich 0.0 bis 1.0 (= 100%) * * @param slot darf null sein; das Ergebnis ist dann 0.0 */ public static float progress(SpectStreamSlot slot) { if ((slot == null) || (slot.stream == null)) return 0.0f; synchronized (slot) { if ((slot.flags & Slots.SLOTS_TYPEMASK) == Slots.SLOTS_READER) { return ((float) slot.stream.framesRead / slot.stream.frames); } else { return ((float) slot.stream.framesWritten / slot.stream.frames); } } } }