Karya, built on 2020-11-26T21:03:17 (patch 23b5be2d53a9e8e7d6136cda5aae2849abe5cded)
Safe HaskellNone

Solkattu.S

Description

Low level support for rhythmic sequences in a Tala. The actual Note type is polymorphic, so this is purely rhythmic.

Synopsis

Documentation

data Note g a Source #

Constructors

Note !a 
TempoChange !TempoChange ![Note g a] 
Group !g ![Note g a] 

Instances

Instances details
Functor (Note g) # 
Instance details

Defined in Solkattu.S

Methods

fmap :: (a -> b) -> Note g a -> Note g b #

(<$) :: a -> Note g b -> Note g a #

Foldable (Note g) # 
Instance details

Defined in Solkattu.S

Methods

fold :: Monoid m => Note g m -> m #

foldMap :: Monoid m => (a -> m) -> Note g a -> m #

foldMap' :: Monoid m => (a -> m) -> Note g a -> m #

foldr :: (a -> b -> b) -> b -> Note g a -> b #

foldr' :: (a -> b -> b) -> b -> Note g a -> b #

foldl :: (b -> a -> b) -> b -> Note g a -> b #

foldl' :: (b -> a -> b) -> b -> Note g a -> b #

foldr1 :: (a -> a -> a) -> Note g a -> a #

foldl1 :: (a -> a -> a) -> Note g a -> a #

toList :: Note g a -> [a] #

null :: Note g a -> Bool #

length :: Note g a -> Int #

elem :: Eq a => a -> Note g a -> Bool #

maximum :: Ord a => Note g a -> a #

minimum :: Ord a => Note g a -> a #

sum :: Num a => Note g a -> a #

product :: Num a => Note g a -> a #

Traversable (Note g) # 
Instance details

Defined in Solkattu.S

Methods

traverse :: Applicative f => (a -> f b) -> Note g a -> f (Note g b) #

sequenceA :: Applicative f => Note g (f a) -> f (Note g a) #

mapM :: Monad m => (a -> m b) -> Note g a -> m (Note g b) #

sequence :: Monad m => Note g (m a) -> m (Note g a) #

Rest (SNote sollu) # 
Instance details

Defined in Solkattu.Dsl.Notation

Methods

__ :: SNote sollu Source #

Rest (SequenceT sollu) # 
Instance details

Defined in Solkattu.Dsl.Notation

Methods

__ :: SequenceT sollu Source #

(Eq a, Eq g) => Eq (Note g a) # 
Instance details

Defined in Solkattu.S

Methods

(==) :: Note g a -> Note g a -> Bool #

(/=) :: Note g a -> Note g a -> Bool #

(Ord a, Ord g) => Ord (Note g a) # 
Instance details

Defined in Solkattu.S

Methods

compare :: Note g a -> Note g a -> Ordering #

(<) :: Note g a -> Note g a -> Bool #

(<=) :: Note g a -> Note g a -> Bool #

(>) :: Note g a -> Note g a -> Bool #

(>=) :: Note g a -> Note g a -> Bool #

max :: Note g a -> Note g a -> Note g a #

min :: Note g a -> Note g a -> Note g a #

(Show a, Show g) => Show (Note g a) # 
Instance details

Defined in Solkattu.S

Methods

showsPrec :: Int -> Note g a -> ShowS #

show :: Note g a -> String #

showList :: [Note g a] -> ShowS #

(Pretty.Pretty a, Pretty.Pretty g) => Pretty.Pretty (Note g a) # 
Instance details

Defined in Solkattu.S

Methods

pretty :: Note g a -> Text Source #

format :: Note g a -> Doc Source #

formatList :: [Note g a] -> Doc Source #

data TempoChange Source #

Relative speed change. Each positive number doubles the number of Matras per akshara. Negative numbers halve them.

data Duration Source #

A single Duration unit is equivalent to 1 Akshara. Unlike FMatra and Matra, this is an absolute duration, so it doesn't depend on _nadai or _speed.

Instances

Instances details
Eq Duration # 
Instance details

Defined in Solkattu.S

Fractional Duration # 
Instance details

Defined in Solkattu.S

Num Duration # 
Instance details

Defined in Solkattu.S

Ord Duration # 
Instance details

Defined in Solkattu.S

Real Duration # 
Instance details

Defined in Solkattu.S

RealFrac Duration # 
Instance details

Defined in Solkattu.S

Methods

properFraction :: Integral b => Duration -> (b, Duration) #

truncate :: Integral b => Duration -> b #

round :: Integral b => Duration -> b #

ceiling :: Integral b => Duration -> b #

floor :: Integral b => Duration -> b #

Show Duration # 
Instance details

Defined in Solkattu.S

Pretty.Pretty Duration # 
Instance details

Defined in Solkattu.S

data FMatra Source #

This is a fractional Matra. The reason to need a fraction matra is if there are speed changes below, so often this is used to mean toplevel matra, which is to say duration / nadai, in which case it's relative to nadai, not speed. But not always! Sometimes it's used to name a number of Matras where it's assumed they have speed changes to accommodate the fraction.

Instances

Instances details
Eq FMatra # 
Instance details

Defined in Solkattu.S

Methods

(==) :: FMatra -> FMatra -> Bool #

(/=) :: FMatra -> FMatra -> Bool #

Fractional FMatra # 
Instance details

Defined in Solkattu.S

Num FMatra # 
Instance details

Defined in Solkattu.S

Ord FMatra # 
Instance details

Defined in Solkattu.S

Real FMatra # 
Instance details

Defined in Solkattu.S

RealFrac FMatra # 
Instance details

Defined in Solkattu.S

Methods

properFraction :: Integral b => FMatra -> (b, FMatra) #

truncate :: Integral b => FMatra -> b #

round :: Integral b => FMatra -> b #

ceiling :: Integral b => FMatra -> b #

floor :: Integral b => FMatra -> b #

Show FMatra # 
Instance details

Defined in Solkattu.S

Pretty.Pretty FMatra # 
Instance details

Defined in Solkattu.S

type Matra = Int Source #

A matra is an akshara divided by the nadai. It corresponds to a single sollu in first speed, which means the actual duration is dependent on Nadai and Speed.

Matra being integral is important, since together with TempoChange, it can encode the invariant that durations are always a power of two rational, once you multiply out the nadai.

type Speed = Int Source #

0 means nadai matras per akshara. Positive numbers double that and negative ones halve it.

type Nadai = Int Source #

type Stride = Int Source #

This could be Duration, but it would make normalizeSpeed tricky.

changeSpeed :: Speed -> [Note g a] -> Note g a Source #

class HasMatras a where Source #

Methods

matrasOf :: a -> Matra Source #

hasSustain :: a -> Bool Source #

True if this note has a duration in time. Otherwise, it's a single stroke, which logically has zero duration. This only affects how the note is drawn and whether it becomes a tracklang event with duration.

Instances

Instances details
HasMatras Pattern # 
Instance details

Defined in Solkattu.Solkattu

HasMatras (Note sollu) # 
Instance details

Defined in Solkattu.Solkattu

Methods

matrasOf :: Note sollu -> Matra Source #

hasSustain :: Note sollu -> Bool Source #

HasMatras (Note stroke) # 
Instance details

Defined in Solkattu.Realize

Methods

matrasOf :: Note stroke -> Matra Source #

hasSustain :: Note stroke -> Bool Source #

transform

mapGroup :: (g -> h) -> Note g a -> Note h a Source #

flattenGroups :: [Note g a] -> [Note h a] Source #

simplify :: [Note g a] -> [Note g a] Source #

Drop empty TempoChanges, combine nested ones. Drop empty groups.

map1 :: (a -> a) -> Note g a -> Note g a Source #

Transform only the first Note.

filterNotes :: (a -> Bool) -> [Note g a] -> [Note g a] Source #

tempo

data Tempo Source #

Constructors

Tempo 

Fields

Instances

Instances details
Eq Tempo # 
Instance details

Defined in Solkattu.S

Methods

(==) :: Tempo -> Tempo -> Bool #

(/=) :: Tempo -> Tempo -> Bool #

Show Tempo # 
Instance details

Defined in Solkattu.S

Methods

showsPrec :: Int -> Tempo -> ShowS #

show :: Tempo -> String #

showList :: [Tempo] -> ShowS #

Pretty.Pretty Tempo # 
Instance details

Defined in Solkattu.S

decompose :: Duration -> Either Text [Speed] Source #

Given a duration, return the speeds of 1 duration notes needed to add up to that duration. Error if the speed went past 4, which means the duration probably isn't binary.

flatten

data Flat g a Source #

This is an intermediate structure where TempoChange has been flattened out. A flat list is easier to deal with, especially since I need to match and replace sections of notes, which may overlap tempo groups arbitrarily.

However, FGroup is actually nested, not flat. Oops. Originally it was flat too, with a count to indicate the scope, with Tempo in a Meta type. I still needed to express the tempo and group order, so I added a separate FGroup, and then it got complicated to keep the count up to date when the children changed size, and process things recursively and it seemed like recursive data would make that simpler again. I only replace sections of notes within group boundaries, so it doesn't need to be flat like TempoChange does.

It turns out it's still annoying to modify trees though, evidence in cancelKarvai.

Another way to look at this, is that each FNote is one Matra.

Constructors

FGroup !Tempo !g ![Flat g a] 
FNote !Tempo !a 

Instances

Instances details
Functor (Flat g) # 
Instance details

Defined in Solkattu.S

Methods

fmap :: (a -> b) -> Flat g a -> Flat g b #

(<$) :: a -> Flat g b -> Flat g a #

(Eq g, Eq a) => Eq (Flat g a) # 
Instance details

Defined in Solkattu.S

Methods

(==) :: Flat g a -> Flat g a -> Bool #

(/=) :: Flat g a -> Flat g a -> Bool #

(Show g, Show a) => Show (Flat g a) # 
Instance details

Defined in Solkattu.S

Methods

showsPrec :: Int -> Flat g a -> ShowS #

show :: Flat g a -> String #

showList :: [Flat g a] -> ShowS #

(Pretty.Pretty g, Pretty.Pretty a) => Pretty.Pretty (Flat g a) # 
Instance details

Defined in Solkattu.S

Methods

pretty :: Flat g a -> Text Source #

format :: Flat g a -> Doc Source #

formatList :: [Flat g a] -> Doc Source #

filterFlat :: (a -> Bool) -> [Flat g a] -> [Flat g a] Source #

mapGroupFlat :: (g -> h) -> [Flat g a] -> [Flat h a] Source #

notes :: [Note g a] -> [a] Source #

flatten :: [Note g a] -> [Flat g a] Source #

flattenWith :: Tempo -> [Note g a] -> [Flat g a] Source #

flattenedNotes :: [Flat g a] -> [a] Source #

flatToState :: (Flat g a -> Duration) -> Tala.Tala -> State -> [Flat g a] -> (State, [(State, Flat g a)]) Source #

Convert events with Tempo into events with absolute positions in State.

I need to look in the group to know what the actual duration is, unfortunately.

withDurations :: HasMatras a => [Flat g a] -> [Flat g (Duration, a)] Source #

Calculate Duration for each note.

TODO this is wrong in general because unfortunately groups can affect duration, as in flatToState. I think I only call it after Koravi.realize, at which point the notes do reflect the duration, but of course that's not reflected in the types. flatDuration has some details.

tempoNotes :: [Flat g a] -> [(Tempo, a)] Source #

maxSpeed :: [Flat g a] -> Speed Source #

tempoToState Source #

Arguments

:: HasMatras a 
=> Tala.Tala 
-> Duration

start time

-> [(Tempo, a)] 
-> (State, [(State, a)]) 

Convert events with Tempo into events with absolute positions in State.

This is only correct when the group doesn't affect duration. Since there's no group in here, presumably it's already been stripped out, which of you should only have done if it's no longer needed, which is the case post realize.

data Stroke a Source #

Constructors

Attack a 
Sustain a 
Rest 

Instances

Instances details
Eq a => Eq (Stroke a) # 
Instance details

Defined in Solkattu.S

Methods

(==) :: Stroke a -> Stroke a -> Bool #

(/=) :: Stroke a -> Stroke a -> Bool #

Show a => Show (Stroke a) # 
Instance details

Defined in Solkattu.S

Methods

showsPrec :: Int -> Stroke a -> ShowS #

show :: Stroke a -> String #

showList :: [Stroke a] -> ShowS #

Pretty.Pretty a => Pretty.Pretty (Stroke a) # 
Instance details

Defined in Solkattu.S

normalizeSpeed :: HasMatras a => Speed -> Tala.Tala -> [Flat g a] -> [Flat g (State, Stroke a)] Source #

Normalize to the fastest speed. Fill slower strokes in with rests. Speed 0 always gets at least one Stroke, even if it's not the slowest.

This normalizes speed, not nadai, because Realize.format lays out notation by nadai, not in absolute time.

flattenSpeed :: HasMatras a => Speed -> [Note g a] -> Either Text [Note g (Stroke a)] Source #

This is similar to normalizeSpeed, but working on Notes instead of Flats. Expand speed to the given toSpeed, or error if there's a speed above it, or if I run into a nadai change. This will eliminate all TempoChanges.

State

data State Source #

Keep track of timing and tala position.

Constructors

State 

Fields

  • stateAvartanam :: !Int
     
  • stateAkshara :: !Tala.Akshara
     
  • stateMatra :: !Duration

    Time through this akshara, so this is always < 1. TODO actually this is not matras, but fraction of the way through the akshara. Is there a better term?

  • stateTempo :: !Tempo

    The tempo at the time of the State. This is not needed internally, but it's easier to record this explicitly than try to figure it out based on the difference between this state and the next.

    TODO this is a bit error prone, because while the rest of the fields are about the current state, this is about the next time step. That means advanceStateBy is too late to set it, and it has to be set by whoever calls advanceStateBy. Ugh.

Instances

Instances details
Show State # 
Instance details

Defined in Solkattu.S

Methods

showsPrec :: Int -> State -> ShowS #

show :: State -> String #

showList :: [State] -> ShowS #

Pretty.Pretty State # 
Instance details

Defined in Solkattu.S

stateMatraPosition :: State -> Duration Source #

Number of aksharas relative to the avartanam.

stateAbsoluteAkshara :: Tala.Tala -> State -> Duration Source #

Absolute number of aksharas from the beginning of the sequence.

showPosition :: State -> Text Source #

Show avartanam, akshara, and matra as avartanam:akshara+n/d.

functions

durationOf :: HasMatras a => Tempo -> Note g a -> Duration Source #

Flatten the note and return its Duration.

matraFMatra :: Tempo -> Matra -> FMatra Source #

Convert a tempo-relative Matra to a toplevel FMatra, which should only be nadai-relative.

matraDuration :: Tempo -> Duration Source #

Duration of one matra in the given tempo. This doesn't include _stride, because stride adds matras to the note duration, it doesn't change the duration of a matra itself.