module MicroMidi (Event(NoteOn,NoteOff,ProgramChange,ControllerChange),
                  mergestreams,
                  tomidi
                  ) where

import Data.Ratio
import qualified Midi
import qualified Pitch

clip lower upper value =
    max lower (min upper value)

centstomidinoteandbend::Double->(Midi.Note,Midi.Bend)
centstomidinoteandbend cents =
    let note = 60 + round (cents / 100)
        residue = cents - (fromIntegral note - 60)*100
        bend = clip 0 16383 (round (8192 + 4096 * residue / 100))
    in (note,bend)

data (Pitch.Pitch p) => Event p = NoteOn Midi.Channel p Midi.Velocity
                                | NoteOff Midi.Channel p Midi.Velocity
                                | ControllerChange Midi.Channel Midi.Controller Midi.Value
                                | ProgramChange Midi.Channel Midi.Program
                                  deriving (Eq,Show)

eventtomidi (NoteOn chn pitch velocity) =
    let (note,bend) = centstomidinoteandbend (Pitch.toCents pitch)
    in [Midi.PitchBend chn bend, Midi.NoteOn chn note velocity]
eventtomidi (NoteOff chn pitch velocity) =
    let (note,bend) = centstomidinoteandbend (Pitch.toCents pitch)
    in [Midi.NoteOff chn note velocity]
eventtomidi (ProgramChange chn program) =
    [Midi.ProgramChange chn program]
eventtomidi (ControllerChange chn controller value) =
    [Midi.ControllerChange chn controller value]

withdelta _ [] = []
withdelta delta (x:xs) = (delta,x):withdelta 0 xs

tomidi timedevents =
    [(0,Midi.MetaEvent (Midi.TimeSignature 4 2))]
    ++ concatMap (\(delta,event) -> withdelta delta (eventtomidi event)) timedevents
    ++ [(0,Midi.MetaEvent Midi.EndOfTrack)]

mergestreams [] ys = ys
mergestreams xs [] = xs
mergestreams (x@(xdelta,xevent):xr) (y@(ydelta,yevent):yr) =
    if xdelta <= ydelta
       then x : mergestreams xr ((ydelta-xdelta,yevent):yr)
       else y : mergestreams ((xdelta-ydelta,xevent):xr) yr

mergetest::[(Midi.DeltaTime,Event Rational)]
mergetest = [(0,NoteOn 0 (1%8) 64)
            ,(800,NoteOff 0 (1%8) 64)]
            `mergestreams`
            [(100,NoteOn 1 (5%4) 64)
            ,(700,NoteOff 1 (5%4) 64)]
            `mergestreams`
            [(200,NoteOn 2 (3%2) 64)
            ,(600,NoteOff 2 (3%2) 64)]
            `mergestreams`
            [(300,NoteOn 3 (11%4) 64)
            ,(300,NoteOff 3 (11%4) 64)
            ,(0,NoteOn 3 (5%2) 64)
            ,(200,NoteOff 3 (5%2) 64)]
-- should equal testsong
              
testsong =
    [(0,NoteOn 0 (1%8) 64)
    ,(100,NoteOn 1 (5%4) 64)
    ,(100,NoteOn 2 (3%2) 64)
    ,(100,NoteOn 3 (11%4) 64)
    ,(300,NoteOff 3 (11%4) 64)
    ,(0,NoteOn 3 (5%2) 64)
    ,(200,NoteOff 0 (1%8) 64)
    ,(0,NoteOff 1 (5%4) 64)
    ,(0,NoteOff 2 (3%2) 64)
    ,(0,NoteOff 3 (5%2) 64)
    ]

test =
    writeFile "test.mid" (Midi.tofile 100 (tomidi testsong))