|
Operator SDK
Build MIDI processing and UI modes for the Operator device
|
The lookup table for everything your mode calls at runtime. The OperatorApi is a versioned struct of function pointers, and a mode resolves an entry by its position, so the table only ever grows and existing entries keep their slot. Your custom UI also receives a flat gesture event. The on-disk binary layout (OpmHeader, ParamSpec) lives in .opm Format.
A mode compiled against SDK version N continues to load on firmware version M where M is greater than or equal to N, for the lifetime of the product. See ABI versioningABI versioning below for the exact rules.
Defined in operator_sdk/abi/mode_api.h. SDK v1.0 populates 28 function pointers in the order listed. The index is the 0-based slot inside the struct. Reordering or removing any entry would make modes un-loadable. Each name links to its full declaration.
| Idx | Function | Purpose |
|---|---|---|
| 0 | send_midi | Queue a 1 to 3 byte MIDI message for output, where the modes loaded after this one can act on it. out selects the output for a global mode (0..7 = OUT 1..8, 8 = USB) and is ignored for an output mode, which reaches its bound output. The output takes the message only if it passes that kind of message. Clock (0xF8) goes to the output on its own and joins no mode's batch. |
| 1 | send_midi_direct | Queue a 1 to 3 byte MIDI message straight to the output, skipping the modes loaded after this one. Output selection and the message-type rule match send_midi. Suits a timing reference or a tap reporting what an output is doing. |
| 2 | send_midi_from | Queue a 1 to 3 byte MIDI message caused by one the mode was handed. It joins the traffic on the input named by the first argument (0..3 = IN 1..4, 4 = USB), so it is routed the way its cause is routed. Global modes only, refused for an output mode. |
| 3 | send_sysex | Queue a SysEx message the mode generates, or one fragment of it. Output scoping matches send_midi: an output mode's argument is ignored and its bound output is used, a global mode names the output (0..7 = OUT 1..8, 8 = USB). Returns the fragment's byte count, kOutputBusy (-2) when the output cannot take it this pass, kOutputTooLarge (-3) when the fragment can never fit, or -1 for an invalid output, an output not set to pass system-exclusive, or a mode not allowed to send SysEx. |
| 4 | output_active | Returns true only if out can currently emit MIDI: in range, not user-disabled, and physically present (USB output activates only while USB MIDI is active). Mirrors send_midi's drop behavior, so a mode can skip dead or disabled outputs instead of emitting into the void. |
| 5 | get_tick | Monotonic microsecond counter since boot. |
| 6 | get_pulse_count | Monotonic-since-boot 24 PPQ pulse counter. Advances when the active clock source emits a clock message. |
| 7 | set_clock_period_us | Program the internal clock generator. Effective only when the caller is a clock-gen mode and Clock Source is set to Mode. |
| 8 | storage_read | Read up to size bytes from /extras/{mode}/<path>, returning bytes read or a negative error. |
| 9 | storage_file_open | Open a handle for streaming reads under /extras/{mode}/<path>. |
| 10 | storage_file_read | Read up to size bytes from a handle. 0 = EOF, -1 = error. |
| 11 | storage_file_seek | Reposition a handle's read cursor. |
| 12 | storage_file_close | Close a streaming handle. |
| 13 | storage_file_open_write | Open a handle for streaming writes under /extras/{mode}/<path>. open_mode is kStorageOpenTruncate (create or overwrite from empty) or kStorageOpenOverwrite (overwrite in place). Returns a handle, -1 on error, or -2 (kStorageBusy). |
| 14 | storage_file_write | Write size bytes to a write handle. Returns bytes written, -1 on error, -2 (kStorageBusy) when busy, or -3 (kStorageNoSpace) when the store is full. |
| 15 | storage_delete | Delete /extras/{mode}/<path>. Returns 0 on success, -1 on error, or -2 (kStorageBusy). |
| 16 | storage_enumerate | List the mode's stored files into out as NUL-separated names, filtered by an optional extension pattern. Returns the entry count, -1 on error, or -2 (kStorageBusy). |
| 17 | draw_rect | Draw a 1-pixel outline rectangle. |
| 18 | fill_rect | Draw a filled rectangle with ON or OFF pixels. |
| 19 | draw_text | Render text in the 5x7 standard font. |
| 20 | draw_text_large | Render text in the 8x12 large font. |
| 21 | draw_bitmap | Blit a packed 1-bit bitmap. The packing is described in Custom UI. |
| 22 | set_pixel | Turn a single pixel ON or OFF. Out-of-range coordinates are discarded. |
| 23 | log | Emit a log line. Level is one of 0 = debug, 1 = info, 2 = warn, 3 = error. Visible and filterable in the Monitor view. |
| 24 | get_param_value | Read the current int32 value for a declarative param. |
| 25 | set_param_value | Write a declarative param value. Core 0 publishes it within one frame. |
| 26 | get_param_filename | Resolve a FilePicker param to a NUL-terminated filename. Returns bytes written (excluding NUL), 0 for "no file picked yet", -1 on validation error, and -2 (kStorageBusy) when busy. Prefer the typed param<FilePickerSpec>() accessor from <operator_sdk/params.h>. |
| 27 | get_clock_state | Returns one of OpClockState: kClockStateActive, kClockStateNoClock, or kClockStateMode. |
Every function is documented on the OperatorApi struct page, which carries the per-parameter and return-value notes from the header's /// comments. Your editor shows the same on hover.
A custom-UI mode's mode_ui_gesture export is a 4-argument flat event handler:
Long-presses are reserved by the firmware and modes NEVER handle them (on either the physical fields or gesture_type): kGestureTypeExitUi (6, exit the custom UI) and kGestureTypeEnterParams (5, open the Parameters page). The layout zones and reserved gestures for custom UIs are covered in Custom UI.
Every new SDK release bumps kOperatorSdkVersion when the function table or header gains new fields. The rules below are absolute when releasing new versions:
Firmware validates sdk_version on load and refuses modes newer than it understands. Modes older than the firmware always load, because the unused appended functions are simply never called by the mode.
| SDK Version | Firmware Versions That Load It | Notes |
|---|---|---|
| v1.0.0 | v1.0.0 | Initial release. |