-- | 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 parameters, ie. (control-name,control-value).
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),("b",2),("c",3),("d",4)]
-- > 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),("c",3),("a",4)]
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),("a",4),("b",5)]
param_merge_r_seq :: [Param] -> Param
param_merge_r_seq :: [Param] -> Param
param_merge_r_seq = (Param -> Param -> Param) -> [Param] -> Param
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),("b",2)]
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 (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] -> String
forall a. [a] -> [[a]] -> [a]
intercalate [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