de.sciss.synth.SynthDef.scala Maven / Gradle / Ivy
/*
* SynthDef.scala
* (ScalaCollider)
*
* Copyright (c) 2008-2013 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.synth
import java.io.{ ByteArrayOutputStream, BufferedOutputStream, DataOutputStream, File, FileOutputStream }
import java.nio.ByteBuffer
import de.sciss.synth.{ Completion => Comp }
import File.{ separator => sep }
import de.sciss.osc.{Bundle, Message, Packet}
/**
* @todo should add load and loadDir to companion object
*/
final case class SynthDef( name: String, graph: UGenGraph ) {
syndef =>
import SynthDef._
override def toString = "SynthDef(" + name + ")"
def freeMsg = osc.SynthDefFreeMessage( name )
// def recv { recv() }
def recv( server: Server = Server.default, completion: Completion = NoCompletion ) {
sendWithAction( server, recvMsg( _ ), completion, "recv" )
}
private def sendWithAction( server: Server, msgFun: Option[ Packet ] => Message, completion: Completion,
name: String ) {
completion.action map { action =>
val syncMsg = server.syncMsg
val syncID = syncMsg.id
val compPacket: Packet = completion.message match {
case Some( msgFun2 ) => Bundle.now( msgFun2( syndef ), syncMsg )
case None => syncMsg
}
server !? (6000L, msgFun( Some( compPacket )), { // XXX timeout kind of arbitrary
case osc.SyncedMessage( `syncID` ) => action( syndef )
case osc.TIMEOUT => println( "ERROR: " + syndef + "." + name + " : timeout!" )
})
} getOrElse {
server ! msgFun( completion.message.map( _.apply( syndef )))
}
}
// private def sendWithAction( msg: Message, syncID: Int, action: SynthDef => Unit, name: String ) {
// server !? (6000L, msg, {
// case osc.SyncedMessage( syncID ) => action( syndef )
// case osc.TIMEOUT => println( "ERROR: " + syndef + "." + name + " : timeout!" )
// })
// }
def recvMsg: osc.SynthDefRecvMessage = recvMsg( None )
def recvMsg( completion: Option[ Packet ]) = osc.SynthDefRecvMessage( toBytes, completion )
def toBytes : ByteBuffer = {
val baos = new ByteArrayOutputStream
val dos = new DataOutputStream( baos )
dos.writeInt( 0x53436766 ) // magic cookie 'SCgf'
dos.writeInt( 1 ) // version
dos.writeShort( 1 ) // number of defs in file.
write( dos )
dos.flush()
dos.close()
ByteBuffer.wrap( baos.toByteArray ).asReadOnlyBuffer()
}
private def write( dos: DataOutputStream ) {
writePascalString( dos, name )
graph.write( dos )
}
// def load { load() }
def load( server: Server = Server.default, dir: String = defaultDir,
completion: Completion = NoCompletion ) {
write( dir )
sendWithAction( server, loadMsg( dir, _ ), completion, "load" )
}
def loadMsg : osc.SynthDefLoadMessage = loadMsg()
def loadMsg( dir: String = defaultDir, completion: Option[ Packet ] = None ) =
osc.SynthDefLoadMessage( dir + sep + name + ".scsyndef", completion )
// def play: Synth = play()
def play( target: Node = Server.default, args: Seq[ ControlSetMap ] = Nil, addAction: AddAction = addToHead ) : Synth = {
val synth = new Synth( target.server )
val newMsg = synth.newMsg( name, target, args, addAction )
target.server ! recvMsg( newMsg )
synth
}
// def write { write() }
def write( dir: String = defaultDir, overwrite: Boolean = true ) {
val file = new File( dir, name + ".scsyndef" )
val exists = file.exists
if( overwrite ) {
if( exists ) file.delete
SynthDef.write( file.getAbsolutePath, List( this ))
} else if( !exists ) {
SynthDef.write( file.getAbsolutePath, List( this ))
}
}
@inline private def writePascalString( dos: DataOutputStream, str: String ) {
dos.writeByte( str.size )
dos.write( str.getBytes )
}
def hexDump() {
Packet.printHexOn( toBytes, Console.out )
}
def testTopoSort() {
var i = 0
graph.ugens.foreach { ru =>
var j = 0
ru.inputSpecs.foreach { spec =>
if( (spec._1 >= 0) && (spec._1 <= i) ) {
sys.error( "Test failed : ugen " + i + " = " + ru.ugen + " -> input " + j + " = " + spec )
}
j += 1
}
i += 1
}
println( "Test succeeded." )
}
def debugDump() {
var i = 0
graph.ugens.foreach { ru =>
println( "#" + i + " : " + ru.ugen.name +
(if( ru.ugen.specialIndex != 0 ) "-" + ru.ugen.specialIndex else "") + ru.inputSpecs.map({
case (-1, idx) => graph.constants( idx ).toString
case (uidx, oidx) =>
val ru = graph.ugens( uidx ); "#" + uidx + " : " + ru.ugen.name +
(if( oidx > 0 ) "@" + oidx else "")
}).mkString( "( ", ", ", " )" ))
i += 1
}
}
}
object SynthDef {
type Completion = Comp[ SynthDef ]
final val NoCompletion = Comp[ SynthDef ]( None, None )
var defaultDir = System.getProperty( "java.io.tmpdir" )
def apply( name: String )( thunk: => Unit ) : SynthDef = SynthDef( name, SynthGraph( thunk ).expand )
def recv( name: String, server: Server = Server.default, completion: Completion = NoCompletion )
( thunk: => Unit ) : SynthDef = {
val d = apply( name )( thunk )
d.recv( server, completion )
d
}
def write( path: String, defs: Seq[ SynthDef ]) {
val os = new FileOutputStream( path )
val dos = new DataOutputStream( new BufferedOutputStream( os ))
try {
dos.writeInt( 0x53436766 ) // magic cookie
dos.writeInt( 1 ) // version
dos.writeShort( defs.size ) // number of defs in file.
defs.foreach( _.write( dos ))
}
finally {
dos.close()
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy