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

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

/*
 *  SynthGraphs.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 java.util

import de.sciss.lucre.event.{Event, Dummy, EventLike, Targets}
import de.sciss.lucre.expr.Expr
import de.sciss.lucre.stm.{Copy, Elem, Obj, Sys}
import de.sciss.lucre.expr
import de.sciss.model.Change
import de.sciss.serial.{DataInput, DataOutput, ImmutableSerializer}
import de.sciss.synth.ugen.{Constant, ControlProxyLike}
import de.sciss.synth.{Lazy, MaybeRate, SynthGraph, proc}

import scala.annotation.switch
import scala.util.control.NonFatal

object SynthGraphObj extends expr.impl.ExprTypeImpl[SynthGraph, SynthGraphObj] {
  final val typeID = 16

  import proc.{SynthGraphObj => Repr}

  protected def mkConst[S <: Sys[S]](id: S#ID, value: A)(implicit tx: S#Tx): Const[S] =
    new _Const[S](id, value)

  protected def mkVar[S <: Sys[S]](targets: Targets[S], vr: S#Var[Ex[S]], connect: Boolean)
                                  (implicit tx: S#Tx): Var[S] = {
    val res = new _Var[S](targets, vr)
    if (connect) res.connect()
    res
  }

  private final class _Const[S <: Sys[S]](val id: S#ID, val constValue: A)
    extends ConstImpl[S] with Repr[S]

  private final class _Var[S <: Sys[S]](val targets: Targets[S], val ref: S#Var[Ex[S]])
    extends VarImpl[S] with Repr[S]

  /** A serializer for synth graphs. */
  object valueSerializer extends ImmutableSerializer[SynthGraph] {
    private final val SER_VERSION = 0x5347

    // private final class RefMapOut {
    // var map   = Map.empty[Product, Int]
    // val map   = collection.mutable.Map.empty[Product, Int] // not faster than immutable map!
    // var count = 0
    // }

      // we use an identity hash map, because we do _not_
      // want to alias objects in the serialization; the input
      // is an in-memory object graph.
    private type RefMapOut = util.IdentityHashMap[Product, Integer]

    private final class RefMapIn {
      var map   = Map.empty[Int, Product]
      // val map   = collection.mutable.Map.empty[Int, Product]
      var count = 0
    }

    private def writeProduct(p: Product, out: DataOutput, ref: RefMapOut): Unit = {
      val id0Ref = ref.get(p)
      // val id0 = ref.map.getOrElse(p, -1)
      if (id0Ref != null) {
        out.writeByte('<')
        out.writeInt(id0Ref)
        return
      }
      out.writeByte('P')
      val pck     = p.getClass.getPackage.getName
      val prefix  = p.productPrefix
      val name    = if (pck == "de.sciss.synth.ugen") prefix else s"$pck.$prefix"
      out.writeUTF(name)
      out.writeShort(p.productArity)
      p.productIterator.foreach(writeElem(_, out, ref))

      val id     = ref.size() // count
      // ref.map   += ((p, id))
      // ref.count  = id + 1
      ref.put(p, id)
    }

    private def writeElemSeq(xs: Seq[Any], out: DataOutput, ref: RefMapOut): Unit = {
      out.writeByte('X')
      out.writeInt(xs.size)
      xs.foreach(writeElem(_, out, ref))
    }

    private def writeElem(e: Any, out: DataOutput, ref: RefMapOut): Unit =
      e match {
        case c: Constant =>
          out.writeByte('C')
          out.writeFloat(c.value)
        case r: MaybeRate =>
          out.writeByte('R')
          out.writeByte(r.id)
        case o: Option[_] =>
          out.writeByte('O')
          out.writeBoolean(o.isDefined)
          if (o.isDefined) writeElem(o.get, out, ref)
        case xs: Seq[_] =>  // 'X'. either indexed seq or var arg (e.g. wrapped array)
          writeElemSeq(xs, out, ref)
        case p: Product =>
          writeProduct(p, out, ref) // 'P' or '<'
        case i: Int =>
          out.writeByte('I')
          out.writeInt(i)
        case s: String =>
          out.writeByte('S')
          out.writeUTF(s)
        case b: Boolean   =>
          out.writeByte('B')
          out.writeBoolean(b)
        case f: Float =>
          out.writeByte('F')
          out.writeFloat(f)
        case d: Double =>
          out.writeByte('D')
          out.writeDouble(d)
      }

    def write(v: SynthGraph, out: DataOutput): Unit = {
      out.writeShort(SER_VERSION)
      writeNew(v, out)
    }

    private def writeNew(v: SynthGraph, out: DataOutput): Unit = {
      val ref = new RefMapOut
      writeElemSeq(v.sources, out, ref)
      val ctl = v.controlProxies
      out.writeByte('T')
      out.writeInt(ctl.size)
      ctl.foreach(writeProduct(_, out, ref))
    }

    // expects that 'X' byte has already been read
    private def readIdentifiedSeq(in: DataInput, ref: RefMapIn): Seq[Any] = {
      val num = in.readInt()
      Vector.fill(num)(readElem(in, ref))
    }

    // expects that 'P' byte has already been read
    private def readIdentifiedProduct(in: DataInput, ref: RefMapIn): Product = {
      val prefix    = in.readUTF()
      val arity     = in.readShort()
      val className = if (Character.isUpperCase(prefix.charAt(0))) s"de.sciss.synth.ugen.$prefix" else prefix

      val res = try {
        if (arity == 0 && className.charAt(className.length - 1) == '$') {
          // case object
          val companion = Class.forName(s"$className").getField("MODULE$").get(null)
          companion.asInstanceOf[Product]

        } else {

          // cf. stackoverflow #3039822
          val companion = Class.forName(s"$className$$").getField("MODULE$").get(null)
          val elems = new Array[AnyRef](arity)
          var i = 0
          while (i < arity) {
            elems(i) = readElem(in, ref).asInstanceOf[AnyRef]
            i += 1
          }
          //    val m         = companion.getClass.getMethods.find(_.getName == "apply")
          //      .getOrElse(sys.error(s"No apply method found on $companion"))
          val ms = companion.getClass.getMethods
          var m = null: java.lang.reflect.Method
          var j = 0
          while (m == null && j < ms.length) {
            val mj = ms(j)
            if (mj.getName == "apply" && mj.getParameterTypes.length == arity) m = mj
            j += 1
          }
          if (m == null) sys.error(s"No apply method found on $companion")

          m.invoke(companion, elems: _*).asInstanceOf[Product]
        }

      } catch {
        case NonFatal(e) =>
          throw new IllegalArgumentException(s"While de-serializing $prefix", e)
      }

      val id        = ref.count
      ref.map      += ((id, res))
      ref.count     = id + 1
      res
    }

    private def readElem(in: DataInput, ref: RefMapIn): Any = {
      (in.readByte(): @switch) match {
        case 'C' => Constant(in.readFloat())
        case 'R' => MaybeRate(in.readByte())
        case 'O' => if (in.readBoolean()) Some(readElem(in, ref)) else None
        case 'X' => readIdentifiedSeq(in, ref)
        case 'P' => readIdentifiedProduct(in, ref)
        case '<' =>
          val id = in.readInt()
          ref.map(id)
        case 'I' => in.readInt()
        case 'S' => in.readUTF()
        case 'B' => in.readBoolean()
        case 'F' => in.readFloat()
        case 'D' => in.readDouble()
      }
    }

    def read(in: DataInput): SynthGraph = {
      val cookie  = in.readShort()
      require(cookie == SER_VERSION, s"Unexpected cookie $cookie")
      val res2  = readNew(in)
      res2
    }

    private def readNew(in: DataInput): SynthGraph = {
      val ref     = new RefMapIn
      val b1 = in.readByte()
      require(b1 == 'X')    // expecting sequence
      val numSources  = in.readInt()
      val sources     = Vector.fill(numSources) {
        readElem(in, ref).asInstanceOf[Lazy]
      }
      val b2 = in.readByte()
      require(b2 == 'T')    // expecting set
      val numControls = in.readInt()
      val controls    = Set.newBuilder[ControlProxyLike] // stupid Set doesn't have `fill` and `tabulate` methods
      var i = 0
      while (i < numControls) {
        controls += readElem(in, ref).asInstanceOf[ControlProxyLike]
        i += 1
      }
      SynthGraph(sources, controls.result())
    }

    //  private def readOld(in: DataInput): SynthGraph = {
    //    val ois = new java.io.ObjectInputStream(in.asInputStream)
    //    val res = ois.readObject().asInstanceOf[SynthGraph]
    //    ois.close()
    //    res
    //  }
  }
  //  private val map = mutable.Map.empty[String, SynthGraph]
  //
  //  /** Adds a predefined synth graph which is serialized through a key.
  //    * Care must be taken so that the key is unique and that the graph
  //    * is registered before any possible de-serialization (which would
  //    * fail if no in-memory graph is found under the key)
  //    */
  //  def add(key: String, graph: SynthGraph): Unit =
  //    map.synchronized(map += (key, graph))

  // private final val oldTapeCookie = 1
  private final val emptyCookie   = 4
  private final val tapeCookie    = 5

  // def valueSerializer: ImmutableSerializer[SynthGraph] = ValueSerializer

//  def readValue (                   in : DataInput ): SynthGraph  = ValueSerializer.read (       in )
//  def writeValue(value: SynthGraph, out: DataOutput): Unit        = ValueSerializer.write(value, out)

  override protected def readCookie[S <: Sys[S]](in: DataInput, access: S#Acc, cookie: Byte)(implicit tx: S#Tx): Ex[S] =
    cookie match {
      case /* `oldTapeCookie` | */ `emptyCookie` | `tapeCookie` =>
        val id = tx.readID(in, access)
        new Predefined(id, cookie)
      case _ => super.readCookie(in, access, cookie)
      //      case `mapCookie`  =>
      //        val key     = in.readUTF()
      //        val graph   = map.synchronized(map(key))
      //        new MapImpl(key, graph)
    }

  // private final class MapImpl[S <: Sys[S]]

  //  private lazy val oldTapeSynthGraph: SynthGraph =
  //    SynthGraph {
  //      import de.sciss.synth._
  //      import ugen._
  //      val sig   = graph.scan.In(Proc.Obj.graphAudio)
  //      val bus   = graph.attribute(ObjKeys.attrBus   ).ir(0)
  //      val mute  = graph.attribute(ObjKeys.attrMute  ).ir(0)
  //      val env   = graph.FadeInOut(ObjKeys.attrFadeIn, ObjKeys.attrFadeOut).ar
  //      val amp   = env * (1 - mute)
  //      Out.ar(bus, sig * amp)
  //    }

  private lazy val tapeSynthGraph: SynthGraph =
    SynthGraph {
      import de.sciss.synth._
      val sig   = graph.VDiskIn  .ar(Proc.graphAudio)
      val gain  = graph.Attribute.kr(ObjKeys.attrGain, 1.0)
      val mute  = graph.Attribute.kr(ObjKeys.attrMute, 0.0)
      val env   = graph.FadeInOut.ar
      val amp   = env * ((1 - mute) * gain)
      val out   = sig * amp
      // (out \ 0).poll(label = "disk")
      graph.ScanOut(out)
    }

  private val emptySynthGraph = SynthGraph {}

  def tape   [S <: Sys[S]](implicit tx: S#Tx): Ex[S] = apply(tapeCookie   )
  // def tapeOld[S <: Sys[S]](implicit tx: S#Tx): Ex[S] = apply(oldTapeCookie)
  def empty  [S <: Sys[S]](implicit tx: S#Tx): Ex[S] = apply(emptyCookie  )

  private def apply[S <: Sys[S]](cookie: Int)(implicit tx: S#Tx): Ex[S] = {
    val id = tx.newID()
    new Predefined(id, cookie)
  }

  private final class Predefined[S <: Sys[S]](val id: S#ID, cookie: Int)
    extends SynthGraphObj[S] with Expr.Const[S, SynthGraph] {

    def event(slot: Int): Event[S, Any] = throw new UnsupportedOperationException

    def tpe: Obj.Type = SynthGraphObj

    def copy[Out <: Sys[Out]]()(implicit tx: S#Tx, txOut: Out#Tx, context: Copy[S, Out]): Elem[Out] =
      new Predefined(txOut.newID(), cookie) // .connect()

    def write(out: DataOutput): Unit = {
      out.writeInt(tpe.typeID)
      out.writeByte(cookie)
      id.write(out)
    }

    def value(implicit tx: S#Tx): SynthGraph = constValue

    def changed: EventLike[S, Change[SynthGraph]] = Dummy[S, Change[SynthGraph]]

    def dispose()(implicit tx: S#Tx) = ()

    def constValue: SynthGraph = cookie match {
      // case `oldTapeCookie`  => oldTapeSynthGraph
      case `emptyCookie`    => emptySynthGraph
      case `tapeCookie`     => tapeSynthGraph
    }
  }
}
trait SynthGraphObj[S <: Sys[S]] extends Expr[S, SynthGraph]




© 2015 - 2025 Weber Informatics LLC | Privacy Policy