Python实现简易小钢琴
使用Python实现的简单小钢琴,给出了两种实现。
实现一
使用Python和内带的Tkinter库、winsound 模块实现的简单小钢琴。
Tkinter库和winsound模块不需要额外安装就可以使用,因为它们是Python的标准库之一,已经随着Python的安装包一起提供。标准库是Python官方提供的一组核心模块和工具,它们被视为Python语言的一部分,因此在安装Python解释器时就会自动包含这些标准库。
Tkinter库是Python的标准图形用户界面(GUI)工具包,它提供了创建和管理GUI应用程序所需的组件和功能。Tkinter基于Tcl/Tk工具包,是Python内置的库,因此无需额外安装。
winsound模块是Python的标准库之一,用于在Windows操作系统中播放声音。它提供了简单的接口,它提供了一些用于控制Windows系统声音的函数,比如播放声音、发出蜂鸣声等,可以用于播放系统提供的声音文件或者自定义的声音。
我对乐理了解几乎为零,相关音高(pitch)可以参考这里https://www.autopiano.cn/toolbox/pitch
以下是一个使用Python和内带的Tkinter库、winsound 模块实现的简单钢琴程序的源码:
import tkinter as tk
import winsound # 注意:winsound 模块只在 Windows 上可用
# 定义一个播放声音的函数
def play_sound(frequency):
winsound.Beep(frequency, 500) # 播放500毫秒的声音
# 创建主窗口
root = tk.Tk()
root.title("The Little Piano")
# 定义音符和对应的频率
notes = {
'a': 261,
's': 294,
'd': 329,
'f': 349,
'g': 392,
'h': 440,
'j': 494,
'k': 523
}
# 创建一个函数来生成钢琴键
def create_key(note):
def on_click(e=None):
play_sound(notes[note])
# 按下键的视觉反馈
button.config(relief=tk.SUNKEN)
button.after(100, lambda: button.config(relief=tk.RAISED))
button = tk.Button(root, text=note, width=5, height=5, command=on_click)
button.pack(side=tk.LEFT, padx=5, pady=5)
root.bind(note.lower(), on_click) # 绑定按键事件
# 为每个音符创建一个钢琴键
for note in notes:
create_key(note)
# 启动Tkinter事件循环
root.mainloop()
运行效果如下:
你可以用英文状态下按下琴键上的字符,或用鼠标单击琴键试试了。
需要注意的是,用Tkinter库、winsound 模块实现小钢琴只能实现基本功能,由于其局限性,音质较差,要实现鼠标滑动时连续播放音符的效果等是很困难的。可能更适合使用如 Pygame库来实现,见下面的实现。
实现二
下面示例假设你已经安装了pygame和numpy库。如果还没有安装,请先使用pip install pygame numpy进行安装。源码如下:
import pygame
import numpy as np
# 初始化pygame
pygame.init()
# 创建窗口
screen = pygame.display.set_mode((640, 240))
pygame.display.set_caption("简易钢琴")
# 设置颜色
white = (255, 255, 255)
black = (0, 0, 0)
gray = (200, 200, 200)
# 初始化字体模块
pygame.font.init()
font = pygame.font.Font(None, 36)
# 音符频率映射
notes = {
'A': 261.63,
'S': 293.66,
'D': 329.63,
'F': 349.23,
'G': 392.00,
'H': 440.00,
'J': 493.88,
'K': 523.25
}
# 键盘按键到音符的映射
key_to_note = {
pygame.K_a: 'A',
pygame.K_s: 'S',
pygame.K_d: 'D',
pygame.K_f: 'F',
pygame.K_g: 'G',
pygame.K_h: 'H',
pygame.K_j: 'J',
pygame.K_k: 'K'
}
# 音符是否被按下
note_pressed = {note: False for note in notes}
def play_sound(frequency):
duration = 1 # 持续时间为1秒
sample_rate = 44100 # 采样率
t = np.linspace(0, duration, int(sample_rate * duration), False)
wave = np.sin(2 * np.pi * frequency * t)
# 确保数组是C-连续的
sound_array = np.ascontiguousarray(np.array([32767 * wave, 32767 * wave]).T.astype(np.int16))
sound = pygame.sndarray.make_sound(sound_array)
sound.play()
def draw_piano_keys():
for i, note in enumerate(notes):
x = i * 80
y = 100
width = 79
height = 100
color = gray if note_pressed[note] else black
pygame.draw.rect(screen, color, pygame.Rect(x, y, width, height))
text_surface = font.render(note, True, white)
screen.blit(text_surface, (x + (width - text_surface.get_width()) / 2, y + (height - text_surface.get_height()) / 2))
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key in key_to_note:
note = key_to_note[event.key]
note_pressed[note] = True
play_sound(notes[note])
elif event.type == pygame.KEYUP:
if event.key in key_to_note:
note = key_to_note[event.key]
note_pressed[note] = False
elif event.type == pygame.MOUSEBUTTONDOWN or (event.type == pygame.MOUSEMOTION and pygame.mouse.get_pressed()[0]):
x, y = pygame.mouse.get_pos()
if 100 <= y <= 200:
key_index = x // 80
if 0 <= key_index < len(notes):
note = list(notes.keys())[key_index]
if not note_pressed[note]: # 防止重复播放
note_pressed[note] = True
play_sound(notes[note])
elif event.type == pygame.MOUSEBUTTONUP:
for note in note_pressed:
note_pressed[note] = False
screen.fill(white)
draw_piano_keys()
pygame.display.flip()
pygame.quit()
运行效果如下:
你可以用英文状态下按下琴键上的字符,或用鼠标单击琴键试试了,效果好些了,你可以继续改进。