summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Cady <d@jerkface.net>2014-01-20 11:02:51 -0500
committerAndrew Cady <d@jerkface.net>2014-01-20 14:36:51 -0500
commit84b5695349e4d549e2535003a6321f450862ac62 (patch)
treec7bd2cf83466610be30f368c6ba774c88a2558a0
parent47ae11f017ad2cecb15ffe807a30f6cbd647f758 (diff)
correctly implement multi-channel display
-rw-r--r--axis.hs78
1 files changed, 29 insertions, 49 deletions
diff --git a/axis.hs b/axis.hs
index 768e4fa..e263862 100644
--- a/axis.hs
+++ b/axis.hs
@@ -15,7 +15,7 @@ import Graphics.UI.SDL.Keysym as SDL.Keysym
15import Graphics.UI.SDL.Primitives as SDL.Primitive 15import Graphics.UI.SDL.Primitives as SDL.Primitive
16import Data.Int (Int16) 16import Data.Int (Int16)
17import qualified System.Exit as Exit 17import qualified System.Exit as Exit
18import Data.List (elemIndex, elemIndices, filter, groupBy, length, reverse) 18import Data.List (elemIndex, elemIndices, filter, groupBy, length, reverse, sort)
19import GHC.Word 19import GHC.Word
20import Data.Bits 20import Data.Bits
21import qualified Sound.ALSA.Sequencer.Event as Event 21import qualified Sound.ALSA.Sequencer.Event as Event
@@ -40,7 +40,7 @@ _COLORIZE_BY_CHANNEL = True
40 40
41_AXIS_ROWS = 7 + 4 41_AXIS_ROWS = 7 + 4
42_AXIS_UNIQUE_COLS = 7 42_AXIS_UNIQUE_COLS = 7
43_AXIS_COLS_REPEAT = 1 43_AXIS_COLS_REPEAT = 2
44_AXIS_TOP_PITCH = 81 + (7 * (_AXIS_ROWS - 7) `div` 2) 44_AXIS_TOP_PITCH = 81 + (7 * (_AXIS_ROWS - 7) `div` 2)
45_AXIS_BOTTOM_PITCH = 81 - (7 * _AXIS_ROWS) - 3 45_AXIS_BOTTOM_PITCH = 81 - (7 * _AXIS_ROWS) - 3
46_OCTAVE_SQUASH = False -- it's terrible, because it draws the octave in the wrong shape 46_OCTAVE_SQUASH = False -- it's terrible, because it draws the octave in the wrong shape
@@ -108,6 +108,7 @@ inMajorC pclass = not (pclass == 1 || pclass == 3 || pclass == 6 || pclass == 8
108-- TODO: try hilighting like in the app, where only part of the key is colored 108-- TODO: try hilighting like in the app, where only part of the key is colored
109-- TODO: idea: for the channels, draw a dot. offset the dot from the center of the key at an angle determined by the channel number 109-- TODO: idea: for the channels, draw a dot. offset the dot from the center of the key at an angle determined by the channel number
110 110
111_UNLABELLED_KEYS = True
111pitchToColor p = 112pitchToColor p =
112 case p `mod` 12 of 113 case p `mod` 12 of
113 2 -> (SDL.Color 0xD0 0xD0 0xFF) -- D 114 2 -> (SDL.Color 0xD0 0xD0 0xFF) -- D
@@ -115,41 +116,32 @@ pitchToColor p =
115 x | inMajorC x -> (SDL.Color 0xE0 0xE0 0xE0) -- _CGA !! 7 116 x | inMajorC x -> (SDL.Color 0xE0 0xE0 0xE0) -- _CGA !! 7
116 _ -> _CGA !! 8 117 _ -> _CGA !! 8
117 118
118smartDrawKeys' :: (Integral a, Integral a2, Integral a1) => Bool -> Set.Set (Event.Channel, Event.Pitch) -> Set.Set (Event.Channel, Event.Pitch) -> SDL.Surface -> SDL.TTF.Font -> [(a1, a2)] -> a -> IO () 119smartDrawKeys :: (Integral a, Integral a2, Integral a1) => Bool -> Set.Set (Event.Channel, Event.Pitch) -> Set.Set (Event.Channel, Event.Pitch) -> SDL.Surface -> SDL.TTF.Font -> [(a1, a2)] -> a -> IO ()
119smartDrawKeys' reallyErase oldKeys nowKeys videoSurface font axis_key_locations axis_key_size = do 120smartDrawKeys reallyErase beforeKeys_ nowKeys_ videoSurface font axis_key_locations axis_key_size = do
120 let 121 let
121 ignoreThese = Set.intersection nowKeys oldKeys 122 chanfilter = Set.filter (\ (c, _) -> c /= (Event.Channel 9))
122 drawThese = Set.difference nowKeys ignoreThese 123 beforeKeys = chanfilter beforeKeys_
123 eraseThese = Set.difference oldKeys ignoreThese 124 nowKeys = chanfilter nowKeys_
124 drawSet = Set.difference nowKeys ignoreThese 125
125 eraseSet = Set.difference oldKeys ignoreThese 126 changedPitches = Set.map (\ (_, p) -> p) $ Set.union (Set.difference nowKeys beforeKeys) (Set.difference beforeKeys nowKeys)
126 127 playingNowChans n = Set.map (\ (c, _) -> c) $ Set.filter (\ (_, p) -> p == n) nowKeys
127 changroup = groupBy (\(_, n1) (_, n2) -> n1 == n2) 128 actions = Set.toList $ Set.map (\p -> (p, Set.toList $ playingNowChans p)) changedPitches
128 chanfilter = filter (\ (c, _) -> unChannel c /= 9) 129
129 drawList = changroup . chanfilter $ Set.toList drawSet 130 forM_ actions $ \ (n, chans) -> do
130 eraseList = changroup . chanfilter $ Set.toList eraseSet 131 let text = smartShowPitch (unPitch n)
131 132 pitch = if _OCTAVE_SQUASH then unPitch n `mod` 12 + 12 * 6 else unPitch n
132 133 forM_ (elemIndices pitch pitchIndex) $ \idx -> do
133 forM_ [(eraseList, True), (drawList, False)] $ \ (ls, erase) -> do 134 drawKey idx videoSurface font axis_key_locations axis_key_size
134 forM_ ls $ \ls' -> do 135 (if reallyErase then _KB_BG_COLOR else pitchToColor pitch)
135 let chans = map (\ (c, _) -> unChannel c) ls' 136 (if reallyErase || _UNLABELLED_KEYS then Nothing else (Just text))
136 (_, n) = head ls' 137 chans
137 text = smartShowPitch (unPitch n) 138
138 pitch = if _OCTAVE_SQUASH then unPitch n `mod` 12 + 12 * 6 else unPitch n 139--drawKey :: (Integral a1, Integral a2, Integral a) => Int -> SDL.Surface -> SDL.TTF.Font -> [(a1, a2)] -> a -> SDL.Color -> t -> Maybe String -> [Int] -> IO ()
139 onColor = _KEY_ON_COLOR 140drawKey idx videoSurface font axis_key_locations axis_key_size fillColor text channels = do
140 offColor = pitchToColor pitch
141 forM_ (elemIndices pitch pitchIndex) $ \idx -> do
142 drawKey idx videoSurface font axis_key_locations axis_key_size
143 offColor
144 (if reallyErase then _KB_BG_COLOR_PIXEL else _KEY_BORDER_COLOR_PIXEL)
145 (if erase then Nothing else (Just text))
146 (if erase then [] else (map fromIntegral chans))
147
148drawKey :: (Integral a1, Integral a2, Integral a) => Int -> SDL.Surface -> SDL.TTF.Font -> [(a1, a2)] -> a -> SDL.Color -> t -> Maybe String -> [Int] -> IO ()
149drawKey idx videoSurface font axis_key_locations axis_key_size fillColor borderColor text channels = do
150 let (x, y) = axis_key_locations !! idx 141 let (x, y) = axis_key_locations !! idx
151 drawFilledHexircle videoSurface (fromIntegral x) (fromIntegral y) (fromIntegral axis_key_size) (colorToPixel fillColor) 142 drawFilledHexircle videoSurface (fromIntegral x) (fromIntegral y) (fromIntegral axis_key_size) (colorToPixel fillColor)
152 let len = length channels 143 let len = length channels
144 let channels' = sort channels
153 Control.Monad.when (len /= 0) $ do 145 Control.Monad.when (len /= 0) $ do
154 forM_ [0 .. len - 1] $ \i -> do 146 forM_ [0 .. len - 1] $ \i -> do
155 let (x', y') = if len == 1 then (0, 0) 147 let (x', y') = if len == 1 then (0, 0)
@@ -160,8 +152,8 @@ drawKey idx videoSurface font axis_key_locations axis_key_size fillColor borderC
160 r' = (fromIntegral axis_key_size) / 2 :: Float 152 r' = (fromIntegral axis_key_size) / 2 :: Float
161 x'' = (round x') + (fromIntegral x) 153 x'' = (round x') + (fromIntegral x)
162 y'' = (round y') + (fromIntegral y) 154 y'' = (round y') + (fromIntegral y)
163 chan = channels !! i 155 chan = channels' !! i
164 color = _CHAN_TO_COLOR !! chan 156 color = _CHAN_TO_COLOR !! fromIntegral(unChannel chan)
165 SDL.Primitive.filledCircle videoSurface x'' y'' (round r') (colorToPixel color) 157 SDL.Primitive.filledCircle videoSurface x'' y'' (round r') (colorToPixel color)
166 158
167 case text of 159 case text of
@@ -200,7 +192,7 @@ main =
200 let (axis_key_size, axis_key_locations) = getKeyLocations videoClipRect 192 let (axis_key_size, axis_key_locations) = getKeyLocations videoClipRect
201 193
202 let _ALL_PITCHES = Set.fromList $ map (\p -> (Event.Channel 1, Event.Pitch p)) [_AXIS_BOTTOM_PITCH .. _AXIS_TOP_PITCH] 194 let _ALL_PITCHES = Set.fromList $ map (\p -> (Event.Channel 1, Event.Pitch p)) [_AXIS_BOTTOM_PITCH .. _AXIS_TOP_PITCH]
203 keysOFF really = smartDrawKeys' really _ALL_PITCHES Set.empty videoSurface font axis_key_locations axis_key_size 195 keysOFF really = smartDrawKeys really _ALL_PITCHES Set.empty videoSurface font axis_key_locations axis_key_size
204 allKeysOFF = keysOFF False 196 allKeysOFF = keysOFF False
205 allKeysReallyOFF = keysOFF True 197 allKeysReallyOFF = keysOFF True
206 allKeysOFF 198 allKeysOFF
@@ -225,19 +217,7 @@ main =
225-- let chord = show $ map (\i->elemIndices i pitchIndex) $ pitchList midiKeysDown' 217-- let chord = show $ map (\i->elemIndices i pitchIndex) $ pitchList midiKeysDown'
226-- textBand videoSurface videoClipRect (SDL.Rect 0 70 0 70) font chord 218-- textBand videoSurface videoClipRect (SDL.Rect 0 70 0 70) font chord
227 219
228 -- gah! This is all wrong with the channels added! Unless 220 smartDrawKeys False midiKeysDown midiKeysDown' videoSurface font axis_key_locations axis_key_size
229 -- smartDrawKeys gets smarter... but really, this interface
230 -- is awkward now and should be changed.
231 --
232 -- What we actually want here is the set of pitches where
233 -- there has been an event. Which is simply derived from
234 -- just the Set.difference midiKeys midiKeys'. But we also
235 -- want the set of channels for each pitch -- which is just
236 -- midiKeysDown' (processed as we are doing already in
237 -- smartDrawKeys). Then we build up a map of pitches ->
238 -- channels.
239
240 smartDrawKeys' False midiKeysDown midiKeysDown' videoSurface font axis_key_locations axis_key_size
241 return () 221 return ()
242 222
243 Control.Monad.when (keysDown' /= keysDown && keyDown SDL.SDLK_BACKSPACE keysDown') allKeysOFF 223 Control.Monad.when (keysDown' /= keysDown && keyDown SDL.SDLK_BACKSPACE keysDown') allKeysOFF