de.sciss.jcollider
Class SynthDef

java.lang.Object
  extended by de.sciss.jcollider.SynthDef
All Implemented Interfaces:
Constants

public class SynthDef
extends Object
implements Constants

This is the representation of a UGen graph, a prototype for a synth node. While it was created to mimic most of the behaviour of the SClang counterpart, a lot of the internals are slightly different, including the whole idea of how a UGen graph is represented.

While in SClang as an interpreter language the graph is represented as a function, this is not appropriate for java as a (semi)compiled language. Therefore, you do not pass a graph function to the constructor, but rather a collection of graph elements (UGens and Constants) which have already been put together.

This also implies that there is no function header which can be read by SynthDef to automatically construct Control UGens from the function's arguments. You therefore have to create Control UGens explicitly yourself. SynthDef will find them and construct the synth def binary object accordingly.

Note that this class includes the functionality found separately in SClang's SynthDesc class, that is methods for reading and formatting a synth def. Unlike SClang, when a synth def is read, UGens are created as instances of the UGen class. In an earlier version, java.lang.reflect was used to dynamically load UGen subclasses. This concept was dropped because it would imply

So instead, there is one clumsy UGen class which carries all the information about inlets and outlets. A synth def file is sufficient to recreate the graph tree using this class. On the other side, when you yourself create a UGen tree, another objects comes in, the UGenInfo which acts as a lookup table for installed UGen clases.

This class is somewhat more simple than the SClang counterpart. For example, input rate consistency is not checked. Tree optimization is still inferior because of the non-existing UGen subclasses (like BinaryOpUGen) that could handle context-sensitive optimization. Control-lags and Trigger-controls are not supported. For the sake of cleanness, all the strange interpenetration of SynthDef and UGen in the building process as exhibited by SClang was dropped, where the def would go and write things into the UGen and vice versa, setting up temporary fields like the building-def and so on. So this implementation is more stripped down but way cleaner and less spaghetti.

There seemed to be a non-finished project in SClang's SynthDef called "variants". i don't know what this was, it has was just been dropped.

Here is an example of building a SynthDef (comments are below):


        GraphElem   f       = null;
        GraphElem   g, h;
        Control     c       = Control.kr( new String[] { "resinv" }, new float[] { 0.5f });
        UGenChannel reso    = c.getChannel( 0 );
        Synth       synth;
        Random      r       = new Random( System.currentTimeMillis() );
        String      defName = "JNoiseBusiness1b";
        OSCBundle   bndl;
        SynthDef    def;
        long        time;
        
        f = null;
        for( int i = 0; i < 4; i++ ) {
            g = UGen.ar( "*", UGen.ar( "LFSaw", UGen.kr( "midicps", UGen.kr( "MulAdd",
                UGen.kr( "LFPulse", UGen.ir( 0.06f ), UGen.ir( 0 ), UGen.ir( 0.5f )),
                    UGen.ir( 2 ), UGen.array( UGen.ir( 34 + r.nextFloat() * 0.2f ),
                                              UGen.ir( 34 + r.nextFloat() * 0.2f ))))),
                  UGen.ir( 0.01f ));
            f = (f == null) ? g : UGen.ar( "+", f, g );
        }
        h   = UGen.kr( "LinExp", UGen.kr( "SinOsc", UGen.ir( 0.07f )),
                  UGen.ir( -1 ), UGen.ir( 1 ), UGen.ir( 300 ), UGen.ir( 5000 ));
        f   = UGen.ar( "softclip", UGen.ar( "RLPF", f, h, reso ));
        f   = UGen.ar( "softclip", UGen.ar( "RLPF", f, h, reso ));
        def = new SynthDef( defName, UGen.ar( "Out", UGen.ir( 0 ), f ));
        
        synth = Synth.basicNew( defName, myServer );
        try {
            def.send( myServer, synth.newMsg( myServer.asTarget(),
                new String[] { "resinv" }, new float[] { 0.98f }));
            time = System.currentTimeMillis();
            for( int i = 500; i < 5000; i += 250 ) {
                bndl = new OSCBundle( time + i );
                bndl.addPacket( synth.setMsg( "resinv", r.nextFloat() * 0.8f + 0.015f ));
                myServer.sendBundle( bndl );
            }
            bndl = new OSCBundle( time + 5500 );
            bndl.addPacket( synth.freeMsg() );
            myServer.sendBundle( bndl );
        }
        catch( IOException e1 ) {
            System.err.println( e1 );
        }
        
Yes, it's true, the code is at least three times as big as would be the SClang counter part, but we're definitely focussing on an application different from jit developing synthesizers.

So some remarks on the example (the sound isn't particularly interesting though ;-) : generally it improves readability if me create piece of the graph in more than one line. While the loop body is difficult to read, the statement that adds clipping and resonance is easy. Since we have only one dead end for the graph (Out.ar), we simply pass the result of UGen.ar( "Out" ... ) to the synth def constructor. to overwrite the resonance control default value of 0.5 (as specified in the Control constructor), we add control name and value parameters to the synth.newMsg call. the result of this call is an OSCMessage (whereas new Synth( ... ) would have sent that message immediately), this is passed to the synth def constructor as the completion message. the rest shows you how to send bundles to set the resonance value of the synth at certain times.

You will probably want to use SClang to prototype the synthesizers and then just port them to JCollider which shouldn't be too difficult after some practising. See the JColliderDemo for more examples of UGen graphs.

Version:
0.32, 25-Feb-08
Author:
Hanns Holger Rutz
Todo:
for the same synth def, the graphs produced by SClang and JCollider can look slightly different regarding the ordering of the topology. this should be reviewed more thoroughly. It doesn't seem that JCollider is less efficient (from the CPU loads point of view), but it makes comparison and debugging a bit tricky

Field Summary
static int SCGF_VERSION
          Currently supported synth def file version (1).
static String SUFFIX
          Default file suffix when writing defs to disk.
 
Fields inherited from interface de.sciss.jcollider.Constants
kAddAfter, kAddBefore, kAddReplace, kAddToHead, kAddToTail, kAudioRate, kControlRate, kDemandRate, kDoneFree, kDoneFreeAll, kDoneFreeAllPred, kDoneFreeAllSucc, kDoneFreeGroup, kDoneFreePausePred, kDoneFreePauseSucc, kDoneFreePred, kDoneFreePredGroup, kDoneFreePredGroupDeep, kDoneFreeSucc, kDoneFreeSuccGroup, kDoneFreeSuccGroupDeep, kDoneNothing, kDonePause, kDumpBoth, kDumpHex, kDumpOff, kDumpText, kHeaderAIFF, kHeaderIRCAM, kHeaderNeXT, kHeaderRaw, kHeaderWAVE, kSampleALaw, kSampleDouble, kSampleFloat, kSampleInt16, kSampleInt24, kSampleInt32, kSampleInt8, kSampleMuLaw, kScalarRate
 
Constructor Summary
SynthDef(String name, GraphElem graph)
          Constructs a new SynthDef from the given graph element.
 
Method Summary
 String getName()
          Returns the name of the synth definition
 List getUGens()
          Return a list of all UGens in the graph (in the depth-first sorted topological order).
static boolean isDefFile(File path)
          Checks to see if a given file is a synth definition file.
 void load(Server s)
          Stores the def in a temp file and sends a corresponding OSC /d_load message to the server.
 void load(Server s, OSCMessage completionMsg)
          Stores the def in a temp file and sends a corresponding OSC /d_load message to the server.
 void load(Server s, OSCMessage completionMsg, File path)
          Stores the def in a file and sends a corresponding OSC /d_load message to the server.
 Synth play(Group target)
          Sends the def to the server and creates a synth from this def.
 Synth play(Group target, String[] argNames, float[] argValues)
          Sends the def to the server and creates a synth from this def.
 Synth play(Node target, String[] argNames, float[] argValues, int addAction)
          Sends the def to the server and creates a synth from this def.
 void printOn(PrintStream out)
          Prints a textual representation of the synth def to the given stream.
static SynthDef read(InputStream is)
          Reads a single SynthDef from an input stream (such as a harddisk file or memory buffer).
static SynthDef[] readDefFile(File path)
          Reads definitions from a synth def file.
static SynthDef[] readDefFile(InputStream is)
          Reads definitions from an input stream (such as a harddisk file or memory buffer).
static SynthDef[] readDefFile(URL path)
          Reads definitions from a synth def file.
 OSCMessage recvMsg()
          Constructs a message to sends to a server for providing the synth def.
 OSCMessage recvMsg(OSCMessage completionMsg)
          Constructs a message to sends to a server for providing the synth def.
 void send(Server server)
          Sends the definition to a server.
 void send(Server server, OSCMessage completionMsg)
          Sends the definition to a server.
 void write(OutputStream os)
          Writes this def to an output stream (such as a file or a memory buffer).
 void writeDefFile(File path)
          Writes this def to a definition file.
static void writeDefFile(File path, SynthDef[] defs)
          Writes an array of definitions to a file.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Field Detail

SUFFIX

public static final String SUFFIX
Default file suffix when writing defs to disk.

See Also:
Constant Field Values

SCGF_VERSION

public static final int SCGF_VERSION
Currently supported synth def file version (1).

See Also:
Constant Field Values
Constructor Detail

SynthDef

public SynthDef(String name,
                GraphElem graph)
Constructs a new SynthDef from the given graph element.

Parameters:
name - the name of the synth def as would be used to instantiate a Synth
graph - a graph element such as a UGen or a collection of ugens. Basically anything that comes out of one of the static contructor methods of the UGen class. Note that when there are several "dead ends" in the graph, those dead ends should be collected in a GraphElemArray which is then passed to SynthDef, otherwise the synthdef may be incomplete. See the JColliderDemo to see how to do it.
Method Detail

send

public void send(Server server)
          throws IOException
Sends the definition to a server.

Parameters:
server - to representation of the server to send the def to
Throws:
IOException - if a network error occured

send

public void send(Server server,
                 OSCMessage completionMsg)
          throws IOException
Sends the definition to a server. The server will execute the optional completion message when it has processed the definition.

Parameters:
server - to representation of the server to send the def to
completionMsg - message to execute by the server when the synth def has become available. typically something like Synth.newMsg( ... ). may be null
Throws:
IOException - if a network error occured

recvMsg

public OSCMessage recvMsg()
                   throws IOException
Constructs a message to sends to a server for providing the synth def.

Returns:
message ready to send to a server
Throws:
IOException - when synth def compilation fails (? this should never happen?)

recvMsg

public OSCMessage recvMsg(OSCMessage completionMsg)
                   throws IOException
Constructs a message to sends to a server for providing the synth def. The optional completion message is attached to the returned message and will be executed by the server, when the definition has become available.

Parameters:
completionMsg - completion message, such as /s_new or null
Returns:
message ready to send to a server
Throws:
IOException - when synth def compilation fails (? this should never happen?)

getName

public String getName()
Returns the name of the synth definition


load

public void load(Server s)
          throws IOException
Stores the def in a temp file and sends a corresponding OSC /d_load message to the server.

Parameters:
s - the server to send the def to
Throws:
IOException - if the file could not be created or the message could not be sent

load

public void load(Server s,
                 OSCMessage completionMsg)
          throws IOException
Stores the def in a temp file and sends a corresponding OSC /d_load message to the server.

Parameters:
s - the server to send the def to
completionMsg - an OSC message to be executed when the def was received (can be null)
Throws:
IOException - if the file could not be created or the message could not be sent

load

public void load(Server s,
                 OSCMessage completionMsg,
                 File path)
          throws IOException
Stores the def in a file and sends a corresponding OSC /d_load message to the server.

Parameters:
s - the server to send the def to
completionMsg - an OSC message to be executed when the def was received (can be null)
path - path to a file. if a file by this name already exists, the caller should delete it before calling this method
Throws:
IOException - if the file could not be created or the message could not be sent
Warning:
unlike in SClang, the path denotes the file not the parent folder of the file

play

public Synth play(Group target)
           throws IOException
Sends the def to the server and creates a synth from this def.

Parameters:
target - the group to whose head the node is added
Returns:
the newly created synth
Throws:
IOException - if a network error occurs

play

public Synth play(Group target,
                  String[] argNames,
                  float[] argValues)
           throws IOException
Sends the def to the server and creates a synth from this def.

Parameters:
target - the group to whose head the node is added
argNames - the names of the controls to set. can be null
argValues - the values of the controls. each array element corresponds to the element in argNames with the same index. the sizes of argValues and argNames must be equal. can be null
Returns:
the newly created synth
Throws:
IOException - if a network error occurs

play

public Synth play(Node target,
                  String[] argNames,
                  float[] argValues,
                  int addAction)
           throws IOException
Sends the def to the server and creates a synth from this def.

Parameters:
target - the node to which the new synth is added
argNames - the names of the controls to set. can be null
argValues - the values of the controls. each array element corresponds to the element in argNames with the same index. the sizes of argValues and argNames must be equal. can be null
addAction - the add action re target
Returns:
the newly created synth
Throws:
IOException - if a network error occurs

printOn

public void printOn(PrintStream out)
Prints a textual representation of the synth def to the given stream. This will print a list of all ugens and their wiring. Useful for debugging.

Parameters:
out - the stream to print on, such as System.out
See Also:
System.out
Todo:
resolve alias names for specialIndex synths such as BinaryOpUGen (would require the use of UGenInfo which in turn requires to read in all UGen definitions, some overhead we may not want ... ?)

getUGens

public List getUGens()
Return a list of all UGens in the graph (in the depth-first sorted topological order).

Returns:
list whose elements are of class UGen

isDefFile

public static boolean isDefFile(File path)
                         throws IOException
Checks to see if a given file is a synth definition file.

Parameters:
path - to the synth def file
Returns:
true if the file starts with the synth definition magic cookie. does not check for the synth def file version
Throws:
IOException - if the file could not be read

writeDefFile

public static void writeDefFile(File path,
                                SynthDef[] defs)
                         throws IOException
Writes an array of definitions to a file.

Parameters:
path - path to a file. if a file by this name already exists, the caller should delete it before calling this method
defs - array of definitions which will be written one after another
Throws:
IOException - if the file cannot be opened, denotes a directory, or if a write error occurs
Warning:
unlike in SClang, the path denotes the file not the parent folder of the file

writeDefFile

public void writeDefFile(File path)
                  throws IOException
Writes this def to a definition file. That it, the resulting file will contain just one definition, that is us.

Parameters:
path - path to a file. if a file by this name already exists, the caller should delete it before calling this method
Throws:
IOException - if the file cannot be opened, denotes a directory, or if a write error occurs
Warning:
unlike in SClang, the path denotes the file not the parent folder of the file

write

public void write(OutputStream os)
           throws IOException
Writes this def to an output stream (such as a file or a memory buffer).

Parameters:
os - stream to write to. the stream will be buffered by this method, so you do not need to do this
Throws:
IOException - if a write error occurs

readDefFile

public static SynthDef[] readDefFile(URL path)
                              throws IOException
Reads definitions from a synth def file.

Parameters:
path - the location of the synth def file such as a local harddisk or remote server file
Returns:
an array of all definitions found in the file
Throws:
IOException - if a read error occurs, if the file has not a valid synth def format or if the synth def file version is unsupported (greater than SCFG_VERSION)

readDefFile

public static SynthDef[] readDefFile(File path)
                              throws IOException
Reads definitions from a synth def file.

Parameters:
path - the location of the synth def file
Returns:
an array of all definitions found in the file
Throws:
IOException - if a read error occurs, if the file has not a valid synth def format or if the synth def file version is unsupported (greater than SCFG_VERSION)

readDefFile

public static SynthDef[] readDefFile(InputStream is)
                              throws IOException
Reads definitions from an input stream (such as a harddisk file or memory buffer).

Parameters:
is - the stream to read from
Returns:
an array of all definitions found in the stream
Throws:
IOException - if a read error occurs, if the stream has not a valid synth def format or if the synth def file version is unsupported (greater than SCFG_VERSION)

read

public static SynthDef read(InputStream is)
                     throws IOException
Reads a single SynthDef from an input stream (such as a harddisk file or memory buffer). Please refer to the SuperCollider document Synth-Definition-File-Format.rtf to read how a synth def is constructed (read the paragraph "a synth-definition is :"). This assumes synth def file format version 1 as used by SuperCollider as of september 2005.

Parameters:
is - the stream to read from with the current read position placed at the start of a new synth def
Returns:
the decoded synth def
Throws:
IOException - if a read error occurs