# sudo apt-get install i2c-tools jack libportmidi python-dev python-gpiozero python-smbus | |
# pip install mido | |
# pip install adafruit-mcp3008 | |
# Enable I2C in raspi-config. | |
# Install Adafruit_LED_Backpack from Github. | |
from Adafruit_LED_Backpack.AlphaNum4 import AlphaNum4 | |
from gpiozero import Button, LED, MCP3008 | |
from gpiozero.tools import smoothed, zip_values | |
import mido | |
def cc(control, value): | |
return mido.Message('control_change', control=control, value=value) | |
MODES = [ | |
{ | |
# Filter / Resonance | |
'display': 'FiRe', | |
'knobs': [ | |
{ | |
'name': 'Filter', | |
'value': lambda x: x / 8, | |
'msgs': lambda val: [cc(74, val)] | |
}, | |
{ | |
'name': 'Resonance', | |
'value': lambda x: x / 8, | |
'msgs': lambda val: [cc(71, val)] | |
} | |
] | |
}, | |
{ | |
# Attack / Release | |
'display': ' A R', | |
'knobs': [ | |
{ | |
'name': 'Attack', | |
'value': lambda x: x / 8, | |
'msgs': lambda val: [cc(73, val)] | |
}, | |
{ | |
'name': 'Release', | |
'value': lambda x: x / 8, | |
'msgs': lambda val: [cc(72, val)] | |
} | |
] | |
}, | |
{ | |
# Decay / Sustain | |
'display': ' D S', | |
'knobs': [ | |
{ | |
'name': 'Decay/Hold', | |
'value': lambda x: x / 8, | |
'msgs': lambda val: [cc(75, val)] | |
}, | |
{ | |
'name': 'Variation/Sustain', | |
'value': lambda x: x / 8, | |
'msgs': lambda val: [cc(70, val)] | |
} | |
] | |
}, | |
{ | |
# Pitch wheel / Portamento | |
'display': 'PiPo', | |
'knobs': [ | |
{ | |
'name': 'Pitchwheel', | |
'value': lambda x: (x - 512) * 16, | |
'formatted_value': lambda val: '%+04d' % (round(val / 4096.0, 2) * 100), | |
'msgs': lambda val: [mido.Message('pitchwheel', pitch=val)] | |
}, | |
{ | |
'name': 'Portamento', | |
'value': lambda x: x / 8, | |
'formatted_value': lambda val: str(val) if val > 10 else 'off', | |
'msgs': lambda val: [cc(5, val), cc(65, 127 if val > 10 else 0)] | |
} | |
] | |
} | |
] | |
TOLERANCE = 24 | |
class MidiController: | |
def __init__(self): | |
port_name = [n for n in mido.get_output_names() | |
if n.startswith('SV1')][0] | |
self.port = mido.open_output(port_name) | |
self.blue_button = Button(21) | |
self.led = LED(26) | |
self.display = AlphaNum4() | |
self.display_timeout = None | |
knob1 = MCP3008(channel=1, clock_pin=18, mosi_pin=24, | |
miso_pin=23, select_pin=25) | |
knob2 = MCP3008(channel=0, clock_pin=18, mosi_pin=24, | |
miso_pin=23, select_pin=25) | |
self.knobs = [smoothed(knob1, 20), smoothed(knob2, 20)] | |
self.values = self.get_knob_values() | |
self.mode_num = 0 | |
self.mode = MODES[self.mode_num] | |
def teardown(self): | |
self.led.off() | |
self.print_to_display('') | |
def get_knob_values(self): | |
return [int(next(knob) * 1024) for knob in self.knobs] | |
def next_mode(self): | |
self.mode_num = (self.mode_num + 1) % len(MODES) | |
self.mode = MODES[self.mode_num] | |
self.print_to_display(self.mode['display']) | |
def print_to_display(self, msg): | |
self.display_msg = msg | |
self.display.clear() | |
self.display.print_str(msg) | |
self.display.write_display() | |
def print_temp_to_display(self, msg, timeout): | |
self.display_timeout = timeout | |
self.display.clear() | |
self.display.print_str(msg) | |
self.display.write_display() | |
def send_cc(self, knob_idx, raw_value): | |
knob_opts = self.mode['knobs'][knob_idx] | |
true_value = knob_opts['value'](raw_value) | |
formatted_value = knob_opts['formatted_value'](true_value) if 'formatted_value' in knob_opts else str(true_value) | |
print('Setting {} to {} ...'.format(knob_opts['name'], formatted_value)) | |
self.print_temp_to_display(formatted_value, 10) | |
for msg in knob_opts['msgs'](true_value): | |
self.port.send(msg) | |
def run(self): | |
self.led.on() | |
self.display.begin() | |
self.print_to_display(self.mode['display']) | |
try: | |
self.blue_button.when_pressed = self.next_mode | |
while True: | |
next_values = self.get_knob_values() | |
for i in range(len(self.knobs)): | |
if abs(next_values[i] - self.values[i]) > TOLERANCE or (next_values[i] == 0 and self.values[i] > 0): | |
self.values[i] = next_values[i] | |
self.send_cc(i, next_values[i]) | |
if self.display_timeout: | |
self.display_timeout -= 1 | |
if self.display_timeout <= 0: | |
self.display_timeout = None | |
self.print_to_display(self.display_msg) | |
except KeyboardInterrupt: | |
pass | |
finally: | |
self.teardown() | |
def main(): | |
mido.set_backend('mido.backends.portmidi') | |
MidiController().run() | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment