module Music.Pitch.Common.Pitch
(
Accidental,
natural,
flat,
sharp,
doubleFlat,
doubleSharp,
isNatural,
isFlattened,
isSharpened,
isStandardAccidental,
Name(..),
Pitch,
mkPitch,
name,
accidental,
upDiatonicP,
downDiatonicP,
upChromaticP,
downChromaticP,
invertDiatonicallyP,
invertChromaticallyP,
) where
import Control.Applicative
import Control.Monad
import Control.Lens hiding (simple)
import Data.AffineSpace
import Data.AffineSpace.Point
import qualified Data.Char as Char
import Data.Either
import qualified Data.List as List
import Data.Maybe
import Data.Semigroup
import Data.Typeable
import Data.VectorSpace
import Data.Aeson (ToJSON (..), FromJSON(..))
import qualified Data.Aeson
import Music.Pitch.Absolute
import Music.Pitch.Alterable
import Music.Pitch.Augmentable
import Music.Pitch.Common.Types
import Music.Pitch.Common.Number
import Music.Pitch.Common.Interval
import Music.Pitch.Common.Semitones
import Music.Pitch.Literal
sharp, flat, natural, doubleFlat, doubleSharp :: Accidental
doubleSharp = 2
sharp = 1
natural = 0
flat = 1
doubleFlat = 2
isNatural, isSharpened, isFlattened :: Accidental -> Bool
isNatural = (== 0)
isSharpened = (> 0)
isFlattened = (< 0)
isStandardAccidental :: Accidental -> Bool
isStandardAccidental a = abs a < 2
instance IsPitch Pitch where
fromPitch (PitchL (c, a, o)) =
Pitch $ (\a b -> (fromIntegral a, fromIntegral b)^.interval') (qual a) c ^+^ (_P8^* fromIntegral o)
where
qual Nothing = 0
qual (Just n) = round n
instance Enum Pitch where
toEnum = Pitch . (\a b -> (fromIntegral a, fromIntegral b)^.interval') 0 . fromIntegral
fromEnum = fromIntegral . pred . number . (.-. c)
instance Alterable Pitch where
sharpen (Pitch a) = Pitch (augment a)
flatten (Pitch a) = Pitch (diminish a)
instance Show Pitch where
show p = showName (name p) ++ showAccidental (accidental p) ++ showOctave (octaves $ getPitch p)
where
showName = fmap Char.toLower . show
showOctave n
| n > 0 = replicate (fromIntegral n) '\''
| otherwise = replicate (negate $ fromIntegral n) '_'
showAccidental n
| n > 0 = replicate (fromIntegral n) 's'
| otherwise = replicate (negate $ fromIntegral n) 'b'
instance Num Pitch where
Pitch a + Pitch b = Pitch (a + b)
negate (Pitch a) = Pitch (negate a)
abs (Pitch a) = Pitch (abs a)
(*) = error "Music.Pitch.Common.Pitch: no overloading for (*)"
signum = error "Music.Pitch.Common.Pitch: no overloading for signum"
fromInteger = toEnum . fromInteger
instance AffineSpace Pitch where
type Diff Pitch = Interval
Pitch a .-. Pitch b = a ^-^ b
Pitch a .+^ b = Pitch (a ^+^ b)
instance ToJSON Pitch where
toJSON = toJSON . (.-. c)
instance FromJSON Pitch where
parseJSON = fmap (c .+^) . parseJSON
mkPitch :: Name -> Accidental -> Pitch
mkPitch name acc = Pitch $ (\a b -> (fromIntegral a, fromIntegral b)^.interval') (fromIntegral acc) (fromEnum name)
name :: Pitch -> Name
name x
| i == 7 = toEnum 0
| 0 <= i && i <= 6 = toEnum i
| otherwise = error $ "Pitch.name: Bad value " ++ show i
where
i = (fromIntegral . pred . number . simple . getPitch) x
accidental :: Pitch -> Accidental
accidental = fromIntegral . intervalDiff . simple . getPitch
where
intervalDiff = view (from interval'._1)
upChromaticP :: Pitch -> ChromaticSteps -> Pitch -> Pitch
upChromaticP origin n = relative origin $ (_alteration +~ n)
downChromaticP :: Pitch -> ChromaticSteps -> Pitch -> Pitch
downChromaticP origin n = relative origin $ (_alteration -~ n)
upDiatonicP :: Pitch -> DiatonicSteps -> Pitch -> Pitch
upDiatonicP origin n = relative origin $ (_steps +~ n)
downDiatonicP :: Pitch -> DiatonicSteps -> Pitch -> Pitch
downDiatonicP origin n = relative origin $ (_steps -~ n)
invertDiatonicallyP :: Pitch -> Pitch -> Pitch
invertDiatonicallyP origin = relative origin $ (_steps %~ negate)
invertChromaticallyP :: Pitch -> Pitch -> Pitch
invertChromaticallyP origin = relative origin $ (_alteration %~ negate)