コンテンツにスキップ

midiglue API Reference

私たちは、ユーザーが興味深いカスタムノードを簡単に作成できるように、いくつかのAPIを公開しています。 カスタムノード自体の作成方法についてはカスタムノードを参照してください。

自分でカスタムノードを作成しない場合は、このページの内容について理解する必要はありません。

We expose some APIs to make it easy for users to create interesting custom nodes. See Custom Nodes for how to create a custom node itself.

If you don't create your own custom node, you don't need to understand the contents of this page.

Types

  • Int : int
  • Float : float
  • MidiPacket : Midiglue::MidiPacket (For details refer to MIDI API - MidiPacket)
  • MidiSysEx : Midiglue::MidiSysEx (For details refer to MIDI API - MidiSysEx)
  • String : std::shared_ptr<std::string>
  • IntList : std::shared_ptr<std::vector<int>>
  • FloatList : std::shared_ptr<std::vector<float>>
  • Signal : FloatList (size = Midiglue::AUDIO_VEC_SIZE)
  • MidiPacketList : std::shared_ptr<std::vector<Midiglue::MidiPacket>>
  • StringList : std::shared_ptr<std::vector<std::string>>
  • event_delay_t : uint64_t (Used in Event API)

Constants

Bang

  • const Int Midiglue::__bang : Used for Bang signal

TPQN

SysEx

  • constexpr size_t Midiglue::SYSEX_MAX_LENGTH = 256u : Maximum supported SysEx Message length

ClockState

enum class ClockState : Int {
    Stop = 0,
    Continue = 1,
    Start = 2 //  DIN Sync does not have this state.
};

Used to represent the status of MIDI Timing Clock or DIN Sync. Can be casted to Int type using static_cast<Int>

LEDColor

enum LEDColor : uint32_t {
    DefaultColor = 0,
    Red = 1,
    Green = 2,
    Blue = 3,
    Yellow = 4
};

Used to represent the LED colors. DefaultColor is white.

String

String Midiglue::make_string(const std::string &str);
String Midiglue::make_string(std::string &&str);

Make String from normal C++ strings.

// Example
String str1 = Midiglue::make_string("abc"); // "abc"
const std::string str = "def"
String str2 = Midiglue::make_string(str); // "def"

List

IntList

IntList Midiglue::make_list<Int>(std::initializer_list<Int> &&list);
IntList Midiglue::make_list<Int>(const std::vector<Int> &vec);
IntList Midiglue::make_list<Int>(std::vector<Int> &&vec);

Make IntList.

// Example
IntList lst1 = Midiglue::make_list<Int>({1, 2, 3}); // {1, 2, 3}
const std::vector<Int> intvec = {4, 5, 6};
IntList lst2 = Midiglue::make_list<Int>(intvec); // {4, 5, 6}

FloatList

FloatList Midiglue::make_list<Float>(std::initializer_list<Float> &&list);
FloatList Midiglue::make_list<Float>(const std::vector<Float> &vec);
FloatList Midiglue::make_list<Float>(std::vector<Float> &&vec);

Make FloatList.

// Example
FloatList lst1 = Midiglue::make_list<Float>({0.1, 0.2, 0.3}); // {0.1, 0.2, 0.3}
const std::vector<Float> floatvec = {0.4, 0.5, 0.6};
FloatList lst2 = Midiglue::make_list<Float>(floatvec); // {0.4, 0.5, 0.6}

StringList

StringList Midiglue::make_list<String>(std::initializer_list<std::string> &&list);
StringList Midiglue::make_list<String>(const std::vector<std::string> &vec);
StringList Midiglue::make_list<String>(std::vector<std::string> &&vec);

Make StringList from std::string (NOT from String!!!).

// Example
StringList lst1 = Midiglue::make_list<String>({"abc", "def", "ghi"}); // {"abc", "def", "ghi"}
const std::vector<std::string> strvec = {"jkl", "mno", "pqr"};
StringList lst2 = Midiglue::make_list<String>(strvec); // {"jkl", "mno", "pqr"}

String str1 = Midiglue::make_string("stu"); // "stu"
String str2 = Midiglue::make_string("vwx"); // "vwx"
// A String is a pointer to std::string, so str1 and str2 must be dereferenced
StringList lst3 = Midiglue::make_list<String>({*str1, *str2}); // {"stu", "vwx"}

MidiPacketList

MidiPacketList Midiglue::make_list<MidiPacket>(std::initializer_list<MidiPacket> &&list);
MidiPacketList Midiglue::make_list<MidiPacket>(const std::vector<MidiPacket> &vec);
MidiPacketList Midiglue::make_list<MidiPacket>(std::vector<MidiPacket> &&vec);

Make MidiPacketList.

// Example
MidiPacket packet1 = Midiglue::makeTimingClock(); // MIDI Timing Clock packet
MidiPacket packet2 = Midiglue::makeNoteOn(1, 50, 64); // NoteOn packet
MidiPacket packet3 =Midiglue::makeNoteOff(1, 50); // NoteOff packet

MidiPacketList lst1 = Midiglue::make_list<MidiPacket>({packet1, packet2, packet3}); // {Timing Clock, NoteOn, NoteOff}
const std::vector<MidiPacket> packetvec = {packet1, packet2, packet3};
MidiPacketList lst2 = Midiglue::make_list<MidiPacket>(packetvec); // {Timing Clock, NoteOn, NoteOff}

MIDI API

MIDIEventType

This enum class is used in MidiPacket class to identify the type of a packet.

namespace Midiglue {
enum class MIDIEventType : uint8_t{
    MiscellaneousFunctionCode = 0,
    CableEvent = 1,
    SystemCommonTwoBytes = 2,
    SystemCommonThreeBytes = 3,
    SystemExclusive = 4,
    SystemCommonSingleByte = 5,
    NoteOff = 8,
    NoteOn = 9,
    PolyKeyPress = 10,
    ControlChange = 11,
    ProgramChange = 12,
    ChannelPressure = 13,
    PitchBend = 14,
    SingleByte = 15,
    InvalidPacket = 255
};
}

MIDICCType

This enum class is used to identify the type of a control change packet.

This can be converted to uint8_t value using static_cast<uint8_t>.

namespace Midiglue {
enum class MIDICCType : uint8_t{
    BreathController = 2,
    FootController = 4,
    PortamntoTime = 5,
    ChannelVolume = 7,
    Balance = 8,
    Pan = 10,
    ExpressionController = 11,
    EffectControl1 = 12,
    EffectControl2 = 13,
    GeneralPurposeController1 = 16,
    GeneralPurposeController2 = 17,
    GeneralPurposeController3 = 18,
    GeneralPurposeController4 = 19,
    DamperPedalOnOff = 64,
    PortamentoOnOff = 65,
    SostenutoOnOff = 66,
    SoftPedalOnOff = 67,
    LegatoFootSwitch = 68,
    Hold2 = 69,
    SoundController1 = 70,
    SoundController2 = 71,
    SoundController3 = 72,
    SoundController4 = 73,
    SoundController5 = 74,
    SoundController6 = 75,
    SoundController7 = 76,
    SoundController8 = 77,
    SoundController9 = 78,
    SoundController10 = 79,
    makeGeneralPurposeController5 = 80,
    makeGeneralPurposeController6 = 81,
    makeGeneralPurposeController7 = 82,
    makeGeneralPurposeController8 = 83,
    PortamentoControl = 84,
    HighResolutionVelocityPrefix = 88,
    Effects1Depth = 91,
    Effects2Depth = 92,
    Effects3Depth = 93,
    Effects4Depth = 94,
    Effects5Depth = 95,
    AllSoundOff = 120,
    ResetAllControllers = 121,
    LocalControlOnOff = 122,
    AllNotesOff = 123,
    OmniModeOff = 124,
    OmniModeOn = 125,
    MonoModeOn = 126,
    PolyModeOn = 127
};
}

MidiPacket

This is a class which represents any single MIDI message except a System Exclusive Message.

You can use construction functions and methods to check the message types or get values in addition to access to raw MIDI message bytes (byte0, byte1, byte2).

For MIDI messages shorter than 3 bytes, bytes not used are ignored. Do not reference bytes which is not used.

A MidiPacket can be "invalid", which you can identify by the isValid() method.

A MidiPacket created by the default constructor is invalid.

namespace Midiglue {
class MidiPacket{
    // Member variables
public:
    MIDIEventType event_type;
    uint8_t byte0, byte1, byte2;

    // Member functions
public:
    MidiPacket(); // Default contructor creates an invalid packet
    MidiPacket(uint8_t status, uint8_t ch1_16, uint8_t byte1, uint8_t byte2, MIDIEventType event_type);
    MidiPacket(uint8_t byte0, uint8_t byte1, uint8_t byte2, MIDIEventType event_type);

    // Operators
    friend bool operator==(const MidiPacket &lhs, const MidiPacket &rhs);

    friend bool operator!=(const MidiPacket &lhs, const MidiPacket &rhs);

    // Construct Channel Voice Messages

    static MidiPacket makeNote(uint8_t ch1_16, uint8_t notenum, uint8_t velocity); // velocity !=0 to NoteOn, velocity == 0 to NoteOff
    static MidiPacket makeNoteOn(uint8_t ch1_16, uint8_t notenum, uint8_t velocity);
    static MidiPacket makeNoteOff(uint8_t ch1_16, uint8_t notenum, uint8_t velocity = 0);
    static MidiPacket makeControlChange(uint8_t ch1_16, MIDICCType type, uint8_t value = 0);
    static MidiPacket makeProgramChange(uint8_t ch1_16, uint8_t number);
    static MidiPacket makePitchBend(uint8_t ch1_16, int16_t value);

    // Construct Single Byte Messages
    static MidiPacket makeSingleByte(uint8_t statusbyte);
    static MidiPacket makeTimingClock();
    static MidiPacket makeTimingClockStart();
    static MidiPacket makeTimingClockStop();
    static MidiPacket makeTimingClockContinue();
    static MidiPacket makeActiveSensing();
    static MidiPacket makeInvalidPacket();

    // Check Message

    bool isNoteOn() const;
    bool isNoteOff() const;
    bool isNote() const;
    bool isControlChange() const;
    bool isControlChange(MIDICCType type) const;
    bool isProgramChange() const;
    bool isProgramChange(uint8_t number) const;
    bool isPitchBend() const;
    bool isTimingClock() const;
    bool isTimingClockStart() const;
    bool isTimingClockStop() const;
    bool isTimingClockContinue() const;
    bool isValid() const;

    // Get Value

    uint8_t getChannel() const; // Return Channel 1-16
    uint8_t getStatus() const; // Return MIDI status byte without channel information (ex. 0x90 for NoteOn)
    uint8_t getRawStatusByte() const; // Return raw MIDI status byte
    uint8_t getNoteNumber() const;
    uint8_t getVelocity() const;
    uint8_t getControlNumber() const;
    uint8_t getControlValue() const;
    int16_t getPitchBendValue() const;
};
}

MidiSysEx

Any MIDI System Exclusive Message is stored in this struct.

buf is a pointer to an array of bytes of a System Exclusive Message (including the first F0 and the last F7).

len is the length of the array.

struct MidiSysEx {
    uint32_t len;
    uint8_t const *buf;
};

Event API

About midiglue Event API

midiglue Event API is a mean to handle time delay in a midiglue node.

In midiglue node programming, you cannot use wait, delay, or sleep functions. Instead, you can use Event API to create an "event", attach a callback function to it, and schedule the event to call the attached callback function with a specified time delay.

Types of events

Time base

First, an event can be classified into two categories: microsec-based events and tick-based events.

These two types of events are different in terms of their units of time.

A microsec-based event is scheduled based on microsecond timescale. It means that you can schedule the event to be triggered exactly a certain microseconds after now.

On the other hand, a tick-based is scheduled based on a time unit called "tick". "Ticks" are incremented in synchronization with internal BPM-based timer or external synchronization signals. So, if you want to create an event that is synchronized with a music, you may find a tick-based event to be a better option.

"Oneshot", "Cyclic", and "Multiple"

In addition, there are three kinds of events: "oneshot" events, cyclic events, and multiple events.

"Oneshot" events and "cyclic" events are represented with Midiglue::SingleEvent class. "Single" means that these events can handle only one event. A "oneshot" event is triggered only once after scheduled, while a "cyclic" event is triggered periodically after scheduled.

"Multiple" events are represented with Midiglue::MultiEvent class. A single instance of Midiglue::MultiEvent class can handle multiple events and this is why it is called "multiple" event. You can register only one callback function to a single instance of Midiglue::MultiEvent, but you can set different delay time and pass different arguments on every schedulement.

For example, consider a node which takes one Int input and output it one second later. If you implement it using a "oneshot" event, the node must ignore any input for one second after it receives an input (because if you process the new input and schedule the event to output it, the information of the previous input will be overwritten!!!). In such cases, you shall use a "multiple" event to handle all the incoming inputs. Using a "multiple" event is much easier (and also more memory-efficient) than creating many instances of Midiglue::SingleEvent class and managing all of them.

About "tick"

As explained earlier, "tick" is a time unit used in midiglue.

Time correspoiding to "one tick" is determined by a constant called Midiglue::TPQN (Ticks-per-Quarter-Note). This value equals to 480, meaning that "one tick" is 1/480 of the time of one quarter note. For example, when BPM=120 with Div=4, one quarter note correnponds to 1/2 second. Therefore, one tick corresponds to 1/960 second, which is small enough for most cases.

"tick" update

Even if BPM changes dynamically, "tick" is updated correctly.

If the external synchronization signal from MIDI, Sync or DIN Sync suddenly stops, "tick" is updated according to the tempo before the sync signal stops. Therefore, "tap tempo" can be used for external sync signal (after you stop tapping, "tick" is continuously updated).

Tips

"tick" is managed by a component called "master tempo".

For more information on "master tempo", refer to Master tempo.

Event classes

Caution

Declarations of constructors of following classes are omitted from this document.

To create an event, use make_**_**_event functions described in make event APIs.

SingleEvent (non-void)

If you want to pass an argument of type T to the callback, Use Midiglue::SingleEvent<T>.

namespace Midiglue {
template <typename T_PAYLOAD = void>
class SingleEvent {
public:
    using callback_t = std::function<void(T_PAYLOAD)>;

    SingleEvent(const SingleEvent &) = delete;
    SingleEvent(SingleEvent &&) = delete;
    SingleEvent &operator=(const SingleEvent &) = delete;
    SingleEvent &operator=(SingleEvent &&) = delete;

    // delay is in microseconds for microsec-based events,
    // or in ticks for tick-based events.
    void schedule(Midiglue::event_delay_t delay, T_PAYLOAD payload);

    void cancel();
};
}
SingleEvent (void)

If there is nothing to pass to the callback as an argument, Use Midiglue::SingleEvent<void> or Midiglue::SingleEvent<>.

namespace Midiglue {
template<>
class SingleEvent<void> {
public:
    using callback_t = std::function<void()>;

    SingleEvent(const SingleEvent &) = delete;
    SingleEvent(SingleEvent &&) = delete;
    SingleEvent &operator=(const SingleEvent &) = delete;
    SingleEvent &operator=(SingleEvent &&) = delete;

    // delay is in microseconds for microsec-based events,
    // or in ticks for tick-based events.
    void schedule(Midiglue::event_delay_t delay);

    void cancel();
};
}
MultiEvent (non-void)

If you want to pass an argument of type T to the callback, Use Midiglue::MultiEvent<MAX_SIZE, T>.

MAX_SIZE is the maximum number of events to handle. You must specify the MAX_SIZE parameter as a template parameter.

namespace Midiglue {
template<uint32_t MAX_SIZE, typename T_PAYLOAD = void>
class MultiEvent {
public:
    using callback_t = std::function<void(T_PAYLOAD)>;

    SingleEvent(const SingleEvent &) = delete;
    SingleEvent(SingleEvent &&) = delete;
    SingleEvent &operator=(const SingleEvent &) = delete;
    SingleEvent &operator=(SingleEvent &&) = delete;

    // delay is in microseconds for microsec-based events,
    // or in ticks for tick-based events.
    void schedule(Midiglue::event_delay_t delay, T_PAYLOAD payload);

    // No cancel method
};
}
MultiEvent (void)

If there is nothing to pass to the callback as an argument, Use Midiglue::MultiEvent<MAX_SIZE, void> or Midiglue::MultiEvent<MAX_SIZE>.

MAX_SIZE is the maximum number of events to handle. You must specify the MAX_SIZE parameter as a template parameter.

namespace Midiglue {
template<uint32_t MAX_SIZE>
class MultiEvent<MAX_SIZE, void> {
public:
    using callback_t = std::function<void(T_PAYLOAD)>;

    SingleEvent(const SingleEvent &) = delete;
    SingleEvent(SingleEvent &&) = delete;
    SingleEvent &operator=(const SingleEvent &) = delete;
    SingleEvent &operator=(SingleEvent &&) = delete;

    // delay is in microseconds for microsec-based events,
    // or in ticks for tick-based events.
    void schedule(Midiglue::event_delay_t delay);

    // No cancel method
};
}

make event APIs

To create an event, use these APIs.

These functions return std::shared_ptr of an event. Use arrow operator to access methods of the created event.

namespace Midiglue {
template<typename T_PAYLOAD = void>
std::shared_ptr<SingleEvent<T_PAYLOAD>> make_oneshot_us_event(typename SingleEvent<T_PAYLOAD>::callback_t cb);

template<typename T_PAYLOAD = void>
std::shared_ptr<SingleEvent<T_PAYLOAD>> make_cyclic_us_event(typename SingleEvent<T_PAYLOAD>::callback_t cb);

template<typename T_PAYLOAD = void>
std::shared_ptr<SingleEvent<T_PAYLOAD>> make_oneshot_tick_event(typename SingleEvent<T_PAYLOAD>::callback_t cb);

template<typename T_PAYLOAD = void>
std::shared_ptr<SingleEvent<T_PAYLOAD>> make_cyclic_tick_event(typename SingleEvent<T_PAYLOAD>::callback_t cb);

template<uint32_t MAX_SIZE, typename T_PAYLOAD = void>
std::shared_ptr<MultiEvent<MAX_SIZE, T_PAYLOAD>> make_multi_us_event(typename MultiEvent<MAX_SIZE, T_PAYLOAD>::callback_t cb);

template<uint32_t MAX_SIZE, typename T_PAYLOAD = void>
std::shared_ptr<MultiEvent<MAX_SIZE, T_PAYLOAD>> make_multi_tick_event(typename MultiEvent<MAX_SIZE, T_PAYLOAD>::callback_t cb);
}

Timer API

midiglue provides high-resolution timer that counts up every 1 microsecond from the start of the application. The timer holds its timestamp in uint64_t format, so virtually it does not overflow.

You can obtain the timer count with Midiglue::get_us_timer_count(), but you cannot reset its value.

uint64_t Midiglue::get_us_timer_count();

Debug API

Print out string label and float value with timestamp to Debug Log of midiglue. This is useful for debugging.

Debug Menu

void Midiglue::Debug::write_log(const std::string &label, float value=0.0);

Music & Music Theory API

Music API and Music Theory API are designed to allow users to easily obtain musical output. Contains some useful functions.

Following APIs are in beta and are subject to change in future updates.

ActiveNoteTracker

ActiveNoteTracker memorizes the note being sounded based on the input MIDI Packet information. You can also use these functions to change the note channel and pitch while maintaining Note On / Off consistency.

class ActiveNoteTracker {
public:
    ActiveNoteTracker();
    ~ActiveNoteTracker();

// -------- Common ----------------

    void reset();
    size_t get_num_held_notes();
    size_t get_num_sounded_notes();
    void get_held_notenums(std::set<uint8_t>& notenums, uint8_t ch_origin);
    void get_sounded_notenums(std::set<uint8_t>& notenums, uint8_t ch_origin);

// -------- NoteShift ----------------

    Midiglue::MidiPacket noteon(Midiglue::MidiPacket const &packet, uint8_t pitch_shifted, uint8_t ch_shifted);
    Midiglue::MidiPacket noteoff(Midiglue::MidiPacket const &packet);
    Midiglue::MidiPacket pedalon(Midiglue::MidiPacket const &packet, uint8_t ch_shifted);
    Midiglue::MidiPacket pedaloff(Midiglue::MidiPacket const &packet);

// -------- Tracking ----------------

    bool track_packet(Midiglue::MidiPacket const &packet, Midiglue::MIDICCType target_pedal = Midiglue::MIDICCType::DamperPedalOnOff);
    void release_all(std::function<void(Midiglue::MidiPacket&)>& func);
};

Sequencer

Sequencer API is a group of classes to configure a simple sequencer.

namespace Sequencer{

struct Note{
    uint8_t notenum;
    uint8_t velocity;
    Note();
    Note(uint8_t notenum, uint8_t velocity);
};

struct Step{
    std::vector<Note> notes;
    int16_t hold_cnt;
    bool record(Midiglue::MidiPacket& packet, bool keep);
    void reset();
};

struct Seq{
    std::vector<Step> steps;
    uint8_t rec_index;
    void reset();
    bool record(Midiglue::MidiPacket& packet);
    bool record();
};

}

Pitch

Convert MIDI note numbers to note names, etc.

namespace Music{
namespace Pitch{
    int mod(const float a, const float b);
    int normalize_interval(const int interval);
    std::string name(const note_t note);
    std::string name_with_oct(const note_t note);
    std::string interval_name(const int interval);
}}

Rhythm

A set of APIs for mutual conversion between tick and other timing representations such as BPM and microseconds. It also includes a euclidean rhythm generator.

namespace Music{
namespace Rhythm{

    enum class TimeDiv : uint8_t {
        _1_1,
        _1_2,
        _1_4,
        _1_8,
        _1_16,
        _1_32,
        _1_2T,
        _1_4T,
        _1_8T,
        _1_16T,
        _1_32T,
        _1_2D,
        _1_4D,
        _1_8D,
        _1_16D,
        _1_32D,
        INDEX_MAX
    };
    std::string timediv_name(TimeDiv div);

    constexpr double timediv(TimeDiv div, int tpqn);
    constexpr double bpm2us(double bpm);
    constexpr double us2bpm(double us);
    constexpr double tick_per_us(double bpm, int tpqn);
    constexpr double tick2us(uint64_t tick, double bpm, int tpqn);
    constexpr double bpm2ms(double bpm);
    constexpr double ms2bpm(double ms);
    constexpr double tick_per_ms(double bpm, int tpqn);
    constexpr double tick2ms(uint64_t tick, double bpm, int tpqn);

    void euclid(std::vector<bool>& pat, uint8_t steps, uint8_t pulses, uint8_t rotate);
}}

Scale

ScaleTones is a class that holds scale. You can quantize notes to that scale, or you can infer the chord scale from chords.

namespace Music{
namespace Scale{

class ScaleTones{

    void ScaleTones::from_chord_tones(note_t, const std::set<note_t>&);
    void ScaleTones::from_chord_tones(note_t, const std::vector<note_t>&);
    void ScaleTones::from_chord_tones(note_t, const std::vector<int8_t>&);
    void ScaleTones::from_chord_tones(const std::set<note_t>& notes);
    void ScaleTones::from_chord_tones(const std::vector<note_t>& notes);
    void ScaleTones::from_chord_tones(const std::vector<int8_t>& notes);

    void from_named_scale(const NamedScale& named_scale);
    std::string from_chord_scale(const std::set<note_t>& notes, bool avoid = false);

    note_t quantize(note_t note, bool upshift = true);

    note_t get_root();
    void set_root(int8_t r);
};

}}

Chord

Perform chord analysis.

namespace Music {
namespace Chord {

std::string name(const std::set<note_t>& notes);

}}

Graphics API (beta)

You can draw on the display using the UserGUI class. This feature is in beta and all APIs are subject to change in the future. By using this function, the operation of midiglue may become unstable.

To enable the User GUI:

  1. Use UserGUI::get_instance() to get a reference to the UserGUI class.
  2. Create a lambda function to draw.
  3. Call enable() of the acquired UserGUI class, register the function created in step 2, and enable drawing.

When the UserGUI is turned on, the drawing by FW is invalidated. To undo it, call disable(). Pressing the system button also disables the User GUI.

sample code

static auto& gui = UserGUI::get_instance(); // ... [1]
static auto draw_func = [&](){ // ... [2]
    // draw something
};
gui.enable(draw_func); // ... [3]

See sample/pong_game.node for detailed usage.

namespace Graphics{

    struct Vector2D{
        float x;
        float y;
    };

    struct Vector3D{
        float x;
        float y;
        float z;
    };

}
class UserGUI{
public:
    static UserGUI& get_instance();

    void enable(std::function<void()> func);
    void disable();
    bool is_enabled();

    float fps;
    Display* display;

    static constexpr int16_t DisplayWidth = 128;
    static constexpr int16_t DisplayHeight = 64;
    static const uint16_t Black = 0x0000;
    static const uint16_t White = 0xFFFF;
};
class Display{
    void drawPixel(int16_t x, int16_t y, uint16_t color);
    void drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color);
    void drawRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color);
    void fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color);
    void setCursor(int16_t x, int16_t y);
    void setTextSize(uint8_t s);
    void setTextColor(uint16_t c);
    int printf(const char *format, ...);
};