{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveFoldable #-} {-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE DeriveTraversable #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE ViewPatterns #-} ------------------------------------------------------------------------------------- -- | -- Copyright : (c) Hans Hoglund 2012-2014 -- -- License : BSD-style -- -- Maintainer : hans@hanshoglund.se -- Stability : experimental -- Portability : non-portable (TF,GNTD) -- ------------------------------------------------------------------------------------- module Music.Time.Note ( -- * Note type Note, -- * Construction note, notee, durationNote, -- noteComplement, ) where import Control.Applicative import Control.Comonad import Control.Lens hiding (Indexable, Level, above, below, index, inside, parts, reversed, transform, (<|), (|>)) import Data.Bifunctor import Data.Foldable (Foldable) import qualified Data.Foldable as Foldable import Data.Functor.Couple import Data.String import Data.Typeable import Data.VectorSpace import Data.Aeson (ToJSON (..), FromJSON(..)) import qualified Data.Aeson as JSON import Music.Dynamics.Literal import Music.Pitch.Literal import Music.Time.Internal.Util (dependingOn) import Music.Time.Juxtapose -- | -- A value 'Note' value, representing a value stretched by some 'Duration'. -- newtype Note a = Note { getNote :: Duration `Couple` a } deriving (Eq, Ord, Typeable, Foldable, Traversable, Functor, Applicative, Monad, Comonad, Num, Fractional, Floating, Real, RealFrac) instance (Show a, Transformable a) => Show (Note a) where show x = show (x^.from note) ++ "^.note" instance Wrapped (Note a) where type Unwrapped (Note a) = (Duration, a) _Wrapped' = iso (getCouple . getNote) (Note . Couple) instance Rewrapped (Note a) (Note b) instance Transformable (Note a) where transform t = over (from note . _1) (transform t) instance HasDuration (Note a) where _duration = _duration . view (from note) {- Splitting a note is surprisingly difficult because of the recursive nature of split. In brief, when splitting (d,(x,a)^.note)^.note at t, we have to split the inner value x at (t/d), and then scale the results xa and xb by some values p and q such that the duration of the new nested notes (da*xa) and (db*xb) is the duration of the original nested note (d*x), as stated by the Splittable laws. Full derivation below. ----- split t d = (da+db) split (t/d) x = (xa*p+xb*q) split t (d*x) = (da*xa+db*xb) d = da+db [split laws] x = (xa*p+xb*q) [split laws] d*x = da*xa+db*xb [split laws] Isolate p and q! xa*p + xb*q = x xa*p = x - xb*q p = (x - xb*q)/xa xa*p + xb*q = x xb*q = x - xa*p q = (x - xa*p)/xb -- Assuming we know t, d, x, xa*p, xb*q, da*xa, db*xb xa = da*xa/da xb = db*xb/db -- Example t = 0.6, d = 1, x = 2 da = 0.6, db = 0.4 [split t d] xa*p = 3/5, xb*q = 7/5, [split (t/d) x] da*xa = 3/5, db*xb = 7/5 [split t (d*x)] xb = (7/5)/0.4 = 3.5 [db*xb/db = xb] xa = (3/5)/0.6 = 1 [da*xa/da = xa] p = (2 - 7/5) / 1 = 0.6 [p def] q = (2 - 3/5) / 3.5 = 0.4 [q def] >>> split 0.6 (1,(2,_)^.note)^.note -- 2 ( (0.6,(1,a)^.note)^.note -- da*xa , (0.4,(3.5,mempty)^.note)^.note -- db*xb -} instance (Splittable a, Transformable a) => Splittable (Note a) where split t ((^.from note) -> (d, x)) = over both (^.note) $ split' t d x split' :: (Transformable a, Splittable a) => Duration -> Duration -> a -> ((Duration, a), (Duration, a)) split' t d x = ((da, compress p xa_p), (db, compress q xb_q)) -- We are really returning ((da, xa), (db, xb)) -- However because of the polymorphic value, we must derive xa and xb from split (t/d) x, p and q where -- (da+db) = split t d -- (xa*p+xb*q) = split (t/d) x -- (da*xa+db*xb) = split t (d*x) (da,db) = split t d (xa_p, xb_q) = split (t/d) x (da_xa, db_xb) = split t (d*(x^.duration)) xa = da_xa/da xb = db_xb/db p = ((x^.duration) - (xb_q^.duration))/xa q = ((x^.duration) - (xa_p^.duration))/xb instance IsString a => IsString (Note a) where fromString = pure . fromString instance IsPitch a => IsPitch (Note a) where fromPitch = pure . fromPitch instance IsInterval a => IsInterval (Note a) where fromInterval = pure . fromInterval instance IsDynamics a => IsDynamics (Note a) where fromDynamics = pure . fromDynamics instance ToJSON a => ToJSON (Note a) where -- TODO meta toJSON a = JSON.object [ ("duration", toJSON d), ("value", toJSON x) ] where (d, x) = a^.from note instance FromJSON a => FromJSON (Note a) where parseJSON (JSON.Object x) = liftA2 (\x y -> (x,y)^.note) dur value where dur = x JSON..: "duration" value = x JSON..: "value" parseJSON _ = empty -- | View a note value as a pair of the original value and a stretch factor. note :: Iso (Duration, a) (Duration, b) (Note a) (Note b) note = _Unwrapped -- | Access the note value. -- Taking a value out carries out the stretch (using the 'Transformable' instance), -- while putting a value in carries out the reverse transformation. -- -- >>> view notee $ (2,3::Duration)^.note -- 6 -- -- >>> set notee 6 $ (2,1::Duration)^.note -- (2,3)^.note -- notee :: (Transformable a, Transformable b) => Lens (Note a) (Note b) a b notee = _Wrapped `dependingOn` (transformed . stretching) -- | A note value as a duration carrying an associated value. -- Whitness by picking a trivial value. -- -- >>> 2^.durationNote -- (2,())^.note -- durationNote :: Iso' Duration (Note ()) durationNote = iso (\d -> (d,())^.note) (^.duration) -- >>> (pure ())^.from durationNote -- 1 -- >>> (pure () :: Note ())^.duration -- 1 -- TODO could also be an iso... noteComplement :: Note a -> Note a noteComplement (Note (Couple (d,x))) = Note $ Couple (negateV d, x) -- FIXME negateV is negate not recip -- The negateV method should follow (^+^), which is (*) for durations (is this bad?)