好玩 | Python 弹琴 Rusty Lake Family Tune
文章目录
0. 安装库
-
安装 alive-progress: 显示音乐进度进度条
pip install alive-progress
-
安装 pyaudio: 播放每个音键wav文件
- pip install pyaudio
在安装的setup部分可能会报错, 导致不能装上 - 跳转到 下载连接, 下载对应python版本的 pyaudio whl文件, 然后 pip install xxxxx.whl 安装刚刚下载的 whl 文件
- pip install pyaudio
1. 准备乐谱
乐谱准备在一个 str.txt里, 形如:
每一行代表一个时间点: |
左边代表时间点, |
右边代表这个时间点要弹的音键
2. 将乐谱转成列表存储在 pkl文件
import pickle
# 读取 str.txt 文件
songName = 'RustyLake FamilyTune'
with open(f'./{songName}/str.txt', 'r') as rf:
lines = rf.readlines()
rf.close()
# 转成列表
musicList = []
for line in lines:
t, w = line[:-1].split('|')
musicList.append((float(t), w))
# 存到pkl文件
with open(f'./{songName}/musicList.pkl', 'wb') as wf:
pickle.dump(musicList, wf)
wf.close()
3. 编写播放wav文件函数
这个函数基本不用改, 只需要传入wav文件的名字即可开始播放
def playWav(wav):
filepath = 'E:/Python program/2_PythonPiano/keys/'
path = filepath + wav + '.wav'
chunk = 1024
file = wave.open(path, "rb")
player = pyaudio.PyAudio()
stream = player.open(format=player.get_format_from_width(file.getsampwidth()),
channels=file.getnchannels(),
rate=file.getframerate(),
output=True)
data = file.readframes(chunk)
while len(data) > 0:
stream.write(data)
data = file.readframes(chunk)
file.close()
4. 根据乐谱顺序播放wav文件
def playMusic(name):
with open(f'./{name}/musicList.pkl', 'rb') as rf:
musicList = pickle.load(rf)
rf.close()
with alive_bar(len(musicList)) as bar: # 进度条
for i in range(len(musicList)): # 遍历每个时间节点音键
bar()
if i + 1 != len(musicList): # sleepTime是当前音键和下一个音键的中间间隔
sleepTime = musicList[i + 1][0] - musicList[i][0]
else:
sleepTime = 0
threads = [] # 将当前时间节点的n个音键通过创建线程同时执行
for w in musicList[i][1].split(','):
threads.append(
threading.Thread(target=playWav, args=(w,))
)
for t in threads: # 当前时间节点的n个音键同时执行, 模拟弹琴同时按下n个音键
t.start()
time.sleep(sleepTime)
5. 效果
PyPiano - RustyLake FamilyTune
6. 乐谱参考
感谢 bilibili up主@CENNHM
乐谱和代码部分参考 来自 up主@CENNHM
博客中的代码在原有代码基础上进行了修改:
- 线程顺序添加, 避免一开始创建太多线程导致系统调度卡顿
- 对乐谱的数据结构进行修改