diff options
author | Andrew Cady <d@jerkface.net> | 2015-12-06 03:56:57 -0500 |
---|---|---|
committer | Andrew Cady <d@jerkface.net> | 2015-12-06 03:56:57 -0500 |
commit | f8ac45371bff74034205fec65a9e69c0a527e8a4 (patch) | |
tree | 3b60e505e4d05a027e0f5ec0baec14dab8599857 | |
parent | b6a53650153ba5f0b6cab3679d582c0788107e9c (diff) |
use RealTimeQueue for queuing midi output to ALSA
-rw-r--r-- | axis-of-eval.cabal | 5 | ||||
-rw-r--r-- | midi-dump.hs | 37 |
2 files changed, 31 insertions, 11 deletions
diff --git a/axis-of-eval.cabal b/axis-of-eval.cabal index 7a879dd..fa1049d 100644 --- a/axis-of-eval.cabal +++ b/axis-of-eval.cabal | |||
@@ -36,7 +36,8 @@ executable midi-dump | |||
36 | hs-source-dirs: . | 36 | hs-source-dirs: . |
37 | build-depends: | 37 | build-depends: |
38 | base, time, containers, haskore, alsa-seq, alsa-core, mtl, clock, | 38 | base, time, containers, haskore, alsa-seq, alsa-core, mtl, clock, |
39 | sqlite-simple, bytestring, base-prelude, midi-alsa, midi | 39 | sqlite-simple, bytestring, base-prelude, midi-alsa, midi, psqueues, |
40 | transformers, semigroups | ||
40 | main-is: midi-dump.hs | 41 | main-is: midi-dump.hs |
41 | other-modules: AlsaSeq | 42 | other-modules: AlsaSeq, Midi, RealTimeQueue |
42 | ghc-options: -threaded -W -Wall -O2 | 43 | ghc-options: -threaded -W -Wall -O2 |
diff --git a/midi-dump.hs b/midi-dump.hs index f332482..536d78d 100644 --- a/midi-dump.hs +++ b/midi-dump.hs | |||
@@ -29,6 +29,7 @@ import qualified Sound.ALSA.Sequencer.Time as Time | |||
29 | import qualified Sound.ALSA.Sequencer.RealTime as RealTime | 29 | import qualified Sound.ALSA.Sequencer.RealTime as RealTime |
30 | 30 | ||
31 | import Midi | 31 | import Midi |
32 | import RealTimeQueue as Q hiding (null) | ||
32 | 33 | ||
33 | verbose :: Bool | 34 | verbose :: Bool |
34 | verbose = False | 35 | verbose = False |
@@ -42,13 +43,14 @@ data LoopState = LoopState { | |||
42 | _wantExit :: Bool, | 43 | _wantExit :: Bool, |
43 | keysDown :: MidiPitchSet, | 44 | keysDown :: MidiPitchSet, |
44 | _playNOW :: [Event.Data], | 45 | _playNOW :: [Event.Data], |
46 | _scheduled :: Q.Queue Event.Data, | ||
45 | _recording :: Recording, | 47 | _recording :: Recording, |
46 | _replay :: Recording, | 48 | _replay :: Recording, |
47 | _lastTick :: TimeSpec | 49 | _lastTick :: TimeSpec |
48 | } | 50 | } |
49 | 51 | ||
50 | initializeState :: TimeSpec -> LoopState | 52 | initializeState :: TimeSpec -> LoopState |
51 | initializeState now = LoopState False Set.empty [] (StartRecording now) (StartRecording now) now | 53 | initializeState now = LoopState False Set.empty [] createQueue (StartRecording now) (StartRecording now) now |
52 | 54 | ||
53 | data LoopEnv = LoopEnv { | 55 | data LoopEnv = LoopEnv { |
54 | _saver :: Chan CompleteRecording, | 56 | _saver :: Chan CompleteRecording, |
@@ -131,14 +133,23 @@ mainLoop = do | |||
131 | maybeReadLine >>= maybe processMidi processCommand | 133 | maybeReadLine >>= maybe processMidi processCommand |
132 | wantExit <- gets _wantExit | 134 | wantExit <- gets _wantExit |
133 | 135 | ||
134 | scheduled <- gets _playNOW | 136 | playImmediates |
135 | unless (null scheduled) $ do | 137 | playScheduled |
136 | forM_ scheduled playNoteEv | ||
137 | -- TODO: flush ALSA output here (and remove flush from playNoteEv) | ||
138 | modify $ \s -> s { _playNOW = [] } | ||
139 | 138 | ||
140 | unless wantExit mainLoop | 139 | unless wantExit mainLoop |
141 | 140 | ||
141 | playScheduled :: MidiController () | ||
142 | playScheduled = queueAction $ runScheduledIO (TimeSpec 1 0) $ uncurry alsaDelayNoteEv | ||
143 | -- TODO: flush ALSA output here (and remove flush from playNoteEv) | ||
144 | |||
145 | playImmediates :: MidiController () | ||
146 | playImmediates = do | ||
147 | immediate <- gets _playNOW | ||
148 | unless (null immediate) $ do | ||
149 | forM_ immediate playNoteEv | ||
150 | -- TODO: flush ALSA output here (and remove flush from playNoteEv) | ||
151 | modify $ \s -> s { _playNOW = [] } | ||
152 | |||
142 | _playNote :: Bool -> Event.Note -> RWST LoopEnv () LoopState IO () | 153 | _playNote :: Bool -> Event.Note -> RWST LoopEnv () LoopState IO () |
143 | _playNote noteOn note = | 154 | _playNote noteOn note = |
144 | playNoteEv $ Event.NoteEv onoff note | 155 | playNoteEv $ Event.NoteEv onoff note |
@@ -149,14 +160,22 @@ delayEvent evt ts = evt {Event.time = Time.consRel $ Time.Real (RealTime.fromInt | |||
149 | where nanosecs = timeSpecAsNanoSecs ts | 160 | where nanosecs = timeSpecAsNanoSecs ts |
150 | 161 | ||
151 | playNoteEv :: Event.Data -> RWST LoopEnv () LoopState IO () | 162 | playNoteEv :: Event.Data -> RWST LoopEnv () LoopState IO () |
152 | playNoteEv = delayNoteEv (TimeSpec 0 0) | 163 | playNoteEv = alsaDelayNoteEv (TimeSpec 0 0) |
153 | 164 | ||
154 | delayNoteEv :: TimeSpec -> Event.Data -> RWST LoopEnv () LoopState IO () | 165 | alsaDelayNoteEv :: TimeSpec -> Event.Data -> RWST LoopEnv () LoopState IO () |
155 | delayNoteEv delay nevdata = do | 166 | alsaDelayNoteEv delay nevdata = do |
156 | ms <- getMidiSender | 167 | ms <- getMidiSender |
157 | publicAddr <- asks _publicAddr | 168 | publicAddr <- asks _publicAddr |
158 | liftIO $ ms $ Event.simple publicAddr nevdata `delayEvent` delay | 169 | liftIO $ ms $ Event.simple publicAddr nevdata `delayEvent` delay |
159 | 170 | ||
171 | queueAction :: (Queue Event.Data -> MidiController (Queue Event.Data)) -> MidiController () | ||
172 | queueAction act = do | ||
173 | q <- gets _scheduled | ||
174 | act q >>= \q' -> modify $ \s -> s { _scheduled = q' } | ||
175 | |||
176 | delayNoteEv :: TimeSpec -> Event.Data -> RWST LoopEnv () LoopState IO () | ||
177 | delayNoteEv delay nevdata = queueAction $ scheduleEventIO (delay, nevdata) | ||
178 | |||
160 | _whenFlag :: forall (m :: * -> *) s. MonadState s m => (s -> Bool) -> m () -> m () | 179 | _whenFlag :: forall (m :: * -> *) s. MonadState s m => (s -> Bool) -> m () -> m () |
161 | _whenFlag flag f = gets flag >>= flip when f | 180 | _whenFlag flag f = gets flag >>= flip when f |
162 | 181 | ||