@@ -56,29 +56,16 @@ data ClockConfig
56
56
}
57
57
58
58
-- | action to be executed on a tick,
59
- -- | given the current timespan and nudge
59
+ -- | given the current timespan, nudge and reference to the clock
60
60
type TickAction
61
- = (Time ,Time ) -> Double -> LinkOperations -> IO ()
62
-
63
- -- | link operations for easy interaction with the clock
64
- data LinkOperations
65
- = LinkOperations
66
- { timeAtBeat :: Link. Beat -> IO Link. Micros
67
- ,timeToCycles :: Link. Micros -> IO Time
68
- ,getTempo :: IO Link. BPM
69
- ,setTempo :: Link. BPM -> Link. Micros -> IO ()
70
- ,linkToOscTime :: Link. Micros -> O. Time
71
- ,beatToCycles :: CDouble -> CDouble
72
- ,cyclesToBeat :: CDouble -> CDouble
73
- }
61
+ = (Time ,Time ) -> Double -> ClockConfig -> ClockRef -> (Link. SessionState , Link. SessionState ) -> IO ()
74
62
75
63
-- | possible actions for interacting with the clock
76
64
data ClockAction
77
65
= NoAction
78
66
| SetCycle Time
79
67
| SetTempo Time
80
68
| SetNudge Double
81
- deriving Show
82
69
83
70
defaultCps :: Double
84
71
defaultCps = 0.575
@@ -187,34 +174,19 @@ tick = do
187
174
-- hands the current link operations to the TickAction
188
175
clockProcess :: Clock ()
189
176
clockProcess = do
190
- (ClockMemory config (ClockRef _ abletonLink) action) <- ask
177
+ (ClockMemory config ref @ (ClockRef _ abletonLink) action) <- ask
191
178
st <- get
192
179
let logicalEnd = logicalTime config (start st) $ ticks st + 1
193
180
startCycle = arcEnd $ nowArc st
194
181
195
182
sessionState <- liftIO $ Link. createAndCaptureAppSessionState abletonLink
196
- endCycle <- liftIO $ timeToCycles' config sessionState logicalEnd
197
-
198
- let st' = st {nowArc = (startCycle,endCycle)}
199
-
200
- nowOsc <- O. time
201
- nowLink <- liftIO $ Link. clock abletonLink
183
+ endCycle <- liftIO $ timeToCycles config sessionState logicalEnd
202
184
203
- let ops = LinkOperations {
204
- timeAtBeat = \ beat -> Link. timeAtBeat sessionState beat (cQuantum config) ,
205
- timeToCycles = timeToCycles' config sessionState,
206
- getTempo = Link. getTempo sessionState,
207
- setTempo = Link. setTempo sessionState,
208
- linkToOscTime = \ lt -> addMicrosToOsc (lt - nowLink) nowOsc,
209
- beatToCycles = \ beat -> beat / (cBeatsPerCycle config),
210
- cyclesToBeat = \ cyc -> cyc * (cBeatsPerCycle config)
211
- }
212
-
213
- liftIO $ action (nowArc st') (nudged st') ops
185
+ liftIO $ action (startCycle,endCycle) (nudged st) config ref (sessionState, sessionState)
214
186
215
187
liftIO $ Link. commitAndDestroyAppSessionState abletonLink sessionState
216
188
217
- put st'
189
+ put (st {nowArc = (startCycle,endCycle)})
218
190
tick
219
191
220
192
processAction :: ClockAction -> Clock ()
@@ -240,7 +212,7 @@ processAction (SetCycle cyc) = do
240
212
modify (\ st -> st {ticks = 0 , start = now, nowArc = (cyc,cyc)})
241
213
242
214
---------------------------------------------------------------
243
- -------------------- helper functions ------------- ------------
215
+ ----------- functions representing link operations ------------
244
216
---------------------------------------------------------------
245
217
246
218
arcStart :: (Time , Time ) -> Time
@@ -249,8 +221,37 @@ arcStart = fst
249
221
arcEnd :: (Time , Time ) -> Time
250
222
arcEnd = snd
251
223
252
- timeToCycles' :: ClockConfig -> Link. SessionState -> Link. Micros -> IO Time
253
- timeToCycles' config ss time = do
224
+ beatToCycles :: ClockConfig -> Double -> Double
225
+ beatToCycles config beat = beat / (coerce $ cBeatsPerCycle config)
226
+
227
+ cyclesToBeat :: ClockConfig -> Double -> Double
228
+ cyclesToBeat config cyc = cyc * (coerce $ cBeatsPerCycle config)
229
+
230
+ getSessionState :: ClockRef -> IO Link. SessionState
231
+ getSessionState (ClockRef _ abletonLink) = Link. createAndCaptureAppSessionState abletonLink
232
+
233
+ -- onSingleTick assumes it runs at beat 0.
234
+ -- The best way to achieve that is to use forceBeatAtTime.
235
+ -- But using forceBeatAtTime means we can not commit its session state.
236
+ getZeroedSessionState :: ClockConfig -> ClockRef -> IO Link. SessionState
237
+ getZeroedSessionState config (ClockRef _ abletonLink) = do
238
+ ss <- Link. createAndCaptureAppSessionState abletonLink
239
+ nowLink <- liftIO $ Link. clock abletonLink
240
+ Link. forceBeatAtTime ss 0 (nowLink + processAhead) (cQuantum config)
241
+ return ss
242
+ where processAhead = round $ (cProcessAhead config) * 1000000
243
+
244
+ getTempo :: Link. SessionState -> IO Time
245
+ getTempo ss = fmap toRational $ Link. getTempo ss
246
+
247
+ setTempoCPS :: Time -> Link. Micros -> ClockConfig -> Link. SessionState -> IO ()
248
+ setTempoCPS cps now conf ss = Link. setTempo ss (coerce $ cyclesToBeat conf ((fromRational cps) * 60 )) now
249
+
250
+ timeAtBeat :: ClockConfig -> Link. SessionState -> Double -> IO Link. Micros
251
+ timeAtBeat config ss beat = Link. timeAtBeat ss (coerce beat) (cQuantum config)
252
+
253
+ timeToCycles :: ClockConfig -> Link. SessionState -> Link. Micros -> IO Time
254
+ timeToCycles config ss time = do
254
255
beat <- Link. beatAtTime ss time (cQuantum config)
255
256
return $! (toRational beat) / (toRational (cBeatsPerCycle config))
256
257
@@ -260,6 +261,12 @@ cyclesToTime config ss cyc = do
260
261
let beat = (fromRational cyc) * (cBeatsPerCycle config)
261
262
Link. timeAtBeat ss beat (cQuantum config)
262
263
264
+ linkToOscTime :: ClockRef -> Link. Micros -> IO O. Time
265
+ linkToOscTime (ClockRef _ abletonLink) lt = do
266
+ nowOsc <- O. time
267
+ nowLink <- liftIO $ Link. clock abletonLink
268
+ return $ addMicrosToOsc (lt - nowLink) nowOsc
269
+
263
270
addMicrosToOsc :: Link. Micros -> O. Time -> O. Time
264
271
addMicrosToOsc m t = ((fromIntegral m) / 1000000 ) + t
265
272
@@ -288,42 +295,10 @@ getCycleTime :: ClockConfig -> ClockRef -> IO Time
288
295
getCycleTime config (ClockRef _ abletonLink) = do
289
296
now <- Link. clock abletonLink
290
297
ss <- Link. createAndCaptureAppSessionState abletonLink
291
- c <- timeToCycles' config ss now
298
+ c <- timeToCycles config ss now
292
299
Link. destroySessionState ss
293
300
return $! c
294
301
295
- -- onSingleTick assumes it runs at beat 0.
296
- -- The best way to achieve that is to use forceBeatAtTime.
297
- -- But using forceBeatAtTime means we can not commit its session state.
298
- -- Another session state, which we will commit,
299
- -- is introduced to keep track of tempo changes.
300
- getZeroedLinkOperations :: ClockConfig -> ClockRef -> IO LinkOperations
301
- getZeroedLinkOperations config (ClockRef _ abletonLink) = do
302
- sessionState <- Link. createAndCaptureAppSessionState abletonLink
303
- zeroedSessionState <- Link. createAndCaptureAppSessionState abletonLink
304
-
305
- nowOsc <- O. time
306
- nowLink <- Link. clock abletonLink
307
-
308
- Link. forceBeatAtTime zeroedSessionState 0 (nowLink + processAhead) (cQuantum config)
309
-
310
- Link. commitAndDestroyAppSessionState abletonLink sessionState
311
- Link. destroySessionState zeroedSessionState
312
-
313
- return $ LinkOperations {
314
- timeAtBeat = \ beat -> Link. timeAtBeat zeroedSessionState beat (cQuantum config),
315
- timeToCycles = timeToCycles' config zeroedSessionState,
316
- getTempo = Link. getTempo zeroedSessionState,
317
- setTempo = \ bpm micros ->
318
- Link. setTempo zeroedSessionState bpm micros >>
319
- Link. setTempo sessionState bpm micros,
320
- linkToOscTime = \ lt -> addMicrosToOsc (lt - nowLink) nowOsc,
321
- beatToCycles = \ beat -> beat / (cBeatsPerCycle config),
322
- cyclesToBeat = \ cyc -> cyc * (cBeatsPerCycle config)
323
- }
324
- where processAhead = round $ (cProcessAhead config) * 1000000
325
-
326
-
327
302
resetClock :: ClockRef -> IO ()
328
303
resetClock clock = setClock clock 0
329
304
@@ -352,6 +327,20 @@ setNudge (ClockRef clock _) n = atomically $ do
352
327
NoAction -> modifyTVar' clock (const $ SetNudge n)
353
328
_ -> retry
354
329
330
+ -- Used for Tempo callback
331
+ -- Tempo changes will be applied.
332
+ -- However, since the full arc is processed at once and since Link does not support
333
+ -- scheduling, tempo change may affect scheduling of events that happen earlier
334
+ -- in the normal stream (the one handled by onTick).
335
+ clockOnce :: TickAction -> ClockConfig -> ClockRef -> IO ()
336
+ clockOnce action config ref@ (ClockRef _ abletonLink) = do
337
+ ss <- getZeroedSessionState config ref
338
+ temposs <- Link. createAndCaptureAppSessionState abletonLink
339
+ -- The nowArc is a full cycle
340
+ action (0 ,1 ) 0 config ref (ss, temposs)
341
+ Link. destroySessionState ss
342
+ Link. commitAndDestroyAppSessionState abletonLink temposs
343
+
355
344
disableLink :: ClockRef -> IO ()
356
345
disableLink (ClockRef _ abletonLink) = Link. disable abletonLink
357
346
0 commit comments