diff options
author | Andrew Cady <d@jerkface.net> | 2014-01-20 11:02:51 -0500 |
---|---|---|
committer | Andrew Cady <d@jerkface.net> | 2014-01-20 14:36:51 -0500 |
commit | 84b5695349e4d549e2535003a6321f450862ac62 (patch) | |
tree | c7bd2cf83466610be30f368c6ba774c88a2558a0 | |
parent | 47ae11f017ad2cecb15ffe807a30f6cbd647f758 (diff) |
correctly implement multi-channel display
-rw-r--r-- | axis.hs | 78 |
1 files changed, 29 insertions, 49 deletions
@@ -15,7 +15,7 @@ import Graphics.UI.SDL.Keysym as SDL.Keysym | |||
15 | import Graphics.UI.SDL.Primitives as SDL.Primitive | 15 | import Graphics.UI.SDL.Primitives as SDL.Primitive |
16 | import Data.Int (Int16) | 16 | import Data.Int (Int16) |
17 | import qualified System.Exit as Exit | 17 | import qualified System.Exit as Exit |
18 | import Data.List (elemIndex, elemIndices, filter, groupBy, length, reverse) | 18 | import Data.List (elemIndex, elemIndices, filter, groupBy, length, reverse, sort) |
19 | import GHC.Word | 19 | import GHC.Word |
20 | import Data.Bits | 20 | import Data.Bits |
21 | import qualified Sound.ALSA.Sequencer.Event as Event | 21 | import 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 | ||
111 | pitchToColor p = | 112 | pitchToColor 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 | ||
118 | smartDrawKeys' :: (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 () | 119 | smartDrawKeys :: (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 () |
119 | smartDrawKeys' reallyErase oldKeys nowKeys videoSurface font axis_key_locations axis_key_size = do | 120 | smartDrawKeys 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 | 140 | drawKey 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 | |||
148 | drawKey :: (Integral a1, Integral a2, Integral a) => Int -> SDL.Surface -> SDL.TTF.Font -> [(a1, a2)] -> a -> SDL.Color -> t -> Maybe String -> [Int] -> IO () | ||
149 | drawKey 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 |