用python制作88键赛博钢琴(能用鼠标键盘进行弹奏)

news2024/11/25 7:09:33

用python制作88键赛博钢琴

前言

  恭喜这位博主终于想起了自己的账号密码!

  时光荏苒,转眼间已逾一年未曾在此留下墨香。尽管这一年间,博主投身于无尽的忙碌与挑战之中,但令人欣慰的是,那份初心与热情似乎并未因岁月的流转而有丝毫减退,依旧保持着与往昔相同的热情与活力。

  提及趣事,前不久博主精心筹备,欲在女友生辰之际,以一份特别的礼物——一台37键的童趣钢琴,为她编织一段温馨的记忆。怎料,这份心意与紧随其后的七夕佳节完美邂逅,却因工作的突然召唤,让博主不得不带着遗憾踏上异乡的征途,错过了亲自弹奏《两只老虎》的温馨时刻。

  望着视频中女友指尖跳跃,旋律悠扬,那份未能亲临现场的遗憾化作了创新的火花。博主灵机一动,决定跨越千山万水,用指尖下的键盘,在数字世界中续写音乐的浪漫。说干就干,经过一番不懈的努力与探索,几个小时后,一个别出心裁的“键盘钢琴”奇迹般地诞生了!

  请允许我们一同见证这创意的结晶。

效果图

在这里插入图片描述

功能实现

  使用一个JSON文件作为核心,来控制整体界面布局、每个键对应的mp3文件、简谱标识、键盘映射等。

  使用PyQt5实现界面绘制。
  使用pygame库播放音乐,会更加流畅、连贯。
  使用keyboard实现键盘监控。
  使用Thread多线程,防止pygame和PyQt5线程冲突

  最终实现的功能很简单,鼠标点击或键盘敲击对应的键即可进行弹奏。

源代码

import sys
import keyboard
import pygame
import json

from threading import Thread
from PyQt5.QtWidgets import QMainWindow, QWidget, QPushButton, QApplication, QLabel

# 读取数据文件
piano_key = json.load(open('JSON/piano_key.json', 'r', encoding='utf8'))


# 主窗口
class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)

        # 获取桌面尺寸
        desktop = QApplication.desktop()
        screen_rect = desktop.screenGeometry()
        # 设置主窗口比例
        main_width = int(screen_rect.width() * 0.9)
        main_height = int(screen_rect.height() * 0.4)
        self.resize(main_width, main_height)
        # 固定窗口大小
        self.setFixedSize(self.width(), self.height())
        # 窗口居中
        self.move((screen_rect.width() - main_width) // 2, (screen_rect.height() - main_height) // 2)

        # 状态栏和标题
        self.status = self.statusBar()
        self.status.showMessage('不是88键买不起,而是赛博钢琴更有性价比!')
        self.setWindowTitle('赛博钢琴')

        # 创建容器存放琴键
        container = QWidget(self)
        self.setCentralWidget(container)

        # 遍历查询黑白键的数量,用于计算每个键宽度
        black_key_num = sum(1 for key in piano_key if 's' in key['sound'])
        white_key_num = len(piano_key) - black_key_num

        self.buttons = []
        button_width = main_width / white_key_num
        white_key_index = 0

        for index, key in enumerate(piano_key):
            button = QPushButton(container)
            button.setObjectName(key['sound'])
            button.clicked.connect(self.on_button_clicked)
            self.set_button_style(button, 's' in key['sound'])

            if 's' in key['sound']:
                button.resize(button_width * 0.8, main_height * 0.6)
                button.move((white_key_index - 1) * button_width + button_width * 0.6, 0)
                button.raise_()
            else:
                button.resize(button_width, main_height)
                button.move(white_key_index * button_width, 0)
                button.lower()
                white_key_index += 1

                self.add_label(container, key, white_key_index, button_width, main_height)

            self.buttons.append(button)

    # 匹配并添加label
    def add_label(self, container, key, white_key_index, button_width, main_height):
        label_map = {
            'a': '6', 'A': '6',
            'b': '7', 'B': '7',
            'c': '1', 'C': '1',
            'd': '2', 'D': '2',
            'e': '3', 'E': '3',
            'f': '4', 'F': '4',
            'g': '5', 'G': '5'
        }
        label_text = label_map.get(key['sound'][0], '') + f"\n{key['key']}"
        label = QLabel(label_text, container)
        label.move(white_key_index * button_width - button_width * 0.5, main_height - 60)

    # 初始化黑白键样式
    def set_button_style(self, button, is_black_key):
        if is_black_key:
            button.setStyleSheet("""
                QPushButton {
                    background-color: black;
                    color: white;
                    border: 1px solid black;
                    padding: 0;
                    margin: 0;
                    text-align: center;
                }
                QPushButton::hover {
                    background-color: lightgray;
                }
                QPushButton:pressed {
                    background-color: gray;
                }
            """)
        else:
            button.setStyleSheet("""
                QPushButton {
                    background-color: white;
                    color: black;
                    border: 1px solid black;
                    padding: 0;
                    margin: 0;
                    text-align: center;
                }
                QPushButton::hover {
                    background-color: lightgray;
                }
                QPushButton:pressed {
                    background-color: gray;
                }
            """)

    # 键盘按下改变样式
    def change_button_color(self, index):
        self.buttons[index].setStyleSheet("background-color: gray;")

    # 抬起后恢复样式
    def release_button_color(self, index, is_black_key):
        self.set_button_style(self.buttons[index], is_black_key)

    # 鼠标点击播放
    def on_button_clicked(self):
        button = self.sender()
        pygame.mixer.Sound('MP3/' + button.objectName()).play()


# 初始化 PyQt 应用
app = QApplication(sys.argv)
# 实例化窗口
form = MainWindow()


# 键盘按下触发
def on_action(event):
    try:
        sound = next(item['sound'] for item in piano_key if item['key'] == event.name)
        index = next(index for index, item in enumerate(piano_key) if item['key'] == event.name)

        if event.event_type == keyboard.KEY_DOWN:
            pygame.mixer.Sound('MP3/' + sound).play()
            form.change_button_color(index)
        elif event.event_type == keyboard.KEY_UP:
            form.release_button_color(index, 's' in sound)

    except StopIteration:
        print(f"No sound file found for key: {event.name}")


# 键盘监听
def start_keyboard_listener():
    keyboard.hook(on_action)
    keyboard.wait()


def main():
    # 显示窗口
    form.show()
    # 初始化 Pygame 混音器
    pygame.mixer.init()
    # 启动键盘监听线程
    listener_thread = Thread(target=start_keyboard_listener)
    listener_thread.daemon = True
    listener_thread.start()
    # 进入事件循环
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()

未来功能扩展

1.自定义功能:用户可以自定义每个琴键对应的键盘值,并保存,这也是我使用JSON文件控制整体的原因。

2.丰富标识:目前琴键上只有简谱的标识,后续会添加Do、Ra、C4、D4等标识。

3.自动弹奏:用户可以以某种方式将谱子录入或导入,程序根据谱子自动弹奏。

4.边弹边记:开启后,接下来的一段弹奏会以乐谱的形式保存下来。

5.趣味玩法:可能会像节奏大师那样?

6.有奇思妙想的兄弟,可以评论区或私信告诉我,我可能会将它实现并放到下一篇博客中。

结束语

  在创意与爱的交织下,这不仅仅是一段代码的展现,更是一次心灵的触碰与跨越。期待未来能在CSDN这片沃土上,继续播种灵感,收获更多温馨与惊喜。每一次的回归,都是新旅程的开始,愿与每一位读者共享知识的盛宴。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2045689.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

谷歌浏览器网页底图设置为全黑

输入网址:chrome://flags/ 搜索dark,选择Enabled,重启浏览器即可

C#使用SharGL实现PUMA560机械臂

1、四轴机械臂 下载链接:https://download.csdn.net/download/panjinliang066333/89645225 关键代码 public void DrawRobot1(ref OpenGL gl,float[] angle,float[] yLength,bool isPuma560_Six){//坐标系说明://①X轴正向:屏幕朝右//②Y轴…

【运维系列】windows虚拟机作为服务器,将服务启动作为脚本设置为开机自启,服务中断、手动操作的烦恼通通滚蛋!

文章目录 前言一、开机启动文件夹(StartUp)是否可行?二、任务计划程序1.编写脚本2.打开任务计划程序3.创建任务4.配置常规选项5.配置触发器选项6. 配置操作选项7.配置条件选项8.配置设置选项 总结 前言 在实际应用过程中,我们难免…

有没有电脑桌面监控软件|大佬都在用的7大电脑屏幕监控软件!

当谈到电脑桌面监控软件时,确实有许多受欢迎且功能强大的选项。 这些软件在企业管理、远程办公、家庭监控等多个领域都有广泛应用。 以下是大佬常用的7大电脑屏幕监控软件推荐: 1. Teramind 特点:它是一款功能强大的企业级监控软件&#x…

永久旋转 PDF 文件的 3 种简便方法

PDF 文件通常由扫描仪创建,用于呈现文档或书籍。当您输出 PDF 作品时,打开它,会发现有几页是颠倒的。 你应该做什么? 将这些页面倒置扫描,按顺序排列,最后创建正确的 PDF 文件? 当然&#xf…

<数据集>安全帽和安全背心识别数据集<目标检测>

数据集格式:VOCYOLO格式 图片数量:22141张 标注数量(xml文件个数):22141 标注数量(txt文件个数):22141 标注类别数:3 标注类别名称:[helmet, vest, head] 序号类别名称图片数框数1helmet15937572402v…

轻量级TinyDB数据库文件写入和增删改查操作

1. TinyDB 数据库简介 TinyDB 是一个轻量级的 NoSQL 文档型数据库,由 Python 实现,无需额外的配置,以 JSON 文件作为存储,默认使用文件系统来存储数据。 2. 安装基本库 pip install tinydb pip install Faker3. 数据库操作 im…

无线麦克风哪个品牌音质最好,口碑最好的麦克风品牌推荐

​随着个人媒体和视频内容的普及,每个人都有机会成为内容的创作者。在这样的趋势下,拥有一个好的录音设备就显得尤为重要。无线领夹麦克风正好满足了这种需要,它不仅能够提供清晰、稳定的音质,还能使内容创作更具有专业性和观赏性…

基于个性化定制的智慧校园管理系统设计与开发

TOC springboot296基于个性化定制的智慧校园管理系统设计与开发 第1章 绪论 1.1 课题背景 二十一世纪互联网的出现,改变了几千年以来人们的生活,不仅仅是生活物资的丰富,还有精神层次的丰富。在互联网诞生之前,地域位置往往是…

C语言常用的内存函数

在上一篇博客中我为大家分享了一些常用的字符串函数,以及它们的用法和模拟实现。通过字符串函数中的strcpy,我们能够做到将一个字符串中的内容拷贝到另一个字符串上,可如果有一天我们想把一个整型数组中的内容拷贝到另一个整型数组中呢&#…

大模型混合云,到了系统性创新突围的关键时刻

文 | 智能相对论 作者 | 陈泊丞 亚马逊与Anthropic、微软与OpenAI以及华为云提出的大模型混合云概念等等种种信号都在表明,云计算与大模型深度融合发展的趋势已是业内共识。 目前,以生成式AI为导向,越来越多的公司开始加速大模型技术在云计…

够豪横,310万不要了,后续损失恐上千万

近日,深圳一套法拍房引起了整个圈子的轰动! 20年前花费382万购入的豪宅,现在竟能拍到8389万,翻了22倍! 就在大家都感慨深圳顶豪的购买力惊人时,事情却迎来了更大的反转! 该豪宅竟重现法拍市场…

【HarmonyOS】鸿蒙应用实现调用系统地图导航或路径规划

【HarmonyOS】鸿蒙应用实现调用系统地图导航或路径规划 前言 在涉及地图业务中,调用地图导航和路径规划是三方应用中较为常见的功能。 若只是子业务需要地图导航效果,整个APP内部集成地图去实现导航或者路径规划,会造成SDK集成冗余。毕竟很…

校园外卖服务系统设计与实现

TOC springboot292校园外卖服务系统设计与实现 第一章 课题背景及研究内容 1.1 课题背景 信息数据从传统到当代,是一直在变革当中,突如其来的互联网让传统的信息管理看到了革命性的曙光,因为传统信息管理从时效性,还是安全性&…

若依 Ruoyi4.7.9 创建页面并显示

自行从https://gitee.com/y_project/RuoYi下载源代码,这个不再赘述。 // 下载后会发现现在的Ruoyi环境,分成了6个模块: 模块主要的介绍大家可以看文档。 官网地址:https://doc.ruoyi.vip/ruoyi/ 我下载的这个是基于mvc框架&…

CTFHUB-技能树-Web题-RCE(远程代码执行)-远程包含-命令注入-过滤空格

CTFHUB-技能树-Web题-RCE&#xff08;远程代码执行&#xff09;-远程包含-命令注入-过滤空格 就是过滤掉了空格 找能代替空格的代替就行了 使用IFS$9、%09、<、>、<>、{,}、%20、${IFS}、${IFS}来代替空格 127.0.0.1;cat${IFS}xxxx.php先来查看一下目录 127.0.0…

python可视化数据练习(地形图)

一、数据展示&#xff08;可通过目录直接跳转到代码部分&#xff09; {"lastUpdateTime":"2021-08-18 10:53:30","chinaTotal":{"confirm":122482,"heal":112984,"dead":5669,"nowConfirm":3829,"…

基于Spring boot的名城小区物业管理系统

TOC springboot240基于Spring boot的名城小区物业管理系统 绪论 1.1研究背景与意义 1.1.1研究背景 近年来&#xff0c;第三产业发展非常迅速&#xff0c;诸如计算机服务、旅游、娱乐、体育等服务行业&#xff0c;对整个社会的经济建设起到了极大地促进作用&#xff0c;这一…

Xilinx(AMD)的怪异行为——ila的radix没有real格式

使用vivado 2021.1的过程中发现&#xff0c;仿真时&#xff0c;可以选择把数据的radix按照定点或浮点格式显示&#xff1a; 显示效果如下&#xff1a; 仿真完后&#xff0c;在ila中观察&#xff0c;发现定点和浮点显示的菜单少了几项&#xff1a; xilinx为什么会把这么有用的…

剑指offer--面试题58.翻转字符串

题目描述 输入一个英文句子&#xff0c;翻转句子中单词的顺序&#xff0c;但单词内字符的顺序不变。为简单起见&#xff0c;标点符号和普通字母一样处理。例如输入字符串”I am a student.“,则输出”student. a am I“。 算法分析 完整代码 #include <string.h> //把起…