
de.sciss.proc.Proc.scala Maven / Gradle / Ivy
/*
* Proc.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.{DummyEvent, ExprTypeImpl}
import de.sciss.lucre.{Copy, Elem, Event, EventLike, Expr, Ident, Obj, Publisher, Txn, Var => LVar}
import de.sciss.model.{Change => MChange}
import de.sciss.proc.Implicits._
import de.sciss.proc.impl.{ProcOutputImpl, ProcImpl => Impl}
import de.sciss.serial.{ConstFormat, DataInput, DataOutput, TFormat}
import de.sciss.synth.SynthGraph
import de.sciss.synth.UGenSource.{RefMapIn, RefMapOut}
import de.sciss.synth.proc.graph
import de.sciss.{model, proc}
import scala.collection.immutable.{IndexedSeq => Vec}
object Proc extends Obj.Type {
final val typeId = 0x10005
// ---- implementation forwards ----
override def init(): Unit = {
super.init()
Output .init()
GraphObj.init()
Code .init()
}
def apply[T <: Txn[T]]()(implicit tx: T): Proc[T] = Impl[T]()
def read[T <: Txn[T]](in: DataInput)(implicit tx: T): Proc[T] = Impl.read(in)
implicit def format[T <: Txn[T]]: TFormat[T, Proc[T]] = Impl.format[T]
// ---- event types ----
/** An update is a sequence of changes */
final case class Update[T <: Txn[T]](proc: Proc[T], changes: Vec[Change[T]])
/** A change is either a state change, or a scan or a grapheme change */
sealed trait Change[T <: Txn[T]]
final case class GraphChange[T <: Txn[T]](change: model.Change[SynthGraph]) extends Change[T]
/** An associative change is either adding or removing an association */
sealed trait OutputsChange[T <: Txn[T]] extends Change[T] {
def output: Output[T]
}
final case class OutputAdded [T <: Txn[T]](output: Output[T]) extends OutputsChange[T]
final case class OutputRemoved[T <: Txn[T]](output: Output[T]) extends OutputsChange[T]
final val mainIn = "in"
final val mainOut = "out"
/** Audio input file (tape) grapheme. */
final val graphAudio = "sig"
/** NOT USED ANY LONGER. Hint key for copying scan connections during `copy`. Value should be a
* predicate function `(Proc[T]) => Boolean`. If absent, all connections
* are copied.
*/
final val hintFilterLinks = "links"
override def readIdentifiedObj[T <: Txn[T]](in: DataInput)(implicit tx: T): Obj[T] =
Impl.readIdentifiedObj(in)
// ---- Outputs ----
import de.sciss.lucre.{Obj, Txn}
import de.sciss.serial.{DataInput, TFormat}
object Output extends Obj.Type {
final val typeId = 0x10009
def read[T <: Txn[T]](in: DataInput)(implicit tx: T): Output[T] = ProcOutputImpl.read(in)
implicit def format[T <: Txn[T]]: TFormat[T, Output[T]] = ProcOutputImpl.format
override def readIdentifiedObj[T <: Txn[T]](in: DataInput)(implicit tx: T): Obj[T] =
ProcOutputImpl.readIdentifiedObj(in)
}
trait Output[T <: Txn[T]] extends Obj[T] {
def proc: Proc[T]
def key : String // or `StringObj`?
}
trait Outputs[T <: Txn[T]] {
def get(key: String)(implicit tx: T): Option[Output[T]]
def keys(implicit tx: T): Set[String]
def iterator(implicit tx: T): Iterator[Output[T]]
/** Adds a new scan by the given key. If a span by that name already exists, the old scan is returned. */
def add (key: String)(implicit tx: T): Output[T]
def remove(key: String)(implicit tx: T): Boolean
}
// ---- GraphObj ----
object GraphObj extends ExprTypeImpl[SynthGraph, GraphObj] {
final val typeId = 16
import proc.Proc.{GraphObj => Repr}
def tryParse(value: Any): Option[SynthGraph] = value match {
case x: SynthGraph => 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 final class _Const[T <: Txn[T]](val id: Ident[T], val constValue: A)
extends ConstImpl[T] with Repr[T]
private final class _Var[T <: Txn[T]](val targets: Targets[T], val ref: LVar[T, E[T]])
extends VarImpl[T] with Repr[T]
final val valueName = "SynthGraph"
override def defaultValue: A = emptySynthGraph
/** A format for synth graphs. */
object valueFormat extends ConstFormat[SynthGraph] {
private final val SER_VERSION = 0x5347
// ---- write ----
def write(v: SynthGraph, out: DataOutput): Unit = {
out.writeShort(SER_VERSION)
val ref = new RefMapOut(out)
ref.writeIdentifiedGraph(v)
}
// ---- read ----
def read(in: DataInput): SynthGraph = {
val cookie = in.readShort()
if (cookie != SER_VERSION) sys.error(s"Unexpected cookie $cookie")
val ref = new RefMapIn(in)
ref.readIdentifiedGraph()
}
}
// private final val oldTapeCookie = 1
private final val emptyCookie = 4
private final val tapeCookie = 5
override protected def readCookie[T <: Txn[T]](in: DataInput, cookie: Byte)(implicit tx: T): E[T] =
cookie match {
case /* `oldTapeCookie` | */ `emptyCookie` | `tapeCookie` =>
val id = tx.readId(in)
new Predefined(id, cookie)
case _ => super.readCookie(in, cookie)
}
private lazy val tapeSynthGraph: SynthGraph =
SynthGraph {
import de.sciss.synth._
import Import._
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 tapeSynthGraphSource =
"""val sig = VDiskIn.ar("sig")
|val gain = "gain".kr(1.0)
|val mute = "mute".kr(0)
|val env = FadeInOut.ar
|val amp = env * ((1 - mute) * gain)
|val out = sig * amp
|// (out \ 0).poll(label = "disk")
|ScanOut(out)
|""".stripMargin
private val emptySynthGraph = SynthGraph {}
def tape [T <: Txn[T]](implicit tx: T): E[T] = apply(tapeCookie )
// def tapeOld[T <: Txn[T]](implicit tx: T): Ex[T] = apply(oldTapeCookie)
def empty [T <: Txn[T]](implicit tx: T): E[T] = apply(emptyCookie )
def tapeSource[T <: Txn[T]](implicit tx: T): Code.Obj[T] = {
val v = Code.Proc(tapeSynthGraphSource)
val res = Code.Obj.newVar[T](v)
res.name = "tape"
res
}
private def apply[T <: Txn[T]](cookie: Int)(implicit tx: T): E[T] = {
val id = tx.newId()
new Predefined(id, cookie)
}
private final class Predefined[T <: Txn[T]](val id: Ident[T], cookie: Int)
extends GraphObj[T] with Expr.Const[T, SynthGraph] {
def event(slot: Int): Event[T, Any] = throw new UnsupportedOperationException
def tpe: Obj.Type = GraphObj
def copy[Out <: Txn[Out]]()(implicit tx: T, txOut: Out, context: Copy[T, 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: T): SynthGraph = constValue
def changed: EventLike[T, MChange[SynthGraph]] = DummyEvent()
def dispose()(implicit tx: T): Unit = ()
def constValue: SynthGraph = cookie match {
// case `oldTapeCookie` => oldTapeSynthGraph
case `emptyCookie` => emptySynthGraph
case `tapeCookie` => tapeSynthGraph
}
}
}
trait GraphObj[T <: Txn[T]] extends Expr[T, SynthGraph]
}
/** The `Proc` trait is the basic entity representing a sound process. */
trait Proc[T <: Txn[T]] extends Obj[T] with Publisher[T, Proc.Update[T]] {
/** The variable synth graph function of the process. */
def graph: Proc.GraphObj.Var[T]
/** The real-time outputs of the process. */
def outputs: Proc.Outputs[T]
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy