{-# OPTIONS_GHC -fno-warn-orphans #-}
{-# Language
        TypeFamilies,
        MultiParamTypeClasses,
        FlexibleInstances,
        FlexibleContexts #-}
module Csound.Typed.Types.SigSpace(
    SigSpace(..), BindSig(..), mul, mul', on, uon, At(..), MixAt(..),
    cfd, genCfds, cfd4, cfds,

    -- * Stereo sig-space
    SigSpace2(..), BindSig2(..), mul2, mul2',
) where

import Control.Monad
import Control.Applicative
import Csound.Typed.Types.Prim
import Csound.Typed.GlobalState.SE

-- | A class for easy way to process the outputs of the instruments.
class SigSpace a where
    mapSig  :: (Sig -> Sig)    -> a -> a

-- | A class for easy way to process the outputs of the instruments.
class SigSpace a => BindSig a where
    bindSig :: (Sig -> SE Sig) -> a -> SE a

-- | A class for easy way to process the outputs of the instruments.
class SigSpace2 a where
    mapSig2  :: (Sig2 -> Sig2)    -> a -> a

-- | A class for easy way to process the outputs of the instruments.
class SigSpace2 a => BindSig2 a where
    bindSig2 :: (Sig2 -> SE Sig2) -> a -> SE a


-- | Scaling the sound.
mul :: SigSpace a => Sig -> a -> a
mul k = mapSig (k * )

-- | Scaling the sound with effectful signal.
mul' :: BindSig a => SE Sig -> a -> SE a
mul' k = bindSig (\x -> fmap (* x) k)

-- | Scaling the sound with a pair.
mul2 :: SigSpace2 a => Sig2 -> a -> a
mul2 (ka, kb) = mapSig2 (\(a, b) -> (ka * a, kb * b))

-- | Scaling the sound with effectful pair of signals.
mul2' :: BindSig2 a => SE Sig2 -> a -> SE a
mul2' k = bindSig2 (\(xa, xb) -> fmap (\(ka, kb) -> (ka * xa, kb * xb)) k)

-- rescaling

-- | Rescaling of the bipolar signal (-1, 1) -> (a, b)
--
-- > on a b biSig
on :: SigSpace a => Sig -> Sig -> a -> a
on a b x = uon a b $ mapSig unipolar x
    where unipolar a = 0.5 + 0.5 * a

-- | Rescaling of the unipolar signal (0, 1) -> (a, b)
--
-- > on a b uniSig
uon :: SigSpace a => Sig -> Sig -> a -> a
uon a b = mapSig (\x -> a + (b - a) * x)

-- | Crossfade.
--
-- > cfd coeff sig1 sig2
--
-- If coeff equals 0 then we get the first signal and if it equals 1 we get the second signal.
cfd :: (Num a, SigSpace a) => Sig -> a -> a -> a
cfd coeff a b = (1 - coeff) `mul` a + coeff `mul` b

genCfds :: a -> (Sig -> a -> a -> a) -> [Sig] -> [a] -> a
genCfds zero mixFun cs xs = case xs of
    []   -> zero
    a:as -> foldl (\x f -> f x) a $ zipWith mix' cs as
    where mix' c a b = mixFun c b a

-- | Bilinear interpolation for four signals.
-- The signals are placed in the corners of the unit square.
-- The first two signals are the xy coordinates in the square.
--
-- > cfd4 x y a b c d
--
-- * (0, 0) is for a
--
-- * (1, 0) is for b
--
-- * (1, 1) is for c
--
-- * (0, 1) is for d
cfd4 :: (Num a, SigSpace a) => Sig -> Sig -> a -> a -> a -> a -> a
cfd4 x y a b c d = sum $ zipWith mul [(1 - x) * (1 - y), x * (1 - y) , x * y, (1 - x) * y] [a, b, c, d]

-- | Generic crossfade for n coefficients and n+1 signals.
--
-- > cfds coeffs sigs
cfds :: (Num a, SigSpace a) => [Sig] -> [a] -> a
cfds = genCfds 0 cfd


instance SigSpace Sig   where  mapSig = id
instance BindSig  Sig   where  bindSig = id

instance (SigSpace a1, SigSpace a2) => SigSpace (a1, a2) where  mapSig  f (a1, a2) = (mapSig f a1, mapSig f a2)
instance (BindSig a1, BindSig a2) => BindSig  (a1, a2) where  bindSig f (a1, a2) = (,) <$> bindSig f a1 <*> bindSig f a2

instance (SigSpace a1, SigSpace a2, SigSpace a3) => SigSpace (a1, a2, a3) where mapSig  f (a1, a2, a3) = (mapSig f a1, mapSig f a2, mapSig f a3)
instance (BindSig a1, BindSig a2, BindSig a3) => BindSig  (a1, a2, a3) where bindSig f (a1, a2, a3) = (,,) <$> bindSig f a1 <*> bindSig f a2 <*> bindSig f a3

instance (SigSpace a1, SigSpace a2, SigSpace a3, SigSpace a4) => SigSpace (a1, a2, a3, a4) where mapSig  f (a1, a2, a3, a4) = (mapSig f a1, mapSig f a2, mapSig f a3, mapSig f a4)
instance (BindSig a1, BindSig a2, BindSig a3, BindSig a4) => BindSig  (a1, a2, a3, a4) where bindSig f (a1, a2, a3, a4) = (,,,) <$> bindSig f a1 <*> bindSig f a2 <*> bindSig f a3 <*> bindSig f a4

instance (SigSpace a1, SigSpace a2, SigSpace a3, SigSpace a4, SigSpace a5) => SigSpace (a1, a2, a3, a4, a5) where mapSig  f (a1, a2, a3, a4, a5) = (mapSig f a1, mapSig f a2, mapSig f a3, mapSig f a4, mapSig f a5)
instance (BindSig a1, BindSig a2, BindSig a3, BindSig a4, BindSig a5) => BindSig  (a1, a2, a3, a4, a5) where bindSig f (a1, a2, a3, a4, a5) = (,,,,) <$> bindSig f a1 <*> bindSig f a2 <*> bindSig f a3 <*> bindSig f a4 <*> bindSig f a5

instance (SigSpace a1, SigSpace a2, SigSpace a3, SigSpace a4, SigSpace a5, SigSpace a6) => SigSpace (a1, a2, a3, a4, a5, a6) where mapSig  f (a1, a2, a3, a4, a5, a6) = (mapSig f a1, mapSig f a2, mapSig f a3, mapSig f a4, mapSig f a5, mapSig f a6)
instance (BindSig a1, BindSig a2, BindSig a3, BindSig a4, BindSig a5, BindSig a6) => BindSig  (a1, a2, a3, a4, a5, a6) where bindSig f (a1, a2, a3, a4, a5, a6) = (,,,,,) <$> bindSig f a1 <*> bindSig f a2 <*> bindSig f a3 <*> bindSig f a4 <*> bindSig f a5 <*> bindSig f a6

instance (SigSpace a1, SigSpace a2, SigSpace a3, SigSpace a4, SigSpace a5, SigSpace a6, SigSpace a7) => SigSpace (a1, a2, a3, a4, a5, a6, a7) where mapSig  f (a1, a2, a3, a4, a5, a6, a7) = (mapSig f a1, mapSig f a2, mapSig f a3, mapSig f a4, mapSig f a5, mapSig f a6, mapSig f a7)
instance (BindSig a1, BindSig a2, BindSig a3, BindSig a4, BindSig a5, BindSig a6, BindSig a7) => BindSig  (a1, a2, a3, a4, a5, a6, a7) where bindSig f (a1, a2, a3, a4, a5, a6, a7) = (,,,,,,) <$> bindSig f a1 <*> bindSig f a2 <*> bindSig f a3 <*> bindSig f a4 <*> bindSig f a5 <*> bindSig f a6 <*> bindSig f a7

instance (SigSpace a1, SigSpace a2, SigSpace a3, SigSpace a4, SigSpace a5, SigSpace a6, SigSpace a7, SigSpace a8) => SigSpace (a1, a2, a3, a4, a5, a6, a7, a8) where mapSig  f (a1, a2, a3, a4, a5, a6, a7, a8) = (mapSig f a1, mapSig f a2, mapSig f a3, mapSig f a4, mapSig f a5, mapSig f a6, mapSig f a7, mapSig f a8)
instance (BindSig a1, BindSig a2, BindSig a3, BindSig a4, BindSig a5, BindSig a6, BindSig a7, BindSig a8) => BindSig  (a1, a2, a3, a4, a5, a6, a7, a8) where bindSig f (a1, a2, a3, a4, a5, a6, a7, a8) = (,,,,,,,) <$> bindSig f a1 <*> bindSig f a2 <*> bindSig f a3 <*> bindSig f a4 <*> bindSig f a5 <*> bindSig f a6 <*> bindSig f a7 <*> bindSig f a8

instance SigSpace a => SigSpace (SE a) where  mapSig  f = fmap (mapSig f)
instance BindSig  a => BindSig  (SE a) where  bindSig f = fmap (bindSig f)

----------------------------------------------------------------------------------------------------------

-- | Converts stereosignal to mono with function mean.
toMono :: (Sig, Sig) -> Sig
toMono (a, b) = 0.5 * a + 0.5 * b

instance SigSpace2 Sig   where  mapSig2  f a = toMono $ f (a, a)
instance BindSig2  Sig   where  bindSig2 f a = fmap toMono $ f (a, a)

instance SigSpace2 (Sig, Sig) where  mapSig2  = id
instance BindSig2  (Sig, Sig) where  bindSig2 = id

instance SigSpace2 (Sig, Sig, Sig) where
    mapSig2  f (a1, a2, a3) = (b1, b2, toMono (b3, b4))
        where
            (b1, b2, b3, b4) = mapSig2 f (a1, a2, a3, a4)
            a4 = a3

instance BindSig2  (Sig, Sig, Sig) where
    bindSig2 f (a1, a2, a3) = do
        (b1, b2, b3, b4) <- bindSig2 f (a1, a2, a3, a4)
        return (b1, b2, toMono (b3, b4))
        where
            a4 = a3

instance SigSpace2 (Sig, Sig, Sig, Sig) where
    mapSig2  f (a1, a2, a3, a4) = (b1, b2, b3, b4)
        where
            (b1, b2) = f (a1, a2)
            (b3, b4) = f (a3, a4)

instance BindSig2  (Sig, Sig, Sig, Sig) where
    bindSig2 f (a1, a2, a3, a4) = do
            (b1, b2) <- f (a1, a2)
            (b3, b4) <- f (a3, a4)
            return (b1, b2, b3, b4)

instance SigSpace2 (Sig, Sig, Sig, Sig, Sig) where
    mapSig2  f (a1, a2, a3, a4, a5) = (b1, b2, b3, b4, toMono (b5, b6))
        where
            (b1, b2, b3, b4, b5, b6) = mapSig2 f (a1, a2, a3, a4, a5, a6)
            a6 = a5

instance BindSig2 (Sig, Sig, Sig, Sig, Sig) where
    bindSig2 f (a1, a2, a3, a4, a5) = do
        (b1, b2, b3, b4, b5, b6) <- bindSig2 f (a1, a2, a3, a4, a5, a6)
        return (b1, b2, b3, b4, toMono (b5, b6))
        where
            a6 = a5

instance SigSpace2 (Sig, Sig, Sig, Sig, Sig, Sig) where
    mapSig2  f (a1, a2, a3, a4, a5, a6) = (b1, b2, b3, b4, b5, b6)
        where
            (b1, b2, b3, b4) = mapSig2 f (a1, a2, a3, a4)
            (b5, b6) = f (a5, a6)

instance BindSig2  (Sig, Sig, Sig, Sig, Sig, Sig) where
    bindSig2 f (a1, a2, a3, a4, a5, a6) = do
        (b1, b2, b3, b4) <- bindSig2 f (a1, a2, a3, a4)
        (b5, b6) <- f (a5, a6)
        return (b1, b2, b3, b4, b5, b6)

instance SigSpace2 (Sig, Sig, Sig, Sig, Sig, Sig, Sig) where
    mapSig2  f (a1, a2, a3, a4, a5, a6, a7) = (b1, b2, b3, b4, b5, b6, toMono (b7, b8))
        where
            (b1, b2, b3, b4, b5, b6, b7, b8) = mapSig2 f (a1, a2, a3, a4, a5, a6, a7, a8)
            a8 = a7

instance BindSig2  (Sig, Sig, Sig, Sig, Sig, Sig, Sig) where
    bindSig2 f (a1, a2, a3, a4, a5, a6, a7) = do
        (b1, b2, b3, b4, b5, b6, b7, b8) <- bindSig2 f (a1, a2, a3, a4, a5, a6, a7, a8)
        return (b1, b2, b3, b4, b5, b6, toMono (b7, b8))
        where
            a8 = a7

instance SigSpace2 (Sig, Sig, Sig, Sig, Sig, Sig, Sig, Sig) where
    mapSig2  f (a1, a2, a3, a4, a5, a6, a7, a8) = (b1, b2, b3, b4, b5, b6, b7, b8)
        where
            (b1, b2, b3, b4, b5, b6) = mapSig2 f (a1, a2, a3, a4, a5, a6)
            (b7, b8) = f (a7, a8)

instance BindSig2  (Sig, Sig, Sig, Sig, Sig, Sig, Sig, Sig) where
    bindSig2 f (a1, a2, a3, a4, a5, a6, a7, a8) = do
        (b1, b2, b3, b4, b5, b6) <- bindSig2 f (a1, a2, a3, a4, a5, a6)
        (b7, b8) <- f (a7, a8)
        return (b1, b2, b3, b4, b5, b6, b7, b8)

instance SigSpace2 (Sig2, Sig2) where  mapSig2  f (a1, a2) = (mapSig2 f a1, mapSig2 f a2)
instance BindSig2  (Sig2, Sig2) where  bindSig2 f (a1, a2) = (,) <$> bindSig2 f a1 <*> bindSig2 f a2

instance SigSpace2 (Sig2, Sig2, Sig2) where  mapSig2  f (a1, a2, a3) = (mapSig2 f a1, mapSig2 f a2, mapSig2 f a3)
instance BindSig2  (Sig2, Sig2, Sig2) where  bindSig2 f (a1, a2, a3) = (,,) <$> bindSig2 f a1 <*> bindSig2 f a2 <*> bindSig2 f a3

instance SigSpace2 (Sig2, Sig2, Sig2, Sig2) where  mapSig2  f (a1, a2, a3, a4) = (mapSig2 f a1, mapSig2 f a2, mapSig2 f a3, mapSig2 f a4)
instance BindSig2  (Sig2, Sig2, Sig2, Sig2) where  bindSig2 f (a1, a2, a3, a4) = (,,,) <$> bindSig2 f a1 <*> bindSig2 f a2 <*> bindSig2 f a3 <*> bindSig2 f a4

instance SigSpace2 (Sig2, Sig2, Sig2, Sig2, Sig2) where  mapSig2  f (a1, a2, a3, a4, a5) = (mapSig2 f a1, mapSig2 f a2, mapSig2 f a3, mapSig2 f a4, mapSig2 f a5)
instance BindSig2  (Sig2, Sig2, Sig2, Sig2, Sig2) where  bindSig2 f (a1, a2, a3, a4, a5) = (,,,,) <$> bindSig2 f a1 <*> bindSig2 f a2 <*> bindSig2 f a3 <*> bindSig2 f a4 <*> bindSig2 f a5

instance SigSpace2 (Sig2, Sig2, Sig2, Sig2, Sig2, Sig2) where  mapSig2  f (a1, a2, a3, a4, a5, a6) = (mapSig2 f a1, mapSig2 f a2, mapSig2 f a3, mapSig2 f a4, mapSig2 f a5, mapSig2 f a6)
instance BindSig2  (Sig2, Sig2, Sig2, Sig2, Sig2, Sig2) where  bindSig2 f (a1, a2, a3, a4, a5, a6) = (,,,,,) <$> bindSig2 f a1 <*> bindSig2 f a2 <*> bindSig2 f a3 <*> bindSig2 f a4 <*> bindSig2 f a5  <*> bindSig2 f a6

instance SigSpace2 (Sig2, Sig2, Sig2, Sig2, Sig2, Sig2, Sig2) where  mapSig2  f (a1, a2, a3, a4, a5, a6, a7) = (mapSig2 f a1, mapSig2 f a2, mapSig2 f a3, mapSig2 f a4, mapSig2 f a5, mapSig2 f a6, mapSig2 f a7)
instance BindSig2  (Sig2, Sig2, Sig2, Sig2, Sig2, Sig2, Sig2) where  bindSig2 f (a1, a2, a3, a4, a5, a6, a7) = (,,,,,,) <$> bindSig2 f a1 <*> bindSig2 f a2 <*> bindSig2 f a3 <*> bindSig2 f a4 <*> bindSig2 f a5  <*> bindSig2 f a6 <*> bindSig2 f a7

instance SigSpace2 (Sig2, Sig2, Sig2, Sig2, Sig2, Sig2, Sig2, Sig2) where  mapSig2  f (a1, a2, a3, a4, a5, a6, a7, a8) = (mapSig2 f a1, mapSig2 f a2, mapSig2 f a3, mapSig2 f a4, mapSig2 f a5, mapSig2 f a6, mapSig2 f a7, mapSig2 f a8)
instance BindSig2  (Sig2, Sig2, Sig2, Sig2, Sig2, Sig2, Sig2, Sig2) where  bindSig2 f (a1, a2, a3, a4, a5, a6, a7, a8) = (,,,,,,,) <$> bindSig2 f a1 <*> bindSig2 f a2 <*> bindSig2 f a3 <*> bindSig2 f a4 <*> bindSig2 f a5  <*> bindSig2 f a6 <*> bindSig2 f a7 <*> bindSig2 f a8

instance SigSpace2 (Sig8, Sig8) where  mapSig2  f (a1, a2) = (mapSig2 f a1, mapSig2 f a2)
instance BindSig2  (Sig8, Sig8) where  bindSig2 f (a1, a2) = (,) <$> bindSig2 f a1 <*> bindSig2 f a2

instance SigSpace2 (Sig8, Sig8, Sig8, Sig8) where  mapSig2  f (a1, a2, a3, a4) = (mapSig2 f a1, mapSig2 f a2, mapSig2 f a3, mapSig2 f a4)
instance BindSig2  (Sig8, Sig8, Sig8, Sig8) where  bindSig2 f (a1, a2, a3, a4) = (,,,) <$> bindSig2 f a1 <*> bindSig2 f a2 <*> bindSig2 f a3 <*> bindSig2 f a4

instance SigSpace2 (SE Sig) where  mapSig2  f = fmap (mapSig2 f)
instance BindSig2  (SE Sig) where  bindSig2 f = fmap (bindSig2 f)

instance SigSpace2 (SE (Sig, Sig)) where mapSig2  f = fmap (mapSig2 f)
instance BindSig2  (SE (Sig, Sig)) where bindSig2 f = fmap (bindSig2 f)

instance SigSpace2 (SE (Sig, Sig, Sig)) where mapSig2  f = fmap (mapSig2 f)
instance BindSig2  (SE (Sig, Sig, Sig)) where bindSig2 f = fmap (bindSig2 f)

instance SigSpace2 (SE (Sig, Sig, Sig, Sig)) where mapSig2  f = fmap (mapSig2 f)
instance BindSig2  (SE (Sig, Sig, Sig, Sig)) where bindSig2 f = fmap (bindSig2 f)

----------------------------------------------------------------------------------------------------------
-- numeric instances

-- Num

instance Num (SE Sig) where
    (+) = liftA2 (+)
    (*) = liftA2 (*)
    negate = fmap negate

    fromInteger = return . fromInteger
    signum = fmap signum
    abs = fmap abs

instance Num (SE (Sig, Sig)) where
    (+) = liftA2 (+)
    (*) = liftA2 (*)
    negate = fmap negate

    fromInteger = return . fromInteger
    signum = fmap signum
    abs = fmap abs

instance Num (SE (Sig, Sig, Sig)) where
    (+) = liftA2 (+)
    (*) = liftA2 (*)
    negate = fmap negate

    fromInteger = return . fromInteger
    signum = fmap signum
    abs = fmap abs

instance Num (SE (Sig, Sig, Sig, Sig)) where
    (+) = liftA2 (+)
    (*) = liftA2 (*)
    negate = fmap negate

    fromInteger = return . fromInteger
    signum = fmap signum
    abs = fmap abs

instance Num (a -> Sig) where
    (+) = liftA2 (+)
    (*) = liftA2 (*)
    negate = fmap negate

    fromInteger = return . fromInteger
    signum = fmap signum
    abs = fmap abs

instance Num (a -> (Sig, Sig)) where
    (+) = liftA2 (+)
    (*) = liftA2 (*)
    negate = fmap negate

    fromInteger = return . fromInteger
    signum = fmap signum
    abs = fmap abs

instance Num (a -> (Sig, Sig, Sig)) where
    (+) = liftA2 (+)
    (*) = liftA2 (*)
    negate = fmap negate

    fromInteger = return . fromInteger
    signum = fmap signum
    abs = fmap abs

instance Num (a -> (Sig, Sig, Sig, Sig)) where
    (+) = liftA2 (+)
    (*) = liftA2 (*)
    negate = fmap negate

    fromInteger = return . fromInteger
    signum = fmap signum
    abs = fmap abs


instance Num (a -> SE Sig) where
    (+) = liftA2 (+)
    (*) = liftA2 (*)
    negate = fmap negate

    fromInteger = return . fromInteger
    signum = fmap signum
    abs = fmap abs

instance Num (a -> SE (Sig, Sig)) where
    (+) = liftA2 (+)
    (*) = liftA2 (*)
    negate = fmap negate

    fromInteger = return . fromInteger
    signum = fmap signum
    abs = fmap abs

instance Num (a -> SE (Sig, Sig, Sig)) where
    (+) = liftA2 (+)
    (*) = liftA2 (*)
    negate = fmap negate

    fromInteger = return . fromInteger
    signum = fmap signum
    abs = fmap abs

instance Num (a -> SE (Sig, Sig, Sig, Sig)) where
    (+) = liftA2 (+)
    (*) = liftA2 (*)
    negate = fmap negate

    fromInteger = return . fromInteger
    signum = fmap signum
    abs = fmap abs

-- Fractional


instance Fractional (SE Sig) where
    (/) = liftA2 (/)
    fromRational = return . fromRational

instance Fractional (SE (Sig, Sig)) where
    (/) = liftA2 (/)
    fromRational = return . fromRational

instance Fractional (SE (Sig, Sig, Sig)) where
    (/) = liftA2 (/)
    fromRational = return . fromRational

instance Fractional (SE (Sig, Sig, Sig, Sig)) where
    (/) = liftA2 (/)
    fromRational = return . fromRational

instance Fractional (a -> SE Sig) where
    (/) = liftA2 (/)
    fromRational = return . fromRational

instance Fractional (a -> SE (Sig, Sig)) where
    (/) = liftA2 (/)
    fromRational = return . fromRational

instance Fractional (a -> SE (Sig, Sig, Sig)) where
    (/) = liftA2 (/)
    fromRational = return . fromRational

instance Fractional (a -> SE (Sig, Sig, Sig, Sig)) where
    (/) = liftA2 (/)
    fromRational = return . fromRational

instance Fractional (a -> Sig) where
    (/) = liftA2 (/)
    fromRational = return . fromRational

instance Fractional (a -> (Sig, Sig)) where
    (/) = liftA2 (/)
    fromRational = return . fromRational

instance Fractional (a -> (Sig, Sig, Sig)) where
    (/) = liftA2 (/)
    fromRational = return . fromRational

instance Fractional (a -> (Sig, Sig, Sig, Sig)) where
    (/) = liftA2 (/)
    fromRational = return . fromRational

-----------------------------------------------------------------------
-----------------------------------------------------------------------

class SigSpace b => At a b c where
    type AtOut a b c :: *
    at :: (a -> b) -> c -> AtOut a b c

instance SigSpace a => At Sig Sig a where
    type AtOut Sig Sig a = a
    at f a = mapSig f a

------------------------------------------------------
-- for (Sig -> SE Sig)

instance At Sig (SE Sig) Sig where
    type AtOut Sig (SE Sig) Sig = SE Sig
    at f a = f a

instance At Sig (SE Sig) Sig2 where
    type AtOut Sig (SE Sig) Sig2 = SE Sig2
    at f a = bindSig f a

instance At Sig (SE Sig) Sig3 where
    type AtOut Sig (SE Sig) Sig3 = SE Sig3
    at f a = bindSig f a

instance At Sig (SE Sig) Sig4 where
    type AtOut Sig (SE Sig) Sig4 = SE Sig4
    at f a = bindSig f a

instance At Sig (SE Sig) (SE Sig) where
    type AtOut Sig (SE Sig) (SE Sig) = SE Sig
    at f a = join $ bindSig f a

instance At Sig (SE Sig) (SE Sig2) where
    type AtOut Sig (SE Sig) (SE Sig2) = SE Sig2
    at f a = join $ bindSig f a

instance At Sig (SE Sig) (SE Sig3) where
    type AtOut Sig (SE Sig) (SE Sig3) = SE Sig3
    at f a = join $ bindSig f a

instance At Sig (SE Sig) (SE Sig4) where
    type AtOut Sig (SE Sig) (SE Sig4) = SE Sig4
    at f a = join $ bindSig f a

-----------------------------------------------------
-- mono to stereo

instance At Sig Sig2 Sig where
    type AtOut Sig Sig2 Sig = Sig2
    at f a = f a

instance At Sig Sig2 (SE Sig) where
    type AtOut Sig Sig2 (SE Sig) = SE Sig2
    at f a = fmap f a

instance At Sig Sig2 Sig2 where
    type AtOut Sig Sig2 Sig2 = Sig2
    at f a = 0.5 * (f (fst a) + f (snd a))

instance At Sig Sig2 (SE Sig2) where
    type AtOut Sig Sig2 (SE Sig2) = SE Sig2
    at f a = fmap (at f) a

---------------------------------------------------------

---------------------------------------------------------
-- Sig2 -> Sig2

fromMono a = (a, a)

instance At Sig2 Sig2 Sig where
    type AtOut Sig2 Sig2 Sig = Sig2
    at f a = f $ fromMono a

instance At Sig2 Sig2 Sig2 where
    type AtOut Sig2 Sig2 Sig2 = Sig2
    at f a = f a

instance At Sig2 Sig2 (SE Sig) where
    type AtOut Sig2 Sig2 (SE Sig) = SE Sig2
    at f a = fmap (f . fromMono) a

instance At Sig2 Sig2 (SE Sig2) where
    type AtOut Sig2 Sig2 (SE Sig2) = SE Sig2
    at f a = fmap f a

---------------------------------------------
-- Sig2 -> SE Sig2

instance At Sig2 (SE Sig2) Sig where
    type AtOut Sig2 (SE Sig2) Sig = SE Sig2
    at f a = f $ fromMono a

instance At Sig2 (SE Sig2) Sig2 where
    type AtOut Sig2 (SE Sig2) Sig2 = SE Sig2
    at f a = f a

instance At Sig2 (SE Sig2) (SE Sig) where
    type AtOut Sig2 (SE Sig2) (SE Sig) = SE Sig2
    at f a = (f . fromMono) =<< a

instance At Sig2 (SE Sig2) (SE Sig2) where
    type AtOut Sig2 (SE Sig2) (SE Sig2) = SE Sig2
    at f a = f =<< a

-----------------------------------------------------------------------
-----------------------------------------------------------------------
-- MixAt

-- | It applies an effect and mixes the processed signal with original one.
-- The first argument is for proportion of dry/wet (original/processed).
-- It's like @at@ but it allows to balance processed signal with original one.
class (SigSpace b, At a b c) => MixAt a b c where
    mixAt :: Sig -> (a -> b) -> c -> AtOut a b c

---------------------------------------------------

instance SigSpace a => MixAt Sig Sig a where
    mixAt k f a = mapSig (\x -> cfd k x (f x)) a

------------------------------------------------------
-- for (Sig -> SE Sig)

instance MixAt Sig (SE Sig) Sig where
    mixAt k f dry = do
        wet <- f dry
        return $ cfd k dry wet

instance MixAt Sig (SE Sig) Sig2 where
    mixAt k f (dry1, dry2) = do
        wet1 <- f dry1
        wet2 <- f dry2
        return $ cfd k (dry1, dry2) (wet1, wet2)

instance MixAt Sig (SE Sig) Sig3 where
    mixAt k f (dry1, dry2, dry3) = do
        wet1 <- f dry1
        wet2 <- f dry2
        wet3 <- f dry3
        return $ cfd k (dry1, dry2, dry3) (wet1, wet2, wet3)

instance MixAt Sig (SE Sig) Sig4 where
    mixAt k f (dry1, dry2, dry3, dry4) = do
        wet1 <- f dry1
        wet2 <- f dry2
        wet3 <- f dry3
        wet4 <- f dry4
        return $ cfd k (dry1, dry2, dry3, dry4) (wet1, wet2, wet3, wet4)

instance MixAt Sig (SE Sig) (SE Sig) where
    mixAt k f dry = do
        dry1 <- dry
        wet1 <- f dry1
        return $ cfd k dry1 wet1

instance MixAt Sig (SE Sig) (SE Sig2) where
    mixAt k f dry = do
        (dry1, dry2) <- dry
        wet1 <- f dry1
        wet2 <- f dry2
        return $ cfd k (dry1, dry2) (wet1, wet2)

instance MixAt Sig (SE Sig) (SE Sig3) where
    mixAt k f dry = do
        (dry1, dry2, dry3) <- dry
        wet1 <- f dry1
        wet2 <- f dry2
        wet3 <- f dry3
        return $ cfd k (dry1, dry2, dry3) (wet1, wet2, wet3)

instance MixAt Sig (SE Sig) (SE Sig4) where
    mixAt k f dry = do
        (dry1, dry2, dry3, dry4) <- dry
        wet1 <- f dry1
        wet2 <- f dry2
        wet3 <- f dry3
        wet4 <- f dry4
        return $ cfd k (dry1, dry2, dry3, dry4) (wet1, wet2, wet3, wet4)

-----------------------------------------------------
-- mono to stereo

instance MixAt Sig Sig2 Sig where
    mixAt k f dry = cfd k (dry, dry) wet
        where wet = f dry

instance MixAt Sig Sig2 (SE Sig) where
    mixAt k f dry = fmap (\x -> cfd k (x, x) (f x)) dry

instance MixAt Sig Sig2 Sig2 where
    mixAt k f dry = cfd k dry wet
        where wet = 0.5 * (f (fst dry) + f (snd dry))

instance MixAt Sig Sig2 (SE Sig2) where
    mixAt k f dry = do
        (dry1, dry2) <- dry
        let wet = 0.5 * (f dry1 + f dry2)
        return $ cfd k (dry1, dry2) wet

---------------------------------------------------------

---------------------------------------------------------
-- Sig2 -> Sig2

instance MixAt Sig2 Sig2 Sig where
    mixAt k f dry1 = cfd k dry wet
        where
            dry = fromMono dry1
            wet = f dry

instance MixAt Sig2 Sig2 Sig2 where
    mixAt k f dry = cfd k dry wet
        where
            wet = f dry

instance MixAt Sig2 Sig2 (SE Sig) where
    mixAt k f dry1 = do
        dry <- fmap fromMono dry1
        let wet = f dry
        return $ cfd k dry wet

instance MixAt Sig2 Sig2 (SE Sig2) where
    mixAt k f drySe = do
        dry <- drySe
        let wet = f dry
        return $ cfd k dry wet


---------------------------------------------
-- Sig2 -> SE Sig2

instance MixAt Sig2 (SE Sig2) Sig where
    mixAt k f dry1 = do
        wet <- f dry
        return $ cfd k dry wet
        where
            dry = fromMono dry1

instance MixAt Sig2 (SE Sig2) Sig2 where
    mixAt k f dry = do
        wet <- f dry
        return $ cfd k dry wet

instance MixAt Sig2 (SE Sig2) (SE Sig) where
    mixAt k f dry1 = do
        dry <- fmap fromMono dry1
        wet <- f dry
        return $ cfd k dry wet

instance MixAt Sig2 (SE Sig2) (SE Sig2) where
    mixAt k f drySe = do
        dry <- drySe
        wet <- f dry
        return $ cfd k dry wet