tawasuyu

tawasuyu/takiy

> takiy (Quechua: to sing). Music in Rust — synthesis, sequencing, playback — on Llimphi.

Rust 116Markdown 41TOML 192075 Definiciones

⎇ 5802bf9ee1f162194bcbfdc797293f2d20d829ae 02_ruway/takiy/takiy-app-llimphi/src/appmodel.rs Rust · 188 líneas · 9.9 KB Historia Quién tocó qué crudo

chore: refresco desde el monorepo (pin tawasuyu.git v0.3.0) 5802bf9ee · Sergio ·

1//! El `Model` de la app: el `EditorState` editable del lib más el estado
2//! de runtime del binario (player, sf2, drag, view rect cacheado…).
3
4use llimphi_theme::Theme;
5use llimphi_widget_toast::Toast;
6use takiy_app::EditorState;
7use takiy_playback::Player;
8use takiy_synth::MultiProgramRenderer;
9
10use crate::msg::{AutoHit, DragState};
11
12/// Pantalla activa de la app. El proyecto se abre en el **panorama** de
13/// pistas (tipo Audacity: una lista vertical de carriles horizontales);
14/// clickear un carril entra al **editor** de esa pista —el piano roll de
15/// siempre— y `Esc` vuelve al panorama.
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub(crate) enum Screen {
18    /// El wizard de arranque: las cinco maneras de empezar una canción.
19    /// Es la primera pantalla — ver `crate::inicio`.
20    Inicio,
21    /// Lista de pistas como carriles (cada uno en midi u onda).
22    Overview,
23    /// Editor de una pista (piano roll). El índice es la pista abierta;
24    /// coincide con `editor.active_track`.
25    Track,
26    /// Pensar la canción desde acordes y ritmos (`takiy-acordes`): paleta
27    /// de la tonalidad, cinta de acordes y editor de patrón rítmico.
28    Acordes,
29}
30
31/// Estado del **modo grabación**: el teclado alfabético toca y graba MIDI
32/// en una pista, opcionalmente con las demás pistas sonando de fondo.
33pub(crate) struct RecState {
34    /// Pista destino de la grabación.
35    pub(crate) track: usize,
36    /// Origen de tiempo (beat 0 de la toma). El beat actual se deriva del
37    /// reloj real: `(now - started_at) * bpm / 60`.
38    pub(crate) started_at: std::time::Instant,
39    /// BPM congelado al arrancar la toma (define el mapeo tiempo→beat).
40    pub(crate) bpm: f32,
41    /// Si las demás pistas suenan de fondo mientras se graba.
42    pub(crate) backing: bool,
43    /// Octava base del mapeo de teclado (fila inferior = esta octava).
44    pub(crate) base_octave: i32,
45    /// Notas con tecla apretada ahora: `midi → beat de inicio`. Se cierran
46    /// (se graban) al soltar la tecla.
47    pub(crate) held: std::collections::HashMap<u8, f32>,
48    /// Cantidad de notas grabadas en la toma (para la UI).
49    pub(crate) count: usize,
50    /// Último beat conocido (lo refresca `Tick` para el cabezal/HUD).
51    pub(crate) last_beat: f32,
52}
53
54/// Estado de un arrastre sobre la cinta de secciones: el ancho de la tira
55/// (para convertir px→beats), el largo total, el límite agarrado (si el
56/// press cayó cerca de uno), su beat original, la sección bajo el cursor (para
57/// abrir el editor si fue un tap) y si hubo movimiento real.
58#[derive(Debug, Clone, Copy)]
59pub(crate) struct SecDrag {
60    pub(crate) rw: f32,
61    pub(crate) total: f32,
62    pub(crate) boundary: Option<usize>,
63    pub(crate) orig_beat: f32,
64    pub(crate) section: usize,
65    pub(crate) moved: bool,
66}
67
68pub(crate) struct Model {
69    pub(crate) editor: EditorState,
70    /// Estado de la pantalla Acordes. Vive siempre (es barato) para que
71    /// entrar y salir no pierda la progresión en la que se venía trabajando.
72    pub(crate) acordes: crate::acordes::Estado,
73    pub(crate) source: String,
74    pub(crate) theme: Theme,
75    pub(crate) player: Option<Player>,
76    pub(crate) sf2: Option<MultiProgramRenderer>,
77    pub(crate) engine: String,
78    pub(crate) playing: bool,
79    pub(crate) status: String,
80    /// BPM con el que se lanzó el render actual. Se congela en `TogglePlay`:
81    /// si cambia el tempo durante la reproducción, el cursor avanza a la
82    /// velocidad del render real (no al BPM editado).
83    pub(crate) playback_bpm: f32,
84    /// Última dimensión conocida del view raíz. La cacheamos del último
85    /// `PressAt` para que `DragNote` pueda convertir píxeles a beats sin
86    /// que llimphi-ui le pase el rect del nodo en cada fase del drag.
87    pub(crate) last_rect: Option<(f32, f32)>,
88    /// Drag-to-move en curso. `None` cuando no hay drag activo.
89    pub(crate) drag: Option<DragState>,
90    /// Hit-test pendiente de un dot de automación: el press lo detecta
91    /// y el primer evento `Move` del drag lo consume para arrancar un
92    /// `DragMode::Automation`. Se limpia al final del drag o si llega
93    /// otro press sin hit.
94    pub(crate) auto_pending: Option<AutoHit>,
95    /// Offset global del rango MIDI visible (en semitonos). Lo mueve la
96    /// rueda del mouse — `pitch_range_with_offset` lo aplica.
97    pub(crate) midi_offset: i32,
98    /// Último Instant en el que se disparó un blip de audition. Sirve
99    /// para throttlear repeticiones rápidas (autorepeat de flechas,
100    /// re-selecciones múltiples) y evitar saturar el device.
101    pub(crate) last_audition_at: Option<std::time::Instant>,
102    /// Barra de menú principal: índice del menú raíz abierto (`None`
103    /// cerrado). Lo abre/cierra `menubar_view` vía `Msg::MenuOpen`.
104    pub(crate) menu_open: Option<usize>,
105    /// Fila resaltada por teclado dentro del dropdown del menú principal
106    /// (`usize::MAX` = ninguna). La mueven las flechas ↑/↓.
107    pub(crate) menu_active: usize,
108    /// Animación de aparición/swap del dropdown del menú principal (0→1).
109    pub(crate) menu_anim: llimphi_motion::Tween<f32>,
110    /// Menú contextual sobre la nota seleccionada: ancla `(x, y)` en
111    /// coords de ventana. `None` cerrado. Sólo se abre con right-click
112    /// cuando hay una nota seleccionada y el click no cayó sobre un
113    /// objeto borrable (nota/dot) — esos siguen borrándose directo.
114    pub(crate) context_menu: Option<(f32, f32)>,
115    /// Estado del **sidebar unificado IZQUIERDO** (widget `rag-sidebar`): el
116    /// selector de proyectos. Es un selector **fijo** — un diente por proyecto,
117    /// el diente abierto sigue a [`Model::proy_activo`] (nunca colapsa); los ejes
118    /// de disposición, el buscador y el ancho del panel los reduce
119    /// `RagSidebarState::update`.
120    pub(crate) sidebar_left: llimphi_widget_rag_sidebar::RagSidebarState,
121    /// Estado del **sidebar unificado DERECHO** (widget `rag-sidebar`): el dock
122    /// de paneles (instrumento/efectos/tonalidad/automación). Es open/collapse:
123    /// `open` es el `DockItem` desplegado (o `None` = sólo el rail).
124    pub(crate) sidebar_right: llimphi_widget_rag_sidebar::RagSidebarState,
125    /// Pantalla activa: panorama de pistas o editor de una pista.
126    pub(crate) screen: Screen,
127    /// Selección de tiempo `[from, to)` en beats sobre la onda de la pista
128    /// abierta (editor de onda). `None` = sin selección → las ops aplican
129    /// a toda la pista. La fija el drag horizontal sobre la forma de onda.
130    pub(crate) wave_sel: Option<(f32, f32)>,
131    /// Modo grabación activo (`Some`) o no. Mientras está activo, el
132    /// teclado alfabético toca y graba MIDI en `RecState.track`.
133    pub(crate) recording: Option<RecState>,
134    /// Proyectos abiertos (como en pluma): cada uno es un DAG de versiones
135    /// de un `Score`. La working copy del proyecto activo se sincroniza
136    /// con `editor.score`. El rail izquierdo lista un diente por proyecto
137    /// + uno para «abrir».
138    pub(crate) proyectos: Vec<takiy_proyecto::Proyecto>,
139    /// Índice del proyecto activo (el que se está editando).
140    pub(crate) proy_activo: usize,
141    /// Directorio donde se guardan los `.takiyproj`.
142    pub(crate) proy_dir: std::path::PathBuf,
143    /// Sección «Versiones» del sidebar desplegada.
144    pub(crate) ver_versiones: bool,
145    /// Sección «Pistas» del sidebar desplegada.
146    pub(crate) ver_pistas: bool,
147    /// Caché de picos de onda por índice de pista, para el carril en
148    /// modo `Onda`. Cada `Vec<f32>` es un perfil normalizado `[0, 1]`
149    /// de resolución fija (`ONDA_PEAK_BUCKETS`) que el painter remapea
150    /// al ancho real del carril. Se recalcula al entrar al panorama y
151    /// al pasar una pista a onda — no en cada frame (el render de audio
152    /// es caro). Ver `crate::overview::compute_onda_peaks`.
153    pub(crate) onda_peaks: std::collections::HashMap<usize, Vec<f32>>,
154    /// Toasts efímeros vivos (confirmaciones de export/guardado, errores).
155    /// La `view_overlay` filtra los expirados; `Msg::ToastExpire` los retira.
156    pub(crate) toasts: Vec<Toast>,
157    /// Id incremental para correlacionar un toast con su `Msg::ToastExpire`.
158    pub(crate) next_toast: u64,
159    /// Formulario del panel **Génesis** (componer con IA) abierto, o `None`.
160    /// Mientras es `Some`, el modal captura el teclado.
161    pub(crate) genesis: Option<crate::genesis::GenesisForm>,
162    /// Grabación de **voz** en curso (micrófono), o `None`. Mientras es
163    /// `Some`, el HUD de captura flota sobre la pantalla.
164    pub(crate) voz: Option<crate::voz::VozRec>,
165    /// Modal **Instrumento IA** abierto (prosa → patch), o `None`. Mientras
166    /// es `Some`, captura el teclado como el de Génesis.
167    pub(crate) instr: Option<crate::instr::InstrForm>,
168    /// Modal **editar sección** abierto (renombrar/borrar/insertar), o `None`.
169    pub(crate) secedit: Option<crate::secedit::SectionEditForm>,
170    /// Arrastre en curso sobre la cinta de secciones, o `None`.
171    pub(crate) sec_drag: Option<SecDrag>,
172    /// Texto acumulado del LLM mientras genera (streaming), o `None`. Mientras
173    /// es `Some`, un cartelito de progreso flota sobre la pantalla.
174    pub(crate) llm_progress: Option<String>,
175    /// Backend del LLM elegido para la IA. `None` = autodetectar por env.
176    pub(crate) llm_backend: Option<takiy_genesis_llm::BackendKind>,
177    /// Modelo concreto del backend. `None` = default del backend.
178    pub(crate) llm_model: Option<String>,
179    /// Modal **importar WAV por ruta** abierto, o `None`. Mientras es `Some`,
180    /// captura el teclado como los otros modales.
181    pub(crate) wavimport: Option<crate::wavimport::WavImportForm>,
182    /// Modal **Escuchar** (dos escuchas IA sobre la pista activa) abierto, o
183    /// `None`. Mientras es `Some`, captura el teclado como los otros modales.
184    pub(crate) escucha: Option<crate::escucha::EscuchaForm>,
185    /// Estado del wizard de arranque (`Screen::Inicio`). Vive siempre: es
186    /// una pantalla, no un modal, y sus perillas se conservan al ir y venir.
187    pub(crate) inicio: crate::inicio::InicioForm,
188}