-- | Intonation and tuning. 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 Data.Basis import Control.Monad import Control.Applicative import Data.Fixed import Data.Ratio import Control.Lens import Music.Pitch.Absolute import Music.Pitch.Literal as Intervals import Music.Pitch.Common.Interval import Music.Pitch.Common.Pitch newtype Tuning i = Tuning { getTuning :: i -> Double } newtype Intonation p = Intonation { getIntonation :: p -> Hertz } basis_A1 :: Interval basis_A1 = basisValue Chromatic basis_d2 :: Interval basis_d2 = basisValue Diatonic synTune :: (Interval, Double) -> (Interval, Double) -> Interval -> Double synTune (i1, i1rat) (i2, i2rat) (view (from 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 makeBasis :: Interval -> (Interval, Double) -> (Interval, Double) -> Double makeBasis i (i1, r1) (i2, r2) = case (convertBasisFloat i i1 i2) of Just (x, y) -> (r1 ** x) * (r2 ** y) Nothing -> error ("Cannot use intervals " ++ (show i1) ++ " and " ++ (show i2) ++ " as basis pair to represent " ++ (show i)) -- | Turn a tuning into an intonation. intone :: (Pitch, Hertz) -> Tuning Interval -> Intonation Pitch intone (b, f) (Tuning t) = Intonation $ int where int 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 :: (Interval, Double) -> Tuning Interval pureOctaveWith = Tuning . 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 :: Interval -> Tuning Interval 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 {- Possible instances for numeric types based on standard intonation. Not used, the user should choose perform the appropriate conversion for a given tuning system. instance IsInterval Double where fromInterval i = getTuning twelveToneEqual $ fromInterval i instance IsInterval Float where fromInterval x = realToFrac (fromInterval x :: Double) instance HasResolution a => IsInterval (Fixed a) where fromInterval x = realToFrac (fromInterval x :: Double) instance Integral a => IsInterval (Ratio a) where fromInterval x = realToFrac (fromInterval x :: Double) -}