music-score-1.6: src/Music/Score/Part.hs
{-# 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