-- ========================================================================================== -- Conversion to MEvent datatype > module Euterpea.IO.MIDI.MEvent where > import Euterpea.Music > data MEvent = MEvent { > eTime :: PTime, -- onset time > eInst :: InstrumentName, -- instrument > ePitch :: AbsPitch, -- pitch number > eDur :: DurT, -- note duration > eVol :: Volume, -- volume > eParams :: [Double]} -- optional other parameters > deriving (Show,Eq,Ord) > type Performance = [MEvent] > type PTime = Rational > type DurT = Rational > merge :: Performance -> Performance -> Performance > merge [] es2 = es2 > merge es1 [] = es1 > merge a@(e1:es1) b@(e2:es2) = > if eTime e1 < eTime e2 then e1 : merge es1 b > else e2 : merge a es2 > data MContext = MContext {mcTime :: PTime, > mcInst :: InstrumentName, > mcDur :: DurT, > mcVol :: Volume} > deriving Show > perform :: (ToMusic1 a) => Music a -> Performance > perform = perform1 . toMusic1 > perform1 :: Music1 -> Performance > perform1 = fst . perform1Dur > perform1Dur :: Music1 -> (Performance, DurT) > perform1Dur = musicToMEvents defCon . applyControls where > defCon = MContext {mcTime = 0, mcInst = AcousticGrandPiano, mcDur = metro 120 qn, mcVol=127} > -- timing musicToMEventss > metro :: Int -> Dur -> DurT > metro setting dur = 60 / (fromIntegral setting * dur) > applyControls :: Music1 -> Music1 > applyControls (Modify (Tempo r) m) = scaleDurations r $ applyControls m > applyControls (Modify (Transpose k) m) = shiftPitches1 k $ applyControls m > applyControls (Modify x m) = Modify x $ applyControls m > applyControls (m1 :+: m2) = applyControls m1 :+: applyControls m2 > applyControls (m1 :=: m2) = applyControls m1 :=: applyControls m2 > applyControls x = x > musicToMEvents :: MContext -> Music1 -> (Performance, DurT) > musicToMEvents c@MContext{mcTime=t, mcDur=dt} (Prim (Note d p)) = ([noteToMEvent c d p], d*dt) > musicToMEvents c@MContext{mcTime=t, mcDur=dt} (Prim (Rest d)) = ([], d*dt) > musicToMEvents c@MContext{mcTime=t, mcDur=dt} (m1 :+: m2) = > let (evs1, d1) = musicToMEvents c m1 > (evs2, d2) = musicToMEvents c{mcTime = t+d1} m2 > in (evs1 ++ evs2, d1+d2) > musicToMEvents c@MContext{mcTime=t, mcDur=dt} (m1 :=: m2) = > let (evs1, d1) = musicToMEvents c m1 > (evs2, d2) = musicToMEvents c m2 > in (merge evs1 evs2, max d1 d2) > musicToMEvents c (Modify (Instrument i) m) = musicToMEvents c{mcInst=i} m > musicToMEvents c (Modify (Phrase pas) m) = phraseToMEvents c pas m > musicToMEvents c (Modify (KeySig x y) m) = musicToMEvents c m -- KeySig causes no change > musicToMEvents c (Modify (Custom x) m) = musicToMEvents c m -- Custom cuases no change > musicToMEvents c m@(Modify x m') = musicToMEvents c $ applyControls m -- Transpose and Tempo addressed by applyControls > noteToMEvent :: MContext -> Dur -> (Pitch, [NoteAttribute]) -> MEvent > noteToMEvent c@(MContext ct ci cdur cvol) d (p, nas) = > let e0 = MEvent{eTime=ct, ePitch=absPitch p, eInst=ci, eDur=d*cdur, eVol=cvol, eParams=[]} > in foldr nasFun e0 nas where > nasFun :: NoteAttribute -> MEvent -> MEvent > nasFun (Volume v) ev = ev {eVol = v} > nasFun (Params pms) ev = ev {eParams = pms} > nasFun _ ev = ev > phraseToMEvents :: MContext -> [PhraseAttribute] -> Music1 -> (Performance, DurT) > phraseToMEvents c [] m = musicToMEvents c m > phraseToMEvents c@MContext{mcTime=t, mcInst=i, mcDur=dt} (pa:pas) m = > let pfd@(pf,dur) = phraseToMEvents c pas m > loud x = phraseToMEvents c (Dyn (Loudness x) : pas) m > stretch x = let t0 = eTime (head pf); r = x/dur > upd (e@MEvent {eTime = t, eDur = d}) = > let dt = t-t0 > t' = (1+dt*r)*dt + t0 > d' = (1+(2*dt+d)*r)*d > in e {eTime = t', eDur = d'} > in (map upd pf, (1+x)*dur) > inflate x = let t0 = eTime (head pf); > r = x/dur > upd (e@MEvent {eTime = t, eVol = v}) = > e {eVol = round ( (1+(t-t0)*r) * > fromIntegral v)} > in (map upd pf, dur) > in case pa of > Dyn (Accent x) -> > ( map (\e-> e {eVol = round (x * fromIntegral (eVol e))}) pf, dur) > Dyn (StdLoudness l) -> > case l of > PPP -> loud 40; PP -> loud 50; P -> loud 60 > MP -> loud 70; SF -> loud 80; MF -> loud 90 > NF -> loud 100; FF -> loud 110; FFF -> loud 120 > Dyn (Loudness x) -> phraseToMEvents c{mcVol = round x} pas m > Dyn (Crescendo x) -> inflate x ; Dyn (Diminuendo x) -> inflate (-x) > Tmp (Ritardando x) -> stretch x ; Tmp (Accelerando x) -> stretch (-x) > Art (Staccato x) -> (map (\e-> e {eDur = x * eDur e}) pf, dur) > Art (Legato x) -> (map (\e-> e {eDur = x * eDur e}) pf, dur) > Art (Slurred x) -> > let lastStartTime = foldr (\e t -> max (eTime e) t) 0 pf > setDur e = if eTime e < lastStartTime > then e {eDur = x * eDur e} > else e > in (map setDur pf, dur) > Art _ -> pfd -- not supported > Orn _ -> pfd -- not supported