Last active
July 30, 2016 13:23
-
-
Save igneus/fb3f44f71c74c8c4d3ec80906ba081f7 to your computer and use it in GitHub Desktop.
mididings patch transforming a single-manual MIDI piano with standard three piano pedals to a two-manual organ with pedal (sort of)
This file contains hidden or 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
# mididings patch making a single-manual MIDI piano with standard | |
# three piano pedals capable of simple organ music: | |
# * splits keyboard in two manuals (MIDI channels 1, 2) | |
# * transforms the piano pedals in a diatonic | |
# single-octave organ pedal playing on MIDI channel 4 | |
# (it isn't possible to play more simultaneous pedal notes - | |
# combinations are instead used to expand the pedal's range) | |
from mididings import * | |
from mididings.event import CtrlEvent | |
config( | |
client_name='threefold_piano', | |
) | |
octave = 12 # semitones | |
# MIDI ctrl ids | |
pedal_soft_ctrl = 67 | |
pedal_sostenuto_ctrl = 66 | |
pedal_sustain_ctrl = 64 | |
class RemoveDuplicateCtrlEvents: | |
"""removes repeated ctrl events of the same type and value""" | |
def __init__(self): | |
self.prev_ev = None | |
def __call__(self, ev): | |
if (self.prev_ev is not None and | |
ev.type == CTRL and self.prev_ev.type == CTRL and | |
ev.ctrl == self.prev_ev.ctrl and | |
ev.value == self.prev_ev.value): | |
r = None | |
else: | |
r = ev | |
self.prev_ev = ev | |
return r | |
class Combinations: | |
""" | |
translates combinations of ctrl events to new events. | |
Resulting events have fixed ctrl ids | |
1, 2, 3 (no combination), 12, 23, 13, 123 (combinations) | |
""" | |
def __init__(self, one, two, three): | |
"""takes ids of the ctrl events to combine""" | |
self.one = one | |
self.two = two | |
self.three = three | |
self.on = {} | |
def __call__(self, ev): | |
if ev.ctrl == self.one: | |
ctrl = 1 | |
elif ev.ctrl == self.two: | |
ctrl = 2 | |
elif ev.ctrl == self.three: | |
ctrl = 3 | |
else: | |
return ev | |
ev.ctrl = ctrl | |
r = [] | |
if len(self.on) == 0: | |
r.append(ev) | |
# note that before a new combination event is emitted, | |
# the last one must be cancelled. | |
if ev.value == 0: | |
if len(self.on) > 1: | |
r.append(self.combo(ev, ev.value)) | |
if ctrl in self.on: | |
del self.on[ctrl] | |
if len(self.on) > 0: | |
r.append(self.combo(ev, 127)) | |
else: | |
if len(self.on) > 0: | |
r.append(self.combo(ev, 0)) | |
self.on[ctrl] = True | |
if len(self.on) > 1: | |
r.append(self.combo(ev, ev.value)) | |
if len(self.on) == 0: | |
r.append(ev) | |
return r | |
def combo(self, copy_from, value): | |
""" creates a combination event """ | |
ctrl = 0 | |
for i, c in enumerate(reversed(sorted(self.on.keys()))): | |
if i > 0: | |
c *= 10 ** i | |
ctrl += c | |
return CtrlEvent(copy_from.port, copy_from.channel, ctrl, value) | |
def two_valued_sustain(): | |
""" | |
patch making sustain pedal a two-value controller | |
like the other two piano pedals | |
""" | |
return ( | |
# 1. limit it to two values 0, 127 | |
CtrlRange(pedal_sustain_ctrl, 0, 127, 0, 0) | |
>> | |
# 2. remove resulting repeated events with value 127 | |
Process(RemoveDuplicateCtrlEvents()) | |
) | |
def ctrls_to_notes(mapping, port=1, channel=1, velocity=50): | |
""" patch translating ctrl events to note events """ | |
split = {} | |
for ctrl, note in mapping.items(): | |
split[ctrl] = CtrlValueSplit({ | |
127: NoteOn(port, channel, note, velocity), | |
0: NoteOff(port, channel, note, velocity) | |
}) | |
return CtrlSplit(split) | |
run( | |
{ | |
# make piano pedals produce sound instead of their normal function | |
CTRL: ( | |
two_valued_sustain() | |
>> | |
# translate pedal events to notes | |
Process(Combinations(pedal_soft_ctrl, pedal_sostenuto_ctrl, pedal_sustain_ctrl)) | |
>> | |
ctrls_to_notes({ | |
1: 'c2', | |
2: 'd2', | |
3: 'e2', | |
12: 'f2', | |
23: 'g2', | |
13: 'a2', | |
123: 'b2', | |
}, channel=4) | |
## simpler setup: Without Combinations, only one note per pedal | |
# ctrls_to_notes({ | |
# pedal_soft_ctrl: 'c2', | |
# pedal_sostenuto_ctrl: 'f2', | |
# pedal_sustain_ctrl: 'g2', | |
# }, channel=4) | |
), | |
# split keyboard in two channels, simulating two organ manuals | |
NOTE: KeySplit( | |
'c3', # MIDI note name of c1 | |
(Channel(2) >> Transpose(octave)), | |
(Channel(1) >> Transpose(-1 * octave)) | |
) | |
} | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment