{-# Language FlexibleContexts #-}

-- | A friendly family of effects. These functions are kindly provided by Iain McCurdy (designed in Csound).
module Csound.Air.Fx.FxBox(
    adele, pongy, tort, fowler, revsy, flan, phasy, crusher, chory, pany, oscPany, triPany, sqrPany, tremy, oscTremy, triTremy, sqrTremy, ringo, EnvelopeModSig,

    -- * Presets
    -- | For all presets we have 5 levels of strength. They are signified by numbers from 1 to 5. Also for some effects (delay and distortion)
    -- we have miscellaneous way to alter preset by suffix b (means bright) and m (means muffled). It alters the tone color of the effect.

    -- ** Analog Delay
    adele1, adele2, adele3, adele4, adele5,
    -- *** Bright
    adele1b, adele2b, adele3b, adele4b, adele5b,
    -- *** Muted
    adele1m, adele2m, adele3m, adele4m, adele5m,

    -- ** Ping Pong Delay
    pongy1, pongy2, pongy3, pongy4, pongy5,

    -- *** Bright
    pongy1b, pongy2b, pongy3b, pongy4b, pongy5b,

    -- *** Muted
    pongy1m, pongy2m, pongy3m, pongy4m, pongy5m,

    -- ** Distortion
    tort1, tort2, tort3, tort4, tort5,

    -- *** Bright
    tort1b, tort2b, tort3b, tort4b, tort5b,

    -- *** Muted
    tort1m, tort2m, tort3m, tort4m, tort5m,

    -- ** Envelope follower
    fowler',
    fowler1, fowler2, fowler3, fowler4, fowler5,

    -- ** Flanger
    flan',
    flan1, flan2, flan3, flan4, flan5,

    -- ** Phaser
    phasy',
    phasy1, phasy2, phasy3, phasy4, phasy5,

    -- ** Chorus
    chory',
    chory1, chory2, chory3, chory4, chory5,

    -- ** Auto Pan
    oscPany',
    oscPany1, oscPany2, oscPany3, oscPany4, oscPany5,

    triPany',
    triPany1, triPany2, triPany3, triPany4, triPany5,

    sqrPany',
    sqrPany1, sqrPany2, sqrPany3, sqrPany4, sqrPany5,

    -- ** Tremolo
    oscTremy',
    oscTremy1, oscTremy2, oscTremy3, oscTremy4, oscTremy5,

    triTremy',
    triTremy1, triTremy2, triTremy3, triTremy4, triTremy5,

    sqrTremy',
    sqrTremy1, sqrTremy2, sqrTremy3, sqrTremy4, sqrTremy5,

    -- ** Ring modulation
    ringo',
    ringo1, ringo2, ringo3, ringo4, ringo5,

    -- * Presets with UIs
    -- | If we use prefix @ui@ we can create an image of our effect that looks like guitar stompbox.
    --
    -- Let's take a distortion fr instance:
    --
    -- > type FxFun = Sig2 -> SE Sig2
    -- >
    -- > uiTort2 :: Source FxFun
    --
    -- We can combine the effects with functions:
    --
    -- > fxHor, fxVer :: [Source FxFun] -> Source FxFun
    -- >
    -- > fxMatrix :: Int -> [Source FxFun] -> Source FxFun
    -- > fxMatrix numberOfColumns fxs = ...
    --
    -- All these functions stack the effects in the list
    -- and align visuals. The visuals can be stacked horizontally, vertically
    -- or placed on a square grid.
    --
    -- Let's create a chain of effects and apply it to the input signal:
    --
    -- > > let pedals ain = lift1 (\f -> f ain) $ fxHor [uiFlan1, uiAdele2 0.25 0.5, uiHall 0.2, uiGain 0.4]
    -- >
    -- > > vdac $ pedals =<< (atMidi $ dryPatch vibraphone)
    --
    -- With @uiGain@ we can change the volume of the output.


    -- ** Reverb

    -- *** Rooms
    uiRoom, uiRoom1, uiRoom2, uiRoom3, uiRoom4, uiRoom5,

    -- ** Chambers
    uiChamber, uiChamber1, uiChamber2, uiChamber3, uiChamber4, uiChamber5,

    -- *** Halls
    uiHall, uiHall1, uiHall2, uiHall3, uiHall4, uiHall5,

    -- *** Caves
    uiCave, uiCave1, uiCave2, uiCave3, uiCave4, uiCave5,

    -- ** Mono Reverb

    -- *** Rooms
    uiMonoRoom, uiRoom1m, uiRoom2m, uiRoom3m, uiRoom4m, uiRoom5m,

    -- ** Chambers
    uiMonoChamber, uiChamber1m, uiChamber2m, uiChamber3m, uiChamber4m, uiChamber5m,

    -- *** Halls
    uiMonoHall, uiHall1m, uiHall2m, uiHall3m, uiHall4m, uiHall5m,

    -- *** Caves
    uiMonoCave, uiCave1m, uiCave2m, uiCave3m, uiCave4m, uiCave5m,

    -- ** Delay
    uiAdele1, uiAdele2, uiAdele3, uiAdele4, uiAdele5,
    uiAdele1b, uiAdele2b, uiAdele3b, uiAdele4b, uiAdele5b,
    uiAdele1m, uiAdele2m, uiAdele3m, uiAdele4m, uiAdele5m,

    -- ** Ping Pong Delay
    uiPongy1, uiPongy2, uiPongy3, uiPongy4, uiPongy5,
    uiPongy1b, uiPongy2b, uiPongy3b, uiPongy4b, uiPongy5b,
    uiPongy1m, uiPongy2m, uiPongy3m, uiPongy4m, uiPongy5m,

    -- ** Distortion
    uiTort1, uiTort2, uiTort3, uiTort4, uiTort5,
    uiTort1b, uiTort2b, uiTort3b, uiTort4b, uiTort5b,
    uiTort1m, uiTort2m, uiTort3m, uiTort4m, uiTort5m,

    -- ** Envelope follower
    uiFowler',
    uiFowler1, uiFowler2, uiFowler3, uiFowler4, uiFowler5,

    -- ** Flanger
    uiFlan', uiFlan1, uiFlan2, uiFlan3, uiFlan4, uiFlan5,

    -- ** Phaser
    uiPhasy', uiPhasy1, uiPhasy2, uiPhasy3, uiPhasy4, uiPhasy5,

    -- ** Chorus
    uiChory', uiChory1, uiChory2, uiChory3, uiChory4, uiChory5,

    -- ** Auto Pan
    uiOscPany', uiTriPany', uiSqrPany',
    uiOscPany1, uiOscPany2, uiOscPany3, uiOscPany4, uiOscPany5,
    uiTriPany1, uiTriPany2, uiTriPany3, uiTriPany4, uiTriPany5,
    uiSqrPany1, uiSqrPany2, uiSqrPany3, uiSqrPany4, uiSqrPany5,

    -- ** Tremolo
    uiOscTremy', uiTriTremy', uiSqrTremy',
    uiOscTremy1, uiOscTremy2, uiOscTremy3, uiOscTremy4, uiOscTremy5,
    uiTriTremy1, uiTriTremy2, uiTriTremy3, uiTriTremy4, uiTriTremy5,
    uiSqrTremy1, uiSqrTremy2, uiSqrTremy3, uiSqrTremy4, uiSqrTremy5,

    -- ** Reverse
    uiRevsy,

    -- ** LoFi
    uiCrusher,

    -- ** Ring modulation
    uiRingo', uiRingo1, uiRingo2, uiRingo3, uiRingo4, uiRingo5

    -- ** Compressor
    -- | TODO


) where

import Data.Default

import Csound.Typed
import Csound.Typed.Opcode(ampdb, scale, expcurve, compress)
import Csound.Typed.Gui

import Csound.SigSpace

import qualified Csound.Typed.Plugins as P(pitchShifterDelay,
    fxAnalogDelay, fxDistortion, fxEnvelopeFollower, fxFlanger, fxFreqShifter, fxLoFi,
    fxPanTrem, fxMonoTrem, fxPhaser, fxPitchShifter, fxReverse, fxRingModulator, fxChorus2)

import Csound.Air.Patch(Fx, Fx1, Fx2)
import Csound.Air.Fx(Balance, DelayTime, Feedback, ToneSig, SensitivitySig,
    BaseCps, Resonance, DepthSig, RateSig, TremWaveSig, FoldoverSig, BitsReductionSig,
    DriveSig, TimeSig, WidthSig,
    rever2, pingPong, pingPong', PingPongSpec(..))

import Csound.Air.Live(fxBox, fxColor)
import Csound.Air.Wav(toMono)
import Csound.Air.Misc(fromMono)

import qualified Data.Colour as C
import qualified Data.Colour.SRGB as C


-- | Analog Delay line with low-pass filter in the feedback chain.
-- The filter adds natural decay to the echoes.
--
-- > adele mixRatio delayTime feedback toneRatio ain
--
-- Note that the center frequency of the filter is measured in normalized units (form 0  to 1).
adele :: Sigs a => Balance -> DelayTime -> Feedback -> ToneSig -> a -> a
adele kmix kdelay kfback ktone = mapSig $ P.fxAnalogDelay kmix kdelay kfback ktone

size1, size2, size3, size4, size5 :: Fractional a => a

size1 = 0.1
size2 = 0.25
size3 = 0.5
size4 = 0.75
size5 = 0.95

-- | Distortion unit with low-pass filter.
--
-- > tort driveLevel toneRatio ain
--
-- Note that the center frequency of the filter is measured in normalized units (form 0  to 1).
tort :: Sigs a => DriveSig -> ToneSig -> a -> a
tort kdrive ktone = mapSig $ P.fxDistortion 1 kdrive ktone

-- | Envelope follower.
--
-- > fowler sensitivity baseFrequencyRatio resonance ain
--
-- Arguments:
--
-- * @sensitivity        @ --  sensitivity of the envelope follower (suggested range: 0 to 1)
--
-- * @baseFrequencyRatio @ --  base frequency of the filter before modulation by the input dynamics (range: 0 to 1)
--
-- * @resonance          @ --  resonance of the lowpass filter (suggested range: 0 to 1)
fowler :: Sigs a => SensitivitySig -> BaseCps -> Resonance -> a -> a
fowler ksens kbaseFreq kreson = mapSig $ P.fxEnvelopeFollower ksens kbaseFreq (0.99 * kreson)

-- | An effect that reverses an audio stream in chunks
--
-- > revsy time
--
-- @time@ -- the size of the chunck in seconds.
revsy :: Sigs a => TimeSig -> a -> a
revsy ktime = mapSig $ P.fxReverse ktime


-- | A flanger effect following the typical design of a so called 'stomp box'
--
-- >  flan rate depth delayTime feedback ain =
--
-- Arguments
--
-- * @rate      @ --  rate control of the lfo of the effect *NOT IN HERTZ* (range 0 to 1)
--
-- * @depth     @ --  depth of the lfo of the effect (range 0 to 1)
--
-- * @delayTime @ --  static delay offset of the flanging effect (range 0 to 1)
--
-- * @feedback  @ --  feedback and therefore intensity of the effect (range 0 to 1)
--
-- * @ain       @ --  input audio to which the flanging effect will be applied
flan :: Sigs a => RateSig -> DepthSig -> DelayTime -> Feedback -> a -> a
flan krate kdepth kdelay kfback = mapSig $ P.fxFlanger krate kdepth kdelay kfback


-- | Phaser
--
-- An phase shifting effect that mimics the design of a so called 'stomp box'
--
-- > phasy rate depth freq fback ain
--
-- Arguments:
--
-- * @rate  @ --  rate of lfo of the effect (range 0 to 1)
--
-- * @depth @ --  depth of lfo of the effect (range 0 to 1)
--
-- * @freq  @ --  centre frequency of the phase shifting effect in octaves (suggested range 0 to 1)
--
-- * @fback @ --  feedback and therefore intensity of the effect (range 0 to 1)
--
-- * @ain   @ --  input audio to be pitch shifted
phasy :: Sigs a => RateSig -> DepthSig -> BaseCps -> Feedback -> a -> a
phasy krate kdepth cps kfback = mapSig $ P.fxPhaser krate kdepth (6 + 5 * cps) kfback

-- | LoFi (Bit Crusher)
--
-- 'Low Fidelity' distorting effects of bit reduction and downsampling (foldover)
--
-- > crusher  bits fold ain = ...
--
-- Arguments
--
-- * @bits  @ --  bit depth reduction (range 0 to 1)
--
-- * @fold  @ --  amount of foldover (range 0 to 1)
--
-- * @ain   @ --  input audio to have low fidelity distortion effects applied
crusher :: Sigs a => BitsReductionSig -> FoldoverSig -> a -> a
crusher kbits kfold = mapSig $ P.fxLoFi (0.6 * kbits) kfold

-- | Stereo Chorus
--
-- A stereo chorus effect
--
-- > chory rate depth width (ainLeft, ainRight)
--
-- Arguments
--
-- * @rate  @ --  rate control of the lfo of the effect *NOT IN HERTZ* (range 0 to 1)
--
-- * @depth @ --  depth of the lfo of the effect (range 0 to 1)
--
-- * @width @ --  width of stereo widening (range 0 to 1)
--
-- * @ainX  @ --  input stereo signal
chory :: RateSig -> DepthSig -> WidthSig -> Sig2 -> Sig2
chory krate kdepth kwidth ain = P.fxChorus2 krate kdepth kwidth ain


-- | Auto pan
--
-- > pany wave rate depth ain
--
-- ; Arguments:
--
-- * @wave  @ --  waveform used by the lfo (0=sine 1=triangle 2=square)
--
-- * @rate  @ --  rate control of the lfo of the effect *NOT IN HERTZ* (range 0 to 1)
--
-- * @depth @ --  depth of the lfo of the effect (range 0 to 1)
--
-- * @ain   @ --  input stereo audio
pany :: TremWaveSig -> DepthSig -> RateSig -> Sig2 -> Sig2
pany tremWave kdepth krate = P.fxPanTrem kdepth krate 0 tremWave

-- | Sine auto pan
--
-- > oscPany = pany 0
oscPany ::DepthSig -> RateSig -> Sig2 -> Sig2
oscPany = pany 0

-- | Triangle auto pan
--
-- > triPany = pany 1
triPany ::DepthSig -> RateSig -> Sig2 -> Sig2
triPany = pany 1

-- | Square auto pan
--
-- > sqrPany = pany 2
sqrPany ::DepthSig -> RateSig -> Sig2 -> Sig2
sqrPany = pany 2


-- | Tremolo
--
-- tremolo effect
--
-- > tremy wave rate depth ain
--
-- ; Arguments:
--
-- * @wave  @ --  waveform used by the lfo (0=sine 1=triangle 2=square)
--
-- * @rate  @ --  rate control of the lfo of the effect *NOT IN HERTZ* (range 0 to 1)
--
-- * @depth @ --  depth of the lfo of the effect (range 0 to 1)
--
-- * @ain   @ --  input stereo audio
tremy :: Sigs a => TremWaveSig -> DepthSig -> RateSig -> a -> a
tremy tremWave kdepth krate = mapSig $ P.fxMonoTrem kdepth krate tremWave

-- | Sine tremolo
--
-- > oscTremy = tremy 0
oscTremy :: Sigs a => DepthSig -> RateSig -> a -> a
oscTremy = tremy 0

-- | Triangle tremolo
--
-- > triTremy = tremy 1
triTremy :: Sigs a => DepthSig -> RateSig -> a -> a
triTremy = tremy 1

-- | Square tremolo
--
-- > sqrTremy = tremy 2
sqrTremy :: Sigs a => DepthSig -> RateSig -> a -> a
sqrTremy = tremy 2

-- ringo

type EnvelopeModSig = Sig

-- | RingModulator
--
-- An ring modulating effect with an envelope follower
--
-- > ringo balance rate envelopeMod ain
--
-- * @balance     @  --  dry / wet mix of the output signal (range 0 to 1)
-- ; @rate        @  --  frequency of thew ring modulator *NOT IN HERTZ* (range 0 to 1)
-- ; @envelopeMod @  --  amount of dynamic envelope following modulation of frequency (range 0 to 1)
-- * @ain         @  --  input audio to be pitch shifted
ringo :: Sigs a => Balance -> RateSig -> EnvelopeModSig -> a -> a
ringo balance rate envelopeMod = mapSig $ \ain -> P.fxRingModulator ain balance rate envelopeMod

---------------------------------------------------------------------
-- Presets

-- Analog Delay

adeleBy :: Sigs a => ToneSig -> Feedback -> Balance -> DelayTime -> a -> a
adeleBy tone size balance delTime = adele balance delTime size tone

adeleBy_ :: Sigs a => Feedback -> Balance -> DelayTime -> a -> a
adeleBy_ = adeleBy 0.5

adele1, adele2, adele3, adele4, adele5 :: Sigs a => Balance -> DelayTime -> a -> a

adele1 = adeleBy_ size1
adele2 = adeleBy_ size2
adele3 = adeleBy_ size3
adele4 = adeleBy_ size4
adele5 = adeleBy_ size5

adeleByB :: Sigs a => Feedback -> Balance -> DelayTime -> a -> a
adeleByB = adeleBy 0.8

adele1b, adele2b, adele3b, adele4b, adele5b :: Sigs a => Balance -> DelayTime -> a -> a

adele1b = adeleByB size1
adele2b = adeleByB size2
adele3b = adeleByB size3
adele4b = adeleByB size4
adele5b = adeleByB size5

adeleByM :: Sigs a => Feedback -> Balance -> DelayTime -> a -> a
adeleByM = adeleBy 0.2

adele1m, adele2m, adele3m, adele4m, adele5m :: Sigs a => Balance -> DelayTime -> a -> a

adele1m = adeleByM size1
adele2m = adeleByM size2
adele3m = adeleByM size3
adele4m = adeleByM size4
adele5m = adeleByM size5

-- Ping Pong delay

-- | Ping-pong delay
--
-- > pongy kmix delayTime feedback tone ain
pongy ::  Sig2s a => Balance -> DelayTime -> Feedback -> ToneSig -> WidthSig -> a -> a
pongy balance delTime fbk tone width = mapSig2 (pingPong' (def { pingPongDamp = absTone, pingPongWidth = width }) delTime fbk balance)
    where absTone = scale (expcurve tone 4) 12000 100

pongyBy :: Sig2s a => ToneSig -> Feedback -> Balance -> DelayTime -> WidthSig -> a -> a
pongyBy tone size balance delTime width = pongy balance delTime size tone width

pongyBy_ :: Sig2s a => Feedback -> Balance -> DelayTime -> WidthSig -> a -> a
pongyBy_ = pongyBy 0.5

pongy1, pongy2, pongy3, pongy4, pongy5 :: Sig2s a => Balance -> DelayTime -> WidthSig -> a -> a

pongy1 = pongyBy_ size1
pongy2 = pongyBy_ size2
pongy3 = pongyBy_ size3
pongy4 = pongyBy_ size4
pongy5 = pongyBy_ size5

pongyByB :: Sig2s a => Feedback -> Balance -> DelayTime -> WidthSig -> a -> a
pongyByB = pongyBy 0.8

pongy1b, pongy2b, pongy3b, pongy4b, pongy5b :: Sig2s a => Balance -> DelayTime -> WidthSig -> a -> a

pongy1b = pongyByB size1
pongy2b = pongyByB size2
pongy3b = pongyByB size3
pongy4b = pongyByB size4
pongy5b = pongyByB size5

pongyByM :: Sig2s a => Feedback -> Balance -> DelayTime -> WidthSig -> a -> a
pongyByM = pongyBy 0.2

pongy1m, pongy2m, pongy3m, pongy4m, pongy5m :: Sig2s a => Balance -> DelayTime -> WidthSig -> a -> a

pongy1m = pongyByM size1
pongy2m = pongyByM size2
pongy3m = pongyByM size3
pongy4m = pongyByM size4
pongy5m = pongyByM size5

-- Distortion

tortBy :: Sigs a => ToneSig -> DriveSig -> a -> a
tortBy tone drive = tort drive tone

tortBy_ :: Sigs a => DriveSig -> a -> a
tortBy_ = tortBy 0.5

tort1, tort2, tort3, tort4, tort5 :: Sigs a => a -> a

tort1 = tortBy_ size1
tort2 = tortBy_ size2
tort3 = tortBy_ size3
tort4 = tortBy_ size4
tort5 = tortBy_ size5

tortByB :: Sigs a => DriveSig -> a -> a
tortByB = tortBy 0.85

tort1b, tort2b, tort3b, tort4b, tort5b :: Sigs a => a -> a

tort1b = tortByB size1
tort2b = tortByB size2
tort3b = tortByB size3
tort4b = tortByB size4
tort5b = tortByB size5

tortByM :: Sigs a => DriveSig -> a -> a
tortByM = tortBy 0.2

tort1m, tort2m, tort3m, tort4m, tort5m :: Sigs a => a -> a

tort1m = tortByM size1
tort2m = tortByM size2
tort3m = tortByM size3
tort4m = tortByM size4
tort5m = tortByM size5

-- Envelope follower

fowler' :: Sigs a => Sig -> a -> a
fowler' size = fowler size size size

fowler1, fowler2, fowler3, fowler4, fowler5 :: Sigs a => a -> a

fowler1 = fowler' size1
fowler2 = fowler' size2
fowler3 = fowler' size3
fowler4 = fowler' size4
fowler5 = fowler' size5

-- Flanger

flan' :: Sigs a => Sig -> a -> a
flan' size = flan size size size size

flan1, flan2, flan3, flan4, flan5 :: Sigs a => a -> a

flan1 = flan' size1
flan2 = flan' size2
flan3 = flan' size3
flan4 = flan' size4
flan5 = flan' size5

-- Phaser

-- phasy :: RateSig -> DepthSig -> BaseCps -> Feedback -> Sig -> Sig

phasy' :: Sigs a => Sig -> a -> a
phasy' size = phasy size size size size

phasy1, phasy2, phasy3, phasy4, phasy5 :: Sigs a => a -> a

phasy1 = phasy' size1
phasy2 = phasy' size2
phasy3 = phasy' size3
phasy4 = phasy' size4
phasy5 = phasy' size5

-- Chorus

chory' :: Sig2s a => Sig -> a -> a
chory' size = mapSig2 (chory size size size)

chory1, chory2, chory3, chory4, chory5 :: Sig2s a => a -> a

chory1 = chory' size1
chory2 = chory' size2
chory3 = chory' size3
chory4 = chory' size4
chory5 = chory' size5

-- Auto Pan

-- pany :: TremWaveSig -> DepthSig -> RateSig -> Sig2 -> Sig2

oscPany' :: Sig2s a => Sig -> a -> a
oscPany' size = mapSig2 (oscPany size size)

oscPany1, oscPany2, oscPany3, oscPany4, oscPany5 :: Sig2s a => a -> a

oscPany1 = oscPany' size1
oscPany2 = oscPany' size2
oscPany3 = oscPany' size3
oscPany4 = oscPany' size4
oscPany5 = oscPany' size5

triPany' :: Sig2s a => Sig -> a -> a
triPany' size = mapSig2 (triPany size size)

triPany1, triPany2, triPany3, triPany4, triPany5 :: Sig2s a => a -> a

triPany1 = triPany' size1
triPany2 = triPany' size2
triPany3 = triPany' size3
triPany4 = triPany' size4
triPany5 = triPany' size5

sqrPany' :: Sig2s a => Sig -> a -> a
sqrPany' size = mapSig2 (sqrPany size size)

sqrPany1, sqrPany2, sqrPany3, sqrPany4, sqrPany5 :: Sig2s a => a -> a

sqrPany1 = sqrPany' size1
sqrPany2 = sqrPany' size2
sqrPany3 = sqrPany' size3
sqrPany4 = sqrPany' size4
sqrPany5 = sqrPany' size5

-- Tremolo

oscTremy' :: Sigs a => Sig -> a -> a
oscTremy' size = oscTremy size size

oscTremy1, oscTremy2, oscTremy3, oscTremy4, oscTremy5 :: Sigs a => a -> a

oscTremy1 = oscTremy' size1
oscTremy2 = oscTremy' size2
oscTremy3 = oscTremy' size3
oscTremy4 = oscTremy' size4
oscTremy5 = oscTremy' size5

triTremy' :: Sigs a => Sig -> a -> a
triTremy' size = triTremy size size

triTremy1, triTremy2, triTremy3, triTremy4, triTremy5 :: Sigs a => a -> a

triTremy1 = triTremy' size1
triTremy2 = triTremy' size2
triTremy3 = triTremy' size3
triTremy4 = triTremy' size4
triTremy5 = triTremy' size5


sqrTremy' :: Sigs a => Sig -> a -> a
sqrTremy' size = sqrTremy size size

sqrTremy1, sqrTremy2, sqrTremy3, sqrTremy4, sqrTremy5 :: Sigs a => a -> a

sqrTremy1 = sqrTremy' size1
sqrTremy2 = sqrTremy' size2
sqrTremy3 = sqrTremy' size3
sqrTremy4 = sqrTremy' size4
sqrTremy5 = sqrTremy' size5

-- Ring modulation

ringo' :: Sigs a => Sig -> a -> a
ringo' size = ringo size size size

ringo1, ringo2, ringo3, ringo4, ringo5 :: Sigs a => a -> a

ringo1 = ringo' size1
ringo2 = ringo' size2
ringo3 = ringo' size3
ringo4 = ringo' size4
ringo5 = ringo' size5

----------------------------------------------------------
-- UI

setAll :: Double -> [String] -> [(String, Double)]
setAll size names = fmap (\s -> (s, size)) names


-- colors

tortColor = red
fowlerColor = maroon
adeleColor = blue
pongColor = orange
flanColor = purple
revsyColor = lime
phasyColor = yellow
crusherColor = fuchsia
choryColor = navy
panyColor = aqua
tremyColor = green
ringoColor = maroon
reverbColor = olive

paintTo = fxColor . C.sRGB24read

red = "#FF4136"
maroon = "#85144b"
blue = "#0074D9"
aqua = "#7FDBFF"
teal = "#39CCCC"
navy = "#001f3f"
orange = "#FF851B"
lime = "#01FF70"
green = "#2ECC40"
yellow = "#FFDC00"
purple = "#B10DC9"
fuchsia = "#F012BE"
olive = "#3D9970"

-- Analog Delay

uiAdeleBy :: Sigs a => Double -> Double -> Double -> Double -> Source (Fx a)
uiAdeleBy initTone initFeedback initBalance initDelayTime = mapSource bindSig $ paintTo adeleColor $ fxBox "Delay" fx True  [("balance", initBalance), ("del time", initDelayTime), ("fbk", initFeedback), ("tone", initTone)]
    where
        fx [balance, delayTime, feedback, tone] = return . adele balance delayTime feedback tone

uiAdeleBy_ :: Sigs a => Double -> Double -> Double -> Source (Fx a)
uiAdeleBy_ = uiAdeleBy 0.5

uiAdele1, uiAdele2, uiAdele3, uiAdele4, uiAdele5 :: Sigs a => Double -> Double -> Source (Fx a)

uiAdele1 = uiAdeleBy_ size1
uiAdele2 = uiAdeleBy_ size2
uiAdele3 = uiAdeleBy_ size3
uiAdele4 = uiAdeleBy_ size4
uiAdele5 = uiAdeleBy_ size5

uiAdeleByB :: Sigs a => Double -> Double -> Double -> Source (Fx a)
uiAdeleByB = uiAdeleBy 0.8

uiAdele1b, uiAdele2b, uiAdele3b, uiAdele4b, uiAdele5b :: Sigs a => Double -> Double -> Source (Fx a)

uiAdele1b = uiAdeleByB size1
uiAdele2b = uiAdeleByB size2
uiAdele3b = uiAdeleByB size3
uiAdele4b = uiAdeleByB size4
uiAdele5b = uiAdeleByB size5

uiAdeleByM :: Sigs a => Double -> Double -> Double -> Source (Fx a)
uiAdeleByM = uiAdeleBy 0.2

uiAdele1m, uiAdele2m, uiAdele3m, uiAdele4m, uiAdele5m :: Sigs a => Double -> Double -> Source (Fx a)

uiAdele1m = uiAdeleByM size1
uiAdele2m = uiAdeleByM size2
uiAdele3m = uiAdeleByM size3
uiAdele4m = uiAdeleByM size4
uiAdele5m = uiAdeleByM size5

-- Ping-pong delay

uiPongyBy :: Sigs a => Double -> Double -> Double -> Double -> Double -> Source (Fx a)
uiPongyBy initTone initWidth initFeedback initBalance initDelayTime = mapSource bindSig $ paintTo adeleColor $ fxBox "Ping-pong" fx True  [("balance", initBalance), ("del time", initDelayTime), ("fbk", initFeedback), ("tone", initTone), ("width", initWidth)]
    where
        fx [balance, delayTime, feedback, tone, width] = return . pongy balance delayTime feedback tone width

defWidth = 0.7

uiPongyBy_ :: Sigs a => Double -> Double -> Double -> Source (Fx a)
uiPongyBy_ = uiPongyBy 0.5 defWidth

uiPongy1, uiPongy2, uiPongy3, uiPongy4, uiPongy5 :: Sigs a => Double -> Double -> Source (Fx a)

uiPongy1 = uiPongyBy_ size1
uiPongy2 = uiPongyBy_ size2
uiPongy3 = uiPongyBy_ size3
uiPongy4 = uiPongyBy_ size4
uiPongy5 = uiPongyBy_ size5

uiPongyByB :: Sigs a => Double -> Double -> Double -> Source (Fx a)
uiPongyByB = uiPongyBy 0.8 defWidth

uiPongy1b, uiPongy2b, uiPongy3b, uiPongy4b, uiPongy5b :: Sigs a => Double -> Double -> Source (Fx a)

uiPongy1b = uiPongyByB size1
uiPongy2b = uiPongyByB size2
uiPongy3b = uiPongyByB size3
uiPongy4b = uiPongyByB size4
uiPongy5b = uiPongyByB size5

uiPongyByM :: Sigs a => Double -> Double -> Double -> Source (Fx a)
uiPongyByM = uiPongyBy 0.2 defWidth

uiPongy1m, uiPongy2m, uiPongy3m, uiPongy4m, uiPongy5m :: Sigs a => Double -> Double -> Source (Fx a)

uiPongy1m = uiPongyByM size1
uiPongy2m = uiPongyByM size2
uiPongy3m = uiPongyByM size3
uiPongy4m = uiPongyByM size4
uiPongy5m = uiPongyByM size5

-- Distortion

uiTortBy :: Sigs a => Double -> Double -> Source (Fx a)
uiTortBy initTone initDrive = mapSource bindSig $ paintTo tortColor $ fxBox "Distort" fx True [("drive", initDrive), ("tone", initTone)]
    where
        fx [drive, tone] = return . tort drive tone

uiTortBy_ :: Sigs a => Double -> Source (Fx a)
uiTortBy_ = uiTortBy 0.5

uiTort1, uiTort2, uiTort3, uiTort4, uiTort5 :: Sigs a => Source (Fx a)

uiTort1 = uiTortBy_ size1
uiTort2 = uiTortBy_ size2
uiTort3 = uiTortBy_ size3
uiTort4 = uiTortBy_ size4
uiTort5 = uiTortBy_ size5

uiTortByB :: Sigs a => Double -> Source (Fx a)
uiTortByB = uiTortBy 0.85

uiTort1b, uiTort2b, uiTort3b, uiTort4b, uiTort5b :: Sigs a => Source (Fx a)

uiTort1b = uiTortByB size1
uiTort2b = uiTortByB size2
uiTort3b = uiTortByB size3
uiTort4b = uiTortByB size4
uiTort5b = uiTortByB size5

uiTortByM :: Sigs a => Double -> Source (Fx a)
uiTortByM = uiTortBy 0.2

uiTort1m, uiTort2m, uiTort3m, uiTort4m, uiTort5m :: Sigs a => Source (Fx a)

uiTort1m = uiTortByM size1
uiTort2m = uiTortByM size2
uiTort3m = uiTortByM size3
uiTort4m = uiTortByM size4
uiTort5m = uiTortByM size5

-- Envelope follower

uiFowler' :: Sigs a => Source (Fx a)
uiFowler' = mapSource bindSig $ paintTo fowlerColor $ fxBox "Follower" fx True [("size", size1)]
    where
        fx [size] = return . fowler' size

uiFowlerBy :: Sigs a => Double -> Source (Fx a)
uiFowlerBy size = mapSource bindSig $ paintTo fowlerColor $ fxBox "Follower" fx True [("sense", size), ("freq", size), ("reson", size)]
    where
        fx [sense, freq, resonance] = return . fowler sense freq resonance

uiFowler1, uiFowler2, uiFowler3, uiFowler4, uiFowler5 :: Sigs a => Source (Fx a)

uiFowler1 = uiFowlerBy size1
uiFowler2 = uiFowlerBy size2
uiFowler3 = uiFowlerBy size3
uiFowler4 = uiFowlerBy size4
uiFowler5 = uiFowlerBy size5

-- Flanger

uiFlan' :: Sigs a => Source (Fx a)
uiFlan' = mapSource bindSig $ paintTo flanColor $ fxBox "Flanger" fx True [("size", size1)]
    where
        fx [size] = return . flan' size

uiFlanBy :: Sigs a => Double -> Source (Fx a)
uiFlanBy size = mapSource bindSig $ paintTo flanColor $ fxBox "Flanger" fx True $ setAll size ["rate", "depth", "del time", "fbk"]
    where
        fx [rate, depth, delayTime, fbk] = return . flan rate depth delayTime fbk

uiFlan1, uiFlan2, uiFlan3, uiFlan4, uiFlan5 :: Sigs a => Source (Fx a)

uiFlan1 = uiFlanBy size1
uiFlan2 = uiFlanBy size2
uiFlan3 = uiFlanBy size3
uiFlan4 = uiFlanBy size4
uiFlan5 = uiFlanBy size5

-- Phaser

-- phasy :: RateSig -> DepthSig -> BaseCps -> Feedback -> Sig -> Sig

uiPhasy' :: Sigs a => Source (Fx a)
uiPhasy' = mapSource bindSig $ paintTo phasyColor $ fxBox "Phaser" fx  True $ [("size", size1)]
    where
        fx [x] = return . phasy' x

uiPhasyBy :: Sigs a => Double -> Source (Fx a)
uiPhasyBy size = mapSource bindSig $ paintTo phasyColor $ fxBox "Phaser" fx True $ setAll size ["rate", "depth", "cps", "fbk"]
    where
        fx [rate, depth, cps, fbk] = return . phasy rate depth cps fbk

uiPhasy1, uiPhasy2, uiPhasy3, uiPhasy4, uiPhasy5 :: Sigs a => Source (Fx a)

uiPhasy1 = uiPhasyBy size1
uiPhasy2 = uiPhasyBy size2
uiPhasy3 = uiPhasyBy size3
uiPhasy4 = uiPhasyBy size4
uiPhasy5 = uiPhasyBy size5

-- Chorus

uiChory' :: Sig2s a => Source (Fx a)
uiChory' = paintTo choryColor $ fxBox "Chorus" fx True [("size", size1)]
    where
        fx [size] = return . chory' size

uiChoryBy :: Sig2s a => Double -> Source (Fx a)
uiChoryBy size = paintTo choryColor $ fxBox "Chorus" fx True $ setAll size ["rate", "depth", "width"]
    where
        fx [rate, depth, width] = return . mapSig2 (chory rate depth width)

uiChory1, uiChory2, uiChory3, uiChory4, uiChory5 :: Sig2s a => Source (Fx a)

uiChory1 = uiChoryBy size1
uiChory2 = uiChoryBy size2
uiChory3 = uiChoryBy size3
uiChory4 = uiChoryBy size4
uiChory5 = uiChoryBy size5

-- Auto Pan

-- pany :: TremWaveSig -> DepthSig -> RateSig -> Sig2 -> Sig2

genUiPany' :: (Sig -> Sig2 -> Sig2) -> Source Fx2
genUiPany' mkPany = paintTo panyColor $ fxBox "Pan" fx True [("size", size1)]
    where
        fx [size] = return . mkPany size

uiOscPany', uiTriPany', uiSqrPany' :: Source Fx2

uiOscPany' = genUiPany' oscPany'
uiTriPany' = genUiPany' triPany'
uiSqrPany' = genUiPany' sqrPany'

genUiPanyBy :: Sig -> Double -> Source Fx2
genUiPanyBy wave size = paintTo panyColor $ fxBox "Pan" fx True $ setAll size ["depth", "rate"]
    where
        fx [depth, rate] = return . pany wave depth rate

uiOscPanyBy, uiTriPanyBy, uiSqrPanyBy :: Double -> Source Fx2

uiOscPanyBy = genUiPanyBy 0
uiTriPanyBy = genUiPanyBy 1
uiSqrPanyBy = genUiPanyBy 2

uiOscPany1, uiOscPany2, uiOscPany3, uiOscPany4, uiOscPany5,
    uiTriPany1, uiTriPany2, uiTriPany3, uiTriPany4, uiTriPany5,
    uiSqrPany1, uiSqrPany2, uiSqrPany3, uiSqrPany4, uiSqrPany5 :: Source Fx2

uiOscPany1 = uiOscPanyBy size1
uiOscPany2 = uiOscPanyBy size2
uiOscPany3 = uiOscPanyBy size3
uiOscPany4 = uiOscPanyBy size4
uiOscPany5 = uiOscPanyBy size5

uiTriPany1 = uiTriPanyBy size1
uiTriPany2 = uiTriPanyBy size2
uiTriPany3 = uiTriPanyBy size3
uiTriPany4 = uiTriPanyBy size4
uiTriPany5 = uiTriPanyBy size5

uiSqrPany1 = uiSqrPanyBy size1
uiSqrPany2 = uiSqrPanyBy size2
uiSqrPany3 = uiSqrPanyBy size3
uiSqrPany4 = uiSqrPanyBy size4
uiSqrPany5 = uiSqrPanyBy size5

-- Tremolo

genUiTremy' :: Sigs a => (Sig -> Sig -> Sig) -> Source (Fx a)
genUiTremy' mkTremy = mapSource bindSig $ paintTo tremyColor $ fxBox "Tremolo" fx True [("size", size1)]
    where
        fx [size] = return . mkTremy size

uiOscTremy', uiTriTremy', uiSqrTremy' :: Sigs a => Source (Fx a)

uiOscTremy' = genUiTremy' oscTremy'
uiTriTremy' = genUiTremy' triTremy'
uiSqrTremy' = genUiTremy' sqrTremy'

genUiTremyBy :: Sigs a => Sig -> Double -> Source (Fx a)
genUiTremyBy wave size = mapSource bindSig $ paintTo tremyColor $ fxBox "Tremolo" fx True $ setAll size ["depth", "rate"]
    where
        fx [depth, rate] = return . tremy wave depth rate

uiOscTremyBy, uiTriTremyBy, uiSqrTremyBy :: Sigs a => Double -> Source (Fx a)

uiOscTremyBy = genUiTremyBy 0
uiTriTremyBy = genUiTremyBy 1
uiSqrTremyBy = genUiTremyBy 2

uiOscTremy1, uiOscTremy2, uiOscTremy3, uiOscTremy4, uiOscTremy5,
    uiTriTremy1, uiTriTremy2, uiTriTremy3, uiTriTremy4, uiTriTremy5,
    uiSqrTremy1, uiSqrTremy2, uiSqrTremy3, uiSqrTremy4, uiSqrTremy5 :: Sigs a => Source (Fx a)

uiOscTremy1 = uiOscTremyBy size1
uiOscTremy2 = uiOscTremyBy size2
uiOscTremy3 = uiOscTremyBy size3
uiOscTremy4 = uiOscTremyBy size4
uiOscTremy5 = uiOscTremyBy size5

uiTriTremy1 = uiTriTremyBy size1
uiTriTremy2 = uiTriTremyBy size2
uiTriTremy3 = uiTriTremyBy size3
uiTriTremy4 = uiTriTremyBy size4
uiTriTremy5 = uiTriTremyBy size5

uiSqrTremy1 = uiSqrTremyBy size1
uiSqrTremy2 = uiSqrTremyBy size2
uiSqrTremy3 = uiSqrTremyBy size3
uiSqrTremy4 = uiSqrTremyBy size4
uiSqrTremy5 = uiSqrTremyBy size5

-- Ring modulation

uiRingo' :: Sigs a => Source (Fx a)
uiRingo' = mapSource bindSig $ paintTo ringoColor $ fxBox "Ringo" fx True [("size", size1)]
    where
        fx [size] = return . ringo' size

uiRingoBy :: Sigs a => Double -> Source (Fx a)
uiRingoBy size = mapSource bindSig $ paintTo ringoColor $ fxBox "Ring Mod" fx True $ setAll size ["mix", "rate", "env mod"]
    where
        fx [balance, rate, envMod] = return . ringo balance rate envMod

uiRingo1, uiRingo2, uiRingo3, uiRingo4, uiRingo5 :: Sigs a => Source (Fx a)

uiRingo1 = uiRingoBy size1
uiRingo2 = uiRingoBy size2
uiRingo3 = uiRingoBy size3
uiRingo4 = uiRingoBy size4
uiRingo5 = uiRingoBy size5

-- Crusher

uiCrusher :: Sigs a => Double -> Double -> Source (Fx a)
uiCrusher initReduction initFoldover = mapSource bindSig $ paintTo revsyColor $ fxBox "LoFi" fx True [("redux", initReduction), ("foldover", initFoldover)]
    where
        fx [redux, foldover] = return . crusher redux foldover

-- Reverse

uiRevsy :: Sigs a => Double -> Source (Fx a)
uiRevsy initTime = mapSource bindSig $ paintTo revsyColor $ fxBox "Reverse" fx True [("time", initTime)]
    where
        fx [time] = return . revsy time

------------------------------------------------------------
-- Reverbs

uiRevBy :: Sig2s a => Double -> Double -> Source (Fx a)
uiRevBy initFeedback initMix = paintTo reverbColor $ fxBox "Reverb" fx True [("mix", initMix), ("fbk", initFeedback)]
    where
        fx [balance, feedback] = \asig -> return $ mapSig2 (\x -> mul (1 - balance) x + mul balance (rever2 feedback x)) asig

uiRoom :: Sig2s a => Double -> Source (Fx a)
uiRoom = uiRevBy 0.6

uiRoom1, uiRoom2, uiRoom3, uiRoom4, uiRoom5 :: Sig2s a => Source (Fx a)

uiRoom1 = uiRoom size1
uiRoom2 = uiRoom size2
uiRoom3 = uiRoom size3
uiRoom4 = uiRoom size4
uiRoom5 = uiRoom size5

uiChamber :: Sig2s a => Double -> Source (Fx a)
uiChamber = uiRevBy 0.8

uiChamber1, uiChamber2, uiChamber3, uiChamber4, uiChamber5 :: Sig2s a => Source (Fx a)

uiChamber1 = uiChamber size1
uiChamber2 = uiChamber size2
uiChamber3 = uiChamber size3
uiChamber4 = uiChamber size4
uiChamber5 = uiChamber size5

uiHall :: Sig2s a => Double -> Source (Fx a)
uiHall = uiRevBy 0.9

uiHall1, uiHall2, uiHall3, uiHall4, uiHall5 :: Sig2s a => Source (Fx a)

uiHall1 = uiHall size1
uiHall2 = uiHall size2
uiHall3 = uiHall size3
uiHall4 = uiHall size4
uiHall5 = uiHall size5

uiCave :: Sig2s a => Double -> Source (Fx a)
uiCave = uiRevBy 0.99

uiCave1, uiCave2, uiCave3, uiCave4, uiCave5 :: Sig2s a => Source (Fx a)

uiCave1 = uiCave size1
uiCave2 = uiCave size2
uiCave3 = uiCave size3
uiCave4 = uiCave size4
uiCave5 = uiCave size5

------------------------------------------------------------
-- Mono Reverbs

rever1 :: Sig -> Sig -> Sig
rever1 fbk = toMono . rever2 fbk . fromMono

uiMonoRevBy :: Double -> Double -> Source Fx1
uiMonoRevBy initFeedback initMix = paintTo reverbColor $ fxBox "Reverb" fx True [("mix", initMix), ("fbk", initFeedback)]
    where
        fx [balance, feedback] = \asig -> return $ mixAt balance (rever1 feedback) asig

uiMonoRoom :: Double -> Source Fx1
uiMonoRoom = uiMonoRevBy 0.6

uiRoom1m, uiRoom2m, uiRoom3m, uiRoom4m, uiRoom5m :: Source Fx1

uiRoom1m = uiMonoRoom size1
uiRoom2m = uiMonoRoom size2
uiRoom3m = uiMonoRoom size3
uiRoom4m = uiMonoRoom size4
uiRoom5m = uiMonoRoom size5

uiMonoChamber :: Double -> Source Fx1
uiMonoChamber = uiMonoRevBy 0.8

uiChamber1m, uiChamber2m, uiChamber3m, uiChamber4m, uiChamber5m :: Source Fx1

uiChamber1m = uiMonoChamber size1
uiChamber2m = uiMonoChamber size2
uiChamber3m = uiMonoChamber size3
uiChamber4m = uiMonoChamber size4
uiChamber5m = uiMonoChamber size5

uiMonoHall :: Double -> Source Fx1
uiMonoHall = uiMonoRevBy 0.9

uiHall1m, uiHall2m, uiHall3m, uiHall4m, uiHall5m :: Source Fx1

uiHall1m = uiMonoHall size1
uiHall2m = uiMonoHall size2
uiHall3m = uiMonoHall size3
uiHall4m = uiMonoHall size4
uiHall5m = uiMonoHall size5

uiMonoCave :: Double -> Source Fx1
uiMonoCave = uiMonoRevBy 0.99

uiCave1m, uiCave2m, uiCave3m, uiCave4m, uiCave5m :: Source Fx1

uiCave1m = uiMonoCave size1
uiCave2m = uiMonoCave size2
uiCave3m = uiMonoCave size3
uiCave4m = uiMonoCave size4
uiCave5m = uiMonoCave size5