![JAR search and dependency download from the Maven repository](/logo.png)
de.sciss.synth.impl.UGenGraphBuilder.scala Maven / Gradle / Ivy
/*
* UGenGraphBuilder.scala
* (ScalaCollider)
*
* Copyright (c) 2008-2012 Hanns Holger Rutz. All rights reserved.
*
* This software is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either
* version 2, june 1991 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License (gpl.txt) along with this software; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*
* For further information, please contact Hanns Holger Rutz at
* [email protected]
*/
package de.sciss
package synth
package impl
import collection.breakOut
import collection.mutable.{Map => MMap, Buffer => MBuffer, Stack => MStack}
import collection.immutable.{IndexedSeq => IIdxSeq, Set => ISet}
import UGenGraph.RichUGen
private[synth] final class DefaultUGenGraphBuilder( graph: SynthGraph ) extends BasicUGenGraphBuilder {
builder =>
override def toString = "UGenGraph.Builder@" + hashCode.toHexString
def build : UGenGraph = {
// graph.sources.foreach( _.force( builder ))
var g = graph
var controlProxies = ISet.empty[ ControlProxyLike[ _ ]]
while( g.nonEmpty ) {
// XXX these two lines could be more efficient eventually -- using a 'clearable' SynthGraph
controlProxies ++= g.controlProxies
g = SynthGraph( g.sources.foreach { src =>
src.force( builder )
}) // allow for further graphs being created
}
build( controlProxies )
}
}
object UGenGraphBuilderLike {
// ---- IndexedUGen ----
private final class IndexedUGen( val ugen: UGen, var index: Int, var effective: Boolean ) {
val parents = MBuffer.empty[ IndexedUGen ]
var children = MBuffer.empty[ IndexedUGen ]
var richInputs : List[ RichUGenInBuilder ] = Nil // null
override def toString = "IndexedUGen(" + ugen + ", " + index + ", " + effective + ") : richInputs = " + richInputs
}
private trait RichUGenInBuilder {
def create : (Int, Int)
def makeEffective() : Int
}
private final class RichConstant( constIdx: Int ) extends RichUGenInBuilder {
def create = (-1, constIdx)
def makeEffective() = 0
override def toString = "RichConstant(" + constIdx + ")"
}
private final class RichUGenProxyBuilder( iu: IndexedUGen, outIdx: Int ) extends RichUGenInBuilder {
def create = (iu.index, outIdx)
def makeEffective() = {
if( !iu.effective ) {
iu.effective = true
var numEff = 1
iu.richInputs.foreach( numEff += _.makeEffective() )
numEff
} else 0
}
override def toString = "RichUGenProxyBuilder(" + iu + ", " + outIdx + ")"
}
}
trait BasicUGenGraphBuilder extends UGenGraphBuilderLike {
protected var ugens = IIdxSeq.empty[ UGen ]
protected var controlValues = IIdxSeq.empty[ Float ]
protected var controlNames = IIdxSeq.empty[ (String, Int) ]
protected var sourceMap = Map.empty[ AnyRef, Any ]
}
/**
* Complete implementation of a ugen graph builder, except for the actual code that
* calls `force` on the sources of a `SynthGraph`. Implementations should call
* the `build` method passing in the control proxies for all involved synth graphs.
*/
trait UGenGraphBuilderLike extends UGenGraph.Builder {
builder =>
import UGenGraphBuilderLike._
// updated during build
protected def ugens: IIdxSeq[ UGen ]
protected def ugens_=( seq: IIdxSeq[ UGen ]) : Unit
protected def controlValues: IIdxSeq[ Float ]
protected def controlValues_=( seq: IIdxSeq[ Float ]) : Unit
protected def controlNames: IIdxSeq[ (String, Int) ]
protected def controlNames_=( seq: IIdxSeq[ (String, Int) ]) : Unit
protected def sourceMap: Map[ AnyRef, Any ]
protected def sourceMap_=( map: Map[ AnyRef, Any ]) : Unit
/**
* Finalizes the build process. It is assumed that the graph elements have been expanded at this
* stage, having called into `addUGen` and `addControl`. The caller must collect all the control
* proxies and pass them into this method.
*
* @param controlProxies the control proxies participating in this graph
*
* @return the completed `UGenGraph` build
*/
final protected def build( controlProxies: Iterable[ ControlProxyLike[ _ ]]) : UGenGraph = {
// val ctrlProxyMap = buildControls( graph.controlProxies )
val ctrlProxyMap = buildControls( controlProxies )
val (igens, constants) = indexUGens( ctrlProxyMap )
val indexedUGens = sortUGens( igens )
val richUGens : IIdxSeq[ RichUGen ] =
indexedUGens.map( iu => RichUGen( iu.ugen, iu.richInputs.map( _.create )))( breakOut )
UGenGraph( constants, controlValues, controlNames, richUGens )
}
private def indexUGens( ctrlProxyMap: Map[ ControlProxyLike[ _ ], (UGen, Int)]) :
(IIdxSeq[ IndexedUGen ], IIdxSeq[ Float ]) = {
val constantMap = MMap.empty[ Float, RichConstant ]
var constants = IIdxSeq.empty[ Float ]
var numIneff = ugens.size
val indexedUGens = ugens.zipWithIndex.map { tup =>
val ugen = tup._1
val idx = tup._2
val eff = ugen.hasSideEffect
if( eff ) numIneff -= 1
new IndexedUGen( ugen, idx, eff )
}
//indexedUGens.foreach( iu => println( iu.ugen.ref ))
//val a0 = indexedUGens(1).ugen
//val a1 = indexedUGens(3).ugen
//val ee = a0.equals(a1)
val ugenMap: Map[ AnyRef, IndexedUGen ] = indexedUGens.map( iu => (iu.ugen /* .ref */, iu) )( breakOut )
indexedUGens.foreach { iu =>
// XXX Warning: match not exhaustive -- "missing combination UGenOutProxy"
// this is clearly a nasty scala bug, as UGenProxy does catch UGenOutProxy;
// might be http://lampsvn.epfl.ch/trac/scala/ticket/4020
iu.richInputs = iu.ugen.inputs.map({ // don't worry -- the match _is_ exhaustive
case Constant( value ) => constantMap.get( value ) getOrElse {
val rc = new RichConstant( constants.size )
constantMap += value -> rc
constants :+= value
rc
}
case up: UGenProxy =>
val iui = ugenMap( up.source /* .ref */)
iu.parents += iui
iui.children += iu
new RichUGenProxyBuilder( iui, up.outputIndex )
case ControlUGenOutProxy( proxy, outputIndex /* , _ */) =>
val (ugen, off) = ctrlProxyMap( proxy )
val iui = ugenMap( ugen /* .ref */)
iu.parents += iui
iui.children += iu
new RichUGenProxyBuilder( iui, off + outputIndex )
})( breakOut )
if( iu.effective ) iu.richInputs.foreach( numIneff -= _.makeEffective() )
}
val filtered: IIdxSeq[ IndexedUGen ] = if( numIneff == 0 ) indexedUGens else indexedUGens.collect {
case iu if iu.effective =>
iu.children = iu.children.filter( _.effective )
iu
}
(filtered, constants)
}
/*
* Note that in Scala like probably in most other languages,
* the UGens _can only_ be added in right topological order,
* as that is the only way they can refer to their inputs.
* However, the Synth-Definition-File-Format help documents
* states that depth-first order is preferable performance-
* wise. Truth is, performance is probably the same,
* mNumWireBufs might be different, so it's a space not a
* time issue.
*/
private def sortUGens( indexedUGens: IIdxSeq[ IndexedUGen ]) : Array[ IndexedUGen ] = {
indexedUGens.foreach( iu => iu.children = iu.children.sortWith( (a, b) => a.index > b.index ))
val sorted = new Array[ IndexedUGen ]( indexedUGens.size )
// val avail = MStack( indexedUGens.filter( _.parents.isEmpty ) : _* )
val avail: MStack[ IndexedUGen ] = indexedUGens.collect({ case iu if iu.parents.isEmpty => iu })( breakOut )
var cnt = 0
while( avail.nonEmpty ) {
val iu = avail.pop()
iu.index = cnt
sorted( cnt ) = iu
cnt += 1
iu.children foreach { iuc =>
iuc.parents.remove( iuc.parents.indexOf( iu ))
if( iuc.parents.isEmpty ) /* avail =*/ avail.push( iuc )
}
}
sorted
}
final def visit[ U ]( ref: AnyRef, init: => U ) : U = {
sourceMap.getOrElse( ref, {
val exp = init // .asInstanceOf[ U ]
sourceMap += ref -> exp
// exp.foreach( addUGen( _ ))
exp
}).asInstanceOf[ U ] // XXX hmmm, not so pretty...
}
final def addUGen( ugen: UGen ) { ugens :+= ugen }
final def addControl( values: IIdxSeq[ Float ], name: Option[ String ]) : Int = {
val specialIndex = controlValues.size
controlValues ++= values
name.foreach( n => controlNames :+= n -> specialIndex )
specialIndex
}
private def buildControls( p: Iterable[ ControlProxyLike[ _ ]]): Map[ ControlProxyLike[ _ ], (UGen, Int) ] = {
p.groupBy( _.factory ).flatMap( tuple => {
val (factory, proxies) = tuple
factory.build( builder, proxies.toSeq.asInstanceOf[ Seq[ factory.Proxy /* XXX horrible */]]: _* )
// res.valuesIterator.foreach( tup => addUGen( tup._1 ))
// res
})( breakOut )
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy