-- | Synthesis parameters
module Sound.Sc3.Server.Param where

import Data.Function {- base -}
import Data.List {- base -}
import Data.Maybe {- base -}

import Data.List.Split {- split -}

import Sound.Sc3.Common.Math {- hsc3 -}

-- | An Sc3 synthesis parameter, ie. (controlName, controlValue).
type Param1 = (String, Double)

-- | Set of Sc3 synthesiser parameters.
type Param = [Param1]

-- | Add new, or overwrite existing, parameter.
param_insert :: Param -> Param1 -> Param
param_insert :: Param -> Param1 -> Param
param_insert Param
p Param1
z = Param1
z Param1 -> Param -> Param
forall a. a -> [a] -> [a]
: (Param1 -> Param1 -> Bool) -> Param1 -> Param -> Param
forall a. (a -> a -> Bool) -> a -> [a] -> [a]
deleteBy (String -> String -> Bool
forall a. Eq a => a -> a -> Bool
(==) (String -> String -> Bool)
-> (Param1 -> String) -> Param1 -> Param1 -> Bool
forall b c a. (b -> b -> c) -> (a -> b) -> a -> a -> c
`on` Param1 -> String
forall a b. (a, b) -> a
fst) Param1
z Param
p

{- | Merge, require keys be unique.

>>> param_merge_uniq [("a",1),("b",2)] [("c",3),("d",4)]
[("a",1.0),("b",2.0),("c",3.0),("d",4.0)]

> param_merge_uniq [("a",1)] [("a",2)] -- error
-}
param_merge_uniq :: Param -> Param -> Param
param_merge_uniq :: Param -> Param -> Param
param_merge_uniq Param
p1 Param
p2 =
  case (Param1 -> String) -> Param -> [String]
forall a b. (a -> b) -> [a] -> [b]
map Param1 -> String
forall a b. (a, b) -> a
fst Param
p1 [String] -> [String] -> [String]
forall a. Eq a => [a] -> [a] -> [a]
`intersect` (Param1 -> String) -> Param -> [String]
forall a b. (a -> b) -> [a] -> [b]
map Param1 -> String
forall a b. (a, b) -> a
fst Param
p2 of
    [] -> Param
p1 Param -> Param -> Param
forall a. [a] -> [a] -> [a]
++ Param
p2
    [String]
_ -> String -> Param
forall a. HasCallStack => String -> a
error String
"param_merge_uniq?"

{- | Merge, right biased.

>>> param_merge_r [("a",1),("b",2)] [("c",3),("a",4)]
[("b",2.0),("c",3.0),("a",4.0)]
-}
param_merge_r :: Param -> Param -> Param
param_merge_r :: Param -> Param -> Param
param_merge_r Param
p1 Param
p2 =
  let p3 :: Param
p3 = let k2 :: [String]
k2 = (Param1 -> String) -> Param -> [String]
forall a b. (a -> b) -> [a] -> [b]
map Param1 -> String
forall a b. (a, b) -> a
fst Param
p2 in (Param1 -> Bool) -> Param -> Param
forall a. (a -> Bool) -> [a] -> [a]
filter (\(String
x, Double
_) -> String
x String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` [String]
k2) Param
p1
  in Param
p3 Param -> Param -> Param
forall a. [a] -> [a] -> [a]
++ Param
p2

{- | Right-fold (right-biased) of 'param_merge'

>>> param_merge_r_seq [[("a",1),("b",2)],[("c",3),("a",4)],[("b",5)]]
[("c",3.0),("a",4.0),("b",5.0)]
-}
param_merge_r_seq :: [Param] -> Param
param_merge_r_seq :: [Param] -> Param
param_merge_r_seq = (Param -> Param -> Param) -> [Param] -> Param
forall a. (a -> a -> a) -> [a] -> a
forall (t :: * -> *) a. Foldable t => (a -> a -> a) -> t a -> a
foldr1 Param -> Param -> Param
param_merge_r

-- | Lookup parameter value, with default.
param_get :: Param -> String -> Double -> Double
param_get :: Param -> String -> Double -> Double
param_get Param
p String
k Double
v = Double -> Maybe Double -> Double
forall a. a -> Maybe a -> a
fromMaybe Double
v (String -> Param -> Maybe Double
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup String
k Param
p)

{- | Given (param-separator,key-value-separator) parse paramter string.

>>> param_parse (';','=') "a=1;b=2"
[("a",1.0),("b",2.0)]
-}
param_parse :: (Char, Char) -> String -> Param
param_parse :: (Char, Char) -> String -> Param
param_parse (Char
c1, Char
c2) String
str =
  let f :: String -> (String, b)
f String
x = case String -> String -> [String]
forall a. Eq a => [a] -> [a] -> [[a]]
splitOn [Char
c2] String
x of
        [String
lhs, String
rhs] -> (String
lhs, String -> b
forall a. Read a => String -> a
read String
rhs)
        [String]
_ -> String -> (String, b)
forall a. HasCallStack => String -> a
error (String
"param_parse: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
x)
  in if String -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null String
str then [] else (String -> Param1) -> [String] -> Param
forall a b. (a -> b) -> [a] -> [b]
map String -> Param1
forall {b}. Read b => String -> (String, b)
f (String -> String -> [String]
forall a. Eq a => [a] -> [a] -> [[a]]
splitOn [Char
c1] String
str)

{- | Inverse of 'param_parse', /k/ is the precision to print values to.

>>> param_pp (';','=') 4 [("a",1),("b",2)]
"a=1.0;b=2.0"
-}
param_pp :: (Char, Char) -> Int -> Param -> String
param_pp :: (Char, Char) -> Int -> Param -> String
param_pp (Char
c1, Char
c2) Int
k =
  let f :: Param1 -> String
f (String
lhs, Double
rhs) = [String] -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [String
lhs, [Char
c2], Int -> Double -> String
double_pp Int
k Double
rhs]
  in [String] -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat ([String] -> String) -> (Param -> [String]) -> Param -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> [String] -> [String]
forall a. a -> [a] -> [a]
intersperse [Char
c1] ([String] -> [String]) -> (Param -> [String]) -> Param -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Param1 -> String) -> Param -> [String]
forall a b. (a -> b) -> [a] -> [b]
map Param1 -> String
f