packages feed

music-pitch 1.7.1 → 1.7.2

raw patch · 11 files changed

+832/−235 lines, 11 filesdep +natsdep +type-unarydep +vector-space-pointsdep ~music-pitch-literalPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: nats, type-unary, vector-space-points

Dependency ranges changed: music-pitch-literal

API changes (from Hackage documentation)

- Music.Pitch.Common.Interval: data Interval
- Music.Pitch.Common.Pitch: data Pitch
- Music.Pitch.Intonation: pure :: Integral a => Intonation a
+ Music.Pitch.Absolute: instance AdditiveGroup Hertz
+ Music.Pitch.Absolute: instance AffineSpace Hertz
+ Music.Pitch.Absolute: instance IsInterval Hertz
+ Music.Pitch.Absolute: instance VectorSpace Hertz
+ Music.Pitch.Clef: data ClefSymbol
+ Music.Pitch.Clef: instance Eq ClefSymbol
+ Music.Pitch.Clef: instance Ord ClefSymbol
+ Music.Pitch.Clef: instance Show ClefSymbol
+ Music.Pitch.Clef: instance Typeable ClefSymbol
+ Music.Pitch.Clef: pitchPosition :: Clef -> Pitch -> Maybe StaffLines
+ Music.Pitch.Clef: positionPitch :: Clef -> StaffLines -> Maybe Pitch
+ Music.Pitch.Clef: symbolName :: ClefSymbol -> String
+ Music.Pitch.Clef: symbolPitch :: ClefSymbol -> Maybe Pitch
+ Music.Pitch.Clef: type Clef = (ClefSymbol, OctaveAdjustment, StaffLines)
+ Music.Pitch.Clef: type OctaveAdjustment = Integer
+ Music.Pitch.Common.Interval: Chromatic :: IntervalBasis
+ Music.Pitch.Common.Interval: Diatonic :: IntervalBasis
+ Music.Pitch.Common.Interval: Interval :: (Int, Int) -> Interval
+ Music.Pitch.Common.Interval: basis_A1 :: Interval
+ Music.Pitch.Common.Interval: basis_P1 :: Interval
+ Music.Pitch.Common.Interval: basis_P5 :: Interval
+ Music.Pitch.Common.Interval: basis_P8 :: Interval
+ Music.Pitch.Common.Interval: basis_d2 :: Interval
+ Music.Pitch.Common.Interval: convertBasis :: Interval -> Interval -> Interval -> Maybe (Int, Int)
+ Music.Pitch.Common.Interval: convertBasisFloat :: (Fractional t, Eq t) => Interval -> Interval -> Interval -> Maybe (t, t)
+ Music.Pitch.Common.Interval: data IntervalBasis
+ Music.Pitch.Common.Interval: getInterval :: Interval -> (Int, Int)
+ Music.Pitch.Common.Interval: instance Augmentable Quality
+ Music.Pitch.Common.Interval: instance Enum IntervalBasis
+ Music.Pitch.Common.Interval: instance Eq IntervalBasis
+ Music.Pitch.Common.Interval: instance HasBasis Interval
+ Music.Pitch.Common.Interval: instance Ord IntervalBasis
+ Music.Pitch.Common.Interval: instance Show IntervalBasis
+ Music.Pitch.Common.Interval: intervalDiv :: Interval -> Interval -> Int
+ Music.Pitch.Common.Interval: newtype Interval
+ Music.Pitch.Common.Pitch: Pitch :: Interval -> Pitch
+ Music.Pitch.Common.Pitch: getPitch :: Pitch -> Interval
+ Music.Pitch.Common.Pitch: newtype Pitch
+ Music.Pitch.Intonation: fiftyThreeToneEqual :: Tuning Interval
+ Music.Pitch.Intonation: fiveToneEqual :: Tuning Interval
+ Music.Pitch.Intonation: intone :: (Pitch, Hertz) -> Tuning Interval -> Intonation Pitch
+ Music.Pitch.Intonation: nineteenToneEqual :: Tuning Interval
+ Music.Pitch.Intonation: pureOctaveWith :: (Interval, Hertz) -> Interval -> Hertz
+ Music.Pitch.Intonation: quarterCommaMeantone :: Tuning Interval
+ Music.Pitch.Intonation: schismaticMeantone :: Tuning Interval
+ Music.Pitch.Intonation: sevenToneEqual :: Tuning Interval
+ Music.Pitch.Intonation: standardIntonation :: Intonation Pitch
+ Music.Pitch.Intonation: synTune :: (Interval, Hertz) -> (Interval, Hertz) -> Interval -> Hertz
+ Music.Pitch.Intonation: tetTune :: Interval -> Interval -> Hertz
+ Music.Pitch.Intonation: thirtyOneToneEqual :: Tuning Interval
+ Music.Pitch.Intonation: type Tuning i = i -> Hertz
+ Music.Pitch.StaffLines: data HalfSpaces
+ Music.Pitch.StaffLines: data StaffLines
+ Music.Pitch.StaffLines: instance Enum HalfSpaces
+ Music.Pitch.StaffLines: instance Enum StaffLines
+ Music.Pitch.StaffLines: instance Eq HalfSpaces
+ Music.Pitch.StaffLines: instance Eq StaffLines
+ Music.Pitch.StaffLines: instance Integral HalfSpaces
+ Music.Pitch.StaffLines: instance Integral StaffLines
+ Music.Pitch.StaffLines: instance Num HalfSpaces
+ Music.Pitch.StaffLines: instance Num StaffLines
+ Music.Pitch.StaffLines: instance Ord HalfSpaces
+ Music.Pitch.StaffLines: instance Ord StaffLines
+ Music.Pitch.StaffLines: instance Read HalfSpaces
+ Music.Pitch.StaffLines: instance Read StaffLines
+ Music.Pitch.StaffLines: instance Real HalfSpaces
+ Music.Pitch.StaffLines: instance Real StaffLines
+ Music.Pitch.StaffLines: instance Show HalfSpaces
+ Music.Pitch.StaffLines: instance Show StaffLines
+ Music.Pitch.StaffLines: instance Typeable HalfSpaces
+ Music.Pitch.StaffLines: instance Typeable StaffLines
- Music.Pitch.Common.Interval: Augmented :: Integer -> Quality
+ Music.Pitch.Common.Interval: Augmented :: Int -> Quality
- Music.Pitch.Common.Interval: Diminished :: Integer -> Quality
+ Music.Pitch.Common.Interval: Diminished :: Int -> Quality
- Music.Pitch.Intonation: pythagorean :: Integral a => Intonation a
+ Music.Pitch.Intonation: pythagorean :: Tuning Interval
- Music.Pitch.Intonation: twelveToneEqual :: Integral a => Intonation a
+ Music.Pitch.Intonation: twelveToneEqual :: Tuning Interval
- Music.Pitch.Intonation: type Intonation a = a -> Hertz
+ Music.Pitch.Intonation: type Intonation p = p -> Hertz

Files

music-pitch.cabal view
@@ -1,12 +1,12 @@  name:                   music-pitch-version:                1.7.1+version:                1.7.2 cabal-version:          >= 1.10 author:                 Hans Hoglund maintainer:             Hans Hoglund license:                BSD3 license-file:           COPYING-synopsis:               Abstract representation of musical pitch.+synopsis:               Musical pitch representation. category:               Music tested-with:            GHC build-type:             Simple@@ -14,11 +14,6 @@ description:      This package provides various representations of musical pitch.     .-    Note that this package is independent of @music-score@ and vice versa.-    This is a deliberate design choice: the user should be free to combine any-    pitch representation with any time structure. Normally you will include-    both packages through @music-preludes@.-    .     This library is part of the Music Suite, see     <http://music-suite.github.io>. @@ -33,16 +28,23 @@                         containers,                         semigroups              >= 0.13.0.1 && < 1,                         semigroupoids,-                        music-pitch-literal     == 1.7.1,-                        vector-space            >= 0.8.7 && < 0.9+                        music-pitch-literal     == 1.7.2,+                        vector-space            >= 0.8.7 && < 0.9,+                        vector-space-points     >= 0.2 && < 0.3,+                        nats,+                        type-unary              >= 0.2.16 && < 1.0+                        -- type-level              >= 0.2.4 && < 3.0     exposed-modules:    Music.Pitch                         Music.Pitch.Absolute+                        Music.Pitch.Equal                         Music.Pitch.Common                         Music.Pitch.Common.Interval                         Music.Pitch.Common.Pitch                         Music.Pitch.Common.Semitones                         Music.Pitch.Common.Spell                         Music.Pitch.Common.Harmony+                        Music.Pitch.StaffLines+                        Music.Pitch.Clef                         Music.Pitch.Tokawa                         Music.Pitch.Intonation     hs-source-dirs:     src
src/Music/Pitch.hs view
@@ -9,11 +9,7 @@ -- Stability   : experimental -- Portability : non-portable (TF,GNTD) ----- The "music-pitch" package provides several pitch and interval types. ------ See 'Pitch' and 'Interval' for common--- representations. If you want to use an alternative represention, import the relevant--- submodule.+-- Provides various representations of musical pitches and intervals.  -- ------------------------------------------------------------------------------------- @@ -22,6 +18,7 @@         module Data.Semigroup,         module Data.VectorSpace,         module Data.AffineSpace,+        module Data.AffineSpace.Point,          -- * Pitch representation         @@ -39,6 +36,9 @@         -- --   Includes representation of common practice pitches and intervals such as /c sharp/,          -- --   /diminished second/ and so on.         module Music.Pitch.Common,++        -- * Intonation+        module Music.Pitch.Intonation,                  -- * Pitch literals         module Music.Pitch.Literal,@@ -47,9 +47,12 @@ import Data.Semigroup import Data.VectorSpace hiding (Sum, getSum) import Data.AffineSpace+import Data.AffineSpace.Point  import Music.Pitch.Absolute import Music.Pitch.Augmentable import Music.Pitch.Alterable import Music.Pitch.Common import Music.Pitch.Literal+import Music.Pitch.Intonation+
src/Music/Pitch/Absolute.hs view
@@ -1,5 +1,5 @@ -{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE GeneralizedNewtypeDeriving, TypeFamilies #-}  ------------------------------------------------------------------------------------- -- |@@ -20,6 +20,7 @@  module Music.Pitch.Absolute (         Hertz(..),+        -- FreqRatio(..),         -- Octaves,         Cents,         Fifths,@@ -32,16 +33,29 @@ import Data.Maybe import Data.Either import Data.Semigroup+import Data.VectorSpace+import Data.AdditiveGroup+import Data.AffineSpace import Control.Monad import Control.Applicative +import Music.Pitch.Literal+ -- |  -- Absolute frequency in Hertz.     --  newtype Hertz = Hertz { getHertz :: Double }     deriving (Read, Show, Eq, Enum, Num, Ord, Fractional, Floating, Real, RealFrac) +{- -- | +-- A ratio between two different (Hertz) frequencies.+-- +newtype FreqRatio = FreqRatio { getFreqRatio :: Double }+    deriving (Read, Show, Eq, Enum, Num, Ord, Fractional, Floating, Real, RealFrac)+-}++-- |  -- Number of pure octaves. -- -- Octaves are a logarithmic representation of frequency such that@@ -82,6 +96,35 @@ instance Monoid Fifths      where { mempty  = 0 ; mappend = (+) } instance Monoid Cents       where { mempty  = 0 ; mappend = (+) } +{-+instance AdditiveGroup FreqRatio where+    zeroV   = 1+    (^+^)   = (*)+    negateV f = 1 / f++instance VectorSpace FreqRatio where+    type Scalar FreqRatio = Double+    (*^) x f = FreqRatio ((getFreqRatio f) ** x)++instance AffineSpace Hertz where+  type Diff Hertz = FreqRatio+  (.-.) f1 f2 = FreqRatio $ (getHertz f1) / (getHertz f2)+  (.+^) p f = Hertz $ (getHertz p) * (getFreqRatio f)+-}+instance AdditiveGroup Hertz where+    zeroV   = 1+    (^+^)   = (*)+    negateV f = 1 / f++instance VectorSpace Hertz where+    type Scalar Hertz = Hertz+    (*^) x f = Hertz ((getHertz f) ** getHertz x)++instance AffineSpace Hertz where+  type Diff Hertz = Hertz+  (.-.) = (-)+  (.+^) = (+)+ class HasFrequency a where     frequency :: a -> Hertz @@ -106,5 +149,22 @@ cents :: HasFrequency a => a -> Cents cents a = Cents $ logBase (2/1) (frequency a) * 1200 +-- +-- For convenience. TODO problematic for these reasons:+-- +--  * Confusing as _P8+m2 is not the same as _P8<>m2+--  * Does not work with HasPitch (up, down...) etc as we do not have an affine/vector space pair+--+-- Can we fix this with newtype wrappers?+-- +instance IsInterval Hertz where+  fromInterval (IntervalL (o,d,c)) = (2**fromIntegral o) * (r !! fromIntegral c)+    where+      r = [+        1/1,+        (8*2)/15, 9/8,      (2*3)/5, 5/4,     (2*2)/3,+        10/7,+        3/2,      (4*2)/5,  5/3,     (2*8)/9, 15/8+        ] -                             +
+ src/Music/Pitch/Clef.hs view
@@ -0,0 +1,89 @@++{-# LANGUAGE ConstraintKinds            #-}+{-# LANGUAGE DeriveDataTypeable         #-}+{-# LANGUAGE DeriveFoldable             #-}+{-# LANGUAGE DeriveFunctor              #-}+{-# LANGUAGE DeriveTraversable          #-}+{-# LANGUAGE FlexibleContexts           #-}+{-# LANGUAGE FlexibleInstances          #-}+{-# LANGUAGE GADTs                      #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE MultiParamTypeClasses      #-}+{-# LANGUAGE ScopedTypeVariables        #-}+{-# LANGUAGE TypeFamilies               #-}++module Music.Pitch.Clef (+      ClefSymbol,+      OctaveAdjustment,+      Clef,+      symbolName,+      symbolPitch,+      positionPitch,+      pitchPosition,+  ) where++import Data.Typeable++import Music.Pitch.StaffLines+import Music.Pitch.Common+import Music.Pitch.Literal++data ClefSymbol = GClef | CClef | FClef | PercClef | NeutralClef+    deriving (Eq, Ord, Show, Typeable)++type OctaveAdjustment = Integer++type Clef = (ClefSymbol, OctaveAdjustment, StaffLines)++symbolName :: ClefSymbol -> String+symbolName GClef = "G clef"+symbolName CClef = "C clef"+symbolName FClef = "F clef"+symbolName PercClef = "Percussion clef"+symbolName NeutralClef = "Neutral clef"++symbolPitch :: ClefSymbol -> Maybe Pitch+symbolPitch GClef = Just g+symbolPitch CClef = Just c+symbolPitch FClef = Just f+symbolPitch _     = Nothing++-- TODO consolidate with common+pitchPosition :: Clef -> Pitch -> Maybe StaffLines+pitchPosition (s,o,l) x = undefined+  where+    numbersPerOctave = 7+    referencePitch = symbolPitch s :: Maybe Pitch++positionPitch :: Clef -> StaffLines -> Maybe Pitch+positionPitch (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 = error "Not implemented: upDiatonic"++{-+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.+-}+++-- isModernClef :: Clef -> Bool+-- isHistoricalClef :: Clef -> Bool++-- i.e. a c clef on some staff+-- isVoiceClef :: Clef -> Bool+
src/Music/Pitch/Common/Interval.hs view
@@ -4,7 +4,7 @@  ------------------------------------------------------------------------------------ -- |--- Copyright   : (c) Hans Hoglund 2012+-- Copyright   : (c) Hans Hoglund, Edward Lilley 2012–2014 -- -- License     : BSD-style --@@ -46,9 +46,9 @@         thirteenth,         fourteenth,         fifteenth,  -        +         -- ** Intervals-        Interval,+        Interval(..),          -- *** Creating intervals         mkInterval,@@ -61,7 +61,6 @@         doublyDiminished,          -- *** Inspecting intervals-        octaves,         isNegative,         isPositive,         isNonNegative,@@ -74,12 +73,30 @@         isCompound,         separate,         simple,+        octaves,          -- *** Inversion         invert,          -- * Utility         asInterval,++        -- * Basis values+        IntervalBasis(..),+       +        -- ** Converting basis+        convertBasis,+        convertBasisFloat,+        intervalDiv,+        +        -- ** Basis values (TODO cleanup)+        basis_P1,+        basis_A1,+        basis_d2,+        basis_P8,+        basis_P5,+        +        -- ** Utility         intervalDiff,         mkInterval',   ) where@@ -88,7 +105,8 @@ import Data.Either import Data.Semigroup import Data.VectorSpace-import Data.AffineSpace+-- import Data.AffineSpace+import Data.Basis import Data.Typeable import Control.Monad import Control.Applicative@@ -108,47 +126,41 @@ -- The quality of a compound interval is the quality of the simple interval on -- which it is based. --+-- Note that (Augmented 0) and (Diminished 0) are superfluous identity+-- values, use Perfect instead. Augmented and Diminished must also+-- take only positive arguments.+ data Quality     = Major     | Minor     | Perfect-    | Augmented Integer-    | Diminished Integer+    | Augmented Int+    | Diminished Int     deriving (Eq, Ord, Show)  instance HasQuality Quality where     quality = id --- There is no instance for (Augmentable Quality) as we can not distinguish --- between m/M and P in cases like (augment $ Diminished 1) or --- (diminish $ Augmented 1). --- instance Augmentable Quality where---     augment = go---         where---             go (Diminished 0)   = error "Diminished 0"---             go (Diminished 1)   = Minor -- Or perfect---             go (Diminished n)   = Diminished (n - 1)--- ---             go Minor            = Major---             go Perfect          = Augmented 1---             go Major            = Augmented 1--- ---             go (Augmented 0)    = error "Augmented 0"---             go (Augmented n)    = Augmented (n + 1)---     diminish = go---         where---             go (Diminished 0)   = error "Diminished 0"---             go (Diminished n)   = Diminished (n + 1)--- ---             go Major            = Minor---             go Perfect          = Diminished 1---             go Minor            = Diminished 1+-- | Augmentable Quality instance -- ---             go (Augmented 0)    = error "Augmented 0"---             go (Augmented 1)    = Major -- or Perfect (?)---             go (Augmented n)    = Augmented (n - 1)+-- This Augmentable instance exists solely for use of the getQuality+-- function, which ensures that there is never any ambiguity around+-- diminished/augmented intervals turning into major/minor/perfect+-- intervals. +instance Augmentable Quality where+  augment Major = Augmented 1+  augment Minor = Major+  augment Perfect = Augmented 1+  augment (Augmented n) = Augmented (n + 1)+  augment (Diminished n) = Diminished (n - 1)+  diminish Major = Minor+  diminish Minor = Diminished 1+  diminish Perfect = Diminished 1+  diminish (Augmented n) = Augmented (n - 1)+  diminish (Diminished n) = Diminished (n + 1)+ class HasQuality a where     quality :: a -> Quality @@ -198,38 +210,6 @@ isDiminished :: HasQuality a => a -> Bool isDiminished a = case quality a of { Diminished _ -> True ; _ -> False } --- Convert an offset to a quality.------ This is different for perfect and imperfect interals:------      Imperfect   Perfect---      ===         ===--- -3   dd          ddd--- -2   d           dd--- -1   m           d---  0   M           P---  1   a           a---  2   aa          aa----diffToQuality :: Bool -> Int -> Quality-diffToQuality = go-    where-        go True  0   = Perfect-        go True  n   = if n > 0 then Augmented (fromIntegral n) else Diminished (fromIntegral $ negate n)-        go False 0    = Major-        go False (-1) = Minor-        go False n    = if n > 0 then Augmented (fromIntegral n) else Diminished (fromIntegral $ negate $ n + 1)--qualityToDiff :: Bool -> Quality -> Int-qualityToDiff perfect = go-    where-        go (Diminished n)   = fromIntegral $ negate $ if perfect then n else n + 1-        go Minor            = fromIntegral $ -1-        go Perfect          = fromIntegral $ 0-        go Major            = fromIntegral $ 0-        go (Augmented n)    = fromIntegral $ n-- -- | -- The number portion of an interval (i.e. second, third, etc). --@@ -239,7 +219,7 @@ -- -- > number (a + b) = number a + number b - 1 ---newtype Number = Number { getNumber :: Integer }+newtype Number = Number { getNumber :: Int }     deriving (Eq, Ord, Num, Enum, Real, Integral)      instance Show Number where { show = show . getNumber }@@ -250,67 +230,67 @@ unison      :: Number unison      = 1 --- | A synonym for @2@.+-- | A synonym for @1@. prime       :: Number prime       = 1 --- | A synonym for @3@.+-- | A synonym for @2@. second      :: Number second      = 2 --- | A synonym for @4@.+-- | A synonym for @3@. third       :: Number third       = 3 --- | A synonym for @5@.+-- | A synonym for @4@. fourth      :: Number fourth      = 4      --- | A synonym for @6@.+-- | A synonym for @5@. fifth       :: Number fifth       = 5 --- | A synonym for @7@.+-- | A synonym for @6@. sixth       :: Number sixth       = 6 --- | A synonym for @8@.+-- | A synonym for @7@. seventh     :: Number seventh     = 7 --- | A synonym for @9@.+-- | A synonym for @8@. octave      :: Number octave      = 8 --- | A synonym for @10@.+-- | A synonym for @9@. ninth       :: Number ninth       = 9 --- | A synonym for @11@.+-- | A synonym for @10@. tenth       :: Number tenth       = 10 --- | A synonym for @12@.+-- | A synonym for @11@. eleventh    :: Number eleventh    = 11 --- | A synonym for @13@.+-- | A synonym for @12@. twelfth     :: Number twelfth     = 12 --- | A synonym for @14@.+-- | A synonym for @12@. duodecim    :: Number duodecim    = 12 --- | A synonym for @15@.+-- | A synonym for @13@. thirteenth  :: Number thirteenth  = 13 --- | A synonym for @16@.+-- | A synonym for @14@. fourteenth  :: Number fourteenth  = 14 --- | A synonym for @17@.+-- | A synonym for @15@. fifteenth   :: Number fifteenth   = 15 @@ -341,7 +321,7 @@ -- > m3 ^+^ _M3 = _P5 -- > d5 ^+^ _M6 = m10 ----- The scalar type of 'Interval' is 'Integer', using '^*' to stack intervals of a certain+-- The scalar type of 'Interval' is 'Int', using '^*' to stack intervals of a certain -- type on top of each other. For example @_P5 ^* 2@ is a stack of 2 perfect fifths, or a -- major ninth. The 'Num' instance works as expected for '+', 'negate' and 'abs', and -- (arbitrarily) uses minor seconds for multiplication. If you find yourself '*', or@@ -357,49 +337,30 @@ -- > d5  == diminished fifth == diminish (perfect fifth) -- newtype Interval = Interval { getInterval :: (-            Int,        -- octaves, may be negative-            Int,        -- diatonic remainder (positive) [0..6]-            Int         -- chromatic remainder (positive) [0..11]+            Int,        -- number of A1, i.e. chromatic steps+            Int        -- number of d2, i.e. diatonic steps     ) }     deriving (Eq, Ord, Typeable) +-- | Avoid using '(*)', or 'signum' on intervals. instance Num Interval where     (+)           = addInterval     negate        = negateInterval     abs a         = if isNegative a then negate a else a-    a * b         = fromIntegral (semitones a) `stackInterval` b-    signum a      = if isNegative a then (-m2) else (if isPositive a then m2 else _P1)-    fromInteger 0 = _P1--    -- fromInteger n = n `stackInterval` m2-    fromInteger n = case fromInteger n `divMod` 12 of-        (octave, chromatic) -> Interval (octave, sharpSpelling chromatic, chromatic)-        where-            -- Copied from Spellings (TODO factor out these)-            sharpSpelling = go-                where-                    go 0  = 0-                    go 1  = 0-                    go 2  = 1-                    go 3  = 1-                    go 4  = 2-                    go 5  = 3-                    go 6  = 3-                    go 7  = 4-                    go 8  = 4-                    go 9  = 5-                    go 10 = 5-                    go 11 = 6+    (*)           = error "Music.Pitch.Common.Interval: no overloading for (*)"+    signum        = error "Music.Pitch.Common.Interval: no overloading for signum"+    fromInteger   = error "Music.Pitch.Common.Interval: no overloading for fromInteger"          instance Show Interval where-    show a | isNegative a = "-" ++ showQuality (quality a) ++ show (abs $ number a)-           | otherwise    =        showQuality (quality a) ++ show (abs $ number a)-           where-               showQuality Major            = "_M"-               showQuality Minor            = "m"-               showQuality Perfect          = "_P"-               showQuality (Augmented n)    = "_" ++ replicate' n 'A'-               showQuality (Diminished n)   = replicate' n 'd'+  show a+    | isNegative a = "-" ++ showQuality (extractQuality a) ++ show (abs $ extractNumber a)+    | otherwise    =        showQuality (extractQuality a) ++ show (abs $ extractNumber a)+    where+      showQuality Major            = "_M"+      showQuality Minor            = "m"+      showQuality Perfect          = "_P"+      showQuality (Augmented n)    = "_" ++ replicate' n 'A'+      showQuality (Diminished n)   = replicate' n 'd'  instance Semigroup Interval where     (<>)    = addInterval@@ -417,39 +378,47 @@     type Scalar Interval = Integer     (*^) = stackInterval +-- TODO move+data IntervalBasis = Chromatic | Diatonic+  deriving (Eq, Ord, Show, Enum)++instance HasBasis Interval where+  type Basis Interval = IntervalBasis+  basisValue Chromatic = basis_A1+  basisValue Diatonic  = basis_d2+  decompose  (Interval (c,d)) = [(Chromatic, fromIntegral c), (Diatonic, fromIntegral d)]+  decompose' (Interval (c,d)) Chromatic = fromIntegral c+  decompose' (Interval (c,d)) Diatonic  = fromIntegral d+ instance HasQuality Interval where-    quality (Interval (o, d, c))-        | o >= 0    =                 diffToQuality (isPerfectNumber d) (c - diatonicToChromatic d)-        | otherwise = invertQuality $ diffToQuality (isPerfectNumber d) (c - diatonicToChromatic d)+  quality i = extractQuality i  instance HasNumber Interval where-    number (Interval (o, d, c)) = fromIntegral $ inc $ o * 7 + d-        where-            inc a = if a >= 0 then succ a else pred a+  number i = extractNumber i  instance Augmentable Interval where-    augment  (Interval (o, d, c)) = Interval (o, d, c + 1)-    diminish (Interval (o, d, c)) = Interval (o, d, c - 1)---- |--- Returns the non-simple part of an interval.------ > (perfect octave)^*x + y = z  iff  y = simple z----octaves = fst . separate+  augment i = i ^+^ basis_A1+  diminish i = i ^-^ basis_A1  instance HasSemitones Interval where-    semitones (Interval (o, d, c)) = fromIntegral $ o * 12 + c+  semitones (Interval (a, d)) = fromIntegral a -- assuming "semitone" == A1  instance IsInterval Interval where-    fromInterval (IntervalL (o,d,c)) = Interval (fromIntegral o, fromIntegral d, fromIntegral c)+  fromInterval (IntervalL (o,d,c)) = (basis_P8^*o) ^+^ (basis_A1^*c) ^+^ (basis_d2^*d) --- |--- This is just the identity function, but is useful to fix the type of 'Interval'.----asInterval :: Interval -> Interval-asInterval = id+negateInterval :: Interval -> Interval+negateInterval (Interval (a, d)) = Interval (-a, -d) +addInterval :: Interval -> Interval -> Interval+addInterval (Interval (a1, d1)) (Interval (a2, d2)) = Interval (a1 + a2, d1 + d2)++stackInterval :: Integer -> Interval -> Interval+stackInterval n a | n >= 0    = mconcat $ replicate (fromIntegral n) a+                  | otherwise = negate $ stackInterval (negate n) a++intervalDiff :: Interval -> Int+intervalDiff (Interval (c, d)) = c - diatonicToChromatic d+ -- | -- Creates an interval from a quality and number. --@@ -457,16 +426,133 @@ -- major interval instead. Given 'Major' or 'Minor' with a number indicating a perfect -- consonance, 'interval' returns a perfect or diminished interval respectively. --+mkInterval' +  :: Int        -- ^ Difference in chromatic steps (?).+  -> Int        -- ^ Number of diatonic steps (NOT interval number).+  -> Interval+mkInterval' diff diatonic = Interval (diatonicToChromatic diatonic + diff, diatonic)++basis_P1 = Interval (0, 0)+basis_A1 = Interval (1, 0)+basis_d2 = Interval (0, 1)+basis_P5 = Interval (7, 4)+basis_P8 = Interval (12, 7)++ mkInterval :: Quality -> Number -> Interval-mkInterval quality number = mkInterval' (qualityToDiff (isPerfectNumber diatonic) quality) (fromIntegral number)-    where-        (_, diatonic) = (fromIntegral $ number - 1) `divMod` 7 -mkInterval' :: Int -> Int -> Interval-mkInterval' diff number = Interval (octave, diatonic, diatonicToChromatic diatonic + diff)-    where-        (octave, diatonic) = (number - 1) `divMod` 7+ -- our identity:+mkInterval Perfect 1 = basis_P1+ -- and our two basis vectors:+mkInterval (Augmented 1) 1 = basis_A1+mkInterval (Diminished 1) 2 = basis_d2 +mkInterval Minor 2 = basis_d2 ^+^ basis_A1+mkInterval Major 2 = (mkInterval Minor 2) ^+^ basis_A1+mkInterval (Augmented 1) 2 = (mkInterval Major 2) ^+^ basis_A1++mkInterval (Diminished 1) 3 = (mkInterval Minor 3) ^-^ basis_A1+mkInterval Minor 3 = (mkInterval Major 2) ^+^ (mkInterval Minor 2)+mkInterval Major 3 = (mkInterval Major 2) ^+^ (mkInterval Major 2)+mkInterval (Augmented 1) 3 =  (mkInterval Major 3) ^+^ basis_A1++mkInterval (Diminished 1) 4 = (mkInterval Perfect 4) ^-^ basis_A1+mkInterval Perfect 4 = (mkInterval Major 3) ^+^ (mkInterval Minor 2)+mkInterval (Augmented 1) 4 =  (mkInterval Perfect 4) ^+^ basis_A1++mkInterval (Diminished 1) 5 = (mkInterval Perfect 5) ^-^ basis_A1+mkInterval Perfect 5 = (mkInterval Perfect 4) ^+^ (mkInterval Major 2)+mkInterval (Augmented 1) 5 =  (mkInterval Perfect 5) ^+^ basis_A1++mkInterval (Diminished 1) 6 = (mkInterval Minor 6) ^-^ basis_A1+mkInterval Minor 6 = (mkInterval Perfect 5) ^+^ (mkInterval Minor 2)+mkInterval Major 6 = (mkInterval Perfect 5) ^+^ (mkInterval Major 2)+mkInterval (Augmented 1) 6 =  (mkInterval Major 6) ^+^ basis_A1++mkInterval (Diminished 1) 7 = (mkInterval Minor 7) ^-^ basis_A1+mkInterval Minor 7 = (mkInterval Major 6) ^+^ (mkInterval Minor 2)+mkInterval Major 7 = (mkInterval Major 6) ^+^ (mkInterval Major 2)+mkInterval (Augmented 1) 7 =  (mkInterval Major 7) ^+^ basis_A1++mkInterval Minor 1 = error "invalid interval"+mkInterval Major 1 = error "invalid interval"+mkInterval Perfect 2 = error "invalid interval"+mkInterval Perfect 3 = error "invalid interval"+mkInterval Minor 4 = error "invalid interval"+mkInterval Major 4 = error "invalid interval"+mkInterval Minor 5 = error "invalid interval"+mkInterval Major 5 = error "invalid interval"+mkInterval Perfect 6 = error "invalid interval"+mkInterval Perfect 7 = error "invalid interval"++mkInterval (Diminished 0) n = error "(Diminished 0) is not a valid Quality"+mkInterval (Augmented 0) n = error  "(Augmented 0) is not a valid Quality"++mkInterval (Diminished q) n = (mkInterval (Diminished (q - 1)) n) ^-^ basis_A1+mkInterval (Augmented q) n = (mkInterval (Diminished (q - 1)) n) ^+^ basis_A1++mkInterval q (Number n) = if n > 0+                          then (mkInterval q (Number (n - 7))) ^+^ basis_P8+                          else (mkInterval q (Number (n + 7))) ^-^ basis_P8+++-- |+-- Extracting the 'number' from an interval vector.+--+-- Note that (a, d) is a representation of the interval (a * A1) + (d+-- * d2), so the 'number' part of the interval must be stored entirely+-- in the d * d2 part (adding a unison, perfect or otherwise, can+-- never increase the number of the interval)+-- +extractNumber :: Interval -> Number+extractNumber (Interval (a, d))+  | d >= 0    = Number (d + 1)+  | otherwise = Number (d - 1)+++-- |+-- Extracting the 'quality' from an interval vector.+--+-- This is much more finicky, as the A1 and d2 intervals interact in a+-- complex way to produce the perfect/major/minor/etc. intervals that+-- we are used to reading.++extractQuality :: Interval -> Quality+extractQuality (Interval (a, d))+  | (a < 0) && (d == 0) = diminish (extractQuality (Interval ((a + 1), d)))+  | (a, d) == (0, 0) = Perfect+  | (a > 0) && (d == 0) = augment (extractQuality (Interval ((a - 1), d)))+  | (a < 1) && (d == 1) = diminish (extractQuality (Interval ((a + 1), d)))+  | (a, d) == (1, 1) = Minor+  | (a, d) == (2, 1) = Major+  | (a > 2) && (d == 1) = augment (extractQuality (Interval ((a - 1), d)))+  | (a < 3) && (d == 2) = diminish (extractQuality (Interval ((a + 1), d)))+  | (a, d) == (3, 2) = Minor+  | (a, d) == (4, 2) = Major+  | (a > 4) && (d == 2) = augment (extractQuality (Interval ((a - 1), d)))+  | (a < 5) && (d == 3) = diminish (extractQuality (Interval ((a + 1), d)))+  | (a, d) == (5, 3) = Perfect+  | (a > 5) && (d == 3) = augment (extractQuality (Interval ((a - 1), d)))+  | (a < 7) && (d == 4) = diminish (extractQuality (Interval ((a + 1), d)))+  | (a, d) == (7, 4) = Perfect+  | (a > 7) && (d == 4) = augment (extractQuality (Interval ((a - 1), d)))+  | (a < 8) && (d == 5) = diminish (extractQuality (Interval ((a + 1), d)))+  | (a, d) == (8, 5) = Minor+  | (a, d) == (9, 5) = Major+  | (a > 9) && (d == 5) = augment (extractQuality (Interval ((a - 1), d)))+  | (a < 10) && (d == 6) = diminish (extractQuality (Interval ((a + 1), d)))+  | (a, d) == (10, 6) = Minor+  | (a, d) == (11, 6) = Major+  | (a > 11) && (d == 6) = augment (extractQuality (Interval ((a - 1), d)))+  | (a < 12) && (d == 7) = diminish (extractQuality (Interval ((a + 1), d)))+  | (a, d) == (12, 7) = Perfect+  | (a > 12) && (d == 7) = augment (extractQuality (Interval ((a - 1), d)))+-- note: these last two cases *have* to be this way round, otherwise+-- infinite loop occurs.+  | (a > 12) || (d > 7) = extractQuality (Interval ((a - 12), (d - 7)))+  | (a < 0) || (d < 0) = extractQuality (Interval ((-a), (-d)))++ -- | Creates a perfect interval. --   If given an inperfect number, constructs a major interval. perfect :: Number -> Interval@@ -498,41 +584,45 @@ doublyDiminished :: Number -> Interval doublyDiminished = mkInterval (Diminished 2) --invertDiatonic :: Num a => a -> a-invertDiatonic d  = 7  - d--invertChromatic :: Num a => a -> a-invertChromatic c = 12 - c--negateInterval :: Interval -> Interval-negateInterval (Interval (o, 0, 0))    = Interval (negate o, 0, 0)-negateInterval (Interval (oa, da, ca)) = Interval (negate (oa + 1), invertDiatonic da, invertChromatic ca)--addInterval :: Interval -> Interval -> Interval-addInterval (Interval (oa, da,ca)) (Interval (ob, db,cb))-    = Interval (oa + ob + carry, steps, chroma)-    where-        (carry, steps) = (da + db) `divMod` 7-        chroma         = trunc (ca + cb)-        trunc          = if carry > 0 then (`mod` 12) else id--stackInterval :: Integer -> Interval -> Interval-stackInterval n a | n >= 0    = mconcat $ replicate (fromIntegral n) a-                  | otherwise = negate $ stackInterval (negate n) a+{- -intervalDiff :: Interval -> Int-intervalDiff (Interval (o, d, c)) = c - diatonicToChromatic d+Prelude Music.Prelude> separate (2*^_P8+m3)+(2,m3)+Prelude Music.Prelude> +Prelude Music.Prelude> separate (3*^_P8+m3)+(3,m3)+Prelude Music.Prelude> +Prelude Music.Prelude> separate (0*^_P8+m3)+(0,m3)+Prelude Music.Prelude> separate ((-1)*^_P8+m3) +-} -- | -- Separate a compound interval into octaves and a simple interval. -- -- > (perfect octave)^*x + y = z  iff  (x, y) = separate z -- separate :: Interval -> (Octaves, Interval)-separate (Interval (o, d, c)) = (fromIntegral o, Interval (0, d, c))+separate i = (fromIntegral o, i ^-^ (fromIntegral o *^ basis_P8))+  where+    o = octaves i  -- |+-- Returns the non-simple part of an interval.+--+-- > _P8^*octaves x ^+^ simple x = x+--+octaves :: Interval -> Octaves+octaves i +  | isNegative i && not (isOctaveMultiple i) = negate (octaves' i) - 1+  | isNegative i && isOctaveMultiple i       = negate (octaves' i)+  | otherwise                                = octaves' i++isOctaveMultiple (Interval (_,d)) = d `mod` 7 == 0++octaves' i = fromIntegral $ intervalDiv i basis_P8++-- | -- Returns the simple part of an interval. -- -- > (perfect octave)^*x + y = z  iff  y = simple z@@ -552,7 +642,7 @@ -- Returns whether the given interval is compound. -- -- A compound interval is either a negative interval, or a positive interval spanning--- more than octave.+-- one octave or more. -- isCompound :: Interval -> Bool isCompound x = octaves x /= 0@@ -561,19 +651,19 @@ -- Returns whether the given interval is negative. -- isNegative :: Interval -> Bool-isNegative x = octaves x < 0+isNegative (Interval (a, d)) = d < 0  -- | -- Returns whether the given interval is positive. -- isPositive :: Interval -> Bool-isPositive x = octaves x >= 0 && not (isPerfectUnison x)+isPositive x@(Interval (a, d)) = d >= 0 && not (isPerfectUnison x)  -- | -- Returns whether the given interval is non-negative. This implies that it is either positive or a perfect unison. -- isNonNegative :: Interval -> Bool-isNonNegative x = octaves x >= 0+isNonNegative (Interval (a, d)) = d >= 0  -- | -- Returns whether the given interval a perfect unison.@@ -589,7 +679,7 @@ -- semitones. -- isStep :: Interval -> Bool-isStep x = isSimple (abs x) && number (abs x) <= 2+isStep (Interval (a, d)) = (abs d) <= 2  -- | -- Returns whether the given interval is a leap (larger than a second).@@ -599,7 +689,7 @@ -- semitones. -- isLeap :: Interval -> Bool-isLeap x = isCompound (abs x) || number (abs x) > 2+isLeap (Interval (a, d)) = (abs d) > 2   -- |@@ -616,8 +706,13 @@ invert :: Interval -> Interval invert = simple . negate -+-- |+-- This is just the identity function, but is useful to fix the type of 'Interval'.+--+asInterval :: Interval -> Interval+asInterval = id +{- isPerfectNumber :: Int -> Bool isPerfectNumber 0 = True isPerfectNumber 1 = False@@ -626,19 +721,85 @@ isPerfectNumber 4 = True isPerfectNumber 5 = False isPerfectNumber 6 = False+-} +-- TODO more generic pattern here diatonicToChromatic :: Int -> Int-diatonicToChromatic = go+diatonicToChromatic d = (octaves*12) + go restDia     where-        go 0 = 0-        go 1 = 2-        go 2 = 4-        go 3 = 5-        go 4 = 7-        go 5 = 9-        go 6 = 11+        -- restDia is always in [0..6]+        (octaves, restDia) = d `divMod` 7+        go = ([0,2,4,5,7,9,11] !!) -{-# DEPRECATED intervalDiff "This should be hidden" #-}-{-# DEPRECATED mkInterval'  "This should be hidden "#-}+-- {-# DEPRECATED intervalDiff "This should be hidden" #-}+-- {-# DEPRECATED mkInterval'  "This should be hidden "#-}  replicate' n = replicate (fromIntegral n)+++++-- | Integer div of intervals: i / di = x, where x is an integer+intervalDiv :: Interval -> Interval -> Int+intervalDiv (Interval (a, d)) (Interval (1, 0)) = a+intervalDiv (Interval (a, d)) (Interval (0, 1)) = d+intervalDiv i di+  | (i > basis_P1) = intervalDivPos i di+  | (i < basis_P1) = intervalDivNeg i di+  | otherwise = 0 :: Int+  where +    intervalDivPos i di+      | (i < basis_P1) = undefined+      | (i ^-^ di) < basis_P1 = 0+      | otherwise = 1 + (intervalDiv (i ^-^ di) di)+    intervalDivNeg i di+      | (i > basis_P1) = undefined+      | (i ^+^ di) > basis_P1 = 0+      | otherwise = 1 + (intervalDiv (i ^+^ di) di)++-- | Represent an interval i in a new basis (j, k).+-- +-- We want x,y where i = x*j + y*k+--+-- e.g., convertBasis basis_d2 _P5 basis_P8 == Just (-12,7), as expected.++convertBasis+  :: Interval +  -> Interval +  -> Interval +  -> Maybe (Int, Int)+convertBasis i j k+  | (p == 0) = Nothing+  | not $ p `divides` r = Nothing+  | not $ p `divides` q = Nothing+  | otherwise = Just (r `div` p, q `div` p)+  where Interval (m, n) = i+        Interval (a, b) = j+        Interval (c, d) = k+        p = (a*d - b*c)+        q = (a*n - b*m)+        r = (d*m - c*n)+++-- | Same as above, but don't worry if new interval has non-integer+-- coefficients -- useful when getting a value to use as a frequency+-- ratio in a tuning system.+convertBasisFloat :: (Fractional t, Eq t)+  => Interval +  -> Interval +  -> Interval +  -> Maybe (t, t)+convertBasisFloat i j k+  | (p == 0) = Nothing+  | otherwise = Just (r / p, q / p)+  where Interval (m, n) = i+        Interval (a, b) = j+        Interval (c, d) = k+        p = fromIntegral $ (a*d - b*c)+        q = fromIntegral $ (a*n - b*m)+        r = fromIntegral $ (d*m - c*n)+++divides :: Integral a => a -> a -> Bool+x `divides` y = (y `rem` x) == 0+
src/Music/Pitch/Common/Pitch.hs view
@@ -4,7 +4,7 @@  ------------------------------------------------------------------------------------ -- |--- Copyright   : (c) Hans Hoglund 2012+-- Copyright   : (c) Hans Hoglund, Edward Lilley 2012–2014 -- -- License     : BSD-style --@@ -35,7 +35,9 @@         Name(..),          -- * Pitch-        Pitch,    +        Pitch,+        Pitch(..),+                 mkPitch,         name,         accidental,@@ -186,8 +188,16 @@ -- > d5  == diminished fifth == diminish (perfect fifth) -- newtype Pitch = Pitch { getPitch :: Interval }-    deriving (Eq, Num, Ord, Typeable)+    deriving (Eq, Ord, Typeable)     +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   = error "Music.Pitch.Common.Pitch: no overloading for fromInteger"+ instance AffineSpace Pitch where     type Diff Pitch     = Interval     Pitch a .-. Pitch b = a ^-^ b@@ -209,8 +219,8 @@     flatten (Pitch a) = Pitch (diminish a)  instance Enum Pitch where-    toEnum = (c .+^) . perfect . fromIntegral-    fromEnum = fromIntegral . number . (.-. c)+    toEnum = Pitch . mkInterval' 0 . fromIntegral+    fromEnum = fromIntegral . pred . number . (.-. c)  -- | -- This is just the identity function, but is useful to fix the type of 'Pitch'.@@ -222,7 +232,7 @@ -- Creates a pitch from name accidental. -- mkPitch :: Name -> Accidental -> Pitch-mkPitch name acc = Pitch $ mkInterval' (fromIntegral acc) (fromEnum name + 1)+mkPitch name acc = Pitch $ mkInterval' (fromIntegral acc) (fromEnum name)  -- | -- Returns the name of a pitch.@@ -235,7 +245,12 @@ -- @ -- name :: Pitch -> Name-name = toEnum . fromIntegral . pred . number . simple . getPitch+name x               +  | i == 7           = toEnum 0 -- Arises for flat C etc.+  | 0 <= i && i <= 6 = toEnum i+  | otherwise        = error $ "Pitch.name: Bad value " ++ show i+  where+    i = (fromIntegral . pred . number . simple . getPitch) x  -- | -- Returns the accidental of a pitch.@@ -252,7 +267,7 @@  instance IsPitch Pitch where     fromPitch (PitchL (c, a, o)) =-        Pitch $ mkInterval' (qual a) (c + 1)+        Pitch $ mkInterval' (qual a) c             ^+^             (perfect octave^* fromIntegral o)         where
src/Music/Pitch/Common/Semitones.hs view
@@ -3,7 +3,7 @@  ------------------------------------------------------------------------------------ -- |--- Copyright   : (c) Hans Hoglund 2012+-- Copyright   : (c) Hans Hoglund, Edward Lilley 2012–2014 -- -- License     : BSD-style --@@ -11,7 +11,7 @@ -- Stability   : experimental -- Portability : non-portable (TF,GNTD) ----- Provides semitone, octave and step representaiton of intervals.+-- Provides semitone, octave and step representation of intervals. -- ------------------------------------------------------------------------------------- @@ -113,11 +113,14 @@ -- -- 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 as in:+-- 'Spelling' must be provided: ----- > spell sharps tritone == augmented fourth--- > spell flats  tritone == diminished fifth+-- >>> spell usingSharps tritone+-- _A4 --+-- >>> spell usingFlats  tritone+-- d5+-- newtype Semitones = Semitones { getSemitones :: Integer }     deriving (Eq, Ord, Num, Enum, Real, Integral) @@ -135,12 +138,14 @@     -- The number of semitones is negative if and only if the interval is     -- negative.     ---    -- Examples:-    ---    -- > semitones (perfect unison)  =  0-    -- > semitones tritone           =  6-    -- > semitones d5                =  6-    -- > semitones (-_P8)            =  -12+    -- >>> semitones (perfect unison)+    -- 0+    -- >>> semitones tritone+    -- 6+    -- >>> semitones d5+    -- 6+    -- >>> semitones (-_P8)+    -- -12     --     semitones :: a -> Semitones @@ -180,11 +185,21 @@ -- | -- 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
src/Music/Pitch/Common/Spell.hs view
@@ -3,7 +3,7 @@  ------------------------------------------------------------------------------------ -- |--- Copyright   : (c) Hans Hoglund 2012+-- Copyright   : (c) Hans Hoglund, Edward Lilley 2012–2014 -- -- License     : BSD-style --@@ -85,7 +85,7 @@     (octaves, steps) = semitones x `divMod` 12     num  = fromIntegral (spelling steps)     diff = fromIntegral steps - fromIntegral (diatonicToChromatic num)-    in mkInterval' diff (num + 1) ^+^ _P8^*(fromIntegral octaves)+    in mkInterval' diff num ^+^ _P8^*(fromIntegral octaves)     where         diatonicToChromatic = go             where
+ src/Music/Pitch/Equal.hs view
@@ -0,0 +1,109 @@++{-# LANGUAGE RankNTypes #-}++-------------------------------------------------------------------------------------+-- |+-- Copyright   : (c) Hans Hoglund 2012+--+-- License     : BSD-style+--+-- Maintainer  : hans@hanshoglund.se+-- Stability   : experimental+-- Portability : portable+--+-- Equal temperament pitch of any size.+--+-------------------------------------------------------------------------------------++module Music.Pitch.Equal (+    -- Equal,+    -- toEqual,+    -- Equal6,+    -- Equal12,+    -- Equal17,+    -- Equal24,+    -- Equal36,+    -- -- toEqual',+    -- unsafeToEqual,+    -- value,+    -- size,+    -- cast,+)+where++-- {-+-- +-- TODO mixing things up here+-- +--   This type implements limited values (useful for interval *steps*)+--   An ET-interval is just an int, with a type-level size (divMod is "separate")+-- +-- +-- -}+-- +-- +-- import Data.Maybe+-- import Data.Either+-- import Data.Semigroup+-- import Control.Monad+-- import Control.Applicative+-- import Music.Pitch.Absolute+-- +-- import Data.TypeLevel.Num hiding ((<), (>), (+), (<=), (*))+-- import qualified Data.TypeLevel.Num as TL+-- +-- data Foo = Foo Int deriving Show+-- +-- newtype Equal a = Equal { getEqual :: Pos a => Int }+--   deriving ()+-- +-- instance Pos a => Show (Equal a) where+--   -- show (Equal x) = "(Equal " ++ (show x) ++ ")"+--   showsPrec d (Equal x) = showParen (d > app_prec) $+--        showString "Equal " . showsPrec (app_prec+1) x+--     where app_prec = 10+-- +-- -- Convenience to avoid ScopedTypeVariables etc    +-- getSize :: Pos a => Equal a -> a+-- getSize = undefined+-- +-- -- | Value, which is in @[0,size x)@.+-- value :: Pos a => Equal a -> Int+-- value = getEqual+-- +-- -- | Size of this type (value not evaluated).+-- size :: Pos a => Equal a -> Int+-- size = toInt . getSize+-- +-- toEqual :: Pos a => Int -> Maybe (Equal a)+-- toEqual = toEqual' undefined+-- +-- toEqual' :: Pos a => a -> Int -> Maybe (Equal a)+-- toEqual' s n = if 0 <= n && n < toInt s then Just (Equal n) else Nothing+-- +-- unsafeToEqual :: Int -> Equal a+-- unsafeToEqual n = Equal n+-- +-- type Equal6  = Equal D6+-- type Equal12 = Equal D12+-- type Equal17 = Equal D17+-- type Equal24 = Equal D24+-- type Equal36 = Equal D36+-- +-- -- unsafemapValue :: (Int -> Int) -> Equal a -> Equal b+-- -- mapValue+-- +-- -- b = 24, a = 12, mul = 24/12+-- -- | Safely cast a tempered value to a larger size.+-- --+-- -- >>> cast (unsafeToEqual 1 :: Equal12) :: Equal24+-- -- Equal 2 :: Equal24+-- --+-- cast :: (Pos a, Pos b, Pos c, Div b a c) => Equal a -> Equal b+-- cast = cast' undefined+-- +-- cast' :: (Pos a, Pos b, Pos c, Div b a c) => Equal c -> Equal a -> Equal b+-- cast' cDummy (Equal a) = Equal (a * size cDummy)+-- +-- +-- 
src/Music/Pitch/Intonation.hs view
@@ -13,24 +13,102 @@ -- ------------------------------------------------------------------------------------- -module Music.Pitch.Intonation -- (--- )+module Music.Pitch.Intonation (+      Intonation,+      Tuning,++      intone,+      -- makeBasis,+      synTune,+      tetTune,+      pureOctaveWith,++      -- * Specific tunings+      pythagorean,+      quarterCommaMeantone,+      schismaticMeantone,+      fiveToneEqual,+      sevenToneEqual,+      twelveToneEqual,+      nineteenToneEqual,+      thirtyOneToneEqual,+      fiftyThreeToneEqual,++      -- * Specific intonations+      -- standardTuning,+      standardIntonation,+) where  import Data.Maybe import Data.Either import Data.Semigroup+import Data.VectorSpace+import Data.AffineSpace import Control.Monad import Control.Applicative+ import Music.Pitch.Absolute+import Music.Pitch.Literal+import Music.Pitch.Common.Interval+import Music.Pitch.Common.Pitch -type Intonation a = a -> Hertz+type Intonation p = p -> Hertz+type Tuning i = i -> {-FreqRatio-}Hertz -pure :: Integral a => Intonation a-pure = undefined+synTune :: (Interval, {-FreqRatio-}Hertz) -> (Interval, {-FreqRatio-}Hertz) -> Interval -> {-FreqRatio-}Hertz+synTune (i1, i1rat) (i2, i2rat) (Interval (a1, d2)) =+  ((makeA1 (i1, i1rat) (i2, i2rat)) ^* (fromIntegral a1)) ^+^ ((maked2 (i1, i1rat) (i2, i2rat)) ^* (fromIntegral d2))+  where makeA1 = makeBasis basis_A1+        maked2 = makeBasis basis_d2 -pythagorean :: Integral a => Intonation a-pythagorean = undefined+makeBasis :: Interval -> (Interval, {-FreqRatio-}Hertz) -> (Interval, {-FreqRatio-}Hertz) -> {-FreqRatio-}Hertz+makeBasis i (i1, r1) (i2, r2) = case (convertBasisFloat i i1 i2) of+  Just (x, y) -> (x *^ r1) ^+^ (y *^ r2)+  Nothing -> error ("Cannot use intervals " ++ (show i1) ++ " and " ++ (show i2) ++ " as basis pair to represent " ++ (show i)) -twelveToneEqual :: Integral a => Intonation a-twelveToneEqual = undefined+-- | Turn a tuning into an intonation.+intone :: (Pitch, Hertz) -> Tuning Interval -> Intonation Pitch+intone (b, f) t p = f .+^ (t i) where i = p .-. b+-- More generally:+-- intone :: AffineSpace p => (p, Hertz) -> Tuning (Diff p) -> Intonation p+++-- Standard syntonic (meantone) tunings, with P8 = 2++pureOctaveWith = synTune (_P8, 2)++pythagorean :: Tuning Interval+pythagorean = pureOctaveWith (_P5, 3/2)++quarterCommaMeantone :: Tuning Interval+quarterCommaMeantone = pureOctaveWith (_M3, 5/4)++schismaticMeantone :: Tuning Interval+schismaticMeantone = pureOctaveWith (8 *^ _P4, 10)++-- TET tunings, i.e. where P8 = 2 and (some other interval) = 1++tetTune i = pureOctaveWith (i, 1)++fiveToneEqual :: Tuning Interval+fiveToneEqual = tetTune m2++sevenToneEqual :: Tuning Interval+sevenToneEqual = tetTune _A1++twelveToneEqual :: Tuning Interval+twelveToneEqual = tetTune d2++nineteenToneEqual :: Tuning Interval+nineteenToneEqual = tetTune dd2 where dd2 = d2 ^-^ _A1++thirtyOneToneEqual :: Tuning Interval+thirtyOneToneEqual = tetTune dddd3 where dddd3 = m3 ^-^ (4 *^ _A1)++fiftyThreeToneEqual :: Tuning Interval+fiftyThreeToneEqual = tetTune ddddddd6 where ddddddd6 = 31 *^ _P8 ^-^ 53 *^ _P5 -- (!)++-- | Modern standard intonation, i.e. 12-TET with @a = 440 Hz@.+standardIntonation :: Intonation Pitch+standardIntonation = intone (a, 440) twelveToneEqual
+ src/Music/Pitch/StaffLines.hs view
@@ -0,0 +1,65 @@++{-# LANGUAGE ConstraintKinds            #-}+{-# LANGUAGE DeriveDataTypeable         #-}+{-# LANGUAGE DeriveFoldable             #-}+{-# LANGUAGE DeriveFunctor              #-}+{-# LANGUAGE DeriveTraversable          #-}+{-# LANGUAGE FlexibleContexts           #-}+{-# LANGUAGE FlexibleInstances          #-}+{-# LANGUAGE GADTs                      #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE MultiParamTypeClasses      #-}+{-# LANGUAGE ScopedTypeVariables        #-}+{-# LANGUAGE TypeFamilies               #-}++module Music.Pitch.StaffLines (+        StaffLines,+        HalfSpaces,+  ) where++import Numeric.Natural+import Data.Typeable++-- TODO maybe parameterize stafflines on number of staves++-- |+-- For notation systems using staff lines (i.e. CMN), this type represents staff number.+--+-- Rules:+--+-- * Incrementing staff lines means moving upward.--+--+-- * For staff systems with an odd number of lines, 0 is the middle staff.+--+-- * For staff systems with an even number of lines, 0 is the staff below the middle space.+--+-- I.e. in CMN the staff lines are @[-2..2]@, with negative numbers representing the lines+-- below the middle line and positions numbers representing the lines above it.+--+newtype StaffLines = StaffLines { getStaffLines :: Integer }+  deriving (Eq, Ord, Read, Show, Enum,+           Num, Real, Integral, Typeable)++-- |+-- For notation systems using staff lines, this type represents the difference betwee two+-- staff positions (i.e. one diatonic step in CMN).+--+-- Rules:+--+-- * Incrementing half spaces means moving upward.+--+-- * For staff systems with an odd number of lines, 0 is the middle staff.+--+-- * For staff systems with an even number of lines, 0 is the middle space.+--+-- I.e. in CMN, the non-ledger positions are @[-5..5]@ with the even numbers representing+-- lines and the odd representing spaces and with positive numbers representing positions+-- above the middle line and negative numbers representing positions below.+--+newtype HalfSpaces = HalfSpaces { getHalfSpaces :: Integer }+  deriving (Eq, Ord, Read, Show, Enum,+           Num, Real, Integral, Typeable)+++-- staffLinesForNumber :: Int -> StaffLines+-- staffLinesForNumber