\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.Write as WriteMidi > import qualified Haskore.Interface.MIDI.InstrumentMap as InstrMap > import qualified Sound.MIDI.General as GeneralMidi > import qualified Sound.MIDI.File.Save as SaveMidi > import qualified Sound.MIDI.File as MidiFile > import qualified Sound.MIDI.Message.Channel as ChannelMsg > 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 Numeric.NonNegative.Class as NonNeg > import qualified Numeric.NonNegative.Wrapper as NonNegW > import System.Process (rawSystem, ) > import System.Exit (ExitCode, ) \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 ExitCode > play cmd opts m = > do fileFromGeneralMIDIMusic fileName m > rawSystem cmd (opts ++ [fileName]) > > playWin95, playWinNT, > playLinux, playAlsa, playTimidity, playTimidityJack :: MidiMusic.T -> IO ExitCode > 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 ExitCode > playTest = > rawSystem "mplay32" [fileName] \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 ChannelMsg.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} }