forked from Allen-Synthesis/EuroPi
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcoin_toss.py
123 lines (103 loc) · 4.09 KB
/
coin_toss.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
from europi import *
from random import random
from time import sleep_ms, ticks_ms, ticks_add, ticks_diff
import machine
from europi_script import EuroPiScript
# Internal clock tempo range.
MAX_BPM = 280
MIN_BPM = 20
# Constant values for display.
FRAME_WIDTH = int(OLED_WIDTH / 8)
class CoinToss(EuroPiScript):
def __init__(self):
self.gate_mode = True
self.internal_clock = True
self._prev_clock = 0
self._tempo = 0
self._deadline = 0
@b1.handler
def toggle_clock():
"""Toggle between internal clock and external clock from digital in."""
self.internal_clock = not self.internal_clock
@b2.handler
def toggle_gate():
"""Toggle between gate and trigger mode."""
self.gate_mode = not self.gate_mode
turn_off_all_cvs()
def tempo(self):
"""Read the current tempo set by k1 within set range."""
return round(k1.read_position(MAX_BPM - MIN_BPM) + MIN_BPM)
def get_next_deadline(self):
"""Get the deadline for next clock tick whole note."""
# The duration of a quarter note in ms for the current tempo.
wait_ms = int(((60 / self.tempo()) / 4) * 1000)
return ticks_add(ticks_ms(), wait_ms)
def wait(self):
"""Pause script execution waiting for next quarter note in the clock cycle."""
if self.internal_clock:
while True:
if ticks_diff(self._deadline, ticks_ms()) <= 0:
self._deadline = self.get_next_deadline()
return
else: # External clock
# Loop until digital in goes high (clock pulse received).
while not self.internal_clock:
if din.value() != self._prev_clock:
# We've detected a new clock value.
self._prev_clock = 1 if self._prev_clock == 0 else 0
# If the previous value was just set to 1 then we are seeing
# a high value for the first time, break wait and return.
if self._prev_clock == 1:
return
def toss(self, a, b, draw=True):
"""If random value is below trigger a, otherwise trigger b.
If draw is true, then display visualization of the coin toss.
"""
coin = random()
# Sum the knob2 and analogue input values to determine threshold.
read_sum = k2.percent() + ain.read_voltage()/12
self.threshold = clamp(read_sum, 0, 1)
if self.gate_mode:
a.value(coin < self.threshold)
b.value(coin > self.threshold)
else:
(a if coin < self.threshold else b).on()
if not draw:
return
# Draw gate/trigger display graphics for coin toss
h = int(self.threshold * OLED_HEIGHT)
tick = FRAME_WIDTH if self.gate_mode else 1
offset = 8 # The amount of negative space before drawing gate.
if coin < self.threshold:
oled.fill_rect(offset, 0, tick, h, 1)
else:
oled.fill_rect(offset, h, tick, OLED_HEIGHT, 1)
def main(self):
# Start the main loop.
counter = 0
while True:
# Scroll and clear new screen area.
oled.scroll(FRAME_WIDTH, 0)
oled.fill_rect(0, 0, FRAME_WIDTH, OLED_HEIGHT, 0)
self.toss(cv1, cv2)
cv3.on() # First column clock trigger
if counter % 4 == 0:
self.toss(cv4, cv5, False)
cv6.on() # Second column clock trigger (1/4x speed)
sleep_ms(10)
if self.gate_mode:
# Only turn off clock triggers.
cv3.off()
cv6.off()
else:
# Turn of all cvs in trigger mode.
turn_off_all_cvs()
# Draw threshold line
oled.hline(0, int(self.threshold * OLED_HEIGHT), FRAME_WIDTH, 1)
oled.show()
counter += 1
self.wait()
if __name__ == '__main__':
# Reset module display state.
coin_toss = CoinToss()
coin_toss.main()