Skip to content
Snippets Groups Projects
Commit 57beb54e authored by dx's avatar dx Committed by moon2
Browse files

bl00mbox: gay drums

parent b80cbb09
Branches
No related tags found
No related merge requests found
Showing
with 531 additions and 275 deletions
......@@ -750,10 +750,10 @@ int16_t * bl00mbox_channel_bud_get_table_pointer(uint8_t channel, uint32_t bud_i
return bud->plugin->plugin_table;
}
int16_t bl00mbox_channel_bud_get_table_len(uint8_t channel, uint32_t bud_index){
uint32_t bl00mbox_channel_bud_get_table_len(uint8_t channel, uint32_t bud_index){
bl00mbox_channel_t * chan = bl00mbox_get_channel(channel);
if(chan == NULL) return false;
if(chan == NULL) return 0;
bl00mbox_bud_t * bud = bl00mbox_channel_get_bud_by_index(channel, bud_index);
if(bud == NULL) return false;
if(bud == NULL) return 0;
return bud->plugin->plugin_table_len;
}
......@@ -52,5 +52,5 @@ uint16_t bl00mbox_channel_get_source_signal(uint8_t channel, uint64_t bud_index,
bool bl00mbox_channel_bud_set_table_value(uint8_t channel, uint32_t bud_index, uint32_t table_index, int16_t value);
int16_t bl00mbox_channel_bud_get_table_value(uint8_t channel, uint32_t bud_index, uint32_t table_index);
int16_t bl00mbox_channel_bud_get_table_len(uint8_t channel, uint32_t bud_index);
uint32_t bl00mbox_channel_bud_get_table_len(uint8_t channel, uint32_t bud_index);
int16_t * bl00mbox_channel_bud_get_table_pointer(uint8_t channel, uint32_t bud_index);
......@@ -11,14 +11,17 @@ radspa_descriptor_t sequencer_desc = {
.destroy_plugin_instance = radspa_standard_plugin_destroy
};
#define SEQUENCER_NUM_SIGNALS 6
#define SEQUENCER_NUM_SIGNALS 7
#define SEQUENCER_STEP 0
#define SEQUENCER_STEP_LEN 1
#define SEQUENCER_SYNC_OUT 2
#define SEQUENCER_SYNC_IN 3
#define SEQUENCER_BPM 4
#define SEQUENCER_BEAT_DIV 5
#define SEQUENCER_OUTPUT 6
#define SEQUENCER_SYNC_OUT 1
#define SEQUENCER_SYNC_IN 2
#define SEQUENCER_START_STEP 3
#define SEQUENCER_END_STEP 4
#define SEQUENCER_BPM 5
#define SEQUENCER_BEAT_DIV 6
// mpx'd
#define SEQUENCER_OUTPUT 7
static uint64_t target(uint64_t step_len, uint64_t bpm, uint64_t beat_div){
if(bpm == 0) return 0;
......@@ -37,20 +40,22 @@ void sequencer_run(radspa_t * sequencer, uint16_t num_samples, uint32_t render_p
radspa_signal_t * step_sig = radspa_signal_get_by_index(sequencer, SEQUENCER_STEP);
if(step_sig->buffer != NULL) output_request = true;
radspa_signal_t * step_len_sig = radspa_signal_get_by_index(sequencer, SEQUENCER_STEP_LEN);
radspa_signal_t * sync_out_sig = radspa_signal_get_by_index(sequencer, SEQUENCER_SYNC_OUT);
if(sync_out_sig->buffer != NULL) output_request = true;
if(!output_request) return;
radspa_signal_t * sync_in_sig = radspa_signal_get_by_index(sequencer, SEQUENCER_SYNC_IN);
radspa_signal_t * start_step_sig = radspa_signal_get_by_index(sequencer, SEQUENCER_START_STEP);
radspa_signal_t * end_step_sig = radspa_signal_get_by_index(sequencer, SEQUENCER_END_STEP);
radspa_signal_t * bpm_sig = radspa_signal_get_by_index(sequencer, SEQUENCER_BPM);
radspa_signal_t * beat_div_sig = radspa_signal_get_by_index(sequencer, SEQUENCER_BEAT_DIV);
int16_t * table = sequencer->plugin_table;
int16_t s1 = radspa_signal_get_value(step_len_sig, 0, num_samples, render_pass_id);
int16_t s2 = data->track_step_len;
data->step_target = s1 > 0 ? (s1 > s2 ? s2 : s1) : 1;
int16_t s1 = radspa_signal_get_value(end_step_sig, 0, num_samples, render_pass_id);
int16_t s2 = data->track_step_len - 1;
data->step_end = s1 > 0 ? (s1 > s2 ? s2 : s1) : 1;
data->step_start = radspa_signal_get_value(start_step_sig, 0, num_samples, render_pass_id);
int16_t bpm = bpm_sig->get_value(bpm_sig, 0, num_samples, render_pass_id);
int16_t beat_div = beat_div_sig->get_value(beat_div_sig, 0, num_samples, render_pass_id);
......@@ -65,8 +70,8 @@ void sequencer_run(radspa_t * sequencer, uint16_t num_samples, uint32_t render_p
if(data->counter >= data->counter_target){
data->counter = 0;
data->step++;
if(data->step >= data->step_target){
data->step = 0;
if(data->step > data->step_end){
data->step = data->step_start;
data->sync_out = -data->sync_out;
}
}
......@@ -74,7 +79,7 @@ void sequencer_run(radspa_t * sequencer, uint16_t num_samples, uint32_t render_p
int16_t sync_in = sync_in_sig->get_value(sync_in_sig, i, num_samples, render_pass_id);
if(((sync_in > 0) && (data->sync_in_prev <= 0)) || ((sync_in > 0) && (data->sync_in_prev <= 0))){
data->counter = 0;
data->step = 0;
data->step = data->step_start;
data->sync_out = -data->sync_out;
}
data->sync_in_prev = sync_in;
......@@ -131,9 +136,10 @@ radspa_t * sequencer_create(uint32_t init_var){
data->beat_div_prev = 16;
data->counter_target = target(data->track_step_len, data->bpm_prev, data->beat_div_prev);
radspa_signal_set(sequencer, SEQUENCER_STEP, "step", RADSPA_SIGNAL_HINT_OUTPUT, 0);
radspa_signal_set(sequencer, SEQUENCER_STEP_LEN, "step_len", RADSPA_SIGNAL_HINT_INPUT, num_pixels);
radspa_signal_set(sequencer, SEQUENCER_SYNC_OUT, "sync_out", RADSPA_SIGNAL_HINT_OUTPUT, 0);
radspa_signal_set(sequencer, SEQUENCER_SYNC_IN, "sync_in", RADSPA_SIGNAL_HINT_INPUT | RADSPA_SIGNAL_HINT_TRIGGER, 0);
radspa_signal_set(sequencer, SEQUENCER_START_STEP, "step_start", RADSPA_SIGNAL_HINT_INPUT, 0);
radspa_signal_set(sequencer, SEQUENCER_END_STEP, "step_end", RADSPA_SIGNAL_HINT_INPUT, num_pixels-1);
radspa_signal_set(sequencer, SEQUENCER_BPM, "bpm", RADSPA_SIGNAL_HINT_INPUT, data->bpm_prev);
radspa_signal_set(sequencer, SEQUENCER_BEAT_DIV, "beat_div", RADSPA_SIGNAL_HINT_INPUT, data->beat_div_prev);
radspa_signal_set_group(sequencer, data->num_tracks, 1, SEQUENCER_OUTPUT, "track",
......
......@@ -11,7 +11,8 @@ typedef struct {
typedef struct {
uint8_t num_tracks;
uint16_t track_step_len;
uint8_t step_target;
uint8_t step_end;
uint8_t step_start;
uint8_t step;
uint64_t counter;
uint64_t counter_target;
......
import bl00mbox
import captouch
import leds
from st3m.application import Application, ApplicationContext
from st3m.input import InputState
from st3m.goose import Tuple, Iterator, Optional, Callable, List, Any, TYPE_CHECKING
from ctx import Context
from st3m.ui.view import View, ViewManager
if TYPE_CHECKING:
Number = float | int
Color = Tuple[Number, Number, Number]
ColorLeds = Tuple[int, int, int]
Rendee = Callable[[Context, Any], None]
else:
Number = Color = ColorLeds = Rendee = None
class GayDrums(Application):
def __init__(self, app_ctx: ApplicationContext) -> None:
super().__init__(app_ctx)
self.blm = bl00mbox.Channel("gay drums")
self.num_samplers = 6
self.seq = self.blm.new(bl00mbox.patches.sequencer, num_tracks=8, num_steps=32)
self.bar = self.seq.signals.step.value // 16
self.set_bar_mode(0)
self.kick: Optional[bl00mbox.patches._Patch] = None
self.hat: Optional[bl00mbox.patches._Patch] = None
self.close: Optional[bl00mbox.patches._Patch] = None
self.open: Optional[bl00mbox.patches._Patch] = None
self.crash: Optional[bl00mbox.patches._Patch] = None
self.snare: Optional[bl00mbox.patches._Patch] = None
self.seq.signals.bpm.value = 80
self.track_names = ["kick", "hihat", "snare", "crash", "nya", "woof"]
self.ct_prev = captouch.read()
self.track = 1
self.blm.background_mute_override = True
self.tap_tempo_press_counter = 0
self.track_back_press_counter = 0
self.delta_acc = 0
self.stopped = False
self.track_back = False
self.bpm = self.seq.signals.bpm.value
self.samples_loaded = 0
self.samples_total = len(self.track_names)
self.loading_text = ""
self.init_complete = False
self.load_iter = self.iterate_loading()
self._render_list_2: List[Tuple[Rendee, Any]] = []
self._render_list_1: List[Tuple[Rendee, Any]] = []
self._group_highlight_on = [False] * 4
self._group_highlight_redraw = [False] * 4
self._redraw_background = 2
self._group_gap = 5
self.background_col: Color = (0, 0, 0)
self.highlight_col: Color = (0.15, 0.15, 0.15)
def set_bar_mode(self, mode: int) -> None:
# TODO: figure out how to speed up re-render
if mode == 0:
self.seq.signals.step_start = 0
self.seq.signals.step_end = 15
if mode == 1:
self.seq.signals.step_start = 16
self.seq.signals.step_end = 31
if mode == 2:
self.seq.signals.step_start = 0
self.seq.signals.step_end = 31
def iterate_loading(self) -> Iterator[Tuple[int, str]]:
yield 0, "kick.wav"
self.nya = self.blm.new(bl00mbox.patches.sampler, "nya.wav")
self.nya.signals.output = self.blm.mixer
self.nya.signals.trigger = self.seq.plugins.seq.signals.track6
self.kick = self.blm.new(bl00mbox.patches.sampler, "kick.wav")
self.kick.signals.output = self.blm.mixer
self.kick.signals.trigger = self.seq.plugins.seq.signals.track0
yield 1, "hihat.wav"
self.woof = self.blm.new(bl00mbox.patches.sampler, "bark.wav")
self.woof.signals.output = self.blm.mixer
self.woof.signals.trigger = self.seq.plugins.seq.signals.track7
self.hat = self.blm.new(bl00mbox.patches.sampler, "hihat.wav")
self.hat.signals.output = self.blm.mixer
self.hat.signals.trigger = self.seq.plugins.seq.signals.track1
yield 2, "close.wav"
self.close = self.blm.new(bl00mbox.patches.sampler, "close.wav")
self.close.signals.output = self.blm.mixer
self.close.signals.trigger = self.seq.plugins.seq.signals.track2
yield 3, "open.wav"
self.open = self.blm.new(bl00mbox.patches.sampler, "open.wav")
self.open.signals.output = self.blm.mixer
self.open.signals.trigger = self.seq.plugins.seq.signals.track3
yield 4, "snare.wav"
self.snare = self.blm.new(bl00mbox.patches.sampler, "snare.wav")
self.snare.signals.output = self.blm.mixer
self.snare.signals.trigger = self.seq.plugins.seq.signals.track4
yield 5, "crash.wav"
self.crash = self.blm.new(bl00mbox.patches.sampler, "crash.wav")
self.crash.signals.output = self.blm.mixer
self.crash.signals.trigger = self.seq.plugins.seq.signals.track5
yield 6, ""
def _highlight_petal(self, num: int, r: int, g: int, b: int) -> None:
for i in range(5):
leds.set_rgb((4 * num - i + 2) % 40, r, g, b)
def _track_col(self, track: int) -> ColorLeds:
rgb = (20, 20, 20)
if track == 0:
rgb = (120, 0, 137)
elif track == 1:
rgb = (0, 75, 255)
elif track == 2:
rgb = (0, 130, 27)
elif track == 3:
rgb = (255, 239, 0)
elif track == 4:
rgb = (255, 142, 0)
elif track == 5:
rgb = (230, 0, 0)
return rgb
def draw_background(self, ctx: Context, data: None) -> None:
ctx.rgb(0, 0, 0).rectangle(-120, -120, 240, 240).fill()
# group bars
bar_len = 10 * (1 + self.num_samplers)
bar_pos = 4 * 12 + self._group_gap
self.ctx_draw_centered_rect(ctx, 0, 0, 1, bar_len, (0.5, 0.5, 0.5))
self.ctx_draw_centered_rect(ctx, bar_pos, 0, 1, bar_len, (0.5, 0.5, 0.5))
self.ctx_draw_centered_rect(ctx, -bar_pos, 0, 1, bar_len, (0.5, 0.5, 0.5))
ctx.font = ctx.get_font_name(1)
ctx.font_size = 15
ctx.rgb(0.6, 0.6, 0.6)
ctx.move_to(0, -85)
ctx.text("(hold) stop")
ctx.move_to(0, -100)
ctx.text("tap tempo")
self.draw_track_name(ctx, None)
self.draw_bpm(ctx, None)
for track in range(self.num_samplers):
for step in range(16):
self.draw_track_step_marker(ctx, (track, step))
def track_get_state(self, track: int, step: int) -> int:
sequencer_track = track
if track > 1:
sequencer_track += 2
if track == 1:
if self.seq.trigger_state(1, step):
return 1
elif self.seq.trigger_state(2, step):
return 2
elif self.seq.trigger_state(3, step):
return 3
else:
return 0
else:
state = self.seq.trigger_state(sequencer_track, step)
if state == 0:
return 0
elif state == 32767:
return 3
elif state < 16384:
return 1
else:
return 2
def track_incr_state(self, track: int, step: int) -> None:
sequencer_track = track
if track > 1:
sequencer_track += 2
if track == 1:
state = self.track_get_state(track, step)
if state == 0:
self.seq.trigger_start(1, step)
self.seq.trigger_clear(2, step)
self.seq.trigger_clear(3, step)
if state == 1:
self.seq.trigger_clear(1, step)
self.seq.trigger_start(2, step)
self.seq.trigger_clear(3, step)
if state == 2:
self.seq.trigger_clear(1, step)
self.seq.trigger_clear(2, step)
self.seq.trigger_start(3, step)
if state == 3:
self.seq.trigger_clear(1, step)
self.seq.trigger_clear(2, step)
self.seq.trigger_clear(3, step)
else:
state = self.seq.trigger_state(sequencer_track, step)
if state == 0:
new_state = 16000
elif state == 32767:
new_state = 0
else:
new_state = 32767
self.seq.trigger_start(sequencer_track, step, new_state)
def draw_track_step_marker(self, ctx: Context, data: Tuple[int, int]) -> None:
track, step = data
self._group_gap = 4
rgb = self._track_col(track)
rgbf = (rgb[0] / 256, rgb[1] / 256, rgb[2] / 256)
y = -int(12 * (track - (self.num_samplers - 1) / 2))
trigger_state = self.track_get_state(track, step)
size = 2
x = 12 * (7.5 - step)
x += self._group_gap * (1.5 - (step // 4))
x = int(-x)
group = step // 4
bg = self.background_col
if self._group_highlight_on[group]:
bg = self.highlight_col
if trigger_state == 3:
self.ctx_draw_centered_rect(ctx, x, y, 8, 8, rgbf)
elif trigger_state == 2:
self.ctx_draw_centered_rect(ctx, x, y, 8, 8, bg)
self.ctx_draw_centered_rect(ctx, x, y - 2, 8, 4, rgbf)
elif trigger_state == 1:
self.ctx_draw_centered_rect(ctx, x, y, 8, 8, bg)
self.ctx_draw_centered_rect(ctx, x, y + 2, 8, 4, rgbf)
elif trigger_state == 0:
self.ctx_draw_centered_rect(ctx, x, y, 8, 8, bg)
self.ctx_draw_centered_rect(ctx, x, y, 2, 2, rgbf)
def ctx_draw_centered_rect(
self, ctx: Context, posx: int, posy: int, sizex: int, sizey: int, col: Color
) -> None:
ctx.rgb(*col)
nosx = int(posx - (sizex / 2))
nosy = int(posy - (sizey / 2))
ctx.rectangle(nosx, nosy, int(sizex), int(sizey)).fill()
def draw_group_highlight(self, ctx: Context, data: int) -> None:
i = data
col = self.background_col
if self._group_highlight_on[i]:
col = self.highlight_col
sizex = 48 + self._group_gap - 2
sizey = 10 * (1 + self.num_samplers)
posx = -int((12 * 4 + 1 + self._group_gap) * (1.5 - i))
posy = 0
self.ctx_draw_centered_rect(ctx, posx, posy, sizex, sizey, col)
for x in range(self.num_samplers):
for y in range(4):
self.draw_track_step_marker(ctx, (x, y + 4 * i))
def draw_bpm(self, ctx: Context, data: None) -> None:
self.ctx_draw_centered_rect(ctx, 0, -65, 200, 22, (0, 0, 0))
bpm = self.seq.signals.bpm.value
ctx.font = ctx.get_font_name(1)
ctx.font_size = 20
ctx.move_to(0, -65)
ctx.rgb(255, 255, 255)
ctx.text(str(bpm) + " bpm")
def draw(self, ctx: Context) -> None:
if not self.init_complete:
ctx.rgb(0, 0, 0).rectangle(-120, -120, 240, 240).fill()
ctx.font = ctx.get_font_name(0)
ctx.font_size = 24
ctx.move_to(0, -10)
ctx.rgb(0.8, 0.8, 0.8)
if self.samples_loaded == self.samples_total:
ctx.text("Loading complete")
else:
ctx.text("Loading samples...")
ctx.font_size = 16
ctx.move_to(0, 10)
ctx.text(self.loading_text)
progress = self.samples_loaded / self.samples_total
bar_len = 120 / self.samples_total
for x in range(self.samples_loaded):
rgb = self._track_col(x)
rgbf = (rgb[0] / 256, rgb[1] / 256, rgb[2] / 256)
ctx.rgb(*rgbf)
ctx.rectangle(x * bar_len - 60, 30, bar_len, 10).fill()
return
for i in range(4):
if self._group_highlight_redraw[i]:
self._group_highlight_redraw[i] = False
self._render_list_1 += [(self.draw_group_highlight, i)]
for rendee in self._render_list_1:
fun, param = rendee
fun(ctx, param)
for rendee in self._render_list_2:
fun, param = rendee
fun(ctx, param)
self._render_list_2 = self._render_list_1.copy()
self._render_list_1 = []
size = 4
st = self.seq.signals.step.value
stepx = -12 * (7.5 - st)
stepx -= self._group_gap * (1.5 - (st // 4))
stepy = -12 - 5 * self.num_samplers
trigger_state = self.track_get_state(self.track, st)
dotsize = 1
if trigger_state:
dotsize = 4
self.ctx_draw_centered_rect(ctx, 0, stepy, 200, 4, self.background_col)
self.ctx_draw_centered_rect(ctx, stepx, stepy, dotsize, dotsize, (1, 1, 1))
def draw_track_name(self, ctx: Context, data: None) -> None:
self.ctx_draw_centered_rect(ctx, 0, 60, 200, 30, self.background_col)
ctx.font = ctx.get_font_name(1)
ctx.font_size = 20
ctx.rgb(1, 1, 1)
ctx.move_to(45, 60)
ctx.text(">")
ctx.move_to(-45, 60)
ctx.text(">")
ctx.font_size = 15
ctx.rgb(0.6, 0.6, 0.6)
ctx.move_to(-45, 75)
ctx.text("(hold)")
ctx.font_size = 28
track = self.track
ctx.move_to(0, 60)
col = [x / 255 for x in self._track_col(track)]
ctx.rgb(*col)
ctx.text(self.track_names[track])
ctx.font_size = 15
track = (track - 1) % self.num_samplers
ctx.move_to(75, 60)
col = [x / 255 for x in self._track_col(track)]
ctx.rgb(*col)
ctx.text(self.track_names[track])
track = (track + 2) % self.num_samplers
ctx.move_to(-75, 60)
col = [x / 255 for x in self._track_col(track)]
ctx.rgb(*col)
ctx.text(self.track_names[track])
def think(self, ins: InputState, delta_ms: int) -> None:
super().think(ins, delta_ms)
if not self.init_complete:
try:
self.samples_loaded, self.loading_text = next(self.load_iter)
except StopIteration:
self.init_complete = True
self._render_list_1 += [(self.draw_background, None)]
return
st = self.seq.signals.step.value
rgb = self._track_col(self.track)
leds.set_all_rgb(0, 0, 0)
self._highlight_petal(10 - (4 - (st // 4)), *rgb)
self._highlight_petal(10 - (6 + (st % 4)), *rgb)
leds.update()
if self.bar != (st // 16):
self.bar = st // 16
self._group_highlight_redraw = [True] * 4
ct = captouch.read()
for i in range(4):
if ct.petals[4 - i].pressed:
if not self._group_highlight_on[i]:
self._group_highlight_redraw[i] = True
self._group_highlight_on[i] = True
for j in range(4):
if ct.petals[6 + j].pressed and not (
self.ct_prev.petals[6 + j].pressed
):
self.track_incr_state(self.track, self.bar * 16 + i * 4 + j)
self._render_list_1 += [
(self.draw_track_step_marker, (self.track, i * 4 + j))
]
else:
if self._group_highlight_on[i]:
self._group_highlight_redraw[i] = True
self._group_highlight_on[i] = False
if not ct.petals[5].pressed and (self.ct_prev.petals[5].pressed):
if self.track_back:
self.track_back = False
else:
self.track = (self.track - 1) % self.num_samplers
self._render_list_1 += [(self.draw_track_name, None)]
if ct.petals[0].pressed and not (self.ct_prev.petals[0].pressed):
if self.stopped:
self.seq.signals.bpm = self.bpm
self._render_list_1 += [(self.draw_bpm, None)]
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.signals.bpm = bpm
self._render_list_1 += [(self.draw_bpm, None)]
self.bpm = bpm
self.delta_acc = 0
if ct.petals[0].pressed:
if self.tap_tempo_press_counter > 500:
self.seq.signals.bpm = 0
self._render_list_1 += [(self.draw_bpm, None)]
self.stopped = True
self.blm.background_mute_override = False
else:
self.tap_tempo_press_counter += delta_ms
else:
self.tap_tempo_press_counter = 0
if ct.petals[5].pressed:
if (self.track_back_press_counter > 500) and not self.track_back:
self.track = (self.track + 1) % self.num_samplers
self._render_list_1 += [(self.draw_track_name, None)]
self.track_back = True
else:
self.track_back_press_counter += delta_ms
else:
self.track_back_press_counter = 0
if self.delta_acc < 3000:
self.delta_acc += delta_ms
self.ct_prev = ct
def on_enter(self, vm: Optional[ViewManager]) -> None:
self._render_list_1 += [(self.draw_background, None)]
self._render_list_1 += [(self.draw_background, None)] # nice
[app]
name = "Simple Drums"
name = "gay drums"
menu = "Music"
[entry]
class = "SimpleDrums"
class = "GayDrums"
[metadata]
author = "Flow3r Badge Authors"
......
import bl00mbox
import captouch
import leds
from st3m.application import Application, ApplicationContext
from st3m.input import InputState
from st3m.goose import Tuple
from ctx import Context
class Dot:
def __init__(
self,
sizex: float,
sizey: float,
imag: float,
real: float,
col: Tuple[float, float, float],
) -> None:
self.sizex = sizex
self.sizey = sizey
self.imag = imag
self.real = real
self.col = col
def draw(self, i: int, ctx: Context) -> None:
imag = self.imag
real = self.real
sizex = self.sizex
sizey = self.sizey
col = self.col
ctx.rgb(*col).rectangle(
-int(imag + (sizex / 2)), -int(real + (sizey / 2)), sizex, sizey
).fill()
class SimpleDrums(Application):
def __init__(self, app_ctx: ApplicationContext) -> None:
super().__init__(app_ctx)
# ctx.rgb(0, 0, 0).rectangle(-120, -120, 240, 240).fill()
self.blm = bl00mbox.Channel("simple drums")
self.seq = self.blm.new(bl00mbox.patches.sequencer)
self.hat = self.blm.new(bl00mbox.patches.sampler, "hihat.wav")
# Dot(10, 10, -30, 0, self._track_col(0)).draw(0,ctx)
self.kick = self.blm.new(bl00mbox.patches.sampler, "kick.wav")
# 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.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.seq.signals.track0
self.hat.signals.trigger = self.seq.plugins.seq.signals.track1
self.snare.signals.trigger = self.seq.plugins.seq.signals.track2
self.seq.signals.bpm.value = 80
self.track_names = ["kick", "hihat", "snare"]
self.track = 0
self.blm.background_mute_override = True
self.tap_tempo_press_counter = 0
self.delta_acc = 0
self.stopped = False
self.bpm = self.seq.signals.bpm.value
# True if a given group should be highlighted, when a corresponding
# petal is pressed.
self._group_highlight = [False for _ in range(4)]
# Disable repeat functionality as we want to detect long holds.
for i in range(10):
self.input.captouch.petals[i].whole.repeat_disable()
def _highlight_petal(self, num: int, r: int, g: int, b: int) -> None:
for i in range(5):
leds.set_rgb((4 * num - i + 2) % 40, r, g, b)
def _track_col(self, track: int) -> Tuple[int, int, int]:
rgb = (20, 20, 20)
if track == 0:
rgb = (0, 255, 0)
elif track == 1:
rgb = (0, 0, 255)
elif track == 2:
rgb = (255, 0, 0)
return rgb
def draw(self, ctx: Context) -> None:
dots = []
groupgap = 4
for i in range(4):
if self._group_highlight[i]:
dots.append(
Dot(
48 + groupgap,
40,
int((12 * 4 + groupgap) * (1.5 - i)),
0,
(0.15, 0.15, 0.15),
)
)
st = self.seq.signals.step.value
for track in range(3):
rgb = self._track_col(track)
rgbf = (rgb[0] / 256, rgb[1] / 256, rgb[2] / 256)
y = 12 * (track - 1)
for i in range(16):
trigger_state = self.seq.trigger_state(track, i)
size = 2
if trigger_state:
size = 8
x = 12 * (7.5 - i)
x += groupgap * (1.5 - (i // 4))
x = int(x)
dots.append(Dot(size, size, x, y, rgbf))
if (i == st) and (track == 0):
dots.append(Dot(size / 2, size / 2, x, 24, (1, 1, 1)))
dots.append(Dot(1, 40, 0, 0, (0.5, 0.5, 0.5)))
dots.append(Dot(1, 40, 4 * 12 + groupgap, 0, (0.5, 0.5, 0.5)))
dots.append(Dot(1, 40, -4 * 12 - groupgap, 0, (0.5, 0.5, 0.5)))
ctx.rgb(0, 0, 0).rectangle(-120, -120, 240, 240).fill()
for i, dot in enumerate(dots):
dot.draw(i, ctx)
ctx.font = ctx.get_font_name(4)
ctx.font_size = 30
ctx.move_to(0, 65)
col = [x / 255 for x in self._track_col(self.track)]
ctx.rgb(*col)
ctx.text(self.track_names[self.track])
ctx.font_size = 18
ctx.move_to(0, 102)
next_track = (self.track + 1) % 3
col = [x / 255 for x in self._track_col(next_track)]
ctx.rgb(*col)
ctx.text(self.track_names[next_track])
ctx.font = ctx.get_font_name(1)
ctx.font_size = 20
ctx.move_to(0, -65)
ctx.rgb(255, 255, 255)
ctx.text(str(self.seq.signals.bpm) + " bpm")
ctx.font_size = 15
ctx.move_to(0, -85)
ctx.rgb(0.6, 0.6, 0.6)
ctx.text("(hold) stop")
ctx.move_to(0, -100)
ctx.text("tap tempo")
ctx.move_to(0, 85)
ctx.text("next:")
def think(self, ins: InputState, delta_ms: int) -> None:
super().think(ins, delta_ms)
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])
else:
leds.set_all_rgb(0, 0, 0)
self._highlight_petal(10 - (4 - (st // 4)), *rgb)
self._highlight_petal(10 - (6 + (st % 4)), *rgb)
leds.update()
petals = self.input.captouch.petals
self._group_highlight = [False for _ in range(4)]
for i in range(4):
if petals[4 - i].whole.down:
self._group_highlight[i] = True
for j in range(4):
if petals[6 + j].whole.pressed:
self.seq.trigger_toggle(self.track, i * 4 + j)
if petals[5].whole.pressed:
self.track = (self.track - 1) % 3
if petals[0].whole.pressed:
if self.stopped:
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.signals.bpm = bpm
self.bpm = bpm
self.delta_acc = 0
if petals[0].whole.down:
if self.tap_tempo_press_counter > 500:
self.seq.signals.bpm = 0
self.stopped = True
self.blm.background_mute_override = False
else:
self.tap_tempo_press_counter += delta_ms
else:
self.tap_tempo_press_counter = 0
if self.delta_acc < 3000:
self.delta_acc += delta_ms
# SPDX-License-Identifier: CC0-1.0
import math
import bl00mbox
try:
import cpython.wave as wave
except ImportError:
wave = None
class _Patch:
......@@ -105,14 +100,14 @@ class tinysynth_fm(tinysynth):
class sampler(_Patch):
"""
needs a wave file with path relative to /flash/sys/samples/
requires a wave file. default path: /sys/samples/
"""
def __init__(self, chan, filename):
super().__init__(chan)
if wave is None:
pass
# raise Bl00mboxError("wave library not found")
if filename.startswith("/"):
f = wave.open("/flash/" + filename, "r")
else:
f = wave.open("/flash/sys/samples/" + filename, "r")
self.len_frames = f.getnframes()
......@@ -146,23 +141,21 @@ class sampler(_Patch):
class sequencer(_Patch):
def __init__(self, chan, num=4):
def __init__(self, chan, num_tracks, num_steps):
super().__init__(chan)
if num > 32:
num = 32
if num < 0:
num = 0
self.seqs = []
prev_seq = None
self.num_pixels = 16
self.num_tracks = num
init_var = (self.num_pixels * 256) + (self.num_tracks) # magic
self.num_steps = num_steps
self.num_tracks = num_tracks
init_var = (self.num_steps * 256) + (self.num_tracks) # magic
self.plugins.seq = chan.new(bl00mbox.plugins._sequencer, init_var)
self.signals.bpm = self.plugins.seq.signals.bpm
self.signals.beat_div = self.plugins.seq.signals.beat_div
self.signals.step = self.plugins.seq.signals.step
self.signals.step_len = self.plugins.seq.signals.step_len
tracktable = [-32767] + ([0] * self.num_pixels)
self.signals.step_end = self.plugins.seq.signals.step_end
self.signals.step_start = self.plugins.seq.signals.step_start
self.signals.step_start = self.plugins.seq.signals.step_start
tracktable = [-32767] + ([0] * self.num_steps)
self.plugins.seq.table = tracktable * self.num_tracks
def __repr__(self):
......@@ -193,14 +186,19 @@ class sequencer(_Patch):
return ret
def _get_table_index(self, track, step):
return step + 1 + track * (self.num_pixels + 1)
return step + 1 + track * (self.num_steps + 1)
def trigger_start(self, track, step, val=32767):
a = self.plugins.seq.table
a[self._get_table_index(track, step)] = val
self.plugins.seq.table = a
def trigger_start(self, track, step):
def trigger_stop(self, track, step, val=32767):
a = self.plugins.seq.table
a[self._get_table_index(track, step)] = 32767
a[self._get_table_index(track, step)] = -1
self.plugins.seq.table = a
def trigger_stop(self, track, step):
def trigger_clear(self, track, step):
a = self.plugins.seq.table
a[self._get_table_index(track, step)] = 0
self.plugins.seq.table = a
......@@ -213,7 +211,7 @@ class sequencer(_Patch):
if self.trigger_state(track, step) == 0:
self.trigger_start(track, step)
else:
self.trigger_stop(track, step)
self.trigger_clear(track, step)
class fuzz(_Patch):
......
......@@ -517,12 +517,6 @@ class Channel:
plugin = Plugin(self, thing, plugin_init_var)
return plugin
def _new_patch(self, patch, init_var=None):
if init_var == None:
return patch(self)
else:
return patch(self, init_var)
@staticmethod
def print_overview():
ret = []
......@@ -540,13 +534,13 @@ class Channel:
for plugin in self.plugins:
print(repr(plugin))
def new(self, thing, init_var=None):
def new(self, thing, *args, **kwargs):
self.free = False
if type(thing) == type:
if issubclass(thing, bl00mbox.patches._Patch):
return self._new_patch(thing, init_var)
return thing(self, *args, **kwargs)
if isinstance(thing, bl00mbox._plugins._Plugin) or (type(thing) == int):
return self._new_plugin(thing, init_var)
return self._new_plugin(thing, *args, **kwargs)
@property
def plugins(self):
......
......@@ -28,8 +28,11 @@ class tinysynth_fm(tinysynth): ...
class sequencer(_Patch):
bpm: int
def trigger_state(self, track: int, i: int) -> bool: ...
def trigger_toggle(self, track: int, i: int) -> None: ...
def trigger_start(self, track: int, step: int, val: int = 32767) -> None: ...
def trigger_stop(self, track: int, step: int, val: int = 32767) -> None: ...
def trigger_clear(self, track: int, step: int) -> None: ...
def trigger_state(self, track: int, step: int) -> int: ...
def trigger_toggle(self, track: int, step: int) -> None: ...
class sampler(_Patch): ...
......
......@@ -50,14 +50,13 @@ 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_plugin(
self,
thing: bl00mbox._plugins._Plugin | int,
init_var: Optional[int | float] = None,
) -> Plugin: ...
@overload
def new(self, thing: Type[P], init_var: Optional[Any] = None) -> P: ...
def new(self, thing: Type[P], *args: Any, **kwargs: Any) -> P: ...
@overload
def new(
self,
......
......@@ -2,3 +2,8 @@ Credits:
https://freesound.org/people/sandyrb/sounds/35633/
https://freesound.org/people/kaonaya/sounds/131363/
https://freesound.org/people/Vrezerino/sounds/16709/
https://freesound.org/people/collinb1000/sounds/634854/
https://freesound.org/people/exotonestudio/sounds/416249/
https://freesound.org/people/TheEndOfACycle/sounds/674293/
https://freesound.org/people/KurireeVA/sounds/476588/
https://freesound.org/people/boop_7/sounds/684369/
File added
File added
File added
No preview for this file type
File added
File added
No preview for this file type
......@@ -17,7 +17,7 @@ if TYPE_CHECKING:
class ABCBase(metaclass=ABCMeta):
pass
from typing import List, Optional, Tuple, Dict, Any, Callable
from typing import List, Optional, Tuple, Dict, Any, Callable, Iterator
from enum import Enum
else:
# We're in CPython or Micropython.
......@@ -31,7 +31,7 @@ else:
return _fail
try:
from typing import List, Optional, Tuple, Dict, Any, Callable
from typing import List, Optional, Tuple, Dict, Any, Callable, Iterator
from enum import Enum
except ImportError:
# We're in Micropython.
......@@ -41,6 +41,7 @@ else:
Dict = None
Any = None
Callable = None
Iterator = None
class Enum:
pass
......@@ -57,4 +58,5 @@ __all__ = [
"Dict",
"Any",
"Callable",
"Iterator",
]
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment