From 34b2dc85ba5dc178c6cd5f0a1e6852fd7da85d43 Mon Sep 17 00:00:00 2001
From: moon2 <moon2protonmail@protonmail.com>
Date: Fri, 11 Aug 2023 23:07:34 +0000
Subject: [PATCH] bl00mbox: api refactor (mypy help from dx)
---
docs/badge/bl00mbox.rst | 73 +++--
python_payload/apps/demo_harmonic/__init__.py | 23 +-
python_payload/apps/demo_melodic/__init__.py | 10 +-
python_payload/apps/otamatone/__init__.py | 9 +-
python_payload/apps/shoegaze/__init__.py | 12 +-
python_payload/apps/simple_drums/__init__.py | 26 +-
python_payload/bl00mbox/_patches.py | 251 ++++++++----------
python_payload/bl00mbox/_user.py | 141 +++++-----
.../mypystubs/bl00mbox/_patches.pyi | 47 ++--
python_payload/mypystubs/bl00mbox/_user.pyi | 10 +-
10 files changed, 290 insertions(+), 312 deletions(-)
diff --git a/docs/badge/bl00mbox.rst b/docs/badge/bl00mbox.rst
index 9a6568bc86..20a820d5d6 100644
--- a/docs/badge/bl00mbox.rst
+++ b/docs/badge/bl00mbox.rst
@@ -11,13 +11,9 @@ Upcoming features
(in no specific order)
-1) Expose hardware such as captouch and IMU as pseudo-signals that buds can subscribe to. This frees the repl for parameter manipulation while the backend takes care of the playing surface, neat for live coding.
+1) Expose hardware such as captouch and IMU as pseudo-signals that plugins can subscribe to. This frees the repl for parameter manipulation while the backend takes care of the playing surface, neat for live coding.
-2) Cross-channel connections
-
-3) Stepped value naming
-
-4) Better signal/bud representation in patches
+2) Stepped value naming
Patches
-------------
@@ -35,8 +31,7 @@ with a channel instance, but you can also spawn one directly:
>>> blm.volume = 5000
The easiest way to get sound is to use patches. These are "macroscopic" units
-that directly connect to the output and provide a compact UI. Here's how to
-create one:
+and can often be used without much thinking:
.. code-block:: pycon
@@ -44,15 +39,17 @@ create one:
>>> bl00mbox.patches.
# create a patch instance
>>> tiny = blm.new(bl00mbox.patches.tinysynth_fm)
+ # connect sound output to mixer of the channel
+ >>> tiny.signals.output = blm.mixer
# play it!
- >>> tiny.start()
+ >>> tiny.signals.trigger.start()
# try autocomplete here too!
>>> tiny.
# patches come with very individual parameters!
- >>> tiny.fm_waveform(tiny.SAW)
- >>> tiny.start()
+ >>> tiny.signals.waveform = 0
+ >>> tiny.signals.trigger.start()
-Buds
+Plugins
----------
We can inspect the patch we created earlier:
@@ -61,13 +58,13 @@ We can inspect the patch we created earlier:
>>> tiny
[patch] tinysynth_fm
- [bud 32] osc_fm
- output [output]: 0 => input in [bud 34] ampliverter
+ [plugin 32] osc_fm
+ output [output]: 0 => input in [plugin 34] ampliverter
pitch [input/pitch]: 18367 / 0.0 semitones / 440.0Hz
waveform [input]: -1
- lin_fm [input]: 0 <= output in [bud 35] osc_fm
- [bud 33] env_adsr
- output [output]: 0 => gain in [bud 34] ampliverter
+ lin_fm [input]: 0 <= output in [plugin 35] osc_fm
+ [plugin 33] env_adsr
+ output [output]: 0 => gain in [plugin 34] ampliverter
phase [output]: 0
input [input]: 32767
trigger [input/trigger]: 0
@@ -76,20 +73,20 @@ We can inspect the patch we created earlier:
sustain [ms] [input]: 0
release [input]: 100
gate [input]: 0
- [bud 34] ampliverter
+ [plugin 34] ampliverter
output [output]: 0 ==> [channel mixer]
- input [input]: 0 <= output in [bud 32] osc_fm
- gain [input]: 0 <= output in [bud 33] env_adsr
+ input [input]: 0 <= output in [plugin 32] osc_fm
+ gain [input]: 0 <= output in [plugin 33] env_adsr
bias [input]: 0
- [bud 35] osc_fm
- output [output]: 0 => lin_fm in [bud 32] osc_fm
+ [plugin 35] osc_fm
+ output [output]: 0 => lin_fm in [plugin 32] osc_fm
pitch [input/pitch]: 21539 / 15.86 semitones / 1099.801Hz
waveform [input]: 1
lin_fm [input]: 0
-The patch is actually composed of buds! Buds are wrappers that contain atomic plugins. Each
-plugin is composed of signals that can be connected to other signals. Signals can have different
+The patch is actually composed of plugins and connections! Plugins are atomic signal processing
+units. Each plugin has signals that can be connected to other signals. Signals can have different
properties that are listed behind their name in square brackets. For starters, each signal is
either an input or output. Connections always happen between an input and an output. Outputs
can fan out to multiple inputs, but inputs can only receive data from a single output. If no
@@ -99,7 +96,7 @@ output is connected to an input, it has a static value.
A special case is the channel mixer (an [input] signal) which only fakes
being a bl00mbox signal and can accept multiple outputs.
-Let's play around with that a bit more and create some fresh unbothered buds:
+Let's play around with that a bit more and create some fresh unbothered plugins:
.. code-block:: pycon
@@ -107,29 +104,29 @@ Let's play around with that a bit more and create some fresh unbothered buds:
>>> bl00mbox.plugins.
# print details about specific plugin
>>> bl00mbox.plugins.ampliverter
- # create a new bud
+ # create a new plugin
>>> osc = blm.new(bl00mbox.plugins.osc_fm)
>>> env = blm.new(bl00mbox.plugins.env_adsr)
-You can inspect properties of the new buds just as with a patch - in fact, many patches simply print
-all their contained buds and maybe some extra info (but that doesn't have to be the case and is up
+You can inspect properties of the new plugins just as with a patch - in fact, many patches simply print
+all their contained plugins and maybe some extra info (but that doesn't have to be the case and is up
to the patch designer).
.. note::
- As of now patch designers can hide buds within the internal structure however they like and
+ As of now patch designers can hide plugins within the internal structure however they like and
you kind of have to know where to find stuff. We'll come up with a better solution soon!
.. code-block:: pycon
- # print general info about bud
+ # print general info about plugin
>>> osc
- [bud 36] osc_fm
+ [plugin 36] osc_fm
output [output]: 0
pitch [input/pitch]: 18367 / 0.0 semitones / 440.0Hz
waveform [input]: -16000
lin_fm [input]: 0
- # print info about a specific bud signal
+ # print info about a specific plugin signal
>>> env.signals.trigger
trigger [input/trigger]: 0
@@ -186,9 +183,9 @@ and we can get them individually:
>>> chan_one
[channel 1: shoegaze] (foreground)
volume: 3000
- buds: 18
+ plugins: 18
[channel mixer] (1 connections)
- output in [bud 1] lowpass
+ output in [plugin 1] lowpass
We have accidentially grabbed the channel used by the shoegaze application! Each application
should have its own channel(s), so in order to get a free one we'll request a free one from the
@@ -209,7 +206,7 @@ backend by skipping the number. We can also provide a name for a new channel ins
>>> chan_free
[channel 3: hewwo] (foreground)
volume: 3000
- buds: 0
+ plugins: 0
[channel mixer] (0 connections)
In case there's no free channel yet you get channel 31, the garbage channel. It behaves like
@@ -258,12 +255,12 @@ doing so:
What constitutes a channel interaction for auto channel foregrounding is a bit in motion at this point
and generally unreliable. For applications it is ideal to mark the channel manually when using it. When
-exiting, an application should free the channel with automatically clears all buds. A channel should
+exiting, an application should free the channel with automatically clears all plugins. A channel should
be no longer used after freeing:
.. code-block:: pycon
- # this clears all buds and sets the internal "free" marker to zero
+ # this clears all plugins and sets the internal "free" marker to zero
>>> chan_one.free = True
# good practice to not accidentially use a free channel
>>> chan_one = None
@@ -278,7 +275,7 @@ Some other misc channel operations for live coding mostly:
>>> bl00mbox.Channels.print_overview()
[channel 3: hewwo] (foreground)
volume: 3000
- buds: 0
+ plugins: 0
[channel mixer] (0 connections)
Example 1: Auto bassline
diff --git a/python_payload/apps/demo_harmonic/__init__.py b/python_payload/apps/demo_harmonic/__init__.py
index bf26c7b951..98402812b3 100644
--- a/python_payload/apps/demo_harmonic/__init__.py
+++ b/python_payload/apps/demo_harmonic/__init__.py
@@ -31,14 +31,15 @@ class HarmonicApp(Application):
self.cp_prev = captouch.read()
for i, synth in enumerate(self.synths):
- synth.decay(500)
- synth.waveform(-32767)
- synth.attack(50)
- synth.volume(0.3)
- synth.sustain(0.9)
- synth.release(800)
- synth.fm_waveform(-32767)
- synth.fm(1.5)
+ synth.signals.decay = 500
+ synth.signals.waveform = -32767
+ synth.signals.attack = 50
+ synth.signals.volume = 0.3 * 32767
+ synth.signals.sustain = 0.9 * 32767
+ synth.signals.release = 800
+ synth.signals.fm_waveform = -32767
+ synth.signals.output = blm.mixer
+ # synth.fm = 1.5
self._set_chord(3)
self.prev_captouch = [0] * 10
@@ -71,11 +72,11 @@ class HarmonicApp(Application):
self._set_chord(k)
else:
k = int(i / 2)
- self.synths[k].tone(self.chord[k])
- self.synths[k].start()
+ self.synths[k].signals.pitch.tone = self.chord[k]
+ self.synths[k].signals.trigger.start()
self.color_intensity = 1.0
elif (not cts.petals[i].pressed) and self.cp_prev.petals[i].pressed:
if (1 + i) % 2:
k = int(i / 2)
- self.synths[k].stop()
+ self.synths[k].signals.trigger.stop()
self.cp_prev = cts
diff --git a/python_payload/apps/demo_melodic/__init__.py b/python_payload/apps/demo_melodic/__init__.py
index 35fe150e16..6866af46cd 100644
--- a/python_payload/apps/demo_melodic/__init__.py
+++ b/python_payload/apps/demo_melodic/__init__.py
@@ -67,16 +67,18 @@ def run(ins: InputState) -> None:
k -= 10
k = 3 - k
note = scale[k] + 12 * octave
- synths[0].tone(note)
- synths[0].start()
+ synths[0].signals.pitch.tone = note
+ synths[0].signals.trigger.start()
def init() -> None:
global synths
for i in range(1):
- synths += [blm.new(bl00mbox.patches.tinysynth_fm)]
+ synth = blm.new(bl00mbox.patches.tinysynth_fm)
+ synth.signals.output = blm.mixer
+ synths += [synth]
for synth in synths:
- synth.decay(100)
+ synth.signals.decay = 100
def foreground() -> None:
diff --git a/python_payload/apps/otamatone/__init__.py b/python_payload/apps/otamatone/__init__.py
index 484b3e9e04..cb0ffeba0a 100644
--- a/python_payload/apps/otamatone/__init__.py
+++ b/python_payload/apps/otamatone/__init__.py
@@ -84,7 +84,8 @@ class Otamatone(Application):
self._blm = bl00mbox.Channel("Otamatone")
self._osc = self._blm.new(bl00mbox.patches.tinysynth)
- self._osc.waveform(2)
+ self._osc.signals.output = self._blm.mixer
+ self._osc.signals.waveform = 2
self._intensity = 0.0
self.input.captouch.petals[self.PETAL_NO].whole.repeat_disable()
@@ -123,12 +124,12 @@ class Otamatone(Application):
if petal.whole.down:
if self._intensity < 1.0:
self._intensity += 0.1 * (delta_ms / 20)
- self._osc.tone(ctrl * 12)
- self._osc.start()
+ self._osc.signals.pitch.tone = ctrl * 12
+ self._osc.signals.trigger.start()
if petal.whole.up:
self._intensity = 0
- self._osc.stop()
+ self._osc.signals.trigger.stop()
self._blob._yell = self._intensity * 0.8 + (ctrl + 1) * 0.1
diff --git a/python_payload/apps/shoegaze/__init__.py b/python_payload/apps/shoegaze/__init__.py
index 2cc92d184a..f625b651ce 100644
--- a/python_payload/apps/shoegaze/__init__.py
+++ b/python_payload/apps/shoegaze/__init__.py
@@ -65,13 +65,13 @@ class ShoegazeApp(Application):
self.git_mixer.signals.input2 = self.git_strings[2].signals.output
self.git_mixer.signals.input3 = self.git_strings[3].signals.output
self.git_mixer.signals.output = self.git_lp.signals.input
- self.git_fuzz.buds.dist.signals.input = self.git_lp.signals.output
+ self.git_fuzz.signals.input = self.git_lp.signals.output
self.bass_lp.signals.input = self.bass_string.signals.output
self.main_mixer.signals.input1 = self.bass_lp.signals.output
- self.main_fuzz.buds.dist.signals.input = self.main_mixer.signals.output
- self.main_fuzz.buds.dist.signals.output = self.main_lp.signals.input
+ self.main_fuzz.signals.input = self.main_mixer.signals.output
+ self.main_fuzz.signals.output = self.main_lp.signals.input
self.main_lp.signals.output = self.blm.mixer
self.git_delay.signals.time = 200
@@ -104,12 +104,12 @@ class ShoegazeApp(Application):
self.main_lp.signals.freq = 2500
self.main_lp.signals.gain = 2000
self.git_mixer.signals.gain = 4000
- self.main_lp.signals.input = self.main_fuzz.buds.dist.signals.output
+ self.main_lp.signals.input = self.main_fuzz.signals.output
if self.delay_on:
- self.git_delay.signals.input = self.git_fuzz.buds.dist.signals.output
+ self.git_delay.signals.input = self.git_fuzz.signals.output
self.main_mixer.signals.input0 = self.git_delay.signals.output
else:
- self.main_mixer.signals.input0 = self.git_fuzz.buds.dist.signals.output
+ self.main_mixer.signals.input0 = self.git_fuzz.signals.output
else:
self.bass_lp.signals.gain = 2000
self.git_lp.signals.gain = 2000
diff --git a/python_payload/apps/simple_drums/__init__.py b/python_payload/apps/simple_drums/__init__.py
index f7d722f4eb..b17027a918 100644
--- a/python_payload/apps/simple_drums/__init__.py
+++ b/python_payload/apps/simple_drums/__init__.py
@@ -48,17 +48,21 @@ class SimpleDrums(Application):
# Dot(20, 20, 0, 40, self._track_col(1)).draw(0,ctx)
self.snare = self.blm.new(bl00mbox.patches.sampler, "snare.wav")
# Dot(30, 30, 2, -20, self._track_col(2)).draw(0,ctx)
- self.kick.sampler.signals.trigger = self.seq.seqs[0].signals.output
- self.hat.sampler.signals.trigger = self.seq.seqs[1].signals.output
- self.snare.sampler.signals.trigger = self.seq.seqs[2].signals.output
+ self.kick.signals.output = self.blm.mixer
+ self.snare.signals.output = self.blm.mixer
+ self.hat.signals.output = self.blm.mixer
+ self.kick.signals.trigger = self.seq.plugins.sequencer0.signals.output
+ self.hat.signals.trigger = self.seq.plugins.sequencer1.signals.output
+ self.snare.signals.trigger = self.seq.plugins.sequencer2.signals.output
+ self.seq.signals.bpm.value = 80
+
self.track_names = ["kick", "hihat", "snare"]
self.track = 0
- self.seq.bpm = 80
self.blm.background_mute_override = True
self.tap_tempo_press_counter = 0
self.delta_acc = 0
self.stopped = False
- self.bpm = self.seq.bpm
+ self.bpm = self.seq.signals.bpm.value
# True if a given group should be highlighted, when a corresponding
# petal is pressed.
@@ -96,7 +100,7 @@ class SimpleDrums(Application):
(0.15, 0.15, 0.15),
)
)
- st = self.seq.seqs[0].signals.step.value
+ st = self.seq.signals.step.value
for track in range(3):
rgb = self._track_col(track)
@@ -143,7 +147,7 @@ class SimpleDrums(Application):
ctx.move_to(0, -65)
ctx.rgb(255, 255, 255)
- ctx.text(str(self.seq.bpm) + " bpm")
+ ctx.text(str(self.seq.signals.bpm) + " bpm")
ctx.font_size = 15
@@ -160,7 +164,7 @@ class SimpleDrums(Application):
def think(self, ins: InputState, delta_ms: int) -> None:
super().think(ins, delta_ms)
- st = self.seq.seqs[0].signals.step.value
+ st = self.seq.signals.step.value
rgb = self._track_col(self.track)
if st == 0:
leds.set_all_rgb(*[int(x / 4) for x in rgb])
@@ -183,19 +187,19 @@ class SimpleDrums(Application):
self.track = (self.track - 1) % 3
if petals[0].whole.pressed:
if self.stopped:
- self.seq.bpm = self.bpm
+ self.seq.signals.bpm = self.bpm
self.blm.background_mute_override = True
self.stopped = False
elif self.delta_acc < 3000 and self.delta_acc > 10:
bpm = int(60000 / self.delta_acc)
if bpm > 40 and bpm < 500:
- self.seq.bpm = bpm
+ self.seq.signals.bpm = bpm
self.bpm = bpm
self.delta_acc = 0
if petals[0].whole.down:
if self.tap_tempo_press_counter > 500:
- self.seq.bpm = 0
+ self.seq.signals.bpm = 0
self.stopped = True
self.blm.background_mute_override = False
else:
diff --git a/python_payload/bl00mbox/_patches.py b/python_payload/bl00mbox/_patches.py
index ac4142ae15..bf7fb5895b 100644
--- a/python_payload/bl00mbox/_patches.py
+++ b/python_payload/bl00mbox/_patches.py
@@ -10,124 +10,106 @@ except ImportError:
class _Patch:
- def bl00mbox_patch_marker(self):
- return True
+ def __init__(self, chan):
+ self.plugins = _PatchPluginList()
+ self.signals = _PatchSignalList()
+ self._channel = chan
+ def __repr__(self):
+ ret = "[patch] " + type(self).__name__
+ ret += "\n [signals:]\n " + "\n ".join(repr(self.signals).split("\n"))
+ ret += "\n [plugins:]\n " + "\n ".join(repr(self.plugins).split("\n"))
+ return ret
-class _PatchSignalList:
- pass
+class _PatchItemList:
+ def __init__(self):
+ self._items = []
-class _PatchBudList:
- pass
+ def __iter__(self):
+ return iter(self._items)
+ def __repr__(self):
+ return "\n".join([x + ": " + repr(getattr(self, x)) for x in self._items])
-class tinysynth(_Patch):
- def __init__(self, chan):
- self.channel = chan
- self.SINE = -32767
- self.TRI = -1
- self.SQUARE = 1
- self.SAW = 32767
-
- self.osc = chan._new_bud(420)
- self.env = chan._new_bud(42)
- self.amp = chan._new_bud(69)
- self.amp.signals.output.value = chan.mixer
- self.amp.signals.gain.value = self.env.signals.output
- self.amp.signals.input.value = self.osc.signals.output
- self.env.signals.sustain.value = 0
- self.env.signals.decay.value = 500
- self.release(100)
+ def __setattr__(self, key, value):
+ current_value = getattr(self, key, None)
- def __repr__(self):
- ret = "[patch] tinysynth"
- ret += "\n " + "\n ".join(repr(self.osc).split("\n"))
- ret += "\n " + "\n ".join(repr(self.env).split("\n"))
- ret += "\n " + "\n ".join(repr(self.amp).split("\n"))
- return ret
+ if current_value is None and not key.startswith("_"):
+ self._items.append(key)
- def tone(self, val):
- self.osc.signals.pitch.tone = val
+ super().__setattr__(key, value)
- def freq(self, val):
- self.osc.signals.pitch.freq = val
- def volume(self, val):
- self.env.signals.input.value = 32767 * val
+class _PatchSignalList(_PatchItemList):
+ def __setattr__(self, key, value):
+ current_value = getattr(self, key, None)
+ if isinstance(current_value, bl00mbox.Signal):
+ current_value.value = value
+ return
+ super().__setattr__(key, value)
- def start(self):
- self.env.signals.trigger.start()
- def stop(self):
- self.env.signals.trigger.stop()
+class _PatchPluginList(_PatchItemList):
+ pass
- def waveform(self, val):
- self.osc.signals.waveform.value = val
- def attack(self, val):
- self.env.signals.attack.value = val
+class tinysynth(_Patch):
+ def __init__(self, chan):
+ super().__init__(chan)
+ self.plugins.osc = self._channel.new(bl00mbox.plugins.osc_fm)
+ self.plugins.env = self._channel.new(bl00mbox.plugins.env_adsr)
+ self.plugins.amp = self._channel.new(bl00mbox.plugins.ampliverter)
- def decay(self, val):
- self.env.signals.decay.value = val
+ self.plugins.amp.signals.gain = self.plugins.env.signals.output
+ self.plugins.amp.signals.input = self.plugins.osc.signals.output
+ self.plugins.env.signals.decay = 500
- def sustain(self, val):
- self.env.signals.sustain.value = val * 32767
+ self.signals.output = self.plugins.amp.signals.output
+ self.signals.pitch = self.plugins.osc.signals.pitch
+ self.signals.waveform = self.plugins.osc.signals.waveform
- def release(self, val):
- self.env.signals.release.value = val
+ self.signals.trigger = self.plugins.env.signals.trigger
+ self.signals.attack = self.plugins.env.signals.attack
+ self.signals.sustain = self.plugins.env.signals.sustain
+ self.signals.decay = self.plugins.env.signals.decay
+ self.signals.release = self.plugins.env.signals.release
+ self.signals.volume = self.plugins.env.signals.input.value
+ self.signals.release = 100
class tinysynth_fm(tinysynth):
def __init__(self, chan):
- tinysynth.__init__(self, chan)
- self.mod_osc = chan._new_bud(420)
- self.fm_mult = 2.5
- self.mod_osc.signals.output.value = self.osc.signals.lin_fm
- self.decay(1000)
- self.attack(20)
- self._update_mod_osc()
- self.fm_waveform(self.SQUARE)
- self.waveform(self.TRI)
-
- def fm_waveform(self, val):
- self.mod_osc.signals.waveform.value = val
+ super().__init__(chan)
+ self.plugins.mod_osc = self._channel.new(bl00mbox.plugins.osc_fm)
+ self.plugins.mod_osc.signals.output = self.plugins.osc.signals.lin_fm
+ self.signals.fm_waveform = self.plugins.mod_osc.signals.waveform
+ self.signals.fm_pitch = self.plugins.mod_osc.signals.pitch
+ self.signals.decay = 1000
+ self.signals.attack = 20
+ self.signals.waveform = -1
+ self.signals.fm_waveform = 0
- def __repr__(self):
- ret = tinysynth.__repr__(self)
- ret = ret.split("\n")
- ret[0] += "_fm"
- ret = "\n".join(ret)
- ret += "\n " + "\n ".join(repr(self.mod_osc).split("\n"))
- return ret
-
- def fm(self, val):
- self.fm_mult = val
- self._update_mod_osc()
-
- def tone(self, val):
- self.osc.signals.pitch.tone = val
- self._update_mod_osc()
-
- def freq(self, val):
- self.osc.signals.pitch.freq = val
- self._update_mod_osc()
+ self.sync_mod_osc(2.5)
- def _update_mod_osc(self):
- self.mod_osc.signals.pitch.freq = self.fm_mult * self.osc.signals.pitch.freq
+ def sync_mod_osc(self, val):
+ self.signals.fm_pitch.freq = int(val) * self.signals.pitch.freq
class sampler(_Patch):
- """needs a wave file with path relative to samples/"""
+ """
+ needs a wave file with path relative to /flash/sys/samples/
+ """
def __init__(self, chan, filename):
+ super().__init__(chan)
if wave is None:
pass
# raise Bl00mboxError("wave library not found")
f = wave.open("/flash/sys/samples/" + filename, "r")
self.len_frames = f.getnframes()
- self.sampler = chan._new_bud(696969, self.len_frames)
+ self.plugins.sampler = chan._new_plugin(696969, self.len_frames)
assert f.getsampwidth() == 2
assert f.getnchannels() in (1, 2)
@@ -135,12 +117,12 @@ class sampler(_Patch):
if f.getnchannels() == 1:
# fast path for mono
- table = self.sampler.table_bytearray
+ table = self.plugins.sampler.table_bytearray
for i in range(0, self.len_frames * 2, 100):
table[i : i + 100] = f.readframes(50)
else:
# somewhat fast path for stereo
- table = self.sampler.table_int16_array
+ table = self.plugins.sampler.table_int16_array
for i in range(self.len_frames):
frame = f.readframes(1)
value = int.from_bytes(frame[0:2], "little")
@@ -148,13 +130,8 @@ class sampler(_Patch):
f.close()
self._filename = filename
- self.sampler.signals.output = chan.mixer
-
- def start(self):
- self.sampler.signals.trigger.start()
-
- def stop(self):
- self.sampler.signals.trigger.stop()
+ self.signals.trigger = self.plugins.sampler.signals.trigger
+ self.signals.output = self.plugins.sampler.signals.output
@property
def filename(self):
@@ -162,30 +139,42 @@ class sampler(_Patch):
class step_sequencer(_Patch):
- def __init__(self, chan):
+ def __init__(self, chan, num=4):
+ super().__init__(chan)
+ if num > 32:
+ num = 32
+ if num < 0:
+ num = 0
self.seqs = []
- for i in range(4):
- seq = chan._new_bud(56709)
+ prev_seq = None
+ for i in range(num):
+ seq = chan._new_plugin(56709)
seq.table = [-32767] + ([0] * 16)
- if len(self.seqs):
- self.seqs[-1].signals.sync_out = seq.signals.sync_in
+ if prev_seq is None:
+ self.signals.bpm = seq.signals.bpm
+ self.signals.beat_div = seq.signals.beat_div
+ self.signals.step = seq.signals.step
+ self.signals.step_len = seq.signals.step_len
+ else:
+ prev_seq.signals.sync_out = seq.signals.sync_in
+ prev_seq = seq
self.seqs += [seq]
- self._bpm = 120
+ setattr(self.plugins, "sequencer" + str(i), seq)
def __repr__(self):
ret = "[patch] step sequencer"
# ret += "\n " + "\n ".join(repr(self.seqs[0]).split("\n"))
ret += (
"\n bpm: "
- + str(self.seqs[0].signals.bpm.value)
+ + str(self.signals.bpm.value)
+ " @ 1/"
- + str(self.seqs[0].signals.beat_div.value)
+ + str(self.signals.beat_div.value)
)
ret += (
- "\n step: "
- + str(self.seqs[0].signals.step.value)
+ " step: "
+ + str(self.signals.step.value)
+ "/"
- + str(self.seqs[0].signals.step_len.value)
+ + str(self.signals.step_len.value)
)
ret += "\n [tracks]"
for x, seq in enumerate(self.seqs):
@@ -196,18 +185,9 @@ class step_sequencer(_Patch):
+ "".join(["X " if x > 0 else ". " for x in seq.table[1:]])
+ "]"
)
+ ret += "\n" + "\n".join(super().__repr__().split("\n")[1:])
return ret
- @property
- def bpm(self):
- return self._bpm
-
- @bpm.setter
- def bpm(self, bpm):
- for seq in self.seqs:
- seq.signals.bpm.value = bpm
- self._bpm = bpm
-
def trigger_start(self, track, step):
a = self.seqs[track].table
a[step + 1] = 32767
@@ -228,18 +208,13 @@ class step_sequencer(_Patch):
else:
self.trigger_stop(track, step)
- @property
- def step(self):
- return self.seqs[0].signals.step
-
class fuzz(_Patch):
def __init__(self, chan):
- self.buds = _PatchBudList()
- self.signals = _PatchSignalList()
- self.buds.dist = chan.new(bl00mbox.plugins.distortion)
- self.signals.input = self.buds.dist.signals.input
- self.signals.output = self.buds.dist.signals.output
+ super().__init__(chan)
+ self.plugins.dist = chan.new(bl00mbox.plugins.distortion)
+ self.signals.input = self.plugins.dist.signals.input
+ self.signals.output = self.plugins.dist.signals.output
self._intensity = 2
self._volume = 32767
self._gate = 0
@@ -287,29 +262,27 @@ class fuzz(_Patch):
gate = self.gate >> 9
for i in range(64 - gate, 64 + gate):
table[i] = 0
- self.buds.dist.table = table
+ self.plugins.dist.table = table
class karplus_strong(_Patch):
def __init__(self, chan):
- self.buds = _PatchBudList()
- self.signals = _PatchSignalList()
-
- self.buds.noise = chan._new_bud(bl00mbox.plugins.noise_burst)
- self.buds.noise.signals.length = 25
+ super().__init__(chan)
+ self.plugins.noise = chan._new_plugin(bl00mbox.plugins.noise_burst)
+ self.plugins.noise.signals.length = 25
- self.buds.flanger = chan._new_bud(bl00mbox.plugins.flanger)
+ self.plugins.flanger = chan._new_plugin(bl00mbox.plugins.flanger)
- self.buds.flanger.signals.resonance = 32500
- self.buds.flanger.signals.manual.tone = "A2"
+ self.plugins.flanger.signals.resonance = 32500
+ self.plugins.flanger.signals.manual.tone = "A2"
- self.buds.flanger.signals.input = self.buds.noise.signals.output
+ self.plugins.flanger.signals.input = self.plugins.noise.signals.output
- self.signals.trigger = self.buds.noise.signals.trigger
- self.signals.pitch = self.buds.flanger.signals.manual
- self.signals.output = self.buds.flanger.signals.output
+ self.signals.trigger = self.plugins.noise.signals.trigger
+ self.signals.pitch = self.plugins.flanger.signals.manual
+ self.signals.output = self.plugins.flanger.signals.output
- self.signals.level = self.buds.flanger.signals.level
+ self.signals.level = self.plugins.flanger.signals.level
self.decay = 1000
@property
@@ -318,9 +291,9 @@ class karplus_strong(_Patch):
@decay.setter
def decay(self, val):
- tone = self.buds.flanger.signals.manual.tone
+ tone = self.plugins.flanger.signals.manual.tone
loss = (50 * (2 ** (-tone / 12))) // (val / 1000)
if loss < 2:
loss = 2
- self.buds.flanger.signals.resonance = 32767 - loss
+ self.plugins.flanger.signals.resonance = 32767 - loss
self._decay = val
diff --git a/python_payload/bl00mbox/_user.py b/python_payload/bl00mbox/_user.py
index d78b9001e9..faee8855db 100644
--- a/python_payload/bl00mbox/_user.py
+++ b/python_payload/bl00mbox/_user.py
@@ -14,22 +14,22 @@ class Bl00mboxError(Exception):
pass
-def _makeSignal(bud, signal_num):
+def _makeSignal(plugin, signal_num):
hints = sys_bl00mbox.channel_bud_get_signal_hints(
- bud.channel_num, bud.bud_num, signal_num
+ plugin.channel_num, plugin.bud_num, signal_num
)
if hints & 2:
- signal = SignalOutput(bud, signal_num)
+ signal = SignalOutput(plugin, signal_num)
signal._hints = "output"
elif hints & 1:
if hints & 4:
- signal = SignalInputTrigger(bud, signal_num)
+ signal = SignalInputTrigger(plugin, signal_num)
signal._hints = "input/trigger"
elif hints & 32:
- signal = SignalInputPitch(bud, signal_num)
+ signal = SignalInputPitch(plugin, signal_num)
signal._hints = "input/pitch"
else:
- signal = SignalInput(bud, signal_num)
+ signal = SignalInput(plugin, signal_num)
signal._hints = "input"
return signal
@@ -44,7 +44,7 @@ class ChannelMixer:
ret += " (" + str(len(self.connections)) + " connections)"
for con in self.connections:
ret += "\n " + con.name
- ret += " in [bud " + str(con._bud.bud_num) + "] " + con._bud.name
+ ret += " in [plugin " + str(con._plugin.bud_num) + "] " + con._plugin.name
return ret
def _unplug_all(self):
@@ -61,33 +61,33 @@ class ChannelMixer:
s = sys_bl00mbox.channel_get_signal_by_mixer_list_pos(
self._channel.channel_num, i
)
- sig = Signal(Bud(self._channel, 0, bud_num=b), s)
+ sig = Signal(Plugin(self._channel, 0, bud_num=b), s)
ret += [sig]
return ret
class Signal:
- def __init__(self, bud, signal_num):
- self._bud = bud
+ def __init__(self, plugin, signal_num):
+ self._plugin = plugin
self._signal_num = signal_num
self._name = sys_bl00mbox.channel_bud_get_signal_name(
- bud.channel_num, bud.bud_num, signal_num
+ plugin.channel_num, plugin.bud_num, signal_num
)
mpx = sys_bl00mbox.channel_bud_get_signal_name_multiplex(
- bud.channel_num, bud.bud_num, signal_num
+ plugin.channel_num, plugin.bud_num, signal_num
)
if mpx != (-1):
self._name += str(mpx)
self._description = sys_bl00mbox.channel_bud_get_signal_description(
- bud.channel_num, bud.bud_num, signal_num
+ plugin.channel_num, plugin.bud_num, signal_num
)
self._unit = sys_bl00mbox.channel_bud_get_signal_unit(
- bud.channel_num, bud.bud_num, signal_num
+ plugin.channel_num, plugin.bud_num, signal_num
)
self._hints = ""
def __repr__(self):
- self._bud._check_existence()
+ self._plugin._check_existence()
ret = self.name
if len(self.unit):
@@ -110,10 +110,10 @@ class Signal:
conret += [
direction
+ con.name
- + " in [bud "
- + str(con._bud.bud_num)
+ + " in [plugin "
+ + str(con._plugin.bud_num)
+ "] "
- + con._bud.name
+ + con._plugin.name
]
if isinstance(con, ChannelMixer):
conret += [" ==> [channel mixer]"]
@@ -146,9 +146,9 @@ class Signal:
@property
def value(self):
- self._bud._check_existence()
+ self._plugin._check_existence()
return sys_bl00mbox.channel_bud_get_signal_value(
- self._bud.channel_num, self._bud.bud_num, self._signal_num
+ self._plugin.channel_num, self._plugin.bud_num, self._signal_num
)
@@ -157,29 +157,31 @@ class SignalOutput(Signal):
def value(self, val):
if val == None:
sys_bl00mbox.channel_disconnect_signal_tx(
- self._bud.channel_num, self._bud.bud_num, self._signal_num
+ self._plugin.channel_num, self._plugin.bud_num, self._signal_num
)
elif isinstance(val, SignalInput):
val.value = self
elif isinstance(val, ChannelMixer):
- if val._channel.channel_num == self._bud.channel_num:
+ if val._channel.channel_num == self._plugin.channel_num:
sys_bl00mbox.channel_connect_signal_to_output_mixer(
- self._bud.channel_num, self._bud.bud_num, self._signal_num
+ self._plugin.channel_num, self._plugin.bud_num, self._signal_num
)
@property
def connections(self):
cons = []
- chan = self._bud.channel_num
- bud = self._bud.bud_num
+ chan = self._plugin.channel_num
+ bud_num = self._plugin.bud_num
sig = self._signal_num
- for i in range(sys_bl00mbox.channel_subscriber_num(chan, bud, sig)):
- b = sys_bl00mbox.channel_get_bud_by_subscriber_list_pos(chan, bud, sig, i)
+ for i in range(sys_bl00mbox.channel_subscriber_num(chan, bud_num, sig)):
+ b = sys_bl00mbox.channel_get_bud_by_subscriber_list_pos(
+ chan, bud_num, sig, i
+ )
s = sys_bl00mbox.channel_get_signal_by_subscriber_list_pos(
- chan, bud, sig, i
+ chan, bud_num, sig, i
)
if (s >= 0) and (b > 0):
- cons += [_makeSignal(Bud(Channel(chan), 0, bud_num=b), s)]
+ cons += [_makeSignal(Plugin(Channel(chan), 0, bud_num=b), s)]
elif s == -1:
cons += [ChannelMixer(Channel(chan))]
return cons
@@ -188,18 +190,18 @@ class SignalOutput(Signal):
class SignalInput(Signal):
@Signal.value.setter
def value(self, val):
- self._bud._check_existence()
+ self._plugin._check_existence()
if isinstance(val, SignalOutput):
if len(self.connections):
if not sys_bl00mbox.channel_disconnect_signal_rx(
- self._bud.channel_num, self._bud.bud_num, self._signal_num
+ self._plugin.channel_num, self._plugin.bud_num, self._signal_num
):
return
sys_bl00mbox.channel_connect_signal(
- self._bud.channel_num,
- self._bud.bud_num,
+ self._plugin.channel_num,
+ self._plugin.bud_num,
self._signal_num,
- val._bud.bud_num,
+ val._plugin.bud_num,
val._signal_num,
)
elif isinstance(val, SignalInput):
@@ -208,23 +210,26 @@ class SignalInput(Signal):
elif (type(val) == int) or (type(val) == float):
if len(self.connections):
if not sys_bl00mbox.channel_disconnect_signal_rx(
- self._bud.channel_num, self._bud.bud_num, self._signal_num
+ self._plugin.channel_num, self._plugin.bud_num, self._signal_num
):
return
sys_bl00mbox.channel_bud_set_signal_value(
- self._bud.channel_num, self._bud.bud_num, self._signal_num, int(val)
+ self._plugin.channel_num,
+ self._plugin.bud_num,
+ self._signal_num,
+ int(val),
)
@property
def connections(self):
cons = []
- chan = self._bud.channel_num
- bud = self._bud.bud_num
+ chan = self._plugin.channel_num
+ bud = self._plugin.bud_num
sig = self._signal_num
b = sys_bl00mbox.channel_get_source_bud(chan, bud, sig)
s = sys_bl00mbox.channel_get_source_signal(chan, bud, sig)
if (s >= 0) and (b > 0):
- cons += [_makeSignal(Bud(Channel(chan), 0, bud_num=b), s)]
+ cons += [_makeSignal(Plugin(Channel(chan), 0, bud_num=b), s)]
return cons
@@ -240,8 +245,8 @@ class SignalInputTrigger(SignalInput):
class SignalInputPitch(SignalInput):
- def __init__(self, bud, signal_num):
- SignalInput.__init__(self, bud, signal_num)
+ def __init__(self, plugin, signal_num):
+ SignalInput.__init__(self, plugin, signal_num)
@property
def tone(self):
@@ -271,26 +276,26 @@ class SignalInputPitch(SignalInput):
class SignalList:
- def __init__(self, bud):
+ def __init__(self, plugin):
self._list = []
for signal_num in range(
- sys_bl00mbox.channel_bud_get_num_signals(bud.channel_num, bud.bud_num)
+ sys_bl00mbox.channel_bud_get_num_signals(plugin.channel_num, plugin.bud_num)
):
hints = sys_bl00mbox.channel_bud_get_signal_hints(
- bud.channel_num, bud.bud_num, signal_num
+ plugin.channel_num, plugin.bud_num, signal_num
)
if hints & 2:
- signal = SignalOutput(bud, signal_num)
+ signal = SignalOutput(plugin, signal_num)
signal._hints = "output"
elif hints & 1:
if hints & 4:
- signal = SignalInputTrigger(bud, signal_num)
+ signal = SignalInputTrigger(plugin, signal_num)
signal._hints = "input/trigger"
elif hints & 32:
- signal = SignalInputPitch(bud, signal_num)
+ signal = SignalInputPitch(plugin, signal_num)
signal._hints = "input/pitch"
else:
- signal = SignalInput(bud, signal_num)
+ signal = SignalInput(plugin, signal_num)
signal._hints = "input"
self._list += [signal]
setattr(self, signal.name.split(" ")[0], signal)
@@ -303,7 +308,7 @@ class SignalList:
super().__setattr__(key, value)
-class Bud:
+class Plugin:
def __init__(self, channel, plugin_id, init_var=0, bud_num=None):
self._channel_num = channel.channel_num
if bud_num == None:
@@ -312,7 +317,7 @@ class Bud:
self.channel_num, self.plugin_id, init_var
)
if self._bud_num == None:
- raise Bl00mboxError("bud init failed")
+ raise Bl00mboxError("plugin init failed")
else:
self._bud_num = bud_num
self._check_existence()
@@ -324,7 +329,7 @@ class Bud:
def __repr__(self):
self._check_existence()
- ret = "[bud " + str(self.bud_num) + "] " + self.name
+ ret = "[plugin " + str(self.bud_num) + "] " + self.name
for sig in self.signals._list:
ret += "\n " + "\n ".join(repr(sig).split("\n"))
return ret
@@ -335,7 +340,7 @@ class Bud:
def _check_existence(self):
if not sys_bl00mbox.channel_bud_exists(self.channel_num, self.bud_num):
- raise Bl00mboxError("bud has been deleted")
+ raise Bl00mboxError("plugin has been deleted")
@property
def channel_num(self):
@@ -429,9 +434,9 @@ class Channel:
ret += " (background mute override)"
ret += "\n volume: " + str(self.volume)
b = sys_bl00mbox.channel_buds_num(self.channel_num)
- ret += "\n buds: " + str(b)
- if len(self.buds) != b:
- ret += " (desync" + str(len(self.buds)) + ")"
+ ret += "\n plugins: " + str(b)
+ if len(self.plugins) != b:
+ ret += " (desync" + str(len(self.plugins)) + ")"
ret += "\n " + "\n ".join(repr(self.mixer).split("\n"))
return ret
@@ -463,16 +468,16 @@ class Channel:
self.clear()
sys_bl00mbox.channel_set_free(self.channel_num, val)
- def _new_bud(self, thing, init_var=None):
- bud_init_var = 0
+ def _new_plugin(self, thing, init_var=None):
+ plugin_init_var = 0
if (type(init_var) == int) or (type(init_var) == float):
- bud_init_var = int(init_var)
- bud = None
+ plugin_init_var = int(init_var)
+ plugin = None
if isinstance(thing, bl00mbox._plugins._Plugin):
- bud = Bud(self, thing.plugin_id, bud_init_var)
+ plugin = Plugin(self, thing.plugin_id, plugin_init_var)
if type(thing) == int:
- bud = Bud(self, thing, bud_init_var)
- return bud
+ plugin = Plugin(self, thing, plugin_init_var)
+ return plugin
def _new_patch(self, patch, init_var=None):
if init_var == None:
@@ -499,17 +504,17 @@ class Channel:
if issubclass(thing, bl00mbox.patches._Patch):
return self._new_patch(thing, init_var)
if isinstance(thing, bl00mbox._plugins._Plugin) or (type(thing) == int):
- return self._new_bud(thing, init_var)
+ return self._new_plugin(thing, init_var)
@property
- def buds(self):
- buds = []
+ def plugins(self):
+ plugins = []
for i in range(sys_bl00mbox.channel_buds_num(self.channel_num)):
b = sys_bl00mbox.channel_get_bud_by_list_pos(self.channel_num, i)
- bud = Bud(self, 0, bud_num=b)
- buds += [bud]
- return buds
+ plugin = Plugin(self, 0, bud_num=b)
+ plugins += [plugin]
+ return plugins
@property
def channel_num(self):
diff --git a/python_payload/mypystubs/bl00mbox/_patches.pyi b/python_payload/mypystubs/bl00mbox/_patches.pyi
index 69c1d88e4d..50581ef2e2 100644
--- a/python_payload/mypystubs/bl00mbox/_patches.pyi
+++ b/python_payload/mypystubs/bl00mbox/_patches.pyi
@@ -2,47 +2,42 @@ import bl00mbox
from typing import List
-class _Patch: ...
+class _Patch:
+ plugins: _PatchPluginList
+ signals: _PatchSignalList
class _PatchSignalList:
def __getattr__(self, name: str) -> bl00mbox.Signal: ...
-
-class _PatchBudList:
- def __getattr__(self, name: str) -> bl00mbox.Bud: ...
-
-class tinysynth(_Patch):
- def decay(self, v: float) -> None: ...
- def waveform(self, v: int) -> None: ...
- def attack(self, v: float) -> None: ...
- def volume(self, v: float) -> None: ...
- def sustain(self, v: float) -> None: ...
- def release(self, v: float) -> None: ...
- def tone(self, v: float) -> None: ...
- def start(self) -> None: ...
- def stop(self) -> None: ...
-
-class tinysynth_fm(tinysynth):
- def fm_waveform(self, v: int) -> None: ...
- def fm(self, v: float) -> None: ...
+ def __setattr__(
+ self,
+ name: str,
+ value: int | float | bl00mbox.Signal | bl00mbox.ChannelMixer,
+ ) -> None: ...
+
+class _PatchPluginList:
+ def __getattr__(self, name: str) -> bl00mbox.Plugin: ...
+ def __setattr__(
+ self,
+ name: str,
+ value: bl00mbox.Plugin,
+ ) -> None: ...
+
+class tinysynth(_Patch): ...
+class tinysynth_fm(tinysynth): ...
class step_sequencer(_Patch):
- seqs: List[bl00mbox.Bud]
+ seqs: List[bl00mbox.Plugin]
bpm: int
def trigger_state(self, track: int, i: int) -> bool: ...
def trigger_toggle(self, track: int, i: int) -> None: ...
-class sampler(_Patch):
- sampler: bl00mbox.Bud
+class sampler(_Patch): ...
class fuzz(_Patch):
- buds: _PatchBudList
- signals: _PatchSignalList
volume: int
intensity: float
gate: int
class karplus_strong(_Patch):
- buds: _PatchBudList
- signals: _PatchSignalList
decay: int
diff --git a/python_payload/mypystubs/bl00mbox/_user.pyi b/python_payload/mypystubs/bl00mbox/_user.pyi
index 74ff882650..054bb681cb 100644
--- a/python_payload/mypystubs/bl00mbox/_user.pyi
+++ b/python_payload/mypystubs/bl00mbox/_user.pyi
@@ -13,7 +13,7 @@ class Signal:
value: Any
# value: None | int | float | "Signal" | "ChannelMixer"
- def __init__(self, bud: "Bud", signal_num: int): ...
+ def __init__(self, plugin: "Plugin", signal_num: int): ...
# SignalInputTrigger functions
def start(self, velocity: int = 32767) -> None: ...
@@ -28,7 +28,7 @@ class SignalList:
def __setattr__(self, name: str, value: Signal | int | "ChannelMixer") -> None: ...
def __getattr__(self, name: str) -> Signal: ...
-class Bud:
+class Plugin:
signals: SignalList
def __init__(
@@ -51,11 +51,11 @@ class Channel:
def __init__(self, name: str): ...
def clear(self) -> None: ...
def _new_patch(self, patch: Type[T], init_var: Optional[Any] = None) -> T: ...
- def _new_bud(
+ def _new_plugin(
self,
thing: bl00mbox._plugins._Plugin | int,
init_var: Optional[int | float] = None,
- ) -> Bud: ...
+ ) -> Plugin: ...
@overload
def new(self, thing: Type[P], init_var: Optional[Any] = None) -> P: ...
@overload
@@ -63,7 +63,7 @@ class Channel:
self,
thing: bl00mbox._plugins._Plugin | int,
init_var: Optional[int | float] = None,
- ) -> Bud: ...
+ ) -> Plugin: ...
class ChannelMixer:
_channel: Channel
--
GitLab