tawasuyu/takiy
> takiy (Quechua: to sing). Music in Rust — synthesis, sequencing, playback — on Llimphi.
⎇ 5802bf9ee1f162194bcbfdc797293f2d20d829ae
02_ruway/takiy/takiy-app-llimphi/src/appmodel.rs
Historia
Quién tocó qué
crudo
chore: refresco desde el monorepo (pin tawasuyu.git v0.3.0)
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}