diff options
author | Andrew Cady <d@jerkface.net> | 2014-01-15 12:25:02 -0500 |
---|---|---|
committer | Andrew Cady <d@jerkface.net> | 2014-01-15 12:25:02 -0500 |
commit | 98a3be73f6fa8a8e3f998dd3ace2efab34aa38b8 (patch) | |
tree | 774dc42b85712515bfde42da6179e4f4a1e53285 /midi-dump.hs | |
parent | ff3936a884b56e0777b25aa27092e30a0bc4ec83 (diff) |
factor functions into separate module AlsaSeq.hs
Diffstat (limited to 'midi-dump.hs')
-rw-r--r-- | midi-dump.hs | 137 |
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 @@ | |||
1 | import AlsaSeq | ||
2 | import Control.Concurrent (threadDelay) | ||
1 | import qualified Sound.ALSA.Exception as AlsaExc | 3 | import qualified Sound.ALSA.Exception as AlsaExc |
2 | import qualified Sound.ALSA.Sequencer.Address as Addr | ||
3 | import qualified Sound.ALSA.Sequencer as SndSeq | ||
4 | import qualified Sound.ALSA.Sequencer.Client as Client | ||
5 | import qualified Sound.ALSA.Sequencer.Client.Info as ClientInfo | ||
6 | import qualified Sound.ALSA.Sequencer.Port.Info as PortInfo | ||
7 | import qualified Sound.ALSA.Sequencer.Connect as Connect | ||
8 | import qualified Sound.ALSA.Sequencer.Event as Event | ||
9 | import qualified Sound.ALSA.Sequencer.Event.RemoveMonad as Remove | ||
10 | import qualified Sound.ALSA.Sequencer.Port as Port | ||
11 | import qualified Sound.ALSA.Sequencer.Port.InfoMonad as PortInfoM | ||
12 | import qualified Sound.ALSA.Sequencer.Queue as Queue | ||
13 | import qualified Sound.ALSA.Sequencer.RealTime as RealTime | ||
14 | import qualified Sound.ALSA.Sequencer.Time as Time | ||
15 | import qualified System.Exit as Exit | ||
16 | import qualified System.IO as IO | ||
17 | import System.Environment (getArgs, ) | ||
18 | import Text.Printf | ||
19 | import Control.Monad (when, forM_, forM) | ||
20 | |||
21 | import qualified Data.Set as Set | 4 | import qualified Data.Set as Set |
22 | import Data.List (group, sort) | ||
23 | import Haskore.Basic.Pitch | ||
24 | import Control.Concurrent (threadDelay) | ||
25 | |||
26 | joinWords [] = "" | ||
27 | joinWords ls = foldr1 (\a b -> a ++ " " ++ b) ls | ||
28 | |||
29 | printWords [] = return () -- print nothing if no words (not an empty line) | ||
30 | printWords ls = putStrLn $ joinWords ls | ||
31 | |||
32 | showPitch x = | ||
33 | let (octave, pitch) = Haskore.Basic.Pitch.fromInt $ fromIntegral x | ||
34 | in Haskore.Basic.Pitch.classFormat pitch (show octave) | ||
35 | |||
36 | pitchWords set = map (showPitch . Event.unPitch) $ Set.toList set | ||
37 | |||
38 | prettyNote :: Event.Note -> String | ||
39 | prettyNote (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 | |||
45 | showPorts ls = joinWords $ map (show . head) $ group $ sort ls | ||
46 | |||
47 | showAlsaPorts 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 | |||
55 | alsaClients 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 | |||
62 | alsaClientPorts 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 | |||
70 | alsaInit 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 | |||
96 | cmdlineAlsaConnect 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 | |||
110 | parseAlsaEvents 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 | |||
127 | forwardNoteEvent 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 | ||
136 | main :: IO () | ||
137 | main = (do | 6 | main = (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!" |