YampaSynth-0.0.1: src/SynthBasics.hs
{-# LANGUAGE Arrows #-}
module SynthBasics (
oscSine
, oscSine'
, amp
, envGen
, oscSampleBased
, mixer
, moogVCF
, CV
, Frequency
, Resonance
) where
import FRP.Yampa
import Data.Audio hiding (sampleRate)
import Codec.Midi hiding (Time)
import SynthParams
type CV = Double
type Frequency = Double
type Resonance = Double -- 0 - 1
oscSine :: Frequency -> SF CV Sample
oscSine f0 = proc cv -> do
let f = f0 * (2 ** cv)
phi <- integral -< 2 * pi * f
returnA -< sin phi
oscSine' :: Frequency -> SF a Sample
oscSine' f0 = time >>> arr (sin . (2 * pi * f0 *))
amp :: CV -> Velocity -> SF (Sample, CV) Sample
amp volumeLevel vel = proc (smpl,cv) -> do
returnA -< (fromIntegral vel / 127) * smpl * cv * volumeLevel
envGen :: CV -> [(Time,CV)] -> Maybe Int
-> SF (Event ()) (CV, Event ())
envGen l0 tls Nothing = envGenAux l0 tls &&& after (sum (map fst tls)) ()
envGen l0 tls (Just n) =
switch (proc release -> do
l <- envGenAux l0 tls1 -< ()
returnA -< ((l, noEvent), release `tag` l))
(\l -> envGenAux l tls2 &&& after (sum (map fst tls2)) ())
where
(tls1, tls2) = splitAt n tls
envGenAux :: CV -> [(Time,CV)] -> SF a CV
envGenAux l0 tls = afterEach trs >>> hold r0 >>> integral >>^ (+ l0)
where
(r0, trs) = toRates l0 tls
toRates :: CV -> [(Time, CV)] -> (Double, [(Time, Double)])
toRates _ [] = (0, [])
toRates l0 ((t,l):tls) = (((l - l0) / t), trAux t l tls)
trAux :: Time -> CV -> [(Time, CV)] -> [(Time, Double)]
trAux t _ [] = [(t, 0)]
trAux t l ((t',l'):tls) = (t, (l' - l) / t') : trAux t' l' tls
moogVCF :: Int -> Frequency -> Resonance -> SF (Sample, CV) Sample
moogVCF sr f0 r = proc (x, cv) -> do
let f = f0 * (2 ** cv)
g = 1 - exp (-2 * pi * f / fromIntegral sr)
rec ya <- moogAux -< (x - 4 * r * y, g)
yb <- moogAux -< (ya, g)
yc <- moogAux -< (yb, g)
yd <- moogAux -< (yc, g)
-- 1/2-sample delay for phase compensation
ye <- iPre 0 -< yd
y <- iPre 0 -< (ye + yd) / 2
returnA -< y
where
vt = 40000 -- 2 * Vt
moogAux = proc (x,g) -> do
rec let y = ym1 + vt * g * (tanh (x / vt) - tanh (ym1 / vt))
ym1 <- iPre 0 -< y
returnA -< y
oscSampleBasedAux :: SynthParams -> SF CV (Sample, Int, Int)
oscSampleBasedAux params = proc cv -> do
phi <- integral -< (freq params) / (rootFreq params) * (2 ** cv)
let (n,f) = properFraction (phi * (fromIntegral $ sampleRate params))
p1 = posAux n
s1 = g p1
p2 = posAux (n + 1)
s2 = g p2
returnA -< (s1 + f * (s2 - s1), p2, n)
where
g = sampleGen params
s = start params
m = startLoop params
e = endLoop params
posAux n = if (s + n < m)
then s + n
else m + mod (s + n - m) (e - m)
oscSampleBased :: SynthParams -> SF (CV, Event ()) Sample
oscSampleBased params | sampleMode params == NoLoop =
switch (
proc (cv,_) -> do
(smpl,_,n) <- oscSampleBasedAux params -< cv
e <- iEdge False -< start params + n >= end params
returnA -< (smpl,e)
) (\_ -> constant 0.0)
oscSampleBased params | sampleMode params == ContLoop =
proc (cv,_) -> do
(smpl,_,_) <- oscSampleBasedAux params -< cv
returnA -< smpl
oscSampleBased params {- | sampleMode params == Audio.PressLoop -} =
proc (cv,e) -> do
smpl <- switch auxSF1 auxSF2 -< (cv,e)
returnA -< smpl
where
auxSF1 = proc (cv,e) -> do
(smpl,p,_) <- oscSampleBasedAux params -< cv
returnA -< (smpl, tag e p )
auxSF2 s = oscSampleBased (params {start = s, sampleMode = NoLoop})
mixer :: SF [Sample] Sample
mixer = arr sum