\subsection{Convenient Functions for Getting Started With Haskore and MIDI}
\seclabel{test-functions}

{\small
\begin{haskelllisting}

> module Haskore.Interface.MIDI.Render where

> import qualified Haskore.Interface.MIDI.InstrumentMap as InstrMap
> import qualified Haskore.Interface.MIDI.Write         as WriteMidi
> import qualified Sound.MIDI.General       as GeneralMidi
>
> import qualified Haskore.Music.GeneralMIDI as MidiMusic
> import qualified Haskore.Music.Rhythmic    as RhyMusic
> import qualified Haskore.Music             as Music
> import qualified Haskore.Melody            as Melody
> import qualified Haskore.Performance.Context as Context
> import qualified Haskore.Performance.Fancy   as FancyPerformance

> import qualified Sound.MIDI.File.Save     as SaveMidi
> import qualified Sound.MIDI.File          as MidiFile
> import qualified Sound.MIDI.Event         as MidiEvent

> import qualified Numeric.NonNegative.Class   as NonNeg
> import qualified Numeric.NonNegative.Wrapper as NonNegW

> import System.Cmd (rawSystem)

\end{haskelllisting}
}

Given a \code{Player.Map}, \code{Context.T}, \code{InstrMap.T},
and file name, we can write a \code{MidiMusic.T} value into a midi file:

{\small
\begin{haskelllisting}

> fileFromRhythmicMusic ::
>    (Ord instr, Ord drum, NonNeg.C time, RealFrac time, RealFrac dyn) =>
>    FilePath ->
>       (InstrMap.ChannelProgramPitchTable drum,
>        InstrMap.ChannelProgramTable instr,
>        Context.T time dyn (RhyMusic.Note drum instr),
>        RhyMusic.T drum instr) -> IO ()
> fileFromRhythmicMusic fn m =
>    SaveMidi.toFile fn (WriteMidi.fromRhythmicMusic m)

\end{haskelllisting} }

\subsubsection{Test routines}

Using the defaults above, from a \code{MidiMusic.T} object, we can:

\begin{enumerate}

\item generate a \code{Performance.T}
using \code{Haskore.Performance.Default.fancyFromMusic}

\item generate a \code{MidiFile.T} data structure

{\small
\begin{haskelllisting}

> midi :: MidiMusic.T -> MidiFile.T
> midi =
>    WriteMidi.fromRhythmicPerformance [] InstrMap.defltGM .
>    FancyPerformance.floatFromMusic

> generalMidi :: MidiMusic.T -> MidiFile.T
> generalMidi =
>    WriteMidi.fromGMPerformanceAuto .
>    FancyPerformance.floatFromMusic

> generalMidiDeflt :: MidiMusic.T -> MidiFile.T
> generalMidiDeflt =
>    WriteMidi.fromGMPerformance (InstrMap.lookup InstrMap.defltCMap) .
>    FancyPerformance.floatFromMusic

> mixedMidi :: MidiMusic.T -> MidiFile.T
> mixedMidi =
>    WriteMidi.fromRhythmicPerformanceMixed [] InstrMap.defltGM .
>    FancyPerformance.floatFromMusic

> mixedGeneralMidi :: MidiMusic.T -> MidiFile.T
> mixedGeneralMidi =
>    WriteMidi.fromGMPerformanceMixedAuto .
>    FancyPerformance.floatFromMusic

\end{haskelllisting} }

\item generate a MIDI file

{\small
\begin{haskelllisting}

> fileFromGeneralMIDIMusic :: FilePath -> MidiMusic.T -> IO ()
> fileFromGeneralMIDIMusic filename = SaveMidi.toFile filename . generalMidi

\end{haskelllisting} }

\item generate and play a MIDI file on Windows 95, Windows NT, or Linux

{\small
\begin{haskelllisting}

> fileName :: FilePath
> fileName = "test.mid"

> play :: String -> [String] -> MidiMusic.T -> IO ()
> play cmd opts m =
>    do fileFromGeneralMIDIMusic fileName m
>       rawSystem cmd (opts ++ [fileName])
>       return ()
>
> playWin95, playWinNT,
>    playLinux, playAlsa, playTimidity, playTimidityJack :: MidiMusic.T -> IO ()
> playWin95        = play "mplayer" []
> playWinNT        = play "mplay32" []
> playLinux        = play "playmidi" ["-rf"]
> playAlsa         = play "pmidi" ["-p 128:0"]
> playTimidity     = play "timidity" ["-B8,9"]
> playTimidityJack = play "timidity" ["-Oj"]

\end{haskelllisting} }

\end{enumerate}

Alternatively, just run \code{fileFromGeneralMIDIMusic "test.mid" m} manually,
and then invoke the midi player
on your system using \code{playTest}, defined below for NT:

{\small
\begin{haskelllisting}

> playTest :: IO ()
> playTest =
>    do rawSystem "mplay32" [fileName]
>       return ()

\end{haskelllisting} }

\subsubsection{Some General Midi test functions}

Use these functions with caution.

A General Midi user patch map; i.e. one that maps GM instrument names
to themselves, using a channel that is the patch number modulo 16.
This is for use ONLY in the code that follows, o/w channel duplication
is possible, which will screw things up in general.

{\small
\begin{haskelllisting}

> gmUpm :: InstrMap.ChannelProgramTable MidiMusic.Instr
> gmUpm =
>    zipWith
>       (\instr chan ->
>          (instr, (chan, GeneralMidi.instrumentToProgram instr)))
>       GeneralMidi.instruments
>       (cycle $ map MidiEvent.toChannel [0..15])

\end{haskelllisting} }

Something to play each "instrument group" of 8 GM instruments;
this function will play a C major arpeggio on each instrument.

{\small
\begin{haskelllisting}

> gmTest :: Int -> IO ()
> gmTest i =
>    let gMM = take 8 (drop (i*8) GeneralMidi.instruments)
>        mu  = Music.line (map simple gMM)
>        simple instr = MidiMusic.fromMelodyNullAttr instr Melody.cMajArp
>    in  fileFromRhythmicMusic fileName
>           ([], gmUpm, FancyPerformance.context ::
>                    Context.T NonNegW.Float Float MidiMusic.Note, mu)

\end{haskelllisting} }
