{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveFoldable #-} {-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} ------------------------------------------------------------------------------------- -- | -- Copyright : (c) Hans Hoglund 2012 -- -- License : BSD-style -- -- Maintainer : hans@hanshoglund.se -- Stability : experimental -- Portability : non-portable (TF,GNTD) -- -- Provides functions for manipulating parts. -- ------------------------------------------------------------------------------------- module Music.Score.Part ( -- * Part representation Part, HasPart(..), HasPart', PartT(..), getParts, ) where import Control.Applicative import Control.Comonad import Control.Monad.Plus import Data.Default import Data.Foldable import qualified Data.List as List import Data.Ord (comparing) import Data.PairMonad import Data.Ratio import Data.Semigroup import Data.Traversable import Data.Typeable import Music.Time -- | Associated part type. Should normally implement 'Ord' and 'Show'. type family Part a :: * -- | -- Class of types with an associated part. -- -- The part type can be any ordered type. A 'Show' instance is also -- required from printing the name of the part in output. -- class HasPart a where -- | Get the voice of the given note. getPart :: a -> Part a -- | Set the voice of the given note. setPart :: Part a -> a -> a -- | Modify the voice of the given note. modifyPart :: (Part a -> Part a) -> a -> a setPart n = modifyPart (const n) modifyPart f x = x newtype PartT n a = PartT { getPartT :: (n, a) } deriving (Eq, Ord, Show, Functor, Applicative, Comonad, Monad, Typeable) type instance Part () = Integer type instance Part Double = Integer type instance Part Float = Integer type instance Part Int = Integer type instance Part Integer = Integer type instance Part (Ratio a) = Integer instance HasPart () where { getPart _ = def } instance HasPart Double where { getPart _ = def } instance HasPart Float where { getPart _ = def } instance HasPart Int where { getPart _ = def } instance HasPart Integer where { getPart _ = def } instance Integral a => HasPart (Ratio a) where { getPart _ = def } type instance Part (PartT n a) = n instance HasPart (PartT n a) where getPart (PartT (v,_)) = v modifyPart f (PartT (v,x)) = PartT (f v, x) type instance Part (a,b) = Part b instance HasPart b => HasPart (a,b) where getPart (a,b) = getPart b modifyPart f (a,b) = (a, modifyPart f b) -- | -- Like 'HasPart', but enforces the part to be ordered. -- This is usually required for part separation and traversal. -- type HasPart' a = (Show (Part a), Ord (Part a), Default (Part a), HasPart a) -- TODO unify with class above as in Pitch? -- | -- Get all parts in the given score. Returns a list of parts. -- -- > Score a -> [Part] -- getParts :: (Foldable t, HasPart' a) => t a -> [Part a] getParts = List.sort . List.nub . fmap getPart . toList