{"id":51524897,"url":"https://github.com/merumerutho/pagedcontrols","last_synced_at":"2026-07-08T20:01:54.038Z","repository":{"id":365057708,"uuid":"1269292881","full_name":"merumerutho/PagedControls","owner":"merumerutho","description":"A tiny, header-only library to quickly deliver a PagedControls UX pattern.","archived":false,"fork":false,"pushed_at":"2026-06-15T18:07:02.000Z","size":19,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-15T18:22:24.350Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/merumerutho.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-14T14:34:17.000Z","updated_at":"2026-06-15T18:07:54.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/merumerutho/PagedControls","commit_stats":null,"previous_names":["merumerutho/pagedcontrols"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/merumerutho/PagedControls","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/merumerutho%2FPagedControls","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/merumerutho%2FPagedControls/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/merumerutho%2FPagedControls/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/merumerutho%2FPagedControls/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/merumerutho","download_url":"https://codeload.github.com/merumerutho/PagedControls/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/merumerutho%2FPagedControls/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35276781,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-08T02:00:06.796Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-07-08T20:01:52.217Z","updated_at":"2026-07-08T20:01:54.033Z","avatar_url":"https://github.com/merumerutho.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PagedControls\n\nA tiny, header-only library to quickly deliver a PagedControls UX pattern.\nControlling many parameters from a few physical knobs by grouping\nthem into **pages** (cycled with a button), featuring **soft-takeover (\"pickup\")** so\nswitching pages never makes a parameter jump, and an **LED page indicator**.\n\n`pagedctl::PagedControls` (in `PagedControls.hpp`) folds the paging UX together with\nan optional **macro / virtualization layer** — a small routing matrix\nthat can be used to map a few knob values onto many derived parameters.\n\n## Features\n- Holds a grid of `NumPages x NumKnobs` stored values (0..1).\n- Each page remembers its own knob values.\n- **Pickup**: after a page change, a knob only starts tracking once it moves\n  within `pickup_threshold` of that page's stored value (no parameter jumps).\n- **LED**: a count-flash pattern — page `p` blinks `(p+1)` times then rests.\n  `LedOn(tick)` is a **pure** function of a caller-supplied monotonic tick (whatever\n  unit matches your `LedTiming`).\n- **Macro layer** (optional): route knobs (\"macros\") to many derived parameters\n  through per-route input windows, output ranges (with inversion), reshaping\n  curves, and combine ops. Targets can be real params *or* other derived nodes,\n  so routes chain. See below.\n- Optional `SaveGrid`/`LoadGrid` for persistence.\n\n\n## Usage\n```cpp\n#include \"PagedControls.hpp\"\npagedctl::PagedControls\u003c2, 4\u003e pg;                 // 2 pages, 4 knobs\n\nconst float defaults[2][4] = {{0.42f,1,1,0.5f}, {0,0.5f,0.2f,0}};\npg.Init(defaults, /*pickup=*/0.03f, /*LED*/{15,40,110});\n\n// call regularly (host glue reads the knobs + button edge):\npg.Process(knob_values, button_falling_edge);\nfloat rt60 = pg.Value(0, 0);                      // map grid -\u003e your params\nbool  led  = pg.LedOn(tick++);                    // pure: pass your own monotonic tick\n```\n\n## Macro layer\n\nThe node graph is one flat array. The first `GridSize = NumPages*NumKnobs` nodes\nare the **inputs** (the grid, page-major); the remaining nodes are **derived**\n(virtuals and real params). A `constexpr` table of `Route`s is evaluated\ntop-to-bottom once per `Process()`:\n\n```cpp\nusing namespace pagedctl;\n// 2 pages x 4 knobs (8 inputs) + 4 derived params; 2 curves.\nPagedControls\u003c2, 4, /*NumNodes=*/12, /*NumCurves=*/2\u003e ctl;\n\nenum { M_DECAY=0, M_TONE, M_SHIMMER, M_MIX,   /* page 0 */\n       M_SPACE, M_MOTION, M_VOICE, M_FREEZE,  /* page 1 */\n       P_RT60=8, P_DAMP, P_SH_AMT, P_V2_LVL };\n\nconstexpr CurveDef kCurves[] = { {curve::Linear,0}, {curve::Pow,1.5f} };\nconstexpr Route kRoutes[] = {\n    // { src, dst, out0, out1, in0, in1, curve, op }   (trailing fields default)\n    { M_DECAY,   P_RT60,   0, 1, 0, 1, 0 },          // identity\n    { M_TONE,    P_DAMP,   0.15f, 1.0f },            // custom output range\n    { M_SHIMMER, P_SH_AMT, 0, 1, 0, 1, 1 },          // ease-in (Pow 1.5) curve\n    { M_SHIMMER, P_V2_LVL, 0, 1, 0.5f, 1.0f },       // input window: only past halfway\n};\nconst float kBase[12] = {}; // seeds derived nodes (constants / Add-Mul baselines)\n\nctl.Init(defaults, kRoutes, 4, kCurves, 2, kBase);\nctl.Process(knob_values, button_falling_edge);\nfloat rt60 = ctl.Out(P_RT60);     // resolved derived value\n```\n\n- **Input window** `in0..in1` is mapped to `[0,1]` and clamped (a zero-width\n  window acts as a step). **Output range** `out0..out1` scales it; `out0 \u003e out1`\n  inverts.\n- **Curves** are sampled from a generator `fn(t,param)` into a LUT at `Init` time,\n  so the hot path is just a lookup + lerp (no transcendentals per block). Built-in\n  generators: `curve::Linear/Pow/Exp/Log/SCurve`; pass any `float(*)(float,float)`\n  for custom shapes. Curve `0` is always linear.\n- **Ops** combine into the target: `Set` (default), `Add`, `Mul` (VCA-style\n  scaling), `Max`, `Min`. The `base` seed provides the baseline they build on, and\n  holds constants for params you don't route.\n- **Chaining**: a route may read a derived node written by an earlier route\n  (virtual → virtual → real). Order matters — producers before consumers.\n\n`Out(node)` returns the resolved value; `Value/Active/Page/LedOn/SaveGrid/LoadGrid`\nwork independently of routing (persistence stores the *macro* grid). With the\ndefault `NumNodes == GridSize` and no routes, the layer is inert.\n\n## Tests\n`tests/run.sh` (or `run.bat`):\n- `test_paged_controls`: defaults, pickup, per-page independence + pickup reset,\n  page cycling, LED flash count, save/load roundtrip.\n- `test_macro_map`: input window clamp + output inversion, every op\n  (Set/Add/Mul/Max/Min), curve LUTs vs their generators (sample points + interp),\n  virtual→virtual chaining + evaluation order, macro fan-out with live pickup, and\n  the plain (no-routing) configuration.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmerumerutho%2Fpagedcontrols","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmerumerutho%2Fpagedcontrols","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmerumerutho%2Fpagedcontrols/lists"}