-- | Clefs and staff positions. module Music.Pitch.Clef ( -- * Staff lines StaffLines, HalfSpaces, ClefLine, -- * Clef representation ClefSymbol(..), ClefOctave, Clef(..), symbolName, symbolPitch, positionPitch, pitchPosition, -- ** Properties isModernClef, isHistoricalClef, isVoiceClef, -- * Standard clefs trebleClef, bassClef, sopranoClef, mezzoSopranoClef, altoClef, tenorClef, baritoneClef, ) where import Data.Typeable import Music.Pitch.Common import Music.Pitch.Literal -- | Represents staff number relative middle. Staff zero is either the middle staff, or if using an -- even number of lines, the staff below the middle space. -- newtype StaffLines = StaffLines { getStaffLines :: Integer } deriving (Eq, Ord, Read, Show, Enum, Num, Real, Integral, Typeable) -- | Represents the difference betwee staff positions (often corresponding to one diatonic step). newtype HalfSpaces = HalfSpaces { getHalfSpaces :: Integer } deriving (Eq, Ord, Read, Show, Enum, Num, Real, Integral, Typeable) -- | Common clef symbols data ClefSymbol = GClef | CClef | FClef | PercClef | NeutralClef deriving (Eq, Ord, Show, Typeable) type ClefOctave = Integer type ClefLine = StaffLines newtype Clef = Clef { getClef :: (ClefSymbol, ClefOctave, ClefLine) } deriving (Eq, Ord, Typeable) instance Show Clef where show x@(Clef a) | x == trebleClef = "trebleClef" | x == bassClef = "bassClef" | x == sopranoClef = "sopranoClef" | x == mezzoSopranoClef = "mezzoSopranoClef" | x == altoClef = "altoClef" | x == tenorClef = "tenorClef" | x == baritoneClef = "baritoneClef" | otherwise = show a -- | Return the English name of the given clef. symbolName :: ClefSymbol -> String symbolName GClef = "G clef" symbolName CClef = "C clef" symbolName FClef = "F clef" symbolName PercClef = "Percussion clef" symbolName NeutralClef = "Neutral clef" -- | Return the pitch implied by the given clef at the middle space or line. symbolPitch :: ClefSymbol -> Maybe Pitch symbolPitch GClef = Just b' symbolPitch CClef = Just c symbolPitch FClef = Just d_ symbolPitch _ = Nothing -- TODO consolidate with common pitchPosition :: Clef -> Pitch -> Maybe StaffLines pitchPosition (Clef (s,o,l)) x = undefined where numbersPerOctave = 7 referencePitch = symbolPitch s :: Maybe Pitch positionPitch :: Clef -> StaffLines -> Maybe Pitch positionPitch (Clef (s,o,l)) x = fmap (upDiatonic relativePosition) referencePitch where numbersPerOctave = 7 referencePitch = symbolPitch s :: Maybe Pitch relativePosition = fromIntegral $ (x - l) + fromIntegral (o*7) -- TODO implement fully in Pitch.Common.Diatonic upDiatonic :: Number -> Pitch -> Pitch upDiatonic = upDiatonicP c . fromIntegral -- TODO Why c? {- TODO IsPitch instance? What to do with the non-standard pitches, i.e. we know what (g :: ClefSymbol) is, but what about (cs :: ClefSymbol)? I think 1) error or 2) default to g. Map this to Pitch.Common Do we make this module 1) depend on Pitch.Common or 2) the other way around? If 1), do we need to separate G/C/F in ClefSymbol, maybe just put a single constructor for common pitch. In that case, maybe a function isStandardClefPitch (i.e. c/f/g) would be appropriate. -} -- | Standard treble clef. trebleClef :: Clef -- | Standard bass clef. bassClef :: Clef -- | Standard soprano clef. sopranoClef :: Clef -- | Standard mezzo soprano clef. mezzoSopranoClef :: Clef -- | Standard alto clef. altoClef :: Clef -- | Standard tenor clef. tenorClef :: Clef -- | Standard baritone clef. baritoneClef :: Clef trebleClef = Clef (GClef, -1 :: ClefOctave, -1 :: ClefLine) bassClef = Clef (FClef, 1 :: ClefOctave, -1 :: ClefLine) sopranoClef = Clef (CClef, 0 :: ClefOctave, -2 :: ClefLine) mezzoSopranoClef = Clef (CClef, 0 :: ClefOctave, -1 :: ClefLine) altoClef = Clef (CClef, 0 :: ClefOctave, 0 :: ClefLine) tenorClef = Clef (CClef, 0 :: ClefOctave, 1 :: ClefLine) baritoneClef = Clef (CClef, 0 :: ClefOctave, 2 :: ClefLine) -- | Is this a clef used in contemporary notation? isModernClef :: Clef -> Bool isModernClef x | x == trebleClef = True isModernClef x | x == bassClef = True isModernClef x | x == altoClef = True isModernClef x | x == tenorClef = True isModernClef x | otherwise = False -- | Is this an historical clef? isHistoricalClef :: Clef -> Bool isHistoricalClef _ = False -- | Is this a traditional voice clef, i.e. a C clef on some staff. isVoiceClef :: Clef -> Bool isVoiceClef x | x == altoClef = True isVoiceClef x | x == tenorClef = True isVoiceClef x | otherwise = False