packages feed

kangaroo-0.1.0: demo/MidiText.hs

{-# OPTIONS -Wall #-}

--------------------------------------------------------------------------------
-- |
-- Module      :  MidiText
-- Copyright   :  (c) Stephen Tetley 2009
-- License     :  BSD3
--
-- Maintainer  :  Stephen Tetley <stephen.tetley@gmail.com>
-- Stability   :  volatile
-- Portability :  to be determined.
--
-- Pretty print a textual representaton of MIDI files. 
--
--------------------------------------------------------------------------------


module MidiText (
    -- * Print a readable text representation
    printMidi, ppMidiFile,
    
    ppHeader, ppMessage,
  
  ) where


import MidiDatatypes

import qualified Data.Foldable as F
import Numeric
import Text.PrettyPrint.HughesPJ


printMidi :: MidiFile -> IO ()
printMidi = putStrLn . render . ppMidiFile 




integral :: Integral a => a -> Doc
integral = integer . fromIntegral

infixl 6 `hyph`, `dblhyph`

hyph :: Doc -> Doc -> Doc
x `hyph` y         = x <> text " - " <> y

dblhyph :: Doc -> Doc -> Doc
x `dblhyph` y         = x <> text " -- " <> y

ppHex :: Integral a => a -> Doc
ppHex i = text $ showHex i []



ppMidiFile :: MidiFile -> Doc
ppMidiFile (MidiFile header tracks)  = ppHeader header $$ tdoc $$ empty
  where
    tdoc = fst $ F.foldl fn (empty,0) tracks
    fn (d,i) t = (d $$ prettyTrack t i, i+1)
      
ppHeader :: Header -> Doc
ppHeader (Header hformat ntrks td) = 
    text "[Header]" <+> ppHFormat hformat 
                    `hyph` (integral ntrks <+> text "tracks") 
                    `hyph` ppTimeDivision td
  
ppHFormat :: HFormat -> Doc
ppHFormat MF0  = text "Type 0"
ppHFormat MF1  = text "Type 1"
ppHFormat MF2  = text "Type 2"
 
ppTimeDivision :: TimeDivision -> Doc
ppTimeDivision (FPS i)   = text "fps"   <+> integral i
ppTimeDivision (TPB i)   = text "ticks" <+> integral i

prettyTrack :: Track -> Int -> Doc
prettyTrack (Track se) i = 
    brackets (text "Track" <+> int i) $$ (snd $ F.foldl fn (0,empty) se)
  where
    fn (gt,doc) msg@(dt,_)  = 
      (gt+dt, doc $$ (padshow 8 (gt+dt) `hyph` ppMessage msg))    


ppMessage :: Message -> Doc
ppMessage (dt,evt) = padshow 5 dt `hyph` ppEvent evt      


ppEvent :: Event -> Doc
ppEvent (VoiceEvent e)   = ppVoiceEvent e
ppEvent (SystemEvent e)  = text "sys"  `dblhyph` ppSystemEvent e
ppEvent (MetaEvent e)    = text "meta" `dblhyph` ppMetaEvent e

padshow :: Show a => Int -> a -> Doc
padshow pad a = let s = show a ; dif = pad - length s in
    text $ replicate dif ' ' ++ s 
                  


fli :: (Integral a, Show a) => a -> Doc
fli = padshow 3

ppVoiceEvent :: VoiceEvent -> Doc
ppVoiceEvent (NoteOff ch nt vel)        = 
    text "note-off" `hyph` fli ch `hyph` fli nt `hyph` fli vel
 
ppVoiceEvent (NoteOn ch nt vel)         =
    text "note-on " `hyph` fli ch `hyph` fli nt `hyph` fli vel
    
ppVoiceEvent (NoteAftertouch ch nt val) = 
    text "note-after-touch" `hyph` fli ch `hyph` fli nt `hyph` fli val
    
ppVoiceEvent (Controller ch ty val)     = 
    text "ctlr" `hyph` fli ch `hyph` fli ty `hyph` fli val
    
ppVoiceEvent (ProgramChange ch num)     = 
    text "pc" `hyph` fli ch `hyph` fli num
    
ppVoiceEvent (ChanAftertouch ch val)    = 
    text "chan-after-touch" `hyph` fli ch `hyph` fli val
      
ppVoiceEvent (PitchBend ch val)         = 
    text "pitch-bend" `hyph` fli ch `hyph` fli val

  

ppSystemEvent :: SystemEvent -> Doc
ppSystemEvent (SysEx _ _)    = text "sysex"   -- system exclusive event - length x data
ppSystemEvent (DataEvent i)  = text "data" <+> ppHex i

ppMetaEvent :: MetaEvent -> Doc
ppMetaEvent (TextEvent ty s)     = ppTextType ty `hyph` doubleQuotes (text s)
ppMetaEvent (SequenceNumber i)   = text "sequence-number" `hyph` integral i
ppMetaEvent (ChannelPrefix ch)   = text "channel-prefix"  `hyph` integral ch
ppMetaEvent (EndOfTrack)         = text "end-of-track"
ppMetaEvent (SetTempo mspqn)     = text "set-tempo" `hyph` integral mspqn  -- microseconds per quarter-note
ppMetaEvent (SMPTEOffset h m s f sf) = 
    text "smpte" `hyph` fli h `hyph` fli m `hyph` fli s `hyph` fli f `hyph` fli sf
    
ppMetaEvent (TimeSignature n d m ns) = 
    text "time-sig" `hyph` fli n `hyph` fli d `hyph` fli m `hyph` fli ns
    
ppMetaEvent (KeySignature i sc)  = 
    text "key-sig" `hyph` integral i `hyph` ppScaleType sc
       
ppMetaEvent (SSME i _)           = text "ssme" `hyph` ppHex i <+> text "..."

ppScaleType :: ScaleType -> Doc
ppScaleType MAJOR  = text "major"
ppScaleType MINOR  = text "minor"
  
ppTextType :: TextType -> Doc
ppTextType GENERIC_TEXT         = text "generic-text" 
ppTextType COPYRIGHT_NOTICE     = text "copyright-notice"  
ppTextType SEQUENCE_NAME        = text "sequence-name"
ppTextType INSTRUMENT_NAME      = text "instrument-name"
ppTextType LYRICS               = text "lyrics"
ppTextType MARKER               = text "marker"
ppTextType CUE_POINT            = text "cue-point"