diff --git a/Help/Server/b_allocReadChannel.help.lhs b/Help/Server/b_allocReadChannel.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/Server/b_allocReadChannel.help.lhs
@@ -0,0 +1,13 @@
+/b_allocReadChannel     Allocate buffer space and read channels from file
+
+int    - buffer number
+string - path name of a sound file.
+int    - starting frame in file (optional. default = 0)
+int    - number of frames to read (optional. default = 0, see below)
+[int]  - source file channel indices
+bytes  - an OSC message to execute upon completion. (optional)
+
+As b_allocRead, but reads individual channels into the allocated
+buffer in the order specified.
+
+Asynchronous. Replies to sender with /done when complete.
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
@@ -2,7 +2,7 @@
 
 [
   int - new group ID
-  int - add action (0,1,2, 3 or 4 see below)
+  int - add action (enumeration)
   int - add target ID
 ] * N
 
diff --git a/Help/Server/s_new.help.lhs b/Help/Server/s_new.help.lhs
--- a/Help/Server/s_new.help.lhs
+++ b/Help/Server/s_new.help.lhs
@@ -2,7 +2,7 @@
 
 string - synth definition name
 int    - synth ID
-int    - add action (0,1,2, 3 or 4 see below)
+int    - add action (enumeration)
 int    - add target ID
 [
   int|string - a control index or name
@@ -13,11 +13,11 @@
 to the tree of nodes. There are four ways to add the node to the tree
 as determined by the add action argument which is defined as follows:
 
-0 - add the new node to the the head of the target group
-1 - add the new node to the the tail of the target group
-2 - add the new node just before the target node
-3 - add the new node just after the target node
-4 - the new node replaces the target node, which is freed
+AddToHead  - 0 - add the new node to the the head of the target group
+AddToTail  - 1 - add the new node to the the tail of the target group
+AddBefore  - 2 - add the new node just before the target node
+AddAfter   - 3 - add the new node just after the target node
+AddReplace - 4 - the new node replaces the target node, which is freed
 
 Controls may be set when creating the synth. The control arguments are
 the same as for the n_set command.
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
@@ -1,4 +1,4 @@
-playBuf numChannels bufnum rate trigger startPos loop
+playBuf numChannels bufnum rate trigger startPos loop doneAction
 
 Sample playback oscillator.  Plays back a memory resident sample.
 
diff --git a/Help/UGen/Envelope/envADSR.help.lhs b/Help/UGen/Envelope/envADSR.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/Envelope/envADSR.help.lhs
@@ -0,0 +1,20 @@
+envADSR :: UGen->UGen->UGen->UGen->UGen->EnvCurve->UGen->[UGen]
+
+Attack, decay, sustain, release envelope.  Argumets are:
+
+    aT = attackTime (0.01)
+    dT = decayTime (0.3)
+    sL = sustainLevel (0.5)
+    rT = releaseTime (1)
+    pL = peakLevel (1)
+    c = curve (-4)
+    b = bias (0)
+
+> import Sound.SC3
+
+> let { g = control KR "gate" 1
+>     ; p = envADSR 0.01 0.3 0.5 1 1 (EnvNum (-4)) 0
+>     ; e = envGen KR g 0.1 0 1 RemoveSynth p }
+> in audition (out 0 (sinOsc AR 440 0 * e))
+
+> withSC3 (\fd -> send fd (n_set1 (-1) "gate" 0))
diff --git a/Help/UGen/Envelope/envASR.help.lhs b/Help/UGen/Envelope/envASR.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/Envelope/envASR.help.lhs
@@ -0,0 +1,17 @@
+envASR :: UGen -> UGen -> UGen -> EnvCurve -> [UGen]
+
+Attack, sustain, release envelope.  Arguments are:
+
+    aT = attackTime (0.01)
+    sL = sustainLevel (1)
+    rT = releaseTime (1)
+    c = curve (-4)
+
+> import Sound.SC3
+
+> let { g = control KR "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 (\fd -> send fd (n_set1 (-1) "gate" 0))
diff --git a/Help/UGen/FFT/pv_HainsworthFoote.help.lhs b/Help/UGen/FFT/pv_HainsworthFoote.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/FFT/pv_HainsworthFoote.help.lhs
@@ -0,0 +1,43 @@
+pv_HainsworthFoote buf proph propf threshold waittime
+
+FFT onset detector based on work described in
+
+  Hainsworth, S. (2003) Techniques for the Automated Analysis of
+  Musical Audio. PhD, University of Cambridge engineering dept. See
+  especially p128.
+
+The Hainsworth metric is a modification of the Kullback Liebler
+distance.
+
+The onset detector has general ability to spot spectral change, so may
+have some ability to track chord changes aside from obvious transient
+jolts, but there's no guarantee it won't be confused by frequency
+modulation artifacts.
+
+Hainsworth metric on it's own gives good results but Foote might be
+useful in some situations: experimental.
+
+    buffer - FFT buffer to read from
+
+     proph - What strength of detection signal from
+             Hainsworth metric to use.
+
+     propf - What strength of detection signal from Foote
+             metric to use. The Foote metric is normalised
+             to [0.0,1.0]
+
+ threshold - Threshold hold level for allowing a detection
+
+  waittime - If triggered, minimum wait until a further
+             frame can cause another spot (useful to stop
+             multiple detects on heavy signals)
+
+> import Sound.SC3
+
+> 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 }
+> in audition (out 0 (o + i))
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
@@ -1,4 +1,4 @@
-grainBuf nc tr dur sndbuf rate pos interp pan envbuf
+grainBuf nc tr dur sndbuf rate pos interp pan envbuf maxgrn
 
 Granular synthesis with sound stored in a buffer
 
@@ -10,9 +10,9 @@
 
 The following args are polled at grain creation time
 
-dur - size of the grain.
+dur - size of the grain (in seconds).
 
-sndbuf - the buffer holding an audio signal
+sndbuf - the buffer holding an audio signal (must be single channel)
 
 rate - the playback rate of the sampled sound
 
@@ -29,18 +29,32 @@
 envb - the buffer number containing a signal to use for the
        grain envelope. -1 uses a built-in Hanning envelope.
 
+maxgrn - maxiumum number of grains
+
 > import Sound.SC3.Monadic
 
-> let { fn = "/home/rohan/audio/metal.wav"
->     ; b = 10
+> let fn = "/home/rohan/audio/metal.wav"
+> in withSC3 (\fd -> send fd (b_allocRead 10 fn 0 0))
+
+> let { buf = 10
+>     ; dur = 15
+>     ; 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 }
+> in audition (out 0 g)
+
+> let { b = 10
 >     ; e = -1
 >     ; x = mouseX KR (-1) 1 Linear 0.1
 >     ; y = mouseY KR 10 45 Linear 0.1
 >     ; i = impulse KR y 0
 >     ; r n = linLin n (-1) 1 0.5 2
 >     ; p n = linLin n (-1) 1 0 1
->     ; g n1 n2 = grainBuf 2 i 0.1 b (r n1) (p n2) 2 x e }
-> in withSC3 (\fd -> do { send fd (b_allocRead 10 fn 0 0)
->                       ; n1 <- lfNoise1 KR 500
+>     ; g n1 n2 = grainBuf 2 i 0.1 b (r n1) (p n2) 2 x e 512 }
+> in withSC3 (\fd -> do { n1 <- lfNoise1 KR 500
 >                       ; n2 <- lfNoise2 KR 0.1
 >                       ; play fd (out 0 (g n1 n2)) })
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
@@ -1,4 +1,4 @@
-grainFM nc tr dur carfreq modfreq index pan envbuf
+grainFM nc tr dur carfreq modfreq index pan envbuf maxgrn
 
 Granular synthesis with frequency modulated sine tones
 
@@ -28,11 +28,20 @@
 
 > import Sound.SC3.Monadic
 
+> let { d = 15
+>     ; lin a b = line KR a b d RemoveSynth
+>     ; l = lin (-0.5) 0.5
+>     ; f = lin 200 800
+>     ; t = impulse KR (lin 7.5 15) 0
+>     ; i = lin (-1) 1 }
+> in audition (out 0 (grainFM 2 t 0.1 f 200 i l (-1) 512 * 0.1))
+
 > do { n1 <- whiteNoise KR
 >    ; n2 <- lfNoise1 KR 500
->    ; let { x = mouseX KR (-0.5) 0.5 Linear 0.1
+>    ; let { d = 5
+>          ; x = mouseX KR (-0.5) 0.5 Linear 0.1
 >          ; y = mouseY KR 0 400 Linear 0.1
 >          ; f = n1 * y + 440
->          ; t = impulse KR 10 0 
+>          ; t = impulse KR 12.5 0
 >          ; i = linLin n2 (-1) 1 1 10 }
->      in audition (out 0 (grainFM 2 t 0.1 f 200 i x (-1) * 0.1)) }
+>      in audition (out 0 (grainFM 2 t 0.1 f 200 i x (-1) 512 * 0.1)) }
diff --git a/Help/UGen/Granular/grainSin.help.lhs b/Help/UGen/Granular/grainSin.help.lhs
--- a/Help/UGen/Granular/grainSin.help.lhs
+++ b/Help/UGen/Granular/grainSin.help.lhs
@@ -1,4 +1,4 @@
-grainSin nc tr dur freq pan envbuf
+grainSin nc tr dur freq pan envbuf maxgrn
 
 Granular synthesis with sine tones
 
@@ -27,4 +27,4 @@
 >          ; y = mouseY KR 0 400 Linear 0.1
 >          ; f = n * y + 440
 >          ; t = impulse KR 10 0 }
->      in audition (out 0 (grainSin 2 t 0.1 f x (-1) * 0.1)) }
+>      in audition (out 0 (grainSin 2 t 0.1 f x (-1) 512 * 0.1)) }
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
@@ -59,6 +59,8 @@
 > in withSC3 (\fd -> do { async fd (b_alloc 10 512 1)
 >                       ; play fd (out 0 (s * e)) })
 
+> audition (out 0 (soundIn 0 * 0.1))
+
 The type argument chooses which onset detection function is
 used. In many cases the default will be fine. The following
 choices are available:
diff --git a/Help/UGen/Panner/panAz.help.lhs b/Help/UGen/Panner/panAz.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/Panner/panAz.help.lhs
@@ -0,0 +1,39 @@
+panAz :: Int -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+
+Azimuth panner.  Multichannel equal power panner.
+
+numChans - number of output channels
+
+in - input signal
+
+pos - pan position. Channels are evenly spaced over a
+      cyclic period of 2.0 with 0.0 equal to the
+      position directly in front, 2.0/numChans a
+      clockwise shift 1/numChans of the way around the
+      ring, 4.0/numChans equal to a shift of
+      2/numChans, etc. Thus all channels will be
+      cyclically panned through if a sawtooth wave from
+      -1 to +1 is used to modulate the pos. N.B. Front
+      may or may not correspond to a speaker depending
+      on the setting of the orientation arg, see below.
+
+level - a control rate level input.
+
+width - The width of the panning envelope. Nominally
+        this is 2.0 which pans between pairs of
+        adjacent speakers. Width values greater than
+        two will spread the pan over greater numbers of
+        speakers. Width values less than one will leave
+        silent gaps between speakers.
+
+orientation - Should be zero if the front is a vertex
+              of the polygon. The first speaker will be
+              directly in front. Should be 0.5 if the
+              front bisects a side of the polygon. Then
+              the first speaker will be the one left of
+              center. Default is 0.5.
+
+> import Sound.SC3.ID
+
+> let n = pinkNoise 'a' AR
+> in audition (out 0 (panAz 2 n (lfSaw KR 2 0) 0.1 2 0.5))
diff --git a/Help/UGen/Trigger/sendReply.help.lhs b/Help/UGen/Trigger/sendReply.help.lhs
new file mode 100644
--- /dev/null
+++ b/Help/UGen/Trigger/sendReply.help.lhs
@@ -0,0 +1,31 @@
+sendReply in replyID cmdName values
+
+On receiving a trigger (0 to non-zero transition), send a trigger
+message from the server back to all registered clients.  Clients
+register by sending a /notify message to the server.
+
+in      - the trigger
+
+replyId - an integer that will be passed with the 
+          trigger message.  This is useful if you
+          have more than one SendReply in a SynthDef
+
+cmdName - the name of the reply command to send
+
+values  - a list of UGen or float values will be polled 
+          at the time of trigger, and returned with the 
+          trigger message
+
+> import Sound.SC3.ID
+
+> let { s0 = lfNoise0 'a' KR 5
+>     ; s1 = lfNoise0 'b' KR 5
+>     ; o = sinOsc AR (s0 * 200 + 500) 0 * s1 * 0.1 }
+> in audition (mrg [sendReply s0 0 "/s-reply" [s0, s1], out 0 o])
+
+> import Sound.OpenSoundControl
+
+> withSC3 (\fd -> do { async fd (notify True)
+>                    ; r <- wait fd "/s-reply"
+>                    ; putStrLn (show r)
+>                    ; async fd (notify False) })
diff --git a/README b/README
--- a/README
+++ b/README
@@ -13,10 +13,11 @@
   http://audiosynth.com/
   http://gnu.org/software/emacs/
 
-(c) rohan drape and others, 2006-2010
+(c) rohan drape and others, 2006-2011
     gpl, http://gnu.org/copyleft/
     with contributions by henning thielemann
                         & stefan kersten
                         & alex mclean
                         & brent yorgey
+                        & shae erisson
     see darcs history for details
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,3 @@
+#!/usr/bin/env runhaskell
+import Distribution.Simple
+main = defaultMain
diff --git a/Setup.lhs b/Setup.lhs
deleted file mode 100644
--- a/Setup.lhs
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/usr/bin/env runhaskell
-> import Distribution.Simple
-> main = defaultMain
diff --git a/Sound/SC3/Server/Command.hs b/Sound/SC3/Server/Command.hs
--- a/Sound/SC3/Server/Command.hs
+++ b/Sound/SC3/Server/Command.hs
@@ -11,76 +11,76 @@
 
 -- | Install a bytecode instrument definition. (Asynchronous)
 d_recv :: [Word8] -> OSC
-d_recv b = Message "/d_recv" [Blob b]
+d_recv b = message "/d_recv" [Blob b]
 
 -- | Load an instrument definition from a named file. (Asynchronous)
 d_load :: String -> OSC
-d_load p = Message "/d_load" [String p]
+d_load p = message "/d_load" [String p]
 
 -- | Load a directory of instrument definitions files. (Asynchronous)
 d_loadDir :: String -> OSC
-d_loadDir p = Message "/d_loadDir" [String p]
+d_loadDir p = message "/d_loadDir" [String p]
 
 -- | Remove definition once all nodes using it have ended.
 d_free :: [String] -> OSC
-d_free = Message "/d_free" . map String
+d_free = message "/d_free" . map String
 
 -- * Node commands
 
 -- | Place a node after another.
 n_after :: [(Int, Int)] -> OSC
-n_after = Message "/n_after" . mk_duples Int Int
+n_after = message "/n_after" . mk_duples Int Int
 
 -- | Place a node before another.
 n_before :: [(Int, Int)] -> OSC
-n_before = Message "/n_before" . mk_duples Int Int
+n_before = message "/n_before" . mk_duples Int Int
 
 -- | Fill ranges of a node's control values.
 n_fill :: Int -> [(String, Int, Double)] -> OSC
-n_fill nid l = Message "/n_fill" (Int nid : mk_triples String Int Float l)
+n_fill nid l = message "/n_fill" (Int nid : mk_triples String Int Float l)
 
 -- | Delete a node.
 n_free :: [Int] -> OSC
-n_free = Message "/n_free" . map Int
+n_free = message "/n_free" . map Int
 
 -- | Map a node's controls to read from a bus.
 n_map :: Int -> [(String, Int)] -> OSC
-n_map nid l = Message "/n_map" (Int nid : mk_duples String Int l)
+n_map nid l = message "/n_map" (Int nid : mk_duples String Int l)
 
 -- | Map a node's controls to read from buses.
 n_mapn :: Int -> [(String, Int, Int)] -> OSC
-n_mapn nid l = Message "/n_mapn" (Int nid : mk_triples String Int Int l)
+n_mapn nid l = message "/n_mapn" (Int nid : mk_triples String Int Int l)
 
 -- | Get info about a node.
 n_query :: [Int] -> OSC
-n_query = Message "/n_query" . map Int
+n_query = message "/n_query" . map Int
 
 -- | Turn node on or off.
 n_run :: [(Int, Bool)] -> OSC
-n_run = Message "/n_run" . mk_duples Int (Int . fromEnum)
+n_run = message "/n_run" . mk_duples Int (Int . fromEnum)
 
 -- | Set a node's control values.
 n_set :: Int -> [(String, Double)] -> OSC
-n_set nid c = Message "/n_set" (Int nid : mk_duples String Float c)
+n_set nid c = message "/n_set" (Int nid : mk_duples String Float c)
 
 -- | Set ranges of a node's control values.
 n_setn :: Int -> [(String, [Double])] -> OSC
-n_setn nid l = Message "/n_setn" (Int nid : concatMap f l)
+n_setn nid l = message "/n_setn" (Int nid : concatMap f l)
     where f (s,d) = String s : Int (length d) : map Float d
 
 -- | Trace a node.
 n_trace :: [Int] -> OSC
-n_trace = Message "/n_trace" . map Int
+n_trace = message "/n_trace" . map Int
 
 -- * Synthesis node commands
 
 -- | Get control values.
 s_get :: Int -> [String] -> OSC
-s_get nid i = Message "/s_get" (Int nid : map String i)
+s_get nid i = message "/s_get" (Int nid : map String i)
 
 -- | Get ranges of control values.
 s_getn :: Int -> [(String, Int)] -> OSC
-s_getn nid l = Message "/s_getn" (Int nid : mk_duples String Int l)
+s_getn nid l = message "/s_getn" (Int nid : mk_duples String Int l)
 
 -- | Enumeration of possible locations to add new nodes (s_new and g_new).
 data AddAction = AddToHead
@@ -92,139 +92,139 @@
 
 -- | Create a new synth.
 s_new :: String -> Int -> AddAction -> Int -> [(String, Double)] -> OSC
-s_new n i a t c = Message "/s_new" (String n : Int i : Int (fromEnum a) : Int t : mk_duples String Float c)
+s_new n i a t c = message "/s_new" (String n : Int i : Int (fromEnum a) : Int t : mk_duples String Float c)
 
 -- | Create a new synth.
 s_newargs :: String -> Int -> AddAction -> Int -> [(String, [Double])] -> OSC
-s_newargs n i a t c = Message "/s_newargs" (String n : Int i : Int (fromEnum a) : Int t : mk_duples_l Int String Float c)
+s_newargs n i a t c = message "/s_newargs" (String n : Int i : Int (fromEnum a) : Int t : mk_duples_l Int String Float c)
 
 -- | Auto-reassign synth's ID to a reserved value.
 s_noid :: [Int] -> OSC
-s_noid = Message "/s_noid" . map Int
+s_noid = message "/s_noid" . map Int
 
 -- * Group node commands
 
 -- | Free all synths in this group and all its sub-groups.
 g_deepFree :: [Int] -> OSC
-g_deepFree = Message "/g_deepFree" . map Int
+g_deepFree = message "/g_deepFree" . map Int
 
 -- | Delete all nodes in a group.
 g_freeAll :: [Int] -> OSC
-g_freeAll = Message "/g_freeAll" . map Int
+g_freeAll = message "/g_freeAll" . map Int
 
 -- | Add node to head of group.
 g_head :: [(Int, Int)] -> OSC
-g_head = Message "/g_head" . mk_duples Int Int
+g_head = message "/g_head" . mk_duples Int Int
 
 -- | Create a new group.
 g_new :: [(Int, AddAction, Int)] -> OSC
-g_new = Message "/g_new" . mk_triples Int (Int . fromEnum) Int
+g_new = message "/g_new" . mk_triples Int (Int . fromEnum) Int
 
 -- | Add node to tail of group.
 g_tail :: [(Int, Int)] -> OSC
-g_tail = Message "/g_tail" . mk_duples Int Int
+g_tail = message "/g_tail" . mk_duples Int Int
 
 -- * Unit Generator commands
 
 -- | Send a command to a unit generator.
 u_cmd :: Int -> Int -> String -> [Datum] -> OSC
-u_cmd nid uid cmd arg = Message "/u_cmd" ([Int nid, Int uid, String cmd] ++ arg)
+u_cmd nid uid cmd arg = message "/u_cmd" ([Int nid, Int uid, String cmd] ++ arg)
 
 -- * Buffer commands
 
 -- | Allocates zero filled buffer to number of channels and samples. (Asynchronous)
 b_alloc :: Int -> Int -> Int -> OSC
-b_alloc nid frames channels = Message "/b_alloc" [Int nid, Int frames, Int channels]
+b_alloc nid frames channels = message "/b_alloc" [Int nid, Int frames, Int channels]
 
 -- | Allocate buffer space and read a sound file. (Asynchronous)
 b_allocRead :: Int -> String -> Int -> Int -> OSC
-b_allocRead nid p f n = Message "/b_allocRead" [Int nid, String p, Int f, Int n]
+b_allocRead nid p f n = message "/b_allocRead" [Int nid, String p, Int f, Int n]
 
 -- | Allocate buffer space and read a sound file, picking specific channels. (Asynchronous)
 b_allocReadChannel :: Int -> String -> Int -> Int -> [Int] -> OSC
-b_allocReadChannel nid p f n cs = Message "/b_allocReadChannel" ([Int nid, String p, Int f, Int n] ++ map Int cs)
+b_allocReadChannel nid p f n cs = message "/b_allocReadChannel" ([Int nid, String p, Int f, Int n] ++ map Int cs)
 
 -- | Close attached soundfile and write header information. (Asynchronous)
 b_close :: Int -> OSC
-b_close nid = Message "/b_close" [Int nid]
+b_close nid = message "/b_close" [Int nid]
 
 -- | Fill ranges of sample values.
 b_fill :: Int -> [(Int, Int, Double)] -> OSC
-b_fill nid l = Message "/b_fill" (Int nid : mk_triples Int Int Float l)
+b_fill nid l = message "/b_fill" (Int nid : mk_triples Int Int Float l)
 
 -- | Free buffer data. (Asynchronous)
 b_free :: Int -> OSC
-b_free nid = Message "/b_free" [Int nid]
+b_free nid = message "/b_free" [Int nid]
 
 -- | Call a command to fill a buffer.  (Asynchronous)
 b_gen :: Int -> String -> [Double] -> OSC
-b_gen bid cmd arg = Message "/b_gen" (Int bid : String cmd : map Float arg)
+b_gen bid cmd arg = message "/b_gen" (Int bid : String cmd : map Float arg)
 
 -- | Get sample values.
 b_get :: Int -> [Int] -> OSC
-b_get nid i = Message "/b_get" (Int nid : map Int i)
+b_get nid i = message "/b_get" (Int nid : map Int i)
 
 -- | Get ranges of sample values.
 b_getn :: Int -> [(Int, Int)] -> OSC
-b_getn nid l = Message "/b_getn" (Int nid : mk_duples Int Int l)
+b_getn nid l = message "/b_getn" (Int nid : mk_duples Int Int l)
 
 -- | Request \/b_info messages.
 b_query :: [Int] -> OSC
-b_query = Message "/b_query" . map Int
+b_query = message "/b_query" . map Int
 
 -- | Read sound file data into an existing buffer. (Asynchronous)
 b_read :: Int -> String -> Int -> Int -> Int -> Int -> OSC
-b_read nid p f n f' z = Message "/b_read" [Int nid, String p, Int f, Int n, Int f', Int z]
+b_read nid p f n f' z = message "/b_read" [Int nid, String p, Int f, Int n, Int f', Int z]
 
 -- | Read sound file data into an existing buffer, picking specific channels. (Asynchronous)
 b_readChannel :: Int -> String -> Int -> Int -> Int -> Int -> [Int] -> OSC
-b_readChannel nid p f n f' z cs = Message "/b_readChannel" ([Int nid, String p, Int f, Int n, Int f', Int z] ++ map Int cs)
+b_readChannel nid p f n f' z cs = message "/b_readChannel" ([Int nid, String p, Int f, Int n, Int f', Int z] ++ map Int cs)
 
 -- | Set sample values.
 b_set :: Int -> [(Int, Double)] -> OSC
-b_set nid l = Message "/b_set" (Int nid : mk_duples Int Float l)
+b_set nid l = message "/b_set" (Int nid : mk_duples Int Float l)
 
 -- | Set ranges of sample values.
 b_setn :: Int -> [(Int, [Double])] -> OSC
-b_setn nid l = Message "/b_setn" (Int nid : concatMap f l)
+b_setn nid l = message "/b_setn" (Int nid : concatMap f l)
     where f (i,d) = Int i : Int (length d) : map Float d
 
 -- | Write sound file data. (Asynchronous)
 b_write :: Int -> String -> Int -> Int -> Int -> Int -> Int -> OSC
-b_write nid p h t f s z = Message "/b_write" [Int nid, String p, Int h, Int t, Int f, Int s, Int z]
+b_write nid p h t f s z = message "/b_write" [Int nid, String p, Int h, Int t, Int f, Int s, Int z]
 
 -- | Zero sample data. (Asynchronous)
 b_zero :: Int -> OSC
-b_zero nid = Message "/b_zero" [Int nid]
+b_zero nid = message "/b_zero" [Int nid]
 
 -- * Control bus commands
 
 -- |  Fill ranges of bus values.
 c_fill :: [(Int, Int, Double)] -> OSC
-c_fill = Message "/c_fill" . mk_triples Int Int Float
+c_fill = message "/c_fill" . mk_triples Int Int Float
 
 -- | Get bus values.
 c_get :: [Int] -> OSC
-c_get = Message "/c_get" . map Int
+c_get = message "/c_get" . map Int
 
 -- | Get ranges of bus values.
 c_getn :: [(Int, Int)] -> OSC
-c_getn = Message "/c_getn" . mk_duples Int Int
+c_getn = message "/c_getn" . mk_duples Int Int
 
 -- | Set bus values.
 c_set :: [(Int, Double)] -> OSC
-c_set = Message "/c_set" . mk_duples Int Float
+c_set = message "/c_set" . mk_duples Int Float
 
 -- | Set ranges of bus values.
 c_setn :: [(Int, [Double])] -> OSC
-c_setn l = Message "/c_setn" (concatMap f l)
+c_setn l = message "/c_setn" (concatMap f l)
     where f (i,d) = Int i : Int (length d) : map Float d
 
 -- * Server operation commands
 
 -- | Remove all bundles from the scheduling queue.
 clearSched :: OSC
-clearSched = Message "/clearSched" []
+clearSched = message "/clearSched" []
 
 -- | Enumeration of OSC printer types.
 data PrintLevel = NoPrinter
@@ -235,23 +235,23 @@
 
 -- | Select printing of incoming Open Sound Control messages.
 dumpOSC :: PrintLevel -> OSC
-dumpOSC c = Message "/dumpOSC" [Int (fromEnum c)]
+dumpOSC c = message "/dumpOSC" [Int (fromEnum c)]
 
 -- | Select reception of notification messages. (Asynchronous)
 notify :: Bool -> OSC
-notify c = Message "/notify" [Int (fromEnum c)]
+notify c = message "/notify" [Int (fromEnum c)]
 
 -- | Stop synthesis server.
 quit :: OSC
-quit = Message "/quit" []
+quit = message "/quit" []
 
 -- | Request \/status.reply message.
 status :: OSC
-status = Message "/status" []
+status = message "/status" []
 
--- | Request \/synced Message when all current asynchronous commands complete.
+-- | Request \/synced message when all current asynchronous commands complete.
 sync :: Int -> OSC
-sync sid = Message "/sync" [Int sid]
+sync sid = message "/sync" [Int sid]
 
 -- * Variants to simplify common cases
 
@@ -286,7 +286,7 @@
 withCM (Message c xs) cm =
     if c `elem` async_cmds
     then let xs' = xs ++ [Blob (B.unpack (encodeOSC cm))]
-         in Message c xs'
+         in message c xs'
     else error ("withCM: not async: " ++ c)
 withCM _ _ = error "withCM: not message"
 
diff --git a/Sound/SC3/Server/Command/Completion.hs b/Sound/SC3/Server/Command/Completion.hs
--- a/Sound/SC3/Server/Command/Completion.hs
+++ b/Sound/SC3/Server/Command/Completion.hs
@@ -34,49 +34,52 @@
 
 -- | Install a bytecode instrument definition. (Asynchronous)
 d_recv' :: OSC -> [Word8] -> OSC
-d_recv' osc b = Message "/d_recv" [Blob b, encode_osc_blob osc]
+d_recv' osc b = message "/d_recv" [Blob b, encode_osc_blob osc]
 
 -- | Load an instrument definition from a named file. (Asynchronous)
 d_load' :: OSC -> String -> OSC
-d_load' osc p = Message "/d_load" [String p, encode_osc_blob osc]
+d_load' osc p = message "/d_load" [String p, encode_osc_blob osc]
 
 -- | Load a directory of instrument definitions files. (Asynchronous)
 d_loadDir' :: OSC -> String -> OSC
-d_loadDir' osc p = Message "/d_loadDir" [String p, encode_osc_blob osc]
+d_loadDir' osc p = message "/d_loadDir" [String p, encode_osc_blob osc]
 
 -- | Allocates zero filled buffer to number of channels and samples. (Asynchronous)
 b_alloc' :: OSC -> Int -> Int -> Int -> OSC
-b_alloc' osc nid frames channels = Message "/b_alloc" [Int nid, Int frames, Int channels, encode_osc_blob osc]
+b_alloc' osc nid frames channels = message "/b_alloc" [Int nid, Int frames, Int channels, encode_osc_blob osc]
 
 -- | Allocate buffer space and read a sound file. (Asynchronous)
 b_allocRead' :: OSC -> Int -> String -> Int -> Int -> OSC
-b_allocRead' osc nid p f n = Message "/b_allocRead" [Int nid, String p, Int f, Int n, encode_osc_blob osc]
+b_allocRead' osc nid p f n = message "/b_allocRead" [Int nid, String p, Int f, Int n, encode_osc_blob osc]
 
 -- | Allocate buffer space and read a sound file, picking specific channels. (Asynchronous)
 b_allocReadChannel' :: OSC -> Int -> String -> Int -> Int -> [Int] -> OSC
-b_allocReadChannel' osc nid p f n cs = Message "/b_allocReadChannel" ([Int nid, String p, Int f, Int n] ++ map Int cs ++ [encode_osc_blob osc])
+b_allocReadChannel' osc nid p f n cs = message "/b_allocReadChannel" ([Int nid, String p, Int f, Int n] ++ map Int cs ++ [encode_osc_blob osc])
 
 -- | Free buffer data. (Asynchronous)
 b_free' :: OSC -> Int -> OSC
-b_free' osc nid = Message "/b_free" [Int nid, encode_osc_blob osc]
+b_free' osc nid = message "/b_free" [Int nid, encode_osc_blob osc]
 
 -- | Close attached soundfile and write header information. (Asynchronous)
 b_close' :: OSC -> Int -> OSC
-b_close' osc nid = Message "/b_close" [Int nid, encode_osc_blob osc]
+b_close' osc nid = message "/b_close" [Int nid, encode_osc_blob osc]
 
 -- | Read sound file data into an existing buffer. (Asynchronous)
 b_read' :: OSC -> Int -> String -> Int -> Int -> Int -> Int -> OSC
-b_read' osc nid p f n f' z = Message "/b_read" [Int nid, String p, Int f, Int n, Int f', Int z, encode_osc_blob osc]
+b_read' osc nid p f n f' z = message "/b_read" [Int nid, String p, Int f, Int n, Int f', Int z, encode_osc_blob osc]
 
 -- | Read sound file data into an existing buffer. (Asynchronous)
 b_readChannel' :: OSC -> Int -> String -> Int -> Int -> Int -> Int -> [Int] -> OSC
-b_readChannel' osc nid p f n f' z cs = Message "/b_readChannel" ([Int nid, String p, Int f, Int n, Int f', Int z] ++ map Int cs ++ [encode_osc_blob osc])
+b_readChannel' osc nid p f n f' z cs = message "/b_readChannel" ([Int nid, String p, Int f, Int n, Int f', Int z] ++ map Int cs ++ [encode_osc_blob osc])
 
 -- | Write sound file data. (Asynchronous)
 b_write' :: OSC -> Int -> String -> Int -> Int -> Int -> Int -> Int -> OSC
-b_write' osc nid p h t f s z = Message "/b_write" [Int nid, String p, Int h, Int t, Int f, Int s, Int z, encode_osc_blob osc]
+b_write' osc nid p h t f s z = message "/b_write" [Int nid, String p, Int h, Int t, Int f, Int s, Int z, encode_osc_blob osc]
 
 -- | Zero sample data. (Asynchronous)
 b_zero' :: OSC -> Int -> OSC
-b_zero' osc nid = Message "/b_zero" [Int nid, encode_osc_blob osc]
+b_zero' osc nid = message "/b_zero" [Int nid, encode_osc_blob osc]
 
+-- Local Variables:
+-- truncate-lines:t
+-- End:
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
@@ -25,9 +25,10 @@
 extractDouble _ = error "extractDouble"
 
 serverStatusData :: Transport t => t -> IO [Datum]
-serverStatusData fd = do send fd status 
-                         (Message _ d) <- wait fd "status.reply"
-                         return d
+serverStatusData fd = do
+  send fd status
+  (Message _ d) <- wait fd "/status.reply"
+  return d
 
 statusFields :: [String]
 statusFields = ["Unused                      ",
@@ -41,7 +42,6 @@
                 "Sample Rate (Actual)        "]
 
 statusFormat :: [Datum] -> [String]
-statusFormat d = s : zipWith (++) 
-                             (tail statusFields) 
-                             (map show (tail d))
-    where s = "***** SuperCollider Server Status *****"
+statusFormat d =
+    let s = "***** SuperCollider Server Status *****"
+    in s : zipWith (++) (tail statusFields) (map show (tail d))
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
@@ -4,7 +4,6 @@
                                  , synth, synthdef, synthstat ) where
 
 import qualified Data.ByteString.Lazy as B
-import qualified Data.Char as C
 import qualified Data.IntMap as M
 import Data.List
 import Data.Word
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
@@ -6,7 +6,8 @@
                                          ,envPerc', envPerc
                                          ,envTriangle
                                          ,envSine
-                                         ,envLinen', envLinen) where
+                                         ,envLinen', envLinen
+                                         ,envADSR, envASR) where
 
 import Sound.SC3.UGen.UGen
 import Sound.SC3.UGen.Math
@@ -81,11 +82,32 @@
     let c = (EnvLin, EnvLin, EnvLin)
     in envLinen' aT sT rT l c
 
+-- aT = attackTime
+-- dT = decayTime
+-- sL = sustainLevel
+-- rT = releaseTime
+-- pL = peakLevel
+-- c = curve
+-- b = bias
+
+-- | Attack, decay, sustain, release envelope parameter constructor.
+envADSR :: UGen -> UGen -> UGen -> UGen -> UGen -> EnvCurve -> UGen -> [UGen]
+envADSR aT dT sL rT pL c b =
+    let l = map (+ b) [0,pL,pL*sL,0]
+        t = [aT,dT,rT]
+        c' = [c,c,c]
+    in env l t c' 2 (-1)
+
+-- | Attack, sustain, release envelope parameter constructor.
+envASR :: UGen -> UGen -> UGen -> EnvCurve -> [UGen]
+envASR aT sL rT c =
+    let l = [0,sL,0]
+        t = [aT,rT]
+        c' = [c,c]
+    in env l t c' 1 (-1)
+
 d_dx :: (Num a) => [a] -> [a]
-d_dx [] = []
-d_dx [_] = []
-d_dx [x,y] = [y - x]
-d_dx (x:y:r) = y - x : d_dx (y:r)
+d_dx xs = zipWith (-) (drop 1 xs) xs
 
 env_curve :: EnvCurve -> UGen
 env_curve EnvStep = Constant 0.0
diff --git a/Sound/SC3/UGen/External/LPC.hs b/Sound/SC3/UGen/External/LPC.hs
--- a/Sound/SC3/UGen/External/LPC.hs
+++ b/Sound/SC3/UGen/External/LPC.hs
@@ -41,7 +41,7 @@
       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
+  _ <- B.hGet h hc
   d <- replicateM nf get_f
   hClose h
   return (LPC hdr d)
diff --git a/Sound/SC3/UGen/Filter.hs b/Sound/SC3/UGen/Filter.hs
--- a/Sound/SC3/UGen/Filter.hs
+++ b/Sound/SC3/UGen/Filter.hs
@@ -277,6 +277,13 @@
 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
diff --git a/Sound/SC3/UGen/Granular.hs b/Sound/SC3/UGen/Granular.hs
--- a/Sound/SC3/UGen/Granular.hs
+++ b/Sound/SC3/UGen/Granular.hs
@@ -5,20 +5,20 @@
 import Sound.SC3.UGen.UGen
 
 -- | Granular synthesis with sound stored in a buffer.
-grainBuf :: Int -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-grainBuf nc t d s r p i l e = mkOsc AR "GrainBuf" [t, d, s, r, p, i, l, e] nc
+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
-grainFM nc t d c m i l e = mkOsc AR "GrainFM" [t, d, c, m, i, l, e] nc
+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
-grainIn nc t d i l e = mkOsc AR "GrainIn" [t, d, i, l, e] nc
+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
-grainSin nc t d f l e = mkOsc AR "GrainSin" [t, d, f, l, e] nc
+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
diff --git a/Sound/SC3/UGen/Panner.hs b/Sound/SC3/UGen/Panner.hs
--- a/Sound/SC3/UGen/Panner.hs
+++ b/Sound/SC3/UGen/Panner.hs
@@ -35,8 +35,8 @@
 decodeB2 :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen
 decodeB2 nc w x y o = mkFilterMCE "DecodeB2" [w, x, y, o] nc 0
 
-panAz :: UGen -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
-panAz nc i p l w o = mkFilterMCE "PanAz" [i, p, l, w, o] nc 0
+panAz :: Int -> UGen -> UGen -> UGen -> UGen -> UGen -> UGen
+panAz nc i p l w o = mkFilter "PanAz" [i, p, l, w, o] nc
 
 xFade2 :: UGen -> UGen -> UGen -> UGen -> UGen
 xFade2 inA inB pan level = mkFilter "XFade2" [inA, inB, pan, level] 2
diff --git a/emacs/hsc3.el b/emacs/hsc3.el
--- a/emacs/hsc3.el
+++ b/emacs/hsc3.el
@@ -99,6 +99,8 @@
 (defun hsc3-quit-haskell ()
   "Quit haskell."
   (interactive)
+  (hsc3-send-string ":quit")
+  (sit-for 0.25)
   (kill-buffer hsc3-buffer)
   (delete-other-windows))
 
@@ -111,6 +113,11 @@
 			      (concat "^"
 				      (thing-at-point 'symbol)
 				      "\\.help\\.lhs"))))
+
+(defun hsc3-request-type ()
+  "Ask ghci for the type of the name at point."
+  (interactive)
+  (hsc3-send-string (concat ":t " (thing-at-point 'symbol))))
 
 (defun chunk-string (n s)
   "Split a string into chunks of 'n' characters."
diff --git a/hsc3.cabal b/hsc3.cabal
--- a/hsc3.cabal
+++ b/hsc3.cabal
@@ -1,17 +1,17 @@
 Name:              hsc3
-Version:           0.8
+Version:           0.9
 Synopsis:          Haskell SuperCollider
 Description:       hsc3 provides Sound.SC3, a haskell module that 
                    facilitates using haskell as a client to the 
                    SuperCollider synthesis server.  
 License:           GPL
 Category:          Sound
-Copyright:         (c) Rohan Drape and others, 2006-2010
+Copyright:         (c) Rohan Drape and others, 2006-2011
 Author:            Rohan Drape
 Maintainer:        rd@slavepianos.org
 Stability:         Experimental
 Homepage:          http://slavepianos.org/rd/?t=hsc3
-Tested-With:       GHC == 6.10.3
+Tested-With:       GHC == 6.12.1
 Build-Type:        Simple
 Cabal-Version:     >= 1.6
 
@@ -45,7 +45,7 @@
                    binary,
                    bytestring,
                    containers,
-                   hosc == 0.8,
+                   hosc == 0.9,
                    network,
                    process,
                    random,
@@ -100,4 +100,4 @@
 
 Source-Repository  head
   Type:            darcs
-  Location:        http://slavepianos.org/~rd/sw/hsc3/
+  Location:        http://slavepianos.org/rd/sw/hsc3/
