Apparently we need to be using keycodes for this for reliability. Also, the 100ms delay is required, otherwise Patapon (the game, not the emulator or the input layer) won't pick up on it
97 lines
3.2 KiB
Python
Executable File
97 lines
3.2 KiB
Python
Executable File
#! /usr/bin/env python3
|
|
import argparse
|
|
import subprocess
|
|
import time
|
|
|
|
def ydotool(key):
|
|
subprocess.run([
|
|
"ydotool",
|
|
"key",
|
|
str(key) + ":1",
|
|
str(key) + ":0",
|
|
"--key-delay",
|
|
"100"
|
|
])
|
|
|
|
def main():
|
|
# A list of all possible songs, expressed as their cardinal directions
|
|
# We'll map these to keystrokes later
|
|
songs = [
|
|
# March of Mobility
|
|
"L-L-L-R-",
|
|
# Aria of Attack
|
|
"R-R-L-R-",
|
|
# Lament of Defense
|
|
"U-U-L-R-",
|
|
# Hold-Tight Hoe-Down/Concerto of Charge
|
|
"R-R-U-U-",
|
|
# Melody with a Bounce/Jingle of Jump
|
|
"D-D-U-U-",
|
|
# Ballad of 1999/Pizzicato of Party
|
|
"L-R-D-U-",
|
|
# Song of Miracles (Djinn)
|
|
"D-DD-DD-",
|
|
# Leisurely Lullaby
|
|
# Not sure why you'd want to cast this on repeat, but you can I guess
|
|
"L-R-L-R-",
|
|
# Step Back Strut
|
|
"U-L-U-L-"
|
|
]
|
|
# A mapping of characters in our encoded songs to keys to send to ydotool
|
|
# These should be the default bindings for PPSSPP
|
|
keymapping = {
|
|
"U": "31",
|
|
"D": "44",
|
|
"L": "30",
|
|
"R": "45"
|
|
}
|
|
drummapping = {
|
|
"U": '\033[32mCHAKA\033[0m',
|
|
"D": '\033[33mDON\033[0m',
|
|
"L": '\033[31mPATA\033[0m',
|
|
"R": '\033[34mPON\033[0m'
|
|
}
|
|
parser = argparse.ArgumentParser(
|
|
description="Play a sequence of Patapon commands on repeat"
|
|
)
|
|
parser.add_argument('song',default="R-R-L-R-",nargs="+",choices=songs,help="The song to play. Defaults to Aria of Attack. When expressing a song, use eighth notes, dashes, and cardinal directions to designate the drums. For example, party would be \"L-R-D-U-\", and djinn would be \"D-DD-DD-\"")
|
|
parser.add_argument('--bpm',type=float,default=1,help="Multiplier for the BPM. Change if you're running the game at a higher speed")
|
|
parser.add_argument('--iterations',type=int,default=10000,help="Number of iterations of the sequence to run. Defaults to 10,000")
|
|
args = parser.parse_args()
|
|
|
|
# Schedule out our beat interval
|
|
# 120 Naiive, does not work
|
|
# 119.905 Still fast
|
|
# 119.900 Still fast
|
|
# 119.890
|
|
bpm_constant = 119.890
|
|
beat_interval = 60 / (bpm_constsant * args.bpm * 2)
|
|
print(f"Beat interval: {beat_interval}")
|
|
|
|
# Set up the environment
|
|
sequence='--------' + '--------'.join(args.song)
|
|
print(f"Song sequence: {sequence}")
|
|
remaining_iterations = args.iterations
|
|
lastbeat = 0
|
|
|
|
# Wait for user confirmation
|
|
input("Press enter on-beat to sync up with Patapon...")
|
|
synctime = time.perf_counter()
|
|
|
|
# Play da notes
|
|
while remaining_iterations > 0:
|
|
for i, key in enumerate(sequence):
|
|
while time.perf_counter() < synctime + lastbeat:
|
|
pass
|
|
lastbeat += beat_interval
|
|
button = keymapping.get(key, '-')
|
|
if button != '-':
|
|
ydotool(button)
|
|
print(drummapping.get(key, '-'), end="", flush=True)
|
|
else:
|
|
print(" ", end="", flush=True)
|
|
remaining_iterations -= 1
|
|
print(f"~ ({remaining_iterations} remaining)")
|
|
|
|
main()
|