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,1 +1,11 @@
 > Sound.SC3.Server.Help.viewServerHelp "/b_alloc"
+
+Buffer indices are not restricted by the number of available buffers
+at the server.  Below allocates a buffer at index 8192.
+
+> withSC3 (async (b_alloc_setn1 8192 0 [0,3,7,10]))
+
+> let {x = mouseX KR 0 9 Linear 0.1
+>     ;k = degreeToKey 8192 x 12
+>     ;o = sinOsc AR (midiCPS (48 + k)) 0 * 0.1}
+> in audition (out 0 o)
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,1 +1,44 @@
 > Sound.SC3.Server.Help.viewServerHelp "/b_allocRead"
+
+> import Sound.SC3
+
+Read a large audio file into a buffer.
+
+> let fn = "/home/rohan/data/audio/xenakis/jonchaies.wav"
+> in withSC3 (async (b_allocRead 0 fn 0 0))
+
+Audio data is loaded in IEEE 32-bit form, so in-memory storage can be
+greater than on-disk storage.
+
+$ sndfile-info data/audio/xenakis/jonchaies.wav
+Sample Rate : 44100
+Frames      : 42271320
+Duration    : 00:15:58.533
+$ du -h data/audio/xenakis/jonchaies.wav
+162M    data/audio/xenakis/jonchaies.wav
+$
+
+> round ((16 * 60 * 44100 * 4 * 2) / (1024 * 1024)) == 323
+> round ((42271320 * 2 * 4) / (1024 * 1024)) == 323
+
+Query buffer.
+
+> withSC3 (do {send (b_query [0])
+>             ;r <- waitReply "/b_info"
+>             ;liftIO (print r)})
+
+Play buffer.
+
+> let {n = 257; s = bufRateScale KR n}
+> in audition (out 0 (playBuf 1 AR n s 1 0 NoLoop RemoveSynth))
+
+Re-read file into buffer with the same identifier.  The existing
+buffer is freed but not before further memory is allocated,
+intermediate in-memory use is greater than final memory use.
+
+> let fn = "/home/rohan/data/audio/xenakis/jonchaies.wav"
+> in withSC3 (async (b_allocRead 0 fn 0 0))
+
+Free buffer.  Memory is immediately made free.
+
+> withSC3 (async (b_free 0))
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,1 +1,9 @@
 > Sound.SC3.Server.Help.viewServerHelp "/b_free"
+
+It is safe to free un-allocated buffers.
+
+> withSC3 (async (b_free (2 ^ 15)))
+
+There is no multiple buffer form.
+
+> withSC3 (mapM_ (\k -> async (b_free k)) [0..256])
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
@@ -4,8 +4,29 @@
 > import Sound.SC3 {- hsc3 -}
 > import Sound.SC3.Plot {- hsc3-plot -}
 
+Allocate and generate wavetable buffer (256 frames)
+
+> withSC3 (do {_ <- async (b_alloc 0 256 1)
+>             ;let f = [Normalise,Clear]
+>              in send (b_gen_sine1 0 f [1,1/2,1/3,1/4,1/5])})
+
+Run simple read...
+
+> d0 <- withSC3 (b_getn1_data 0 (0,255))
+
+and draw buffer
+
+> plotTable [d0]
+
+Load sound file
+
 > let fn = "/home/rohan/data/audio/pf-c5.aif"
-> in withSC3 (async (b_allocRead 0 fn 0 0))
+> in withSC3 (async (b_allocRead 1 fn 0 0))
 
-> d <- withSC3 (b_getn1_data_segment 1024 0 (0,2^15))
-> plotTable [d]
+Run segmented read (2^15 frames in 1024 frame segments)...
+
+> d1 <- withSC3 (b_getn1_data_segment 1024 1 (0,2^15))
+
+and draw buffer
+
+> plotTable [d1]
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,1 +1,30 @@
 > Sound.SC3.Server.Help.viewServerHelp "/b_query"
+
+> import Sound.OSC {- hosc -}
+> import Sound.SC3 {- hsc3 -}
+
+Allocate and generate wavetable buffer
+
+> withSC3 (do {_ <- async (b_alloc 0 256 1)
+>             ;let f = [Normalise,Wavetable,Clear]
+>              in send (b_gen_sine1 0 f [1,1/2,1/3,1/4,1/5])})
+
+Query buffer
+
+> withSC3 (do {send (b_query [0])
+>             ;r <- waitReply "/b_info"
+>             ;liftIO (print r)})
+
+Play buffer
+
+> audition (out 0 (osc AR 0 220 0 * 0.1))
+
+Free buffer
+
+> withSC3 (async (b_free 0))
+
+Query multiple un-allocated buffers
+
+> withSC3 (do {send (b_query [2^14,2^15])
+>             ;r <- waitReply "/b_info"
+>             ;liftIO (print r)})
diff --git a/Help/Server/b_readChannel.help.lhs b/Help/Server/b_readChannel.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/Server/b_readChannel.help.lhs
@@ -0,0 +1,1 @@
+> Sound.SC3.Server.Help.viewServerHelp "/b_readChannel"
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,1 +1,10 @@
 > Sound.SC3.Server.Help.viewServerHelp "/c_get"
+
+> import Sound.OSC {- hosc -}
+> import Sound.SC3.ID {- hsc3 -}
+
+> audition (out 0 (tRand 'α' 220 2200 (dust 'β' KR 1)))
+
+> withSC3 (do {send (c_get [0])
+>             ;r <- waitReply "/c_set"
+>             ;liftIO (print r)})
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
@@ -3,6 +3,6 @@
 > import Sound.SC3.ID
 
 > withSC3 (send (dumpOSC TextPrinter))
-> audition (out 0 (sinOsc AR (rand 'a' 440 880) 0 * 0.1))
+> audition (out 0 (sinOsc AR (rand 'α' 440 880) 0 * 0.1))
 > withSC3 reset
 > withSC3 (send (dumpOSC NoPrinter))
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,1 +1,10 @@
 > Sound.SC3.Server.Help.viewServerHelp "/g_new"
+
+The root node of the synthesiser tree is a group with ID zero.
+
+By convention there is a group with ID one at the root group, but
+this is only a convention.  We need to make the group.
+
+> import Sound.SC3
+
+> withSC3 (send (g_new [(1,AddToTail,0)]))
diff --git a/Help/Server/g_queryTree.help.lhs b/Help/Server/g_queryTree.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/Server/g_queryTree.help.lhs
@@ -0,0 +1,28 @@
+> Sound.SC3.Server.Help.viewServerHelp "/g_queryTree"
+
+> import Sound.OSC {- hosc -}
+> import Sound.SC3 {- hsc3 -}
+
+> let d = let {f = control KR "freq" 440
+>             ;o = saw AR f * 0.05}
+>         in synthdef "saw" (out 0 o)
+
+> withSC3 (async (d_recv d) >>
+>          send (g_new [(100,AddToTail,1)]) >>
+>          send (s_new0 "saw" 1000 AddToTail 100))
+
+> r <- withSC3 (send (g_queryTree [(0,True)]) >>
+>               waitReply "/g_queryTree.reply")
+
+> print r
+
+> withSC3 (send (g_dumpTree [(0,True)]))
+
+There is support for extracting the node tree into standard haskell
+tree data type.
+
+> import qualified Data.Tree as T {- containers -}
+
+> let tr = queryTree_rt (queryTree (messageDatum r))
+
+> putStrLn (unlines ["::TREE::",T.drawTree (fmap query_node_pp tr)])
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,1 +1,16 @@
 > Sound.SC3.Server.Help.viewServerHelp "/n_query"
+
+> import Sound.OSC
+> import Sound.SC3
+
+> let d = let {f = control KR "freq" 440
+>             ;o = saw AR f * 0.1}
+>         in synthdef "saw" (out 0 o)
+
+> withSC3 (async (d_recv d) >>
+>          send (s_new0 "saw" 1000 AddToTail 1))
+
+> r <- withSC3 (send (n_query [1000]) >>
+>               waitReply "/n_info")
+
+> print r
diff --git a/Help/UGen/Analysis/amplitude.help.lhs b/Help/UGen/Analysis/amplitude.help.lhs
--- a/Help/UGen/Analysis/amplitude.help.lhs
+++ b/Help/UGen/Analysis/amplitude.help.lhs
@@ -3,10 +3,14 @@
 
 > import Sound.SC3
 
-> let {s = in' 1 AR numOutputBuses
->     ;a = amplitude KR s 0.1 0.1}
+> let {s = soundIn 4
+>     ;a = amplitude KR s 0.01 0.01}
 > in audition (out 0 (pulse AR 90 0.3 * a))
 
-> let {s = in' 1 AR numOutputBuses
->     ;f = amplitude KR s 0.1 0.1 * 1200 + 400}
-> in audition (out 0 (sinOsc AR f 0 * 0.3))
+> let {s = soundIn 4
+>     ;f = amplitude KR s 0.5 0.5 * 1200 + 400}
+> in audition (out 0 (sinOsc AR f 0 * 0.1))
+
+> let {s = soundIn 4
+>     ;a = amplitude AR s 0.5 0.05}
+> in audition (out 0 (s * a))
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
@@ -8,20 +8,26 @@
 >             ;p = mix (pulse AR (mce [80, 81]) 0.3)}
 >         in e * p
 
+> let z = soundIn 4
+
 > audition (out 0 z)
 
-Noise gate
-> 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]))
+Noise gate (no hold, no hysteresis)
+> let x = mouseX KR 0.01 0.15 Linear 0.1
+> in audition (out 0 (mce [z, compander z z x 10 1 0.002 0.15]))
 
 Compressor
 > 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]))
+> in audition (out 0 (mce [z, compander z z x 1 (1/3) 0.01 0.01]))
 
+Expander
+> let x = mouseX KR 0.01 1 Linear 0.1
+> in audition (out 0 (mce [z, compander z z x 1 3 0.01 0.1]))
+
 Limiter
 > 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]))
+> in audition (out 0 (mce [z, compander z z x 1 (1/10) 0.01 0.01]))
 
 Sustainer
-> 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]))
+> let x = mouseX KR 0.01 0.15 Linear 0.1
+> in audition (out 0 (mce [z, compander z z x (1/3) 1.0 0.01 0.05]))
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
@@ -7,10 +7,21 @@
 >     ;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}
+>     ;f = pitch s 440 60 4000 100 16 7 0.02 0.5 1 0}
 > in audition (out 0 (mce [s, sinOsc AR (mceChannel 0 f / 2) 0 * a]))
 
-> let {s = in' 1 AR numOutputBuses
+> let {s = soundIn 4
 >     ;a = amplitude KR s 0.1 0.1
->     ;f = pitch s 440 60 4000 100 16 7 0.02 0.5 1}
+>     ;f = pitch s 440 60 4000 100 16 7 0.02 0.5 1 0}
 > in audition (out 0 (mce [s, sinOsc AR (mceChannel 0 f) 0 * a]))
+
+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
+>     ;[f,_] = mceChannels (pitch o 440 60 4000 100 16 7 0.02 0.5 1 0)
+>     ;r = sinOsc AR f 0 * 0.1
+>     ;t = impulse KR 4 0
+>     ;pf = poll t f (label "f") 0
+>     ;px = poll t x (label "x") 0}
+> in audition (mrg [out 0 (mce2 o r),pf,px])
diff --git a/Help/UGen/Analysis/runningSum.help.lhs b/Help/UGen/Analysis/runningSum.help.lhs
--- a/Help/UGen/Analysis/runningSum.help.lhs
+++ b/Help/UGen/Analysis/runningSum.help.lhs
@@ -3,5 +3,23 @@
 
 > import Sound.SC3
 
-> let a = runningSum (in' 1 AR numOutputBuses) 40 * (1/40)
-> in audition (out 0 (sinOsc AR 440 0 * a))
+distorts of course - would need scaling
+> audition (out 0 (runningSum (soundIn 4) 40))
+
+Running Average over x samples
+> let {x = 100
+>     ;o = runningSum (lfSaw AR 440 0) x * recip x}
+> in audition (out 0 o)
+
+RMS Power
+> let {input = lfSaw AR 440 0
+>     ;numsamp = 30
+>     ;o = runningSum (input * input) numsamp / (sqrt numsamp)}
+> in audition (out 0 o)
+
+composite UGen
+> audition (out 0 (runningSumRMS (soundIn 4) 40))
+
+> let {z = soundIn 4
+>     ;a = runningSum z 40}
+> in audition (out 0 (sinOsc AR 440 0 * a * 0.1))
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
@@ -4,10 +4,12 @@
 > import Sound.SC3
 
 Load sound file to buffer zero (required for examples)
+
 > let fn = "/home/rohan/data/audio/pf-c5.aif"
 > 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
 >     ;p = sweep t (bufSampleRate KR 0)}
 > in audition (out 0 (bufRdL 1 AR 0 p NoLoop))
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
@@ -1,17 +1,38 @@
 > Sound.SC3.UGen.Help.viewSC3Help "BufRd"
 > Sound.SC3.UGen.DB.ugenSummary "BufRd"
 
-> import Sound.SC3.ID
+> import Sound.SC3.ID {- hsc3 -}
 
 Load sound file to buffer zero (required for examples)
+
 > let fn = "/home/rohan/data/audio/pf-c5.aif"
 > in withSC3 (async (b_allocRead 0 fn 0 0))
 
+Phasor as phase input
+
+> let {sc = bufRateScale KR 0
+>     ;tr = impulse AR (recip (bufDur KR 0)) 0
+>     ;ph = phasor AR tr sc 0 (bufFrames KR 0) 0}
+> in audition (out 0 (bufRdL 1 AR 0 ph NoLoop))
+
 Audio rate sine oscillator as phase input
-> let phase = sinOsc AR 0.1 0 * bufFrames KR 0
+
+> let phase = sinOsc AR 0.1 0 * bufFrames KR 0 * bufRateScale 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
->     ;n = lfNoise1 'a' AR x}
-> in audition (out 0 (bufRdL 1 AR 0 (n * bufFrames KR 0) Loop))
+>     ;n = lfNoise1 'α' AR x}
+> in audition (out 0 (bufRdL 1 AR 0 (n * bufFrames KR 0 * bufRateScale KR 0) Loop))
+
+Allocate and generate (non-wavetable) buffer
+
+> withSC3 (do {_ <- async (b_alloc 11 256 1)
+>             ;let f = [Normalise,Clear]
+>              in send (b_gen_sine1 11 f [1,1/2,1/3,1/4,1/5])})
+
+Fixed frequency wavetable oscillator
+
+> let phase = linLin (saw AR 440) (-1) 1 0 1 * bufFrames KR 11
+> in audition (out 0 (bufRd 1 AR 11 phase Loop NoInterpolation * 0.1))
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,33 +4,41 @@
 > import Sound.SC3
 
 Allocate and generate wavetable buffer
+
 > 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))
 
 Modulate frequency
+
 > let f = xLine KR 2000 200 1 DoNothing
 > in audition (out 0 (osc AR 10 f 0 * 0.1))
 
 As frequency modulator
+
 > let f = osc AR 10 (xLine KR 1 1000 9 RemoveSynth) 0 * 200 + 800
 > in audition (out 0 (osc AR 10 f 0 * 0.1))
 
 As phase modulatulator
+
 > let p = osc AR 10 (xLine KR 20 8000 10 RemoveSynth) 0 * 2 * pi
 > in audition (out 0 (osc AR 10 800 p * 0.1))
 
 Fixed frequency wavetable oscillator
+
 > audition (out 0 (osc AR 10 220 0 * 0.1))
 
 Change the wavetable while its playing
+
 > 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)
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
@@ -4,37 +4,82 @@
 > import Sound.SC3
 
 Load sound file to buffer zero (single channel file required for examples)
+
 > let fn = "/home/rohan/data/audio/pf-c5.aif"
 > in withSC3 (async (b_allocRead 0 fn 0 0))
 
 Play once only.
+
 > let s = bufRateScale KR 0
 > in audition (out 0 (playBuf 1 AR 0 s 1 0 NoLoop RemoveSynth))
 
 Play in infinite loop.
+
 > let s = bufRateScale KR 0
 > in audition (out 0 (playBuf 1 AR 0 s 1 0 Loop DoNothing))
 
 Trigger playback at each pulse.
+
 > let {t = impulse KR 2 0
 >     ;s = bufRateScale KR 0}
 > in audition (out 0 (playBuf 1 AR 0 s t 0 NoLoop DoNothing))
 
 Trigger playback at each pulse (diminishing intervals).
+
 > let {f = xLine KR 0.1 100 10 RemoveSynth
 >     ;t = impulse KR f 0
 >     ;s = bufRateScale KR 0}
 > in audition (out 0 (playBuf 1 AR 0 s t 0 NoLoop DoNothing))
 
 Loop playback, accelerating pitch.
+
 > let r = xLine KR 0.1 100 60 RemoveSynth
 > in audition (out 0 (playBuf 1 AR 0 r 1 0 Loop DoNothing))
 
 Sine wave control of playback rate, negative rate plays backwards.
+
 > let {f = xLine KR 0.2 8 30 RemoveSynth
 >     ;r = fSinOsc KR f 0 * 3 + 0.6
 >     ;s = bufRateScale KR 0 * r}
 > in audition (out 0 (playBuf 1 AR 0 s 1 0 Loop DoNothing))
 
 Release buffer.
+
 > withSC3 (send (b_free 0))
+
+Channel mismatch, single channel buffer, two channel playBuf, result
+is silence and channel mismatch message in server log.
+
+> let fn = "/home/rohan/data/audio/pf-c5.aif"
+> in withSC3 (async (b_allocRead 0 fn 0 0))
+
+> let s = bufRateScale KR 0
+> in audition (out 0 (playBuf 2 AR 0 s 1 0 Loop DoNothing))
+
+Graph will sound after loading a two channel signal to buffer, and
+stop again after loading a single channel sound file.
+
+> let fn = "/home/rohan/data/audio/sp/tinguely.aif"
+> in withSC3 (async (b_allocRead 0 fn 0 0))
+
+Scan sequence of buffers:
+
+> let {n = 29 * 6
+>     ;b = mouseX KR 0 n Linear 0.2
+>     ;r = bufRateScale KR b}
+> in audition (out 0 (playBuf 1 AR b r 1 0 Loop DoNothing))
+
+In sclanguage:
+
+{var fn = "/home/rohan/data/audio/pf-c5.aif"
+;s.sendMsg("/b_allocRead",0,fn,0,0)}.value
+
+{var sc = BufRateScale.kr(0)
+;Out.ar(0,PlayBuf.ar(2,0,sc,1,0,1,0))}.play
+
+{var fn = "/home/rohan/data/audio/sp/tinguely.aif"
+;s.sendMsg("/b_allocRead",0,fn,0,0)}.value
+
+{var b = MouseX.kr(32,64,0,0.2)
+;var r = BufRateScale.kr(b)
+;Out.ar(0,PlayBuf.ar(1,b,r,1,0,1,0))}.play
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
@@ -1,24 +1,25 @@
 > Sound.SC3.UGen.Help.viewSC3Help "RecordBuf"
 > Sound.SC3.UGen.DB.ugenSummary "RecordBuf"
 
-# SC3
-reorders inputArray from last to first argument.
-
 > import Sound.SC3
 
 Allocate a buffer (assume SR of 48k)
+
 > 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
 > in audition (mrg2 (out 0 o)
 >                   (recordBuf AR 0 0 1 0 1 NoLoop 1 RemoveSynth o))
 
 Play it back
+
 > let p = playBuf 1 AR 0 1 1 0 NoLoop RemoveSynth
 > in audition (out 0 p)
 
-Mix second signal equally with existing signal
+Mix second signal equally with existing signal, replay to hear
+
 > let o = formant AR (xLine KR 200 1000 4 DoNothing) 2000 800 * 0.125
 > in audition (mrg2 (out 0 o)
 >                   (recordBuf AR 0 0 0.5 0.5 1 NoLoop 1 RemoveSynth o))
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
@@ -4,6 +4,7 @@
 > 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)
@@ -15,12 +16,15 @@
 > in withSC3 (mapM_ setup [0 .. 7])
 
 Oscillator at buffers 0 through 7, mouse selects buffer.
-> let x = mouseX KR 0 7 Linear 0.1
-> in audition (out 0 (vOsc AR x (mce [120, 121]) 0 * 0.3))
 
-> import Sound.SC3.Lang.Random.IO
+> let {x = mouseX KR 0 7 Linear 0.1
+>     ;y = mouseY KR 0.01 0.2 Exponential 0.2}
+> in audition (out 0 (vOsc AR x (mce [120, 121]) 0 * y))
 
+> import Sound.SC3.Lang.Random.IO {- hsc3-lang -}
+
 Reallocate buffers while oscillator is running.
+
 > let {bf = [Normalise,Wavetable,Clear]
 >     ;resetTable i = do {h <- nrrand 12 0 1
 >                        ;send (b_gen_sine1 i bf h)}}
diff --git a/Help/UGen/Buffer/vOsc3.help.lhs b/Help/UGen/Buffer/vOsc3.help.lhs
--- a/Help/UGen/Buffer/vOsc3.help.lhs
+++ b/Help/UGen/Buffer/vOsc3.help.lhs
@@ -4,6 +4,7 @@
 > 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)
@@ -15,7 +16,9 @@
 > in withSC3 (mapM_ setup [0 .. 7])
 
 oscillator at buffers 0 through 7, mouse selects buffer.
+
 > let {x = mouseX KR 0 7 Linear 0.1
+>     ;y = mouseY KR 0.01 0.2 Exponential 0.2
 >     ;o1 = vOsc3 AR x 120 121 129
 >     ;o2 = vOsc3 AR x 119 123 127}
-> in audition (out 0 (mce2 o1 o2 * 0.3))
+> in audition (out 0 (mce2 o1 o2 * y))
diff --git a/Help/UGen/Chaos/crackle.help.lhs b/Help/UGen/Chaos/crackle.help.lhs
--- a/Help/UGen/Chaos/crackle.help.lhs
+++ b/Help/UGen/Chaos/crackle.help.lhs
@@ -2,7 +2,9 @@
 > Sound.SC3.UGen.DB.ugenSummary "Crackle"
 
 > import Sound.SC3
+
 > audition (out 0 (crackle AR 1.95 * 0.2))
 
 Modulate chaos parameter
+
 > audition (out 0 (crackle AR (line KR 1.0 2.0 3 RemoveSynth) * 0.2))
diff --git a/Help/UGen/Control/trigControl.help.lhs b/Help/UGen/Control/trigControl.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/Control/trigControl.help.lhs
@@ -0,0 +1,38 @@
+> import Sound.SC3 {- hsc3 -}
+
+Graph with the three types of non-audio controls.
+
+> let u = let {freq = control KR "freq" 440
+>             ;phase = control IR "phase" 0
+>             ;gate = tr_control "gate" 1
+>             ;amp = control KR "amp" 0.1
+>             ;e = envGen KR gate amp 0 1 DoNothing (envASR 0.01 0.1 1 EnvLin)}
+>         in sinOsc AR freq phase * e
+
+Make a drawing
+
+> import Sound.SC3.UGen.Dot {- hsc3-ugen -}
+
+> draw (out 0 u)
+
+Listen
+
+> audition_at (10,AddToHead,1) (out 0 u)
+
+Set frequency and the trigger gate.
+
+> withSC3 (send (n_set1 10 "freq" 2200))
+
+> withSC3 (send (n_set1 10 "gate" 1))
+
+Make a control rate graph to write freq and gate values.
+
+> let c = out 0 (mce2 (tRand 'α' 220 2200 (dust 'β' KR 1)) (dust 'γ' KR 3))
+
+Add it _before_ the node it will map to, the trigger is only on the bus for the current cycle.
+
+> audition_at (-1,AddBefore,10) c
+
+Map the control values at the audio graph.
+
+> withSC3 (send (n_map 10 [("freq",0),("gate",1)]))
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
@@ -3,7 +3,7 @@
 
 > import Sound.SC3.ID
 
-> let {n = dbrown 'a' dinf 0 15 1
+> let {n = dbrown 'α' dinf 0 15 1
 >     ;x = mouseX KR 1 40 Exponential 0.1
 >     ;t = impulse KR x 0
 >     ;f = demand t 0 n * 30 + 340}
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
@@ -1,35 +1,40 @@
 > Sound.SC3.UGen.Help.viewSC3Help "Dbufrd"
 > Sound.SC3.UGen.DB.ugenSummary "Dbufrd"
 
-> import Sound.SC3.ID
-> import System.Random
+> import Sound.SC3.ID {- hsc3 -}
+> import System.Random {- random -}
 
 setup pattern at buffer 10
+
 > let n = randomRs (200.0,500.0) (mkStdGen 0)
 > 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])
->     ;b = dbrown 'a' 5 0 23 1
->     ;p = dseq 'a' dinf (mce [s,b])
->     ;t = dust 'a' KR 10
->     ;r = dbufrd 'a' 10 p Loop}
+
+> let {s = dseq 'α' 3 (mce [0,3,5,0,3,7,0,5,9])
+>     ;b = dbrown 'β' 5 0 23 1
+>     ;p = dseq 'γ' dinf (mce [s,b])
+>     ;t = dust 'δ' KR 10
+>     ;r = dbufrd 'ε' 10 p Loop}
 > in audition (out 0 (sinOsc AR (demand t 0 r) 0 * 0.1))
 
 setup time pattern
+
 > let {i = randomRs (0,2) (mkStdGen 0)
 >     ;n = map ([1,0.5,0.25] !!) i}
 > 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])
->     ;b = dbrown 'a' 5 0 23 1
->     ;p = dseq 'a' dinf (mce [s,b])
->     ;j = dseries 'a' dinf 0 1
->     ;d = dbufrd 'a' 11 j Loop
->     ;l = dbufrd 'a' 10 p Loop
+
+> let {s = dseq 'α' 3 (mce [0,3,5,0,3,7,0,5,9])
+>     ;b = dbrown 'β' 5 0 23 1
+>     ;p = dseq 'γ' dinf (mce [s,b])
+>     ;j = dseries 'δ' dinf 0 1
+>     ;d = dbufrd 'ε' 11 j Loop
+>     ;l = dbufrd 'ζ' 10 p Loop
 >     ;f = duty KR (d * 0.5) 0 DoNothing l}
 > in audition (out 0 (sinOsc AR f 0 * 0.1))
 
 free buffers
+
 > 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
@@ -1,8 +1,8 @@
 > Sound.SC3.UGen.Help.viewSC3Help "Dbufwr"
 > Sound.SC3.UGen.DB.ugenSummary "Dbufwr"
 
-> import Sound.SC3
-> import qualified Sound.SC3.Monadic as M
+> import Sound.SC3 {- hsc3 -}
+> import qualified Sound.SC3.Monad as M {- hsc3 -}
 
 > do {s1 <- M.dseries 30 0 3
 >    ;s2 <- M.dseries 30 0 1
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
@@ -11,7 +11,7 @@
 >         ;o = sinOsc AR (mce [f,f + 0.7]) 0}
 >     in audition (out 0 (max (cubed o) 0 * 0.1))}
 
-> let {n = diwhite 'a' dinf 60 72
+> let {n = diwhite 'α' dinf 60 72
 >     ;t = impulse KR 10 0
 >     ;s = midiCPS n
 >     ;f = demand t 0 s
@@ -19,8 +19,9 @@
 > 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])
+
+> let {i = lfNoise2 'α' AR 8000
+>     ;d = dseq 'β' dinf (mce [i])
 >     ;x = mouseX KR 1 3000 Exponential 0.2
 >     ;t = impulse AR x 0
 >     ;x1 = demand t 0 d
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
@@ -4,22 +4,25 @@
 > import Sound.SC3.ID
 
 Frequency ramp, exponential curve.
-> let {l = dseq 'a' dinf (mce2 440 9600)
+
+> let {l = dseq 'α' dinf (mce2 440 9600)
 >     ;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.
-> 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])
+
+> let {l = dseq 'α' dinf (mce [204,400,201,502,300,200])
+>     ;t = drand 'β' 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
+Frequency modulation
+
+> let {n = dwhite 'α' 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)
@@ -27,14 +30,16 @@
 >     ;o = sinOsc AR f 0 * 0.1}
 > in audition (out 0 o)
 
-short sequence with doneAction, linear
-> let {l = dseq 'a' 1 (mce [1300,500,800,300,400])
+Short sequence with doneAction, linear
+
+> let {l = dseq 'α' 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
+Gate, mouse x on right side of screen toggles gate
+
+> let {n = roundTo (dwhite 'α' 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
@@ -44,7 +49,8 @@
 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])
+
+> let {l = dseq 'α' 2 (mce [dseries 'β' 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
@@ -56,13 +62,30 @@
 
 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
+> let {b = 0
+>     ;l_i = dseries 'β' dinf 0 2
+>     ;d_i = dseries 'γ' dinf 1 2
+>     ;l = dbufrd 'δ' b l_i Loop
+>     ;d = dbufrd 'ε' b 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))
+
+change envelope by setting values or indeed reallocating buffer
+
+> withSC3 (send (b_set1 0 1 0.1))
+> withSC3 (async (b_alloc_setn1 0 0 [0.5,0.9,0.1,0.1,1,0.01]))
+
+read envelope break-points from buffer, here simply duration/level pairs.
+the behavior is odd if the curve is zero (ie. flat segments).
+
+> let {b = asLocalBuf 'α' [61,1,60,2,72,1,55,5,67,9,67]
+>     ;lvl = dbufrd 'β' b (dseries 'γ' 6 0 2) Loop
+>     ;dur = dbufrd 'δ' b (dseries 'ε' 5 1 2) Loop
+>     ;e = demandEnvGen KR lvl dur 1 0 1 1 1 0 1 RemoveSynth
+>     ;o = sinOsc AR (midiCPS e) 0 * 0.1}
+> in audition (out 0 o)
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
@@ -3,7 +3,7 @@
 
 > import Sound.SC3.ID
 
-> let {n = dgeom 'a' 15 1 1.2
+> let {n = dgeom 'α' 15 1 1.2
 >     ;x = mouseX KR 1 40 Exponential 0.1
 >     ;t = impulse KR x 0
 >     ;f = demand t 0 n * 30 + 340}
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
@@ -3,7 +3,7 @@
 
 > import Sound.SC3.ID
 
-> let {n = drand 'a' dinf (mce [1, 3, 2, 7, 8])
+> let {n = drand 'α' dinf (mce [1, 3, 2, 7, 8])
 >     ;x = mouseX KR 1 400 Exponential 0.1
 >     ;t = impulse KR x 0
 >     ;f = demand t 0 n * 30 + 340}
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,27 +1,36 @@
 > 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])
+> let {n = dseq 'α' 3 (mce [1, 3, 2, 7, 8])
 >     ;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])
+
+> let {n = dseq 'α' dinf (mce [1,3,2,7,8,32,16,18,12,24])
 >     ;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))
 
 The SC2 Sequencer UGen is somewhat like the sequ function below
+
 > let {sequ e s tr = demand tr 0 (dseq e dinf (mce s))
->     ;t = lfPulse AR 6 0 0.5
->     ;n0 = sequ 'a' [60,62,63,58,48,55] t
->     ;n1 = sequ 'b' [63,60,48,62,55,58] t
+>     ;t = impulse AR 6 0
+>     ;n0 = sequ 'α' [60,62,63,58,48,55] t
+>     ;n1 = sequ 'β' [63,60,48,62,55,58] t
 >     ;o = lfSaw AR (midiCPS (mce2 n0 n1)) 0 * 0.1}
 > in audition (out 0 o)
+
+Rather than MCE expansion at /tr/, it can be clearer to view /tr/ as a
+functor.
+
+> let {tr = impulse KR (mce [2,3,5]) 0
+>     ;f t = demand t 0 (dseq t dinf (mce [60,63,67,69]))
+>     ;m = mceMap f tr
+>     ;o = sinOsc AR (midiCPS m) 0 * 0.1}
+> in audition (out 0 (splay o 1 1 0 True))
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
@@ -3,7 +3,7 @@
 
 > import Sound.SC3.ID
 
-> let {a = dser 'a' 7 (mce [1, 3, 2, 7, 8])
+> let {a = dser 'α' 7 (mce [1, 3, 2, 7, 8])
 >     ;x = mouseX KR 1 40 Exponential 0.1
 >     ;t = impulse KR x 0
 >     ;f = demand t 0 a * 30 + 340}
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
@@ -3,7 +3,7 @@
 
 > import Sound.SC3.ID
 
-> let {n = dseries 'a' 15 0 1
+> let {n = dseries 'α' 15 0 1
 >     ;x = mouseX KR 1 40 Exponential 0.1
 >     ;t = impulse KR x 0
 >     ;f = demand t 0 n * 30 + 340}
diff --git a/Help/UGen/Demand/dshuf.help.lhs b/Help/UGen/Demand/dshuf.help.lhs
--- a/Help/UGen/Demand/dshuf.help.lhs
+++ b/Help/UGen/Demand/dshuf.help.lhs
@@ -1,19 +1,17 @@
 > Sound.SC3.UGen.Help.viewSC3Help "Dshuf"
 > Sound.SC3.UGen.DB.ugenSummary "Dshuf"
 
-# sclang re-orders inputs
-
-> import Sound.SC3.ID
+> import Sound.SC3.ID {- hsc3 -}
 
-> let {a = dseq 'a' dinf (dshuf 'a' 3 (mce [1,3,2,7,8.5]))
+> let {a = dseq 'α' dinf (dshuf 'β' 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
+> import Sound.SC3.UGen.External.RDU.ID {- sc3-rdu -}
 
-> let {a = dseq 'a' dinf (dshuf 'a' 5 (randN 81 'a' 0 10))
+> let {a = dseq 'α' dinf (dshuf 'β' 5 (randN 81 'γ' 0 10))
 >     ;x = mouseX KR 1 10000 Exponential 0.1
 >     ;t = impulse AR x 0
 >     ;f = demand t 0 a * 30 + 340}
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
@@ -3,14 +3,15 @@
 
 > import Sound.SC3.ID
 
-> let {inp = dseq 'a' dinf (mce [1,2,3])
->     ;nse = diwhite 'a' dinf 2 8
->     ;rep = dstutter 'a' nse inp
+> let {inp = dseq 'α' dinf (mce [1,2,3])
+>     ;nse = diwhite 'β' dinf 2 8
+>     ;rep = dstutter 'γ' nse inp
 >     ;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
diff --git a/Help/UGen/Demand/dswitch.help.lhs b/Help/UGen/Demand/dswitch.help.lhs
--- a/Help/UGen/Demand/dswitch.help.lhs
+++ b/Help/UGen/Demand/dswitch.help.lhs
@@ -2,24 +2,24 @@
 > Sound.SC3.UGen.DB.ugenSummary "Dswitch"
 
 > import Sound.SC3
-> import qualified Sound.SC3.Monadic as M
 
-> do {a0 <- M.dwhite 2 3 4
->    ;a1 <- M.dwhite 2 0 1
->    ;a2 <- M.dseq 2 (mce [1,1,1,0])
->    ;i <- M.dseq 2 (mce [0,1,2,1,0])
->    ;d <- M.dswitch i (mce [a0,a1,a2])
+> do {a0 <- dwhiteM 2 3 4
+>    ;a1 <- dwhiteM 2 0 1
+>    ;a2 <- dseqM 2 (mce [1,1,1,0])
+>    ;i <- dseqM 2 (mce [0,1,2,1,0])
+>    ;d <- dswitchM i (mce [a0,a1,a2])
 >    ;let {t = impulse KR 4 0
 >         ;f = demand t 0 d * 300 + 400
 >         ;o = sinOsc AR f 0 * 0.1}
 >      in audition (out 0 o)}
 
 compare with dswitch1
-> do {a0 <- M.dwhite 2 3 4
->    ;a1 <- M.dwhite 2 0 1
->    ;a2 <- M.dseq 2 (mce [1,1,1,0])
->    ;i <- M.dseq 2 (mce [0,1,2,1,0])
->    ;d <- M.dswitch1 i (mce [a0,a1,a2])
+
+> do {a0 <- dwhiteM 2 3 4
+>    ;a1 <- dwhiteM 2 0 1
+>    ;a2 <- dseqM 2 (mce [1,1,1,0])
+>    ;i <- dseqM 2 (mce [0,1,2,1,0])
+>    ;d <- dswitch1M i (mce [a0,a1,a2])
 >    ;let {t = impulse KR 4 0
 >         ;f = demand t 0 d * 300 + 400
 >         ;o = sinOsc AR f 0 * 0.1}
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
@@ -6,7 +6,7 @@
 > 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])
+>     ;w = dwhite 'α' dinf 20 23
+>     ;n = dswitch1 'β' x (mce [1, 3, y, 2, w])
 >     ;f = demand t 0 n * 30 + 340}
 > in audition (out 0 (sinOsc AR f 0 * 0.1))
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,10 +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
+> import qualified Sound.SC3.Monad as M
 
 > do {n0 <- M.drand dinf (mce [0.01,0.2,0.4])
 >    ;n1 <- M.dseq dinf (mce [204,400,201,502,300,200])
@@ -12,7 +10,8 @@
 >     in audition (out 0 (sinOsc AR (f * mce2 1 1.01) 0 * 0.1))}
 
 Using control rate signal, mouseX, to determine duration.
-> let {n = dseq 'a' dinf (mce [204,400,201,502,300,200])
+
+> let {n = dseq 'α' dinf (mce [204,400,201,502,300,200])
 >     ;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
@@ -1,9 +1,9 @@
 > Sound.SC3.UGen.Help.viewSC3Help "Dwhite"
 > Sound.SC3.UGen.DB.ugenSummary "Dwhite"
 
-> import Sound.SC3.ID
+> import Sound.SC3
 
-> let {n = dwhite 'a' dinf 0 15
+> let {n = dwhite 'α' 30 0 15
 >     ;x = mouseX KR 1 40 Exponential 0.1
 >     ;t = impulse KR x 0
 >     ;f = demand t 0 n * 30 + 340}
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
@@ -1,9 +1,9 @@
 > Sound.SC3.UGen.Help.viewSC3Help "Dwrand"
 > Sound.SC3.UGen.DB.ugenSummary "Dwrand"
 
-> import Sound.SC3.ID
+> import Sound.SC3
 
-> let {n = dwrand 'a' dinf (mce [1,3,2,7,8]) (mce [0.4,0.4,0.05,0.05,0.1])
+> let {n = dwrand 'α' dinf (mce [0.3,0.2,0.1,0.2,0.2]) (mce [1,3,2,7,8])
 >     ;x = mouseX KR 1 400 Exponential 0.1
 >     ;t = impulse KR x 0
 >     ;f = demand t 0 n * 30 + 340}
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
@@ -7,9 +7,9 @@
 > 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])]
->     ;d = dxrand 'b' dinf i
->     ;t = tDuty AR d 0 DoNothing (dwhite 'c' dinf 0.5 1) 0}
+> let {i = mce [0.2,0.4,dseq 'α' 2 (mce [0.1,0.1])]
+>     ;d = dxrand 'β' dinf i
+>     ;t = tDuty AR d 0 DoNothing (dwhite 'γ' dinf 0.5 1) 0}
 > in audition (out 0 t) >> drw t
 
 The list inputs to demand rate ugens may operate at different rates.
@@ -18,14 +18,15 @@
 maximum rate of the inputs and then does not revise this after mce
 transformation, where it may be lower.  The hsc3 constructors attempt
 to get this right!
-> let {i = mce [0.2,0.4,dseq 'a' 2 (mce [0.1,0.1])]
+
+> let {i = mce [0.2,0.4,dseq 'α' 2 (mce [0.1,0.1])]
 >     ;i' = mceMap (* 0.5) i
 >     ;i'' = i * 0.5
->     ;d = dxrand 'c' dinf i''
->     ;t = tDuty AR d 0 DoNothing (dwhite 'c' dinf 0.5 1) 0}
+>     ;d = dxrand 'β' dinf i''
+>     ;t = tDuty AR d 0 DoNothing (dwhite 'γ' dinf 0.5 1) 0}
 > in audition (out 0 t) >> drw t
 
-> let {n = dxrand 'a' dinf (mce [1, 3, 2, 7, 8])
+> let {n = dxrand 'α' dinf (mce [1, 3, 2, 7, 8])
 >     ;x = mouseX KR 1 400 Exponential 0.1
 >     ;t = impulse KR x 0
 >     ;f = demand t 0 n * 30 + 340}
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
@@ -4,23 +4,41 @@
 > import Sound.SC3.ID
 
 Play a little rhythm
-> let d = dseq 'a' dinf (mce [0.1, 0.2, 0.4, 0.3])
+
+> let d = dseq 'α' dinf (mce [0.1, 0.2, 0.4, 0.3])
 > in audition (out 0 (tDuty AR d 0 DoNothing 1 0))
 
 Amplitude changes
-> let {d0 = dseq '0' dinf (mce [0.1, 0.2, 0.4, 0.3])
->     ;d1 = dseq '1' dinf (mce [0.1, 0.4, 0.01, 0.5, 1.0])
+
+> let {d0 = dseq 'α' dinf (mce [0.1, 0.2, 0.4, 0.3])
+>     ;d1 = dseq 'β' dinf (mce [0.1, 0.4, 0.01, 0.5, 1.0])
 >     ;s = ringz (tDuty AR d0 0 DoNothing d1 1) 1000 0.1}
 > in audition (out 0 s)
 
 Mouse control.
-> let {d = dseq 'a' dinf (mce [0.1, 0.4, 0.01, 0.5, 1.0])
+
+> let {d = dseq 'α' dinf (mce [0.1, 0.4, 0.01, 0.5, 1.0])
 >     ;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)
 
 Note that the 440 is the shorter pitch, since gap is set to false
-> let {d0 = dser '0' 12 (mce [0.1, 0.3])
->     ;d1 = dser '1' 12 (mce [440, 880])
+
+> let {d0 = dser 'α' 12 (mce [0.1, 0.3])
+>     ;d1 = dser 'β' 12 (mce [440, 880])
 >     ;t = tDuty AR d0 0 RemoveSynth d1 0}
 > in audition (out 0 (sinOsc AR (latch t t) 0 * 0.1))
+
+Abstraction
+
+> import Data.List
+
+> let {bp n d act = let {(e,t) = unzip d
+>                       ;mk z l = dser z n (mce l)
+>                       ;sq = tDuty AR (mk 'α' t) 0 act (mk 'β' e) 0}
+>                   in latch sq sq
+>     ;bp' d = bp (genericLength d) d
+>     ;tm m = let f (e,t) = (e,t * m) in map f
+>     ;f1 = midiCPS (bp 35 (tm 0.125 [(60,1),(63,1),(67,2),(68,1),(62,1)]) RemoveSynth)
+>     ;f2 = midiCPS (bp' [(60,1),(63,0.5),(67,0.5),(68,1),(62,1)] DoNothing)}
+> in audition (out 0 (sinOsc AR (mce2 f1 f2) 0 * 0.1))
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
@@ -3,12 +3,12 @@
 
 > import Sound.SC3
 
-> let {f = "/home/rohan/data/audio/pf-c5.snd"
->     ;n = 1
->     ;g = out 0 (diskIn n 0 Loop)}
-> in withSC3 (do {_ <- async (b_alloc 0 65536 n)
->                ;_ <- async (b_read 0 f 0 (-1) 0 True)
->                ;play g})
+> let {fn = "/home/rohan/data/audio/pf-c5.snd"
+>     ;nc = 1
+>     ;gr = out 0 (diskIn nc 0 Loop)}
+> in withSC3 (do {_ <- async (b_alloc 0 65536 nc)
+>                ;_ <- async (b_read 0 fn 0 (-1) 0 True)
+>                ;play gr})
 
 > withSC3 (do {reset
 >             ;_ <- async (b_close 0)
diff --git a/Help/UGen/DiskIO/diskOut.help.lhs b/Help/UGen/DiskIO/diskOut.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/DiskIO/diskOut.help.lhs
@@ -0,0 +1,41 @@
+> Sound.SC3.UGen.Help.viewSC3Help "DiskOut"
+> Sound.SC3.UGen.DB.ugenSummary "DiskOut"
+
+> import Sound.OSC {- hosc -}
+> import Sound.SC3.ID {- hsc3 -}
+
+Example graph
+
+> let gr = let d = xLine KR 20000 2 10 RemoveSynth
+>          in dust 'α' AR d * 0.15
+
+> let gr = soundIn 0
+
+Check incoming signal (either graph above or the outside world)
+
+> audition (out 0 gr)
+
+Record incoming signal (or above...), print some informational traces...
+
+> let trace str = liftIO (putStrLn str)
+
+> withSC3 (do {trace "b_alloc & b_write"
+>             ;_ <- async (b_alloc 0 65536 1)
+>             ;_ <- async (b_write 0 "/tmp/disk-out.aiff" Aiff PcmInt16 (-1) 0 True)
+>             ;trace "record for 10 seconds"
+>             ;playSynthdef 2001 (synthdef "disk-out" (diskOut 0 gr))
+>             ;pauseThread 10
+>             ;trace "stop recording and tidy up"
+>             ;send (n_free [2001])
+>             ;_ <- async (b_close 0)
+>             ;_ <- async (b_free 0)
+>             ;return ()})
+
+Listen to recording (on loop...)
+
+> let {fn = "/tmp/disk-out.aiff"
+>     ;nc = 1
+>     ;gr = out 0 (diskIn nc 0 Loop)}
+> in withSC3 (do {_ <- async (b_alloc 0 65536 nc)
+>                ;_ <- async (b_read 0 fn 0 (-1) 0 True)
+>                ;play gr})
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
@@ -1,14 +1,14 @@
 > Sound.SC3.UGen.Help.viewSC3Help "VDiskIn"
 > Sound.SC3.UGen.DB.ugenSummary "VDiskIn"
 
-> import Sound.SC3
+> import Sound.SC3 {- hsc3 -}
 
-> 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 (do {_ <- async (b_alloc 0 8192 n)
->                ;_ <- async (b_read 0 f 0 (-1) 0 True)
->                ;play g })
+> let {fn = "/home/rohan/data/audio/pf-c5.snd"
+>     ;nc = 1
+>     ;gr = out 0 (vDiskIn nc 0 (sinOsc KR 0.25 0 * 0.25 + 1) Loop)}
+> in withSC3 (do {_ <- async (b_alloc 0 8192 nc)
+>                ;_ <- async (b_read 0 fn 0 (-1) 0 True)
+>                ;play gr})
 
 > withSC3 (do {reset
 >             ;_ <- async (b_close 0)
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,37 +1,42 @@
-
+> Sound.SC3.UGen.Help.viewSC3Help "Env.*adsr"
 > :i Sound.SC3.ADSR
 > :t Sound.SC3.envADSR_r
 > :t Sound.SC3.envADSR
 
-> import Sound.SC3
+> import Sound.SC3 {- hsc3 -}
 
-> let {g = control KR "gate" 1
->     ;p = envADSR 0.75 0.75 0.5 0.75 1 (EnvNum (-4)) 0
->     ;e = envGen KR g 0.1 0 1 DoNothing p}
-> in audition (out 0 (sinOsc AR 440 0 * e))
+{SinOsc.ar * EnvGen.kr(Env.adsr(0.75, 2.75, 0.1, 7.25, 1, -4, 0))}.draw;
 
-> withSC3 (send (n_set1 (-1) "gate" 0))
-> withSC3 (send (n_set1 (-1) "gate" 1))
+> let g = let {c = control KR "env-gate" 1
+>             ;p = envADSR 0.75 2.75 0.1 7.25 1 (EnvNum (-4)) 0
+>             ;e = envGen KR c 1 0 1 DoNothing p}
+>         in sinOsc AR 440 0 * e * 0.1
+
+> audition (out 0 g)
+
+> withSC3 (send (n_set1 (-1) "env-gate" 0))
+> withSC3 (send (n_set1 (-1) "env-gate" 1))
 > withSC3 (send (n_free [-1]))
 
-> import Sound.SC3.Plot
+> import Sound.SC3.Plot {- hsc3 -}
 
 > 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]
+>              ,envADSR 0 2 1 0.1 0.5 EnvSin 0
+>              ,envADSR 0.001 1.54 1 0.001 0.4 EnvSin 0]
 
 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}
+>     ;r = ADSR {adsr_attackTime = 0.75
+>               ,adsr_decayTime = 0.75
+>               ,adsr_sustainLevel = 0.5
+>               ,adsr_releaseTime = 0.75
+>               ,adsr_peakLevel = 1
+>               ,adsr_curve = (c,c,c)
+>               ,adsr_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))
@@ -39,3 +44,17 @@
 > withSC3 (send (n_set1 (-1) "gate" 0))
 > withSC3 (send (n_set1 (-1) "gate" 1))
 > withSC3 (send (n_free [-1]))
+
+SC3 comparison:
+
+Env.adsr.asArray == [0,3,2,-99,1,0.01,5,-4,0.5,0.3,5,-4,0,1,5,-4];
+
+> let r = [0,3,2,-99,1,0.01,5,-4,0.5,0.3,5,-4,0,1,5,-4]
+> in envelope_sc3_array (envADSR 0.01 0.3 0.5 1 1 (EnvNum (-4)) 0) == Just r
+
+> let r = [0,3,2,-99,1,0.3,5,-4,0.1,0.4,5,-4,0,1.2,5,-4]
+> in envelope_sc3_array (envADSR 0.3 0.4 0.1 1.2 1 (EnvNum (-4)) 0) == Just r
+
+x = {|gate=0, freq=440 | EnvGen.kr(Env.adsr,gate) * SinOsc.ar(freq,0) * 0.1}.play
+x.set(\gate,1);
+x.set(\gate,0);
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,16 +1,17 @@
 > Sound.SC3.UGen.Help.viewSC3Help "Env.*asr"
-> :t Sound.SC3.envASR
+> :i Sound.SC3.ASR
 
 > import Sound.SC3
 
-> let {g = control KR "gate" 1
+> let {g = control KR "env-gate" 1
 >     ;p = envASR 0.01 1 1 (EnvNum (-4))
 >     ;e = envGen KR g 0.1 0 1 RemoveSynth p}
 > in audition (out 0 (sinOsc AR 440 0 * e))
 
-> withSC3 (send (n_set1 (-1) "gate" 0))
+> withSC3 (send (n_set1 (-1) "env-gate" 0))
 
 > import Sound.SC3.Plot
 
 > plotEnvelope [envASR 0.1 1 1 (EnvNum (-4))
->              ,envASR 0.3 0.25 1 EnvSin]
+>              ,envASR 0.3 0.25 1 EnvSin
+>              ,envASR 0.01 0.5 1.25 EnvLin]
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
@@ -2,12 +2,14 @@
 > :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
@@ -22,7 +24,8 @@
 
 > import Sound.SC3.ID
 
-likewise, but internal graph triggers and line end points
+likewise, but internal graph triggers and randomises line end points
+
 > let {tr = dust 'α' KR 2
 >     ;st = 440
 >     ;en = tRand 'β' 300 900 tr
@@ -32,8 +35,14 @@
 > in audition (out 0 (sinOsc AR e 0 * 0.2))
 
 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]
+>     ;c1 = [(0,0),(0.15,0.6),(0.35,0.2),(1,0)]
+>     ;c2 = [(0,0),(0.65,0.3),(0.85,0.7),(1,0)]
+>     ;c3 = [(0,0.1),(0.25,0.6),(0.5,0.4),(1,0.4)]}
+> in plotEnvelope [envCoord c0 9 0.1 EnvLin
+>                 ,envCoord c1 6 0.1 EnvSin
+>                 ,envCoord c2 5 0.1 EnvCub
+>                 ,envCoord c3 7 0.1 EnvStep]
diff --git a/Help/UGen/Envelope/envGate.help.lhs b/Help/UGen/Envelope/envGate.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/Envelope/envGate.help.lhs
@@ -0,0 +1,30 @@
+> Sound.SC3.UGen.Help.viewSC3Help "EnvGate"
+
+> import Sound.SC3
+
+Make envGate, giving the /default/ arguments, as used by envGate'.
+
+> let {k = control KR
+>     ;e = envGate 1 (k "gate" 1) (k "fadeTime" 0.02) RemoveSynth EnvSin}
+> in audition (out 0 (lpf (saw AR 200) 600 * 0.1 * e))
+
+Set fade time, then release gate.
+
+> withSC3 (send (n_set1 (-1) "fadeTime" 2))
+> withSC3 (send (n_set1 (-1) "gate" 0))
+
+The same, but built in defaults.
+
+> let e = envGate'
+> in audition (out 0 (lpf (saw AR 200) 600 * 0.1 * e))
+
+Several envGate nodes can coexist in one synth, but if they are the
+same they're shared (as ever).
+
+> let {e = envGate'
+>     ;s1 = lpf (saw AR 80) 600 * e
+>     ;s2 = rlpf (saw AR 200 * 0.5) (6000 * e + 60) 0.1 * e}
+> in audition (out 0 (mce2 s1 s2 * 0.1))
+
+> withSC3 (send (n_set1 (-1) "fadeTime" 5))
+> withSC3 (send (n_set1 (-1) "gate" 0))
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
@@ -1,30 +1,31 @@
 > Sound.SC3.UGen.Help.viewSC3Help "EnvGen"
 > Sound.SC3.UGen.DB.ugenSummary "EnvGen"
 
-# SC3
-SC3 reorders inputs so that the envelope is the first argument.
-
-The following envelope constructors are provided: envPerc, envSine,
-envCoord, envTrapezoid, and envLinen.
+At least the following envelope constructors are provided:
+envPerc, envSine, envCoord, envTrapezoid, and envLinen.
 
-> import Sound.SC3.ID
+> import Sound.SC3 {- hsc3 -}
 
 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 'α' KR 2)
 >     ;e = Envelope [0,1] [n] [EnvLin] Nothing (Just 0)
 >     ;a = envGen AR 1 1 0 1 DoNothing (env_circle e 0 EnvLin)
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
@@ -1,4 +1,5 @@
 > Sound.SC3.UGen.Help.viewSC3Help "Env.*linen"
+> :i Sound.SC3.LINEN
 > :t envLinen
 
 > import Sound.SC3
@@ -8,4 +9,18 @@
 > 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]
+
+> plotEnvelope [envLinen 0 1 0 0.4
+>              ,envelope_normalise (envLinen 0 2 0 0.5)
+>              ,envLinen 0.4 2 0.4 0.6
+>              ,envLinen 0.6 1 1.2 0.7]
+
+> let e = envLinen 0 1 0 1
+> in (envelope_duration e
+>    ,envelope_segment_ix e 0
+>    ,envelope_segment_ix e 1
+>    ,envelope_segment e 0
+>    ,envelope_segment e 1
+>    ,envelope_at e 0
+>    ,envelope_at e 1
+>    ,envelope_render 10 e)
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
@@ -1,7 +1,7 @@
 > Sound.SC3.UGen.Help.viewSC3Help "Env.*perc"
 > :t envPerc'
 
-> import Sound.SC3
+> import Sound.SC3 {- hsc3 -}
 
 > let {a = 0.1
 >     ;p = envPerc 0.01 1
@@ -14,5 +14,6 @@
 >     ;e = envGen KR 1 1 0 1 RemoveSynth p }
 > in audition (out 0 (sinOsc AR 440 0 * e))
 
-> import Sound.SC3.Plot
+> import Sound.SC3.Plot {- hsc3-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,11 +1,12 @@
 > Sound.SC3.UGen.Help.viewSC3Help "Env.*sine"
 > :t Sound.SC3.envSine
 
-> import Sound.SC3
+> import Sound.SC3 {- hsc3 -}
 
 > let {s = envSine 9 0.1
 >     ;e = envGen KR 1 1 0 1 RemoveSynth s}
 > in audition (out 0 (sinOsc AR 440 0 * e))
 
-> import Sound.SC3.Plot
+> import Sound.SC3.Plot {- hsc3-plot -}
+
 > plotEnvelope [envSine 9 1,envSine 3 0.25]
diff --git a/Help/UGen/Envelope/envStep.help.lhs b/Help/UGen/Envelope/envStep.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/Envelope/envStep.help.lhs
@@ -0,0 +1,22 @@
+> Sound.SC3.UGen.Help.viewSC3Help "Env.*step"
+> :i Sound.SC3.envStep
+
+> import Sound.SC3 {- hsc3 -}
+
+> let {env = envStep [0.0,0.5,0.7,1.0,0.9,0.0] [0.5,0.1,0.2,1.0,1.5,3] Nothing Nothing
+>     ;envgen = envGen AR 1 1 0 1 RemoveSynth env
+>     ;o = sinOsc AR (envgen * 1000 + 440) 0 * (envgen + 1) * 0.1}
+> in audition (out 0 o)
+
+major scale, accelerating
+
+> let {env = envStep [0,2,4,5,7,9,11,12] (take 8 (iterate (* 0.75) 1)) Nothing Nothing
+>     ;envgen = envGen AR 1 1 0 1 DoNothing env
+>     ;o = sinOsc AR (midiCPS (envgen + 60)) 0 * 0.1}
+> in audition (out 0 o)
+
+draw envelope
+
+> import Sound.SC3.Plot
+
+> plotEnvelope [envStep [0,2,4,5,7,9,11,12] (take 8 (iterate (* 0.75) 1)) Nothing Nothing]
diff --git a/Help/UGen/Envelope/iEnvGen.help.lhs b/Help/UGen/Envelope/iEnvGen.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/Envelope/iEnvGen.help.lhs
@@ -0,0 +1,25 @@
+> Sound.SC3.UGen.Help.viewSC3Help "IEnvGen"
+> Sound.SC3.UGen.DB.ugenSummary "IEnvGen"
+
+> import Sound.SC3.ID
+
+> let {l = [0,0.6,0.3,1.0,0]
+>     ;t = [0.1,0.02,0.4,1.1]
+>     ;c = [EnvLin,EnvExp,EnvNum (-6),EnvSin]
+>     ;e = Envelope l t c Nothing Nothing
+>     ;x = mouseX KR 0 (sum t) Linear 0.2
+>     ;g = iEnvGen KR x e
+>     ;o = sinOsc AR (g * 500 + 440) 0 * 0.1}
+> in audition (out 0 o)
+
+index with an SinOsc ... mouse controls amplitude of SinOsc
+use offset so negative values of SinOsc will map into the Env
+
+> let {l = [-1,-0.7,0.7,1]
+>     ;t = [0.8666,0.2666,0.8668]
+>     ;c = [EnvLin,EnvLin]
+>     ;e = Envelope l t c Nothing Nothing
+>     ;x = mouseX KR 0 1 Linear 0.2
+>     ;o = (sinOsc AR 440 0 + 1) * x
+>     ;g = iEnvGen AR o e * 0.1}
+> in audition (out 0 g)
diff --git a/Help/UGen/Envelope/line.help.lhs b/Help/UGen/Envelope/line.help.lhs
--- a/Help/UGen/Envelope/line.help.lhs
+++ b/Help/UGen/Envelope/line.help.lhs
@@ -1,10 +1,16 @@
 > Sound.SC3.UGen.Help.viewSC3Help "Line"
 > Sound.SC3.UGen.DB.ugenSummary "Line"
 
-#SC3
-SC3 reorders the mul and add inputs to precede the doneAction input.
+#SC3 reorders the mul and add inputs to precede the doneAction input.
 
 > import Sound.SC3
 
 > let f = line KR 200 17000 5 RemoveSynth
 > in audition (out 0 (sinOsc AR f 0 * 0.1))
+
+Demonstrate RemoveGroup done-action.
+
+> withSC3 (send (g_new [(10,AddToTail,1)]))
+
+> let f = line KR 200 (mce2 209 211) 5 RemoveGroup
+> in audition_at (-1,AddToTail,10) (out 0 (sinOsc AR f 0 * 0.1))
diff --git a/Help/UGen/Envelope/xLine.help.lhs b/Help/UGen/Envelope/xLine.help.lhs
--- a/Help/UGen/Envelope/xLine.help.lhs
+++ b/Help/UGen/Envelope/xLine.help.lhs
@@ -1,8 +1,7 @@
 > Sound.SC3.UGen.Help.viewSC3Help "XLine"
 > Sound.SC3.UGen.DB.ugenSummary "XLine"
 
-# SC3
-At SC3 mul and add inputs precede the doneAction input.
+# SC3 reorders mul and add inputs to precede the doneAction input.
 
 > import Sound.SC3
 
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
@@ -1,7 +1,7 @@
 > Sound.SC3.UGen.Help.viewSC3Help "Atari2600"
 > Sound.SC3.UGen.DB.ugenSummary "Atari2600"
 
-> import Sound.SC3
+> import Sound.SC3 {- hsc3 -}
 
 > audition (out 0 (atari2600 1 2 3 4 5 5 1))
 > audition (out 0 (atari2600 2 3 10 10 5 5 1))
diff --git a/Help/UGen/External/coyote.help.lhs b/Help/UGen/External/coyote.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/External/coyote.help.lhs
@@ -0,0 +1,9 @@
+> Sound.SC3.UGen.Help.viewSC3Help "Coyote"
+> Sound.SC3.UGen.DB.ugenSummary "Coyote"
+
+> import Sound.SC3.ID
+
+> let {i = soundIn 4
+>     ;c = coyote KR i 0.2 0.2 0.01 0.5 0.05 0.05
+>     ;o = pinkNoise 'a' AR * decay c 1}
+> in audition (out 0 (mce2 i o))
diff --git a/Help/UGen/External/dPW3Tri.help.lhs b/Help/UGen/External/dPW3Tri.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/External/dPW3Tri.help.lhs
@@ -0,0 +1,42 @@
+> Sound.SC3.UGen.Help.viewSC3Help "DPW3Tri"
+> Sound.SC3.UGen.DB.ugenSummary "DPW3Tri"
+
+> import Sound.SC3.ID
+
+distortion creeps in under 200Hz
+> let o = dPW3Tri AR (xLine KR 2000 20 10 DoNothing)
+> in audition (out 0 (o * 0.1))
+
+very fast sweeps can have transient distortion effects
+> let o = dPW3Tri AR (mouseX KR 200 12000 Exponential 0.2)
+> in audition (out 0 (o * 0.1))
+
+compare
+> let o = lfTri AR (mouseX KR 200 12000 Exponential 0.2) 0
+> in audition (out 0 (o * 0.1))
+
+(for randN)
+> import Sound.SC3.UGen.External.RDU.ID
+
+less efficient than LFTri
+> let f = randN 50 'a' 50 5000
+> in audition (out 0 (splay (dPW3Tri AR f) 1 0.1 0 True))
+
+> let f = randN 50 'a' 50 5000
+> in audition (out 0 (splay (lfTri AR f 0) 1 0.1 0 True))
+
+triangle is integration of square wave
+> let {f = mouseX KR 440 8800 Exponential 0.2
+>     ;o = pulse AR f 0.5
+>     ;s = integrator o 0.99}
+> in audition (out 0 (s * 0.05))
+
+differentiation of triangle is square
+> let {f = mouseX KR 440 8800 Exponential 0.2
+>     ;o = dPW3Tri AR f
+>     ;s = hpz1 (o * 2)}
+> in audition (out 0 (s * 0.25))
+
+compare
+> let f = mouseX KR 440 8800 Exponential 0.2
+> in audition (out 0 (pulse AR f 0.5 * 0.1))
diff --git a/Help/UGen/External/dWGPlucked2.help.lhs b/Help/UGen/External/dWGPlucked2.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/External/dWGPlucked2.help.lhs
@@ -0,0 +1,37 @@
+> Sound.SC3.UGen.Help.viewSC3Help "DWGPlucked2"
+> Sound.SC3.UGen.DB.ugenSummary "DWGPlucked2"
+
+> import Sound.SC3.ID
+
+self deleting
+> let {amp = 0.5
+>     ;gate = 1
+>     ;freq = 440
+>     ;c3 = 20
+>     ;pan = 0
+>     ;e = Envelope
+>      [0,1,1,0] [0.001,0.006,0.0005]
+>      (map EnvNum [5,-5,-8]) Nothing Nothing
+>     ;i = amp * lfClipNoise 'α' AR 2000 * envGen AR gate 1 0 1 DoNothing e
+>     ;s = dWGPlucked2 AR freq amp gate 0.1 1 c3 i 0.1 1.008 0.55 0.01
+>     ;z = detectSilence s 0.001 0.1 RemoveSynth}
+> in audition (out 0 (mrg2 (pan2 s pan 0.1) z))
+
+re-sounding
+> let {sequ e s tr = demand tr 0 (dseq e dinf (mce s))
+>     ;d = dseq 'α' dinf (mce [1,1,2,1,1,1,2,3,1,1,1,1,2,3,4] * 0.175)
+>     ;t = tDuty AR d 0 DoNothing 1 0
+>     ;amp = tRand 'β' 0.01 0.25 t
+>     ;n0 = sequ 'γ' [60,62,63,58,48,55] t
+>     ;n1 = sequ 'δ' [63,60,48,62,55,58] t
+>     ;freq = midiCPS (mce2 n0 n1)
+>     ;c3 = tRand 'ε' 300 1400 t
+>     ;pan = tRand 'ζ' (-1) 1 t
+>     ;e_dt = tRand 'η' 0.05 0.150 t
+>     ;mt = tRand 'θ' 0.992 1.008 t
+>     ;pp = tRand 'ι' 0.05 0.15 t
+>     ;dt = tRand 'κ' 0.25 1.75 t
+>     ;env = decay2 t 0.001 e_dt * lfClipNoise 'λ' AR 2000
+>     ;i = amp * lfClipNoise 'μ' AR 2000 * env
+>     ;ps = dWGPlucked2 AR freq amp 1 pp (1 / dt) c3 i 0.1 mt 0.55 0.01}
+> in audition (out 0 (pan2 ps pan 0.1))
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
@@ -4,12 +4,14 @@
 > import Sound.SC3.ID
 
 Play it with the mouse
+
 > let { n = pinkNoise 'α' AR * 0.5
 >     ; 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
+Bass...
+
 > let { i = pulse AR 100 0.5 * 0.4 + pulse AR 100.1 0.5 * 0.4
 >     ; f = range 80 2000 (sinOsc KR (range 0.2 5 (sinOsc KR 0.3 0)) 0)
 >     ; s = dfm1 i f 1.1 2 0 3e-4 * 0.1 }
diff --git a/Help/UGen/External/envDetect.help.lhs b/Help/UGen/External/envDetect.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/External/envDetect.help.lhs
@@ -0,0 +1,12 @@
+> Sound.SC3.UGen.Help.viewSC3Help "EnvDetect"
+> Sound.SC3.UGen.DB.ugenSummary "EnvDetect"
+
+> import Sound.SC3.ID
+
+> let {i = soundIn 4
+>     ;c = envDetect AR i 0.01 0.1
+>     ;p = pitch i 440 60 4000 100 16 1 0.01 0.5 1 0
+>     ;f = mceChannel 0 p * 3
+>     ;e = lagUD (mceChannel 1 p) 0 0.1
+>     ;o = pinkNoise 'α' AR * c + sinOsc AR f 0 * c * e}
+> in audition (out 0 (mce2 i o))
diff --git a/Help/UGen/External/envFollow.help.lhs b/Help/UGen/External/envFollow.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/External/envFollow.help.lhs
@@ -0,0 +1,10 @@
+> Sound.SC3.UGen.Help.viewSC3Help "EnvFollow"
+> Sound.SC3.UGen.DB.ugenSummary "EnvFollow"
+
+> import Sound.SC3.ID
+
+> let {z = soundIn 4
+>     ;d = mouseX KR 0.990 0.999 Linear 0.2
+>     ;c = envFollow KR z d
+>     ;o = pinkNoise 'α' AR * c}
+> in audition (out 0 (mce2 z o))
diff --git a/Help/UGen/External/lti.help.lhs b/Help/UGen/External/lti.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/External/lti.help.lhs
@@ -0,0 +1,10 @@
+> Sound.SC3.UGen.Help.viewSC3Help "LTI"
+> Sound.SC3.UGen.DB.ugenSummary "LTI"
+
+> import Sound.SC3.ID
+
+> let {a = [0.02,-0.01]
+>     ;b = [1,0.7,0,0,0,0,-0.8,0,0,0,0,0.9,0,0,0,-0.5,0,0,0,0,0,0,0.25,0.1,0.25]
+>     ;z = pinkNoise 'a' AR * 0.1 {- soundIn 4 -}
+>     ;f = lti AR z (asLocalBuf 'a' a) (asLocalBuf 'b' b)}
+> in audition (out 0 f)
diff --git a/Help/UGen/External/playBufCF.help.lhs b/Help/UGen/External/playBufCF.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/External/playBufCF.help.lhs
@@ -0,0 +1,33 @@
+wslib: external/composite
+
+> import Sound.SC3.ID
+
+Load sound file to buffer zero (single channel file required for examples)
+
+> let {fn' = "/home/rohan/data/audio/pf-c5.aif"
+>     ;fn = "/home/rohan/opt/share/SuperCollider/sounds/a11wlk01.wav"}
+> in withSC3 (async (b_allocRead 0 fn 0 0))
+
+control-rate trigger and start-position inputs
+
+> let {b = 0
+>     ;r = bufRateScale KR b
+>     ;tr = impulse KR 2 0
+>     ;wn = whiteNoise 'α' KR
+>     ;sp = linLin wn (-1) 1 0 (bufFrames KR b - (0.5 * 44100))
+>     ;o = playBufCF 1 b r tr sp NoLoop 0.1 2
+>     ;o' = playBuf 1 AR b r tr sp NoLoop DoNothing}
+> in audition (out 0 (mce2 o o'))
+
+demand ugens inputs
+
+> let {b = 0
+>     ;r = drand 'α' dinf (mce [0.95,1,1.05])
+>     ;tr = dwhite 'β' dinf 0.1 0.3
+>     ;sp = dbrown 'γ' dinf 0 0.95 0.1 * bufFrames KR b
+>     ;o = playBufCF 1 b r tr sp NoLoop 2 5}
+> in audition (out 0 o)
+
+for drawings...
+
+> import Sound.SC3.UGen.Dot {- hsc3-dot -}
diff --git a/Help/UGen/External/pv_BufRd.help.lhs b/Help/UGen/External/pv_BufRd.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/External/pv_BufRd.help.lhs
@@ -0,0 +1,32 @@
+> import Sound.SC3
+
+allocate anazlysis buffer and load soundfile
+
+> let {p = "/home/rohan/opt/share/SuperCollider/sounds/a11wlk01.wav"
+>     ;f = 1024 {- frame size -}
+>     ;h = 0.25 {- hop size -}
+>     ;p_dur = 4.2832879818594 {- duration (in seconds) of p -}
+>     ;b_size = pv_calcPVRecSize p_dur f h 48000}
+> in withSC3 (do {_ <- async (b_alloc 0 b_size 1)
+>                ;async (b_allocRead 1 p 0 0)})
+
+do the analysis and store to buffer.  the window type and overlaps are
+important for resynthesis parameters
+
+> let {rec_buf = 0
+>     ;au_buf = 1
+>     ;l_buf = localBuf 'α' 1024 1;
+>     ;rt = bufRateScale KR au_buf
+>     ;i = playBuf 1 AR au_buf rt 1 0 NoLoop RemoveSynth
+>     ;c0 = fft l_buf i 0.25 1 1 0
+>     ;c1 = pv_RecordBuf c0 rec_buf 0 1 0 0.25 1}
+> in audition (mrg2 (out 0 (dc AR 0)) c1)
+
+play analysis back
+
+> let {rec_buf = 0
+>     ;l_buf = localBuf 'α' 1024 1
+>     ;x = mouseX KR 0 1 Linear 0.2
+>     ;c0 = pv_BufRd l_buf rec_buf x
+>     ;s = ifft c0 1 0}
+> in audition (out 0 s)
diff --git a/Help/UGen/External/qitch.help.lhs b/Help/UGen/External/qitch.help.lhs
--- a/Help/UGen/External/qitch.help.lhs
+++ b/Help/UGen/External/qitch.help.lhs
@@ -14,7 +14,8 @@
 > let {x = mouseX KR 440 880 Exponential 0.1
 >     ;o = sinOsc AR x 0 * 0.1
 >     ;[f,e] = mceChannels (qitch KR o 10 1e-2 1 0 0 2500)
+>     ;r = sinOsc AR f 0 * 0.1
 >     ;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])
+> in audition (mrg [out 0 (mce2 o r),pf,px])
diff --git a/Help/UGen/External/sms.help.lhs b/Help/UGen/External/sms.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/External/sms.help.lhs
@@ -0,0 +1,11 @@
+> Sound.SC3.UGen.Help.viewSC3Help "SMS"
+> Sound.SC3.UGen.DB.ugenSummary "SMS"
+
+> import Sound.SC3
+
+sine reconstruction left channel, noises on right
+> let {z= soundIn 4
+>     ;y = mouseY KR 1 50 Linear 0.2
+>     ;x = mouseX KR 0.5 4 Linear 0.2
+>     ;o = sms z 50 y 8 0.3 x 0 0 0 1 (-1)}
+> in audition (out 0 o)
diff --git a/Help/UGen/External/squiz.help.lhs b/Help/UGen/External/squiz.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/External/squiz.help.lhs
@@ -0,0 +1,27 @@
+> Sound.SC3.UGen.Help.viewSC3Help "Squiz"
+> Sound.SC3.UGen.DB.ugenSummary "Squiz"
+
+> import Sound.SC3
+
+Squiz of sin oscillator
+
+> let {o = sinOsc AR 440 0
+>     ;x = mouseX KR 1 10 Exponential 0.2
+>     ;y = mouseY KR 1 10 Linear 0.2
+>     ;s = squiz o x y 0.1 * 0.1}
+> in audition (out 0 s)
+
+Load sound file to buffer zero
+
+> let {fn' = "/home/rohan/data/audio/pf-c5.aif"
+>     ;fn = "/home/rohan/opt/share/SuperCollider/sounds/a11wlk01.wav"}
+> in withSC3 (async (b_allocRead 0 fn 0 0))
+
+Squiz of audio file.
+
+> let {r = bufRateScale KR 0
+>     ;p = playBuf 1 AR 0 (r * 0.5) 1 0 Loop DoNothing
+>     ;x = mouseX KR 1 100 Exponential 0.2
+>     ;y = mouseY KR 1 10 Linear 0.2
+>     ;o = squiz p x y 0.1 * 0.5}
+> in audition (out 0 o)
diff --git a/Help/UGen/External/switchDelay.help.lhs b/Help/UGen/External/switchDelay.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/External/switchDelay.help.lhs
@@ -0,0 +1,13 @@
+> Sound.SC3.UGen.Help.viewSC3Help "SwitchDelay"
+> Sound.SC3.UGen.DB.ugenSummary "SwitchDelay"
+
+> import Sound.SC3
+
+simple feedback delay
+> audition (out 0 (switchDelay (soundIn 4) 1 1 1 0.99 20))
+
+change the buffer read pointer periodically.
+> let {ix = stepper (impulse KR 0.5 0) 0 0 3 1 0
+>     ;dt = select ix (mce [0.02,0.1,0.725,0.25])
+>     ;sd = switchDelay (soundIn 4) 1 1 dt 0.99 20}
+> in audition (out 0 sd)
diff --git a/Help/UGen/External/tBetaRand.help.lhs b/Help/UGen/External/tBetaRand.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/External/tBetaRand.help.lhs
@@ -0,0 +1,17 @@
+> Sound.SC3.UGen.Help.viewSC3Help "TBetaRand"
+> Sound.SC3.UGen.DB.ugenSummary "TBetaRand"
+
+> import Sound.SC3.ID
+
+> let {t = dust 'α' KR 10
+>     ;f = tBetaRand 'β' 300 3000 0.1 0.1 t
+>     ;o = sinOsc AR f 0 * 0.1}
+> in audition (out 0 o)
+
+mouse control of parameters
+> let {t = dust 'α' KR 10
+>     ;p1 = mouseX KR 1 5 Linear 0.2
+>     ;p2 = mouseY KR 1 5 Linear 0.2
+>     ;f = tBetaRand 'β' 300 3000 p1 p2 t
+>     ;o = sinOsc AR f 0 * 0.1}
+> in audition (out 0 o)
diff --git a/Help/UGen/External/tBrownRand.help.lhs b/Help/UGen/External/tBrownRand.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/External/tBrownRand.help.lhs
@@ -0,0 +1,18 @@
+> Sound.SC3.UGen.Help.viewSC3Help "TBrownRand"
+> Sound.SC3.UGen.DB.ugenSummary "TBrownRand"
+
+> import Sound.SC3.ID
+
+> let {t = dust 'α' KR 10
+>     ;dist = mouseX KR 0 5 Linear 0.2
+>     ;f = tBrownRand 'β' 300 3000 1 dist t
+>     ;o = sinOsc AR f 0 * 0.1}
+> in audition (out 0 o)
+
+> let {t = dust 'α' KR 10
+>     ;n = tBrownRand 'β' 0 1 0.2 0 t
+>     ;f = linExp n 0 1 300 3000
+>     ;o = sinOsc AR f 0
+>     ;l = tBrownRand 'γ' (-1) 1 1 4 t
+>     ;p = pan2 o l 0.1}
+> in audition (out 0 p)
diff --git a/Help/UGen/External/tGaussRand.help.lhs b/Help/UGen/External/tGaussRand.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/External/tGaussRand.help.lhs
@@ -0,0 +1,19 @@
+> Sound.SC3.UGen.Help.viewSC3Help "TGaussRand"
+> Sound.SC3.UGen.DB.ugenSummary "TGaussRand"
+
+> import Sound.SC3.ID
+
+> let {t = dust 'α' KR 10
+>     ;f = tGaussRand 'β' 300 3000 t
+>     ;o = sinOsc AR f 0
+>     ;l = tGaussRand 'γ' (-1) 1 t
+>     ;p = pan2 o l 0.1}
+> in audition (out 0 p)
+
+compare to tRand
+> let {t = dust 'α' KR 10
+>     ;f = tRand 'β' 300 3000 t
+>     ;o = sinOsc AR f 0
+>     ;l = tRand 'γ' (-1) 1 t
+>     ;p = pan2 o l 0.1}
+> in audition (out 0 p)
diff --git a/Help/UGen/External/tartini.help.lhs b/Help/UGen/External/tartini.help.lhs
--- a/Help/UGen/External/tartini.help.lhs
+++ b/Help/UGen/External/tartini.help.lhs
@@ -7,11 +7,13 @@
 > let {x = mouseX KR 440 880 Exponential 0.1
 >     ;o = lfSaw AR x 0 * 0.05 {- sinOsc AR x 0 * 0.1 -}
 >     ;[f,e] = mceChannels (tartini KR o 0.2 2048 0 1024 0.5)
+>     ;r = sinOsc AR f 0 * 0.1
 >     ;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])
+> in audition (mrg [out 0 (mce2 o r),pf,px])
 
 Fast test of live pitch tracking, not careful with amplitude of input
-> let [f,e] = mceChannels (tartini KR (soundIn 0) 0.2 2048 0 1024 0.5)
-> in audition (out 0 (saw AR f * 0.05))
+> let {z = soundIn 4
+>     ;[f,e] = mceChannels (tartini KR z 0.2 2048 0 1024 0.5)}
+> in audition (out 0 (saw AR f * 0.05 * e))
diff --git a/Help/UGen/External/tpv.help.lhs b/Help/UGen/External/tpv.help.lhs
--- a/Help/UGen/External/tpv.help.lhs
+++ b/Help/UGen/External/tpv.help.lhs
@@ -7,21 +7,19 @@
 > 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)
+> let tpv' b i = tpv (fft b i 0.5 1 1 0) (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))
+>     ;o = tpv' 0 i 70 x y 4 0.2}
+> in audition (out 0 (mce2 (i * 0.1) o))
 
 > 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)}
+>     ;o = tpv' 0 i 50 50 1 x (dbAmp y)}
 > in audition (out 0 (pan2 o 0 1))
diff --git a/Help/UGen/External/waveTerrain.help.lhs b/Help/UGen/External/waveTerrain.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/External/waveTerrain.help.lhs
@@ -0,0 +1,74 @@
+> Sound.SC3.UGen.Help.viewSC3Help "WaveTerrain"
+> Sound.SC3.UGen.DB.ugenSummary "WaveTerrain"
+
+> import Sound.SC3.ID {- hsc3 -}
+> import Sound.SC3.Plot {- hsc3-plot -}
+
+Terrain function
+
+> let z (x,y) = let {a = x ** 2
+>                   ;b = abs (sin (10 * y)) ** (1/3)}
+> in 2 * (a + b) - 1
+
+Create terrain given function.
+
+> let t z = let {w = 100 {- width -}
+>               ;h = 50 {- height -}
+>               ;tk n = take (round n)
+>               ;xs = tk w [0,1/w ..]
+>               ;ys = tk h [0,1/h ..]
+>               ;ix = map (\y -> map (\x -> (x,y)) xs) ys
+>               ;add_z = map (\(x,y) -> (x,y,z (x,y)))}
+>           in concatMap add_z ix
+
+Confirm terrain
+
+> plot_p3_pt [t z]
+
+Create table.
+
+> let t' = map (\(_,_,z) -> z) (t z)
+
+Confirm table
+
+> plotTable [t']
+
+Send table to scsynth
+
+> withSC3 (async (b_alloc_setn1 0 0 t'))
+
+Hear terrain
+
+> let {x' = mouseX KR 1 200 Exponential 0.2
+>     ;y' = mouseY KR 1 300 Exponential 0.2
+>     ;x = abs (sinOsc AR x' 0) + lfNoise2 'α' AR 2
+>     ;y = abs (sinOsc AR y' (pi / 2))
+>     ;b = 0
+>     ;o = waveTerrain AR b x y 100 50}
+> in audition (out 0 o)
+
+Alternate terrain function.
+
+> let z (x,y) = let {a = (cos(5 * x + 1.7)) ** 3
+>                   ;b = abs (sin (23 * y)) ** (1/3)}
+>               in (a - b)
+
+Free buffer
+
+> withSC3 (async (b_free 0))
+
+Gerate mesh given terrain given function.
+
+> let t z = let {w = 100 {- width -}
+>               ;h = 50 {- height -}
+>               ;tk n = take (round n)
+>               ;xs = tk w [0,1/w ..]
+>               ;ys = tk h [0,1/h ..]
+>               ;ix0 = map (\y -> map (\x -> (x,y)) xs) ys
+>               ;ix1 = map (\x -> map (\y -> (x,y)) ys) xs
+>               ;add_z = map (\(x,y) -> (x,y,z (x,y)))}
+>           in (map add_z ix0,map add_z ix1)
+
+Confirm terrain mesh
+
+> plot_p3_ln ((\(p,q) -> p ++ q) (t z))
diff --git a/Help/UGen/External/zitaRev1.help.lhs b/Help/UGen/External/zitaRev1.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/External/zitaRev1.help.lhs
@@ -0,0 +1,34 @@
+faust2supercollider zita_rev1.dsp
+http://kokkinizita.linuxaudio.org/linuxaudio/zita-rev1-doc/quickguide.html
+
+delay, lin,   0.02,    0.1,     0.04
+xover, log,  50.0,  1000.0,   200.0
+rtlow, log,   1.0,     8.0,     3.0
+rtmid, log,   1.0,     8.0,     2.0
+fdamp, log,   1.5e3,  24.0e3,   6.0e3
+eq1fr, log,  40.0,     2.5e3, 160.0
+eq1gn, lin, -15.0,    15.0,     0.0
+eq2fr, log, 160.0,    10.0e3,   2.5e3
+eq2gn, lin, -15.0,    15.0,     0.0
+opmix, lin,   0.0,     1.0,     0.5
+level, lin,  -9.0,     9.0,   -20.0
+
+> import Sound.SC3
+
+default settings
+> let {i = soundIn 4
+>     ;o = zitaRev1 i i 0.04 200 3 2 6000 160 0 2500 0 0.5 (-6)}
+> in audition (out 0 o)
+
+longer
+> let {i = soundIn 4
+>     ;o = zitaRev1 i i 0.08 200 6 4 6000 190 (-6) 3500 6 0.5 0}
+> in audition (out 0 o)
+
+longer still
+> let {i = soundIn 4
+>     ;o = zitaRev1 i i 0.1 200 6 8 6000 190 (-6) 3500 6 0.5 0}
+> in audition (out 0 o)
+
+hsc3-db
+> Sound.SC3.UGen.DB.u_summary zitaRev1_dsc
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,12 +4,18 @@
 
 > import Sound.SC3.ID
 
+Non-local buffer
+
 > withSC3 (async (b_alloc 10 2048 1))
 
-> let n = whiteNoise 'a' AR
+Variants with default values
+
+> let n = whiteNoise 'α' AR
 > in audition (out 0 (ifft' (fft' 10 (n * 0.05))))
 
-> let { s0 = sinOsc KR 0.08 0 * 6 + 6.2
->     ; s1 = sinOsc KR (squared s0) 0 * 100 + 800
->     ; s2 = sinOsc AR s1 0 }
-> in audition (out 0 (ifft' (fft' 10 s2) * 0.25))
+Local buffer allocating fft variant
+
+> let {s0 = sinOsc KR 0.08 0 * 6 + 6.2
+>     ;s1 = sinOsc KR (squared s0) 0 * 100 + 800
+>     ;s2 = sinOsc AR s1 0}
+> in audition (out 0 (ifft (ffta 'α' 2048 s2 0.5 0 1 0) 0 0 * 0.25))
diff --git a/Help/UGen/FFT/ifft.help.lhs b/Help/UGen/FFT/ifft.help.lhs
--- a/Help/UGen/FFT/ifft.help.lhs
+++ b/Help/UGen/FFT/ifft.help.lhs
@@ -2,5 +2,4 @@
 > Sound.SC3.UGen.DB.ugenSummary "IFFT"
 > :t ifft'
 
-# hsc3
-ifft' is a variant with the default window type and size
+# ifft' is a variant with the default window type and size
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
@@ -2,18 +2,19 @@
 > Sound.SC3.UGen.DB.ugenSummary "PackFFT"
 
 > import Sound.SC3.ID
+> import Sound.SC3.UGen.Protect
 
 > withSC3 (async (b_alloc 10 512 1))
 
 > let {n = 100
 >     ;square a = a * a
->     ;r1 = let f = expRand 'a' 0.1 1
+>     ;r1 = let f = expRand 'α' 0.1 1
 >           in linLin (fSinOsc KR f 0) (-1) 1 0 1
->     ;m1 = uclone' 'a' n r1
+>     ;m1 = uclone' 'β' n r1
 >     ;m2 = zipWith (*) m1 (map square [1.0, 0.99 ..])
->     ;r2 = let r = iRand 'a' (-3) 5
+>     ;r2 = let r = iRand 'γ' (-3) 5
 >           in lfPulse KR (2 ** r) 0 0.3
->     ;i = uclone' 'a' n r2
+>     ;i = uclone' 'δ' n r2
 >     ;m3 = zipWith (*) m2 i
 >     ;p = replicate n 0.0
 >     ;c1 = fft' 10 (fSinOsc AR 440 0)
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
@@ -4,8 +4,8 @@
 > import Sound.SC3.ID
 
 > let { fft_size = 2048
->     ; ir_file = "/home/rohan/data/audio/church.ir.wav"
->     ; ir_length = 82756
+>     ; ir_file = "/home/rohan/data/audio/reverbs/chapel.wav"
+>     ; ir_length = 62494 {- frame count of ir_file -}
 >     ; accum_size = pc_calcAccumSize fft_size ir_length
 >     ; ir_td_b = 10 {- time domain -}
 >     ; ir_fd_b = 11 {- frequency domain -}
diff --git a/Help/UGen/FFT/pv_BinDelay.help.lhs b/Help/UGen/FFT/pv_BinDelay.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/FFT/pv_BinDelay.help.lhs
@@ -0,0 +1,57 @@
+> Sound.SC3.UGen.Help.viewSC3Help "PV_BinDelay"
+> Sound.SC3.UGen.DB.ugenSummary "PV_BinDelay"
+
+> import Sound.SC3
+
+function to allocate buffers (fft,delay,feedback)
+
+> let mk_b sz = withSC3 (do {_ <- async (b_alloc 10 (sz * 2) 1)
+>                           ;_ <- async (b_alloc 11 sz 1)
+>                           ;async (b_alloc 12 sz 1)})
+
+allocate buffers (number of bins)
+
+> mk_b 128
+
+function to generate bindelay filter
+
+> let mk_u z = let {maxdel = 0.5
+>                  ;c1 = fft 10 z 0.25 0 1 0
+>                  ;c2 = pv_BinDelay c1 maxdel 11 12 0.25}
+>              in z + ifft c2 0 0
+
+start filter
+
+> audition (out 0 (mk_u (soundIn 4)))
+
+set delay times (unary)
+
+> withSC3 (send (b_fill 11 [(0,128,0.25)]))
+
+set feedback gain
+
+> withSC3 (send (b_fill 12 [(0,128,0.75)]))
+
+function to generate sin table of n places in range (l,r)
+
+> let gen_sin l r n ph =
+>     let f x = range l r (sin ((x / n) * 2 * pi + ph))
+>     in map f [0..n]
+
+set delay times (sin)
+
+> withSC3 (send (b_setn1 11 0 (gen_sin 0 0.35 128 0)))
+
+set feedback gain (sin)
+
+> withSC3 (send (b_setn1 12 0 (gen_sin 0.75 0.95 128 pi)))
+
+modulate delay times (lfo)
+
+> let o = range 0.15 0.35 (blip KR (1/23) 3)
+> in audition (recordBuf KR 11 0 1 0 1 Loop 1 DoNothing o)
+
+modulate feedback gains (lfo)
+
+> let o = range 0.75 0.95 (blip KR (1/25) 5)
+> in audition (recordBuf KR 12 0 1 0 1 Loop 1 DoNothing o)
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
@@ -11,15 +11,15 @@
 >     ;f = fft' 10 a
 >     ;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)}
+>     ;g = pv_BinScramble 'α' 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
+>     ;x = mouseX KR 0.15 1 Linear 0.1
+>     ;y = mouseY KR 0.15 1 Linear 0.1
+>     ;i = impulse KR (lfNoise0 'α' KR 2 * 8 + 10) 0
+>     ;g = pv_BinScramble 'β' 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,12 +3,23 @@
 
 > import Sound.SC3.ID
 
+allocate buffer
 > withSC3 (async (b_alloc 10 2048 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
->     ; pv = pv_BinShift (fft' 10 s2) y x }
-> in audition (out 0 (pan2 (ifft' pv) 0 0.1))
+source signal (oscillators)
+> let z = let {s0 = sinOsc KR 0.08 0 * 6 + 6.2
+>             ;s1 = sinOsc KR (squared s0) 0 * 100 + 800}
+>         in sinOsc AR s1 0 * 0.2
+
+source signal (the world)
+> let z = soundIn 4
+
+default values
+> audition (out 0 (ifft' (pv_BinShift (fft' 10 z) 1 0 0)))
+
+mouse control
+> let {x = mouseX KR (-10) 100 Linear 0.1
+>     ;y = mouseY KR 1 4 Linear 0.1
+>     ;b = mouseButton KR 0 1 0.2
+>     ;pv = pv_BinShift (fft' 10 z) y x b}
+> in audition (out 0 (ifft' pv))
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
@@ -8,7 +8,7 @@
 >                ;_ <- async (b_alloc 11 2048 1)
 >                ;async (b_allocRead 12 fileName 0 0)})
 
-> let {n = whiteNoise 'a' AR
+> let {n = whiteNoise 'α' AR
 >     ;b = playBuf 1 AR 12 (bufRateScale KR 12) 0 0 Loop DoNothing
 >     ;f = fft' 10 (n * 0.2)
 >     ;g = fft' 11 b
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
@@ -5,6 +5,6 @@
 
 > withSC3 (async (b_alloc 10 2048 1))
 
-> let {n = whiteNoise 'a' AR
+> let {n = whiteNoise 'α' AR
 >     ;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
@@ -5,7 +5,7 @@
 
 > withSC3 (async (b_alloc 10 1024 1))
 
-> let { i = in' 1 AR numOutputBuses * 0.5
+> let { i = soundIn 4
 >     ; 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))
@@ -13,11 +13,14 @@
 With filtering.
 > 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
->     ; c = fft' 0 s
->     ; m = ifft' (pv_ConformalMap c x y) }
+> let z = 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}
+>         in mix (lfSaw AR f 0) * 0.3
+
+> let z = soundIn 4
+
+> let {x = mouseX KR 0.01  2.0 Linear 0.1
+>     ;y = mouseY KR 0.01 10.0 Linear 0.1
+>     ;c = fft' 0 z
+>     ;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
deleted file mode 100644
--- a/Help/UGen/FFT/pv_Copy.help.lhs
+++ /dev/null
@@ -1,13 +0,0 @@
-> Sound.SC3.UGen.Help.viewSC3Help "PV_Copy"
-> Sound.SC3.UGen.DB.ugenSummary "PV_Copy"
-
-> import Sound.SC3.ID
-
-> 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
->     ;c0 = fft' 0 i
->     ;c1 = pv_Copy c0 1}
-> in audition (out 0 (ifft' c0 - ifft' c1))
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
@@ -7,8 +7,11 @@
 > 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
->     ; h = pv_Diffuser f (x >* 0.5) }
+> let z = playBuf 1 AR 12 (bufRateScale KR 12) 0 0 Loop DoNothing
+
+> let z = soundIn 4
+
+> let {f = fft' 10 z
+>     ;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
@@ -3,10 +3,10 @@
 
 > import Sound.SC3.ID
 
-> 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
->     ; h = pv_HainsworthFoote f 1 0 x 0.04
->     ; o = sinOsc AR (mrg2 440 445) 0 * decay (h * 0.1) 0.1 }
+> let {i = soundIn 4
+>     ;b = localBuf 'α' 2048 1
+>     ;f = fft' b i
+>     ;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_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
@@ -7,16 +7,19 @@
 > 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_MagClip f x }
-> in audition (out 0 (ifft' h * 0.5))
+File input
+> let z = playBuf 1 AR 12 (bufRateScale KR 12) 0 0 Loop DoNothing
 
 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
->     ; h = pv_MagClip f x }
+> let z = let {f0 = squared (sinOsc KR 0.08 0 * 6 + 6.2)
+>             ;f1 = sinOsc KR f0 0 * 100 + 800}
+>         in sinOsc AR f1 0
+
+Outside world
+> let z = soundIn 4
+
+> let {f = fft' 10 z
+>     ;c = 128
+>     ;x = mouseX KR 0 c 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
@@ -1,22 +1,31 @@
 > Sound.SC3.UGen.Help.viewSC3Help "PV_MagFreeze"
 > Sound.SC3.UGen.DB.ugenSummary "PV_MagFreeze"
 
-> import Sound.SC3.ID
+> import Sound.SC3.ID {- hsc3 -}
 
+Load audio file.
+
 > let fileName = "/home/rohan/data/audio/pf-c5.snd"
 > 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
->     ; h = pv_MagFreeze f (x >* 0.5) }
-> in audition (out 0 (ifft' h * 0.5))
+File as signal...
 
-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 1 Linear 0.1
->     ; h = pv_MagFreeze f (x >* 0.5) }
+> let z = playBuf 1 AR 12 (bufRateScale KR 12) 0 0 Loop DoNothing
+
+Synthesised signal...
+
+> let z = let {o1 = sinOsc KR 0.08 0
+>             ;o2 = sinOsc KR (squared (o1 * 6 + 6.2)) 0 * 100 + 800}
+>         in sinOsc AR o2 0
+
+Outside world signal...
+
+> let z = soundIn 4
+
+Process (freeze) 'z'...
+
+> let {f = fft' 10 z
+>     ;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,10 +3,17 @@
 
 > import Sound.SC3.ID
 
+allocate buffer
 > withSC3 (async (b_alloc 10 2048 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}
+noise signal
+> let z = whiteNoise 'a' AR * 0.5
+
+outside world
+> let z = soundIn 4
+
+processor
+> let {t = impulse KR 0.1 0
+>     ;x = mouseX KR 0.6 0.95 Linear 0.1
+>     ;c = pv_RandComb 'a' (fft' 10 z) x t}
 > in audition (out 0 (pan2 (ifft' c) 0 1))
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
@@ -5,14 +5,18 @@
 
 > 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
->     ; c = pv_RectComb (fft' 10 (n * 0.3)) 8 x y }
+noise source
+> let z = whiteNoise 'a' AR * 0.3
+
+outside world
+> let z = soundIn 4
+
+> let {x = mouseX KR 0 0.5 Linear 0.1
+>     ;y = mouseY KR 0 0.5 Linear 0.1
+>     ;c = pv_RectComb (fft' 10 z) 8 x y}
 > in audition (out 0 (pan2 (ifft' c) 0 1))
 
-> let { n = whiteNoise 'a' AR
->     ; p = lfTri KR 0.097 0 *   0.4  + 0.5
->     ; w = lfTri KR 0.240 0 * (-0.5) + 0.5
->     ; c = pv_RectComb (fft' 10 (n * 0.3)) 8 p w }
+> let {p = lfTri KR 0.097 0 *   0.4  + 0.5
+>     ;w = lfTri KR 0.240 0 * (-0.5) + 0.5
+>     ;c = pv_RectComb (fft' 10 z) 8 p w}
 > 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
@@ -1,7 +1,8 @@
 > Sound.SC3.UGen.Help.viewSC3Help "PV_ChainUGen.pvcollect"
 > :t pvcollect
 
-> import Sound.SC3.ID
+> import Data.List {- base -}
+> import Sound.SC3 {- hsc3 -}
 
 > let fileName = "/home/rohan/data/audio/pf-c5.snd"
 > in withSC3 (do {_ <- async (b_alloc 10 1024 1)
@@ -19,22 +20,33 @@
 
 > let pv_g nf cf =
 >   let {no_op m p _ = (m,p)
->       ;combf m p i = ((fmod i 7.0 ==* 0) * m,p)
+>       ;combf m p i = ((modE 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 pv_au nf cf =
+>   let {no_op m p _ = (m,p)
+>       ;combf m p i = ((modE i 7.0 ==* 0) * m,p)
+>       ;c1 = fft' 10 (soundIn 0)
+>       ;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
+> in r `isPrefixOf` synthstat (pv_g 1024 spectral_delay)
 
 > 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
+> audition (pv_au 1024 spectral_delay)
+> audition (pv_au 1024 (bpf_sweep 1024))
+
+> import Sound.SC3.UGen.Dot {- hsc3-dot -}
+
 > draw_svg (pv_g 1024 (bpf_sweep 1024))
diff --git a/Help/UGen/Filter/allpassN.help.lhs b/Help/UGen/Filter/allpassN.help.lhs
--- a/Help/UGen/Filter/allpassN.help.lhs
+++ b/Help/UGen/Filter/allpassN.help.lhs
@@ -1,26 +1,30 @@
 > Sound.SC3.UGen.Help.viewSC3Help "AllpassN"
 > Sound.SC3.UGen.DB.ugenSummary "AllpassN"
 
-> import Sound.SC3.ID
+> import Sound.SC3
 
 Since the allpass delay has no audible effect as a resonator on steady
 state sound ...
+
 > let {dly = xLine KR 0.0001 0.01 20 RemoveSynth
 >     ;n = whiteNoise 'a' AR}
 > in audition (out 0 (allpassC (n * 0.1) 0.01 dly 0.2))
 
 ...these examples add the input to the effected sound so that you
 can hear the effect of the phase comb.
+
 > let {n = whiteNoise 'a' AR
 >     ;dly = xLine KR 0.0001 0.01 20 RemoveSynth}
 > in audition (out 0 ((n + allpassN (n * 0.1) 0.01 dly 0.2) * 0.1))
 
 Linear variant
+
 > let {n = whiteNoise 'a' AR
 >     ;dly = xLine KR 0.0001 0.01 20 RemoveSynth}
 > in audition (out 0 ((n + allpassL (n * 0.1) 0.01 dly 0.2) * 0.1))
 
 Cubic variant
+
 > let {n = whiteNoise 'a' AR
 >     ;dly = xLine KR 0.0001 0.01 20 RemoveSynth}
 > in audition (out 0 ((n + allpassC (n * 0.1) 0.01 dly 0.2) * 0.1))
@@ -28,6 +32,7 @@
 Used as an echo - doesn't really sound different than Comb, but it
 outputs the input signal immediately (inverted) and the echoes are
 lower in amplitude.
+
 > let {n = whiteNoise 'a' AR
 >     ;d = dust 'a' AR 1
 >     ;src = decay (d * 0.5) 0.2 * n}
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
@@ -3,7 +3,7 @@
 
 > import Sound.SC3
 
-> let { i = soundIn (mce2 0 1)
+> let { i = soundIn 4
 >     ; 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/bHiShelf.help.lhs b/Help/UGen/Filter/bHiShelf.help.lhs
--- a/Help/UGen/Filter/bHiShelf.help.lhs
+++ b/Help/UGen/Filter/bHiShelf.help.lhs
@@ -3,12 +3,12 @@
 
 > import Sound.SC3
 
-> let { i = soundIn (mce2 0 1)
+> let { i = soundIn 4
 >     ; 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)
+> let { i = soundIn 4
 >     ; 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/bPeakEQ.help.lhs b/Help/UGen/Filter/bPeakEQ.help.lhs
--- a/Help/UGen/Filter/bPeakEQ.help.lhs
+++ b/Help/UGen/Filter/bPeakEQ.help.lhs
@@ -3,12 +3,12 @@
 
 > import Sound.SC3.ID
 
-> let { i = soundIn (mce2 0 1)
+> let { i = soundIn 4
 >     ; 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)
+> let { i = soundIn 4
 >     ; 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/changed.help.lhs b/Help/UGen/Filter/changed.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/Filter/changed.help.lhs
@@ -0,0 +1,12 @@
+> Sound.SC3.UGen.Help.viewSC3Help "Changed"
+> Sound.SC3.UGen.DB.ugenSummary "Changed"
+
+> import Sound.SC3.ID
+
+simple composition of hpz1 and >*
+
+> let {s = lfNoise0 'α' KR 2
+>     ;c = changed s 0
+>     ;c' = decay2 c 0.01 0.5
+>     ;o = sinOsc AR (440 + mce2 s c' * 440) 0 * 0.1}
+> in audition (out 0 o)
diff --git a/Help/UGen/Filter/combN.help.lhs b/Help/UGen/Filter/combN.help.lhs
--- a/Help/UGen/Filter/combN.help.lhs
+++ b/Help/UGen/Filter/combN.help.lhs
@@ -1,29 +1,32 @@
 > Sound.SC3.UGen.Help.viewSC3Help "CombN"
 > Sound.SC3.UGen.DB.ugenSummary "CombN"
 
-> import Sound.SC3.ID
+> import Sound.SC3.ID {- hsc3 -}
 
 Comb filter as resonator. The resonant fundamental is equal to
 reciprocal of the delay time.
-> let {n = whiteNoise 'a' AR
+
+> let {n = whiteNoise 'α' AR
 >     ;dt = xLine KR 0.0001 0.01 20 RemoveSynth}
 > in audition (out 0 (combN (n * 0.1) 0.01 dt 0.2))
 
-> let {n = whiteNoise 'a' AR
+> let {n = whiteNoise 'α' AR
 >     ;dt = xLine KR 0.0001 0.01 20 RemoveSynth}
 > in audition (out 0 (combL (n * 0.1) 0.01 dt 0.2))
 
-> let {n = whiteNoise 'a' AR
+> let {n = whiteNoise 'α' AR
 >     ;dt = xLine KR 0.0001 0.01 20 RemoveSynth}
 > in audition (out 0 (combC (n * 0.1) 0.01 dt 0.2))
 
 With negative feedback
-> let {n = whiteNoise 'a' AR
+
+> let {n = whiteNoise 'α' AR
 >     ;dt = xLine KR 0.0001 0.01 20 RemoveSynth}
 > in audition (out 0 (combC (n * 0.1) 0.01 dt (-0.2)))
 
 Used as an echo.
-> let {d = dust 'a' AR 1
->     ;n = whiteNoise 'a' AR
+
+> let {d = dust 'α' AR 1
+>     ;n = whiteNoise 'β' AR
 >     ;i = decay (d * 0.5) 0.2 * n}
 > in audition (out 0 (combC i 0.2 0.2 3))
diff --git a/Help/UGen/Filter/decay2.help.lhs b/Help/UGen/Filter/decay2.help.lhs
--- a/Help/UGen/Filter/decay2.help.lhs
+++ b/Help/UGen/Filter/decay2.help.lhs
@@ -4,11 +4,13 @@
 > import Sound.SC3
 
 Used as an envelope
-> let { s = fSinOsc AR 600 0 * 0.25
->     ; f = xLine KR 1 50 20 RemoveSynth }
+
+> let {s = fSinOsc AR 600 0 * 0.25
+>     ;f = xLine KR 1 50 20 RemoveSynth}
 > in audition (out 0 (decay2 (impulse AR f 0) 0.01 0.2 * s))
 
 Compare the above with Decay used as the envelope.
-> let { s = fSinOsc AR 600 0 * 0.25
->     ; f = xLine KR 1 50 20 RemoveSynth }
+
+> let {s = fSinOsc AR 600 0 * 0.25
+>     ;f = xLine KR 1 50 20 RemoveSynth}
 > in audition (out 0 (decay (impulse AR f 0) 0.2 * s))
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,10 +4,12 @@
 > import Sound.SC3.ID
 
 allocate & initialise buffer zero
+
 > 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])
+
+> let {n = lfNoise1 'α' KR (mce [3,3.05])
 >     ;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
diff --git a/Help/UGen/Filter/dynKlank.help.lhs b/Help/UGen/Filter/dynKlank.help.lhs
--- a/Help/UGen/Filter/dynKlank.help.lhs
+++ b/Help/UGen/Filter/dynKlank.help.lhs
@@ -1,31 +1,36 @@
 > Sound.SC3.UGen.Help.viewSC3Help "DynKlank"
 > Sound.SC3.UGen.DB.ugenSummary "DynKlank"
 
-> import Sound.SC3.ID
+> import Sound.SC3.ID {- hsc3 -}
 
 {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))
+> in audition (out 0 (dynKlank (dust 'α' 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))
+> in audition (out 0 (dynKlank (pinkNoise 'α' 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))
+> in audition (out 0 (dynKlank (pinkNoise 'α' AR * a) 1 0 1 s))
 
-change freqs and ringtimes with mouse
+Change frequencies (x) and ring-times (y) 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
diff --git a/Help/UGen/Filter/formlet.help.lhs b/Help/UGen/Filter/formlet.help.lhs
--- a/Help/UGen/Filter/formlet.help.lhs
+++ b/Help/UGen/Filter/formlet.help.lhs
@@ -1,7 +1,7 @@
 > Sound.SC3.UGen.Help.viewSC3Help "Formlet"
 > Sound.SC3.UGen.DB.ugenSummary "Formlet"
 
-> import Sound.SC3.ID
+> import Sound.SC3.ID {- hsc3 -}
 
 > audition (out 0 (formlet (impulse AR 20 0.5) 1000 0.01 0.1))
 
@@ -9,6 +9,24 @@
 > in audition (out 0 (formlet (blip AR f 1000 * 0.1) 1000 0.01 0.1))
 
 Modulating formant frequency.
+
 > let {s = blip AR (sinOsc KR 5 0 * 20 + 300) 1000 * 0.1
 >     ;ff = xLine KR 1500 700 8 RemoveSynth}
 > in audition (out 0 (formlet s ff 0.005 0.04))
+
+Mouse control of frequency and decay time.
+
+> let {s = blip AR (sinOsc KR 5 0 * 20 + 300) 1000 * 0.1
+>     ;x = mouseX KR 0.01 0.2 Exponential 0.2
+>     ;y = mouseY KR 700 2000 Exponential 0.2
+>     ;o = formlet s y 0.005 x}
+> in audition (out 0 o)
+
+and again...
+
+> let {s = dust 'α' KR (mce2 10 11)
+>     ;x = mouseX KR 0.1 2 Exponential 0.2
+>     ;y = mouseY KR 7 200 Exponential 0.2
+>     ;f = formlet s y 0.005 x
+>     ;o = sinOsc AR (f * 200 + mce2 500 600 - 100) 0 * 0.2}
+> in audition (out 0 o)
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
@@ -1,28 +1,32 @@
 > Sound.SC3.UGen.Help.viewSC3Help "FreqShift"
 > Sound.SC3.UGen.DB.ugenSummary "FreqShift"
 
-> import Sound.SC3.ID
+> import Sound.SC3.ID {- hcs3 -}
 
-shifting a 100Hz tone by 1 Hz rising to 500Hz
+Shifting a 100Hz tone by 1 Hz rising to 500Hz
+
 > let {i = sinOsc AR 100 0
 >     ;s = xLine KR 1 500 5 RemoveSynth}
 > in audition (out 0 (freqShift i s 0 * 0.1))
 
-shifting a complex tone by 1 Hz rising to 500Hz
+Shifting a complex tone by 1 Hz rising to 500Hz
+
 > let {d = klangSpec [101, 303, 606, 808] [1, 1, 1, 1] [1, 1, 1, 1]
 >     ;i = klang AR 1 0 d
 >     ;s = xLine KR 1 500 5 RemoveSynth}
 > in audition (out 0 (freqShift i s 0 * 0.1))
 
-modulating shift and phase
-> let {s = lfNoise2 'a' AR 0.3
+Modulating shift and phase
+
+> let {s = lfNoise2 'α' AR 0.3
 >     ;i = sinOsc AR 10 0
 >     ;p = linLin (sinOsc AR 500 0) (-1) 1 0 (2 * pi)}
 > in audition (out 0 (freqShift i (s * 1500) p * 0.1))
 
-shifting bandpassed noise
-> let {n1 = whiteNoise 'a' AR
->     ;n2 = lfNoise0 'a' AR 5.5
+Shifting bandpassed noise
+
+> let {n1 = whiteNoise 'α' AR
+>     ;n2 = lfNoise0 'β' AR 5.5
 >     ;i = bpf n1 1000 0.001
 >     ;s = n2 * 1000}
 > in audition (out 0 (freqShift i s 0 * 32))
@@ -32,9 +36,10 @@
 ;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
+>     ;s = freqShift a (lfNoise0 'α' 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/klank.help.lhs b/Help/UGen/Filter/klank.help.lhs
--- a/Help/UGen/Filter/klank.help.lhs
+++ b/Help/UGen/Filter/klank.help.lhs
@@ -1,18 +1,15 @@
 > Sound.SC3.UGen.Help.viewSC3Help "Klank"
 > Sound.SC3.UGen.DB.ugenSummary "Klank"
 
-# SC3
-SC3 reorders the inputs, hsc3 does not.
+> import Sound.SC3 {- hsc3 -}
 
-# hsc3
 The function klankSpec can help create the 'spec' entry.
 
-> import Sound.SC3
-
-> let s = klankSpec [800,1071,1153,1723] [1,1,1,1] [1,1,1,1]
+> let s = klankSpec' [800,1071,1153,1723] [1,1,1,1] [1,1,1,1]
 > in audition (out 0 (klank (impulse AR 2 0 * 0.1) 1 0 1 s))
 
 A variant spec function takes non-UGen inputs
+
 > let {f = [800::Double,1071,1153,1723]
 >     ;u = [1,1,1,1]
 >     ;s = klankSpec' f u u}
@@ -21,13 +18,14 @@
 There is a limited form of multiple channel expansion possible at
 'specification' input, below three equal dimensional specifications
 are transposed and force expansion in a sensible manner.
-> let { u = [1,1,1,1]
->     ; p = [200,171,153,172]
->     ; q = [930,971,953,1323]
->     ; r = [8900,16062,9013,7892]
->     ; k = mce [klankSpec' p u u,klankSpec' q u u,klankSpec' r u u]
->     ; s = mceTranspose k
->     ; i = mce [2,2.07,2.13]
->     ; t = impulse AR i 0 * 0.1
->     ; l = mce [-1,0,1] }
+
+> let {u = [1,1,1,1]
+>     ;p = [200,171,153,172]
+>     ;q = [930,971,953,1323]
+>     ;r = [8900,16062,9013,7892]
+>     ;k = mce [klankSpec' p u u,klankSpec' q u u,klankSpec' r u u]
+>     ;s = mceTranspose k
+>     ;i = mce [2,2.07,2.13]
+>     ;t = impulse AR i 0 * 0.1
+>     ;l = mce [-1,0,1]}
 > in audition (out 0 (mix (pan2 (klank t 1 0 1 s) l 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,6 @@
 > import Sound.SC3
 
 used to lag pitch
+
 > 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/lagUD.help.lhs b/Help/UGen/Filter/lagUD.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/Filter/lagUD.help.lhs
@@ -0,0 +1,10 @@
+> Sound.SC3.UGen.Help.viewSC3Help "LagUD"
+> Sound.SC3.UGen.DB.ugenSummary "LagUD"
+
+> import Sound.SC3
+
+lag pitch, slower down (5 seconds) than up (1 second)
+
+> let {x = mouseX KR 220 440 Linear 0.2
+>     ;o = sinOsc AR (mce2 x (lagUD x 1 5)) 0 * 0.1}
+> in audition (out 0 o)
diff --git a/Help/UGen/Filter/linXFade2.help.lhs b/Help/UGen/Filter/linXFade2.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/Filter/linXFade2.help.lhs
@@ -0,0 +1,12 @@
+> Sound.SC3.UGen.Help.viewSC3Help "LinXFade2"
+> Sound.SC3.UGen.DB.ugenSummary "LinXFade2"
+
+> import Sound.SC3.ID
+
+> let o = linXFade2 (saw AR 440) (sinOsc AR 440 0) (lfTri KR 0.1 0) * 0.1
+> in audition (out 0 o)
+
+> let o = linXFade2 (fSinOsc AR 800 0 * 0.2)
+>                   (pinkNoise 'α' AR * 0.2)
+>                   (fSinOsc KR 1 0)
+> in audition (out 0 o)
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,12 @@
 
 > import Sound.SC3
 
-> let {r = mouseX KR 0.5 2.0 Linear 0.1
+> let {s = sinOsc AR 440 0 * 0.1
+>     ;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))
+> in audition (out 0 (pitchShift s 0.2 r d 0))
+
+> let {s = soundIn 4
+>     ;pd = mouseX KR 0.0 0.1 Linear 0.1
+>     ;td = mouseY KR 0.0 0.1 Linear 0.1}
+> in audition (out 0 (pitchShift s 0.2 (mce2 1.0 1.5) pd td))
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
@@ -1,27 +1,28 @@
 > Sound.SC3.UGen.Help.viewSC3Help "Pluck"
 > Sound.SC3.UGen.DB.ugenSummary "Pluck"
 
-> import Sound.SC3.ID
+> import Sound.SC3.ID {- hsc3 -}
 
 Excitation signal is white noise, triggered twice a second with
 varying OnePole coef.
-> let {n = whiteNoise 'a' AR
+
+> let {n = whiteNoise 'α' 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
 >     ;dl = 1 / 440}
 > in audition (out 0 (pluck (n * 0.25) t dl (dl * y) 10 x))
 
-> import Sound.SC3.UGen.Protect
+> import Sound.SC3.UGen.Protect {- hsc3 -}
 
 > let {n = 50
->     ;udup = uclone 'a'
->     ;f = udup n (rand 'a' 0.05 0.2)
->     ;p = udup n (rand 'a' 0 1)
->     ;w = udup n (whiteNoise 'a' AR)
->     ;fi = udup n (rand 'a' 10 12)
->     ;coef = rand 'a' 0.01 0.2
->     ;l = udup n (rand 'a' (-1) 1)
+>     ;udup = uclone 'α'
+>     ;f = udup n (rand 'β' 0.05 0.2)
+>     ;p = udup n (rand 'γ' 0 1)
+>     ;w = udup n (whiteNoise 'δ' AR)
+>     ;fi = udup n (rand 'ε' 10 12)
+>     ;coef = rand 'ζ' 0.01 0.2
+>     ;l = udup n (rand 'η' (-1) 1)
 >     ;x = mouseX KR 60 1000 Exponential 0.1
 >     ;o = linLin (sinOsc KR f p) (-1) 1 x 3000
 >     ;i = impulse KR fi 0
diff --git a/Help/UGen/Filter/ramp.help.lhs b/Help/UGen/Filter/ramp.help.lhs
--- a/Help/UGen/Filter/ramp.help.lhs
+++ b/Help/UGen/Filter/ramp.help.lhs
@@ -4,7 +4,14 @@
 > import Sound.SC3
 
 Used to lag pitch
+
 > let {o = lfPulse KR 4 0 0.5 * 50 + 400
 >     ;l = line KR 0 1 15 DoNothing
 >     ;f = ramp o l}
 > in audition (out 0 (sinOsc AR f 0 * 0.3))
+
+mouse control
+
+> let {x = mouseX KR 220 440 Exponential 0
+>     ;x' = ramp x (300 / 1000)}
+> in audition (out 0 (sinOsc AR (mce2 x x') 0 * 0.1))
diff --git a/Help/UGen/Filter/resonz.help.lhs b/Help/UGen/Filter/resonz.help.lhs
--- a/Help/UGen/Filter/resonz.help.lhs
+++ b/Help/UGen/Filter/resonz.help.lhs
@@ -3,20 +3,33 @@
 
 > import Sound.SC3.ID
 
-> let n = whiteNoise 'a' AR
+> let n = whiteNoise 'α' AR
 > in audition (out 0 (resonz (n * 0.5) 2000 0.1))
 
 Modulate frequency
-> let { n = whiteNoise 'a' AR
->     ; f = xLine KR 1000 8000 10 RemoveSynth }
+
+> let {n = whiteNoise 'α' AR
+>     ;f = xLine KR 1000 8000 10 RemoveSynth}
 > in audition (out 0 (resonz (n * 0.5) f 0.05))
 
 Modulate bandwidth
-> let { n = whiteNoise 'a' AR
->     ; bw = xLine KR 1 0.001 8 RemoveSynth }
+
+> let {n = whiteNoise 'α' AR
+>     ;bw = xLine KR 1 0.001 8 RemoveSynth}
 > in audition (out 0 (resonz (n * 0.5) 2000 bw))
 
 Modulate bandwidth opposite direction
-> let { n = whiteNoise 'a' AR
->     ; bw = xLine KR 0.001 1 8 RemoveSynth }
+
+> let {n = whiteNoise 'α' AR
+>     ;bw = xLine KR 0.001 1 8 RemoveSynth}
 > in audition (out 0 (resonz (n * 0.5) 2000 bw))
+
+Mouse exam (1/Q = bandwidth / center-frequency)
+
+> let {n = pinkNoise 'α' AR
+>     ;m = mouseX KR 36 85 Linear 0.2 {- midi note -}
+>     ;w = mouseY KR 0.1 5 Linear 0.2 {- bandwidth -}
+>     ;f = midiCPS (floorE m) {- centre frequency -}
+>     ;rq = w / f {- 1/Q (reciprocal of Q) -}
+>     ;o = resonz (n * 0.5) f rq}
+> in audition (out 0 o)
diff --git a/Help/UGen/Filter/selectX.help.lhs b/Help/UGen/Filter/selectX.help.lhs
--- a/Help/UGen/Filter/selectX.help.lhs
+++ b/Help/UGen/Filter/selectX.help.lhs
@@ -1,7 +1,7 @@
 > Sound.SC3.UGen.Help.viewSC3Help "SelectX"
 > :t selectX
 
-# composite
+# composite ugen graph
 
 > import Sound.SC3
 > import Sound.SC3.UGen.Dot
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
@@ -7,10 +7,20 @@
 >                 ;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
+> let z = sinOsc AR 300 0 * line KR 0 1 6 RemoveSynth
 > in withSC3 (do {_ <- mk_b [1,0,1,1,0,1]
->                ;play (out 0 (shaper 10 s * 0.1))})
+>                ;play (out 0 (shaper 10 z * 0.1))})
 
-> let s = sinOsc AR 400 (pi / 2) * line KR 0 1 6 RemoveSynth
+> let z = 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))})
+>                ;play (out 0 (shaper 10 z * 0.1))})
+
+> let {z = soundIn 4
+>     ;x = sinOsc KR (1/4) 0}
+> in withSC3 (do {_ <- mk_b [1,0,1,1,0,1]
+>                ;play (out 0 (xFade2 z (shaper 10 z) x 0.5))})
+
+> let {z = soundIn 4
+>     ;x = mouseX KR (-1) 1 Linear 0.2}
+> in withSC3 (do {_ <- mk_b [1,0,1,1,0,1,0.5,0,0.25,0,0.75,1]
+>                ;play (out 0 (xFade2 z (shaper 10 z) x 0.5))})
diff --git a/Help/UGen/Filter/slew.help.lhs b/Help/UGen/Filter/slew.help.lhs
--- a/Help/UGen/Filter/slew.help.lhs
+++ b/Help/UGen/Filter/slew.help.lhs
@@ -3,8 +3,8 @@
 
 > import Sound.SC3
 
-> let z = lfPulse AR 800 0 0.5 * 0.2
+> let z = lfPulse AR 800 0 0.5 * 0.1
 > in audition (out 0 (mce2 z (slew z 4000 4000)))
 
-> let z = saw AR 800 * 0.2
+> let z = saw AR 800 * 0.1
 > in audition (out 0 (mce2 z (slew z 400 400)))
diff --git a/Help/UGen/Filter/varLag.help.lhs b/Help/UGen/Filter/varLag.help.lhs
--- a/Help/UGen/Filter/varLag.help.lhs
+++ b/Help/UGen/Filter/varLag.help.lhs
@@ -1,15 +1,16 @@
 > 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.
+#VarLag at sclang is a composite UGen, at hsc3 it's 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
@@ -7,4 +7,9 @@
 
 > let {x = mouseX KR 0 18 Linear 0.1
 >     ;f = wrapIndex 0 x}
-> in audition (out 0 (sinOsc AR f 0 * 0.5))
+> in audition (out 0 (sinOsc AR f 0 * 0.1))
+
+> let {b = asLocalBuf 'α' [200,300,400,500,600,800]
+>     ;x = mouseX KR 0 18 Linear 0.1
+>     ;f = wrapIndex b x}
+> in audition (out 0 (sinOsc AR f 0 * 0.1))
diff --git a/Help/UGen/Filter/xFade2.help.lhs b/Help/UGen/Filter/xFade2.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/Filter/xFade2.help.lhs
@@ -0,0 +1,12 @@
+> Sound.SC3.UGen.Help.viewSC3Help "XFade2"
+> Sound.SC3.UGen.DB.ugenSummary "XFade2"
+
+> import Sound.SC3.ID
+
+> let o = xFade2 (saw AR 440) (sinOsc AR 440 0) (lfTri KR 0.1 0) 0.1
+> in audition (out 0 o)
+
+> let o = linXFade2 (fSinOsc AR 800 0 * 0.2)
+>                   (pinkNoise 'α' AR * 0.2)
+>                   (fSinOsc KR 1 0)
+> in audition (out 0 o)
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
@@ -11,10 +11,10 @@
 >     ;lin a b = line KR a b dur RemoveSynth
 >     ;tr = impulse KR (lin 7.5 15) 0
 >     ;gd = lin 0.05 0.1
->     ;r = lin 1 0.5
->     ;i = lin 0 1
->     ;l = lin (-0.5) 0.5
->     ;g = grainBuf 2 tr gd buf r i 2 0 (-1) 512}
+>     ;r = lin 1 0.5 {- rate -}
+>     ;i = lin 0 1 {- read-location -}
+>     ;l = lin (-0.5) 0.5 {- stereo-location -}
+>     ;g = grainBuf 2 tr gd buf r i 2 l (-1) 512}
 > in audition (out 0 g)
 
 > let {b = 10
@@ -23,7 +23,7 @@
 >     ;y = mouseY KR 10 45 Linear 0.1
 >     ;i = impulse KR y 0
 >     ;n1 = lfNoise1 'α' KR 500
->     ;n2 = lfNoise2 'α' KR 0.1
+>     ;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
@@ -12,7 +12,7 @@
 > in audition (out 0 (grainFM 2 t 0.1 f 200 i l (-1) 512 * 0.1))
 
 > let {n1 = whiteNoise 'α' KR
->     ;n2 = lfNoise1 'α' KR 500
+>     ;n2 = lfNoise1 'β' KR 500
 >     ;d = 5
 >     ;x = mouseX KR (-0.5) 0.5 Linear 0.1
 >     ;y = mouseY KR 0 400 Linear 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
@@ -10,3 +10,14 @@
 > in withSC3 (do {send (b_allocRead 10 fn 0 0)
 >                ;play (out 0 w)})
 
+real-time (delayed) input
+
+> let {i = soundIn 4
+>     ;r = recordBuf AR 10 0 1 0 1 Loop 1 DoNothing i
+>     ;ph = (8192 / sampleRate) * 2 * pi
+>     ;p = lfSaw KR (1 / bufDur KR 10) ph * 0.5 + 0.5
+>     ;x = mouseX KR 0.5 2 Linear 0.2
+>     ;y = mouseY KR 0.01 0.2 Linear 0.2
+>     ;w = warp1 1 10 p x 0.1 (-1) 8 y 4}
+> in withSC3 (do {send (b_alloc 10 8192 1)
+>                ;play (out 0 (mrg2 (i + w) r))})
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
@@ -1,30 +1,53 @@
 > Sound.SC3.UGen.Help.viewSC3Help "In"
 > Sound.SC3.UGen.DB.ugenSummary "In"
 
-# hsc3
-hsc3 renames UGen to in' since in is a reserved keyword
+# hsc3 renames UGen to in' since in is a reserved keyword
 
 > import Sound.SC3.ID
 
-Patching input to output.
+Patching input to output (see also soundIn).
+
 > audition (out 0 (in' 2 AR numOutputBuses))
 
 Patching input to output, with delay.
+
 > let {i = in' 2 AR numOutputBuses
 >     ;d = delayN i 0.5 0.5}
 > in audition (out 0 (i + d))
 
-Write noise to bus 10, then read it out, the multiple root graph is ordered.
-> let {n = pinkNoise 'a' AR
->     ;wr = out 10 (n * 0.3)
->     ;rd = out 0 (in' 1 AR 10)}
-> in audition (mrg [rd, wr])
+Write noise to first private bus, then read it out.
+The multiple root graph is ordered.
 
+> let {n = pinkNoise 'α' AR
+>     ;b = numOutputBuses + numInputBuses
+>     ;wr = out b (n * 0.3)
+>     ;rd = out 0 (in' 1 AR b)}
+> in audition (mrg [rd,wr])
+
+There are functions to encapsulate the offset calculation.
+(There is also a firstPrivateBus value.)
+
+> let {n = pinkNoise 'α' AR
+>     ;wr = privateOut 0 (n * 0.3)
+>     ;rd = out 0 (privateIn 1 AR 0)}
+> in audition (mrg [rd,wr])
+
 Set value on a control bus
-> withSC3 (send (c_set [(0, 300)]))
 
+> withSC3 (send (c_set1 0 300))
+
 Read a control bus
+
 > audition (out 0 (sinOsc AR (in' 1 KR 0) 0 * 0.1))
 
 Re-set value on bus
-> withSC3 (send (c_set [(0, 600)]))
+
+> withSC3 (send (c_set1 0 600))
+
+Control rate graph writing buses 0 & 1.
+
+> audition (out 0 (mce2 (tRand 'α' 220 2200 (dust 'β' KR 1)) (dust 'γ' KR 3)))
+
+Audio rate graph reading control buses 0 & 1.
+
+> audition (out 0 (sinOsc AR (in' 1 KR 0) 0 * decay (in' 1 KR 1) 0.2 * 0.1))
diff --git a/Help/UGen/IO/inFeedback.help.lhs b/Help/UGen/IO/inFeedback.help.lhs
--- a/Help/UGen/IO/inFeedback.help.lhs
+++ b/Help/UGen/IO/inFeedback.help.lhs
@@ -4,31 +4,36 @@
 > import Sound.SC3
 
 Audio feedback modulation
+
 > let {f = inFeedback 1 0 * 1300 + 300
 >     ;s = sinOsc AR f 0 * 0.4}
 > in audition (out 0 s)
 
 Evaluate these in either order and hear both tones.
-> let {b = numInputBuses + numOutputBuses
+
+> let {b = firstPrivateBus
 >     ;s = inFeedback 1 b}
 > in audition (out 0 s)
 
-> let {b  = numInputBuses + numOutputBuses
+> let {b  = firstPrivateBus
 >     ;s0 = out b (sinOsc AR 220 0 * 0.1)
 >     ;s1 = out 0 (sinOsc AR 660 0 * 0.1)}
 > in audition (mrg [s0, s1])
 
 Doubters consult this
-> let {b = numInputBuses + numOutputBuses
+
+> let {b = firstPrivateBus
 >     ;s = in' 1 AR b}
 > in audition (out 0 s)
 
 Resonator, see localOut for variant.
-> let {b = numInputBuses + numOutputBuses
+
+> let {b = firstPrivateBus
 >     ;p = inFeedback 1 b
 >     ;i = impulse AR 1 0
 >     ;d = delayC (i + (p * 0.995)) 1 (recip 440 - recip controlRate)}
 > in audition (mrg [offsetOut b d, offsetOut 0 p])
 
 Compare with oscillator.
+
 > audition (out 1 (sinOsc AR 440 0 * 0.2))
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
@@ -1,15 +1,14 @@
 > Sound.SC3.UGen.Help.viewSC3Help "InTrig"
 > Sound.SC3.UGen.DB.ugenSummary "InTrig"
 
-# hsc3
-channel count (Int) is first argument
-
 > import Sound.SC3
 
 Run an oscillator with the trigger at bus 10.
+
 > let {t = inTrig 1 10
 >     ;e = envGen KR t t 0 1 DoNothing (envPerc 0.01 1)}
 > in audition (out 0 (sinOsc AR 440 0 * e))
 
 Set bus 10, each set will trigger a ping.
+
 > withSC3 (send (c_set1 10 0.1))
diff --git a/Help/UGen/IO/keyState.help.lhs b/Help/UGen/IO/keyState.help.lhs
--- a/Help/UGen/IO/keyState.help.lhs
+++ b/Help/UGen/IO/keyState.help.lhs
@@ -5,4 +5,5 @@
 
 The keycode 38 is the A key on my keyboard.  Under X the xev(1)
 command is useful in determining your keyboard layout.
+
 > audition (out 0 (sinOsc AR 800 0 * keyState KR 38 0 0.1 0.5))
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,13 @@
 > import Sound.SC3
 
 Set frequency at control bus
+
 > 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 (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
@@ -1,49 +1,49 @@
 > Sound.SC3.UGen.Help.viewSC3Help "LocalBuf"
 > Sound.SC3.UGen.DB.ugenSummary "LocalBuf"
 
-# SC3
-automatically inserts a maxLocalBufs into graphs
-
 > import Sound.SC3.ID
 
 Allocate a buffer local to the synthesis graph.
+
 > let {n = whiteNoise 'α' AR
->     ;m = maxLocalBufs 1
->     ;b = mrg2 (localBuf 'α' 2048 1) m
+>     ;b = localBuf 'β' 2048 1
 >     ;f = fft' b n
 >     ;c = pv_BrickWall f (sinOsc KR 0.1 0 * 0.75)}
 > in audition (out 0 (ifft' c * 0.1))
 
+> import Sound.SC3.UGen.Protect
+
 Variant with two local buffers
-> let {n = uclone 'α' 2 (whiteNoise 'α' AR)
->     ;m = maxLocalBufs 2
->     ;b = mrg2 (uclone 'α' 2 (localBuf 'α' 2048 1)) m
+
+> let {n = uclone 'α' 2 (whiteNoise 'β' AR)
+>     ;b = uclone 'γ' 2 (localBuf 'δ' 2048 1)
 >     ;f = fft' b n
 >     ;c = pv_BrickWall f (sinOsc KR (mce2 0.1 0.11) 0 * 0.75)}
 > in audition (out 0 (ifft' c * 0.1))
 
 Not clearing the buffer accesses old data, slowly overwrite data with noise
-> let {m = maxLocalBufs 1
->     ;b = mrg2 (localBuf 'α' 2048 2) m
+
+> let {b = localBuf 'α' 2048 2
 >     ;nf = bufFrames KR b
 >     ;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 = uclone 'α' 2 (whiteNoise 'α' AR)
->     ;ph = lfNoise0 'α' AR 530}
+>     ;n = uclone 'β' 2 (whiteNoise 'γ' AR)
+>     ;ph = lfNoise0 'δ' AR 530}
 > in audition (mrg2 (out 0 r) (wr ph n))
 
 bufCombC needs no clearing, because the delay line is filled by the ugen
-> let {d = uclone 'α' 2 (dust 'α' AR 1)
->     ;n = whiteNoise 'α' AR
+
+> let {d = uclone 'α' 2 (dust 'β' AR 1)
+>     ;n = whiteNoise 'γ' AR
 >     ;z = decay d 0.3 * n
 >     ;l = xLine KR 0.0001 0.01 20 DoNothing
 >     ;sr = sampleRate
->     ;m = maxLocalBufs 2
->     ;b = mrg2 (uclone 'α' 2 (localBuf 'α' sr 2)) m}
+>     ;b = uclone 'δ' 2 (localBuf 'ε' sr 2)}
 > in audition (out 0 (bufCombC b z l 0.2))
 
 asLocalBuf combines localBuf and setBuf
+
 > let {b = asLocalBuf 'α' [2,1,5,3,4,0]
 >     ;x = mouseX KR 0 (bufFrames KR b) Linear 0.2
 >     ;f = indexL b x * 100 + 40
@@ -51,16 +51,18 @@
 > in audition (out 0 o)
 
 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)
 >     ;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])
+degreeToKey example ('modal space') using local buffer
+
+> let {n = lfNoise1 'α' KR (mce [3,3.05])
 >     ;x = mouseX KR 0 15 Linear 0.1
->     ;b = asLocalBuf 'α' [0,2,3.2,5,7,9,10]
+>     ;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)
 >                    ;o = sinOsc AR f0 0 * 0.1
diff --git a/Help/UGen/IO/localIn.help.lhs b/Help/UGen/IO/localIn.help.lhs
deleted file mode 100644
--- a/Help/UGen/IO/localIn.help.lhs
+++ /dev/null
@@ -1,12 +0,0 @@
-> Sound.SC3.UGen.Help.viewSC3Help "LocalIn"
-> Sound.SC3.UGen.DB.ugenSummary "LocalIn"
-
-> import Sound.SC3.ID
-
-Ping-pong delay
-> let {n = whiteNoise 'α' AR
->     ;a0 = decay (impulse AR 0.3 0) 0.1 * n * 0.2
->     ;a1 = localIn 2 AR + mce [a0,0]
->     ;a2 = delayN a1 0.2 0.2
->     ;a3 = mceEdit reverse a2 * 0.8}
-> in audition (mrg [localOut a3,out 0 a2])
diff --git a/Help/UGen/IO/localOut.help.lhs b/Help/UGen/IO/localOut.help.lhs
--- a/Help/UGen/IO/localOut.help.lhs
+++ b/Help/UGen/IO/localOut.help.lhs
@@ -4,10 +4,12 @@
 > import Sound.SC3.ID
 
 Resonator, must subtract blockSize for correct tuning
+
 > let {p = localIn 1 AR
 >     ;i = impulse AR 1 0
 >     ;d = delayC (i + (p * 0.995)) 1 (recip 440 - recip controlRate)}
 > in audition (mrg [offsetOut 0 p,localOut d])
 
 Compare with oscillator.
+
 > audition (out 1 (sinOsc AR 440 0 * 0.2))
diff --git a/Help/UGen/IO/mouseButton.help.lhs b/Help/UGen/IO/mouseButton.help.lhs
--- a/Help/UGen/IO/mouseButton.help.lhs
+++ b/Help/UGen/IO/mouseButton.help.lhs
@@ -4,7 +4,9 @@
 > import Sound.SC3
 
 As amplitude envelope
+
 > audition (out 0 (sinOsc AR 800 0 * mouseButton KR 0 0.1 0.1))
 
 There is a variant that randomly presses the button.
+
 > audition (out 0 (sinOsc AR 800 0 * mouseButton' KR 0 0.1 0.1))
diff --git a/Help/UGen/IO/mouseX.help.lhs b/Help/UGen/IO/mouseX.help.lhs
--- a/Help/UGen/IO/mouseX.help.lhs
+++ b/Help/UGen/IO/mouseX.help.lhs
@@ -4,9 +4,11 @@
 > import Sound.SC3
 
 Frequency control
+
 > let x = mouseX KR 40 10000 Exponential 0.2
 > in audition (out 0 (sinOsc AR x 0 * 0.1))
 
 There is a variant with equal arguments but random traversal.
+
 > let x = mouseX' KR 40 10000 Exponential 0.2
 > in audition (out 0 (sinOsc AR x 0 * 0.1))
diff --git a/Help/UGen/IO/mouseY.help.lhs b/Help/UGen/IO/mouseY.help.lhs
--- a/Help/UGen/IO/mouseY.help.lhs
+++ b/Help/UGen/IO/mouseY.help.lhs
@@ -4,11 +4,13 @@
 > import Sound.SC3
 
 Frequency at X axis and amplitude at Y axis.
+
 > let {freq = mouseX KR 20 2000 Exponential 0.1
 >     ;ampl = mouseY KR 0.01 0.1 Linear 0.1}
 > in audition (out 0 (sinOsc AR freq 0 * ampl))
 
 There is a variant with equal arguments but a random traversal.
+
 > let {freq = mouseX' KR 20 2000 Exponential 0.1
 >     ;ampl = mouseY' KR 0.01 0.1 Linear 0.1}
 > in audition (out 0 (sinOsc AR freq 0 * ampl))
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
@@ -15,6 +15,7 @@
 
 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 <- time
 >       ;sr <- serverSampleRateActual
diff --git a/Help/UGen/IO/out.help.lhs b/Help/UGen/IO/out.help.lhs
--- a/Help/UGen/IO/out.help.lhs
+++ b/Help/UGen/IO/out.help.lhs
@@ -4,8 +4,10 @@
 > import Sound.SC3
 
 Oscillators at outputs zero (330) and one (331)
+
 > audition (out 0 (sinOsc AR (mce2 330 331) 0 * 0.1))
 
 out is summing, as opposed to replaceOut
+
 > audition (mrg [out 0 (sinOsc AR (mce2 330 990) 0 * 0.1)
 >               ,out 0 (sinOsc AR (mce2 331 991) 0 * 0.1)])
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
@@ -3,13 +3,17 @@
 
 > import Sound.SC3.ID
 
+> audition (replaceOut 0 (sinOsc AR 440 0 * 0.1))
+
 Send signal to a bus, overwrite existing signal.
+
 > let {a = out 0 (sinOsc AR (mce [330, 331]) 0 * 0.1)
 >     ;b = replaceOut 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])
 
 Compare to
+
 > let {a = out 0 (sinOsc AR (mce [330, 331]) 0 * 0.1)
 >     ;b = out 0 (sinOsc AR (mce [880, 881]) 0 * 0.1)
 >     ;c = out 0 (sinOsc AR (mce [120, 121]) 0 * 0.1)}
@@ -17,7 +21,8 @@
 
 a writes noise to 24
 b reads 24 and replaces with filtered variant
-c reads 24 and write to 0
+c reads 24 and writes 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)}
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
@@ -1,13 +1,15 @@
 > Sound.SC3.UGen.Help.viewSC3Help "SoundIn"
 
-# composite
+# composite of in' and numOutputBuses
 
 > import Sound.SC3
 
 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
@@ -15,4 +17,5 @@
             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
@@ -4,6 +4,7 @@
 > import Sound.SC3
 
 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}
diff --git a/Help/UGen/Information/controlDur.help.lhs b/Help/UGen/Information/controlDur.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/Information/controlDur.help.lhs
@@ -0,0 +1,9 @@
+> Sound.SC3.UGen.Help.viewSC3Help "ControlDur"
+> Sound.SC3.UGen.DB.ugenSummary "ControlDur"
+
+> import Sound.SC3
+
+controlRate and controlDur are reciprocals
+
+> let f = mce2 controlRate (recip controlDur)
+> in audition (out 0 (sinOsc AR f 0 * 0.1))
diff --git a/Help/UGen/Information/controlRate.help.lhs b/Help/UGen/Information/controlRate.help.lhs
--- a/Help/UGen/Information/controlRate.help.lhs
+++ b/Help/UGen/Information/controlRate.help.lhs
@@ -3,5 +3,7 @@
 
 > import Sound.SC3
 
-play a sine tone at control rate
-> audition (out 0 (sinOsc AR controlRate 0 * 0.1))
+play a sine tone at control rate, the reciprocal of controlDur
+
+> let f = mce2 controlRate (recip controlDur)
+> in audition (out 0 (sinOsc AR f 0 * 0.1))
diff --git a/Help/UGen/Information/numBuffers.help.lhs b/Help/UGen/Information/numBuffers.help.lhs
--- a/Help/UGen/Information/numBuffers.help.lhs
+++ b/Help/UGen/Information/numBuffers.help.lhs
@@ -1,2 +1,10 @@
 > Sound.SC3.UGen.Help.viewSC3Help "NumBuffers"
 > Sound.SC3.UGen.DB.ugenSummary "NumBuffers"
+
+> import Sound.SC3
+
+the number of audio buffers available at the server (by default 1024)
+> audition (poll (impulse KR 1 0) numBuffers (label "numBuffers") 0)
+
+> let f = 110 + numBuffers
+> in audition (out 0 (sinOsc AR f 0 * 0.1))
diff --git a/Help/UGen/Information/poll.help.lhs b/Help/UGen/Information/poll.help.lhs
--- a/Help/UGen/Information/poll.help.lhs
+++ b/Help/UGen/Information/poll.help.lhs
@@ -1,13 +1,14 @@
 > Sound.SC3.UGen.Help.viewSC3Help "Poll"
 > Sound.SC3.UGen.DB.ugenSummary "Poll"
 
-> import Sound.SC3.ID
+> import Sound.SC3.ID {- hsc3 -}
 
 > 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/radiansPerSample.help.lhs b/Help/UGen/Information/radiansPerSample.help.lhs
--- a/Help/UGen/Information/radiansPerSample.help.lhs
+++ b/Help/UGen/Information/radiansPerSample.help.lhs
@@ -1,3 +1,8 @@
 > Sound.SC3.UGen.Help.viewSC3Help "RadiansPerSample"
 > Sound.SC3.UGen.DB.ugenSummary "RadiansPerSample"
 
+> import Sound.SC3
+
+two pi divided by the nominal sample rate (ie. a very small number)
+> let f = mce2 radiansPerSample ((2 * pi) / sampleRate) * 5e6
+> in audition (out 0 (sinOsc AR f 0 * 0.1))
diff --git a/Help/UGen/Information/sampleDur.help.lhs b/Help/UGen/Information/sampleDur.help.lhs
--- a/Help/UGen/Information/sampleDur.help.lhs
+++ b/Help/UGen/Information/sampleDur.help.lhs
@@ -1,2 +1,9 @@
 > Sound.SC3.UGen.Help.viewSC3Help "SampleDur"
 > Sound.SC3.UGen.DB.ugenSummary "SampleDur"
+
+> import Sound.SC3
+
+the reciprocal of the nominal sample rate of the server
+
+> let f = mce2 sampleRate (recip sampleDur) * 0.01
+> in audition (out 0 (sinOsc AR f 0 * 0.1))
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
@@ -3,11 +3,15 @@
 
 > import Sound.SC3
 
-Compare a sine tone derived from sample rate with a 440Hz tone.
-> let f = mce [sampleRate * 0.01, 440]
+the current nominal sample rate of the server
+
+> let {sr = 48000 {- 44100 -}
+>     ;f = mce2 sampleRate sr * 0.01}
 > in audition (out 0 (sinOsc AR f 0 * 0.1))
 
 The server status command can extract nominal and actual sample rates
 from a running server.
+
 > import Control.Monad
+
 > withSC3 (liftM2 (,) serverSampleRateNominal serverSampleRateActual)
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
@@ -7,7 +7,7 @@
 > withSC3 (async (b_alloc 10 512 1))
 
 > let { x = mouseX KR 0 1 Linear 0.2
->     ; i = soundIn 0
+>     ; i = soundIn 4
 >     ; c = fft' 10 i
 >     ; o = onsets' c x (onsetType "rcomplex")
 >     ; s = sinOsc AR 440 0 * 0.2
diff --git a/Help/UGen/MachineListening/specCentroid.help.lhs b/Help/UGen/MachineListening/specCentroid.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/MachineListening/specCentroid.help.lhs
@@ -0,0 +1,14 @@
+> Sound.SC3.UGen.Help.viewSC3Help "SpecCentroid"
+> Sound.SC3.UGen.DB.ugenSummary "SpecCentroid"
+
+> import Sound.SC3
+
+as the number of harmonics increases, the centroid is pushed higher
+> let {f0 = mouseY KR 1000 100 Exponential 0.2
+>     ;nh = mouseX KR 1 100 Exponential 0.2
+>     ;z = blip AR f0 nh
+>     ;f = fft' (localBuf 'α' 2048 1) z
+>     ;c = specCentroid f
+>     ;p = poll' (impulse KR 1 0) c (label "c") 0
+>     ;o = sinOsc AR p 0 * 0.1}
+> in audition (out 0 o)
diff --git a/Help/UGen/MachineListening/specFlatness.help.lhs b/Help/UGen/MachineListening/specFlatness.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/MachineListening/specFlatness.help.lhs
@@ -0,0 +1,13 @@
+> Sound.SC3.UGen.Help.viewSC3Help "SpecFlatness"
+> Sound.SC3.UGen.DB.ugenSummary "SpecFlatness"
+
+> import Sound.SC3.ID
+
+> let {z = soundIn 4
+>     ;g = 1 {- gain, set as required -}
+>     ;a = wAmp KR z 0.05
+>     ;f = fft' (localBuf 'α' 2048 1) z
+>     ;c = poll' 1 (specCentroid f) (label "c") 0
+>     ;w = poll' 1 (specFlatness f) (label "w") 0
+>     ;o = bpf (pinkNoise 'a' AR) c w * a * g}
+> in audition (out 0 o)
diff --git a/Help/UGen/Math/gt.help.lhs b/Help/UGen/Math/gt.help.lhs
--- a/Help/UGen/Math/gt.help.lhs
+++ b/Help/UGen/Math/gt.help.lhs
@@ -1,9 +1,8 @@
 > Sound.SC3.UGen.Help.viewSC3Help "Operator.=="
 > :t (==*)
 
-#hsc3
 The star suffixes (<*,<=*,>*,>=*) are because the result of the
-operatros is not of type Bool, as is required by the signature for the
+operators is not of type Bool, as is required by the signature for the
 class Ord.
 
 > import Sound.SC3
diff --git a/Help/UGen/Noise/brownNoise.help.lhs b/Help/UGen/Noise/brownNoise.help.lhs
--- a/Help/UGen/Noise/brownNoise.help.lhs
+++ b/Help/UGen/Noise/brownNoise.help.lhs
@@ -3,10 +3,10 @@
 
 > import Sound.SC3.ID
 
-> let n = brownNoise 'a' AR
+> let n = brownNoise 'α' AR
 > in audition (out 0 (n * 0.1))
 
-> let {n = brownNoise 'a' KR
+> let {n = brownNoise 'α' KR
 >     ;o = sinOsc AR (linExp n (-1) 1 64 9600) 0 * 0.1}
 > in audition (out 0 o)
 
diff --git a/Help/UGen/Noise/choose.help.lhs b/Help/UGen/Noise/choose.help.lhs
--- a/Help/UGen/Noise/choose.help.lhs
+++ b/Help/UGen/Noise/choose.help.lhs
@@ -1,7 +1,6 @@
 > :t choose
 
-# composite
-choose is a composite of iRand and select.
+# choose is a composite of iRand and select.
 
 > import Sound.SC3.ID
 
diff --git a/Help/UGen/Noise/lfNoise1.help.lhs b/Help/UGen/Noise/lfNoise1.help.lhs
--- a/Help/UGen/Noise/lfNoise1.help.lhs
+++ b/Help/UGen/Noise/lfNoise1.help.lhs
@@ -3,14 +3,16 @@
 
 > import Sound.SC3.ID
 
-> audition (out 0 (lfNoise1 'a' AR 1000 * 0.05))
+> audition (out 0 (lfNoise1 'α' AR 1000 * 0.05))
 
 Modulate frequency.
+
 > let {f = xLine KR 1000 10000 10 RemoveSynth
->     ;n = lfNoise1 'a' AR f}
+>     ;n = lfNoise1 'α' AR f}
 > in audition (out 0 (n * 0.05))
 
 Use as frequency control.
-> let {n = lfNoise1 'a' KR 4
+
+> let {n = lfNoise1 'α' KR 4
 >     ;f = n * 400 + 450}
 > in audition (out 0 (sinOsc AR f 0 * 0.1))
diff --git a/Help/UGen/Oscillator/blip.help.lhs b/Help/UGen/Oscillator/blip.help.lhs
--- a/Help/UGen/Oscillator/blip.help.lhs
+++ b/Help/UGen/Oscillator/blip.help.lhs
@@ -1,19 +1,22 @@
 > Sound.SC3.UGen.Help.viewSC3Help "Blip"
 > Sound.SC3.UGen.DB.ugenSummary "Blip"
 
-> import Sound.SC3
+> import Sound.SC3 {- hsc3 -}
 
 > audition (out 0 (blip AR 440 200 * 0.1))
 
 Modulate frequency
+
 > let f = xLine KR 20000 200 6 RemoveSynth
 > in audition (out 0 (blip AR f 100 * 0.1))
 
 Modulate number of harmonics.
+
 > let nh = line KR 1 100 20 RemoveSynth
 > in audition (out 0 (blip AR 200 nh * 0.2))
 
 Self-modulation at control rate.
+
 > let {fr = blip KR 0.25 3 * 300 + 500
 >     ;nh = blip KR 0.15 2 * 20 + 21}
 > in audition (out 0 (blip AR fr nh * 0.2))
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,16 +4,20 @@
 > import Sound.SC3
 
 Allocate and fill buffer.
+
 > 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.1))
 
 Modulate beat frequency with mouseX
+
 > 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.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
@@ -4,17 +4,21 @@
 > import Sound.SC3
 
 nothing
+
 > audition (out 0 0)
 > withSC3 (send (n_trace [-1]))
 
 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))
 
 Transient before LeakDC adapts and suppresses the offset?
+
 > audition (out 0 (dc AR 1))
 > audition (out 0 (leakDC (dc AR 1) 0.995))
diff --git a/Help/UGen/Oscillator/dynKlang.help.lhs b/Help/UGen/Oscillator/dynKlang.help.lhs
--- a/Help/UGen/Oscillator/dynKlang.help.lhs
+++ b/Help/UGen/Oscillator/dynKlang.help.lhs
@@ -1,18 +1,21 @@
 > Sound.SC3.UGen.Help.viewSC3Help "DynKlang"
 > Sound.SC3.UGen.DB.ugenSummary "DynKlang"
 
-> import Sound.SC3.ID
+> import Sound.SC3.ID {- hsc3 -}
 
 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}
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
@@ -1,21 +1,23 @@
 > Sound.SC3.UGen.Help.viewSC3Help "FSinOsc"
 > Sound.SC3.UGen.DB.ugenSummary "FSinOsc"
 
-# SC2
-The initial phase argument was not in the SC2 variant.
+# SC2 did not have the initial phase argument.
 
 > import Sound.SC3
 
 > audition (out 0 (fSinOsc AR (mce2 440 550) 0 * 0.05))
 
 Modulate frequency
+
 > audition (out 0 (fSinOsc AR (xLine KR 200 4000 1 RemoveSynth) 0 * 0.1))
 
 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 sine envelope (see also 'sine_grain_ugen_graph')
+
 > let sine = let {b = control IR "out" 0
 >                ;f = control IR "freq" 440
 >                ;d = control IR "dur" 0.2
@@ -30,6 +32,7 @@
 > import Sound.SC3.Lang.Pattern {- hsc3-lang -}
 
 granular synthesis
+
 > audition (pbind [(K_instr,psynth sine)
 >                 ,(K_midinote,fmap roundE (pbrown 'α' 72 84 1 inf))
 >                 ,(K_detune,pwhite 'β' 0 10 inf)
diff --git a/Help/UGen/Oscillator/formant.help.lhs b/Help/UGen/Oscillator/formant.help.lhs
--- a/Help/UGen/Oscillator/formant.help.lhs
+++ b/Help/UGen/Oscillator/formant.help.lhs
@@ -4,14 +4,17 @@
 > import Sound.SC3
 
 Modulate fundamental frequency, formant frequency stays constant.
+
 > let f = xLine KR 400 1000 8 RemoveSynth
 > in audition (out 0 (formant AR f 2000 800 * 0.125))
 
 Modulate formant frequency, fundamental frequency stays constant.
+
 > let {f = mce [200, 300, 400, 500]
 >     ;ff = xLine KR 400 4000 8 RemoveSynth}
 > in audition (out 0 (formant AR f ff 200 * 0.125))
 
 Modulate width frequency, other frequencies stay constant.
+
 > let bw = xLine KR 800 8000 8 RemoveSynth
 > in audition (out 0 (formant AR 400 2000 bw * 0.1))
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
@@ -1,6 +1,8 @@
 > Sound.SC3.UGen.Help.viewSC3Help "Impulse"
 > Sound.SC3.UGen.DB.ugenSummary "Impulse"
 
+# SC2 had no phase input.
+
 > import Sound.SC3
 
 > audition (out 0 (impulse AR 800 0 * 0.1))
@@ -13,4 +15,5 @@
 > in audition (out 0 (impulse AR f (mce [0,x]) * 0.1))
 
 An impulse with frequency 0 returns a single impulse
+
 > audition (out 0 (decay (impulse AR 0 0) 1 * brownNoise 'a' AR * 0.1))
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
@@ -1,8 +1,7 @@
 > Sound.SC3.UGen.Help.viewSC3Help "Klang"
 > Sound.SC3.UGen.DB.ugenSummary "Klang"
 
-# SC3
-Input re-ordering of specification array.
+# SC2 had mul/add inputs.
 
 > import Sound.SC3.ID
 
@@ -12,14 +11,17 @@
 > 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
--- a/Help/UGen/Oscillator/lfGauss.help.lhs
+++ b/Help/UGen/Oscillator/lfGauss.help.lhs
@@ -43,7 +43,7 @@
 >     ;g = lfGauss AR d w 0 Loop DoNothing}
 > in audition (out 0 (g * 0.2))
 
-several frequecies and widths combined
+several frequencies 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
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
@@ -1,8 +1,7 @@
 > Sound.SC3.UGen.Help.viewSC3Help "LFPulse"
 > Sound.SC3.UGen.DB.ugenSummary "LFPulse"
 
-#SC2
-The initial phase argument was not present in SC2.
+# SC2 had no initial phase argument.
 
 > import Sound.SC3
 
@@ -11,3 +10,17 @@
 
 > let x = mouseX KR 0 1 Linear 0.2
 > in audition (out 0 (lfPulse AR 220 0 x * 0.1))
+
+square wave as sum of sines.
+for odd partials n, amplitude is (1 / n), for even partials amplitude is 0.
+phase is always 0.
+
+> let {mk_freq f0 n = f0 * fromInteger n
+>     ;mk_amp n = if even n then 0 else 1 / fromInteger n
+>     ;mk_param f0 n = let m = [1,3 .. n] in zip (map (mk_freq f0) m) (map mk_amp m)
+>     ;x = midiCPS (mouseX KR 20 72 Linear 0.2)
+>     ;y = mouseY KR 0.01 0.1 Exponential 0.2
+>     ;e = xLine KR 0.01 1 20 DoNothing
+>     ;o1 = sum (map (\(fr,am) -> sinOsc AR fr 0 * am) (mk_param x 50)) * (1 - e)
+>     ;o2 = lfPulse AR x 0 0.5 * e}
+> in audition (out 0 (mce2 o1 o2 * y))
diff --git a/Help/UGen/Oscillator/lfSaw.help.lhs b/Help/UGen/Oscillator/lfSaw.help.lhs
--- a/Help/UGen/Oscillator/lfSaw.help.lhs
+++ b/Help/UGen/Oscillator/lfSaw.help.lhs
@@ -1,17 +1,32 @@
 > Sound.SC3.UGen.Help.viewSC3Help "LFSaw"
 > Sound.SC3.UGen.DB.ugenSummary "LFSaw"
 
-# SC2
-SC2 LFSaw did not have an initial phase argument.
+# SC2 did not have the initial phase argument.
 
 > import Sound.SC3
 
 > audition (out 0 (lfSaw AR 500 1 * 0.1))
 
 Used as both Oscillator and LFO.
+
 > audition (out 0 (lfSaw AR (lfSaw KR 4 0 * 400 + 400) 0 * 0.1))
 
 Output range is bi-polar.
+
 > let {f = mce [linLin (lfSaw KR 0.5 0) (-1) 1 200 1600, 200, 1600]
 >     ;a = mce [0.1,0.05,0.05]}
 > in audition (out 0 (mix (sinOsc AR f 0 * a)))
+
+saw-tooth wave as sum of sines.
+for all partials n, amplitude is (1 / n).
+phase is always 0.
+
+> let {mk_freq f0 n = f0 * fromInteger n
+>     ;mk_amp n = 1 / fromInteger n
+>     ;mk_param f0 n = let m = [1,2 .. n] in zip (map (mk_freq f0) m) (map mk_amp m)
+>     ;x = midiCPS (mouseX KR 20 72 Linear 0.2)
+>     ;y = mouseY KR 0.01 0.1 Exponential 0.2
+>     ;e = xLine KR 0.01 1 20 DoNothing
+>     ;o1 = sum (map (\(fr,am) -> sinOsc AR fr 0 * am) (mk_param x 25)) * (1 - e)
+>     ;o2 = lfSaw AR x 0 * e}
+> in audition (out 0 (mce2 o1 o2 * y))
diff --git a/Help/UGen/Oscillator/lfTri.help.lhs b/Help/UGen/Oscillator/lfTri.help.lhs
--- a/Help/UGen/Oscillator/lfTri.help.lhs
+++ b/Help/UGen/Oscillator/lfTri.help.lhs
@@ -6,8 +6,27 @@
 > audition (out 0 (lfTri AR 500 1 * 0.1))
 
 Used as both Oscillator and LFO.
+
 > audition (out 0 (lfTri AR (lfTri KR 4 0 * 400 + 400) 0 * 0.1))
 
 Multiple phases
+
 > let f = lfTri KR 0.4 (mce [0..3]) * 200 + 400
 > in audition (out 0 (mix (lfTri AR f 0 * 0.1)))
+
+triangle wave as sum of sines.
+for partial n, amplitude is (1 / square n) and phase is pi at every other odd partial
+
+> import Sound.SC3.UGen.Dot
+
+> let {mk_freq f0 n = f0 * fromInteger n
+>     ;mk_amp n = if even n then 0 else 1 / fromInteger (n * n)
+>     ;mk_ph n = if n + 1 `mod` 4 == 0 then pi else 0
+>     ;mk_param f0 n =
+>      let m = [1,3 .. n]
+>      in zip3 (map (mk_freq f0) m) (map mk_ph m) (map mk_amp m)
+>     ;x = midiCPS (mouseX KR 20 72 Linear 0.2)
+>     ;e = xLine KR 0.01 1 20 DoNothing
+>     ;o1 = sum (map (\(fr,ph,am) -> sinOsc AR fr ph * am) (mk_param x 25)) * (1 - e)
+>     ;o2 = lfTri AR x 0 * e}
+> in audition (out 0 (mce2 o1 o2 * 0.1))
diff --git a/Help/UGen/Oscillator/osc1.help.lhs b/Help/UGen/Oscillator/osc1.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/Oscillator/osc1.help.lhs
@@ -0,0 +1,9 @@
+> import Sound.SC3
+
+> withSC3 (let {z = [Normalise,Wavetable,Clear]
+>              ;a = [[13,8,55,34,5,21,3,1,2],[55,34,1,3,2,13,5,8,21]]
+>              ;f (b,l) = do {_ <- async (b_alloc b 512 1)
+>                            ;send (b_gen_sine1 b z (map recip l))}}
+>          in mapM_ f (zip [10,11] a))
+
+> audition (out 0 (lfSaw AR (mce2 110 164) 0 * 0.1 * osc1 AR (mce2 10 11) 4 RemoveSynth))
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
@@ -1,8 +1,7 @@
 > Sound.SC3.UGen.Help.viewSC3Help "PMOsc"
 > :t pmOsc
 
-# composite
-sinOsc r cf (sinOsc r mf mp * pm)
+# pmOsc is a composite of sinOsc, ie. sinOsc r cf (sinOsc r mf mp * pm)
 
 > import Sound.SC3.ID
 
@@ -18,5 +17,6 @@
 
 PM textures
 > import qualified Sound.SC3.Lang.Control.OverlapTexture as L
-> L.overlapTextureU (0,1,5,maxBound) (pmi 1)
+> L.overlapTextureU (0,1,8,maxBound) (pmi 1)
+> L.overlapTextureU (1,2,7,maxBound) (pmi 2)
 > L.overlapTextureU (6,6,6,maxBound) (pmi 12)
diff --git a/Help/UGen/Oscillator/pulse.help.lhs b/Help/UGen/Oscillator/pulse.help.lhs
--- a/Help/UGen/Oscillator/pulse.help.lhs
+++ b/Help/UGen/Oscillator/pulse.help.lhs
@@ -4,14 +4,17 @@
 > import Sound.SC3
 
 Modulate frequency
+
 > let f = xLine KR 40 4000 6 RemoveSynth
 > in audition (out 0 (pulse AR f 0.1 * 0.1))
 
 Modulate pulse width
+
 > let w = line KR 0.01 0.99 8 RemoveSynth
 > in audition (out 0 (pulse AR 200 w * 0.1))
 
 Two band limited square waves through a resonant low pass filter
+
 > let {p = pulse AR (mce2 100 250) 0.5 * 0.1
 >     ;f = xLine KR 8000 400 5 DoNothing}
 > in audition (out 0 (rlpf p f 0.05))
diff --git a/Help/UGen/Oscillator/sinOsc.help.lhs b/Help/UGen/Oscillator/sinOsc.help.lhs
--- a/Help/UGen/Oscillator/sinOsc.help.lhs
+++ b/Help/UGen/Oscillator/sinOsc.help.lhs
@@ -4,22 +4,44 @@
 > import Sound.SC3
 
 Fixed frequency
+
 > audition (out 0 (sinOsc AR 440 0 * 0.25))
 
 Modulate freq
+
 > audition (out 0 (sinOsc AR (xLine KR 2000 200 9 RemoveSynth) 0 * 0.5))
 
 Modulate freq
+
 > let f = sinOsc AR (xLine KR 1 1000 9 RemoveSynth) 0 * 200 + 800
 > in audition (out 0 (sinOsc AR f 0 * 0.1))
 
 Modulate phase
+
 > let p = sinOsc AR (xLine KR 20 8000 10 RemoveSynth) 0 * 2 * pi
 > in audition (out 0 (sinOsc AR 800 p * 0.1))
 
 Simple bell-like tone.
+
 > let {f = mce [0.5,1,1.19,1.56,2,2.51,2.66,3.01,4.1]
 >     ;a = mce [0.25,1,0.8,0.5,0.9,0.4,0.3,0.6,0.1]
 >     ;o = sinOsc AR (500 * f) 0 * a
 >     ;e = envGen KR 1 0.1 0 1 RemoveSynth (envPerc 0.01 10)}
 > in audition (out 0 (mix o * e))
+
+"When two pure tones of slightly different frequency are superposed,
+our ears perceive audible beats at a rate given by the difference of
+the two frequencies."
+
+> let {f0 = 220;f1 = 221.25;d = abs (f1 - f0)}
+> in audition (out 0 (sinOsc AR (mce2 f0 f1) 0 * 0.1 + impulse AR d 0 * 0.25))
+
+"When two tones are sounded together, a tone of lower frequency is
+frequently heard. Such a tone is called a combination tone.  The most
+commonly heard combination tone occurs at a frequency f2 - f1."
+
+> let {f1 = 300
+>     ;f2 = 300 * (3/2)
+>     ;f = mce2 (mce2 f1 f2) (abs (f2 - f1))
+>     ;a = mce2 0.1 (max (sinOsc KR 0.05 0 * 0.1) 0)}
+> in audition (out 0 (sinOsc AR f 0 * a))
diff --git a/Help/UGen/Oscillator/sinOscFB.help.lhs b/Help/UGen/Oscillator/sinOscFB.help.lhs
--- a/Help/UGen/Oscillator/sinOscFB.help.lhs
+++ b/Help/UGen/Oscillator/sinOscFB.help.lhs
@@ -4,6 +4,7 @@
 > 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)
@@ -11,6 +12,7 @@
 {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}
@@ -19,6 +21,7 @@
 {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}
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
@@ -1,10 +1,9 @@
 > Sound.SC3.UGen.Help.viewSC3Help "TChoose"
 > :t tChoose
 
-# composite
-tChoose is a composite of tIRand and select.
+# tChoose is a composite of tIRand and select.
 
-> import Sound.SC3.ID
+> import Sound.SC3
 
 > let {x = mouseX KR 1 1000 Exponential 0.1
 >     ;t = dust 'a' AR x
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
@@ -1,17 +1,19 @@
 > Sound.SC3.UGen.Help.viewSC3Help "TGrains"
 > Sound.SC3.UGen.DB.ugenSummary "TGrains"
 
-> import Sound.SC3.ID
+> import Sound.SC3
 
-Load audio data
+Load audio (#10) data
+
 > let fn = "/home/rohan/data/audio/pf-c5.aif"
 > 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
 >     ;tr = impulse AR tRate 0}
-> in audition (out 0 (tGrains 2 tr 10 1 ctr (4 / tRate) 0 0.1 2))
+> in audition (out 0 (tGrains 2 tr 10 1 ctr (4 / tRate) 0 0.25 2))
 
 > let {b = 10
 >     ;rt = mouseY KR 8 120 Exponential 0.1
@@ -21,7 +23,7 @@
 >     ;pan = whiteNoise 'γ' KR * 0.6
 >     ;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))
+> in audition (out 0 (tGrains 2 clk b 1 pos dur pan 0.25 2))
 
 > let {b = 10
 >     ;rt = mouseY KR 2 120 Exponential 0.1
@@ -33,7 +35,8 @@
 >     ;rate = shiftLeft 1.2 (roundTo (n0 * 3) 1)}
 > in audition (out 0 (tGrains 2 clk b rate pos dur (n1 * 0.6) 0.25 2))
 
-Demand UGens as inputs (will eventually hang scsynth...)
+Demand UGens as inputs (may eventually hang scsynth?)
+
 > let {b = 10
 >     ;rt = mouseY KR 2 100 Linear 0.2
 >     ;d e = dwhite e 1 0.1 0.2
diff --git a/Help/UGen/Oscillator/tWindex.help.lhs b/Help/UGen/Oscillator/tWindex.help.lhs
--- a/Help/UGen/Oscillator/tWindex.help.lhs
+++ b/Help/UGen/Oscillator/tWindex.help.lhs
@@ -6,12 +6,13 @@
 > let {p = mce [1/5, 2/5, 2/5]
 >     ;a = mce [400, 500, 600]
 >     ;t = impulse KR 6 0
->     ;i = tWindex 'a' t 0 p}
+>     ;i = tWindex 'α' t 0 p}
 > in audition (out 0 (sinOsc AR (select i  a) 0 * 0.1))
 
 Modulating probability values
+
 > let {p = mce [1/4, 1/2, sinOsc KR 0.3 0 * 0.5 + 0.5]
 >     ;a = mce [400, 500, 600]
 >     ;t = impulse KR 6 0
->     ;i = tWindex 'a' t 1 p}
+>     ;i = tWindex 'α' t 1 p}
 > in audition (out 0 (sinOsc AR (select i a) 0 * 0.1))
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
@@ -1,7 +1,6 @@
 > Sound.SC3.UGen.Help.viewSC3Help "TWChoose"
 
-# composite
-tWChoose is a composite of tWindex and select
+# tWChoose is a composite of tWindex and select
 
 > import Sound.SC3.ID
 
diff --git a/Help/UGen/Oscillator/varSaw.help.lhs b/Help/UGen/Oscillator/varSaw.help.lhs
--- a/Help/UGen/Oscillator/varSaw.help.lhs
+++ b/Help/UGen/Oscillator/varSaw.help.lhs
@@ -8,6 +8,20 @@
 > in audition (out 0 (varSaw AR f 0 w * 0.1))
 
 Compare with lfPulse at AR
+
 > let f = lfPulse KR 3 0 0.3 * 200 + 200
 > in audition (out 0 (mce [varSaw AR f 0 0.2
 >                         ,lfPulse AR f 0 0.2] * 0.1))
+
+per-note width modulation
+
+> let {d = linLin (lfNoise2 'α' KR 0.1) (-1) 1 0.15 0.5
+>     ;t = impulse AR (1 / d) 0
+>     ;w0 = tRand 'β' 0 0.35 t
+>     ;w1 = tRand 'γ' 0.65 1 t
+>     ;w = phasor AR t ((w1 - w0) * sampleDur) w0 w1 0
+>     ;e = decay2 t 0.1 d
+>     ;f = midiCPS (tRand 'δ' 36 72 t)
+>     ;o = varSaw AR f 0 w * e * 0.1
+>     ;l = tRand 'ε' (-1) 1 t}
+> in audition (out 0 (pan2 o l 1))
diff --git a/Help/UGen/Oscillator/vibrato.help.lhs b/Help/UGen/Oscillator/vibrato.help.lhs
--- a/Help/UGen/Oscillator/vibrato.help.lhs
+++ b/Help/UGen/Oscillator/vibrato.help.lhs
@@ -4,12 +4,16 @@
 > 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))
 
@@ -24,11 +28,13 @@
 > 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
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
@@ -3,9 +3,10 @@
 
 splay is a composite UGen.
 
-> import Sound.SC3.ID
+> import Sound.SC3
 
 mouse control
+
 > let {i = 6
 >     ;r = map (\e -> rand e 10 20) (take i ['a'..])
 >     ;n = lfNoise2 'a' KR (mce r)
@@ -17,6 +18,7 @@
 > in audition (out 0 (splay o y 0.2 x True))
 
 n_set control
+
 > let {i = 10
 >     ;s = control KR "spread" 1
 >     ;l = control KR "level" 0.2
@@ -28,16 +30,21 @@
 > in audition (out 0 (splay (sinOsc AR n 0) s l c True))
 
 full stereo
+
 > withSC3 (send (n_set (-1) [("spread",1),("center",0)]))
 
 less wide
+
 > withSC3 (send (n_set (-1) [("spread",0.5),("center",0)]))
 
 mono center
+
 > withSC3 (send (n_set (-1) [("spread",0),("center",0)]))
 
 from center to right
+
 > withSC3 (send (n_set (-1) [("spread",0.5),("center",0.5)]))
 
 all left
+
 > withSC3 (send (n_set (-1) [("spread",0),("center",-1)]))
diff --git a/Help/UGen/Trigger/gate.help.lhs b/Help/UGen/Trigger/gate.help.lhs
--- a/Help/UGen/Trigger/gate.help.lhs
+++ b/Help/UGen/Trigger/gate.help.lhs
@@ -1,8 +1,6 @@
 > Sound.SC3.UGen.Help.viewSC3Help "Gate"
 > Sound.SC3.UGen.DB.ugenSummary "Gate"
 
-# hsc3: filter
-
 > import Sound.SC3
 
 > let t = lfPulse AR 1 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,9 +4,48 @@
 > import Sound.SC3
 
 phasor controls sine frequency, end frequency matches second sine.
+
 > 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
 >     ;f = mce [linLin x 0 1 600 1000, 1000]}
 > in audition (out 0 (sinOsc AR f 0 * 0.2))
+
+Load sound file to buffer zero
+
+> let fn = "/home/rohan/data/audio/pf-c5.aif"
+> in withSC3 (async (b_allocRead 0 fn 0 0))
+
+Phasor as phase input to bufRd
+
+> let ph = phasor AR 0 (bufRateScale KR 0) 0 (bufFrames KR 0) 0
+> in audition (out 0 (bufRdN 1 AR 0 ph Loop))
+
+Allocate and generate (non-wavetable) buffer at index one
+(see osc for wavetable oscillator)
+
+> withSC3 (do {_ <- async (b_alloc 1 256 1)
+>             ;let f = [Normalise,Clear]
+>              in send (b_gen_sine1 1 f [1])})
+
+Audio rate phasor oscillator as phase input to bufRd
+
+> let {b = 1
+>     ;f = 440
+>     ;fr = bufFrames KR b
+>     ;rt = f * (fr / sampleRate)
+>     ;ph = phasor AR b (rt * bufRateScale KR b) 0 fr 0}
+> in audition (out 0 (bufRdL 1 AR b ph Loop * 0.1))
+
+Phasor as impulse with reset
+
+> let {impulse_reset freq reset =
+>      let ph = phasor AR reset (freq / sampleRate) 0 1 0
+>      in hpz1 ph <* 0
+>     ;x = mouseX KR 0 1 Linear 0.2 >* 0.5
+>     ;ck = impulse AR 3 0
+>     ;im = impulse_reset 3 x
+>     ;x' = sinOsc AR 440 0 * x * 0.05
+>     ;im' = sinOsc AR 220 0 * decay2 (ck + im) 0.01 0.5 * 0.1}
+> in audition (out 0 (mce2 x' im'))
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,13 +1,14 @@
 > Sound.SC3.UGen.Help.viewSC3Help "PulseCount"
 > Sound.SC3.UGen.DB.ugenSummary "PulseCount"
 
-> import Sound.SC3.ID
+> import Sound.SC3.ID {- hsc3 -}
 
 > 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
+printer
+
+> let {b = localBuf 'α' 11 1
 >     ;t = impulse AR 10 0
 >     ;p = pulseCount t 0
 >     ;d = demand t 0 (dbufwr 'α' (-666) b p NoLoop)}
diff --git a/Help/UGen/Trigger/setResetFF.help.lhs b/Help/UGen/Trigger/setResetFF.help.lhs
--- a/Help/UGen/Trigger/setResetFF.help.lhs
+++ b/Help/UGen/Trigger/setResetFF.help.lhs
@@ -4,7 +4,18 @@
 > import Sound.SC3.ID
 
 d0 is the set trigger, d1 the reset trigger
+
 > let {n = brownNoise 'α' AR
->     ;d0 = dust 'α' AR 5
->     ;d1 = dust 'β' AR 5}
+>     ;d0 = dust 'β' AR 5
+>     ;d1 = dust 'γ' AR 5}
 > in audition (out 0 (setResetFF d0 d1 * n * 0.2))
+
+silence
+
+> let tr = setResetFF (impulse KR 5 0) (impulse KR 10 0)
+> in audition (out 0 (brownNoise 'α' AR * 0.1 * decay2 tr 0.01 0.05))
+
+duty cycle
+
+> let tr = 1 - setResetFF (impulse KR 10 0) (impulse KR 5 0)
+> in audition (out 0 (brownNoise 'α' AR * 0.1 * decay2 tr 0.01 0.05))
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,23 +4,27 @@
 > import Sound.SC3.ID
 
 Using sweep to modulate sine frequency
+
 > 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 (send (b_allocRead 0 fn 0 0))
 
 Using sweep to index into a buffer
+
 > 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
+
+> let {n = lfNoise0 'α' KR 15
 >     ;x = mouseX KR 0.5 10 Exponential 0.1
 >     ;t = impulse AR x 0
 >     ;r = bufSampleRate KR 0
@@ -28,6 +32,7 @@
 > in audition (out 0 (bufRdL 1 AR 0 p NoLoop))
 
 Raising rate
+
 > let {x = mouseX KR 0.5 10 Exponential 0.1
 >     ;t = impulse AR x 0
 >     ;r = sweep t 2 + 0.5
@@ -35,7 +40,8 @@
 > 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)
+
+> let {lf = range 0.01 1.25 (lfNoise2 'α' KR 1)
 >     ;du = duty AR lf 0 DoNothing lf
 >     ;tr = abs (hpz1 du) >* 0
 >     ;ph = sweep tr (1/du)
@@ -45,6 +51,7 @@
 
 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
diff --git a/Help/UGen/Trigger/trig.help.lhs b/Help/UGen/Trigger/trig.help.lhs
--- a/Help/UGen/Trigger/trig.help.lhs
+++ b/Help/UGen/Trigger/trig.help.lhs
@@ -3,6 +3,6 @@
 
 > import Sound.SC3.ID
 
-> let {d = dust 'a' AR 1
+> let {d = dust 'α' AR 1
 >     ;o = fSinOsc AR 800 0 * 0.5}
 > in audition (out 0 (trig d 0.2 * o))
diff --git a/Help/UGen/Trigger/trig1.help.lhs b/Help/UGen/Trigger/trig1.help.lhs
--- a/Help/UGen/Trigger/trig1.help.lhs
+++ b/Help/UGen/Trigger/trig1.help.lhs
@@ -3,5 +3,6 @@
 
 > import Sound.SC3.ID
 
-> let d = dust 'a' AR 1
-> in audition (out 0 (trig1 d 0.2 * fSinOsc AR 800 0 * 0.2))
+> let {d = dust 'α' AR 1
+>     ;o = fSinOsc AR 800 0 * 0.2}
+> in audition (out 0 (trig1 d 0.2 * o))
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
@@ -3,8 +3,8 @@
 
 > import Sound.SC3.ID
 
-> let {i = whiteNoise 'a' AR * 0.05
->     ;b = mrg2 (localBuf 'α' 1024 1) (maxLocalBufs 1)
+> let {i = whiteNoise 'α' AR * 0.05
+>     ;b = localBuf 'β' 1024 1
 >     ;c = dwt b i 0.5 0 1 0 0}
 > in audition (out 0 (mce2 (idwt c 0 0 0) i))
 
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
@@ -4,7 +4,7 @@
 > import Sound.SC3.ID
 
 > let {i = whiteNoise 'α' AR * 0.2
->     ;b = mrg2 (localBuf 'α' 2048 1) (maxLocalBufs 1)
+>     ;b = localBuf 'β' 2048 1
 >     ;c = dwt b i 0.5 0 1 0 0
 >     ;x = mouseX KR (-1) 1 Linear 0.1
 >     ;c' = wt_FilterScale c x}
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
@@ -4,7 +4,7 @@
 > import Sound.SC3.ID
 
 > let {i = whiteNoise 'α' AR * 0.2
->     ;b = mrg2 (localBuf 'α' 2048 1) (maxLocalBufs 1)
+>     ;b = localBuf 'β' 2048 1
 >     ;c = dwt b i 0.5 0 1 0 0
 >     ;x = mouseX KR 0 1 Linear 0.1
 >     ;c' = wt_TimeWipe c x}
diff --git a/README b/README
--- a/README
+++ b/README
@@ -1,11 +1,11 @@
 hsc3 - haskell supercollider
 ----------------------------
 
-[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).
+[hsc3][hsc3] provides `Sound.SC3`, a module for 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
+For installation and configuration information see the
 [tutorial][tutorial] at [hsc3-texts][hsc3-texts].
 
 There are a number of related projects:
@@ -17,11 +17,13 @@
 - [hsc3-rec](?t=hsc3-rec) & [hsc3-unsafe](?t=hsc3-rec): UGen Variants
 - [hsc3-db](?t=hsc3-db): UGen Database
 - [hsc3-rw](?t=hsc3-rw): UGen Graph Re-writing
+- [hsc3-forth](?t=hsc3-forth): FORTH SuperCollider
+- [hsc3-lisp](?t=hsc3-lisp): LISP SuperCollider
 
 The hsc3 interaction environment is written for [GNU][gnu]
 [Emacs][emacs].
 
-© [rohan drape][rd] and others, 2006-2013, [gpl][gpl].
+© [rohan drape][rd] and others, 2005-2014, [gpl][gpl].
 with contributions by:
 
 - henning thielemann
@@ -30,16 +32,18 @@
 - brent yorgey
 - shae erisson
 
-see the [darcs][darcs] [history][hsc3-history] for details
+see the [darcs][darcs] [history](?t=hsc3&q=history) for details
 
+initial announcement:
+[[haskell.org](http://www.haskell.org/pipermail/haskell-cafe/2005-November/012483.html)]
+
 [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
+[tutorial]: http://rd.slavepianos.org/?t=hsc3-texts&e=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,5 +1,8 @@
--- | Composite of "Sound.SC3.Server.Monad" and "Sound.SC3.UGen".
+-- | Composite of "Sound.SC3.Server.Monad", "Sound.SC3.UGen" and "Sound.SC3.UGen.Bindings".
 module Sound.SC3 (module M) where
 
 import Sound.SC3.Server.Monad as M
 import Sound.SC3.UGen as M
+
+import Sound.SC3.UGen.Bindings as M
+
diff --git a/Sound/SC3/Common.hs b/Sound/SC3/Common.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/Common.hs
@@ -0,0 +1,79 @@
+module Sound.SC3.Common where
+
+import Data.Char {- base -}
+import Data.List {- base -}
+
+-- | Variant of 'reads' requiring exact match.
+reads_exact :: Read a => String -> Maybe a
+reads_exact s =
+    case reads s of
+      [(r,"")] -> Just r
+      _ -> Nothing
+
+-- * STRING / CASE
+
+-- | CI = Case insensitive, CS = case sensitive.
+data Case_Rule = CI | CS deriving (Eq)
+
+-- | Predicates for 'Case_Rule'.
+is_ci,is_cs :: Case_Rule -> Bool
+is_ci = (==) CI
+is_cs = (==) CS
+
+-- | String equality with 'Case_Rule'.
+--
+-- > string_eq CI "lower" "LOWER" == True
+string_eq :: Case_Rule -> String -> String -> Bool
+string_eq cr x y = if is_ci cr then map toLower x == map toLower y else x == y
+
+-- | 'rlookup_by' of 'string_eq'.
+rlookup_str :: Case_Rule -> String -> [(a,String)] -> Maybe a
+rlookup_str = rlookup_by . string_eq
+
+-- | 'Enum' parser with 'Case_Rule'.
+--
+-- > parse_enum CI "FALSE" == Just False
+parse_enum :: (Show t,Enum t,Bounded t) => Case_Rule -> String -> Maybe t
+parse_enum cr nm =
+    let u = [minBound .. maxBound]
+        t = zip (map show u) u
+    in lookup_by (string_eq cr) nm t
+
+-- * LIST
+
+-- | 'lookup' with equality function.
+lookup_by :: (a -> a -> Bool) -> a -> [(a,b)] -> Maybe b
+lookup_by f x = fmap snd . find (f x . fst)
+
+-- | Reverse 'lookup' with equality function.
+rlookup_by :: (b -> b -> Bool) -> b -> [(a,b)] -> Maybe a
+rlookup_by f x = fmap fst . find (f x . snd)
+
+-- | (prev,cur,next) triples.
+--
+-- > pcn_triples [1..3] == [(Nothing,1,Just 2),(Just 1,2,Just 3),(Just 2,3,Nothing)]
+pcn_triples :: [a] -> [(Maybe a,a,Maybe a)]
+pcn_triples =
+    let f e l = case l of
+                  e1 : e2 : l' -> (e,e1,Just e2) : f (Just e1) (e2 : l')
+                  [e'] -> [(e,e',Nothing)]
+                  [] -> undefined
+    in f Nothing
+
+-- * TUPLES
+
+type T2 a = (a,a)
+type T3 a = (a,a,a)
+type T4 a = (a,a,a,a)
+
+-- | 'concatMap' of /f/ at /x/ and /g/ at /y/.
+mk_duples :: (a -> c) -> (b -> c) -> [(a, b)] -> [c]
+mk_duples a b = concatMap (\(x,y) -> [a x, b y])
+
+-- | Length prefixed list variant of 'mk_duples'.
+mk_duples_l :: (Int -> c) -> (a -> c) -> (b -> c) -> [(a,[b])] -> [c]
+mk_duples_l i a b = concatMap (\(x,y) -> a x : i (length y) : map b y)
+
+-- | 'concatMap' of /f/ at /x/ and /g/ at /y/ and /h/ at /z/.
+mk_triples :: (a -> d) -> (b -> d) -> (c -> d) -> [(a, b, c)] -> [d]
+mk_triples a b c = concatMap (\(x,y,z) -> [a x, b y, c z])
diff --git a/Sound/SC3/Common/Monad/Syntax.hs b/Sound/SC3/Common/Monad/Syntax.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/Common/Monad/Syntax.hs
@@ -0,0 +1,106 @@
+-- | Functions to make writing 'Applicative' and 'Monad' UGen graphs
+-- less clumsy.
+module Sound.SC3.Common.Monad.Syntax where
+
+import Control.Applicative {- base -}
+import Control.Monad {- base -}
+
+infixl 7  .*,*.,.*.
+infixl 6  .+,+.,.+.
+
+infixl 7  ./,/.,./.
+infixl 6  .-,-.,.-.
+
+-- | '+' variant with 'Functor' at left.
+--
+-- > fmap (== 5) (return 3 .+ 2)
+-- > [3,4] .+ 2 == [5,6]
+(.+) :: (Functor f, Num a) => f a -> a -> f a
+m .+ n = fmap (+ n) m
+
+-- | '+' variant with 'Functor' at right.
+--
+-- > fmap (== 5) (3 +. return 2)
+-- > 3 +. [2,3] == [5,6]
+(+.) :: (Functor f, Num a) => a -> f a -> f a
+m +. n = fmap (+ m) n
+
+-- | '+' variant with 'Applicative' at left and right.
+--
+-- > fmap (== 5) (return 3 .+. return 2)
+-- > [3,4] .+. [2,3] == [5,6,6,7]
+-- > getZipList (ZipList [3,4] .+. ZipList [2,3]) == [5,7]
+(.+.) :: (Applicative m, Num a) => m a -> m a -> m a
+(.+.) = liftA2 (+)
+
+-- | '*' variant with 'Functor' at left.
+--
+-- > fmap (== 6) (return 3 .* 2)
+(.*) :: (Functor f, Num a) => f a -> a -> f a
+m .* n = fmap (* n) m
+
+-- | '*' variant with 'Functor' at right.
+--
+-- > fmap (== 6) (3 *. return 2)
+(*.) :: (Functor f, Num a) => a -> f a -> f a
+m *. n = fmap (* m) n
+
+-- | '*' variant with 'Applicative' at left and right.
+--
+-- > fmap (== 6) (return 3 .*. return 2)
+(.*.) :: (Applicative m, Num a) => m a -> m a -> m a
+(.*.) = liftA2 (*)
+
+-- | '-' variant with 'Functor' at left.
+--
+-- > fmap (== 1) (return 3 .- 2)
+-- > [3,4] .- 2 == [1,2]
+(.-) :: (Functor f, Num a) => f a -> a -> f a
+m .- n = fmap (subtract n) m
+
+-- | '-' variant with 'Functor' at right.
+--
+-- > fmap (== 1) (3 -. return 2)
+-- > 3 -. [2,3] == [1,0]
+(-.) :: (Functor f, Num a) => a -> f a -> f a
+m -. n = fmap (m -) n
+
+-- | '-' variant with 'Applicative' at left and right.
+--
+-- > fmap (== 1) (return 3 .-. return 2)
+-- > [3,4] .-. [2,3] == [1,0,2,1]
+-- > getZipList (ZipList [3,4] .-. ZipList [2,3]) == [1,1]
+(.-.) :: (Applicative m, Num a) => m a -> m a -> m a
+(.-.) = liftA2 (-)
+
+-- | '/' variant with 'Functor' at left.
+--
+-- > fmap (== 3) (return 6 ./ 2)
+(./) :: (Functor f,Fractional a) => f a -> a -> f a
+m ./ n = fmap (/ n) m
+
+-- | '/' variant with 'Functor' at right.
+--
+-- > fmap (== 3) (6 /. return 2)
+(/.) :: (Functor f,Fractional a) => a -> f a -> f a
+m /. n = fmap (m /) n
+
+-- | '/' variant with 'Applicative' at left and right.
+--
+-- > fmap (== 3) (return 6 ./. return 2)
+-- > [5,6] ./. [2,3] == [5/2,5/3,3,2]
+(./.) :: (Applicative m,Fractional a) => m a -> m a -> m a
+(./.) = liftA2 (/)
+
+-- | Right to left compositon of 'Monad' functions.
+--
+-- > fmap (== 7) (composeM [return . (+ 1),return . (/ 2)] 3)
+-- > fmap (== 8) (composeM [return . (* 2),return . (+ 1)] 3)
+composeM :: Monad m => [a -> m a] -> a -> m a
+composeM f = foldr (<=<) return f
+
+-- | Feed forward composition of /n/ applications of /f/.
+--
+-- > fmap (== 3) (chainM 3 (return . (+ 1)) 0)
+chainM :: Monad m => Int -> (b -> m b) -> b -> m b
+chainM n f = foldr (<=<) return (replicate n f)
diff --git a/Sound/SC3/FD.hs b/Sound/SC3/FD.hs
--- a/Sound/SC3/FD.hs
+++ b/Sound/SC3/FD.hs
@@ -1,5 +1,7 @@
--- | Composite of "Sound.SC3.Server.FD" and "Sound.SC3.UGen".
+-- | Composite of "Sound.SC3.Server.FD" and "Sound.SC3.UGen" and "Sound.SC3.UGen.Bindings".
 module Sound.SC3.FD (module M) where
 
 import Sound.SC3.Server.FD as M
 import Sound.SC3.UGen as M
+
+import Sound.SC3.UGen.Bindings as M
diff --git a/Sound/SC3/ID.hs b/Sound/SC3/ID.hs
deleted file mode 100644
--- a/Sound/SC3/ID.hs
+++ /dev/null
@@ -1,5 +0,0 @@
--- | Composite of "Sound.SC3.UGen.ID" and "Sound.SC3.Server.Monad".
-module Sound.SC3.ID (module M) where
-
-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
deleted file mode 100644
--- a/Sound/SC3/ID/FD.hs
+++ /dev/null
@@ -1,5 +0,0 @@
--- | 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/Monad.hs b/Sound/SC3/Monad.hs
deleted file mode 100644
--- a/Sound/SC3/Monad.hs
+++ /dev/null
@@ -1,6 +0,0 @@
--- | Composite of "Sound.SC3.UGen.Monad" and "Sound.SC3.Server.Monad"
-module Sound.SC3.Monad (module M) where
-
-import Sound.SC3.Monad.Syntax as M
-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
deleted file mode 100644
--- a/Sound/SC3/Monad/FD.hs
+++ /dev/null
@@ -1,5 +0,0 @@
--- | 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/Monad/Syntax.hs b/Sound/SC3/Monad/Syntax.hs
deleted file mode 100644
--- a/Sound/SC3/Monad/Syntax.hs
+++ /dev/null
@@ -1,58 +0,0 @@
--- | Functions to make writing 'Applicative' and 'Monad' UGen graphs
--- less clumsy.
-module Sound.SC3.Monad.Syntax where
-
-import Control.Applicative {- base -}
-import Control.Monad {- base -}
-
-infixl 7  .*,*.,.*.
-infixl 6  .+,+.,.+.
-
--- | '+' variant with 'Functor' at left.
---
--- > fmap (== 5) (return 3 .+ 2)
-(.+) :: (Functor f, Num a) => f a -> a -> f a
-m .+ n = fmap (+ n) m
-
--- | '+' variant with 'Functor' at right.
---
--- > fmap (== 5) (3 +. return 2)
-(+.) :: (Functor f, Num a) => a -> f a -> f a
-m +. n = fmap (+ m) n
-
--- | '+' variant with 'Applicative' at left and right.
---
--- > fmap (== 5) (return 3 .+. return 2)
-(.+.) :: (Applicative m, Num a) => m a -> m a -> m a
-(.+.) = liftA2 (+)
-
--- | '*' variant with 'Functor' at left.
---
--- > fmap (== 6) (return 3 .* 2)
-(.*) :: (Functor f, Num a) => f a -> a -> f a
-m .* n = fmap (* n) m
-
--- | '*' variant with 'Functor' at right.
---
--- > fmap (== 6) (3 *. return 2)
-(*.) :: (Functor f, Num a) => a -> f a -> f a
-m *. n = fmap (* m) n
-
--- | '*' variant with 'Applicative' at left and right.
---
--- > fmap (== 6) (return 3 .*. return 2)
-(.*.) :: (Applicative m, Num a) => m a -> m a -> m a
-(.*.) = liftA2 (*)
-
--- | Right to left compositon of 'Monad' functions.
---
--- > fmap (== 7) (composeM [return . (+ 1),return . (* 2)] 3)
--- > fmap (== 8) (composeM [return . (* 2),return . (+ 1)] 3)
-composeM :: Monad m => [a -> m a] -> a -> m a
-composeM f = foldr (<=<) return f
-
--- | Feed forward composition of /n/ applications of /f/.
---
--- > fmap (== 3) (chainM 3 (return . (+ 1)) 0)
-chainM :: Monad m => Int -> (b -> m b) -> b -> m b
-chainM n f = foldr (<=<) return (replicate n f)
diff --git a/Sound/SC3/Server.hs b/Sound/SC3/Server.hs
--- a/Sound/SC3/Server.hs
+++ b/Sound/SC3/Server.hs
@@ -3,11 +3,10 @@
 -- "Sound.SC3.Server.Monad".
 module Sound.SC3.Server (module S) where
 
-import Sound.SC3.Server.Command.Core as S
-import Sound.SC3.Server.Command.Int as S
-import Sound.SC3.Server.Command.Double as S
+import Sound.SC3.Server.Command as S
 import Sound.SC3.Server.Enum as S
 import Sound.SC3.Server.Synthdef as S
-import Sound.SC3.Server.Synthdef.Type as S
 import Sound.SC3.Server.Status as S
 import Sound.SC3.Server.NRT as S
+import Sound.SC3.Server.NRT.Edit as S
+import Sound.SC3.Server.Recorder as S
diff --git a/Sound/SC3/Server/Command.hs b/Sound/SC3/Server/Command.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/Server/Command.hs
@@ -0,0 +1,5 @@
+-- | Collection of standard /command/ modules.
+module Sound.SC3.Server.Command (module S) where
+
+import Sound.SC3.Server.Command.Enum as S
+import Sound.SC3.Server.Command.Plain as S
diff --git a/Sound/SC3/Server/Command/Core.hs b/Sound/SC3/Server/Command/Core.hs
deleted file mode 100644
--- a/Sound/SC3/Server/Command/Core.hs
+++ /dev/null
@@ -1,102 +0,0 @@
--- | Core non-type variant constructors.
-module Sound.SC3.Server.Command.Core where
-
-import Sound.OSC.Core {- hosc -}
-
-import Sound.SC3.Server.Enum
-import Sound.SC3.Server.Synthdef
-
--- * Instrument definition commands
-
--- | Install a bytecode instrument definition. (Asynchronous)
-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 -> Message
-d_load p = message "/d_load" [string p]
-
--- | Load a directory of instrument definitions files. (Asynchronous)
-d_loadDir :: String -> Message
-d_loadDir p = message "/d_loadDir" [string p]
-
--- | Remove definition once all nodes using it have ended.
-d_free :: [String] -> Message
-d_free = message "/d_free" . map string
-
--- * Plugin commands
-
--- | Send a plugin command.
-cmd :: String -> [Datum] -> Message
-cmd name = message "/cmd" . (string name :)
-
--- * Server operation commands
-
--- | Remove all bundles from the scheduling queue.
-clearSched :: Message
-clearSched = message "/clearSched" []
-
--- | Select printing of incoming Open Sound Control messages.
-dumpOSC :: PrintLevel -> Message
-dumpOSC c = message "/dumpOSC" [int32 (fromEnum c)]
-
--- | Select reception of notification messages. (Asynchronous)
-notify :: Bool -> Message
-notify c = message "/notify" [int32 (fromEnum c)]
-
--- | Stop synthesis server.
-quit :: Message
-quit = message "/quit" []
-
--- | Request \/status.reply message.
-status :: Message
-status = message "/status" []
-
--- | Set error posting scope and mode.
-errorMode :: ErrorScope -> ErrorMode -> Message
-errorMode scope mode =
-    let e = case scope of
-              Globally -> fromEnum mode
-              Locally  -> -1 - fromEnum mode
-    in message "/error" [int32 e]
-
--- * Modify existing message to include completion message
-
--- | List of asynchronous server commands.
-async_cmds :: [String]
-async_cmds =
-    ["/b_alloc"
-    ,"/b_allocRead"
-    ,"/b_allocReadChannel"
-    ,"/b_close"
-    ,"/b_free"
-    ,"/b_read"
-    ,"/b_readChannel"
-    ,"/b_write"
-    ,"/b_zero"
-    ,"/d_load"
-    ,"/d_loadDir"
-    ,"/d_recv"
-    ,"/notify"
-    ,"/quit"
-    ,"/sync"]
-
--- | '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 (or bundle, the name is misleading) to
--- an existing asynchronous command.
---
--- > let {m = n_set1 0 "0" 0
--- >     ;m' = encodeMessage m}
--- > in withCM (b_close 0) m == Message "/b_close" [Int 0,Blob m']
-withCM :: OSC o => Message -> o -> Message
-withCM (Message c xs) cm =
-    if c `elem` async_cmds
-    then let xs' = xs ++ [Blob (encodeOSC cm)]
-         in Message c xs'
-    else error ("withCM: not async: " ++ c)
-
diff --git a/Sound/SC3/Server/Command/Double.hs b/Sound/SC3/Server/Command/Double.hs
deleted file mode 100644
--- a/Sound/SC3/Server/Command/Double.hs
+++ /dev/null
@@ -1,85 +0,0 @@
--- | Functions from "Sound.SC3.Server.Command.Generic" specialised to 'Int' and 'Double'.
-module Sound.SC3.Server.Command.Double where
-
-import Sound.OSC.Core {- hosc -}
-
-import qualified Sound.SC3.Server.Command.Generic as G
-import Sound.SC3.Server.Enum
-import Sound.SC3.UGen.Enum
-
--- | Fill ranges of a node's control values.
-n_fill :: Int -> [(String,Int,Double)] -> Message
-n_fill = G.n_fill
-
--- | Set a node's control values.
-n_set :: Int -> [(String,Double)] -> Message
-n_set = G.n_set
-
--- | Set ranges of a node's control values.
-n_setn :: Int -> [(String,[Double])] -> Message
-n_setn = G.n_setn
-
--- | Create a new synth.
-s_new :: String -> Int -> AddAction -> Int -> [(String,Double)] -> Message
-s_new = G.s_new
-
--- | Fill ranges of sample values.
-b_fill :: Int -> [(Int,Int,Double)] -> Message
-b_fill = G.b_fill
-
--- | Call @sine1@ 'b_gen' command.
-b_gen_sine1 :: Int -> [B_Gen] -> [Double] -> Message
-b_gen_sine1 = G.b_gen_sine1
-
--- | Call @sine2@ 'b_gen' command.
-b_gen_sine2 :: Int -> [B_Gen] -> [(Double,Double)] -> Message
-b_gen_sine2 = G.b_gen_sine2
-
--- | Call @sine3@ 'b_gen' command.
-b_gen_sine3 :: Int -> [B_Gen] -> [(Double,Double,Double)] -> Message
-b_gen_sine3 = G.b_gen_sine3
-
--- | Call @cheby@ 'b_gen' command.
-b_gen_cheby :: Int -> [B_Gen] -> [Double] -> Message
-b_gen_cheby = G.b_gen_cheby
-
--- | Set sample values.
-b_set :: Int -> [(Int,Double)] -> Message
-b_set = G.b_set
-
--- | Set ranges of sample values.
-b_setn :: Int -> [(Int,[Double])] -> Message
-b_setn = G.b_setn
-
--- |  Fill ranges of bus values.
-c_fill :: [(Int,Int,Double)] -> Message
-c_fill = G.c_fill
-
--- | Set bus values.
-c_set :: [(Int,Double)] -> Message
-c_set = G.c_set
-
--- | Set ranges of bus values.
-c_setn :: [(Int,[Double])] -> Message
-c_setn = G.c_setn
-
--- | Pre-allocate for b_setn1, values preceding offset are zeroed.
-b_alloc_setn1 :: Int -> Int -> [Double] -> Message
-b_alloc_setn1 = G.b_alloc_setn1
-
--- | Set single sample value.
-b_set1 :: Int -> Int -> Double -> Message
-b_set1 = G.b_set1
-
--- | Set a range of sample values.
-b_setn1 :: Int -> Int -> [Double] -> Message
-b_setn1 = G.b_setn1
-
--- | Set single bus values.
-c_set1 :: Int -> Double -> Message
-c_set1 = G.c_set1
-
--- | Set a single node control value.
-n_set1 :: Int -> String -> Double -> Message
-n_set1 = G.n_set1
-
diff --git a/Sound/SC3/Server/Command/Enum.hs b/Sound/SC3/Server/Command/Enum.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/Server/Command/Enum.hs
@@ -0,0 +1,137 @@
+-- | Enumeration of SC3 server commands.
+module Sound.SC3.Server.Command.Enum where
+
+import Data.List {- base -}
+import Data.Maybe {- base -}
+import Sound.OSC.Type {- hosc -}
+
+-- | SC3 server commands are strings.
+type SC3_Command = String
+
+-- | Enumerate server command numbers.
+sc3_cmd_enumeration :: [(SC3_Command,Int)]
+sc3_cmd_enumeration =
+    [("/notify",1)
+    ,("/status",2)
+    ,("/quit",3)
+    ,("/cmd",4)
+    -- /d = synthdef
+    ,("/d_recv",5)
+    ,("/d_load",6)
+    ,("/d_loadDir",7)
+    ,("/d_freeAll",8)
+    -- /s = synth
+    ,("/s_new",9)
+    -- /n = node
+    ,("/n_trace",10)
+    ,("/n_free",11)
+    ,("/n_run",12)
+    ,("/n_cmd",13)
+    ,("/n_map",14)
+    ,("/n_set",15)
+    ,("/n_setn",16)
+    ,("/n_fill",17)
+    ,("/n_before",18)
+    ,("/n_after",19)
+    -- /u = ugen
+    ,("/u_cmd",20)
+    -- /g = group
+    ,("/g_new",21)
+    ,("/g_head",22)
+    ,("/g_tail",23)
+    ,("/g_freeAll",24)
+    -- /c = control
+    ,("/c_set",25)
+    ,("/c_setn",26)
+    ,("/c_fill",27)
+    -- /b = buffer
+    ,("/b_alloc",28)
+    ,("/b_allocRead",29)
+    ,("/b_read",30)
+    ,("/b_write",31)
+    ,("/b_free",32)
+    ,("/b_close",33)
+    ,("/b_zero",34)
+    ,("/b_set",35)
+    ,("/b_setn",36)
+    ,("/b_fill",37)
+    ,("/b_gen",38)
+    --
+    ,("/dumpOSC",39)
+    -- _get
+    ,("/c_get",40)
+    ,("/c_getn",41)
+    ,("/b_get",42)
+    ,("/b_getn",43)
+    ,("/s_get",44)
+    ,("/s_getn",45)
+    -- _query
+    ,("/n_query",46)
+    ,("/b_query",47)
+    --
+    ,("/n_mapn",48)
+    ,("/s_noid",49)
+    --
+    ,("/g_deepFree",50)
+    ,("/clearSched",51)
+    --
+    ,("/sync",52)
+    --
+    ,("/d_free",53)
+    -- _channel
+    ,("/b_allocReadChannel",54)
+    ,("/b_readChannel",55)
+    -- _tree
+    ,("/g_dumpTree",56)
+    ,("/g_queryTree",57)
+    -- error
+    ,("/error",58)
+    -- _args
+    ,("/s_newargs",59)
+    --
+    ,("/n_mapa",60)
+    ,("/n_mapan",61)
+    ,("/n_order",62)
+    ]
+
+-- | Lookup command number in 'sc3_cmd_enumeration'.
+--
+-- > map sc3_cmd_number ["/b_alloc","/s_new"] == [Just 28,Just 9]
+sc3_cmd_number :: SC3_Command -> Maybe Int
+sc3_cmd_number = flip lookup sc3_cmd_enumeration
+
+-- | 'isJust' of 'sc3_cmd_number'.
+known_sc3_cmd :: SC3_Command -> Bool
+known_sc3_cmd = isJust . sc3_cmd_number
+
+-- | List of asynchronous server commands.
+async_cmds :: [SC3_Command]
+async_cmds =
+    ["/b_alloc"
+    ,"/b_allocRead"
+    ,"/b_allocReadChannel"
+    ,"/b_close"
+    ,"/b_free"
+    ,"/b_read"
+    ,"/b_readChannel"
+    ,"/b_write"
+    ,"/b_zero"
+    ,"/d_load"
+    ,"/d_loadDir"
+    ,"/d_recv"
+    ,"/notify"
+    ,"/quit"
+    ,"/sync"]
+
+-- | '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
+
+-- | Asynchronous commands are at the left.  This function should
+-- preserve the ordering of both branches.
+--
+-- > partition_async [b_close 0,n_set1 0 "0" 0]
+partition_async :: [Message] -> ([Message],[Message])
+partition_async = partition isAsync
diff --git a/Sound/SC3/Server/Command/Float.hs b/Sound/SC3/Server/Command/Float.hs
deleted file mode 100644
--- a/Sound/SC3/Server/Command/Float.hs
+++ /dev/null
@@ -1,85 +0,0 @@
--- | Functions from "Sound.SC3.Server.Command.Generic" specialised to 'Int' and 'Float'.
-module Sound.SC3.Server.Command.Float where
-
-import Sound.OSC.Core {- hosc -}
-
-import qualified Sound.SC3.Server.Command.Generic as G
-import Sound.SC3.Server.Enum
-import Sound.SC3.UGen.Enum
-
--- | Fill ranges of a node's control values.
-n_fill :: Int -> [(String,Int,Float)] -> Message
-n_fill = G.n_fill
-
--- | Set a node's control values.
-n_set :: Int -> [(String,Float)] -> Message
-n_set = G.n_set
-
--- | Set ranges of a node's control values.
-n_setn :: Int -> [(String,[Float])] -> Message
-n_setn = G.n_setn
-
--- | Create a new synth.
-s_new :: String -> Int -> AddAction -> Int -> [(String,Float)] -> Message
-s_new = G.s_new
-
--- | Fill ranges of sample values.
-b_fill :: Int -> [(Int,Int,Float)] -> Message
-b_fill = G.b_fill
-
--- | Call @sine1@ 'b_gen' command.
-b_gen_sine1 :: Int -> [B_Gen] -> [Float] -> Message
-b_gen_sine1 = G.b_gen_sine1
-
--- | Call @sine2@ 'b_gen' command.
-b_gen_sine2 :: Int -> [B_Gen] -> [(Float,Float)] -> Message
-b_gen_sine2 = G.b_gen_sine2
-
--- | Call @sine3@ 'b_gen' command.
-b_gen_sine3 :: Int -> [B_Gen] -> [(Float,Float,Float)] -> Message
-b_gen_sine3 = G.b_gen_sine3
-
--- | Call @cheby@ 'b_gen' command.
-b_gen_cheby :: Int -> [B_Gen] -> [Float] -> Message
-b_gen_cheby = G.b_gen_cheby
-
--- | Set sample values.
-b_set :: Int -> [(Int,Float)] -> Message
-b_set = G.b_set
-
--- | Set ranges of sample values.
-b_setn :: Int -> [(Int,[Float])] -> Message
-b_setn = G.b_setn
-
--- |  Fill ranges of bus values.
-c_fill :: [(Int,Int,Float)] -> Message
-c_fill = G.c_fill
-
--- | Set bus values.
-c_set :: [(Int,Float)] -> Message
-c_set = G.c_set
-
--- | Set ranges of bus values.
-c_setn :: [(Int,[Float])] -> Message
-c_setn = G.c_setn
-
--- | Pre-allocate for b_setn1, values preceding offset are zeroed.
-b_alloc_setn1 :: Int -> Int -> [Float] -> Message
-b_alloc_setn1 = G.b_alloc_setn1
-
--- | Set single sample value.
-b_set1 :: Int -> Int -> Float -> Message
-b_set1 = G.b_set1
-
--- | Set a range of sample values.
-b_setn1 :: Int -> Int -> [Float] -> Message
-b_setn1 = G.b_setn1
-
--- | Set single bus values.
-c_set1 :: Int -> Float -> Message
-c_set1 = G.c_set1
-
--- | Set a single node control value.
-n_set1 :: Int -> String -> Float -> Message
-n_set1 = G.n_set1
-
diff --git a/Sound/SC3/Server/Command/Generic.hs b/Sound/SC3/Server/Command/Generic.hs
--- a/Sound/SC3/Server/Command/Generic.hs
+++ b/Sound/SC3/Server/Command/Generic.hs
@@ -5,153 +5,13 @@
 import Data.Maybe {- base -}
 import Sound.OSC.Core {- hosc -}
 
-import Sound.SC3.Server.Command.Core
+import Sound.SC3.Common
+import Sound.SC3.Server.Command.Enum
 import Sound.SC3.Server.Enum
-import Sound.SC3.Server.Utilities
-import Sound.SC3.UGen.Enum
-
--- * Node commands
-
--- | Place a node after another.
-n_after :: (Integral i) => [(i,i)] -> Message
-n_after = message "/n_after" . mk_duples int32 int32
-
--- | Place a node before another.
-n_before :: (Integral i) => [(i,i)] -> Message
-n_before = message "/n_before" . mk_duples int32 int32
-
--- | Fill ranges of a node's control values.
-n_fill :: (Integral i,Real n) => i -> [(String,i,n)] -> Message
-n_fill nid l = message "/n_fill" (int32 nid : mk_triples string int32 float l)
-
--- | Delete a node.
-n_free :: (Integral i) => [i] -> Message
-n_free = message "/n_free" . map int32
-
-n_map :: (Integral i) => i -> [(String,i)] -> Message
-n_map nid l = message "/n_map" (int32 nid : mk_duples string int32 l)
-
--- | Map a node's controls to read from buses.
-n_mapn :: (Integral i) => i -> [(String,i,i)] -> Message
-n_mapn nid l = message "/n_mapn" (int32 nid : mk_triples string int32 int32 l)
-
--- | Map a node's controls to read from an audio bus.
-n_mapa :: (Integral i) => i -> [(String,i)] -> Message
-n_mapa nid l = message "/n_mapa" (int32 nid : mk_duples string int32 l)
-
--- | Map a node's controls to read from audio buses.
-n_mapan :: (Integral i) => i -> [(String,i,i)] -> Message
-n_mapan nid l = message "/n_mapan" (int32 nid : mk_triples string int32 int32 l)
-
--- | Get info about a node.
-n_query :: (Integral i) => [i] -> Message
-n_query = message "/n_query" . map int32
-
--- | Turn node on or off.
-n_run :: (Integral i) => [(i,Bool)] -> Message
-n_run = message "/n_run" . mk_duples int32 (int32 . fromEnum)
-
--- | Set a node's control values.
-n_set :: (Integral i,Real n) => i -> [(String,n)] -> Message
-n_set nid c = message "/n_set" (int32 nid : mk_duples string float c)
-
--- | Set ranges of a node's control values.
-n_setn :: (Integral i,Real n) => i -> [(String,[n])] -> Message
-n_setn nid l =
-    let f (s,d) = string s : int32 (length d) : map float d
-    in message "/n_setn" (int32 nid : concatMap f l)
-
--- | Trace a node.
-n_trace :: (Integral i) => [i] -> Message
-n_trace = message "/n_trace" . map int32
-
--- | Move an ordered sequence of nodes.
-n_order :: (Integral i) => AddAction -> i -> [i] -> Message
-n_order a n ns = message "/n_order" (int32 (fromEnum a) : int32 n : map int32 ns)
-
--- * Synthesis node commands
-
--- | Get control values.
-s_get :: (Integral i) => i -> [String] -> Message
-s_get nid i = message "/s_get" (int32 nid : map string i)
-
--- | Get ranges of control values.
-s_getn :: (Integral i) => i -> [(String,i)] -> Message
-s_getn nid l = message "/s_getn" (int32 nid : mk_duples string int32 l)
-
--- | Create a new synth.
-s_new :: (Integral i,Real n) => String -> i -> AddAction -> i -> [(String,n)] -> Message
-s_new n i a t c = message "/s_new" (string n : int32 i : int32 (fromEnum a) : int32 t : mk_duples string float c)
-
--- | Auto-reassign synth's ID to a reserved value.
-s_noid :: (Integral i) => [i] -> Message
-s_noid = message "/s_noid" . map int32
-
--- * Group node commands
-
--- | Free all synths in this group and all its sub-groups.
-g_deepFree :: (Integral i) => [i] -> Message
-g_deepFree = message "/g_deepFree" . map int32
-
--- | Delete all nodes in a group.
-g_freeAll :: (Integral i) => [i] -> Message
-g_freeAll = message "/g_freeAll" . map int32
-
--- | Add node to head of group.
-g_head :: (Integral i) => [(i,i)] -> Message
-g_head = message "/g_head" . mk_duples int32 int32
-
--- | Create a new group.
-g_new :: (Integral i) => [(i,AddAction,i)] -> Message
-g_new = message "/g_new" . mk_triples int32 (int32 . fromEnum) int32
-
--- | Add node to tail of group.
-g_tail :: (Integral i) => [(i,i)] -> Message
-g_tail = message "/g_tail" . mk_duples int32 int32
-
--- | Post a representation of a group's node subtree, optionally including the current control values for synths.
-g_dumpTree :: (Integral i) => [(i,Bool)] -> Message
-g_dumpTree = message "/g_dumpTree" . mk_duples int32 (int32 . fromEnum)
-
--- | Request a representation of a group's node subtree, optionally including the current control values for synths.
---
--- Replies to the sender with a @/g_queryTree.reply@ message listing all of the nodes contained within the group in the following format:
---
--- > int32 - if synth control values are included 1, else 0
--- > int32 - node ID of the requested group
--- > int32 - number of child nodes contained within the requested group
--- >
--- > For each node in the subtree:
--- > [
--- >   int32 - node ID
--- >   int32 - number of child nodes contained within this node. If -1 this is a synth, if >= 0 it's a group.
--- >
--- >   If this node is a synth:
--- >     symbol - the SynthDef name for this node.
--- >
--- >   If flag (see above) is true:
--- >     int32 - numControls for this synth (M)
--- >     [
--- >       symbol or int: control name or index
--- >       float or symbol: value or control bus mapping symbol (e.g. 'c1')
--- >     ] * M
--- > ] * 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 :: (Integral i) => [(i,Bool)] -> Message
-g_queryTree = message "/g_queryTree" . mk_duples int32 (int32 . fromEnum)
-
--- | Create a new parallel group (supernova specific).
-p_new :: (Integral i) => [(i,AddAction,i)] -> Message
-p_new = message "/p_new" . mk_triples int32 (int32 . fromEnum) int32
-
--- * Unit Generator commands
-
--- | Send a command to a unit generator.
-u_cmd :: (Integral i) => i -> i -> String -> [Datum] -> Message
-u_cmd nid uid name arg = message "/u_cmd" ([int32 nid,int32 uid,string name] ++ arg)
+import qualified Sound.SC3.Server.Graphdef as G
+import Sound.SC3.Server.Synthdef
 
--- * Buffer commands
+-- * Buffer commands (b_)
 
 -- | Allocates zero filled buffer to number of channels and samples. (Asynchronous)
 b_alloc :: (Integral i) => i -> i -> i -> Message
@@ -241,7 +101,7 @@
 b_zero :: (Integral i) => i -> Message
 b_zero nid = message "/b_zero" [int32 nid]
 
--- * Control bus commands
+-- * Control bus commands (c_)
 
 -- |  Fill ranges of bus values.
 c_fill :: (Integral i,Real n) => [(i,i,n)] -> Message
@@ -265,12 +125,228 @@
     let f (i,d) = int32 i : int32 (length d) : map float d
     in message "/c_setn" (concatMap f l)
 
+-- * Instrument definition commands (d_)
+
+-- | Install a bytecode instrument definition. (Asynchronous)
+d_recv' :: G.Graphdef -> Message
+d_recv' g = message "/d_recv" [Blob (G.encode_graphdef g)]
+
+-- | Install a bytecode instrument definition. (Asynchronous)
+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 -> Message
+d_load p = message "/d_load" [string p]
+
+-- | Load a directory of instrument definitions files. (Asynchronous)
+d_loadDir :: String -> Message
+d_loadDir p = message "/d_loadDir" [string p]
+
+-- | Remove definition once all nodes using it have ended.
+d_free :: [String] -> Message
+d_free = message "/d_free" . map string
+
+-- * Group node commands (g_)
+
+-- | Free all synths in this group and all its sub-groups.
+g_deepFree :: (Integral i) => [i] -> Message
+g_deepFree = message "/g_deepFree" . map int32
+
+-- | Delete all nodes in a group.
+g_freeAll :: (Integral i) => [i] -> Message
+g_freeAll = message "/g_freeAll" . map int32
+
+-- | Add node to head of group.
+g_head :: (Integral i) => [(i,i)] -> Message
+g_head = message "/g_head" . mk_duples int32 int32
+
+-- | Create a new group.
+g_new :: (Integral i) => [(i,AddAction,i)] -> Message
+g_new = message "/g_new" . mk_triples int32 (int32 . fromEnum) int32
+
+-- | Add node to tail of group.
+g_tail :: (Integral i) => [(i,i)] -> Message
+g_tail = message "/g_tail" . mk_duples int32 int32
+
+-- | Post a representation of a group's node subtree, optionally including the current control values for synths.
+g_dumpTree :: (Integral i) => [(i,Bool)] -> Message
+g_dumpTree = message "/g_dumpTree" . mk_duples int32 (int32 . fromEnum)
+
+-- | Request a representation of a group's node subtree, optionally including the current control values for synths.
+--
+-- Replies to the sender with a @/g_queryTree.reply@ message listing all of the nodes contained within the group in the following format:
+--
+-- > int32 - if synth control values are included 1, else 0
+-- > int32 - node ID of the requested group
+-- > int32 - number of child nodes contained within the requested group
+-- >
+-- > For each node in the subtree:
+-- > [
+-- >   int32 - node ID
+-- >   int32 - number of child nodes contained within this node. If -1 this is a synth, if >= 0 it's a group.
+-- >
+-- >   If this node is a synth:
+-- >     symbol - the SynthDef name for this node.
+-- >
+-- >   If flag (see above) is true:
+-- >     int32 - numControls for this synth (M)
+-- >     [
+-- >       symbol or int: control name or index
+-- >       float or symbol: value or control bus mapping symbol (e.g. 'c1')
+-- >     ] * M
+-- > ] * 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 :: (Integral i) => [(i,Bool)] -> Message
+g_queryTree = message "/g_queryTree" . mk_duples int32 (int32 . fromEnum)
+
+-- * Node commands (n_)
+
+-- | Place a node after another.
+n_after :: (Integral i) => [(i,i)] -> Message
+n_after = message "/n_after" . mk_duples int32 int32
+
+-- | Place a node before another.
+n_before :: (Integral i) => [(i,i)] -> Message
+n_before = message "/n_before" . mk_duples int32 int32
+
+-- | Fill ranges of a node's control values.
+n_fill :: (Integral i,Real f) => i -> [(String,i,f)] -> Message
+n_fill nid l = message "/n_fill" (int32 nid : mk_triples string int32 float l)
+
+-- | Delete a node.
+n_free :: (Integral i) => [i] -> Message
+n_free = message "/n_free" . map int32
+
+n_map :: (Integral i) => i -> [(String,i)] -> Message
+n_map nid l = message "/n_map" (int32 nid : mk_duples string int32 l)
+
+-- | Map a node's controls to read from buses.
+n_mapn :: (Integral i) => i -> [(String,i,i)] -> Message
+n_mapn nid l = message "/n_mapn" (int32 nid : mk_triples string int32 int32 l)
+
+-- | Map a node's controls to read from an audio bus.
+n_mapa :: (Integral i) => i -> [(String,i)] -> Message
+n_mapa nid l = message "/n_mapa" (int32 nid : mk_duples string int32 l)
+
+-- | Map a node's controls to read from audio buses.
+n_mapan :: (Integral i) => i -> [(String,i,i)] -> Message
+n_mapan nid l = message "/n_mapan" (int32 nid : mk_triples string int32 int32 l)
+
+-- | Get info about a node.
+n_query :: (Integral i) => [i] -> Message
+n_query = message "/n_query" . map int32
+
+-- | Turn node on or off.
+n_run :: (Integral i) => [(i,Bool)] -> Message
+n_run = message "/n_run" . mk_duples int32 (int32 . fromEnum)
+
+-- | Set a node's control values.
+n_set :: (Integral i,Real n) => i -> [(String,n)] -> Message
+n_set nid c = message "/n_set" (int32 nid : mk_duples string float c)
+
+-- | Set ranges of a node's control values.
+n_setn :: (Integral i,Real n) => i -> [(String,[n])] -> Message
+n_setn nid l =
+    let f (s,d) = string s : int32 (length d) : map float d
+    in message "/n_setn" (int32 nid : concatMap f l)
+
+-- | Trace a node.
+n_trace :: (Integral i) => [i] -> Message
+n_trace = message "/n_trace" . map int32
+
+-- | Move an ordered sequence of nodes.
+n_order :: (Integral i) => AddAction -> i -> [i] -> Message
+n_order a n ns = message "/n_order" (int32 (fromEnum a) : int32 n : map int32 ns)
+
+-- * Par commands (p_)
+
+-- | Create a new parallel group (supernova specific).
+p_new :: (Integral i) => [(i,AddAction,i)] -> Message
+p_new = message "/p_new" . mk_triples int32 (int32 . fromEnum) int32
+
+-- * Synthesis node commands (s_)
+
+-- | Get control values.
+s_get :: (Integral i) => i -> [String] -> Message
+s_get nid i = message "/s_get" (int32 nid : map string i)
+
+-- | Get ranges of control values.
+s_getn :: (Integral i) => i -> [(String,i)] -> Message
+s_getn nid l = message "/s_getn" (int32 nid : mk_duples string int32 l)
+
+-- | Create a new synth.
+s_new :: (Integral i,Real n) => String -> i -> AddAction -> i -> [(String,n)] -> Message
+s_new n i a t c = message "/s_new" (string n : int32 i : int32 (fromEnum a) : int32 t : mk_duples string float c)
+
+-- | Auto-reassign synth's ID to a reserved value.
+s_noid :: (Integral i) => [i] -> Message
+s_noid = message "/s_noid" . map int32
+
+-- * UGen commands (u_)
+
+-- | Send a command to a unit generator.
+u_cmd :: (Integral i) => i -> i -> String -> [Datum] -> Message
+u_cmd nid uid name arg = message "/u_cmd" ([int32 nid,int32 uid,string name] ++ arg)
+
 -- * Server operation commands
 
+-- | Send a plugin command.
+cmd :: String -> [Datum] -> Message
+cmd name = message "/cmd" . (string name :)
+
+-- | Remove all bundles from the scheduling queue.
+clearSched :: Message
+clearSched = message "/clearSched" []
+
+-- | Select printing of incoming Open Sound Control messages.
+dumpOSC :: PrintLevel -> Message
+dumpOSC c = message "/dumpOSC" [int32 (fromEnum c)]
+
+-- | Set error posting scope and mode.
+errorMode :: ErrorScope -> ErrorMode -> Message
+errorMode scope mode =
+    let e = case scope of
+              Globally -> fromEnum mode
+              Locally  -> -1 - fromEnum mode
+    in message "/error" [int32 e]
+
+-- | Select reception of notification messages. (Asynchronous)
+notify :: Bool -> Message
+notify c = message "/notify" [int32 (fromEnum c)]
+
+-- | End real time mode, close file (un-implemented).
+nrt_end :: Message
+nrt_end = message "/nrt_end" []
+
+-- | Stop synthesis server.
+quit :: Message
+quit = message "/quit" []
+
+-- | Request \/status.reply message.
+status :: Message
+status = message "/status" []
+
 -- | Request \/synced message when all current asynchronous commands complete.
 sync :: (Integral i) => i -> Message
 sync sid = message "/sync" [int32 sid]
 
+-- * Modify existing message to include completion message
+
+-- | Add a completion message (or bundle, the name is misleading) to
+-- an existing asynchronous command.
+--
+-- > let {m = n_set1 0 "0" 0
+-- >     ;m' = encodeMessage m}
+-- > in withCM (b_close 0) m == Message "/b_close" [Int 0,Blob m']
+withCM :: OSC o => Message -> o -> Message
+withCM (Message c xs) cm =
+    if c `elem` async_cmds
+    then let xs' = xs ++ [Blob (encodeOSC cm)]
+         in Message c xs'
+    else error ("withCM: not async: " ++ c)
+
 -- * Variants to simplify common cases
 
 -- | Pre-allocate for b_setn1, values preceding offset are zeroed.
@@ -284,6 +360,10 @@
 b_getn1 :: (Integral i) => i -> (i,i) -> Message
 b_getn1 nid = b_getn nid . return
 
+-- | Variant on 'b_query'.
+b_query1 :: (Integral i) => i -> Message
+b_query1 = b_query . return
+
 -- | Set single sample value.
 b_set1 :: (Integral i,Real n) => i -> i -> n -> Message
 b_set1 nid i x = b_set nid [(i,x)]
@@ -292,13 +372,17 @@
 b_setn1 :: (Integral i,Real n) => i -> i -> [n] -> Message
 b_setn1 nid i xs = b_setn nid [(i,xs)]
 
--- | Variant on 'b_query'.
-b_query1 :: (Integral i) => i -> Message
-b_query1 = b_query . return
+-- | Get ranges of sample values.
+c_getn1 :: (Integral i) => (i,i) -> Message
+c_getn1 = c_getn . return
 
 -- | Set single bus values.
 c_set1 :: (Integral i,Real n) => i -> n -> Message
 c_set1 i x = c_set [(i,x)]
+
+-- | Set single range of bus values.
+c_setn1 :: (Integral i,Real n) => (i,[n]) -> Message
+c_setn1 = c_setn . return
 
 -- | Set a single node control value.
 n_set1 :: (Integral i,Real n) => i -> String -> n -> Message
diff --git a/Sound/SC3/Server/Command/Int.hs b/Sound/SC3/Server/Command/Int.hs
deleted file mode 100644
--- a/Sound/SC3/Server/Command/Int.hs
+++ /dev/null
@@ -1,245 +0,0 @@
--- | Functions from "Sound.SC3.Server.Command.Generic" specialised to 'Int'.
-module Sound.SC3.Server.Command.Int where
-
-import Sound.OSC.Core {- hosc -}
-
-import qualified Sound.SC3.Server.Command.Generic as G
-import Sound.SC3.Server.Enum
-
--- * Node commands
-
--- | Place a node after another.
-n_after :: [(Int,Int)] -> Message
-n_after = G.n_after
-
--- | Place a node before another.
-n_before :: [(Int,Int)] -> Message
-n_before = G.n_before
-
--- | Delete a node.
-n_free :: [Int] -> Message
-n_free = G.n_free
-
-n_map :: Int -> [(String,Int)] -> Message
-n_map = G.n_map
-
--- | Map a node's controls to read from buses.
-n_mapn :: Int -> [(String,Int,Int)] -> Message
-n_mapn = G.n_mapn
-
--- | Map a node's controls to read from an audio bus.
-n_mapa :: Int -> [(String,Int)] -> Message
-n_mapa = G.n_mapa
-
--- | Map a node's controls to read from audio buses.
-n_mapan :: Int -> [(String,Int,Int)] -> Message
-n_mapan = G.n_mapan
-
--- | Get info about a node.
-n_query :: [Int] -> Message
-n_query = G.n_query
-
--- | Turn node on or off.
-n_run :: [(Int,Bool)] -> Message
-n_run = G.n_run
-
--- | Trace a node.
-n_trace :: [Int] -> Message
-n_trace = G.n_trace
-
--- | Move an ordered sequence of nodes.
-n_order :: AddAction -> Int -> [Int] -> Message
-n_order = G.n_order
-
--- * Synthesis node commands
-
--- | Get control values.
-s_get :: Int -> [String] -> Message
-s_get = G.s_get
-
--- | Get ranges of control values.
-s_getn :: Int -> [(String,Int)] -> Message
-s_getn = G.s_getn
-
--- | Auto-reassign synth's ID to a reserved value.
-s_noid :: [Int] -> Message
-s_noid = G.s_noid
-
--- * Group node commands
-
--- | Free all synths in this group and all its sub-groups.
-g_deepFree :: [Int] -> Message
-g_deepFree = G.g_deepFree
-
--- | Delete all nodes in a group.
-g_freeAll :: [Int] -> Message
-g_freeAll = G.g_freeAll
-
--- | Add node to head of group.
-g_head :: [(Int,Int)] -> Message
-g_head = G.g_head
-
--- | Create a new group.
-g_new :: [(Int,AddAction,Int)] -> Message
-g_new = G.g_new
-
--- | Add node to tail of group.
-g_tail :: [(Int,Int)] -> Message
-g_tail = G.g_tail
-
--- | Post a representation of a group's node subtree, optionally including the current control values for synths.
-g_dumpTree :: [(Int,Bool)] -> Message
-g_dumpTree = G.g_dumpTree
-
--- | Request a representation of a group's node subtree, optionally including the current control values for synths.
---
--- Replies to the sender with a @/g_queryTree.reply@ message listing all of the nodes contained within the group in the following format:
---
--- > int32 - if synth control values are included 1, else 0
--- > int32 - node ID of the requested group
--- > int32 - number of child nodes contained within the requested group
--- >
--- > For each node in the subtree:
--- > [
--- >   int32 - node ID
--- >   int32 - number of child nodes contained within this node. If -1 this is a synth, if >= 0 it's a group.
--- >
--- >   If this node is a synth:
--- >     symbol - the SynthDef name for this node.
--- >
--- >   If flag (see above) is true:
--- >     int32 - numControls for this synth (M)
--- >     [
--- >       symbol or int: control name or index
--- >       float or symbol: value or control bus mapping symbol (e.g. 'c1')
--- >     ] * M
--- > ] * 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)] -> Message
-g_queryTree = G.g_queryTree
-
--- | Create a new parallel group (supernova specific).
-p_new :: [(Int,AddAction,Int)] -> Message
-p_new = G.p_new
-
--- * Unit Generator commands
-
--- | Send a command to a unit generator.
-u_cmd :: Int -> Int -> String -> [Datum] -> Message
-u_cmd = G.u_cmd
-
--- * Buffer commands
-
--- | Allocates zero filled buffer to number of channels and samples. (Asynchronous)
-b_alloc :: Int -> Int -> Int -> Message
-b_alloc = G.b_alloc
-
--- | Allocate buffer space and read a sound file. (Asynchronous)
-b_allocRead :: Int -> String -> Int -> Int -> Message
-b_allocRead = G.b_allocRead
-
--- | Allocate buffer space and read a sound file, picking specific channels. (Asynchronous)
-b_allocReadChannel :: Int -> String -> Int -> Int -> [Int] -> Message
-b_allocReadChannel = G.b_allocReadChannel
-
--- | Close attached soundfile and write header information. (Asynchronous)
-b_close :: Int -> Message
-b_close = G.b_close
-
--- | Free buffer data. (Asynchronous)
-b_free :: Int -> Message
-b_free = G.b_free
-
--- | Call a command to fill a buffer.  (Asynchronous)
-b_gen :: Int -> String -> [Datum] -> Message
-b_gen = G.b_gen
-
--- | Call @copy@ 'b_gen' command.
-b_gen_copy :: Int -> Int -> Int -> Int -> Maybe Int -> Message
-b_gen_copy = G.b_gen_copy
-
--- | Get sample values.
-b_get :: Int -> [Int] -> Message
-b_get = G.b_get
-
--- | Get ranges of sample values.
-b_getn :: Int -> [(Int,Int)] -> Message
-b_getn = G.b_getn
-
--- | Request \/b_info messages.
-b_query :: [Int] -> Message
-b_query = G.b_query
-
--- | Read sound file data into an existing buffer. (Asynchronous)
-b_read :: Int -> String -> Int -> Int -> Int -> Bool -> Message
-b_read = G.b_read
-
--- | Read sound file data into an existing buffer, picking specific channels. (Asynchronous)
-b_readChannel :: Int -> String -> Int -> Int -> Int -> Bool -> [Int] -> Message
-b_readChannel = G.b_readChannel
-
--- | Write sound file data. (Asynchronous)
-b_write :: Int -> String -> SoundFileFormat -> SampleFormat -> Int -> Int -> Bool -> Message
-b_write = G.b_write
-
--- | Zero sample data. (Asynchronous)
-b_zero :: Int -> Message
-b_zero = G.b_zero
-
--- * Control bus commands
-
--- | Get bus values.
-c_get :: [Int] -> Message
-c_get = G.c_get
-
--- | Get ranges of bus values.
-c_getn :: [(Int,Int)] -> Message
-c_getn = G.c_getn
-
--- * Server operation commands
-
--- | Request \/synced message when all current asynchronous commands
--- complete.
-sync :: Int -> Message
-sync = G.sync
-
--- * Variants to simplify common cases
-
--- | Get ranges of sample values.
-b_getn1 :: Int -> (Int,Int) -> Message
-b_getn1 = G.b_getn1
-
--- | Variant on 'b_query'.
-b_query1 :: Int -> Message
-b_query1 = b_query . return
-
--- | @s_new@ with no parameters.
-s_new0 :: String -> Int -> AddAction -> Int -> Message
-s_new0 = G.s_new0
-
--- * 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 = G.b_segment
-
--- | 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 = G.b_indices
-
--- * UGen commands.
-
--- | Generate accumulation buffer given time-domain IR buffer and FFT size.
-pc_preparePartConv :: Int -> Int -> Int -> Message
-pc_preparePartConv = G.pc_preparePartConv
-
--- Local Variables:
--- truncate-lines:t
--- End:
diff --git a/Sound/SC3/Server/Command/Plain.hs b/Sound/SC3/Server/Command/Plain.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/Server/Command/Plain.hs
@@ -0,0 +1,362 @@
+-- | Functions from "Sound.SC3.Server.Command.Generic" specialised to 'Int' and 'Double'.
+module Sound.SC3.Server.Command.Plain where
+
+import Sound.OSC.Core {- hosc -}
+
+import qualified Sound.SC3.Server.Command.Generic as G
+import Sound.SC3.Server.Enum
+import qualified Sound.SC3.Server.Graphdef as G
+import Sound.SC3.Server.Synthdef
+
+-- * Buffer commands (b_)
+
+-- | Allocates zero filled buffer to number of channels and samples. (Asynchronous)
+b_alloc :: Int -> Int -> Int -> Message
+b_alloc = G.b_alloc
+
+-- | Allocate buffer space and read a sound file. (Asynchronous)
+b_allocRead :: Int -> String -> Int -> Int -> Message
+b_allocRead = G.b_allocRead
+
+-- | Allocate buffer space and read a sound file, picking specific channels. (Asynchronous)
+b_allocReadChannel :: Int -> String -> Int -> Int -> [Int] -> Message
+b_allocReadChannel = G.b_allocReadChannel
+
+-- | Close attached soundfile and write header information. (Asynchronous)
+b_close :: Int -> Message
+b_close = G.b_close
+
+-- | Free buffer data. (Asynchronous)
+b_free :: Int -> Message
+b_free = G.b_free
+
+-- | Call a command to fill a buffer.  (Asynchronous)
+b_gen :: Int -> String -> [Datum] -> Message
+b_gen = G.b_gen
+
+-- | Call @copy@ 'b_gen' command.
+b_gen_copy :: Int -> Int -> Int -> Int -> Maybe Int -> Message
+b_gen_copy = G.b_gen_copy
+
+-- | Get sample values.
+b_get :: Int -> [Int] -> Message
+b_get = G.b_get
+
+-- | Get ranges of sample values.
+b_getn :: Int -> [(Int,Int)] -> Message
+b_getn = G.b_getn
+
+-- | Request \/b_info messages.
+b_query :: [Int] -> Message
+b_query = G.b_query
+
+-- | Read sound file data into an existing buffer. (Asynchronous)
+b_read :: Int -> String -> Int -> Int -> Int -> Bool -> Message
+b_read = G.b_read
+
+-- | Read sound file data into an existing buffer, picking specific channels. (Asynchronous)
+b_readChannel :: Int -> String -> Int -> Int -> Int -> Bool -> [Int] -> Message
+b_readChannel = G.b_readChannel
+
+-- | Write sound file data. (Asynchronous)
+b_write :: Int -> String -> SoundFileFormat -> SampleFormat -> Int -> Int -> Bool -> Message
+b_write = G.b_write
+
+-- | Zero sample data. (Asynchronous)
+b_zero :: Int -> Message
+b_zero = G.b_zero
+
+-- * Control bus commands
+
+-- |  Fill ranges of bus values.
+c_fill :: [(Int,Int,Double)] -> Message
+c_fill = G.c_fill
+
+-- | Get bus values.
+c_get :: [Int] -> Message
+c_get = G.c_get
+
+-- | Get ranges of bus values.
+c_getn :: [(Int,Int)] -> Message
+c_getn = G.c_getn
+
+-- | Set bus values.
+c_set :: [(Int,Double)] -> Message
+c_set = G.c_set
+
+-- | Set ranges of bus values.
+c_setn :: [(Int,[Double])] -> Message
+c_setn = G.c_setn
+
+-- * Instrument definition commands (d_)
+
+-- | Install a bytecode instrument definition. (Asynchronous)
+d_recv' :: G.Graphdef -> Message
+d_recv' = G.d_recv'
+
+-- | Install a bytecode instrument definition. (Asynchronous)
+d_recv :: Synthdef -> Message
+d_recv = G.d_recv
+
+-- | Load an instrument definition from a named file. (Asynchronous)
+d_load :: String -> Message
+d_load = G.d_load
+
+-- | Load a directory of instrument definitions files. (Asynchronous)
+d_loadDir :: String -> Message
+d_loadDir = G.d_loadDir
+
+-- | Remove definition once all nodes using it have ended.
+d_free :: [String] -> Message
+d_free = G.d_free
+
+-- * Group node commands (g_)
+
+-- | Free all synths in this group and all its sub-groups.
+g_deepFree :: [Int] -> Message
+g_deepFree = G.g_deepFree
+
+-- | Delete all nodes in a group.
+g_freeAll :: [Int] -> Message
+g_freeAll = G.g_freeAll
+
+-- | Add node to head of group.
+g_head :: [(Int,Int)] -> Message
+g_head = G.g_head
+
+-- | Create a new group.
+g_new :: [(Int,AddAction,Int)] -> Message
+g_new = G.g_new
+
+-- | Add node to tail of group.
+g_tail :: [(Int,Int)] -> Message
+g_tail = G.g_tail
+
+-- | Post a representation of a group's node subtree, optionally including the current control values for synths.
+g_dumpTree :: [(Int,Bool)] -> Message
+g_dumpTree = G.g_dumpTree
+
+-- | Request a representation of a group's node subtree, optionally including the current control values for synths.
+g_queryTree :: [(Int,Bool)] -> Message
+g_queryTree = G.g_queryTree
+
+-- * Node commands (n_)
+
+-- | Place a node after another.
+n_after :: [(Int,Int)] -> Message
+n_after = G.n_after
+
+-- | Place a node before another.
+n_before :: [(Int,Int)] -> Message
+n_before = G.n_before
+
+-- | Fill ranges of a node's control values.
+n_fill :: Int -> [(String,Int,Double)] -> Message
+n_fill = G.n_fill
+
+-- | Delete a node.
+n_free :: [Int] -> Message
+n_free = G.n_free
+
+n_map :: Int -> [(String,Int)] -> Message
+n_map = G.n_map
+
+-- | Map a node's controls to read from buses.
+n_mapn :: Int -> [(String,Int,Int)] -> Message
+n_mapn = G.n_mapn
+
+-- | Map a node's controls to read from an audio bus.
+n_mapa :: Int -> [(String,Int)] -> Message
+n_mapa = G.n_mapa
+
+-- | Map a node's controls to read from audio buses.
+n_mapan :: Int -> [(String,Int,Int)] -> Message
+n_mapan = G.n_mapan
+
+-- | Get info about a node.
+n_query :: [Int] -> Message
+n_query = G.n_query
+
+-- | Turn node on or off.
+n_run :: [(Int,Bool)] -> Message
+n_run = G.n_run
+
+-- | Set a node's control values.
+n_set :: Int -> [(String,Double)] -> Message
+n_set = G.n_set
+
+-- | Set ranges of a node's control values.
+n_setn :: Int -> [(String,[Double])] -> Message
+n_setn = G.n_setn
+
+-- | Trace a node.
+n_trace :: [Int] -> Message
+n_trace = G.n_trace
+
+-- | Move an ordered sequence of nodes.
+n_order :: AddAction -> Int -> [Int] -> Message
+n_order = G.n_order
+
+-- * Par commands (p_)
+
+-- | Create a new parallel group (supernova specific).
+p_new :: [(Int,AddAction,Int)] -> Message
+p_new = G.p_new
+
+-- * Synthesis node commands (s_)
+
+-- | Get control values.
+s_get :: Int -> [String] -> Message
+s_get = G.s_get
+
+-- | Get ranges of control values.
+s_getn :: Int -> [(String,Int)] -> Message
+s_getn = G.s_getn
+
+-- | Auto-reassign synth's ID to a reserved value.
+s_noid :: [Int] -> Message
+s_noid = G.s_noid
+
+-- * Unit Generator commands (u_)
+
+-- | Send a command to a unit generator.
+u_cmd :: Int -> Int -> String -> [Datum] -> Message
+u_cmd = G.u_cmd
+
+-- * Server operation commands
+
+-- | Send a plugin command.
+cmd :: String -> [Datum] -> Message
+cmd = G.cmd
+
+-- | Remove all bundles from the scheduling queue.
+clearSched :: Message
+clearSched = G.clearSched
+
+-- | Select printing of incoming Open Sound Control messages.
+dumpOSC :: PrintLevel -> Message
+dumpOSC = G.dumpOSC
+
+-- | Set error posting scope and mode.
+errorMode :: ErrorScope -> ErrorMode -> Message
+errorMode = G.errorMode
+
+-- | Select reception of notification messages. (Asynchronous)
+notify :: Bool -> Message
+notify = G.notify
+
+-- | End real time mode, close file (un-implemented).
+nrt_end :: Message
+nrt_end = G.nrt_end
+
+-- | Stop synthesis server.
+quit :: Message
+quit = G.quit
+
+-- | Request \/status.reply message.
+status :: Message
+status = G.status
+
+-- | Request \/synced message when all current asynchronous commands complete.
+sync :: Int -> Message
+sync = G.sync
+
+-- * Variants to simplify common cases
+
+-- | Get ranges of sample values.
+b_getn1 :: Int -> (Int,Int) -> Message
+b_getn1 = G.b_getn1
+
+-- | Variant on 'b_query'.
+b_query1 :: Int -> Message
+b_query1 = b_query . return
+
+-- | Get ranges of sample values.
+c_getn1 :: (Int,Int) -> Message
+c_getn1 = G.c_getn1
+
+-- | Set single bus values.
+c_set1 :: Int -> Double -> Message
+c_set1 = G.c_set1
+
+-- | Set single range of bus values.
+c_setn1 :: (Int,[Double]) -> Message
+c_setn1 = G.c_setn1
+
+-- | Set a single node control value.
+n_set1 :: Int -> String -> Double -> Message
+n_set1 = G.n_set1
+
+-- | @s_new@ with no parameters.
+s_new0 :: String -> Int -> AddAction -> Int -> Message
+s_new0 = G.s_new0
+
+-- * 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 = G.b_segment
+
+-- | 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 = G.b_indices
+
+-- | Create a new synth.
+s_new :: String -> Int -> AddAction -> Int -> [(String,Double)] -> Message
+s_new = G.s_new
+
+-- | Fill ranges of sample values.
+b_fill :: Int -> [(Int,Int,Double)] -> Message
+b_fill = G.b_fill
+
+-- | Call @sine1@ 'b_gen' command.
+b_gen_sine1 :: Int -> [B_Gen] -> [Double] -> Message
+b_gen_sine1 = G.b_gen_sine1
+
+-- | Call @sine2@ 'b_gen' command.
+b_gen_sine2 :: Int -> [B_Gen] -> [(Double,Double)] -> Message
+b_gen_sine2 = G.b_gen_sine2
+
+-- | Call @sine3@ 'b_gen' command.
+b_gen_sine3 :: Int -> [B_Gen] -> [(Double,Double,Double)] -> Message
+b_gen_sine3 = G.b_gen_sine3
+
+-- | Call @cheby@ 'b_gen' command.
+b_gen_cheby :: Int -> [B_Gen] -> [Double] -> Message
+b_gen_cheby = G.b_gen_cheby
+
+-- | Set sample values.
+b_set :: Int -> [(Int,Double)] -> Message
+b_set = G.b_set
+
+-- | Set ranges of sample values.
+b_setn :: Int -> [(Int,[Double])] -> Message
+b_setn = G.b_setn
+
+-- | Pre-allocate for b_setn1, values preceding offset are zeroed.
+b_alloc_setn1 :: Int -> Int -> [Double] -> Message
+b_alloc_setn1 = G.b_alloc_setn1
+
+-- | Set single sample value.
+b_set1 :: Int -> Int -> Double -> Message
+b_set1 = G.b_set1
+
+-- | Set a range of sample values.
+b_setn1 :: Int -> Int -> [Double] -> Message
+b_setn1 = G.b_setn1
+
+-- * UGen commands.
+
+-- | Generate accumulation buffer given time-domain IR buffer and FFT size.
+pc_preparePartConv :: Int -> Int -> Int -> Message
+pc_preparePartConv = G.pc_preparePartConv
+
+-- Local Variables:
+-- truncate-lines:t
+-- End:
diff --git a/Sound/SC3/Server/Enum.hs b/Sound/SC3/Server/Enum.hs
--- a/Sound/SC3/Server/Enum.hs
+++ b/Sound/SC3/Server/Enum.hs
@@ -9,6 +9,22 @@
                | AddReplace
                  deriving (Eq,Show,Enum)
 
+-- | 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)
+
 -- | Error posting scope.
 data ErrorScope = Globally  -- ^ Global scope
                 | Locally   -- ^ Bundle scope
@@ -37,6 +53,7 @@
   | PcmMulaw | PcmAlaw
   deriving (Enum, Eq, Read, Show)
 
+-- | Sample format to standard file extension name.
 soundFileFormatString :: SoundFileFormat -> String
 soundFileFormatString f =
     case f of
@@ -46,6 +63,18 @@
       Next -> "next"
       Raw -> "raw"
       Wave -> "wav"
+
+-- | Infer sample format from file extension name.
+soundFileFormat_from_extension :: String -> Maybe SoundFileFormat
+soundFileFormat_from_extension =
+    let tbl = [("aif",Aiff)
+              ,("aiff",Aiff)
+              ,("flac",Flac)
+              ,("ircam",Ircam)
+              ,("next",Next)
+              ,("raw",Raw)
+              ,("wav",Wave)]
+    in flip lookup tbl
 
 sampleFormatString :: SampleFormat -> String
 sampleFormatString f =
diff --git a/Sound/SC3/Server/Graphdef.hs b/Sound/SC3/Server/Graphdef.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/Server/Graphdef.hs
@@ -0,0 +1,215 @@
+-- | Binary 'Graph Definition' as understood by @scsynth@.
+module Sound.SC3.Server.Graphdef where
+
+import Control.Monad {- base -}
+import qualified Data.ByteString.Lazy as L {- bytestring -}
+import qualified Data.ByteString.Char8 as C {- bytestring -}
+import Data.List
+import System.IO {- base -}
+
+import Sound.OSC.Coding.Byte {- hosc -}
+import Sound.OSC.Coding.Cast {- hosc -}
+import Sound.OSC.Type {- hosc -}
+
+-- * Type
+
+type Name = ASCII
+
+type Control = (Name,Int)
+
+type Sample = Double
+
+data Input = Input Int Int deriving (Eq,Show)
+
+input_ugen_ix :: Input -> Maybe Int
+input_ugen_ix (Input u p) = if p == -1 then Nothing else Just u
+
+type Output = Int
+
+type Rate = Int
+
+type Special = Int
+
+type UGen = (Name,Rate,[Input],[Output],Special)
+
+ugen_inputs :: UGen -> [Input]
+ugen_inputs (_,_,i,_,_) = i
+
+ugen_outputs :: UGen -> [Output]
+ugen_outputs (_,_,_,o,_) = o
+
+ugen_is_control :: UGen -> Bool
+ugen_is_control (nm,_,_,_,_) = ascii_to_string nm `elem` ["Control","LagControl","TrigControl"]
+
+ugen_rate :: UGen -> Rate
+ugen_rate (_,r,_,_,_) = r
+
+input_is_control :: Graphdef -> Input -> Bool
+input_is_control g (Input u _) =
+    if u == -1
+    then False
+    else ugen_is_control (graphdef_ugen g u)
+
+data Graphdef = Graphdef {graphdef_name :: Name
+                         ,graphdef_constants :: [Sample]
+                         ,graphdef_controls :: [(Control,Sample)]
+                         ,graphdef_ugens :: [UGen]}
+                deriving (Eq,Show)
+
+graphdef_ugen :: Graphdef -> Int -> UGen
+graphdef_ugen g = (graphdef_ugens g !!)
+
+graphdef_control :: Graphdef -> Int -> (Control,Sample)
+graphdef_control g = (graphdef_controls g !!)
+
+graphdef_constant_nid :: Graphdef -> Int -> Int
+graphdef_constant_nid _ = id
+
+graphdef_control_nid :: Graphdef -> Int -> Int
+graphdef_control_nid g = (+) (length (graphdef_constants g))
+
+graphdef_ugen_nid :: Graphdef -> Int -> Int
+graphdef_ugen_nid g n = graphdef_control_nid g 0 + length (graphdef_controls g) + n
+
+-- * Read
+
+read_i8 :: Handle -> IO Int
+read_i8 h = fmap decode_i8 (L.hGet h 1)
+
+read_i16 :: Handle -> IO Int
+read_i16 h = fmap decode_i16 (L.hGet h 2)
+
+read_i32 :: Handle -> IO Int
+read_i32 h = fmap decode_i32 (L.hGet h 4)
+
+read_sample :: Handle -> IO Sample
+read_sample h = fmap (realToFrac . decode_f32) (L.hGet h 4)
+
+read_pstr :: Handle -> IO ASCII
+read_pstr h = do
+  n <- fmap decode_u8 (L.hGet h 1)
+  fmap decode_str (L.hGet h n)
+
+read_control :: Handle -> IO Control
+read_control h = do
+  nm <- read_pstr h
+  ix <- read_i16 h
+  return (nm,ix)
+
+read_input :: Handle -> IO Input
+read_input h = do
+  u <- read_i16 h
+  p <- read_i16 h
+  return (Input u p)
+
+read_output :: Handle -> IO Int
+read_output = read_i8
+
+read_ugen :: Handle -> IO UGen
+read_ugen h = do
+  name <- read_pstr h
+  rate <- read_i8 h
+  number_of_inputs <- read_i16 h
+  number_of_outputs <- read_i16 h
+  special <- read_i16 h
+  inputs <- replicateM number_of_inputs (read_input h)
+  outputs <- replicateM number_of_outputs (read_output h)
+  return (name
+         ,rate
+         ,inputs
+         ,outputs
+         ,special)
+
+read_graphdef :: Handle -> IO Graphdef
+read_graphdef h = do
+  magic <- L.hGet h 4
+  version <- read_i32 h
+  number_of_definitions <- read_i16 h
+  when (magic /= L.pack (map (fromIntegral . fromEnum) "SCgf"))
+       (error "read_graphdef: illegal magic string")
+  when (version /= 0)
+       (error "read_graphdef: version not at zero")
+  when (number_of_definitions /= 1)
+       (error "read_graphdef: non unary graphdef file")
+  name <- read_pstr h
+  number_of_constants <- read_i16 h
+  constants <- replicateM number_of_constants (read_sample h)
+  number_of_control_defaults <- read_i16 h
+  control_defaults <- replicateM number_of_control_defaults (read_sample h)
+  number_of_controls <- read_i16 h
+  controls <- replicateM number_of_controls (read_control h)
+  number_of_ugens <- read_i16 h
+  ugens <- replicateM number_of_ugens (read_ugen h)
+  return (Graphdef name
+                   constants
+                   (zip controls control_defaults)
+                   ugens)
+
+-- > g <- read_graphdef_file "/home/rohan/sw/rsc3-disassembler/scsyndef/simple.scsyndef"
+-- > g <- read_graphdef_file "/home/rohan/sw/rsc3-disassembler/scsyndef/with-ctl.scsyndef"
+-- > g <- read_graphdef_file "/home/rohan/sw/rsc3-disassembler/scsyndef/mce.scsyndef"
+-- > g <- read_graphdef_file "/home/rohan/sw/rsc3-disassembler/scsyndef/mrg.scsyndef"
+read_graphdef_file :: FilePath -> IO Graphdef
+read_graphdef_file nm = do
+  h <- openFile nm ReadMode
+  g <- read_graphdef h
+  hClose h
+  return g
+
+-- * Encode
+
+-- | Pascal (length prefixed) encoding of string.
+encode_pstr :: ASCII -> L.ByteString
+encode_pstr = L.pack . str_pstr . ascii_to_string
+
+-- | Byte-encode 'Input' value.
+encode_input :: Input -> L.ByteString
+encode_input (Input u p) = L.append (encode_i16 u) (encode_i16 p)
+
+encode_control :: Control -> L.ByteString
+encode_control (nm,k) = L.concat [encode_pstr nm,encode_i16 k]
+
+-- | Byte-encode 'UGen'.
+encode_ugen :: UGen -> L.ByteString
+encode_ugen (nm,r,i,o,s) =
+    L.concat [encode_pstr nm
+             ,encode_i8 r
+             ,encode_i16 (length i)
+             ,encode_i16 (length o)
+             ,encode_i16 s
+             ,L.concat (map encode_input i)
+             ,L.concat (map encode_i8 o)]
+
+encode_sample :: Sample -> L.ByteString
+encode_sample = encode_f32 . realToFrac
+
+encode_graphdef :: Graphdef -> L.ByteString
+encode_graphdef (Graphdef nm cs ks us) =
+    let (ks_ctl,ks_def) = unzip ks
+    in L.concat [encode_str (C.pack "SCgf")
+                ,encode_i32 0 -- version
+                ,encode_i16 1 -- number of graphs
+                ,encode_pstr nm
+                ,encode_i16 (length cs)
+                ,L.concat (map encode_sample cs)
+                ,encode_i16 (length ks_def)
+                ,L.concat (map encode_sample ks_def)
+                ,encode_i16 (length ks_ctl)
+                ,L.concat (map encode_control ks_ctl)
+                ,encode_i16 (length us)
+                ,L.concat (map encode_ugen us)]
+
+-- * Stat
+
+graphdef_stat :: Graphdef -> String
+graphdef_stat (Graphdef _ cs ks us) =
+    let u_nm (sc3_nm,_,_,_,_) = ascii_to_string sc3_nm
+        f g = let h (x:xs) = (x,length (x:xs))
+                  h [] = error "graphdef_stat"
+              in show . map h . group . sort . map g
+        sq = intercalate "," (map u_nm us)
+    in unlines ["number of constants       : " ++ show (length cs)
+               ,"number of controls        : " ++ show (length ks)
+               ,"number of unit generators : " ++ show (length us)
+               ,"unit generator rates      : " ++ f ugen_rate us
+               ,"unit generator sequence   : " ++ sq]
diff --git a/Sound/SC3/Server/Graphdef/Graph.hs b/Sound/SC3/Server/Graphdef/Graph.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/Server/Graphdef/Graph.hs
@@ -0,0 +1,48 @@
+-- | Transform 'Graph' to 'Graphdef'.
+module Sound.SC3.Server.Graphdef.Graph where
+
+import Data.Maybe{- base -}
+
+import Sound.OSC.Type {- hosc -}
+
+import qualified Sound.SC3.Server.Graphdef as G
+import Sound.SC3.UGen.Graph
+import Sound.SC3.UGen.Rate
+import Sound.SC3.UGen.Type
+
+-- * Encode to 'G.Graphdef'
+
+-- | Construct 'Input' form required by byte-code generator.
+make_input :: Maps -> FromPort -> G.Input
+make_input (cs,ks,_,us,kt) fp =
+    case fp of
+      FromPort_C n -> G.Input (-1) (fetch n cs)
+      FromPort_K n t -> let i = ktype_map_lookup t kt
+                        in G.Input i (fetch_k n t ks)
+      FromPort_U n p -> G.Input (fetch n us) (fromMaybe 0 p)
+
+node_k_to_control :: Maps -> Node -> G.Control
+node_k_to_control (_,_,ks,_,_) nd =
+    case nd of
+      NodeK n _ _ nm _ _ _ -> (ascii nm,fetch n ks)
+      _ -> error "node_k_to_control"
+
+-- | Byte-encode 'NodeU' primitive node.
+node_u_to_ugen :: Maps -> Node -> G.UGen
+node_u_to_ugen m n =
+    case n of
+      NodeU _ r nm i o (Special s) _ ->
+          let i' = map (make_input m) i
+          in (ascii nm,rateId r,i',map rateId o,s)
+      _ -> error "encode_node_u: illegal input"
+
+-- | Construct instrument definition bytecode.
+graph_to_graphdef :: String -> Graph -> G.Graphdef
+graph_to_graphdef nm g =
+    let Graph _ cs ks us = g
+        cs' = map node_c_value cs
+        mm = mk_maps g
+        ks_def = map node_k_default ks
+        ks_ctl = map (node_k_to_control mm) ks
+        us' = map (node_u_to_ugen mm) us
+    in G.Graphdef (ascii nm) cs' (zip ks_ctl ks_def) us'
diff --git a/Sound/SC3/Server/Graphdef/Read.hs b/Sound/SC3/Server/Graphdef/Read.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/Server/Graphdef/Read.hs
@@ -0,0 +1,48 @@
+-- | Transform (read) a 'Graphdef' into a 'Graph'.
+module Sound.SC3.Server.Graphdef.Read where
+
+import Sound.OSC.Type
+import Sound.SC3.Server.Graphdef
+import qualified Sound.SC3.UGen.Graph as G
+import qualified Sound.SC3.UGen.Rate as R
+import qualified Sound.SC3.UGen.Type as U
+
+mk_node_k :: Graphdef -> G.NodeId -> (Control,U.Sample) -> G.Node
+mk_node_k g z ((nm,ix),v) =
+    let z' = graphdef_control_nid g z
+        nm' = ascii_to_string nm
+    in G.NodeK z' R.KR (Just ix) nm' v G.K_KR Nothing
+
+input_to_from_port :: Graphdef -> Input -> G.FromPort
+input_to_from_port g (Input u p) =
+    if u == -1
+    then G.FromPort_C (graphdef_constant_nid g p)
+    else if input_is_control g (Input u p)
+         then if u /= 0
+              then error "multiple control UGens..."
+              else G.FromPort_K (graphdef_control_nid g p) G.K_KR
+         else let ugen = graphdef_ugens g !! u
+                  port = if length (ugen_outputs ugen) > 1
+                         then Just p
+                         else Nothing
+              in G.FromPort_U (graphdef_ugen_nid g u) port
+
+mk_node_u :: Graphdef -> G.NodeId -> UGen -> G.Node
+mk_node_u g z u =
+    let (name,rate,inputs,outputs,special) = u
+        z' = graphdef_ugen_nid g z
+        rate' = toEnum rate
+        name' = ascii_to_string name
+        inputs' = map (input_to_from_port g) inputs
+        outputs' = map toEnum outputs
+        special' = U.Special special
+    in G.NodeU z' rate' name' inputs' outputs' special' (U.UId z')
+
+graphdef_to_graph :: Graphdef -> (String,G.Graph)
+graphdef_to_graph g =
+    let constants_nd = zipWith G.NodeC [0..] (graphdef_constants g)
+        controls_nd = zipWith (mk_node_k g) [0 ..] (graphdef_controls g)
+        ugens_nd = zipWith (mk_node_u g) [0 ..] (graphdef_ugens g)
+        nm = ascii_to_string (graphdef_name g)
+        gr = G.Graph (-1) constants_nd controls_nd ugens_nd
+    in (nm,gr) -- S.Synthdef nm gr
diff --git a/Sound/SC3/Server/Help.hs b/Sound/SC3/Server/Help.hs
--- a/Sound/SC3/Server/Help.hs
+++ b/Sound/SC3/Server/Help.hs
@@ -2,8 +2,8 @@
 module Sound.SC3.Server.Help where
 
 import Control.Monad {- base -}
-import System.Cmd {- process -}
 import System.FilePath {- filepath -}
+import System.Process {- process -}
 
 import Sound.SC3.UGen.Help
 
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,11 +1,17 @@
 -- | Non-realtime score generation.
 module Sound.SC3.Server.NRT where
 
+import Data.Maybe {- base -}
 import qualified Data.ByteString.Lazy as B {- bytestring -}
+import System.FilePath {- filepath -}
+import System.IO {- base -}
+import System.Process {- process -}
+
 import Sound.OSC.Core {- hosc -}
 import Sound.OSC.Coding.Byte {- hosc -}
-import System.IO {- base -}
 
+import Sound.SC3.Server.Enum
+
 -- | Encode and prefix with encoded length.
 oscWithSize :: Bundle -> B.ByteString
 oscWithSize o =
@@ -16,6 +22,11 @@
 -- | An 'NRT' score is a sequence of 'Bundle's.
 data NRT = NRT {nrt_bundles :: [Bundle]} deriving (Show)
 
+-- | 'span' of 'f' of 'bundleTime'.  Can be used to separate the
+-- /initialisation/ and /remainder/ parts of a score.
+nrt_span :: (Time -> Bool) -> NRT -> ([Bundle],[Bundle])
+nrt_span f = span (f . bundleTime) . nrt_bundles
+
 -- | Encode an 'NRT' score.
 encodeNRT :: NRT -> B.ByteString
 encodeNRT = B.concat . map oscWithSize . nrt_bundles
@@ -46,3 +57,30 @@
 -- | 'decodeNRT' of 'B.readFile'.
 readNRT :: FilePath -> IO NRT
 readNRT = fmap decodeNRT . B.readFile
+
+-- * Render
+
+-- | Minimal NRT rendering options.  The sound file type is inferred
+-- from the file name extension.  Structure is: OSC file name, output
+-- audio file name, output number of channels, sample rate, sample
+-- format.
+type NRT_Render_Plain = (FilePath,FilePath,Int,Int,SampleFormat)
+
+-- | Minimal NRT rendering, for more control see Stefan Kersten's
+-- /hsc3-process/ package at:
+-- <https://github.com/kaoskorobase/hsc3-process>.
+nrt_render_plain :: NRT_Render_Plain -> NRT -> IO ()
+nrt_render_plain (osc_nm,sf_nm,nc,sr,sf) sc = do
+  let sf_ty = case takeExtension sf_nm of
+                '.':ext -> let fmt = soundFileFormat_from_extension ext
+                           in fromMaybe (error "nrt_render_plain: unknown sf extension") fmt
+                _ -> error "nrt_render_plain: invalid sf extension"
+      sys = unwords ["scsynth"
+                    ,"-i","0"
+                    ,"-o",show nc
+                    ,"-N"
+                    ,osc_nm,"_"
+                    ,sf_nm,show sr,soundFileFormatString sf_ty,sampleFormatString sf]
+  writeNRT osc_nm sc
+  _ <- system sys
+  return ()
diff --git a/Sound/SC3/Server/NRT/Edit.hs b/Sound/SC3/Server/NRT/Edit.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/Server/NRT/Edit.hs
@@ -0,0 +1,81 @@
+-- | 'NRT' operations.
+module Sound.SC3.Server.NRT.Edit where
+
+import Data.List {- base -}
+import qualified Data.List.Ordered as L {- data-ordlist -}
+import Sound.OSC {- hosc -}
+
+import Sound.SC3.Server.Command
+import Sound.SC3.Server.NRT
+
+-- * List
+
+-- | Inserts at the first position where it compares less but not
+-- equal to the next element.
+--
+-- > import Data.Function
+-- > insertBy (compare `on` fst) (3,'x') (zip [1..5] ['a'..])
+-- > insertBy_post (compare `on` fst) (3,'x') (zip [1..5] ['a'..])
+insertBy_post :: (a -> a -> Ordering) -> a -> [a] -> [a]
+insertBy_post cmp e l =
+    case l of
+      [] -> [e]
+      h:l' -> case cmp e h of
+                LT -> e : l
+                _ -> h : insertBy_post cmp e l'
+
+-- | 'insertBy_post' using 'compare'.
+insert_post :: Bundle -> [Bundle] -> [Bundle]
+insert_post = insertBy_post compare
+
+-- | Apply /f/ at all but last element, and /g/ at last element.
+--
+-- > at_last (* 2) negate [1..4] == [2,4,6,-4]
+at_last :: (a -> b) -> (a -> b) -> [a] -> [b]
+at_last f g x =
+    case x of
+      [] -> []
+      [i] -> [g i]
+      i:x' -> f i : at_last f g x'
+
+-- * NRT
+
+-- | Merge two NRT scores.  Retains internal 'nrt_end' messages.
+nrt_merge :: NRT -> NRT -> NRT
+nrt_merge (NRT p) (NRT q) = NRT (L.merge p q)
+
+-- | Merge a set of NRT.  Retains internal 'nrt_end' messages.
+nrt_merge_set :: [NRT] -> NRT
+nrt_merge_set = foldr nrt_merge nrt_empty
+
+-- | The empty NRT.
+nrt_empty :: NRT
+nrt_empty = NRT []
+
+-- | Add bundle at first permissable location of NRT.
+nrt_insert_pre :: Bundle -> NRT -> NRT
+nrt_insert_pre p (NRT q) = NRT (insert p q)
+
+-- | Add bundle at last permissable location of NRT.
+nrt_insert_post :: Bundle -> NRT -> NRT
+nrt_insert_post p (NRT q) = NRT (insert_post p q)
+
+-- | 'bundleTime' of 'last' of 'nrt_bundles'.
+nrt_end_time :: NRT -> Time
+nrt_end_time = bundleTime . last . nrt_bundles
+
+-- | Apply temporal and message functions to bundle.
+bundle_map :: (Time -> Time) -> ([Message] -> [Message]) -> Bundle -> Bundle
+bundle_map t_f m_f (Bundle t m) = Bundle (t_f t) (m_f m)
+
+-- | Delete any internal 'nrt_end' messages, and require one at the
+-- final bundle.
+nrt_close :: NRT -> NRT
+nrt_close (NRT l) =
+    let is_nrt_end_msg = (== "/nrt_end") . messageAddress
+        rem_end_msg = bundle_map id (filter (not . is_nrt_end_msg))
+        req_end_msg = let f m = if any is_nrt_end_msg m
+                                then m
+                                else m ++ [nrt_end]
+                      in bundle_map id f
+    in NRT (at_last rem_end_msg req_end_msg l)
diff --git a/Sound/SC3/Server/Recorder.hs b/Sound/SC3/Server/Recorder.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/Server/Recorder.hs
@@ -0,0 +1,91 @@
+-- | Recording @scsynth@.
+module Sound.SC3.Server.Recorder where
+
+import Data.Default {- data-default -}
+import Sound.OSC {- hosc -}
+
+import Sound.SC3.Server.Command
+import Sound.SC3.Server.Enum
+import Sound.SC3.Server.NRT
+import Sound.SC3.Server.Synthdef
+import Sound.SC3.UGen
+import Sound.SC3.UGen.Bindings
+
+-- | Parameters for recording @scsynth@.
+data SC3_Recorder =
+    SC3_Recorder {rec_sftype :: SoundFileFormat -- ^ Sound file format.
+                 ,rec_coding :: SampleFormat -- ^ Sample format.
+                 ,rec_fname :: FilePath -- ^ File name.
+                 ,rec_nc :: Int -- ^ Number of channels.
+                 ,rec_bus :: Int -- ^ Bus number.
+                 ,rec_buf_id :: Int -- ^ ID of buffer to allocate.
+                 ,rec_buf_frames :: Int -- ^ Number of frames at buffer.
+                 ,rec_node_id :: Int -- ^ ID to allocate for node.
+                 ,rec_group_id :: Int -- ^ Group to allocate node within.
+                 ,rec_dur :: Maybe Time -- ^ Recoring duration if fixed.
+                 }
+
+-- | Default recording structure.
+default_SC3_Recorder :: SC3_Recorder
+default_SC3_Recorder =
+    SC3_Recorder {rec_sftype = Wave
+                 ,rec_coding = PcmFloat
+                 ,rec_fname = "/tmp/sc3-recorder.wav"
+                 ,rec_nc = 2
+                 ,rec_bus = 0
+                 ,rec_buf_id = 2001
+                 ,rec_buf_frames = 48000
+                 ,rec_node_id = 2001
+                 ,rec_group_id = 0
+                 ,rec_dur = Just 60}
+
+instance Default SC3_Recorder where def = default_SC3_Recorder
+
+-- | Generate 'Synthdef' with required number of channels.
+rec_synthdef :: SC3_Recorder -> Synthdef
+rec_synthdef r =
+    let bufnum = control KR "bufnum" 0
+        bus = control KR "bus" 0
+        nm = "sc3-recorder-" ++ show (rec_nc r)
+    in synthdef nm (diskOut bufnum (in' (rec_nc r) AR bus))
+
+-- | Asyncronous initialisation 'Message's ('d_recv', 'b_alloc' and
+-- 'b_write').
+--
+-- > withSC3 (sendBundle (bundle immediately (rec_init_m def)))
+rec_init_m :: SC3_Recorder -> [Message]
+rec_init_m r =
+    let buf = rec_buf_id r
+    in [d_recv (rec_synthdef r)
+       ,b_alloc buf (rec_buf_frames r) (rec_nc r)
+       ,b_write buf (rec_fname r) (rec_sftype r) (rec_coding r) (-1) 0 True]
+
+-- | Begin recording 'Message' ('s_new').
+--
+-- > withSC3 (sendMessage (rec_begin_m def))
+rec_begin_m :: SC3_Recorder -> Message
+rec_begin_m r =
+    s_new (synthdefName (rec_synthdef r))
+          (rec_node_id r)
+          AddToTail
+          (rec_group_id r)
+          [("bus",fromIntegral (rec_bus r))
+          ,("bufnum",fromIntegral (rec_buf_id r))]
+
+-- | End recording 'Message's ('n_free', 'b_close' and 'b_free').
+--
+-- > withSC3 (sendBundle (bundle immediately (rec_end_m def)))
+rec_end_m :: SC3_Recorder -> [Message]
+rec_end_m r =
+    [n_free [rec_node_id r]
+    ,b_close (rec_buf_id r)
+    ,b_free (rec_buf_id r)]
+
+-- | 'NRT' score for recorder, if 'rec_dur' is given schedule
+-- 'rec_end_m'.
+sc3_recorder :: SC3_Recorder -> NRT
+sc3_recorder r =
+    let b0 = bundle 0 (rec_init_m r ++ [rec_begin_m r])
+    in case rec_dur r of
+         Nothing -> NRT [b0]
+         Just d -> NRT [b0,bundle d (rec_end_m r)]
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,7 +1,10 @@
 -- | Request and display status information from the synthesis server.
 module Sound.SC3.Server.Status where
 
+import qualified Data.ByteString.Char8 as C {- bytestring -}
+import Data.List {- base -}
 import Data.Maybe {- base -}
+import qualified Data.Tree as T {- containers -}
 import Sound.OSC.Type {- hosc -}
 
 -- | Get /n/th field of status as 'Floating'.
@@ -29,3 +32,116 @@
 statusFormat d =
     let s = "***** SuperCollider Server Status *****"
     in s : zipWith (++) (tail statusFields) (map show (tail d))
+
+
+-- * Query Group
+
+-- | Name or index and value or bus mapping.
+type Query_Ctl = (Either String Int,Either Double Int)
+
+-- | Nodes are either groups of synths.
+data Query_Node = Query_Group Int [Query_Node]
+                | Query_Synth Int String (Maybe [Query_Ctl])
+                deriving (Eq,Show)
+
+query_ctl_pp :: Query_Ctl -> String
+query_ctl_pp (p,q) = either id show p ++ ":" ++ either show show q
+
+query_node_pp :: Query_Node -> String
+query_node_pp n =
+    case n of
+      Query_Group k _ -> show k
+      Query_Synth k nm c ->
+          let c' = unwords (map query_ctl_pp (fromMaybe [] c))
+          in show (k,nm,c')
+
+-- | Control (parameter) data may be given as names or indices and as
+-- values or bus mappings.
+--
+-- > queryTree_ctl (string "freq",float 440) == (Left "freq",Left 440.0)
+-- > queryTree_ctl (int32 1,string "c0") == (Right 1,Right 0)
+queryTree_ctl :: (Datum,Datum) -> Query_Ctl
+queryTree_ctl (p,q) =
+    let err msg val = error (show ("queryTree_ctl",msg,val))
+        f d = case d of
+                ASCII_String nm -> Left (C.unpack nm)
+                Int32 ix -> Right (fromIntegral ix)
+                _ -> err "string/int32" d
+        g d = case d of
+                Float k -> Left (realToFrac k)
+                ASCII_String b -> case C.unpack b of
+                                    'c' : n -> Right (read n)
+                                    _ -> err "c:_" d
+                _ -> err "float/string" d
+    in (f p,g q)
+
+-- > let d = [int32 1,string "freq",float 440]
+-- > in queryTree_synth True 1000 "saw" d
+queryTree_synth :: Bool -> Int -> String -> [Datum] -> (Query_Node,[Datum])
+queryTree_synth rc k nm d =
+    let pairs l = case l of
+                    e0:e1:l' -> (e0,e1) : pairs l'
+                    _ -> []
+        f r = case r of
+                Int32 n : r' -> let (p,r'') = genericSplitAt (n * 2) r'
+                                in (map queryTree_ctl (pairs p),r'')
+                _ -> error "queryTree_synth"
+    in if rc
+       then let (p,d') = f d
+            in (Query_Synth k nm (Just p),d')
+       else (Query_Synth k nm Nothing,d)
+
+queryTree_group :: Bool -> Int -> Int -> [Datum] -> (Query_Node,[Datum])
+queryTree_group rc gid nc =
+    let rec n r d = if n == 0
+                    then (Query_Group gid (reverse r),d)
+                    else let (c,d') = queryTree_child rc d
+                         in rec (n - 1) (c : r) d'
+    in rec nc []
+
+queryTree_child :: Bool -> [Datum] -> (Query_Node,[Datum])
+queryTree_child rc d =
+    case d of
+      Int32 nid : Int32 (-1) : ASCII_String nm : d' ->
+          queryTree_synth rc (fromIntegral nid) (C.unpack nm) d'
+      Int32 gid : Int32 nc : d' ->
+          queryTree_group rc (fromIntegral gid) (fromIntegral nc) d'
+      _ -> error "queryTree_child"
+
+-- | Parse result of 'g_queryTree'.
+--
+-- > let r = [int32 1,int32 0,int32 2,int32 1,int32 1
+-- >         ,int32 100,int32 1
+-- >         ,int32 1000,int32 (-1),string "saw"
+-- >         ,int32 1,string "freq",float 440.0
+-- >         ,int32 2,int32 0]
+-- > in queryTree r
+queryTree :: [Datum] -> Query_Node
+queryTree d =
+    case d of
+      Int32 rc : Int32 gid : Int32 nc : d' ->
+          let rc' = not (rc == 0)
+              gid' = fromIntegral gid
+              nc' = fromIntegral nc
+          in case queryTree_group rc' gid' nc' d' of
+               (r,[]) -> r
+               _ -> error "queryTree"
+      _ -> error "queryTree"
+
+-- | Transform 'Query_Node' to 'T.Tree'.
+--
+-- > putStrLn (T.drawTree (fmap query_node_pp (queryTree_rt (queryTree r))))
+-- > > 0
+-- > > |
+-- > > +- 1
+-- > > |  |
+-- > > |  `- 100
+-- > > |     |
+-- > > |     `- (1000,"saw","freq:440.0")
+-- > > |
+-- > > `- 2
+queryTree_rt :: Query_Node -> T.Tree Query_Node
+queryTree_rt n =
+    case n of
+      Query_Synth _ _ _ -> T.Node n []
+      Query_Group _ c -> T.Node n (map queryTree_rt c)
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
@@ -2,55 +2,58 @@
 --   SuperCollider synthesis server.
 module Sound.SC3.Server.Synthdef where
 
-import qualified Data.ByteString.Char8 as C {- bytestring -}
-import qualified Data.ByteString.Lazy as B {- bytestring -}
+import qualified Data.ByteString.Lazy as L {- bytestring -}
 import Data.Default {- data-default -}
 import Data.List {- base -}
 import Data.Maybe {- base -}
-import Sound.OSC.Coding.Byte {- hosc -}
-import Sound.OSC.Coding.Cast {- hosc -}
 import System.FilePath {- filepath -}
 
-import Sound.SC3.Server.Synthdef.Internal
-import Sound.SC3.Server.Synthdef.Type
+import qualified Sound.SC3.Server.Graphdef as G
+import qualified Sound.SC3.Server.Graphdef.Graph as G
 import Sound.SC3.UGen.Graph
+import Sound.SC3.UGen.Help.Graph
 import Sound.SC3.UGen.Type
-
--- | 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
-        g' = g {ugens = reverse (ugens g)}
-    in add_implicit g'
+import Sound.SC3.UGen.UGen
 
--- | Binary representation of a unit generator synth definition.
+-- | A named unit generator graph.
 data Synthdef = Synthdef {synthdefName :: String
-                         ,synthdefGraph :: Graph}
+                         ,synthdefUGen :: UGen}
                 deriving (Eq,Show)
 
 instance Default Synthdef where def = defaultSynthdef
 
 -- | Lift a 'UGen' graph into a 'Synthdef'.
 synthdef :: String -> UGen -> Synthdef
-synthdef s u = Synthdef s (synth u)
+synthdef = Synthdef
 
--- | The SC3 /default/ instrument 'Synthdef'.
+-- | The SC3 /default/ instrument 'Synthdef', see
+-- 'default_ugen_graph'.
+--
+-- > withSC3 (send (d_recv defaultSynthdef))
+-- > audition defaultSynthdef
 defaultSynthdef :: Synthdef
 defaultSynthdef = synthdef "default" default_ugen_graph
 
+-- | The SC3 /default/ sample (buffer) playback instrument 'Synthdef',
+-- see 'default_sampler_ugen_graph'.
+--
+-- > withSC3 (send (d_recv (defaultSampler False)))
+-- > audition (defaultSampler False)
+defaultSampler :: Bool -> Synthdef
+defaultSampler use_gate =
+    let nm = "default-sampler-" ++ if use_gate then "gate" else "fixed"
+    in synthdef nm (default_sampler_ugen_graph use_gate)
+
+-- | 'ugen_to_graph' of 'synthdefUGen'.
+synthdefGraph :: Synthdef -> Graph
+synthdefGraph = ugen_to_graph . synthdefUGen
+
 -- | Parameter names at 'Synthdef'.
 --
 -- > synthdefParam def == ["amp","pan","gate","freq"]
 synthdefParam :: Synthdef -> [String]
 synthdefParam = map node_k_name . controls . synthdefGraph
 
--- | Transform a unit generator graph into bytecode.
-graphdef :: Graph -> Graphdef
-graphdef = encode_graphdef
-
 -- | Find the indices of the named UGen at 'Graph'.  The index is
 -- required when using 'Sound.SC3.Server.Command.u_cmd'.
 ugenIndices :: String -> Graph -> [Integer]
@@ -61,34 +64,40 @@
               _ -> Nothing
     in mapMaybe f . zip [0..] . ugens
 
+-- | 'graph_to_graphdef' at 'Synthdef'.
+synthdef_to_graphdef :: Synthdef -> G.Graphdef
+synthdef_to_graphdef (Synthdef nm u) = G.graph_to_graphdef nm (ugen_to_graph u)
+
 -- | Encode 'Synthdef' as a binary data stream.
-synthdefData :: Synthdef -> Graphdef
-synthdefData (Synthdef s g) =
-    B.concat [encode_str (C.pack "SCgf")
-             ,encode_i32 0
-             ,encode_i16 1
-             ,B.pack (str_pstr s)
-             ,encode_graphdef g]
+synthdefData :: Synthdef -> L.ByteString
+synthdefData = G.encode_graphdef . synthdef_to_graphdef
 
 -- | 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)
+    in L.writeFile nm (synthdefData s)
 
 -- | Simple statistical analysis of a unit generator graph.
-synthstat :: UGen -> String
-synthstat u =
-    let s = synth u
-        cs = constants s
+graph_stat :: Graph -> String
+graph_stat s =
+    let cs = constants s
         ks = controls s
         us = ugens s
+        u_nm z = ugen_user_name (node_u_name z) (node_u_special z)
         f g = let h (x:xs) = (x,length (x:xs))
-                  h [] = error "synthstat"
+                  h [] = error "graph_stat"
               in show . map h . group . sort . map g
+        sq = intercalate "," (map u_nm us)
     in unlines ["number of constants       : " ++ show (length cs)
                ,"number of controls        : " ++ show (length ks)
                ,"control rates             : " ++ f node_k_rate ks
                ,"number of unit generators : " ++ show (length us)
-               ,"unit generator rates      : " ++ f node_u_rate us]
+               ,"unit generator rates      : " ++ f node_u_rate us
+               ,"unit generator sequence   : " ++ sq]
+
+-- | 'graph_stat' of 'synth'.
+synthstat :: UGen -> String
+synthstat = graph_stat . ugen_to_graph
+
diff --git a/Sound/SC3/Server/Synthdef/Internal.hs b/Sound/SC3/Server/Synthdef/Internal.hs
deleted file mode 100644
--- a/Sound/SC3/Server/Synthdef/Internal.hs
+++ /dev/null
@@ -1,381 +0,0 @@
-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 {- base -}
-import Data.List{- base -}
-import Data.Maybe{- base -}
-import Sound.OSC.Coding.Byte {- hosc -}
-import Sound.OSC.Coding.Cast {- hosc -}
-
-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 :: Float -> 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 :: Float -> 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,Float,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
deleted file mode 100644
--- a/Sound/SC3/Server/Synthdef/Reconstruct.hs
+++ /dev/null
@@ -1,129 +0,0 @@
--- | A /disasembler/ for UGen graphs.
-module Sound.SC3.Server.Synthdef.Reconstruct where
-
-import Data.Char {- base -}
-import Data.Function {- base -}
-import Data.List {- base -}
-import Text.Printf {- base -}
-
-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
-
-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
-
--- | Generate a reconstruction of a 'Graph'.
---
--- > 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::Float)" l c
-
-reconstruct_c_ugen :: Node -> UGen
-reconstruct_c_ugen u = constant (node_c_value u)
-
-reconstruct_k_rnd :: Node -> (Rate,String,Float)
-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_f32 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
deleted file mode 100644
--- a/Sound/SC3/Server/Synthdef/Transform.hs
+++ /dev/null
@@ -1,78 +0,0 @@
--- | Transformations over 'Graph' structure.
-module Sound.SC3.Server.Synthdef.Transform where
-
-import Data.Either {- base -}
-import Data.List {- base -}
-import Data.Maybe {- base -}
-
-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
deleted file mode 100644
--- a/Sound/SC3/Server/Synthdef/Type.hs
+++ /dev/null
@@ -1,61 +0,0 @@
--- | 'Node', 'Graph' and related 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 :: Float}
-          | NodeK {node_id :: NodeId
-                  ,node_k_rate :: Rate
-                  ,node_k_name :: String
-                  ,node_k_default :: Float
-                  ,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)
-
diff --git a/Sound/SC3/Server/Transport/FD.hs b/Sound/SC3/Server/Transport/FD.hs
--- a/Sound/SC3/Server/Transport/FD.hs
+++ b/Sound/SC3/Server/Transport/FD.hs
@@ -1,17 +1,19 @@
 -- | /FD/ variant of interaction with the scsynth server.
+--
+-- This duplicates functions at 'Sound.SC3.Server.Transport.Monad' and
+-- at some point at least part of the duplication will be removed.
 module Sound.SC3.Server.Transport.FD where
 
 import Data.Maybe {- base -}
 import Control.Monad {- base -}
 import Sound.OSC.FD {- hosc -}
 
-import Sound.SC3.Server.Command.Core
-import Sound.SC3.Server.Command.Int
+import Sound.SC3.Server.Command
 import Sound.SC3.Server.Enum
+import qualified Sound.SC3.Server.Graphdef as G
 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
@@ -41,14 +43,18 @@
   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_new0 (synthdefName s) (-1) AddToTail 1)
+playGraphdef :: Transport t => Int -> t -> G.Graphdef -> IO ()
+playGraphdef k fd g = do
+  _ <- async fd (d_recv' g)
+  sendMessage fd (s_new0 (ascii_to_string (G.graphdef_name g)) k AddToTail 1)
 
+-- | 'playGraphdef' of 'synthdef_to_graphdef'.
+playSynthdef :: Transport t => Int -> t -> Synthdef -> IO ()
+playSynthdef k fd = playGraphdef k fd . synthdef_to_graphdef
+
 -- | Send an /anonymous/ instrument definition using 'playSynthdef'.
-playUGen :: Transport t => t -> UGen -> IO ()
-playUGen fd = playSynthdef fd . synthdef "Anonymous"
+playUGen :: Transport t => Int -> t -> UGen -> IO ()
+playUGen k fd = playSynthdef k fd . synthdef "Anonymous"
 
 -- * Non-real time
 
@@ -56,12 +62,14 @@
 -- to initial 'Time', then send each message, asynchronously if
 -- required.
 run_bundle :: Transport t => t -> Time -> Bundle -> IO ()
-run_bundle fd i (Bundle t x) = do
-  let wr m = if isAsync m
-             then void (async fd m)
-             else sendMessage fd m
-  pauseThreadUntil (i + t)
-  mapM_ wr x
+run_bundle fd st b = do
+  let t = bundleTime b
+      latency = 0.1
+      wr m = if isAsync m
+             then async fd m >> return ()
+             else sendBundle fd (bundle (st + t) [m])
+  pauseThreadUntil (st + t - latency)
+  mapM_ wr (bundleMessages b)
 
 -- | Perform an 'NRT' score (as would be rendered by 'writeNRT').  In
 -- particular note that all timestamps /must/ be in 'NTPr' form.
@@ -70,24 +78,30 @@
 
 -- * Audible
 
--- | Class for values that can be encoded and send to @scsynth@ for
+-- | Class for values that can be encoded and sent to @scsynth@ for
 -- audition.
 class Audible e where
+    play_id :: Transport t => Int -> t -> e -> IO ()
     play :: Transport t => t -> e -> IO ()
-    audition :: e -> IO ()
-    audition e = withSC3 (`play` e)
+    play = play_id (-1)
 
-instance Audible Graph where
-    play fd g = playSynthdef fd (Synthdef "Anonymous" g)
+instance Audible G.Graphdef where
+    play_id k fd = playGraphdef k fd
 
 instance Audible Synthdef where
-    play = playSynthdef
+    play_id = playSynthdef
 
 instance Audible UGen where
-    play = playUGen
+    play_id = playUGen
 
 instance Audible NRT where
-    play = performNRT
+    play_id _ = performNRT
+
+audition_id :: Audible e => Int -> e -> IO ()
+audition_id k e = withSC3 (\fd -> play_id k fd e)
+
+audition :: Audible e => e -> IO ()
+audition = audition_id (-1)
 
 -- * Notifications
 
diff --git a/Sound/SC3/Server/Transport/Monad.hs b/Sound/SC3/Server/Transport/Monad.hs
--- a/Sound/SC3/Server/Transport/Monad.hs
+++ b/Sound/SC3/Server/Transport/Monad.hs
@@ -5,13 +5,14 @@
 import Data.Maybe {- base -}
 import Sound.OSC {- hosc -}
 
-import Sound.SC3.Server.Command.Core
-import Sound.SC3.Server.Command.Int
+import Sound.SC3.Server.Command
 import Sound.SC3.Server.Enum
+import qualified Sound.SC3.Server.Graphdef as G
 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.Bindings.Composite (wrapOut)
 import Sound.SC3.UGen.Type
 
 -- * hosc variants
@@ -39,22 +40,33 @@
 stop :: SendOSC m => m ()
 stop = send (g_freeAll [1])
 
--- | Free all nodes ('g_freeAll') at and re-create groups @1@ and @2@.
+-- * Composite
+
+-- | 'clearSched', free all nodes ('g_freeAll') at, and then
+-- re-create, groups @1@ and @2@.
 reset :: SendOSC m => m ()
 reset =
-    let m = [g_freeAll [1,2],g_new [(1,AddToTail,0),(2,AddToTail,0)]]
+    let m = [clearSched
+            ,g_freeAll [1,2]
+            ,g_new [(1,AddToHead,0),(2,AddToTail,0)]]
     in sendBundle (bundle immediately m)
 
+-- | Send 'd_recv' and 's_new' messages to scsynth.
+playGraphdef :: DuplexOSC m => (Int,AddAction,Int) -> G.Graphdef -> m ()
+playGraphdef (nid,act,gid) g = do
+  _ <- async (d_recv' g)
+  send (s_new0 (ascii_to_string (G.graphdef_name g)) nid act gid)
 
 -- | Send 'd_recv' and 's_new' messages to scsynth.
-playSynthdef :: DuplexOSC m => Synthdef -> m ()
-playSynthdef s = do
-  _ <- async (d_recv s)
-  send (s_new0 (synthdefName s) (-1) AddToTail 1)
+playSynthdef :: DuplexOSC m => (Int,AddAction,Int) -> Synthdef -> m ()
+playSynthdef opt = playGraphdef opt . synthdef_to_graphdef
 
 -- | Send an /anonymous/ instrument definition using 'playSynthdef'.
-playUGen :: DuplexOSC m => UGen -> m ()
-playUGen = playSynthdef . synthdef "Anonymous"
+playUGen :: DuplexOSC m => (Int,AddAction,Int) -> UGen -> m ()
+playUGen loc =
+    playSynthdef loc .
+    synthdef "Anonymous" .
+    wrapOut Nothing
 
 -- * NRT
 
@@ -62,39 +74,61 @@
 -- to the initial 'Time', then send each message, asynchronously if
 -- required.
 run_bundle :: Transport m => Time -> Bundle -> m ()
-run_bundle i (Bundle t x) = do
-  let wr m = if isAsync m
+run_bundle st b = do
+  let t = bundleTime b
+      latency = 0.1
+      wr m = if isAsync m
              then async m >> return ()
-             else send m
-  liftIO (pauseThreadUntil (i + t))
-  mapM_ wr x
+             else sendBundle (bundle (st + t) [m])
+  liftIO (pauseThreadUntil (st + t - latency))
+  mapM_ wr (bundleMessages b)
 
--- | Perform an 'NRT' score (as would be rendered by 'writeNRT').  In
--- particular note that all timestamps /must/ be in 'NTPr' form.
+-- | Perform an 'NRT' score (as would be rendered by 'writeNRT').
+-- Asynchronous commands at time @0@ are separated out and run before
+-- the initial time-stamp is taken.  This re-orders synchronous
+-- commands in relation to asynchronous at time @0@.
+--
+-- > let sc = NRT [bundle 1 [s_new0 "default" (-1) AddToHead 1]
+-- >              ,bundle 2 [n_set1 (-1) "gate" 0]]
+-- > in withSC3 (performNRT sc)
 performNRT :: Transport m => NRT -> m ()
-performNRT s = liftIO time >>= \i -> mapM_ (run_bundle i) (nrt_bundles s)
+performNRT s = do
+  let (i,r) = nrt_span (<= 0) s
+      i' = concatMap bundleMessages i
+      (a,b) = partition_async i'
+  mapM_ async a
+  t <- liftIO time
+  mapM_ (run_bundle t) (Bundle 0 b : r)
 
 -- * Audible
 
 -- | Class for values that can be encoded and send to @scsynth@ for
 -- audition.
 class Audible e where
+    play_at :: Transport m => (Int,AddAction,Int) -> e -> m ()
+    -- | Variant where /id/ is @-1@.
     play :: Transport m => e -> m ()
+    play = play_at (-1,AddToHead,1)
 
-instance Audible Graph where
-    play g = playSynthdef (Synthdef "Anonymous" g)
+instance Audible G.Graphdef where
+    play_at k = playGraphdef k
 
 instance Audible Synthdef where
-    play = playSynthdef
+    play_at = playSynthdef
 
 instance Audible UGen where
-    play = playUGen
+    play_at = playUGen
 
 instance Audible NRT where
-    play = performNRT
+    play_at _ = performNRT
 
+-- | 'withSC3' of 'play_at'.
+audition_at :: Audible e => (Int,AddAction,Int) -> e -> IO ()
+audition_at k = withSC3 . play_at k
+
+-- | Variant where /id/ is @-1@.
 audition :: Audible e => e -> IO ()
-audition e = withSC3 (play e)
+audition = audition_at (-1,AddToHead,1)
 
 -- * Notifications
 
@@ -141,6 +175,14 @@
               _ -> error "b_fetch"
   sendMessage (b_query1 b)
   waitDatum "/b_info" >>= f
+
+c_getn1_data :: DuplexOSC m => (Int,Int) -> m [Double]
+c_getn1_data s = do
+  let f d = case d of
+              Int32 _:Int32 _:x -> mapMaybe datum_floating x
+              _ -> error "c_getn1_data"
+  sendMessage (c_getn1 s)
+  liftM f (waitDatum "/c_setn")
 
 -- * Status
 
diff --git a/Sound/SC3/Server/Utilities.hs b/Sound/SC3/Server/Utilities.hs
deleted file mode 100644
--- a/Sound/SC3/Server/Utilities.hs
+++ /dev/null
@@ -1,16 +0,0 @@
--- | Various utility functions, not exported.
-module Sound.SC3.Server.Utilities where
-
--- | Concatentative application of /f/ at /x/ and /g/ at /y/.
-mk_duples :: (a -> c) -> (b -> c) -> [(a, b)] -> [c]
-mk_duples a b = concatMap (\(x,y) -> [a x, b y])
-
--- | Concatentative application of /g/ at /x/ and /f/ at length of /y/
--- and /g/ at each element of /y/.
-mk_duples_l :: (Int -> c) -> (a -> c) -> (b -> c) -> [(a, [b])] -> [c]
-mk_duples_l i a b = concatMap (\(x,y) -> a x : i (length y) : map b y)
-
--- | Concatentative application of /f/ at /x/ and /g/ at /y/ and /h/
--- at /z/.
-mk_triples :: (a -> d) -> (b -> d) -> (c -> d) -> [(a, b, c)] -> [d]
-mk_triples a b c = concatMap (\(x,y,z) -> [a x, b y, c z])
diff --git a/Sound/SC3/UGen.hs b/Sound/SC3/UGen.hs
--- a/Sound/SC3/UGen.hs
+++ b/Sound/SC3/UGen.hs
@@ -1,33 +1,17 @@
 -- | Collection of modules for writing unit-generator graphs.
 module Sound.SC3.UGen (module U) where
 
-import Sound.SC3.UGen.Analysis as U
-import Sound.SC3.UGen.Buffer as U
-import Sound.SC3.UGen.Chaos as U
-import Sound.SC3.UGen.Composite as U
-import Sound.SC3.UGen.Demand as U
-import Sound.SC3.UGen.DiskIO as U
 import Sound.SC3.UGen.Envelope as U
 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
-import Sound.SC3.UGen.Filter as U
-import Sound.SC3.UGen.Granular as U
 import Sound.SC3.UGen.Help as U
-import Sound.SC3.UGen.Information as U
-import Sound.SC3.UGen.IO as U
+import Sound.SC3.UGen.Identifier 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.Optimise as U
+import Sound.SC3.UGen.Protect 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
deleted file mode 100644
--- a/Sound/SC3/UGen/Analysis.hs
+++ /dev/null
@@ -1,26 +0,0 @@
--- | Signal analysis unit generators.
-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
-
--- | Autocorrelation pitch follower.
-pitch :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-pitch i initFreq minFreq maxFreq execFreq maxBinsPerOctave median ampThreshold peakThreshold downSample = mkOsc KR "Pitch" [i, initFreq, minFreq, maxFreq, execFreq, maxBinsPerOctave, median, ampThreshold, peakThreshold, downSample] 2
-
--- | Slope of signal.
-slope :: UGen -> UGen
-slope i = mkFilter "Slope" [i] 1
-
--- | Zero crossing frequency follower.
-zeroCrossing :: UGen -> UGen
-zeroCrossing i = mkFilter "ZeroCrossing" [i] 1
-
--- Local Variables:
--- truncate-lines:t
--- End:
diff --git a/Sound/SC3/UGen/Bindings.hs b/Sound/SC3/UGen/Bindings.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/UGen/Bindings.hs
@@ -0,0 +1,7 @@
+module Sound.SC3.UGen.Bindings (module B) where
+
+import Sound.SC3.UGen.Bindings.Composite as B
+import Sound.SC3.UGen.Bindings.DB as B
+import Sound.SC3.UGen.Bindings.HW as B
+import Sound.SC3.UGen.Bindings.HW.External as B
+import Sound.SC3.UGen.Bindings.Monad as B
diff --git a/Sound/SC3/UGen/Bindings/Composite.hs b/Sound/SC3/UGen/Bindings/Composite.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/UGen/Bindings/Composite.hs
@@ -0,0 +1,438 @@
+-- | Common unit generator graphs.
+module Sound.SC3.UGen.Bindings.Composite where
+
+import Control.Monad {- base -}
+import Data.List {- base -}
+import Data.List.Split {- split -}
+import Data.Maybe {- base -}
+
+import Sound.SC3.UGen.Bindings.DB
+import Sound.SC3.UGen.Bindings.HW
+import Sound.SC3.UGen.Bindings.Monad
+import Sound.SC3.UGen.Enum
+import Sound.SC3.UGen.Envelope
+import Sound.SC3.UGen.Identifier
+import Sound.SC3.UGen.Math
+import Sound.SC3.UGen.Rate
+import Sound.SC3.UGen.Type
+import Sound.SC3.UGen.UGen
+import Sound.SC3.UGen.UId
+
+-- | Generate a localBuf and use setBuf to initialise it.
+asLocalBuf :: ID i => i -> [UGen] -> UGen
+asLocalBuf i xs =
+    let b = localBuf i 1 (fromIntegral (length xs))
+        s = setBuf' b xs 0
+    in mrg2 b s
+
+-- | Calculate coefficients for bi-quad low pass filter.
+bLowPassCoef :: Floating a => a -> a -> a -> (a,a,a,a,a)
+bLowPassCoef sr freq rq =
+    let w0 = pi * 2 * freq * (1 / sr)
+        cos_w0 = cos w0
+        i = 1 - cos_w0
+        alpha = sin w0 * 0.5 * rq
+        b0rz = recip (1 + alpha)
+        a0 = i * 0.5 * b0rz
+        a1 = i * b0rz
+        b1 = cos_w0 * 2 * b0rz
+        b2 = (1 - alpha) * negate b0rz
+    in (a0,a1,a0,b1,b2)
+
+-- | Buffer reader (no interpolation).
+bufRdN :: Int -> Rate -> UGen -> UGen -> Loop -> UGen
+bufRdN n r b p l = bufRd n r b p l NoInterpolation
+
+-- | Buffer reader (linear interpolation).
+bufRdL :: Int -> Rate -> UGen -> UGen -> Loop -> UGen
+bufRdL n r b p l = bufRd n r b p l LinearInterpolation
+
+-- | Buffer reader (cubic interpolation).
+bufRdC :: Int -> Rate -> UGen -> UGen -> Loop -> UGen
+bufRdC n r b p l = bufRd n r b p l CubicInterpolation
+
+-- | Triggers when a value changes
+changed :: UGen -> UGen -> UGen
+changed input threshold = abs (hpz1 input) >* threshold
+
+-- | 'mce' variant of 'lchoose'.
+choose :: ID m => m -> UGen -> UGen
+choose e = lchoose e . mceChannels
+
+-- | 'liftUId' of 'choose'.
+chooseM :: UId m => UGen -> m UGen
+chooseM = liftUId choose
+
+-- | 'clearBuf' of 'localBuf'.
+clearLocalBuf :: ID a => a -> UGen -> UGen -> UGen
+clearLocalBuf z nc nf = clearBuf (localBuf z nc nf)
+
+-- | Demand rate (:) function.
+dcons :: ID m => (m,m,m) -> UGen -> UGen -> UGen
+dcons (z0,z1,z2) x xs =
+    let i = dseq z0 1 (mce2 0 1)
+        a = dseq z1 1 (mce2 x xs)
+    in dswitch z2 i a
+
+-- | Demand rate (:) function.
+dconsM :: (UId m) => UGen -> UGen -> m UGen
+dconsM x xs = do
+  i <- dseqM 1 (mce2 0 1)
+  a <- dseqM 1 (mce2 x xs)
+  dswitchM i a
+
+-- | 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 =
+    let gen (f:a:d:xs) = ringz i (f * fs + fo) (d * ds) * a + gen xs
+        gen _ = 0
+    in gen (mceChannels s)
+
+-- | Variant FFT constructor with default values for hop size (0.5),
+-- window type (0), active status (1) and window size (0).
+fft' :: UGen -> UGen -> UGen
+fft' buf i = fft buf i 0.5 0 1 0
+
+-- | 'fft' variant that allocates 'localBuf'.
+--
+-- > let c = ffta 'α' 2048 (soundIn 0) 0.5 0 1 0
+-- > in audition (out 0 (ifft c 0 0))
+ffta :: ID i => i -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+ffta z nf i h wt a ws =
+    let b = localBuf z 1 nf
+    in fft b i h wt a ws
+
+-- | Sum of 'numInputBuses' and 'numOutputBuses'.
+firstPrivateBus :: UGen
+firstPrivateBus = numInputBuses + numOutputBuses
+
+-- | Frequency shifter, in terms of 'hilbert' (see also 'freqShift').
+freqShift_hilbert :: UGen -> UGen -> UGen -> UGen
+freqShift_hilbert i f p =
+    let o = sinOsc AR f (mce [p + 0.5 * pi, p])
+        h = hilbert i
+    in mix (h * o)
+
+-- | Variant ifft with default value for window type.
+ifft' :: UGen -> UGen
+ifft' buf = ifft buf 0 0
+
+{-
+-- | Linear interpolating variant on index.
+indexL :: UGen -> UGen -> UGen
+indexL b i =
+    let x = index b i
+        y = index b (i + 1)
+    in linLin (frac i) 0 1 x y
+-}
+
+-- | Format frequency, amplitude and phase data as required for klang.
+klangSpec :: [UGen] -> [UGen] -> [UGen] -> UGen
+klangSpec f a p = mce ((concat . transpose) [f, a, p])
+
+-- | Variant of 'klangSpec' for non-UGen inputs.
+klangSpec' :: Real n => [n] -> [n] -> [n] -> UGen
+klangSpec' f a p =
+    let u = map constant
+    in klangSpec (u f) (u a) (u p)
+
+-- | 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)
+
+-- | Format frequency, amplitude and decay time data as required for klank.
+klankSpec :: [UGen] -> [UGen] -> [UGen] -> UGen
+klankSpec f a dt = mce ((concat . transpose) [f,a,dt])
+
+-- | Variant for non-UGen inputs.
+klankSpec' :: Real n => [n] -> [n] -> [n] -> UGen
+klankSpec' f a dt =
+    let u = map constant
+    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)
+
+-- | 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)
+
+-- | 'liftUId' of 'lchoose'.
+lchooseM :: UId m => [UGen] -> m UGen
+lchooseM = liftUId lchoose
+
+-- | 'linExp' of (-1,1).
+linExp_b :: UGen -> UGen -> UGen -> UGen
+linExp_b i = linExp i (-1) 1
+
+-- | 'linExp' of (0,1).
+linExp_u :: UGen -> UGen -> UGen -> UGen
+linExp_u i = linExp i 0 1
+
+-- | Map from one linear range to another linear range.
+linLin :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+linLin i sl sr dl dr =
+    let (m,a) = linLin_muladd sl sr dl dr
+    in mulAdd i m a
+
+-- | 'linLin' where source is (0,1).
+linLin_u :: UGen -> UGen -> UGen -> UGen
+linLin_u i = linLin i 0 1
+
+-- | 'linLin' where source is (-1,1).
+linLin_b :: UGen -> UGen -> UGen -> UGen
+linLin_b i = linLin i (-1) 1
+
+-- | Variant with defaults of zero.
+localIn' :: Int -> Rate -> UGen
+localIn' nc r = localIn nc r (mce (replicate nc 0))
+
+-- | Generate an 'envGen' UGen with @fadeTime@ and @gate@ controls.
+--
+-- > import Sound.SC3
+-- > audition (out 0 (makeFadeEnv 1 * sinOsc AR 440 0 * 0.1))
+-- > withSC3 (send (n_set1 (-1) "gate" 0))
+makeFadeEnv :: Double -> UGen
+makeFadeEnv fadeTime =
+    let dt = control KR "fadeTime" (realToFrac fadeTime)
+        gate_ = control KR "gate" 1
+        startVal = dt <=* 0
+        env = Envelope [startVal,1,0] [1,1] [EnvLin,EnvLin] (Just 1) Nothing
+    in envGen KR gate_ 1 0 dt RemoveSynth env
+
+-- | Count 'mce' channels.
+mceN :: UGen -> UGen
+mceN = constant . length . mceChannels
+
+-- | Collapse possible mce by summing.
+mix :: UGen -> UGen
+mix = sum_opt . mceChannels
+
+-- | Mix variant, sum to n channels.
+mixN :: Int -> UGen -> UGen
+mixN n u =
+    let xs = transpose (chunksOf n (mceChannels u))
+    in mce (map sum xs)
+
+-- | Construct and sum a set of UGens.
+mixFill :: Integral n => Int -> (n -> UGen) -> UGen
+mixFill n f = mix (mce (map f [0 .. fromIntegral 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
+mouseButton' rt l r tm =
+    let o = lfClipNoise 'z' rt 1
+    in lag (linLin o (-1) 1 l r) tm
+
+-- | Randomised mouse UGen (see also 'mouseX'' and 'mouseY'').
+mouseR :: ID a => a -> Rate -> UGen -> UGen -> Warp -> UGen -> UGen
+mouseR z rt l r ty tm =
+  let f = case ty of
+            Linear -> linLin
+            Exponential -> linExp
+            _ -> undefined
+  in lag (f (lfNoise1 z rt 1) (-1) 1 l r) tm
+
+-- | Variant that randomly traverses the mouseX space.
+mouseX' :: Rate -> UGen -> UGen -> Warp -> UGen -> UGen
+mouseX' = mouseR 'x'
+
+-- | Variant that randomly traverses the mouseY space.
+mouseY' :: Rate -> UGen -> UGen -> Warp -> UGen -> UGen
+mouseY' = mouseR 'y'
+
+-- | Translate onset type string to constant UGen value.
+onsetType :: Num a => String -> a
+onsetType s =
+    let t = ["power", "magsum", "complex", "rcomplex", "phase", "wphase", "mkl"]
+    in fromIntegral (fromMaybe 3 (elemIndex s t))
+
+-- | Onset detector with default values for minor parameters.
+onsets' :: UGen -> UGen -> UGen -> UGen
+onsets' c t o = onsets c t o 1 0.1 10 11 1 0
+
+-- | Format magnitude and phase data data as required for packFFT.
+packFFTSpec :: [UGen] -> [UGen] -> UGen
+packFFTSpec m p =
+    let interleave x = concat . zipWith (\a b -> [a,b]) x
+    in mce (interleave m p)
+
+-- | Calculate size of accumulation buffer given FFT and IR sizes.
+pc_calcAccumSize :: Int -> Int -> Int
+pc_calcAccumSize fft_size ir_length =
+    let partition_size = fft_size `div` 2
+        num_partitions = (ir_length `div` partition_size) + 1
+    in fft_size * num_partitions
+
+-- | PM oscillator.
+pmOsc :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen
+pmOsc r cf mf pm mp = sinOsc r cf (sinOsc r mf mp * pm)
+
+-- | Variant of 'poll' that generates an 'mrg' value with the input
+-- signal at left, and that allows a constant /frequency/ input in
+-- place of a trigger.
+poll' :: UGen -> UGen -> UGen -> UGen -> UGen
+poll' t i l tr =
+    let t' = if isConstant t then impulse KR t 0 else t
+    in mrg [i,poll t' i l tr]
+
+-- | Variant of 'in'' offset so zero if the first private bus.
+privateIn :: Int -> Rate -> UGen -> UGen
+privateIn nc rt k = in' nc rt (k + firstPrivateBus)
+
+-- | Variant of 'out' offset so zero if the first private bus.
+privateOut :: UGen -> UGen -> UGen
+privateOut k = out (k + firstPrivateBus)
+
+-- | Apply function /f/ to each bin of an @FFT@ chain, /f/ receives
+-- magnitude, phase and index and returns a (magnitude,phase).
+pvcollect :: UGen -> UGen -> (UGen -> UGen -> UGen -> (UGen, UGen)) -> UGen -> UGen -> UGen -> UGen
+pvcollect c nf f from to z = packFFT c nf from to z mp
+  where m = unpackFFT c nf from to 0
+        p = unpackFFT c nf from to 1
+        i = [from .. to]
+        e = zipWith3 f m p i
+        mp = uncurry packFFTSpec (unzip e)
+
+-- | RMS variant of 'runningSum'.
+runningSumRMS :: UGen -> UGen -> UGen
+runningSumRMS z n = sqrt (runningSum (z * z) n * (recip n))
+
+-- | Mix one output from many sources
+selectX :: UGen -> UGen -> UGen
+selectX ix xs =
+    let s0 = select (roundTo ix 2) xs
+        s1 = select (trunc ix 2 + 1) xs
+    in xFade2 s0 s1 (fold2 (ix * 2 - 1) 1) 1
+
+-- | Set local buffer values.
+setBuf' :: UGen -> [UGen] -> UGen -> UGen
+setBuf' b xs o = setBuf b o (fromIntegral (length xs)) (mce xs)
+
+-- | 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 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
+splay i s l c lc =
+    let n = fromIntegral (mceDegree i)
+        m = n - 1
+        p = map ( (+ (-1.0)) . (* (2 / m)) ) [0 .. m]
+        a = if lc then sqrt (1 / n) else 1
+    in mix (pan2 i (mce p * s + c) 1) * l * a
+
+-- | Optimised sum function.
+sum_opt :: [UGen] -> UGen
+sum_opt l =
+    case l of
+      p:q:r:s:l' -> sum_opt (sum4 p q r s : l')
+      p:q:r:l' -> sum_opt (sum3 p q r : l')
+      _ -> sum l
+
+-- | Single tap into a delayline
+tap :: Int -> UGen -> UGen -> UGen
+tap numChannels bufnum delaytime =
+    let n = delaytime * negate sampleRate
+    in playBuf numChannels AR bufnum 1 0 n Loop DoNothing
+
+-- | Randomly select one of several inputs on trigger.
+tChoose :: ID m => m -> UGen -> UGen -> UGen
+tChoose z t a = select (tIRand z 0 (mceN a) t) a
+
+-- | Randomly select one of several inputs.
+tChooseM :: (UId m) => UGen -> UGen -> m UGen
+tChooseM t a = do
+  r <- tIRandM 0 (constant (length (mceChannels a))) t
+  return (select r a)
+
+-- | Randomly select one of several inputs on trigger (weighted).
+tWChoose :: ID m => m -> UGen -> UGen -> UGen -> UGen -> UGen
+tWChoose z t a w n =
+    let i = tWindex z t n w
+    in select i a
+
+-- | Randomly select one of several inputs (weighted).
+tWChooseM :: (UId m) => UGen -> UGen -> UGen -> UGen -> m UGen
+tWChooseM t a w n = do
+  i <- tWindexM t n w
+  return (select i a)
+
+-- | Unpack an FFT chain into separate demand-rate FFT bin streams.
+unpackFFT :: UGen -> UGen -> UGen -> UGen -> UGen -> [UGen]
+unpackFFT c nf from to w = map (\i -> unpack1FFT c nf i w) [from .. to]
+
+-- | If @z@ isn't a sink node route to an @out@ node writing to @bus@.
+-- If @fadeTime@ is given multiply by 'makeFadeEnv'.
+--
+-- > import Sound.SC3
+-- > audition (wrapOut (sinOsc AR 440 0 * 0.1) 1)
+-- > withSC3 (send (n_set1 (-1) "gate" 0))
+wrapOut :: Maybe Double -> UGen -> UGen
+wrapOut fadeTime z =
+    let bus = control KR "out" 0
+    in if isSink z
+       then z
+       else out bus (z * maybe 1 makeFadeEnv fadeTime)
+
+-- * wslib
+
+playBufCF :: Int -> UGen -> UGen -> UGen -> UGen -> Loop -> UGen -> Int -> UGen
+playBufCF nc bufnum rate trigger startPos loop lag' n =
+    let trigger' = if rateOf trigger == DR
+                   then tDuty AR trigger 0 DoNothing 1 0
+                   else trigger
+        index' = stepper trigger' 0 0 (constant n - 1) 1 0
+        on = map
+             (\i -> inRange index' (i - 0.5) (i + 0.5))
+             [0 .. constant n - 1]
+        rate' = case rateOf rate of
+                  DR -> map (\on' -> demand on' 0 rate) on
+                  KR -> map (\on' -> gate rate on') on
+                  AR -> map (\on' -> gate rate on') on
+                  IR -> map (const rate) on
+        startPos' = if rateOf startPos == DR
+                    then demand trigger' 0 startPos
+                    else startPos
+        lag'' = 1 / lag'
+        s = map
+            (\(on',r) -> let p = playBuf nc AR bufnum r on' startPos' loop DoNothing
+                         in p * sqrt (slew on' lag'' lag''))
+            (zip on rate')
+    in sum s
+
+-- * adc
+
+-- | An oscillator that reads through a table once.
+osc1 :: Rate -> UGen -> UGen -> DoneAction -> UGen
+osc1 rt buf dur doneAction =
+    let ph = line rt 0 (bufFrames IR buf - 1) dur doneAction
+    in bufRd 1 rt buf ph NoLoop LinearInterpolation
diff --git a/Sound/SC3/UGen/Bindings/DB.hs b/Sound/SC3/UGen/Bindings/DB.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/UGen/Bindings/DB.hs
@@ -0,0 +1,1516 @@
+module Sound.SC3.UGen.Bindings.DB where
+
+import Sound.SC3.UGen.Envelope
+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
+
+-- | Audio to control rate converter.
+a2K :: UGen -> UGen
+a2K in_ = mkUGen Nothing [KR] (Left KR) "A2K" [in_] Nothing 1 (Special 0) NoId
+
+-- | FIXME: APF purpose.
+apf :: UGen -> UGen -> UGen -> UGen
+apf in_ freq radius = mkUGen Nothing [KR,AR] (Right [0]) "APF" [in_,freq,radius] Nothing 1 (Special 0) NoId
+
+-- | All pass delay line with cubic interpolation.
+allpassC :: UGen -> UGen -> UGen -> UGen -> UGen
+allpassC in_ maxdelaytime delaytime decaytime = mkUGen Nothing [KR,AR] (Right [0]) "AllpassC" [in_,maxdelaytime,delaytime,decaytime] Nothing 1 (Special 0) NoId
+
+-- | All pass delay line with linear interpolation.
+allpassL :: UGen -> UGen -> UGen -> UGen -> UGen
+allpassL in_ maxdelaytime delaytime decaytime = mkUGen Nothing [KR,AR] (Right [0]) "AllpassL" [in_,maxdelaytime,delaytime,decaytime] Nothing 1 (Special 0) NoId
+
+-- | All pass delay line with no interpolation.
+allpassN :: UGen -> UGen -> UGen -> UGen -> UGen
+allpassN in_ maxdelaytime delaytime decaytime = mkUGen Nothing [KR,AR] (Right [0]) "AllpassN" [in_,maxdelaytime,delaytime,decaytime] Nothing 1 (Special 0) NoId
+
+-- | Basic psychoacoustic amplitude compensation.
+ampComp :: Rate -> UGen -> UGen -> UGen -> UGen
+ampComp rate freq root exp_ = mkUGen Nothing [IR,KR,AR] (Left rate) "AmpComp" [freq,root,exp_] Nothing 1 (Special 0) NoId
+
+-- | Basic psychoacoustic amplitude compensation (ANSI A-weighting curve).
+ampCompA :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen
+ampCompA rate freq root minAmp rootAmp = mkUGen Nothing [IR,KR,AR] (Left rate) "AmpCompA" [freq,root,minAmp,rootAmp] Nothing 1 (Special 0) NoId
+
+-- | Amplitude follower
+amplitude :: Rate -> UGen -> UGen -> UGen -> UGen
+amplitude rate in_ attackTime releaseTime = mkUGen Nothing [KR,AR] (Left rate) "Amplitude" [in_,attackTime,releaseTime] Nothing 1 (Special 0) NoId
+
+-- | (Undocumented class)
+audioControl :: Rate -> UGen -> UGen
+audioControl rate values = mkUGen Nothing [AR] (Left rate) "AudioControl" [values] Nothing 0 (Special 0) NoId
+
+-- | All Pass Filter
+bAllPass :: UGen -> UGen -> UGen -> UGen
+bAllPass in_ freq rq = mkUGen Nothing [AR] (Right [0]) "BAllPass" [in_,freq,rq] Nothing 1 (Special 0) NoId
+
+-- | Band Pass Filter
+bBandPass :: UGen -> UGen -> UGen -> UGen
+bBandPass in_ freq bw = mkUGen Nothing [AR] (Right [0]) "BBandPass" [in_,freq,bw] Nothing 1 (Special 0) NoId
+
+-- | Band reject filter
+bBandStop :: UGen -> UGen -> UGen -> UGen
+bBandStop in_ freq bw = mkUGen Nothing [AR] (Right [0]) "BBandStop" [in_,freq,bw] Nothing 1 (Special 0) NoId
+
+-- | 12db/oct rolloff - 2nd order resonant  Hi Pass Filter
+bHiPass :: UGen -> UGen -> UGen -> UGen
+bHiPass in_ freq rq = mkUGen Nothing [AR] (Right [0]) "BHiPass" [in_,freq,rq] Nothing 1 (Special 0) NoId
+
+-- | Hi Shelf
+bHiShelf :: UGen -> UGen -> UGen -> UGen -> UGen
+bHiShelf in_ freq rs db = mkUGen Nothing [AR] (Right [0]) "BHiShelf" [in_,freq,rs,db] Nothing 1 (Special 0) NoId
+
+-- | 12db/oct rolloff - 2nd order resonant Low Pass Filter
+bLowPass :: UGen -> UGen -> UGen -> UGen
+bLowPass in_ freq rq = mkUGen Nothing [AR] (Right [0]) "BLowPass" [in_,freq,rq] Nothing 1 (Special 0) NoId
+
+-- | Low Shelf
+bLowShelf :: UGen -> UGen -> UGen -> UGen -> UGen
+bLowShelf in_ freq rs db = mkUGen Nothing [AR] (Right [0]) "BLowShelf" [in_,freq,rs,db] Nothing 1 (Special 0) NoId
+
+-- | 2nd order Butterworth bandpass filter.
+bpf :: UGen -> UGen -> UGen -> UGen
+bpf in_ freq rq = mkUGen Nothing [KR,AR] (Right [0]) "BPF" [in_,freq,rq] Nothing 1 (Special 0) NoId
+
+-- | Two zero fixed midpass.
+bpz2 :: UGen -> UGen
+bpz2 in_ = mkUGen Nothing [KR,AR] (Right [0]) "BPZ2" [in_] Nothing 1 (Special 0) NoId
+
+-- | Parametric equalizer
+bPeakEQ :: UGen -> UGen -> UGen -> UGen -> UGen
+bPeakEQ in_ freq rq db = mkUGen Nothing [AR] (Right [0]) "BPeakEQ" [in_,freq,rq,db] Nothing 1 (Special 0) NoId
+
+-- | 2nd order Butterworth band reject filter.
+brf :: UGen -> UGen -> UGen -> UGen
+brf in_ freq rq = mkUGen Nothing [KR,AR] (Right [0]) "BRF" [in_,freq,rq] Nothing 1 (Special 0) NoId
+
+-- | Two zero fixed midcut.
+brz2 :: UGen -> UGen
+brz2 in_ = mkUGen Nothing [KR,AR] (Right [0]) "BRZ2" [in_] Nothing 1 (Special 0) NoId
+
+-- | Stereo signal balancer
+balance2 :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen
+balance2 rate left right pos level = mkUGen Nothing [KR,AR] (Left rate) "Balance2" [left,right,pos,level] Nothing 2 (Special 0) NoId
+
+-- | physical model of bouncing object
+ball :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen
+ball rate in_ g damp friction = mkUGen Nothing [KR,AR] (Left rate) "Ball" [in_,g,damp,friction] Nothing 1 (Special 0) NoId
+
+-- | Autocorrelation beat tracker
+beatTrack :: Rate -> UGen -> UGen -> UGen
+beatTrack rate chain lock = mkUGen Nothing [KR] (Left rate) "BeatTrack" [chain,lock] Nothing 1 (Special 0) NoId
+
+-- | Template matching beat tracker
+beatTrack2 :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+beatTrack2 rate busindex numfeatures windowsize phaseaccuracy lock weightingscheme = mkUGen Nothing [KR] (Left rate) "BeatTrack2" [busindex,numfeatures,windowsize,phaseaccuracy,lock,weightingscheme] Nothing 6 (Special 0) NoId
+
+-- | 2D Ambisonic B-format panner.
+biPanB2 :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen
+biPanB2 rate inA inB azimuth gain = mkUGen Nothing [KR,AR] (Left rate) "BiPanB2" [inA,inB,azimuth,gain] Nothing 3 (Special 0) NoId
+
+-- | Apply a binary operation to the values of an input UGen
+binaryOpUGen :: UGen -> UGen -> UGen
+binaryOpUGen a b = mkUGen Nothing [IR,KR,AR,DR] (Right [0,1]) "BinaryOpUGen" [a,b] Nothing 1 (Special 0) NoId
+
+-- | Band limited impulse oscillator.
+blip :: Rate -> UGen -> UGen -> UGen
+blip rate freq numharm = mkUGen Nothing [KR,AR] (Left rate) "Blip" [freq,numharm] Nothing 1 (Special 0) NoId
+
+-- | (Undocumented class)
+blockSize :: UGen
+blockSize = mkUGen Nothing [IR] (Left IR) "BlockSize" [] Nothing 1 (Special 0) NoId
+
+-- | Brown Noise.
+brownNoise :: ID a => a -> Rate -> UGen
+brownNoise z rate = mkUGen Nothing [KR,AR] (Left rate) "BrownNoise" [] Nothing 1 (Special 0) (toUId z)
+
+-- | Buffer based all pass delay line with cubic interpolation.
+bufAllpassC :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen
+bufAllpassC rate buf in_ delaytime decaytime = mkUGen Nothing [AR] (Left rate) "BufAllpassC" [buf,in_,delaytime,decaytime] Nothing 1 (Special 0) NoId
+
+-- | Buffer based all pass delay line with linear interpolation.
+bufAllpassL :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen
+bufAllpassL rate buf in_ delaytime decaytime = mkUGen Nothing [AR] (Left rate) "BufAllpassL" [buf,in_,delaytime,decaytime] Nothing 1 (Special 0) NoId
+
+-- | Buffer based all pass delay line with no interpolation.
+bufAllpassN :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen
+bufAllpassN rate buf in_ delaytime decaytime = mkUGen Nothing [AR] (Left rate) "BufAllpassN" [buf,in_,delaytime,decaytime] Nothing 1 (Special 0) NoId
+
+-- | Current number of channels of soundfile in buffer.
+bufChannels :: Rate -> UGen -> UGen
+bufChannels rate bufnum = mkUGen Nothing [IR,KR] (Left rate) "BufChannels" [bufnum] Nothing 1 (Special 0) NoId
+
+-- | Buffer based comb delay line with cubic interpolation.
+bufCombC :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen
+bufCombC rate buf in_ delaytime decaytime = mkUGen Nothing [AR] (Left rate) "BufCombC" [buf,in_,delaytime,decaytime] Nothing 1 (Special 0) NoId
+
+-- | Buffer based comb delay line with linear interpolation.
+bufCombL :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen
+bufCombL rate buf in_ delaytime decaytime = mkUGen Nothing [AR] (Left rate) "BufCombL" [buf,in_,delaytime,decaytime] Nothing 1 (Special 0) NoId
+
+-- | Buffer based comb delay line with no interpolation.
+bufCombN :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen
+bufCombN rate buf in_ delaytime decaytime = mkUGen Nothing [AR] (Left rate) "BufCombN" [buf,in_,delaytime,decaytime] Nothing 1 (Special 0) NoId
+
+-- | Buffer based simple delay line with cubic interpolation.
+bufDelayC :: Rate -> UGen -> UGen -> UGen -> UGen
+bufDelayC rate buf in_ delaytime = mkUGen Nothing [KR,AR] (Left rate) "BufDelayC" [buf,in_,delaytime] Nothing 1 (Special 0) NoId
+
+-- | Buffer based simple delay line with linear interpolation.
+bufDelayL :: Rate -> UGen -> UGen -> UGen -> UGen
+bufDelayL rate buf in_ delaytime = mkUGen Nothing [KR,AR] (Left rate) "BufDelayL" [buf,in_,delaytime] Nothing 1 (Special 0) NoId
+
+-- | Buffer based simple delay line with no interpolation.
+bufDelayN :: Rate -> UGen -> UGen -> UGen -> UGen
+bufDelayN rate buf in_ delaytime = mkUGen Nothing [KR,AR] (Left rate) "BufDelayN" [buf,in_,delaytime] Nothing 1 (Special 0) NoId
+
+-- | Current duration of soundfile in buffer.
+bufDur :: Rate -> UGen -> UGen
+bufDur rate bufnum = mkUGen Nothing [IR,KR] (Left rate) "BufDur" [bufnum] Nothing 1 (Special 0) NoId
+
+-- | Current number of frames allocated in the buffer.
+bufFrames :: Rate -> UGen -> UGen
+bufFrames rate bufnum = mkUGen Nothing [IR,KR] (Left rate) "BufFrames" [bufnum] Nothing 1 (Special 0) NoId
+
+-- | Buffer rate scaling in respect to server samplerate.
+bufRateScale :: Rate -> UGen -> UGen
+bufRateScale rate bufnum = mkUGen Nothing [IR,KR] (Left rate) "BufRateScale" [bufnum] Nothing 1 (Special 0) NoId
+
+-- | Buffer reading oscillator.
+bufRd :: Int -> Rate -> UGen -> UGen -> Loop -> Interpolation -> UGen
+bufRd numChannels rate bufnum phase loop interpolation = mkUGen Nothing [KR,AR] (Left rate) "BufRd" [bufnum,phase,(from_loop loop),(from_interpolation interpolation)] Nothing numChannels (Special 0) NoId
+
+-- | Buffer sample rate.
+bufSampleRate :: Rate -> UGen -> UGen
+bufSampleRate rate bufnum = mkUGen Nothing [IR,KR] (Left rate) "BufSampleRate" [bufnum] Nothing 1 (Special 0) NoId
+
+-- | Current number of samples in buffer.
+bufSamples :: Rate -> UGen -> UGen
+bufSamples rate bufnum = mkUGen Nothing [IR,KR] (Left rate) "BufSamples" [bufnum] Nothing 1 (Special 0) NoId
+
+-- | Buffer writing oscillator.
+bufWr :: UGen -> UGen -> Loop -> UGen -> UGen
+bufWr bufnum phase loop inputArray = mkUGen Nothing [KR,AR] (Right [3]) "BufWr" [bufnum,phase,(from_loop loop)] (Just inputArray) 1 (Special 0) NoId
+
+-- | Chorusing wavetable oscillator.
+cOsc :: Rate -> UGen -> UGen -> UGen -> UGen
+cOsc rate bufnum freq beats = mkUGen Nothing [KR,AR] (Left rate) "COsc" [bufnum,freq,beats] Nothing 1 (Special 0) NoId
+
+-- | Test for infinity, not-a-number, and denormals
+checkBadValues :: UGen -> UGen -> UGen -> UGen
+checkBadValues in_ id_ post = mkUGen Nothing [KR,AR] (Right [0]) "CheckBadValues" [in_,id_,post] Nothing 1 (Special 0) NoId
+
+-- | Clip a signal outside given thresholds.
+clip :: UGen -> UGen -> UGen -> UGen
+clip in_ lo hi = mkUGen Nothing [IR,KR,AR] (Right [0]) "Clip" [in_,lo,hi] Nothing 1 (Special 0) NoId
+
+-- | Clip Noise.
+clipNoise :: ID a => a -> Rate -> UGen
+clipNoise z rate = mkUGen Nothing [KR,AR] (Left rate) "ClipNoise" [] Nothing 1 (Special 0) (toUId z)
+
+-- | Statistical gate.
+coinGate :: ID a => a -> UGen -> UGen -> UGen
+coinGate z prob in_ = mkUGen Nothing [KR,AR] (Right [1]) "CoinGate" [prob,in_] Nothing 1 (Special 0) (toUId z)
+
+-- | Comb delay line with cubic interpolation.
+combC :: UGen -> UGen -> UGen -> UGen -> UGen
+combC in_ maxdelaytime delaytime decaytime = mkUGen Nothing [KR,AR] (Right [0]) "CombC" [in_,maxdelaytime,delaytime,decaytime] Nothing 1 (Special 0) NoId
+
+-- | Comb delay line with linear interpolation.
+combL :: UGen -> UGen -> UGen -> UGen -> UGen
+combL in_ maxdelaytime delaytime decaytime = mkUGen Nothing [KR,AR] (Right [0]) "CombL" [in_,maxdelaytime,delaytime,decaytime] Nothing 1 (Special 0) NoId
+
+-- | Comb delay line with no interpolation.
+combN :: UGen -> UGen -> UGen -> UGen -> UGen
+combN in_ maxdelaytime delaytime decaytime = mkUGen Nothing [KR,AR] (Right [0]) "CombN" [in_,maxdelaytime,delaytime,decaytime] Nothing 1 (Special 0) NoId
+
+-- | Compressor, expander, limiter, gate, ducker
+compander :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+compander in_ control_ thresh slopeBelow slopeAbove clampTime relaxTime = mkUGen Nothing [AR] (Right [0]) "Compander" [in_,control_,thresh,slopeBelow,slopeAbove,clampTime,relaxTime] Nothing 1 (Special 0) NoId
+
+-- | Compressor, expander, limiter, gate, ducker.
+companderD :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+companderD rate in_ thresh slopeBelow slopeAbove clampTime relaxTime = mkUGen Nothing [AR] (Left rate) "CompanderD" [in_,thresh,slopeBelow,slopeAbove,clampTime,relaxTime] Nothing 1 (Special 0) NoId
+
+-- | Duration of one block
+controlDur :: UGen
+controlDur = mkUGen Nothing [IR] (Left IR) "ControlDur" [] Nothing 1 (Special 0) NoId
+
+-- | Server control rate.
+controlRate :: UGen
+controlRate = mkUGen Nothing [IR] (Left IR) "ControlRate" [] Nothing 1 (Special 0) NoId
+
+-- | Real-time convolver.
+convolution :: Rate -> UGen -> UGen -> UGen -> UGen
+convolution rate in_ kernel framesize = mkUGen Nothing [AR] (Left rate) "Convolution" [in_,kernel,framesize] Nothing 1 (Special 0) NoId
+
+-- | Real-time fixed kernel convolver.
+convolution2 :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen
+convolution2 rate in_ kernel trigger framesize = mkUGen Nothing [AR] (Left rate) "Convolution2" [in_,kernel,trigger,framesize] Nothing 1 (Special 0) NoId
+
+-- | Real-time convolver with linear interpolation
+convolution2L :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+convolution2L rate in_ kernel trigger framesize crossfade = mkUGen Nothing [AR] (Left rate) "Convolution2L" [in_,kernel,trigger,framesize,crossfade] Nothing 1 (Special 0) NoId
+
+-- | Time based convolver.
+convolution3 :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen
+convolution3 rate in_ kernel trigger framesize = mkUGen Nothing [KR,AR] (Left rate) "Convolution3" [in_,kernel,trigger,framesize] Nothing 1 (Special 0) NoId
+
+-- | Chaotic noise function.
+crackle :: Rate -> UGen -> UGen
+crackle rate chaosParam = mkUGen Nothing [KR,AR] (Left rate) "Crackle" [chaosParam] Nothing 1 (Special 0) NoId
+
+-- | Cusp map chaotic generator
+cuspL :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen
+cuspL rate freq a b xi = mkUGen Nothing [AR] (Left rate) "CuspL" [freq,a,b,xi] Nothing 1 (Special 0) NoId
+
+-- | Cusp map chaotic generator
+cuspN :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen
+cuspN rate freq a b xi = mkUGen Nothing [AR] (Left rate) "CuspN" [freq,a,b,xi] Nothing 1 (Special 0) NoId
+
+-- | Create a constant amplitude signal
+dc :: Rate -> UGen -> UGen
+dc rate in_ = mkUGen Nothing [KR,AR] (Left rate) "DC" [in_] Nothing 1 (Special 0) NoId
+
+-- | Demand rate brownian movement generator.
+dbrown :: ID a => a -> UGen -> UGen -> UGen -> UGen -> UGen
+dbrown z length_ lo hi step = mkUGen Nothing [DR] (Left DR) "Dbrown" [length_,lo,hi,step] Nothing 1 (Special 0) (toUId z)
+
+-- | Buffer read demand ugen
+dbufrd :: ID a => a -> UGen -> UGen -> Loop -> UGen
+dbufrd z bufnum phase loop = mkUGen Nothing [DR] (Left DR) "Dbufrd" [bufnum,phase,(from_loop loop)] Nothing 1 (Special 0) (toUId z)
+
+-- | Buffer write demand ugen
+dbufwr :: ID a => a -> UGen -> UGen -> UGen -> Loop -> UGen
+dbufwr z bufnum phase loop input = mkUGen Nothing [DR] (Left DR) "Dbufwr" [bufnum,phase,loop,(from_loop input)] Nothing 1 (Special 0) (toUId z)
+
+-- | Exponential decay
+decay :: UGen -> UGen -> UGen
+decay in_ decayTime = mkUGen Nothing [KR,AR] (Right [0]) "Decay" [in_,decayTime] Nothing 1 (Special 0) NoId
+
+-- | Exponential decay
+decay2 :: UGen -> UGen -> UGen -> UGen
+decay2 in_ attackTime decayTime = mkUGen Nothing [KR,AR] (Right [0]) "Decay2" [in_,attackTime,decayTime] Nothing 1 (Special 0) NoId
+
+-- | 2D Ambisonic B-format decoder.
+decodeB2 :: Int -> Rate -> UGen -> UGen -> UGen -> UGen -> UGen
+decodeB2 numChannels rate w x y orientation = mkUGen Nothing [KR,AR] (Left rate) "DecodeB2" [w,x,y,orientation] Nothing numChannels (Special 0) NoId
+
+-- | Convert signal to modal pitch.
+degreeToKey :: UGen -> UGen -> UGen -> UGen
+degreeToKey bufnum in_ octave = mkUGen Nothing [KR,AR] (Right [1]) "DegreeToKey" [bufnum,in_,octave] Nothing 1 (Special 0) NoId
+
+-- | Tap a delay line from a DelTapWr UGen
+delTapRd :: UGen -> UGen -> UGen -> UGen -> UGen
+delTapRd buffer phase delTime interp = mkUGen Nothing [KR,AR] (Right [1]) "DelTapRd" [buffer,phase,delTime,interp] Nothing 1 (Special 0) NoId
+
+-- | Write to a buffer for a DelTapRd UGen
+delTapWr :: UGen -> UGen -> UGen
+delTapWr buffer in_ = mkUGen Nothing [KR,AR] (Right [1]) "DelTapWr" [buffer,in_] Nothing 1 (Special 0) NoId
+
+-- | Single sample delay.
+delay1 :: UGen -> UGen
+delay1 in_ = mkUGen Nothing [KR,AR] (Right [0]) "Delay1" [in_] Nothing 1 (Special 0) NoId
+
+-- | Two sample delay.
+delay2 :: UGen -> UGen
+delay2 in_ = mkUGen Nothing [KR,AR] (Right [0]) "Delay2" [in_] Nothing 1 (Special 0) NoId
+
+-- | Simple delay line with cubic interpolation.
+delayC :: UGen -> UGen -> UGen -> UGen
+delayC in_ maxdelaytime delaytime = mkUGen Nothing [KR,AR] (Right [0]) "DelayC" [in_,maxdelaytime,delaytime] Nothing 1 (Special 0) NoId
+
+-- | Simple delay line with linear interpolation.
+delayL :: UGen -> UGen -> UGen -> UGen
+delayL in_ maxdelaytime delaytime = mkUGen Nothing [KR,AR] (Right [0]) "DelayL" [in_,maxdelaytime,delaytime] Nothing 1 (Special 0) NoId
+
+-- | Simple delay line with no interpolation.
+delayN :: UGen -> UGen -> UGen -> UGen
+delayN in_ maxdelaytime delaytime = mkUGen Nothing [KR,AR] (Right [0]) "DelayN" [in_,maxdelaytime,delaytime] Nothing 1 (Special 0) NoId
+
+-- | Demand results from demand rate UGens.
+demand :: UGen -> UGen -> UGen -> UGen
+demand trig_ reset demandUGens = mkUGen Nothing [KR,AR] (Right [0]) "Demand" [trig_,reset] (Just demandUGens) (length (mceChannels demandUGens) + 0) (Special 0) NoId
+
+-- | Demand rate envelope generator
+demandEnvGen :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> DoneAction -> UGen
+demandEnvGen rate level dur shape curve gate_ reset levelScale levelBias timeScale doneAction = mkUGen Nothing [KR,AR] (Left rate) "DemandEnvGen" [level,dur,shape,curve,gate_,reset,levelScale,levelBias,timeScale,(from_done_action doneAction)] Nothing 1 (Special 0) NoId
+
+-- | Search a buffer for a value
+detectIndex :: UGen -> UGen -> UGen
+detectIndex bufnum in_ = mkUGen Nothing [KR,AR] (Right [1]) "DetectIndex" [bufnum,in_] Nothing 1 (Special 0) NoId
+
+-- | When input falls below a threshhold, evaluate doneAction.
+detectSilence :: UGen -> UGen -> UGen -> DoneAction -> UGen
+detectSilence in_ amp time doneAction = mkUGen Nothing [KR,AR] (Right [0]) "DetectSilence" [in_,amp,time,(from_done_action doneAction)] Nothing 1 (Special 0) NoId
+
+-- | Demand rate geometric series UGen.
+dgeom :: ID a => a -> UGen -> UGen -> UGen -> UGen
+dgeom z length_ start grow = mkUGen Nothing [DR] (Left DR) "Dgeom" [length_,start,grow] Nothing 1 (Special 0) (toUId z)
+
+-- | Demand rate brownian movement generator.
+dibrown :: ID a => a -> UGen -> UGen -> UGen -> UGen -> UGen
+dibrown z length_ lo hi step = mkUGen Nothing [DR] (Left DR) "Dibrown" [length_,lo,hi,step] Nothing 1 (Special 0) (toUId z)
+
+-- | Stream in audio from a file.
+diskIn :: Int -> UGen -> Loop -> UGen
+diskIn numChannels bufnum loop = mkUGen Nothing [AR] (Left AR) "DiskIn" [bufnum,(from_loop loop)] Nothing numChannels (Special 0) NoId
+
+-- | Record to a soundfile to disk.
+diskOut :: UGen -> UGen -> UGen
+diskOut bufnum input = mkUGen Nothing [AR] (Left AR) "DiskOut" [bufnum] (Just input) 1 (Special 0) NoId
+
+-- | Demand rate white noise random generator.
+diwhite :: ID a => a -> UGen -> UGen -> UGen -> UGen
+diwhite z length_ lo hi = mkUGen Nothing [DR] (Left DR) "Diwhite" [length_,lo,hi] Nothing 1 (Special 0) (toUId z)
+
+-- | (Undocumented class)
+donce :: ID a => a -> UGen -> UGen
+donce z in_ = mkUGen Nothing [DR] (Left DR) "Donce" [in_] Nothing 1 (Special 0) (toUId z)
+
+-- | Monitors another UGen to see when it is finished
+done :: Rate -> UGen -> UGen
+done rate src = mkUGen Nothing [KR] (Left rate) "Done" [src] Nothing 1 (Special 0) NoId
+
+-- | Print the current output value of a demand rate UGen
+dpoll :: ID a => a -> UGen -> UGen -> UGen -> UGen -> UGen
+dpoll z in_ label_ run trigid = mkUGen Nothing [DR] (Left DR) "Dpoll" [in_,label_,run,trigid] Nothing 1 (Special 0) (toUId z)
+
+-- | Demand rate random sequence generator.
+drand :: ID a => a -> UGen -> UGen -> UGen
+drand z repeats list_ = mkUGen Nothing [DR] (Left DR) "Drand" [repeats] (Just list_) 1 (Special 0) (toUId z)
+
+-- | demand rate reset
+dreset :: ID a => a -> UGen -> UGen -> UGen
+dreset z in_ reset = mkUGen Nothing [DR] (Left DR) "Dreset" [in_,reset] Nothing 1 (Special 0) (toUId z)
+
+-- | Demand rate sequence generator.
+dseq :: ID a => a -> UGen -> UGen -> UGen
+dseq z repeats list_ = mkUGen Nothing [DR] (Left DR) "Dseq" [repeats] (Just list_) 1 (Special 0) (toUId z)
+
+-- | Demand rate sequence generator.
+dser :: ID a => a -> UGen -> UGen -> UGen
+dser z repeats list_ = mkUGen Nothing [DR] (Left DR) "Dser" [repeats] (Just list_) 1 (Special 0) (toUId z)
+
+-- | Demand rate arithmetic series UGen.
+dseries :: ID a => a -> UGen -> UGen -> UGen -> UGen
+dseries z length_ start step = mkUGen Nothing [DR] (Left DR) "Dseries" [length_,start,step] Nothing 1 (Special 0) (toUId z)
+
+-- | Demand rate random sequence generator
+dshuf :: ID a => a -> UGen -> UGen -> UGen
+dshuf z repeats list_ = mkUGen Nothing [DR] (Left DR) "Dshuf" [repeats] (Just list_) 1 (Special 0) (toUId z)
+
+-- | Demand rate input replicator
+dstutter :: ID a => a -> UGen -> UGen -> UGen
+dstutter z n in_ = mkUGen Nothing [DR] (Left DR) "Dstutter" [n,in_] Nothing 1 (Special 0) (toUId z)
+
+-- | Demand rate generator for embedding different inputs
+dswitch :: ID a => a -> UGen -> UGen -> UGen
+dswitch z index_ list_ = mkUGen Nothing [DR] (Left DR) "Dswitch" [index_] (Just list_) 1 (Special 0) (toUId z)
+
+-- | Demand rate generator for switching between inputs.
+dswitch1 :: ID a => a -> UGen -> UGen -> UGen
+dswitch1 z index_ list_ = mkUGen Nothing [DR] (Left DR) "Dswitch1" [index_] (Just list_) 1 (Special 0) (toUId z)
+
+-- | Return the same unique series of values for several demand streams
+dunique :: ID a => a -> UGen -> UGen -> UGen -> UGen
+dunique z source maxBufferSize protected = mkUGen Nothing [DR] (Left DR) "Dunique" [source,maxBufferSize,protected] Nothing 1 (Special 0) (toUId z)
+
+-- | Random impulses.
+dust :: ID a => a -> Rate -> UGen -> UGen
+dust z rate density = mkUGen Nothing [KR,AR] (Left rate) "Dust" [density] Nothing 1 (Special 0) (toUId z)
+
+-- | Random impulses.
+dust2 :: ID a => a -> Rate -> UGen -> UGen
+dust2 z rate density = mkUGen Nothing [KR,AR] (Left rate) "Dust2" [density] Nothing 1 (Special 0) (toUId z)
+
+-- | Demand results from demand rate UGens.
+duty :: Rate -> UGen -> UGen -> DoneAction -> UGen -> UGen
+duty rate dur reset doneAction level = mkUGen Nothing [KR,AR] (Left rate) "Duty" [dur,reset,(from_done_action doneAction),level] Nothing 1 (Special 0) NoId
+
+-- | Demand rate white noise random generator.
+dwhite :: ID a => a -> UGen -> UGen -> UGen -> UGen
+dwhite z length_ lo hi = mkUGen Nothing [DR] (Left DR) "Dwhite" [length_,lo,hi] Nothing 1 (Special 0) (toUId z)
+
+-- | Demand rate weighted random sequence generator
+-- dwrand :: ID a => a -> UGen -> UGen -> UGen -> UGen
+-- dwrand z repeats weights list_ = mkUGen Nothing [DR] (Left DR) "Dwrand" [repeats,weights] (Just list_) 1 (Special 0) (toUId z)
+
+-- | Demand rate random sequence generator.
+dxrand :: ID a => a -> UGen -> UGen -> UGen
+dxrand z repeats list_ = mkUGen Nothing [DR] (Left DR) "Dxrand" [repeats] (Just list_) 1 (Special 0) (toUId z)
+
+-- | Envelope generator
+envGen :: Rate -> UGen -> UGen -> UGen -> UGen -> DoneAction -> Envelope UGen -> UGen
+envGen rate gate_ levelScale levelBias timeScale doneAction envelope_ = mkUGen Nothing [KR,AR] (Left rate) "EnvGen" [gate_,levelScale,levelBias,timeScale,(from_done_action doneAction)] (Just (envelope_to_ugen envelope_)) 1 (Special 0) NoId
+
+-- | Exponential single random number generator.
+expRand :: ID a => a -> UGen -> UGen -> UGen
+expRand z lo hi = mkUGen Nothing [IR] (Right [0,1]) "ExpRand" [lo,hi] Nothing 1 (Special 0) (toUId z)
+
+-- | Feedback sine with chaotic phase indexing
+fBSineC :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+fBSineC rate freq im fb a c xi yi = mkUGen Nothing [AR] (Left rate) "FBSineC" [freq,im,fb,a,c,xi,yi] Nothing 1 (Special 0) NoId
+
+-- | Feedback sine with chaotic phase indexing
+fBSineL :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+fBSineL rate freq im fb a c xi yi = mkUGen Nothing [AR] (Left rate) "FBSineL" [freq,im,fb,a,c,xi,yi] Nothing 1 (Special 0) NoId
+
+-- | Feedback sine with chaotic phase indexing
+fBSineN :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+fBSineN rate freq im fb a c xi yi = mkUGen Nothing [AR] (Left rate) "FBSineN" [freq,im,fb,a,c,xi,yi] Nothing 1 (Special 0) NoId
+
+-- | Fast Fourier Transform
+fft :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+fft buffer in_ hop wintype active winsize = mkUGen Nothing [KR] (Left KR) "FFT" [buffer,in_,hop,wintype,active,winsize] Nothing 1 (Special 0) NoId
+
+-- | First order filter section.
+fos :: UGen -> UGen -> UGen -> UGen -> UGen
+fos in_ a0 a1 b1 = mkUGen Nothing [KR,AR] (Right [0]) "FOS" [in_,a0,a1,b1] Nothing 1 (Special 0) NoId
+
+-- | Fast sine oscillator.
+fSinOsc :: Rate -> UGen -> UGen -> UGen
+fSinOsc rate freq iphase = mkUGen Nothing [KR,AR] (Left rate) "FSinOsc" [freq,iphase] Nothing 1 (Special 0) NoId
+
+-- | Fold a signal outside given thresholds.
+fold :: UGen -> UGen -> UGen -> UGen
+fold in_ lo hi = mkUGen Nothing [IR,KR,AR] (Right [0]) "Fold" [in_,lo,hi] Nothing 1 (Special 0) NoId
+
+-- | Formant oscillator
+formant :: Rate -> UGen -> UGen -> UGen -> UGen
+formant rate fundfreq formfreq bwfreq = mkUGen Nothing [AR] (Left rate) "Formant" [fundfreq,formfreq,bwfreq] Nothing 1 (Special 0) NoId
+
+-- | FOF-like filter.
+formlet :: UGen -> UGen -> UGen -> UGen -> UGen
+formlet in_ freq attacktime decaytime = mkUGen Nothing [KR,AR] (Right [0]) "Formlet" [in_,freq,attacktime,decaytime] Nothing 1 (Special 0) NoId
+
+-- | When triggered, frees a node.
+free :: UGen -> UGen -> UGen
+free trig_ id_ = mkUGen Nothing [KR] (Right [0]) "Free" [trig_,id_] Nothing 1 (Special 0) NoId
+
+-- | When triggered, free enclosing synth.
+freeSelf :: UGen -> UGen
+freeSelf in_ = mkUGen Nothing [KR] (Left KR) "FreeSelf" [in_] Nothing 1 (Special 0) NoId
+
+-- | Free the enclosing synth when a UGen is finished
+freeSelfWhenDone :: Rate -> UGen -> UGen
+freeSelfWhenDone rate src = mkUGen Nothing [KR] (Left rate) "FreeSelfWhenDone" [src] Nothing 1 (Special 0) NoId
+
+-- | A reverb
+freeVerb :: UGen -> UGen -> UGen -> UGen -> UGen
+freeVerb in_ mix room damp = mkUGen Nothing [AR] (Right [0]) "FreeVerb" [in_,mix,room,damp] Nothing 1 (Special 0) NoId
+
+-- | A two-channel reverb
+freeVerb2 :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+freeVerb2 in_ in2 mix room damp = mkUGen Nothing [AR] (Right [0]) "FreeVerb2" [in_,in2,mix,room,damp] Nothing 2 (Special 0) NoId
+
+-- | Frequency Shifter.
+freqShift :: UGen -> UGen -> UGen -> UGen
+freqShift in_ freq phase = mkUGen Nothing [AR] (Left AR) "FreqShift" [in_,freq,phase] Nothing 1 (Special 0) NoId
+
+-- | A two-channel reverb
+gVerb :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+gVerb in_ roomsize revtime damping inputbw spread drylevel earlyreflevel taillevel maxroomsize = mkUGen Nothing [AR] (Right [0]) "GVerb" [in_,roomsize,revtime,damping,inputbw,spread,drylevel,earlyreflevel,taillevel,maxroomsize] Nothing 2 (Special 0) NoId
+
+-- | Gate or hold.
+gate :: UGen -> UGen -> UGen
+gate in_ trig_ = mkUGen Nothing [KR,AR] (Right [0]) "Gate" [in_,trig_] Nothing 1 (Special 0) NoId
+
+-- | Gingerbreadman map chaotic generator
+gbmanL :: Rate -> UGen -> UGen -> UGen -> UGen
+gbmanL rate freq xi yi = mkUGen Nothing [AR] (Left rate) "GbmanL" [freq,xi,yi] Nothing 1 (Special 0) NoId
+
+-- | Gingerbreadman map chaotic generator
+gbmanN :: Rate -> UGen -> UGen -> UGen -> UGen
+gbmanN rate freq xi yi = mkUGen Nothing [AR] (Left rate) "GbmanN" [freq,xi,yi] Nothing 1 (Special 0) NoId
+
+-- | Dynamic stochastic synthesis generator.
+gendy1 :: ID a => a -> Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+gendy1 z rate ampdist durdist adparam ddparam minfreq maxfreq ampscale durscale initCPs knum = mkUGen Nothing [KR,AR] (Left rate) "Gendy1" [ampdist,durdist,adparam,ddparam,minfreq,maxfreq,ampscale,durscale,initCPs,knum] Nothing 1 (Special 0) (toUId z)
+
+-- | Dynamic stochastic synthesis generator.
+gendy2 :: ID a => a -> Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+gendy2 z rate ampdist durdist adparam ddparam minfreq maxfreq ampscale durscale initCPs knum a c = mkUGen Nothing [KR,AR] (Left rate) "Gendy2" [ampdist,durdist,adparam,ddparam,minfreq,maxfreq,ampscale,durscale,initCPs,knum,a,c] Nothing 1 (Special 0) (toUId z)
+
+-- | Dynamic stochastic synthesis generator.
+gendy3 :: ID a => a -> Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+gendy3 z rate ampdist durdist adparam ddparam freq ampscale durscale initCPs knum = mkUGen Nothing [KR,AR] (Left rate) "Gendy3" [ampdist,durdist,adparam,ddparam,freq,ampscale,durscale,initCPs,knum] Nothing 1 (Special 0) (toUId z)
+
+-- | Granular synthesis with sound stored in a buffer
+grainBuf :: Int -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+grainBuf numChannels trigger dur sndbuf rate_ pos interp pan envbufnum maxGrains = mkUGen Nothing [AR] (Left AR) "GrainBuf" [trigger,dur,sndbuf,rate_,pos,interp,pan,envbufnum,maxGrains] Nothing numChannels (Special 0) NoId
+
+-- | Granular synthesis with frequency modulated sine tones
+grainFM :: Int -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+grainFM numChannels trigger dur carfreq modfreq index_ pan envbufnum maxGrains = mkUGen Nothing [AR] (Left AR) "GrainFM" [trigger,dur,carfreq,modfreq,index_,pan,envbufnum,maxGrains] Nothing numChannels (Special 0) NoId
+
+-- | Granulate an input signal
+grainIn :: Int -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+grainIn numChannels trigger dur in_ pan envbufnum maxGrains = mkUGen Nothing [AR] (Left AR) "GrainIn" [trigger,dur,in_,pan,envbufnum,maxGrains] Nothing numChannels (Special 0) NoId
+
+-- | Granular synthesis with sine tones
+grainSin :: Int -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+grainSin numChannels trigger dur freq pan envbufnum maxGrains = mkUGen Nothing [AR] (Left AR) "GrainSin" [trigger,dur,freq,pan,envbufnum,maxGrains] Nothing numChannels (Special 0) NoId
+
+-- | Gray Noise.
+grayNoise :: ID a => a -> Rate -> UGen
+grayNoise z rate = mkUGen Nothing [KR,AR] (Left rate) "GrayNoise" [] Nothing 1 (Special 0) (toUId z)
+
+-- | 2nd order Butterworth highpass filter.
+hpf :: UGen -> UGen -> UGen
+hpf in_ freq = mkUGen Nothing [KR,AR] (Right [0]) "HPF" [in_,freq] Nothing 1 (Special 0) NoId
+
+-- | Two point difference filter
+hpz1 :: UGen -> UGen
+hpz1 in_ = mkUGen Nothing [KR,AR] (Right [0]) "HPZ1" [in_] Nothing 1 (Special 0) NoId
+
+-- | Two zero fixed midcut.
+hPZ2 :: UGen -> UGen
+hPZ2 in_ = mkUGen Nothing [KR,AR] (Right [0]) "HPZ2" [in_] Nothing 1 (Special 0) NoId
+
+-- | Randomized value.
+hasher :: UGen -> UGen
+hasher in_ = mkUGen Nothing [KR,AR] (Right [0]) "Hasher" [in_] Nothing 1 (Special 0) NoId
+
+-- | Henon map chaotic generator
+henonC :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+henonC rate freq a b x0 x1 = mkUGen Nothing [AR] (Left rate) "HenonC" [freq,a,b,x0,x1] Nothing 1 (Special 0) NoId
+
+-- | Henon map chaotic generator
+henonL :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+henonL rate freq a b x0 x1 = mkUGen Nothing [AR] (Left rate) "HenonL" [freq,a,b,x0,x1] Nothing 1 (Special 0) NoId
+
+-- | Henon map chaotic generator
+henonN :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+henonN rate freq a b x0 x1 = mkUGen Nothing [AR] (Left rate) "HenonN" [freq,a,b,x0,x1] Nothing 1 (Special 0) NoId
+
+-- | Applies the Hilbert transform to an input signal.
+hilbert :: UGen -> UGen
+hilbert in_ = mkUGen Nothing [AR] (Right [0]) "Hilbert" [in_] Nothing 2 (Special 0) NoId
+
+-- | Applies the Hilbert transform to an input signal.
+hilbertFIR :: Rate -> UGen -> UGen -> UGen
+hilbertFIR rate in_ buffer = mkUGen Nothing [AR] (Left rate) "HilbertFIR" [in_,buffer] Nothing 2 (Special 0) NoId
+
+-- | Envelope generator for polling values from an Env
+iEnvGen :: Rate -> UGen -> Envelope UGen -> UGen
+iEnvGen rate index_ envelope_ = mkUGen Nothing [KR,AR] (Left rate) "IEnvGen" [index_] (Just (envelope_to_ugen envelope_)) 1 (Special 0) NoId
+
+-- | Inverse Fast Fourier Transform
+ifft :: UGen -> UGen -> UGen -> UGen
+ifft buffer wintype winsize = mkUGen Nothing [KR,AR] (Left AR) "IFFT" [buffer,wintype,winsize] Nothing 1 (Special 0) NoId
+
+-- | Single integer random number generator.
+iRand :: ID a => a -> UGen -> UGen -> UGen
+iRand z lo hi = mkUGen Nothing [IR] (Left IR) "IRand" [lo,hi] Nothing 1 (Special 0) (toUId z)
+
+-- | Impulse oscillator.
+impulse :: Rate -> UGen -> UGen -> UGen
+impulse rate freq phase = mkUGen Nothing [KR,AR] (Left rate) "Impulse" [freq,phase] Nothing 1 (Special 0) NoId
+
+-- | Read a signal from a bus.
+in' :: Int -> Rate -> UGen -> UGen
+in' numChannels rate bus = mkUGen Nothing [KR,AR] (Left rate) "In" [bus] Nothing numChannels (Special 0) NoId
+
+-- | Read signal from a bus with a current or one cycle old timestamp.
+inFeedback :: Int -> UGen -> UGen
+inFeedback numChannels bus = mkUGen Nothing [AR] (Left AR) "InFeedback" [bus] Nothing numChannels (Special 0) NoId
+
+-- | Tests if a signal is within a given range.
+inRange :: UGen -> UGen -> UGen -> UGen
+inRange in_ lo hi = mkUGen Nothing [IR,KR,AR] (Right [0]) "InRange" [in_,lo,hi] Nothing 1 (Special 0) NoId
+
+-- | Test if a point is within a given rectangle.
+inRect :: Rate -> UGen -> UGen -> UGen -> UGen
+inRect rate x y rect = mkUGen Nothing [KR,AR] (Left rate) "InRect" [x,y,rect] Nothing 1 (Special 0) NoId
+
+-- | Generate a trigger anytime a bus is set.
+inTrig :: Int -> Rate -> UGen -> UGen
+inTrig numChannels rate bus = mkUGen Nothing [KR] (Left rate) "InTrig" [bus] Nothing numChannels (Special 0) NoId
+
+-- | Index into a table with a signal
+index :: UGen -> UGen -> UGen
+index bufnum in_ = mkUGen Nothing [KR,AR] (Right [1]) "Index" [bufnum,in_] Nothing 1 (Special 0) NoId
+
+-- | Finds the (lowest) point in the Buffer at which the input signal lies in-between the two values
+indexInBetween :: Rate -> UGen -> UGen -> UGen
+indexInBetween rate bufnum in_ = mkUGen Nothing [KR,AR] (Left rate) "IndexInBetween" [bufnum,in_] Nothing 1 (Special 0) NoId
+
+-- | Index into a table with a signal, linear interpolated
+indexL :: Rate -> UGen -> UGen -> UGen
+indexL rate bufnum in_ = mkUGen Nothing [KR,AR] (Left rate) "IndexL" [bufnum,in_] Nothing 1 (Special 0) NoId
+
+-- | Base class for info ugens
+infoUGenBase :: Rate -> UGen
+infoUGenBase rate = mkUGen Nothing [IR] (Left rate) "InfoUGenBase" [] Nothing 1 (Special 0) NoId
+
+-- | A leaky integrator.
+integrator :: UGen -> UGen -> UGen
+integrator in_ coef = mkUGen Nothing [KR,AR] (Right [0]) "Integrator" [in_,coef] Nothing 1 (Special 0) NoId
+
+-- | Control to audio rate converter.
+k2A :: UGen -> UGen
+k2A in_ = mkUGen Nothing [AR] (Left AR) "K2A" [in_] Nothing 1 (Special 0) NoId
+
+-- | Respond to the state of a key
+keyState :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen
+keyState rate keycode minval maxval lag_ = mkUGen Nothing [KR] (Left rate) "KeyState" [keycode,minval,maxval,lag_] Nothing 1 (Special 0) NoId
+
+-- | Key tracker
+keyTrack :: Rate -> UGen -> UGen -> UGen -> UGen
+keyTrack rate chain keydecay chromaleak = mkUGen Nothing [KR] (Left rate) "KeyTrack" [chain,keydecay,chromaleak] Nothing 1 (Special 0) NoId
+
+-- | Sine oscillator bank
+klang :: Rate -> UGen -> UGen -> UGen -> UGen
+klang rate freqscale freqoffset specificationsArrayRef = mkUGen Nothing [AR] (Left rate) "Klang" [freqscale,freqoffset] (Just specificationsArrayRef) 1 (Special 0) NoId
+
+-- | Bank of resonators
+klank :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+klank input freqscale freqoffset decayscale specificationsArrayRef = mkUGen Nothing [AR] (Right [0]) "Klank" [input,freqscale,freqoffset,decayscale] (Just specificationsArrayRef) 1 (Special 0) NoId
+
+-- | Clipped noise
+lfClipNoise :: ID a => a -> Rate -> UGen -> UGen
+lfClipNoise z rate freq = mkUGen Nothing [KR,AR] (Left rate) "LFClipNoise" [freq] Nothing 1 (Special 0) (toUId z)
+
+-- | A sine like shape made of two cubic pieces
+lfCub :: Rate -> UGen -> UGen -> UGen
+lfCub rate freq iphase = mkUGen Nothing [KR,AR] (Left rate) "LFCub" [freq,iphase] Nothing 1 (Special 0) NoId
+
+-- | Dynamic clipped noise
+lfdClipNoise :: ID a => a -> Rate -> UGen -> UGen
+lfdClipNoise z rate freq = mkUGen Nothing [KR,AR] (Left rate) "LFDClipNoise" [freq] Nothing 1 (Special 0) (toUId z)
+
+-- | Dynamic step noise
+lfdNoise0 :: ID a => a -> Rate -> UGen -> UGen
+lfdNoise0 z rate freq = mkUGen Nothing [KR,AR] (Left rate) "LFDNoise0" [freq] Nothing 1 (Special 0) (toUId z)
+
+-- | Dynamic ramp noise
+lfdNoise1 :: ID a => a -> Rate -> UGen -> UGen
+lfdNoise1 z rate freq = mkUGen Nothing [KR,AR] (Left rate) "LFDNoise1" [freq] Nothing 1 (Special 0) (toUId z)
+
+-- | Dynamic cubic noise
+lfdNoise3 :: ID a => a -> Rate -> UGen -> UGen
+lfdNoise3 z rate freq = mkUGen Nothing [KR,AR] (Left rate) "LFDNoise3" [freq] Nothing 1 (Special 0) (toUId z)
+
+-- | Gaussian function oscillator
+lfGauss :: Rate -> UGen -> UGen -> UGen -> Loop -> DoneAction -> UGen
+lfGauss rate duration width iphase loop doneAction = mkUGen Nothing [KR,AR] (Left rate) "LFGauss" [duration,width,iphase,(from_loop loop),(from_done_action doneAction)] Nothing 1 (Special 0) NoId
+
+-- | Step noise
+lfNoise0 :: ID a => a -> Rate -> UGen -> UGen
+lfNoise0 z rate freq = mkUGen Nothing [KR,AR] (Left rate) "LFNoise0" [freq] Nothing 1 (Special 0) (toUId z)
+
+-- | Ramp noise
+lfNoise1 :: ID a => a -> Rate -> UGen -> UGen
+lfNoise1 z rate freq = mkUGen Nothing [KR,AR] (Left rate) "LFNoise1" [freq] Nothing 1 (Special 0) (toUId z)
+
+-- | Quadratic noise.
+lfNoise2 :: ID a => a -> Rate -> UGen -> UGen
+lfNoise2 z rate freq = mkUGen Nothing [KR,AR] (Left rate) "LFNoise2" [freq] Nothing 1 (Special 0) (toUId z)
+
+-- | Parabolic oscillator
+lfPar :: Rate -> UGen -> UGen -> UGen
+lfPar rate freq iphase = mkUGen Nothing [KR,AR] (Left rate) "LFPar" [freq,iphase] Nothing 1 (Special 0) NoId
+
+-- | pulse oscillator
+lfPulse :: Rate -> UGen -> UGen -> UGen -> UGen
+lfPulse rate freq iphase width = mkUGen Nothing [KR,AR] (Left rate) "LFPulse" [freq,iphase,width] Nothing 1 (Special 0) NoId
+
+-- | Sawtooth oscillator
+lfSaw :: Rate -> UGen -> UGen -> UGen
+lfSaw rate freq iphase = mkUGen Nothing [KR,AR] (Left rate) "LFSaw" [freq,iphase] Nothing 1 (Special 0) NoId
+
+-- | Triangle oscillator
+lfTri :: Rate -> UGen -> UGen -> UGen
+lfTri rate freq iphase = mkUGen Nothing [KR,AR] (Left rate) "LFTri" [freq,iphase] Nothing 1 (Special 0) NoId
+
+-- | 2nd order Butterworth lowpass filter
+lpf :: UGen -> UGen -> UGen
+lpf in_ freq = mkUGen Nothing [KR,AR] (Right [0]) "LPF" [in_,freq] Nothing 1 (Special 0) NoId
+
+-- | Two point average filter
+lpz1 :: UGen -> UGen
+lpz1 in_ = mkUGen Nothing [KR,AR] (Right [0]) "LPZ1" [in_] Nothing 1 (Special 0) NoId
+
+-- | Two zero fixed lowpass
+lPZ2 :: UGen -> UGen
+lPZ2 in_ = mkUGen Nothing [KR,AR] (Right [0]) "LPZ2" [in_] Nothing 1 (Special 0) NoId
+
+-- | Exponential lag
+lag :: UGen -> UGen -> UGen
+lag in_ lagTime = mkUGen Nothing [KR,AR] (Right [0]) "Lag" [in_,lagTime] Nothing 1 (Special 0) NoId
+
+-- | Exponential lag
+lag2 :: UGen -> UGen -> UGen
+lag2 in_ lagTime = mkUGen Nothing [KR,AR] (Right [0]) "Lag2" [in_,lagTime] Nothing 1 (Special 0) NoId
+
+-- | Exponential lag
+lag2UD :: UGen -> UGen -> UGen -> UGen
+lag2UD in_ lagTimeU lagTimeD = mkUGen Nothing [KR,AR] (Right [0]) "Lag2UD" [in_,lagTimeU,lagTimeD] Nothing 1 (Special 0) NoId
+
+-- | Exponential lag
+lag3 :: UGen -> UGen -> UGen
+lag3 in_ lagTime = mkUGen Nothing [KR,AR] (Right [0]) "Lag3" [in_,lagTime] Nothing 1 (Special 0) NoId
+
+-- | Exponential lag
+lag3UD :: UGen -> UGen -> UGen -> UGen
+lag3UD in_ lagTimeU lagTimeD = mkUGen Nothing [KR,AR] (Right [0]) "Lag3UD" [in_,lagTimeU,lagTimeD] Nothing 1 (Special 0) NoId
+
+-- | Read a control signal from a bus with a lag
+lagIn :: Int -> UGen -> UGen -> UGen
+lagIn numChannels bus lag_ = mkUGen Nothing [KR] (Left KR) "LagIn" [bus,lag_] Nothing numChannels (Special 0) NoId
+
+-- | Exponential lag
+lagUD :: UGen -> UGen -> UGen -> UGen
+lagUD in_ lagTimeU lagTimeD = mkUGen Nothing [KR,AR] (Right [0]) "LagUD" [in_,lagTimeU,lagTimeD] Nothing 1 (Special 0) NoId
+
+-- | Output the last value before the input changed
+lastValue :: UGen -> UGen -> UGen
+lastValue in_ diff = mkUGen Nothing [KR,AR] (Right [0]) "LastValue" [in_,diff] Nothing 1 (Special 0) NoId
+
+-- | Sample and hold
+latch :: UGen -> UGen -> UGen
+latch in_ trig_ = mkUGen Nothing [KR,AR] (Right [0]) "Latch" [in_,trig_] Nothing 1 (Special 0) NoId
+
+-- | Latoocarfian chaotic generator
+latoocarfianC :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+latoocarfianC rate freq a b c d xi yi = mkUGen Nothing [AR] (Left rate) "LatoocarfianC" [freq,a,b,c,d,xi,yi] Nothing 1 (Special 0) NoId
+
+-- | Latoocarfian chaotic generator
+latoocarfianL :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+latoocarfianL rate freq a b c d xi yi = mkUGen Nothing [AR] (Left rate) "LatoocarfianL" [freq,a,b,c,d,xi,yi] Nothing 1 (Special 0) NoId
+
+-- | Latoocarfian chaotic generator
+latoocarfianN :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+latoocarfianN rate freq a b c d xi yi = mkUGen Nothing [AR] (Left rate) "LatoocarfianN" [freq,a,b,c,d,xi,yi] Nothing 1 (Special 0) NoId
+
+-- | Remove DC
+leakDC :: UGen -> UGen -> UGen
+leakDC in_ coef = mkUGen Nothing [KR,AR] (Right [0]) "LeakDC" [in_,coef] Nothing 1 (Special 0) NoId
+
+-- | Output least changed
+leastChange :: Rate -> UGen -> UGen -> UGen
+leastChange rate a b = mkUGen Nothing [KR,AR] (Left rate) "LeastChange" [a,b] Nothing 1 (Special 0) NoId
+
+-- | Peak limiter
+limiter :: UGen -> UGen -> UGen -> UGen
+limiter in_ level dur = mkUGen Nothing [AR] (Right [0]) "Limiter" [in_,level,dur] Nothing 1 (Special 0) NoId
+
+-- | Linear congruential chaotic generator
+linCongC :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+linCongC rate freq a c m xi = mkUGen Nothing [AR] (Left rate) "LinCongC" [freq,a,c,m,xi] Nothing 1 (Special 0) NoId
+
+-- | Linear congruential chaotic generator
+linCongL :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+linCongL rate freq a c m xi = mkUGen Nothing [AR] (Left rate) "LinCongL" [freq,a,c,m,xi] Nothing 1 (Special 0) NoId
+
+-- | Linear congruential chaotic generator
+linCongN :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+linCongN rate freq a c m xi = mkUGen Nothing [AR] (Left rate) "LinCongN" [freq,a,c,m,xi] Nothing 1 (Special 0) NoId
+
+-- | Map a linear range to an exponential range
+linExp :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+linExp in_ srclo srchi dstlo dsthi = mkUGen Nothing [KR,AR] (Right [0]) "LinExp" [in_,srclo,srchi,dstlo,dsthi] Nothing 1 (Special 0) NoId
+
+-- | Two channel linear pan.
+linPan2 :: UGen -> UGen -> UGen -> UGen
+linPan2 in_ pos level = mkUGen Nothing [KR,AR] (Right [0]) "LinPan2" [in_,pos,level] Nothing 2 (Special 0) NoId
+
+-- | Skewed random number generator.
+linRand :: ID a => a -> UGen -> UGen -> UGen -> UGen
+linRand z lo hi minmax = mkUGen Nothing [IR] (Left IR) "LinRand" [lo,hi,minmax] Nothing 1 (Special 0) (toUId z)
+
+-- | Two channel linear crossfade.
+linXFade2 :: UGen -> UGen -> UGen -> UGen -> UGen
+linXFade2 inA inB pan level = mkUGen Nothing [KR,AR] (Right [0,1]) "LinXFade2" [inA,inB,pan,level] Nothing 1 (Special 0) NoId
+
+-- | Line generator.
+line :: Rate -> UGen -> UGen -> UGen -> DoneAction -> UGen
+line rate start end dur doneAction = mkUGen Nothing [KR,AR] (Left rate) "Line" [start,end,dur,(from_done_action doneAction)] Nothing 1 (Special 0) NoId
+
+-- | Simple linear envelope generator.
+linen :: UGen -> UGen -> UGen -> UGen -> DoneAction -> UGen
+linen gate_ attackTime susLevel releaseTime doneAction = mkUGen Nothing [KR] (Left KR) "Linen" [gate_,attackTime,susLevel,releaseTime,(from_done_action doneAction)] Nothing 1 (Special 0) NoId
+
+-- | Allocate a buffer local to the synth
+localBuf :: ID a => a -> UGen -> UGen -> UGen
+localBuf z numChannels numFrames = mkUGen Nothing [IR] (Left IR) "LocalBuf" [numChannels,numFrames] Nothing 1 (Special 0) (toUId z)
+
+-- | Define and read from buses local to a synth.
+localIn :: Int -> Rate -> UGen -> UGen
+localIn numChannels rate default_ = mkUGen Nothing [KR,AR] (Left rate) "LocalIn" [] (Just default_) numChannels (Special 0) NoId
+
+-- | Write to buses local to a synth.
+localOut :: UGen -> UGen
+localOut input = mkUGen Nothing [KR,AR] (Right [0]) "LocalOut" [] (Just input) 0 (Special 0) NoId
+
+-- | Chaotic noise function
+logistic :: Rate -> UGen -> UGen -> UGen -> UGen
+logistic rate chaosParam freq init_ = mkUGen Nothing [KR,AR] (Left rate) "Logistic" [chaosParam,freq,init_] Nothing 1 (Special 0) NoId
+
+-- | Lorenz chaotic generator
+lorenzL :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+lorenzL rate freq s r b h xi yi zi = mkUGen Nothing [AR] (Left rate) "LorenzL" [freq,s,r,b,h,xi,yi,zi] Nothing 1 (Special 0) NoId
+
+-- | Extraction of instantaneous loudness in sones
+loudness :: Rate -> UGen -> UGen -> UGen -> UGen
+loudness rate chain smask tmask = mkUGen Nothing [KR] (Left rate) "Loudness" [chain,smask,tmask] Nothing 1 (Special 0) NoId
+
+-- | Mel frequency cepstral coefficients
+mFCC :: Rate -> UGen -> UGen -> UGen
+mFCC rate chain numcoeff = mkUGen Nothing [KR] (Left rate) "MFCC" [chain,numcoeff] Nothing 13 (Special 0) NoId
+
+-- | Reduce precision.
+mantissaMask :: UGen -> UGen -> UGen
+mantissaMask in_ bits = mkUGen Nothing [KR,AR] (Right [0]) "MantissaMask" [in_,bits] Nothing 1 (Special 0) NoId
+
+-- | Median filter.
+median :: UGen -> UGen -> UGen
+median length_ in_ = mkUGen Nothing [KR,AR] (Right [1]) "Median" [length_,in_] Nothing 1 (Special 0) NoId
+
+-- | Parametric filter.
+midEQ :: UGen -> UGen -> UGen -> UGen -> UGen
+midEQ in_ freq rq db = mkUGen Nothing [KR,AR] (Right [0]) "MidEQ" [in_,freq,rq,db] Nothing 1 (Special 0) NoId
+
+-- | Minimum difference of two values in modulo arithmetics
+modDif :: Rate -> UGen -> UGen -> UGen -> UGen
+modDif rate x y mod_ = mkUGen Nothing [IR,KR,AR] (Left rate) "ModDif" [x,y,mod_] Nothing 1 (Special 0) NoId
+
+-- | Moog VCF implementation, designed by Federico Fontana
+moogFF :: UGen -> UGen -> UGen -> UGen -> UGen
+moogFF in_ freq gain reset = mkUGen Nothing [KR,AR] (Right [0]) "MoogFF" [in_,freq,gain,reset] Nothing 1 (Special 0) NoId
+
+-- | Output most changed.
+mostChange :: UGen -> UGen -> UGen
+mostChange a b = mkUGen Nothing [KR,AR] (Right [0,1]) "MostChange" [a,b] Nothing 1 (Special 0) NoId
+
+-- | Mouse button UGen.
+mouseButton :: Rate -> UGen -> UGen -> UGen -> UGen
+mouseButton rate minval maxval lag_ = mkUGen Nothing [KR] (Left rate) "MouseButton" [minval,maxval,lag_] Nothing 1 (Special 0) NoId
+
+-- | Cursor tracking UGen.
+mouseX :: Rate -> UGen -> UGen -> Warp -> UGen -> UGen
+mouseX rate minval maxval warp lag_ = mkUGen Nothing [KR] (Left rate) "MouseX" [minval,maxval,(from_warp warp),lag_] Nothing 1 (Special 0) NoId
+
+-- | Cursor tracking UGen.
+mouseY :: Rate -> UGen -> UGen -> Warp -> UGen -> UGen
+mouseY rate minval maxval warp lag_ = mkUGen Nothing [KR] (Left rate) "MouseY" [minval,maxval,(from_warp warp),lag_] Nothing 1 (Special 0) NoId
+
+-- | Sum of uniform distributions.
+nRand :: ID a => a -> UGen -> UGen -> UGen -> UGen
+nRand z lo hi n = mkUGen Nothing [IR] (Left IR) "NRand" [lo,hi,n] Nothing 1 (Special 0) (toUId z)
+
+-- | Flattens dynamics.
+normalizer :: UGen -> UGen -> UGen -> UGen
+normalizer in_ level dur = mkUGen Nothing [AR] (Right [0]) "Normalizer" [in_,level,dur] Nothing 1 (Special 0) NoId
+
+-- | Number of audio busses.
+numAudioBuses :: UGen
+numAudioBuses = mkUGen Nothing [IR] (Left IR) "NumAudioBuses" [] Nothing 1 (Special 0) NoId
+
+-- | Number of open buffers.
+numBuffers :: UGen
+numBuffers = mkUGen Nothing [IR] (Left IR) "NumBuffers" [] Nothing 1 (Special 0) NoId
+
+-- | Number of control busses.
+numControlBuses :: UGen
+numControlBuses = mkUGen Nothing [IR] (Left IR) "NumControlBuses" [] Nothing 1 (Special 0) NoId
+
+-- | Number of input busses.
+numInputBuses :: UGen
+numInputBuses = mkUGen Nothing [IR] (Left IR) "NumInputBuses" [] Nothing 1 (Special 0) NoId
+
+-- | Number of output busses.
+numOutputBuses :: UGen
+numOutputBuses = mkUGen Nothing [IR] (Left IR) "NumOutputBuses" [] Nothing 1 (Special 0) NoId
+
+-- | Number of currently running synths.
+numRunningSynths :: UGen
+numRunningSynths = mkUGen Nothing [IR,KR] (Left IR) "NumRunningSynths" [] Nothing 1 (Special 0) NoId
+
+-- | Write a signal to a bus with sample accurate timing.
+offsetOut :: UGen -> UGen -> UGen
+offsetOut bus input = mkUGen Nothing [KR,AR] (Right [1]) "OffsetOut" [bus] (Just input) 0 (Special 0) NoId
+
+-- | One pole filter.
+onePole :: UGen -> UGen -> UGen
+onePole in_ coef = mkUGen Nothing [KR,AR] (Right [0]) "OnePole" [in_,coef] Nothing 1 (Special 0) NoId
+
+-- | One zero filter.
+oneZero :: UGen -> UGen -> UGen
+oneZero in_ coef = mkUGen Nothing [KR,AR] (Right [0]) "OneZero" [in_,coef] Nothing 1 (Special 0) NoId
+
+-- | Onset detector
+onsets :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+onsets chain threshold odftype relaxtime floor_ mingap medianspan whtype rawodf = mkUGen Nothing [KR] (Left KR) "Onsets" [chain,threshold,odftype,relaxtime,floor_,mingap,medianspan,whtype,rawodf] Nothing 1 (Special 0) NoId
+
+-- | Interpolating wavetable oscillator.
+osc :: Rate -> UGen -> UGen -> UGen -> UGen
+osc rate bufnum freq phase = mkUGen Nothing [KR,AR] (Left rate) "Osc" [bufnum,freq,phase] Nothing 1 (Special 0) NoId
+
+-- | Noninterpolating wavetable oscillator.
+oscN :: Rate -> UGen -> UGen -> UGen -> UGen
+oscN rate bufnum freq phase = mkUGen Nothing [KR,AR] (Left rate) "OscN" [bufnum,freq,phase] Nothing 1 (Special 0) NoId
+
+-- | Write a signal to a bus.
+out :: UGen -> UGen -> UGen
+out bus input = mkUGen Nothing [KR,AR] (Right [1]) "Out" [bus] (Just input) 0 (Special 0) NoId
+
+-- | Very fast sine grain with a parabolic envelope
+pSinGrain :: Rate -> UGen -> UGen -> UGen -> UGen
+pSinGrain rate freq dur amp = mkUGen Nothing [AR] (Left rate) "PSinGrain" [freq,dur,amp] Nothing 1 (Special 0) NoId
+
+-- | Complex addition.
+pv_Add :: UGen -> UGen -> UGen
+pv_Add bufferA bufferB = mkUGen Nothing [KR] (Left KR) "PV_Add" [bufferA,bufferB] Nothing 1 (Special 0) NoId
+
+-- | Scramble bins.
+pv_BinScramble :: ID a => a -> UGen -> UGen -> UGen -> UGen -> UGen
+pv_BinScramble z buffer wipe width trig_ = mkUGen Nothing [KR] (Left KR) "PV_BinScramble" [buffer,wipe,width,trig_] Nothing 1 (Special 0) (toUId z)
+
+-- | Shift and stretch bin position.
+pv_BinShift :: UGen -> UGen -> UGen -> UGen -> UGen
+pv_BinShift buffer stretch shift interp = mkUGen Nothing [KR] (Left KR) "PV_BinShift" [buffer,stretch,shift,interp] Nothing 1 (Special 0) NoId
+
+-- | Combine low and high bins from two inputs.
+pv_BinWipe :: UGen -> UGen -> UGen -> UGen
+pv_BinWipe bufferA bufferB wipe = mkUGen Nothing [KR] (Left KR) "PV_BinWipe" [bufferA,bufferB,wipe] Nothing 1 (Special 0) NoId
+
+-- | Zero bins.
+pv_BrickWall :: UGen -> UGen -> UGen
+pv_BrickWall buffer wipe = mkUGen Nothing [KR] (Left KR) "PV_BrickWall" [buffer,wipe] Nothing 1 (Special 0) NoId
+
+-- | Base class for UGens that alter FFT chains
+pv_ChainUGen :: UGen -> UGen
+pv_ChainUGen maxSize = mkUGen Nothing [KR] (Left KR) "PV_ChainUGen" [maxSize] Nothing 1 (Special 0) NoId
+
+-- | Complex plane attack.
+pv_ConformalMap :: UGen -> UGen -> UGen -> UGen
+pv_ConformalMap buffer areal aimag = mkUGen Nothing [KR] (Left KR) "PV_ConformalMap" [buffer,areal,aimag] Nothing 1 (Special 0) NoId
+
+-- | Complex conjugate
+pv_Conj :: UGen -> UGen
+pv_Conj buffer = mkUGen Nothing [KR] (Left KR) "PV_Conj" [buffer] Nothing 1 (Special 0) NoId
+
+-- | Copy an FFT buffer
+pv_Copy :: UGen -> UGen -> UGen
+pv_Copy bufferA bufferB = mkUGen Nothing [KR] (Left KR) "PV_Copy" [bufferA,bufferB] Nothing 1 (Special 0) NoId
+
+-- | Copy magnitudes and phases.
+pv_CopyPhase :: UGen -> UGen -> UGen
+pv_CopyPhase bufferA bufferB = mkUGen Nothing [KR] (Left KR) "PV_CopyPhase" [bufferA,bufferB] Nothing 1 (Special 0) NoId
+
+-- | Random phase shifting.
+pv_Diffuser :: UGen -> UGen -> UGen
+pv_Diffuser buffer trig_ = mkUGen Nothing [KR] (Left KR) "PV_Diffuser" [buffer,trig_] Nothing 1 (Special 0) NoId
+
+-- | Complex division
+pv_Div :: UGen -> UGen -> UGen
+pv_Div bufferA bufferB = mkUGen Nothing [KR] (Left KR) "PV_Div" [bufferA,bufferB] Nothing 1 (Special 0) NoId
+
+-- | FFT onset detector.
+pv_HainsworthFoote :: UGen -> UGen
+pv_HainsworthFoote maxSize = mkUGen Nothing [KR,AR] (Left KR) "PV_HainsworthFoote" [maxSize] Nothing 1 (Special 0) NoId
+
+-- | FFT feature detector for onset detection.
+pv_JensenAndersen :: UGen -> UGen
+pv_JensenAndersen maxSize = mkUGen Nothing [KR,AR] (Left KR) "PV_JensenAndersen" [maxSize] Nothing 1 (Special 0) NoId
+
+-- | Pass bins which are a local maximum.
+pv_LocalMax :: UGen -> UGen -> UGen
+pv_LocalMax buffer threshold = mkUGen Nothing [KR] (Left KR) "PV_LocalMax" [buffer,threshold] Nothing 1 (Special 0) NoId
+
+-- | Pass bins above a threshold.
+pv_MagAbove :: UGen -> UGen -> UGen
+pv_MagAbove buffer threshold = mkUGen Nothing [KR] (Left KR) "PV_MagAbove" [buffer,threshold] Nothing 1 (Special 0) NoId
+
+-- | Pass bins below a threshold.
+pv_MagBelow :: UGen -> UGen -> UGen
+pv_MagBelow buffer threshold = mkUGen Nothing [KR] (Left KR) "PV_MagBelow" [buffer,threshold] Nothing 1 (Special 0) NoId
+
+-- | Clip bins to a threshold.
+pv_MagClip :: UGen -> UGen -> UGen
+pv_MagClip buffer threshold = mkUGen Nothing [KR] (Left KR) "PV_MagClip" [buffer,threshold] Nothing 1 (Special 0) NoId
+
+-- | Division of magnitudes
+pv_MagDiv :: UGen -> UGen -> UGen -> UGen
+pv_MagDiv bufferA bufferB zeroed = mkUGen Nothing [KR] (Left KR) "PV_MagDiv" [bufferA,bufferB,zeroed] Nothing 1 (Special 0) NoId
+
+-- | Freeze magnitudes.
+pv_MagFreeze :: UGen -> UGen -> UGen
+pv_MagFreeze buffer freeze = mkUGen Nothing [KR] (Left KR) "PV_MagFreeze" [buffer,freeze] Nothing 1 (Special 0) NoId
+
+-- | Multiply magnitudes.
+pv_MagMul :: UGen -> UGen -> UGen
+pv_MagMul bufferA bufferB = mkUGen Nothing [KR] (Left KR) "PV_MagMul" [bufferA,bufferB] Nothing 1 (Special 0) NoId
+
+-- | Multiply magnitudes by noise.
+pv_MagNoise :: UGen -> UGen
+pv_MagNoise buffer = mkUGen Nothing [KR] (Left KR) "PV_MagNoise" [buffer] Nothing 1 (Special 0) NoId
+
+-- | shift and stretch magnitude bin position.
+pv_MagShift :: UGen -> UGen -> UGen -> UGen
+pv_MagShift buffer stretch shift = mkUGen Nothing [KR] (Left KR) "PV_MagShift" [buffer,stretch,shift] Nothing 1 (Special 0) NoId
+
+-- | Average magnitudes across bins.
+pv_MagSmear :: UGen -> UGen -> UGen
+pv_MagSmear buffer bins = mkUGen Nothing [KR] (Left KR) "PV_MagSmear" [buffer,bins] Nothing 1 (Special 0) NoId
+
+-- | Square magnitudes.
+pv_MagSquared :: UGen -> UGen
+pv_MagSquared buffer = mkUGen Nothing [KR] (Left KR) "PV_MagSquared" [buffer] Nothing 1 (Special 0) NoId
+
+-- | Maximum magnitude.
+pv_Max :: UGen -> UGen -> UGen
+pv_Max bufferA bufferB = mkUGen Nothing [KR] (Left KR) "PV_Max" [bufferA,bufferB] Nothing 1 (Special 0) NoId
+
+-- | Minimum magnitude.
+pv_Min :: UGen -> UGen -> UGen
+pv_Min bufferA bufferB = mkUGen Nothing [KR] (Left KR) "PV_Min" [bufferA,bufferB] Nothing 1 (Special 0) NoId
+
+-- | Complex multiply.
+pv_Mul :: UGen -> UGen -> UGen
+pv_Mul bufferA bufferB = mkUGen Nothing [KR] (Left KR) "PV_Mul" [bufferA,bufferB] Nothing 1 (Special 0) NoId
+
+-- | Shift phase.
+pv_PhaseShift :: UGen -> UGen -> UGen -> UGen
+pv_PhaseShift buffer shift integrate = mkUGen Nothing [KR] (Left KR) "PV_PhaseShift" [buffer,shift,integrate] Nothing 1 (Special 0) NoId
+
+-- | Shift phase by 270 degrees.
+pv_PhaseShift270 :: UGen -> UGen
+pv_PhaseShift270 buffer = mkUGen Nothing [KR] (Left KR) "PV_PhaseShift270" [buffer] Nothing 1 (Special 0) NoId
+
+-- | Shift phase by 90 degrees.
+pv_PhaseShift90 :: UGen -> UGen
+pv_PhaseShift90 buffer = mkUGen Nothing [KR] (Left KR) "PV_PhaseShift90" [buffer] Nothing 1 (Special 0) NoId
+
+-- | Pass random bins.
+pv_RandComb :: ID a => a -> UGen -> UGen -> UGen -> UGen
+pv_RandComb z buffer wipe trig_ = mkUGen Nothing [KR] (Left KR) "PV_RandComb" [buffer,wipe,trig_] Nothing 1 (Special 0) (toUId z)
+
+-- | Crossfade in random bin order.
+pv_RandWipe :: ID a => a -> UGen -> UGen -> UGen -> UGen -> UGen
+pv_RandWipe z bufferA bufferB wipe trig_ = mkUGen Nothing [KR] (Left KR) "PV_RandWipe" [bufferA,bufferB,wipe,trig_] Nothing 1 (Special 0) (toUId z)
+
+-- | Make gaps in spectrum.
+pv_RectComb :: UGen -> UGen -> UGen -> UGen -> UGen
+pv_RectComb buffer numTeeth phase width = mkUGen Nothing [KR] (Left KR) "PV_RectComb" [buffer,numTeeth,phase,width] Nothing 1 (Special 0) NoId
+
+-- | Make gaps in spectrum.
+pv_RectComb2 :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+pv_RectComb2 bufferA bufferB numTeeth phase width = mkUGen Nothing [KR] (Left KR) "PV_RectComb2" [bufferA,bufferB,numTeeth,phase,width] Nothing 1 (Special 0) NoId
+
+-- | Two channel equal power pan.
+pan2 :: UGen -> UGen -> UGen -> UGen
+pan2 in_ pos level = mkUGen Nothing [KR,AR] (Right [0]) "Pan2" [in_,pos,level] Nothing 2 (Special 0) NoId
+
+-- | Four channel equal power pan.
+pan4 :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen
+pan4 rate in_ xpos ypos level = mkUGen Nothing [KR,AR] (Left rate) "Pan4" [in_,xpos,ypos,level] Nothing 4 (Special 0) NoId
+
+-- | Azimuth panner
+panAz :: Int -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+panAz numChannels in_ pos level width orientation = mkUGen Nothing [KR,AR] (Right [0]) "PanAz" [in_,pos,level,width,orientation] Nothing numChannels (Special 0) NoId
+
+-- | Ambisonic B-format panner.
+panB :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen
+panB rate in_ azimuth elevation gain = mkUGen Nothing [KR,AR] (Left rate) "PanB" [in_,azimuth,elevation,gain] Nothing 4 (Special 0) NoId
+
+-- | 2D Ambisonic B-format panner.
+panB2 :: Rate -> UGen -> UGen -> UGen -> UGen
+panB2 rate in_ azimuth gain = mkUGen Nothing [KR,AR] (Left rate) "PanB2" [in_,azimuth,gain] Nothing 3 (Special 0) NoId
+
+-- | Real-time partitioned convolution
+partConv :: UGen -> UGen -> UGen -> UGen
+partConv in_ fftsize irbufnum = mkUGen Nothing [AR] (Left AR) "PartConv" [in_,fftsize,irbufnum] Nothing 1 (Special 0) NoId
+
+-- | When triggered, pauses a node.
+pause :: Rate -> UGen -> UGen -> UGen
+pause rate gate_ id_ = mkUGen Nothing [KR] (Left rate) "Pause" [gate_,id_] Nothing 1 (Special 0) NoId
+
+-- | When triggered, pause enclosing synth.
+pauseSelf :: Rate -> UGen -> UGen
+pauseSelf rate in_ = mkUGen Nothing [KR] (Left rate) "PauseSelf" [in_] Nothing 1 (Special 0) NoId
+
+-- | FIXME: PauseSelfWhenDone purpose.
+pauseSelfWhenDone :: Rate -> UGen -> UGen
+pauseSelfWhenDone rate src = mkUGen Nothing [KR] (Left rate) "PauseSelfWhenDone" [src] Nothing 1 (Special 0) NoId
+
+-- | Track peak signal amplitude.
+peak :: UGen -> UGen -> UGen
+peak in_ trig_ = mkUGen Nothing [KR,AR] (Right [0]) "Peak" [in_,trig_] Nothing 1 (Special 0) NoId
+
+-- | Track peak signal amplitude.
+peakFollower :: UGen -> UGen -> UGen
+peakFollower in_ decay_ = mkUGen Nothing [KR,AR] (Right [0]) "PeakFollower" [in_,decay_] Nothing 1 (Special 0) NoId
+
+-- | A resettable linear ramp between two levels.
+phasor :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+phasor rate trig_ rate_ start end resetPos = mkUGen Nothing [KR,AR] (Left rate) "Phasor" [trig_,rate_,start,end,resetPos] Nothing 1 (Special 0) NoId
+
+-- | Pink Noise.
+pinkNoise :: ID a => a -> Rate -> UGen
+pinkNoise z rate = mkUGen Nothing [KR,AR] (Left rate) "PinkNoise" [] Nothing 1 (Special 0) (toUId z)
+
+-- | Autocorrelation pitch follower
+pitch :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+pitch in_ initFreq minFreq maxFreq execFreq maxBinsPerOctave median_ ampThreshold peakThreshold downSample clar = mkUGen Nothing [KR] (Left KR) "Pitch" [in_,initFreq,minFreq,maxFreq,execFreq,maxBinsPerOctave,median_,ampThreshold,peakThreshold,downSample,clar] Nothing 2 (Special 0) NoId
+
+-- | Time domain pitch shifter.
+pitchShift :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+pitchShift in_ windowSize pitchRatio pitchDispersion timeDispersion = mkUGen Nothing [AR] (Right [0]) "PitchShift" [in_,windowSize,pitchRatio,pitchDispersion,timeDispersion] Nothing 1 (Special 0) NoId
+
+-- | Sample playback oscillator.
+playBuf :: Int -> Rate -> UGen -> UGen -> UGen -> UGen -> Loop -> DoneAction -> UGen
+playBuf numChannels rate bufnum rate_ trigger startPos loop doneAction = mkUGen Nothing [KR,AR] (Left rate) "PlayBuf" [bufnum,rate_,trigger,startPos,(from_loop loop),(from_done_action doneAction)] Nothing numChannels (Special 0) NoId
+
+-- | A Karplus-Strong UGen
+pluck :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+pluck in_ trig_ maxdelaytime delaytime decaytime coef = mkUGen Nothing [AR] (Right [0]) "Pluck" [in_,trig_,maxdelaytime,delaytime,decaytime,coef] Nothing 1 (Special 0) NoId
+
+-- | Print the current output value of a UGen
+-- poll :: UGen -> UGen -> UGen -> UGen -> UGen
+-- poll trig_ in_ label_ trigid = mkUGen Nothing [KR,AR] (Right [1]) "Poll" [trig_,in_,label_,trigid] Nothing 1 (Special 0) NoId
+
+-- | Band limited pulse wave.
+pulse :: Rate -> UGen -> UGen -> UGen
+pulse rate freq width = mkUGen Nothing [KR,AR] (Left rate) "Pulse" [freq,width] Nothing 1 (Special 0) NoId
+
+-- | Pulse counter.
+pulseCount :: UGen -> UGen -> UGen
+pulseCount trig_ reset = mkUGen Nothing [KR,AR] (Right [0]) "PulseCount" [trig_,reset] Nothing 1 (Special 0) NoId
+
+-- | Pulse divider.
+pulseDivider :: UGen -> UGen -> UGen -> UGen
+pulseDivider trig_ div_ start = mkUGen Nothing [KR,AR] (Right [0]) "PulseDivider" [trig_,div_,start] Nothing 1 (Special 0) NoId
+
+-- | General quadratic map chaotic generator
+quadC :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+quadC rate freq a b c xi = mkUGen Nothing [AR] (Left rate) "QuadC" [freq,a,b,c,xi] Nothing 1 (Special 0) NoId
+
+-- | General quadratic map chaotic generator
+quadL :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+quadL rate freq a b c xi = mkUGen Nothing [AR] (Left rate) "QuadL" [freq,a,b,c,xi] Nothing 1 (Special 0) NoId
+
+-- | General quadratic map chaotic generator
+quadN :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+quadN rate freq a b c xi = mkUGen Nothing [AR] (Left rate) "QuadN" [freq,a,b,c,xi] Nothing 1 (Special 0) NoId
+
+-- | A resonant high pass filter.
+rhpf :: UGen -> UGen -> UGen -> UGen
+rhpf in_ freq rq = mkUGen Nothing [KR,AR] (Right [0]) "RHPF" [in_,freq,rq] Nothing 1 (Special 0) NoId
+
+-- | A resonant low pass filter.
+rlpf :: UGen -> UGen -> UGen -> UGen
+rlpf in_ freq rq = mkUGen Nothing [KR,AR] (Right [0]) "RLPF" [in_,freq,rq] Nothing 1 (Special 0) NoId
+
+-- | Number of radians per sample.
+radiansPerSample :: UGen
+radiansPerSample = mkUGen Nothing [IR] (Left IR) "RadiansPerSample" [] Nothing 1 (Special 0) NoId
+
+-- | Break a continuous signal into line segments
+ramp :: UGen -> UGen -> UGen
+ramp in_ lagTime = mkUGen Nothing [KR,AR] (Right [0]) "Ramp" [in_,lagTime] Nothing 1 (Special 0) NoId
+
+-- | Single random number generator.
+rand :: ID a => a -> UGen -> UGen -> UGen
+rand z lo hi = mkUGen Nothing [IR] (Left IR) "Rand" [lo,hi] Nothing 1 (Special 0) (toUId z)
+
+-- | Set the synth's random generator ID.
+randID :: Rate -> UGen -> UGen
+randID rate id_ = mkUGen Nothing [IR,KR] (Left rate) "RandID" [id_] Nothing 0 (Special 0) NoId
+
+-- | Sets the synth's random generator seed.
+randSeed :: Rate -> UGen -> UGen -> UGen
+randSeed rate trig_ seed = mkUGen Nothing [IR,KR,AR] (Left rate) "RandSeed" [trig_,seed] Nothing 0 (Special 0) NoId
+
+-- | Record or overdub into a Buffer.
+recordBuf :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> Loop -> UGen -> DoneAction -> UGen -> UGen
+recordBuf rate bufnum offset recLevel preLevel run loop trigger doneAction inputArray = mkUGen Nothing [KR,AR] (Left rate) "RecordBuf" [bufnum,offset,recLevel,preLevel,run,(from_loop loop),trigger,(from_done_action doneAction)] (Just inputArray) 1 (Special 0) NoId
+
+-- | Send signal to a bus, overwriting previous contents.
+replaceOut :: UGen -> UGen -> UGen
+replaceOut bus input = mkUGen Nothing [KR,AR] (Right [1]) "ReplaceOut" [bus] (Just input) 0 (Special 0) NoId
+
+-- | Resonant filter.
+resonz :: UGen -> UGen -> UGen -> UGen
+resonz in_ freq bwr = mkUGen Nothing [KR,AR] (Right [0]) "Resonz" [in_,freq,bwr] Nothing 1 (Special 0) NoId
+
+-- | Ringing filter.
+ringz :: UGen -> UGen -> UGen -> UGen
+ringz in_ freq decaytime = mkUGen Nothing [KR,AR] (Right [0]) "Ringz" [in_,freq,decaytime] Nothing 1 (Special 0) NoId
+
+-- | Rotate a sound field.
+rotate2 :: UGen -> UGen -> UGen -> UGen
+rotate2 x y pos = mkUGen Nothing [KR,AR] (Right [0,1]) "Rotate2" [x,y,pos] Nothing 2 (Special 0) NoId
+
+-- | Track maximum level.
+runningMax :: UGen -> UGen -> UGen
+runningMax in_ trig_ = mkUGen Nothing [KR,AR] (Right [0]) "RunningMax" [in_,trig_] Nothing 1 (Special 0) NoId
+
+-- | Track minimum level.
+runningMin :: UGen -> UGen -> UGen
+runningMin in_ trig_ = mkUGen Nothing [KR,AR] (Right [0]) "RunningMin" [in_,trig_] Nothing 1 (Special 0) NoId
+
+-- | Running sum over n frames
+runningSum :: UGen -> UGen -> UGen
+runningSum in_ numsamp = mkUGen Nothing [KR,AR] (Right [0]) "RunningSum" [in_,numsamp] Nothing 1 (Special 0) NoId
+
+-- | Second order filter section (biquad).
+sos :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+sos in_ a0 a1 a2 b1 b2 = mkUGen Nothing [KR,AR] (Right [0]) "SOS" [in_,a0,a1,a2,b1,b2] Nothing 1 (Special 0) NoId
+
+-- | Duration of one sample.
+sampleDur :: UGen
+sampleDur = mkUGen Nothing [IR] (Left IR) "SampleDur" [] Nothing 1 (Special 0) NoId
+
+-- | Server sample rate.
+sampleRate :: UGen
+sampleRate = mkUGen Nothing [IR] (Left IR) "SampleRate" [] Nothing 1 (Special 0) NoId
+
+-- | Band limited sawtooth.
+saw :: Rate -> UGen -> UGen
+saw rate freq = mkUGen Nothing [KR,AR] (Left rate) "Saw" [freq] Nothing 1 (Special 0) NoId
+
+-- | Schmidt trigger.
+schmidt :: Rate -> UGen -> UGen -> UGen -> UGen
+schmidt rate in_ lo hi = mkUGen Nothing [IR,KR,AR] (Left rate) "Schmidt" [in_,lo,hi] Nothing 1 (Special 0) NoId
+
+-- | FIXME: ScopeOut purpose.
+scopeOut :: Rate -> UGen -> UGen -> UGen
+scopeOut rate inputArray bufnum = mkUGen Nothing [KR,AR] (Left rate) "ScopeOut" [inputArray,bufnum] Nothing 0 (Special 0) NoId
+
+-- | (Undocumented class)
+scopeOut2 :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen
+scopeOut2 rate inputArray scopeNum maxFrames scopeFrames = mkUGen Nothing [KR,AR] (Left rate) "ScopeOut2" [inputArray,scopeNum,maxFrames,scopeFrames] Nothing 0 (Special 0) NoId
+
+-- | Select output from an array of inputs.
+select :: UGen -> UGen -> UGen
+select which array = mkUGen Nothing [IR,KR,AR] (Right [0,1]) "Select" [which] (Just array) 1 (Special 0) NoId
+
+-- | Send a trigger message from the server back to the client.
+sendTrig :: UGen -> UGen -> UGen -> UGen
+sendTrig in_ id_ value = mkUGen Nothing [KR,AR] (Right [0]) "SendTrig" [in_,id_,value] Nothing 0 (Special 0) NoId
+
+-- | Set-reset flip flop.
+setResetFF :: UGen -> UGen -> UGen
+setResetFF trig_ reset = mkUGen Nothing [KR,AR] (Right [0]) "SetResetFF" [trig_,reset] Nothing 1 (Special 0) NoId
+
+-- | Wave shaper.
+shaper :: UGen -> UGen -> UGen
+shaper bufnum in_ = mkUGen Nothing [KR,AR] (Right [1]) "Shaper" [bufnum,in_] Nothing 1 (Special 0) NoId
+
+-- | Interpolating sine wavetable oscillator.
+sinOsc :: Rate -> UGen -> UGen -> UGen
+sinOsc rate freq phase = mkUGen Nothing [KR,AR] (Left rate) "SinOsc" [freq,phase] Nothing 1 (Special 0) NoId
+
+-- | Feedback FM oscillator
+sinOscFB :: Rate -> UGen -> UGen -> UGen
+sinOscFB rate freq feedback = mkUGen Nothing [KR,AR] (Left rate) "SinOscFB" [freq,feedback] Nothing 1 (Special 0) NoId
+
+-- | Slew rate limiter.
+slew :: UGen -> UGen -> UGen -> UGen
+slew in_ up dn = mkUGen Nothing [KR,AR] (Right [0]) "Slew" [in_,up,dn] Nothing 1 (Special 0) NoId
+
+-- | Slope of signal
+slope :: UGen -> UGen
+slope in_ = mkUGen Nothing [KR,AR] (Right [0]) "Slope" [in_] Nothing 1 (Special 0) NoId
+
+-- | Spectral centroid
+specCentroid :: Rate -> UGen -> UGen
+specCentroid rate buffer = mkUGen Nothing [KR] (Left rate) "SpecCentroid" [buffer] Nothing 1 (Special 0) NoId
+
+-- | Spectral Flatness measure
+specFlatness :: Rate -> UGen -> UGen
+specFlatness rate buffer = mkUGen Nothing [KR] (Left rate) "SpecFlatness" [buffer] Nothing 1 (Special 0) NoId
+
+-- | Find a percentile of FFT magnitude spectrum
+specPcile :: Rate -> UGen -> UGen -> UGen -> UGen
+specPcile rate buffer fraction interpolate = mkUGen Nothing [KR] (Left rate) "SpecPcile" [buffer,fraction,interpolate] Nothing 1 (Special 0) NoId
+
+-- | physical model of resonating spring
+spring :: Rate -> UGen -> UGen -> UGen -> UGen
+spring rate in_ spring_ damp = mkUGen Nothing [KR,AR] (Left rate) "Spring" [in_,spring_,damp] Nothing 1 (Special 0) NoId
+
+-- | Standard map chaotic generator
+standardL :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen
+standardL rate freq k xi yi = mkUGen Nothing [AR] (Left rate) "StandardL" [freq,k,xi,yi] Nothing 1 (Special 0) NoId
+
+-- | Standard map chaotic generator
+standardN :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen
+standardN rate freq k xi yi = mkUGen Nothing [AR] (Left rate) "StandardN" [freq,k,xi,yi] Nothing 1 (Special 0) NoId
+
+-- | Pulse counter.
+stepper :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+stepper trig_ reset min_ max_ step resetval = mkUGen Nothing [KR,AR] (Right [0]) "Stepper" [trig_,reset,min_,max_,step,resetval] Nothing 1 (Special 0) NoId
+
+-- | Stereo real-time convolver with linear interpolation
+stereoConvolution2L :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+stereoConvolution2L rate in_ kernelL kernelR trigger framesize crossfade = mkUGen Nothing [AR] (Left rate) "StereoConvolution2L" [in_,kernelL,kernelR,trigger,framesize,crossfade] Nothing 2 (Special 0) NoId
+
+-- | Offset from synth start within one sample.
+subsampleOffset :: UGen
+subsampleOffset = mkUGen Nothing [IR] (Left IR) "SubsampleOffset" [] Nothing 1 (Special 0) NoId
+
+-- | Sum three signals
+sum3 :: UGen -> UGen -> UGen -> UGen
+sum3 in0 in1 in2 = mkUGen Nothing [IR,KR,AR,DR] (Right [0,1,2]) "Sum3" [in0,in1,in2] Nothing 1 (Special 0) NoId
+
+-- | Sum four signals
+sum4 :: UGen -> UGen -> UGen -> UGen -> UGen
+sum4 in0 in1 in2 in3 = mkUGen Nothing [IR,KR,AR,DR] (Right [0,1,2,3]) "Sum4" [in0,in1,in2,in3] Nothing 1 (Special 0) NoId
+
+-- | Triggered linear ramp
+sweep :: UGen -> UGen -> UGen
+sweep trig_ rate_ = mkUGen Nothing [KR,AR] (Right [0]) "Sweep" [trig_,rate_] Nothing 1 (Special 0) NoId
+
+-- | Hard sync sawtooth wave.
+syncSaw :: Rate -> UGen -> UGen -> UGen
+syncSaw rate syncFreq sawFreq = mkUGen Nothing [KR,AR] (Left rate) "SyncSaw" [syncFreq,sawFreq] Nothing 1 (Special 0) NoId
+
+-- | Control rate trigger to audio rate trigger converter
+t2A :: UGen -> UGen -> UGen
+t2A in_ offset = mkUGen Nothing [AR] (Left AR) "T2A" [in_,offset] Nothing 1 (Special 0) NoId
+
+-- | Audio rate trigger to control rate trigger converter
+t2K :: Rate -> UGen -> UGen
+t2K rate in_ = mkUGen Nothing [KR] (Left rate) "T2K" [in_] Nothing 1 (Special 0) NoId
+
+-- | physical model of bouncing object
+tBall :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen
+tBall rate in_ g damp friction = mkUGen Nothing [KR,AR] (Left rate) "TBall" [in_,g,damp,friction] Nothing 1 (Special 0) NoId
+
+-- | Trigger delay.
+tDelay :: UGen -> UGen -> UGen
+tDelay in_ dur = mkUGen Nothing [KR,AR] (Right [0]) "TDelay" [in_,dur] Nothing 1 (Special 0) NoId
+
+-- | Demand results as trigger from demand rate UGens.
+tDuty :: Rate -> UGen -> UGen -> DoneAction -> UGen -> UGen -> UGen
+tDuty rate dur reset doneAction level gapFirst = mkUGen Nothing [KR,AR] (Left rate) "TDuty" [dur,reset,(from_done_action doneAction),level,gapFirst] Nothing 1 (Special 0) NoId
+
+-- | Triggered exponential random number generator.
+tExpRand :: ID a => a -> UGen -> UGen -> UGen -> UGen
+tExpRand z lo hi trig_ = mkUGen Nothing [KR,AR] (Right [2]) "TExpRand" [lo,hi,trig_] Nothing 1 (Special 0) (toUId z)
+
+-- | Buffer granulator.
+tGrains :: Int -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+tGrains numChannels trigger bufnum rate_ centerPos dur pan amp interp = mkUGen Nothing [AR] (Left AR) "TGrains" [trigger,bufnum,rate_,centerPos,dur,pan,amp,interp] Nothing numChannels (Special 0) NoId
+
+-- | Triggered integer random number generator.
+tIRand :: ID a => a -> UGen -> UGen -> UGen -> UGen
+tIRand z lo hi trig_ = mkUGen Nothing [KR,AR] (Right [2]) "TIRand" [lo,hi,trig_] Nothing 1 (Special 0) (toUId z)
+
+-- | Triggered random number generator.
+tRand :: ID a => a -> UGen -> UGen -> UGen -> UGen
+tRand z lo hi trig_ = mkUGen Nothing [KR,AR] (Right [2]) "TRand" [lo,hi,trig_] Nothing 1 (Special 0) (toUId z)
+
+-- | Triggered windex.
+tWindex :: ID a => a -> UGen -> UGen -> UGen -> UGen
+tWindex z in_ normalize array = mkUGen Nothing [KR,AR] (Right [0]) "TWindex" [in_,normalize] (Just array) 1 (Special 0) (toUId z)
+
+-- | Returns time since last triggered.
+timer :: UGen -> UGen
+timer trig_ = mkUGen Nothing [KR,AR] (Right [0]) "Timer" [trig_] Nothing 1 (Special 0) NoId
+
+-- | Toggle flip flop.
+toggleFF :: UGen -> UGen
+toggleFF trig_ = mkUGen Nothing [KR,AR] (Right [0]) "ToggleFF" [trig_] Nothing 1 (Special 0) NoId
+
+-- | Timed trigger.
+trig :: UGen -> UGen -> UGen
+trig in_ dur = mkUGen Nothing [KR,AR] (Right [0]) "Trig" [in_,dur] Nothing 1 (Special 0) NoId
+
+-- | Timed trigger.
+trig1 :: UGen -> UGen -> UGen
+trig1 in_ dur = mkUGen Nothing [KR,AR] (Right [0]) "Trig1" [in_,dur] Nothing 1 (Special 0) NoId
+
+-- | FIXME: TrigControl purpose.
+trigControl :: Rate -> UGen -> UGen
+trigControl rate values = mkUGen Nothing [IR,KR] (Left rate) "TrigControl" [values] Nothing 0 (Special 0) NoId
+
+-- | Two pole filter.
+twoPole :: UGen -> UGen -> UGen -> UGen
+twoPole in_ freq radius = mkUGen Nothing [KR,AR] (Right [0]) "TwoPole" [in_,freq,radius] Nothing 1 (Special 0) NoId
+
+-- | Two zero filter.
+twoZero :: UGen -> UGen -> UGen -> UGen
+twoZero in_ freq radius = mkUGen Nothing [KR,AR] (Right [0]) "TwoZero" [in_,freq,radius] Nothing 1 (Special 0) NoId
+
+-- | Apply a unary operation to the values of an input ugen
+unaryOpUGen :: UGen -> UGen
+unaryOpUGen a = mkUGen Nothing [IR,KR,AR,DR] (Right [0]) "UnaryOpUGen" [a] Nothing 1 (Special 0) NoId
+
+-- | Stream in audio from a file, with variable rate
+vDiskIn :: Int -> UGen -> UGen -> Loop -> UGen -> UGen
+vDiskIn numChannels bufnum rate_ loop sendID = mkUGen Nothing [AR] (Left AR) "VDiskIn" [bufnum,rate_,(from_loop loop),sendID] Nothing numChannels (Special 0) NoId
+
+-- | Variable wavetable oscillator.
+vOsc :: Rate -> UGen -> UGen -> UGen -> UGen
+vOsc rate bufpos freq phase = mkUGen Nothing [KR,AR] (Left rate) "VOsc" [bufpos,freq,phase] Nothing 1 (Special 0) NoId
+
+-- | Three variable wavetable oscillators.
+vOsc3 :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen
+vOsc3 rate bufpos freq1 freq2 freq3 = mkUGen Nothing [KR,AR] (Left rate) "VOsc3" [bufpos,freq1,freq2,freq3] Nothing 1 (Special 0) NoId
+
+-- | Variable shaped lag
+varLag :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+varLag rate in_ time curvature warp start = mkUGen Nothing [KR,AR] (Left rate) "VarLag" [in_,time,curvature,warp,start] Nothing 0 (Special 0) NoId
+
+-- | Variable duty saw
+varSaw :: Rate -> UGen -> UGen -> UGen -> UGen
+varSaw rate freq iphase width = mkUGen Nothing [KR,AR] (Left rate) "VarSaw" [freq,iphase,width] Nothing 1 (Special 0) NoId
+
+-- | The Vibrato oscillator models a slow frequency modulation.
+vibrato :: ID a => a -> Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+vibrato z rate freq rate_ depth delay onset rateVariation depthVariation iphase = mkUGen Nothing [KR,AR] (Left rate) "Vibrato" [freq,rate_,depth,delay,onset,rateVariation,depthVariation,iphase] Nothing 1 (Special 0) (toUId z)
+
+-- | Warp a buffer with a time pointer
+warp1 :: Int -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+warp1 numChannels bufnum pointer freqScale windowSize envbufnum overlaps windowRandRatio interp = mkUGen Nothing [AR] (Left AR) "Warp1" [bufnum,pointer,freqScale,windowSize,envbufnum,overlaps,windowRandRatio,interp] Nothing numChannels (Special 0) NoId
+
+-- | White noise.
+whiteNoise :: ID a => a -> Rate -> UGen
+whiteNoise z rate = mkUGen Nothing [KR,AR] (Left rate) "WhiteNoise" [] Nothing 1 (Special 0) (toUId z)
+
+-- | (Undocumented class)
+widthFirstUGen :: Rate -> UGen -> UGen
+widthFirstUGen rate maxSize = mkUGen Nothing [IR,KR,AR,DR] (Left rate) "WidthFirstUGen" [maxSize] Nothing 1 (Special 0) NoId
+
+-- | Wrap a signal outside given thresholds.
+wrap :: UGen -> UGen -> UGen -> UGen
+wrap in_ lo hi = mkUGen Nothing [IR,KR,AR] (Right [0]) "Wrap" [in_,lo,hi] Nothing 1 (Special 0) NoId
+
+-- | Index into a table with a signal.
+wrapIndex :: UGen -> UGen -> UGen
+wrapIndex bufnum in_ = mkUGen Nothing [KR,AR] (Right [1]) "WrapIndex" [bufnum,in_] Nothing 1 (Special 0) NoId
+
+-- | Equal power two channel cross fade.
+xFade2 :: UGen -> UGen -> UGen -> UGen -> UGen
+xFade2 inA inB pan level = mkUGen Nothing [KR,AR] (Right [0,1]) "XFade2" [inA,inB,pan,level] Nothing 1 (Special 0) NoId
+
+-- | Exponential line generator.
+xLine :: Rate -> UGen -> UGen -> UGen -> DoneAction -> UGen
+xLine rate start end dur doneAction = mkUGen Nothing [KR,AR] (Left rate) "XLine" [start,end,dur,(from_done_action doneAction)] Nothing 1 (Special 0) NoId
+
+-- | Send signal to a bus, crossfading with previous contents.
+xOut :: UGen -> UGen -> UGen -> UGen
+xOut bus xfade input = mkUGen Nothing [KR,AR] (Right [2]) "XOut" [bus,xfade] (Just input) 0 (Special 0) NoId
+
+-- | Zero crossing frequency follower
+zeroCrossing :: UGen -> UGen
+zeroCrossing in_ = mkUGen Nothing [KR,AR] (Right [0]) "ZeroCrossing" [in_] Nothing 1 (Special 0) NoId
+
+-- | LocalBuf count
+maxLocalBufs :: Rate -> UGen -> UGen
+maxLocalBufs rate count = mkUGen Nothing [IR] (Left rate) "MaxLocalBufs" [count] Nothing 1 (Special 0) NoId
+
+-- | Multiply add
+mulAdd :: UGen -> UGen -> UGen -> UGen
+mulAdd in_ mul add = mkUGen Nothing [IR,KR,AR] (Right [0]) "MulAdd" [in_,mul,add] Nothing 1 (Special 0) NoId
+
+-- | Set local buffer
+setBuf :: UGen -> UGen -> UGen -> UGen -> UGen
+setBuf buf offset length_ array = mkUGen Nothing [IR] (Left IR) "SetBuf" [buf,offset,length_] (Just array) 1 (Special 0) NoId
diff --git a/Sound/SC3/UGen/Bindings/HW.hs b/Sound/SC3/UGen/Bindings/HW.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/UGen/Bindings/HW.hs
@@ -0,0 +1,48 @@
+-- | Hand-written bindings.
+module Sound.SC3.UGen.Bindings.HW where
+
+import Sound.SC3.UGen.Bindings.HW.Construct
+import Sound.SC3.UGen.Identifier
+import Sound.SC3.UGen.Rate
+import Sound.SC3.UGen.Type
+import Sound.SC3.UGen.UGen
+
+-- | Zero local buffer.
+--
+-- ClearBuf does not copy the buffer number through so this is an MRG node.
+clearBuf :: UGen -> UGen
+clearBuf b = mrg2 b (mkOsc IR "ClearBuf" [b] 1)
+
+-- | Demand rate weighted random sequence generator.
+dwrand :: ID i => i -> UGen -> UGen -> UGen -> UGen
+dwrand z repeats weights list_ =
+    let n = mceDegree list_
+        weights' = mceExtend n weights
+        inp = repeats : constant n : weights'
+    in mkUGen Nothing [DR] (Left DR) "Dwrand" inp (Just list_) 1 (Special 0) (toUId z)
+
+-- | Outputs signal for @FFT@ chains, without performing FFT.
+fftTrigger :: UGen -> UGen -> UGen -> UGen
+fftTrigger b h p = mkOsc KR "FFTTrigger" [b,h,p] 1
+
+-- | 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 =
+    let n = constant (mceDegree mp)
+    in mkOscMCE KR "PackFFT" [b, sz, from, to, z, n] mp 1
+
+-- | 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
+
+-- | Send a reply message from the server back to the all registered clients.
+sendReply :: UGen -> UGen -> String -> [UGen] -> UGen
+sendReply i k n v =
+    let n' = map (fromIntegral . fromEnum) n
+        s = fromIntegral (length n')
+    in mkFilter "SendReply" ([i,k,s] ++ n' ++ v) 0
+
+-- | Unpack a single value (magnitude or phase) from an FFT chain
+unpack1FFT :: UGen -> UGen -> UGen -> UGen -> UGen
+unpack1FFT buf size index' which = mkOsc DR "Unpack1FFT" [buf, size, index', which] 1
+
diff --git a/Sound/SC3/UGen/Bindings/HW/Construct.hs b/Sound/SC3/UGen/Bindings/HW/Construct.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/UGen/Bindings/HW/Construct.hs
@@ -0,0 +1,82 @@
+-- | For hand-writing UGens.
+module Sound.SC3.UGen.Bindings.HW.Construct where
+
+import Sound.SC3.UGen.Rate
+import Sound.SC3.UGen.Type
+
+-- | Oscillator constructor with constrained set of operating 'Rate's.
+mk_osc :: [Rate] -> UGenId -> Rate -> String -> [UGen] -> Int -> UGen
+mk_osc rs z r c i o =
+    if r `elem` rs
+    then mkUGen Nothing rs (Left r) c i Nothing o (Special 0) z
+    else error ("mk_osc: rate restricted: " ++ show (r, rs, c))
+
+-- | Oscillator constructor with 'all_rates'.
+mkOsc :: Rate -> String -> [UGen] -> Int -> UGen
+mkOsc = mk_osc all_rates no_id
+
+-- | Oscillator constructor, rate restricted variant.
+mkOscR :: [Rate] -> Rate -> String -> [UGen] -> Int -> UGen
+mkOscR rs = mk_osc rs no_id
+
+-- | Rate restricted oscillator constructor, setting identifier.
+mkOscIdR :: [Rate] -> UGenId -> Rate -> String -> [UGen] -> Int -> UGen
+mkOscIdR rr z = mk_osc rr z
+
+-- | Oscillator constructor, setting identifier.
+mkOscId :: UGenId -> Rate -> String -> [UGen] -> Int -> UGen
+mkOscId z = mk_osc all_rates z
+
+-- | Provided 'UGenId' variant of 'mkOscMCE'.
+mk_osc_mce :: UGenId -> Rate -> String -> [UGen] -> UGen -> Int -> UGen
+mk_osc_mce z r c i j =
+    let i' = i ++ mceChannels j
+    in mk_osc all_rates z r c i'
+
+-- | Variant oscillator constructor with MCE collapsing input.
+mkOscMCE :: Rate -> String -> [UGen] -> UGen -> Int -> UGen
+mkOscMCE = mk_osc_mce no_id
+
+-- | Variant oscillator constructor with MCE collapsing input.
+mkOscMCEId :: UGenId -> Rate -> String -> [UGen] -> UGen -> Int -> UGen
+mkOscMCEId z = mk_osc_mce z
+
+-- | Rate constrained filter 'UGen' constructor.
+mk_filter :: [Rate] -> [Int] -> UGenId -> String -> [UGen] -> Int -> UGen
+mk_filter rs ix z c i o = mkUGen Nothing rs (Right ix) c i Nothing o (Special 0) z
+
+-- | Filter UGen constructor.
+mkFilterIdR :: [Rate] -> UGenId -> String -> [UGen] -> Int -> UGen
+mkFilterIdR rs z nm i o = mk_filter rs [0 .. length i - 1] z nm i o
+
+-- | Filter UGen constructor.
+mkFilterR :: [Rate] -> String -> [UGen] -> Int -> UGen
+mkFilterR rs = mkFilterIdR rs no_id
+
+-- | Filter 'UGen' constructor.
+mkFilter :: String -> [UGen] -> Int -> UGen
+mkFilter = mkFilterR all_rates
+
+-- | Filter UGen constructor.
+mkFilterId :: UGenId -> String -> [UGen] -> Int -> UGen
+mkFilterId = mkFilterIdR all_rates
+
+-- | Provided 'UGenId' filter with 'mce' input.
+mk_filter_mce :: [Rate] -> UGenId -> String -> [UGen] -> UGen -> Int -> UGen
+mk_filter_mce rs z c i j = mkFilterIdR rs z c (i ++ mceChannels j)
+
+-- | Variant filter constructor with MCE collapsing input.
+mkFilterMCER :: [Rate] -> String -> [UGen] -> UGen -> Int -> UGen
+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 no_id
+
+-- | Variant filter constructor with MCE collapsing input.
+mkFilterMCEId :: UGenId -> String -> [UGen] -> UGen -> Int -> UGen
+mkFilterMCEId z = mk_filter_mce all_rates z
+
+-- | Information unit generators are very specialized.
+mkInfo :: String -> UGen
+mkInfo name = mkOsc IR name [] 1
diff --git a/Sound/SC3/UGen/Bindings/HW/External.hs b/Sound/SC3/UGen/Bindings/HW/External.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/UGen/Bindings/HW/External.hs
@@ -0,0 +1,10 @@
+-- | Bindings to unit generators not distributed with SuperCollider
+--   proper.
+module Sound.SC3.UGen.Bindings.HW.External (module U) where
+
+import Sound.SC3.UGen.Bindings.HW.External.ATS as U
+import Sound.SC3.UGen.Bindings.HW.External.F0 as U
+import Sound.SC3.UGen.Bindings.HW.External.ID as U
+import Sound.SC3.UGen.Bindings.HW.External.LPC as U
+import Sound.SC3.UGen.Bindings.HW.External.SC3_Plugins as U
+import Sound.SC3.UGen.Bindings.HW.External.Zita as U
diff --git a/Sound/SC3/UGen/Bindings/HW/External/ATS.hs b/Sound/SC3/UGen/Bindings/HW/External/ATS.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/UGen/Bindings/HW/External/ATS.hs
@@ -0,0 +1,107 @@
+-- | Reader for ATS analyis data files.
+module Sound.SC3.UGen.Bindings.HW.External.ATS (ATS(..)
+                                               ,ATSHeader(..)
+                                               ,ATSFrame,atsFrames
+                                               ,atsRead) where
+
+import qualified Data.ByteString.Lazy as B {- bytestring -}
+import Data.Int {- base -}
+import Data.List.Split {- split -}
+import Sound.OSC.Coding.Byte {- hosc -}
+
+-- | ATS analysis data.
+data ATS = ATS { atsHeader :: ATSHeader
+               , atsData :: [Double] }
+           deriving (Eq, Show)
+
+-- | ATS analysis meta-data.
+data ATSHeader = ATSHeader { atsSampleRate :: Double
+                           , atsFrameSize :: Int
+                           , atsWindowSize :: Int
+                           , atsNPartials :: Int
+                           , atsNFrames :: Int
+                           , atsMaxAmplitude :: Double
+                           , atsMaxFrequency :: Double
+                           , atsAnalysisDuration :: Double
+                           , atsFileType :: Int
+                           , atsFrameLength :: Int
+                           } deriving (Eq, Show)
+
+-- | ATS analysis frame data.
+type ATSFrame = [Double]
+
+bSep :: Int64 -> Int64 -> B.ByteString -> [B.ByteString]
+bSep n i d =
+    if i == 1
+    then [d]
+    else let (p,q) = B.splitAt n d
+         in p : bSep n (i - 1) q
+
+atsParse :: FilePath -> IO [Double]
+atsParse fn = do
+  d <- B.readFile fn
+  let n = B.length d `div` 8
+      v = B.take 8 d
+      f = get_decoder v
+  return (map f (bSep 8 n d))
+
+-- | Read an ATS data file.
+atsRead :: FilePath -> IO ATS
+atsRead fn = do
+  d <- atsParse fn
+  let f j = d !! j
+      g = floor . f
+      ft = g 9
+      (n, x) = ftype_n ft
+      np = g 4
+      nf = g 5
+      fl = np * n + x
+      hdr = ATSHeader (f 1) (g 2) (g 3) np nf (f 6) (f 7) (f 8) ft fl
+  return (ATS hdr d)
+
+-- | Extract set of 'ATSFrame's from 'ATS'.
+atsFrames :: ATS -> [ATSFrame]
+atsFrames a = chunksOf (atsFrameLength (atsHeader a)) (atsData a)
+
+-- Determine endianess and hence decoder.
+get_decoder :: B.ByteString -> B.ByteString -> Double
+get_decoder v =
+    if decode_f64 v == 123.0
+    then decode_f64
+    else decode_f64 . B.reverse
+
+-- Calculate partial depth and frame constant.
+ftype_n :: Int -> (Int, Int)
+ftype_n n =
+    case n of
+      1 -> (2, 1)
+      2 -> (3, 1)
+      3 -> (2, 26)
+      4 -> (3, 26)
+      _ -> error "ftype_n"
+
+{-
+-- | Analysis data in format required by the sc3 ATS UGens.
+atsSC3 :: ATS -> [Double]
+atsSC3 (ATS h d) =
+    let f = fromIntegral
+        td = transpose d
+    in f (atsFileType h) :
+       f (atsNPartials h) :
+       f (atsNFrames h) :
+       f (atsWindowSize h) :
+       concatMap (td !!) (atsSC3Indices h)
+
+-- Indices for track data in the order required by sc3.
+atsSC3Indices :: ATSHeader -> [Int]
+atsSC3Indices h =
+    let np = atsNPartials h
+        o = 3 * (np - 1)
+        a = [1,4 .. (1 + o)]
+        f = map (+ 1) a
+        p = map (+ 1) f
+        n = map (+ (4+o)) [0..24]
+    in if atsFileType h == 4
+       then a ++ f ++ p ++ n
+       else error "atsSC3Indices: illegal ATS file type (/= 4)"
+-}
diff --git a/Sound/SC3/UGen/Bindings/HW/External/F0.hs b/Sound/SC3/UGen/Bindings/HW/External/F0.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/UGen/Bindings/HW/External/F0.hs
@@ -0,0 +1,20 @@
+-- | F0 UGens.
+module Sound.SC3.UGen.Bindings.HW.External.F0 where
+
+import Sound.SC3.UGen.Bindings.HW.Construct
+import Sound.SC3.UGen.Rate
+import Sound.SC3.UGen.Type
+
+-- * f0plugins
+
+-- | 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
+
+-- Local Variables:
+-- truncate-lines:t
+-- End:
diff --git a/Sound/SC3/UGen/Bindings/HW/External/ID.hs b/Sound/SC3/UGen/Bindings/HW/External/ID.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/UGen/Bindings/HW/External/ID.hs
@@ -0,0 +1,22 @@
+-- | Non-deterministic external 'UGen's.
+module Sound.SC3.UGen.Bindings.HW.External.ID where
+
+import Sound.SC3.UGen.Bindings.HW.Construct
+import Sound.SC3.UGen.Identifier
+import Sound.SC3.UGen.Rate
+import Sound.SC3.UGen.Type
+import Sound.SC3.UGen.UGen
+
+-- * SC3plugins/BhobUGens
+
+-- | random walk step
+lfBrownNoise0 :: ID a => a -> Rate -> UGen -> UGen -> UGen -> UGen
+lfBrownNoise0 z r freq dev dist = mkOscIdR [AR,KR] (toUId 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] (toUId 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] (toUId z) r "LFBrownNoise2" [freq,dev,dist] 1
diff --git a/Sound/SC3/UGen/Bindings/HW/External/LPC.hs b/Sound/SC3/UGen/Bindings/HW/External/LPC.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/UGen/Bindings/HW/External/LPC.hs
@@ -0,0 +1,63 @@
+-- | Reader for LPC analysis data files.
+module Sound.SC3.UGen.Bindings.HW.External.LPC ( LPC(..)
+                                               , LPCHeader(..)
+                                               , LPCFrame
+                                               , lpcRead
+                                               , lpcSC3 ) where
+
+import Control.Monad {- base -}
+import qualified Data.ByteString.Lazy as B {- bytestring -}
+import Data.List {- base -}
+import System.IO {- base -}
+
+import Sound.OSC.Coding.Byte {- hosc -}
+
+-- | LPC analysis data.
+data LPC = LPC { lpcHeader :: LPCHeader
+               , lpcFrames :: [LPCFrame] }
+           deriving (Eq, Show)
+
+-- | LPC analysis meta-data.
+data LPCHeader = LPCHeader { lpcHeaderSize :: Int
+                           , lpcMagic :: Int
+                           , lpcNPoles :: Int
+                           , lpcFrameSize :: Int
+                           , lpcFrameRate :: Float
+                           , lpcSampleRate :: Float
+                           , lpcAnalysisDuration :: Float
+                           , lpcNFrames :: Int
+                           } deriving (Eq, Show)
+
+-- | LPC analysis frame data.
+type LPCFrame = [Float]
+
+-- | Read an lpanal format LPC data file.
+lpcRead :: FilePath -> IO LPC
+lpcRead fn = do
+  h <- openFile fn ReadMode
+  l <- hFileSize h
+  [hs, lm, np, fs] <- replicateM 4 (read_i32 h)
+  [fr, sr, fd] <- replicateM 3 (read_f32 h)
+  let nf = ((fromIntegral l - hs) `div` 4) `div` fs
+      hdr = LPCHeader hs lm np fs fr sr fd nf
+      hc = hs - (7 * 4)
+      get_f = replicateM fs (read_f32 h)
+  _ <- B.hGet h hc
+  d <- replicateM nf get_f
+  hClose h
+  return (LPC hdr d)
+
+-- | Analysis data in format required by the sc3 LPC UGens.
+lpcSC3 :: LPC -> [Float]
+lpcSC3 (LPC h d) = let f = fromIntegral
+                       np = f (lpcNPoles h)
+                       nf = f (lpcNFrames h)
+                       fs = f (lpcFrameSize h)
+                   in np : nf : fs : concat (transpose d)
+
+read_i32 :: Handle -> IO Int
+read_i32 h = liftM decode_i32 (B.hGet h 4)
+
+read_f32 :: Handle -> IO Float
+read_f32 h = liftM decode_f32 (B.hGet h 4)
+
diff --git a/Sound/SC3/UGen/Bindings/HW/External/SC3_Plugins.hs b/Sound/SC3/UGen/Bindings/HW/External/SC3_Plugins.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/UGen/Bindings/HW/External/SC3_Plugins.hs
@@ -0,0 +1,345 @@
+-- | Bindings to unit generators in sc3-plugins.
+module Sound.SC3.UGen.Bindings.HW.External.SC3_Plugins where
+
+import Sound.SC3.UGen.Bindings.HW.Construct
+import Sound.SC3.UGen.Identifier
+import Sound.SC3.UGen.Rate
+import Sound.SC3.UGen.Type
+import Sound.SC3.UGen.UGen
+
+-- * AntiAliasingOscillators (Nick Collins)
+
+-- | Band limited impulse generation
+blitB3 :: Rate -> UGen -> UGen
+blitB3 rate freq = mkOscR [AR] rate "BlitB3" [freq] 1
+
+-- | BLIT derived sawtooth
+blitB3Saw :: Rate -> UGen -> UGen -> UGen
+blitB3Saw rate freq leak = mkOscR [AR] rate "BlitB3Saw" [freq,leak] 1
+
+-- | Bipolar BLIT derived square waveform
+blitB3Square :: Rate -> UGen -> UGen -> UGen
+blitB3Square rate freq leak = mkOscR [AR] rate "BlitB3Square" [freq,leak] 1
+
+-- | Bipolar BLIT derived triangle
+blitB3Tri :: Rate -> UGen -> UGen -> UGen -> UGen
+blitB3Tri rate freq leak leak2 = mkOscR [AR] rate "BlitB3Tri" [freq,leak,leak2] 1
+
+-- | Triangle via 3rd order differerentiated polynomial waveform
+dPW3Tri :: Rate -> UGen -> UGen
+dPW3Tri rate freq = mkOscR [AR] rate "DPW3Tri" [freq] 1
+
+-- | Sawtooth via 4th order differerentiated polynomial waveform
+dPW4Saw :: Rate -> UGen -> UGen
+dPW4Saw rate freq = mkOscR [AR] rate "DPW4Saw" [freq] 1
+
+-- * AuditoryModeling
+
+-- | Single gammatone filter
+gammatone :: UGen -> UGen -> UGen -> UGen
+gammatone input centrefrequency bandwidth = mkFilterR [AR] "Gammatone" [input,centrefrequency,bandwidth] 1
+
+-- | Simple cochlear hair cell model
+hairCell :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+hairCell input spontaneousrate boostrate restorerate loss = mkFilterR [AR,KR] "HairCell" [input,spontaneousrate,boostrate,restorerate,loss] 1
+
+-- | Meddis cochlear hair cell model
+meddis :: UGen -> UGen
+meddis input = mkFilterR [AR,KR] "Meddis" [input] 1
+
+-- * 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)
+
+-- * BatUGens
+
+-- | An amplitude tracking based onset detector
+coyote :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+coyote rate in_ trackFall slowLag fastLag fastMul thresh minDur = mkOscR [KR] rate "Coyote" [in_,trackFall,slowLag,fastLag,fastMul,thresh,minDur] 1
+
+-- | Windowed amplitude follower
+wAmp :: Rate -> UGen -> UGen -> UGen
+wAmp rate in_ winSize = mkOscR [KR] rate "WAmp" [in_,winSize] 1
+
+-- * BhobUGens
+
+-- | Impulses around a certain frequency
+gaussTrig :: Rate -> UGen -> UGen -> UGen
+gaussTrig rate freq dev = mkOscR [AR,KR] rate "GaussTrig" [freq,dev] 1
+
+-- | String resonance filter
+streson :: UGen -> UGen -> UGen -> UGen
+streson input delayTime res = mkFilter "Streson" [input,delayTime,res] 1
+
+-- | Triggered beta random distribution
+tBetaRand :: ID a => a -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+tBetaRand z lo hi prob1 prob2 trig_ = mkFilterIdR [AR,KR] (toUId z) "TBetaRand" [lo,hi,prob1,prob2,trig_] 1
+
+-- | Triggered random walk generator
+tBrownRand :: ID a => a -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+tBrownRand z lo hi dev dist trig_ = mkFilterIdR [AR,KR] (toUId z) "TBrownRand" [lo,hi,dev,dist,trig_] 1
+
+-- | Triggered gaussian random distribution
+tGaussRand :: ID a => a -> UGen -> UGen -> UGen -> UGen
+tGaussRand z lo hi trig_ = mkFilterIdR [AR,KR] (toUId z) "TGaussRand" [lo,hi,trig_] 1
+
+-- * 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
+
+-- * DEIND UGens
+
+-- | FM-modulable resonating filter
+complexRes :: Rate -> UGen -> UGen -> UGen -> UGen
+complexRes rate in_ freq decay_ = mkOscR [AR] rate "ComplexRes" [in_,freq,decay_] 1
+
+-- | Ring modulation based on the physical model of a diode.
+diodeRingMod :: Rate -> UGen -> UGen -> UGen
+diodeRingMod rate car mod_ = mkOscR [AR] rate "DiodeRingMod" [car,mod_] 1
+
+-- | Demand rate implementation of a Wiard noise ring
+dNoiseRing :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+dNoiseRing rate change chance shift numBits resetval = mkOscR [] rate "DNoiseRing" [change,chance,shift,numBits,resetval] 1
+
+-- | algorithmic delay
+greyholeRaw :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+greyholeRaw rate in1 in2 damping delaytime diffusion feedback moddepth modfreq size = mkOscR [AR] rate "GreyholeRaw" [in1,in2,damping,delaytime,diffusion,feedback,moddepth,modfreq,size] 1
+
+-- | Raw version of the JPverb algorithmic reverberator, designed to produce long tails with chorusing
+jPverbRaw :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+jPverbRaw rate in1 in2 damp earlydiff highband highx lowband lowx mdepth mfreq midx size t60 = mkOscR [AR,KR] rate "JPverbRaw" [in1,in2,damp,earlydiff,highband,highx,lowband,lowx,mdepth,mfreq,midx,size,t60] 1
+
+-- * Distortion
+
+-- | Brown noise.
+disintegrator :: ID a => a -> UGen -> UGen -> UGen -> UGen
+disintegrator z i p m = mkFilterId (toUId z) "Disintegrator" [i,p,m] 1
+
+-- * DWGUGens
+
+-- | Plucked physical model.
+dWGPlucked2 :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+dWGPlucked2 rate freq amp gate_ pos c1 c3 inp release mistune mp gc = mkOscR [AR] rate "DWGPlucked2" [freq,amp,gate_,pos,c1,c3,inp,release,mistune,mp,gc] 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
+
+-- | Delay and Feedback on a bin by bin basis.
+pv_BinDelay :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+pv_BinDelay buffer maxdelay delaybuf fbbuf hop = mkOsc KR "PV_BinDelay" [buffer,maxdelay,delaybuf,fbbuf,hop] 1
+
+-- | Play FFT data from a memory buffer.
+pv_BufRd :: UGen -> UGen -> UGen -> UGen
+pv_BufRd buffer playbuf_ point = mkOsc KR "PV_BufRd" [buffer,playbuf_,point] 1
+
+-- | /dur/ and /hop/ are in seconds, /frameSize/ and /sampleRate/ in
+-- frames, though the latter maybe fractional.
+--
+-- > pv_calcPVRecSize 4.2832879818594 1024 0.25 48000.0 == 823299
+pv_calcPVRecSize :: Double -> Int -> Double -> Double -> Int
+pv_calcPVRecSize dur frameSize hop sampleRate =
+    let frameSize' = fromIntegral frameSize
+        rawsize = ceiling ((dur * sampleRate) / frameSize') * frameSize
+    in ceiling (fromIntegral rawsize * recip hop + 3)
+
+-- | Invert FFT amplitude data.
+pv_Invert :: UGen -> UGen
+pv_Invert b = mkOsc KR "PV_Invert" [b] 1
+
+-- | Plays FFT data from a memory buffer.
+pv_PlayBuf :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+pv_PlayBuf buffer playbuf_ rate_ offset loop = mkOsc KR "PV_PlayBuf" [buffer,playbuf_,rate_,offset,loop] 1
+
+-- | Records FFT data to a memory buffer.
+pv_RecordBuf :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+pv_RecordBuf buffer recbuf offset run loop hop wintype = mkOsc KR "PV_RecordBuf" [buffer,recbuf,offset,run,loop,hop,wintype] 1
+
+-- | Sample looping oscillator
+loopBuf :: Int -> Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+loopBuf numChannels rate bufnum rate_ gate_ startPos startLoop endLoop interpolation = mkOscR [AR] rate "LoopBuf" [bufnum,rate_,gate_,startPos,startLoop,endLoop,interpolation] numChannels
+
+-- * MCLD
+
+-- | Detect the largest value (and its position) in an array of UGens
+arrayMax :: Rate -> UGen -> UGen
+arrayMax rate array = mkOscR [AR,KR] rate "ArrayMax" [array] 2
+
+-- | Detect the smallest value (and its position) in an array of UGens
+arrayMin :: Rate -> UGen -> UGen
+arrayMin rate array = mkOscR [AR,KR] rate "ArrayMin" [array] 2
+
+-- | Detect the largest value (and its position) in an array of UGens
+bufMax :: Rate -> UGen -> UGen -> UGen
+bufMax rate bufnum gate_ = mkOscR [KR] rate "BufMax" [bufnum,gate_] 2
+
+-- | Detect the largest value (and its position) in an array of UGens
+bufMin :: Rate -> UGen -> UGen -> UGen
+bufMin rate bufnum gate_ = mkOscR [KR] rate "BufMin" [bufnum,gate_] 2
+
+-- | 3D Perlin Noise
+perlin3 :: Rate -> UGen -> UGen -> UGen -> UGen
+perlin3 rate x y z = mkOscR [AR,KR] rate "Perlin3" [x,y,z] 1
+
+-- | Wave squeezer. Maybe a kind of pitch shifter.
+squiz :: UGen -> UGen -> UGen -> UGen -> UGen
+squiz in_ pitchratio zcperchunk memlen = mkFilterR [AR,KR] "Squiz" [in_,pitchratio,zcperchunk,memlen] 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
+
+-- | Spectral Modeling Synthesis
+sms :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+sms input maxpeaks currentpeaks tolerance noisefloor freqmult freqadd formantpreserve useifft ampmult graphicsbufnum = mkFilterR [AR] "SMS" [input,maxpeaks,currentpeaks,tolerance,noisefloor,freqmult,freqadd,formantpreserve,useifft,ampmult,graphicsbufnum] 2
+
+-- | 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
+
+-- * PitchDetection
+
+-- | 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
+
+-- | 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
+
+-- * RFWUGens
+
+-- | Calculates mean average of audio or control rate signal.
+averageOutput :: UGen -> UGen -> UGen
+averageOutput in_ trig_ = mkFilterR [KR,AR] "AverageOutput" [in_,trig_] 1
+
+-- | Feedback delay line implementing switch-and-ramp buffer jumping.
+switchDelay :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+switchDelay in_ drylevel wetlevel delaytime delayfactor maxdelaytime = mkFilterR [AR] "SwitchDelay" [in_,drylevel,wetlevel,delaytime,delayfactor,maxdelaytime] 1
+
+-- * SCMIRUGens
+
+-- | Octave chroma band based representation of energy in a signal; Chromagram for nTET tuning systems with any base reference
+chromagram :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+chromagram rate fft_ fftsize n tuningbase octaves integrationflag coeff = mkOscR [KR] rate "Chromagram" [fft_,fftsize,n,tuningbase,octaves,integrationflag,coeff] 1
+
+-- * skUG
+
+-- | Phase modulation oscillator matrix.
+fm7 :: [[UGen]] -> [[UGen]] -> UGen
+fm7 ctl m0d = mkOsc AR "FM7" (concat ctl ++ concat m0d) 6
+
+-- * SLU
+
+-- | Prigogine oscillator
+brusselator :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+brusselator rate reset rate_ mu gamma initx inity = mkOscR [AR] rate "Brusselator" [reset,rate_,mu,gamma,initx,inity] 2
+
+-- | Forced DoubleWell Oscillator
+doubleWell3 :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+doubleWell3 rate reset rate_ f delta initx inity = mkOscR [AR] rate "DoubleWell3" [reset,rate_,f,delta,initx,inity] 1
+
+-- | Envelope Follower Filter
+envDetect :: Rate -> UGen -> UGen -> UGen -> UGen
+envDetect rate in_ attack release = mkOscR [AR] rate "EnvDetect" [in_,attack,release] 1
+
+-- | Envelope Follower
+envFollow :: Rate -> UGen -> UGen -> UGen
+envFollow rate input decaycoeff = mkOscR [AR,KR] rate "EnvFollow" [input,decaycoeff] 1
+
+-- | Linear Time Invariant General Filter Equation
+lti :: Rate -> UGen -> UGen -> UGen -> UGen
+lti rate input bufnuma bufnumb = mkOscR [AR] rate "LTI" [input,bufnuma,bufnumb] 1
+
+-- | Experimental time domain onset detector
+sLOnset :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+sLOnset rate input memorysize1 before after threshold hysteresis = mkOscR [KR] rate "SLOnset" [input,memorysize1,before,after,threshold,hysteresis] 1
+
+-- | wave terrain synthesis
+waveTerrain :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+waveTerrain rate bufnum x y xsize ysize = mkOscR [AR] rate "WaveTerrain" [bufnum,x,y,xsize,ysize] 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
+
+-- * TJUGens
+
+-- | Digitally modelled analog filter
+dfm1 :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+dfm1 i f r g ty nl = mkFilter "DFM1" [i,f,r,g,ty,nl] 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/Bindings/HW/External/Wavelets.hs b/Sound/SC3/UGen/Bindings/HW/External/Wavelets.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/UGen/Bindings/HW/External/Wavelets.hs
@@ -0,0 +1,31 @@
+-- | Wavelet unit generators (Nick Collins).
+module Sound.SC3.UGen.Bindings.HW.External.Wavelets where
+
+import Sound.SC3.UGen.Bindings.HW.Construct
+import Sound.SC3.UGen.Rate
+import Sound.SC3.UGen.Type
+
+-- | Forward wavelet transform.
+dwt :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+dwt buf i h wnt a wns wlt = mkOsc KR "DWT" [buf,i,h,wnt,a,wns,wlt] 1
+
+-- | Inverse of 'dwt'.
+idwt :: UGen -> UGen -> UGen -> UGen -> UGen
+idwt buf wnt wns wlt = mkOsc AR "IDWT" [buf,wnt,wns,wlt] 1
+
+-- | Pass wavelets above a threshold, ie. 'pv_MagAbove'.
+wt_MagAbove :: UGen -> UGen -> UGen
+wt_MagAbove buf thr = mkOsc KR "WT_MagAbove" [buf,thr] 1
+
+-- | Pass wavelets with /scale/ above threshold.
+wt_FilterScale :: UGen -> UGen -> UGen
+wt_FilterScale buf wp = mkOsc KR "WT_FilterScale" [buf,wp] 1
+
+-- | Pass wavelets with /time/ above threshold.
+wt_TimeWipe :: UGen -> UGen -> UGen
+wt_TimeWipe buf wp = mkOsc KR "WT_TimeWipe" [buf,wp] 1
+
+-- | Product in /W/ domain, ie. 'pv_Mul'.
+wt_Mul :: UGen -> UGen -> UGen
+wt_Mul ba bb = mkOsc KR "WT_Mul" [ba,bb] 1
+
diff --git a/Sound/SC3/UGen/Bindings/HW/External/Zita.hs b/Sound/SC3/UGen/Bindings/HW/External/Zita.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/UGen/Bindings/HW/External/Zita.hs
@@ -0,0 +1,58 @@
+-- | Zita UGen definitions.
+--
+-- To build the SC3 plugin run @faust2supercollider -d@ on
+-- @zita_rev1.dsp@, which is in the @examples@ directory of Faust, see
+-- <http://faust.grame.fr/>.
+module Sound.SC3.UGen.Bindings.HW.External.Zita where
+
+import Sound.SC3.UGen.Bindings.HW.Construct
+import Sound.SC3.UGen.Rate
+import Sound.SC3.UGen.Type
+
+data ZitaRev1 a =
+    ZitaRev1 {zr1_in1 :: a
+             ,zr1_in2 :: a
+             ,zr1_delay :: a
+             ,zr1_xover :: a
+             ,zr1_rtlow :: a
+             ,zr1_rtmid :: a
+             ,zr1_fdamp :: a
+             ,zr1_eq1fr :: a
+             ,zr1_eq1gn :: a
+             ,zr1_eq2fr :: a
+             ,zr1_eq2gn :: a
+             ,zr1_opmix :: a -- ^ (-1,+1)
+             ,zr1_level :: a}
+
+zitaRev1_r :: ZitaRev1 UGen -> UGen
+zitaRev1_r r =
+    let (ZitaRev1 in1 in2 dly xov rtl rtm fda e1f e1g e2f e2g opm lvl) = r
+    in zitaRev1 in1 in2 dly xov rtl rtm fda e1f e1g e2f e2g opm lvl
+
+zitaRev1 :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+zitaRev1 in1 in2 dly xov rtl rtm fda e1f e1g e2f e2g opm lvl = mkFilterR [AR] "FaustZitaRev1" [in1,in2,dly,xov,rtl,rtm,fda,e1f,e1g,e2f,e2g,opm,lvl] 2
+
+{-
+hsc3-db:
+
+std_I :: Int -> String -> Double -> I
+std_I ix nm df = I (ix,ix) nm df Nothing
+
+zitaRev1_dsc :: U
+zitaRev1_dsc =
+    let i = [std_I 0 "in1" 0.0
+            ,std_I 1 "in2" 0.0
+            ,std_I 2 "delay" 0.04
+            ,std_I 3 "xover" 200.0
+            ,std_I 4 "rtlow" 3.0
+            ,std_I 5 "rtmid" 2.0
+            ,std_I 6 "fdamp" 6.0e3
+            ,std_I 7 "eq1fr" 160
+            ,std_I 8 "eq1gn" 0.0
+            ,std_I 9 "eq2fr" 2.5e3
+            ,std_I 10 "eq2gn" 0.0
+            ,std_I 11 "opmix" 0.5
+            ,std_I 12 "level" (-20)]
+    in U "FaustZitaRev1" [AR] AR Nothing i Nothing (Left 2) "Zita Reverb 1"
+-}
+
diff --git a/Sound/SC3/UGen/Bindings/Monad.hs b/Sound/SC3/UGen/Bindings/Monad.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/UGen/Bindings/Monad.hs
@@ -0,0 +1,195 @@
+-- | Monad constructors for 'UGen's.
+module Sound.SC3.UGen.Bindings.Monad where
+
+import Sound.SC3.UGen.Bindings.DB
+import Sound.SC3.UGen.Bindings.HW
+import Sound.SC3.UGen.Enum
+import Sound.SC3.UGen.Rate
+import Sound.SC3.UGen.Type
+import Sound.SC3.UGen.UId
+
+-- * Demand
+
+-- | Buffer demand ugen.
+dbufrdM :: (UId m) => UGen -> UGen -> Loop -> m UGen
+dbufrdM = liftUId3 dbufrd
+
+-- | Buffer write on demand unit generator.
+dbufwrM :: (UId m) => UGen -> UGen -> UGen -> Loop -> m UGen
+dbufwrM = liftUId4 dbufwr
+
+-- | Demand rate white noise.
+dwhiteM :: (UId m) => UGen -> UGen -> UGen -> m UGen
+dwhiteM = liftUId3 dwhite
+
+-- | Demand rate integer white noise.
+diwhiteM :: (UId m) => UGen -> UGen -> UGen -> m UGen
+diwhiteM = liftUId3 diwhite
+
+-- | Demand rate brown noise.
+dbrownM :: (UId m) => UGen -> UGen -> UGen -> UGen -> m UGen
+dbrownM = liftUId4 dbrown
+
+-- | Demand rate integer brown noise.
+dibrownM :: (UId m) => UGen -> UGen -> UGen -> UGen -> m UGen
+dibrownM = liftUId4 dibrown
+
+-- | Demand rate random selection.
+drandM :: (UId m) => UGen -> UGen -> m UGen
+drandM = liftUId2 drand
+
+-- | Demand rate weighted random sequence generator.
+dwrandM :: (UId m) => UGen -> UGen -> UGen -> m UGen
+dwrandM = liftUId3 dwrand
+
+-- | Demand rate random selection with no immediate repetition.
+dxrandM :: (UId m) => UGen -> UGen -> m UGen
+dxrandM = liftUId2 dxrand
+
+-- | Demand rate arithmetic series.
+dseriesM :: (UId m) => UGen -> UGen -> UGen -> m UGen
+dseriesM = liftUId3 dseries
+
+-- | Demand rate geometric series.
+dgeomM :: (UId m) => UGen -> UGen -> UGen -> m UGen
+dgeomM = liftUId3 dgeom
+
+-- | Demand rate sequence generator.
+dseqM :: (UId m) => UGen -> UGen -> m UGen
+dseqM = liftUId2 dseq
+
+-- | Demand rate series generator.
+dserM :: (UId m) => UGen -> UGen -> m UGen
+dserM = liftUId2 dser
+
+-- | Demand rate sequence shuffler.
+dshufM :: (UId m) => UGen -> UGen -> m UGen
+dshufM = liftUId2 dshuf
+
+-- | Demand input replication
+dstutterM :: (UId m) => UGen -> UGen -> m UGen
+dstutterM = liftUId2 dstutter
+
+-- | Demand rate input switching.
+dswitch1M :: (UId m) => UGen -> UGen -> m UGen
+dswitch1M = liftUId2 dswitch1
+
+-- | Demand rate input switching.
+dswitchM :: (UId m) => UGen -> UGen -> m UGen
+dswitchM = liftUId2 dswitch
+
+-- * FFT
+
+-- | Randomize order of bins.
+pv_BinScrambleM :: (UId m) => UGen -> UGen -> UGen -> UGen -> m UGen
+pv_BinScrambleM = liftUId4 pv_BinScramble
+
+-- | Randomly clear bins.
+pv_RandCombM :: (UId m) => UGen -> UGen -> UGen -> m UGen
+pv_RandCombM = liftUId3 pv_RandComb
+
+-- | Cross fade, copying bins in random order.
+pv_RandWipeM :: (UId m) => UGen -> UGen -> UGen -> UGen -> m UGen
+pv_RandWipeM = liftUId4 pv_RandWipe
+
+-- * Noise
+
+-- | Brown noise.
+brownNoiseM :: (UId m) => Rate -> m UGen
+brownNoiseM = liftUId brownNoise
+
+-- | Clip noise.
+clipNoiseM :: (UId m) => Rate -> m UGen
+clipNoiseM = liftUId clipNoise
+
+-- | Randomly pass or block triggers.
+coinGateM :: (UId m) => UGen -> UGen -> m UGen
+coinGateM = liftUId2 coinGate
+
+-- | Random impulses in (-1, 1).
+dust2M :: (UId m) => Rate -> UGen -> m UGen
+dust2M = liftUId2 dust2
+
+-- | Random impulse in (0,1).
+dustM :: (UId m) => Rate -> UGen -> m UGen
+dustM = liftUId2 dust
+
+-- | Random value in exponential distribution.
+expRandM :: (UId m) => UGen -> UGen -> m UGen
+expRandM = liftUId2 expRand
+
+-- | Gray noise.
+grayNoiseM :: (UId m) => Rate -> m UGen
+grayNoiseM = liftUId grayNoise
+
+-- | Random integer in uniform distribution.
+iRandM :: (UId m) => UGen -> UGen -> m UGen
+iRandM = liftUId2 iRand
+
+-- | Clip noise.
+lfClipNoiseM :: (UId m) => Rate -> UGen -> m UGen
+lfClipNoiseM = liftUId2 lfClipNoise
+
+-- | Dynamic clip noise.
+lfdClipNoiseM :: (UId m) => Rate -> UGen -> m UGen
+lfdClipNoiseM = liftUId2 lfdClipNoise
+
+-- | Dynamic step noise.
+lfdNoise0M :: (UId m) => Rate -> UGen -> m UGen
+lfdNoise0M = liftUId2 lfdNoise0
+
+-- | Dynamic ramp noise.
+lfdNoise1M :: (UId m) => Rate -> UGen -> m UGen
+lfdNoise1M = liftUId2 lfdNoise1
+
+-- | Dynamic cubic noise
+lfdNoise3M :: (UId m) => Rate -> UGen -> m UGen
+lfdNoise3M = liftUId2 lfdNoise3
+
+-- | Step noise.
+lfNoise0M :: (UId m) => Rate -> UGen -> m UGen
+lfNoise0M = liftUId2 lfNoise0
+
+-- | Ramp noise.
+lfNoise1M :: (UId m) => Rate -> UGen -> m UGen
+lfNoise1M = liftUId2 lfNoise1
+
+-- | Quadratic noise.
+lfNoise2M :: (UId m) => Rate -> UGen -> m UGen
+lfNoise2M = liftUId2 lfNoise2
+
+-- | Random value in skewed linear distribution.
+linRandM :: (UId m) => UGen -> UGen -> UGen -> m UGen
+linRandM = liftUId3 linRand
+
+-- | Random value in sum of n linear distribution.
+nRandM :: (UId m) => UGen -> UGen -> UGen -> m UGen
+nRandM = liftUId3 nRand
+
+-- | Pink noise.
+pinkNoiseM :: (UId m) => Rate -> m UGen
+pinkNoiseM = liftUId pinkNoise
+
+-- | Random value in uniform distribution.
+randM :: (UId m) => UGen -> UGen -> m UGen
+randM = liftUId2 rand
+
+-- | Random value in exponential distribution on trigger.
+tExpRandM :: (UId m) => UGen -> UGen -> UGen -> m UGen
+tExpRandM = liftUId3 tExpRand
+
+-- | Random integer in uniform distribution on trigger.
+tIRandM :: (UId m) => UGen -> UGen -> UGen -> m UGen
+tIRandM = liftUId3 tIRand
+
+-- | Random value in uniform distribution on trigger.
+tRandM :: (UId m) => UGen -> UGen -> UGen -> m UGen
+tRandM = liftUId3 tRand
+
+-- | Triggered windex.
+tWindexM :: (UId m) => UGen -> UGen -> UGen -> m UGen
+tWindexM = liftUId3 tWindex
+
+-- | White noise.
+whiteNoiseM :: (UId m) => Rate -> m UGen
+whiteNoiseM = liftUId whiteNoise
diff --git a/Sound/SC3/UGen/Buffer.hs b/Sound/SC3/UGen/Buffer.hs
deleted file mode 100644
--- a/Sound/SC3/UGen/Buffer.hs
+++ /dev/null
@@ -1,161 +0,0 @@
--- | Unit generators to query, read and write audio buffers.
-module Sound.SC3.UGen.Buffer where
-
-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
-
--- * Buffer query unit generators
-
--- | Buffer channel count.
-bufChannels :: Rate -> UGen -> UGen
-bufChannels r buf = mkOsc r "BufChannels" [buf] 1
-
--- | Buffer duration, in seconds.
-bufDur :: Rate -> UGen -> UGen
-bufDur r buf = mkOsc r "BufDur" [buf] 1
-
--- | Buffer frame count.
-bufFrames :: Rate -> UGen -> UGen
-bufFrames r buf = mkOsc r "BufFrames" [buf] 1
-
--- | Buffer rate scalar with respect to server sample rate.
-bufRateScale :: Rate -> UGen -> UGen
-bufRateScale r buf = mkOsc r "BufRateScale" [buf] 1
-
--- | Buffer sample rate.
-bufSampleRate :: Rate -> UGen -> UGen
-bufSampleRate r buf = mkOsc r "BufSampleRate" [buf] 1
-
--- | Buffer sample count (ie. frame count by channel count).
-bufSamples :: Rate -> UGen -> UGen
-bufSamples r buf = mkOsc r "BufSamples" [buf] 1
-
--- * Buffer filters and delays
-
--- | Allpass filter (cubic interpolation).
-bufAllpassC :: UGen -> UGen -> UGen -> UGen -> UGen
-bufAllpassC buf i dly dcy = mkFilter "BufAllpassC" [buf, i, dly, dcy] 1
-
--- | Allpass filter (linear interpolation).
-bufAllpassL :: UGen -> UGen -> UGen -> UGen -> UGen
-bufAllpassL buf i dly dcy = mkFilter "BufAllpassL" [buf, i, dly, dcy] 1
-
--- | Allpass filter (no interpolation).
-bufAllpassN :: UGen -> UGen -> UGen -> UGen -> UGen
-bufAllpassN buf i dly dcy = mkFilter "BufAllpassN" [buf, i, dly, dcy] 1
-
--- | Comb filter (cubic interpolation).
-bufCombC :: UGen -> UGen -> UGen -> UGen -> UGen
-bufCombC buf i dly dcy = mkFilter "BufCombC" [buf, i, dly, dcy] 1
-
--- | Comb filter (linear interpolation).
-bufCombL :: UGen -> UGen -> UGen -> UGen -> UGen
-bufCombL buf i dly dcy = mkFilter "BufCombL" [buf, i, dly, dcy] 1
-
--- | Comb filter (no interpolation).
-bufCombN :: UGen -> UGen -> UGen -> UGen -> UGen
-bufCombN buf i dly dcy = mkFilter "BufCombN" [buf, i, dly, dcy] 1
-
--- | Delay line (cubic interpolation).
-bufDelayC :: UGen -> UGen -> UGen -> UGen
-bufDelayC buf i dly = mkFilter "BufDelayC" [buf, i, dly] 1
-
--- | Delay line (linear interpolation).
-bufDelayL :: UGen -> UGen -> UGen -> UGen
-bufDelayL buf i dly = mkFilter "BufDelayL" [buf, i, dly] 1
-
--- | Delay line (no interpolation).
-bufDelayN :: UGen -> UGen -> UGen -> UGen
-bufDelayN buf i dly = mkFilter "BufDelayN" [buf, i, dly] 1
-
--- * Buffer I\/O
-
--- | Buffer reader.
-bufRd :: Int -> Rate -> UGen -> UGen -> Loop -> Interpolation -> UGen
-bufRd n r buf phs lp intp = mkOsc r "BufRd" [buf, phs, from_loop lp, from_interpolation intp] n
-
--- | Buffer reader (no interpolation).
-bufRdN :: Int -> Rate -> UGen -> UGen -> Loop -> UGen
-bufRdN n r b p l = bufRd n r b p l NoInterpolation
-
--- | Buffer reader (linear interpolation).
-bufRdL :: Int -> Rate -> UGen -> UGen -> Loop -> UGen
-bufRdL n r b p l = bufRd n r b p l LinearInterpolation
-
--- | Buffer reader (cubic interpolation).
-bufRdC :: Int -> Rate -> UGen -> UGen -> Loop -> UGen
-bufRdC n r b p l = bufRd n r b p l CubicInterpolation
-
--- | Buffer writer.
-bufWr :: UGen -> UGen -> Loop -> UGen -> UGen
-bufWr buf phs lp i = mkFilterMCE "BufWr" [buf, phs, from_loop lp] i 0
-
--- | Search a buffer for a value.
-detectIndex :: UGen -> UGen -> UGen
-detectIndex b i = mkFilter "DetectIndex" [b, i] 1
-
--- | Index into table with signal.
-index :: UGen -> UGen -> UGen
-index b i = mkFilter "Index" [b, i] 1
-
--- | Interpolating search in ordered table.
-indexInBetween :: UGen -> UGen -> UGen
-indexInBetween b i = mkFilter "IndexInBetween" [b, i] 1
-
--- | Wavetable oscillator.
-osc :: Rate -> UGen -> UGen -> UGen -> UGen
-osc r bufnum freq phase = mkOsc r "Osc" [bufnum, freq, phase] 1
-
--- | Wavetable oscillator.
-oscN :: Rate -> UGen -> UGen -> UGen -> UGen
-oscN r bufnum freq phase = mkOsc r "OscN" [bufnum, freq, phase] 1
-
--- | Buffer playback.
-playBuf :: Int -> Rate -> UGen -> UGen -> UGen -> UGen -> Loop -> DoneAction -> UGen
-playBuf n rt b r t s l a = mkOsc rt "PlayBuf" [b, r, t, s, from_loop l, from_done_action a] n
-
--- | Buffer recording.
-recordBuf :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> Loop -> UGen -> DoneAction -> UGen -> UGen
-recordBuf rt b o rl pl r l t a i = mkOscMCE rt "RecordBuf" [b, o, rl, pl, r, from_loop l, t, from_done_action a] i 0
-
--- | Triggered buffer shuffler (grain generator).
-tGrains :: Int -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-tGrains n t b r c d p a i = mkOsc AR "TGrains" [t, b, r, c, d, p, a, i] n
-
--- | Three variable wavetable oscillator.
-vOsc3 :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen
-vOsc3 r b f1 f2 f3 = mkOsc r "VOsc3" [b, f1, f2, f3] 1
-
--- | Variable wavetable oscillator.
-vOsc :: Rate -> UGen -> UGen -> UGen -> UGen
-vOsc r b f phase = mkOsc r "VOsc" [b, f, phase] 1
-
--- * Local buffers
-
--- | Allocate a buffer local to the synth.
-localBuf :: ID i => i -> UGen -> UGen -> UGen
-localBuf z nf nc = mkOscId z IR "LocalBuf" [nc, nf] 1
--- note that nf & nc are swapped at actual ugen
-
--- | Set the maximum number of local buffers in a synth.
-maxLocalBufs :: UGen -> UGen
-maxLocalBufs n = mkOsc IR "MaxLocalBufs" [n] 0
-
--- | Set local buffer values.
-setBuf :: UGen -> [UGen] -> UGen -> UGen
-setBuf b xs o = mkOsc IR "SetBuf" ([b, o, fromIntegral (length xs)] ++ xs) 1
-
--- | Generate a localBuf and use setBuf to initialise it.
-asLocalBuf :: ID i => i -> [UGen] -> UGen
-asLocalBuf i xs =
-    let m = maxLocalBufs 8
-        b = mrg2 (localBuf i (fromIntegral (length xs)) 1) m
-        s = setBuf b xs 0
-    in mrg2 b s
-
--- Local Variables:
--- truncate-lines:t
--- End:
diff --git a/Sound/SC3/UGen/Chaos.hs b/Sound/SC3/UGen/Chaos.hs
deleted file mode 100644
--- a/Sound/SC3/UGen/Chaos.hs
+++ /dev/null
@@ -1,98 +0,0 @@
--- | Chaotic functions.
-module Sound.SC3.UGen.Chaos where
-
-import Sound.SC3.UGen.Rate
-import Sound.SC3.UGen.Type
-import Sound.SC3.UGen.UGen
-
--- | Chaotic noise.
-crackle :: Rate -> UGen -> UGen
-crackle r chaosParam = mkOsc r "Crackle" [chaosParam] 1
-
--- | Cusp map chaotic generator (linear interpolation).
-cuspL :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen
-cuspL r freq a b xi = mkOsc r "CuspL" [freq, a, b, xi] 1
-
--- | Cusp map chaotic generator (no interpolation).
-cuspN :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen
-cuspN r freq a b xi = mkOsc r "CuspN" [freq, a, b, xi] 1
-
--- | Feedback sine with chaotic phase indexing (cubic interpolation).
-fbSineC :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-fbSineC r freq im fb a c xi yi = mkOsc r "FBSineC" [freq, im, fb, a, c, xi, yi] 1
-
--- | Feedback sine with chaotic phase indexing (linear interpolation).
-fbSineL :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-fbSineL r freq im fb a c xi yi = mkOsc r "FBSineL" [freq, im, fb, a, c, xi, yi] 1
-
--- | 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
-henonC r freq a b x0 x1 = mkOsc r "HenonC" [freq, a, b, x0, x1] 1
-
--- | Henon map chaotic generator (linear interpolation).
-henonL :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-henonL r freq a b x0 x1 = mkOsc r "HenonL" [freq, a, b, x0, x1] 1
-
--- | Henon map chaotic generator (no interpolation).
-henonN :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-henonN r freq a b x0 x1 = mkOsc r "HenonN" [freq, a, b, x0, x1] 1
-
--- | Latoocarfian chaotic function (cubic interpolation).
-latoocarfianC :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-latoocarfianC r f a b c d xi yi = mkOsc r "LatoocarfianC" [f, a, b, c, d, xi, yi] 1
-
--- | Latoocarfian chaotic function (linear interpolation).
-latoocarfianL :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-latoocarfianL r f a b c d xi yi = mkOsc r "LatoocarfianL" [f, a, b, c, d, xi, yi] 1
-
--- | Latoocarfian chaotic function (no interpolation).
-latoocarfianN :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-latoocarfianN r f a b c d xi yi = mkOsc r "LatoocarfianN" [f, a, b, c, d, xi, yi] 1
-
--- | Linear congruential chaotic generator (cubic interpolation).
-linCongC :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-linCongC r f a c m xi = mkOsc r "LinCongC" [f, a, c, m, xi] 1
-
--- | Linear congruential chaotic generator (linear interpolation).
-linCongL :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-linCongL r f a c m xi = mkOsc r "LinCongL" [f, a, c, m, xi] 1
-
--- | Linear congruential chaotic generator (no interpolation).
-linCongN :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-linCongN r f a c m xi = mkOsc r "LinCongN" [f, a, c, m, xi] 1
-
--- | The logistic map y = chaosParam * y * (1.0 - y)
-logistic :: Rate -> UGen -> UGen -> UGen -> UGen
-logistic r cp f i = mkOsc r "Logistic" [cp,f,i] 1
-
--- | Lorenz chaotic generator (linear interpolation).
-lorenzL :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-lorenzL rate freq s r b h xi yi zi = mkOsc rate "LorenzL" [freq, s, r, b, h, xi, yi, zi] 1
-
--- | General quadratic map chaotic generator (cubic interpolation).
-quadC :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-quadC r freq a b c xi = mkOsc r "QuadC" [freq, a, b, c, xi] 1
-
--- | General quadratic map chaotic generator (linear interpolation).
-quadL :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-quadL r freq a b c xi = mkOsc r "QuadL" [freq, a, b, c, xi] 1
-
--- | General quadratic map chaotic generator (no interpolation).
-quadN :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-quadN r freq a b c xi = mkOsc r "QuadN" [freq, a, b, c, xi] 1
-
--- Local Variables:
--- truncate-lines:t
--- End:
diff --git a/Sound/SC3/UGen/Composite.hs b/Sound/SC3/UGen/Composite.hs
deleted file mode 100644
--- a/Sound/SC3/UGen/Composite.hs
+++ /dev/null
@@ -1,147 +0,0 @@
--- | Common unit generator graphs.
-module Sound.SC3.UGen.Composite where
-
-import Control.Monad
-import Data.List
-import Data.List.Split
-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
-import Sound.SC3.UGen.Noise.ID
-import Sound.SC3.UGen.Oscillator
-import Sound.SC3.UGen.Panner
-import Sound.SC3.UGen.Rate
-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 =
-    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 =
-    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
-indexL b i =
-    let x = index b i
-        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
-
--- | Mix variant, sum to n channels.
-mixN :: Int -> UGen -> UGen
-mixN n u =
-    let xs = transpose (chunksOf n (mceChannels u))
-    in mce (map sum xs)
-
--- | Construct and sum a set of UGens.
-mixFill :: Integral n => Int -> (n -> UGen) -> UGen
-mixFill n f = mix (mce (map f [0 .. fromIntegral 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
-mouseButton' rt l r tm =
-    let o = lfClipNoise 'z' rt 1
-    in lag (linLin o (-1) 1 l r) tm
-
--- | Randomised mouse UGen (see also 'mouseX'' and 'mouseY'').
-mouseR :: ID a => a -> Rate -> UGen -> UGen -> Warp -> UGen -> UGen
-mouseR z rt l r ty tm =
-  let f = case ty of
-            Linear -> linLin
-            Exponential -> linExp
-            _ -> undefined
-  in lag (f (lfNoise1 z rt 1) (-1) 1 l r) tm
-
--- | Variant that randomly traverses the mouseX space.
-mouseX' :: Rate -> UGen -> UGen -> Warp -> UGen -> UGen
-mouseX' = mouseR 'x'
-
--- | Variant that randomly traverses the mouseY space.
-mouseY' :: Rate -> UGen -> UGen -> Warp -> UGen -> UGen
-mouseY' = mouseR 'y'
-
--- | PM oscillator.
-pmOsc :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen
-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
-        a = m + l
-    in (+ a) . (* m)
-
--- | Mix one output from many sources
-selectX :: UGen -> UGen -> UGen
-selectX ix xs =
-    let s0 = select (roundTo ix 2) xs
-        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 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
-splay i s l c lc =
-    let n = fromIntegral (mceDegree i)
-        m = n - 1
-        p = map ( (+ (-1.0)) . (* (2 / m)) ) [0 .. m]
-        a = if lc then sqrt (1 / n) else 1
-    in mix (pan2 i (mce p * s + c) 1) * l * a
diff --git a/Sound/SC3/UGen/Composite/ID.hs b/Sound/SC3/UGen/Composite/ID.hs
deleted file mode 100644
--- a/Sound/SC3/UGen/Composite/ID.hs
+++ /dev/null
@@ -1,38 +0,0 @@
--- | Explicit identifier functions for composite 'UGen's.
-module Sound.SC3.UGen.Composite.ID where
-
-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.
-dcons :: ID m => (m,m,m) -> UGen -> UGen -> UGen
-dcons (z0,z1,z2) x xs =
-    let i = dseq z0 1 (mce2 0 1)
-        a = dseq z1 1 (mce2 x xs)
-    in dswitch z2 i a
-
--- | Count 'mce' channels.
-mceN :: UGen -> UGen
-mceN = constant . length . mceChannels
-
--- | 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 '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
-tChoose z t a = select (tIRand z 0 (mceN a) t) a
-
--- | Randomly select one of several inputs on trigger (weighted).
-tWChoose :: ID m => m -> UGen -> UGen -> UGen -> UGen -> UGen
-tWChoose z t a w n =
-    let i = tWindex z t n w
-    in select i a
diff --git a/Sound/SC3/UGen/Composite/Monad.hs b/Sound/SC3/UGen/Composite/Monad.hs
deleted file mode 100644
--- a/Sound/SC3/UGen/Composite/Monad.hs
+++ /dev/null
@@ -1,37 +0,0 @@
--- | 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.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
-
--- | 'liftUId' of 'C.choose'.
-choose :: UId m => UGen -> m UGen
-choose = liftUId C.choose
-
--- | 'liftUId' of 'C.lchoose'.
-lchoose :: UId m => [UGen] -> m UGen
-lchoose = liftUId 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/Demand.hs b/Sound/SC3/UGen/Demand.hs
deleted file mode 100644
--- a/Sound/SC3/UGen/Demand.hs
+++ /dev/null
@@ -1,33 +0,0 @@
--- | Demand rate unit generators.
-module Sound.SC3.UGen.Demand where
-
-import Sound.SC3.UGen.Enum
-import Sound.SC3.UGen.Rate
-import Sound.SC3.UGen.Type
-import Sound.SC3.UGen.UGen
-
--- | Infinte repeat counter for demand rate unit generators.
-dinf :: UGen
-dinf = constant (9e8::Float)
-
--- | Demand results from demand rate ugens.
-demand :: UGen -> UGen -> UGen -> UGen
-demand t r d =
-    let d' = mceChannels d
-    in mkFilterKeyed "Demand" 0 (t : r : d') (length d')
-
--- | 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
-
--- | Demand results from demand rate ugens.
-duty :: Rate -> UGen -> UGen -> DoneAction -> UGen -> UGen
-duty rate d r act l = mkOsc rate "Duty" [d, r, from_done_action act, l] 1
-
--- | Demand results as trigger from demand rate ugens.
-tDuty :: Rate -> UGen -> UGen -> DoneAction -> UGen -> UGen -> UGen
-tDuty r d rst act l gap = mkOsc r "TDuty" [d, rst, from_done_action act, l, gap] 1
-
--- Local Variables:
--- truncate-lines:t
--- End:
diff --git a/Sound/SC3/UGen/Demand/ID.hs b/Sound/SC3/UGen/Demand/ID.hs
deleted file mode 100644
--- a/Sound/SC3/UGen/Demand/ID.hs
+++ /dev/null
@@ -1,79 +0,0 @@
--- | Explicit identifier demand rate 'UGen' functions.
-module Sound.SC3.UGen.Demand.ID where
-
-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
-
--- | Buffer demand ugen.
-dbufrd :: ID i => i -> UGen -> UGen -> Loop -> UGen
-dbufrd z b p l = mkOscId z DR "Dbufrd" [b, p, from_loop l] 1
-
--- | Buffer write on demand unit generator.
-dbufwr :: ID i => i -> UGen -> UGen -> UGen -> Loop -> UGen
-dbufwr z b p i l = mkOscId z DR "Dbufwr" [b, p, i, from_loop l] 1
-
--- | Demand rate white noise.
-dwhite :: ID i => i -> UGen -> UGen -> UGen -> UGen
-dwhite z l lo hi = mkOscId z DR "Dwhite" [l, lo, hi] 1
-
--- | Demand rate integer white noise.
-diwhite :: ID i => i -> UGen -> UGen -> UGen -> UGen
-diwhite z l lo hi = mkOscId z DR "Diwhite" [l, lo, hi] 1
-
--- | Demand rate brown noise.
-dbrown :: ID i => i -> UGen -> UGen -> UGen -> UGen -> UGen
-dbrown z l lo hi step = mkOscId z DR "Dbrown" [l, lo, hi, step] 1
-
--- | Demand rate integer brown noise.
-dibrown :: ID i => i -> UGen -> UGen -> UGen -> UGen -> UGen
-dibrown z l lo hi step = mkOscId z DR "Dibrown" [l, lo, hi, step] 1
-
--- | Demand rate random selection.
-drand :: ID i => i -> UGen -> UGen -> UGen
-drand z l array = mkOscMCEId z DR "Drand" [l] array 1
-
--- | Demand rate random selection with no immediate repetition.
-dxrand :: ID i => i -> UGen -> UGen -> UGen
-dxrand z l array = mkOscMCEId z DR "Dxrand" [l] array 1
-
--- | Demand rate weighted random sequence generator.
-dwrand :: ID i => i -> UGen -> UGen -> UGen -> UGen
-dwrand z l a w =
-    let n = mceDegree a
-        w' = mceExtend n w
-    in mkOscMCEId z DR "Dxrand" (l:w') a 1
-
--- | Demand rate arithmetic series.
-dseries :: ID i => i -> UGen -> UGen -> UGen -> UGen
-dseries z l i n = mkOscId z DR "Dseries" [l, i, n] 1
-
--- | Demand rate geometric series.
-dgeom :: ID i => i -> UGen -> UGen -> UGen -> UGen
-dgeom z l i n = mkOscId z DR "Dgeom" [l, i, n] 1
-
--- | Demand rate sequence generator.
-dseq :: ID i => i -> UGen -> UGen -> UGen
-dseq z l array = mkOscMCEId z DR "Dseq" [l] array 1
-
--- | 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
-dstutter z n i = mkOscId z DR "Dstutter" [n,i] 1
-
--- | Demand rate input switching.
-dswitch1 :: ID i => i -> UGen -> UGen -> UGen
-dswitch1 z l array = mkOscMCEId z DR "Dswitch1" [l] array 1
-
--- | Demand rate input switching.
-dswitch :: ID i => i -> UGen -> UGen -> UGen
-dswitch z l array = mkOscMCEId z DR "Dswitch" [l] array 1
diff --git a/Sound/SC3/UGen/Demand/Monad.hs b/Sound/SC3/UGen/Demand/Monad.hs
deleted file mode 100644
--- a/Sound/SC3/UGen/Demand/Monad.hs
+++ /dev/null
@@ -1,76 +0,0 @@
--- | 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.UId
-
--- | Buffer demand ugen.
-dbufrd :: (UId m) => UGen -> UGen -> Loop -> m UGen
-dbufrd = liftUId3 D.dbufrd
-
--- | Buffer write on demand unit generator.
-dbufwr :: (UId m) => UGen -> UGen -> UGen -> Loop -> m UGen
-dbufwr = liftUId4 D.dbufwr
-
--- | Demand rate white noise.
-dwhite :: (UId m) => UGen -> UGen -> UGen -> m UGen
-dwhite = liftUId3 D.dwhite
-
--- | Demand rate integer white noise.
-diwhite :: (UId m) => UGen -> UGen -> UGen -> m UGen
-diwhite = liftUId3 D.diwhite
-
--- | Demand rate brown noise.
-dbrown :: (UId m) => UGen -> UGen -> UGen -> UGen -> m UGen
-dbrown = liftUId4 D.dbrown
-
--- | Demand rate integer brown noise.
-dibrown :: (UId m) => UGen -> UGen -> UGen -> UGen -> m UGen
-dibrown = liftUId4 D.dibrown
-
--- | Demand rate random selection.
-drand :: (UId m) => UGen -> UGen -> m UGen
-drand = liftUId2 D.drand
-
--- | Demand rate random selection with no immediate repetition.
-dxrand :: (UId m) => UGen -> UGen -> m UGen
-dxrand = liftUId2 D.dxrand
-
--- | Demand rate weighted random sequence generator.
-dwrand :: (UId m) => UGen -> UGen -> UGen -> m UGen
-dwrand = liftUId3 D.dwrand
-
--- | Demand rate arithmetic series.
-dseries :: (UId m) => UGen -> UGen -> UGen -> m UGen
-dseries = liftUId3 D.dseries
-
--- | Demand rate geometric series.
-dgeom :: (UId m) => UGen -> UGen -> UGen -> m UGen
-dgeom = liftUId3 D.dgeom
-
--- | Demand rate sequence generator.
-dseq :: (UId m) => UGen -> UGen -> m UGen
-dseq = liftUId2 D.dseq
-
--- | Demand rate series generator.
-dser :: (UId m) => UGen -> UGen -> m UGen
-dser = liftUId2 D.dser
-
--- | Demand rate sequence shuffler.
-dshuf :: (UId m) => UGen -> UGen -> m UGen
-dshuf = liftUId2 D.dshuf
-
--- | Demand input replication
-dstutter :: (UId m) => UGen -> UGen -> m UGen
-dstutter = liftUId2 D.dstutter
-
--- | Demand rate input switching.
-dswitch1 :: (UId m) => UGen -> UGen -> m UGen
-dswitch1 = liftUId2 D.dswitch1
-
--- | Demand rate input switching.
-dswitch :: (UId m) => UGen -> UGen -> m UGen
-dswitch = liftUId2 D.dswitch
diff --git a/Sound/SC3/UGen/DiskIO.hs b/Sound/SC3/UGen/DiskIO.hs
deleted file mode 100644
--- a/Sound/SC3/UGen/DiskIO.hs
+++ /dev/null
@@ -1,40 +0,0 @@
--- | Disk file input and output UGens.
-module Sound.SC3.UGen.DiskIO where
-
-import Sound.SC3.UGen.Enum
-import Sound.SC3.UGen.Rate
-import Sound.SC3.UGen.Type
-import Sound.SC3.UGen.UGen
-
--- | Stream soundfile from disk.
---
---  [@nc@] Number of channels in buffer/soundfile.
---
---  [@bufnum@] Buffer used for streaming (the file descriptor has to be left
---             open, see the @/b_read@ server command).
---
---  [@loop@] Whether to loop playback (0, 1)
-diskIn :: Int -> UGen -> Loop -> UGen
-diskIn nc bufnum loop = mkOsc AR "DiskIn" [bufnum, from_loop loop] nc
-
--- | Stream soundfile from disk with variable playback rate.
---
---  [@nc@] Number of channels in buffer/soundfile.
---
---  [@bufnum@] Buffer used for streaming (the file descriptor has to be left
---             open, see the @/b_read@ server command).
---
---  [@rate@] Playback rate
---
---  [@loop@] Whether to loop playback (0,1)
-vDiskIn :: Int -> UGen -> UGen -> Loop -> UGen
-vDiskIn nc bufnum rate loop = mkOsc AR "VDiskIn" [bufnum, rate, from_loop loop] nc
-
--- | Stream soundfile to disk.
---
---  [@bufnum@] Buffer used for streaming (the file descriptor has to be left
---             open, see the @/b_write@ server command).
---
---  [@output@] Current number of written frames.
-diskOut :: UGen -> UGen -> UGen
-diskOut bufnum inputs = mkOscMCE AR "DiskOut" [bufnum] inputs 1
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.Envelope.Interpolate
+import Sound.SC3.Common
+import qualified Sound.SC3.UGen.Envelope.Interpolate as I
 import Sound.SC3.UGen.Type
 
 -- | Loop indicator input.
@@ -38,6 +39,7 @@
 data DoneAction = DoNothing
                 | PauseSynth
                 | RemoveSynth
+                | RemoveGroup
                 | DoneAction UGen
                   deriving (Eq, Show)
 
@@ -48,6 +50,7 @@
       DoNothing -> 0
       PauseSynth -> 1
       RemoveSynth -> 2
+      RemoveGroup -> 14
       DoneAction u -> u
 
 -- | Warp interpolation indicator input.
@@ -69,12 +72,23 @@
                       | EnvLin
                       | EnvExp
                       | EnvSin
-                      | EnvCos -- ^ Note: not implemented at SC3
+                      | EnvWelch -- ^ Note: not implemented at SC3
                       | EnvNum a
                       | EnvSqr
                       | EnvCub
+                      | EnvHold
                         deriving (Eq, Show)
 
+-- | Envelope curve pair.
+type Envelope_Curve2 a = T2 (Envelope_Curve a)
+
+-- | Envelope curve triple.
+type Envelope_Curve3 a = T3 (Envelope_Curve a)
+
+-- | Envelope curve triple.
+type Envelope_Curve4 a = T4 (Envelope_Curve a)
+
+-- | Type specialised ('UGen') envelope curve.
 type EnvCurve = Envelope_Curve UGen
 
 -- | Convert 'Envelope_Curve' to shape value.
@@ -87,55 +101,44 @@
       EnvLin -> 1
       EnvExp -> 2
       EnvSin -> 3
-      EnvCos -> 4
+      EnvWelch -> 4
       EnvNum _ -> 5
       EnvSqr -> 6
       EnvCub -> 7
+      EnvHold -> 8
 
 -- | The /value/ of 'EnvCurve' is non-zero for 'EnvNum'.
 --
--- > map env_curve_value [EnvCos,EnvNum 2] == [0,2]
+-- > map env_curve_value [EnvWelch,EnvNum 2] == [0,2]
 env_curve_value :: Num a => Envelope_Curve a -> a
 env_curve_value e =
     case e of
       EnvNum u -> u
       _ -> 0
 
+-- | 'Interpolation_F' of 'Envelope_Curve'.
 env_curve_interpolation_f :: (Ord t, Floating t) =>
-                             Envelope_Curve t -> Interpolation_F t
+                             Envelope_Curve t -> I.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
+      EnvStep -> I.step
+      EnvLin -> I.linear
+      EnvExp -> I.exponential
+      EnvSin -> I.sine
+      EnvWelch -> I.welch
+      EnvNum n -> I.curve n
+      EnvSqr -> I.squared
+      EnvCub -> I.cubed
+      EnvHold -> undefined
 
+-- | Unification of integer and 'UGen' buffer identifiers.
 data Buffer = Buffer_Id Int
             | Buffer UGen
               deriving (Eq, Show)
 
+-- | Lift to 'UGen'.
 from_buffer :: Buffer -> UGen
 from_buffer b =
     case b of
       Buffer_Id i -> constant i
       Buffer u -> u
-
--- | 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
@@ -4,9 +4,7 @@
 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
 
 -- * Envelope
 
@@ -20,6 +18,10 @@
              }
     deriving (Eq,Show)
 
+-- | Variant without release and loop node inputs (defaulting to nil).
+envelope :: [a] -> [a] -> [Envelope_Curve a] -> Envelope a
+envelope l t c = Envelope l t c Nothing Nothing
+
 -- | Duration of 'Envelope', ie. 'sum' '.' 'env_times'.
 envelope_duration :: Num n => Envelope n -> n
 envelope_duration = sum . env_times
@@ -49,8 +51,42 @@
         c = envelope_curves e !! i
     in (t0,x0,t1,x1,c)
 
+-- | Extract all segments.
+envelope_segments :: Num t => Envelope t -> [Envelope_Segment t]
+envelope_segments e =
+    let n = envelope_n_segments e
+    in map (envelope_segment e) [0 .. n - 1]
+
+-- | Transform list of 'Envelope_Segment's into lists ('env_levels','env_times','env_curves').
+pack_envelope_segments :: Num t => [Envelope_Segment t] -> ([t],[t],[Envelope_Curve t])
+pack_envelope_segments s =
+    case s of
+      [] -> error ""
+      [(t0,l0,t1,l1,c)] -> ([l0,l1],[t1 - t0],[c])
+      (_,l0,_,_,_) : _ ->
+          let t (t0,_,t1,_,_) = t1 - t0
+              c (_,_,_,_,x) = x
+              l (_,_,_,x,_) = x
+          in (l0 : map l s,map t s,map c s)
+
+-- | An envelope is /normal/ if it has no segments with zero duration.
+envelope_is_normal :: (Eq n,Num n) => Envelope n -> Bool
+envelope_is_normal = null . filter (== 0) . env_times
+
+-- | Normalise envelope by deleting segments of zero duration.
+envelope_normalise :: (Num a, Ord a) => Envelope a -> Envelope a
+envelope_normalise e =
+    let s = envelope_segments e
+        f (t0,_,t1,_,_) = t1 <= t0
+        s' = filter (not . f) s
+        (l,t,c) = pack_envelope_segments s'
+    in case e of
+         Envelope _ _ _ Nothing Nothing -> Envelope l t c Nothing Nothing
+         _ -> error "envelope_normalise: has release or loop node..."
+
 -- | Get value for 'Envelope' at time /t/, or zero if /t/ is out of
--- range.
+-- range.  By convention if the envelope has a segment of zero
+-- duration we give the rightmost value.
 envelope_at :: (Ord t, Floating t) => Envelope t -> t -> t
 envelope_at e t =
     case envelope_segment_ix e t of
@@ -58,10 +94,12 @@
                     d = t1 - t0
                     t' = (t - t0) / d
                     f = env_curve_interpolation_f c
-                in f x0 x1 t'
+                in if d <= 0
+                   then x1
+                   else f x0 x1 t'
       Nothing -> 0
 
--- | Render 'Envelope' to breakpoint set of /n/ places.
+-- | Render 'Envelope' to breakpoint set of /n/ equi-distant places.
 envelope_render :: (Ord t, Floating t, Enum t) => t -> Envelope t -> [(t,t)]
 envelope_render n e =
     let d = envelope_duration e
@@ -84,6 +122,15 @@
        else take n (cycle c)
 
 -- | Linear SC3 form of 'Envelope' data.
+--
+-- Form is: l0 #t reset loop l1 t0 c0 c0' ...
+--
+-- > let {l = [0,0.6,0.3,1.0,0]
+-- >     ;t = [0.1,0.02,0.4,1.1]
+-- >     ;c = [EnvLin,EnvExp,EnvNum (-6),EnvSin]
+-- >     ;e = Envelope l t c Nothing Nothing
+-- >     ;r = [0,4,-99,-99,0.6,0.1,1,0,0.3,0.02,2,0,1,0.4,5,-6,0,1.1,3,0]}
+-- > in envelope_sc3_array e == Just r
 envelope_sc3_array :: Num a => Envelope a -> Maybe [a]
 envelope_sc3_array e =
     let Envelope l t _ rn ln = e
@@ -97,6 +144,26 @@
          l0:l' -> Just (l0 : n' : rn' : ln' : concat (zipWith3 f l' t c))
          _ -> Nothing
 
+-- | @IEnvGen@ SC3 form of 'Envelope' data.  Offset not supported (zero).
+--
+-- > let {l = [0,0.6,0.3,1.0,0]
+-- >     ;t = [0.1,0.02,0.4,1.1]
+-- >     ;c = [EnvLin,EnvExp,EnvNum (-6),EnvSin]
+-- >     ;e = Envelope l t c Nothing Nothing
+-- >     ;r = [0,0,4,1.62,0.1,1,0,0.6,0.02,2,0,0.3,0.4,5,-6,1,1.1,3,0,0]}
+-- > in envelope_sc3_ienvgen_array e == Just r
+envelope_sc3_ienvgen_array :: Num a => Envelope a -> Maybe [a]
+envelope_sc3_ienvgen_array e =
+    let Envelope l t _ _ _ = e
+        n = length t
+        n' = fromIntegral n
+        c = envelope_curves e
+        f i j k = [j,env_curve_shape k,env_curve_value k,i]
+    in case l of
+         l0:l' -> Just (0 : l0 : n' : sum t : concat (zipWith3 f l' t c))
+         _ -> Nothing
+
+-- | 'True' if 'env_release_node' is not 'Nothing'.
 env_is_sustained :: Envelope a -> Bool
 env_is_sustained = isJust . env_release_node
 
@@ -130,57 +197,10 @@
 
 -- * UGen
 
--- | Segment based envelope generator.
-envGen :: Rate -> UGen -> UGen -> UGen -> UGen -> DoneAction -> Envelope UGen -> UGen
-envGen r gate lvl bias scale act e =
+envelope_to_ugen :: Envelope UGen -> UGen
+envelope_to_ugen =
     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.
-line :: Rate -> UGen -> UGen -> UGen -> DoneAction -> UGen
-line r start end dur act = mkOsc r "Line" [start, end, dur, from_done_action act] 1
-
--- | Exponential line generator.
-xLine :: Rate -> UGen -> UGen -> UGen -> DoneAction -> UGen
-xLine r start end dur act = mkOsc r "XLine" [start, end, dur, from_done_action act] 1
-
--- | Free node on trigger.
-freeSelf :: UGen -> UGen
-freeSelf i = mkFilter "FreeSelf" [i] 1
-
--- | Free node on done action at source.
-freeSelfWhenDone :: UGen -> UGen
-freeSelfWhenDone i = mkFilter "FreeSelfWhenDone" [i] 1
-
--- | Pause specified node on trigger.
-pause :: UGen -> UGen -> UGen
-pause t n = mkFilter "Pause" [t, n] 1
-
--- | Pause node on trigger.
-pauseSelf :: UGen -> UGen
-pauseSelf i = mkFilter "PauseSelf" [i] 1
-
--- | Pause node on done action at source.
-pauseSelfWhenDone :: UGen -> UGen
-pauseSelfWhenDone i = mkFilter "PauseSelfWhenDone" [i] 1
-
--- | One while the source is marked done, else zero.
-done :: UGen -> UGen
-done i = mkFilter "Done" [i] 1
-
--- | Raise specified done action when input goes silent.
-detectSilence ::  UGen -> UGen -> UGen -> DoneAction -> UGen
-detectSilence i a t act = mkFilter "DetectSilence" [i, a, t, from_done_action act] 1
-
--- | When triggered free specified node.
-free :: UGen -> UGen -> UGen
-free i n = mkFilter "Free" [i, n] 1
-
--- | 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
+    in mce . fromMaybe err . envelope_sc3_array
 
 -- * List
 
@@ -192,3 +212,12 @@
 -- > dx_d [0.5,0.5] == [0.5,1]
 dx_d :: Num n => [n] -> [n]
 dx_d = scanl1 (+)
+
+-- > d_dx' [0,1,3,6] == [1,2,3]
+d_dx' :: Num n => [n] -> [n]
+d_dx' l = zipWith (-) (tail l) l
+
+-- > dx_d' (d_dx' [0,1,3,6]) == [0,1,3,6]
+-- > dx_d' [0.5,0.5] == [0,0.5,1]
+dx_d' :: Num n => [n] -> [n]
+dx_d' = (0 :) . 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
@@ -2,9 +2,13 @@
 --   types.
 module Sound.SC3.UGen.Envelope.Construct where
 
+import Sound.SC3.UGen.Bindings
 import Sound.SC3.UGen.Math
 import Sound.SC3.UGen.Enum
 import Sound.SC3.UGen.Envelope
+import Sound.SC3.UGen.Rate
+import Sound.SC3.UGen.Type
+import Sound.SC3.UGen.UGen
 
 -- | Co-ordinate based static envelope generator.
 --
@@ -25,24 +29,24 @@
 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)
-             , (x1, 1)
-             , (shape + x1, 1)
-             , (1, skew >=* 1) ]
+        bp = [(0,skew <=* 0)
+             ,(x1,1)
+             ,(shape + x1,1)
+             ,(1,skew >=* 1)]
     in envCoord bp dur amp EnvLin
 
 -- | 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 Envelope [0, lvl, 0] [atk, rls] c Nothing Nothing
+envPerc' :: Num a => a -> a -> a -> Envelope_Curve2 a -> Envelope a
+envPerc' atk rls lvl (c0,c1) =
+    let c = [c0,c1]
+    in Envelope [0,lvl,0] [atk,rls] c Nothing Nothing
 
 -- | Percussive envelope, with attack, release, level and curve
 --   inputs.
 envPerc :: Num a => a -> a -> Envelope a
 envPerc atk rls =
     let cn = EnvNum (-4)
-    in envPerc' atk rls 1 (cn, cn)
+    in envPerc' atk rls 1 (cn,cn)
 
 -- | Triangular envelope, with duration and level inputs.
 --
@@ -64,26 +68,50 @@
         d = replicate 2 (dur / 2)
     in Envelope [0,lvl,0] d c Nothing Nothing
 
+-- | Parameters for LINEN envelopes.
+data LINEN a = LINEN {linen_attackTime :: a
+                     ,linen_sustainTime :: a
+                     ,linen_releaseTime :: a
+                     ,linen_level :: a
+                     ,linen_curve :: Envelope_Curve3 a}
+
+-- | Record ('LINEN') variant of 'envLinen'.
+envLinen_r :: Num a => LINEN a -> Envelope a
+envLinen_r (LINEN aT sT rT lv (c0,c1,c2)) =
+    let l = [0,lv,lv,0]
+        t = [aT,sT,rT]
+        c = [c0,c1,c2]
+    in Envelope l t c Nothing Nothing
+
 -- | 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) =
-    Envelope [0, l, l, 0] [aT, sT, rT] [c0, c1, c2] Nothing Nothing
+envLinen' :: Num a => a -> a -> a -> a -> Envelope_Curve3 a -> Envelope a
+envLinen' aT sT rT lv c = envLinen_r (LINEN aT sT rT lv c)
 
 -- | Linear envelope parameter constructor.
+--
+-- > let {e = envLinen 0 1 0 1
+-- >     ;s = envelope_segments e
+-- >     ;p = pack_envelope_segments s}
+-- > in p == (env_levels e,env_times e,env_curves e)
 envLinen :: Num a => a -> a -> a -> a -> Envelope a
 envLinen aT sT rT l =
-    let c = (EnvLin, EnvLin, EnvLin)
+    let c = (EnvLin,EnvLin,EnvLin)
     in envLinen' aT sT rT l c
 
--- | 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}
+-- | Parameters for ADSR envelopes.  The sustain level is given as a proportion of the peak level.
+data ADSR a = ADSR {adsr_attackTime :: a
+                   ,adsr_decayTime :: a
+                   ,adsr_sustainLevel :: a
+                   ,adsr_releaseTime :: a
+                   ,adsr_peakLevel :: a
+                   ,adsr_curve :: Envelope_Curve3 a
+                   ,adsr_bias :: a}
 
+adsrDefault :: Fractional n => ADSR n
+adsrDefault =
+    let c = EnvNum (-4)
+    in ADSR 0.01 0.3 0.5 1 1 (c,c,c) 0
+
 -- | Attack, decay, sustain, release envelope parameter constructor.
 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)
@@ -96,10 +124,74 @@
         c = [c0,c1,c2]
     in Envelope l t c (Just 2) Nothing
 
+-- | Parameters for Roland type ADSSR envelopes.
+data ADSSR a = ADSSR {adssr_attackTime :: a
+                     ,adssr_attackLevel :: a
+                     ,adssr_decayTime :: a
+                     ,adssr_decayLevel :: a
+                     ,adssr_slopeTime :: a
+                     ,adssr_sustainLevel :: a
+                     ,adssr_releaseTime :: a
+                     ,adssr_curve :: Envelope_Curve4 a
+                     ,adssr_bias :: a}
+
+-- | Attack, decay, slope, sustain, release envelope parameter constructor.
+envADSSR :: Num a => a -> a -> a -> a -> a -> a -> a -> Envelope_Curve a -> a -> Envelope a
+envADSSR t1 l1 t2 l2 t3 l3 t4 c b = envADSSR_r (ADSSR t1 l1 t2 l2 t3 l3 t4 (c,c,c,c) b)
+
+-- | Record ('ADSSR') variant of 'envADSSR'.
+envADSSR_r :: Num a => ADSSR a -> Envelope a
+envADSSR_r (ADSSR t1 l1 t2 l2 t3 l3 t4 (c1,c2,c3,c4) b) =
+    let l = map (+ b) [0,l1,l2,l3,0]
+        t = [t1,t2,t3,t4]
+        c = [c1,c2,c3,c4]
+    in Envelope l t c (Just 3) Nothing
+
+-- | Parameters for ASR envelopes.
+data ASR a = ASR {asr_attackTime :: a
+                 ,asr_sustainLevel :: a
+                 ,asr_releaseTime :: a
+                 ,asr_curve :: Envelope_Curve2 a}
+
 -- | Attack, sustain, release envelope parameter constructor.
+--
+-- > let {c = 3
+-- >     ;r = Just [0,2,1,-99,0.1,3,c,0,0,2,c,0]}
+-- > in envelope_sc3_array (envASR 3 0.1 2 EnvSin) == r
 envASR :: Num a => a -> a -> a -> Envelope_Curve a -> Envelope a
-envASR aT sL rT c =
+envASR aT sL rT c = envASR_r (ASR aT sL rT (c,c))
+
+-- | Record ('ASR') variant of 'envASR'.
+envASR_r :: Num a => ASR a -> Envelope a
+envASR_r (ASR aT sL rT (c0,c1)) =
     let l = [0,sL,0]
         t = [aT,rT]
-        c' = [c,c]
+        c' = [c0,c1]
     in Envelope l t c' (Just 1) Nothing
+
+-- | All segments are horizontal lines.
+envStep :: [a] -> [a] -> Maybe Int -> Maybe Int -> Envelope a
+envStep levels times releaseNode loopNode =
+    if length levels /= length times
+    then error ("envStep: levels and times must have same size")
+    else let levels' = head levels : levels
+         in Envelope levels' times [EnvStep] releaseNode loopNode
+
+-- | Singleton fade envelope.
+envGate :: UGen -> UGen -> UGen -> DoneAction -> Envelope_Curve UGen -> UGen
+envGate level gate_ fadeTime doneAction curve =
+    let startVal = fadeTime <=* 0
+        e = Envelope [startVal,1,0] [1,1] [curve] (Just 1) Nothing
+    in envGen KR gate_ level 0 fadeTime doneAction e
+
+-- | Variant with default values for all inputs.  @gate@ and
+-- @fadeTime@ are 'control's, @doneAction@ is 'RemoveSynth', @curve@
+-- is 'EnvSin'.
+envGate' :: UGen
+envGate' =
+    let level = 1
+        gate_ = meta_control KR "gate" 1 (0,1,"lin",1,"")
+        fadeTime = meta_control KR "fadeTime" 0.02 (0,10,"lin",0,"s")
+        doneAction = RemoveSynth
+        curve = EnvSin
+    in envGate level gate_ fadeTime doneAction curve
diff --git a/Sound/SC3/UGen/Envelope/Interpolate.hs b/Sound/SC3/UGen/Envelope/Interpolate.hs
--- a/Sound/SC3/UGen/Envelope/Interpolate.hs
+++ b/Sound/SC3/UGen/Envelope/Interpolate.hs
@@ -1,31 +1,71 @@
--- | 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.
+-- | Interpolation functions for envelope segments.
 module Sound.SC3.UGen.Envelope.Interpolate where
 
+-- | An interpolation function take three arguments. /x0/ is the left
+-- or begin value, /x1/ is the right or end value, and /t/ is a (0,1)
+-- index.
 type Interpolation_F t = t -> t -> t -> t
 
+-- | Step function, ignores /t/ and returns /x1/.
 step :: Interpolation_F t
 step _ x1 _ = x1
 
+-- | Linear interpolation.
 linear :: Num t => Interpolation_F t
 linear x0 x1 t = t * (x1 - x0) + x0
 
+-- | Exponential interpolation, /x0/ must not be @0@, (/x0/,/x1/) must
+-- not span @0@.
+--
+-- > import Sound.SC3.Plot
+-- > plotTable1 (map (exponential 0.001 1) [0,0.01 .. 1])
 exponential :: Floating t => Interpolation_F t
 exponential x0 x1 t = x0 * ((x1 / x0) ** t)
 
+-- | Variant that allows /x0/ to be @0@, though (/x0/,/x1/) must not
+-- span @0@.
+--
+-- > plotTable1 (map (exponential' 0 1) [0,0.01 .. 1])
+-- > plotTable1 (map (exponential' 0 (-1)) [0,0.01 .. 1])
+exponential' :: (Eq t,Floating t) => Interpolation_F t
+exponential' x0 x1 =
+    let epsilon = 1e-6
+        x0' = if x0 == 0 then epsilon * signum x1 else x0
+    in exponential x0' x1
+
+-- | 'linear' of 'exponential'', ie. allows (/x0/,/x1/) to span @0@.
+--
+-- > plotTable1 (map (exponential'' (-1) 1) [0,0.01 .. 1])
+exponential'' :: (Eq t,Floating t) => Interpolation_F t
+exponential'' x0 x1 t = linear x0 x1 (exponential' 0 1 t)
+
+-- | 'linear' with /t/ transformed by sine function over (-pi/2,pi/2).
+--
+-- > plotTable1 (map (sine (-1) 1) [0,0.01 .. 1])
 sine :: Floating t => Interpolation_F t
-sine x0 x1 t = x0 + (x1 - x0) * (- cos (pi * t) * 0.5 + 0.5)
+sine x0 x1 t =
+    let t' = - cos (pi * t) * 0.5 + 0.5
+    in linear x0 x1 t'
 
 half_pi :: Floating a => a
 half_pi = pi / 2
 
+-- | If /x0/ '<' /x1/ rising sine segment (0,pi/2), else falling
+-- segment (pi/2,pi).
+--
+-- > plotTable1 (map (welch (-1) 1) [0,0.01 .. 1])
+-- > plotTable1 (map (welch 1 (-1)) [0,0.01 .. 1])
 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))
 
+-- | Curvature controlled by single parameter /c/.  @0@ is 'linear',
+-- increasing /c/ approaches 'exponential'.
+--
+-- > plotTable1 (map (curve 0 (-1) 1) [0,0.01 .. 1])
+-- > plotTable1 (map (curve 9 (-1) 1) [0,0.01 .. 1])
 curve :: (Ord t, Floating t) => t -> Interpolation_F t
 curve c x0 x1 t =
     if abs c < 0.0001
@@ -34,17 +74,27 @@
              n = 1 - exp (t * c)
          in x0 + (x1 - x0) * (n/d)
 
+-- | Square of 'linear' of 'sqrt' of /x0/ and /x1/, threfore neither
+-- may be negative.
+--
+-- > plotTable1 (map (squared 0 1) [0,0.01 .. 1])
 squared :: Floating t => Interpolation_F t
 squared x0 x1 t =
     let x0' = sqrt x0
         x1' = sqrt x1
-        l = t * (x1' - x0') + x0'
+        l = linear x0' x1' t
     in l * l
 
+-- | Cubic variant of 'squared'.
+--
+-- > plotTable1 (map (cubed 0 1) [0,0.01 .. 1])
 cubed :: Floating t => Interpolation_F t
 cubed x0 x1 t =
     let x0' = x0 ** (1/3)
         x1' = x1 ** (1/3)
-        l = t * (x1' - x0') + x0'
+        l = linear x0' x1' t
     in l * l * l
 
+-- | x0 until end, then immediately x1.
+hold :: (Num t,Eq t) => Interpolation_F t
+hold x0 x1 t = if t == 1 then x1 else x0
diff --git a/Sound/SC3/UGen/External.hs b/Sound/SC3/UGen/External.hs
deleted file mode 100644
--- a/Sound/SC3/UGen/External.hs
+++ /dev/null
@@ -1,49 +0,0 @@
--- | Bindings to unit generators not distributed with SuperCollider
---   proper.
-module Sound.SC3.UGen.External where
-
-import Sound.SC3.UGen.Rate
-import Sound.SC3.UGen.Type
-import Sound.SC3.UGen.UGen
-
--- * f0plugins
-
--- | 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
-
--- * PitchDetection (sc3-plugins)
-
--- | 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
-
--- | 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
-
--- * RFW UGens (sc3-plugins)
-
--- | Calculates mean average of audio or control rate signal.
-averageOutput :: UGen -> UGen -> UGen
-averageOutput in_ trig_ = mkFilter "AverageOutput" [in_,trig_] 1
-
--- * skUG
-
--- | Phase modulation oscillator matrix.
-fm7 :: [[UGen]] -> [[UGen]] -> UGen
-fm7 ctl m0d = mkOsc AR "FM7" (concat ctl ++ concat m0d) 6
-
--- * TJ UGens (sc3-plugins)
-
--- | 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
-
--- Local Variables:
--- truncate-lines:t
--- End:
diff --git a/Sound/SC3/UGen/External/ATS.hs b/Sound/SC3/UGen/External/ATS.hs
deleted file mode 100644
--- a/Sound/SC3/UGen/External/ATS.hs
+++ /dev/null
@@ -1,107 +0,0 @@
--- | Reader for ATS analyis data files.
-module Sound.SC3.UGen.External.ATS (ATS(..)
-                                   ,ATSHeader(..)
-                                   ,ATSFrame,atsFrames
-                                   ,atsRead) where
-
-import qualified Data.ByteString.Lazy as B {- bytestring -}
-import Data.Int {- base -}
-import Data.List.Split {- split -}
-import Sound.OSC.Coding.Byte {- hosc -}
-
--- | ATS analysis data.
-data ATS = ATS { atsHeader :: ATSHeader
-               , atsData :: [Double] }
-           deriving (Eq, Show)
-
--- | ATS analysis meta-data.
-data ATSHeader = ATSHeader { atsSampleRate :: Double
-                           , atsFrameSize :: Int
-                           , atsWindowSize :: Int
-                           , atsNPartials :: Int
-                           , atsNFrames :: Int
-                           , atsMaxAmplitude :: Double
-                           , atsMaxFrequency :: Double
-                           , atsAnalysisDuration :: Double
-                           , atsFileType :: Int
-                           , atsFrameLength :: Int
-                           } deriving (Eq, Show)
-
--- | ATS analysis frame data.
-type ATSFrame = [Double]
-
-bSep :: Int64 -> Int64 -> B.ByteString -> [B.ByteString]
-bSep n i d =
-    if i == 1
-    then [d]
-    else let (p,q) = B.splitAt n d
-         in p : bSep n (i - 1) q
-
-atsParse :: FilePath -> IO [Double]
-atsParse fn = do
-  d <- B.readFile fn
-  let n = B.length d `div` 8
-      v = B.take 8 d
-      f = get_decoder v
-  return (map f (bSep 8 n d))
-
--- | Read an ATS data file.
-atsRead :: FilePath -> IO ATS
-atsRead fn = do
-  d <- atsParse fn
-  let f j = d !! j
-      g = floor . f
-      ft = g 9
-      (n, x) = ftype_n ft
-      np = g 4
-      nf = g 5
-      fl = np * n + x
-      hdr = ATSHeader (f 1) (g 2) (g 3) np nf (f 6) (f 7) (f 8) ft fl
-  return (ATS hdr d)
-
--- | Extract set of 'ATSFrame's from 'ATS'.
-atsFrames :: ATS -> [ATSFrame]
-atsFrames a = chunksOf (atsFrameLength (atsHeader a)) (atsData a)
-
--- Determine endianess and hence decoder.
-get_decoder :: B.ByteString -> B.ByteString -> Double
-get_decoder v =
-    if decode_f64 v == 123.0
-    then decode_f64
-    else decode_f64 . B.reverse
-
--- Calculate partial depth and frame constant.
-ftype_n :: Int -> (Int, Int)
-ftype_n n =
-    case n of
-      1 -> (2, 1)
-      2 -> (3, 1)
-      3 -> (2, 26)
-      4 -> (3, 26)
-      _ -> error "ftype_n"
-
-{-
--- | Analysis data in format required by the sc3 ATS UGens.
-atsSC3 :: ATS -> [Double]
-atsSC3 (ATS h d) =
-    let f = fromIntegral
-        td = transpose d
-    in f (atsFileType h) :
-       f (atsNPartials h) :
-       f (atsNFrames h) :
-       f (atsWindowSize h) :
-       concatMap (td !!) (atsSC3Indices h)
-
--- Indices for track data in the order required by sc3.
-atsSC3Indices :: ATSHeader -> [Int]
-atsSC3Indices h =
-    let np = atsNPartials h
-        o = 3 * (np - 1)
-        a = [1,4 .. (1 + o)]
-        f = map (+ 1) a
-        p = map (+ 1) f
-        n = map (+ (4+o)) [0..24]
-    in if atsFileType h == 4
-       then a ++ f ++ p ++ n
-       else error "atsSC3Indices: illegal ATS file type (/= 4)"
--}
diff --git a/Sound/SC3/UGen/External/ID.hs b/Sound/SC3/UGen/External/ID.hs
deleted file mode 100644
--- a/Sound/SC3/UGen/External/ID.hs
+++ /dev/null
@@ -1,19 +0,0 @@
--- | 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/LPC.hs b/Sound/SC3/UGen/External/LPC.hs
deleted file mode 100644
--- a/Sound/SC3/UGen/External/LPC.hs
+++ /dev/null
@@ -1,62 +0,0 @@
--- | Reader for LPC analysis data files.
-module Sound.SC3.UGen.External.LPC ( LPC(..)
-                                   , LPCHeader(..)
-                                   , LPCFrame
-                                   , lpcRead
-                                   , lpcSC3 ) where
-
-import Control.Monad
-import qualified Data.ByteString.Lazy as B
-import Data.List
-import Sound.OSC.Coding.Byte
-import System.IO
-
--- | LPC analysis data.
-data LPC = LPC { lpcHeader :: LPCHeader
-               , lpcFrames :: [LPCFrame] }
-           deriving (Eq, Show)
-
--- | LPC analysis meta-data.
-data LPCHeader = LPCHeader { lpcHeaderSize :: Int
-                           , lpcMagic :: Int
-                           , lpcNPoles :: Int
-                           , lpcFrameSize :: Int
-                           , lpcFrameRate :: Float
-                           , lpcSampleRate :: Float
-                           , lpcAnalysisDuration :: Float
-                           , lpcNFrames :: Int
-                           } deriving (Eq, Show)
-
--- | LPC analysis frame data.
-type LPCFrame = [Float]
-
--- | Read an lpanal format LPC data file.
-lpcRead :: FilePath -> IO LPC
-lpcRead fn = do
-  h <- openFile fn ReadMode
-  l <- hFileSize h
-  [hs, lm, np, fs] <- replicateM 4 (read_i32 h)
-  [fr, sr, fd] <- replicateM 3 (read_f32 h)
-  let nf = ((fromIntegral l - hs) `div` 4) `div` fs
-      hdr = LPCHeader hs lm np fs fr sr fd nf
-      hc = hs - (7 * 4)
-      get_f = replicateM fs (read_f32 h)
-  _ <- B.hGet h hc
-  d <- replicateM nf get_f
-  hClose h
-  return (LPC hdr d)
-
--- | Analysis data in format required by the sc3 LPC UGens.
-lpcSC3 :: LPC -> [Float]
-lpcSC3 (LPC h d) = let f = fromIntegral
-                       np = f (lpcNPoles h)
-                       nf = f (lpcNFrames h)
-                       fs = f (lpcFrameSize h)
-                   in np : nf : fs : concat (transpose d)
-
-read_i32 :: Handle -> IO Int
-read_i32 h = liftM decode_i32 (B.hGet h 4)
-
-read_f32 :: Handle -> IO Float
-read_f32 h = liftM decode_f32 (B.hGet h 4)
-
diff --git a/Sound/SC3/UGen/External/SC3_Plugins.hs b/Sound/SC3/UGen/External/SC3_Plugins.hs
deleted file mode 100644
--- a/Sound/SC3/UGen/External/SC3_Plugins.hs
+++ /dev/null
@@ -1,138 +0,0 @@
--- | 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)
-
--- * Bhob
-
--- | String resonance filter
-streson :: UGen -> UGen -> UGen -> UGen
-streson input delayTime res = mkFilter "Streson" [input,delayTime,res] 1
-
--- * 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
-
--- * MCLD
-
--- | 3D Perlin Noise
-perlin3 :: Rate -> UGen -> UGen -> UGen -> UGen
-perlin3 rate x y z = mkOscR [AR,KR] rate "Perlin3" [x,y,z] 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
-
--- * SLU
-
--- | Prigogine oscillator
-brusselator :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-brusselator rate reset rate_ mu gamma initx inity = mkOscR [AR] rate "Brusselator" [reset,rate_,mu,gamma,initx,inity] 2
-
--- | Forced DoubleWell Oscillator
-doubleWell3 :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-doubleWell3 rate reset rate_ f delta initx inity = mkOscR [AR] rate "DoubleWell3" [reset,rate_,f,delta,initx,inity] 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
deleted file mode 100644
--- a/Sound/SC3/UGen/FFT.hs
+++ /dev/null
@@ -1,202 +0,0 @@
--- | Frequency domain unit generators.
-module Sound.SC3.UGen.FFT where
-
---import Sound.SC3.Server.Command
-import Sound.SC3.UGen.Rate
-import Sound.SC3.UGen.Type
-import Sound.SC3.UGen.UGen
-
--- | Fast fourier transform.
-fft :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-fft buf i h wt a ws = mkOsc KR "FFT" [buf,i,h,wt,a,ws] 1
-
--- | Variant FFT constructor with default values for hop size, window
--- | type, active status and window size.
-fft' :: UGen -> UGen -> UGen
-fft' buf i = fft buf i 0.5 0 1 0
-
--- | Outputs signal for @FFT@ chains, without performing FFT.
-fftTrigger :: UGen -> UGen -> UGen -> UGen
-fftTrigger b h p = mkOsc KR "FFTTrigger" [b,h,p] 1
-
--- | Inverse Fast Fourier Transform.
-ifft :: UGen -> UGen -> UGen -> UGen
-ifft buf wt ws = mkOsc AR "IFFT" [buf,wt,ws] 1
-
--- | Variant ifft with default value for window type.
-ifft' :: UGen -> UGen
-ifft' buf = ifft buf 0 0
-
--- | Strict convolution of two continuously changing inputs.
-convolution :: UGen -> UGen -> UGen -> UGen
-convolution i kernel frameSize = mkOsc AR "Convolution" [i, kernel, frameSize] 1
-
--- | Real-time fixed kernel convolver.
-convolution2 :: UGen -> UGen -> UGen -> UGen -> UGen
-convolution2 in_ kernel trigger framesize = mkOsc AR "Convolution2" [in_,kernel,trigger,framesize] 1
-
--- | Real-time convolver with linear interpolation
-convolution2L :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-convolution2L in_ kernel trigger framesize crossfade = mkOsc AR "Convolution2L" [in_,kernel,trigger,framesize,crossfade] 1
-
--- | Time based convolver.
-convolution3 :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen
-convolution3 rate in_ kernel trigger framesize = mkOscR [AR,KR] rate "Convolution3" [in_,kernel,trigger,framesize] 1
-
--- | 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 =
-    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
-packFFTSpec m p = mce (interleave m p)
-    where interleave x = concat . zipWith (\a b -> [a,b]) x
-
--- | Apply function /f/ to each bin of an @FFT@ chain, /f/ receives
--- magnitude, phase and index and returns a (magnitude,phase).
-pvcollect :: UGen -> UGen -> (UGen -> UGen -> UGen -> (UGen, UGen)) -> UGen -> UGen -> UGen -> UGen
-pvcollect c nf f from to z = packFFT c nf from to z mp
-  where m = unpackFFT c nf from to 0
-        p = unpackFFT c nf from to 1
-        i = [from .. to]
-        e = zipWith3 f m p i
-        mp = uncurry packFFTSpec (unzip e)
-
--- | Complex addition.
-pv_Add :: UGen -> UGen -> UGen
-pv_Add ba bb = mkOsc KR "PV_Add" [ba,bb] 1
-
--- | Shift and scale the bin positions.
-pv_BinShift :: UGen -> UGen -> UGen -> UGen
-pv_BinShift buf str shift = mkOsc KR "PV_BinShift" [buf,str,shift] 1
-
--- | Combine low and high bins from two inputs.
-pv_BinWipe :: UGen -> UGen -> UGen -> UGen
-pv_BinWipe ba bb wp = mkOsc KR "PV_BinWipe" [ba,bb,wp] 1
-
--- | Clear bins above or below a cutoff point.
-pv_BrickWall :: UGen -> UGen -> UGen
-pv_BrickWall buf wp = mkOsc KR "PV_BrickWall" [buf,wp] 1
-
--- | Complex plane attack.
-pv_ConformalMap :: UGen -> UGen -> UGen -> UGen
-pv_ConformalMap buf real imag = mkOsc KR "PV_ConformalMap" [buf,real,imag] 1
-
--- | Copies spectral frame.
-pv_Copy :: UGen -> UGen -> UGen
-pv_Copy ba bb = mkOsc KR "PV_Copy" [ba,bb] 1
-
--- | Copy magnitudes and phases.
-pv_CopyPhase :: UGen -> UGen -> UGen
-pv_CopyPhase ba bb = mkOsc KR "PV_CopyPhase" [ba,bb] 1
-
--- | Random phase shifting.
-pv_Diffuser :: UGen -> UGen -> UGen
-pv_Diffuser buf trg = mkOsc KR "PV_Diffuser" [buf,trg] 1
-
--- | FFT onset detector.
-pv_HainsworthFoote :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-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 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
-pv_LocalMax buf thr = mkOsc KR "PV_LocalMax" [buf,thr] 1
-
--- | Pass bins above a threshold.
-pv_MagAbove :: UGen -> UGen -> UGen
-pv_MagAbove buf thr = mkOsc KR "PV_MagAbove" [buf,thr] 1
-
--- | Pass bins below a threshold.
-pv_MagBelow :: UGen -> UGen -> UGen
-pv_MagBelow buf thr = mkOsc KR "PV_MagBelow" [buf,thr] 1
-
--- | Clip bins to a threshold.
-pv_MagClip :: UGen -> UGen -> UGen
-pv_MagClip buf thr = mkOsc KR "PV_MagClip" [buf,thr] 1
-
--- | Freeze magnitudes.
-pv_MagFreeze :: UGen -> UGen -> UGen
-pv_MagFreeze buf frz = mkOsc KR "PV_MagFreeze" [buf,frz] 1
-
--- | Multiply magnitudes.
-pv_MagMul :: UGen -> UGen -> UGen
-pv_MagMul ba bb = mkOsc KR "PV_MagMul" [ba,bb] 1
-
--- | Multiply magnitudes by noise.
-pv_MagNoise :: UGen -> UGen
-pv_MagNoise buf = mkOsc KR "PV_MagNoise" [buf] 1
-
--- | Shift and stretch magnitude bin position.
-pv_MagShift :: UGen -> UGen -> UGen -> UGen
-pv_MagShift buf str shift = mkOsc KR "PV_MagShift" [buf,str,shift] 1
-
--- | Average magnitudes across bins.
-pv_MagSmear :: UGen -> UGen -> UGen
-pv_MagSmear buf bins = mkOsc KR "PV_MagSmear" [buf,bins] 1
-
--- | Square magnitudes.
-pv_MagSquared :: UGen -> UGen
-pv_MagSquared buf = mkOsc KR "PV_MagSquared" [buf] 1
-
--- | Maximum magnitude.
-pv_Max :: UGen -> UGen -> UGen
-pv_Max ba bb = mkOsc KR "PV_Max" [ba,bb] 1
-
--- | Minimum magnitude.
-pv_Min :: UGen -> UGen -> UGen
-pv_Min ba bb = mkOsc KR "PV_Min" [ba,bb] 1
-
--- | Complex multiply.
-pv_Mul :: UGen -> UGen -> UGen
-pv_Mul ba bb = mkOsc KR "PV_Mul" [ba,bb] 1
-
--- | Shift phase by 270 degrees.
-pv_PhaseShift270 :: UGen -> UGen
-pv_PhaseShift270 buf = mkOsc KR "PV_PhaseShift270" [buf] 1
-
--- | Shift phase by 90 degrees.
-pv_PhaseShift90 :: UGen -> UGen
-pv_PhaseShift90 buf = mkOsc KR "PV_PhaseShift90" [buf] 1
-
--- | Shift phase.
-pv_PhaseShift :: UGen -> UGen -> UGen
-pv_PhaseShift buf shift = mkOsc KR "PV_PhaseShift" [buf,shift] 1
-
--- | Make gaps in spectrum.
-pv_RectComb2 :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-pv_RectComb2 ba bb teeth phase width = mkOsc KR "PV_RectComb2" [ba,bb,teeth,phase,width] 1
-
--- | Make gaps in spectrum.
-pv_RectComb :: UGen -> UGen -> UGen -> UGen -> UGen
-pv_RectComb buf teeth phase width = mkOsc KR "PV_RectComb" [buf,teeth,phase,width] 1
-
--- | Unpack a single value (magnitude or phase) from an FFT chain
-unpack1FFT :: UGen -> UGen -> UGen -> UGen -> UGen
-unpack1FFT buf size index which = mkOsc DR "Unpack1FFT" [buf, size, index, which] 1
-
--- | Unpack an FFT chain into separate demand-rate FFT bin streams.
-unpackFFT :: UGen -> UGen -> UGen -> UGen -> UGen -> [UGen]
-unpackFFT c nf from to w = map (\i -> unpack1FFT c nf i w) [from .. to]
-
--- * Partitioned convolution
-
--- | Calculate size of accumulation buffer given FFT and IR sizes.
-pc_calcAccumSize :: Int -> Int -> Int
-pc_calcAccumSize fft_size ir_length =
-    let partition_size = fft_size `div` 2
-        num_partitions = (ir_length `div` partition_size) + 1
-    in fft_size * num_partitions
-
--- | Partitioned convolution.
-partConv :: UGen -> UGen -> UGen -> UGen
-partConv i sz ib = mkOsc AR "PartConv" [i, sz, ib] 1
-
--- Local Variables:
--- truncate-lines:t
--- End:
diff --git a/Sound/SC3/UGen/FFT/ID.hs b/Sound/SC3/UGen/FFT/ID.hs
deleted file mode 100644
--- a/Sound/SC3/UGen/FFT/ID.hs
+++ /dev/null
@@ -1,23 +0,0 @@
--- | Non-deterministic FFT 'UGen's.
-module Sound.SC3.UGen.FFT.ID where
-
-import Sound.SC3.UGen.Identifier
-import Sound.SC3.UGen.Rate
-import Sound.SC3.UGen.Type
-import Sound.SC3.UGen.UGen
-
--- | Randomize order of bins.
-pv_BinScramble :: ID i => i -> UGen -> UGen -> UGen -> UGen -> UGen
-pv_BinScramble z buf wp width trg = mkOscId z KR "PV_BinScramble" [buf,wp,width,trg] 1
-
--- | Randomly clear bins.
-pv_RandComb :: ID i => i -> UGen -> UGen -> UGen -> UGen
-pv_RandComb z buf wp trg = mkOscId z KR "PV_RandComb" [buf,wp,trg] 1
-
--- | Cross fade, copying bins in random order.
-pv_RandWipe :: ID i => i -> UGen -> UGen -> UGen -> UGen -> UGen
-pv_RandWipe z ba bb wp trg = mkOscId z KR "PV_RandWipe" [ba,bb,wp,trg] 1
-
--- Local Variables:
--- truncate-lines:t
--- End:
diff --git a/Sound/SC3/UGen/FFT/Monad.hs b/Sound/SC3/UGen/FFT/Monad.hs
deleted file mode 100644
--- a/Sound/SC3/UGen/FFT/Monad.hs
+++ /dev/null
@@ -1,18 +0,0 @@
--- | 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.UId
-
--- | Randomize order of bins.
-pv_BinScramble :: (UId m) => UGen -> UGen -> UGen -> UGen -> m UGen
-pv_BinScramble = liftUId4 F.pv_BinScramble
-
--- | Randomly clear bins.
-pv_RandComb :: (UId m) => UGen -> UGen -> UGen -> m UGen
-pv_RandComb = liftUId3 F.pv_RandComb
-
--- | Cross fade, copying bins in random order.
-pv_RandWipe :: (UId m) => UGen -> UGen -> UGen -> UGen -> m UGen
-pv_RandWipe = liftUId4 F.pv_RandWipe
diff --git a/Sound/SC3/UGen/Filter.hs b/Sound/SC3/UGen/Filter.hs
deleted file mode 100644
--- a/Sound/SC3/UGen/Filter.hs
+++ /dev/null
@@ -1,462 +0,0 @@
--- | Time-domain filter unit generators.
-module Sound.SC3.UGen.Filter where
-
-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
-
--- | Allpass filter (linear interpolation)
-allpassL :: UGen -> UGen -> UGen -> UGen -> UGen
-allpassL i mt dly dcy = mkFilter "AllpassL" [i,mt,dly,dcy] 1
-
--- | Allpass filter (cubic interpolation)
-allpassC :: UGen -> UGen -> UGen -> UGen -> UGen
-allpassC i mt dly dcy = mkFilter "AllpassC" [i,mt,dly,dcy] 1
-
--- | Basic psychoacoustic amplitude compensation.
-ampComp :: UGen -> UGen -> UGen -> UGen
-ampComp f r e = mkFilter "AmpComp" [f,r,e] 1
-
--- | ANSI A-weighting curve psychoacoustic amplitude compensation.
-ampCompA :: UGen -> UGen -> UGen -> UGen -> UGen
-ampCompA f r ma ra = mkFilter "AmpCompA" [f,r,ma,ra] 1
-
--- | Bandpass filter
-bpf :: UGen -> UGen -> UGen -> UGen
-bpf i freq rq = mkFilter "BPF" [i,freq,rq] 1
-
--- | Two zero fixed midpass filter.
-bpz2 :: UGen -> UGen
-bpz2 i = mkFilter "BPZ2" [i] 1
-
--- | Band reject filter
-brf :: UGen -> UGen -> UGen -> UGen
-brf i freq rq = mkFilter "BRF" [i,freq,rq] 1
-
--- | Two zero fixed midcut filter.
-brz2 :: UGen -> UGen
-brz2 i = mkFilter "BRZ2" [i] 1
-
--- | Clip input between lower and upper bounds.
-clip :: UGen -> UGen -> UGen -> UGen
-clip i l h = mkFilter "Clip" [i,l,h] 1
-
--- | Comb filter (no interpolation)
-combN :: UGen -> UGen -> UGen -> UGen -> UGen
-combN i mt dly dcy = mkFilter "CombN" [i,mt,dly,dcy] 1
-
--- | Comb filter (linear interpolation)
-combL :: UGen -> UGen -> UGen -> UGen -> UGen
-combL i mt dly dcy = mkFilter "CombL" [i,mt,dly,dcy] 1
-
--- | Comb filter (cubic interpolation)
-combC :: UGen -> UGen -> UGen -> UGen -> UGen
-combC i mt dly dcy = mkFilter "CombC" [i,mt,dly,dcy] 1
-
--- | Compressor,expander,limiter,gate,ducker.
-compander :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-compander i c t sb sa ct rt = mkFilter "Compander" [i,c,t,sb,sa,ct,rt] 1
-
--- | Convert signal to modal pitch.
-degreeToKey :: UGen -> UGen -> UGen -> UGen
-degreeToKey b i o = mkFilter "DegreeToKey" [b,i,o] 1
-
--- | Exponential decay.
-decay :: UGen -> UGen -> UGen
-decay i dcy = mkFilter "Decay" [i,dcy] 1
-
--- | Exponential decay (equvalent to $decay dcy - decay atk$).
-decay2 :: UGen -> UGen -> UGen -> UGen
-decay2 i atk dcy = mkFilter "Decay2" [i,atk,dcy] 1
-
--- | Single sample delay.
-delay1 :: UGen -> UGen
-delay1 i = mkFilter "Delay1" [i] 1
-
--- | Two sample delay.
-delay2 :: UGen -> UGen
-delay2 i = mkFilter "Delay2" [i] 1
-
--- | Simple delay line (cubic interpolation).
-delayC :: UGen -> UGen -> UGen -> UGen
-delayC i mt dly = mkFilter "DelayC" [i,mt,dly] 1
-
--- | Simple delay line (linear interpolation).
-delayL :: UGen -> UGen -> UGen -> UGen
-delayL i mt dly = mkFilter "DelayL" [i,mt,dly] 1
-
--- | Simple delay line (no interpolation).
-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
-
--- | FOF like filter.
-formlet :: UGen -> UGen -> UGen -> UGen -> UGen
-formlet i f a d = mkFilter "Formlet" [i,f,a,d] 1
-
--- | First order filter section.
-fos :: UGen -> UGen -> UGen -> UGen -> UGen
-fos i a0 a1 b1 = mkFilter "FOS" [i,a0,a1,b1] 1
-
--- | A simple reverb.
-freeVerb :: UGen -> UGen -> UGen -> UGen -> UGen
-freeVerb i mx room damp = mkFilter "FreeVerb" [i,mx,room,damp] 1
-
--- | A simple reverb (two channel).
-freeVerb2 :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-freeVerb2 i1 i2 mx room damp = mkFilter "FreeVerb2" [i1,i2,mx,room,damp] 2
-
--- | Gate.
-gate :: UGen -> UGen -> UGen
-gate i t = mkFilter "Gate" [i,t] 1
-
--- | A less-simple reverb.
-gVerb :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-gVerb i rs rt d bw sp dl rl tl mrs = mkFilter "GVerb" [i,rs,rt,d,bw,sp,dl,rl,tl,mrs] 2
-
--- | Hash input values.
-hasher :: UGen -> UGen
-hasher i = mkFilter "Hasher" [i] 1
-
--- | Hilbert transform.
-hilbert :: UGen -> UGen
-hilbert i = mkFilter "Hilbert" [i] 2
-
--- | Highpass filter.
-hpf :: UGen -> UGen -> UGen
-hpf i f = mkFilter "HPF" [i,f] 1
-
--- | Two point difference filter.
-hpz1 :: UGen -> UGen
-hpz1 i = mkFilter "HPZ1" [i] 1
-
--- | Two zero fixed highpass filter.
-hpz2 :: UGen -> UGen
-hpz2 i = mkFilter "HPZ2" [i] 1
-
--- | Is signal within specified range.
-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.
-klankSpec :: [UGen] -> [UGen] -> [UGen] -> UGen
-klankSpec f a dt = mce ((concat . transpose) [f,a,dt])
-
--- | Variant for non-UGen inputs.
-klankSpec' :: Real n => [n] -> [n] -> [n] -> UGen
-klankSpec' f a dt =
-    let u = map constant
-    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
-
--- | Nested lag filter.
-lag2 :: UGen -> UGen -> UGen
-lag2 i t = mkFilter "Lag2" [i,t] 1
-
--- | Twice nested lag filter.
-lag3 :: UGen -> UGen -> UGen
-lag3 i t = mkFilter "Lag3" [i,t] 1
-
--- | Lag variant with separate upward and downward times.
-lagUD :: UGen -> UGen -> UGen -> UGen
-lagUD i t1 t2 = mkFilter "LagUD" [i,t1,t2] 1
-
--- | Nested lag filter.
-lag2UD :: UGen -> UGen -> UGen -> UGen
-lag2UD i t1 t2 = mkFilter "Lag2UD" [i,t1,t2] 1
-
--- | Twice nested lag filter.
-lag3UD :: UGen -> UGen -> UGen -> UGen
-lag3UD i t1 t2 = mkFilter "Lag3UD" [i,t1,t2] 1
-
--- | Last value before chang above threshhold.
-lastValue :: UGen -> UGen -> UGen
-lastValue i t = mkFilter "LastValue" [i,t] 1
-
--- | Sample and hold.
-latch :: UGen -> UGen -> UGen
-latch i t = mkFilter "Latch" [i,t] 1
-
--- | Remove DC offset.
-leakDC :: UGen -> UGen -> UGen
-leakDC i coef = mkFilter "LeakDC" [i,coef] 1
-
--- | Limiter.
-limiter :: UGen -> UGen -> UGen -> UGen
-limiter i l d = mkFilter "Limiter" [i,l,d] 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
-
--- | Lowpass filter.
-lpf :: UGen -> UGen -> UGen
-lpf i f = mkFilter "LPF" [i,f] 1
-
--- | Two point average filter.
-lpz1 :: UGen -> UGen
-lpz1 i = mkFilter "LPZ1" [i] 1
-
--- | Two zero fixed lowpass filter.
-lpz2 :: UGen -> UGen
-lpz2 i = mkFilter "LPZ2" [i] 1
-
--- | Masks off bits in the mantissa of signal.
-mantissaMask :: UGen -> UGen -> UGen
-mantissaMask i bits = mkFilter "MantissaMask" [i,bits] 1
-
--- | Median filter.
-median :: UGen -> UGen -> UGen
-median size i = mkFilter "Median" [size,i] 1
-
--- | Parametric filter.
-midEQ :: UGen -> UGen -> UGen -> UGen -> UGen
-midEQ i f rq db = mkFilter "MidEQ" [i,f,rq,db] 1
-
--- | Moog VCF implementation.
-moogFF :: UGen -> UGen -> UGen -> UGen -> UGen
-moogFF i f g r = mkFilter "MoogFF" [i,f,g,r] 1
-
--- | Most changed input.
-mostChange :: UGen -> UGen -> UGen
-mostChange a b = mkFilter "MostChange" [a,b] 1
-
--- | Multiply add ternary operator.
-mulAdd :: UGen -> UGen -> UGen -> UGen
-mulAdd s m a = mkFilter "MulAdd" [s,m,a] 1
-
--- | Normalizer (flattens dynamics).
-normalizer :: UGen -> UGen -> UGen -> UGen
-normalizer i l d = mkFilter "Normalizer" [i,l,d] 1
-
--- | One pole filter.
-onePole :: UGen -> UGen -> UGen
-onePole i coef = mkFilter "OnePole" [i,coef] 1
-
--- | One zero filter.
-oneZero :: UGen -> UGen -> UGen
-oneZero i coef = mkFilter "OneZero" [i,coef] 1
-
--- | Maximum value.
-peak :: UGen -> UGen -> UGen
-peak t r = mkFilter "Peak" [t,r] 1
-
--- | Simple time domain pitch shifter.
-pitchShift :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-pitchShift i w p d t = mkFilter "PitchShift" [i,w,p,d,t] 1
-
--- | Karplus-Strong synthesis.
-pluck :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-pluck i tr mdl dl dc coef = mkFilter "Pluck" [i,tr,mdl,dl,dc,coef] 1
-
--- | Trigger counter.
-pulseCount :: UGen -> UGen -> UGen
-pulseCount t r = mkFilter "PulseCount" [t,r] 1
-
--- | Pass every nth trigger.
-pulseDivider :: UGen -> UGen -> UGen -> UGen
-pulseDivider t factor start = mkFilter "PulseDivider" [t,factor,start] 1
-
--- | Linear lag.
-ramp :: UGen -> UGen -> UGen
-ramp i t = mkFilter "Ramp" [i,t] 1
-
--- | Resonant highpass filter.
-rhpf :: UGen -> UGen -> UGen -> UGen
-rhpf i freq rq = mkFilter "RHPF" [i,freq,rq] 1
-
--- | Resonant lowpass filter.
-rlpf :: UGen -> UGen -> UGen -> UGen
-rlpf i freq rq = mkFilter "RLPF" [i,freq,rq] 1
-
--- | Resonant filter.
-resonz :: UGen -> UGen -> UGen -> UGen
-resonz i freq bwr = mkFilter "Resonz" [i,freq,bwr] 1
-
--- | Ringing filter (equivalent to Resonz).
-ringz :: UGen -> UGen -> UGen -> UGen
-ringz i freq dcy = mkFilter "Ringz" [i,freq,dcy] 1
-
--- | Track maximum level.
-runningMax :: UGen -> UGen -> UGen
-runningMax i t = mkFilter "RunningMax" [i,t] 1
-
--- | Track minimum level.
-runningMin :: UGen -> UGen -> UGen
-runningMin i t = mkFilter "RunningMin" [i,t] 1
-
--- | Running sum.
-runningSum :: UGen -> UGen -> UGen
-runningSum i n = mkFilter "RunningSum" [i,n] 1
-
--- | Select output from array of inputs.
-select :: UGen -> UGen -> UGen
-select i a = mkFilterMCE "Select" [i] a 1
-
--- | Send a trigger message from the server back to the all registered clients.
-sendTrig :: UGen -> UGen -> UGen -> UGen
-sendTrig i k v = mkFilter "SendTrig" [i,k,v] 0
-
--- | Send a reply message from the server back to the all registered clients.
-sendReply :: UGen -> UGen -> String -> [UGen] -> UGen
-sendReply i k n v =
-    let n' = map (fromIntegral . fromEnum) n
-        s = fromIntegral (length n')
-    in mkFilter "SendReply" ([i,k,s] ++ n' ++ v) 0
-
--- | Set-reset flip flop.
-setResetFF :: UGen -> UGen -> UGen
-setResetFF t r = mkFilter "SetResetFF" [t,r] 1
-
--- | Wave shaper.
-shaper :: UGen -> UGen -> UGen
-shaper b s = mkFilter "Shaper" [b,s] 1
-
--- | Remove transients and higher frequencies.
-slew :: UGen -> UGen -> UGen -> UGen
-slew i up dn = mkFilter "Slew" [i,up,dn] 1
-
--- | Second order filter section (biquad).
-sos :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-sos i a0 a1 a2 b1 b2 = mkFilter "SOS" [i,a0,a1,a2,b1,b2] 1
-
--- | Stepper pulse counter.
-stepper :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-stepper t r mn mx s v = mkFilter "Stepper" [t,r,mn,mx,s,v] 1
-
--- | Triggered linear ramp.
-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
-
--- | Time since last triggered.
-timer :: UGen -> UGen
-timer t = mkFilter "Timer" [t] 1
-
--- | Toggle flip flop.
-toggleFF :: UGen -> UGen
-toggleFF t = mkFilter "ToggleFF" [t] 1
-
--- | When triggered output trigger for specified duration.
-trig :: UGen -> UGen -> UGen
-trig i d = mkFilter "Trig" [i,d] 1
-
--- | When triggered output unit signal for specified duration.
-trig1 :: UGen -> UGen -> UGen
-trig1 i d = mkFilter "Trig1" [i,d] 1
-
--- | Two pole filter.
-twoPole :: UGen -> UGen -> UGen -> UGen
-twoPole i freq radius = mkFilter "TwoPole" [i,freq,radius] 1
-
--- | 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
-wrap i j k = mkFilter "Wrap" [i,j,k] 1
-
--- | Index into a table with a signal.
-wrapIndex :: UGen -> UGen -> UGen
-wrapIndex b i = mkFilter "WrapIndex" [b,i] 1
-
--- * BEQ filters
-
--- | Bi-quad low-pass filter.
-bLowPass :: UGen -> UGen -> UGen -> UGen
-bLowPass i f rq = mkFilter "BLowPass" [i,f,rq] 1
-
--- | Bi-quad high-pass filter.
-bHiPass :: UGen -> UGen -> UGen -> UGen
-bHiPass i f rq = mkFilter "BHiPass" [i,f,rq] 1
-
--- | Bi-quad all-pass filter.
-bAllPass :: UGen -> UGen -> UGen -> UGen
-bAllPass i f rq = mkFilter "BAllPass" [i,f,rq] 1
-
--- | Bi-quad band-pass filter.
-bBandPass :: UGen -> UGen -> UGen -> UGen
-bBandPass i f bw = mkFilter "BBandPass" [i,f,bw] 1
-
--- | Bi-quad band-stop filter.
-bBandStop :: UGen -> UGen -> UGen -> UGen
-bBandStop i f bw = mkFilter "BBandStop" [i,f,bw] 1
-
--- | Bi-quad peak equaliser.
-bPeakEQ :: UGen -> UGen -> UGen -> UGen -> UGen
-bPeakEQ i f rq db = mkFilter "BPeakEQ" [i,f,rq,db] 1
-
--- | Bi-quad low shelf filter.
-bLowShelf :: UGen -> UGen -> UGen -> UGen -> UGen
-bLowShelf i f rs db = mkFilter "BLowShelf" [i,f,rs,db] 1
-
--- | Bi-quad high shelf filter.
-bHiShelf :: UGen -> UGen -> UGen -> UGen -> UGen
-bHiShelf i f rs db = mkFilter "BHiShelf" [i,f,rs,db] 1
-
--- | Calculate coefficients for bi-quad low pass filter.
-bLowPassCoef :: Floating a => a -> a -> a -> (a,a,a,a,a)
-bLowPassCoef sr freq rq =
-    let w0 = pi * 2 * freq * (1 / sr)
-        cos_w0 = cos w0
-        i = 1 - cos_w0
-        alpha = sin w0 * 0.5 * rq
-        b0rz = recip (1 + alpha)
-        a0 = i * 0.5 * b0rz
-        a1 = i * b0rz
-        b1 = cos_w0 * 2 * b0rz
-        b2 = (1 - alpha) * negate b0rz
-    in (a0,a1,a0,b1,b2)
diff --git a/Sound/SC3/UGen/Granular.hs b/Sound/SC3/UGen/Granular.hs
deleted file mode 100644
--- a/Sound/SC3/UGen/Granular.hs
+++ /dev/null
@@ -1,30 +0,0 @@
--- | Granular synthesis unit generators.
-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.
-grainBuf :: Int -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-grainBuf nc t d s r p i l e mx = mkOsc AR "GrainBuf" [t, d, s, r, p, i, l, e, mx] nc
-
--- | Granular synthesis with frequency modulated sine tones.
-grainFM :: Int -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-grainFM nc t d c m i l e mx = mkOsc AR "GrainFM" [t, d, c, m, i, l, e, mx] nc
-
--- | Granulate an input signal.
-grainIn :: Int -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-grainIn nc t d i l e mx = mkOsc AR "GrainIn" [t, d, i, l, e, mx] nc
-
--- | Granular synthesis with sine tones.
-grainSin :: Int -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-grainSin nc t d f l e mx = mkOsc AR "GrainSin" [t, d, f, l, e, mx] nc
-
--- | Warp a buffer with a time pointer.
-warp1 :: Int -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-warp1 nc b p f w e o r i = mkOsc AR "Warp1" [b, p, f, w, e, o, r, i] nc
-
--- Local Variables:
--- truncate-lines:t
--- End:
diff --git a/Sound/SC3/UGen/Graph.hs b/Sound/SC3/UGen/Graph.hs
--- a/Sound/SC3/UGen/Graph.hs
+++ b/Sound/SC3/UGen/Graph.hs
@@ -1,43 +1,494 @@
--- | Standard SC3 graphs, referenced in documentation.
+-- | 'Graph' and related types.
 module Sound.SC3.UGen.Graph where
 
-import Sound.SC3.UGen.ID
+import qualified Data.IntMap as M {- containers -}
+import Data.Function {- base -}
+import Data.List{- base -}
+import Data.Maybe{- base -}
 
--- | The SC3 /default/ instrument 'UGen' graph.
-default_ugen_graph :: UGen
-default_ugen_graph =
-    let f = control KR "freq" 440
-        a = control KR "amp" 0.1
-        p = control KR "pan" 0
-        g = control KR "gate" 1
-        e = linen g 0.01 0.7 0.3 RemoveSynth
-        f3 = mce [f,f + rand 'α' (-0.4) 0,f + rand 'β' 0 0.4]
-        l = xLine KR (rand 'γ' 4000 5000) (rand 'δ' 2500 3200) 1 DoNothing
-        z = lpf (mix (varSaw AR f3 0 0.3 * 0.3)) l * e
-    in out 0 (pan2 z p a)
+import Sound.SC3.UGen.Rate
+import Sound.SC3.UGen.Type
+import Sound.SC3.UGen.UGen
 
--- | A /Gabor/ grain, envelope is by 'lfGauss'.
-gabor_grain_ugen_graph :: UGen
-gabor_grain_ugen_graph =
-    let o = control IR "out" 0
-        f = control IR "freq" 440
-        d = control IR "sustain" 1
-        l = control IR "pan" 1
-        a = control IR "amp" 0.1
-        w = control IR "width" 0.25
-        e = lfGauss AR d w 0 NoLoop RemoveSynth
-        s = fSinOsc AR f (0.5 * pi) * e
-    in offsetOut o (pan2 s l a)
+-- * Type
 
--- | A /sine/ grain, envelope is by 'envGen' of 'envSine'.
-sine_grain_ugen_graph :: UGen
-sine_grain_ugen_graph =
-    let o = control IR "out" 0
-        f = control IR "freq" 440
-        d = control IR "sustain" 1
-        l = control IR "pan" 1
-        a = control IR "amp" 0.1
-        w = control IR "width" 0.25
-        e = envGen AR 1 1 0 1 DoNothing (envSine (d * w) 1)
-        s = fSinOsc AR f (0.5 * pi) * e
-    in offsetOut o (pan2 s l a)
+-- | 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 (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 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 :: Sample}
+          | NodeK {node_id :: NodeId
+                  ,node_k_rate :: Rate
+                  ,node_k_index :: Maybe Int
+                  ,node_k_name :: String
+                  ,node_k_default :: Sample
+                  ,node_k_type :: KType
+                  ,node_k_meta :: Maybe (C_Meta Sample)}
+          | 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 (Show)
+
+node_k_eq :: Node -> Node -> Bool
+node_k_eq p q =
+    case (p,q) of
+      (NodeK k rt ix nm df tr me,NodeK k' rt' ix' nm' df' tr' me') ->
+          k == k' && rt == rt' && ix == ix' && nm == nm' && df == df' && tr == tr' && me == me'
+      _ -> error "node_k_eq? not Node_K"
+
+-- * Building
+
+-- | 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)
+
+-- | 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"
+
+-- | Predicate to determine if 'Node' is a constant with indicated /value/.
+find_c_p :: Sample -> 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 :: Sample -> 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 :: Control -> Graph -> (Node,Graph)
+push_k (Control r ix nm d tr meta) g =
+    let n = NodeK (nextId g) r ix nm d (ktype r tr) meta
+    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 c g =
+    let nm = controlName c
+        y = find (find_k_p nm) (controls g)
+    in maybe (push_k c 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})
+
+-- | Transform 'UGen' into 'Graph', appending to existing 'Graph'.
+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 (show ("mk_node: label",u))
+      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 ->
+          -- allow RHS of MRG node to be MCE (splice all nodes into graph)
+          let f g' l = case l of
+                         [] -> g'
+                         n:l' -> let (_,g'') = mk_node n g' in f g'' l'
+          in mk_node (mrgLeft m) (f g (mceChannels (mrgRight m)))
+      MCE_U _ -> error (show ("mk_node: mce",u))
+
+-- | 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
+
+-- | If controls have been given indices they must be coherent.
+sort_controls :: [Node] -> [Node]
+sort_controls c =
+    let node_k_ix n = maybe maxBound id (node_k_index n)
+        cmp = compare `on` node_k_ix
+        c' = sortBy cmp c
+        coheres z = maybe True (== z) . node_k_index
+        coherent = all id (zipWith coheres [0..] c')
+    in if coherent then c' else error (show ("sort_controls: incoherent",c))
+
+-- | Variant on 'mk_node' starting with an empty graph, reverses the
+-- 'UGen' list and sorts the 'Control' list, and adds implicit nodes.
+mk_graph :: UGen -> Graph
+mk_graph u =
+    let (_,g) = mk_node (prepare_root u) empty_graph
+        g' = g {ugens = reverse (ugens g)
+               ,controls = sort_controls (controls g)}
+    in add_implicit g'
+
+-- * Encoding
+
+type Map = M.IntMap Int
+
+type Maps = (Map,[Node],Map,Map,[(KType,Int)])
+
+-- | 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
+
+-- * Implicit (Control, MaxLocalBuf)
+
+-- | 4-tuple to count 'KType's.
+type KS_COUNT = (Int,Int,Int,Int)
+
+-- | Count the number of /controls/ of 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_ctl :: [Node] -> [Node]
+mk_implicit_ctl 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)]
+
+-- | Add implicit /control/ UGens to 'Graph'.
+add_implicit_ctl :: Graph -> Graph
+add_implicit_ctl g =
+    let (Graph z cs ks us) = g
+        ks' = sortBy node_k_cmp ks
+        im = if null ks' then [] else mk_implicit_ctl ks'
+        us' = im ++ us
+    in Graph z cs ks' us'
+
+-- | Zero if no local buffers, or if maxLocalBufs is given.
+localbuf_count :: [Node] -> Int
+localbuf_count us =
+    case find ((==) "MaxLocalBufs" . node_u_name) us of
+      Nothing -> length (filter ((==) "LocalBuf" . node_u_name) us)
+      Just _ -> 0
+
+-- | Add implicit 'maxLocalBufs' if not present.
+add_implicit_buf :: Graph -> Graph
+add_implicit_buf g =
+    case localbuf_count (ugens g) of
+      0 -> g
+      n -> let (c,g') = mk_node_c (Constant (fromIntegral n)) g
+               p = as_from_port c
+               u = NodeU (-1) IR "MaxLocalBufs" [p] [] (Special 0) no_id
+           in g' {ugens = u : ugens g'}
+
+-- | 'add_implicit_buf' and 'add_implicit_ctl'.
+add_implicit :: Graph -> Graph
+add_implicit = add_implicit_buf . add_implicit_ctl
+
+-- | 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
+
+-- | Is Node implicit?
+is_implicit :: Node -> Bool
+is_implicit n = node_u_name n == "MaxLocalBufs" || is_implicit_control n
+
+-- | Remove implicit UGens from 'Graph'
+remove_implicit :: Graph -> Graph
+remove_implicit g =
+    let u = filter (not . is_implicit) (ugens g)
+    in g {ugens = u}
+
+-- * Queries
+
+-- | Is 'FromPort' 'FromPort_U'.
+is_from_port_u :: FromPort -> Bool
+is_from_port_u p =
+    case p of
+      FromPort_U _ _ -> True
+      _ -> False
+
+-- | List of 'FromPort_U' at /e/ with multiple out edges.
+multiple_u_out_edges :: [Edge] -> [FromPort]
+multiple_u_out_edges e =
+    let p = filter is_from_port_u (map fst e)
+        p' = group (sortBy (compare `on` port_nid) p)
+    in map head (filter ((> 1) . length) p')
+
+-- | Descendents at 'Graph' of 'Node'.
+node_descendents :: Graph -> Node -> [Node]
+node_descendents g n =
+    let e = edges (ugens g)
+        c = filter ((== node_id n) . port_nid . fst) e
+        f (ToPort k _) = k
+    in mapMaybe (find_node g) (map (f . snd) c)
+
+-- * PV edge accounting
+
+-- | List @PV@ 'Node's at 'Graph' with multiple out edges.
+pv_multiple_out_edges :: Graph -> [Node]
+pv_multiple_out_edges g =
+    let e = edges (ugens g)
+        p = multiple_u_out_edges e
+        n = mapMaybe (find_node g) (map port_nid p)
+    in filter (primitive_is_pv_rate . node_u_name) n
+
+-- | Error if graph has invalid @PV@ subgraph, ie. multiple out edges
+-- at @PV@ node not connecting to @Unpack1FFT@ & @PackFFT@.
+pv_validate :: Graph -> Graph
+pv_validate g =
+    case pv_multiple_out_edges g of
+      [] -> g
+      n -> let d = concatMap (map node_u_name . node_descendents g) n
+           in if any primitive_is_pv_rate d || any (`elem` ["IFFT"]) d
+              then error (show
+                          ("pv_validate: multiple out edges, see pv_split"
+                          ,map node_u_name n
+                          ,d))
+              else g
+
+-- | Transform a unit generator into a graph.
+--
+-- > import Sound.SC3.UGen
+-- > ugen_to_graph (out 0 (pan2 (sinOsc AR 440 0) 0.5 0.1))
+ugen_to_graph :: UGen -> Graph
+ugen_to_graph = pv_validate . mk_graph
+
diff --git a/Sound/SC3/UGen/Graph/Reconstruct.hs b/Sound/SC3/UGen/Graph/Reconstruct.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/UGen/Graph/Reconstruct.hs
@@ -0,0 +1,129 @@
+-- | A /disasembler/ for UGen graphs.
+module Sound.SC3.UGen.Graph.Reconstruct where
+
+import Data.Char {- base -}
+import Data.Function {- base -}
+import Data.List {- base -}
+import Text.Printf {- base -}
+
+import Sound.SC3.UGen.Graph
+import Sound.SC3.UGen.Operator
+import Sound.SC3.UGen.Rate
+import Sound.SC3.UGen.Type
+import Sound.SC3.UGen.UGen
+
+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
+
+-- | Generate a reconstruction of a 'Graph'.
+--
+-- > 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::Sample)" l c
+
+reconstruct_c_ugen :: Node -> UGen
+reconstruct_c_ugen u = constant (node_c_value u)
+
+-- | Discards index.
+reconstruct_k_rnd :: Node -> (Rate,String,Sample)
+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_f64 r Nothing 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/UGen/Graph/Transform.hs b/Sound/SC3/UGen/Graph/Transform.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/UGen/Graph/Transform.hs
@@ -0,0 +1,78 @@
+-- | Transformations over 'Graph' structure.
+module Sound.SC3.UGen.Graph.Transform where
+
+import Data.Either {- base -}
+import Data.List {- base -}
+import Data.Maybe {- base -}
+
+import Sound.SC3.UGen.Graph
+import Sound.SC3.UGen.Rate
+
+-- * Lift constants
+
+-- | Transform 'NodeC' to 'NodeK', 'id' for other 'Node' types.
+--
+-- > let r = (NodeK 8 KR Nothing "k_8" 0.1 K_KR,9)
+-- > in constant_to_control 8 (NodeC 0 0.1) == r
+constant_to_control :: NodeId -> Node -> (NodeId,Node)
+constant_to_control z n =
+    case n of
+      NodeC _ k -> (z+1,NodeK z KR Nothing ("k_" ++ show z) k K_KR Nothing)
+      _ -> (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' [] (nubBy node_k_eq (k ++ k')) u'
+    in add_implicit g'
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
@@ -6,7 +6,7 @@
 import Data.List.Split {- split -}
 import Data.Maybe
 import System.IO.Error
-import System.Cmd {- process -}
+import System.Process {- process -}
 import System.Directory {- directory -}
 import System.Environment
 import System.FilePath {- filepath -}
@@ -92,6 +92,8 @@
            Nothing -> error (show ("ugenSC3HelpFile",d,cf,x,s))
 
 -- | Use @BROWSER@ or @x-www-browser@ to view SC3 help file for `u'.
+--
+-- > get_env_default "BROWSER" "x-www-browser"
 --
 -- > import Sound.SC3.UGen.Name
 -- >
diff --git a/Sound/SC3/UGen/Help/Graph.hs b/Sound/SC3/UGen/Help/Graph.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/UGen/Help/Graph.hs
@@ -0,0 +1,77 @@
+-- | Standard SC3 graphs, referenced in documentation.
+module Sound.SC3.UGen.Help.Graph where
+
+import Sound.SC3.UGen
+import Sound.SC3.UGen.Bindings
+
+-- | The SC3 /default/ instrument 'UGen' graph.
+default_ugen_graph :: UGen
+default_ugen_graph =
+    let f = control KR "freq" 440
+        a = control KR "amp" 0.1
+        p = control KR "pan" 0
+        g = control KR "gate" 1
+        o = control KR "out" 0
+        e = linen g 0.01 0.7 0.3 RemoveSynth
+        f3 = mce [f,f + rand 'α' (-0.4) 0,f + rand 'β' 0 0.4]
+        l = xLine KR (rand 'γ' 4000 5000) (rand 'δ' 2500 3200) 1 DoNothing
+        z = lpf (mix (varSaw AR f3 0 0.3 * 0.3)) l * e
+    in out o (pan2 z p a)
+
+-- | A /Gabor/ grain, envelope is by 'lfGauss'.
+gabor_grain_ugen_graph :: UGen
+gabor_grain_ugen_graph =
+    let o = control IR "out" 0
+        f = control IR "freq" 440
+        d = control IR "sustain" 1
+        l = control IR "pan" 1
+        a = control IR "amp" 0.1
+        w = control IR "width" 0.25
+        e = lfGauss AR d w 0 NoLoop RemoveSynth
+        s = fSinOsc AR f (0.5 * pi) * e
+    in offsetOut o (pan2 s l a)
+
+-- | A /sine/ grain, envelope is by 'envGen' of 'envSine'.
+sine_grain_ugen_graph :: UGen
+sine_grain_ugen_graph =
+    let o = control IR "out" 0
+        f = control IR "freq" 440
+        d = control IR "sustain" 1
+        l = control IR "pan" 1
+        a = control IR "amp" 0.1
+        w = control IR "width" 0.25
+        e = envGen AR 1 1 0 1 DoNothing (envSine (d * w) 1)
+        s = fSinOsc AR f (0.5 * pi) * e
+    in offsetOut o (pan2 s l a)
+
+-- | Trivial file playback instrument.
+--
+-- If /use_gate/ is 'True' there is a /gate/ parameter and the synth
+-- ends either when the sound file ends or the gate closes, else there
+-- is a /sustain/ parameter to indicate the duration.  In both cases a
+-- linear envelope with a decay time of /decay/ is applied.
+--
+-- The /rdelay/ parameter sets the maximum pre-delay time (in
+-- seconds), each instance is randomly pre-delayed between zero and
+-- the indicated time.  The /ramplitude/ parameter sets the maximum
+-- amplitude offset of the /amp/ parameter, each instance is randomly
+-- amplified between zero and the indicated value.
+default_sampler_ugen_graph :: Bool -> UGen
+default_sampler_ugen_graph use_gate =
+    let b = control KR "bufnum" 0
+        l = control KR "pan" 0
+        a = control KR "amp" 0.1
+        r = control KR "rate" 1
+        m = control KR "rdelay" 0
+        v = control KR "ramplitude" 0
+        w = control KR "attack" 0
+        y = control KR "decay" 0.5
+        r' = bufRateScale KR b * r
+        p = playBuf 1 AR b r' 1 0 NoLoop RemoveSynth
+        e = if use_gate
+            then let g = control KR "gate" 1
+                 in envGen KR g 1 0 1 RemoveSynth (envASR w 1 y EnvSin)
+            else let s = control KR "sustain" 1
+                 in envGen KR 1 1 0 1 RemoveSynth (envLinen w s y 1)
+        d = delayC (p * e) m (rand 'α' 0 m)
+    in out 0 (pan2 d l (a + rand 'β' 0 v))
diff --git a/Sound/SC3/UGen/ID.hs b/Sound/SC3/UGen/ID.hs
deleted file mode 100644
--- a/Sound/SC3/UGen/ID.hs
+++ /dev/null
@@ -1,12 +0,0 @@
--- | 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
deleted file mode 100644
--- a/Sound/SC3/UGen/IO.hs
+++ /dev/null
@@ -1,87 +0,0 @@
--- | Audio bus, control bus and input device unit generators.
-module Sound.SC3.UGen.IO where
-
-import Sound.SC3.UGen.Enum
-import Sound.SC3.UGen.Rate
-import Sound.SC3.UGen.Type
-import Sound.SC3.UGen.UGen
-
--- | Read signal from an audio or control bus.
-in' :: Int -> Rate -> UGen -> UGen
-in' nc r bus = mkOsc r "In" [bus] nc
-
--- | Define and read from buses local to a synthesis node.
-localIn :: Int -> Rate -> UGen
-localIn nc r = mkOsc r "LocalIn" [] nc
-
--- | Control rate bus input with lag.
-lagIn :: Int -> UGen -> UGen -> UGen
-lagIn nc bus lag = mkOsc KR "LagIn" [bus, lag] nc
-
--- | Read signal from a bus without erasing it.
-inFeedback :: Int -> UGen -> UGen
-inFeedback nc bus = mkOsc AR "InFeedback" [bus] nc
-
--- | Generate a trigger anytime a bus is set.
-inTrig :: Int -> UGen -> UGen
-inTrig nc bus = mkOsc KR "InTrig" [bus] nc
-
--- | Mix signal to an audio or control bus.
-out :: UGen -> UGen -> UGen
-out bus i = mkFilterMCE "Out" [bus] i 0
-
--- | Over-write signal to an audio or control bus.
-replaceOut :: UGen -> UGen -> UGen
-replaceOut bus i = mkFilterMCE "ReplaceOut" [bus] i 0
-
--- | Mix signal to an audio bus at precise sample offset.
-offsetOut :: UGen -> UGen -> UGen
-offsetOut bus i = mkOscMCE AR "OffsetOut" [bus] i 0
-
--- | Write signal to bus local to a synthesis node, see localIn.
-localOut :: UGen -> UGen
-localOut i = mkFilterMCE "LocalOut" [] i 0
-
--- | Crossfade signal to an audio or control bus.
-xOut :: UGen -> UGen -> UGen -> UGen
-xOut bus xfade i = mkFilterMCE "XOut" [bus, xfade] i 0
-
--- | Write to a shared control bus.
-sharedOut :: UGen -> UGen -> UGen
-sharedOut bus i = mkOscMCE KR "SharedOut" [bus] i 0
-
--- | Read from a shared control bus.
-sharedIn :: Int -> UGen -> UGen
-sharedIn nc bus = mkOsc KR "SharedIn" [bus] nc
-
--- | Report the status of a particular key.
-keyState :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen
-keyState r key minVal maxVal lag = mkOscR [KR] r "KeyState" [key, minVal, maxVal, lag] 1
-
--- | Report the status of the first pointer button.
-mouseButton :: Rate -> UGen -> UGen -> UGen -> UGen
-mouseButton r ll rl lag = mkOscR [KR] r "MouseButton" [ll, rl, lag] 1
-
--- | Cursor UGen, X axis.
-mouseX :: Rate -> UGen -> UGen -> Warp -> UGen -> UGen
-mouseX r ll rl w lag = mkOscR [KR] r "MouseX" [ll, rl, from_warp w, lag] 1
-
--- | Cursor UGen, Y axis.
-mouseY :: Rate -> UGen -> UGen -> Warp -> UGen -> UGen
-mouseY r ll rl w lag = mkOscR [KR] r "MouseY" [ll, rl, from_warp w, lag] 1
-
--- | Control variant.
-trigControl :: Int -> Rate -> UGen
-trigControl nc r = mkOsc r "TrigControl" [] nc
-
--- | Set the synth's random generator ID.
-randID :: Rate -> UGen -> UGen
-randID r n = mkOsc r "RandID" [n] 1
-
--- | Set the synth's random generator seed.
-randSeed :: Rate -> UGen -> UGen -> UGen
-randSeed r tr sd = mkOsc r "RandSeed" [tr,sd] 1
-
--- Local Variables:
--- truncate-lines:t
--- End:
diff --git a/Sound/SC3/UGen/Identifier.hs b/Sound/SC3/UGen/Identifier.hs
--- a/Sound/SC3/UGen/Identifier.hs
+++ b/Sound/SC3/UGen/Identifier.hs
@@ -1,32 +1,21 @@
 -- | Typeclass and functions to manage UGen identifiers.
 module Sound.SC3.UGen.Identifier where
 
-import Data.Char {- base -}
-import qualified Data.Digest.Murmur32 as H {- murmur-hash -}
+import qualified Data.Hashable as H {- hashable -}
 
 -- | Typeclass to constrain UGen identifiers.
-class ID a where
+class H.Hashable a => ID a where
     resolveID :: a -> Int
+    resolveID = H.hash
 
 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
+instance ID Float where
+instance ID Double where
 
 -- | Hash 'ID's /p/ and /q/ and sum to form an 'Int'.
 --
--- > 'a' `joinID` (1::Int) == 149929881
+-- > 'a' `joinID` (1::Int) == 1627429042
 joinID :: (ID a,ID b) => a -> b -> Int
-joinID p q = idHash p + idHash q
+joinID p q = H.hash p `H.hashWithSalt` H.hash q
diff --git a/Sound/SC3/UGen/Information.hs b/Sound/SC3/UGen/Information.hs
deleted file mode 100644
--- a/Sound/SC3/UGen/Information.hs
+++ /dev/null
@@ -1,60 +0,0 @@
--- | Unit generators to access information related to the synthesis
---   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.
-sampleRate :: UGen
-sampleRate = mkInfo "SampleRate"
-
--- | Duration of one sample, seconds.
-sampleDur :: UGen
-sampleDur = mkInfo "SampleDur"
-
--- | Duration of one sample, radians.
-radiansPerSample :: UGen
-radiansPerSample = mkInfo "RadiansPerSample"
-
--- | Control rate of synthesis server, periods per second.
-controlRate :: UGen
-controlRate = mkInfo "ControlRate"
-
--- | Sub-sample accurate scheduling offset.
-subsampleOffset :: UGen
-subsampleOffset = mkInfo "SubsampleOffset"
-
--- | Number of allocated output audio rate buses.
-numOutputBuses :: UGen
-numOutputBuses = mkInfo "NumOutputBuses"
-
--- | Number of allocated input audio rate buses.
-numInputBuses :: UGen
-numInputBuses = mkInfo "NumInputBuses"
-
--- | Number of allocated audio rate buses.
-numAudioBuses :: UGen
-numAudioBuses = mkInfo "NumAudioBuses"
-
--- | Number of allocated control rate buses.
-numControlBuses :: UGen
-numControlBuses = mkInfo "NumControlBuses"
-
--- | Number of allocated buffers.
-numBuffers :: UGen
-numBuffers = mkInfo "NumBuffers"
-
--- | 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
--- a/Sound/SC3/UGen/MCE.hs
+++ b/Sound/SC3/UGen/MCE.hs
@@ -37,8 +37,10 @@
 
 instance Num n => Num (MCE n) where
     (+) = mce_binop (+)
+    (-) = mce_binop (-)
     (*) = mce_binop (*)
     abs = mce_map abs
+    negate = mce_map negate
     signum = mce_map signum
     fromInteger = MCE_Unit . fromInteger
 
diff --git a/Sound/SC3/UGen/MachineListening.hs b/Sound/SC3/UGen/MachineListening.hs
deleted file mode 100644
--- a/Sound/SC3/UGen/MachineListening.hs
+++ /dev/null
@@ -1,58 +0,0 @@
--- | Machine listening & feature extraction analysis unit generators.
-module Sound.SC3.UGen.MachineListening where
-
-import Data.List
-import Data.Maybe
-import Sound.SC3.UGen.Rate
-import Sound.SC3.UGen.Type
-import Sound.SC3.UGen.UGen
-
--- | Autocorrelation beat tracker.
-beatTrack :: UGen -> UGen -> UGen
-beatTrack fft lock = mkOsc KR "BeatTrack" [fft, lock] 4
-
--- | Template matching beat tracker.
-beatTrack2 :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-beatTrack2 b mf ws pa lk w = mkOsc KR "BeatTrack2" [b, mf, ws, pa, lk, w] 6
-
--- | Extraction of instantaneous loudness in sones.
-loudness :: UGen -> UGen -> UGen -> UGen
-loudness fft smask tmask = mkOsc KR "Loudness" [fft, smask, tmask] 1
-
--- | Translate onset type string to constant UGen value.
-onsetType :: Num a => String -> a
-onsetType s =
-    let t = ["power", "magsum", "complex", "rcomplex", "phase", "wphase", "mkl"]
-    in fromIntegral (fromMaybe 3 (elemIndex s t))
-
--- | Onset detector.
-onsets :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-onsets c t o r f mg ms wt rw = mkOsc KR "Onsets" [c, t, o, r, f, mg, ms, wt, rw] 1
-
--- | Onset detector with default values for minor parameters.
-onsets' :: UGen -> UGen -> UGen -> UGen
-onsets' c t o = onsets c t o 1 0.1 10 11 1 0
-
--- | Key tracker.
-keyTrack :: UGen -> UGen -> UGen -> UGen -> UGen
-keyTrack fft kd cl _ = mkOsc KR "KeyTrack" [fft, kd, cl] 1
-
--- | Mel frequency cepstral coefficients.
-mfcc :: Int -> UGen -> UGen
-mfcc nc b = mkOsc KR "MFCC" [b, constant nc] nc
-
--- | Spectral Flatness measure.
-specFlatness :: UGen -> UGen
-specFlatness b = mkOsc KR "SpecFlatness" [b] 1
-
--- | Find a percentile of FFT magnitude spectrum.
-specPcile :: UGen -> UGen -> UGen -> UGen
-specPcile b f i = mkOsc KR "SpecPcile" [b, f, i] 1
-
--- | Spectral centroid.
-specCentroid :: UGen -> UGen
-specCentroid b = mkOsc KR "SpecCentroid" [b] 1
-
--- Local Variables:
--- truncate-lines:t
--- End:
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
@@ -4,18 +4,131 @@
 import qualified Data.Fixed as F {- base -}
 import Data.Int
 
+import Sound.SC3.UGen.Bindings.DB (mulAdd)
 import Sound.SC3.UGen.Operator
 import Sound.SC3.UGen.Type
 
+-- | Pseudo-infinite constant UGen.
+dinf :: UGen
+dinf = constant (9e8::Float)
+
+-- | True is conventionally 1.  The test to determine true is @> 0@.
+sc3_true :: Num n => n
+sc3_true = 1
+
+-- | False is conventionally 0.
+sc3_false :: Num n => n
+sc3_false = 0
+
+-- | Lifted 'not'.
+--
+-- > sc3_not sc3_true == sc3_false
+-- > sc3_not sc3_false == sc3_true
+sc3_not :: (Ord n,Num n) => n -> n
+sc3_not = sc3_bool . not . (> 0)
+
+-- | Translate 'Bool' to 'sc3_true' and 'sc3_false'.
+sc3_bool :: Num n => Bool -> n
+sc3_bool b = if b then sc3_true else sc3_false
+
+-- | Lift comparison function.
+sc3_comparison :: Num n => (n -> n -> Bool) -> n -> n -> n
+sc3_comparison f p q = sc3_bool (f p q)
+
+-- | Lifted '=='.
+sc3_eq :: (Num n, Eq n) => n -> n -> n
+sc3_eq = sc3_comparison (==)
+
+-- | Lifted '/='.
+sc3_neq :: (Num n, Eq n) => n -> n -> n
+sc3_neq = sc3_comparison (/=)
+
+-- | Lifted '<'.
+sc3_lt :: (Num n, Ord n) => n -> n -> n
+sc3_lt = sc3_comparison (<)
+
+-- | Lifted '<='.
+sc3_lte :: (Num n, Ord n) => n -> n -> n
+sc3_lte = sc3_comparison (<=)
+
+-- | Lifted '>'.
+sc3_gt :: (Num n, Ord n) => n -> n -> n
+sc3_gt = sc3_comparison (>)
+
+-- | Lifted '>='.
+sc3_gte :: (Num n, Ord n) => n -> n -> n
+sc3_gte = sc3_comparison (>=)
+
+-- | Variant of @SC3@ @roundTo@ function.
+--
+-- > let r = [0,0,0.25,0.25,0.5,0.5,0.5,0.75,0.75,1,1]
+-- > in map (`roundTo_` 0.25) [0,0.1 .. 1] == r
+roundTo_ :: (RealFrac n, Ord n) => n -> n -> n
+roundTo_ = sc3_round_to
+
+sc3_round_to :: (RealFrac n, Ord n) => n -> n -> n
+sc3_round_to a b = if b == 0 then a else sc3_floor ((a / b) + 0.5) * b
+
+sc3_idiv :: RealFrac n => n -> n -> n
+sc3_idiv a b = fromInteger (floor a `div` floor b)
+
+-- | Association table for 'Binary' to haskell function implementing operator.
+binop_hs_tbl :: (Real n,Floating n,RealFrac n,Ord n) => [(Binary,n -> n -> n)]
+binop_hs_tbl =
+    [(Add,(+))
+    ,(Sub,(-))
+    ,(FDiv,(/))
+    ,(IDiv,sc3_idiv)
+    ,(Mod,F.mod')
+    ,(EQ_,sc3_eq)
+    ,(NE,sc3_neq)
+    ,(LT_,sc3_lt)
+    ,(LE,sc3_lte)
+    ,(GT_,sc3_gt)
+    ,(GE,sc3_gte)
+    ,(Min,min)
+    ,(Max,max)
+    ,(Mul,(*))
+    ,(Pow,(**))
+    ,(Min,min)
+    ,(Max,max)
+    ,(Round,sc3_round_to)]
+
+-- | 'lookup' 'binop_hs_tbl' via 'toEnum'.
+binop_special_hs :: (Real n,RealFrac n,Floating n, Ord n) => Int -> Maybe (n -> n -> n)
+binop_special_hs z = lookup (toEnum z) binop_hs_tbl
+
+-- | Association table for 'Unary' to haskell function implementing operator.
+uop_hs_tbl :: (RealFrac n,Floating n,Ord n) => [(Unary,n -> n)]
+uop_hs_tbl =
+    [(Neg,negate)
+    ,(Not,\z -> if z > 0 then 0 else 1)
+    ,(Abs,abs)
+    ,(Ceil,sc3_ceiling)
+    ,(Floor,sc3_floor)
+    ,(Squared,squared')
+    ,(Cubed,cubed')
+    ,(Sqrt,sqrt)
+    ,(Recip,recip)
+    ,(MIDICPS,midiCPS')
+    ,(CPSMIDI,cpsMIDI')
+    ,(Sin,sin)
+    ,(Cos,cos)
+    ,(Tan,tan)]
+
+-- | 'lookup' 'uop_hs_tbl' via 'toEnum'.
+uop_special_hs :: (RealFrac n,Floating n, Ord n) => Int -> Maybe (n -> n)
+uop_special_hs z = lookup (toEnum z) uop_hs_tbl
+
 -- The Eq and Ord classes in the Prelude require Bool, hence the name
 -- mangling.  True is 1.0, False is 0.0
 
 -- | Variant on Eq class, result is of the same type as the values compared.
 class (Eq a,Num a) => EqE a where
     (==*) :: a -> a -> a
-    a ==* b = if a == b then 1 else 0
+    (==*) = sc3_eq
     (/=*) :: a -> a -> a
-    a /=* b = if a /= b then 1 else 0
+    (/=*) = sc3_neq
 
 instance EqE Int where
 instance EqE Integer where
@@ -31,13 +144,13 @@
 -- | Variant on Ord class, result is of the same type as the values compared.
 class (Ord a,Num a) => OrdE a where
     (<*) :: a -> a -> a
-    a <* b = if a < b then 1 else 0
+    (<*) = sc3_lt
     (<=*) :: a -> a -> a
-    a <=* b = if a <= b then 1 else 0
+    (<=*) = sc3_lte
     (>*) :: a -> a -> a
-    a >* b = if a > b then 1 else 0
+    (>*) = sc3_gt
     (>=*) :: a -> a -> a
-    a >=* b = if a >= b then 1 else 0
+    (>=*) = sc3_gte
 
 instance OrdE Int
 instance OrdE Integer
@@ -47,32 +160,44 @@
 instance OrdE Double
 
 instance OrdE UGen where
-    (<*) = mkBinaryOperator LT_ (<*)
-    (<=*) = mkBinaryOperator LE (<=*)
-    (>*) = mkBinaryOperator GT_ (>*)
-    (>=*) = mkBinaryOperator GE (>=*)
+    (<*) = mkBinaryOperator LT_ sc3_lt
+    (<=*) = mkBinaryOperator LE sc3_lte
+    (>*) = mkBinaryOperator GT_ sc3_gt
+    (>=*) = mkBinaryOperator GE sc3_gte
 
+sc3_properFraction :: (RealFrac t, Num t) => t -> (t,t)
+sc3_properFraction a =
+    let (p,q) = properFraction a
+    in (fromInteger p,q)
+
+sc3_truncate :: (RealFrac a, Num a) => a -> a
+sc3_truncate a = fromInteger (truncate a)
+
+sc3_round :: (RealFrac a, Num a) => a -> a
+sc3_round a = fromInteger (round a)
+
+sc3_ceiling :: (RealFrac a, Num a) => a -> a
+sc3_ceiling a = fromInteger (ceiling a)
+
+sc3_floor :: (RealFrac a, Num a) => a -> a
+sc3_floor a = fromInteger (floor a)
+
 -- | Variant of 'RealFrac' with non 'Integral' results.
 class RealFrac a => RealFracE a where
   properFractionE :: a -> (a,a)
-  properFractionE a = let (p,q) = properFraction a
-                      in (fromInteger p,q)
+  properFractionE = sc3_properFraction
   truncateE :: a -> a
-  truncateE a = fromInteger (truncate a)
+  truncateE = sc3_truncate
   roundE :: a -> a
-  roundE a = fromInteger (round a)
+  roundE = sc3_round
   ceilingE :: a -> a
-  ceilingE a = fromInteger (ceiling a)
+  ceilingE = sc3_ceiling
   floorE :: a -> a
-  floorE a = fromInteger (floor a)
+  floorE = sc3_floor
 
 instance RealFracE Float
 instance RealFracE Double
 
--- | Variant of @SC3@ @roundTo@ function.
-roundTo_ :: RealFracE a => a -> a -> a
-roundTo_ a b = if b == 0 then a else floorE (a/b + 0.5) * b
-
 -- | 'UGen' form or 'roundTo_'.
 roundTo :: UGen -> UGen -> UGen
 roundTo = mkBinaryOperator Round roundTo_
@@ -92,24 +217,52 @@
 midiCPS' :: Floating a => a -> a
 midiCPS' i = 440.0 * (2.0 ** ((i - 69.0) * (1.0 / 12.0)))
 
+-- | 'Floating' form of 'cpsMIDI'.
+cpsMIDI' :: Floating a => a -> a
+cpsMIDI' a = (logBase 2 (a * (1.0 / 440.0)) * 12.0) + 69.0
+
+cpsOct' :: Floating a => a -> a
+cpsOct' a = logBase 2 (a * (1.0 / 440.0)) + 4.75
+
+ampDb' :: Floating a => a -> a
+ampDb' a = logBase 10 a * 20
+
+dbAmp' :: Floating a => a -> a
+dbAmp' a = 10 ** (a * 0.05)
+
+cubed' :: Num a => a -> a
+cubed' a = a * a * a
+
+midiRatio' :: Floating a => a -> a
+midiRatio' a = 2.0 ** (a * (1.0 / 12.0))
+
+octCPS' :: Floating a => a -> a
+octCPS' a = 440.0 * (2.0 ** (a - 4.75))
+
+ratioMIDI' :: Floating a => a -> a
+ratioMIDI' a = 12.0 * logBase 2 a
+
+squared' :: Num a => a -> a
+squared' a = a * a
+
 -- | 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
+    ampDb = ampDb'
     asFloat :: a -> a
     asFloat = error "asFloat"
     asInt :: a -> a
     asInt = error "asInt"
     cpsMIDI :: a -> a
-    cpsMIDI a = (log2 (a * (1.0 / 440.0)) * 12.0) + 69.0
+    cpsMIDI = cpsMIDI'
     cpsOct :: a -> a
-    cpsOct a = log2 (a * (1.0 / 440.0)) + 4.75
+    cpsOct = cpsOct'
     cubed :: a -> a
-    cubed   a = a * a * a
+    cubed = cubed'
     dbAmp :: a -> a
-    dbAmp a = 10 ** (a * 0.05)
+    dbAmp = dbAmp'
     distort :: a -> a
     distort = error "distort"
     frac :: a -> a
@@ -123,21 +276,21 @@
     midiCPS :: a -> a
     midiCPS = midiCPS'
     midiRatio :: a -> a
-    midiRatio a = 2.0 ** (a * (1.0 / 12.0))
+    midiRatio = midiRatio'
     notE :: a -> a
-    notE a = if a >  0.0 then 0.0 else 1.0
+    notE a = if a > 0.0 then 0.0 else 1.0
     notNil :: a -> a
     notNil a = if a /= 0.0 then 0.0 else 1.0
     octCPS :: a -> a
-    octCPS a = 440.0 * (2.0 ** (a - 4.75))
+    octCPS = octCPS'
     ramp_ :: a -> a
     ramp_ _ = error "ramp_"
     ratioMIDI :: a -> a
-    ratioMIDI a = 12.0 * log2 a
+    ratioMIDI = ratioMIDI'
     softClip :: a -> a
     softClip = error "softClip"
     squared :: a -> a
-    squared a = a * a
+    squared = squared'
 
 instance UnaryOp Float where
 instance UnaryOp Double where
@@ -160,13 +313,19 @@
     notE = mkUnaryOperator Not notE
     notNil = mkUnaryOperator NotNil notNil
     octCPS = mkUnaryOperator OctCPS octCPS
-    ramp_ = mkUnaryOperator Ramp ramp_
+    ramp_ = mkUnaryOperator Ramp_ ramp_
     ratioMIDI = mkUnaryOperator RatioMIDI ratioMIDI
     softClip = mkUnaryOperator SoftClip softClip
     squared = mkUnaryOperator Squared squared
 
+difSqr' :: Num a => a -> a -> a
+difSqr' a b = (a * a) - (b * b)
+
+hypotx' :: (Ord a, Floating a) => a -> a -> a
+hypotx' x y = abs x + abs y - ((sqrt 2 - 1) * min (abs x) (abs y))
+
 -- | Binary operator class.
-class (Floating a, Ord a) => BinaryOp a where
+class (Floating a,RealFrac a, Ord a) => BinaryOp a where
     absDif :: a -> a -> a
     absDif a b = abs (a - b)
     amClip :: a -> a -> a
@@ -176,7 +335,7 @@
     clip2 :: a -> a -> a
     clip2 a b = clip_ a (-b) b
     difSqr :: a -> a -> a
-    difSqr a b = (a*a) - (b*b)
+    difSqr = difSqr'
     excess :: a -> a -> a
     excess a b = a - clip_ a (-b) b
     exprandRange :: a -> a -> a
@@ -190,11 +349,11 @@
     gcdE :: a -> a -> a
     gcdE = error "gcdE"
     hypot :: a -> a -> a
-    hypot = error "hypot"
+    hypot x y = sqrt (x * x + y * y)
     hypotx :: a -> a -> a
-    hypotx = error "hypotx"
+    hypotx = hypotx'
     iDiv :: a -> a -> a
-    iDiv = error "iDiv"
+    iDiv = sc3_idiv
     lcmE :: a -> a -> a
     lcmE = error "lcmE"
     modE :: a -> a -> a
@@ -226,27 +385,27 @@
     wrap2 :: a -> a -> a
     wrap2 = error "wrap2"
 
--- | The SC3 @%@ operator is libc fmod function.
+-- | The SC3 @%@ operator is the 'F.mod'' 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 % 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.5 `fmod_f32` 1.2 -- ~= 0.3
+-- > (-1.5) `fmod_f32` 1.2 -- ~= 0.9
+-- > 1.5 `fmod_f32` (-1.2) -- ~= -0.9
+-- > (-1.5) `fmod_f32` (-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 % 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
+-- > 1.2 `fmod_f32` 1.5 -- ~= 1.2
+-- > (-1.2) `fmod_f32` 1.5 -- ~= 0.3
+-- > 1.2 `fmod_f32` (-1.5) -- ~= -0.3
+-- > (-1.2) `fmod_f32` (-1.5) -- ~= -1.2
 fmod_f32 :: Float -> Float -> Float
 fmod_f32 = F.mod'
 
@@ -293,6 +452,15 @@
     randRange = mkBinaryOperator RandRange randRange
     exprandRange = mkBinaryOperator ExpRandRange exprandRange
 
+-- | Ternary operator class.
+class Num a => TernaryOp a where
+    mul_add :: a -> a -> a -> a
+    mul_add i m a = i * m + a
+
+instance TernaryOp UGen where mul_add = mulAdd
+instance TernaryOp Float where
+instance TernaryOp Double where
+
 -- | Wrap /k/ to within range /(i,j)/, ie. @AbstractFunction.wrap@.
 --
 -- > > [5,6].wrap(0,5) == [5,0]
@@ -353,3 +521,54 @@
 -- | Variant of 'clip'' with @SC3@ argument ordering.
 clip_ :: (Ord a) => a -> a -> a -> a
 clip_ n i j = clip' i j n
+
+hypot_ :: (Floating a) => a -> a -> a
+hypot_ x y = sqrt (x * x + y * y)
+
+-- | Calculate multiplier and add values for 'linLin' transform.
+--
+-- > range_muladd 3 4 == (0.5,3.5)
+-- > linLin_muladd (-1) 1 3 4 == (0.5,3.5)
+-- > linLin_muladd 0 1 3 4 == (1,3)
+-- > linLin_muladd (-1) 1 0 1 == (0.5,0.5)
+linLin_muladd :: Fractional t => t -> t -> t -> t -> (t, t)
+linLin_muladd sl sr dl dr =
+    let m = (dr - dl) / (sr - sl)
+        a = dl - (m * sl)
+    in (m,a)
+
+-- | Map from one linear range to another linear range.
+linlin :: (Fractional a,TernaryOp a) => a -> a -> a -> a -> a -> a
+linlin i sl sr dl dr = let (m,a) = linLin_muladd sl sr dl dr in mul_add i m a
+
+-- | Variant without 'TernaryOp' constraint.
+linlin' :: Fractional a => a -> a -> a -> a -> a -> a
+linlin' i sl sr dl dr = let (m,a) = linLin_muladd sl sr dl dr in i * m + a
+
+-- | 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 a,TernaryOp a) => a -> a -> a -> a
+urange l r i = let m = r - l in mul_add i m l
+
+-- | Variant without 'TernaryOp' constraint.
+urange' :: Fractional a => a -> a -> a -> a
+urange' l r i = let m = r - l in i * m + l
+
+-- | Calculate multiplier and add values for 'range' transform.
+--
+-- > range_muladd 3 4 == (0.5,3.5)
+range_muladd :: Fractional t => t -> t -> (t, t)
+range_muladd = linLin_muladd (-1) 1
+
+-- | Scale bi-polar (-1,1) input to linear (l,r) range.  Note that the
+-- argument order is not the same as 'linLin'.
+--
+-- > map (range 3 4) [-1,0,1] == [3,3.5,4]
+-- > map (\x -> let (m,a) = linLin_muladd (-1) 1 3 4 in x * m + a) [-1,0,1]
+range :: (Fractional a,TernaryOp a) => a -> a -> a -> a
+range l r i = let (m,a) = range_muladd l r in mul_add i m a
+
+-- | Variant without 'TernaryOp' constraint.
+range' :: Fractional a => a -> a -> a -> a
+range' l r i = let (m,a) = range_muladd l r in i * m + a
diff --git a/Sound/SC3/UGen/Monad.hs b/Sound/SC3/UGen/Monad.hs
deleted file mode 100644
--- a/Sound/SC3/UGen/Monad.hs
+++ /dev/null
@@ -1,15 +0,0 @@
--- | 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
--- a/Sound/SC3/UGen/Name.hs
+++ b/Sound/SC3/UGen/Name.hs
@@ -1,17 +1,35 @@
--- | Functions to normalise UGen names.
+-- | Functions to normalise UGen names.  @SC3@ UGen names are
+-- capitalised, @hsc3@ cannot use the same names for UGen constructor
+-- functions.  The functions here are heuristics, and are likely only
+-- partial.
 module Sound.SC3.UGen.Name where
 
-import Data.Char
+import Data.Char {- base -}
+import Data.List.Split {- split -}
 
+import Sound.SC3.Common
+import Sound.SC3.UGen.Rate {- hsc3 -}
+
 -- | 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"]
+-- > map toSC3Name ["bpf","fft","tpv","out","in'"]
 toSC3Name :: String -> String
 toSC3Name nm =
     case nm of
+      "in'" -> "In"
+      "bpz2" -> "BPZ2"
+      "brz2" -> "BRZ2"
+      "hpz1" -> "HPZ1"
+      "ifft" -> "IFFT"
+      "lpz1" -> "LPZ1"
+      "out" -> "Out"
+      "rhpf" -> "RHPF"
+      "rlpf" -> "RLPF"
+      'l':'f':'d':nm' -> "LFD" ++ nm'
+      'l':'p':'z':nm' -> "LPZ" ++ nm'
       'l':'f':nm' -> "LF" ++ nm'
       'p':'v':'_':nm' -> "PV_" ++ nm'
       p:q -> if all isLower nm && length nm <= 3
@@ -25,13 +43,56 @@
 -- > in map fromSC3Name nm == ["sinOsc","lfSaw","pv_Copy"]
 --
 -- > map fromSC3Name ["BPF","FFT","TPV"] == ["bpf","fft","tpv"]
+--
+-- > map fromSC3Name (words "HPZ1 RLPF")
 fromSC3Name :: String -> String
 fromSC3Name nm =
     case nm of
-      'L':'F':nm' -> "lf"++nm'
-      'P':'V':'_':nm' -> "pv_"++nm'
+      "In" -> "in'"
+      "BPZ2" -> "bpz2"
+      "BRZ2" -> "brz2"
+      "HPZ1" -> "hpz1"
+      "IFFT" -> "ifft"
+      "LPZ1" -> "lpz1"
+      "RHPF" -> "rhpf"
+      "RLPF" -> "rlpf"
+      'L':'F':'D':nm' -> "lfd" ++ nm'
+      'l':'p':'z':nm' -> "lpz" ++ nm'
+      '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
       [] -> []
 
+-- | Find SC3 name edges.
+sc3_name_edges :: String -> [Bool]
+sc3_name_edges =
+    let f t = case t of
+                (Nothing,_,_) -> False
+                (Just p,q,Just r) ->
+                    (isLower p && isUpper q) ||
+                    (isUpper p && isUpper q && isLower r &&
+                     (not (p == 'U' && q == 'G' && r == 'e')))
+                (Just p,q,Nothing) -> isLower p && isUpper q
+    in map f . pcn_triples
+
+-- | Convert from SC3 name to Lisp style name.
+--
+-- > let {s = words "SinOsc LFSaw FFT PV_Add AllpassN BHiPass BinaryOpUGen HPZ1 RLPF TGrains"
+-- >     ;l = words "sin-osc lf-saw fft pv-add allpass-n b-hi-pass binary-op-ugen hpz1 rlpf t-grains"}
+-- > in map sc3_name_to_lisp_name s == l
+sc3_name_to_lisp_name :: String -> String
+sc3_name_to_lisp_name s =
+    let f (c,e) = if e then ['-',c] else if c == '_' then "-" else [c]
+    in concatMap f (zip (map toLower s) (sc3_name_edges s))
+
+-- | SC3 UGen /names/ are given with rate suffixes if oscillators, without if filters.
+--
+-- > map sc3_ugen_name_sep (words "SinOsc.ar LPF *")
+sc3_ugen_name_sep :: String -> Maybe (String,Maybe Rate)
+sc3_ugen_name_sep u =
+    case splitOn "." u of
+      [nm,rt] -> Just (nm,rate_parse (map toUpper rt))
+      [nm] -> Just (nm,Nothing)
+      _ -> Nothing
diff --git a/Sound/SC3/UGen/Noise/ID.hs b/Sound/SC3/UGen/Noise/ID.hs
deleted file mode 100644
--- a/Sound/SC3/UGen/Noise/ID.hs
+++ /dev/null
@@ -1,111 +0,0 @@
--- | Non-deterministic noise 'UGen's.
-module Sound.SC3.UGen.Noise.ID where
-
-import Sound.SC3.UGen.Identifier
-import Sound.SC3.UGen.Rate
-import Sound.SC3.UGen.Type
-import Sound.SC3.UGen.UGen
-
--- | Brown noise.
-brownNoise :: ID a => a -> Rate -> UGen
-brownNoise z r = mkOscId z r "BrownNoise" [] 1
-
--- | Clip noise.
-clipNoise :: ID a => a -> Rate -> UGen
-clipNoise z r = mkOscId z r "ClipNoise" [] 1
-
--- | Randomly pass or block triggers.
-coinGate :: ID a => a -> UGen -> UGen -> UGen
-coinGate z prob i = mkFilterId z "CoinGate" [prob, i] 1
-
--- | Random impulses in (-1, 1).
-dust2 :: ID a => a -> Rate -> UGen -> UGen
-dust2 z r density = mkOscId z r "Dust2" [density] 1
-
--- | Random impulse in (0,1).
-dust :: ID a => a -> Rate -> UGen -> UGen
-dust z r density = mkOscId z r "Dust" [density] 1
-
--- | Random value in exponential distribution.
-expRand :: ID a => a -> UGen -> UGen -> UGen
-expRand z lo hi = mkOscId z IR "ExpRand" [lo, hi] 1
-
--- | Gray noise.
-grayNoise :: ID a => a -> Rate -> UGen
-grayNoise z r = mkOscId z r "GrayNoise" [] 1
-
--- | Random integer in uniform distribution.
-iRand :: ID a => a -> UGen -> UGen -> UGen
-iRand z lo hi = mkOscId z IR "IRand" [lo, hi] 1
-
--- | Clip noise.
-lfClipNoise :: ID a => a -> Rate -> UGen -> UGen
-lfClipNoise z r freq = mkOscId z r "LFClipNoise" [freq] 1
-
--- | Dynamic clip noise.
-lfdClipNoise :: ID a => a -> Rate -> UGen -> UGen
-lfdClipNoise z r freq = mkOscId z r "LFDClipNoise" [freq] 1
-
--- | Dynamic step noise.
-lfdNoise0 :: ID a => a -> Rate -> UGen -> UGen
-lfdNoise0 z r freq = mkOscId z r "LFDNoise0" [freq] 1
-
--- | Dynamic ramp noise.
-lfdNoise1 :: ID a => a -> Rate -> UGen -> UGen
-lfdNoise1 z r freq = mkOscId z r "LFDNoise1" [freq] 1
-
--- | Dynamic quadratic noise
-lfdNoise2 :: ID a => a -> Rate -> UGen -> UGen
-lfdNoise2 z r freq = mkOscId z r "LFDNoise2" [freq] 1
-
--- | Dynamic cubic noise
-lfdNoise3 :: ID a => a -> Rate -> UGen -> UGen
-lfdNoise3 z r freq = mkOscId z r "LFDNoise3" [freq] 1
-
--- | Step noise.
-lfNoise0 :: ID a => a -> Rate -> UGen -> UGen
-lfNoise0 z r freq = mkOscId z r "LFNoise0" [freq] 1
-
--- | Ramp noise.
-lfNoise1 :: ID a => a -> Rate -> UGen -> UGen
-lfNoise1 z r freq = mkOscId z r "LFNoise1" [freq] 1
-
--- | Quadratic noise.
-lfNoise2 :: ID a => a -> Rate -> UGen -> UGen
-lfNoise2 z r freq = mkOscId z r "LFNoise2" [freq] 1
-
--- | Random value in skewed linear distribution.
-linRand :: ID a => a -> UGen -> UGen -> UGen -> UGen
-linRand z lo hi m = mkOscId z IR "LinRand" [lo, hi, m] 1
-
--- | Random value in sum of n linear distribution.
-nRand :: ID a => a -> UGen -> UGen -> UGen -> UGen
-nRand z lo hi n = mkOscId z IR "NRand" [lo, hi, n] 1
-
--- | Pink noise.
-pinkNoise :: ID a => a -> Rate -> UGen
-pinkNoise z r = mkOscId z r "PinkNoise" [] 1
-
--- | Random value in uniform distribution.
-rand :: ID a => a -> UGen -> UGen -> UGen
-rand z lo hi = mkOscId z IR "Rand" [lo, hi] 1
-
--- | Random value in exponential distribution on trigger.
-tExpRand :: ID a => a -> UGen -> UGen -> UGen -> UGen
-tExpRand z lo hi trig = mkFilterId z "TExpRand" [lo, hi, trig] 1
-
--- | Random integer in uniform distribution on trigger.
-tIRand :: ID a => a -> UGen -> UGen -> UGen -> UGen
-tIRand z lo hi trig = mkFilterId z "TIRand" [lo, hi, trig] 1
-
--- | Random value in uniform distribution on trigger.
-tRand :: ID a => a -> UGen -> UGen -> UGen -> UGen
-tRand z lo hi trig = mkFilterId z "TRand" [lo, hi, trig] 1
-
--- | Triggered windex.
-tWindex :: ID a => a -> UGen -> UGen -> UGen -> UGen
-tWindex z i n a = mkFilterMCEId z "TWindex" [i, n] a 1
-
--- | White noise.
-whiteNoise :: ID a => a -> Rate -> UGen
-whiteNoise z r = mkOscId z r "WhiteNoise" [] 1
diff --git a/Sound/SC3/UGen/Noise/Monad.hs b/Sound/SC3/UGen/Noise/Monad.hs
deleted file mode 100644
--- a/Sound/SC3/UGen/Noise/Monad.hs
+++ /dev/null
@@ -1,111 +0,0 @@
--- | 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.UId
-
--- | Brown noise.
-brownNoise :: (UId m) => Rate -> m UGen
-brownNoise = liftUId N.brownNoise
-
--- | Clip noise.
-clipNoise :: (UId m) => Rate -> m UGen
-clipNoise = liftUId N.clipNoise
-
--- | Randomly pass or block triggers.
-coinGate :: (UId m) => UGen -> UGen -> m UGen
-coinGate = liftUId2 N.coinGate
-
--- | Random impulses in (-1, 1).
-dust2 :: (UId m) => Rate -> UGen -> m UGen
-dust2 = liftUId2 N.dust2
-
--- | Random impulse in (0,1).
-dust :: (UId m) => Rate -> UGen -> m UGen
-dust = liftUId2 N.dust
-
--- | Random value in exponential distribution.
-expRand :: (UId m) => UGen -> UGen -> m UGen
-expRand = liftUId2 N.expRand
-
--- | Gray noise.
-grayNoise :: (UId m) => Rate -> m UGen
-grayNoise = liftUId N.grayNoise
-
--- | Random integer in uniform distribution.
-iRand :: (UId m) => UGen -> UGen -> m UGen
-iRand = liftUId2 N.iRand
-
--- | Clip noise.
-lfClipNoise :: (UId m) => Rate -> UGen -> m UGen
-lfClipNoise = liftUId2 N.lfClipNoise
-
--- | Dynamic clip noise.
-lfdClipNoise :: (UId m) => Rate -> UGen -> m UGen
-lfdClipNoise = liftUId2 N.lfdClipNoise
-
--- | Dynamic step noise.
-lfdNoise0 :: (UId m) => Rate -> UGen -> m UGen
-lfdNoise0 = liftUId2 N.lfdNoise0
-
--- | Dynamic ramp noise.
-lfdNoise1 :: (UId m) => Rate -> UGen -> m UGen
-lfdNoise1 = liftUId2 N.lfdNoise1
-
--- | Dynamic quadratic noise
-lfdNoise2 :: (UId m) => Rate -> UGen -> m UGen
-lfdNoise2 = liftUId2 N.lfdNoise2
-
--- | Dynamic cubic noise
-lfdNoise3 :: (UId m) => Rate -> UGen -> m UGen
-lfdNoise3 = liftUId2 N.lfdNoise3
-
--- | Step noise.
-lfNoise0 :: (UId m) => Rate -> UGen -> m UGen
-lfNoise0 = liftUId2 N.lfNoise0
-
--- | Ramp noise.
-lfNoise1 :: (UId m) => Rate -> UGen -> m UGen
-lfNoise1 = liftUId2 N.lfNoise1
-
--- | Quadratic noise.
-lfNoise2 :: (UId m) => Rate -> UGen -> m UGen
-lfNoise2 = liftUId2 N.lfNoise2
-
--- | Random value in skewed linear distribution.
-linRand :: (UId m) => UGen -> UGen -> UGen -> m UGen
-linRand = liftUId3 N.linRand
-
--- | Random value in sum of n linear distribution.
-nRand :: (UId m) => UGen -> UGen -> UGen -> m UGen
-nRand = liftUId3 N.nRand
-
--- | Pink noise.
-pinkNoise :: (UId m) => Rate -> m UGen
-pinkNoise = liftUId N.pinkNoise
-
--- | Random value in uniform distribution.
-rand :: (UId m) => UGen -> UGen -> m UGen
-rand = liftUId2 N.rand
-
--- | Random value in exponential distribution on trigger.
-tExpRand :: (UId m) => UGen -> UGen -> UGen -> m UGen
-tExpRand = liftUId3 N.tExpRand
-
--- | Random integer in uniform distribution on trigger.
-tIRand :: (UId m) => UGen -> UGen -> UGen -> m UGen
-tIRand = liftUId3 N.tIRand
-
--- | Random value in uniform distribution on trigger.
-tRand :: (UId m) => UGen -> UGen -> UGen -> m UGen
-tRand = liftUId3 N.tRand
-
--- | Triggered windex.
-tWindex :: (UId m) => UGen -> UGen -> UGen -> m UGen
-tWindex = liftUId3 N.tWindex
-
--- | White noise.
-whiteNoise :: (UId m) => Rate -> m UGen
-whiteNoise = liftUId 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,9 +1,13 @@
--- | Enumerations of the unary and binary math unit generators.
+-- | Enumerations of the unary and binary math unit generators.  Names
+-- that conflict with existing names have a @_@ suffix.
 module Sound.SC3.UGen.Operator where
 
-import Data.Maybe
-import Data.List
+import Data.Maybe {- base -}
 
+import Sound.SC3.Common
+
+-- * Unary
+
 -- | Enumeration of @SC3@ unary operator UGens.
 data Unary  = Neg
             | Not
@@ -42,9 +46,9 @@
             | SinH
             | CosH
             | TanH
-            | Rand
+            | Rand_ -- UGen
             | Rand2
-            | LinRand
+            | LinRand_ -- UGen
             | BiLinRand
             | Sum3Rand
             | Distort
@@ -57,24 +61,57 @@
             | HanWindow
             | WelchWindow
             | TriWindow
-            | Ramp
+            | Ramp_ -- UGen
             | SCurve
-              deriving (Eq,Show,Enum,Read)
+              deriving (Eq,Show,Enum,Bounded,Read)
 
+-- | Type-specialised 'parse_enum'.
+parse_unary :: Case_Rule -> String -> Maybe Unary
+parse_unary cr = parse_enum cr
+
+-- | Table of symbolic names for standard unary operators.
+unaryTable :: [(Unary,String)]
+unaryTable = [] -- (Neg,"-")
+
+-- | Lookup possibly symbolic name for standard unary operators.
+unaryName :: Int -> String
+unaryName n =
+    let e = toEnum n
+    in fromMaybe (show e) (lookup e unaryTable)
+
+-- | Given name of unary operator derive index.
+--
+-- > mapMaybe (unaryIndex True) (words "NEG CUBED") == [0,13]
+-- > unaryIndex True "SinOsc" == Nothing
+unaryIndex :: Case_Rule -> String -> Maybe Int
+unaryIndex cr nm =
+    let ix = rlookup_str cr nm unaryTable
+        ix' = parse_unary cr nm
+    in fmap fromEnum (maybe ix' Just ix)
+
+-- | 'isJust' of 'unaryIndex'.
+--
+-- > map (is_unary True) (words "ABS MIDICPS NEG")
+-- > map (is_unary True) (words "- RAND")
+is_unary :: Case_Rule -> String -> Bool
+is_unary cr = isJust . unaryIndex cr
+
+-- * Binary
+
 -- | Enumeration of @SC3@ unary operator UGens.
-data Binary = Add
-            | Sub
-            | Mul
+data Binary = Add -- 0
+            | Sub -- 1
+            | Mul -- 2
             | IDiv
-            | FDiv
-            | Mod
-            | EQ_
-            | NE
-            | LT_
-            | GT_
-            | LE
-            | GE
-            | Min
+            | FDiv -- 4
+            | Mod -- 5
+            | EQ_ -- 6
+            | NE -- 7
+            | LT_ -- 8
+            | GT_ -- 9
+            | LE -- 10
+            | GE -- 11
+            | Min -- 12
             | Max
             | BitAnd
             | BitOr
@@ -87,7 +124,7 @@
             | Atan2
             | Hypot
             | Hypotx
-            | Pow
+            | Pow -- 25
             | ShiftLeft
             | ShiftRight
             | UnsignedShift
@@ -111,58 +148,61 @@
             | FirstArg
             | RandRange
             | ExpRandRange
-              deriving (Eq,Show,Enum,Read)
-
--- | Table of symbolic names for standard unary operators.
-unaryTable :: [(Int,String)]
-unaryTable = [(0,"-")]
+              deriving (Eq,Show,Enum,Bounded,Read)
 
--- | Lookup possibly symbolic name for standard unary operators.
-unaryName :: Int -> String
-unaryName n =
-    let s = show (toEnum n :: Unary)
-    in fromMaybe s (lookup n unaryTable)
+-- | Type-specialised 'parse_enum'.
+parse_binary :: Case_Rule -> String -> Maybe Binary
+parse_binary cr = parse_enum cr
 
 -- | Table of symbolic names for standard binary operators.
-binaryTable :: [(Int,String)]
+binaryTable :: [(Binary,String)]
 binaryTable =
-    [(0,"+")
-    ,(1,"-")
-    ,(2,"*")
-    ,(4,"/")
-    ,(5,"%")
-    ,(6,"==")
-    ,(7,"/=")
-    ,(8,"<")
-    ,(9,">")
-    ,(10,"<=")
-    ,(11,">=")
-    ,(25,"**")]
+    [(Add,"+")
+    ,(Sub,"-")
+    ,(Mul,"*")
+    ,(FDiv,"/")
+    ,(Mod,"%")
+    ,(EQ_,"==")
+    ,(NE,"/=")
+    ,(LT_,"<")
+    ,(GT_,">")
+    ,(LE,"<=")
+    ,(GE,">=")
+    ,(Pow,"**")]
 
 -- | Lookup possibly symbolic name for standard binary operators.
 --
--- > map binaryName [1,2,8] == ["-","*","<"]
+-- > map binaryName [1,2,8,12] == ["-","*","<","Min"]
 binaryName :: Int -> String
 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)
+    let e = toEnum n
+    in fromMaybe (show e) (lookup e binaryTable)
 
 -- | 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)
+-- > mapMaybe (binaryIndex True) (words "* MUL RING1") == [2,2,30]
+-- > binaryIndex True "SINOSC" == Nothing
+binaryIndex :: Case_Rule -> String -> Maybe Int
+binaryIndex cr nm =
+    let ix = rlookup_str cr nm binaryTable
+        ix' = parse_binary cr nm
+    in fmap fromEnum (maybe ix' Just ix)
 
--- | Given name of unary operator derive index.
+-- | 'isJust' of 'binaryIndex'.
 --
--- > map unaryIndex ["-","Neg","Cubed"] == [0,0,13]
-unaryIndex :: String -> Int
-unaryIndex nm =
-    let e = fromEnum (read nm :: Unary)
-    in fromMaybe e (rlookup nm unaryTable)
+-- > map (is_binary True) (words "== > % TRUNC MAX")
+is_binary :: Case_Rule -> String -> Bool
+is_binary cr = isJust . binaryIndex cr
+
+-- * Operator
+
+-- | Order of lookup: binary then unary.
+--
+-- > map (resolve_operator True) (words "+ - ADD SUB NEG")
+resolve_operator :: Case_Rule -> String -> (String,Maybe Int)
+resolve_operator cr nm =
+    case binaryIndex cr nm of
+      Just sp -> ("BinaryOpUGen",Just sp)
+      Nothing -> case unaryIndex cr nm of
+                   Just sp -> ("UnaryOpUGen",Just sp)
+                   _ -> (nm,Nothing)
diff --git a/Sound/SC3/UGen/Optimise.hs b/Sound/SC3/UGen/Optimise.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/UGen/Optimise.hs
@@ -0,0 +1,80 @@
+-- | Optimisations of UGen graphs.
+module Sound.SC3.UGen.Optimise where
+
+import System.Random {- random -}
+
+import Sound.SC3.UGen.Math
+import Sound.SC3.UGen.Rate
+import Sound.SC3.UGen.Type
+import Sound.SC3.UGen.UGen
+
+-- | Constant form of 'rand' UGen.
+c_rand :: Random a => Int -> a -> a -> a
+c_rand z l r = fst (randomR (l,r) (mkStdGen z))
+
+-- | Constant form of 'iRand' UGen.
+c_irand :: (Num b, RealFrac a, Random a) => Int -> a -> a -> b
+c_irand z l r = fromInteger (round (c_rand z l r))
+
+-- | Optimise 'UGen' graph by re-writing 'rand' and 'iRand' UGens that
+-- have 'Constant' inputs.  This, of course, changes the nature of the
+-- graph, it is no longer randomised at the server.  It's a useful
+-- transformation for very large graphs which are being constructed
+-- and sent each time the graph is played.
+--
+-- > import Sound.SC3.UGen.Dot
+--
+-- > let u = sinOsc AR (rand 'a' 220 440) 0 * 0.1
+-- > in draw (u + ugen_optimise_ir_rand u)
+ugen_optimise_ir_rand :: UGen -> UGen
+ugen_optimise_ir_rand =
+    let f u =
+            case u of
+              Primitive_U p ->
+                  case p of
+                    Primitive IR "Rand" [Constant_U (Constant l),Constant_U (Constant r)] [IR] _ (UId z) ->
+                        Constant_U (Constant (c_rand z l r))
+                    Primitive IR "IRand" [Constant_U (Constant l),Constant_U (Constant r)] [IR] _ (UId z) ->
+                        Constant_U (Constant (c_irand z l r))
+                    _ -> u
+              _ -> u
+    in ugenTraverse f
+
+-- | Optimise 'UGen' graph by re-writing binary operators with
+-- 'Constant' inputs.  The standard graph constructors already do
+-- this, however subsequent optimisations, ie. 'ugen_optimise_ir_rand'
+-- can re-introduce these sub-graphs, and the /Plain/ graph
+-- constructors are un-optimised.
+--
+-- > let u = constant
+-- > u 5 * u 10 == u 50
+-- > u 5 ==* u 5 == u 1
+-- > u 5 >* u 4 == u 1
+-- > u 5 <=* u 5 == u 1
+-- > abs (u (-1)) == u 1
+-- > u 5 / u 2 == u 2.5
+--
+-- > let {u = lfPulse AR (2 ** rand 'α' (-9) 1) 0 0.5
+-- >     ;u' = ugen_optimise_ir_rand u}
+-- > in draw (mix (mce [u,u',ugen_optimise_const_operator u']))
+ugen_optimise_const_operator :: UGen -> UGen
+ugen_optimise_const_operator =
+    let f u =
+            case u of
+              Primitive_U p ->
+                  case p of
+                    Primitive _ "BinaryOpUGen" [Constant_U (Constant l),Constant_U (Constant r)] [_] (Special z) _ ->
+                        case binop_special_hs z of
+                          Just fn -> Constant_U (Constant (fn l r))
+                          _ -> u
+                    Primitive _ "UnaryOpUGen" [Constant_U (Constant i)] [_] (Special z) _ ->
+                        case uop_special_hs z of
+                          Just fn -> Constant_U (Constant (fn i))
+                          _ -> u
+                    _ -> u
+              _ -> u
+    in ugenTraverse f
+
+constant_opt :: UGen -> Maybe Sample
+constant_opt = u_constant . ugen_optimise_ir_rand
+
diff --git a/Sound/SC3/UGen/Oscillator.hs b/Sound/SC3/UGen/Oscillator.hs
deleted file mode 100644
--- a/Sound/SC3/UGen/Oscillator.hs
+++ /dev/null
@@ -1,119 +0,0 @@
--- | Oscillators.
-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.
-blip :: Rate -> UGen -> UGen -> UGen
-blip r freq nharm = mkOscR [AR,KR] r "Blip" [freq, nharm] 1
-
--- | Chorusing wavetable oscillator.
-cOsc :: Rate -> UGen -> UGen -> UGen -> UGen
-cOsc r n f b = mkOsc r "COsc" [n,f,b] 1
-
--- | Create a constant amplitude signal.
-dc :: Rate -> UGen -> UGen
-dc r k = mkOsc r "DC" [k] 1
-
--- | Formant oscillator.
-formant :: Rate -> UGen -> UGen -> UGen -> UGen
-formant r f0 f bw = mkOscR [AR] r "Formant" [f0, f, bw] 1
-
--- | Fast sine wave oscillator implemented using a ringing filter.
-fSinOsc :: Rate -> UGen -> UGen -> UGen
-fSinOsc r freq phase = mkOsc r "FSinOsc" [freq, phase] 1
-
--- | Dynamic stochastic synthesis generator conceived by Iannis Xenakis.
-gendy1 :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-gendy1 r ampDist durDist adParam ddParam minFreq maxFreq ampScale durScale initCPs kNum = mkOsc r "Gendy1" [ampDist, durDist, adParam, ddParam, minFreq, maxFreq, ampScale, durScale, initCPs, kNum] 1
-
--- | Impulse oscillator (non band limited).
-impulse :: Rate -> UGen -> UGen -> UGen
-impulse r freq phase = mkOsc r "Impulse" [freq, phase] 1
-
--- | Bank of fixed oscillators.
-klang :: Rate -> UGen -> UGen -> UGen -> UGen
-klang r fs fo a =
-    if r == AR
-    then mkOscMCE r "Klang" [fs, fo] a 1
-    else error "klang: not AR"
-
--- | Format frequency, amplitude and phase data as required for klang.
-klangSpec :: [UGen] -> [UGen] -> [UGen] -> UGen
-klangSpec f a p = mce ((concat . transpose) [f, a, p])
-
--- | Variant of 'klangSpec' for non-UGen inputs.
-klangSpec' :: Real n => [n] -> [n] -> [n] -> UGen
-klangSpec' f a p =
-    let u = map constant
-    in klangSpec (u f) (u a) (u p)
-
--- | 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
-
--- | Pulse oscillator (non band limited).
-lfPulse :: Rate -> UGen -> UGen -> UGen -> UGen
-lfPulse r freq iphase width = mkOsc r "LFPulse" [freq, iphase, width] 1
-
--- | Sawtooth oscillator (non band limited).
-lfSaw :: Rate -> UGen -> UGen -> UGen
-lfSaw r freq phase = mkOsc r "LFSaw" [freq, phase] 1
-
--- | Sawtooth oscillator (non band limited).
-lfTri :: Rate -> UGen -> UGen -> UGen
-lfTri r freq phase = mkOsc r "LFTri" [freq, phase] 1
-
--- | Triggered linear ramp between two levels.
-phasor :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-phasor r t f s e p = mkOsc r "Phasor" [t, f, s, e, p] 1
-
--- | Band limited pulse wave.
-pulse :: Rate -> UGen -> UGen -> UGen
-pulse rate freq width = mkOscR [AR,KR] rate "Pulse" [freq,width] 1
-
--- | Sawtooth oscillator (band limited).
-saw :: Rate -> UGen -> UGen
-saw r freq = mkOscR [AR,KR] r "Saw" [freq] 1
-
--- | 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
-
--- | 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
--- End:
diff --git a/Sound/SC3/UGen/PP.hs b/Sound/SC3/UGen/PP.hs
new file mode 100644
--- /dev/null
+++ b/Sound/SC3/UGen/PP.hs
@@ -0,0 +1,40 @@
+module Sound.SC3.UGen.PP where
+
+import Data.List {- split -}
+import Data.Ratio {- base -}
+import Numeric {- base -}
+
+import Sound.SC3.UGen.MCE
+import Sound.SC3.UGen.Type
+import Sound.SC3.UGen.UGen
+
+-- | The default show is odd, 0.05 shows as 5.0e-2.
+double_pp :: Int -> Double -> String
+double_pp k n =
+    let f = reverse . dropWhile (== '0') . reverse
+    in f (showFFloat (Just k) n "")
+
+-- | Print as integer if integral, else as real.
+real_pp :: Double -> String
+real_pp n =
+    let r = toRational n
+    in if denominator r == 1 then show (numerator r) else double_pp 5 n
+
+bracketed :: (a,a) -> [a] -> [a]
+bracketed (l,r) x = l : x ++ [r]
+
+-- | Print constants and labels directly, primitives as un-adorned
+-- names, mce as [p,q], mrg as p&q, contols as nm=def and proxies as
+-- u@n.
+ugen_concise_pp :: UGen -> String
+ugen_concise_pp u =
+    let prim_pp (Primitive _ nm _ _ sp _) = ugen_user_name nm sp
+    in case u of
+         Constant_U (Constant n) -> real_pp n
+         Control_U (Control _ _ nm def _ _) -> nm ++ "=" ++ real_pp def
+         Label_U (Label s) -> bracketed ('"','"') s
+         Primitive_U p -> prim_pp p
+         Proxy_U (Proxy p n) -> prim_pp p ++ "@" ++ show n
+         MCE_U (MCE_Unit u') -> ugen_concise_pp u'
+         MCE_U (MCE_Vector v) -> bracketed ('[',']') (intercalate "," (map ugen_concise_pp v))
+         MRG_U (MRG l r) -> unwords [ugen_concise_pp l,"&",ugen_concise_pp r]
diff --git a/Sound/SC3/UGen/Panner.hs b/Sound/SC3/UGen/Panner.hs
deleted file mode 100644
--- a/Sound/SC3/UGen/Panner.hs
+++ /dev/null
@@ -1,53 +0,0 @@
--- | 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.
-pan2 :: UGen -> UGen -> UGen -> UGen
-pan2 i x level = mkFilter "Pan2" [i, x, level] 2
-
--- | Two channel linear pan.
-linPan2 :: UGen -> UGen -> UGen -> UGen
-linPan2 i x level = mkFilter "LinPan2" [i, x, level] 2
-
--- | Four channel equal power panner.
-pan4 :: UGen -> UGen -> UGen -> UGen -> UGen
-pan4 i x y level = mkFilter "Pan4" [i, x, y, level] 4
-
--- | Stereo signal balancer.
-balance2 :: UGen -> UGen -> UGen -> UGen -> UGen
-balance2 l r p level = mkFilter "Balance2" [l, r, p, level] 2
-
--- | Rotate a sound field.
-rotate2 :: UGen -> UGen -> UGen -> UGen
-rotate2 x y pos = mkFilter "Rotate2" [x, y, pos] 2
-
--- | Ambisonic B-format panner.
-panB :: UGen -> UGen -> UGen -> UGen -> UGen
-panB i az el level = mkFilter "PanB" [i, az, el, level] 4
-
--- | 2D Ambisonic B-format panner.
-panB2 :: UGen -> UGen -> UGen -> UGen
-panB2 i az level = mkFilter "PanB2" [i, az, level] 3
-
--- | 2D Ambisonic B-format panner.
-biPanB2 :: UGen -> UGen -> UGen -> UGen -> UGen
-biPanB2 inA inB azimuth gain = mkFilter "BiPanB2" [inA, inB, azimuth, gain] 3
-
--- | 2D Ambisonic B-format decoder.
-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
-panAz nc i p l w o = mkFilter "PanAz" [i, p, l, w, o] nc
-
--- | Equal power two channel cross fade.
-xFade2 :: UGen -> UGen -> UGen -> UGen -> UGen
-xFade2 inA inB pan level = mkFilter "XFade2" [inA, inB, pan, level] 2
-
--- | Two channel linear crossfade.
-linXFade2 :: UGen -> UGen -> UGen -> UGen
-linXFade2 inA inB pan = mkFilter "LinXFade2" [inA, inB, pan] 2
diff --git a/Sound/SC3/UGen/Plain.hs b/Sound/SC3/UGen/Plain.hs
--- a/Sound/SC3/UGen/Plain.hs
+++ b/Sound/SC3/UGen/Plain.hs
@@ -1,4 +1,4 @@
--- | Plain UGen constructor functions.
+
 --
 -- > let {s = ugen "SinOsc" AR [440,0] 1
 -- >     ;m = binop "*" AR s 0.1
@@ -8,31 +8,33 @@
 -- > audition (out 0 (sinOsc AR 440 0 * 0.1))
 module Sound.SC3.UGen.Plain where
 
+import Sound.SC3.Common
 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)
+mk_plain r nm inp = mkUGen Nothing all_rates (Left r) nm inp Nothing
 
 -- | 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
+-- > uop True "NEG" AR 1
+uop :: Case_Rule -> String -> Rate -> UGen -> UGen
+uop cr nm r p =
+    case unaryIndex cr nm of
+      Just s -> mk_plain r "UnaryOpUGen" [p] 1 (Special s) NoId
+      Nothing -> error "uop"
 
 -- | 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
+-- > binop True "*" AR 1 2 == binop True "MUL" AR 1 2
+-- > binop False "*" AR (ugen "SinOsc" AR [440,0] 1) 0.1 == sinOsc AR 440 0 * 0.1
+binop :: Case_Rule -> String -> Rate -> UGen -> UGen -> UGen
+binop cr nm r p q =
+    case binaryIndex cr nm of
+      Just s -> mk_plain r "BinaryOpUGen" [p,q] 1 (Special s) NoId
+      Nothing -> error "binop"
 
 -- | Construct deterministic UGen.
 --
diff --git a/Sound/SC3/UGen/Protect.hs b/Sound/SC3/UGen/Protect.hs
--- a/Sound/SC3/UGen/Protect.hs
+++ b/Sound/SC3/UGen/Protect.hs
@@ -25,7 +25,7 @@
 -- | Add 'idHash' of /e/ to all 'Primitive_U' at /u/.
 uprotect :: ID a => a -> UGen -> UGen
 uprotect e =
-    let e' = idHash e
+    let e' = resolveID e
         f u = case u of
                 Primitive_U p -> Primitive_U (p {ugenId = atUGenId (+ e') (ugenId p)})
                 _ -> u
@@ -35,7 +35,7 @@
 -- incrementing initial identifier.
 uprotect' :: ID a => a -> [UGen] -> [UGen]
 uprotect' e =
-    let n = map (+ idHash e) [1..]
+    let n = map (+ resolveID e) [1..]
     in zipWith uprotect n
 
 -- | Make /n/ parallel instances of 'UGen' with protected identifiers.
@@ -51,7 +51,7 @@
 ucompose e xs =
     let go [] u = u
         go ((f,k):f') u = go f' (uprotect k (f u))
-    in go (zip xs [idHash e ..])
+    in go (zip xs [resolveID e ..])
 
 -- | Make /n/ sequential instances of `f' with protected Ids.
 useq :: ID a => a -> Int -> (UGen -> UGen) -> UGen -> UGen
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,11 +1,12 @@
 -- | Operating rate definitions and utilities.
 module Sound.SC3.UGen.Rate where
 
-import Data.Function
+import Data.Char {- base -}
+import Data.Function {- base -}
 
 -- | Operating rate of unit generator.
 data Rate = IR | KR | AR | DR
-            deriving (Eq, Show, Enum, Bounded)
+            deriving (Eq,Enum,Bounded,Show,Read)
 
 instance Ord Rate where
     compare = compare `on` rate_ord
@@ -21,7 +22,7 @@
       IR -> 0
       KR -> 1
       AR -> 2
-      DR -> 3
+      DR -> 3 -- ?
 
 -- | Color identifiers for each 'Rate'.
 rate_color :: Rate -> String
@@ -35,3 +36,15 @@
 -- | Set of all 'Rate' values.
 all_rates :: [Rate]
 all_rates = [minBound .. maxBound]
+
+-- | Case insensitive parser for rate.
+--
+-- > Data.Maybe.mapMaybe rate_parse (words "ar kR IR Dr") == [AR,KR,IR,DR]
+rate_parse :: String -> Maybe Rate
+rate_parse r =
+    case map toUpper r of
+      "AR" -> Just AR
+      "KR" -> Just KR
+      "IR" -> Just IR
+      "DR" -> Just DR
+      _ -> Nothing
diff --git a/Sound/SC3/UGen/Type.hs b/Sound/SC3/UGen/Type.hs
--- a/Sound/SC3/UGen/Type.hs
+++ b/Sound/SC3/UGen/Type.hs
@@ -4,9 +4,10 @@
 import Data.Bits {- base -}
 import Data.List {- base -}
 import Data.Maybe {- base -}
+import Safe {- safe -}
 import System.Random {- random -}
+import qualified Text.Read as R {- base -}
 
-import Sound.SC3.UGen.Identifier
 import Sound.SC3.UGen.MCE
 import Sound.SC3.UGen.Operator
 import Sound.SC3.UGen.Rate
@@ -17,18 +18,48 @@
 data UGenId = NoId | UId Int
               deriving (Eq,Show)
 
+-- | Alias of 'NoId', the 'UGenId' used for deterministic UGens.
+no_id :: UGenId
+no_id = NoId
+
+-- | SC3 samples are 32-bit 'Float'.  hsc3 represents data as 64-bit
+-- 'Double'.  If 'UGen' values are used more generally (ie. see
+-- hsc3-forth) 'Float' may be too imprecise, ie. for representing time
+-- stamps.
+type Sample = Double
+
 -- | Constants.
 --
 -- > Constant 3 == Constant 3
 -- > (Constant 3 > Constant 1) == True
-data Constant = Constant {constantValue :: Float}
+data Constant = Constant {constantValue :: Sample}
                 deriving (Eq,Ord,Show)
 
--- | Control inputs.
+-- | Control meta-data.
+data C_Meta n =
+    C_Meta {ctl_min :: n -- ^ Minimum
+           ,ctl_max :: n -- ^ Maximum
+           ,ctl_warp :: String -- ^ @(0,1)@ @(min,max)@ transfer function.
+           ,ctl_step :: n -- ^ The step to increment & decrement by.
+           ,ctl_units :: String -- ^ Unit of measure (ie hz, ms etc.).
+           }
+    deriving (Eq,Show)
+
+-- | 5-tuple form of 'C_Meta' data.
+type C_Meta' n = (n,n,String,n,String)
+
+-- | Lift 'C_Meta'' to 'C_Meta' allowing type coercion.
+c_meta' :: (n -> m) -> C_Meta' n -> C_Meta m
+c_meta' f (l,r,w,stp,u) = C_Meta (f l) (f r) w (f stp) u
+
+-- | Control inputs.  It is an invariant that controls with equal
+-- names within a UGen graph must be equal in all other respects.
 data Control = Control {controlOperatingRate :: Rate
+                       ,controlIndex :: Maybe Int
                        ,controlName :: String
-                       ,controlDefault :: Float
-                       ,controlTriggered :: Bool}
+                       ,controlDefault :: Sample
+                       ,controlTriggered :: Bool
+                       ,controlMeta :: Maybe (C_Meta Sample)}
                deriving (Eq,Show)
 
 -- | Labels.
@@ -71,14 +102,32 @@
           | MRG_U MRG
             deriving (Eq,Show)
 
+-- * Parser
+
+parse_constant :: String -> Maybe UGen
+parse_constant s =
+    let d :: Maybe Double
+        d = R.readMaybe s
+    in fmap constant d
+
+-- * Accessors
+
+-- | See into 'Constant_U'.
+un_constant :: UGen -> Maybe Constant
+un_constant u =
+    case u of
+      Constant_U c -> Just c
+      _ -> Nothing
+
+-- | Value of 'Constant_U' 'Constant'.
+u_constant :: UGen -> Maybe Sample
+u_constant = fmap constantValue . un_constant
+
 -- * Predicates
 
 -- | Constant node predicate.
 isConstant :: UGen -> Bool
-isConstant u =
-    case u of
-      Constant_U _ -> True
-      _ -> False
+isConstant = isJust . un_constant
 
 -- | True if input is a sink 'UGen', ie. has no outputs.
 isSink :: UGen -> Bool
@@ -89,6 +138,16 @@
       MRG_U m -> isSink (mrgLeft m)
       _ -> False
 
+-- | See into 'Proxy_U'.
+un_proxy :: UGen -> Maybe Proxy
+un_proxy u =
+    case u of
+      Proxy_U p -> Just p
+      _ -> Nothing
+
+isProxy :: UGen -> Bool
+isProxy = isJust . un_proxy
+
 -- * Validators
 
 -- | Ensure input 'UGen' is valid, ie. not a sink.
@@ -98,21 +157,24 @@
     then error ("checkInput: " ++ show u)
     else u
 
--- * Accessors
-
--- | Value of 'Constant_U' 'Constant'.
-u_constant :: UGen -> Float
-u_constant u =
-    case u of
-      Constant_U (Constant n) -> n
-      _ -> error "u_constant"
-
 -- * Constructors
 
 -- | Constant value node constructor.
 constant :: Real n => n -> UGen
 constant = Constant_U . Constant . realToFrac
 
+-- | Type specialised 'constant'.
+int_to_ugen :: Int -> UGen
+int_to_ugen = constant
+
+-- | Type specialised 'constant'.
+float_to_ugen :: Float -> UGen
+float_to_ugen = constant
+
+-- | Type specialised 'constant'.
+double_to_ugen :: Double -> UGen
+double_to_ugen = constant
+
 -- | Multiple channel expansion node constructor.
 mce :: [UGen] -> UGen
 mce xs =
@@ -147,6 +209,7 @@
 isMCE u =
     case u of
       MCE_U _ -> True
+      MRG_U (MRG u' _) -> isMCE u'
       _ -> False
 
 -- | Output channels of UGen as a list.
@@ -189,6 +252,18 @@
       Nothing -> f i
       Just i' -> MCE_U (MCE_Vector (map (mceBuild f) i'))
 
+-- | True if MCE is an immediate proxy for a multiple-out Primitive.
+mce_is_direct_proxy :: MCE UGen -> Bool
+mce_is_direct_proxy m =
+    case m of
+      MCE_Unit _ -> False
+      MCE_Vector v ->
+          let p = map un_proxy v
+              p' = catMaybes p
+          in all isJust p &&
+             length (nub (map proxySource p')) == 1 &&
+             map proxyIndex p' `isPrefixOf` [0..]
+
 -- | Determine the rate of a UGen.
 rateOf :: UGen -> Rate
 rateOf u =
@@ -210,37 +285,42 @@
       Primitive_U p ->
           let o = ugenOutputs p
           in case o of
-               (_:_:_) -> mce (map (proxy u) [0..(length o - 1)])
+               _:_:_ -> 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 ([Float] -> Float) -> [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
+--
+-- cf = constant function, rs = rate set, r = rate, nm = name, i =
+-- inputs, o = outputs.
+mkUGen :: Maybe ([Sample] -> Sample) -> [Rate] -> Either Rate [Int] ->
+          String -> [UGen] -> Maybe UGen -> Int -> Special -> UGenId -> UGen
+mkUGen cf rs r nm i i_mce o s z =
+    let i' = maybe i ((i ++) . mceChannels) i_mce
+        f h = let r' = either id (maximum . map (rateOf . (atNote ("mkUGen: " ++ nm) 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))
+                            then constant (cf' (mapMaybe u_constant h))
                             else u
                         Nothing -> u
                  else error ("mkUGen: rate restricted: " ++ show (r,rs,nm))
-    in proxify (mceBuild f (map checkInput i))
+    in proxify (mceBuild f (map checkInput i'))
 
 -- * Operators
 
 -- | Operator UGen constructor.
-mkOperator :: ([Float] -> Float) -> String -> [UGen] -> Int -> UGen
+mkOperator :: ([Sample] -> Sample) -> String -> [UGen] -> Int -> UGen
 mkOperator f c i s =
-    mkUGen (Just f) all_rates Nothing c i 1 (Special s) NoId
+    let ix = [0 .. length i - 1]
+    in mkUGen (Just f) all_rates (Right ix) c i Nothing 1 (Special s) NoId
 
 -- | Unary math constructor with constant optimization.
-mkUnaryOperator :: Unary -> (Float -> Float) -> UGen -> UGen
+mkUnaryOperator :: Unary -> (Sample -> Sample) -> UGen -> UGen
 mkUnaryOperator i f a =
     let g [x] = f x
         g _ = error "mkUnaryOperator: non unary input"
@@ -255,8 +335,8 @@
 -- > o - 0 == o && 0 - o /= o
 -- > o / 1 == o && 1 / o /= o
 -- > o ** 1 == o && o ** 2 /= o
-mkBinaryOperator_optimize :: Binary -> (Float -> Float -> Float) ->
-                             (Either Float Float -> Bool) ->
+mkBinaryOperator_optimize :: Binary -> (Sample -> Sample -> Sample) ->
+                             (Either Sample Sample -> Bool) ->
                              UGen -> UGen -> UGen
 mkBinaryOperator_optimize i f o a b =
    let g [x,y] = f x y
@@ -270,7 +350,7 @@
    in fromMaybe (mkOperator g "BinaryOpUGen" [a, b] (fromEnum i)) r
 
 -- | Binary math constructor with constant optimization.
-mkBinaryOperator :: Binary -> (Float -> Float -> Float) ->
+mkBinaryOperator :: Binary -> (Sample -> Sample -> Sample) ->
                     UGen -> UGen -> UGen
 mkBinaryOperator i f a b =
    let g [x,y] = f x y
@@ -332,10 +412,10 @@
     toInteger _ = error "UGen.toInteger: non-constant"
 
 instance RealFrac UGen where
-  properFraction = error "UGen.properFraction"
-  round = error "UGen.round"
-  ceiling = error "UGen.ceiling"
-  floor = error "UGen.floor"
+  properFraction = error "UGen.properFraction, see properFractionE"
+  round = error "UGen.round, see roundE"
+  ceiling = error "UGen.ceiling, see ceilingE"
+  floor = error "UGen.floor, see floorE"
 
 -- | Unit generators are orderable (when 'Constants').
 --
@@ -386,8 +466,12 @@
     bit = error "UGen.bit"
     testBit = error "UGen.testBit"
     popCount = error "UGen.popCount"
+    bitSizeMaybe = error "UGen.bitSizeMaybe"
     isSigned _ = True
 
+{-
+import Sound.SC3.UGen.Identifier
+
 -- * UGen ID Instance
 
 -- | Hash function for unit generators.
@@ -396,3 +480,4 @@
 
 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
@@ -2,13 +2,19 @@
 module Sound.SC3.UGen.UGen where
 
 import qualified Data.Char as C {- base -}
+import Data.Maybe {- base -}
 import Data.List {- base -}
 
 import Sound.SC3.UGen.Identifier
+import Sound.SC3.UGen.MCE
 import Sound.SC3.UGen.Operator
 import Sound.SC3.UGen.Rate
 import Sound.SC3.UGen.Type
 
+-- | 'UId' of 'resolveID'.
+toUId :: (ID a) => a -> UGenId
+toUId = UId . resolveID
+
 -- | Lookup operator name for operator UGens, else UGen name.
 ugen_user_name :: String -> Special -> String
 ugen_user_name nm (Special n) =
@@ -54,24 +60,38 @@
 -- * Unit generator node constructors
 
 -- | Control input node constructor.
-control_f32 :: Rate -> String -> Float -> UGen
-control_f32 r nm d = Control_U (Control r nm d False)
+control_f64 :: Rate -> Maybe Int -> String -> Sample -> UGen
+control_f64 r ix nm d = Control_U (Control r ix nm d False Nothing)
 
 -- | 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.
+-- converted to a triggered control.  Please see 'tr_control'.
 control :: Rate -> String -> Double -> UGen
-control r nm = control_f32 r nm . realToFrac
+control r nm = control_f64 r Nothing nm -- . realToFrac
 
+-- | Variant of 'control' with meta data.
+meta_control :: Rate -> String -> Double -> C_Meta' Double -> UGen
+meta_control rt nm df meta =
+    let m = c_meta' id meta
+    in Control_U (Control rt Nothing nm df False (Just m))
+
 -- | Triggered (kr) control input node constructor.
-tr_control_f32 :: String -> Float -> UGen
-tr_control_f32 nm d = Control_U (Control KR nm d True)
+tr_control_f64 :: Maybe Int -> String -> Sample -> UGen
+tr_control_f64 ix nm d = Control_U (Control KR ix nm d True Nothing)
 
 -- | Triggered (kr) control input node constructor.
 tr_control :: String -> Double -> UGen
-tr_control nm = tr_control_f32 nm . realToFrac
+tr_control nm = tr_control_f64 Nothing nm -- . realToFrac
 
+-- | Set indices at a list of controls.
+control_set :: [UGen] -> [UGen]
+control_set =
+    let f ix u = case u of
+                   Control_U c -> Control_U (c {controlIndex = Just ix})
+                   _ -> error "control_set: non control input?"
+    in zipWith f [0..]
+
 -- | Multiple root graph node constructor.
 mrg2 :: UGen -> UGen -> UGen
 mrg2 u = MRG_U . MRG u
@@ -126,6 +146,37 @@
 mceSum :: UGen -> UGen
 mceSum = sum . mceChannels
 
+-- * Transform
+
+-- | Separate first list element.
+--
+-- > sep_first "astring" == Just ('a',"string")
+sep_first :: [t] -> Maybe (t,[t])
+sep_first l =
+    case l of
+      e:l' -> Just (e,l')
+      _ -> Nothing
+
+-- | Separate last list element.
+--
+-- > sep_last "stringb" == Just ("string",'b')
+sep_last :: [t] -> Maybe ([t], t)
+sep_last =
+    let f (e,l) = (reverse l,e)
+    in fmap f . sep_first . reverse
+
+-- | Given /unmce/ function make halt mce transform.
+halt_mce_transform' :: (a -> [a]) -> [a] -> [a]
+halt_mce_transform' f l =
+    let (l',e) = fromMaybe (error "halt_mce_transform: null?") (sep_last l)
+    in l' ++ f e
+
+-- | The halt MCE transform, ie. lift channels of last input into list.
+--
+-- > halt_mce_transform [1,2,mce2 3 4] == [1,2,3,4]
+halt_mce_transform :: [UGen] -> [UGen]
+halt_mce_transform = halt_mce_transform' mceChannels
+
 -- * Multiple root graphs
 
 -- * Labels
@@ -161,94 +212,6 @@
              else error (show ("unpackLabel: mce length /=",x))
       _ -> error (show ("unpackLabel: non-label",u))
 
--- * Unit generator function builders
-
--- | Oscillator constructor with constrained set of operating 'Rate's.
-mk_osc :: [Rate] -> UGenId -> Rate -> String -> [UGen] -> Int -> UGen
-mk_osc rs z r c i o =
-    if r `elem` rs
-    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 no_id
-
--- | Oscillator constructor, rate restricted variant.
-mkOscR :: [Rate] -> Rate -> String -> [UGen] -> Int -> UGen
-mkOscR rs = mk_osc rs no_id
-
-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 = mk_osc all_rates (toUId z)
-
--- | Provided 'UGenId' variant of 'mkOscMCE'.
-mk_osc_mce :: UGenId -> Rate -> String -> [UGen] -> UGen -> Int -> UGen
-mk_osc_mce z r c i j =
-    let i' = i ++ mceChannels j
-    in mk_osc all_rates z r c i'
-
--- | Variant oscillator constructor with MCE collapsing input.
-mkOscMCE :: Rate -> String -> [UGen] -> UGen -> Int -> UGen
-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 = mk_osc_mce (toUId z)
-
--- | Rate constrained filter 'UGen' constructor.
-mk_filter :: [Rate] -> UGenId -> String -> [UGen] -> Int -> UGen
-mk_filter rs z c i o = mkUGen Nothing rs Nothing c i o (Special 0) z
-
--- | Filter 'UGen' constructor.
-mkFilter :: String -> [UGen] -> Int -> UGen
-mkFilter = mk_filter all_rates no_id
-
--- | Filter UGen constructor.
-mkFilterR :: [Rate] -> String -> [UGen] -> Int -> UGen
-mkFilterR rs = mk_filter rs no_id
-
--- | Filter UGen constructor.
-mkFilterId :: (ID a) => a -> String -> [UGen] -> Int -> UGen
-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) no_id
-
--- | Provided 'UGenId' filter with 'mce' input.
-mk_filter_mce :: [Rate] -> UGenId -> String -> [UGen] -> UGen -> Int -> UGen
-mk_filter_mce rs z c i j = mk_filter rs z c (i ++ mceChannels j)
-
--- | Variant filter constructor with MCE collapsing input.
-mkFilterMCER :: [Rate] -> String -> [UGen] -> UGen -> Int -> UGen
-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 no_id
-
--- | Variant filter constructor with MCE collapsing input.
-mkFilterMCEId :: ID a => a -> String -> [UGen] -> UGen -> Int -> UGen
-mkFilterMCEId z = mk_filter_mce all_rates (toUId z)
-
--- | Information unit generators are very specialized.
-mkInfo :: String -> UGen
-mkInfo name = mkOsc IR name [] 1
-
 -- * Bitwise
 
 bitAnd :: UGen -> UGen -> UGen
@@ -277,3 +240,66 @@
 
 (.>>.) :: UGen -> UGen -> UGen
 (.>>.) = shiftRight
+
+-- * Analysis
+
+-- | UGen primitive.  Sees through Proxy and MRG, possible multiple
+-- primitives for MCE.
+ugen_primitive :: UGen -> [Primitive]
+ugen_primitive u =
+    case u of
+      Constant_U _ -> []
+      Control_U _ -> []
+      Label_U _ -> []
+      Primitive_U p -> [p]
+      Proxy_U p -> [proxySource p]
+      MCE_U m -> concatMap ugen_primitive (mce_elem m)
+      MRG_U m -> ugen_primitive (mrgLeft m)
+
+-- | Heuristic based on primitive name (@FFT@, @PV_@).  Note that
+-- @IFFT@ is at /control/ rate, not @PV@ rate.
+primitive_is_pv_rate :: String -> Bool
+primitive_is_pv_rate nm = nm == "FFT" || "PV_" `isPrefixOf` nm
+
+-- | Variant on primitive_is_pv_rate.
+ugen_is_pv_rate :: UGen -> Bool
+ugen_is_pv_rate = any (primitive_is_pv_rate . ugenName)
+                  . ugen_primitive
+
+-- | Traverse input graph until an @FFT@ or @PV_Split@ node is
+-- encountered, and then locate the buffer input.  Biases left at MCE
+-- nodes.
+--
+-- > import Sound.SC3
+-- > let z = soundIn 4
+-- > let f1 = fft 10 z 0.5 0 1 0
+-- > let f2 = ffta 'a' 1024 z 0.5 0 1 0
+-- > pv_track_buffer (pv_BrickWall f1 0.5) == Right 10
+-- > pv_track_buffer (pv_BrickWall f2 0.5) == Right (localBuf 'a' 1024 1)
+pv_track_buffer :: UGen -> Either String UGen
+pv_track_buffer u =
+    case ugen_primitive u of
+      [] -> Left "pv_track_buffer: not located"
+      p:_ -> case ugenName p of
+               "FFT" -> Right (ugenInputs p !! 0)
+               "PV_Split" -> Right (ugenInputs p !! 1)
+               _ -> pv_track_buffer (ugenInputs p !! 0)
+
+-- | Buffer node number of frames. Biases left at MCE nodes.  Sees
+-- through @LocalBuf@, otherwise uses 'bufFrames'.
+--
+-- > buffer_nframes 10 == bufFrames IR 10
+-- > buffer_nframes (control KR "b" 0) == bufFrames KR (control KR "b" 0)
+-- > buffer_nframes (localBuf 'α' 2048 1) == 2048
+buffer_nframes :: UGen -> UGen
+buffer_nframes u =
+    let b = mkUGen Nothing [IR,KR] (Left (rateOf u)) "BufFrames" [u] Nothing 1 (Special 0) NoId
+    in case ugen_primitive u of
+         [] -> b
+         p:_ -> case ugenName p of
+                  "LocalBuf" -> ugenInputs p !! 1
+                  _ -> b
+
+-- | 'pv_track_buffer' then 'buffer_nframes'.
+pv_track_nframes :: UGen -> Either String UGen
+pv_track_nframes u = pv_track_buffer u >>= Right . buffer_nframes
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
@@ -7,8 +7,11 @@
 import Control.Monad.IO.Class as M {- transformers -}
 import Control.Monad.Trans.Reader {- transformers -}
 import Data.Unique {- base -}
+
 import Sound.OSC.Transport.FD as T {- hosc -}
 
+import Sound.SC3.UGen.Type
+
 -- | A class indicating a monad that will generate a sequence of
 --   unique integer identifiers.
 class (Functor m,Applicative m,M.MonadIO m) => UId m where
@@ -22,6 +25,8 @@
     UId (ReaderT t io) where
    generateUId = ReaderT (M.liftIO . const generateUId)
 
+-- * Lift
+
 -- | Unary function.
 type Fn1 a b = a -> b
 
@@ -57,3 +62,9 @@
 liftUId4 f a b c d = do
   n <- generateUId
   return (f n a b c d)
+
+-- * Clone
+
+-- | 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/Wavelets.hs b/Sound/SC3/UGen/Wavelets.hs
deleted file mode 100644
--- a/Sound/SC3/UGen/Wavelets.hs
+++ /dev/null
@@ -1,31 +0,0 @@
--- | Wavelet unit generators (Nick Collins).
-module Sound.SC3.UGen.Wavelets where
-
-import Sound.SC3.UGen.Rate
-import Sound.SC3.UGen.Type
-import Sound.SC3.UGen.UGen
-
--- | Forward wavelet transform.
-dwt :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-dwt buf i h wnt a wns wlt = mkOsc KR "DWT" [buf,i,h,wnt,a,wns,wlt] 1
-
--- | Inverse of 'dwt'.
-idwt :: UGen -> UGen -> UGen -> UGen -> UGen
-idwt buf wnt wns wlt = mkOsc AR "IDWT" [buf,wnt,wns,wlt] 1
-
--- | Pass wavelets above a threshold, ie. 'pv_MagAbove'.
-wt_MagAbove :: UGen -> UGen -> UGen
-wt_MagAbove buf thr = mkOsc KR "WT_MagAbove" [buf,thr] 1
-
--- | Pass wavelets with /scale/ above threshold.
-wt_FilterScale :: UGen -> UGen -> UGen
-wt_FilterScale buf wp = mkOsc KR "WT_FilterScale" [buf,wp] 1
-
--- | Pass wavelets with /time/ above threshold.
-wt_TimeWipe :: UGen -> UGen -> UGen
-wt_TimeWipe buf wp = mkOsc KR "WT_TimeWipe" [buf,wp] 1
-
--- | Product in /W/ domain, ie. 'pv_Mul'.
-wt_Mul :: UGen -> UGen -> UGen
-wt_Mul ba bb = mkOsc KR "WT_Mul" [ba,bb] 1
-
diff --git a/emacs/hsc3.el b/emacs/hsc3.el
--- a/emacs/hsc3.el
+++ b/emacs/hsc3.el
@@ -30,8 +30,9 @@
   "Remove bird literate marks and preceding comment marker"
    (replace-regexp-in-string "^[> ]* ?" "" s))
 
+;; (hsc3-uncomment "  no comment")
 (defun hsc3-uncomment (s)
-  "Remove initial comment and Bird-literate markers if present"
+  "Remove initial comment and Bird-literate markers if present."
    (replace-regexp-in-string "^[- ]*[> ]*" "" s))
 
 (defun hsc3-remove-non-literates (s)
@@ -46,7 +47,7 @@
 	(find-lisp-find-files hsc3-help-directory
 			      (concat "^"
 				      (thing-at-point 'symbol)
-				      "\\.help\\.lhs"))))
+				      "\\.help\\.l?hs$"))))
 
 (defun hsc3-sc3-ugen-help ()
   "Lookup up the UGen name at point in the SC3 help files."
@@ -63,11 +64,15 @@
    (format "Sound.SC3.Server.Help.viewServerHelp \"%s\""
            (thing-at-point 'symbol))))
 
+(defun hsc3-sc3-forth-pp () "Forth PP" (interactive)
+  (hsc3-send-string
+   (format "Sound.SC3.UGen.DB.PP.ugen_graph_forth_pp False %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\""
+   (format "Sound.SC3.UGen.DB.ugenSummary \"%s\""
            (thing-at-point 'symbol))))
 
 (defun hsc3-request-type ()
@@ -116,41 +121,45 @@
 
 (defun region-string ()
   "Get region as string (no properties)"
-  (buffer-substring-no-properties (region-beginning)
-                                  (region-end)))
+  (buffer-substring-no-properties
+   (region-beginning)
+   (region-end)))
 
 (defun hsc3-concat (l)
   (apply #'concat l))
 
 (defun hsc3-region-string ()
-  "Translate the current region into a single line (unlit, uncomment)."
-  (let* ((s (region-string))
-	 (s* (if hsc3-literate-p
-		 (hsc3-unlit (hsc3-remove-non-literates s))
-	       (hsc3-concat (mapcar 'hsc3-uncomment (split-string s "\n"))))))
-    (replace-regexp-in-string "\n" " " s*)))
+  "The current region (unlit, uncomment)."
+  (let* ((s (region-string)))
+    (if hsc3-literate-p
+        (hsc3-unlit (hsc3-remove-non-literates s))
+      (hsc3-concat (mapcar 'hsc3-uncomment (split-string s "\n"))))))
 
+(defun hsc3-region-string-one-line ()
+  "Replace newlines with spaces in `hsc3-region-string'."
+  (replace-regexp-in-string "\n" " " (hsc3-region-string)))
+
 (defun hsc3-run-multiple-lines ()
   "Send the current region to the haskell interpreter as a single line."
   (interactive)
-  (hsc3-send-string (hsc3-region-string)))
+  (hsc3-send-string (hsc3-region-string-one-line)))
 
 (defun hsc3-run-multiple-lines-sclang ()
   "Send the current region to the sclang interpreter as a single line."
   (interactive)
-  (sclang-eval-string (hsc3-region-string) t))
+  (sclang-eval-string (hsc3-region-string-one-line) t))
 
 (defun hsc3-run-consecutive-lines ()
   "Send the current region to the interpreter one line at a time."
   (interactive)
   (mapcar 'hsc3-send-string
-          (mapcar 'hsc3-unlit (split-string (region-string) "\n"))))
+          (split-string (hsc3-region-string) "\n")))
 
 (defun hsc3-run-layout-block ()
   "Variant of `hsc3-run-consecutive-lines' with ghci layout quoting."
   (interactive)
   (hsc3-send-string ":{")
-  (hsc3-run-consecutive-lines)
+  (hsc3-send-string (hsc3-region-string))
   (hsc3-send-string ":}"))
 
 (defun hsc3-run-main ()
@@ -158,6 +167,12 @@
   (interactive)
   (hsc3-send-string "main"))
 
+(defun hsc3-load-main ()
+  "Load current buffer and run main."
+  (interactive)
+  (hsc3-load-buffer)
+  (hsc3-run-main))
+
 (defun hsc3-wait ()
   "Wait for prompt after sending command."
   (interactive)
@@ -177,7 +192,7 @@
   (shell-command-on-region (point-min) (point-max) "hsc3-id-rewrite" nil t))
 
 (defun hsc3-interrupt-haskell ()
-  "Interrup haskell interpreter"
+  "Interrupt haskell interpreter"
   (interactive)
   (if (comint-check-proc inferior-haskell-buffer)
       (with-current-buffer inferior-haskell-buffer
@@ -190,10 +205,11 @@
   (hsc3-send-string "Sound.SC3.withSC3 Sound.SC3.reset"))
 
 (defun hsc3-stop ()
-  "Interrup haskell interpreter & reset scsynth"
+  "Interrupt haskell interpreter & reset scsynth"
   (interactive)
   (progn
     (hsc3-interrupt-haskell)
+    (sleep-for 0.15)
     (hsc3-reset-scsynth)))
 
 (defun hsc3-status-scsynth ()
@@ -218,6 +234,18 @@
        nil)
     (error "not at hsc3 directory?")))
 
+(defun hsc3-audition-graph ()
+  "Audition the UGen graph at point."
+  (interactive)
+  (hsc3-send-string
+   (concat "Sound.SC3.audition " (thing-at-point 'symbol))))
+
+(defun hsc3-audition-graph-m ()
+  "Audition the (monadic) UGen graph at point."
+  (interactive)
+  (hsc3-send-string
+   (concat "Sound.SC3.audition =<<" (thing-at-point 'symbol))))
+
 (defun hsc3-draw-graph ()
   "Draw the UGen graph at point."
   (interactive)
@@ -225,7 +253,7 @@
    (concat "Sound.SC3.UGen.Dot.draw " (thing-at-point 'symbol))))
 
 (defun hsc3-draw-graph-m ()
-  "Draw the UGen graph at point."
+  "Draw the (monadic) UGen graph at point."
   (interactive)
   (hsc3-send-string
    (concat "Sound.SC3.UGen.Dot.draw =<<" (thing-at-point 'symbol))))
@@ -236,6 +264,12 @@
   (let ((nm (concat (file-name-sans-extension (buffer-name)) ".dot")))
     (copy-file "/tmp/hsc3.dot" nm t)))
 
+(defun hsc3-gen-param ()
+  "Rewrite an SC3 argument list as control definitions."
+  (interactive)
+  (hsc3-send-string
+   (concat "putStrLn $ Sound.SC3.RW.PSynth.rewrite_param_list \"" (region-string) "\"")))
+
 (defun hsc3-set-prompt ()
   "Set ghci prompt to hsc3."
   (interactive)
@@ -268,13 +302,17 @@
   (define-key map [?\C-c ?\C-r] 'hsc3-run-consecutive-lines)
   (define-key map [?\C-c ?\C-f] 'hsc3-run-layout-block)
   (define-key map [?\C-c ?\C-h] 'hsc3-help)
+  (define-key map [?\C-c ?\C-a] 'hsc3-audition-graph)
+  (define-key map [?\C-c ?\M-a] 'hsc3-audition-graph-m)
   (define-key map [?\C-c ?\C-g] 'hsc3-draw-graph)
   (define-key map [?\C-c ?\M-g] 'hsc3-draw-graph-m)
   (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 ?\M-f] 'hsc3-sc3-forth-pp)
   (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)
+  (define-key map [?\C-c ?\M-m] 'hsc3-load-main)
   (define-key map [?\C-c ?\C-p] 'hsc3-status-scsynth)
   (define-key map [?\C-c ?\C-q] 'hsc3-quit-haskell)
   (define-key map [?\C-c ?\C-0] 'hsc3-quit-scsynth)
diff --git a/hsc3.cabal b/hsc3.cabal
--- a/hsc3.cabal
+++ b/hsc3.cabal
@@ -1,23 +1,19 @@
 Name:              hsc3
-Version:           0.14
+Version:           0.15
 Synopsis:          Haskell SuperCollider
 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".
+                   <http://rd.slavepianos.org/t/hsc3-texts>.
 License:           GPL
 Category:          Sound
-Copyright:         (c) Rohan Drape and others, 2006-2013
+Copyright:         (c) Rohan Drape and others, 2005-2014
 Author:            Rohan Drape
 Maintainer:        rd@slavepianos.org
 Stability:         Experimental
-Homepage:          http://rd.slavepianos.org/?t=hsc3
-Tested-With:       GHC == 7.6.1
+Homepage:          http://rd.slavepianos.org/t/hsc3
+Tested-With:       GHC == 7.8.2
 Build-Type:        Simple
 Cabal-Version:     >= 1.8
 
@@ -51,92 +47,93 @@
                    bytestring,
                    containers,
                    data-default,
+                   data-ordlist,
                    directory,
                    filepath,
-                   hosc == 0.14.*,
-                   murmur-hash,
+                   hashable,
+                   hosc == 0.15.*,
                    network,
                    process,
                    random,
+                   safe,
                    split >= 0.2,
                    transformers
   GHC-Options:     -Wall -fwarn-tabs
   Exposed-modules: Sound.SC3
+                   Sound.SC3.Common
+                   Sound.SC3.Common.Monad.Syntax
                    Sound.SC3.FD
-                   Sound.SC3.ID
-                   Sound.SC3.ID.FD
-                   Sound.SC3.Monad
-                   Sound.SC3.Monad.FD
-                   Sound.SC3.Monad.Syntax
                    Sound.SC3.Server
+                   Sound.SC3.Server.Command
                    Sound.SC3.Server.Command.Completion
-                   Sound.SC3.Server.Command.Core
-                   Sound.SC3.Server.Command.Double
-                   Sound.SC3.Server.Command.Float
-                   Sound.SC3.Server.Command.Int
+                   Sound.SC3.Server.Command.Enum
                    Sound.SC3.Server.Command.Generic
+                   Sound.SC3.Server.Command.Plain
                    Sound.SC3.Server.Enum
+                   Sound.SC3.Server.Graphdef
+                   Sound.SC3.Server.Graphdef.Graph
+                   Sound.SC3.Server.Graphdef.Read
                    Sound.SC3.Server.FD
                    Sound.SC3.Server.Help
                    Sound.SC3.Server.Monad
                    Sound.SC3.Server.NRT
-                   Sound.SC3.Server.Transport.FD
-                   Sound.SC3.Server.Transport.Monad
+                   Sound.SC3.Server.NRT.Edit
+                   Sound.SC3.Server.Recorder
                    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.Server.Transport.FD
+                   Sound.SC3.Server.Transport.Monad
                    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.Monad
-                   Sound.SC3.UGen.Demand
-                   Sound.SC3.UGen.Demand.ID
-                   Sound.SC3.UGen.Demand.Monad
-                   Sound.SC3.UGen.DiskIO
+                   Sound.SC3.UGen.Bindings
+                   Sound.SC3.UGen.Bindings.Composite
+                   Sound.SC3.UGen.Bindings.DB
+                   Sound.SC3.UGen.Bindings.HW
+                   Sound.SC3.UGen.Bindings.Monad
+                   --Sound.SC3.UGen.Bindings.HW.Analysis
+                   --Sound.SC3.UGen.Bindings.HW.Buffer
+                   --Sound.SC3.UGen.Bindings.HW.Chaos
+                   Sound.SC3.UGen.Bindings.HW.Construct
+                   --Sound.SC3.UGen.Bindings.HW.Demand
+                   --Sound.SC3.UGen.Bindings.HW.DiskIO
+                   Sound.SC3.UGen.Bindings.HW.External
+                   Sound.SC3.UGen.Bindings.HW.External.ATS
+                   Sound.SC3.UGen.Bindings.HW.External.F0
+                   Sound.SC3.UGen.Bindings.HW.External.ID
+                   Sound.SC3.UGen.Bindings.HW.External.LPC
+                   Sound.SC3.UGen.Bindings.HW.External.SC3_Plugins
+                   Sound.SC3.UGen.Bindings.HW.External.Wavelets
+                   Sound.SC3.UGen.Bindings.HW.External.Zita
+                   --Sound.SC3.UGen.Bindings.HW.FFT
+                   --Sound.SC3.UGen.Bindings.HW.Filter
+                   --Sound.SC3.UGen.Bindings.HW.Granular
+                   --Sound.SC3.UGen.Bindings.HW.IO
+                   --Sound.SC3.UGen.Bindings.HW.Information
+                   --Sound.SC3.UGen.Bindings.HW.MachineListening
+                   --Sound.SC3.UGen.Bindings.HW.Noise
+                   --Sound.SC3.UGen.Bindings.HW.Oscillator
+                   --Sound.SC3.UGen.Bindings.HW.Panner
                    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.Monad
-                   Sound.SC3.UGen.Filter
                    Sound.SC3.UGen.Graph
-                   Sound.SC3.UGen.Granular
+                   Sound.SC3.UGen.Graph.Reconstruct
+                   Sound.SC3.UGen.Graph.Transform
                    Sound.SC3.UGen.Help
+                   Sound.SC3.UGen.Help.Graph
                    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.Monad
                    Sound.SC3.UGen.Operator
-                   Sound.SC3.UGen.Oscillator
-                   Sound.SC3.UGen.Panner
+                   Sound.SC3.UGen.Optimise
                    Sound.SC3.UGen.Plain
+                   Sound.SC3.UGen.PP
                    Sound.SC3.UGen.Protect
                    Sound.SC3.UGen.Rate
                    Sound.SC3.UGen.Type
                    Sound.SC3.UGen.UGen
                    Sound.SC3.UGen.UId
-                   Sound.SC3.UGen.Wavelets
-  Other-modules:   Sound.SC3.Server.Utilities
 
 Source-Repository  head
   Type:            darcs
