1 / 8

Chapter 22

Chapter 22. From Performance to MIDI. 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.

ivanbritt
Download Presentation

Chapter 22

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  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)

More Related