Skip to main content

XIAO MIDI 合成器入门指南


介绍

XIAO MIDI 合成器是一款紧凑的一体化音乐创作设备,由 XIAO ESP32-C3 和专业级 SAM2695 MIDI 芯片驱动。尽管体积小巧,但它能提供丰富、富有表现力的声音,支持多达 64 声部复音,并提供数百种乐器音色。无论您是实验声音的开发者还是创作新曲的音乐家,这款合成器都旨在让音乐创作变得流畅而富有灵感。

特性

  • 零门槛创作,非常适合音乐初学者
  • 一体化 MIDI + 节拍器 -- 更智能、更流畅的工作流程
  • 为高级演奏者提供专业级性能
  • 超便携且完全可定制

硬件概述

XIAO MIDI 合成器集成了以下组件:

核心模块
MCUXIAO 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 秒):切换到下一个模式(循环切换:声音测试 → 节拍器 → 多轨播放 → ...)
tip

您可以在无需任何额外设置的情况下体验所有功能。如需高级自定义或重新刷写固件,请参阅 StateMachine 示例代码

引导加载器模式

在某些情况下,您可能会发现 XIAO MIDI 合成器无法通过串口编程(例如,设备无法识别或上传失败)。在这些情况下,您需要进入 引导加载器(BOOT)模式 来重新刷写固件。

为什么要进入 BOOT 模式?

  • 当设备无法通过 USB/串口正常编程时恢复设备
  • 在上传失败或固件损坏后重新刷写固件

如何进入 BOOT 模式

首先,打开迷你合成器的后盖以访问 XIAO ESP32-C3 模块上的 BOOT 和 RESET 按钮。有两种方法:

  • 方法 1:按钮序列

    1. 在设备通电的情况下,按住 BOOT 按钮(不要松开)。
    2. 在按住 BOOT 的同时,按一次 RESET 按钮。
    3. 松开 BOOT 按钮。
  • 方法 2:电源循环

    1. 断开电源(拔掉 USB-C 线缆)。
    2. 按住 BOOT 按钮。
    3. 在按住 BOOT 的同时,重新连接电源(插入 USB-C 线缆)。
    4. 松开 BOOT 按钮。

进入 BOOT 模式后,设备将准备好通过 Arduino IDE 或其他工具进行固件上传。

复位

要复位设备,只需按下 XIAO ESP32-C3 模块上的 RESET 按钮。设备将重新启动。

Arduino 库概述

tip

如果这是您第一次使用 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 合成器:

  1. 在适当的波特率下建立串行通信
  2. 初始化 SAM2695 合成器引擎
  3. 在通道 0 上配置默认乐器(大钢琴)
  synth.setInstrument(0,CHANNEL_0,unit_synth_instrument_t::GrandPiano_1);
  1. 允许通过时间延迟进行稳定

演示循环通过以下方式展示了基本的声音生成能力:

void loop()
{
synth.setNoteOn(CHANNEL_0,NOTE_E4,VELOCITY_DEFAULT);
delay(1000);
synth.setNoteOff(CHANNEL_0,NOTE_E4);
delay(1000);
}
  1. 以标准力度触发音符 E4
  2. 保持音符一秒钟
  3. 释放音符
  4. 在下一个循环前暂停

这个实现代表了使用 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通道上播放,并具有自己的时序。

程序注释

这个程序演示了带有实时按钮控制的多轨和弦播放。代码定义了两个和弦模式(chordTwoNoteschordFourNotes),每个都有自己的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)遍历旋律并按顺序播放每个音符
  • 选择特定的乐器(小提琴)进行播放
  • 在启动时自动播放歌曲

您可以通过以下方式扩展此演示:

  • 更改乐器或音符数据以播放不同的歌曲
  • 添加按钮控制来启动/停止或重复播放
  • 实现多轨或复音播放以获得更高级的音乐效果
tip

对于那些希望将MIDI文件转换为Arduino代码的用户,您可以尝试使用Midi2ArduinoTone等工具。但是,请注意,此类工具生成的音符值可能需要修改才能与XIAO MIDI合成器配合使用,因为它们使用不同的音符映射。这些工具仍然可以作为将MIDI文件转换为您可以为此合成器调整的格式的起点。

资源

故障排除

Q1:如何将MIDI文件转换为XIAO MIDI合成器的Arduino代码?

要将MIDI文件转换为可用于XIAO MIDI合成器的Arduino代码,您可以按照以下步骤操作:

  1. 获取MIDI文件

    您可以从以下网站下载免费的MIDI文件:

  2. 将MIDI文件转换为文本

    使用开源工具midicomp将您的MIDI文件转换为人类可读的文本格式。

  3. 将MIDI文本转换为可播放的代码

    使用midicomp将MIDI文件转换为文本格式后,您需要将此文本处理为XIAO MIDI合成器可以播放的C/C++数组。Seeed提供Python脚本来自动化此转换:

    使用示例:

    1. 将您的MIDI文本文件(例如1.txt)放在与脚本相同的目录中。
    2. 在终端中运行脚本:
    python change_fast.py

    这将生成一个包含音符数组的头文件(例如track_from_miditext.h)。

    1. 将生成的数组复制到您的Arduino草图中,替换旋律数据。
    tip
    • 脚本会自动将MIDI音符编号映射到XIAO MIDI合成器的音符定义。
    • 您可以调整脚本或生成的数组以进行自定义编排或适应内存限制。
    1. 集成生成的代码
    • 打开生成的.h文件并复制musicData数组。
    • 将其粘贴到您的Arduino项目中,替换现有的旋律数组。
    • 如果需要,更新您的播放逻辑以匹配新的数组结构。

技术支持和产品讨论

感谢您选择我们的产品!我们在这里为您提供不同的支持,以确保您对我们产品的体验尽可能顺畅。我们提供多种沟通渠道,以满足不同的偏好和需求。

Loading Comments...