mandioca-tabs-mcp 0.2.17 → 0.2.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/index.mjs +1934 -1262
  2. package/package.json +1 -1
package/index.mjs CHANGED
@@ -12336,12 +12336,13 @@ function migrarMarcasPorGrado(guardadas, pintarGradosViejo) {
12336
12336
  }
12337
12337
  return salida;
12338
12338
  }
12339
- var DocumentoTabs;
12339
+ var CLAVE_DE_LA_SESION, DocumentoTabs;
12340
12340
  var init_documento = __esm({
12341
12341
  "packages/model/src/documento.ts"() {
12342
12342
  init_yjs();
12343
12343
  init_src();
12344
12344
  init_tipos();
12345
+ CLAVE_DE_LA_SESION = "sesion";
12345
12346
  DocumentoTabs = class {
12346
12347
  doc;
12347
12348
  cabecera;
@@ -16458,6 +16459,13 @@ var init_moverNotas = __esm({
16458
16459
  });
16459
16460
 
16460
16461
  // packages/editor/src/atajos.ts
16462
+ function comoSeLee(combinacion) {
16463
+ const partes = combinacion.split("+");
16464
+ if (combinacion === "+") return "+";
16465
+ const ultima = partes.pop() ?? "";
16466
+ const legible = COMO_SE_LEE[ultima] ?? (ultima.length === 1 ? ultima.toUpperCase() : ultima);
16467
+ return [...partes.map((m) => MODIFICADORAS[m] ?? m), legible].join(" + ");
16468
+ }
16461
16469
  function combinacionesDeLasTablas() {
16462
16470
  return [
16463
16471
  ...Object.keys(MOVIMIENTOS_POR_TECLA),
@@ -16465,7 +16473,7 @@ function combinacionesDeLasTablas() {
16465
16473
  ...Object.keys(ACCIONES_CON_MAYUSCULAS).map(conMayusculas)
16466
16474
  ];
16467
16475
  }
16468
- var conMayusculas, COMANDOS_DE_MENU, COMANDOS_DE_AUDIO, NOMBRE_DE_LA_FIGURA, ATAJOS;
16476
+ var conMayusculas, COMANDOS_DE_MENU, COMANDOS_DE_AUDIO, NOMBRE_DE_LA_FIGURA, ATAJOS, COMO_SE_LEE, MODIFICADORAS;
16469
16477
  var init_atajos = __esm({
16470
16478
  "packages/editor/src/atajos.ts"() {
16471
16479
  init_efectos();
@@ -16496,6 +16504,16 @@ var init_atajos = __esm({
16496
16504
  { id: "voz-3", nombre: "Escribir en la voz 3", grupo: "Escribir notas" },
16497
16505
  { id: "voz-4", nombre: "Escribir en la voz 4", grupo: "Escribir notas" },
16498
16506
  { id: "voz-siguiente", nombre: "Pasar a la voz siguiente", grupo: "Escribir notas" },
16507
+ // ★ Las dos puertas a las paletas que NO piden sostener nada (informe «Navegación de paletas
16508
+ // con teclado», 2026-11-11). Sin tecla de fábrica: el informe pide un comando reasignable,
16509
+ // y cualquier tecla libre que se eligiera acá le quitaría una a quien ya la usa para otra cosa.
16510
+ // · `paleta-con-teclado` deja ENGANCHADO el modo del Tab sostenido: para quien no puede
16511
+ // sostener teclas (temblor, una mano, teclado en pantalla). Sticky Keys de Windows sólo
16512
+ // engancha modificadores, y Tab no lo es. Se sale con Esc o con el mismo comando.
16513
+ // · `buscar-en-paletas` es la búsqueda de las siete a la vez: un combobox estándar, que es
16514
+ // además el camino principal para un lector de pantalla.
16515
+ { id: "paleta-con-teclado", nombre: "Elegir en la paleta con el teclado, sin sostener Tab", grupo: "Escribir notas" },
16516
+ { id: "buscar-en-paletas", nombre: "Buscar en las paletas\u2026", grupo: "Escribir notas" },
16499
16517
  // ── ⛔⛔ EL MENÚ DE LA LÍNEA DIVISORIA, que faltaba (2026-09-01) ───────────────
16500
16518
  //
16501
16519
  // Estas cuatro se construyeron el 2026-09-01 con su menú, su modelo y sus cuatro
@@ -16519,6 +16537,12 @@ var init_atajos = __esm({
16519
16537
  // ★ La sección (2026-09-13): el menú Compás la abre desde el cursor, y entra al catálogo el
16520
16538
  // mismo día —asignable, sin tecla de fábrica—, que es la regla de toda acción nueva.
16521
16539
  { id: "compas-seccion", nombre: "Secci\xF3n en un comp\xE1s\u2026", grupo: "Compases" },
16540
+ // ★ Repeticiones, casillas y saltos a mano (2026-11-11, función 3 de la competencia). Entran
16541
+ // el mismo día que el menú, sin tecla de fábrica y asignables. Los nombres llevan las
16542
+ // palabras que se buscan («casilla», «coda», «segno», «fine»): el buscador lee el nombre.
16543
+ { id: "compas-repeticion", nombre: "Barras de repetici\xF3n en un comp\xE1s o pasaje\u2026", grupo: "Compases" },
16544
+ { id: "compas-casillas", nombre: "Casillas de repetici\xF3n (1.\xAA, 2.\xAA\u2026)\u2026", grupo: "Compases" },
16545
+ { id: "compas-saltos", nombre: "Saltos y destinos: D.C., D.S., Segno, Coda, Fine\u2026", grupo: "Compases" },
16522
16546
  { id: "borrar-compases-desde", nombre: "Borrar de un comp\xE1s en adelante", grupo: "Compases" },
16523
16547
  { id: "borrar-compases-hasta", nombre: "Borrar de un comp\xE1s para atr\xE1s", grupo: "Compases" },
16524
16548
  // ── EL ASPECTO DE LA OBRA (2026-09-01) ────────────────────────────
@@ -16657,11 +16681,29 @@ var init_atajos = __esm({
16657
16681
  // teclado (ver `accionDe`).
16658
16682
  { id: "paleta-siguiente", nombre: "Pasar a la siguiente paleta (abre el panel de edici\xF3n si est\xE1 cerrado)", grupo: "Escribir notas", teclas: ["Tab"], reasignable: true },
16659
16683
  // ★ 2026-11-03. Tab tocado pasa de paleta; Tab SOSTENIDO en la de acordes le da las flechas.
16684
+ {
16685
+ // ★ Desde el 2026-11-11, en las siete paletas y con la misma gramática. Ver
16686
+ // `paletasConTeclado.ts`.
16687
+ id: "paleta-con-tab",
16688
+ nombre: "Con Tab sostenido en cualquier paleta: \u2190 \u2192 elegir, \u2193 o Intro escribir, \u2191 quitar, letras para buscar, Esc para salir",
16689
+ grupo: "Escribir notas",
16690
+ teclas: ["Tab + \u2190 \u2192", "Tab + \u2193", "Tab + \u2191", "Tab + letras", "Tab + Esc"],
16691
+ reasignable: false,
16692
+ porQueNo: "Va con las flechas, que marcan la direcci\xF3n; Tab se reasigna en \xABPasar a la siguiente paleta\xBB."
16693
+ },
16694
+ {
16695
+ id: "paleta-con-tab-posicion",
16696
+ nombre: "Con Tab sostenido en Figura o Matiz: 1 a 9 eligen la ficha por su lugar",
16697
+ grupo: "Escribir notas",
16698
+ teclas: ["Tab + 1 \u2026 9"],
16699
+ reasignable: false,
16700
+ porQueNo: "Los d\xEDgitos son la posici\xF3n de la ficha, como sin Tab son el traste."
16701
+ },
16660
16702
  {
16661
16703
  id: "acorde-con-tab",
16662
- nombre: "Con Tab sostenido en la paleta de acordes: \u2190 \u2192 elegir, \u2193 o Intro escribirlo (Ctrl: s\xF3lo las notas), \u2191 quitarlo",
16704
+ nombre: "Con Tab sostenido en la paleta de acordes: Ctrl + \u2193 escribe s\xF3lo las notas",
16663
16705
  grupo: "Escribir notas",
16664
- teclas: ["Tab + \u2190 \u2192", "Tab + \u2193", "Tab + Ctrl + \u2193", "Tab + \u2191"],
16706
+ teclas: ["Tab + Ctrl + \u2193"],
16665
16707
  reasignable: false,
16666
16708
  porQueNo: "Va con las flechas, que marcan la direcci\xF3n; Tab se reasigna en \xABPasar a la siguiente paleta\xBB."
16667
16709
  },
@@ -16790,6 +16832,20 @@ var init_atajos = __esm({
16790
16832
  // toda la ventana— y por eso todavía no se reasigna.
16791
16833
  { id: "info", nombre: "Mostrar u ocultar la ventana de info", grupo: "Vista y pistas", teclas: ["?"], reasignable: false, porQueNo: "Responde en toda la ventana, tambi\xE9n fuera del pliego: cambiarlo es otro trabajo." }
16792
16834
  ];
16835
+ COMO_SE_LEE = {
16836
+ " ": "Espacio",
16837
+ ArrowUp: "\u2191",
16838
+ ArrowDown: "\u2193",
16839
+ ArrowLeft: "\u2190",
16840
+ ArrowRight: "\u2192",
16841
+ Delete: "Supr",
16842
+ Backspace: "Retroceso",
16843
+ Home: "Inicio",
16844
+ End: "Fin",
16845
+ Enter: "Entrar",
16846
+ Escape: "Esc"
16847
+ };
16848
+ MODIFICADORAS = { Shift: "May" };
16793
16849
  }
16794
16850
  });
16795
16851
 
@@ -20612,9 +20668,207 @@ var init_acordes = __esm({
20612
20668
  });
20613
20669
 
20614
20670
  // packages/editor/src/miraDeAcordes.ts
20671
+ var MIRA_DEDUCIR;
20615
20672
  var init_miraDeAcordes = __esm({
20616
20673
  "packages/editor/src/miraDeAcordes.ts"() {
20617
20674
  init_acordes();
20675
+ MIRA_DEDUCIR = "+deducir";
20676
+ }
20677
+ });
20678
+
20679
+ // packages/editor/src/paletasConTeclado.ts
20680
+ function fichasFijas(familia) {
20681
+ switch (familia) {
20682
+ case "figura":
20683
+ return [
20684
+ ...FIGURAS.map((f) => ({ id: `figura-${f.valor}`, nombre: f.nombre, atajo: `figura-${f.valor}` })),
20685
+ { id: "puntillo", nombre: "puntillo", atajo: "puntillo" },
20686
+ { id: "silencio", nombre: "silencio", atajo: "silencio" }
20687
+ ].map(conSinonimos);
20688
+ case "tecnica":
20689
+ return [
20690
+ ...EFECTOS_EN_ORDEN.map((e) => ({ id: `efecto-${e}`, nombre: EFECTOS[e].nombre, atajo: `efecto-${e}` })),
20691
+ ...FORMAS_EN_ORDEN.map((f) => ({
20692
+ id: `bend-${f}`,
20693
+ nombre: FORMAS_DE_BEND[f].nombre,
20694
+ ...f === "bend" ? { atajo: "bend" } : {}
20695
+ })),
20696
+ ...ALTURAS_DE_BEND.map((a) => ({ id: `sube-${a.cuartos}`, nombre: `sube ${a.nombre.toLowerCase()}` })),
20697
+ { id: "curva", nombre: "curva propia" }
20698
+ ].map(conSinonimos);
20699
+ case "signo":
20700
+ return GRUPOS_EN_ORDEN.flatMap(
20701
+ (g) => signosDelGrupo(g).map((s) => ({ id: `signo-${s.clave}`, nombre: s.nombre, atajo: `signo-${s.clave}` }))
20702
+ ).map(conSinonimos);
20703
+ case "matiz":
20704
+ return MATICES.map((m) => {
20705
+ const [italiano = "", resto = ""] = m.detalle.split("\u2014");
20706
+ const castellano = resto.split(".")[0] ?? "";
20707
+ return {
20708
+ id: `matiz-${m.nombre}`,
20709
+ nombre: m.nombre,
20710
+ sinonimos: [italiano.trim(), castellano.trim()].filter(Boolean),
20711
+ atajo: `comando-matiz-${m.nombre}`
20712
+ };
20713
+ });
20714
+ case "kit":
20715
+ return KIT.map((p) => conSinonimos({ id: `kit-${p.indice}`, nombre: p.nombre }));
20716
+ case "acorde":
20717
+ case "seccion":
20718
+ return [];
20719
+ }
20720
+ }
20721
+ function fichasDeAcordes(usados) {
20722
+ return [
20723
+ { id: MIRA_DEDUCIR, nombre: "+ acorde", sinonimos: ["a\xF1adir", "deducir", "el del golpe"] },
20724
+ ...usados.map((a) => ({ id: a.clave, nombre: a.letra ? `${a.nombre} ${a.letra}` : a.nombre }))
20725
+ ];
20726
+ }
20727
+ function fichasDeSecciones(nombres) {
20728
+ const vistos = /* @__PURE__ */ new Set();
20729
+ const unicos = [];
20730
+ for (const n of nombres) {
20731
+ const limpio = n.trim();
20732
+ if (limpio && !vistos.has(limpio)) {
20733
+ vistos.add(limpio);
20734
+ unicos.push(limpio);
20735
+ }
20736
+ }
20737
+ return [
20738
+ { id: SECCION_NUEVA, nombre: "nombre nuevo", sinonimos: ["escribir", "otra"] },
20739
+ ...unicos.map((n) => ({ id: `seccion:${n}`, nombre: n }))
20740
+ ];
20741
+ }
20742
+ function normalizarBusqueda(texto4) {
20743
+ return texto4.toLowerCase().normalize("NFD").replace(/[̀-ͯ]/g, "").replace(/\s+/g, " ").trim();
20744
+ }
20745
+ function puntajeDeBusqueda(ficha, texto4) {
20746
+ const q = normalizarBusqueda(texto4);
20747
+ if (!q) return null;
20748
+ const nombre = normalizarBusqueda(ficha.nombre);
20749
+ const otros = (ficha.sinonimos ?? []).map(normalizarBusqueda);
20750
+ if (nombre === q) return -1;
20751
+ if (nombre.startsWith(q)) return 0;
20752
+ if (otros.some((s) => s.startsWith(q))) return 1;
20753
+ const palabras = [nombre, ...otros].flatMap((t) => t.split(/[\s(),/-]+/));
20754
+ if (palabras.some((p) => p.startsWith(q))) return 2;
20755
+ if (nombre.includes(q) || otros.some((s) => s.includes(q))) return 3;
20756
+ return null;
20757
+ }
20758
+ function buscarEnLasPaletas(paletas, texto4, limite = 12) {
20759
+ if (!normalizarBusqueda(texto4)) return [];
20760
+ const todos = [];
20761
+ let orden = 0;
20762
+ for (const { familia, fichas } of paletas) {
20763
+ for (const ficha of fichas) {
20764
+ const p = puntajeDeBusqueda(ficha, texto4);
20765
+ if (p !== null) todos.push({ familia, ficha, puntaje: p, orden });
20766
+ orden++;
20767
+ }
20768
+ }
20769
+ return todos.sort((a, b) => a.puntaje - b.puntaje || a.orden - b.orden).slice(0, limite).map(({ familia, ficha, puntaje: p }) => ({ familia, ficha, puntaje: p }));
20770
+ }
20771
+ var NOMBRE_DE_LA_FAMILIA, SINONIMOS, conSinonimos, FIGURAS, SECCION_NUEVA;
20772
+ var init_paletasConTeclado = __esm({
20773
+ "packages/editor/src/paletasConTeclado.ts"() {
20774
+ init_efectos();
20775
+ init_bend();
20776
+ init_variantes();
20777
+ init_signos();
20778
+ init_dinamicas();
20779
+ init_miraDeAcordes();
20780
+ init_src2();
20781
+ NOMBRE_DE_LA_FAMILIA = {
20782
+ figura: "Figura",
20783
+ tecnica: "T\xE9cnica",
20784
+ signo: "Signo",
20785
+ acorde: "Acorde",
20786
+ kit: "Bater\xEDa",
20787
+ matiz: "Matiz",
20788
+ seccion: "Secci\xF3n"
20789
+ };
20790
+ SINONIMOS = {
20791
+ // Figuras
20792
+ "figura-1": ["entera", "whole"],
20793
+ "figura-2": ["mitad", "half"],
20794
+ "figura-4": ["cuarto", "quarter"],
20795
+ "figura-8": ["octavo", "eighth"],
20796
+ "figura-16": ["dieciseisavo", "sixteenth"],
20797
+ "figura-32": ["treintaidosavo", "thirty-second"],
20798
+ "figura-64": ["sesentaicuatroavo", "sixty-fourth"],
20799
+ puntillo: ["punto", "dot"],
20800
+ silencio: ["pausa", "rest"],
20801
+ // Técnicas
20802
+ "efecto-martillo": ["ligado", "hammer", "hammer-on", "pull-off"],
20803
+ "efecto-deslizamiento": ["slide", "glissando", "arrastre"],
20804
+ "efecto-muerta": ["apagada", "dead note", "muted"],
20805
+ "efecto-palmMute": ["muteado", "apagado con la palma", "pm"],
20806
+ "efecto-letRing": ["dejar sonar", "que suene"],
20807
+ "efecto-vibrato": ["temblor"],
20808
+ "efecto-fantasma": ["ghost", "nota fantasma"],
20809
+ "efecto-armonico": ["harmonic", "flageolet"],
20810
+ "efecto-staccato": ["staccato", "corto"],
20811
+ "efecto-ligadaAAnterior": ["ligadura de prolongaci\xF3n", "tie", "prolongar"],
20812
+ "bend-bend": ["estirar", "estirada", "bending"],
20813
+ "bend-bendVuelta": ["release", "estirar y soltar"],
20814
+ "bend-prebend": ["pre bend", "ya estirada"],
20815
+ "bend-prebendVuelta": ["pre bend release"],
20816
+ curva: ["curva de bend", "dibujar"],
20817
+ // Signos
20818
+ "signo-acento": ["accent", "marcato"],
20819
+ "signo-ligaduraDeExpresion": ["ligado", "slur", "legato"],
20820
+ "signo-calderon": ["fermata", "corona"],
20821
+ "signo-pedal": ["sustain", "pedal"],
20822
+ "signo-anchoDeVibrato": ["vibrato"],
20823
+ "signo-vibratoDePalanca": ["whammy", "tremolo bar", "palanca"],
20824
+ "signo-tappingIzquierda": ["tap"],
20825
+ "signo-dedoIzquierda": ["digitacion", "dedo", "fingering"],
20826
+ "signo-cejilla": ["barre", "cejuela", "capo"],
20827
+ "signo-puaDireccion": ["pick", "plectro", "downstroke", "upstroke", "direccion"],
20828
+ "signo-rasgueo": ["arpegio", "strum", "brush"],
20829
+ "signo-rasgueado": ["flamenco"],
20830
+ "signo-slap": ["bajo"],
20831
+ "signo-pop": ["bajo"],
20832
+ "signo-tapping": ["tap"],
20833
+ "signo-slapMuerto": ["dead slap"],
20834
+ "signo-golpeEnLaTapa": ["percusion", "tapa"],
20835
+ "signo-dedoDerecha": ["pima", "digitacion", "dedo"],
20836
+ "signo-adorno": ["grace", "appoggiatura", "acciaccatura", "mordente"],
20837
+ "signo-tremolo": ["tremolo picking", "tremolo"],
20838
+ "signo-trino": ["trill"],
20839
+ "signo-regulador": ["crescendo", "decrescendo", "hairpin"],
20840
+ "signo-fundido": ["fade", "volume swell"],
20841
+ "signo-wah": ["wah"],
20842
+ "signo-alteracion": ["sostenido", "bemol", "becuadro", "accidental"],
20843
+ "signo-muestraCuerda": ["cuerda", "string"],
20844
+ "signo-barraOblicua": ["slash", "ritmo"],
20845
+ "signo-plicaInvertida": ["plica", "stem"],
20846
+ "signo-barrado": ["beam", "corchete"],
20847
+ // Batería
20848
+ "kit-0": ["kick", "bass drum"],
20849
+ "kit-1": ["snare", "redoblante", "tarola"],
20850
+ "kit-2": ["rimshot", "aro"],
20851
+ "kit-3": ["tom", "high tom"],
20852
+ "kit-4": ["tom", "mid tom"],
20853
+ "kit-5": ["floor tom", "tom de pie"],
20854
+ "kit-6": ["hi-hat", "hihat", "contratiempo", "hh"],
20855
+ "kit-7": ["hi-hat abierto", "hihat abierto"],
20856
+ "kit-8": ["hi-hat pedal", "pie"],
20857
+ "kit-9": ["platillo", "crash"],
20858
+ "kit-10": ["platillo", "ride"],
20859
+ "kit-11": ["bell", "campana"]
20860
+ };
20861
+ conSinonimos = (f) => SINONIMOS[f.id] ? { ...f, sinonimos: SINONIMOS[f.id] } : f;
20862
+ FIGURAS = [
20863
+ { valor: 1, nombre: "redonda" },
20864
+ { valor: 2, nombre: "blanca" },
20865
+ { valor: 4, nombre: "negra" },
20866
+ { valor: 8, nombre: "corchea" },
20867
+ { valor: 16, nombre: "semicorchea" },
20868
+ { valor: 32, nombre: "fusa" },
20869
+ { valor: 64, nombre: "semifusa" }
20870
+ ];
20871
+ SECCION_NUEVA = "+nueva";
20618
20872
  }
20619
20873
  });
20620
20874
 
@@ -20823,9 +21077,11 @@ var init_instrumentos = __esm({
20823
21077
  programaMidi: PROGRAMAS.guitarraNylon
20824
21078
  },
20825
21079
  // ── Teclados ──
21080
+ // ★ (2026-11-11) Los tres en DOS pentagramas bajo una llave, como se escriben: el órgano también
21081
+ // (antes salía en uno, sin llave). Ver `verificar-llave-del-piano`.
20826
21082
  { clave: "piano", nombre: "Piano", nombreCorto: "pno", grupo: "Teclados", tipo: "altura", afinacion: [], programaMidi: PROGRAMAS.piano, pentagramas: 2 },
20827
21083
  { clave: "piano-electrico", nombre: "Piano el\xE9ctrico", nombreCorto: "e.pno", grupo: "Teclados", tipo: "altura", afinacion: [], programaMidi: PROGRAMAS.pianoElectrico, pentagramas: 2 },
20828
- { clave: "organo", nombre: "\xD3rgano", nombreCorto: "org", grupo: "Teclados", tipo: "altura", afinacion: [], programaMidi: PROGRAMAS.organo },
21084
+ { clave: "organo", nombre: "\xD3rgano", nombreCorto: "org", grupo: "Teclados", tipo: "altura", afinacion: [], programaMidi: PROGRAMAS.organo, pentagramas: 2 },
20829
21085
  // ── Vientos ──
20830
21086
  { clave: "trompeta", nombre: "Trompeta", nombreCorto: "tpt", grupo: "Vientos", tipo: "altura", afinacion: [], programaMidi: PROGRAMAS.trompeta },
20831
21087
  { clave: "trombon", nombre: "Tromb\xF3n", nombreCorto: "tbn", grupo: "Vientos", tipo: "altura", afinacion: [], programaMidi: PROGRAMAS.trombon },
@@ -21301,6 +21557,18 @@ var init_menus = __esm({
21301
21557
  principiante: "Borra la nota que est\xE1 bajo el cursor. El tiempo que ocupaba queda en su lugar, como silencio, as\xED que el comp\xE1s no se corre.",
21302
21558
  profesional: "Borra la nota; el golpe queda."
21303
21559
  },
21560
+ "menu-buscar-en-paletas": {
21561
+ nombre: "Buscar en las paletas\u2026",
21562
+ accion: "comando-buscar-en-paletas",
21563
+ principiante: "Abre un buscador que mira las siete paletas a la vez. Escribe el nombre de un signo, una t\xE9cnica, una figura o un matiz, y elige con las flechas. Sirve cuando no sabes en qu\xE9 paleta est\xE1 algo.",
21564
+ profesional: "B\xFAsqueda global de paletas."
21565
+ },
21566
+ "menu-paleta-con-teclado": {
21567
+ nombre: "Paleta con el teclado, sin sostener Tab",
21568
+ accion: "comando-paleta-con-teclado",
21569
+ principiante: "Hace lo mismo que mantener Tab apretado, pero sin tener que sostenerlo: las flechas eligen en la paleta, \u2193 escribe, \u2191 quita y las letras buscan. Sirve si sostener una tecla te cuesta. Para salir, Esc o este mismo comando.",
21570
+ profesional: "Modo fijo del Tab sostenido; Esc o el mismo comando salen."
21571
+ },
21304
21572
  "menu-borrar-figura": {
21305
21573
  nombre: "Borrar la figura entera",
21306
21574
  accion: "borrar-figura",
@@ -21383,6 +21651,24 @@ var init_menus = __esm({
21383
21651
  principiante: "Le pone un nombre a la parte que empieza en el comp\xE1s del cursor: Intro, Estrofa, Coro, el que quieras. Si ah\xED ya empieza una secci\xF3n, puedes cambiarle el nombre o borrarlo.",
21384
21652
  profesional: "Marca de secci\xF3n en el comp\xE1s del cursor."
21385
21653
  },
21654
+ "menu-repeticion": {
21655
+ nombre: "Repetici\xF3n\u2026",
21656
+ accion: "comando-compas-repeticion",
21657
+ principiante: "Pone las barras de repetici\xF3n. Con un pasaje marcado, abre en su primer comp\xE1s y cierra en el \xFAltimo. Sin pasaje, va en el comp\xE1s del cursor. Eliges cu\xE1ntas veces se toca. Desmarca las barras para quitarlas.",
21658
+ profesional: "Barras de inicio y fin de repetici\xF3n, con n\xFAmero de veces."
21659
+ },
21660
+ "menu-casillas": {
21661
+ nombre: "Casillas\u2026",
21662
+ accion: "comando-compas-casillas",
21663
+ principiante: "Pone una casilla de repetici\xF3n, la 1.\xAA o la 2.\xAA: el comp\xE1s se toca s\xF3lo en esas vueltas. Con un pasaje marcado, la casilla abarca todo el pasaje. Necesita una barra de cierre para hacer algo.",
21664
+ profesional: "Casillas 1.\xAA, 2.\xAA\u2026 del comp\xE1s o del pasaje."
21665
+ },
21666
+ "menu-saltos": {
21667
+ nombre: "D.C., D.S. y Coda\u2026",
21668
+ accion: "comando-compas-saltos",
21669
+ principiante: "Pone en el comp\xE1s del cursor un salto (D.C., D.S., To Coda) o un destino (Segno, Coda, Fine). Un D.S. al Coda necesita tres marcas: el Segno, el To Coda y la Coda. La ventana te avisa si falta alguna.",
21670
+ profesional: "Segno, Coda, Fine y los saltos del comp\xE1s del cursor."
21671
+ },
21386
21672
  // ── Nota ────────────────────────────────────────────────────────────────────
21387
21673
  "menu-duplicar": {
21388
21674
  nombre: "Repetir la nota anterior",
@@ -21651,6 +21937,12 @@ var init_barra = __esm({
21651
21937
  principiante: "Ajustes para practicar: un comp\xE1s de cuenta antes de que entre la m\xFAsica, y que la velocidad suba sola cada tantas vueltas del bucle hasta la que elijas. De f\xE1brica todo apagado.",
21652
21938
  profesional: "Count-in y speed trainer del bucle."
21653
21939
  },
21940
+ // ★ Sólo existe con el teléfono parado (2026-11-11): la de escritorio vive en el panel de audio.
21941
+ "barra.velocidad": {
21942
+ nombre: "Velocidad",
21943
+ principiante: "La obra suena m\xE1s lenta o m\xE1s r\xE1pida, de \xD70,25 a \xD71,5. La afinaci\xF3n no cambia. Sirve para sacar un pasaje dif\xEDcil de a poco.",
21944
+ profesional: "Velocidad del transporte, \xD70,25 a \xD71,5, con el tono intacto."
21945
+ },
21654
21946
  "barra.voz": {
21655
21947
  nombre: "Voz",
21656
21948
  principiante: "En qu\xE9 voz escribes. Una voz es una l\xEDnea mel\xF3dica dentro del mismo pentagrama: por ejemplo, el bajo en la voz 1 y la melod\xEDa en la voz 2, cada una con sus propias figuras. Lo que teclees va a la voz marcada.",
@@ -21711,7 +22003,7 @@ function fichaDeSigno(s) {
21711
22003
  profesional: s.ayuda.length <= 120 && (s.ayuda.match(/[.!?](\s|$)/g) ?? []).length <= 2 ? s.ayuda : `${s.nombre}.`
21712
22004
  };
21713
22005
  }
21714
- var FICHAS_DE_SIGNOS, FIGURAS, FICHAS_DE_FIGURAS, FICHAS_DE_MATICES, FICHAS_DE_LA_PALETA;
22006
+ var FICHAS_DE_SIGNOS, FIGURAS2, FICHAS_DE_FIGURAS, FICHAS_DE_MATICES, FICHAS_DE_LA_PALETA;
21715
22007
  var init_paleta = __esm({
21716
22008
  "packages/editor/src/info/fichas/paleta.ts"() {
21717
22009
  init_signos();
@@ -21719,7 +22011,7 @@ var init_paleta = __esm({
21719
22011
  FICHAS_DE_SIGNOS = Object.fromEntries(
21720
22012
  SIGNOS.map((s) => [`signo.${s.clave}`, fichaDeSigno(s)])
21721
22013
  );
21722
- FIGURAS = [
22014
+ FIGURAS2 = [
21723
22015
  { valor: 1, nombre: "Redonda", dura: "cuatro tiempos en un comp\xE1s de 4/4" },
21724
22016
  { valor: 2, nombre: "Blanca", dura: "dos tiempos" },
21725
22017
  { valor: 4, nombre: "Negra", dura: "un tiempo" },
@@ -21729,7 +22021,7 @@ var init_paleta = __esm({
21729
22021
  { valor: 64, nombre: "Semifusa", dura: "un dieciseisavo de tiempo" }
21730
22022
  ];
21731
22023
  FICHAS_DE_FIGURAS = Object.fromEntries(
21732
- FIGURAS.map((f) => [
22024
+ FIGURAS2.map((f) => [
21733
22025
  `figura.${f.valor}`,
21734
22026
  {
21735
22027
  nombre: f.nombre,
@@ -21898,6 +22190,19 @@ var init_paleta = __esm({
21898
22190
  principiante: "Borra el nombre del acorde del golpe del cursor. Las notas no se tocan.",
21899
22191
  profesional: "Quita el cifrado del golpe."
21900
22192
  },
22193
+ // ── Sección ─────────────────────────────────────────────────────────────────
22194
+ "seccion.usada": {
22195
+ nombre: "Secci\xF3n de la obra",
22196
+ principiante: "Un nombre de secci\xF3n que ya usaste en esta obra, como Coro o Verso. Un clic lo pone en el comp\xE1s del cursor. Para uno nuevo, escribe en el campo.",
22197
+ profesional: "Reutiliza un r\xF3tulo de secci\xF3n de la obra."
22198
+ },
22199
+ // ── El teclado en las paletas (2026-11-11) ──────────────────────────────────
22200
+ "paleta.busqueda": {
22201
+ nombre: "Buscar en las paletas",
22202
+ accion: "comando-buscar-en-paletas",
22203
+ principiante: "Escribe el nombre de lo que quieres poner \u2014acento, martillo, corchea, ff\u2014 y busca en las siete paletas a la vez. Acepta otros nombres, como hammer o fermata. Cada resultado dice en qu\xE9 paleta est\xE1 y su tecla, para que la pr\xF3xima vez vayas directo. Las flechas eligen, Intro lo pone y Esc cierra.",
22204
+ profesional: "B\xFAsqueda en todas las paletas, con sin\xF3nimos y la tecla de cada resultado."
22205
+ },
21901
22206
  // ── Batería ─────────────────────────────────────────────────────────────────
21902
22207
  "kit.pieza": {
21903
22208
  nombre: "Pieza de la bater\xEDa",
@@ -22265,6 +22570,21 @@ var init_paneles = __esm({
22265
22570
  principiante: "Pone una armadura nueva desde el comp\xE1s que empieza en esta l\xEDnea, hasta el pr\xF3ximo cambio.",
22266
22571
  profesional: "Cambio de armadura desde este comp\xE1s."
22267
22572
  },
22573
+ "linea.repeticion": {
22574
+ nombre: "Repetici\xF3n",
22575
+ principiante: "Pone las barras de repetici\xF3n en el comp\xE1s que empieza en esta l\xEDnea. Si ese comp\xE1s est\xE1 dentro de un pasaje marcado, las pone en todo el pasaje.",
22576
+ profesional: "Barras de repetici\xF3n aqu\xED o en el pasaje."
22577
+ },
22578
+ "linea.casillas": {
22579
+ nombre: "Casillas",
22580
+ principiante: "Pone una casilla 1.\xAA, 2.\xAA\u2026 en el comp\xE1s que empieza en esta l\xEDnea, o en todo el pasaje marcado si est\xE1 adentro.",
22581
+ profesional: "Casillas aqu\xED o en el pasaje."
22582
+ },
22583
+ "linea.saltos": {
22584
+ nombre: "D.C., D.S. y Coda",
22585
+ principiante: "Pone un salto o un destino \u2014Segno, Coda, Fine, D.S. al Coda\u2014 en el comp\xE1s que empieza en esta l\xEDnea.",
22586
+ profesional: "Saltos y destinos de este comp\xE1s."
22587
+ },
22268
22588
  "linea.insertar-izquierda": {
22269
22589
  nombre: "Insertar comp\xE1s a la izquierda",
22270
22590
  principiante: "Mete un comp\xE1s vac\xEDo ANTES del comp\xE1s que empieza en esta l\xEDnea. Sirve tambi\xE9n para agregar uno al principio de la obra.",
@@ -22489,6 +22809,37 @@ var init_dialogos = __esm({
22489
22809
  principiante: "Escribe c\xF3mo se llama la parte \u2014Intro, Coro, Puente\u2014. Vac\xEDo, saca la secci\xF3n que hab\xEDa.",
22490
22810
  profesional: "Texto de la secci\xF3n; vac\xEDo la quita."
22491
22811
  },
22812
+ // ── Repeticiones, casillas y saltos (2026-11-11) ────────────────────────────
22813
+ "repeticion.abre": {
22814
+ nombre: "Barra de inicio",
22815
+ principiante: "La barra con dos puntos que marca d\xF3nde empieza lo que se repite. Desm\xE1rcala para quitarla.",
22816
+ profesional: "Barra de inicio de repetici\xF3n."
22817
+ },
22818
+ "repeticion.cierra": {
22819
+ nombre: "Barra de fin",
22820
+ principiante: "La barra con dos puntos donde la m\xFAsica vuelve al inicio de la repetici\xF3n. Desm\xE1rcala para quitarla.",
22821
+ profesional: "Barra de fin de repetici\xF3n."
22822
+ },
22823
+ "repeticion.veces": {
22824
+ nombre: "Veces",
22825
+ principiante: "Cu\xE1ntas veces se toca el pasaje en total, contando la primera. 2 es lo normal y no se escribe en el pliego; desde 3 aparece \xABx3\xBB.",
22826
+ profesional: "Pasadas totales (2\u201364)."
22827
+ },
22828
+ "casillas.vuelta": {
22829
+ nombre: "Vuelta",
22830
+ principiante: "En qu\xE9 vuelta de la repetici\xF3n se toca este comp\xE1s. Puedes marcar m\xE1s de una, como \xAB1. 2.\xBB. Sin ninguna marcada, se quita la casilla.",
22831
+ profesional: "Vueltas en que suena; ninguna la quita."
22832
+ },
22833
+ "saltos.destino": {
22834
+ nombre: "Destino",
22835
+ principiante: "Un lugar al que otro comp\xE1s salta: el Segno para un D.S., la Coda para un \xABal Coda\xBB, el Fine para un \xABal Fine\xBB.",
22836
+ profesional: "Marca de destino (Segno, Coda, Fine)."
22837
+ },
22838
+ "saltos.salto": {
22839
+ nombre: "Salto",
22840
+ principiante: "Manda la m\xFAsica a otro lugar al terminar el comp\xE1s. D.C. vuelve al principio y D.S. al Segno. To Coda marca d\xF3nde se salta a la Coda en la segunda pasada.",
22841
+ profesional: "Salto (D.C., D.S., To Coda\u2026)."
22842
+ },
22492
22843
  "golpe.letra": {
22493
22844
  nombre: "S\xEDlaba",
22494
22845
  principiante: "La s\xEDlaba de la letra que se canta en este golpe. Vac\xEDa, saca la que hab\xEDa.",
@@ -23029,6 +23380,7 @@ var init_src3 = __esm({
23029
23380
  init_silabas();
23030
23381
  init_acordes();
23031
23382
  init_miraDeAcordes();
23383
+ init_paletasConTeclado();
23032
23384
  init_alinearLetra();
23033
23385
  init_diapason();
23034
23386
  init_video();
@@ -87649,6 +88001,151 @@ var init_alphaTab = __esm({
87649
88001
  }
87650
88002
  });
87651
88003
 
88004
+ // packages/io/src/manosDelPiano.ts
88005
+ function repartirManos(notas, P = PARAMETROS_DE_MANOS) {
88006
+ const derecha = [];
88007
+ const izquierda = [];
88008
+ const orden = notas.map((_, i3) => i3).sort((a, b) => notas[a].ini - notas[b].ini || notas[a].altura - notas[b].altura);
88009
+ let centroD = P.centroDerecha;
88010
+ let centroI = P.centroIzquierda;
88011
+ let sostieneD = [];
88012
+ let sostieneI = [];
88013
+ let ultimaD = null;
88014
+ let ultimaI = null;
88015
+ let i2 = 0;
88016
+ while (i2 < orden.length) {
88017
+ const ini = notas[orden[i2]].ini;
88018
+ const golpe = [];
88019
+ while (i2 < orden.length && notas[orden[i2]].ini === ini) golpe.push(orden[i2++]);
88020
+ sostieneD = sostieneD.filter((j) => notas[j].fin > ini);
88021
+ sostieneI = sostieneI.filter((j) => notas[j].fin > ini);
88022
+ const techoI = sostieneI.length ? Math.max(...sostieneI.map((j) => notas[j].altura)) : -Infinity;
88023
+ const pisoD = sostieneD.length ? Math.min(...sostieneD.map((j) => notas[j].altura)) : Infinity;
88024
+ let mejor = { corte: 0, costo: Infinity, distanciaAlDo: Infinity };
88025
+ for (let corte = 0; corte <= golpe.length; corte++) {
88026
+ const abajo = golpe.slice(0, corte).map((j) => notas[j].altura);
88027
+ const arriba = golpe.slice(corte).map((j) => notas[j].altura);
88028
+ let costo = 0;
88029
+ for (const a of abajo) costo += Math.abs(a - centroI) + (a >= DO_CENTRAL ? P.costoLadoDelDo : 0);
88030
+ for (const a of arriba) costo += Math.abs(a - centroD) + (a < DO_CENTRAL ? P.costoLadoDelDo : 0);
88031
+ const manoI = [...abajo, ...abajo.length ? sostieneI.map((j) => notas[j].altura) : []];
88032
+ const manoD = [...arriba, ...arriba.length ? sostieneD.map((j) => notas[j].altura) : []];
88033
+ for (const mano of [manoI, manoD]) {
88034
+ if (mano.length < 2) continue;
88035
+ const abre = Math.max(...mano) - Math.min(...mano);
88036
+ if (abre > P.abertura) costo += P.costoAbertura + (abre - P.abertura) * 4;
88037
+ }
88038
+ for (const a of arriba) if (a < techoI) costo += P.costoCruce;
88039
+ for (const a of abajo) if (a > pisoD) costo += P.costoCruce;
88040
+ if (golpe.length === 1) {
88041
+ const a = notas[golpe[0]].altura;
88042
+ if (abajo.length && ultimaI !== null && Math.abs(a - ultimaI) <= P.continuidadCerca) costo -= P.premioContinuidad;
88043
+ if (arriba.length && ultimaD !== null && Math.abs(a - ultimaD) <= P.continuidadCerca) costo -= P.premioContinuidad;
88044
+ }
88045
+ const delDo = Math.abs(corte - golpe.filter((j) => notas[j].altura < DO_CENTRAL).length);
88046
+ if (costo < mejor.costo - 1e-9 || Math.abs(costo - mejor.costo) <= 1e-9 && delDo < mejor.distanciaAlDo)
88047
+ mejor = { corte, costo, distanciaAlDo: delDo };
88048
+ }
88049
+ const deI = golpe.slice(0, mejor.corte);
88050
+ const deD = golpe.slice(mejor.corte);
88051
+ izquierda.push(...deI);
88052
+ derecha.push(...deD);
88053
+ sostieneI.push(...deI);
88054
+ sostieneD.push(...deD);
88055
+ const media = (js) => js.reduce((s, j) => s + notas[j].altura, 0) / js.length;
88056
+ if (deI.length) {
88057
+ centroI = centroI * (1 - P.seguimiento) + media(deI) * P.seguimiento;
88058
+ ultimaI = notas[deI[deI.length - 1]].altura;
88059
+ } else centroI += (P.centroIzquierda - centroI) * P.regreso;
88060
+ if (deD.length) {
88061
+ centroD = centroD * (1 - P.seguimiento) + media(deD) * P.seguimiento;
88062
+ ultimaD = notas[deD[0]].altura;
88063
+ } else centroD += (P.centroDerecha - centroD) * P.regreso;
88064
+ }
88065
+ return { derecha, izquierda };
88066
+ }
88067
+ function baseDeLasManos(nombreDerecha, nombreIzquierda) {
88068
+ const d = DERECHA.exec(nombreDerecha.trim());
88069
+ const i2 = IZQUIERDA.exec(nombreIzquierda.trim());
88070
+ if (!d || !i2) return null;
88071
+ const base = (s) => s.trim().replace(/[\s\-_.:,(]+$/, "").trim();
88072
+ const bd = base(d[1]);
88073
+ const bi = base(i2[1]);
88074
+ if (bd.toLocaleLowerCase("es") !== bi.toLocaleLowerCase("es")) return null;
88075
+ return bd || "Piano";
88076
+ }
88077
+ function juntarManosDelPiano(documento) {
88078
+ const enOrden = documento.pistasEnOrden().map((p) => p.id);
88079
+ const parejas = [];
88080
+ for (let i2 = 0; i2 + 1 < enOrden.length; i2++) {
88081
+ const d = documento.pistas.get(enOrden[i2]);
88082
+ const iz = documento.pistas.get(enOrden[i2 + 1]);
88083
+ const mismoInstrumento = d.get("tipo") === "altura" && iz.get("tipo") === "altura" && (d.get("pentagramas") ?? 1) === 1 && (iz.get("pentagramas") ?? 1) === 1 && d.get("programaMidi") === iz.get("programaMidi");
88084
+ if (!mismoInstrumento) continue;
88085
+ const porNombre = baseDeLasManos(String(d.get("nombre") ?? ""), String(iz.get("nombre") ?? ""));
88086
+ const porCorto = baseDeLasManos(String(d.get("nombreCorto") ?? ""), String(iz.get("nombreCorto") ?? ""));
88087
+ const base = porNombre ?? (porCorto !== null ? String(d.get("nombre") ?? "").trim() || porCorto : null);
88088
+ if (!base) continue;
88089
+ parejas.push({ derecha: enOrden[i2], izquierda: enOrden[i2 + 1], indiceIzquierda: i2 + 1, base });
88090
+ i2++;
88091
+ }
88092
+ if (parejas.length === 0) return 0;
88093
+ documento.doc.transact(() => {
88094
+ for (const { derecha, izquierda, base } of parejas) {
88095
+ const d = documento.pistas.get(derecha);
88096
+ const corto = String(d.get("nombreCorto") ?? "");
88097
+ d.set("pentagramas", 2);
88098
+ d.set("nombre", base);
88099
+ d.set("nombreCorto", corto.replace(/[\s\-_.:,(]*(mano\s+derecha|right\s+hand|r\.?\s?h\.?|m\.?\s?d\.?|rh|md)\)?\s*$/i, "").trim() || base);
88100
+ for (const compas of documento.compases.values()) {
88101
+ if (compas.get("pistaId") !== izquierda) continue;
88102
+ compas.set("pistaId", derecha);
88103
+ compas.set("pentagrama", 1);
88104
+ }
88105
+ documento.pistas.delete(izquierda);
88106
+ }
88107
+ const sesion = documento.ajustes.get(CLAVE_DE_LA_SESION);
88108
+ if (sesion && typeof sesion === "object" && !Array.isArray(sesion)) {
88109
+ const quitadas = new Set(parejas.map((p) => p.izquierda));
88110
+ const nueva = { ...sesion };
88111
+ for (const clave of ["mezcla", "volumenEnSolo", "notacionPorPista"]) {
88112
+ const v = nueva[clave];
88113
+ if (!v || typeof v !== "object" || Array.isArray(v)) continue;
88114
+ nueva[clave] = Object.fromEntries(Object.entries(v).filter(([id2]) => !quitadas.has(id2)));
88115
+ }
88116
+ if (Array.isArray(nueva.pistasConOjo)) {
88117
+ let lista2 = nueva.pistasConOjo.filter((n) => typeof n === "number");
88118
+ for (const quitado of parejas.map((p) => p.indiceIzquierda).sort((x2, y) => y - x2))
88119
+ lista2 = lista2.filter((n) => n !== quitado).map((n) => n > quitado ? n - 1 : n);
88120
+ nueva.pistasConOjo = lista2;
88121
+ }
88122
+ documento.ajustes.set(CLAVE_DE_LA_SESION, nueva);
88123
+ }
88124
+ });
88125
+ return parejas.length;
88126
+ }
88127
+ var DO_CENTRAL, PARAMETROS_DE_MANOS, DERECHA, IZQUIERDA;
88128
+ var init_manosDelPiano = __esm({
88129
+ "packages/io/src/manosDelPiano.ts"() {
88130
+ init_src2();
88131
+ DO_CENTRAL = 60;
88132
+ PARAMETROS_DE_MANOS = {
88133
+ centroDerecha: 72,
88134
+ centroIzquierda: 52,
88135
+ abertura: 12,
88136
+ costoAbertura: 20,
88137
+ costoCruce: 25,
88138
+ continuidadCerca: 7,
88139
+ premioContinuidad: 12,
88140
+ costoLadoDelDo: 1.5,
88141
+ seguimiento: 0.3,
88142
+ regreso: 0.1
88143
+ };
88144
+ DERECHA = /^(.*?)[\s\-_.:,(]*\b(mano\s+derecha|m\.?\s?d\.?|right\s+hand|r\.?\s?h\.?|rh|md|derecha|right|treble)\)?\s*$/i;
88145
+ IZQUIERDA = /^(.*?)[\s\-_.:,(]*\b(mano\s+izquierda|m\.?\s?i\.?|left\s+hand|l\.?\s?h\.?|lh|mi|izquierda|left|bass)\)?\s*$/i;
88146
+ }
88147
+ });
88148
+
87652
88149
  // node_modules/fflate/esm/index.mjs
87653
88150
  import { createRequire } from "module";
87654
88151
  function deflateSync(data, opts) {
@@ -88630,484 +89127,1048 @@ var init_esm = __esm({
88630
89127
  }
88631
89128
  });
88632
89129
 
88633
- // packages/io/src/musicxmlEntrada.ts
88634
- function textoMusicXml(bytes) {
88635
- if (bytes.length > 4 && bytes[0] === 80 && bytes[1] === 75) {
88636
- let archivos;
88637
- try {
88638
- archivos = unzipSync(bytes);
88639
- } catch {
88640
- return null;
88641
- }
88642
- const contenedor = archivos["META-INF/container.xml"];
88643
- if (!contenedor) return null;
88644
- const ruta = /<rootfile\b[^>]*full-path="([^"]+)"/.exec(strFromU8(contenedor))?.[1];
88645
- const raiz = ruta ? archivos[ruta] : void 0;
88646
- return raiz ? decodificar(raiz) : null;
88647
- }
88648
- const texto4 = decodificar(bytes.subarray(0, Math.min(bytes.length, 4096)));
88649
- if (!/<score-(partwise|timewise)\b/.test(texto4) && !/<!DOCTYPE\s+score-/.test(texto4)) return null;
88650
- return decodificar(bytes);
89130
+ // packages/io/src/musicxml.ts
89131
+ function escapar(texto4) {
89132
+ return texto4.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
88651
89133
  }
88652
- function decodificar(bytes) {
88653
- if (bytes[0] === 255 && bytes[1] === 254) return new TextDecoder("utf-16le").decode(bytes);
88654
- if (bytes[0] === 254 && bytes[1] === 255) return new TextDecoder("utf-16be").decode(bytes);
88655
- return new TextDecoder("utf-8").decode(bytes);
89134
+ function elemento(nombre, texto4) {
89135
+ if (texto4 === null || texto4 === void 0 || texto4 === "") return [];
89136
+ return [`<${nombre}>${escapar(String(texto4))}</${nombre}>`];
88656
89137
  }
88657
- function prepararMusicXml(xml) {
88658
- const correcciones = [];
88659
- let atributosMovidos = 0;
88660
- let acordesSeparados = 0;
88661
- let saltosMovidos = 0;
88662
- let salida = xml.replace(/<part\b[^>]*>[\s\S]*?<\/part>/g, (parteOriginal) => {
88663
- const movidos = moverAtributosDeFinDeCompas(parteOriginal);
88664
- atributosMovidos += movidos.cuantos;
88665
- const separados = separarAcordesEntrePentagramas(movidos.texto);
88666
- acordesSeparados += separados.cuantos;
88667
- const saltos = sacarSaltosDeLaIndicacion(separados.texto);
88668
- saltosMovidos += saltos.cuantos;
88669
- const { texto: texto4, cambio } = compactarVoces(saltos.texto);
88670
- if (cambio) correcciones.push(cambio);
88671
- return texto4;
88672
- });
88673
- if (atributosMovidos) {
88674
- correcciones.push(
88675
- `${atributosMovidos} cambios de clave, armadura o comp\xE1s escritos entre las notas se pusieron al principio del comp\xE1s donde rigen.`
88676
- );
89138
+ function alturaMusicXml(midi, fifths, modo = 0) {
89139
+ const octavaAlphaTab = Math.floor(midi / 12);
89140
+ const clase = midi - octavaAlphaTab * 12;
89141
+ const octava = octavaAlphaTab - 1;
89142
+ const pedida = ALTERACION_POR_MODO[modo];
89143
+ if (pedida !== void 0) {
89144
+ const sinAlterar = clase - pedida;
89145
+ const vueltas = Math.floor(sinAlterar / 12);
89146
+ const paso = PASOS.find((p) => p.semitonos === sinAlterar - vueltas * 12);
89147
+ if (paso) return { paso: paso.paso, alteracion: pedida, octava: octava + vueltas };
88677
89148
  }
88678
- if (acordesSeparados) {
88679
- correcciones.push(`${acordesSeparados} notas de acordes repartidos entre los dos pentagramas se pusieron en el suyo.`);
89149
+ const natural = PASOS.find((p) => p.semitonos === clase);
89150
+ if (natural) return { paso: natural.paso, alteracion: 0, octava };
89151
+ if (fifths < 0) {
89152
+ const arriba = PASOS.find((p) => p.semitonos === clase + 1);
89153
+ if (arriba) return { paso: arriba.paso, alteracion: -1, octava };
88680
89154
  }
88681
- if (saltosMovidos) correcciones.push(`${saltosMovidos} saltos (D.C., D.S., Coda, Fine, Segno) se leyeron de su indicaci\xF3n.`);
88682
- let juntados = 0;
88683
- salida = salida.replace(/<direction\b[^>]*>[\s\S]*?<\/direction>/g, (dir) => {
88684
- const palabras = [...dir.matchAll(/<words\b[^>]*>([\s\S]*?)<\/words>/g)];
88685
- if (palabras.length < 2) return dir;
88686
- juntados++;
88687
- const todo = palabras.map((p) => p[1].trim()).filter(Boolean).join("\n");
88688
- let primero = true;
88689
- return dir.replace(/<words\b([^>]*)>[\s\S]*?<\/words>/g, (_m, attrs) => {
88690
- if (!primero) return "";
88691
- primero = false;
88692
- return `<words${attrs}>${todo}</words>`;
88693
- });
88694
- });
88695
- if (juntados) correcciones.push(`${juntados} indicaciones de texto en varios renglones se juntaron en una.`);
88696
- let silabas2 = 0;
88697
- salida = salida.replace(/<lyric\b[^>]*>[\s\S]*?<\/lyric>/g, (lyric) => {
88698
- const tipo = /<syllabic>\s*(\w+)\s*<\/syllabic>/.exec(lyric)?.[1];
88699
- if (tipo !== "begin" && tipo !== "middle") return lyric;
88700
- return lyric.replace(/(<text\b[^>]*>)([\s\S]*?)(<\/text>)/, (m, a, texto4, b) => {
88701
- if (texto4.trimEnd().endsWith("-")) return m;
88702
- silabas2++;
88703
- return `${a}${texto4.trimEnd()}-${b}`;
88704
- });
88705
- });
88706
- if (silabas2) correcciones.push(`${silabas2} s\xEDlabas unidas de la letra se marcaron con su guion.`);
88707
- let tempos = 0;
88708
- salida = salida.replace(/<metronome\b[^>]*>[\s\S]*?<\/metronome>/g, (met) => {
88709
- const unidad = /<beat-unit>\s*([\w-]+)\s*<\/beat-unit>/.exec(met)?.[1];
88710
- const porMinuto = /<per-minute>\s*([\d.]+)\s*<\/per-minute>/.exec(met)?.[1];
88711
- const negras = unidad ? NEGRAS_POR_FIGURA[unidad] : void 0;
88712
- if (!unidad || !porMinuto || negras === void 0 || /<beat-unit>[\s\S]*<beat-unit>/.test(met)) return met;
88713
- const puntillos = (met.match(/<beat-unit-dot\s*\/>/g) ?? []).length;
88714
- const factor = negras * (puntillos === 0 ? 1 : puntillos === 1 ? 1.5 : 1.75);
88715
- if (factor === 1) return met;
88716
- tempos++;
88717
- const bpm = Math.round(Number.parseFloat(porMinuto) * factor * 100) / 100;
88718
- return met.replace(/<beat-unit>[\s\S]*?<\/beat-unit>/, "<beat-unit>quarter</beat-unit>").replace(/<beat-unit-dot\s*\/>/g, "").replace(/<per-minute>[\s\S]*?<\/per-minute>/, `<per-minute>${bpm}</per-minute>`);
88719
- });
88720
- if (tempos) correcciones.push(`${tempos} indicaciones de tempo se pasaron a negras (mismo pulso).`);
88721
- return { xml: salida, bytes: new TextEncoder().encode(salida), correcciones };
89155
+ const abajo = PASOS.find((p) => p.semitonos === clase - 1);
89156
+ if (abajo) return { paso: abajo.paso, alteracion: 1, octava };
89157
+ throw new Error(`altura MIDI no representable: ${midi}`);
88722
89158
  }
88723
- function compasesDe(parte) {
88724
- return [...parte.matchAll(/(<measure\b[^>]*>)([\s\S]*?)<\/measure>/g)].map((m) => ({
88725
- inicio: m.index,
88726
- fin: m.index + m[0].length,
88727
- apertura: m[1],
88728
- contenido: m[2]
88729
- }));
89159
+ function claveDelPentagrama(staff, bar) {
89160
+ if (staff.isPercussion) return { signo: "percussion", linea: 2, lineas: 5, conAfinacion: false };
89161
+ if (staff.showTablature && staff.tuning.length > 0)
89162
+ return { signo: "TAB", linea: 5, lineas: staff.tuning.length, conAfinacion: true };
89163
+ switch (bar.clef) {
89164
+ case _barrel_exports$4.Clef.C3:
89165
+ return { signo: "C", linea: 3, lineas: 5, conAfinacion: false };
89166
+ case _barrel_exports$4.Clef.C4:
89167
+ return { signo: "C", linea: 4, lineas: 5, conAfinacion: false };
89168
+ case _barrel_exports$4.Clef.F4:
89169
+ return { signo: "F", linea: 4, lineas: 5, conAfinacion: false };
89170
+ case _barrel_exports$4.Clef.Neutral:
89171
+ return { signo: "percussion", linea: 2, lineas: 5, conAfinacion: false };
89172
+ default:
89173
+ return { signo: "G", linea: 2, lineas: 5, conAfinacion: false };
89174
+ }
88730
89175
  }
88731
- function rearmar(parte, compases, nuevos) {
88732
- let salida = "";
88733
- let cursor = 0;
88734
- compases.forEach((c, i2) => {
88735
- salida += parte.slice(cursor, c.inicio) + c.apertura + nuevos[i2] + "</measure>";
88736
- cursor = c.fin;
88737
- });
88738
- return salida + parte.slice(cursor);
89176
+ function gruposDeTresillo(beats) {
89177
+ const abren = /* @__PURE__ */ new Set();
89178
+ const cierran = /* @__PURE__ */ new Set();
89179
+ let i2 = 0;
89180
+ while (i2 < beats.length) {
89181
+ const b = beats[i2];
89182
+ if (b.tupletNumerator <= 0 || b.tupletDenominator <= 0) {
89183
+ i2++;
89184
+ continue;
89185
+ }
89186
+ let j = i2;
89187
+ while (j + 1 < beats.length && beats[j + 1].tupletNumerator === b.tupletNumerator && beats[j + 1].tupletDenominator === b.tupletDenominator && (b.tupletGroup === null || beats[j + 1].tupletGroup === b.tupletGroup))
89188
+ j++;
89189
+ abren.add(i2);
89190
+ cierran.add(j);
89191
+ i2 = j + 1;
89192
+ }
89193
+ return { abren, cierran };
88739
89194
  }
88740
- function tiempoAl(pedazo) {
88741
- let t = 0;
88742
- for (const m of pedazo.matchAll(/<(note|backup|forward)\b[\s\S]*?<\/\1>/g)) {
88743
- const dur = entero(m[0], /<duration>\s*(\d+)\s*<\/duration>/) ?? 0;
88744
- if (m[1] === "backup") t -= dur;
88745
- else if (m[1] === "forward") t += dur;
88746
- else if (!/<chord\s*\/>/.test(m[0]) && !/<grace\b/.test(m[0])) t += dur;
89195
+ function direccionDeLasPalabras(texto4) {
89196
+ return SALTO_POR_CLAVE.get(claveDeSalto(texto4)) ?? null;
89197
+ }
89198
+ function saltosDelCompas(master) {
89199
+ const s = [];
89200
+ for (const [direccion, salto] of SALTOS) {
89201
+ if (!master?.directions?.has(direccion)) continue;
89202
+ s.push('<direction placement="above">', "<direction-type>");
89203
+ s.push(salto.simbolo ? `<${salto.simbolo}/>` : `<words>${escapar(salto.texto ?? "")}</words>`);
89204
+ s.push("</direction-type>", `<sound ${salto.sonido}/>`, "</direction>");
88747
89205
  }
88748
- return t;
89206
+ return s;
88749
89207
  }
88750
- function moverAtributosDeFinDeCompas(parte) {
88751
- const compases = compasesDe(parte);
88752
- let cuantos = 0;
88753
- let arrastre = "";
88754
- const nuevos = compases.map((c) => {
88755
- let contenido = c.contenido;
88756
- const primeraNota = contenido.search(/<note\b/);
88757
- if (primeraNota >= 0) {
88758
- let llevar = "";
88759
- let alPrincipio = "";
88760
- const despues = contenido.slice(primeraNota);
88761
- contenido = contenido.slice(0, primeraNota) + despues.replace(/<attributes\b[^>]*>([\s\S]*?)<\/attributes>/g, (bloque, adentro, donde) => {
88762
- const hijos = adentro.match(HIJO_QUE_SE_MUEVE);
88763
- if (!hijos) return bloque;
88764
- cuantos += hijos.length;
88765
- if (tiempoAl(despues.slice(0, donde)) <= 0) alPrincipio += hijos.join("");
88766
- else llevar += hijos.join("");
88767
- const resto = adentro.replace(HIJO_QUE_SE_MUEVE, "");
88768
- return resto.trim() ? `<attributes>${resto}</attributes>` : "";
88769
- });
88770
- const entra2 = arrastre + alPrincipio;
88771
- arrastre = llevar;
88772
- return (entra2 ? `<attributes>${entra2}</attributes>` : "") + contenido;
88773
- }
88774
- const entra = arrastre;
88775
- arrastre = "";
88776
- return (entra ? `<attributes>${entra}</attributes>` : "") + contenido;
88777
- });
88778
- return cuantos ? { texto: rearmar(parte, compases, nuevos), cuantos } : { texto: parte, cuantos: 0 };
89208
+ function nombreDeFigura(duracion) {
89209
+ return NOMBRE_DE_LA_FIGURA2.get(duracion) ?? "quarter";
88779
89210
  }
88780
- function separarAcordesEntrePentagramas(parte) {
88781
- const compases = compasesDe(parte);
88782
- let cuantos = 0;
88783
- const nuevos = compases.map((c) => {
88784
- const notas = [...c.contenido.matchAll(/<note\b[\s\S]*?<\/note>/g)];
88785
- if (notas.length < 2) return c.contenido;
88786
- let salida = "";
88787
- let cursor = 0;
88788
- let i2 = 0;
88789
- while (i2 < notas.length) {
88790
- const grupo = [notas[i2]];
88791
- let j = i2 + 1;
88792
- while (j < notas.length) {
88793
- const anterior = notas[j - 1];
88794
- const entre = c.contenido.slice(anterior.index + anterior[0].length, notas[j].index);
88795
- if (entre.trim() || !/<chord\s*\/>/.test(notas[j][0])) break;
88796
- grupo.push(notas[j]);
88797
- j++;
88798
- }
88799
- i2 = j;
88800
- const pent = (n) => entero(n[0], /<staff>\s*(\d+)\s*<\/staff>/) ?? 1;
88801
- const cabeza = grupo[0];
88802
- const pentDeCabeza = pent(cabeza);
88803
- const ajenas = grupo.filter((n) => pent(n) !== pentDeCabeza);
88804
- const durCabeza = entero(cabeza[0], /<duration>\s*(\d+)\s*<\/duration>/);
88805
- if (ajenas.length === 0 || /<chord\s*\/>/.test(cabeza[0]) || grupo.some((n) => /<grace\b|<cue\s*\/>/.test(n[0])) || durCabeza === null) {
88806
- continue;
88807
- }
88808
- cuantos += ajenas.length;
88809
- const propias = grupo.filter((n) => pent(n) === pentDeCabeza).map((n) => n[0]);
88810
- let texto4 = propias.join("");
88811
- const porPentagrama = /* @__PURE__ */ new Map();
88812
- for (const n of ajenas) porPentagrama.set(pent(n), [...porPentagrama.get(pent(n)) ?? [], n[0]]);
88813
- for (const notasDelPent of porPentagrama.values()) {
88814
- const primera = notasDelPent[0].replace(/<chord\s*\/>\s*/, "");
88815
- const durPrimera = entero(primera, /<duration>\s*(\d+)\s*<\/duration>/) ?? durCabeza;
88816
- texto4 += `<backup><duration>${durCabeza}</duration></backup>` + primera + notasDelPent.slice(1).join("");
88817
- const delta = durCabeza - durPrimera;
88818
- if (delta > 0) texto4 += `<forward><duration>${delta}</duration></forward>`;
88819
- else if (delta < 0) texto4 += `<backup><duration>${-delta}</duration></backup>`;
88820
- }
88821
- const ultimo = grupo[grupo.length - 1];
88822
- salida += c.contenido.slice(cursor, cabeza.index) + texto4;
88823
- cursor = ultimo.index + ultimo[0].length;
88824
- }
88825
- return salida + c.contenido.slice(cursor);
88826
- });
88827
- return cuantos ? { texto: rearmar(parte, compases, nuevos), cuantos } : { texto: parte, cuantos: 0 };
89211
+ function instrumentosDePercusion(track) {
89212
+ const usadas = /* @__PURE__ */ new Set();
89213
+ for (const staff of track.staves)
89214
+ for (const bar of staff.bars)
89215
+ for (const voice of bar.voices)
89216
+ for (const beat of voice.beats)
89217
+ for (const note of beat.notes)
89218
+ if (note.percussionArticulation >= 0) usadas.add(note.percussionArticulation);
89219
+ const ids = /* @__PURE__ */ new Map();
89220
+ for (const art of [...usadas].sort((a, b) => a - b))
89221
+ ids.set(art, `P${track.index + 1}-I${art + 1}`);
89222
+ return ids;
88828
89223
  }
88829
- function sacarSaltosDeLaIndicacion(parte) {
88830
- let cuantos = 0;
88831
- const texto4 = parte.replace(/<direction\b[^>]*>[\s\S]*?<\/direction>/g, (dir, desde, todo) => {
88832
- const sonido = /<sound\b([^>]*?)\/?>/.exec(dir)?.[1];
88833
- if (!sonido) return dir;
88834
- const atributos = ATRIBUTOS_DE_SALTO.map((a) => new RegExp(`\\b${a}="([^"]*)"`).exec(sonido)?.[0]).filter((a) => a !== void 0);
88835
- if (atributos.length === 0) return dir;
88836
- const copia = `<sound ${atributos.join(" ")}/>`;
88837
- if (todo.slice(desde + dir.length).trimStart().startsWith(copia)) return dir;
88838
- cuantos++;
88839
- return dir + copia;
88840
- });
88841
- return { texto: texto4, cuantos };
89224
+ function posicionDePercusion(staffLine) {
89225
+ const indiceDeB = 6;
89226
+ const bruto = indiceDeB + (5 - staffLine);
89227
+ const paso = PASOS[(bruto % 7 + 7) % 7].paso;
89228
+ return { paso, octava: 4 + Math.floor(bruto / 7) };
88842
89229
  }
88843
- function compactarVoces(parte) {
88844
- const porPentagrama = /* @__PURE__ */ new Map();
88845
- const pentagramaDeVoz = /* @__PURE__ */ new Map();
88846
- for (const m of parte.matchAll(/<note\b[\s\S]*?<\/note>/g)) {
88847
- const voz = entero(m[0], /<voice>\s*(\d+)\s*<\/voice>/);
88848
- if (voz === null) continue;
88849
- const pent = entero(m[0], /<staff>\s*(\d+)\s*<\/staff>/) ?? 1;
88850
- const lista2 = porPentagrama.get(pent) ?? [];
88851
- if (!lista2.includes(voz)) lista2.push(voz);
88852
- porPentagrama.set(pent, lista2);
88853
- if (!pentagramaDeVoz.has(voz)) pentagramaDeVoz.set(voz, pent);
89230
+ function escribirNota(note, beat, staff, opciones) {
89231
+ const s = ["<note>"];
89232
+ const esAdorno = beat.graceType !== _barrel_exports$4.GraceType.None;
89233
+ if (esAdorno) s.push(beat.graceType === _barrel_exports$4.GraceType.BeforeBeat ? "<grace/>" : '<grace slash="yes"/>');
89234
+ if (opciones.esAcorde) s.push("<chord/>");
89235
+ const esGolpeDeKit = staff.isPercussion && note.percussionArticulation >= 0;
89236
+ if (esGolpeDeKit) {
89237
+ const art = staff.track.percussionArticulations[note.percussionArticulation];
89238
+ const { paso, octava } = posicionDePercusion(art?.staffLine ?? 5);
89239
+ s.push("<unpitched>");
89240
+ s.push(`<display-step>${paso}</display-step>`);
89241
+ s.push(`<display-octave>${octava}</display-octave>`);
89242
+ s.push("</unpitched>");
89243
+ } else {
89244
+ const { paso, alteracion, octava } = alturaMusicXml(
89245
+ note.calculateRealValue(false, false),
89246
+ opciones.fifths,
89247
+ note.accidentalMode
89248
+ );
89249
+ s.push("<pitch>");
89250
+ s.push(`<step>${paso}</step>`);
89251
+ if (alteracion !== 0) s.push(`<alter>${alteracion}</alter>`);
89252
+ s.push(`<octave>${octava}</octave>`);
89253
+ s.push("</pitch>");
88854
89254
  }
88855
- const nueva = /* @__PURE__ */ new Map();
88856
- let hayCambio = false;
88857
- for (const [pent, voces] of porPentagrama) {
88858
- ;
88859
- [...voces].sort((a, b) => a - b).forEach((voz, i2) => {
88860
- nueva.set(`${pent}|${voz}`, i2 + 1);
88861
- if (voz !== i2 + 1) hayCambio = true;
88862
- });
89255
+ if (!esAdorno) s.push(`<duration>${opciones.duracion}</duration>`);
89256
+ if (note.isTieDestination) s.push('<tie type="stop"/>');
89257
+ if (note.isTieOrigin) s.push('<tie type="start"/>');
89258
+ const idDeInstrumento = opciones.instrumentos.get(note.percussionArticulation);
89259
+ if (esGolpeDeKit && idDeInstrumento) s.push(`<instrument id="${idDeInstrumento}"/>`);
89260
+ s.push(`<voice>${opciones.numeroDeVoz}</voice>`);
89261
+ s.push(...elemento("type", nombreDeFigura(beat.duration)));
89262
+ for (let d = 0; d < beat.dots; d++) s.push("<dot/>");
89263
+ if (beat.tupletNumerator > 0 && beat.tupletDenominator > 0) {
89264
+ s.push("<time-modification>");
89265
+ s.push(`<actual-notes>${beat.tupletNumerator}</actual-notes>`);
89266
+ s.push(`<normal-notes>${beat.tupletDenominator}</normal-notes>`);
89267
+ s.push("</time-modification>");
88863
89268
  }
88864
- if (!hayCambio) return { texto: parte, cambio: null };
88865
- const renumerar = (bloque, pentPorOmision) => {
88866
- const voz = entero(bloque, /<voice>\s*(\d+)\s*<\/voice>/);
88867
- if (voz === null) return bloque;
88868
- const pent = entero(bloque, /<staff>\s*(\d+)\s*<\/staff>/) ?? pentPorOmision(voz);
88869
- const n = nueva.get(`${pent}|${voz}`);
88870
- return n === void 0 ? bloque : bloque.replace(/<voice>\s*\d+\s*<\/voice>/, `<voice>${n}</voice>`);
88871
- };
88872
- const texto4 = parte.replace(/<note\b[\s\S]*?<\/note>/g, (b) => renumerar(b, () => 1)).replace(/<forward\b[\s\S]*?<\/forward>/g, (b) => renumerar(b, (v) => pentagramaDeVoz.get(v) ?? 1));
88873
- const id2 = /<part\b[^>]*\bid="([^"]*)"/.exec(parte)?.[1] ?? "?";
88874
- const detalle = [...porPentagrama].map(([p, vs]) => `pentagrama ${p}: voces ${[...vs].sort((a, b) => a - b).join(", ")}`).join("; ");
88875
- return { texto: texto4, cambio: `Parte ${id2}: las voces se renumeraron por pentagrama (${detalle}).` };
89269
+ if (note.isDead) s.push("<notehead>x</notehead>");
89270
+ else if (esGolpeDeKit) {
89271
+ const art = staff.track.percussionArticulations[note.percussionArticulation];
89272
+ if (art?.noteHeadDefault === _barrel_exports$4.MusicFontSymbol.NoteheadXBlack)
89273
+ s.push("<notehead>x</notehead>");
89274
+ } else if (note.isGhost) s.push('<notehead parentheses="yes">normal</notehead>');
89275
+ if (opciones.numeroDePentagrama !== null) s.push(`<staff>${opciones.numeroDePentagrama}</staff>`);
89276
+ const notaciones = [];
89277
+ if (note.isTieDestination) notaciones.push('<tied type="stop"/>');
89278
+ if (note.isTieOrigin) notaciones.push('<tied type="start"/>');
89279
+ if (opciones.abreTresillo) notaciones.push('<tuplet type="start" number="1"/>');
89280
+ if (opciones.cierraTresillo) notaciones.push('<tuplet type="stop" number="1"/>');
89281
+ const tecnica = [];
89282
+ if (note.string > 0 && staff.tuning.length > 0) {
89283
+ tecnica.push(`<string>${staff.tuning.length - note.string + 1}</string>`);
89284
+ tecnica.push(`<fret>${note.fret}</fret>`);
89285
+ }
89286
+ const ligado = (desde, hasta) => hasta >= desde ? "hammer-on" : "pull-off";
89287
+ const origen = note.hammerPullOrigin;
89288
+ if (origen !== null) {
89289
+ const nombre = ligado(origen.fret, note.fret);
89290
+ tecnica.push(`<${nombre} type="stop"/>`);
89291
+ }
89292
+ const destino = note.hammerPullDestination;
89293
+ if (note.isHammerPullOrigin && destino) {
89294
+ const nombre = ligado(note.fret, destino.fret);
89295
+ tecnica.push(`<${nombre} type="start">${nombre === "hammer-on" ? "H" : "P"}</${nombre}>`);
89296
+ }
89297
+ if (note.harmonicType !== _barrel_exports$4.HarmonicType.None) tecnica.push("<harmonic/>");
89298
+ if (beat.isPalmMute) tecnica.push("<other-technical>palm-mute</other-technical>");
89299
+ if (beat.isLetRing) tecnica.push("<other-technical>let-ring</other-technical>");
89300
+ if (tecnica.length > 0) notaciones.push("<technical>", ...tecnica, "</technical>");
89301
+ const articulaciones = [];
89302
+ if (note.accentuated === _barrel_exports$4.AccentuationType.Normal) articulaciones.push("<accent/>");
89303
+ else if (note.accentuated === _barrel_exports$4.AccentuationType.Heavy)
89304
+ articulaciones.push("<strong-accent/>");
89305
+ else if (note.accentuated === _barrel_exports$4.AccentuationType.Tenuto)
89306
+ articulaciones.push("<tenuto/>");
89307
+ if (note.isStaccato) articulaciones.push("<staccato/>");
89308
+ if (articulaciones.length > 0)
89309
+ notaciones.push("<articulations>", ...articulaciones, "</articulations>");
89310
+ if (notaciones.length > 0) s.push("<notations>", ...notaciones, "</notations>");
89311
+ if (!opciones.esAcorde) s.push(...letraDelGolpe(beat));
89312
+ s.push("</note>");
89313
+ return s;
88876
89314
  }
88877
- function leerOrtografia(xml) {
88878
- return leerDelArchivo(xml).ortografia;
89315
+ function letraDelGolpe(beat) {
89316
+ if (!beat.lyrics) return [];
89317
+ const s = [];
89318
+ beat.lyrics.forEach((verso, i2) => {
89319
+ if (!verso) return;
89320
+ const sigue = verso.endsWith("-") && verso.length > 1;
89321
+ let anterior = beat.previousBeat;
89322
+ while (anterior && !anterior.lyrics?.[i2]) anterior = anterior.previousBeat;
89323
+ const viene = !!anterior?.lyrics?.[i2]?.endsWith("-");
89324
+ const silabico = sigue ? viene ? "middle" : "begin" : viene ? "end" : "single";
89325
+ s.push(`<lyric number="${i2 + 1}">`);
89326
+ s.push(`<syllabic>${silabico}</syllabic>`);
89327
+ s.push(...elemento("text", sigue ? verso.slice(0, -1) : verso));
89328
+ s.push("</lyric>");
89329
+ });
89330
+ return s;
88879
89331
  }
88880
- function leerDelArchivo(xml) {
88881
- const ortografia = /* @__PURE__ */ new Map();
88882
- const bajos = /* @__PURE__ */ new Map();
88883
- const matices = /* @__PURE__ */ new Map();
88884
- let iParte = -1;
88885
- for (const parte of xml.matchAll(/<part\b[^>]*>([\s\S]*?)<\/part>/g)) {
88886
- iParte++;
88887
- let divisiones = 1;
88888
- let transposicion = 0;
88889
- let iCompas = -1;
88890
- for (const compas of parte[1].matchAll(/<measure\b[^>]*>([\s\S]*?)<\/measure>/g)) {
88891
- iCompas++;
88892
- let tic = 0;
88893
- let ticAnterior = 0;
88894
- let bajoPendiente = null;
88895
- for (const el of compas[1].matchAll(/<(note|backup|forward|attributes|harmony|direction)\b[\s\S]*?<\/\1>/g)) {
88896
- const bloque = el[0];
88897
- const tipo = el[1];
88898
- if (tipo === "direction") {
88899
- const nombre = /<dynamics\b[^>]*>\s*<([a-z]+)\s*\/>/.exec(bloque)?.[1];
88900
- if (nombre) {
88901
- const pent2 = (entero(bloque, /<staff>\s*(\d+)\s*<\/staff>/) ?? 1) - 1;
88902
- const clave2 = `${iParte}|${pent2}`;
88903
- const lista2 = matices.get(clave2) ?? [];
88904
- lista2.push({ compas: iCompas, tic: Math.round(tic * TICS_POR_NEGRA2 / divisiones), nombre });
88905
- matices.set(clave2, lista2);
88906
- }
88907
- continue;
88908
- }
88909
- if (tipo === "attributes") {
88910
- divisiones = entero(bloque, /<divisions>\s*(\d+)\s*<\/divisions>/) ?? divisiones;
88911
- const transponer2 = /<transpose\b[^>]*>([\s\S]*?)<\/transpose>/.exec(bloque)?.[1];
88912
- if (transponer2 !== void 0) {
88913
- transposicion = (entero(transponer2, /<chromatic>\s*(-?\d+)\s*<\/chromatic>/) ?? 0) + 12 * (entero(transponer2, /<octave-change>\s*(-?\d+)\s*<\/octave-change>/) ?? 0);
88914
- }
88915
- continue;
88916
- }
88917
- if (tipo === "harmony") {
88918
- const bajo = /<bass>([\s\S]*?)<\/bass>/.exec(bloque)?.[1];
88919
- const paso2 = bajo ? /<bass-step>\s*([A-G])\s*<\/bass-step>/.exec(bajo)?.[1] : void 0;
88920
- if (bajoPendiente === null) {
88921
- const alter2 = bajo ? entero(bajo, /<bass-alter>\s*(-?\d+)\s*<\/bass-alter>/) ?? 0 : 0;
88922
- bajoPendiente = paso2 ? `/${paso2}${alter2 === -2 ? "bb" : alter2 === -1 ? "b" : alter2 === 1 ? "#" : alter2 === 2 ? "x" : ""}` : "";
88923
- }
88924
- continue;
88925
- }
88926
- const dur = entero(bloque, /<duration>\s*(\d+)\s*<\/duration>/) ?? 0;
88927
- if (tipo === "backup") {
88928
- tic -= dur;
88929
- continue;
88930
- }
88931
- if (tipo === "forward") {
88932
- tic += dur;
88933
- continue;
88934
- }
88935
- if (/<grace\b/.test(bloque) || /<cue\s*\/>/.test(bloque)) continue;
88936
- const acorde = /<chord\s*\/>/.test(bloque);
88937
- const inicio = acorde ? ticAnterior : tic;
88938
- if (!acorde) {
88939
- ticAnterior = tic;
88940
- tic += dur;
89332
+ function escribirSilencio2(beat, duracion, numeroDeVoz, numeroDePentagrama) {
89333
+ const s = ["<note>", "<rest/>", `<duration>${duracion}</duration>`];
89334
+ s.push(`<voice>${numeroDeVoz}</voice>`);
89335
+ s.push(...elemento("type", nombreDeFigura(beat.duration)));
89336
+ for (let d = 0; d < beat.dots; d++) s.push("<dot/>");
89337
+ if (beat.tupletNumerator > 0 && beat.tupletDenominator > 0) {
89338
+ s.push("<time-modification>");
89339
+ s.push(`<actual-notes>${beat.tupletNumerator}</actual-notes>`);
89340
+ s.push(`<normal-notes>${beat.tupletDenominator}</normal-notes>`);
89341
+ s.push("</time-modification>");
89342
+ }
89343
+ if (numeroDePentagrama !== null) s.push(`<staff>${numeroDePentagrama}</staff>`);
89344
+ s.push(...letraDelGolpe(beat));
89345
+ s.push("</note>");
89346
+ return s;
89347
+ }
89348
+ function exportarMusicXml(score) {
89349
+ const l = [];
89350
+ l.push('<?xml version="1.0" encoding="UTF-8"?>');
89351
+ l.push('<!DOCTYPE score-partwise PUBLIC "-//Recordare//DTD MusicXML 4.0 Partwise//EN"');
89352
+ l.push(' "http://www.musicxml.org/dtds/partwise.dtd">');
89353
+ l.push('<score-partwise version="4.0">');
89354
+ if (score.title) l.push("<work>", ...elemento("work-title", score.title), "</work>");
89355
+ l.push(...elemento("movement-title", score.subTitle));
89356
+ l.push("<identification>");
89357
+ if (score.music) l.push(`<creator type="composer">${escapar(score.music)}</creator>`);
89358
+ else if (score.artist) l.push(`<creator type="composer">${escapar(score.artist)}</creator>`);
89359
+ if (score.words) l.push(`<creator type="lyricist">${escapar(score.words)}</creator>`);
89360
+ l.push(...elemento("rights", score.copyright));
89361
+ l.push("<encoding>", "<software>Mandioca Tabs</software>", "</encoding>");
89362
+ l.push("</identification>");
89363
+ const instrumentosPorPista = /* @__PURE__ */ new Map();
89364
+ l.push("<part-list>");
89365
+ for (const track of score.tracks) {
89366
+ const id2 = `P${track.index + 1}`;
89367
+ l.push(`<score-part id="${id2}">`);
89368
+ l.push(...elemento("part-name", track.name || `Pista ${track.index + 1}`));
89369
+ l.push(...elemento("part-abbreviation", track.shortName));
89370
+ const esPercusion = track.staves.some((st) => st.isPercussion);
89371
+ const instrumentos = esPercusion ? instrumentosDePercusion(track) : /* @__PURE__ */ new Map();
89372
+ instrumentosPorPista.set(track.index, instrumentos);
89373
+ if (esPercusion) {
89374
+ for (const [art, idInstrumento] of instrumentos) {
89375
+ const a = track.percussionArticulations[art];
89376
+ l.push(`<score-instrument id="${idInstrumento}">`);
89377
+ l.push(...elemento("instrument-name", `Percusi\xF3n ${art + 1}`));
89378
+ l.push("</score-instrument>");
89379
+ l.push(`<midi-instrument id="${idInstrumento}">`);
89380
+ l.push("<midi-channel>10</midi-channel>");
89381
+ l.push(`<midi-unpitched>${(a?.outputMidiNumber ?? 36) + 1}</midi-unpitched>`);
89382
+ l.push("</midi-instrument>");
89383
+ }
89384
+ } else {
89385
+ l.push(`<score-instrument id="${id2}-I1">`);
89386
+ l.push(...elemento("instrument-name", track.name || `Pista ${track.index + 1}`));
89387
+ l.push("</score-instrument>");
89388
+ l.push(`<midi-instrument id="${id2}-I1">`);
89389
+ l.push(`<midi-channel>${track.playbackInfo.primaryChannel + 1}</midi-channel>`);
89390
+ l.push(`<midi-program>${track.playbackInfo.program + 1}</midi-program>`);
89391
+ l.push(`<volume>${Math.round(track.playbackInfo.volume / 16 * 100)}</volume>`);
89392
+ l.push(`<pan>${Math.round((track.playbackInfo.balance - 8) / 8 * 90)}</pan>`);
89393
+ l.push("</midi-instrument>");
89394
+ }
89395
+ l.push("</score-part>");
89396
+ }
89397
+ l.push("</part-list>");
89398
+ for (const track of score.tracks) {
89399
+ const instrumentos = instrumentosPorPista.get(track.index) ?? /* @__PURE__ */ new Map();
89400
+ l.push(`<part id="P${track.index + 1}">`);
89401
+ const pentagramas = track.staves;
89402
+ if (pentagramas.length === 0) {
89403
+ l.push("</part>");
89404
+ continue;
89405
+ }
89406
+ let armaduraAnterior = null;
89407
+ let compasAnterior = null;
89408
+ let clavesAnteriores = null;
89409
+ const cantidadDeCompases = Math.max(...pentagramas.map((st) => st.bars.length));
89410
+ for (let indiceDeCompas2 = 0; indiceDeCompas2 < cantidadDeCompases; indiceDeCompas2++) {
89411
+ const master = score.masterBars[indiceDeCompas2];
89412
+ l.push(`<measure number="${indiceDeCompas2 + 1}">`);
89413
+ const fifths = (pentagramas[0]?.bars[indiceDeCompas2] ?? pentagramas[0]?.bars[0])?.keySignature ?? master?.keySignature ?? 0;
89414
+ const modo = master?.keySignatureType === _barrel_exports$4.KeySignatureType.Minor ? "minor" : "major";
89415
+ const armadura = `${fifths}/${modo}`;
89416
+ const numerador = master?.timeSignatureNumerator ?? 4;
89417
+ const denominador = master?.timeSignatureDenominator ?? 4;
89418
+ const compas = `${numerador}/${denominador}`;
89419
+ const claves = pentagramas.map((st, k) => {
89420
+ const bar = st.bars[indiceDeCompas2] ?? st.bars[0];
89421
+ return { pentagrama: st, numero: k + 1, ...claveDelPentagrama(st, bar) };
89422
+ });
89423
+ const firmaDeClaves = claves.map((c) => `${c.signo}${c.linea}`).join(",");
89424
+ const cambios = [];
89425
+ if (indiceDeCompas2 === 0) cambios.push(`<divisions>${DIVISIONES_POR_NEGRA}</divisions>`);
89426
+ if (armadura !== armaduraAnterior) {
89427
+ cambios.push("<key>");
89428
+ cambios.push(`<fifths>${fifths}</fifths>`);
89429
+ cambios.push(`<mode>${modo}</mode>`);
89430
+ cambios.push("</key>");
89431
+ armaduraAnterior = armadura;
89432
+ }
89433
+ if (compas !== compasAnterior) {
89434
+ cambios.push("<time>");
89435
+ cambios.push(`<beats>${numerador}</beats>`);
89436
+ cambios.push(`<beat-type>${denominador}</beat-type>`);
89437
+ cambios.push("</time>");
89438
+ compasAnterior = compas;
89439
+ }
89440
+ if (firmaDeClaves !== clavesAnteriores) {
89441
+ if (pentagramas.length > 1 && clavesAnteriores === null)
89442
+ cambios.push(`<staves>${pentagramas.length}</staves>`);
89443
+ const num = (clave) => pentagramas.length > 1 ? ` number="${clave.numero}"` : "";
89444
+ for (const clave of claves) {
89445
+ cambios.push(`<clef${num(clave)}>`);
89446
+ cambios.push(`<sign>${clave.signo}</sign>`);
89447
+ cambios.push(`<line>${clave.linea}</line>`);
89448
+ cambios.push("</clef>");
88941
89449
  }
88942
- const pent = (entero(bloque, /<staff>\s*(\d+)\s*<\/staff>/) ?? 1) - 1;
88943
- const ticAlphaTab = Math.round(inicio * TICS_POR_NEGRA2 / divisiones);
88944
- if (!acorde && bajoPendiente !== null) {
88945
- if (bajoPendiente) bajos.set(`${iParte}|${pent}|${iCompas}|${ticAlphaTab}`, bajoPendiente);
88946
- bajoPendiente = null;
89450
+ for (const clave of claves) {
89451
+ cambios.push(`<staff-details${num(clave)}>`);
89452
+ cambios.push(`<staff-lines>${clave.lineas}</staff-lines>`);
89453
+ if (clave.conAfinacion)
89454
+ clave.pentagrama.tuning.forEach((midi, i2) => {
89455
+ const linea = clave.pentagrama.tuning.length - i2;
89456
+ const { paso, alteracion, octava } = alturaMusicXml(midi, fifths);
89457
+ cambios.push(`<staff-tuning line="${linea}">`);
89458
+ cambios.push(`<tuning-step>${paso}</tuning-step>`);
89459
+ if (alteracion !== 0) cambios.push(`<tuning-alter>${alteracion}</tuning-alter>`);
89460
+ cambios.push(`<tuning-octave>${octava}</tuning-octave>`);
89461
+ cambios.push("</staff-tuning>");
89462
+ });
89463
+ cambios.push("</staff-details>");
88947
89464
  }
88948
- const altura = /<pitch>([\s\S]*?)<\/pitch>/.exec(bloque)?.[1];
88949
- if (!altura) continue;
88950
- const paso = /<step>\s*([A-G])\s*<\/step>/.exec(altura)?.[1];
88951
- const octava = entero(altura, /<octave>\s*(-?\d+)\s*<\/octave>/);
88952
- const alterTexto = /<alter>\s*(-?[\d.]+)\s*<\/alter>/.exec(altura)?.[1];
88953
- const alter = alterTexto === void 0 ? 0 : Number.parseFloat(alterTexto);
88954
- if (!paso || octava === null || !Number.isInteger(alter)) continue;
88955
- const modo = MODO_POR_ALTER.get(alter);
88956
- if (modo === void 0) continue;
88957
- const midi = (octava + 1) * 12 + SEMITONOS[paso] + alter + transposicion;
88958
- const clave = `${iParte}|${pent}|${iCompas}|${ticAlphaTab}|${midi}`;
88959
- if (!ortografia.has(clave)) ortografia.set(clave, modo);
89465
+ clavesAnteriores = firmaDeClaves;
89466
+ }
89467
+ if (cambios.length > 0) l.push("<attributes>", ...cambios, "</attributes>");
89468
+ const casilla = master?.alternateEndings ?? 0;
89469
+ const casillaAntes = score.masterBars[indiceDeCompas2 - 1]?.alternateEndings ?? 0;
89470
+ const casillaDespues = score.masterBars[indiceDeCompas2 + 1]?.alternateEndings ?? 0;
89471
+ const numerosDeCasilla = [0, 1, 2, 3, 4, 5, 6, 7].filter((b) => casilla & 1 << b).map((b) => b + 1).join(", ");
89472
+ if (master?.isRepeatStart || casilla && casilla !== casillaAntes) {
89473
+ l.push('<barline location="left">');
89474
+ if (master?.isRepeatStart) l.push("<bar-style>heavy-light</bar-style>");
89475
+ if (casilla && casilla !== casillaAntes) l.push(`<ending number="${numerosDeCasilla}" type="start"/>`);
89476
+ if (master?.isRepeatStart) l.push('<repeat direction="forward"/>');
89477
+ l.push("</barline>");
89478
+ }
89479
+ if (master?.section?.text || master?.section?.marker)
89480
+ l.push(
89481
+ '<direction placement="above">',
89482
+ "<direction-type>",
89483
+ `<rehearsal>${escapar(master.section.marker || master.section.text)}</rehearsal>`,
89484
+ "</direction-type>",
89485
+ "</direction>"
89486
+ );
89487
+ l.push(...saltosDelCompas(master));
89488
+ const ticsDelCompas = DIVISIONES_POR_NEGRA * 4 * numerador / denominador;
89489
+ const tempos = (master?.tempoAutomations ?? []).map((a) => ({ bpm: a.value, desde: a.ratioPosition }));
89490
+ if (indiceDeCompas2 === 0 && score.tempo && !tempos.some((t) => t.desde <= 1e-6))
89491
+ tempos.unshift({ bpm: score.tempo, desde: 0 });
89492
+ for (const t of tempos) {
89493
+ const bpm = Math.round(t.bpm);
89494
+ const offset = Math.round(t.desde * ticsDelCompas);
89495
+ l.push(
89496
+ '<direction placement="above">',
89497
+ "<direction-type>",
89498
+ "<metronome>",
89499
+ "<beat-unit>quarter</beat-unit>",
89500
+ `<per-minute>${bpm}</per-minute>`,
89501
+ "</metronome>",
89502
+ "</direction-type>",
89503
+ ...offset > 0 ? [`<offset>${offset}</offset>`] : [],
89504
+ `<sound tempo="${bpm}"/>`,
89505
+ "</direction>"
89506
+ );
89507
+ }
89508
+ let ultimoAvance = 0;
89509
+ let hayAlgoEscrito = false;
89510
+ for (const { pentagrama, numero: numero2 } of claves) {
89511
+ const bar = pentagrama.bars[indiceDeCompas2];
89512
+ if (!bar) continue;
89513
+ bar.voices.forEach((voice, indiceDeVoz) => {
89514
+ if (voice.beats.length === 0 || voice.isEmpty) return;
89515
+ if (hayAlgoEscrito && ultimoAvance > 0)
89516
+ l.push("<backup>", `<duration>${ultimoAvance}</duration>`, "</backup>");
89517
+ const { abren, cierran } = gruposDeTresillo(voice.beats);
89518
+ const numeroDePentagrama = pentagramas.length > 1 ? numero2 : null;
89519
+ let avance = 0;
89520
+ voice.beats.forEach((beat, i2) => {
89521
+ const duracion = beat.displayDuration;
89522
+ if (beat.graceType === _barrel_exports$4.GraceType.None) avance += duracion;
89523
+ if (beat.isRest || beat.notes.length === 0) {
89524
+ l.push(...escribirSilencio2(beat, duracion, indiceDeVoz + 1, numeroDePentagrama));
89525
+ return;
89526
+ }
89527
+ beat.notes.forEach((note, k) => {
89528
+ l.push(
89529
+ ...escribirNota(note, beat, pentagrama, {
89530
+ esAcorde: k > 0,
89531
+ fifths,
89532
+ duracion,
89533
+ // ★ La marca de tresillo va en la PRIMERA nota del golpe: es una marca del
89534
+ // golpe, no de cada cuerda que suena en él.
89535
+ abreTresillo: k === 0 && abren.has(i2),
89536
+ cierraTresillo: k === 0 && cierran.has(i2),
89537
+ instrumentos,
89538
+ numeroDeVoz: indiceDeVoz + 1,
89539
+ numeroDePentagrama
89540
+ })
89541
+ );
89542
+ });
89543
+ });
89544
+ ultimoAvance = avance;
89545
+ hayAlgoEscrito = true;
89546
+ });
89547
+ }
89548
+ const cierraCasilla = casilla !== 0 && casilla !== casillaDespues;
89549
+ if (master?.isRepeatEnd || master?.isDoubleBar || cierraCasilla) {
89550
+ l.push('<barline location="right">');
89551
+ if (master?.isRepeatEnd || master?.isDoubleBar)
89552
+ l.push(`<bar-style>${master.isRepeatEnd ? "light-heavy" : "light-light"}</bar-style>`);
89553
+ if (cierraCasilla)
89554
+ l.push(`<ending number="${numerosDeCasilla}" type="${master?.isRepeatEnd ? "stop" : "discontinue"}"/>`);
89555
+ if (master?.isRepeatEnd)
89556
+ l.push(`<repeat direction="backward" times="${Math.max(2, master.repeatCount)}"/>`);
89557
+ l.push("</barline>");
88960
89558
  }
89559
+ l.push("</measure>");
88961
89560
  }
89561
+ l.push("</part>");
88962
89562
  }
88963
- return { ortografia, bajos, matices };
89563
+ l.push("</score-partwise>");
89564
+ return sangrar(l);
88964
89565
  }
88965
- var MODO_DE_ALTERACION, MODO_POR_ALTER, SEMITONOS, TICS_POR_NEGRA2, entero, HIJO_QUE_SE_MUEVE, ATRIBUTOS_DE_SALTO, NEGRAS_POR_FIGURA;
88966
- var init_musicxmlEntrada = __esm({
88967
- "packages/io/src/musicxmlEntrada.ts"() {
89566
+ function sangrar(lineas) {
89567
+ const salida = [];
89568
+ let nivel = 0;
89569
+ for (const linea of lineas) {
89570
+ const cierra = linea.startsWith("</");
89571
+ if (cierra) nivel = Math.max(0, nivel - 1);
89572
+ salida.push(" ".repeat(nivel) + linea);
89573
+ const abre = /^<[^/?!]/.test(linea) && !linea.endsWith("/>") && !/<\/[^>]+>$/.test(linea);
89574
+ if (abre) nivel++;
89575
+ }
89576
+ return salida.join("\n") + "\n";
89577
+ }
89578
+ function exportarMusicXmlComprimido(score) {
89579
+ const xml = exportarMusicXml(score);
89580
+ const contenedor = `<?xml version="1.0" encoding="UTF-8"?>
89581
+ <container>
89582
+ <rootfiles>
89583
+ <rootfile full-path="partitura.musicxml" media-type="application/vnd.recordare.musicxml+xml"/>
89584
+ </rootfiles>
89585
+ </container>
89586
+ `;
89587
+ const codificador = new TextEncoder();
89588
+ return zipSync(
89589
+ {
89590
+ "META-INF/container.xml": [codificador.encode(contenedor), { level: 0 }],
89591
+ "partitura.musicxml": codificador.encode(xml)
89592
+ },
89593
+ // ⚠️ Fecha FIJA, no la de hoy: dos exportaciones iguales tienen que dar dos archivos
89594
+ // iguales byte a byte, o no hay prueba que pueda compararlos ni `diff` de git que
89595
+ // sirva. El porqué del valor concreto está en la constante — se tardó dos rojos en
89596
+ // encontrarlo, y ninguno de los dos era el que yo esperaba.
89597
+ { mtime: FECHA_FIJA_DEL_ZIP }
89598
+ );
89599
+ }
89600
+ var DIVISIONES_POR_NEGRA, FECHA_FIJA_DEL_ZIP, PASOS, NOMBRE_DE_LA_FIGURA2, ALTERACION_POR_MODO, D, SALTOS, claveDeSalto, SALTO_POR_CLAVE;
89601
+ var init_musicxml = __esm({
89602
+ "packages/io/src/musicxml.ts"() {
89603
+ init_alphaTab();
88968
89604
  init_esm();
88969
- MODO_DE_ALTERACION = {
88970
- porDefecto: 0,
88971
- natural: 2,
88972
- sostenido: 3,
88973
- dobleSostenido: 4,
88974
- bemol: 5,
88975
- dobleBemol: 6
88976
- };
88977
- MODO_POR_ALTER = /* @__PURE__ */ new Map([
88978
- [-2, MODO_DE_ALTERACION.dobleBemol],
88979
- [-1, MODO_DE_ALTERACION.bemol],
88980
- [0, MODO_DE_ALTERACION.natural],
88981
- [1, MODO_DE_ALTERACION.sostenido],
88982
- [2, MODO_DE_ALTERACION.dobleSostenido]
89605
+ DIVISIONES_POR_NEGRA = 960;
89606
+ FECHA_FIJA_DEL_ZIP = 946728e6;
89607
+ PASOS = [
89608
+ { paso: "C", semitonos: 0 },
89609
+ { paso: "D", semitonos: 2 },
89610
+ { paso: "E", semitonos: 4 },
89611
+ { paso: "F", semitonos: 5 },
89612
+ { paso: "G", semitonos: 7 },
89613
+ { paso: "A", semitonos: 9 },
89614
+ { paso: "B", semitonos: 11 }
89615
+ ];
89616
+ NOMBRE_DE_LA_FIGURA2 = /* @__PURE__ */ new Map([
89617
+ [-4, "long"],
89618
+ [-2, "breve"],
89619
+ [1, "whole"],
89620
+ [2, "half"],
89621
+ [4, "quarter"],
89622
+ [8, "eighth"],
89623
+ [16, "16th"],
89624
+ [32, "32nd"],
89625
+ [64, "64th"],
89626
+ [128, "128th"],
89627
+ [256, "256th"]
88983
89628
  ]);
88984
- SEMITONOS = { C: 0, D: 2, E: 4, F: 5, G: 7, A: 9, B: 11 };
88985
- TICS_POR_NEGRA2 = 960;
88986
- entero = (texto4, re) => {
88987
- const m = re.exec(texto4);
88988
- return m ? Number.parseInt(m[1], 10) : null;
88989
- };
88990
- HIJO_QUE_SE_MUEVE = /<(clef|key|time|transpose|staff-details)\b[^>]*?(?:\/>|>[\s\S]*?<\/\1>)/g;
88991
- ATRIBUTOS_DE_SALTO = ["coda", "tocoda", "dacapo", "dalsegno", "fine", "segno"];
88992
- NEGRAS_POR_FIGURA = {
88993
- breve: 8,
88994
- whole: 4,
88995
- half: 2,
88996
- quarter: 1,
88997
- eighth: 0.5,
88998
- "16th": 0.25,
88999
- "32nd": 0.125
89000
- };
89629
+ ALTERACION_POR_MODO = { 2: 0, 3: 1, 4: 2, 5: -1, 6: -2 };
89630
+ D = _barrel_exports$4.Direction;
89631
+ SALTOS = [
89632
+ [D.TargetSegno, { simbolo: "segno", sonido: 'segno="segno"' }],
89633
+ [D.TargetSegnoSegno, { simbolo: "segno", sonido: 'segno="segno2"' }],
89634
+ [D.TargetCoda, { simbolo: "coda", sonido: 'coda="coda"' }],
89635
+ [D.TargetDoubleCoda, { simbolo: "coda", sonido: 'coda="coda2"' }],
89636
+ [D.TargetFine, { texto: "Fine", sonido: 'fine="yes"' }],
89637
+ [D.JumpDaCapo, { texto: "D.C.", sonido: 'dacapo="yes"' }],
89638
+ [D.JumpDaCapoAlCoda, { texto: "D.C. al Coda", sonido: 'dacapo="yes"' }],
89639
+ [D.JumpDaCapoAlDoubleCoda, { texto: "D.C. al Coda 2", sonido: 'dacapo="yes"' }],
89640
+ [D.JumpDaCapoAlFine, { texto: "D.C. al Fine", sonido: 'dacapo="yes"' }],
89641
+ [D.JumpDalSegno, { texto: "D.S.", sonido: 'dalsegno="segno"' }],
89642
+ [D.JumpDalSegnoAlCoda, { texto: "D.S. al Coda", sonido: 'dalsegno="segno"' }],
89643
+ [D.JumpDalSegnoAlDoubleCoda, { texto: "D.S. al Coda 2", sonido: 'dalsegno="segno"' }],
89644
+ [D.JumpDalSegnoAlFine, { texto: "D.S. al Fine", sonido: 'dalsegno="segno"' }],
89645
+ [D.JumpDalSegnoSegno, { texto: "D.S.S.", sonido: 'dalsegno="segno2"' }],
89646
+ [D.JumpDalSegnoSegnoAlCoda, { texto: "D.S.S. al Coda", sonido: 'dalsegno="segno2"' }],
89647
+ [D.JumpDalSegnoSegnoAlDoubleCoda, { texto: "D.S.S. al Coda 2", sonido: 'dalsegno="segno2"' }],
89648
+ [D.JumpDalSegnoSegnoAlFine, { texto: "D.S.S. al Fine", sonido: 'dalsegno="segno2"' }],
89649
+ [D.JumpDaCoda, { texto: "To Coda", sonido: 'tocoda="coda"' }],
89650
+ [D.JumpDaDoubleCoda, { texto: "To Coda 2", sonido: 'tocoda="coda2"' }]
89651
+ ];
89652
+ claveDeSalto = (texto4) => texto4.toLowerCase().replace(/double\s*coda/g, "coda 2").replace(/dal\s*segno\s*segno/g, "dss").replace(/dal\s*segno/g, "ds").replace(/da\s*capo/g, "dc").replace(/[^a-z0-9]/g, "");
89653
+ SALTO_POR_CLAVE = new Map(
89654
+ SALTOS.filter(([, s]) => s.texto).map(([d, s]) => [
89655
+ claveDeSalto(s.texto),
89656
+ _barrel_exports$4.Direction[d]
89657
+ ])
89658
+ );
89001
89659
  }
89002
89660
  });
89003
89661
 
89004
- // packages/io/src/importarGp.ts
89005
- function piezaDeBateria(pista, numero2) {
89006
- if (numero2 < 0) return null;
89007
- const delArchivo = pista.percussionArticulations ?? [];
89008
- const midi = numero2 < delArchivo.length ? delArchivo[numero2].outputMidiNumber : SALIDA_DE_ID_GP[numero2] ?? numero2;
89009
- const buscada = PARIENTE_GM[midi] ?? midi;
89010
- return KIT.find((p) => p.midi === buscada)?.indice ?? null;
89011
- }
89012
- function importarGp(bytes, opciones = {}) {
89013
- const xml = textoMusicXml(bytes);
89014
- if (xml === null) {
89015
- const score2 = _barrel_exports$1.ScoreLoader.loadScoreFromBytes(bytes);
89016
- return importarScore(score2, opciones);
89662
+ // packages/io/src/musicxmlEntrada.ts
89663
+ function textoMusicXml(bytes) {
89664
+ if (bytes.length > 4 && bytes[0] === 80 && bytes[1] === 75) {
89665
+ let archivos;
89666
+ try {
89667
+ archivos = unzipSync(bytes);
89668
+ } catch {
89669
+ return null;
89670
+ }
89671
+ const contenedor = archivos["META-INF/container.xml"];
89672
+ if (!contenedor) return null;
89673
+ const ruta = /<rootfile\b[^>]*full-path="([^"]+)"/.exec(strFromU8(contenedor))?.[1];
89674
+ const raiz = ruta ? archivos[ruta] : void 0;
89675
+ return raiz ? decodificar(raiz) : null;
89017
89676
  }
89018
- const preparado = prepararMusicXml(xml);
89019
- const score = _barrel_exports$1.ScoreLoader.loadScoreFromBytes(preparado.bytes);
89020
- const leido = leerDelArchivo(preparado.xml);
89021
- const resultado = importarScore(score, opciones, { ortografia: leido.ortografia, bajos: leido.bajos, matices: leido.matices });
89022
- resultado.avisos.push(...preparado.correcciones);
89023
- return resultado;
89677
+ const texto4 = decodificar(bytes.subarray(0, Math.min(bytes.length, 4096)));
89678
+ if (!/<score-(partwise|timewise)\b/.test(texto4) && !/<!DOCTYPE\s+score-/.test(texto4)) return null;
89679
+ return decodificar(bytes);
89024
89680
  }
89025
- function prepararParaAlphaTab(bytes) {
89026
- const xml = textoMusicXml(bytes);
89027
- return xml === null ? bytes : prepararMusicXml(xml).bytes;
89681
+ function decodificar(bytes) {
89682
+ if (bytes[0] === 255 && bytes[1] === 254) return new TextDecoder("utf-16le").decode(bytes);
89683
+ if (bytes[0] === 254 && bytes[1] === 255) return new TextDecoder("utf-16be").decode(bytes);
89684
+ return new TextDecoder("utf-8").decode(bytes);
89028
89685
  }
89029
- function importarScore(score, opciones = {}, datos = {}) {
89030
- const avisos = [];
89031
- const documento = crearDocumento({ clienteId: opciones.clienteId });
89032
- documento.ponerCabecera({
89033
- titulo: score.title ?? "",
89034
- artista: score.artist ?? "",
89035
- album: score.album ?? "",
89036
- tempo: score.tempo ?? 0,
89037
- mostrarMatices: !score.stylesheet.hideDynamics
89686
+ function prepararMusicXml(xml) {
89687
+ const correcciones = [];
89688
+ let atributosMovidos = 0;
89689
+ let acordesSeparados = 0;
89690
+ let saltosMovidos = 0;
89691
+ let salida = xml.replace(/<part\b[^>]*>[\s\S]*?<\/part>/g, (parteOriginal) => {
89692
+ const movidos = moverAtributosDeFinDeCompas(parteOriginal);
89693
+ atributosMovidos += movidos.cuantos;
89694
+ const separados = separarAcordesEntrePentagramas(movidos.texto);
89695
+ acordesSeparados += separados.cuantos;
89696
+ const saltos = sacarSaltosDeLaIndicacion(separados.texto);
89697
+ saltosMovidos += saltos.cuantos;
89698
+ const { texto: texto4, cambio } = compactarVoces(saltos.texto);
89699
+ if (cambio) correcciones.push(cambio);
89700
+ return texto4;
89038
89701
  });
89039
- avisos.push(
89040
- 'Las preferencias de dibujo del archivo (el "stylesheet" de Guitar Pro) no se importan, salvo si se muestran los matices. La m\xFAsica no cambia; el aspecto s\xED.'
89041
- );
89042
- if (opciones.autoria) {
89043
- documento.ponerAutoria(opciones.autoria);
89044
- } else {
89045
- documento.ponerAutoria({
89046
- escritaPor: "",
89047
- origen: "transcripcion",
89048
- obraTitulo: score.title ?? "",
89049
- obraAutores: score.artist ?? "",
89050
- obraEditora: ""
89051
- });
89052
- avisos.push(
89053
- "La autor\xEDa qued\xF3 sin completar: se import\xF3 como transcripci\xF3n con los datos del archivo. Hay que confirmarla antes de publicar o compartir el documento."
89702
+ if (atributosMovidos) {
89703
+ correcciones.push(
89704
+ `${atributosMovidos} cambios de clave, armadura o comp\xE1s escritos entre las notas se pusieron al principio del comp\xE1s donde rigen.`
89054
89705
  );
89055
89706
  }
89056
- const saltosEscritos = saltosDelTexto(score);
89057
- const idsDeCompasMaestro = score.masterBars.map(
89058
- (maestro) => documento.agregarCompasMaestro(normalizarCompasMaestro({
89059
- numerador: maestro.timeSignatureNumerator,
89060
- denominador: maestro.timeSignatureDenominator,
89061
- tonalidad: numeroDeTonalidad(tonalidadDeConcierto(score, maestro.index)),
89062
- // ⚠️ El ensayo de un MusicXML (`<rehearsal>A</rehearsal>`) llena sólo `section.marker`;
89063
- // `text` queda vacío y las letras A, B, C… se perdían todas. Un `.gp` trae las dos y
89064
- // manda el texto, como siempre.
89065
- seccion: maestro.section?.text || maestro.section?.marker || "",
89066
- // El cambio de tempo que arranca EN este compás. Los que caen a mitad van a su figura
89067
- // (ver `temposAMitad` abajo).
89068
- // ⛔ (2026-11-10) Antes era `tempoAutomations[0]` sin mirar dónde caía: un 140 a mitad
89069
- // del compás 2 se aplicaba desde su principio y la primera mitad sonaba a 140.
89070
- tempo: maestro.tempoAutomations?.find((a) => a.ratioPosition <= 1e-6)?.value ?? null,
89071
- repiteDesdeAca: maestro.isRepeatStart,
89072
- repiteVeces: maestro.repeatCount,
89073
- // ⚠️ Novena pérdida silenciosa de la misma familia: 2 de los 15 `.gp` de Daniel
89074
- // declaran una anacruza y el modelo la descartaba.
89075
- esAnacruza: maestro.isAnacrusis,
89076
- // ── Los signos de compás de Guitar Pro ────────────────────────────────
89077
- casillas: maestro.alternateEndings,
89078
- dobleBarra: maestro.isDoubleBar,
89079
- tiempoLibre: maestro.isFreeTime,
89080
- swing: maestro.tripletFeel,
89081
- // ⚠️ `simileMark` es del compás DE LA PISTA en alphaTab (`Bar.simileMark`), no del
89082
- // maestro. Se lee más abajo, al recorrer los compases de cada pentagrama, y se
89083
- // escribe acá arriba porque en el modelo la repetición de compás es de la obra:
89084
- // un «igual que el anterior» que valiera para la guitarra y no para el bajo
89085
- // dejaría los dos instrumentos diciendo cosas distintas sobre el mismo compás.
89086
- repiteAnterior: 0,
89087
- // ⚠️ `directions` es un `Set` o `null`, no una lista. Un `?? []` sobre un Set daría
89088
- // el Set, y `.map` no existe ahí: hay que expandirlo.
89089
- //
89090
- // ⚠️⚠️ Y se guarda el NOMBRE, no el número. `String(d)` daba `"1"` —el valor del enum
89091
- // convertido a texto— y la proyección buscaba `Direction["1"]`, que en un enum con
89092
- // mapeo inverso devuelve el *nombre* y no un número: la guarda lo descartaba y **las
89093
- // direcciones se perdían enteras, en silencio**. Lo cazó la prueba de ida y vuelta
89094
- // con signos, que es exactamente para lo que existe.
89095
- //
89096
- // ★ El nombre y no el número, además, porque acá es una LISTA y se lee: un
89097
- // `["JumpDalSegnoAlCoda"]` en el documento dice algo; un `[10]` no.
89098
- direcciones: maestro.directions ? [...saltosEscritos.precisos.get(maestro.index) ?? maestro.directions].map(
89099
- (d) => _barrel_exports$4.Direction[d] ?? String(d)
89100
- ) : []
89101
- }))
89102
- );
89103
- const saltos = /* @__PURE__ */ new Set();
89104
- for (const pista of score.tracks) for (const i2 of pista.lineBreaks ?? []) saltos.add(i2);
89105
- for (const i2 of saltos) {
89106
- const id2 = idsDeCompasMaestro[i2];
89107
- if (id2 !== void 0 && i2 > 0) documento.ponerSaltoDeRenglon(id2, true);
89707
+ if (acordesSeparados) {
89708
+ correcciones.push(`${acordesSeparados} notas de acordes repartidos entre los dos pentagramas se pusieron en el suyo.`);
89108
89709
  }
89109
- const letrasDobles = { cuantas: 0, versosDeMas: 0 };
89110
- const tipoDelPentagrama = (p) => p.isPercussion ? "percusion" : p.tuning.length === 0 ? "altura" : "trastes";
89710
+ if (saltosMovidos) correcciones.push(`${saltosMovidos} saltos (D.C., D.S., Coda, Fine, Segno) se leyeron de su indicaci\xF3n.`);
89711
+ let juntados = 0;
89712
+ salida = salida.replace(/<direction\b[^>]*>[\s\S]*?<\/direction>/g, (dir) => {
89713
+ const palabras = [...dir.matchAll(/<words\b[^>]*>([\s\S]*?)<\/words>/g)];
89714
+ if (palabras.length < 2) return dir;
89715
+ juntados++;
89716
+ const todo = palabras.map((p) => p[1].trim()).filter(Boolean).join("\n");
89717
+ let primero = true;
89718
+ return dir.replace(/<words\b([^>]*)>[\s\S]*?<\/words>/g, (_m, attrs) => {
89719
+ if (!primero) return "";
89720
+ primero = false;
89721
+ return `<words${attrs}>${todo}</words>`;
89722
+ });
89723
+ });
89724
+ if (juntados) correcciones.push(`${juntados} indicaciones de texto en varios renglones se juntaron en una.`);
89725
+ let silabas2 = 0;
89726
+ salida = salida.replace(/<lyric\b[^>]*>[\s\S]*?<\/lyric>/g, (lyric) => {
89727
+ const tipo = /<syllabic>\s*(\w+)\s*<\/syllabic>/.exec(lyric)?.[1];
89728
+ if (tipo !== "begin" && tipo !== "middle") return lyric;
89729
+ return lyric.replace(/(<text\b[^>]*>)([\s\S]*?)(<\/text>)/, (m, a, texto4, b) => {
89730
+ if (texto4.trimEnd().endsWith("-")) return m;
89731
+ silabas2++;
89732
+ return `${a}${texto4.trimEnd()}-${b}`;
89733
+ });
89734
+ });
89735
+ if (silabas2) correcciones.push(`${silabas2} s\xEDlabas unidas de la letra se marcaron con su guion.`);
89736
+ let tempos = 0;
89737
+ salida = salida.replace(/<metronome\b[^>]*>[\s\S]*?<\/metronome>/g, (met) => {
89738
+ const unidad = /<beat-unit>\s*([\w-]+)\s*<\/beat-unit>/.exec(met)?.[1];
89739
+ const porMinuto = /<per-minute>\s*([\d.]+)\s*<\/per-minute>/.exec(met)?.[1];
89740
+ const negras = unidad ? NEGRAS_POR_FIGURA[unidad] : void 0;
89741
+ if (!unidad || !porMinuto || negras === void 0 || /<beat-unit>[\s\S]*<beat-unit>/.test(met)) return met;
89742
+ const puntillos = (met.match(/<beat-unit-dot\s*\/>/g) ?? []).length;
89743
+ const factor = negras * (puntillos === 0 ? 1 : puntillos === 1 ? 1.5 : 1.75);
89744
+ if (factor === 1) return met;
89745
+ tempos++;
89746
+ const bpm = Math.round(Number.parseFloat(porMinuto) * factor * 100) / 100;
89747
+ return met.replace(/<beat-unit>[\s\S]*?<\/beat-unit>/, "<beat-unit>quarter</beat-unit>").replace(/<beat-unit-dot\s*\/>/g, "").replace(/<per-minute>[\s\S]*?<\/per-minute>/, `<per-minute>${bpm}</per-minute>`);
89748
+ });
89749
+ if (tempos) correcciones.push(`${tempos} indicaciones de tempo se pasaron a negras (mismo pulso).`);
89750
+ return { xml: salida, bytes: new TextEncoder().encode(salida), correcciones };
89751
+ }
89752
+ function compasesDe(parte) {
89753
+ return [...parte.matchAll(/(<measure\b[^>]*>)([\s\S]*?)<\/measure>/g)].map((m) => ({
89754
+ inicio: m.index,
89755
+ fin: m.index + m[0].length,
89756
+ apertura: m[1],
89757
+ contenido: m[2]
89758
+ }));
89759
+ }
89760
+ function rearmar(parte, compases, nuevos) {
89761
+ let salida = "";
89762
+ let cursor = 0;
89763
+ compases.forEach((c, i2) => {
89764
+ salida += parte.slice(cursor, c.inicio) + c.apertura + nuevos[i2] + "</measure>";
89765
+ cursor = c.fin;
89766
+ });
89767
+ return salida + parte.slice(cursor);
89768
+ }
89769
+ function tiempoAl(pedazo) {
89770
+ let t = 0;
89771
+ for (const m of pedazo.matchAll(/<(note|backup|forward)\b[\s\S]*?<\/\1>/g)) {
89772
+ const dur = entero(m[0], /<duration>\s*(\d+)\s*<\/duration>/) ?? 0;
89773
+ if (m[1] === "backup") t -= dur;
89774
+ else if (m[1] === "forward") t += dur;
89775
+ else if (!/<chord\s*\/>/.test(m[0]) && !/<grace\b/.test(m[0])) t += dur;
89776
+ }
89777
+ return t;
89778
+ }
89779
+ function moverAtributosDeFinDeCompas(parte) {
89780
+ const compases = compasesDe(parte);
89781
+ let cuantos = 0;
89782
+ let arrastre = "";
89783
+ const nuevos = compases.map((c) => {
89784
+ let contenido = c.contenido;
89785
+ const primeraNota = contenido.search(/<note\b/);
89786
+ if (primeraNota >= 0) {
89787
+ let llevar = "";
89788
+ let alPrincipio = "";
89789
+ const despues = contenido.slice(primeraNota);
89790
+ contenido = contenido.slice(0, primeraNota) + despues.replace(/<attributes\b[^>]*>([\s\S]*?)<\/attributes>/g, (bloque, adentro, donde) => {
89791
+ const hijos = adentro.match(HIJO_QUE_SE_MUEVE);
89792
+ if (!hijos) return bloque;
89793
+ cuantos += hijos.length;
89794
+ if (tiempoAl(despues.slice(0, donde)) <= 0) alPrincipio += hijos.join("");
89795
+ else llevar += hijos.join("");
89796
+ const resto = adentro.replace(HIJO_QUE_SE_MUEVE, "");
89797
+ return resto.trim() ? `<attributes>${resto}</attributes>` : "";
89798
+ });
89799
+ const entra2 = arrastre + alPrincipio;
89800
+ arrastre = llevar;
89801
+ return (entra2 ? `<attributes>${entra2}</attributes>` : "") + contenido;
89802
+ }
89803
+ const entra = arrastre;
89804
+ arrastre = "";
89805
+ return (entra ? `<attributes>${entra}</attributes>` : "") + contenido;
89806
+ });
89807
+ return cuantos ? { texto: rearmar(parte, compases, nuevos), cuantos } : { texto: parte, cuantos: 0 };
89808
+ }
89809
+ function separarAcordesEntrePentagramas(parte) {
89810
+ const compases = compasesDe(parte);
89811
+ let cuantos = 0;
89812
+ const nuevos = compases.map((c) => {
89813
+ const notas = [...c.contenido.matchAll(/<note\b[\s\S]*?<\/note>/g)];
89814
+ if (notas.length < 2) return c.contenido;
89815
+ let salida = "";
89816
+ let cursor = 0;
89817
+ let i2 = 0;
89818
+ while (i2 < notas.length) {
89819
+ const grupo = [notas[i2]];
89820
+ let j = i2 + 1;
89821
+ while (j < notas.length) {
89822
+ const anterior = notas[j - 1];
89823
+ const entre = c.contenido.slice(anterior.index + anterior[0].length, notas[j].index);
89824
+ if (entre.trim() || !/<chord\s*\/>/.test(notas[j][0])) break;
89825
+ grupo.push(notas[j]);
89826
+ j++;
89827
+ }
89828
+ i2 = j;
89829
+ const pent = (n) => entero(n[0], /<staff>\s*(\d+)\s*<\/staff>/) ?? 1;
89830
+ const cabeza = grupo[0];
89831
+ const pentDeCabeza = pent(cabeza);
89832
+ const ajenas = grupo.filter((n) => pent(n) !== pentDeCabeza);
89833
+ const durCabeza = entero(cabeza[0], /<duration>\s*(\d+)\s*<\/duration>/);
89834
+ if (ajenas.length === 0 || /<chord\s*\/>/.test(cabeza[0]) || grupo.some((n) => /<grace\b|<cue\s*\/>/.test(n[0])) || durCabeza === null) {
89835
+ continue;
89836
+ }
89837
+ cuantos += ajenas.length;
89838
+ const propias = grupo.filter((n) => pent(n) === pentDeCabeza).map((n) => n[0]);
89839
+ let texto4 = propias.join("");
89840
+ const porPentagrama = /* @__PURE__ */ new Map();
89841
+ for (const n of ajenas) porPentagrama.set(pent(n), [...porPentagrama.get(pent(n)) ?? [], n[0]]);
89842
+ for (const notasDelPent of porPentagrama.values()) {
89843
+ const primera = notasDelPent[0].replace(/<chord\s*\/>\s*/, "");
89844
+ const durPrimera = entero(primera, /<duration>\s*(\d+)\s*<\/duration>/) ?? durCabeza;
89845
+ texto4 += `<backup><duration>${durCabeza}</duration></backup>` + primera + notasDelPent.slice(1).join("");
89846
+ const delta = durCabeza - durPrimera;
89847
+ if (delta > 0) texto4 += `<forward><duration>${delta}</duration></forward>`;
89848
+ else if (delta < 0) texto4 += `<backup><duration>${-delta}</duration></backup>`;
89849
+ }
89850
+ const ultimo = grupo[grupo.length - 1];
89851
+ salida += c.contenido.slice(cursor, cabeza.index) + texto4;
89852
+ cursor = ultimo.index + ultimo[0].length;
89853
+ }
89854
+ return salida + c.contenido.slice(cursor);
89855
+ });
89856
+ return cuantos ? { texto: rearmar(parte, compases, nuevos), cuantos } : { texto: parte, cuantos: 0 };
89857
+ }
89858
+ function sacarSaltosDeLaIndicacion(parte) {
89859
+ let cuantos = 0;
89860
+ const texto4 = parte.replace(/<direction\b[^>]*>[\s\S]*?<\/direction>/g, (dir, desde, todo) => {
89861
+ const sonido = /<sound\b([^>]*?)\/?>/.exec(dir)?.[1];
89862
+ if (!sonido) return dir;
89863
+ const atributos = ATRIBUTOS_DE_SALTO.map((a) => new RegExp(`\\b${a}="([^"]*)"`).exec(sonido)?.[0]).filter((a) => a !== void 0);
89864
+ if (atributos.length === 0) return dir;
89865
+ const copia = `<sound ${atributos.join(" ")}/>`;
89866
+ if (todo.slice(desde + dir.length).trimStart().startsWith(copia)) return dir;
89867
+ cuantos++;
89868
+ return dir + copia;
89869
+ });
89870
+ return { texto: texto4, cuantos };
89871
+ }
89872
+ function compactarVoces(parte) {
89873
+ const porPentagrama = /* @__PURE__ */ new Map();
89874
+ const pentagramaDeVoz = /* @__PURE__ */ new Map();
89875
+ for (const m of parte.matchAll(/<note\b[\s\S]*?<\/note>/g)) {
89876
+ const voz = entero(m[0], /<voice>\s*(\d+)\s*<\/voice>/);
89877
+ if (voz === null) continue;
89878
+ const pent = entero(m[0], /<staff>\s*(\d+)\s*<\/staff>/) ?? 1;
89879
+ const lista2 = porPentagrama.get(pent) ?? [];
89880
+ if (!lista2.includes(voz)) lista2.push(voz);
89881
+ porPentagrama.set(pent, lista2);
89882
+ if (!pentagramaDeVoz.has(voz)) pentagramaDeVoz.set(voz, pent);
89883
+ }
89884
+ const nueva = /* @__PURE__ */ new Map();
89885
+ let hayCambio = false;
89886
+ for (const [pent, voces] of porPentagrama) {
89887
+ ;
89888
+ [...voces].sort((a, b) => a - b).forEach((voz, i2) => {
89889
+ nueva.set(`${pent}|${voz}`, i2 + 1);
89890
+ if (voz !== i2 + 1) hayCambio = true;
89891
+ });
89892
+ }
89893
+ if (!hayCambio) return { texto: parte, cambio: null };
89894
+ const renumerar = (bloque, pentPorOmision) => {
89895
+ const voz = entero(bloque, /<voice>\s*(\d+)\s*<\/voice>/);
89896
+ if (voz === null) return bloque;
89897
+ const pent = entero(bloque, /<staff>\s*(\d+)\s*<\/staff>/) ?? pentPorOmision(voz);
89898
+ const n = nueva.get(`${pent}|${voz}`);
89899
+ return n === void 0 ? bloque : bloque.replace(/<voice>\s*\d+\s*<\/voice>/, `<voice>${n}</voice>`);
89900
+ };
89901
+ const texto4 = parte.replace(/<note\b[\s\S]*?<\/note>/g, (b) => renumerar(b, () => 1)).replace(/<forward\b[\s\S]*?<\/forward>/g, (b) => renumerar(b, (v) => pentagramaDeVoz.get(v) ?? 1));
89902
+ const id2 = /<part\b[^>]*\bid="([^"]*)"/.exec(parte)?.[1] ?? "?";
89903
+ const detalle = [...porPentagrama].map(([p, vs]) => `pentagrama ${p}: voces ${[...vs].sort((a, b) => a - b).join(", ")}`).join("; ");
89904
+ return { texto: texto4, cambio: `Parte ${id2}: las voces se renumeraron por pentagrama (${detalle}).` };
89905
+ }
89906
+ function leerOrtografia(xml) {
89907
+ return leerDelArchivo(xml).ortografia;
89908
+ }
89909
+ function direccionesDeLaIndicacion(bloque) {
89910
+ const sonido = /<sound\b([^>]*?)\/?>/.exec(bloque)?.[1] ?? "";
89911
+ const atributo = (n) => new RegExp(`(?:^|\\s)${n}="([^"]*)"`).exec(sonido)?.[1];
89912
+ const palabras = [...bloque.matchAll(/<words\b[^>]*>([\s\S]*?)<\/words>/g)].map((m) => m[1]).join(" ").trim();
89913
+ const dicho = palabras ? direccionDeLasPalabras(palabras) : null;
89914
+ const salida = /* @__PURE__ */ new Set();
89915
+ const segno = atributo("segno");
89916
+ if (/<segno\b/.test(bloque) || segno !== void 0) salida.add(segno === "segno2" ? "TargetSegnoSegno" : "TargetSegno");
89917
+ const coda = atributo("coda");
89918
+ if (/<coda\b/.test(bloque) || coda !== void 0) salida.add(coda === "coda2" ? "TargetDoubleCoda" : "TargetCoda");
89919
+ if (atributo("fine") !== void 0) salida.add("TargetFine");
89920
+ const tocoda = atributo("tocoda");
89921
+ if (tocoda !== void 0) salida.add(tocoda === "coda2" ? "JumpDaDoubleCoda" : "JumpDaCoda");
89922
+ if (atributo("dacapo") !== void 0) salida.add(dicho?.startsWith("JumpDaCapo") ? dicho : "JumpDaCapo");
89923
+ const dalsegno = atributo("dalsegno");
89924
+ if (dalsegno !== void 0) {
89925
+ const familia = dalsegno === "segno2" ? "JumpDalSegnoSegno" : "JumpDalSegno";
89926
+ const delaFamilia = dicho !== null && dicho.startsWith(familia) && (familia === "JumpDalSegnoSegno" || !dicho.startsWith("JumpDalSegnoSegno"));
89927
+ salida.add(delaFamilia ? dicho : familia);
89928
+ }
89929
+ return [...salida];
89930
+ }
89931
+ function leerDelArchivo(xml) {
89932
+ const ortografia = /* @__PURE__ */ new Map();
89933
+ const bajos = /* @__PURE__ */ new Map();
89934
+ const matices = /* @__PURE__ */ new Map();
89935
+ const saltos = /* @__PURE__ */ new Map();
89936
+ let iParte = -1;
89937
+ for (const parte of xml.matchAll(/<part\b[^>]*>([\s\S]*?)<\/part>/g)) {
89938
+ iParte++;
89939
+ let divisiones = 1;
89940
+ let transposicion = 0;
89941
+ let iCompas = -1;
89942
+ for (const compas of parte[1].matchAll(/<measure\b[^>]*>([\s\S]*?)<\/measure>/g)) {
89943
+ iCompas++;
89944
+ let tic = 0;
89945
+ let ticAnterior = 0;
89946
+ let bajoPendiente = null;
89947
+ for (const el of compas[1].matchAll(/<(note|backup|forward|attributes|harmony|direction)\b[\s\S]*?<\/\1>/g)) {
89948
+ const bloque = el[0];
89949
+ const tipo = el[1];
89950
+ if (tipo === "direction") {
89951
+ if (iParte === 0) {
89952
+ const aca = direccionesDeLaIndicacion(bloque);
89953
+ if (aca.length > 0) saltos.set(iCompas, [.../* @__PURE__ */ new Set([...saltos.get(iCompas) ?? [], ...aca])]);
89954
+ }
89955
+ const nombre = /<dynamics\b[^>]*>\s*<([a-z]+)\s*\/>/.exec(bloque)?.[1];
89956
+ if (nombre) {
89957
+ const pent2 = (entero(bloque, /<staff>\s*(\d+)\s*<\/staff>/) ?? 1) - 1;
89958
+ const clave2 = `${iParte}|${pent2}`;
89959
+ const lista2 = matices.get(clave2) ?? [];
89960
+ lista2.push({ compas: iCompas, tic: Math.round(tic * TICS_POR_NEGRA2 / divisiones), nombre });
89961
+ matices.set(clave2, lista2);
89962
+ }
89963
+ continue;
89964
+ }
89965
+ if (tipo === "attributes") {
89966
+ divisiones = entero(bloque, /<divisions>\s*(\d+)\s*<\/divisions>/) ?? divisiones;
89967
+ const transponer2 = /<transpose\b[^>]*>([\s\S]*?)<\/transpose>/.exec(bloque)?.[1];
89968
+ if (transponer2 !== void 0) {
89969
+ transposicion = (entero(transponer2, /<chromatic>\s*(-?\d+)\s*<\/chromatic>/) ?? 0) + 12 * (entero(transponer2, /<octave-change>\s*(-?\d+)\s*<\/octave-change>/) ?? 0);
89970
+ }
89971
+ continue;
89972
+ }
89973
+ if (tipo === "harmony") {
89974
+ const bajo = /<bass>([\s\S]*?)<\/bass>/.exec(bloque)?.[1];
89975
+ const paso2 = bajo ? /<bass-step>\s*([A-G])\s*<\/bass-step>/.exec(bajo)?.[1] : void 0;
89976
+ if (bajoPendiente === null) {
89977
+ const alter2 = bajo ? entero(bajo, /<bass-alter>\s*(-?\d+)\s*<\/bass-alter>/) ?? 0 : 0;
89978
+ bajoPendiente = paso2 ? `/${paso2}${alter2 === -2 ? "bb" : alter2 === -1 ? "b" : alter2 === 1 ? "#" : alter2 === 2 ? "x" : ""}` : "";
89979
+ }
89980
+ continue;
89981
+ }
89982
+ const dur = entero(bloque, /<duration>\s*(\d+)\s*<\/duration>/) ?? 0;
89983
+ if (tipo === "backup") {
89984
+ tic -= dur;
89985
+ continue;
89986
+ }
89987
+ if (tipo === "forward") {
89988
+ tic += dur;
89989
+ continue;
89990
+ }
89991
+ if (/<grace\b/.test(bloque) || /<cue\s*\/>/.test(bloque)) continue;
89992
+ const acorde = /<chord\s*\/>/.test(bloque);
89993
+ const inicio = acorde ? ticAnterior : tic;
89994
+ if (!acorde) {
89995
+ ticAnterior = tic;
89996
+ tic += dur;
89997
+ }
89998
+ const pent = (entero(bloque, /<staff>\s*(\d+)\s*<\/staff>/) ?? 1) - 1;
89999
+ const ticAlphaTab = Math.round(inicio * TICS_POR_NEGRA2 / divisiones);
90000
+ if (!acorde && bajoPendiente !== null) {
90001
+ if (bajoPendiente) bajos.set(`${iParte}|${pent}|${iCompas}|${ticAlphaTab}`, bajoPendiente);
90002
+ bajoPendiente = null;
90003
+ }
90004
+ const altura = /<pitch>([\s\S]*?)<\/pitch>/.exec(bloque)?.[1];
90005
+ if (!altura) continue;
90006
+ const paso = /<step>\s*([A-G])\s*<\/step>/.exec(altura)?.[1];
90007
+ const octava = entero(altura, /<octave>\s*(-?\d+)\s*<\/octave>/);
90008
+ const alterTexto = /<alter>\s*(-?[\d.]+)\s*<\/alter>/.exec(altura)?.[1];
90009
+ const alter = alterTexto === void 0 ? 0 : Number.parseFloat(alterTexto);
90010
+ if (!paso || octava === null || !Number.isInteger(alter)) continue;
90011
+ const modo = MODO_POR_ALTER.get(alter);
90012
+ if (modo === void 0) continue;
90013
+ const midi = (octava + 1) * 12 + SEMITONOS[paso] + alter + transposicion;
90014
+ const clave = `${iParte}|${pent}|${iCompas}|${ticAlphaTab}|${midi}`;
90015
+ if (!ortografia.has(clave)) ortografia.set(clave, modo);
90016
+ }
90017
+ }
90018
+ }
90019
+ return { ortografia, bajos, matices, saltos };
90020
+ }
90021
+ var MODO_DE_ALTERACION, MODO_POR_ALTER, SEMITONOS, TICS_POR_NEGRA2, entero, HIJO_QUE_SE_MUEVE, ATRIBUTOS_DE_SALTO, NEGRAS_POR_FIGURA;
90022
+ var init_musicxmlEntrada = __esm({
90023
+ "packages/io/src/musicxmlEntrada.ts"() {
90024
+ init_esm();
90025
+ init_musicxml();
90026
+ MODO_DE_ALTERACION = {
90027
+ porDefecto: 0,
90028
+ natural: 2,
90029
+ sostenido: 3,
90030
+ dobleSostenido: 4,
90031
+ bemol: 5,
90032
+ dobleBemol: 6
90033
+ };
90034
+ MODO_POR_ALTER = /* @__PURE__ */ new Map([
90035
+ [-2, MODO_DE_ALTERACION.dobleBemol],
90036
+ [-1, MODO_DE_ALTERACION.bemol],
90037
+ [0, MODO_DE_ALTERACION.natural],
90038
+ [1, MODO_DE_ALTERACION.sostenido],
90039
+ [2, MODO_DE_ALTERACION.dobleSostenido]
90040
+ ]);
90041
+ SEMITONOS = { C: 0, D: 2, E: 4, F: 5, G: 7, A: 9, B: 11 };
90042
+ TICS_POR_NEGRA2 = 960;
90043
+ entero = (texto4, re) => {
90044
+ const m = re.exec(texto4);
90045
+ return m ? Number.parseInt(m[1], 10) : null;
90046
+ };
90047
+ HIJO_QUE_SE_MUEVE = /<(clef|key|time|transpose|staff-details)\b[^>]*?(?:\/>|>[\s\S]*?<\/\1>)/g;
90048
+ ATRIBUTOS_DE_SALTO = ["coda", "tocoda", "dacapo", "dalsegno", "fine", "segno"];
90049
+ NEGRAS_POR_FIGURA = {
90050
+ breve: 8,
90051
+ whole: 4,
90052
+ half: 2,
90053
+ quarter: 1,
90054
+ eighth: 0.5,
90055
+ "16th": 0.25,
90056
+ "32nd": 0.125
90057
+ };
90058
+ }
90059
+ });
90060
+
90061
+ // packages/io/src/importarGp.ts
90062
+ function piezaDeBateria(pista, numero2) {
90063
+ if (numero2 < 0) return null;
90064
+ const delArchivo = pista.percussionArticulations ?? [];
90065
+ const midi = numero2 < delArchivo.length ? delArchivo[numero2].outputMidiNumber : SALIDA_DE_ID_GP[numero2] ?? numero2;
90066
+ const buscada = PARIENTE_GM[midi] ?? midi;
90067
+ return KIT.find((p) => p.midi === buscada)?.indice ?? null;
90068
+ }
90069
+ function importarGp(bytes, opciones = {}) {
90070
+ const xml = textoMusicXml(bytes);
90071
+ if (xml === null) {
90072
+ const score2 = _barrel_exports$1.ScoreLoader.loadScoreFromBytes(bytes);
90073
+ return importarScore(score2, opciones);
90074
+ }
90075
+ const preparado = prepararMusicXml(xml);
90076
+ const score = _barrel_exports$1.ScoreLoader.loadScoreFromBytes(preparado.bytes);
90077
+ const leido = leerDelArchivo(preparado.xml);
90078
+ const resultado = importarScore(score, opciones, { ortografia: leido.ortografia, bajos: leido.bajos, matices: leido.matices, saltos: leido.saltos });
90079
+ resultado.avisos.push(...preparado.correcciones);
90080
+ return resultado;
90081
+ }
90082
+ function prepararParaAlphaTab(bytes) {
90083
+ const xml = textoMusicXml(bytes);
90084
+ return xml === null ? bytes : prepararMusicXml(xml).bytes;
90085
+ }
90086
+ function importarScore(score, opciones = {}, datos = {}) {
90087
+ const avisos = [];
90088
+ const documento = crearDocumento({ clienteId: opciones.clienteId });
90089
+ documento.ponerCabecera({
90090
+ titulo: score.title ?? "",
90091
+ artista: score.artist ?? "",
90092
+ album: score.album ?? "",
90093
+ tempo: score.tempo ?? 0,
90094
+ mostrarMatices: !score.stylesheet.hideDynamics
90095
+ });
90096
+ avisos.push(
90097
+ 'Las preferencias de dibujo del archivo (el "stylesheet" de Guitar Pro) no se importan, salvo si se muestran los matices. La m\xFAsica no cambia; el aspecto s\xED.'
90098
+ );
90099
+ if (opciones.autoria) {
90100
+ documento.ponerAutoria(opciones.autoria);
90101
+ } else {
90102
+ documento.ponerAutoria({
90103
+ escritaPor: "",
90104
+ origen: "transcripcion",
90105
+ obraTitulo: score.title ?? "",
90106
+ obraAutores: score.artist ?? "",
90107
+ obraEditora: ""
90108
+ });
90109
+ avisos.push(
90110
+ "La autor\xEDa qued\xF3 sin completar: se import\xF3 como transcripci\xF3n con los datos del archivo. Hay que confirmarla antes de publicar o compartir el documento."
90111
+ );
90112
+ }
90113
+ const saltosEscritos = saltosDelTexto(score);
90114
+ const idsDeCompasMaestro = score.masterBars.map(
90115
+ (maestro) => documento.agregarCompasMaestro(normalizarCompasMaestro({
90116
+ numerador: maestro.timeSignatureNumerator,
90117
+ denominador: maestro.timeSignatureDenominator,
90118
+ tonalidad: numeroDeTonalidad(tonalidadDeConcierto(score, maestro.index)),
90119
+ // ⚠️ El ensayo de un MusicXML (`<rehearsal>A</rehearsal>`) llena sólo `section.marker`;
90120
+ // `text` queda vacío y las letras A, B, C… se perdían todas. Un `.gp` trae las dos y
90121
+ // manda el texto, como siempre.
90122
+ seccion: maestro.section?.text || maestro.section?.marker || "",
90123
+ // El cambio de tempo que arranca EN este compás. Los que caen a mitad van a su figura
90124
+ // (ver `temposAMitad` abajo).
90125
+ // ⛔ (2026-11-10) Antes era `tempoAutomations[0]` sin mirar dónde caía: un 140 a mitad
90126
+ // del compás 2 se aplicaba desde su principio y la primera mitad sonaba a 140.
90127
+ tempo: maestro.tempoAutomations?.find((a) => a.ratioPosition <= 1e-6)?.value ?? null,
90128
+ repiteDesdeAca: maestro.isRepeatStart,
90129
+ repiteVeces: maestro.repeatCount,
90130
+ // ⚠️ Novena pérdida silenciosa de la misma familia: 2 de los 15 `.gp` de Daniel
90131
+ // declaran una anacruza y el modelo la descartaba.
90132
+ esAnacruza: maestro.isAnacrusis,
90133
+ // ── Los signos de compás de Guitar Pro ────────────────────────────────
90134
+ casillas: maestro.alternateEndings,
90135
+ dobleBarra: maestro.isDoubleBar,
90136
+ tiempoLibre: maestro.isFreeTime,
90137
+ swing: maestro.tripletFeel,
90138
+ // ⚠️ `simileMark` es del compás DE LA PISTA en alphaTab (`Bar.simileMark`), no del
90139
+ // maestro. Se lee más abajo, al recorrer los compases de cada pentagrama, y se
90140
+ // escribe acá arriba porque en el modelo la repetición de compás es de la obra:
90141
+ // un «igual que el anterior» que valiera para la guitarra y no para el bajo
90142
+ // dejaría los dos instrumentos diciendo cosas distintas sobre el mismo compás.
90143
+ repiteAnterior: 0,
90144
+ // ⚠️ `directions` es un `Set` o `null`, no una lista. Un `?? []` sobre un Set daría
90145
+ // el Set, y `.map` no existe ahí: hay que expandirlo.
90146
+ //
90147
+ // ⚠️⚠️ Y se guarda el NOMBRE, no el número. `String(d)` daba `"1"` —el valor del enum
90148
+ // convertido a texto— y la proyección buscaba `Direction["1"]`, que en un enum con
90149
+ // mapeo inverso devuelve el *nombre* y no un número: la guarda lo descartaba y **las
90150
+ // direcciones se perdían enteras, en silencio**. Lo cazó la prueba de ida y vuelta
90151
+ // con signos, que es exactamente para lo que existe.
90152
+ //
90153
+ // ★ El nombre y no el número, además, porque acá es una LISTA y se lee: un
90154
+ // `["JumpDalSegnoAlCoda"]` en el documento dice algo; un `[10]` no.
90155
+ //
90156
+ // ⛔ (2026-11-11) De un MusicXML mandan las que se leyeron del archivo (`datos.saltos`):
90157
+ // alphaTab 1.8.4 confunde Segno Segno con Segno, Double Coda con Coda, y en un compás
90158
+ // vacío pierde todos los «al Coda» y «al Fine». Ver `LoQueSeLeeDelArchivo.saltos`.
90159
+ direcciones: datos.saltos?.get(maestro.index) ? [...datos.saltos.get(maestro.index)] : maestro.directions ? [...saltosEscritos.precisos.get(maestro.index) ?? maestro.directions].map(
90160
+ (d) => _barrel_exports$4.Direction[d] ?? String(d)
90161
+ ) : []
90162
+ }))
90163
+ );
90164
+ const saltos = /* @__PURE__ */ new Set();
90165
+ for (const pista of score.tracks) for (const i2 of pista.lineBreaks ?? []) saltos.add(i2);
90166
+ for (const i2 of saltos) {
90167
+ const id2 = idsDeCompasMaestro[i2];
90168
+ if (id2 !== void 0 && i2 > 0) documento.ponerSaltoDeRenglon(id2, true);
90169
+ }
90170
+ const letrasDobles = { cuantas: 0, versosDeMas: 0 };
90171
+ const tipoDelPentagrama = (p) => p.isPercussion ? "percusion" : p.tuning.length === 0 ? "altura" : "trastes";
89111
90172
  for (const pista of score.tracks) {
89112
90173
  if (pista.staves.length === 0) {
89113
90174
  avisos.push(`La pista "${pista.name}" no tiene pentagramas y se salte\xF3 entera.`);
@@ -89358,7 +90419,12 @@ function importarScore(score, opciones = {}, datos = {}) {
89358
90419
  );
89359
90420
  }
89360
90421
  const audioEmbebido = importarAudioYSync(score, documento, idsDeCompasMaestro, avisos);
89361
- return { documento, avisos, ...audioEmbebido ? { audioEmbebido } : {} };
90422
+ const manosJuntadas = juntarManosDelPiano(documento);
90423
+ if (manosJuntadas > 0)
90424
+ avisos.push(
90425
+ manosJuntadas === 1 ? "El piano ven\xEDa con una pista por mano: se junt\xF3 en un solo instrumento, con su llave." : `${manosJuntadas} pianos ven\xEDan con una pista por mano: se juntaron, cada uno con su llave.`
90426
+ );
90427
+ return { documento, avisos, manosJuntadas, ...audioEmbebido ? { audioEmbebido } : {} };
89362
90428
  }
89363
90429
  function importarAudioYSync(score, documento, idsDeCompasMaestro, avisos) {
89364
90430
  const puntos = score.exportFlatSyncPoints();
@@ -89475,13 +90541,15 @@ function ortografiaDe(ortografia, parte, pentagrama, compas, tic, nota) {
89475
90541
  const base = `${parte}|${pentagrama}|${compas}|${tic}|`;
89476
90542
  return ortografia.get(base + nota.calculateRealValue(false, false)) ?? ortografia.get(base + (nota.octave * 12 + nota.tone));
89477
90543
  }
89478
- var SALIDA_DE_ID_GP, PARIENTE_GM, DIR, SALTO_POR_TEXTO, claveDeSalto;
90544
+ var SALIDA_DE_ID_GP, PARIENTE_GM, DIR, SALTO_POR_TEXTO;
89479
90545
  var init_importarGp = __esm({
89480
90546
  "packages/io/src/importarGp.ts"() {
89481
90547
  init_alphaTab();
89482
90548
  init_src2();
89483
90549
  init_src2();
90550
+ init_manosDelPiano();
89484
90551
  init_musicxmlEntrada();
90552
+ init_musicxml();
89485
90553
  SALIDA_DE_ID_GP = {
89486
90554
  31: 40,
89487
90555
  83: 53,
@@ -89546,6 +90614,7 @@ var init_importarGp = __esm({
89546
90614
  dsalfine: [DIR.JumpDalSegno, DIR.JumpDalSegnoAlFine],
89547
90615
  dssalcoda: [DIR.JumpDalSegno, DIR.JumpDalSegnoSegnoAlCoda],
89548
90616
  dssalfine: [DIR.JumpDalSegno, DIR.JumpDalSegnoSegnoAlFine],
90617
+ dssalcoda2: [DIR.JumpDalSegno, DIR.JumpDalSegnoSegnoAlDoubleCoda],
89549
90618
  dss: [DIR.JumpDalSegno, DIR.JumpDalSegnoSegno],
89550
90619
  dcalcoda: [DIR.JumpDaCapo, DIR.JumpDaCapoAlCoda],
89551
90620
  dcalcoda2: [DIR.JumpDaCapo, DIR.JumpDaCapoAlDoubleCoda],
@@ -89556,7 +90625,6 @@ var init_importarGp = __esm({
89556
90625
  tocoda2: [DIR.JumpDaCoda, DIR.JumpDaDoubleCoda],
89557
90626
  fine: [DIR.TargetFine, DIR.TargetFine]
89558
90627
  };
89559
- claveDeSalto = (texto4) => texto4.toLowerCase().replace(/dals*segno/g, "ds").replace(/das*capo/g, "dc").replace(/[^a-z0-9]/g, "");
89560
90628
  }
89561
90629
  });
89562
90630
 
@@ -90054,7 +91122,7 @@ function claseDe(g) {
90054
91122
  if (g.esBateria) return "bateria";
90055
91123
  if (g.programa >= 24 && g.programa <= 31) return "guitarra";
90056
91124
  if (g.programa >= 32 && g.programa <= 39) return "bajo";
90057
- if (g.programa <= 7) return "piano";
91125
+ if (g.programa <= 7 || g.programa >= 16 && g.programa <= 20) return "piano";
90058
91126
  return "altura";
90059
91127
  }
90060
91128
  function digitar(alturas, afinacion, ocupadas) {
@@ -90166,9 +91234,32 @@ function figurasQueLlenan(ini, fin, ventana) {
90166
91234
  }
90167
91235
  return salida;
90168
91236
  }
91237
+ function juntarPistasDeManos(grupos, avisos) {
91238
+ const salida = [];
91239
+ for (let i2 = 0; i2 < grupos.length; i2++) {
91240
+ const a = grupos[i2];
91241
+ const b = grupos[i2 + 1];
91242
+ const base = b && claseDe(a) === "piano" && claseDe(b) === "piano" ? baseDeLasManos(a.nombre, b.nombre) : null;
91243
+ if (!b || base === null) {
91244
+ salida.push(a);
91245
+ continue;
91246
+ }
91247
+ salida.push({
91248
+ ...a,
91249
+ nombre: base,
91250
+ canales: [.../* @__PURE__ */ new Set([...a.canales, ...b.canales])],
91251
+ notas: [...a.notas.map((n) => ({ ...n, mano: 0 })), ...b.notas.map((n) => ({ ...n, mano: 1 }))].sort(
91252
+ (x2, y) => x2.ini - y.ini || x2.altura - y.altura
91253
+ )
91254
+ });
91255
+ avisos.decir(`\xAB${a.nombre}\xBB y \xAB${b.nombre}\xBB son las dos manos de un piano: se abrieron como un solo instrumento.`);
91256
+ i2++;
91257
+ }
91258
+ return salida;
91259
+ }
90169
91260
  function armarScore(leido, opciones, avisos) {
90170
91261
  const rejilla = rejillaDe(opciones.cuantizacion);
90171
- const grupos = leido.grupos.filter((g) => g.notas.length > 0);
91262
+ const grupos = juntarPistasDeManos(leido.grupos.filter((g) => g.notas.length > 0), avisos);
90172
91263
  if (grupos.length === 0) {
90173
91264
  avisos.decir("El archivo no trae ninguna nota: se abri\xF3 una obra vac\xEDa con un piano.");
90174
91265
  }
@@ -90199,13 +91290,13 @@ function armarScore(leido, opciones, avisos) {
90199
91290
  if (fin <= ini) fin = ini + pasoEn(ventanas, ini);
90200
91291
  if (Math.abs(ini - cruda.ini) > 2) redondeadas++;
90201
91292
  else if (Math.abs(fin - cruda.fin) > 2 && clase !== "bateria") avisos.contar("duracion-ajustada");
90202
- notas.push({ ini, fin: Math.min(fin, finDeLaObra2), altura: cruda.altura, cuerda: 0 });
91293
+ notas.push({ ini, fin: Math.min(fin, finDeLaObra2), altura: cruda.altura, cuerda: 0, ...cruda.mano !== void 0 ? { mano: cruda.mano } : {} });
90203
91294
  }
90204
91295
  notas.sort((a, b) => a.altura - b.altura || a.ini - b.ini || b.fin - a.fin);
90205
91296
  const limpias = [];
90206
91297
  for (const n of notas) {
90207
91298
  const anterior = limpias[limpias.length - 1];
90208
- if (anterior && anterior.altura === n.altura) {
91299
+ if (anterior && anterior.altura === n.altura && anterior.mano === n.mano) {
90209
91300
  if (anterior.ini === n.ini) {
90210
91301
  avisos.contar("repetidas");
90211
91302
  continue;
@@ -90248,8 +91339,16 @@ function armarScore(leido, opciones, avisos) {
90248
91339
  const voces = repartirEnVoces(enRegistro, ventanas, avisos, (alturas, ocupadas) => digitar(alturas, afinacion, ocupadas));
90249
91340
  pista.pentagramas.push({ voces, clave: clase === "guitarra" ? _barrel_exports$4.Clef.G2 : _barrel_exports$4.Clef.F4 });
90250
91341
  } else if (clase === "piano") {
90251
- const derecha = notas.filter((n) => n.altura >= 60);
90252
- const izquierda = notas.filter((n) => n.altura < 60);
91342
+ let derecha;
91343
+ let izquierda;
91344
+ if (notas.some((n) => n.mano !== void 0)) {
91345
+ derecha = notas.filter((n) => n.mano !== 1);
91346
+ izquierda = notas.filter((n) => n.mano === 1);
91347
+ } else {
91348
+ const manos = repartirManos(notas);
91349
+ derecha = manos.derecha.map((i2) => notas[i2]).sort((a, b) => a.ini - b.ini || a.altura - b.altura);
91350
+ izquierda = manos.izquierda.map((i2) => notas[i2]).sort((a, b) => a.ini - b.ini || a.altura - b.altura);
91351
+ }
90253
91352
  pista.pentagramas.push({ voces: repartirEnVoces(derecha, ventanas, avisos, null), clave: _barrel_exports$4.Clef.G2 });
90254
91353
  pista.pentagramas.push({ voces: repartirEnVoces(izquierda, ventanas, avisos, null), clave: _barrel_exports$4.Clef.F4 });
90255
91354
  } else {
@@ -90515,6 +91614,7 @@ var init_importarMidi = __esm({
90515
91614
  "packages/io/src/importarMidi.ts"() {
90516
91615
  init_alphaTab();
90517
91616
  init_src2();
91617
+ init_manosDelPiano();
90518
91618
  init_importarGp();
90519
91619
  MidiIlegible = class extends Error {
90520
91620
  constructor(mensaje) {
@@ -90805,818 +91905,298 @@ function desempaquetar(bytes) {
90805
91905
  }
90806
91906
  if (!Number.isFinite(manifiesto.formato) || manifiesto.formato > VERSION_DEL_FORMATO) {
90807
91907
  throw new ArchivoIlegible(
90808
- `Este archivo se guard\xF3 con una versi\xF3n m\xE1s nueva de Mandioca Tabs (formato ${manifiesto.formato}; esta versi\xF3n entiende hasta el ${VERSION_DEL_FORMATO}). Abrirlo igual podr\xEDa borrarte lo que esa versi\xF3n guard\xF3.`
90809
- );
90810
- }
90811
- const doc = entradas[DOCUMENTO];
90812
- if (!doc) throw new ArchivoIlegible("El archivo no trae el documento.");
90813
- let documento;
90814
- try {
90815
- documento = abrirDocumento(doc);
90816
- } catch {
90817
- throw new ArchivoIlegible("El documento que trae el archivo est\xE1 da\xF1ado.");
90818
- }
90819
- const grabaciones = [];
90820
- const fuentes = [];
90821
- for (const [nombre, contenido] of Object.entries(entradas)) {
90822
- if (nombre.startsWith(CARPETA_DE_AUDIO)) {
90823
- const fuenteId = nombre.slice(CARPETA_DE_AUDIO.length);
90824
- if (fuenteId) grabaciones.push({ fuenteId, bytes: contenido });
90825
- continue;
90826
- }
90827
- if (nombre.startsWith(CARPETA_DE_FUENTES)) {
90828
- const tipografiaId = nombre.slice(CARPETA_DE_FUENTES.length);
90829
- if (tipografiaId) fuentes.push({ tipografiaId, bytes: contenido });
90830
- }
90831
- }
90832
- return { documento, manifiesto, grabaciones, fuentes };
90833
- }
90834
- var VERSION_DEL_FORMATO, MANIFIESTO, DOCUMENTO, CARPETA_DE_AUDIO, CARPETA_DE_FUENTES, ArchivoIlegible;
90835
- var init_archivoMtabs = __esm({
90836
- "packages/io/src/archivoMtabs.ts"() {
90837
- init_esm();
90838
- init_src2();
90839
- VERSION_DEL_FORMATO = 6;
90840
- MANIFIESTO = "mandioca.json";
90841
- DOCUMENTO = "documento.ydoc";
90842
- CARPETA_DE_AUDIO = "audio/";
90843
- CARPETA_DE_FUENTES = "fuentes/";
90844
- ArchivoIlegible = class extends Error {
90845
- constructor(mensaje) {
90846
- super(mensaje);
90847
- this.name = "ArchivoIlegible";
90848
- }
90849
- };
90850
- }
90851
- });
90852
-
90853
- // packages/io/src/recorridoDeRepeticiones.ts
90854
- function instalarRecorridoDeRepeticiones() {
90855
- const version2 = String(VersionInfo?.VersionInfo?.version ?? VersionInfo?.version ?? "");
90856
- const Generador = _barrel_exports$3.MidiFileGenerator;
90857
- if (!Generador || typeof Generador._playThroughSong !== "function") return false;
90858
- if (Generador.__recorridoDe19) return true;
90859
- if (version2 && !version2.startsWith("1.8.")) return false;
90860
- Generador._playThroughSong = function(score, syncPoints, createNewSyncPoints, generateMasterBar, generateTracks, finish) {
90861
- const controller = new RecorridoDeCompases(score);
90862
- const playContext = {
90863
- synthTick: 0,
90864
- synthTime: 0,
90865
- currentTempo: score.tempo,
90866
- automationToSyncPoint: /* @__PURE__ */ new Map(),
90867
- syncPoints,
90868
- createNewSyncPoints
90869
- };
90870
- let previousMasterBar = null;
90871
- const barOccurence = /* @__PURE__ */ new Map();
90872
- while (!controller.finished) {
90873
- const index = controller.index;
90874
- const bar = score.masterBars[index];
90875
- const currentTick = controller.currentTick;
90876
- controller.processCurrent();
90877
- if (controller.shouldPlay) {
90878
- let occurence = barOccurence.has(index) ? barOccurence.get(index) : -1;
90879
- occurence++;
90880
- barOccurence.set(index, occurence);
90881
- generateMasterBar(bar, previousMasterBar, currentTick, playContext.currentTempo, occurence);
90882
- generateTracks(index, currentTick, bar.tempoAutomations.length > 0 ? bar.tempoAutomations[0].value : playContext.currentTempo);
90883
- playContext.synthTick = currentTick;
90884
- Generador._processBarTime(bar, occurence, playContext);
90885
- }
90886
- controller.moveNext();
90887
- previousMasterBar = bar;
90888
- }
90889
- if (syncPoints.length > 0) {
90890
- const lastSyncPoint = syncPoints[syncPoints.length - 1];
90891
- const remainingTicks = controller.currentTick - lastSyncPoint.synthTick;
90892
- if (remainingTicks > 0) {
90893
- const p = new _barrel_exports$7.BackingTrackSyncPoint();
90894
- p.masterBarIndex = previousMasterBar.index;
90895
- p.masterBarOccurence = barOccurence.get(previousMasterBar.index) - 1;
90896
- p.synthTick = controller.currentTick;
90897
- p.synthBpm = playContext.currentTempo;
90898
- if (playContext.createNewSyncPoints) {
90899
- p.syncBpm = lastSyncPoint.synthBpm;
90900
- p.synthBpm = lastSyncPoint.synthBpm;
90901
- } else if (syncPoints.length === 1) p.syncBpm = lastSyncPoint.synthBpm;
90902
- else p.syncBpm = syncPoints[syncPoints.length - 2].syncBpm;
90903
- p.synthTime = lastSyncPoint.synthTime + ticksAMs(remainingTicks, lastSyncPoint.synthBpm);
90904
- p.syncTime = lastSyncPoint.syncTime + ticksAMs(remainingTicks, p.syncBpm);
90905
- if (!playContext.createNewSyncPoints) lastSyncPoint.updateSyncBpm(p.synthTime, p.syncTime);
90906
- syncPoints.push(p);
90907
- }
90908
- }
90909
- finish(controller.currentTick);
90910
- return playContext;
90911
- };
90912
- Generador.__recorridoDe19 = true;
90913
- return true;
90914
- }
90915
- var D, Repeticion, RecorridoDeCompases, ticksAMs;
90916
- var init_recorridoDeRepeticiones = __esm({
90917
- "packages/io/src/recorridoDeRepeticiones.ts"() {
90918
- init_alphaTab();
90919
- D = {
90920
- TargetFine: 0,
90921
- TargetSegno: 1,
90922
- TargetSegnoSegno: 2,
90923
- TargetCoda: 3,
90924
- TargetDoubleCoda: 4,
90925
- JumpDaCoda: 17,
90926
- JumpDaDoubleCoda: 18
90927
- };
90928
- Repeticion = class {
90929
- group;
90930
- /** Con casillas, cada cierre termina la pasada y todos comparten el contador. */
90931
- conCasillas;
90932
- /** La pasada actual (desde 0): elige qué casillas suenan. */
90933
- pasada = 0;
90934
- /** Las repeticiones hechas por cierre (sólo sin casillas). */
90935
- iteraciones;
90936
- /** Las casillas del último compás que las tenía: los que no tienen, las continúan. */
90937
- casillasActuales = 0;
90938
- /** @param pasadaFinal si se toca como última pasada (después de un salto): sólo la última casilla. */
90939
- constructor(group, pasadaFinal) {
90940
- this.group = group;
90941
- this.conCasillas = group.masterBars.some((m) => m.alternateEndings !== 0);
90942
- this.iteraciones = group.closings.map((c) => pasadaFinal ? Math.max(0, c.repeatCount - 1) : 0);
90943
- if (pasadaFinal) for (const i2 of this.iteraciones) this.pasada = Math.max(this.pasada, i2);
90944
- }
90945
- suena(compas) {
90946
- let casillas = compas.alternateEndings;
90947
- if (casillas === 0) casillas = this.casillasActuales;
90948
- else this.casillasActuales = casillas;
90949
- return casillas === 0 || (casillas & 1 << this.pasada) !== 0;
90950
- }
90951
- /** 0 = seguir, 1 = volver al comienzo de la repetición, 2 = terminó. */
90952
- alCerrar(cierre, sono) {
90953
- const cierres = this.group.closings;
90954
- const esElUltimo = cierre === cierres[cierres.length - 1];
90955
- if (this.conCasillas) {
90956
- if (!sono && !esElUltimo) return 0;
90957
- if (this.pasada < cierre.repeatCount - 1) {
90958
- this.pasada++;
90959
- this.casillasActuales = 0;
90960
- return 1;
90961
- }
90962
- return esElUltimo ? 2 : 0;
90963
- }
90964
- const i2 = cierres.indexOf(cierre);
90965
- if (i2 === -1) return 0;
90966
- if (this.iteraciones[i2] < cierre.repeatCount - 1) {
90967
- this.iteraciones[i2]++;
90968
- for (let j = 0; j < i2; j++) this.iteraciones[j] = 0;
90969
- this.casillasActuales = 0;
90970
- return 1;
90971
- }
90972
- return esElUltimo ? 2 : 0;
90973
- }
90974
- /** Si algún cierre todavía va a volver por encima de este compás. */
90975
- vaAVolver(compas) {
90976
- const cierres = this.group.closings;
90977
- for (let i2 = 0; i2 < cierres.length; i2++) {
90978
- const cierre = cierres[i2];
90979
- if (cierre.index >= compas.index) {
90980
- if ((this.conCasillas ? this.pasada : this.iteraciones[i2]) < cierre.repeatCount - 1) return true;
90981
- }
90982
- }
90983
- return false;
90984
- }
90985
- };
90986
- RecorridoDeCompases = class _RecorridoDeCompases {
90987
- /** Los saltos en el orden en que se revisan si un compás tiene varios. */
90988
- static SALTOS = [
90989
- { jump: 5, target: null, playUntil: null, continueAt: null },
90990
- { jump: 6, target: null, playUntil: D.JumpDaCoda, continueAt: D.TargetCoda },
90991
- { jump: 7, target: null, playUntil: D.JumpDaDoubleCoda, continueAt: D.TargetDoubleCoda },
90992
- { jump: 8, target: null, playUntil: D.TargetFine, continueAt: null },
90993
- { jump: 9, target: D.TargetSegno, playUntil: null, continueAt: null },
90994
- { jump: 10, target: D.TargetSegno, playUntil: D.JumpDaCoda, continueAt: D.TargetCoda },
90995
- { jump: 11, target: D.TargetSegno, playUntil: D.JumpDaDoubleCoda, continueAt: D.TargetDoubleCoda },
90996
- { jump: 12, target: D.TargetSegno, playUntil: D.TargetFine, continueAt: null },
90997
- { jump: 13, target: D.TargetSegnoSegno, playUntil: null, continueAt: null },
90998
- { jump: 14, target: D.TargetSegnoSegno, playUntil: D.JumpDaCoda, continueAt: D.TargetCoda },
90999
- { jump: 15, target: D.TargetSegnoSegno, playUntil: D.JumpDaDoubleCoda, continueAt: D.TargetDoubleCoda },
91000
- { jump: 16, target: D.TargetSegnoSegno, playUntil: D.TargetFine, continueAt: null }
91001
- ];
91002
- score;
91003
- pila = [];
91004
- /** El D.C./D.S. que se siguió; null si se toca normal (antes del salto o después de la coda). */
91005
- saltoActivo = null;
91006
- /** Los compases cuyo salto ya se tomó: cada salto se toma una sola vez. */
91007
- saltosTomados = /* @__PURE__ */ new Set();
91008
- shouldPlay = true;
91009
- index = 0;
91010
- currentTick = 0;
91011
- constructor(score) {
91012
- this.score = score;
91013
- }
91014
- get finished() {
91015
- return this.index >= this.score.masterBars.length;
91016
- }
91017
- processCurrent() {
91018
- const compas = this.score.masterBars[this.index];
91019
- this.entrarARepeticion(compas);
91020
- const rep = this.pila.length > 0 ? this.pila[this.pila.length - 1] : null;
91021
- this.shouldPlay = rep === null || rep.suena(compas);
91022
- if (this.shouldPlay) this.currentTick += compas.calculateDuration();
91023
- }
91024
- moveNext() {
91025
- if (this.moverConSaltos()) return;
91026
- const compas = this.score.masterBars[this.index];
91027
- if (this.pila.length > 0 && compas.repeatCount > 1) {
91028
- const rep = this.pila[this.pila.length - 1];
91029
- switch (rep.alCerrar(compas, this.shouldPlay)) {
91030
- case 1:
91031
- this.index = rep.group.opening.index;
91032
- return;
91033
- case 2:
91034
- this.pila.pop();
91035
- }
91036
- }
91037
- this.index++;
91038
- }
91039
- /** Empieza la repetición del compás si hace falta: al llegar a su apertura, o tras un salto que cayó adentro. */
91040
- entrarARepeticion(compas) {
91041
- const group = compas.repeatGroup;
91042
- if (!group.isClosed || this.pila.some((r) => r.group === group)) return;
91043
- const adentro = compas.index <= group.closings[group.closings.length - 1].index;
91044
- if (compas === group.opening || this.saltoActivo !== null && adentro) {
91045
- this.pila.push(new Repeticion(group, this.saltoActivo !== null));
91046
- }
91047
- }
91048
- moverConSaltos() {
91049
- const compas = this.score.masterBars[this.index];
91050
- const dir = compas.directions;
91051
- if (!this.shouldPlay || dir === null || dir === void 0 || dir.size === 0) return false;
91052
- if (this.saltoActivo === null) return this.tomarSalto(compas, dir);
91053
- const hasta = this.saltoActivo.playUntil;
91054
- if (hasta !== null && dir.has(hasta)) return this.seguirTrasElSalto(this.saltoActivo.continueAt);
91055
- return false;
91056
- }
91057
- tomarSalto(compas, dir) {
91058
- if (this.saltosTomados.has(compas) || this.pila.some((r) => r.vaAVolver(compas))) return false;
91059
- for (const salto of _RecorridoDeCompases.SALTOS) {
91060
- if (!dir.has(salto.jump)) continue;
91061
- const destino = salto.target === null ? 0 : this.buscar(salto.target, true);
91062
- if (destino === -1) continue;
91063
- this.saltosTomados.add(compas);
91064
- this.saltoActivo = salto;
91065
- this.pila = [];
91066
- this.index = destino;
91067
- return true;
91068
- }
91069
- return false;
91070
- }
91071
- seguirTrasElSalto(seguirEn) {
91072
- if (seguirEn === null) {
91073
- this.index = this.score.masterBars.length;
91074
- return true;
91075
- }
91076
- const coda = this.buscar(seguirEn, false);
91077
- if (coda === -1) return false;
91078
- this.saltoActivo = null;
91079
- this.pila = [];
91080
- this.index = coda;
91081
- return true;
91082
- }
91083
- buscar(direccion, primeroAtras) {
91084
- const paso = primeroAtras ? -1 : 1;
91085
- const i2 = this.buscarHacia(direccion, paso);
91086
- return i2 !== -1 ? i2 : this.buscarHacia(direccion, -paso);
91087
- }
91088
- buscarHacia(direccion, paso) {
91089
- const compases = this.score.masterBars;
91090
- for (let i2 = this.index; i2 >= 0 && i2 < compases.length; i2 += paso) {
91091
- const d = compases[i2].directions;
91092
- if (d !== null && d !== void 0 && d.has(direccion)) return i2;
91093
- }
91094
- return -1;
91095
- }
91096
- };
91097
- ticksAMs = (ticks, tempo) => ticks * (6e4 / (tempo * 960)) | 0;
91098
- }
91099
- });
91100
-
91101
- // packages/io/src/musicxml.ts
91102
- function escapar(texto4) {
91103
- return texto4.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
91104
- }
91105
- function elemento(nombre, texto4) {
91106
- if (texto4 === null || texto4 === void 0 || texto4 === "") return [];
91107
- return [`<${nombre}>${escapar(String(texto4))}</${nombre}>`];
91108
- }
91109
- function alturaMusicXml(midi, fifths, modo = 0) {
91110
- const octavaAlphaTab = Math.floor(midi / 12);
91111
- const clase = midi - octavaAlphaTab * 12;
91112
- const octava = octavaAlphaTab - 1;
91113
- const pedida = ALTERACION_POR_MODO[modo];
91114
- if (pedida !== void 0) {
91115
- const sinAlterar = clase - pedida;
91116
- const vueltas = Math.floor(sinAlterar / 12);
91117
- const paso = PASOS.find((p) => p.semitonos === sinAlterar - vueltas * 12);
91118
- if (paso) return { paso: paso.paso, alteracion: pedida, octava: octava + vueltas };
91119
- }
91120
- const natural = PASOS.find((p) => p.semitonos === clase);
91121
- if (natural) return { paso: natural.paso, alteracion: 0, octava };
91122
- if (fifths < 0) {
91123
- const arriba = PASOS.find((p) => p.semitonos === clase + 1);
91124
- if (arriba) return { paso: arriba.paso, alteracion: -1, octava };
91125
- }
91126
- const abajo = PASOS.find((p) => p.semitonos === clase - 1);
91127
- if (abajo) return { paso: abajo.paso, alteracion: 1, octava };
91128
- throw new Error(`altura MIDI no representable: ${midi}`);
91129
- }
91130
- function claveDelPentagrama(staff, bar) {
91131
- if (staff.isPercussion) return { signo: "percussion", linea: 2, lineas: 5, conAfinacion: false };
91132
- if (staff.showTablature && staff.tuning.length > 0)
91133
- return { signo: "TAB", linea: 5, lineas: staff.tuning.length, conAfinacion: true };
91134
- switch (bar.clef) {
91135
- case _barrel_exports$4.Clef.C3:
91136
- return { signo: "C", linea: 3, lineas: 5, conAfinacion: false };
91137
- case _barrel_exports$4.Clef.C4:
91138
- return { signo: "C", linea: 4, lineas: 5, conAfinacion: false };
91139
- case _barrel_exports$4.Clef.F4:
91140
- return { signo: "F", linea: 4, lineas: 5, conAfinacion: false };
91141
- case _barrel_exports$4.Clef.Neutral:
91142
- return { signo: "percussion", linea: 2, lineas: 5, conAfinacion: false };
91143
- default:
91144
- return { signo: "G", linea: 2, lineas: 5, conAfinacion: false };
91145
- }
91146
- }
91147
- function gruposDeTresillo(beats) {
91148
- const abren = /* @__PURE__ */ new Set();
91149
- const cierran = /* @__PURE__ */ new Set();
91150
- let i2 = 0;
91151
- while (i2 < beats.length) {
91152
- const b = beats[i2];
91153
- if (b.tupletNumerator <= 0 || b.tupletDenominator <= 0) {
91154
- i2++;
91155
- continue;
91156
- }
91157
- let j = i2;
91158
- while (j + 1 < beats.length && beats[j + 1].tupletNumerator === b.tupletNumerator && beats[j + 1].tupletDenominator === b.tupletDenominator && (b.tupletGroup === null || beats[j + 1].tupletGroup === b.tupletGroup))
91159
- j++;
91160
- abren.add(i2);
91161
- cierran.add(j);
91162
- i2 = j + 1;
91163
- }
91164
- return { abren, cierran };
91165
- }
91166
- function saltosDelCompas(master) {
91167
- const s = [];
91168
- for (const [direccion, salto] of SALTOS) {
91169
- if (!master?.directions?.has(direccion)) continue;
91170
- s.push('<direction placement="above">', "<direction-type>");
91171
- s.push(salto.simbolo ? `<${salto.simbolo}/>` : `<words>${escapar(salto.texto ?? "")}</words>`);
91172
- s.push("</direction-type>", `<sound ${salto.sonido}/>`, "</direction>");
91173
- }
91174
- return s;
91175
- }
91176
- function nombreDeFigura(duracion) {
91177
- return NOMBRE_DE_LA_FIGURA2.get(duracion) ?? "quarter";
91178
- }
91179
- function instrumentosDePercusion(track) {
91180
- const usadas = /* @__PURE__ */ new Set();
91181
- for (const staff of track.staves)
91182
- for (const bar of staff.bars)
91183
- for (const voice of bar.voices)
91184
- for (const beat of voice.beats)
91185
- for (const note of beat.notes)
91186
- if (note.percussionArticulation >= 0) usadas.add(note.percussionArticulation);
91187
- const ids = /* @__PURE__ */ new Map();
91188
- for (const art of [...usadas].sort((a, b) => a - b))
91189
- ids.set(art, `P${track.index + 1}-I${art + 1}`);
91190
- return ids;
91191
- }
91192
- function posicionDePercusion(staffLine) {
91193
- const indiceDeB = 6;
91194
- const bruto = indiceDeB + (5 - staffLine);
91195
- const paso = PASOS[(bruto % 7 + 7) % 7].paso;
91196
- return { paso, octava: 4 + Math.floor(bruto / 7) };
91197
- }
91198
- function escribirNota(note, beat, staff, opciones) {
91199
- const s = ["<note>"];
91200
- const esAdorno = beat.graceType !== _barrel_exports$4.GraceType.None;
91201
- if (esAdorno) s.push(beat.graceType === _barrel_exports$4.GraceType.BeforeBeat ? "<grace/>" : '<grace slash="yes"/>');
91202
- if (opciones.esAcorde) s.push("<chord/>");
91203
- const esGolpeDeKit = staff.isPercussion && note.percussionArticulation >= 0;
91204
- if (esGolpeDeKit) {
91205
- const art = staff.track.percussionArticulations[note.percussionArticulation];
91206
- const { paso, octava } = posicionDePercusion(art?.staffLine ?? 5);
91207
- s.push("<unpitched>");
91208
- s.push(`<display-step>${paso}</display-step>`);
91209
- s.push(`<display-octave>${octava}</display-octave>`);
91210
- s.push("</unpitched>");
91211
- } else {
91212
- const { paso, alteracion, octava } = alturaMusicXml(
91213
- note.calculateRealValue(false, false),
91214
- opciones.fifths,
91215
- note.accidentalMode
91216
- );
91217
- s.push("<pitch>");
91218
- s.push(`<step>${paso}</step>`);
91219
- if (alteracion !== 0) s.push(`<alter>${alteracion}</alter>`);
91220
- s.push(`<octave>${octava}</octave>`);
91221
- s.push("</pitch>");
91222
- }
91223
- if (!esAdorno) s.push(`<duration>${opciones.duracion}</duration>`);
91224
- if (note.isTieDestination) s.push('<tie type="stop"/>');
91225
- if (note.isTieOrigin) s.push('<tie type="start"/>');
91226
- const idDeInstrumento = opciones.instrumentos.get(note.percussionArticulation);
91227
- if (esGolpeDeKit && idDeInstrumento) s.push(`<instrument id="${idDeInstrumento}"/>`);
91228
- s.push(`<voice>${opciones.numeroDeVoz}</voice>`);
91229
- s.push(...elemento("type", nombreDeFigura(beat.duration)));
91230
- for (let d = 0; d < beat.dots; d++) s.push("<dot/>");
91231
- if (beat.tupletNumerator > 0 && beat.tupletDenominator > 0) {
91232
- s.push("<time-modification>");
91233
- s.push(`<actual-notes>${beat.tupletNumerator}</actual-notes>`);
91234
- s.push(`<normal-notes>${beat.tupletDenominator}</normal-notes>`);
91235
- s.push("</time-modification>");
91236
- }
91237
- if (note.isDead) s.push("<notehead>x</notehead>");
91238
- else if (esGolpeDeKit) {
91239
- const art = staff.track.percussionArticulations[note.percussionArticulation];
91240
- if (art?.noteHeadDefault === _barrel_exports$4.MusicFontSymbol.NoteheadXBlack)
91241
- s.push("<notehead>x</notehead>");
91242
- } else if (note.isGhost) s.push('<notehead parentheses="yes">normal</notehead>');
91243
- if (opciones.numeroDePentagrama !== null) s.push(`<staff>${opciones.numeroDePentagrama}</staff>`);
91244
- const notaciones = [];
91245
- if (note.isTieDestination) notaciones.push('<tied type="stop"/>');
91246
- if (note.isTieOrigin) notaciones.push('<tied type="start"/>');
91247
- if (opciones.abreTresillo) notaciones.push('<tuplet type="start" number="1"/>');
91248
- if (opciones.cierraTresillo) notaciones.push('<tuplet type="stop" number="1"/>');
91249
- const tecnica = [];
91250
- if (note.string > 0 && staff.tuning.length > 0) {
91251
- tecnica.push(`<string>${staff.tuning.length - note.string + 1}</string>`);
91252
- tecnica.push(`<fret>${note.fret}</fret>`);
91253
- }
91254
- const ligado = (desde, hasta) => hasta >= desde ? "hammer-on" : "pull-off";
91255
- const origen = note.hammerPullOrigin;
91256
- if (origen !== null) {
91257
- const nombre = ligado(origen.fret, note.fret);
91258
- tecnica.push(`<${nombre} type="stop"/>`);
91259
- }
91260
- const destino = note.hammerPullDestination;
91261
- if (note.isHammerPullOrigin && destino) {
91262
- const nombre = ligado(note.fret, destino.fret);
91263
- tecnica.push(`<${nombre} type="start">${nombre === "hammer-on" ? "H" : "P"}</${nombre}>`);
91264
- }
91265
- if (note.harmonicType !== _barrel_exports$4.HarmonicType.None) tecnica.push("<harmonic/>");
91266
- if (beat.isPalmMute) tecnica.push("<other-technical>palm-mute</other-technical>");
91267
- if (beat.isLetRing) tecnica.push("<other-technical>let-ring</other-technical>");
91268
- if (tecnica.length > 0) notaciones.push("<technical>", ...tecnica, "</technical>");
91269
- const articulaciones = [];
91270
- if (note.accentuated === _barrel_exports$4.AccentuationType.Normal) articulaciones.push("<accent/>");
91271
- else if (note.accentuated === _barrel_exports$4.AccentuationType.Heavy)
91272
- articulaciones.push("<strong-accent/>");
91273
- else if (note.accentuated === _barrel_exports$4.AccentuationType.Tenuto)
91274
- articulaciones.push("<tenuto/>");
91275
- if (note.isStaccato) articulaciones.push("<staccato/>");
91276
- if (articulaciones.length > 0)
91277
- notaciones.push("<articulations>", ...articulaciones, "</articulations>");
91278
- if (notaciones.length > 0) s.push("<notations>", ...notaciones, "</notations>");
91279
- if (!opciones.esAcorde) s.push(...letraDelGolpe(beat));
91280
- s.push("</note>");
91281
- return s;
91282
- }
91283
- function letraDelGolpe(beat) {
91284
- if (!beat.lyrics) return [];
91285
- const s = [];
91286
- beat.lyrics.forEach((verso, i2) => {
91287
- if (!verso) return;
91288
- const sigue = verso.endsWith("-") && verso.length > 1;
91289
- let anterior = beat.previousBeat;
91290
- while (anterior && !anterior.lyrics?.[i2]) anterior = anterior.previousBeat;
91291
- const viene = !!anterior?.lyrics?.[i2]?.endsWith("-");
91292
- const silabico = sigue ? viene ? "middle" : "begin" : viene ? "end" : "single";
91293
- s.push(`<lyric number="${i2 + 1}">`);
91294
- s.push(`<syllabic>${silabico}</syllabic>`);
91295
- s.push(...elemento("text", sigue ? verso.slice(0, -1) : verso));
91296
- s.push("</lyric>");
91297
- });
91298
- return s;
91299
- }
91300
- function escribirSilencio2(beat, duracion, numeroDeVoz, numeroDePentagrama) {
91301
- const s = ["<note>", "<rest/>", `<duration>${duracion}</duration>`];
91302
- s.push(`<voice>${numeroDeVoz}</voice>`);
91303
- s.push(...elemento("type", nombreDeFigura(beat.duration)));
91304
- for (let d = 0; d < beat.dots; d++) s.push("<dot/>");
91305
- if (beat.tupletNumerator > 0 && beat.tupletDenominator > 0) {
91306
- s.push("<time-modification>");
91307
- s.push(`<actual-notes>${beat.tupletNumerator}</actual-notes>`);
91308
- s.push(`<normal-notes>${beat.tupletDenominator}</normal-notes>`);
91309
- s.push("</time-modification>");
91310
- }
91311
- if (numeroDePentagrama !== null) s.push(`<staff>${numeroDePentagrama}</staff>`);
91312
- s.push(...letraDelGolpe(beat));
91313
- s.push("</note>");
91314
- return s;
91315
- }
91316
- function exportarMusicXml(score) {
91317
- const l = [];
91318
- l.push('<?xml version="1.0" encoding="UTF-8"?>');
91319
- l.push('<!DOCTYPE score-partwise PUBLIC "-//Recordare//DTD MusicXML 4.0 Partwise//EN"');
91320
- l.push(' "http://www.musicxml.org/dtds/partwise.dtd">');
91321
- l.push('<score-partwise version="4.0">');
91322
- if (score.title) l.push("<work>", ...elemento("work-title", score.title), "</work>");
91323
- l.push(...elemento("movement-title", score.subTitle));
91324
- l.push("<identification>");
91325
- if (score.music) l.push(`<creator type="composer">${escapar(score.music)}</creator>`);
91326
- else if (score.artist) l.push(`<creator type="composer">${escapar(score.artist)}</creator>`);
91327
- if (score.words) l.push(`<creator type="lyricist">${escapar(score.words)}</creator>`);
91328
- l.push(...elemento("rights", score.copyright));
91329
- l.push("<encoding>", "<software>Mandioca Tabs</software>", "</encoding>");
91330
- l.push("</identification>");
91331
- const instrumentosPorPista = /* @__PURE__ */ new Map();
91332
- l.push("<part-list>");
91333
- for (const track of score.tracks) {
91334
- const id2 = `P${track.index + 1}`;
91335
- l.push(`<score-part id="${id2}">`);
91336
- l.push(...elemento("part-name", track.name || `Pista ${track.index + 1}`));
91337
- l.push(...elemento("part-abbreviation", track.shortName));
91338
- const esPercusion = track.staves.some((st) => st.isPercussion);
91339
- const instrumentos = esPercusion ? instrumentosDePercusion(track) : /* @__PURE__ */ new Map();
91340
- instrumentosPorPista.set(track.index, instrumentos);
91341
- if (esPercusion) {
91342
- for (const [art, idInstrumento] of instrumentos) {
91343
- const a = track.percussionArticulations[art];
91344
- l.push(`<score-instrument id="${idInstrumento}">`);
91345
- l.push(...elemento("instrument-name", `Percusi\xF3n ${art + 1}`));
91346
- l.push("</score-instrument>");
91347
- l.push(`<midi-instrument id="${idInstrumento}">`);
91348
- l.push("<midi-channel>10</midi-channel>");
91349
- l.push(`<midi-unpitched>${(a?.outputMidiNumber ?? 36) + 1}</midi-unpitched>`);
91350
- l.push("</midi-instrument>");
91351
- }
91352
- } else {
91353
- l.push(`<score-instrument id="${id2}-I1">`);
91354
- l.push(...elemento("instrument-name", track.name || `Pista ${track.index + 1}`));
91355
- l.push("</score-instrument>");
91356
- l.push(`<midi-instrument id="${id2}-I1">`);
91357
- l.push(`<midi-channel>${track.playbackInfo.primaryChannel + 1}</midi-channel>`);
91358
- l.push(`<midi-program>${track.playbackInfo.program + 1}</midi-program>`);
91359
- l.push(`<volume>${Math.round(track.playbackInfo.volume / 16 * 100)}</volume>`);
91360
- l.push(`<pan>${Math.round((track.playbackInfo.balance - 8) / 8 * 90)}</pan>`);
91361
- l.push("</midi-instrument>");
91362
- }
91363
- l.push("</score-part>");
91908
+ `Este archivo se guard\xF3 con una versi\xF3n m\xE1s nueva de Mandioca Tabs (formato ${manifiesto.formato}; esta versi\xF3n entiende hasta el ${VERSION_DEL_FORMATO}). Abrirlo igual podr\xEDa borrarte lo que esa versi\xF3n guard\xF3.`
91909
+ );
91364
91910
  }
91365
- l.push("</part-list>");
91366
- for (const track of score.tracks) {
91367
- const instrumentos = instrumentosPorPista.get(track.index) ?? /* @__PURE__ */ new Map();
91368
- l.push(`<part id="P${track.index + 1}">`);
91369
- const pentagramas = track.staves;
91370
- if (pentagramas.length === 0) {
91371
- l.push("</part>");
91911
+ const doc = entradas[DOCUMENTO];
91912
+ if (!doc) throw new ArchivoIlegible("El archivo no trae el documento.");
91913
+ let documento;
91914
+ try {
91915
+ documento = abrirDocumento(doc);
91916
+ } catch {
91917
+ throw new ArchivoIlegible("El documento que trae el archivo est\xE1 da\xF1ado.");
91918
+ }
91919
+ const grabaciones = [];
91920
+ const fuentes = [];
91921
+ for (const [nombre, contenido] of Object.entries(entradas)) {
91922
+ if (nombre.startsWith(CARPETA_DE_AUDIO)) {
91923
+ const fuenteId = nombre.slice(CARPETA_DE_AUDIO.length);
91924
+ if (fuenteId) grabaciones.push({ fuenteId, bytes: contenido });
91372
91925
  continue;
91373
91926
  }
91374
- let armaduraAnterior = null;
91375
- let compasAnterior = null;
91376
- let clavesAnteriores = null;
91377
- const cantidadDeCompases = Math.max(...pentagramas.map((st) => st.bars.length));
91378
- for (let indiceDeCompas2 = 0; indiceDeCompas2 < cantidadDeCompases; indiceDeCompas2++) {
91379
- const master = score.masterBars[indiceDeCompas2];
91380
- l.push(`<measure number="${indiceDeCompas2 + 1}">`);
91381
- const fifths = (pentagramas[0]?.bars[indiceDeCompas2] ?? pentagramas[0]?.bars[0])?.keySignature ?? master?.keySignature ?? 0;
91382
- const modo = master?.keySignatureType === _barrel_exports$4.KeySignatureType.Minor ? "minor" : "major";
91383
- const armadura = `${fifths}/${modo}`;
91384
- const numerador = master?.timeSignatureNumerator ?? 4;
91385
- const denominador = master?.timeSignatureDenominator ?? 4;
91386
- const compas = `${numerador}/${denominador}`;
91387
- const claves = pentagramas.map((st, k) => {
91388
- const bar = st.bars[indiceDeCompas2] ?? st.bars[0];
91389
- return { pentagrama: st, numero: k + 1, ...claveDelPentagrama(st, bar) };
91390
- });
91391
- const firmaDeClaves = claves.map((c) => `${c.signo}${c.linea}`).join(",");
91392
- const cambios = [];
91393
- if (indiceDeCompas2 === 0) cambios.push(`<divisions>${DIVISIONES_POR_NEGRA}</divisions>`);
91394
- if (armadura !== armaduraAnterior) {
91395
- cambios.push("<key>");
91396
- cambios.push(`<fifths>${fifths}</fifths>`);
91397
- cambios.push(`<mode>${modo}</mode>`);
91398
- cambios.push("</key>");
91399
- armaduraAnterior = armadura;
91927
+ if (nombre.startsWith(CARPETA_DE_FUENTES)) {
91928
+ const tipografiaId = nombre.slice(CARPETA_DE_FUENTES.length);
91929
+ if (tipografiaId) fuentes.push({ tipografiaId, bytes: contenido });
91930
+ }
91931
+ }
91932
+ const manosJuntadas = juntarManosDelPiano(documento);
91933
+ return { documento, manifiesto, grabaciones, fuentes, manosJuntadas };
91934
+ }
91935
+ var VERSION_DEL_FORMATO, MANIFIESTO, DOCUMENTO, CARPETA_DE_AUDIO, CARPETA_DE_FUENTES, ArchivoIlegible;
91936
+ var init_archivoMtabs = __esm({
91937
+ "packages/io/src/archivoMtabs.ts"() {
91938
+ init_esm();
91939
+ init_src2();
91940
+ init_manosDelPiano();
91941
+ VERSION_DEL_FORMATO = 6;
91942
+ MANIFIESTO = "mandioca.json";
91943
+ DOCUMENTO = "documento.ydoc";
91944
+ CARPETA_DE_AUDIO = "audio/";
91945
+ CARPETA_DE_FUENTES = "fuentes/";
91946
+ ArchivoIlegible = class extends Error {
91947
+ constructor(mensaje) {
91948
+ super(mensaje);
91949
+ this.name = "ArchivoIlegible";
91400
91950
  }
91401
- if (compas !== compasAnterior) {
91402
- cambios.push("<time>");
91403
- cambios.push(`<beats>${numerador}</beats>`);
91404
- cambios.push(`<beat-type>${denominador}</beat-type>`);
91405
- cambios.push("</time>");
91406
- compasAnterior = compas;
91951
+ };
91952
+ }
91953
+ });
91954
+
91955
+ // packages/io/src/recorridoDeRepeticiones.ts
91956
+ function instalarRecorridoDeRepeticiones() {
91957
+ const version2 = String(VersionInfo?.VersionInfo?.version ?? VersionInfo?.version ?? "");
91958
+ const Generador = _barrel_exports$3.MidiFileGenerator;
91959
+ if (!Generador || typeof Generador._playThroughSong !== "function") return false;
91960
+ if (Generador.__recorridoDe19) return true;
91961
+ if (version2 && !version2.startsWith("1.8.")) return false;
91962
+ Generador._playThroughSong = function(score, syncPoints, createNewSyncPoints, generateMasterBar, generateTracks, finish) {
91963
+ const controller = new RecorridoDeCompases(score);
91964
+ const playContext = {
91965
+ synthTick: 0,
91966
+ synthTime: 0,
91967
+ currentTempo: score.tempo,
91968
+ automationToSyncPoint: /* @__PURE__ */ new Map(),
91969
+ syncPoints,
91970
+ createNewSyncPoints
91971
+ };
91972
+ let previousMasterBar = null;
91973
+ const barOccurence = /* @__PURE__ */ new Map();
91974
+ while (!controller.finished) {
91975
+ const index = controller.index;
91976
+ const bar = score.masterBars[index];
91977
+ const currentTick = controller.currentTick;
91978
+ controller.processCurrent();
91979
+ if (controller.shouldPlay) {
91980
+ let occurence = barOccurence.has(index) ? barOccurence.get(index) : -1;
91981
+ occurence++;
91982
+ barOccurence.set(index, occurence);
91983
+ generateMasterBar(bar, previousMasterBar, currentTick, playContext.currentTempo, occurence);
91984
+ generateTracks(index, currentTick, bar.tempoAutomations.length > 0 ? bar.tempoAutomations[0].value : playContext.currentTempo);
91985
+ playContext.synthTick = currentTick;
91986
+ Generador._processBarTime(bar, occurence, playContext);
91407
91987
  }
91408
- if (firmaDeClaves !== clavesAnteriores) {
91409
- if (pentagramas.length > 1 && clavesAnteriores === null)
91410
- cambios.push(`<staves>${pentagramas.length}</staves>`);
91411
- const num = (clave) => pentagramas.length > 1 ? ` number="${clave.numero}"` : "";
91412
- for (const clave of claves) {
91413
- cambios.push(`<clef${num(clave)}>`);
91414
- cambios.push(`<sign>${clave.signo}</sign>`);
91415
- cambios.push(`<line>${clave.linea}</line>`);
91416
- cambios.push("</clef>");
91988
+ controller.moveNext();
91989
+ previousMasterBar = bar;
91990
+ }
91991
+ if (syncPoints.length > 0) {
91992
+ const lastSyncPoint = syncPoints[syncPoints.length - 1];
91993
+ const remainingTicks = controller.currentTick - lastSyncPoint.synthTick;
91994
+ if (remainingTicks > 0) {
91995
+ const p = new _barrel_exports$7.BackingTrackSyncPoint();
91996
+ p.masterBarIndex = previousMasterBar.index;
91997
+ p.masterBarOccurence = barOccurence.get(previousMasterBar.index) - 1;
91998
+ p.synthTick = controller.currentTick;
91999
+ p.synthBpm = playContext.currentTempo;
92000
+ if (playContext.createNewSyncPoints) {
92001
+ p.syncBpm = lastSyncPoint.synthBpm;
92002
+ p.synthBpm = lastSyncPoint.synthBpm;
92003
+ } else if (syncPoints.length === 1) p.syncBpm = lastSyncPoint.synthBpm;
92004
+ else p.syncBpm = syncPoints[syncPoints.length - 2].syncBpm;
92005
+ p.synthTime = lastSyncPoint.synthTime + ticksAMs(remainingTicks, lastSyncPoint.synthBpm);
92006
+ p.syncTime = lastSyncPoint.syncTime + ticksAMs(remainingTicks, p.syncBpm);
92007
+ if (!playContext.createNewSyncPoints) lastSyncPoint.updateSyncBpm(p.synthTime, p.syncTime);
92008
+ syncPoints.push(p);
92009
+ }
92010
+ }
92011
+ finish(controller.currentTick);
92012
+ return playContext;
92013
+ };
92014
+ Generador.__recorridoDe19 = true;
92015
+ return true;
92016
+ }
92017
+ var D2, Repeticion, RecorridoDeCompases, ticksAMs;
92018
+ var init_recorridoDeRepeticiones = __esm({
92019
+ "packages/io/src/recorridoDeRepeticiones.ts"() {
92020
+ init_alphaTab();
92021
+ D2 = {
92022
+ TargetFine: 0,
92023
+ TargetSegno: 1,
92024
+ TargetSegnoSegno: 2,
92025
+ TargetCoda: 3,
92026
+ TargetDoubleCoda: 4,
92027
+ JumpDaCoda: 17,
92028
+ JumpDaDoubleCoda: 18
92029
+ };
92030
+ Repeticion = class {
92031
+ group;
92032
+ /** Con casillas, cada cierre termina la pasada y todos comparten el contador. */
92033
+ conCasillas;
92034
+ /** La pasada actual (desde 0): elige qué casillas suenan. */
92035
+ pasada = 0;
92036
+ /** Las repeticiones hechas por cierre (sólo sin casillas). */
92037
+ iteraciones;
92038
+ /** Las casillas del último compás que las tenía: los que no tienen, las continúan. */
92039
+ casillasActuales = 0;
92040
+ /** @param pasadaFinal si se toca como última pasada (después de un salto): sólo la última casilla. */
92041
+ constructor(group, pasadaFinal) {
92042
+ this.group = group;
92043
+ this.conCasillas = group.masterBars.some((m) => m.alternateEndings !== 0);
92044
+ this.iteraciones = group.closings.map((c) => pasadaFinal ? Math.max(0, c.repeatCount - 1) : 0);
92045
+ if (pasadaFinal) for (const i2 of this.iteraciones) this.pasada = Math.max(this.pasada, i2);
92046
+ }
92047
+ suena(compas) {
92048
+ let casillas = compas.alternateEndings;
92049
+ if (casillas === 0) casillas = this.casillasActuales;
92050
+ else this.casillasActuales = casillas;
92051
+ return casillas === 0 || (casillas & 1 << this.pasada) !== 0;
92052
+ }
92053
+ /** 0 = seguir, 1 = volver al comienzo de la repetición, 2 = terminó. */
92054
+ alCerrar(cierre, sono) {
92055
+ const cierres = this.group.closings;
92056
+ const esElUltimo = cierre === cierres[cierres.length - 1];
92057
+ if (this.conCasillas) {
92058
+ if (!sono && !esElUltimo) return 0;
92059
+ if (this.pasada < cierre.repeatCount - 1) {
92060
+ this.pasada++;
92061
+ this.casillasActuales = 0;
92062
+ return 1;
92063
+ }
92064
+ return esElUltimo ? 2 : 0;
91417
92065
  }
91418
- for (const clave of claves) {
91419
- cambios.push(`<staff-details${num(clave)}>`);
91420
- cambios.push(`<staff-lines>${clave.lineas}</staff-lines>`);
91421
- if (clave.conAfinacion)
91422
- clave.pentagrama.tuning.forEach((midi, i2) => {
91423
- const linea = clave.pentagrama.tuning.length - i2;
91424
- const { paso, alteracion, octava } = alturaMusicXml(midi, fifths);
91425
- cambios.push(`<staff-tuning line="${linea}">`);
91426
- cambios.push(`<tuning-step>${paso}</tuning-step>`);
91427
- if (alteracion !== 0) cambios.push(`<tuning-alter>${alteracion}</tuning-alter>`);
91428
- cambios.push(`<tuning-octave>${octava}</tuning-octave>`);
91429
- cambios.push("</staff-tuning>");
91430
- });
91431
- cambios.push("</staff-details>");
92066
+ const i2 = cierres.indexOf(cierre);
92067
+ if (i2 === -1) return 0;
92068
+ if (this.iteraciones[i2] < cierre.repeatCount - 1) {
92069
+ this.iteraciones[i2]++;
92070
+ for (let j = 0; j < i2; j++) this.iteraciones[j] = 0;
92071
+ this.casillasActuales = 0;
92072
+ return 1;
91432
92073
  }
91433
- clavesAnteriores = firmaDeClaves;
92074
+ return esElUltimo ? 2 : 0;
91434
92075
  }
91435
- if (cambios.length > 0) l.push("<attributes>", ...cambios, "</attributes>");
91436
- const casilla = master?.alternateEndings ?? 0;
91437
- const casillaAntes = score.masterBars[indiceDeCompas2 - 1]?.alternateEndings ?? 0;
91438
- const casillaDespues = score.masterBars[indiceDeCompas2 + 1]?.alternateEndings ?? 0;
91439
- const numerosDeCasilla = [0, 1, 2, 3, 4, 5, 6, 7].filter((b) => casilla & 1 << b).map((b) => b + 1).join(", ");
91440
- if (master?.isRepeatStart || casilla && casilla !== casillaAntes) {
91441
- l.push('<barline location="left">');
91442
- if (master?.isRepeatStart) l.push("<bar-style>heavy-light</bar-style>");
91443
- if (casilla && casilla !== casillaAntes) l.push(`<ending number="${numerosDeCasilla}" type="start"/>`);
91444
- if (master?.isRepeatStart) l.push('<repeat direction="forward"/>');
91445
- l.push("</barline>");
92076
+ /** Si algún cierre todavía va a volver por encima de este compás. */
92077
+ vaAVolver(compas) {
92078
+ const cierres = this.group.closings;
92079
+ for (let i2 = 0; i2 < cierres.length; i2++) {
92080
+ const cierre = cierres[i2];
92081
+ if (cierre.index >= compas.index) {
92082
+ if ((this.conCasillas ? this.pasada : this.iteraciones[i2]) < cierre.repeatCount - 1) return true;
92083
+ }
92084
+ }
92085
+ return false;
91446
92086
  }
91447
- if (master?.section?.text || master?.section?.marker)
91448
- l.push(
91449
- '<direction placement="above">',
91450
- "<direction-type>",
91451
- `<rehearsal>${escapar(master.section.marker || master.section.text)}</rehearsal>`,
91452
- "</direction-type>",
91453
- "</direction>"
91454
- );
91455
- l.push(...saltosDelCompas(master));
91456
- const ticsDelCompas = DIVISIONES_POR_NEGRA * 4 * numerador / denominador;
91457
- const tempos = (master?.tempoAutomations ?? []).map((a) => ({ bpm: a.value, desde: a.ratioPosition }));
91458
- if (indiceDeCompas2 === 0 && score.tempo && !tempos.some((t) => t.desde <= 1e-6))
91459
- tempos.unshift({ bpm: score.tempo, desde: 0 });
91460
- for (const t of tempos) {
91461
- const bpm = Math.round(t.bpm);
91462
- const offset = Math.round(t.desde * ticsDelCompas);
91463
- l.push(
91464
- '<direction placement="above">',
91465
- "<direction-type>",
91466
- "<metronome>",
91467
- "<beat-unit>quarter</beat-unit>",
91468
- `<per-minute>${bpm}</per-minute>`,
91469
- "</metronome>",
91470
- "</direction-type>",
91471
- ...offset > 0 ? [`<offset>${offset}</offset>`] : [],
91472
- `<sound tempo="${bpm}"/>`,
91473
- "</direction>"
91474
- );
92087
+ };
92088
+ RecorridoDeCompases = class _RecorridoDeCompases {
92089
+ /** Los saltos en el orden en que se revisan si un compás tiene varios. */
92090
+ static SALTOS = [
92091
+ { jump: 5, target: null, playUntil: null, continueAt: null },
92092
+ { jump: 6, target: null, playUntil: D2.JumpDaCoda, continueAt: D2.TargetCoda },
92093
+ { jump: 7, target: null, playUntil: D2.JumpDaDoubleCoda, continueAt: D2.TargetDoubleCoda },
92094
+ { jump: 8, target: null, playUntil: D2.TargetFine, continueAt: null },
92095
+ { jump: 9, target: D2.TargetSegno, playUntil: null, continueAt: null },
92096
+ { jump: 10, target: D2.TargetSegno, playUntil: D2.JumpDaCoda, continueAt: D2.TargetCoda },
92097
+ { jump: 11, target: D2.TargetSegno, playUntil: D2.JumpDaDoubleCoda, continueAt: D2.TargetDoubleCoda },
92098
+ { jump: 12, target: D2.TargetSegno, playUntil: D2.TargetFine, continueAt: null },
92099
+ { jump: 13, target: D2.TargetSegnoSegno, playUntil: null, continueAt: null },
92100
+ { jump: 14, target: D2.TargetSegnoSegno, playUntil: D2.JumpDaCoda, continueAt: D2.TargetCoda },
92101
+ { jump: 15, target: D2.TargetSegnoSegno, playUntil: D2.JumpDaDoubleCoda, continueAt: D2.TargetDoubleCoda },
92102
+ { jump: 16, target: D2.TargetSegnoSegno, playUntil: D2.TargetFine, continueAt: null }
92103
+ ];
92104
+ score;
92105
+ pila = [];
92106
+ /** El D.C./D.S. que se siguió; null si se toca normal (antes del salto o después de la coda). */
92107
+ saltoActivo = null;
92108
+ /** Los compases cuyo salto ya se tomó: cada salto se toma una sola vez. */
92109
+ saltosTomados = /* @__PURE__ */ new Set();
92110
+ shouldPlay = true;
92111
+ index = 0;
92112
+ currentTick = 0;
92113
+ constructor(score) {
92114
+ this.score = score;
91475
92115
  }
91476
- let ultimoAvance = 0;
91477
- let hayAlgoEscrito = false;
91478
- for (const { pentagrama, numero: numero2 } of claves) {
91479
- const bar = pentagrama.bars[indiceDeCompas2];
91480
- if (!bar) continue;
91481
- bar.voices.forEach((voice, indiceDeVoz) => {
91482
- if (voice.beats.length === 0 || voice.isEmpty) return;
91483
- if (hayAlgoEscrito && ultimoAvance > 0)
91484
- l.push("<backup>", `<duration>${ultimoAvance}</duration>`, "</backup>");
91485
- const { abren, cierran } = gruposDeTresillo(voice.beats);
91486
- const numeroDePentagrama = pentagramas.length > 1 ? numero2 : null;
91487
- let avance = 0;
91488
- voice.beats.forEach((beat, i2) => {
91489
- const duracion = beat.displayDuration;
91490
- if (beat.graceType === _barrel_exports$4.GraceType.None) avance += duracion;
91491
- if (beat.isRest || beat.notes.length === 0) {
91492
- l.push(...escribirSilencio2(beat, duracion, indiceDeVoz + 1, numeroDePentagrama));
92116
+ get finished() {
92117
+ return this.index >= this.score.masterBars.length;
92118
+ }
92119
+ processCurrent() {
92120
+ const compas = this.score.masterBars[this.index];
92121
+ this.entrarARepeticion(compas);
92122
+ const rep = this.pila.length > 0 ? this.pila[this.pila.length - 1] : null;
92123
+ this.shouldPlay = rep === null || rep.suena(compas);
92124
+ if (this.shouldPlay) this.currentTick += compas.calculateDuration();
92125
+ }
92126
+ moveNext() {
92127
+ if (this.moverConSaltos()) return;
92128
+ const compas = this.score.masterBars[this.index];
92129
+ if (this.pila.length > 0 && compas.repeatCount > 1) {
92130
+ const rep = this.pila[this.pila.length - 1];
92131
+ switch (rep.alCerrar(compas, this.shouldPlay)) {
92132
+ case 1:
92133
+ this.index = rep.group.opening.index;
91493
92134
  return;
91494
- }
91495
- beat.notes.forEach((note, k) => {
91496
- l.push(
91497
- ...escribirNota(note, beat, pentagrama, {
91498
- esAcorde: k > 0,
91499
- fifths,
91500
- duracion,
91501
- // ★ La marca de tresillo va en la PRIMERA nota del golpe: es una marca del
91502
- // golpe, no de cada cuerda que suena en él.
91503
- abreTresillo: k === 0 && abren.has(i2),
91504
- cierraTresillo: k === 0 && cierran.has(i2),
91505
- instrumentos,
91506
- numeroDeVoz: indiceDeVoz + 1,
91507
- numeroDePentagrama
91508
- })
91509
- );
91510
- });
91511
- });
91512
- ultimoAvance = avance;
91513
- hayAlgoEscrito = true;
91514
- });
92135
+ case 2:
92136
+ this.pila.pop();
92137
+ }
92138
+ }
92139
+ this.index++;
91515
92140
  }
91516
- const cierraCasilla = casilla !== 0 && casilla !== casillaDespues;
91517
- if (master?.isRepeatEnd || master?.isDoubleBar || cierraCasilla) {
91518
- l.push('<barline location="right">');
91519
- if (master?.isRepeatEnd || master?.isDoubleBar)
91520
- l.push(`<bar-style>${master.isRepeatEnd ? "light-heavy" : "light-light"}</bar-style>`);
91521
- if (cierraCasilla)
91522
- l.push(`<ending number="${numerosDeCasilla}" type="${master?.isRepeatEnd ? "stop" : "discontinue"}"/>`);
91523
- if (master?.isRepeatEnd)
91524
- l.push(`<repeat direction="backward" times="${Math.max(2, master.repeatCount)}"/>`);
91525
- l.push("</barline>");
92141
+ /** Empieza la repetición del compás si hace falta: al llegar a su apertura, o tras un salto que cayó adentro. */
92142
+ entrarARepeticion(compas) {
92143
+ const group = compas.repeatGroup;
92144
+ if (!group.isClosed || this.pila.some((r) => r.group === group)) return;
92145
+ const adentro = compas.index <= group.closings[group.closings.length - 1].index;
92146
+ if (compas === group.opening || this.saltoActivo !== null && adentro) {
92147
+ this.pila.push(new Repeticion(group, this.saltoActivo !== null));
92148
+ }
91526
92149
  }
91527
- l.push("</measure>");
91528
- }
91529
- l.push("</part>");
91530
- }
91531
- l.push("</score-partwise>");
91532
- return sangrar(l);
91533
- }
91534
- function sangrar(lineas) {
91535
- const salida = [];
91536
- let nivel = 0;
91537
- for (const linea of lineas) {
91538
- const cierra = linea.startsWith("</");
91539
- if (cierra) nivel = Math.max(0, nivel - 1);
91540
- salida.push(" ".repeat(nivel) + linea);
91541
- const abre = /^<[^/?!]/.test(linea) && !linea.endsWith("/>") && !/<\/[^>]+>$/.test(linea);
91542
- if (abre) nivel++;
91543
- }
91544
- return salida.join("\n") + "\n";
91545
- }
91546
- function exportarMusicXmlComprimido(score) {
91547
- const xml = exportarMusicXml(score);
91548
- const contenedor = `<?xml version="1.0" encoding="UTF-8"?>
91549
- <container>
91550
- <rootfiles>
91551
- <rootfile full-path="partitura.musicxml" media-type="application/vnd.recordare.musicxml+xml"/>
91552
- </rootfiles>
91553
- </container>
91554
- `;
91555
- const codificador = new TextEncoder();
91556
- return zipSync(
91557
- {
91558
- "META-INF/container.xml": [codificador.encode(contenedor), { level: 0 }],
91559
- "partitura.musicxml": codificador.encode(xml)
91560
- },
91561
- // ⚠️ Fecha FIJA, no la de hoy: dos exportaciones iguales tienen que dar dos archivos
91562
- // iguales byte a byte, o no hay prueba que pueda compararlos ni `diff` de git que
91563
- // sirva. El porqué del valor concreto está en la constante — se tardó dos rojos en
91564
- // encontrarlo, y ninguno de los dos era el que yo esperaba.
91565
- { mtime: FECHA_FIJA_DEL_ZIP }
91566
- );
91567
- }
91568
- var DIVISIONES_POR_NEGRA, FECHA_FIJA_DEL_ZIP, PASOS, NOMBRE_DE_LA_FIGURA2, ALTERACION_POR_MODO, D2, SALTOS;
91569
- var init_musicxml = __esm({
91570
- "packages/io/src/musicxml.ts"() {
91571
- init_alphaTab();
91572
- init_esm();
91573
- DIVISIONES_POR_NEGRA = 960;
91574
- FECHA_FIJA_DEL_ZIP = 946728e6;
91575
- PASOS = [
91576
- { paso: "C", semitonos: 0 },
91577
- { paso: "D", semitonos: 2 },
91578
- { paso: "E", semitonos: 4 },
91579
- { paso: "F", semitonos: 5 },
91580
- { paso: "G", semitonos: 7 },
91581
- { paso: "A", semitonos: 9 },
91582
- { paso: "B", semitonos: 11 }
91583
- ];
91584
- NOMBRE_DE_LA_FIGURA2 = /* @__PURE__ */ new Map([
91585
- [-4, "long"],
91586
- [-2, "breve"],
91587
- [1, "whole"],
91588
- [2, "half"],
91589
- [4, "quarter"],
91590
- [8, "eighth"],
91591
- [16, "16th"],
91592
- [32, "32nd"],
91593
- [64, "64th"],
91594
- [128, "128th"],
91595
- [256, "256th"]
91596
- ]);
91597
- ALTERACION_POR_MODO = { 2: 0, 3: 1, 4: 2, 5: -1, 6: -2 };
91598
- D2 = _barrel_exports$4.Direction;
91599
- SALTOS = [
91600
- [D2.TargetSegno, { simbolo: "segno", sonido: 'segno="segno"' }],
91601
- [D2.TargetSegnoSegno, { simbolo: "segno", sonido: 'segno="segno2"' }],
91602
- [D2.TargetCoda, { simbolo: "coda", sonido: 'coda="coda"' }],
91603
- [D2.TargetDoubleCoda, { simbolo: "coda", sonido: 'coda="coda2"' }],
91604
- [D2.TargetFine, { texto: "Fine", sonido: 'fine="yes"' }],
91605
- [D2.JumpDaCapo, { texto: "D.C.", sonido: 'dacapo="yes"' }],
91606
- [D2.JumpDaCapoAlCoda, { texto: "D.C. al Coda", sonido: 'dacapo="yes"' }],
91607
- [D2.JumpDaCapoAlDoubleCoda, { texto: "D.C. al Coda 2", sonido: 'dacapo="yes"' }],
91608
- [D2.JumpDaCapoAlFine, { texto: "D.C. al Fine", sonido: 'dacapo="yes"' }],
91609
- [D2.JumpDalSegno, { texto: "D.S.", sonido: 'dalsegno="segno"' }],
91610
- [D2.JumpDalSegnoAlCoda, { texto: "D.S. al Coda", sonido: 'dalsegno="segno"' }],
91611
- [D2.JumpDalSegnoAlDoubleCoda, { texto: "D.S. al Coda 2", sonido: 'dalsegno="segno"' }],
91612
- [D2.JumpDalSegnoAlFine, { texto: "D.S. al Fine", sonido: 'dalsegno="segno"' }],
91613
- [D2.JumpDalSegnoSegno, { texto: "D.S.S.", sonido: 'dalsegno="segno2"' }],
91614
- [D2.JumpDalSegnoSegnoAlCoda, { texto: "D.S.S. al Coda", sonido: 'dalsegno="segno2"' }],
91615
- [D2.JumpDalSegnoSegnoAlDoubleCoda, { texto: "D.S.S. al Coda 2", sonido: 'dalsegno="segno2"' }],
91616
- [D2.JumpDalSegnoSegnoAlFine, { texto: "D.S.S. al Fine", sonido: 'dalsegno="segno2"' }],
91617
- [D2.JumpDaCoda, { texto: "To Coda", sonido: 'tocoda="coda"' }],
91618
- [D2.JumpDaDoubleCoda, { texto: "To Coda 2", sonido: 'tocoda="coda2"' }]
91619
- ];
92150
+ moverConSaltos() {
92151
+ const compas = this.score.masterBars[this.index];
92152
+ const dir = compas.directions;
92153
+ if (!this.shouldPlay || dir === null || dir === void 0 || dir.size === 0) return false;
92154
+ if (this.saltoActivo === null) return this.tomarSalto(compas, dir);
92155
+ const hasta = this.saltoActivo.playUntil;
92156
+ if (hasta !== null && dir.has(hasta)) return this.seguirTrasElSalto(this.saltoActivo.continueAt);
92157
+ return false;
92158
+ }
92159
+ tomarSalto(compas, dir) {
92160
+ if (this.saltosTomados.has(compas) || this.pila.some((r) => r.vaAVolver(compas))) return false;
92161
+ for (const salto of _RecorridoDeCompases.SALTOS) {
92162
+ if (!dir.has(salto.jump)) continue;
92163
+ const destino = salto.target === null ? 0 : this.buscar(salto.target, true);
92164
+ if (destino === -1) continue;
92165
+ this.saltosTomados.add(compas);
92166
+ this.saltoActivo = salto;
92167
+ this.pila = [];
92168
+ this.index = destino;
92169
+ return true;
92170
+ }
92171
+ return false;
92172
+ }
92173
+ seguirTrasElSalto(seguirEn) {
92174
+ if (seguirEn === null) {
92175
+ this.index = this.score.masterBars.length;
92176
+ return true;
92177
+ }
92178
+ const coda = this.buscar(seguirEn, false);
92179
+ if (coda === -1) return false;
92180
+ this.saltoActivo = null;
92181
+ this.pila = [];
92182
+ this.index = coda;
92183
+ return true;
92184
+ }
92185
+ buscar(direccion, primeroAtras) {
92186
+ const paso = primeroAtras ? -1 : 1;
92187
+ const i2 = this.buscarHacia(direccion, paso);
92188
+ return i2 !== -1 ? i2 : this.buscarHacia(direccion, -paso);
92189
+ }
92190
+ buscarHacia(direccion, paso) {
92191
+ const compases = this.score.masterBars;
92192
+ for (let i2 = this.index; i2 >= 0 && i2 < compases.length; i2 += paso) {
92193
+ const d = compases[i2].directions;
92194
+ if (d !== null && d !== void 0 && d.has(direccion)) return i2;
92195
+ }
92196
+ return -1;
92197
+ }
92198
+ };
92199
+ ticksAMs = (ticks, tempo) => ticks * (6e4 / (tempo * 960)) | 0;
91620
92200
  }
91621
92201
  });
91622
92202
 
@@ -91796,7 +92376,9 @@ async function abrirDeArchivo(ruta) {
91796
92376
  abierta = {
91797
92377
  documento: archivo.documento,
91798
92378
  origen: { clase: "archivo", ruta },
91799
- avisos: [],
92379
+ // ★ (2026-11-11) Una obra vieja con el piano en dos pistas vuelve junta: se dice, para que
92380
+ // la IA no busque la «mano izquierda» como pista aparte.
92381
+ avisos: archivo.manosJuntadas > 0 ? ["El piano estaba guardado en dos pistas (una por mano): ahora es un solo instrumento con dos pentagramas."] : [],
91800
92382
  grabaciones: archivo.grabaciones,
91801
92383
  grabacionesEnEspera: [],
91802
92384
  fuentes: archivo.fuentes
@@ -91980,7 +92562,7 @@ var VERSION_DEL_CONECTOR;
91980
92562
  var init_version = __esm({
91981
92563
  "apps/mcp/src/version.ts"() {
91982
92564
  "use strict";
91983
- VERSION_DEL_CONECTOR = true ? "0.2.17" : null;
92565
+ VERSION_DEL_CONECTOR = true ? "0.2.18" : null;
91984
92566
  }
91985
92567
  });
91986
92568
 
@@ -124605,6 +125187,7 @@ var LEEN = /* @__PURE__ */ new Set([
124605
125187
  "ajustes_de_video",
124606
125188
  "atajos_del_programa",
124607
125189
  "audios_rechazados_por_largos",
125190
+ "buscar_en_las_paletas",
124608
125191
  "captura",
124609
125192
  "direcciones_disponibles",
124610
125193
  "efectos_disponibles",
@@ -128446,6 +129029,94 @@ function registrarHerramientasDeArchivo(servidor2) {
128446
129029
  );
128447
129030
  }
128448
129031
 
129032
+ // apps/mcp/src/herramientas/paletas.ts
129033
+ init_src2();
129034
+ init_src3();
129035
+ init_obra();
129036
+ var FAMILIAS = ["figura", "tecnica", "signo", "acorde", "kit", "matiz", "seccion"];
129037
+ function herramientaDeLaFicha(familia, ficha) {
129038
+ const id2 = ficha.id;
129039
+ switch (familia) {
129040
+ case "figura":
129041
+ if (id2 === "puntillo") return { herramienta: "alternar_puntillo", argumentos: {} };
129042
+ if (id2 === "silencio") return { herramienta: "escribir_silencio", argumentos: {} };
129043
+ return { herramienta: "cambiar_duracion", argumentos: { figura: Number(id2.slice("figura-".length)) } };
129044
+ case "tecnica":
129045
+ if (id2.startsWith("efecto-")) return { herramienta: "poner_efecto", argumentos: { clave: id2.slice("efecto-".length) } };
129046
+ if (id2.startsWith("bend-")) return { herramienta: "poner_bend", argumentos: { forma: id2.slice("bend-".length) } };
129047
+ if (id2.startsWith("sube-")) return { herramienta: "poner_bend", argumentos: { cuartos: Number(id2.slice("sube-".length)) } };
129048
+ return { herramienta: "escribir_curva_de_bend", argumentos: {} };
129049
+ case "signo":
129050
+ return { herramienta: "poner_signo", argumentos: { clave: id2.slice("signo-".length) } };
129051
+ case "matiz":
129052
+ return { herramienta: "poner_dinamica", argumentos: { matiz: id2.slice("matiz-".length) } };
129053
+ case "kit": {
129054
+ const pieza = KIT[Number(id2.slice("kit-".length))];
129055
+ return pieza ? { herramienta: "escribir_notas", argumentos: { notas: [{ pieza: pieza.nombre }] } } : null;
129056
+ }
129057
+ case "acorde":
129058
+ if (id2 === MIRA_DEDUCIR) return { herramienta: "acorde_en_el_cursor", argumentos: {} };
129059
+ {
129060
+ const [nombre, letra] = ficha.nombre.split(" ");
129061
+ return { herramienta: "poner_acorde", argumentos: { nombre, ...letra ? { forma: letra } : {} } };
129062
+ }
129063
+ case "seccion":
129064
+ if (id2 === SECCION_NUEVA) return null;
129065
+ return { herramienta: "poner_seccion", argumentos: { nombre: id2.slice("seccion:".length) } };
129066
+ }
129067
+ }
129068
+ function paletasParaBuscar() {
129069
+ let acordes = [];
129070
+ let secciones = [];
129071
+ try {
129072
+ const indice = indexar(obraAbierta().documento);
129073
+ acordes = fichasDeAcordes(acordesUsados(indice)).filter((f) => f.id !== MIRA_DEDUCIR);
129074
+ const nombres = [];
129075
+ for (let i2 = 0; i2 < indice.cantidadDeCompases; i2++) {
129076
+ nombres.push(String(indice.compasMaestro(i2)?.datos.seccion ?? ""));
129077
+ }
129078
+ secciones = fichasDeSecciones(nombres).filter((f) => f.id !== SECCION_NUEVA);
129079
+ } catch {
129080
+ }
129081
+ return FAMILIAS.map((familia) => ({
129082
+ familia,
129083
+ fichas: familia === "acorde" ? acordes : familia === "seccion" ? secciones : fichasFijas(familia)
129084
+ }));
129085
+ }
129086
+ function registrarHerramientasDePaletas(servidor2) {
129087
+ servidor2.registerTool(
129088
+ "buscar_en_las_paletas",
129089
+ {
129090
+ title: "Buscar en las paletas",
129091
+ description: "Busca por nombre en las siete paletas del programa a la vez \u2014figuras, t\xE9cnicas, signos, acordes, bater\xEDa, matices y secciones\u2014 y dice qu\xE9 es cada resultado, en qu\xE9 paleta est\xE1, su tecla de f\xE1brica y **qu\xE9 herramienta lo escribe, con qu\xE9 argumentos**. Acepta sin\xF3nimos en espa\xF1ol y los nombres en ingl\xE9s de los programas: \xABligado\xBB, \xABhammer\xBB, \xABfermata\xBB, \xABhi-hat\xBB. Es la misma b\xFAsqueda de \xABEditar \u25B8 Buscar en las paletas\u2026\xBB. Sirve cuando no sabes la clave de algo: b\xFAscalo ac\xE1 y usa la herramienta que te indica, agregando d\xF3nde (comp\xE1s, golpe, pista\u2026).",
129092
+ inputSchema: {
129093
+ texto: external_exports.string().min(1).max(60).describe("Lo que se busca: \xABacento\xBB, \xABmartillo\xBB, \xABcorchea\xBB, \xABff\xBB\u2026"),
129094
+ limite: external_exports.number().int().min(1).max(30).optional().describe("Cu\xE1ntos resultados, como mucho. Por omisi\xF3n 12.")
129095
+ }
129096
+ },
129097
+ ({ texto: buscado, limite }) => {
129098
+ const paletas = paletasParaBuscar();
129099
+ const hayObra = paletas.some((p) => (p.familia === "acorde" || p.familia === "seccion") && p.fichas.length > 0);
129100
+ const resultados = buscarEnLasPaletas(paletas, buscado, limite ?? 12).map((r) => {
129101
+ const tecla = r.ficha.atajo ? ATAJOS.find((a) => a.id === r.ficha.atajo)?.teclas[0] : void 0;
129102
+ return {
129103
+ nombre: r.ficha.nombre,
129104
+ paleta: NOMBRE_DE_LA_FAMILIA[r.familia],
129105
+ ...tecla ? { teclaDeFabrica: comoSeLee(tecla) } : {},
129106
+ ...herramientaDeLaFicha(r.familia, r.ficha) ?? {}
129107
+ };
129108
+ });
129109
+ return texto2({
129110
+ resultados,
129111
+ // ⚠️ Un cero se tiene que poder leer como «no existe» y no como «no busqué».
129112
+ ...resultados.length === 0 ? { nota: `nada se llama \xAB${buscado}\xBB en las paletas` } : {},
129113
+ ...hayObra ? {} : { sinObra: "los acordes y las secciones salen de la obra: sin una abierta, no se buscan" },
129114
+ teclas: "las teclas son las de f\xE1brica: el m\xFAsico pudo haberlas reasignado en su navegador"
129115
+ });
129116
+ }
129117
+ );
129118
+ }
129119
+
128449
129120
  // apps/mcp/src/servidor.ts
128450
129121
  var NOMBRE = "mandioca-tabs";
128451
129122
  var VERSION = VERSION_DEL_CONECTOR ?? "0.0.0";
@@ -128503,6 +129174,7 @@ function crearServidor() {
128503
129174
  registrarHerramientasDeBend(servidor2);
128504
129175
  registrarHerramientasDePistas(servidor2);
128505
129176
  registrarHerramientasDeArchivo(servidor2);
129177
+ registrarHerramientasDePaletas(servidor2);
128506
129178
  return servidor2;
128507
129179
  }
128508
129180