{-# OPTIONS -fno-implicit-prelude #-}
module Music.FilterSaw where

import PreludeBase
import NumericPrelude

import qualified Synthesizer                  as Syn
import qualified Synthesizer.Plain.Control    as Ctrl
import qualified Synthesizer.Plain.Oscillator as Osci
import Instruments
import Sox.File

import qualified Haskore.Basic.Pitch as Pitch
import qualified Haskore.Melody          as Melody
import           Haskore.Melody.Standard as StdMelody
import qualified Haskore.Music           as Music
import           Haskore.Music.Standard  as StdMusic
import           Haskore.Performance.Player  as Player
import qualified Haskore.Interface.Signal.Note  as Note
import qualified Haskore.Interface.Signal.Write as MusicSignal
import Haskore.Interface.Signal.Write(Time,Volume)

import System.Random
import Algebra.Additive as Additive(zero)
import qualified Algebra.Transcendental as Trans
import qualified Algebra.Module         as Module

import System.Exit(ExitCode)
import qualified Data.List as List

------------ The song description -------------


data MyNote = FilterSaw Double Pitch.T
   deriving (Show, Eq, Ord)

type Resonance = Time


pattern :: Music.T MyNote
pattern = makePattern patternList (Osci.staticSine 0 0.05)

makePattern :: [Music.Dur -> Resonance -> Melody.T Resonance] ->
   [Resonance] -> Music.T MyNote
makePattern mel reson = line
   (zipWith (\n r -> Music.mapNote
                        (\(Melody.Note r' p) -> (FilterSaw r' p))
                        (n qn r))
            mel reson)

patternList :: [Music.Dur -> Resonance -> Melody.T Resonance]
patternList = concatMap (List.take 16 . cycle) (
   [[a 0, b 0, c 1, e 1],
    [a 0, b 0, c 1, f 1],
    [a 0, d 1, e 1, f 1],
    [a 0, b 0, c 1, e 1]])


----------- Configuration of the player -----------


noteToSignal ::
   Time -> Volume -> Pitch.Relative -> MyNote -> Note.T Volume Volume
noteToSignal detune _ trans (FilterSaw reso p) =
   Note.Cons (\sampleRate ->
      filterSaw sampleRate ((2000::Time)  *  2 ** (1.5*reso))
         (detune * Note.pitchFromStd trans p))

-- Volume type arises from Haskore
songToSignalMono :: Time -> Volume -> Music.T MyNote -> [Volume]
songToSignalMono sampleRate dif song =
   MusicSignal.fromMusic
      sampleRate (noteToSignal dif)
      Player.fancyMap
      (MusicSignal.contextMetro 480 qn)
      song

songSignal :: Time -> [Volume]
songSignal sampleRate = songToSignalMono sampleRate 1 pattern

{- use allpass with different parameters for both stereo channels
   in order to 'spread' the frequency spectrum over the stereo panorama -}
allpassChannel ::
   (Module.C a v, Trans.C a) => a -> a -> [v] -> [v]
allpassChannel sampleRate sign x =
   let order = 10
   in  Syn.allpassCascade order
          (map ((\(Syn.AllpassParam k) -> Syn.AllpassParam (sign*k)) .
                (Syn.allpassFlangerParam order))
               (Ctrl.exponential2 (3*sampleRate) (2000/sampleRate))
               --(repeat (50/sampleRate))
           ) x

stereoSignal :: Time -> [(Volume, Volume)]
stereoSignal sampleRate =
   zip (allpassChannel sampleRate ( 1 :: Volume) (songSignal sampleRate))
       (allpassChannel sampleRate (-1 :: Volume) (songSignal sampleRate))

reverb :: Time -> [(Volume, Volume)] -> [(Volume, Volume)]
reverb sampleRate =
   Syn.echoMulti
      (List.take 8 (randomRs (round(0.10*sampleRate::Time),
                              round(0.50*sampleRate::Time))
                             (mkStdGen 12354)))
      (0.1::Volume)

reverbedSignal :: Time -> [(Volume, Volume)]
reverbedSignal sampleRate =
   reverb sampleRate
      (stereoSignal sampleRate ++
       List.replicate (round (2*sampleRate)) zero)

main :: IO ExitCode
main = Sox.File.renderStereo "FilterSaw" 22050 reverbedSignal
