Created
July 20, 2020 23:57
-
-
Save PeteMidi/cfdd9483c8f5fa4433a2bcfead9c0ada to your computer and use it in GitHub Desktop.
TD-3 arpeggiator in Python 3 using numpy and Mido
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
#!/usr/bin/env python3 | |
# -*- coding: utf-8 -*- | |
""" | |
Created on July 7 2020 | |
@author: Pete Midi | |
""" | |
import mido | |
import numpy as np | |
# print available port names | |
print('Inputs:') | |
inputs = mido.get_input_names() | |
print(inputs) | |
print('Outputs:') | |
outputs = mido.get_output_names() | |
print(outputs) | |
# print received sequence notes | |
print_note_flag = False | |
# open ports | |
# inputs | |
name_in_td3 = 'mio10:mio10 MIDI 1' # sequence master | |
name_in_kbd = 'QuNexus' # keyboard chord notes | |
port_in_td3 = mido.open_input([s for s in inputs if s.startswith(name_in_td3)][0]) | |
port_in_kbd = mido.open_input([s for s in inputs if s.startswith(name_in_kbd)][0]) | |
# outputs | |
name_out_td3 = 'mio10:mio10 MIDI 1' # arpeggio slave | |
port_out_td3 = mido.open_output([s for s in outputs if s.startswith(name_out_td3)][0]) | |
#name_out_fx = 'mio10:mio10 MIDI 2' # delay effect clock sync | |
#port_out_fx = mido.open_output([s for s in outputs if s.startswith(name_out_fx)][0]) | |
# midi channels (0,1,...,15) | |
chan_out_td3 = 1 # arpeggio channel out | |
chan_in_td3 = 0 # trigger sequence channel in | |
# init | |
msg_ctrl = mido.Message('control_change', control=123, value=0, channel=chan_out_td3) # all notes off | |
msg_note = mido.Message('note_on', note=60, velocity=0, channel=chan_out_td3) | |
port_out_td3.send(msg_ctrl) | |
# helper function to close all midi ports | |
def close_ports(): | |
#%% close ports | |
print('close ports!') | |
vd = locals() | |
keys = list(vd.keys()) | |
for k in keys: | |
if k.startswith('port'): | |
if k.startswith('port_out_td3'): | |
for note_num in range(128): | |
msg_note = mido.Message('note_on', note=note_num, velocity=0, channel=chan_out_td3) | |
vd[k].send(msg_note) | |
vd[k].close() | |
# trigger map definition: sequence master input trigger notes | |
trig_c2c3 = np.array([36,48], dtype=int) | |
trig_c2c3c4 = np.array([36,48,60], dtype=int) | |
trig_c2g2c3 = np.array([36,43,48], dtype=int) | |
trig_c2g2c3g3 = np.array([36,43,48,55], dtype=int) | |
trig_c2c3g3c4 = np.array([36,48,55,60], dtype=int) | |
trig_vec = trig_c2g2c3 # trigger map selection | |
# chord note ring buffer init | |
chord_in_vec = np.array(trig_vec, dtype = np.int8) | |
chord_on_vec = np.full(trig_vec.size, -1, dtype = np.int8) | |
chord_idx = 0 # chord note write index (position) | |
# chord index reset time | |
reset_time = 48 # clock tics (96 per measure) | |
clock_counter = 0 | |
clock_counter_old = 0 | |
stop_flag = False | |
note_stop = -1 # exit script note number - change this acc. to your keyboard range! | |
# loop | |
while not stop_flag: | |
for msg in port_in_td3.iter_pending(): | |
if msg.type == 'start' or msg.type == 'stop': | |
print(clock_counter) | |
clock_counter = 0 | |
#port_out_fx.send(msg) | |
if msg.type == 'clock': | |
clock_counter += 1 | |
#port_out_fx.send(msg) | |
if msg.type == 'note_on' and msg.channel == chan_in_td3: # arpeggiator trigger notes | |
if print_note_flag: | |
print(msg) | |
i_vec = (trig_vec==msg.note) | |
if i_vec.max() > 0: | |
msg.channel = chan_out_td3 | |
note_out = chord_in_vec[i_vec.argmax()] | |
chord_on_vec[i_vec.argmax()] = note_out | |
msg.note = note_out | |
port_out_td3.send(msg) | |
if msg.type == 'note_off' and msg.channel == chan_in_td3: # arpeggiator trigger notes | |
i_vec = (trig_vec==msg.note) | |
if i_vec.max() > 0: | |
msg.channel = chan_out_td3 | |
msg.velocity = 0 | |
note_out = chord_on_vec[i_vec.argmax()] | |
if note_out < 0: # faulty ! | |
note_out = chord_in_vec[i_vec.argmax()] | |
else: | |
chord_on_vec[i_vec.argmax()] = -1 | |
msg.note = note_out | |
port_out_td3.send(msg) | |
for msg in port_in_kbd.iter_pending(): | |
if msg.type == 'note_on' and msg.velocity > 0: # arpeggiator chord notes | |
if clock_counter - clock_counter_old > reset_time: | |
chord_idx = 0 # start filling array from begin | |
else: | |
chord_idx = (chord_idx + 1) % len(chord_in_vec) | |
clock_counter_old = clock_counter | |
chord_in_vec[chord_idx] = msg.note | |
# print(chord_in_vec) | |
if msg.note == note_stop: | |
stop_flag = True | |
stop_flag = False | |
close_ports() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment