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

de.sciss.proc.ParamSpec.scala Maven / Gradle / Ivy

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

package de.sciss.proc

import de.sciss.lucre.Event.Targets
import de.sciss.lucre.expr.graph.Ex
import de.sciss.lucre.impl.ExprTypeImpl
import de.sciss.lucre.{Event, Expr, Ident, Txn, Var => LVar}
import de.sciss.serial.{ConstFormat, DataInput, DataOutput, Writable}
import de.sciss.synth
import de.sciss.synth.UGenSource.Vec

object ParamSpec {
  final val typeId = 21

  final val Key     = "spec"
  final val DashKey = "-spec"

  def composeKey(attrKey: String): String = s"$attrKey$DashKey"

  private final val COOKIE = 0x505301 // "PS\1"

  def init(): Unit = Obj.init()

  //  def apply[T <: Txn[T]](lo: Expr[T, Double], hi: Expr[T, Double], warp: Expr[T, Warp],
  //                         step: Expr[T, Double], unit: Expr[T, String])(implicit tx: T): Obj[T]

  object Obj extends ExprTypeImpl[ParamSpec, Obj] {
    import ParamSpec.{Obj => Repr}

    def typeId: Int = ParamSpec.typeId

    final val valueName = "ParamSpec"

    override def defaultValue: A = ParamSpec()

    implicit def valueFormat: ConstFormat[ParamSpec] = ParamSpec.format

    def tryParse(value: Any): Option[ParamSpec] = value match {
      case x: ParamSpec => Some(x)
      case _            => None
    }

    override protected def mkConst[T <: Txn[T]](id: Ident[T], value: A)(implicit tx: T): Const[T] =
      new _Const[T](id, value)

    override protected def mkVar[T <: Txn[T]](targets: Targets[T], vr: LVar[T, E[T]], connect: Boolean)
                                             (implicit tx: T): Var[T] = {
      val res = new _Var[T](targets, vr)
      if (connect) res.connect()
      res
    }

    override protected def mkProgram[T <: Txn[T]](targets: Targets[T], program: LVar[T, Ex[A]],
                                                  sources: LVar[T, Vec[Event[T, Any]]],
                                                  value: LVar[T, A], connect: Boolean)
                                                 (implicit tx: T): Program[T] =
      throw new UnsupportedOperationException

    private[this] final class _Const[T <: Txn[T]](val id: Ident[T], val constValue: A)
      extends ConstImpl[T] with Repr[T]

    private[this] final class _Var[T <: Txn[T]](val targets: Targets[T], val ref: LVar[T, E[T]])
      extends VarImpl[T] with Repr[T]
  }
  trait Obj[T <: Txn[T]] extends Expr[T, ParamSpec] {
    //    def lo  (implicit tx: T): Expr[T, Double]
    //    def hi  (implicit tx: T): Expr[T, Double]
    //    def warp(implicit tx: T): Expr[T, Warp  ]
    //    def unit(implicit tx: T): Expr[T, String]
  }

  implicit object format extends ConstFormat[ParamSpec] {
    def write(v: ParamSpec, out: DataOutput): Unit = {
      import v._
      out.writeInt(ParamSpec.COOKIE)
      out.writeDouble(lo)
      out.writeDouble(hi)
      warp.write(out)
      out.writeUTF(unit)
    }

    def read(in: DataInput): ParamSpec = {
      val cookie = in.readInt()
      if (cookie != COOKIE) sys.error(s"Unexpected cookie (found $cookie, expected $COOKIE)")

      val lo    = in.readDouble()
      val hi    = in.readDouble()
      val warp  = Warp.read(in)
      val unit  = in.readUTF()
      ParamSpec(lo = lo, hi = hi, warp = warp, /* step = step, */ unit = unit)
    }
  }

  def read(in: DataInput): ParamSpec = format.read(in)

//  /** A no-op now! */
//  def copyAttr[T <: Txn[T]](source: LObj[T], target: LObj[T])(implicit tx: T): Unit = {
//    //    val a = source.attr
//    //    val b = target.attr
//    //
//    //    a.get(Key).foreach { spec =>
//    //      b.put(Key, spec)
//    //    }
//  }
}
final case class ParamSpec(lo: Double = 0.0, hi: Double = 1.0, warp: Warp = Warp.Lin, // step: Double = 0.0,
                           unit: String = "")
  extends Writable {

  import synth._

  def range: Double = hi - lo
  def ratio: Double = hi / lo

  def clip(value: Double): Double = math.max(lo, math.min(hi, value))

  /** Maps a number from normalized range to spec.
    * Note: this does involve rounding
    * according to the spec's step parameter (unless step is zero).
    */
  def map(value: Double): Double = {
    val w = warp.map(this, value)
    w // if (step <= 0.0) w else w.roundTo(step)
  }

  /** Maps a number from spec spaced to normalized (0 ... 1) space. */
  def inverseMap(value: Double): Double = warp.inverseMap(this, value)

  /** Maps a graph element from normalized range to spec.
    * Note: this does involve rounding
    * according to the spec's step parameter (unless step is zero).
    */
  def map(value: GE): GE = {
    val w = warp.map(this, value)
    w // if (step <= 0.0) w else w.roundTo(step)
  }

  /** Maps a graph element from spec spaced to normalized (0 ... 1) space. */
  def inverseMap(value: GE): GE = warp.inverseMap(this, value)

  def write(out: DataOutput): Unit = ParamSpec.format.write(this, out)
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy