instead of starting on cc 64 I started mapping from cc65 so I could use my piano sustain pedal to trigger loops and use the dpad to select the loops.
x doubles, a halves the length.
the middle guide (logitech) button dumps the loops to the timeline (add to project)
L button kills all the loops
Originally I did it with this 2$ ali express gamepad, later I wanted to get it working with this logitech ps1 style controller that has more buttons (more possibilities).
dropbox link to snes-midicc.pyUsing It within Super 8

import pygame
import rtmidi
import time
import sys
def initialize_midi():
midi_out = rtmidi.MidiOut()
available_ports = midi_out.get_ports()
if not available_ports:
print("No MIDI output ports found. Creating virtual port 'Gamepad MIDI'...")
midi_out.open_virtual_port("Gamepad MIDI")
else:
print("Available MIDI ports:", available_ports)
midi_out.open_port(0)
print(f"Connected to: {available_ports[0]}")
return midi_out
def initialize_joystick():
pygame.init()
pygame.joystick.init()
if pygame.joystick.get_count() == 0:
print("No gamepad detected!")
sys.exit(1)
joystick = pygame.joystick.Joystick(0)
joystick.init()
print(f"Detected gamepad: {joystick.get_name()}")
return joystick
def main():
midi_out = initialize_midi()
joystick = initialize_joystick()
button_states = {
'left': False,
'right': False,
'up': False,
'down': False
}
prev_buttons = [False] * joystick.get_numbuttons()
print("Gamepad to MIDI mapper running. Press Ctrl+C to exit.")
try:
while True:
for event in pygame.event.get():
if event.type == pygame.JOYAXISMOTION:
if event.axis == 0: # Left/Right
if event.value < -0.5 and not button_states['left']:
midi_out.send_message([0xB0, 64, 127])
button_states['left'] = True
elif event.value > -0.5 and button_states['left']:
midi_out.send_message([0xB0, 64, 0])
button_states['left'] = False
if event.value > 0.5 and not button_states['right']:
midi_out.send_message([0xB0, 66, 127])
button_states['right'] = True
elif event.value < 0.5 and button_states['right']:
midi_out.send_message([0xB0, 66, 0])
button_states['right'] = False
elif event.axis == 1: # Up/Down
if event.value < -0.5 and not button_states['up']:
midi_out.send_message([0xB0, 65, 127])
button_states['up'] = True
elif event.value > -0.5 and button_states['up']:
midi_out.send_message([0xB0, 65, 0])
button_states['up'] = False
if event.value > 0.5 and not button_states['down']:
midi_out.send_message([0xB0, 67, 127])
button_states['down'] = True
elif event.value < 0.5 and button_states['down']:
midi_out.send_message([0xB0, 67, 0])
button_states['down'] = False
elif event.type == pygame.JOYBUTTONDOWN:
button_num = event.button
cc_number = 68 + button_num
midi_out.send_message([0xB0, cc_number, 127])
prev_buttons[button_num] = True
elif event.type == pygame.JOYBUTTONUP:
button_num = event.button
cc_number = 68 + button_num
midi_out.send_message([0xB0, cc_number, 0])
prev_buttons[button_num] = False
time.sleep(0.001)
except KeyboardInterrupt:
print("\nExiting...")
finally:
midi_out.close_port()
pygame.quit()
if __name__ == "__main__":
main()
original attempt
starts from cc64 and up
blue X = cc69
red A = cc70
yellow b = cc71
green y = cc72
select = cc77
start = cc78
Lt = cc73
Rt = cc74
up/down = 0/ 126
left/right = 0/126
I want to fix this so each direction sends on its own cc as momentary but this is fine for now
import pygame
import mido
MIDI_CC_START = 64
# Initialize MIDI output
midi_out = mido.open_output(mido.get_output_names()[0])
def send_midi_cc(cc, value):
msg = mido.Message('control_change', control=cc, value=value)
midi_out.send(msg)
def main():
pygame.init()
pygame.joystick.init()
if pygame.joystick.get_count() == 0:
print("No gamepad detected.")
return
joystick = pygame.joystick.Joystick(0)
joystick.init()
print(f"Gamepad detected: {joystick.get_name()}")
print("Press Ctrl+C to exit.")
prev_axes = [0] * joystick.get_numaxes()
prev_buttons = [0] * joystick.get_numbuttons()
prev_hats = [(0, 0)] * joystick.get_numhats()
try:
while True:
pygame.event.pump()
# Read axis values
axes = [joystick.get_axis(i) for i in range(joystick.get_numaxes())]
for i, value in enumerate(axes):
if abs(value) > 0.1 and abs(value - prev_axes[i]) > 0.01:
send_midi_cc(MIDI_CC_START + i, int((value + 1) / 2 * 127)) # Normalize to 0-127
prev_axes[i] = value
# Read button states
buttons = [joystick.get_button(i) for i in range(joystick.get_numbuttons())]
for i, pressed in enumerate(buttons):
cc_number = MIDI_CC_START + 5 + i # Buttons start after D-pad CCs
if pressed and not prev_buttons[i]:
send_midi_cc(cc_number, 127)
elif not pressed and prev_buttons[i]:
send_midi_cc(cc_number, 0)
prev_buttons[i] = pressed
# Read hat (D-pad) states (momentary, dedicated CCs)
hats = [joystick.get_hat(i) for i in range(joystick.get_numhats())]
for i, hat in enumerate(hats):
left_cc = 64
up_cc = 65
right_cc = 66
down_cc = 67
if hat[0] == -1 and prev_hats[i][0] != -1:
send_midi_cc(left_cc, 127)
elif hat[0] != -1 and prev_hats[i][0] == -1:
send_midi_cc(left_cc, 0)
if hat[1] == 1 and prev_hats[i][1] != 1:
send_midi_cc(up_cc, 127)
elif hat[1] != 1 and prev_hats[i][1] == 1:
send_midi_cc(up_cc, 0)
if hat[0] == 1 and prev_hats[i][0] != 1:
send_midi_cc(right_cc, 127)
elif hat[0] != 1 and prev_hats[i][0] == 1:
send_midi_cc(right_cc, 0)
if hat[1] == -1 and prev_hats[i][1] != -1:
send_midi_cc(down_cc, 127)
elif hat[1] != -1 and prev_hats[i][1] == -1:
send_midi_cc(down_cc, 0)
prev_hats[i] = hat
pygame.time.wait(100) # Reduce CPU usage
except KeyboardInterrupt:
print("\nExiting...")
finally:
joystick.quit()
pygame.quit()
midi_out.close()
if __name__ == "__main__":
main()
No comments:
Post a Comment