{- The famous song, that is named "Tomatensalat" in German. -} module Main where import qualified Sound.MIDI.File as MidiFile import qualified Sound.MIDI.File.Save as Save import Sound.MIDI.File (ElapsedTime, ) import qualified Sound.MIDI.File.Event.Meta as MetaEvent import qualified Sound.MIDI.File.Event as Event import qualified Sound.MIDI.Message.Channel as ChannelMsg import qualified Sound.MIDI.Message.Channel.Voice as VoiceMsg -- import qualified Sound.MIDI.Parser.Report as Report import qualified Data.EventList.Relative.TimeMixed as EventListTM import qualified Data.EventList.Relative.BodyTime as EventListBT import qualified Data.EventList.Relative.MixedTime as EventListMT import qualified Data.EventList.Relative.TimeBody as EventList -- import Data.EventList.Relative.MixedBody ((/.), (./), ) import qualified Data.ByteString.Lazy as B -- import qualified Numeric.NonNegative.Wrapper as NonNeg import Data.Tuple.HT (mapFst, ) import qualified Control.Monad.Trans.State as State import Control.Monad (liftM3, ) g0,a0,b0,c1,cs1,d1,e1,f1,g1,a1,as1 :: VoiceMsg.Pitch [g0,a0,b0,c1,cs1,d1,e1,f1,g1,a1,as1] = map VoiceMsg.toPitch [55,57,59,60,61,62,64,65,67,69,70] melody :: [(VoiceMsg.Pitch, ElapsedTime)] melody = let n p t = (VoiceMsg.increasePitch (-12) p, t) in n g0 3 : n c1 1 : n c1 1 : n c1 1 : n e1 1 : n d1 1 : n c1 1 : n d1 1 : n g0 1 : n g0 1 : n g0 3 : n c1 1 : n c1 1 : n c1 1 : n e1 1 : n d1 1 : n c1 1 : n g1 3 : n e1 3 : n f1 1 : n f1 1 : n f1 1 : n a1 1 : n g1 1 : n f1 1 : n e1 1 : n e1 1 : n e1 1 : n g1 1 : n f1 1 : n e1 1 : n d1 1 : n d1 1 : n d1 1 : n f1 1 : n e1 1 : n d1 1 : n c1 3 : [] melodyEvents :: [(VoiceMsg.Pitch, ElapsedTime)] -> Int -> EventListBT.T ElapsedTime Event.T melodyEvents mel pn = let chan = ChannelMsg.toChannel 0 vel = VoiceMsg.toVelocity (VoiceMsg.normalVelocity+25) event = Event.MIDIEvent . ChannelMsg.Cons chan . ChannelMsg.Voice in EventListBT.fromPairList $ concatMap (\(pgm, (n,t)) -> [(event $ VoiceMsg.ProgramChange pgm, 0), (event $ VoiceMsg.NoteOn n vel, t), (event $ VoiceMsg.NoteOff n vel, 0)]) $ zip (cycle $ map VoiceMsg.toProgram [pn..(pn+4)]) $ concat $ replicate 5 $ mel solo :: Int -> MidiFile.T solo pn = MidiFile.Cons MidiFile.Parallel (MidiFile.Ticks 4) [EventList.cons 0 (Event.MetaEvent $ MetaEvent.SetTempo 1500000) $ EventListTM.switchTimeR const $ EventListMT.consTime 0 $ melodyEvents melody pn] melody2 :: [(VoiceMsg.Pitch, ElapsedTime)] melody2 = let n p t = (p, t) in n g0 3 : n c1 1 : n c1 1 : n c1 1 : n e1 1 : n d1 1 : n c1 1 : n d1 1 : n g0 1 : n g0 1 : n g0 3 : n d1 1 : n d1 1 : n d1 1 : n f1 1 : n e1 1 : n d1 1 : n g1 3 : n e1 3 : n f1 1 : n f1 1 : n f1 1 : n a1 1 : n g1 1 : n f1 1 : n e1 1 : n e1 1 : n e1 1 : n g1 1 : n f1 1 : n e1 1 : n d1 1 : n d1 1 : n d1 1 : n f1 1 : n e1 1 : n d1 1 : n c1 3 : [] nextProgram :: State.State [VoiceMsg.Program] VoiceMsg.Program nextProgram = State.state $ \(pgm:pgms) -> (pgm,pgms) accompEvents :: Int -> EventListBT.T ElapsedTime Event.T accompEvents pn = let chan = ChannelMsg.toChannel 0 vel = VoiceMsg.toVelocity (VoiceMsg.normalVelocity-25) event = Event.MIDIEvent . ChannelMsg.Cons chan . ChannelMsg.Voice chord :: VoiceMsg.Pitch -> (VoiceMsg.Pitch, VoiceMsg.Pitch) -> State.State [VoiceMsg.Program] [(Event.T, ElapsedTime)] chord a_ (b_,c_) = liftM3 (\ pgm0 pgm1 pgm2 -> let a = VoiceMsg.increasePitch (-12) a_ b = VoiceMsg.increasePitch (-12) b_ c = VoiceMsg.increasePitch (-12) c_ in map (mapFst event) $ (VoiceMsg.ProgramChange pgm0, 0) : (VoiceMsg.NoteOn a vel, 1) : (VoiceMsg.NoteOff a vel, 0) : (VoiceMsg.ProgramChange pgm1, 0) : (VoiceMsg.NoteOn b vel, 0) : (VoiceMsg.NoteOn c vel, 1) : (VoiceMsg.NoteOff b vel, 0) : (VoiceMsg.NoteOff c vel, 0) : (VoiceMsg.ProgramChange pgm2, 0) : (VoiceMsg.NoteOn b vel, 0) : (VoiceMsg.NoteOn c vel, 1) : (VoiceMsg.NoteOff b vel, 0) : (VoiceMsg.NoteOff c vel, 0) : []) nextProgram nextProgram nextProgram introChords = chord c1 (e1, g1) : chord g0 (e1, g1) : chord c1 (e1, g1) : chord g0 (e1, g1) : [] chords = chord c1 (e1, g1) : chord g0 (e1, g1) : chord b0 (d1, g1) : chord g0 (d1, g1) : chord b0 (f1, g1) : chord g0 (f1, g1) : chord c1 (e1, g1) : chord g0 (g1, as1) : chord c1 (f1, a1) : chord a0 (f1, a1) : chord c1 (e1, g1) : chord cs1 (e1, a1) : chord b0 (d1, g1) : chord g0 (d1, g1) : chord c1 (e1, g1) : chord g0 (e1, g1) : [] in EventListBT.fromPairList $ concat $ State.evalState (sequence $ concat $ introChords : replicate 5 chords) $ cycle $ map VoiceMsg.toProgram [pn..(pn+4)] song :: Int -> Int -> MidiFile.T song pna pnm = MidiFile.Cons MidiFile.Parallel (MidiFile.Ticks 4) [EventList.cons 0 (Event.MetaEvent $ MetaEvent.SetTempo 1500000) $ let tb t = EventListTM.switchTimeR const . EventListMT.consTime t in EventList.mergeBy (\ _ _ -> True) (tb 0 $ accompEvents pna) (tb 9 $ melodyEvents melody2 pnm)] main :: IO () main = B.writeFile "tomatosalad.mid" (Save.toByteString (solo 16)) >> B.writeFile "hal.mid" (Save.toByteString (solo 21)) >> B.writeFile "graphtheory.mid" (Save.toByteString (solo 26)) >> B.writeFile "haltomato.mid" (Save.toByteString (song 16 21))