content: Post Processing Module to Link Grammar with OPTIC Functions
  Donya Quick and Paul Hudak
  Last modified: 19-Dec-2014
  For paper: Grammar-Based Automated Music Composition in Haskell
  
  Post processing module to turn Terms into music using Euterpea.
  
  > module Kulitta.PostProc where
  > import Kulitta.EuterpeaSpecial 
  > import Kulitta.PTGG 
  > import Kulitta.Grammars.MusicGrammars
  > import Kulitta.ChordSpaces
  > import Data.List
  > import System.Random
  
  Intermediate types:
  (NOTE: AbsPitch = PitchNum)
  
  > type Key = (AbsPitch, Mode)
  > type RChord = (Key, Dur, CType)
  > type TChord = (Key, Dur, AbsChord)
  > type TNote = (Key, Dur, AbsPitch)
  > type Voice = [TNote]
  
  Accessing the members of a TNote:
  
  > tnK (k,d,p) = k
  > tnD (k,d,p) = d
  > tnP (k,d,p) = p
  > newP (k,d,p) p' = (k,d,p')
  
  
  The goal using these intermediate types is the following:
  
  INPUT	     STEP            OUTPUT         FUNCTION
  Seeds -----(grammar)-------> Sentence       gen
  Sentence --(mode info)-----> [TChord]       toAbsChords
  [TChord] ------------------> [Voice] 		toVoices
  [Voice] -------------------> Music Pitch    vsToMusic or vsToMusicI
  
  
  > unTerm :: [Term a MP] -> [(Key, Dur, a)]
  > unTerm = map (\(a,mp) -> ((key mp, mode mp), dur mp, a)) . toPairs . expand []
  
  > toChords :: [Term CType MP] -> [RChord]
  > toChords = unTerm
  
  > toAbsChords :: [Term CType MP] -> [TChord]
  > toAbsChords ts = map toAbsChord $ toChords ts
  
  > toAbsChord :: RChord -> TChord
  > toAbsChord ((k,m),d,c) = ((k,m), d, t k $ toAs c m)
  
  We also provide an alternate version that doesn't use diminished chords.
  
  > toAbsChordNoDim :: RChord -> TChord
  > toAbsChordNoDim ((k,m),d,c) = ((k,m), d, t k $ toAsNoDim c m)
  
  > toAbsChordsNoDim :: [Term CType MP] -> [TChord]
  > toAbsChordsNoDim ts = map toAbsChordNoDim $ toChords ts
  
  Conversion of a single chord to a mode rooted at zero:
  
  > toAs :: CType -> Mode -> [AbsPitch]
  > toAs ct m = 
  >     let s = getScale m ++ map (+12) s -- fininite scale
  >         i = head $ findIndices (==ct) [I, II, III, IV, V, VI, VII] -- can be updated w/enum
  >     in  map (s !!) $ map (+i) [0,2,4]
  
  > toAsNoDim :: CType -> Mode -> [AbsPitch]
  > toAsNoDim ct m = 
  >     let s = getScale m ++ map (+12) s -- fininite scale
  >         i = head $ findIndices (==ct) [I, II, III, IV, V, VI, VII] -- can be updated w/enum
  >     in  map (s !!) $ fixDim $ map (+i) [0,2,4] where
  >         fixDim x = if optEq x [0,3,6] then t (head x) [0,3,7] else x
  
  
  
  Transposition using a key (to avoid C-major assignment only):
  
  > atTrans :: AbsPitch -> [(Key, Dur, AbsChord)] -> [(Key, Dur, AbsChord)]
  > atTrans a = map (\((k,m),d,c) -> (((k+a) `mod` 12,m), d, t a c)) 
  
  map (\((k,m),d,c) -> ((fixK k a m,m),d, t (a `mod` 12) c)) 
  
  The toCords functon does a similar thing, but returns a CType and 
  its key/mode context without performing the conversion to AbsChord.
  
  > ctTrans :: AbsPitch -> [(Key, Dur, CType)] -> [(Key, Dur, CType)]
  > ctTrans a = map (\((k,m),d,c) -> (((k+a) `mod` 12,m),d,c)) 
  
  map (\((k,m),d,c) -> ((fixK k a m,m),d,c)) 
  
  > fixK k a Major = (k + a) `mod` 12
  > fixK k a Minor = ((k + a) `mod` 12) + 12
  
  
  Conversion of intermediate type to Music Pitch:
  
  > tChordsToMusic :: [TChord] -> Music Pitch
  > tChordsToMusic = line . map f  where
  >     f ((k,m),d, as) = chord $ map (\a -> note d (pitch a)) as
  
  
  ============ SPLITTING VOICES APART ===========
  
  The code here places TChords into a form more suitable
  for additional musical processing. A Voice is a list of
  pitches with duration and key/mode context. 
  
  > toVoices :: [TChord] -> [Voice]
  > toVoices ts = 
  >     let (ks,ds,ps) = unzip3 ts
  >     in  if checkMatrix ps then map (\v -> zip3 ks ds v) $ Data.List.transpose $ ps 
  >         else error "(toVoices) chords must all have the same number of voices!" where
  >     checkMatrix [] = True
  >     checkMatrix (x:xs) = and $ map (==length x) $ map length xs
  
  This alternative version of the function turns the list of chords into a matrix 
  by filling in holes with pitch number -1 (which will be interpreted as a rest).
  Chords are padded on the right. So, the progression [[0,4], [0,4,7]] would become
  [[0,4,-1], [0,4,7]].
  
  > toVoices' :: [TChord] -> [Voice]
  > toVoices' ts = 
  >     let (ks,ds,ps) = unzip3 ts
  >     in  map (\v -> zip3 ks ds v) $ Data.List.transpose $ fillGaps ps where
  >     fillGaps [] = []
  >     fillGaps cs = 
  >         let maxLen = maximum $ map length cs
  >             f x = x ++ take (maxLen - length x) (repeat (-1))
  >         in  map f cs
  
  > toNotes :: Voice -> Music Pitch
  > toNotes = line . map (\(k,d,p) -> note' d p) where
  >     note' d p = if p<0 then rest d else note d (pitch p)
  
  > vsToMusic :: [Voice] -> Music Pitch
  > vsToMusic = 
  >     chord . map toNotes 
  
  > vsToMusicI :: [InstrumentName] -> [Voice] -> Music Pitch
  > vsToMusicI is = 
  >     chord . zipWith (\i m -> instrument i m) is . map toNotes
  
  
  
  
  
  
  
  