Skip to content

Commit c3ae113

Browse files
committed
hooked up delay to knobs, added first cut at save/load of ParamSets
1 parent d11dec8 commit c3ae113

File tree

4 files changed

+93
-47
lines changed

4 files changed

+93
-47
lines changed

circuitpython/tbish/code.py

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@
2828
display = setup_display()
2929
touches = setup_touch()
3030

31+
bpm = 180
32+
steps_per_beat = 2 # 4 = 16th note, 2 = 8th note, 1 = quarter note
33+
3134
seqs = [
3235
[[36, 36, 48, 36, 48, 48+7, 36, 48], # notes, 0 = rest
3336
[127, 80, 80, 80, 127, 1, 30, 1]], # vels, 1=slide, 127=accent
@@ -46,20 +49,22 @@
4649
]
4750

4851
params = [
49-
Param("cutoff", 4000, 200, 6000, "%4d", 'cutoff'),
52+
# name val, min, max, str, tb.attr name
53+
Param("cutoff", 4000, 100, 6000, "%4d", 'cutoff'),
5054
Param('envmod', 0.5, 0.0, 1.0, "%.2f",'envmod'),
5155

5256
Param("resQ", 1.0, 0.5, 4.0, "%.2f", 'resonance'),
53-
Param('decay', 0.5, 0.0, 1.0, "%.2f", 'decay'),
57+
Param('decay', 0.75, 0.0, 1.0, "%.2f", 'decay'),
5458

55-
Param('drive', 20, 5, 40, "%2d", 'drive'),
59+
Param('drive', 320, 5, 40, "%2d", 'drive'),
5660
Param('drivemix', 0.2, 0.0, 1.0, "%.2f", 'drive_mix'),
5761

58-
Param('delay', 0.3, 0.0, 1.0, "%.2f"),
59-
Param('dtime', 0.25, 0.0, 1.0, "%.2f"),
62+
Param('delay', 0.0, 0.0, 1.0, "%.2f", 'delay_mix'),
63+
Param('dtime', 0.25, 0.0, 1.0, "%.2f", 'delay_time'),
6064

6165
Param('seq', 0, 0, len(seqs), "%1d"),
62-
Param('bpm', 120, 40, 200, "%3d"),
66+
Param('bpm', bpm, 40, 200, "%3d"),
67+
6368
]
6469

6570
touchpad_to_knobset = [1,3,6,8,10] # ,13]
@@ -70,8 +75,10 @@
7075
mixer.voice[0].play(tb_audio)
7176

7277
sequencer = TBishSequencer(tb, seqs)
78+
sequencer.bpm = bpm
79+
sequencer.steps_per_beat = steps_per_beat
7380

74-
param_set = ParamSet(params, num_knobs=2)
81+
param_set = ParamSet(params, num_knobs=2, knob_smooth=0.125)
7582
param_set.apply_params(tb) # set up synth with param set
7683

7784
tb_disp = TBishUI(display, params)
@@ -87,14 +94,16 @@
8794
def update_ui():
8895
global last_ui_time
8996
ki = tb_disp.curr_param_pair # shorthand
90-
if time.monotonic() - last_ui_time > 0.05:
97+
#if time.monotonic() - last_ui_time > 0.05: # every 50 millis
98+
if time.monotonic() - last_ui_time > 0.01: # every 10 millis
9199
last_ui_time = time.monotonic()
92100

93101
# bit-reduction filtering then normalize
94102
#knobvals = (knobA.value, knobB.value)
95103
#knobvals = [((k>>8)<<8)/65535 for k in knobvals]
96104
# just normalized
97105
knobvals = (knobA.value/65535, knobB.value/65535)
106+
#print(knobvals, time.monotonic())
98107
param_set.update_knobs(knobvals)
99108

100109
# set synth with params
@@ -118,6 +127,11 @@ def update_ui():
118127
if key.pressed:
119128
if sequencer.playing:
120129
sequencer.stop()
130+
# testing out save/load params
131+
s = ParamSet.dump(param_set)
132+
print(s)
133+
ParamSet.load(s)
134+
121135
else:
122136
sequencer.start()
123137

circuitpython/tbish/param_set.py

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
1717
"""
1818

19+
import json
20+
1921
KNOB_MODE_PICKUP = 0
2022
KNOB_MODE_SCALE = 1
2123
KNOB_MODE_RELATIVE = 2
@@ -26,17 +28,19 @@ class Param:
2628
(optionally) an object attribute that they represent.
2729
"""
2830

29-
def __init__(self, name, val, min_val, max_val, fmt, objattr=None):
31+
def __init__(self, name, val, vmin, vmax, fmt, objattr=None):
3032
self.name = name
3133
self.val = val
32-
self.vmin = min_val
33-
self.vmax = max_val
34+
self.vmin = vmin
35+
self.vmax = vmax
3436
self.fmt = fmt
3537
self.objattr = objattr
3638

3739
def __str__(self):
40+
obstr = 'None' if self.objattr is None else "'%s'" % self.objattr
3841
return("Param('" + self.name + "'," + str(self.val) + "," +
39-
str(self.vmin) + "," + str(self.vmax) + ",'" + str(self.fmt) + "')")
42+
str(self.vmin) + "," + str(self.vmax) + ",'" + str(self.fmt) +
43+
"'," + obstr + ")")
4044

4145
def __repr__(self):
4246
return self.__str__()
@@ -101,7 +105,7 @@ def update_knobs(self, new_knob_vals):
101105
delta = param.val - new_val
102106
if abs(delta) < self.min_change * param.span:
103107
self.is_tracking[i] = True
104-
param.val = new_val
108+
#param.val = new_val
105109

106110
def apply_params(self, obj):
107111
""" Apply all params to given object """
@@ -117,12 +121,25 @@ def param_for_name(self, name):
117121
for p in filter(lambda p: p.name == name, self.params):
118122
return p
119123
return None
120-
124+
121125
def __str__(self):
122126
return("ParamSet(nknobs="+str(self.nknobs) + ", " +
123127
"nknobsets="+str(self.nknobsets) +
124128
", params="+str(self.params)+")")
125129

130+
@staticmethod
131+
def load(dumpstr):
132+
dumpobj = json.loads(dumpstr)
133+
newparams = [Param(**d) for d in dumpobj['params']]
134+
return newparams
135+
136+
@staticmethod
137+
def dump(paramset):
138+
dumpobj = {
139+
'params': [p.__dict__ for p in paramset.params]
140+
}
141+
return json.dumps(dumpobj)
142+
126143
# simple test
127144

128145
if __name__ == "__main__":

circuitpython/tbish/tbish_sequencer.py

100755100644
File mode changed.

circuitpython/tbish/tbish_synth.py

Lines changed: 48 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,13 @@ def add_audioeffects(self):
8686

8787
# No distortion on rp2040 (not enough CPU)
8888
self.fx_distortion = Distortion(**fxcfg, mix = 0.0,
89-
# other distortion modes are too slow?
9089
mode = audiofilters.DistortionMode.LOFI,
90+
# other distortion modes are too slow?
91+
#mode = audiofilters.DistortionMode.OVERDRIVE,
9192
soft_clip = True,
9293
pre_gain = 30,
9394
post_gain = -10)
95+
9496
# but yes filter on rp2040 with custom compile
9597
self.fx_filter1 = Filter(**fxcfg, mix=1.0)
9698
self.fx_filter2 = Filter(**fxcfg, mix=1.0)
@@ -117,43 +119,48 @@ def add_audioeffects(self):
117119
return self.fx_delay # the "output" of this synth
118120

119121
def note_on_step(self, midi_note, slide=False, accent=False):
120-
print("note_on_step:", midi_note, slide, accent)
121-
pass
122-
123-
def note_on(self, midi_note, vel=127):
124-
"""
125-
Trigger a note. 'vel' has special meaning.
126-
vel = 1 : slide, not new not
127-
vel = 127 : accent
128-
"""
129-
self.note_off(midi_note) # just in case
130-
131-
#frate = 1 / self.secs_per_step
122+
print("note_on_step: %3d %1d %1d" % (midi_note, slide, accent))
123+
self.note_off(midi_note) # must do when in step mode
124+
attack_level = 0.8
125+
cutoff = self.cutoff
126+
# resonance = self.resonance # fixme how to do this
127+
envmod = self.envmod
128+
glide_time = 0.001 # minimal slide
129+
if accent:
130+
attack_level += 0.2
131+
cutoff *= 1.3 # FIXME: verify
132+
#resonance *= 1.3
133+
envmod *= 0.5
134+
if slide:
135+
glide_time = 0.1
136+
# FIXME also do appropriate other actions for slide
132137
envdecay = max(0.05, self.secs_per_step * self.envdecay * 1.5) # 1.5 fudge
133138
frate = 1 / envdecay
134-
cutoff = self.cutoff * 1.3 if vel==127 else self.cutoff # FIXME verify
135-
envmod = self.envmod * 0.5 if vel==127 else self.envmod
136-
137139
self.filt_env.offset = ((1-envmod) * cutoff)
138140
self.filt_env.scale = cutoff - self.filt_env.offset
139141
self.filt_env.rate = frate
140142
self.filt_env.retrigger() # must retrigger once-shot LFOs
141-
self.glider.glide_time = 0.1 if vel==1 else 0.001 # slide or minimal slide
143+
self.glider.glide_time = glide_time
142144
self.glider.update(midi_note) # glide up to new note
143145
ampenv = synthio.Envelope(attack_time=0.001,
144-
attack_level = 0.8 if vel != 127 else 1.0,
146+
attack_level = attack_level,
145147
decay_time = envdecay,
146-
#decay_time = frate,
147148
sustain_level=0,
148-
release_time=0.01,) # self.envdecay)
149+
release_time=0.01)
149150
self.note = synthio.Note(synthio.midi_to_hz(midi_note),
150151
bend = self.glider.lerp,
151152
filter = self.filter,
152153
envelope = ampenv,
153154
waveform = waves[self.wavenum])
154155
self.synth.press(self.note)
156+
157+
158+
def note_on(self, midi_note, vel=100):
159+
"""For MIDI use"""
160+
self.note_on_step(midi_note, slide=(vel==1), accent=(vel==127))
155161

156162
def note_off(self, midi_note, vel=0):
163+
"""Used for both MIDI and sequencer mode"""
157164
if self.note:
158165
self.synth.release(self.note)
159166
self.note = None
@@ -174,6 +181,17 @@ def accent(self):
174181
def accent(self, v):
175182
self.accent = v
176183

184+
@property
185+
def resonance(self):
186+
return self.filter.Q
187+
188+
@resonance.setter
189+
def resonance(self,q):
190+
self.filter.Q = q
191+
if self.fx_filter1:
192+
self.fx_filter1.filter.Q = q
193+
self.fx_filter2.filter.Q = q
194+
177195
@property
178196
def drive(self):
179197
return self.fx_distortion.pre_gain
@@ -184,20 +202,17 @@ def drive(self,d):
184202
self.fx_distortion.pre_gain = d
185203

186204
@property
187-
def drive_mix(self):
188-
return self.fx_distortion.mix
205+
def delay_mix(self):
206+
return self.fx_delay.mix
189207

190-
@drive_mix.setter
191-
def drive_mix(self, m):
192-
self.fx_distortion.mix = m
208+
@delay_mix.setter
209+
def delay_mix(self, m):
210+
self.fx_delay.mix = m
193211

194212
@property
195-
def resonance(self):
196-
return self.filter.Q
213+
def delay_time(self):
214+
return self.fx_delay.delay_ms / (self.secs_per_step * 100 * 2)
197215

198-
@resonance.setter
199-
def resonance(self,q):
200-
self.filter.Q = q
201-
if self.fx_filter1:
202-
self.fx_filter1.filter.Q = q
203-
self.fx_filter2.filter.Q = q
216+
@delay_time.setter
217+
def delay_time(self, m):
218+
self.fx_delay.delay_ms = self.secs_per_step * 1000 * 2 * m

0 commit comments

Comments
 (0)