summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--midi-dump.hs54
1 files changed, 48 insertions, 6 deletions
diff --git a/midi-dump.hs b/midi-dump.hs
index 534671f..52fe6b2 100644
--- a/midi-dump.hs
+++ b/midi-dump.hs
@@ -39,17 +39,20 @@ main = main' `AlsaExc.catch` handler
39 where 39 where
40 handler e = when verbose $ putStrLn $ "alsa_exception: " ++ AlsaExc.show e 40 handler e = when verbose $ putStrLn $ "alsa_exception: " ++ AlsaExc.show e
41 41
42data Triad = Major Event.Pitch | Minor Event.Pitch deriving (Show, Eq)
43
42data LoopState = LoopState { 44data LoopState = LoopState {
43 _wantExit :: Bool, 45 _wantExit :: Bool,
44 keysDown :: MidiPitchSet, 46 _keysDown :: MidiPitchSet,
47 _triad :: Maybe Triad,
45 _scheduled :: Q.Queue Event.Data, 48 _scheduled :: Q.Queue Event.Data,
46 _recording :: Recording, 49 _recording :: Recording,
47 _replay :: Recording, 50 _replay :: Recording,
48 _lastTick :: TimeSpec 51 _lastTick :: TimeSpec
49} 52}
50 53
51initializeState :: TimeSpec -> LoopState 54initializeState :: TimeSpec -> LoopState
52initializeState now = LoopState False Set.empty createQueue (StartRecording now) (StartRecording now) now 55initializeState now = LoopState False Set.empty Nothing createQueue (StartRecording now) (StartRecording now) now
53 56
54data LoopEnv = LoopEnv { 57data LoopEnv = LoopEnv {
55 _saver :: Chan CompleteRecording, 58 _saver :: Chan CompleteRecording,
@@ -125,7 +128,6 @@ main' :: IO ()
125main' = withAlsaInit $ \h public private q publicAddr privateAddr -> do 128main' = withAlsaInit $ \h public private q publicAddr privateAddr -> do
126 cmdlineAlsaConnect h public 129 cmdlineAlsaConnect h public
127 130
128 putStrLn "Rock on!"
129 startTime <- getTime Monotonic 131 startTime <- getTime Monotonic
130 startTimeReal <- getTime Realtime 132 startTimeReal <- getTime Realtime
131 133
@@ -138,6 +140,7 @@ main' = withAlsaInit $ \h public private q publicAddr privateAddr -> do
138 140
139 let env = LoopEnv saver sqlite startTime startTimeReal h public private q publicAddr privateAddr doSave False lineReader 141 let env = LoopEnv saver sqlite startTime startTimeReal h public private q publicAddr privateAddr doSave False lineReader
140 142
143 putStrLn "Rock on!"
141 (_, ()) <- execRWST mainLoop env $ initializeState startTimeReal 144 (_, ()) <- execRWST mainLoop env $ initializeState startTimeReal
142 return () 145 return ()
143 146
@@ -228,7 +231,7 @@ getMidiSender = do
228processMidi :: MidiController () 231processMidi :: MidiController ()
229processMidi = do 232processMidi = do
230 h <- asks _h 233 h <- asks _h
231 oldKeys <- gets keysDown 234 oldKeys <- gets _keysDown
232 forwardNOW <- getMidiSender 235 forwardNOW <- getMidiSender
233 (events, newKeys) <- liftIO $ parseAlsaEvents' h oldKeys forwardNOW 236 (events, newKeys) <- liftIO $ parseAlsaEvents' h oldKeys forwardNOW
234 237
@@ -241,13 +244,15 @@ processMidi = do
241 244
242 modify $ \s -> 245 modify $ \s ->
243 s 246 s
244 { keysDown = newKeys 247 { _keysDown = newKeys
245 , _recording = recordEvents (_recording s) newEvents 248 , _recording = recordEvents (_recording s) newEvents
246 , _lastTick = now 249 , _lastTick = now
247 } 250 }
248 251
249 whenFlag _printChordKeys $ liftIO $ printChordLn newKeys 252 whenFlag _printChordKeys $ liftIO $ printChordLn newKeys
250 253
254 filterTriads newKeys
255
251 -- Whenever no keys are pressed, flush any buffered events to the database 256 -- Whenever no keys are pressed, flush any buffered events to the database
252 when (Set.null newKeys) $ do 257 when (Set.null newKeys) $ do
253 doSave <- asks _doSave 258 doSave <- asks _doSave
@@ -263,6 +268,43 @@ processMidi = do
263 268
264 modify $ \s -> s { _replay = recordEvents (_replay s) newEvents } 269 modify $ \s -> s { _replay = recordEvents (_replay s) newEvents }
265 270
271filterTriads :: MidiPitchSet -> MidiController ()
272filterTriads newKeys = do
273 let newTriad = detectTriad newKeys
274 oldTriad <- gets _triad
275 when (newTriad /= oldTriad) $ do
276 forM_ oldTriad (sendTriadEvents False)
277 forM_ newTriad (sendTriadEvents True)
278 modify $ \s -> s { _triad = newTriad }
279
280triadBase :: Triad -> Event.Pitch
281triadBase (Major n) = n
282triadBase (Minor n) = n
283
284-- TODO: set velocity based on average from triad (this requires storing that
285-- information, changing the MidiPitchSet type for one more complex than a mere
286-- Set)
287sendTriadEvents :: Bool -> Triad -> MidiController ()
288sendTriadEvents sendOn triad = do
289 forM_ notes (delayNoteEv (TimeSpec 0 0))
290 return ()
291
292 where
293 onoff = bool Event.NoteOff Event.NoteOn sendOn
294 base = Event.unPitch $ triadBase triad
295 notes = (Event.NoteEv onoff . mkNote) <$> fill base
296 fill n = [ x + y | x <- [n, n + 7], y <- [12, -12]]
297
298detectTriad :: MidiPitchSet -> Maybe Triad
299detectTriad pitches = listToMaybe $ concatMap f pitches
300 where
301 f pitch
302 | not $ Set.member (addPitch 7 pitch) pitches = []
303 | Set.member (addPitch 4 pitch) pitches = [Major $ snd pitch] -- TODO: do not just drop the channel!!
304 | Set.member (addPitch 3 pitch) pitches = [Minor $ snd pitch]
305 | otherwise = []
306 addPitch n (c, Event.Pitch p) = (c, Event.Pitch $ p+n)
307
266latestEvent :: Recording -> TimeSpec 308latestEvent :: Recording -> TimeSpec
267latestEvent (StartRecording x) = x 309latestEvent (StartRecording x) = x
268latestEvent (RecordingInProgress _ x []) = x 310latestEvent (RecordingInProgress _ x []) = x