Motor de Teoria Musical para Sistemas Embarcados
- 🪇 Gingo[duino]
Gingoduino é um motor de teoria musical para sistemas embarcados. Ele cobre o domínio musical (notas, intervalos, acordes, escalas, campos harmônicos, árvores harmônicas, análise de progressão, engine de braço), um monitor harmônico em tempo real, um interpretador de stream UMP ao vivo (GingoFlow), e adaptadores de saída sem estado que traduzem estruturas musicais em formatos serializados (bytes MIDI 1.0 e UMP Flex Data MIDI 2.0).
Não cobre o fio. Parsing de byte stream, dispatch de UMP recebido, e descoberta MIDI-CI com Property Exchange são responsabilidades de protocolo delegadas a bibliotecas dedicadas: midi2cpp para UMP, Arduino MIDI Library ou parser próprio para byte stream MIDI 1.0, biblioteca midi2 C99 para responders MIDI-CI.
Arquitetura zero-heap, tabelas em PROGMEM, compatível com C++11.
0.4.0 é uma release com breaking changes. Veja CHANGELOG.md para o guia de migração se você está atualizando da 0.3.x.
Gingoduino é uma das três bibliotecas companheiras, com papéis distintos e complementares.
| Biblioteca | Papel | Linguagem | Fala |
|---|---|---|---|
midi2 |
Infraestrutura: wire UMP, parsing, dispatch, MIDI-CI | C99 | MIDI 2.0 |
midi2cpp |
Plataforma: USB MIDI 2.0 device, host, bridge | C++17 | MIDI 2.0 |
| gingoduino | Motor de teoria musical. Notas, intervalos, acordes, escalas, campos, monitor harmônico, adaptadores de saída | C++11 | MIDI 1.0 e 2.0 |
┌─────────────────────────────────────────────┐
│ Aplicação │
└─────────────────────────────────────────────┘
│ │
│ usa para teoria │ usa para transporte
▼ ▼
┌────────────────────┐ ┌──────────────────────┐
│ gingoduino │ │ midi2cpp │
│ Teoria musical │ │ USB MIDI 2.0 │
│ Notas · Acordes │ │ Device · Host │
│ Escalas · Campos │ │ Bridge · CI │
│ MIDI 1.0 + 2.0 │ └──────────┬───────────┘
│ adaptadores de │ │ usa
│ saída │ ▼
└────────────────────┘ ┌──────────────────────┐
│ midi2 │
│ Wire UMP · MIDI-CI │
│ Parsing · Dispatch │
└──────────────────────┘
Gingoduino é a única das três com um pé em MIDI 1.0. GingoMIDI1 serializa estruturas musicais em bytes MIDI 1.0 puros (DIN, USB MIDI 1.0, portas virtuais); GingoMIDI2 serializa as mesmas estruturas em UMP Flex Data MIDI 2.0, prontas para alimentar midi2cpp ou qualquer transporte UMP.
┌─────────────────┐ evento musical ┌──────────────────┐
│ Transporte │ ──────────────────▶ │ GingoMonitor │
│ (midi2cpp, │ noteOn / noteOff / │ acorde, campo, │
│ Arduino MIDI, │ sustainOn / Off │ contexto/nota │
│ ESP32_Host_ │ └────────┬─────────┘
│ MIDI, ...) │ │
└─────────────────┘ │
▲ ▼
│ ┌─────────────────────────────────┐
│ │ Teoria + Análise │
│ │ Note · Interval · Chord · │
│ │ Scale · Field · Tree · │
│ │ Progression · Comparison · │
│ │ Fretboard · Event · Sequence │
│ └─────────────────┬────────────────┘
│ │
│ ▼
│ ┌─────────────────────────────────┐
│ │ Adaptadores de saída │
│ bytes / │ GingoMIDI1::fromEvent │
└────────────┤ GingoMIDI1::fromSequence │
UMP │ GingoMIDI2::chordName │
│ GingoMIDI2::keySignature │
│ GingoMIDI2::perNoteController │
└─────────────────────────────────┘
O transporte vive fora da biblioteca. Ele decodifica bytes ou pacotes UMP e alimenta o Monitor pela API de eventos musicais. Tudo o que Gingoduino calcula pode ser convertido de volta em bytes ou UMP através dos adaptadores de saída em namespaces dedicados.
Teoria:
GingoNote: nota cromática, frequência, número MIDI, transposiçãoGingoInterval: intervalos rotulados, consonância, nome completoGingoChord: 42 fórmulas de acorde, identificação, transposiçãoGingoScale: tipos de escala, modos, armadura, brilho,formalNotes(spelling diatônico)GingoField: campo harmônico, funções T/S/D, role,relative/parallel,branchOf,appliedChords(dominantes secundários),compare(GingoFieldComparisoncom 21 dimensões)GingoTree: grafo harmônico (tradições clássica e jazz)GingoProgression: identify, deduce, predictGingoChordComparison: 17 dimensões, Neo-Riemann, vetores de ForteGingoFretboard: guitar, violão, cavaquinho, mandolim/bandolim, ukulele; afinações alternativas
Tempo e eventos:
GingoDuration(racional,operator+,operator*),GingoTempo,GingoTimeSigGingoEvent,GingoSequence
Análise em tempo real:
GingoMonitor: detecção de acorde, dedução de campo, contexto por nota,onPredictioncom history rotativo de branchesGingoFlow: interpretador de stream UMP ao vivo; captura não-bloqueante, um stream de eventos lógicos com pareamento de notas, agrupamento de acordes e durações, uma face opcional de forward de UMP bruto, e uma regra de agrupamento de acordes calibrável
Adaptadores de saída (sem estado):
GingoMIDI1::fromEvent,GingoMIDI1::fromSequence: bytes MIDI 1.0GingoMIDI2::chordName,keySignature,perNoteController: UMP Flex Data MIDI 2.0
Portabilidade:
Gingoduino é C++11 portável, zero heap e tabelas de lookup em PROGMEM. Não depende de API de placa nenhuma e deve compilar em qualquer toolchain que entregue C++11 padrão. As famílias declaradas em library.properties (esp32, esp8266, samd, nrf52, rp2040, mbed_rp2040, renesas_uno, renesas_portenta, stm32, teensy) são os alvos que exercitamos; outros Cortex-M / RISC-V / desktop também compilam sem problema. Veja Validação em hardware para as placas onde rodamos a prova on-device.
Algumas notas práticas:
<functional>só é incluído quando o toolchain entrega. AVR (avr-libc) não tem, então os overloadsstd::functioncom lambda capture doGingoMonitorficam indisponíveis em AVR; os overloads com ponteiro de função continuam funcionando.- AVR (Uno, Nano, Leonardo etc., 32 KB Flash / 2-2.5 KB SRAM) não comporta a biblioteca completa junto com o pipeline Field/Monitor/Progression. Escopo prático em AVR:
GingoNote,GingoInterval,GingoChord,GingoScale(vejaexamples/Foundation_AVR).
- Sistema cromático de 12 notas com equivalentes enarmônicos
- 42 fórmulas de acordes com lookup reverso (identify)
- Mais de 40 tipos de escalas e modos com armadura, brilho, escala relativa e paralela
- Análise de campo harmônico com funções T/S/D e roles, mais dedução a partir de notas e acordes
- Árvore harmônica (grafo dirigido, maior e menor, tradições clássica e jazz)
- Análise de progressão: identify, deduce (ranqueado), predict (próximo branch)
- Engine de braço: guitar, violão, cavaquinho, mandolim/bandolim, ukulele; afinações alternativas (Drop D, Open G, DADGAD); acordes comuns e digitações em primeira posição
- Eventos musicais (nota, acorde, pausa) e sequências com tempo e fórmula de compasso
- Monitor harmônico em tempo real com detecção de acordes e campos e contexto por nota
- Interpretador de stream UMP ao vivo (
GingoFlow): captura não-bloqueante, eventos lógicos com pareamento de notas, agrupamento de acordes e durações, e uma face opcional de forward de UMP bruto - Adaptadores de saída MIDI 1.0:
GingoMIDI1::fromEvent,GingoMIDI1::fromSequence - Adaptadores de saída MIDI 2.0 UMP Flex Data:
GingoMIDI2::chordName,keySignature,perNoteController - Comparação de acordes em 17 dimensões, incluindo transformações Neo-Riemannianas e vetores Forte
- Arrays de tamanho fixo, sem alocação dinâmica, suporte a PROGMEM
- Compatível com Arduino IDE, PlatformIO, ESP-IDF e CMake (nativo)
- 585 testes nativos passando com
-Wall -Wextra -Werror
| Spec | Documento | Superfície no gingoduino |
|---|---|---|
| MIDI 1.0 | MIDI 1.0 Detailed Specification | Saída: bytes NoteOn/NoteOff de GingoEvent e GingoSequence via GingoMIDI1::fromEvent, GingoMIDI1::fromSequence |
| MIDI 2.0 UMP | M2-104-UM v1.1.2 | Saída: Flex Data (Set Chord Name, Set Key Signature, Set Per-Note Controller) via GingoMIDI2 |
| MIDI 2.0 Bit Scaling | M2-115-U v1.0.2 | Interno: scaling de valores entre campos 7/14/16/32 bits com round-trip seguro |
Parsing de entrada (byte streams MIDI 1.0, dispatch de UMP recebido, MIDI-CI) é responsabilidade da biblioteca de transporte.
Arduino IDE Library Manager:
- Sketch > Include Library > Manage Libraries > buscar
Gingoduino> Install
PlatformIO:
; platformio.ini
lib_deps = sauloverissimo/GingoduinoESP-IDF Component:
idf.py add-dependency "sauloverissimo/gingoduino"CMake / nativo:
include(FetchContent)
FetchContent_Declare(gingoduino
GIT_REPOSITORY https://github.com/sauloverissimo/gingoduino.git
GIT_TAG v0.6.0)
FetchContent_MakeAvailable(gingoduino)
target_link_libraries(my_target PRIVATE gingoduino::gingoduino)Ou instale (cmake --install) e use find_package(gingoduino CONFIG).
Manual:
- Baixe e copie para sua pasta de bibliotecas Arduino (
~/Arduino/libraries/).
#include <Gingoduino.h>
using namespace gingoduino;
void setup() {
Serial.begin(9600);
GingoNote nota("C");
Serial.println(nota.name()); // "C"
Serial.println(nota.midiNumber(4)); // 60
Serial.println(nota.frequency(4), 1); // 261.6
GingoChord acorde("Dm7");
GingoNote notas[7];
acorde.notes(notas, 7); // D, F, A, C
GingoScale escala("C", SCALE_MAJOR);
GingoField campo("C", SCALE_MAJOR);
GingoChord triades[7];
campo.chords(triades, 7); // CM, Dm, Em, FM, GM, Am, Bdim
}
void loop() {}GingoNote note("C#");
note.name(); // "C#"
note.natural(); // "C#" (forma canônica em sustenidos: Bb -> A#, Eb -> D#)
note.semitone(); // 1 (0-11)
note.frequency(4); // Hz (float)
note.midiNumber(4); // 0-127
note.transpose(7); // GingoNote
note.distance(other); // distância mínima no ciclo de quintas (0-6)
note.isEnharmonic(other); // bool
GingoNote::fromMIDI(60); // "C"
GingoNote::octaveFromMIDI(60); // 4GingoInterval iv("5J"); // ou GingoInterval(7) ou GingoInterval(noteA, noteB)
char buf[32];
iv.label(buf, sizeof(buf)); // "5J"
iv.semitones(); // 7
iv.degree(); // 5
iv.consonance(buf, sizeof(buf)); // "perfect", "imperfect" ou "dissonant"
iv.fullName(buf, sizeof(buf)); // "Perfect Fifth"
iv.fullNamePt(buf, sizeof(buf)); // "Quinta Justa"
iv.simple(); // reduz composto para simples
iv.invert(); // complemento dentro da oitavaGingoChord chord("Dm7");
chord.name(); // "Dm7"
chord.root(); // GingoNote("D")
chord.type(); // "m7"
chord.size(); // 4
GingoNote notes[7];
chord.notes(notes, 7);
GingoNote arr[3] = {GingoNote("C"), GingoNote("E"), GingoNote("G")};
char name[16];
GingoChord::identify(arr, 3, name, 16); // "CM"GingoScale scale("C", SCALE_MAJOR); // ou GingoScale("C", "dorian")
char buf[22];
scale.modeName(buf, sizeof(buf)); // "Ionian"
scale.quality(); // "major" ou "minor"
scale.signature(); // 0 (sustenidos > 0, bemóis < 0)
scale.brightness(); // 1-7 (maior = mais brilhante)
GingoNote notes[12];
scale.notes(notes, 12);
scale.mode(2); // Dorian
scale.pentatonic();
scale.relative(); // relativa maior/menor
scale.parallel(); // paralela maior/menorGingoField field("C", SCALE_MAJOR);
GingoChord triads[7]; field.chords(triads, 7); // CM, Dm, Em, FM, GM, Am, Bdim
GingoChord sevs[7]; field.sevenths(sevs, 7); // C7M, Dm7, Em7, F7M, G7, Am7, Bm7(b5)
field.function(5); // FUNC_DOMINANT
field.functionOf(GingoChord("GM")); // FUNC_DOMINANT
char buf[12];
field.role(1, buf, sizeof(buf)); // "primary"
GingoNoteContext ctx = field.noteContext(GingoNote("E"));
ctx.degree; // 3
ctx.function; // FUNC_TONIC
ctx.inScale; // true
ctx.interval.semitones(); // 4GingoFretboard guitar = GingoFretboard::guitar();
// Também: ::violao(), ::cavaquinho(), ::mandolin(), ::bandolim(), ::ukulele()
// Afinações alternativas: ::dropD(), ::openG(), ::dadgad()
guitar.noteAt(0, 5); // GingoNote("A")
guitar.midiAt(0, 0); // 40 (E2)
GingoFingering fgs[5];
guitar.fingerings(GingoChord("CM"), fgs, 5);
GingoFingering opens[5];
guitar.openFingerings(GingoChord("GM"), opens, 5);
GingoFingering ccs[7];
guitar.commonChords(GingoScale("G", SCALE_MAJOR), ccs, 7);
GingoFretboard capo2 = guitar.capo(2);GingoEvent ne = GingoEvent::noteEvent(GingoNote("C"), GingoDuration("quarter"), 4);
GingoEvent ce = GingoEvent::chordEvent(GingoChord("CM"), GingoDuration("half"));
GingoEvent re = GingoEvent::rest(GingoDuration("quarter"));
GingoSequence seq(GingoTempo(120), GingoTimeSig(4, 4));
seq.add(ne);
seq.totalBeats(); // 1.0
seq.totalSeconds(); // 0.5 a 120 BPM
seq.transpose(5);O Monitor recebe eventos musicais e mantém notas seguradas, pedal de sustain, acorde detectado, campo deduzido e contexto por nota. As entradas chegam de qualquer transporte externo. O Monitor em si não decodifica MIDI.
GingoMonitor monitor;
monitor.setChannel(0xFF); // aceita todos os channels (default), ou 0-15 pra filtrar
// Alimentar a partir dos callbacks do transporte:
monitor.noteOn(0, 60, 100); // channel 0, C4, velocity 100
monitor.noteOn(0, 64, 100); // channel 0, E4
monitor.noteOn(0, 67, 100); // channel 0, G4
monitor.sustainOn();
monitor.sustainOff();
monitor.reset(); // all notes off
// Consultar estado:
monitor.hasChord(); // true
monitor.currentChord(); // GingoChord("CM")
monitor.currentField(); // GingoField
// Callbacks (lambdas std::function, fora do AVR):
monitor.onChordDetected([](const GingoChord& c) { /* ... */ });
monitor.onFieldChanged([](const GingoField& f) { /* ... */ });
monitor.onNoteOn ([](const GingoNoteContext& ctx) { /* ... */ });GingoFlow é um interpretador de stream UMP ao vivo. Ele captura Universal MIDI Packets brutos sem bloquear nem descartar, e então tece um stream de eventos lógicos estruturado pelo eixo rítmico: cada evento carrega resolução MIDI 2.0 completa, uma duração assim que o note-off pareia, e uma família de índices que liga pares note-on/note-off e agrupa notas simultâneas em acordes. A captura está sempre ligada; as faces lógica e de forward bruto são opcionais. Template header-only (N = tamanho do ring de captura), disponível onde o toolchain entrega <functional> (não em AVR).
GingoFlow<256> flow; // N = tamanho do ring de captura
// Face lógica: eventos estruturados com pareamento de notas, agrupamento de acordes, duração.
flow.onEvent([](const GingoFlowEvent& ev) {
ev.kind; // NOTE_ON / NOTE_OFF / CC / ...
ev.note; // número da nota
ev.velocity; // resolução MIDI 2.0 completa de 16 bits
ev.idx.event; // ordem global
ev.idx.note; // pareia on/off; o off carrega ev.durationMs
ev.idx.chord; // grupo de notas simultâneas
});
// Face opcional de forward de UMP bruto (bridge ou chain):
flow.onForward([](const uint32_t* words, uint8_t n) { /* re-emite */ });
// Alimente UMP bruto de qualquer transporte (USB MIDI 2.0, MIDI 1.0 convertido, bridge):
uint32_t words[2] = { 0x40903C00u, 0x80000000u }; // note-on MIDI 2.0, C4
flow.ingest(words, 2, millis()); // não-bloqueante (vai pra spine)
// Drene no seu loop; a captura nunca bloqueia, a interpretação é opcional:
while (flow.process()) { /* eventos disparam via onEvent */ }
flow.activeNoteCount(); // notas seguradas agora
flow.droppedTotal(); // pacotes que o ring de captura teve que descartar (backpressure)
// Regra de agrupamento de acordes calibrável (onset-cluster por default):
flow.config().chordRule = CHORD_ONSET_CLUSTER; // ou CHORD_SIMULTANEITY
flow.config().onsetWindowMs = 50;UMP bruto vem de qualquer transporte: midi2cpp (USB MIDI 2.0), uma conexão ESP32_Host_MIDI, ou bytes MIDI 1.0 convertidos para UMP. Veja os exemplos Flow e T-Display-S3-Piano-Flow.
// Evento único -> bytes MIDI 1.0 (NoteOn + NoteOff, 6 bytes pra eventos de nota).
uint8_t buf[6];
uint8_t n = GingoMIDI1::fromEvent(noteEvent, buf, sizeof(buf));
// Sequência -> stream de bytes MIDI 1.0. O default mantém o canal de
// cada evento; passe um 0-15 explícito pra forçar override em todos.
uint8_t out[256];
uint16_t total = GingoMIDI1::fromSequence(seq, out, sizeof(out));
// Ou com override explícito:
uint16_t total2 = GingoMIDI1::fromSequence(seq, out, sizeof(out), 5);A entrada de byte stream MIDI 1.0 fica intencionalmente fora do escopo. Use qualquer parser externo (Arduino MIDI Library, parser próprio, etc.) e chame GingoMonitor direto.
auto chordUMP = GingoMIDI2::chordName(GingoChord("CM"));
auto keySigUMP = GingoMIDI2::keySignature(scale); // group=0, channel=0 default
auto keySigCh5 = GingoMIDI2::keySignature(scale, /*group=*/0, /*channel=*/5);
GingoNoteContext ctx = field.noteContext(GingoNote("E"));
auto rccUMP = GingoMIDI2::perNoteController(ctx, /*midiNote=*/64);
chordUMP.wordCount; // 4 (Flex Data 128-bit)
rccUMP.wordCount; // 2 (per-note CC 64-bit)
chordUMP.byteCount(); // 16
uint8_t bytes[16];
chordUMP.toBytesBE(bytes, sizeof(bytes)); // serialização big-endian pro fioDispatch de UMP recebido e MIDI-CI estão fora do escopo. Use midi2cpp (ou outra lib UMP) para callbacks de recepção, e a biblioteca midi2 C99 para fluxos de responder/initiator MIDI-CI.
GingoChordComparison cmp(GingoChord("CM"), GingoChord("Am"));
cmp.common_count; // 2 (C e E em comum)
cmp.root_distance; // 3 semitons
cmp.same_quality; // false
cmp.voice_leading; // movimento mínimo em semitons
cmp.transformation; // NEO_R (Relative)
cmp.interval_vector_a[6]; // vetor intervalar de ForteO Monitor é o ponto único de entrada para eventos musicais. A cola entre o transporte externo e o Monitor leva poucas linhas e vive no seu sketch.
Receba UMP pela midi2cpp e encaminhe cada evento ao Monitor (noteOn, noteOff, sustainOn / sustainOff). No onChordDetected, renderize o acorde com GingoMIDI2::chordName(...) e envie o UMP resultante de volta pela midi2cpp. Veja os exemplos da midi2cpp para a API de recepção e envio.
MIDI.setHandleNoteOn ([](byte ch, byte note, byte vel) {
monitor.noteOn(ch - 1, note, vel); // 1-16 -> 0-15 (convenção UMP)
});
MIDI.setHandleNoteOff([](byte ch, byte note, byte vel) {
monitor.noteOff(ch - 1, note);
});
MIDI.setHandleControlChange([](byte ch, byte cc, byte val) {
if (cc == 64) { (val >= 64) ? monitor.sustainOn() : monitor.sustainOff(); }
else if (cc == 123) { monitor.reset(); }
});Decodifique o byte stream da UART inline (running status, absorção de SysEx, bytes de real-time) e encaminhe os eventos musicais ao Monitor com noteOn / noteOff / sustainOn / sustainOff. Um parser de ~30 linhas resolve.
| Exemplo | Descrição | Tier |
|---|---|---|
| BasicNote | Criação de notas, transposição, MIDI, frequência | 1 |
| ChordNotes | Notas do acorde, intervalos, identify | 1 |
| ScaleExplorer | Escalas, modos, pentatônica | 2 |
| HarmonicField | Tríades, tétrades, funções harmônicas | 2 |
| Gingoduino_to_MIDI | Constrói uma sequência e serializa via GingoMIDI1::fromSequence |
3 |
| Flow | Harness de flow RP2040: um device emite um gabarito UMP ritmado, um host o interpreta ao vivo com GingoFlow (captura + interpretação) |
3 |
| T-Display-S3-Piano-Flow | Piano de flow ao vivo no T-Display S3: acorde com inversão, notas seguradas, duração, e um log rolante de eventos processados, agnóstico à fonte sobre um host USB MIDI 2.0 (GingoFlow) |
3 |
| V06_SelfTest | Suite de aceitação on-device cobrindo a superfície pública até a v0.6.0, incluindo GingoFlow |
3 |
| Foundation | Prova exaustiva on-device: 398 asserções musicais (cada entrada de INTERVAL_TABLE, cada CHORD_FORMULAS, cada SCALE_MASKS, pipeline de análise de campo completo) | 3 |
| Foundation_AVR | Subconjunto reduzido que cabe em AVR 8-bit: primitivas Note + Interval + Chord + Scale + MIDI, 93 asserções | 1 |
| Foundation_Daisy | Port nativo libDaisy da prova de fundação para Daisy Seed (STM32H750, Cortex-M7). Build com make, flash via DFU |
3 |
Procurando exemplos de entrada MIDI USB/BLE? Eles vivem na biblioteca de transporte, não aqui:
ESP32_Host_MIDI/examples/USB-Host-MIDI2/- host USB que recebe UMP bruto de um device MIDI 2.0 e decodifica Channel VoiceESP32_Host_MIDI/examples/T-Display-S3-BLE-Receiver/- entrada MIDI BLE com um visualizador piano em tempo real
A separação é deliberada: gingoduino é motor de teoria musical e fica fora do fio; a biblioteca de transporte cuida dos exemplos de integração no nível dos bytes.
g++ -std=c++11 -I. -Wall -Wextra -Werror \
-o extras/tests/test_native extras/tests/test_native.cpp \
&& ./extras/tests/test_native585 testes passam sob -Wall -Wextra -Werror, mais 398 asserções
adicionais de correção musical em extras/tests/foundation.cpp que
cobrem exaustivamente a INTERVAL_TABLE, cada entrada de CHORD_FORMULAS,
cada alias de CHORD_TYPE_MAP e cada escala de SCALE_MASKS. As duas
suítes passam em gcc 13, clang LLVM 22 e em gcc + AddressSanitizer +
UndefinedBehaviorSanitizer.
A biblioteca é verificada no host (PC com gcc/clang nativos + sanitizers)
e em placas reais nas famílias de MCU que ela diz suportar. Mesmo sketch
Foundation, só muda o FQBN; resultados byte a byte idênticos entre
compiladores e arquiteturas. Em AVR 8-bit o Foundation completo não cabe,
então um sketch Foundation_AVR reduzido exercita as primitivas Note +
Interval + Chord + Scale + MIDI (Field, Monitor, Progression e Compare
ficam fora de escopo em AVR).
| Placa | MCU | Resultado Foundation | Flash usado | Toolchain |
|---|---|---|---|---|
| Arduino Leonardo | ATmega32U4, AVR 8-bit @16 MHz | Subset AVR 93/93 PASS | 51% (14.7 KB / 28.6 KB), SRAM 14% | avr-gcc |
| Seeed XIAO SAMD21 | ARM Cortex-M0+ @48 MHz | 398/398 PASS | 25% (67 KB / 256 KB) | arm-none-eabi (Seeed SAMD core) |
| Adafruit Feather RP2040 USB Host | RP2040, ARM Cortex-M0+ dual @133 MHz | 398/398 PASS | 1% (94 KB / 8 MB) | arm-none-eabi (arduino-pico) |
| Nice!Nano nRF52840 (classe Pro Micro) | nRF52840, ARM Cortex-M4F @64 MHz + SoftDevice S140 | 398/398 PASS | 15% (125 KB / 815 KB) | arm-none-eabi 9.2 (adafruit-nrf52) |
| Daisy Seed | STM32H750, ARM Cortex-M7 @480 MHz | 398/398 PASS (libDaisy, Foundation_Daisy) |
91% (119 KB / 128 KB) | arm-none-eabi 13.2 + libDaisy |
| Teensy 4.1 | NXP i.MX RT1062, ARM Cortex-M7 @600 MHz | 398/398 PASS | <1% (82 KB / 8 MB) | arm-none-eabi (Teensyduino) |
| ESP32-S3 DevKitC | Espressif Xtensa LX7 dual @240 MHz | 398/398 PASS | 24% (319 KB / 1.25 MB) | xtensa-esp32-elf-g++ (arduino-esp32) |
Total: 980 asserts nativos no PC + 5×398 + 93 = 3 063 asserções musicais exercitadas em silício através de seis famílias de MCU (AVR 8-bit, Cortex-M0+, Cortex-M4F, Cortex-M7, Xtensa LX7) e cinco toolchains, validadas em 2026-06-02.
A fronteira está cravada pra manter o motor focado. Algumas coisas deliberadamente não pertencem aqui.
- Não é um parser UMP. Dispatch de UMP recebido pertence à biblioteca de transporte. Use
midi2cpppra USB MIDI 2.0 ou o core C99midi2direto, e encaminhe os callbacks decodificados proGingoMonitor. - Não é um stack USB. Gingoduino não cuida de descriptors, endpoints, ou alt settings. Combine com
midi2cpp(USB MIDI 2.0), Arduino MIDI Library (MIDI 1.0 DIN/Serial), ou seu transport próprio. - Não é um responder MIDI-CI. Capability Inquiry, negociação de Profile e Property Exchange vivem em
midi2. Gingoduino não expõe superfície MIDI-CI. - Não é um sintetizador. Eventos musicais são detectados e analisados; geração de som é território da aplicação.
- Não é leitor nem gravador de arquivo MIDI. Standard MIDI File (SMF) I/O está fora do escopo.
GingoSequenceé uma estrutura de runtime, não um formato de arquivo. - Não é biblioteca de desktop. O alvo são MCUs. Testes rodam em desktop com
g++, mas a API e o modelo de memória assumem restrições de embedded.
MIT License. Veja LICENSE.
Saulo Veríssimo
