szjuliet 发表于 2020-8-29 23:02:34

基于Python和Arduino的MIDI架子鼓

https://www.bilibili.com/video/BV1AA411n7q2/

从小我就想要一个架子鼓。那时的音乐设备没有我们今天这些丰富的数字应用。最近我决定从eBay购买最便宜的架子鼓,最低要求是能够拆下来并将我自己软硬件附加到设备上。




这次入手令人相当满意:便携式可折卷的架子鼓套件,带9个不同的音垫(sound pad),两个脚踏开关,一个击鼓,一个击铙(hi-hat),还有一个Micro USB电源插座。真正令人兴奋的是输出声音(将架子鼓套件连接音箱来欣赏)。我决定将这玩意儿转换为自己的可编程设备,通过USB连接基于Arduino的MIDI架子鼓,用户界面使用Python来编写。这样可以方便使用和轻松修改,如音量、音符及选择通道。



**译注**:hi-hat在wiki百科中的解释就是两片钹加一个踏板,就是下图这个东西。不知道翻译成“铙”对不对。原文的鼓组图片不是很清楚,从wikipedia上找了张图,看图一目了然:

![](https://mc.dfrobot.com.cn/data/attachment/album/202008/28/091253qc7ggaz6k9s1qq96.jpg)

### **设备亮点:**

+ 价格亲民
+ 可以从任何数字输入 - 甚至是Arduino的按钮阵列来创建架子鼓组合
+ 仅通过USB接口提供通信支持和电源 - 集成了USB至UART转换器和Arduino设备
+ 仅需最少的部件即可正常操作
+ 基于Python的易于使用的用户界面
+ 带可调节力度、音符及Arduino引脚的完整的MIDI支持
+ 保存并加载存储在设备内存中的自定义架子鼓配置

## 步骤1 操作原理

### **项目框图**

如图,我们将项目结构划分若干部分:



### 1. **可折卷架子鼓**

这部分是项目的主体。它由9个单独的击打鼓垫组成,每个打击垫都是一个按钮阵列,可在击打时改变逻辑状态。它的结构可以让任何按钮来构造特定的鼓组。每个鼓垫都连接到主板上的上拉电阻,当反复击打鼓垫时,特定开关连接到电路的接地,鼓垫线路上呈现逻辑低电平。如果没有施加压力,则鼓垫开关断开,由于接了上拉电阻,鼓垫线路上呈现逻辑高电平。由于该项目是创建一个完整的数字MIDI设备,因此可以忽略主PCB上的所有模拟部件。请务必注意,架子鼓有两个踏板,用于击鼓和铙 - 它们同样也接了上拉电阻并和其它鼓垫共享逻辑操作(我们稍后讨论)。

### 2. **Arduino Pro-Micro**

这部分是架子鼓的大脑。它的作用是检测鼓垫是否发出信号,并提供所有必要参数的MIDI输出:音符、力度和信号的持续时间。由于鼓垫的数字特性,可以将它们简单地连接到arduino的数字输入(共10个引脚)。为了存储所需的设置和MIDI信息,我们将使用其内存-EEPROM,每次打开设备电源都会从EEPROM加载MIDI信息,从而使其可重新编程和重新配置。另外,Arduino Pro-Micro的尺寸非常小,可以很容易地在架子鼓内部的盒子中放置​配。

### 3. **FTDI USB转串行转换器**

由于Arduino Pro-Micro没有USB接口,为了能在电脑中对设备功能进行编程和定义,需要将USB接口转换为串行接口。由于设备之间的通信基于UART,而FTDI设备简便易用,因此在本项目中使用FTDI USB转换器。

### 4. **电脑端应用程序 - Python**

涉及到用户界面和快速构建项目的开发时,Python是一个极好的解决方案。UI应用程序可以更加方便的为架子鼓重定义MIDI属性、存储信息、编程设备以及在系统之间进行通信,而无需一遍又一遍地编译代码。我们使用串口与架子鼓进行通信,网上有很多免费模块支持任何类型的串行通信。另外正如稍后要讨论的,UART接口总共由三个引脚组成:RXD,TXD和DTR。DTR用于在Arduino模块上执行重置,因此当我们运行MIDI应用程序或将用户界面连接到程序设备时,完全不需要重新连接USB或任何其他设备。





## 步骤2 零部件和工具

### **零部件**
+ [可折卷架子鼓套件](https://www.aliexpress.com/item/32969143448.html?src=google&src=google&albch=shopping&acnt=494-037-6276&isdl=y&slnk=&plac=&mtctp=&albbt=Google_7_shopping&aff_platform=google&aff_short_key=UneMJZVf&&albagn=888888&albcp=9594014534&albag=99232325655&trgt=296904913880&crea=he32969143448&netw=u&device=c&albpg=296904913880&albpd=he32969143448&gclid=CjwKCAjw95D0BRBFEiwAcO1KDH_diuScZTAVof9Sys2xLjpCVMSUTUoCVUnrkeL3jUFgl_zhwLFEVhoCOS4QAvD_BwE&gclsrc=aw.ds)

![](https://mc.dfrobot.com.cn/data/attachment/album/202008/18/145056rizmll6ao52olfob.jpg)

+ (https://www.ebay.com/itm/Foot-Sustain-Pedal-Controller-Switch-For-Electronic-Keyboard-Piano-GuitarT5/142854833642?hash=item2142cfc5ea:g:B2wAAOSwke9aG6PD)(通常包含在架子鼓包装中)

+ (https://www.ebay.com/itm/FT232RL-3-3V-5-5V-FTDI-USB-to-TTL-Serial-Adapter-Module-for-Arduino-Mini-Port/382715199065?hash=item591b9a7e59:g:22MAAOSw6jVdUoOx)



+ (https://www.ebay.com/itm/ATmega328P-MINI-5V-16M-Micro-controller-Board-Arduino-Pro-Mini-328-5V-16MHz-ASS/264284914955?hash=item3d889bc50b:g:IQ0AAOSwvp5ZfxVN)



+ (https://www.ebay.com/itm/10cm-Portable-Replacement-Micro-USB-Charging-Cable-Phone-Charge-Cord-for-Android/173961102105?hash=item2880e3c319%3Am%3Ama2JXSv9aQstAx0m04vGCtA&LH_BIN=1)

### **工具**
+ 电烙铁/焊台
+ 焊锡
+ 细径单芯线
+ 镊子
+ 切刀
+ 老虎钳
+ 刀
+ 螺丝刀
+ 3D打印机(可选-用于制作踏板平台)

### **软件**
+ (https://www.arduino.cc/en/main/software)
+ (https://www.python.org/downloads/)
+ (https://www.jetbrains.com/pycharm/download/)
+ (https://projectgus.github.io/hairless-midiserial/)
+ (http://www.tobias-erichsen.de/software/loopmidi.html)

## 步骤3 焊接和组装



焊接和组装过程非常简单,只需将三个模块组合在一起:

+ 将Arduino Pro-Micro与FTDI设备连接在一起:
+ **VBUS-VBUS**
+ **GND-GND**
+ **DTR-DTR**
+ **RXD-TXD**
+ **TXD-RXD**

![](https://mc.dfrobot.com.cn/data/attachment/album/202008/18/145056l38irayrm3o1bzmb.jpg)

![](https://mc.dfrobot.com.cn/data/attachment/album/202008/18/145056tk4chch9ncg43ckg.jpg)



+ 卸下架子鼓塑料外壳上的所有螺钉,集中注意力连接鼓垫和板子以及上拉电阻



+ 用细线焊接上一步接好的Arduino-FTDI模块:
+ **数字输入: **
+ **VBUS**
+ **D+**
+ **D-**
+ **GND**



+ 将模块插入电池盒内,使导线与鼓垫的上拉电阻浮在同一侧



+ 如图所示,将所有数字输入焊接到鼓垫端子。



+ 将Micro-USB总线(VBUS,D +,D-,GND)焊接到FTDI设备,确保没有错误。

+ 将带有热胶的Arduino-FTDI模块连接到电池盒

+ 重新用螺钉组装设备

我们已经完成了组装,下一步开始编写代码。

## 步骤4:编程A:Arduino


下面我们逐段说明Arduino代码:

+ 为了正常操作架子鼓,需要包括两个必要的库。EPROM已预先安装在Arduino IDE中,但用于击鼓的去抖模块(debouncer module)必须[单独安装](https://github.com/hideakitai/Debouncer)。
`#include <Debouncer.h><br>#include <EEPROM.h>`

+ 下面这些开关主要用于调试。如果你想尝试鼓垫与Arduino引脚的连接,并确定所有的数字输入,那么需要定义这些开关。
`/* Developer Switches: Uncomment desired mode for debugging or initializing */<br>//#define LOAD_DEFAULT_VALUES // Load constant values instead of EEPROM
//#define PRINT_PADS_PIN_NUMBERS // Print pin number that is connected to a pad that was hit via serial port`

+ 常数字段代表所有默认值,包括鼓垫的枚举。在首次运行设备时,需要了解踏板和铙的确切连接(引脚)。
`/* Drum type enumeration */`
```
enum DRUM_POSITION { KICK = 0, SNARE, HIHAT, RIDE, CYMBAL1, CYMBAL2, TOM_HIGH, TOM_MID, TOM_LO, HIHAT_PEDAL };``/* Default values */
const uint8_t DRUM_NOTES = { 36, 40, 42, 51, 49, 55, 47, 45, 43, 48};
const uint8_t DRUM_VELOCITIES = { 110, 100, 100, 110, 110,110,110,110,110,110};
const uint8_t DRUM_PINS = { 8, 6, 4, 3, 11, 9, 5, 10, 2, 7 };`
`/* Kick drum debounce duration */
const uint8_t KICK_DB_DURATION = 30;
```

+ EEPROM用于存储/加载来自PC应用程序的所有数据。上述地址范围显示了每个鼓垫的MIDI信息的确切位置
`/* EEPROM Addresses mapping <br>`
```
Notes:      |0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09|
Pins:       |0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0x10,0x11,0x12,0x13|
Velocities|0x14,0x15,0x16,0x17,0x18,0x19,0x20,0x21,0x22,0x23| */
const uint8_t NOTES_ADDR = 0x00;
const uint8_t VELOCITIES_ADDR = 0x14;
const uint8_t PINS_ADDR = 0x0A;
```

+ 全局变量用于确定每个鼓垫的状态,并相应地执行MIDI通信。
`/* Global Variables */<br>`
```
uint8_t drumNotes, drumVelocities, drumPins;// MIDI Variables
uint8_t uartBuffer;                                 // UART Buffer for collecting and storing MIDI Data
Debouncer kick(DRUM_PINS, KICK_DB_DURATION);      // Debouncer object for kick drum
volatile bool previousState = {0,0,0,0,0,0,0,0,0};   // Drum pad previous logic states
volatile bool currentState = {0,0,0,0,0,0,0,0,0};      // Drum pad current logic states
```

+ EEPROM函数。
`/* Store settings in the EEPROM*/`

```
void storeEEPROM() {
memcpy(drumNotes, uartBuffer, 10);
memcpy(drumPins, uartBuffer + 10, 10);
memcpy(drumVelocities, uartBuffer + 20, 10);
for (uint8_t i = 0; i < 10; i++) EEPROM.write(NOTES_ADDR + i, drumNotes);
for (uint8_t i = 0; i < 10; i++) EEPROM.write(PINS_ADDR + i, drumPins);
for (uint8_t i = 0; i < 10; i++) EEPROM.write(VELOCITIES_ADDR + i, drumVelocities);
}```

```
/* Load settings from the EEPROM*/
void loadEEPROM() {
for (uint8_t i = 0; i < 10; i++) drumNotes = EEPROM.read(NOTES_ADDR + i);
for (uint8_t i = 0; i < 10; i++) drumPins = EEPROM.read(PINS_ADDR + i);
for (uint8_t i = 0; i < 10; i++) drumVelocities = EEPROM.read(VELOCITIES_ADDR + i);
}

```

- 在踏板和Arduino启动同时激活的情况下,初始化变量和编程模式。


- MIDI通信处理程序,延迟1ms的音符保持时间
`/* Play MIDI note function */`

+ 设备循环操作的setup()和loop()函数:
`void setup() {`

```
Serial.begin(115200);
for (uint8_t i = 0; i < 10; i++) {
    pinMode(i + 2,INPUT);
}
#ifdef PRINT_PADS_PIN_NUMBERS
    while(true) { // Infinite debug loop
       for (uint8_t i = 0; i < 10; i++) {
      if (!digitalRead(i + 2)) {
          Serial.print("Pin No: D");
          Serial.print(i + '0'); // Convert number to ASCII character
      }
       }
    }
#else
initValues();
/* Programming mode: If two pedals are pressed while booting - mode is activated */
if (!digitalRead(drumPins) && !digitalRead(drumPins)) enterProgrammingMode();
#endif
}
```

```
void loop() {<br>    for (uint8_t i = 1; i < 9; i = i + 1) {
      currentState = digitalRead(drumPins);
      if (!currentState && previousState) midiOut(i); // Compare states and detect falling edge
      previousState = currentState;
    }
    kick.update(); // Kick drum uses custom debounce algorithm
    if (kick.edge())if (kick.falling()) midiOut(KICK);
   
}
```
## 步骤5 编程B:Python和用户界面



乍一看Python用户界面有点复杂,下面我们试着解释基本知识、使用方法、每个按钮具有什么功能以及如何正确编程Arduino设备。

### **用户界面-应用程序**




UI用户界面是架子鼓的图形表示形式,它使得在任何时候对Arduino设备的编程既易用又方便。用户界面由几个图形模块组成,这些图形模块与它们的建议操作相关。我们一个个来看:

1. **鼓组图片**: Python UI使用不同鼓的图片的XY坐标来确定选择了哪种鼓型。如果选择了有效的鼓区,则会显示辅助IO消息,包括音符、力度和鼓垫的Arduino终端。这些参数由用户验证并确认后,会直接传输到Arduino设备。

2. **外部控制器图像**:为了能够在VST/音乐创建环境中使用MIDI鼓组,需要运行Serial-To-MIDI解释器。我使用过Hairless的,免费,只需按压其图像即可直接从UI运行。

3. **COM端口列表**:为了与Arduino通信,需要指定其连接的COM端口。按下刷新按钮可以刷新端口列表。

4. **加载/保存配置**:代码中定义了默认的MIDI值,用户可以通过与UI交互进行修改。在config.txt文件中配置以特定格式定义,可以由用户保存或加载。

5. **程序设备按钮**:为了将所有修改后的MIDI值存储在Arduino EEPROM中,需要踩下两个脚踏板(鼓和铙),等待数据传输完成。如果出现任何通讯问题,将弹出相关的窗口。如果传输成功,UI显示成功消息。

6. **退出按钮**:仅在用户许可下退出应用程序。

### **Python代码重点**

代码完成了很多工作,我们下面逐段说明。

为了使用UI,需要先下载几个模块来使代码正常工作:

```
import os<br>import threading
import tkinter as tk
from tkinter import messagebox
from tkinter import *
from PIL import ImageTk, Image
import numpy as np
import serial
import glob
```
一些模块包含在Python默认软件包中。可以通过PIP工具安装几个模块:
```
pip install Pillow
pip install numpy
pip install ScreenInfo
```
强烈建议通过PyCharm运行应用程序。我计划在将来的版本导出该项目的可执行文件。

### **简要代码说明**

从函数和类的角度看代码会更容易理解:

1.main 函数 - 代码从这里开始
`if __name__ == '__main__':<br>    drumkit_gui()`

2.鼓组常数、坐标和默认MIDI信息
```
class Drums:<br>    DRUM_TYPES = ["Kick", "Hihat", "Snare", "Crash 1", "Crash 2", "Tom High", "Tom Mid", "Tom Low", "Ride",
               "Hihat Pedal", "Controller"]
```
```
    COORDINATES_X =
    COORDINATES_Y =
    DIMS_WIDTH =
    DIMS_LENGTH =
```
```
    DRUM_ENUM = ["Kick", "Snare", "Hihat", "Ride", "Crash 1", "Crash 2", "Tom High", "Tom Mid", "Tom Low", "Hihat Pedal"]
    DRUM_NOTES =
    DRUM_VELOCITIES =
    DRUM_PINS =
```
3. UI函数 - 处理用户界面和图形对象
`def set_active(ui)`
`def secondary_ui(drum_type)`
`class SelectionUi(tk.Frame)`
`class Application(tk.Frame)`
`def drumkit_gui()`
`def event_ui_clicked(event)`
`def getorigin(self, event)`
4. 串行通讯
`def get_serial_ports()`
`def communicate_with_arduino(port)`
5. 使用文件:从txt文件存储/加载设置
`def save_config()`
`def load_config()`
6. 使用Python线程功能运行外部应用程序hairless.exe
`class ExternalExecutableThread(threading.Thread)`
`def run_hairless_executable()`

为了运行代码,必须在项目文件夹中附加一个文件列表:

+ config.txt:设置文件
+ (https://projectgus.github.io/hairless-midiserial/):Hairless MIDI转换器



+ drumkit.png:定义了我们UI上所有可击打的鼓垫图像(下图)

![](https://mc.dfrobot.com.cn/data/attachment/album/202008/28/222625y68bxbxppnnnvnbo.png)

+ drumgui.py:项目代码

要让架子鼓正常工作,以上这些就是我们需要强调的所有内容。将文件添加到项目中非常重要:鼓组图片、hairless.exe可执行文件和设置文件config.txt。请在附件中下载。

现在...大功告成啦!:)

感谢阅读!:)

> 原文链接:https://www.instructables.com/id/MIDI-Drum-Kit-on-Python-and-Arduino/   
> 作者:Faransky   
> 翻译:szjuliet
>

20060606 发表于 2020-9-3 20:47:00

好创意,赞一个

佛系唐法官 发表于 2020-9-5 13:30:04

{:6_209:}{:6_213:}

breaker_MAG 发表于 2020-9-20 18:37:18

赞{:5_131:}
页: [1]
查看完整版本: 基于Python和Arduino的MIDI架子鼓