Skip to content

Commit

Permalink
interp
Browse files Browse the repository at this point in the history
  • Loading branch information
ejconlon committed Feb 4, 2024
1 parent 16122cc commit 692149d
Show file tree
Hide file tree
Showing 10 changed files with 103 additions and 93 deletions.
2 changes: 1 addition & 1 deletion minipat-dirt/minipat-dirt.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ source-repository head
library
exposed-modules:
Minipat.Dirt.Core
Minipat.Dirt.EStream
Minipat.Dirt.Eval
Minipat.Dirt.Flow
Minipat.Dirt.Logger
Minipat.Dirt.Notes
Minipat.Dirt.Osc
Expand Down
56 changes: 56 additions & 0 deletions minipat-dirt/src/Minipat/Dirt/EStream.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{-# LANGUAGE RoleAnnotations #-}
{-# LANGUAGE UndecidableInstances #-}

module Minipat.Dirt.EStream where

import Control.Exception (Exception, SomeException (..))
import Data.Kind (Type)
import Data.Proxy (Proxy (..))
import Data.Semigroup (Semigroup (..))
import Data.String (IsString (..))
import Data.Text (Text)
import Data.Typeable (Typeable)
import Minipat.Eval (EvalEnv, evalPat)
import Minipat.Stream (Stream)
import Minipat.Stream qualified as S

-- Attempting to add a few things to Streams
-- 1) Tracking errors for later logging
-- 2) IsString instance for seamless parsing
newtype EStream (k :: k1) (a :: Type) = EStream {unEStream :: Either SomeException (Stream a)}
deriving stock (Functor)

type role EStream phantom nominal

instance Applicative (EStream k) where
pure = EStream . Right . pure
liftA2 f (EStream ca) (EStream cb) = EStream (liftA2 (liftA2 f) ca cb)

instance Semigroup (EStream k a) where
EStream es1 <> EStream es2 = EStream (liftA2 (<>) es1 es2)
sconcat = EStream . fmap sconcat . traverse unEStream

instance Monoid (EStream k a) where
mempty = EStream (Right mempty)
mconcat = EStream . fmap mconcat . traverse unEStream

estreamMap :: (Stream a -> Stream b) -> EStream k a -> EStream j b
estreamMap f (EStream c) = EStream (fmap f c)

estreamBind :: EStream k a -> (Stream a -> EStream j b) -> EStream j b
estreamBind (EStream c) f = EStream (c >>= unEStream . f)

estreamThrow :: (Exception e) => e -> EStream k a
estreamThrow = EStream . Left . SomeException

estreamFilter :: (a -> Bool) -> EStream k a -> EStream j a
estreamFilter = estreamMap . S.streamFilter

class EStreamEval k e a | k -> e a where
estreamEvalEnv :: Proxy k -> EvalEnv e a

instance (EStreamEval k e a, Show e, Typeable e) => IsString (EStream k a) where
fromString = estreamEval @e (estreamEvalEnv (Proxy :: Proxy k)) . fromString

estreamEval :: (Show e, Typeable e) => EvalEnv e a -> Text -> EStream k a
estreamEval ee txt = EStream (evalPat ee txt)
12 changes: 6 additions & 6 deletions minipat-dirt/src/Minipat/Dirt/Eval.hs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ import Data.Text (Text)
import Data.Void (Void)
import Looksee (intP, sciP)
import Minipat.Ast (Ident (..), Select (..))
import Minipat.Class (Pattern (..))
import Minipat.Dirt.Osc (Attrs)
import Minipat.Eval (EvalEnv (..), evalPat)
import Minipat.Interp (InterpEnv (..), InterpErr (..), forbidInterpEnv)
import Minipat.Parser (P, identP)
import Minipat.Stream (Stream)

datumP :: DatumType -> P Datum
datumP = \case
Expand All @@ -30,13 +30,13 @@ datumP = \case
forbidEvalEnv :: DatumType -> EvalEnv e Datum
forbidEvalEnv dt = EvalEnv forbidInterpEnv (datumP dt)

liveEvalPat :: DatumType -> Text -> Stream Datum
liveEvalPat dt txt = either (pure mempty) id (evalPat @Void (forbidEvalEnv dt) txt)
liveEvalPat :: (Pattern f) => DatumType -> Text -> f Datum
liveEvalPat dt txt = either (pure patEmpty) id (evalPat @_ @Void (forbidEvalEnv dt) txt)

data SoundSelectErr = SoundSelectErr
deriving stock (Eq, Ord, Show)

soundSelFn :: Select -> Stream Attrs -> Either (InterpErr SoundSelectErr) (Stream Attrs)
soundSelFn :: (Pattern f) => Select -> f Attrs -> Either (InterpErr SoundSelectErr) (f Attrs)
soundSelFn sel attrs =
case sel of
SelectSample n -> Right (fmap (Map.insert "note" (DatumInt32 (fromIntegral n))) attrs)
Expand All @@ -51,5 +51,5 @@ soundInterpEnv = InterpEnv soundSelFn soundProjFn
soundEvalEnv :: EvalEnv SoundSelectErr Attrs
soundEvalEnv = EvalEnv soundInterpEnv identP

liveEvalSoundPat :: Text -> Stream Attrs
liveEvalSoundPat txt = either (pure mempty) id (evalPat soundEvalEnv txt)
liveEvalSoundPat :: (Pattern f) => Text -> f Attrs
liveEvalSoundPat txt = either (pure patEmpty) id (evalPat soundEvalEnv txt)
56 changes: 0 additions & 56 deletions minipat-dirt/src/Minipat/Dirt/Flow.hs

This file was deleted.

8 changes: 7 additions & 1 deletion minipat/src/Minipat/Ast.hs
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,13 @@ instance (Monoid b) => Pattern (Pat b) where
patEmpty = mkPat PatSilence
patPar = mkPat . PatGroup . Group 0 GroupTypePar . fmap unPat
patAlt = mkPat . PatGroup . Group 0 GroupTypeAlt . fmap unPat
patRand = error "TODO"
patRand = mkPat . PatGroup . Group 0 GroupTypeRand . fmap unPat
patSeq = error "TODO"
patEuc = error "TODO"
patRep = error "TODO"
patFastBy = error "TODO"
patFast = error "TODO"
patSlowBy = error "TODO"
patSlow = error "TODO"
patDegBy = error "TODO"
patDeg = error "TODO"
4 changes: 4 additions & 0 deletions minipat/src/Minipat/Class.hs
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@ class (Functor f) => Pattern f where
patSeq :: NESeq (f a, CycleDelta) -> f a
patEuc :: Int -> Int -> Maybe Int -> f a -> f a
patRep :: Int -> f a -> f a
patFastBy, patSlowBy :: Rational -> f a -> f a
patFast, patSlow :: f Rational -> f a -> f a
patDegBy :: Rational -> f a -> f a
patDeg :: f Rational -> f a -> f a
4 changes: 2 additions & 2 deletions minipat/src/Minipat/Eval.hs
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ import Control.Exception (SomeException (..))
import Data.Text (Text)
import Data.Typeable (Typeable)
import Looksee (parse)
import Minipat.Class (Pattern)
import Minipat.Interp (InterpEnv, interpPat)
import Minipat.Norm (normPat)
import Minipat.Parser (P, topPatP)
import Minipat.Stream (Stream)

data EvalEnv e a where
EvalEnv :: InterpEnv e z a -> P z -> EvalEnv e a

-- | The canonical way to parse, normalize, and interpret patterns as streams
evalPat :: (Show e, Typeable e) => EvalEnv e a -> Text -> Either SomeException (Stream a)
evalPat :: (Pattern f, Show e, Typeable e) => EvalEnv e a -> Text -> Either SomeException (f a)
evalPat (EvalEnv ie p) t = do
pat <- either (Left . SomeException) Right (parse (topPatP p) t)
let pat' = normPat pat
Expand Down
32 changes: 13 additions & 19 deletions minipat/src/Minipat/Interp.hs
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,10 @@ import Minipat.Ast
)
import Minipat.Class (Pattern (..))
import Minipat.Rewrite (RwErr, RwT, rewriteM, throwRw)
import Minipat.Stream
( Stream (..)
, streamDegradeBy
, streamFast
, streamFastBy
, streamSlow
)
import Minipat.Time (CycleDelta (..))

-- | A function that processes a 'Select'
type Sel e a = Select -> Stream a -> Either (InterpErr e) (Stream a)
type Sel e a = forall f. (Pattern f) => Select -> f a -> Either (InterpErr e) (f a)

data InterpEnv e a c = InterpEnv
{ ieSel :: !(Sel e c)
Expand Down Expand Up @@ -98,9 +91,10 @@ runM :: M b e a -> Either (RwErr (InterpErr e) b) a
runM = runExcept

lookInterp
:: InterpEnv e a c
-> PatX b a (M b e (Stream c, CycleDelta))
-> RwT b (M b e) (Stream c, CycleDelta)
:: (Pattern f)
=> InterpEnv e a c
-> PatX b a (M b e (f c, CycleDelta))
-> RwT b (M b e) (f c, CycleDelta)
lookInterp (InterpEnv sel proj) = \case
PatPure a -> pure (patPure (proj a), 1)
PatSilence -> pure (patEmpty, 1)
Expand All @@ -120,20 +114,20 @@ lookInterp (InterpEnv sel proj) = \case
GroupTypeSeq _ -> pure (patSeq els', 1)
GroupTypePar -> pure (patPar (fmap fst els'), 1)
GroupTypeRand ->
let els'' = fmap (\(el, w) -> streamFastBy (unCycleDelta w) el) els'
let els'' = fmap (\(el, w) -> patFastBy (unCycleDelta w) el) els'
s = patRand els''
in pure (s, 1)
GroupTypeAlt ->
let els'' = fmap (\(el, w) -> streamFastBy (unCycleDelta w) el) els'
let els'' = fmap (\(el, w) -> patFastBy (unCycleDelta w) el) els'
s = patAlt els''
in pure (s, 1)
PatMod (Mod melw md) ->
case md of
ModTypeSpeed (Speed dir spat) -> do
spat' <- lift (subInterp forbidInterpEnv spat)
let f = case dir of
SpeedDirFast -> streamFast
SpeedDirSlow -> streamSlow
SpeedDirFast -> patFast
SpeedDirSlow -> patSlow
spat'' = fmap factorValue spat'
(el, w) <- lift melw
pure (f spat'' el, w)
Expand All @@ -145,7 +139,7 @@ lookInterp (InterpEnv sel proj) = \case
ModTypeDegrade (Degrade dd) -> do
let d = maybe (1 % 2) factorValue dd
(el, w) <- lift melw
let el' = streamDegradeBy d el
let el' = patDegBy d el
pure (el', w)
ModTypeEuclid euc -> do
let (Euclid (fromInteger -> filled) (fromInteger -> steps) (fmap fromInteger -> mshift)) = euc
Expand All @@ -154,9 +148,9 @@ lookInterp (InterpEnv sel proj) = \case
pure (s, fromIntegral steps)
PatPoly (Poly _ _) -> error "TODO"

subInterp :: InterpEnv e a c -> Pat b a -> M b e (Stream c)
subInterp :: (Pattern f) => InterpEnv e a c -> Pat b a -> M b e (f c)
subInterp env = fmap fst . rewriteM (lookInterp env) . unPat

-- | Interpret the given 'Pat' as a 'Stream'
interpPat :: InterpEnv e a c -> Pat b a -> Either (RwErr (InterpErr e) b) (Stream c)
-- | Interpret the given 'Pat' as any 'Pattern'
interpPat :: (Pattern f) => InterpEnv e a c -> Pat b a -> Either (RwErr (InterpErr e) b) (f c)
interpPat env = runM . subInterp env
18 changes: 12 additions & 6 deletions minipat/src/Minipat/Stream.hs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ module Minipat.Stream
, streamLateBy
, streamEarly
, streamLate
, streamDegradeBy
, streamDegrade
, streamDegBy
, streamDeg
, streamCont
, streamEuc
, streamRand
Expand Down Expand Up @@ -215,11 +215,11 @@ streamEarly, streamLate :: Stream CycleTime -> Stream a -> Stream a
streamEarly = streamAdjust streamEarlyBy
streamLate = streamAdjust streamLateBy

streamDegradeBy :: Rational -> Stream a -> Stream a
streamDegradeBy r (Stream k) = Stream (tapeDegradeBy r . k)
streamDegBy :: Rational -> Stream a -> Stream a
streamDegBy r (Stream k) = Stream (tapeDegradeBy r . k)

streamDegrade :: Stream Rational -> Stream a -> Stream a
streamDegrade = streamAdjust streamDegradeBy
streamDeg :: Stream Rational -> Stream a -> Stream a
streamDeg = streamAdjust streamDegBy

streamSeq :: NESeq (Stream a, CycleDelta) -> Stream a
streamSeq ss = Stream $ \arc ->
Expand Down Expand Up @@ -302,3 +302,9 @@ instance Pattern Stream where
patSeq = streamSeq
patEuc = streamEuc
patRep = streamRep
patFastBy = streamFastBy
patSlowBy = streamSlowBy
patFast = streamFast
patSlow = streamSlow
patDegBy = streamDegBy
patDeg = streamDeg
4 changes: 2 additions & 2 deletions minipat/test/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import Minipat.Interp (SelAcc, accInterpEnv, accProj, interpPat)
import Minipat.Norm (normPat)
import Minipat.Parser (P, ParseErr, factorP, identP, identPatP)
import Minipat.Print (render)
import Minipat.Stream (Ev (..), streamRun)
import Minipat.Stream (Ev (..), Stream, streamRun)
import Minipat.Time (Arc (..), CycleTime (..), Span (..))
import Prettyprinter qualified as P
import System.IO (BufferMode (..), hSetBuffering, stdout)
Expand Down Expand Up @@ -420,7 +420,7 @@ runPatInterpCase :: (TestName, Maybe Arc, Text, [Ev (SelAcc Ident)]) -> TestTree
runPatInterpCase (n, mayArc, patStr, evs) = testCase n $ do
pat <- either throwIO pure (parse tpatP patStr)
let pat' = normPat pat
pat'' <- either throwIO pure (interpPat @Void accInterpEnv pat')
pat'' <- either throwIO pure (interpPat @Stream @Void accInterpEnv pat')
let arc = fromMaybe (Arc 0 1) mayArc
actualEvs = streamRun pat'' arc
actualEvs @?= evs
Expand Down

0 comments on commit 692149d

Please sign in to comment.