文章编写背景
玩了N久的Freepiano,碍于本人没有天赋,左右手一直没法协调。
于是,突然奇想,也是代码设计的思路:
用多线程的方式,开两个线程,然后通过按键模拟的方式,分别模拟左右手去演奏。觉得可行,于是开干
依赖
import time
import pyautogui
import keyboard
from threading import Thread
大致分析
按键模拟
首先要研究按键模拟,就是获取所有键盘的Mapping值。然后发现很多坑:
1、部分依赖不支持小键盘(键盘最右边)
2、部分按键不能模拟(menu,right crtl等)
3、键盘上的按键是不一样的,但是他们的mapping值是一样的(home page up 等)
这时候需要分两步走,获取所有按键对应的mapping值,然后配置一个Mapping 常量,我们可以借助下面的片段:
import keyboard
# keyboard.press_and_release('num lock') # 小键盘锁键
def on_key_press(event):
print("按键:", event.name)
keyboard.on_press(on_key_press)
keyboard.wait()
运行后,任意按键会打印对应的Mapping值,这里注意的是小键盘的锁切换后会不一样
然后得到一个mapping常量(按自身实际配置):
left_hand_mapping = {
'1': 'q',
'111': '8',
'2': 'w',
"222": "9",
'3': 'e',
'33': '3',
'4': 'r',
'44': '4',
'5': 't',
'55': '5',
'6': 'y',
'66': '6',
'7': 'u',
'77': '7',
'P': ' '
}
right_hand_mapping = {
'1': 'end',
'2': 'down',
'3': 'pagedown',
'333': 'alt',
'4': 'left',
'5': 'clear',
'555': "f2",
'6': 'right',
'666': 'f1',
'666#': 'backspace',
'7': 'home',
'P': ' '
}
综合上面的坑,要对freepiano进行键位修改,这里的思路是放弃小键盘的锁键,还有HOME等功能键,见下图:
PS:这里要注意新按键的通道,因为左右手的力度是不一样的。
线程模拟
开始编码模拟左右手,因为左右手的演奏速度是不一样的,所以这里要做区分。
左手按键与松开:
def play_left_hand(melody):
for note in melody:
if note == "-":
time.sleep(stop_note)
press_key(note, 'left')
time.sleep(note_duration_left)
release_key_left(note)
def release_key_left(note):
if note in left_hand_mapping:
keyboard.release(left_hand_mapping[note])
右手按键与松开:
def play_right_hand(melody):
for note in melody:
if note == "-":
time.sleep(stop_note)
press_key(note, 'right')
time.sleep(note_duration_right)
release_key_right(note)
def release_key_right(note):
if note in right_hand_mapping:
keyboard.release(right_hand_mapping[note])
按键方法:
def press_key(note, hand):
if hand == 'left' and note in left_hand_mapping:
left_hand_key = left_hand_mapping[note]
keyboard.press(left_hand_key)
if hand == 'right' and note in right_hand_mapping:
right_hand_key = right_hand_mapping[note]
keyboard.press(right_hand_key)
乐谱设计
这里是最重要的,这里我们用两个列表分别存左右手的音符。具体字符需要根据实际mapping值来设计
left_hand_melody = ['33', '66', '77', '111', '-', '44', '66', '77', '111', '-', '55', '66', '222', '111', '77']
right_hand_melody = ["666", "666", "666", "-", "666", "666#", "666#", "666#", "555", "555", "555", "-", "555",
"333", "333", "333", "333"]
这是一段某游戏登录前奏。乐谱地址:简谱
具体效果
效果不理想,休止音符和节奏没设计好,下版本更新。。。。
具体效果:
python演奏Freepiano(双手合奏)DEMO1
。TBD
完整代码
key_mapping.py:
left_hand_mapping = {
'1': 'q',
'111': '8',
'2': 'w',
"222": "9",
'3': 'e',
'33': '3',
'4': 'r',
'44': '4',
'5': 't',
'55': '5',
'6': 'y',
'66': '6',
'7': 'u',
'77': '7',
'P': ' '
}
right_hand_mapping = {
'1': 'end',
'2': 'down',
'3': 'pagedown',
'333': 'alt',
'4': 'left',
'5': 'clear',
'555': "f2",
'6': 'right',
'666': 'f1',
'666#': 'backspace',
'7': 'home',
'P': ' '
}
piano.py
import time
import pyautogui
import keyboard
from threading import Thread
from key_mapping import *
def play_left_hand(melody):
for note in melody:
if note == "-":
time.sleep(stop_note)
press_key(note, 'left')
time.sleep(note_duration_left)
release_key_left(note)
def play_right_hand(melody):
for note in melody:
if note == "-":
time.sleep(stop_note)
press_key(note, 'right')
time.sleep(note_duration_right)
release_key_right(note)
def press_key(note, hand):
if hand == 'left' and note in left_hand_mapping:
left_hand_key = left_hand_mapping[note]
keyboard.press(left_hand_key)
if hand == 'right' and note in right_hand_mapping:
right_hand_key = right_hand_mapping[note]
keyboard.press(right_hand_key)
def release_key_left(note):
if note in left_hand_mapping:
keyboard.release(left_hand_mapping[note])
def release_key_right(note):
if note in right_hand_mapping:
keyboard.release(right_hand_mapping[note])
if __name__ == '__main__':
repeat = True
right_first = True
left_hand_melody = ['33', '66', '77', '111', '-', '44', '66', '77', '111', '-', '55', '66', '222', '111', '77']
right_hand_melody = ["666", "666", "666", "-", "666", "666#", "666#", "666#", "555", "555", "555", "-", "555",
"333", "333", "333", "333"]
note_duration_left = 0.4 # 每个音符的持续时间(秒) 左手
note_duration_right = 0.3 # 每个音符的持续时间(秒) 左手
stop_note = 0.1 # 休止音符
# 切换到FreePiano界面
time.sleep(2)
freepiano_window = pyautogui.getWindowsWithTitle("FreePiano")[0]
freepiano_window.activate()
# 简谱是否重复
if repeat:
left_hand_melody.append("-")
left_hand_melody += left_hand_melody
right_hand_melody.append("-")
right_hand_melody += right_hand_melody
# 启动左手伴奏和右手主旋律线程
left_hand_thread = Thread(target=play_left_hand, args=(left_hand_melody,))
right_hand_thread = Thread(target=play_right_hand, args=(right_hand_melody,))
left_hand_thread.start()
right_hand_thread.start()
left_hand_thread.join()
right_hand_thread.join()