MidiFile outputMidiFile :: String -> MidiFile -> IO () “MidiFile” and “outputMidiFile” are imported from Haskore. Our main job is to define “performToMidi”."> MidiFile outputMidiFile :: String -> MidiFile -> IO () “MidiFile” and “outputMidiFile” are imported from Haskore. Our main job is to define “performToMidi”.">
Download presentation
Presentation is loading. Please wait.
1
Chapter 22 From Performance to MIDI
2
Motivation Abstractly, an MDL program denotes a Performance. But a Performance is just a Haskell data structure – we can analyze it, but we cannot hear it! To remedy this, we will convert a Performance into a MIDI File. MIDI (Musical Instrument Digital Interface) is a standard protocol for describing electronic music. The MIDI file format is part of this standard, and all consumer PC’s are capable of playing them through the PC’s sound card. In addition, General MIDI standardizes instrument and percussion names as used in MDL.
3
Our Goal We need to define a function (called “test” in text): musicToMidi :: Music -> IO () musicToMidi = outputMidiFile "test.mid“. performToMidi. perform defCon “perform” was defined in the previous chapter, and “defCon” is just the default Context. So additionally we need: performToMidi :: Performance -> MidiFile outputMidiFile :: String -> MidiFile -> IO () “MidiFile” and “outputMidiFile” are imported from Haskore. Our main job is to define “performToMidi”.
4
MidiFile Data Type The MidiFile data type (imported from Haskore) captures the essence of a Midi file: data MidiFile = MidiFile MFType Division [Track] deriving (Show, Eq) type MFType = Int type Track = [MEvent] data Division = Ticks Int | SMPTE Int Int deriving (Show,Eq) data MEvent= MidiEvent ElapsedTime MidiEvent | MetaEvent ElapsedTime MetaEvent | NoEvent deriving (Show,Eq) type ElapsedTime = Int [ see text for details ]
5
MIDI Events MIDI events are like MDL events, except that there are events for both start and end of a note: data MidiEvent = | NoteOff MidiChannel MPitch Velocity | NoteOn MidiChannel MPitch Velocity | ProgChange MidiChannel ProgNum |... deriving (Show, Eq) type MPitch= Int;type Velocity = Int type ProgNum= Int;type MidiChannel = Int data MetaEvent = SetTempo MTempo |... deriving (Show, Eq) type MTempo = Int
6
performToMidi To convert a Performance into a MidiFile value: performToMidi :: Performance -> MidiFile performToMidi pf = MidiFile mfType (Ticks division) (map performToMEvs (splitByInst pf)) mfType = 1:: Int division = 96:: Int This leaves two functions: splitByInst :: Performance -> [(MidiChannel,ProgNum,Performance)] performToMEvs :: (MidiChannel,ProgNum,Performance) -> [MEvent] In a Type 1 MIDI file, each instrument is associated with a separate track. splitByInst splits the Performance to achieve this (see text for details).
7
performToMEvs performToMEvs :: (MidiChannel,ProgNum,Performance) -> [MEvent] performToMEvs (ch,pn,perf) = let setupInst= MidiEvent 0 (ProgChange ch pn) setTempo= MetaEvent 0 (SetTempo tempo) loop []= [] loop (e:es)= let (mev1,mev2) = mkMEvents ch e in mev1 : insertMEvent mev2 (loop es) in setupInst : setTempo : loop perf tempo = 500000 :: Int-- number of microsecs in one beat insertMEvent :: MEvent -> [MEvent] -> [MEvent] insertMEvent ev1 [] = [ev1] insertMEvent ev1@(MidiEvent t1 _) evs@(ev2@(MidiEvent t2 _) : evs') = if t1 <= t2 then ev1 : evs else ev2 : insertMEvent ev1 evs'
8
mkMEvents Finally, we convert individual notes: mkMEvents :: MidiChannel -> Event -> (MEvent,MEvent) mkMEvents mChan (Event { eTime = t, ePitch = p, eDur = d }) = ( MidiEvent (toDelta t) (NoteOn mChan p 127), MidiEvent (toDelta (t+d)) (NoteOff mChan p 127) ) toDelta t = round (t * 4.0 * intToFloat division)
Similar presentations
© 2024 SlidePlayer.com. Inc.
All rights reserved.