music-pitch-1.8: src/Music/Pitch/Common/Semitones.hs
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE StandaloneDeriving #-}
------------------------------------------------------------------------------------
-- |
-- Copyright : (c) Hans Hoglund, Edward Lilley 2012–2014
--
-- License : BSD-style
--
-- Maintainer : hans@hanshoglund.se
-- Stability : experimental
-- Portability : non-portable (TF,GNTD)
--
-- Provides semitone, octave and step representation of intervals.
--
-------------------------------------------------------------------------------------
module Music.Pitch.Common.Semitones (
-- * Types
-- ** Octaves
Octaves,
-- HasOctaves(..),
-- ** Steps
Steps,
-- HasSteps(..),
-- ** Semitones
Semitones,
HasSemitones(..),
semitone,
tone,
ditone,
tritone,
isSemitone,
isTone,
isTritone,
-- * Enharmonic equivalence
(=:=),
(/:=),
) where
-- |
-- An interval represented as a number of octaves, including negative
-- intervals.
--
-- > octaves a = semitones a `div` 12
-- > steps a = semitones a `mod` 12
--
newtype Octaves = Octaves { getOctaves :: Integer }
deriving (Eq, Ord, Num, Enum, Real, Integral)
instance Show Octaves where { show = show . getOctaves }
instance HasOctaves Octaves where { octaves = id }
-- |
-- Class of intervals that has a number of 'Octaves'.
--
class HasOctaves a where
-- |
-- Returns the number of octaves spanned by an interval.
--
-- The number of octaves is negative if and only if the interval is
-- negative.
--
-- Examples:
--
-- > octaves (perfect unison) = 0
-- > octaves (d5 ^* 4) = 2
-- > octaves (-_P8) = -1
--
octaves :: a -> Octaves
-- |
-- An interval represented as a number of steps in the range /0 ≤ x < 12/.
--
-- > octaves a = semitones a `div` 12
-- > steps a = semitones a `mod` 12
--
newtype Steps = Steps { getSteps :: Integer }
deriving (Eq, Ord, Num, Enum, Real, Integral)
instance Show Steps where { show = show . getSteps }
-- instance HasSteps Steps where { steps = id }
{-
-- |
-- Class of intervals that has a number of 'Steps'.
--
class HasSteps a where
-- |
-- The number of steps is always in the range /0 ≤ x < 12/.
--
-- Examples:
--
-- > octaves (perfect unison) = 0
-- > octaves (d5 ^* 4) = 2
-- > octaves (-m7) = -1
--
steps :: a -> Steps
-}
-- |
-- An interval represented as a number of semitones, including negative
-- intervals, as well as intervals larger than one octave. This representation
-- does not take spelling into account, so for example a major third and a
-- diminished fourth can not be distinguished.
--
-- Intervals that name a number of semitones (i.e. 'semitone', 'tritone') does
-- not have an unequivocal spelling. To convert these to an interval, a
-- 'Spelling' must be provided:
--
-- >>> spell usingSharps tritone
-- _A4
--
-- >>> spell usingFlats tritone
-- d5
--
newtype Semitones = Semitones { getSemitones :: Integer }
deriving (Eq, Ord, Num, Enum, Real, Integral)
instance Show Semitones where { show = show . getSemitones }
instance HasSemitones Semitones where { semitones = id }
-- |
-- Class of intervals that can be converted to a number of 'Semitones'.
--
class HasSemitones a where
-- |
-- Returns the number of semitones spanned by an interval.
--
-- The number of semitones is negative if and only if the interval is
-- negative.
--
-- >>> semitones (perfect unison)
-- 0
-- >>> semitones tritone
-- 6
-- >>> semitones d5
-- 6
-- >>> semitones (-_P8)
-- -12
--
semitones :: a -> Semitones
semitone, tone, ditone, tritone :: Semitones
-- | Precisely one semitone.
semitone = 1
-- | Precisely one whole tone, or two semitones.
tone = 2
-- | Precisely two whole tones, or four semitones.
ditone = 4
-- | Precisely three whole tones, or six semitones.
tritone = 6
isTone, isSemitone, isTritone :: HasSemitones a => a -> Bool
-- | Returns true iff the given interval spans one semitone.
isSemitone = (== semitone) . abs . semitones
-- | Returns true iff the given interval spans one whole tone (two semitones).
isTone = (== tone) . abs . semitones
-- | Returns true iff the given interval spans three whole tones (six semitones).
isTritone = (== tritone) . abs . semitones
infix 4 =:=
infix 4 /:=
-- |
-- Enharmonic equivalence.
--
-- >>> asInterval _A2 == m3
-- False
-- >>> asInterval _A2 =:= m3
-- True
--
(=:=) :: HasSemitones a => a -> a -> Bool
a =:= b = semitones a == semitones b
-- |
-- Enharmonic non-equivalence.
--
-- >>> asInterval _A2 /= m3
-- True
-- >>> asInterval _A2 /:= m3
-- False
--
(/:=) :: HasSemitones a => a -> a -> Bool
a /:= b = semitones a /= semitones b