music-score-1.6: src/Music/Score/Pitch.hs
{-# LANGUAGE
CPP,
DeriveFunctor,
DefaultSignatures,
DeriveFoldable,
DeriveDataTypeable,
FlexibleInstances,
FlexibleContexts,
ConstraintKinds,
TypeFamilies,
TypeOperators,
MultiParamTypeClasses,
NoMonomorphismRestriction,
UndecidableInstances,
RankNTypes,
ScopedTypeVariables,
GeneralizedNewtypeDeriving #-}
-------------------------------------------------------------------------------------
-- |
-- Copyright : (c) Hans Hoglund 2012
--
-- License : BSD-style
--
-- Maintainer : hans@hanshoglund.se
-- Stability : experimental
-- Portability : non-portable (TF,GNTD)
--
-- Provides functions for manipulating pitch.
--
-------------------------------------------------------------------------------------
module Music.Score.Pitch (
-- * Accessors
pitch',
pitch,
-- pitch_,
-- pitches',
-- pitches,
-- * Transformations
-- ** Transformations
inv,
-- ** Transformations
up,
down,
fifthsUp,
fifthsDown,
octavesUp,
octavesDown,
-- ** Transformations
-- above,
-- below,
fifthsAbove,
fifthsBelow,
octavesAbove,
octavesBelow,
-- * Pitch representation
Pitch,
Interval,
HasGetPitch(..),
HasSetPitch(..),
HasPitch'(..),
HasPitch(..),
HasSetPitch'(..),
Transposable,
Transposable1,
) where
import Control.Monad (ap, mfilter, join, liftM, MonadPlus(..))
import Control.Applicative
import Control.Lens
import Data.Semigroup
import Data.String
import Data.Typeable
import Data.Foldable (Foldable)
import Data.Traversable (Traversable)
import Data.Ratio
import Data.VectorSpace
import Data.AffineSpace
import Data.AffineSpace.Point
import qualified Data.List as List
import Music.Time
import Music.Pitch.Literal
-- This is outside HasGetPitch etc because HasSetPitch needs it
-- (and it allow us to derive more instances, see #95)
type family Pitch a
type Interval a = Diff (Pitch a)
-- |
-- Class of types with readable pitch.
--
class HasGetPitch s where
__getPitch :: (a ~ Pitch s) => s -> a
-- |
-- Class of types with mutable pitch.
--
-- Either 'setPitch' or 'mapPitch' can be implemented. If both are implemented,
-- the following laws should be satisfied:
--
-- > setPitch x = mapPitch (const x)
-- > mapPitch f x = setPitch p x where p = f (__getPitch x)
--
-- For types that are 'Functors', the following instance can be used
--
-- > type instance Pitch (T a) = Pitch a
-- > instance HasSetPitch a b => HasSetPitch (T a) (T b) where
-- > type SetPitch g (T a) = T (SetPitch g a)
-- > mapPitch = fmap . mapPitch
--
class (SetPitch (Pitch t) s ~ t) => HasSetPitch (s :: *) (t :: *) where
type SetPitch (b :: *) (s :: *) :: *
__setPitch :: Pitch t -> s -> t
__setPitch x = __mapPitch (const x)
__mapPitch :: (Pitch s -> Pitch t) -> s -> t
default __mapPitch :: HasGetPitch s => (Pitch s -> Pitch t) -> s -> t
__mapPitch f x = __setPitch p x where p = f (__getPitch x)
type HasPitch s t = (HasGetPitch s, HasSetPitch s t)
type HasPitch' a = HasPitch a a
type HasSetPitch' a = HasSetPitch a a
-- | A lens to the pitch in a note, score or other structure.
--
pitch' :: HasPitch' a => Lens' a (Pitch a)
pitch' = pitch
-- | A lens to the pitch in a note, score or other structure.
--
pitch :: HasPitch a b => Lens a b (Pitch a) (Pitch b)
pitch = lens __getPitch (flip __setPitch)
-- | A setter to the pitch in a note, score or other structure.
--
pitch_ :: HasSetPitch a b => Setter a b (Pitch a) (Pitch b)
pitch_ = sets __mapPitch
-- | Traverses all pitches in structure.
--
pitches' :: (Traversable t, HasPitch' a) => Traversal' (t a) (Pitch a)
pitches' = traverse . pitch'
-- | Traverses all pitches in structure.
--
pitches :: (Traversable t, HasPitch a b) => Traversal (t a) (t b) (Pitch a) (Pitch b)
pitches = traverse . pitch
type HasPitchConstr a = (
HasPitch' a,
VectorSpace (Interval a), Integer ~ Scalar (Interval a),
AffineSpace (Pitch a)
)
newtype PitchT p a = PitchT { __getPitchT :: (p, a) }
deriving (Eq, Ord, Show, Functor, Foldable, Traversable)
instance (Semigroup p, Monoid p) => Applicative (PitchT p) where
pure x = PitchT (mempty,x)
PitchT (pf,vf) <*> PitchT (px,vx) = PitchT (pf <> px, vf $ vx)
-- TODO move these
instance Stretchable ()
instance Stretchable Double
instance Stretchable Float
instance Stretchable Int
instance Stretchable Integer
instance Integral a => Stretchable (Ratio a)
instance Delayable ()
instance Delayable Double
instance Delayable Float
instance Delayable Int
instance Delayable Integer
instance Integral a => Delayable (Ratio a)
type instance Pitch (c,a) = Pitch a
instance HasGetPitch a => HasGetPitch (c,a) where
__getPitch (c,a) = __getPitch a
-- Undecidable ??
instance (HasGetPitch a, HasSetPitch a b) => HasSetPitch (c,a) (c,b) where
type SetPitch b (c,a) = (c,SetPitch b a)
__setPitch b = fmap (__setPitch b)
#define HAS_PITCH_PRIM(T) \
type instance Pitch T = T; \
instance HasGetPitch T where { \
__getPitch = id }
#define HAS_SET_PITCH_PRIM(T) \
instance (a ~ Pitch a) => HasSetPitch T a where { \
type SetPitch a T = a; \
__mapPitch = id }
HAS_PITCH_PRIM(())
HAS_PITCH_PRIM(Bool)
HAS_PITCH_PRIM(Double)
HAS_PITCH_PRIM(Float)
HAS_PITCH_PRIM(Int)
HAS_PITCH_PRIM(Integer)
HAS_SET_PITCH_PRIM(())
HAS_SET_PITCH_PRIM(Bool)
HAS_SET_PITCH_PRIM(Double)
HAS_SET_PITCH_PRIM(Float)
HAS_SET_PITCH_PRIM(Int)
HAS_SET_PITCH_PRIM(Integer)
-- type Transposable p i = (Diff p ~ i, AffineSpace p, VectorSpace i, IsPitch p, IsInterval i)
type Transposable a =
(
HasSetPitch' a,
Transposable1 a
)
type Transposable1 a =
(
Diff (Pitch a) ~ Interval a,
AffineSpace (Pitch a),
VectorSpace (Interval a),
IsPitch (Pitch a),
IsInterval (Interval a)
)
-- |
-- Transpose up.
--
up :: Transposable a => Interval a -> a -> a
up a = pitch_ %~ (.+^ a)
-- |
-- Transpose down.
--
down :: Transposable a => Interval a -> a -> a
down a = pitch_ %~ (.-^ a)
-- |
-- Add the given interval above.
--
above :: (Semigroup a, Transposable a) => Interval a -> a -> a
above a x = x <> up a x
-- |
-- Add the given interval below.
--
below :: (Semigroup a, Transposable a) => Interval a -> a -> a
below a x = x <> down a x
-- |
-- Invert pitches.
--
inv :: Transposable a => Pitch a -> a -> a
inv p = pitch_ %~ (reflectThrough p)
-- |
-- Transpose up by the given number of octaves.
--
octavesUp :: Transposable a => Scalar (Interval a) -> a -> a
octavesUp a = up (_P8^*a)
-- |
-- Transpose down by the given number of octaves.
--
octavesDown :: Transposable a => Scalar (Interval a) -> a -> a
octavesDown a = down (_P8^*a)
-- |
-- Add the given interval below.
--
fifthsUp :: Transposable a => Scalar (Interval a) -> a -> a
fifthsUp a = up (_P8^*a)
-- |
-- Add the given interval below.
--
fifthsDown :: Transposable a => Scalar (Interval a) -> a -> a
fifthsDown a = down (_P8^*a)
-- |
-- Add the given interval below.
--
octavesAbove :: (Semigroup a, Transposable a) => Scalar (Interval a) -> a -> a
octavesAbove n x = x <> octavesUp n x
-- |
-- Add the given interval below.
--
octavesBelow :: (Semigroup a, Transposable a) => Scalar (Interval a) -> a -> a
octavesBelow n x = x <> octavesUp n x
-- |
-- Add the given interval below.
--
fifthsAbove :: (Semigroup a, Transposable a) => Scalar (Interval a) -> a -> a
fifthsAbove n x = x <> fifthsUp n x
-- |
-- Add the given interval below.
--
fifthsBelow :: (Semigroup a, Transposable a) => Scalar (Interval a) -> a -> a
fifthsBelow n x = x <> fifthsUp n x
{-
highestPitch = maximum . __getPitches
lowestPitch = maximum . __getPitches
meanPitch = mean . __getPitches
mean x = fst $ foldl (\(m, n) x -> (m+(x-m)/(n+1),n+1)) (0,0) x
-}