Created
January 3, 2026 21:25
-
-
Save dannytheliar/10e0c9ed9066bd713e887ad2220dfad6 to your computer and use it in GitHub Desktop.
freepie drum midi controller for elden ring
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
| import time | |
| class KeyDef: | |
| tap_duration = 50 | |
| hold_duration = 200 | |
| max_scalable_duration = 400 | |
| def __init__(self, name, key, notes, threshold = 0.1, holdable = False, scale_with_velocity = False): | |
| """ | |
| Define a MIDI key definition. | |
| :param name: Readable name of the input | |
| :param key: FreePIE output key | |
| :param notes: MIDI notes which will trigger this key | |
| :param threshold: Velocity threshold for triggering this key | |
| :param holdable: Use longer hold duration instead of tap duration. For holdable keys | |
| :param scale_with_velocity: Hold duration will scale with velocity | |
| """ | |
| self.name = name | |
| self.key = key | |
| self.notes = notes | |
| self.threshold = threshold | |
| self.holdable = holdable | |
| self.scale_with_velocity = scale_with_velocity | |
| KEYS = [ | |
| KeyDef('Snare', Key.W, [38], holdable=True), | |
| KeyDef('Snare Rim', Key.UpArrow, [37, 40], threshold=0.6, scale_with_velocity=False), | |
| KeyDef('Tom 1', Key.A, [48], holdable=True), | |
| KeyDef('Tom 1 Rim', Key.LeftArrow, [50], threshold=0.2, scale_with_velocity=False), | |
| KeyDef('Tom 2', Key.D, [45], holdable=True), | |
| KeyDef('Tom 2 Rim', Key.RightArrow, [47], threshold=0.2, scale_with_velocity=False), | |
| KeyDef('Tom 3', Key.S, [43], holdable=True), | |
| KeyDef('Tom 3 Rim', Key.DownArrow, [58], threshold=0.5, scale_with_velocity=False), | |
| KeyDef('Tom 4', Key.R, [41], threshold=0.15), | |
| KeyDef('Tom 4 Rim', Key.Escape, [39], threshold=0.6), | |
| KeyDef('Hihat Open', Key.P, [26, 46]), | |
| KeyDef('Hihat Closed', Key.P, [22, 42]), | |
| KeyDef('Crash 1', Key.Q, [49]), | |
| KeyDef('Crash 2', Key.B, [52, 57], threshold=0.3), | |
| KeyDef('Crash 3', Key.M, [27, 28], holdable=True), | |
| KeyDef('Ride 1', Key.N, [51], holdable=True), | |
| KeyDef('Ride 1 Bell', Key.O, [53]), | |
| KeyDef('Ride 2', Key.E, [29, 30], holdable=True), | |
| KeyDef('Splash 1', Key.V, [33]), | |
| KeyDef('Splash 2', Key.F, [31]), | |
| KeyDef('Kick', Key.Space, [36]), | |
| ] | |
| # Hihat pedal config | |
| PEDAL_TAP_KEY = Key.F | |
| PEDAL_HOLD_KEY = Key.Space | |
| PEDAL_HOLD_MIN_DURATION = 200 # Tap if pedal is released under min duration, else hold | |
| PEDAL_PRESS_THRESHOLD = 0.4 # Trigger if pedal is down more than this amount | |
| PEDAL_MIDI_NOTE = 4 # TODO: Look more into what this actually is? | |
| # Drum module kit change buttons | |
| # TODO: Differentiate between program change up and down | |
| PROGRAM_CHANGE_KEY = Key.X | |
| VELOCITY_MAX = 127 | |
| MIDI_DEVICE_INDEX = 0 | |
| def get_key_def(note): | |
| for key in KEYS: | |
| if note in key.notes: | |
| return key | |
| def update(): | |
| global state | |
| payload = midi[0].data | |
| midi_note, velocity = payload.buffer | |
| t = int(time.time() * 1000) | |
| if payload.status == MidiStatus.NoteOn: | |
| # Standard MIDI Note On, a key was just pressed | |
| key_def = get_key_def(midi_note) | |
| if key_def: | |
| scaled_velocity = float(velocity) / VELOCITY_MAX | |
| if scaled_velocity >= key_def.threshold: | |
| duration = KeyDef.hold_duration if key_def.holdable else KeyDef.tap_duration | |
| if key_def.scale_with_velocity: | |
| scaled_duration = scaled_velocity * KeyDef.max_scalable_duration | |
| duration = max(KeyDef.tap_duration, scaled_duration) | |
| if key_def.name == "Kick": | |
| #if state['pedal_is_held']: | |
| # keyboard.setKeyDown(Key.F) | |
| # state['pressed_keys'][Key.F] = t + 50 | |
| #else: | |
| keyboard.setKeyDown(key_def.key) | |
| state['pressed_keys'][key_def.key] = t + 50 | |
| else: | |
| keyboard.setKeyDown(key_def.key) | |
| state['pressed_keys'][key_def.key] = t + duration | |
| diagnostics.debug("MIDI {} ({}): Press {} for {}ms".format(midi_note, key_def.name, key_def.key, duration)) | |
| else: | |
| diagnostics.debug("MIDI {}: Unknown".format(midi_note)) | |
| elif payload.status == MidiStatus.Control and midi_note == PEDAL_MIDI_NOTE: | |
| # Control message sent from analog pedal input change | |
| if velocity >= PEDAL_PRESS_THRESHOLD * VELOCITY_MAX and state['pedal_press_time'] is None: | |
| # Pedal was pressed | |
| state['pedal_press_time'] = t | |
| elif velocity < PEDAL_PRESS_THRESHOLD * VELOCITY_MAX and state['pedal_press_time'] is not None: | |
| # Pedal was released | |
| if t - state['pedal_press_time'] < PEDAL_HOLD_MIN_DURATION: | |
| # Within tap duration, handle like a normal key press | |
| keyboard.setKeyDown(PEDAL_TAP_KEY) | |
| state['pressed_keys'][PEDAL_TAP_KEY] = t + KeyDef.tap_duration | |
| diagnostics.debug("Pedal Tap: {}".format(PEDAL_TAP_KEY)) | |
| else: | |
| # Release the hold key, pressing it is handled separately | |
| keyboard.setKeyUp(PEDAL_HOLD_KEY) | |
| state['pedal_is_held'] = False | |
| diagnostics.debug("Pedal Hold release: {}".format(PEDAL_HOLD_KEY)) | |
| state['pedal_press_time'] = None | |
| elif payload.status == MidiStatus.ProgramChange: | |
| # Program Change message from module changing kit | |
| # TODO: Separately handle changing kit up vs down | |
| keyboard.setKeyDown(PROGRAM_CHANGE_KEY) | |
| state['pressed_keys'][PROGRAM_CHANGE_KEY] = t + KeyDef.tap_duration | |
| diagnostics.debug("Key down {} [MIDI program change]".format(PROGRAM_CHANGE_KEY)) | |
| if not state['pedal_is_held'] and state['pedal_press_time'] and t - state['pedal_press_time'] > PEDAL_HOLD_MIN_DURATION: | |
| # Pedal has been held down for longer than the tap threshold | |
| keyboard.setKeyDown(PEDAL_HOLD_KEY) | |
| state['pedal_is_held'] = True | |
| diagnostics.debug("Pedal Hold on: {}".format(PEDAL_HOLD_KEY)) | |
| # Release any keys which have been pressed for long enough | |
| for key, key_release_time in state['pressed_keys'].items(): | |
| if t >= key_release_time: | |
| keyboard.setKeyUp(key) | |
| diagnostics.debug("Key up {}".format(key)) | |
| del state['pressed_keys'][key] | |
| if starting: | |
| state = { | |
| # Mapping of key: release_time | |
| 'pressed_keys': {}, | |
| # Time of the pedal being pressed, or None if not pressed | |
| 'pedal_press_time': None, | |
| # Whether hold threshold for pedal has been reached | |
| 'pedal_is_held': False, | |
| } | |
| midi[MIDI_DEVICE_INDEX].update += update |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment