summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Cady <d@jerkface.net>2015-12-05 11:45:51 -0500
committerAndrew Cady <d@jerkface.net>2015-12-05 11:47:55 -0500
commit9207096755c1db24b81115fe02ca6e97e21e48bf (patch)
treecb40e55cfd9ee8ddb3cbf54ead7c26c9e5b83e2c
parent7477265c3c91341bcc96181fd876de0231bd667f (diff)
Implement replay functionality
(Replay last MIDI input, divided by 3 second periods of silence.) This has a bug where if there is too much input to replay, the program exits. (Oddly, there is no exit failure code.) I think this is because the kernel ALSA buffer is full. The solution is to implement in-application queueing.
-rw-r--r--midi-dump.hs53
1 files changed, 37 insertions, 16 deletions
diff --git a/midi-dump.hs b/midi-dump.hs
index 07281f5..07dfb1c 100644
--- a/midi-dump.hs
+++ b/midi-dump.hs
@@ -14,7 +14,6 @@ import qualified Sound.ALSA.Sequencer.Event as Event
14import System.Clock 14import System.Clock
15 15
16import Control.Applicative 16import Control.Applicative
17import Data.Int
18import Database.SQLite.Simple 17import Database.SQLite.Simple
19import Database.SQLite.Simple.FromRow () 18import Database.SQLite.Simple.FromRow ()
20 19
@@ -145,33 +144,51 @@ _playNote noteOn note =
145 playNoteEv $ Event.NoteEv onoff note 144 playNoteEv $ Event.NoteEv onoff note
146 where onoff = if noteOn then Event.NoteOn else Event.NoteOff 145 where onoff = if noteOn then Event.NoteOn else Event.NoteOff
147 146
148delayEvent :: Event.T -> Integer -> Event.T 147delayEvent :: Event.T -> TimeSpec -> Event.T
149delayEvent evt nanosecs = evt {Event.time = Time.consRel $ Time.Real (RealTime.fromInteger nanosecs)} 148delayEvent evt ts = evt {Event.time = Time.consRel $ Time.Real (RealTime.fromInteger nanosecs)}
149 where nanosecs = timeSpecAsNanoSecs ts
150 150
151playNoteEv :: Event.Data -> RWST LoopEnv () LoopState IO () 151playNoteEv :: Event.Data -> RWST LoopEnv () LoopState IO ()
152playNoteEv nevdata = do 152playNoteEv = delayNoteEv (TimeSpec 0 0)
153
154delayNoteEv :: TimeSpec -> Event.Data -> RWST LoopEnv () LoopState IO ()
155delayNoteEv delay nevdata = do
153 ms <- getMidiSender 156 ms <- getMidiSender
154 publicAddr <- asks _publicAddr 157 publicAddr <- asks _publicAddr
155 liftIO $ ms $ Event.simple publicAddr nevdata `delayEvent` (2 * 10^(9::Int)) 158 liftIO $ ms $ Event.simple publicAddr nevdata `delayEvent` delay
156 159
157_whenFlag :: forall (m :: * -> *) s. MonadState s m => (s -> Bool) -> m () -> m () 160_whenFlag :: forall (m :: * -> *) s. MonadState s m => (s -> Bool) -> m () -> m ()
158_whenFlag flag f = gets flag >>= flip when f 161_whenFlag flag f = gets flag >>= flip when f
159 162
160processCommand :: forall (m :: * -> *). (MonadIO m, MonadState LoopState m) => String -> m () 163mkNote :: Word8 -> Event.Note
164mkNote pitch = Event.simpleNote (Event.Channel 0) (Event.Pitch pitch) (Event.Velocity 128)
165
166processCommand :: String -> RWST LoopEnv () LoopState IO ()
161processCommand "exit" = modify $ \s -> s { _wantExit = True } 167processCommand "exit" = modify $ \s -> s { _wantExit = True }
162processCommand "" = return () 168-- processCommand "" = return ()
169processCommand "" = gets _replay >>= playRecording
163processCommand "C" = do 170processCommand "C" = do
164 let mkNote pitch = Event.simpleNote (Event.Channel 0) (Event.Pitch pitch) (Event.Velocity 128)
165 let notes = (Event.NoteEv Event.NoteOn . mkNote) <$> [60, 64, 67] 171 let notes = (Event.NoteEv Event.NoteOn . mkNote) <$> [60, 64, 67]
166 modify $ \s -> s { _playNOW = notes } 172 modify $ \s -> s { _playNOW = notes }
167processCommand "C'" = do 173processCommand "C'" = do
174 let notes = (Event.NoteEv Event.NoteOn . mkNote) <$> [60, 64, 67]
175 forM_ notes (delayNoteEv (TimeSpec 2 0))
176{-
177processCommand "C'" = do
168 -- changing the duration seems to do nothing 178 -- changing the duration seems to do nothing
169 let mkNote pitch = Event.simpleNote (Event.Channel 0) (Event.Pitch pitch) (Event.Velocity 128) 179 let mkNote pitch = Event.simpleNote (Event.Channel 0) (Event.Pitch pitch) (Event.Velocity 128)
170 setDuration d note = note { Event.noteDuration = Event.Duration d } 180 setDuration d note = note { Event.noteDuration = Event.Duration d }
171 let notes = (Event.NoteEv Event.NoteOn . setDuration 4290000000 . mkNote) <$> [60, 64, 67] 181 let notes = (Event.NoteEv Event.NoteOn . setDuration 4290000000 . mkNote) <$> [60, 64, 67]
172 modify $ \s -> s { _playNOW = notes } 182 modify $ \s -> s { _playNOW = notes }
183-}
173processCommand str = liftIO $ putStrLn $ "Unknown command: " ++ str 184processCommand str = liftIO $ putStrLn $ "Unknown command: " ++ str
174 185
186playRecording :: Recording -> RWST LoopEnv () LoopState IO ()
187playRecording (RecordingInProgress _ _ evts@(_:_)) =
188 mapM_ (uncurry delayNoteEv) (zip (subtract (head delays) <$> delays) events)
189 where (delays, events) = unzip $ fmap Event.body <$> reverse evts
190playRecording _ = return ()
191
175getMidiSender :: RWST LoopEnv () LoopState IO MidiHook 192getMidiSender :: RWST LoopEnv () LoopState IO MidiHook
176getMidiSender = do 193getMidiSender = do
177 h <- asks _h 194 h <- asks _h
@@ -196,21 +213,25 @@ processMidi = do
196 liftIO $ printChordLn newKeys 213 liftIO $ printChordLn newKeys
197 modify $ \s -> s { keysDown = newKeys, _recording = recordEvents (_recording s) newEvents, lastTick = now } 214 modify $ \s -> s { keysDown = newKeys, _recording = recordEvents (_recording s) newEvents, lastTick = now }
198 215
199{- 216 when (Set.null newKeys) $ do
217
218 doSave <- asks _doSave
219 when doSave $ gets _recording >>= saveMidi >> return ()
220 modify $ \s -> s { _recording = StartRecording now }
200 221
201 when (Set.null oldKeys) $ do 222 when (Set.null oldKeys) $ do
202 223
203 replay <- gets _replay 224 replay <- gets _replay
204 225 when (latestEvent replay < (now - TimeSpec 3 0)) $ do
205 when (lastEventTime replay < now - 10*10^9) $ do
206 modify $ \s -> s { _replay = StartRecording now } 226 modify $ \s -> s { _replay = StartRecording now }
207-} 227 return ()
208 228
209 when (Set.null newKeys) $ do 229 modify $ \s -> s { _replay = recordEvents (_replay s) newEvents }
210 230
211 doSave <- asks _doSave 231latestEvent :: Recording -> TimeSpec
212 when doSave $ gets _recording >>= saveMidi >> return () 232latestEvent (StartRecording x) = x
213 modify $ \s -> s { _recording = StartRecording now } 233latestEvent (RecordingInProgress _ x []) = x
234latestEvent (RecordingInProgress _ _ ((x,_):_)) = x
214 235
215maybeReadLine :: RWST LoopEnv () LoopState IO (Maybe String) 236maybeReadLine :: RWST LoopEnv () LoopState IO (Maybe String)
216maybeReadLine = asks _lineReader >>= liftIO . tryTakeMVar 237maybeReadLine = asks _lineReader >>= liftIO . tryTakeMVar