Skip to content

Modules Reference

This document describes each module in PolyBoard, its responsibilities, and key functions.

The main module that handles SDK callbacks and routes events to appropriate handlers.

// Called once at startup
void app_init(const u16 *adc_raw);
// Called every 1ms
void app_timer_event(void);
// Called on button/pad press/release
void app_surface_event(u8 type, u8 index, u8 value);
// Called on pad pressure change
void app_aftertouch_event(u8 index, u8 value);
// Called on MIDI input (not used)
void app_midi_event(u8 port, u8 status, u8 d1, u8 d2);
void app_surface_event(u8 type, u8 index, u8 value) {
// Setup button toggles setup mode
if (type == TYPESETUP) { ... }
// Mode buttons (Circle, Double) work in all modes
if (index == BTN_CIRCLE) { ... }
if (index == BTN_DOUBLE) { ... }
// Route to mode-specific handler
switch (get_mode()) {
case MODE_PLAY: handle_play_mode(index, value); break;
case MODE_ROOT_SELECT: handle_root_select(index, value); break;
case MODE_SCALE_SELECT: handle_scale_select(index, value); break;
case MODE_SETUP: handle_setup_mode(index, value); break;
}
}
static void send_note_on(unsigned short channels, u8 note, u8 velocity) {
for (u8 ch = 0; ch < 16; ch++) {
if (channels & (1 << ch)) {
u8 status = NOTEON | ch;
hal_send_midi(USBSTANDALONE, status, note, velocity);
hal_send_midi(USBMIDI, status, note, velocity);
hal_send_midi(DINMIDI, status, note, velocity);
}
}
}

Manages volatile runtime state that doesn’t persist across power cycles.

typedef struct AppState {
AppMode mode; // MODE_PLAY, MODE_ROOT_SELECT, etc.
unsigned char active_notes[16]; // 128-bit bitmask for MIDI notes
unsigned char dirty; // Flash needs saving
unsigned char held_page; // Which page button is held (0xFF = none)
unsigned char held_shift; // Shift button state
unsigned char held_arrow_left;
unsigned char held_arrow_right;
unsigned char scale_lock; // Global (not per-page)
unsigned char in_setup_mode;
unsigned char pad_notes[100]; // Maps pad index to MIDI note
} AppState;

Active notes are tracked with a 128-bit bitmask (16 bytes):

void set_note_active(unsigned char note) {
g_state.active_notes[note / 8] |= (1 << (note % 8));
}
void clear_note_active(unsigned char note) {
g_state.active_notes[note / 8] &= ~(1 << (note % 8));
}
unsigned char is_note_active(unsigned char note) {
return (g_state.active_notes[note / 8] >> (note % 8)) & 1;
}

Maps each pad to its currently playing MIDI note:

void set_pad_note(unsigned char index, unsigned char note);
unsigned char get_pad_note(unsigned char index); // Returns 0xFF if inactive
void clear_pad_note(unsigned char index);

Manages the four configurable pages, each with independent settings.

typedef struct Page {
unsigned char root; // 0-11 (C=0, C#=1, ... B=11)
unsigned char scale_type; // 0-9 (see note.h)
unsigned char octave; // 0-10
unsigned char interval_index; // 0-3 → [3, 4, 5, 7] semitones
unsigned short midi_channels; // Bitmask: bit 0 = ch1, bit 15 = ch16
unsigned char aftertouch_mode; // 0=channel, 1=poly
unsigned char velocity_curve; // 0=linear, 1=soft, 2=hard, 3=fixed
signed char transpose; // -7 to +7 scale degrees
} Page;
// Change active page (0-3)
const Page *change_page(unsigned char page);
// Get page by index
const Page *get_page(unsigned char page);
// Get current page index
unsigned char get_current_page();
// Copy settings between pages
void copy_page(unsigned char from, unsigned char to);

Four row intervals available:

IndexSemitonesInterval Name
03Minor 3rd
14Major 3rd
25Perfect 4th (default)
37Perfect 5th

Handles scale definitions and note calculations.

// 10 scales available
enum ScaleIndex {
SCALE_MAJOR, SCALE_MINOR,
SCALE_PENT_MAJOR, SCALE_PENT_MINOR,
SCALE_DORIAN, SCALE_MIXOLYDIAN,
SCALE_HARMONIC_MINOR, SCALE_BLUES,
SCALE_LYDIAN, SCALE_PHRYGIAN
};
// Scale membership: 1=root, 0=in scale, -1=chromatic
static const short scales[SCALE_COUNT][12] = {
// Major: W W H W W W H
{1, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0},
// ... etc
};
short is_note_in_scale(unsigned char note, unsigned char root, int scale_index);
// Returns: 1 = root note, 0 = in scale, -1 = chromatic
int transpose_semitone_by_degree(int semitone_offset,
unsigned char scale_index,
signed char transpose);

This function:

  1. Converts semitone offset to scale degree
  2. Adds transpose value
  3. Converts back to semitones
  4. Handles octave wrapping

Manages all LED output and visual feedback.

Dirty-region tracking to minimize HAL calls:

#define LED_CACHE_INVALID 0xFFFFFFFFU
static u32 led_cache[GRID_SIZE];
static volatile u8 render_pending;
void set_pad_color(u8 index, u32 color) {
if (led_cache[index] != color) {
led_cache[index] = color;
PLOT_LED_FAST(TYPEPAD, index, color);
}
}
// Full display refresh based on current mode
void refresh_display(void);
// Mode-specific renderers
void render_play_grid(const Page *page);
void render_controls(unsigned char page, unsigned char interval, signed char transpose, unsigned char octave);
void render_root_select(unsigned char current_root);
void render_scale_select(unsigned char current_scale);
void render_setup_grid(const Page *page);
// Single pad update (more efficient for note on/off)
void update_pad_color(u8 index, const Page *page);
// Request render on next timer tick
void request_render(void);
// Process pending render (called from app_timer_event)
void process_pending_render(void);
// Force full refresh (e.g., on mode change)
void invalidate_led_cache(void);
// Note colors
#define COLOR_ROOT 0x3F003FU // Full magenta (root note)
#define COLOR_IN_SCALE 0x0F000FU // Dim magenta (in scale)
#define COLOR_CHROMATIC 0x0F0F0FU // Dim white (chromatic)
#define COLOR_CYAN 0x003F3FU // Active note
// Control colors
#define COLOR_ORANGE 0x3F1F00U // Active page
#define COLOR_WHITE 0x3F3F3FU // Quantise off
#define COLOR_CTRL_HINT 0x0F0F0FU // Button hints

Handles saving and loading configuration from flash memory.

#define FLASH_MAGIC 0x000B0A4DUL
#define FLASH_VERSION 7
typedef struct FlashData {
unsigned long magic; // Validation marker
unsigned char version; // Schema version
unsigned char current_page;
unsigned char reserved[2];
Page pages[PAGE_COUNT];
} __attribute__((packed)) FlashData;
void flash_save(void); // Write current state to flash
void flash_load(void); // Load state from flash (or defaults)
unsigned char flash_is_valid(void); // Check if flash has valid data

When adding new fields:

  1. Increment FLASH_VERSION
  2. Handle migration in flash_load()
  3. Preserve valid ranges for existing fields
void flash_load(void) {
FlashData data;
hal_read_flash(0, (u8*)&data, sizeof(data));
if (data.magic != FLASH_MAGIC) {
// No valid data - use defaults
return;
}
// Validate and migrate old versions
if (data.version < FLASH_VERSION) {
// Handle migration...
}
}

Utility functions for working with the button/pad surface.

enum SurfaceKeyType {
SURFACE_PAD, // 8x8 main grid
SURFACE_CTRL // Control buttons
};
enum SurfaceKeyType index_to_surface_key_type(unsigned char index);
// Check if index is a grid pad (not control)
static unsigned char is_grid_pad(u8 index) {
u8 row = index / 10;
u8 col = index % 10;
return (row >= 1 && row <= 8 && col >= 1 && col <= 8);
}

Applies velocity curves to pad pressure values.

CurveDescription
LinearDirect mapping (default)
SoftEmphasizes light touches
HardRequires more force
FixedAlways maximum velocity
typedef enum {
VELOCITY_LINEAR = 0,
VELOCITY_SOFT,
VELOCITY_HARD,
VELOCITY_FIXED
} VelocityCurve;
unsigned char apply_velocity_curve(VelocityCurve curve, unsigned char value);

Defines button indices for cleaner code:

// Top row controls
#define BTN_OCTAVE_UP 91
#define BTN_OCTAVE_DOWN 92
#define BTN_ARROW_LEFT 93
#define BTN_ARROW_RIGHT 94
#define BTN_SESSION_1 95
#define BTN_SESSION_2 96
#define BTN_SESSION_3 97
#define BTN_SESSION_4 98
// Left column controls
#define BTN_SHIFT 80
#define BTN_CLICK 70
#define BTN_UNDO 60
#define BTN_DELETE 50
#define BTN_QUANTISE 40
#define BTN_DUPLICATE 30
#define BTN_DOUBLE 20
#define BTN_CIRCLE 10
// Scene buttons (right column)
#define BTN_SCENE_1 89
// ... through BTN_SCENE_8 = 19

app.c
├── app_state.c (runtime state)
├── page.c (configuration)
├── layout.c (display)
├── flash.c (persistence)
├── note.c (calculations)
├── surface.c (utilities)
└── velocity.c (curves)
layout.c
├── app_state.c
├── page.c
├── note.c
└── buttons.h
flash.c
├── page.c
└── app_state.c