-- | Harmonic series
module Music.Theory.Tuning.Hs where

import Data.List {- base -}
import Data.Ratio {- base -}
import qualified Safe {- safe -}

import qualified Music.Theory.Pitch as T {- hmt -}
import Music.Theory.Tuning {- hmt -}
import Music.Theory.Tuning.Type {- hmt -}


-- | Harmonic series to /n/th partial, with indicated octave.
--
-- > harmonic_series 17 2
harmonic_series :: Integer -> Maybe Rational -> Tuning
harmonic_series :: Integer -> Maybe Rational -> Tuning
harmonic_series Integer
n Maybe Rational
o = Either [Rational] [Cents]
-> Maybe (Either Rational Cents) -> Tuning
Tuning (forall a b. a -> Either a b
Left [Rational
1 .. Integer
nforall a. Integral a => a -> a -> Ratio a
%Integer
1]) (forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a b. a -> Either a b
Left Maybe Rational
o)

-- | Harmonic series on /n/.
harmonic_series_cps :: (Num t, Enum t) => t -> [t]
harmonic_series_cps :: forall t. (Num t, Enum t) => t -> [t]
harmonic_series_cps t
n = [t
n,t
n forall a. Num a => a -> a -> a
* t
2 ..]

-- | /n/ elements of 'harmonic_series_cps'.
--
-- > let r = [55,110,165,220,275,330,385,440,495,550,605,660,715,770,825,880,935]
-- > harmonic_series_cps_n 17 55 == r
harmonic_series_cps_n :: (Num a, Enum a) => Int -> a -> [a]
harmonic_series_cps_n :: forall a. (Num a, Enum a) => Int -> a -> [a]
harmonic_series_cps_n Int
n = forall a. Int -> [a] -> [a]
take Int
n forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall t. (Num t, Enum t) => t -> [t]
harmonic_series_cps

-- | Sub-harmonic series on /n/.
subharmonic_series_cps :: (Fractional t,Enum t) => t -> [t]
subharmonic_series_cps :: forall t. (Fractional t, Enum t) => t -> [t]
subharmonic_series_cps t
n = forall a b. (a -> b) -> [a] -> [b]
map ((forall a. Num a => a -> a -> a
* t
n) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Fractional a => a -> a
recip) [t
1..]

-- | /n/ elements of 'harmonic_series_cps'.
--
-- > let r = [1760,880,587,440,352,293,251,220,196,176,160,147,135,126,117,110,104]
-- > map round (subharmonic_series_cps_n 17 1760) == r
subharmonic_series_cps_n :: (Fractional t,Enum t) => Int -> t -> [t]
subharmonic_series_cps_n :: forall t. (Fractional t, Enum t) => Int -> t -> [t]
subharmonic_series_cps_n Int
n = forall a. Int -> [a] -> [a]
take Int
n forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall t. (Fractional t, Enum t) => t -> [t]
subharmonic_series_cps

-- | /n/th partial of /f1/, ie. one indexed.
--
-- > map (partial 55) [1,5,3] == [55,275,165]
partial :: (Num a, Enum a) => a -> Int -> a
partial :: forall a. (Num a, Enum a) => a -> Int -> a
partial a
f1 Int
k = forall t. (Num t, Enum t) => t -> [t]
harmonic_series_cps a
f1 forall a. Partial => [a] -> Int -> a
`Safe.at` (Int
k forall a. Num a => a -> a -> a
- Int
1)

-- | Derivative harmonic series, based on /k/th partial of /f1/.
--
-- > import Music.Theory.Pitch
--
-- > let r = [52,103,155,206,258,309,361,412,464,515,567,618,670,721,773]
-- > let d = harmonic_series_cps_derived 5 (T.octpc_to_cps (1,4))
-- > map round (take 15 d) == r
harmonic_series_cps_derived :: (RealFrac a, Floating a, Enum a) => Int -> a -> [a]
harmonic_series_cps_derived :: forall a. (RealFrac a, Floating a, Enum a) => Int -> a -> [a]
harmonic_series_cps_derived Int
k a
f1 =
    let f0 :: a
f0 = forall f. (Floating f, RealFrac f) => f -> f -> f
T.cps_in_octave_above a
f1 (forall a. (Num a, Enum a) => a -> Int -> a
partial a
f1 Int
k)
    in forall t. (Num t, Enum t) => t -> [t]
harmonic_series_cps a
f0

-- | Harmonic series to /n/th harmonic (folded, duplicated removed).
--
-- > harmonic_series_folded_r 17 == [1,17/16,9/8,5/4,11/8,3/2,13/8,7/4,15/8]
--
-- > let r = [0,105,204,386,551,702,841,969,1088]
-- > map (round . ratio_to_cents) (harmonic_series_folded_r 17) == r
harmonic_series_folded_r :: Integer -> [Rational]
harmonic_series_folded_r :: Integer -> [Rational]
harmonic_series_folded_r Integer
n = forall a. Eq a => [a] -> [a]
nub (forall a. Ord a => [a] -> [a]
sort (forall a b. (a -> b) -> [a] -> [b]
map forall n. (Ord n, Fractional n) => n -> n
fold_ratio_to_octave_err [Rational
1 .. Integer
nforall a. Integral a => a -> a -> Ratio a
%Integer
1]))

-- | 'ratio_to_cents' variant of 'harmonic_series_folded'.
harmonic_series_folded_c :: Integer -> [Cents]
harmonic_series_folded_c :: Integer -> [Cents]
harmonic_series_folded_c = forall a b. (a -> b) -> [a] -> [b]
map forall i. Integral i => Ratio i -> Cents
ratio_to_cents forall b c a. (b -> c) -> (a -> b) -> a -> c
. Integer -> [Rational]
harmonic_series_folded_r

harmonic_series_folded :: Integer -> Tuning
harmonic_series_folded :: Integer -> Tuning
harmonic_series_folded Integer
n = Either [Rational] [Cents]
-> Maybe (Either Rational Cents) -> Tuning
Tuning (forall a b. a -> Either a b
Left (Integer -> [Rational]
harmonic_series_folded_r Integer
n)) forall a. Maybe a
Nothing

-- | @12@-tone tuning of first @21@ elements of the harmonic series.
--
-- > tn_cents_i harmonic_series_folded_21 == [0,105,204,298,386,471,551,702,841,969,1088]
-- > tn_divisions harmonic_series_folded_21 == 11
harmonic_series_folded_21 :: Tuning
harmonic_series_folded_21 :: Tuning
harmonic_series_folded_21 = Integer -> Tuning
harmonic_series_folded Integer
21