# Copyright 2024 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.
Binary tree based looping gate sequencer
from europi_script import EuroPiScript
from framebuf import FrameBuffer, MONO_HLSB
from random import random as rnd
class Dcsn2(EuroPiScript):
loop_image = FrameBuffer(bytearray(b'\x1cZ\x81\x81\x81\x81Z8'), CHAR_WIDTH, CHAR_HEIGHT, MONO_HLSB)
self.unhandled_clock = False
# initialize random pattern
for i in range(self.MAX_LENGTH):
self.pattern.append(self.choose_random())
self.unhandled_clock = True
def regenerate_pattern():
for i in range(self.MAX_LENGTH):
self.pattern[i] = self.choose_random()
din.handler(on_clock_rise)
din.handler_falling(on_clock_fall)
b1.handler(on_clock_rise)
b1.handler_falling(on_clock_fall)
b2.handler(regenerate_pattern)
def calculate_randomness(self):
"""Combine AIN & K2 to determine the probability that the pattern loops
# this will be in the range [0, 2]
randomness = self.randomness_cv.percent() + self.randomness_knob.percent()
randomness = 2.0 - randomness
Pick a random gate for the output pattern
0: child 1, grandchild 1-1
1: child 1, grandchild 1-2
2: child 2, grandchild 2-1
3: child 2, grandchild 2-2
self.children[g >> 1].on()
self.grandchildren[g >> 1][g & 1].on()
def draw(self, pattern_length, loop_prob):
active_child = self.pattern[0] >> 1
active_grandchild = self.pattern[0] & 1
# draw the tree with lines & circles
oled.ellipse(OLED_WIDTH//2, 5, 4, 4, 1, True) # root, always filled
oled.ellipse(OLED_WIDTH//4, OLED_HEIGHT//2, 4, 4, 1, active_child == 0)
oled.ellipse(3*OLED_WIDTH//4, OLED_HEIGHT//2, 4, 4, 1, active_child != 0)
oled.ellipse(6, OLED_HEIGHT-5, 4, 4, 1, active_child == 0 and active_grandchild == 0)
oled.ellipse(OLED_WIDTH//2-6, OLED_HEIGHT-5, 4, 4, 1, active_child == 0 and active_grandchild != 0)
oled.ellipse(OLED_WIDTH//2+6, OLED_HEIGHT-5, 4, 4, 1, active_child != 0 and active_grandchild == 0)
oled.ellipse(OLED_WIDTH-6, OLED_HEIGHT-5, 4, 4, 1, active_child != 0 and active_grandchild != 0)
oled.line(OLED_WIDTH//2, 5, OLED_WIDTH//4, OLED_HEIGHT//2, 1)
if active_grandchild == 0:
oled.line(OLED_WIDTH//4, OLED_HEIGHT//2, 6, OLED_HEIGHT-5, 1)
oled.line(OLED_WIDTH//4, OLED_HEIGHT//2, OLED_WIDTH//2-6, OLED_HEIGHT-5, 1)
oled.line(OLED_WIDTH//2, 5, 3*OLED_WIDTH//4, OLED_HEIGHT//2, 1)
if active_grandchild == 0:
oled.line(3*OLED_WIDTH//4, OLED_HEIGHT//2, OLED_WIDTH//2+6, OLED_HEIGHT-5, 1)
oled.line(3*OLED_WIDTH//4, OLED_HEIGHT//2, OLED_WIDTH-6, OLED_HEIGHT-5, 1)
oled.text(f"{pattern_length}", 0, 0, 1)
s = f"{round(loop_prob * 100)}"
oled.blit(self.loop_image, OLED_WIDTH - CHAR_WIDTH * (len(s)+1) - 1, 0)
oled.text(s, OLED_WIDTH - len(s) * CHAR_WIDTH, 0, 1)
oled.text(f"{self.pattern[0]}", OLED_WIDTH//2-CHAR_WIDTH//2, OLED_HEIGHT//2 - CHAR_HEIGHT//2, 1)
loop_prob = 1.0 - self.calculate_randomness() # 0 -> random, 1 -> loop
pattern_length = round(self.length_knob.percent() * (self.MAX_LENGTH - self.MIN_LENGTH) + self.MIN_LENGTH)
self.unhandled_clock = False
# shift the pattern over 1 step, introducing randomness as needed
tmp = self.pattern[pattern_length - 1]
tmp = self.choose_random()
for i in range(pattern_length - 1):
self.pattern[pattern_length-1-i] = self.pattern[pattern_length-2-i]
self.draw(pattern_length, loop_prob)
if __name__ == "__main__":