diff --git a/main-micropython.py b/main-micropython.py new file mode 100644 index 0000000..134fd0b --- /dev/null +++ b/main-micropython.py @@ -0,0 +1,40 @@ +import sys +from machine import ADC, Pin +import pyb +import midi + +# Devices +exp = machine.ADC(31) # Expression pedal device on pin 31 +serial = pyb.USB_VCP() +led = Pin(25, Pin.OUT) + +# Expression pedal settings +exp_min = 0 # Expression pedal minimum value +exp_max = 65535 #Expression pedal maximum value + +# Midi settings +midi_channel = 1 # Target midi channel to write to +cc = 68 # Target Control Change number - this is for Behringer X32 Matrix 5 +cc_min = 0 # Minimum desired CC output +cc_max = 100 # Maximum desired CC output (only want fader to go to unity gain - hence not 127) + +# This function translates the expression pedal value to the equivalent CC value +def translate(exp_val): + return (((exp_val - exp_min) * (cc_max - cc_min)) / (exp_max - exp_min)) + cc_min + +# Test the translate function: +# for i in range(65535): +# print (translate(i)) + +usb_midi = midi.Controller(serial, channel=midi_channel) +exp_value_previous = 0 + +while True: + exp_value_current = exp.read_u16() + if exp_value_current != exp_value_previous: + led.value(1) # Turn led on + cc_val = translate(exp_value_current) + usb_midi.control_change(cc, cc_val) + led.value(0) # Turn led off + exp_value_previous = exp_value_current + print("Writing CC value " + cc_val) \ No newline at end of file diff --git a/main.py b/main.py index 134fd0b..6d68c29 100644 --- a/main.py +++ b/main.py @@ -1,40 +1,37 @@ import sys -from machine import ADC, Pin -import pyb -import midi +import usb_midi +import adafruit_midi +from adafruit_midi.control_change import ControlChange +import digitalio +import analogio +from board import * # Devices -exp = machine.ADC(31) # Expression pedal device on pin 31 -serial = pyb.USB_VCP() -led = Pin(25, Pin.OUT) +exp = analogio.AnalogIn(A0) # Expression pedal +led = digitalio.DigitalInOut(LED) # Expression pedal settings exp_min = 0 # Expression pedal minimum value exp_max = 65535 #Expression pedal maximum value # Midi settings -midi_channel = 1 # Target midi channel to write to +midi_channel = 0 # Target midi channel to write to cc = 68 # Target Control Change number - this is for Behringer X32 Matrix 5 cc_min = 0 # Minimum desired CC output cc_max = 100 # Maximum desired CC output (only want fader to go to unity gain - hence not 127) # This function translates the expression pedal value to the equivalent CC value def translate(exp_val): - return (((exp_val - exp_min) * (cc_max - cc_min)) / (exp_max - exp_min)) + cc_min + return int((((exp_val - exp_min) * (cc_max - cc_min)) / (exp_max - exp_min)) + cc_min) -# Test the translate function: -# for i in range(65535): -# print (translate(i)) - -usb_midi = midi.Controller(serial, channel=midi_channel) -exp_value_previous = 0 +midi = adafruit_midi.MIDI(midi_out=usb_midi.ports[1], out_channel=midi_channel) +cc_val_last = 0 +led.switch_to_output(value=True) while True: - exp_value_current = exp.read_u16() - if exp_value_current != exp_value_previous: - led.value(1) # Turn led on - cc_val = translate(exp_value_current) - usb_midi.control_change(cc, cc_val) - led.value(0) # Turn led off - exp_value_previous = exp_value_current - print("Writing CC value " + cc_val) \ No newline at end of file + cc_val = translate(exp.value) + if cc_val != cc_val_last: + led.switch_to_output(value=False) # Flicker led + midi.send(ControlChange(cc, cc_val)) # Write midi + led.switch_to_output(value=True) + cc_val_last = cc_val \ No newline at end of file diff --git a/midi.py b/midi.py deleted file mode 100644 index a1d6461..0000000 --- a/midi.py +++ /dev/null @@ -1,224 +0,0 @@ -# Copyright (C) 2014 Craig Barnes -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -""" -Micro Python Midi -This module implements channel commands according to the midi specification. -Each midi message consists of 3 bytes. -The first byte is the sum of the command and the midi channel (1-16 > 0-F). -the value of bytes 2 and 3 (data 1 and 2) are dependant on the command. -command data1 data2 Description -------- ----- ----- ----------- -0x80-0x8F Key # (0-127) Off Velocity (0-127) Note Off -0x90-0x90 Key # (0-127) On Velocity (0-127) Note On -0xA0-0xA0 Key # (0-127) Pressure (0-127) Poly Pressure -0xB0-0xB0 Control # (0-127) Control Value (0-127) Control -0xC0-0xC0 Program # (0-127) Not Used (send 0) Program Change -0xD0-0xD0 Pressure Value (0-127) Not Used (send 0) Channel Pressure -0xE0-0xE0 Range LSB (0-127) Range MSB (0-127) Pitch Bend -http://www.midi.org/techspecs/midimessages.php -""" - - -class MidiInteger: - """A midi message sends data as 7 bit values between 0 and 127.""" - def __init__(self, value): - if 0 <= value < 2 ** 7: - self.value = value - else: - raise ValueError( - 'Invalid midi data value: {}'.format(value), - 'A midi data value must be an integer between 0 and 127') - - def __repr__(self): - return ''.format(self.value) - - -class BigMidiInteger: - """Some messages use 14 bit values, these need to be spit down to - msb and lsb before being sent.""" - def __init__(self, value): - if 0 <= value <= 2 ** 14: - self.msb = value // 2 ** 7 - self.lsb = value % 2 ** 7 - else: - raise ValueError( - 'Invalid midi data value: {}'.format(value), - 'A midi datavalue must be an integer between0' - ' and {}'.format(2 ** 14)) - - def __repr__(self): - return ''.format(self.lsb, self.msb) - - -class Controller: - """A device that is designed to send midi messages to an external - instrument or sequencer, is commonly referred to as a midi controller. - http://en.wikipedia.org/wiki/MIDI_controller - An instance of the Controller class should be considered to be a midi - contoller in the same context. - More than one can be created, there are no constraints. but the - convention is to keep each controller on a separate midi port or channel. - Usage - ===== - The following example creates a controller on midi channel one using the - USB Virtual Com Port device. Then sends a note on message followed by a - note off message. - >>> import pyb - >>> my_controller = Controller(pyb.USB_VCP(), 1) - >>> my_controller.note_on(65) - >>> pyb.delay(100) - >>> my_controller.note_off(65) - In order to pass these midi signals on to your computers midi stack, you - will need to disconnect your console from the Micro Python board, - and run a serial to midi bridge utility such as ttymidi for Linux. - Others are available for Mac and Windows. - With your console still attached you would have seen the characters - representing the bytes. - The example above demonstrates the nature of midi note information. - When a keyboard key is pressed a note_on message is sent. When the - key is released a note off message is sent. - An optional velocity value can also be sent, this usually controls - the volume of the note played, but this is often used to control other - characteristics of the sound played. - """ - COMMANDS = ( - 0x80, # Note Off - 0x90, # Note On - 0xA0, # Poly Pressure - 0xB0, # Control Change - 0xC0, # Program Change - 0xD0, # Mono Pressure - 0xE0 # Pich Bend - ) - - def __init__(self, port, channel=1): - self.port = port - try: - assert 1 <= channel <= 16 - except: - raise ValueError('channel must be an integer between 1 & 16') - self.channel = channel - self.timeout = 100 - - def __repr__(self): - return ''.format( - port=self.port, - channel=self.channel) - - def send_message(self, command, data1, data2=0): - """Send a midi message to the serial device.""" - if command not in self.COMMANDS: - raise ValueError('Invalid Command: {}'.format(command)) - - command += self.channel - 1 - - self.port.send(command, timeout=self.timeout) - self.port.send(MidiInteger(data1).value, timeout=self.timeout) - self.port.send(MidiInteger(data2).value, timeout=self.timeout) - - def note_off(self, note, velocity=0): - """Send a 'Note Off' message""" - self.send_message(0x80, note, velocity) - - def note_on(self, note, velocity=127): - """Send a 'Note On' message""" - self.send_message(0x90, note, velocity) - - def pressure(self, value, note=None): - """If a note value is provided then send a polyphonic pressure - message, otherwise send a Channel (mono) pressure message.""" - if note: - self.send_message(0xA0, note, value) - else: - self.send_message(0xD0, value) - - def control_change(self, control, value): - """Send a control e.g. modulation or pedal message.""" - self.send_message(0xB0, control, value) - - def program_change(self, value, bank=None): - """Send a program change message, include bank if provided.""" - if bank: - bank = BigMidiInteger(bank) - self.control_change(32, bank.lsb) - self.control_change(0, bank.msb) - self.send_message(0xC0, value) - - def pitch_bend(self, value=0x2000): - """Send a pich bend message. - Pich bend is a 14 bit value, centreed at 0x2000""" - value = BigMidiInteger(value) - self.send_message(0xE0, value.lsb, value.msb) - - def modulation(self, value, fine=False): - """Send modulation control change.""" - if fine: - value = MidiInteger(value) - self.control_change(33, value.lsb) - self.control_change(1, value.msb) - else: - self.control_change(1, value) - - def volume(self, value, fine=False): - """Send volume control change.""" - if fine: - value = BigMidiInteger(value) - self.control_change(39, value.lsb) - self.control_change(7, value.msb) - else: - self.control_change(7, value) - - def all_sound_off(self): - """Switch all sounds off. """ - self.control_change(120, 0) - - def reset_all_controllers(self): - """Set all controllers to their default values.""" - self.control_change(121, 0) - - def local_control(self, value): - """Enable or disable local control.""" - if bool(value): - self.control_change(122, 127) - else: - self.control_change(122, 0) - - def all_notes_off(self): - """Send 'All Notes Off' message.""" - self.control_change(123, 0) - - def panic(self): - """Reset everything and stop making noise.""" - self.all_sound_off() - self.reset_all_controllers() - self.all_notes_off() - - -if __name__ == '__main__': - import pyb - serial = pyb.USB_VCP() - instrument1 = Controller(serial, channel=1) - accel = pyb.Accel() - switch = pyb.Switch() - while 1: - while not switch(): - pyb.delay(10) - note = accel.x() - velocity = accel.y() - instrument1.note_on(+(note + 65), +(velocity + 65)) - while switch(): - pyb.delay(50) - instrument1.note_off(note + 65) \ No newline at end of file