|
Operator SDK
Build MIDI processing and UI modes for the Operator device
|
The function table a mode calls into. More...
#include <mode_api.h>
Public Attributes | |
| bool(* | send_midi )(uint8_t output_idx, uint8_t status, uint8_t data1, uint8_t data2) |
| Queue a 1 to 3 byte MIDI message for output. | |
| bool(* | send_midi_direct )(uint8_t output_idx, uint8_t status, uint8_t data1, uint8_t data2) |
| Queue a 1 to 3 byte MIDI message straight to the output. | |
| bool(* | send_midi_from )(uint8_t input_idx, uint8_t status, uint8_t data1, uint8_t data2) |
| Queue a 1 to 3 byte MIDI message caused by one this mode was handed. | |
| int32_t(* | send_sysex )(uint8_t output_idx, const uint8_t *data, uint16_t len, uint8_t flags) |
| Queue a system-exclusive message, or a fragment of one, for output. | |
| bool(* | output_active )(uint8_t output_idx) |
| Report whether an output can currently emit MIDI. | |
| uint32_t(* | get_tick )() |
| Monotonic microsecond counter since boot. | |
| uint32_t(* | get_pulse_count )() |
| Monotonic 24 PPQ pulse counter since boot. | |
| void(* | set_clock_period_us )(uint32_t period_us) |
| Program the internal clock generator's period. | |
| int32_t(* | storage_read )(const char *path, uint8_t *buf, uint32_t size) |
| Read up to size bytes from a scoped path into buf. | |
| int32_t(* | storage_file_open )(const char *path) |
| Open a handle for streaming reads under a scoped path. | |
| int32_t(* | storage_file_read )(int32_t handle, uint8_t *buf, uint32_t size) |
| Read up to size bytes from an open handle. | |
| int32_t(* | storage_file_seek )(int32_t handle, uint32_t offset) |
| Move an open handle's read cursor. | |
| int32_t(* | storage_file_close )(int32_t handle) |
| Close an open handle. | |
| int32_t(* | storage_file_open_write )(const char *path, uint8_t open_mode) |
| Open a handle for streaming writes under a scoped path. | |
| int32_t(* | storage_file_write )(int32_t handle, const uint8_t *buf, uint32_t size) |
| Write size bytes from buf to an open write handle. | |
| int32_t(* | storage_delete )(const char *path) |
| Delete a file under a scoped path. | |
| int32_t(* | storage_enumerate )(const char *pattern, char *out, uint32_t out_size) |
| List the mode's stored files into out as NUL-separated names. | |
| void(* | draw_rect )(uint16_t x, uint16_t y, uint16_t w, uint16_t h) |
| Draw a 1 pixel outline rectangle. | |
| void(* | fill_rect )(uint16_t x, uint16_t y, uint16_t w, uint16_t h, bool on) |
| Draw a filled rectangle of on or off pixels. | |
| void(* | draw_text )(uint16_t x, uint16_t y, const char *text) |
| Draw text in the 5x7 standard font. | |
| void(* | draw_text_large )(uint16_t x, uint16_t y, const char *text) |
| Draw text in the 8x12 large font. | |
| void(* | draw_bitmap )(uint16_t x, uint16_t y, const uint8_t *data, uint16_t w, uint16_t h) |
| Draw a packed 1-bit bitmap. | |
| void(* | set_pixel )(uint16_t x, uint16_t y, bool on) |
| Turn a single pixel on or off. | |
| void(* | log )(LogLevel level, const char *message) |
| Emit a log line, viewable in the Log Monitor view. | |
| int32_t(* | get_param_value )(uint8_t param_id) |
| Read the current int32 value of a declarative parameter. | |
| void(* | set_param_value )(uint8_t param_id, int32_t value) |
| Write a declarative parameter value. | |
| int32_t(* | get_param_filename )(uint8_t param_id, char *buf, uint32_t buf_size) |
| Resolve a FilePicker parameter to its selected filename. | |
| uint8_t(* | get_clock_state )() |
| Read the current clock authority state. | |
The function table a mode calls into.
Entries are grouped by concern: MIDI I/O, Timing, Storage (scoped read and write), Display, Logging, Parameters, and Clock. SDK version 1 defines 28 entries, and their order below is part of the ABI.
| void(* OperatorApi::draw_bitmap) (uint16_t x, uint16_t y, const uint8_t *data, uint16_t w, uint16_t h) |
Draw a packed 1-bit bitmap.
| x | Left edge in pixels. |
| y | Top edge in pixels. |
| data | Page-addressed 1-bit pixel data. |
| w | Bitmap width in pixels. |
| h | Bitmap height in pixels. |
| void(* OperatorApi::draw_rect) (uint16_t x, uint16_t y, uint16_t w, uint16_t h) |
Draw a 1 pixel outline rectangle.
| x | Left edge in pixels. |
| y | Top edge in pixels. |
| w | Width in pixels. |
| h | Height in pixels. |
| void(* OperatorApi::draw_text) (uint16_t x, uint16_t y, const char *text) |
Draw text in the 5x7 standard font.
| x | Left edge of the first glyph in pixels. |
| y | Top edge in pixels. |
| text | NUL-terminated string to draw. |
| void(* OperatorApi::draw_text_large) (uint16_t x, uint16_t y, const char *text) |
Draw text in the 8x12 large font.
| x | Left edge of the first glyph in pixels. |
| y | Top edge in pixels. |
| text | NUL-terminated string to draw. |
| void(* OperatorApi::fill_rect) (uint16_t x, uint16_t y, uint16_t w, uint16_t h, bool on) |
Draw a filled rectangle of on or off pixels.
| x | Left edge in pixels. |
| y | Top edge in pixels. |
| w | Width in pixels. |
| h | Height in pixels. |
| on | True sets the pixels, false clears them. |
| uint8_t(* OperatorApi::get_clock_state) () |
Read the current clock authority state.
| int32_t(* OperatorApi::get_param_filename) (uint8_t param_id, char *buf, uint32_t buf_size) |
Resolve a FilePicker parameter to its selected filename.
Copies the null-terminated filename into buf, truncated to buf_size with the NUL always preserved when buf_size is greater than 0. Prefer the typed param<FilePickerSpec>() accessor from <operator_sdk/params.h>, which wraps this entry and returns a FilePickerValue.
| param_id | The FilePicker parameter id. |
| buf | Destination buffer for the filename. |
| buf_size | Size of buf. |
| int32_t(* OperatorApi::get_param_value) (uint8_t param_id) |
Read the current int32 value of a declarative parameter.
| param_id | The parameter's value slot index. |
| uint32_t(* OperatorApi::get_pulse_count) () |
Monotonic 24 PPQ pulse counter since boot.
Advances when the current clock source emits a clock message.
| uint32_t(* OperatorApi::get_tick) () |
Monotonic microsecond counter since boot.
| void(* OperatorApi::log) (LogLevel level, const char *message) |
Emit a log line, viewable in the Log Monitor view.
| level | A LogLevel severity (op::LogLevel::Debug / Info / Warn / Error). The Log Monitor renders it as a D / I / W / E prefix and can filter on it. |
| message | Null-terminated text to log. |
| bool(* OperatorApi::output_active) (uint8_t output_idx) |
Report whether an output can currently emit MIDI.
True when output_idx is in range, is not user-disabled, and is present. The USB output reports true while USB MIDI is active. A mode rotating between outputs uses this to pick one that can take the message.
| output_idx | Output index to query. |
| bool(* OperatorApi::send_midi) (uint8_t output_idx, uint8_t status, uint8_t data1, uint8_t data2) |
Queue a 1 to 3 byte MIDI message for output.
The message joins that output's traffic, so the modes loaded after this one there are handed it and can transform it, hold it back, or answer it.
The output takes it only if it passes that kind of message, so one set to pass clock alone takes no notes. Timing clock (0xF8) goes to the output on its own and joins no mode's batch.
| output_idx | Output for global modes (0..7 = OUT 1..8, 8 = USB). Ignored for output modes, which always reach their bound output. |
| status | MIDI status byte. |
| data1 | First data byte. |
| data2 | Second data byte (ignored for 1 and 2 byte messages). |
| bool(* OperatorApi::send_midi_direct) (uint8_t output_idx, uint8_t status, uint8_t data1, uint8_t data2) |
Queue a 1 to 3 byte MIDI message straight to the output.
The modes loaded after this one on that output are never handed it, so it reaches the wire untouched. This suits a timing reference or a tap reporting what an output is doing.
Only those modes are skipped. The output still takes the message only if it passes that kind of message.
| output_idx | Output for global modes (0..7 = OUT 1..8, 8 = USB). Ignored for output modes, which always reach their bound output. |
| status | MIDI status byte. |
| data1 | First data byte. |
| data2 | Second data byte (ignored for 1 and 2 byte messages). |
| bool(* OperatorApi::send_midi_from) (uint8_t input_idx, uint8_t status, uint8_t data1, uint8_t data2) |
Queue a 1 to 3 byte MIDI message caused by one this mode was handed.
The message joins the traffic on the input named by input_idx, so it is routed exactly as a message arriving there would be.
It carries input_idx as its port, and the global modes loaded after this one are handed it.
Global modes only. An output mode runs once routing has decided where everything goes, so the call is refused there and answers false.
| input_idx | Input the causing message arrived on (0..3 = IN 1..4, 4 = USB). |
| status | MIDI status byte. |
| data1 | First data byte. |
| data2 | Second data byte (ignored for 1 and 2 byte messages). |
| int32_t(* OperatorApi::send_sysex) (uint8_t output_idx, const uint8_t *data, uint16_t len, uint8_t flags) |
Queue a system-exclusive message, or a fragment of one, for output.
A short message goes out in a single call. A long one is built up over several calls, and flags says where each call sits in the message.
Everything sent between the opening and the closing call reaches the output as one message. Each call queues its whole fragment or queues nothing at all, so a call answered with kOutputBusy leaves the open message untouched and the same fragment goes out again on a later process().
One call carries at most 253 payload bytes to a physical output and 448 to the USB output, so 253 bytes per call reaches any output. A message longer than that goes out over several calls.
A message ends by itself after a full second in which none of it reached the output. A refused call sends nothing, so it counts towards that second, and a retry that keeps being refused ends the message it is retrying. Calls after that answer -1 and a new message has to be opened.
| output_idx | Output index. An output mode passes its own bound output. |
| data | The message bytes, without the opening 0xF0 and the closing 0xF7, which are added around the message. |
| len | Number of bytes in data. Zero pairs with kSysExEnd to close a message that has no bytes left to send. |
| flags | Where this call sits in the message (see the list above). |
| void(* OperatorApi::set_clock_period_us) (uint32_t period_us) |
Program the internal clock generator's period.
Only effective when the caller is a clock generating mode and Clock Source is set to Mode. A period of 0 disables the clock, and setting the same period again is a no-op.
| period_us | Clock period in microseconds. |
| void(* OperatorApi::set_param_value) (uint8_t param_id, int32_t value) |
Write a declarative parameter value.
The new value is published within one frame.
| param_id | The parameter's value slot index. |
| value | The value to store. |
| void(* OperatorApi::set_pixel) (uint16_t x, uint16_t y, bool on) |
Turn a single pixel on or off.
Out-of-range coordinates are a no-op.
| x | Column in pixels. |
| y | Row in pixels. |
| on | True sets the pixel, false clears it. |
| int32_t(* OperatorApi::storage_delete) (const char *path) |
Delete a file under a scoped path.
| path | Relative path under the mode's storage area. |
| int32_t(* OperatorApi::storage_enumerate) (const char *pattern, char *out, uint32_t out_size) |
List the mode's stored files into out as NUL-separated names.
| pattern | Extension filter (for example ".scl"), or empty for all files. |
| out | Destination buffer for the packed name list. The list is clipped to fit. |
| out_size | Size of out. |
| int32_t(* OperatorApi::storage_file_close) (int32_t handle) |
Close an open handle.
This is not idempotent: closing an already closed handle returns -1.
| handle | A handle from storage_file_open. |
| int32_t(* OperatorApi::storage_file_open) (const char *path) |
Open a handle for streaming reads under a scoped path.
The handle stays valid across process() calls until it is closed or the mode is destroyed (which auto-closes it).
| path | Relative path under the mode's storage area. |
| int32_t(* OperatorApi::storage_file_open_write) (const char *path, uint8_t open_mode) |
Open a handle for streaming writes under a scoped path.
The handle stays valid across process() calls until it is closed or the mode is destroyed (which auto-closes it).
| path | Relative path under the mode's storage area. |
| open_mode | kStorageOpenTruncate creates or overwrites from empty, kStorageOpenOverwrite overwrites in place from offset 0. |
| int32_t(* OperatorApi::storage_file_read) (int32_t handle, uint8_t *buf, uint32_t size) |
Read up to size bytes from an open handle.
| handle | A handle from storage_file_open. |
| buf | Destination buffer. |
| size | Maximum bytes to read. |
| int32_t(* OperatorApi::storage_file_seek) (int32_t handle, uint32_t offset) |
Move an open handle's read cursor.
Seeking past the end of the file is allowed, and later reads return 0.
| handle | A handle from storage_file_open. |
| offset | New absolute read offset. |
| int32_t(* OperatorApi::storage_file_write) (int32_t handle, const uint8_t *buf, uint32_t size) |
Write size bytes from buf to an open write handle.
| handle | A handle from storage_file_open_write. |
| buf | Source bytes. |
| size | Number of bytes to write. |
| int32_t(* OperatorApi::storage_read) (const char *path, uint8_t *buf, uint32_t size) |
Read up to size bytes from a scoped path into buf.
| path | Relative path under the mode's storage area. |
| buf | Destination buffer. |
| size | Maximum bytes to read. |