Skip to content
Snippets Groups Projects
Commit 359485df authored by David Huss's avatar David Huss :speech_balloon:
Browse files

Add snapping to Pitch Knob

parent 309c549e
No related branches found
No related tags found
No related merge requests found
%% Cell type:code id:52c8bec3-db2d-4522-b692-b035a71410de tags: %% Cell type:code id:52c8bec3-db2d-4522-b692-b035a71410de tags:
``` python ``` python
from matplotlib import pyplot as plt from matplotlib import pyplot as plt
%matplotlib inline %matplotlib inline
import math import math
!{sys.executable} -m pip install clipboard !{sys.executable} -m pip install clipboard
%matplotlib inline %matplotlib inline
import clipboard import clipboard
def draw(r, g, b): def draw(r, g, b):
x = [x for x in range(256)] x = [x for x in range(256)]
fig = plt.figure(figsize=(8, 4)) fig = plt.figure(figsize=(8, 4))
ax = fig.add_axes([0, 0, 1, 1]) ax = fig.add_axes([0, 0, 1, 1])
ax.axhline(y=0.5, color='black', linestyle='--') ax.axhline(y=0.5, color='black', linestyle='--')
ax.set_xticks(range(0, 256, 64)) ax.set_xticks(range(0, 256, 64))
ax.set_yticks(range(-256*2, 256*2+1, 128)) ax.set_yticks(range(-256*2, 256*2+1, 128))
ax.grid() ax.grid()
ax.plot(x, r, 'r') ax.plot(x, r, 'r')
ax.plot(x, g, 'g') ax.plot(x, g, 'g')
ax.plot(x, b, 'b') ax.plot(x, b, 'b')
def r(): def r():
start = 100 start = 100
start2 = 220 start2 = 220
last_a = 0 last_a = 0
for i in range(256): for i in range(256):
if i < start: if i < start:
yield 0 yield 0
elif i < start2: elif i < start2:
span = 255-start span = 255-start
d = (i-start)/span d = (i-start)/span
last_a = int(d*30.0) last_a = int(d*30.0)
yield min(255, last_a) yield min(255, last_a)
else: else:
span = 255-start2 span = 255-start2
d = (i-start2)/span d = (i-start2)/span
d = d*d*d d = d*d*d
yield min(255, last_a + int(d*350.0)) yield min(255, last_a + int(d*350.0))
def g(): def g():
start = 0 start = 0
end = 180-80 end = 180-80
scale = 0.25 scale = 0.25
for i in range(256): for i in range(256):
if i < start: if i < start:
yield 0 yield 0
elif i > end: elif i > end:
d = 1.0 - ((i-end)/(295-end)) d = 1.0 - ((i-end)/(295-end))
# d = (d*d)/4 + d/2 # d = (d*d)/4 + d/2
yield max(0, min(255, int(d*175*scale))) yield max(0, min(255, int(d*175*scale)))
else: else:
d = ((i-start)/(255-start)) d = ((i-start)/(255-start))
d = (d*d*d) d = (d*d*d)
yield min(255, int(d*2800*scale)) yield min(255, int(d*2800*scale))
def b(): def b():
start = 4 start = 4
end = 40 end = 40
scale = 0.2 scale = 0.2
for i in range(256): for i in range(256):
if i < start: if i < start:
yield 0 yield 0
elif i > end: elif i > end:
d = (i-end)/(60) d = (i-end)/(60)
d = d*d d = d*d
d = 1.0 - d d = 1.0 - d
# d = math.sqrt(d) # d = math.sqrt(d)
yield max(0, min(255, int(d*32*scale))) yield max(0, min(255, int(d*32*scale)))
else: else:
d = (i-start)/(255-start) d = (i-start)/(255-start)
d = math.sqrt(d)/2 + d/2 d = math.sqrt(d)/2 + d/2
yield max(0, min(255, int(d*107*scale))) yield max(0, min(255, int(d*107*scale)))
r, g, b = list(r()), list(g()), list(b()) r, g, b = list(r()), list(g()), list(b())
draw(r, g, b) draw(r, g, b)
text = "" text = ""
text += "// Lookup Table for Red LED Channel\n" text += "// Lookup Table for Red LED Channel\n"
text += f"int red_lookup[] = {{{', '.join(str(v) for v in r)}}};\n\n" text += f"int red_lookup[] = {{{', '.join(str(v) for v in r)}}};\n\n"
text += "// Lookup Table for Green LED Channel\n" text += "// Lookup Table for Green LED Channel\n"
text += f"int green_lookup[] = {{{', '.join(str(v) for v in g)}}};\n\n" text += f"int green_lookup[] = {{{', '.join(str(v) for v in g)}}};\n\n"
text += "// Lookup Table for Blue LED Channel\n" text += "// Lookup Table for Blue LED Channel\n"
text += f"int blue_lookup[] = {{{', '.join(str(v) for v in b)}}};\n\n" text += f"int blue_lookup[] = {{{', '.join(str(v) for v in b)}}};\n\n"
import ipywidgets as widgets import ipywidgets as widgets
from IPython.display import display, HTML, Javascript from IPython.display import display, HTML, Javascript
mybtn = widgets.Button(description='copy C++ to clipboard', button_style='success') mybtn = widgets.Button(description='copy C++ to clipboard', button_style='success')
def mybtn_event_handler(b): def mybtn_event_handler(b):
print("copied") print("copied")
clipboard.copy(text) clipboard.copy(text)
mybtn.on_click(mybtn_event_handler) mybtn.on_click(mybtn_event_handler)
display(mybtn) display(mybtn)
``` ```
%% Output %% Output
zsh:1: parse error near `-m' zsh:1: parse error near `-m'
%% Cell type:code id:41562cc6-9911-4fb1-87ec-f2b3c8bfb3c2 tags: %% Cell type:code id:41562cc6-9911-4fb1-87ec-f2b3c8bfb3c2 tags:
``` python ``` python
from matplotlib import pyplot as plt from matplotlib import pyplot as plt
%matplotlib inline %matplotlib inline
import math import math
def draw(r): def draw(r):
l = len(r) l = len(r)
x = [x for x in range(l)] x = [x for x in range(l)]
fig = plt.figure(figsize=(8, 4)) fig = plt.figure(figsize=(8, 4))
ax = fig.add_axes([0, 0, 1, 1]) ax = fig.add_axes([0, 0, 1, 1])
ax.axhline(y=0.5, color='black', linestyle='--') ax.axhline(y=0.5, color='black', linestyle='--')
ax.axvline(x=l/2, color='black', linestyle='--') ax.axvline(x=l/2, color='black', linestyle='--')
ax.set_xticks(range(0, l, 64)) ax.set_xticks(range(0, l, 64))
ax.set_yticks(range(-l*2, l*2+1, 128)) ax.set_yticks(range(-l*2, l*2+1, 128))
ax.grid() ax.grid()
ax.plot(x, r) ax.plot(x, r)
def deadband(length, deadband=0.04): def deadband(length, deadband=0.04):
readings = [] readings = []
for i in range(length): for i in range(length):
current_reading = i/(length-1) current_reading = i/(length-1)
scaler = (1.0) / (1.0 - deadband) scaler = (1.0) / (1.0 - deadband)
scaler += 0.1 scaler += 0.1
if current_reading < 0.5: if current_reading < 0.5:
current_reading += deadband current_reading += deadband
current_reading = min(0.5, current_reading) current_reading = min(0.5, current_reading)
current_reading = 0.5 - current_reading current_reading = 0.5 - current_reading
current_reading *= scaler current_reading *= scaler
current_reading = 0.5 - current_reading current_reading = 0.5 - current_reading
# current_reading = # current_reading =
else: else:
current_reading -= deadband current_reading -= deadband
current_reading = max(0.5, current_reading) current_reading = max(0.5, current_reading)
current_reading = 0.5 - current_reading current_reading = 0.5 - current_reading
current_reading *= scaler current_reading *= scaler
current_reading = 0.5 - current_reading current_reading = 0.5 - current_reading
val = min(length, max(0, current_reading)) val = min(length, max(0, current_reading))
readings.append(val) readings.append(val)
return readings return readings
bip = deadband(16, deadband = 0.08) bip = deadband(16, deadband = 0.08)
draw(bip) draw(bip)
text = "" text = ""
text += "// Lookup Table for Bipolar Curve with deadband\n" text += "// Lookup Table for Bipolar Curve with deadband\n"
text += f"float bip_lookup[] = {{{', '.join(str(v) for v in bip)}}};\n\n" text += f"float bip_lookup[] = {{{', '.join(str(v) for v in bip)}}};\n\n"
import ipywidgets as widgets import ipywidgets as widgets
from IPython.display import display, HTML, Javascript from IPython.display import display, HTML, Javascript
mybtn = widgets.Button(description='copy C++ to clipboard', button_style='success') mybtn = widgets.Button(description='copy C++ to clipboard', button_style='success')
def mybtn_event_handler(b): def mybtn_event_handler(b):
print("copied") print("copied")
clipboard.copy(text) clipboard.copy(text)
mybtn.on_click(mybtn_event_handler) mybtn.on_click(mybtn_event_handler)
display(mybtn) display(mybtn)
len(bip) len(bip)
``` ```
%% Output %% Output
16 16
%% Cell type:code id:ecc666b0-8195-4276-a576-39d41753b540 tags: %% Cell type:code id:ecc666b0-8195-4276-a576-39d41753b540 tags:
``` python ``` python
from matplotlib import pyplot as plt from matplotlib import pyplot as plt
%matplotlib inline %matplotlib inline
import math import math
import sys import sys
def draw(r): def draw(r):
l = len(r) l = len(r)
x = [x for x in range(l)] x = [x for x in range(l)]
fig = plt.figure(figsize=(8, 4)) fig = plt.figure(figsize=(8, 4))
ax = fig.add_axes([0, 0, 1, 1]) ax = fig.add_axes([0, 0, 1, 1])
ax.axhline(y=0.5, color='black', linestyle='--') ax.axhline(y=0.5, color='black', linestyle='--')
ax.set_xticks(range(0, l, 64)) ax.set_xticks(range(0, l, 64))
ax.set_yticks(range(-l*2, l*2+1, 128)) ax.set_yticks(range(-l*2, l*2+1, 128))
ax.grid() ax.grid()
ax.plot(x, r) ax.plot(x, r)
def lin_to_log(length, strength=1.0): def lin_to_log(length, strength=1.0):
# Limit to range 0.0 and 1.0 # Limit to range 0.0 and 1.0
strength = min(1.0, max(0.0, strength)) strength = min(1.0, max(0.0, strength))
readings = [] readings = []
linear_readings = [] linear_readings = []
for i in range(length): for i in range(length):
current_reading = i/length current_reading = i/length
linear_readings.append(current_reading) linear_readings.append(current_reading)
# Log of 0 is error, so handle it explicitly # Log of 0 is error, so handle it explicitly
if i == 0: if i == 0:
current_reading = 0.0 current_reading = 0.0
else: else:
current_reading = math.log10(i) current_reading = math.log10(i)
readings.append(current_reading) readings.append(current_reading)
# Normalize to scale 0.1 to one # Normalize to scale 0.1 to one
maxima = max(readings) maxima = max(readings)
scaler = 1.0 / maxima scaler = 1.0 / maxima
readings = [r*scaler for r in readings] readings = [r*scaler for r in readings]
output = [] output = []
for i, r in enumerate(readings): for i, r in enumerate(readings):
val = r*strength + linear_readings[i] * (1.0 - strength) val = r*strength + linear_readings[i] * (1.0 - strength)
output.append(val) output.append(val)
# Convert to integer value range # Convert to integer value range
output = [o for o in output] output = [o for o in output]
return output return output
# lilo = lin_to_log(4096, strength=1.0) # lilo = lin_to_log(4096, strength=1.0)
lilo = lin_to_log(32, strength=1.0) lilo = lin_to_log(32, strength=1.0)
# lilo = [l/256.0 for l in lilo] # lilo = [l/256.0 for l in lilo]
draw(lilo) draw(lilo)
text = "" text = ""
text += "// Lookup Table for Logarithmic Curve\n" text += "// Lookup Table for Logarithmic Curve\n"
text += f"float log_lookup[] = {{{', '.join(str(v) for v in lilo)}}};\n\n" text += f"float log_lookup[] = {{{', '.join(str(v) for v in lilo)}}};\n\n"
# print(text) # print(text)
import ipywidgets as widgets import ipywidgets as widgets
from IPython.display import display, HTML, Javascript from IPython.display import display, HTML, Javascript
mybtn = widgets.Button(description='copy C++ to clipboard', button_style='success') mybtn = widgets.Button(description='copy C++ to clipboard', button_style='success')
def mybtn_event_handler(b): def mybtn_event_handler(b):
print("copied") print("copied")
clipboard.copy(text) clipboard.copy(text)
mybtn.on_click(mybtn_event_handler) mybtn.on_click(mybtn_event_handler)
display(mybtn) display(mybtn)
min(lilo) min(lilo)
``` ```
%% Output %% Output
0.0 0.0
%% Cell type:code id:2ecfda42-4a3c-489a-bc90-8576648c339c tags: %% Cell type:code id:2ecfda42-4a3c-489a-bc90-8576648c339c tags:
``` python ``` python
from matplotlib import pyplot as plt from matplotlib import pyplot as plt
%matplotlib inline %matplotlib inline
import math import math
import sys import sys
def draw(r): def draw(r):
l = len(r) l = len(r)
x = [x for x in range(l)] x = [x for x in range(l)]
fig = plt.figure(figsize=(8, 4)) fig = plt.figure(figsize=(8, 4))
ax = fig.add_axes([0, 0, 1, 1]) ax = fig.add_axes([0, 0, 1, 1])
ax.axhline(y=0.5, color='black', linestyle='--') ax.axhline(y=0.5, color='black', linestyle='--')
ax.set_xticks(range(0, l, 64)) ax.set_xticks(range(0, l, 64))
ax.set_yticks(range(-l*2, l*2+1, 128)) ax.set_yticks(range(-l*2, l*2+1, 128))
ax.grid() ax.grid()
ax.plot(x, r) ax.plot(x, r)
def exp_lookup(length, strength=1.0): def exp_lookup(length, strength=1.0):
# Limit to range 0.0 and 1.0 # Limit to range 0.0 and 1.0
strength = min(1.0, max(0.0, strength)) strength = min(1.0, max(0.0, strength))
readings = [] readings = []
linear_readings = [] linear_readings = []
for i in range(length): for i in range(length):
current_reading = i/length current_reading = i/length
linear_readings.append(current_reading) linear_readings.append(current_reading)
# Log of 0 is error, so handle it explicitly # Log of 0 is error, so handle it explicitly
if i == 0: if i == 0:
current_reading = 0.0 current_reading = 0.0
else: else:
current_reading = i*i current_reading = i*i
readings.append(current_reading) readings.append(current_reading)
# Normalize to scale 0.1 to one # Normalize to scale 0.1 to one
maxima = max(readings) maxima = max(readings)
scaler = 1.0 / maxima scaler = 1.0 / maxima
readings = [r*scaler for r in readings] readings = [r*scaler for r in readings]
output = [] output = []
for i, r in enumerate(readings): for i, r in enumerate(readings):
val = r*strength + linear_readings[i] * (1.0 - strength) val = r*strength + linear_readings[i] * (1.0 - strength)
output.append(val) output.append(val)
# Convert to integer value range # Convert to integer value range
output = [o for o in output] output = [o for o in output]
return output return output
# lilo = lin_to_log(4096, strength=1.0) # lilo = lin_to_log(4096, strength=1.0)
lilo = exp_lookup(8, strength=1.0) lilo = exp_lookup(8, strength=1.0)
draw(lilo) draw(lilo)
text = "" text = ""
text += "// Lookup Table for Exponential Curve\n" text += "// Lookup Table for Exponential Curve\n"
text += f"float exp_lookup[] = {{{', '.join(str(v) for v in lilo)}}};\n\n" text += f"float exp_lookup[] = {{{', '.join(str(v) for v in lilo)}}};\n\n"
# print(text) # print(text)
import ipywidgets as widgets import ipywidgets as widgets
from IPython.display import display, HTML, Javascript from IPython.display import display, HTML, Javascript
mybtn = widgets.Button(description='copy C++ to clipboard', button_style='success') mybtn = widgets.Button(description='copy C++ to clipboard', button_style='success')
def mybtn_event_handler(b): def mybtn_event_handler(b):
print("copied") print("copied")
clipboard.copy(text) clipboard.copy(text)
mybtn.on_click(mybtn_event_handler) mybtn.on_click(mybtn_event_handler)
display(mybtn) display(mybtn)
max(lilo) max(lilo)
``` ```
%% Output %% Output
0.9999999999999999 0.9999999999999999
%% Cell type:code id:e51416f3-f34d-4513-9f0c-fa52c468274e tags: %% Cell type:code id:e51416f3-f34d-4513-9f0c-fa52c468274e tags:
``` python ``` python
from matplotlib import pyplot as plt from matplotlib import pyplot as plt
%matplotlib inline %matplotlib inline
from ipywidgets import interact, FloatSlider from ipywidgets import interact, FloatSlider
import matplotlib.transforms as transforms import matplotlib.transforms as transforms
import math import math
import clipboard import clipboard
def draw(f): def draw(f):
fig = plt.figure(figsize=(8, 4)) fig = plt.figure(figsize=(8, 4))
ax = fig.add_axes([0, 0, 1, 1]) ax = fig.add_axes([0, 0, 1, 1])
b = scan2d(x, y, f) b = scan2d(x, y, f)
ax.axhline(y=b, color='red', linestyle='--') ax.axhline(y=b, color='red', linestyle='--')
ax.axvline(x=f, color='red', linestyle='--') ax.axvline(x=f, color='red', linestyle='--')
trans = transforms.blended_transform_factory( trans = transforms.blended_transform_factory(
ax.get_yticklabels()[0].get_transform(), ax.transData) ax.get_yticklabels()[0].get_transform(), ax.transData)
ax.text(0.95, b, "{:.02f}".format(b), color="red", transform=trans, ha="right", va="bottom") ax.text(0.95, b, "{:.02f}".format(b), color="red", transform=trans, ha="right", va="bottom")
ax.grid() ax.grid()
ax.plot(x, y) ax.plot(x, y)
def lerp(a, b, f=0.5) -> float: def lerp(a, b, f=0.5) -> float:
f = min(1.0, max(0.0, f)) f = min(1.0, max(0.0, f))
if f == 0.0: if f == 0.0:
return a return a
elif f == 1.0: elif f == 1.0:
return b return b
else: else:
return a * (1.0-f) + b * f return a * (1.0-f) + b * f
def lerp2d(x1, y1, x2, y2, f=0.5): def lerp2d(x1, y1, x2, y2, f=0.5):
if f == 0.0: if f == 0.0:
return [x1, x2] return [x1, x2]
elif f == 1.0: elif f == 1.0:
return [x1, x2] return [x1, x2]
else: else:
x = lerp(x1, x2, f) x = lerp(x1, x2, f)
y = lerp(y1, y2, f) y = lerp(y1, y2, f)
return [x, y] return [x, y]
# A function that scans through two lists representing x/y values using a # A function that scans through two lists representing x/y values using a
# third value called f and returns the linear interpolation between those points # third value called f and returns the linear interpolation between those points
def scan2d(x, y, f): def scan2d(x, y, f):
# f = min(1.0, max(0.0, f)) # f = min(1.0, max(0.0, f))
assert len(x) == len(y) assert len(x) == len(y)
# Find ax and bx for given factor # Find ax and bx for given factor
xa = None xa = None
last_value = None last_value = None
idx = None idx = None
for i, v in enumerate(x): for i, v in enumerate(x):
# this = abs(f-v) # this = abs(f-v)
this = f-v this = f-v
if xa is None or this > 0: if xa is None or this > 0:
xa = this xa = this
idx = i idx = i
idx2 = min(idx+1, len(x)-1) idx2 = min(idx+1, len(x)-1)
if idx == idx2: if idx == idx2:
return y[idx] return y[idx]
xa = x[idx] xa = x[idx]
xb = x[idx2] xb = x[idx2]
ya = y[idx] ya = y[idx]
yb = y[idx2] yb = y[idx2]
xspan = xb-xa xspan = xb-xa
xscaler = 1/xspan xscaler = 1/xspan
new_f = (f-xa)*xscaler new_f = (f-xa)*xscaler
return lerp(ya, yb, new_f) return lerp(ya, yb, new_f)
lines_orig = [ lines_orig = [
[0.0, 0.0, -0.5], [0.0, 0.0, -0.5],
[0.45, 0.5, 0.0], [0.45, 0.5, 0.0],
[0.55, 0.5, 1.0], [0.55, 0.5, 1.0],
[1.0, 1.0, 0.0], [1.0, 1.0, 0.0],
] ]
half_deadband = 0.02 half_deadband = 0.005
lines_orig = [ lines_orig = [
[0.0, -1.0, 0.0], [0.0, -1.0, 0.0],
[0.0+half_deadband, -1.0, 0.0], [0.0+half_deadband, -1.0, 0.0],
] ]
steps = list(range(-9, 11)) steps = list(range(-9, 11))
for i in steps: for i in steps:
f = float(i) f = float(i)
print(f/10)
lines_orig.append([0.5+f/20-half_deadband, f/10, 0.0]) lines_orig.append([0.5+f/20-half_deadband, f/10, 0.0])
lines_orig.append([0.5+f/20+half_deadband, f/10, 0.0]) lines_orig.append([0.5+f/20+half_deadband, f/10, 0.0])
# Calculate curves for points of curvature # Calculate curves for points of curvature
lines = [] lines = []
for i, l in enumerate(lines_orig): for i, l in enumerate(lines_orig):
i2 = min(len(lines_orig)-1, i+1) i2 = min(len(lines_orig)-1, i+1)
if l[2] == 0.0: if l[2] == 0.0:
lines.append(l) lines.append(l)
else: else:
xa = lines_orig[i][0] xa = lines_orig[i][0]
xb = lines_orig[i2][0] xb = lines_orig[i2][0]
ya = lines_orig[i][1] ya = lines_orig[i][1]
yb = lines_orig[i2][1] yb = lines_orig[i2][1]
x_span = xb-xa x_span = xb-xa
y_span = yb-ya y_span = yb-ya
x_step = 1/20 x_step = 1/20
y_step = 1/20 y_step = 1/20
for j in range(20): for j in range(20):
x = x_step * j x = x_step * j
y = y_step * j y = y_step * j
y_curve = 0 y_curve = 0
if l[2] > 0.0: if l[2] > 0.0:
y_curve = y*y*y y_curve = y*y*y
else: else:
y_curve = y*y y_curve = y*y
y = (1.0-l[2]) * y + l[2] * y_curve y = (1.0-l[2]) * y + l[2] * y_curve
lines.append([xa+x*x_span, ya+y*y_span, 0.0]) lines.append([xa+x*x_span, ya+y*y_span, 0.0])
x = [a[0] for a in lines] x = [a[0] for a in lines]
y = [a[1] for a in lines] y = [a[1] for a in lines]
c = [a[2] for a in lines] c = [a[2] for a in lines]
# draw(x, y, 0.45/2) # draw(x, y, 0.45/2)
interact(draw, f=FloatSlider(min=min(x), max=max(x), step=0.001, value=0.2)) interact(draw, f=FloatSlider(min=min(x), max=max(x), step=0.001, value=0.2))
length = len(x)
text = "" text = ""
text += "// Lookup Table for Pitch Knob\n" text += "// Lookup Table for Pitch Knob\n"
text += f"float pitch_knob_x[] = {{{', '.join(str(xv) for xv in x)}}};\n\n" text += f"float pitch_knob_lookup_x[] = {{{', '.join(str(xv) for xv in x)}}};\n"
text += f"float pitch_knob_y[] = {{{', '.join(str(yv) for yv in y)}}};\n\n" text += f"float pitch_knob_lookup_y[] = {{{', '.join(str(yv) for yv in y)}}};\n"
text += f"size_t pitch_knob_lookup_length = {length};\n"
import ipywidgets as widgets import ipywidgets as widgets
from IPython.display import display, HTML, Javascript from IPython.display import display, HTML, Javascript
mybtn = widgets.Button(description='copy C++ to clipboard', button_style='success') mybtn = widgets.Button(description='copy C++ to clipboard', button_style='success')
def mybtn_event_handler(b): def mybtn_event_handler(b):
print("copied") print("copied")
clipboard.copy(text) clipboard.copy(text)
mybtn.on_click(mybtn_event_handler) mybtn.on_click(mybtn_event_handler)
display(mybtn) display(mybtn)
len(x)
``` ```
%% Output %% Output
-0.9
-0.8
-0.7
-0.6
-0.5
-0.4
-0.3
-0.2
-0.1
0.0
0.1
0.2
0.3
0.4
0.5
0.6
0.7
0.8
0.9
1.0
42
%% Cell type:code id:f35f1609-3a10-4dce-b7dd-201d79f2c39c tags: %% Cell type:code id:f35f1609-3a10-4dce-b7dd-201d79f2c39c tags:
``` python ``` python
# Saturation curve # Saturation curve
# X / Y / Curvature # X / Y / Curvature
lines_orig = [ lines_orig = [
[-1.5, -1.0, 1.0], [-1.5, -1.0, 1.0],
[-0.7, -0.7, 0.0], [-0.7, -0.7, 0.0],
[0.0, 0.0, 0.0], [0.0, 0.0, 0.0],
[0.7, 0.7, -1.4], [0.7, 0.7, -1.4],
[1.5, 1.0, 0.0], [1.5, 1.0, 0.0],
] ]
lines = [] lines = []
for i, l in enumerate(lines_orig): for i, l in enumerate(lines_orig):
i2 = min(len(lines_orig)-1, i+1) i2 = min(len(lines_orig)-1, i+1)
if l[2] == 0.0: if l[2] == 0.0:
lines.append(l) lines.append(l)
else: else:
xa = lines_orig[i][0] xa = lines_orig[i][0]
xb = lines_orig[i2][0] xb = lines_orig[i2][0]
ya = lines_orig[i][1] ya = lines_orig[i][1]
yb = lines_orig[i2][1] yb = lines_orig[i2][1]
x_span = xb-xa x_span = xb-xa
y_span = yb-ya y_span = yb-ya
x_step = 1/20 x_step = 1/20
y_step = 1/20 y_step = 1/20
for j in range(20): for j in range(20):
x = x_step * j x = x_step * j
y = y_step * j y = y_step * j
y_curve = 0 y_curve = 0
if l[2] > 0.0: if l[2] > 0.0:
y_curve = y*y*y y_curve = y*y*y
else: else:
y_curve = y*y y_curve = y*y
y = (1.0-l[2]) * y + l[2] * y_curve y = (1.0-l[2]) * y + l[2] * y_curve
lines.append([xa+x*x_span, ya+y*y_span, 0.0]) lines.append([xa+x*x_span, ya+y*y_span, 0.0])
x = [a[0] for a in lines] x = [a[0] for a in lines]
y = [a[1] for a in lines] y = [a[1] for a in lines]
c = [a[2] for a in lines] c = [a[2] for a in lines]
# draw(x, y, 0.45/2) # draw(x, y, 0.45/2)
interact(draw, f=FloatSlider(min=min(x), max=max(x), step=0.001, value=0.2)) interact(draw, f=FloatSlider(min=min(x), max=max(x), step=0.001, value=0.2))
``` ```
%% Output %% Output
<function __main__.draw(f)> <function __main__.draw(f)>
%% Cell type:code id:f0cd9d06-a15a-46b4-a3ef-4aee3d4f7cd0 tags: %% Cell type:code id:f0cd9d06-a15a-46b4-a3ef-4aee3d4f7cd0 tags:
``` python ``` python
# Midi to pitch # Midi to pitch
notes = [36, 48, 60] notes = [36, 48, 60]
for note in notes: for note in notes:
print((note-36)/12.0) print((note-36)/12.0)
``` ```
%% Output %% Output
0.0 0.0
1.0 1.0
2.0 2.0
%% Cell type:code id:59b56dbc-6852-4989-bc19-3525ee7caf8b tags: %% Cell type:code id:59b56dbc-6852-4989-bc19-3525ee7caf8b tags:
``` python ``` python
for i in range(0, 101):
x = i/100
print(x, x%0.1)
```
%% Output
0.0 0.0
0.01 0.01
0.02 0.02
0.03 0.03
0.04 0.04
0.05 0.05
0.06 0.06
0.07 0.07
0.08 0.08
0.09 0.09
0.1 0.0
0.11 0.009999999999999995
0.12 0.01999999999999999
0.13 0.03
0.14 0.04000000000000001
0.15 0.04999999999999999
0.16 0.06
0.17 0.07
0.18 0.07999999999999999
0.19 0.09
0.2 0.0
0.21 0.009999999999999981
0.22 0.01999999999999999
0.23 0.03
0.24 0.03999999999999998
0.25 0.04999999999999999
0.26 0.06
0.27 0.07
0.28 0.08000000000000002
0.29 0.08999999999999997
0.3 0.09999999999999998
0.31 0.009999999999999981
0.32 0.01999999999999999
0.33 0.03
0.34 0.04000000000000001
0.35 0.04999999999999996
0.36 0.05999999999999997
0.37 0.06999999999999998
0.38 0.07999999999999999
0.39 0.09
0.4 0.0
0.41 0.009999999999999953
0.42 0.019999999999999962
0.43 0.02999999999999997
0.44 0.03999999999999998
0.45 0.04999999999999999
0.46 0.06
0.47 0.06999999999999995
0.48 0.07999999999999996
0.49 0.08999999999999997
0.5 0.09999999999999998
0.51 0.009999999999999981
0.52 0.01999999999999999
0.53 0.03
0.54 0.04000000000000001
0.55 0.05000000000000002
0.56 0.060000000000000026
0.57 0.06999999999999992
0.58 0.07999999999999993
0.59 0.08999999999999994
0.6 0.09999999999999995
0.61 0.009999999999999953
0.62 0.019999999999999962
0.63 0.02999999999999997
0.64 0.03999999999999998
0.65 0.04999999999999999
0.66 0.06
0.67 0.07
0.68 0.08000000000000002
0.69 0.08999999999999991
0.7 0.09999999999999992
0.71 0.009999999999999926
0.72 0.019999999999999934
0.73 0.029999999999999943
0.74 0.03999999999999995
0.75 0.04999999999999996
0.76 0.05999999999999997
0.77 0.06999999999999998
0.78 0.07999999999999999
0.79 0.09
0.8 0.0
0.81 0.010000000000000009
0.82 0.019999999999999907
0.83 0.029999999999999916
0.84 0.039999999999999925
0.85 0.04999999999999993
0.86 0.05999999999999994
0.87 0.06999999999999995
0.88 0.07999999999999996
0.89 0.08999999999999997
0.9 0.09999999999999998
0.91 0.009999999999999981
0.92 0.01999999999999999
0.93 0.03
0.94 0.0399999999999999
0.95 0.049999999999999906
0.96 0.059999999999999915
0.97 0.06999999999999992
0.98 0.07999999999999993
0.99 0.08999999999999994
1.0 0.09999999999999995
%% Cell type:code id:5a511d24-cc91-450f-83e4-8295647d9391 tags:
``` python
``` ```
......
...@@ -241,7 +241,7 @@ void setup() { ...@@ -241,7 +241,7 @@ void setup() {
// Set the analog read and write resolution to 12 bits // Set the analog read and write resolution to 12 bits
analogReadResolution(12); analogReadResolution(12);
// // Setup MIDI handlers
MIDI.setHandleNoteOn(handleNoteOn); MIDI.setHandleNoteOn(handleNoteOn);
MIDI.setHandleNoteOff(handleNoteOff); MIDI.setHandleNoteOff(handleNoteOff);
MIDI.begin(MIDI_CHANNEL_OMNI); // Listen to all incoming messages MIDI.begin(MIDI_CHANNEL_OMNI); // Listen to all incoming messages
...@@ -256,7 +256,8 @@ void setup() { ...@@ -256,7 +256,8 @@ void setup() {
pot_7.setDisplayMode("LFO", 100.0f, POT_DISPLAY_MODE_PERCENT); pot_7.setDisplayMode("LFO", 100.0f, POT_DISPLAY_MODE_PERCENT);
// Set Knob Scaling Modes // Set Knob Scaling Modes
pot_3.setBipolar(); // pot_3.setBipolar();
pot_3.setPitch();
// Initialize Buttons (callbacks are assigned in the Ui class) // Initialize Buttons (callbacks are assigned in the Ui class)
button_1.init(); button_1.init();
......
#ifndef LUTs_h #ifndef LUTs_h
#define LUTs_h #define LUTs_h
#include "MultiMap.h"
// Lookup Table for Exponential Curve // Lookup Table for Exponential Curve
float exp_lookup[] = {0.0, 0.02040816326530612, 0.08163265306122448, 0.18367346938775508, 0.32653061224489793, 0.5102040816326531, 0.7346938775510203, 1.0}; float exp_lookup[] = {0.0, 0.02040816326530612, 0.08163265306122448, 0.18367346938775508, 0.32653061224489793, 0.5102040816326531, 0.7346938775510203, 1.0};
size_t exp_lookup_length = 8;
// Lookup Table for Bipolar Curve with deadband // Lookup Table for Bipolar Curve with deadband
float bip_lookup[] = {0.0, 0.08060869565217388, 0.1597391304347826, 0.23886956521739133, 0.318, 0.3971304347826087, 0.47626086956521746, 0.5, 0.5, 0.5237391304347826, 0.6028695652173913, 0.6819999999999999, 0.7611304347826088, 0.8402608695652175, 0.9193913043478261, 1.0}; float bip_lookup[] = {0.0, 0.08060869565217388, 0.1597391304347826, 0.23886956521739133, 0.318, 0.3971304347826087, 0.47626086956521746, 0.5, 0.5, 0.5237391304347826, 0.6028695652173913, 0.6819999999999999, 0.7611304347826088, 0.8402608695652175, 0.9193913043478261, 1.0};
size_t bip_lookup_length = 16;
// Lookup Table for Pitch Knob
float pitch_knob_lookup_x[] = {0.0, 0.005, 0.04499999999999999, 0.054999999999999986, 0.09499999999999997, 0.10499999999999998, 0.14500000000000002, 0.15500000000000003, 0.195, 0.20500000000000002, 0.245, 0.255, 0.295, 0.305, 0.345, 0.355, 0.395, 0.405, 0.445, 0.455, 0.495, 0.505, 0.545, 0.555, 0.595, 0.605, 0.645, 0.655, 0.695, 0.705, 0.745, 0.755, 0.795, 0.805, 0.845, 0.855, 0.895, 0.905, 0.945, 0.955, 0.995, 1.005};
float pitch_knob_lookup_y[] = {-1.0, -1.0, -0.9, -0.9, -0.8, -0.8, -0.7, -0.7, -0.6, -0.6, -0.5, -0.5, -0.4, -0.4, -0.3, -0.3, -0.2, -0.2, -0.1, -0.1, 0.0, 0.0, 0.1, 0.1, 0.2, 0.2, 0.3, 0.3, 0.4, 0.4, 0.5, 0.5, 0.6, 0.6, 0.7, 0.7, 0.8, 0.8, 0.9, 0.9, 1.0, 1.0};
size_t pitch_knob_lookup_length = 42;
...@@ -21,9 +29,8 @@ float lerp(float a, float b, float f) { ...@@ -21,9 +29,8 @@ float lerp(float a, float b, float f) {
else { return a * (1.0f-f) + b * f; } else { return a * (1.0f-f) + b * f; }
} }
float get_from_table(float* table, float f) { float get_from_table(float* table, float f, size_t length) {
f = min(1.0f, max(0.0f, f)); f = min(1.0f, max(0.0f, f));
int length = sizeof(table)*sizeof(float);
float pos = (length-1) * f; float pos = (length-1) * f;
float pos_frac = fmod(pos, 1.0f); float pos_frac = fmod(pos, 1.0f);
if (pos_frac == 0.0f) { if (pos_frac == 0.0f) {
...@@ -35,4 +42,8 @@ float get_from_table(float* table, float f) { ...@@ -35,4 +42,8 @@ float get_from_table(float* table, float f) {
return lerp(a, b, pos_frac); return lerp(a, b, pos_frac);
} }
float get_from_xy_table(float* xtable, float* ytable, float f, size_t length) {
return multiMap<float>(f, xtable, ytable, length);
}
#endif #endif
\ No newline at end of file
...@@ -24,6 +24,7 @@ enum PotMode { ...@@ -24,6 +24,7 @@ enum PotMode {
POT_MODE_LIN, POT_MODE_LIN,
POT_MODE_EXP, POT_MODE_EXP,
POT_MODE_BIP, POT_MODE_BIP,
POT_MODE_PITCH,
POT_MODE_LAST POT_MODE_LAST
}; };
...@@ -52,6 +53,7 @@ class Potentiometer { ...@@ -52,6 +53,7 @@ class Potentiometer {
void setLinear(); void setLinear();
void setExponential(); void setExponential();
void setBipolar(); void setBipolar();
void setPitch();
float read(); float read();
void setOnChange(callback_function f); void setOnChange(callback_function f);
void renderUi(); void renderUi();
...@@ -82,6 +84,12 @@ void Potentiometer::setBipolar() { ...@@ -82,6 +84,12 @@ void Potentiometer::setBipolar() {
this->mode = POT_MODE_BIP; this->mode = POT_MODE_BIP;
} }
void Potentiometer::setPitch() {
this->mode = POT_MODE_PITCH;
}
float Potentiometer::read() { float Potentiometer::read() {
int reading = analogRead(this->pin); int reading = analogRead(this->pin);
// Shift all readings in the buffer over by one position, deleting the oldest // Shift all readings in the buffer over by one position, deleting the oldest
...@@ -103,27 +111,30 @@ float Potentiometer::read() { ...@@ -103,27 +111,30 @@ float Potentiometer::read() {
// Convert the last reading to a float and return // Convert the last reading to a float and return
float current_reading = reading / 4096.0f; float current_reading = reading / 4096.0f;
float normalized_reading = current_reading;
// Depending on the Mode // Depending on the Mode
if (this->mode == POT_MODE_EXP ) { switch (this->mode) {
case POT_MODE_EXP:
current_reading = get_from_table(exp_lookup, current_reading); current_reading = get_from_table(exp_lookup, current_reading, exp_lookup_length);
} break;
if (this->mode == POT_MODE_BIP) { case POT_MODE_BIP:
current_reading = get_from_table(bip_lookup, current_reading); current_reading = get_from_table(bip_lookup, current_reading, bip_lookup_length);
// Bipolar Knobs go from -1.0 to +1.0 (Centered = 0.0)
current_reading = (current_reading - 0.5f) * 2.0f; current_reading = (current_reading - 0.5f) * 2.0f;
break;
case POT_MODE_PITCH:
current_reading = get_from_xy_table(pitch_knob_lookup_x, pitch_knob_lookup_y, current_reading, pitch_knob_lookup_length);
break;
} }
// If the difference to the last reading is big enough assume the knob has been touched // If the difference to the last reading is big enough assume the knob has been touched
if (this->last_reading && abs(current_reading - this->last_reading) > 0.002) { if (this->last_reading && abs(current_reading - this->last_reading) > 0.002) {
// Serial.print("Touched Potentiometer "); Serial.print("Touched Potentiometer ");
// Serial.print(this->name); Serial.print(this->name);
// Serial.print(": "); Serial.print(": ");
// Serial.print(input); Serial.print(normalized_reading);
// Serial.print(" --> "); Serial.print(" --> ");
// Serial.println(current_reading); Serial.println(current_reading);
if (display_value_changes) { if (display_value_changes) {
last_displayed = millis(); last_displayed = millis();
should_display = true; should_display = true;
...@@ -173,6 +184,14 @@ void Potentiometer::renderUi() { ...@@ -173,6 +184,14 @@ void Potentiometer::renderUi() {
display.fillTriangle(x_center, y_center+10, x_center+3, y_center+15, x_center-3, y_center+15, SH110X_WHITE); display.fillTriangle(x_center, y_center+10, x_center+3, y_center+15, x_center-3, y_center+15, SH110X_WHITE);
} }
// If we are on a pitch pot display an indicator if we are in the the right steps
if (this->mode == POT_MODE_PITCH) {
float reading_mod = fmod(abs(this->last_reading), 0.05f);
if (reading_mod > 0.999f || reading_mod < 0.001f) {
display.fillTriangle(x_center, y_center+10, x_center+3, y_center+15, x_center-3, y_center+15, SH110X_WHITE);
}
}
// The float value may contain some empty whitespace characters, remove them by // The float value may contain some empty whitespace characters, remove them by
// first figuring out which the first actual character is // first figuring out which the first actual character is
int nonwhite = 0; int nonwhite = 0;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment