最近我把 devpiano 推进到了 v0.1.1,这是项目计划中的第一个发布版本。
写这篇文章并不是为了做详尽的项目介绍——如果你对项目本身感兴趣,可以直接去看 README。我想记录的,是我如何从一个 FreePiano 的普通用户,转变为独立发起 devpiano 开源项目的开发者。
devpiano 是我第一个真正意义上从零开始主导的开源项目。在这之前,我参与的开源项目大多是别人已经搭好了基本框架、定好了发展方向,我只是在其中贡献一些代码,添砖加瓦。但 devpiano 不同,从最初的构想到建立代码仓库,从敲下第一行代码的 commit 到最终打包发布,整个过程都是我在全盘推进。这种体验,与参与他人主导的项目截然不同,充满了挑战与成就感。
起心动念
我一直很喜欢用 FreePiano,这是一款 Windows 平台上的键盘钢琴工具。只要敲击电脑键盘,就能发出钢琴的声音,平时用来练琴非常方便。
用得久了,我心里冒出了一个想法:我能不能自己也做一个类似的软件?
FreePiano 是一个年代比较久远的项目了,它的代码是基于较老的 Windows 原生 API 编写的,整体架构也带着时代的印记。并不是说它不好,只是如果在那个基础上继续修改和扩展,我感觉会有些吃力,不够顺手。
我曾尝试 fork FreePiano 的源码作为参考,并主动联系了原作者李佳(tiwb),探讨接手维护的可能性。作者非常热情,甚至邀请我成为项目的维护者。但在我提供 GitHub 用户名后,可能因为他忙于现实生活,这件事便没有了下文。
深思熟虑后,我决定自己从头开始——使用现代的框架和工程实践,去打造一个让自己用起来真正觉得舒服和顺手的工具。至于 fork 过来的源码,我将它们保留在仓库中作为迁移的参考,但不直接用于新项目的构建。
这就是 devpiano 的起点:它不是为了替代 FreePiano,也不是为了打造一个商业产品,它纯粹是我想要去完成的一个个人项目。
技术选型:C++ 与 JUCE
在技术选型阶段,我考虑了几个方向。
首先是编程语言。我选择了 C++。考虑到音频处理和 MIDI 交互对性能和底层控制的要求,C++ 在这方面的生态最为成熟,性能也完全能够胜任。虽然 Python 等脚本语言也能实现类似功能,但用于开发桌面音频应用总感觉力不从心。C# 也是一个潜在的选项,但我更倾向于探索跨平台的可能性,而 C++ 相对更底层,更符合我的需求。
其次是框架。我选择了 JUCE。JUCE 将音频处理、MIDI 交互、UI 构建等我需要的核心组件都进行了良好的封装,让我无需从头“造轮子”。在音频应用开发领域,JUCE 是一个非常成熟的框架,拥有活跃的社区和丰富的文档支持。
实际开发中,我深刻体会到 JUCE API 设计的优雅,远比直接调用 Windows API 舒适得多。它将音频设备管理、插件宿主、组件树等复杂逻辑都高度抽象化了。虽然 JUCE 有一定的学习曲线,但与自己手写 WASAPI 或 ASIO 代码相比,门槛已经大幅降低。
开发环境方面,我采用了 WSL 主工作树与 Windows 镜像树相结合的混合模式。在 WSL 中进行代码编辑,利用 clangd 提供高效的 LSP 提示;在 Windows 环境下验证和构建,使用 MSVC 编译器。构建系统选用了 CMake 搭配 Ninja,并编写了 ./scripts/dev.sh 脚本来管理环境自检和构建流程。刚开始接触这套工具链时觉得有些繁琐,但熟练掌握后,发现它极大地提升了开发效率。
核心功能概览
说了这么多,devpiano 究竟能实现哪些功能?
简而言之:它能将你的电脑键盘变成钢琴键盘,按下 QWE 等按键就能发出美妙的乐音。它支持加载专业的 VST3 插件,提供更丰富的音色选择。你还可以用它录音、播放、调整速度,并将你的演奏保存为文件,随时回味。
具体功能包括:
- 将电脑键盘输入映射为 MIDI 信号,驱动 VST3 插件或内置的 fallback 合成器发声
- 支持接入外部 MIDI 设备
- VST3 插件的扫描、加载与参数编辑
- 演奏的实时录制与回放
- MIDI 文件的导入与导出
- 利用 fallback 合成器进行 WAV 格式的离线导出
- 专属的 .devpiano 格式,用于保存和加载演奏记录
- 灵活的播放速度控制(0.5x 至 2.0x)
- 键盘布局预设的保存与快速切换
这些功能并非一蹴而就,而是经过几个阶段的迭代逐步完善的。
从“堆功能”到工程化架构
坦白讲,在项目初期,我完全处于一种“堆功能”的状态——只要代码能跑通就行,几乎没有考虑代码结构和架构设计。结果导致 MainComponent 文件变得异常庞大,甚至超过了一千五百行,我自己看着都感到头疼。
意识到这种开发模式不可持续后,我开始着手进行架构的重构和收敛。我将 MainComponent 中的逻辑逐步拆分出去:布局管理、录制回放控制、插件操作、设置窗口、状态快照构建等功能,都被独立成了各个模块。
重构完成后,MainComponent 的代码量从 1587 行锐减到 600 行左右。这并不是说我的重构技巧有多么高超,而仅仅是将原本杂乱无章的代码按照职责进行了合理的划分,使得各个模块边界更加清晰,各司其职。
这个过程让我深刻体会到:在个人项目的初期,快速验证想法、让项目先“跑起来”是正确的策略;但当项目发展到一定阶段,就必须回过头来进行整理和重构,否则代码库会变得越来越难以维护。
AI 助手的得力辅助
在 devpiano 的开发过程中,我充分利用了 AI 的辅助能力。我并没有指望 AI 能够“全自动写代码”,而是把它当作一个随时可以请教的编程助手。
AI 能够迅速理解并辅助实现我的想法,这让我感到非常惊喜。它能够熟练运用各种工具和技能,帮助我将项目做得越来越规范化、工程化,这个过程充满了乐趣。
例如,我会向 AI 提问:
- 某个特定的 JUCE API 应该如何使用?
- 某个功能的设计思路是否合理?
- 代码中潜在的 bug 可能出在哪里?
- 帮我生成一些繁琐的模板代码。
为了让 AI 更好地理解项目的规范,我专门编写了 AGENTS.md 文件,定义了开发规范和边界。AI 助手严格遵循这些规范,确保了代码风格的统一和项目的一致性。
当然,AI 并非万能。对于架构决策、核心业务逻辑以及关键的技术选型,仍然需要我自己来把控。AI 提供的建议有时也会存在偏差,需要我进行判断和取舍。AI 的真正价值在于显著提升开发效率,而非替代开发者的独立思考。
踩坑实录
在开发过程中,我也遇到了一些棘手的问题,在此记录下来,也算是一份经验总结。
音频设备初始化顺序问题:项目初期,经常会出现启动后的第一个音符走音的情况。经过仔细排查,发现是音频设备初始化顺序不当导致的,后来通过引入 25ms 的预热(warmup)时间解决了这个问题。
MIDI 导入播放首音无声:导入 MIDI 文件后播放,第一个音符经常没有声音。这个问题困扰了我很久,最终通过添加播放起始的预滚(playback-start pre-roll)机制才得以修复。
散落的 Logger 调用:初期代码中随处可见 juce::Logger::writeToLog 和 DBG 等日志调用,导致调试信息非常混乱。后来我统一引入了 DP_LOG_* 系列宏,并区分了 Debug 和 Release 环境下的日志行为,使日志输出更加清晰有序。
MainComponent 膨胀:如前所述,这是项目初期“堆代码”带来的直接后果。
项目现状
目前的 devpiano 处于什么状态?
v0.1.1 版本是一个初步可用、功能相对完整的版本。核心的演奏、录制、回放、导出功能均已就绪。用户可以保存和打开演奏文件,并调整播放速度。此外,项目的基础设施(如 Diagnostics、测试夹具等)也已初步搭建完成。
还有一些尚未实现的功能:
- 最近文件列表及拖拽打开文件
- 基础的 MIDI 编辑功能
- VST3 插件的离线渲染
- 外部 MIDI 硬件的兼容性验证(目前手头缺乏测试设备)
这些都是未来计划中要实现的功能,但为了保持项目目前处于一个“可用且精简”的状态,我选择暂缓开发。
未来规划
短期计划:
- 实现最近文件列表及拖拽打开功能
- 增加基础的 MIDI 编辑功能(如删除音符)
长期规划:
- 引入多轨支持
- 开发 Piano roll(钢琴卷帘)编辑器
- 支持多语言用户界面
虽然这些长期目标还需要时间去实现,但我会一步一个脚印,先把手头的短期目标完成好。
写在最后
回首这段开发历程,devpiano 带给我的远不止“做出了一个能用的工具”这么简单。
首先,我深刻感受到了 AI 技术发展带来的震撼。能够与 AI 紧密配合完成一个项目,从最初的构想到最终的实现,AI 能够准确理解我的意图,并帮助我将项目做得更加规范化、标准化。这种人机协同的体验是前所未有的。
其次,在技术层面我也收获颇丰。钢琴、音乐、编程,当这三个我热爱的事物结合在一起时,本身就是一件无比快乐的事情。
最后,我学会了如何去管理和推进一个完整的项目。从 AI 工具的使用、项目管理、代码标准化到工程化实践,这些概念以前对我来说只是停留在书本上,而这次亲身实践让我深刻体会到了其中的门道和细节。
这是一个从 0 到 1 完整构建项目的宝贵体验:从一个简单的想法到建立代码仓库,从实现第一个功能到发布第一个版本,从为了实现功能而堆砌代码到关注架构和可维护性。在这个过程中学到的知识和经验,远比单纯编写代码要丰富得多。
虽然项目离最终完善还有很长的路要走,但至少它证明了一点:我有能力将脑海中的想法,转化为一个真实运行的代码仓库。
剩下的路,我会慢慢走,不断完善 devpiano。
如果你对 devpiano 感兴趣,可以观看 B 站上的演示视频:https://www.bilibili.com/video/BV1SRRBBhEf2