Skip to content
Snippets Groups Projects
Commit 862bc387 authored by moon2's avatar moon2 :speech_balloon:
Browse files

new app: wobbler

parent 9f764ab1
No related branches found
No related tags found
1 merge request!717docs: bl00mbox
Pipeline #13184 passed
from st3m.application import Application
import math, cmath
import bl00mbox
import captouch
import leds
from st3m.ui import widgets, colours
class Wobbler(Application):
def __init__(self, app_ctx):
super().__init__(app_ctx)
self.blm = None
self.any_playing = False
self.tilt_ref = None
self.tilt = None
self.pitch = -36
def on_enter(self, vm):
super().on_enter(vm)
if self.blm is not None:
try:
self.blm.foreground = True
except ReferenceError:
self.blm = None
if self.blm is None:
self.build_synth()
leds.set_slew_rate(min(160, leds.get_slew_rate()))
def on_exit(self):
super().on_exit()
if self.any_playing:
self.blm.background_mute_override = True
else:
self.blm.delete()
self.blm = None
def build_synth(self):
self.blm = bl00mbox.Channel("wobbler")
self.blm.gain_dB = 0
self.mixer = self.blm.new(bl00mbox.plugins.mixer, 2)
self.mixer.signals.gain.mult = 8
self.filter = self.blm.new(bl00mbox.plugins.filter)
self.filter.signals.gain.mult = 0.2
self.filter.signals.reso.value = 22000
self.env = self.blm.new(bl00mbox.plugins.env_adsr)
self.filter.signals.input << self.mixer.signals.output
self.env.signals.input << self.filter.signals.output
self.blm.signals.line_out << self.env.signals.output
self.oscs = [self.blm.new(bl00mbox.plugins.osc) for x in range(2)]
for x, osc in enumerate(self.oscs):
osc.signals.output >> self.mixer.signals.input[x]
osc.signals.waveform.switch.SAW = True
# background widget, doesn't do normal think/on_enter/on_exit
self.tilt_widget = widgets.Inclinometer(buffer_len=2)
self.tilt_widget.on_enter()
def synth_callback(ins, delta_ms):
self.tilt_widget.think(ins, delta_ms)
roll = self.tilt_widget.roll
if roll is not None:
tilt = complex(roll, self.tilt_widget.pitch)
if self.tilt_ref is None:
self.tilt_ref = tilt
else:
tilt = tilt - self.tilt_ref
tilt *= 1.6
abs_tilt = abs(tilt)
if abs_tilt > 1:
tilt /= abs_tilt
self.tilt = tilt
self.filter.signals.cutoff.tone = self.pitch + (2 - tilt.imag) * 20
self.oscs[0].signals.pitch.tone = self.pitch + tilt.real * 2
self.oscs[1].signals.pitch.tone = self.pitch - tilt.real * 2
self.blm.callback = synth_callback
def think(self, ins, delta_ms):
super().think(ins, delta_ms)
if self.input.buttons.app.middle.pressed:
roll = self.tilt_widget.roll
if roll is not None:
tilt = complex(roll, self.tilt_widget.pitch)
self.tilt_ref = tilt
any_playing = False
pos = 0
pos_div = 0
for x in range(0, 10, 2):
if pressed := ins.captouch.petals[x].pressed:
any_playing = True
pos += ins.captouch.petals[x].pos.real
pos_div += 1
"""
if x == 2:
for x, osc in enumerate(self.oscs):
if pressed:
osc.signals.waveform.switch.SAW = True
else:
osc.signals.waveform.switch.TRI = True
elif x == 8:
self.mixer.signals.gain.mult = 8 if pressed else 2
"""
if pos_div:
self.pitch = -44 + (pos / pos_div + 1) * 6
if self.any_playing != any_playing:
if any_playing:
self.env.signals.trigger.start()
else:
self.env.signals.trigger.stop()
self.any_playing = any_playing
def get_help(self):
ret = (
"Press any top petal to play the note. How far away from "
"the center you press controls pitch.\n\n"
"Tilt forward/backward to change filter cutoff and left/right "
"to detune the oscillators. Press app button down to zero the "
"tilt reference.\n\n"
"If you exit while playing the note it will continue playing "
"in the background while still responding to tilt."
)
return ret
def draw(self, ctx):
ctx.gray(0).rectangle(-120, -120, 240, 240).fill()
col = (1, 1, 1)
eye_pos = complex(0, 0)
hue = 0
tilt = self.tilt_widget.pitch
if self.tilt is not None:
eye_pos = self.tilt * 10
hue = (eye_pos.real + eye_pos.imag) % math.tau
eye_pos *= 5
ctx.radial_gradient(
eye_pos.real, eye_pos.imag, 0, eye_pos.real, eye_pos.imag, 100
)
for x in range(5):
rel = x / 4
nval = rel
if self.any_playing:
nval /= 3
col = colours.hsv_to_rgb(hue + math.tau * rel, 1 - rel / 2, 1 - nval)
ctx.add_stop(rel, col, 1)
for y in range(8):
leds.set_rgb(y * 5 + x, *col)
leds.update()
length = 100
openness = 60
ctx.line_width = 1
ctx.move_to(length, 0).quad_to(0, openness, -length, 0).stroke()
ctx.move_to(length, 0).quad_to(0, -openness, -length, 0).stroke()
for x in range(3):
lash_length = 0.3 if x == 1 else 0.2
t = (x + 1) / 4
nt = 1 - t
# nt^2 * p0 + 2 * nt * t * p1 + t^2 * P2
lash_start_x = (nt * nt - t * t) * length
lash_start_y = t * nt * openness * 2
# 2 * (nt - t) * p1 - 2 * nt * p0 + 2 * t * p2
lash_angle_x = 2 * length
lash_angle_y = 2 * (nt - t) * openness
ctx.move_to(lash_start_x, lash_start_y)
ctx.rel_line_to(
lash_angle_y * lash_length, lash_angle_x * lash_length
).stroke()
ctx.arc(eye_pos.real, eye_pos.imag, 10, 0, math.tau, 0).stroke()
ctx.arc(eye_pos.real, eye_pos.imag, 20, 0, math.tau, 0).stroke()
if __name__ == "__main__":
import st3m.run
st3m.run.run_app(Wobbler)
[app]
name = "wobbler"
category = "Music"
[entry]
class = "Wobbler"
[metadata]
author = "Flow3r Badge Authors"
license = "LGPL-3.0-only"
url = "https://git.flow3r.garden/flow3r/flow3r-firmware"
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment