Added tests and updated Documentation
This commit is contained in:
parent
782ff440f0
commit
517cbbb318
|
|
@ -1,6 +1,12 @@
|
||||||
cmake_minimum_required(VERSION 3.13)
|
cmake_minimum_required(VERSION 3.13)
|
||||||
|
|
||||||
add_library(cs_midi STATIC
|
# Interface selection — consumers set these before add_subdirectory()
|
||||||
|
option(CS_MIDI_BLE "Enable BLE MIDI interface (BTstack)" ON)
|
||||||
|
option(CS_MIDI_USB "Enable USB MIDI interface (TinyUSB)" OFF)
|
||||||
|
option(CS_MIDI_SERIAL "Enable Serial MIDI interface (UART)" OFF)
|
||||||
|
|
||||||
|
# Core sources — always compiled
|
||||||
|
set(CS_MIDI_CORE_SOURCES
|
||||||
AH/Debug/Debug.cpp
|
AH/Debug/Debug.cpp
|
||||||
AH/Error/Exit.cpp
|
AH/Error/Exit.cpp
|
||||||
AH/PrintStream/PrintStream.cpp
|
AH/PrintStream/PrintStream.cpp
|
||||||
|
|
@ -21,27 +27,65 @@ add_library(cs_midi STATIC
|
||||||
MIDI_Parsers/MIDI_MessageTypes.cpp
|
MIDI_Parsers/MIDI_MessageTypes.cpp
|
||||||
MIDI_Interfaces/MIDI_Interface.cpp
|
MIDI_Interfaces/MIDI_Interface.cpp
|
||||||
MIDI_Interfaces/MIDI_Pipes.cpp
|
MIDI_Interfaces/MIDI_Pipes.cpp
|
||||||
MIDI_Interfaces/BLEMIDI/BLEMIDIPacketBuilder.cpp
|
|
||||||
MIDI_Interfaces/BLEMIDI/BTstack/gatt_midi.cpp
|
|
||||||
MIDI_Interfaces/BLEMIDI/BTstack/advertising.cpp
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
set(CS_MIDI_SOURCES ${CS_MIDI_CORE_SOURCES})
|
||||||
|
|
||||||
|
if(CS_MIDI_BLE)
|
||||||
|
list(APPEND CS_MIDI_SOURCES
|
||||||
|
MIDI_Interfaces/BLEMIDI/BLEMIDIPacketBuilder.cpp
|
||||||
|
MIDI_Interfaces/BLEMIDI/BTstack/gatt_midi.cpp
|
||||||
|
MIDI_Interfaces/BLEMIDI/BTstack/advertising.cpp
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(CS_MIDI_USB)
|
||||||
|
list(APPEND CS_MIDI_SOURCES
|
||||||
|
MIDI_Parsers/USBMIDI_Parser.cpp
|
||||||
|
MIDI_Interfaces/USBMIDI_Interface.cpp
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(CS_MIDI_SERIAL)
|
||||||
|
list(APPEND CS_MIDI_SOURCES
|
||||||
|
MIDI_Interfaces/SerialMIDI_Interface.cpp
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_library(cs_midi STATIC ${CS_MIDI_SOURCES})
|
||||||
|
|
||||||
target_include_directories(cs_midi PUBLIC
|
target_include_directories(cs_midi PUBLIC
|
||||||
${CMAKE_CURRENT_LIST_DIR}
|
${CMAKE_CURRENT_LIST_DIR}
|
||||||
)
|
)
|
||||||
|
|
||||||
target_compile_definitions(cs_midi PUBLIC
|
target_compile_definitions(cs_midi PUBLIC
|
||||||
MIDI_NUM_CABLES=1
|
MIDI_NUM_CABLES=1
|
||||||
|
$<$<BOOL:${CS_MIDI_BLE}>:CS_MIDI_BLE=1>
|
||||||
|
$<$<BOOL:${CS_MIDI_USB}>:CS_MIDI_USB=1>
|
||||||
|
$<$<BOOL:${CS_MIDI_SERIAL}>:CS_MIDI_SERIAL=1>
|
||||||
)
|
)
|
||||||
|
|
||||||
# pico_stdlib: needed by the library for millis/micros/gpio
|
|
||||||
# pico_btstack_ble: BTstack BLE headers + objects (linked PRIVATE to avoid
|
|
||||||
# propagating the full btstack source build to consumers)
|
|
||||||
# hardware_sync: save_and_disable_interrupts used by BTstackBackgroundBackend
|
|
||||||
target_link_libraries(cs_midi
|
target_link_libraries(cs_midi
|
||||||
PUBLIC pico_stdlib hardware_sync hardware_adc
|
PUBLIC pico_stdlib hardware_sync hardware_adc
|
||||||
PRIVATE pico_btstack_ble pico_btstack_cyw43
|
|
||||||
pico_cyw43_arch_lwip_threadsafe_background
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(CS_MIDI_BLE)
|
||||||
|
target_link_libraries(cs_midi
|
||||||
|
PRIVATE pico_btstack_ble pico_btstack_cyw43
|
||||||
|
pico_cyw43_arch_lwip_threadsafe_background
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(CS_MIDI_SERIAL)
|
||||||
|
target_link_libraries(cs_midi
|
||||||
|
PRIVATE hardware_uart
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(CS_MIDI_USB)
|
||||||
|
target_link_libraries(cs_midi
|
||||||
|
PRIVATE tinyusb_device tinyusb_board
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
target_compile_features(cs_midi PUBLIC cxx_std_17)
|
target_compile_features(cs_midi PUBLIC cxx_std_17)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
#include "SerialMIDI_Interface.hpp"
|
||||||
|
#include "hardware/gpio.h"
|
||||||
|
|
||||||
|
BEGIN_CS_NAMESPACE
|
||||||
|
|
||||||
|
void HardwareSerialMIDI_Interface::begin() {
|
||||||
|
uart_init(uart, MIDI_BAUD);
|
||||||
|
gpio_set_function(txPin, GPIO_FUNC_UART);
|
||||||
|
gpio_set_function(rxPin, GPIO_FUNC_UART);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HardwareSerialMIDI_Interface::update() {
|
||||||
|
MIDI_Interface::updateIncoming(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
MIDIReadEvent HardwareSerialMIDI_Interface::read() {
|
||||||
|
return parser.pull(UARTPuller{uart});
|
||||||
|
}
|
||||||
|
|
||||||
|
ChannelMessage HardwareSerialMIDI_Interface::getChannelMessage() const {
|
||||||
|
return parser.getChannelMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
SysCommonMessage HardwareSerialMIDI_Interface::getSysCommonMessage() const {
|
||||||
|
return parser.getSysCommonMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
RealTimeMessage HardwareSerialMIDI_Interface::getRealTimeMessage() const {
|
||||||
|
return parser.getRealTimeMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
SysExMessage HardwareSerialMIDI_Interface::getSysExMessage() const {
|
||||||
|
return parser.getSysExMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HardwareSerialMIDI_Interface::sendChannelMessageImpl(ChannelMessage msg) {
|
||||||
|
if (msg.hasTwoDataBytes()) {
|
||||||
|
uint8_t buf[3] = {msg.header, msg.data1, msg.data2};
|
||||||
|
uart_write_blocking(uart, buf, 3);
|
||||||
|
} else {
|
||||||
|
uint8_t buf[2] = {msg.header, msg.data1};
|
||||||
|
uart_write_blocking(uart, buf, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HardwareSerialMIDI_Interface::sendSysCommonImpl(SysCommonMessage msg) {
|
||||||
|
uint8_t ndata = msg.getNumberOfDataBytes();
|
||||||
|
if (ndata == 2) {
|
||||||
|
uint8_t buf[3] = {msg.header, msg.data1, msg.data2};
|
||||||
|
uart_write_blocking(uart, buf, 3);
|
||||||
|
} else if (ndata == 1) {
|
||||||
|
uint8_t buf[2] = {msg.header, msg.data1};
|
||||||
|
uart_write_blocking(uart, buf, 2);
|
||||||
|
} else {
|
||||||
|
uart_write_blocking(uart, &msg.header, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HardwareSerialMIDI_Interface::sendSysExImpl(SysExMessage msg) {
|
||||||
|
uart_write_blocking(uart, msg.data, msg.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HardwareSerialMIDI_Interface::sendRealTimeImpl(RealTimeMessage msg) {
|
||||||
|
uart_write_blocking(uart, &msg.message, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
END_CS_NAMESPACE
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "MIDI_Interface.hpp"
|
||||||
|
#include <MIDI_Parsers/SerialMIDI_Parser.hpp>
|
||||||
|
|
||||||
|
#include "hardware/uart.h"
|
||||||
|
|
||||||
|
BEGIN_CS_NAMESPACE
|
||||||
|
|
||||||
|
struct UARTPuller {
|
||||||
|
uart_inst_t *uart;
|
||||||
|
bool pull(uint8_t &byte) {
|
||||||
|
if (!uart_is_readable(uart))
|
||||||
|
return false;
|
||||||
|
byte = uart_getc(uart);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class HardwareSerialMIDI_Interface : public MIDI_Interface {
|
||||||
|
public:
|
||||||
|
HardwareSerialMIDI_Interface(uart_inst_t *uart, uint tx_pin, uint rx_pin)
|
||||||
|
: uart(uart), txPin(tx_pin), rxPin(rx_pin) {}
|
||||||
|
|
||||||
|
void begin() override;
|
||||||
|
void update() override;
|
||||||
|
|
||||||
|
MIDIReadEvent read();
|
||||||
|
ChannelMessage getChannelMessage() const;
|
||||||
|
SysCommonMessage getSysCommonMessage() const;
|
||||||
|
RealTimeMessage getRealTimeMessage() const;
|
||||||
|
SysExMessage getSysExMessage() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void sendChannelMessageImpl(ChannelMessage msg) override;
|
||||||
|
void sendSysCommonImpl(SysCommonMessage msg) override;
|
||||||
|
void sendSysExImpl(SysExMessage msg) override;
|
||||||
|
void sendRealTimeImpl(RealTimeMessage msg) override;
|
||||||
|
void sendNowImpl() override {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
#if !DISABLE_PIPES
|
||||||
|
void handleStall() override { MIDI_Interface::handleStall(this); }
|
||||||
|
#ifdef DEBUG_OUT
|
||||||
|
const char *getName() const override { return "serial"; }
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
SerialMIDI_Parser parser;
|
||||||
|
uart_inst_t *uart;
|
||||||
|
uint txPin;
|
||||||
|
uint rxPin;
|
||||||
|
};
|
||||||
|
|
||||||
|
END_CS_NAMESPACE
|
||||||
|
|
@ -0,0 +1,112 @@
|
||||||
|
#include "USBMIDI_Interface.hpp"
|
||||||
|
|
||||||
|
BEGIN_CS_NAMESPACE
|
||||||
|
|
||||||
|
void USBMIDI_Interface::update() {
|
||||||
|
tud_task();
|
||||||
|
MIDI_Interface::updateIncoming(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
MIDIReadEvent USBMIDI_Interface::read() {
|
||||||
|
return parser.pull(TinyUSBPuller{cableNum});
|
||||||
|
}
|
||||||
|
|
||||||
|
ChannelMessage USBMIDI_Interface::getChannelMessage() const {
|
||||||
|
return parser.getChannelMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
SysCommonMessage USBMIDI_Interface::getSysCommonMessage() const {
|
||||||
|
return parser.getSysCommonMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
RealTimeMessage USBMIDI_Interface::getRealTimeMessage() const {
|
||||||
|
return parser.getRealTimeMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
SysExMessage USBMIDI_Interface::getSysExMessage() const {
|
||||||
|
return parser.getSysExMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map MIDI status high nibble to USB MIDI Code Index Number
|
||||||
|
MIDICodeIndexNumber USBMIDI_Interface::CIN(uint8_t status) {
|
||||||
|
if (status >= 0x80 && status < 0xF0)
|
||||||
|
return static_cast<MIDICodeIndexNumber>((status >> 4) & 0x0F);
|
||||||
|
switch (status) {
|
||||||
|
case 0xF1: // MTC Quarter Frame
|
||||||
|
case 0xF3: // Song Select
|
||||||
|
return MIDICodeIndexNumber::SystemCommon2B;
|
||||||
|
case 0xF2: // Song Position Pointer
|
||||||
|
return MIDICodeIndexNumber::SystemCommon3B;
|
||||||
|
case 0xF6: // Tune Request
|
||||||
|
case 0xF7: // SysEx End (bare)
|
||||||
|
return MIDICodeIndexNumber::SystemCommon1B;
|
||||||
|
default:
|
||||||
|
if (status >= 0xF8)
|
||||||
|
return MIDICodeIndexNumber::SingleByte;
|
||||||
|
return MIDICodeIndexNumber::MiscFunctionCodes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void USBMIDI_Interface::writePacket(uint8_t cin, uint8_t b0,
|
||||||
|
uint8_t b1, uint8_t b2) {
|
||||||
|
uint8_t packet[4] = {
|
||||||
|
static_cast<uint8_t>((cableNum << 4) | (cin & 0x0F)),
|
||||||
|
b0, b1, b2
|
||||||
|
};
|
||||||
|
tud_midi_n_packet_write(cableNum, packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
void USBMIDI_Interface::sendChannelMessageImpl(ChannelMessage msg) {
|
||||||
|
auto cin = CIN(msg.header);
|
||||||
|
if (msg.hasTwoDataBytes())
|
||||||
|
writePacket(uint8_t(cin), msg.header, msg.data1, msg.data2);
|
||||||
|
else
|
||||||
|
writePacket(uint8_t(cin), msg.header, msg.data1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void USBMIDI_Interface::sendSysCommonImpl(SysCommonMessage msg) {
|
||||||
|
auto cin = CIN(msg.header);
|
||||||
|
uint8_t ndata = msg.getNumberOfDataBytes();
|
||||||
|
if (ndata == 2)
|
||||||
|
writePacket(uint8_t(cin), msg.header, msg.data1, msg.data2);
|
||||||
|
else if (ndata == 1)
|
||||||
|
writePacket(uint8_t(cin), msg.header, msg.data1, 0);
|
||||||
|
else
|
||||||
|
writePacket(uint8_t(cin), msg.header, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void USBMIDI_Interface::sendSysExImpl(SysExMessage msg) {
|
||||||
|
const uint8_t *data = msg.data;
|
||||||
|
uint16_t remaining = msg.length;
|
||||||
|
|
||||||
|
while (remaining > 3) {
|
||||||
|
writePacket(uint8_t(MIDICodeIndexNumber::SysExStartCont),
|
||||||
|
data[0], data[1], data[2]);
|
||||||
|
data += 3;
|
||||||
|
remaining -= 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (remaining) {
|
||||||
|
case 3:
|
||||||
|
writePacket(uint8_t(MIDICodeIndexNumber::SysExEnd3B),
|
||||||
|
data[0], data[1], data[2]);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
writePacket(uint8_t(MIDICodeIndexNumber::SysExEnd2B),
|
||||||
|
data[0], data[1], 0);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
writePacket(uint8_t(MIDICodeIndexNumber::SysExEnd1B),
|
||||||
|
data[0], 0, 0);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void USBMIDI_Interface::sendRealTimeImpl(RealTimeMessage msg) {
|
||||||
|
writePacket(uint8_t(MIDICodeIndexNumber::SingleByte),
|
||||||
|
msg.message, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
END_CS_NAMESPACE
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "MIDI_Interface.hpp"
|
||||||
|
#include <MIDI_Parsers/USBMIDI_Parser.hpp>
|
||||||
|
|
||||||
|
#include "tusb.h"
|
||||||
|
|
||||||
|
BEGIN_CS_NAMESPACE
|
||||||
|
|
||||||
|
struct TinyUSBPuller {
|
||||||
|
uint8_t itf;
|
||||||
|
bool pull(USBMIDI_Parser::MIDIUSBPacket_t &packet) {
|
||||||
|
return tud_midi_n_packet_read(itf, packet.data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class USBMIDI_Interface : public MIDI_Interface {
|
||||||
|
public:
|
||||||
|
explicit USBMIDI_Interface(uint8_t cable_num = 0)
|
||||||
|
: cableNum(cable_num) {}
|
||||||
|
|
||||||
|
void begin() override {}
|
||||||
|
void update() override;
|
||||||
|
|
||||||
|
MIDIReadEvent read();
|
||||||
|
ChannelMessage getChannelMessage() const;
|
||||||
|
SysCommonMessage getSysCommonMessage() const;
|
||||||
|
RealTimeMessage getRealTimeMessage() const;
|
||||||
|
SysExMessage getSysExMessage() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void sendChannelMessageImpl(ChannelMessage msg) override;
|
||||||
|
void sendSysCommonImpl(SysCommonMessage msg) override;
|
||||||
|
void sendSysExImpl(SysExMessage msg) override;
|
||||||
|
void sendRealTimeImpl(RealTimeMessage msg) override;
|
||||||
|
void sendNowImpl() override {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
#if !DISABLE_PIPES
|
||||||
|
void handleStall() override { MIDI_Interface::handleStall(this); }
|
||||||
|
#ifdef DEBUG_OUT
|
||||||
|
const char *getName() const override { return "usb"; }
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static MIDICodeIndexNumber CIN(uint8_t status);
|
||||||
|
void writePacket(uint8_t cin, uint8_t b0, uint8_t b1, uint8_t b2);
|
||||||
|
|
||||||
|
USBMIDI_Parser parser;
|
||||||
|
uint8_t cableNum;
|
||||||
|
};
|
||||||
|
|
||||||
|
END_CS_NAMESPACE
|
||||||
|
|
@ -0,0 +1,166 @@
|
||||||
|
#include "USBMIDI_Parser.hpp"
|
||||||
|
#include <Settings/SettingsWrapper.hpp>
|
||||||
|
|
||||||
|
BEGIN_CS_NAMESPACE
|
||||||
|
|
||||||
|
MIDIReadEvent USBMIDI_Parser::handleChannelMessage(MIDIUSBPacket_t packet,
|
||||||
|
Cable cable) {
|
||||||
|
midimsg.header = packet[1];
|
||||||
|
midimsg.data1 = packet[2];
|
||||||
|
midimsg.data2 = packet[3];
|
||||||
|
midimsg.cable = cable;
|
||||||
|
return MIDIReadEvent::CHANNEL_MESSAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
MIDIReadEvent USBMIDI_Parser::handleSysExStartCont(MIDIUSBPacket_t packet,
|
||||||
|
Cable cable) {
|
||||||
|
#if !IGNORE_SYSEX
|
||||||
|
if (packet[1] == uint8_t(MIDIMessageType::SysExStart)) {
|
||||||
|
startSysEx(cable);
|
||||||
|
} else if (!receivingSysEx(cable)) {
|
||||||
|
DEBUGREF(F("No SysExStart received"));
|
||||||
|
return MIDIReadEvent::NO_MESSAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasSysExSpace(cable, 3)) {
|
||||||
|
storePacket(packet);
|
||||||
|
endSysExChunk(cable);
|
||||||
|
return MIDIReadEvent::SYSEX_CHUNK;
|
||||||
|
}
|
||||||
|
|
||||||
|
addSysExBytes(cable, &packet[1], 3);
|
||||||
|
#else
|
||||||
|
(void)packet;
|
||||||
|
(void)cable;
|
||||||
|
#endif
|
||||||
|
return MIDIReadEvent::NO_MESSAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <uint8_t NumBytes>
|
||||||
|
MIDIReadEvent USBMIDI_Parser::handleSysExEnd(MIDIUSBPacket_t packet,
|
||||||
|
Cable cable) {
|
||||||
|
static_assert(NumBytes == 2 || NumBytes == 3,
|
||||||
|
"Only 2- or 3-byte SysEx packets are supported");
|
||||||
|
|
||||||
|
#if !IGNORE_SYSEX
|
||||||
|
if (packet[1] == uint8_t(MIDIMessageType::SysExStart)) {
|
||||||
|
startSysEx(cable);
|
||||||
|
} else if (!receivingSysEx(cable)) {
|
||||||
|
DEBUGFN(F("No SysExStart received"));
|
||||||
|
return MIDIReadEvent::NO_MESSAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasSysExSpace(cable, NumBytes)) {
|
||||||
|
storePacket(packet);
|
||||||
|
endSysExChunk(cable);
|
||||||
|
return MIDIReadEvent::SYSEX_CHUNK;
|
||||||
|
}
|
||||||
|
|
||||||
|
addSysExBytes(cable, &packet[1], NumBytes);
|
||||||
|
endSysEx(cable);
|
||||||
|
return MIDIReadEvent::SYSEX_MESSAGE;
|
||||||
|
#else
|
||||||
|
(void)packet;
|
||||||
|
(void)cable;
|
||||||
|
return MIDIReadEvent::NO_MESSAGE;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
MIDIReadEvent USBMIDI_Parser::handleSysExEnd<1>(MIDIUSBPacket_t packet,
|
||||||
|
Cable cable) {
|
||||||
|
if (packet[1] != uint8_t(MIDIMessageType::SysExEnd)) {
|
||||||
|
midimsg.header = packet[1];
|
||||||
|
midimsg.cable = cable;
|
||||||
|
return MIDIReadEvent::SYSCOMMON_MESSAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !IGNORE_SYSEX
|
||||||
|
else {
|
||||||
|
if (!receivingSysEx(cable)) {
|
||||||
|
DEBUGREF(F("No SysExStart received"));
|
||||||
|
return MIDIReadEvent::NO_MESSAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasSysExSpace(cable, 1)) {
|
||||||
|
storePacket(packet);
|
||||||
|
endSysExChunk(cable);
|
||||||
|
return MIDIReadEvent::SYSEX_CHUNK;
|
||||||
|
}
|
||||||
|
|
||||||
|
addSysExByte(cable, packet[1]);
|
||||||
|
endSysEx(cable);
|
||||||
|
return MIDIReadEvent::SYSEX_MESSAGE;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
(void)packet;
|
||||||
|
(void)cable;
|
||||||
|
return MIDIReadEvent::NO_MESSAGE;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
MIDIReadEvent USBMIDI_Parser::handleSysCommon(MIDIUSBPacket_t packet,
|
||||||
|
Cable cable) {
|
||||||
|
midimsg.header = packet[1];
|
||||||
|
midimsg.data1 = packet[2];
|
||||||
|
midimsg.data2 = packet[3];
|
||||||
|
midimsg.cable = cable;
|
||||||
|
return MIDIReadEvent::SYSCOMMON_MESSAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
MIDIReadEvent USBMIDI_Parser::handleSingleByte(MIDIUSBPacket_t packet,
|
||||||
|
Cable cable) {
|
||||||
|
rtmsg.message = packet[1];
|
||||||
|
rtmsg.cable = cable;
|
||||||
|
return MIDIReadEvent::REALTIME_MESSAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
MIDIReadEvent USBMIDI_Parser::feed(MIDIUSBPacket_t packet) {
|
||||||
|
Cable cable = Cable(packet[0] >> 4);
|
||||||
|
MIDICodeIndexNumber CIN = MIDICodeIndexNumber(packet[0] & 0xF);
|
||||||
|
|
||||||
|
if (cable.getRaw() >= USB_MIDI_NUMBER_OF_CABLES)
|
||||||
|
return MIDIReadEvent::NO_MESSAGE;
|
||||||
|
|
||||||
|
using M = MIDICodeIndexNumber;
|
||||||
|
switch (CIN) {
|
||||||
|
case M::MiscFunctionCodes: break;
|
||||||
|
case M::CableEvents: break;
|
||||||
|
case M::SystemCommon2B:
|
||||||
|
case M::SystemCommon3B: return handleSysCommon(packet, cable);
|
||||||
|
case M::SysExStartCont: return handleSysExStartCont(packet, cable);
|
||||||
|
case M::SysExEnd1B: return handleSysExEnd<1>(packet, cable);
|
||||||
|
case M::SysExEnd2B: return handleSysExEnd<2>(packet, cable);
|
||||||
|
case M::SysExEnd3B: return handleSysExEnd<3>(packet, cable);
|
||||||
|
case M::NoteOff:
|
||||||
|
case M::NoteOn:
|
||||||
|
case M::KeyPressure:
|
||||||
|
case M::ControlChange:
|
||||||
|
case M::ProgramChange:
|
||||||
|
case M::ChannelPressure:
|
||||||
|
case M::PitchBend: return handleChannelMessage(packet, cable);
|
||||||
|
case M::SingleByte: return handleSingleByte(packet, cable);
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return MIDIReadEvent::NO_MESSAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
MIDIReadEvent USBMIDI_Parser::resume() {
|
||||||
|
#if !IGNORE_SYSEX
|
||||||
|
if (!hasStoredPacket())
|
||||||
|
return MIDIReadEvent::NO_MESSAGE;
|
||||||
|
|
||||||
|
MIDIUSBPacket_t packet = popStoredPacket();
|
||||||
|
|
||||||
|
if (receivingSysEx(activeCable)) {
|
||||||
|
startSysEx(activeCable);
|
||||||
|
}
|
||||||
|
|
||||||
|
return feed(packet);
|
||||||
|
#else
|
||||||
|
return MIDIReadEvent::NO_MESSAGE;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
END_CS_NAMESPACE
|
||||||
24
Makefile
24
Makefile
|
|
@ -1,14 +1,22 @@
|
||||||
BUILD_DIR = tests/build
|
BUILD_DIR = build
|
||||||
|
TEST_BUILD_DIR = tests/build
|
||||||
|
|
||||||
dist-tests: $(BUILD_DIR)/Makefile
|
all: $(BUILD_DIR)/Makefile
|
||||||
@$(MAKE) -C $(BUILD_DIR) examples
|
@$(MAKE) -C $(BUILD_DIR)
|
||||||
@echo "All examples compiled successfully."
|
|
||||||
|
|
||||||
$(BUILD_DIR)/Makefile: tests/CMakeLists.txt tests/examples/CMakeLists.txt
|
$(BUILD_DIR)/Makefile: CMakeLists.txt
|
||||||
@mkdir -p $(BUILD_DIR)
|
@mkdir -p $(BUILD_DIR)
|
||||||
@cd $(BUILD_DIR) && PICO_SDK_PATH=$$HOME/Staging/pico-sdk cmake ..
|
@cd $(BUILD_DIR) && PICO_SDK_PATH=$$HOME/Staging/pico-sdk cmake ..
|
||||||
|
|
||||||
clean:
|
tests: $(TEST_BUILD_DIR)/Makefile
|
||||||
@rm -rf $(BUILD_DIR)
|
@$(MAKE) -C $(TEST_BUILD_DIR)
|
||||||
|
@echo "All examples compiled successfully."
|
||||||
|
|
||||||
.PHONY: dist-tests clean
|
$(TEST_BUILD_DIR)/Makefile: tests/CMakeLists.txt tests/examples/CMakeLists.txt CMakeLists.txt
|
||||||
|
@mkdir -p $(TEST_BUILD_DIR)
|
||||||
|
@cd $(TEST_BUILD_DIR) && PICO_SDK_PATH=$$HOME/Staging/pico-sdk cmake ..
|
||||||
|
|
||||||
|
clean:
|
||||||
|
@rm -rf $(BUILD_DIR) $(TEST_BUILD_DIR)
|
||||||
|
|
||||||
|
.PHONY: all tests clean
|
||||||
|
|
|
||||||
18
cs_midi.h
18
cs_midi.h
|
|
@ -21,10 +21,22 @@
|
||||||
#include <MIDI_Constants/Notes.hpp>
|
#include <MIDI_Constants/Notes.hpp>
|
||||||
#include <MIDI_Constants/Program_Change.hpp>
|
#include <MIDI_Constants/Program_Change.hpp>
|
||||||
|
|
||||||
// MIDI interfaces
|
// MIDI interfaces — common
|
||||||
|
#include <MIDI_Interfaces/MIDI_Pipes.hpp>
|
||||||
|
|
||||||
|
// MIDI interfaces — transport-specific
|
||||||
|
#ifdef CS_MIDI_BLE
|
||||||
#include <MIDI_Interfaces/GenericBLEMIDI_Interface.hpp>
|
#include <MIDI_Interfaces/GenericBLEMIDI_Interface.hpp>
|
||||||
#include <MIDI_Interfaces/BLEMIDI/BTstackBackgroundBackend.hpp>
|
#include <MIDI_Interfaces/BLEMIDI/BTstackBackgroundBackend.hpp>
|
||||||
#include <MIDI_Interfaces/MIDI_Pipes.hpp>
|
#endif
|
||||||
|
|
||||||
|
#ifdef CS_MIDI_USB
|
||||||
|
#include <MIDI_Interfaces/USBMIDI_Interface.hpp>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CS_MIDI_SERIAL
|
||||||
|
#include <MIDI_Interfaces/SerialMIDI_Interface.hpp>
|
||||||
|
#endif
|
||||||
|
|
||||||
// Control Surface singleton
|
// Control Surface singleton
|
||||||
#include <Control_Surface/Control_Surface_Class.hpp>
|
#include <Control_Surface/Control_Surface_Class.hpp>
|
||||||
|
|
@ -72,7 +84,9 @@
|
||||||
|
|
||||||
BEGIN_CS_NAMESPACE
|
BEGIN_CS_NAMESPACE
|
||||||
|
|
||||||
|
#ifdef CS_MIDI_BLE
|
||||||
using BluetoothMIDI_Interface =
|
using BluetoothMIDI_Interface =
|
||||||
GenericBLEMIDI_Interface<BTstackBackgroundBackend>;
|
GenericBLEMIDI_Interface<BTstackBackgroundBackend>;
|
||||||
|
#endif
|
||||||
|
|
||||||
END_CS_NAMESPACE
|
END_CS_NAMESPACE
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
// tusb_config.h — Minimal TinyUSB configuration for USB MIDI.
|
||||||
|
// Copy this file into your project root (alongside btstack_config.h).
|
||||||
|
// Adjust VID/PID and descriptor strings in usb_descriptors.c.
|
||||||
|
|
||||||
|
#ifndef _TUSB_CONFIG_H_
|
||||||
|
#define _TUSB_CONFIG_H_
|
||||||
|
|
||||||
|
#define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE
|
||||||
|
|
||||||
|
#define CFG_TUD_MIDI 1
|
||||||
|
#define CFG_TUD_MIDI_RX_BUFSIZE 64
|
||||||
|
#define CFG_TUD_MIDI_TX_BUFSIZE 64
|
||||||
|
|
||||||
|
// Disable unused device classes
|
||||||
|
#define CFG_TUD_CDC 0
|
||||||
|
#define CFG_TUD_MSC 0
|
||||||
|
#define CFG_TUD_HID 0
|
||||||
|
#define CFG_TUD_VENDOR 0
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
// usb_descriptors.c — USB MIDI device descriptors for TinyUSB.
|
||||||
|
// Copy this file into your project and adjust VID, PID, and strings.
|
||||||
|
|
||||||
|
#include "tusb.h"
|
||||||
|
|
||||||
|
#define USB_VID 0xCafe
|
||||||
|
#define USB_PID 0x4001
|
||||||
|
#define USB_BCD 0x0200
|
||||||
|
|
||||||
|
// Device descriptor
|
||||||
|
tusb_desc_device_t const desc_device = {
|
||||||
|
.bLength = sizeof(tusb_desc_device_t),
|
||||||
|
.bDescriptorType = TUSB_DESC_DEVICE,
|
||||||
|
.bcdUSB = USB_BCD,
|
||||||
|
.bDeviceClass = 0x00,
|
||||||
|
.bDeviceSubClass = 0x00,
|
||||||
|
.bDeviceProtocol = 0x00,
|
||||||
|
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
|
||||||
|
.idVendor = USB_VID,
|
||||||
|
.idProduct = USB_PID,
|
||||||
|
.bcdDevice = 0x0100,
|
||||||
|
.iManufacturer = 0x01,
|
||||||
|
.iProduct = 0x02,
|
||||||
|
.iSerialNumber = 0x03,
|
||||||
|
.bNumConfigurations = 0x01
|
||||||
|
};
|
||||||
|
|
||||||
|
uint8_t const *tud_descriptor_device_cb(void) {
|
||||||
|
return (uint8_t const *)&desc_device;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configuration descriptor (Audio + MIDI)
|
||||||
|
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_MIDI_DESC_LEN)
|
||||||
|
|
||||||
|
uint8_t const desc_configuration[] = {
|
||||||
|
TUD_CONFIG_DESCRIPTOR(1, 2, 0, CONFIG_TOTAL_LEN,
|
||||||
|
0x00, 100),
|
||||||
|
TUD_MIDI_DESCRIPTOR(1, 0, 0x01, 0x81, 64)
|
||||||
|
};
|
||||||
|
|
||||||
|
uint8_t const *tud_descriptor_configuration_cb(uint8_t index) {
|
||||||
|
(void)index;
|
||||||
|
return desc_configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
// String descriptors
|
||||||
|
static char const *string_desc_arr[] = {
|
||||||
|
(const char[]){0x09, 0x04}, // 0: English
|
||||||
|
"cs-midi", // 1: Manufacturer
|
||||||
|
"MIDI Controller", // 2: Product
|
||||||
|
"000001", // 3: Serial
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint16_t _desc_str[32 + 1];
|
||||||
|
|
||||||
|
uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
|
||||||
|
(void)langid;
|
||||||
|
uint8_t chr_count;
|
||||||
|
|
||||||
|
if (index == 0) {
|
||||||
|
memcpy(&_desc_str[1], string_desc_arr[0], 2);
|
||||||
|
chr_count = 1;
|
||||||
|
} else {
|
||||||
|
if (index >= sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))
|
||||||
|
return NULL;
|
||||||
|
const char *str = string_desc_arr[index];
|
||||||
|
chr_count = (uint8_t)strlen(str);
|
||||||
|
if (chr_count > 31) chr_count = 31;
|
||||||
|
for (uint8_t i = 0; i < chr_count; i++)
|
||||||
|
_desc_str[1 + i] = str[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
_desc_str[0] = (uint16_t)((TUSB_DESC_STRING << 8) | (2 * chr_count + 2));
|
||||||
|
return _desc_str;
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
# Standalone build for cs-midi examples — compile-only verification.
|
# Standalone build for cs-midi examples — compile-only verification.
|
||||||
# Invoked via: make dist-tests (from lib/cs-midi/)
|
|
||||||
|
|
||||||
set(PICO_BOARD pico2_w)
|
set(PICO_BOARD pico2_w)
|
||||||
|
|
||||||
|
|
@ -13,9 +12,12 @@ set(CMAKE_CXX_STANDARD 17)
|
||||||
|
|
||||||
pico_sdk_init()
|
pico_sdk_init()
|
||||||
|
|
||||||
|
set(CS_MIDI_BLE ON CACHE BOOL "" FORCE)
|
||||||
|
set(CS_MIDI_USB ON CACHE BOOL "" FORCE)
|
||||||
|
set(CS_MIDI_SERIAL ON CACHE BOOL "" FORCE)
|
||||||
|
|
||||||
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/.. ${CMAKE_CURRENT_BINARY_DIR}/cs_midi)
|
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/.. ${CMAKE_CURRENT_BINARY_DIR}/cs_midi)
|
||||||
|
|
||||||
# cs_midi's BTstack sources need btstack_config.h from this directory
|
|
||||||
target_include_directories(cs_midi PRIVATE ${CMAKE_CURRENT_LIST_DIR})
|
target_include_directories(cs_midi PRIVATE ${CMAKE_CURRENT_LIST_DIR})
|
||||||
|
|
||||||
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/examples ${CMAKE_CURRENT_BINARY_DIR}/examples)
|
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/examples ${CMAKE_CURRENT_BINARY_DIR}/examples)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,42 @@
|
||||||
# Build each example as a standalone executable to verify compilation.
|
# Compile-verify all examples as a single OBJECT library.
|
||||||
# Invoked via: make dist-tests (from lib/cs-midi/)
|
# Each .cpp compiles independently — no linking, no main() conflicts.
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# pico_deps — aggregates all pico-sdk and cs_midi include directories
|
||||||
|
# and compile definitions into one STATIC library. Everything links PRIVATE
|
||||||
|
# so INTERFACE_SOURCES compile only once here. Examples then copy the
|
||||||
|
# resolved includes/defs via generator expressions (no target_link_libraries
|
||||||
|
# on the OBJECT library, so no INTERFACE_SOURCES leak into it).
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
|
||||||
|
add_library(pico_deps STATIC pico_deps_stub.c)
|
||||||
|
|
||||||
|
target_include_directories(pico_deps PUBLIC
|
||||||
|
${CMAKE_SOURCE_DIR}
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(pico_deps PRIVATE pico_stdlib hardware_adc hardware_sync cs_midi)
|
||||||
|
|
||||||
|
if(CS_MIDI_BLE)
|
||||||
|
target_link_libraries(pico_deps PRIVATE
|
||||||
|
pico_cyw43_arch_lwip_threadsafe_background
|
||||||
|
pico_btstack_ble
|
||||||
|
pico_btstack_cyw43
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(CS_MIDI_USB)
|
||||||
|
target_link_libraries(pico_deps PRIVATE tinyusb_device tinyusb_board)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(CS_MIDI_SERIAL)
|
||||||
|
target_link_libraries(pico_deps PRIVATE hardware_uart)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Example sources
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
|
||||||
set(EXAMPLE_SOURCES
|
set(EXAMPLE_SOURCES
|
||||||
# Output elements
|
# Output elements
|
||||||
|
|
@ -50,21 +87,31 @@ set(EXAMPLE_SOURCES
|
||||||
banks/bankable_note_led.cpp
|
banks/bankable_note_led.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
foreach(src ${EXAMPLE_SOURCES})
|
if(CS_MIDI_USB)
|
||||||
get_filename_component(name ${src} NAME_WE)
|
list(APPEND EXAMPLE_SOURCES
|
||||||
set(target "example_${name}")
|
interfaces/usb_midi.cpp
|
||||||
add_executable(${target} ${src})
|
usb_descriptors.c
|
||||||
target_include_directories(${target} PRIVATE ${CMAKE_SOURCE_DIR})
|
|
||||||
target_link_libraries(${target}
|
|
||||||
pico_stdlib
|
|
||||||
pico_cyw43_arch_lwip_threadsafe_background
|
|
||||||
pico_btstack_ble
|
|
||||||
pico_btstack_cyw43
|
|
||||||
hardware_adc
|
|
||||||
cs_midi
|
|
||||||
)
|
)
|
||||||
set_target_properties(${target} PROPERTIES EXCLUDE_FROM_ALL TRUE)
|
endif()
|
||||||
list(APPEND EXAMPLE_TARGETS ${target})
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
add_custom_target(examples DEPENDS ${EXAMPLE_TARGETS})
|
if(CS_MIDI_SERIAL)
|
||||||
|
list(APPEND EXAMPLE_SOURCES interfaces/serial_midi.cpp)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(CS_MIDI_BLE AND CS_MIDI_USB)
|
||||||
|
list(APPEND EXAMPLE_SOURCES interfaces/dual_midi.cpp)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# OBJECT library — compile only, no linking.
|
||||||
|
# Uses resolved includes/defs from pico_deps directly to avoid
|
||||||
|
# pico-sdk INTERFACE_SOURCES being injected into this target.
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
|
||||||
|
add_library(examples OBJECT ${EXAMPLE_SOURCES})
|
||||||
|
target_include_directories(examples PRIVATE
|
||||||
|
$<TARGET_PROPERTY:pico_deps,INCLUDE_DIRECTORIES>
|
||||||
|
)
|
||||||
|
target_compile_definitions(examples PRIVATE
|
||||||
|
$<TARGET_PROPERTY:pico_deps,COMPILE_DEFINITIONS>
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
// Dual MIDI — BLE + USB with pipe routing.
|
||||||
|
// Elements send to both interfaces simultaneously.
|
||||||
|
|
||||||
|
#include "pico/stdlib.h"
|
||||||
|
#include "pico/cyw43_arch.h"
|
||||||
|
#include "tusb.h"
|
||||||
|
#include <cs_midi.h>
|
||||||
|
|
||||||
|
using namespace cs;
|
||||||
|
|
||||||
|
BluetoothMIDI_Interface ble;
|
||||||
|
USBMIDI_Interface usb;
|
||||||
|
|
||||||
|
MIDI_PipeFactory<2> pipes;
|
||||||
|
|
||||||
|
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;
|
||||||
|
tusb_init();
|
||||||
|
|
||||||
|
Control_Surface >> pipes >> ble;
|
||||||
|
Control_Surface >> pipes >> usb;
|
||||||
|
|
||||||
|
Control_Surface.begin();
|
||||||
|
while (true) {
|
||||||
|
Control_Surface.loop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
// Serial MIDI — classic 5-pin DIN MIDI over UART at 31250 baud.
|
||||||
|
// Uses hardware UART0 with TX on GPIO 12, RX on GPIO 13.
|
||||||
|
|
||||||
|
#include "pico/stdlib.h"
|
||||||
|
#include <cs_midi.h>
|
||||||
|
|
||||||
|
using namespace cs;
|
||||||
|
|
||||||
|
HardwareSerialMIDI_Interface midi {uart0, 12, 13};
|
||||||
|
|
||||||
|
CCRotaryEncoder enc {{0, 2}, {16, Channel_1}, 1, 4};
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
stdio_init_all();
|
||||||
|
Control_Surface.begin();
|
||||||
|
while (true) {
|
||||||
|
Control_Surface.loop();
|
||||||
|
sleep_ms(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
// USB MIDI — basic USB MIDI interface with TinyUSB.
|
||||||
|
// Requires tusb_config.h and usb_descriptors.c in the project.
|
||||||
|
// See lib/cs-midi/templates/ for reference files.
|
||||||
|
|
||||||
|
#include "pico/stdlib.h"
|
||||||
|
#include "tusb.h"
|
||||||
|
#include <cs_midi.h>
|
||||||
|
|
||||||
|
using namespace cs;
|
||||||
|
|
||||||
|
USBMIDI_Interface midi;
|
||||||
|
|
||||||
|
NoteButton button {5, {MIDI_Notes::C[4], Channel_1}};
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
stdio_init_all();
|
||||||
|
tusb_init();
|
||||||
|
Control_Surface.begin();
|
||||||
|
while (true) {
|
||||||
|
Control_Surface.loop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
// Stub — pico_deps collects pico-sdk INTERFACE sources into one static lib.
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
// Minimal USB MIDI descriptors for compile-verification of examples.
|
||||||
|
|
||||||
|
#include "tusb.h"
|
||||||
|
|
||||||
|
tusb_desc_device_t const desc_device = {
|
||||||
|
.bLength = sizeof(tusb_desc_device_t),
|
||||||
|
.bDescriptorType = TUSB_DESC_DEVICE,
|
||||||
|
.bcdUSB = 0x0200,
|
||||||
|
.bDeviceClass = 0x00,
|
||||||
|
.bDeviceSubClass = 0x00,
|
||||||
|
.bDeviceProtocol = 0x00,
|
||||||
|
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
|
||||||
|
.idVendor = 0xCafe,
|
||||||
|
.idProduct = 0x4001,
|
||||||
|
.bcdDevice = 0x0100,
|
||||||
|
.iManufacturer = 0x01,
|
||||||
|
.iProduct = 0x02,
|
||||||
|
.iSerialNumber = 0x03,
|
||||||
|
.bNumConfigurations = 0x01
|
||||||
|
};
|
||||||
|
|
||||||
|
uint8_t const *tud_descriptor_device_cb(void) {
|
||||||
|
return (uint8_t const *)&desc_device;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_MIDI_DESC_LEN)
|
||||||
|
|
||||||
|
uint8_t const desc_configuration[] = {
|
||||||
|
TUD_CONFIG_DESCRIPTOR(1, 2, 0, CONFIG_TOTAL_LEN, 0x00, 100),
|
||||||
|
TUD_MIDI_DESCRIPTOR(1, 0, 0x01, 0x81, 64)
|
||||||
|
};
|
||||||
|
|
||||||
|
uint8_t const *tud_descriptor_configuration_cb(uint8_t index) {
|
||||||
|
(void)index;
|
||||||
|
return desc_configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char const *string_desc_arr[] = {
|
||||||
|
(const char[]){0x09, 0x04},
|
||||||
|
"cs-midi",
|
||||||
|
"MIDI Test",
|
||||||
|
"000001",
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint16_t _desc_str[32 + 1];
|
||||||
|
|
||||||
|
uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
|
||||||
|
(void)langid;
|
||||||
|
uint8_t chr_count;
|
||||||
|
|
||||||
|
if (index == 0) {
|
||||||
|
memcpy(&_desc_str[1], string_desc_arr[0], 2);
|
||||||
|
chr_count = 1;
|
||||||
|
} else {
|
||||||
|
if (index >= sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))
|
||||||
|
return NULL;
|
||||||
|
const char *str = string_desc_arr[index];
|
||||||
|
chr_count = (uint8_t)strlen(str);
|
||||||
|
if (chr_count > 31) chr_count = 31;
|
||||||
|
for (uint8_t i = 0; i < chr_count; i++)
|
||||||
|
_desc_str[1 + i] = str[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
_desc_str[0] = (uint16_t)((TUSB_DESC_STRING << 8) | (2 * chr_count + 2));
|
||||||
|
return _desc_str;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
#ifndef _TUSB_CONFIG_H_
|
||||||
|
#define _TUSB_CONFIG_H_
|
||||||
|
|
||||||
|
#define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE
|
||||||
|
|
||||||
|
#define CFG_TUD_MIDI 1
|
||||||
|
#define CFG_TUD_MIDI_RX_BUFSIZE 64
|
||||||
|
#define CFG_TUD_MIDI_TX_BUFSIZE 64
|
||||||
|
|
||||||
|
#define CFG_TUD_CDC 0
|
||||||
|
#define CFG_TUD_MSC 0
|
||||||
|
#define CFG_TUD_HID 0
|
||||||
|
#define CFG_TUD_VENDOR 0
|
||||||
|
|
||||||
|
#endif
|
||||||
Loading…
Reference in New Issue