Provide 1:1 examples from original to port and use them tests when building docs.

This commit is contained in:
pszsh 2026-03-04 03:12:46 -08:00
parent 52bf6e4c95
commit fe877262d1
50 changed files with 1654 additions and 46 deletions

View File

@ -1,12 +0,0 @@
# Getting Started
cs-midi is a standalone MIDI library for pico-sdk, extracted from [Control Surface](https://github.com/tttapa/Control-Surface). It provides BLE MIDI, Classic BT SPP MIDI, rotary encoders, buttons, potentiometers, LEDs, banks, and a flexible pipe-based routing system.
## What's included
- **BLE MIDI** via BTstack (pico-native, no Arduino)
- **Output elements**: NoteButton, CCRotaryEncoder, CCPotentiometer, and more
- **Input elements**: NoteValue, CCValue, PBValue, LEDs
- **Pipe routing**: bidirectional MIDI pipes with filter/transform support
- **Banks**: switch between multiple CC/note mappings
- **Custom hooks**: extend the library without modifying it

View File

@ -1,33 +0,0 @@
# Installation
## Prerequisites
- [pico-sdk](https://github.com/raspberrypi/pico-sdk) (v2.0+)
- CMake 3.13+
- ARM GCC toolchain
## Adding to your project
Add cs-midi as a git submodule:
```bash
git submodule add https://git.else-if.org/jess/cs-midi.git lib/cs-midi
```
In your `CMakeLists.txt`:
```cmake
add_subdirectory(lib/cs-midi)
target_link_libraries(your_target cs_midi)
target_include_directories(cs_midi PRIVATE ${CMAKE_CURRENT_LIST_DIR})
```
The last line is required so cs-midi can find your project's `btstack_config.h` and `lwipopts.h`.
## Include
```cpp
#include <cs_midi.h>
```
This single header pulls in everything: output elements, input elements, pipes, banks, BLE interface, and the Control Surface singleton.

42
docs/_index.md Normal file
View File

@ -0,0 +1,42 @@
# cs-midi
A standalone pico-sdk extraction of [tttapa/Control-Surface](https://github.com/tttapa/Control-Surface) (GPL-3.0).
cs-midi provides the core MIDI element system, BLE MIDI transport, banks, selectors, and the declarative `Control_Surface` singleton for use with the Raspberry Pi Pico SDK and the CYW43 radio module (BTstack BLE backend).
## Getting Started
```cpp
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include <cs_midi.h>
using namespace cs;
BluetoothMIDI_Interface midi;
NoteButton button {5, {MIDI_Notes::C[4], Channel_1}};
int main() {
stdio_init_all();
if (cyw43_arch_init()) return 1;
Control_Surface.begin();
while (true) {
Control_Surface.loop();
sleep_ms(1);
}
}
```
## Differences from Control Surface
- `#include <cs_midi.h>` replaces `#include <Control_Surface.h>`
- `cs::BluetoothMIDI_Interface` replaces `USBMIDI_Interface`
- Standard `main()` with `cyw43_arch_init()` replaces Arduino `setup()`/`loop()`
- All types live in the `cs::` namespace
- No `MCU::` namespace — use raw CC numbers or `MIDI_CC::` constants
## Credits
Original library: [Control Surface](https://github.com/tttapa/Control-Surface) by **Pieter P (tttapa)** (GPL-3.0).
pico-sdk port: [pszsh](https://else-if.org).

View File

@ -1,2 +1,2 @@
title: cs-midi
author: jess
author: Pieter P (tttapa) — pico-sdk port

264
docs/classes.md Normal file
View File

@ -0,0 +1,264 @@
# Class Reference
Cross-reference between cs-midi and the original [Control Surface](https://tttapa.github.io/Control-Surface/Doxygen/annotated.html) library.
## Ported Classes
These classes are available in cs-midi and work identically to their Control Surface equivalents (within the `cs::` namespace unless noted).
### MIDI Output Elements
| Class | Template | Description |
|-------|----------|-------------|
| `NoteButton` | | Note On/Off on button press/release |
| `NoteButtonLatched` | | Toggle Note On/Off on each press |
| `NoteButtonLatching` | | Note On while held, toggles |
| `NoteChordButton` | | Chord (multiple notes) on press |
| `NoteButtons` | `<N>` | Array of sequential note buttons |
| `NoteButtonMatrix` | `<R, C>` | Row/column scanned note grid |
| `CCButton` | | CC 127/0 on press/release |
| `CCButtonLatched` | | Toggle CC 127/0 on each press |
| `CCButtonLatching` | | CC 127 while held, toggles |
| `CCButtons` | `<N>` | Array of sequential CC buttons |
| `CCButtonMatrix` | `<R, C>` | Row/column scanned CC grid |
| `CCPotentiometer` | | Analog input to CC 0-127 |
| `PBPotentiometer` | | Analog input to 14-bit Pitch Bend |
| `CCRotaryEncoder` | | Relative CC from encoder |
| `BorrowedCCRotaryEncoder` | | Shared encoder reference for CC |
| `CCAbsoluteEncoder` | | Absolute CC from encoder position |
| `PBAbsoluteEncoder` | | 14-bit Pitch Bend from encoder |
| `CCIncrementDecrementButtons` | | Two buttons for CC inc/dec + reset |
| `PCButton` | | Program Change on press |
| `ProgramChanger` | `<N>` | Selectable program change list |
### MIDI Input Elements
| Class | Template | Description |
|-------|----------|-------------|
| `NoteValue` | | Incoming Note value (8-bit) |
| `CCValue` | | Incoming CC value (8-bit) |
| `KPValue` | | Incoming Key Pressure value (8-bit) |
| `PBValue` | | Incoming Pitch Bend value (14-bit) |
| `NoteRange` | `<Len>` | Array of incoming Note values |
| `CCRange` | `<Len>` | Array of incoming CC values |
| `KPRange` | `<Len>` | Array of incoming Key Pressure values |
| `NoteLED` | | LED driven by Note On/Off |
| `CCLED` | | LED driven by CC value |
| `KPLED` | | LED driven by Key Pressure |
### Bankable Input Elements
| Class | Template | Description |
|-------|----------|-------------|
| `Bankable::NoteValue` | `<BankSize>` | Bank-switched Note value |
| `Bankable::CCValue` | `<BankSize>` | Bank-switched CC value |
| `Bankable::KPValue` | `<BankSize>` | Bank-switched Key Pressure value |
| `Bankable::PBValue` | `<BankSize>` | Bank-switched Pitch Bend value |
| `Bankable::NoteLED` | `<BankSize>` | Bank-switched Note LED |
| `Bankable::CCLED` | `<BankSize>` | Bank-switched CC LED |
| `Bankable::KPLED` | `<BankSize>` | Bank-switched Key Pressure LED |
### Banks and Selectors
| Class | Template | Description |
|-------|----------|-------------|
| `Bank` | `<N>` | N-setting bank for bankable elements |
| `Transposer` | `<Min, Max>` | Note transposition bank |
| `OutputBank` | | Bank for output elements |
| `EncoderSelector` | `<N>` | Bank selection via rotary encoder |
| `IncrementSelector` | `<N>` | Bank selection via single button |
| `IncrementDecrementSelector` | `<N>` | Bank selection via two buttons |
| `ManyButtonsSelector` | `<N>` | Bank selection via N buttons |
| `SwitchSelector` | | Two-state selector from switch |
| `ProgramChangeSelector` | `<N>` | Bank selection via incoming MIDI PC |
| `EncoderSelectorLEDs` | `<N>` | EncoderSelector with LED indicators |
| `IncrementSelectorLEDs` | `<N>` | IncrementSelector with LED indicators |
| `IncrementDecrementSelectorLEDs` | `<N>` | IncrementDecrementSelector with LEDs |
| `ManyButtonsSelectorLEDs` | `<N>` | ManyButtonsSelector with LEDs |
| `SwitchSelectorLED` | | SwitchSelector with LED |
| `ProgramChangeSelectorLEDs` | `<N>` | ProgramChangeSelector with LEDs |
### MIDI Interfaces
| Class | Template | Description |
|-------|----------|-------------|
| `BluetoothMIDI_Interface` | | BLE MIDI via CYW43 BTstack |
### MIDI Routing
| Class | Template | Description |
|-------|----------|-------------|
| `MIDI_Pipe` | | Connects a MIDI source to a sink |
| `MIDI_PipeFactory` | `<N>` | Creates N unidirectional pipes |
| `MIDI_Sink` | | Base class for MIDI receivers |
| `MIDI_Source` | | Base class for MIDI senders |
### Hardware Utilities (`AH::` namespace)
| Class | Template | Description |
|-------|----------|-------------|
| `Button` | | Debounced button with state machine |
| `IncrementButton` | | Button with auto-repeat |
| `IncrementDecrementButtons` | | Button pair with auto-repeat |
| `ButtonMatrix` | `<R, C>` | Row/column button scanner |
| `GenericFilteredAnalog` | `<...>` | EMA-filtered analog input with hysteresis |
| `AHEncoder` | | Interrupt-driven quadrature encoder |
### Infrastructure
| Class | Description |
|-------|-------------|
| `Control_Surface_` | Singleton coordinating all elements (`Control_Surface.begin()` / `.loop()`) |
| `MIDIAddress` | Note/CC address with channel and cable |
| `MIDIChannelCable` | Channel + cable pair |
| `RelativeMIDIAddress` | Relative address offset |
| `Channel` | MIDI channel (1-16) |
| `Cable` | MIDI cable number |
| `DigitalCCSender` | Sends CC 0/127 |
| `ContinuousCCSender` | Sends CC 0-127 |
| `ContinuousCCSender14` | Sends 14-bit CC |
| `DigitalNoteSender` | Sends Note On/Off |
| `RelativeCCSender` | Sends relative CC (Mackie, etc.) |
| `PitchBendSender` | Sends Pitch Bend |
| `ProgramChangeSender` | Sends Program Change |
---
## Not Yet Ported
These classes exist in the original Control Surface but are not yet available in cs-midi.
### Bankable Output Elements
The entire bankable output layer (Phase 11) has not been extracted. This includes bank-switched versions of every output element.
| Class | Original | Priority |
|-------|----------|----------|
| `Bankable::NoteButton` | Bankable note button | High |
| `Bankable::CCButton` | Bankable CC button | High |
| `Bankable::CCPotentiometer` | Bankable CC pot | High |
| `Bankable::CCRotaryEncoder` | Bankable CC encoder | High |
| `Bankable::CCAbsoluteEncoder` | Bankable absolute encoder | Medium |
| `Bankable::PBAbsoluteEncoder` | Bankable PB encoder | Medium |
| `Bankable::PBPotentiometer` | Bankable PB pot | Medium |
| `Bankable::PCButton` | Bankable program change button | Medium |
| `Bankable::NoteButtonLatched` | Bankable latched note | Low |
| `Bankable::NoteButtonLatching` | Bankable latching note | Low |
| `Bankable::CCButtonLatched` | Bankable latched CC | Low |
| `Bankable::CCButtonLatching` | Bankable latching CC | Low |
| `Bankable::NoteButtons` | Bankable note button array | Low |
| `Bankable::CCButtons` | Bankable CC button array | Low |
| `Bankable::NoteButtonMatrix` | Bankable note matrix | Low |
| `Bankable::CCButtonMatrix` | Bankable CC matrix | Low |
| `Bankable::NoteChordButton` | Bankable chord button | Low |
| `Bankable::CCIncrementDecrementButtons` | Bankable inc/dec | Low |
| `Bankable::CCSmartPotentiometer` | Smart pot (avoids bank-switch jumps) | Medium |
| `Bankable::PBSmartPotentiometer` | Smart PB pot | Medium |
### Bankable ManyAddresses Output Elements
Per-bank explicit address lists (alternative to offset-based banking).
| Class | Original |
|-------|----------|
| `Bankable::ManyAddresses::NoteButton` | Per-bank note addresses |
| `Bankable::ManyAddresses::CCButton` | Per-bank CC addresses |
| `Bankable::ManyAddresses::CCPotentiometer` | Per-bank CC pot addresses |
| `Bankable::ManyAddresses::CCRotaryEncoder` | Per-bank CC encoder addresses |
| `Bankable::ManyAddresses::CCAbsoluteEncoder` | Per-bank absolute encoder addresses |
| `Bankable::ManyAddresses::PBAbsoluteEncoder` | Per-bank PB encoder addresses |
| `Bankable::ManyAddresses::PBPotentiometer` | Per-bank PB pot addresses |
| `Bankable::ManyAddresses::PCButton` | Per-bank PC addresses |
| `Bankable::ManyAddresses::NoteButtonMatrix` | Per-bank note matrix |
| `Bankable::ManyAddresses::CCButtonMatrix` | Per-bank CC matrix |
| `Bankable::ManyAddresses::CCIncrementDecrementButtons` | Per-bank inc/dec |
### MCU (Mackie Control Universal)
Full Mackie Control protocol support — VU meters, V-Pots, LCD, time display, etc.
| Class | Description |
|-------|-------------|
| `MCU::VU` | VU meter input element |
| `MCU::VULEDs` | VU meter with LED output |
| `MCU::VPotRing` | V-Pot ring value reader |
| `MCU::VPotRingLEDs` | V-Pot ring with LED output |
| `MCU::LCD` | 112-char LCD display reader |
| `MCU::SevenSegmentDisplay` | 7-segment display driver |
| `MCU::TimeDisplay` | Time code display |
| `MCU::AssignmentDisplay` | Assignment display |
| `MCU::Bankable::VU` | Bankable VU meter |
| `MCU::Bankable::VULEDs` | Bankable VU with LEDs |
| `MCU::Bankable::VPotRing` | Bankable V-Pot ring |
| `MCU::Bankable::VPotRingLEDs` | Bankable V-Pot ring with LEDs |
### USB MIDI
| Class | Description |
|-------|-------------|
| `USBMIDI_Interface` | USB MIDI device interface |
| `USBHostMIDI_Interface` | USB Host MIDI interface |
| `GenericUSBMIDI_Interface` | Configurable USB MIDI backend |
| `USBDebugMIDI_Interface` | Serial monitor debug output |
### Serial MIDI
| Class | Description |
|-------|-------------|
| `HardwareSerialMIDI_Interface` | 5-pin DIN MIDI over UART |
| `SoftwareSerialMIDI_Interface` | Software serial MIDI |
| `HairlessMIDI_Interface` | Hairless MIDI serial bridge |
### LED Bars and PWM
| Class | Description |
|-------|-------------|
| `NoteCCKPLEDBar` | LED bar graph driven by MIDI |
| `NoteCCKPLEDPWM` | PWM LED brightness by velocity |
| `Bankable::NoteCCKPLEDBar` | Bankable LED bar |
| `Bankable::NoteCCKPLEDPWM` | Bankable PWM LED |
### FastLED (Addressable LEDs)
| Class | Description |
|-------|-------------|
| `NoteCCKPRangeFastLED` | Addressable LED strip driven by MIDI |
| `Bankable::NoteCCKPRangeFastLED` | Bankable variant |
### Extended I/O Hardware
| Class | Description |
|-------|-------------|
| `AH::AnalogMultiplex` | CD74HC4067 / CD74HC4051 multiplexer |
| `AH::ShiftRegisterOut` | 74HC595 shift register output |
| `AH::SPIShiftRegisterOut` | SPI-based shift register |
| `AH::ShiftRegisterOutRGB` | RGB LED shift register |
| `AH::MAX7219` | MAX7219 LED driver |
| `AH::MAX7219SevenSegmentDisplay` | MAX7219 7-segment display |
| `AH::LEDs` | Array of simple LEDs |
| `AH::DotBarDisplayLEDs` | Dot/bar mode LED array |
| `AH::MultiPurposeButton` | Long press / double press detection |
| `AH::RegisterEncoders` | Multiple encoders via registers |
### Display
| Class | Description |
|-------|-------------|
| `DisplayElement` | Base for on-screen elements |
| `DisplayInterface` | Abstract display driver |
| `SSD1306_DisplayInterface` | SSD1306 OLED driver |
| `BitmapDisplay` | Bitmap rendering on display |
### Audio
| Class | Description |
|-------|-------------|
| `AudioVU` | Audio level analysis |
| `AudioVULEDs` | Audio VU with LED output |
| `VolumeControl` | Audio volume knob |
### Motor Fader
| Class | Description |
|-------|-------------|
| `MotorFader` | Motorized fader with touch sense |

View File

@ -0,0 +1,30 @@
# NoteButton
Sends Note On/Off on button press/release.
> Original: `NoteButton.ino`
```cpp
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include <cs_midi.h>
using namespace cs;
BluetoothMIDI_Interface midi;
NoteButton button {
5, // push button on GPIO 5
{MIDI_Notes::C[4], Channel_1}, // note C4, channel 1
};
int main() {
stdio_init_all();
if (cyw43_arch_init()) return 1;
Control_Surface.begin();
while (true) {
Control_Surface.loop();
sleep_ms(1);
}
}
```

View File

@ -0,0 +1,30 @@
# CCButton
Sends CC 127 on press, CC 0 on release.
> Original: `CCButton.ino`
```cpp
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include <cs_midi.h>
using namespace cs;
BluetoothMIDI_Interface midi;
CCButton button {
5,
{MIDI_CC::General_Purpose_Controller_1, Channel_1},
};
int main() {
stdio_init_all();
if (cyw43_arch_init()) return 1;
Control_Surface.begin();
while (true) {
Control_Surface.loop();
sleep_ms(1);
}
}
```

View File

@ -0,0 +1,30 @@
# PCButton
Sends Program Change on button press.
> Original: `PCButton.ino`
```cpp
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include <cs_midi.h>
using namespace cs;
BluetoothMIDI_Interface midi;
PCButton pcBtn {
2,
{MIDI_PC::Steel_Drums, Channel_1},
};
int main() {
stdio_init_all();
if (cyw43_arch_init()) return 1;
Control_Surface.begin();
while (true) {
Control_Surface.loop();
sleep_ms(1);
}
}
```

View File

@ -0,0 +1,37 @@
# NoteChordButton
Plays a chord on button press.
> Original: `NoteChordButton.ino`
```cpp
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include <cs_midi.h>
using namespace cs;
BluetoothMIDI_Interface midi;
constexpr Channel channel = Channel_1;
NoteChordButton buttons[] {
{2, {MIDI_Notes::C[4], channel}, Bass::Double + Chords::Major},
{3, {MIDI_Notes::D[4], channel}, Bass::Double + Chords::Minor},
{4, {MIDI_Notes::E[4], channel}, Bass::Double + Chords::Minor},
{5, {MIDI_Notes::F[4], channel}, Bass::Double + Chords::MajorFirstInv},
{6, {MIDI_Notes::G[4], channel}, Bass::Double + Chords::MajorSecondInv},
{7, {MIDI_Notes::A[4], channel}, Bass::Double + Chords::MinorSecondInv},
{8, {MIDI_Notes::B[4], channel}, Bass::Double + Chords::Diminished},
};
int main() {
stdio_init_all();
if (cyw43_arch_init()) return 1;
Control_Surface.begin();
while (true) {
Control_Surface.loop();
sleep_ms(1);
}
}
```

View File

@ -0,0 +1,28 @@
# NoteButtonLatched
First press sends Note On, second press sends Note Off.
```cpp
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include <cs_midi.h>
using namespace cs;
BluetoothMIDI_Interface midi;
NoteButtonLatched button {
5,
{MIDI_Notes::C[4], Channel_1},
};
int main() {
stdio_init_all();
if (cyw43_arch_init()) return 1;
Control_Surface.begin();
while (true) {
Control_Surface.loop();
sleep_ms(1);
}
}
```

View File

@ -0,0 +1,28 @@
# NoteButtonLatching
Note On while held, Note Off on release; toggles state.
```cpp
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include <cs_midi.h>
using namespace cs;
BluetoothMIDI_Interface midi;
NoteButtonLatching button {
5,
{MIDI_Notes::C[4], Channel_1},
};
int main() {
stdio_init_all();
if (cyw43_arch_init()) return 1;
Control_Surface.begin();
while (true) {
Control_Surface.loop();
sleep_ms(1);
}
}
```

View File

@ -0,0 +1,29 @@
# NoteButtons
Multiple note buttons with sequential addresses.
```cpp
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include <cs_midi.h>
using namespace cs;
BluetoothMIDI_Interface midi;
NoteButtons<4> buttons {
{2, 3, 4, 5}, // GPIO pins
{MIDI_Notes::C[4], Channel_1}, // base address
1, // increment between addresses
};
int main() {
stdio_init_all();
if (cyw43_arch_init()) return 1;
Control_Surface.begin();
while (true) {
Control_Surface.loop();
sleep_ms(1);
}
}
```

View File

@ -0,0 +1,35 @@
# NoteButtonMatrix
Grid of note buttons using row/column scanning.
> Original: `NoteButtonMatrix.ino`
```cpp
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include <cs_midi.h>
using namespace cs;
BluetoothMIDI_Interface midi;
NoteButtonMatrix<2, 3> matrix {
{0, 1}, // row pins
{2, 3, 4}, // column pins
{{
{MIDI_Notes::C[4], MIDI_Notes::D[4], MIDI_Notes::E[4]},
{MIDI_Notes::F[4], MIDI_Notes::G[4], MIDI_Notes::A[4]},
}},
Channel_1,
};
int main() {
stdio_init_all();
if (cyw43_arch_init()) return 1;
Control_Surface.begin();
while (true) {
Control_Surface.loop();
sleep_ms(1);
}
}
```

View File

@ -0,0 +1,28 @@
# CCButtonLatched
First press sends CC 127, second press sends CC 0.
```cpp
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include <cs_midi.h>
using namespace cs;
BluetoothMIDI_Interface midi;
CCButtonLatched button {
5,
{MIDI_CC::General_Purpose_Controller_1, Channel_1},
};
int main() {
stdio_init_all();
if (cyw43_arch_init()) return 1;
Control_Surface.begin();
while (true) {
Control_Surface.loop();
sleep_ms(1);
}
}
```

View File

@ -0,0 +1,28 @@
# CCButtonLatching
CC 127 while held, CC 0 on release; toggles state.
```cpp
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include <cs_midi.h>
using namespace cs;
BluetoothMIDI_Interface midi;
CCButtonLatching button {
5,
{MIDI_CC::General_Purpose_Controller_1, Channel_1},
};
int main() {
stdio_init_all();
if (cyw43_arch_init()) return 1;
Control_Surface.begin();
while (true) {
Control_Surface.loop();
sleep_ms(1);
}
}
```

View File

@ -0,0 +1,29 @@
# CCButtons
Multiple CC buttons with sequential addresses.
```cpp
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include <cs_midi.h>
using namespace cs;
BluetoothMIDI_Interface midi;
CCButtons<4> buttons {
{2, 3, 4, 5},
{MIDI_CC::General_Purpose_Controller_1, Channel_1},
1,
};
int main() {
stdio_init_all();
if (cyw43_arch_init()) return 1;
Control_Surface.begin();
while (true) {
Control_Surface.loop();
sleep_ms(1);
}
}
```

View File

@ -0,0 +1,33 @@
# CCButtonMatrix
Grid of CC buttons using row/column scanning.
```cpp
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include <cs_midi.h>
using namespace cs;
BluetoothMIDI_Interface midi;
CCButtonMatrix<2, 3> matrix {
{0, 1}, // row pins
{2, 3, 4}, // column pins
{{
{16, 17, 18},
{19, 20, 21},
}},
Channel_1,
};
int main() {
stdio_init_all();
if (cyw43_arch_init()) return 1;
Control_Surface.begin();
while (true) {
Control_Surface.loop();
sleep_ms(1);
}
}
```

View File

@ -0,0 +1,30 @@
# CCPotentiometer
Analog input mapped to CC value 0-127.
> Original: `Control-Change-Potentiometer.ino`
```cpp
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include <cs_midi.h>
using namespace cs;
BluetoothMIDI_Interface midi;
CCPotentiometer pot {
26, // ADC pin (GPIO 26 = ADC0)
{MIDI_CC::Channel_Volume, Channel_1},
};
int main() {
stdio_init_all();
if (cyw43_arch_init()) return 1;
Control_Surface.begin();
while (true) {
Control_Surface.loop();
sleep_ms(1);
}
}
```

View File

@ -0,0 +1,30 @@
# PBPotentiometer
Analog input mapped to 14-bit Pitch Bend.
> Original: `Pitch-Bend-Potentiometer.ino`
```cpp
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include <cs_midi.h>
using namespace cs;
BluetoothMIDI_Interface midi;
PBPotentiometer pot {
26, // ADC pin
Channel_1,
};
int main() {
stdio_init_all();
if (cyw43_arch_init()) return 1;
Control_Surface.begin();
while (true) {
Control_Surface.loop();
sleep_ms(1);
}
}
```

View File

@ -0,0 +1,33 @@
# CCRotaryEncoder
Relative CC encoder for DAW parameter control.
> Original: `RotaryEncoder.ino`
```cpp
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include <cs_midi.h>
using namespace cs;
BluetoothMIDI_Interface midi;
CCRotaryEncoder enc {
{0, 2}, // encoder pins A and B (constructs AHEncoder)
{16, Channel_1}, // CC address
1, // speed multiplier
4, // pulses per step
};
int main() {
stdio_init_all();
RelativeCCSender::setMode(relativeCCmode::MACKIE_CONTROL_RELATIVE);
if (cyw43_arch_init()) return 1;
Control_Surface.begin();
while (true) {
Control_Surface.loop();
sleep_ms(1);
}
}
```

View File

@ -0,0 +1,32 @@
# CCAbsoluteEncoder
Encoder position mapped to absolute CC value.
> Original: `AbsoluteRotaryEncoder.ino`
```cpp
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include <cs_midi.h>
using namespace cs;
BluetoothMIDI_Interface midi;
CCAbsoluteEncoder enc {
{0, 2}, // encoder pins
MIDI_CC::Pan, // CC address
1, // speed multiplier
4, // pulses per step
};
int main() {
stdio_init_all();
if (cyw43_arch_init()) return 1;
Control_Surface.begin();
while (true) {
Control_Surface.loop();
sleep_ms(1);
}
}
```

View File

@ -0,0 +1,30 @@
# PBAbsoluteEncoder
Encoder position mapped to 14-bit Pitch Bend.
```cpp
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include <cs_midi.h>
using namespace cs;
BluetoothMIDI_Interface midi;
PBAbsoluteEncoder enc {
{0, 2}, // encoder pins
Channel_1,
127, // large multiplier for PB's 14-bit range
4, // pulses per step
};
int main() {
stdio_init_all();
if (cyw43_arch_init()) return 1;
Control_Surface.begin();
while (true) {
Control_Surface.loop();
sleep_ms(1);
}
}
```

View File

@ -0,0 +1,33 @@
# CCIncrementDecrementButtons
Two buttons for CC increment/decrement with optional reset.
> Original: `CCIncrementDecrementButtons.ino`
```cpp
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include <cs_midi.h>
using namespace cs;
BluetoothMIDI_Interface midi;
CCIncrementDecrementButtons buttons {
{5, 6}, // increment pin, decrement pin
{16, Channel_1}, // CC address
1, // multiplier
{0x45, Channel_1}, // reset note (sent when both pressed)
};
int main() {
stdio_init_all();
RelativeCCSender::setMode(relativeCCmode::MACKIE_CONTROL_RELATIVE);
if (cyw43_arch_init()) return 1;
Control_Surface.begin();
while (true) {
Control_Surface.loop();
sleep_ms(1);
}
}
```

View File

@ -0,0 +1,41 @@
# ProgramChanger
Sends different program changes based on selector state.
> Original: `Program-Changer.ino`
```cpp
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include <cs_midi.h>
using namespace cs;
BluetoothMIDI_Interface midi;
ProgramChanger<4> programChanger {
{{
MIDI_PC::Acoustic_Grand_Piano,
MIDI_PC::Rock_Organ,
MIDI_PC::Steel_Drums,
MIDI_PC::Pad_2,
}},
Channel_1,
};
IncrementDecrementSelector<4> selector {
programChanger,
{2, 3},
Wrap::Wrap,
};
int main() {
stdio_init_all();
if (cyw43_arch_init()) return 1;
Control_Surface.begin();
while (true) {
Control_Surface.loop();
sleep_ms(1);
}
}
```

View File

@ -0,0 +1,31 @@
# Multiple CCPotentiometers
Several analog inputs mapped to CC on different channels.
> Original: `Multiple-Control-Change-Potentiometers.ino`
```cpp
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include <cs_midi.h>
using namespace cs;
BluetoothMIDI_Interface midi;
CCPotentiometer pots[] {
{26, {MIDI_CC::Channel_Volume, Channel_1}},
{27, {MIDI_CC::Channel_Volume, Channel_2}},
{28, {MIDI_CC::Channel_Volume, Channel_3}},
};
int main() {
stdio_init_all();
if (cyw43_arch_init()) return 1;
Control_Surface.begin();
while (true) {
Control_Surface.loop();
sleep_ms(1);
}
}
```

View File

@ -0,0 +1,32 @@
# BorrowedCCRotaryEncoder
Shared encoder reference for CC output, allowing multiple elements to read the same physical encoder.
```cpp
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include <cs_midi.h>
using namespace cs;
BluetoothMIDI_Interface midi;
AHEncoder encoder {0, 2};
BorrowedCCRotaryEncoder enc {
encoder,
{16, Channel_1},
1,
4,
};
int main() {
stdio_init_all();
if (cyw43_arch_init()) return 1;
Control_Surface.begin();
while (true) {
Control_Surface.loop();
sleep_ms(1);
}
}
```

View File

@ -0,0 +1,3 @@
# MIDI Output Elements
Output elements read physical controls (buttons, potentiometers, encoders) and send MIDI messages.

View File

@ -0,0 +1,30 @@
# NoteLED
LED responds to incoming Note On/Off messages.
> Original: `1.Note-LED.ino`
```cpp
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include <cs_midi.h>
using namespace cs;
BluetoothMIDI_Interface midi;
NoteLED led {
13, // LED pin
{MIDI_Notes::C[4], Channel_1},
};
int main() {
stdio_init_all();
if (cyw43_arch_init()) return 1;
Control_Surface.begin();
while (true) {
Control_Surface.loop();
sleep_ms(1);
}
}
```

View File

@ -0,0 +1,32 @@
# NoteRangeLEDs
Multiple LEDs respond to a range of incoming notes.
> Original: `2.Note-Range-LEDs.ino`
```cpp
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include <cs_midi.h>
using namespace cs;
BluetoothMIDI_Interface midi;
NoteLED leds[] {
{2, {MIDI_Notes::C[4], Channel_1}},
{3, {MIDI_Notes::D[4], Channel_1}},
{4, {MIDI_Notes::E[4], Channel_1}},
{5, {MIDI_Notes::F[4], Channel_1}},
};
int main() {
stdio_init_all();
if (cyw43_arch_init()) return 1;
Control_Surface.begin();
while (true) {
Control_Surface.loop();
sleep_ms(1);
}
}
```

View File

@ -0,0 +1,29 @@
# CCValue
Reads incoming CC messages and stores the value.
```cpp
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include <cs_midi.h>
using namespace cs;
BluetoothMIDI_Interface midi;
CCValue ccVal {
{MIDI_CC::Channel_Volume, Channel_1},
};
int main() {
stdio_init_all();
if (cyw43_arch_init()) return 1;
Control_Surface.begin();
while (true) {
Control_Surface.loop();
uint8_t val = ccVal.getValue();
(void)val;
sleep_ms(1);
}
}
```

View File

@ -0,0 +1,29 @@
# PBValue
Reads incoming Pitch Bend messages (14-bit).
> Original: `Pitch-Bend-Value.ino`
```cpp
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include <cs_midi.h>
using namespace cs;
BluetoothMIDI_Interface midi;
PBValue pbVal {Channel_1};
int main() {
stdio_init_all();
if (cyw43_arch_init()) return 1;
Control_Surface.begin();
while (true) {
Control_Surface.loop();
uint16_t val = pbVal.getValue();
(void)val;
sleep_ms(1);
}
}
```

View File

@ -0,0 +1,27 @@
# NoteValue
Reads incoming Note On values (8-bit).
```cpp
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include <cs_midi.h>
using namespace cs;
BluetoothMIDI_Interface midi;
NoteValue noteVal {{MIDI_Notes::C[4], Channel_1}};
int main() {
stdio_init_all();
if (cyw43_arch_init()) return 1;
Control_Surface.begin();
while (true) {
Control_Surface.loop();
uint8_t val = noteVal.getValue();
(void)val;
sleep_ms(1);
}
}
```

View File

@ -0,0 +1,27 @@
# KPValue
Reads incoming Key Pressure (aftertouch) values.
```cpp
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include <cs_midi.h>
using namespace cs;
BluetoothMIDI_Interface midi;
KPValue kpVal {{MIDI_Notes::C[4], Channel_1}};
int main() {
stdio_init_all();
if (cyw43_arch_init()) return 1;
Control_Surface.begin();
while (true) {
Control_Surface.loop();
uint8_t val = kpVal.getValue();
(void)val;
sleep_ms(1);
}
}
```

View File

@ -0,0 +1,28 @@
# CCLED
LED responds to incoming CC messages.
```cpp
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include <cs_midi.h>
using namespace cs;
BluetoothMIDI_Interface midi;
CCLED led {
13,
{MIDI_CC::General_Purpose_Controller_1, Channel_1},
};
int main() {
stdio_init_all();
if (cyw43_arch_init()) return 1;
Control_Surface.begin();
while (true) {
Control_Surface.loop();
sleep_ms(1);
}
}
```

View File

@ -0,0 +1,29 @@
# NoteRange
Reads a contiguous range of incoming Note values.
```cpp
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include <cs_midi.h>
using namespace cs;
BluetoothMIDI_Interface midi;
NoteRange<4> noteRange {{MIDI_Notes::C[4], Channel_1}};
int main() {
stdio_init_all();
if (cyw43_arch_init()) return 1;
Control_Surface.begin();
while (true) {
Control_Surface.loop();
for (uint8_t i = 0; i < 4; i++) {
uint8_t val = noteRange.getValue(i);
(void)val;
}
sleep_ms(1);
}
}
```

View File

@ -0,0 +1,29 @@
# CCRange
Reads a contiguous range of incoming CC values.
```cpp
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include <cs_midi.h>
using namespace cs;
BluetoothMIDI_Interface midi;
CCRange<8> ccRange {{MIDI_CC::General_Purpose_Controller_1, Channel_1}};
int main() {
stdio_init_all();
if (cyw43_arch_init()) return 1;
Control_Surface.begin();
while (true) {
Control_Surface.loop();
for (uint8_t i = 0; i < 8; i++) {
uint8_t val = ccRange.getValue(i);
(void)val;
}
sleep_ms(1);
}
}
```

View File

@ -0,0 +1,3 @@
# MIDI Input Elements
Input elements receive MIDI messages and drive outputs (LEDs, stored values).

View File

@ -0,0 +1,26 @@
# BLE MIDI
Basic Bluetooth Low Energy MIDI interface setup. Replaces `USBMIDI_Interface` from the original library.
```cpp
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include <cs_midi.h>
using namespace cs;
BluetoothMIDI_Interface midi;
NoteButton button {5, {MIDI_Notes::C[4], Channel_1}};
CCRotaryEncoder enc {{0, 2}, {16, Channel_1}, 1, 4};
int main() {
stdio_init_all();
if (cyw43_arch_init()) return 1;
Control_Surface.begin();
while (true) {
Control_Surface.loop();
sleep_ms(1);
}
}
```

View File

@ -0,0 +1,27 @@
# MIDI Pipes
Routing between MIDI interfaces using pipe operators.
> Original: `MIDI_Pipes-Routing.ino`
```cpp
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include <cs_midi.h>
using namespace cs;
BluetoothMIDI_Interface ble;
NoteButton button {5, {MIDI_Notes::C[4], Channel_1}};
int main() {
stdio_init_all();
if (cyw43_arch_init()) return 1;
Control_Surface.begin();
while (true) {
Control_Surface.loop();
sleep_ms(1);
}
}
```

View File

@ -0,0 +1,3 @@
# MIDI Interfaces
Interface setup and routing between MIDI transports.

View File

@ -0,0 +1,53 @@
# Bank with CC Values
Bank switching with bankable input elements. Bank switching changes the CC address listened to.
> Original: `Bank.ino` (adapted)
```cpp
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include <cs_midi.h>
using namespace cs;
BluetoothMIDI_Interface midi;
// 4 banks, 2 tracks per bank
Bank<4> bank(2);
// Selector: two buttons to increment/decrement the active bank
IncrementDecrementSelector<4> selector {
bank,
{2, 3},
Wrap::Wrap,
};
// Bankable CC value readers — bank switching changes the CC address.
// Bank 1: reads CC 16, CC 17
// Bank 2: reads CC 18, CC 19
// Bank 3: reads CC 20, CC 21
// Bank 4: reads CC 22, CC 23
Bankable::CCValue<4> ccVal1 {
{bank, BankType::ChangeAddress},
{16, Channel_1},
};
Bankable::CCValue<4> ccVal2 {
{bank, BankType::ChangeAddress},
{17, Channel_1},
};
int main() {
stdio_init_all();
if (cyw43_arch_init()) return 1;
Control_Surface.begin();
while (true) {
Control_Surface.loop();
uint8_t v1 = ccVal1.getValue();
uint8_t v2 = ccVal2.getValue();
(void)v1;
(void)v2;
sleep_ms(1);
}
}
```

View File

@ -0,0 +1,35 @@
# Transposer
Note transposition via increment/decrement buttons.
> Original: `Transposer.ino`
```cpp
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include <cs_midi.h>
using namespace cs;
BluetoothMIDI_Interface midi;
Transposer<-12, +12> transposer;
NoteButton button {5, {MIDI_Notes::C[4], Channel_1}};
IncrementDecrementSelector<25> selector {
transposer,
{2, 3},
Wrap::Clamp,
};
int main() {
stdio_init_all();
if (cyw43_arch_init()) return 1;
Control_Surface.begin();
while (true) {
Control_Surface.loop();
sleep_ms(1);
}
}
```

View File

@ -0,0 +1,39 @@
# EncoderSelector
Bank selection via rotary encoder.
```cpp
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include <cs_midi.h>
using namespace cs;
BluetoothMIDI_Interface midi;
Bank<4> bank(2);
Bankable::CCValue<4> ccVal {
{bank, BankType::ChangeAddress},
{16, Channel_1},
};
EncoderSelector<4> selector {
bank,
{0, 2},
4,
Wrap::Wrap,
};
int main() {
stdio_init_all();
if (cyw43_arch_init()) return 1;
Control_Surface.begin();
while (true) {
Control_Surface.loop();
uint8_t val = ccVal.getValue();
(void)val;
sleep_ms(1);
}
}
```

View File

@ -0,0 +1,37 @@
# ManyButtonsSelector
One button per bank for direct bank selection.
```cpp
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include <cs_midi.h>
using namespace cs;
BluetoothMIDI_Interface midi;
Bank<4> bank(2);
Bankable::CCValue<4> ccVal {
{bank, BankType::ChangeAddress},
{16, Channel_1},
};
ManyButtonsSelector<4> selector {
bank,
{2, 3, 4, 5},
};
int main() {
stdio_init_all();
if (cyw43_arch_init()) return 1;
Control_Surface.begin();
while (true) {
Control_Surface.loop();
uint8_t val = ccVal.getValue();
(void)val;
sleep_ms(1);
}
}
```

View File

@ -0,0 +1,37 @@
# SwitchSelector
Two-state bank selection from a toggle switch.
```cpp
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include <cs_midi.h>
using namespace cs;
BluetoothMIDI_Interface midi;
Bank<2> bank(1);
Bankable::CCValue<2> ccVal {
{bank, BankType::ChangeAddress},
{16, Channel_1},
};
SwitchSelector selector {
bank,
5,
};
int main() {
stdio_init_all();
if (cyw43_arch_init()) return 1;
Control_Surface.begin();
while (true) {
Control_Surface.loop();
uint8_t val = ccVal.getValue();
(void)val;
sleep_ms(1);
}
}
```

View File

@ -0,0 +1,37 @@
# ProgramChangeSelector
Bank selection triggered by incoming MIDI Program Change messages.
```cpp
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include <cs_midi.h>
using namespace cs;
BluetoothMIDI_Interface midi;
Bank<8> bank(1);
Bankable::CCValue<8> ccVal {
{bank, BankType::ChangeAddress},
{16, Channel_1},
};
ProgramChangeSelector<8> selector {
bank,
{Channel_1, Cable_1},
};
int main() {
stdio_init_all();
if (cyw43_arch_init()) return 1;
Control_Surface.begin();
while (true) {
Control_Surface.loop();
uint8_t val = ccVal.getValue();
(void)val;
sleep_ms(1);
}
}
```

View File

@ -0,0 +1,37 @@
# Bankable NoteLED
Bank-switched LED responds to different notes per bank.
```cpp
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include <cs_midi.h>
using namespace cs;
BluetoothMIDI_Interface midi;
Bank<4> bank(1);
IncrementDecrementSelector<4> selector {
bank,
{2, 3},
Wrap::Wrap,
};
Bankable::NoteLED<4> led {
{bank, BankType::ChangeAddress},
13,
{MIDI_Notes::C[4], Channel_1},
};
int main() {
stdio_init_all();
if (cyw43_arch_init()) return 1;
Control_Surface.begin();
while (true) {
Control_Surface.loop();
sleep_ms(1);
}
}
```

View File

@ -0,0 +1,3 @@
# Banks and Selectors
Bank switching allows a single physical control to address multiple MIDI parameters.

3
docs/examples/_index.md Normal file
View File

@ -0,0 +1,3 @@
# Examples
Each example is a 1:1 adaptation of the corresponding [Control Surface example](https://tttapa.github.io/Control-Surface/Doxygen/d4/de9/examples.html), rewritten for pico-sdk conventions. All examples are compile-verified.

87
docs/roadmap.md Normal file
View File

@ -0,0 +1,87 @@
# cs-midi Roadmap
Extraction status of [tttapa/Control-Surface](https://github.com/tttapa/Control-Surface) (GPL-3.0) into cs-midi for pico-sdk + BTstack.
## Implemented
### MIDI Output Elements
- [x] `NoteButton` — momentary note on/off
- [x] `NoteButtons<N>` — multiple sequential note buttons
- [x] `NoteButtonLatched` — toggle note on first press, off second
- [x] `NoteButtonLatching` — note on while held, toggles state
- [x] `NoteButtonMatrix<R,C>` — row/column scanned note grid
- [x] `NoteChordButton` — plays chord on press
- [x] `CCButton` — momentary CC 127/0
- [x] `CCButtons<N>` — multiple sequential CC buttons
- [x] `CCButtonLatched` — toggle CC
- [x] `CCButtonLatching` — latching CC
- [x] `CCButtonMatrix<R,C>` — row/column scanned CC grid
- [x] `CCPotentiometer` — analog input to CC 0-127
- [x] `PBPotentiometer` — analog input to 14-bit Pitch Bend
- [x] `CCRotaryEncoder` — relative CC encoder
- [x] `CCAbsoluteEncoder` — absolute CC encoder
- [x] `PBAbsoluteEncoder` — absolute Pitch Bend encoder
- [x] `CCIncrementDecrementButtons` — inc/dec CC with reset
- [x] `PCButton` — Program Change on press
- [x] `ProgramChanger<N>` — bank-selectable program changes
### MIDI Input Elements
- [x] `NoteValue` / `CCValue` / `KPValue` — 8-bit value readers
- [x] `PBValue` — 14-bit Pitch Bend reader
- [x] `NoteRange<N>` / `CCRange<N>` / `KPRange<N>` — range readers
- [x] `NoteLED` / `CCLED` / `KPLED` — LED output from MIDI input
- [x] All bankable variants (`Bankable::NoteValue<N>`, etc.)
### Banks & Selectors
- [x] `Bank<N>` — groups bankable elements, manages bank switching
- [x] `Transposer<Min,Max>` — specialized bank for note transposition
- [x] `EncoderSelector<N>` — rotary encoder bank selection
- [x] `IncrementDecrementSelector<N>` — two-button bank selection
- [x] `IncrementSelector<N>` — single-button wrapping selector
- [x] `ManyButtonsSelector<N>` — direct bank selection per button
- [x] `SwitchSelector` — two-state toggle selector
- [x] `ProgramChangeSelector<N>` — MIDI-driven bank selection
### MIDI Interfaces
- [x] `BluetoothMIDI_Interface` — BLE MIDI via BTstack (pico-native)
- [x] `MIDI_Pipe` / `MIDI_PipeFactory<N>` / `BidirectionalMIDI_PipeFactory<N>`
- [x] `Control_Surface` singleton — declarative begin/loop lifecycle
### Infrastructure
- [x] `AHEncoder` — pico-native interrupt-driven rotary encoder
- [x] `ExtendedInputOutput` — GPIO abstraction (direct pins)
- [x] MIDI constants (notes, CC numbers, PC programs, chords/intervals)
- [x] Platform shim (`pico_shim.h`) — replaces Arduino runtime
## Not Yet Implemented
### Bankable Output Elements
- [ ] `Bankable::NoteButton` / `Bankable::CCButton` / etc.
- [ ] `Bankable::CCPotentiometer` / `Bankable::PBPotentiometer`
- [ ] `Bankable::CCRotaryEncoder` / `Bankable::CCAbsoluteEncoder`
- [ ] `Bankable::NoteChordButton` (with Transposer)
- [ ] `Bankable::NoteButtonMatrix` / `Bankable::CCButtonMatrix`
*Blocked Phase 11 of the extraction. Required for Bank.ino and Transposer.ino
output-side examples.*
### Additional MIDI Interfaces
- [ ] USB MIDI Interface (TinyUSB)
- [ ] Serial/Hardware MIDI Interface
- [ ] Debug MIDI Interface (serial monitor output)
### Display Elements
- [ ] OLED display support
- [ ] VU meter elements
- [ ] NoteLEDBar
### Hardware Expansion
- [ ] MCP23017 I2C I/O expander
- [ ] MAX7219 LED driver
- [ ] Shift register chains (74HC595, etc.)
- [ ] FastLED / NeoPixel integration
### Advanced Features
- [ ] MIDI input fine-grained callbacks (per-message-type)
- [ ] SysEx send/receive helpers
- [ ] AppleMIDI (RTP-MIDI over WiFi)