10
10
"""
11
11
12
12
import random
13
+ import time
13
14
14
15
from europi import *
15
16
from europi_script import EuroPiScript
16
17
17
18
from experimental .euclid import generate_euclidean_pattern
18
- from experimental .screensaver import OledWithScreensaver
19
+ from experimental .screensaver import Screensaver
19
20
20
- ssoled = OledWithScreensaver ()
21
21
22
22
class EuclidGenerator :
23
23
"""Generates the euclidean rhythm for a single output
@@ -141,23 +141,45 @@ def __init__(self, script):
141
141
@param script The EuclideanRhythms script that owns this menu
142
142
"""
143
143
self .script = script
144
+ self .first_render = True
145
+
146
+ self .generator_index = None
147
+ self .k2_percent = None
148
+ self .read_knobs ()
149
+
150
+ def read_knobs (self ):
151
+ """Read the current state of the knobs and return whether or not the state has changed, requiring a re-render
152
+
153
+ @return True if we should re-render the GUI, False if we can keep the previous render
154
+ """
155
+ index = k1 .range (len (self .script .generators ))
156
+ percent = round (k2 .percent () * 100 )
157
+ dirty = (
158
+ self .generator_index != index or
159
+ self .k2_percent != percent
160
+ )
161
+ if dirty :
162
+ self .generator_index = index
163
+ self .k2_percent = percent
164
+
165
+ return dirty or self .first_render
144
166
145
167
def draw (self ):
146
- generator_index = k1 . range ( len ( self . script . generators ))
147
- g = self .script .generators [generator_index ]
168
+ self . first_render = False
169
+ g = self .script .generators [self . generator_index ]
148
170
pattern_str = str (g )
149
171
150
- ssoled .fill (0 )
151
- ssoled .text (f"-- CV { generator_index + 1 } --" , 0 , 0 )
172
+ oled .fill (0 )
173
+ oled .text (f"-- CV { self . generator_index + 1 } --" , 0 , 0 )
152
174
if len (pattern_str ) > 16 :
153
175
pattern_row1 = pattern_str [0 :16 ]
154
176
pattern_row2 = pattern_str [16 :]
155
- ssoled .text (f"{ pattern_row1 } " , 0 , 10 )
156
- ssoled .text (f"{ pattern_row2 } " , 0 , 20 )
177
+ oled .text (f"{ pattern_row1 } " , 0 , 10 )
178
+ oled .text (f"{ pattern_row2 } " , 0 , 20 )
157
179
else :
158
- ssoled .text (f"{ pattern_str } " , 0 , 10 )
180
+ oled .text (f"{ pattern_str } " , 0 , 10 )
159
181
160
- ssoled .show ()
182
+ oled .show ()
161
183
162
184
class SettingsMenu :
163
185
"""A menu screen for controlling a single setting of the generator
@@ -181,6 +203,15 @@ def __init__(self, script):
181
203
"Skip %"
182
204
]
183
205
206
+ self .first_render = True
207
+
208
+ self .menu_item = None
209
+ self .lower_bound = None
210
+ self .upper_bound = None
211
+ self .current_setting = None
212
+ self .new_setting = None
213
+ self .read_knobs ()
214
+
184
215
def set_generator (self , g ):
185
216
"""Configure this menu to control a given EuclideanGenerator
186
217
@@ -191,9 +222,8 @@ def set_generator(self, g):
191
222
def read_knobs (self ):
192
223
"""Returns a tuple with the current options
193
224
194
- @return a tuple of the form (menu_item, lower_bound, upper_bound, current_setting, new_setting)
225
+ @return True if the user has moved any inputs, requiring a re-render. Otherwise False
195
226
"""
196
-
197
227
menu_item = k1 .range (len (self .menu_items ))
198
228
lower_bound = 0
199
229
upper_bound = 0
@@ -218,36 +248,49 @@ def read_knobs(self):
218
248
219
249
new_setting = k2 .range (upper_bound - lower_bound + 1 ) + lower_bound
220
250
221
- return (menu_item , lower_bound , upper_bound , current_setting , new_setting )
251
+ dirty = (
252
+ self .menu_item != menu_item or
253
+ self .lower_bound != lower_bound or
254
+ self .upper_bound != upper_bound or
255
+ self .current_setting != current_setting or
256
+ self .new_setting != new_setting
257
+ )
222
258
223
- def draw (self ):
224
- (menu_item , lower_bound , upper_bound , current_setting , new_setting ) = self .read_knobs ()
259
+ if dirty :
260
+ self .menu_item = menu_item
261
+ self .lower_bound = lower_bound
262
+ self .upper_bound = upper_bound
263
+ self .current_setting = current_setting
264
+ self .new_setting = new_setting
265
+
266
+ return dirty or self .first_render
225
267
226
- ssoled .fill (0 )
227
- ssoled .text (f"-- { self .menu_items [menu_item ]} --" , 0 , 0 )
228
- ssoled .text (f"{ current_setting } <- { new_setting } " , 0 , 10 )
229
- ssoled .show ()
268
+ def draw (self ):
269
+ self .first_render = False
270
+ oled .fill (0 )
271
+ oled .text (f"-- { self .menu_items [self .menu_item ]} --" , 0 , 0 )
272
+ oled .text (f"{ self .current_setting } <- { self .new_setting } " , 0 , 10 )
273
+ oled .show ()
230
274
231
275
def apply_setting (self ):
232
276
"""Apply the current setting
233
277
"""
234
- (menu_item , lower_bound , upper_bound , current_setting , new_setting ) = self .read_knobs ()
235
-
236
- if menu_item == self .MENU_ITEMS_STEPS :
237
- self .generator .steps = new_setting
238
- if self .generator .pulses > new_setting :
239
- self .generator .pulses = new_setting
240
- if self .generator .rotation > new_setting :
241
- self .generator .rotation = new_setting
242
- elif menu_item == self .MENU_ITEMS_PULSES :
243
- self .generator .pulses = new_setting
244
- elif menu_item == self .MENU_ITEMS_ROTATION :
245
- self .generator .rotation = new_setting
246
- elif menu_item == self .MENU_ITEMS_SKIP :
247
- self .generator .skip = new_setting / 100.0
278
+ if self .menu_item == self .MENU_ITEMS_STEPS :
279
+ self .generator .steps = self .new_setting
280
+ if self .generator .pulses > self .new_setting :
281
+ self .generator .pulses = self .new_setting
282
+ if self .generator .rotation > self .new_setting :
283
+ self .generator .rotation = self .new_setting
284
+ elif self .menu_item == self .MENU_ITEMS_PULSES :
285
+ self .generator .pulses = self .new_setting
286
+ elif self .menu_item == self .MENU_ITEMS_ROTATION :
287
+ self .generator .rotation = self .new_setting
288
+ elif self .menu_item == self .MENU_ITEMS_SKIP :
289
+ self .generator .skip = self .new_setting / 100.0
248
290
249
291
self .generator .regenerate ()
250
292
293
+
251
294
class EuclideanRhythms (EuroPiScript ):
252
295
"""Generates 6 different Euclidean rhythms, one per output
253
296
@@ -272,8 +315,17 @@ def __init__(self):
272
315
273
316
self .load ()
274
317
318
+ # Do we need to save the current settings?
319
+ self .settings_dirty = False
320
+
321
+ # Do we need to re-draw the GUI?
322
+ self .ui_dirty = True
323
+
324
+ self .last_user_interaction_at = time .ticks_ms ()
325
+
275
326
self .channel_menu = ChannelMenu (self )
276
327
self .settings_menu = SettingsMenu (self )
328
+ self .screensaver = Screensaver ()
277
329
278
330
self .active_screen = self .channel_menu
279
331
@@ -285,33 +337,35 @@ def on_rising_clock():
285
337
"""
286
338
for g in self .generators :
287
339
g .advance ()
340
+ self .ui_dirty = True
288
341
289
342
@din .handler_falling
290
343
def on_falling_clock ():
291
344
"""Handler for the falling edge of the input clock
292
345
293
346
Turn off all of the CVs so we don't stay on for adjacent pulses
294
347
"""
295
- for cv in cvs :
296
- cv .off ()
348
+ turn_off_all_cvs ()
297
349
298
350
@b1 .handler
299
351
def on_b1_press ():
300
352
"""Handler for pressing button 1
301
353
"""
302
- ssoled .notify_user_interaction ()
354
+ self .ui_dirty = True
355
+ self .last_user_interaction_at = time .ticks_ms ()
303
356
304
357
if self .active_screen == self .channel_menu :
305
358
self .activate_settings_menu ()
306
359
else :
307
360
self .settings_menu .apply_setting ()
308
- self .save ()
361
+ self .settings_dirty = True
309
362
310
363
@b2 .handler
311
364
def on_b2_press ():
312
365
"""Handler for pressing button 2
313
366
"""
314
- ssoled .notify_user_interaction ()
367
+ self .ui_dirty = True
368
+ self .last_user_interaction_at = time .ticks_ms ()
315
369
316
370
if self .active_screen == self .channel_menu :
317
371
self .activate_settings_menu ()
@@ -371,7 +425,22 @@ def save(self):
371
425
372
426
def main (self ):
373
427
while True :
374
- self .active_screen .draw ()
428
+ now = time .ticks_ms ()
429
+
430
+ if self .settings_dirty :
431
+ self .settings_dirty = False
432
+ self .save ()
433
+
434
+ if self .active_screen .read_knobs ():
435
+ self .last_user_interaction_at = now
436
+ self .ui_dirty = True
437
+
438
+ if time .ticks_diff (now , self .last_user_interaction_at ) >= self .screensaver .ACTIVATE_TIMEOUT_MS :
439
+ self .last_user_interaction_at = time .ticks_add (now , - self .screensaver .ACTIVATE_TIMEOUT_MS )
440
+ self .screensaver .draw ()
441
+ elif self .ui_dirty :
442
+ self .ui_dirty = False
443
+ self .active_screen .draw ()
375
444
376
445
if __name__ == "__main__" :
377
446
EuclideanRhythms ().main ()
0 commit comments