diff --git a/BootTidal.hs b/BootTidal.hs index af44fd5c..e3639b5a 100644 --- a/BootTidal.hs +++ b/BootTidal.hs @@ -1,86 +1,43 @@ +:set -fno-warn-orphans +:set -XMultiParamTypeClasses :set -XOverloadedStrings :set prompt "" -import Sound.Tidal.Context - -import System.IO (hSetEncoding, stdout, utf8) -hSetEncoding stdout utf8 - -tidal <- startTidal (superdirtTarget {oLatency = 0.05, oAddress = "127.0.0.1", oPort = 57120}) (defaultConfig {cVerbose = True}) - -:set -Wno-name-shadowing - -:{ -let only = (hush >>) - p = streamReplace tidal - hush = streamHush tidal - panic = do hush - once $ sound "superpanic" - list = streamList tidal - mute = streamMute tidal - unmute = streamUnmute tidal - unmuteAll = streamUnmuteAll tidal - unsoloAll = streamUnsoloAll tidal - solo = streamSolo tidal - unsolo = streamUnsolo tidal - once = streamOnce tidal - first = streamFirst tidal - asap = once - nudgeAll = streamNudgeAll tidal - all = streamAll tidal - resetCycles = streamResetCycles tidal - setCycle = streamSetCycle tidal - setcps = asap . cps - getcps = streamGetCPS tidal - getnow = streamGetNow tidal - xfade i = transition tidal True (Sound.Tidal.Transition.xfadeIn 4) i - xfadeIn i t = transition tidal True (Sound.Tidal.Transition.xfadeIn t) i - histpan i t = transition tidal True (Sound.Tidal.Transition.histpan t) i - wait i t = transition tidal True (Sound.Tidal.Transition.wait t) i - waitT i f t = transition tidal True (Sound.Tidal.Transition.waitT f t) i - jump i = transition tidal True (Sound.Tidal.Transition.jump) i - jumpIn i t = transition tidal True (Sound.Tidal.Transition.jumpIn t) i - jumpIn' i t = transition tidal True (Sound.Tidal.Transition.jumpIn' t) i - jumpMod i t = transition tidal True (Sound.Tidal.Transition.jumpMod t) i - jumpMod' i t p = transition tidal True (Sound.Tidal.Transition.jumpMod' t p) i - mortal i lifespan release = transition tidal True (Sound.Tidal.Transition.mortal lifespan release) i - interpolate i = transition tidal True (Sound.Tidal.Transition.interpolate) i - interpolateIn i t = transition tidal True (Sound.Tidal.Transition.interpolateIn t) i - clutch i = transition tidal True (Sound.Tidal.Transition.clutch) i - clutchIn i t = transition tidal True (Sound.Tidal.Transition.clutchIn t) i - anticipate i = transition tidal True (Sound.Tidal.Transition.anticipate) i - anticipateIn i t = transition tidal True (Sound.Tidal.Transition.anticipateIn t) i - forId i t = transition tidal False (Sound.Tidal.Transition.mortalOverlay t) i - d1 = p 1 . (|< orbit 0) - d2 = p 2 . (|< orbit 1) - d3 = p 3 . (|< orbit 2) - d4 = p 4 . (|< orbit 3) - d5 = p 5 . (|< orbit 4) - d6 = p 6 . (|< orbit 5) - d7 = p 7 . (|< orbit 6) - d8 = p 8 . (|< orbit 7) - d9 = p 9 . (|< orbit 8) - d10 = p 10 . (|< orbit 9) - d11 = p 11 . (|< orbit 10) - d12 = p 12 . (|< orbit 11) - d13 = p 13 - d14 = p 14 - d15 = p 15 - d16 = p 16 -:} - -:set -Wname-shadowing - -:{ -let getState = streamGet tidal - setI = streamSetI tidal - setF = streamSetF tidal - setS = streamSetS tidal - setR = streamSetR tidal - setB = streamSetB tidal -:} - +default (Signal String, Integer, Double) + +-- Import all the boot functions and aliases. +import Sound.Tidal.Boot + +-- Create a Tidal Stream with the default settings. +-- Use 'mkTidalWith' to customize these settings. +tidalInst <- mkTidal + +-- This orphan instance makes the boot aliases work! +-- It has to go after you define 'tidalInst'. +instance Tidally where tidal = tidalInst + +-- You can add your own aliases in this file. Here are some examples: +-- :{ +-- let xfade i = transition tidal True (Sound.Tidal.Transition.xfadeIn 4) i +-- xfadeIn i t = transition tidal True (Sound.Tidal.Transition.xfadeIn t) i +-- histpan i t = transition tidal True (Sound.Tidal.Transition.histpan t) i +-- wait i t = transition tidal True (Sound.Tidal.Transition.wait t) i +-- waitT i f t = transition tidal True (Sound.Tidal.Transition.waitT f t) i +-- jump i = transition tidal True (Sound.Tidal.Transition.jump) i +-- jumpIn i t = transition tidal True (Sound.Tidal.Transition.jumpIn t) i +-- jumpIn' i t = transition tidal True (Sound.Tidal.Transition.jumpIn' t) i +-- jumpMod i t = transition tidal True (Sound.Tidal.Transition.jumpMod t) i +-- jumpMod' i t p = transition tidal True (Sound.Tidal.Transition.jumpMod' t p) i +-- mortal i lifespan release = transition tidal True (Sound.Tidal.Transition.mortal lifespan release) i +-- interpolate i = transition tidal True (Sound.Tidal.Transition.interpolate) i +-- interpolateIn i t = transition tidal True (Sound.Tidal.Transition.interpolateIn t) i +-- clutch i = transition tidal True (Sound.Tidal.Transition.clutch) i +-- clutchIn i t = transition tidal True (Sound.Tidal.Transition.clutchIn t) i +-- anticipate i = transition tidal True (Sound.Tidal.Transition.anticipate) i +-- anticipateIn i t = transition tidal True (Sound.Tidal.Transition.anticipateIn t) i +-- forId i t = transition tidal False (Sound.Tidal.Transition.mortalOverlay t) i +-- :} + +:set -fwarn-orphans :set prompt "tidal> " :set prompt-cont "" - -default (Pattern String, Integer, Double) diff --git a/example.tidal b/example.tidal new file mode 100644 index 00000000..795b4404 --- /dev/null +++ b/example.tidal @@ -0,0 +1,5 @@ +-- This is an example file you can use to quickly test Tidal editor integration. + +d1 $ s "bd sd bd [~ sd] bd sd bd*3 sd" + +hush diff --git a/src/Sound/Tidal/Boot.hs b/src/Sound/Tidal/Boot.hs new file mode 100644 index 00000000..9d4d04a6 --- /dev/null +++ b/src/Sound/Tidal/Boot.hs @@ -0,0 +1,226 @@ +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE OverloadedStrings #-} + +module Sound.Tidal.Boot + ( Tidally (..) + , OscMap + , mkOscMap + , mkTidal + , mkTidalWith + , only + , p + , hush + , panic + , list + , mute + , unmute + , unmuteAll + , unsoloAll + , solo + , unsolo + , once + , asap + , first + , nudgeAll + , all + , resetCycles + , setCycle + , setcps + , getcps + , getnow + , d1 + , d2 + , d3 + , d4 + , d5 + , d6 + , d7 + , d8 + , d9 + , d10 + , d11 + , d12 + , d13 + , d14 + , d15 + , d16 + , getState + , setI + , setF + , setS + , setR + , setB + , module Sound.Tidal.Context + ) +where + +{- + Boot.hs - Shortcuts for using an in-scope Tidal Stream. + Copyright (C) 2023, Alex McLean and contributors + + This library is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this library. If not, see . +-} + +import Prelude hiding (all, (*>), (<*)) +import Sound.Tidal.Context hiding (mute, solo) +import Sound.Tidal.ID (ID) +import System.IO (hSetEncoding, stdout, utf8) + +-- | Functions using this constraint can access the in-scope Tidal instance. +-- You must implement an instance of this in 'BootTidal.hs'. Note that GHC +-- will complain that it is an "orphan" instance, but that is ok. +class Tidally where + tidal :: Stream + +type OscMap = [(Target, [OSC])] + +-- | A reasonable OscMap +mkOscMap :: OscMap +mkOscMap = [(superdirtTarget {oLatency = 0.05, oAddress = "127.0.0.1", oPort = 57120}, [superdirtShape])] + +-- | Creates a Tidal instance using default config. Use 'mkTidalWith' to customize. +mkTidal :: IO Stream +mkTidal = mkTidalWith defaultConfig mkOscMap + +-- | See 'Sound.Tidal.Stream.startStream'. +mkTidalWith :: Config -> OscMap -> IO Stream +mkTidalWith config oscmap = do + hSetEncoding stdout utf8 + startStream config oscmap + +-- | 'hush' then execute the given action. +only :: Tidally => IO () -> IO () +only = (hush >>) + +-- | See 'Sound.Tidal.Stream.streamReplace'. +p :: Tidally => ID -> ControlPattern -> IO () +p = streamReplace tidal + +-- | See 'Sound.Tidal.Stream.streamHush'. +hush :: Tidally => IO () +hush = streamHush tidal + +panic :: Tidally => IO () +panic = hush >> once (sound "superpanic") + +-- | See 'Sound.Tidal.Stream.streamList'. +list :: Tidally => IO () +list = streamList tidal + +-- | See 'Sound.Tidal.Stream.streamMute'. +mute :: Tidally => ID -> IO () +mute = streamMute tidal + +-- | See 'Sound.Tidal.Stream.streamUnmute'. +unmute :: Tidally => ID -> IO () +unmute = streamUnmute tidal + +-- | See 'Sound.Tidal.Stream.streamUnmuteAll'. +unmuteAll :: Tidally => IO () +unmuteAll = streamUnmuteAll tidal + +-- | See 'Sound.Tidal.Stream.streamUnsoloAll'. +unsoloAll :: Tidally => IO () +unsoloAll = streamUnsoloAll tidal + +-- | See 'Sound.Tidal.Stream.streamSolo'. +solo :: Tidally => ID -> IO () +solo = streamSolo tidal + +-- | See 'Sound.Tidal.Stream.streamUnsolo'. +unsolo :: Tidally => ID -> IO () +unsolo = streamUnsolo tidal + +-- | See 'Sound.Tidal.Stream.streamOnce'. +once :: Tidally => ControlPattern -> IO () +once = streamOnce tidal + +-- | An alias for 'once'. +asap :: Tidally => ControlPattern -> IO () +asap = once + +-- | See 'Sound.Tidal.Stream.first'. +first :: Tidally => ControlPattern -> IO () +first = streamFirst tidal + +-- | See 'Sound.Tidal.Stream.nudgeAll'. +nudgeAll :: Tidally => Double -> IO () +nudgeAll = streamNudgeAll tidal + +-- | See 'Sound.Tidal.Stream.streamAll'. +all :: Tidally => (ControlPattern -> ControlPattern) -> IO () +all = streamAll tidal + +-- | See 'Sound.Tidal.Stream.resetCycles'. +resetCycles :: Tidally => IO () +resetCycles = streamResetCycles tidal + +-- | See 'Sound.Tidal.Stream.streamSetCycle'. +setCycle :: Tidally => Time -> IO () +setCycle = streamSetCycle tidal + +-- | See 'Sound.Tidal.Params.cps'. +setcps :: Tidally => Pattern Double -> IO () +setcps = once . cps + +-- | See 'Sound.Tidal.Stream.streamGetcps'. +getcps :: Tidally => IO Time +getcps = streamGetCPS tidal + +-- | See 'Sound.Tidal.Stream.streamGetnow'. +getnow :: Tidally => IO Time +getnow = streamGetNow tidal + +-- | Replace what's playing on the given orbit. +d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16 :: Tidally => ControlPattern -> IO () +d1 = p 1 . (|< orbit 0) +d2 = p 2 . (|< orbit 1) +d3 = p 3 . (|< orbit 2) +d4 = p 4 . (|< orbit 3) +d5 = p 5 . (|< orbit 4) +d6 = p 6 . (|< orbit 5) +d7 = p 7 . (|< orbit 6) +d8 = p 8 . (|< orbit 7) +d9 = p 9 . (|< orbit 8) +d10 = p 10 . (|< orbit 9) +d11 = p 11 . (|< orbit 10) +d12 = p 12 . (|< orbit 11) +d13 = p 13 +d14 = p 14 +d15 = p 15 +d16 = p 16 + +-- | See 'Sound.Tidal.Stream.streamGet'. +getState :: Tidally => String -> IO (Maybe Value) +getState = streamGet tidal + +-- | See 'Sound.Tidal.Stream.streamSetI'. +setI :: Tidally => String -> Pattern Int -> IO () +setI = streamSetI tidal + +-- | See 'Sound.Tidal.Stream.streamSetF'. +setF :: Tidally => String -> Pattern Double -> IO () +setF = streamSetF tidal + +-- | See 'Sound.Tidal.Stream.streamSetS'. +setS :: Tidally => String -> Pattern String -> IO () +setS = streamSetS tidal + +-- | See 'Sound.Tidal.Stream.streamSetR'. +setR :: Tidally => String -> Pattern Rational -> IO () +setR = streamSetR tidal + +-- | See 'Sound.Tidal.Stream.streamSetB'. +setB :: Tidally => String -> Pattern Bool -> IO () +setB = streamSetB tidal diff --git a/src/Sound/Tidal/Chords.hs b/src/Sound/Tidal/Chords.hs index 8131dac4..323bc05e 100644 --- a/src/Sound/Tidal/Chords.hs +++ b/src/Sound/Tidal/Chords.hs @@ -184,14 +184,18 @@ chordTable = [("major", major), ("6by9", sixNine), ("major7", major7), ("maj7", major7), + ("M7", major7), ("major9", major9), ("maj9", major9), + ("M9", major9), ("add9", add9), ("major11", major11), ("maj11", major11), + ("M11", major11), ("add11", add11), ("major13", major13), ("maj13", major13), + ("M13", major13), ("add13", add13), ("dom7", dom7), ("dom9", dom9), diff --git a/src/Sound/Tidal/UI.hs b/src/Sound/Tidal/UI.hs index 0ed88779..e74ec3f6 100644 --- a/src/Sound/Tidal/UI.hs +++ b/src/Sound/Tidal/UI.hs @@ -46,7 +46,8 @@ import Data.List (elemIndex, findIndex, findIndices, groupBy, intercalate, sort, sortOn, transpose) import qualified Data.Map.Strict as Map -import Data.Maybe (fromJust, fromMaybe, isJust, mapMaybe) +import Data.Maybe (catMaybes, fromJust, fromMaybe, isJust, + mapMaybe) import Data.Ratio (Ratio, (%)) import qualified Data.Text as T @@ -2176,7 +2177,18 @@ _pressBy r pat = squeezeJoin $ (compressTo (r,1) . pure) <$> pat > (s "cp:3*16" # speed sine + 1.5) -} sew :: Pattern Bool -> Pattern a -> Pattern a -> Pattern a -sew pb a b = overlay (mask pb a) (mask (inv pb) b) +-- Replaced with more efficient version below +-- sew pb a b = overlay (mask pb a) (mask (inv pb) b) +sew pb a b = Pattern $ pf + where pf st = concatMap match evs + where evs = query pb st + parts = map part evs + subarc = Arc (minimum $ map start parts) (maximum $ map stop parts) + match ev | value ev = find (query a st {arc = subarc}) ev + | otherwise = find (query b st {arc = subarc}) ev + find evs' ev = catMaybes $ map (check ev) evs' + check bev xev = do newarc <- subArc (part bev) (part xev) + return $ xev {part = newarc} {-| Uses the first (binary) pattern to switch between the following two patterns. The resulting structure comes from the binary diff --git a/tidal.cabal b/tidal.cabal index 477a8783..bf4c9dbc 100644 --- a/tidal.cabal +++ b/tidal.cabal @@ -28,6 +28,7 @@ library autogen-modules: Paths_tidal Exposed-modules: Sound.Tidal.Bjorklund + Sound.Tidal.Boot Sound.Tidal.Chords Sound.Tidal.Control Sound.Tidal.Context @@ -63,7 +64,7 @@ library , hosc >= 0.20 && < 0.21 , text < 2.2 , parsec >= 3.1.12 && < 3.2 - , network < 3.2 + , network < 3.3 , transformers >= 0.5 && < 0.7 , bytestring < 0.13 , clock < 0.9