Skip to content

Instantly share code, notes, and snippets.

@igneus
Last active July 30, 2016 13:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save igneus/fb3f44f71c74c8c4d3ec80906ba081f7 to your computer and use it in GitHub Desktop.
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)
# 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