-- Copyright 2013 Evan Laforge
-- This program is distributed under the terms of the GNU General Public
-- License 3.0, see COPYING or http://www.gnu.org/licenses/gpl-3.0.txt

{- | Meters for Carnatic music.

    The main data type is 'Meter.LabeledMeter', produced by 'make_meter', which
    can be easily turned into a 'Ruler.Ruler' via 'ruler' if needed.

    E.g., 3 avartanams of adi talam chatusra nadai followed by 4 avartanams of
    tisra nadai: @make_meter [Ruler adi_tala 1 3 4 1, Ruler adi_tala 2 4 3 1]@
-}
module Cmd.Ruler.Tala (
    Sections, Avartanams, Nadai
    -- * standard talams
    , simple
    , make, make_until
    , tala_to_meter
    , adi, adi3, adi6
    , config
    , adi_tala, dhruva_tala, matya_tala, rupaka_tala, jhampa_tala, triputa_tala
    , ata_tala, eka_tala
    , misra_chapu, kanda_chapu, rupaka_fast
) where
import qualified Data.Set as Set

import qualified Util.Num as Num
import qualified Cmd.Ruler.RulerUtil as RulerUtil
import qualified Solkattu.Tala as Tala
import           Solkattu.Tala
    (Tala(..), adi_tala, ata_tala, dhruva_tala, eka_tala, jhampa_tala,
     kanda_chapu, matya_tala, misra_chapu, rupaka_fast, rupaka_tala,
     triputa_tala)
import qualified Ui.Meter.Make as Make
import qualified Ui.Meter.Meter as Meter
import           Ui.Meter.Meter (AbstractMeter(..))

import           Global
import           Types


type Sections = Int
type Avartanams = Int
type Nadai = Int

-- * standard talams

-- | Simplified version of 'make', avartanam dur and sections hardcoded to 1.
simple :: Tala -> Nadai -> Avartanams -> Meter.Meter
simple :: Tala -> Int -> Int -> Meter
simple Tala
tala Int
nadai Int
avartanams = Tala -> Int -> Duration -> Int -> Int -> Meter
make Tala
tala Int
nadai Duration
1 Int
avartanams Int
1

-- TODO too many parameters.. use a record?
make :: Tala -> Nadai -> Meter.Duration -> Avartanams -> Sections
    -> Meter.Meter
make :: Tala -> Int -> Duration -> Int -> Int -> Meter
make Tala
tala Int
nadai Duration
avartanam_dur Int
avartanams Int
sections =
    Config -> [MSection] -> Meter
Meter.meter (Tala -> Config
config Tala
tala) (forall a. Int -> a -> [a]
replicate Int
sections MSection
section)
    where
    section :: MSection
section = Int -> Duration -> AbstractMeter -> MSection
Meter.MSection Int
avartanams Duration
avartanam_dur (Tala -> Int -> AbstractMeter
tala_to_meter Tala
tala Int
nadai)

make_until :: Tala -> Nadai -> Meter.Duration -> TrackTime -> Meter.Meter
make_until :: Tala -> Int -> Duration -> Duration -> Meter
make_until Tala
tala Int
nadai Duration
avartanam_dur Duration
end =
    Duration -> Meter -> Meter
RulerUtil.meter_take Duration
end forall a b. (a -> b) -> a -> b
$ Tala -> Int -> Duration -> Int -> Int -> Meter
make Tala
tala Int
nadai Duration
avartanam_dur Int
avartanams Int
1
    where avartanams :: Int
avartanams = forall a b. (RealFrac a, Integral b) => a -> b
ceiling (Duration
end forall a. Fractional a => a -> a -> a
/ Duration
avartanam_dur)

-- | 4 avartanams of everyone's favorite talam.
adi :: Avartanams -> Meter.Meter
adi :: Int -> Meter
adi = Tala -> Int -> Int -> Meter
simple Tala
adi_tala Int
4

-- | 'adi' but in tisram.
adi3 :: Avartanams -> Meter.Meter
adi3 :: Int -> Meter
adi3 = Tala -> Int -> Int -> Meter
simple Tala
adi_tala Int
3

adi6 :: Avartanams -> Meter.Meter
adi6 :: Int -> Meter
adi6 = Tala -> Int -> Int -> Meter
simple Tala
adi_tala Int
6

-- * implementation

config :: Tala -> Meter.Config
config :: Tala -> Config
config Tala
tala = Meter.Config
    { config_labeled_ranks :: Set Rank
config_labeled_ranks = Set Rank
labeled_ranks
    , config_label :: LabelConfig
config_label = [Label] -> LabelConfig
Meter.Cycle forall a b. (a -> b) -> a -> b
$ Tala -> [Label]
tala_labels Tala
tala
    , config_start_measure :: Int
config_start_measure = Int
1
    , config_min_depth :: Int
config_min_depth = Int
1
    , config_strip_depth :: Int
config_strip_depth = Int
2
    }

-- Ranks (* marks labeled ranks):
-- * r_section is several avartanams -- sections
-- * r_1 avartanam -- avartanams
-- * r_2 anga -- based on tala
-- * r_4 akshara -- basad on akshara type and jati
-- * r_8 nadai / gati -- variable
-- . r_16 -- 2
-- * r_32 -- 2
-- . r_64 -- 2
-- * r_128 -- 2
-- . r_256 -- 2
labeled_ranks :: Set Meter.Rank
labeled_ranks :: Set Rank
labeled_ranks = forall a. Ord a => [a] -> Set a
Set.fromList [Rank
Meter.W, Rank
Meter.Q, Rank
Meter.E, Rank
Meter.T32, Rank
Meter.T128]

tala_to_meter :: Tala -> Nadai -> AbstractMeter
tala_to_meter :: Tala -> Int -> AbstractMeter
tala_to_meter Tala
tala Int
nadai =
    -- r_8, r_16, r_32, r_64, ...
    [Int] -> AbstractMeter -> AbstractMeter
Meter.subdivides [Int
nadai, Int
2, Int
2, Int
2, Int
2, Int
2] forall a b. (a -> b) -> a -> b
$ [AbstractMeter] -> AbstractMeter
D
        -- r_2, r_4
        [ [AbstractMeter] -> AbstractMeter
D (forall a. Int -> a -> [a]
replicate (Int -> Anga -> Int
Tala.anga_aksharas (Tala -> Int
Tala._jati Tala
tala) Anga
anga) AbstractMeter
T)
        | Anga
anga <- Tala -> [Anga]
Tala._angas Tala
tala
        ]

anga_claps :: Tala -> Int
anga_claps :: Tala -> Int
anga_claps Tala
tala =
    forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
Num.sum (forall a b. (a -> b) -> [a] -> [b]
map (Int -> Anga -> Int
Tala.anga_aksharas (Tala -> Int
Tala._jati Tala
tala)) (Tala -> [Anga]
Tala._angas Tala
tala))

tala_labels :: Tala -> [Meter.Label]
tala_labels :: Tala -> [Label]
tala_labels Tala
tala = forall a b. (a -> b) -> [a] -> [b]
map Label -> Label
Make.big_label forall a b. (a -> b) -> a -> b
$ forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Anga -> [Label]
mk (Tala -> [Anga]
Tala._angas Tala
tala)
    where
    mk :: Anga -> [Label]
mk Anga
anga = case Anga
anga of
        Tala.Clap Int
n -> Label
"X" forall a. a -> [a] -> [a]
: forall a. Int -> a -> [a]
replicate (Int
nforall a. Num a => a -> a -> a
-Int
1) Label
"-"
        Tala.Wave Int
n -> Label
"O" forall a. a -> [a] -> [a]
: forall a. Int -> a -> [a]
replicate (Int
nforall a. Num a => a -> a -> a
-Int
1) Label
"-"
        Anga
Tala.I -> forall a. Int -> [a] -> [a]
take (Tala -> Int
Tala._jati Tala
tala) (Int -> [Label]
Make.count_from Int
0)
        Anga
Tala.O -> [Label
"X", Label
"O"]
        Anga
Tala.U -> [Label
"X"]