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

de.sciss.synth.SynthGraph.scala Maven / Gradle / Ivy

/*
 *  SynthGraph.scala
 *  (ScalaColliderUGens)
 *
 *  Copyright (c) 2008-2022 Hanns Holger Rutz. All rights reserved.
 *
 *  This software is published under the GNU Lesser General Public License v2.1+
 *
 *
 *  For further information, please contact Hanns Holger Rutz at
 *  [email protected]
 */

package de.sciss.synth

import de.sciss.synth.impl.SynthGraphBuilderImpl
import de.sciss.synth.ugen.ControlProxyLike

import scala.collection.immutable.{IndexedSeq => Vec}

object SynthGraph {
  trait Builder {
    def addLazy(g: Lazy): Unit

    def addControlProxy(proxy: ControlProxyLike): Unit
  }

  // java.lang.ThreadLocal is around 30% faster than
  // using a synchronized map, plus we don't need
  // to look after its cleaning
  private val builders = new ThreadLocal[Builder] {
    override protected def initialValue: Builder = BuilderDummy
  }

  def builder: Builder = builders.get

  def apply(thunk: => Any): SynthGraph = {
    val b = new SynthGraphBuilderImpl
    use(b) {
      thunk
      b.build
    }
  }

  /** Installs a custom synth graph builder on the current thread,
    * during the invocation of a closure. This method is typically
    * called from other libraries which wish to provide a graph
    * builder other than the default.
    *
    * When the method returns, the previous graph builder has automatically
    * been restored. During the execution of the `body`, calling
    * `SynthGraph.builder` will return the given `builder` argument.
    *
    * @param builder    the builder to install on the current thread
    * @param body       the body which is executed with the builder found through `SynthGraph.builder`
    * @tparam A         the result type of the body
    * @return           the result of executing the body
    */
  def use[A](builder: Builder)(body: => A): A = {
    val old = builders.get()
    builders.set(builder)
    try {
      body
    } finally {
      builders.set(old)
    }
  }

  /** A boolean setting (defaults to `false`) which can help track down
    * bugs with graph elements being added outside a `SynthGraph` context.
    * When this setting is `true`, a warning message is printed to
    * `Console.err` with the graph element added and the stack trace,
    * indicating calls such as `SinOsc.ar` outside a
    * thread local `SynthGraph` builder.
    */
  var warnOutsideContext = false

  private object BuilderDummy extends Builder {
    private def warn(obj: => String): Unit =
      if (warnOutsideContext) {
        Console.err.println("Warning - adding SynthGraph element outside context: " + obj)
        val e   = new Throwable()
        e.fillInStackTrace()
        val t   = e.getStackTrace
        val n   = t.length
        var i   = 0
        var go  = true
        while (i < n && go) {
          val c = t(i).getClassName
          if ((c.startsWith("de.sciss.synth.") &&
            (c.charAt(15).isUpper || c.startsWith("de.sciss.synth.ugen."))) ||
            c.startsWith("scala.collection.")) {
            i += 1
          } else {
            go = false
          }
        }
        while (i < n) {
          Console.err.println("  at " + t(i))
          i += 1
        }
      }

    def addLazy(g: Lazy): Unit = warn(g.toString)

    def addControlProxy(proxy: ControlProxyLike): Unit = warn(proxy.toString)
  }
}

final case class SynthGraph(sources: Vec[Lazy], controlProxies: Set[ControlProxyLike]) {
  def isEmpty : Boolean  = sources.isEmpty && controlProxies.isEmpty
  def nonEmpty: Boolean  = !isEmpty
  def expand(implicit factory: UGenGraph.BuilderFactory): UGenGraph = factory.build(this)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy