diff --git a/Help/Server/b_alloc.help.lhs b/Help/Server/b_alloc.help.lhs
--- a/Help/Server/b_alloc.help.lhs
+++ b/Help/Server/b_alloc.help.lhs
@@ -1,10 +1,1 @@
-/b_alloc                                Allocate buffer space
-
-int   - buffer number
-int   - number of frames
-int   - number of channels (optional. default = 1 channel)
-bytes - an OSC message to execute upon completion. (optional)
-
-Allocates zero filled buffer to number of channels and samples.
-
-Asynchronous. Replies to sender with /done when complete.
+> Sound.SC3.Server.Help.viewServerHelp "/b_alloc"
diff --git a/Help/Server/b_allocRead.help.lhs b/Help/Server/b_allocRead.help.lhs
--- a/Help/Server/b_allocRead.help.lhs
+++ b/Help/Server/b_allocRead.help.lhs
@@ -1,15 +1,1 @@
-/b_allocRead            Allocate buffer space and read a sound file
-
-int    - buffer number
-string - path name of a sound file.
-int    - starting frame in file (optional. default = 0)
-int    - number of frames to read (optional. default = 0, see below)
-bytes  - an OSC message to execute upon completion. (optional)
-
-Allocates buffer to number of channels of file and number of samples
-requested, or fewer if sound file is smaller than requested. Reads
-sound file data from the given starting frame in the file. If the
-number of frames argument is less than or equal to zero, the entire
-file is read.
-
-Asynchronous. Replies to sender with /done when complete.
+> Sound.SC3.Server.Help.viewServerHelp "/b_allocRead"
diff --git a/Help/Server/b_allocReadChannel.help.lhs b/Help/Server/b_allocReadChannel.help.lhs
--- a/Help/Server/b_allocReadChannel.help.lhs
+++ b/Help/Server/b_allocReadChannel.help.lhs
@@ -1,13 +1,1 @@
-/b_allocReadChannel     Allocate buffer space and read channels from file
-
-int    - buffer number
-string - path name of a sound file.
-int    - starting frame in file (optional. default = 0)
-int    - number of frames to read (optional. default = 0, see below)
-[int]  - source file channel indices
-bytes  - an OSC message to execute upon completion. (optional)
-
-As b_allocRead, but reads individual channels into the allocated
-buffer in the order specified.
-
-Asynchronous. Replies to sender with /done when complete.
+> Sound.SC3.Server.Help.viewServerHelp "/b_allocReadChannel"
diff --git a/Help/Server/b_close.help.lhs b/Help/Server/b_close.help.lhs
--- a/Help/Server/b_close.help.lhs
+++ b/Help/Server/b_close.help.lhs
@@ -1,6 +1,1 @@
-/b_close        Close attached soundfile and write header information
-
-int - buffer number
-
-After using a buffer with DiskOut, close the soundfile and write
-header information.
+> Sound.SC3.Server.Help.viewServerHelp "/b_close"
diff --git a/Help/Server/b_fill.help.lhs b/Help/Server/b_fill.help.lhs
--- a/Help/Server/b_fill.help.lhs
+++ b/Help/Server/b_fill.help.lhs
@@ -1,13 +1,1 @@
-/b_fill                              Fill ranges of sample value(s)
-
-int     - buffer number
-[
-  int   - sample starting index
-  int   - number of samples to fill (M)
-  float - value
-] * N
-
-Set contiguous ranges of sample indices to single values. For each
-range, the starting sample index is given followed by the number of
-samples to change, followed by the value to fill. This is only meant
-for setting a few samples, not whole buffers or large sections.
+> Sound.SC3.Server.Help.viewServerHelp "/b_fill"
diff --git a/Help/Server/b_free.help.lhs b/Help/Server/b_free.help.lhs
--- a/Help/Server/b_free.help.lhs
+++ b/Help/Server/b_free.help.lhs
@@ -1,8 +1,1 @@
-/b_free                                         Free buffer data.
-
-int   - buffer number
-bytes - an OSC message to execute upon completion (optional)
-
-Frees buffer space allocated for this buffer.
-
-Asynchronous. Replies to sender with /done when complete.
+> Sound.SC3.Server.Help.viewServerHelp "/b_free"
diff --git a/Help/Server/b_gen.help.lhs b/Help/Server/b_gen.help.lhs
--- a/Help/Server/b_gen.help.lhs
+++ b/Help/Server/b_gen.help.lhs
@@ -1,9 +1,1 @@
-/b_gen                              Call a command to fill a buffer
-
-int    - buffer number
-string - command name
-...    - command arguments
-
-Plug-ins can define commands that operate on buffers. The arguments
-after the command name are defined by the command. The currently
-defined buffer fill commands are listed below in a separate section.
+> Sound.SC3.Server.Help.viewServerHelp "/b_gen"
diff --git a/Help/Server/b_get.help.lhs b/Help/Server/b_get.help.lhs
--- a/Help/Server/b_get.help.lhs
+++ b/Help/Server/b_get.help.lhs
@@ -1,8 +1,1 @@
-/b_get					Get sample value(s)
-
-int   - buffer number
-[
-  int - a sample index
-] * N
-
-Replies to sender with the corresponding /b_set command.
+> Sound.SC3.Server.Help.viewServerHelp "/b_get"
diff --git a/Help/Server/b_getn.help.lhs b/Help/Server/b_getn.help.lhs
--- a/Help/Server/b_getn.help.lhs
+++ b/Help/Server/b_getn.help.lhs
@@ -1,11 +1,11 @@
-/b_getn                               Get ranges of sample value(s)
+> Sound.SC3.Server.Help.viewServerHelp "/b_getn"
 
-int   - buffer number
-[
-  int - starting sample index
-  int - number of sequential samples to get (M)
-] * N
+> import Sound.OSC {- hosc -}
+> import Sound.SC3 {- hsc3 -}
+> import Sound.SC3.Plot {- hsc3-plot -}
 
-Get contiguous ranges of samples. Replies to sender with the
-corresponding /b_setn command. This is only meant for getting a few
-samples, not whole buffers or large sections.
+> let fn = "/home/rohan/data/audio/pf-c5.aif"
+> in withSC3 (async (b_allocRead 0 fn 0 0))
+
+> d <- withSC3 (b_getn1_data_segment 1024 0 (0,2^15))
+> plotTable [d]
diff --git a/Help/Server/b_query.help.lhs b/Help/Server/b_query.help.lhs
--- a/Help/Server/b_query.help.lhs
+++ b/Help/Server/b_query.help.lhs
@@ -1,15 +1,1 @@
-/b_query
-
-[
-  int - buffer number
-] * N
-
-Responds to the sender with a /b_info message.  The arguments to
-/b_info are as follows:
-
-[
-  int   - buffer number
-  int   - number of frames
-  int   - number of channels
-  float - sample rate
-] * N
+> Sound.SC3.Server.Help.viewServerHelp "/b_query"
diff --git a/Help/Server/b_read.help.lhs b/Help/Server/b_read.help.lhs
--- a/Help/Server/b_read.help.lhs
+++ b/Help/Server/b_read.help.lhs
@@ -1,17 +1,1 @@
-/b_read                Read sound file data into an existing buffer
-
-int    - buffer number
-string - path name of a sound file
-int    - starting frame in file (optional, default = 0)
-int    - number of frames to read (optional, default = -1, see below)
-int    - starting frame in buffer (optional, default = 0)
-int    - leave file open (optional, default = 0)
-bytes  - an OSC message to execute upon completion (optional)
-
-Reads sound file data from the given starting frame in the file and
-writes it to the given starting frame in the buffer. If number of
-frames is less than zero, the entire file is read.  If reading a file
-to be used by DiskIn ugen then you will want to set "leave file open"
-to one, otherwise set it to zero.
-
-Asynchronous.  Replies to sender with /done when complete.
+> Sound.SC3.Server.Help.viewServerHelp "/b_read"
diff --git a/Help/Server/b_set.help.lhs b/Help/Server/b_set.help.lhs
--- a/Help/Server/b_set.help.lhs
+++ b/Help/Server/b_set.help.lhs
@@ -1,10 +1,1 @@
-/b_set					    Set sample value(s)
-
-int     - buffer number
-[
-  int   - a sample index
-  float - a sample value
-] * N
-
-Takes a list of pairs of sample indices and values and sets the
-samples to those values.
+> Sound.SC3.Server.Help.viewServerHelp "/b_set"
diff --git a/Help/Server/b_setn.help.lhs b/Help/Server/b_setn.help.lhs
--- a/Help/Server/b_setn.help.lhs
+++ b/Help/Server/b_setn.help.lhs
@@ -1,14 +1,1 @@
-/b_setn                               Set ranges of sample value(s)
-
-int       - buffer number
-[
-  int     - sample starting index
-  int     - number of sequential samples to change (M)
-  [
-    float - a sample value
-  ] * M
-] * N
-
-Set contiguous ranges of sample indices to sets of values. For each
-range, the starting sample index is given followed by the number of
-samples to change, followed by the values.
+> Sound.SC3.Server.Help.viewServerHelp "/b_setn"
diff --git a/Help/Server/b_write.help.lhs b/Help/Server/b_write.help.lhs
--- a/Help/Server/b_write.help.lhs
+++ b/Help/Server/b_write.help.lhs
@@ -1,27 +1,1 @@
-/b_write					Write sound file data
-
-int    - buffer number
-string - path name of a sound file
-string - header format
-string - sample format
-int    - number of frames to write (optional. default = -1, see below)
-int    - starting frame in buffer (optional. default = 0)
-int    - leave file open (optional. default = 0)
-bytes  - an OSC message to execute upon completion (optional)
-
-Write a buffer as a sound file.  
-
-Header format is one of: "aiff", "next", "wav", "ircam"", "raw" 
-
-Sample format is one of: "int8", "int16", "int24", "int32", "float",
-"double", "mulaw", "alaw"
-
-Not all combinations of header format and sample format are possible.
-If number of frames is less than zero, all samples from the starting
-frame to the end of the buffer are written.  If opening a file to be
-used by DiskOut ugen then you will want to set "leave file open" to
-one, otherwise set it to zero. If "leave file open" is set to one then
-the file is created, but no frames are written until the DiskOut ugen
-does so.
-
-Asynchronous. Replies to sender with /done when complete.
+> Sound.SC3.Server.Help.viewServerHelp "/b_write"
diff --git a/Help/Server/b_zero.help.lhs b/Help/Server/b_zero.help.lhs
--- a/Help/Server/b_zero.help.lhs
+++ b/Help/Server/b_zero.help.lhs
@@ -1,8 +1,1 @@
-/b_zero                                       Zero sample data
-
-int   - buffer number
-bytes - an OSC message to execute upon completion. (optional)
-
-Sets all samples in the buffer to zero.
-
-Asynchronous. Replies to sender with /done when complete.
+> Sound.SC3.Server.Help.viewServerHelp "/b_zero"
diff --git a/Help/Server/c_fill.help.lhs b/Help/Server/c_fill.help.lhs
--- a/Help/Server/c_fill.help.lhs
+++ b/Help/Server/c_fill.help.lhs
@@ -1,11 +1,1 @@
-/c_fill                                 Fill ranges of bus value(s)
-
-[
-  int   - starting bus index
-  int   - number of buses to fill (M)
-  float - value
-] * N
-
-Set contiguous ranges of buses to single values. For each range, the
-starting sample index is given followed by the number of buses to
-change, followed by the value to fill.
+> Sound.SC3.Server.Help.viewServerHelp "/c_fill"
diff --git a/Help/Server/c_get.help.lhs b/Help/Server/c_get.help.lhs
--- a/Help/Server/c_get.help.lhs
+++ b/Help/Server/c_get.help.lhs
@@ -1,8 +1,1 @@
-/c_get					           Get bus value(s)
-
-[
-  int - a bus index
-] * N
-
-Takes a list of buses and replies to sender with the corresponding
-/c_set command.
+> Sound.SC3.Server.Help.viewServerHelp "/c_get"
diff --git a/Help/Server/c_getn.help.lhs b/Help/Server/c_getn.help.lhs
--- a/Help/Server/c_getn.help.lhs
+++ b/Help/Server/c_getn.help.lhs
@@ -1,9 +1,1 @@
-/c_getn					Get ranges of bus value(s)
-
-[
-  int - starting bus index
-  int - number of sequential buses to get (M)
-] * N
-
-Get contiguous ranges of buses. Replies to sender with the
-corresponding /c_setn command.
+> Sound.SC3.Server.Help.viewServerHelp "/c_getn"
diff --git a/Help/Server/c_set.help.lhs b/Help/Server/c_set.help.lhs
--- a/Help/Server/c_set.help.lhs
+++ b/Help/Server/c_set.help.lhs
@@ -1,9 +1,1 @@
-/c_set                                               Set bus value(s)
-
-[
-  int   - a bus index
-  float - a control value
-] * N
-
-Takes a list of pairs of bus indices and values and sets the buses to
-those values.
+> Sound.SC3.Server.Help.viewServerHelp "/c_set"
diff --git a/Help/Server/c_setn.help.lhs b/Help/Server/c_setn.help.lhs
--- a/Help/Server/c_setn.help.lhs
+++ b/Help/Server/c_setn.help.lhs
@@ -1,13 +1,1 @@
-/c_setn                                    Set ranges of bus value(s)
-
-[
-  int     - starting bus index
-  int     - number of sequential buses to change (M)
-  [
-    float - a control value
-  ] * M
-] * N
-
-Set contiguous ranges of buses to sets of values. For each range, the
-starting bus index is given followed by the number of channels to
-change, followed by the values.
+> Sound.SC3.Server.Help.viewServerHelp "/c_setn"
diff --git a/Help/Server/clearSched.help.lhs b/Help/Server/clearSched.help.lhs
--- a/Help/Server/clearSched.help.lhs
+++ b/Help/Server/clearSched.help.lhs
@@ -1,3 +1,1 @@
-/clearSched                           Clear all scheduled bundles
-
-Removes all bundles from the scheduling queue.
+> Sound.SC3.Server.Help.viewServerHelp "/clearSched"
diff --git a/Help/Server/d_free.help.lhs b/Help/Server/d_free.help.lhs
--- a/Help/Server/d_free.help.lhs
+++ b/Help/Server/d_free.help.lhs
@@ -1,7 +1,1 @@
-/d_free                                Delete synth definition
-
-[
-  string - synth def name
-] * N
-
-Removes a synth definition once all synths using it have ended.
+> Sound.SC3.Server.Help.viewServerHelp "/d_free"
diff --git a/Help/Server/d_load.help.lhs b/Help/Server/d_load.help.lhs
--- a/Help/Server/d_load.help.lhs
+++ b/Help/Server/d_load.help.lhs
@@ -1,9 +1,1 @@
-/d_load                                            Load synth definition
-
-string - pathname of file. Can be a pattern like "synthdefs/perc-*"
-bytes  - an OSC message to execute upon completion. (optional)
-
-Loads a file of synth definitions. Resident definitions with the same
-names are overwritten.
-
-Asynchronous. Replies to sender with /done when complete.
+> Sound.SC3.Server.Help.viewServerHelp "/d_load"
diff --git a/Help/Server/d_loadDir.help.lhs b/Help/Server/d_loadDir.help.lhs
--- a/Help/Server/d_loadDir.help.lhs
+++ b/Help/Server/d_loadDir.help.lhs
@@ -1,9 +1,1 @@
-/d_loadDir                             Load directory of synth definitions
-
-string - pathname of directory.
-bytes  - an OSC message to execute upon completion. (optional)
-
-Loads a directory of synth definitions. Resident definitions with the
-same names are overwritten.
-
-Asynchronous. Replies to sender with /done when complete.
+> Sound.SC3.Server.Help.viewServerHelp "/d_loadDir"
diff --git a/Help/Server/d_recv.help.lhs b/Help/Server/d_recv.help.lhs
--- a/Help/Server/d_recv.help.lhs
+++ b/Help/Server/d_recv.help.lhs
@@ -1,9 +1,1 @@
-/d_recv                             Receive a synth definition file
-
-bytes - buffer of data.
-bytes - an OSC message to execute upon completion. (optional)
-
-Loads a file of synth definitions from a buffer in the message.
-Resident definitions with the same names are overwritten.
-
-Asynchronous. Replies to sender with /done when complete.
+> Sound.SC3.Server.Help.viewServerHelp "/d_recv"
diff --git a/Help/Server/dumpOSC.help.lhs b/Help/Server/dumpOSC.help.lhs
--- a/Help/Server/dumpOSC.help.lhs
+++ b/Help/Server/dumpOSC.help.lhs
@@ -1,13 +1,1 @@
-/dumpOSC                                  Display incoming OSC messages
-
-PrintLevel (int) - code
-
-Turns on and off printing of the contents of incoming Open Sound
-Control messages. This is useful when debugging your command stream.
-
-The values for the code are as follows:
-
-NoPrinter   (0) - turn dumping OFF.
-TextPrinter (1) - print the parsed contents of the message.
-HexPrinter  (2) - print the contents in hexadecimal.
-AllPrinter  (3) - print both the parsed and hexadecimal representations of the contents.
+> Sound.SC3.Server.Help.viewServerHelp "/dumpOSC"
diff --git a/Help/Server/g_deepFree.help.lhs b/Help/Server/g_deepFree.help.lhs
--- a/Help/Server/g_deepFree.help.lhs
+++ b/Help/Server/g_deepFree.help.lhs
@@ -1,9 +1,1 @@
-/g_deepFree	 Free all synths in this group and all its sub-groups
-
-[
-  int - group ID
-] * N
-
-Traverses all groups below this group and frees all the synths.
-Sub-groups are not freed. A list of groups may be specified.
-
+> Sound.SC3.Server.Help.viewServerHelp "/g_deepFree"
diff --git a/Help/Server/g_freeAll.help.lhs b/Help/Server/g_freeAll.help.lhs
--- a/Help/Server/g_freeAll.help.lhs
+++ b/Help/Server/g_freeAll.help.lhs
@@ -1,7 +1,1 @@
-/g_freeAll                          Delete all nodes in a group
-
-[
-  int - group ID
-] * N
-
-Frees all nodes in the group. A list of groups may be specified.
+> Sound.SC3.Server.Help.viewServerHelp "/g_freeAll"
diff --git a/Help/Server/g_head.help.lhs b/Help/Server/g_head.help.lhs
--- a/Help/Server/g_head.help.lhs
+++ b/Help/Server/g_head.help.lhs
@@ -1,8 +1,1 @@
-/g_head      		              Add node to head of group
-
-[
-  int - group ID
-  int - node ID
-] * N
-
-Adds the node to the head (first to be executed) of the group.
+> Sound.SC3.Server.Help.viewServerHelp "/g_head"
diff --git a/Help/Server/g_new.help.lhs b/Help/Server/g_new.help.lhs
--- a/Help/Server/g_new.help.lhs
+++ b/Help/Server/g_new.help.lhs
@@ -1,13 +1,1 @@
-/g_new				              Create a new group
-
-[
-  int - new group ID
-  int - add action (enumeration)
-  int - add target ID
-] * N
-
-Create a new group and add it to the tree of nodes.
-
-For add action definitions see /s_new.
-
-Multiple groups may be created in one command by adding arguments.
+> Sound.SC3.Server.Help.viewServerHelp "/g_new"
diff --git a/Help/Server/g_tail.help.lhs b/Help/Server/g_tail.help.lhs
--- a/Help/Server/g_tail.help.lhs
+++ b/Help/Server/g_tail.help.lhs
@@ -1,8 +1,1 @@
-/g_tail                               Add node to tail of group
-
-[
-  int - group ID
-  int - node ID
-] * N
-
-Adds the node to the tail (last to be executed) of the group.
+> Sound.SC3.Server.Help.viewServerHelp "/g_tail"
diff --git a/Help/Server/n_after.help.lhs b/Help/Server/n_after.help.lhs
--- a/Help/Server/n_after.help.lhs
+++ b/Help/Server/n_after.help.lhs
@@ -1,9 +1,1 @@
-/n_after			      Place a node after another
-
-[
-  int - the ID of the node to place (A)
-  int - the ID of the node after which the above is placed (B)
-] * N
-
-Places node A in the same group as node B, to execute immediately
-after node B.
+> Sound.SC3.Server.Help.viewServerHelp "/n_after"
diff --git a/Help/Server/n_before.help.lhs b/Help/Server/n_before.help.lhs
--- a/Help/Server/n_before.help.lhs
+++ b/Help/Server/n_before.help.lhs
@@ -1,9 +1,1 @@
-/n_before			      Place a node before another
-
-[
-  int - the ID of the node to place (A)
-  int - the ID of the node before which the above is placed (B)
-] * N
-
-Places node A in the same group as node B, to execute immediately
-before node B.
+> Sound.SC3.Server.Help.viewServerHelp "/n_before"
diff --git a/Help/Server/n_fill.help.lhs b/Help/Server/n_fill.help.lhs
--- a/Help/Server/n_fill.help.lhs
+++ b/Help/Server/n_fill.help.lhs
@@ -1,13 +1,1 @@
-/n_fill                    Fill ranges of a node's control value(s)
-
-int - node ID
-[
-  int or string - a control index or name
-  int - number of values to fill (M)
-  float - value
-] * N
-
-Set contiguous ranges of control indices to single values. For each
-range, the starting control index is given followed by the number of
-controls to change, followed by the value to fill. If the node is a
-group, then it sets the controls of every node in the group.
+> Sound.SC3.Server.Help.viewServerHelp "/n_fill"
diff --git a/Help/Server/n_free.help.lhs b/Help/Server/n_free.help.lhs
--- a/Help/Server/n_free.help.lhs
+++ b/Help/Server/n_free.help.lhs
@@ -1,9 +1,1 @@
-/n_free                                            Delete a node
-
-[
-  int - node ID
-] * N
-
-Stops a node abruptly, removes it from its group, and frees its
-memory. A list of node IDs may be specified. Using this method can
-cause a click if the node is not silent at the time it is freed.
+> Sound.SC3.Server.Help.viewServerHelp "/n_free"
diff --git a/Help/Server/n_map.help.lhs b/Help/Server/n_map.help.lhs
--- a/Help/Server/n_map.help.lhs
+++ b/Help/Server/n_map.help.lhs
@@ -1,14 +1,1 @@
-/n_map                        Map a node's controls to read from a bus
-
-int          - node ID
-[
-  int|string - a control index or name
-  int        - control bus index
-] * N
-
-Takes a list of pairs of control names or indices and bus indices and
-causes those controls to be read continuously from a global control
-bus instead of responding to n_set, n_setn and n_fill commands. If the
-node is a group, then it maps the controls of every node in the
-group. If the control bus index is -1 then any current mapping is
-undone and control reverts to normal.
+> Sound.SC3.Server.Help.viewServerHelp "/n_map"
diff --git a/Help/Server/n_mapn.help.lhs b/Help/Server/n_mapn.help.lhs
--- a/Help/Server/n_mapn.help.lhs
+++ b/Help/Server/n_mapn.help.lhs
@@ -1,16 +1,1 @@
-/n_mapn                       Map a node's controls to read from buses
-
-int          - node ID
-[
-  int|string - a control index or name
-  int        - control bus index
-  int        - number of controls to map
-] * N
-
-Takes a list of triplets of control names or indices, bus indices, and
-number of controls to map and causes those controls to be mapped
-sequentially to buses. If the node is a group, then it maps the
-controls of every node in the group. If the control bus index is -1
-then any current mapping is undone and control reverts to normal.
-
-See also: /n_map
+> Sound.SC3.Server.Help.viewServerHelp "/n_mapn"
diff --git a/Help/Server/n_query.help.lhs b/Help/Server/n_query.help.lhs
--- a/Help/Server/n_query.help.lhs
+++ b/Help/Server/n_query.help.lhs
@@ -1,10 +1,1 @@
-/n_query                                        Get info about a node
-
-[
-  int - node ID
-] * N
-
-The server sends an /n_info message for each node to registered
-clients.
-
-See Node Notifications below for the format of the /n_info message.
+> Sound.SC3.Server.Help.viewServerHelp "/n_query"
diff --git a/Help/Server/n_run.help.lhs b/Help/Server/n_run.help.lhs
--- a/Help/Server/n_run.help.lhs
+++ b/Help/Server/n_run.help.lhs
@@ -1,11 +1,1 @@
-/n_run                                          Turn node on or off
-
-[
-  int - node ID
-  int - run flag
-] * N
-
-If the run flag set to zero then the node will not be executed.  If
-the run flag is set back to one, then it will be executed.  Using this
-method to start and stop nodes can cause a click if the node is not
-silent at the time run flag is toggled.
+> Sound.SC3.Server.Help.viewServerHelp "/n_run"
diff --git a/Help/Server/n_set.help.lhs b/Help/Server/n_set.help.lhs
--- a/Help/Server/n_set.help.lhs
+++ b/Help/Server/n_set.help.lhs
@@ -1,11 +1,1 @@
-/n_set                                Set a node's control value(s)
-
-int          - node ID
-[
-  int|string - a control index or name
-  float      - a control value
-] * N
-
-Takes a list of pairs of control indices and values and sets the
-controls to those values. If the node is a group, then it sets the
-controls of every node in the group.
+> Sound.SC3.Server.Help.viewServerHelp "/n_set"
diff --git a/Help/Server/n_setn.help.lhs b/Help/Server/n_setn.help.lhs
--- a/Help/Server/n_setn.help.lhs
+++ b/Help/Server/n_setn.help.lhs
@@ -1,15 +1,1 @@
-/n_setn                       Set ranges of a node's control value(s)
-
-int - node ID
-[
-  int|string - a control index or name
-  int        - number of sequential controls to change (M)
-  [
-    float    - a control value
-  ] * M
-] * N
-
-Set contiguous ranges of control indices to sets of values. For each
-range, the starting control index is given followed by the number of
-controls to change, followed by the values. If the node is a group,
-then it sets the controls of every node in the group.
+> Sound.SC3.Server.Help.viewServerHelp "/n_setn"
diff --git a/Help/Server/n_trace.help.lhs b/Help/Server/n_trace.help.lhs
--- a/Help/Server/n_trace.help.lhs
+++ b/Help/Server/n_trace.help.lhs
@@ -1,10 +1,1 @@
-/n_trace                                               Trace a node
-
-[
-  int - node ID
-] * N
-
-Causes a synth to print out the values of the inputs and outputs of
-its unit generators for one control period. Causes a group to print
-the node IDs and names of each node in the group for one control
-period.
+> Sound.SC3.Server.Help.viewServerHelp "/n_trace"
diff --git a/Help/Server/notify.help.lhs b/Help/Server/notify.help.lhs
--- a/Help/Server/notify.help.lhs
+++ b/Help/Server/notify.help.lhs
@@ -1,9 +1,10 @@
-/notify            Register to receive notifications from server
+> Sound.SC3.Server.Help.viewServerHelp "/notify"
 
-int - one to receive notifications, zero to stop receiving them.
+> import Sound.OSC
+> import Sound.SC3.ID
 
-If argument is one, server will remember your return address and
-send you notifications. if argument is zero, server will stop
-sending you notifications.
+> let g = synthdef "g" (out 0 (sinOsc AR (rand 'a' 440 880) 0 * 0.1))
+> in withSC3 (async (d_recv g))
 
-Asynchronous. Replies to sender with /done when complete.
+> withSC3 (withNotifications (do {send (s_new "g" (-2) AddToHead 1 [])
+>                                ;waitReply "/n_go"}))
diff --git a/Help/Server/quit.help.lhs b/Help/Server/quit.help.lhs
--- a/Help/Server/quit.help.lhs
+++ b/Help/Server/quit.help.lhs
@@ -1,7 +1,1 @@
-/quit                                                Quit program
-
-No arguments.
-
-Exits the synthesis server.
-
-Asynchronous. Replies to sender with /done just before completion.
+> Sound.SC3.Server.Help.viewServerHelp "/quit"
diff --git a/Help/Server/s_get.help.lhs b/Help/Server/s_get.help.lhs
--- a/Help/Server/s_get.help.lhs
+++ b/Help/Server/s_get.help.lhs
@@ -1,8 +1,1 @@
-/s_get				      Get control value(s)
-
-int - synth ID
-[
-  int|string - a control index or name
-] * N
-
-Replies to sender with the corresponding /n_set command.
+> Sound.SC3.Server.Help.viewServerHelp "/s_get"
diff --git a/Help/Server/s_getn.help.lhs b/Help/Server/s_getn.help.lhs
--- a/Help/Server/s_getn.help.lhs
+++ b/Help/Server/s_getn.help.lhs
@@ -1,10 +1,1 @@
-/s_getn                          Get ranges of control value(s)
-
-int - synth ID
-[
-  int|string - a control index or name
-  int        - number of sequential controls to get (M)
-] * N
-
-Get contiguous ranges of controls. Replies to sender with the
-corresponding /n_setn command.
+> Sound.SC3.Server.Help.viewServerHelp "/s_getn"
diff --git a/Help/Server/s_new.help.lhs b/Help/Server/s_new.help.lhs
--- a/Help/Server/s_new.help.lhs
+++ b/Help/Server/s_new.help.lhs
@@ -1,38 +1,1 @@
-/s_new                                             Create a new synth
-
-string - synth definition name
-int    - synth ID
-int    - add action (enumeration)
-int    - add target ID
-[
-  int|string - a control index or name
-  float      - a control value
-] * N
-
-Create a new synth from a synth definition, give it an ID, and add it
-to the tree of nodes. There are four ways to add the node to the tree
-as determined by the add action argument which is defined as follows:
-
-AddToHead  - 0 - add the new node to the the head of the target group
-AddToTail  - 1 - add the new node to the the tail of the target group
-AddBefore  - 2 - add the new node just before the target node
-AddAfter   - 3 - add the new node just after the target node
-AddReplace - 4 - the new node replaces the target node, which is freed
-
-Controls may be set when creating the synth. The control arguments are
-the same as for the n_set command.
-
-If you send /s_new with a synth ID of -1, then the server will
-generate an ID for you. The server reserves all negative IDs. Since
-you don't know what the ID is, you cannot talk to this node directly
-later. So this is useful for nodes that are of finite duration and
-that get the control information they need from arguments and buses or
-messages directed to their group. In addition no notifications are
-sent when there are changes of state for this node, such as /go, /end,
-/on, /off.
-
-If you use a node ID of -1 for any other command, such as /n_map, then
-it refers to the most recently created node by /s_new (auto generated
-ID or not). This is how you can map the controls of a node with an
-auto generated ID. In a multi-client situation, the only way you can
-be sure what node -1 refers to is to put the messages in a bundle.
+> Sound.SC3.Server.Help.viewServerHelp "/s_new"
diff --git a/Help/Server/s_noid.help.lhs b/Help/Server/s_noid.help.lhs
--- a/Help/Server/s_noid.help.lhs
+++ b/Help/Server/s_noid.help.lhs
@@ -1,11 +1,1 @@
-/s_noid               Auto-reassign synth's ID to a reserved value
-
-[
-  int - synth ID
-] * N
-
-This command is used when the client no longer needs to communicate
-with the synth and wants to have the freedom to reuse the ID. The
-server will reassign this synth to a reserved negative number. This
-command is purely for bookkeeping convenience of the client. No
-notification is sent when this occurs.
+> Sound.SC3.Server.Help.viewServerHelp "/s_noid"
diff --git a/Help/Server/status.help.lhs b/Help/Server/status.help.lhs
--- a/Help/Server/status.help.lhs
+++ b/Help/Server/status.help.lhs
@@ -1,16 +1,1 @@
-/status				       query the status
-
-No arguments.
-
-Replies to sender with the following message.
-
-/status.reply
-	int    - unused
-	int    - number of unit generators
-	int    - number of synths
-	int    - number of groups
-	int    - number of loaded synth definitions
-	float  - average percent CPU usage for signal processing
-	float  - peak percent CPU usage for signal processing
-	double - nominal sample rate
-	double - actual sample rate
+> Sound.SC3.Server.Help.viewServerHelp "/status"
diff --git a/Help/Server/sync.help.lhs b/Help/Server/sync.help.lhs
--- a/Help/Server/sync.help.lhs
+++ b/Help/Server/sync.help.lhs
@@ -1,9 +1,1 @@
-/sync                notify when async commands have completed.
-
-int - a unique number identifying this command.
-
-Replies with a /synced message when all asynchronous commands received
-before this one have completed. The reply will contain the sent unique
-ID.
-
-Asynchronous. Replies to sender with /synced, ID when complete.
+> Sound.SC3.Server.Help.viewServerHelp "/sync"
diff --git a/Help/Server/tr.help.lhs b/Help/Server/tr.help.lhs
--- a/Help/Server/tr.help.lhs
+++ b/Help/Server/tr.help.lhs
@@ -1,13 +1,1 @@
-/tr				a trigger message
-
-int   - node ID
-int   - trigger ID
-float - trigger value
-
-This command is the mechanism that synths can use to trigger events in
-clients.
-
-The node ID is the node that is sending the trigger. The trigger ID
-and value are determined by inputs to the SendTrig unit generator
-which is the originator of this message.
-
+> Sound.SC3.Server.Help.viewServerHelp "/tr"
diff --git a/Help/Server/u_cmd.help.lhs b/Help/Server/u_cmd.help.lhs
--- a/Help/Server/u_cmd.help.lhs
+++ b/Help/Server/u_cmd.help.lhs
@@ -1,9 +1,1 @@
-/u_cmd                            Send a command to a unit generator
-
-int    - node ID
-int    - unit generator index
-string - command name
-...any arguments
-
-Sends all arguments following the command name to the unit generator
-to be performed. Commands are defined by unit generator plug ins.
+> Sound.SC3.Server.Help.viewServerHelp "/u_cmd"
diff --git a/Help/UGen/Analysis/compander.help.lhs b/Help/UGen/Analysis/compander.help.lhs
--- a/Help/UGen/Analysis/compander.help.lhs
+++ b/Help/UGen/Analysis/compander.help.lhs
@@ -11,17 +11,17 @@
 > audition (out 0 z)
 
 Noise gate
-> let x = mouseX' KR 0.01 1 Linear 0.1
+> let x = mouseX KR 0.01 1 Linear 0.1
 > in audition (out 0 (mce [z, compander z z x 10 1 0.01 0.01]))
 
 Compressor
-> let x = mouseX' KR 0.01 1 Linear 0.1
+> let x = mouseX KR 0.01 1 Linear 0.1
 > in audition (out 0 (mce [z, compander z z x 1 0.5 0.01 0.01]))
 
 Limiter
-> let x = mouseX' KR 0.01 1 Linear 0.1
+> let x = mouseX KR 0.01 1 Linear 0.1
 > in audition (out 0 (mce [z, compander z z x 1 0.1 0.01 0.01]))
 
 Sustainer
-> let x = mouseX' KR 0.01 1 Linear 0.1
+> let x = mouseX KR 0.01 1 Linear 0.1
 > in audition (out 0 (mce [z, compander z z x 0.1 1.0 0.01 0.01]))
diff --git a/Help/UGen/Analysis/pitch.help.lhs b/Help/UGen/Analysis/pitch.help.lhs
--- a/Help/UGen/Analysis/pitch.help.lhs
+++ b/Help/UGen/Analysis/pitch.help.lhs
@@ -3,8 +3,8 @@
 
 > import Sound.SC3
 
-> let {x = mouseX' KR 220 660 Linear 0.1
->     ;y = mouseY' KR 0.05 0.25 Linear 0.1
+> let {x = mouseX KR 220 660 Linear 0.1
+>     ;y = mouseY KR 0.05 0.25 Linear 0.1
 >     ;s = sinOsc AR x 0 * y
 >     ;a = amplitude KR s 0.05 0.05
 >     ;f = pitch s 440 60 4000 100 16 7 0.02 0.5 1}
diff --git a/Help/UGen/Buffer/bufAllpassC.help.lhs b/Help/UGen/Buffer/bufAllpassC.help.lhs
--- a/Help/UGen/Buffer/bufAllpassC.help.lhs
+++ b/Help/UGen/Buffer/bufAllpassC.help.lhs
@@ -4,7 +4,7 @@
 > import Sound.SC3.ID
 
 Allocate buffer
-> withSC3 (\fd -> async fd (b_alloc 0 44100 1))
+> withSC3 (async (b_alloc 0 44100 1))
 
 Filtered decaying noise bursts
 > let {d = dust 'a' AR 1
diff --git a/Help/UGen/Buffer/bufCombC.help.lhs b/Help/UGen/Buffer/bufCombC.help.lhs
--- a/Help/UGen/Buffer/bufCombC.help.lhs
+++ b/Help/UGen/Buffer/bufCombC.help.lhs
@@ -4,7 +4,7 @@
 > import Sound.SC3.ID
 
 Allocate buffer zero (required for examples below)
-> withSC3 (\fd -> async fd (b_alloc 0 44100 1))
+> withSC3 (async (b_alloc 0 44100 1))
 
 Filtered decaying noise bursts
 > let {d = dust 'a' AR 1
diff --git a/Help/UGen/Buffer/bufDelayC.help.lhs b/Help/UGen/Buffer/bufDelayC.help.lhs
--- a/Help/UGen/Buffer/bufDelayC.help.lhs
+++ b/Help/UGen/Buffer/bufDelayC.help.lhs
@@ -4,7 +4,7 @@
 > import Sound.SC3.ID
 
 Allocate buffer zero (required for examples below)
-> withSC3 (\fd -> async fd (b_alloc 0 44100 1))
+> withSC3 (async (b_alloc 0 44100 1))
 
 Dust randomly triggers Decay to create an exponential decay envelope
 for the WhiteNoise input source.  The input is mixed with the delay.
@@ -17,5 +17,5 @@
 > let {t = dust 'a' AR 1
 >     ;n = whiteNoise 'b' AR
 >     ;d = decay t 0.3 * n
->     ;x = mouseX' KR 0.0 0.2 Linear 0.1}
+>     ;x = mouseX KR 0.0 0.2 Linear 0.1}
 > in audition (out 0 (d + bufDelayC 0 d x))
diff --git a/Help/UGen/Buffer/bufDur.help.lhs b/Help/UGen/Buffer/bufDur.help.lhs
--- a/Help/UGen/Buffer/bufDur.help.lhs
+++ b/Help/UGen/Buffer/bufDur.help.lhs
@@ -5,7 +5,7 @@
 
 Load sound file to buffer zero (required for examples)
 > let fn = "/home/rohan/data/audio/pf-c5.aif"
-> in withSC3 (\fd -> async fd (b_allocRead 0 fn 0 0))
+> in withSC3 (async (b_allocRead 0 fn 0 0))
 
 Read without loop, trigger reset based on buffer duration
 > let {t = impulse AR (recip (bufDur KR 0)) 0
diff --git a/Help/UGen/Buffer/bufFrames.help.lhs b/Help/UGen/Buffer/bufFrames.help.lhs
--- a/Help/UGen/Buffer/bufFrames.help.lhs
+++ b/Help/UGen/Buffer/bufFrames.help.lhs
@@ -5,7 +5,7 @@
 
 Load sound file to buffer zero (required for examples)
 > let fn = "/home/rohan/data/audio/pf-c5.aif"
-> in withSC3 (\fd -> async fd (b_allocRead 0 fn 0 0))
+> in withSC3 (async (b_allocRead 0 fn 0 0))
 
 Read without loop, trigger reset based on buffer duration
 > let p = phasor AR 0 (bufRateScale KR 0) 0 (bufFrames KR 0) 0
@@ -13,5 +13,5 @@
 
 Mouse location drags play head
 > let {r = mce [0.05,0.075 .. 0.15]
->     ;p = k2A (mouseX' KR 0 (bufFrames KR 0) Linear r)}
+>     ;p = k2A (mouseX KR 0 (bufFrames KR 0) Linear r)}
 > in audition (out 0 (mix (bufRdL 1 AR 0 p NoLoop)))
diff --git a/Help/UGen/Buffer/bufRateScale.help.lhs b/Help/UGen/Buffer/bufRateScale.help.lhs
--- a/Help/UGen/Buffer/bufRateScale.help.lhs
+++ b/Help/UGen/Buffer/bufRateScale.help.lhs
@@ -5,7 +5,7 @@
 
 Load sound file to buffer zero (required for examples)
 > let fn = "/home/rohan/data/audio/pf-c5.aif"
-> in withSC3 (\fd -> async fd (b_allocRead 0 fn 0 0))
+> in withSC3 (async (b_allocRead 0 fn 0 0))
 
 Read buffer at 3/4 reported sample rate.
 > let {r = 0.75 * bufRateScale KR 0
diff --git a/Help/UGen/Buffer/bufRd.help.lhs b/Help/UGen/Buffer/bufRd.help.lhs
--- a/Help/UGen/Buffer/bufRd.help.lhs
+++ b/Help/UGen/Buffer/bufRd.help.lhs
@@ -5,13 +5,13 @@
 
 Load sound file to buffer zero (required for examples)
 > let fn = "/home/rohan/data/audio/pf-c5.aif"
-> in withSC3 (\fd -> async fd (b_allocRead 0 fn 0 0))
+> in withSC3 (async (b_allocRead 0 fn 0 0))
 
 Audio rate sine oscillator as phase input
 > let phase = (sinOsc AR 0.1 0 * bufFrames KR 0)
 > in audition (out 0 (bufRd 1 AR 0 phase Loop NoInterpolation))
 
 There are constructors, bufRd{N|L|C}, for the fixed cases.
-> let {x = mouseX' KR (mce [5, 10]) 100 Linear 0.1
+> let {x = mouseX KR (mce [5, 10]) 100 Linear 0.1
 >     ;n = lfNoise1 'a' AR x}
 > in audition (out 0 (bufRdL 1 AR 0 (n * bufFrames KR 0) Loop))
diff --git a/Help/UGen/Buffer/bufSampleRate.help.lhs b/Help/UGen/Buffer/bufSampleRate.help.lhs
--- a/Help/UGen/Buffer/bufSampleRate.help.lhs
+++ b/Help/UGen/Buffer/bufSampleRate.help.lhs
@@ -5,7 +5,7 @@
 
 Load sound file to buffer zero (required for examples)
 > let fn = "/home/rohan/data/audio/pf-c5.aif"
-> in withSC3 (\fd -> async fd (b_allocRead 0 fn 0 0))
+> in withSC3 (async (b_allocRead 0 fn 0 0))
 
 Sine tone derived from sample rate of buffer an 440Hz tone.
 > let f = mce [bufSampleRate KR 0 * 0.01, 440]
diff --git a/Help/UGen/Buffer/detectIndex.help.lhs b/Help/UGen/Buffer/detectIndex.help.lhs
--- a/Help/UGen/Buffer/detectIndex.help.lhs
+++ b/Help/UGen/Buffer/detectIndex.help.lhs
@@ -4,13 +4,13 @@
 > import Sound.SC3
 
 Allocate and set values at buffer ten
-> withSC3 (\fd -> async fd (b_alloc_setn1 10 0 [2,3,4,0,1,5]))
+> withSC3 (async (b_alloc_setn1 10 0 [2,3,4,0,1,5]))
 
 Find indexes and map to an audible frequency range.
 > let {n = 6
->     ;x = floorE (mouseX' KR 0 n Linear 0.1)
+>     ;x = floorE (mouseX KR 0 n Linear 0.1)
 >     ;i = detectIndex 10 x}
 > in audition (out 0 (sinOsc AR (linExp i 0 n 200 700) 0 * 0.1))
 
 Free buffer.
-> withSC3 (\fd -> send fd (b_free 10))
+> withSC3 (send (b_free 10))
diff --git a/Help/UGen/Buffer/index.help.lhs b/Help/UGen/Buffer/index.help.lhs
--- a/Help/UGen/Buffer/index.help.lhs
+++ b/Help/UGen/Buffer/index.help.lhs
@@ -4,11 +4,11 @@
 > import Sound.SC3
 
 Allocate and set values at buffer ten
-> withSC3 (\fd -> async fd (b_alloc_setn1 10 0 [50,100,200,400,800,1600]))
+> withSC3 (async (b_alloc_setn1 10 0 [50,100,200,400,800,1600]))
 
 Index buffer for frequency values
 > let f = index 10 (lfSaw KR 2 3 * 4)
 > in audition (out 0 (sinOsc AR (mce [f,f * 9]) 0 * 0.1))
 
 Free buffer
-> withSC3 (\fd -> send fd (b_free 10))
+> withSC3 (send (b_free 10))
diff --git a/Help/UGen/Buffer/indexInBetween.help.lhs b/Help/UGen/Buffer/indexInBetween.help.lhs
--- a/Help/UGen/Buffer/indexInBetween.help.lhs
+++ b/Help/UGen/Buffer/indexInBetween.help.lhs
@@ -4,10 +4,10 @@
 > import Sound.SC3
 
 Allocate and set values at buffer ten
-> withSC3 (\fd -> async fd (b_alloc_setn1 10 0 [200,210,400,430,600,800]))
+> withSC3 (async (b_alloc_setn1 10 0 [200,210,400,430,600,800]))
 
 Index into buffer for frequency values
-> let {f0 = mouseX' KR 200 900 Linear 0.1
+> let {f0 = mouseX KR 200 900 Linear 0.1
 >     ;i = indexInBetween 10 f0
 >     ;l0 = index 10 i
 >     ;l1 = index 10 (i + 1)
@@ -15,4 +15,4 @@
 > in audition (out 0 (sinOsc AR (mce [f0,f1]) 0 * 0.1))
 
 Free buffer
-> withSC3 (\fd -> send fd (b_free 10))
+> withSC3 (send (b_free 10))
diff --git a/Help/UGen/Buffer/osc.help.lhs b/Help/UGen/Buffer/osc.help.lhs
--- a/Help/UGen/Buffer/osc.help.lhs
+++ b/Help/UGen/Buffer/osc.help.lhs
@@ -4,8 +4,9 @@
 > import Sound.SC3
 
 Allocate and generate wavetable buffer
-> withSC3 (\fd -> do {_ <- async fd (b_alloc 10 512 1)
->                    ;send fd (b_gen 10 "sine1" [1+2+4,1,1/2,1/3,1/4,1/5])})
+> withSC3 (do {_ <- async (b_alloc 10 512 1)
+>             ;let f = [Normalise,Wavetable,Clear]
+>              in send (b_gen_sine1 10 f [1,1/2,1/3,1/4,1/5])})
 
 Fixed frequency wavetable oscillator
 > audition (out 0 (osc AR 10 220 0 * 0.1))
@@ -26,4 +27,11 @@
 > audition (out 0 (osc AR 10 220 0 * 0.1))
 
 Change the wavetable while its playing
-> withSC3 (\fd -> send fd (b_gen 10 "sine1" [1+2+4,1,0.6,1/4]))
+> let f = [Normalise,Wavetable,Clear]
+> in withSC3 (send (b_gen_sine1 10 f [1,0.6,1/4]))
+
+Send directly calculated wavetable
+> import Sound.SC3.Lang.Collection {- hsc3-lang -}
+> import Sound.SC3.Lang.Math.Window
+> let t = to_wavetable (triangular_table 512)
+> withSC3 (send (b_setn1 10 0 t))
diff --git a/Help/UGen/Buffer/playBuf.help.lhs b/Help/UGen/Buffer/playBuf.help.lhs
--- a/Help/UGen/Buffer/playBuf.help.lhs
+++ b/Help/UGen/Buffer/playBuf.help.lhs
@@ -3,9 +3,9 @@
 
 > import Sound.SC3
 
-Load sound file to buffer zero (required for examples)
+Load sound file to buffer zero (single channel file required for examples)
 > let fn = "/home/rohan/data/audio/pf-c5.aif"
-> in withSC3 (\fd -> async fd (b_allocRead 0 fn 0 0))
+> in withSC3 (async (b_allocRead 0 fn 0 0))
 
 Play once only.
 > let s = bufRateScale KR 0
@@ -37,4 +37,4 @@
 > in audition (out 0 (playBuf 1 AR 0 s 1 0 Loop DoNothing))
 
 Release buffer.
-> withSC3 (\fd -> send fd (b_free 0))
+> withSC3 (send (b_free 0))
diff --git a/Help/UGen/Buffer/recordBuf.help.lhs b/Help/UGen/Buffer/recordBuf.help.lhs
--- a/Help/UGen/Buffer/recordBuf.help.lhs
+++ b/Help/UGen/Buffer/recordBuf.help.lhs
@@ -7,7 +7,7 @@
 > import Sound.SC3
 
 Allocate a buffer (assume SR of 48k)
-> withSC3 (\fd -> async fd (b_alloc 0 (48000 * 4) 1))
+> withSC3 (async (b_alloc 0 (48000 * 4) 1))
 
 Record for four seconds (until end of buffer)
 > let o = formant AR (xLine KR 400 1000 4 DoNothing) 2000 800 * 0.125
diff --git a/Help/UGen/Buffer/vOsc.help.lhs b/Help/UGen/Buffer/vOsc.help.lhs
--- a/Help/UGen/Buffer/vOsc.help.lhs
+++ b/Help/UGen/Buffer/vOsc.help.lhs
@@ -5,24 +5,23 @@
 
 Allocate and fill tables 0 to 7.
 > let {square a = a * a
+>     ;bf = [Normalise,Wavetable,Clear]
 >     ;harm i = let {n = square (i + 1)
 >                   ;f j = square ((n - j) / n)}
 >                in map f [0 .. n - 1]
->     ;setup fd i = do {i' <- return (fromIntegral i)
->                      ;_ <- async fd (b_alloc i 1024 1)
->                      ;send fd (b_gen i "sine1" (1 + 2 + 4 : harm i'))}}
-> in withSC3 (\fd -> mapM_ (setup fd) [0 .. 7])
+>     ;setup i = do {i' <- return (fromIntegral i)
+>                   ;_ <- async (b_alloc i 1024 1)
+>                   ;send (b_gen_sine1 i bf (harm i'))}}
+> in withSC3 (mapM_ setup [0 .. 7])
 
 Oscillator at buffers 0 through 7, mouse selects buffer.
-> let x = mouseX' KR 0 7 Linear 0.1
+> let x = mouseX KR 0 7 Linear 0.1
 > in audition (out 0 (vOsc AR x (mce [120, 121]) 0 * 0.3))
 
-> import Control.Monad
-> import System.Random
+> import Sound.SC3.Lang.Random.IO
 
 Reallocate buffers while oscillator is running.
-> let {rrand l r = getStdRandom (randomR (l,r))
->     ;rrandl n l r = replicateM n (rrand l r)
->     ;resetTable fd i = do {h <- rrandl 12 0 1
->                           ;send fd (b_gen i "sine1" (1 + 2 + 4 : h))}}
-> in withSC3 (\fd -> mapM_ (resetTable fd) [0 .. 7])
+> let {bf = [Normalise,Wavetable,Clear]
+>     ;resetTable i = do {h <- nrrand 12 0 1
+>                        ;send (b_gen_sine1 i bf h)}}
+> in withSC3 (mapM_ resetTable [0 .. 7])
diff --git a/Help/UGen/Buffer/vOsc3.help.lhs b/Help/UGen/Buffer/vOsc3.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/Buffer/vOsc3.help.lhs
@@ -0,0 +1,21 @@
+> Sound.SC3.UGen.Help.viewSC3Help "VOsc3"
+> Sound.SC3.UGen.DB.ugenSummary "VOsc3"
+
+> import Sound.SC3
+
+allocate and fill tables 0 to 7.
+> let {square a = a * a
+>     ;bf = [Normalise,Wavetable,Clear]
+>     ;harm i = let {n = square (i + 1)
+>                   ;f j = square ((n - j) / n)}
+>                in map f [0 .. n - 1]
+>     ;setup i = do {i' <- return (fromIntegral i)
+>                   ;_ <- async (b_alloc i 1024 1)
+>                   ;send (b_gen_sine1 i bf (harm i'))}}
+> in withSC3 (mapM_ setup [0 .. 7])
+
+oscillator at buffers 0 through 7, mouse selects buffer.
+> let {x = mouseX KR 0 7 Linear 0.1
+>     ;o1 = vOsc3 AR x 120 121 129
+>     ;o2 = vOsc3 AR x 119 123 127}
+> in audition (out 0 (mce2 o1 o2 * 0.3))
diff --git a/Help/UGen/Chaos/cuspL.help.lhs b/Help/UGen/Chaos/cuspL.help.lhs
--- a/Help/UGen/Chaos/cuspL.help.lhs
+++ b/Help/UGen/Chaos/cuspL.help.lhs
@@ -4,19 +4,16 @@
 > import Sound.SC3
 
 Vary frequency
-
-> let x = mouseX' KR 20 sampleRate Linear 0.1
+> let x = mouseX KR 20 sampleRate Linear 0.1
 > in audition (out 0 (cuspL AR x 1.0 1.99 0 * 0.3))
 
 Mouse-controlled parameters.
-
-> let {x = mouseX' KR 0.9 1.1 Linear 0.1
->     ;y = mouseY' KR 1.8 2.0 Linear 0.1}
+> let {x = mouseX KR 0.9 1.1 Linear 0.1
+>     ;y = mouseY KR 1.8 2.0 Linear 0.1}
 > in audition (out 0 (cuspL AR (sampleRate / 4) x y 0 * 0.3))
 
 As frequency control.
-
-> let {x = mouseX' KR 0.9 1.1 Linear 0.1
->     ;y = mouseY' KR 1.8 2.0 Linear 0.1
+> let {x = mouseX KR 0.9 1.1 Linear 0.1
+>     ;y = mouseY KR 1.8 2.0 Linear 0.1
 >     ;n = cuspL AR 40 x y 0 * 0.3}
 > in audition (out 0 (sinOsc AR (n * 800 + 900) 0 * 0.4))
diff --git a/Help/UGen/Chaos/fbSineC.help.lhs b/Help/UGen/Chaos/fbSineC.help.lhs
--- a/Help/UGen/Chaos/fbSineC.help.lhs
+++ b/Help/UGen/Chaos/fbSineC.help.lhs
@@ -4,26 +4,22 @@
 > import Sound.SC3.ID
 
 SC3 default values.
-
 > let o = fbSineC AR (sampleRate / 4) 1 0.1 1.1 0.5 0.1 0.1 * 0.2
 > in audition (out 0 o)
 
 Increase feedback
-
 > let {fb = line KR 0.01 4 10 DoNothing
 >     ;o = fbSineC AR sampleRate 1 fb 1.1 0.5 0.1 0.1 * 0.2}
 > in audition (out 0 o)
 
 Increase phase multiplier
-
 > let {a = line KR 1 2 10 DoNothing
 >     ;o = fbSineC AR sampleRate 1 0 a 0.5 0.1 0.1 * 0.2}
 > in audition (out 0 o)
 
 Randomly modulate parameters
-
 > let {madd a m = (+ a) . (* m)
->     ;x = mouseX' KR 1 12 Linear 0.1
+>     ;x = mouseX KR 1 12 Linear 0.1
 >     ;n e = lfNoise2 e KR x
 >     ;n0 = madd 1e4 1e4 (n 'a')
 >     ;n1 = madd 33 32 (n 'b')
diff --git a/Help/UGen/Chaos/gbmanL.help.lhs b/Help/UGen/Chaos/gbmanL.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/Chaos/gbmanL.help.lhs
@@ -0,0 +1,1 @@
+See GbmanN
diff --git a/Help/UGen/Chaos/gbmanN.help.lhs b/Help/UGen/Chaos/gbmanN.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/Chaos/gbmanN.help.lhs
@@ -0,0 +1,20 @@
+> Sound.SC3.UGen.Help.viewSC3Help "GbmanN"
+> Sound.SC3.UGen.DB.ugenSummary "GbmanN"
+
+> import Sound.SC3
+
+default initial params
+> let x = mouseX KR 20 sampleRate Linear 0.2
+> in audition (out 0 (gbmanN AR x 1.2 2.1 * 0.1))
+
+change initial params
+> let x = mouseX KR 20 sampleRate Linear 0.2
+> in audition (out 0 (gbmanN AR x (-0.7) (-2.7) * 0.1))
+
+wait for it...
+> let x = mouseX KR 20 sampleRate Linear 0.2
+> in audition (out 0 (gbmanN AR x 1.2 2.0002 * 0.1))
+
+as a frequency control
+> let f = gbmanN AR 40 1.2 2.1 * 400 + 500
+> in audition (out 0 (sinOsc AR f 0 * 0.4))
diff --git a/Help/UGen/Chaos/henonN.help.lhs b/Help/UGen/Chaos/henonN.help.lhs
--- a/Help/UGen/Chaos/henonN.help.lhs
+++ b/Help/UGen/Chaos/henonN.help.lhs
@@ -4,26 +4,22 @@
 > import Sound.SC3.ID
 
 With SC3 default initial parameters.
-
-> let x = mouseX' KR 20 sampleRate Linear 0.1
+> let x = mouseX KR 20 sampleRate Linear 0.1
 > in audition (out 0 (henonN AR x 1.4 0.3 0 0 * 0.1))
 
 With mouse-control of parameters.
-
-> let {x = mouseX' KR 1 1.4 Linear 0.1
->     ;y = mouseY' KR 0 0.3 Linear 0.1}
+> let {x = mouseX KR 1 1.4 Linear 0.1
+>     ;y = mouseY KR 0 0.3 Linear 0.1}
 > in audition (out 0 (henonN AR (sampleRate / 4) x y 0 0 * 0.1))
 
 With randomly modulated parameters.
-
 > let {n0 = lfNoise2 'a' KR 1 * 0.20 + 1.20
 >     ;n1 = lfNoise2 'a' KR 1 * 0.15 + 0.15}
 > in audition (out 0 (henonN AR (sampleRate / 8) n0 n1 0 0 * 0.1))
 
 As a frequency control.
-
-> let {x = mouseX' KR 1 1.4 Linear 0.1
->     ;y = mouseY' KR 0 0.3 Linear 0.1
+> let {x = mouseX KR 1 1.4 Linear 0.1
+>     ;y = mouseY KR 0 0.3 Linear 0.1
 >     ;f0 = 40
 >     ;f = henonN AR f0 x y 0 0 * 800 + 900}
 > in audition (out 0 (sinOsc AR f 0 * 0.4))
diff --git a/Help/UGen/Chaos/latoocarfianC.help.lhs b/Help/UGen/Chaos/latoocarfianC.help.lhs
--- a/Help/UGen/Chaos/latoocarfianC.help.lhs
+++ b/Help/UGen/Chaos/latoocarfianC.help.lhs
@@ -4,12 +4,10 @@
 > import Sound.SC3.ID
 
 SC3 default initial parameters.
-
-> let x = mouseX' KR 20 sampleRate Linear 0.1
+> let x = mouseX KR 20 sampleRate Linear 0.1
 > in audition (out 0 (latoocarfianC AR x 1 3 0.5 0.5 0.5 0.5 * 0.2))
 
 Randomly modulate all parameters.
-
 > let {[n0,n1,n2,n3] = map (\e -> lfNoise2 e KR 5) "abcd"
 >     ;f = sampleRate / 4
 >     ;a = n0 * 1.5 + 1.5
diff --git a/Help/UGen/Chaos/linCongC.help.lhs b/Help/UGen/Chaos/linCongC.help.lhs
--- a/Help/UGen/Chaos/linCongC.help.lhs
+++ b/Help/UGen/Chaos/linCongC.help.lhs
@@ -4,12 +4,10 @@
 > import Sound.SC3.ID
 
 Default SC3 initial parameters.
-
-> let x = mouseX' KR 20 sampleRate Linear 0.1
+> let x = mouseX KR 20 sampleRate Linear 0.1
 > in audition (out 0 (linCongC AR x 1.1 0.13 1 0 * 0.2))
 
 Randomly modulate parameters.
-
 > let {fr = [1,0.1,0.1,0.1]
 >     ;[n0,n1,n2,m] = map (\(i,j) -> lfNoise2 i KR j) (zip "abde" fr)
 >     ;f = n0 * 1e4 + 1e4
diff --git a/Help/UGen/Chaos/logistic.help.lhs b/Help/UGen/Chaos/logistic.help.lhs
--- a/Help/UGen/Chaos/logistic.help.lhs
+++ b/Help/UGen/Chaos/logistic.help.lhs
@@ -10,6 +10,6 @@
 > audition (out 0 (logistic AR (line KR 3.55 3.6 5 DoNothing) 1000 0.01))
 
 Mouse control
-> let {x = mouseX' KR 3 3.99 Linear 0.1
->     ;y = mouseY' KR 10 10000 Exponential 0.1}
+> let {x = mouseX KR 3 3.99 Linear 0.1
+>     ;y = mouseY KR 10 10000 Exponential 0.1}
 > in audition (out 0 (logistic AR x y 0.25 * 0.5))
diff --git a/Help/UGen/Chaos/lorenzL.help.lhs b/Help/UGen/Chaos/lorenzL.help.lhs
--- a/Help/UGen/Chaos/lorenzL.help.lhs
+++ b/Help/UGen/Chaos/lorenzL.help.lhs
@@ -4,7 +4,7 @@
 > import Sound.SC3.ID
 
 Vary frequency
-> let x = mouseX' KR 20 sampleRate Linear 0.1
+> let x = mouseX KR 20 sampleRate Linear 0.1
 > in audition (out 0 (lorenzL AR x 10 27 2.667 0.05 0.1 0 0 * 0.3))
 
 Randomly modulate params
@@ -17,6 +17,6 @@
 > in audition (out 0 o)
 
 As frequency control
-> let {x = mouseX' KR 1 200 Linear 0.1
+> let {x = mouseX KR 1 200 Linear 0.1
 >     ;n = lorenzL AR x 10 28 2.667 0.05 0.1 0 0}
 > in audition (out 0 (sinOsc AR (lag n 0.003 * 800 + 900) 0 * 0.4))
diff --git a/Help/UGen/Chaos/quadN.help.lhs b/Help/UGen/Chaos/quadN.help.lhs
--- a/Help/UGen/Chaos/quadN.help.lhs
+++ b/Help/UGen/Chaos/quadN.help.lhs
@@ -5,9 +5,9 @@
 
 > audition (out 0 (quadC AR 4000 1 (-1) (-0.75) 0 * 0.2))
 
-> let x = mouseX' KR 3.5441 4 Linear 0.1
+> let x = mouseX KR 3.5441 4 Linear 0.1
 > in audition (out 0 (quadC AR 4000 (negate x) x 0 0.1 * 0.4))
 
-> let {x = mouseX' KR 3.5441 4 Linear 0.1
+> let {x = mouseX KR 3.5441 4 Linear 0.1
 >     ;f = quadC AR 4 (negate x) x 0 0.1 * 800 + 900}
 > in audition (out 0 (sinOsc AR f 0 * 0.4))
diff --git a/Help/UGen/Demand/dbrown.help.lhs b/Help/UGen/Demand/dbrown.help.lhs
--- a/Help/UGen/Demand/dbrown.help.lhs
+++ b/Help/UGen/Demand/dbrown.help.lhs
@@ -4,7 +4,7 @@
 > import Sound.SC3.ID
 
 > let {n = dbrown 'a' dinf 0 15 1
->     ;x = mouseX' KR 1 40 Exponential 0.1
+>     ;x = mouseX KR 1 40 Exponential 0.1
 >     ;t = impulse KR x 0
 >     ;f = demand t 0 n * 30 + 340}
 > in audition (out 0 (sinOsc AR f 0 * 0.1))
diff --git a/Help/UGen/Demand/dbufrd.help.lhs b/Help/UGen/Demand/dbufrd.help.lhs
--- a/Help/UGen/Demand/dbufrd.help.lhs
+++ b/Help/UGen/Demand/dbufrd.help.lhs
@@ -6,7 +6,7 @@
 
 setup pattern at buffer 10
 > let n = randomRs (200.0,500.0) (mkStdGen 0)
-> in withSC3 (\fd -> async fd (b_alloc_setn1 10 0 (take 24 n)))
+> in withSC3 (async (b_alloc_setn1 10 0 (take 24 n)))
 
 pattern as frequency input
 > let {s = dseq 'a' 3 (mce [0,3,5,0,3,7,0,5,9])
@@ -19,7 +19,7 @@
 setup time pattern
 > let {i = randomRs (0,2) (mkStdGen 0)
 >     ;n = map ([1,0.5,0.25] !!) i}
-> in withSC3 (\fd -> async fd (b_alloc_setn1 11 0 (take 24 n)))
+> in withSC3 (async (b_alloc_setn1 11 0 (take 24 n)))
 
 requires buffers 10 and 11 as allocated above
 > let {s = dseq 'a' 3 (mce [0,3,5,0,3,7,0,5,9])
@@ -32,5 +32,4 @@
 > in audition (out 0 (sinOsc AR f 0 * 0.1))
 
 free buffers
-> withSC3 (\fd -> do {async fd (b_free 10)
->                    ;async fd (b_free 11)})
+> withSC3 (async (b_free 10) >> async (b_free 11))
diff --git a/Help/UGen/Demand/dbufwr.help.lhs b/Help/UGen/Demand/dbufwr.help.lhs
--- a/Help/UGen/Demand/dbufwr.help.lhs
+++ b/Help/UGen/Demand/dbufwr.help.lhs
@@ -18,6 +18,6 @@
 >         ;f = lag (demand (impulse KR 16 0) 0 r) 0.01
 >         ;o = sinOsc AR (f * mce2 1 1.01) 0 * 0.1
 >         ;g = mrg [d, out 0 o]
->         ;run fd = do {async fd (b_alloc_setn1 0 0 (replicate 24 210))
->                      ;play fd g}}
+>         ;run = do {_ <- async (b_alloc_setn1 0 0 (replicate 24 210))
+>                   ;play g}}
 >     in withSC3 run}
diff --git a/Help/UGen/Demand/demand.help.lhs b/Help/UGen/Demand/demand.help.lhs
--- a/Help/UGen/Demand/demand.help.lhs
+++ b/Help/UGen/Demand/demand.help.lhs
@@ -2,7 +2,7 @@
 > Sound.SC3.UGen.DB.ugenSummary "Demand"
 
 > import Sound.SC3.ID
-> import qualified Sound.SC3.Monadic as M
+> import qualified Sound.SC3.Monad as M
 
 > do {r <- M.dust KR 1
 >    ;s <- M.dgeom dinf (midiCPS 72) (midiRatio 1)
@@ -17,3 +17,15 @@
 >     ;f = demand t 0 s
 >     ;o = sinOsc AR (mce [f,f + 0.7]) 0}
 > in audition (out 0 (cubed (cubed o) * 0.1))
+
+audio rate (poll output is equal for x1 and x2)
+> let {i = lfNoise2 'a' AR 8000
+>     ;d = dseq 'a' dinf (mce [i])
+>     ;x = mouseX KR 1 3000 Exponential 0.2
+>     ;t = impulse AR x 0
+>     ;x1 = demand t 0 d
+>     ;x2 = latch i t
+>     ;s = mce2 x1 x2
+>     ;p = poll t s (mce2 (label "x1") (label "x2")) 0
+>     ;o = sinOsc AR (s * 300 + 400) 0 * 0.1}
+> in audition (mrg2 (out 0 o) p)
diff --git a/Help/UGen/Demand/demandEnvGen.help.lhs b/Help/UGen/Demand/demandEnvGen.help.lhs
--- a/Help/UGen/Demand/demandEnvGen.help.lhs
+++ b/Help/UGen/Demand/demandEnvGen.help.lhs
@@ -2,22 +2,67 @@
 > Sound.SC3.UGen.DB.ugenSummary "DemandEnvGen"
 
 > import Sound.SC3.ID
-> import qualified Sound.SC3.Monadic as M
 
 Frequency ramp, exponential curve.
 > let {l = dseq 'a' dinf (mce2 440 9600)
->     ;y = mouseY' KR 0.01 3 Exponential 0.1
->     ;f = demandEnvGen AR l y 2 0 1 1 1 0 1 DoNothing}
+>     ;y = mouseY KR 0.01 3 Exponential 0.2
+>     ;s = env_curve_shape EnvExp
+>     ;f = demandEnvGen AR l y s 0 1 1 1 0 1 DoNothing}
 > in audition (out 0 (sinOsc AR f 0 * 0.1))
 
 Frequency envelope with random times.
-> do {l <- M.dseq dinf (mce [204, 400, 201, 502, 300, 200])
->    ;t <- M.drand dinf (mce [1.01, 0.2, 0.1, 2.0])
->    ;let {y = mouseY' KR 0.01 3 Exponential 0.1
->         ;f = demandEnvGen AR l (t * y) 7 0 1 1 1 0 1 DoNothing}
->     in audition (out 0 (sinOsc AR (f * mce2 1 1.01) 0 * 0.1))}
+> let {l = dseq 'a' dinf (mce [204,400,201,502,300,200])
+>     ;t = drand 'a' dinf (mce [1.01,0.2,0.1,2.0])
+>     ;y = mouseY KR 0.01 3 Exponential 0.2
+>     ;s = env_curve_shape EnvCub
+>     ;f = demandEnvGen AR l (t * y) s 0 1 1 1 0 1 DoNothing}
+> in audition (out 0 (sinOsc AR (f * mce2 1 1.01) 0 * 0.1))
 
+frequency modulation
+> let {n = dwhite 'a' dinf 200 1000
+>     ;x = mouseX KR (-0.01) (-4) Linear 0.2
+>     ;y = mouseY KR 1 3000 Exponential 0.2
+>     ;s = env_curve_shape (EnvNum undefined)
+>     ;f = demandEnvGen AR n (sampleDur * y) s x 1 1 1 0 1 DoNothing
+>     ;o = sinOsc AR f 0 * 0.1}
+> in audition (out 0 o)
+
 short sequence with doneAction, linear
-> let {s = dseq 'a' 1 (mce [1300,500,800,300,400])
->     ;f = demandEnvGen KR s 2 1 0 1 1 1 0 1 RemoveSynth}
+> let {l = dseq 'a' 1 (mce [1300,500,800,300,400])
+>     ;s = env_curve_shape EnvLin
+>     ;f = demandEnvGen KR l 2 s 0 1 1 1 0 1 RemoveSynth}
+> in audition (out 0 (sinOsc AR (f * mce2 1 1.01) 0 * 0.1))
+
+gate, mouse x on right side of screen toggles gate
+> let {n = roundTo (dwhite 'a' dinf 300 1000) 100
+>     ;x = mouseX KR 0 1 Linear 0.2
+>     ;g = x >* 0.5
+>     ;f = demandEnvGen AR n 0.1 5 0.3 g 1 1 0 1 DoNothing
+>     ;o = sinOsc AR (f * mce2 1 1.21) 0 * 0.1}
+> in audition (out 0 o)
+
+gate
+mouse x on right side of screen toggles sample and hold
+mouse button does hard reset
+> let {l = dseq 'a' 2 (mce [dseries 'a' 5 400 200,500,800,530,4000,900])
+>     ;x = mouseX KR 0 1 Linear 0.2
+>     ;g = (x >* 0.5) - 0.1
+>     ;b = mouseButton KR 0 1 0.2
+>     ;r = (b >* 0.5) * 2
+>     ;s = env_curve_shape EnvSin
+>     ;f = demandEnvGen KR l 0.1 s 0 g r 1 0 1 DoNothing
+>     ;o = sinOsc AR (f * mce2 1 1.001) 0 * 0.1}
+> in audition (out 0 o)
+
+initialise coordinate buffer
+layout is (initial-level,duration,level,..,loop-duration)
+> withSC3 (async (b_alloc_setn1 0 0 [0,0.5,0.1,0.5,1,0.01]))
+
+> let {l_i = dseries 'a' dinf 0 2
+>     ;d_i = dseries 'b' dinf 1 2
+>     ;l = dbufrd 'a' 0 l_i Loop
+>     ;d = dbufrd 'b' 0 d_i Loop
+>     ;s = env_curve_shape EnvLin
+>     ;e = demandEnvGen KR l d s 0 1 1 1 0 5 RemoveSynth
+>     ;f = midiCPS (60 + (e * 12))}
 > in audition (out 0 (sinOsc AR (f * mce2 1 1.01) 0 * 0.1))
diff --git a/Help/UGen/Demand/dgeom.help.lhs b/Help/UGen/Demand/dgeom.help.lhs
--- a/Help/UGen/Demand/dgeom.help.lhs
+++ b/Help/UGen/Demand/dgeom.help.lhs
@@ -4,7 +4,7 @@
 > import Sound.SC3.ID
 
 > let {n = dgeom 'a' 15 1 1.2
->     ;x = mouseX' KR 1 40 Exponential 0.1
+>     ;x = mouseX KR 1 40 Exponential 0.1
 >     ;t = impulse KR x 0
 >     ;f = demand t 0 n * 30 + 340}
 > in audition (out 0 (sinOsc AR f 0 * 0.1))
diff --git a/Help/UGen/Demand/drand.help.lhs b/Help/UGen/Demand/drand.help.lhs
--- a/Help/UGen/Demand/drand.help.lhs
+++ b/Help/UGen/Demand/drand.help.lhs
@@ -4,7 +4,7 @@
 > import Sound.SC3.ID
 
 > let {n = drand 'a' dinf (mce [1, 3, 2, 7, 8])
->     ;x = mouseX' KR 1 400 Exponential 0.1
+>     ;x = mouseX KR 1 400 Exponential 0.1
 >     ;t = impulse KR x 0
 >     ;f = demand t 0 n * 30 + 340}
 > in audition (out 0 (sinOsc AR f 0 * 0.1))
diff --git a/Help/UGen/Demand/dseq.help.lhs b/Help/UGen/Demand/dseq.help.lhs
--- a/Help/UGen/Demand/dseq.help.lhs
+++ b/Help/UGen/Demand/dseq.help.lhs
@@ -1,17 +1,19 @@
 > Sound.SC3.UGen.Help.viewSC3Help "Dseq"
 > Sound.SC3.UGen.DB.ugenSummary "Dseq"
 
+# sclang re-orders inputs
+
 > import Sound.SC3.ID
 
 > let {n = dseq 'a' 3 (mce [1, 3, 2, 7, 8])
->     ;x = mouseX' KR 1 40 Exponential 0.1
+>     ;x = mouseX KR 1 40 Exponential 0.1
 >     ;t = impulse KR x 0
 >     ;f = demand t 0 n * 30 + 340}
 > in audition (out 0 (sinOsc AR f 0 * 0.1))
 
 At audio rate.
 > let {n = dseq 'a' dinf (mce [1,3,2,7,8,32,16,18,12,24])
->     ;x = mouseX' KR 1 10000 Exponential 0.1
+>     ;x = mouseX KR 1 10000 Exponential 0.1
 >     ;t = impulse AR x 0
 >     ;f = demand t 0 n * 30 + 340}
 > in audition (out 0 (sinOsc AR f 0 * 0.1))
diff --git a/Help/UGen/Demand/dser.help.lhs b/Help/UGen/Demand/dser.help.lhs
--- a/Help/UGen/Demand/dser.help.lhs
+++ b/Help/UGen/Demand/dser.help.lhs
@@ -4,7 +4,7 @@
 > import Sound.SC3.ID
 
 > let {a = dser 'a' 7 (mce [1, 3, 2, 7, 8])
->     ;x = mouseX' KR 1 40 Exponential 0.1
+>     ;x = mouseX KR 1 40 Exponential 0.1
 >     ;t = impulse KR x 0
 >     ;f = demand t 0 a * 30 + 340}
 > in audition (out 0 (sinOsc AR f 0 * 0.1))
diff --git a/Help/UGen/Demand/dseries.help.lhs b/Help/UGen/Demand/dseries.help.lhs
--- a/Help/UGen/Demand/dseries.help.lhs
+++ b/Help/UGen/Demand/dseries.help.lhs
@@ -4,7 +4,7 @@
 > import Sound.SC3.ID
 
 > let {n = dseries 'a' 15 0 1
->     ;x = mouseX' KR 1 40 Exponential 0.1
+>     ;x = mouseX KR 1 40 Exponential 0.1
 >     ;t = impulse KR x 0
 >     ;f = demand t 0 n * 30 + 340}
 > in audition (out 0 (sinOsc AR f 0 * 0.1))
diff --git a/Help/UGen/Demand/dshuf.help.lhs b/Help/UGen/Demand/dshuf.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/Demand/dshuf.help.lhs
@@ -0,0 +1,20 @@
+> Sound.SC3.UGen.Help.viewSC3Help "Dshuf"
+> Sound.SC3.UGen.DB.ugenSummary "Dshuf"
+
+# sclang re-orders inputs
+
+> import Sound.SC3.ID
+
+> let {a = dseq 'a' dinf (dshuf 'a' 3 (mce [1,3,2,7,8.5]))
+>     ;x = mouseX KR 1 40 Exponential 0.1
+>     ;t = impulse KR x 0
+>     ;f = demand t 0 a * 30 + 340}
+> in audition (out 0 (sinOsc AR f 0 * 0.1))
+
+> import Sound.SC3.UGen.External.RDU
+
+> let {a = dseq 'a' dinf (dshuf 'a' 5 (randN 81 'a' 0 10))
+>     ;x = mouseX KR 1 10000 Exponential 0.1
+>     ;t = impulse AR x 0
+>     ;f = demand t 0 a * 30 + 340}
+> in audition (out 0 (sinOsc AR f 0 * 0.1))
diff --git a/Help/UGen/Demand/dstutter.help.lhs b/Help/UGen/Demand/dstutter.help.lhs
--- a/Help/UGen/Demand/dstutter.help.lhs
+++ b/Help/UGen/Demand/dstutter.help.lhs
@@ -6,6 +6,18 @@
 > let {inp = dseq 'a' dinf (mce [1,2,3])
 >     ;nse = diwhite 'a' dinf 2 8
 >     ;rep = dstutter 'a' nse inp
->     ;trg = impulse KR (mouseX' KR 1 40 Exponential 0.2) 0
+>     ;trg = impulse KR (mouseX KR 1 40 Exponential 0.2) 0
 >     ;frq = demand trg 0 rep * 30 + 340}
 > in audition (out 0 (sinOsc AR frq 0 * 0.1))
+
+https://www.listarc.bham.ac.uk/lists/sc-users/msg14775.html
+> let {a z = let {xr = dxrand z dinf (mce [0.1,0.2,0.3,0.4,0.5])
+>                ;lf = dstutter z 2 xr
+>                ;du = duty AR lf 0 DoNothing lf
+>                ;tr = abs (hpz1 du) >* 0
+>                ;ph = sweep tr (1/du)}
+>            in linExp ph 0 1 (rand z 50 100) (rand z 500 2000)
+>     ;f = mce (map a ['a'..'h'])
+>     ;[s0,s1] = mceChannels (splay (sinOsc AR f 0) 1 1 0 True)
+>     ;o = limiter (rotate2 s0 s1 (lfSaw KR 0.1 0)) 1 1e-2}
+> in audition (out 0 (o * 0.25))
diff --git a/Help/UGen/Demand/dswitch1.help.lhs b/Help/UGen/Demand/dswitch1.help.lhs
--- a/Help/UGen/Demand/dswitch1.help.lhs
+++ b/Help/UGen/Demand/dswitch1.help.lhs
@@ -3,8 +3,8 @@
 
 > import Sound.SC3.ID
 
-> let {x = mouseX' KR 0 4 Linear 0.1
->     ;y = mouseY' KR 1 15 Linear 0.1
+> let {x = mouseX KR 0 4 Linear 0.1
+>     ;y = mouseY KR 1 15 Linear 0.1
 >     ;t = impulse KR 3 0
 >     ;w = dwhite 'a' dinf 20 23
 >     ;n = dswitch1 'a' x (mce [1, 3, y, 2, w])
diff --git a/Help/UGen/Demand/duty.help.lhs b/Help/UGen/Demand/duty.help.lhs
--- a/Help/UGen/Demand/duty.help.lhs
+++ b/Help/UGen/Demand/duty.help.lhs
@@ -1,6 +1,8 @@
 > Sound.SC3.UGen.Help.viewSC3Help "Duty"
 > Sound.SC3.UGen.DB.ugenSummary "Duty"
 
+# sc3 reorders inputs
+
 > import Sound.SC3
 > import qualified Sound.SC3.Monadic as M
 
@@ -11,6 +13,6 @@
 
 Using control rate signal, mouseX, to determine duration.
 > let {n = dseq 'a' dinf (mce [204,400,201,502,300,200])
->     ;x = mouseX' KR 0.001 2 Linear 0.1
+>     ;x = mouseX KR 0.001 2 Linear 0.1
 >     ;f = duty KR x 0 RemoveSynth n}
 > in audition (out 0 (sinOsc AR (f * mce2 1 1.01) 0 * 0.1))
diff --git a/Help/UGen/Demand/dwhite.help.lhs b/Help/UGen/Demand/dwhite.help.lhs
--- a/Help/UGen/Demand/dwhite.help.lhs
+++ b/Help/UGen/Demand/dwhite.help.lhs
@@ -4,7 +4,7 @@
 > import Sound.SC3.ID
 
 > let {n = dwhite 'a' dinf 0 15
->     ;x = mouseX' KR 1 40 Exponential 0.1
+>     ;x = mouseX KR 1 40 Exponential 0.1
 >     ;t = impulse KR x 0
 >     ;f = demand t 0 n * 30 + 340}
 > in audition (out 0 (sinOsc AR f 0 * 0.1))
diff --git a/Help/UGen/Demand/dwrand.help.lhs b/Help/UGen/Demand/dwrand.help.lhs
--- a/Help/UGen/Demand/dwrand.help.lhs
+++ b/Help/UGen/Demand/dwrand.help.lhs
@@ -4,7 +4,7 @@
 > import Sound.SC3.ID
 
 > let {n = dwrand 'a' dinf (mce [1,3,2,7,8]) (mce [0.4,0.4,0.05,0.05,0.1])
->     ;x = mouseX' KR 1 400 Exponential 0.1
+>     ;x = mouseX KR 1 400 Exponential 0.1
 >     ;t = impulse KR x 0
 >     ;f = demand t 0 n * 30 + 340}
 > in audition (out 0 (sinOsc AR f 0 * 0.1))
diff --git a/Help/UGen/Demand/dxrand.help.lhs b/Help/UGen/Demand/dxrand.help.lhs
--- a/Help/UGen/Demand/dxrand.help.lhs
+++ b/Help/UGen/Demand/dxrand.help.lhs
@@ -3,8 +3,8 @@
 
 > import Sound.SC3.ID
 
-Select to draw or not...
-> let drw = Sound.SC3.UGen.Dot.draw
+Select to draw graphs, or not...
+> let drw = Sound.SC3.UGen.Dot.draw :: UGen -> IO ()
 > let drw = const (return ()) :: UGen -> IO ()
 
 > let {i = mce [0.2,0.4,dseq 'a' 2 (mce [0.1,0.1])]
@@ -26,7 +26,7 @@
 > in audition (out 0 t) >> drw t
 
 > let {n = dxrand 'a' dinf (mce [1, 3, 2, 7, 8])
->     ;x = mouseX' KR 1 400 Exponential 0.1
+>     ;x = mouseX KR 1 400 Exponential 0.1
 >     ;t = impulse KR x 0
 >     ;f = demand t 0 n * 30 + 340}
-> in audition (out 0 (sinOsc AR f 0 * 0.1))
+> in audition (out 0 (sinOsc AR f 0 * 0.1)) >> drw f
diff --git a/Help/UGen/Demand/tDuty.help.lhs b/Help/UGen/Demand/tDuty.help.lhs
--- a/Help/UGen/Demand/tDuty.help.lhs
+++ b/Help/UGen/Demand/tDuty.help.lhs
@@ -15,7 +15,7 @@
 
 Mouse control.
 > let {d = dseq 'a' dinf (mce [0.1, 0.4, 0.01, 0.5, 1.0])
->     ;x = mouseX' KR 0.003 1 Exponential 0.1
+>     ;x = mouseX KR 0.003 1 Exponential 0.1
 >     ;s = ringz (tDuty AR x 0 DoNothing d 1) 1000 0.1 * 0.5}
 > in audition (out 0 s)
 
diff --git a/Help/UGen/DiskIO/diskIn.help.lhs b/Help/UGen/DiskIO/diskIn.help.lhs
--- a/Help/UGen/DiskIO/diskIn.help.lhs
+++ b/Help/UGen/DiskIO/diskIn.help.lhs
@@ -6,10 +6,10 @@
 > let {f = "/home/rohan/data/audio/pf-c5.snd"
 >     ;n = 1
 >     ;g = out 0 (diskIn n 0 Loop)}
-> in withSC3 (\fd -> do {async fd (b_alloc 0 8192 n)
->                       ;async fd (b_read 0 f 0 (-1) 0 True)
->                       ;play fd g})
+> in withSC3 (do {_ <- async (b_alloc 0 65536 n)
+>                ;_ <- async (b_read 0 f 0 (-1) 0 True)
+>                ;play g})
 
-> withSC3 (\fd -> do {reset fd
->                    ;async fd (b_close 0)
->                    ;async fd (b_free 0)})
+> withSC3 (do {reset
+>             ;_ <- async (b_close 0)
+>             ;async (b_free 0)})
diff --git a/Help/UGen/DiskIO/vDiskIn.help.lhs b/Help/UGen/DiskIO/vDiskIn.help.lhs
--- a/Help/UGen/DiskIO/vDiskIn.help.lhs
+++ b/Help/UGen/DiskIO/vDiskIn.help.lhs
@@ -6,11 +6,11 @@
 > let {f = "/home/rohan/data/audio/pf-c5.snd"
 >     ;n = 1
 >     ;g = out 0 (vDiskIn n 0 (sinOsc KR 0.25 0 * 0.25 + 1) Loop)}
-> in withSC3 (\fd -> do {_ <- async fd (b_alloc 0 8192 n)
->                       ;_ <- async fd (b_read 0 f 0 (-1) 0 True)
->                       ;play fd g })
+> in withSC3 (do {_ <- async (b_alloc 0 8192 n)
+>                ;_ <- async (b_read 0 f 0 (-1) 0 True)
+>                ;play g })
 
-> withSC3 (\fd -> do {reset fd
->                    ;_ <- async fd (b_close 0)
->                    ;_ <- async fd (b_free 0)
->                    ;return ()})
+> withSC3 (do {reset
+>             ;_ <- async (b_close 0)
+>             ;_ <- async (b_free 0)
+>             ;return ()})
diff --git a/Help/UGen/Envelope/detectSilence.help.lhs b/Help/UGen/Envelope/detectSilence.help.lhs
--- a/Help/UGen/Envelope/detectSilence.help.lhs
+++ b/Help/UGen/Envelope/detectSilence.help.lhs
@@ -3,6 +3,6 @@
 
 > import Sound.SC3
 
-> let {s = sinOsc AR 440 0 * mouseY' KR 0 0.4 Linear 0.1
+> let {s = sinOsc AR 440 0 * mouseY KR 0 0.4 Linear 0.1
 >     ;d = detectSilence s 0.1 0.2 RemoveSynth}
 > in audition (mrg [out 0 s,d])
diff --git a/Help/UGen/Envelope/done.help.lhs b/Help/UGen/Envelope/done.help.lhs
--- a/Help/UGen/Envelope/done.help.lhs
+++ b/Help/UGen/Envelope/done.help.lhs
@@ -3,7 +3,7 @@
 
 > import Sound.SC3
 
-> let {x = mouseX' KR (-1) 1 Linear 0.1
+> let {x = mouseX KR (-1) 1 Linear 0.1
 >     ;e = linen x 0.1 0.1 0.5 DoNothing
 >     ;o1 = sinOsc AR 880 0 * 0.1
 >     ;o2 = sinOsc AR 440 0 * e}
diff --git a/Help/UGen/Envelope/envADSR.help.lhs b/Help/UGen/Envelope/envADSR.help.lhs
--- a/Help/UGen/Envelope/envADSR.help.lhs
+++ b/Help/UGen/Envelope/envADSR.help.lhs
@@ -1,5 +1,7 @@
 > Sound.SC3.UGen.Help.viewSC3Help "Env.*adsr"
-> :t envADSR
+> :i Sound.SC3.ADSR
+> :t Sound.SC3.envADSR_r
+> :t Sound.SC3.envADSR
 
 > import Sound.SC3
 
@@ -8,8 +10,31 @@
 >     ;e = envGen KR g 0.1 0 1 DoNothing p}
 > in audition (out 0 (sinOsc AR 440 0 * e))
 
-> withSC3 (\fd -> send fd (n_set1 (-1) "gate" 0))
+> withSC3 (send (n_set1 (-1) "gate" 0))
+> withSC3 (send (n_set1 (-1) "gate" 1))
+> withSC3 (send (n_free [-1]))
 
-> withSC3 (\fd -> send fd (n_set1 (-1) "gate" 1))
+> import Sound.SC3.Plot
+> plotEnvelope [envADSR 0.75 0.75 0.5 0.75 1 (EnvNum (-4)) 0
+>              ,envADSR 0.02 0.2 0.25 1 1 (EnvNum (-4)) 0
+>              ,envADSR 0.001 0.2 0.25 1 1 (EnvNum (-4)) 0
+>              ,envADSR 0 2 1 0.1 0.5 EnvSin 0]
 
-> withSC3 (\fd -> send fd (n_free [-1]))
+There is a record variant:
+
+> let {g = control KR "gate" 1
+>     ;c = EnvNum (-4)
+>     ;r = ADSR {attackTime = 0.75
+>               ,decayTime = 0.75
+>               ,sustainLevel = 0.5
+>               ,releaseTime = 0.75
+>               ,peakLevel = 1
+>               ,curve = (c,c,c)
+>               ,bias = 0}
+>     ;p = envADSR_r r
+>     ;e = envGen KR g 0.1 0 1 DoNothing p}
+> in audition (out 0 (sinOsc AR 440 0 * e))
+
+> withSC3 (send (n_set1 (-1) "gate" 0))
+> withSC3 (send (n_set1 (-1) "gate" 1))
+> withSC3 (send (n_free [-1]))
diff --git a/Help/UGen/Envelope/envASR.help.lhs b/Help/UGen/Envelope/envASR.help.lhs
--- a/Help/UGen/Envelope/envASR.help.lhs
+++ b/Help/UGen/Envelope/envASR.help.lhs
@@ -1,5 +1,5 @@
 > Sound.SC3.UGen.Help.viewSC3Help "Env.*asr"
-> :t envASR
+> :t Sound.SC3.envASR
 
 > import Sound.SC3
 
@@ -8,4 +8,8 @@
 >     ;e = envGen KR g 0.1 0 1 RemoveSynth p}
 > in audition (out 0 (sinOsc AR 440 0 * e))
 
-> withSC3 (\fd -> send fd (n_set1 (-1) "gate" 0))
+> withSC3 (send (n_set1 (-1) "gate" 0))
+
+> import Sound.SC3.Plot
+> plotEnvelope [envASR 0.1 1 1 (EnvNum (-4))
+>              ,envASR 0.3 0.25 1 EnvSin]
diff --git a/Help/UGen/Envelope/envCoord.help.lhs b/Help/UGen/Envelope/envCoord.help.lhs
--- a/Help/UGen/Envelope/envCoord.help.lhs
+++ b/Help/UGen/Envelope/envCoord.help.lhs
@@ -1,11 +1,29 @@
-> :t envCoord
-
-Co-ordinate (break-point) envelope
-
 > import Sound.SC3
+> :t envCoord
 
+co-ordinate (break-point) envelope
 > let {c = EnvLin
 >     ;p = envCoord [(0,0),(0.5,0.1),(0.55,1),(1,0)] 9 0.1 c
 >     ;e = envGen KR 1 1 0 1 RemoveSynth p}
 > in audition (out 0 (sinOsc AR 440 0 * e))
+
+line segments, set target value & transition time and trigger
+> let {tr = tr_control "tr" 1
+>     ;st = control KR "st" 440
+>     ;en = control KR "en" 880
+>     ;tm = control KR "tm" 2
+>     ;p = envCoord [(0,st),(tm,en)] 1 1 EnvLin
+>     ;e = envGen KR tr 1 0 1 DoNothing p}
+> in audition (out 0 (sinOsc AR e 0 * 0.2))
+
+> withSC3 (send (n_set (-1) [("en",550),("tm",4),("tr",1)]))
+> withSC3 (send (n_set (-1) [("en",990),("tm",1),("tr",1)]))
+> withSC3 (send (n_set (-1) [("en",110),("tm",2),("tr",1)]))
+
+plotting
+> import Sound.SC3.Plot
+
+> let {c0 = [(0,0),(0.35,0.1),(0.55,1),(1,0)]
+>     ;c1 = [(0,0),(0.15,0.6),(0.35,0.2),(1,0)]}
+> in plotEnvelope [envCoord c0 9 0.1 EnvLin,envCoord c1 6 0.1 EnvLin]
 
diff --git a/Help/UGen/Envelope/envGen.help.lhs b/Help/UGen/Envelope/envGen.help.lhs
--- a/Help/UGen/Envelope/envGen.help.lhs
+++ b/Help/UGen/Envelope/envGen.help.lhs
@@ -6,3 +6,27 @@
 
 The following envelope constructors are provided: envPerc, envSine,
 envCoord, envTrapezoid, and envLinen.
+
+> import Sound.SC3.ID
+
+env_circle joins the end of the envelope to the start
+> let {e = Envelope [6000,700,100] [1,1] [EnvExp,EnvLin] Nothing Nothing
+>     ;f = envGen KR 1 1 0 1 DoNothing (env_circle e 0 EnvLin)
+>     ;o = sinOsc AR f 0 * 0.1 + impulse AR 1 0}
+> in audition (out 0 o)
+
+Env([6000,700,100],[1,1],['exp','lin']).circle.asArray
+> let {e = Envelope [6000,700,100] [1,1] [EnvExp,EnvLin] Nothing Nothing
+>     ;r = [0,4,3,0,6000,0,1,0,700,1,2,0,100,1,1,0,0,9e8,1,0]}
+> in envelope_sc3_array (env_circle e 0 EnvLin) == Just r
+
+Env([0,1],[0.1]).asArray == [0,1,-99,-99,1,0.1,1,0]
+> let e = (Envelope [0,1] [0.1] [EnvLin] Nothing Nothing)
+> in envelope_sc3_array e == Just [0,1,-99,-99,1,0.1,1,0]
+
+https://www.listarc.bham.ac.uk/lists/sc-users/msg14815.html
+> let {n = range 0.01 0.1 (lfNoise1 'a' KR 2)
+>     ;e = Envelope [0,1] [n] [EnvLin] Nothing (Just 0)
+>     ;a = envGen AR 1 1 0 1 DoNothing (env_circle e 0 EnvLin)
+>     ;o = sinOsc AR (a * 400 + 500) 0 * 0.1}
+> in audition (out 0 o)
diff --git a/Help/UGen/Envelope/envLinen.help.lhs b/Help/UGen/Envelope/envLinen.help.lhs
--- a/Help/UGen/Envelope/envLinen.help.lhs
+++ b/Help/UGen/Envelope/envLinen.help.lhs
@@ -6,3 +6,6 @@
 > let {t = envLinen 0.4 2 0.4 0.1
 >     ;e = envGen KR 1 1 0 1 RemoveSynth t}
 > in audition (out 0 (sinOsc AR 440 0 * e))
+
+> import Sound.SC3.Plot
+> plotEnvelope [envLinen 0.4 2 0.4 1,envLinen 0.6 1 1.2 0.6]
diff --git a/Help/UGen/Envelope/envPerc.help.lhs b/Help/UGen/Envelope/envPerc.help.lhs
--- a/Help/UGen/Envelope/envPerc.help.lhs
+++ b/Help/UGen/Envelope/envPerc.help.lhs
@@ -13,3 +13,6 @@
 >     ;p = envPerc' 0.01 1 a (c,c)
 >     ;e = envGen KR 1 1 0 1 RemoveSynth p }
 > in audition (out 0 (sinOsc AR 440 0 * e))
+
+> import Sound.SC3.Plot
+> plotEnvelope [envPerc 0.05 1,envPerc 0.2 0.75]
diff --git a/Help/UGen/Envelope/envSine.help.lhs b/Help/UGen/Envelope/envSine.help.lhs
--- a/Help/UGen/Envelope/envSine.help.lhs
+++ b/Help/UGen/Envelope/envSine.help.lhs
@@ -1,5 +1,5 @@
 > Sound.SC3.UGen.Help.viewSC3Help "Env.*sine"
-> :t envSine
+> :t Sound.SC3.envSine
 
 > import Sound.SC3
 
@@ -7,3 +7,5 @@
 >     ;e = envGen KR 1 1 0 1 RemoveSynth s}
 > in audition (out 0 (sinOsc AR 440 0 * e))
 
+> import Sound.SC3.Plot
+> plotEnvelope [envSine 9 1,envSine 3 0.25]
diff --git a/Help/UGen/Envelope/envTrapezoid.help.lhs b/Help/UGen/Envelope/envTrapezoid.help.lhs
--- a/Help/UGen/Envelope/envTrapezoid.help.lhs
+++ b/Help/UGen/Envelope/envTrapezoid.help.lhs
@@ -1,4 +1,4 @@
-> :t envTrapezoid
+> :t Sound.SC3.envTrapezoid
 
 > import Sound.SC3
 
@@ -6,5 +6,8 @@
 >     ; e = envGen KR 1 1 0 1 RemoveSynth t }
 > in audition (out 0 (sinOsc AR 440 0 * e))
 
-> let e = [0,3,-1,-1,0.1,0.5,1,0,0.1,0,1,0,0,1.5,1,0]
-> in envTrapezoid 0 0.25 2 0.1 == e
+> let e = [0,3,-99,-99,0.1,0.5,1,0,0.1,0,1,0,0,1.5,1,0]
+> in envelope_sc3_array (envTrapezoid 0 0.25 2 0.1) == Just e
+
+> import Sound.SC3.Plot
+> plotEnvelope [envTrapezoid 0.75 0.25 2 1,envTrapezoid 0.25 0.75 3 0.5]
diff --git a/Help/UGen/Envelope/envTriangle.help.lhs b/Help/UGen/Envelope/envTriangle.help.lhs
--- a/Help/UGen/Envelope/envTriangle.help.lhs
+++ b/Help/UGen/Envelope/envTriangle.help.lhs
@@ -6,3 +6,6 @@
 > let {t = envTriangle 1 0.1
 >     ;e = envGen KR 1 1 0 1 RemoveSynth t}
 > in audition (out 0 (sinOsc AR 440 0 * e))
+
+> import Sound.SC3.Plot
+> plotEnvelope [envTriangle 1 1,envTriangle 0.25 0.5]
diff --git a/Help/UGen/Envelope/free.help.lhs b/Help/UGen/Envelope/free.help.lhs
--- a/Help/UGen/Envelope/free.help.lhs
+++ b/Help/UGen/Envelope/free.help.lhs
@@ -7,7 +7,7 @@
 >     ;n0 = pinkNoise 'a' AR
 >     ;n1 = dust 'b' AR 20
 >     ;b = mrg [out 1 (n0 * 0.1), free n1 1001]}
-> in withSC3 (\fd -> do {_ <- async fd (d_recv (synthdef "a" a))
->                       ;_ <- async fd (d_recv (synthdef "b" b))
->                       ;send fd (s_new "a" 1001 AddToTail 0 [])
->                       ;send fd (s_new "b" (-1) AddToTail 0 [])})
+> in withSC3 (do {_ <- async (d_recv (synthdef "a" a))
+>                ;_ <- async (d_recv (synthdef "b" b))
+>                ;send (s_new "a" 1001 AddToTail 1 [])
+>                ;send (s_new "b" (-1) AddToTail 1 [])})
diff --git a/Help/UGen/Envelope/freeSelfWhenDone.help.lhs b/Help/UGen/Envelope/freeSelfWhenDone.help.lhs
--- a/Help/UGen/Envelope/freeSelfWhenDone.help.lhs
+++ b/Help/UGen/Envelope/freeSelfWhenDone.help.lhs
@@ -4,11 +4,11 @@
 > import Sound.SC3
 
 using RemoveSynth doneAction
-> let {x = mouseX' KR (-1) 1 Linear 0.1
+> let {x = mouseX KR (-1) 1 Linear 0.1
 >     ;e = linen x 1 0.1 1 RemoveSynth}
 > in audition (out 0 (sinOsc AR 440 0 * e))
 
 using FreeSelfWhenDone UGen
-> let {x = mouseX' KR (-1) 1 Linear 0.1
+> let {x = mouseX KR (-1) 1 Linear 0.1
 >     ;e = linen x 1 0.1 1 DoNothing}
 > in audition (mrg [freeSelfWhenDone e, out 0 (sinOsc AR 440 0 * e)])
diff --git a/Help/UGen/Envelope/linen.help.lhs b/Help/UGen/Envelope/linen.help.lhs
--- a/Help/UGen/Envelope/linen.help.lhs
+++ b/Help/UGen/Envelope/linen.help.lhs
@@ -6,7 +6,7 @@
 > let e = linen (impulse KR 2 0) 0.01 0.6 0.4 DoNothing
 > in audition (out 0 (e * sinOsc AR 440 0 * 0.1))
 
-> let {x = mouseX' KR (-1) 1 Linear 0.1
->     ;y = mouseY' KR 0.1 0.5 Linear 0.1
+> let {x = mouseX KR (-1) 1 Linear 0.1
+>     ;y = mouseY KR 0.1 0.5 Linear 0.1
 >     ;e = linen x 1 y 1.0 DoNothing}
 > in audition (out 0 (sinOsc AR 440 0 * e))
diff --git a/Help/UGen/Envelope/pause.help.lhs b/Help/UGen/Envelope/pause.help.lhs
--- a/Help/UGen/Envelope/pause.help.lhs
+++ b/Help/UGen/Envelope/pause.help.lhs
@@ -7,12 +7,12 @@
 >     ;g = control KR "g" 1
 >     ;a = mrg [out 0 (sinOsc AR f 0 * 0.1),pause g 1001]
 >     ;a' = synthdef "a" a}
-> in withSC3 (\fd -> do {async fd (d_recv a')
->                       ;send fd (s_new "a" 1001 AddToTail 0 [])
->                       ;send fd (s_new "a" 1002 AddToTail 0 [("f",880)])})
+> in withSC3 (do {_ <- async (d_recv a')
+>                ;send (s_new "a" 1001 AddToTail 1 [])
+>                ;send (s_new "a" 1002 AddToTail 1 [("f",880)])})
 
 Request that node 1002 pause node 1001.
-> withSC3 (\fd -> send fd (n_set 1002 [("g",0)]))
+> withSC3 (send (n_set 1002 [("g",0)]))
 
 Restart node 1001.
-> withSC3 (\fd -> send fd (n_set 1002 [("g",1)]))
+> withSC3 (send (n_set 1002 [("g",1)]))
diff --git a/Help/UGen/Envelope/pauseSelf.help.lhs b/Help/UGen/Envelope/pauseSelf.help.lhs
--- a/Help/UGen/Envelope/pauseSelf.help.lhs
+++ b/Help/UGen/Envelope/pauseSelf.help.lhs
@@ -3,9 +3,9 @@
 
 > import Sound.SC3
 
-> let {x = mouseX' KR (-1) 1 Linear 0.1
+> let {x = mouseX KR (-1) 1 Linear 0.1
 >     ;o = sinOsc AR 440 0 * 0.1}
 > in audition (mrg [pauseSelf x, out 0 o])
 
 Run paused node (assuming no intermediate node is created).
-> withSC3 (\fd -> send fd (n_run [(-1, True)]))
+> withSC3 (send (n_run [(-1, True)]))
diff --git a/Help/UGen/Envelope/pauseSelfWhenDone.help.lhs b/Help/UGen/Envelope/pauseSelfWhenDone.help.lhs
--- a/Help/UGen/Envelope/pauseSelfWhenDone.help.lhs
+++ b/Help/UGen/Envelope/pauseSelfWhenDone.help.lhs
@@ -4,16 +4,16 @@
 > import Sound.SC3
 
 using PauseSynth done action
-> let { x = mouseX' KR (-1) 1 Linear 0.1
+> let { x = mouseX KR (-1) 1 Linear 0.1
 >     ; e = linen x 1 0.1 1 PauseSynth }
 > in audition (out 0 (sinOsc AR 440 0 * e))
 
 Run paused node (assuming no intermediate node is created).
-> withSC3 (\fd -> send fd (n_run [(-1, True)]))
+> withSC3 (send (n_run [(-1, True)]))
 
-> let {x = mouseX' KR (-1) 1 Linear 0.1
+> let {x = mouseX KR (-1) 1 Linear 0.1
 >     ;e = linen x 1 0.1 1 DoNothing
 >     ;o = sinOsc AR 440 0 * e}
 > in audition (mrg [pauseSelfWhenDone e, out 0 o])
 
-> withSC3 (\fd -> send fd (n_run [(-1, True)]))
+> withSC3 (send (n_run [(-1, True)]))
diff --git a/Help/UGen/External/atari2600.help.lhs b/Help/UGen/External/atari2600.help.lhs
--- a/Help/UGen/External/atari2600.help.lhs
+++ b/Help/UGen/External/atari2600.help.lhs
@@ -6,21 +6,21 @@
 > audition (out 0 (atari2600 1 2 3 4 5 5 1))
 > audition (out 0 (atari2600 2 3 10 10 5 5 1))
 
-> let {x = mouseX' KR 0 15 Linear 0.1
->     ;y = mouseY' KR 0 15 Linear 0.1}
+> let {x = mouseX KR 0 15 Linear 0.1
+>     ;y = mouseY KR 0 15 Linear 0.1}
 > in audition (out 0 (atari2600 x y 10 10 5 5 1))
 
-> let {x = mouseX' KR 0 31 Linear 0.1
->     ;y = mouseY' KR 0 31 Linear 0.1}
+> let {x = mouseX KR 0 31 Linear 0.1
+>     ;y = mouseY KR 0 31 Linear 0.1}
 > in audition (out 0 (atari2600 2 3 x y 5 5 1))
 
-> let {x = mouseX' KR 0 15 Linear 0.1
->     ;y = mouseY' KR 0 15 Linear 0.1}
+> let {x = mouseX KR 0 15 Linear 0.1
+>     ;y = mouseY KR 0 15 Linear 0.1}
 > in audition (out 0 (atari2600 2 3 10 10 x y 1))
 
-> let {x = mouseX' KR 0 15 Linear 0.1
+> let {x = mouseX KR 0 15 Linear 0.1
 >     ;o1 = sinOsc KR 0.35 0 * 7.5 + 7.5
->     ;y = mouseY' KR 0 31 Linear 0.1
+>     ;y = mouseY KR 0 31 Linear 0.1
 >     ;o2 = sinOsc KR 0.3 0 * 5.5 + 5.5}
 > in audition (out 0 (atari2600 x o1 10 y o2 5 1))
 
diff --git a/Help/UGen/External/atsNoiSynth.help.lhs b/Help/UGen/External/atsNoiSynth.help.lhs
--- a/Help/UGen/External/atsNoiSynth.help.lhs
+++ b/Help/UGen/External/atsNoiSynth.help.lhs
@@ -4,11 +4,11 @@
 > import Sound.SC3
 
 segmented file loader
-> let load_data fd b i d =
+> let load_data b i d =
 >         if length d < 512
->         then send fd (b_setn1 b i d)
->         else do {send fd (b_setn1 b i (take 512 d))
->                 ;load_data fd b (i + 512) (drop 512 d)}
+>         then send (b_setn1 b i d)
+>         else do {send (b_setn1 b i (take 512 d))
+>                 ;load_data b (i + 512) (drop 512 d)}
 
 read file
 > ats <- atsRead "/home/rohan/cvs/tn/tn-56/ats/metal.ats"
@@ -16,12 +16,12 @@
 run re-synthesis
 > let {d = atsData ats
 >     ;h = atsHeader ats
->     ;x = mouseX' KR 0.05 1.5 Linear 0.2
->     ;y = mouseY' KR 0 1 Linear 0.2
+>     ;x = mouseX KR 0.05 1.5 Linear 0.2
+>     ;y = mouseY KR 0 1 Linear 0.2
 >     ;np = constant (atsNPartials h)
 >     ;f = x / constant (atsAnalysisDuration h)
 >     ;ptr = clip (lfSaw AR f 1 * 0.5 + 0.5) 0 1
 >     ;rs = atsNoiSynth 10 np 0 1 ptr (1 - y) y 1 0 25 0 1}
-> in withSC3 (\fd -> do {async fd (b_alloc 10 (length d) 1)
->                       ;load_data fd 10 0 d
->                       ;play fd (out 0 rs)})
+> in withSC3 (do {_ <- async (b_alloc 10 (length d) 1)
+>                ;load_data 10 0 d
+>                ;play (out 0 rs)})
diff --git a/Help/UGen/External/atsSynth.help.lhs b/Help/UGen/External/atsSynth.help.lhs
--- a/Help/UGen/External/atsSynth.help.lhs
+++ b/Help/UGen/External/atsSynth.help.lhs
@@ -10,21 +10,21 @@
 > atsHeader ats
 
 data loader that works in segments (udp packet limits)
-> let load_data fd b i d =
+> let load_data b i d =
 >         if length d < 512
->         then send fd (b_setn1 b i d)
->         else do {send fd (b_setn1 b i (take 512 d))
->                 ;load_data fd b (i + 512) (drop 512 d)}
+>         then send (b_setn1 b i d)
+>         else do {send (b_setn1 b i (take 512 d))
+>                 ;load_data b (i + 512) (drop 512 d)}
 
 simple re-synthesiser
 > let {h = atsHeader ats
 >     ;d = atsData ats
->     ;x = mouseX' KR 0.05 1.5 Linear 0.2
->     ;y = mouseY' KR 0.25 2.0 Linear 0.2
+>     ;x = mouseX KR 0.05 1.5 Linear 0.2
+>     ;y = mouseY KR 0.25 2.0 Linear 0.2
 >     ;np = constant (atsNPartials h)
 >     ;f = x / constant (atsAnalysisDuration h)
 >     ;ptr = lfSaw AR f 1 * 0.5 + 0.5
 >     ;rs = atsSynth 10 np 0 1 (clip ptr 0 1) y 0}
-> in withSC3 (\fd -> do {_ <- async fd (b_alloc 10 (length d) 1)
->                       ;load_data fd 10 0 d
->                       ;play fd (out 0 rs)})
+> in withSC3 (do {_ <- async (b_alloc 10 (length d) 1)
+>                ;load_data 10 0 d
+>                ;play (out 0 rs)})
diff --git a/Help/UGen/External/ay.help.lhs b/Help/UGen/External/ay.help.lhs
--- a/Help/UGen/External/ay.help.lhs
+++ b/Help/UGen/External/ay.help.lhs
@@ -2,12 +2,12 @@
 > Sound.SC3.UGen.DB.ugenSummary "AY"
 
 > import Sound.SC3.ID
-> import qualified Sound.SC3.Monadic as M
+> import qualified Sound.SC3.Monad as M
 
 > audition (out 0 (ay 1777 1666 1555 1 7 15 15 15 4 1 0))
 
-> let { tonea = mouseY' KR 10 3900 Exponential 0.2
->     ; toneb = mouseX' KR 10 3900 Exponential 0.2
+> let { tonea = mouseY KR 10 3900 Exponential 0.2
+>     ; toneb = mouseX KR 10 3900 Exponential 0.2
 >     ; ctl = 3
 >     ; vola = 14
 >     ; volb = 14
@@ -15,7 +15,7 @@
 >     ; s = ay tonea toneb 1555 1 ctl vola volb volc 4 1 0 }
 > in audition (out 0 (pan2 s 0 0.25))
 
-> let {rate = mouseX' KR 0.1 10 Linear 0.2
+> let {rate = mouseX KR 0.1 10 Linear 0.2
 >     ;rng l r i = return (linLin i (-1) 1 l r)
 >     ;mk_ctl l r = M.lfdNoise3 KR rate >>= rng l r
 >     ;mk_ctl_0 l r = M.lfdNoise0 KR rate >>= rng l r}
diff --git a/Help/UGen/External/concat.help.lhs b/Help/UGen/External/concat.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/External/concat.help.lhs
@@ -0,0 +1,20 @@
+> Sound.SC3.UGen.Help.viewSC3Help "Concat"
+> Sound.SC3.UGen.DB.ugenSummary "Concat"
+
+> import Sound.SC3.ID
+
+> let fileName = "/home/rohan/data/audio/pf-c5.snd"
+> in withSC3 (async (b_allocRead 12 fileName 0 0))
+
+Granulator
+> let {y0 = mouseY KR 0.01 1 Linear 0.2
+>     ;y1 = mouseY KR 12 100 Linear 0.2
+>     ;n = lfNoise0 'a' KR y0 * 3 + 4.5
+>     ;k = saw AR (sinOsc KR n 0 * 10 + y1)
+>     ;i = playBuf 1 AR 12 (bufRateScale KR 12) 0 0 Loop DoNothing
+>     ;x0 = mouseX KR 0.01 0.1 Linear 0.2
+>     ;y2 = mouseY KR 0 0.1 Linear 0.2
+>     ;c :: UGen
+>     ;c = concat' k i 2 2 2 x0 0 y2 1 0.5 0 0
+>     ;o = out 0 (pan2 c 0 1)}
+> in audition o
diff --git a/Help/UGen/External/dfm1.help.lhs b/Help/UGen/External/dfm1.help.lhs
--- a/Help/UGen/External/dfm1.help.lhs
+++ b/Help/UGen/External/dfm1.help.lhs
@@ -5,8 +5,8 @@
 
 Play it with the mouse
 > let { n = pinkNoise 'a' AR * 0.5
->     ; x = mouseX' KR 80 5000 Exponential 0.1
->     ; y = mouseX' KR 0.1 1.2 Linear 0.1 }
+>     ; x = mouseX KR 80 5000 Exponential 0.1
+>     ; y = mouseX KR 0.1 1.2 Linear 0.1 }
 > in audition (out 0 (dfm1 n x y 1 0 3e-4))
 
 Bass
diff --git a/Help/UGen/External/disintegrator.help.lhs b/Help/UGen/External/disintegrator.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/External/disintegrator.help.lhs
@@ -0,0 +1,10 @@
+> Sound.SC3.UGen.Help.viewSC3Help "Disintegrator"
+> Sound.SC3.UGen.DB.ugenSummary "Disintegrator"
+
+> import Sound.SC3
+
+> let {x = mouseX KR 0 1 Linear 0.2
+>     ;y = mouseY KR 0 1 Linear 0.2
+>     ;s = sinOsc AR (mce2 400 404) 0 * 0.2
+>     ;o = disintegrator 'a' s x y}
+> in audition (out 0 o)
diff --git a/Help/UGen/External/fmGrain.help.lhs b/Help/UGen/External/fmGrain.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/External/fmGrain.help.lhs
@@ -0,0 +1,11 @@
+> Sound.SC3.UGen.Help.viewSC3Help "FMGrain"
+> Sound.SC3.UGen.DB.ugenSummary "FMGrain"
+
+> import Sound.SC3.ID
+
+> let {t = impulse AR 20 0
+>     ;n = linLin (lfNoise1 'a' KR 1) (-1) 1 1 10
+>     ;s = envSine 9 0.1
+>     ;e = envGen KR 1 1 0 1 RemoveSynth s
+>     ;o = fmGrain t 0.2 440 220 n * e}
+> in audition (out 0 o)
diff --git a/Help/UGen/External/fmGrainB.help.lhs b/Help/UGen/External/fmGrainB.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/External/fmGrainB.help.lhs
@@ -0,0 +1,15 @@
+> Sound.SC3.UGen.Help.viewSC3Help "FMGrainB"
+> Sound.SC3.UGen.DB.ugenSummary "FMGrainB"
+
+> import Sound.SC3.ID
+
+> withSC3 (do {_ <- async (b_alloc 10 512 1)
+>             ;let f = [Normalise,Wavetable,Clear]
+>              in send (b_gen_sine2 10 f [(0.5,0.1)])})
+
+> let {t = impulse AR 20 0
+>     ;n = linLin (lfNoise1 'a' KR 1) (-1) 1 1 10
+>     ;s = envSine 9 0.1
+>     ;e = envGen KR 1 1 0 1 RemoveSynth s
+>     ;o = fmGrainB t 0.2 440 220 n 10 * e}
+> in audition (out 0 o)
diff --git a/Help/UGen/External/lfBrownNoise.help.lhs b/Help/UGen/External/lfBrownNoise.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/External/lfBrownNoise.help.lhs
@@ -0,0 +1,13 @@
+> Sound.SC3.UGen.Help.viewSC3Help "LFBrownNoise2"
+> Sound.SC3.UGen.DB.ugenSummary "LFBrownNoise2"
+
+> import Sound.SC3.ID
+
+Modulate frequency.
+> let {x = mouseX KR 0 5 Linear 0.2
+>     ;n = lfBrownNoise2 'a' AR 1000 1 x}
+> in audition (out 0 (n * 0.25))
+
+Use as frequency control.
+> let f = lfBrownNoise2 'a' KR 8 0.2 0 * 400 + 450
+> in audition (out 0 (sinOsc AR f 0 * 0.2))
diff --git a/Help/UGen/External/lpcSynth.help.lhs b/Help/UGen/External/lpcSynth.help.lhs
--- a/Help/UGen/External/lpcSynth.help.lhs
+++ b/Help/UGen/External/lpcSynth.help.lhs
@@ -1,27 +1,27 @@
 > Sound.SC3.UGen.Help.viewSC3Help "LPCSynth"
 > Sound.SC3.UGen.DB.ugenSummary "LPCSynth"
 
-> import Sound.SC3
+> import Sound.SC3.ID
 
-> let { load_data fd b i d =
->       if length d < 512
->       then send fd (b_setn1 b i d)
->       else do { send fd (b_setn1 b i (take 512 d))
->               ; load_data fd b (i + 512) (drop 512 d) }
->     ; lpc_instr b n lpc =
->       let { x = mouseX' KR 0.05 1.5 Linear 0.2
->           ; y = mouseY' KR 0.25 2.0 Linear 0.2
->           ; f = x / constant (lpcAnalysisDuration (lpcHeader lpc))
->           ; ptr = lfSaw AR f 1 * 0.5 + 0.5
->           ; MCE [cps, rms, err] = lpcVals AR b ptr
->           ; nh = floorE (22000 / cps)
->           ; voc = blip AR (cps * y) nh * (1 - err)
->           ; s = lpcSynth b (voc + (n * err * 20)) ptr }
->       in s * 1e-5 * rms }
-> in do { lpc <- lpcRead "/home/rohan/cvs/tn/tn-56/lpc/fate.lpc"
->       ; let { n = pinkNoise 'a' AR
->             ; d = lpcSC3 lpc
->             ; s = lpc_instr 10 n lpc }
->         in withSC3 (\fd -> do { async fd (b_alloc 10 (length d) 1)
->                               ; load_data fd 10 0 d
->                               ; play fd (out 0 s) }) }
+> let {load_data b i d =
+>          if length d < 512
+>          then send (b_setn1 b i d)
+>          else do {send (b_setn1 b i (take 512 d))
+>                  ;load_data b (i + 512) (drop 512 d)}
+>     ;lpc_instr b n lpc =
+>         let {x = mouseX KR 0.05 1.5 Linear 0.2
+>             ;y = mouseY KR 0.25 2.0 Linear 0.2
+>             ;f = x / constant (lpcAnalysisDuration (lpcHeader lpc))
+>             ;ptr = lfSaw AR f 1 * 0.5 + 0.5
+>             ;MCE [cps, rms, err] = lpcVals AR b ptr
+>             ;nh = floorE (22000 / cps)
+>             ;voc = blip AR (cps * y) nh * (1 - err)
+>             ;s = lpcSynth b (voc + (n * err * 20)) ptr}
+>         in s * 1e-5 * rms}
+> in do {lpc <- lpcRead "/home/rohan/cvs/tn/tn-56/lpc/fate.lpc"
+>       ;let {n = pinkNoise 'a' AR
+>            ;d = lpcSC3 lpc
+>            ; s = lpc_instr 10 n lpc}
+>        in withSC3 (do {_ <- async (b_alloc 10 (length d) 1)
+>                       ;load_data 10 0 d
+>                       ;play (out 0 s)})}
diff --git a/Help/UGen/External/membraneCircle.help.lhs b/Help/UGen/External/membraneCircle.help.lhs
--- a/Help/UGen/External/membraneCircle.help.lhs
+++ b/Help/UGen/External/membraneCircle.help.lhs
@@ -6,8 +6,8 @@
 Excite the mesh with some pink noise, triggered by an
 impulse generator.  mouseX is tension and impulse frequency,
 mouseY is duration of excitation, release-time and amplitude.
-> let { x = mouseX' KR 0 1 Linear 0.2
->     ; y = mouseY' KR 1e-9 1 Exponential 0.2
+> let { x = mouseX KR 0 1 Linear 0.2
+>     ; y = mouseY KR 1e-9 1 Exponential 0.2
 >     ; loss = linLin y 0 1 0.999999 0.999
 >     ; wobble = sinOsc KR 2 0
 >     ; tension = linLin x 0 1 0.01 0.1 + (wobble * 0.0001)
diff --git a/Help/UGen/External/mzPokey.help.lhs b/Help/UGen/External/mzPokey.help.lhs
--- a/Help/UGen/External/mzPokey.help.lhs
+++ b/Help/UGen/External/mzPokey.help.lhs
@@ -16,8 +16,8 @@
 > audition (out 0 (mz1c bln (b "10101111") (b "01000001")))
 
 > let mz2c i j p q c = mzPokey i j p q 0 0 0 0 c
-> let bX = mouseX' KR 0 255 Linear 0.1
-> let bY = mouseY' KR 0 255 Linear 0.1
+> let bX = mouseX KR 0 255 Linear 0.1
+> let bY = mouseY KR 0 255 Linear 0.1
 
 > audition (out 0 (mz2c bX (b "10101010") bY (b "10101010") (b "00000001")))
 
diff --git a/Help/UGen/External/pv_Invert.help.lhs b/Help/UGen/External/pv_Invert.help.lhs
--- a/Help/UGen/External/pv_Invert.help.lhs
+++ b/Help/UGen/External/pv_Invert.help.lhs
@@ -8,6 +8,6 @@
 >     ; i = s + n
 >     ; c0 = fft' 10 i
 >     ; c1 = pv_Invert c0
->     ; run fd = do { _ <- async fd (b_alloc 10 2048 1)
->                   ; audition (out 0 (mce2 i (ifft' c1) * 0.5)) } }
+>     ; run = do {_ <- async (b_alloc 10 2048 1)
+>                ;play (out 0 (mce2 i (ifft' c1) * 0.5))}}
 > in withSC3 run
diff --git a/Help/UGen/External/qitch.help.lhs b/Help/UGen/External/qitch.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/External/qitch.help.lhs
@@ -0,0 +1,20 @@
+> Sound.SC3.UGen.Help.viewSC3Help "Qitch"
+> Sound.SC3.UGen.DB.ugenSummary "Qitch"
+
+> import Sound.SC3.ID
+> import System.FilePath
+
+Load required data file (edit path as required)
+> let {d = "/home/rohan/opt/share/SuperCollider/Extensions/SC3plugins"
+>     ;qf = d </> "PitchDetection/extraqitchfiles/QspeckernN2048SR48000.wav"}
+> in withSC3 (async (b_allocRead 10 qf 0 0))
+
+Comparison of input frequency (x) and tracked oscillator frequency (f).
+Output is printed to the console by scsynth.
+> let {x = mouseX KR 440 880 Exponential 0.1
+>     ;o = sinOsc AR x 0 * 0.1
+>     ;MCE [f,e] = qitch KR o 10 1e-2 1 0 0 2500
+>     ;t = impulse KR 4 0
+>     ;pf = poll t f (label "f") 0
+>     ;px = poll t x (label "x") 0}
+> in audition (mrg [out 0 o,pf,px])
diff --git a/Help/UGen/External/stkMandolin.help.lhs b/Help/UGen/External/stkMandolin.help.lhs
--- a/Help/UGen/External/stkMandolin.help.lhs
+++ b/Help/UGen/External/stkMandolin.help.lhs
@@ -5,13 +5,13 @@
 > import Sound.SC3.ID
 > import qualified Sound.SC3.Monadic as M
 
-> let { x = mouseX' KR 0.25 4 Linear 0.2
+> let { x = mouseX KR 0.25 4 Linear 0.2
 >     ; tr = impulse KR x 0 - 0.5 }
 > in do { mn <- M.tRand 54 66 tr
 >       ; [bs, pp, dm, dt, at] <- replicateM 5 (M.tRand 0 127 tr)
 >       ; audition (out 0 (stkMandolin AR (midiCPS mn) bs pp dm dt at tr)) }
 
-> let { x = mouseX' KR 3 16 Linear 0.2
+> let { x = mouseX KR 3 16 Linear 0.2
 >     ; t = impulse KR x 0 - 0.5
 >     ; tr = pulseDivider t 6 0 }
 > in do { mn <- M.tIRand 54 66 t
diff --git a/Help/UGen/External/stkModalBar.help.lhs b/Help/UGen/External/stkModalBar.help.lhs
--- a/Help/UGen/External/stkModalBar.help.lhs
+++ b/Help/UGen/External/stkModalBar.help.lhs
@@ -5,7 +5,7 @@
 > import Sound.SC3.ID
 > import qualified Sound.SC3.Monadic as M
 
-> let {x = mouseX' KR 0.25 4 Linear 0.2
+> let {x = mouseX KR 0.25 4 Linear 0.2
 >     ;tr = impulse KR x 0 - 0.5
 >     ;tR = M.tRand 0 127 tr}
 > in do {i <- M.tRand 0 9 tr
@@ -14,7 +14,7 @@
 >       ;let s = stkModalBar AR (midiCPS mn) i sh sp vg vf mx v tr
 >        in audition (out 0 s)}
 
-> let {x = mouseX' KR 1 6 Linear 0.2
+> let {x = mouseX KR 1 6 Linear 0.2
 >     ;t = impulse KR x 0 - 0.5
 >     ;tr = pulseDivider t 6 0}
 > in do {mn <- M.tIRand 52 64 t
diff --git a/Help/UGen/External/stkShakers.help.lhs b/Help/UGen/External/stkShakers.help.lhs
--- a/Help/UGen/External/stkShakers.help.lhs
+++ b/Help/UGen/External/stkShakers.help.lhs
@@ -5,7 +5,7 @@
 > import Sound.SC3.ID
 > import qualified Sound.SC3.Monadic as M
 
-> let {x = mouseX' KR 0.25 4 Linear 0.2
+> let {x = mouseX KR 0.25 4 Linear 0.2
 >     ;tr = impulse KR x 0 - 0.5}
 > in do {i <- M.tRand 0 23 tr
 >       ;[e,sd,no,rf] <- replicateM 4 (M.tRand 0 127 tr)
diff --git a/Help/UGen/External/tartini.help.lhs b/Help/UGen/External/tartini.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/External/tartini.help.lhs
@@ -0,0 +1,18 @@
+> Sound.SC3.UGen.Help.viewSC3Help "Tartini"
+> Sound.SC3.UGen.DB.ugenSummary "Tartini"
+
+> import Sound.SC3.ID
+
+Comparison of input frequency (x) and tracked oscillator frequency (f).
+> let {x = mouseX KR 440 880 Exponential 0.1
+>     ;o = lfSaw AR x 0 * 0.05 {- sinOsc AR x 0 * 0.1 -}
+>     ;MCE [f,e] = tartini KR o 0.2 2048 0 1024 0.5
+>     ;t = impulse KR 4 0
+>     ;pf = poll t f (label "f") 0
+>     ;px = poll t x (label "x") 0}
+> in audition (mrg [out 0 o,pf,px])
+
+Fast test of live pitch tracking, not careful with amplitude of input
+(see better example below)
+> let MCE [f,e] = tartini KR (soundIn 0) 0.2 2048 0 1024 0.5
+> in audition (out 0 (saw AR f * 0.05))
diff --git a/Help/UGen/External/tpv.help.lhs b/Help/UGen/External/tpv.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/External/tpv.help.lhs
@@ -0,0 +1,27 @@
+> Sound.SC3.UGen.Help.viewSC3Help "TPV"
+> Sound.SC3.UGen.DB.ugenSummary "TPV"
+
+> import Sound.SC3
+
+> let fft_sz = 2048::Int
+> let hop_sz = fft_sz `div` 2
+> let fn = "/home/rohan/data/audio/pf-c5.snd"
+> let fn = "/home/rohan/data/audio/material/tyndall/var/talking-fragments/0001.WAV"
+> let tpv' f = tpv f (constant fft_sz) (constant hop_sz)
+
+> withSC3 (do {_ <- async (b_alloc 0 fft_sz 1)
+>             ;async (b_allocRead 1 fn 0 0)})
+
+> let {i = playBuf 1 AR 1 (bufRateScale KR 1) 1 0 Loop DoNothing
+>     ;f = fft 0 i 0.5 1 1 0
+>     ;x = mouseX KR 1 70 Linear 0.1
+>     ;y = mouseY KR 0.25 3 Linear 0.1
+>     ;o = tpv' f 70 x y 4 0.2}
+> in audition (out 0 (pan2 o 0 1))
+
+> let {i = playBuf 1 AR 1 (bufRateScale KR 1) 1 0 Loop DoNothing
+>     ;f = fft 0 i 0.5 1 1 0
+>     ;x = mouseX KR 0.1 100 Linear 0.1
+>     ;y = mouseY KR (-20) 40 Linear 0.1
+>     ;o = tpv' f 50 50 1 x (dbAmp y)}
+> in audition (out 0 (pan2 o 0 1))
diff --git a/Help/UGen/External/vosim.help.lhs b/Help/UGen/External/vosim.help.lhs
--- a/Help/UGen/External/vosim.help.lhs
+++ b/Help/UGen/External/vosim.help.lhs
@@ -5,16 +5,16 @@
 
 > let {p = tRand 'a' 0 1 (impulse AR 6 0)
 >     ;t = impulse AR (9 * ( 1 + ( p >* 0.95))) 0
->     ;x = mouseX' KR 0.25 2 Linear 0.2
->     ;y = mouseY' KR 0.25 1.5 Linear 0.2
+>     ;x = mouseX KR 0.25 2 Linear 0.2
+>     ;y = mouseY KR 0.25 0.75 Linear 0.2
 >     ;z = 9
->     ;rng l r i = linLin i (-1) 1 l r
->     ;mk_n e = rng 0.25 2 (lfNoise2 e KR z)
->     ;tR e l r = tRand e (mce l) (mce r)
+>     ;rng i = linLin i (-1) 1
+>     ;mk_n e = rng (lfNoise2 e KR z) 0.25 2
+>     ;tR e ll rl = tRand e (mce ll) (mce rl)
 >     ;f = tR 'b' [40,120,220] [440,990,880] t
 >     ;n = tR 'b' [4] [8,16,32] t
 >     ;d = tR 'b' [0.2,0.4,0.6] [0.6,0.8,1] t
->     ;a = tR 'b' [0] [0.2,0.6,1] t
+>     ;a = tR 'b' [0] [0.05,0.15,0.25] t
 >     ;l = tR 'b' [-1] [1] t
 >     ;xn = mk_n 'c'
 >     ;yn = mk_n 'd'
diff --git a/Help/UGen/FFT/fft.help.lhs b/Help/UGen/FFT/fft.help.lhs
--- a/Help/UGen/FFT/fft.help.lhs
+++ b/Help/UGen/FFT/fft.help.lhs
@@ -4,7 +4,7 @@
 
 > import Sound.SC3.ID
 
-> withSC3 (\fd -> async fd (b_alloc 10 2048 1))
+> withSC3 (async (b_alloc 10 2048 1))
 
 > let n = whiteNoise 'a' AR
 > in audition (out 0 (ifft' (fft' 10 (n * 0.05))))
diff --git a/Help/UGen/FFT/packFFT.help.lhs b/Help/UGen/FFT/packFFT.help.lhs
--- a/Help/UGen/FFT/packFFT.help.lhs
+++ b/Help/UGen/FFT/packFFT.help.lhs
@@ -3,7 +3,7 @@
 
 > import Sound.SC3.ID
 
-> withSC3 (\fd -> send fd (b_alloc 10 512 1))
+> withSC3 (async (b_alloc 10 512 1))
 
 > let {n = 100
 >     ;square a = a * a
diff --git a/Help/UGen/FFT/partConv.help.lhs b/Help/UGen/FFT/partConv.help.lhs
--- a/Help/UGen/FFT/partConv.help.lhs
+++ b/Help/UGen/FFT/partConv.help.lhs
@@ -12,12 +12,12 @@
 >     ; target_b = 12 {- source signal -}
 >     ; target_file = "/home/rohan/data/audio/pf-c5.snd"
 >     ; c = constant
->     ; g = let { i = playBuf 1 (c target_b) 1 0 0 Loop DoNothing
+>     ; g = let { i = playBuf 1 AR (c target_b) 1 0 0 Loop DoNothing
 >               ; pc = partConv i (c fft_size) (c ir_fd_b) }
 >           in out 0 (pc * 0.1) }
-> in withSC3 (\fd -> do
->     { async fd (b_allocRead ir_td_b ir_file 0 ir_length)
->     ; async fd (b_alloc ir_fd_b accum_size 1)
->     ; send fd (pc_preparePartConv ir_fd_b ir_td_b fft_size)
->     ; async fd (b_allocRead target_b target_file 0 0)
->     ; play fd g })
+> in withSC3 (do
+>     {_ <- async (b_allocRead ir_td_b ir_file 0 ir_length)
+>     ;_ <- async (b_alloc ir_fd_b accum_size 1)
+>     ;send (pc_preparePartConv ir_fd_b ir_td_b fft_size)
+>     ;_ <- async (b_allocRead target_b target_file 0 0)
+>     ;play g })
diff --git a/Help/UGen/FFT/pv_BinScramble.help.lhs b/Help/UGen/FFT/pv_BinScramble.help.lhs
--- a/Help/UGen/FFT/pv_BinScramble.help.lhs
+++ b/Help/UGen/FFT/pv_BinScramble.help.lhs
@@ -4,12 +4,22 @@
 > import Sound.SC3.ID
 
 > let fileName = "/home/rohan/data/audio/pf-c5.snd"
-> in withSC3 (\fd -> do {_ <- async fd (b_alloc 10 2048 1)
->                       ;async fd (b_allocRead 12 fileName 0 0)})
+> in withSC3 (do {_ <- async (b_alloc 10 2048 1)
+>                ;async (b_allocRead 12 fileName 0 0)})
 
 > let {a = playBuf 1 AR 12 (bufRateScale KR 12) 1 0 Loop DoNothing
 >     ;f = fft' 10 a
->     ;x = mouseX' KR 0.0 1.0 Linear 0.1
->     ;y = mouseY' KR 0.0 1.0 Linear 0.1
+>     ;x = mouseX KR 0.0 1.0 Linear 0.1
+>     ;y = mouseY KR 0.0 1.0 Linear 0.1
 >     ;g = pv_BinScramble 'a' f x y (impulse KR 4 0)}
 > in audition (out 0 (pan2 (ifft' g) 0 0.5))
+
+careful - feedback loop!
+> let {a = soundIn (mce2 4 5) * 4
+>     ;f = fft' 10 a
+>     ;x = mouseX KR 0.25 1 Linear 0.1
+>     ;y = mouseY KR 0.25 1 Linear 0.1
+>     ;i = impulse KR (lfNoise0 'a' KR 2 * 8 + 10) 0
+>     ;g = pv_BinScramble 'a' f x y i
+>     ;h = ifft' g}
+> in audition (out 0 (pan2 h 0 0.5))
diff --git a/Help/UGen/FFT/pv_BinShift.help.lhs b/Help/UGen/FFT/pv_BinShift.help.lhs
--- a/Help/UGen/FFT/pv_BinShift.help.lhs
+++ b/Help/UGen/FFT/pv_BinShift.help.lhs
@@ -3,10 +3,10 @@
 
 > import Sound.SC3.ID
 
-> withSC3 (\fd -> async fd (b_alloc 10 2048 1))
+> withSC3 (async (b_alloc 10 2048 1))
 
-> let { x  = mouseX' KR (-10) 100 Linear 0.1
->     ; y  = mouseY' KR 1 4 Linear 0.1
+> let { x  = mouseX KR (-10) 100 Linear 0.1
+>     ; y  = mouseY KR 1 4 Linear 0.1
 >     ; s0 = sinOsc KR 0.08 0 * 6 + 6.2
 >     ; s1 = sinOsc KR (squared s0) 0 * 100 + 800
 >     ; s2 = sinOsc AR s1 0
diff --git a/Help/UGen/FFT/pv_BinWipe.help.lhs b/Help/UGen/FFT/pv_BinWipe.help.lhs
--- a/Help/UGen/FFT/pv_BinWipe.help.lhs
+++ b/Help/UGen/FFT/pv_BinWipe.help.lhs
@@ -4,14 +4,14 @@
 > import Sound.SC3.ID
 
 > let fileName = "/home/rohan/data/audio/pf-c5.snd"
-> in withSC3 (\fd -> do {_ <- async fd (b_alloc 10 2048 1)
->                       ;_ <- async fd (b_alloc 11 2048 1)
->                       ;async fd (b_allocRead 12 fileName 0 0)})
+> in withSC3 (do {_ <- async (b_alloc 10 2048 1)
+>                ;_ <- async (b_alloc 11 2048 1)
+>                ;async (b_allocRead 12 fileName 0 0)})
 
 > let {n = whiteNoise 'a' AR
 >     ;b = playBuf 1 AR 12 (bufRateScale KR 12) 0 0 Loop DoNothing
 >     ;f = fft' 10 (n * 0.2)
 >     ;g = fft' 11 b
->     ;x = mouseX' KR 0.0 1.0 Linear 0.1
+>     ;x = mouseX KR 0.0 1.0 Linear 0.1
 >     ;h = pv_BinWipe f g x}
 > in audition (out 0 (pan2 (ifft' h) 0 0.5))
diff --git a/Help/UGen/FFT/pv_BrickWall.help.lhs b/Help/UGen/FFT/pv_BrickWall.help.lhs
--- a/Help/UGen/FFT/pv_BrickWall.help.lhs
+++ b/Help/UGen/FFT/pv_BrickWall.help.lhs
@@ -3,8 +3,8 @@
 
 > import Sound.SC3.ID
 
-> withSC3 (\fd -> async fd (b_alloc 10 2048 1))
+> withSC3 (async (b_alloc 10 2048 1))
 
 > let {n = whiteNoise 'a' AR
->     ;x = mouseX' KR (-1) 1 Linear 0.1}
+>     ;x = mouseX KR (-1) 1 Linear 0.1}
 > in audition (out 0 (ifft' (pv_BrickWall (fft' 10 (n * 0.2)) x)))
diff --git a/Help/UGen/FFT/pv_ConformalMap.help.lhs b/Help/UGen/FFT/pv_ConformalMap.help.lhs
--- a/Help/UGen/FFT/pv_ConformalMap.help.lhs
+++ b/Help/UGen/FFT/pv_ConformalMap.help.lhs
@@ -3,21 +3,21 @@
 
 > import Sound.SC3.ID
 
-> withSC3 (\fd -> async fd (b_alloc 10 1024 1))
+> withSC3 (async (b_alloc 10 1024 1))
 
 > let { i = in' 1 AR numOutputBuses * 0.5
->     ; x = mouseX' KR (-1) 1 Linear 0.1
->     ; y = mouseY' KR (-1) 1 Linear 0.1 }
+>     ; x = mouseX KR (-1) 1 Linear 0.1
+>     ; y = mouseY KR (-1) 1 Linear 0.1 }
 > in audition (out 0 (pan2 (ifft' (pv_ConformalMap (fft' 10 i) x y)) 0 1))
 
 With filtering.
-> withSC3 (\fd -> async fd (b_alloc 0 2048 1))
+> withSC3 (async (b_alloc 0 2048 1))
 
 > let { o = mce [1, 1.1, 1.5, 1.78, 2.45, 6.7, 8] * 220
 >     ; f = sinOsc KR (mce [0.16, 0.33, 0.41]) 0 * 10 + o
 >     ; s = mix (lfSaw AR f 0) * 0.3
->     ; x = mouseX' KR 0.01  2.0 Linear 0.1
->     ; y = mouseY' KR 0.01 10.0 Linear 0.1
+>     ; x = mouseX KR 0.01  2.0 Linear 0.1
+>     ; y = mouseY KR 0.01 10.0 Linear 0.1
 >     ; c = fft' 0 s
 >     ; m = ifft' (pv_ConformalMap c x y) }
 > in audition (out 0 (pan2 (combN m 0.1 0.1 10 * 0.5 + m) 0 1))
diff --git a/Help/UGen/FFT/pv_Copy.help.lhs b/Help/UGen/FFT/pv_Copy.help.lhs
--- a/Help/UGen/FFT/pv_Copy.help.lhs
+++ b/Help/UGen/FFT/pv_Copy.help.lhs
@@ -3,8 +3,8 @@
 
 > import Sound.SC3.ID
 
-> withSC3 (\fd -> do {_ <- async fd (b_alloc 0 2048 1)
->                    ;async fd (b_alloc 1 2048 1)})
+> withSC3 (do {_ <- async (b_alloc 0 2048 1)
+>             ;async (b_alloc 1 2048 1)})
 
 Proof of concept, silence
 > let {i = lfClipNoise 'a' AR 100 * 0.1
diff --git a/Help/UGen/FFT/pv_Diffuser.help.lhs b/Help/UGen/FFT/pv_Diffuser.help.lhs
--- a/Help/UGen/FFT/pv_Diffuser.help.lhs
+++ b/Help/UGen/FFT/pv_Diffuser.help.lhs
@@ -4,11 +4,11 @@
 > import Sound.SC3.ID
 
 > let fileName = "/home/rohan/data/audio/pf-c5.snd"
-> in withSC3 (\fd -> do {_ <- async fd (b_alloc 10 2048 1)
->                       ;async fd (b_allocRead 12 fileName 0 0)})
+> in withSC3 (do {_ <- async (b_alloc 10 2048 1)
+>                ;async (b_allocRead 12 fileName 0 0)})
 
 > let { a = playBuf 1 AR 12 (bufRateScale KR 12) 0 0 Loop DoNothing
 >     ; f = fft' 10 a
->     ; x = mouseX' KR 0 1 Linear 0.1
+>     ; x = mouseX KR 0 1 Linear 0.1
 >     ; h = pv_Diffuser f (x >* 0.5) }
 > in audition (out 0 (ifft' h * 0.5))
diff --git a/Help/UGen/FFT/pv_HainsworthFoote.help.lhs b/Help/UGen/FFT/pv_HainsworthFoote.help.lhs
--- a/Help/UGen/FFT/pv_HainsworthFoote.help.lhs
+++ b/Help/UGen/FFT/pv_HainsworthFoote.help.lhs
@@ -6,7 +6,7 @@
 > let { i = soundIn 0
 >     ; b = mrg2 (localBuf 'a' 2048 1) (maxLocalBufs 1)
 >     ; f = fft' b i
->     ; x = mouseX' KR 0.5 1.25 Linear 0.2
+>     ; x = mouseX KR 0.5 1.25 Linear 0.2
 >     ; h = pv_HainsworthFoote f 1 0 x 0.04
 >     ; o = sinOsc AR (mrg2 440 445) 0 * decay (h * 0.1) 0.1 }
 > in audition (out 0 (o + i))
diff --git a/Help/UGen/FFT/pv_LocalMax.help.lhs b/Help/UGen/FFT/pv_LocalMax.help.lhs
--- a/Help/UGen/FFT/pv_LocalMax.help.lhs
+++ b/Help/UGen/FFT/pv_LocalMax.help.lhs
@@ -4,11 +4,11 @@
 > import Sound.SC3.ID
 
 > let fileName = "/home/rohan/data/audio/pf-c5.snd"
-> in withSC3 (\fd -> do {_ <- async fd (b_alloc 10 2048 1)
->                       ;async fd (b_allocRead 12 fileName 0 0)})
+> in withSC3 (do {_ <- async (b_alloc 10 2048 1)
+>                ;async (b_allocRead 12 fileName 0 0)})
 
 > let { a = playBuf 1 AR 12 (bufRateScale KR 12) 0 0 Loop DoNothing
 >     ; f = fft' 10 a
->     ; x = mouseX' KR 0 100 Linear 0.1
+>     ; x = mouseX KR 0 100 Linear 0.1
 >     ; h = pv_LocalMax f x }
 > in audition (out 0 (ifft' h * 0.5))
diff --git a/Help/UGen/FFT/pv_MagAbove.help.lhs b/Help/UGen/FFT/pv_MagAbove.help.lhs
--- a/Help/UGen/FFT/pv_MagAbove.help.lhs
+++ b/Help/UGen/FFT/pv_MagAbove.help.lhs
@@ -4,19 +4,19 @@
 > import Sound.SC3.ID
 
 > let fileName = "/home/rohan/data/audio/pf-c5.snd"
-> in withSC3 (\fd -> do { async fd (b_alloc 10 2048 1)
->                       ; async fd (b_allocRead 12 fileName 0 0) })
+> in withSC3 (do {_ <- async (b_alloc 10 2048 1)
+>                ;async (b_allocRead 12 fileName 0 0) })
 
-> let { a = playBuf 1 AR 12 (bufRateScale KR 12) 0 0 Loop DoNothing
->     ; f = fft' 10 a
->     ; x = mouseX' KR 0 100 Linear 0.1
->     ; h = pv_MagAbove f x }
+> let {a = playBuf 1 AR 12 (bufRateScale KR 12) 0 0 Loop DoNothing
+>     ;f = fft' 10 a
+>     ;x = mouseX KR 0 100 Linear 0.1
+>     ;h = pv_MagAbove f x}
 > in audition (out 0 (ifft' h * 0.5))
 
 Synthesised input.
-> let { a = sinOsc KR (squared (sinOsc KR 0.08 0 * 6 + 6.2)) 0 * 100 + 800
->     ; b = sinOsc AR a 0
->     ; f = fft' 10 b
->     ; x = mouseX' KR 0 1024 Linear 0.1
->     ; h = pv_MagAbove f x }
+> let {a = sinOsc KR (squared (sinOsc KR 0.08 0 * 6 + 6.2)) 0 * 100 + 800
+>     ;b = sinOsc AR a 0
+>     ;f = fft' 10 b
+>     ;x = mouseX KR 0 1024 Linear 0.1
+>     ;h = pv_MagAbove f x}
 > in audition (out 0 (ifft' h * 0.5))
diff --git a/Help/UGen/FFT/pv_MagBelow.help.lhs b/Help/UGen/FFT/pv_MagBelow.help.lhs
--- a/Help/UGen/FFT/pv_MagBelow.help.lhs
+++ b/Help/UGen/FFT/pv_MagBelow.help.lhs
@@ -4,12 +4,12 @@
 > import Sound.SC3.ID
 
 > let fileName = "/home/rohan/data/audio/pf-c5.snd"
-> in withSC3 (\fd -> do { async fd (b_alloc 10 2048 1)
->                       ; async fd (b_allocRead 12 fileName 0 0) })
+> in withSC3 (do {_ <- async (b_alloc 10 2048 1)
+>                ;async (b_allocRead 12 fileName 0 0)})
 
 > let { a = playBuf 1 AR 12 (bufRateScale KR 12) 0 0 Loop DoNothing
 >     ; f = fft' 10 a
->     ; x = mouseX' KR 0 100 Linear 0.1
+>     ; x = mouseX KR 0 100 Linear 0.1
 >     ; h = pv_MagBelow f x }
 > in audition (out 0 (ifft' h * 0.5))
 
@@ -17,6 +17,6 @@
 > let { a = sinOsc KR (squared (sinOsc KR 0.08 0 * 6 + 6.2)) 0 * 100 + 800
 >     ; b = sinOsc AR a 0
 >     ; f = fft' 10 b
->     ; x = mouseX' KR 0 1024 Linear 0.1
+>     ; x = mouseX KR 0 1024 Linear 0.1
 >     ; h = pv_MagBelow f x }
 > in audition (out 0 (ifft' h * 0.5))
diff --git a/Help/UGen/FFT/pv_MagClip.help.lhs b/Help/UGen/FFT/pv_MagClip.help.lhs
--- a/Help/UGen/FFT/pv_MagClip.help.lhs
+++ b/Help/UGen/FFT/pv_MagClip.help.lhs
@@ -4,19 +4,19 @@
 > import Sound.SC3.ID
 
 > let fileName = "/home/rohan/data/audio/pf-c5.snd"
-> in withSC3 (\fd -> do { async fd (b_alloc 10 2048 1)
->                       ; async fd (b_allocRead 12 fileName 0 0) })
+> in withSC3 (do {_ <- async (b_alloc 10 2048 1)
+>                ;async (b_allocRead 12 fileName 0 0)})
 
 > let { a = playBuf 1 AR 12 (bufRateScale KR 12) 0 0 Loop DoNothing
 >     ; f = fft' 10 a
->     ; x = mouseX' KR 0 5 Linear 0.1
->     ; h = pv_MagBelow f x }
+>     ; x = mouseX KR 0 5 Linear 0.1
+>     ; h = pv_MagClip f x }
 > in audition (out 0 (ifft' h * 0.5))
 
 Synthesised input.
 > let { a = sinOsc KR (squared (sinOsc KR 0.08 0 * 6 + 6.2)) 0 * 100 + 800
 >     ; b = sinOsc AR a 0
 >     ; f = fft' 10 b
->     ; x = mouseX' KR 0 128 Linear 0.1
+>     ; x = mouseX KR 0 128 Linear 0.1
 >     ; h = pv_MagClip f x }
 > in audition (out 0 (ifft' h * 0.5))
diff --git a/Help/UGen/FFT/pv_MagFreeze.help.lhs b/Help/UGen/FFT/pv_MagFreeze.help.lhs
--- a/Help/UGen/FFT/pv_MagFreeze.help.lhs
+++ b/Help/UGen/FFT/pv_MagFreeze.help.lhs
@@ -4,12 +4,12 @@
 > import Sound.SC3.ID
 
 > let fileName = "/home/rohan/data/audio/pf-c5.snd"
-> in withSC3 (\fd -> do { async fd (b_alloc 10 2048 1)
->                       ; async fd (b_allocRead 12 fileName 0 0) })
+> in withSC3 (do {_ <- async (b_alloc 10 2048 1)
+>                ;async (b_allocRead 12 fileName 0 0)})
 
 > let { a = playBuf 1 AR 12 (bufRateScale KR 12) 0 0 Loop DoNothing
 >     ; f = fft' 10 a
->     ; x = mouseX' KR 0 1 Linear 0.1
+>     ; x = mouseX KR 0 1 Linear 0.1
 >     ; h = pv_MagFreeze f (x >* 0.5) }
 > in audition (out 0 (ifft' h * 0.5))
 
@@ -17,6 +17,6 @@
 > let { a = sinOsc KR (squared (sinOsc KR 0.08 0 * 6 + 6.2)) 0 * 100 + 800
 >     ; b = sinOsc AR a 0
 >     ; f = fft' 10 b
->     ; x = mouseX' KR 0 1 Linear 0.1
+>     ; x = mouseX KR 0 1 Linear 0.1
 >     ; h = pv_MagFreeze f (x >* 0.5) }
 > in audition (out 0 (ifft' h * 0.5))
diff --git a/Help/UGen/FFT/pv_RandComb.help.lhs b/Help/UGen/FFT/pv_RandComb.help.lhs
--- a/Help/UGen/FFT/pv_RandComb.help.lhs
+++ b/Help/UGen/FFT/pv_RandComb.help.lhs
@@ -3,9 +3,9 @@
 
 > import Sound.SC3.ID
 
-> withSC3 (\fd -> async fd (b_alloc 10 2048 1))
+> withSC3 (async (b_alloc 10 2048 1))
 
-> let {x = mouseX' KR 0.6 0.95 Linear 0.1
+> let {x = mouseX KR 0.6 0.95 Linear 0.1
 >     ;t = impulse KR 0.4 0
 >     ;n = whiteNoise 'a' AR
 >     ;c = pv_RandComb 'a' (fft' 10 (n * 0.5)) x t}
diff --git a/Help/UGen/FFT/pv_RandWipe.help.lhs b/Help/UGen/FFT/pv_RandWipe.help.lhs
--- a/Help/UGen/FFT/pv_RandWipe.help.lhs
+++ b/Help/UGen/FFT/pv_RandWipe.help.lhs
@@ -4,8 +4,8 @@
 > import Sound.SC3.ID
 > import qualified System.Random as R
 
-> withSC3 (\fd -> do { async fd (b_alloc 10 2048 1)
->                    ; async fd (b_alloc 11 2048 1) })
+> withSC3 (do {_ <- async (b_alloc 10 2048 1)
+>             ;async (b_alloc 11 2048 1)})
 
 > let { n0 = R.randomRs (400.0, 1000.0) (R.mkStdGen 0)
 >     ; n1 = R.randomRs (80.0, 400.0) (R.mkStdGen 1)
@@ -17,7 +17,7 @@
 >     ; b = mix (mce (zipWith (\p s -> p * (max s 0.0)) o1 o2))
 >     ; f = fft' 10 a
 >     ; g = fft' 11 b
->     ; x = mouseX' KR 0 1 Linear 0.1
->     ; y = mouseY' KR 0 1 Linear 0.1
+>     ; x = mouseX KR 0 1 Linear 0.1
+>     ; y = mouseY KR 0 1 Linear 0.1
 >     ; h = pv_RandWipe 'a' f g x (y >* 0.5) }
 > in audition (out 0 (pan2 (ifft' h) 0 0.5))
diff --git a/Help/UGen/FFT/pv_RectComb.help.lhs b/Help/UGen/FFT/pv_RectComb.help.lhs
--- a/Help/UGen/FFT/pv_RectComb.help.lhs
+++ b/Help/UGen/FFT/pv_RectComb.help.lhs
@@ -3,11 +3,11 @@
 
 > import Sound.SC3.ID
 
-> withSC3 (\fd -> async fd (b_alloc 10 2048 1))
+> withSC3 (async (b_alloc 10 2048 1))
 
 > let { n = whiteNoise 'a' AR
->     ; x = mouseX' KR 0 0.5 Linear 0.1
->     ; y = mouseY' KR 0 0.5 Linear 0.1
+>     ; x = mouseX KR 0 0.5 Linear 0.1
+>     ; y = mouseY KR 0 0.5 Linear 0.1
 >     ; c = pv_RectComb (fft' 10 (n * 0.3)) 8 x y }
 > in audition (out 0 (pan2 (ifft' c) 0 1))
 
diff --git a/Help/UGen/FFT/pvcollect.help.lhs b/Help/UGen/FFT/pvcollect.help.lhs
--- a/Help/UGen/FFT/pvcollect.help.lhs
+++ b/Help/UGen/FFT/pvcollect.help.lhs
@@ -4,21 +4,37 @@
 > import Sound.SC3.ID
 
 > let fileName = "/home/rohan/data/audio/pf-c5.snd"
-> in withSC3 (\fd -> do { async fd (b_alloc 10 1024 1)
->                       ; async fd (b_allocRead 11 fileName 0 0) })
+> in withSC3 (do {_ <- async (b_alloc 10 1024 1)
+>                ;async (b_allocRead 11 fileName 0 0)})
 
-> let {no_op m p _ = (m,p)
->     ;combf m p i = ((fmod i 7.0 ==* 0) * m,p)
->     ;spectral_delay m p _ =
->      let {l = lfPar KR 0.5 0
->          ;v = linLin l (-1) 1 0.1 1}
->      in (m + delayN m 1 v,p)
->     ;nf = 1024
->     ;bpf_sweep m p i =
->      let {l = lfPar KR 0.1 0
->          ;e = abs (i - (linLin l (-1) 1 2 (nf / 20)))}
->      in ((e <* 10) * m,p)
->     ;sf = playBuf 1 AR 11 (bufRateScale KR 11) 1 0 Loop DoNothing
->     ;c1 = fft' 10 sf
->     ;c2 = pvcollect c1 nf spectral_delay 0 250 0}
-> in audition (out 0 (0.1 * ifft' c2))
+> let spectral_delay m p _ =
+>     let {l = lfPar KR 0.5 0
+>         ;v = linLin l (-1) 1 0.1 1}
+>     in (m + delayN m 1 v,p)
+
+> let bpf_sweep nf m p i =
+>     let {l = lfPar KR 0.1 0
+>         ;e = abs (i - (linLin l (-1) 1 2 (nf / 20)))}
+>     in ((e <* 10) * m,p)
+
+> let pv_g nf cf =
+>   let {no_op m p _ = (m,p)
+>       ;combf m p i = ((fmod i 7.0 ==* 0) * m,p)
+>       ;sf = playBuf 1 AR 11 (bufRateScale KR 11) 1 0 Loop DoNothing
+>       ;c1 = fft' 10 sf
+>       ;c2 = pvcollect c1 nf cf 0 250 0}
+>   in out 0 (0.1 * ifft' c2)
+
+> let r = unlines ["number of constants       : 257"
+>                 ,"number of controls        : 0"
+>                 ,"control rates             : []"
+>                 ,"number of unit generators : 1013"
+>                 ,"unit generator rates      : [(KR,5),(AR,4),(DR,1004)]"]
+> in synthstat (pv_g 1024 spectral_delay) == r
+
+> synthstat (pv_g 1024 (bpf_sweep 1024))
+> audition (pv_g 1024 spectral_delay)
+> audition (pv_g 1024 (bpf_sweep 1024))
+
+> import Sound.SC3.UGen.Dot
+> draw_svg (pv_g 1024 (bpf_sweep 1024))
diff --git a/Help/UGen/Filter/bBandPass.help.lhs b/Help/UGen/Filter/bBandPass.help.lhs
--- a/Help/UGen/Filter/bBandPass.help.lhs
+++ b/Help/UGen/Filter/bBandPass.help.lhs
@@ -4,6 +4,6 @@
 > import Sound.SC3
 
 > let { i = soundIn (mce2 0 1)
->     ; f = mouseX' KR 20 20000 Exponential 0.2
->     ; bw = mouseY' KR 0 10 Linear 0.2 }
+>     ; f = mouseX KR 20 20000 Exponential 0.2
+>     ; bw = mouseY KR 0 10 Linear 0.2 }
 > in audition (out 0 (bBandPass i f bw))
diff --git a/Help/UGen/Filter/bBandStop.help.lhs b/Help/UGen/Filter/bBandStop.help.lhs
--- a/Help/UGen/Filter/bBandStop.help.lhs
+++ b/Help/UGen/Filter/bBandStop.help.lhs
@@ -4,11 +4,11 @@
 > import Sound.SC3
 
 > let {i = soundIn (mce2 0 1)
->     ;f = mouseX' KR 20 20000 Exponential 0.2
->     ;bw = mouseY' KR 0 10 Linear 0.2}
+>     ;f = mouseX KR 20 20000 Exponential 0.2
+>     ;bw = mouseY KR 0 10 Linear 0.2}
 > in audition (out 0 (bBandStop i f bw))
 
 > let {i = sinOsc AR 1000 (mce2 0 0)
->     ;f = mouseX' KR 800 1200 Exponential 0.2
->     ;bw = mouseY' KR 0 10 Linear 0.2}
+>     ;f = mouseX KR 800 1200 Exponential 0.2
+>     ;bw = mouseY KR 0 10 Linear 0.2}
 > in audition (out 0 (bBandStop i f bw))
diff --git a/Help/UGen/Filter/bHiPass.help.lhs b/Help/UGen/Filter/bHiPass.help.lhs
--- a/Help/UGen/Filter/bHiPass.help.lhs
+++ b/Help/UGen/Filter/bHiPass.help.lhs
@@ -4,6 +4,6 @@
 > import Sound.SC3
 
 > let { i = soundIn (mce2 0 1)
->     ; f = mouseX' KR 10 20000 Exponential 0.2
->     ; rq = mouseY' KR 0 1 Linear 0.2 }
+>     ; f = mouseX KR 10 20000 Exponential 0.2
+>     ; rq = mouseY KR 0 1 Linear 0.2 }
 > in audition (out 0 (bHiPass i f rq))
diff --git a/Help/UGen/Filter/bHiShelf.help.lhs b/Help/UGen/Filter/bHiShelf.help.lhs
--- a/Help/UGen/Filter/bHiShelf.help.lhs
+++ b/Help/UGen/Filter/bHiShelf.help.lhs
@@ -4,11 +4,11 @@
 > import Sound.SC3
 
 > let { i = soundIn (mce2 0 1)
->     ; f = mouseX' KR 2200 18000 Exponential 0.2
->     ; db = mouseY' KR 18 (-18) Linear 0.2 }
+>     ; f = mouseX KR 2200 18000 Exponential 0.2
+>     ; db = mouseY KR 18 (-18) Linear 0.2 }
 > in audition (out 0 (bHiShelf i f 1 db))
 
 > let { i = soundIn (mce2 0 1)
->     ; f = mouseX' KR 2200 18000 Exponential 0.2
->     ; rs = mouseY' KR 0.1 1 Linear 0.2 }
+>     ; f = mouseX KR 2200 18000 Exponential 0.2
+>     ; rs = mouseY KR 0.1 1 Linear 0.2 }
 > in audition (out 0 (bHiShelf i f rs 6))
diff --git a/Help/UGen/Filter/bLowPass.help.lhs b/Help/UGen/Filter/bLowPass.help.lhs
--- a/Help/UGen/Filter/bLowPass.help.lhs
+++ b/Help/UGen/Filter/bLowPass.help.lhs
@@ -5,19 +5,19 @@
 > import Sound.SC3.ID
 
 > let { i = soundIn (mce2 0 1)
->     ; f = mouseX' KR 10 20000 Exponential 0.2
->     ; rq = mouseY' KR 0 1 Linear 0.2 }
+>     ; f = mouseX KR 10 20000 Exponential 0.2
+>     ; rq = mouseY KR 0 1 Linear 0.2 }
 > in audition (out 0 (bLowPass i f rq))
 
 > let { i = mix (saw AR (mce [0.99, 1, 1.01] * 440) * 0.3)
->     ; f = mouseX' KR 100 20000 Exponential 0.2
->     ; rq = mouseY' KR 0.1 1 Linear 0.2 }
+>     ; f = mouseX KR 100 20000 Exponential 0.2
+>     ; rq = mouseY KR 0.1 1 Linear 0.2 }
 > in audition (out 0 (bLowPass i f rq))
 
 Calculate coefficients and use sos.
 > let { i = mix (saw AR (mce [0.99, 1, 1.01] * 440) * 0.3)
->     ; f = mouseX' KR 100 20000 Exponential 0.2
->     ; rq = mouseY' KR 0.1 1 Linear 0.2
+>     ; f = mouseX KR 100 20000 Exponential 0.2
+>     ; rq = mouseY KR 0.1 1 Linear 0.2
 >     ; (a0, a1, a2, b1, b2) = bLowPassCoef sampleRate f rq
 >     ; flt ip = sos ip a0 a1 a2 b1 b2 }
 > in audition (out 0 (flt (flt i)))
diff --git a/Help/UGen/Filter/bLowShelf.help.lhs b/Help/UGen/Filter/bLowShelf.help.lhs
--- a/Help/UGen/Filter/bLowShelf.help.lhs
+++ b/Help/UGen/Filter/bLowShelf.help.lhs
@@ -4,13 +4,13 @@
 > import Sound.SC3.ID
 
 > let { i = soundIn (mce2 0 1)
->     ; f = mouseX' KR 40 6000 Exponential 0.2
+>     ; f = mouseX KR 40 6000 Exponential 0.2
 >     ; rs = 1
->     ; db = mouseY' KR 24 (-24) Linear 0.2 }
+>     ; db = mouseY KR 24 (-24) Linear 0.2 }
 > in audition (out 0 (bLowShelf i f rs db))
 
 > let { i = soundIn (mce2 0 1)
->     ; f = mouseX' KR 20 6000 Exponential 0.2
->     ; rs = mouseY' KR 0.1 1 Linear 0.2
+>     ; f = mouseX KR 20 6000 Exponential 0.2
+>     ; rs = mouseY KR 0.1 1 Linear 0.2
 >     ; db = 6}
 > in audition (out 0 (bLowShelf i f rs db))
diff --git a/Help/UGen/Filter/bPeakEQ.help.lhs b/Help/UGen/Filter/bPeakEQ.help.lhs
--- a/Help/UGen/Filter/bPeakEQ.help.lhs
+++ b/Help/UGen/Filter/bPeakEQ.help.lhs
@@ -4,11 +4,11 @@
 > import Sound.SC3.ID
 
 > let { i = soundIn (mce2 0 1)
->     ; f = mouseX' KR 2200 18000 Exponential 0.2
->     ; db = mouseY' KR 12 (-12) Linear 0.2 }
+>     ; f = mouseX KR 2200 18000 Exponential 0.2
+>     ; db = mouseY KR 12 (-12) Linear 0.2 }
 > in audition (out 0 (bPeakEQ i f 0.8 db))
 
 > let { i = soundIn (mce2 0 1)
->     ; f = mouseX' KR 2200 18000 Exponential 0.2
->     ; rq = mouseY' KR 10 0.4 Linear 0.2 }
+>     ; f = mouseX KR 2200 18000 Exponential 0.2
+>     ; rq = mouseY KR 10 0.4 Linear 0.2 }
 > in audition (out 0 (bPeakEQ i f rq 6))
diff --git a/Help/UGen/Filter/bpf.help.lhs b/Help/UGen/Filter/bpf.help.lhs
--- a/Help/UGen/Filter/bpf.help.lhs
+++ b/Help/UGen/Filter/bpf.help.lhs
@@ -7,6 +7,6 @@
 > in audition (out 0 (bpf (saw AR 200 * 0.5) f 0.3 ))
 
 > let { n = whiteNoise 'a' AR
->     ; x = mouseX' KR 220 440 Exponential 0.1
->     ; y = mouseY' KR 0.01 0.2 Linear 0.1 }
+>     ; x = mouseX KR 220 440 Exponential 0.1
+>     ; y = mouseY KR 0.01 0.2 Linear 0.1 }
 > in audition (out 0 (bpf n (mce [x, 550 - x]) y))
diff --git a/Help/UGen/Filter/degreeToKey.help.lhs b/Help/UGen/Filter/degreeToKey.help.lhs
--- a/Help/UGen/Filter/degreeToKey.help.lhs
+++ b/Help/UGen/Filter/degreeToKey.help.lhs
@@ -4,11 +4,11 @@
 > import Sound.SC3.ID
 
 allocate & initialise buffer zero
-> withSC3 (\fd -> async fd (b_alloc_setn1 0 0 [0,2,3.2,5,7,9,10]))
+> withSC3 (async (b_alloc_setn1 0 0 [0,2,3.2,5,7,9,10]))
 
 modal space, mouse x controls discrete pitch in dorian mode
 > let {n = lfNoise1 'a' KR (mce [3,3.05])
->     ;x = mouseX' KR 0 15 Linear 0.1
+>     ;x = mouseX KR 0 15 Linear 0.1
 >     ;k = degreeToKey 0 x 12
 >     ;f b = let {o = sinOsc AR (midiCPS (b + k + n * 0.04)) 0 * 0.1
 >                ;t = lfPulse AR (midiCPS (mce [48,55])) 0.15 0.5
diff --git a/Help/UGen/Filter/delayN.help.lhs b/Help/UGen/Filter/delayN.help.lhs
--- a/Help/UGen/Filter/delayN.help.lhs
+++ b/Help/UGen/Filter/delayN.help.lhs
@@ -8,12 +8,12 @@
 > let {d = dust 'a' AR 1
 >     ;n = whiteNoise 'b' AR
 >     ;z = decay d 0.3 * n
->     ;x = mouseX' KR 0.0 0.2 Linear 0.1}
+>     ;x = mouseX KR 0.0 0.2 Linear 0.1}
 > in audition (out 0 (z + delayN z 0.2 x))
 
 The delay time can be varied at control rate.  An oscillator either
 reinforcing or cancelling with the delayed copy of itself.
 > let { o = sinOsc AR 320 0 * 0.1
 >     ; l = 0.005
->     ; x = mouseX' KR 0.0 l Linear 0.15 }
+>     ; x = mouseX KR 0.0 l Linear 0.15 }
 > in audition (out 0 (o + delayN o l x))
diff --git a/Help/UGen/Filter/dynKlank.help.lhs b/Help/UGen/Filter/dynKlank.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/Filter/dynKlank.help.lhs
@@ -0,0 +1,35 @@
+> Sound.SC3.UGen.Help.viewSC3Help "DynKlank"
+> Sound.SC3.UGen.DB.ugenSummary "DynKlank"
+
+> import Sound.SC3.ID
+
+{s=`[[800,1071,1153,1723],nil,[1,1,1,1]]
+;DynKlank.ar(,Impulse.ar(2,0,0.1))}.play
+> let s = klankSpec [800,1071,1153,1723] [1,1,1,1] [1,1,1,1]
+> in audition (out 0 (dynKlank (impulse AR 2 0 * 0.1) 1 0 1 s))
+
+{s=`[[800,1071,1353,1723],nil,[1,1,1,1]]
+;DynKlank.ar(s,Dust.ar(8,0.1))}.play
+> let s = klankSpec [800,1071,1353,1723] [1,1,1,1] [1,1,1,1]
+> in audition (out 0 (dynKlank (dust 'a' AR 8 * 0.1) 1 0 1 s))
+
+{s=`[[800,1071,1353,1723],nil,[1,1,1,1]]
+;DynKlank.ar(s,PinkNoise.ar(0.007))}.play
+> let s = klankSpec [800,1071,1353,1723] [1,1,1,1] [1,1,1,1]
+> in audition (out 0 (dynKlank (pinkNoise 'a' AR * 0.007) 1 0 1 s))
+
+{s=`[[200,671,1153,1723],nil,[1,1,1,1]]
+;a=[0.007,0.007]
+;DynKlank.ar(s,PinkNoise.ar(a))}.play;
+> let {s = klankSpec [200,671,1153,1723] [1,1,1,1] [1,1,1,1]
+>     ;a = mce2 0.007 0.007}
+> in audition (out 0 (dynKlank (pinkNoise 'a' AR * a) 1 0 1 s))
+
+change freqs and ringtimes with mouse
+> let {x = mouseX KR 0.5 2 Exponential 0.2
+>     ;f = map (* x) [800,1071,1153,1723]
+>     ;y = mouseY KR 0.1 10 Exponential 0.2
+>     ;d = map (* y) [1,1,1,1]
+>     ;s = klankSpec f [1,1,1,1] d
+>     ;i = impulse AR 2 0 * 0.1}
+> in audition (out 0 (dynKlank i 1 0 1 s))
diff --git a/Help/UGen/Filter/freeVerb.help.lhs b/Help/UGen/Filter/freeVerb.help.lhs
--- a/Help/UGen/Filter/freeVerb.help.lhs
+++ b/Help/UGen/Filter/freeVerb.help.lhs
@@ -6,15 +6,15 @@
 > let {i = impulse AR 1 0
 >     ;c = lfCub AR 1200 0
 >     ;s = decay i 0.25 * c * 0.1
->     ;x = mouseX' KR 0 1 Linear 0.1
->     ;y = mouseY' KR 0 1 Linear 0.1
+>     ;x = mouseX KR 0 1 Linear 0.1
+>     ;y = mouseY KR 0 1 Linear 0.1
 >     ;r = freeVerb s y x 0.5}
 > in audition (out 0 r)
 
 Process input channels
 > let {i = soundIn (mce2 0 1)
 >     ;c = mceChannel
->     ;x = mouseX' KR 0 1 Linear 0.1
->     ;y = mouseY' KR 0 1 Linear 0.1
+>     ;x = mouseX KR 0 1 Linear 0.1
+>     ;y = mouseY KR 0 1 Linear 0.1
 >     ;r = freeVerb2 (c 0 i) (c 1 i) y x 0.5}
 > in audition (out 0 r)
diff --git a/Help/UGen/Filter/freqShift.help.lhs b/Help/UGen/Filter/freqShift.help.lhs
--- a/Help/UGen/Filter/freqShift.help.lhs
+++ b/Help/UGen/Filter/freqShift.help.lhs
@@ -26,3 +26,15 @@
 >     ;i = bpf n1 1000 0.001
 >     ;s = n2 * 1000}
 > in audition (out 0 (freqShift i s 0 * 32))
+
+{a=Blip.ar(60,4,LFGauss.ar(4,1/8))
+;a=a/4+LocalIn.ar(2)
+;a=FreqShift.ar(a,LFNoise0.kr(1/4,90))
+;LocalOut.ar(DelayC.ar(a,1,0.1,0.9))
+;a}.play
+> let {e = lfGauss AR 4 (1/8) 0 Loop DoNothing
+>     ;o = blip AR 60 4 * e
+>     ;a = o / 4 + localIn 2 AR
+>     ;s = freqShift a (lfNoise0 'a' KR (1/4) * 90) 0
+>     ;z = delayC s 1 0.1 * 0.9}
+> in audition (mrg2 (out 0 s) (localOut z))
diff --git a/Help/UGen/Filter/hasher.help.lhs b/Help/UGen/Filter/hasher.help.lhs
--- a/Help/UGen/Filter/hasher.help.lhs
+++ b/Help/UGen/Filter/hasher.help.lhs
@@ -7,6 +7,6 @@
 > audition (out 0 (hasher (line AR 0 1 1 RemoveSynth) * 0.2))
 
 remap x
-> let {x = mouseX' KR 0 10 Linear 0.2
+> let {x = mouseX KR 0 10 Linear 0.2
 >     ;f = hasher (roundTo x 1) * 300 + 500}
 > in audition (out 0 (sinOsc AR f 0 * 0.1))
diff --git a/Help/UGen/Filter/lag.help.lhs b/Help/UGen/Filter/lag.help.lhs
--- a/Help/UGen/Filter/lag.help.lhs
+++ b/Help/UGen/Filter/lag.help.lhs
@@ -4,5 +4,5 @@
 > import Sound.SC3
 
 used to lag pitch
-> let x = mouseX' KR 220 440 Linear 0.2
+> let x = mouseX KR 220 440 Linear 0.2
 > in audition (out 0 (sinOsc AR (mce [x, lag x 1]) 0 * 0.1))
diff --git a/Help/UGen/Filter/lag2.help.lhs b/Help/UGen/Filter/lag2.help.lhs
--- a/Help/UGen/Filter/lag2.help.lhs
+++ b/Help/UGen/Filter/lag2.help.lhs
@@ -3,5 +3,5 @@
 
 > import Sound.SC3
 
-> let x = mouseX' KR 220 440 Exponential 0.1
+> let x = mouseX KR 220 440 Exponential 0.1
 > in audition (out 0 (sinOsc AR (mce [x, lag2 x 1]) 0 * 0.1))
diff --git a/Help/UGen/Filter/lag3.help.lhs b/Help/UGen/Filter/lag3.help.lhs
--- a/Help/UGen/Filter/lag3.help.lhs
+++ b/Help/UGen/Filter/lag3.help.lhs
@@ -3,5 +3,5 @@
 
 > import Sound.SC3
 
-> let x = mouseX' KR 220 440 Exponential 0.1
+> let x = mouseX KR 220 440 Exponential 0.1
 > in audition (out 0 (sinOsc AR (mce [x, lag3 x 1]) 0 * 0.1))
diff --git a/Help/UGen/Filter/latch.help.lhs b/Help/UGen/Filter/latch.help.lhs
--- a/Help/UGen/Filter/latch.help.lhs
+++ b/Help/UGen/Filter/latch.help.lhs
@@ -17,5 +17,5 @@
 > do {n0 <- lfNoise2 KR 8
 >    ;n1 <- lfNoise2 KR 3
 >    ;let {s = blip AR (n0 * 200 + 300) (n1 * 10 + 20)
->         ;x = mouseX' KR 1000 (sampleRate * 0.1) Exponential 0.1}
+>         ;x = mouseX KR 1000 (sampleRate * 0.1) Exponential 0.1}
 >     in audition (out 0 (latch s (impulse AR x 0)))}
diff --git a/Help/UGen/Filter/linExp.help.lhs b/Help/UGen/Filter/linExp.help.lhs
--- a/Help/UGen/Filter/linExp.help.lhs
+++ b/Help/UGen/Filter/linExp.help.lhs
@@ -1,11 +1,11 @@
 > Sound.SC3.UGen.Help.viewSC3Help "LinExp"
 > Sound.SC3.UGen.DB.ugenSummary "LinExp"
 
-> let f = linExp (mouseX' KR 0 1 Linear 0.2) 0 1 440 660
+> let f = linExp (mouseX KR 0 1 Linear 0.2) 0 1 440 660
 > in audition (out 0 (sinOsc AR f 0 * 0.1))
 
 The destination range may be k-rate.
-> let {x = mouseX' KR 0 1 Linear 0.2
->     ;y = mouseY' KR 220 440 Linear 0.2
+> let {x = mouseX KR 0 1 Linear 0.2
+>     ;y = mouseY KR 220 440 Linear 0.2
 >     ;f = linExp x 0 1 y 660}
 > in audition (out 0 (sinOsc AR f 0 * 0.1))
diff --git a/Help/UGen/Filter/linLin.help.lhs b/Help/UGen/Filter/linLin.help.lhs
--- a/Help/UGen/Filter/linLin.help.lhs
+++ b/Help/UGen/Filter/linLin.help.lhs
@@ -3,18 +3,20 @@
 
 > import Sound.SC3
 
-> let f = linLin (mouseX' KR 0 1 Linear 0.2) 0 1 440 660
+> let f = linLin (mouseX KR 0 1 Linear 0.2) 0 1 440 660
 > in audition (out 0 (sinOsc AR f 0 * 0.1))
 
 The destination range may be k-rate.
-> let { x = mouseX' KR 0 1 Linear 0.2
->     ; y = mouseY' KR 220 440 Linear 0.2
+> let { x = mouseX KR 0 1 Linear 0.2
+>     ; y = mouseY KR 220 440 Linear 0.2
 >     ; f = linLin x 0 1 y 660 }
 > in audition (out 0 (sinOsc AR f 0 * 0.1))
 
+> import Sound.SC3.ID
+
 Modulating source and destination values.
 > let {n = lfNoise2 'a' AR 80
->     ;x = mouseX' KR 200 8000 Linear 0.2
->     ;y = mouseY' KR 200 8000 Linear 0.2
+>     ;x = mouseX KR 200 8000 Linear 0.2
+>     ;y = mouseY KR 200 8000 Linear 0.2
 >     ;f = linLin n (sinOsc KR 0.2 0) (sinOsc KR 0.2543 0) x y}
 > in audition (out 0 (sinOsc AR f 0 * 0.1))
diff --git a/Help/UGen/Filter/lpf.help.lhs b/Help/UGen/Filter/lpf.help.lhs
--- a/Help/UGen/Filter/lpf.help.lhs
+++ b/Help/UGen/Filter/lpf.help.lhs
@@ -8,5 +8,5 @@
 > in audition (out 0 (lpf (saw AR 200 * 0.1) ff))
 
 Control rate filtering.
-> let ctl = lpf (lfPulse KR 8 0 0.5) (mouseX' KR 2 50 Exponential 0.1)
+> let ctl = lpf (lfPulse KR 8 0 0.5) (mouseX KR 2 50 Exponential 0.1)
 > in audition (out 0 (sinOsc AR (ctl * 200 + 400) 0 * 0.1))
diff --git a/Help/UGen/Filter/moogFF.help.lhs b/Help/UGen/Filter/moogFF.help.lhs
--- a/Help/UGen/Filter/moogFF.help.lhs
+++ b/Help/UGen/Filter/moogFF.help.lhs
@@ -4,13 +4,13 @@
 > import Sound.SC3.ID
 
 > let {n = whiteNoise 'a' AR
->     ;y = mouseY' KR 100 10000 Exponential 0.1
->     ;x = mouseX' KR 0 4 Linear 0.1}
+>     ;y = mouseY KR 100 10000 Exponential 0.1
+>     ;x = mouseX KR 0 4 Linear 0.1}
 > in audition (out 0 (moogFF (n * 0.1) y x 0))
 
 Note distortion at high gain.
-> let {x = mouseX' KR 100 20000 Exponential 0.1
->     ;y = mouseY' KR 0.1 4.0 Linear 0.1
+> let {x = mouseX KR 100 20000 Exponential 0.1
+>     ;y = mouseY KR 0.1 4.0 Linear 0.1
 >     ;i = mix (saw AR (mce [0.99, 1, 1.01] * 440)) * 0.3 }
 > in audition (out 0 (moogFF i x y 0))
 
@@ -18,5 +18,5 @@
 >     ;p = pulse AR (mce [40, 121]) (mce [0.3, 0.7])
 >     ;f0 = linLin n 0 1 0.001 2.2
 >     ;f = linLin (sinOsc KR f0 0) (-1) 1 30 4200
->     ;y = mouseY' KR 1 4 Linear 0.1}
+>     ;y = mouseY KR 1 4 Linear 0.1}
 > in audition (out 0 (moogFF p f (0.83 * y) 0))
diff --git a/Help/UGen/Filter/pitchShift.help.lhs b/Help/UGen/Filter/pitchShift.help.lhs
--- a/Help/UGen/Filter/pitchShift.help.lhs
+++ b/Help/UGen/Filter/pitchShift.help.lhs
@@ -3,6 +3,6 @@
 
 > import Sound.SC3
 
-> let {r = mouseX' KR 0.5 2.0 Linear 0.1
->     ;d = mouseY' KR 0.0 0.1 Linear 0.1}
+> let {r = mouseX KR 0.5 2.0 Linear 0.1
+>     ;d = mouseY KR 0.0 0.1 Linear 0.1}
 > in audition (out 0 (pitchShift (sinOsc AR 440 0) 0.2 r d 0))
diff --git a/Help/UGen/Filter/pluck.help.lhs b/Help/UGen/Filter/pluck.help.lhs
--- a/Help/UGen/Filter/pluck.help.lhs
+++ b/Help/UGen/Filter/pluck.help.lhs
@@ -7,8 +7,8 @@
 varying OnePole coef.
 > let {n = whiteNoise 'a' AR
 >     ;t = impulse KR 9 0
->     ;x = mouseX' KR (-0.999) 0.999 Linear 0.1
->     ;y = mouseY' KR 0.1 1 Linear 0.1
+>     ;x = mouseX KR (-0.999) 0.999 Linear 0.1
+>     ;y = mouseY KR 0.1 1 Linear 0.1
 >     ;dl = 1 / 440}
 > in audition (out 0 (pluck (n * 0.25) t dl (dl * y) 10 x))
 
@@ -19,7 +19,7 @@
 >     ;fi = udup n (rand 'a' 10 12)
 >     ;coef = rand 'a' 0.01 0.2
 >     ;l = udup n (rand 'a' (-1) 1)
->     ;x = mouseX' KR 60 1000 Exponential 0.1
+>     ;x = mouseX KR 60 1000 Exponential 0.1
 >     ;o = linLin (sinOsc KR f p) (-1) 1 x 3000
 >     ;i = impulse KR fi 0
 >     ;ks = pluck (w * 0.1) i 0.01 (1 / o) 2 coef}
diff --git a/Help/UGen/Filter/shaper.help.lhs b/Help/UGen/Filter/shaper.help.lhs
--- a/Help/UGen/Filter/shaper.help.lhs
+++ b/Help/UGen/Filter/shaper.help.lhs
@@ -3,7 +3,14 @@
 
 > import Sound.SC3
 
+> let mk_b a = do {_ <- async (b_alloc 10 512 1)
+>                 ;let f = [Normalise,Wavetable,Clear]
+>                  in async (b_gen_cheby 10 f a)}
+
 > let s = sinOsc AR 300 0 * line KR 0 1 6 RemoveSynth
-> in withSC3 (\fd -> do {async fd (b_alloc 10 512 1)
->                       ;async fd (b_gen 10 "cheby" [0, 1, 0, 1, 1, 0, 1])
->                       ;audition (out 0 (shaper 10 s * 0.5))})
+> in withSC3 (do {_ <- mk_b [1,0,1,1,0,1]
+>                ;play (out 0 (shaper 10 s * 0.1))})
+
+> let s = sinOsc AR 400 (pi / 2) * line KR 0 1 6 RemoveSynth
+> in withSC3 (do {_ <- mk_b [0.25,0.5,0.25]
+>                ;play (out 0 (shaper 10 s * 0.1))})
diff --git a/Help/UGen/Filter/varLag.help.lhs b/Help/UGen/Filter/varLag.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/Filter/varLag.help.lhs
@@ -0,0 +1,15 @@
+> Sound.SC3.UGen.Help.viewSC3Help "VarLag"
+> Sound.SC3.UGen.DB.ugenSummary "VarLag"
+
+#sc3
+SC3 is a composite UGen, hsc3 is a direct binding to the underlying UGen.
+
+> import Sound.SC3
+
+used to lag pitch
+> let x = mouseX KR 220 440 Linear 0.2
+> in audition (out 0 (sinOsc AR (mce [x, varLag x 1 x]) 0 * 0.1))
+
+compare to lag UGen
+> let x = mouseX KR 220 440 Linear 0.2
+> in audition (out 0 (sinOsc AR (mce [x, lag x 1]) 0 * 0.1))
diff --git a/Help/UGen/Filter/wrapIndex.help.lhs b/Help/UGen/Filter/wrapIndex.help.lhs
--- a/Help/UGen/Filter/wrapIndex.help.lhs
+++ b/Help/UGen/Filter/wrapIndex.help.lhs
@@ -3,8 +3,8 @@
 
 > import Sound.SC3
 
-> withSC3 (\fd -> async fd (b_alloc_setn1 0 0 [200,300,400,500,600,800]))
+> withSC3 (async (b_alloc_setn1 0 0 [200,300,400,500,600,800]))
 
-> let {x = mouseX' KR 0 18 Linear 0.1
+> let {x = mouseX KR 0 18 Linear 0.1
 >     ;f = wrapIndex 0 x}
 > in audition (out 0 (sinOsc AR f 0 * 0.5))
diff --git a/Help/UGen/Granular/grainBuf.help.lhs b/Help/UGen/Granular/grainBuf.help.lhs
--- a/Help/UGen/Granular/grainBuf.help.lhs
+++ b/Help/UGen/Granular/grainBuf.help.lhs
@@ -4,7 +4,7 @@
 > import Sound.SC3.ID
 
 > let fn = "/home/rohan/data/audio/pf-c5.snd"
-> in withSC3 (\fd -> send fd (b_allocRead 10 fn 0 0))
+> in withSC3 (send (b_allocRead 10 fn 0 0))
 
 > let {buf = 10
 >     ;dur = 15
@@ -19,11 +19,11 @@
 
 > let {b = 10
 >     ;e = -1
->     ;x = mouseX' KR (-1) 1 Linear 0.1
->     ;y = mouseY' KR 10 45 Linear 0.1
+>     ;x = mouseX KR (-1) 1 Linear 0.1
+>     ;y = mouseY KR 10 45 Linear 0.1
 >     ;i = impulse KR y 0
->     ;n1 = lfNoise1 'a' KR 500
->     ;n2 = lfNoise2 'b' KR 0.1
+>     ;n1 = lfNoise1 'α' KR 500
+>     ;n2 = lfNoise2 'α' KR 0.1
 >     ;r = linLin n1 (-1) 1 0.5 2
 >     ;p = linLin n2 (-1) 1 0 1
 >     ;g = grainBuf 2 i 0.1 b r p 2 x e 512}
diff --git a/Help/UGen/Granular/grainFM.help.lhs b/Help/UGen/Granular/grainFM.help.lhs
--- a/Help/UGen/Granular/grainFM.help.lhs
+++ b/Help/UGen/Granular/grainFM.help.lhs
@@ -11,11 +11,11 @@
 >     ;i = lin (-1) 1}
 > in audition (out 0 (grainFM 2 t 0.1 f 200 i l (-1) 512 * 0.1))
 
-> let {n1 = whiteNoise 'a' KR
->     ;n2 = lfNoise1 'b' KR 500
+> let {n1 = whiteNoise 'α' KR
+>     ;n2 = lfNoise1 'α' KR 500
 >     ;d = 5
->     ;x = mouseX' KR (-0.5) 0.5 Linear 0.1
->     ;y = mouseY' KR 0 400 Linear 0.1
+>     ;x = mouseX KR (-0.5) 0.5 Linear 0.1
+>     ;y = mouseY KR 0 400 Linear 0.1
 >     ;f = n1 * y + 440
 >     ;t = impulse KR 12.5 0
 >     ;i = linLin n2 (-1) 1 1 10}
diff --git a/Help/UGen/Granular/grainIn.help.lhs b/Help/UGen/Granular/grainIn.help.lhs
--- a/Help/UGen/Granular/grainIn.help.lhs
+++ b/Help/UGen/Granular/grainIn.help.lhs
@@ -3,9 +3,9 @@
 
 > import Sound.SC3.ID
 
-> let {n = pinkNoise 'a' AR
->     ;x = mouseX' KR (-0.5) 0.5 Linear 0.1
->     ;y = mouseY' KR 5 25 Linear 0.1
+> let {n = pinkNoise 'α' AR
+>     ;x = mouseX KR (-0.5) 0.5 Linear 0.1
+>     ;y = mouseY KR 5 25 Linear 0.1
 >     ;t = impulse KR y 0
 >     ;g = grainIn 2 t 0.1 n x (-1) 512 * 0.1}
 > in audition (out 0 g)
diff --git a/Help/UGen/Granular/grainSin.help.lhs b/Help/UGen/Granular/grainSin.help.lhs
--- a/Help/UGen/Granular/grainSin.help.lhs
+++ b/Help/UGen/Granular/grainSin.help.lhs
@@ -3,9 +3,9 @@
 
 > import Sound.SC3.ID
 
-> let {n = whiteNoise 'a' KR
->     ;x = mouseX' KR (-0.5) 0.5 Linear 0.1
->     ;y = mouseY' KR 0 400 Linear 0.1
+> let {n = whiteNoise 'α' KR
+>     ;x = mouseX KR (-0.5) 0.5 Linear 0.1
+>     ;y = mouseY KR 0 400 Linear 0.1
 >     ;f = n * y + 440
 >     ;t = impulse KR 10 0}
 > in audition (out 0 (grainSin 2 t 0.1 f x (-1) 512 * 0.1))
diff --git a/Help/UGen/Granular/warp1.help.lhs b/Help/UGen/Granular/warp1.help.lhs
--- a/Help/UGen/Granular/warp1.help.lhs
+++ b/Help/UGen/Granular/warp1.help.lhs
@@ -5,8 +5,8 @@
 
 > let {fn = "/home/rohan/data/audio/pf-c5.aif"
 >     ;p = linLin (lfSaw KR 0.05 0) (-1) 1 0 1
->     ;x = mouseX' KR 0.5 2 Linear 0.1
+>     ;x = mouseX KR 0.5 2 Linear 0.1
 >     ;w = warp1 1 10 p x 0.1 (-1) 8 0.1 2}
-> in withSC3 (\fd -> do {send fd (b_allocRead 10 fn 0 0)
->                       ;play fd (out 0 w)})
+> in withSC3 (do {send (b_allocRead 10 fn 0 0)
+>                ;play (out 0 w)})
 
diff --git a/Help/UGen/IO/in.help.lhs b/Help/UGen/IO/in.help.lhs
--- a/Help/UGen/IO/in.help.lhs
+++ b/Help/UGen/IO/in.help.lhs
@@ -21,10 +21,10 @@
 > in audition (mrg [rd, wr])
 
 Set value on a control bus
-> withSC3 (\fd -> send fd (c_set [(0, 300)]))
+> withSC3 (send (c_set [(0, 300)]))
 
 Read a control bus
 > audition (out 0 (sinOsc AR (in' 1 KR 0) 0 * 0.1))
 
 Re-set value on bus
-> withSC3 (\fd -> send fd (c_set [(0, 600)]))
+> withSC3 (send (c_set [(0, 600)]))
diff --git a/Help/UGen/IO/inTrig.help.lhs b/Help/UGen/IO/inTrig.help.lhs
--- a/Help/UGen/IO/inTrig.help.lhs
+++ b/Help/UGen/IO/inTrig.help.lhs
@@ -12,4 +12,4 @@
 > in audition (out 0 (sinOsc AR 440 0 * e))
 
 Set bus 10, each set will trigger a ping.
-> withSC3 (\fd -> send fd (c_set1 10 0.1))
+> withSC3 (send (c_set1 10 0.1))
diff --git a/Help/UGen/IO/lagIn.help.lhs b/Help/UGen/IO/lagIn.help.lhs
--- a/Help/UGen/IO/lagIn.help.lhs
+++ b/Help/UGen/IO/lagIn.help.lhs
@@ -4,10 +4,10 @@
 > import Sound.SC3
 
 Set frequency at control bus
-> withSC3 (\fd -> send fd (c_set1 10 200))
+> withSC3 (send (c_set1 10 200))
 
 Oscillator reading frequency at control bus
 > audition (out 0 (sinOsc AR (lagIn 1 10 1) 0 * 0.1))
 
 Re-set frequency at control bus
-> withSC3 (\fd -> send fd (c_set1 10 2000))
+> withSC3 (send (c_set1 10 2000))
diff --git a/Help/UGen/IO/localBuf.help.lhs b/Help/UGen/IO/localBuf.help.lhs
--- a/Help/UGen/IO/localBuf.help.lhs
+++ b/Help/UGen/IO/localBuf.help.lhs
@@ -26,7 +26,7 @@
 > let {m = maxLocalBufs 1
 >     ;b = mrg2 (localBuf 'α' 2048 2) m
 >     ;nf = bufFrames KR b
->     ;x = mouseX' KR 1 2 Linear 0.2
+>     ;x = mouseX KR 1 2 Linear 0.2
 >     ;r = playBuf 2 AR b x 1 0 Loop DoNothing * 0.1
 >     ;wr p i = bufWr b (linLin p (-1) 1 0 nf) Loop i
 >     ;n = udup 2 (whiteNoise 'α' AR)
@@ -45,7 +45,7 @@
 
 asLocalBuf combines localBuf and setBuf
 > let {b = asLocalBuf 'α' [2,1,5,3,4,0]
->     ;x = mouseX' KR 0 (bufFrames KR b) Linear 0.2
+>     ;x = mouseX KR 0 (bufFrames KR b) Linear 0.2
 >     ;f = indexL b x * 100 + 40
 >     ;o = saw AR (f * mce2 1 1.1) * 0.1}
 > in audition (out 0 o)
@@ -53,13 +53,13 @@
 detectIndex example using local buffer
 > let {b = asLocalBuf 'α' [2,3,4,0,1,5]
 >     ;n = bufFrames KR b
->     ;x = floorE (mouseX' KR 0 n Linear 0.1)
+>     ;x = floorE (mouseX KR 0 n Linear 0.1)
 >     ;i = detectIndex b x}
 > in audition (out 0 (sinOsc AR (linExp i 0 n 200 700) 0 * 0.1))
 
 degreeToKey example using local buffer
 > let {n = lfNoise1 'a' KR (mce [3,3.05])
->     ;x = mouseX' KR 0 15 Linear 0.1
+>     ;x = mouseX KR 0 15 Linear 0.1
 >     ;b = asLocalBuf 'α' [0,2,3.2,5,7,9,10]
 >     ;k = degreeToKey b x 12
 >     ;mk_c bf = let {f0 = midiCPS (bf + k + n * 0.04)
diff --git a/Help/UGen/IO/offsetOut.help.lhs b/Help/UGen/IO/offsetOut.help.lhs
--- a/Help/UGen/IO/offsetOut.help.lhs
+++ b/Help/UGen/IO/offsetOut.help.lhs
@@ -10,3 +10,21 @@
 > let {a = out 0 (impulse AR 5 0)
 >     ;b = out 0 (sinOsc AR 60 0 * 0.1) }
 > in audition (mrg [a,b])
+
+> import Sound.OSC
+
+Phase cancellation, the 'offsetOut' at bus 0 cancels, the 'out'
+at bus 1 doesn't (or at least is exceedingly unlikely to).
+> let a = do
+>       {t <- utcr
+>       ;sr <- serverSampleRateActual
+>       ;let {f = sr / 100
+>            ;c = 1 / f
+>            ;g = let o = sinOsc AR (constant f) 0 * 0.2
+>                 in synthdef "g" (mrg [offsetOut 0 o,out 1 o])
+>            ;m = s_new "g" (-1) AddToTail 1 []
+>            ;p = Bundle (UTCr (t + 0.1)) [m]
+>            ;q = Bundle (UTCr (t + 0.1 + c/2)) [m]}
+>       ;_ <- async (d_recv g)
+>       ;mapM_ sendBundle [p,q]}
+> in withSC3 a
diff --git a/Help/UGen/IO/replaceOut.help.lhs b/Help/UGen/IO/replaceOut.help.lhs
--- a/Help/UGen/IO/replaceOut.help.lhs
+++ b/Help/UGen/IO/replaceOut.help.lhs
@@ -1,7 +1,7 @@
 > Sound.SC3.UGen.Help.viewSC3Help "ReplaceOut"
 > Sound.SC3.UGen.DB.ugenSummary "ReplaceOut"
 
-> import Sound.SC3
+> import Sound.SC3.ID
 
 Send signal to a bus, overwrite existing signal.
 > let {a = out 0 (sinOsc AR (mce [330, 331]) 0 * 0.1)
@@ -14,3 +14,11 @@
 >     ;b = out 0 (sinOsc AR (mce [880, 881]) 0 * 0.1)
 >     ;c = out 0 (sinOsc AR (mce [120, 121]) 0 * 0.1)}
 > in audition (mrg [a, b, c])
+
+a writes noise to 24
+b reads 24 and replaces with filtered variant
+c reads 24 and write to 0
+> let {a = out 24 (pinkNoise 'a' AR * 0.1)
+>     ;b = replaceOut 24 (bpf (in' 1 AR 24) 440 1)
+>     ;c = out 0 (in' 1 AR 24)}
+> in mapM_ audition [a,b,c]
diff --git a/Help/UGen/IO/soundIn.help.lhs b/Help/UGen/IO/soundIn.help.lhs
--- a/Help/UGen/IO/soundIn.help.lhs
+++ b/Help/UGen/IO/soundIn.help.lhs
@@ -4,6 +4,15 @@
 
 > import Sound.SC3
 
-> audition (out 0 (soundIn 0))
-> audition (out 0 (soundIn (mce2 0 1)))
+Copy 5th input channel (index 4) to 1st output channel (index 0).
+> audition (out 0 (soundIn 4))
+
+Copy input from 4 & 5 to 0 & 1.
+> audition (out 0 (soundIn (mce2 4 5)))
+
+IO matrix:    0 1 2 3
+            0 *
+            1     *
+            2   *
+            3       *
 > audition (out 0 (soundIn (mce [0, 2, 1, 3])))
diff --git a/Help/UGen/IO/xOut.help.lhs b/Help/UGen/IO/xOut.help.lhs
--- a/Help/UGen/IO/xOut.help.lhs
+++ b/Help/UGen/IO/xOut.help.lhs
@@ -5,8 +5,8 @@
 
 Send signal to a bus, crossfading with existing contents.
 > let {p a b = sinOsc AR (mce [a, b]) 0 * 0.1
->     ;x = mouseX' KR 0 1 Linear 0.1
->     ;y = mouseY' KR 0 1 Linear 0.1}
+>     ;x = mouseX KR 0 1 Linear 0.1
+>     ;y = mouseY KR 0 1 Linear 0.1}
 > in audition (mrg [out  0   (p 220 221)
 >                  ,xOut 0 x (p 330 331)
 >                  ,xOut 0 y (p 440 441)
diff --git a/Help/UGen/Information/numRunningSynths.help.lhs b/Help/UGen/Information/numRunningSynths.help.lhs
--- a/Help/UGen/Information/numRunningSynths.help.lhs
+++ b/Help/UGen/Information/numRunningSynths.help.lhs
@@ -1,5 +1,7 @@
 > Sound.SC3.UGen.Help.viewSC3Help "NumRunningSynths"
 > Sound.SC3.UGen.DB.ugenSummary "NumRunningSynths"
 
+> import Sound.SC3
+
 each concurrent audition increases oscillator frequency
 > audition (out 0 (sinOsc AR (numRunningSynths * 200 + 400) 0 * 0.1))
diff --git a/Help/UGen/Information/poll.help.lhs b/Help/UGen/Information/poll.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/Information/poll.help.lhs
@@ -0,0 +1,13 @@
+> Sound.SC3.UGen.Help.viewSC3Help "Poll"
+> Sound.SC3.UGen.DB.ugenSummary "Poll"
+
+> import Sound.SC3.ID
+
+> let {t = impulse KR 10 0
+>     ;l = line KR 0 1 1 RemoveSynth}
+> in audition (poll t l (label "polling...") 0)
+
+multichannel expansion (requires labels be equal length...)
+> let {t = impulse KR (mce2 10 5) 0
+>     ;l = line KR 0 (mce2 1 5) (mce2 1 2) DoNothing}
+> in audition (poll t l (mce2 (label "t1") (label "t2")) 0)
diff --git a/Help/UGen/Information/sampleRate.help.lhs b/Help/UGen/Information/sampleRate.help.lhs
--- a/Help/UGen/Information/sampleRate.help.lhs
+++ b/Help/UGen/Information/sampleRate.help.lhs
@@ -9,7 +9,5 @@
 
 The server status command can extract nominal and actual sample rates
 from a running server.
-> withSC3 (\fd -> Control.Monad.liftM2
->                 (,)
->                 (serverSampleRateNominal fd)
->                 (serverSampleRateActual fd))
+> import Control.Monad
+> withSC3 (liftM2 (,) serverSampleRateNominal serverSampleRateActual)
diff --git a/Help/UGen/Information/subsampleOffset.help.lhs b/Help/UGen/Information/subsampleOffset.help.lhs
--- a/Help/UGen/Information/subsampleOffset.help.lhs
+++ b/Help/UGen/Information/subsampleOffset.help.lhs
@@ -1,15 +1,15 @@
 > Sound.SC3.UGen.Help.viewSC3Help "SubsampleOffset"
 > Sound.SC3.UGen.DB.ugenSummary "SubsampleOffset"
 
-> import Sound.OpenSoundControl
+> import Sound.OSC
 > import Sound.SC3
 
 Impulse train that can be moved between samples
-> let s = let {a = control KR "a" 0
+> let g = let {a = control KR "a" 0
 >             ;i = impulse AR 2000 0 * 0.3
 >             ;d = sampleDur
 >             ;x = 4
->             ;o = (1 - subsampleOffset) + mouseX' KR 0 a Linear 0.1
+>             ;o = (1 - subsampleOffset) + mouseX KR 0 a Linear 0.1
 >             ;r = delayC i (d * (1 + x)) (d * (o + x))}
 >         in (synthdef "s" (offsetOut 0 r))
 
@@ -17,13 +17,14 @@
 other.  When cursor is at the left, the impulses are adjacent, on the
 right, they are exactly 1 sample apart.  View this with an
 oscilloscope.
-> let run s fd = do
->       {_ <- async fd (d_recv s)
+> let run s = do
+>       {_ <- async (d_recv s)
 >       ;t <- utcr
+>       ;sr <- serverSampleRateActual
 >       ;let {t' = t + 0.2
->            ;dt = 1 / 44100.0
+>            ;dt = 1 / sr
 >            ;m n = s_new "s" (-1) AddToTail 1 [("a", n)]}
->        in do {send fd (Bundle (UTCr t') [m 3])
->              ;send fd (Bundle (UTCr (t' + dt)) [m 0]) }}
+>        in do {sendBundle (Bundle (UTCr t') [m 3])
+>              ;sendBundle (Bundle (UTCr (t' + dt)) [m 0]) }}
 
-> withSC3 (run s)
+> withSC3 (run g)
diff --git a/Help/UGen/MachineListening/beatTrack.help.lhs b/Help/UGen/MachineListening/beatTrack.help.lhs
--- a/Help/UGen/MachineListening/beatTrack.help.lhs
+++ b/Help/UGen/MachineListening/beatTrack.help.lhs
@@ -4,12 +4,12 @@
 > import Sound.SC3
 
 > let { i = soundIn 0
->     ; x = mouseX' KR (-1) 1 Linear 0.2
+>     ; x = mouseX KR (-1) 1 Linear 0.2
 >     ; MCE [b, h, q, t] = beatTrack (fft' 10 i) x
 >     ; f = mce [440, 660, 880]
 >     ; a = mce [0.4, 0.2, 0.1]
 >     ; s = mix (sinOsc AR f 0 * a * decay (mce [b, h, q]) 0.05) }
-> in withSC3 (\fd -> do { async fd (b_alloc 10 1024 1)
->                       ; play fd (out 0 (i + s)) })
+> in withSC3 (do {_ <- async (b_alloc 10 1024 1)
+>                ;play (out 0 (i + s))})
 
 
diff --git a/Help/UGen/MachineListening/loudness.help.lhs b/Help/UGen/MachineListening/loudness.help.lhs
--- a/Help/UGen/MachineListening/loudness.help.lhs
+++ b/Help/UGen/MachineListening/loudness.help.lhs
@@ -4,9 +4,9 @@
 > import Sound.SC3
 
 Assume hop of half fftsize
-> withSC3 (\fd -> async fd (b_alloc 10 1024 1))
+> withSC3 (async (b_alloc 10 1024 1))
 
-> let { x = mouseX' KR 0.001 0.1 Exponential 0.2
+> let { x = mouseX KR 0.001 0.1 Exponential 0.2
 >     ; i = sinOsc AR 1000 0 * x
 >     ; f = fft' 10 i
 >     ; l = loudness f 0.25 6
diff --git a/Help/UGen/MachineListening/onsets.help.lhs b/Help/UGen/MachineListening/onsets.help.lhs
--- a/Help/UGen/MachineListening/onsets.help.lhs
+++ b/Help/UGen/MachineListening/onsets.help.lhs
@@ -4,9 +4,9 @@
 > import Sound.SC3.ID
 
 allocate buffer 10
-> withSC3 (\fd -> async fd (b_alloc 10 512 1))
+> withSC3 (async (b_alloc 10 512 1))
 
-> let { x = mouseX' KR 0 1 Linear 0.2
+> let { x = mouseX KR 0 1 Linear 0.2
 >     ; i = soundIn 0
 >     ; c = fft' 10 i
 >     ; o = onsets' c x (onsetType "rcomplex")
@@ -27,7 +27,7 @@
 
 x varies threshold, whitenoise bursts indicate detected onsets
 > let {c = fft' 10 z
->     ;x = mouseX' KR 0 1 Linear 0.2
+>     ;x = mouseX KR 0 1 Linear 0.2
 >     ;o = onsets' c x (onsetType "rcomplex")
 >     ;p = let d = envPerc 0.001 0.1
 >          in whiteNoise 'a' AR * envGen KR o 0.2 0 1 DoNothing d}
diff --git a/Help/UGen/Math/hypot.help.lhs b/Help/UGen/Math/hypot.help.lhs
--- a/Help/UGen/Math/hypot.help.lhs
+++ b/Help/UGen/Math/hypot.help.lhs
@@ -3,8 +3,8 @@
 
 > import Sound.SC3
 
-> let { x = mouseX' KR 0 0.1 Linear 0.1
->     ; y = mouseY' KR 0 0.1 Linear 0.1 }
+> let { x = mouseX KR 0 0.1 Linear 0.1
+>     ; y = mouseY KR 0 0.1 Linear 0.1 }
 > in audition (out 0 (sinOsc AR 440 0 * hypot x y))
 
 Object travels 200 meters in 6 secs (=120kph) passing 10 meters
diff --git a/Help/UGen/Math/pow.help.lhs b/Help/UGen/Math/pow.help.lhs
--- a/Help/UGen/Math/pow.help.lhs
+++ b/Help/UGen/Math/pow.help.lhs
@@ -10,8 +10,8 @@
 > do { n0 <- lfNoise2 KR 8
 >    ; n1 <- lfNoise2 KR 3
 >    ; let { s = blip AR (n0 * 200 + 300) (n1 * 10 + 20)
->          ; x = mouseX' KR 1000 (sampleRate * 0.5) Exponential 0.1
->          ; y = mouseY' KR 1 24 Exponential 0.1
+>          ; x = mouseX KR 1000 (sampleRate * 0.5) Exponential 0.1
+>          ; y = mouseY KR 1 24 Exponential 0.1
 >          ; d = latch s (impulse AR x 0)
 >          ; b = roundUp d (0.5 ** y) }
 >      in audition (out 0 (mce2 d b)) }
diff --git a/Help/UGen/Math/roundUp.help.lhs b/Help/UGen/Math/roundUp.help.lhs
--- a/Help/UGen/Math/roundUp.help.lhs
+++ b/Help/UGen/Math/roundUp.help.lhs
@@ -3,7 +3,7 @@
 
 > import Sound.SC3
 
-> let { x = mouseX' KR 60 4000 Linear 0.1
+> let { x = mouseX KR 60 4000 Linear 0.1
 >     ; f = roundUp x 100 }
 > in audition (out 0 (sinOsc ar f 0 * 0.1))
 
diff --git a/Help/UGen/Noise/choose.help.lhs b/Help/UGen/Noise/choose.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/Noise/choose.help.lhs
@@ -0,0 +1,9 @@
+> :t choose
+
+# composite
+choose is a composite of iRand and select.
+
+> import Sound.SC3.ID
+
+> let f = udup 2 (choose 'a' (mce [440,460 .. 880]))
+> in audition (out 0 (sinOsc AR f  0 * 0.1))
diff --git a/Help/UGen/Noise/iChoose.help.lhs b/Help/UGen/Noise/iChoose.help.lhs
deleted file mode 100644
--- a/Help/UGen/Noise/iChoose.help.lhs
+++ /dev/null
@@ -1,9 +0,0 @@
-> :t iChoose
-
-# composite
-iChoose is a composite of iRand and select.
-
-> import Sound.SC3.ID
-
-> let f = udup 2 (iChoose 'a' (mce [440,460 .. 880]))
-> in audition (out 0 (sinOsc AR f  0 * 0.1))
diff --git a/Help/UGen/Noise/lfdClipNoise.help.lhs b/Help/UGen/Noise/lfdClipNoise.help.lhs
--- a/Help/UGen/Noise/lfdClipNoise.help.lhs
+++ b/Help/UGen/Noise/lfdClipNoise.help.lhs
@@ -5,11 +5,11 @@
 > import qualified Sound.SC3.Monadic as M
 
 for fast x lfClipNoise frequently seems stuck, lfdClipNoise changes smoothly
-> let {x = mouseX' KR 0.1 1000 Exponential 0.2
+> let {x = mouseX KR 0.1 1000 Exponential 0.2
 >     ;n = lfdClipNoise 'a' AR x}
 > in audition (out 0 (sinOsc AR (n * 200 + 500) 0 * 0.05))
 
-> let {x = mouseX' KR 0.1 1000 Exponential 0.2
+> let {x = mouseX KR 0.1 1000 Exponential 0.2
 >     ;n = lfClipNoise 'a' AR x}
 > in audition (out 0 (sinOsc AR (n * 200 + 500) 0 * 0.05))
 
diff --git a/Help/UGen/Noise/lfdNoise0.help.lhs b/Help/UGen/Noise/lfdNoise0.help.lhs
--- a/Help/UGen/Noise/lfdNoise0.help.lhs
+++ b/Help/UGen/Noise/lfdNoise0.help.lhs
@@ -5,10 +5,10 @@
 > import qualified Sound.SC3.Monadic as M
 
 for fast x LFNoise frequently seems stuck, LFDNoise changes smoothly
-> let x = mouseX' KR 0.1 1000 Exponential 0.2
+> let x = mouseX KR 0.1 1000 Exponential 0.2
 > in audition . (out 0) . (* 0.1) =<< M.lfdNoise0 AR x
 
-> let x = mouseX' KR 0.1 1000 Exponential 0.2
+> let x = mouseX KR 0.1 1000 Exponential 0.2
 > in audition . (out 0) . (* 0.1) =<< M.lfNoise0 AR x
 
 silent for 2 secs before going up in freq
diff --git a/Help/UGen/Noise/randSeed.help.lhs b/Help/UGen/Noise/randSeed.help.lhs
--- a/Help/UGen/Noise/randSeed.help.lhs
+++ b/Help/UGen/Noise/randSeed.help.lhs
@@ -11,7 +11,7 @@
 
 reset the seed at a variable rate
 > let {s = control KR "seed" 1956
->     ;r = randSeed KR (impulse KR (mouseX' KR 0.1 100 Linear 0.2) 0) s}
+>     ;r = randSeed KR (impulse KR (mouseX KR 0.1 100 Linear 0.2) 0) s}
 > in audition r
 
 always the same (for a given seed)...
diff --git a/Help/UGen/Noise/tExpRand.help.lhs b/Help/UGen/Noise/tExpRand.help.lhs
--- a/Help/UGen/Noise/tExpRand.help.lhs
+++ b/Help/UGen/Noise/tExpRand.help.lhs
@@ -1,11 +1,7 @@
-tExpRand lo hi trig
-
-Generates a random float value in exponential distribution from lo
-to hi each time the trig signal changes from nonpositive to
-positive values lo and hi must both have the same sign and be
-non-zero.
+> Sound.SC3.UGen.Help.viewSC3Help "TExpRand"
+> Sound.SC3.UGen.DB.ugenSummary "TExpRand"
 
-> import Sound.SC3.Monadic
+> import Sound.SC3.Monad
 
 > do { f <- tExpRand 300.0 3000.0 =<< dust KR 10
 >    ; audition (out 0 (sinOsc AR f 0 * 0.1)) }
diff --git a/Help/UGen/Oscillator/cOsc.help.lhs b/Help/UGen/Oscillator/cOsc.help.lhs
--- a/Help/UGen/Oscillator/cOsc.help.lhs
+++ b/Help/UGen/Oscillator/cOsc.help.lhs
@@ -4,15 +4,16 @@
 > import Sound.SC3
 
 Allocate and fill buffer.
-> let d = [1+2+4,1,1/2,1/3,1/4,1/5,1/6,1/7,1/8,1/9,1/10]
-> in withSC3 (\fd -> do {_ <- async fd (b_alloc 10 512 1)
->                       ;async fd (b_gen 10 "sine1" d)})
+> let {f = [Normalise,Wavetable,Clear]
+>     ;d = [1,1/2,1/3,1/4,1/5,1/6,1/7,1/8,1/9,1/10]}
+> in withSC3 ( do {_ <- async (b_alloc 10 512 1)
+>                 ;async (b_gen_sine1 10 f d)})
 
 Fixed beat frequency
-> audition (out 0 (cOsc AR 10 200 0.7 * 0.25))
+> audition (out 0 (cOsc AR 10 200 0.7 * 0.1))
 
 Modulate beat frequency with mouseX
-> audition (out 0 (cOsc AR 10 200 (mouseX' KR 0 4 Linear 0.2) * 0.25))
+> audition (out 0 (cOsc AR 10 200 (mouseX KR 0 4 Linear 0.2) * 0.1))
 
 Compare with plain osc
-> audition (out 0 (osc AR 10 200 0.0 * 0.25))
+> audition (out 0 (osc AR 10 200 0.0 * 0.1))
diff --git a/Help/UGen/Oscillator/dc.help.lhs b/Help/UGen/Oscillator/dc.help.lhs
--- a/Help/UGen/Oscillator/dc.help.lhs
+++ b/Help/UGen/Oscillator/dc.help.lhs
@@ -3,13 +3,15 @@
 
 > import Sound.SC3
 
-Not DC offset 0.5
-> audition (out 0 0.5)
+nothing
+> audition (out 0 0)
+> withSC3 (send (n_trace [-1]))
 
-Constantly zero
-> audition (out 0 (dc AR 0.5))
+zero
+> audition (out 0 (dc AR 0))
 
 DC offset; will click on start and finish
+> audition (out 0 (dc AR 0.5))
 > audition (out 0 (0.5 + sinOsc AR 440 0 * 0.1))
 > audition (out 0 (dc AR 0.5 + sinOsc AR 440 0 * 0.1))
 
diff --git a/Help/UGen/Oscillator/dynKlang.help.lhs b/Help/UGen/Oscillator/dynKlang.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/Oscillator/dynKlang.help.lhs
@@ -0,0 +1,19 @@
+> Sound.SC3.UGen.Help.viewSC3Help "DynKlang"
+> Sound.SC3.UGen.DB.ugenSummary "DynKlang"
+
+> import Sound.SC3.ID
+
+fixed
+> let s = klangSpec [800,1000,1200] [0.3,0.3,0.3] [pi,pi,pi]
+> in audition (out 0 (dynKlang AR 1 0 s * 0.4))
+
+fixed: randomised
+> let {f = map (\z -> rand z 600 1000) ['a'..'l']
+>     ;s = klangSpec f (replicate 12 1) (replicate 12 0)}
+> in audition (out 0 (dynKlang AR 1 0 s * 0.05))
+
+dynamic: frequency modulation
+> let {f = mce3 800 1000 1200 + sinOsc KR (mce3 2 3 4.2) 0 * mce3 13 24 12
+>     ;a = mce3 0.3 0.3 0.3
+>     ;p = mce3 pi pi pi}
+> in audition (out 0 (dynKlang AR 1 0 (klangSpec_mce f a p) * 0.1))
diff --git a/Help/UGen/Oscillator/fSinOsc.help.lhs b/Help/UGen/Oscillator/fSinOsc.help.lhs
--- a/Help/UGen/Oscillator/fSinOsc.help.lhs
+++ b/Help/UGen/Oscillator/fSinOsc.help.lhs
@@ -14,3 +14,27 @@
 Loses amplitude towards the end
 > let f = fSinOsc AR (xLine KR 4 401 8 RemoveSynth)
 > in audition (out 0 (fSinOsc AR (f 0 * 200 + 800) 0 * 0.1))
+
+sin grain with sin envelope
+> let {b = control IR "out" 0
+>     ;f = control IR "freq" 440
+>     ;d = control IR "dur" 0.2
+>     ;a = control IR "amp" 0.1
+>     ;p = control IR "pan" 0
+>     ;o = fSinOsc AR f 0
+>     ;s = envSine d a
+>     ;e = envGen AR 1 1 0 1 RemoveSynth s
+>     ;u = offsetOut b (pan2 o p e)
+>     ;i = synthdef "grain" u}
+> in withSC3 (async (d_recv i))
+
+> import Sound.SC3.Lang.Pattern.ID
+
+granular synthesis
+> let p = pbind [("midinote",fmap fround (pbrown 'a' 72 84 1 inf))
+>               ,("detune",pwhite 'a' 0 10 inf)
+>               ,("dur",pbrown 'b' 0.005 0.15 0.05 inf)
+>               ,("legato",pbrown 'c' 1 2 0.1 inf)
+>               ,("amp",pbrown 'd' 0.05 0.25 0.05 inf)
+>               ,("pan",pbrown 'e' (-1) 1 0.2 inf)]
+> in audition ("grain",p)
diff --git a/Help/UGen/Oscillator/gendy1.help.lhs b/Help/UGen/Oscillator/gendy1.help.lhs
--- a/Help/UGen/Oscillator/gendy1.help.lhs
+++ b/Help/UGen/Oscillator/gendy1.help.lhs
@@ -12,13 +12,13 @@
 > in audition (out 0 (pan2 g 0 0.15))
 
 Play me
-> let {x = mouseX' KR 100 1000 Exponential 0.1
+> let {x = mouseX KR 100 1000 Exponential 0.1
 >     ;g = gendy1 AR 1 1 1.0 1.0 30 100 0.3 0.05 5 5}
 > in audition (out 0 (pan2 (rlpf g 500 0.3 * 0.2) 0 0.25))
 
 Scream!
-> let {x = mouseX' KR 220 440 Exponential 0.1
->     ;y = mouseY' KR 0.0 1.0 Linear 0.1}
+> let {x = mouseX KR 220 440 Exponential 0.1
+>     ;y = mouseY KR 0.0 1.0 Linear 0.1}
 > in audition (out 0 (pan2 (gendy1 AR 2 3 1 1 x (8 * x) y y 7 7) 0.0 0.3))
 
 1 CP = random noise
@@ -43,19 +43,19 @@
 
 Modulate distributions. Change of pitch as distributions change
 the duration structure and spectrum
-> let {x = mouseX' KR 0 7 Linear 0.1
->     ;y = mouseY' KR 0 7 Linear 0.1
+> let {x = mouseX KR 0 7 Linear 0.1
+>     ;y = mouseY KR 0 7 Linear 0.1
 >     ;g = gendy1 AR x y 1 1 440 660 0.5 0.5 12 12}
 > in audition (out 0 (pan2 g 0 0.2))
 
 Modulate number of CPs.
-> let {x = mouseX' KR 1 13 Linear 0.1
+> let {x = mouseX KR 1 13 Linear 0.1
 >     ;g = gendy1 AR 1 1 1 1 440 660 0.5 0.5 12 x}
 > in audition (out 0 (pan2 g 0 0.2))
 
 Self modulation.
-> let {x = mouseX' KR 1   13 Linear 0.1
->     ;y = mouseY' KR 0.1 10 Linear 0.1
+> let {x = mouseX KR 1   13 Linear 0.1
+>     ;y = mouseY KR 0.1 10 Linear 0.1
 >     ;g0 = gendy1 AR 5 4 0.3 0.7 0.1 y 1.0 1.0 5 5
 >     ;g1 = gendy1 AR 1 1 1 1 440 (g0 * 500 + 600) 0.5 0.5 12 x}
 > in audition (out 0 (pan2 g1 0 0.2))
@@ -68,8 +68,8 @@
 > in audition (out 0 (pan2 g 0 0.2))
 
 Near the corners are interesting.
-> let {x = mouseX' KR 0 200 Linear 0.1
->     ;y = mouseY' KR 0 200 Linear 0.1
+> let {x = mouseX KR 0 200 Linear 0.1
+>     ;y = mouseY KR 0 200 Linear 0.1
 >     ;p = lfPulse KR x 0 0.4
 >     ;s = sinOsc KR y 0 * 0.5
 >     ;g = gendy1 AR 6 6 p s 440 660 0.5 0.5 12 12}
@@ -90,7 +90,7 @@
 > in audition (out 0 (mix (mce (map node ['a'..'i']))))
 
 Try durscale 10.0 and 0.0 too.
-> let {x = mouseX' KR 10 700 Linear 0.1
->     ;y = mouseY' KR 50 1000 Linear 0.1
+> let {x = mouseX KR 10 700 Linear 0.1
+>     ;y = mouseY KR 50 1000 Linear 0.1
 >     ;g = gendy1 AR 2 3 1 1 1 x 0.5 0.1 10 10}
 > in audition (out 0 (pan2 (combN (resonz g y 0.1) 0.1 0.1 5) 0.0 0.6))
diff --git a/Help/UGen/Oscillator/impulse.help.lhs b/Help/UGen/Oscillator/impulse.help.lhs
--- a/Help/UGen/Oscillator/impulse.help.lhs
+++ b/Help/UGen/Oscillator/impulse.help.lhs
@@ -8,8 +8,8 @@
 > let f = xLine KR 800 10 5 RemoveSynth
 > in audition (out 0 (impulse AR f 0.0 * 0.1))
 
-> let {f = mouseY' KR 4 8 Linear 0.1
->     ;x = mouseX' KR 0 1 Linear 0.1}
+> let {f = mouseY KR 4 8 Linear 0.1
+>     ;x = mouseX KR 0 1 Linear 0.1}
 > in audition (out 0 (impulse AR f (mce [0,x]) * 0.1))
 
 An impulse with frequency 0 returns a single impulse
diff --git a/Help/UGen/Oscillator/klang.help.lhs b/Help/UGen/Oscillator/klang.help.lhs
--- a/Help/UGen/Oscillator/klang.help.lhs
+++ b/Help/UGen/Oscillator/klang.help.lhs
@@ -4,9 +4,23 @@
 # SC3
 Input re-ordering of specification array.
 
-> import Sound.SC3
+> import Sound.SC3.ID
 
 > let {f = [440,550..1100]
 >     ;a = take 7 (cycle [0.05, 0.02])
 >     ;p = replicate 7 0}
 > in audition (out 0 (klang AR 1 0 (klangSpec f a p)))
+
+play({Klang.ar(`[[800,1000,1200],[0.3,0.3,0.3],[pi,pi,pi]],1,0)*0.4})
+> let s = klangSpec [800,1000,1200] [0.3,0.3,0.3] [pi,pi,pi]
+> in audition (out 0 (klang AR 1 0 s * 0.4))
+
+play({Klang.ar(`[[800,1000,1200],nil,nil],1,0)*0.25})
+> let s = klangSpec [800,1000,1200] [1,1,1] [0,0,0]
+> in audition (out 0 (klang AR 1 0 s * 0.25))
+
+play({Klang.ar(`[Array.rand(12,600.0,1000.0),nil,nil],1,0)*0.05})
+> let {f = map (\z -> rand z 600 1000) ['a'..'l']
+>     ;s = klangSpec f (replicate 12 1) (replicate 12 0)}
+> in audition (out 0 (klang AR 1 0 s * 0.05))
+
diff --git a/Help/UGen/Oscillator/lfGauss.help.lhs b/Help/UGen/Oscillator/lfGauss.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/Oscillator/lfGauss.help.lhs
@@ -0,0 +1,76 @@
+> Sound.SC3.UGen.Help.viewSC3Help "LFGauss"
+> Sound.SC3.UGen.DB.ugenSummary "LFGauss"
+
+> import Sound.SC3
+
+modulating duration
+> let {d = xLine KR 0.1 0.001 10 DoNothing
+>     ;g = lfGauss AR d 0.03 0 Loop DoNothing}
+> in audition (out 0 (g * 0.2))
+
+modulating width, freq 60 Hz
+> let {w = xLine KR 0.1 0.001 10 DoNothing
+>     ;g = lfGauss AR (1/60) w 0 Loop DoNothing}
+> in audition (out 0 (g * 0.2))
+
+modulating both: x position is frequency, y is width factor.
+note the artefacts due to alisasing at high frequencies
+> let {d = mouseX KR (1/8000) 0.1 Exponential 0.2
+>     ;w = mouseX KR 0.001 0.1 Exponential 0.2
+>     ;g = lfGauss AR d w 0 Loop DoNothing}
+> in audition (out 0 (g * 0.1))
+
+LFGauss as amplitude modulator
+> let {d = mouseX KR 1 0.001 Exponential 0.2
+>     ;g = lfGauss AR d 0.1 0 Loop DoNothing
+>     ;o = sinOsc AR 1000 0}
+> in audition (out 0 (g * o * 0.1))
+
+modulate iphase
+> let {ph = mouseX KR (-1) 1 Linear 0.2
+>     ;g = lfGauss AR 0.001 0.2 (mce2 0 ph) Loop DoNothing}
+> in audition (out 0 (mix g * 0.2))
+
+for very small width we are "approaching" a dirac function
+> let {w = sampleDur * mouseX KR 10 3000 Exponential 0.2
+>     ;g = lfGauss AR 0.01 w 0 Loop DoNothing}
+> in audition (out 0 (g * 0.2))
+
+dur and width can be modulated at audio rate
+> let {x = mouseX KR 2 1000 Exponential 0.2
+>     ;d = range 0.0006 0.01 (sinOsc AR x 0 * mce2 1 1.1)
+>     ;w = range 0.01 0.3 (sinOsc AR (0.5 * (mce2 1 1.1)) 0)
+>     ;g = lfGauss AR d w 0 Loop DoNothing}
+> in audition (out 0 (g * 0.2))
+
+several frequecies and widths combined
+> let {x = mouseX KR 1 0.07 Exponential 0.2
+>     ;y = mouseY KR 1 3 Linear 0.2
+>     ;g = lfGauss AR x (y ** mce [-1,-2 .. -6]) 0 Loop DoNothing
+>     ;o = sinOsc AR (200 * (1.3 ** mce [0..5])) 0}
+> in audition (out 0 (mix (g * o) * 0.1))
+
+> withSC3 (send (n_trace [-1]))
+
+gabor grain
+> let {b = control IR "out" 0
+>     ;f = control IR "freq" 440
+>     ;s = control IR "sustain" 1
+>     ;p = control IR "pan" 1
+>     ;a = control IR "amp" 0.1
+>     ;w = control IR "width" 0.25
+>     ;e = lfGauss AR s w 0 NoLoop RemoveSynth
+>     ;o = fSinOsc AR f (pi / 2) * e
+>     ;u = offsetOut b (pan2 o p a)
+>     ;i = synthdef "gabor" u}
+> in withSC3 (async (d_recv i))
+
+> import Sound.SC3.Lang.Pattern.ID
+
+granular synthesis, modulating duration, width and pan
+> let {p = pbind [("freq",1000)
+>                ,("legato",2)
+>                ,("dur",pbrown 'a' 0.005 0.025 0.001 inf)
+>                ,("width",pbrown 'b' 0.05 0.25 0.005 inf)
+>                ,("pan",pbrown 'c' (-1) 1 0.05 inf)]}
+> in audition ("gabor",p)
diff --git a/Help/UGen/Oscillator/lfPulse.help.lhs b/Help/UGen/Oscillator/lfPulse.help.lhs
--- a/Help/UGen/Oscillator/lfPulse.help.lhs
+++ b/Help/UGen/Oscillator/lfPulse.help.lhs
@@ -9,5 +9,5 @@
 > let f = lfPulse KR 3 0 0.3 * 200 + 200
 > in audition (out 0 (lfPulse AR f 0 0.2 * 0.1))
 
-> let x = mouseX' KR 0 1 Linear 0.2
+> let x = mouseX KR 0 1 Linear 0.2
 > in audition (out 0 (lfPulse AR 220 0 x * 0.1))
diff --git a/Help/UGen/Oscillator/pmOsc.help.lhs b/Help/UGen/Oscillator/pmOsc.help.lhs
--- a/Help/UGen/Oscillator/pmOsc.help.lhs
+++ b/Help/UGen/Oscillator/pmOsc.help.lhs
@@ -18,5 +18,5 @@
 
 PM textures
 > import qualified Sound.SC3.Lang.Control.OverlapTexture as L
-> L.overlapTextureU (1,0,5,maxBound) (pmi 1)
+> L.overlapTextureU (0,1,5,maxBound) (pmi 1)
 > L.overlapTextureU (6,6,6,maxBound) (pmi 12)
diff --git a/Help/UGen/Oscillator/sinOscFB.help.lhs b/Help/UGen/Oscillator/sinOscFB.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/Oscillator/sinOscFB.help.lhs
@@ -0,0 +1,25 @@
+> Sound.SC3.UGen.Help.viewSC3Help "SinOscFB"
+> Sound.SC3.UGen.DB.ugenSummary "SinOscFB"
+
+> import Sound.SC3
+
+{SinOscFB.ar([400,301],MouseX.kr(0,4))*0.1}.play
+> let {x = mouseX KR 0 4 Linear 0.2
+>     ;o = sinOscFB AR (mce2 400 301) x * 0.1}
+> in audition (out 0 o)
+
+{y=MouseY.kr(10,1000,'exponential')
+;x=MouseX.kr(0.5pi,pi)
+;SinOscFB.ar(y,x)*0.1}.play
+> let {y = mouseY KR 10 1000 Exponential 0.2
+>     ;x = mouseX KR (pi/2) pi Linear 0.2
+>     ;o = sinOscFB AR y x * 0.1}
+> in audition (out 0 o)
+
+{y=MouseY.kr(1,1000,'exponential')
+;x=MouseX.kr(0.5pi,pi)
+;SinOscFB.ar(100*SinOscFB.ar(y)+200,x)*0.1}.play
+> let {y = mouseY KR 1 1000 Exponential 0.2
+>     ;x = mouseX KR (pi/2) pi Linear 0.2
+>     ;o = sinOscFB AR (100 * sinOscFB AR y 0 + 200) x * 0.1}
+> in audition (out 0 o)
diff --git a/Help/UGen/Oscillator/syncSaw.help.lhs b/Help/UGen/Oscillator/syncSaw.help.lhs
--- a/Help/UGen/Oscillator/syncSaw.help.lhs
+++ b/Help/UGen/Oscillator/syncSaw.help.lhs
@@ -7,6 +7,6 @@
 > in audition (out 0 (syncSaw AR 100 f * 0.1))
 
 Mouse control
-> let {sy_f = mouseY' KR 80 220 Exponential 0.2
->     ;sw_f = sy_f * mouseX' KR 1 3 Linear 0.2}
+> let {sy_f = mouseY KR 80 220 Exponential 0.2
+>     ;sw_f = sy_f * mouseX KR 1 3 Linear 0.2}
 > in audition (out 0 (syncSaw AR sy_f sw_f * 0.1))
diff --git a/Help/UGen/Oscillator/tChoose.help.lhs b/Help/UGen/Oscillator/tChoose.help.lhs
--- a/Help/UGen/Oscillator/tChoose.help.lhs
+++ b/Help/UGen/Oscillator/tChoose.help.lhs
@@ -6,7 +6,7 @@
 
 > import Sound.SC3.ID
 
-> let {x = mouseX' kr 1 1000 Exponential 0.1
+> let {x = mouseX KR 1 1000 Exponential 0.1
 >     ;t = dust 'a' AR x
 >     ;f = midiCPS (tIRand 'b' 48 60 t)
 >     ;o = let a = mce [sinOsc AR f 0
diff --git a/Help/UGen/Oscillator/tGrains.help.lhs b/Help/UGen/Oscillator/tGrains.help.lhs
--- a/Help/UGen/Oscillator/tGrains.help.lhs
+++ b/Help/UGen/Oscillator/tGrains.help.lhs
@@ -5,29 +5,29 @@
 
 Load audio data
 > let fn = "/home/rohan/data/audio/pf-c5.aif"
-> in withSC3 (\fd -> async fd (b_allocRead 10 fn 0 0))
+> in withSC3 (async (b_allocRead 10 fn 0 0))
 
 Mouse control
-> let {tRate = mouseY' KR 2 200 Exponential 0.1
->     ;ctr = mouseX' KR 0 (bufDur KR 10) Linear 0.1
+> let {tRate = mouseY KR 2 200 Exponential 0.1
+>     ;ctr = mouseX KR 0 (bufDur KR 10) Linear 0.1
 >     ;tr = impulse AR tRate 0}
 > in audition (out 0 (tGrains 2 tr 10 1 ctr (4 / tRate) 0 0.1 2))
 
 > let {b = 10
->     ;rt = mouseY' KR 8 120 Exponential 0.1
+>     ;rt = mouseY KR 8 120 Exponential 0.1
 >     ;dur = 4 / rt
 >     ;clk = dust 'a' AR rt
 >     ;r = tRand 'a' 0 0.01 clk
 >     ;pan = whiteNoise 'a' KR * 0.6
->     ;x = mouseX' KR 0 (bufDur KR b) Linear 0.1
+>     ;x = mouseX KR 0 (bufDur KR b) Linear 0.1
 >     ;pos = x + r}
 > in audition (out 0 (tGrains 2 clk b 1 pos dur pan 0.1 2))
 
 > let {b = 10
->     ;rt = mouseY' KR 2 120 Exponential 0.1
+>     ;rt = mouseY KR 2 120 Exponential 0.1
 >     ;dur = 1.2 / rt
 >     ;clk = impulse AR rt 0
->     ;pos = mouseX' KR 0 (bufDur KR b) Linear 0.1
+>     ;pos = mouseX KR 0 (bufDur KR b) Linear 0.1
 >     ;n0 = whiteNoise 'a' KR
 >     ;n1 = whiteNoise 'b' KR
 >     ;rate = shiftLeft 1.2 (roundTo (n0 * 3) 1)}
@@ -35,7 +35,7 @@
 
 Demand UGens as inputs (will eventually hang scsynth...)
 > let {b = 10
->     ;rt = mouseY' KR 2 100 Linear 0.2
+>     ;rt = mouseY KR 2 100 Linear 0.2
 >     ;d e = dwhite e 1 0.1 0.2
 >     ;z e0 e1 = drand e0 1 (mce [dgeom e0 (diwhite e0 1 20 40) 0.1 (1 + d e0)
 >                                ,dgeom e1 (diwhite e1 1 20 40) 1 (1 - d e1)])
diff --git a/Help/UGen/Oscillator/twChoose.help.lhs b/Help/UGen/Oscillator/twChoose.help.lhs
--- a/Help/UGen/Oscillator/twChoose.help.lhs
+++ b/Help/UGen/Oscillator/twChoose.help.lhs
@@ -5,7 +5,7 @@
 
 > import Sound.SC3.ID
 
-> let {x = mouseX' KR 1 1000 Exponential 0.1
+> let {x = mouseX KR 1 1000 Exponential 0.1
 >     ;d = dust 'a' AR x
 >     ;a = mce [sinOsc AR 220 0
 >              ,saw AR 440
diff --git a/Help/UGen/Oscillator/vibrato.help.lhs b/Help/UGen/Oscillator/vibrato.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/Oscillator/vibrato.help.lhs
@@ -0,0 +1,36 @@
+> Sound.SC3.UGen.Help.viewSC3Help "Vibrato"
+> Sound.SC3.UGen.DB.ugenSummary "Vibrato"
+
+> import Sound.SC3.ID
+
+vibrato at 1 Hz, note the use of DC.ar
+{SinOsc.ar(Vibrato.ar(DC.ar(400.0),1,0.02))*0.1}.play
+> let v = vibrato AR (dc AR 400) 1 0.02 0 0 0.04 0.1 0
+> in audition (out 0 (sinOsc AR v 0 * 0.1))
+
+compare: k-rate freq input can be a constant
+{SinOsc.ar(Vibrato.kr(400.0,1,0.02))}.play
+> let v = vibrato KR 400 1 0.02 0 0 0.04 0.1 0
+> in audition (out 0 (sinOsc AR v 0 * 0.1))
+
+control rate and rateVariation
+{x=MouseX.kr(2.0,100.0)
+;y=MouseY.kr(0.0,1.0)
+;v=Vibrato.ar(DC.ar(400.0),x,0.1,1.0,1.0,y,0.1)
+;SinOsc.ar(v)}.play
+> let {x = mouseX KR 2 100 Linear 0.2
+>     ;y = mouseY KR 0 1 Linear 0.2
+>     ;v = vibrato AR (dc AR 400) x 0.1 1 1 y 0.1 0}
+> in audition (out 0 (sinOsc AR v 0 * 0.1))
+
+control depth and depthVariation
+{n=LFNoise1.kr(1,3,7)
+;x=MouseX.kr(0.0,1.0)
+;y=MouseY.kr(0.0,1.0)
+;v=Vibrato.ar(DC.ar(400.0),n,x,1.0,1.0,y,0.1)
+;SinOsc.ar(v)}.play
+> let {n = lfNoise1 'a' KR 1 * 3 + 7
+>     ;x = mouseX KR 0 1 Linear 0.2
+>     ;y = mouseY KR 0 1 Linear 0.2
+>     ;v = vibrato AR (dc AR 400) n x 1 1 y 0.1 0}
+> in audition (out 0 (sinOsc AR v 0 * 0.1))
diff --git a/Help/UGen/Panner/balance2.help.lhs b/Help/UGen/Panner/balance2.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/Panner/balance2.help.lhs
@@ -0,0 +1,37 @@
+> Sound.SC3.UGen.Help.viewSC3Help "Balance2"
+> Sound.SC3.UGen.DB.ugenSummary "Balance2"
+
+> import Sound.SC3.ID
+
+{Balance2.ar(LFSaw.ar(44),Pulse.ar(33),FSinOsc.kr(0.5),0.1)}.play
+> let {l = lfSaw AR 44 0
+>     ;r = pulse AR 33 0.5
+>     ;p = fSinOsc KR 0.5 0
+>     ;o = balance2 l r p 0.1}
+> in audition (out 0 o)
+
+{var s=SinOsc.ar([440,550]);Balance2.ar(s[0],s[1],LFNoise0.kr(4),0.3)}.play
+> let {[s0,s1] = mceChannels (sinOsc AR (mce2 440 550) 0)
+>     ;n = lfNoise0 'a' KR 4
+>     ;o = balance2 s0 s1 n 0.3}
+> in audition (out 0 o)
+
+{var s=SinOsc.ar(440);Out.ar(0,0.2*Balance2.ar(s,s,SinOsc.kr(0.2)))}.play
+> let {s = sinOsc AR 440 0
+>     ;p = sinOsc KR 0.2 0
+>     ;o = balance2 s s p 1 * 0.2}
+> in audition (out 0 o) {- >> Sound.SC3.UGen.Dot.draw (out 0 o) -}
+
+{var s=SinOsc.ar(440);Out.ar(0,Balance2.ar(s,s,SinOsc.kr(0.2),0.2))}.play
+> let {s = sinOsc AR 440 0
+>     ;p = sinOsc KR 0.2 0
+>     ;o = balance2 s s p 0.2}
+> in audition (out 0 o) {- >> Sound.SC3.UGen.Dot.draw (out 0 o) -}
+
+> withSC3 (send (n_trace [-1]))
+
+{var s=SinOsc.ar(440);Out.ar(0,Balance2.ar(s,s,MouseX.kr(-1,1),0.2))}.play
+> let {s0 = sinOsc AR 440 0
+>     ;s1 = sinOsc AR 550 0
+>     ;x = mouseX KR (-1) 1 Linear 0.2}
+> in audition (out 0 (balance2 s0 s0 x 0.2))
diff --git a/Help/UGen/Panner/decodeB2.help.lhs b/Help/UGen/Panner/decodeB2.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/Panner/decodeB2.help.lhs
@@ -0,0 +1,12 @@
+> Sound.SC3.UGen.Help.viewSC3Help "DecodeB2"
+> Sound.SC3.UGen.DB.ugenSummary "DecodeB2"
+
+> import Sound.SC3.ID
+
+fails..., but so does sclang...
+> let {p = pinkNoise 'a' AR
+>     ;mx = mouseX KR (-1) 1 Linear 0.2
+>     ;my = mouseY KR 0 0.1 Linear 0.2
+>     ;[w,x,y] = mceChannels (panB2 p mx my)
+>     ;o = decodeB2 2 w x y 0.5}
+> in audition (out 0 o)
diff --git a/Help/UGen/Panner/pan2.help.lhs b/Help/UGen/Panner/pan2.help.lhs
--- a/Help/UGen/Panner/pan2.help.lhs
+++ b/Help/UGen/Panner/pan2.help.lhs
@@ -7,6 +7,6 @@
 > in audition (out 0 (pan2 n (fSinOsc KR 2 0) 0.3))
 
 > let {n = pinkNoise 'a' AR
->     ;x = mouseX' KR (-1) 1 Linear 0.2
->     ;y = mouseY' KR 0 1 Linear 0.2}
+>     ;x = mouseX KR (-1) 1 Linear 0.2
+>     ;y = mouseY KR 0 1 Linear 0.2}
 > in audition (out 0 (pan2 n x y))
diff --git a/Help/UGen/Panner/panAz.help.lhs b/Help/UGen/Panner/panAz.help.lhs
--- a/Help/UGen/Panner/panAz.help.lhs
+++ b/Help/UGen/Panner/panAz.help.lhs
@@ -1,5 +1,5 @@
-> Sound.SC3.UGen.Help.viewSC3Help "PanAZ"
-> Sound.SC3.UGen.DB.ugenSummary "PanAZ"
+> Sound.SC3.UGen.Help.viewSC3Help "PanAz"
+> Sound.SC3.UGen.DB.ugenSummary "PanAz"
 
 > import Sound.SC3.ID
 
diff --git a/Help/UGen/Panner/rotate2.help.lhs b/Help/UGen/Panner/rotate2.help.lhs
--- a/Help/UGen/Panner/rotate2.help.lhs
+++ b/Help/UGen/Panner/rotate2.help.lhs
@@ -11,5 +11,5 @@
 Rotation of stereo sound, via mouse.
 > let {x = mix (lfSaw AR (mce [198..201]) 0 * 0.1)
 >     ;y = sinOsc AR 900 0 * lfPulse KR 3 0 0.3 * 0.2
->     ;p = mouseX' KR 0 2 Linear 0.2}
+>     ;p = mouseX KR 0 2 Linear 0.2}
 > in audition (out 0 (rotate2 x y p))
diff --git a/Help/UGen/Panner/splay.help.lhs b/Help/UGen/Panner/splay.help.lhs
--- a/Help/UGen/Panner/splay.help.lhs
+++ b/Help/UGen/Panner/splay.help.lhs
@@ -9,8 +9,8 @@
 > let {i = 6
 >     ;r = map (\e -> rand e 10 20) (take i ['a'..])
 >     ;n = lfNoise2 'a' KR (mce r)
->     ;x = mouseX' KR (-1) 1 Linear 0.1
->     ;y = mouseY' KR 1 0 Linear 0.1
+>     ;x = mouseX KR (-1) 1 Linear 0.1
+>     ;y = mouseY KR 1 0 Linear 0.1
 >     ;ci = constant . fromIntegral
 >     ;f = mce [1 .. ci i] + 3 * 100
 >     ;o = sinOsc AR (n * 200 + f) 0}
@@ -28,16 +28,16 @@
 > in audition (out 0 (splay (sinOsc AR n 0) s l c True))
 
 full stereo
-> withSC3 (\fd -> send fd (n_set (-1) [("spread",1),("center",0)]))
+> withSC3 (send (n_set (-1) [("spread",1),("center",0)]))
 
 less wide
-> withSC3 (\fd -> send fd (n_set (-1) [("spread",0.5),("center",0)]))
+> withSC3 (send (n_set (-1) [("spread",0.5),("center",0)]))
 
 mono center
-> withSC3 (\fd -> send fd (n_set (-1) [("spread",0),("center",0)]))
+> withSC3 (send (n_set (-1) [("spread",0),("center",0)]))
 
 from center to right
-> withSC3 (\fd -> send fd (n_set (-1) [("spread",0.5),("center",0.5)]))
+> withSC3 (send (n_set (-1) [("spread",0.5),("center",0.5)]))
 
 all left
-> withSC3 (\fd -> send fd (n_set (-1) [("spread",0),("center",-1)]))
+> withSC3 (send (n_set (-1) [("spread",0),("center",-1)]))
diff --git a/Help/UGen/Trigger/inRange.help.lhs b/Help/UGen/Trigger/inRange.help.lhs
--- a/Help/UGen/Trigger/inRange.help.lhs
+++ b/Help/UGen/Trigger/inRange.help.lhs
@@ -5,6 +5,6 @@
 
 trigger noise burst
 > let {n = brownNoise 'α' AR * 0.1
->     ;x = mouseX' KR 1 2 Linear 0.1
+>     ;x = mouseX KR 1 2 Linear 0.1
 >     ;o = sinOsc KR x 0 * 0.2}
 > in audition (out 0 (inRange o (-0.15) 0.15 * n))
diff --git a/Help/UGen/Trigger/lastValue.help.lhs b/Help/UGen/Trigger/lastValue.help.lhs
--- a/Help/UGen/Trigger/lastValue.help.lhs
+++ b/Help/UGen/Trigger/lastValue.help.lhs
@@ -3,10 +3,10 @@
 
 > import Sound.SC3
 
-> let x = mouseX' KR 100 400 Linear 0.1
+> let x = mouseX KR 100 400 Linear 0.1
 > in audition (out 0 (sinOsc AR (lastValue x 40) 0 * 0.1))
 
 Difference between currrent and the last changed
-> let {x = mouseX' KR 0.1 4 Linear 0.1
+> let {x = mouseX KR 0.1 4 Linear 0.1
 >     ;f = abs (lastValue x 0.5 - x) * 400 + 200}
 > in audition (out 0 (sinOsc AR f 0 * 0.2))
diff --git a/Help/UGen/Trigger/mostChange.help.lhs b/Help/UGen/Trigger/mostChange.help.lhs
--- a/Help/UGen/Trigger/mostChange.help.lhs
+++ b/Help/UGen/Trigger/mostChange.help.lhs
@@ -4,6 +4,6 @@
 > import Sound.SC3.ID
 
 > let {n = lfNoise0 'α' KR 1
->     ;x = mouseX' KR 200 300 Linear 0.1
+>     ;x = mouseX KR 200 300 Linear 0.1
 >     ;f = mostChange (n * 400 + 900) x}
 > in audition (out 0 (sinOsc AR f 0 * 0.1))
diff --git a/Help/UGen/Trigger/phasor.help.lhs b/Help/UGen/Trigger/phasor.help.lhs
--- a/Help/UGen/Trigger/phasor.help.lhs
+++ b/Help/UGen/Trigger/phasor.help.lhs
@@ -4,7 +4,7 @@
 > import Sound.SC3
 
 phasor controls sine frequency, end frequency matches second sine.
-> let {rate = mouseX' KR 0.2 2 Exponential 0.1
+> let {rate = mouseX KR 0.2 2 Exponential 0.1
 >     ;tr = impulse AR rate 0
 >     ;sr = sampleRate
 >     ;x = phasor AR tr (rate / sr) 0 1 0
diff --git a/Help/UGen/Trigger/pulseCount.help.lhs b/Help/UGen/Trigger/pulseCount.help.lhs
--- a/Help/UGen/Trigger/pulseCount.help.lhs
+++ b/Help/UGen/Trigger/pulseCount.help.lhs
@@ -1,7 +1,14 @@
 > Sound.SC3.UGen.Help.viewSC3Help "PulseCount"
 > Sound.SC3.UGen.DB.ugenSummary "PulseCount"
 
-> import Sound.SC3
+> import Sound.SC3.ID
 
 > let c = pulseCount (impulse AR 10 0) (impulse AR 0.4 0)
 > in audition (out 0 (sinOsc AR (c * 200) 0 * 0.05))
+
+> let {m = maxLocalBufs 1
+>     ;b = mrg2 (localBuf 'α' 11 1) m
+>     ;t = impulse AR 10 0
+>     ;p = pulseCount t 0
+>     ;d = demand t 0 (dbufwr 'α' (-666) b p NoLoop)}
+> in audition (mrg [out 0 (dc AR 0),poll t p (label "p") 0])
diff --git a/Help/UGen/Trigger/runningMax.help.lhs b/Help/UGen/Trigger/runningMax.help.lhs
--- a/Help/UGen/Trigger/runningMax.help.lhs
+++ b/Help/UGen/Trigger/runningMax.help.lhs
@@ -9,6 +9,6 @@
 > in audition (out 0 (sinOsc AR f 0 * 0.2))
 
 follow a sine lfo, reset rate controlled by mouse x
-> let {t = impulse KR (mouseX' KR 0.01 2 Linear 0.1) 0
+> let {t = impulse KR (mouseX KR 0.01 2 Linear 0.1) 0
 >     ;f = runningMax (sinOsc KR 0.2 0) t * 500 + 200}
 > in audition (out 0 (sinOsc AR f 0 * 0.2))
diff --git a/Help/UGen/Trigger/runningMin.help.lhs b/Help/UGen/Trigger/runningMin.help.lhs
--- a/Help/UGen/Trigger/runningMin.help.lhs
+++ b/Help/UGen/Trigger/runningMin.help.lhs
@@ -5,7 +5,7 @@
 
 Follow a sine lfo, reset rate controlled by mouseX
 > let {o = sinOsc KR 2 0
->     ;x = mouseX' KR 0.01 10 Exponential 0.1
+>     ;x = mouseX KR 0.01 10 Exponential 0.1
 >     ;t = impulse AR x 0
 >     ;f = runningMin o t * 500 + 200 }
 > in audition (out 0 (sinOsc AR f 0 * 0.2))
diff --git a/Help/UGen/Trigger/sendReply.help.lhs b/Help/UGen/Trigger/sendReply.help.lhs
--- a/Help/UGen/Trigger/sendReply.help.lhs
+++ b/Help/UGen/Trigger/sendReply.help.lhs
@@ -6,11 +6,6 @@
 > let {s0 = lfNoise0 'a' KR 5
 >     ;s1 = lfNoise0 'b' KR 5
 >     ;o = sinOsc AR (s0 * 200 + 500) 0 * s1 * 0.1}
-> in audition (mrg [sendReply s0 0 "/s-reply" [s0,s1],out 0 o])
-
-> import Sound.OpenSoundControl
+> in audition (mrg [sendReply s0 0 "/send-reply" [s0,s1],out 0 o])
 
-> withSC3 (\fd -> do {async fd (notify True)
->                    ;r <- wait fd "/s-reply"
->                    ;putStrLn (show r)
->                    ;async fd (notify False)})
+> withSC3 (withNotifications (wait "/send-reply"))
diff --git a/Help/UGen/Trigger/sendTrig.help.lhs b/Help/UGen/Trigger/sendTrig.help.lhs
--- a/Help/UGen/Trigger/sendTrig.help.lhs
+++ b/Help/UGen/Trigger/sendTrig.help.lhs
@@ -7,10 +7,5 @@
 >     ;o = sinOsc AR (s * 200 + 500) 0 * 0.1}
 > in audition (mrg [sendTrig s 0 s,out 0 o])
 
-> import Sound.OpenSoundControl
-
 Retrieve a single message
-> withSC3 (\fd -> do {_ <- async fd (notify True)
->                    ;tr <- wait fd "/tr"
->                    ;putStrLn (show tr)
->                    ;async fd (notify False)})
+> withSC3 (withNotifications (wait "/tr"))
diff --git a/Help/UGen/Trigger/stepper.help.lhs b/Help/UGen/Trigger/stepper.help.lhs
--- a/Help/UGen/Trigger/stepper.help.lhs
+++ b/Help/UGen/Trigger/stepper.help.lhs
@@ -9,11 +9,12 @@
 
 Using Stepper and BufRd for sequencing
 > let {compose = foldl (flip (.)) id
->     ;rvb s =
->         let f i = let dly = mce [rand (i//'a') 0 0.5,rand (i//'b') 0 0.5]
->                   in allpassN i 0.05 dly (rand i 1.5 2)
+>     ;rvb z s =
+>         let f i = let dly = mce [rand (z `joinID` i `joinID` 'a') 0 0.5
+>                                 ,rand (z `joinID` i `joinID` 'b') 0 0.5]
+>                     in allpassN i 0.05 dly (rand i 1.5 2)
 >         in compose (replicate 5 f) s
->     ;stpr = let {rate = mouseX' KR 2 2.01 Exponential 0.1
+>     ;stpr = let {rate = mouseX KR 1.75 2.25 Exponential 0.1
 >                 ;clock = impulse KR rate 0
 >                 ;envl = decay2 clock 0.002 2.5
 >                 ;indx = stepper clock 0 0 15 1 0
@@ -25,17 +26,16 @@
 >                        ,\s -> rlpf s ffreq 0.3 * envl
 >                        ,\s -> s * 0.5
 >                        ,\s -> combL s 1 (0.66 / rate) 2 * 0.8 + s
->                        ,\s -> s + (rvb s * 0.3)
+>                        ,\s -> s + (rvb 'a' s * 0.3)
 >                        ,\s -> leakDC s 0.1
 >                        ,\s -> delayL s 0.1 lfo + s
 >                        ,\s -> onePole s 0.9]}
 >             in compose chn top
->     ;stprInit fd =
+>     ;stprInit =
 >      let n = [97.999,195.998,523.251,466.164,195.998
 >              ,233.082,87.307,391.995,87.307,261.626
 >              ,195.998,77.782,233.082,195.998,97.999
 >              ,155.563]
->      in do {_ <- async fd (b_alloc 10 128 1)
->            ;send fd (b_setn 10 [(0,n)])}}
-> in withSC3 (\fd -> do {stprInit fd
->                       ;audition (out 0 stpr)})
+>      in do {_ <- async (b_alloc 10 128 1)
+>            ;send (b_setn 10 [(0,n)])}}
+> in withSC3 (stprInit >> play (out 0 stpr))
diff --git a/Help/UGen/Trigger/sweep.help.lhs b/Help/UGen/Trigger/sweep.help.lhs
--- a/Help/UGen/Trigger/sweep.help.lhs
+++ b/Help/UGen/Trigger/sweep.help.lhs
@@ -4,32 +4,54 @@
 > import Sound.SC3.ID
 
 Using sweep to modulate sine frequency
-> let {x = mouseX' KR 0.5 20 Exponential 0.1
+> let {x = mouseX KR 0.5 20 Exponential 0.1
 >     ;t = impulse KR x 0
 >     ;f = sweep t 700 + 500}
 > in audition (out 0 (sinOsc AR f 0 * 0.2))
 
 Load audio to buffer
 > let fn = "/home/rohan/data/audio/pf-c5.aif"
-> in withSC3 (\fd -> send fd (b_allocRead 0 fn 0 0))
+> in withSC3 (send (b_allocRead 0 fn 0 0))
 
 Using sweep to index into a buffer
-> let {x = mouseX' KR 0.5 20 Exponential 0.1
+> let {x = mouseX KR 0.5 20 Exponential 0.1
 >     ;t = impulse AR x 0
 >     ;p = sweep t (bufSampleRate KR 0)}
 > in audition (out 0 (bufRdL 1 AR 0 p NoLoop))
 
 Backwards, variable offset
 > let {n = lfNoise0 'a' KR 15
->     ;x = mouseX' KR 0.5 10 Exponential 0.1
+>     ;x = mouseX KR 0.5 10 Exponential 0.1
 >     ;t = impulse AR x 0
 >     ;r = bufSampleRate KR 0
 >     ;p = sweep t (negate r) + (bufFrames KR 0 * n)}
 > in audition (out 0 (bufRdL 1 AR 0 p NoLoop))
 
 Raising rate
-> let {x = mouseX' KR 0.5 10 Exponential 0.1
+> let {x = mouseX KR 0.5 10 Exponential 0.1
 >     ;t = impulse AR x 0
 >     ;r = sweep t 2 + 0.5
 >     ;p = sweep t (bufSampleRate KR 0 * r)}
 > in audition (out 0 (bufRdL 1 AR 0 p NoLoop))
+
+f0 (sc-users, 2012-02-09)
+> let {lf = range 0.01 1.25 (lfNoise2 'a' KR 1)
+>     ;du = duty AR lf 0 DoNothing lf
+>     ;tr = abs (hpz1 du) >* 0
+>     ;ph = sweep tr (1/du)
+>     ;fr = linExp ph 0 1 400 800
+>     ;os = sinOsc AR fr 0 * 0.2}
+> in audition (out 0 os)
+
+line segments, set start & end values, transition time and trigger.
+continues past end point if not re-triggered.
+> let {tr = tr_control "tr" 0
+>     ;st = control KR "st" 440
+>     ;en = control KR "en" 880
+>     ;tm = control KR "tm" 2
+>     ;rt = ((en - st) / tm)
+>     ;sw = sweep tr rt + st}
+> in audition (out 0 (sinOsc AR sw 0 * 0.2))
+
+> withSC3 (send (n_set (-1) [("st",660),("en",550),("tm",4),("tr",1)]))
+> withSC3 (send (n_set (-1) [("st",110),("en",990),("tm",1),("tr",1)]))
diff --git a/Help/UGen/Trigger/timer.help.lhs b/Help/UGen/Trigger/timer.help.lhs
--- a/Help/UGen/Trigger/timer.help.lhs
+++ b/Help/UGen/Trigger/timer.help.lhs
@@ -3,5 +3,5 @@
 
 > import Sound.SC3
 
-> let t = impulse KR (mouseX' KR 0.5 20 Exponential 0.1) 0
+> let t = impulse KR (mouseX KR 0.5 20 Exponential 0.1) 0
 > in audition (out 0 (sinOsc AR (timer t * 500 + 500) 0 * 0.2))
diff --git a/Help/UGen/Wavelets/idwt.help.lhs b/Help/UGen/Wavelets/idwt.help.lhs
--- a/Help/UGen/Wavelets/idwt.help.lhs
+++ b/Help/UGen/Wavelets/idwt.help.lhs
@@ -10,8 +10,7 @@
 
 direct synthesis via writing values to buffer (try changing wavelet
 type...)
-> withSC3 (\fd -> do {_ <- async fd (b_alloc 10 1024 1)
->                    ;send fd (b_zero 10)})
+> withSC3 (async (b_alloc 10 1024 1) >> send (b_zero 10))
 
 > let {c = fftTrigger 10 0.5 0
 >     ;i = idwt c (-1) 0 0}
@@ -20,14 +19,14 @@
 > import Control.Monad.Random
 > import Sound.SC3.Lang.Random.Monad
 
-> withSC3 (\fd -> send fd (b_zero 10))
+> withSC3 (send (b_zero 10))
 
 run this to change sound: WARNING, NOISY!
 > do {a <- evalRandIO (nrrand 1024 (-1) 1)
->    ;withSC3 (\fd -> send fd (b_setn 10 [(0,a)]))}
+>    ;withSC3 (send (b_setn 10 [(0,a)]))}
 
 > let a = map (/ 1024) [0..1023]
-> in withSC3 (\fd -> send fd (b_setn 10 [(0,a)]))
+> in withSC3 (send (b_setn 10 [(0,a)]))
 
 > let a = map (\i -> 1 - i / 1024) [0..1023]
-> in withSC3 (\fd -> send fd (b_setn 10 [(0,a)]))
+> in withSC3 (send (b_setn 10 [(0,a)]))
diff --git a/Help/UGen/Wavelets/wt_FilterScale.help.lhs b/Help/UGen/Wavelets/wt_FilterScale.help.lhs
--- a/Help/UGen/Wavelets/wt_FilterScale.help.lhs
+++ b/Help/UGen/Wavelets/wt_FilterScale.help.lhs
@@ -6,6 +6,6 @@
 > let {i = whiteNoise 'α' AR * 0.2
 >     ;b = mrg2 (localBuf 'α' 2048 1) (maxLocalBufs 1)
 >     ;c = dwt b i 0.5 0 1 0 0
->     ;x = mouseX' KR (-1) 1 Linear 0.1
+>     ;x = mouseX KR (-1) 1 Linear 0.1
 >     ;c' = wt_FilterScale c x}
 > in audition (out 0 (pan2 (idwt c' 0 0 0) x 1))
diff --git a/Help/UGen/Wavelets/wt_TimeWipe.help.lhs b/Help/UGen/Wavelets/wt_TimeWipe.help.lhs
--- a/Help/UGen/Wavelets/wt_TimeWipe.help.lhs
+++ b/Help/UGen/Wavelets/wt_TimeWipe.help.lhs
@@ -6,6 +6,6 @@
 > let {i = whiteNoise 'α' AR * 0.2
 >     ;b = mrg2 (localBuf 'α' 2048 1) (maxLocalBufs 1)
 >     ;c = dwt b i 0.5 0 1 0 0
->     ;x = mouseX' KR 0 1 Linear 0.1
+>     ;x = mouseX KR 0 1 Linear 0.1
 >     ;c' = wt_TimeWipe c x}
 > in audition (out 0 (pan2 (idwt c' 0 0 0) (x * 2 - 1) 1))
diff --git a/README b/README
--- a/README
+++ b/README
@@ -1,23 +1,44 @@
 hsc3 - haskell supercollider
+----------------------------
 
-hsc3 provides Sound.SC3, a Haskell module that facilitates using
-Haskell as a client to the SuperCollider synthesis server.
+[hsc3][hsc3] provides Sound.SC3, a module that facilitates using
+[Haskell][hs] as a client to the [SuperCollider][sc3] synthesis
+server.  hsc3 requires [hosc](?t=hosc).
 
 For installation and configuration information please consult the
-tutorial file at http://slavepianos.org/rd/ut/hsc3-texts/
+[tutorial][tutorial] at [hsc3-texts][hsc3-texts].
 
-The hsc3 interaction environment is written for GNU Emacs.
+There are a number of related projects:
 
-  http://slavepianos.org/rd/?t=hsc3
-  http://haskell.org/
-  http://audiosynth.com/
-  http://gnu.org/software/emacs/
+- [hsc3-dot](?t=hsc3-dot): UGen Graph Drawing
+- [hsc3-graphs](?t=hsc3-graphs): UGen Graphs
+- [hsc3-lang](?t=hsc3-lang): SC3 Language
+- [hsc3-cairo](?t=hsc3-cairo) & [hsc3-plot](?t=hsc3-plot): Drawing & Plotting
+- [hsc3-rec](?t=hsc3-rec) & [hsc3-unsafe](?t=hsc3-rec): UGen Variants
+- [hsc3-db](?t=hsc3-db): UGen Database
 
-(c) rohan drape and others, 2006-2011
-    gpl, http://gnu.org/copyleft/
-    with contributions by henning thielemann
-                        & stefan kersten
-                        & alex mclean
-                        & brent yorgey
-                        & shae erisson
-    see darcs history for details
+The hsc3 interaction environment is written for [GNU][gnu]
+[Emacs][emacs].
+
+© [rohan drape][rd] and others, 2006-2012, [gpl][gpl].
+with contributions by:
+
+- henning thielemann
+- stefan kersten
+- alex mclean
+- brent yorgey
+- shae erisson
+
+see the [darcs][darcs] [history][hsc3-history] for details
+
+[rd]: http://rd.slavepianos.org/
+[hsc3]: http://rd.slavepianos.org/?t=hsc3
+[hs]: http://haskell.org/
+[sc3]: http://audiosynth.com/
+[tutorial]: http://rd.slavepianos.org/?t=hsc3-texts&l=lhs/hsc3-tutorial.lhs
+[hsc3-texts]: http://rd.slavepianos.org/?t=hsc3-texts
+[gnu]: http://gnu.org/
+[emacs]: http://gnu.org/software/emacs/
+[darcs]: http://darcs.net/
+[gpl]: http://gnu.org/copyleft/
+[hsc3-history]:  http://rd.slavepianos.org/r/d/darcsweb.cgi?r=hsc3
diff --git a/Sound/SC3.hs b/Sound/SC3.hs
--- a/Sound/SC3.hs
+++ b/Sound/SC3.hs
@@ -1,8 +1,5 @@
--- | Exports both "Sound.SC3.Server" and "Sound.SC3.UGen", however see
--- also "Sound.SC3.ID" and "Sound.SC3.Monadic".
-module Sound.SC3 (module Sound.SC3.Server
-                 ,module Sound.SC3.UGen) where
-
-import Sound.SC3.Server
-import Sound.SC3.UGen
+-- | Composite of "Sound.SC3.Server.Monad" and "Sound.SC3.UGen".
+module Sound.SC3 (module M) where
 
+import Sound.SC3.Server.Monad as M
+import Sound.SC3.UGen as M
diff --git a/Sound/SC3/FD.hs b/Sound/SC3/FD.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/FD.hs
@@ -0,0 +1,5 @@
+-- | Composite of "Sound.SC3.Server.FD" and "Sound.SC3.UGen".
+module Sound.SC3.FD (module M) where
+
+import Sound.SC3.Server.FD as M
+import Sound.SC3.UGen as M
diff --git a/Sound/SC3/ID.hs b/Sound/SC3/ID.hs
--- a/Sound/SC3/ID.hs
+++ b/Sound/SC3/ID.hs
@@ -1,12 +1,5 @@
--- | Module exporting all of "Sound.SC3" and also the explicit
--- identifier variants for non-deterministic and non-sharable unit
--- generators.
-module Sound.SC3.ID (module I) where
+-- | Composite of "Sound.SC3.UGen.ID" and "Sound.SC3.Server.Monad".
+module Sound.SC3.ID (module M) where
 
-import Sound.SC3.Identifier as I
-import Sound.SC3.UGen as I
-import Sound.SC3.UGen.Composite.ID as I
-import Sound.SC3.UGen.Demand.ID as I
-import Sound.SC3.UGen.FFT.ID as I
-import Sound.SC3.UGen.Noise.ID as I
-import Sound.SC3.Server as I
+import Sound.SC3.UGen.ID as M
+import Sound.SC3.Server.Monad as M
diff --git a/Sound/SC3/ID/FD.hs b/Sound/SC3/ID/FD.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/ID/FD.hs
@@ -0,0 +1,5 @@
+-- | Composite of "Sound.SC3.UGen.ID" and "Sound.SC3.Server.FD".
+module Sound.SC3.ID.FD (module M) where
+
+import Sound.SC3.UGen.ID as M
+import Sound.SC3.Server.FD as M
diff --git a/Sound/SC3/Identifier.hs b/Sound/SC3/Identifier.hs
deleted file mode 100644
--- a/Sound/SC3/Identifier.hs
+++ /dev/null
@@ -1,27 +0,0 @@
--- | Typeclass and functions to manage UGen identifiers.
-module Sound.SC3.Identifier where
-
-import Data.Char
-import qualified Data.Digest.Murmur32 as H
-
--- | Typeclass to constrain UGen identifiers.
-class ID a where
-    resolveID :: a -> Int
-
-instance ID Int where
-    resolveID = id
-
-instance ID Char where
-    resolveID = ord
-
--- | Hash 'ID' to 'Int'.
-idHash :: ID a => a -> Int
-idHash = fromIntegral . H.asWord32 . H.hash32 . resolveID
-
--- | Resolve the ID at 'i' and add the resolved enumeration of 'j'.
-editID :: (ID a, Enum b) => a -> b -> Int
-editID i j = resolveID i + fromEnum j
-
--- | Infix alias for editID
-(//) :: (ID a, Enum b) => a -> b -> Int
-(//) = editID
diff --git a/Sound/SC3/Monad.hs b/Sound/SC3/Monad.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/Monad.hs
@@ -0,0 +1,5 @@
+-- | Composite of "Sound.SC3.UGen.Monad" and "Sound.SC3.Server.Monad"
+module Sound.SC3.Monad (module M) where
+
+import Sound.SC3.UGen.Monad as M
+import Sound.SC3.Server.Monad as M
diff --git a/Sound/SC3/Monad/FD.hs b/Sound/SC3/Monad/FD.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/Monad/FD.hs
@@ -0,0 +1,5 @@
+-- | Composite of "Sound.SC3.UGen.Monad" and "Sound.SC3.Server.FD"
+module Sound.SC3.Monad.FD (module M) where
+
+import Sound.SC3.UGen.Monad as M
+import Sound.SC3.Server.FD as M
diff --git a/Sound/SC3/Monadic.hs b/Sound/SC3/Monadic.hs
deleted file mode 100644
--- a/Sound/SC3/Monadic.hs
+++ /dev/null
@@ -1,11 +0,0 @@
--- | Module exporting all of "Sound.SC3" and also the monadic
--- constructor variants for non-deterministic and non-sharable unit
--- generators.
-module Sound.SC3.Monadic (module M) where
-
-import Sound.SC3.UGen as M
-import Sound.SC3.UGen.Composite.Monadic as M
-import Sound.SC3.UGen.Demand.Monadic as M
-import Sound.SC3.UGen.FFT.Monadic as M
-import Sound.SC3.UGen.Noise.Monadic as M
-import Sound.SC3.Server as M
diff --git a/Sound/SC3/Server.hs b/Sound/SC3/Server.hs
--- a/Sound/SC3/Server.hs
+++ b/Sound/SC3/Server.hs
@@ -1,9 +1,10 @@
 -- | Collection of modules for communicating with the SuperCollider
---   synthesis server.
+-- synthesis server, see also "Sound.SC3.Server.FD" and
+-- "Sound.SC3.Server.Monad".
 module Sound.SC3.Server (module S) where
 
 import Sound.SC3.Server.Command as S
 import Sound.SC3.Server.Synthdef as S
-import Sound.SC3.Server.Play as S
+import Sound.SC3.Server.Synthdef.Type as S
 import Sound.SC3.Server.Status as S
 import Sound.SC3.Server.NRT as S
diff --git a/Sound/SC3/Server/Command.hs b/Sound/SC3/Server/Command.hs
--- a/Sound/SC3/Server/Command.hs
+++ b/Sound/SC3/Server/Command.hs
@@ -2,95 +2,98 @@
 --   synthesis server.
 module Sound.SC3.Server.Command where
 
-import Sound.OpenSoundControl
+import Data.Maybe
+import Sound.OpenSoundControl {- hosc -}
 import Sound.SC3.Server.Utilities
 import Sound.SC3.Server.Synthdef
+import Sound.SC3.Server.Synthdef.Type
+import Sound.SC3.UGen.Enum
 
 -- * Instrument definition commands
 
 -- | Install a bytecode instrument definition. (Asynchronous)
-d_recv :: Synthdef -> OSC
+d_recv :: Synthdef -> Message
 d_recv d = message "/d_recv" [Blob (synthdefData d)]
 
 -- | Load an instrument definition from a named file. (Asynchronous)
-d_load :: String -> OSC
+d_load :: String -> Message
 d_load p = message "/d_load" [String p]
 
 -- | Load a directory of instrument definitions files. (Asynchronous)
-d_loadDir :: String -> OSC
+d_loadDir :: String -> Message
 d_loadDir p = message "/d_loadDir" [String p]
 
 -- | Remove definition once all nodes using it have ended.
-d_free :: [String] -> OSC
+d_free :: [String] -> Message
 d_free = message "/d_free" . map String
 
 -- * Node commands
 
 -- | Place a node after another.
-n_after :: [(Int, Int)] -> OSC
+n_after :: [(Int, Int)] -> Message
 n_after = message "/n_after" . mk_duples Int Int
 
 -- | Place a node before another.
-n_before :: [(Int, Int)] -> OSC
+n_before :: [(Int, Int)] -> Message
 n_before = message "/n_before" . mk_duples Int Int
 
 -- | Fill ranges of a node's control values.
-n_fill :: Int -> [(String, Int, Double)] -> OSC
+n_fill :: Int -> [(String, Int, Double)] -> Message
 n_fill nid l = message "/n_fill" (Int nid : mk_triples String Int Float l)
 
 -- | Delete a node.
-n_free :: [Int] -> OSC
+n_free :: [Int] -> Message
 n_free = message "/n_free" . map Int
 
 -- | Map a node's controls to read from a bus.
-n_map :: Int -> [(String, Int)] -> OSC
+n_map :: Int -> [(String, Int)] -> Message
 n_map nid l = message "/n_map" (Int nid : mk_duples String Int l)
 
 -- | Map a node's controls to read from buses.
-n_mapn :: Int -> [(String, Int, Int)] -> OSC
+n_mapn :: Int -> [(String, Int, Int)] -> Message
 n_mapn nid l = message "/n_mapn" (Int nid : mk_triples String Int Int l)
 
 -- | Map a node's controls to read from an audio bus.
-n_mapa :: Int -> [(String, Int)] -> OSC
+n_mapa :: Int -> [(String, Int)] -> Message
 n_mapa nid l = message "/n_mapa" (Int nid : mk_duples String Int l)
 
 -- | Map a node's controls to read from audio buses.
-n_mapan :: Int -> [(String, Int, Int)] -> OSC
+n_mapan :: Int -> [(String, Int, Int)] -> Message
 n_mapan nid l = message "/n_mapan" (Int nid : mk_triples String Int Int l)
 
 -- | Get info about a node.
-n_query :: [Int] -> OSC
+n_query :: [Int] -> Message
 n_query = message "/n_query" . map Int
 
 -- | Turn node on or off.
-n_run :: [(Int, Bool)] -> OSC
+n_run :: [(Int, Bool)] -> Message
 n_run = message "/n_run" . mk_duples Int (Int . fromEnum)
 
 -- | Set a node's control values.
-n_set :: Int -> [(String, Double)] -> OSC
+n_set :: Int -> [(String, Double)] -> Message
 n_set nid c = message "/n_set" (Int nid : mk_duples String Float c)
 
 -- | Set ranges of a node's control values.
-n_setn :: Int -> [(String, [Double])] -> OSC
+n_setn :: Int -> [(String, [Double])] -> Message
 n_setn nid l = message "/n_setn" (Int nid : concatMap f l)
     where f (s,d) = String s : Int (length d) : map Float d
 
 -- | Trace a node.
-n_trace :: [Int] -> OSC
+n_trace :: [Int] -> Message
 n_trace = message "/n_trace" . map Int
 
 -- | Move an ordered sequence of nodes.
-n_order :: AddAction -> Int -> [Int] -> OSC
+n_order :: AddAction -> Int -> [Int] -> Message
 n_order a n ns = message "/n_order" (Int (fromEnum a) : Int n : map Int ns)
 
 -- * Synthesis node commands
 
 -- | Get control values.
-s_get :: Int -> [String] -> OSC
+s_get :: Int -> [String] -> Message
 s_get nid i = message "/s_get" (Int nid : map String i)
 
 -- | Get ranges of control values.
-s_getn :: Int -> [(String, Int)] -> OSC
+s_getn :: Int -> [(String, Int)] -> Message
 s_getn nid l = message "/s_getn" (Int nid : mk_duples String Int l)
 
 -- | Enumeration of possible locations to add new nodes (s_new and g_new).
@@ -102,37 +105,37 @@
                  deriving (Eq, Show, Enum)
 
 -- | Create a new synth.
-s_new :: String -> Int -> AddAction -> Int -> [(String, Double)] -> OSC
+s_new :: String -> Int -> AddAction -> Int -> [(String, Double)] -> Message
 s_new n i a t c = message "/s_new" (String n : Int i : Int (fromEnum a) : Int t : mk_duples String Float c)
 
 -- | Auto-reassign synth's ID to a reserved value.
-s_noid :: [Int] -> OSC
+s_noid :: [Int] -> Message
 s_noid = message "/s_noid" . map Int
 
 -- * Group node commands
 
 -- | Free all synths in this group and all its sub-groups.
-g_deepFree :: [Int] -> OSC
+g_deepFree :: [Int] -> Message
 g_deepFree = message "/g_deepFree" . map Int
 
 -- | Delete all nodes in a group.
-g_freeAll :: [Int] -> OSC
+g_freeAll :: [Int] -> Message
 g_freeAll = message "/g_freeAll" . map Int
 
 -- | Add node to head of group.
-g_head :: [(Int, Int)] -> OSC
+g_head :: [(Int, Int)] -> Message
 g_head = message "/g_head" . mk_duples Int Int
 
 -- | Create a new group.
-g_new :: [(Int, AddAction, Int)] -> OSC
+g_new :: [(Int, AddAction, Int)] -> Message
 g_new = message "/g_new" . mk_triples Int (Int . fromEnum) Int
 
 -- | Add node to tail of group.
-g_tail :: [(Int, Int)] -> OSC
+g_tail :: [(Int, Int)] -> Message
 g_tail = message "/g_tail" . mk_duples Int Int
 
 -- | Post a representation of a group's node subtree, optionally including the current control values for synths.
-g_dumpTree :: [(Int, Bool)] -> OSC
+g_dumpTree :: [(Int, Bool)] -> Message
 g_dumpTree = message "/g_dumpTree" . mk_duples Int (Int . fromEnum)
 
 -- | Request a representation of a group's node subtree, optionally including the current control values for synths.
@@ -160,122 +163,144 @@
 -- > ] * the number of nodes in the subtree
 --
 -- N.B. The order of nodes corresponds to their execution order on the server. Thus child nodes (those contained within a group) are listed immediately following their parent.
-g_queryTree :: [(Int, Bool)] -> OSC
+g_queryTree :: [(Int, Bool)] -> Message
 g_queryTree = message "/g_queryTree" . mk_duples Int (Int . fromEnum)
 
 -- | Create a new parallel group (supernova specific).
-p_new :: [(Int, AddAction, Int)] -> OSC
+p_new :: [(Int, AddAction, Int)] -> Message
 p_new = message "/p_new" . mk_triples Int (Int . fromEnum) Int
 
 -- * Plugin commands
 
 -- | Send a plugin command.
-cmd :: String -> [Datum] -> OSC
+cmd :: String -> [Datum] -> Message
 cmd name = message "/cmd" . (String name :)
 
 -- * Unit Generator commands
 
 -- | Send a command to a unit generator.
-u_cmd :: Int -> Int -> String -> [Datum] -> OSC
+u_cmd :: Int -> Int -> String -> [Datum] -> Message
 u_cmd nid uid name arg = message "/u_cmd" ([Int nid, Int uid, String name] ++ arg)
 
 -- * Buffer commands
 
 -- | Allocates zero filled buffer to number of channels and samples. (Asynchronous)
-b_alloc :: Int -> Int -> Int -> OSC
+b_alloc :: Int -> Int -> Int -> Message
 b_alloc nid frames channels = message "/b_alloc" [Int nid, Int frames, Int channels]
 
 -- | Allocate buffer space and read a sound file. (Asynchronous)
-b_allocRead :: Int -> String -> Int -> Int -> OSC
+b_allocRead :: Int -> String -> Int -> Int -> Message
 b_allocRead nid p f n = message "/b_allocRead" [Int nid, String p, Int f, Int n]
 
 -- | Allocate buffer space and read a sound file, picking specific channels. (Asynchronous)
-b_allocReadChannel :: Int -> String -> Int -> Int -> [Int] -> OSC
+b_allocReadChannel :: Int -> String -> Int -> Int -> [Int] -> Message
 b_allocReadChannel nid p f n cs = message "/b_allocReadChannel" ([Int nid, String p, Int f, Int n] ++ map Int cs)
 
 -- | Close attached soundfile and write header information. (Asynchronous)
-b_close :: Int -> OSC
+b_close :: Int -> Message
 b_close nid = message "/b_close" [Int nid]
 
 -- | Fill ranges of sample values.
-b_fill :: Int -> [(Int, Int, Double)] -> OSC
+b_fill :: Int -> [(Int, Int, Double)] -> Message
 b_fill nid l = message "/b_fill" (Int nid : mk_triples Int Int Float l)
 
 -- | Free buffer data. (Asynchronous)
-b_free :: Int -> OSC
+b_free :: Int -> Message
 b_free nid = message "/b_free" [Int nid]
 
 -- | Call a command to fill a buffer.  (Asynchronous)
-b_gen :: Int -> String -> [Double] -> OSC
-b_gen bid name arg = message "/b_gen" (Int bid : String name : map Float arg)
+b_gen :: Int -> String -> [Datum] -> Message
+b_gen bid name arg = message "/b_gen" (Int bid : String name : arg)
 
+-- | Call @sine1@ 'b_gen' command.
+b_gen_sine1 :: Int -> [B_Gen] -> [Double] -> Message
+b_gen_sine1 z f n = b_gen z "sine1" (Int (b_gen_flag f) : map Float n)
+
+-- | Call @sine2@ 'b_gen' command.
+b_gen_sine2 :: Int -> [B_Gen] -> [(Double,Double)] -> Message
+b_gen_sine2 z f n = b_gen z "sine2" (Int (b_gen_flag f) : mk_duples Float Float n)
+
+-- | Call @sine3@ 'b_gen' command.
+b_gen_sine3 :: Int -> [B_Gen] -> [(Double,Double,Double)] -> Message
+b_gen_sine3 z f n = b_gen z "sine3" (Int (b_gen_flag f) : mk_triples Float Float Float n)
+
+-- | Call @cheby@ 'b_gen' command.
+b_gen_cheby :: Int -> [B_Gen] -> [Double] -> Message
+b_gen_cheby z f n = b_gen z "cheby" (Int (b_gen_flag f) : map Float n)
+
+-- | Call @copy@ 'b_gen' command.
+b_gen_copy :: Int -> Int -> Int -> Int -> Maybe Int -> Message
+b_gen_copy z dst_ix src_b src_ix nf =
+    let nf' = fromMaybe (-1) nf
+    in b_gen z "copy" (map Int [dst_ix,src_b,src_ix,nf'])
+
 -- | Get sample values.
-b_get :: Int -> [Int] -> OSC
+b_get :: Int -> [Int] -> Message
 b_get nid i = message "/b_get" (Int nid : map Int i)
 
 -- | Get ranges of sample values.
-b_getn :: Int -> [(Int, Int)] -> OSC
+b_getn :: Int -> [(Int, Int)] -> Message
 b_getn nid l = message "/b_getn" (Int nid : mk_duples Int Int l)
 
 -- | Request \/b_info messages.
-b_query :: [Int] -> OSC
+b_query :: [Int] -> Message
 b_query = message "/b_query" . map Int
 
 -- | Read sound file data into an existing buffer. (Asynchronous)
-b_read :: Int -> String -> Int -> Int -> Int -> Bool -> OSC
+b_read :: Int -> String -> Int -> Int -> Int -> Bool -> Message
 b_read nid p f n f' z = message "/b_read" [Int nid, String p, Int f, Int n, Int f', Int (fromEnum z)]
 
 -- | Read sound file data into an existing buffer, picking specific channels. (Asynchronous)
-b_readChannel :: Int -> String -> Int -> Int -> Int -> Bool -> [Int] -> OSC
+b_readChannel :: Int -> String -> Int -> Int -> Int -> Bool -> [Int] -> Message
 b_readChannel nid p f n f' z cs = message "/b_readChannel" ([Int nid, String p, Int f, Int n, Int f', Int (fromEnum z)] ++ map Int cs)
 
 -- | Set sample values.
-b_set :: Int -> [(Int, Double)] -> OSC
+b_set :: Int -> [(Int, Double)] -> Message
 b_set nid l = message "/b_set" (Int nid : mk_duples Int Float l)
 
 -- | Set ranges of sample values.
-b_setn :: Int -> [(Int, [Double])] -> OSC
+b_setn :: Int -> [(Int, [Double])] -> Message
 b_setn nid l = message "/b_setn" (Int nid : concatMap f l)
     where f (i,d) = Int i : Int (length d) : map Float d
 
 -- | Write sound file data. (Asynchronous)
-b_write :: Int -> String -> String -> String -> Int -> Int -> Bool -> OSC
+b_write :: Int -> String -> String -> String -> Int -> Int -> Bool -> Message
 b_write nid p h t f s z = message "/b_write" [Int nid, String p, String h, String t, Int f, Int s, Int (fromEnum z)]
 
 -- | Zero sample data. (Asynchronous)
-b_zero :: Int -> OSC
+b_zero :: Int -> Message
 b_zero nid = message "/b_zero" [Int nid]
 
 -- * Control bus commands
 
 -- |  Fill ranges of bus values.
-c_fill :: [(Int, Int, Double)] -> OSC
+c_fill :: [(Int, Int, Double)] -> Message
 c_fill = message "/c_fill" . mk_triples Int Int Float
 
 -- | Get bus values.
-c_get :: [Int] -> OSC
+c_get :: [Int] -> Message
 c_get = message "/c_get" . map Int
 
 -- | Get ranges of bus values.
-c_getn :: [(Int, Int)] -> OSC
+c_getn :: [(Int, Int)] -> Message
 c_getn = message "/c_getn" . mk_duples Int Int
 
 -- | Set bus values.
-c_set :: [(Int, Double)] -> OSC
+c_set :: [(Int, Double)] -> Message
 c_set = message "/c_set" . mk_duples Int Float
 
 -- | Set ranges of bus values.
-c_setn :: [(Int, [Double])] -> OSC
+c_setn :: [(Int, [Double])] -> Message
 c_setn l = message "/c_setn" (concatMap f l)
     where f (i,d) = Int i : Int (length d) : map Float d
 
 -- * Server operation commands
 
 -- | Remove all bundles from the scheduling queue.
-clearSched :: OSC
+clearSched :: Message
 clearSched = message "/clearSched" []
 
--- | Enumeration of OSC printer types.
+-- | Enumeration of Message printer types.
 data PrintLevel = NoPrinter
                 | TextPrinter
                 | HexPrinter
@@ -283,23 +308,23 @@
                   deriving (Eq, Show, Enum)
 
 -- | Select printing of incoming Open Sound Control messages.
-dumpOSC :: PrintLevel -> OSC
-dumpOSC c = message "/dumpOSC" [Int (fromEnum c)]
+dumpMessage :: PrintLevel -> Message
+dumpMessage c = message "/dumpMessage" [Int (fromEnum c)]
 
 -- | Select reception of notification messages. (Asynchronous)
-notify :: Bool -> OSC
+notify :: Bool -> Message
 notify c = message "/notify" [Int (fromEnum c)]
 
 -- | Stop synthesis server.
-quit :: OSC
+quit :: Message
 quit = message "/quit" []
 
 -- | Request \/status.reply message.
-status :: OSC
+status :: Message
 status = message "/status" []
 
 -- | Request \/synced message when all current asynchronous commands complete.
-sync :: Int -> OSC
+sync :: Int -> Message
 sync sid = message "/sync" [Int sid]
 
 -- | Error posting scope.
@@ -313,7 +338,7 @@
                  deriving (Eq, Show, Enum)
 
 -- | Set error posting scope and mode.
-errorMode :: ErrorScope -> ErrorMode -> OSC
+errorMode :: ErrorScope -> ErrorMode -> Message
 errorMode scope mode = message "/error" [Int e]
     where e = case scope of
                 Globally -> fromEnum mode
@@ -322,26 +347,34 @@
 -- * Variants to simplify common cases
 
 -- | Pre-allocate for b_setn1, values preceding offset are zeroed.
-b_alloc_setn1 :: Int -> Int -> [Double] -> OSC
+b_alloc_setn1 :: Int -> Int -> [Double] -> Message
 b_alloc_setn1 nid i xs =
     let k = i + length xs
         xs' = replicate i 0 ++ xs
     in withCM (b_alloc nid k 1) (b_setn1 nid 0 xs')
 
+-- | Get ranges of sample values.
+b_getn1 :: Int -> (Int,Int) -> Message
+b_getn1 nid = b_getn nid . return
+
 -- | Set single sample value.
-b_set1 :: Int -> Int -> Double -> OSC
+b_set1 :: Int -> Int -> Double -> Message
 b_set1 nid i x = b_set nid [(i,x)]
 
 -- | Set a range of sample values.
-b_setn1 :: Int -> Int -> [Double] -> OSC
+b_setn1 :: Int -> Int -> [Double] -> Message
 b_setn1 nid i xs = b_setn nid [(i,xs)]
 
+-- | Variant on 'b_query'.
+b_query1 :: Int -> Message
+b_query1 = b_query . return
+
 -- | Set single bus values.
-c_set1 :: Int -> Double -> OSC
+c_set1 :: Int -> Double -> Message
 c_set1 i x = c_set [(i, x)]
 
 -- | Set a signle node control value.
-n_set1 :: Int -> String -> Double -> OSC
+n_set1 :: Int -> String -> Double -> Message
 n_set1 nid k n = n_set nid [(k, n)]
 
 -- * Modify existing message to include completion message
@@ -365,21 +398,47 @@
     ,"/quit"
     ,"/sync"]
 
--- | 'True' if 'OSC' is an asynchronous 'Message'.
-isAsync :: OSC -> Bool
-isAsync o =
-    case o of
-      Message a _ -> a `elem` async_cmds
-      Bundle _ _ -> error "isAsync: bundle"
+-- | 'True' if 'Message' is an asynchronous 'Message'.
+--
+-- > map isAsync [b_close 0,n_set1 0 "0" 0] == [True,False]
+isAsync :: Message -> Bool
+isAsync (Message a _) = a `elem` async_cmds
 
 -- | Add a completion message to an existing asynchronous command.
-withCM :: OSC -> OSC -> OSC
+--
+-- > let {m = n_set1 0 "0" 0
+-- >     ;m' = encodeMessage m}
+-- > in withCM (b_close 0) m == Message "/b_close" [Int 0,Blob m']
+withCM :: Message -> Message -> Message
 withCM (Message c xs) cm =
     if c `elem` async_cmds
-    then let xs' = xs ++ [Blob (encodeOSC cm)]
+    then let xs' = xs ++ [Blob (encodeMessage cm)]
          in message c xs'
     else error ("withCM: not async: " ++ c)
-withCM _ _ = error "withCM: not message"
+
+-- * Buffer segmentation and indices
+
+-- | Segment a request for /m/ places into sets of at most /n/.
+--
+-- > b_segment 1024 2056 == [8,1024,1024]
+-- > b_segment 1 5 == replicate 5 1
+b_segment :: Int -> Int -> [Int]
+b_segment n m =
+    let (q,r) = m `quotRem` n
+        s = replicate q n
+    in if r == 0 then s else r : s
+
+-- | Variant of 'b_segment' that takes a starting index and returns
+-- /(index,size)/ duples.
+--
+-- > b_indices 1 5 0 == zip [0..4] (replicate 5 1)
+-- > b_indices 1024 2056 16 == [(16,8),(24,1024),(1048,1024)]
+b_indices :: Int -> Int -> Int -> [(Int,Int)]
+b_indices n m k =
+    let dx_d = scanl1 (+)
+        s = b_segment n m
+        i = 0 : dx_d s
+    in zip (map (+ k) i) s
 
 -- Local Variables:
 -- truncate-lines:t
diff --git a/Sound/SC3/Server/Command/Completion.hs b/Sound/SC3/Server/Command/Completion.hs
--- a/Sound/SC3/Server/Command/Completion.hs
+++ b/Sound/SC3/Server/Command/Completion.hs
@@ -26,58 +26,59 @@
 
 import Sound.OpenSoundControl
 import Sound.SC3.Server.Synthdef
+import Sound.SC3.Server.Synthdef.Type
 
 -- Encode an OSC packet as an OSC blob.
-encode_osc_blob :: OSC -> Datum
-encode_osc_blob = Blob . encodeOSC
+encode_message_blob :: Message -> Datum
+encode_message_blob = Blob . encodeMessage
 
 -- | Install a bytecode instrument definition. (Asynchronous)
-d_recv' :: OSC -> Synthdef -> OSC
-d_recv' osc d = message "/d_recv" [Blob (synthdefData d), encode_osc_blob osc]
+d_recv' :: Message -> Synthdef -> Message
+d_recv' osc d = message "/d_recv" [Blob (synthdefData d), encode_message_blob osc]
 
 -- | Load an instrument definition from a named file. (Asynchronous)
-d_load' :: OSC -> String -> OSC
-d_load' osc p = message "/d_load" [String p, encode_osc_blob osc]
+d_load' :: Message -> String -> Message
+d_load' osc p = message "/d_load" [String p, encode_message_blob osc]
 
 -- | Load a directory of instrument definitions files. (Asynchronous)
-d_loadDir' :: OSC -> String -> OSC
-d_loadDir' osc p = message "/d_loadDir" [String p, encode_osc_blob osc]
+d_loadDir' :: Message -> String -> Message
+d_loadDir' osc p = message "/d_loadDir" [String p, encode_message_blob osc]
 
 -- | Allocates zero filled buffer to number of channels and samples. (Asynchronous)
-b_alloc' :: OSC -> Int -> Int -> Int -> OSC
-b_alloc' osc nid frames channels = message "/b_alloc" [Int nid, Int frames, Int channels, encode_osc_blob osc]
+b_alloc' :: Message -> Int -> Int -> Int -> Message
+b_alloc' osc nid frames channels = message "/b_alloc" [Int nid, Int frames, Int channels, encode_message_blob osc]
 
 -- | Allocate buffer space and read a sound file. (Asynchronous)
-b_allocRead' :: OSC -> Int -> String -> Int -> Int -> OSC
-b_allocRead' osc nid p f n = message "/b_allocRead" [Int nid, String p, Int f, Int n, encode_osc_blob osc]
+b_allocRead' :: Message -> Int -> String -> Int -> Int -> Message
+b_allocRead' osc nid p f n = message "/b_allocRead" [Int nid, String p, Int f, Int n, encode_message_blob osc]
 
 -- | Allocate buffer space and read a sound file, picking specific channels. (Asynchronous)
-b_allocReadChannel' :: OSC -> Int -> String -> Int -> Int -> [Int] -> OSC
-b_allocReadChannel' osc nid p f n cs = message "/b_allocReadChannel" ([Int nid, String p, Int f, Int n] ++ map Int cs ++ [encode_osc_blob osc])
+b_allocReadChannel' :: Message -> Int -> String -> Int -> Int -> [Int] -> Message
+b_allocReadChannel' osc nid p f n cs = message "/b_allocReadChannel" ([Int nid, String p, Int f, Int n] ++ map Int cs ++ [encode_message_blob osc])
 
 -- | Free buffer data. (Asynchronous)
-b_free' :: OSC -> Int -> OSC
-b_free' osc nid = message "/b_free" [Int nid, encode_osc_blob osc]
+b_free' :: Message -> Int -> Message
+b_free' osc nid = message "/b_free" [Int nid, encode_message_blob osc]
 
 -- | Close attached soundfile and write header information. (Asynchronous)
-b_close' :: OSC -> Int -> OSC
-b_close' osc nid = message "/b_close" [Int nid, encode_osc_blob osc]
+b_close' :: Message -> Int -> Message
+b_close' osc nid = message "/b_close" [Int nid, encode_message_blob osc]
 
 -- | Read sound file data into an existing buffer. (Asynchronous)
-b_read' :: OSC -> Int -> String -> Int -> Int -> Int -> Bool -> OSC
-b_read' osc nid p f n f' z = message "/b_read" [Int nid, String p, Int f, Int n, Int f', Int (fromEnum z), encode_osc_blob osc]
+b_read' :: Message -> Int -> String -> Int -> Int -> Int -> Bool -> Message
+b_read' osc nid p f n f' z = message "/b_read" [Int nid, String p, Int f, Int n, Int f', Int (fromEnum z), encode_message_blob osc]
 
 -- | Read sound file data into an existing buffer. (Asynchronous)
-b_readChannel' :: OSC -> Int -> String -> Int -> Int -> Int -> Bool -> [Int] -> OSC
-b_readChannel' osc nid p f n f' z cs = message "/b_readChannel" ([Int nid, String p, Int f, Int n, Int f', Int (fromEnum z)] ++ map Int cs ++ [encode_osc_blob osc])
+b_readChannel' :: Message -> Int -> String -> Int -> Int -> Int -> Bool -> [Int] -> Message
+b_readChannel' osc nid p f n f' z cs = message "/b_readChannel" ([Int nid, String p, Int f, Int n, Int f', Int (fromEnum z)] ++ map Int cs ++ [encode_message_blob osc])
 
 -- | Write sound file data. (Asynchronous)
-b_write' :: OSC -> Int -> String -> String -> String -> Int -> Int -> Bool -> OSC
-b_write' osc nid p h t f s z = message "/b_write" [Int nid, String p, String h, String t, Int f, Int s, Int (fromEnum z), encode_osc_blob osc]
+b_write' :: Message -> Int -> String -> String -> String -> Int -> Int -> Bool -> Message
+b_write' osc nid p h t f s z = message "/b_write" [Int nid, String p, String h, String t, Int f, Int s, Int (fromEnum z), encode_message_blob osc]
 
 -- | Zero sample data. (Asynchronous)
-b_zero' :: OSC -> Int -> OSC
-b_zero' osc nid = message "/b_zero" [Int nid, encode_osc_blob osc]
+b_zero' :: Message -> Int -> Message
+b_zero' osc nid = message "/b_zero" [Int nid, encode_message_blob osc]
 
 -- Local Variables:
 -- truncate-lines:t
diff --git a/Sound/SC3/Server/FD.hs b/Sound/SC3/Server/FD.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/Server/FD.hs
@@ -0,0 +1,5 @@
+-- | Composite of "Sound.SC3.Server" and "Sound.SC3.Server.Transport.FD".
+module Sound.SC3.Server.FD (module M) where
+
+import Sound.SC3.Server as M
+import Sound.SC3.Server.Transport.FD as M
diff --git a/Sound/SC3/Server/Help.hs b/Sound/SC3/Server/Help.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/Server/Help.hs
@@ -0,0 +1,30 @@
+-- | Functions to provide mediated access to the SC3 help system.
+module Sound.SC3.Server.Help where
+
+import Control.Monad
+import Sound.SC3.UGen.Help
+import System.Cmd {- process -}
+import System.FilePath {- filepath -}
+
+-- | Generate path to indicated SC3 instance method help.
+--
+-- > let r = "./Reference/Server-Command-Reference.html#/b_alloc"
+-- > in sc3HelpServerCommand "." "b_alloc" == r
+sc3_server_command_ref :: FilePath -> String -> FilePath
+sc3_server_command_ref d c =
+    let f = d </> "Reference/Server-Command-Reference.html"
+        c' = case c of
+               '/':_ -> c
+               _ -> '/':c
+    in f ++ '#':c'
+
+-- | Lookup @SC3@ help file for server command `c'.
+--
+-- > Sound.SC3.Server.Help.viewServerHelp "/b_allocRead"
+-- > viewServerHelp "done"
+viewServerHelp :: String -> IO ()
+viewServerHelp c = do
+  d <- sc3HelpDirectory
+  let nm = sc3_server_command_ref d c
+  br <- get_env_default "BROWSER" "x-www-browser"
+  void (rawSystem br ["file://" ++ nm])
diff --git a/Sound/SC3/Server/Monad.hs b/Sound/SC3/Server/Monad.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/Server/Monad.hs
@@ -0,0 +1,5 @@
+-- | Composite of "Sound.SC3.Server" and "Sound.SC3.Server.Transport.Monad".
+module Sound.SC3.Server.Monad (module S) where
+
+import Sound.SC3.Server as S
+import Sound.SC3.Server.Transport.Monad as S
diff --git a/Sound/SC3/Server/NRT.hs b/Sound/SC3/Server/NRT.hs
--- a/Sound/SC3/Server/NRT.hs
+++ b/Sound/SC3/Server/NRT.hs
@@ -1,27 +1,103 @@
 -- | Non-realtime score generation.
-module Sound.SC3.Server.NRT (encodeNRT
-                            ,writeNRT
-                            ,putNRT ) where
+module Sound.SC3.Server.NRT where
 
-import qualified Data.ByteString.Lazy as B
-import Sound.OpenSoundControl
+import qualified Data.ByteString.Lazy as B {- bytestring -}
+import Data.Maybe
+import Sound.OpenSoundControl {- hosc -}
 import Sound.OpenSoundControl.Coding.Byte
+import System.Exit
 import System.IO
+import System.Process {- process -}
 
 -- | Encode and prefix with encoded length.
-oscWithSize :: OSC -> B.ByteString
-oscWithSize o = B.append l b
-    where b = encodeOSC o
-          l = encode_i32 (fromIntegral (B.length b))
+oscWithSize :: Bundle -> B.ByteString
+oscWithSize o =
+    let b = encodeBundle o
+        l = encode_i32 (fromIntegral (B.length b))
+    in B.append l b
 
--- | Encode a list of OSC bundles as an NRT score.
-encodeNRT :: [OSC] -> B.ByteString
-encodeNRT = B.concat . map oscWithSize
+-- | An 'NRT' score is a sequence of 'Bundle's.
+data NRT = NRT {nrt_bundles :: [Bundle]} deriving (Show)
 
--- | Write a list of OSC bundles as an NRT score.
-writeNRT :: FilePath -> [OSC] -> IO ()
+-- | Encode an 'NRT' score.
+encodeNRT :: NRT -> B.ByteString
+encodeNRT = B.concat . map oscWithSize . nrt_bundles
+
+-- | Write an 'NRT' score.
+writeNRT :: FilePath -> NRT -> IO ()
 writeNRT fn = B.writeFile fn . encodeNRT
 
--- | Write a list of OSC bundles as an NRT score to a file handle.
-putNRT :: Handle -> [OSC] -> IO ()
+-- | Write an 'NRT' score to a file handle.
+putNRT :: Handle -> NRT -> IO ()
 putNRT h = B.hPut h . encodeNRT
+
+-- | Decode an 'NRT' 'B.ByteString' to a list of 'Bundle's.
+decode_nrt_bundles :: B.ByteString -> [Bundle]
+decode_nrt_bundles s =
+    let (p,q) = B.splitAt 4 s
+        n = fromIntegral (decode_i32 p)
+        (r,s') = B.splitAt n q
+        r' = decodeBundle r
+    in if B.null s'
+       then [r']
+       else r' : decode_nrt_bundles s'
+
+-- | Decode an 'NRT' 'B.ByteString'.
+decodeNRT :: B.ByteString -> NRT
+decodeNRT = NRT . decode_nrt_bundles
+
+-- | 'decodeNRT' of 'B.readFile'.
+readNRT :: FilePath -> IO NRT
+readNRT = fmap decodeNRT . B.readFile
+
+-- | File formats @scsynth@ renders to.
+data NRT_File_Format = AIFF | FLAC | NeXT | WAVE deriving (Eq,Show)
+
+-- | Sample formats @scsynth@ renders to.
+data NRT_Sample_Format = I16 | I24 | I32 | F32 | F64 deriving (Eq,Show)
+
+-- | Data required to render an 'NRT' score using @scsynth@.  The
+-- input file is optional.
+data NRT_Render = NRT_Render {nrt_score :: FilePath
+                             ,nrt_input_file ::Maybe FilePath
+                             ,nrt_output_file :: FilePath
+                             ,nrt_channels :: Int
+                             ,nrt_sample_rate :: Double
+                             ,nrt_file_format :: NRT_File_Format
+                             ,nrt_sample_format :: NRT_Sample_Format}
+
+-- | Format 'NRT_Sample_Format' for @scsynth@.
+nrt_sf_pp :: NRT_Sample_Format -> String
+nrt_sf_pp f =
+    case f of
+         I16 -> "int16"
+         I24 -> "int24"
+         I32 -> "int32"
+         F32 -> "float"
+         F64 -> "double"
+
+-- | Format 'NRT_Render' as list of arguments to @scsynth@.
+--
+-- > let {r = NRT_Render "x.osc" Nothing "x.aif" 2 44100 AIFF I16
+-- >     ;a = ["-o","2","-N","x.osc","_","x.aif","44100","AIFF","int16"]}
+-- > in renderNRT_opt r == a
+renderNRT_opt :: NRT_Render -> [String]
+renderNRT_opt (NRT_Render c_fn i_fn o_fn nc sr hdr fmt) =
+    let i_fn' = fromMaybe "_" i_fn
+        nc' = show nc
+        sr' = show (round sr :: Integer)
+        hdr' = show hdr
+        fmt' = nrt_sf_pp fmt
+    in ["-o",nc',"-N",c_fn,i_fn',o_fn,sr',hdr',fmt']
+
+-- | 'renderNRT' command as 'String', with shell protected arguments.
+--
+-- > renderNRT_cmd [] (NRT_Render "s.osc" Nothing "s.flac" 2 44100 FLAC I24)
+renderNRT_cmd :: [String] -> NRT_Render -> String
+renderNRT_cmd x =
+    let protect s = '\'' : s ++ "\'"
+    in unwords . ("scsynth" :) . map protect . (x ++) . renderNRT_opt
+
+-- | Run @scsynth@ to render 'NRT_Render' with given @scsynth@ options.
+renderNRT :: [String] -> NRT_Render -> IO ExitCode
+renderNRT o = rawSystem "scsynth" . (o ++ ) . renderNRT_opt
diff --git a/Sound/SC3/Server/Play.hs b/Sound/SC3/Server/Play.hs
deleted file mode 100644
--- a/Sound/SC3/Server/Play.hs
+++ /dev/null
@@ -1,74 +0,0 @@
--- | Basic user interaction with the scsynth server.
-module Sound.SC3.Server.Play (stop,reset,send,async
-                             ,withSC3
-                             ,Audible(..)
-                             ,perform) where
-
-import Sound.OpenSoundControl
-import Sound.SC3.Server.Command
-import Sound.SC3.Server.Synthdef
-import Sound.SC3.UGen.UGen
-
--- | Free all nodes ('g_freeAll') at group @1@.
-stop :: Transport t => t -> IO ()
-stop fd = send fd (g_freeAll [1])
-
--- | Send an 'OSC' message and wait for a @\/done@ reply.
-async :: Transport t => t -> OSC -> IO OSC
-async fd m = send fd m >> wait fd "/done"
-
--- | Free all nodes ('g_freeAll') at group @0@ and re-create groups
--- @1@ and @2@.
-reset :: Transport t => t -> IO ()
-reset fd = do
-  send fd (g_freeAll [0])
-  send fd (g_new [(1,AddToTail,0),(2,AddToTail,0)])
-
--- | Bracket @SC3@ communication.
-withSC3 :: (UDP -> IO a) -> IO a
-withSC3 = withTransport (openUDP "127.0.0.1" 57110)
-
--- | Send 'd_recv' and 's_new' messages to scsynth.
-playSynthdef :: Transport t => t -> Synthdef -> IO ()
-playSynthdef fd s = do
-  _ <- async fd (d_recv s)
-  send fd (s_new (synthdefName s) (-1) AddToTail 1 [])
-
--- | Send an /anonymous/ instrument definition using 'playSynthdef'.
-playUGen :: Transport t => t -> UGen -> IO ()
-playUGen fd = playSynthdef fd . synthdef "Anonymous"
-
--- | Class for values that can be encoded and send to @scsynth@ for
--- audition.
-class Audible e where
-    play :: Transport t => t -> e -> IO ()
-    audition :: e -> IO ()
-    audition e = withSC3 (`play` e)
-
-instance Audible Synthdef where
-    play = playSynthdef
-
-instance Audible UGen where
-    play = playUGen
-
--- | Wait ('pauseThreadUntil') until bundle is due to be sent relative
--- to initial 'UTCr' time, then send each message, asynchronously if
--- required.
-run_bundle :: Transport t => t -> Double -> OSC -> IO ()
-run_bundle fd i o =
-    let wr m = if isAsync m
-               then async fd m >> return ()
-               else send fd m
-    in case o of
-         Bundle (NTPr t) x' -> do
-             pauseThreadUntil (i + t)
-             mapM_ wr x'
-         _ -> error "run_bundle: non bundle or non-NTPr bundle"
-
--- | Perform an 'OSC' score (as would be rendered by 'writeNRT').  In
--- particular note that: (1) all 'OSC' must be 'Bundle's and (2)
--- timestamps /must/ be in 'NTPr' form.
-perform :: [OSC] -> IO ()
-perform s = do
-  let f i fd = run_bundle fd i
-  withSC3 (\fd -> utcr >>= \i -> mapM_ (f i fd) s)
diff --git a/Sound/SC3/Server/Status.hs b/Sound/SC3/Server/Status.hs
--- a/Sound/SC3/Server/Status.hs
+++ b/Sound/SC3/Server/Status.hs
@@ -1,35 +1,13 @@
 -- | Request and display status information from the synthesis server.
-module Sound.SC3.Server.Status (serverStatus
-                               ,serverSampleRateNominal
-                               ,serverSampleRateActual) where
-
-import Control.Monad
-import Sound.OpenSoundControl
-import Sound.SC3.Server.Command
-
--- | Collect server status information.
-serverStatus :: Transport t => t -> IO [String]
-serverStatus = liftM statusFormat . serverStatusData
-
--- | Read nominal sample rate of server.
-serverSampleRateNominal :: (Transport t) => t -> IO Double
-serverSampleRateNominal = liftM (extractDouble . (!! 7)) . serverStatusData
-
--- | Read actual sample rate of server.
-serverSampleRateActual :: (Transport t) => t -> IO Double
-serverSampleRateActual = liftM (extractDouble . (!! 8)) . serverStatusData
+module Sound.SC3.Server.Status where
 
-extractDouble :: Datum -> Double
-extractDouble (Float f) = f
-extractDouble (Double f) = f
-extractDouble _ = error "extractDouble"
+import Sound.OpenSoundControl.Type
 
-serverStatusData :: Transport t => t -> IO [Datum]
-serverStatusData fd = do
-  send fd status
-  (Message _ d) <- wait fd "/status.reply"
-  return d
+-- | Get /n/th field of status as 'Double'.
+extractStatusField :: Int -> [Datum] -> Double
+extractStatusField n = datum_real_err . (!! n)
 
+-- | Names of status fields.
 statusFields :: [String]
 statusFields = ["Unused                      ",
                 "# UGens                     ",
@@ -41,6 +19,7 @@
                 "Sample Rate (Nominal)       ",
                 "Sample Rate (Actual)        "]
 
+-- | Status pretty printer.
 statusFormat :: [Datum] -> [String]
 statusFormat d =
     let s = "***** SuperCollider Server Status *****"
diff --git a/Sound/SC3/Server/Synthdef.hs b/Sound/SC3/Server/Synthdef.hs
--- a/Sound/SC3/Server/Synthdef.hs
+++ b/Sound/SC3/Server/Synthdef.hs
@@ -1,104 +1,32 @@
 -- | The unit-generator graph structure implemented by the
 --   SuperCollider synthesis server.
-module Sound.SC3.Server.Synthdef (NodeId,PortIndex,KType(..)
-                                 ,Node(..),FromPort(..)
-                                 ,Graph(..),Graphdef,graphdef
-                                 ,Synthdef(..),synthdefData,synth,synthdef
-                                 ,synthstat) where
+module Sound.SC3.Server.Synthdef where
 
-import qualified Data.ByteString.Lazy as B
-import qualified Data.IntMap as M
+import qualified Data.ByteString.Lazy as B {- bytestring -}
 import Data.List
-import Sound.OpenSoundControl.Coding.Byte
+import Sound.OpenSoundControl.Coding.Byte {- hosc -}
 import Sound.OpenSoundControl.Coding.Cast
-import Sound.SC3.UGen.UGen
-import Sound.SC3.UGen.Rate
-
--- | Node identifier.
-type NodeId = Int
-
--- | Port index.
-type PortIndex = Int
-
--- | Type to represent unit generator graph.
-data Graph = Graph {nextId :: NodeId
-                   ,constants :: [Node]
-                   ,controls :: [Node]
-                   ,ugens :: [Node]}
-            deriving (Eq,Show)
-
--- | Enumeration of the four operating rates for controls.
-data KType = K_IR | K_KR | K_TR | K_AR
-             deriving (Eq,Show,Ord)
-
--- | Type to represent nodes in unit generator graph.
-data Node = NodeC {node_id :: NodeId
-                  ,node_c_value :: Double}
-          | NodeK {node_id :: NodeId
-                  ,node_k_rate :: Rate
-                  ,node_k_name :: String
-                  ,node_k_default :: Double
-                  ,node_k_type :: KType}
-          | NodeU {node_id :: NodeId
-                  ,node_u_rate :: Rate
-                  ,node_u_name :: String
-                  ,node_u_inputs :: [FromPort]
-                  ,node_u_outputs :: [Output]
-                  ,node_u_special :: Special
-                  ,node_u_ugenid :: UGenId}
-          | NodeP {node_id :: NodeId
-                  ,node_p_node :: Node
-                  ,node_p_index :: PortIndex}
-            deriving (Eq,Show)
-
-node_k_cmp :: Node -> Node -> Ordering
-node_k_cmp p q = compare (node_k_type p) (node_k_type q)
-
--- Determine class of control given rate and name.
-ktype :: Rate -> Bool -> KType
-ktype r tr =
-    if tr
-    then case r of
-           KR -> K_TR
-           _ -> error "ktype"
-    else case r of
-           IR -> K_IR
-           KR -> K_KR
-           AR -> K_AR
-           DR -> error "ktype"
-
--- | Type to represent the left hand side of an edge in a unit
---   generator graph.
-data FromPort = FromPort_C {port_nid :: NodeId}
-              | FromPort_K {port_nid :: NodeId,port_kt :: KType}
-              | FromPort_U {port_nid :: NodeId,port_idx :: PortIndex}
-                deriving (Eq,Show)
+import Sound.SC3.Server.Synthdef.Internal
+import Sound.SC3.Server.Synthdef.Type
+import Sound.SC3.UGen.Type
+import System.FilePath {- filepath -}
 
 -- | Transform a unit generator into a graph.
+--
+-- > import Sound.SC3.UGen
+-- > synth (out 0 (pan2 (sinOsc AR 440 0) 0.5 0.1))
 synth :: UGen -> Graph
 synth u =
     let (_,g) = mk_node (prepare_root u) empty_graph
-        (Graph _ cs ks us) = g
-        ks' = sortBy node_k_cmp ks
-        us' = if null ks'
-              then reverse us
-              else implicit ks' ++ reverse us
-    in Graph (-1) cs ks' us'
-
--- | Binary representation of a unit generator graph.
-type Graphdef = B.ByteString
+        g' = g {ugens = reverse (ugens g)}
+    in add_implicit g'
 
 -- | Transform a unit generator graph into bytecode.
 graphdef :: Graph -> Graphdef
 graphdef = encode_graphdef
 
--- | Binary representation of a unit generator synth definition.
-data Synthdef = Synthdef {synthdefName :: String
-                         ,synthdefGraph :: Graph}
-                deriving (Eq,Show)
-
--- | Encode 'Synthdef' as binary data stream.
-synthdefData :: Synthdef -> B.ByteString
+-- | Encode 'Synthdef' as a binary data stream.
+synthdefData :: Synthdef -> Graphdef
 synthdefData (Synthdef s g) =
     B.concat [encode_str "SCgf"
              ,encode_i32 0
@@ -110,6 +38,13 @@
 synthdef :: String -> UGen -> Synthdef
 synthdef s u = Synthdef s (synth u)
 
+-- | Write 'Synthdef' to indicated directory.  The filename is the
+-- 'synthdefName' with the appropriate extension (@scsyndef@).
+synthdefWrite :: Synthdef -> FilePath -> IO ()
+synthdefWrite s dir =
+    let nm = dir </> synthdefName s <.> "scsyndef"
+    in B.writeFile nm (synthdefData s)
+
 -- | Simple statistical analysis of a unit generator graph.
 synthstat :: UGen -> String
 synthstat u =
@@ -126,241 +61,4 @@
                ,"number of unit generators : " ++ show (length us)
                ,"unit generator rates      : " ++ f node_u_rate us]
 
-as_from_port :: Node -> FromPort
-as_from_port d =
-    case d of
-      NodeC n _ -> FromPort_C n
-      NodeK n _ _ _ t -> FromPort_K n t
-      NodeU n _ _ _ _ _ _ -> FromPort_U n 0
-      NodeP _ u p -> FromPort_U (node_id u) p
 
--- The empty graph.
-empty_graph :: Graph
-empty_graph = Graph 0 [] [] []
-
--- Predicate to locate constant.
-find_c_p :: Double -> Node -> Bool
-find_c_p x n =
-    case n of
-      NodeC _ y -> x == y
-      _ -> error "find_c_p"
-
--- Insert a constant node into the graph.
-push_c :: Double -> Graph -> (Node,Graph)
-push_c x g =
-    let n = NodeC (nextId g) x
-    in (n,g {constants = n : constants g
-             ,nextId = nextId g + 1})
-
--- Either find existing constant node,or insert a new node.
-mk_node_c :: UGen -> Graph -> (Node,Graph)
-mk_node_c u g =
-    case u of
-      Constant x ->
-          let y = find (find_c_p x) (constants g)
-          in maybe (push_c x g) (\y' -> (y',g)) y
-      _ -> error "mk_node_c"
-
--- Predicate to locate control,names must be unique.
-find_k_p :: String -> Node -> Bool
-find_k_p x n =
-    case n of
-      NodeK _ _ y _ _ -> x == y
-      _ -> error "find_k_p"
-
--- Insert a control node into the graph.
-push_k :: (Rate,String,Double,Bool) -> Graph -> (Node,Graph)
-push_k (r,nm,d,tr) g =
-    let n = NodeK (nextId g) r nm d (ktype r tr)
-    in (n,g {controls = n : controls g
-             ,nextId = nextId g + 1})
-
--- Either find existing control node,or insert a new node.
-mk_node_k :: UGen -> Graph -> (Node,Graph)
-mk_node_k u g =
-    case u of
-      Control r nm d tr ->
-          let y = find (find_k_p nm) (controls g)
-          in maybe (push_k (r,nm,d,tr) g) (\y' -> (y',g)) y
-      _ -> error "mk_node_k"
-
-acc :: [UGen] -> [Node] -> Graph -> ([Node],Graph)
-acc [] n g = (reverse n,g)
-acc (x:xs) ys g =
-    let (y,g') = mk_node x g
-    in acc xs (y:ys) g'
-
-type UGenParts = (Rate,String,[FromPort],[Output],Special,UGenId)
-
--- Predicate to locate primitive,names must be unique.
-find_u_p :: UGenParts -> Node -> Bool
-find_u_p (r,n,i,o,s,d) nd =
-    case nd of
-      NodeU _ r' n' i' o' s' d' ->
-          r == r' && n == n' && i == i' && o == o' && s == s' && d == d'
-      _ ->  error "find_u_p"
-
--- Insert a primitive node into the graph.
-push_u :: UGenParts -> Graph -> (Node,Graph)
-push_u (r,nm,i,o,s,d) g =
-    let n = NodeU (nextId g) r nm i o s d
-    in (n,g {ugens = n : ugens g
-             ,nextId = nextId g + 1})
-
--- Either find existing control node,or insert a new node.
-mk_node_u :: UGen -> Graph -> (Node,Graph)
-mk_node_u ug g =
-    case ug of
-      Primitive r nm i o s d ->
-          let (i',g') = acc i [] g
-              i'' = map as_from_port i'
-              u = (r,nm,i'',o,s,d)
-              y = find (find_u_p u) (ugens g')
-          in maybe (push_u u g') (\y' -> (y',g')) y
-      _ -> error "mk_node_u"
-
--- Proxies do not get stored in the graph.
-mk_node_p :: Node -> PortIndex -> Graph -> (Node,Graph)
-mk_node_p n p g =
-    let z = nextId g
-    in (NodeP z n p,g {nextId = z + 1})
-
-mk_node :: UGen -> Graph -> (Node,Graph)
-mk_node u g =
-    case ugenType u of
-      Constant_U -> mk_node_c u g
-      Control_U -> mk_node_k u g
-      Primitive_U -> mk_node_u u g
-      Proxy_U ->
-          let (n,g') = mk_node_u (proxySource u) g
-          in mk_node_p n (proxyIndex u) g'
-      MRG_U ->
-          let (_,g') = mk_node (mrgRight u) g
-          in mk_node (mrgLeft u) g'
-      MCE_U -> error "mk_node: mce"
-
-type Map = M.IntMap Int
-type Maps = (Map,[Node],Map,Map)
-
--- Generate maps from node identifiers to synthdef indexes.
-mk_maps :: Graph -> Maps
-mk_maps (Graph _ cs ks us) =
-    (M.fromList (zip (map node_id cs) [0..])
-    ,ks
-    ,M.fromList (zip (map node_id ks) [0..])
-    ,M.fromList (zip (map node_id us) [0..]))
-
--- Locate index in map given node identifer.
-fetch :: NodeId -> Map -> Int
-fetch = M.findWithDefault (error "fetch")
-
-data Input = Input Int Int
-             deriving (Eq,Show)
-
--- For controls we need to know not the overall index
--- but in relation to controls of the same type.
-fetch_k :: NodeId -> KType -> [Node] -> Int
-fetch_k n t ks =
-    let f _ [] = error "fetch_k"
-        f i (x:xs) =
-            if n == node_id x
-            then i
-            else if t == node_k_type x
-                 then f (i + 1) xs
-                 else f i xs
-    in f 0 ks
-
--- Construct input form required by byte-code generator.
-make_input :: Maps -> FromPort -> Input
-make_input (cs,ks,_,us) fp =
-    case fp of
-      FromPort_C n -> Input (-1) (fetch n cs)
-      FromPort_K n t -> let i = case t of
-                                  K_IR -> 0
-                                  K_KR -> 1
-                                  K_TR -> 2
-                                  K_AR -> 3
-                        in Input i (fetch_k n t ks)
-      FromPort_U n p -> Input (fetch n us) p
-
--- Byte-encode input value.
-encode_input :: Input -> B.ByteString
-encode_input (Input u p) = B.append (encode_i16 u) (encode_i16 p)
-
--- Byte-encode control node.
-encode_node_k :: Maps -> Node -> B.ByteString
-encode_node_k (_,_,ks,_) nd =
-    case nd of
-      NodeK n _ nm _ _ -> B.concat [B.pack (str_pstr nm)
-                                   ,encode_i16 (fetch n ks)]
-      _ -> error "encode_node_k"
-
--- Byte-encode primitive node.
-encode_node_u :: Maps -> Node -> B.ByteString
-encode_node_u m n =
-    case n of
-      NodeU _ r nm i o s _ ->
-          let i' = map (encode_input . make_input m) i
-              o' = map (encode_i8 . rateId) o
-              (Special s') = s
-          in B.concat [B.pack (str_pstr nm)
-                      ,encode_i8 (rateId r)
-                      ,encode_i16 (length i)
-                      ,encode_i16 (length o)
-                      ,encode_i16 s'
-                      ,B.concat i'
-                      ,B.concat o']
-      _ -> error "encode_node_u: illegal input"
-
--- Construct instrument definition bytecode.
-encode_graphdef :: Graph -> B.ByteString
-encode_graphdef g =
-    let (Graph _ cs ks us) = g
-        mm = mk_maps g
-    in B.concat [encode_i16 (length cs)
-                ,B.concat (map (encode_f32 . node_c_value) cs)
-                ,encode_i16 (length ks)
-                ,B.concat (map (encode_f32 . node_k_default) ks)
-                ,encode_i16 (length ks)
-                ,B.concat (map (encode_node_k mm) ks)
-                ,encode_i16 (length us)
-                ,B.concat (map (encode_node_u mm) us)]
-
-type KS_COUNT = (Int,Int,Int,Int)
-
-ks_count :: [Node] -> KS_COUNT
-ks_count ks =
-    let f r [] = r
-        f (i,k,t,a) (x:xs) =
-            let r' = case node_k_type x of
-                       K_IR -> (i+1,k,t,a)
-                       K_KR -> (i,k+1,t,a)
-                       K_TR -> (i,k,t+1,a)
-                       K_AR -> (i,k,t,a+1)
-            in f r' xs
-    in f (0,0,0,0) ks
-
--- Construct implicit control unit generator nodes.
-implicit :: [Node] -> [Node]
-implicit ks =
-    let (ni,nk,nt,na) = ks_count ks
-        mk_n t n o =
-            let (nm,r) = case t of
-                            K_IR -> ("Control",IR)
-                            K_KR -> ("Control",KR)
-                            K_TR -> ("TrigControl",KR)
-                            K_AR -> ("AudioControl",AR)
-                i = replicate n r
-            in NodeU (-1) r nm [] i (Special o) NoId
-    in [mk_n K_IR ni 0
-       ,mk_n K_KR nk ni
-       ,mk_n K_TR nt (ni + nk)
-       ,mk_n K_AR na (ni + nk + nt)]
-
--- Transform mce nodes to mrg nodes
-prepare_root :: UGen -> UGen
-prepare_root u =
-    case ugenType u of
-      MCE_U -> mrg (mceProxies u)
-      MRG_U -> MRG (prepare_root (mrgLeft u)) (prepare_root (mrgRight u))
-      _ -> u
diff --git a/Sound/SC3/Server/Synthdef/Internal.hs b/Sound/SC3/Server/Synthdef/Internal.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/Server/Synthdef/Internal.hs
@@ -0,0 +1,379 @@
+module Sound.SC3.Server.Synthdef.Internal where
+
+import qualified Data.ByteString.Lazy as B {- bytestring -}
+import qualified Data.IntMap as M {- containers -}
+import Data.Function
+import Data.List
+import Data.Maybe
+import Sound.OpenSoundControl.Coding.Byte {- hosc -}
+import Sound.OpenSoundControl.Coding.Cast
+import Sound.SC3.Server.Synthdef.Type
+import Sound.SC3.UGen.Rate
+import Sound.SC3.UGen.Type
+import Sound.SC3.UGen.UGen
+
+-- | Find 'Node' with indicated 'NodeId'.
+find_node :: Graph -> NodeId -> Maybe Node
+find_node (Graph _ cs ks us) n =
+    let f x = node_id x == n
+    in find f (cs ++ ks ++ us)
+
+-- | Is 'Node' an /implicit/ control UGen?
+is_implicit_control :: Node -> Bool
+is_implicit_control n =
+    let cs = ["AudioControl","Control","TrigControl"]
+    in case n of
+        NodeU x _ s _ _ _ _ -> x == -1 && s `elem` cs
+        _ -> False
+
+-- | Generate a label for 'Node' using the /type/ and the 'node_id'.
+node_label :: Node -> String
+node_label nd =
+    case nd of
+      NodeC n _ -> "c_" ++ show n
+      NodeK n _ _ _ _ -> "k_" ++ show n
+      NodeU n _ _ _ _ _ _ -> "u_" ++ show n
+      NodeP n _ _ -> "p_" ++ show n
+
+-- | Get 'port_idx' for 'FromPort_U', else @0@.
+port_idx_or_zero :: FromPort -> PortIndex
+port_idx_or_zero p =
+    case p of
+      FromPort_U _ (Just x) -> x
+      _ -> 0
+
+-- | Is 'Node' a /constant/.
+is_node_c :: Node -> Bool
+is_node_c n =
+    case n of
+      NodeC _ _ -> True
+      _ -> False
+
+-- | Is 'Node' a /control/.
+is_node_k :: Node -> Bool
+is_node_k n =
+    case n of
+      NodeK {} -> True
+      _ -> False
+
+-- | Is 'Node' a /UGen/.
+is_node_u :: Node -> Bool
+is_node_u n =
+    case n of
+      NodeU {} -> True
+      _ -> False
+
+-- | Calculate all edges given a set of 'NodeU'.
+edges :: [Node] -> [Edge]
+edges =
+    let f n = case n of
+                NodeU x _ _ i _ _ _ -> zip i (map (ToPort x) [0..])
+                _ -> error "edges: non NodeU input node"
+    in concatMap f
+
+-- | Transform 'Node' to 'FromPort'.
+as_from_port :: Node -> FromPort
+as_from_port d =
+    case d of
+      NodeC n _ -> FromPort_C n
+      NodeK n _ _ _ t -> FromPort_K n t
+      NodeU n _ _ _ o _ _ ->
+          case o of
+            [_] -> FromPort_U n Nothing
+            _ -> error (show ("as_from_port: non unary NodeU",d))
+      NodeP _ u p -> FromPort_U (node_id u) (Just p)
+
+-- | Locate 'Node' of 'FromPort' in 'Graph'.
+from_port_node :: Graph -> FromPort -> Maybe Node
+from_port_node g fp = find_node g (port_nid fp)
+
+-- | The empty 'Graph'.
+empty_graph :: Graph
+empty_graph = Graph 0 [] [] []
+
+-- | Find the maximum 'NodeId' used at 'Graph' (this ought normally be
+-- the 'nextId').
+graph_maximum_id :: Graph -> NodeId
+graph_maximum_id (Graph _ c k u) = maximum (map node_id (c ++ k ++ u))
+
+-- | Compare 'NodeK' values 'on' 'node_k_type'.
+node_k_cmp :: Node -> Node -> Ordering
+node_k_cmp = compare `on` node_k_type
+
+-- | Determine class of control given 'Rate' and /trigger/ status.
+ktype :: Rate -> Bool -> KType
+ktype r tr =
+    if tr
+    then case r of
+           KR -> K_TR
+           _ -> error "ktype: non KR trigger control"
+    else case r of
+           IR -> K_IR
+           KR -> K_KR
+           AR -> K_AR
+           DR -> error "ktype: DR control"
+
+-- | Remove implicit /control/ UGens from 'Graph'
+remove_implicit :: Graph -> Graph
+remove_implicit g =
+    let u = filter (not . is_implicit_control) (ugens g)
+    in g {ugens = u}
+
+-- | Add implicit /control/ UGens to 'Graph'.
+add_implicit :: Graph -> Graph
+add_implicit g =
+    let (Graph z cs ks us) = g
+        ks' = sortBy node_k_cmp ks
+        im = if null ks' then [] else mk_implicit ks'
+        us' = im ++ us
+    in Graph z cs ks' us'
+
+-- | Predicate to determine if 'Node' is a constant with indicated /value/.
+find_c_p :: Double -> Node -> Bool
+find_c_p x n =
+    case n of
+      NodeC _ y -> x == y
+      _ -> error "find_c_p: non NodeC"
+
+-- | Insert a constant 'Node' into the 'Graph'.
+push_c :: Double -> Graph -> (Node,Graph)
+push_c x g =
+    let n = NodeC (nextId g) x
+    in (n,g {constants = n : constants g
+            ,nextId = nextId g + 1})
+
+-- | Either find existing 'Constant' 'Node', or insert a new 'Node'.
+mk_node_c :: Constant -> Graph -> (Node,Graph)
+mk_node_c (Constant x) g =
+    let y = find (find_c_p x) (constants g)
+    in maybe (push_c x g) (\y' -> (y',g)) y
+
+-- | Predicate to determine if 'Node' is a control with indicated
+-- /name/.  Names must be unique.
+find_k_p :: String -> Node -> Bool
+find_k_p x n =
+    case n of
+      NodeK _ _ y _ _ -> x == y
+      _ -> error "find_k_p"
+
+-- | Insert a control node into the 'Graph'.
+push_k :: (Rate,String,Double,Bool) -> Graph -> (Node,Graph)
+push_k (r,nm,d,tr) g =
+    let n = NodeK (nextId g) r nm d (ktype r tr)
+    in (n,g {controls = n : controls g
+            ,nextId = nextId g + 1})
+
+-- | Either find existing 'Control' 'Node', or insert a new 'Node'.
+mk_node_k :: Control -> Graph -> (Node,Graph)
+mk_node_k (Control r nm d tr) g =
+    let y = find (find_k_p nm) (controls g)
+    in maybe (push_k (r,nm,d,tr) g) (\y' -> (y',g)) y
+
+type UGenParts = (Rate,String,[FromPort],[Output],Special,UGenId)
+
+-- | Predicate to locate primitive, names must be unique.
+find_u_p :: UGenParts -> Node -> Bool
+find_u_p (r,n,i,o,s,d) nd =
+    case nd of
+      NodeU _ r' n' i' o' s' d' ->
+          r == r' && n == n' && i == i' && o == o' && s == s' && d == d'
+      _ ->  error "find_u_p"
+
+-- | Insert a /primitive/ 'NodeU' into the 'Graph'.
+push_u :: UGenParts -> Graph -> (Node,Graph)
+push_u (r,nm,i,o,s,d) g =
+    let n = NodeU (nextId g) r nm i o s d
+    in (n,g {ugens = n : ugens g
+            ,nextId = nextId g + 1})
+
+mk_node_u_acc :: [UGen] -> [Node] -> Graph -> ([Node],Graph)
+mk_node_u_acc u n g =
+    case u of
+      [] -> (reverse n,g)
+      x:xs -> let (y,g') = mk_node x g
+              in mk_node_u_acc xs (y:n) g'
+
+-- | Either find existing 'Primitive' node, or insert a new 'Node'.
+mk_node_u :: Primitive -> Graph -> (Node,Graph)
+mk_node_u (Primitive r nm i o s d) g =
+    let (i',g') = mk_node_u_acc i [] g
+        i'' = map as_from_port i'
+        u = (r,nm,i'',o,s,d)
+        y = find (find_u_p u) (ugens g')
+    in maybe (push_u u g') (\y' -> (y',g')) y
+
+-- | Proxies do not get stored in the graph.
+mk_node_p :: Node -> PortIndex -> Graph -> (Node,Graph)
+mk_node_p n p g =
+    let z = nextId g
+    in (NodeP z n p,g {nextId = z + 1})
+
+mk_node :: UGen -> Graph -> (Node,Graph)
+mk_node u g =
+    case u of
+      Constant_U c -> mk_node_c c g
+      Control_U k -> mk_node_k k g
+      Label_U _ -> error "mk_node: label"
+      Primitive_U p -> mk_node_u p g
+      Proxy_U p ->
+          let (n,g') = mk_node_u (proxySource p) g
+          in mk_node_p n (proxyIndex p) g'
+      MRG_U m ->
+          let (_,g') = mk_node (mrgRight m) g
+          in mk_node (mrgLeft m) g'
+      MCE_U _ -> error "mk_node: mce"
+
+type Map = M.IntMap Int
+
+type Maps = (Map,[Node],Map,Map,[(KType,Int)])
+
+data Input = Input Int Int
+             deriving (Eq,Show)
+
+-- | Determine 'KType' of a /control/ UGen at 'NodeU', or not.
+node_ktype :: Node -> Maybe KType
+node_ktype n =
+    case (node_u_name n,node_u_rate n) of
+      ("Control",IR) -> Just K_IR
+      ("Control",KR) -> Just K_KR
+      ("TrigControl",KR) -> Just K_TR
+      ("AudioControl",AR) -> Just K_AR
+      _ -> Nothing
+
+-- | Map associating 'KType' with UGen index.
+mk_ktype_map :: [Node] -> [(KType,Int)]
+mk_ktype_map =
+    let f (i,n) = let g ty = (ty,i) in fmap g (node_ktype n)
+    in mapMaybe f . zip [0..]
+
+-- | Lookup 'KType' index from map (erroring variant of 'lookup').
+ktype_map_lookup :: KType -> [(KType,Int)] -> Int
+ktype_map_lookup k =
+    let e = error (show ("ktype_map_lookup",k))
+    in fromMaybe e . lookup k
+
+-- | Generate 'Maps' translating node identifiers to synthdef indexes.
+mk_maps :: Graph -> Maps
+mk_maps (Graph _ cs ks us) =
+    (M.fromList (zip (map node_id cs) [0..])
+    ,ks
+    ,M.fromList (zip (map node_id ks) [0..])
+    ,M.fromList (zip (map node_id us) [0..])
+    ,mk_ktype_map us)
+
+-- | Locate index in map given node identifer 'NodeId'.
+fetch :: NodeId -> Map -> Int
+fetch = M.findWithDefault (error "fetch")
+
+-- | Controls are a special case.  We need to know not the overall
+-- index but the index in relation to controls of the same type.
+fetch_k :: NodeId -> KType -> [Node] -> Int
+fetch_k z t =
+    let rec i ns =
+            case ns of
+              [] -> error "fetch_k"
+              n:ns' -> if z == node_id n
+                       then i
+                       else if t == node_k_type n
+                            then rec (i + 1) ns'
+                            else rec i ns'
+    in rec 0
+
+-- | Construct 'Input' form required by byte-code generator.
+make_input :: Maps -> FromPort -> Input
+make_input (cs,ks,_,us,kt) fp =
+    case fp of
+      FromPort_C n -> Input (-1) (fetch n cs)
+      FromPort_K n t -> let i = ktype_map_lookup t kt
+                        in Input i (fetch_k n t ks)
+      FromPort_U n p -> Input (fetch n us) (fromMaybe 0 p)
+
+-- | Byte-encode 'Input' value.
+encode_input :: Input -> B.ByteString
+encode_input (Input u p) = B.append (encode_i16 u) (encode_i16 p)
+
+-- | Byte-encode 'NodeK' control node.
+encode_node_k :: Maps -> Node -> B.ByteString
+encode_node_k (_,_,ks,_,_) nd =
+    case nd of
+      NodeK n _ nm _ _ -> B.concat [B.pack (str_pstr nm)
+                                   ,encode_i16 (fetch n ks)]
+      _ -> error "encode_node_k"
+
+-- | Byte-encode 'NodeU' primitive node.
+encode_node_u :: Maps -> Node -> B.ByteString
+encode_node_u m n =
+    case n of
+      NodeU _ r nm i o s _ ->
+          let i' = map (encode_input . make_input m) i
+              o' = map (encode_i8 . rateId) o
+              (Special s') = s
+          in B.concat [B.pack (str_pstr nm)
+                      ,encode_i8 (rateId r)
+                      ,encode_i16 (length i)
+                      ,encode_i16 (length o)
+                      ,encode_i16 s'
+                      ,B.concat i'
+                      ,B.concat o']
+      _ -> error "encode_node_u: illegal input"
+
+-- | Construct instrument definition bytecode.
+encode_graphdef :: Graph -> B.ByteString
+encode_graphdef g =
+    let (Graph _ cs ks us) = g
+        mm = mk_maps g
+    in B.concat [encode_i16 (length cs)
+                ,B.concat (map (encode_f32 . node_c_value) cs)
+                ,encode_i16 (length ks)
+                ,B.concat (map (encode_f32 . node_k_default) ks)
+                ,encode_i16 (length ks)
+                ,B.concat (map (encode_node_k mm) ks)
+                ,encode_i16 (length us)
+                ,B.concat (map (encode_node_u mm) us)]
+
+-- | 4-tuple to count 'KType's.
+type KS_COUNT = (Int,Int,Int,Int)
+
+-- | Count the number of /controls/ if each 'KType'.
+ks_count :: [Node] -> KS_COUNT
+ks_count =
+    let rec r ns =
+            let (i,k,t,a) = r
+            in case ns of
+                 [] -> r
+                 n:ns' -> let r' = case node_k_type n of
+                                     K_IR -> (i+1,k,t,a)
+                                     K_KR -> (i,k+1,t,a)
+                                     K_TR -> (i,k,t+1,a)
+                                     K_AR -> (i,k,t,a+1)
+                          in rec r' ns'
+    in rec (0,0,0,0)
+
+-- | Construct implicit /control/ unit generator 'Nodes'.  Unit
+-- generators are only constructed for instances of control types that
+-- are present.
+mk_implicit :: [Node] -> [Node]
+mk_implicit ks =
+    let (ni,nk,nt,na) = ks_count ks
+        mk_n t n o =
+            let (nm,r) = case t of
+                            K_IR -> ("Control",IR)
+                            K_KR -> ("Control",KR)
+                            K_TR -> ("TrigControl",KR)
+                            K_AR -> ("AudioControl",AR)
+                i = replicate n r
+            in if n == 0
+               then Nothing
+               else Just (NodeU (-1) r nm [] i (Special o) no_id)
+    in catMaybes [mk_n K_IR ni 0
+                 ,mk_n K_KR nk ni
+                 ,mk_n K_TR nt (ni + nk)
+                 ,mk_n K_AR na (ni + nk + nt)]
+
+-- | Transform /mce/ nodes to /mrg/ nodes
+prepare_root :: UGen -> UGen
+prepare_root u =
+    case u of
+      MCE_U m -> mrg (mceProxies m)
+      MRG_U m -> mrg2 (prepare_root (mrgLeft m)) (prepare_root (mrgRight m))
+      _ -> u
diff --git a/Sound/SC3/Server/Synthdef/Reconstruct.hs b/Sound/SC3/Server/Synthdef/Reconstruct.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/Server/Synthdef/Reconstruct.hs
@@ -0,0 +1,125 @@
+module Sound.SC3.Server.Synthdef.Reconstruct where
+
+import Data.Char
+import Data.Function
+import Data.List
+import Sound.SC3.Server.Synthdef.Internal
+import Sound.SC3.Server.Synthdef.Type
+import Sound.SC3.UGen.Operator
+import Sound.SC3.UGen.Rate
+import Sound.SC3.UGen.Type
+import Sound.SC3.UGen.UGen
+import Text.Printf
+
+node_sort :: [Node] -> [Node]
+node_sort = sortBy (compare `on` node_id)
+
+from_port_label :: Char -> FromPort -> String
+from_port_label jn fp =
+    case fp of
+      FromPort_C n -> printf "c_%d" n
+      FromPort_K n _ -> printf "k_%d" n
+      FromPort_U n Nothing -> printf "u_%d" n
+      FromPort_U n (Just i) -> printf "u_%d%co_%d" n jn i
+
+is_operator_name :: String -> Bool
+is_operator_name nm =
+    case nm of
+      c:_ -> not (isLetter c)
+      _ -> False
+
+parenthesise_operator :: String -> String
+parenthesise_operator nm =
+    if is_operator_name nm
+    then printf "(%s)" nm
+    else nm
+
+-- > import Sound.SC3.ID
+--
+-- > let {k = control KR "bus" 0
+-- >     ;o = sinOsc AR 440 0 + whiteNoise 'a' AR
+-- >     ;u = out k (pan2 (o * 0.1) 0 1)
+-- >     ;m = mrg [u,out 1 (impulse AR 1 0 * 0.1)]}
+-- > in putStrLn (reconstruct_graph_str (synth m))
+reconstruct_graph_str :: Graph -> String
+reconstruct_graph_str g =
+    let (Graph _ c k u) = g
+        ls = concat [map reconstruct_c_str (node_sort c)
+                    ,map reconstruct_k_str (node_sort k)
+                    ,concatMap reconstruct_u_str u
+                    ,[reconstruct_mrg_str u]]
+    in unlines (filter (not . null) ls)
+
+reconstruct_c_str :: Node -> String
+reconstruct_c_str u =
+    let l = node_label u
+        c = node_c_value u
+    in printf "%s = constant (%f::Double)" l c
+
+reconstruct_c_ugen :: Node -> UGen
+reconstruct_c_ugen u = constant (node_c_value u)
+
+reconstruct_k_rnd :: Node -> (Rate,String,Double)
+reconstruct_k_rnd u =
+    let r = node_k_rate u
+        n = node_k_name u
+        d = node_k_default u
+    in (r,n,d)
+
+reconstruct_k_str :: Node -> String
+reconstruct_k_str u =
+    let l = node_label u
+        (r,n,d) = reconstruct_k_rnd u
+    in printf "%s = control %s \"%s\" %f" l (show r) n d
+
+reconstruct_k_ugen :: Node -> UGen
+reconstruct_k_ugen u =
+    let (r,n,d) = reconstruct_k_rnd u
+    in control r n d
+
+ugen_qname :: String -> Special -> (String,String)
+ugen_qname nm (Special n) =
+    case nm of
+      "UnaryOpUGen" -> ("uop",unaryName n)
+      "BinaryOpUGen" -> ("binop",binaryName n)
+      _ -> ("ugen",nm)
+
+reconstruct_mce_str :: Node -> String
+reconstruct_mce_str u =
+    let o = length (node_u_outputs u)
+        l = node_label u
+        p = map (printf "%s_o_%d" l) [0 .. o - 1]
+        p' = intercalate "," p
+    in if o <= 1
+       then ""
+       else printf "[%s] = mceChannels %s" p' l
+
+reconstruct_u_str :: Node -> [String]
+reconstruct_u_str u =
+    let l = node_label u
+        r = node_u_rate u
+        i = node_u_inputs u
+        i_s = unwords (map (from_port_label '_') i)
+        i_l = intercalate "," (map (from_port_label '_') i)
+        s = node_u_special u
+        (q,n) = ugen_qname (node_u_name u) s
+        z = node_id u
+        o = length (node_u_outputs u)
+        u_s = printf "%s = ugen \"%s\" %s [%s] %d" l n (show r) i_l o
+        nd_s = let t = "%s = nondet \"%s\" (UId %d) %s [%s] %d"
+               in printf t l n z (show r) i_l o
+        c = case q of
+              "ugen" -> if node_u_ugenid u == NoId then u_s else nd_s
+              _ -> printf "%s = %s \"%s\" %s %s" l q n (show r) i_s
+        m = reconstruct_mce_str u
+    in if is_implicit_control u
+       then []
+       else if null m then [c] else [c,m]
+
+reconstruct_mrg_str :: [Node] -> String
+reconstruct_mrg_str u =
+    let zero_out n = not (is_implicit_control n) && null (node_u_outputs n)
+    in case map node_label (filter zero_out u) of
+         [] -> error "reconstruct_mrg_str"
+         [o] -> printf "%s" o
+         o -> printf "mrg [%s]" (intercalate "," o)
diff --git a/Sound/SC3/Server/Synthdef/Transform.hs b/Sound/SC3/Server/Synthdef/Transform.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/Server/Synthdef/Transform.hs
@@ -0,0 +1,77 @@
+-- | Transformations over 'Graph' structure.
+module Sound.SC3.Server.Synthdef.Transform where
+
+import Data.Either
+import Data.List
+import Data.Maybe
+import Sound.SC3.Server.Synthdef.Internal
+import Sound.SC3.Server.Synthdef.Type
+import Sound.SC3.UGen.Rate
+
+-- * Lift constants
+
+-- | Transform 'NodeC' to 'NodeK', 'id' for other 'Node' types.
+--
+-- > constant_to_control 8 (NodeC 0 0.1) == (NodeK 8 KR "k_8" 0.1 K_KR,9)
+constant_to_control :: NodeId -> Node -> (NodeId,Node)
+constant_to_control z n =
+    case n of
+      NodeC _ k -> (z+1,NodeK z KR ("k_" ++ show z) k K_KR)
+      _ -> (z,n)
+
+-- | Erroring variant of 'from_port_node'.
+from_port_node_err :: Graph -> FromPort -> Node
+from_port_node_err g fp =
+    let e = error "from_port_node_err"
+    in fromMaybe e (from_port_node g fp)
+
+-- | If the 'FromPort' is a /constant/ generate a /control/ 'Node',
+-- else retain 'FromPort'.
+c_lift_from_port :: Graph -> NodeId -> FromPort -> (NodeId,Either FromPort Node)
+c_lift_from_port g z fp =
+    case fp of
+      FromPort_C _ -> let n = from_port_node_err g fp
+                          (z',n') = constant_to_control z n
+                      in (z',Right n')
+      _ -> (z,Left fp)
+
+-- | Lift a set of 'NodeU' /inputs/ from constants to controls.  The
+-- result triple gives the incremented 'NodeId', the transformed
+-- 'FromPort' list, and the list of newly minted control 'Node's.
+c_lift_inputs :: Graph -> NodeId -> [FromPort] -> (NodeId,[FromPort],[Node])
+c_lift_inputs g z i =
+    let (z',r) = mapAccumL (c_lift_from_port g) z i
+        f e = case e of
+                Left fp -> fp
+                Right n -> as_from_port n
+        r' = map f r
+    in (z',r',rights r)
+
+c_lift_ugen :: Graph -> NodeId -> Node -> (NodeId,Node,[Node])
+c_lift_ugen g z n =
+    let i = node_u_inputs n
+        (z',i',k) = c_lift_inputs g z i
+    in (z',n {node_u_inputs = i'},k)
+
+c_lift_ugens :: Graph -> NodeId -> [Node] -> (NodeId,[Node],[Node])
+c_lift_ugens g  =
+    let rec (k,r) z u =
+            case u of
+              [] -> (z,k,reverse r)
+              n:u' -> let (z',n',k') = c_lift_ugen g z n
+                      in rec (k++k',n':r) z' u'
+    in rec ([],[])
+
+-- > import Sound.SC3
+-- > import Sound.SC3.UGen.Dot
+--
+-- > let u = out 0 (sinOsc AR 440 0 * 0.1)
+-- > let g = synth u
+-- > draw g
+-- > draw (lift_constants g)
+lift_constants :: Graph -> Graph
+lift_constants g =
+    let (Graph z _ k u) = remove_implicit g
+        (z',k',u') = c_lift_ugens g z u
+        g' = Graph z' [] (nub (k ++ k')) u'
+    in add_implicit g'
diff --git a/Sound/SC3/Server/Synthdef/Type.hs b/Sound/SC3/Server/Synthdef/Type.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/Server/Synthdef/Type.hs
@@ -0,0 +1,65 @@
+-- | The 'Graph' and 'Synthdef' types.
+module Sound.SC3.Server.Synthdef.Type where
+
+import qualified Data.ByteString.Lazy as B {- bytestring -}
+import Sound.SC3.UGen.Rate
+import Sound.SC3.UGen.Type
+
+-- | Node identifier.
+type NodeId = Int
+
+-- | Port index.
+type PortIndex = Int
+
+-- | Type to represent unit generator graph.
+data Graph = Graph {nextId :: NodeId
+                   ,constants :: [Node]
+                   ,controls :: [Node]
+                   ,ugens :: [Node]}
+            deriving (Eq,Show)
+
+-- | Binary representation of a unit generator graph.
+type Graphdef = B.ByteString
+
+-- | Enumeration of the four operating rates for controls.
+data KType = K_IR | K_KR | K_TR | K_AR
+             deriving (Eq,Show,Ord)
+
+-- | Type to represent the left hand side of an edge in a unit
+--   generator graph.
+data FromPort = FromPort_C {port_nid :: NodeId}
+              | FromPort_K {port_nid :: NodeId,port_kt :: KType}
+              | FromPort_U {port_nid :: NodeId,port_idx :: Maybe PortIndex}
+                deriving (Eq,Show)
+
+-- | A destination port.
+data ToPort = ToPort NodeId PortIndex deriving (Eq,Show)
+
+-- | A connection from 'FromPort' to 'ToPort'.
+type Edge = (FromPort,ToPort)
+
+-- | Type to represent nodes in unit generator graph.
+data Node = NodeC {node_id :: NodeId
+                  ,node_c_value :: Double}
+          | NodeK {node_id :: NodeId
+                  ,node_k_rate :: Rate
+                  ,node_k_name :: String
+                  ,node_k_default :: Double
+                  ,node_k_type :: KType}
+          | NodeU {node_id :: NodeId
+                  ,node_u_rate :: Rate
+                  ,node_u_name :: String
+                  ,node_u_inputs :: [FromPort]
+                  ,node_u_outputs :: [Output]
+                  ,node_u_special :: Special
+                  ,node_u_ugenid :: UGenId}
+          | NodeP {node_id :: NodeId
+                  ,node_p_node :: Node
+                  ,node_p_index :: PortIndex}
+            deriving (Eq,Show)
+
+-- | Binary representation of a unit generator synth definition.
+data Synthdef = Synthdef {synthdefName :: String
+                         ,synthdefGraph :: Graph}
+                deriving (Eq,Show)
+
diff --git a/Sound/SC3/Server/Transport/FD.hs b/Sound/SC3/Server/Transport/FD.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/Server/Transport/FD.hs
@@ -0,0 +1,162 @@
+-- | /FD/ variant of interaction with the scsynth server.
+module Sound.SC3.Server.Transport.FD where
+
+import Control.Monad
+import Sound.OpenSoundControl.Time
+import Sound.OpenSoundControl.Type
+import Sound.OSC.Transport.FD
+import Sound.OSC.Transport.FD.UDP
+import Sound.SC3.Server.Command
+import Sound.SC3.Server.NRT
+import Sound.SC3.Server.Status
+import Sound.SC3.Server.Synthdef
+import Sound.SC3.Server.Synthdef.Type
+import Sound.SC3.UGen.Type
+
+-- * hosc variants
+
+-- | Synonym for 'sendMessage'.
+send :: (Transport t) => t -> Message -> IO ()
+send = sendMessage
+
+-- | Synonym for 'waitReply'.
+wait :: Transport t => t -> String -> IO Message
+wait = waitReply
+
+-- | Send a 'Message' and 'wait' for a @\/done@ reply.
+async :: Transport t => t -> Message -> IO Message
+async fd m = sendMessage fd m >> wait fd "/done"
+
+-- | Bracket @SC3@ communication.
+withSC3 :: (UDP -> IO a) -> IO a
+withSC3 = withTransport (openUDP "127.0.0.1" 57110)
+
+-- * Server control
+
+-- | Free all nodes ('g_freeAll') at group @1@.
+stop :: Transport t => t -> IO ()
+stop fd = sendMessage fd (g_freeAll [1])
+
+-- | Free all nodes ('g_freeAll') at and re-create groups @1@ and @2@.
+reset :: Transport t => t -> IO ()
+reset fd = do
+  sendMessage fd (g_freeAll [1,2])
+  sendMessage fd (g_new [(1,AddToTail,0),(2,AddToTail,0)])
+
+-- | Send 'd_recv' and 's_new' messages to scsynth.
+playSynthdef :: Transport t => t -> Synthdef -> IO ()
+playSynthdef fd s = do
+  _ <- async fd (d_recv s)
+  sendMessage fd (s_new (synthdefName s) (-1) AddToTail 1 [])
+
+-- | Send an /anonymous/ instrument definition using 'playSynthdef'.
+playUGen :: Transport t => t -> UGen -> IO ()
+playUGen fd = playSynthdef fd . synthdef "Anonymous"
+
+-- * Non-real time
+
+-- | Wait ('pauseThreadUntil') until bundle is due to be sent relative
+-- to initial 'UTCr' time, then send each message, asynchronously if
+-- required.
+run_bundle :: Transport t => t -> Double -> Bundle -> IO ()
+run_bundle fd i (Bundle t x) =
+    let wr m = if isAsync m
+               then void (async fd m)
+               else sendMessage fd m
+    in case t of
+          NTPr n -> do
+                pauseThreadUntil (i + n)
+                mapM_ wr x
+          _ -> error "run_bundle: non-NTPr bundle"
+
+-- | Perform an 'NRT' score (as would be rendered by 'writeNRT').  In
+-- particular note that all timestamps /must/ be in 'NTPr' form.
+performNRT :: Transport t => t -> NRT -> IO ()
+performNRT fd s = utcr >>= \i -> mapM_ (run_bundle fd i) (nrt_bundles s)
+
+-- * Audible
+
+-- | Class for values that can be encoded and send to @scsynth@ for
+-- audition.
+class Audible e where
+    play :: Transport t => t -> e -> IO ()
+    audition :: e -> IO ()
+    audition e = withSC3 (`play` e)
+
+instance Audible Graph where
+    play fd g = playSynthdef fd (Synthdef "Anonymous" g)
+
+instance Audible Synthdef where
+    play = playSynthdef
+
+instance Audible UGen where
+    play = playUGen
+
+instance Audible NRT where
+    play = performNRT
+
+-- * Notifications
+
+-- | Turn on notifications, run /f/, turn off notifications, return
+-- result.
+withNotifications :: Transport t => t -> (t -> IO a) -> IO a
+withNotifications fd f = do
+  _ <- async fd (notify True)
+  r <- f fd
+  _ <- async fd (notify False)
+  return r
+
+-- * Buffer
+
+-- | Variant of 'b_getn1' that waits for return message and unpacks it.
+--
+-- > withSC3 (\fd -> b_getn1_data fd 0 (0,5))
+b_getn1_data :: Transport t => t -> Int -> (Int,Int) -> IO [Double]
+b_getn1_data fd b s = do
+  let f d = case d of
+              Int _:Int _:Int _:x -> map datum_real_err x
+              _ -> error "b_getn1_data"
+  sendMessage fd (b_getn1 b s)
+  fmap f (waitDatum fd "/b_setn")
+
+-- | Variant of 'b_getn1_data' that segments individual 'b_getn'
+-- messages to /n/ elements.
+--
+-- > withSC3 (\fd -> b_getn1_data_segment fd 1 0 (0,5))
+b_getn1_data_segment :: Transport t => t -> Int -> Int -> (Int,Int) -> IO [Double]
+b_getn1_data_segment fd n b (i,j) = do
+  let ix = b_indices n j i
+  d <- mapM (b_getn1_data fd b) ix
+  return (concat d)
+
+-- | Variant of 'b_getn1_data_segment' that gets the entire buffer.
+b_fetch :: Transport t => t -> Int -> Int -> IO [Double]
+b_fetch fd n b = do
+  let f d = case d of
+              [Int _,Int nf,Int nc,Float _] ->
+                  let ix = (0,nf * nc)
+                  in b_getn1_data_segment fd n b ix
+              _ -> error "b_fetch"
+  sendMessage fd (b_query1 b)
+  waitDatum fd "/b_info" >>= f
+
+
+-- * Status
+
+-- | Collect server status information.
+serverStatus :: Transport t => t -> IO [String]
+serverStatus = liftM statusFormat . serverStatusData
+
+-- | Read nominal sample rate of server.
+serverSampleRateNominal :: (Transport t) => t -> IO Double
+serverSampleRateNominal = liftM (extractStatusField 7) . serverStatusData
+
+-- | Read actual sample rate of server.
+serverSampleRateActual :: (Transport t) => t -> IO Double
+serverSampleRateActual = liftM (extractStatusField 8) . serverStatusData
+
+-- | Retrieve status data from server.
+serverStatusData :: Transport t => t -> IO [Datum]
+serverStatusData fd = do
+  sendMessage fd status
+  waitDatum fd "/status.reply"
diff --git a/Sound/SC3/Server/Transport/Monad.hs b/Sound/SC3/Server/Transport/Monad.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/Server/Transport/Monad.hs
@@ -0,0 +1,165 @@
+-- | /Monad/ variant of interaction with the scsynth server.
+module Sound.SC3.Server.Transport.Monad where
+
+import Control.Monad
+import Sound.OSC
+import Sound.SC3.Server.Command
+import Sound.SC3.Server.NRT
+import Sound.SC3.Server.Status
+import Sound.SC3.Server.Synthdef
+import Sound.SC3.Server.Synthdef.Type
+import Sound.SC3.UGen.Type
+
+-- * hosc variants
+
+-- | Synonym for 'sendMessage'.
+send :: (Transport m) => Message -> m ()
+send = sendMessage
+
+-- | Synonym for 'waitReply'.
+wait :: Transport m => String -> m Message
+wait = waitReply
+
+-- | Send a 'Message' and 'wait' for a @\/done@ reply.
+async :: Transport m => Message -> m Message
+async m = send m >> wait "/done"
+
+-- | Bracket @SC3@ communication. 'withTransport' at standard SC3 UDP
+-- port.
+--
+-- > import Sound.SC3.Server.Command
+--
+-- > withSC3 (send status >> wait "/status.reply")
+withSC3 :: Connection UDP a -> IO a
+withSC3 = withTransport (openUDP "127.0.0.1" 57110)
+
+-- * Server control
+
+-- | Free all nodes ('g_freeAll') at group @1@.
+stop :: Transport m => m ()
+stop = send (g_freeAll [1])
+
+-- | Free all nodes ('g_freeAll') at and re-create groups @1@ and @2@.
+reset :: Transport m => m ()
+reset =
+    let m = [g_freeAll [1,2],g_new [(1,AddToTail,0),(2,AddToTail,0)]]
+    in sendBundle (Bundle immediately m)
+
+
+-- | Send 'd_recv' and 's_new' messages to scsynth.
+playSynthdef :: Transport m => Synthdef -> m ()
+playSynthdef s = do
+  _ <- async (d_recv s)
+  send (s_new (synthdefName s) (-1) AddToTail 1 [])
+
+-- | Send an /anonymous/ instrument definition using 'playSynthdef'.
+playUGen :: Transport m => UGen -> m ()
+playUGen = playSynthdef . synthdef "Anonymous"
+
+-- * NRT
+
+-- | Wait ('pauseThreadUntil') until bundle is due to be sent relative
+-- to initial 'UTCr' time, then send each message, asynchronously if
+-- required.
+run_bundle :: (Transport m) => Double -> Bundle -> m ()
+run_bundle i (Bundle t x) =
+    let wr m = if isAsync m
+               then void (async m)
+               else send m
+    in case t of
+          NTPr n -> do
+                liftIO (pauseThreadUntil (i + n))
+                mapM_ wr x
+          _ -> error "run_bundle: non-NTPr bundle"
+
+-- | Perform an 'NRT' score (as would be rendered by 'writeNRT').  In
+-- particular note that all timestamps /must/ be in 'NTPr' form.
+performNRT :: (Transport m) => NRT -> m ()
+performNRT s = liftIO utcr >>= \i -> mapM_ (run_bundle i) (nrt_bundles s)
+
+-- * Audible
+
+-- | Class for values that can be encoded and send to @scsynth@ for
+-- audition.
+class Audible e where
+    play :: (Transport m) => e -> m ()
+
+instance Audible Graph where
+    play g = playSynthdef (Synthdef "Anonymous" g)
+
+instance Audible Synthdef where
+    play = playSynthdef
+
+instance Audible UGen where
+    play = playUGen
+
+instance Audible NRT where
+    play = performNRT
+
+audition :: Audible e => e -> IO ()
+audition e = withSC3 (play e)
+
+-- * Notifications
+
+-- | Turn on notifications, run /f/, turn off notifications, return
+-- result.
+withNotifications :: Transport m => m a -> m a
+withNotifications f = do
+  _ <- async (notify True)
+  r <- f
+  _ <- async (notify False)
+  return r
+
+-- * Buffer
+
+-- | Variant of 'b_getn1' that waits for return message and unpacks it.
+--
+-- > withSC3 (b_getn1_data 0 (0,5))
+b_getn1_data :: Transport m => Int -> (Int,Int) -> m [Double]
+b_getn1_data b s = do
+  let f d = case d of
+              Int _:Int _:Int _:x -> map datum_real_err x
+              _ -> error "b_getn1_data"
+  sendMessage (b_getn1 b s)
+  fmap f (waitDatum "/b_setn")
+
+-- | Variant of 'b_getn1_data' that segments individual 'b_getn'
+-- messages to /n/ elements.
+--
+-- > withSC3 (b_getn1_data_segment 1 0 (0,5))
+b_getn1_data_segment :: Transport m => Int -> Int -> (Int,Int) -> m [Double]
+b_getn1_data_segment n b (i,j) = do
+  let ix = b_indices n j i
+  d <- mapM (b_getn1_data b) ix
+  return (concat d)
+
+-- | Variant of 'b_getn1_data_segment' that gets the entire buffer.
+b_fetch :: Transport m => Int -> Int -> m [Double]
+b_fetch n b = do
+  let f d = case d of
+              [Int _,Int nf,Int nc,Float _] ->
+                  let ix = (0,nf * nc)
+                  in b_getn1_data_segment n b ix
+              _ -> error "b_fetch"
+  sendMessage (b_query1 b)
+  waitDatum "/b_info" >>= f
+
+-- * Status
+
+-- | Collect server status information.
+serverStatus :: Transport m => m [String]
+serverStatus = liftM statusFormat serverStatusData
+
+-- | Read nominal sample rate of server.
+serverSampleRateNominal :: (Transport m) => m Double
+serverSampleRateNominal = liftM (extractStatusField 7) serverStatusData
+
+-- | Read actual sample rate of server.
+serverSampleRateActual :: (Transport m) => m Double
+serverSampleRateActual = liftM (extractStatusField 8) serverStatusData
+
+-- | Retrieve status data from server.
+serverStatusData :: Transport m => m [Datum]
+serverStatusData = do
+  sendMessage status
+  waitDatum "/status.reply"
diff --git a/Sound/SC3/UGen.hs b/Sound/SC3/UGen.hs
--- a/Sound/SC3/UGen.hs
+++ b/Sound/SC3/UGen.hs
@@ -11,6 +11,7 @@
 import Sound.SC3.UGen.Envelope.Construct as U
 import Sound.SC3.UGen.Enum as U
 import Sound.SC3.UGen.External as U
+import Sound.SC3.UGen.External.SC3_Plugins as U
 import Sound.SC3.UGen.External.ATS as U
 import Sound.SC3.UGen.External.LPC as U
 import Sound.SC3.UGen.FFT as U
@@ -21,10 +22,12 @@
 import Sound.SC3.UGen.IO as U
 import Sound.SC3.UGen.Math as U
 import Sound.SC3.UGen.MachineListening as U
+import Sound.SC3.UGen.Name as U
 import Sound.SC3.UGen.Operator as U
 import Sound.SC3.UGen.Oscillator as U
 import Sound.SC3.UGen.Panner as U
 import Sound.SC3.UGen.Rate as U
+import Sound.SC3.UGen.Type as U
 import Sound.SC3.UGen.UGen as U
 import Sound.SC3.UGen.UId as U
 import Sound.SC3.UGen.Wavelets as U
diff --git a/Sound/SC3/UGen/Analysis.hs b/Sound/SC3/UGen/Analysis.hs
--- a/Sound/SC3/UGen/Analysis.hs
+++ b/Sound/SC3/UGen/Analysis.hs
@@ -2,11 +2,16 @@
 module Sound.SC3.UGen.Analysis where
 
 import Sound.SC3.UGen.Rate
+import Sound.SC3.UGen.Type
 import Sound.SC3.UGen.UGen
 
 -- | Amplitude follower.
 amplitude :: Rate -> UGen -> UGen -> UGen -> UGen
 amplitude r i at rt = mkOsc r "Amplitude" [i, at, rt] 1
+
+-- | Calculates mean average of audio or control rate signal.
+averageOutput :: UGen -> UGen -> UGen
+averageOutput in_ trig_ = mkFilter "AverageOutput" [in_,trig_] 1
 
 -- | Autocorrelation pitch follower.
 pitch :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
diff --git a/Sound/SC3/UGen/Buffer.hs b/Sound/SC3/UGen/Buffer.hs
--- a/Sound/SC3/UGen/Buffer.hs
+++ b/Sound/SC3/UGen/Buffer.hs
@@ -1,11 +1,11 @@
 -- | Unit generators to query, read and write audio buffers.
 module Sound.SC3.UGen.Buffer where
 
-import Sound.SC3.Identifier
 import Sound.SC3.UGen.Enum
+import Sound.SC3.UGen.Identifier
 import Sound.SC3.UGen.Rate
+import Sound.SC3.UGen.Type
 import Sound.SC3.UGen.UGen
-import Sound.SC3.UGen.Utilities
 
 -- * Buffer query unit generators
 
diff --git a/Sound/SC3/UGen/Chaos.hs b/Sound/SC3/UGen/Chaos.hs
--- a/Sound/SC3/UGen/Chaos.hs
+++ b/Sound/SC3/UGen/Chaos.hs
@@ -2,6 +2,7 @@
 module Sound.SC3.UGen.Chaos where
 
 import Sound.SC3.UGen.Rate
+import Sound.SC3.UGen.Type
 import Sound.SC3.UGen.UGen
 
 -- | Chaotic noise.
@@ -27,6 +28,14 @@
 -- | Feedback sine with chaotic phase indexing (no interpolation).
 fbSineN :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
 fbSineN r freq im fb a c xi yi = mkOsc r "FBSineN" [freq, im, fb, a, c, xi, yi] 1
+
+-- | Gingerbreadman map chaotic generator
+gbmanL :: Rate -> UGen -> UGen -> UGen -> UGen
+gbmanL r freq xi yi = mkOscR [AR] r "GbmanL" [freq,xi,yi] 1
+
+-- | Gingerbreadman map chaotic generator
+gbmanN :: Rate -> UGen -> UGen -> UGen -> UGen
+gbmanN r freq xi yi = mkOscR [AR] r "GbmanN" [freq,xi,yi] 1
 
 -- | Henon map chaotic generator (cubic interpolation).
 henonC :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
diff --git a/Sound/SC3/UGen/Composite.hs b/Sound/SC3/UGen/Composite.hs
--- a/Sound/SC3/UGen/Composite.hs
+++ b/Sound/SC3/UGen/Composite.hs
@@ -4,10 +4,10 @@
 import Control.Monad
 import Data.List
 import Data.List.Split
-import Sound.SC3.Identifier
 import Sound.SC3.UGen.Buffer
 import Sound.SC3.UGen.Enum
 import Sound.SC3.UGen.Filter
+import Sound.SC3.UGen.Identifier
 import Sound.SC3.UGen.Information
 import Sound.SC3.UGen.IO
 import Sound.SC3.UGen.Math
@@ -15,19 +15,28 @@
 import Sound.SC3.UGen.Oscillator
 import Sound.SC3.UGen.Panner
 import Sound.SC3.UGen.Rate
-import Sound.SC3.UGen.UGen
+import Sound.SC3.UGen.Type
 
+-- | Dynamic klang, dynamic sine oscillator bank
+dynKlang :: Rate -> UGen -> UGen -> UGen -> UGen
+dynKlang r fs fo s =
+    let gen (f:a:ph:xs) = sinOsc r (f * fs + fo) ph * a + gen xs
+        gen _ = 0
+    in gen (mceChannels s)
+
 -- | Dynamic klank, set of non-fixed resonating filters.
 dynKlank :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-dynKlank i fs fo ds s = gen (mceChannels s)
-    where gen (f:a:d:xs) = ringz i (f * fs + fo) (d * ds) * a + gen xs
-          gen _ = 0
+dynKlank i fs fo ds s =
+    let gen (f:a:d:xs) = ringz i (f * fs + fo) (d * ds) * a + gen xs
+        gen _ = 0
+    in gen (mceChannels s)
 
 -- | Frequency shifter, in terms of Hilbert UGen.
 freqShift :: UGen -> UGen -> UGen -> UGen
-freqShift i f p = mix (h * o)
-    where o = sinOsc AR f (mce [p + 0.5 * pi, p])
-          h = hilbert i
+freqShift i f p =
+    let o = sinOsc AR f (mce [p + 0.5 * pi, p])
+        h = hilbert i
+    in mix (h * o)
 
 -- | Linear interpolating variant on index.
 indexL :: UGen -> UGen -> UGen
@@ -36,6 +45,13 @@
         y = index b (i + 1)
     in linLin (frac i) 0 1 x y
 
+-- | Map from one linear range to another linear range.
+linLin :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+linLin i sl sr dl dr =
+    let m = (dr - dl) / (sr - sl)
+        a = dl - (m * sl)
+    in mulAdd i m a
+
 -- | Collapse possible mce by summing.
 mix :: UGen -> UGen
 mix = sum . mceChannels
@@ -43,16 +59,16 @@
 -- | Mix variant, sum to n channels.
 mixN :: Int -> UGen -> UGen
 mixN n u =
-    let xs = transpose (splitEvery n (mceChannels u))
+    let xs = transpose (chunksOf n (mceChannels u))
     in mce (map sum xs)
 
 -- | Construct and sum a set of UGens.
-mixFill :: Int -> (Int -> UGen) -> UGen
-mixFill n f = mix (mce (map f [0..n-1]))
+mixFill :: Integral n => Int -> (n -> UGen) -> UGen
+mixFill n f = mix (mce (map f [0 .. fromIntegral n - 1]))
 
--- | Monadic variant on mixFill.
-mixFillM :: (Monad m) => Int -> (Int -> m UGen) -> m UGen
-mixFillM n f = liftM sum (mapM f [0 .. n - 1])
+-- | Monad variant on mixFill.
+mixFillM :: (Integral n,Monad m) => Int -> (n -> m UGen) -> m UGen
+mixFillM n f = liftM sum (mapM f [0 .. fromIntegral n - 1])
 
 -- | Variant that is randomly pressed.
 mouseButton' :: Rate -> UGen -> UGen -> UGen -> UGen
@@ -82,12 +98,16 @@
 pmOsc r cf mf pm mp = sinOsc r cf (sinOsc r mf mp * pm)
 
 -- | Scale uni-polar (0,1) input to linear (l,r) range
+--
+-- > map (urange 3 4) [0,0.5,1] == [3,3.5,4]
 urange :: Fractional c => c -> c -> c -> c
 urange l r =
     let m = r - l
     in (+ l) . (* m)
 
 -- | Scale bi-polar (-1,1) input to linear (l,r) range
+--
+-- > map (range 3 4) [-1,0,1] == [3,3.5,4]
 range :: Fractional c => c -> c -> c -> c
 range l r =
     let m = (r - l) * 0.5
@@ -101,12 +121,21 @@
         s1 = select (trunc ix 2 + 1) xs
     in xFade2 s0 s1 (fold2 (ix * 2 - 1) 1) 1
 
+-- | Silence.
+silent :: Int -> UGen
+silent n = let s = dc AR 0 in mce (replicate n s)
+
 -- | Zero indexed audio input buses.
 soundIn :: UGen -> UGen
-soundIn (MCE ns) | all (==1) $ zipWith (-) (tail ns) ns =
-    in' (length ns) AR (numOutputBuses + head ns)
-soundIn n =
-    in' 1 AR (numOutputBuses + n)
+soundIn u =
+    let r = in' 1 AR (numOutputBuses + u)
+    in case u of
+         MCE_U m ->
+             let n = mceProxies m
+             in if all (==1) (zipWith (-) (tail n) n)
+                then in' (length n) AR (numOutputBuses + head n)
+                else r
+         _ -> r
 
 -- | Pan a set of channels across the stereo field.
 splay :: UGen -> UGen -> UGen -> UGen -> Bool -> UGen
diff --git a/Sound/SC3/UGen/Composite/ID.hs b/Sound/SC3/UGen/Composite/ID.hs
--- a/Sound/SC3/UGen/Composite/ID.hs
+++ b/Sound/SC3/UGen/Composite/ID.hs
@@ -1,10 +1,11 @@
 -- | Explicit identifier functions for composite 'UGen's.
 module Sound.SC3.UGen.Composite.ID where
 
-import Sound.SC3.Identifier
 import Sound.SC3.UGen.Demand.ID
 import Sound.SC3.UGen.Filter
+import Sound.SC3.UGen.Identifier
 import Sound.SC3.UGen.Noise.ID
+import Sound.SC3.UGen.Type
 import Sound.SC3.UGen.UGen
 
 -- | Demand rate (:) function.
@@ -18,13 +19,13 @@
 mceN :: UGen -> UGen
 mceN = constant . length . mceChannels
 
--- | Randomly select one of several inputs (initialiastion rate).
-iChoose :: ID m => m -> UGen -> UGen
-iChoose e a = select (iRand e 0 (mceN a)) a
+-- | Randomly select one of a list of UGens (initialiastion rate).
+lchoose :: ID m => m -> [UGen] -> UGen
+lchoose e a = select (iRand e 0 (fromIntegral (length a))) (mce a)
 
--- | 'mce' variant of 'iChoose'.
-iChoose' :: ID m => m -> [UGen] -> UGen
-iChoose' e = iChoose e . mce
+-- | 'mce' variant of 'lchoose'.
+choose :: ID m => m -> UGen -> UGen
+choose e = lchoose e . mceChannels
 
 -- | Randomly select one of several inputs on trigger.
 tChoose :: ID m => m -> UGen -> UGen -> UGen
diff --git a/Sound/SC3/UGen/Composite/Monad.hs b/Sound/SC3/UGen/Composite/Monad.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/UGen/Composite/Monad.hs
@@ -0,0 +1,38 @@
+-- | Monad constructors for composite 'UGen's.
+module Sound.SC3.UGen.Composite.Monad where
+
+import qualified Sound.SC3.UGen.Composite.ID as C
+import Sound.SC3.UGen.Demand.Monad
+import Sound.SC3.UGen.Filter
+import Sound.SC3.UGen.Noise.Monad
+import Sound.SC3.UGen.Type
+import Sound.SC3.UGen.UGen
+import Sound.SC3.UGen.UGen.Lift
+import Sound.SC3.UGen.UId
+
+-- | Demand rate (:) function.
+dcons :: (UId m) => UGen -> UGen -> m UGen
+dcons x xs = do
+  i <- dseq 1 (mce2 0 1)
+  a <- dseq 1 (mce2 x xs)
+  dswitch i a
+
+-- | 'liftU' of 'C.choose'.
+choose :: UId m => UGen -> m UGen
+choose = liftU C.choose
+
+-- | 'liftU' of 'C.lchoose'.
+lchoose :: UId m => [UGen] -> m UGen
+lchoose = liftU C.lchoose
+
+-- | Randomly select one of several inputs.
+tChoose :: (UId m) => UGen -> UGen -> m UGen
+tChoose t a = do
+  r <- tIRand 0 (constant (length (mceChannels a))) t
+  return (select r a)
+
+-- | Randomly select one of several inputs (weighted).
+tWChoose :: (UId m) => UGen -> UGen -> UGen -> UGen -> m UGen
+tWChoose t a w n = do
+  i <- tWindex t n w
+  return (select i a)
diff --git a/Sound/SC3/UGen/Composite/Monadic.hs b/Sound/SC3/UGen/Composite/Monadic.hs
deleted file mode 100644
--- a/Sound/SC3/UGen/Composite/Monadic.hs
+++ /dev/null
@@ -1,37 +0,0 @@
--- | Monadic constructors for composite 'UGen's.
-module Sound.SC3.UGen.Composite.Monadic where
-
-import qualified Sound.SC3.UGen.Composite.ID as C
-import Sound.SC3.UGen.Demand.Monadic
-import Sound.SC3.UGen.Filter
-import Sound.SC3.UGen.Noise.Monadic
-import Sound.SC3.UGen.UGen
-import Sound.SC3.UGen.UGen.Lift
-import Sound.SC3.UGen.UId
-
--- | Demand rate (:) function.
-dcons :: (UId m) => UGen -> UGen -> m UGen
-dcons x xs = do
-  i <- dseq 1 (mce2 0 1)
-  a <- dseq 1 (mce2 x xs)
-  dswitch i a
-
--- | 'liftU' of 'C.iChoose'.
-iChoose :: UId m => UGen -> m UGen
-iChoose = liftU C.iChoose
-
--- | 'liftU' of 'C.iChoose''.
-iChoose' :: UId m => [UGen] -> m UGen
-iChoose' = liftU C.iChoose'
-
--- | Randomly select one of several inputs.
-tChoose :: (UId m) => UGen -> UGen -> m UGen
-tChoose t a = do
-  r <- tIRand 0 (constant (length (mceChannels a))) t
-  return (select r a)
-
--- | Randomly select one of several inputs (weighted).
-tWChoose :: (UId m) => UGen -> UGen -> UGen -> UGen -> m UGen
-tWChoose t a w n = do
-  i <- tWindex t n w
-  return (select i a)
diff --git a/Sound/SC3/UGen/Demand.hs b/Sound/SC3/UGen/Demand.hs
--- a/Sound/SC3/UGen/Demand.hs
+++ b/Sound/SC3/UGen/Demand.hs
@@ -3,12 +3,12 @@
 
 import Sound.SC3.UGen.Enum
 import Sound.SC3.UGen.Rate
+import Sound.SC3.UGen.Type
 import Sound.SC3.UGen.UGen
-import Sound.SC3.UGen.Utilities
 
 -- | Infinte repeat counter for demand rate unit generators.
 dinf :: UGen
-dinf = Constant 9E8
+dinf = constant (9E8::Double)
 
 -- | Demand results from demand rate ugens.
 demand :: UGen -> UGen -> UGen -> UGen
@@ -16,7 +16,7 @@
     let d' = mceChannels d
     in mkFilterKeyed "Demand" 0 (t : r : d') (length d')
 
--- | Demand envlope generator.
+-- | Demand envelope generator.
 demandEnvGen :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> DoneAction -> UGen
 demandEnvGen r l d s c g rst ls lb ts a = mkOsc r "DemandEnvGen" [l, d, s, c, g, rst, ls, lb, ts, from_done_action a] 1
 
diff --git a/Sound/SC3/UGen/Demand/ID.hs b/Sound/SC3/UGen/Demand/ID.hs
--- a/Sound/SC3/UGen/Demand/ID.hs
+++ b/Sound/SC3/UGen/Demand/ID.hs
@@ -1,11 +1,11 @@
 -- | Explicit identifier demand rate 'UGen' functions.
 module Sound.SC3.UGen.Demand.ID where
 
-import Sound.SC3.Identifier
 import Sound.SC3.UGen.Enum
+import Sound.SC3.UGen.Identifier
 import Sound.SC3.UGen.Rate
+import Sound.SC3.UGen.Type
 import Sound.SC3.UGen.UGen
-import Sound.SC3.UGen.Utilities
 
 -- | Buffer demand ugen.
 dbufrd :: ID i => i -> UGen -> UGen -> Loop -> UGen
@@ -61,6 +61,10 @@
 -- | Demand rate series generator.
 dser :: ID i => i -> UGen -> UGen -> UGen
 dser z l array = mkOscMCEId z DR "Dser" [l] array 1
+
+-- | Demand rate sequence shuffler.
+dshuf :: ID i => i -> UGen -> UGen -> UGen
+dshuf z l array = mkOscMCEId z DR "Dshuf" [l] array 1
 
 -- | Demand input replication
 dstutter :: ID i => i -> UGen -> UGen -> UGen
diff --git a/Sound/SC3/UGen/Demand/Monad.hs b/Sound/SC3/UGen/Demand/Monad.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/UGen/Demand/Monad.hs
@@ -0,0 +1,77 @@
+-- | Monad constructors for demand 'UGen's, see also
+-- "Sound.SC3.UGen.Demand.ID".
+module Sound.SC3.UGen.Demand.Monad where
+
+import Sound.SC3.UGen.Demand.ID as D
+import Sound.SC3.UGen.Enum
+import Sound.SC3.UGen.Type
+import Sound.SC3.UGen.UGen.Lift
+import Sound.SC3.UGen.UId
+
+-- | Buffer demand ugen.
+dbufrd :: (UId m) => UGen -> UGen -> Loop -> m UGen
+dbufrd = liftU3 D.dbufrd
+
+-- | Buffer write on demand unit generator.
+dbufwr :: (UId m) => UGen -> UGen -> UGen -> Loop -> m UGen
+dbufwr = liftU4 D.dbufwr
+
+-- | Demand rate white noise.
+dwhite :: (UId m) => UGen -> UGen -> UGen -> m UGen
+dwhite = liftU3 D.dwhite
+
+-- | Demand rate integer white noise.
+diwhite :: (UId m) => UGen -> UGen -> UGen -> m UGen
+diwhite = liftU3 D.diwhite
+
+-- | Demand rate brown noise.
+dbrown :: (UId m) => UGen -> UGen -> UGen -> UGen -> m UGen
+dbrown = liftU4 D.dbrown
+
+-- | Demand rate integer brown noise.
+dibrown :: (UId m) => UGen -> UGen -> UGen -> UGen -> m UGen
+dibrown = liftU4 D.dibrown
+
+-- | Demand rate random selection.
+drand :: (UId m) => UGen -> UGen -> m UGen
+drand = liftU2 D.drand
+
+-- | Demand rate random selection with no immediate repetition.
+dxrand :: (UId m) => UGen -> UGen -> m UGen
+dxrand = liftU2 D.dxrand
+
+-- | Demand rate weighted random sequence generator.
+dwrand :: (UId m) => UGen -> UGen -> UGen -> m UGen
+dwrand = liftU3 D.dwrand
+
+-- | Demand rate arithmetic series.
+dseries :: (UId m) => UGen -> UGen -> UGen -> m UGen
+dseries = liftU3 D.dseries
+
+-- | Demand rate geometric series.
+dgeom :: (UId m) => UGen -> UGen -> UGen -> m UGen
+dgeom = liftU3 D.dgeom
+
+-- | Demand rate sequence generator.
+dseq :: (UId m) => UGen -> UGen -> m UGen
+dseq = liftU2 D.dseq
+
+-- | Demand rate series generator.
+dser :: (UId m) => UGen -> UGen -> m UGen
+dser = liftU2 D.dser
+
+-- | Demand rate sequence shuffler.
+dshuf :: (UId m) => UGen -> UGen -> m UGen
+dshuf = liftU2 D.dshuf
+
+-- | Demand input replication
+dstutter :: (UId m) => UGen -> UGen -> m UGen
+dstutter = liftU2 D.dstutter
+
+-- | Demand rate input switching.
+dswitch1 :: (UId m) => UGen -> UGen -> m UGen
+dswitch1 = liftU2 D.dswitch1
+
+-- | Demand rate input switching.
+dswitch :: (UId m) => UGen -> UGen -> m UGen
+dswitch = liftU2 D.dswitch
diff --git a/Sound/SC3/UGen/Demand/Monadic.hs b/Sound/SC3/UGen/Demand/Monadic.hs
deleted file mode 100644
--- a/Sound/SC3/UGen/Demand/Monadic.hs
+++ /dev/null
@@ -1,73 +0,0 @@
--- | Monadic constructors for demand 'UGen's, see also
--- "Sound.SC3.UGen.Demand.ID".
-module Sound.SC3.UGen.Demand.Monadic where
-
-import Sound.SC3.UGen.UGen
-import Sound.SC3.UGen.UGen.Lift
-import qualified Sound.SC3.UGen.Demand.ID as D
-import Sound.SC3.UGen.UId
-import Sound.SC3.UGen.Enum
-
--- | Buffer demand ugen.
-dbufrd :: (UId m) => UGen -> UGen -> Loop -> m UGen
-dbufrd = liftU3 D.dbufrd
-
--- | Buffer write on demand unit generator.
-dbufwr :: (UId m) => UGen -> UGen -> UGen -> Loop -> m UGen
-dbufwr = liftU4 D.dbufwr
-
--- | Demand rate white noise.
-dwhite :: (UId m) => UGen -> UGen -> UGen -> m UGen
-dwhite = liftU3 D.dwhite
-
--- | Demand rate integer white noise.
-diwhite :: (UId m) => UGen -> UGen -> UGen -> m UGen
-diwhite = liftU3 D.diwhite
-
--- | Demand rate brown noise.
-dbrown :: (UId m) => UGen -> UGen -> UGen -> UGen -> m UGen
-dbrown = liftU4 D.dbrown
-
--- | Demand rate integer brown noise.
-dibrown :: (UId m) => UGen -> UGen -> UGen -> UGen -> m UGen
-dibrown = liftU4 D.dibrown
-
--- | Demand rate random selection.
-drand :: (UId m) => UGen -> UGen -> m UGen
-drand = liftU2 D.drand
-
--- | Demand rate random selection with no immediate repetition.
-dxrand :: (UId m) => UGen -> UGen -> m UGen
-dxrand = liftU2 D.dxrand
-
--- | Demand rate weighted random sequence generator.
-dwrand :: (UId m) => UGen -> UGen -> UGen -> m UGen
-dwrand = liftU3 D.dwrand
-
--- | Demand rate arithmetic series.
-dseries :: (UId m) => UGen -> UGen -> UGen -> m UGen
-dseries = liftU3 D.dseries
-
--- | Demand rate geometric series.
-dgeom :: (UId m) => UGen -> UGen -> UGen -> m UGen
-dgeom = liftU3 D.dgeom
-
--- | Demand rate sequence generator.
-dseq :: (UId m) => UGen -> UGen -> m UGen
-dseq = liftU2 D.dseq
-
--- | Demand rate series generator.
-dser :: (UId m) => UGen -> UGen -> m UGen
-dser = liftU2 D.dser
-
--- | Demand input replication
-dstutter :: (UId m) => UGen -> UGen -> m UGen
-dstutter = liftU2 D.dstutter
-
--- | Demand rate input switching.
-dswitch1 :: (UId m) => UGen -> UGen -> m UGen
-dswitch1 = liftU2 D.dswitch1
-
--- | Demand rate input switching.
-dswitch :: (UId m) => UGen -> UGen -> m UGen
-dswitch = liftU2 D.dswitch
diff --git a/Sound/SC3/UGen/DiskIO.hs b/Sound/SC3/UGen/DiskIO.hs
--- a/Sound/SC3/UGen/DiskIO.hs
+++ b/Sound/SC3/UGen/DiskIO.hs
@@ -3,8 +3,8 @@
 
 import Sound.SC3.UGen.Enum
 import Sound.SC3.UGen.Rate
+import Sound.SC3.UGen.Type
 import Sound.SC3.UGen.UGen
-import Sound.SC3.UGen.Utilities
 
 -- | Stream soundfile from disk.
 --
diff --git a/Sound/SC3/UGen/Enum.hs b/Sound/SC3/UGen/Enum.hs
--- a/Sound/SC3/UGen/Enum.hs
+++ b/Sound/SC3/UGen/Enum.hs
@@ -1,7 +1,8 @@
 -- | Data types for enumerated and non signal unit generator inputs.
 module Sound.SC3.UGen.Enum where
 
-import Sound.SC3.UGen.UGen
+import Sound.SC3.UGen.Envelope.Interpolate
+import Sound.SC3.UGen.Type
 
 -- | Loop indicator input.
 data Loop = Loop
@@ -9,6 +10,14 @@
           | WithLoop UGen
             deriving (Eq, Show)
 
+-- | Resolve 'Loop'.
+from_loop :: Loop -> UGen
+from_loop e =
+    case e of
+      NoLoop -> 0
+      Loop -> 1
+      WithLoop u -> u
+
 -- | Interpolation indicator input.
 data Interpolation = NoInterpolation
                    | LinearInterpolation
@@ -16,6 +25,15 @@
                    | Interpolation UGen
                      deriving (Eq, Show)
 
+-- | Resolve 'Interpolation'.
+from_interpolation :: Interpolation -> UGen
+from_interpolation e =
+    case e of
+      NoInterpolation -> 1
+      LinearInterpolation -> 2
+      CubicInterpolation -> 4
+      Interpolation u -> u
+
 -- | Completion mode indicator input.
 data DoneAction = DoNothing
                 | PauseSynth
@@ -23,19 +41,91 @@
                 | DoneAction UGen
                   deriving (Eq, Show)
 
+-- | Resolve 'DoneAction'.
+from_done_action :: DoneAction -> UGen
+from_done_action e =
+    case e of
+      DoNothing -> 0
+      PauseSynth -> 1
+      RemoveSynth -> 2
+      DoneAction u -> u
+
 -- | Warp interpolation indicator input.
 data Warp = Linear
           | Exponential
           | Warp UGen
             deriving (Eq, Show)
 
+-- | Resolve 'Warp'.
+from_warp :: Warp -> UGen
+from_warp e =
+    case e of
+      Linear -> 0
+      Exponential -> 1
+      Warp u -> u
+
 -- | Envelope curve indicator input.
-data EnvCurve = EnvStep
-              | EnvLin
-              | EnvExp
-              | EnvSin
-              | EnvCos
-              | EnvNum UGen
-              | EnvSqr
-              | EnvCub
-              deriving (Eq, Show)
+data Envelope_Curve a = EnvStep
+                      | EnvLin
+                      | EnvExp
+                      | EnvSin
+                      | EnvCos -- ^ Note: not implemented at SC3
+                      | EnvNum a
+                      | EnvSqr
+                      | EnvCub
+                        deriving (Eq, Show)
+
+type EnvCurve = Envelope_Curve UGen
+
+-- | Convert 'Envelope_Curve' to shape value.
+--
+-- > map env_curve_shape [EnvSin,EnvSqr] == [3,6]
+env_curve_shape :: Num a => Envelope_Curve a -> a
+env_curve_shape e =
+    case e of
+      EnvStep -> 0
+      EnvLin -> 1
+      EnvExp -> 2
+      EnvSin -> 3
+      EnvCos -> 4
+      EnvNum _ -> 5
+      EnvSqr -> 6
+      EnvCub -> 7
+
+-- | The /value/ of 'EnvCurve' is non-zero for 'EnvNum'.
+--
+-- > map env_curve_value [EnvCos,EnvNum 2] == [0,2]
+env_curve_value :: Num a => Envelope_Curve a -> a
+env_curve_value e =
+    case e of
+      EnvNum u -> u
+      _ -> 0
+
+env_curve_interpolation_f :: (Ord t, Floating t) =>
+                             Envelope_Curve t -> Interpolation_F t
+env_curve_interpolation_f c =
+    case c of
+      EnvStep -> step
+      EnvLin -> linear
+      EnvExp -> exponential
+      EnvSin -> sine
+      EnvCos -> error "env_curve_interpolation_f:EnvCos"
+      EnvNum n -> curve n
+      EnvSqr -> squared
+      EnvCub -> cubed
+
+-- | Enumeration of flags for '/b_gen' command.
+data B_Gen = Normalise | Wavetable | Clear
+             deriving (Eq,Enum,Bounded,Show)
+
+-- | 'B_Gen' to bit number.
+--
+-- > map b_gen_bit [minBound .. maxBound]
+b_gen_bit :: B_Gen -> Int
+b_gen_bit = fromEnum
+
+-- | Set of 'B_Gen' to flag.
+--
+-- > b_gen_flag [minBound .. maxBound] == 7
+b_gen_flag :: [B_Gen] -> Int
+b_gen_flag = sum . map ((2 ^) . b_gen_bit)
diff --git a/Sound/SC3/UGen/Envelope.hs b/Sound/SC3/UGen/Envelope.hs
--- a/Sound/SC3/UGen/Envelope.hs
+++ b/Sound/SC3/UGen/Envelope.hs
@@ -1,15 +1,141 @@
 -- | Envelope generators.
 module Sound.SC3.UGen.Envelope where
 
+import Data.List
+import Data.Maybe
 import Sound.SC3.UGen.Enum
 import Sound.SC3.UGen.Rate
+import Sound.SC3.UGen.Type
 import Sound.SC3.UGen.UGen
-import Sound.SC3.UGen.Utilities
 
+-- * Envelope
+
+-- | SC3 envelope segment model
+data Envelope a =
+    Envelope {env_levels :: [a] -- ^ Set of /n/ levels, n is >= 1
+             ,env_times :: [a] -- ^ Set of /n-1/ time intervals
+             ,env_curves :: [Envelope_Curve a] -- ^ Possibly empty curve set
+             ,env_release_node :: Maybe Int -- ^ Maybe index to release node
+             ,env_loop_node :: Maybe Int -- ^ Maybe index to loop node
+             }
+    deriving (Eq,Show)
+
+-- | Duration of 'Envelope', ie. 'sum' '.' 'env_times'.
+envelope_duration :: Num n => Envelope n -> n
+envelope_duration = sum . env_times
+
+-- | Number of segments at 'Envelope', ie. 'length' '.' 'env_times'.
+envelope_n_segments :: (Num n,Integral i) => Envelope n -> i
+envelope_n_segments = genericLength . env_times
+
+-- | Determine which envelope segment a given time /t/ falls in.
+envelope_segment_ix :: (Ord a, Num a) => Envelope a -> a -> Maybe Int
+envelope_segment_ix e t =
+    let d = dx_d (env_times e)
+    in findIndex (>= t) d
+
+-- | A set of start time, start level, end time, end level and curve.
+type Envelope_Segment t = (t,t,t,t,Envelope_Curve t)
+
+-- | Extract envelope segment given at index /i/.
+envelope_segment :: Num t => Envelope t -> Int -> Envelope_Segment t
+envelope_segment e i =
+    let l = env_levels e
+        t = env_times e
+        x0 = l !! i
+        x1 = l !! (i + 1)
+        t0 = (0 : dx_d t) !! i
+        t1 = t0 + t !! i
+        c = envelope_curves e !! i
+    in (t0,x0,t1,x1,c)
+
+-- | Get value for 'Envelope' at time /t/, or zero if /t/ is out of
+-- range.
+envelope_at :: (Ord t, Floating t) => Envelope t -> t -> t
+envelope_at e t =
+    case envelope_segment_ix e t of
+      Just n -> let (t0,x0,t1,x1,c) = envelope_segment e n
+                    d = t1 - t0
+                    t' = (t - t0) / d
+                    f = env_curve_interpolation_f c
+                in f x0 x1 t'
+      Nothing -> 0
+
+-- | Render 'Envelope' to breakpoint set of /n/ places.
+envelope_render :: (Ord t, Floating t, Enum t) => t -> Envelope t -> [(t,t)]
+envelope_render n e =
+    let d = envelope_duration e
+        k = d / (n - 1)
+        t = [0,k .. d]
+    in zip t (map (envelope_at e) t)
+
+-- | Contruct a lookup table of /n/ places from 'Envelope'.
+envelope_table :: (Ord t, Floating t, Enum t) => t -> Envelope t -> [t]
+envelope_table n = map snd . envelope_render n
+
+-- | Variant on 'env_curves' that expands the, possibly empty, user
+-- list by cycling (if not empty) or by filling with 'EnvLin'.
+envelope_curves :: Num a => Envelope a -> [Envelope_Curve a]
+envelope_curves e =
+    let c = env_curves e
+        n = envelope_n_segments e
+    in if null c
+       then replicate n EnvLin
+       else take n (cycle c)
+
+-- | Linear SC3 form of 'Envelope' data.
+envelope_sc3_array :: Num a => Envelope a -> Maybe [a]
+envelope_sc3_array e =
+    let Envelope l t _ rn ln = e
+        n = length t
+        n' = fromIntegral n
+        rn' = fromIntegral (fromMaybe (-99) rn)
+        ln' = fromIntegral (fromMaybe (-99) ln)
+        c = envelope_curves e
+        f i j k = [i,j,env_curve_shape k,env_curve_value k]
+    in case l of
+         l0:l' -> Just (l0 : n' : rn' : ln' : concat (zipWith3 f l' t c))
+         _ -> Nothing
+
+env_is_sustained :: Envelope a -> Bool
+env_is_sustained = isJust . env_release_node
+
+-- | Delay the onset of the envelope.
+env_delay :: Envelope a -> a -> Envelope a
+env_delay (Envelope l t c rn ln) d =
+    let (l0:_) = l
+        l' = l0 : l
+        t' = d : t
+        c' = EnvLin : c
+        rn' = fmap (+ 1) rn
+        ln' = fmap (+ 1) ln
+    in Envelope l' t' c' rn' ln'
+
+-- | Connect releaseNode (or end) to first node of envelope.
+env_circle :: (Num a,Fractional a) => Envelope a -> a -> Envelope_Curve a -> Envelope a
+env_circle (Envelope l t c rn _) tc cc =
+    let z = 1 {- 1 - impulse KR 0 0 -}
+        n = length t
+    in case rn of
+         Nothing -> let l' = 0 : l ++ [0]
+                        t' = z * tc : t ++ [9e8]
+                        c' = cc : take n (cycle c) ++ [EnvLin]
+                        rn' = Just (n + 1)
+                    in Envelope l' t' c' rn' (Just 0)
+         Just i -> let l' = 0 : l
+                       t' = z * tc : t
+                       c' = cc : take n (cycle c)
+                       rn' = Just (i + 1)
+                   in  Envelope l' t' c' rn' (Just 0)
+
+-- * UGen
+
 -- | Segment based envelope generator.
-envGen :: Rate -> UGen -> UGen -> UGen -> UGen -> DoneAction -> [UGen] -> UGen
-envGen r gate lvl bias scale act pts =
-    let i = [gate, lvl, bias, scale, from_done_action act] ++ pts
+envGen :: Rate -> UGen -> UGen -> UGen -> UGen -> DoneAction -> Envelope UGen -> UGen
+envGen r gate lvl bias scale act e =
+    let err = error "envGen: bad Envelope"
+        z = fromMaybe err (envelope_sc3_array e)
+        i = [gate, lvl, bias, scale, from_done_action act] ++ z
     in mkOsc r "EnvGen" i 1
 
 -- | Line generator.
@@ -55,3 +181,14 @@
 -- | Linear envelope generator.
 linen :: UGen -> UGen -> UGen -> UGen -> DoneAction -> UGen
 linen g at sl rt da = mkFilter "Linen" [g, at, sl, rt, from_done_action da] 1
+
+-- * List
+
+-- > d_dx [0,1,3,6] == [0,1,2,3]
+d_dx :: (Num a) => [a] -> [a]
+d_dx l = zipWith (-) l (0:l)
+
+-- > dx_d (d_dx [0,1,3,6]) == [0,1,3,6]
+-- > dx_d [0.5,0.5] == [0.5,1]
+dx_d :: Num n => [n] -> [n]
+dx_d = scanl1 (+)
diff --git a/Sound/SC3/UGen/Envelope/Construct.hs b/Sound/SC3/UGen/Envelope/Construct.hs
--- a/Sound/SC3/UGen/Envelope/Construct.hs
+++ b/Sound/SC3/UGen/Envelope/Construct.hs
@@ -1,35 +1,20 @@
 -- | Functions to generate break point data for standard envelope
 --   types.
-module Sound.SC3.UGen.Envelope.Construct (env
-                                         ,envCoord
-                                         ,envTrapezoid
-                                         ,envPerc', envPerc
-                                         ,envTriangle
-                                         ,envSine
-                                         ,envLinen', envLinen
-                                         ,envADSR, envASR) where
+module Sound.SC3.UGen.Envelope.Construct where
 
-import Sound.SC3.UGen.UGen
 import Sound.SC3.UGen.Math
 import Sound.SC3.UGen.Enum
-
--- | Basic envelope data constructor.  The curve argument are cycled
---   if required.
-env :: [UGen] -> [UGen] -> [EnvCurve] -> UGen -> UGen -> [UGen]
-env [] _   _   _   _  = error "env: illegal specification"
-env (l:ls) tms crv rls lp =
-    let f l' t c = [l', t, env_curve c, env_value c]
-        n = length tms
-        n' = fromIntegral n
-        crv' = take n (cycle crv)
-    in [l, n', rls, lp] ++ concat (zipWith3 f ls tms crv')
+import Sound.SC3.UGen.Envelope
 
 -- | Co-ordinate based static envelope generator.
-envCoord :: [(UGen, UGen)] -> UGen -> UGen -> EnvCurve -> [UGen]
+--
+-- > let e = envCoord [(0,0),(1/4,1),(1,0)] 1 1 EnvLin
+-- > in envelope_sc3_array e == Just [0,2,-99,-99,1,1/4,1,0,0,3/4,1,0]
+envCoord :: Num a => [(a,a)] -> a -> a -> Envelope_Curve a -> Envelope a
 envCoord bp dur amp c =
     let l = map ((* amp) . snd) bp
-        t = map (* dur) (d_dx (map fst bp))
-    in env l t (repeat c) (-1) (-1)
+        t = map (* dur) (tail (d_dx (map fst bp)))
+    in Envelope l t [c] Nothing Nothing
 
 -- | Trapezoidal envelope generator.  The arguments are: 1. @shape@
 -- determines the sustain time as a proportion of @dur@, zero is a
@@ -37,7 +22,7 @@
 -- determines the attack\/decay ratio, zero is an immediate attack and
 -- a slow decay, one a slow attack and an immediate decay;
 -- 3. @duration@ in seconds; 4. @amplitude@ as linear gain.
-envTrapezoid :: UGen -> UGen -> UGen -> UGen -> [UGen]
+envTrapezoid :: (Num a,OrdE a) => a -> a -> a -> a -> Envelope a
 envTrapezoid shape skew dur amp =
     let x1 = skew * (1 - shape)
         bp = [ (0, skew <=* 0)
@@ -46,81 +31,75 @@
              , (1, skew >=* 1) ]
     in envCoord bp dur amp EnvLin
 
--- | Variant 'envPerc' with user specified 'EnvCurve'.
-envPerc' :: UGen -> UGen -> UGen -> (EnvCurve, EnvCurve) -> [UGen]
+-- | Variant 'envPerc' with user specified 'Envelope_Curve a'.
+envPerc' :: Num a => a -> a -> a -> (Envelope_Curve a,Envelope_Curve a) -> Envelope a
 envPerc' atk rls lvl (c0, c1) =
     let c = [c0, c1]
-    in env [0.0, lvl, 0.0] [atk, rls] c (-1.0) (-1.0)
+    in Envelope [0, lvl, 0] [atk, rls] c Nothing Nothing
 
 -- | Percussive envelope, with attack, release, level and curve
 --   inputs.
-envPerc :: UGen -> UGen -> [UGen]
+envPerc :: Num a => a -> a -> Envelope a
 envPerc atk rls =
-    let cn = EnvNum (-4.0)
-    in envPerc' atk rls 1.0 (cn, cn)
+    let cn = EnvNum (-4)
+    in envPerc' atk rls 1 (cn, cn)
 
 -- | Triangular envelope, with duration and level inputs.
-envTriangle :: UGen -> UGen -> [UGen]
+--
+-- > let e = envTriangle 1 0.1
+-- > in envelope_sc3_array e = Just [0,2,-99,-99,0.1,0.5,1,0,0,0.5,1,0]
+envTriangle :: (Num a,Fractional a) => a -> a -> Envelope a
 envTriangle dur lvl =
     let c = replicate 2 EnvLin
-        d = replicate 2 (dur / 2.0)
-    in env [0.0, lvl, 0.0] d c (-1.0) (-1.0)
+        d = replicate 2 (dur / 2)
+    in Envelope [0,lvl,0] d c Nothing Nothing
 
 -- | Sine envelope, with duration and level inputs.
-envSine :: UGen -> UGen -> [UGen]
+--
+-- > let e = envSine 0 0.1
+-- > in envelope_sc3_array e == Just [0,2,-99,-99,0.1,0,3.0,0,0,0,3,0]
+envSine :: (Num a,Fractional a) => a -> a -> Envelope a
 envSine dur lvl =
     let c = replicate 2 EnvSin
-        d = replicate 2 (dur / 2.0)
-    in env [0.0, lvl, 0.0] d c (-1.0) (-1.0)
+        d = replicate 2 (dur / 2)
+    in Envelope [0,lvl,0] d c Nothing Nothing
 
--- | Variant of 'envLinen' with user specified 'EnvCurve'.
-envLinen' :: UGen -> UGen -> UGen -> UGen -> (EnvCurve, EnvCurve, EnvCurve) -> [UGen]
+-- | Variant of 'envLinen' with user specified 'Envelope_Curve a'.
+envLinen' :: Num a => a -> a -> a -> a -> (Envelope_Curve a,Envelope_Curve a,Envelope_Curve a) -> Envelope a
 envLinen' aT sT rT l (c0, c1, c2) =
-    env [0, l, l, 0] [aT, sT, rT] [c0, c1, c2] (-1) (-1)
+    Envelope [0, l, l, 0] [aT, sT, rT] [c0, c1, c2] Nothing Nothing
 
 -- | Linear envelope parameter constructor.
-envLinen :: UGen -> UGen -> UGen -> UGen -> [UGen]
+envLinen :: Num a => a -> a -> a -> a -> Envelope a
 envLinen aT sT rT l =
     let c = (EnvLin, EnvLin, EnvLin)
     in envLinen' aT sT rT l c
 
--- aT = attackTime
--- dT = decayTime
--- sL = sustainLevel
--- rT = releaseTime
--- pL = peakLevel
--- c = curve
--- b = bias
+-- | Parameters for ADSR envelopes.
+data ADSR a = ADSR {attackTime :: a
+                   ,decayTime :: a
+                   ,sustainLevel :: a
+                   ,releaseTime :: a
+                   ,peakLevel :: a
+                   ,curve :: (Envelope_Curve a,Envelope_Curve a,Envelope_Curve a)
+                   ,bias :: a}
 
 -- | Attack, decay, sustain, release envelope parameter constructor.
-envADSR :: UGen -> UGen -> UGen -> UGen -> UGen -> EnvCurve -> UGen -> [UGen]
-envADSR aT dT sL rT pL c b =
+envADSR :: Num a => a -> a -> a -> a -> a -> Envelope_Curve a -> a -> Envelope a
+envADSR aT dT sL rT pL c b = envADSR_r (ADSR aT dT sL rT pL (c,c,c) b)
+
+-- | Record ('ADSR') variant of 'envADSR'.
+envADSR_r :: Num a => ADSR a -> Envelope a
+envADSR_r (ADSR aT dT sL rT pL (c0,c1,c2) b) =
     let l = map (+ b) [0,pL,pL*sL,0]
         t = [aT,dT,rT]
-        c' = [c,c,c]
-    in env l t c' 2 (-1)
+        c = [c0,c1,c2]
+    in Envelope l t c (Just 2) Nothing
 
 -- | Attack, sustain, release envelope parameter constructor.
-envASR :: UGen -> UGen -> UGen -> EnvCurve -> [UGen]
+envASR :: Num a => a -> a -> a -> Envelope_Curve a -> Envelope a
 envASR aT sL rT c =
     let l = [0,sL,0]
         t = [aT,rT]
         c' = [c,c]
-    in env l t c' 1 (-1)
-
-d_dx :: (Num a) => [a] -> [a]
-d_dx xs = zipWith (-) (drop 1 xs) xs
-
-env_curve :: EnvCurve -> UGen
-env_curve EnvStep = Constant 0.0
-env_curve EnvLin = Constant 1.0
-env_curve EnvExp = Constant 2.0
-env_curve EnvSin = Constant 3.0
-env_curve EnvCos = Constant 4.0
-env_curve (EnvNum _) = Constant 5.0
-env_curve EnvSqr = Constant 6.0
-env_curve EnvCub = Constant 7.0
-
-env_value :: EnvCurve -> UGen
-env_value (EnvNum u) = u
-env_value _ = Constant 0.0
+    in Envelope l t c' (Just 1) Nothing
diff --git a/Sound/SC3/UGen/Envelope/Interpolate.hs b/Sound/SC3/UGen/Envelope/Interpolate.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/UGen/Envelope/Interpolate.hs
@@ -0,0 +1,50 @@
+-- | Interpolation function for envelope segments.  Each function
+-- takes three arguments, /x0/ is the left or begin value, /x1/ is the
+-- right or end value, and /t/ is a (0,1) index.
+module Sound.SC3.UGen.Envelope.Interpolate where
+
+type Interpolation_F t = t -> t -> t -> t
+
+step :: Interpolation_F t
+step _ x1 _ = x1
+
+linear :: Num t => Interpolation_F t
+linear x0 x1 t = t * (x1 - x0) + x0
+
+exponential :: Floating t => Interpolation_F t
+exponential x0 x1 t = x0 * ((x1 / x0) ** t)
+
+sine :: Floating t => Interpolation_F t
+sine x0 x1 t = x0 + (x1 - x0) * (- cos (pi * t) * 0.5 + 0.5)
+
+half_pi :: Floating a => a
+half_pi = pi / 2
+
+welch :: (Ord t, Floating t) => Interpolation_F t
+welch x0 x1 t =
+    if x0 < x1
+    then x0 + (x1 - x0) * sin (half_pi * t)
+    else x1 - (x1 - x0) * sin (half_pi - (half_pi * t))
+
+curve :: (Ord t, Floating t) => t -> Interpolation_F t
+curve c x0 x1 t =
+    if abs c < 0.0001
+    then t * (x1 - x0) + x0
+    else let d = 1 - exp c
+             n = 1 - exp (t * c)
+         in x0 + (x1 - x0) * (n/d)
+
+squared :: Floating t => Interpolation_F t
+squared x0 x1 t =
+    let x0' = sqrt x0
+        x1' = sqrt x1
+        l = t * (x1' - x0') + x0'
+    in l * l
+
+cubed :: Floating t => Interpolation_F t
+cubed x0 x1 t =
+    let x0' = x0 ** (1/3)
+        x1' = x1 ** (1/3)
+        l = t * (x1' - x0') + x0'
+    in l * l * l
+
diff --git a/Sound/SC3/UGen/External.hs b/Sound/SC3/UGen/External.hs
--- a/Sound/SC3/UGen/External.hs
+++ b/Sound/SC3/UGen/External.hs
@@ -3,87 +3,40 @@
 module Sound.SC3.UGen.External where
 
 import Sound.SC3.UGen.Rate
+import Sound.SC3.UGen.Type
 import Sound.SC3.UGen.UGen
 
--- | Emulation of the sound generation hardware of the Atari TIA chip.
-atari2600 :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-atari2600 audc0 audc1 audf0 audf1 audv0 audv1 rate = mkOsc AR "Atari2600" [audc0,audc1,audf0,audf1,audv0,audv1,rate] 1
-
--- | Resynthesize sinusoidal ATS analysis data.
-atsSynth :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-atsSynth b np ps pk fp m a = mkOsc AR "AtsSynth" [b, np, ps, pk, fp, m, a] 1
-
--- | Resynthesize sinusoidal and critical noise ATS analysis data.
-atsNoiSynth :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-atsNoiSynth b np ps pk fp sr nr m a nb bs bk = mkOsc AR "AtsNoiSynth" [b, np, ps, pk, fp, sr, nr, m, a, nb, bs, bk] 1
-
--- | Emulation of AY (aka YM) soundchip, used in Spectrum\/Atari.
-ay :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-ay ta tb tc n c va vb vc ef es ct = mkOsc AR "AY" [ta, tb, tc, n, c, va, vb, vc, ef, es, ct] 1
-
--- | Convert frequency value to value appropriate for AY tone inputs.
-ayFreqToTone :: Fractional a => a -> a
-ayFreqToTone f = 110300 / (f - 0.5)
+-- * DFM1
 
 -- | Variant FM synthesis node.
 dfm1 :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
 dfm1 i f r g ty nl = mkFilter "DFM1" [i,f,r,g,ty,nl] 1
 
--- | Phase modulation oscillator matrix.
-fm7 :: [[UGen]] -> [[UGen]] -> UGen
-fm7 ctl m0d = mkOsc AR "FM7" (concat ctl ++ concat m0d) 6
-
--- | Triangular waveguide mesh of a drum-like membrane.
-membraneCircle :: UGen -> UGen -> UGen -> UGen
-membraneCircle i t l = mkOsc AR "MembraneCircle" [i, t, l] 1
-
--- | Triangular waveguide mesh of a drum-like membrane.
-membraneHexagon :: UGen -> UGen -> UGen -> UGen
-membraneHexagon i t l = mkOsc AR "MembraneHexagon" [i, t, l] 1
+-- * f0plugins
 
--- | Metronome
-metro :: Rate -> UGen -> UGen -> UGen
-metro rt bpm nb = mkOsc rt "Metro" [bpm,nb] 1
+-- | Emulation of the sound generation hardware of the Atari TIA chip.
+atari2600 :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+atari2600 audc0 audc1 audf0 audf1 audv0 audv1 rate = mkOsc AR "Atari2600" [audc0,audc1,audf0,audf1,audv0,audv1,rate] 1
 
 -- | POKEY Chip Sound Simulator
 mzPokey :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
 mzPokey f1 c1 f2 c2 f3 c3 f4 c4 ctl = mkOsc AR "MZPokey" [f1,c1,f2,c2,f3,c3,f4,c4,ctl] 1
 
--- | Extract cps, rmso and err signals from LPC data.
-lpcVals :: Rate -> UGen -> UGen -> UGen
-lpcVals r b ptr = mkOsc r "LPCVals" [b, ptr] 3
-
--- | Resynthesize LPC analysis data.
-lpcSynth :: UGen -> UGen -> UGen -> UGen
-lpcSynth b s ptr = mkOsc AR "LPCSynth" [b, s, ptr] 1
-
--- | Invert FFT amplitude data.
-pv_Invert :: UGen -> UGen
-pv_Invert b = mkOsc KR "PV_Invert" [b] 1
-
--- | STK flute model.
-stkFlute :: Rate-> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-stkFlute rt f jd ng vf vg bp tr = mkOsc rt "StkFlute" [f, jd, ng, vf, vg, bp, tr] 1
-
--- | STK modal bar models.
-stkModalBar :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-stkModalBar rt f i sh sp vg vf mx v tr = mkOsc rt "StkModalBar" [f, i, sh, sp, vg, vf, mx, v, tr] 1
+-- * skUG
 
--- | STK bowed string model.
-stkBowed :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-stkBowed rt f pr po vf vg l g at dc = mkOsc rt "StkBowed" [f, pr, po, vf, vg, l, g, at, dc] 1
+-- | Phase modulation oscillator matrix.
+fm7 :: [[UGen]] -> [[UGen]] -> UGen
+fm7 ctl m0d = mkOsc AR "FM7" (concat ctl ++ concat m0d) 6
 
--- | STK mandolin model.
-stkMandolin :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-stkMandolin rt f bs pp dm dt at tr = mkOsc rt "StkMandolin" [f, bs, pp, dm, dt, at, tr] 1 
+-- * PitchDetection
 
--- | STK shaker models.
-stkShakers :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-stkShakers rt i e d o rf tr = mkOsc rt "StkShakers" [i, e, d, o, rf, tr] 1
+-- | Tartini model pitch tracker.
+tartini ::  Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+tartini r input threshold n k overlap smallCutoff = mkOscR [KR] r "Tartini" [input,threshold,n,k,overlap,smallCutoff] 2
 
--- | Vocal simulation due to W. Kaegi.
-vosim :: UGen -> UGen -> UGen -> UGen -> UGen
-vosim t f nc d = mkOsc AR "VOSIM" [t, f, nc, d] 1
+-- | Constant Q transform pitch follower.
+qitch ::  Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+qitch r input databufnum ampThreshold algoflag ampbufnum minfreq maxfreq = mkOscR [KR] r "Qitch" [input,databufnum,ampThreshold,algoflag,ampbufnum,minfreq,maxfreq] 2
 
 -- Local Variables:
 -- truncate-lines:t
diff --git a/Sound/SC3/UGen/External/ATS.hs b/Sound/SC3/UGen/External/ATS.hs
--- a/Sound/SC3/UGen/External/ATS.hs
+++ b/Sound/SC3/UGen/External/ATS.hs
@@ -61,7 +61,7 @@
 
 -- | Extract set of 'ATSFrame's from 'ATS'.
 atsFrames :: ATS -> [ATSFrame]
-atsFrames a = splitEvery (atsFrameLength (atsHeader a)) (atsData a)
+atsFrames a = chunksOf (atsFrameLength (atsHeader a)) (atsData a)
 
 -- Determine endianess and hence decoder.
 get_decoder :: B.ByteString -> B.ByteString -> Double
diff --git a/Sound/SC3/UGen/External/ID.hs b/Sound/SC3/UGen/External/ID.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/UGen/External/ID.hs
@@ -0,0 +1,19 @@
+-- | Non-deterministic external 'UGen's.
+module Sound.SC3.UGen.External.ID where
+
+import Sound.SC3.UGen.Identifier
+import Sound.SC3.UGen.Rate
+import Sound.SC3.UGen.Type
+import Sound.SC3.UGen.UGen
+
+-- | random walk step
+lfBrownNoise0 :: ID a => a -> Rate -> UGen -> UGen -> UGen -> UGen
+lfBrownNoise0 z r freq dev dist = mkOscIdR [AR,KR] z r "LFBrownNoise0" [freq,dev,dist] 1
+
+-- | random walk linear interp
+lfBrownNoise1 :: ID a => a -> Rate -> UGen -> UGen -> UGen -> UGen
+lfBrownNoise1 z r freq dev dist = mkOscIdR [AR,KR] z r "LFBrownNoise1" [freq,dev,dist] 1
+
+-- | random walk cubic interp
+lfBrownNoise2 :: ID a => a -> Rate -> UGen -> UGen -> UGen -> UGen
+lfBrownNoise2 z r freq dev dist = mkOscIdR [AR,KR] z r "LFBrownNoise2" [freq,dev,dist] 1
diff --git a/Sound/SC3/UGen/External/SC3_Plugins.hs b/Sound/SC3/UGen/External/SC3_Plugins.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/UGen/External/SC3_Plugins.hs
@@ -0,0 +1,116 @@
+-- | Bindings to unit generators in sc3-plugins.
+module Sound.SC3.UGen.External.SC3_Plugins where
+
+import Sound.SC3.UGen.Identifier
+import Sound.SC3.UGen.Rate
+import Sound.SC3.UGen.Type
+import Sound.SC3.UGen.UGen
+
+-- * AY
+
+-- | Emulation of AY (aka YM) soundchip, used in Spectrum\/Atari.
+ay :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+ay ta tb tc n c va vb vc ef es ct = mkOsc AR "AY" [ta, tb, tc, n, c, va, vb, vc, ef, es, ct] 1
+
+-- | Convert frequency value to value appropriate for AY tone inputs.
+ayFreqToTone :: Fractional a => a -> a
+ayFreqToTone f = 110300 / (f - 0.5)
+
+-- * Concat
+
+-- | Concatenative cross-synthesis.
+concat' :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+concat' ctl src sz sk sd ml fs zcr lms sc st rs = mkOsc AR "Concat" [ctl,src,sz,sk,sd,ml,fs,zcr,lms,sc,st,rs] 1
+
+-- | Concatenative cross-synthesis (variant).
+concat2 :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+concat2 ctl src sz sk sd ml fs zcr lms sc st rs th = mkOsc AR "Concat2" [ctl,src,sz,sk,sd,ml,fs,zcr,lms,sc,st,rs,th] 1
+
+-- * Distortion
+
+-- | Brown noise.
+disintegrator :: ID a => a -> UGen -> UGen -> UGen -> UGen
+disintegrator z i p m = mkFilterId z "Disintegrator" [i,p,m] 1
+
+-- * Josh
+
+-- | Resynthesize sinusoidal ATS analysis data.
+atsSynth :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+atsSynth b np ps pk fp m a = mkOsc AR "AtsSynth" [b, np, ps, pk, fp, m, a] 1
+
+-- | Resynthesize sinusoidal and critical noise ATS analysis data.
+atsNoiSynth :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+atsNoiSynth b np ps pk fp sr nr m a nb bs bk = mkOsc AR "AtsNoiSynth" [b, np, ps, pk, fp, sr, nr, m, a, nb, bs, bk] 1
+
+-- | Granular synthesis with FM grains.
+fmGrain :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+fmGrain trigger dur carfreq modfreq ix = mkOsc AR "FMGrain" [trigger,dur,carfreq,modfreq,ix] 1
+
+-- | Granular synthesis with FM grains and user supplied envelope.
+fmGrainB :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+fmGrainB trigger dur carfreq modfreq ix e = mkOsc AR "FMGrain" [trigger,dur,carfreq,modfreq,ix,e] 1
+
+-- | Resynthesize LPC analysis data.
+lpcSynth :: UGen -> UGen -> UGen -> UGen
+lpcSynth b s ptr = mkOsc AR "LPCSynth" [b, s, ptr] 1
+
+-- | Extract cps, rmso and err signals from LPC data.
+lpcVals :: Rate -> UGen -> UGen -> UGen
+lpcVals r b ptr = mkOsc r "LPCVals" [b, ptr] 3
+
+-- | Metronome
+metro :: Rate -> UGen -> UGen -> UGen
+metro rt bpm nb = mkOsc rt "Metro" [bpm,nb] 1
+
+-- | Invert FFT amplitude data.
+pv_Invert :: UGen -> UGen
+pv_Invert b = mkOsc KR "PV_Invert" [b] 1
+
+-- * Membrane
+
+-- | Triangular waveguide mesh of a drum-like membrane.
+membraneCircle :: UGen -> UGen -> UGen -> UGen
+membraneCircle i t l = mkOsc AR "MembraneCircle" [i, t, l] 1
+
+-- | Triangular waveguide mesh of a drum-like membrane.
+membraneHexagon :: UGen -> UGen -> UGen -> UGen
+membraneHexagon i t l = mkOsc AR "MembraneHexagon" [i, t, l] 1
+
+-- * NCAnalysisUGens
+
+-- | Tracking Phase Vocoder
+tpv :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+tpv chain windowsize hopsize maxpeaks currentpeaks freqmult tolerance noisefloor = mkOsc AR "TPV" [chain,windowsize,hopsize,maxpeaks,currentpeaks,freqmult,tolerance,noisefloor] 1
+
+-- * Stk
+
+-- | STK bowed string model.
+stkBowed :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+stkBowed rt f pr po vf vg l g at dc = mkOsc rt "StkBowed" [f, pr, po, vf, vg, l, g, at, dc] 1
+
+-- | STK flute model.
+stkFlute :: Rate-> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+stkFlute rt f jd ng vf vg bp tr = mkOsc rt "StkFlute" [f, jd, ng, vf, vg, bp, tr] 1
+
+-- | STK mandolin model.
+stkMandolin :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+stkMandolin rt f bs pp dm dt at tr = mkOsc rt "StkMandolin" [f, bs, pp, dm, dt, at, tr] 1
+
+-- | STK modal bar models.
+stkModalBar :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+stkModalBar rt f i sh sp vg vf mx v tr = mkOsc rt "StkModalBar" [f, i, sh, sp, vg, vf, mx, v, tr] 1
+
+-- | STK shaker models.
+stkShakers :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+stkShakers rt i e d o rf tr = mkOsc rt "StkShakers" [i, e, d, o, rf, tr] 1
+
+-- * VOSIM
+
+-- | Vocal simulation due to W. Kaegi.
+vosim :: UGen -> UGen -> UGen -> UGen -> UGen
+vosim t f nc d = mkOsc AR "VOSIM" [t, f, nc, d] 1
+
+
+-- Local Variables:
+-- truncate-lines:t
+-- End:
diff --git a/Sound/SC3/UGen/FFT.hs b/Sound/SC3/UGen/FFT.hs
--- a/Sound/SC3/UGen/FFT.hs
+++ b/Sound/SC3/UGen/FFT.hs
@@ -1,9 +1,10 @@
 -- | Frequency domain unit generators.
 module Sound.SC3.UGen.FFT where
 
-import Sound.OpenSoundControl (OSC)
-import Sound.SC3.Server.Command (b_gen)
+import Sound.OpenSoundControl
+import Sound.SC3.Server.Command
 import Sound.SC3.UGen.Rate
+import Sound.SC3.UGen.Type
 import Sound.SC3.UGen.UGen
 
 -- | Fast fourier transform.
@@ -33,8 +34,9 @@
 
 -- | Pack demand-rate FFT bin streams into an FFT chain.
 packFFT :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-packFFT b sz from to z mp = mkOscMCE KR "PackFFT" [b, sz, from, to, z, n] mp 1
-    where n = constant (mceDegree mp)
+packFFT b sz from to z mp =
+    let n = constant (mceDegree mp)
+    in mkOscMCE KR "PackFFT" [b, sz, from, to, z, n] mp 1
 
 -- | Format magnitude and phase data data as required for packFFT.
 packFFTSpec :: [UGen] -> [UGen] -> UGen
@@ -85,11 +87,11 @@
 
 -- | FFT onset detector.
 pv_HainsworthFoote :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-pv_HainsworthFoote buf h f thr wait = mkOsc AR "PV_HainsworthFoote" [buf,h,f,thr,wait] 1
+pv_HainsworthFoote buf h f thr wt = mkOsc AR "PV_HainsworthFoote" [buf,h,f,thr,wt] 1
 
 -- | FFT feature detector for onset detection.
 pv_JensenAndersen :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-pv_JensenAndersen buf sc hfe hfc sf thr wait = mkOsc AR "PV_JensenAndersen" [buf,sc,hfe,hfc,sf,thr,wait] 1
+pv_JensenAndersen buf sc hfe hfc sf thr wt = mkOsc AR "PV_JensenAndersen" [buf,sc,hfe,hfc,sf,thr,wt] 1
 
 -- | Pass bins which are a local maximum.
 pv_LocalMax :: UGen -> UGen -> UGen
@@ -181,9 +183,9 @@
     in fft_size * num_partitions
 
 -- | Generate accumulation buffer given time-domain IR buffer and FFT size.
-pc_preparePartConv :: Int -> Int -> Int -> OSC
+pc_preparePartConv :: Int -> Int -> Int -> Message
 pc_preparePartConv b irb fft_size =
-    b_gen b "PreparePartConv" (map fromIntegral [irb, fft_size])
+    b_gen b "PreparePartConv" (map Int [irb, fft_size])
 
 -- | Partitioned convolution.
 partConv :: UGen -> UGen -> UGen -> UGen
diff --git a/Sound/SC3/UGen/FFT/ID.hs b/Sound/SC3/UGen/FFT/ID.hs
--- a/Sound/SC3/UGen/FFT/ID.hs
+++ b/Sound/SC3/UGen/FFT/ID.hs
@@ -1,8 +1,9 @@
 -- | Non-deterministic FFT 'UGen's.
 module Sound.SC3.UGen.FFT.ID where
 
-import Sound.SC3.Identifier
+import Sound.SC3.UGen.Identifier
 import Sound.SC3.UGen.Rate
+import Sound.SC3.UGen.Type
 import Sound.SC3.UGen.UGen
 
 -- | Randomize order of bins.
diff --git a/Sound/SC3/UGen/FFT/Monad.hs b/Sound/SC3/UGen/FFT/Monad.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/UGen/FFT/Monad.hs
@@ -0,0 +1,19 @@
+-- | Monad constructors for non-deterministic FFT 'UGen's.
+module Sound.SC3.UGen.FFT.Monad where
+
+import Sound.SC3.UGen.FFT.ID as F
+import Sound.SC3.UGen.Type
+import Sound.SC3.UGen.UGen.Lift
+import Sound.SC3.UGen.UId
+
+-- | Randomize order of bins.
+pv_BinScramble :: (UId m) => UGen -> UGen -> UGen -> UGen -> m UGen
+pv_BinScramble = liftU4 F.pv_BinScramble
+
+-- | Randomly clear bins.
+pv_RandComb :: (UId m) => UGen -> UGen -> UGen -> m UGen
+pv_RandComb = liftU3 F.pv_RandComb
+
+-- | Cross fade, copying bins in random order.
+pv_RandWipe :: (UId m) => UGen -> UGen -> UGen -> UGen -> m UGen
+pv_RandWipe = liftU4 F.pv_RandWipe
diff --git a/Sound/SC3/UGen/FFT/Monadic.hs b/Sound/SC3/UGen/FFT/Monadic.hs
deleted file mode 100644
--- a/Sound/SC3/UGen/FFT/Monadic.hs
+++ /dev/null
@@ -1,19 +0,0 @@
--- | Monadic constructors for non-deterministic FFT 'UGen's.
-module Sound.SC3.UGen.FFT.Monadic where
-
-import Sound.SC3.UGen.UGen
-import Sound.SC3.UGen.UGen.Lift
-import qualified Sound.SC3.UGen.FFT.ID as F
-import Sound.SC3.UGen.UId
-
--- | Randomize order of bins.
-pv_BinScramble :: (UId m) => UGen -> UGen -> UGen -> UGen -> m UGen
-pv_BinScramble = liftU4 F.pv_BinScramble
-
--- | Randomly clear bins.
-pv_RandComb :: (UId m) => UGen -> UGen -> UGen -> m UGen
-pv_RandComb = liftU3 F.pv_RandComb
-
--- | Cross fade, copying bins in random order.
-pv_RandWipe :: (UId m) => UGen -> UGen -> UGen -> UGen -> m UGen
-pv_RandWipe = liftU4 F.pv_RandWipe
diff --git a/Sound/SC3/UGen/Filter.hs b/Sound/SC3/UGen/Filter.hs
--- a/Sound/SC3/UGen/Filter.hs
+++ b/Sound/SC3/UGen/Filter.hs
@@ -3,8 +3,13 @@
 
 import Data.List
 import Sound.SC3.UGen.Rate
+import Sound.SC3.UGen.Type
 import Sound.SC3.UGen.UGen
 
+-- | Audio to control rate converter.
+a2K ::  UGen -> UGen
+a2K i = mkOscR [KR] KR "A2K" [i] 1
+
 -- | Allpass filter (no interpolation)
 allpassN :: UGen -> UGen -> UGen -> UGen -> UGen
 allpassN i mt dly dcy = mkFilter "AllpassN" [i,mt,dly,dcy] 1
@@ -93,6 +98,14 @@
 delayN :: UGen -> UGen -> UGen -> UGen
 delayN i mt dly = mkFilter "DelayN" [i,mt,dly] 1
 
+-- | Tap a delay line from a DelTapWr UGen
+delTapRd :: UGen -> UGen -> UGen -> UGen -> UGen
+delTapRd buffer phase delTime interp = mkFilter "DelTapRd" [buffer,phase,delTime,interp] 1
+
+-- | Write to a buffer for a DelTapRd UGen
+delTapWr :: Rate -> UGen -> UGen -> UGen
+delTapWr rate buffer in_ = mkOscR [AR,KR] rate "DelTapWr" [buffer,in_] 1
+
 -- | Fold to range.
 fold :: UGen -> UGen -> UGen -> UGen
 fold i j k = mkFilter "Fold" [i,j,k] 1
@@ -145,20 +158,30 @@
 inRange :: UGen -> UGen -> UGen -> UGen
 inRange i lo hi = mkFilter "InRange" [i,lo,hi] 1
 
+-- | Control to audio rate converter.
+k2A :: UGen -> UGen
+k2A i = mkOscR [AR] AR "K2A" [i] 1
+
 -- | Fixed resonator filter bank.
 klank :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen
 klank i fs fp d s = mkFilterMCER [AR] "Klank" [i,fs,fp,d] s 1
 
--- | Format frequency,amplitude and decay time data as required for klank.
+-- | Format frequency, amplitude and decay time data as required for klank.
 klankSpec :: [UGen] -> [UGen] -> [UGen] -> UGen
-klankSpec f a p = mce ((concat . transpose) [f,a,p])
+klankSpec f a dt = mce ((concat . transpose) [f,a,dt])
 
 -- | Variant for non-UGen inputs.
 klankSpec' :: [Double] -> [Double] -> [Double] -> UGen
-klankSpec' f a p =
+klankSpec' f a dt =
     let u = map constant
-    in klankSpec (u f) (u a) (u p)
+    in klankSpec (u f) (u a) (u dt)
 
+-- | Variant of 'klankSpec' for 'MCE' inputs.
+klankSpec_mce :: UGen -> UGen -> UGen -> UGen
+klankSpec_mce f a dt =
+    let m = mceChannels
+    in klankSpec (m f) (m a) (m dt)
+
 -- | Simple averaging filter.
 lag :: UGen -> UGen -> UGen
 lag i t = mkFilter "Lag" [i,t] 1
@@ -199,10 +222,6 @@
 limiter :: UGen -> UGen -> UGen -> UGen
 limiter i l d = mkFilter "Limiter" [i,l,d] 1
 
--- | Map from one linear range to another linear range.
-linLin :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-linLin i sl sh dl dh = mkFilter "LinLin" [i,sl,sh,dl,dh] 1
-
 -- | Map from a linear range to an exponential range.
 linExp :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen
 linExp i sl sh dl dh = mkFilter "LinExp" [i,sl,sh,dl,dh] 1
@@ -346,6 +365,14 @@
 sweep :: UGen -> UGen -> UGen
 sweep t r = mkFilter "Sweep" [t,r] 1
 
+-- | Control rate trigger to audio rate trigger converter
+t2A :: UGen -> UGen -> UGen
+t2A i offset = mkOscR [AR] AR "T2A" [i,offset] 1
+
+-- | Audio rate trigger to control rate trigger converter
+t2K :: UGen -> UGen
+t2K i = mkOscR [KR] KR "T2K" [i] 1
+
 -- | Delay trigger by specified interval.
 tDelay :: UGen -> UGen -> UGen
 tDelay i d = mkFilter "TDelay" [i,d] 1
@@ -373,6 +400,10 @@
 -- | Two zero filter.
 twoZero :: UGen -> UGen -> UGen -> UGen
 twoZero i freq radius = mkFilter "TwoZero" [i,freq,radius] 1
+
+-- | Variable shaped lag.
+varLag :: UGen -> UGen -> UGen -> UGen
+varLag i t s = mkFilter "VarLag" [i,t,s] 1
 
 -- | Wrap to range.
 wrap :: UGen -> UGen -> UGen -> UGen
diff --git a/Sound/SC3/UGen/Granular.hs b/Sound/SC3/UGen/Granular.hs
--- a/Sound/SC3/UGen/Granular.hs
+++ b/Sound/SC3/UGen/Granular.hs
@@ -2,6 +2,7 @@
 module Sound.SC3.UGen.Granular where
 
 import Sound.SC3.UGen.Rate
+import Sound.SC3.UGen.Type
 import Sound.SC3.UGen.UGen
 
 -- | Granular synthesis with sound stored in a buffer.
diff --git a/Sound/SC3/UGen/Help.hs b/Sound/SC3/UGen/Help.hs
--- a/Sound/SC3/UGen/Help.hs
+++ b/Sound/SC3/UGen/Help.hs
@@ -3,32 +3,41 @@
 
 import Control.Exception
 import Control.Monad
-import Data.Char
 import Data.List.Split {- split -}
 import System.IO.Error
 import System.Cmd {- process -}
 import System.Directory {- directory -}
 import System.Environment
-import System.FilePath
+import System.FilePath {- filepath -}
 
+-- | Guarded variant of 'getEnv' with default value.
+get_env_default :: String -> String -> IO String
+get_env_default e k = do
+  r <- tryJust (guard . isDoesNotExistError) (getEnv e)
+  case r of
+    Right v -> return v
+    _ -> return k
+
 -- | Read the environment variable @SC3_HELP@, the default value is
--- @~\/.local\/share\/SuperCollider@.
+-- @~\/.local\/share\/SuperCollider/Help@.
 sc3HelpDirectory :: IO String
 sc3HelpDirectory = do
-  r <- tryJust (guard . isDoesNotExistError) (getEnv "SC3_HELP")
-  case r of
-    Right v -> return v
-    _ -> do h <- getEnv "HOME"
-            return (h </> ".local/share/SuperCollider")
+  h <- getEnv "HOME"
+  let d = h </> ".local/share/SuperCollider/Help"
+  get_env_default "SC3_HELP" d
 
 -- | Locate path to indicated SC3 class help file.
 --
--- > sc3HelpDirectory >>= (flip sc3HelpClassFile) "SinOsc"
+-- > import System.FilePath
+-- >
+-- > d <- sc3HelpDirectory
+-- > h <- sc3HelpClassFile d "SinOsc"
+-- > h == Just (d </> "Classes/SinOsc.html")
 sc3HelpClassFile :: FilePath -> String -> IO (Maybe FilePath)
 sc3HelpClassFile d c = do
   let f = d </> "Classes" </> c <.> "html"
   e <- doesFileExist f
-  if e then return (Just f) else return Nothing
+  return (if e then Just f else Nothing)
 
 -- | Generate path to indicated SC3 operator help file.
 --
@@ -57,8 +66,10 @@
 -- | The name of the local SC3 Help file documenting `u'.  Deletes
 -- @\@@ to allow use on haddock quoted comments.
 --
--- > ugenSC3HelpFile (toSC3Name "Collection.*fill")
--- > ugenSC3HelpFile (toSC3Name "Collection.inject")
+-- > import Sound.SC3.UGen.Name
+-- >
+-- > ugenSC3HelpFile "Collection.*fill"
+-- > ugenSC3HelpFile "Collection.inject"
 -- > ugenSC3HelpFile (toSC3Name "sinOsc")
 ugenSC3HelpFile :: String -> IO FilePath
 ugenSC3HelpFile x = do
@@ -73,25 +84,15 @@
            Just cf' -> return cf'
            Nothing -> error (show ("ugenSC3HelpFile",d,cf,x,s))
 
--- | Convert from hsc3 name to SC3 name.
---
--- > toSC3Name "sinOsc" == "SinOsc"
--- > toSC3Name "lfSaw" == "LFSaw"
--- > toSC3Name "pv_Copy" == "PV_Copy"
-toSC3Name :: String -> String
-toSC3Name nm =
-    case nm of
-      'l':'f':nm' -> "LF"++nm'
-      'p':'v':'_':nm' -> "PV_"++nm'
-      p:q -> toUpper p : q
-      [] -> []
-
--- | Use x-www-browser to view SC3 help file for `u'.
+-- | Use @BROWSER@ or @x-www-browser@ to view SC3 help file for `u'.
 --
+-- > import Sound.SC3.UGen.Name
+-- >
 -- > viewSC3Help (toSC3Name "Collection.*fill")
 -- > viewSC3Help (toSC3Name "Collection.inject")
 -- > viewSC3Help (toSC3Name "sinOsc")
 viewSC3Help :: String -> IO ()
 viewSC3Help u = do
   nm <- ugenSC3HelpFile u
-  void (system ("x-www-browser file://" ++ nm))
+  br <- get_env_default "BROWSER" "x-www-browser"
+  void (rawSystem br ["file://" ++ nm])
diff --git a/Sound/SC3/UGen/ID.hs b/Sound/SC3/UGen/ID.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/UGen/ID.hs
@@ -0,0 +1,12 @@
+-- | Module exporting all of "Sound.SC3" and also the explicit
+-- identifier variants for non-deterministic and non-sharable unit
+-- generators.
+module Sound.SC3.UGen.ID (module I) where
+
+import Sound.SC3.UGen as I
+import Sound.SC3.UGen.Composite.ID as I
+import Sound.SC3.UGen.Demand.ID as I
+import Sound.SC3.UGen.External.ID as I
+import Sound.SC3.UGen.FFT.ID as I
+import Sound.SC3.UGen.Identifier as I
+import Sound.SC3.UGen.Noise.ID as I
diff --git a/Sound/SC3/UGen/IO.hs b/Sound/SC3/UGen/IO.hs
--- a/Sound/SC3/UGen/IO.hs
+++ b/Sound/SC3/UGen/IO.hs
@@ -3,8 +3,8 @@
 
 import Sound.SC3.UGen.Enum
 import Sound.SC3.UGen.Rate
+import Sound.SC3.UGen.Type
 import Sound.SC3.UGen.UGen
-import Sound.SC3.UGen.Utilities
 
 -- | Read signal from an audio or control bus.
 in' :: Int -> Rate -> UGen -> UGen
diff --git a/Sound/SC3/UGen/Identifier.hs b/Sound/SC3/UGen/Identifier.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/UGen/Identifier.hs
@@ -0,0 +1,32 @@
+-- | Typeclass and functions to manage UGen identifiers.
+module Sound.SC3.UGen.Identifier where
+
+import Data.Char
+import qualified Data.Digest.Murmur32 as H {- murmur-hash -}
+
+-- | Typeclass to constrain UGen identifiers.
+class ID a where
+    resolveID :: a -> Int
+
+instance ID Int where
+    resolveID = id
+
+instance ID Integer where
+    resolveID = fromInteger
+
+instance ID Char where
+    resolveID = ord
+
+-- | Hash value to 'Int'.
+hash :: H.Hashable32 a => a -> Int
+hash = fromIntegral . H.asWord32 . H.hash32
+
+-- | Hash 'ID' to 'Int'.
+idHash :: ID a => a -> Int
+idHash = hash . resolveID
+
+-- | Hash 'ID's /p/ and /q/ and sum to form an 'Int'.
+--
+-- > 'a' `joinID` (1::Int) == 149929881
+joinID :: (ID a,ID b) => a -> b -> Int
+joinID p q = idHash p + idHash q
diff --git a/Sound/SC3/UGen/Information.hs b/Sound/SC3/UGen/Information.hs
--- a/Sound/SC3/UGen/Information.hs
+++ b/Sound/SC3/UGen/Information.hs
@@ -2,6 +2,7 @@
 --   environment.
 module Sound.SC3.UGen.Information where
 
+import Sound.SC3.UGen.Type
 import Sound.SC3.UGen.UGen
 
 -- | Sample rate of synthesis server, frames per second.
@@ -47,3 +48,13 @@
 -- | Number of runnings synthesis nodes.
 numRunningSynths :: UGen
 numRunningSynths = mkInfo "NumRunningSynths"
+
+
+-- | Poll value of input UGen when triggered.
+poll :: UGen -> UGen -> UGen -> UGen -> UGen
+poll t i l tr = mkFilter "Poll" ([t,i,tr] ++ unpackLabel l) 0
+
+-- | Variant of 'poll' that generates an 'mrg' value with the input
+-- signal at left.
+poll' :: UGen -> UGen -> UGen -> UGen -> UGen
+poll' t i l tr = mrg [i,poll t i l tr]
diff --git a/Sound/SC3/UGen/MCE.hs b/Sound/SC3/UGen/MCE.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/UGen/MCE.hs
@@ -0,0 +1,47 @@
+-- | Multiple channel expansion.
+module Sound.SC3.UGen.MCE where
+
+-- | Multiple channel expansion.
+data MCE n = MCE_Unit n | MCE_Vector [n]
+             deriving (Eq,Show)
+
+mce_elem :: MCE t -> [t]
+mce_elem m =
+    case m of
+      MCE_Unit e -> [e]
+      MCE_Vector e -> e
+
+-- | Extend 'MCE' to specified degree.
+mce_extend :: Int -> MCE n -> MCE n
+mce_extend n m =
+    case m of
+      MCE_Unit e -> MCE_Vector (replicate n e)
+      MCE_Vector e -> MCE_Vector (take n (cycle e))
+
+mce_map :: (a -> b) -> MCE a -> MCE b
+mce_map f m =
+    case m of
+      MCE_Unit e -> MCE_Unit (f e)
+      MCE_Vector e -> MCE_Vector (map f e)
+
+mce_binop :: (a -> b -> c) -> MCE a -> MCE b -> MCE c
+mce_binop f m1 m2 =
+    case (m1,m2) of
+      (MCE_Unit e1,MCE_Unit e2) -> MCE_Unit (f e1 e2)
+      (MCE_Unit e1,MCE_Vector e2) -> MCE_Vector (zipWith f (repeat e1) e2)
+      (MCE_Vector e1,MCE_Unit e2) -> MCE_Vector (zipWith f e1 (repeat e2))
+      (MCE_Vector e1,MCE_Vector e2) ->
+          let n = max (length e1) (length e2)
+              ext = take n . cycle
+          in MCE_Vector (zipWith f (ext e1) (ext e2))
+
+instance Num n => Num (MCE n) where
+    (+) = mce_binop (+)
+    (*) = mce_binop (*)
+    abs = mce_map abs
+    signum = mce_map signum
+    fromInteger = MCE_Unit . fromInteger
+
+instance Fractional n => Fractional (MCE n) where
+    (/) = mce_binop (/)
+    fromRational = MCE_Unit . fromRational
diff --git a/Sound/SC3/UGen/MachineListening.hs b/Sound/SC3/UGen/MachineListening.hs
--- a/Sound/SC3/UGen/MachineListening.hs
+++ b/Sound/SC3/UGen/MachineListening.hs
@@ -4,6 +4,7 @@
 import Data.List
 import Data.Maybe
 import Sound.SC3.UGen.Rate
+import Sound.SC3.UGen.Type
 import Sound.SC3.UGen.UGen
 
 -- | Autocorrelation beat tracker.
@@ -22,7 +23,7 @@
 onsetType :: Num a => String -> a
 onsetType s =
     let t = ["power", "magsum", "complex", "rcomplex", "phase", "wphase", "mkl"]
-    in fromIntegral (fromMaybe 3 (findIndex (== s) t))
+    in fromIntegral (fromMaybe 3 (elemIndex s t))
 
 -- | Onset detector.
 onsets :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
diff --git a/Sound/SC3/UGen/Math.hs b/Sound/SC3/UGen/Math.hs
--- a/Sound/SC3/UGen/Math.hs
+++ b/Sound/SC3/UGen/Math.hs
@@ -1,9 +1,9 @@
 -- | Non-standard mathematical classes and class instances.
 module Sound.SC3.UGen.Math where
 
-import qualified Foreign.C.Math.Double as M
+import qualified Data.Fixed as F
 import Sound.SC3.UGen.Operator
-import Sound.SC3.UGen.UGen
+import Sound.SC3.UGen.Type
 
 -- The Eq and Ord classes in the Prelude require Bool, hence the name
 -- mangling.  True is 1.0, False is 0.0
@@ -29,10 +29,10 @@
     (>=*) :: a -> a -> a
 
 instance OrdE Double where
-    a <* b = if a < b   then 1.0 else 0.0
-    a <=* b = if a <= b  then 1.0 else 0.0
-    a >* b = if a > b   then 1.0 else 0.0
-    a >=* b = if a >= b  then 1.0 else 0.0
+    a <* b = if a < b then 1.0 else 0.0
+    a <=* b = if a <= b then 1.0 else 0.0
+    a >* b = if a > b then 1.0 else 0.0
+    a >=* b = if a >= b then 1.0 else 0.0
 
 instance OrdE UGen where
     (<*) = mkBinaryOperator LT_ (<*)
@@ -64,34 +64,18 @@
 floorf :: RealFrac a => a -> a
 floorf a = fromIntegral (floor a :: Integer)
 
--- | Variant of 'truncatef' (via libc).
-ftruncate :: Double -> Double
-ftruncate = M.trunc
-
--- | Variant of 'roundf' (via libc).
-fround :: Double -> Double
-fround = M.round
-
--- | Variant of 'ceilingf' (via libc).
-fceiling :: Double -> Double
-fceiling = M.ceil
-
--- | Variant of 'floorf' (via libc).
-ffloor :: Double -> Double
-ffloor = M.floor
-
 instance RealFracE Double where
     properFractionE n =
         let (i,j) = properFraction n
         in (fromIntegral (i::Integer),j)
-    truncateE = ftruncate
-    roundE = fround
-    ceilingE = fceiling
-    floorE = ffloor
+    truncateE = truncatef
+    roundE = roundf
+    ceilingE = ceilingf
+    floorE = floorf
 
 -- | Variant of @SC3@ @roundTo@ function.
 roundTo_ :: Double -> Double -> Double
-roundTo_ a b = if b == 0 then a else ffloor (a/b + 0.5) * b
+roundTo_ a b = if b == 0 then a else floorf (a/b + 0.5) * b
 
 -- | 'UGen' form or 'roundTo_'.
 roundTo :: UGen -> UGen -> UGen
@@ -101,8 +85,8 @@
     properFractionE = error "RealFracE,UGen,partial"
     truncateE = error "RealFracE,UGen,partial"
     roundE i = roundTo i 1
-    ceilingE = mkUnaryOperator Ceil fceiling
-    floorE = mkUnaryOperator Floor ffloor
+    ceilingE = mkUnaryOperator Ceil ceilingf
+    floorE = mkUnaryOperator Floor floorf
 
 -- | 'UGen' form of 'ceilingE'.
 ceil :: UGen -> UGen
@@ -113,6 +97,8 @@
 midiCPS' i = 440.0 * (2.0 ** ((i - 69.0) * (1.0 / 12.0)))
 
 -- | Unary operator class.
+--
+-- > map (floor . (* 1e4) . dbAmp) [-90,-60,-30,0] == [0,10,316,10000]
 class (Floating a, Ord a) => UnaryOp a where
     ampDb :: a -> a
     ampDb a = log10 a * 20
@@ -120,8 +106,6 @@
     asFloat = error "asFloat"
     asInt :: a -> a
     asInt = error "asInt"
-    bitNot :: a -> a
-    bitNot = error "bitNot"
     cpsMIDI :: a -> a
     cpsMIDI a = (log2 (a * (1.0 / 440.0)) * 12.0) + 69.0
     cpsOct :: a -> a
@@ -165,7 +149,6 @@
     ampDb = mkUnaryOperator AmpDb ampDb
     asFloat = mkUnaryOperator AsFloat asFloat
     asInt = mkUnaryOperator AsInt asInt
-    bitNot = mkUnaryOperator BitNot bitNot
     cpsMIDI = mkUnaryOperator CPSMIDI cpsMIDI
     cpsOct = mkUnaryOperator CPSOct cpsOct
     cubed = mkUnaryOperator Cubed cubed
@@ -193,12 +176,6 @@
     amClip a b = if b <= 0 then 0 else a * b
     atan2E :: a -> a -> a
     atan2E a b = atan (b/a)
-    bitAnd :: a -> a -> a
-    bitAnd = error "bitAnd"
-    bitOr :: a -> a -> a
-    bitOr = error "bitOr"
-    bitXOr :: a -> a -> a
-    bitXOr = error "bitXOr"
     clip2 :: a -> a -> a
     clip2 a b = clip_ a (-b) b
     difSqr :: a -> a -> a
@@ -236,10 +213,6 @@
     roundUp :: a -> a -> a
     scaleNeg :: a -> a -> a
     scaleNeg a b = (abs a - a) * b' + a where b' = 0.5 * b + 0.5
-    shiftLeft :: a -> a -> a
-    shiftLeft = error "shiftLeft"
-    shiftRight :: a -> a -> a
-    shiftRight = error "shiftRight"
     sqrDif :: a -> a -> a
     sqrDif a b = (a-b) * (a-b)
     sqrSum :: a -> a -> a
@@ -250,28 +223,41 @@
     thresh a b = if a <  b then 0 else a
     trunc :: a -> a -> a
     trunc = error "trunc"
-    unsignedShift :: a -> a -> a
-    unsignedShift = error "unsignedShift"
     wrap2 :: a -> a -> a
 
--- | SC3 @%@ does not return negative numbers.
+-- | The SC3 @%@ operator is libc fmod function.
+--
+-- > 1.5 % 1.2 // ~= 0.3
+-- > -1.5 % 1.2 // ~= 0.9
+-- > 1.5 % -1.2 // ~= -0.9
+-- > -1.5 % -1.2 // ~= -0.3
+--
+-- > 1.5 `fmod` 1.2 -- ~= 0.3
+-- > (-1.5) `fmod` 1.2 -- ~= 0.9
+-- > 1.5 `fmod` (-1.2) -- ~= -0.9
+-- > (-1.5) `fmod` (-1.2) -- ~= -0.3
+--
+-- 1.2 % 1.5 // ~= 1.2
+-- -1.2 % 1.5 // ~= 0.3
+-- 1.2 % -1.5 // ~= -0.3
+-- -1.2 % -1.5 // ~= -1.2
+--
+-- > 1.2 `fmod` 1.5 -- ~= 1.2
+-- > (-1.2) `fmod` 1.5 -- ~= 0.3
+-- > 1.2 `fmod` (-1.5) -- ~= -0.3
+-- > (-1.2) `fmod` (-1.5) -- ~= -1.2
 fmod :: Double -> Double -> Double
-fmod i j =
-    let k = i `M.fmod` j
-    in if k < 0 then fmod (i + j) j else k
+fmod = F.mod'
 
 instance BinaryOp Double where
     fold2 a b = fold_ a (-b) b
     modE = fmod
-    roundUp a b = if b == 0 then a else fceiling (a/b + 0.5) * b
+    roundUp a b = if b == 0 then a else ceilingf (a/b + 0.5) * b
     wrap2 a b = wrap_ a (-b) b
 
 instance BinaryOp UGen where
     iDiv = mkBinaryOperator IDiv iDiv
     modE = mkBinaryOperator Mod fmod
-    bitAnd = mkBinaryOperator BitAnd bitAnd
-    bitOr = mkBinaryOperator BitOr bitOr
-    bitXOr = mkBinaryOperator BitXor bitXOr
     lcmE = mkBinaryOperator LCM lcmE
     gcdE = mkBinaryOperator GCD gcdE
     roundUp = mkBinaryOperator RoundUp roundUp
@@ -279,9 +265,6 @@
     atan2E = mkBinaryOperator Atan2 atan2E
     hypot = mkBinaryOperator Hypot hypot
     hypotx = mkBinaryOperator Hypotx hypotx
-    shiftLeft = mkBinaryOperator ShiftLeft shiftLeft
-    shiftRight = mkBinaryOperator ShiftRight shiftRight
-    unsignedShift = mkBinaryOperator UnsignedShift unsignedShift
     fill = mkBinaryOperator Fill fill
     ring1 = mkBinaryOperator Ring1 ring1
     ring2 = mkBinaryOperator Ring2 ring2
@@ -311,7 +294,7 @@
     let r = j - i
     in if k >= i && k <= j
        then k
-       else k - r * ffloor ((k-i) / r)
+       else k - r * floorf ((k-i) / r)
 
 -- | Generic variant of 'wrap''.
 --
diff --git a/Sound/SC3/UGen/Monad.hs b/Sound/SC3/UGen/Monad.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/UGen/Monad.hs
@@ -0,0 +1,15 @@
+-- | Module exporting all of "Sound.SC3.UGen" and also the monad
+-- constructor variants for non-deterministic and non-sharable unit
+-- generators.
+module Sound.SC3.UGen.Monad (module M,clone) where
+
+import Control.Monad
+import Sound.SC3.UGen as M
+import Sound.SC3.UGen.Composite.Monad as M
+import Sound.SC3.UGen.Demand.Monad as M
+import Sound.SC3.UGen.FFT.Monad as M
+import Sound.SC3.UGen.Noise.Monad as M
+
+-- | Clone a unit generator (mce . replicateM).
+clone :: (UId m) => Int -> m UGen -> m UGen
+clone n = liftM mce . replicateM n
diff --git a/Sound/SC3/UGen/Name.hs b/Sound/SC3/UGen/Name.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/UGen/Name.hs
@@ -0,0 +1,37 @@
+-- | Functions to normalise UGen names.
+module Sound.SC3.UGen.Name where
+
+import Data.Char
+
+-- | Convert from @hsc3@ name to @SC3@ name.
+--
+-- > toSC3Name "sinOsc" == "SinOsc"
+-- > toSC3Name "lfSaw" == "LFSaw"
+-- > toSC3Name "pv_Copy" == "PV_Copy"
+-- > map toSC3Name ["bpf","fft","tpv"] == ["BPF","FFT","TPV"]
+toSC3Name :: String -> String
+toSC3Name nm =
+    case nm of
+      'l':'f':nm' -> "LF" ++ nm'
+      'p':'v':'_':nm' -> "PV_" ++ nm'
+      p:q -> if all isLower nm && length nm <= 3
+             then map toUpper nm
+             else toUpper p : q
+      [] -> []
+
+-- | Inverse of 'toSC3Name'.
+--
+-- > let nm = ["SinOsc","LFSaw","PV_Copy"]
+-- > in map fromSC3Name nm == ["sinOsc","lfSaw","pv_Copy"]
+--
+-- > map fromSC3Name ["BPF","FFT","TPV"] == ["bpf","fft","tpv"]
+fromSC3Name :: String -> String
+fromSC3Name nm =
+    case nm of
+      'L':'F':nm' -> "lf"++nm'
+      'P':'V':'_':nm' -> "pv_"++nm'
+      p:q -> if all isUpper nm && length nm <= 3
+             then map toLower nm
+             else toLower p : q
+      [] -> []
+
diff --git a/Sound/SC3/UGen/Noise/ID.hs b/Sound/SC3/UGen/Noise/ID.hs
--- a/Sound/SC3/UGen/Noise/ID.hs
+++ b/Sound/SC3/UGen/Noise/ID.hs
@@ -1,8 +1,9 @@
 -- | Non-deterministic noise 'UGen's.
 module Sound.SC3.UGen.Noise.ID where
 
-import Sound.SC3.Identifier
+import Sound.SC3.UGen.Identifier
 import Sound.SC3.UGen.Rate
+import Sound.SC3.UGen.Type
 import Sound.SC3.UGen.UGen
 
 -- | Brown noise.
diff --git a/Sound/SC3/UGen/Noise/Monad.hs b/Sound/SC3/UGen/Noise/Monad.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/UGen/Noise/Monad.hs
@@ -0,0 +1,112 @@
+-- | Monad constructors for noise 'UGen's.
+module Sound.SC3.UGen.Noise.Monad where
+
+import Sound.SC3.UGen.Noise.ID as N
+import Sound.SC3.UGen.Rate
+import Sound.SC3.UGen.Type
+import Sound.SC3.UGen.UGen.Lift
+import Sound.SC3.UGen.UId
+
+-- | Brown noise.
+brownNoise :: (UId m) => Rate -> m UGen
+brownNoise = liftU N.brownNoise
+
+-- | Clip noise.
+clipNoise :: (UId m) => Rate -> m UGen
+clipNoise = liftU N.clipNoise
+
+-- | Randomly pass or block triggers.
+coinGate :: (UId m) => UGen -> UGen -> m UGen
+coinGate = liftU2 N.coinGate
+
+-- | Random impulses in (-1, 1).
+dust2 :: (UId m) => Rate -> UGen -> m UGen
+dust2 = liftU2 N.dust2
+
+-- | Random impulse in (0,1).
+dust :: (UId m) => Rate -> UGen -> m UGen
+dust = liftU2 N.dust
+
+-- | Random value in exponential distribution.
+expRand :: (UId m) => UGen -> UGen -> m UGen
+expRand = liftU2 N.expRand
+
+-- | Gray noise.
+grayNoise :: (UId m) => Rate -> m UGen
+grayNoise = liftU N.grayNoise
+
+-- | Random integer in uniform distribution.
+iRand :: (UId m) => UGen -> UGen -> m UGen
+iRand = liftU2 N.iRand
+
+-- | Clip noise.
+lfClipNoise :: (UId m) => Rate -> UGen -> m UGen
+lfClipNoise = liftU2 N.lfClipNoise
+
+-- | Dynamic clip noise.
+lfdClipNoise :: (UId m) => Rate -> UGen -> m UGen
+lfdClipNoise = liftU2 N.lfdClipNoise
+
+-- | Dynamic step noise.
+lfdNoise0 :: (UId m) => Rate -> UGen -> m UGen
+lfdNoise0 = liftU2 N.lfdNoise0
+
+-- | Dynamic ramp noise.
+lfdNoise1 :: (UId m) => Rate -> UGen -> m UGen
+lfdNoise1 = liftU2 N.lfdNoise1
+
+-- | Dynamic quadratic noise
+lfdNoise2 :: (UId m) => Rate -> UGen -> m UGen
+lfdNoise2 = liftU2 N.lfdNoise2
+
+-- | Dynamic cubic noise
+lfdNoise3 :: (UId m) => Rate -> UGen -> m UGen
+lfdNoise3 = liftU2 N.lfdNoise3
+
+-- | Step noise.
+lfNoise0 :: (UId m) => Rate -> UGen -> m UGen
+lfNoise0 = liftU2 N.lfNoise0
+
+-- | Ramp noise.
+lfNoise1 :: (UId m) => Rate -> UGen -> m UGen
+lfNoise1 = liftU2 N.lfNoise1
+
+-- | Quadratic noise.
+lfNoise2 :: (UId m) => Rate -> UGen -> m UGen
+lfNoise2 = liftU2 N.lfNoise2
+
+-- | Random value in skewed linear distribution.
+linRand :: (UId m) => UGen -> UGen -> UGen -> m UGen
+linRand = liftU3 N.linRand
+
+-- | Random value in sum of n linear distribution.
+nRand :: (UId m) => UGen -> UGen -> UGen -> m UGen
+nRand = liftU3 N.nRand
+
+-- | Pink noise.
+pinkNoise :: (UId m) => Rate -> m UGen
+pinkNoise = liftU N.pinkNoise
+
+-- | Random value in uniform distribution.
+rand :: (UId m) => UGen -> UGen -> m UGen
+rand = liftU2 N.rand
+
+-- | Random value in exponential distribution on trigger.
+tExpRand :: (UId m) => UGen -> UGen -> UGen -> m UGen
+tExpRand = liftU3 N.tExpRand
+
+-- | Random integer in uniform distribution on trigger.
+tIRand :: (UId m) => UGen -> UGen -> UGen -> m UGen
+tIRand = liftU3 N.tIRand
+
+-- | Random value in uniform distribution on trigger.
+tRand :: (UId m) => UGen -> UGen -> UGen -> m UGen
+tRand = liftU3 N.tRand
+
+-- | Triggered windex.
+tWindex :: (UId m) => UGen -> UGen -> UGen -> m UGen
+tWindex = liftU3 N.tWindex
+
+-- | White noise.
+whiteNoise :: (UId m) => Rate -> m UGen
+whiteNoise = liftU N.whiteNoise
diff --git a/Sound/SC3/UGen/Noise/Monadic.hs b/Sound/SC3/UGen/Noise/Monadic.hs
deleted file mode 100644
--- a/Sound/SC3/UGen/Noise/Monadic.hs
+++ /dev/null
@@ -1,112 +0,0 @@
--- | Monadic constructors for noise 'UGen's.
-module Sound.SC3.UGen.Noise.Monadic where
-
-import Sound.SC3.UGen.Rate
-import Sound.SC3.UGen.UGen
-import Sound.SC3.UGen.UGen.Lift
-import qualified Sound.SC3.UGen.Noise.ID as N
-import Sound.SC3.UGen.UId
-
--- | Brown noise.
-brownNoise :: (UId m) => Rate -> m UGen
-brownNoise = liftU N.brownNoise
-
--- | Clip noise.
-clipNoise :: (UId m) => Rate -> m UGen
-clipNoise = liftU N.clipNoise
-
--- | Randomly pass or block triggers.
-coinGate :: (UId m) => UGen -> UGen -> m UGen
-coinGate = liftU2 N.coinGate
-
--- | Random impulses in (-1, 1).
-dust2 :: (UId m) => Rate -> UGen -> m UGen
-dust2 = liftU2 N.dust2
-
--- | Random impulse in (0,1).
-dust :: (UId m) => Rate -> UGen -> m UGen
-dust = liftU2 N.dust
-
--- | Random value in exponential distribution.
-expRand :: (UId m) => UGen -> UGen -> m UGen
-expRand = liftU2 N.expRand
-
--- | Gray noise.
-grayNoise :: (UId m) => Rate -> m UGen
-grayNoise = liftU N.grayNoise
-
--- | Random integer in uniform distribution.
-iRand :: (UId m) => UGen -> UGen -> m UGen
-iRand = liftU2 N.iRand
-
--- | Clip noise.
-lfClipNoise :: (UId m) => Rate -> UGen -> m UGen
-lfClipNoise = liftU2 N.lfClipNoise
-
--- | Dynamic clip noise.
-lfdClipNoise :: (UId m) => Rate -> UGen -> m UGen
-lfdClipNoise = liftU2 N.lfdClipNoise
-
--- | Dynamic step noise.
-lfdNoise0 :: (UId m) => Rate -> UGen -> m UGen
-lfdNoise0 = liftU2 N.lfdNoise0
-
--- | Dynamic ramp noise.
-lfdNoise1 :: (UId m) => Rate -> UGen -> m UGen
-lfdNoise1 = liftU2 N.lfdNoise1
-
--- | Dynamic quadratic noise
-lfdNoise2 :: (UId m) => Rate -> UGen -> m UGen
-lfdNoise2 = liftU2 N.lfdNoise2
-
--- | Dynamic cubic noise
-lfdNoise3 :: (UId m) => Rate -> UGen -> m UGen
-lfdNoise3 = liftU2 N.lfdNoise3
-
--- | Step noise.
-lfNoise0 :: (UId m) => Rate -> UGen -> m UGen
-lfNoise0 = liftU2 N.lfNoise0
-
--- | Ramp noise.
-lfNoise1 :: (UId m) => Rate -> UGen -> m UGen
-lfNoise1 = liftU2 N.lfNoise1
-
--- | Quadratic noise.
-lfNoise2 :: (UId m) => Rate -> UGen -> m UGen
-lfNoise2 = liftU2 N.lfNoise2
-
--- | Random value in skewed linear distribution.
-linRand :: (UId m) => UGen -> UGen -> UGen -> m UGen
-linRand = liftU3 N.linRand
-
--- | Random value in sum of n linear distribution.
-nRand :: (UId m) => UGen -> UGen -> UGen -> m UGen
-nRand = liftU3 N.nRand
-
--- | Pink noise.
-pinkNoise :: (UId m) => Rate -> m UGen
-pinkNoise = liftU N.pinkNoise
-
--- | Random value in uniform distribution.
-rand :: (UId m) => UGen -> UGen -> m UGen
-rand = liftU2 N.rand
-
--- | Random value in exponential distribution on trigger.
-tExpRand :: (UId m) => UGen -> UGen -> UGen -> m UGen
-tExpRand = liftU3 N.tExpRand
-
--- | Random integer in uniform distribution on trigger.
-tIRand :: (UId m) => UGen -> UGen -> UGen -> m UGen
-tIRand = liftU3 N.tIRand
-
--- | Random value in uniform distribution on trigger.
-tRand :: (UId m) => UGen -> UGen -> UGen -> m UGen
-tRand = liftU3 N.tRand
-
--- | Triggered windex.
-tWindex :: (UId m) => UGen -> UGen -> UGen -> m UGen
-tWindex = liftU3 N.tWindex
-
--- | White noise.
-whiteNoise :: (UId m) => Rate -> m UGen
-whiteNoise = liftU N.whiteNoise
diff --git a/Sound/SC3/UGen/Operator.hs b/Sound/SC3/UGen/Operator.hs
--- a/Sound/SC3/UGen/Operator.hs
+++ b/Sound/SC3/UGen/Operator.hs
@@ -1,7 +1,9 @@
 -- | Enumerations of the unary and binary math unit generators.
-module Sound.SC3.UGen.Operator (Unary(..), unaryName,
-                                Binary(..), binaryName) where
+module Sound.SC3.UGen.Operator where
 
+import Data.Maybe
+import Data.List
+
 -- | Enumeration of @SC3@ unary operator UGens.
 data Unary  = Neg
             | Not
@@ -57,7 +59,7 @@
             | TriWindow
             | Ramp
             | SCurve
-              deriving (Eq, Show, Enum)
+              deriving (Eq,Show,Enum,Read)
 
 -- | Enumeration of @SC3@ unary operator UGens.
 data Binary = Add
@@ -109,25 +111,58 @@
             | FirstArg
             | RandRange
             | ExpRandRange
-              deriving (Eq, Show, Enum)
+              deriving (Eq,Show,Enum,Read)
 
--- | Provide symbolic names for standard unary operators.
+-- | Table of symbolic names for standard unary operators.
+unaryTable :: [(Int,String)]
+unaryTable = [(0,"-")]
+
+-- | Lookup possibly symbolic name for standard unary operators.
 unaryName :: Int -> String
-unaryName   0 = "-"
-unaryName   n = show (toEnum n :: Unary)
+unaryName n =
+    let s = show (toEnum n :: Unary)
+    in fromMaybe s (lookup n unaryTable)
 
--- | Provide symbolic names for standard binary operators.
+-- | Table of symbolic names for standard binary operators.
+binaryTable :: [(Int,String)]
+binaryTable =
+    [(0,"+")
+    ,(1,"-")
+    ,(2,"*")
+    ,(4,"/")
+    ,(5,"%")
+    ,(6,"==")
+    ,(7,"/=")
+    ,(8,"<")
+    ,(9,">")
+    ,(10,"<=")
+    ,(11,">=")
+    ,(25,"**")]
+
+-- | Lookup possibly symbolic name for standard binary operators.
+--
+-- > map binaryName [1,2,8] == ["-","*","<"]
 binaryName :: Int -> String
-binaryName  0 = "+"
-binaryName  1 = "-"
-binaryName  2 = "*"
-binaryName  4 = "/"
-binaryName  5 = "%"
-binaryName  6 = "=="
-binaryName  7 = "/="
-binaryName  8 = "<"
-binaryName  9 = ">"
-binaryName 10 = "<="
-binaryName 11 = ">="
-binaryName 25 = "**"
-binaryName n  = show (toEnum n :: Binary)
+binaryName n =
+    let s = show (toEnum n :: Binary)
+    in fromMaybe s (lookup n binaryTable)
+
+-- | Reverse 'lookup'.
+rlookup :: Eq b => b -> [(a,b)] -> Maybe a
+rlookup x = fmap fst . find ((== x) . snd)
+
+-- | Given name of binary operator derive index.
+--
+-- > map binaryIndex ["*","Mul","Ring1"] == [2,2,30]
+binaryIndex :: String -> Int
+binaryIndex nm =
+    let e = fromEnum (read nm :: Binary)
+    in fromMaybe e (rlookup nm binaryTable)
+
+-- | Given name of unary operator derive index.
+--
+-- > map unaryIndex ["-","Neg","Cubed"] == [0,0,13]
+unaryIndex :: String -> Int
+unaryIndex nm =
+    let e = fromEnum (read nm :: Unary)
+    in fromMaybe e (rlookup nm unaryTable)
diff --git a/Sound/SC3/UGen/Oscillator.hs b/Sound/SC3/UGen/Oscillator.hs
--- a/Sound/SC3/UGen/Oscillator.hs
+++ b/Sound/SC3/UGen/Oscillator.hs
@@ -2,7 +2,9 @@
 module Sound.SC3.UGen.Oscillator where
 
 import Data.List
+import Sound.SC3.UGen.Enum
 import Sound.SC3.UGen.Rate
+import Sound.SC3.UGen.Type
 import Sound.SC3.UGen.UGen
 
 -- | Band Limited ImPulse generator.
@@ -44,20 +46,26 @@
 klangSpec :: [UGen] -> [UGen] -> [UGen] -> UGen
 klangSpec f a p = mce ((concat . transpose) [f, a, p])
 
--- | Variant for non-UGen inputs.
+-- | Variant of 'klangSpec' for non-UGen inputs.
 klangSpec' :: [Double] -> [Double] -> [Double] -> UGen
 klangSpec' f a p =
     let u = map constant
     in klangSpec (u f) (u a) (u p)
 
--- | Upsample control rate signal to audio rate.
-k2A :: UGen -> UGen
-k2A i = mkOsc AR "K2A" [i] 1
+-- | Variant of 'klangSpec' for 'MCE' inputs.
+klangSpec_mce :: UGen -> UGen -> UGen -> UGen
+klangSpec_mce f a p =
+    let m = mceChannels
+    in klangSpec (m f) (m a) (m p)
 
 -- | A sine like shape made of two cubic pieces.
 lfCub :: Rate -> UGen -> UGen -> UGen
 lfCub r freq phase = mkOsc r "LFCub" [freq, phase] 1
 
+-- | Gaussian function oscillator
+lfGauss ::  Rate -> UGen -> UGen -> UGen -> Loop -> DoneAction -> UGen
+lfGauss r duration width iphase loop doneAction = mkOscR [AR,KR] r "LFGauss" [duration,width,iphase,from_loop loop,from_done_action doneAction] 1
+
 -- | A sine like shape made of two cubic pieces.
 lfPar :: Rate -> UGen -> UGen -> UGen
 lfPar r freq phase = mkOsc r "LFPar" [freq, phase] 1
@@ -86,14 +94,14 @@
 saw :: Rate -> UGen -> UGen
 saw r freq = mkOscR [AR,KR] r "Saw" [freq] 1
 
--- | Silence.
-silent :: Int -> UGen
-silent = mkOsc AR "Silent" []
-
 -- | Sine oscillator.
 sinOsc :: Rate -> UGen -> UGen -> UGen
 sinOsc r freq phase = mkOsc r "SinOsc" [freq, phase] 1
 
+-- | Feedback FM oscillator.
+sinOscFB :: Rate -> UGen -> UGen -> UGen
+sinOscFB r freq feedback = mkOscR [AR,KR] r "SinOscFB" [freq,feedback] 1
+
 -- | Sawtooth oscillator hard synched to a fundamental.
 syncSaw :: Rate -> UGen -> UGen -> UGen
 syncSaw r syncFreq sawFreq = mkOsc r "SyncSaw" [syncFreq, sawFreq] 1
@@ -101,6 +109,10 @@
 -- | Variable duty sawtooth oscillator.
 varSaw :: Rate -> UGen -> UGen -> UGen -> UGen
 varSaw r freq iphase width = mkOsc r "VarSaw" [freq, iphase, width] 1
+
+-- | The Vibrato oscillator models a slow frequency modulation.
+vibrato :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+vibrato r freq rate depth delay onset rateVariation depthVariation iphase = mkOscR [AR,KR] r "Vibrato" [freq,rate,depth,delay,onset,rateVariation,depthVariation,iphase] 1
 
 -- Local Variables:
 -- truncate-lines:t
diff --git a/Sound/SC3/UGen/Panner.hs b/Sound/SC3/UGen/Panner.hs
--- a/Sound/SC3/UGen/Panner.hs
+++ b/Sound/SC3/UGen/Panner.hs
@@ -1,6 +1,7 @@
 -- | Sound field location and analysis unit generators.
 module Sound.SC3.UGen.Panner where
 
+import Sound.SC3.UGen.Type
 import Sound.SC3.UGen.UGen
 
 -- | Two channel equal power panner.
@@ -36,8 +37,8 @@
 biPanB2 inA inB azimuth gain = mkFilter "BiPanB2" [inA, inB, azimuth, gain] 3
 
 -- | 2D Ambisonic B-format decoder.
-decodeB2 :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-decodeB2 nc w x y o = mkFilterMCE "DecodeB2" [w, x, y, o] nc 0
+decodeB2 :: Int -> UGen -> UGen -> UGen -> UGen -> UGen
+decodeB2 nc w x y o = mkFilter "DecodeB2" [w,x,y,o] nc
 
 -- | Azimuth panner.
 panAz :: Int -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
diff --git a/Sound/SC3/UGen/Plain.hs b/Sound/SC3/UGen/Plain.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/UGen/Plain.hs
@@ -0,0 +1,50 @@
+-- | Plain UGen constructor functions.
+--
+-- > let {s = ugen "SinOsc" AR [440,0] 1
+-- >     ;m = binop "*" AR s 0.1
+-- >     ;o = ugen "Out" AR [0,m] 0}
+-- > in Sound.SC3.Server.Play.audition o >> Sound.SC3.UGen.Dot.draw o
+--
+-- > audition (out 0 (sinOsc AR 440 0 * 0.1))
+module Sound.SC3.UGen.Plain where
+
+import Sound.SC3.UGen.Operator
+import Sound.SC3.UGen.Rate
+import Sound.SC3.UGen.Type
+
+-- | Variant of 'mkUGen'.
+mk_plain :: Rate -> String -> [UGen] -> Int -> Special -> UGenId -> UGen
+mk_plain r = mkUGen Nothing all_rates (Just r)
+
+-- | Construct unary operator, the name can textual or symbolic.
+--
+-- > uop "-" AR 1 == uop "Neg" AR 1
+uop :: String -> Rate -> UGen -> UGen
+uop nm r p =
+    let s = unaryIndex nm
+    in mk_plain r "UnaryOpUGen" [p] 1 (Special s) NoId
+
+-- | Construct binary operator, the name can textual or symbolic.
+--
+-- > binop "*" AR 1 2 == binop "Mul" AR 1 2
+-- > binop "*" AR (ugen "SinOsc" AR [440,0] 1) 0.1 == sinOsc AR 440 0 * 0.1
+-- > ugenName (binop "*" AR 1 2) == "BinaryOpUGen"
+binop :: String -> Rate -> UGen -> UGen -> UGen
+binop nm r p q =
+    let s = binaryIndex nm
+    in mk_plain r "BinaryOpUGen" [p,q] 1 (Special s) NoId
+
+-- | Construct deterministic UGen.
+--
+-- > let o = ugen "SinOsc" AR [440,0] 1
+-- > o == sinOsc AR 440 0
+-- > ugen "Out" AR [0,o] 0 == out 0 (sinOsc AR 440 0)
+ugen :: String -> Rate -> [UGen] -> Int -> UGen
+ugen nm r i nc = mk_plain r nm i nc (Special 0) NoId
+
+-- | Construct non-deterministic UGen.
+--
+-- > import Sound.SC3.ID
+-- > nondet "WhiteNoise" (UId (fromEnum 'a')) AR [] 1 == whiteNoise 'a' AR
+nondet :: String -> UGenId -> Rate -> [UGen] -> Int -> UGen
+nondet nm z r i nc = mk_plain r nm i nc (Special 0) z
diff --git a/Sound/SC3/UGen/Protect.hs b/Sound/SC3/UGen/Protect.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/UGen/Protect.hs
@@ -0,0 +1,58 @@
+-- | Functions to re-write assigned node identifiers at UGen graphs.
+-- Used carefully it allows for composition of sub-graphs with
+-- psuedo-random nodes.
+module Sound.SC3.UGen.Protect where
+
+import Sound.SC3.UGen.Identifier
+import Sound.SC3.UGen.Type
+import Sound.SC3.UGen.UGen
+
+-- | Collect Ids at UGen graph
+ugenIds :: UGen -> [UGenId]
+ugenIds =
+    let f u = case u of
+                Primitive_U p -> [ugenId p]
+                _ -> []
+    in ugenFoldr ((++) . f) []
+
+-- | Apply /f/ at 'UId', or no-op at 'NoId'.
+atUGenId :: (Int -> Int) -> UGenId -> UGenId
+atUGenId f z =
+    case z of
+      NoId -> NoId
+      UId i -> UId (f i)
+
+-- | Add 'idHash' of /e/ to all 'Primitive_U' at /u/.
+uprotect :: ID a => a -> UGen -> UGen
+uprotect e =
+    let e' = idHash e
+        f u = case u of
+                Primitive_U p -> Primitive_U (p {ugenId = atUGenId (+ e') (ugenId p)})
+                _ -> u
+    in ugenTraverse f
+
+-- | Variant of 'uprotect' with subsequent identifiers derived by
+-- incrementing initial identifier.
+uprotect' :: ID a => a -> [UGen] -> [UGen]
+uprotect' e =
+    let n = map (+ idHash e) [1..]
+    in zipWith uprotect n
+
+-- | Make /n/ parallel instances of 'UGen' with protected identifiers.
+uclone' :: ID a => a -> Int -> UGen -> [UGen]
+uclone' e n = uprotect' e . replicate n
+
+-- | 'mce' variant of 'uclone''.
+uclone :: ID a => a -> Int -> UGen -> UGen
+uclone e n = mce . uclone' e n
+
+-- | Left to right UGen function composition with 'UGenId' protection.
+ucompose :: ID a => a -> [UGen -> UGen] -> UGen -> UGen
+ucompose e xs =
+    let go [] u = u
+        go ((f,k):f') u = go f' (uprotect k (f u))
+    in go (zip xs [idHash e ..])
+
+-- | Make /n/ sequential instances of `f' with protected Ids.
+useq :: ID a => a -> Int -> (UGen -> UGen) -> UGen -> UGen
+useq e n f = ucompose e (replicate n f)
diff --git a/Sound/SC3/UGen/Rate.hs b/Sound/SC3/UGen/Rate.hs
--- a/Sound/SC3/UGen/Rate.hs
+++ b/Sound/SC3/UGen/Rate.hs
@@ -1,7 +1,5 @@
 -- | Operating rate definitions and utilities.
-module Sound.SC3.UGen.Rate (Rate(..)
-                           ,rateId
-                           ,ar,kr,ir,dr) where
+module Sound.SC3.UGen.Rate where
 
 import Data.Function
 
@@ -12,28 +10,11 @@
 instance Ord Rate where
     compare = compare `on` rate_ord
 
-{-# DEPRECATED ar,kr,ir,dr "Aliases to be removed" #-}
--- | Rate constructors alias.
-ar :: Rate
-ar = AR
-
--- | Rate constructors alias.
-kr :: Rate
-kr = KR
-
--- | Rate constructors alias.
-ir :: Rate
-ir = IR
-
--- | Rate constructors alias.
-dr :: Rate
-dr = DR
-
 -- | Integer rate identifier, as required for scsynth bytecode.
 rateId :: Rate -> Int
 rateId = fromEnum
 
--- Rates as ordered for filter rate selection.
+-- | Rates as ordered for filter rate selection.
 rate_ord :: Rate -> Int
 rate_ord r =
     case r of
@@ -41,3 +22,16 @@
       KR -> 1
       AR -> 2
       DR -> 3
+
+-- | Color identifiers for each 'Rate'.
+rate_color :: Rate -> String
+rate_color r =
+    case r of
+      AR -> "black"
+      KR -> "blue"
+      IR -> "yellow"
+      DR -> "red"
+
+-- | Set of all 'Rate' values.
+all_rates :: [Rate]
+all_rates = [minBound .. maxBound]
diff --git a/Sound/SC3/UGen/Type.hs b/Sound/SC3/UGen/Type.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/UGen/Type.hs
@@ -0,0 +1,391 @@
+--  | Unit Generator ('UGen'), and associated types and instances.
+module Sound.SC3.UGen.Type where
+
+import Data.Bits
+import Data.List
+import Data.Maybe
+import Sound.SC3.UGen.Identifier
+import Sound.SC3.UGen.MCE
+import Sound.SC3.UGen.Operator
+import Sound.SC3.UGen.Rate
+import System.Random {- random -}
+
+-- * Basic types
+
+-- | Data type for internalised identifier at 'UGen'.
+data UGenId = NoId | UId Int
+              deriving (Eq,Show)
+
+-- | Constants.
+--
+-- > Constant 3 == Constant 3
+-- > (Constant 3 > Constant 1) == True
+data Constant = Constant {constantValue :: Double}
+                deriving (Eq,Ord,Show)
+
+-- | Control inputs.
+data Control = Control {controlOperatingRate :: Rate
+                       ,controlName :: String
+                       ,controlDefault :: Double
+                       ,controlTriggered :: Bool}
+               deriving (Eq,Show)
+
+-- | Labels.
+data Label = Label {ugenLabel :: String}
+             deriving (Eq,Show)
+
+-- | Unit generator output descriptor.
+type Output = Rate
+
+-- | Operating mode of unary and binary operators.
+newtype Special = Special Int
+    deriving (Eq, Show)
+
+-- | UGen primitives.
+data Primitive = Primitive {ugenRate :: Rate
+                           ,ugenName :: String
+                           ,ugenInputs :: [UGen]
+                           ,ugenOutputs :: [Output]
+                           ,ugenSpecial :: Special
+                           ,ugenId :: UGenId}
+                 deriving (Eq,Show)
+
+-- | Proxy to multiple channel input.
+data Proxy = Proxy {proxySource :: Primitive
+                   ,proxyIndex :: Int}
+            deriving (Eq,Show)
+
+-- | Multiple root graph.
+data MRG = MRG {mrgLeft :: UGen
+               ,mrgRight :: UGen}
+           deriving (Eq,Show)
+
+-- | Union type of Unit Generator forms.
+data UGen = Constant_U Constant
+          | Control_U Control
+          | Label_U Label
+          | Primitive_U Primitive
+          | Proxy_U Proxy
+          | MCE_U (MCE UGen)
+          | MRG_U MRG
+            deriving (Eq,Show)
+
+-- * Predicates
+
+-- | Constant node predicate.
+isConstant :: UGen -> Bool
+isConstant u =
+    case u of
+      Constant_U _ -> True
+      _ -> False
+
+-- | True if input is a sink 'UGen', ie. has no outputs.
+isSink :: UGen -> Bool
+isSink u =
+    case u of
+      Primitive_U p -> null (ugenOutputs p)
+      MCE_U m -> all isSink (mceProxies m)
+      MRG_U m -> isSink (mrgLeft m)
+      _ -> False
+
+-- * Validators
+
+-- | Ensure input 'UGen' is valid, ie. not a sink.
+checkInput :: UGen -> UGen
+checkInput u =
+    if isSink u
+    then error ("checkInput: illegal input: " ++ show u)
+    else u
+
+-- * Accessors
+
+-- | Value of 'Constant_U' 'Constant'.
+u_constant :: UGen -> Double
+u_constant u =
+    case u of
+      Constant_U (Constant n) -> n
+      _ -> error "u_constant"
+
+-- * Constructors
+
+-- | Constant value node constructor.
+constant :: (Real a) => a -> UGen
+constant = Constant_U . Constant . realToFrac
+
+-- | Multiple channel expansion node constructor.
+mce :: [UGen] -> UGen
+mce xs =
+    case xs of
+      [] -> error "mce: empty list"
+      [x] -> x
+      _ -> MCE_U (MCE_Vector xs)
+
+-- | Multiple root graph constructor.
+mrg :: [UGen] -> UGen
+mrg u =
+    case u of
+      [] -> error "mrg: null"
+      [x] -> x
+      (x:xs) -> MRG_U (MRG x (mrg xs))
+
+-- | Unit generator proxy node constructor.
+proxy :: UGen -> Int -> UGen
+proxy u n =
+    case u of
+      Primitive_U p -> Proxy_U (Proxy p n)
+      _ -> error "proxy"
+
+-- * MCE
+
+-- | Type specified 'mce_elem'.
+mceProxies :: MCE UGen -> [UGen]
+mceProxies = mce_elem
+
+-- | Multiple channel expansion node ('MCE_U') predicate.
+isMCE :: UGen -> Bool
+isMCE u =
+    case u of
+      MCE_U _ -> True
+      _ -> False
+
+-- | Output channels of UGen as a list.
+mceChannels :: UGen -> [UGen]
+mceChannels u =
+    case u of
+      MCE_U m -> mceProxies m
+      MRG_U (MRG x y) -> let r:rs = mceChannels x in MRG_U (MRG r y) : rs
+      _ -> [u]
+
+-- | Number of channels to expand to.
+mceDegree :: UGen -> Int
+mceDegree u =
+    case u of
+      MCE_U m -> length (mceProxies m)
+      MRG_U (MRG x _) -> mceDegree x
+      _ -> error "mceDegree: illegal ugen"
+
+-- | Extend UGen to specified degree.
+mceExtend :: Int -> UGen -> [UGen]
+mceExtend n u =
+    case u of
+      MCE_U m -> mceProxies (mce_extend n m)
+      MRG_U (MRG x y) -> let (r:rs) = mceExtend n x
+                         in MRG_U (MRG r y) : rs
+      _ -> replicate n u
+
+-- | Apply MCE transform to a list of inputs.
+mceInputTransform :: [UGen] -> Maybe [[UGen]]
+mceInputTransform i =
+    if any isMCE i
+    then let n = maximum (map mceDegree (filter isMCE i))
+         in Just (transpose (map (mceExtend n) i))
+    else Nothing
+
+-- | Build a UGen after MCE transformation of inputs.
+mceBuild :: ([UGen] -> UGen) -> [UGen] -> UGen
+mceBuild f i =
+    case mceInputTransform i of
+      Nothing -> f i
+      Just i' -> MCE_U (MCE_Vector (map (mceBuild f) i'))
+
+-- | Determine the rate of a UGen.
+rateOf :: UGen -> Rate
+rateOf u =
+    case u of
+      Constant_U _ -> IR
+      Control_U c -> controlOperatingRate c
+      Label_U _ -> IR
+      Primitive_U p -> ugenRate p
+      Proxy_U p -> ugenRate (proxySource p)
+      MCE_U _ -> maximum (map rateOf (mceChannels u))
+      MRG_U m -> rateOf (mrgLeft m)
+
+-- | Apply proxy transformation if required.
+proxify :: UGen -> UGen
+proxify u =
+    case u of
+      MCE_U m -> mce (map proxify (mceProxies m))
+      MRG_U m -> mrg [proxify (mrgLeft m), mrgRight m]
+      Primitive_U p ->
+          let o = ugenOutputs p
+          in case o of
+               (_:_:_) -> mce (map (proxy u) [0..(length o - 1)])
+               _ -> u
+      Constant_U _ -> u
+      _ -> error "proxify: illegal ugen"
+
+-- | Construct proxied and multiple channel expanded UGen.
+mkUGen :: Maybe ([Double] -> Double) -> [Rate] -> Maybe Rate ->
+          String -> [UGen] -> Int -> Special -> UGenId -> UGen
+mkUGen cf rs r nm i o s z =
+    let f h = let r' = fromMaybe (maximum (map rateOf h)) r
+                  o' = replicate o r'
+                  u = Primitive_U (Primitive r' nm h o' s z)
+              in if r' `elem` rs
+                 then case cf of
+                        Just cf' ->
+                            if all isConstant h
+                            then constant (cf' (map u_constant h))
+                            else u
+                        Nothing -> u
+                 else error ("mkUGen: rate restricted: " ++ show (r,rs,nm))
+    in proxify (mceBuild f (map checkInput i))
+
+-- * Operators
+
+-- | Operator UGen constructor.
+mkOperator :: ([Double] -> Double) -> String -> [UGen] -> Int -> UGen
+mkOperator f c i s =
+    mkUGen (Just f) all_rates Nothing c i 1 (Special s) NoId
+
+-- | Unary math constructor with constant optimization.
+mkUnaryOperator :: Unary -> (Double -> Double) -> UGen -> UGen
+mkUnaryOperator i f a =
+    let g [x] = f x
+        g _ = error "mkUnaryOperator: non unary input"
+    in mkOperator g "UnaryOpUGen" [a] (fromEnum i)
+
+-- | Binary math constructor with constant optimization.
+--
+-- > let o = sinOsc AR 440 0
+--
+-- > o * 1 == o && 1 * o == o && o * 2 /= o
+-- > o + 0 == o && 0 + o == o && o + 1 /= o
+-- > o - 0 == o && 0 - o /= o
+-- > o / 1 == o && 1 / o /= o
+-- > o ** 1 == o && o ** 2 /= o
+mkBinaryOperator_optimize :: Binary -> (Double -> Double -> Double) ->
+                             (Either Double Double -> Bool) ->
+                             UGen -> UGen -> UGen
+mkBinaryOperator_optimize i f o a b =
+   let g [x,y] = f x y
+       g _ = error "mkBinaryOperator: non binary input"
+       r = case (a,b) of
+             (Constant_U (Constant a'),_) ->
+                 if o (Left a') then Just b else Nothing
+             (_,Constant_U (Constant b')) ->
+                 if o (Right b') then Just a else Nothing
+             _ -> Nothing
+   in fromMaybe (mkOperator g "BinaryOpUGen" [a, b] (fromEnum i)) r
+
+-- | Binary math constructor with constant optimization.
+mkBinaryOperator :: Binary -> (Double -> Double -> Double) ->
+                    UGen -> UGen -> UGen
+mkBinaryOperator i f a b =
+   let g [x,y] = f x y
+       g _ = error "mkBinaryOperator: non binary input"
+   in mkOperator g "BinaryOpUGen" [a, b] (fromEnum i)
+
+-- * Numeric instances
+
+-- | Unit generators are numbers.
+instance Num UGen where
+    negate = mkUnaryOperator Neg negate
+    (+) = mkBinaryOperator_optimize Add (+) (`elem` [Left 0,Right 0])
+    (-) = mkBinaryOperator_optimize Sub (-) (Right 0 ==)
+    (*) = mkBinaryOperator_optimize Mul (*) (`elem` [Left 1,Right 1])
+    abs = mkUnaryOperator Abs abs
+    signum = mkUnaryOperator Sign signum
+    fromInteger = Constant_U . Constant . fromInteger
+
+-- | Unit generators are fractional.
+instance Fractional UGen where
+    recip = mkUnaryOperator Recip recip
+    (/) = mkBinaryOperator_optimize FDiv (/) (Right 1 ==)
+    fromRational = Constant_U . Constant . fromRational
+
+-- | Unit generators are floating point.
+instance Floating UGen where
+    pi = Constant_U (Constant pi)
+    exp = mkUnaryOperator Exp exp
+    log = mkUnaryOperator Log log
+    sqrt = mkUnaryOperator Sqrt sqrt
+    (**) = mkBinaryOperator_optimize Pow (**) (Right 1 ==)
+    logBase a b = log b / log a
+    sin = mkUnaryOperator Sin sin
+    cos = mkUnaryOperator Cos cos
+    tan = mkUnaryOperator Tan tan
+    asin = mkUnaryOperator ArcSin asin
+    acos = mkUnaryOperator ArcCos acos
+    atan = mkUnaryOperator ArcTan atan
+    sinh = mkUnaryOperator SinH sinh
+    cosh = mkUnaryOperator CosH cosh
+    tanh = mkUnaryOperator TanH tanh
+    asinh x = log (sqrt (x*x+1) + x)
+    acosh x = log (sqrt (x*x-1) + x)
+    atanh x = (log (1+x) - log (1-x)) / 2
+
+-- | Unit generators are real.
+instance Real UGen where
+    toRational (Constant_U (Constant n)) = toRational n
+    toRational _ = error "toRational at non-constant UGen"
+
+-- | Unit generators are integral.
+instance Integral UGen where
+    quot = mkBinaryOperator IDiv (error "ugen: quot")
+    rem = mkBinaryOperator Mod (error "ugen: rem")
+    quotRem a b = (quot a b, rem a b)
+    div = mkBinaryOperator IDiv (error "ugen: div")
+    mod = mkBinaryOperator Mod (error "ugen: mod")
+    toInteger (Constant_U (Constant n)) = floor n
+    toInteger _ = error "toInteger at non-constant UGen"
+
+-- | Unit generators are orderable (when 'Constants').
+--
+-- > (constant 2 > constant 1) == True
+instance Ord UGen where
+    (Constant_U a) < (Constant_U b) = a < b
+    _ < _ = error "< at UGen is partial, see <*"
+    (Constant_U a) <= (Constant_U b) = a <= b
+    _ <= _ = error "<= at UGen is partial, see <=*"
+    (Constant_U a) > (Constant_U b) = a > b
+    _ > _ = error "> at UGen is partial, see >*"
+    (Constant_U a) >= (Constant_U b) = a >= b
+    _ >= _ = error ">= at UGen is partial, see >=*"
+    min = mkBinaryOperator Min min
+    max = mkBinaryOperator Max max
+
+-- | Unit generators are enumerable.
+instance Enum UGen where
+    succ u = u + 1
+    pred u = u - 1
+    toEnum n = Constant_U (Constant (fromIntegral n))
+    fromEnum (Constant_U (Constant n)) = truncate n
+    fromEnum _ = error "cannot enumerate non-constant UGens"
+    enumFrom = iterate (+1)
+    enumFromThen n m = iterate (+(m-n)) n
+    enumFromTo n m = takeWhile (<= m+1/2) (enumFrom n)
+    enumFromThenTo n n' m =
+        let p = if n' >= n then (>=) else (<=)
+        in takeWhile (p (m + (n'-n)/2)) (enumFromThen n n')
+
+-- | Unit generators are stochastic.
+instance Random UGen where
+    randomR (Constant_U (Constant l),Constant_U (Constant r)) g =
+        let (n, g') = randomR (l,r) g
+        in (Constant_U (Constant n), g')
+    randomR _ _ = error "randomR: non constant (l,r)"
+    random = randomR (-1.0, 1.0)
+
+-- | UGens are bit patterns.
+instance Bits UGen where
+    (.&.) = mkBinaryOperator BitAnd undefined
+    (.|.) = mkBinaryOperator BitOr undefined
+    xor = mkBinaryOperator BitXor undefined
+    complement = mkUnaryOperator BitNot undefined
+    shift = error "Bits/UGen is partial"
+    rotate = error "Bits/UGen is partial"
+    bitSize = error "Bits/UGen is partial"
+    bit = error "Bits/UGen is partial"
+    testBit = error "Bits/UGen is partial"
+    popCount = error "Bits/UGen is partial"
+    isSigned _ = True
+
+-- * UGen ID Instance
+
+-- | Hash function for unit generators.
+hashUGen :: UGen -> Int
+hashUGen = hash . show
+
+instance ID UGen where
+    resolveID = hashUGen
diff --git a/Sound/SC3/UGen/UGen.hs b/Sound/SC3/UGen/UGen.hs
--- a/Sound/SC3/UGen/UGen.hs
+++ b/Sound/SC3/UGen/UGen.hs
@@ -1,83 +1,20 @@
 -- | UGen data structure representation and associated functions.
 module Sound.SC3.UGen.UGen where
 
-import Control.Monad
-import qualified Data.Digest.Murmur32 as H
+import qualified Data.Char as C
 import Data.List
-import Data.Maybe
-import Sound.SC3.Identifier
+import Sound.SC3.UGen.Identifier
 import Sound.SC3.UGen.Operator
 import Sound.SC3.UGen.Rate
-import Sound.SC3.UGen.UId
-import System.Random
-
--- * UGen Id type and functions
-
--- | Data type for internalised identifier at 'UGen'.
-data UGenId = NoId
-            | UserId {userId :: (String,Int)}
-            | SystemId {systemId :: Int}
-              deriving (Eq,Show)
-
--- | Predicate for 'NoId'.
-isNoId :: UGenId -> Bool
-isNoId i =
-    case i of
-      NoId -> True
-      _ -> False
-
--- | Predicate for 'UserId'.
-isUserId :: UGenId -> Bool
-isUserId i =
-    case i of
-      UserId _ -> True
-      _ -> False
-
--- | Predicate for 'SystemId'.
-isSystemId :: UGenId -> Bool
-isSystemId i =
-    case i of
-      SystemId _ -> True
-      _ -> False
-
--- | Hash value to 'Int'.
-hash :: H.Hashable32 a => a -> Int
-hash = fromIntegral . H.asWord32 . H.hash32
-
--- | Shift from 'UserId' to 'SystemId'.
-userIdProtect :: Int -> UGenId -> UGenId
-userIdProtect k i =
-    case i of
-      UserId j -> SystemId (fromIntegral (hash (show (k,j))))
-      _ -> i
-
--- | Increment 'UserId'.
-userIdIncr :: Int -> UGenId -> UGenId
-userIdIncr n i =
-        case i of
-          UserId (nm,k) -> UserId (nm,k+n)
-          _ -> i
-
--- * Unit Generator type
+import Sound.SC3.UGen.Type
 
--- | Unit generator.
-data UGen = Constant { constantValue :: Double }
-          | Control { controlOperatingRate :: Rate
-                    , controlName :: String
-                    , controlDefault :: Double
-                    , controlTriggered :: Bool }
-          | Primitive { ugenRate :: Rate
-                      , ugenName :: String
-                      , ugenInputs :: [UGen]
-                      , ugenOutputs :: [Output]
-                      , ugenSpecial :: Special
-                      , ugenId :: UGenId }
-          | Proxy { proxySource :: UGen
-                  , proxyIndex :: Int }
-          | MCE { mceProxies :: [UGen] }
-          | MRG { mrgLeft :: UGen
-                , mrgRight :: UGen }
-            deriving (Eq, Show)
+-- | Lookup operator name for operator UGens, else UGen name.
+ugen_user_name :: String -> Special -> String
+ugen_user_name nm (Special n) =
+    case nm of
+      "UnaryOpUGen" -> unaryName n
+      "BinaryOpUGen" -> binaryName n
+      _ -> nm
 
 -- * UGen graph functions
 
@@ -86,10 +23,16 @@
 ugenTraverse f u =
     let rec = ugenTraverse f
     in case u of
-         Primitive _ _ i _ _ _ -> f (u {ugenInputs = map rec i})
-         Proxy s _ -> f (u {proxySource = rec s})
-         MCE p -> f (u {mceProxies = map rec p})
-         MRG l r -> f (MRG (rec l) (rec r))
+         Primitive_U p ->
+             let i = ugenInputs p
+             in f (Primitive_U (p {ugenInputs = map rec i}))
+         Proxy_U p ->
+             let s = Primitive_U (proxySource p)
+             in case rec s of
+                  Primitive_U p' -> f (Proxy_U (p {proxySource = p'}))
+                  _ -> error "ugenTraverse"
+         MCE_U m -> f (mce (map rec (mceProxies m)))
+         MRG_U (MRG l r) -> f (MRG_U (MRG (rec l) (rec r)))
          _ -> f u
 
 -- | Right fold of UGen graph.
@@ -97,216 +40,54 @@
 ugenFoldr f st u =
     let rec = flip (ugenFoldr f)
     in case u of
-         Primitive _ _ i _ _ _ -> f u (foldr rec st i)
-         Proxy s _ -> f u (f s st)
-         MCE p -> f u (foldr rec st p)
-         MRG l r -> f u (f l (f r st))
+         Primitive_U p ->
+             let i = ugenInputs p
+             in f u (foldr rec st i)
+         Proxy_U p ->
+             let s = proxySource p
+             in f u (f (Primitive_U s) st)
+         MCE_U m -> f u (foldr rec st (mceProxies m))
+         MRG_U (MRG l r) -> f u (f l (f r st))
          _ -> f u st
 
--- * UGen graph Id reassignment
-
--- | Collect Ids at UGen graph
-ugenIds :: UGen -> [UGenId]
-ugenIds =
-    let f u = case ugenType u of
-                Primitive_U -> [ugenId u]
-                _ -> []
-    in ugenFoldr ((++) . f) []
-
--- | Recursive replacement of 'UGenId's according to table.
-ugenReplaceIds :: [(UGenId,UGenId)] -> UGen -> UGen
-ugenReplaceIds m =
-    let f u = case ugenType u of
-                Primitive_U ->
-                    case lookup (ugenId u) m of
-                      Just j -> u {ugenId = j}
-                      Nothing -> u
-                _ -> u
-    in ugenTraverse f
-
--- | Protect user specified UGen Ids.
-ugenProtectUserId :: Int -> UGen -> UGen
-ugenProtectUserId k =
-    let f u = case ugenType u of
-                Primitive_U -> u {ugenId = userIdProtect k (ugenId u)}
-                _ -> u
-    in ugenTraverse f
-
--- | 'idHash' variant of 'ugenProtectUserId'.
-uprotect :: ID a => a -> UGen -> UGen
-uprotect e = ugenProtectUserId (idHash e)
-
--- | Variant of 'uprotect' with subsequent identifiers derived by
--- incrementing initial identifier.
-uprotect' :: ID a => a -> [UGen] -> [UGen]
-uprotect' e =
-    let n = map (+ idHash e) [1..]
-    in zipWith ugenProtectUserId n
-
--- | Make /n/ parallel instances of 'UGen' with protected identifiers.
-uclone' :: ID a => a -> Int -> UGen -> [UGen]
-uclone' e n = uprotect' e . replicate n
-
--- | 'mce' variant of 'uclone''.
-uclone :: ID a => a -> Int -> UGen -> UGen
-uclone e n = mce . uclone' e n
-
--- | Left to right UGen function composition with user id protection.
-ucompose :: ID a => a -> [UGen -> UGen] -> UGen -> UGen
-ucompose e xs =
-    let go [] u = u
-        go ((f,k):f') u = go f' (ugenProtectUserId k (f u))
-    in go (zip xs [idHash e ..])
-
--- | Make /n/ sequential instances of `f' with protected Ids.
-useq :: ID a => a -> Int -> (UGen -> UGen) -> UGen -> UGen
-useq e n f = ucompose e (replicate n f)
-
--- | Increment user specified UGen Ids.
-ugenIncrUserId :: Int -> UGen -> UGen
-ugenIncrUserId k =
-    let f u = case ugenType u of
-                Primitive_U -> u {ugenId = userIdIncr k (ugenId u)}
-                _ -> u
-    in ugenTraverse f
-
--- | Duplicate `u' `n' times, increment user assigned Ids.
-udup' :: Int -> UGen -> [UGen]
-udup' n u =
-    let g k = ugenIncrUserId k u
-    in u : map g [1..n-1]
-
--- | 'mce' variant of 'udup''.
-udup :: Int -> UGen -> UGen
-udup n = mce . udup' n
-
--- * UGen ID Instance
-
--- | Hash function for unit generators.
-hashUGen :: UGen -> Int
-hashUGen = hash . show
-
-instance ID UGen where
-    resolveID = hashUGen
-
--- | Unit generator output descriptor.
-type Output = Rate
-
--- | Operating mode of unary and binary operators.
-newtype Special = Special Int
-    deriving (Eq, Show)
-
 -- * Unit generator node constructors
 
--- | Constant value node constructor.
-constant :: (Real a) => a -> UGen
-constant = Constant . realToFrac
-
 -- | Control input node constructor.
 --
 --   Note that if the name begins with a t_ prefix the control is
 --   not converted to a triggered control.  Please see tr_control.
 control :: Rate -> String -> Double -> UGen
-control r n d = Control r n d False
+control r n d = Control_U (Control r n d False)
 
 -- | Triggered (kr) control input node constructor.
 tr_control :: String -> Double -> UGen
-tr_control n d = Control KR n d True
-
--- | Multiple channel expansion node constructor.
-mce :: [UGen] -> UGen
-mce xs =
-    case xs of
-      [] -> error "mce: empty list"
-      [x] -> x
-      _ -> MCE xs
+tr_control n d = Control_U (Control KR n d True)
 
 -- | Multiple root graph node constructor.
 mrg2 :: UGen -> UGen -> UGen
-mrg2 = MRG
-
--- | Unit generator proxy node constructor.
-proxy :: UGen -> Int -> UGen
-proxy = Proxy
+mrg2 u = MRG_U . MRG u
 
 -- * Unit generator node predicates
 
--- | Enumeration of 'UGen' types.
-data UGenType = Constant_U
-              | Control_U
-              | Primitive_U
-              | Proxy_U
-              | MCE_U
-              | MRG_U
-                deriving (Eq,Enum,Bounded,Show)
-
--- | Multiple channel expansion node predicate.
-isMCE :: UGen -> Bool
-isMCE = (== MCE_U) . ugenType
-
--- | Constant node predicate.
-isConstant :: UGen -> Bool
-isConstant = (== Constant_U) . ugenType
-
--- | Constant node predicate.
-ugenType :: UGen -> UGenType
-ugenType u =
-    case u of
-      Constant _ -> Constant_U
-      Control _ _ _ _ -> Control_U
-      Primitive _ _ _ _ _ _ -> Primitive_U
-      Proxy _ _ -> Proxy_U
-      MCE _ -> MCE_U
-      MRG _ _ -> MRG_U
-
 -- * Multiple channel expansion
 
 -- | Multiple channel expansion for two inputs.
 mce2 :: UGen -> UGen -> UGen
-mce2 x y = mce [x, y]
+mce2 x y = mce [x,y]
 
 -- | Extract two channels from possible MCE.
 mce2c :: UGen -> (UGen,UGen)
 mce2c u =
     case u of
-      MCE (p:q:_) -> (p,q)
+      MCE_U m -> case mceProxies m of
+                     [] -> error "mce2c: nil mce"
+                     p:[] -> (p,p)
+                     p:q:_ -> (p,q)
       _ -> (u,u)
 
--- | Clone a unit generator (mce . replicateM).
-clone :: (UId m) => Int -> m UGen -> m UGen
-clone n = liftM mce . replicateM n
-
--- | Number of channels to expand to.
-mceDegree :: UGen -> Int
-mceDegree u =
-    case u of
-      MCE l -> length l
-      MRG x _ -> mceDegree x
-      _ -> error "mceDegree: illegal ugen"
-
--- | Extend UGen to specified degree.
-mceExtend :: Int -> UGen -> [UGen]
-mceExtend n u =
-    case u of
-      MCE l -> take n (cycle l)
-      MRG x y -> let (r:rs) = mceExtend n x
-                 in MRG r y : rs
-      _ -> replicate n u
-
--- | Apply MCE transform to a list of inputs.
-mceInputTransform :: [UGen] -> Maybe [[UGen]]
-mceInputTransform i =
-    if any isMCE i
-    then let n = maximum (map mceDegree (filter isMCE i))
-         in Just (transpose (map (mceExtend n) i))
-    else Nothing
-
--- | Build a UGen after MCE transformation of inputs.
-mceBuild :: ([UGen] -> UGen) -> [UGen] -> UGen
-mceBuild f i =
-    case mceInputTransform i of
-      Nothing -> f i
-      Just i' -> MCE (map (mceBuild f) i')
+-- | Multiple channel expansion for two inputs.
+mce3 :: UGen -> UGen -> UGen -> UGen
+mce3 x y z = mce [x,y,z]
 
 -- | Apply a function to each channel at a unit generator.
 mceMap :: (UGen -> UGen) -> UGen -> UGen
@@ -316,7 +97,7 @@
 mceEdit :: ([UGen] -> [UGen]) -> UGen -> UGen
 mceEdit f u =
     case u of
-      MCE l -> MCE (f l)
+      MCE_U m -> mce (f (mceProxies m))
       _ -> error "mceEdit: non MCE value"
 
 -- | Reverse order of channels at MCE.
@@ -327,17 +108,9 @@
 mceChannel :: Int -> UGen -> UGen
 mceChannel n u =
     case u of
-      MCE l -> l !! n
+      MCE_U m -> mceProxies m !! n
       _ -> error "mceChannel: non MCE value"
 
--- | Output channels of UGen as a list.
-mceChannels :: UGen -> [UGen]
-mceChannels u =
-    case u of
-      MCE l -> l
-      MRG x y -> let (r:rs) = mceChannels x in MRG r y : rs
-      _ -> [u]
-
 -- | Transpose rows and columns, ie. {{a,b},{c,d}} to {{a,c},{b,d}}.
 mceTranspose :: UGen -> UGen
 mceTranspose = mce . map mce . transpose . map mceChannels . mceChannels
@@ -348,97 +121,40 @@
 
 -- * Multiple root graphs
 
--- | Multiple root graph constructor.
-mrg :: [UGen] -> UGen
-mrg u =
-    case u of
-      [] -> error "mrg: null"
-      [x] -> x
-      (x:xs) -> MRG x (mrg xs)
-
--- * Unit generator function builders
-
--- | Apply proxy transformation if required.
-proxify :: UGen -> UGen
-proxify u =
-    case ugenType u of
-    MCE_U -> mce (map proxify (mceProxies u))
-    MRG_U -> mrg [proxify (mrgLeft u), mrgRight u]
-    Primitive_U ->
-        let o = ugenOutputs u
-        in case o of
-             (_:_:_) -> mce (map (proxy u) [0..(length o - 1)])
-             _ -> u
-    Constant_U -> u
-    _ -> error "proxify: illegal ugen"
-
--- | Determine the rate of a UGen.
-rateOf :: UGen -> Rate
-rateOf u =
-    case ugenType u of
-      Constant_U -> IR
-      Control_U -> controlOperatingRate u
-      Primitive_U -> ugenRate u
-      Proxy_U -> rateOf (proxySource u)
-      MCE_U -> maximum (map rateOf (mceChannels u))
-      MRG_U -> rateOf (mrgLeft u)
-
--- | True if input is a sink 'UGen', ie. has no outputs.
-is_sink :: UGen -> Bool
-is_sink u =
-    case ugenType u of
-      Primitive_U -> null (ugenOutputs u)
-      MCE_U -> all is_sink (mceProxies u)
-      MRG_U -> is_sink (mrgLeft u)
-      _ -> False
-
--- | Ensure input 'UGen' is valid, ie. not a sink.
-check_input :: UGen -> UGen
-check_input u =
-    if is_sink u
-    then error ("illegal input: " ++ show u)
-    else u
-
--- | Construct proxied and multiple channel expanded UGen.
-mkUGen :: Maybe ([Double] -> Double) -> [Rate] -> Maybe Rate ->
-          String -> [UGen] -> Int -> Special -> UGenId -> UGen
-mkUGen cf rs r nm i o s z =
-    let f h = let r' = fromMaybe (maximum (map rateOf h)) r
-                  o' = replicate o r'
-                  u = Primitive r' nm h o' s z
-              in if r' `elem` rs
-                 then case cf of
-                        Just cf' ->
-                            if all isConstant h
-                            then Constant (cf' (map constantValue h))
-                            else u
-                        Nothing -> u
-                 else error ("mkUGen: rate restricted: " ++ show (r,rs,nm))
-    in proxify (mceBuild f (map check_input i))
+-- * Labels
 
--- | Set of all 'Rate' values.
-all_rates :: [Rate]
-all_rates = [minBound .. maxBound]
+-- | Lift a 'String' to a UGen label (ie. for 'poll').
+label :: String -> UGen
+label = Label_U . Label
 
--- | Operator UGen constructor.
-mkOperator :: ([Double] -> Double) -> String -> [UGen] -> Int -> UGen
-mkOperator f c i s =
-    mkUGen (Just f) all_rates Nothing c i 1 (Special s) NoId
+-- | Are lists of equal length?
+--
+-- > equal_length_p ["t1","t2"] == True
+-- > equal_length_p ["t","t1","t2"] == False
+equal_length_p :: [[a]] -> Bool
+equal_length_p = (== 1) . length . nub . map length
 
--- | Unary math constructor with constant optimization.
-mkUnaryOperator :: Unary -> (Double -> Double) -> UGen -> UGen
-mkUnaryOperator i f a =
-    let g [x] = f x
-        g _ = error "mkUnaryOperator: non unary input"
-    in mkOperator g "UnaryOpUGen" [a] (fromEnum i)
+-- | Unpack a label to a length prefixed list of 'Constant's.  There
+-- is a special case for mce nodes, but it requires labels to be equal
+-- length.  Properly, 'poll' would not unpack the label, it would be
+-- done by the synthdef builder.
+unpackLabel :: UGen -> [UGen]
+unpackLabel u =
+    case u of
+      Label_U (Label s) ->
+          let q = fromEnum '?'
+              f c = if C.isAscii c then fromEnum c else q
+              s' = map (fromIntegral . f) s
+              n = fromIntegral (length s)
+          in n : s'
+      MCE_U m ->
+          let x = map unpackLabel (mceProxies m)
+          in if equal_length_p x
+             then map mce (transpose x)
+             else error (show ("unpackLabel: mce length /=",x))
+      _ -> error (show ("unpackLabel: non-label",u))
 
--- | Binary math constructor with constant optimization.
-mkBinaryOperator :: Binary -> (Double -> Double -> Double) ->
-                    UGen -> UGen -> UGen
-mkBinaryOperator i f a b =
-   let g [x,y] = f x y
-       g _ = error "mkBinaryOperator: non binary input"
-   in mkOperator g "BinaryOpUGen" [a, b] (fromEnum i)
+-- * Unit generator function builders
 
 -- | Oscillator constructor with constrained set of operating 'Rate's.
 mk_osc :: [Rate] -> UGenId -> Rate -> String -> [UGen] -> Int -> UGen
@@ -447,21 +163,28 @@
     then mkUGen Nothing rs (Just r) c i o (Special 0) z
     else error ("mk_osc: rate restricted: " ++ show (r, rs, c))
 
+-- | 'UGenId' used for deterministic UGens.
+no_id :: UGenId
+no_id = NoId
+
 -- | Oscillator constructor with 'all_rates'.
 mkOsc :: Rate -> String -> [UGen] -> Int -> UGen
-mkOsc = mk_osc all_rates NoId
+mkOsc = mk_osc all_rates no_id
 
 -- | Oscillator constructor, rate restricted variant.
 mkOscR :: [Rate] -> Rate -> String -> [UGen] -> Int -> UGen
-mkOscR rs = mk_osc rs NoId
+mkOscR rs = mk_osc rs no_id
 
--- | Transform 'String' and 'ID' to a 'UserId'.
-toUserId :: ID a => String -> a -> UGenId
-toUserId nm z = UserId (nm,resolveID z)
+toUId :: (ID a) => a -> UGenId
+toUId = UId . resolveID
 
+-- | Rate restricted oscillator constructor, setting identifier.
+mkOscIdR :: (ID a) => [Rate] -> a -> Rate -> String -> [UGen] -> Int -> UGen
+mkOscIdR rr z = mk_osc rr (toUId z)
+
 -- | Oscillator constructor, setting identifier.
 mkOscId :: (ID a) => a -> Rate -> String -> [UGen] -> Int -> UGen
-mkOscId z r nm = mk_osc all_rates (toUserId nm z) r nm
+mkOscId z = mk_osc all_rates (toUId z)
 
 -- | Provided 'UGenId' variant of 'mkOscMCE'.
 mk_osc_mce :: UGenId -> Rate -> String -> [UGen] -> UGen -> Int -> UGen
@@ -471,11 +194,11 @@
 
 -- | Variant oscillator constructor with MCE collapsing input.
 mkOscMCE :: Rate -> String -> [UGen] -> UGen -> Int -> UGen
-mkOscMCE = mk_osc_mce NoId
+mkOscMCE = mk_osc_mce no_id
 
 -- | Variant oscillator constructor with MCE collapsing input.
 mkOscMCEId :: ID a => a -> Rate -> String -> [UGen] -> UGen -> Int -> UGen
-mkOscMCEId z r nm = mk_osc_mce (toUserId nm z) r nm
+mkOscMCEId z = mk_osc_mce (toUId z)
 
 -- | Rate constrained filter 'UGen' constructor.
 mk_filter :: [Rate] -> UGenId -> String -> [UGen] -> Int -> UGen
@@ -483,21 +206,21 @@
 
 -- | Filter 'UGen' constructor.
 mkFilter :: String -> [UGen] -> Int -> UGen
-mkFilter = mk_filter all_rates NoId
+mkFilter = mk_filter all_rates no_id
 
 -- | Filter UGen constructor.
 mkFilterR :: [Rate] -> String -> [UGen] -> Int -> UGen
-mkFilterR rs = mk_filter rs NoId
+mkFilterR rs = mk_filter rs no_id
 
 -- | Filter UGen constructor.
 mkFilterId :: (ID a) => a -> String -> [UGen] -> Int -> UGen
-mkFilterId z nm = mk_filter all_rates (toUserId nm z) nm
+mkFilterId z = mk_filter all_rates (toUId z)
 
 -- | Variant filter with rate derived from keyed input.
 mkFilterKeyed :: String -> Int -> [UGen] -> Int -> UGen
 mkFilterKeyed c k i o =
     let r = rateOf (i !! k)
-    in mkUGen Nothing all_rates (Just r) c i o (Special 0) NoId
+    in mkUGen Nothing all_rates (Just r) c i o (Special 0) no_id
 
 -- | Provided 'UGenId' filter with 'mce' input.
 mk_filter_mce :: [Rate] -> UGenId -> String -> [UGen] -> UGen -> Int -> UGen
@@ -505,103 +228,45 @@
 
 -- | Variant filter constructor with MCE collapsing input.
 mkFilterMCER :: [Rate] -> String -> [UGen] -> UGen -> Int -> UGen
-mkFilterMCER rs = mk_filter_mce rs NoId
+mkFilterMCER rs = mk_filter_mce rs no_id
 
 -- | Variant filter constructor with MCE collapsing input.
 mkFilterMCE :: String -> [UGen] -> UGen -> Int -> UGen
-mkFilterMCE = mk_filter_mce all_rates NoId
+mkFilterMCE = mk_filter_mce all_rates no_id
 
 -- | Variant filter constructor with MCE collapsing input.
 mkFilterMCEId :: ID a => a -> String -> [UGen] -> UGen -> Int -> UGen
-mkFilterMCEId z nm = mk_filter_mce all_rates (toUserId nm z) nm
+mkFilterMCEId z = mk_filter_mce all_rates (toUId z)
 
 -- | Information unit generators are very specialized.
 mkInfo :: String -> UGen
 mkInfo name = mkOsc IR name [] 1
 
--- Unit generators are numbers.
-instance Num UGen where
-    negate = mkUnaryOperator Neg negate
-    (+) = mkBinaryOperator Add (+)
-    (-) = mkBinaryOperator Sub (-)
-    (*) = mkBinaryOperator Mul (*)
-    abs = mkUnaryOperator Abs abs
-    signum = mkUnaryOperator Sign signum
-    fromInteger = Constant . fromInteger
+-- * Bitwise
 
--- Unit generators are fractional.
-instance Fractional UGen where
-    recip = mkUnaryOperator Recip recip
-    (/) = mkBinaryOperator FDiv (/)
-    fromRational = Constant . fromRational
+bitAnd :: UGen -> UGen -> UGen
+bitAnd = mkBinaryOperator BitAnd undefined
 
--- Unit generators are floating point.
-instance Floating UGen where
-    pi = Constant pi
-    exp = mkUnaryOperator Exp exp
-    log = mkUnaryOperator Log log
-    sqrt = mkUnaryOperator Sqrt sqrt
-    (**) = mkBinaryOperator Pow (**)
-    logBase a b = log b / log a
-    sin = mkUnaryOperator Sin sin
-    cos = mkUnaryOperator Cos cos
-    tan = mkUnaryOperator Tan tan
-    asin = mkUnaryOperator ArcSin asin
-    acos = mkUnaryOperator ArcCos acos
-    atan = mkUnaryOperator ArcTan atan
-    sinh = mkUnaryOperator SinH sinh
-    cosh = mkUnaryOperator CosH cosh
-    tanh = mkUnaryOperator TanH tanh
-    asinh x = log (sqrt (x*x+1) + x)
-    acosh x = log (sqrt (x*x-1) + x)
-    atanh x = (log (1+x) - log (1-x)) / 2
+bitOr :: UGen -> UGen -> UGen
+bitOr = mkBinaryOperator BitOr undefined
 
--- Unit generators are real.
-instance Real UGen where
-    toRational (Constant n) = toRational n
-    toRational _ = error "toRational at non-constant UGen"
+bitXOr :: UGen -> UGen -> UGen
+bitXOr = mkBinaryOperator BitXor undefined
 
--- Unit generators are integral.
-instance Integral UGen where
-    quot = mkBinaryOperator IDiv (error "ugen: quot")
-    rem = mkBinaryOperator Mod (error "ugen: rem")
-    quotRem a b = (quot a b, rem a b)
-    div = mkBinaryOperator IDiv (error "ugen: div")
-    mod = mkBinaryOperator Mod (error "ugen: mod")
-    toInteger (Constant n) = floor n
-    toInteger _ = error "toInteger at non-constant UGen"
+bitNot :: UGen -> UGen
+bitNot = mkUnaryOperator BitNot undefined
 
--- Unit generators are orderable.
-instance Ord UGen where
-    (Constant a) < (Constant b) = a < b
-    _ < _ = error "< at UGen is partial, see <*"
-    (Constant a) <= (Constant b) = a <= b
-    _ <= _ = error "<= at UGen is partial, see <=*"
-    (Constant a) > (Constant b) = a < b
-    _ > _ = error "> at UGen is partial, see >*"
-    (Constant a) >= (Constant b) = a >= b
-    _ >= _ = error ">= at UGen is partial, see >=*"
-    min = mkBinaryOperator Min min
-    max = mkBinaryOperator Max max
+shiftLeft :: UGen -> UGen -> UGen
+shiftLeft = mkBinaryOperator ShiftLeft undefined
 
--- Unit generators are enumerable.
-instance Enum UGen where
-    succ u = u + 1
-    pred u = u - 1
-    toEnum = constant
-    fromEnum (Constant n) = truncate n
-    fromEnum _ = error "cannot enumerate non-constant UGens"
-    enumFrom = iterate (+1)
-    enumFromThen n m = iterate (+(m-n)) n
-    enumFromTo n m = takeWhile (<= m+1/2) (enumFrom n)
-    enumFromThenTo n n' m =
-        let p = if n' >= n then (>=) else (<=)
-        in takeWhile (p (m + (n'-n)/2)) (enumFromThen n n')
+shiftRight :: UGen -> UGen -> UGen
+shiftRight = mkBinaryOperator ShiftRight undefined
 
--- Unit generators are stochastic.
-instance Random UGen where
-    randomR (Constant l, Constant r) g =
-        let (n, g') = randomR (l,r) g
-        in (Constant n, g')
-    randomR _ _ = error "randomR: non constant (l,r)"
-    random = randomR (-1.0, 1.0)
+unsignedShift :: UGen -> UGen -> UGen
+unsignedShift = mkBinaryOperator UnsignedShift undefined
+
+(.<<.) :: UGen -> UGen -> UGen
+(.<<.) = shiftLeft
+
+(.>>.) :: UGen -> UGen -> UGen
+(.>>.) = shiftRight
diff --git a/Sound/SC3/UGen/UGen/Lift.hs b/Sound/SC3/UGen/UGen/Lift.hs
--- a/Sound/SC3/UGen/UGen/Lift.hs
+++ b/Sound/SC3/UGen/UGen/Lift.hs
@@ -2,7 +2,7 @@
 -- monadic 'UGen' constructors.
 module Sound.SC3.UGen.UGen.Lift where
 
-import Sound.SC3.UGen.UGen
+import Sound.SC3.UGen.Type
 import Sound.SC3.UGen.UId
 
 -- | Lift base UGen to monadic form.
diff --git a/Sound/SC3/UGen/UId.hs b/Sound/SC3/UGen/UId.hs
--- a/Sound/SC3/UGen/UId.hs
+++ b/Sound/SC3/UGen/UId.hs
@@ -3,12 +3,19 @@
 module Sound.SC3.UGen.UId where
 
 import Control.Monad
+import Control.Monad.IO.Class as M {- transformers -}
+import Control.Monad.Trans.Reader
 import Data.Unique
+import Sound.OSC.Transport.FD as T
 
 -- | A class indicating a monad that will generate a sequence of
 --   unique integer identifiers.
-class (Monad m) => UId m where
+class (Functor m,M.MonadIO m) => UId m where
    generateUId :: m Int
+   generateUId = fmap hashUnique (M.liftIO newUnique)
 
 instance UId IO where
-   generateUId = liftM hashUnique newUnique
+    generateUId = liftM hashUnique newUnique
+
+instance (T.Transport t,Functor io,MonadIO io) => UId (ReaderT t io) where
+   generateUId = ReaderT (M.liftIO . const generateUId)
diff --git a/Sound/SC3/UGen/Utilities.hs b/Sound/SC3/UGen/Utilities.hs
deleted file mode 100644
--- a/Sound/SC3/UGen/Utilities.hs
+++ /dev/null
@@ -1,41 +0,0 @@
--- | Internal UGen related functions.
-module Sound.SC3.UGen.Utilities where
-
-import Sound.SC3.UGen.Enum
-import Sound.SC3.UGen.UGen
-
--- * Un-enumerations.
-
--- | Resolve 'Loop'.
-from_loop :: Loop -> UGen
-from_loop e =
-    case e of
-      NoLoop -> Constant 0
-      Loop -> Constant 1
-      WithLoop u -> u
-
--- | Resolve 'Interpolation'.
-from_interpolation :: Interpolation -> UGen
-from_interpolation e =
-    case e of
-      NoInterpolation -> Constant 1
-      LinearInterpolation -> Constant 2
-      CubicInterpolation -> Constant 4
-      Interpolation u -> u
-
--- | Resolve 'DoneAction'.
-from_done_action :: DoneAction -> UGen
-from_done_action e =
-    case e of
-      DoNothing -> Constant 0
-      PauseSynth -> Constant 1
-      RemoveSynth -> Constant 2
-      DoneAction u -> u
-
--- | Resolve 'Warp'.
-from_warp :: Warp -> UGen
-from_warp e =
-    case e of
-      Linear -> Constant 0
-      Exponential -> Constant 1
-      Warp u -> u
diff --git a/Sound/SC3/UGen/Wavelets.hs b/Sound/SC3/UGen/Wavelets.hs
--- a/Sound/SC3/UGen/Wavelets.hs
+++ b/Sound/SC3/UGen/Wavelets.hs
@@ -2,6 +2,7 @@
 module Sound.SC3.UGen.Wavelets where
 
 import Sound.SC3.UGen.Rate
+import Sound.SC3.UGen.Type
 import Sound.SC3.UGen.UGen
 
 -- | Forward wavelet transform.
diff --git a/emacs/hsc3.el b/emacs/hsc3.el
--- a/emacs/hsc3.el
+++ b/emacs/hsc3.el
@@ -47,18 +47,27 @@
 				      (thing-at-point 'symbol)
 				      "\\.help\\.lhs"))))
 
-(defun hsc3-sc3-help ()
-  "Lookup up the name at point in the SC3 help files."
+(defun hsc3-sc3-ugen-help ()
+  "Lookup up the UGen name at point in the SC3 help files."
   (interactive)
   (hsc3-send-string
-   (format "Sound.SC3.viewSC3Help (Sound.SC3.toSC3Name \"%s\")"
+   (format
+    "Sound.SC3.viewSC3Help (Sound.SC3.toSC3Name \"%s\")"
+    (thing-at-point 'symbol))))
+
+(defun hsc3-sc3-server-help ()
+  "Lookup up the Server Command name at point in the SC3 help files."
+  (interactive)
+  (hsc3-send-string
+   (format "Sound.SC3.Server.Help.viewServerHelp \"%s\""
            (thing-at-point 'symbol))))
 
 (defun hsc3-ugen-summary ()
   "Lookup up the UGen at point in hsc3-db"
   (interactive)
-  (hsc3-send-string (format "Sound.SC3.UGen.DB.ugenSummary_ci \"%s\""
-                            (thing-at-point 'symbol))))
+  (hsc3-send-string
+   (format "Sound.SC3.UGen.DB.ugenSummary_ci \"%s\""
+           (thing-at-point 'symbol))))
 
 (defun hsc3-request-type ()
   "Ask ghci for the type of the name at point."
@@ -143,7 +152,7 @@
 (defun hsc3-reset-scsynth ()
   "Reset scsynth"
   (interactive)
-  (hsc3-send-string "withSC3 reset"))
+  (hsc3-send-string "Sound.SC3.withSC3 Sound.SC3.reset"))
 
 (defun hsc3-stop ()
   "Interrup haskell interpreter & reset scsynth"
@@ -155,12 +164,14 @@
 (defun hsc3-status-scsynth ()
   "Status"
   (interactive)
-  (hsc3-send-string "withSC3 serverStatus >>= mapM putStrLn"))
+  (hsc3-send-string
+   "Sound.SC3.withSC3 Sound.SC3.serverStatus >>= mapM putStrLn"))
 
 (defun hsc3-quit-scsynth ()
   "Quit"
   (interactive)
-  (hsc3-send-string "withSC3 (\fd -> send fd quit)"))
+  (hsc3-send-string
+   "Sound.SC3.withSC3 (Sound.SC3.send Sound.SC3.quit)"))
 
 (defun hsc3-update-hsc3-tags ()
   "Update hsc3 TAGS file, must be run from hsc3 directory."
@@ -172,6 +183,18 @@
        nil)
     (error "not at hsc3 directory?")))
 
+(defun hsc3-draw-graph ()
+  "Draw the UGen graph at point."
+  (interactive)
+  (hsc3-send-string
+   (concat "Sound.SC3.UGen.Dot.draw " (thing-at-point 'symbol))))
+
+(defun hsc3-local-dot ()
+  "Copy '/tmp/hsc3.dot' to 'buffer-name' .dot."
+  (interactive)
+  (let ((nm (concat (file-name-sans-extension (buffer-name)) ".dot")))
+    (copy-file "/tmp/hsc3.dot" nm t)))
+
 (defun hsc3-set-prompt ()
   "Set ghci prompt to hsc3."
   (interactive)
@@ -202,7 +225,9 @@
   (define-key map [?\C-c ?\C-e] 'hsc3-run-multiple-lines)
   (define-key map [?\C-c ?\C-r] 'hsc3-run-consecutive-lines)
   (define-key map [?\C-c ?\C-h] 'hsc3-help)
-  (define-key map [?\C-c ?\C-j] 'hsc3-sc3-help)
+  (define-key map [?\C-c ?\C-g] 'hsc3-draw-graph)
+  (define-key map [?\C-c ?\C-j] 'hsc3-sc3-ugen-help)
+  (define-key map [?\C-c ?\C-/] 'hsc3-sc3-server-help)
   (define-key map [?\C-c ?\C-i] 'hsc3-interrupt-haskell)
   (define-key map [?\C-c ?\C-k] 'hsc3-reset-scsynth)
   (define-key map [?\C-c ?\C-m] 'hsc3-run-main)
@@ -222,6 +247,10 @@
     '("Haskell SuperCollider help" . hsc3-help))
   (define-key map [menu-bar hsc3 help ugen]
     '("UGen parameter summary" . hsc3-ugen-summary))
+  (define-key map [menu-bar hsc3 help sc3-server]
+    '("SuperCollider Server Command help" . hsc3-sc3-server-help))
+  (define-key map [menu-bar hsc3 help sc3-ugen]
+    '("SuperCollider UGen help" . hsc3-sc3-ugen-help))
   (define-key map [menu-bar hsc3 expression]
     (cons "Expression" (make-sparse-keymap "Expression")))
   (define-key map [menu-bar hsc3 expression stop]
diff --git a/hsc3.cabal b/hsc3.cabal
--- a/hsc3.cabal
+++ b/hsc3.cabal
@@ -1,22 +1,23 @@
 Name:              hsc3
-Version:           0.11
+Version:           0.12
 Synopsis:          Haskell SuperCollider
-Description:       hsc3 facilitates using haskell as a client to the
-                   SuperCollider synthesis server.
-
-                   For detailed installation and configuration
-                   information please consult the /Tutorial/ and
-                   /Introduction/ documents at
-                   <http://slavepianos.org/rd/ut/hsc3-texts/>
-
+Description:       Haskell client for the SuperCollider synthesis server,
+                   <http://audiosynth.com/>.
+                   .
+                   For installation and configuration see the Tutorial at
+                   <http://rd.slavepianos.org/?t=hsc3-texts>.
+                   .
+                   hsc3 has two implementations of the non-determinstic
+                   Unit Generators, "Sound.SC3.UGen.ID" and
+                   "Sound.SC3.UGen.Monad".
 License:           GPL
 Category:          Sound
-Copyright:         (c) Rohan Drape and others, 2006-2011
+Copyright:         (c) Rohan Drape and others, 2006-2012
 Author:            Rohan Drape
 Maintainer:        rd@slavepianos.org
 Stability:         Experimental
-Homepage:          http://slavepianos.org/rd/?t=hsc3
-Tested-With:       GHC == 7.2.2
+Homepage:          http://rd.slavepianos.org/?t=hsc3
+Tested-With:       GHC == 7.6.1
 Build-Type:        Simple
 Cabal-Version:     >= 1.8
 
@@ -48,68 +49,88 @@
   Build-Depends:   base == 4.*,
                    binary,
                    bytestring,
-                   cmath,
                    containers,
                    directory,
                    filepath,
-                   hosc == 0.11.*,
+                   hosc == 0.12.*,
                    murmur-hash,
                    network,
                    process,
                    random,
-                   split
+                   split,
+                   transformers
   GHC-Options:     -Wall -fwarn-tabs
   Exposed-modules: Sound.SC3
-                   Sound.SC3.Identifier
+                   Sound.SC3.FD
                    Sound.SC3.ID
-                   Sound.SC3.Monadic
+                   Sound.SC3.ID.FD
+                   Sound.SC3.Monad
+                   Sound.SC3.Monad.FD
                    Sound.SC3.Server
                    Sound.SC3.Server.Command
                    Sound.SC3.Server.Command.Completion
+                   Sound.SC3.Server.FD
+                   Sound.SC3.Server.Help
+                   Sound.SC3.Server.Monad
                    Sound.SC3.Server.NRT
-                   Sound.SC3.Server.Play
+                   Sound.SC3.Server.Transport.FD
+                   Sound.SC3.Server.Transport.Monad
                    Sound.SC3.Server.Status
                    Sound.SC3.Server.Synthdef
+                   Sound.SC3.Server.Synthdef.Internal
+                   Sound.SC3.Server.Synthdef.Reconstruct
+                   Sound.SC3.Server.Synthdef.Transform
+                   Sound.SC3.Server.Synthdef.Type
                    Sound.SC3.UGen
                    Sound.SC3.UGen.Analysis
                    Sound.SC3.UGen.Buffer
                    Sound.SC3.UGen.Chaos
                    Sound.SC3.UGen.Composite
                    Sound.SC3.UGen.Composite.ID
-                   Sound.SC3.UGen.Composite.Monadic
+                   Sound.SC3.UGen.Composite.Monad
                    Sound.SC3.UGen.Demand
                    Sound.SC3.UGen.Demand.ID
-                   Sound.SC3.UGen.Demand.Monadic
+                   Sound.SC3.UGen.Demand.Monad
                    Sound.SC3.UGen.DiskIO
                    Sound.SC3.UGen.Enum
                    Sound.SC3.UGen.Envelope
                    Sound.SC3.UGen.Envelope.Construct
+                   Sound.SC3.UGen.Envelope.Interpolate
                    Sound.SC3.UGen.External
                    Sound.SC3.UGen.External.ATS
+                   Sound.SC3.UGen.External.ID
                    Sound.SC3.UGen.External.LPC
+                   Sound.SC3.UGen.External.SC3_Plugins
                    Sound.SC3.UGen.FFT
                    Sound.SC3.UGen.FFT.ID
-                   Sound.SC3.UGen.FFT.Monadic
+                   Sound.SC3.UGen.FFT.Monad
                    Sound.SC3.UGen.Filter
                    Sound.SC3.UGen.Granular
                    Sound.SC3.UGen.Help
+                   Sound.SC3.UGen.Identifier
+                   Sound.SC3.UGen.ID
                    Sound.SC3.UGen.IO
                    Sound.SC3.UGen.Information
                    Sound.SC3.UGen.MachineListening
                    Sound.SC3.UGen.Math
+                   Sound.SC3.UGen.MCE
+                   Sound.SC3.UGen.Monad
+                   Sound.SC3.UGen.Name
                    Sound.SC3.UGen.Noise.ID
-                   Sound.SC3.UGen.Noise.Monadic
+                   Sound.SC3.UGen.Noise.Monad
                    Sound.SC3.UGen.Operator
                    Sound.SC3.UGen.Oscillator
                    Sound.SC3.UGen.Panner
+                   Sound.SC3.UGen.Plain
+                   Sound.SC3.UGen.Protect
                    Sound.SC3.UGen.Rate
+                   Sound.SC3.UGen.Type
                    Sound.SC3.UGen.UGen
                    Sound.SC3.UGen.UGen.Lift
                    Sound.SC3.UGen.UId
                    Sound.SC3.UGen.Wavelets
   Other-modules:   Sound.SC3.Server.Utilities
-                   Sound.SC3.UGen.Utilities
 
 Source-Repository  head
   Type:            darcs
-  Location:        http://slavepianos.org/rd/sw/hsc3/
+  Location:        http://rd.slavepianos.org/sw/hsc3/
