summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Cady <d@jerkface.net>2015-12-13 12:23:17 -0500
committerAndrew Cady <d@jerkface.net>2015-12-13 12:23:17 -0500
commit24d588281bb8d0d4c3967b425266d28490edd830 (patch)
tree7f26a550d0cd668735433b507e19846a41f10f4b
parentd372f8ba1f6852fce5a5ac8eec4a9371828617c5 (diff)
initial support for reprogammable triads
-rw-r--r--midi-dump.hs63
1 files changed, 55 insertions, 8 deletions
diff --git a/midi-dump.hs b/midi-dump.hs
index 369ee9c..7b7bf8b 100644
--- a/midi-dump.hs
+++ b/midi-dump.hs
@@ -3,12 +3,14 @@
3{-# LANGUAGE OverloadedStrings #-} 3{-# LANGUAGE OverloadedStrings #-}
4{-# LANGUAGE ScopedTypeVariables #-} 4{-# LANGUAGE ScopedTypeVariables #-}
5{-# LANGUAGE KindSignatures #-} 5{-# LANGUAGE KindSignatures #-}
6{-# LANGUAGE LambdaCase #-}
6 7
7import AlsaSeq 8import AlsaSeq
8import Control.Monad.RWS.Strict 9import Control.Monad.RWS.Strict
9import Data.List 10import Data.List
10import Data.Maybe 11import Data.Maybe
11import qualified Data.Map.Strict as Map 12import qualified Data.Map.Strict as Map
13import Data.Map.Strict (Map)
12import qualified Sound.ALSA.Exception as AlsaExc 14import qualified Sound.ALSA.Exception as AlsaExc
13import qualified Sound.ALSA.Sequencer.Event as Event 15import qualified Sound.ALSA.Sequencer.Event as Event
14import System.Clock 16import System.Clock
@@ -53,13 +55,30 @@ main = main' `AlsaExc.catch` handler
53 handler e = when verbose $ putStrLn $ "alsa_exception: " ++ AlsaExc.show e 55 handler e = when verbose $ putStrLn $ "alsa_exception: " ++ AlsaExc.show e
54 56
55data TriadType = Major | Minor deriving (Show, Eq, Ord) 57data TriadType = Major | Minor deriving (Show, Eq, Ord)
56data Triad = Triad TriadType Event.Pitch Event.Velocity deriving (Show, Eq, Ord) 58data Triad =
59 Triad
60 { _triadType :: TriadType
61 , triadPitch :: Event.Pitch
62 , _triadVelocity :: Event.Velocity
63 }
64 deriving (Show, Eq, Ord)
65toPitchClass :: Event.Pitch -> PitchClass
66toPitchClass = Event.unPitch >>> (`mod` 12) >>> fromIntegral
67tonic :: Triad -> PitchClass
68tonic (Triad _ p _) = toPitchClass p
69
70data TriadRecorder = TriadNotRecording | AwaitingTriad | AwaitingRelease Event.Pitch TriadType | Recording Event.Pitch TriadType
71
72type PitchClass = Int
73type ScaleDegree = Int
57 74
58data LoopState = LoopState { 75data LoopState = LoopState {
59 _wantExit :: Bool, 76 _wantExit :: Bool,
60 _waitThreads :: [IO (Thread.Result ())], 77 _waitThreads :: [IO (Thread.Result ())],
61 _keysDown :: MidiPitchMap, 78 _keysDown :: MidiPitchMap,
62 _triad :: Set Triad, 79 _triad :: Set Triad,
80 _triadMap :: Map (PitchClass, TriadType) (Set ScaleDegree),
81 _triadRecording :: TriadRecorder,
63 _scheduled :: Q.Queue Event.Data, 82 _scheduled :: Q.Queue Event.Data,
64 _metronome :: Maybe Metronome, 83 _metronome :: Maybe Metronome,
65 _recording :: Recording, 84 _recording :: Recording,
@@ -74,7 +93,7 @@ data Metronome = Metronome {
74} 93}
75 94
76initializeState :: TimeSpec -> LoopState 95initializeState :: TimeSpec -> LoopState
77initializeState now = LoopState False [] Map.empty Set.empty createQueue Nothing (StartRecording now) (StartRecording now) now 96initializeState now = LoopState False [] Map.empty Set.empty Map.empty TriadNotRecording createQueue Nothing (StartRecording now) (StartRecording now) now
78 97
79data LoopEnv = LoopEnv { 98data LoopEnv = LoopEnv {
80 _saver :: Chan CompleteRecording, 99 _saver :: Chan CompleteRecording,
@@ -292,6 +311,13 @@ processCommand "dump" = sqlSelectEVERYTHING >>= saveRecording "dumped.mid" . mco
292processCommand "C" = do 311processCommand "C" = do
293 let notes = (Event.NoteEv Event.NoteOn . mkNote) <$> [60, 64, 67] 312 let notes = (Event.NoteEv Event.NoteOn . mkNote) <$> [60, 64, 67]
294 forM_ notes (delayNoteEv (TimeSpec 0 0)) 313 forM_ notes (delayNoteEv (TimeSpec 0 0))
314processCommand "M-t" = gets _triadRecording >>= \case
315 TriadNotRecording -> do
316 liftIO $ putStrLn "Recording triad"
317 modify $ \s -> s { _triadRecording = AwaitingTriad }
318 _ -> do
319 liftIO $ putStrLn "Cancelled ecording triad"
320 modify $ \s -> s { _triadRecording = TriadNotRecording }
295processCommand "M-m" = do 321processCommand "M-m" = do
296 now <- getAbsTime 322 now <- getAbsTime
297 m <- gets _metronome 323 m <- gets _metronome
@@ -402,7 +428,27 @@ processMidi = do
402 428
403 whenFlag _printChordKeys $ liftIO $ printChordLn' newKeys 429 whenFlag _printChordKeys $ liftIO $ printChordLn' newKeys
404 430
405 filterTriads newKeys 431 triadRecording <- gets _triadRecording
432 case triadRecording of
433 TriadNotRecording -> filterTriads newKeys
434 AwaitingTriad -> do
435 let detected = snd <$> listToMaybe (detectTriads newKeys)
436 forM_ detected $ \t@(Triad _ p _) ->
437 (modify $ \s -> s { _triadRecording = AwaitingRelease p (_triadType t) })
438 AwaitingRelease pitch ttype -> do
439 let pc = toPitchClass pitch
440 when (Map.null newKeys) $
441 modify $ \s -> s { _triadRecording = Recording pitch ttype, _triadMap = Map.delete (pc, ttype) (_triadMap s) }
442 Recording pitch ttype -> do
443 let f :: Map (Event.Channel, Event.Pitch) Event.Velocity -> Set ScaleDegree
444 f = Set.fromList . map (subtract (fromIntegral $ Event.unPitch pitch) . fromIntegral . Event.unPitch . snd) . Map.keys
445 pc = toPitchClass pitch
446 let detected = triadPitch . snd <$> detectTriads newKeys
447 detected :: [Event.Pitch]
448 if pitch `elem` detected then
449 modify $ \s -> s { _triadRecording = TriadNotRecording }
450 else
451 modify $ \s -> s { _triadMap = Map.insertWith Set.union (pc, ttype) (f newKeys) (_triadMap s) }
406 452
407 -- Whenever no keys are pressed, flush any buffered events to the database 453 -- Whenever no keys are pressed, flush any buffered events to the database
408 when (Map.null newKeys) $ do 454 when (Map.null newKeys) $ do
@@ -432,13 +478,14 @@ triadOff :: Triad -> Triad
432triadOff (Triad t p _) = Triad t p (Event.Velocity 0) 478triadOff (Triad t p _) = Triad t p (Event.Velocity 0)
433 479
434sendTriadEvents :: Triad -> MidiController () 480sendTriadEvents :: Triad -> MidiController ()
435sendTriadEvents (Triad _ (Event.Pitch base) vel) = do 481sendTriadEvents t@(Triad ttype (Event.Pitch base) vel) = do
436 forM_ notes (delayNoteEv (TimeSpec 0 0)) 482 mappedNotes <- Map.lookup (tonic t, ttype) <$> gets _triadMap
437 return () 483 when (isJust mappedNotes) $ notesOn $ notes (map fromIntegral . Set.toList <$> mappedNotes)
438 484
439 where 485 where
440 notes = fromVel vel <$> fill base 486 notesOn n = forM_ n (delayNoteEv (TimeSpec 0 0))
441 fill n = [ x + y | x <- [n, n + 7], y <- [12, -12]] 487 notes :: Maybe [Word8] -> [Event.Data]
488 notes mappedNotes = fromVel vel <$> map (base +) (fromMaybe [12, -12, 7+12, 7-12] mappedNotes)
442 fromVel (Event.Velocity 0) pitch = Event.NoteEv Event.NoteOff $ Event.simpleNote (Event.Channel 0) (Event.Pitch pitch) (Event.Velocity 0) 489 fromVel (Event.Velocity 0) pitch = Event.NoteEv Event.NoteOff $ Event.simpleNote (Event.Channel 0) (Event.Pitch pitch) (Event.Velocity 0)
443 fromVel v pitch = Event.NoteEv Event.NoteOn $ Event.simpleNote (Event.Channel 0) (Event.Pitch pitch) v 490 fromVel v pitch = Event.NoteEv Event.NoteOn $ Event.simpleNote (Event.Channel 0) (Event.Pitch pitch) v
444 491