content: > {-# LANGUAGE Arrows #-}
  
  > module HSoM.Examples.MUIExamples2 where
  > import HSoM
  > import FRP.UISF
  > import Euterpea
  > import Data.Maybe (mapMaybe)
  
  
  =============
  Chord builder
  
  Here is a simple program that plays the selected chord when a root
  note is entered using a Midi input device.
  
  We define a mapping between chord extensions and their intervals with
  respect to the root note.
  
  > chordIntervals = [("Maj", [4,3,5]),
  >                   ("Maj7", [4,3,4,1]),
  >                   ("Maj9", [2,2,3,4,1]),
  >                   ("6", [4,3,2,3]),
  >                   ("m", [3,4,5]),
  >                   ("m7", [3,4,3,2]),
  >                   ("m9", [2,1,4,3,2]),
  >                   ("m7b5", [3,3,4,2]),
  >                   ("mMaj7", [3,4,4,1]),
  >                   ("dim", [3,3,3]),
  >                   ("7", [4,3,3,2]),
  >                   ("9", [2,2,3,3,2]),
  >                   ("7b9", [1,3,3,3,2])]
  
  We display the list of extensions on the screen as radio buttons for
  the user to click on.
  
  The toChord function takes in the index of the selected chord extension 
  and an input message as the root note, and outputs the notes of
  the selected chord based on the root note.  For simplicity, we only
  process the head of the message list and ignore everything else.
  
  > toChord :: Int -> [MidiMessage] -> [MidiMessage]
  > toChord i (ms@(m:_)) = 
  >   case m of 
  >     Std (NoteOn c k v) -> f NoteOn c k v
  >     Std (NoteOff c k v) -> f NoteOff c k v
  >     _ -> ms
  >   where f g c k v = map (\k -> Std (g c k v)) 
  >                         (scanl (+) k (snd (chordIntervals !! i)))
  
  The UI is arranged in the following way.  On the left side, the list
  of input and output devices are displayed top-down. On the right is
  the list of chord extensions.  We take the name of each extension from
  the chordIntervals list to create the radio buttons.  
  
  When a Midi input event occurs, the input message and the currently
  selected index to the list of chords is sent to the toChord function,
  and the resulting chord is sent to the output device.
  
  > buildChord = runMUI (defaultMUIParams {uiSize=(500,500), uiTitle="Chord Builder"}) $ leftRight $ proc _ -> do
  >   (mi,mo) <- topDown (selectInput &&& selectOutput) -< ()
  >   m <- midiIn -< mi
  >   i <- topDown $ title "Extension" $ radio (fst (unzip chordIntervals)) 0 -< ()
  >   midiOut -< (mo, fmap (toChord i) m)
  
  
  =================
  Bifurcate example
  
  Here is an example with some ideas borrowed from Gary Lee Nelson's
  composition "Bifurcate me, Baby!"
  
  The basic idea is to evaluate the logistic growth function at
  different points and convert the value to a musical note.  The growth
  function is given by
  
    x_(n+1) = r x_n (1 - x_n)
  
  We start with an initial population x_0 and iteratively apply the
  growth function to it, where r is the growth rate.  For certain values
  of r, the population stablizes to a certain value, but as r increases,
  the period doubles, quadruples, and eventually leads to chaos.  It is
  one of the classic examples in chaos theory.
  
  First we define the growth function which, given a rate r and
  current population x, generates the next population.
  
  > grow :: Double -> Double -> Double
  > grow r x = r * x * (1-x)
  
  Then we define a signal 'tick' that pulsates at a given frequency
  specified by slider f.  This is the signal that will drive the
  simulation.  The timer function takes in a frequency.
  
  The next thing we need is a time-varying population.  This is where 
  the delay function and the rec keyword come in handy.  We initialize 
  the 'pop' signal with the value 0.1, and then on every tick, we 
  grow it with the instantaneous value of the growth rate signal.
  
  We can now write a simple function that maps a population value to a
  musical note:
  
  > popToNote :: Double -> [MidiMessage]
  > popToNote x = [ANote 0 n 64 0.05] where n = truncate (x * 127)
  
  Finally, to play the note, we simply send the current population to 
  popToNote, and send the result to the selected Midi output device.  
  
  > bifurcate = runMUI (defaultMUIParams {uiSize=(300,500), uiTitle="Bifurcate!"}) $ proc _ -> do
  >   mo <- selectOutput -< ()
  >   f  <- title "Frequency" $ withDisplay (hSlider (1, 10) 1) -< ()
  >   r  <- title "Growth rate" $ withDisplay (hSlider (2.4, 4.0) 2.4) -< ()
  >   
  >   tick <- timer -< 1.0 / f
  >   rec pop <- delay 0.1 -<  maybe pop (const $ grow r pop) tick
  >       
  >   _ <- title "Population" $ display -< pop
  >   midiOut -< (mo, fmap (const (popToNote pop)) tick)
  
  
  ============
  Echo example
  
  Here we present a program that takes in a Midi event stream and, in
  addition to playing each note received from the input device, it also
  echoes the note at a given rate, while playing each successive note
  more softly until the velocity reduces to 0.
  
  The key component we need for this problem is a delay function that
  can delay a given event signal for a certain amount of time.  vdelay
  takes in the amount of time to delay and an input signal
  and outputs the delayed signal.
  
  There are two signals we want to attenuate.  One is the signal coming
  from the input device, and the other is the delayed and decayed signal
  containing the echoes.  In the code shown below, they are denoted as m
  and s, respectively.  We merge the two event streams into one and then 
  remove events with empty Midi messages by replacing them with Nothing.  
  The resulting signal, m', is then sent to the Midi output device.
  
  The echo signal s is created recursively from m' as follows.  We examine 
  the signal m' and decay any events that we find there, using the decay 
  rate indicated by the instantaneous value from the slider r.  This 
  decayed signal is fed into the vdelay signal function along with 
  the amount of time to delay (the inverse of the echo frequency, 
  which is given by the other slider f).
  
  > echo = runMUI (defaultMUIParams {uiSize=(500,500), uiTitle="Echo"}) $ proc _ -> do
  >   mi <- selectInput  -< ()
  >   mo <- selectOutput -< ()
  >   m <- midiIn -< mi
  >   r <- title "Decay rate" $ withDisplay (hSlider (0, 0.9) 0.5) -< ()
  >   f <- title "Echoing frequency" $ withDisplay (hSlider (1, 10) 10) -< ()
  >   
  >   rec let m' = removeNull $ mergeS m s
  >       s <- vdelay -< (1.0 / f, fmap (mapMaybe (decay 0.1 r)) m')
  >   
  >   midiOut -< (mo, m')
  
  > mergeS :: Maybe [MidiMessage] -> Maybe [MidiMessage] -> Maybe [MidiMessage]
  > mergeS (Just ns1) (Just ns2) = Just (ns1 ++ ns2)
  > mergeS n1         Nothing    = n1
  > mergeS Nothing    n2         = n2
  
  > removeNull :: Maybe [MidiMessage] -> Maybe [MidiMessage]
  > removeNull Nothing   = Nothing
  > removeNull (Just []) = Nothing
  > removeNull (Just xs) = Just xs
  
  > decay :: Time -> Double -> MidiMessage -> Maybe MidiMessage
  > decay dur r m = 
  >   let f c k v d = if v > 0 
  >                   then Just (ANote c k (truncate (fromIntegral v * r)) d)
  >                   else Nothing
  >   in case m of
  >     ANote c k v d -> f c k v d
  >     Std (NoteOn c k v) -> f c k v dur
  >     _ -> Nothing
  
  