module Synthesizer.Plain.ToneModulation where
import qualified Synthesizer.Basic.Phase as Phase
import qualified Synthesizer.Plain.Interpolation as Interpolation
import Data.Array (Array, (!), listArray, )
import qualified Algebra.RealField as RealField
import qualified Algebra.Field as Field
import qualified Algebra.Ring as Ring
import qualified Algebra.Additive as Additive
import qualified Number.NonNegative as NonNeg
import qualified Number.NonNegativeChunky as Chunky
import Synthesizer.Utility (viewListL, viewListR, clip, mapPair, )
import Control.Monad (guard)
import qualified Data.List as List
import NumericPrelude.List (replicateMatch, takeMatch, )
import NumericPrelude
import PreludeBase
interpolateCell ::
Interpolation.T a y ->
Interpolation.T b y ->
(a, b) ->
[[y]] -> y
interpolateCell ipLeap ipStep (qLeap,qStep) =
Interpolation.func ipStep qStep .
map (Interpolation.func ipLeap qLeap)
untangleShapePhase :: (Field.C a) =>
Int -> a -> (a, a) -> (a, a)
untangleShapePhase periodInt period (shape,phase) =
let leap = shape/period phase
step = shape leap * fromIntegral periodInt
in (leap, step)
untangleShapePhaseAnalytic :: (Field.C a) =>
Int -> a -> (a, a) -> (a, a)
untangleShapePhaseAnalytic periodInt period (shape,phase) =
let periodRound = fromIntegral periodInt
vLeap = (periodRound, periodRoundperiod)
vStep = (1,1)
in solveSLE2 (vLeap,vStep) (shape,period*phase)
solveSLE2 :: Field.C a => ((a,a), (a,a)) -> (a,a) -> (a,a)
solveSLE2 a@(a0,a1) b =
let det = det2 a
in (det2 (b, a1) / det,
det2 (a0, b) / det)
det2 :: Ring.C a => ((a,a), (a,a)) -> a
det2 ((a00,a10),(a01,a11)) =
a00*a11 a10*a01
flattenShapePhase :: RealField.C a =>
Int
-> a
-> (a, Phase.T a)
-> (Int, (a, a))
flattenShapePhase periodInt period (shape,phase) =
let (xShape,xWave) =
untangleShapePhase periodInt period (shape, Phase.toRepresentative phase)
(nLeap,qLeap) = splitFraction xShape
(nStep,qStep) = splitFraction xWave
n = nStep + nLeap * periodInt
in (n,(qLeap,qStep))
shapeLimits :: Ring.C t =>
Interpolation.T a v
-> Interpolation.T a v
-> Int
-> t
-> (t, t)
shapeLimits ipLeap ipStep periodInt len =
let minShape =
fromIntegral $
interpolationOffset ipLeap ipStep periodInt +
periodInt
maxShape =
minShape + len
fromIntegral
(Interpolation.number ipStep +
Interpolation.number ipLeap * periodInt)
in (minShape, maxShape)
interpolationOffset ::
Interpolation.T a v
-> Interpolation.T a v
-> Int
-> Int
interpolationOffset ipLeap ipStep periodInt =
Interpolation.offset ipStep +
Interpolation.offset ipLeap * periodInt
data Prototype a v =
Prototype {
protoIpLeap,
protoIpStep :: Interpolation.T a v,
protoIpOffset :: Int,
protoPeriod :: a,
protoPeriodInt :: Int,
protoShapeLimits :: (a,a),
protoArray :: Array Int v
}
makePrototype :: (RealField.C a) =>
Interpolation.T a v ->
Interpolation.T a v ->
a -> [v] -> Prototype a v
makePrototype ipLeap ipStep period tone =
let periodInt = round period
ipOffset =
interpolationOffset ipLeap ipStep periodInt
len = length tone
(lower,upper) =
shapeLimits ipLeap ipStep periodInt len
limits =
if lower > upper
then error "min>max"
else
(fromIntegral lower, fromIntegral upper)
arr = listArray (0, pred len) tone
in Prototype {
protoIpLeap = ipLeap,
protoIpStep = ipStep,
protoIpOffset = ipOffset,
protoPeriod = period,
protoPeriodInt = periodInt,
protoShapeLimits = limits,
protoArray = arr
}
sampledToneCell :: (RealField.C a) =>
Prototype a v -> a -> Phase.T a -> ((a,a),[[v]])
sampledToneCell p shape phase =
let (n, q) =
flattenShapePhase (protoPeriodInt p) (protoPeriod p)
(uncurry clip (protoShapeLimits p) shape, phase)
in (q,
map (map (protoArray p ! ) . iterate (protoPeriodInt p +)) $
enumFrom (n protoIpOffset p))
sampledToneAltCell :: (RealField.C a) =>
Prototype a v -> a -> Phase.T a -> ((a,a),[[v]])
sampledToneAltCell p shape phase =
let (n, q) =
flattenShapePhase (protoPeriodInt p) (protoPeriod p)
(uncurry clip (protoShapeLimits p) shape, phase)
in (q,
iterate (drop (protoPeriodInt p)) $
map (protoArray p ! ) (enumFrom (n protoIpOffset p)))
oscillatorCells :: (RealField.C t) =>
Interpolation.T t y ->
Interpolation.T t y ->
t -> [y] -> (t,[t]) -> (Phase.T t,[t]) -> [((t,t),[[y]])]
oscillatorCells
ipLeap ipStep period sampledTone shapes freqs =
let periodInt = round period
ptrs =
List.transpose $
takeWhile (not . null) $
iterate (drop periodInt) sampledTone
ipOffset =
interpolationOffset ipLeap ipStep periodInt
(skip:skips,coords) =
unzip $
oscillatorCoords periodInt period
(limitRelativeShapes ipLeap ipStep periodInt sampledTone shapes)
freqs
in zipWith
(\(k,q) (n,ptr) ->
if n>0
then error "ToneModulation.oscillatorCells: limit of shape parameter is buggy"
else
(q, drop (periodInt+k) ptr))
coords $
tail $
scanl
(\ (n,ptr0) d0 -> dropRem (n+d0) ptr0)
(0,ptrs)
((skip ipOffset periodInt) : skips)
dropFrac :: RealField.C i => i -> [a] -> (Int, i, [a])
dropFrac =
let recurse acc n xt =
if n>=1
then
case xt of
_:xs -> recurse (succ acc) (n1) xs
[] -> (acc, n, [])
else (acc,n,xt)
in recurse 0
dropFrac' :: RealField.C i => i -> [a] -> (Int, i, [a])
dropFrac' =
let recurse acc n xt =
maybe
(acc,n,xt)
(recurse (succ acc) (n1) . snd)
(guard (n>=1) >> viewListL xt)
in recurse 0
propDropFrac :: (RealField.C i, Eq a) => i -> [a] -> Bool
propDropFrac n xs =
dropFrac n xs == dropFrac' n xs
dropRem :: Int -> [a] -> (Int, [a])
dropRem =
let recurse n xt =
if n>0
then
case xt of
_:xs -> recurse (pred n) xs
[] -> (n, [])
else (n,xt)
in recurse
dropRem' :: Int -> [a] -> (Int, [a])
dropRem' =
let recurse n xt =
maybe
(n,xt)
(recurse (pred n) . snd)
(guard (n>0) >> viewListL xt)
in recurse
propDropRem :: (Eq a) => Int -> [a] -> Bool
propDropRem n xs =
dropRem n xs == dropRem' n xs
oscillatorCoords :: (RealField.C t) =>
Int -> t -> (t,[t]) -> (Phase.T t, [t]) -> [(Int,(Int,(t,t)))]
oscillatorCoords periodInt period
(shape0, shapes) (phase, freqs) =
let shapeOffsets =
scanl
(\(_,s) c -> splitFraction (s+c))
(splitFraction shape0) shapes
phases =
let (s:ss) = map (\(n,_) -> fromIntegral n / period) shapeOffsets
in freqToPhase
(Phase.increment (s) phase)
(zipWith () freqs ss)
in zipWith
(\(d,s) p -> (d, flattenShapePhase periodInt period (s,p)))
shapeOffsets
phases
freqToPhase :: RealField.C a => Phase.T a -> [a] -> [Phase.T a]
freqToPhase phase freq = scanl (flip Phase.increment) phase freq
limitRelativeShapes :: (RealField.C t) =>
Interpolation.T t y ->
Interpolation.T t y ->
Int -> [y] -> (t,[t]) -> (t,[t])
limitRelativeShapes ipLeap ipStep periodInt sampledTone =
let
len = Chunky.fromChunks (replicateMatch sampledTone one)
(minShape, maxShape) = shapeLimits ipLeap ipStep periodInt len
fromChunky = NonNeg.toNumber . Chunky.toNumber
toChunky = Chunky.fromNumber . NonNeg.fromNumber
in mapPair (fromChunky, map fromChunky) .
uncurry (limitMaxRelativeValuesNonNeg maxShape) .
mapPair (toChunky, map toChunky) .
uncurry (limitMinRelativeValues (fromChunky minShape))
limitMinRelativeValues :: (Additive.C a, Ord a) =>
a -> a -> [a] -> (a, [a])
limitMinRelativeValues xMin x0 xs =
let (ys,zs) =
span ((<zero).fst) (zip (scanl (+) (x0xMin) xs) (x0:xs))
in case ys of
[] -> (x0,xs)
(_:yr) -> (xMin, replicateMatch yr zero ++
case zs of
[] -> []
(z:zr) -> fst z : map snd zr)
limitMaxRelativeValues :: (Additive.C a, Ord a) =>
a -> a -> [a] -> (a, [a])
limitMaxRelativeValues xMax x0 xs =
let (ys,zs) =
span (>zero) (scanl () (xMaxx0) xs)
in maybe
(xMax, replicateMatch xs zero)
(\ ~(yl,yr) -> (x0, takeMatch yl xs ++ takeMatch zs (yr : repeat zero)))
(viewListR ys)
limitMaxRelativeValuesNonNeg :: (Additive.C a, Ord a) =>
a -> a -> [a] -> (a, [a])
limitMaxRelativeValuesNonNeg xMax x0 xs =
let (ys,zs) =
span fst (scanl (\(_,acc) d -> safeSub acc d) (safeSub xMax x0) xs)
in maybe
(xMax, replicateMatch xs zero)
(\ ~(yl, ~(_,yr)) -> (x0, takeMatch yl xs ++ takeMatch zs (yr : repeat zero)))
(viewListR ys)
safeSub :: (Additive.C a, Ord a) => a -> a -> (Bool, a)
safeSub a b = (a>=b, ab)