hmt-0.12: Music/Theory/Tuning/Table.hs
-- | Tuning tables
module Music.Theory.Tuning.Table where
import qualified Music.Theory.Diagram.Grid as G
import Music.Theory.List
import Music.Theory.Pitch
import Music.Theory.Pitch.Spelling
import Music.Theory.Tuning
import qualified Text.HTML.Light as H {- html-minimalist -}
import Text.Printf
-- * Equal temperament
-- | 'octpc_to_pitch' and 'octpc_to_cps'.
octpc_to_pitch_cps :: (Floating n) => OctPC -> (Pitch,n)
octpc_to_pitch_cps x = (octpc_to_pitch pc_spell_ks x,octpc_to_cps x)
-- | 12-tone equal temperament table equating 'Pitch' and frequency
-- over range of human hearing, where @A4@ = @440@hz.
--
-- > length tbl_12et == 132
-- > min_max (map (round . snd) tbl_12et) == (16,31609)
tbl_12et :: [(Pitch,Double)]
tbl_12et =
let z = [(o,pc) | o <- [0..10], pc <- [0..11]]
in map octpc_to_pitch_cps z
-- | 24-tone equal temperament variant of 'tbl_12et'.
--
-- > length tbl_24et == 264
-- > min_max (map (round . snd) tbl_24et) == (16,32535)
tbl_24et :: [(Pitch, Double)]
tbl_24et =
let f x = let p = fmidi_to_pitch pc_spell_ks x
p' = pitch_rewrite_threequarter_alteration p
in (p',fmidi_to_cps x)
in map f [12,12.5 .. 143.5]
-- | Given an @ET@ table (or like) find bounds of frequency.
--
-- > let r = Just (at_pair octpc_to_pitch_cps ((3,11),(4,0)))
-- > in bounds_et_table tbl_12et 256 == r
bounds_et_table :: Ord s => [(t,s)] -> s -> Maybe ((t,s),(t,s))
bounds_et_table tbl =
let f (_,p) = compare p
in find_bounds f (adj2 1 tbl)
-- | 'bounds_et_table' of 'tbl_12et'.
--
-- > map bounds_12et_tone (hsn 17 55)
bounds_12et_tone :: Double -> Maybe ((Pitch,Double),(Pitch,Double))
bounds_12et_tone = bounds_et_table tbl_12et
-- | Tuple indicating nearest 'Pitch' to /frequency/ with @ET@
-- frequency, and deviation in hertz and 'Cents'.
type HS_R = (Double,Pitch,Double,Double,Cents)
-- | Form 'HS_R' for /frequency/ by consulting table.
--
-- > let {f = 256
-- > ;f' = octpc_to_cps (4,0)
-- > ;r = (f,Pitch C Natural 4,f',f-f',to_cents (f/f'))}
-- > in nearest_et_table_tone tbl_12et 256 == r
nearest_et_table_tone :: [(Pitch,Double)] -> Double -> HS_R
nearest_et_table_tone tbl f =
case bounds_et_table tbl f of
Nothing -> undefined
Just ((lp,lf),(rp,rf)) ->
let ld = f - lf
rd = f - rf
in if abs ld < abs rd
then (f,lp,lf,ld,to_cents (f/lf))
else (f,rp,rf,rd,to_cents (f/rf))
nearest_12et_tone :: Double -> HS_R
nearest_12et_tone = nearest_et_table_tone tbl_12et
nearest_24et_tone :: Double -> HS_R
nearest_24et_tone = nearest_et_table_tone tbl_24et
-- * Cell
-- | /n/-decimal places.
--
-- > ndp 3 (1/3) == "0.333"
ndp :: Int -> Double -> String
ndp = printf "%.*f"
-- | 'G.Table_Cell' from set of 'HS_R'.
hs_r_cell :: Int -> (Int -> String) -> [HS_R] -> (Int,Int) -> G.Table_Cell
hs_r_cell n nm_f t (i,j) =
let dp = ndp n
(f,p,pf,fd,c) = t !! i
e = case j of
0 -> nm_f i
1 -> dp f
2 -> pitch_pp p
3 -> dp pf
4 -> dp fd
5 -> dp c
_ -> undefined
in ([],[H.cdata e])