-- |
-- Module      :  DobutokO.Sound.Effects.MCompand
-- Copyright   :  (c) OleksandrZhabenko 2020
-- License     :  MIT
-- Stability   :  Experimental
-- Maintainer  :  olexandr543@yahoo.com
--
-- Helps to create experimental music. 
-- Can be used for applying the SoX \"compand\" and \"mcompand\" effects. 
-- 

{-# OPTIONS_GHC -threaded #-}
{-# LANGUAGE CPP, FlexibleInstances #-}

module DobutokO.Sound.Effects.MCompand where

#ifdef __GLASGOW_HASKELL__
#if __GLASGOW_HASKELL__>=710
/* code that applies only to GHC 7.10.* and higher versions */
import GHC.Base (mconcat)
#endif
#endif
import Numeric (showFFloat)
import Data.List (intersperse)
import DobutokO.Sound.One

#ifdef __GLASGOW_HASKELL__
#if __GLASGOW_HASKELL__==708
/* code that applies only to GHC 7.8.* */
mconcat = concat
#endif
#endif

data FloatE a = InfP | InfM | Float1 a deriving Eq

instance Show (FloatE Float) where
  show InfP = "inf "
  show InfM = "-inf "
  show (Float1 x) = showFFloat Nothing x " "

type Float3 = FloatE Float

floatE1 :: FloatE a -> Maybe a
floatE1 (Float1 x) = Just x
floatE1 _ = Nothing

floatESet1 :: a -> FloatE a
floatESet1 = Float1

isInfS :: FloatE a -> Bool
isInfS (Float1 _)  = False
isInfS _ = True

isInfP :: FloatE a -> Bool
isInfP InfP = True
isInfP _ = False

isInfM :: FloatE a -> Bool
isInfM InfM = True
isInfM _ = False

isFloatE1 :: FloatE a -> Bool
isFloatE1 (Float1 _) = True
isFloatE1 _ = False

absEP :: Float3 -> Float3
absEP (Float1 x) = Float1 (abs x)
absEP _ = InfP

absEN :: Float3 -> Float3
absEN (Float1 x) = Float1 (-abs x)
absEN _ = InfM

data CompandTail a b = N | CT1 a | CT2 a a | CT3 a a b deriving Eq

instance Show (CompandTail Float3 Float) where
  show N = " "
  show (CT1 x) = mconcat [show x, " "]
  show (CT2 x y) = mconcat [show x, " ", show (absEN y), " "]
  show (CT3 x y z) = mconcat [show x, " ", show (absEN y), " ",  showFFloat Nothing (abs z) " "]

type CompTail = CompandTail Float3 Float

compandTail1 :: CompandTail a b -> Maybe (One2 a)
compandTail1 (CT1 x) = Just (O21 x)
compandTail1 (CT2 x y) = Just (O22 x y)
compandTail1 (CT3 x y _) = Just (O22 x y)
compandTail1 _ = Nothing

compandTail2 :: CompandTail a b -> Maybe b
compandTail2 (CT3 _ _ y) = Just y
compandTail2 _ = Nothing

compandTailSet1 :: One2 a -> CompandTail a b -> CompandTail a b
compandTailSet1 (O21 x) (CT2 _ y) = CT2 x y
compandTailSet1 (O21 x) (CT3 _ y z) = CT3 x y z
compandTailSet1 (O22 x y) (CT2 _ _) = CT2 x y
compandTailSet1 (O22 x y) (CT3 _ _ z) = CT3 x y z
compandTailSet1 (O21 x) _ = CT1 x
compandTailSet1 (O22 x y) _ = CT2 x y

compandTailSet2 :: b -> CompandTail a b -> Maybe (CompandTail a b)
compandTailSet2 z (CT2 x y) = Just (CT3 x y z)
compandTailSet2 z (CT3 x y _) = Just (CT3 x y z)
compandTailSet2 _ _ = Nothing

showCTQ :: CompTail -> [String]
showCTQ = words . show

data Pair a = AD a a deriving Eq

instance Show (Pair Float) where
  show (AD x y) = mconcat [showFFloat Nothing (abs x) ",",showFFloat Nothing (abs y) ""]

type Pr = Pair Float

pair1 :: Pair a -> a
pair1 (AD x _) = x

pair2 :: Pair a -> a
pair2 (AD _ y) = y

pairSet1 :: a -> Pair a -> Pair a
pairSet1 x (AD _ y) = AD x y

pairSet2 :: a -> Pair a -> Pair a
pairSet2 y (AD x _) = AD x y

data AtDe a = ADM a [a] deriving Eq

instance Show (AtDe Pr) where
  show (ADM (AD x y) zs)
    | null zs = mconcat [show (AD x y), " "]
    | otherwise = mconcat [show (AD x y), ",", mconcat . intersperse "," . map show $ zs, " "]

type AtD2 = AtDe Pr

atDe1 :: AtDe a -> a
atDe1 (ADM x _) = x

atDe2 :: AtDe a -> [a]
atDe2 (ADM _ xs) = xs

atDeSet1 :: a -> AtDe a -> AtDe a
atDeSet1 x (ADM _ xs) = ADM x xs

atDeSet2 :: [a] -> AtDe a -> AtDe a
atDeSet2 xs (ADM x _) = ADM x xs

showADQ :: AtD2 -> [String]
showADQ = words . show

data Neg a = NG a deriving Eq

instance Show (Neg Float) where
  show (NG x) = showFFloat Nothing (-abs x) " "

type Ng1 = Neg Float

neg1 :: Neg a -> a
neg1 (NG x) = x

negSet1 :: a -> Neg a
negSet1 = NG

type AtDeNF = AtDe (Neg Float)

instance Show AtDeNF where
  show (ADM (NG x) zs)
    | null zs = mconcat [show (NG x), " "]
    | otherwise = mconcat [show (NG x), ",", mconcat . intersperse "," . map show $ zs, " "]

data SoftKnee a = NK | SK a deriving Eq

instance Show (SoftKnee Float) where
  show (SK x) = showFFloat Nothing x ":"
  show _ = ""

type SoftK1 = SoftKnee Float

softKneeC :: SoftKnee a -> String
softKneeC NK = "NK"
softKneeC _ = "SK"

softKnee1 :: SoftKnee a -> Maybe a
softKnee1 (SK x)   = Just x
softKnee1 _ = Nothing

softKneeSet1 :: a -> SoftKnee a
softKneeSet1 = SK

data Compand a b c d = CP3 a b c | CP4 a b c d deriving Eq

instance Show (Compand AtD2 SoftK1 AtDeNF CompTail) where
  show (CP3 x y z) = mconcat ["compand ", show x, show y, show z]
  show (CP4 x y z t) = mconcat ["compand ", show x, show y, show z, show t]

type Compand4 = Compand AtD2 SoftK1 AtDeNF CompTail

compand1 :: Compand a b c d -> a
compand1 (CP3 x _ _) = x
compand1 (CP4 x _ _ _) = x

compand2 :: Compand a b c d -> b
compand2 (CP3 _ y _) = y
compand2 (CP4 _ y _ _) = y

compand3 :: Compand a b c d -> c
compand3 (CP3 _ _ z) = z
compand3 (CP4 _ _ z _) = z

compand4 :: Compand a b c d -> Maybe d
compand4 (CP4 _ _ _ t) = Just t
compand4 _ = Nothing

compandSet1 :: a -> Compand a b c d -> Compand a b c d
compandSet1 x (CP3 _ y z) = CP3 x y z
compandSet1 x (CP4 _ y z t) = CP4 x y z t

compandSet2 :: b -> Compand a b c d -> Compand a b c d
compandSet2 y (CP3 x _ z) = CP3 x y z
compandSet2 y (CP4 x _ z t) = CP4 x y z t

compandSet3 :: c -> Compand a b c d -> Compand a b c d
compandSet3 z (CP3 x y _) = CP3 x y z
compandSet3 z (CP4 x y _ t) = CP4 x y z t

compandSet4 :: d -> Compand a b c d -> Compand a b c d
compandSet4 t (CP3 x y z) = CP4 x y z t
compandSet4 t (CP4 x y z _) = CP4 x y z t

showCMPDQ :: Compand4 -> [String]
showCMPDQ = words . show

data KFreq a = Fr a | KFr a deriving Eq

instance Show (KFreq Int) where
  show (Fr n) = mconcat [show (abs n), " "]
  show (KFr n) = mconcat [show (abs n), "k "]

instance Show (KFreq Float) where
  show (Fr x) = mconcat [showFFloat Nothing (abs x) " "]
  show (KFr x) = mconcat [showFFloat Nothing (abs x) "k "]

type KFQ = KFreq Int

kFreqC :: KFQ -> String
kFreqC (Fr _)   = "Fr"
kFreqC _ = "KFr"

kFreq1 :: KFQ -> Int
kFreq1 (Fr n) = n
kFreq1 (KFr n) = fromIntegral 1000 * n

kFreqSet1 :: Int -> KFQ -> KFreq Float
kFreqSet1 n (Fr _) = Fr (fromIntegral n)
kFreqSet1 n _ = KFr (fromIntegral n / 1000.0)

data FreqComp a b = FrCmp a b deriving Eq

instance Show (FreqComp KFQ String) where
  show (FrCmp x ys) = mconcat [show x, show ys, " "]

type FrCmpnd2 = FreqComp KFQ String

freqComp1 :: FreqComp a b -> a
freqComp1 (FrCmp x _) = x

freqComp2 :: FreqComp a b -> b
freqComp2 (FrCmp _ y) = y

freqCompSet1 :: a -> FreqComp a b -> FreqComp a b
freqCompSet1 x (FrCmp _ y) = FrCmp x y

freqCompSet2 :: b -> FreqComp a b -> FreqComp a b
freqCompSet2 y (FrCmp x _) = FrCmp x y

showFC :: FrCmpnd2 -> [String]
showFC = words . show

data MCompand a b = MCN1 a | MCNM a [b] deriving Eq

instance Show (MCompand String FrCmpnd2) where
  show (MCN1 xs) = mconcat ["mcompand ", show xs, " "]
  show (MCNM xs ys) = mconcat ["mcompand ", show xs, " ", mconcat . map show $ ys]

type MComPand2 = MCompand String FrCmpnd2

mCompandC :: MCompand a b -> String
mCompandC (MCN1 _) = "MCN1"
mCompandC (MCNM _ _) = "MCNM"

mCompand1 :: MCompand a b -> a
mCompand1 (MCN1 x) = x
mCompand1 (MCNM x _) = x

mCompand2 :: MCompand a b -> Maybe [b]
mCompand2 (MCNM _ ys) = Just ys
mCompand2 _ = Nothing

mCompandSet1 :: a -> MCompand a b -> MCompand a b
mCompandSet1 x (MCN1 _) = MCN1 x
mCompandSet1 x (MCNM _ ys) = MCNM x ys

mCompandSet2 :: [b] -> MCompand a b -> MCompand a b
mCompandSet2 ys (MCN1 x) = MCNM x ys
mCompandSet2 ys (MCNM x _) = MCNM x ys