All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.sciss.synth.proc.UGenGraphBuilder.scala Maven / Gradle / Ivy

/*
 *  UGenGraphBuilder.scala
 *  (SoundProcesses)
 *
 *  Copyright (c) 2010-2016 Hanns Holger Rutz. All rights reserved.
 *
 *	This software is published under the GNU General Public License v2+
 *
 *
 *  For further information, please contact Hanns Holger Rutz at
 *  [email protected]
 */

package de.sciss.synth.proc

import de.sciss.lucre.synth.{Server, Sys}
import de.sciss.synth.UGenGraph

import scala.util.control.ControlThrowable
import impl.{UGenGraphBuilderImpl => Impl}

object UGenGraphBuilder {
  def get: UGenGraphBuilder = UGenGraph.builder match {
    case b: UGenGraphBuilder => b
    case _ => throw new IllegalStateException("Expansion out of context")
  }

  /** An exception thrown when during incremental build an input is required for which the underlying source
    * cannot yet be determined.
    *
    * This can be a case class because it is used only within the same transaction,
    * and thereby the `timed` argument does not become stale.
    */
  final case class MissingIn(input: Key /* Input */) extends ControlThrowable

  /** '''Note''': The resulting object is mutable, therefore must not be shared across threads and also must be
    * created and consumed within the same transaction. That is to say, to be transactionally safe, it may only
    * be stored in a `TxnLocal`, but not a full STM ref.
    */
  def apply[S <: Sys[S]](context: Context[S], proc: Proc[S])
                        (implicit tx: S#Tx): State[S] = Impl(context, proc)

  def init[S <: Sys[S]](proc: Proc[S])(implicit tx: S#Tx): Incomplete[S] = Impl.init(proc)

  case class ScanIn(numChannels: Int, fixed: Boolean)

  trait Context[S <: Sys[S]] {
    def server: Server

    def requestInput[Res](req: UGenGraphBuilder.Input { type Value = Res }, state: Incomplete[S])
                         (implicit tx: S#Tx): Res
  }

  sealed trait State[S <: Sys[S]] {
    def acceptedInputs: Map[Key, (Input, Input#Value)]
    def rejectedInputs: Set[Key]

    /** Current set of used outputs (scan keys to number of channels).
      * This is guaranteed to only grow during incremental building, never shrink.
      */
    def outputs: Map[String, Int]

    def isComplete: Boolean
  }

  trait Incomplete[S <: Sys[S]] extends State[S] {
    def retry(context: Context[S])(implicit tx: S#Tx): State[S]

    final def isComplete = false
  }

  trait Complete[S <: Sys[S]] extends State[S] {
    def result: UGenGraph

    final def isComplete = true
    // final def missingIns = Set.empty[String]

    final def rejectedInputs = Set.empty[UGenGraphBuilder.Key]
  }

  // --------------------------------------------

  /** A pure marker trait to rule out some type errors. */
  trait Key
  /** A scalar value found in the attribute map. */
  final case class AttributeKey(name: String) extends Key
//  /** A entry in a proc's scan map. */
//  final case class InputKey    (name: String) extends Key
  //  /** A buffer source found in the attribute map. */
  //  final case class BufferKey(name: String) extends Key

  // final case class NumChannels(value: Int) extends UGenGraphBuilder.Value

  /** A pure marker trait to rule out some type errors. */
  trait Value {
    def async: Boolean
  }
  case object Unit extends Value {
    final val async = false
  }
  type Unit = Unit.type

  object Input {
//    object Scan {
//      final case class Value(numChannels: Int) extends UGenGraphBuilder.Value {
//        def async = false
//        override def productPrefix = "Input.Scan.Value"
//        override def toString = s"$productPrefix(numChannels = $numChannels)"
//      }
//    }
//    final case class Scan(name: String, fixed: Int) extends Input {
//      type Key    = ScanKey
//      type Value  = Scan.Value
//
//      def key = ScanKey(name)
//
//      override def productPrefix = "Input.Scan"
//    }

    object Stream {
      def EmptySpec = Spec(0.0, 0)

      final case class Spec(maxSpeed: Double, interp: Int) {
        /** Empty indicates that the stream is solely used for information
          * purposes such as `BufChannels`.
          */
        def isEmpty: Boolean = interp == 0

        /** Native indicates that the stream will be transported by the UGen
          * itself, i.e. via `DiskIn` or `VDiskIn`.
          */
        def isNative: Boolean = interp == -1

        override def productPrefix = "Input.Stream.Spec"

        override def toString = f"$productPrefix(maxSpeed = $maxSpeed%1.1f, interp = $interp)"
      }
      final case class Value(numChannels: Int, sampleRate: Double, specs: List[Spec]) extends UGenGraphBuilder.Value {
        override def productPrefix = "Input.Stream.Value"
        override def toString = s"$productPrefix(numChannels = $numChannels, spec = ${specs.mkString("[", ",", "]")})"
        def async = false
      }
    }
    final case class Stream(name: String, spec: Stream.Spec) extends Input {
      type Key    = AttributeKey
      type Value  = Stream.Value

      def key = AttributeKey(name)

      override def productPrefix = "Input.Stream"
    }

    object DiskOut {
      final case class Value(numChannels: Int) extends UGenGraphBuilder.Value {
        def async = false
        override def productPrefix = "Input.DiskOut.Value"
        override def toString = s"$productPrefix(numChannels = $numChannels)"
      }
    }
    final case class DiskOut(name: String, numChannels: Int) extends Input {
      type Key    = AttributeKey
      type Value  = DiskOut.Value

      def key = AttributeKey(name)

      override def productPrefix = "Input.DiskOut"
    }

    object Attribute {
      final case class Value(numChannels: Int) extends UGenGraphBuilder.Value {
        def async = false
        override def productPrefix = "Input.Attribute.Value"
        override def toString = s"$productPrefix(numChannels = $numChannels)"
      }
    }
    /** Specifies access to a scalar attribute.
      *
      * @param name                 name (key) of the attribute
      * @param requiredNumChannels  the required number of channels or `-1` if no specific requirement
      * @param defaultNumChannels   the default  number of channels or `-1` if no default provided
      */
    final case class Attribute(name: String, requiredNumChannels: Int, defaultNumChannels: Int) extends Input {
      type Key    = AttributeKey
      type Value  = Attribute.Value

      def key = AttributeKey(name)

      override def productPrefix = "Input.Attribute"
    }

    object Buffer {
      /** Maximum number of samples (channels multiplied by frames)
        * prepared on-the-fly. If the number of samples exceeds this
        * value, use asynchronous preparation.
        */
      final val AsyncThreshold = 65536

      final case class Value(numFrames: Long, numChannels: Int, async: Boolean) extends UGenGraphBuilder.Value {
        override def productPrefix = "Input.Buffer.Value"
        override def toString = s"$productPrefix(numFrames = $numFrames, numChannels = $numChannels, async = $async)"
      }
    }
    /** Specifies access to a random access buffer.
      *
      * @param name         name (key) of the attribute referring to an object that
      *                     can be buffered (e.g. audio grapheme)
      */
    final case class Buffer(name: String) extends Input {
      type Key    = AttributeKey
      type Value  = Buffer.Value

      def key = AttributeKey(name)

      override def productPrefix = "Input.Buffer"
    }

    object Action {
      case object Value extends UGenGraphBuilder.Value {
        def async = false
        override def productPrefix = "Input.Action.Value"
      }
    }
    /** Specifies access to an action.
      *
      * @param name   name (key) of the attribute referring to an action
      */
    final case class Action(name: String) extends Input {
      type Key    = AttributeKey
      type Value  = Action.Value.type

      def key = AttributeKey(name)

      override def productPrefix = "Input.Action"
    }
  }
  trait Input {
    type Key   <: UGenGraphBuilder.Key
    type Value <: UGenGraphBuilder.Value

    def key: Key
  }
}
trait UGenGraphBuilder extends UGenGraph.Builder {
  import UGenGraphBuilder._

  def server: Server

  /** Called by graph elements during their expansion, this method forwards a request
    * for input specifications to the `UGenGraphBuilder.Context`. The context should
    * examine the input and return an appropriate value of type `input.Value` that
    * will then be stored under `input.key` in the `acceptedInputs` map of the builder
    * state.
    *
    * Note that the builder will not check whether an entry with the key already exists
    * in the map or not. It is the responsibility of the context to react appropriately
    * to repeated calls with the same input key. For example, the same attribute key
    * for a streaming operation may be used multiple times, perhaps with different
    * streaming speeds.
    *
    * If an input resource is not ready, the context should throw a `MissingIn` exception.
    * The builder will catch that exception and add the key to `rejectedInputs` instead
    * of `acceptedInputs` instead.
    */
  def requestInput(input: Input): input.Value

  /** This method should only be invoked by the `graph.scan.Elem` instances. It declares a scan output along
    * with the number of channels written to it.
    */
  def addOutput(key: String, numChannels: Int): scala.Unit
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy