diff --git a/BootTidal.hs b/BootTidal.hs
--- a/BootTidal.hs
+++ b/BootTidal.hs
@@ -66,7 +66,8 @@
 :}
 
 :{
-let setI = streamSetI tidal
+let getState = streamGet tidal
+    setI = streamSetI tidal
     setF = streamSetF tidal
     setS = streamSetS tidal
     setR = streamSetR tidal
diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,15 @@
 # TidalCycles log of changes
 
+## 1.7.3 - Symonds Yat
+   * Signed ratio shorthands now supported @ndr_brt
+   * OSC API - mute/unmute stream @mindofmatthew
+   * improve performance of playFor/seqP @yaxu
+   * Expand tests with autodiscovered laws @RSWilli
+   * Add `getState` to BootTidal @mindofmatthew
+   * add fadeTime / fadeInTime / fadeOutTime params for grain envelopes @yaxu
+   * Fix for nrpnn and nrpnv params @yaxu
+   * Disable busses for MIDI parameters @mindofmatthew
+   
 ## 1.7.2 - IKLECTIK c
    * tidal-parse additions and improved errors, haskellish dependency fix @dktr0
    * spring cleaning and delinting, recreated Time.hs, renamed ControlMap toValueMAp @yaxu
diff --git a/src/Sound/Tidal/Params.hs b/src/Sound/Tidal/Params.hs
--- a/src/Sound/Tidal/Params.hs
+++ b/src/Sound/Tidal/Params.hs
@@ -105,6 +105,12 @@
 nrpn :: Pattern String -> ControlPattern
 nrpn = grp [mI "nrpn", mI "val"]
 
+nrpnn :: Pattern Int -> ControlPattern
+nrpnn = pI "nrpn"
+
+nrpnv :: Pattern Int -> ControlPattern
+nrpnv = pI "val"
+
 grain' :: Pattern String -> ControlPattern
 grain' = grp [mF "begin", mF "end"]
 
@@ -216,9 +222,7 @@
 arrayTake :: String -> [Double] -> ControlPattern
 arrayTake name xs = pStateListF "array" name xs
 arraybus :: Pattern Int -> Pattern [Word8] -> ControlPattern
-arraybus busid pat = (pX "array" pat) # (pI "^array" busid)
-arrayrecv :: Pattern Int -> ControlPattern
-arrayrecv busid = pI "^array" busid
+arraybus _ _ = error $ "Control parameter 'array' can't be sent to a bus."
 
 -- | a pattern of numbers to specify the attack time (in seconds) of an envelope applied to each sample.
 attack :: Pattern Double -> ControlPattern
@@ -546,9 +550,7 @@
 ccnCountTo name ipat = innerJoin $ (\i -> pStateF "ccn" name (maybe 0 ((`mod'` i) . (+1)))) <$> ipat
 
 ccnbus :: Pattern Int -> Pattern Double -> ControlPattern
-ccnbus busid pat = (pF "ccn" pat) # (pI "^ccn" busid)
-ccnrecv :: Pattern Int -> ControlPattern
-ccnrecv busid = pI "^ccn" busid
+ccnbus _ _ = error $ "Control parameter 'ccn' can't be sent to a bus."
 
 -- | 
 ccv :: Pattern Double -> ControlPattern
@@ -561,9 +563,7 @@
 ccvCountTo name ipat = innerJoin $ (\i -> pStateF "ccv" name (maybe 0 ((`mod'` i) . (+1)))) <$> ipat
 
 ccvbus :: Pattern Int -> Pattern Double -> ControlPattern
-ccvbus busid pat = (pF "ccv" pat) # (pI "^ccv" busid)
-ccvrecv :: Pattern Int -> ControlPattern
-ccvrecv busid = pI "^ccv" busid
+ccvbus _ _ = error $ "Control parameter 'ccv' can't be sent to a bus."
 
 -- | choose the channel the pattern is sent to in superdirt
 channel :: Pattern Int -> ControlPattern
@@ -634,9 +634,7 @@
 controlCountTo name ipat = innerJoin $ (\i -> pStateF "control" name (maybe 0 ((`mod'` i) . (+1)))) <$> ipat
 
 controlbus :: Pattern Int -> Pattern Double -> ControlPattern
-controlbus busid pat = (pF "control" pat) # (pI "^control" busid)
-controlrecv :: Pattern Int -> ControlPattern
-controlrecv busid = pI "^control" busid
+controlbus _ _ = error $ "Control parameter 'control' can't be sent to a bus."
 
 -- | 
 cps :: Pattern Double -> ControlPattern
@@ -679,9 +677,7 @@
 ctlNumCountTo name ipat = innerJoin $ (\i -> pStateF "ctlNum" name (maybe 0 ((`mod'` i) . (+1)))) <$> ipat
 
 ctlNumbus :: Pattern Int -> Pattern Double -> ControlPattern
-ctlNumbus busid pat = (pF "ctlNum" pat) # (pI "^ctlNum" busid)
-ctlNumrecv :: Pattern Int -> ControlPattern
-ctlNumrecv busid = pI "^ctlNum" busid
+ctlNumbus _ _ = error $ "Control parameter 'ctlNum' can't be sent to a bus."
 
 -- | 
 ctranspose :: Pattern Double -> ControlPattern
@@ -936,6 +932,32 @@
 expressionrecv :: Pattern Int -> ControlPattern
 expressionrecv busid = pI "^expression" busid
 
+-- | As with fadeTime, but controls the fade in time of the grain envelope. Not used if the grain begins at position 0 in the sample.
+fadeInTime :: Pattern Double -> ControlPattern
+fadeInTime = pF "fadeInTime"
+fadeInTimeTake :: String -> [Double] -> ControlPattern
+fadeInTimeTake name xs = pStateListF "fadeInTime" name xs
+fadeInTimeCount :: String -> ControlPattern
+fadeInTimeCount name = pStateF "fadeInTime" name (maybe 0 (+1))
+fadeInTimeCountTo :: String -> Pattern Double -> Pattern ValueMap
+fadeInTimeCountTo name ipat = innerJoin $ (\i -> pStateF "fadeInTime" name (maybe 0 ((`mod'` i) . (+1)))) <$> ipat
+
+fadeInTimebus :: Pattern Int -> Pattern Double -> ControlPattern
+fadeInTimebus _ _ = error $ "Control parameter 'fadeInTime' can't be sent to a bus."
+
+-- | Used when using begin/end or chop/striate and friends, to change the fade out time of the 'grain' envelope.
+fadeTime :: Pattern Double -> ControlPattern
+fadeTime = pF "fadeTime"
+fadeTimeTake :: String -> [Double] -> ControlPattern
+fadeTimeTake name xs = pStateListF "fadeTime" name xs
+fadeTimeCount :: String -> ControlPattern
+fadeTimeCount name = pStateF "fadeTime" name (maybe 0 (+1))
+fadeTimeCountTo :: String -> Pattern Double -> Pattern ValueMap
+fadeTimeCountTo name ipat = innerJoin $ (\i -> pStateF "fadeTime" name (maybe 0 ((`mod'` i) . (+1)))) <$> ipat
+
+fadeTimebus :: Pattern Int -> Pattern Double -> ControlPattern
+fadeTimebus _ _ = error $ "Control parameter 'fadeTime' can't be sent to a bus."
+
 -- | 
 frameRate :: Pattern Double -> ControlPattern
 frameRate = pF "frameRate"
@@ -947,9 +969,7 @@
 frameRateCountTo name ipat = innerJoin $ (\i -> pStateF "frameRate" name (maybe 0 ((`mod'` i) . (+1)))) <$> ipat
 
 frameRatebus :: Pattern Int -> Pattern Double -> ControlPattern
-frameRatebus busid pat = (pF "frameRate" pat) # (pI "^frameRate" busid)
-frameRaterecv :: Pattern Int -> ControlPattern
-frameRaterecv busid = pI "^frameRate" busid
+frameRatebus _ _ = error $ "Control parameter 'frameRate' can't be sent to a bus."
 
 -- | 
 frames :: Pattern Double -> ControlPattern
@@ -962,9 +982,7 @@
 framesCountTo name ipat = innerJoin $ (\i -> pStateF "frames" name (maybe 0 ((`mod'` i) . (+1)))) <$> ipat
 
 framesbus :: Pattern Int -> Pattern Double -> ControlPattern
-framesbus busid pat = (pF "frames" pat) # (pI "^frames" busid)
-framesrecv :: Pattern Int -> ControlPattern
-framesrecv busid = pI "^frames" busid
+framesbus _ _ = error $ "Control parameter 'frames' can't be sent to a bus."
 
 -- | Spectral freeze
 freeze :: Pattern Double -> ControlPattern
@@ -1170,9 +1188,7 @@
 hoursCountTo name ipat = innerJoin $ (\i -> pStateF "hours" name (maybe 0 ((`mod'` i) . (+1)))) <$> ipat
 
 hoursbus :: Pattern Int -> Pattern Double -> ControlPattern
-hoursbus busid pat = (pF "hours" pat) # (pI "^hours" busid)
-hoursrecv :: Pattern Int -> ControlPattern
-hoursrecv busid = pI "^hours" busid
+hoursbus _ _ = error $ "Control parameter 'hours' can't be sent to a bus."
 
 -- | a pattern of numbers from 0 to 1. Applies the resonance of the high-pass filter. Has alias @hpq@
 hresonance :: Pattern Double -> ControlPattern
@@ -1601,9 +1617,7 @@
 midibendCountTo name ipat = innerJoin $ (\i -> pStateF "midibend" name (maybe 0 ((`mod'` i) . (+1)))) <$> ipat
 
 midibendbus :: Pattern Int -> Pattern Double -> ControlPattern
-midibendbus busid pat = (pF "midibend" pat) # (pI "^midibend" busid)
-midibendrecv :: Pattern Int -> ControlPattern
-midibendrecv busid = pI "^midibend" busid
+midibendbus _ _ = error $ "Control parameter 'midibend' can't be sent to a bus."
 
 -- | 
 midichan :: Pattern Double -> ControlPattern
@@ -1616,9 +1630,7 @@
 midichanCountTo name ipat = innerJoin $ (\i -> pStateF "midichan" name (maybe 0 ((`mod'` i) . (+1)))) <$> ipat
 
 midichanbus :: Pattern Int -> Pattern Double -> ControlPattern
-midichanbus busid pat = (pF "midichan" pat) # (pI "^midichan" busid)
-midichanrecv :: Pattern Int -> ControlPattern
-midichanrecv busid = pI "^midichan" busid
+midichanbus _ _ = error $ "Control parameter 'midichan' can't be sent to a bus."
 
 -- | 
 midicmd :: Pattern String -> ControlPattern
@@ -1626,9 +1638,7 @@
 midicmdTake :: String -> [Double] -> ControlPattern
 midicmdTake name xs = pStateListF "midicmd" name xs
 midicmdbus :: Pattern Int -> Pattern String -> ControlPattern
-midicmdbus busid pat = (pS "midicmd" pat) # (pI "^midicmd" busid)
-midicmdrecv :: Pattern Int -> ControlPattern
-midicmdrecv busid = pI "^midicmd" busid
+midicmdbus _ _ = error $ "Control parameter 'midicmd' can't be sent to a bus."
 
 -- | 
 miditouch :: Pattern Double -> ControlPattern
@@ -1641,9 +1651,7 @@
 miditouchCountTo name ipat = innerJoin $ (\i -> pStateF "miditouch" name (maybe 0 ((`mod'` i) . (+1)))) <$> ipat
 
 miditouchbus :: Pattern Int -> Pattern Double -> ControlPattern
-miditouchbus busid pat = (pF "miditouch" pat) # (pI "^miditouch" busid)
-miditouchrecv :: Pattern Int -> ControlPattern
-miditouchrecv busid = pI "^miditouch" busid
+miditouchbus _ _ = error $ "Control parameter 'miditouch' can't be sent to a bus."
 
 -- | 
 minutes :: Pattern Double -> ControlPattern
@@ -1656,9 +1664,7 @@
 minutesCountTo name ipat = innerJoin $ (\i -> pStateF "minutes" name (maybe 0 ((`mod'` i) . (+1)))) <$> ipat
 
 minutesbus :: Pattern Int -> Pattern Double -> ControlPattern
-minutesbus busid pat = (pF "minutes" pat) # (pI "^minutes" busid)
-minutesrecv :: Pattern Int -> ControlPattern
-minutesrecv busid = pI "^minutes" busid
+minutesbus _ _ = error $ "Control parameter 'minutes' can't be sent to a bus."
 
 -- | 
 modwheel :: Pattern Double -> ControlPattern
@@ -1716,36 +1722,6 @@
 notebus :: Pattern Int -> Pattern Note -> ControlPattern
 notebus _ _ = error $ "Control parameter 'note' can't be sent to a bus."
 
--- | 
-nrpnn :: Pattern Int -> ControlPattern
-nrpnn = pI "nrpnn"
-nrpnnTake :: String -> [Double] -> ControlPattern
-nrpnnTake name xs = pStateListF "nrpnn" name xs
-nrpnnCount :: String -> ControlPattern
-nrpnnCount name = pStateF "nrpnn" name (maybe 0 (+1))
-nrpnnCountTo :: String -> Pattern Double -> Pattern ValueMap
-nrpnnCountTo name ipat = innerJoin $ (\i -> pStateF "nrpnn" name (maybe 0 ((`mod'` i) . (+1)))) <$> ipat
-
-nrpnnbus :: Pattern Int -> Pattern Int -> ControlPattern
-nrpnnbus busid pat = (pI "nrpnn" pat) # (pI "^nrpnn" busid)
-nrpnnrecv :: Pattern Int -> ControlPattern
-nrpnnrecv busid = pI "^nrpnn" busid
-
--- | 
-nrpnv :: Pattern Int -> ControlPattern
-nrpnv = pI "nrpnv"
-nrpnvTake :: String -> [Double] -> ControlPattern
-nrpnvTake name xs = pStateListF "nrpnv" name xs
-nrpnvCount :: String -> ControlPattern
-nrpnvCount name = pStateF "nrpnv" name (maybe 0 (+1))
-nrpnvCountTo :: String -> Pattern Double -> Pattern ValueMap
-nrpnvCountTo name ipat = innerJoin $ (\i -> pStateF "nrpnv" name (maybe 0 ((`mod'` i) . (+1)))) <$> ipat
-
-nrpnvbus :: Pattern Int -> Pattern Int -> ControlPattern
-nrpnvbus busid pat = (pI "nrpnv" pat) # (pI "^nrpnv" busid)
-nrpnvrecv :: Pattern Int -> ControlPattern
-nrpnvrecv busid = pI "^nrpnv" busid
-
 -- | Nudges events into the future by the specified number of seconds. Negative numbers work up to a point as well (due to internal latency)
 nudge :: Pattern Double -> ControlPattern
 nudge = pF "nudge"
@@ -2081,9 +2057,7 @@
 polyTouchCountTo name ipat = innerJoin $ (\i -> pStateF "polyTouch" name (maybe 0 ((`mod'` i) . (+1)))) <$> ipat
 
 polyTouchbus :: Pattern Int -> Pattern Double -> ControlPattern
-polyTouchbus busid pat = (pF "polyTouch" pat) # (pI "^polyTouch" busid)
-polyTouchrecv :: Pattern Int -> ControlPattern
-polyTouchrecv busid = pI "^polyTouch" busid
+polyTouchbus _ _ = error $ "Control parameter 'polyTouch' can't be sent to a bus."
 
 -- | 
 portamento :: Pattern Double -> ControlPattern
@@ -2111,9 +2085,7 @@
 progNumCountTo name ipat = innerJoin $ (\i -> pStateF "progNum" name (maybe 0 ((`mod'` i) . (+1)))) <$> ipat
 
 progNumbus :: Pattern Int -> Pattern Double -> ControlPattern
-progNumbus busid pat = (pF "progNum" pat) # (pI "^progNum" busid)
-progNumrecv :: Pattern Int -> ControlPattern
-progNumrecv busid = pI "^progNum" busid
+progNumbus _ _ = error $ "Control parameter 'progNum' can't be sent to a bus."
 
 -- | used in SuperDirt softsynths as a control rate or 'speed'
 rate :: Pattern Double -> ControlPattern
@@ -2321,9 +2293,7 @@
 secondsCountTo name ipat = innerJoin $ (\i -> pStateF "seconds" name (maybe 0 ((`mod'` i) . (+1)))) <$> ipat
 
 secondsbus :: Pattern Int -> Pattern Double -> ControlPattern
-secondsbus busid pat = (pF "seconds" pat) # (pI "^seconds" busid)
-secondsrecv :: Pattern Int -> ControlPattern
-secondsrecv busid = pI "^seconds" busid
+secondsbus _ _ = error $ "Control parameter 'seconds' can't be sent to a bus."
 
 -- | 
 semitone :: Pattern Double -> ControlPattern
@@ -2651,9 +2621,7 @@
 songPtrCountTo name ipat = innerJoin $ (\i -> pStateF "songPtr" name (maybe 0 ((`mod'` i) . (+1)))) <$> ipat
 
 songPtrbus :: Pattern Int -> Pattern Double -> ControlPattern
-songPtrbus busid pat = (pF "songPtr" pat) # (pI "^songPtr" busid)
-songPtrrecv :: Pattern Int -> ControlPattern
-songPtrrecv busid = pI "^songPtr" busid
+songPtrbus _ _ = error $ "Control parameter 'songPtr' can't be sent to a bus."
 
 -- | a pattern of numbers which changes the speed of sample playback, i.e. a cheap way of changing pitch. Negative values will play the sample backwards!
 speed :: Pattern Double -> ControlPattern
@@ -2867,9 +2835,7 @@
 uidCountTo name ipat = innerJoin $ (\i -> pStateF "uid" name (maybe 0 ((`mod'` i) . (+1)))) <$> ipat
 
 uidbus :: Pattern Int -> Pattern Double -> ControlPattern
-uidbus busid pat = (pF "uid" pat) # (pI "^uid" busid)
-uidrecv :: Pattern Int -> ControlPattern
-uidrecv busid = pI "^uid" busid
+uidbus _ _ = error $ "Control parameter 'uid' can't be sent to a bus."
 
 -- | used in conjunction with `speed`, accepts values of "r" (rate, default behavior), "c" (cycles), or "s" (seconds). Using `unit "c"` means `speed` will be interpreted in units of cycles, e.g. `speed "1"` means samples will be stretched to fill a cycle. Using `unit "s"` means the playback speed will be adjusted so that the duration is the number of seconds specified by `speed`.
 unit :: Pattern String -> ControlPattern
@@ -2890,9 +2856,7 @@
 valCountTo name ipat = innerJoin $ (\i -> pStateF "val" name (maybe 0 ((`mod'` i) . (+1)))) <$> ipat
 
 valbus :: Pattern Int -> Pattern Double -> ControlPattern
-valbus busid pat = (pF "val" pat) # (pI "^val" busid)
-valrecv :: Pattern Int -> ControlPattern
-valrecv busid = pI "^val" busid
+valbus _ _ = error $ "Control parameter 'val' can't be sent to a bus."
 
 -- | 
 vcfegint :: Pattern Double -> ControlPattern
@@ -3293,6 +3257,9 @@
 gatbus = gatebus
 gatrecv :: Pattern Int -> ControlPattern
 gatrecv = gaterecv
+
+fadeOutTime :: Pattern Double -> ControlPattern
+fadeOutTime = fadeTime
 
 dt :: Pattern Double -> ControlPattern
 dt = delaytime
diff --git a/src/Sound/Tidal/ParseBP.hs b/src/Sound/Tidal/ParseBP.hs
--- a/src/Sound/Tidal/ParseBP.hs
+++ b/src/Sound/Tidal/ParseBP.hs
@@ -321,12 +321,11 @@
          <|> return Positive
 
 intOrFloat :: MyParser Double
-intOrFloat =  do s   <- sign
-                 num <- naturalOrFloat
-                 return (case num of
-                            Right x -> applySign s x
-                            Left  x -> fromIntegral $ applySign s x
-                        )
+intOrFloat = do num <- naturalOrFloat
+                return (case num of
+                  Right x -> x
+                  Left  x -> fromIntegral x
+                  )
 
 parseRhythm :: Parseable a => MyParser (TPat a) -> String -> Either ParseError (TPat a)
 parseRhythm f = runParser (pSequence f' Prelude.<* eof) (0 :: Int) ""
@@ -439,16 +438,20 @@
 pChar = wrapPos $ TPat_Atom Nothing <$> pCharNum
 
 pDouble :: MyParser (TPat Double)
-pDouble = wrapPos $ do f <- choice [intOrFloat, pRatioChar, parseNote] <?> "float"
-                       do TPat_Stack . map (TPat_Atom Nothing . (+ f)) <$> parseChord
-                         <|> return (TPat_Atom Nothing f)
+pDouble = wrapPos $ do s <- sign
+                       f <- choice [intOrFloat, pRatioChar, parseNote] <?> "float"
+                       let v = applySign s f
+                       do TPat_Stack . map (TPat_Atom Nothing . (+ v)) <$> parseChord
+                         <|> return (TPat_Atom Nothing v)
                       <|>
                          do TPat_Stack . map (TPat_Atom Nothing) <$> parseChord
 
 pNote :: MyParser (TPat Note)
-pNote = wrapPos $ fmap (fmap Note) $ do f <- choice [intOrFloat, parseNote] <?> "float"
-                                        do TPat_Stack . map (TPat_Atom Nothing . (+ f)) <$> parseChord
-                                           <|> return (TPat_Atom Nothing f)
+pNote = wrapPos $ fmap (fmap Note) $ do s <- sign
+                                        f <- choice [intOrFloat, parseNote] <?> "float"
+                                        let v = applySign s f
+                                        do TPat_Stack . map (TPat_Atom Nothing . (+ v)) <$> parseChord
+                                           <|> return (TPat_Atom Nothing v)
                                            <|> do TPat_Stack . map (TPat_Atom Nothing) <$> parseChord
                                            <|> do TPat_Atom Nothing <$> pRatioChar
 
diff --git a/src/Sound/Tidal/Pattern.hs b/src/Sound/Tidal/Pattern.hs
--- a/src/Sound/Tidal/Pattern.hs
+++ b/src/Sound/Tidal/Pattern.hs
@@ -449,7 +449,7 @@
 filterAnalog = filterEvents isAnalog
 
 playFor :: Time -> Time -> Pattern a -> Pattern a
-playFor s e = filterWhen (\t -> (t >= s) && (t < e))
+playFor s e pat = Pattern $ \st -> maybe [] (\a -> query pat (st {arc = a})) $ subArc (Arc s e) (arc st)
 
 -- ** Temporal parameter helpers
 
diff --git a/src/Sound/Tidal/Stream.hs b/src/Sound/Tidal/Stream.hs
--- a/src/Sound/Tidal/Stream.hs
+++ b/src/Sound/Tidal/Stream.hs
@@ -287,8 +287,9 @@
           where (a,b) = break (== '}') xs
 
 getString :: ValueMap -> String -> Maybe String
-getString cm s = defaultValue $ simpleShow <$> Map.lookup s cm
-                      where simpleShow :: Value -> String
+getString cm s = (simpleShow <$> Map.lookup param cm) <|> defaultValue dflt
+                      where (param, dflt) = break (== '=') s
+                            simpleShow :: Value -> String
                             simpleShow (VS str) = str
                             simpleShow (VI i) = show i
                             simpleShow (VF f) = show f
@@ -299,11 +300,9 @@
                             simpleShow (VState _) = show "<stateful>"
                             simpleShow (VPattern _) = show "<pattern>"
                             simpleShow (VList _) = show "<list>"
-                            (_, dflt) = break (== '=') s
-                            defaultValue :: Maybe String -> Maybe String
-                            defaultValue Nothing | null dflt = Nothing
-                                                 | otherwise = Just $ tail dflt
-                            defaultValue x = x
+                            defaultValue :: String -> Maybe String
+                            defaultValue ('=':dfltVal) = Just dfltVal
+                            defaultValue _ = Nothing
 
 playStack :: PlayMap -> ControlPattern
 playStack pMap = stack $ map pattern active
@@ -592,6 +591,9 @@
 streamAll s f = do _ <- swapMVar (sGlobalFMV s) f
                    return ()
 
+streamGet :: Stream -> String -> IO (Maybe Value)
+streamGet s k = Map.lookup k <$> readMVar (sStateMV s)
+
 streamSet :: Valuable a => Stream -> String -> Pattern a -> IO ()
 streamSet s k pat = do sMap <- takeMVar $ sStateMV s
                        let pat' = toValue <$> pat
@@ -647,14 +649,28 @@
             bufferIndices [] = []
             bufferIndices (x:xs') | x == (O.ASCII_String $ O.ascii "&controlBusIndices") = catMaybes $ takeWhile isJust $ map O.datum_integral xs'
                                   | otherwise = bufferIndices xs'
-        act (O.Message x (O.Int32 k:v:[]))
-          = act (O.Message x [O.string $ show k,v])
-        act (O.Message _ (O.ASCII_String k:v@(O.Float _):[]))
+        -- External controller commands
+        act (O.Message "/ctrl" (O.Int32 k:v:[]))
+          = act (O.Message "/ctrl" [O.string $ show k,v])
+        act (O.Message "/ctrl" (O.ASCII_String k:v@(O.Float _):[]))
           = add (O.ascii_to_string k) (VF (fromJust $ O.datum_floating v))
-        act (O.Message _ (O.ASCII_String k:O.ASCII_String v:[]))
+        act (O.Message "/ctrl" (O.ASCII_String k:O.ASCII_String v:[]))
           = add (O.ascii_to_string k) (VS (O.ascii_to_string v))
-        act (O.Message _ (O.ASCII_String k:O.Int32 v:[]))
+        act (O.Message "/ctrl" (O.ASCII_String k:O.Int32 v:[]))
           = add (O.ascii_to_string k) (VI (fromIntegral v))
+        -- Stream playback commands
+        act (O.Message "/mute" (O.Int32 k:[]))
+          = streamMute stream k
+        act (O.Message "/mute" (O.ASCII_String k:[]))
+          = streamMute stream (O.ascii_to_string k)
+        act (O.Message "/unmute" (O.Int32 k:[]))
+          = streamUnmute stream k
+        act (O.Message "/unmute" (O.ASCII_String k:[]))
+          = streamUnmute stream (O.ascii_to_string k)
+        act (O.Message "/muteAll" [])
+          = streamMuteAll stream
+        act (O.Message "/unmuteAll" [])
+          = streamUnmuteAll stream
         act m = hPutStrLn stderr $ "Unhandled OSC: " ++ show m
         add :: String -> Value -> IO ()
         add k v = do sMap <- takeMVar (sStateMV stream)
diff --git a/src/Sound/Tidal/Version.hs b/src/Sound/Tidal/Version.hs
--- a/src/Sound/Tidal/Version.hs
+++ b/src/Sound/Tidal/Version.hs
@@ -21,7 +21,7 @@
 -}
 
 tidal_version :: String
-tidal_version = "1.7.2"
+tidal_version = "1.7.3"
 
 tidal_status :: IO ()
 tidal_status = tidal_status_string >>= putStrLn 
diff --git a/test/Sound/Tidal/CoreTest.hs b/test/Sound/Tidal/CoreTest.hs
--- a/test/Sound/Tidal/CoreTest.hs
+++ b/test/Sound/Tidal/CoreTest.hs
@@ -2,140 +2,305 @@
 
 module Sound.Tidal.CoreTest where
 
-import TestUtils
-import Test.Microspec
-
-import Prelude hiding ((<*), (*>))
-
-import Data.Ratio
 import Data.List (sort)
-
+import Data.Ratio
 import Sound.Tidal.Context
+import Test.Microspec
+import TestUtils
+import Prelude hiding ((*>), (<*))
 
 run :: Microspec ()
 run =
   describe "Sound.Tidal.Core" $ do
-    describe "append" $ do
+    describe "append" $
       it "can switch between the cycles from two pures" $ do
-        (queryArc (append (pure "a") (pure "b")) (Arc 0 5)) `shouldBe`
-          fmap toEvent
-          [(((0,1), (0,1)), "a" :: String),
-            (((1,2), (1,2)), "b"),
-            (((2,3), (2,3)), "a"),
-            (((3,4), (3,4)), "b"),
-            (((4,5), (4,5)), "a")
-          ]
+        queryArc (append (pure "a") (pure "b")) (Arc 0 5)
+          `shouldBe` fmap
+            toEvent
+            [ (((0, 1), (0, 1)), "a" :: String),
+              (((1, 2), (1, 2)), "b"),
+              (((2, 3), (2, 3)), "a"),
+              (((3, 4), (3, 4)), "b"),
+              (((4, 5), (4, 5)), "a")
+            ]
     describe "cat" $ do
       it "can switch between the cycles from three pures" $ do
-        queryArc (cat [pure "a", pure "b", pure "c"]) (Arc 0 5) `shouldBe`
-          fmap toEvent
-          [(((0,1), (0,1)), "a" :: String),
-            (((1,2), (1,2)), "b"),
-            (((2,3), (2,3)), "c"),
-            (((3,4), (3,4)), "a"),
-            (((4,5), (4,5)), "b")
-          ]
-
+        queryArc (cat [pure "a", pure "b", pure "c"]) (Arc 0 5)
+          `shouldBe` fmap
+            toEvent
+            [ (((0, 1), (0, 1)), "a" :: String),
+              (((1, 2), (1, 2)), "b"),
+              (((2, 3), (2, 3)), "c"),
+              (((3, 4), (3, 4)), "a"),
+              (((4, 5), (4, 5)), "b")
+            ]
+      it "can extract nested revs" $
+        let a = "1 2 3" :: Pattern Int
+            b = "4 5 6" :: Pattern Int
+            c = "7 8 9" :: Pattern Int
+         in comparePD
+              (Arc 0 10)
+              (rev $ cat [a, b, c])
+              (cat [rev a, rev b, rev c])
     describe "fastCat" $ do
       it "can switch between the cycles from three pures inside one cycle" $ do
-        it "1" $ queryArc (fastCat [pure "a", pure "b", pure "c"]) (Arc 0 1)
-          `shouldBe` fmap toEvent
-          [(((0,1/3),   (0,1/3)),   "a" :: String),
-            (((1/3,2/3), (1/3,2/3)), "b"),
-            (((2/3,1),   (2/3,1)),   "c")
-          ]
-        it "5/3" $ queryArc (fastCat [pure "a", pure "b", pure "c"]) (Arc 0 (5/3))
-          `shouldBe` fmap toEvent
-          [(((0,1/3),   (0,1/3)),   "a" :: String),
-            (((1/3,2/3), (1/3,2/3)), "b"),
-            (((2/3,1),   (2/3,1)),   "c"),
-            (((1,4/3),   (1,4/3)),   "a"),
-            (((4/3,5/3), (4/3,5/3)), "b")
-          ]
+        it "1" $
+          queryArc (fastCat [pure "a", pure "b", pure "c"]) (Arc 0 1)
+            `shouldBe` fmap
+              toEvent
+              [ (((0, 1 / 3), (0, 1 / 3)), "a" :: String),
+                (((1 / 3, 2 / 3), (1 / 3, 2 / 3)), "b"),
+                (((2 / 3, 1), (2 / 3, 1)), "c")
+              ]
+        it "5/3" $
+          queryArc (fastCat [pure "a", pure "b", pure "c"]) (Arc 0 (5 / 3))
+            `shouldBe` fmap
+              toEvent
+              [ (((0, 1 / 3), (0, 1 / 3)), "a" :: String),
+                (((1 / 3, 2 / 3), (1 / 3, 2 / 3)), "b"),
+                (((2 / 3, 1), (2 / 3, 1)), "c"),
+                (((1, 4 / 3), (1, 4 / 3)), "a"),
+                (((4 / 3, 5 / 3), (4 / 3, 5 / 3)), "b")
+              ]
       it "works with zero-length queries" $ do
         it "0" $
           queryArc (fastCat [pure "a", pure "b"]) (Arc 0 0)
-            `shouldBe` fmap toEvent [(((0,0.5), (0,0)), "a" :: String)]
+            `shouldBe` fmap toEvent [(((0, 0.5), (0, 0)), "a" :: String)]
         it "1/3" $
-          queryArc (fastCat [pure "a", pure "b"]) (Arc (1%3) (1%3))
-            `shouldBe` fmap toEvent [(((0,0.5), (1%3,1%3)), "a" :: String)]
+          queryArc (fastCat [pure "a", pure "b"]) (Arc (1 % 3) (1 % 3))
+            `shouldBe` fmap toEvent [(((0, 0.5), (1 % 3, 1 % 3)), "a" :: String)]
 
     describe "rev" $ do
       it "mirrors events" $ do
         let forward = fastCat [fastCat [pure 7, pure 8], pure 9] :: Pattern Int
             backward = fastCat [pure 9, fastCat [pure 8, pure 7]]
         -- sort the events into time order to compare them
-        (sort $ queryArc (rev forward) (Arc 0 1)) `shouldBe` (sort $ queryArc (backward) (Arc 0 1))
+        sort (queryArc (rev forward) (Arc 0 1)) `shouldBe` sort (queryArc backward (Arc 0 1))
 
       it "returns the original if you reverse it twice" $ do
         let x = fastCat [fastCat [pure 7, pure 8], pure 9] :: Pattern Int
-        (queryArc (rev $ rev x) (Arc 0 5)) `shouldBe` (queryArc x (Arc 0 5))
+        queryArc (rev $ rev x) (Arc 0 5) `shouldBe` queryArc x (Arc 0 5)
 
+    describe "|>|" $ do
+      let a = "[1, 1] [2,2] 3" :: Pattern Int
+          b = "4 [5, 5] 6 7" :: Pattern Int
+          c = "7 8 9 10" :: Pattern Int
+          d = "7 [8, 9] 10 11" :: Pattern Int
+      it "creates silence when" $ do
+        it "first argument silent" $
+          comparePD
+            (Arc 0 1)
+            (silence |>| a)
+            silence
+        it "second argument silent" $
+          comparePD
+            (Arc 0 1)
+            (a |>| silence)
+            silence
+      it "creates the same pattern when left argument has the same structure" $
+        comparePD
+          (Arc 0 1)
+          (b |>| a)
+          (d |>| a)
+      it "can extract rev from first argument" $
+        comparePD
+          (Arc 0 1)
+          (rev a |>| b)
+          (rev (a |>| rev b))
+      it "is assiociative" $
+        comparePD
+          (Arc 0 1)
+          ((a |>| b) |>| c)
+          (a |>| (b |>| c))
+      it "is commutative in all arguments except the rightmost" $
+        comparePD
+          (Arc 0 1)
+          (a |>| b |>| c)
+          (b |>| a |>| c)
+
+    describe "stack" $ do
+      let a = "1 2 3" :: Pattern Int
+          b = "4 5 6" :: Pattern Int
+          c = "7 8 9" :: Pattern Int
+      it "is neutral with silence" $
+        comparePD
+          (Arc 0 1)
+          (stack [a, silence])
+          a
+      it "can create silence" $
+        comparePD
+          (Arc 0 1)
+          (stack [] :: Pattern Int)
+          silence
+      it "follows commutative laws" $
+        comparePD
+          (Arc 0 1)
+          (stack [a, b])
+          (stack [b, a])
+      it "follows assiociative laws" $
+        comparePD
+          (Arc 0 1)
+          (stack [a, stack [b, c]])
+          (stack [stack [a, b], c])
+      it "can extract nested revs" $
+        comparePD
+          (Arc 0 1)
+          (rev $ stack [a, b, c])
+          (stack [rev a, rev b, rev c])
+
+    describe "fast" $ do
+      let x = "1 2 3" :: Pattern Time
+          y = "4 5 6" :: Pattern Time
+      it "is neutral with speedup 1" $
+        comparePD
+          (Arc 0 1)
+          (fast 1 x)
+          x
+      it "mutes, when there is" $ do
+        it "silence in first argument" $
+          comparePD
+            (Arc 0 1)
+            (fast silence x)
+            silence
+        it "silence in second argument" $
+          comparePD
+            (Arc 0 1)
+            (fast x silence :: Pattern Time)
+            silence
+        it "speedup by 0" $
+          comparePD
+            (Arc 0 1)
+            (fast 0 x)
+            silence
+      it "is reciprocal to slow" $
+        comparePD
+          (Arc 0 1)
+          (fast 2 x)
+          (slow (fromRational $ 1 % 2) x)
+      it "can be reversed by reciprocal speedup" $
+        comparePD
+          (Arc 0 1)
+          (fast 2 $ fast (fromRational $ 1 % 2) x)
+          x
+      it "preserves structure" $
+        comparePD
+          (Arc 0 1)
+          (fast x (stack [y, y]))
+          (fast (stack [x, x]) y)
+
+    describe "slow" $ do
+      let x = "1 2 3" :: Pattern Time
+          y = "4 5 6" :: Pattern Time
+      it "is neutral with slowdown 1" $
+        comparePD
+          (Arc 0 10)
+          (slow 1 x)
+          x
+      it "mutes, when there is" $ do
+        it "silence in first argument" $
+          comparePD
+            (Arc 0 10)
+            (slow silence x)
+            silence
+        it "silence in second argument" $
+          comparePD
+            (Arc 0 10)
+            (slow x silence :: Pattern Time)
+            silence
+        it "speedup by 0" $
+          comparePD
+            (Arc 0 10)
+            (slow 0 x)
+            silence
+      it "is reciprocal to fast" $
+        comparePD
+          (Arc 0 10)
+          (slow 2 x)
+          (fast (fromRational $ 1 % 2) x)
+      it "can be reversed by reciprocal slowdown" $
+        comparePD
+          (Arc 0 10)
+          (slow 2 $ slow (fromRational $ 1 % 2) x)
+          x
+      it "preserves structure" $
+        comparePD
+          (Arc 0 1)
+          (slow x (stack [y, y]))
+          (slow (stack [x, x]) y)
+
     describe "compress" $ do
       it "squashes cycles to the start of a cycle" $ do
         let p = compress (0, 0.5) $ fastCat [pure 7, pure 8] :: Pattern Int
-        (queryArc p (Arc 0 1)) `shouldBe` fmap toEvent
-          [ (((0,0.25),  (0,0.25)),   7),
-            (((0.25,0.5),(0.25,0.5)), 8)
-          ]
+        queryArc p (Arc 0 1)
+          `shouldBe` fmap
+            toEvent
+            [ (((0, 0.25), (0, 0.25)), 7),
+              (((0.25, 0.5), (0.25, 0.5)), 8)
+            ]
       it "squashes cycles to the end of a cycle" $ do
         let p = compress (0.5, 1) $ fastCat [pure 7, pure 8] :: Pattern Int
-        (queryArc p (Arc 0 1)) `shouldBe` fmap toEvent
-          [(((0.5,0.75),  (0.5,0.75)), 7 :: Int),
-           (((0.75,1),    (0.75,1)),   8)
-          ]
+        queryArc p (Arc 0 1)
+          `shouldBe` fmap
+            toEvent
+            [ (((0.5, 0.75), (0.5, 0.75)), 7 :: Int),
+              (((0.75, 1), (0.75, 1)), 8)
+            ]
       it "squashes cycles to the middle of a cycle" $ do
         let p = compress (0.25, 0.75) $ fastCat [pure 7, pure 8]
-        (queryArc p (Arc 0 1)) `shouldBe` fmap toEvent
-          [(((0.25,0.5),  (0.25,0.5)), 7 :: Int),
-            (((0.5,0.75),  (0.5,0.75)), 8)
-          ]
+        queryArc p (Arc 0 1)
+          `shouldBe` fmap
+            toEvent
+            [ (((0.25, 0.5), (0.25, 0.5)), 7 :: Int),
+              (((0.5, 0.75), (0.5, 0.75)), 8)
+            ]
 
     describe "saw" $ do
       it "goes from 0 up to 1 every cycle" $ do
         it "0" $
-          (queryArc saw (Arc 0 0)) `shouldBe` [(Event (Context []) Nothing (Arc 0 0) 0 :: Event Double)]
+          queryArc saw (Arc 0 0) `shouldBe` [Event (Context []) Nothing (Arc 0 0) 0 :: Event Double]
         it "0.25" $
-          (queryArc saw (Arc 0.25 0.25)) `shouldBe` [(Event (Context []) Nothing (Arc 0.25 0.25) 0.25 :: Event Double)]
+          queryArc saw (Arc 0.25 0.25) `shouldBe` [Event (Context []) Nothing (Arc 0.25 0.25) 0.25 :: Event Double]
         it "0.5" $
-          (queryArc saw (Arc 0.5 0.5))  `shouldBe` [(Event (Context []) Nothing (Arc 0.5 0.5) 0.5 :: Event Double)]
+          queryArc saw (Arc 0.5 0.5) `shouldBe` [Event (Context []) Nothing (Arc 0.5 0.5) 0.5 :: Event Double]
         it "0.75" $
-          (queryArc saw (Arc 0.75 0.75)) `shouldBe` [(Event (Context []) Nothing (Arc 0.75 0.75) 0.75 :: Event Double)]
-      it "can be added to" $ do
-        (map value $ queryArc ((+1) <$> saw) (Arc 0.5 0.5)) `shouldBe` [1.5 :: Float]
-      it "works on the left of <*>" $ do
-        (queryArc ((+) <$> saw <*> pure 3) (Arc 0 1))
+          queryArc saw (Arc 0.75 0.75) `shouldBe` [Event (Context []) Nothing (Arc 0.75 0.75) 0.75 :: Event Double]
+      it "can be added to" $
+        map value (queryArc ((+ 1) <$> saw) (Arc 0.5 0.5)) `shouldBe` [1.5 :: Float]
+      it "works on the left of <*>" $
+        queryArc ((+) <$> saw <*> pure 3) (Arc 0 1)
           `shouldBe` [Event (Context []) Nothing (Arc 0 1) 3.5 :: Event Double]
-      it "works on the right of <*>" $ do
-        (queryArc ((fast 4 $ pure (+3)) <*> saw) (Arc 0 1))
-          `shouldBe` 
-          [Event (Context []) Nothing (Arc 0 0.25) 3.5 :: Event Double,
-           Event (Context []) Nothing (Arc 0.25 0.5) 3.5,
-           Event (Context []) Nothing (Arc 0.5 0.75) 3.5,
-           Event (Context []) Nothing (Arc 0.75 1) 3.5
-          ]
+      it "works on the right of <*>" $
+        queryArc (fast 4 (pure (+ 3)) <*> saw) (Arc 0 1)
+          `shouldBe` [ Event (Context []) Nothing (Arc 0 0.25) 3.5 :: Event Double,
+                       Event (Context []) Nothing (Arc 0.25 0.5) 3.5,
+                       Event (Context []) Nothing (Arc 0.5 0.75) 3.5,
+                       Event (Context []) Nothing (Arc 0.75 1) 3.5
+                     ]
       it "can be reversed" $ do
         it "works with whole cycles" $
-          (queryArc (rev saw) (Arc 0 1))
-            `shouldBe` [(Event (Context []) Nothing (Arc 0 1) 0.5 :: Event Double)]
+          queryArc (rev saw) (Arc 0 1)
+            `shouldBe` [Event (Context []) Nothing (Arc 0 1) 0.5 :: Event Double]
         it "works with half cycles" $
-          (queryArc (rev saw) (Arc 0 0.5))
-            `shouldBe` [(Event (Context []) Nothing (Arc 0 0.5) 0.75 :: Event Double)]
+          queryArc (rev saw) (Arc 0 0.5)
+            `shouldBe` [Event (Context []) Nothing (Arc 0 0.5) 0.75 :: Event Double]
         it "works with inset points" $
-          (queryArc (rev saw) (Arc 0.25 0.25))
-            `shouldBe` [(Event (Context []) Nothing (Arc 0.25 0.25) 0.75 :: Event Double)]
+          queryArc (rev saw) (Arc 0.25 0.25)
+            `shouldBe` [Event (Context []) Nothing (Arc 0.25 0.25) 0.75 :: Event Double]
 
     describe "tri" $ do
-      it "goes from 0 up to 1 and back every cycle" $ do
-        comparePD (Arc 0 1)
+      it "goes from 0 up to 1 and back every cycle" $
+        comparePD
+          (Arc 0 1)
           (struct "t*8" (tri :: Pattern Double))
-          ("0.125 0.375 0.625 0.875 0.875 0.625 0.375 0.125")
-      it "can be added to" $ do
-        comparePD (Arc 0 1)
+          "0.125 0.375 0.625 0.875 0.875 0.625 0.375 0.125"
+      it "can be added to" $
+        comparePD
+          (Arc 0 1)
           (struct "t*8" $ (tri :: Pattern Double) + 1)
-          ("1.125 1.375 1.625 1.875 1.875 1.625 1.375 1.125")
-    describe "every" $ do
+          "1.125 1.375 1.625 1.875 1.875 1.625 1.375 1.125"
+    describe "every" $
       it "`every n id` doesn't change the pattern's structure" $ do
-        comparePD (Arc 0 4)
-          (every 2 id $ "x/2" :: Pattern String)
-          ("x/2")
+        comparePD
+          (Arc 0 4)
+          (every 2 id "x/2" :: Pattern String)
+          "x/2"
diff --git a/test/Sound/Tidal/ExceptionsTest.hs b/test/Sound/Tidal/ExceptionsTest.hs
--- a/test/Sound/Tidal/ExceptionsTest.hs
+++ b/test/Sound/Tidal/ExceptionsTest.hs
@@ -5,7 +5,7 @@
 import Test.Microspec
 import Control.Exception
 import Control.DeepSeq
-import Data.Typeable (typeOf)
+import Data.Typeable ()
 import Prelude hiding ((<*), (*>))
 
 import Sound.Tidal.Pattern
diff --git a/test/Sound/Tidal/ParseTest.hs b/test/Sound/Tidal/ParseTest.hs
--- a/test/Sound/Tidal/ParseTest.hs
+++ b/test/Sound/Tidal/ParseTest.hs
@@ -131,6 +131,10 @@
         compareP (Arc 0 1)
           ("  bd  " :: Pattern String)
           ("bd" :: Pattern String)
+      it "can parse negative ratio shorthands" $ do
+        compareP (Arc 0 1)
+          ("h -h" :: Pattern Double)
+          ("0.5 -0.5" :: Pattern Double)
       it "doesn't crash on zeroes (1)" $ do
         compareP (Arc 0 2)
           ("cp/0" :: Pattern String)
diff --git a/test/Sound/Tidal/StreamTest.hs b/test/Sound/Tidal/StreamTest.hs
--- a/test/Sound/Tidal/StreamTest.hs
+++ b/test/Sound/Tidal/StreamTest.hs
@@ -6,6 +6,7 @@
 import Sound.Tidal.Stream
 import Sound.Tidal.Pattern
 import qualified Sound.OSC.FD as O
+import qualified Data.Map.Strict as M
 
 run :: Microspec ()
 run =
@@ -13,4 +14,23 @@
     describe "toDatum" $ do
       it "should convert VN to osc float" $ do
         toDatum (VN (Note 3.5)) `shouldBe` O.float (3.5 :: Double)
-          
+    
+    describe "substitutePath" $ do
+      -- ValueMap
+      let state = M.fromList [("sound", VS "sn"), ("n", VI 8)]
+      it "should return same string if no params are specified" $ do
+        substitutePath "/s_new" state `shouldBe` Just "/s_new"
+      it "should substitute values for params if present" $ do
+        substitutePath "/{sound}/{n}/vol" state `shouldBe` Just "/sn/8/vol"
+      it "should return Nothing if a param is not present" $ do
+        substitutePath "/{sound}/{inst}" state `shouldBe` Nothing
+    
+    describe "getString" $ do
+      it "should return Nothing for missing params" $ do
+        getString M.empty "s" `shouldBe` Nothing
+      it "should work for strings" $ do
+        getString (M.singleton "s" (VS "sn")) "s" `shouldBe` Just "sn"
+      it "should work for params with fallback expressions" $ do
+        getString (M.singleton "s" (VS "sn")) "s=bd" `shouldBe` Just "sn"
+      it "should work for missing params with fallback expressions" $ do
+        getString M.empty "s=bd" `shouldBe` Just "bd"
diff --git a/test/Sound/Tidal/UITest.hs b/test/Sound/Tidal/UITest.hs
--- a/test/Sound/Tidal/UITest.hs
+++ b/test/Sound/Tidal/UITest.hs
@@ -313,28 +313,28 @@
           (arpeggiate $ "[0,0]*2")
           ("0 0 0 0" :: Pattern Int)
 
-      describe "chunk" $ do
-        it "can chunk a rev pattern" $ do
-          compareP (Arc 0 4)
-            (chunk 2 (rev) $  ("a b c d" :: Pattern String))
-            (slow 2 $ "d c c d a b b a" :: Pattern String)
-        it "can chunk a fast pattern" $ do
-          compareP (Arc 0 4)
-            (chunk 2 (fast 2) $ "a b" :: Pattern String)
-            (slow 2 $ "a b b _ a _ a b" :: Pattern String)
+    describe "chunk" $ do
+      it "can chunk a rev pattern" $ do
+        compareP (Arc 0 4)
+          (chunk 2 (rev) $  ("a b c d" :: Pattern String))
+          (slow 2 $ "d c c d a b b a" :: Pattern String)
+      it "can chunk a fast pattern" $ do
+        compareP (Arc 0 4)
+          (chunk 2 (fast 2) $ "a b" :: Pattern String)
+          (slow 2 $ "a b b _ a _ a b" :: Pattern String)
 
-      describe "binary" $ do
-        it "converts a number to a pattern of boolean" $ do
-          compareP (Arc 0 1)
-            (binary "128")
-            ("t f f f f f f f" :: Pattern Bool)
+    describe "binary" $ do
+      it "converts a number to a pattern of boolean" $ do
+        compareP (Arc 0 1)
+          (binary "128")
+          ("t f f f f f f f" :: Pattern Bool)
 
-      describe "binaryN" $ do
-        it "convert a number to a pattern of boolean of specified length" $ do
-          compareP (Arc 0 1)
-            (binaryN 4 "8")
-            ("t f f f" :: Pattern Bool)
-        it "convert a number to a pattern of boolean of specified patternable length" $ do
-          compareP (Arc 0 2)
-            (binaryN "<4 8>" "8")
-            (cat ["t f f f", "f f f f t f f f"] :: Pattern Bool)
+    describe "binaryN" $ do
+      it "convert a number to a pattern of boolean of specified length" $ do
+        compareP (Arc 0 1)
+          (binaryN 4 "8")
+          ("t f f f" :: Pattern Bool)
+      it "convert a number to a pattern of boolean of specified patternable length" $ do
+        compareP (Arc 0 2)
+          (binaryN "<4 8>" "8")
+          (cat ["t f f f", "f f f f t f f f"] :: Pattern Bool)
diff --git a/tidal.cabal b/tidal.cabal
--- a/tidal.cabal
+++ b/tidal.cabal
@@ -1,5 +1,5 @@
 name:                tidal
-version:             1.7.2
+version:             1.7.3
 synopsis:            Pattern language for improvised music
 -- description:
 homepage:            http://tidalcycles.org/
@@ -58,7 +58,6 @@
     , text < 1.3
     , parsec >= 3.1.12 && < 3.2
     , network < 3.2
-    , vector < 0.13
     , bifunctors < 5.6
     , transformers >= 0.5 && < 0.5.7
     , bytestring < 0.12
