3 import qualified MIDI as React
5 import qualified Sound.JACK as Jack
6 import qualified Sound.MIDI.Message as MIDI
7 import qualified Sound.JACK.MIDI as JMIDI
9 import Data.IORef ( IORef
15 import Control.Concurrent
16 import qualified Foreign.C.Error as E
17 import qualified Data.EventList.Relative.TimeBody as EventList
18 import qualified Data.EventList.Absolute.TimeBody as EventListAbs
19 import qualified Data.EventList.Relative.TimeMixed as EventListTM
20 import qualified Control.Monad.Exception.Synchronous as Sync
21 import qualified Control.Monad.Trans.Class as Trans
23 import qualified Sound.MIDI.Message.Channel as Channel
24 import qualified Sound.MIDI.Message.Channel.Voice as Voice
25 import qualified Sound.MIDI.Message.Class.Construct as MidiCons
31 -- | List of absolute times (at which events should occur) and events.
32 -- We assume that the list is sorted.
33 outLoop :: [(Time,MIDI.T)]
34 outLoop = concat [[(t,MIDI.Channel $ Channel.Cons
35 { Channel.messageChannel = Channel.toChannel 4
36 , Channel.messageBody =
37 Channel.Voice $ Voice.NoteOn (Voice.toPitch 60) (Voice.toVelocity 100)
38 }),(t+0.5,MIDI.Channel $ Channel.Cons
39 { Channel.messageChannel = Channel.toChannel 4
40 , Channel.messageBody =
41 Channel.Voice $ Voice.NoteOff (Voice.toPitch 60) (Voice.toVelocity 100)
44 reactogonName :: String
45 reactogonName = "Reactogon"
51 outPortName = "output"
54 fsPortName = "fluidsynth:midi"
57 stateRef <- newMVar outLoop
58 Jack.handleExceptions $
59 Jack.withClientDefault reactogonName $ \client ->
60 Jack.withPort client outPortName $ \output -> do
61 Jack.withProcess client (process client stateRef output) $
62 Jack.withActivation client $ do
63 Jack.connect client (reactogonName ++ ":" ++ outPortName) fsPortName
64 Trans.lift $ putStrLn $ "Started " ++ reactogonName
65 Trans.lift $ Jack.waitForBreak
69 MVar [(Time,MIDI.T)] ->
70 JMIDI.Port Jack.Output ->
72 Sync.ExceptionalT E.Errno IO ()
73 process client stateRef output nframes@(Jack.NFrames nframesInt) =
75 rate <- Trans.lift $ Jack.getSampleRate client
76 events <- Trans.lift $ takeMVar stateRef
77 lframe <- Trans.lift $ Jack.lastFrameTime client
78 let rateD = fromIntegral rate
79 (Jack.NFrames lframeInt) = lframe
80 currentTime = fromIntegral lframeInt / rateD
81 playableEvents = filter
82 (\(t,_) -> t - currentTime > - fromIntegral nframesInt / rateD) events
83 (processableEvents, futureEvents) = break ((> currentTime) . fst) $
85 Trans.lift $ print currentTime
86 Trans.lift $ putMVar stateRef futureEvents
87 if null processableEvents
88 then Trans.lift $ putStrLn "No events in queue."
89 else Trans.lift $ putStrLn "Event!"
90 let firstEventTime = fst $ head processableEvents
91 Trans.lift $ print $ map ((* rateD) . smartSub currentTime . fst) processableEvents
92 JMIDI.writeEventsToPort output nframes $
93 EventListAbs.fromPairList $
94 map (\(t,e) -> (Jack.NFrames $ floor $ rateD * smartSub t currentTime, e))
98 smartSub x y = if x < y then y - x else x - y
101 else JMIDI.writeEventsToPort output nframes $
102 EventListAbs.mapTime Jack.NFrames $
103 EventList.toAbsoluteEventList 0 $
104 EventList.mapTime (\(Jack.NFrames n) -> n) $
105 EventList.fromPairList processableEvents