-
-
Save AlexNisnevich/00f451118550035b780995caa2733f92 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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