diff --git a/README b/README
--- a/README
+++ b/README
@@ -3,9 +3,21 @@
 
 [supercollider3][sc3] unit generators
 
-- [rdl](?t=sc3-rdu&l=hs/Help/rdl.help.lhs) - dsp dynamic library host
+- [DustR](?t=sc3-rdu&e=md/sc3-rdu.md#dustr)
+- [PV_Split](?t=sc3-rdu&e=md/sc3-rdu.md#pv_split)
+- [*RandN](?t=sc3-rdu&e=md/sc3-rdu.md#randn)
+- [RDelayMap](?t=sc3-rdu&e=md/sc3-rdu.md#rdelaymap)
+- [RDelaySet*](?t=sc3-rdu&e=md/sc3-rdu.md#rdelayset)
+- [RDL](?t=sc3-rdu&e=md/sc3-rdu.md#rdl)
+- [RFreezer](?t=sc3-rdu&e=md/sc3-rdu.md#rfreezer)
+- [RLoopSet](?t=sc3-rdu&e=md/sc3-rdu.md#rloopset)
+- [RPlayTrace](?t=sc3-rdu&e=md/sc3-rdu.md#rplaytrace)
+- [RShufflerB](?t=sc3-rdu&e=md/sc3-rdu.md#rshufflerb)
+- [RShufflerL](?t=sc3-rdu&e=md/sc3-rdu.md#rshufflerl)
+- [RTraceRd](?t=sc3-rdu&e=md/sc3-rdu.md#rtracerd)
+- [TScramble](?t=sc3-rdu&e=md/sc3-rdu.md#tscramble)
 
-© [rohan drape][rd], 1998-2012, [gpl][gpl].
+© [rohan drape][rd], 1998-2014, [gpl][gpl].
 
 [sc3]: http://audiosynth.com/
 [rd]:  http://rd.slavepianos.org/
diff --git a/hs/Sound/SC3/UGen/External/RDU.hs b/hs/Sound/SC3/UGen/External/RDU.hs
--- a/hs/Sound/SC3/UGen/External/RDU.hs
+++ b/hs/Sound/SC3/UGen/External/RDU.hs
@@ -1,36 +1,74 @@
 -- | RDU UGen definitions.
 module Sound.SC3.UGen.External.RDU where
 
-import Sound.SC3.UGen.DB.Record
-import Sound.SC3.UGen.Rate
-import Sound.SC3.UGen.Type
-import Sound.SC3.UGen.UGen
+import Sound.SC3.UGen {- hsc3 -}
+import Sound.SC3.UGen.Bindings.DB {- hsc3 -}
+import Sound.SC3.UGen.Bindings.HW.Construct {- hsc3 -}
+import Sound.SC3.UGen.DB.Record {- hsc3-db -}
 
 std_I :: Int -> String -> Double -> I
-std_I ix nm df = I (ix,ix) nm df Nothing
+std_I _ nm df = I nm df
 
-osc_U :: String -> [Rate] -> Rate -> [I] -> Int -> String -> U
-osc_U nm rr r i nc dsc = U nm rr r Nothing i Nothing (Left nc) dsc
+-- | Input meta-data, @(min,max,warp,step,units)@.
+type I_Meta = (Double,Double,String,Double,String)
 
+-- | In cases where inputs have clear meta-data this should be stored
+-- at hsc3-db, but it isn't.
+std_I' :: Int -> String -> Double -> I_Meta -> I
+std_I' ix nm df _ = std_I ix nm df
+
+osc_U :: String -> [Rate] -> Rate -> [I] -> Int -> String -> Bool -> U
+osc_U nm rr r i nc dsc nd = (read_meta (nm,rr,r,i,nc,dsc)) {ugen_nondet = nd}
+
 dustR_dsc :: U
 dustR_dsc =
     let i = [std_I 0 "lo" 0.0001
             ,std_I 1 "hi" 1.0]
-    in osc_U "DustR" [AR] AR i 1 "Range variant of Dust"
+    in osc_U "DustR" [AR] AR i 1 "Range variant of Dust" True
 
+u_nc_input :: U -> U
+u_nc_input u = u {ugen_outputs = Nothing,ugen_nc_input = True}
+
 expRandN_dsc :: U
 expRandN_dsc =
     let i = [std_I 0 "lo" 0.0001
             ,std_I 1 "hi" 1.0]
         dsc = "Multi-channel variant of Rand"
-    in U "ExpRandN" [IR] IR Nothing i Nothing (Right 0) dsc
+    in u_nc_input (osc_U "ExpRandN" [IR] IR i (-1) dsc True)
 
+-- | Copies spectral frame (ie. PV_Copy with two outputs).
+pv_Split :: UGen -> UGen -> UGen
+pv_Split ba bb = mkOsc KR "PV_Split" [ba,bb] 2
+
+-- | Variant that unpacks the output /mce/ node.
+pv_Split' :: UGen -> UGen -> (UGen,UGen)
+pv_Split' a b =
+    case mceChannels (pv_Split a b) of
+      [p,q] -> (p,q)
+      _ -> error "pv_split"
+
+-- | A 'pv_Split' variant, like 'ffta', that allocates a 'localBuf' by
+-- tracing the input graph to locate the parent @FFT@ or @PV_Split@
+-- node.
+pv_Splita :: ID i => i -> UGen -> UGen
+pv_Splita z u =
+    case pv_track_nframes u of
+      Left err -> error err
+      Right nf -> let b = localBuf z nf 1 in pv_Split u b
+
+-- | Variant that unpacks the output /mce/ node.
+pv_splita :: ID i => i -> UGen -> (UGen,UGen)
+pv_splita z u =
+    case mceChannels (pv_Splita z u) of
+      [p,q] -> (p,q)
+      _ -> error "pv_splita"
+
 randN_dsc :: U
 randN_dsc =
     let i = [std_I 0 "lo" 0.0001
             ,std_I 1 "hi" 1.0]
         dsc = "Multi-channel variant of Rand"
-    in U "RandN" [IR] IR Nothing i Nothing (Right 0) dsc
+    in u_nc_input (osc_U "RandN" [IR] IR i (-1) dsc True)
 
 rDelayMap_dsc :: U
 rDelayMap_dsc =
@@ -38,7 +76,7 @@
             ,std_I 1 "input" 0
             ,std_I 2 "dynamic" 0
             ,std_I 3 "mapArray" 0]
-    in osc_U "RDelayMap" [AR] AR i 1 "Network of delay line maps"
+    in osc_U "RDelayMap" [AR] AR i 1 "Network of delay line maps" False
 
 -- | Network of delay line operations.
 rDelayMap :: UGen -> UGen -> UGen -> UGen -> UGen
@@ -48,7 +86,7 @@
 rDelaySet_dsc =
     let i = [std_I 0 "input" 0
             ,std_I 1 "setArray" 0]
-    in osc_U "RDelaySet" [AR] AR i 1 "Delay set (RTAlloc)"
+    in osc_U "RDelaySet" [AR] AR i 1 "Delay set (RTAlloc)" False
 
 rDelaySet :: UGen -> UGen -> UGen
 rDelaySet i s = mkFilterMCE "RDelaySet" [i] s 1
@@ -58,7 +96,7 @@
     let i = [std_I 0 "buffer" 0
             ,std_I 1 "input" 0
             ,std_I 2 "setArray" 0]
-    in osc_U "RDelaySetB" [AR] AR i 1 "Delay set (Buffer)"
+    in osc_U "RDelaySetB" [AR] AR i 1 "Delay set (Buffer)" False
 
 rDelaySetB :: UGen -> UGen -> UGen -> UGen
 rDelaySetB b i s = mkFilterMCE "RDelaySetB" [b,i] s 1
@@ -66,7 +104,7 @@
 rdl_dsc :: U
 rdl_dsc =
     let i = [std_I 0 "inputs" 0]
-    in osc_U "RDL" [AR] AR i 1 "Dynamic library host"
+    in osc_U "RDL" [AR] AR i 1 "Dynamic library host" False
 
 rdl :: Int -> UGen -> UGen
 rdl nc i = mkOscMCE AR "RDL" [] i nc
@@ -84,7 +122,7 @@
             ,std_I 8 "syncPhaseTrigger" 0
             ,std_I 9 "randomizePhaseTrigger" 0
             ,std_I 10 "numberOfLoops" 6]
-    in osc_U "RFreezer" [AR] AR i 1 "Concurrent loops at signal buffer"
+    in osc_U "RFreezer" [AR] AR i 1 "Concurrent loops at signal buffer" False
 
 rFreezer :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
 rFreezer b l r g i io ir rr ps pt nl =
@@ -117,7 +155,7 @@
          ,std_I 17 "ftableReadLocationIncrement" 0
          ,std_I 18 "readIncrementQuanta" 0
          ,std_I 19 "interOffsetTimeQuanta" 0]
- in osc_U "RShufflerB" [AR] AR i 2 "Signal shuffler (Buffer)"
+ in osc_U "RShufflerB" [AR] AR i 2 "Signal shuffler (Buffer)" False
 
 data RShufflerB a = RShufflerB {bufnum :: a
                                ,readLocationMinima :: a
@@ -151,7 +189,7 @@
     let i = [std_I 0 "in" 0
             ,std_I 1 "fragmentSize" 0.005
             ,std_I 2 "maxDelay" 0.005]
-    in osc_U "RShufflerL" [AR] AR i 1 "Signal shuffler (Linear)"
+    in osc_U "RShufflerL" [AR] AR i 1 "Signal shuffler (Linear)" False
 
 rShufflerL :: UGen -> UGen -> UGen -> UGen
 rShufflerL i fs md = mkFilterR [AR] "RShufflerL" [i,fs,md] 1
@@ -162,7 +200,7 @@
             ,std_I 1 "degree" 4
             ,std_I 2 "index" 0
             ,std_I 3 "access" 1]
-    in osc_U "RTraceRd" [AR] AR i 1 "Read trace buffer"
+    in osc_U "RTraceRd" [AR] AR i 1 "Read trace buffer" False
 
 rTraceRd :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen
 rTraceRd rt b d ix ac = mkOsc rt "RTraceRd" [b,d,ix,ac] 1
@@ -173,7 +211,7 @@
             ,std_I 1 "degree" 4
             ,std_I 2 "rate" 0
             ,std_I 3 "access" 1]
-    in osc_U "RPlayTrace" [AR] AR i 1 "Play trace buffer"
+    in osc_U "RPlayTrace" [AR] AR i 1 "Play trace buffer" False
 
 rPlayTrace :: Rate -> UGen -> UGen -> UGen -> UGen -> UGen
 rPlayTrace rt b d ix ac = mkOsc rt "RPlayTrace" [b,d,ix,ac] 1
@@ -184,7 +222,60 @@
     let i = [std_I 0 "trigger" 0
             ,std_I 1 "inputs" 0]
         s = "Scramble inputs on trigger."
-    in U "TScramble" [KR] KR Nothing i (Just 1) (Right 1) s
+    in U {ugen_name = "TScramble"
+         ,ugen_operating_rates = [KR]
+         ,ugen_default_rate = KR
+         ,ugen_inputs = i
+         ,ugen_outputs = Nothing
+         ,ugen_summary = s
+         ,ugen_std_mce = True
+         ,ugen_nc_input = False
+         ,ugen_nc_mce = Just 1
+         ,ugen_filter = Nothing
+         ,ugen_reorder = Nothing
+         ,ugen_enumerations = Nothing
+         ,ugen_nondet = True
+         ,ugen_pseudo_inputs = Nothing
+         ,ugen_fixed_rate = Just KR
+         }
+
+dustR :: ID z => z -> Rate -> UGen -> UGen -> UGen
+dustR z rt lo hi = mkOscId (toUId z) rt "DustR" [lo,hi] 1
+
+expRandN :: ID z => Int -> z -> UGen -> UGen -> UGen
+expRandN nc z l r = mkOscId (toUId z) IR "ExpRandN" [l,r] nc
+
+iRandN :: ID z => Int -> z -> UGen -> UGen -> UGen
+iRandN nc z l r = mkOscId (toUId z) IR "IRandN" [l,r] nc
+
+linRandN :: ID z => Int -> z -> UGen -> UGen -> UGen -> UGen
+linRandN nc z l r bias = mkOscId (toUId z) IR "LinRandN" [l,r,bias] nc
+
+randN :: ID z => Int -> z -> UGen -> UGen -> UGen
+randN nc z l r = mkOscId (toUId z) IR "RandN" [l,r] nc
+
+tScramble :: ID z => z -> Rate -> UGen -> UGen -> UGen
+tScramble z rt tr i = mkOscMCEId (toUId z) rt "TScramble" [tr] i (mceDegree i)
+
+-- * Monadic
+
+dustRM :: UId m => Rate -> UGen -> UGen -> m UGen
+dustRM = liftUId3 dustR
+
+expRandNM :: UId m => Int -> UGen -> UGen -> m UGen
+expRandNM nc = liftUId2 (expRandN nc)
+
+iRandNM :: UId m => Int -> UGen -> UGen -> m UGen
+iRandNM nc = liftUId2 (iRandN nc)
+
+linRandNM :: UId m => Int -> UGen -> UGen -> UGen -> m UGen
+linRandNM nc = liftUId3 (linRandN nc)
+
+randNM :: UId m => Int -> UGen -> UGen -> m UGen
+randNM nc = liftUId2 (randN nc)
+
+tScrambleM :: UId m => Rate -> UGen -> UGen -> m UGen
+tScrambleM = liftUId3 tScramble
 
 -- Local Variables:
 -- truncate-lines:t
diff --git a/hs/Sound/SC3/UGen/External/RDU/ID.hs b/hs/Sound/SC3/UGen/External/RDU/ID.hs
deleted file mode 100644
--- a/hs/Sound/SC3/UGen/External/RDU/ID.hs
+++ /dev/null
@@ -1,19 +0,0 @@
--- | RDU UGen definitions.
-module Sound.SC3.UGen.External.RDU.ID where
-
-import Sound.SC3.UGen.Identifier
-import Sound.SC3.UGen.Rate
-import Sound.SC3.UGen.Type
-import Sound.SC3.UGen.UGen
-
-dustR :: ID z => z -> Rate -> UGen -> UGen -> UGen
-dustR z rt lo hi = mkOscId z rt "DustR" [lo,hi] 1
-
-expRandN :: ID z => Int -> z -> UGen -> UGen -> UGen
-expRandN nc z l r = mkOscId z IR "ExpRandN" [l,r] nc
-
-randN :: ID z => Int -> z -> UGen -> UGen -> UGen
-randN nc z l r = mkOscId z IR "RandN" [l,r] nc
-
-tScramble :: ID z => z -> Rate -> UGen -> UGen -> UGen
-tScramble z rt tr i = mkOscMCEId z rt "TScramble" [tr] i (mceDegree i)
diff --git a/hs/Sound/SC3/UGen/External/RDU/Monad.hs b/hs/Sound/SC3/UGen/External/RDU/Monad.hs
deleted file mode 100644
--- a/hs/Sound/SC3/UGen/External/RDU/Monad.hs
+++ /dev/null
@@ -1,20 +0,0 @@
--- | RDU UGen definitions.
-module Sound.SC3.UGen.External.RDU.Monad where
-
-import Sound.SC3.UGen.Rate
-import Sound.SC3.UGen.Type
-import Sound.SC3.UGen.UId
-
-import qualified Sound.SC3.UGen.External.RDU.ID as I
-
-dustR :: UId m => Rate -> UGen -> UGen -> m UGen
-dustR = liftUId3 I.dustR
-
-expRandN :: UId m => Int -> UGen -> UGen -> m UGen
-expRandN nc = liftUId2 (I.expRandN nc)
-
-randN :: UId m => Int -> UGen -> UGen -> m UGen
-randN nc = liftUId2 (I.randN nc)
-
-tScramble :: UId m => Rate -> UGen -> UGen -> m UGen
-tScramble = liftUId3 I.tScramble
diff --git a/sc3-rdu.cabal b/sc3-rdu.cabal
--- a/sc3-rdu.cabal
+++ b/sc3-rdu.cabal
@@ -1,15 +1,15 @@
 Name:              sc3-rdu
-Version:           0.14
+Version:           0.15
 Synopsis:          Haskell bindings to sc3-rdu (sc3 rd ugens)
 Description:       Haskell bindings to sc3-rdu (sc3 rd ugens)
 License:           GPL
 Category:          Sound
-Copyright:         (c) Rohan Drape, 1998-2012
+Copyright:         (c) Rohan Drape, 1998-2014
 Author:            Rohan Drape
 Maintainer:        rd@slavepianos.org
 Stability:         Experimental
-Homepage:          http://rd.slavepianos.org/?t=sc3-rdu
-Tested-With:       GHC == 7.6.1
+Homepage:          http://rd.slavepianos.org/t/sc3-rdu
+Tested-With:       GHC == 7.8.2
 Build-Type:        Simple
 Cabal-Version:     >= 1.8
 
@@ -18,12 +18,10 @@
 Library
   hs-source-dirs:  hs
   Build-Depends:   base == 4.*,
-                   hsc3 == 0.14.*,
-                   hsc3-db == 0.14.*
+                   hsc3 == 0.15.*,
+                   hsc3-db == 0.15.*
   GHC-Options:     -Wall -fwarn-tabs
   Exposed-modules: Sound.SC3.UGen.External.RDU
-                   Sound.SC3.UGen.External.RDU.ID
-                   Sound.SC3.UGen.External.RDU.Monad
 
 Source-Repository  head
   Type:            darcs
