-
Notifications
You must be signed in to change notification settings - Fork 89
/
Copy pathpiconacci.py
148 lines (126 loc) · 4.76 KB
/
piconacci.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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# Copyright 2022 Allen Synthesis
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from europi import *
import machine
from time import ticks_diff, ticks_ms
from random import randint, uniform, choice
from europi_script import EuroPiScript
"""
Piconacci
author: Sean Bechhofer (github.com/seanbechhofer)
date: 2022-05
labels: triggers, maths
Triggers based on the Fibonacci sequence
digital_in: clock in
analog_in:
knob_1:
knob_2:
button_1: Short Press: Move window on sequence left. Long Press: Rotate values left
button_2: Short Press: Move window on sequence to the right. Long Press: Rotate values right
output_1: trigger
output_2: trigger
output_3: trigger
output_4: trigger
output_5: trigger
output_6: trigger
"""
# This probably works for most sensible circumstances. The 50th value
# (disregarding 0, 1) is 20,365,011,074. At a BPM of 120, that'll be a
# trigger every 300 years which is quite a lot even for slow moving
# ambient pieces,
MAX_FIB = 50
class Piconacci(EuroPiScript):
def __init__(self):
# Flag to indicate whether the display needs updating.
self.display_update_required = True
# offset determines the sublist to be used for the values.
self.offset = 0
# rotate adjusts how the values are mapped to the cv
# outputs. A value of 0 means that cv1 is controlled by the
# first, cv2 the second etc. A rotate value of 1 means that
# cv2 is controlled by the first, cv3 the second, and cv1 the
# last.
self.rotate = 0
# Overclock the Pico for improved performance.
machine.freq(250_000_000)
# List of fibonacci numbers.
self.fib = [1, 1]
for i in range(0, MAX_FIB):
self.fib.append(self.fib[-2] + self.fib[-1])
# Strip off first 1
self.fib = self.fib[1:]
self.steps = [0] * 6
@b1.handler_falling
def b1Pressed():
if ticks_diff(ticks_ms(), b1.last_pressed()) > 300:
# Rotate values left
self.rotate = (self.rotate - 1) % 6
else:
# Shift all the values left in the sequence
if self.offset > 0:
self.offset -= 1
# State has changed
self.display_update_required = True
@b2.handler_falling
def b2Pressed():
if ticks_diff(ticks_ms(), b2.last_pressed()) > 300:
# Rotate values right
self.rotate = (self.rotate + 1) % 6
else:
# Shift all the values right in the sequence
if self.offset < MAX_FIB - 7:
self.offset += 1
# State has changed
self.display_update_required = True
# Triggered on each clock into digital input. Output triggers.
@din.handler
def clockTrigger():
for tr in range(0, 6):
self.steps[tr] += 1
if self.steps[tr] >= self.value(tr):
# Trigger on tr
cvs[tr].on()
# If the values change due to shift or rotation,
# we may get slightly odd behaviour here,
# particularly if the values reduce.
self.steps[tr] = 0
@din.handler_falling
def clockTriggerEnd():
for cv in cvs:
cv.off()
def value(self, index):
# Return a value from the series, taking into account offset and rotation
return self.fib[self.offset : self.offset + 6][(index + self.rotate) % 6]
def main(self):
# Reset all outputs
turn_off_all_cvs()
while True:
# If the state has changed, update display
if self.display_update_required:
self.updateScreen()
self.display_update_required = False
def updateScreen(self):
oled.fill(0)
# Show the values.
oled.text("Piconacci", 28, 0, 1)
oled.text(str(self.value(0)), 10, 12, 1)
oled.text(str(self.value(1)), 50, 12, 1)
oled.text(str(self.value(2)), 90, 12, 1)
oled.text(str(self.value(3)), 10, 24, 1)
oled.text(str(self.value(4)), 50, 24, 1)
oled.text(str(self.value(5)), 90, 24, 1)
oled.show()
if __name__ == "__main__":
pc = Piconacci()
pc.main()