summaryrefslogtreecommitdiff
path: root/midi-dump.hs
diff options
context:
space:
mode:
authorAndrew Cady <d@jerkface.net>2014-01-15 12:25:02 -0500
committerAndrew Cady <d@jerkface.net>2014-01-15 12:25:02 -0500
commit98a3be73f6fa8a8e3f998dd3ace2efab34aa38b8 (patch)
tree774dc42b85712515bfde42da6179e4f4a1e53285 /midi-dump.hs
parentff3936a884b56e0777b25aa27092e30a0bc4ec83 (diff)
factor functions into separate module AlsaSeq.hs
Diffstat (limited to 'midi-dump.hs')
-rw-r--r--midi-dump.hs137
1 files changed, 3 insertions, 134 deletions
diff --git a/midi-dump.hs b/midi-dump.hs
index d463750..3de065b 100644
--- a/midi-dump.hs
+++ b/midi-dump.hs
@@ -1,139 +1,8 @@
1import AlsaSeq
2import Control.Concurrent (threadDelay)
1import qualified Sound.ALSA.Exception as AlsaExc 3import qualified Sound.ALSA.Exception as AlsaExc
2import qualified Sound.ALSA.Sequencer.Address as Addr
3import qualified Sound.ALSA.Sequencer as SndSeq
4import qualified Sound.ALSA.Sequencer.Client as Client
5import qualified Sound.ALSA.Sequencer.Client.Info as ClientInfo
6import qualified Sound.ALSA.Sequencer.Port.Info as PortInfo
7import qualified Sound.ALSA.Sequencer.Connect as Connect
8import qualified Sound.ALSA.Sequencer.Event as Event
9import qualified Sound.ALSA.Sequencer.Event.RemoveMonad as Remove
10import qualified Sound.ALSA.Sequencer.Port as Port
11import qualified Sound.ALSA.Sequencer.Port.InfoMonad as PortInfoM
12import qualified Sound.ALSA.Sequencer.Queue as Queue
13import qualified Sound.ALSA.Sequencer.RealTime as RealTime
14import qualified Sound.ALSA.Sequencer.Time as Time
15import qualified System.Exit as Exit
16import qualified System.IO as IO
17import System.Environment (getArgs, )
18import Text.Printf
19import Control.Monad (when, forM_, forM)
20
21import qualified Data.Set as Set 4import qualified Data.Set as Set
22import Data.List (group, sort)
23import Haskore.Basic.Pitch
24import Control.Concurrent (threadDelay)
25
26joinWords [] = ""
27joinWords ls = foldr1 (\a b -> a ++ " " ++ b) ls
28
29printWords [] = return () -- print nothing if no words (not an empty line)
30printWords ls = putStrLn $ joinWords ls
31
32showPitch x =
33 let (octave, pitch) = Haskore.Basic.Pitch.fromInt $ fromIntegral x
34 in Haskore.Basic.Pitch.classFormat pitch (show octave)
35
36pitchWords set = map (showPitch . Event.unPitch) $ Set.toList set
37
38prettyNote :: Event.Note -> String
39prettyNote (Event.Note noteChannel noteNote noteVelocity noteOffVelocity noteDuration) =
40 let pitch = Event.unPitch noteNote
41 vlcty = Event.unVelocity noteVelocity
42 in
43 printf "%d(%d)" pitch vlcty
44
45showPorts ls = joinWords $ map (show . head) $ group $ sort ls
46
47showAlsaPorts h = do
48 c <- alsaClients h
49 putStrLn ""
50 printf "%15s %s\n" "Ports" "Names"
51 printf "%15s %s\n" "=====" "====="
52 forM c (\(name, ports) -> printf "%15s %s\n" (showPorts ports) name)
53 putStrLn ""
54
55alsaClients h = do
56 portsNames <- ClientInfo.queryLoop h $ \cinfo -> do
57 ports <- alsaClientPorts h cinfo
58 name <- ClientInfo.getName cinfo
59 return (name, ports)
60 return portsNames
61
62alsaClientPorts h cinfo = do
63 client <- ClientInfo.getClient cinfo
64 ports <- PortInfo.queryLoop h client $ \pinfo -> do
65 c <- PortInfo.getClient pinfo
66 let (Client.Cons p) = c in
67 return p
68 return ports
69
70alsaInit k = do
71 SndSeq.withDefault SndSeq.Nonblock $ \h -> do
72
73 Client.setName (h :: SndSeq.T SndSeq.DuplexMode) "Haskell Beats" -- In imperative language MIDI sequencer, you rock beat. In Haskell language MIDI sequencer, rock beat you!
74
75 Port.withSimple h "inout"
76 (Port.caps [Port.capRead, Port.capSubsRead, Port.capWrite, Port.capSubsWrite])
77 (Port.types [Port.typeMidiGeneric, Port.typeApplication]) $ \public -> do
78 Port.withSimple h "private"
79 (Port.caps [Port.capRead, Port.capWrite])
80 (Port.types [Port.typeMidiGeneric]) $ \private -> do
81 Queue.with h $ \q -> do
82
83 PortInfoM.modify h public $ do
84 PortInfoM.setTimestamping True
85 PortInfoM.setTimestampReal True
86 PortInfoM.setTimestampQueue q
87
88 c <- Client.getId h
89 let publicAddr = Addr.Cons c public
90 privateAddr = Addr.Cons c private
91
92 Queue.control h q Event.QueueStart Nothing
93
94 k h public private q publicAddr privateAddr
95
96cmdlineAlsaConnect h public = do
97 args <- getArgs
98 case args of
99
100 [input, output] -> do
101 (Connect.createFrom h public =<< Addr.parse h input)
102 (Connect.createTo h public =<< Addr.parse h output)
103 return ()
104
105 _ -> do
106 showAlsaPorts h
107 IO.hPutStrLn IO.stderr "need arguments: input-port output-port"
108 Exit.exitFailure
109
110parseAlsaEvents h keysDown immediate = loop keysDown
111 where
112 loop keysDown = do
113 pending <- Event.inputPending h True
114 if (pending == 0) then
115 return keysDown
116 else do
117 ev <- Event.input h
118 case Event.body ev of
119 Event.NoteEv Event.NoteOn n -> do
120 immediate ev
121 loop (Set.insert (Event.noteNote n) keysDown)
122 Event.NoteEv Event.NoteOff n -> do
123 immediate ev
124 loop (Set.delete (Event.noteNote n) keysDown)
125 _ -> loop keysDown
126
127forwardNoteEvent h q publicAddr ev = do
128 let mkEv e = (Event.simple publicAddr e) { Event.queue = q, Event.time = Time.consAbs $ Time.Real $ RealTime.fromDouble 0 }
129 let immediate = \a b c -> do { Event.output h $ mkEv $ a b c; _ <- Event.drainOutput h; return (); }
130 case Event.body ev of
131 Event.NoteEv Event.NoteOn n -> immediate Event.NoteEv Event.NoteOn n
132 Event.NoteEv Event.NoteOff n -> immediate Event.NoteEv Event.NoteOff n
133 -- TODO: forward other event types?
134 _ -> return ()
135 5
136main :: IO ()
137main = (do 6main = (do
138 7
139 alsaInit $ \h public private q publicAddr privateAddr -> do 8 alsaInit $ \h public private q publicAddr privateAddr -> do
@@ -146,7 +15,7 @@ main = (do
146 if (keysDown == keysDown') then 15 if (keysDown == keysDown') then
147 threadDelay 15000 -- 15ms. Seems like a lot, but it sounds OK. Cuts CPU down to 2%. 16 threadDelay 15000 -- 15ms. Seems like a lot, but it sounds OK. Cuts CPU down to 2%.
148 else do 17 else do
149 printWords $ pitchWords keysDown' 18 printChordLn keysDown'
150 go keysDown' 19 go keysDown'
151 20
152 putStrLn "Rock on!" 21 putStrLn "Rock on!"