Brick Groove Machine 0.9.3
Firmware embarqué pour contrôleur/synthé Brick
 
Chargement...
Recherche...
Aucune correspondance
Référence du fichier ui_controller.c

Gestion logique de l'interface utilisateur Brick. Plus de détails...

#include "ui_controller.h"
#include "ui_backend.h"
#include "ui_spec.h"
#include <string.h>
#include <stdbool.h>
#include "ui_led_backend.h"
#include "ui_keyboard_ui.h"
#include "seq_led_bridge.h"
#include "ui_overlay.h"

Structures de données

struct  ui_cycle_t
 Structure interne représentant un cycle BM (bouton menu cyclant). Plus de détails...
 

Fonctions

void ui_mark_dirty (void)
 
bool ui_is_dirty (void)
 
void ui_clear_dirty (void)
 
void ui_init (const ui_cart_spec_t *spec)
 Initialise la couche UI avec la spécification fournie.
 
void ui_switch_cart (const ui_cart_spec_t *spec)
 Changement de cartouche (reload complet de la spécification).
 
const ui_state_tui_get_state (void)
 
const ui_cart_spec_tui_get_cart (void)
 
const ui_menu_spec_tui_resolve_menu (uint8_t bm_index)
 
void ui_on_button_menu (int index)
 Gestion des boutons MENU (BM1..BM8).
 
void ui_on_button_page (int index)
 Gestion des boutons PAGE (1..5).
 
void ui_on_encoder (int enc_index, int delta)
 Gestion des encodeurs rotatifs (édition de paramètre).
 

Description détaillée

Gestion logique de l'interface utilisateur Brick.

Logique centrale de l’UI Brick : menus, pages, encodeurs + cycles BM.

  • *

    Traduit les entrées (boutons, encodeurs) en modifications d'état UI

  • et transmet les changements via ui_backend. */
    - Groupes :
    - `@defgroup ui` (UI Layer)
    - `@defgroup cart` (Cart Link / Bus / Registry)
    - `@defgroup drv` (Drivers)
    - `@defgroup core` (Services transverses)
    - **Include guards** : obligatoires, forme `BRICK_<DIRS>_<FILE>_H` (remplace `#pragma once`).
    - Préfixes normalisés : `ui_`, `cart_`, `drv_`, `seq_`.
    ---
    ## Perspectives
    | Domaine | Objectif | Statut |
    |--------------------|------------------------------------------------|---------------|
    | Découplage Cart/UI | Forward-decl & header public minimal côté link | **Fait** |
    | Ranges UI | `int16_t` sur CONT (overflow évité) | **Fait** |
    | Guards | Uniformisation guards explicites | **Fait** |
    | MIDI USB TX | Prio dédiée + timeout sém. (anti-blocage) | **Fait** |
    | Debug UART | Désactivé (USB CDC à envisager plus tard) | **Fait** |
    | SEQ Model/Engine | Implémentation temps réel déterministe | À implémenter |
    | Live record | Enregistrement temps réel (quantize, overdub) | À concevoir |
    | UI perf | Draw batching + cache `dirty` | En cours |
    | Sauvegarde | Dump patterns → flash ou SysEx | À définir |
    ---
    ## Phase 5 — Overlays & Modes customs (SEQ / ARP)
    #### Correctif navigation overlay (2025‑10‑13)
    - **Rebuild déterministe** des overlays : à chaque `enter/exit/switch_subspec`, l’overlay **réinitialise** `cur_menu=0` / `cur_page=0`, **publie** le `overlay_tag` si présent, et **force** un `ui_mark_dirty()` pour éviter tout état « fantôme ».
    - **BM1..BM8 en sortie d’overlay** : le contrôleur (`ui_controller`) **ferme d’abord** tout overlay actif (`ui_overlay_exit()`), **puis** traite le bouton menu sur la **cart réelle** restaurée.
    → Plus de menus vides ni de cycles MODE/SETUP inattendus après un mode custom.
    Cette phase introduit un **nouveau module** UI ainsi que des règles de navigation/rendu associées, sans modifier l’architecture en couches.
    ### 1. Nouveau module : `ui_overlay.[ch]`
    **Rôle :** centraliser la gestion des **overlays UI** (ex. SEQ, ARP).
    **Invariants :** module UI pur, aucune dépendance bus/UART/driver.
    **API principale :**
    - `void ui_overlay_enter(ui_overlay_id_t id, const ui_cart_spec_t* spec);`
    Active un overlay : **sauvegarde** la cartouche/état **réels** et bascule sur `spec`.
    Bascule de **MODE ↔ SETUP** sans quitter l’overlay.
    - `void ui_overlay_exit(void);`
    Ferme l’overlay et **restaure** cartouche/état réels.
    - `bool ui_overlay_is_active(void);` — `const ui_cart_spec_t* ui_overlay_get_spec(void);`
    **Flag persistant** indiquant le **dernier mode custom actif** (utilisable par les règles de pas et le rendu).
    - `void ui_overlay_prepare_banner(const ui_cart_spec_t* src_mode, const ui_cart_spec_t* src_setup, const ui_cart_spec_t** dst_mode, const ui_cart_spec_t** dst_setup, const ui_cart_spec_t* prev_cart, const char* mode_tag);`
    Utilitaire : prépare deux **bannières** d’overlay (MODE/SETUP) en injectant le **nom de la cartouche réelle** et un **tag** (`overlay_tag`, p.ex. `"SEQ"`, `"ARP"`).
    **Remarques d’implémentation :**
    - L’overlay est **exclusif** : activer un overlay **ferme** le précédent (avec restauration), puis entre dans le nouveau.
    - `ui_overlay_exit()` **ne réinitialise pas** le flag persistant du mode custom — il reste disponible pour le rendu et la logique des steps (le **label reste affiché** dans le bandeau, même hors écran du mode).
    ### 2. `ui_task` — Raccourcis overlay
    - `SHIFT + BS9` → **SEQ overlay**
    1er appui : MODE ; appuis suivants : **MODE ↔ SETUP**.
    - `SHIFT + BS10` → **ARP overlay**
    1er appui : MODE ; appuis suivants : **MODE ↔ SETUP**.
    - **Sortie overlay** : **premier appui** sur **BM1..BM8** (avec/sans SHIFT) → **ferme** l’overlay puis exécute le BM sur la cartouche **réelle**.
    - **Changement de cartouche réelle** (`SHIFT+BM1..BM4`) : **ferme** tout overlay actif avant bascule.
    Ces comportements remplacent les essais précédents et **stabilisent** la navigation.
    - **SHIFT+SEQ11 (KEYBOARD)** : si **KEY** est déjà affiché, l’action **quitte puis rouvre**
    la bannière pour **préserver `cart_name`** et reconstruire le **label** (`KEY ±N`).
    - **Contexte persistant** : un flag runtime `s_keys_active` conserve l’état **KEY actif**
    hors overlay ; à la **sortie de MUTE/PMUTE**, **LEDs KEYBOARD + label** sont restaurés si ce flag est vrai.
    ### 3. `ui_controller` — Cycles BM déclaratifs
    - Les cycles sont définis dans `ui_spec.h::cycles[]` (par cartouche).
    - Chargement/activation : `ui_init()` et `ui_switch_cart()`.
    - `resume=true` : retour au **dernier menu** du cycle ; `resume=false` : **premier menu** du cycle (utile pour overlays).
    **Cas standards utilisés :**
    - **BM6** : cycle des enveloppes (Filter/Amp/Pitch env).
    - **BM7** : cycle des Mods (ex. LFO1/LFO2/MIDI Mod) — bug de delta **corrigé** (les paramètres couvrent leur plage complète).
    - **BM8** : FX1→FX2→FX3→FX4 (`resume=true`).
    ### 4. `ui_spec.h` — Champ optionnel `overlay_tag`
    La structure `ui_cart_spec_t` inclut désormais un champ optionnel :
    Spécification UI complète d’une cartouche.
    Definition ui_spec.h:200
    void ui_mark_dirty(void)
    Definition ui_backend_test_stubs.c:76
    void ui_switch_cart(const ui_cart_spec_t *spec)
    Definition ui_backend_test_stubs.c:80
    void ui_init(const ui_cart_spec_t *spec)
    Initialise la couche UI avec la spécification fournie.
    Definition ui_controller.c:199
    ui_overlay_id_t
    Definition ui_overlay.h:25
    ui_custom_mode_t
    Definition ui_overlay.h:31
    void ui_overlay_set_custom_mode(ui_custom_mode_t mode)
    Definition ui_overlay_stub.c:41
    bool ui_overlay_is_active(void)
    Definition ui_overlay_stub.c:26
    void ui_overlay_enter(ui_overlay_id_t id, const ui_cart_spec_t *spec)
    Definition ui_overlay_stub.c:13
    void ui_overlay_switch_subspec(const ui_cart_spec_t *spec)
    Definition ui_overlay_stub.c:31
    ui_custom_mode_t ui_overlay_get_custom_mode(void)
    Definition ui_overlay_stub.c:46
    void ui_overlay_prepare_banner(const ui_cart_spec_t *src_mode, const ui_cart_spec_t *src_setup, const ui_cart_spec_t **dst_mode, const ui_cart_spec_t **dst_setup, const ui_cart_spec_t *prev_cart, const char *mode_tag)
    Definition ui_overlay_stub.c:51
    const ui_cart_spec_t * ui_overlay_get_spec(void)
    Definition ui_overlay_stub.c:36
    void ui_overlay_exit(void)
    Definition ui_overlay_stub.c:20
    c const char* overlay_tag; /* Tag visuel du mode custom actif, ex: "SEQ" */ ``

ValeurNULLpar défaut — les specs existantes restent **compatibles**.

  • Lors de l’utilisation d’un overlay,ui_overlay_prepare_banner` configure désormais les overrides visuels (nom/cart tag) pour que le renderer affiche le label (ex. “SEQ”) accolé au nom de cartouche sans dupliquer les specs.

5. Rendu (<tt>ui_renderer</tt>) — <strong>implémenté</strong>

  • Affichage du mode custom actif (overlay_tag) en 4×6 non inversé, sous le nom de cartouche (4×6 non inversé).
  • Si la spec active ne fournit pas de overlay_tag, le renderer utilise ui_backend_get_mode_label() (dernière valeur gérée par le backend, par défaut « SEQ » au démarrage).
  • Le titre du menu est centré dans un cadre à coins ouverts (voir Rendu (ui_renderer.c)*).
  • Invariants respectés : aucune logique d’état dans le renderer ; pas d’accès bus/driver hors drv_display/primitives.


Statut de la phase :

  • Navigation overlay stabilisée (SEQ/ARP).
  • Cycles BM fiabilisés (BM6, BM7, BM8).
  • Préparation du rendu (tag persistant) et des règles de pas en fonction du dernier mode custom actif.


📘 ANNEXE : Mise à jour Phase 5

✅ Architecture validée sans dépendance circulaire.

📦 Prochaine étape : création des futures UIs custom (ui_fx_ui, ui_drum_ui, etc.) sur le modèle SEQ/ARP.


✅ Mise à jour — <strong>Phase 6</strong>

🔧 Mise à jour (2025‑10‑13) — Cohérence <strong>SEQ UI/LED</strong> (Elektron‑like)

  • Suppression totale du focus violet (ancien « P‑Lock hold visuel »).
    Les steps maintenus ne changent plus de couleur.
  • Priorité des états LED SEQ (par step) :
    Playhead (blanc)Param‑only (bleu)Active/Recorded (vert)Off.
  • Param‑only = bleu : un step P‑Lock sans note (toutes vélocités = 0, au moins un param locké) s’affiche bleu.
  • Hold / Preview P‑Lock : le maintien d’un ou plusieurs steps sert uniquement à éditer les P‑Locks au(x) step(s) sélectionné(s) ;
    aucune couleur spécifique n’est rendue pendant le maintien. Le masque UI de preview est posé à l’appui et retiré au relâchement.
  • Quick Step / Quick Clear : tap court toggle immédiatement l’état du step (on/off) — comportement inchangé.
  • Playhead stable : latch anti‑double (premier tick post‑PLAY) pour éviter l’allumage simultané playhead+step précédent lors d’un redémarrage.
  • Threading / découplage : ui_led_seq reste mis à jour uniquement via ui_led_backend (aucune dépendance à clock_manager dans le renderer). (UIs custom + LEDs adressables)

Ajouts Phase 6½ (runtime Keyboard)

  • apps/ui_keyboard_app.c/.h : moteur notes/accords (inversions/extensions inspirées Orchid), API claire (`_note_on/off,*_chord_on/off,*_all_notes_off). -apps/kbd_chords_dict.c/.h: dictionnaire d’accords (intervalles relatifs root) + utilitaires de transposition par *Gamme/Root*. -apps/kbd_input_mapper.c/.h: mappingSEQ1..16→ actions note/accord + détection de combinaisons **Chord+Note** (ordre libre). -apps/ui_keyboard_bridge.c/.h: lecture **shadow UI** (Root/Gamme/Omni) → app+mapper+LEDs ; **émission directe** viaui_backend_note_on/off(). -ui/ui_backend.c: ajout **shadow UI** (espaceUI_DEST_UI) + APIs **NoteOn/Off/AllOff** + PANIC via **CC#123** ; routageMIDI_DEST_BOTH, canal par défaut **0**. -ui/ui_task.c: latence entrée **réduite** (poll 2 ms, priorité **NORMALPRIO**, yield 1 ms), synchro **à chaque itération** vers le bridge ; routing **SEQ1..16** verskbd_input_mapper_process(...)`.

Cette section récapitule les ajouts réalisés en Phase 6, sans modifier l’architecture de la Phase 5.

Nouveaux modules

  • ui/led/
    • ui_led_backend.c/.h : observateur passif de l’UI (aucune logique LED dans ui_task / ui_controller / ui_shortcuts). Pilote drv_leds_addr (format GRB).
    • ui_led_palette.h : palette centralisée des couleurs (C1..C4, REC, Playhead, Keyboard/Omnichord).
    • ui_led_seq.c/.h : renderer SEQ (playhead absolu, pages, priorités d’état, sans dépendre de clock_manager).
  • ui/seq/
  • ui/customs/
    • ui_keyboard_ui.c/.h : vitrine UI KEYBOARD (menu unique Mode avec 4 paramètres : Gamme, Root, Arp On/Off, Omnichord On/Off).
  • apps/

Raccourcis & overlays

  • SHIFT + SEQ11KEYBOARD (overlay vitrine).
    • Mise en place d’un banner clone : le nom affiché reste celui de la cart active (ex. XVA1), et le tag court "KEY" apparaît à droite (comme "SEQ" / "ARP").
  • MUTE actif (QUICK ou PMUTE) : tous les overlays sont bloqués (aucun SHIFT+SEQx ne s’active).

Comportement LEDs (unifié par <tt>ui_led_backend</tt>)

  • REC : OFF par défaut, ROUGE quand actif.
  • MUTE : les 16 steps ne s’allument que en mode MUTE.
    • Track mutéerouge (MUTE/PMUTE sans distinction visuelle).
    • Track activecouleur de sa cartouche (C1=bleu, C2=jaune, C3=violet, C4=cyan).
    • Aucun chenillard en MUTE (pas d’accent tick).
  • KEYBOARD (bleu froid) :
    • Omnichord OFF : layout scalaire ; SEQ1..8 = octave haute (bleu fort), SEQ9..16 = octave basse (bleu atténué).
    • Omnichord ON :
      • Chords area : SEQ1..4 & SEQ9..12 → 8 couleurs distinctes (palette dédiée).
      • Notes area : SEQ5..8 & SEQ13..16 → bleu (7 notes de la gamme + SEQ16 = octave haute de la root).
  • SEQ (séquenceur) :
    • Param‑only = bleu, Active = vert, Playhead = blanc, Off = éteint.
    • Playhead absolu qui avance sur toutes les pages (pages × 16), sans auto-changer la page visible.
    • Affichage stable : le pas courant est allumé plein (pas de pulse).
    • Pages : +/ (sans SHIFT) changent la page visible ; SHIFT + (+/−) = MUTE/PMUTE (prioritaire).
    • Longueur : défaut 4 pages (64 pas) ; ajustable via seq_led_bridge_set_max_pages(N).

Hook encodeur → LEDs (mise à jour immédiate)

  • Dans ui_controller.c, un hook met à jour instantanément le rendu LEDs lorsque le paramètre Omnichord (Off/On) de la vitrine Keyboard change :
    ui_led_backend_set_mode(UI_LED_MODE_KEYBOARD);
    ui_led_backend_set_keyboard_omnichord(on_off);

Arborescence — compléments

  • Ajouts par rapport à la table existante :
    • ui/led/ui_led_backend.*, ui_led_palette.h
    • ui/customs/ui_keyboard_ui.*

‍ℹ️ L’**ordre d’initialisation** reste identique ; la boucle principale continue d’appeler drv_leds_addr_render() pour rafraîchir les LEDs. ui_led_backend ne bloque pas le flux principal.

Depuis 2025‑10 : capture des boutons PLUS/MINUS pour piloter l’**octave shift** lorsque KEY est le contexte actif (overlay visible ou non) ; mise à jour du label bandeau en conséquence.

  • ui/customs/ui_keyboard_ui.* (menus Keyboard, page 2)
  • apps/ui_keyboard_app.*, kbd_input_mapper.*, kbd_chords_dict.*
  • ui/ui_shortcuts.* (mapping neutre → actions), ui_backend.* (contexte UI + effets secondaires)

🧪 Tests & lint

Les vérifications rapides à lancer avant une PR :

  • make — compilation firmware complète (nécessite le dépôt ChibiOS ../../chibios2111).
  • make lint-cppcheck — analyse statique (cppcheck) des dossiers core/ et ui/.
  • make check-host — exécute les tests hôtes (modèle SEQ, bridge hold/runtime, transitions UI, edge-cases), la régression ui_track_pmute_regression_tests (overlay Track + QUICK/PMute) via stubs LED/flash et les garde-fous quick-step / live capture (note fantôme) dans seq_hold_runtime_tests.
  • Gestion des menus, pages et paramètres de la vitrine UI.
  • Routage neutre des changements vers ui_backend_param_changed().
  • Hook direct sur les changements du paramètre Omnichord (Keyboard), avec mise à jour immédiate des LEDs via ui_led_backend_*.
  • Phase 6+ : activation automatique du mode LED SEQ au démarrage.

Architecture :

  • Ce contrôleur reste purement UI (aucune dépendance clock_manager).
  • Initialise les cycles BM (menus liés) depuis la ui_cart_spec_t.
  • Interface directe vers le backend LED pour initialisation de mode.

Documentation des fonctions

◆ ui_clear_dirty()

void ui_clear_dirty ( void  )

◆ ui_get_cart()

const ui_cart_spec_t * ui_get_cart ( void  )

◆ ui_get_state()

const ui_state_t * ui_get_state ( void  )

◆ ui_init()

void ui_init ( const ui_cart_spec_t spec)

Initialise la couche UI avec la spécification fournie.

  • Réinitialise les cycles BM.
  • Active le mode LED SEQ par défaut au boot (nouveau).

◆ ui_is_dirty()

bool ui_is_dirty ( void  )

◆ ui_mark_dirty()

void ui_mark_dirty ( void  )

◆ ui_on_button_menu()

void ui_on_button_menu ( int  index)

Gestion des boutons MENU (BM1..BM8).

◆ ui_on_button_page()

void ui_on_button_page ( int  index)

Gestion des boutons PAGE (1..5).

◆ ui_on_encoder()

void ui_on_encoder ( int  enc_index,
int  delta 
)

Gestion des encodeurs rotatifs (édition de paramètre).

  • UI_PARAM_CONT → variation continue dans la plage.
  • UI_PARAM_ENUM → incrément/décrément dans la liste d’options.
  • UI_PARAM_BOOL → toggle bitwise / booléen.

Hooks LED :

  • Si le paramètre correspond à KBD_OMNICHORD_ID, alors :

◆ ui_resolve_menu()

const ui_menu_spec_t * ui_resolve_menu ( uint8_t  bm_index)

◆ ui_switch_cart()

void ui_switch_cart ( const ui_cart_spec_t spec)

Changement de cartouche (reload complet de la spécification).