XIAO MIDI 合成器入门指南

介绍
XIAO MIDI 合成器是一款紧凑的一体化音乐创作设备,由 XIAO ESP32-C3 和专业级 SAM2695 MIDI 芯片驱动。尽管体积小巧,但它能提供丰富、富有表现力的声音,支持多达 64 声部复音,并提供数百种乐器音色。无论您是实验声音的开发者还是创作新曲的音乐家,这款合成器都旨在让音乐创作变得流畅而富有灵感。
特性
- 零门槛创作,非常适合音乐初学者
- 一体化 MIDI + 节拍器 -- 更智能、更流畅的工作流程
- 为高级演奏者提供专业级性能
- 超便携且完全可定制
硬件概述
XIAO MIDI 合成器集成了以下组件:
核心模块 | |
---|---|
MCU | XIAO ESP32-C3 |
声音生成 | |
MIDI 芯片 | SAM2695 |
复音数 | 多达 64 声部 |
乐器预设 | 超过 100 种不同的乐器声音可用 |
接口和连接 | |
3.5mm 音频插孔 | 用于连接外部音频设备(如扬声器或耳机) 具有插入线缆时自动禁用 D 类放大器,拔出时重新启用的功能 |
物理按钮 | 四个侧面安装的物理按钮用于用户交互 |
XIAO 插座 | 专为 XIAO 模块设计的专用插座,可通过额外的侧面连接器进行扩展 |
D 类放大器 | 内置扬声器 |
电源和指示器 | |
电源供应 | 5V Type-C |
指示 LED | 单色 LED,用于指示电源状态并提供用户反馈 |


入门指南
本节将指导您首次设置 XIAO MIDI 合成器。
出厂固件使用
XIAO MIDI 合成器预装了多功能的出厂固件,让您可以开箱即用地探索多种声音和节拍功能。以下是默认功能和按键操作的快速指南。

详细按键功能
-
声音测试(默认模式):
- 🎵0:循环切换 128 种预设乐器声音。每次按下切换到下一个声音。
- 🎵1:提高音符音高(例如,从 C4 到 D4)。
- 🎵2:降低音符音高(例如,从 C4 到 B3)。
- 🎵3:点击开始/停止节拍器。
-
节拍器模式:
- 🎵0:(此模式下无功能)
- 🎵1:增加 BPM(节拍)1 步(仅在节拍器激活时有效)。
- 🎵2:减少 BPM(节拍)1 步(仅在节拍器激活时有效)。
- 🎵3:点击开始/停止节拍器(默认:120 BPM,范围:40–240 BPM)。
-
多轨播放模式:
- 🎵0:播放轨道 1(预加载的声音或序列)
- 🎵1:播放轨道 2
- 🎵2:播放轨道 3
- 🎵3:播放轨道 4
长按功能(任何模式):
- 🎵1(长按 2 秒):音量增加
- 🎵2(长按 2 秒):音量减少
- 🎵3(长按 2 秒):切换到下一个模式(循环切换:声音测试 → 节拍器 → 多轨播放 → ...)
您可以在无需任何额外设置的情况下体验所有功能。如需高级自定义或重新刷写固件,请参阅 StateMachine 示例代码。
引导加载器模式
在某些情况下,您可能会发现 XIAO MIDI 合成器无法通过串口编程(例如,设备无法识别或上传失败)。在这些情况下,您需要进入 引导加载器(BOOT)模式 来重新刷写固件。
为什么要进入 BOOT 模式?
- 当设备无法通过 USB/串口正常编程时恢复设备
- 在上传失败或固件损坏后重新刷写固件
如何进入 BOOT 模式
首先,打开迷你合成器的后盖以访问 XIAO ESP32-C3 模块上的 BOOT 和 RESET 按钮。有两种方法:

-
方法 1:按钮序列
- 在设备通电的情况下,按住 BOOT 按钮(不要松开)。
- 在按住 BOOT 的同时,按一次 RESET 按钮。
- 松开 BOOT 按钮。
-
方法 2:电源循环
- 断开电源(拔掉 USB-C 线缆)。
- 按住 BOOT 按钮。
- 在按住 BOOT 的同时,重新连接电源(插入 USB-C 线缆)。
- 松开 BOOT 按钮。
进入 BOOT 模式后,设备将准备好通过 Arduino IDE 或其他工具进行固件上传。
复位
要复位设备,只需按下 XIAO ESP32-C3 模块上的 RESET 按钮。设备将重新启动。
Arduino 库概述
如果这是您第一次使用 Arduino,我们强烈建议您参考 Arduino 入门指南。
XIAO MIDI 合成器由 Seeed_Arduino_MIDIMaster 库支持,该库提供全面的 MIDI 控制和声音合成功能。
功能
在我们开始开发代码之前,让我们先了解一下库的可用功能。
static SAM2695Synth& getInstance()
– 返回 SAM2695Synth 类的单例实例。
- 输入参数:无。
- 返回值:对单例 SAM2695Synth 对象的引用。
void begin(T& serial, int baud)
– 使用指定的串行接口和波特率初始化合成器。
- 输入参数:
T& serial
:用于 MIDI 通信的串行端口对象(硬件或软件串行)。int baud
:MIDI 通信的波特率(标准 MIDI 通常为 31250)。
- 返回值:无。
void setInstrument(uint8_t bank, uint8_t channel, uint8_t value)
– 为特定的 MIDI 通道和音色库设置乐器(音色)。
- 输入参数:
uint8_t bank
:乐器音色库编号(0–127)。uint8_t channel
:MIDI 通道编号(0–15)。uint8_t value
:乐器(程序)编号(0–127)。
- 返回值:无。
void setNoteOn(uint8_t channel, uint8_t pitch, uint8_t velocity)
– 在指定通道上触发音符开启事件,使用给定的音高和力度。
- 输入参数:
uint8_t channel
:MIDI 通道编号(0–15)。uint8_t pitch
:MIDI 音符编号(0–127)。uint8_t velocity
:音符的力度(音量)(0–127)。
- 返回值:无。
void setNoteOff(uint8_t channel, uint8_t pitch)
– 在指定通道上为给定音高触发音符关闭事件。
- 输入参数:
uint8_t channel
:MIDI 通道编号(0–15)。uint8_t pitch
:MIDI 音符编号(0–127)。
- 返回值:无。
void setAllNotesOff(uint8_t channel)
– 关闭指定通道上当前播放的所有音符。
- 输入参数:
uint8_t channel
:MIDI 通道编号(0–15)。
- 返回值:无。
void playChord(const musicData& chord)
– 通过为提供的和弦结构中的所有音符发送音符开启事件来播放和弦。
- 输入参数:
const musicData& chord
:包含和弦的通道、音符、力度和时间信息的结构。
- 返回值:无。
void setPitch(uint8_t pitch)
– 为后续音符事件设置默认音高值。
- 输入参数:
uint8_t pitch
:要设置为默认音高的 MIDI 音符编号(0–127)。
- 返回值:无。
uint8_t getPitch() const
– 获取当前默认音高值。
- 输入参数:无。
- 返回值:当前默认 MIDI 音符编号(0–127)。
void setVolume(uint8_t channel, uint8_t level)
– 为特定 MIDI 通道设置音量。
- 输入参数:
uint8_t channel
:MIDI 通道编号(0–15)。uint8_t level
:音量级别(0–127)。
- 返回值:无。
void increasePitch()
– 将默认音高值增加一个半音。
- 输入参数:无。
- 返回值:无。
void decreasePitch()
– 将默认音高值减少一个半音。
- 输入参数:无。
- 返回值:无。
void increaseVelocity()
– 增加音符事件的默认力度(音量)。
- 输入参数:无。
- 返回值:无。
void decreaseVelocity()
– 减少音符事件的默认力度(音量)。
- 输入参数:无。
- 返回值:无。
void increaseBpm()
– 将当前节拍(每分钟节拍数)按预定义步长增加。
- 输入参数:无。
- 返回值:无。
void decreaseBpm()
– 将当前节拍(每分钟节拍数)按预定义步长减少。
- 输入参数:无。
- 返回值:无。
void setBpm(uint8_t bpm)
– 将节拍(每分钟节拍数)设置为指定值。
- 输入参数:
uint8_t bpm
:所需的每分钟节拍数(通常为 40–240)。
- 返回值:无。
uint8_t getBpm() const
– 获取当前节拍(每分钟节拍数)。
- 输入参数:无。
- 返回值:当前 BPM 值。
默认变量
以下代码显示了库中预定义的一些值,如乐器类型、音符等,供查询使用。
点击此处预览完整代码
#define MIDI_SERIAL_BAUD_RATE 31250 //MIDI串口波特率
#define USB_SERIAL_BAUD_RATE 115200 //USB串口波特率
#define MIDI_COMMAND_ON 0x90 //MIDI控制开关 - 开
#define MIDI_COMMAND_OFF 0x80 //MIDI控制开关 - 关
#define BPM_DEFAULT 120 //默认bpm
#define BPM_MIN 40 //最小bpm
#define BPM_MAX 240 //最大bpm
#define BPM_STEP 10 //bpm步长
#define MIDI_CMD_CONTROL_CHANGE \
0xB0 // 控制变化(或通道模式消息)
#define MIDI_CMD_PROGRAM_CHANGE 0xC0 // 程序变化
#define VELOCITY_MIN 0 //最小力度(0~127) 0
#define VELOCITY_MAX 127 //最大力度(0~127) 127
#define VELOCITY_STEP 10 //力度步长
#define VELOCITY_DEFAULT 64 //默认力度(0~127) 64
#define BASIC_TIME 60000 //1分钟对应的毫秒数
#define QUATER_NOTE 0 //四分音符
#define EIGHTH_NOTE 1 //八分音符
#define SIXTEENTH_NOTE 2 //十六分音符
#define BEATS_BAR_DEFAULT 4 //默认节拍 - 4/4拍
#define BEATES_BAR_2 2 //2/4拍
#define BEATS_BAR_3 3 //3/4拍
#define BEATS_BAR_4 4 //4/4拍
#define NOTE_COUNT_DEFAULT 4 //默认音符数量
#define NOTE_COUNT_MIN 1 //最小音符数量
#define NOTE_COUNT_MAX 16 //最大音符数量
/* 定义通道开始 */
#define CHANNEL_0 0
#define CHANNEL_1 1
#define CHANNEL_2 2
#define CHANNEL_3 3
#define CHANNEL_4 4
#define CHANNEL_5 5
#define CHANNEL_6 6
#define CHANNEL_7 7
#define CHANNEL_8 8
#define CHANNEL_9 9
#define CHANNEL_10 10
#define CHANNEL_11 11
#define CHANNEL_12 12
#define CHANNEL_13 13
#define CHANNEL_14 14
#define CHANNEL_15 15
#define NOTE_B0 23
#define NOTE_C1 24
#define NOTE_CS1 25
#define NOTE_D1 26
#define NOTE_DS1 27
#define NOTE_E1 28
#define NOTE_F1 29
#define NOTE_FS1 30
#define NOTE_G1 31
#define NOTE_GS1 32
#define NOTE_A1 33
#define NOTE_AS1 34
#define NOTE_B1 35
#define NOTE_C2 36
#define NOTE_CS2 37
#define NOTE_D2 38
#define NOTE_DS2 39
#define NOTE_E2 40
#define NOTE_F2 41
#define NOTE_FS2 42
#define NOTE_G2 43
#define NOTE_GS2 44
#define NOTE_A2 45
#define NOTE_AS2 46
#define NOTE_B2 47
#define NOTE_C3 48
#define NOTE_CS3 49
#define NOTE_D3 50
#define NOTE_DS3 51
#define NOTE_E3 52
#define NOTE_F3 53
#define NOTE_FS3 54
#define NOTE_G3 55
#define NOTE_GS3 56
#define NOTE_A3 57
#define NOTE_AS3 58
#define NOTE_B3 59
#define NOTE_C4 60
#define NOTE_CS4 61
#define NOTE_D4 62
#define NOTE_DS4 63
#define NOTE_E4 64
#define NOTE_F4 65
#define NOTE_FS4 66
#define NOTE_G4 67
#define NOTE_GS4 68
#define NOTE_A4 69
#define NOTE_AS4 70
#define NOTE_B4 71
#define NOTE_C5 72
#define NOTE_CS5 73
#define NOTE_D5 74
#define NOTE_DS5 75
#define NOTE_E5 76
#define NOTE_F5 77
#define NOTE_FS5 78
#define NOTE_G5 79
#define NOTE_GS5 80
#define NOTE_A5 81
#define NOTE_AS5 82
#define NOTE_B5 83
#define NOTE_C6 84
#define NOTE_CS6 85
#define NOTE_D6 86
#define NOTE_DS6 87
#define NOTE_E6 88
#define NOTE_F6 89
#define NOTE_FS6 90
#define NOTE_G6 91
#define NOTE_GS6 92
#define NOTE_A6 93
#define NOTE_AS6 94
#define NOTE_B6 95
#define NOTE_C7 96
#define NOTE_CS7 97
#define NOTE_D7 98
#define NOTE_DS7 99
#define NOTE_E7 100
#define NOTE_F7 101
#define NOTE_FS7 102
#define NOTE_G7 103
#define NOTE_GS7 104
#define NOTE_A7 105
#define NOTE_AS7 106
#define NOTE_B7 107
#define NOTE_C8 108
#define NOTE_CS8 109
#define NOTE_D8 110
#define NOTE_DS8 111
#define REST 0
typedef enum {
GrandPiano_1 = 0,
BrightPiano_2,
ElGrdPiano_3,
HonkyTonkPiano,
ElPiano1,
ElPiano2,
Harpsichord,
Clavi,
Celesta,
Glockenspiel,
MusicBox,
Vibraphone,
Marimba,
Xylophone,
TubularBells,
Santur,
DrawbarOrgan,
PercussiveOrgan,
RockOrgan,
ChurchOrgan,
ReedOrgan,
AccordionFrench,
Harmonica,
TangoAccordion,
AcGuitarNylon,
AcGuitarSteel,
AcGuitarJazz,
AcGuitarClean,
AcGuitarMuted,
OverdrivenGuitar,
DistortionGuitar,
GuitarHarmonics,
AcousticBass,
FingerBass,
PickedBass,
FretlessBass,
SlapBass1,
SlapBass2,
SynthBass1,
SynthBass2,
Violin,
Viola,
Cello,
Contrabass,
TremoloStrings,
PizzicatoStrings,
OrchestralHarp,
Timpani,
StringEnsemble1,
StringEnsemble2,
SynthStrings1,
SynthStrings2,
ChoirAahs,
VoiceOohs,
SynthVoice,
OrchestraHit,
Trumpet,
Trombone,
Tuba,
MutedTrumpet,
FrenchHorn,
BrassSection,
SynthBrass1,
SynthBrass2,
SopranoSax,
AltoSax,
TenorSax,
BaritoneSax,
Oboe,
EnglishHorn,
Bassoon,
Clarinet,
Piccolo,
Flute,
Recorder,
PanFlute,
BlownBottle,
Shakuhachi,
Whistle,
Ocarina,
Lead1Square,
Lead2Sawtooth,
Lead3Calliope,
Lead4Chiff,
Lead5Charang,
Lead6Voice,
Lead7Fifths,
Lead8BassLead,
Pad1Fantasia,
Pad2Warm,
Pad3PolySynth,
Pad4Choir,
Pad5Bowed,
Pad6Metallic,
Pad7Halo,
Pad8Sweep,
FX1Rain,
FX2Soundtrack,
FX3Crystal,
FX4Atmosphere,
FX5Brightness,
FX6Goblins,
FX7Echoes,
FX8SciFi,
Sitar,
Banjo,
Shamisen,
Koto,
Kalimba,
BagPipe,
Fiddle,
Shanai,
TinkleBell,
Agogo,
SteelDrums,
Woodblock,
TaikoDrum,
MelodicTom,
SynthDrum,
ReverseCymbal,
GtFretNoise,
BreathNoise,
Seashore,
BirdTweet,
TelephRing,
Helicopter,
Applause,
Gunshot,
} unit_synth_instrument_t;
安装
步骤 1. 下载 Seeed_Arduino_MIDIMaster 库作为 ZIP 文件。
步骤 2. 在 Arduino IDE 中,点击 Sketch > Include Library > Add .ZIP Library 并选择下载的 ZIP 文件。

XIAO MIDI 合成器示例
现在我们已经安装了库并了解了基本功能,让我们运行一些 XIAO MIDI 合成器的示例来看看它的表现。
步骤 1. 启动 Arduino 应用程序。

步骤 2. 选择您的开发板型号并将其添加到 Arduino IDE。
- XIAO MIDI 合成器内置 XIAO ESP32-C3,请参考 此教程 完成添加。
演示 1:基本 MIDI 音符播放
此演示展示如何使用 XIAO MIDI 合成器播放单个音符。
所需材料
XIAO MIDI 合成器 |
---|
![]() |
参考程序
#include <Arduino.h>
#include "SAM2695Synth.h"
#ifdef __AVR__
#include <SoftwareSerial.h>
SoftwareSerial SSerial(2, 3); // RX, TX
#define COM_SERIAL SSerial
#define SHOW_SERIAL Serial
SAM2695Synth<SoftwareSerial> synth = SAM2695Synth<SoftwareSerial>::getInstance();
#endif
#if defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_ARCH_RP2350) || defined(ARDUINO_XIAO_RA4M1)
#include <SoftwareSerial.h>
SoftwareSerial SSerial(D7, D6); // RX, TX
#define COM_SERIAL SSerial
#define SHOW_SERIAL Serial
SAM2695Synth<SoftwareSerial> synth = SAM2695Synth<SoftwareSerial>::getInstance();
#endif
#if defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32S3)
#define COM_SERIAL Serial0
#define SHOW_SERIAL Serial
SAM2695Synth<HardwareSerial> synth = SAM2695Synth<HardwareSerial>::getInstance();
#endif
#ifdef SEEED_XIAO_M0
#define COM_SERIAL Serial1
#define SHOW_SERIAL Serial
SAM2695Synth<Uart> synth = SAM2695Synth<Uart>::getInstance();
#elif defined(ARDUINO_SAMD_VARIANT_COMPLIANCE)
#define COM_SERIAL Serial1
#define SHOW_SERIAL SerialUSB
SAM2695Synth<Uart> synth = SAM2695Synth<Uart>::getInstance();
#endif
#if defined(NRF52840_XXAA)
#ifdef USE_TINYUSB
#include <Adafruit_TinyUSB.h>
#endif
#define COM_SERIAL Serial1
#define SHOW_SERIAL Serial
SAM2695Synth<Uart> synth = SAM2695Synth<Uart>::getInstance();
#endif
void setup()
{
//串口初始化到 USB
SHOW_SERIAL.begin(USB_SERIAL_BAUD_RATE);
// 合成器初始化。由于这里使用硬件串口,软件串口被注释掉。
synth.begin(COM_SERIAL, MIDI_SERIAL_BAUD_RATE);
synth.setInstrument(0,CHANNEL_0,unit_synth_instrument_t::GrandPiano_1);
delay(1000);
}
void loop()
{
synth.setNoteOn(CHANNEL_0,NOTE_E4,VELOCITY_DEFAULT);
delay(1000);
synth.setNoteOff(CHANNEL_0,NOTE_E4);
delay(1000);
}
这段代码将在内置合成器上播放 E4 音符,每秒交替开关。您可以根据需要更改音符、乐器或时序。
程序注释
这段代码代表了 XIAO MIDI 合成器的核心功能,这是一个基于 SAM2695 合成器芯片的紧凑型音频合成解决方案。软件实现演示了合成器如何通过不同的串行通信方法与各种 XIAO 平台变体进行接口。
XIAO MIDI 合成器通过条件编译支持 XIAO 生态系统中的多个微控制器平台:
- 基于 AVR 的开发板:利用 SoftwareSerial 与合成器芯片通信
- RP2040/RP2350 和 XIAO RA4M1:在指定引脚(D7、D6)上实现 SoftwareSerial
- ESP32 变体:通过 Serial0 利用原生硬件串口功能
- 基于 SAMD 的开发板(包括 XIAO M0):使用硬件 UART 接口
- nRF52840:实现 UART 通信,可选支持 TinyUSB
这种多功能方法确保合成器模块在整个 XIAO 产品系列中都能一致工作。
合成器实现建立了两个通信路径:
SHOW_SERIAL.begin(USB_SERIAL_BAUD_RATE);
synth.begin(COM_SERIAL, MIDI_SERIAL_BAUD_RATE);
- 专用的串行连接到 SAM2695 芯片用于 MIDI 命令
- 独立的调试/用户界面串行连接用于监控和控制
在启动期间,XIAO MIDI 合成器:
- 在适当的波特率下建立串行通信
- 初始化 SAM2695 合成器引擎
- 在通道 0 上配置默认乐器(大钢琴)
synth.setInstrument(0,CHANNEL_0,unit_synth_instrument_t::GrandPiano_1);
- 允许通过时间延迟进行稳定
演示循环通过以下方式展示了基本的声音生成能力:
void loop()
{
synth.setNoteOn(CHANNEL_0,NOTE_E4,VELOCITY_DEFAULT);
delay(1000);
synth.setNoteOff(CHANNEL_0,NOTE_E4);
delay(1000);
}
- 以标准力度触发音符 E4
- 保持音符一秒钟
- 释放音符
- 在下一个循环前暂停
这个实现代表了使用 XIAO MIDI 合成器平台构建更复杂音乐应用的基础,无论是用于教育目的、音乐原型制作还是嵌入式音频项目。
演示 2:带按钮控制的节拍音序器
这个演示展示了如何使用 XIAO MIDI 合成器的物理按钮来控制一个简单的节拍音序器。您可以启动/停止节拍,并使用按钮实时调整节拍速度。
所需材料
XIAO MIDI 合成器 |
---|
![]() |
参考程序
#include <Arduino.h>
#include "SAM2695Synth.h"
#include "Button.h"
#ifdef __AVR__
#include <SoftwareSerial.h>
SoftwareSerial SSerial(2, 3); // RX, TX
#define COM_SERIAL SSerial
#define SHOW_SERIAL Serial
SAM2695Synth<SoftwareSerial> synth = SAM2695Synth<SoftwareSerial>::getInstance();
#endif
#if defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_ARCH_RP2350) || defined(ARDUINO_XIAO_RA4M1)
#include <SoftwareSerial.h>
SoftwareSerial SSerial(D7, D6); // RX, TX
#define COM_SERIAL SSerial
#define SHOW_SERIAL Serial
SAM2695Synth<SoftwareSerial> synth = SAM2695Synth<SoftwareSerial>::getInstance();
#endif
#if defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32S3)
#define COM_SERIAL Serial0
#define SHOW_SERIAL Serial
SAM2695Synth<HardwareSerial> synth = SAM2695Synth<HardwareSerial>::getInstance();
#endif
#ifdef SEEED_XIAO_M0
#define COM_SERIAL Serial1
#define SHOW_SERIAL Serial
SAM2695Synth<Uart> synth = SAM2695Synth<Uart>::getInstance();
#elif defined(ARDUINO_SAMD_VARIANT_COMPLIANCE)
#define COM_SERIAL Serial1
#define SHOW_SERIAL SerialUSB
SAM2695Synth<Uart> synth = SAM2695Synth<Uart>::getInstance();
#endif
#if defined(NRF52840_XXAA)
#ifdef USE_TINYUSB
#include <Adafruit_TinyUSB.h>
#endif
#define COM_SERIAL Serial1
#define SHOW_SERIAL Serial
SAM2695Synth<Uart> synth = SAM2695Synth<Uart>::getInstance();
#endif
#if defined(CONFIG_IDF_TARGET_ESP32S3)
#define BUTTON_A_PIN 4
#define BUTTON_B_PIN 3
#define BUTTON_C_PIN 2
#define BUTTON_D_PIN 1
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
#define BUTTON_A_PIN 5
#define BUTTON_B_PIN 4
#define BUTTON_C_PIN 3
#define BUTTON_D_PIN 2
#elif defined(CONFIG_IDF_TARGET_ESP32C6)
#define BUTTON_A_PIN 21
#define BUTTON_B_PIN 2
#define BUTTON_C_PIN 1
#define BUTTON_D_PIN 0
#else //您需要参考相应的手册...
#define BUTTON_A_PIN 0 //您需要参考相应的手册...
#define BUTTON_B_PIN 1 //您需要参考相应的手册...
#define BUTTON_C_PIN 2 //您需要参考相应的手册...
#define BUTTON_D_PIN 3 //您需要参考相应的手册...
#endif
//定义按钮所需的结构
BtnState btnA = {HIGH, HIGH, 0, 0, false};
BtnState btnB = {HIGH, HIGH, 0, 0, false};
BtnState btnC = {HIGH, HIGH, 0, 0, false};
bool music_on_off_flag = false;
int bpm = BPM_DEFAULT; // 初始化BPM,BPM_DEFAULT = 120
int beatsPerBar = BEATS_BAR_DEFAULT; // 每小节的拍数,可以是2、3或4
int noteType = QUATER_NOTE; // 音符类型选择:0(四分音符)、1(八分音符)、2(十六分音符)
int beatCount = 0; // 拍子计数器
unsigned long previousMillis = 0; // 记录上次发送MIDI信号的时间
void setup()
{
//串口初始化到USB
SHOW_SERIAL.begin(USB_SERIAL_BAUD_RATE);
// 合成器初始化。由于这里使用硬件串口,软件串口被注释掉。
synth.begin(COM_SERIAL, MIDI_SERIAL_BAUD_RATE);
synth.setInstrument(0,CHANNEL_0,unit_synth_instrument_t::GrandPiano_1);
initButtons(BUTTON_A_PIN);
initButtons(BUTTON_B_PIN);
initButtons(BUTTON_C_PIN);
delay(1000);
}
void loop()
{
detectButtonEvents(BUTTON_A_PIN, btnA, shortPressFlag_A, longPressFlag_A, releaseFlag_A);
detectButtonEvents(BUTTON_B_PIN, btnB, shortPressFlag_B, longPressFlag_B, releaseFlag_B);
detectButtonEvents(BUTTON_C_PIN, btnC, shortPressFlag_C, longPressFlag_C, releaseFlag_C);
if(shortPressFlag_A){
shortPressFlag_A = false;
music_on_off_flag = !music_on_off_flag;
}
if(shortPressFlag_B){
shortPressFlag_B = false;
synth.increaseBpm();
}
if(shortPressFlag_C){
shortPressFlag_C = false;
synth.decreaseBpm();
}
play();
}
void play()
{
// 计算每拍(四分音符)之间的间隔(毫秒)
// 60,000毫秒等于1分钟
// 根据音符类型调整间隔
unsigned long interval = (BASIC_TIME / synth.getBpm()) / (noteType + 1);
if(music_on_off_flag)
{
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval)
{
previousMillis = currentMillis;
if (beatCount == 0)
synth.setNoteOn(CHANNEL_9, NOTE_D2, VELOCITY_DEFAULT);
else
synth.setNoteOn(CHANNEL_9, NOTE_C2, VELOCITY_DEFAULT);
// 增加拍子计数
beatCount++;
// 每四拍,表示一小节结束
if (beatCount >= beatsPerBar)
{
// 重置计数
beatCount = 0;
}
}
}
}
这段代码演示了一个简单的节拍音序器。按钮A切换节拍的开/关,按钮B增加节拍(BPM),按钮C减少节拍。音序器使用第9通道的MIDI音符播放基本的鼓点模式。您可以扩展这个示例来添加更复杂的节拍或额外的按钮功能。
程序注释
这个程序在基本MIDI音符演示的基础上,通过物理按钮引入了实时用户交互。代码使用三个按钮:
- 按钮A:开始/停止节拍序列
- 按钮B:增加节拍(BPM)
- 按钮C:减少节拍(BPM)
play()
函数根据当前BPM和音符类型计算节拍间隔,并在重复模式中触发鼓声(使用第9通道的MIDI音符D2和C2)。节拍计数每小节重置,创建一个简单的节拍器或鼓机效果。
这个示例演示了如何:
- 集成按钮输入进行实时控制
- 动态调整节拍和播放状态
- 将XIAO MIDI合成器用作可编程节拍生成器
您可以通过添加更多按钮、支持不同的鼓声或实现更高级的音序器逻辑来进一步增强这个演示。
演示3:带按钮控制的多轨和弦播放
这个演示展示了如何使用XIAO MIDI合成器播放多轨和弦,每个和弦由按钮触发。您可以使用两个物理按钮独立地开始/停止两种不同的和弦模式。
所需材料
XIAO MIDI合成器 |
---|
![]() |
参考程序
#include <Arduino.h>
#include "SAM2695Synth.h"
#include "Button.h"
#ifdef __AVR__
#include <SoftwareSerial.h>
SoftwareSerial SSerial(2, 3); // RX, TX
#define COM_SERIAL SSerial
#define SHOW_SERIAL Serial
SAM2695Synth<SoftwareSerial> synth = SAM2695Synth<SoftwareSerial>::getInstance();
#endif
#if defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_ARCH_RP2350) || defined(ARDUINO_XIAO_RA4M1)
#include <SoftwareSerial.h>
SoftwareSerial SSerial(D7, D6); // RX, TX
#define COM_SERIAL SSerial
#define SHOW_SERIAL Serial
SAM2695Synth<SoftwareSerial> synth = SAM2695Synth<SoftwareSerial>::getInstance();
#endif
#if defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32S3)
#define COM_SERIAL Serial0
#define SHOW_SERIAL Serial
SAM2695Synth<HardwareSerial> synth = SAM2695Synth<HardwareSerial>::getInstance();
#endif
#ifdef SEEED_XIAO_M0
#define COM_SERIAL Serial1
#define SHOW_SERIAL Serial
SAM2695Synth<Uart> synth = SAM2695Synth<Uart>::getInstance();
#elif defined(ARDUINO_SAMD_VARIANT_COMPLIANCE)
#define COM_SERIAL Serial1
#define SHOW_SERIAL SerialUSB
SAM2695Synth<Uart> synth = SAM2695Synth<Uart>::getInstance();
#endif
#if defined(NRF52840_XXAA)
#ifdef USE_TINYUSB
#include <Adafruit_TinyUSB.h>
#endif
#define COM_SERIAL Serial1
#define SHOW_SERIAL Serial
SAM2695Synth<Uart> synth = SAM2695Synth<Uart>::getInstance();
#endif
#if defined(CONFIG_IDF_TARGET_ESP32S3)
#define BUTTON_A_PIN 4
#define BUTTON_B_PIN 3
#define BUTTON_C_PIN 2
#define BUTTON_D_PIN 1
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
#define BUTTON_A_PIN 5
#define BUTTON_B_PIN 4
#define BUTTON_C_PIN 3
#define BUTTON_D_PIN 2
#elif defined(CONFIG_IDF_TARGET_ESP32C6)
#define BUTTON_A_PIN 21
#define BUTTON_B_PIN 2
#define BUTTON_C_PIN 1
#define BUTTON_D_PIN 0
#else //您需要参考相应的手册...
#define BUTTON_A_PIN 0 //您需要参考相应的手册...
#define BUTTON_B_PIN 1 //您需要参考相应的手册...
#define BUTTON_C_PIN 2 //您需要参考相应的手册...
#define BUTTON_D_PIN 3 //您需要参考相应的手册...
#endif
//定义按钮所需的结构
BtnState btnA = {HIGH, HIGH, 0, 0, false};
BtnState btnB = {HIGH, HIGH, 0, 0, false};
//多轨和弦数据定义示例
// 此结构控制在通道9上播放,只播放两个音符。
// 声音设置为默认声音,持续时间可根据需要调整。
const musicData chordTwoNotes =
{
CHANNEL_1, //在通道9上播放
{
{NOTE_C2, true}, //NOTE_C2 开启
{NOTE_FS2, true}, //NOTE_FS2 开启
{NOTE_E6, false}, //NOTE_E6 关闭
{NOTE_CS5, false}, //NOTE_CS5 关闭
},
VELOCITY_DEFAULT , //默认力度(64)
0, // 索引 此处未使用
BPM_DEFAULT*8, //间隔时间
};
const musicData chordFourNotes =
{
CHANNEL_2, // 在通道0上播放
{
{NOTE_F2, true}, // NOTE_F2 开启
{NOTE_B2, true}, // NOTE_B2 开启
{NOTE_E6, true}, // NOTE_E6 开启
{NOTE_CS5, true}, // NOTE_CS5 开启
},
VELOCITY_DEFAULT , // 默认力度(64)
0, // 索引 此处未使用
BPM_DEFAULT*4, // 间隔时间
};
bool chordTwoNotesFlag = false; // 开启或关闭 chord_twoNotes
bool chordFourNotesFlag = false; // 开启或关闭 chord_fourNotes
unsigned long preMillisTwoNotes = 0; // 记录在chord_twoNotes上最后一次发送MIDI信号的时间
unsigned long preMillisFourNotes= 0; // 记录在const musicData chord_fourNotes上最后一次发送MIDI信号的时间
void setup()
{
//串口初始化到usb
SHOW_SERIAL.begin(USB_SERIAL_BAUD_RATE);
// 合成器初始化。由于这里使用硬件串口,软件串口被注释掉。
synth.begin(COM_SERIAL, MIDI_SERIAL_BAUD_RATE);
synth.setInstrument(0,CHANNEL_0,unit_synth_instrument_t::GrandPiano_1);
// 初始化您正在使用的按钮。
initButtons(BUTTON_A_PIN);
initButtons(BUTTON_B_PIN);
delay(1000);
}
void loop()
{
detectButtonEvents(BUTTON_A_PIN, btnA, shortPressFlag_A, longPressFlag_A, releaseFlag_A);
detectButtonEvents(BUTTON_B_PIN, btnB, shortPressFlag_B, longPressFlag_B, releaseFlag_B);
if(shortPressFlag_A)
{
shortPressFlag_A = false;
chordTwoNotesFlag = !chordTwoNotesFlag;
}
if(shortPressFlag_B)
{
shortPressFlag_B = false;
chordFourNotesFlag = !chordFourNotesFlag;
}
play();
}
void play()
{
unsigned long currentMillis = millis();
if(chordTwoNotesFlag)
{
if (currentMillis - preMillisTwoNotes >= chordTwoNotes.delay)
{
preMillisTwoNotes = currentMillis;
synth.playChord(chordTwoNotes);
}
}
if(chordFourNotesFlag)
{
if (currentMillis - preMillisFourNotes >= chordFourNotes.delay)
{
preMillisFourNotes = currentMillis;
synth.playChord(chordFourNotes);
}
}
}
这段代码演示了如何使用两个按钮触发两种不同的多音符和弦。按钮A切换两音符和弦的播放,按钮B切换四音符和弦的播放。每个和弦在不同的MIDI通道上播放,并具有自己的时序。
程序注释
这个程序演示了带有实时按钮控制的多轨和弦播放。代码定义了两个和弦模式(chordTwoNotes
和 chordFourNotes
),每个都有自己的MIDI通道、音符和时序。使用两个按钮:
- 按钮A:开始/停止两音符和弦序列
- 按钮B:开始/停止四音符和弦序列
play()
函数检查每个和弦的时序,如果相应的标志被启用,则触发播放。这允许不同和弦模式的独立、重叠播放,模拟一个简单的多轨音序器。
这个示例演示了如何:
- 定义和播放自定义和弦结构
- 使用多个按钮进行独立的音乐控制
- 使用XIAO MIDI合成器构建更复杂、分层的音乐表演
您可以通过添加更多和弦模式、支持额外按钮或同步播放来扩展此演示,以获得更高级的音乐效果。
演示4:播放完整的旋律序列
此演示展示了如何使用XIAO MIDI合成器通过将音符、持续时间和延迟数据存储在数组中来播放完整的旋律序列(如歌曲)。该示例演示了如何使用内置的小提琴乐器播放预编程的旋律。
所需材料
XIAO MIDI合成器 |
---|
![]() |
参考程序
#include <SAM2695Synth.h>
#ifdef __AVR__
#include <SoftwareSerial.h>
SoftwareSerial SSerial(2, 3); // RX, TX
#define COM_SERIAL SSerial
#define SHOW_SERIAL Serial
SAM2695Synth<SoftwareSerial> synth = SAM2695Synth<SoftwareSerial>::getInstance();
#endif
#if defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_ARCH_RP2350) || defined(ARDUINO_XIAO_RA4M1)
#include <SoftwareSerial.h>
SoftwareSerial SSerial(D7, D6); // RX, TX
#define COM_SERIAL SSerial
#define SHOW_SERIAL Serial
SAM2695Synth<SoftwareSerial> synth = SAM2695Synth<SoftwareSerial>::getInstance();
#endif
#if defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32S3)
#define COM_SERIAL Serial0
#define SHOW_SERIAL Serial
SAM2695Synth<HardwareSerial> synth = SAM2695Synth<HardwareSerial>::getInstance();
#endif
#ifdef SEEED_XIAO_M0
#define COM_SERIAL Serial1
#define SHOW_SERIAL Serial
SAM2695Synth<Uart> synth = SAM2695Synth<Uart>::getInstance();
#elif defined(ARDUINO_SAMD_VARIANT_COMPLIANCE)
#define COM_SERIAL Serial1
#define SHOW_SERIAL SerialUSB
SAM2695Synth<Uart> synth = SAM2695Synth<Uart>::getInstance();
#endif
#if defined(NRF52840_XXAA)
#ifdef USE_TINYUSB
#include <Adafruit_TinyUSB.h>
#endif
#define COM_SERIAL Serial1
#define SHOW_SERIAL Serial
SAM2695Synth<Uart> synth = SAM2695Synth<Uart>::getInstance();
#endif
// 可以移动到头文件中,例如 notes.h
#define ARRAY_LEN(array) (sizeof(array) / sizeof(array[0]))
const int midi1[453][3] = {
{NOTE_D4, 300, 0},
{NOTE_C4, 300, 0},
{NOTE_F4, 600, 0},
{NOTE_F4, 600, 0},
{NOTE_G4, 600, 0},
{NOTE_G4, 600, 0},
{NOTE_A4, 600, 0},
{NOTE_F4, 600, 0},
{NOTE_C4, 600, 0},
{NOTE_D4, 300, 0},
{NOTE_C4, 300, 0},
{NOTE_F4, 600, 0},
{NOTE_F4, 600, 0},
{NOTE_G4, 600, 0},
{NOTE_G4, 600, 0},
{NOTE_A4, 600, 0},
{NOTE_F4, 1500, 0},
{NOTE_D4, 300, 0},
{NOTE_C4, 300, 0},
{NOTE_F4, 600, 0},
{NOTE_F4, 600, 0},
{NOTE_G4, 600, 0},
{NOTE_G4, 600, 0},
{NOTE_A4, 600, 0},
{NOTE_F4, 600, 0},
{NOTE_C4, 600, 0},
{NOTE_D4, 300, 0},
{NOTE_C4, 300, 0},
{NOTE_F4, 600, 0},
{NOTE_F4, 600, 0},
{NOTE_G4, 600, 0},
{NOTE_F4, 2000, 500},
{NOTE_F4, 300, 0},
{NOTE_E4, 300, 0},
{NOTE_C4, 300, 0},
{NOTE_A3, 300, 0},
{NOTE_D4, 300, 0},
{NOTE_C4, 300, 0},
{NOTE_F4, 600, 0},
{NOTE_F4, 600, 0},
{NOTE_G4, 600, 0},
{NOTE_G4, 600, 0},
{NOTE_A4, 600, 0},
{NOTE_F4, 600, 0},
{NOTE_E4, 600, 0},
{NOTE_D4, 1200, 0},
{NOTE_E4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_E4, 600, 0},
{NOTE_F4, 2000, 500},
{NOTE_C4, 1200, 0},
{NOTE_E4, 400, 0},
{NOTE_F4, 2000, 500},
{NOTE_C4, 1500, 0},
{NOTE_F4, 600, 0},
{NOTE_G4, 1600, 0},
{NOTE_G4, 300, 0},
{NOTE_A4, 300, 0},
{NOTE_AS4, 600, 0},
{NOTE_A4, 1200, 0},
{NOTE_G4, 500, 0},
{NOTE_F4, 300, 0},
{NOTE_G4, 1000, 500},
{NOTE_F4, 400, 0},
{NOTE_E4, 300, 0},
{NOTE_F4, 3000, 0},
{NOTE_C4, 1200, 0},
{NOTE_E4, 400, 0},
{NOTE_F4, 2000, 500},
{NOTE_C4, 1500, 0},
{NOTE_F4, 600, 0},
{NOTE_G4, 1600, 0},
{NOTE_G4, 300, 0},
{NOTE_A4, 300, 0},
{NOTE_AS4, 600, 0},
{NOTE_A4, 1200, 0},
{NOTE_G4, 500, 0},
{NOTE_F4, 800, 0},
{NOTE_G4, 800, 0},
{NOTE_AS4, 400, 0},
{NOTE_A4, 300, 0},
{NOTE_F4, 3000, 0},
{NOTE_AS4, 1200, 0},
{NOTE_F4, 600, 0},
{NOTE_G4, 300, 0},
{NOTE_A4, 2000, 500},
{NOTE_AS4, 1200, 0},
{NOTE_F4, 600, 0},
{NOTE_G4, 300, 0},
{NOTE_A4, 2000, 500},
{NOTE_G4, 300, 0},
{NOTE_F4, 300, 0},
{NOTE_D4, 600, 0},
{NOTE_E4, 600, 0},
{NOTE_F4, 600, 0},
{NOTE_F4, 600, 0},
{NOTE_C4, 600, 0},
{NOTE_C4, 300, 0},
{NOTE_F4, 1100, 500},
{NOTE_F4, 300, 0},
{NOTE_AS4, 300, 0},
{NOTE_A4, 600, 0},
{NOTE_G4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_G4, 1800, 500},
{NOTE_C4, 300, 0},
{NOTE_E4, 300, 0},
{NOTE_F4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_F4, 1000, 500},
{NOTE_G4, 400, 0},
{NOTE_G4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_D4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_A4, 300, 0},
{NOTE_B4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_A4, 700, 0},
{NOTE_F4, 300, 0},
{NOTE_C4, 300, 0},
{NOTE_F4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_G4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_D4, 600, 0},
{NOTE_G4, 300, 0},
{NOTE_F4, 300, 0},
{NOTE_B4, 300, 0},
{NOTE_A4, 1500, 500},
{NOTE_C4, 300, 0},
{NOTE_F4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_G4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_D4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_A4, 300, 0},
{NOTE_B4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_A4, 600, 0},
{NOTE_F4, 600, 0},
{NOTE_E4, 600, 0},
{NOTE_D4, 1200, 0},
{NOTE_E4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_E4, 600, 0},
{NOTE_F4, 1200, 500},
{NOTE_G4, 300, 0},
{NOTE_G4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_D4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_A4, 300, 0},
{NOTE_B4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_A4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_C4, 300, 0},
{NOTE_F4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_G4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_D4, 600, 0},
{NOTE_G4, 300, 0},
{NOTE_F4, 300, 0},
{NOTE_B4, 300, 57},
{NOTE_A4, 1000, 0},
{NOTE_G3, 300, 0},
{NOTE_C4, 300, 0},
{NOTE_F4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_G4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_D4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_A4, 300, 0},
{NOTE_B4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_A4, 600, 0},
{NOTE_F4, 600, 0},
{NOTE_E4, 600, 0},
{NOTE_D4, 1000, 0},
{NOTE_E4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_E4, 600, 0},
{NOTE_F4, 1000, 0},
{NOTE_G3, 300, 0},
{NOTE_C4, 300, 0},
{NOTE_F4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_G4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_D4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_A4, 300, 0},
{NOTE_B4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_A4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_C4, 300, 0},
{NOTE_F4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_G4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_D4, 600, 0},
{NOTE_G4, 300, 0},
{NOTE_F4, 300, 0},
{NOTE_B4, 300, 26},
{NOTE_A4, 1200, 0},
{NOTE_G3, 300, 0},
{NOTE_C4, 300, 0},
{NOTE_F4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_G4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_D4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_A4, 300, 0},
{NOTE_B4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_A4, 600, 0},
{NOTE_F4, 600, 0},
{NOTE_E4, 600, 0},
{NOTE_D4, 1000, 0},
{NOTE_E4, 1000, 0},
{NOTE_F4, 300, 0},
{NOTE_E4, 600, 0},
{NOTE_C4, 800, 0},
{NOTE_C4, 300, 0},
{NOTE_C4, 300, 0},
{NOTE_DS4, 300, 0},
{NOTE_D4, 300, 0},
{NOTE_C4, 600, 0},
{NOTE_AS3, 600, 0},
{NOTE_C4, 2000, 0},
{NOTE_C4, 800, 0},
{NOTE_C4, 800, 0},
{NOTE_DS4, 300, 0},
{NOTE_F4, 300, 0},
{NOTE_G4, 600, 0},
{NOTE_F4, 600, 0},
{NOTE_GS4, 600, 0},
{NOTE_G4, 1200, 0},
{NOTE_C4, 800, 0},
{NOTE_C4, 800, 0},
{NOTE_DS4, 300, 0},
{NOTE_D4, 300, 0},
{NOTE_C4, 300, 0},
{NOTE_C4, 300, 0},
{NOTE_AS3, 600, 0},
{NOTE_C4, 1200, 0},
{NOTE_C4, 300, 0},
{NOTE_F4, 300, 0},
{NOTE_G4, 600, 0},
{NOTE_F4, 600, 0},
{NOTE_F4, 600, 0},
{NOTE_GS4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_F4, 300, 0},
{NOTE_F4, 300, 0},
{NOTE_DS4, 600, 1200},
{NOTE_G3, 300, 0},
{NOTE_C4, 300, 0},
{NOTE_F4, 500, 100},
{NOTE_G4, 500, 0},
{NOTE_F4, 300, 0},
{NOTE_D4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_A4, 300, 0},
{NOTE_B4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_A4, 500, 0},
{NOTE_F4, 300, 0},
{NOTE_C4, 300, 0},
{NOTE_F4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_G4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_D4, 500, 100},
{NOTE_G4, 300, 0},
{NOTE_F4, 300, 0},
{NOTE_B4, 300, 57},
{NOTE_A4, 1000, 0},
{NOTE_G3, 300, 0},
{NOTE_C4, 300, 0},
{NOTE_F4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_G4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_D4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_A4, 300, 0},
{NOTE_B4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_A4, 600, 0},
{NOTE_F4, 600, 0},
{NOTE_E4, 600, 0},
{NOTE_D4, 1000, 0},
{NOTE_E4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_E4, 600, 0},
{NOTE_F4, 1500, 0},
{NOTE_G3, 300, 0},
{NOTE_C4, 300, 0},
{NOTE_F4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_G4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_D4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_A4, 300, 0},
{NOTE_B4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_A4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_C4, 300, 0},
{NOTE_F4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_G4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_D4, 600, 0},
{NOTE_G4, 300, 0},
{NOTE_F4, 300, 10},
{NOTE_B4, 300, 10},
{NOTE_A4, 1500, 0},
{NOTE_G3, 300, 0},
{NOTE_C4, 300, 0},
{NOTE_F4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_G4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_D4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_A4, 300, 0},
{NOTE_B4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_A4, 600, 0},
{NOTE_F4, 600, 0},
{NOTE_E4, 600, 0},
{NOTE_D4, 1200, 0},
{NOTE_E4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_E4, 600, 0},
{NOTE_F4, 1500, 0},
{NOTE_G3, 300, 0},
{NOTE_C4, 300, 0},
{NOTE_F4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_G4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_D4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_A4, 300, 0},
{NOTE_B4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_A4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_C4, 300, 0},
{NOTE_F4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_G4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_D4, 600, 0},
{NOTE_G4, 300, 0},
{NOTE_F4, 300, 0},
{NOTE_B4, 300, 57},
{NOTE_A4, 1200, 0},
{NOTE_G3, 300, 0},
{NOTE_C4, 300, 0},
{NOTE_F4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_G4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_D4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_A4, 300, 0},
{NOTE_B4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_A4, 600, 0},
{NOTE_F4, 600, 0},
{NOTE_E4, 600, 0},
{NOTE_D4, 1200, 0},
{NOTE_E4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_E4, 600, 0},
{NOTE_F4, 2000, 0},
{NOTE_G4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_D4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_A4, 300, 0},
{NOTE_B4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_A4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_C4, 300, 0},
{NOTE_F4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_G4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_D4, 600, 0},
{NOTE_G4, 300, 0},
{NOTE_F4, 300, 0},
{NOTE_B4, 300, 0},
{NOTE_A4, 1200, 0},
{NOTE_G3, 300, 0},
{NOTE_C4, 300, 0},
{NOTE_F4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_G4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_D4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_A4, 300, 0},
{NOTE_B4, 300, 0},
{NOTE_G4, 300, 0},
{NOTE_A4, 600, 0},
{NOTE_F4, 600, 0},
{NOTE_E4, 600, 0},
{NOTE_D4, 1200, 0},
{NOTE_E4, 600, 0},
{NOTE_F4, 300, 0},
{NOTE_E4, 600, 0},
{NOTE_F4, 1500, 0},
};
void playMidi(const int notes[][3], size_t len) {
for (int i = 0; i < len; i++) {
// 播放音符
synth.setNoteOn(CHANNEL_0, notes[i][0], 110);
delay(notes[i][1]);
// 停止音符
synth.setNoteOff(CHANNEL_0, notes[i][0]);
delay(notes[i][2]);
}
}
void setup() {
// 初始化USB串口
SHOW_SERIAL.begin(USB_SERIAL_BAUD_RATE);
// 初始化MIDI合成器
synth.begin(COM_SERIAL, MIDI_SERIAL_BAUD_RATE);
// 将乐器声音设置为小提琴
synth.setInstrument(0, CHANNEL_0, unit_synth_instrument_t::Violin);
delay(1000);
// 播放MIDI音符
playMidi(midi1, ARRAY_LEN(midi1));
}
void loop() {
// 主循环为空,因为音乐只播放一次
}
这段代码演示了如何通过遍历一个包含音符、持续时间和延迟的大型数组来播放完整的旋律序列。旋律将在上电或复位后播放一次。您可以修改数组来播放不同的歌曲或添加更复杂的编排。
程序注释
这个程序演示了如何:
- 将完整的旋律存储为音符、持续时间和延迟值的数组
- 使用辅助函数(
playMidi
)遍历旋律并按顺序播放每个音符 - 选择特定的乐器(小提琴)进行播放
- 在启动时自动播放歌曲
您可以通过以下方式扩展此演示:
- 更改乐器或音符数据以播放不同的歌曲
- 添加按钮控制来启动/停止或重复播放
- 实现多轨或复音播放以获得更高级的音乐效果
对于那些希望将MIDI文件转换为Arduino代码的用户,您可以尝试使用Midi2ArduinoTone等工具。但是,请注意,此类工具生成的音符值可能需要修改才能与XIAO MIDI合成器配合使用,因为它们使用不同的音符映射。这些工具仍然可以作为将MIDI文件转换为您可以为此合成器调整的格式的起点。
资源
- [PDF] XIAO MIDI合成器原理图
- [ZIP] XIAO MIDI合成器原理图和PCB
- [GitHub] Seeed_Arduino_MIDIMaster库
故障排除
Q1:如何将MIDI文件转换为XIAO MIDI合成器的Arduino代码?
要将MIDI文件转换为可用于XIAO MIDI合成器的Arduino代码,您可以按照以下步骤操作:
-
获取MIDI文件
您可以从以下网站下载免费的MIDI文件:
-
将MIDI文件转换为文本
使用开源工具midicomp将您的MIDI文件转换为人类可读的文本格式。
-
将MIDI文本转换为可播放的代码
使用
midicomp
将MIDI文件转换为文本格式后,您需要将此文本处理为XIAO MIDI合成器可以播放的C/C++数组。Seeed提供Python脚本来自动化此转换:- 快节奏,单轨(每个音符开始作为间隔):
change_fast.py - 慢节奏,单轨(每个音符结束作为间隔):
change_shuqing.py - 多轨处理:
change_mutich.py
使用示例:
- 将您的MIDI文本文件(例如
1.txt
)放在与脚本相同的目录中。 - 在终端中运行脚本:
python change_fast.py
这将生成一个包含音符数组的头文件(例如
track_from_miditext.h
)。- 将生成的数组复制到您的Arduino草图中,替换旋律数据。
tip- 脚本会自动将MIDI音符编号映射到XIAO MIDI合成器的音符定义。
- 您可以调整脚本或生成的数组以进行自定义编排或适应内存限制。
- 集成生成的代码
- 打开生成的
.h
文件并复制musicData
数组。 - 将其粘贴到您的Arduino项目中,替换现有的旋律数组。
- 如果需要,更新您的播放逻辑以匹配新的数组结构。
- 快节奏,单轨(每个音符开始作为间隔):
技术支持和产品讨论
感谢您选择我们的产品!我们在这里为您提供不同的支持,以确保您对我们产品的体验尽可能顺畅。我们提供多种沟通渠道,以满足不同的偏好和需求。